/*
Theme Name: Sebo Theme
Theme URI: https://sebomarketing.com
Author: Sebo Marketing
Author URI: https://sebomarketing.com
Description: Brand-agnostic WordPress block theme used as the starting point for Sebo Marketing client sites. All colour, type, and spacing decisions live as design tokens in theme.json; this stylesheet only covers what theme.json cannot express. WooCommerce-ready.
Requires at least: 6.6
Tested up to: 7.0
Requires PHP: 8.1
Version: 1.0.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: sebo
Tags: block-theme, full-site-editing, e-commerce, one-column, custom-colors, custom-menu, editor-style
*/

/* -------------------------------------------------------------------------
   1. Base
   ---------------------------------------------------------------------- */

:root {
	/* 3:1 against the field background, per WCAG 1.4.11. A hairline that
	   fails contrast is the thing that tells someone where the input is. */
	--sb-field-border: color-mix(in srgb, var(--wp--preset--color--ink) 58%, var(--wp--preset--color--base));
}

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

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

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

body {
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	text-rendering: optimizeLegibility;
	font-optical-sizing: auto;
}

/* Balance short headings rather than leaving a single orphaned word. */
h1,
h2,
h3,
.wp-block-post-title {
	text-wrap: balance;
}

p,
li {
	text-wrap: pretty;
}

/* Body copy caps its measure even inside wide containers. */
:where(.entry-content) p:not([class*="has-"]) {
	max-width: 68ch;
}

img,
video {
	max-width: 100%;
	height: auto;
}

/* WordPress puts a block-gap margin between every top-level block, which
   leaves a white strip between the header and the first full-bleed section,
   and another between the last section and the footer. Sections carry their
   own padding, so those gaps are never wanted here. */
.wp-site-blocks > main,
.wp-site-blocks > .wp-block-template-part {
	margin-block-start: 0;
}

/* -------------------------------------------------------------------------
   2. Focus — a single visible treatment everywhere.
      Never remove this; keyboard users depend on it.
   ---------------------------------------------------------------------- */

/* A single-colour focus ring cannot work on this palette. `action` (maroon) was
   invisible on the maroon buttons and on the maroon CTA band; swapping it for
   `base` on dark grounds then made it invisible on the white buttons sitting on
   those bands. Measured, 22 focusable elements failed WCAG 1.4.11.

   Two tones fixes it in one rule: a white ring hugging the element, a near-black
   ring outside that. On a light ground the dark ring carries it; on navy or
   maroon the white ring does; on a white button on a dark band, the dark ring
   again. There is no ground this theme can produce where both disappear. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
	outline: 3px solid var(--wp--preset--color--base);
	outline-offset: 0;
	box-shadow: 0 0 0 6px var(--wp--preset--color--ink);
	border-radius: 2px;
}

/* No per-ground override: the two-tone ring above already covers dark bands.
   The framework shipped `outline-color: accent` here, which worked only while
   the starter's accent was a pale mint. On this brand accent IS the navy the
   footer is painted with, so it measured 1.00:1 — a focus indicator that did
   not exist. Do not reintroduce a single-colour override. */

.skip-link {
	position: absolute;
	top: -100vh;
	left: 0;
	z-index: 999999;
	padding: 0.85rem 1.5rem;
	background: var(--wp--preset--color--ink);
	color: var(--wp--preset--color--base);
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--small);
	font-weight: 600;
	text-decoration: none;
	border-bottom-right-radius: 6px;
}

.skip-link:focus {
	top: 0;
}

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

/* -------------------------------------------------------------------------
   3. Links — underlines that sit below the baseline rather than
      slicing through descenders.
   ---------------------------------------------------------------------- */

:where(.entry-content, .wp-block-post-content) a:where(:not(.wp-element-button):not(.wp-block-button__link)) {
	text-decoration-thickness: 1px;
	text-underline-offset: 0.18em;
	text-decoration-color: color-mix(in srgb, var(--wp--preset--color--action) 42%, transparent);
	transition: text-decoration-color var(--wp--custom--transition--base);
}

:where(.entry-content, .wp-block-post-content) a:where(:not(.wp-element-button)):hover {
	text-decoration-color: currentColor;
}

/* Buttons and nav links must not inherit the content underline. */
.wp-block-navigation a,
.wp-element-button,
.wp-block-button__link {
	text-decoration: none;
}

.wp-element-button,
.wp-block-button__link {
	transition: background-color var(--wp--custom--transition--base),
		color var(--wp--custom--transition--base),
		transform var(--wp--custom--transition--base);
}

.wp-element-button:hover,
.wp-block-button__link:hover {
	transform: translateY(-1px);
}

/* Secondary button: outlined, for lower-priority actions. */
.wp-block-button.is-style-outline > .wp-block-button__link {
	background: transparent;
	color: var(--wp--preset--color--action);
	border: 1.5px solid var(--wp--preset--color--action);
}

.wp-block-button.is-style-outline > .wp-block-button__link:hover {
	background: var(--wp--preset--color--action);
	color: var(--wp--preset--color--base);
}

/* -------------------------------------------------------------------------
   4. Header
   ---------------------------------------------------------------------- */

/* header.js adds this to <html> for the length of one measurement, so it can
   read the header's expanded height instead of whatever height the transition
   happened to be part-way through. Removed in the same frame. */
.sb-measuring *,
.sb-measuring *::before,
.sb-measuring *::after {
	transition: none !important;
	animation: none !important;
}

/* Stickiness has to live on the template-part wrapper, not on .sb-header.
   WordPress renders the header part inside a <div class="wp-block-template-part">
   that is exactly as tall as the header, and a sticky element cannot travel
   beyond its containing block — so sticking it to .sb-header gives it zero
   travel and it scrolls away immediately. */
.wp-site-blocks > .wp-block-template-part:has(.sb-header),
.wp-site-blocks > .wp-block-template-part:first-child {
	position: sticky;
	/* The admin bar is position:fixed at the top of the viewport for logged-in
	   users, so a header stuck to 0 slides underneath it. WordPress exposes the
	   bar's height as a custom property (32px, or 46px under 782px), which also
	   keeps this correct when the bar is absent. */
	top: var(--wp-admin--admin-bar--height, 0px);
	z-index: 100;
}

/* Fallback for WordPress versions that do not set the custom property. */
body.admin-bar .wp-site-blocks > .wp-block-template-part:has(.sb-header),
body.admin-bar .wp-site-blocks > .wp-block-template-part:first-child {
	top: var(--wp-admin--admin-bar--height, 32px);
}

@media screen and (max-width: 782px) {
	body.admin-bar .wp-site-blocks > .wp-block-template-part:has(.sb-header),
	body.admin-bar .wp-site-blocks > .wp-block-template-part:first-child {
		top: var(--wp-admin--admin-bar--height, 46px);
	}
}

/* Below 600px the admin bar stops being fixed and scrolls away with the page,
   so the header must sit flush again or it leaves a 46px gap. */
@media screen and (max-width: 600px) {
	body.admin-bar .wp-site-blocks > .wp-block-template-part:has(.sb-header),
	body.admin-bar .wp-site-blocks > .wp-block-template-part:first-child {
		top: 0;
	}
}

/* The frosted background lives on a pseudo-element, not on .sb-header itself.
   backdrop-filter makes an element the containing block for its
   position:fixed descendants, which collapsed the mobile menu overlay to the
   height of the header instead of the viewport. Keeping the filter on a
   childless pseudo-element preserves the effect without that side effect. */
.sb-header {
	position: relative;
	background: transparent;
	border-bottom: var(--wp--custom--rule--hairline);
}

.sb-header::before {
	content: "";
	position: absolute;
	inset: 0;
	pointer-events: none;
	background: rgba(255, 255, 255, 0.92);
	backdrop-filter: saturate(180%) blur(12px);
	-webkit-backdrop-filter: saturate(180%) blur(12px);
}

.sb-header__bar {
	position: relative;
	z-index: 1;
}

/* The bar carries the vertical rhythm, and shrinks in the compact state.
   Padding is on the bar rather than the header so the sticky background
   still spans the full viewport width. */
.sb-header__bar {
	padding-block: clamp(0.85rem, 1.6vw, 1.35rem);
	transition: padding-block var(--wp--custom--transition--base);
}

.sb-header .custom-logo-link,
.sb-header .wp-block-site-logo a {
	display: block;
	line-height: 0;
}

.sb-header .wp-block-site-logo img {
	/* The lockup is 5.89:1 and carries a tracked-caps descriptor, so its
	   legibility is set by width, not height: at 198px the descriptor's cap
	   height is 5.7px. See the min-width:1181px block below, which is where
	   this grows. */
	width: clamp(156px, 17vw, 198px);
	height: auto;
	transition: opacity var(--wp--custom--transition--base);
}

/* Wide viewports: a bigger lockup in the SAME bar height.
   Measured at 1440 before changing anything: the bar was 86px — 21.6px padding
   top and bottom around a 42.8px row — and the logo was only 33.6px of that
   row, because the nav links (45.9px) were setting the height. So the logo had
   9px of free headroom AND 21.6px of padding to trade against.

   Confined to >1180px on purpose. Between 901 and 1180 the nav, the phone
   number and the button still share the bar, so there is no headroom to take.
   (That band used to start at 783 and the nav wrapped to three rows inside it;
   the mobile header now covers everything up to 900px, which removed the worst
   of it.) Above 1180px there is 93-129px of measured horizontal slack. */
@media (min-width: 1181px) {
	.sb-header .wp-block-site-logo img {
		width: clamp(216px, 21vw, 304px);
	}

	.sb-header__bar {
		padding-block: clamp(0.75rem, 1.1vw, 1rem);
	}
}

.sb-header .wp-block-site-logo a:hover img {
	opacity: 0.78;
}

/* -----------------------------------------------------------------
   Compact (scrolled) header.

   The `is-compact` class is toggled by assets/js/header.js past a
   scroll threshold. Only the two-tile PL mark shows and the bar loses
   height, so more of the page stays visible while scrolling.
   ----------------------------------------------------------------- */

.sb-brand__mark {
	display: none;
	margin: 0;
	line-height: 0;
}

.sb-brand__mark img {
	/* The mark is 2:1, so width sets height. The compact bar measures 59px with
	   8px of padding, giving a 43px content row, and 46px here rendered only
	   23px tall — half the row, reading as a chip next to 20px nav text. 72px
	   fills the row (36px) with a little air and does not reopen the bar. */
	display: block;
	width: 72px;
	height: auto;
	transition: opacity var(--wp--custom--transition--base);
}

.sb-brand__mark a:hover img {
	opacity: 0.78;
}

.sb-header.is-compact .sb-header__bar {
	padding-block: 0.5rem;
}

.sb-header.is-compact .wp-block-site-logo {
	display: none;
}

.sb-header.is-compact .sb-brand__mark {
	display: block;
}

/* Without JS the header simply stays in its full state — the mark is the
   progressive enhancement, not the baseline. */

/* Nav links: an animated rule that grows from the left on hover, and
   stays put for the current page. */
.sb-header .wp-block-navigation-item__content {
	position: relative;
	padding-block: 0.35rem;
	color: var(--wp--preset--color--ink);
}

.sb-header .wp-block-navigation-item__content::after {
	content: "";
	position: absolute;
	left: 0;
	bottom: 0;
	width: 100%;
	height: 2px;
	background: var(--wp--preset--color--accent);
	transform: scaleX(0);
	transform-origin: left;
	transition: transform var(--wp--custom--transition--base);
}

.sb-header .wp-block-navigation-item:hover .wp-block-navigation-item__content::after,
.sb-header .wp-block-navigation-item.current-menu-item .wp-block-navigation-item__content::after,
.sb-header .wp-block-navigation-item.current-menu-ancestor .wp-block-navigation-item__content::after {
	transform: scaleX(1);
}

/* Submenu (the Books dropdown). */
.sb-header .wp-block-navigation__submenu-container {
	border: none;
	border-radius: 10px;
	padding: 0.4rem;
	min-width: 15rem;
	background: var(--wp--preset--color--base);
	box-shadow: var(--wp--preset--shadow--lifted);
}

.sb-header .wp-block-navigation__submenu-container .wp-block-navigation-item__content {
	padding: 0.6rem 0.85rem;
	border-radius: 6px;
	width: 100%;
}

.sb-header .wp-block-navigation__submenu-container .wp-block-navigation-item__content::after {
	display: none;
}

.sb-header .wp-block-navigation__submenu-container .wp-block-navigation-item__content:hover {
	background: var(--wp--preset--color--surface);
	color: var(--wp--preset--color--action);
}

/* -----------------------------------------------------------------
   Mobile overlay menu

   Full-screen, translucent so the page stays faintly visible behind
   it, with a large close control. Woo/WP already render the open and
   close buttons; this is presentation only.
   ----------------------------------------------------------------- */

.wp-block-navigation__responsive-container.is-menu-open,
.wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) {
	padding: clamp(1.25rem, 5vw, 2.5rem);
	/* Warm wash — enough to read against, still showing the page beneath.
	   !important because Woo/WP set `background-color: inherit` here at equal
	   specificity, which otherwise picks up the header's opaque white. */
	background-color: rgba(247, 244, 236, 0.9) !important;
	backdrop-filter: blur(16px) saturate(150%);
	-webkit-backdrop-filter: blur(16px) saturate(150%);
	align-items: stretch;
	justify-content: flex-start;
}

/* Give the list room below the close button and centre it in the sheet. */
/* WordPress nests two wrappers between the overlay and the menu list
   (__responsive-close, then __responsive-dialog). Both must grow, or the
   content has no height to be centred within. */
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__responsive-close,
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__responsive-dialog {
	display: flex;
	flex-direction: column;
	flex: 1 1 auto;
	width: 100%;
	min-height: 0;
}

.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__responsive-container-content {
	display: flex;
	flex: 1 1 auto;
	flex-direction: column;
	align-items: center;
	/* Centre the list in the sheet rather than stacking it under the close
	   button with dead space below. */
	justify-content: center;
	padding-block: clamp(2rem, 8vh, 4rem);
	width: 100%;
	min-height: 0;
}

.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__container {
	gap: 0;
	width: 100%;
	max-width: 22rem;
	text-align: center;
	/* WordPress gives the list flex-grow, which pins the items to the top of
	   the sheet. Auto block margins centre it regardless of that. */
	flex: 0 0 auto;
	margin-block: auto;
}

.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation-item {
	width: 100%;
	justify-content: center;
	border-bottom: 1px solid color-mix(in srgb, var(--wp--preset--color--ink) 14%, transparent);
}

.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation-item:last-child {
	border-bottom: 0;
}

.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item__content {
	font-family: var(--wp--preset--font-family--heading);
	font-size: var(--wp--preset--font-size--x-large);
	font-weight: 500;
	color: var(--wp--preset--color--ink);
	/* Comfortable tap target. */
	padding-block: 0.85rem;
	width: 100%;
	justify-content: center;
}

/* The desktop hover rule draws an underline bar; not wanted in the sheet. */
.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation-item__content::after {
	display: none;
}

.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation-item__content:hover {
	color: var(--wp--preset--color--action);
}

/* Submenu sits quieter, always expanded in the sheet — and CENTRED.

   Core indents overlay submenus with `padding-left: 2rem`, which pushed the
   child items 17.2px right of the centred parents above them. A same-specificity
   override lost to it, so this selector adds the content wrapper to outrank it
   rather than reaching for !important. Padding is zeroed on both sides and the
   container is given the full menu width, so a child's text centre lands on the
   same axis as its parent's. */
.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__responsive-container-content
	.wp-block-navigation__submenu-container {
	box-shadow: none;
	background: transparent;
	border: 0;
	width: 100%;
	min-width: 0;
	padding-block: 0 0.5rem;
	padding-inline: 0;
	margin-inline: 0;
}

/* The parent item holds its own link and then the submenu beneath it, so it has
   to stack rather than sit as a flex row. */
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item.has-child {
	flex-direction: column;
	align-items: center;
}

/* The real cause of the off-centre child items, measured rather than guessed:
   the theme gives every overlay link `width: 100%`, and core gives SUBMENU links
   their own ~1.2rem horizontal padding on top. Without border-box that makes the
   child link wider than the <li> it sits in, and it overflows to one side — the
   text centre landed 19px off the parents above it. Zeroing the inline padding
   and counting the border in the width puts the child's centre back on the
   parent's axis. */
.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__submenu-container
	.wp-block-navigation-item__content {
	box-sizing: border-box;
	width: 100%;
	padding-inline: 0;
	justify-content: center;
}

.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__submenu-container
	.wp-block-navigation-item {
	border-bottom: 0;
}

.wp-block-navigation__responsive-container.is-menu-open
	.wp-block-navigation__submenu-container
	.wp-block-navigation-item__content {
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--medium);
	font-weight: 400;
	color: var(--wp--preset--color--muted);
	padding-block: 0.6rem;
}

/* Open (hamburger) control. */
.wp-block-navigation__responsive-container-open {
	padding: 0.4rem;
	color: var(--wp--preset--color--ink);
}

.wp-block-navigation__responsive-container-open:hover {
	color: var(--wp--preset--color--action);
}

/* Close (X) control — enlarged and given a visible hit area, since the
   default is a small bare glyph in the corner. */
.wp-block-navigation__responsive-container-close {
	top: clamp(1.25rem, 5vw, 2.5rem);
	right: clamp(1.25rem, 5vw, 2.5rem);
	display: flex;
	align-items: center;
	justify-content: center;
	width: 46px;
	height: 46px;
	border-radius: 999px;
	background: rgba(255, 255, 255, 0.9);
	color: var(--wp--preset--color--ink);
	box-shadow: var(--wp--preset--shadow--soft);
	transition: background-color var(--wp--custom--transition--base),
		color var(--wp--custom--transition--base);
}

.wp-block-navigation__responsive-container-close:hover {
	background: var(--wp--preset--color--action);
	color: var(--wp--preset--color--base);
}

.wp-block-navigation__responsive-container-close svg {
	width: 26px;
	height: 26px;
}

/* The overlay is fixed to the viewport, so it must clear the admin bar. */
body.admin-bar .wp-block-navigation__responsive-container.is-menu-open {
	top: var(--wp-admin--admin-bar--height, 46px);
}

/* -------------------------------------------------------------------------
   5. Cart indicator in the header
   ---------------------------------------------------------------------- */

.sb-cart {
	position: relative;
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	padding: 0.5rem;
	border-radius: 999px;
	color: var(--wp--preset--color--ink);
	text-decoration: none;
	transition: background-color var(--wp--custom--transition--base);
}

.sb-cart:hover {
	background: var(--wp--preset--color--surface);
	color: var(--wp--preset--color--action);
}

.sb-cart svg {
	width: 22px;
	height: 22px;
	display: block;
}

.sb-cart__count {
	position: absolute;
	top: -1px;
	right: -3px;
	min-width: 18px;
	height: 18px;
	padding: 0 5px;
	border-radius: 999px;
	background: var(--wp--preset--color--action);
	color: var(--wp--preset--color--base);
	font-family: var(--wp--preset--font-family--body);
	font-size: 11px;
	font-weight: 700;
	line-height: 18px;
	text-align: center;
}

.sb-cart__count:empty,
.sb-cart__count[data-count="0"] {
	display: none;
}

/* -------------------------------------------------------------------------
   6. Footer
   ---------------------------------------------------------------------- */

.sb-footer {
	background: var(--wp--preset--color--ink);
	color: rgba(255, 255, 255, 0.82);
}

/* The stacked lockup's fairy mark is hairline linework. At 200px those
   white strokes fall below a device pixel and alias into looking rough,
   which reads as pixelation even though the source is vector. Giving it
   more room is the actual fix. */
.sb-footer .wp-block-site-logo img,
.sb-footer__logo img {
	width: clamp(215px, 22vw, 275px);
	height: auto;
	/* Nudge the fine strokes away from sub-pixel rendering. */
	shape-rendering: geometricPrecision;
}

.sb-footer :where(h2, h3, h4, h5, h6) {
	color: var(--wp--preset--color--base);
}

.sb-footer a {
	color: rgba(255, 255, 255, 0.82);
	text-decoration: none;
}

.sb-footer a:hover {
	color: var(--wp--preset--color--accent);
	text-decoration: underline;
	text-underline-offset: 0.18em;
}

.sb-footer .wp-block-navigation {
	gap: 0.55rem;
}

.sb-footer__legal {
	border-top: 1px solid rgba(255, 255, 255, 0.14);
}

/* -------------------------------------------------------------------------
   7. Editorial pieces
   ---------------------------------------------------------------------- */

/* Eyebrow label above a section heading. */
.sb-eyebrow {
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--x-small);
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--wp--preset--color--action);
}

/* When the eyebrow is a term list (e.g. the product category on a book page)
   its links must not pick up the content underline — at this size and letter
   spacing an underline reads as noise. */
.sb-eyebrow a {
	color: inherit;
	text-decoration: none;
}

.sb-eyebrow a:hover {
	text-decoration: underline;
	text-underline-offset: 0.25em;
}

/* A framed image with depth, for a product shot or cover. */
.sb-object img {
	border-radius: 3px 10px 10px 3px;
	box-shadow: var(--wp--preset--shadow--lifted);
}

/* -----------------------------------------------------------------
   Immersive hero — artwork behind the copy

   A whole-image dim would flatten the painting, so the scrim is
   left-weighted: dark enough for white type on the copy side, and
   clear on the right where the cover sits.
   ----------------------------------------------------------------- */

.sb-hero-immersive {
	position: relative;
	overflow: hidden;
}

.sb-hero-immersive::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	pointer-events: none;
	background:
		linear-gradient(
			100deg,
			rgba(38, 30, 26, 0.88) 0%,
			rgba(38, 30, 26, 0.74) 36%,
			rgba(38, 30, 26, 0.34) 62%,
			rgba(38, 30, 26, 0.12) 100%
		);
}

/* On narrow screens the columns stack, so the copy sits over the middle of
   the image and needs an even wash instead of a side gradient. */
@media (max-width: 781px) {
	.sb-hero-immersive::before {
		background: linear-gradient(
			180deg,
			rgba(38, 30, 26, 0.72) 0%,
			rgba(38, 30, 26, 0.84) 55%,
			rgba(38, 30, 26, 0.72) 100%
		);
	}
}

.sb-hero-immersive .wp-block-cover__inner-container {
	position: relative;
	z-index: 2;
}

.sb-hero-immersive__eyebrow {
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--x-small);
	font-weight: 700;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	/* Brand green reads well against the dark scrim; it is only unusable
	   on light grounds. */
	color: var(--wp--preset--color--accent);
}

.sb-hero-immersive__meta {
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--x-small);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: rgba(255, 255, 255, 0.72);
}

.sb-hero-immersive h1 em {
	font-style: italic;
}

/* Lift the cover off the artwork so it reads as a physical object rather
   than part of the painting. */
.sb-object--floating img {
	box-shadow: 0 4px 12px rgba(20, 28, 25, 0.3), 0 28px 64px rgba(20, 28, 25, 0.45);
}

/* Outline button on a dark ground. */
.sb-btn-on-dark .wp-block-button__link,
.wp-block-button.is-style-outline.sb-btn-on-dark > .wp-block-button__link {
	color: var(--wp--preset--color--base);
	border-color: rgba(255, 255, 255, 0.65);
	background: transparent;
}

.sb-btn-on-dark .wp-block-button__link:hover,
.wp-block-button.is-style-outline.sb-btn-on-dark > .wp-block-button__link:hover {
	background: var(--wp--preset--color--base);
	color: var(--wp--preset--color--action);
	border-color: var(--wp--preset--color--base);
}

/* A soft rounded frame for photography and artwork. */
.sb-media img {
	border-radius: 14px;
}

/* Emphasised pull quote, for a tagline or short testimonial. */
.sb-lede-quote {
	font-family: var(--wp--preset--font-family--heading);
	font-size: var(--wp--preset--font-size--large);
	font-weight: 500;
	font-style: italic;
	line-height: 1.55;
	color: var(--wp--preset--color--action);
	text-align: center;
}

/* Decorative divider: a hairline rule either side of a small glyph. */
.sb-divider {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 0.75rem;
	color: var(--wp--preset--color--accent);
}

.sb-divider::before,
.sb-divider::after {
	content: "";
	height: 1px;
	flex: 1 1 auto;
	max-width: 7rem;
	background: currentColor;
}

/* Cards */
.sb-card {
	background: var(--wp--preset--color--base);
	border-radius: 16px;
	box-shadow: var(--wp--preset--shadow--soft);
	overflow: hidden;
	transition: box-shadow var(--wp--custom--transition--base),
		transform var(--wp--custom--transition--base);
}

.sb-card:hover {
	box-shadow: var(--wp--preset--shadow--lifted);
	transform: translateY(-3px);
}

/* -------------------------------------------------------------------------
   8. Blog
   ---------------------------------------------------------------------- */

/* The post grid is declared as a fixed 3-up in the templates, because
   combining columnCount with minimumColumnWidth made WordPress emit an
   unpredictable 2-up. Collapsing for narrower screens is done here. */
.wp-block-post-template.is-layout-grid {
	gap: var(--wp--preset--spacing--30);
}

@media (max-width: 1024px) {
	.wp-block-post-template.is-layout-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
	}
}

@media (max-width: 660px) {
	.wp-block-post-template.is-layout-grid {
		grid-template-columns: minmax(0, 1fr) !important;
	}
}

/* The blog intro sits in a wide container so its rule spans the grid; its
   text should start at the same left edge as the first card, not be centred
   inside it. */
.sb-blog-intro > * {
	margin-inline: 0;
}

.sb-blog-intro p {
	max-width: 58ch;
}

/* Cards stretch to the row height so the "Keep reading" link lands on a
   consistent baseline instead of floating wherever the excerpt ends. */
.sb-post-card {
	display: flex;
	flex-direction: column;
	height: 100%;
}

.sb-post-card .wp-block-post-excerpt {
	display: flex;
	flex-direction: column;
	flex: 1 1 auto;
}

.sb-post-card .wp-block-post-excerpt__more-text {
	margin-top: auto;
	padding-top: 0.75rem;
}

.sb-post-card .wp-block-post-excerpt__more-text a {
	font-weight: 600;
	text-decoration: none;
	border-bottom: 1px solid color-mix(in srgb, var(--wp--preset--color--action) 38%, transparent);
}

.sb-post-card .wp-block-post-excerpt__more-text a:hover {
	border-bottom-color: currentColor;
}

.sb-post-card .wp-block-post-featured-image img {
	border-radius: 12px;
	aspect-ratio: 3 / 2;
	object-fit: cover;
	width: 100%;
}

.sb-post-card .wp-block-post-title {
	font-size: var(--wp--preset--font-size--large);
	line-height: 1.25;
}

.sb-post-card .wp-block-post-title a {
	text-decoration: none;
	color: var(--wp--preset--color--ink);
}

.sb-post-card .wp-block-post-title a:hover {
	color: var(--wp--preset--color--action);
}

.sb-meta {
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--x-small);
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--wp--preset--color--muted);
}

.wp-block-query-pagination {
	font-size: var(--wp--preset--font-size--small);
	font-weight: 600;
}

/* -------------------------------------------------------------------------
   9. Forms — Gravity Forms and core inputs share one look
   ---------------------------------------------------------------------- */

input:where([type="text"], [type="email"], [type="url"], [type="tel"], [type="number"], [type="password"], [type="search"], [type="date"]),
select,
textarea,
.wp-block-search__input {
	width: 100%;
	padding: 0.8rem 1rem;
	background: var(--wp--preset--color--base);
	border: 1.5px solid var(--sb-field-border);
	border-radius: 8px;
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--medium);
	color: var(--wp--preset--color--ink);
	transition: border-color var(--wp--custom--transition--base),
		box-shadow var(--wp--custom--transition--base);
}

input:where([type="text"], [type="email"], [type="url"], [type="tel"], [type="number"], [type="password"], [type="search"]):focus,
select:focus,
textarea:focus {
	border-color: var(--wp--preset--color--action);
	/* No `outline: none` and no faint halo here. Both were in this file, and a
	   16%-opacity maroon glow is roughly 1.1:1 — not a focus indicator. The
	   border colour change is the hover/typing affordance; the ring comes from
	   the :focus-visible rule in section 2. */
}

::placeholder {
	color: color-mix(in srgb, var(--wp--preset--color--ink) 55%, transparent);
	opacity: 1;
}

label {
	display: block;
	margin-bottom: 0.4rem;
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--small);
	font-weight: 600;
	color: var(--wp--preset--color--ink);
}

/* -----------------------------------------------------------------
   Gravity Forms

   Selectors are doubled up on `.gform_wrapper.gravity-theme` where
   Gravity's own stylesheet would otherwise win on specificity.
   ----------------------------------------------------------------- */

.gform_wrapper.gravity-theme .gform_fields {
	grid-row-gap: var(--wp--preset--spacing--20);
	grid-column-gap: var(--wp--preset--spacing--20);
}

/* `.gform_fields` is load-bearing. Gravity ships
   `.gform_wrapper.gravity-theme .gfield_label` verbatim and its stylesheet is
   printed after the theme's, so an identical selector loses on source order
   and silently does nothing. */
.gform_wrapper.gravity-theme .gform_fields .gfield_label,
.gform_wrapper.gravity-theme .gform_fields legend.gfield_label,
.gform_wrapper.gravity-theme .gform_fields .gform-field-label {
	display: block;
	margin-bottom: 0.4rem;
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--small);
	font-weight: 600;
	color: var(--wp--preset--color--ink);
	letter-spacing: 0;
}

.gform_wrapper.gravity-theme .gfield_label .gfield_required,
.gform_wrapper.gravity-theme .gfield_label .gfield_required_text,
.gform_wrapper.gravity-theme .gfield_label .gfield_required_asterisk {
	color: #a4341f;
	font-weight: 600;
}

.gform_wrapper.gravity-theme .gform_fields .gfield_description {
	padding-top: 0.35rem;
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--x-small);
	color: var(--wp--preset--color--muted);
	line-height: 1.5;
}

/* Inputs: match the site's field styling, overriding Gravity's. */
.gform_wrapper.gravity-theme .ginput_container input[type="text"],
.gform_wrapper.gravity-theme .ginput_container input[type="email"],
.gform_wrapper.gravity-theme .ginput_container input[type="tel"],
.gform_wrapper.gravity-theme .ginput_container input[type="url"],
.gform_wrapper.gravity-theme .ginput_container input[type="number"],
.gform_wrapper.gravity-theme .ginput_container select,
.gform_wrapper.gravity-theme .ginput_container textarea {
	width: 100%;
	padding: 0.8rem 1rem;
	background: var(--wp--preset--color--base);
	border: 1.5px solid var(--sb-field-border);
	border-radius: 8px;
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--medium);
	color: var(--wp--preset--color--ink);
	line-height: 1.5;
	transition: border-color var(--wp--custom--transition--base),
		box-shadow var(--wp--custom--transition--base);
}

.gform_wrapper.gravity-theme .ginput_container textarea {
	min-height: 9rem;
	resize: vertical;
}

.gform_wrapper.gravity-theme .ginput_container input:focus,
.gform_wrapper.gravity-theme .ginput_container select:focus,
.gform_wrapper.gravity-theme .ginput_container textarea:focus {
	border-color: var(--wp--preset--color--action);
	/* No `outline: none` and no faint halo here. Both were in this file, and a
	   16%-opacity maroon glow is roughly 1.1:1 — not a focus indicator. The
	   border colour change is the hover/typing affordance; the ring comes from
	   the :focus-visible rule in section 2. */
}

/* Native select needs its own chevron once the default look is replaced. */
.gform_wrapper.gravity-theme .ginput_container select {
	appearance: none;
	-webkit-appearance: none;
	background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234A4E52' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 0.9rem center;
	background-size: 18px 18px;
	padding-right: 2.6rem;
}

/* Submit button — the brand pill. */
.gform_wrapper.gravity-theme .gform_footer input[type="submit"],
.gform_wrapper.gravity-theme .gform_footer button[type="submit"],
.gform_wrapper.gravity-theme .gform_page_footer input[type="button"],
.gform_wrapper.gravity-theme .gform_page_footer input[type="submit"] {
	width: auto;
	background: var(--wp--preset--color--action);
	color: var(--wp--preset--color--base);
	border: 0;
	border-radius: 999px;
	padding: 0.95rem 2.1rem;
	margin: 0;
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--small);
	font-weight: 600;
	letter-spacing: 0.03em;
	line-height: 1.2;
	text-transform: none;
	cursor: pointer;
	transition: background-color var(--wp--custom--transition--base),
		transform var(--wp--custom--transition--base);
}

.gform_wrapper.gravity-theme .gform_footer input[type="submit"]:hover,
.gform_wrapper.gravity-theme .gform_footer button[type="submit"]:hover {
	background: var(--wp--preset--color--ink);
	transform: translateY(-1px);
}

.gform_wrapper.gravity-theme .gform_footer {
	padding: var(--wp--preset--spacing--20) 0 0;
	margin: 0;
	justify-content: flex-start;
}

/* Validation */
.gform_wrapper.gravity-theme .gfield_description.validation_message,
.gform_wrapper.gravity-theme .validation_message {
	margin-top: 0.4rem;
	padding: 0.5rem 0.75rem;
	background: #fdf3f1;
	border: 0;
	border-left: 3px solid #a4341f;
	border-radius: 6px;
	font-size: var(--wp--preset--font-size--x-small);
	color: #8f2d1b;
}

/* These restate the base field selector's shape with `.gfield_error` added.
   Written as `.gfield_error input` alone they are LESS specific than the base
   rule, which carries an [type="..."] attribute selector, so an invalid field
   keeps its normal border and never looks wrong. */
.gform_wrapper.gravity-theme .gfield_error .ginput_container input[type="text"],
.gform_wrapper.gravity-theme .gfield_error .ginput_container input[type="email"],
.gform_wrapper.gravity-theme .gfield_error .ginput_container input[type="tel"],
.gform_wrapper.gravity-theme .gfield_error .ginput_container input[type="url"],
.gform_wrapper.gravity-theme .gfield_error .ginput_container input[type="number"],
.gform_wrapper.gravity-theme .gfield_error .ginput_container select,
.gform_wrapper.gravity-theme .gfield_error .ginput_container textarea,
.gform_wrapper.gravity-theme .gfield_error .gchoice {
	border-color: #a4341f;
	box-shadow: 0 0 0 3px rgba(164, 52, 31, 0.1);
}

.gform_wrapper.gravity-theme .gform_validation_errors {
	border-radius: 10px;
	border: 0;
	border-left: 3px solid #a4341f;
	background: #fdf3f1;
	box-shadow: none;
	padding: 1rem 1.25rem;
}

.gform_wrapper.gravity-theme .gform_validation_errors > h2 {
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--small);
	font-weight: 600;
	color: #8f2d1b;
	margin: 0;
}

.gform_wrapper.gravity-theme .gform_validation_errors > h2::before {
	display: none;
}

/* Inline confirmation, for any form not routed to a Thank You page. */
.gform_confirmation_message {
	padding: var(--wp--preset--spacing--30);
	background: var(--wp--preset--color--surface);
	border-left: 3px solid var(--wp--preset--color--action);
	border-radius: 10px;
	font-size: var(--wp--preset--font-size--large);
	font-family: var(--wp--preset--font-family--heading);
}

/* -------------------------------------------------------------------------
   10. Utilities
   ---------------------------------------------------------------------- */

/* Let a wide/full block bleed to the viewport edge on small screens. */
@media (max-width: 600px) {
	.sb-bleed-mobile {
		margin-left: calc(var(--wp--preset--spacing--20) * -1);
		margin-right: calc(var(--wp--preset--spacing--20) * -1);
		border-radius: 0;
	}
}

/* Overlay text needs a scrim to stay readable over artwork. */
.sb-scrim::before {
	content: "";
	position: absolute;
	inset: 0;
	background: linear-gradient(
		180deg,
		rgba(74, 78, 82, 0.55) 0%,
		rgba(74, 78, 82, 0.2) 45%,
		rgba(74, 78, 82, 0.5) 100%
	);
	pointer-events: none;
}

.sb-scrim > * {
	position: relative;
	z-index: 1;
}

/* A monochrome logo can serve both the light header and the dark footer by
   being inverted, rather than needing a separate reversed asset. Only sound for
   single-colour marks — a full-colour logo must have its own reversed file. */
.sb-footer__logo--invert img {
	filter: invert(1) brightness(1.9);
}

/* -------------------------------------------------------------------------
   11. Motion

   Paired with assets/js/reveal.js. The `sb-js` flag is set by an inline
   script in the head, BEFORE paint, so without JavaScript everything stays
   visible rather than being hidden behind an animation that never runs.
   ---------------------------------------------------------------------- */

.sb-reveal {
	opacity: 0;
	transform: translateY(14px);
	transition: opacity 640ms cubic-bezier(0.16, 1, 0.3, 1),
		transform 640ms cubic-bezier(0.16, 1, 0.3, 1);
}

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

/* Without JavaScript nothing is ever revealed, so the content must start
   visible and the animation must be the enhancement. */
html:not(.sb-js) .sb-reveal {
	opacity: 1;
	transform: none;
	transition: none;
}

@media (prefers-reduced-motion: reduce) {
	.sb-reveal {
		opacity: 1;
		transform: none;
		transition: none;
	}
}

/* -----------------------------------------------------------------
   Gravity radios, checkboxes and the datepicker

   Gravity renders the choice input as a SIBLING of its label, not
   inside it, so the card has to be the row. Styling the label instead
   leaves the native control floating outside the bordered box.
   ----------------------------------------------------------------- */

.gform_wrapper.gravity-theme .gfield_radio,
.gform_wrapper.gravity-theme .gfield_checkbox {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.gform_wrapper.gravity-theme .gchoice {
	display: flex;
	align-items: flex-start;
	gap: 0.75rem;
	margin: 0;
	padding: 0 0.9rem;
	background: var(--wp--preset--color--surface);
	border: 1.5px solid var(--sb-field-border);
	border-radius: 10px;
	transition: background-color var(--wp--custom--transition--base),
		border-color var(--wp--custom--transition--base);
}

.gform_wrapper.gravity-theme .gchoice:hover,
.gform_wrapper.gravity-theme .gchoice:has(input:checked) {
	background: var(--wp--preset--color--tint);
	border-color: var(--wp--preset--color--action);
}

.gform_wrapper.gravity-theme .gchoice:has(input:focus-visible) {
	outline: 3px solid var(--wp--preset--color--action);
	outline-offset: 2px;
}

.gform_wrapper.gravity-theme .gchoice input[type="radio"],
.gform_wrapper.gravity-theme .gchoice input[type="checkbox"] {
	flex: none;
	width: 20px;
	height: 20px;
	margin: 0.85rem 0 0;
	accent-color: var(--wp--preset--color--action);
	cursor: pointer;
}

/* The label carries the vertical padding so the whole row height is a tap
   target, not just the text. `.gform_fields` for specificity: Gravity sets 600
   here, which otherwise renders UNCHECKED options bolder than the chosen one. */
.gform_wrapper.gravity-theme .gform_fields .gchoice label {
	flex: 1 1 auto;
	display: flex;
	align-items: center;
	min-height: 44px;
	margin: 0;
	padding: 0.7rem 0;
	background: none;
	border: 0;
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--small);
	font-weight: 400;
	line-height: 1.45;
	color: var(--wp--preset--color--ink);
	cursor: pointer;
}

.gform_wrapper.gravity-theme .gform_fields .gchoice:has(input:checked) label {
	font-weight: 600;
}

/* Gravity Forms 3.0 ships its OWN datepicker, not jQuery UI, so
   `#ui-datepicker-div` selectors match nothing. */
.gform-datepicker-calendar {
	border-radius: 12px;
	box-shadow: var(--wp--preset--shadow--lifted);
	font-family: var(--wp--preset--font-family--body);
}

.gform-datepicker-calendar td.day {
	border-radius: 999px;
	color: var(--wp--preset--color--ink);
}

.gform-datepicker-calendar td.day:hover {
	background: var(--wp--preset--color--tint);
	color: var(--wp--preset--color--action);
}

.gform-datepicker-calendar td.day.selected,
.gform-datepicker-calendar td.day[aria-selected="true"] {
	background: var(--wp--preset--color--action);
	color: var(--wp--preset--color--base);
	box-shadow: none;
}

.gform-datepicker-calendar td.day.dayToday {
	color: var(--wp--preset--color--action);
	font-weight: 600;
}

/* The honeypot is the whole anti-spam mechanism; it must stay hidden. */
.gform_wrapper.gravity-theme .gform_validation_container,
.gform_wrapper.gravity-theme .gfield--type-honeypot {
	display: none !important;
}

/* -------------------------------------------------------------------------
   12. Prigmore Law

   Sections the brief needs that the framework has no pattern for. Everything
   here is layout and positioning — no colour, size, or spacing literals; all
   values come from theme.json tokens so a rebrand stays a one-file change.
   ---------------------------------------------------------------------- */

/* ---- Hero -------------------------------------------------------------- */

/* The nameplate sits over the bottom-left corner of the portrait, the way a
   caption plate sits on a framed photograph. */
.pl-plate-wrap {
	position: relative;
}

.pl-plate-wrap > * {
	margin-block: 0;
}

.pl-plate {
	background: var(--wp--preset--color--base);
	border-left: 4px solid var(--wp--preset--color--action);
	border-radius: 3px;
	box-shadow: var(--wp--preset--shadow--lifted);
	padding: 0.85rem 1.25rem;
}

.pl-plate strong {
	display: block;
	font-family: var(--wp--preset--font-family--heading);
	color: var(--wp--preset--color--accent);
	font-weight: 500;
}

/* Overlap only where there is room for it. Below the column breakpoint the
   plate becomes a normal block under the photograph, which is why it is not
   absolutely positioned by default — a stacked layout with an absolutely
   positioned child collapses the space it used to occupy. */
@media (min-width: 900px) {
	.pl-plate {
		position: absolute;
		left: calc(var(--wp--preset--spacing--20) * -0.6);
		/* Low enough to sit over the foot of the frame rather than across the
		   subject's hands, which is where it landed at spacing-20. */
		bottom: 0.75rem;
		max-width: 16rem;
	}
}

/* ---- Gold hairline rule ------------------------------------------------
   The one piece of ornament in the design. Gold is 2.84:1 on white, so it is
   cleared for rules and fills only, never for text. */
.pl-rule {
	display: block;
	width: 4.5rem;
	height: 2px;
	border: 0;
	margin: 0;
	background: var(--wp--preset--color--action);
}

.pl-rule.is-centred {
	margin-inline: auto;
}

.sb-on-dark .pl-rule,
.pl-band-accent .pl-rule,
.pl-band-action .pl-rule {
	background: var(--wp--preset--color--tint);
}

/* ---- Service grid -----------------------------------------------------
   Six cards. `auto-fit` rather than a fixed column count so the grid reflows
   to 3 / 2 / 1 without three media queries.

   blockGap is switched off on this container in the block markup: WordPress
   implements block gap as margin-block-start on `* + *`, which lands on top of
   the CSS `gap` and — because the first child is exempt — leaves exactly one
   card sitting higher than its row. */
.pl-svc-grid {
	display: grid;
	/* 20rem, not 17rem: there are SIX cards, and at the 76rem alignwide width a
	   17rem minimum fits four per row, which orphans two on the second row.
	   20rem caps it at three and still steps to 2 and then 1 as it narrows. */
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
	gap: var(--wp--preset--spacing--20);
}

.pl-svc-grid > * {
	margin-block: 0;
}

.pl-svc-card {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	height: 100%;
	padding: var(--wp--preset--spacing--20);
	background: var(--wp--preset--color--base);
	border: var(--wp--custom--rule--hairline);
	border-top: 3px solid var(--wp--preset--color--accent);
	border-radius: 4px;
	transition: box-shadow var(--wp--custom--transition--base),
		transform var(--wp--custom--transition--base),
		border-color var(--wp--custom--transition--base);
}

.pl-svc-card:hover {
	border-top-color: var(--wp--preset--color--action);
	box-shadow: var(--wp--preset--shadow--soft);
	transform: translateY(-2px);
}

.pl-svc-card h3 {
	margin: 0;
}

.pl-svc-card p {
	margin: 0;
	flex: 1 1 auto;
}

/* The whole card is the click target, not just the six-word link at the bottom
   of it. The audience for this site is frequently in its seventies, so a 320px
   target beats a 90px one. The <a> keeps its own visible text and accessible
   name; the pseudo-element only widens the hit area. */
.pl-svc-card .pl-svc-more {
	margin-top: auto;
	padding-top: 0.35rem;
	font-weight: 600;
	text-decoration: none;
}

/* The pseudo-element has to hang off the <a>, not off its wrapping <p> — a
   paragraph's ::after is not a link, so stretching that one produces a card
   that looks clickable everywhere and is clickable nowhere. */
.pl-svc-card .pl-svc-more a::after {
	content: "";
	position: absolute;
	inset: 0;
	border-radius: inherit;
}

/* Keep the visible link above its own hit area so the focus ring still draws
   around the words rather than being painted over. */
.pl-svc-card .pl-svc-more a:focus-visible {
	z-index: 1;
}

/* ---- Two-visit process ------------------------------------------------- */

.pl-steps {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
	gap: var(--wp--preset--spacing--30);
	counter-reset: pl-step;
}

.pl-steps > * {
	margin-block: 0;
}

.pl-step {
	position: relative;
	padding-top: var(--wp--preset--spacing--20);
	border-top: 2px solid var(--wp--preset--color--action);
}

.pl-step__label {
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--x-small);
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--wp--preset--color--action);
	margin: 0 0 0.35rem;
}

.pl-step h3 {
	margin: 0 0 0.5rem;
}

.pl-step p {
	margin: 0;
}

/* ---- Live Google reviews ----------------------------------------------
   The widget brings its own styling, so there is deliberately almost nothing
   here. Only the outer container is touched, and only to stop the third-party
   markup inheriting the block-gap margins the theme applies to siblings. */
.pl-reviews > * {
	margin-block: 0;
}

/* ---- Offices ----------------------------------------------------------- */

.pl-offices {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
	gap: var(--wp--preset--spacing--20);
}

.pl-offices > * {
	margin-block: 0;
}

.pl-office {
	padding: var(--wp--preset--spacing--20);
	background: var(--wp--preset--color--base);
	border: var(--wp--custom--rule--hairline);
	border-left: 4px solid var(--wp--preset--color--accent);
	border-radius: 4px;
}

.pl-office.is-satellite {
	border-left-color: var(--wp--preset--color--action);
}

.pl-office p {
	margin: 0 0 0.25rem;
}

/* ---- Lining figures in the heading face ------------------------------
   Cormorant Garamond defaults to OLDSTYLE figures: its GSUB carries `lnum` and
   `tnum` but no `onum`, which means text figures are the default and lining
   figures are the thing you have to ask for. Fine for a date in running prose,
   wrong for every number this site sets in the heading face — a year, a phone
   number, a statistic. Applied to every heading-face context rather than to
   `:root`, so body copy in Palanquin is untouched. */
h1, h2, h3, h4, h5,
.wp-block-site-title,
.wp-block-post-title,
.wp-block-quote,
.wp-block-pullquote,
.wp-block-woocommerce-product-price,
.woocommerce-Price-amount,
.sb-lede-quote,
.gform_confirmation_message,
.pl-plate strong,
.sb-faq .wp-block-details > summary,
.wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item__content {
	font-variant-numeric: lining-nums;
	font-feature-settings: "lnum" 1;
}

/* ---- Header: phone number and consultation button ------------------- */

.pl-phone {
	display: inline-flex;
	align-items: center;
	gap: 0.45rem;
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--large);
	font-weight: 600;
	color: var(--wp--preset--color--accent);
	text-decoration: none;
	white-space: nowrap;
}

.pl-phone a {
	color: inherit;
	/* theme.json underlines links globally. Correct for body copy, wrong for a
	   phone number set at heading size in the header bar. */
	text-decoration: none;
}

.pl-phone:hover,
.pl-phone a:hover {
	color: var(--wp--preset--color--action);
	text-decoration: none;
}

/* The phone number is the primary conversion on this site — Ken takes far more
   calls than emails — so it stays visible as long as there is room, and the
   nav collapses to the hamburger before it does. */
@media (max-width: 1180px) {
	.pl-phone {
		font-size: var(--wp--preset--font-size--small);
	}
}

@media (max-width: 999px) {
	.sb-header .pl-header-cta {
		display: none;
	}
}

/* ---- Mobile header breakpoint: 999px --------------------------------
   The whole mobile header — hamburger, full lockup, no CTA button — now
   applies up to 999px, which closes the 901-985 band where the inline nav
   wrapped to three rows. Three of the four numbers are in this stylesheet; the
   fourth is inside core's navigation block and is why this override exists.

   `core/navigation` is set to `overlayMenu: "mobile"`, and that breakpoint is
   NOT configurable: `wp-includes/blocks/navigation/style.min.css` hard-codes
   `@media (min-width: 600px)` in two places, one to turn the fixed overlay back
   into an inline nav and one to hide the hamburger. Setting the block to
   `overlayMenu: "always"` instead would mean fighting more of core's CSS to get
   the inline nav back above the breakpoint, so the two rules are re-asserted
   here for the 600-999 band instead.

   Only `display` needs restating. Core's min-width:600 rule carries
   `:not(.is-menu-open)`, so the moment the menu opens it stops applying and the
   block's base `position: fixed` overlay rules take over on their own.

   Both selectors are prefixed with `.sb-header` to raise specificity above
   core's, so this does not depend on stylesheet load order — sebo-css happens
   to enqueue after wp-block-navigation-css today, and that is not a thing to
   rely on.

   NOTE: the `@media screen and (max-width: 782px)` block near the top of this
   file is WordPress's ADMIN BAR breakpoint, not a design one. It stays at 782. */
@media (max-width: 999px) {
	.sb-header .wp-block-navigation__responsive-container:not(.hidden-by-default):not(.is-menu-open) {
		display: none;
	}

	.sb-header .wp-block-navigation__responsive-container-open:not(.always-shown) {
		display: flex;
	}
}

/* ---- Dark bands -------------------------------------------------------
   Flat colour, not a gradient. brand/brand-deep interpolate the accent through
   the warm stone tint, which lands on a muddy lilac midpoint; two saturated
   brand colours are better served by hard edges.

   Named for the TOKEN, not the colour. A class called `.pl-band-navy` would
   still be called that after a rebrand painted it green, which is the drift the
   no-hard-coded-colour rule exists to prevent. */
.pl-band-accent {
	background: var(--wp--preset--color--accent);
}

.pl-band-action {
	background: var(--wp--preset--color--action);
}

/* The class must NOT be wrapped in :where() here. theme.json paints headings
   with `:root :where(h1, h2, ...)`, which carries specificity from `:root`;
   `:where(.pl-band-action) :where(h2)` is specificity ZERO and loses to it, so
   the heading rendered navy on maroon at 1.51:1. Written out, the class wins. */
.pl-band-accent :where(h1, h2, h3, h4, h5, h6),
.pl-band-action :where(h1, h2, h3, h4, h5, h6) {
	color: var(--wp--preset--color--base);
}

.pl-band-accent p,
.pl-band-action p {
	color: rgba(255, 255, 255, 0.92);
}

/* ---- Footer on navy ---------------------------------------------------
   The framework footer is ink (near-black). Navy is the brand's structural
   colour, and white on it measures 17.74:1. */
.sb-footer {
	background: var(--wp--preset--color--accent);
}

/* ---- Service-page body ------------------------------------------------ */

.pl-checks {
	list-style: none;
	margin: 0;
	padding: 0;
}

.pl-checks li {
	position: relative;
	padding-left: 2rem;
	margin-bottom: 0.7rem;
}

.pl-checks li::before {
	content: "";
	position: absolute;
	left: 0;
	top: 0.45em;
	width: 1.1rem;
	height: 1.1rem;
	border-radius: 50%;
	background: var(--wp--preset--color--accent);
	-webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='white' d='M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z'/></svg>") center / 0.72rem no-repeat;
	mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='white' d='M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z'/></svg>") center / 0.72rem no-repeat;
}

.pl-aside {
	padding: var(--wp--preset--spacing--20);
	background: var(--wp--preset--color--tint);
	border-radius: 4px;
}

.pl-aside h3 {
	margin-top: 0;
}

/* ---- Family strip ----------------------------------------------------- */

.pl-family img {
	border-radius: 4px;
}

/* ---- FAQ ---------------------------------------------------------------
   The framework's faq pattern has always carried an `.sb-faq` class with no
   rules behind it, so core/details rendered with browser defaults: a bare
   disclosure triangle and no separation between questions. */

.sb-faq .wp-block-details {
	background: var(--wp--preset--color--base);
	border: var(--wp--custom--rule--hairline);
	border-radius: 4px;
	padding: 0;
}

.sb-faq .wp-block-details > summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	padding: 1rem var(--wp--preset--spacing--20);
	cursor: pointer;
	font-family: var(--wp--preset--font-family--heading);
	font-size: var(--wp--preset--font-size--large);
	font-weight: 500;
	line-height: 1.35;
	color: var(--wp--preset--color--accent);
	/* Replaced by the +/- below; the native marker cannot be positioned. */
	list-style: none;
}

.sb-faq .wp-block-details > summary::-webkit-details-marker {
	display: none;
}

.sb-faq .wp-block-details > summary::after {
	content: "+";
	flex: none;
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--x-large);
	font-weight: 400;
	line-height: 1;
	color: var(--wp--preset--color--action);
}

.sb-faq .wp-block-details[open] > summary::after {
	content: "\2013";
}

.sb-faq .wp-block-details > summary:hover {
	color: var(--wp--preset--color--action);
}

/* The answer is padded to align with the question rather than to the element
   edge, and the top padding is zero so it sits under its own question. */
.sb-faq .wp-block-details > :not(summary) {
	margin: 0;
	padding: 0 var(--wp--preset--spacing--20) 1rem;
}

/* ---- Muted text on dark grounds ---------------------------------------
   `.sb-meta` and `.sb-eyebrow` resolve to `muted` / `action`, both chosen
   against LIGHT grounds. On the navy footer the disclaimer measured 2.15:1 —
   a real failure on the one piece of text with legal weight on the page.
   Re-point them at values measured against navy.

   The eyebrow used to take `gold`. The 11 Sep style guide has no ornament
   colour, so it takes the stone `tint` instead: 10.50:1 on navy and 8.66:1 on
   burgundy, against the 4.5:1 an eyebrow needs (14px bold is NOT WCAG large
   text). It stays a step quieter than the pure-white headline above it, which
   is the job gold was doing. */
.sb-footer .sb-meta,
.pl-band-accent .sb-meta,
.pl-band-action .sb-meta {
	color: rgba(255, 255, 255, 0.72);
}

.sb-footer .sb-eyebrow,
.pl-band-accent .sb-eyebrow,
.pl-band-action .sb-eyebrow {
	color: var(--wp--preset--color--tint);
}

/* ---- Brand lockup across breakpoints ---------------------------------
   Three assets, one visible at a time:

     .sb-brand__full     the primary lockup, from the media library
     .sb-brand__compact  the same lockup WITHOUT the descriptor
     .sb-brand__mark     the two tiles alone, for the scrolled state

   Why a compact variant exists: in a 390px bar shared with a hamburger and the
   phone number the logo gets about 148px of width, which renders the primary
   lockup 24px tall and its descriptor's cap height at 4px. Legible in the
   design, a smudge on a phone. Below the breakpoint the descriptor is dropped
   rather than shipped at a size nobody can read. */

.sb-brand__compact {
	display: none;
	margin: 0;
	line-height: 0;
}

.sb-brand__compact img {
	/* 148px was sized for a 390px phone bar shared with a hamburger and the
	   phone number, and it was the only width this lockup ever had to cover.
	   Now that the mobile header runs all the way to 900px it has to work
	   across a 510px range, and a fixed 148px reads lost in a 900px bar. The
	   floor stays 148 so the phone case is untouched — 22vw does not overtake
	   it until ~673px — and the ceiling is 210px, which at 900px leaves the
	   hamburger and the phone number their room. */
	display: block;
	width: clamp(148px, 22vw, 210px);
	height: auto;
	transition: opacity var(--wp--custom--transition--base);
}

.sb-brand__compact a:hover img {
	opacity: 0.78;
}

/* The FULL lockup — descriptor and all — at every mobile width. Asked for on
   11 Sep 2026, replacing the name-only compact variant here.

   It costs nothing in layout: `logo.svg` is 589x100 and `logo-compact.svg` is
   586x100, so at a given rendered width the two occupy the same box and the
   descriptor simply appears. Swapping them changes no geometry.

   What it does cost is descriptor legibility at phone widths, and the number is
   worth writing down: the descriptor's cap height is DESC_CAP/S = 17% of the
   lockup's height, and the lockup is 5.89:1, so cap height is width / 34.6. At
   the 148px floor that is 4.3px. Four pixels is the exact fault Ken objected to
   on the reviewed mockup — "Utah County estate planning was really tiny and
   then big letters next to it" — and it is why the compact variant was built.
   Flagged to the team rather than quietly overridden; the rules for
   `.sb-brand__compact` above are left in place and re-enabling it below a
   chosen width is the two lines in this block.

   26vw rather than a larger slope so the step at the 999/1000 boundary stays
   moderate: the desktop bar has to fit the inline nav and the CTA button again
   there, so the logo necessarily drops back. */
@media (max-width: 999px) {
	.sb-header .wp-block-site-logo img {
		width: clamp(148px, 26vw, 240px);
	}
}

/* ---- Service-page photography ----------------------------------------
   One crop for all six pages. The source images are the client's own and vary
   from 1961x2560 portrait to 2560x1707 landscape; unconstrained, the portrait
   ones rendered ~660px tall and dominated the page. 3:2 matches the blog cards
   so the whole site crops consistently. */
.pl-svc-photo img {
	aspect-ratio: 3 / 2;
	object-fit: cover;
	width: 100%;
	/* Faces sit high in these frames; centring the crop cut heads off. */
	object-position: center 32%;
}

/* ---- Banner photograph ------------------------------------------------
   A wide crop, so one image can break up a text-heavy page without
   dominating it. 3:1 has room to breathe on a laptop; at phone widths the
   same ratio is a 130px letterbox slot, so it opens up to 16:9. */
.pl-banner img {
	width: 100%;
	aspect-ratio: 3 / 1;
	object-fit: cover;
	/* Subjects in these frames sit above the midline. */
	object-position: center 38%;
}

@media (max-width: 781px) {
	.pl-banner img {
		aspect-ratio: 16 / 9;
	}
}
