/*
Theme Name: Sunny
Theme URI: https://sunnybuildersgroupsd.com/
Author: Sunny Builders Group
Author URI: https://sunnybuildersgroupsd.com/
Description: Fast, custom Gutenberg block theme for Sunny Builders Group (San Diego exterior remodeling). No page builders. All variable content flows from the data layer (data/*.json) — see CLAUDE.md. Design tokens are generated into theme.json from data/brand.json.
Requires at least: 6.5
Tested up to: 6.6
Requires PHP: 8.0
Version: 0.38.0
License: GNU General Public License v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: sunny
*/

/*
 * Design-system CSS ("Site paper / builder's olive" — see CLAUDE.md §9).
 * RULE (enforced by `npm run check`): no raw hex colors or font-family literals —
 * only var(--wp--preset--*) / var(--wp--custom--*) tokens. Component dimensions
 * that the nav spec fixes numerically (header/subnav/callbar heights) live as
 * --sb-* custom properties in the :root block below, nowhere else.
 */

:root {
	/* Nav spec budgets (CLAUDE.md §9): header ≤64px condensed desktop,
	   subnav ≤48px, callbar ≤64px, combined stack ≤112px @390 / ≤120px @1440. */
	--sb-header-h: 4rem;        /* 64px mobile, relaxed */
	--sb-header-h-cond: 3.5rem; /* 56px mobile, condensed */
	--sb-subnav-h: 2.75rem;     /* 44px */
	--sb-callbar-h: 3.5rem;     /* 56px */
	--sb-tap: 2.75rem;          /* 44px minimum tap target */
	--sb-hero-chrome: 27.5rem; /* T1 stacked hero, >=900px: measured height of
	   the ABOVE-FOLD set around the reveal photo (header+chips+H1+dek+review
	   badge+margins; CTAs sit below the fold by design, D129) — the photo
	   gets what's left of 100vh, so the poster closes above the fold */
	/* TOKEN ALIAS LAYER (consolidation pass 2026-07-13): short handles for the
	   most-repeated theme.json tokens, defined here ONLY. They resolve per-skin
	   at use time, so both palettes flow through unchanged. Rarely-used tokens
	   keep their full var(--wp--...) form; add an alias only when a token
	   crosses ~15 uses. Colors: acc=accent ink=foreground line=border
	   pri=primary pc=primary-contrast sec=secondary mut=muted.
	   Type/space: semi=weight-semibold fs-s=font-sm sp-s/m/l=spacing-sm/md/lg.
	   pill=radius-pill, bg=color-background (2026-08-14 consolidation). */
	--sb-acc: var(--wp--preset--color--accent);
	--sb-pill: var(--wp--custom--radius--pill);
	--sb-bg: var(--wp--preset--color--background);
	--sb-ink: var(--wp--preset--color--foreground);
	--sb-line: var(--wp--preset--color--border);
	--sb-pri: var(--wp--preset--color--primary);
	--sb-pc: var(--wp--preset--color--primary-contrast);
	--sb-sec: var(--wp--preset--color--secondary);
	--sb-mut: var(--wp--preset--color--muted);
	--sb-sur: var(--wp--preset--color--surface);
	--sb-not: var(--wp--preset--color--notice);
	--sb-sh-m: var(--wp--custom--shadow--md);
	--sb-sh-l: var(--wp--custom--shadow--lg);
	--sb-fs-l: var(--wp--preset--font-size--lg);
	--sb-rad-s: var(--wp--custom--radius--sm);
	--sb-rad-m: var(--wp--custom--radius--md);
	--sb-rad-l: var(--wp--custom--radius--lg);
	--sb-semi: var(--wp--custom--font-weight--semibold);
	--sb-bold: var(--wp--custom--font-weight--bold);
	--sb-fs-s: var(--wp--preset--font-size--sm);
	--sb-sp-s: var(--wp--preset--spacing--sm);
	--sb-sp-m: var(--wp--preset--spacing--md);
	--sb-sp-l: var(--wp--preset--spacing--lg);
	--sb-focus-ring: 2px solid var(--sb-acc);
}
@media (min-width: 1024px) {
	:root {
		--sb-header-h: 5rem;       /* 80px desktop, relaxed */
		--sb-header-h-cond: 4rem;  /* 64px desktop, condensed */
	}
}

/* ---------------------------------------------------------------------------
 * Base
 * ------------------------------------------------------------------------- */
html {
	/* anchor jumps clear the sticky header+subnav stack */
	scroll-padding-top: calc(var(--sb-header-h-cond) + var(--sb-subnav-h) + 0.5rem);
}
/* sections own their spacing (theme.json blockGap is 0); belt-and-braces for
   older cached global styles so no stray gap appears under the header */
.wp-site-blocks > * + *,
main.wp-block-group > * + * { margin-block-start: 0; }

p { text-wrap: pretty; }
@media (prefers-reduced-motion: no-preference) {
	html { scroll-behavior: smooth; }
}
body { -webkit-font-smoothing: antialiased; }

h1, h2 {
	text-transform: uppercase;
	letter-spacing: 0.015em;
	text-wrap: balance;
}

::selection {
	background: var(--sb-acc);
	color: var(--sb-ink);
}

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
	outline: var(--sb-focus-ring);
	outline-offset: 2px;
	border-radius: var(--sb-rad-s);
}

a { text-underline-offset: 0.2em; transition: color 120ms ease; }
a:hover { color: var(--sb-ink); text-decoration-thickness: 2px; }

.sb-icon { flex: none; }

/* ---------------------------------------------------------------------------
 * Buttons — exactly ONE primary style site-wide (sb-btn--primary)
 * ------------------------------------------------------------------------- */
.sb-btn {
	box-sizing: border-box; /* width:100% buttons must not overflow their panel */
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.5rem;
	min-height: 3rem;
	padding: 0.625rem 1.25rem;
	border: 1.5px solid transparent;
	border-radius: var(--sb-rad-s);
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--md);
	font-weight: var(--sb-semi);
	line-height: 1.2;
	text-decoration: none;
	cursor: pointer;
	transition: transform 140ms ease, box-shadow 140ms ease, background-color 140ms ease, border-color 140ms ease;
}
.sb-btn:hover { transform: translateY(-2px); box-shadow: var(--sb-sh-m); text-decoration: none; }
.sb-btn:active { transform: translateY(0); box-shadow: var(--wp--custom--shadow--sm); }

.sb-btn--primary {
	background: var(--sb-pri);
	color: var(--sb-pc);
}
.sb-btn--primary:hover {
	background: color-mix(in srgb, var(--sb-pri), var(--sb-ink) 18%);
	color: var(--sb-pc);
}

/* secondary/ghost: outline, inherits context color (works on light + dark) */
.sb-btn--ghost {
	background: transparent;
	border-color: currentColor;
	color: inherit;
}
.sb-btn--ghost:hover { background: color-mix(in srgb, currentColor 10%, transparent); color: inherit; }

/* hero / final-CTA size — the conversion buttons carry more weight */
.sb-btn--lg {
	min-height: 3.5rem;
	padding: 0.875rem 1.75rem;
	font-size: var(--sb-fs-l);
}

/* ---------------------------------------------------------------------------
 * Motif 3: spirit-level rule (section eyebrows, header bottom edge)
 * ------------------------------------------------------------------------- */
.sb-level {
	display: flex;
	align-items: center;
	gap: 0.625rem;
	width: 9rem;
	max-width: 40%;
	height: 0.875rem;
	color: inherit;
}
.sb-level::before,
.sb-level::after {
	content: "";
	flex: 1;
	height: 2px;
	background: currentColor;
	opacity: 0.4;
}
.sb-level__bubble {
	position: relative;
	flex: none;
	width: 2.5rem;
	height: 0.875rem;
	border: 1.5px solid currentColor;
	border-radius: var(--sb-pill);
	opacity: 0.9;
}
.sb-level__bubble::before {
	/* the bubble, dead-center: "we build level" */
	content: "";
	position: absolute;
	top: 2.5px;
	bottom: 2.5px;
	left: 50%;
	width: 0.625rem;
	transform: translateX(-50%);
	border-radius: var(--sb-pill);
	background: var(--sb-acc);
}
.sb-level__bubble::after {
	/* gauge marks flanking center */
	content: "";
	position: absolute;
	top: -4px;
	bottom: -4px;
	left: calc(50% - 0.5rem);
	width: 1rem;
	border-left: 1.5px solid currentColor;
	border-right: 1.5px solid currentColor;
	opacity: 0.55;
}

/* ---------------------------------------------------------------------------
 * Motif 4: tape-measure ticks (process numbering, trust-bar top edge)
 * ------------------------------------------------------------------------- */
.sb-ticks-edge {
	background-image: repeating-linear-gradient(
		90deg,
		var(--sb-line) 0 1px,
		transparent 1px 0.875rem
	);
	background-size: 100% 0.5rem;
	background-repeat: no-repeat;
	background-position: top left;
}
.sb-step-num {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	font-family: var(--wp--preset--font-family--heading);
	font-size: var(--wp--preset--font-size--xl);
	font-weight: var(--sb-bold);
	/* primary, not accent: accent green is only 2.3:1 on paper (AA fail) */
	color: var(--sb-pri);
	line-height: 1;
}
.sb-section--dark .sb-step-num { color: var(--sb-acc); }
.sb-step-num::after {
	content: "";
	width: 2.25rem;
	height: 0.75rem;
	background-image: repeating-linear-gradient(
		90deg,
		currentColor 0 1.5px,
		transparent 1.5px 0.5rem
	);
	opacity: 0.5;
}

/* Motif 2 (v2): filled roofline edge — the next section's background rises
   into a gable silhouette. Color it with the section's own background. */
/* (roof-cap divider removed entirely — owner 2026-07-13: sections meet flush) */

/* ---------------------------------------------------------------------------
 * Motif 1: gable image mask (hero media, case-study feature ONLY)
 * ------------------------------------------------------------------------- */
.sb-mask-gable {
	clip-path: polygon(0 16%, 50% 0, 100% 16%, 100% 100%, 0 100%);
}
/* ridge variant: the wrapper's accent background shows through above the
   padded-down image as a thin roofline stroke along the gable cut */
.sb-mask-gable--ridge {
	background: var(--sb-acc);
	padding-top: 5px;
}

/* ---------------------------------------------------------------------------
 * Header + primary nav
 * ------------------------------------------------------------------------- */
.sb-header {
	position: sticky;
	top: 0;
	z-index: 60;
	background: var(--sb-ink);
	color: var(--sb-pc);
}
.sb-header::after {
	/* spirit-level rule as the header's bottom edge (static motif variant) */
	content: "";
	display: block;
	height: 2px;
	background: linear-gradient(
		90deg,
		var(--sb-sec) 0 calc(50% - 1.5rem),
		var(--sb-acc) calc(50% - 1.5rem) calc(50% + 1.5rem),
		var(--sb-sec) calc(50% + 1.5rem) 100%
	);
}
.sb-header__bar {
	display: flex;
	align-items: center;
	gap: 1rem;
	max-width: var(--wp--style--global--wide-size);
	margin-inline: auto;
	padding-inline: var(--sb-sp-s);
	height: var(--sb-header-h);
	transition: height 200ms ease;
}
html.sb-js .sb-header.is-condensed .sb-header__bar { height: var(--sb-header-h-cond); }

.sb-header__logo { display: inline-flex; align-items: center; flex: none; }
.sb-header__logo img {
	display: block;
	width: auto;
	height: 3.25rem; /* the mark carries small text — it must be READABLE */
	transition: height 200ms ease;
}
@media (min-width: 1024px) { .sb-header__logo img { height: 3.75rem; } }
html.sb-js .sb-header.is-condensed .sb-header__logo img { height: 2.75rem; }
@media (min-width: 1024px) { html.sb-js .sb-header.is-condensed .sb-header__logo img { height: 3.25rem; } }

.sb-nav { display: none; }
@media (min-width: 1024px) {
	.sb-nav { display: block; margin-inline: auto; }
	.sb-nav__list {
		display: flex;
		align-items: center;
		gap: 0.25rem;
		list-style: none;
		margin: 0;
		padding: 0;
	}
	.sb-nav__link {
		display: inline-flex;
		align-items: center;
		gap: 0.375rem;
		min-height: var(--sb-tap);
		padding: 0.5rem 0.875rem;
		white-space: nowrap;
		border: 0;
		border-radius: var(--sb-rad-s);
		background: transparent;
		color: var(--sb-pc);
		font: inherit;
		font-weight: var(--wp--custom--font-weight--medium);
		text-decoration: none;
		cursor: pointer;
		transition: background-color 120ms ease, color 120ms ease;
	}
	.sb-nav__link:hover {
		background: var(--sb-sec);
		color: var(--sb-pc);
	}
	.sb-nav__link[aria-expanded="true"] { background: var(--sb-sec); }
	.sb-nav__link[aria-expanded="true"] .sb-icon { transform: rotate(180deg); }
	.sb-nav__link .sb-icon { width: 1rem; height: 1rem; transition: transform 160ms ease; }
}

.sb-header__cta { display: none; }
@media (min-width: 1024px) {
	.sb-header__cta { display: flex; align-items: center; gap: 0.875rem; flex: none; }
	.sb-header__tel {
		display: inline-flex;
		align-items: center;
		gap: 0.5rem;
		color: var(--sb-pc);
		font-weight: var(--sb-semi);
		text-decoration: none;
	}
	.sb-header__tel:hover { color: var(--sb-acc); }
	.sb-header__tel .sb-icon { width: 1.125rem; height: 1.125rem; color: var(--sb-acc); }
}

/* --- mega menu (desktop) --- */
.sb-mega {
	position: absolute;
	inset-inline: 0;
	top: 100%;
	z-index: 55;
	background: var(--sb-bg);
	color: var(--sb-ink);
	border-top: 3px solid var(--sb-acc);
	box-shadow: var(--sb-sh-l);
}
.sb-mega__inner {
	display: grid;
	/* content-sized columns spread across the bar; nowrap keeps every item one line */
	grid-template-columns: repeat(4, minmax(0, auto));
	justify-content: space-between;
	gap: var(--sb-sp-m);
	max-width: var(--wp--style--global--wide-size);
	margin-inline: auto;
	padding: var(--sb-sp-m) var(--sb-sp-s) var(--sb-sp-l);
}
.sb-mega__group-title {
	display: block;
	margin: 0 0 0.625rem;
	font-size: var(--sb-fs-s);
	font-weight: var(--sb-bold);
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--sb-mut);
}
.sb-mega__list { list-style: none; margin: 0; padding: 0; }
.sb-mega__list a {
	display: flex;
	align-items: center;
	gap: 0.625rem;
	min-height: var(--sb-tap);
	padding: 0.375rem 0.5rem;
	margin-inline: -0.5rem;
	border-radius: var(--sb-rad-s);
	color: var(--sb-ink);
	text-decoration: none;
	white-space: nowrap;
	transition: background-color 120ms ease, color 120ms ease;
}
.sb-mega__list a:hover { background: var(--sb-sur); color: var(--sb-pri); }
.sb-mega__list .sb-icon { width: 1.375rem; height: 1.375rem; color: var(--sb-pri); }
.sb-mega__all { font-weight: var(--sb-semi); color: var(--sb-pri); }

/* "Coming soon" items (Mor 2026-09-15): a non-clickable <a> (no href), dimmed, native title tooltip on hover. Nav + tools hub. Mobile/no-hover surfaces add a plain <em>coming soon</em> label (no extra CSS). */
.sb-soon { opacity: .5; cursor: default; position: relative; }
/* Instant styled tooltip on hover (native title is slow/unreliable); nav dropdowns only. */
.sb-mega__list .sb-soon:hover::after { content: attr(title); position: absolute; top: 100%; left: 0; z-index: 60; padding: .15rem .5rem; background: var(--sb-ink); color: var(--sb-pc); font-size: .72rem; white-space: nowrap; }

/* the About dropdown reuses .sb-mega__list styling inside a compact panel */
.sb-nav__item { position: relative; }
.sb-drop {
	position: absolute;
	top: calc(100% + 0.5rem);
	left: 50%;
	transform: translateX(-50%);
	z-index: 55;
	min-width: 13.5rem;
	padding: 0.625rem;
	background: var(--sb-bg);
	color: var(--sb-ink);
	border-top: 3px solid var(--sb-acc);
	border-radius: 0 0 var(--sb-rad-m) var(--sb-rad-m);
	box-shadow: var(--sb-sh-l);
}
.sb-drop .sb-mega__list a { margin-inline: 0; }

/* --- burger + mobile sheet --- */
.sb-burger {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: var(--sb-tap);
	height: var(--sb-tap);
	margin-left: auto;
	border: 0;
	border-radius: var(--sb-rad-s);
	background: transparent;
	color: var(--sb-pc);
	cursor: pointer;
}
.sb-burger:hover { background: var(--sb-sec); }
@media (min-width: 1024px) { .sb-burger { display: none; } }

.sb-sheet {
	position: fixed;
	inset: 0;
	z-index: 80;
	display: flex;
	flex-direction: column;
	background: var(--sb-ink);
	color: var(--sb-pc);
}
.sb-sheet[hidden] { display: none; }
.sb-sheet__top {
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex: none;
	height: var(--sb-header-h-cond);
	padding-inline: var(--sb-sp-s);
	border-bottom: 1px solid var(--sb-sec);
}
.sb-sheet__top img { display: block; width: auto; height: 2.75rem; }
.sb-sheet__close {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: var(--sb-tap);
	height: var(--sb-tap);
	border: 0;
	border-radius: var(--sb-rad-s);
	background: transparent;
	color: inherit;
	cursor: pointer;
}
.sb-sheet__close:hover { background: var(--sb-sec); }
.sb-sheet__scroll {
	flex: 1;
	overflow-y: auto;
	padding: var(--sb-sp-s) var(--sb-sp-s) var(--sb-sp-l);
}
.sb-sheet__group { border-bottom: 1px solid var(--sb-sec); }
.sb-sheet__group > summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	min-height: 3rem;
	padding: 0.5rem 0.25rem;
	font-weight: var(--sb-semi);
	list-style: none;
	cursor: pointer;
}
.sb-sheet__group > summary::-webkit-details-marker { display: none; }
.sb-sheet__group > summary .sb-icon { width: 1.125rem; height: 1.125rem; transition: transform 160ms ease; }
.sb-sheet__group[open] > summary .sb-icon { transform: rotate(180deg); }
.sb-sheet__group ul { list-style: none; margin: 0; padding: 0 0 0.625rem; }
.sb-sheet__group ul a,
.sb-sheet__flat {
	display: flex;
	align-items: center;
	gap: 0.625rem;
	min-height: var(--sb-tap);
	padding: 0.375rem 0.25rem;
	color: inherit;
	text-decoration: none;
	border-radius: var(--sb-rad-s);
}
.sb-sheet__group ul a { color: var(--sb-line); }
.sb-sheet__group ul a:hover,
.sb-sheet__flat:hover { color: var(--sb-acc); }
.sb-sheet__group .sb-icon { width: 1.25rem; height: 1.25rem; color: var(--sb-acc); }
.sb-sheet__flat { font-weight: var(--sb-semi); border-bottom: 1px solid var(--sb-sec); }
.sb-sheet__cta {
	flex: none;
	display: grid;
	grid-template-columns: 1fr; /* stacked: each CTA stays on ONE line */
	gap: 0.625rem;
	padding: var(--sb-sp-s);
	padding-bottom: max(var(--sb-sp-s), env(safe-area-inset-bottom));
	border-top: 1px solid var(--sb-sec);
	background: var(--sb-ink);
}
html.sb-lock, html.sb-lock body { overflow: hidden; }

/* ---------------------------------------------------------------------------
 * Sticky bottom call bar (mobile; appears after the hero; hides on form focus)
 * ------------------------------------------------------------------------- */
/* docked flush to the viewport bottom (Mor 2026-08-19: the old inset floating
   pill hovered mid-air over content as iOS Safari's toolbar collapsed —
   read as broken, not designed). Full-width bar, softened top corners, and
   the safe-area inset absorbed as bottom padding so it reaches the physical
   edge behind the home indicator. */
.sb-callbar {
	position: fixed;
	inset-inline: 0;
	bottom: 0;
	z-index: 70;
	box-sizing: border-box; /* padding inside the ≤64px height budget */
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 0.375rem;
	height: calc(var(--sb-callbar-h) + env(safe-area-inset-bottom));
	padding: 0.375rem 0.625rem calc(0.375rem + env(safe-area-inset-bottom));
	background: var(--sb-ink);
	border-top: 1px solid var(--sb-sec);
	border-radius: var(--sb-rad-l) var(--sb-rad-l) 0 0;
	box-shadow: var(--sb-sh-l);
	transform: translateY(calc(100% + 1.25rem));
	transition: transform 220ms ease;
}
.sb-callbar .sb-btn { border-radius: var(--sb-pill); font-size: var(--sb-fs-s); padding-inline: 0.625rem; white-space: nowrap; }
.sb-callbar.is-on { transform: translateY(0); }
.sb-callbar .sb-btn { min-height: 0; height: 100%; }
.sb-callbar .sb-btn--ghost { color: var(--sb-pc); }
@media (min-width: 1024px) { .sb-callbar { display: none; } }

/* ---------------------------------------------------------------------------
 * Long-page sub-nav (scrollspy chips; sticks under the header)
 * ------------------------------------------------------------------------- */
.sb-subnav {
	position: sticky;
	top: var(--sb-header-h-cond);
	z-index: 50;
	height: var(--sb-subnav-h);
	background: var(--sb-bg);
	border-bottom: 1px solid var(--sb-line);
}
.sb-subnav__track { height: 100%; overflow-x: auto; scrollbar-width: none; }
.sb-subnav__track::-webkit-scrollbar { display: none; }
/* Photo Wall's 15-trade jump bar: contain to the content width on desktop and
   fade the clipped edge so the overflow reads as scrollable, not cut off. */
.page-template-page-photo-wall .sb-subnav__track { max-width: var(--wp--style--global--wide-size); margin-inline: auto; padding-inline: var(--sb-sp-s); mask-image: linear-gradient(to right, var(--sb-ink) 92%, transparent); }
.sb-subnav__list {
	display: flex;
	align-items: stretch;
	gap: 0.25rem;
	height: 100%;
	max-width: var(--wp--style--global--wide-size);
	margin-inline: auto;
	padding-inline: var(--sb-sp-s);
	list-style: none;
	margin-block: 0;
}
.sb-subnav__list { align-items: center; gap: 0.25rem; }
.sb-subnav__list a {
	display: inline-flex;
	align-items: center;
	height: 2.125rem; /* pill inside the 44px bar — every chip on ONE line */
	padding-inline: 0.875rem;
	border-radius: var(--sb-pill);
	color: var(--sb-mut);
	font-size: var(--sb-fs-s);
	font-weight: var(--wp--custom--font-weight--medium);
	text-decoration: none;
	white-space: nowrap;
	transition: color 120ms ease, background-color 120ms ease;
}
.sb-subnav__list a:hover { color: var(--sb-ink); background: var(--sb-sur); }
.sb-subnav__list a.is-active {
	color: var(--sb-ink);
	background: color-mix(in srgb, var(--sb-acc) 26%, transparent);
	font-weight: var(--sb-semi);
}

/* ---------------------------------------------------------------------------
 * Sections scaffolding
 * ------------------------------------------------------------------------- */
.sb-section { padding-block: var(--wp--preset--spacing--xl); }
.sb-section--tight { padding-block: var(--sb-sp-l); }
/* "blueprint at dusk": every ink panel carries a faint drafting grid + two
   soft glows (accent top-left, primary bottom-right) — depth, not decoration */
.sb-section--dark,
.sb-footer {
	background-color: var(--sb-ink);
	color: var(--sb-pc);
	background-image:
		radial-gradient(56rem 30rem at 12% -6%, color-mix(in srgb, var(--sb-acc) 10%, transparent), transparent 68%),
		radial-gradient(44rem 28rem at 98% 112%, color-mix(in srgb, var(--sb-pri) 26%, transparent), transparent 62%),
		repeating-linear-gradient(90deg, color-mix(in srgb, var(--sb-pc) 3%, transparent) 0 1px, transparent 1px 56px),
		repeating-linear-gradient(0deg, color-mix(in srgb, var(--sb-pc) 3%, transparent) 0 1px, transparent 1px 56px);
}
.sb-section--dark a:not(.sb-btn) { color: var(--sb-acc); }
.sb-section--dark a:not(.sb-btn):hover { color: var(--sb-pc); }
.sb-section--surface { background: var(--sb-sur); }

.sb-eyebrow {
	display: block;
	margin-bottom: 0.5rem;
	font-size: var(--sb-fs-s);
	font-weight: var(--sb-bold);
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--sb-pri);
}
.sb-section--dark .sb-eyebrow { color: var(--sb-acc); }
.sb-section__head { margin-bottom: var(--sb-sp-l); }
.sb-section__intro { max-width: 44rem; margin: 0.875rem 0 0; color: var(--sb-mut); font-size: var(--sb-fs-l); }
.sb-section--dark .sb-section__intro { color: var(--sb-line); } /* muted fails AA on ink */
.sb-section__head .sb-level { margin-top: 0.875rem; color: var(--sb-mut); }

/* ---------------------------------------------------------------------------
 * Cards + media
 * ------------------------------------------------------------------------- */
.sb-card {
	position: relative;
	display: flex;
	flex-direction: column;
	background: var(--sb-bg);
	border: 1px solid var(--sb-line);
	border-radius: var(--sb-rad-l);
	box-shadow: var(--wp--custom--shadow--sm);
	overflow: hidden;
	transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
}
.sb-card .sb-media img { transition: transform 500ms ease; }
.sb-card:hover .sb-media img { transform: scale(1.04); }
.sb-card:hover {
	transform: translateY(-3px);
	box-shadow: var(--sb-sh-m);
	border-color: var(--sb-acc);
}
.sb-card:focus-within { outline: var(--sb-focus-ring); outline-offset: 2px; }
.sb-card__body { display: flex; flex-direction: column; gap: 0.375rem; padding: var(--sb-sp-m); }
.sb-card--center .sb-card__body { text-align: center; align-items: center; }
.sb-card__link { color: inherit; text-decoration: none; font-weight: var(--sb-semi); }
.sb-card__link::after { content: ""; position: absolute; inset: 0; } /* whole card clickable */
.sb-card__link:focus-visible { outline: none; } /* focus ring handled by .sb-card:focus-within */
/* Light cards floating on an ink panel (related-reading's "Put this to work").
   .sb-card keeps the paper background, so the inherited dark-section colors
   would paint primary-contrast body text and ACCENT GREEN links/eyebrows onto
   paper — 2.3:1, an AA fail (§9). Reset the card interior to the light palette;
   .sb-card__more is a <span> and is already primary. */
.sb-section--dark .sb-card { color: var(--sb-ink); }
.sb-section--dark .sb-card a:not(.sb-btn) { color: inherit; }
.sb-section--dark .sb-card .sb-eyebrow { color: var(--sb-pri); }
/* Service cards (sunny_service_card): centered; the service NAME is the card's
   one link (keyword-bearing anchor). The trade icon rides an ink badge that
   straddles the photo edge — accent-on-ink is an AA pair on BOTH skins (lime
   on near-black / logo green on warm ink) and inverts on hover. */
.sb-card--service .sb-card__body { padding-top: 0; gap: 0.5rem; }
.sb-card__badge {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	/* Own paint layer, unconditionally. The photo's hover zoom is a transform,
	   which paints ABOVE untransformed inline content — and the badge's own
	   transform expires at 160ms while the photo keeps transforming to 500ms,
	   so on hover-OUT the badge sank under the photo for ~340ms and its top
	   half vanished. z-index (not a transform) keeps the order fixed at every
	   frame. pointer-events:none because this now sits above the stretched
	   .sb-card__link::after overlay: the badge is decorative (aria-hidden
	   icon), so clicks must pass through and keep the whole card clickable. */
	position: relative;
	z-index: 1;
	pointer-events: none;
	width: 3rem;
	height: 3rem;
	flex: none;
	margin-top: -1.5rem; /* half the badge sits on the photo */
	background: var(--sb-ink);
	color: var(--sb-acc);
	border: 3px solid var(--sb-bg); /* ring = card bg */
	border-radius: var(--sb-pill);
	box-shadow: var(--wp--custom--shadow--sm);
	transition: background 160ms ease, color 160ms ease, transform 160ms ease;
}
.sb-card--service:hover .sb-card__badge,
.sb-story .sb-card:hover .sb-card__badge { background: var(--sb-acc); color: var(--sb-ink); transform: translateY(-2px); }
.sb-card--service:hover .sb-card__link { color: var(--sb-pri); text-decoration: underline; }

.sb-media { display: block; overflow: hidden; background: var(--sb-sur); }
.sb-media img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* origin:"render" media only (service-gallery): the always-on "3D design rendering" tag */
.sb-media--render { position: relative; }
.sb-media__tag { position: absolute; left: 0.5rem; bottom: 0.5rem; z-index: 1; margin: 0; padding: 0.2rem 0.65rem; border-radius: var(--sb-pill); background: color-mix(in srgb, var(--sb-ink) 80%, transparent); color: var(--sb-bg); font-size: var(--sb-fs-s); line-height: 1.4; }
/* ---------------------------------------------------------------------------
 * Forms (estimate form: ≤5 fields, visible labels, trust element)
 * ------------------------------------------------------------------------- */
.sb-form { display: grid; gap: 0.875rem; }
.sb-form__note { margin: 0; font-size: var(--sb-fs-s); color: var(--sb-mut); }
.sb-form__row { display: grid; gap: 0.375rem; }
.sb-form label {
	font-size: var(--wp--preset--font-size--md);
	font-weight: var(--sb-semi);
}
.sb-input {
	box-sizing: border-box;
	width: 100%;
	min-height: 3rem;
	padding: 0.5rem 0.875rem;
	border: 1.5px solid var(--sb-line);
	border-radius: var(--sb-rad-s);
	background: var(--sb-bg);
	color: var(--sb-ink);
	font: inherit;
	transition: border-color 120ms ease, box-shadow 120ms ease;
}
.sb-input:hover { border-color: var(--sb-mut); }
.sb-input:focus { border-color: var(--sb-pri); outline: var(--sb-focus-ring); outline-offset: 1px; }
.sb-form__trust {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	font-size: var(--sb-fs-s);
	color: var(--sb-mut);
}
.sb-form__trust .sb-icon { width: 1.125rem; height: 1.125rem; color: var(--sb-pri); }
.sb-form .sb-btn[disabled] { opacity: 0.6; cursor: progress; transform: none; box-shadow: none; }
/* review trust badge (sunny_review_badge): stars + count, links to /reviews/.
   Stars default to accent (dark hero contexts); inside the light form card
   they switch to primary (accent never sits on light — §9). Wraps as two
   CENTERED rows (stars / one-line text) where the card is narrow — the text
   never breaks mid-sentence (Mor 2026-08-19: the ragged wrap read broken). */
.sb-revbadge {
	display: inline-flex;
	align-items: center;
	flex-wrap: wrap;
	justify-content: center;
	gap: 0.5rem;
	font-weight: var(--sb-semi);
	color: inherit;
	text-decoration: none;
}
.sb-revbadge > span:last-child { white-space: nowrap; }
.sb-revbadge:hover span:last-child { text-decoration: underline; }
/* every star row (badge, rating pill, review card) shares one rule; each
   element carries only its own class, so the merge is cascade-neutral */
.sb-revbadge__stars, .sb-rating-pill__stars, .sb-review__stars { display: inline-flex; gap: 0.125rem; color: var(--sb-acc); }
.sb-revbadge__stars .sb-icon { width: 1.125rem; height: 1.125rem; fill: currentColor; }
.sb-form .sb-revbadge__stars { color: var(--sb-pri); }
/* inside the light form card the WHOLE badge is persistent primary, even on
   hover (owner 2026-07-13) — outranks the dark-section link colors that leak
   in when the card floats on an ink panel */
.sb-form a.sb-revbadge,
.sb-form a.sb-revbadge:hover { color: var(--sb-pri); }
.sb-form__badge { justify-self: center; }

/* ---------------------------------------------------------------------------
 * Hero (T1 poster hero + T18 form hero)
 * ------------------------------------------------------------------------- */
.sb-hero {
	position: relative;
	padding-block: var(--sb-sp-l) 0;
	overflow: hidden;
}
.sb-hero .sb-container { position: relative; z-index: 1; }
.sb-hero__grid { display: grid; gap: var(--sb-sp-m); align-items: end; }
.sb-hero__chips { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: var(--sb-sp-s); }
.sb-chip {
	display: inline-flex;
	align-items: center;
	gap: 0.375rem;
	padding: 0.25rem 0.75rem;
	border: 1px solid var(--sb-sec);
	border-radius: var(--sb-pill);
	font-size: var(--sb-fs-s);
	font-weight: var(--wp--custom--font-weight--medium);
	color: var(--sb-line);
}
.sb-chip .sb-icon { width: 1rem; height: 1rem; color: var(--sb-acc); }
.sb-hero__sub {
	max-width: 38rem;
	font-size: var(--sb-fs-l);
	color: var(--sb-line);
}
.sb-hero__cta { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-block: var(--sb-sp-m) 0.875rem; }
/* phones: the contact picker's three pills fit one row only at the small label */
@media (max-width: 479px) { .sb-hero__cta { flex-direction: column; align-items: stretch; } .sb-pick .sb-btn { font-size: var(--sb-fs-s); } }
.sb-hero__note {
	margin: 0 0 var(--sb-sp-s);
	font-size: var(--sb-fs-s);
	color: var(--sb-line);
}
/* T3/geo hero photo (T1 is scene-only): contained in its column so the whole
   gable reads; hidden on mobile where the build scene is the one focal point */
.sb-hero__media { margin-inline: calc(-1 * var(--sb-sp-s)); }
.sb-hero__media img { display: block; width: 100%; height: auto; }
@media (min-width: 900px) {
	.sb-hero__grid { grid-template-columns: 1.05fr 0.95fr; gap: var(--sb-sp-l); min-height: min(38rem, 78vh); }
	.sb-hero__copy { position: relative; z-index: 1; align-self: center; padding-block: var(--sb-sp-m); }
	/* inner heroes match the home title scale (poster size wrapped to 4 lines) */
	.sb-hero__copy h1 { font-size: var(--wp--preset--font-size--xxxl); text-wrap: balance; }
	.sb-hero__media { margin-inline: 0; align-self: center; }
	.sb-hero__media img { aspect-ratio: 4 / 4.6; object-fit: cover; }
}

/* ---------------------------------------------------------------------------
 * Trust bar / steps / reviews / areas / callout / FAQ / CTA
 * ------------------------------------------------------------------------- */
.sb-trustbar { padding-block: var(--sb-sp-l); }
.sb-trustbar__grid { display: grid; grid-template-columns: 1fr; gap: var(--sb-sp-m); }
@media (min-width: 600px) { .sb-trustbar__grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 900px) { .sb-trustbar__grid { grid-template-columns: repeat(4, 1fr); gap: 0; } }
.sb-trustbar__item { display: grid; gap: 0.375rem; justify-items: center; text-align: center; align-content: start; }
@media (min-width: 900px) {
	.sb-trustbar__item { padding-inline: var(--sb-sp-s); }
	.sb-trustbar__item + .sb-trustbar__item { border-left: 1px solid var(--sb-line); }
}
.sb-trustbar__value {
	display: inline-flex;
	align-items: center;
	gap: 0.625rem;
	font-family: var(--wp--preset--font-family--heading);
	font-size: var(--wp--preset--font-size--xl);
	font-weight: var(--sb-bold);
	line-height: 1.1;
	white-space: nowrap;
}
.sb-trustbar__value .sb-icon { width: 1.5rem; height: 1.5rem; flex: none; color: var(--sb-pri); }
.sb-trustbar__label { color: var(--sb-mut); font-size: var(--sb-fs-s); }

.sb-steps { list-style: none; padding: 0; counter-reset: none; position: relative; row-gap: var(--sb-sp-l); }
.sb-steps__item h3 { margin-block: 0.5rem 0.25rem; }
.sb-steps__item p { margin: 0; color: var(--sb-mut); }
.sb-section--dark .sb-steps__item p { color: var(--sb-line); } /* muted fails AA on ink */
.sb-steps__item {
	border-top: 2px solid var(--sb-line);
	padding-top: var(--sb-sp-s);
	transition: border-color 300ms ease;
}
.sb-steps__item:hover { border-top-color: var(--sb-acc); }
.sb-steps__item h3 { font-size: var(--sb-fs-l); }

.sb-rating-pill {
	display: inline-flex;
	align-items: center;
	gap: 0.625rem;
	margin: 1rem 0 0;
	padding: 0.5rem 1.125rem;
	border: 1px solid var(--sb-line);
	border-radius: var(--sb-pill);
	background: var(--sb-bg);
	box-shadow: var(--wp--custom--shadow--sm);
	font-size: var(--wp--preset--font-size--md);
	color: var(--sb-mut);
}
.sb-rating-pill strong { color: var(--sb-ink); }
.sb-rating-pill__stars .sb-icon { width: 1rem; height: 1rem; }
.sb-review { padding: var(--sb-sp-m); margin: 0; position: relative; }
.sb-review::before {
	/* oversized display-face quote glyph, watermark-weight */
	content: "\201C";
	position: absolute;
	top: 0.25rem;
	right: 0.875rem;
	font-family: var(--wp--preset--font-family--heading);
	font-size: 5rem;
	line-height: 1;
	color: var(--sb-acc);
	opacity: 0.35;
	pointer-events: none;
}
.sb-review__stars .sb-icon { width: 1.125rem; height: 1.125rem; }
.sb-review p { flex: 1; }
.sb-review footer { display: grid; margin-top: auto; }
/* review-card clamp (clamp.js): uniform heights, expandable */
.sb-clamp { flex: none; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 7; overflow: hidden; }
.sb-review__more { background: none; border: 0; padding: 0; color: var(--sb-pri); font-weight: var(--sb-semi); font-size: var(--sb-fs-s); cursor: pointer; align-self: flex-start; }
.sb-review__more:hover, .sb-review__more:focus-visible { text-decoration: underline; }
/* Gist folding (2026-08-14): the hand-picked verbatim excerpt is a <mark>
   inside the FULL review text; clamp.js folds the rest away and adds the
   toggle. Expanded, the mark keeps a soft accent wash so the pulled quote is
   findable in context. No JS = no fold, full text, no highlight logic. */
.sb-review__gist { background: color-mix(in srgb, var(--sb-acc) 16%, transparent); color: inherit; padding: 0 0.15em; }
.sb-gist--folded .sb-review__fold { display: none; }
.sb-gist--folded .sb-review__gist { background: none; padding: 0; }
.sb-review--featured .sb-review__more { color: var(--sb-acc); }
.sb-review__meta { color: var(--sb-mut); font-size: var(--sb-fs-s); }
.sb-review__meta .sb-icon { width: 1rem; height: 1rem; vertical-align: -0.1875rem; }
/* service-tag pill: each quote links to the service page it praises */
.sb-review__tag {
	justify-self: start;
	margin-top: 0.625rem;
	padding: 0.125rem 0.75rem;
	border: 1px solid var(--sb-line);
	border-radius: var(--sb-pill);
	font-size: var(--sb-fs-s);
	font-weight: var(--sb-semi);
	text-decoration: none;
	color: var(--sb-pri);
}
.sb-review__tag:hover { background: var(--sb-sur); }
/* spotlight: the page's longest quote goes full-width on the ink panel */
.sb-review--featured {
	grid-column: 1 / -1;
	display: grid;
	justify-items: center;
	text-align: center;
	gap: 0.625rem;
	padding: var(--sb-sp-l) var(--sb-sp-m);
	border-radius: var(--sb-rad-l);
	box-shadow: var(--sb-sh-m);
}
.sb-review--featured::before { right: 1.25rem; opacity: 0.5; }
.sb-review--featured p { font-size: var(--sb-fs-l); max-width: 56ch; }
.sb-review--featured .sb-review__stars .sb-icon { width: 1.5rem; height: 1.5rem; }
.sb-review--featured .sb-review__meta { color: var(--sb-line); }
.sb-review--featured .sb-review__tag { justify-self: center; color: var(--sb-acc); border-color: var(--sb-sec); }
.sb-review--featured .sb-review__tag:hover { background: var(--sb-sec); }

.sb-areas { display: flex; flex-wrap: wrap; gap: 0.5rem; list-style: none; margin: 0; padding: 0; }
.sb-areas a {
	display: inline-flex;
	align-items: center;
	gap: 0.375rem;
	min-height: var(--sb-tap);
	padding: 0.375rem 0.875rem;
	border: 1px solid var(--sb-line);
	border-radius: var(--sb-pill);
	color: var(--sb-ink);
	text-decoration: none;
	transition: border-color 120ms ease, background-color 120ms ease;
}
.sb-areas a:hover { border-color: var(--sb-pri); background: var(--sb-sur); }
.sb-areas .sb-icon { width: 1rem; height: 1rem; color: var(--sb-pri); }
.sb-areas__all { font-weight: var(--sb-semi); color: var(--sb-pri); }

.sb-callout { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sb-sp-m); text-align: left; }
.sb-callout p { margin: 0.625rem 0 0; }

.sb-faq__item { border-bottom: 1px solid var(--sb-line); }
.sb-faq__item summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	min-height: 3.25rem;
	padding: 0.75rem 0.25rem;
	font-weight: var(--sb-semi);
	font-size: var(--sb-fs-l);
	cursor: pointer;
	list-style: none;
}
.sb-faq__item summary::-webkit-details-marker { display: none; }
.sb-faq__item summary:hover { color: var(--sb-pri); }
.sb-faq__item summary .sb-icon { flex: none; transition: transform 160ms ease; }
.sb-faq__item[open] summary .sb-icon { transform: rotate(180deg); }
.sb-faq__body { padding: 0 0.25rem var(--sb-sp-s); color: var(--sb-mut); }

.sb-cta__grid { display: grid; gap: var(--sb-sp-l); }
@media (min-width: 900px) { .sb-cta__grid { grid-template-columns: 1fr 1.1fr; align-items: center; } }
.sb-cta__tel {
	margin-top: 0.75rem;
	display: inline-flex;
	align-items: center;
	gap: 0.625rem;
	font-family: var(--wp--preset--font-family--heading);
	font-size: var(--wp--preset--font-size--xxl);
	font-weight: var(--sb-bold);
	color: var(--sb-pc);
	text-decoration: none;
}
.sb-cta__tel:hover { color: var(--sb-acc); }
.sb-cta__tel .sb-icon { color: var(--sb-acc); }
.sb-cta__form {
	background: var(--sb-bg);
	color: var(--sb-ink);
	border-radius: var(--sb-rad-m);
	border-top: 4px solid var(--sb-acc);
	box-shadow: var(--sb-sh-l);
	padding: var(--sb-sp-m);
}

/* section outro (2026-08-18, Mor): the "more" pill sits centered with no
   arrow — a quiet surface fill + hairline reads finished without competing
   with primary CTAs. Callout foots (text+button bands) keep their own
   left-set layout via the .sb-callout override below. Hover shares the
   primary-fill trio with the contact picker's checked state. */
.sb-section__foot { margin-top: var(--sb-sp-l); text-align: center; }
.sb-section__foot .sb-btn {
	background: var(--sb-sur);
	border-color: var(--sb-line);
	color: var(--sb-ink);
}
.sb-section__foot .sb-btn:hover,
.sb-pick .sb-btn:has(:checked) {
	background: var(--sb-pri);
	border-color: var(--sb-pri);
	color: var(--sb-pc);
}

/* Review-platform outbound tiles (reviews page foot; pattern reviews-all;
   centering now comes from .sb-section__foot) */
.sb-revlinks__title { font-size: var(--sb-fs-l); margin-bottom: 0.25rem; }
.sb-revlinks__lead { color: var(--sb-mut); font-size: var(--sb-fs-s); margin-bottom: var(--sb-sp-m); }
.sb-revlink {
	flex-direction: row;
	align-items: center;
	gap: 0.75rem;
	padding: var(--sb-sp-s) var(--sb-sp-m);
	min-height: 44px;
	text-decoration: none;
	color: inherit;
	text-align: left;
}
.sb-revlink__brand { display: inline-flex; flex: none; color: var(--sb-pri); }
.sb-revlink__brand .sb-icon { width: 1.75rem; height: 1.75rem; }
.sb-revlink__text { display: flex; flex-direction: column; min-width: 0; }
.sb-revlink__text strong { font-weight: var(--sb-semi); }
.sb-revlink__text small { color: var(--sb-mut); font-size: var(--sb-fs-s); }
.sb-revlink__ext { display: inline-flex; margin-left: auto; color: var(--sb-mut); transition: transform 150ms ease, color 150ms ease; }
.sb-revlink__ext .sb-icon { width: 1.125rem; height: 1.125rem; }
.sb-revlink:hover .sb-revlink__ext { transform: translate(2px, -2px); color: var(--sb-pri); }

.sb-card__more {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	color: var(--sb-pri);
	font-size: var(--sb-fs-s);
	font-weight: var(--sb-semi);
}
.sb-card__more .sb-icon { width: 1.125rem; height: 1.125rem; }
.sb-card:hover .sb-card__more .sb-icon:last-child { transform: translateX(3px); }
.sb-card__more .sb-icon:last-child { transition: transform 140ms ease; }
.sb-gallery__item { border-radius: var(--sb-rad-l); margin: 0; aspect-ratio: 4 / 3; }
/* ratio utilities AFTER .sb-gallery__item on purpose: both are single-class
   selectors, so source order decides — the case-study 21:9 band was silently
   rendering 4:3 when this block lived above (2026-08-14 audit).
   .sb-gallery__item is a 4:3 TILE: never use it without .sb-media + an
   .sb-ratio-* that matches the photo. The blog featured image (single.html)
   had it alone, so a 21:9 photo sat in a 4:3 box — ~0.32x its width of empty
   box under every post's cover photo (2026-10-01). */
.sb-ratio-4x3 { aspect-ratio: 4 / 3; }
.sb-ratio-16x9 { aspect-ratio: 16 / 9; }
.sb-ratio-21x9 { aspect-ratio: 21 / 9; }
.sb-gallery-grid { display: grid; gap: var(--sb-sp-m); }
@media (min-width: 600px) { .sb-gallery-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 900px) {
	/* editorial mosaic: lead photo anchors a 2×2 cell */
	.sb-gallery-grid { grid-template-columns: repeat(4, 1fr); }
	.sb-gallery__item--lead { grid-column: span 2; grid-row: span 2; aspect-ratio: 4 / 3; }
	/* thin services (pools-spas has 3 scenes): 2-up pyramid instead of a
	   4-column grid with a hole (2026-08-14 audit) */
	.sb-gallery-grid--compact { grid-template-columns: repeat(2, 1fr); }
}
.sb-gallery__item img { transition: transform 600ms ease; }
.sb-gallery__item:hover img { transform: scale(1.03); }

/* Lightbox (assets/js/lightbox.js). Deliberately thin, because it BORROWS the
   design system instead of restating it: the overlay carries .sb-section--dark
   (ink field + the blueprint-at-dusk layer + primary-contrast text — an AA pair
   already declared in scripts/standards-check.mjs) and every control is a real
   .sb-btn--ghost, so hover/focus/motion match every other button on the site.
   The tile link is the NO-JS path — it must fill the .sb-media box so the photo
   still fills its ratio slot. The overlay only exists in the DOM while open, so
   it needs no [hidden] handling. Arrows are centered with calc(), NOT a
   transform: .sb-btn:hover owns transform (the -2px lift) and would fight it. */
.sb-lightbox-link { display: block; height: 100%; }
.sb-lb { position: fixed; inset: 0; z-index: 90; display: grid; place-items: center; padding: var(--sb-sp-l); text-align: center; }
.sb-lb figure { margin: 0; }
.sb-lb img { display: block; max-width: min(94vw, 76rem); max-height: 78vh; border-radius: var(--sb-rad-l); }
.sb-lb figcaption { margin: 0.75rem 0 0; font-size: var(--sb-fs-s); }
.sb-lb__btn { position: absolute; width: var(--sb-tap); height: var(--sb-tap); min-height: 0; padding: 0; border-radius: var(--sb-pill); }
.sb-lb__close { top: var(--sb-sp-s); right: var(--sb-sp-s); }
.sb-lb__prev, .sb-lb__next { top: calc(50% - var(--sb-tap) / 2); }
.sb-lb__prev { left: var(--sb-sp-s); }
.sb-lb__next { right: var(--sb-sp-s); }

/* ---------------------------------------------------------------------------
 * Footer
 * ------------------------------------------------------------------------- */
.sb-footer {
	background-color: var(--sb-ink); /* NOT the shorthand — it wipes the blueprint texture layer */
	color: var(--sb-line);
}
.sb-footer__grid {
	display: grid;
	gap: var(--sb-sp-l);
	max-width: var(--wp--style--global--wide-size);
	margin-inline: auto;
	padding: var(--wp--preset--spacing--xl) var(--sb-sp-s) var(--sb-sp-l);
}
@media (min-width: 782px) {
	.sb-footer__grid { grid-template-columns: 1.4fr 1fr 1fr 1.2fr; }
}
.sb-footer__title {
	display: block;
	margin: 0 0 0.75rem;
	font-size: var(--sb-fs-s);
	font-weight: var(--sb-bold);
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--sb-pc);
}
.sb-footer ul { list-style: none; margin: 0; padding: 0; }
.sb-footer li { margin-bottom: 0.125rem; }
.sb-footer a {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	min-height: 2rem;
	color: var(--sb-line);
	text-decoration: none;
}
.sb-footer a:hover { color: var(--sb-acc); }
.sb-footer .sb-icon { width: 1.125rem; height: 1.125rem; color: var(--sb-acc); }
.sb-footer__nap { display: grid; gap: 0.375rem; font-style: normal; }
/* social row under the NAP — cream marks that light up on hover, ≥44px targets */
.sb-footer__social { display: flex; gap: 1rem; margin-top: 0.875rem; }
.sb-footer__social a { min-width: 2.75rem; min-height: 2.75rem; justify-content: center; }
.sb-footer__social .sb-icon { width: 1.5rem; height: 1.5rem; color: inherit; }
.sb-footer__logo img { display: block; width: auto; height: 3rem; margin-bottom: 0.875rem; }
.sb-footer__legal {
	border-top: 1px solid var(--sb-sec);
	font-size: var(--sb-fs-s);
	/* muted is a light-background token — on ink it fails AA; use border */
	color: var(--sb-line);
}
.sb-footer__legal-inner {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem 1.5rem;
	justify-content: space-between;
	max-width: var(--wp--style--global--wide-size);
	margin-inline: auto;
	padding: var(--sb-sp-s);
}

/* ---------------------------------------------------------------------------
 * Utilities
 * ------------------------------------------------------------------------- */
.sb-grid { display: grid; gap: var(--sb-sp-m); }
@media (min-width: 600px) { .sb-grid--2 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 900px) {
	.sb-grid--3 { grid-template-columns: repeat(3, 1fr); }
	.sb-grid--4 { grid-template-columns: repeat(4, 1fr); }
}
.sb-container {
	max-width: var(--wp--style--global--wide-size);
	margin-inline: auto;
	padding-inline: var(--sb-sp-s);
}
.sb-container--narrow { max-width: var(--wp--style--global--content-size); }

/* Accessibility: skip link + screen-reader text */
.skip-link.screen-reader-text {
	position: absolute;
	left: -9999px;
	top: 0;
	z-index: 100000;
}
.skip-link.screen-reader-text:focus {
	left: var(--wp--preset--spacing--sm, 1rem);
	top: var(--wp--preset--spacing--sm, 1rem);
	width: auto;
	height: auto;
	padding: var(--wp--preset--spacing--xxs, 0.5rem) var(--wp--preset--spacing--sm, 1rem);
	background: var(--sb-bg);
	color: var(--sb-ink);
	box-shadow: var(--sb-sh-m);
}
.screen-reader-text {
	border: 0;
	clip: rect(1px, 1px, 1px, 1px);
	clip-path: inset(50%);
	height: 1px;
	width: 1px;
	margin: -1px;
	overflow: hidden;
	padding: 0;
	position: absolute;
}

/*
 * ⚠ TODO fact markers. When a required business fact is unknown, the data-layer
 * accessor renders one of these instead of inventing a value — impossible to miss.
 */
.sunny-todo {
	display: inline-block;
	padding: 0 0.35em;
	border: 1px dashed var(--sb-not);
	border-radius: var(--sb-rad-s);
	color: var(--sb-not);
	font-weight: var(--wp--custom--font-weight--semibold, 600);
	font-size: 0.9em;
	white-space: nowrap;
}
.sb-section--dark .sunny-todo,
.sb-footer .sunny-todo { background: var(--sb-bg); }

/*
 * Pending placeholder values (sunny_pending_marker): read as real copy for
 * design review, but stay visibly asterisked until the fact is confirmed.
 */
.sunny-pending { cursor: help; } /* renders clean; disclosure = footer fine print */
.sb-fineprint { display: block; flex-basis: 100%; opacity: 0.75; }

/* ---------------------------------------------------------------------------
 * Breadcrumbs + T3 "what's included" grid
 * ------------------------------------------------------------------------- */
.sb-breadcrumbs { padding-block: 0.625rem; background: var(--sb-sur); border-bottom: 1px solid var(--sb-line); }
.sb-breadcrumbs ol { display: flex; flex-wrap: wrap; gap: 0.25rem; list-style: none; margin: 0; padding: 0; font-size: var(--sb-fs-s); }
.sb-breadcrumbs li { display: flex; align-items: center; gap: 0.25rem; color: var(--sb-mut); }
.sb-breadcrumbs li + li::before { content: "/"; opacity: 0.45; }
.sb-breadcrumbs a { color: var(--sb-mut); text-decoration: none; }
.sb-breadcrumbs a:hover { color: var(--sb-pri); text-decoration: underline; }
.sb-breadcrumbs [aria-current] { color: var(--sb-ink); font-weight: var(--sb-semi); }

/* count-aware flex grid: any partial last row centers (5 items -> 3 + 2
   pyramid via the --cols3 variant; 7 -> 4 + 3 centered; 4/8 stay square) */
/* .sb-grid--center rides these rules: same centered pyramid for data-driven tile counts */
.sb-included, .sb-grid--center { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sb-sp-m); }
/* border-box: the % bases below must include a bordered card's 1px edge, or 3-up wraps to 2+1 */
.sb-included__item, .sb-grid--center > * { flex: 0 1 100%; box-sizing: border-box; }
@media (min-width: 600px) {
	.sb-included__item, .sb-grid--center > * { flex-basis: calc(50% - var(--sb-sp-m) / 2); }
}
@media (min-width: 900px) {
	.sb-included--cols4 .sb-included__item { flex-basis: calc(25% - var(--sb-sp-m) * 3 / 4); }
	.sb-included--cols3 .sb-included__item, .sb-grid--3.sb-grid--center > * { flex-basis: calc(33.333% - var(--sb-sp-m) * 2 / 3); }
	/* a lone showcase render reads as a feature, not a stranded thumb (2026-08-14) */
	.sb-grid--3.sb-grid--center > *:only-child { flex-basis: 60%; }
}
.sb-included__item { display: grid; gap: 0.375rem; align-content: start; }
.sb-included__icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.75rem;
	height: 2.75rem;
	border-radius: var(--sb-rad-m);
	background: color-mix(in srgb, var(--sb-acc) 16%, transparent);
	color: var(--sb-pri);
	margin-bottom: 0.375rem;
}
.sb-included__item h3 { font-size: var(--wp--preset--font-size--md); margin: 0; }
.sb-included__item p { margin: 0; color: var(--sb-mut); font-size: var(--sb-fs-s); }

/* ---------------------------------------------------------------------------
 * T3 planning guide — zigzag rows: prose beside a REAL project photo, photo
 * side alternating per row (patterns/service-guide.php)
 * ------------------------------------------------------------------------- */
.sb-guide { display: grid; gap: var(--sb-sp-l); }
.sb-guide__row { display: grid; gap: var(--sb-sp-s); align-items: center; }
.sb-guide__media { border-radius: var(--sb-rad-m); overflow: hidden; box-shadow: var(--sb-sh-m); }
/* photo-less services: a dark "plan sheet" — the blueprint-at-dusk panel
   (sb-section--dark supplies the texture) carrying a lime drafting numeral,
   sheet kicker with a badge-size trade icon, a 0N/04 counter and drafting
   corner ticks. Sleek, high-contrast, zero photography required. */
.sb-guide__sheet {
	position: relative;
	margin: 0;
	box-shadow: var(--sb-sh-m);
	/* the sheet's soft lime glow — shared by numeral, keyword and pulse */
	--sb-g: color-mix(in srgb, var(--sb-acc) 45%, transparent);
}
.sb-guide__sheet::before,
.sb-guide__sheet::after {
	content: '';
	position: absolute;
	width: 1.25rem;
	height: 1.25rem;
	border: 0 solid var(--sb-sec);
}
.sb-guide__sheet::before { top: 0.875rem; left: 0.875rem; border-top-width: 2px; border-left-width: 2px; }
.sb-guide__sheet::after { bottom: 0.875rem; right: 0.875rem; border-bottom-width: 2px; border-right-width: 2px; }
.sb-guide__sheet-kicker {
	position: absolute;
	top: 1.5rem;
	left: 1.75rem;
	display: flex;
	align-items: center;
	gap: 0.625rem;
	font-size: var(--sb-fs-s);
	font-weight: var(--sb-semi);
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--sb-line);
}
.sb-guide__sheet-kicker .sb-icon { width: 1.375rem; height: 1.375rem; color: var(--sb-acc); }
.sb-guide__sheet-count {
	position: absolute;
	top: 1.5rem;
	right: 1.75rem;
	font-family: var(--wp--preset--font-family--heading);
	font-size: var(--sb-fs-l);
	font-weight: var(--sb-semi);
	color: var(--sb-acc);
}
.sb-guide__sheet-num,
.sb-guide__sheet-word {
	position: absolute;
	font-family: var(--wp--preset--font-family--heading);
	font-weight: var(--sb-bold);
	color: var(--sb-acc);
}
.sb-guide__sheet-num {
	left: 1.625rem;
	bottom: 0.625rem;
	font-size: clamp(6rem, 9vw, 8.5rem);
	line-height: 0.9;
	text-shadow: 0 0 3.5rem var(--sb-g);
}
/* keyword mode (guide section carries k): the word IS the sheet's centerpiece —
   poster type with a breathing glow (pulse keyframes live in the shared
   no-preference block below); the numeral steps back to an outlined watermark.
   Even rows park the numeral in the opposite corner and desync the pulse so no
   two sheets on a page read identical. */
.sb-guide__sheet--word .sb-guide__sheet-num {
	color: transparent;
	-webkit-text-stroke: 1.5px var(--sb-g);
	text-shadow: none;
}
.sb-guide__row:nth-child(even) .sb-guide__sheet-num { left: auto; right: 1.375rem; }
.sb-guide__sheet-word {
	inset: 0;
	display: grid;
	place-items: center;
	padding: 2rem;
	text-align: center;
	font-size: clamp(2.5rem, 4.5vw, 3.5rem);
	line-height: 0.95;
	text-transform: uppercase;
	text-shadow: 0 0 1.5rem var(--sb-g);
}
@media (min-width: 900px) {
	.sb-guide { gap: var(--wp--preset--spacing--xl); }
	.sb-guide__row { grid-template-columns: 0.9fr 1.1fr; gap: var(--wp--preset--spacing--xl); }
	/* zigzag: even rows put the photo on the right */
	.sb-guide__row:nth-child(even) .sb-guide__media { order: 2; }
}
.sb-guide__item { max-width: 62ch; }
.sb-guide__item h3 {
	margin: 0 0 0.625rem;
	font-size: var(--sb-fs-l);
}
.sb-guide__item p { margin: 0 0 0.875rem; color: var(--sb-mut); }
.sb-guide__item p:last-child { margin-bottom: 0; }
/* scannable bolds: the glance-layer the muted prose hangs off */
.sb-guide__item strong { color: var(--sb-ink); }
.sb-guide__cta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--sb-sp-s);
	margin-top: var(--sb-sp-l);
	padding-top: var(--sb-sp-m);
	border-top: 1px solid var(--sb-line);
}
.sb-guide__cta p { margin: 0; max-width: 42rem; }

/* geo local-knowledge prose (patterns/geo-local.php; reused for financing/referral notes) */
.sb-geo-local p { margin: 0 0 1rem; }
.sb-geo-local p:last-child { margin-bottom: 0; }
.sb-geo-local + .sb-faq { margin-top: var(--sb-sp-m); }

/* calculator methodology + cost-factor list (patterns/tool-calculator.php) */
.sb-calc__method { max-width: 60ch; margin: 0 0 1.5rem; }
.sb-calc__factors-title { font-size: var(--sb-fs-l); margin: 0 0 0.75rem; }
.sb-calc__factors { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.75rem; }
.sb-calc__factors li { display: grid; gap: 0.125rem; }
.sb-calc__factors li strong { color: var(--sb-ink); }
.sb-calc__factors li span { color: var(--sb-mut); font-size: var(--sb-fs-s); }
@media (min-width: 700px) { .sb-calc__factors { grid-template-columns: 1fr 1fr; column-gap: var(--sb-sp-l); } }

/* ---------------------------------------------------------------------------
 * Video testimonials — 16:9 cards, native player on tap
 * ------------------------------------------------------------------------- */
.sb-section__head--center { text-align: center; }
.sb-section__head--center .sb-section__intro { margin-inline: auto; }
.sb-eyebrow--pill {
	display: inline-block;
	padding: 0.3rem 0.875rem;
	border-radius: var(--sb-pill);
	background: color-mix(in srgb, var(--sb-acc) 14%, transparent);
	color: var(--sb-acc);
}
.sb-videos__grid { display: grid; gap: var(--sb-sp-m); }
@media (min-width: 600px) { .sb-videos__grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 900px) { .sb-videos__grid { grid-template-columns: repeat(3, 1fr); } }
.sb-video-card {
	position: relative;
	display: block;
	width: 100%;
	aspect-ratio: 16 / 9;
	padding: 0;
	border: 0;
	border-radius: var(--sb-rad-l);
	overflow: hidden;
	background: var(--sb-sec);
	cursor: pointer;
	box-shadow: var(--sb-sh-m);
	transition: transform 200ms ease, box-shadow 200ms ease;
}
.sb-video-card--soon { cursor: default; }
button.sb-video-card:hover { transform: translateY(-3px); box-shadow: var(--sb-sh-l); }
.sb-video-card img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.sb-video-card::after {
	content: "";
	position: absolute;
	inset: 0;
	background: linear-gradient(to top, color-mix(in srgb, var(--sb-ink) 82%, transparent), transparent 55%);
}
.sb-video-card__play {
	position: absolute;
	inset: 0;
	margin: auto;
	z-index: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 3.75rem;
	height: 3.75rem;
	border-radius: var(--sb-pill);
	background: var(--sb-acc);
	color: var(--sb-ink);
	box-shadow: var(--sb-sh-l);
	transition: transform 200ms ease;
}
button.sb-video-card:hover .sb-video-card__play { transform: scale(1.08); }
.sb-video-card__play .sb-icon { width: 1.5rem; height: 1.5rem; margin-left: 2px; }
.sb-video-card__caption {
	position: absolute;
	left: 1rem;
	right: 1rem;
	bottom: 0.875rem;
	z-index: 1;
	color: var(--sb-pc);
	font-weight: var(--sb-semi);
	text-align: left;
}
.sb-video-card__soon {
	position: absolute;
	inset: 0;
	margin: auto;
	z-index: 1;
	width: fit-content;
	height: fit-content;
	padding: 0.375rem 0.875rem;
	border-radius: var(--sb-pill);
	background: color-mix(in srgb, var(--sb-ink) 70%, transparent);
	color: var(--sb-pc);
	font-size: var(--sb-fs-s);
	font-weight: var(--sb-semi);
}
.sb-video-card.is-playing video,
.sb-video-card.is-playing iframe { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; border: 0; }
/* the poster gradient sits above the card — it would swallow player clicks */
.sb-video-card.is-playing::after { display: none; }

/* ---------------------------------------------------------------------------
 * Build scene — the line-art house assembling itself on the blueprint grid
 * ------------------------------------------------------------------------- */
.sb-build {
	position: absolute;
	pointer-events: none;
	/* stroke palette shorthands — the base line is sleek warm white (owner
	   2026-07-13: secondary-gray lines read too faint on the ink panels);
	   colored strokes mix toward it */
	--sb-bs: var(--sb-pc);
	--sb-bp: var(--sb-pc);
}
.sb-build svg { display: block; width: 100%; height: auto; }
.sb-build path, .sb-build circle { stroke: var(--sb-bs); }
.sb-build .sb-build__roof,
.sb-build .sb-build__found { stroke: color-mix(in srgb, var(--sb-acc) 70%, var(--sb-bs)); }
.sb-build .sb-build__turf,
.sb-build .sb-build__trees { stroke: color-mix(in srgb, var(--sb-acc) 72%, var(--sb-bs)); }
.sb-build .sb-build__pool,
.sb-build .sb-build__bbq { stroke: color-mix(in srgb, var(--sb-bp) 30%, var(--sb-bs)); }
.sb-build .sb-build__water { stroke: color-mix(in srgb, var(--sb-bp) 48%, var(--sb-bs)); }
.sb-build .sb-build__sun circle,
.sb-build .sb-build__sun path { stroke: color-mix(in srgb, var(--sb-acc) 85%, var(--sb-bs)); }
/* home hero: one centered stack — chips / balanced 2-line title / the
   sketch->photo reveal / sub / CTAs (owner 2026-07-13: the reveal follows the
   title at EVERY width so mobile sees it above the fold; the CTAs must still
   land in the first 390px viewport — verify in the screenshot, never assume). */
/* the stacked hero trims the section's poster-scale top padding — every
   invisible pixel here pushes the CTAs toward the fold on laptops */
.sb-hero:has(.sb-hero__stack) { padding-block: var(--wp--preset--spacing--xxxs) 0; }
.sb-hero__stack {
	display: flex;
	flex-direction: column;
	align-items: center; /* children shrink-to-fit and self-center */
	text-align: center;
	padding-block: var(--wp--preset--spacing--xxs) var(--sb-sp-l);
}
/* the stacked hero is center-set at every width — the chip row joins it
   (Mor 2026-08-18: chips sat left-aligned on mobile) */
.sb-hero__stack .sb-hero__chips { justify-content: center; margin-bottom: var(--wp--preset--spacing--xxs); }
/* xxxl at EVERY width (owner: desktop 2026-07-13, mobile 2026-07-23): the
   `display` preset wrapped the title to 5 lines at 390px and shoved the CTAs
   under the fold. 4 balanced lines + photo + CTAs now share one screen. */
.sb-hero__stack h1 { margin-top: 0; font-size: var(--wp--preset--font-size--xxxl); max-width: 30ch; text-wrap: balance; }
.sb-hero__stack .sb-hero__sub { max-width: 52rem; }
.sb-hero__stack .sb-hero__cta { margin-block: var(--wp--preset--spacing--xxs) 0.75rem; }
@media (max-width: 899px) {
	/* mid-stack reveal — the xxxl title buys it width while the CTAs still
	   make the fold on a REAL phone viewport (~390x660 with Safari chrome —
	   Mor 2026-08-19; verify in the screenshot, never assume). The caption
	   row under the photo exists only in the FULL motion tier, so only
	   html.sb-full pays the 2.5rem reserve for it; the subtle default keeps
	   the photo snug against the sub. The dek tightens to snug line-height
	   here — those two trims are what put the CTA back above the fold. */
	.sb-hero__stack .sb-reveal { width: min(78vw, 430px); margin-block: 0 1rem; }
	html.sb-full .sb-hero__stack .sb-reveal { margin-bottom: 2.5rem; }
	.sb-hero__stack .sb-reveal__note { bottom: -2.25rem; }
	.sb-hero__stack .sb-hero__sub { margin-block: 0.25rem 0; line-height: var(--wp--custom--line-height--snug); }
}
@media (min-width: 900px) {
	/* breathing room ABOVE the pills; they hug the title below (owner) */
	.sb-hero__stack .sb-hero__chips { margin-block: var(--sb-sp-m) var(--wp--preset--spacing--xxs); }
	/* D129 fold composition (Mor 2026-09-08): the review badge — centered —
	   closes the above-fold poster (claim → proof → dek → stars) and the CTA
	   pair waits just BELOW the fold with generous whitespace, first thing on
	   scroll; the header already carries the always-visible desktop CTAs. The
	   photo stays the flexible element (100vh-aware width below, chrome
	   constant retuned to the shorter above-fold set). Flex `order` swaps
	   badge/CTAs visually only: DOM = the verified mobile flow, untouched.
	   The CTAs animate in below the fold, so no stagger retime is needed. */
	.sb-hero__stack .sb-reveal { margin-block: var(--wp--preset--spacing--xs) 0; }
	/* dek: the same snug trim the 390px fold fix uses */
	.sb-hero__stack .sb-hero__sub { line-height: var(--wp--custom--line-height--snug); margin-block: 0.375rem 0; }
	.sb-hero__stack .sb-revbadge { margin-block: var(--wp--preset--spacing--xxs) 0; }
	/* 4.75rem: the standard inter-section rhythm (trust bar's is 3rem pad /
	   ~6rem text-to-text) — and just enough that the CTAs stay under a
	   ~995px fold, where the quote skin's badge ends at ~922px (rendering
	   varies a few px between loads; 4.5rem left a 1px button sliver) */
	.sb-hero__stack .sb-hero__cta { order: 1; margin-block: 4.75rem 0.875rem; }
	.sb-hero__stack .sb-hero__note { order: 2; }
}

/* the hero visual slot: the scene, or (home) the sketch->photo reveal.
   Slot rules bind to DIRECT grid children so the wrapped home scene needs no
   resets; T3 service heroes keep their gable-masked photo on desktop. */
.sb-reveal,
.sb-build--error,
.sb-hero__grid > .sb-build--hero {
	position: relative;
	justify-self: center;
	width: min(88vw, 430px);
	margin-block: 0 -0.75rem;
}
/* The reveal box owns the scene's own ratio (the 430x260 viewBox; the 1.654 in
   the desktop width calc below is the same number) and the sketch floats inside
   it. The box height must NEVER depend on the sketch being in flow: the skip
   paths below display:none it, which collapsed the box to 0 and took the
   absolutely-positioned photo with it (hero rendered empty). Also pre-reserves
   the space, so the reveal costs no CLS. */
.sb-reveal { aspect-ratio: 430 / 260; }
.sb-reveal .sb-build--hero { position: absolute; inset: 0; }
@media (max-width: 899px) {
	/* phones show the FEATURED PHOTO, not the line-art filler (Mor 2026-08-19:
	   the sketch under the CTAs read as an image that failed to load). Home-
	   hero treatment: centered, rounded, landscape crop; the gable clip stays
	   a desktop move. Source order puts it right after the copy + CTAs, so the
	   verified first-viewport fold is untouched. */
	.sb-hero__grid > .sb-build--hero { display: none; }
	/* neutralize the gable motif's PAINT as well as its geometry (Mor
	   2026-08-19: the ridge variant's accent background + 5px padding leaked
	   around the rounded photo as a lime frame) */
	.sb-hero__media { margin-inline: 0; margin-block-end: var(--sb-sp-m); justify-self: center; width: min(88vw, 430px); clip-path: none; background: none; padding-top: 0; }
	.sb-hero__media:empty { display: none; }
	.sb-hero__media img { aspect-ratio: 430 / 260; object-fit: cover; border-radius: var(--sb-rad-l); }
}
@media (min-width: 900px) {
	.sb-hero__grid > .sb-build--hero { display: none; }
	.sb-build--hero.sb-build--geo { display: block; width: 400px; }
	.sb-reveal,
	.sb-build--hero.sb-build--geo { justify-self: end; align-self: center; margin: 0; }
	/* the flexible element of the D128 fold composition: full 52rem text
	   measure (edges aligned with the dek — Mor liked this) wherever the
	   window is tall enough (~995px+), and on shorter windows the BOX shrinks
	   proportionally so title→photo→dek→CTAs→review badge all stay above the
	   fold. Ratio never changes, so the sketch overlay + both motion tiers
	   are untouched; 24rem floor so the photo never reads small. */
	.sb-reveal { width: min(66vw, 52rem, max(24rem, calc((100vh - var(--sb-hero-chrome)) * 1.654))); }
}

/* two-act reveal: sketch draws, a chip teases at the built moment, then a
   DREAM-PACED handover (owner 2026-07-13): the photo breathes in over ~2.6s
   while it settles from a gentle drift and the sketch bows out just as slowly */
.sb-reveal__photo {
	position: absolute;
	inset: 0;
	margin: 0;
	opacity: 0;
	border-radius: var(--sb-rad-m);
	overflow: hidden;
	transition: opacity 2600ms cubic-bezier(0.4, 0, 0.2, 1);
}
.sb-reveal__photo img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	transform: scale(1.05);
	transition: transform 3400ms cubic-bezier(0.16, 1, 0.3, 1);
}
.sb-reveal__note {
	position: absolute;
	left: 50%;
	bottom: 0.75rem;
	transform: translateX(-50%);
	margin: 0;
	background: var(--sb-sec);
	white-space: nowrap;
	opacity: 0;
	transition: opacity 800ms ease;
}
.sb-reveal.is-ready .sb-reveal__note { opacity: 1; }
.sb-reveal.is-built .sb-reveal__photo { opacity: 1; }
.sb-reveal.is-built .sb-reveal__photo img { transform: scale(1); }
.sb-reveal.is-built .sb-build--hero { opacity: 0; transition: opacity 2400ms ease; }
.sb-reveal.is-built .sb-reveal__note { opacity: 0; transition: opacity 1400ms ease; }
/* once-per-session skip (motifs.js adds is-instant): the photo lands with ZERO
   theatrics — no crossfade, no drift, and the sketch never renders (its fresh
   draw was visible on top of the photo through the slow fade). Safe only
   because .sb-reveal carries its own aspect-ratio — see above. */
.sb-reveal.is-instant .sb-reveal__photo,
.sb-reveal.is-instant .sb-reveal__photo img { transition: none; }
.sb-reveal.is-instant .sb-build--hero { display: none; }
/* 404: no photo competes, so the scene IS the hero media at every width */
.sb-build--error { align-self: center; }
.sb-error404 .sb-hero__grid { min-height: min(36rem, 70vh); align-items: center; }
.sb-build--cta { right: 1%; bottom: 0.5rem; width: 350px; opacity: 0.55; display: none; }
@media (min-width: 1200px) { .sb-build--cta { display: block; } }
.sb-cta__grid { position: relative; z-index: 1; }
#estimate.sb-section--dark { position: relative; }

@media (prefers-reduced-motion: no-preference) {
	/* 8s build sequence (owner 2026-07-13: 16s lost the audience before the
	   payoff): ground -> foundation -> walls -> roof -> chimney -> door/windows
	   pop -> HOLD. Draws ONCE and stays built; only the sun keeps breathing.
	   The home reveal crossfades the photo over the held sketch.
	   MOTION TIERS (2026-08-18, Mor: the theatrics risked reading low-end to
	   part of the client base): the draw runs only under html.sb-full — nav.js
	   sets it when the motion tier is 'full' (?sb_motion=full previews it).
	   Default tier 'subtle' renders every sketch fully drawn, instantly. */
	html.sb-full .sb-build svg { animation: sb-build-cycle 8s linear forwards; }
	html.sb-full .sb-build path[pathLength] { stroke-dasharray: 1; }
	html.sb-full .sb-build .sb-build__ground  { animation: sb-b-draw-a 8s linear forwards; }
	html.sb-full .sb-build .sb-build__found   { animation: sb-b-draw-b 8s linear forwards; }
	html.sb-full .sb-build .sb-build__walls   { animation: sb-b-draw-c 8s linear forwards; }
	html.sb-full .sb-build .sb-build__roof    { animation: sb-b-draw-d 8s linear forwards; }
	html.sb-full .sb-build .sb-build__chimney { animation: sb-b-draw-e 8s linear forwards; }
	html.sb-full .sb-build .sb-build__fill    { animation: sb-b-pop 8s linear forwards; }
	html.sb-full .sb-build .sb-build__turf    { animation: sb-b-draw-f 8s linear forwards; }
	html.sb-full .sb-build .sb-build__pool    { animation: sb-b-draw-g 8s linear forwards; }
	html.sb-full .sb-build .sb-build__water   { animation: sb-b-draw-h 8s linear forwards; }
	html.sb-full .sb-build .sb-build__bbq     { animation: sb-b-draw-h 8s linear forwards; }
	html.sb-full .sb-build .sb-build__trees   { animation: sb-b-draw-i 8s linear forwards; }
	html.sb-full .sb-build .sb-build__sun     { animation: sb-b-sun 6s ease-in-out infinite; transform-origin: 46px 56px; }
	@keyframes sb-build-cycle { 0% { opacity: 0; } 2%, 100% { opacity: 1; } }
	@keyframes sb-b-draw-a { 0% { stroke-dashoffset: 1; } 8%, 100% { stroke-dashoffset: 0; } }
	@keyframes sb-b-draw-b { 0%, 6% { stroke-dashoffset: 1; } 14%, 100% { stroke-dashoffset: 0; } }
	@keyframes sb-b-draw-c { 0%, 10% { stroke-dashoffset: 1; } 22%, 100% { stroke-dashoffset: 0; } }
	@keyframes sb-b-draw-d { 0%, 21% { stroke-dashoffset: 1; } 33%, 100% { stroke-dashoffset: 0; } }
	@keyframes sb-b-draw-e { 0%, 33% { stroke-dashoffset: 1; } 38%, 100% { stroke-dashoffset: 0; } }
	@keyframes sb-b-pop { 0%, 38% { opacity: 0; } 45%, 100% { opacity: 1; } }
	@keyframes sb-b-draw-f { 0%, 45% { stroke-dashoffset: 1; } 56%, 100% { stroke-dashoffset: 0; } }
	@keyframes sb-b-draw-g { 0%, 54% { stroke-dashoffset: 1; } 65%, 100% { stroke-dashoffset: 0; } }
	@keyframes sb-b-draw-h { 0%, 63% { stroke-dashoffset: 1; } 73%, 100% { stroke-dashoffset: 0; } }
	@keyframes sb-b-draw-i { 0%, 71% { stroke-dashoffset: 1; } 84%, 100% { stroke-dashoffset: 0; } }
	@keyframes sb-b-sun { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.06); } }

	/* subtle "keep watching" cue on the home reveal: a hairline accent
	   progress rule under the sketch, timed to the draw, gone at the photo */
	html.sb-full .sb-reveal::before {
		content: '';
		position: absolute;
		left: 18%;
		right: 18%;
		bottom: -0.5rem;
		height: 2px;
		background: var(--sb-acc);
		transform: scaleX(0);
		transform-origin: left;
		opacity: 0.7;
		animation: sb-reveal-progress 8s linear forwards;
		transition: opacity 1400ms ease;
	}
	html.sb-full .sb-reveal.is-built::before { opacity: 0; }
	html.sb-full .sb-reveal.is-instant::before { content: none; }
	@keyframes sb-reveal-progress { to { transform: scaleX(1); } }
}

/* ---------------------------------------------------------------------------
 * Inner-page components: page hero, location grid, about, contact, prose, blog
 * ------------------------------------------------------------------------- */
/* inner-page heroes: centered ink band in the homepage hero's language */
.sb-pagehero { padding-block: var(--sb-sp-l); text-align: center; }
.sb-pagehero .sb-hero__chips { justify-content: center; }
.sb-pagehero h1 { margin: 0 auto 0.625rem; font-size: var(--wp--preset--font-size--xxxl); max-width: 30ch; text-wrap: balance; }
/* the blog masthead runs at the same poster scale as every other page hero
   (owner 2026-07-27: the shrunk editorial variant read as an afterthought);
   only the measure is wider, because guide titles are long questions */
.sb-pagehero--post h1 { max-width: 32ch; }
/* byline row under the dek: author, date, read time, review badge. Dim text on
   ink is the border token — muted fails AA there (§9). */
.sb-postmeta {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: center;
	gap: 0.375rem 1.25rem;
	margin: 1.125rem 0 0;
	color: var(--sb-line);
	font-size: var(--sb-fs-s);
}
/* lead color follows the band: dim-on-dark uses the border token (muted fails
   on ink — §9); the light tool hero needs muted (border was invisible there) */
.sb-pagehero__lead { max-width: 46rem; color: var(--sb-mut); font-size: var(--sb-fs-l); margin: 0 auto; }
.sb-section--dark .sb-pagehero__lead { color: var(--sb-line); }
.sb-pagehero__badge { margin-top: 1rem; }

/* one column on phones: long city names ("Rancho Penasquitos") wrap to two
   lines in a 390px two-up grid and the pills read broken — owner 2026-07-12 */
.sb-loc-grid { display: grid; gap: 0.625rem; list-style: none; margin: 0; padding: 0; }
@media (min-width: 560px) { .sb-loc-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 700px) { .sb-loc-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1024px) { .sb-loc-grid { grid-template-columns: repeat(4, 1fr); } }
.sb-loc-grid a span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sb-loc-grid a {
	display: flex;
	align-items: center;
	gap: 0.625rem;
	min-height: var(--sb-tap);
	padding: 0.75rem 1rem;
	border: 1px solid var(--sb-line);
	border-radius: var(--sb-rad-m);
	background: var(--sb-bg);
	color: var(--sb-ink);
	font-weight: var(--sb-semi);
	text-decoration: none;
	transition: border-color 140ms ease, transform 140ms ease, box-shadow 140ms ease;
}
.sb-loc-grid a:hover { border-color: var(--sb-acc); transform: translateY(-2px); box-shadow: var(--wp--custom--shadow--sm); }
.sb-loc-grid .sb-icon:first-child { color: var(--sb-pri); }
.sb-loc-grid a .sb-icon:last-child { margin-left: auto; opacity: 0.5; }

.sb-about__values > div h3 { margin: 0 0 0.5rem; font-size: max(var(--sb-fs-l), 1.25rem); } /* fluid lg dips under body size at 390 */
.sb-about__values > div p { margin: 0; color: var(--sb-mut); }
.sb-contact__reassure { max-width: 52rem; margin-inline: auto; } /* centered box, not just centered text */
.sb-referral__note { margin-top: var(--sb-sp-l); max-width: 60ch; }
/* .sb-about = the blog author box only (sunny/post-author); About's "Who we are" went full-width as a .sb-about__values grid (Mor 2026-09-29). Bolds read in ink so the story skims. */
.sb-story strong { color: var(--sb-ink); }
.sb-story .sb-grid { grid-auto-rows: 1fr; } /* every story card as tall as the tallest */
.sb-story .sb-card__badge { margin: 0 0 0.5rem; } /* the service-card badge, minus the photo overlap */
/* author box = ONE centered column, no photo (owner 2026-10-01): the title
   section spans the container; every paragraph (copy, trust row, CTA) shrinks
   to its content under a reading measure and centers on auto margins —
   fit-content is what centers the flex trust row and the lone button without
   a rule each. Top margins only, so the last child leaves no extra band
   above the section's own bottom padding. */
.sb-about { text-align: center; }
.sb-about p { width: fit-content; max-width: 65ch; margin: var(--sb-sp-s) auto 0; }
.sb-about .sb-level { margin-inline: auto; }

.sb-contact { display: grid; gap: var(--sb-sp-l); }
@media (min-width: 900px) { .sb-contact { grid-template-columns: 0.9fr 1.1fr; align-items: start; } }
.sb-contact__nap { font-size: var(--sb-fs-l); gap: 0.75rem; color: var(--sb-ink); }
.sb-contact__nap a { color: var(--sb-ink); }
.sb-contact__nap a:hover { color: var(--sb-pri); }
.sb-contact__nap .sb-icon { color: var(--sb-pri); }
.sb-contact__note { color: var(--sb-mut); max-width: 26rem; }
.sb-contact .sb-cta__form { border-top-color: var(--sb-acc); }

.sb-casenote {
	display: flex;
	gap: 0.625rem;
	align-items: flex-start;
	padding: 1rem 1.25rem;
	border: 1px dashed var(--sb-line);
	border-radius: var(--sb-rad-m);
	background: var(--sb-sur);
	color: var(--sb-mut);
}
.sb-casenote .sb-icon { color: var(--sb-pri); flex: none; margin-top: 0.15rem; }

.sb-gallery-grid--wall { grid-template-columns: 1fr 1fr; }
@media (min-width: 900px) { .sb-gallery-grid--wall { grid-template-columns: repeat(3, 1fr); } }
.sb-gallery-grid--wall .sb-gallery__item { aspect-ratio: 4 / 3; }

/* blog cards + article prose */
/* the global 0 blockGap flattens WP's grid — the card grid owns its own gap */
.sb-postgrid { gap: var(--sb-sp-m); }
.sb-postcard { height: 100%; }
.sb-postcard .wp-block-post-featured-image img { border-radius: 0; }
.sb-postcard h3 a { color: inherit; text-decoration: none; }
.sb-postcard h3 a:hover { color: var(--sb-pri); }
.sb-prose-wrap .wp-block-post-featured-image { margin-bottom: var(--sb-sp-m); }
.sb-prose > * { max-width: var(--wp--style--global--content-size); margin-inline: auto; }
.sb-prose p, .sb-prose ul, .sb-prose ol { line-height: var(--wp--custom--line-height--relaxed); margin-block: 0 1.1rem; }
.sb-prose h2 { font-size: var(--wp--preset--font-size--xxl); margin-block: 2.25rem 0.75rem; }
.sb-prose h3 { margin-block: 1.75rem 0.5rem; }
.sb-prose img { border-radius: var(--sb-rad-l); }
.sb-prose blockquote {
	border-left: 3px solid var(--sb-acc);
	padding-left: 1.25rem;
	color: var(--sb-mut);
	margin-block: 1.5rem;
}

/* ---------------------------------------------------------------------------
 * Cost calculators (tools network) + lead thank-you state
 * ------------------------------------------------------------------------- */
.sb-calc { display: grid; gap: var(--sb-sp-m); }
@media (min-width: 900px) { .sb-calc { grid-template-columns: 1.05fr 0.95fr; align-items: start; } }
.sb-calc__panel, .sb-calc__result {
	background: var(--sb-bg);
	border: 1px solid var(--sb-line);
	border-radius: var(--sb-rad-l);
	box-shadow: var(--wp--custom--shadow--sm);
	padding: var(--sb-sp-m);
}
.sb-calc__panel { display: grid; gap: 0.875rem; }
.sb-calc__panel h2 { font-size: var(--wp--preset--font-size--xl); }
.sb-calc__result { position: sticky; top: calc(var(--sb-header-h-cond) + 1rem); }
.sb-calc__total {
	font-family: var(--wp--preset--font-family--heading);
	font-size: var(--wp--preset--font-size--xxxl);
	font-weight: var(--sb-bold);
	line-height: 1.05;
	margin: 0.25rem 0 0.5rem;
	color: var(--sb-pri);
}
.sb-calc__note { color: var(--sb-mut); font-size: var(--sb-fs-s); }
.sb-calc__result .sb-fineprint { color: var(--sb-not); opacity: 1; margin-bottom: 0.75rem; }
.sb-calc__lead { border-top: 1px solid var(--sb-line); padding-top: 1rem; margin-top: 0.75rem; }
/* phones: the result card stacks BELOW the inputs (behind the call bar), so a
   docked copy of the figure rides the panel's bottom edge while any input is
   in view, parked just above the call bar; gone where the card sits alongside */
.sb-calc__live {
	position: sticky;
	bottom: calc(var(--sb-callbar-h) + 0.5rem);
	margin: 0;
	padding: 0.625rem 1rem;
	border-radius: var(--sb-rad-m);
	background: var(--sb-ink);
	color: var(--sb-line);
	box-shadow: var(--sb-sh-m);
}
.sb-calc__live strong { color: var(--sb-acc); font-size: var(--sb-fs-l); }
/* focus-scrolling parks a field ABOVE the docked total + call bar, not under them */
.sb-calc__panel .sb-input { scroll-margin-bottom: calc(var(--sb-callbar-h) + 4.5rem); }
@media (min-width: 900px) { .sb-calc__live { display: none; } }
.sb-form__thanks {
	padding: var(--sb-sp-m);
	border: 1px solid var(--sb-acc);
	border-radius: var(--sb-rad-m);
	background: color-mix(in srgb, var(--sb-acc) 12%, transparent);
}
.sb-form__thanks p { margin: 0.375rem 0 0; color: var(--sb-mut); }
.sb-form__thanks ol { margin: 0.625rem 0 0; padding-left: 1.25rem; color: var(--sb-mut); }
.sb-form__thanks li { margin-bottom: 0.25rem; }
.sb-form__thanks a { color: var(--sb-pri); font-weight: var(--sb-semi); }
.sb-toolcard__icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 3rem;
	height: 3rem;
	border-radius: var(--sb-rad-m);
	background: color-mix(in srgb, var(--sb-acc) 16%, transparent);
	color: var(--sb-pri);
	margin-bottom: 0.5rem;
}
.sb-toolcard p { color: var(--sb-mut); margin: 0; }

/* ---------------------------------------------------------------------------
 * Before/after sliders
 * ------------------------------------------------------------------------- */
.sb-ba-grid { display: grid; gap: var(--sb-sp-l); }
@media (min-width: 900px) { .sb-ba-grid { grid-template-columns: 1fr 1fr; } }
.sb-ba { margin: 0; }
.sb-ba__frame {
	--sb-ba: 50%;
	position: relative;
	aspect-ratio: 4 / 3;
	border-radius: var(--sb-rad-l);
	overflow: hidden;
	box-shadow: var(--sb-sh-m);
	touch-action: pan-y;
	cursor: ew-resize;
}
.sb-ba__after, .sb-ba__before { position: absolute; inset: 0; }
.sb-ba__after img, .sb-ba__before img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sb-ba__before { clip-path: inset(0 calc(100% - var(--sb-ba)) 0 0); }
.sb-ba__tag {
	position: absolute;
	top: 0.75rem;
	padding: 0.25rem 0.75rem;
	border-radius: var(--sb-pill);
	background: color-mix(in srgb, var(--sb-ink) 78%, transparent);
	color: var(--sb-pc);
	font-size: var(--sb-fs-s);
	font-weight: var(--sb-semi);
}
.sb-ba__tag--before { left: 0.75rem; }
.sb-ba__tag--after { right: 0.75rem; }
.sb-ba__handle {
	position: absolute;
	top: 0;
	bottom: 0;
	left: var(--sb-ba);
	transform: translateX(-50%);
	width: 2px;
	padding: 0;
	border: 0;
	background: var(--sb-pc);
	cursor: ew-resize;
}
.sb-ba__grip {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	width: 2.75rem;
	height: 2.75rem;
	border-radius: var(--sb-pill);
	background: var(--sb-acc);
	box-shadow: var(--sb-sh-l);
}
.sb-ba__grip::before {
	content: "";
	position: absolute;
	inset: 0;
	margin: auto;
	width: 1rem;
	height: 0.75rem;
	background:
		linear-gradient(to right, var(--sb-ink) 2px, transparent 2px 5px, var(--sb-ink) 5px 7px, transparent 7px 11px, var(--sb-ink) 11px 13px, transparent 13px);
}
.sb-ba__caption { display: grid; gap: 0.25rem; padding-top: 0.75rem; }
.sb-ba__caption a { display: inline-flex; align-items: center; gap: 0.375rem; color: var(--sb-pri); text-decoration: none; font-weight: var(--sb-semi); }
.sb-ba__caption .sb-icon { width: 1rem; height: 1rem; }
.sb-ba__caption .sb-fineprint { color: var(--sb-not); opacity: 1; }
.sb-form__error { color: var(--sb-not); font-weight: var(--sb-semi); margin: 0; }
/* contact-method picker (2026-08-18, Mor; Call / Text / Email since
   2026-10-02): the three pills share one row at 390px+ (flex:auto grows
   them to fill it); narrower phones wrap 2 + 1, never a label. Phones use
   the small label (the max-width 479px block), wider screens the button size.
   Real radios inside pill labels — the dot carries the state for everyone,
   :has() fills the picked pill (checked-state trio shared with the section
   outro hover above). No-JS never sees this: both fields render instead. */
.sb-pick { border: 0; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 0.5rem; }
.sb-pick legend { width: 100%; margin-bottom: 0.375rem; float: left; }
.sb-pick .sb-btn { cursor: pointer; flex: auto; padding: 0 0.5rem; }
.sb-pick input { accent-color: var(--sb-pri); margin: 0; }
.sb-pick .sb-btn:focus-within { outline: 2px solid var(--sb-acc); outline-offset: 2px; }

/* ---------------------------------------------------------------------------
 * Motion — scroll reveals (motifs.js arms below-fold elements with .sb-anim)
 * and the hero's entrance choreography. All collapse under reduced motion.
 * TIERS (2026-08-18): the quick fades below run in BOTH tiers, retuned to the
 * premium-subtle range (12px/550ms, was 20px/700ms). The set pieces — sketch
 * draw, hero reveal, map plot-in, level bubble, header sweep — are 'full'-tier
 * only (html.sb-full via nav.js; motifs.js arms .sb-level/.sb-map only then).
 * ------------------------------------------------------------------------- */
html.sb-js .sb-anim {
	opacity: 0;
	transform: translateY(12px);
}
html.sb-js .sb-anim.is-inview {
	opacity: 1;
	transform: none;
	transition: opacity 450ms ease, transform 550ms cubic-bezier(0.16, 1, 0.3, 1);
}
/* gentle stagger inside grids */
.sb-anim.is-inview:nth-child(2) { transition-delay: 70ms; }
.sb-anim.is-inview:nth-child(3) { transition-delay: 140ms; }
.sb-anim.is-inview:nth-child(4) { transition-delay: 210ms; }
.sb-anim.is-inview:nth-child(5) { transition-delay: 280ms; }
.sb-anim.is-inview:nth-child(6) { transition-delay: 350ms; }

/* level bubble "finds center" when its section scrolls in */
html.sb-js .sb-level.sb-armed .sb-level__bubble::before { transform: translateX(-170%); }
html.sb-js .sb-level.sb-armed.is-inview .sb-level__bubble::before {
	transform: translateX(-50%);
	transition: transform 1000ms cubic-bezier(0.34, 1.3, 0.5, 1) 200ms;
}
/* tape-measure ticks draw in after their step reveals */
html.sb-js .sb-steps__item.sb-anim .sb-step-num::after { width: 0; opacity: 0; }
html.sb-js .sb-steps__item.sb-anim.is-inview .sb-step-num::after {
	width: 2.25rem;
	opacity: 0.5;
	transition: width 600ms ease 350ms, opacity 400ms ease 350ms;
}

@media (prefers-reduced-motion: no-preference) {
	/* the header's level edge sweeps its accent bubble to center once on load
	   ('full' tier only — a per-page-load flourish reads busy in subtle) */
	@keyframes sb-edge-sweep { from { background-position: -60% 0; } to { background-position: 0% 0; } }
	html.sb-full .sb-header::after { background-size: 200% 100%; animation: sb-edge-sweep 1400ms cubic-bezier(0.16, 1, 0.3, 1) both; }

	@keyframes sb-rise { from { opacity: 0; transform: translateY(16px); } }
	@keyframes sb-fade { from { opacity: 0; } }
	html.sb-js .sb-hero__copy > * { animation: sb-rise 600ms cubic-bezier(0.16, 1, 0.3, 1) backwards; }
	html.sb-js .sb-hero__copy > :nth-child(2) { animation-delay: 90ms; }
	html.sb-js .sb-hero__copy > :nth-child(3) { animation-delay: 180ms; }
	html.sb-js .sb-hero__copy > :nth-child(4) { animation-delay: 270ms; }
	html.sb-js .sb-hero__copy > :nth-child(5) { animation-delay: 360ms; }
	html.sb-js .sb-hero__copy > :nth-child(6) { animation-delay: 450ms; }
	html.sb-js .sb-hero__copy > :nth-child(7) { animation-delay: 540ms; }
	html.sb-js .sb-hero__media { animation: sb-fade 900ms ease 250ms backwards; }
	.sb-lb { animation: sb-fade 180ms ease both; } /* reuses sb-fade above */

	/* plan-sheet keyword breathes its glow; even rows run counter-phase */
	@keyframes sb-word-pulse { 50% { text-shadow: 0 0 2.75rem color-mix(in srgb, var(--sb-acc) 85%, transparent); opacity: 0.88; } }
	.sb-guide__sheet-word { animation: sb-word-pulse 4.4s ease-in-out infinite; }
	.sb-guide__row:nth-child(even) .sb-guide__sheet-word { animation-delay: -2.2s; }
}

/* ---------------------------------------------------------------------------
 * Motion guard — every animation/transition collapses under reduced motion
 * ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
	*, *::before, *::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
	}
	html { scroll-behavior: auto; }
	html.sb-js .sb-anim { opacity: 1; transform: none; }
	html.sb-js .sb-level.sb-armed .sb-level__bubble::before { transform: translateX(-50%); }
	html.sb-js .sb-steps__item.sb-anim .sb-step-num::after { width: 2.25rem; opacity: 0.5; }
}

/* ---------------------------------------------------------------------------
 * Service-area map (patterns/area-map.php) — blueprint site plan on the ink
 * panel. SVG geometry comes from the pattern; every colour is a token here.
 * ------------------------------------------------------------------------- */
.sb-map { max-width: 560px; margin: var(--sb-sp-m) auto 0; }
.sb-map__svg { display: block; width: 100%; height: auto; }

/* geography guide lines */
.sb-map__coast {
	fill: none;
	stroke: var(--sb-line);
	stroke-width: 2;
	stroke-linejoin: round;
	stroke-linecap: round;
	opacity: 0.5;
}
.sb-map__bound {
	fill: none;
	stroke: var(--sb-line);
	stroke-width: 1.5;
	stroke-dasharray: 7 8;
	opacity: 0.28;
}
.sb-map__ocean {
	fill: var(--sb-line);
	font-family: var(--wp--preset--font-family--body);
	font-size: 19px;
	letter-spacing: 0.34em;
	text-transform: uppercase;
	opacity: 0.4;
}

/* compass + scale flourishes */
.sb-map__compass-ring { fill: none; stroke: var(--sb-line); stroke-width: 1.5; opacity: 0.45; }
.sb-map__compass-tick,
.sb-map__scale-rule { stroke: var(--sb-line); stroke-width: 1.5; opacity: 0.5; }
.sb-map__compass-n { fill: var(--sb-acc); }
.sb-map__compass-label,
.sb-map__scale-text {
	fill: var(--sb-line);
	font-family: var(--wp--preset--font-family--heading);
	font-size: 18px;
	letter-spacing: 0.08em;
	opacity: 0.85;
}
.sb-map__scale-text { font-family: var(--wp--preset--font-family--body); font-size: 16px; opacity: 0.6; }

/* pins — accent dot + soft glow, real focusable links */
.sb-map__hit { fill: transparent; }
.sb-map__glow { fill: var(--sb-acc); opacity: 0.22; transition: opacity 160ms ease, r 160ms ease; }
.sb-map__dot { fill: var(--sb-acc); transition: fill 160ms ease, r 160ms ease; }
.sb-map__label {
	fill: var(--sb-line);
	dominant-baseline: central;
	font-family: var(--wp--preset--font-family--body);
	font-size: 20px;
	font-weight: var(--wp--custom--font-weight--medium);
	pointer-events: none;
}
.sb-map__pin:hover .sb-map__dot,
.sb-map__pin:focus-visible .sb-map__dot { fill: var(--sb-pc); r: 5.5px; }
.sb-map__pin:hover .sb-map__glow,
.sb-map__pin:focus-visible .sb-map__glow { opacity: 0.55; r: 13px; }
.sb-map__pin:hover .sb-map__label,
.sb-map__pin:focus-visible .sb-map__label { fill: var(--sb-pc); }
.sb-map__pin:focus-visible { outline: none; } /* ring drawn on the glow below */
.sb-map__pin:focus-visible .sb-map__glow {
	stroke: var(--sb-acc);
	stroke-width: 2;
}

/* accent footnote BELOW the plan (owner 2026-07-13: off the map, clear of the
   Chula Vista label) — reassures visitors outside a pinned city */
.sb-map__note {
	margin: 0.625rem 0 0;
	text-align: right;
	color: var(--sb-acc);
	font-size: var(--sb-fs-s);
	font-style: italic;
}

/* county land label — quiet, all-caps, sits in the empty north-inland band */
.sb-map__county {
	fill: var(--sb-line);
	font-family: var(--wp--preset--font-family--heading);
	font-size: 26px;
	letter-spacing: 0.3em;
	text-transform: uppercase;
	opacity: 0.4;
}

/* small screens: labels STAY (an unlabeled dot field means nothing — owner
   2026-07-12); slightly larger type + taps so they read at ~350px wide */
@media (max-width: 700px) {
	.sb-map__label { font-size: 21px; }
	.sb-map__hit { r: 22px; }
	.sb-map__dot { r: 5.5px; }
	.sb-map__ocean { font-size: 22px; }
	.sb-map__county { font-size: 28px; }
}

/* plot-in choreography: motifs.js arms .sb-map below the fold (sb-armed) and
   flips is-inview on scroll. The coastline draws itself, then pins plot in
   coast-to-inland (--sb-i stagger), then the annotations fade up. Fully drawn
   static with no JS; motifs.js no-ops under prefers-reduced-motion. */
/* .sb-armed only ever exists via JS, so no html.sb-js guard is needed */
.sb-map.sb-armed .sb-map__coast {
	stroke-dasharray: 1000;
	stroke-dashoffset: 1000;
	transition: stroke-dashoffset 1300ms ease-out;
}
.sb-map.is-inview .sb-map__coast { stroke-dashoffset: 0; }
/* the armed (hidden) rule must PRECEDE every is-inview reveal — equal
   specificity means source order decides, and sb-armed stays on the node */
.sb-map.sb-armed .sb-map__pin,
.sb-map.sb-armed .sb-map__bound,
.sb-map.sb-armed .sb-map__ocean,
.sb-map.sb-armed .sb-map__county,
.sb-map.sb-armed .sb-map__compass,
.sb-map.sb-armed .sb-map__note,
.sb-map.sb-armed .sb-map__scale { opacity: 0; }
.sb-map.is-inview .sb-map__pin {
	opacity: 1;
	transition: opacity 360ms ease;
	transition-delay: calc(420ms + var(--sb-i, 0) * 55ms);
}
.sb-map.is-inview .sb-map__bound { opacity: 0.28; transition: opacity 600ms ease 1500ms; }
.sb-map.is-inview .sb-map__ocean,
.sb-map.is-inview .sb-map__county { opacity: 0.4; transition: opacity 600ms ease 1650ms; }
.sb-map.is-inview .sb-map__compass,
.sb-map.is-inview .sb-map__scale,
.sb-map.is-inview .sb-map__note { opacity: 1; transition: opacity 600ms ease 1800ms; }

/* reduced-motion: covered by the global collapse rule (motifs.js also no-ops) */

/* T5 location hero: the city IN FOCUS on the plan (inc/map.php focus mode, Mor
   2026-09-29). Dot-matrix land, the service territory lit, spotlight + survey
   crosshair + pulse on the city; every other city grayed but still a live link.
   Geometry rides SVG attributes; currentColor = the accent (set here). The
   pulse needs no motion wrapper: the global guard runs it once, and a ring's
   resting state is invisible. */
.sb-gmap {
	position: relative;
	justify-self: end;
	width: 100%;
	max-width: 34rem;
	margin-block: 0 var(--sb-sp-m);
	color: var(--sb-acc);
	border: 1px solid color-mix(in srgb, var(--sb-line) 14%, transparent);
	border-radius: var(--sb-rad-l);
	background: color-mix(in srgb, var(--sb-ink) 72%, transparent);
	overflow: hidden;
}
.sb-gmap__land { fill: var(--sb-line); opacity: 0.16; }
.sb-gmap a { opacity: 0.6; }
.sb-gmap a:is(:hover, :focus-visible) { opacity: 1; }
.sb-gmap a .sb-map__dot { fill: var(--sb-line); }
.sb-gmap a .sb-map__glow { opacity: 0; }
.sb-gmap .sb-map__label { paint-order: stroke; stroke: var(--sb-ink); stroke-width: 5px; stroke-linejoin: round; }
.sb-gmap__core { fill: var(--sb-ink); }
.sb-gmap__name { fill: var(--sb-acc); font-size: 22px; font-weight: var(--sb-bold); }
.sb-gmap__ring { opacity: 0; transform-box: fill-box; transform-origin: center; animation: sb-ping 2.8s ease-out infinite; }
.sb-gmap__ring + .sb-gmap__ring { animation-delay: 1.4s; }
@keyframes sb-ping { from { opacity: 0.9; } to { transform: scale(3.8); opacity: 0; } }
.sb-gmap__tag {
	position: absolute;
	top: 3%;
	right: 3%;
	display: grid;
	max-width: 40%;
	padding: 0.5rem 0.75rem;
	border: 1px solid color-mix(in srgb, var(--sb-line) 18%, transparent);
	border-radius: var(--sb-rad-m);
	background: color-mix(in srgb, var(--sb-ink) 80%, transparent);
	color: var(--sb-line);
	font-size: var(--sb-fs-s);
}
.sb-gmap__tag .sb-eyebrow { margin: 0; }
.sb-gmap__city { color: var(--sb-pc); font-size: clamp(0.9375rem, 1.6vw, 1.375rem); font-weight: var(--sb-bold); line-height: 1.2; }
