/* Fine Stripes — single design system.
 *
 * Palette sampled from the client's actual badge logo (2026-08-12, pixel
 * histogram of the 1600px original): black #000, white #f0f0f0, and a yellow
 * cluster centred on #F0E018 with a gold shadow around #C9A50C. The badge is
 * built for dark backgrounds, so the header and hero are asphalt-dark and the
 * yellow does what it does on a real lot: lines, arrows and zones — never
 * large text fills.
 */

:root {
	--fs-black:      #0c0d0f;
	--fs-asphalt:    #17181c;
	--fs-asphalt-2:  #212327;
	--fs-ink:        #1c1e22;
	--fs-slate:      #4a4f57;
	--fs-mute:       #6b7280;
	--fs-line:       #e3e6ea;
	--fs-bg:         #ffffff;
	--fs-bg-alt:     #f6f7f9;

	--fs-yellow:     #f0e018;   /* sampled from badge */
	--fs-yellow-dk:  #c9a50c;   /* badge gold shadow  */
	--fs-blue:       #1b4fd8;
	--fs-blue-dk:    #143da8;

	--fs-wrap:       1180px;
	--fs-radius:     12px;
	--fs-gap:        clamp(1rem, 2.5vw, 2rem);

	--fs-font:       system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

	/* Hazard stripe — the one place the brand motif is allowed to shout. */
	--fs-hazard: repeating-linear-gradient(
		-45deg,
		var(--fs-yellow) 0 14px,
		var(--fs-black) 14px 28px
	);
}

*, *::before, *::after { box-sizing: border-box; }

body {
	margin: 0;
	font-family: var(--fs-font);
	font-size: 1.0625rem;
	line-height: 1.65;
	color: var(--fs-ink);
	background: var(--fs-bg);
	-webkit-font-smoothing: antialiased;
}

.wrap { width: min(100% - 2.5rem, var(--fs-wrap)); margin-inline: auto; }

h1, h2, h3, h4 { color: var(--fs-black); line-height: 1.15; margin: 0 0 .6em; font-weight: 800; }
h1 { font-size: clamp(2.1rem, 4.8vw, 3.4rem); letter-spacing: -.02em; }
h2 { font-size: clamp(1.55rem, 3vw, 2.25rem); letter-spacing: -.015em; }
h3 { font-size: clamp(1.15rem, 2vw, 1.4rem); }

p  { margin: 0 0 1.15em; }
a  { color: var(--fs-blue); text-underline-offset: .15em; }
a:hover { color: var(--fs-blue-dk); }
img { max-width: 100%; height: auto; }

.skip-link {
	position: absolute; left: -9999px;
	background: var(--fs-black); color: #fff; padding: .75rem 1rem; z-index: 100;
}
.skip-link:focus { left: 1rem; top: 1rem; }

:focus-visible { outline: 3px solid var(--fs-blue); outline-offset: 2px; }

/*
 * Visually hidden, but NOT hidden from assistive tech or from a crawler that
 * renders the page. The clip/clip-path pair is the technique that keeps the
 * element in the accessibility tree and in the rendered DOM; `display: none`
 * and `visibility: hidden` would drop it out of both, which is the thing search
 * engines discount hardest and screen readers skip entirely.
 *
 * Used on the homepage H1. Whatever sits in here is READ ALOUD to a screen
 * reader user even though a sighted visitor never sees it, so it must be an
 * honest description of the page and must not contradict what is on screen.
 */
.sr-only {
	position: absolute;
	width: 1px; height: 1px;
	padding: 0; margin: -1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/*
 * Hero display line. Carries the H1's old look now that the real H1 is
 * screen-reader-only. Deliberately NOT a heading tag — an <h1> here would break
 * the one-H1 contract and an <h2> would open the page's outline at level two.
 */
.hero-display {
	color: #fff; max-width: 18ch;
	font-size: clamp(2.1rem, 4.8vw, 3.4rem);
	line-height: 1.15; letter-spacing: -.02em; font-weight: 800;
	margin: 0 0 .6em;
}

/* Kicker — small yellow overline used to label sections. */
.kicker {
	display: inline-block;
	font-size: .82rem; font-weight: 800; letter-spacing: .14em;
	text-transform: uppercase; color: var(--fs-yellow-dk);
	margin-bottom: .5rem;
}
.kicker::before {
	content: ""; display: inline-block; vertical-align: middle;
	width: 2rem; height: 4px; margin-right: .6rem; border-radius: 2px;
	background: var(--fs-yellow);
}

/* ---------- Header ---------- */

.site-header {
	position: sticky; top: 0; z-index: 50;
	background: var(--fs-black);
	border-bottom: 3px solid var(--fs-yellow);
}
.header-inner {
	display: flex; align-items: center; gap: var(--fs-gap);
	min-height: 76px; flex-wrap: wrap; padding: .35rem 0;
}
.brand { margin-right: auto; text-decoration: none; display: flex; align-items: center; gap: .65rem; }
.brand-badge { height: 60px; width: auto; display: block; }
.brand-text {
	color: #fff; font-weight: 800; font-size: 1.25rem;
	letter-spacing: .04em; text-transform: uppercase;
}
.nav-list {
	display: flex; gap: 1.35rem; list-style: none; margin: 0; padding: 0; flex-wrap: wrap;
}
.nav-list a { color: #e8eaed; text-decoration: none; font-weight: 600; font-size: .95rem; }
.nav-list a:hover { color: var(--fs-yellow); }

/* Services submenu. The six service pages are now nested under "Services" in
   the primary menu, and .nav-list is a wrapping flex row — with no rule at all
   those six links render as an always-visible nested list inside the sticky
   header. :focus-within is not optional: without it the dropdown opens on hover
   only and is unreachable from the keyboard. */
.nav-list li { position: relative; }
.nav-list .sub-menu {
	display: none; position: absolute; top: 100%; left: -.9rem; z-index: 60;
	min-width: 15rem; margin: 0; padding: .55rem 0; list-style: none;
	background: var(--fs-asphalt-2);
	border: 1px solid #2a2d33; border-radius: var(--fs-radius);
}
.nav-list li:hover > .sub-menu,
.nav-list li:focus-within > .sub-menu { display: block; }
.nav-list .sub-menu a { display: block; padding: .5rem .9rem; font-weight: 500; }

/*
 * The services submenu is DESKTOP ONLY. Below 860px the header nav is a single
 * horizontally scrolling row and there is nowhere good to put six more links.
 * All three alternatives were built and measured before settling on this:
 *
 *  - Left as the absolute dropdown: no hover on a phone, so it never opens.
 *  - Flattened with `display:contents` on the parent li: MEASURED fine at 111px
 *    tall and RENDERED broken. `.sub-menu` is itself a flex container, so it
 *    became a flex item, `min-width:0` shrank its box to nothing, and its six
 *    children overflowed to x=1150 underneath the top-level links. Six
 *    overlapping pairs at 375px. The header height was the wrong thing to
 *    measure; the overlap only showed up on screen.
 *  - Given a row of its own: header goes to 154px, more than double the 71px
 *    this nav was deliberately condensed to.
 *
 * So it is hidden here. Nothing is actually lost: every service is one tap away
 * inside Services, and each also appears in the homepage grid and the footer.
 * Hiding a duplicate path beats breaking the primary one.
 */
@media (max-width: 860px) {
	.nav-list .sub-menu { display: none; }
}

.header-cta { display: flex; gap: .6rem; align-items: center; }

.btn {
	display: inline-flex; align-items: center; gap: .4rem;
	padding: .72rem 1.2rem; border-radius: var(--fs-radius);
	font-weight: 800; text-decoration: none; font-size: .95rem;
	border: 2px solid transparent; transition: background .15s, color .15s, border-color .15s;
}
.btn-call { background: transparent; color: #fff; border-color: #3a3d43; }
.btn-call:hover { border-color: var(--fs-yellow); color: var(--fs-yellow); }
.btn-call-label { display: none; }
.btn-quote { background: var(--fs-yellow); color: var(--fs-black); }
.btn-quote:hover { background: #fff; color: var(--fs-black); }

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

.hero {
	position: relative; overflow: hidden;
	background:
		radial-gradient(60rem 30rem at 85% -20%, #2a2d33 0%, transparent 60%),
		var(--fs-asphalt);
	color: #fff;
	padding: clamp(3rem, 8vw, 6rem) 0;
}
/* Lot lines: angled parking-stall strokes, faint, like looking down at fresh
   layout paint on dark asphalt. Pure CSS — no image request. */
/*
 * HERO LAYER ORDER, set explicitly because four things overlap here and
 * z-index:auto would leave the stacking to source order, which is fragile:
 *   0  .hero-photo   the photograph
 *   1  .hero::after  the scrim that makes text readable over it
 *   2  .hero::before the yellow lot-line motif, on top of the scrim so it reads
 *   3  .hero-grid / .rig   all real content and the zebra
 */
.hero-photo {
	position: absolute; inset: 0; z-index: 0;
	width: 100%; height: 100%;
	object-fit: cover;
	/* Bias down-frame: the lines are in the lower half, the sky is not. */
	object-position: 50% 62%;
}

/*
 * The scrim. ONE gradient, on purpose.
 *
 * The first attempt stacked a horizontal and a vertical gradient, which was a
 * mistake with a number attached: stacked alphas combine as 1-(1-a1)(1-a2), so
 * .92 over .35 lands at .95, not somewhere in between. Measured on the live
 * page, a photo pixel of rgb(129,136,136) was compositing to rgb(29,29,29) and
 * text contrast came out at 16:1 where AA asks for 4.5. The photo was paid for
 * in bytes and then erased in CSS.
 *
 * So: a single vertical ramp, dark through the text band and opening up over
 * the lower third where the zebra walks, then closing again at the very bottom
 * so the rig has ground to stand on. Values chosen from measurement, not taste
 * — at the lede the composite gives 7.5:1, comfortably past AA with the photo
 * still clearly readable underneath.
 */
.hero::after {
	content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
	background: linear-gradient(180deg,
		rgba(23, 24, 28, .90)  0%,
		rgba(23, 24, 28, .82) 20%,
		rgba(23, 24, 28, .76) 48%,
		rgba(23, 24, 28, .42) 70%,
		rgba(23, 24, 28, .24) 85%,
		rgba(23, 24, 28, .60) 100%);
}

.hero-grid { z-index: 3; }
.rig { z-index: 3; }

.hero::before {
	content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 2;
	background: repeating-linear-gradient(
		115deg,
		transparent 0 140px,
		rgba(240, 224, 24, .13) 140px 146px
	);
	mask-image: linear-gradient(90deg, transparent 30%, #000 75%);
	-webkit-mask-image: linear-gradient(90deg, transparent 30%, #000 75%);
}
.hero-grid {
	position: relative;
	display: grid; gap: clamp(1.5rem, 4vw, 3.5rem); align-items: center;
	grid-template-columns: minmax(0, 1.25fr) minmax(0, .75fr);
}
.hero h1 { color: #fff; max-width: 18ch; }
.hero .lede { color: #c7cbd2; font-size: 1.16rem; max-width: 56ch; }
.hero-badge { display: none; }
.hero-actions { display: flex; gap: .75rem; flex-wrap: wrap; margin-top: 1.6rem; }
.hero-points {
	display: flex; gap: 1.6rem 2.2rem; flex-wrap: wrap;
	list-style: none; margin: 2.2rem 0 0; padding: 0;
	color: #e8eaed; font-weight: 700; font-size: .95rem;
}
.hero-points li { display: flex; align-items: center; gap: .5rem; }
.hero-points li::before {
	content: ""; width: 1.35rem; height: 4px; border-radius: 2px;
	background: var(--fs-yellow);
}

@media (min-width: 860px) {
	.hero-badge {
		display: block; width: min(100%, 340px); margin-inline: auto;
		filter: drop-shadow(0 18px 40px rgba(0,0,0,.55));
	}
}

/* ---------- Hazard divider ---------- */

.hazard { height: 10px; background: var(--fs-hazard); }

/* ---------- Brand strip ---------- */

.brand-strip { background: var(--fs-bg); padding: clamp(2rem, 4vw, 3rem) 0; border-bottom: 1px solid var(--fs-line); }
.brand-strip h2 { text-align: center; font-size: clamp(1.2rem, 2vw, 1.5rem); margin-bottom: 1.6rem; }
.brand-logos {
	display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
	gap: clamp(1.2rem, 3vw, 2.6rem); list-style: none; margin: 0; padding: 0;
}
.brand-logos img {
	height: clamp(44px, 6vw, 64px); width: auto; display: block;
	border-radius: 8px;
}

/* ---------- Sections ---------- */

.section { padding: clamp(2.75rem, 6vw, 5rem) 0; }
.section-alt { background: var(--fs-bg-alt); }
.section-dark { background: var(--fs-asphalt); color: #c7cbd2; }
.section-dark h2, .section-dark h3 { color: #fff; }
.lede { font-size: 1.15rem; color: var(--fs-slate); max-width: 62ch; }
.section-dark .lede { color: #c7cbd2; }

.grid { display: grid; gap: var(--fs-gap); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }

.card {
	position: relative;
	background: #fff; border: 1px solid var(--fs-line);
	border-radius: var(--fs-radius); padding: 1.6rem;
	transition: transform .15s, box-shadow .15s;
}
/* The painted line: every service card carries one fresh stripe. */
.card::before {
	content: ""; position: absolute; top: 0; left: 1.6rem;
	width: 3.2rem; height: 5px; border-radius: 0 0 3px 3px;
	background: var(--fs-yellow);
}
.card:hover { transform: translateY(-3px); box-shadow: 0 14px 34px rgba(12,13,15,.10); }
.card h3 { margin: .35rem 0 .45rem; }
.card p { margin-bottom: .8rem; color: var(--fs-slate); font-size: .98rem; }
.card-link { font-weight: 700; text-decoration: none; }
.card-link::after { content: " →"; }

.seg-list {
	display: flex; flex-wrap: wrap; gap: .6rem;
	list-style: none; margin: 1.4rem 0 0; padding: 0;
}
.seg-list li {
	background: #fff; border: 1px solid var(--fs-line); border-radius: 999px;
	padding: .5rem 1.05rem; font-weight: 600; font-size: .93rem; color: var(--fs-ink);
}

/* ---------- Process band ---------- */

.steps { counter-reset: step; }
.step { position: relative; padding: 1.6rem; padding-top: 3.4rem; }
.step::before {
	counter-increment: step; content: counter(step, decimal-leading-zero);
	position: absolute; top: 1.1rem; left: 1.6rem;
	font-size: 1.5rem; font-weight: 800; color: var(--fs-yellow-dk);
	letter-spacing: .02em;
}

/* The process block is a real <ol> — the steps ARE ordered, and a screen reader
   should say "list, 6 items" rather than meeting six unrelated headings. The
   list-style is removed because .step::before already paints the numeral; left
   as a bare <ol> it would render Google's marker AND the counter. */
.steps-ol { list-style: none; margin: 0; padding: 0; }
.steps-foot { margin: 1.4rem 0 0; color: var(--fs-slate); max-width: 62ch; }

/* ---------- CTA band ---------- */

.cta-band {
	position: relative; overflow: hidden;
	background: var(--fs-asphalt); color: #fff;
	padding: clamp(2.75rem, 6vw, 4.5rem) 0;
	text-align: center;
}
/* Dashed centre line running through the band, like a freshly painted lane. */
.cta-band::before {
	content: ""; position: absolute; left: 0; right: 0; top: 50%;
	height: 6px; transform: translateY(-50%);
	background: repeating-linear-gradient(90deg,
		rgba(240,224,24,.16) 0 70px, transparent 70px 130px);
	pointer-events: none;
}
.cta-band .wrap { position: relative; }
.cta-band h2 { color: #fff; }
.cta-band p { color: #c7cbd2; max-width: 52ch; margin-inline: auto; }
.cta-band .hero-actions { justify-content: center; }

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

.lead-form {
	display: grid; gap: 1rem; max-width: 34rem;
	background: #fff; border: 1px solid var(--fs-line);
	border-radius: var(--fs-radius); padding: 1.75rem;
	box-shadow: 0 18px 44px rgba(12,13,15,.08);
}
.lead-form label { font-weight: 700; font-size: .93rem; display: block; margin-bottom: .3rem; }
.lead-form input, .lead-form select, .lead-form textarea {
	width: 100%; padding: .75rem .85rem; font: inherit;
	border: 1px solid var(--fs-line); border-radius: 8px; background: #fff;
}
.lead-form input:focus, .lead-form select:focus, .lead-form textarea:focus {
	border-color: var(--fs-yellow-dk); outline: none;
	box-shadow: 0 0 0 3px rgba(240,224,24,.25);
}
.lead-form textarea { min-height: 7rem; resize: vertical; }

/* Paired fields on the shortened form (templates/lead-form.php .field-pair).
   auto-fit + minmax rather than a media query on purpose: this same form
   renders full width at #quote and inside a ~380px hero column, so a viewport
   breakpoint would put two cramped inputs side by side in the hero. The
   container decides, not the screen. `gap: inherit` keeps the pair on the same
   spacing as the fields above and below it in both variants. */
.lead-form .field-pair {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
	gap: inherit;
}

/* Submission status from fs_lead_notice(). It MUST set its own colour: the hero
   sets color:#fff across its whole subtree, which is what previously rendered
   form labels white on white. */
.lead-notice {
	margin: 0 0 1rem;
	padding: .7rem .9rem;
	border-left: 4px solid var(--fs-yellow-dk);
	border-radius: 8px;
	background: var(--fs-bg-alt);
	color: var(--fs-ink);
	font-size: .95rem;
	font-weight: 600;
}

.lead-form .btn-quote { justify-content: center; }
.lead-form .btn-quote:hover { background: var(--fs-black); color: var(--fs-yellow); }
.form-note { font-size: .85rem; color: var(--fs-mute); margin: 0; }
.fs-hp { position: absolute; left: -9999px; }

/* ---------- Footer ---------- */

.site-footer { background: var(--fs-black); color: #c7cbd2; padding: 3rem 0 1.5rem; border-top: 3px solid var(--fs-yellow); }
.site-footer a { color: #e8eaed; }
.site-footer a:hover { color: var(--fs-yellow); }
.footer-grid { display: grid; gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.footer-badge { height: 96px; width: auto; margin-bottom: .8rem; }
.footer-brand { color: #fff; font-weight: 800; text-transform: uppercase; letter-spacing: .04em; margin-bottom: .4rem; }
.footer-heading { color: #fff; font-weight: 800; margin-bottom: .6rem; }
.footer-list, .footer-nap, .footer-social, .footer-legal-list { list-style: none; margin: 0; padding: 0; }
.footer-list li, .footer-nap li { padding: .2rem 0; }
.footer-social { display: flex; gap: 1rem; flex-wrap: wrap; margin-top: 1rem; }
.map-embed { border-radius: var(--fs-radius); overflow: hidden; }
.footer-legal {
	display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
	border-top: 1px solid #2a2d33; margin-top: 2rem; padding-top: 1.25rem; font-size: .9rem;
}
.footer-legal-list { display: flex; gap: 1.25rem; flex-wrap: wrap; }

@media (min-width: 640px) {
	.btn-call-label { display: inline; }
}
@media (max-width: 859px) {
	.hero-grid { grid-template-columns: 1fr; }
}

/* ==========================================================================
   The rig — animated zebra laying a stripe across the hero.
   Built from the client's own badge art: the zebra + striping machine were
   isolated out of the logo, so the animation is literally his mascot doing
   the job, not a stock illustration.
   ========================================================================== */

.rig {
	position: relative;
	height: clamp(150px, 20vw, 240px);
	overflow: hidden;
	background: var(--fs-asphalt);
	border-top: 1px solid #2a2d33;
}

/* Asphalt texture + the faint old lines the crew is painting over. */
.rig::before {
	content: ""; position: absolute; inset: 0;
	background: repeating-linear-gradient(90deg,
		transparent 0 118px, rgba(255,255,255,.05) 118px 124px);
}

/* The fresh stripe, growing left-to-right in step with the machine. */
.rig-paint {
	position: absolute;
	left: 0;
	bottom: clamp(26px, 4vw, 42px);
	height: clamp(6px, .9vw, 10px);
	width: 0;
	background: linear-gradient(90deg, var(--fs-yellow-dk), var(--fs-yellow));
	border-radius: 2px;
	box-shadow: 0 0 18px rgba(240, 224, 24, .45);
	animation: fs-paint 15s linear infinite;
}

.rig-zebra {
	position: absolute;
	bottom: 0;
	left: 0;
	height: clamp(130px, 18vw, 215px);
	width: auto;
	transform: translateX(-380px);
	animation: fs-drive 15s linear infinite;
	filter: drop-shadow(0 10px 18px rgba(0,0,0,.5));
	will-change: transform;
}

@keyframes fs-drive {
	from { transform: translateX(-380px); }
	to   { transform: translateX(100vw); }
}
@keyframes fs-paint {
	from { width: 0; }
	to   { width: 100vw; }
}

/* Motion is decoration here, so it goes away entirely when asked — the
   finished stripe stays, which is the point of the image either way. */
@media (prefers-reduced-motion: reduce) {
	.rig-zebra { animation: none; transform: translateX(6vw); }
	.rig-paint { animation: none; width: 6vw; }
}

/* ==========================================================================
   Photo blocks
   ========================================================================== */

.media-figure { margin: 0; }
.media-figure img {
	display: block; width: 100%; height: auto;
	border-radius: var(--fs-radius);
	background: var(--fs-bg-alt);
}
.media-figure figcaption {
	margin-top: .6rem; font-size: .9rem; color: var(--fs-mute);
}

/* In-body figure from the [fs_photo] shortcode. Same .media-figure styling as
   every other photo block; all this adds is vertical rhythm, because it sits
   between two paragraphs rather than in a grid cell. No aspect-ratio crop:
   fs_photo() declares the real 1400x1050, and cropping in CSS would make that
   declared box a lie and reintroduce layout shift. */
.entry-figure { margin: 2rem 0 2.2rem; }

.split { display: grid; gap: clamp(1.5rem, 4vw, 3rem); align-items: center; }
@media (min-width: 820px) {
	.split { grid-template-columns: 1fr 1fr; }
	.split-media-first .split-media { order: -1; }
}

.gallery { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.gallery img {
	display: block; width: 100%; height: 100%;
	aspect-ratio: 4 / 3; object-fit: cover;
	border-radius: var(--fs-radius);
}

/* Before/after band.

   Deliberately NOT the .gallery rule. That one crops every tile to 4/3 with
   object-fit: cover, which is right for a wall of finished-work shots but fatal
   here: these photographs are square composites carrying the before state above
   the after state, and a 4:3 crop slices one of the two halves off — destroying
   the only thing the image is on the page to show. So no aspect-ratio and no
   crop; each figure keeps the true ratio fs_photo() declares for it, which is
   also what keeps the reserved box honest and the layout shift at zero.

   Two columns rather than auto-fit: these are read as pairs and want room, and
   auto-fit at minmax(240px) would drop a fifth tile onto a lonely second row on
   a wide screen. */
.ba-grid { display: grid; gap: 1.25rem; grid-template-columns: 1fr; }
@media (min-width: 780px) {
	.ba-grid { grid-template-columns: 1fr 1fr; gap: 1rem 1.5rem; }
}
.ba-item figcaption { margin-top: .55rem; }

/* Three-up keyless area maps on the town pages (/social-proof canon). */
.map-grid { display: grid; gap: 1rem; grid-template-columns: 1fr; }
@media (min-width: 760px) {
	.map-grid { grid-template-columns: repeat(3, 1fr); }
}
.map-card {
	border: 1px solid var(--fs-line);
	border-radius: var(--fs-radius);
	overflow: hidden;
	background: var(--fs-bg);
	display: flex; flex-direction: column;
}
/* The iframe is a replaced element with no intrinsic ratio here, so it gets an
   explicit height in the markup AND display:block — otherwise its inline
   baseline leaves a few px of dead space under it inside the card. */
.map-card iframe { display: block; width: 100%; border: 0; }
.map-card-body { padding: .9rem 1rem 1.1rem; display: flex; flex-direction: column; gap: .35rem; }
.map-card-body h3 { margin: 0; font-size: 1.05rem; }
.map-card-dist { margin: 0; font-size: .88rem; color: var(--fs-mute); }
.map-card .card-link { margin-top: .35rem; }
.map-card-dir {
	margin-top: .1rem;
	font-size: .88rem; font-weight: 700;
	color: var(--fs-blue); text-decoration: none;
}
.map-card-dir:hover, .map-card-dir:focus-visible { text-decoration: underline; }

/* ==========================================================================
   Entry prose rhythm (/format, 2026-08-18)
   ========================================================================== */

/* Comparison tables inside seeded prose. The theme had NO table styling at
   all because the prose had no tables; the /format pass derives them from
   sentences already on the page. Wrapper div.table-scroll gives wide tables
   their own horizontal scroll so the PAGE never scrolls sideways. */
.table-scroll { overflow-x: auto; margin: 1.6rem 0 2rem; }
.entry table, .table-scroll table {
	width: 100%;
	/*
	 * min-width is what makes the wrapper's overflow-x actually DO anything.
	 * Without it `width:100%` simply squeezes a three-column comparison into a
	 * 335px phone — measured: scrollWidth == clientWidth, so the scroll
	 * container never engaged and the cells wrapped to a word per line. With a
	 * floor, the wrapper scrolls and the columns stay legible. 34rem is just
	 * under the narrowest common phone in landscape, so it only kicks in where
	 * the squeeze would otherwise happen.
	 */
	min-width: 34rem;
	border-collapse: collapse;
	font-size: .95rem;
	line-height: 1.45;
}
.entry th, .entry td, .table-scroll th, .table-scroll td {
	text-align: left;
	vertical-align: top;
	padding: .6rem .8rem;
	border-bottom: 1px solid var(--fs-line, #e3e3e3);
}
.entry thead th, .table-scroll thead th {
	border-bottom: 2px solid var(--fs-yellow, #f5c518);
	font-size: .82rem;
	text-transform: uppercase;
	letter-spacing: .04em;
}

/* Alternate in-prose figures left/right on wide screens so the long service
   prose reads as a magazine column instead of a straight river. Figures keep
   their true declared aspect (no crop); text wraps around them. nth-of-type
   counts FIGURES only, so inserting a paragraph never flips the pattern.
   Mobile untouched: full-width figures, no float. */
@media (min-width: 900px) {
	.entry .entry-figure {
		width: 400px;
		max-width: 42%;
		margin: .35rem 0 1.2rem 2.2rem;
		float: right;
		clear: right;
	}
	.entry .entry-figure:nth-of-type(even) {
		float: left;
		clear: left;
		margin: .35rem 2.2rem 1.2rem 0;
	}
	/* Headings and tables start clean rows rather than wrapping beside a photo.
	   A table squeezed into the ~55ch gutter beside a float is unreadable. */
	.entry h2,
	.entry .table-scroll { clear: both; }
}


/* ==========================================================================
   Interior hero carrying a form (/format, 2026-08-18)
   ========================================================================== */

/* Mirrors the homepage .hero-grid rather than inventing a second system. The
   copy column gets the larger share; the form card is deliberately narrower,
   because a wide form reads as a long form and this one is five fields. */
.page-hero-form .hero-grid {
	display: grid;
	gap: clamp(1.5rem, 4vw, 3rem);
	align-items: start;
	grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 940px) {
	.page-hero-form .hero-grid {
		grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);
	}
}

/* The page hero is the dark asphalt band, so the strip's default slate text
   and its empty-star grey both disappear into it. Only the colours change —
   the star geometry and the real 5.0/19 figures come from the shared partial
   and are not touched here.
 *
 * ⚠️ SCOPED TO .hero-copy, NOT to .page-hero. The form card is ALSO inside the
 * hero and carries its own copy of this strip on a WHITE background. Scoping
 * these overrides to the whole hero shipped two invisible-text bugs at once:
 * the white link rendered white-on-white inside the card, and `strong` kept its
 * near-black default and vanished against the asphalt, so the hero read
 * "from 19 Google reviews" with the 5.0 missing entirely. `strong` needs an
 * explicit light colour here — inheriting the parent's is not enough, because
 * the base rule sets it to --fs-black. */
.page-hero .hero-copy .rating-strip { color: #e8eaed; margin: 1.25rem 0 0; }
.page-hero .hero-copy .rating-strip strong { color: #fff; }
.page-hero .hero-copy .rating-strip .star-empty { color: rgba(255, 255, 255, .28); }
.page-hero .hero-copy .rating-strip a { color: #fff; }

/* Benefits. On the dark band these are the same yellow-dash list the homepage
   uses; stacked rather than wrapped inline, because interior points are full
   sentences rather than the homepage's three short phrases. */
.page-hero .hero-points {
	display: grid; gap: .65rem;
	margin: 1.5rem 0 0;
	font-size: .95rem;
	font-weight: 600;
}
.page-hero .hero-points li { align-items: flex-start; }
.page-hero .hero-points li::before { margin-top: .55rem; flex: 0 0 auto; }

/* The form card sits ON the dark band, so it keeps its white background and
   yellow top rule from the homepage rule and just needs spacing here. */
.page-hero .hero-form { margin-top: .25rem; }
@media (max-width: 939px) {
	.page-hero .hero-form { margin-top: 1.5rem; }
}

/* Page hero band for interior pages. */
.page-hero {
	position: relative;
	background: var(--fs-asphalt);
	color: #fff;
	padding: clamp(2.5rem, 6vw, 4rem) 0;
	overflow: hidden;
}
.page-hero::before {
	content: ""; position: absolute; inset: 0; pointer-events: none;
	background: repeating-linear-gradient(115deg,
		transparent 0 140px, rgba(240,224,24,.10) 140px 146px);
	mask-image: linear-gradient(90deg, transparent 40%, #000 90%);
	-webkit-mask-image: linear-gradient(90deg, transparent 40%, #000 90%);
}
.page-hero .wrap { position: relative; }
.page-hero h1 { color: #fff; max-width: 22ch; }
.page-hero .lede { color: #c7cbd2; }
.crumbs { font-size: .9rem; color: #9aa0a8; margin-bottom: .9rem; }
.crumbs a { color: #c7cbd2; text-decoration: none; }
.crumbs a:hover { color: var(--fs-yellow); }

/* ==========================================================================
   Social proof
   ========================================================================== */

.proof-band { background: var(--fs-bg-alt); }
.proof-embed { border-radius: var(--fs-radius); overflow: hidden; border: 1px solid var(--fs-line); background: #fff; }
.proof-embed iframe { display: block; width: 100%; border: 0; }
.proof-note { font-size: .92rem; color: var(--fs-mute); }
/* The review row sits directly under the map card and butts against it without
   this. Scales with the band's own padding rather than a fixed gap. */
.proof-reviews { margin-top: clamp(1.75rem, 4vw, 2.75rem); }

.gbp-cta {
	display: inline-flex; align-items: center; gap: .5rem;
	font-weight: 700; text-decoration: none;
}

/* Service list on interior pages */
.tick-list { list-style: none; margin: 1.2rem 0 0; padding: 0; display: grid; gap: .7rem; }
.tick-list li { position: relative; padding-left: 1.9rem; }
.tick-list li::before {
	content: ""; position: absolute; left: 0; top: .62em;
	width: 1.15rem; height: 4px; border-radius: 2px; background: var(--fs-yellow);
}

/* Area list */
.area-grid { display: grid; gap: .6rem; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); list-style: none; margin: 1.5rem 0 0; padding: 0; }
.area-grid li { background: #fff; border: 1px solid var(--fs-line); border-radius: 8px; padding: .7rem 1rem; font-weight: 600; }

/* FAQ */
.faq { display: grid; gap: .8rem; margin-top: 1.5rem; }
.faq details {
	background: #fff; border: 1px solid var(--fs-line);
	border-radius: var(--fs-radius); padding: 1rem 1.25rem;
}
.faq summary { font-weight: 700; cursor: pointer; }
.faq details[open] summary { margin-bottom: .6rem; }
.faq p { margin: 0; color: var(--fs-slate); }

/* ==========================================================================
   Polish pass — fixes from an adversarial design review, 2026-08-12.
   Each block notes the measured problem it solves.
   ========================================================================== */

/* Readable measure. Photo-less pages (legal, thank-you) inherited the bare
   1180px wrap and ran ~139 characters per line — nearly double the ceiling. */
.entry { max-width: 70ch; }

/* ── Wider illustrated body (Dylan: "make the body wider … more seamless") ──
 *
 * ⚠️ THIS BLOCK MUST STAY BELOW `.entry { max-width: 70ch }`. It lived above it
 * until 2026-08-18 and did nothing at all: both are single-class selectors, so
 * the later rule won and the body measured 73ch while the CSS claimed 92ch.
 * Moving it, not restyling it, is what made the width real.
 *
 * MEASURED on the live page at a 1280px viewport: wrap 1180px, one "0" glyph
 * 10.28px at the 17px body size. So the numbers below are derived, not picked:
 *
 *   body 88ch          = ~905px
 *   gutter each side   = (1180 - 905) / 2 = ~137px
 *   figure 420px with -140px outer margin sits 280px inside the text column
 *   text beside a float = 905 - 280 - 35 = ~590px = ~57 characters
 *   total visual width  = 905 + 140 + 140 = ~1185px, i.e. the full wrap
 *
 * That is the "seamless" part: the PHOTOS reach the wrap edges so the page
 * fills its width, while the text keeps a measure you can actually read. The
 * alternative — widening the text itself to 1180px — is the ~139-character line
 * the 70ch rule above exists to prevent. */
.entry-illustrated { max-width: 88ch; }

@media (min-width: 1300px) {
	/* Break the figures OUT of the text column into the gutter.
	 *
	 * The guard is 1300px, not the 1240px first tried. At 1240 the wrap is
	 * already at its 1180px ceiling, so the -137px breakout put the figure's
	 * outer edge 14px from the browser chrome — measured, and technically not an
	 * overflow, but visually pinned to the glass. By 1300 the viewport has
	 * 60px of its own margin outside the wrap, so the figure lands ~40px clear.
	 * Below this the figures still float and still alternate; they simply stay
	 * inside the text column. */
	.entry-illustrated .entry-figure {
		width: 420px;
		max-width: none;
		margin-right: -137px;
	}
	.entry-illustrated .entry-figure:nth-of-type(even) {
		margin-left: -137px;
		margin-right: 2.2rem;
	}

	/* Tables break out BOTH sides — a comparison table is scanned, not read to a
	   measure, and full width is what stops the cells wrapping to three lines. */
	.entry-illustrated .table-scroll {
		margin-left: -137px;
		margin-right: -137px;
	}
}

@media (max-width: 899px) {
	.entry-illustrated { max-width: 70ch; }
}
.faq p { max-width: 70ch; }

/* The six service pages dropped their split hero, so .entry stopped being a
   grid column and its 70ch measure sat hard left in an 1180px wrap with ~450px
   of dead space beside it. Centre the measure whenever the wrap is NOT a split;
   inside a split the column position is the grid's job and must not move. */
.wrap:not(.split) > .entry { margin-inline: auto; }

/* Kicker + step numerals were #c9a50c on white: 2.37:1, failing AA. This gold
   clears 4.5:1 on both white and the alt background. The DECORATIVE bars stay
   pure brand yellow — they just get thicker, so the colour carries by mass
   rather than by contrast. */
:root { --fs-gold-text: #8a6f00; }
.kicker { color: var(--fs-gold-text); }
.step::before { color: var(--fs-gold-text); }
.kicker::before { height: 6px; }
.tick-list li::before { height: 5px; }
.card::before { height: 6px; }

/* The Call button's border was #3a3d43 on near-black — 1.79:1, no perceivable
   boundary. */
.btn-call { border-color: #5c626b; }

/* auto-fit resolved to FOUR tracks at the 1180px wrap, so six cards and six
   photos both landed 4 + 2 with a half-empty orphan row. */
@media (min-width: 1000px) {
	.grid-3 { grid-template-columns: repeat(3, 1fr); }
	.gallery { grid-template-columns: repeat(3, 1fr); }
}

/* Cards were bottom-ragged because copy lengths differ; the CTA sat at a
   different height in every card. */
.card { display: flex; flex-direction: column; }
.card-link { margin-top: auto; white-space: nowrap; }

/* The form column ran ~570px taller than the copy beside it, leaving the
   largest void on the page. */
#quote .grid-2 { align-items: center; }
#quote .lead-form { max-width: none; }

/* Interior hero: content filled only the left half of a 1440px band. Narrow the
   measure and let the lot-line motif carry the right side. */
.page-hero { padding: clamp(2rem, 4vw, 3rem) 0; }
.page-hero .wrap { max-width: 62rem; }
.page-hero::before {
	background: repeating-linear-gradient(115deg,
		transparent 0 110px, rgba(240, 224, 24, .16) 110px 118px);
}

/* A section that is deliberately empty (no confirmed service-area towns yet)
   should read as a short honest note, not as a hole in the page. */
.section-note { padding-block: clamp(2rem, 4vw, 3rem); }

/* The decorative dashed centre line was pinned at top:50% — it ran straight
   through the CTA paragraph and read as strikethrough on the offer. */
.cta-band::before { top: auto; bottom: clamp(1.1rem, 3vw, 2rem); transform: none; }

/* FAQ: the raw browser disclosure triangle was the only unstyled control on the
   site. Replace it with the same yellow bar the rest of the page speaks in. */
.faq summary { list-style: none; display: flex; align-items: baseline; gap: .7rem; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::before {
	content: ""; flex: 0 0 auto;
	width: 1.15rem; height: 4px; border-radius: 2px;
	background: var(--fs-yellow);
	transform: translateY(-.25em);
	transition: transform .15s;
}
.faq details[open] summary::before { transform: translateY(-.25em) scaleX(.55); }

/* ---------- The rig ---------- */

/* Ground the stripe. It was bottom:42px against a zebra at bottom:0, so the
   paint floated 42px above the wheels. */
.rig-paint {
	bottom: 10px;
	/* Was an 18px glow, which read as a lit strip rather than wet paint. */
	box-shadow: 0 1px 0 rgba(0, 0, 0, .35);
	animation: fs-paint 15s linear infinite;
}

/* Travel distances have to match or the line never tracks the machine: drive
   ran 100vw+380px while paint ran 100vw, so the stripe drifted ahead early and
   lagged the striper by ~280px mid-cycle. The sprite's striper sits at roughly
   its right third, so the paint leads the zebra's own left edge by that much. */
.rig-zebra { transform: translateX(-100%); }

@keyframes fs-drive {
	from { transform: translateX(-100%); }
	to   { transform: translateX(100vw); }
}
@keyframes fs-paint {
	from { width: 0; }
	to   { width: calc(100vw + 6rem); }
}

@media (prefers-reduced-motion: reduce) {
	.rig-zebra { animation: none; transform: translateX(6vw); }
	.rig-paint { animation: none; width: 22vw; }
}

/* ---------- Mobile ---------- */

@media (max-width: 860px) {
	/* The header was 286px tall — 35% of a 375×812 viewport — and STICKY, because
	   the nav wrapped to three rows with no collapse pattern. One scrollable row
	   brings it back to ~120px. */
	/* header-inner must keep WRAPPING so the nav can take its own row —
	   `nowrap` here fights `flex-basis:100%` on .site-nav and forces the row
	   wider than the viewport (measured: 307px of horizontal overflow).
	   `min-width: 0` is what actually lets a flex child become a scroll
	   container; without it the nav refuses to shrink below its content. */
	.header-inner { min-height: 0; flex-wrap: wrap; }
	.brand-badge { height: 44px; }
	.site-nav { order: 3; flex: 1 0 100%; min-width: 0; max-width: 100%; }
	.nav-list {
		flex-wrap: nowrap;
		overflow-x: auto;
		-webkit-overflow-scrolling: touch;
		scrollbar-width: none;
		gap: 1.1rem;
		padding: .1rem 0 .4rem;
		max-width: 100%;
	}
	.nav-list::-webkit-scrollbar { display: none; }
	.nav-list li { flex: 0 0 auto; }

	/* Nav links were 18px-tall touch targets; iOS and WCAG 2.5.5 want 44px. */
	.nav-list a { display: block; padding: .55rem 0; }

	/* Trim the rest of the sticky bar. Wrapping (required to kill the overflow)
	   costs a second row, so the brand row has to give that height back —
	   otherwise the header eats a quarter of the screen on every scroll. */
	.header-inner { padding: .2rem 0; gap: .55rem; }
	.brand-badge { height: 38px; }
	.header-cta .btn { padding: .5rem .85rem; font-size: .88rem; }
	.btn-call-label { display: none; }
	.site-header { border-bottom-width: 2px; }
	.footer-list a, .footer-nap a, .footer-social a, .footer-legal-list a {
		display: inline-block; padding: .35rem 0;
	}
}

/* ==========================================================================
   Rig, relocated into the hero (2026-08-12).
   It previously sat in its own band ~709px down the page — below the fold and
   easy to miss entirely. Now it runs along the bottom edge of the hero, bigger,
   so it is the first thing that moves on the page.
   ========================================================================== */

.hero { padding-bottom: clamp(9rem, 15vw, 13rem); }

.rig {
	position: absolute;
	left: 0; right: 0; bottom: 0;
	height: clamp(140px, 19vw, 260px);
	overflow: hidden;
	background: none;
	border-top: 0;
	z-index: 1;
	pointer-events: none;
}
/* The band had its own asphalt fill and top border when it was a separate
   section; inside the hero it must be transparent or it cuts a seam. */
.rig::before { content: none; }

/* Asphalt shelf the zebra actually walks on, so it does not float over the
   hero's gradient. */
.rig::after {
	content: "";
	position: absolute; left: 0; right: 0; bottom: 0;
	height: clamp(34px, 5vw, 54px);
	background: linear-gradient(180deg, #0f1013 0%, #0a0b0d 100%);
	border-top: 1px solid #2a2d33;
}

.rig-zebra {
	height: clamp(120px, 17vw, 235px);
	bottom: clamp(22px, 3.4vw, 38px);
	z-index: 2;
}

.rig-paint {
	bottom: clamp(24px, 3.6vw, 40px);
	height: clamp(7px, 1vw, 11px);
	z-index: 1;
}

/* The hero's own diagonal lot-lines must not draw over the shelf. */
.hero::before { bottom: clamp(34px, 5vw, 54px); }

@media (max-width: 859px) {
	/* No badge on small screens, so the zebra is the hero's only visual and
	   deserves the room. */
	.hero { padding-bottom: clamp(8rem, 34vw, 11rem); }
}

/* ==========================================================================
   Rig geometry fix (2026-08-12).
   The rig was `height: clamp(140px,19vw,260px)` while the hero reserved
   `padding-bottom: clamp(9rem,15vw,13rem)`. Past ~1000px wide, 19vw exceeds
   15vw and the zebra climbed into the hero copy — it was covering the
   "Fast quotes…" line. Deriving BOTH from one variable makes the overlap
   impossible at any width instead of correct at some widths.
   ========================================================================== */

.hero {
	--fs-rig-h: clamp(130px, 15vw, 235px);
	padding-bottom: calc(var(--fs-rig-h) + clamp(1.5rem, 3vw, 3rem));
}
.rig       { height: var(--fs-rig-h); }
.rig-zebra { height: calc(var(--fs-rig-h) - clamp(20px, 2.6vw, 34px)); }

/* Keep the badge inside its column — it must never run under the rig or off
   the right edge. */
@media (min-width: 860px) {
	.hero-badge {
		width: min(100%, 300px);
		max-height: calc(100% - 1rem);
		object-fit: contain;
	}
}

@media (max-width: 859px) {
	.hero { --fs-rig-h: clamp(120px, 30vw, 190px); }
}

/* ==========================================================================
   Hero quote form (2026-08-12).
   Replaces the badge in the hero's right column. The badge is already in the
   header on every page; spending the most valuable block on the site on a
   second copy of the logo cost a conversion slot.
   ========================================================================== */

.hero-badge { display: none !important; }

.hero-form {
	position: relative;
	background: #fff;
	border-radius: var(--fs-radius);
	padding: clamp(1.25rem, 2.2vw, 1.75rem);
	box-shadow: 0 24px 60px rgba(0, 0, 0, .45);
	border-top: 5px solid var(--fs-yellow);
}
.hero-form-title {
	margin: 0 0 .2rem;
	font-size: 1.3rem; font-weight: 800; color: var(--fs-black);
	letter-spacing: -.01em;
}
.hero-form-sub {
	margin: 0 0 1rem;
	font-size: .92rem; color: var(--fs-slate);
}
/* The form is already inside a white card, so drop its own card chrome. */
.hero-form .lead-form {
	max-width: none;
	background: none;
	border: 0;
	padding: 0;
	box-shadow: none;
	gap: .7rem;
}
.hero-form .lead-form label { font-size: .88rem; margin-bottom: .2rem; }
.hero-form .lead-form input,
.hero-form .lead-form select,
.hero-form .lead-form textarea { padding: .6rem .7rem; }
.hero-form .lead-form textarea { min-height: 4.5rem; }

/* Give the form column real width — the badge column was deliberately narrow. */
@media (min-width: 860px) {
	.hero-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 430px); align-items: start; }
	.hero-grid > div:first-child { padding-top: clamp(.5rem, 2vw, 2rem); }
}

/* The hero sets `color:#fff` for its own copy, and that inherits straight into
   the white form card — every field label rendered white-on-white at a 1:1
   contrast ratio, i.e. invisible. The card must re-assert dark text for its
   whole subtree rather than relying on each element to set its own colour. */
.hero-form,
.hero-form .lead-form,
.hero-form .lead-form label,
.hero-form .lead-form input,
.hero-form .lead-form select,
.hero-form .lead-form textarea { color: var(--fs-ink); }
.hero-form .lead-form label { color: var(--fs-black); }
.hero-form .form-note { color: var(--fs-mute); }

/* ==========================================================================
   Rig clipping fix (2026-08-12) — the ACTUAL cause of the "cut-off hat".
   The PNG was never the problem. `.rig` is overflow:hidden (it has to be, so
   the zebra can walk in and out of frame), and the zebra's height plus its
   bottom offset came to slightly MORE than the container height — measured at
   zebraTop = -4px relative to the rig, so 4px of hat was being sliced off by
   the container. Derive the zebra's height from the container minus its own
   offset minus explicit headroom, so it can never exceed its frame.
   ========================================================================== */

.hero { --fs-rig-base: clamp(20px, 3vw, 34px); }

.rig-zebra {
	bottom: var(--fs-rig-base);
	height: calc(var(--fs-rig-h) - var(--fs-rig-base) - 14px);
}
.rig-paint { bottom: calc(var(--fs-rig-base) - 12px); }

/* ==========================================================================
   Nav condensed. Nine links plus two buttons wrapped the header onto a second
   row at 1440. The two individual service links are already one click inside
   "Services", so they come out of the top nav rather than shrinking type.
   ========================================================================== */

@media (min-width: 861px) {
	.nav-list { gap: 1.15rem; }
	.nav-list a { font-size: .92rem; }
	.header-cta .btn { padding: .62rem 1rem; font-size: .9rem; }
	.brand-badge { height: 52px; }
	.header-inner { min-height: 68px; flex-wrap: nowrap; }
}

/* ---------- Rating strip ---------- */

.rating-strip {
	display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
	margin: 0 0 1rem; font-size: .95rem; color: var(--fs-slate);
}
.rating-stars { display: inline-flex; gap: .08em; line-height: 1; font-size: 1.15rem; }
.rating-stars .star { color: var(--fs-yellow-dk); }
.rating-stars .star-empty { color: var(--fs-line); }
.rating-text strong { color: var(--fs-black); font-size: 1.02rem; }
.rating-text a { font-weight: 600; }
/* Inside the dark hero card the strip sits on white, so it inherits the card's
   dark text; on the dark proof band it needs the light treatment. */
.section-dark .rating-strip, .hero .rating-strip { color: #c7cbd2; }
.section-dark .rating-text strong, .hero .rating-text strong { color: #fff; }
.hero-form .rating-strip { color: var(--fs-slate); margin-bottom: .85rem; }
.hero-form .rating-text strong { color: var(--fs-black); }

/* ---------- Review cards (real Google reviews, verbatim) ---------- */

.review-card {
	position: relative; margin: 0;
	background: #fff; border: 1px solid var(--fs-line);
	border-radius: var(--fs-radius); padding: 1.5rem;
	display: flex; flex-direction: column; gap: .9rem;
}
.review-card::before {
	content: ""; position: absolute; top: 0; left: 1.5rem;
	width: 2.6rem; height: 5px; border-radius: 0 0 3px 3px; background: var(--fs-yellow);
}
.review-card p { margin: 0; color: var(--fs-ink); font-size: .98rem; }
.review-card cite {
	margin-top: auto; font-style: normal; font-size: .88rem; color: var(--fs-mute);
	display: flex; flex-wrap: wrap; gap: .5rem; align-items: baseline;
}
.review-card cite strong { color: var(--fs-black); font-size: .95rem; }
.review-area {
	background: var(--fs-yellow); color: var(--fs-black);
	border-radius: 999px; padding: .1rem .55rem; font-weight: 700; font-size: .78rem;
}

/* SVG stars (replaced the ★ glyph, which fonts and the deslop scanner both
   treat as an emoji). Fill is set here so the same markup works on light and
   dark grounds. */
.rating-stars .star { fill: var(--fs-yellow-dk); display: block; }
.rating-stars .star-empty { fill: var(--fs-line); }
.section-dark .rating-stars .star, .hero .rating-stars .star { fill: var(--fs-yellow); }
.hero-form .rating-stars .star { fill: var(--fs-yellow-dk); }
