/* ============================================================================
   Samson Bespoke — main CSS (deferred).
   Global styles that are not needed for first paint: reveal states, footer,
   forms (including low-specificity Gravity Forms overrides), shared tiles and
   pills, editorial content, listings, and print defaults. Component files
   home.css and pages.css (owned by other agents) layer on top of this.
   Tokens are defined once in critical.css :root (v2 "Navy & Amber" restyle,
   DESIGN.md, August 2026). Most colour references below use the OLD v1
   token names (--vent, --charslate, --daylight, etc.) which now alias to
   the v2 palette (critical.css :root) — they resolve correctly without
   per-rule edits. Only rules that need a v2-specific value not reachable via
   the alias (amber panels, sky field grounds, literal re-anchors) are called
   out below.
   ========================================================================== */

/* --- Scroll reveal. Targets are marked with the [data-reveal] attribute (the
       convention shared with the homepage and inner-page templates); the legacy
       .reveal class is also honoured. Gated behind (scripting: enabled) so no-JS
       visitors never get stuck with hidden content, and reduced motion shows
       everything at rest. On GSAP pages, home.js claims [data-reveal] and flags
       has-gsap, so reveal.js stands down and GSAP drives the final state inline. --- */
@media (scripting: enabled) {
	[data-reveal],
	.reveal {
		opacity: 0;
		transform: translateY(18px);
		transition: opacity .6s ease, transform .6s ease;
	}
	[data-reveal].is-revealed,
	.reveal.is-revealed {
		opacity: 1;
		transform: none;
	}
}
@media (prefers-reduced-motion: reduce) {
	[data-reveal],
	.reveal {
		opacity: 1 !important;
		transform: none !important;
		transition: none;
	}
}

/* --- Roofline underline helper (DESIGN.md section 7). --- */
.roofed {
	display: inline-block;
}
.roofline {
	display: block;
	margin-top: var(--s3);
	color: var(--vent);
}

/* --- Illustration retint (DESIGN.md section 7). Single sitewide definition:
   the 29 line-art webps and hero-breathe.webp bake in the v1 teal canvas, so
   templates put .ill-retint on those <img>s to shift the line work into the
   v2 blue family. The #E4F0FA hero-band ground literals in pages.css /
   area.css / blog.css are sampled from hero-breathe.webp's sky AFTER this
   filter; re-sample them if these values ever change. --- */
.ill-retint {
	filter: hue-rotate(24deg) saturate(.85);
}

/* Round 5 spec C: no underlines sitewide (base rule lives in critical.css);
   hover feedback everywhere outside running text is a colour shift only.
   .entry-content running-text links keep their own permanent underline
   (set in the editorial content rules below), which already outranks this. */

/* ============================================================================
   Editorial content (the_content output from the Classic Editor).
   ========================================================================== */
.entry-content > * + * {
	margin-top: var(--s5);
}
.entry-content h2 {
	margin-top: var(--s9);
	margin-bottom: var(--s6);
}
.entry-content h3 {
	margin-top: var(--s7);
	margin-bottom: var(--s4);
}
.entry-content ul,
.entry-content ol {
	padding-left: 1.4em;
}
.entry-content ul {
	list-style: disc;
}
.entry-content ol {
	list-style: decimal;
}
.entry-content li + li {
	margin-top: .4em;
}
.entry-content a {
	color: var(--brand);
	text-decoration: underline;
	text-decoration-thickness: 1px;
}
.entry-content a:hover {
	color: var(--vent-deep);
}
.entry-content img {
	border-radius: var(--r-tile);
}
.entry-content blockquote {
	border-left: 3px solid var(--vent);
	padding-left: var(--s5);
	color: var(--slate-60);
}
.entry-content figure {
	margin-block: var(--s6);
}
.entry-content figcaption {
	margin-top: var(--s2);
	font-size: .9375rem;
	color: var(--slate-60);
}
.page-links {
	margin-top: var(--s6);
	display: flex;
	gap: var(--s2);
	flex-wrap: wrap;
}

/* ============================================================================
   Forms: survey card, live fallback panel, and Gravity Forms overrides.
   GF selectors are kept to a single leading class so specificity stays low
   (DESIGN.md section 11, claude.md section 2).
   ========================================================================== */
.survey-card {
	background: var(--daylight);
	border-radius: var(--r-tile);
	box-shadow: var(--shadow);
	overflow: hidden;
	/* This card is always a light surface, including the cta-band form variant
	   where it sits on .band--dark. .band--dark re-scopes --charslate/--slate-60/
	   --line to on-dark values so ordinary text recolours for that ground
	   (critical.css); left alone, anything in here painted with those tokens
	   (e.g. .survey-card__title below) would inherit the on-dark remap and
	   render near-white on this white card. Re-anchor to the literal
	   light-scheme values from critical.css :root so the card reads the same
	   regardless of what band it is dropped into. */
	--charslate: #16345E;
	--slate-60: #55677A;
	--line: #DCE6EF;
	/* Same trap, one level down: the Gravity Forms submit button paints from
	   --vent (see the .gform_wrapper rules further down), and .band--dark
	   remaps --vent to the pale on-dark #8CBAE8. That made the submit button
	   render light blue in every cta-band form on the site while the homepage
	   hero card, whose band is styled in home.css rather than with
	   .band--dark, kept the correct deep blue. Re-anchored here so the button
	   is the same blue in every card. */
	--brand: #2E74C0;
	--vent: #2E74C0;
	--vent-deep: #245D9C;
	color: var(--charslate);
}
/* Amber header panel (DESIGN.md section 8, section 11): navy text on amber,
   never the reverse (section 2). Literal, not var(--ochre)/var(--amber): this
   bar can render inside .band--dark (the cta-band form variant), which
   remaps --ochre-ink but not the plain amber value itself — literal keeps
   the panel amber regardless of the ground it is dropped into. */
.survey-card__bar {
	display: flex;
	align-items: center;
	gap: .5em;
	margin: 0;
	background: #F2A72E;
	color: #16345E;
	font-weight: 700;
	font-size: .95rem;
	padding: .7em 1.2em;
}
.survey-card__bar .samson-icon {
	width: 1.1em;
	height: 1.1em;
	flex: none;
}
.survey-card__body {
	/* Round 7: --s6 (24px) read baggy against the mockup's tighter card body;
	   dropped one step to --s5 (20px) as part of the general compaction pass. */
	padding: var(--s5);
	display: flex;
	flex-direction: column;
}
/* Card intro (heading + support line), rendered by every caller ahead of the
   form so the card reads the same wherever it appears (round 6 spec). Heading
   level is chosen per call site, not here; the size below is set explicitly
   rather than left to the base h2/h3 scale so a homepage h2 and an inner-page
   h3 in this slot render at the same size (pages.css also targets this class
   on the designed page templates with the same 1.5rem/700/1.2 values, kept in
   step here so the card matches even before that stylesheet loads). */
.survey-card__title {
	margin: 0 0 var(--s2);
	font-size: 1.5rem;
	font-weight: 700;
	line-height: 1.2;
	color: var(--charslate);
}
.survey-card__intro {
	/* Round 7: tightened from --s5 (20px) to --s4 (16px) so the intro line
	   sits closer to the first field, part of the general compaction pass. */
	margin: 0 0 var(--s4);
	color: var(--slate-60);
	font-size: .9375rem;
	line-height: 1.5;
}
/* Fallback panel (no live form): same width and stacking rhythm as the real
   fields and submit button, so swapping GF in later does not shift the card. */
.survey-fallback {
	display: flex;
	flex-direction: column;
}
.survey-fallback .btn {
	width: 100%;
	margin-bottom: var(--s3);
}
.survey-fallback .btn:last-of-type {
	margin-bottom: 0;
}
.survey-fallback__email {
	display: block;
	margin-top: var(--s3);
	color: var(--brand);
	font-weight: 700;
	text-align: center;
	text-decoration: none;
}
.survey-fallback__email:hover {
	color: var(--vent-deep);
}

/* Footer "Faster: WhatsApp a photo" strip, shared by every card instance.
   DESIGN.md section 11: navy-on-sky (was a teal-on-white fill in v1), so the
   card now reads amber-topped and sky-tint-footed rather than teal-bookended.
   Literal re-anchor to the light-scheme sky/navy, same reasoning as the
   .survey-card block above: this strip can sit inside .band--dark, whose
   token remap would otherwise turn --charslate/--skytint into on-dark values. */
.survey-card__faster {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: .5em;
	padding: .9em;
	background: #F1F6FA;
	color: #16345E;
	font-weight: 500;
	font-size: .9375rem;
	text-decoration: none;
}
.survey-card__faster:hover,
.survey-card__faster:focus-visible {
	background: #DCE6EF;
	color: #16345E;
	text-decoration: underline;
	text-underline-offset: 2px;
}
.survey-card__faster svg {
	width: 1.15em;
	height: 1.15em;
	flex: none;
	color: #16345E;
}
.survey-card__faster strong {
	font-weight: 700;
}

/* Round 7: the round-6 var(--s4) intent never actually rendered. GF's own
   foundation stylesheet carries `.gform-theme--foundation .gform_fields { row-gap:
   var(--gf-form-gap-y) }` at the SAME specificity (two classes) as the rule
   below, and GF's stylesheet prints after main.css in <head> (verified in the
   rendered page source), so on a tie the later rule wins the cascade — the
   card was rendering GF's 40px default, not our value, which is what the
   user was circling. row-gap/grid-row-gap need !important to reliably beat
   it regardless of GF's own load order or version. Matched to column-gap
   below for a uniform grid. */
.gform_wrapper .gform_fields {
	grid-row-gap: var(--s4) !important;
	row-gap: var(--s4) !important;
}
.gform_wrapper .gfield {
	margin-bottom: 0;
}
.gform_wrapper .gfield_label {
	display: block;
	margin-bottom: .35em;
	font-weight: 700;
	font-size: .8125rem;
	letter-spacing: .03em;
	text-transform: uppercase;
	color: var(--slate-60);
}
/* Round 7: corners were not reading as rounded because GF's Orbital theme
   ships #gform_wrapper_1[data-form-index="0"].gform-theme {--gf-radius: 3px}
   as an inline per-instance <style> block with an ID selector, which cascades
   through --gf-ctrl-radius (theme-framework.min.css) into `border-radius:
   var(--gf-local-radius)` on `.gform-theme--framework input[type]:where(...)`.
   That GF rule outranks the plain-class rule below on raw specificity even
   before the ID-scoped variable is considered (CDP inspection confirmed the
   rendered radius was 3px, not our --r-tile). --gf-ctrl-radius is set below
   too (belt and braces for any other GF control that reads it, e.g. the
   multiselect chip), but the reliable fix is the !important fallback here —
   same pattern as the submit button's background/colour overrides further
   down. --r-btn (10px, DESIGN.md section 4 "buttons and form fields 10px")
   is used instead of --r-tile so fields match the button radius, not the
   16px card radius. Field ground is --sky (DESIGN.md section 11), not white,
   so a field is still visible against the white .survey-card it sits on. */
.gform_wrapper input[type="text"],
.gform_wrapper input[type="email"],
.gform_wrapper input[type="tel"],
.gform_wrapper input[type="number"],
.gform_wrapper input[type="url"],
.gform_wrapper textarea,
.gform_wrapper select {
	width: 100%;
	font-family: var(--ff-body);
	font-size: 1rem;
	color: var(--charslate);
	padding: .75em .9em;
	border: 1.5px solid var(--line);
	border-radius: var(--r-btn) !important;
	background: var(--skytint);
}
/* Round 22: GF's own arrow sat 32px from the edge, so a long option ran under
   it. Drawn chevron with room to spare; !important because GF's theme sets
   the select background and padding at higher specificity. */
.gform_wrapper select {
	appearance: none;
	padding-right: 48px !important;
	background: var(--skytint) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 6l4.5 4.5L12.5 6' fill='none' stroke='%2316345E' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 18px center / 18px !important;
}
.gform_wrapper input::placeholder,
.gform_wrapper textarea::placeholder {
	color: var(--slate-60);
	opacity: .85;
}

/* Field pairing: containerised on the card itself so the Free Survey form (ID
   1) pairs Name+Postcode and Phone+"What are you seeing?" whenever the CARD
   is wide enough to hold two fields comfortably, regardless of viewport (the
   same card renders narrower in the hero 7/5 split than in a full-width
   contact layout). GF's foundation theme already grids .gform_fields at equal
   specificity, so the pairing rules match it two classes deep; the per-field
   spans use GF's own stable field IDs (verified against the live markup),
   which as ID selectors outrank GF's class-based default regardless of load
   order.

   Breakpoint note: the card never actually reaches 560px under the live
   grids (measured on the real page: 457px in the hero 7fr/5fr split, 491px
   in the cta-band--form 6fr/5fr split, 552px on the contact page's 1fr/1fr,
   all capped by .wrap's 1240px max-width), so 420px is used instead. That
   still sits well clear of the narrower tablet-width single-column contexts
   (~300px to 306px at 700 to 1079px viewports) that must stay stacked. */
.survey-card,
.home-survey {
	container-type: inline-size;
	container-name: samson-survey;
}
@container samson-survey (min-width: 420px) {
	.gform_wrapper .gform_fields {
		grid-template-columns: repeat(12, 1fr) !important;
		/* Same tie as the row-gap above; GF's foundation column-gap default
		   happens to also be 16px so this was masked until measured directly. */
		column-gap: var(--s4) !important;
	}
	.gform_wrapper .gfield {
		grid-column: span 12 !important;
	}
	.gform_wrapper #field_1_1,
	.gform_wrapper #field_1_2,
	.gform_wrapper #field_1_3,
	.gform_wrapper #field_1_4 {
		grid-column: span 6 !important;
	}
}
.gform_wrapper input:focus,
.gform_wrapper textarea:focus,
.gform_wrapper select:focus {
	outline: none;
	border-color: var(--vent);
	box-shadow: 0 0 0 3px rgba(46, 116, 192, .18);
}
/* Required asterisks: the mockup shows none. GF sets aria-required="true" on
   the input itself, not on this glyph, so the requirement is still exposed to
   assistive tech with the visible asterisk hidden outright. Scoped to labels
   only (not .gform_required_legend below, which quotes this same asterisk
   markup sr-only as "'*' indicates required fields"; hiding it there too
   would leave that sentence announcing empty quotes). Labels themselves are
   untouched and stay fully visible. */
.gform_wrapper .gfield_label .gfield_required {
	display: none;
}
/* Round 7: GF's own foundation stylesheet sets margin-block-start: 24px on
   .gform_footer unopposed (nothing here previously targeted that logical
   property), which stacked with our own padding-top to leave ~44px above the
   button — the second gap the user circled. Our selector is more specific
   than GF's single-class one so the override does not strictly need
   !important, but it is added for parity with the other GF fights on this
   page and to stay correct if GF ever raises its own specificity. */
.gform_wrapper .gform_footer {
	margin-block-start: 0 !important;
	padding-top: var(--s3);
}
.gform_wrapper .gform_footer .btn {
	width: 100%;
}
/* GF renders its own button classes and a required-fields legend; restyle the
   button to the vent primary and keep the legend for screen readers only.
   GF's Orbital theme paints the button through its own custom properties with
   higher specificity, so we set the supported theme properties and keep a
   hard fallback on the button itself. */
.gform_wrapper,
.gform-theme--framework,
.gform_wrapper.gravity-theme {
	--gf-color-primary: var(--vent);
	--gf-color-primary-darker: var(--vent-deep);
	--gf-ctrl-btn-bg-color-primary: var(--vent);
	--gf-ctrl-btn-bg-color-hover-primary: var(--vent-deep);
	--gf-ctrl-btn-border-color-primary: var(--vent);
	--gf-ctrl-btn-border-color-hover-primary: var(--vent-deep);
	/* Belt and braces for the field-radius fix above: this loses to the
	   per-instance inline --gf-radius set by GF's Orbital theme (that block
	   uses an ID selector, which always outranks the classes here), so it is
	   not what makes the fields round, but it keeps any other GF control that
	   reads --gf-ctrl-radius (e.g. the multiselect chip) in step regardless. */
	--gf-ctrl-radius: var(--r-btn);
}
.gform_wrapper .gform_button,
.gform_wrapper button.gform_button,
.gform_wrapper input[type="submit"] {
	width: 100%;
	border: 0;
	/* Same specificity tie as the text inputs: GF's framework button rule
	   (.gform-theme--framework...button:where(...)) was quietly winning this
	   property too, rendering 3px instead of --r-btn. */
	border-radius: var(--r-btn) !important;
	background: var(--vent) !important;
	color: var(--daylight) !important;
	padding: .85em 1.4em;
	font-family: var(--ff-body);
	font-size: 1rem;
	font-weight: 700;
	cursor: pointer;
}
.gform_wrapper .gform_button:hover,
.gform_wrapper .gform_button:focus-visible,
.gform_wrapper input[type="submit"]:hover,
.gform_wrapper input[type="submit"]:focus-visible {
	background: var(--vent-deep) !important;
}
.gform_wrapper .gform_required_legend {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
}
.gform_wrapper .validation_message {
	color: #B3261E;
	font-size: .85rem;
	margin-top: .25em;
}
.gform_confirmation_message {
	background: var(--skytint);
	border-radius: var(--r-tile);
	padding: var(--s5);
}

/* --- Search form. --- */
.search-form__row {
	display: flex;
	gap: var(--s2);
	flex-wrap: wrap;
}
.search-form__input {
	flex: 1 1 12em;
	min-width: 0;
	font-family: var(--ff-body);
	font-size: 1rem;
	color: var(--charslate);
	padding: .7em .8em;
	border: 1.5px solid var(--line);
	border-radius: var(--r-tile);
	background: var(--daylight);
}
.search-form__input:focus {
	outline: none;
	border-color: var(--vent);
	box-shadow: 0 0 0 3px rgba(46, 116, 192, .18);
}

/* ============================================================================
   Shared components: pills, review badge, image placeholder.
   ========================================================================== */
.pill {
	display: inline-flex;
	align-items: center;
	gap: .5em;
	border: 2px solid var(--vent);
	color: var(--vent-deep);
	border-radius: var(--r-pill);
	padding: .5em 1.05em;
	font-weight: 700;
	font-size: .95rem;
	text-decoration: none;
	background: var(--daylight);
}
.pill:hover {
	background: var(--skytint);
	text-decoration: none;
}
.pill.is-hq {
	background: var(--vent);
	color: var(--daylight);
	border-color: var(--vent);
}
.reviews-badge {
	display: inline-flex;
	align-items: center;
	gap: .4em;
	font-family: var(--ff-display);
	font-weight: 700;
	color: var(--ochre-ink);
	font-size: 1.15rem;
	text-decoration: none;
}
.reviews-badge:hover {
	color: var(--vent-deep);
}
.reviews-badge strong {
	font-weight: 800;
}
.samson-img-placeholder {
	width: 100%;
	background: var(--skytint);
	border-radius: var(--r-tile);
	aspect-ratio: 3 / 2;
}
.samson-img-placeholder[data-size="card"] {
	aspect-ratio: 2 / 1;
}
.samson-img-placeholder[data-size="hero"] {
	aspect-ratio: 16 / 9;
}

/* ============================================================================
   Reviews block v2 (shared component, round 4 spec section B). Rendered by
   template-parts/blocks/reviews-scroller.php on the front page and every
   designed page template, and echoed in the same visual language (without the
   scroller shell) by the static grid on template-reviews.php. The component
   lives here in global main.css (not home.css/pages.css) because it appears in
   every one of those contexts and must not depend on any of them; only
   context tweaks (adjacent-band padding) live in the component files.
   Mobile-first. Cards snap on a horizontal track that clips inside its own
   overflow container, so the page body never scrolls sideways at any width.
   Prev/next controls and their disabled/hidden states are added by
   modules/reviews-scroller.js; without JS the track still scrolls by
   touch/trackpad and the peeking next card shows it can.
   ========================================================================== */

/* Section head: heading full width on its own line, then the optional lead
   (round 7: the heading used to share a flex row with the Google-reviews
   button, but a longer H2 now wraps that row onto two ragged lines with the
   button stranded mid-wrap; the heading needs the row to itself), then a
   dedicated .rev-block__meta row carries the rating badge and the button
   together so they wrap as a pair, not against the heading. */
.rev-block__head {
	display: flex;
	flex-direction: column;
	gap: var(--s4);
	margin-block-end: var(--s7);
}
.rev-block__title {
	margin: 0;
}
/* Round 7: optional lead under the heading, matching .home-head__lead's
   type (home.css) so the block reads consistently wherever it renders full
   width, no narrow max-width cap. Margin reset to 0: spacing between it and
   its siblings comes from the .rev-block__head gap above, not its own
   margin, so it costs nothing when the lead arg is omitted (most callers). */
.rev-block__lead {
	margin: 0;
	color: var(--slate-60);
	font-size: 1.25rem;
	line-height: 1.6;
}
/* Badge left, Google-reviews button right; wraps to a stack on narrow
   viewports since the row is no longer sharing space with the heading. */
.rev-block__meta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--s4);
}
/* Round 5 spec C.2: "Read our reviews on Google" is a small outline button
   now (.btn .btn-outline .btn-sm in markup carries colour, sizing and hover
   feedback); this just points its chevron inline-end. */
.rev-block__google .samson-icon {
	transform: rotate(-90deg);
}
.rev-block__badge {
	margin: 0;
}
/* Bigger badge (round 4 spec B): the "4.9" numeral steps up to a big fact
   numeral treatment; scoped to this head so samson_reviews_badge() keeps its
   smaller default size everywhere else it is echoed. */
.rev-block__badge .reviews-badge {
	gap: .5em;
	font-size: 1.25rem;
}
.rev-block__badge .reviews-badge strong {
	font-family: var(--ff-display);
	font-weight: 800;
	font-size: clamp(1.75rem, 3vw, 2rem);
	letter-spacing: -.01em;
}

/* Scroller shell. ROOT CAUSE of the round 5 sitewide horizontal scrollbar
   (confirmed with a headless-Chrome CDP scrollWidth probe): the snap track
   sits inside .rev-scroller__viewport (overflow-x: auto), which correctly
   clips and scrolls on its own, but the shell around it had no horizontal
   clip of its own. On every page that renders this block (front page and
   every designed inner template) the full, unclipped width of the card
   track was measured into this element's scrollWidth and bubbled up through
   .wrap/.band/<body> to document.documentElement.scrollWidth. overflow-x:
   hidden here costs nothing visually (nothing in this shell needs to paint
   outside its own box) and stops the leak at its source; html { overflow-x:
   clip } in critical.css is the sitewide belt-and-braces backstop. */
.rev-scroller {
	position: relative;
	overflow-x: hidden;
}

/* The clipping viewport is the only element that scrolls sideways. The vertical
   padding gives a focused card room for its focus ring inside the clip (overflow
   is hidden on that axis); the matching negative margin keeps the vertical rhythm
   unchanged. Padding is kept off the inline axis on purpose so the start snap
   rests at scrollLeft 0, which the control's at-start disabled logic relies on. */
.rev-scroller__viewport {
	overflow-x: auto;
	overflow-y: hidden;
	scroll-snap-type: x mandatory;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: none; /* Firefox */
	-ms-overflow-style: none; /* legacy Edge */
	padding: 6px 0;
	margin: -6px 0;
}
.rev-scroller__viewport::-webkit-scrollbar {
	display: none; /* Chromium, Safari */
}

/* Flex track holds the snap items. */
.rev-scroller__track {
	display: flex;
	gap: var(--s5);
	margin: 0;
	padding: 0;
	list-style: none;
	align-items: stretch;
}

/* Snap item: a fixed inline-size that stays narrower than the viewport so the
   next card always peeks (the scrollability affordance). */
.rev-scroller__item {
	scroll-snap-align: start;
	flex: 0 0 auto;
	inline-size: clamp(15rem, 82%, 19rem);
	display: flex;
}

/* Card: daylight ground on the skytint band, the single soft shadow token,
   10px radius (DESIGN.md section 4; round 4 spec B "daylight with the soft
   shadow token"). */
.rev-card {
	display: flex;
	flex-direction: column;
	gap: var(--s3);
	inline-size: 100%;
	margin: 0;
	padding: var(--s6);
	background: var(--daylight);
	border-radius: var(--r-tile, 10px);
	box-shadow: var(--shadow, 0 12px 32px -12px rgba(18, 42, 76, .16));
}
/* Five solid ochre stars (round 4 spec B). Decorative: the rating is spoken
   for screen readers by the sr-only text alongside them. */
.rev-card__stars {
	display: inline-flex;
	gap: .15em;
	color: var(--ochre);
}
.rev-card__star {
	width: 16px;
	height: 16px;
	flex: none;
}
.rev-card__quote {
	margin: 0;
	max-width: 32ch; /* comfortable reading measure */
	font-family: var(--ff-display, "Plus Jakarta Sans", system-ui, sans-serif);
	font-weight: 700;
	font-size: 1.25rem;
	line-height: 1.3;
	letter-spacing: -.005em;
	color: var(--charslate);
}
.rev-card__meta {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: .5em;
	margin-block-start: auto; /* pin to foot for equal-height rows */
}
.rev-card__cite {
	font-family: var(--ff-body, "Plus Jakarta Sans", system-ui, sans-serif);
	font-weight: 700;
	font-size: .8125rem;
	letter-spacing: .07em;
	text-transform: uppercase;
	color: var(--charslate);
	font-style: normal;
}
.rev-card__source {
	display: inline-flex;
	padding: .25em .65em;
	border-radius: var(--r-pill, 999px);
	background: var(--skytint);
	color: var(--vent-deep);
	font-size: .8125rem;
	font-weight: 700;
}

/* Prev/next controls. Hidden by default: they do nothing without JS, and the
   script only reveals them (is-scrollable) when the track actually overflows. */
.rev-scroller__nav {
	display: none;
	justify-content: flex-end;
	gap: var(--s3);
	margin-block-start: var(--s5);
}
.rev-scroller.is-scrollable .rev-scroller__nav {
	display: flex;
}
.rev-scroller__btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	padding: 0;
	color: var(--charslate);
	background: var(--daylight);
	border: 1.5px solid var(--line);
	border-radius: var(--r-pill, 999px);
	cursor: pointer;
	transition: color .18s ease, background-color .18s ease, border-color .18s ease;
}
.rev-scroller__btn:hover {
	border-color: var(--vent);
	color: var(--vent-deep);
}
.rev-scroller__btn:disabled {
	opacity: .4;
	cursor: default;
	color: var(--slate-60);
	border-color: var(--line);
	background: var(--daylight);
}
.rev-scroller__btn svg {
	width: 18px;
	height: 18px;
}
.rev-scroller__btn--next svg {
	transform: rotate(-90deg); /* chevron points inline-end */
}
.rev-scroller__btn--prev svg {
	transform: rotate(90deg); /* chevron points inline-start */
}

/* Honest fallback panel when no testimonials are published (the badge sits in
   the head above; this keeps the Google route). Never an empty scroller.
   Daylight ground: the band is skytint now (round 4 spec B), so the panel
   needs the card treatment's contrast, not the old skytint-on-skytint. */
.rev-block__panel {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--s5);
	padding: var(--s7);
	background: var(--daylight);
	border-radius: var(--r-tile, 10px);
	box-shadow: var(--shadow, 0 12px 32px -12px rgba(18, 42, 76, .16));
}
.rev-block__panel p {
	margin: 0;
	max-width: 52ch;
	color: var(--slate-60);
}

/* Static two-column card grid (template-reviews.php): the same v2 .rev-card
   in chronological, newest-first order, no slider (round 4 spec B, I). One
   column under 700px. */
.rev-grid {
	display: grid;
	gap: var(--s5);
	margin: 0;
	padding: 0;
	list-style: none;
}
@media (min-width: 700px) {
	.rev-grid {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (min-width: 700px) {
	.rev-scroller__item {
		inline-size: 19rem;
	}
}

/* Reduced motion: the script already jumps instantly; settle the control
   transitions and any snap smoothing too. */
@media (prefers-reduced-motion: reduce) {
	.rev-scroller__btn {
		transition: none;
	}
	.rev-scroller__viewport {
		scroll-behavior: auto;
	}
}

/* ============================================================================
   Leaflet coverage/single-town map (shared component, round 4 spec section
   C). Rendered by template-parts/blocks/map.php wherever a caller includes
   it; assets/js/modules/map.js lazily creates the Leaflet map inside
   .map-block__el once it scrolls near the viewport. Callers size the panel
   themselves (home.css sizes the homepage coverage panel; the area/contact
   templates size their own single-town panel) — this file only supplies the
   sensible default so the block never collapses to zero height if a caller
   forgets to. No Google Maps anywhere in this theme: OpenStreetMap tiles only.
   ========================================================================== */
.map-block {
	/* Leaflet panes carry high internal z-indexes of their own; isolation traps
	   them in their own stacking context so they can never paint over the
	   sticky header while scrolling underneath it (round 6 defect 4). Applied
	   here on the shared wrapper so every map on the site is covered, not just
	   the town-hero map (area.css .area-hero__map already had this fix). */
	position: relative;
	isolation: isolate;
	z-index: 0;
	border-radius: var(--r-tile, 10px);
	overflow: hidden;
	background: var(--skytint);
}
.map-block__el {
	width: 100%;
	height: 100%;
	min-height: 320px;
}
/* No-JS fallback: a plain list of town links, standing in for the map. */
.map-block__fallback {
	display: grid;
	gap: var(--s2);
	margin: 0;
	padding: var(--s6);
	list-style: none;
}
.map-block__fallback a {
	color: var(--brand);
	font-weight: 700;
	text-decoration: none;
}
.map-block__fallback a:hover {
	color: var(--vent-deep);
}

/* On-brand teal divIcon pin (never the default Leaflet marker images). */
.map-pin {
	background: none;
	border: 0;
}
.map-pin__glyph {
	display: block;
	filter: drop-shadow(0 2px 4px rgba(18, 42, 76, .3));
}

/* Leaflet's own chrome (zoom control, attribution) restyled to the tokens
   rather than its default Google-Maps-adjacent look. */
.leaflet-popup-content-wrapper {
	border-radius: var(--r-tile, 10px);
	box-shadow: var(--shadow, 0 12px 32px -12px rgba(18, 42, 76, .16));
}
.leaflet-popup-content {
	margin: var(--s3) var(--s4);
	font-family: var(--ff-body, "Plus Jakarta Sans", system-ui, sans-serif);
}
.map-pin__popup {
	color: var(--vent-deep);
	font-weight: 700;
	text-decoration: none;
}
.map-pin__popup:hover {
	text-decoration: underline;
}
.leaflet-container {
	font-family: var(--ff-body, "Plus Jakarta Sans", system-ui, sans-serif);
	background: var(--skytint);
}
.leaflet-control-attribution a {
	color: var(--vent-deep);
}

/* ============================================================================
   Listings: post grid, post cards, pagination, post navigation.
   ========================================================================== */
.post-grid {
	display: grid;
	gap: var(--s6);
}
@media (min-width: 640px) {
	.post-grid {
		grid-template-columns: repeat(2, 1fr);
	}
}
@media (min-width: 1000px) {
	.post-grid {
		grid-template-columns: repeat(3, 1fr);
	}
}
.post-card {
	display: flex;
	flex-direction: column;
	background: var(--daylight);
	border: 1px solid var(--line);
	border-radius: var(--r-tile);
	overflow: hidden;
}
.post-card__media img {
	width: 100%;
	aspect-ratio: 2 / 1;
	object-fit: cover;
}
.post-card__body {
	display: flex;
	flex-direction: column;
	gap: var(--s2);
	flex: 1;
	padding: var(--s5);
}
.post-card__title {
	font-size: 1.375rem;
}
.post-card__title a {
	color: var(--charslate);
	text-decoration: none;
}
.post-card__title a:hover {
	color: var(--vent-deep);
}
.post-card__meta {
	color: var(--slate-60);
	font-size: .9rem;
}
.post-card__excerpt {
	color: var(--slate-60);
	flex: 1;
}
.post-card__link {
	margin-top: var(--s2);
	font-weight: 700;
	color: var(--vent-deep);
	text-decoration: none;
}
.post-card__link:hover {
	color: var(--vent);
}
.pagination {
	margin-top: var(--s8);
}
.pagination .nav-links {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s2);
	align-items: center;
}
.pagination .page-numbers {
	display: inline-flex;
	min-width: 2.5em;
	height: 2.5em;
	align-items: center;
	justify-content: center;
	padding: 0 .6em;
	border: 1px solid var(--line);
	border-radius: var(--r-btn);
	color: var(--charslate);
	text-decoration: none;
	font-weight: 700;
}
.pagination a.page-numbers:hover {
	background: var(--skytint);
}
.pagination .page-numbers.current {
	background: var(--vent);
	color: var(--daylight);
	border-color: var(--vent);
}
.entry-meta {
	margin-bottom: var(--s5);
	color: var(--slate-60);
	font-size: .9375rem;
}
.entry-figure {
	margin-bottom: var(--s6);
}
.entry-figure img {
	width: 100%;
	border-radius: var(--r-tile);
}
.post-navigation {
	margin-top: var(--s9);
	padding-top: var(--s5);
	border-top: 1px solid var(--line);
	display: flex;
	justify-content: space-between;
	gap: var(--s4);
}
.post-navigation .nav-label {
	display: block;
	font-size: .8125rem;
	text-transform: uppercase;
	letter-spacing: .07em;
	color: var(--slate-60);
}
.post-navigation a {
	color: var(--charslate);
	text-decoration: none;
	font-weight: 700;
}
.post-navigation a:hover {
	color: var(--vent-deep);
}

/* --- No-results and 404 helpers. --- */
.no-results,
.error-404 {
	display: flex;
	flex-direction: column;
	gap: var(--s5);
}
.error-404-actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s4);
	align-items: center;
}
.error-404-hours {
	color: var(--slate-60);
	font-size: .9375rem;
}

/* --- Comments (minimal). --- */
.comments-area {
	border-top: 1px solid var(--line);
}
.comments-title {
	margin-bottom: var(--s5);
}
.comment-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var(--s5);
}
.comments-closed {
	color: var(--slate-60);
}

/* ============================================================================
   Footer (DESIGN.md section 5): navy-deep ground, one shade darker than the
   .band--dark gradient's base navy, so a page ending on a dark band still
   shows a seam before the footer. Old --on-dark-soft/--on-dark-muted tokens
   (retired from :root, see critical.css) are replaced here with the single
   v2 --on-dark (78%) body-text value, plus the section's own literal
   rgba(255,255,255,.55) for the dimmer legal line (DESIGN.md section 12).
   ========================================================================== */
.site-footer {
	background: var(--navy-deep);
	color: var(--on-dark);
	border-top: 1px solid var(--line-dark);
}
.footer-grid {
	display: grid;
	gap: var(--s7);
	padding-block: clamp(2.5rem, 5vw, 4rem);
}
@media (min-width: 700px) {
	.footer-grid {
		grid-template-columns: repeat(2, 1fr);
	}
}
@media (min-width: 1080px) {
	.footer-grid {
		grid-template-columns: repeat(4, 1fr) 1.3fr;
	}
}
.footer-col .footer-heading,
.footer-col h4 {
	margin-bottom: var(--s3);
	color: var(--daylight);
	font-size: 1rem;
}
.footer-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: .55em;
}
.footer-list a {
	color: var(--on-dark);
	text-decoration: none;
	font-size: .95rem;
}
.footer-list a:hover {
	color: var(--daylight);
}
.footer-contact address {
	display: flex;
	flex-direction: column;
	gap: .5em;
}
.footer-contact p {
	font-size: .95rem;
}
.footer-contact a {
	color: var(--daylight);
	font-weight: 700;
	text-decoration: none;
}
.footer-contact a:hover {
	color: var(--skytint);
}
.footer-hours {
	color: var(--on-dark);
	font-size: .9rem;
}
.footer-badges {
	display: flex;
	flex-wrap: wrap;
	gap: var(--s3);
	align-items: center;
	padding-block: var(--s6);
	border-top: 1px solid var(--line-dark);
}
.badge-chip {
	display: inline-flex;
	align-items: center;
	background: var(--daylight);
	border-radius: var(--r-btn);
	padding: .4em .6em;
}
.badge-chip img {
	height: 46px;
	width: auto;
}
.footer-legal {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--s4);
	padding-block: var(--s5);
	border-top: 1px solid var(--line-dark);
	/* DESIGN.md section 12: legal line specifically at rgba(255,255,255,.55),
	   dimmer than the general --on-dark (.78) body text used elsewhere. */
	color: rgba(255, 255, 255, .55);
	font-size: .85rem;
}
.footer-logo-chip {
	display: inline-flex;
	background: var(--daylight);
	border-radius: var(--r-btn);
	padding: .3em .5em;
}
.footer-logo-chip img {
	height: 34px;
	width: auto;
}
.footer-legal-line {
	flex: 1 1 auto;
}
.footer-privacy {
	color: rgba(255, 255, 255, .55);
}
.footer-privacy:hover {
	color: var(--daylight);
}

/* ============================================================================
   Print: hide chrome, keep content legible.
   ========================================================================== */
@media print {
	.site-header,
	.mobile-bar,
	.nav-backdrop,
	.footer-badges,
	.skip-link {
		display: none !important;
	}
	body {
		color: #000;
		background: #fff;
	}
	.band {
		padding-block: 1rem;
	}
	a {
		text-decoration: underline;
	}
}
