/* ===========================================================================
   Classic page templates (template-*.php)
   ---------------------------------------------------------------------------
   Page copy lives in ACF and is rendered by PHP templates rather than blocks,
   so these sections need their own layout rules. Colours, spacing and fonts
   still come from theme.json presets, so there remains one source of truth.
   =========================================================================== */

/* Twenty Twenty-Five references two colour slugs our palette does not define
   (accent-4 on button focus, accent-6 on separators). Undefined custom properties
   fall back to currentColor, which silently breaks the focus outline. Alias them
   to our tokens rather than adding them to the palette, so they do not appear as
   pickable colours in the editor. */
:root {
	--wp--preset--color--accent-4: var(--wp--preset--color--primary);
	--wp--preset--color--accent-6: var(--wp--preset--color--pale);
}

/* 1600px to match penguinmep's container. Prose uses a narrower container:
   .pps-container--narrow (760px), or .pps-container--project (1000px) on case
   studies. A 1600px line length would be unreadable for body copy. */
.pps-container {
	max-inline-size: 1600px;
	margin-inline: auto;
	padding-inline: var(--wp--preset--spacing--40);
}

.pps-container--narrow {
	max-inline-size: 760px;
}

/* Case-study pages. Every section, hero included, shares this width so the
   page has one left edge and reads as a single column (2026-10-07). */
.pps-container--project {
	max-inline-size: 1000px;
}

.pps-section {
	padding-block: var(--wp--preset--spacing--70);
}

.pps-section--tight {
	padding-block: var(--wp--preset--spacing--60);
}

.pps-section--offwhite {
	background-color: var(--wp--preset--color--offwhite);
}

.pps-section--dark {
	background-color: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--white);
}

.pps-section--dark h2,
.pps-section--dark h3 {
	color: var(--wp--preset--color--white);
}

.pps-h-sm {
	font-size: 1.5rem;
}

.has-text-align-center {
	text-align: center;
}

/* --- Hero ---------------------------------------------------------------- */

.pps-hero {
	position: relative;
	overflow: hidden; /* clips the absolutely positioned background <img> */
	display: flex;
	align-items: center;
	min-block-size: 72vh;
	padding-block: var(--wp--preset--spacing--70);
	background-color: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--white);
}

/* The hero and CTA images are <img> elements (for srcset) laid behind the
   content rather than CSS backgrounds. */
.pps-hero__bg,
.pps-cta__bg {
	position: absolute;
	inset: 0;
	inline-size: 100%;
	block-size: 100%;
	object-fit: cover;
	object-position: center 45%;
	z-index: 0;
}

/* Hero scrim.
   A purely horizontal gradient does not work here: the text block is capped at
   760px, so on a narrow viewport it spans nearly the full width and runs off the
   dark end of the gradient — white text measured 2.4:1 at 768px. So the default
   is a flat dim that is safe at every width, and the directional gradient is
   layered on only once the viewport is wide enough for the text to sit in its
   dark half. Nowhere does the alpha drop below 60%. */
.pps-hero--image::before {
	content: "";
	position: absolute;
	inset: 0;
	background: rgb(0 0 0 / 64%);
}

@media (min-width: 1200px) {
	.pps-hero--image::before {
		background: linear-gradient(90deg, rgb(0 0 0 / 80%) 0%, rgb(0 0 0 / 68%) 55%, rgb(0 0 0 / 60%) 100%);
	}
}

.pps-hero--image::before,
.pps-cta--image::before {
	z-index: 1;
}

.pps-hero .pps-container,
.pps-cta .pps-container {
	position: relative;
	z-index: 2;
	inline-size: 100%;
}

.pps-hero__inner {
	max-inline-size: 760px;
}

.pps-hero__heading {
	color: var(--wp--preset--color--white);
	font-size: var(--wp--preset--font-size--display);
	line-height: 1.1;
	margin-block: 0.5rem;
}

.pps-hero__sub {
	font-size: 1.375rem;
	margin-block-start: var(--wp--preset--spacing--30);
}

/* --- Eyebrow ------------------------------------------------------------- */

.pps-eyebrow {
	font-family: var(--wp--preset--font-family--poppins);
	font-size: 0.9375rem;
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--wp--preset--color--secondary);
	margin-block: 0;
}

.pps-eyebrow--accent {
	color: var(--wp--preset--color--accent);
}

/* --- Grids --------------------------------------------------------------- */

.pps-grid {
	list-style: none;
	margin: var(--wp--preset--spacing--50) 0 0;
	padding: 0;
	display: grid;
	gap: var(--wp--preset--spacing--40);
}

.pps-grid--4 { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }

/* Two-up, for the Services page. A stated column count rather than auto-fit:
   these cards must be two across or one, never an in-between the track sizing
   might pick. The gap is a step wider than the other grids because the cards
   themselves are roughly twice the width — spacing-40 between 764px cards reads
   as a crack rather than a gutter. */
.pps-grid--2 {
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: var(--wp--preset--spacing--50);
}

/* One column from tablet down. 900px rather than the theme's usual 782px so an
   iPad in portrait gets the single column too — at 800px the two cards are
   ~370px wide and the photographs start to lose their subject. */
@media (max-width: 900px) {
	.pps-grid--2 { grid-template-columns: 1fr; }
}
.pps-grid--3 { grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); }
.pps-grid--auto { grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); }

/* Explicit 5-up, for the two sections that have exactly five items (Why Choose
   Us, and the Concept-to-Completion steps). auto-fit is not used here: at a
   1600px container, minmax(20rem, 1fr) maths out to four columns and orphans the
   fifth item on its own row. Stated column counts are predictable instead.
   The gap is tightened a step, since five columns leaves each card narrower. */
.pps-grid--5 {
	grid-template-columns: repeat(5, minmax(0, 1fr));
	gap: var(--wp--preset--spacing--30);
}

/* Steps down at 1200px, not 1400px, so a 1280 or 1366 laptop still gets five
   across. Card width at 1280 works out around 227px, which a short title and
   two sentences carry; below that it gets cramped. */
@media (max-width: 1200px) {
	.pps-grid--5 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 900px) {
	.pps-grid--5 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 600px) {
	.pps-grid--5 { grid-template-columns: 1fr; }
}

/* --- Cards --------------------------------------------------------------- */

.pps-card {
	display: flex;
	flex-direction: column;
	background: var(--wp--preset--color--white);
	border-radius: 2px;
	box-shadow: var(--wp--preset--shadow--card);
	overflow: hidden;
}

/* The clip for the hover zoom has to be HERE, on the media frame, not on
   .pps-card. The card's own `overflow: hidden` only clips at the card's outer
   edge, so a scaled photograph stayed inside the card but grew DOWNWARDS over
   the title and summary beneath it. Clipping at the frame keeps the picture
   inside the box it is supposed to occupy, which is the point of the effect.

   The anchor is display:block for the same reason: left inline it forms a line
   box, and the descender space under it puts a few stray pixels of image below
   the frame the clip is meant to define. */
.pps-card__media {
	overflow: hidden;
}

.pps-card__media a {
	display: block;
}

/* The focus ring has to be drawn INSIDE the frame, or it is not drawn at all.
   `overflow: hidden` above clips everything a descendant paints, outlines
   included — and the anchor's border box is exactly the frame, so the global
   3px ring at `outline-offset: 2px` falls entirely in the clipped region.

   That matters most on the home and projects cards, which carry TWO links (the
   image and the title) and share one `:focus-within` lift between them: without
   a ring there is nothing at all telling a keyboard user which of the two they
   are on. */
.pps-card__media a:focus-visible {
	outline-offset: -3px;
}

.pps-card__media img {
	display: block;
	inline-size: 100%;
	block-size: auto;
	aspect-ratio: 4 / 3;
	object-fit: cover;
}

.pps-card__body {
	padding: var(--wp--preset--spacing--30) var(--wp--preset--spacing--30) var(--wp--preset--spacing--40);
}

.pps-card__title {
	font-size: 1.25rem;
	margin-block: 0 0.5rem;
}

.pps-card__title a {
	text-decoration: none;
}

.pps-card__copy {
	font-size: 1rem;
	margin-block: 0;
}

.pps-card__copy p:last-child {
	margin-block-end: 0;
}

/* --- Service cards (Services page, two-up) -------------------------------- */

/* Bigger than a listing card, so the body copy gets room the standard
   spacing-30 padding would not give it at this width. */
.pps-card--service .pps-card__body {
	padding: var(--wp--preset--spacing--40);
}

/* 16/9 rather than the 4/3 every other card uses. At 764px wide a 4/3 crop
   stands 573px tall and the photograph starts to dominate the card it belongs
   to; 16/9 keeps it banner-like, which is what was asked for. */
.pps-card--service .pps-card__media img {
	aspect-ratio: 16 / 9;
}

/* Carries the accent rule from .pps-rule, so the yellow arrives the same way it
   does under every other heading on the site. */
.pps-card--service .pps-card__heading {
	font-size: var(--wp--preset--font-size--x-large);
	line-height: 1.2;
	margin-block: 0 var(--wp--preset--spacing--30);
}

/* --- Split (text beside image) ------------------------------------------- */

.pps-split {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: var(--wp--preset--spacing--60);
	align-items: center;
}

.pps-split--single {
	grid-template-columns: 1fr;
	max-inline-size: 820px;
}

.pps-split__media img {
	display: block;
	inline-size: 100%;
	block-size: auto;
	border-radius: 2px;
	box-shadow: var(--wp--preset--shadow--lifted);
}

@media (max-width: 782px) {
	.pps-split {
		grid-template-columns: 1fr;
	}
}

/* --- Why Choose Us ------------------------------------------------------- */

.pps-reason {
	background: var(--wp--preset--color--ink);
	border-block-start: 3px solid var(--wp--preset--color--accent);
	padding: var(--wp--preset--spacing--40);
}

.pps-reason__title {
	font-size: 1.1875rem;
	margin-block: 0 0.5rem;
}

.pps-reason__body {
	font-size: 1rem;
}

.pps-reason__body p:last-child {
	margin-block-end: 0;
}

/* --- Testimonials -------------------------------------------------------- */

.pps-quote {
	background: var(--wp--preset--color--white);
	padding: var(--wp--preset--spacing--40);
	border-inline-start: 4px solid var(--wp--preset--color--accent);
	box-shadow: var(--wp--preset--shadow--card);
}

.pps-quote__text {
	margin: 0;
	padding: 0;
	border: 0;
	font-size: 1.0625rem;
}

.pps-quote__cite {
	font-family: var(--wp--preset--font-family--poppins);
	font-size: 0.875rem;
	font-weight: 600;
	color: var(--wp--preset--color--secondary);
	margin-block-end: 0;
}

/* A placeholder testimonial keeps the dashed treatment but loses the accent bar,
   so it never reads as a real, approved quote. The background must be restated
   here: .pps-quote's white fill is declared later in the cascade than the shared
   .pps-placeholder rule in style.css and would otherwise win. */
.pps-quote.pps-placeholder {
	border-inline-start: 0;
	box-shadow: none;
	background: var(--wp--preset--color--offwhite);
}

/* --- Accreditation logos ------------------------------------------------- */

.pps-logos {
	list-style: none;
	margin: var(--wp--preset--spacing--50) 0 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: var(--wp--preset--spacing--50);
	align-items: center;
	justify-content: center;
}

.pps-logos img {
	display: block;
	inline-size: auto;
	max-inline-size: 120px;
	block-size: auto;
}

/* --- Closing CTA --------------------------------------------------------- */

.pps-cta {
	position: relative;
	overflow: hidden; /* clips the absolutely positioned background <img> */
	background-color: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--white);
	text-align: center;
}

.pps-cta--image::before {
	content: "";
	position: absolute;
	inset: 0;
	background: rgb(0 0 0 / 78%);
}

.pps-cta h2 {
	color: var(--wp--preset--color--white);
}

.pps-cta__body {
	margin-block-start: var(--wp--preset--spacing--40);
}

/* --- Prose and buttons --------------------------------------------------- */

.pps-prose > :first-child { margin-block-start: 0; }
.pps-prose > :last-child { margin-block-end: 0; }

.pps-buttons {
	margin-block-start: var(--wp--preset--spacing--40);
}

.pps-buttons--center,
.pps-cta .pps-buttons {
	text-align: center;
}

.pps-buttons .wp-element-button {
	display: inline-block;
	text-decoration: none;
}

/* Pill buttons, matching penguinmep. theme.json covers block buttons; these are
   the anchors rendered by the classic templates. */
.pps-main .wp-element-button,
.pps-header .wp-element-button {
	border-radius: 100px;
	padding: 16px 35px;
}

/* ---------------------------------------------------------------------------
   The header's button is smaller than the page's — 44px against 59px.

   At the page's size it left 3.9px of air above and below inside the 67px
   stuck bar, which read as cramped. Shrinking it only here rather than
   site-wide, for two reasons:

   - The space problem exists only in the header. The buttons in the page body
     are the site's calls to action and there is nothing to gain from making
     them smaller.
   - 44px is the touch-target size platform guidance asks for. Taking every
     button on the site down to 40px would put all of them just under it, on
     phones most of all.

   penguinmep.com has NO button in its header — just the logo and the nav — so
   its slim sticky bar never had to solve this. Ours carries "Contact us"
   because Contact is deliberately not a nav link (see parts/header.html).

   line-height is stated rather than inherited so the height is arithmetic and
   not a consequence of the root 1.7: 24px text + 10px + 10px = 44px.
   --------------------------------------------------------------------------- */
.pps-header .wp-element-button {
	padding: 10px 28px;
	line-height: 1.5;
}

/* ---------------------------------------------------------------------------
   Interactive states — buttons.

   The hover COLOUR lives in theme.json (so the block editor shows the same
   thing); the movement lives here, because theme.json cannot express transform.

   Why the hover keeps a yellow fill instead of inverting like penguinmep.com:
   theirs goes transparent on hover, which works because every button on their
   page sits on something dark. Ours do not. Measured against the real
   backgrounds, the previous #1C1C1C hover scored 1.13:1 on the closing CTA
   (charcoal under a 78% black scrim) and 1.67:1 on the charcoal header — the
   pill disappeared entirely on both, leaving floating yellow text.

   A pill that supplies its own light fill cannot vanish, whatever is behind it:
   12.45:1 on the CTA, 6.58:1 on the header, and 6.58:1 for the charcoal label
   sitting on it. The one weak number is the pill's EDGE against white (1.55:1)
   — but that is the base state's number too (1.49:1), so hovering improves it
   rather than making it worse. Fixing that properly means changing the brand
   yellow, which is the client's call and not this change's business.
   --------------------------------------------------------------------------- */
.pps-main .wp-element-button,
.pps-header .wp-element-button {
	transition: background-color 0.2s ease, color 0.2s ease, transform 0.2s ease,
		box-shadow 0.2s ease;
}

.pps-main .wp-element-button:hover,
.pps-main .wp-element-button:focus-visible,
.pps-header .wp-element-button:hover,
.pps-header .wp-element-button:focus-visible {
	transform: translateY(-1px);
	box-shadow: var(--wp--preset--shadow--lifted);
}

/* ---------------------------------------------------------------------------
   Interactive states — cards.

   `lifted` has been sitting in theme.json's shadow presets unused since the
   build; this is what it was for. The card already clips its own overflow, so
   the photograph can scale inside a frame that does not move.

   :focus-within as well as :hover — every card's title is a link, so tabbing
   through the Projects grid gives the same feedback a mouse does.
   --------------------------------------------------------------------------- */
.pps-card {
	transition: transform 0.25s ease, box-shadow 0.25s ease;
}

.pps-card:hover,
.pps-card:focus-within {
	transform: translateY(-4px);
	box-shadow: var(--wp--preset--shadow--lifted);
}

.pps-card__media img {
	transition: transform 0.45s ease;
}

.pps-card:hover .pps-card__media img,
.pps-card:focus-within .pps-card__media img {
	transform: scale(1.04);
}

/* ---------------------------------------------------------------------------
   Entrance — a short fade and drop as an element scrolls into view, matching
   the `fadeInDown` penguinmep.com puts on its buttons.

   THE HIDDEN STATE IS GATED ON `.pps-js`, which a tiny inline script in the
   document head adds. That matters: if the reveal CSS hid things by default and
   the script never arrived, the content would be invisible for good. No
   JavaScript, a blocked file, an old browser — none of them can hide anything,
   because none of them add the class. The same inline script also strips the
   class after two seconds if assets/js/reveal.js has not checked in.

   The classes are REMOVED once the animation finishes (see reveal.js). They
   have to be: `.pps-js .pps-reveal.is-revealed` out-specifies `.pps-card:hover`,
   so leaving them on the element would hold `transform` at none and silently
   kill the card lift above.
   --------------------------------------------------------------------------- */
.pps-js .pps-reveal {
	opacity: 0;
}

/* Never leave a focused control invisible.
   The gallery frames are links now, so they are in the tab order while still
   waiting to be revealed. Tabbing scrolls, which normally trips the observer
   first — but "normally" is not a guarantee, and a keyboard user landing on
   something they cannot see is a genuine trap rather than a cosmetic slip. */
.pps-js .pps-reveal:focus-within {
	opacity: 1;
	animation: none;
}

.pps-js .pps-reveal.is-revealed {
	animation: pps-fade-down 0.5s ease both;
}

@keyframes pps-fade-down {
	from {
		opacity: 0;
		transform: translateY(-10px);
	}

	to {
		opacity: 1;
		transform: translateY(0);
	}
}

/* ---------------------------------------------------------------------------
   Gallery frames — hover zoom, and the affordance that goes with it.

   The zoom is the same treatment the cards get, clipped by the link rather than
   the card. `cursor: zoom-in` is the point: a photograph that grows under the
   pointer implies clicking does something, so it has to.
   --------------------------------------------------------------------------- */
.pps-gallery__link {
	display: block;
	overflow: hidden;
	border-radius: 2px;
	cursor: zoom-in;
}

.pps-gallery__link img {
	transition: transform 0.45s ease;
}

.pps-gallery__link:hover img,
.pps-gallery__link:focus-visible img {
	transform: scale(1.04);
}

/* ---------------------------------------------------------------------------
   The lightbox.

   Fixed to the viewport at a high z-index — above the sticky header, which sits
   at 100. `.pps-lightbox-open` on the root stops the page behind it scrolling;
   without it the backdrop moves under the picture as you scroll, which reads as
   the dialog being broken.
   --------------------------------------------------------------------------- */
.pps-lightbox-open {
	overflow: hidden;
}

/* ⚠️ THIS RULE IS LOAD-BEARING. Without it the lightbox cannot be closed.
   The browser's own `[hidden] { display: none }` is a user-agent rule, so ANY
   author `display` beats it — `display: flex` below included. `box.hidden = true`
   then sets the attribute and changes nothing on screen, and both the close
   button and Escape appear to do nothing while actually running correctly.
   Specificity (0,2,0) here beats (0,1,0) below, so no !important is needed. */
.pps-lightbox[hidden] {
	display: none;
}

.pps-lightbox {
	position: fixed;
	inset: 0;
	z-index: 200;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--wp--preset--spacing--20);
	padding: var(--wp--preset--spacing--40);
	background: rgb(0 0 0 / 88%);
}

.pps-lightbox__figure {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--wp--preset--spacing--20);
	margin: 0;
	/* The figure is a click target for closing, so it must not exceed the
	   viewport or the backdrop becomes unreachable. */
	max-inline-size: 100%;
	max-block-size: 100%;
	min-inline-size: 0;
}

.pps-lightbox__img {
	display: block;
	max-inline-size: 100%;
	/* Leaves room for the caption beneath. */
	max-block-size: calc(100vh - 10rem);
	inline-size: auto;
	block-size: auto;
	object-fit: contain;
	border-radius: 2px;
}

.pps-lightbox__caption {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	justify-content: center;
	gap: 0.75rem;
	max-inline-size: 60ch;
	color: var(--wp--preset--color--white);
	font-size: 0.9375rem;
	text-align: center;
}

.pps-lightbox__counter {
	color: var(--wp--preset--color--pale);
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}

/* Same trap as above: lightbox.js sets `.hidden` on the arrows when a gallery
   holds a single image, and `display: flex` below would override it. Not
   currently reachable — every gallery on the site has several frames — but it
   is one image away from being a bug, and invisible-but-present controls are
   exactly what the script's focus trap counts on `.hidden` to exclude. */
.pps-lightbox__nav[hidden] {
	display: none;
}

.pps-lightbox__close,
.pps-lightbox__nav {
	flex: none;
	display: flex;
	align-items: center;
	justify-content: center;
	/* 44px: the same touch target the header button uses. */
	inline-size: 44px;
	block-size: 44px;
	padding: 0;
	border: 0;
	border-radius: 100px;
	background: rgb(255 255 255 / 12%);
	color: var(--wp--preset--color--white);
	font-size: 1.75rem;
	line-height: 1;
	cursor: pointer;
	transition: background-color 0.2s ease;
}

.pps-lightbox__close:hover,
.pps-lightbox__nav:hover {
	background: rgb(255 255 255 / 24%);
}

.pps-lightbox__close:focus-visible,
.pps-lightbox__nav:focus-visible {
	outline: 2px solid var(--wp--preset--color--accent);
	outline-offset: 2px;
}

.pps-lightbox__close {
	position: absolute;
	inset-block-start: var(--wp--preset--spacing--30);
	inset-inline-end: var(--wp--preset--spacing--30);
}

/* On a phone the arrows would otherwise squeeze the picture to nothing. */
@media (max-width: 600px) {
	.pps-lightbox {
		flex-wrap: wrap;
		padding: var(--wp--preset--spacing--30);
	}

	.pps-lightbox__figure {
		order: -1;
		inline-size: 100%;
	}

	.pps-lightbox__img {
		max-block-size: calc(100vh - 14rem);
	}
}

/* Motion is decoration here — every state it conveys is also carried by colour
   and shadow, so removing it costs nothing. */
@media (prefers-reduced-motion: reduce) {
	.pps-main .wp-element-button,
	.pps-header .wp-element-button,
	.pps-card,
	.pps-card__media img,
	.pps-gallery__link img {
		transition: none;
	}

	.pps-gallery__link:hover img,
	.pps-gallery__link:focus-visible img {
		transform: none;
	}

	.pps-main .wp-element-button:hover,
	.pps-main .wp-element-button:focus-visible,
	.pps-header .wp-element-button:hover,
	.pps-header .wp-element-button:focus-visible,
	.pps-card:hover,
	.pps-card:focus-within,
	.pps-card:hover .pps-card__media img,
	.pps-card:focus-within .pps-card__media img {
		transform: none;
	}

	.pps-js .pps-reveal {
		opacity: 1;
	}

	.pps-js .pps-reveal.is-revealed {
		animation: none;
	}
}

/* --- Inner pages --------------------------------------------------------- */

/* The intro carries the page's H1, so it needs a little more air above it than
   a mid-page section. */
.pps-intro {
	padding-block-start: var(--wp--preset--spacing--60);
}

.pps-intro h1 {
	margin-block-start: 0.25rem;
}

.pps-prose--center {
	max-inline-size: 760px;
	margin-inline: auto;
	text-align: center;
}

/* --- Process steps ------------------------------------------------------- */

ol.pps-steps {
	list-style: none;
}

.pps-step {
	background: var(--wp--preset--color--white);
	border: 1px solid var(--wp--preset--color--pale);
	border-radius: 2px;
	padding: var(--wp--preset--spacing--40);
}

.pps-step > h3 {
	display: flex;
	align-items: center;
	font-size: 1.1875rem;
	margin-block: 0 0.5rem;
}

.pps-step__body {
	font-size: 1rem;
}

.pps-step__body p:last-child {
	margin-block-end: 0;
}

/* --- Contact ------------------------------------------------------------- */

.pps-contact-list {
	list-style: none;
	margin: var(--wp--preset--spacing--40) 0 0;
	padding: 0;
}

.pps-contact-list li {
	display: flex;
	flex-wrap: wrap;
	gap: 0.25rem 1rem;
	padding-block: 0.75rem;
	border-block-end: 1px solid var(--wp--preset--color--pale);
}

.pps-contact-list__label {
	flex: 0 0 5.5rem;
	font-family: var(--wp--preset--font-family--poppins);
	font-size: 0.8125rem;
	font-weight: 600;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--wp--preset--color--secondary);
}

/* The contact page reuses .pps-split for two TEXT columns of very unequal
   height, where the inherited align-items:center pushes the shorter column's
   heading visibly out of line with the other. Top-align that case. */
.pps-contact-split {
	align-items: start;
}

.pps-map {
	margin-block-start: var(--wp--preset--spacing--40);
}

.pps-map iframe {
	display: block;
	inline-size: 100%;
	min-block-size: 320px;
	border: 0;
}

/* Give the form room and let its fields use the full column width. */
.pps-form {
	margin-block-start: var(--wp--preset--spacing--40);
}

.pps-form input[type="text"],
.pps-form input[type="email"],
.pps-form input[type="tel"],
.pps-form textarea,
.pps-form select {
	inline-size: 100%;
	max-inline-size: 100%;
}

/* --- Case studies -------------------------------------------------------- */

/* A project hero is shorter than a page hero: the title and summary are the
   whole content, so 72vh leaves an awkward gap. */
.pps-hero--project {
	min-block-size: 46vh;
}

/* Project heroes centre the title and summary (2026-10-07). The page heroes
   stay left-aligned. The scrim behind the text is never lighter than 60%. */
.pps-hero--project .pps-hero__inner {
	margin-inline: auto;
	text-align: center;
}

/* Example-project banner. Deliberately conspicuous — a demo case study must
   never be mistaken for real client work, including by the client. */
.pps-notice {
	background: var(--wp--preset--color--accent);
	color: var(--wp--preset--color--primary);
	padding-block: var(--wp--preset--spacing--30);
	font-size: 1rem;
}

.pps-notice p {
	margin: 0;
}

/* Case-study narrative. It fills the 1000px .pps-container--project, so it
   lines up with the sections above and below it. It used to have its own 760px
   measure, removed 2026-10-07 when the page went to a single 1000px column. */
.pps-prose--article > * + * {
	margin-block-start: 1.25em;
}

.pps-prose--article h2,
.pps-prose--article h3 {
	margin-block-start: 2em;
}

.pps-prose--article img {
	max-inline-size: 100%;
	block-size: auto;
	border-radius: 2px;
}

/* --- Case-study layout --------------------------------------------------- */

/* Project details and scope of works stack, one above the other. */
.pps-project-details > * + * {
	margin-block-start: var(--wp--preset--spacing--60);
}

/* --- Key facts (Location, Type of work) ---

   Two states:

   - SIDE BY SIDE (desktop, when they fit): 60px apart, each label only as wide
     as its own text.
   - STACKED (mobile and tablet below 783px, or desktop once a long value pushes
     them onto separate rows): one shared label column sized to the longest
     label, so the values line up down the page.

   CSS cannot tell when a flex row has wrapped, so assets/js/key-facts.js adds
   .is-stacked in that desktop case. Without the script, a wrapped desktop list
   just keeps the natural label widths.

   Labels never wrap ("Type of work" used to break at a fixed 5.5rem).

   Label and value sit on a shared baseline, not centred, on purpose.
   align-items: center lines up the middle of the label's small capitals with
   the middle of the value's capitals, but the value is mostly lowercase and the
   eye reads its centre at x-height, so the label looked ~1.5px high. On a
   shared baseline the label's cap-middle lands within 0.5px of the value's
   x-height middle, which reads as centred. Measured 2026-10-07.

   Project page only. The contact page keeps its own list styles. */
.pps-project-details .pps-contact-list li {
	align-items: baseline;
}

.pps-project-details .pps-contact-list__label {
	flex: 0 0 auto;
	white-space: nowrap;
}

/* Stacked. Subgrid keeps each li as a box, so its bottom border survives. */
.pps-project-details .pps-contact-list.is-stacked {
	display: grid;
	grid-template-columns: max-content 1fr;
}

.pps-project-details .pps-contact-list.is-stacked li {
	display: grid;
	grid-column: 1 / -1;
	grid-template-columns: subgrid;
	column-gap: 1rem;
}

@media (max-width: 782px) {
	.pps-project-details .pps-contact-list {
		display: grid;
		grid-template-columns: max-content 1fr;
	}

	.pps-project-details .pps-contact-list li {
		display: grid;
		grid-column: 1 / -1;
		grid-template-columns: subgrid;
		column-gap: 1rem;
	}
}

/* Side by side. Each item's basis is its own content width, so they wrap only
   when they no longer fit on one line, then grow to share the width. The
   columns are close to, not exactly, equal. Equal columns were tried with
   flex-basis 0 and min-inline-size: max-content, but that lets one long value
   overflow the page, and min(max-content, 100%) is not valid in Chrome. */
@media (min-width: 783px) {
	.pps-project-details .pps-contact-list {
		display: flex;
		flex-wrap: wrap;
		column-gap: 60px;
	}

	.pps-project-details .pps-contact-list li {
		flex: 1 1 max-content;
	}

	/* Scope of works bullets in two columns, read down the left then down
	   the right, with the same 60px gap as the key facts above. The second
	   column's markers sit inside that gap. */
	.pps-project-details .pps-prose ul {
		columns: 2;
		column-gap: 60px;
	}

	.pps-project-details .pps-prose li {
		break-inside: avoid;
	}

	/* With a row count from the template, a grid replaces the columns. It
	   still fills down the left then the right, but the two columns share row
	   tracks, so when a bullet wraps onto two lines, its partner in the other
	   column takes the same height and the rows stay level. */
	.pps-project-details .pps-scope--grid ul {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		grid-template-rows: repeat(var(--pps-scope-rows), auto);
		grid-auto-flow: column;
		columns: auto;
	}
}

/* Before/after galleries. */
.pps-gallery__heading {
	font-family: var(--wp--preset--font-family--poppins);
	font-size: 0.9375rem;
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--wp--preset--color--secondary);
	margin-block: var(--wp--preset--spacing--50) 0;
}

.pps-gallery {
	list-style: none;
	margin: var(--wp--preset--spacing--30) 0 0;
	padding: 0;
	display: grid;
	gap: var(--wp--preset--spacing--30);
	grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}

.pps-gallery img {
	display: block;
	inline-size: 100%;
	block-size: auto;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	border-radius: 2px;
	box-shadow: var(--wp--preset--shadow--card);
}

/* Listing card badge for an example project. */
.pps-card__badge {
	display: inline-block;
	margin-block-end: 0.5rem;
	padding: 0.15rem 0.5rem;
	background: var(--wp--preset--color--accent);
	color: var(--wp--preset--color--primary);
	font-family: var(--wp--preset--font-family--poppins);
	font-size: 0.6875rem;
	font-weight: 700;
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

/* --- Pagination ---------------------------------------------------------- */

.pps-pagination {
	margin-block-start: var(--wp--preset--spacing--50);
}

.pps-pagination ul {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	justify-content: center;
}

.pps-pagination a,
.pps-pagination span {
	display: inline-block;
	min-inline-size: 2.75rem;
	padding: 0.6rem 0.9rem;
	text-align: center;
	text-decoration: none;
	border: 1px solid var(--wp--preset--color--pale);
	border-radius: 100px;
	font-family: var(--wp--preset--font-family--poppins);
	font-size: 0.9375rem;
}

.pps-pagination a:hover,
.pps-pagination a:focus-visible {
	background: var(--wp--preset--color--accent);
	border-color: var(--wp--preset--color--accent);
	color: var(--wp--preset--color--primary);
}

/* The current page is a <span>, not a link — give it the filled treatment. */
.pps-pagination .current {
	background: var(--wp--preset--color--primary);
	border-color: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--white);
	font-weight: 600;
}

/* Team cards keep their portrait taller than a landscape service card. */
/* Team portraits are square, not 4:3. This selector previously matched NOTHING
   — no template emitted a `pps-team` class — so team photographs silently took
   .pps-card__media's 4:3 landscape crop and had faces cut off top and bottom.
   template-about.php now emits the class. */
.pps-team .pps-card__media img {
	aspect-ratio: 1 / 1;
}

/* --- WPForms ------------------------------------------------------------- */

/* WPForms ships its own base styling, which does not know about our tokens.
   These rules bring it onto the brand — same flat 2px-radius fields and accent
   submit as penguinmep's Elementor form — without fighting the plugin's layout. */

.pps-form .wpforms-field {
	padding: 0 0 var(--wp--preset--spacing--30);
}

.pps-form .wpforms-field-label {
	font-family: var(--wp--preset--font-family--poppins);
	font-size: 0.875rem;
	font-weight: 600;
	color: var(--wp--preset--color--primary);
}

.pps-form input[type="text"],
.pps-form input[type="email"],
.pps-form input[type="tel"],
.pps-form input[type="number"],
.pps-form textarea,
.pps-form select {
	border: 1px solid var(--wp--preset--color--pale);
	border-radius: 2px;
	padding: 0.7rem 0.85rem;
	font-family: var(--wp--preset--font-family--raleway);
	font-size: 1rem;
	color: var(--wp--preset--color--body);
	background: var(--wp--preset--color--white);
}

.pps-form input:focus,
.pps-form textarea:focus,
.pps-form select:focus {
	border-color: var(--wp--preset--color--primary);
	outline: 2px solid var(--wp--preset--color--primary);
	outline-offset: 1px;
}

.pps-form textarea {
	min-block-size: 8rem;
	resize: vertical;
}

/* Submit: identical to the header's "Contact us" button, at rest and on hover
   (2026-10-07). That is the compact 44px pill (see the header button notes
   above), theme.json's #EAD01E hover fill, and the shared 1px lift and shadow.

   The selector is long on purpose. WPForms styles the button through
   `div.wpforms-container-full button[type="submit"]` (0,2,2), which beat the
   shorter `.pps-form .wpforms-submit` this used to be, so the button stayed
   WPForms blue with square corners. */
.pps-form div.wpforms-container-full .wpforms-form button[type="submit"].wpforms-submit {
	block-size: auto;
	padding: 10px 28px;
	border: 0;
	border-radius: 100px;
	background: var(--wp--preset--color--accent);
	color: var(--wp--preset--color--primary);
	font-family: var(--wp--preset--font-family--poppins);
	font-size: var(--wp--preset--font-size--medium);
	font-weight: 600;
	letter-spacing: 0.02em;
	line-height: 1.5;
	box-shadow: none;
	cursor: pointer;
	transition: background-color 0.2s ease, color 0.2s ease, transform 0.2s ease,
		box-shadow 0.2s ease;
}

.pps-form div.wpforms-container-full .wpforms-form button[type="submit"].wpforms-submit:hover,
.pps-form div.wpforms-container-full .wpforms-form button[type="submit"].wpforms-submit:focus-visible {
	background: #EAD01E;
	color: var(--wp--preset--color--primary);
	transform: translateY(-1px);
	box-shadow: var(--wp--preset--shadow--lifted);
}

/* Keyboard focus ring. style.css gives every button one, but WPForms' own
   button focus rule outranks it, so without this Send had no visible focus.
   Charcoal, not the header's yellow: the form sits on a light section, where
   yellow is 1.42:1 (style.css, "Visible keyboard focus"). */
.pps-form div.wpforms-container-full .wpforms-form button[type="submit"].wpforms-submit:focus-visible {
	outline: 3px solid var(--wp--preset--color--primary);
	outline-offset: 2px;
}

/* As for every other button: no lift under reduced motion. Repeated here
   because the general reduced-motion block above cannot outrank this selector. */
@media (prefers-reduced-motion: reduce) {
	.pps-form div.wpforms-container-full .wpforms-form button[type="submit"].wpforms-submit {
		transition: none;
	}

	.pps-form div.wpforms-container-full .wpforms-form button[type="submit"].wpforms-submit:hover,
	.pps-form div.wpforms-container-full .wpforms-form button[type="submit"].wpforms-submit:focus-visible {
		transform: none;
	}
}

/* Required marker and errors need to be legible, not decorative. */
.pps-form .wpforms-required-label {
	color: #B3261E;
}

.pps-form .wpforms-error,
.pps-form label.wpforms-error {
	color: #B3261E;
	font-size: 0.875rem;
	font-weight: 600;
}

.pps-form .wpforms-confirmation-container-full {
	border: 0;
	border-inline-start: 4px solid var(--wp--preset--color--accent);
	background: var(--wp--preset--color--offwhite);
	padding: var(--wp--preset--spacing--40);
}

/* --- Skip link ----------------------------------------------------------- */

.skip-link.screen-reader-text {
	position: absolute;
	inset-inline-start: -9999px;
	z-index: 999;
}

.skip-link.screen-reader-text:focus {
	inset-inline-start: 1rem;
	inset-block-start: 1rem;
	inline-size: auto;
	block-size: auto;
	padding: 0.75rem 1.25rem;
	background: var(--wp--preset--color--accent);
	color: var(--wp--preset--color--primary);
	font-weight: 700;
}
