/* =============================================================================
   pages.css: shared styles for the eight designed inner-page templates.

   Enqueued as `samson-pages` on those templates only (see inc/enqueue.php).
   Mobile-first. Breakpoints at 700px and 1080px. Uses DESIGN.md tokens only.

   Relies on the foundation layer for: the CSS custom properties in :root
   (palette --daylight/--charslate/--vent/--vent-deep/--brand/--skytint/--ochre/
   --ochre-ink/--line/--slate-60 and the --s1..--s12 space scale), the layout
   classes .wrap / .band / .band--tint / .band--dark, the buttons .btn / .btn--
   outline, and base element typography. Font-family tokens are read with a
   literal fallback so headings are correct even before critical.css lands.

   Role-based radii and the single soft shadow are written as literals because
   DESIGN.md section 4 defines them by value, not as named tokens:
     tiles / photos / fields 10px · buttons 8px · pills 999px
     shadow 0 10px 30px -12px rgba(34,48,60,.18)
   ============================================================================= */

/* -----------------------------------------------------------------------------
   Small utilities used across the templates
   ----------------------------------------------------------------------------- */
.sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

.lead {
	font-size: 1.25rem;
	line-height: 1.6;
}

.numeral {
	font-family: var(--ff-display, "Plus Jakarta Sans", system-ui, sans-serif);
	font-weight: 800;
	font-size: clamp(3rem, 6vw, 5rem);
	line-height: 1;
	letter-spacing: -0.01em;
}

.ochre {
	color: var(--amber);
}

.ochre-ink {
	color: var(--ochre-ink);
}

/* Pitched-roofline motif that sits under at most three H2s per page. */
.roofline {
	display: block;
	margin-top: var(--s3);
	color: var(--blue);
	line-height: 0;
}

.roofline svg {
	display: block;
	width: 46px;
	height: 16px;
}

.i-arrow {
	flex: none;
	transition: transform 0.18s ease;
}

/* Inner-page H1/H2/H3 sizing, per DESIGN.md section 3. Set explicitly on the
   template heading classes so they hold even if base element styles differ. */
.page-hero__title,
.piv-hero__title {
	font-family: var(--ff-display, "Plus Jakarta Sans", system-ui, sans-serif);
	font-weight: 800;
	font-size: clamp(2.25rem, 4.5vw, 3.75rem);
	line-height: 1.04;
	letter-spacing: -0.01em;
}

.trust-strip__title,
.how__title,
.runcost__title,
.fixes__title,
.areas-grid__title,
.area-services__title,
.area-map__title,
.reviews-slice__title,
.reviews-list__title,
.related__title,
.cta-band__title,
.contact__title,
.contact-map__title,
.about-intro__title,
.about-approach__title,
.about-work__title,
.about-credentials__title,
.about-team__title,
.before-after-band__title {
	font-family: var(--ff-display, "Plus Jakarta Sans", system-ui, sans-serif);
	font-weight: 800;
	font-size: clamp(1.875rem, 3.6vw, 3.25rem);
	line-height: 1.08;
	letter-spacing: -0.005em;
}

.step__head {
	font-family: var(--ff-display, "Plus Jakarta Sans", system-ui, sans-serif);
	font-weight: 700;
	font-size: 1.375rem;
	line-height: 1.25;
}

/* -----------------------------------------------------------------------------
   Page hero (shared navy hero)
   ----------------------------------------------------------------------------- */
/* Every inner-page hero (this file, area.css, problem.css, service.css,
   blog.css) shares one uniform, tight recipe: identical padding top and
   bottom, no min-height, no background image. hero-breathe.webp and the old
   house-clearance padding/backgrounds are retired (v2.2 navy hero restyle). */
.page-hero {
	padding-block: clamp(48px, 6vw, 84px);
}

.page-hero__inner {
	display: grid;
	gap: var(--s7);
	align-items: start;
}

/* Round 5 spec F (raised three times): no ch cap on the hero copy column or
   lead. Text-only heroes fill the full container; media heroes fill the
   7-col grid track (below) with nothing extra capping it further. Colour:
   v2.2 navy hero restyle, lead copy on the dark ground reads at --on-dark. */
.page-hero__lead {
	margin-top: var(--s5);
	color: var(--on-dark);
}

.page-hero__media {
	margin: 0;
	border-radius: var(--r-card);
	overflow: hidden;
	background: var(--sky);
	aspect-ratio: 5 / 4;
	max-height: 60vh;
	box-shadow: var(--shadow-card);
}

.page-hero__img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
}

/* -----------------------------------------------------------------------------
   PIV hero + cutaway band + running cost
   ----------------------------------------------------------------------------- */
/* v2.2: the PIV hero is now a plain navy hero (H1, lead, CTAs only), same
   recipe as every other inner-page hero; the cutaway moved out into its own
   light band below (.piv-cutaway) because its navy-stroked line art would be
   invisible on this ground. No ch cap on the intro column: nothing else
   fills the row now the cutaway has moved out (claude.md layout rule). */
.piv-hero__lead {
	margin-top: var(--s5);
	color: var(--on-dark);
}

/* The cutaway's own light band: sky ground so the navy-stroked drawing stays
   legible, same tight hero padding scale as the band above it. */
.piv-cutaway {
	padding-block: clamp(48px, 6vw, 84px);
}

.piv-cutaway__intro {
	max-width: 72ch;
}

/* No extra top margin: the band's own padding-block-start already clears
   it, so the old hero-internal spacing (built for sitting under the lead
   paragraph) is removed here to avoid doubling up. */
.piv-hero__hint {
	margin-top: 0;
	color: var(--slate);
	max-width: 64ch;
}

.runcost__inner {
	display: grid;
	gap: var(--s7);
	align-items: center;
}

/* Stat block: centred label / figure / label stack. Two-thirds/one-third
   column split is set at the 1080px breakpoint below; this stays a plain
   centred stack at every narrower width. */
.runcost__figure {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--s2);
	text-align: center;
	/* Centres the stat block within its own grid column (not just within
	   itself) once the 1080px breakpoint puts it in a 1fr 2fr row next to
	   the wider copy column. */
	justify-self: center;
}

/* Both the "from about" and "a day to run" labels share this look, split
   from the single old caption so each reads as its own line rather than
   one run-on phrase either side of the figure. */
.runcost__label {
	font-weight: 700;
	color: var(--slate);
	text-transform: uppercase;
	letter-spacing: 0.07em;
	font-size: 0.8125rem;
}

.runcost__num {
	display: block;
	/* Overrides .numeral's smaller clamp() (critical.css): this is the
	   headline figure of the band, so it needs real display-type weight. */
	font-size: clamp(4.5rem, 8vw, 7.5rem);
}

.runcost__copy > * + * {
	margin-top: var(--s4);
}

.runcost__copy p {
	max-width: 60ch;
}

.runcost__note {
	color: var(--slate);
	font-size: 0.9375rem;
}

/* -----------------------------------------------------------------------------
   The cutaway (ported from the mockup, upgraded for GSAP in piv.js)
   ----------------------------------------------------------------------------- */
.cutaway-scroll {
	margin-top: var(--s7);
	overflow-x: auto;
	overflow-y: hidden;
	-webkit-overflow-scrolling: touch;
	padding-bottom: 2px;
}

/* SPEC-V4 section J: the wrapper's aspect-ratio MUST match the svg viewBox
   (1270 x 640) exactly. It previously used 1600/640, which let the svg's own
   preserveAspectRatio="xMidYMid meet" letterbox and centre inside a wider
   box, so the hotspot layer's percentage coordinates (mapped to this
   wrapper) no longer lined up with where the drawing actually rendered. At
   1270/640 the svg fills the box edge to edge with no letterboxing, so the
   hotspots layer and the svg share one true coordinate space. */
.cutaway {
	position: relative;
	width: 100%;
	min-width: 680px;
	aspect-ratio: 1270 / 640;
}

/* Direct-child combinator only: this must size the house illustration svg
   alone. A descendant selector here also matched the "See the fix" arrow
   icon nested deep inside .hotspots > .hotspot > .tip > .tip-link, forcing
   that 14x14 icon to width/height:100% of its flex row and blowing it up
   to fill the tooltip button — squashing the button text into a
   single-word-per-line column. That only became visible on hover/focus
   (when the tooltip shows), which is why it read as "the button distorts
   on hover" even though no hover rule ever touches the hotspot dot itself. */
.cutaway > svg {
	display: block;
	width: 100%;
	height: 100%;
	overflow: visible;
}

.hotspots {
	position: absolute;
	inset: 0;
}

.hotspot {
	position: absolute;
	transform: translate(-50%, -50%);
	width: 34px;
	height: 34px;
}

.dot-btn {
	width: 100%;
	height: 100%;
	padding: 0;
	border: 0;
	background: none;
	cursor: pointer;
	display: grid;
	place-items: center;
}

.hotspot .dot {
	width: 14px;
	height: 14px;
	border-radius: 50%;
	background: var(--blue);
	box-shadow: 0 0 0 4px rgba(46, 116, 192, 0.22), 0 0 0 8px rgba(46, 116, 192, 0.1);
	position: relative;
	display: block;
}

/* Tooltip card, DESIGN.md section 7: "tooltip cards white with --shadow-card". */
.hotspot .tip {
	position: absolute;
	left: 50%;
	bottom: calc(100% + 12px);
	transform: translateX(-50%) translateY(6px);
	width: 240px;
	max-width: 74vw;
	background: var(--white);
	color: var(--navy);
	border: 1px solid var(--line);
	border-radius: var(--r-card);
	box-shadow: var(--shadow-card);
	padding: var(--s4);
	text-align: left;
	opacity: 0;
	visibility: hidden;
	pointer-events: none;
	transition: opacity 0.16s ease, transform 0.16s ease, visibility 0.16s;
	z-index: 5;
}

.hotspot .tip::after {
	content: "";
	position: absolute;
	top: 100%;
	left: 50%;
	transform: translateX(-50%);
	border: 8px solid transparent;
	border-top-color: var(--white);
}

.hotspot .tip-name {
	font-family: var(--ff-display, "Plus Jakarta Sans", system-ui, sans-serif);
	font-weight: 700;
	font-size: 1.05rem;
	display: block;
	margin-bottom: 0.2em;
}

.hotspot .tip-sym {
	font-size: 0.9rem;
	color: var(--slate);
	display: block;
	margin-bottom: 0.5em;
	line-height: 1.45;
}

/* The "See the fix" link is a .btn-sm.btn-outline (SPEC-V4 section C): reset
   the button's own margin so it sits tight under the symptom copy, at full
   width so it never wraps awkwardly inside the 240px tip panel. */
.hotspot .tip-link {
	margin-top: var(--s2);
	width: 100%;
}

.tip-link__arrow {
	flex: none;
}

.hotspot:hover .tip,
.hotspot:focus-within .tip,
.hotspot.show .tip {
	opacity: 1;
	visibility: visible;
	transform: translateX(-50%) translateY(0);
	pointer-events: auto;
}

.dot-btn:focus-visible {
	outline: none;
}

.dot-btn:focus-visible .dot {
	box-shadow: 0 0 0 3px var(--white), 0 0 0 6px var(--blue);
}

/* Edge-aware tips: nudge in so they never overflow the drawing horizontally. */
.hotspot.tip-left .tip {
	left: 0;
	transform: translateX(0) translateY(6px);
}

.hotspot.tip-left:hover .tip,
.hotspot.tip-left:focus-within .tip,
.hotspot.tip-left.show .tip {
	transform: translateX(0) translateY(0);
}

.hotspot.tip-left .tip::after {
	left: 20px;
}

.hotspot.tip-right .tip {
	left: auto;
	right: 0;
	transform: translateX(0) translateY(6px);
}

.hotspot.tip-right:hover .tip,
.hotspot.tip-right:focus-within .tip,
.hotspot.tip-right.show .tip {
	transform: translateX(0) translateY(0);
}

.hotspot.tip-right .tip::after {
	left: auto;
	right: 20px;
}

/* Static airflow dashes and visible droplets are the base (no-JS / reduced)
   state; piv.js drives the continuous flow and droplet cycle with GSAP. */
.airflow {
	stroke-dasharray: 9 11;
}

.droplet {
	opacity: 0.9;
}

/* Motion-only flourishes: the hotspot pulse ring only exists when motion is
   welcome, so reduced-motion users get a clean, static dot. */
@media (prefers-reduced-motion: no-preference) {
	.hotspot .dot::after {
		content: "";
		position: absolute;
		inset: -4px;
		border-radius: 50%;
		border: 2px solid var(--blue);
		animation: samson-pulse 2.6s ease-out infinite;
	}

	@keyframes samson-pulse {
		0% {
			transform: scale(0.7);
			opacity: 0.8;
		}
		70% {
			transform: scale(1.9);
			opacity: 0;
		}
		100% {
			opacity: 0;
		}
	}
}

/* -----------------------------------------------------------------------------
   Facts band
   ----------------------------------------------------------------------------- */
.facts__grid {
	display: grid;
	gap: var(--s7);
}

.fact__num {
	display: block;
	margin-bottom: var(--s3);
}

/* var(--on-dark) is safe inside .band--dark (facts-band.php): it is a
   :root-level token that always means "secondary text on a dark ground", not
   one of the old alias names .band--dark remaps, so no re-anchor needed here. */
.fact__label {
	color: var(--on-dark);
	max-width: 26ch;
}

.facts__source {
	color: rgba(255, 255, 255, .55);
	font-size: 0.85rem;
	margin-top: var(--s7);
	max-width: 66ch;
}

/* -----------------------------------------------------------------------------
   How PIV works (numbered steps)
   ----------------------------------------------------------------------------- */
.how__lead {
	margin-top: var(--s4);
	color: var(--slate);
	max-width: 60ch;
	margin-bottom: var(--s8);
}

.steps {
	display: grid;
	gap: var(--s7);
	counter-reset: none;
	list-style: none;
	padding: 0;
	margin: 0;
}

.step {
	display: flex;
	flex-direction: column;
	gap: var(--s3);
}

.step__n {
	font-family: var(--ff-display, "Plus Jakarta Sans", system-ui, sans-serif);
	font-weight: 800;
	font-size: clamp(2.5rem, 4vw, 3.5rem);
	color: var(--blue);
	line-height: 1;
}

.step__body {
	color: var(--slate);
	max-width: 36ch;
}

/* -----------------------------------------------------------------------------
   Reviews (compact slice + full list)
   ----------------------------------------------------------------------------- */
.reviews-slice__head,
.reviews-list__head {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	align-items: flex-end;
	gap: var(--s4);
	margin-bottom: var(--s7);
}

.reviews-slice__badge,
.reviews-list__badge,
.trust-strip__rating {
	font-family: var(--ff-display, "Plus Jakarta Sans", system-ui, sans-serif);
	font-weight: 700;
	color: var(--ochre-ink);
	font-size: 1.15rem;
}

.reviews-slice__grid,
.reviews-list__grid {
	display: grid;
	gap: var(--s5);
	list-style: none;
	padding: 0;
	margin: 0;
}

.review {
	margin: 0;
	background: var(--white);
	border: 1px solid var(--line);
	border-radius: var(--r-card);
	box-shadow: var(--shadow-card);
	padding: var(--s6);
	transition: transform .18s ease, box-shadow .18s ease;
}

/* DESIGN.md section 4 "hover lifts 4px with a slightly deeper shadow";
   --shadow-pop is reserved for dropdown panels only (section 4), so the lift
   here keeps --shadow-card and just deepens via the translateY itself. */
@media (prefers-reduced-motion: no-preference) {
	.review:hover {
		transform: translateY(-4px);
	}
}

.review__quote {
	font-family: var(--ff-display, "Plus Jakarta Sans", system-ui, sans-serif);
	font-weight: 700;
	letter-spacing: -0.01em;
	line-height: 1.25;
	font-size: 1.3rem;
	margin: 0 0 var(--s4);
}

.review__who {
	color: var(--slate);
	font-weight: 700;
	font-size: 0.9rem;
	display: flex;
	flex-direction: column;
	gap: 0.15em;
}

.review__name {
	color: var(--navy);
}

.review__role {
	font-weight: 500;
}

.review__meta {
	font-weight: 500;
	color: var(--slate);
}

.reviews-slice__empty,
.reviews-list__empty {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--s4);
	background: var(--sky);
	border: 1px solid var(--line);
	border-radius: var(--r-card);
	padding: var(--s7);
	max-width: 60ch;
}

/* Reviews page: the shared reviews-scroller block (component styles are global
   in main.css) sits directly above the full list, two daylight bands in a row,
   so halve the shared edge (DESIGN.md section 6.5). The full list is a simple
   two-column grid (one column under 700px), inheriting the grid above. */
.reviews-scroller.band {
	padding-block-end: calc(var(--sec-pad, clamp(64px, 9vw, 120px)) / 2);
}
.reviews-list--full.band {
	padding-block-start: calc(var(--sec-pad, clamp(64px, 9vw, 120px)) / 2);
}

/* -----------------------------------------------------------------------------
   Trust strip + accreditation badges (service, about)
   ----------------------------------------------------------------------------- */
.trust-strip__inner {
	display: grid;
	gap: var(--s7);
	align-items: center;
}

.trust-strip__lead > * + * {
	margin-top: var(--s4);
}

.trust-strip__lead p {
	max-width: 60ch;
	color: var(--navy);
}

.badge-strip {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s3);
	align-items: center;
	list-style: none;
	padding: 0;
	margin: 0;
}

.badge-chip {
	background: var(--white);
	border: 1px solid var(--line);
	border-radius: 8px;
	padding: 0.4em 0.6em;
	display: inline-flex;
	align-items: center;
}

.badge-chip img {
	height: 46px;
	width: auto;
	display: block;
}

.badge-chip--text {
	font-weight: 700;
	font-size: 0.85rem;
	color: var(--slate);
}

/* -----------------------------------------------------------------------------
   Link grids: related services, problem fixes, area services summary
   ----------------------------------------------------------------------------- */
.related__grid,
.fixes__grid,
.services-summary {
	display: grid;
	gap: var(--s4);
	list-style: none;
	padding: 0;
	margin: var(--s6) 0 0;
}

.related__link,
.fixes__link,
.services-summary__link {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--s4);
	padding: var(--s5);
	background: var(--sky);
	border: 1px solid transparent;
	border-radius: var(--r-card);
	text-decoration: none;
	color: var(--navy);
	font-family: var(--ff-display, "Plus Jakarta Sans", system-ui, sans-serif);
	font-weight: 700;
	font-size: 1.125rem;
	transition: border-color 0.18s ease, transform 0.18s ease;
}

.related__link:hover,
.fixes__link:hover,
.services-summary__link:hover {
	border-color: var(--blue);
	transform: translateY(-2px);
}

.related__link .i-arrow,
.fixes__link .i-arrow,
.services-summary__link .i-arrow {
	color: var(--blue-deep);
}

.related__link:hover .i-arrow,
.fixes__link:hover .i-arrow,
.services-summary__link:hover .i-arrow {
	transform: translateX(3px);
}

.fixes__lead {
	color: var(--slate);
	max-width: 60ch;
	margin-top: var(--s4);
}

/* -----------------------------------------------------------------------------
   Areas hub pills
   ----------------------------------------------------------------------------- */
.pills {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s3);
	list-style: none;
	padding: 0;
	margin: var(--s6) 0;
}

.pill {
	display: inline-flex;
	align-items: center;
	gap: 0.5em;
	border: 2px solid var(--blue);
	color: var(--blue-deep);
	border-radius: 999px;
	padding: 0.5em 1.05em;
	font-weight: 700;
	font-size: 0.95rem;
	text-decoration: none;
	background: var(--white);
	transition: background-color 0.18s ease;
}

.pill:hover {
	background: var(--sky);
}

.pill--hq {
	background: var(--blue);
	color: var(--white);
	border-color: var(--blue);
}

.pill--hq:hover {
	background: var(--blue-deep);
}

.pill__tag {
	background: var(--white);
	color: var(--blue-deep);
	border-radius: 999px;
	font-size: 0.7rem;
	padding: 0.15em 0.5em;
	letter-spacing: 0.04em;
}

.areas-grid__support {
	max-width: 62ch;
	color: var(--slate);
}

/* -----------------------------------------------------------------------------
   Area single: map embed
   ----------------------------------------------------------------------------- */
.area-map {
	position: relative;
	margin-top: var(--s6);
	border-radius: var(--r-card);
	overflow: hidden;
	border: 1px solid var(--line);
	aspect-ratio: 16 / 10;
	background: var(--sky);
}

.area-map__frame {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	border: 0;
}

.area-map__note {
	margin-top: var(--s5);
	color: var(--slate);
	max-width: 62ch;
}

/* -----------------------------------------------------------------------------
   About: editorial two-column story + trust row

   .about-story__columns is the old the_content() band. Round 14 moved the
   About copy into template-about.php as designed sections (below), so nothing
   renders these two rules today; they are kept because page.php and the other
   editorial templates can still drop .entry-content into a story band.
   ----------------------------------------------------------------------------- */
.about-story__columns {
	max-width: 100%;
}

.about-story__columns > * + * {
	margin-top: var(--s5);
}

/* -----------------------------------------------------------------------------
   About (round 14 rebuild): intro split, approach cards, "backed by" panel,
   mid-page CTA, job-stage photographs and the client-type chips. Everything
   here is scoped to .about-* classes used only by template-about.php.
   ----------------------------------------------------------------------------- */
.about-intro__grid {
	display: grid;
	gap: var(--s8);
	align-items: start;
}

.about-intro__title {
	margin-top: var(--s3);
}

.about-intro__copy .lead {
	margin-top: var(--s4);
	color: var(--navy);
}

.about-intro__copy p + p {
	margin-top: var(--s5);
}

.about-intro__copy p:not(.lead) {
	color: var(--slate);
}

.about-intro__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s4);
	margin-top: var(--s7);
}

/* Shared photo frame: card language (radius, hairline, shadow) around a
   client photograph, with the caption outside the frame. */
.about-figure {
	margin: 0;
}

.about-figure__img {
	width: 100%;
	border-radius: var(--r-card);
	border: 1px solid var(--line);
	box-shadow: var(--shadow-card);
	object-fit: cover;
}

.about-figure__img--wide {
	aspect-ratio: 4 / 3;
}

.about-figure__caption {
	margin-top: var(--s3);
	color: var(--slate);
	font-size: 0.9375rem;
	line-height: 1.55;
}

/* How we work */
.about-approach__title {
	margin-top: var(--s3);
}

.about-approach__lead {
	margin-top: var(--s4);
	margin-bottom: var(--s8);
	color: var(--navy);
}

.about-approach__grid {
	display: grid;
	gap: var(--s6);
	list-style: none;
	padding: 0;
	margin: 0;
}

.about-card {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--s3);
	background: var(--white);
	border: 1px solid var(--line);
	border-radius: var(--r-card);
	box-shadow: var(--shadow-card);
	padding: var(--s6);
}

.about-card__icon {
	flex: none;
	width: 56px;
	height: 56px;
	border-radius: 50%;
	background: var(--sky);
	color: var(--blue);
	display: grid;
	place-items: center;
}

.about-card__icon .samson-icon {
	width: 28px;
	height: 28px;
}

.about-card__title {
	margin: 0;
}

.about-card__body {
	margin: 0;
	color: var(--slate);
}

/* Guarantee / reports / rating panel. White panel on the sky band, so the
   default light tokens resolve correctly (no .band--dark ancestor, no
   literal re-anchor needed; DESIGN.md section 2 dark-band token trap). */
.about-backed {
	margin-top: var(--s7);
	background: var(--white);
	border: 1px solid var(--line);
	border-radius: var(--r-card);
	box-shadow: var(--shadow-card);
	padding: var(--s7);
}

.about-backed__title {
	margin: 0 0 var(--s6);
}

.about-backed__list {
	display: grid;
	gap: var(--s6);
	list-style: none;
	padding: 0;
	margin: 0;
}

.about-backed__item {
	display: flex;
	align-items: flex-start;
	gap: var(--s4);
}

.about-backed__badge {
	flex: none;
	width: 76px;
}

.about-backed__badge-img {
	width: 100%;
	height: auto;
}

.about-backed__icon {
	flex: none;
	width: 44px;
	height: 44px;
	border-radius: 50%;
	background: var(--sky);
	color: var(--blue);
	display: grid;
	place-items: center;
}

.about-backed__icon--stars {
	color: var(--amber);
}

.about-backed__icon .samson-icon {
	width: 22px;
	height: 22px;
}

.about-backed__text {
	margin: 0;
	color: var(--slate);
}

.about-backed__link {
	font-weight: 700;
	color: var(--blue);
}

.about-backed__link:hover {
	color: var(--blue-deep);
}

/* Mid-page dark CTA strip. Literal white on the line: this element sits
   inside .band--dark, where var(--charslate) resolves to white anyway, but
   the literal keeps it right if the band ground ever changes. */
.about-cta {
	padding-block: clamp(36px, 5vw, 60px);
}

.about-cta__inner {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--s5);
}

.about-cta__line {
	font-family: var(--ff-display, "Plus Jakarta Sans", system-ui, sans-serif);
	font-weight: 700;
	font-size: clamp(1.25rem, 2.4vw, 1.75rem);
	line-height: 1.25;
	color: #FFFFFF;
	max-width: 34ch;
}

.about-cta__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s4);
}

/* Job-stage photographs */
.about-work__title {
	margin-top: var(--s3);
}

.about-work__lead {
	margin-top: var(--s4);
	margin-bottom: var(--s8);
	color: var(--navy);
}

.about-work__grid {
	display: grid;
	gap: var(--s7);
	list-style: none;
	padding: 0;
	margin: 0;
}

.about-step {
	display: flex;
	flex-direction: column;
}

/* Native 3:4, so nothing is cropped at mobile and tablet widths where the
   photo is already large relative to the viewport. The desktop rule below
   trades a little height for width; object-position then decides which end of
   the frame that height comes off, per photo. */
.about-step__img {
	aspect-ratio: 3 / 4;
}

.about-step__img--top {
	object-position: center top;
}

.about-step__img--lower {
	object-position: center 68%;
}

.about-step__img--bottom {
	object-position: center bottom;
}

.about-step__label {
	display: block;
	margin-top: var(--s4);
	font-size: 0.8125rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.1em;
	color: var(--blue-deep);
}

.about-step__title {
	margin-top: var(--s2);
	font-size: 1.125rem;
}

.about-step__caption {
	margin-top: var(--s3);
	color: var(--slate);
}

.about-work__note {
	margin-top: var(--s8);
	color: var(--slate);
}

.about-work__link {
	font-weight: 700;
	color: var(--blue);
}

.about-work__link:hover {
	color: var(--blue-deep);
}

/* Experience and approvals */
.about-credentials__title {
	margin-top: var(--s3);
	margin-bottom: var(--s8);
}

.about-clients {
	margin-top: var(--s8);
}

.about-clients__title {
	margin: 0 0 var(--s4);
}

.about-clients__list {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s3);
	list-style: none;
	padding: 0;
	margin: 0;
}

.about-chip {
	background: var(--white);
	border: 1px solid var(--line);
	border-radius: var(--r-pill);
	padding: var(--s2) var(--s5);
	font-weight: 600;
	color: var(--navy);
}

/* Round 23: the social housing chip links to that page. The link carries the
   padding so the whole pill is the tap target. */
.about-chip--link {
	padding: 0;
	border-color: var(--blue);
}

.about-chip--link a {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	padding: 0 var(--s5);
	border-radius: inherit;
	color: var(--blue);
	text-decoration: underline;
	text-underline-offset: 3px;
}

.about-chip--link a:hover {
	text-decoration-thickness: 2px;
}

.about-chip--link a:focus-visible {
	outline: 3px solid var(--blue);
	outline-offset: 2px;
}

.trust-row {
	display: grid;
	gap: var(--s6);
	list-style: none;
	padding: 0;
	margin: 0 0 var(--s8);
}

.trust-row__item {
	display: flex;
	flex-direction: column;
	gap: var(--s2);
}

.trust-row__value {
	display: block;
}

.trust-row__value--text {
	font-family: var(--ff-display, "Plus Jakarta Sans", system-ui, sans-serif);
	font-weight: 800;
	font-size: clamp(1.75rem, 3vw, 2.5rem);
	line-height: 1.05;
	color: var(--ochre-ink);
	letter-spacing: -0.01em;
}

.trust-row__label {
	color: var(--slate);
	max-width: 30ch;
}

/* -----------------------------------------------------------------------------
   About: Meet the team. The section renders only when template-about.php's
   $samson_team array is filled in with real people (it is empty until the
   client supplies names, roles, photographs and bios), so these rules style
   nothing today and are kept ready for that data. Cards use the same card
   language as .home-room/.home-service (radius, hairline border, spacing) on
   the tint ground.
   ----------------------------------------------------------------------------- */
.about-team__lead {
	margin-top: var(--s4);
	color: var(--navy);
	max-width: 60ch;
	margin-bottom: var(--s8);
}

.about-team__grid {
	display: grid;
	gap: var(--s6);
	list-style: none;
	padding: 0;
	margin: 0;
}

.team-card {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--s3);
	background: var(--white);
	border: 1px solid var(--line);
	border-radius: var(--r-card);
	box-shadow: var(--shadow-card);
	padding: var(--s6);
	transition: transform .18s ease;
}

@media (prefers-reduced-motion: no-preference) {
	.team-card:hover {
		transform: translateY(-4px);
	}
}

.team-card__avatar {
	flex: none;
	width: 84px;
	height: 84px;
	border-radius: 50%;
	background: var(--sky);
	color: var(--blue-deep);
	display: grid;
	place-items: center;
}

.team-card__avatar .samson-icon {
	width: 42px;
	height: 42px;
}

/* Real portrait, when a member entry carries a 'photo' key. */
.team-card__photo {
	display: block;
	width: 100%;
}

.team-card__photo-img {
	width: 100%;
	aspect-ratio: 1 / 1;
	object-fit: cover;
	border-radius: var(--r-card);
	border: 1px solid var(--line);
}

.team-card__name {
	margin: 0;
	font-family: var(--ff-display, "Plus Jakarta Sans", system-ui, sans-serif);
	font-weight: 700;
	font-size: 1.125rem;
}

.team-card__role {
	display: block;
	color: var(--blue-deep);
	font-weight: 700;
	font-size: 0.8125rem;
	text-transform: uppercase;
	letter-spacing: 0.06em;
}

.team-card__bio {
	margin: 0;
	color: var(--slate);
}

/* -----------------------------------------------------------------------------
   Before and after comparison block. NO TEMPLATE RENDERS THIS TODAY: round 14
   removed it from template-about.php, where it had been paired with stand-in
   images that were never a genuine same-wall before/after (the About page now
   shows the client's real job photographs instead). The block
   (template-parts/blocks/before-after.php) and its module
   (assets/js/modules/before-after.js) are left in place for the day a real
   same-framing pair arrives, so these styles stay with them. Card language
   matches the figures used elsewhere (radius, hairline border); the divider
   position lives in the --cut custom property the figure sets inline,
   consumed by the clip-path below.
   ----------------------------------------------------------------------------- */
.before-after-band__lead {
	margin-top: var(--s4);
	color: var(--navy);
	max-width: 60ch;
	margin-bottom: var(--s8);
}

.before-after-grid {
	display: grid;
	gap: var(--s7);
}

.before-after {
	margin: 0;
}

.before-after__frame {
	position: relative;
	overflow: hidden;
	border-radius: var(--r-card);
	border: 1px solid var(--line);
	aspect-ratio: 4 / 3;
	background: var(--sky);
	cursor: ew-resize;
	/* No scroll/pan gesture hijacking the drag on touch devices. A drag that
	   "starts then stops" a few pixels in is the browser reinterpreting it as
	   a pan (touch) or a text/image selection (mouse) and cancelling the
	   pointer session (pointercancel) out from under before-after.js's own
	   drag handling — see that module's docblock. `pan-y` was considered so a
	   vertical-ish drag could still scroll the page from inside the figure,
	   but that is exactly the ambiguous gesture that would misfire into a
	   cancelled drag; `none` is correct since the figure never fills the
	   viewport, so the page is always scrollable from just outside it. */
	touch-action: none;
	user-select: none;
	-webkit-user-select: none;
}

.before-after__layer {
	position: absolute;
	inset: 0;
}

.before-after__layer img,
.before-after__layer .samson-img-placeholder {
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
	display: block;
	border-radius: 0;
	aspect-ratio: auto;
}

.before-after__layer--before {
	/* The one property the drag/keyboard interaction ever writes (before-after.js);
	   deliberately no transition here (claude.md section 8: motion should
	   respond directly to the pointer, not chase it with an eased lag). */
	clip-path: inset(0 calc(100% - var(--cut, 50%)) 0 0);
}

.ba-pill {
	position: absolute;
	top: var(--s3);
	z-index: 3;
	background: var(--navy);
	color: var(--white);
	font-weight: 700;
	font-size: 0.75rem;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	padding: 0.3em 0.75em;
	border-radius: var(--r-pill, 999px);
	pointer-events: none;
}

.ba-pill--before {
	left: var(--s3);
}

.ba-pill--after {
	right: var(--s3);
}

.before-after__handle {
	position: absolute;
	inset-block: 0;
	left: var(--cut, 50%);
	transform: translateX(-50%);
	z-index: 2;
	display: flex;
	align-items: center;
	justify-content: center;
	pointer-events: none;
}

.before-after__handle::before {
	content: "";
	position: absolute;
	inset-block: 0;
	left: 50%;
	width: 2px;
	transform: translateX(-50%);
	background: var(--white);
	box-shadow: 0 0 0 1px rgba(18, 42, 76, 0.25);
}

.before-after__grip {
	position: relative;
	z-index: 1;
	width: 40px;
	height: 40px;
	border-radius: 50%;
	background: var(--white);
	color: var(--navy);
	display: grid;
	place-items: center;
	box-shadow: var(--shadow-card);
}

.before-after__grip svg {
	width: 16px;
	height: 16px;
}

.before-after__caption {
	margin-top: var(--s3);
	color: var(--slate);
	font-size: 0.9375rem;
}

/* -----------------------------------------------------------------------------
   Contact
   ----------------------------------------------------------------------------- */
.contact__grid {
	display: grid;
	gap: var(--s8);
	align-items: start;
}

.contact-method-list {
	list-style: none;
	padding: 0;
	margin: var(--s6) 0 0;
	display: grid;
	gap: var(--s5);
}

.contact-method {
	display: flex;
	gap: var(--s4);
	align-items: flex-start;
}

/* Round icon chip, DESIGN.md section 4 "56px round chips, --sky ground with
   --blue stroke icon on light cards" (44px here: a smaller list icon, not a
   hero-scale trust icon, so scaled down from the 56px reference size). */
.contact-method__icon {
	flex: none;
	width: 44px;
	height: 44px;
	display: grid;
	place-items: center;
	border-radius: 50%;
	background: var(--sky);
	color: var(--blue);
}

.contact-method__icon svg {
	width: 22px;
	height: 22px;
}

.contact-method__text {
	display: flex;
	flex-direction: column;
	gap: 0.15em;
}

.contact-method__label {
	font-weight: 700;
	font-size: 0.8125rem;
	text-transform: uppercase;
	letter-spacing: 0.07em;
	color: var(--slate);
}

.contact-method__value {
	font-family: var(--ff-display, "Plus Jakarta Sans", system-ui, sans-serif);
	font-weight: 700;
	font-size: 1.2rem;
	color: var(--navy);
	text-decoration: none;
}

a.contact-method__value:hover {
	color: var(--blue-deep);
	text-decoration: underline;
}

.contact-method__value--static {
	font-size: 1rem;
	font-family: var(--ff-body, "Plus Jakarta Sans", system-ui, sans-serif);
	font-weight: 500;
}

.contact__smallprint {
	margin-top: var(--s5);
	color: var(--slate);
	font-size: 0.9375rem;
}

/* -----------------------------------------------------------------------------
   Survey card (shared: contact page + CTA band with form). v2 restyle note:
   this block used to fully re-declare .survey-card* here with stale v1 10px
   radius/literal shadow and NO dark-band re-anchor. main.css (out of scope,
   already ported to v2) now owns the canonical .survey-card/.survey-card__bar/
   __body/__title/__intro/GF-field styling, amber header bar, --sky field
   grounds and the --charslate/--slate-60/--line literal re-anchor so the card
   reads correctly when the cta-band.php form variant drops it onto a
   .band--dark ground (DESIGN.md section 2 dark-band token trap). Because
   pages.css loads AFTER main.css, redeclaring the same selectors here at equal
   specificity would win the cascade and silently undo that re-anchor (plus
   ship the wrong white field ground instead of --sky) — so this pass removes
   the duplicate entirely rather than fix it twice in two files.
   ----------------------------------------------------------------------------- */

/* -----------------------------------------------------------------------------
   CTA band (charslate). Foundation supplies .band--dark ground + light text.
   ----------------------------------------------------------------------------- */
.cta-band {
	position: relative;
	overflow: hidden;
}

/* v2 restyle: cta-dark.webp (the flat-#22303C skyline strip) is removed from
   cta-band.php outright — the band is the plain .band--dark navy gradient
   ground now (DESIGN.md section 7 "the homepage hero no longer carries the
   street illustration"), same treatment as the homepage dark bands. The image
   file stays on disk, unreferenced; .cta-band__bg has no matching element
   left to style, so the rule is removed rather than left as dead CSS. */

.cta-band__inner {
	position: relative;
	z-index: 1;
	display: grid;
	gap: var(--s7);
}

/* var(--on-dark) is safe here: a :root-level token, not one of the old alias
   names .band--dark remaps, so it always means "secondary text on dark". */
.cta-band__body {
	margin-top: var(--s4);
	color: var(--on-dark);
	max-width: 46ch;
}

.cta-band__actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--s4);
	margin-top: var(--s6);
}

.cta-band__actions svg {
	width: 1.15em;
	height: 1.15em;
	flex: none;
}

/* Note: cta-band.php's outline buttons render .btn-outline (critical.css),
   not .btn--outline; the dark-ground on-white-outline treatment already lives
   in critical.css's ".band--dark .btn-outline" rule (literal-anchored, same
   trap awareness), so no duplicate is needed here. */

.cta-band__phone {
	color: var(--white);
	font-family: var(--ff-display, "Plus Jakarta Sans", system-ui, sans-serif);
	font-weight: 700;
	font-size: 1.35rem;
	text-decoration: none;
	display: inline-flex;
	align-items: center;
	gap: 0.4em;
}

.cta-band__phone:hover {
	color: var(--amber);
}

.cta-band__form .survey-card {
	max-width: 480px;
}

/* -----------------------------------------------------------------------------
   .entry-content: the_content() output from the Classic Editor
   ----------------------------------------------------------------------------- */
.entry-content {
	max-width: 68ch;
}

.entry-content > * + * {
	margin-top: var(--s5);
}

.entry-content h2 {
	font-family: var(--ff-display, "Plus Jakarta Sans", system-ui, sans-serif);
	font-weight: 800;
	font-size: clamp(1.625rem, 3vw, 2.25rem);
	line-height: 1.1;
	margin-top: var(--s8);
}

.entry-content h3 {
	font-family: var(--ff-display, "Plus Jakarta Sans", system-ui, sans-serif);
	font-weight: 700;
	font-size: 1.375rem;
	line-height: 1.25;
	margin-top: var(--s7);
}

.entry-content ul,
.entry-content ol {
	padding-left: 1.25em;
}

.entry-content li + li {
	margin-top: var(--s2);
}

.entry-content a {
	color: var(--blue);
	text-decoration: underline;
	text-underline-offset: 2px;
}

.entry-content img {
	border-radius: var(--r-card);
	height: auto;
}

.entry-content blockquote {
	border-left: 3px solid var(--blue);
	padding-left: var(--s5);
	color: var(--navy);
	font-size: 1.25rem;
}

/* -----------------------------------------------------------------------------
   Breakpoint: 700px (comfortable two-column rhythm)
   ----------------------------------------------------------------------------- */
@media (min-width: 700px) {
	.facts__grid {
		grid-template-columns: repeat(3, 1fr);
	}

	.steps {
		grid-template-columns: repeat(3, 1fr);
	}

	.reviews-slice__grid,
	.reviews-list__grid {
		grid-template-columns: repeat(2, 1fr);
	}

	.related__grid,
	.fixes__grid,
	.services-summary {
		grid-template-columns: repeat(2, 1fr);
	}

	.trust-row {
		grid-template-columns: repeat(3, 1fr);
	}

	.about-team__grid {
		grid-template-columns: repeat(3, 1fr);
	}

	.about-work__grid {
		grid-template-columns: repeat(2, 1fr);
	}

	.before-after-grid {
		grid-template-columns: repeat(2, 1fr);
	}

	.contact__grid {
		grid-template-columns: 1fr 1fr;
	}

	.cta-band--form .cta-band__inner {
		grid-template-columns: 1fr 1fr;
		align-items: center;
	}
}

/* -----------------------------------------------------------------------------
   Breakpoint: 900px (About: three-up approach cards, CTA strip goes to a row)
   ----------------------------------------------------------------------------- */
@media (min-width: 900px) {
	.about-approach__grid {
		grid-template-columns: repeat(3, 1fr);
	}

	.about-cta__inner {
		flex-direction: row;
		align-items: center;
		justify-content: space-between;
		gap: var(--s8);
	}

	.about-cta__line {
		margin: 0;
	}
}

/* -----------------------------------------------------------------------------
   Breakpoint: 1080px (full desktop grids, 7/5 hero split)
   ----------------------------------------------------------------------------- */
@media (min-width: 1080px) {
	.page-hero--media .page-hero__inner {
		grid-template-columns: 7fr 5fr;
		gap: var(--s10);
	}

	.trust-strip__inner {
		grid-template-columns: 7fr 5fr;
		gap: var(--s10);
	}

	.services-summary {
		grid-template-columns: repeat(3, 1fr);
	}

	/* One-third stat block, two-thirds heading + copy, per DESIGN.md's
	   corrected runcost layout: the figure needs real column width to
	   read as a standalone stat rather than floating beside the copy. */
	.runcost__inner {
		grid-template-columns: 1fr 2fr;
		gap: var(--s10);
	}

	.about-story__columns {
		column-count: 2;
		column-gap: var(--s9);
		max-width: 1000px;
	}

	.about-story__columns > * {
		break-inside: avoid;
	}

	.about-story__columns > * + * {
		margin-top: var(--s5);
	}

	/* 6/6 rather than 7/5 (still the full container, no dead gutter): at 7/5
	   the copy column ran a good deal taller than a 4:3 photo could fill, so
	   the row bottomed out unevenly. An even split gives the photograph enough
	   width to stretch, and the rules below let it grow to whatever height is
	   left once the caption is placed, so both columns finish level. */
	.about-intro__grid {
		grid-template-columns: 1fr 1fr;
		gap: var(--s10);
		align-items: stretch;
	}

	.about-intro__figure {
		display: flex;
		flex-direction: column;
	}

	/* Round 23: zero basis, so the photo fills the height the copy sets rather
	   than setting the row height from its own portrait shape. */
	.about-intro__figure .about-figure__img--wide {
		aspect-ratio: auto;
		flex: 1 1 0;
		height: 0;
		min-height: 340px;
		object-position: center 60%;
	}

	.about-backed__list {
		grid-template-columns: repeat(3, 1fr);
		gap: var(--s8);
	}

	/* Two-up, not four-up. At four across, each photograph was only about
	   270px wide and the detail the caption points at (the dimpled membrane
	   on the wall in stage three, the operative's hands in stage two) was too
	   small to read, even though the full frame was on screen. Two across
	   doubles the width; max-height keeps the row from becoming a 2000px wall
	   of photographs, and the per-photo object-position above decides what is
	   trimmed. */
	.about-work__grid {
		grid-template-columns: repeat(2, 1fr);
		gap: var(--s9) var(--s10);
	}

	.about-step__img {
		max-height: 620px;
	}

	.cta-band--form .cta-band__inner {
		grid-template-columns: 6fr 5fr;
		gap: var(--s10);
	}
}

/* -----------------------------------------------------------------------------
   Reduced motion: settle every enhancement to its finished state
   ----------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
	.i-arrow,
	.pill,
	.related__link,
	.fixes__link,
	.services-summary__link,
	.hotspot .tip {
		transition: none;
	}
}

/* -----------------------------------------------------------------------------
   Image lightbox (assets/js/modules/lightbox.js). Round 23: its only trigger,
   the About page training certificate, was removed at the client's request;
   the module and these styles stay for the next [data-lightbox] link.
   ----------------------------------------------------------------------------- */
/* --- The overlay itself (assets/js/modules/lightbox.js) --- */
.lightbox {
	max-width: min(94vw, 900px);
	max-height: 94dvh;
	padding: 0;
	border: 0;
	background: transparent;
	overflow: visible;
}

.lightbox::backdrop {
	background: rgba(18, 42, 76, .92);
}

.lightbox__inner {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: var(--s3);
}

.lightbox__img {
	display: block;
	width: auto;
	height: auto;
	max-width: 100%;
	/* Leave room under the picture for the caption and above it for the
	   close button, so neither ever overlaps the certificate itself. */
	max-height: 82dvh;
	margin-inline: auto;
	border-radius: 10px;
	background: var(--white);
	box-shadow: var(--shadow-pop);
}

.lightbox__cap {
	/* Held to a readable measure: the dialog is up to 900px wide but the
	   picture inside it is usually narrower, and a caption running the full
	   width would float out past the edges of what it describes. */
	max-width: 62ch;
	margin: 0 auto;
	color: rgba(255, 255, 255, .86);
	font-size: 0.875rem;
	line-height: 1.5;
	text-align: center;
	text-wrap: pretty;
}

/* Top-right of the overlay, not of the picture: the picture's width varies
   with its aspect ratio (it is height-constrained), so anchoring to it would
   move the button around. Solid navy fill so it reads against the backdrop
   and against a light picture equally. */
.lightbox__close {
	position: absolute;
	inset-block-start: var(--s3);
	inset-inline-end: var(--s3);
	z-index: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	padding: 0;
	border: 1px solid rgba(255, 255, 255, .34);
	border-radius: 50%;
	background: color-mix(in srgb, var(--navy-deep) 88%, transparent);
	color: var(--white);
	font-size: 1.5rem;
	line-height: 1;
	cursor: pointer;
	transition: background .2s ease;
}

.lightbox__close:hover {
	background: var(--navy-deep);
}

.lightbox :focus-visible {
	outline: 3px solid var(--amber);
	outline-offset: 3px;
}
