/* ---------------------------------------------------------------------------
 * overrides.css -- loaded last, so it wins.
 *
 * This file was deliberately EMPTY for the whole clone phase: the rule was
 * "if a section looks wrong, ask what DOM the theme's CSS expects that we
 * haven't emitted", not "what rule can I override". That policy existed to
 * protect pixel-fidelity with the Consoul demo, and fidelity stopped being the
 * acceptance criterion when the site was rebranded to Evergreen Home Energy.
 *
 * It may now carry entries, under one condition kept from the old policy:
 * EVERY rule states what it fixes and why markup alone could not.
 * ------------------------------------------------------------------------- */


/* 0. Logo sizing -----------------------------------------------------------
 * The Consoul logo was 1868x426 (4.385:1); the Evergreen logo is 3.631:1 once
 * cropped to its ink. At the theme's `width:220px` that renders 61px tall
 * instead of 50px, and -- the actual bug -- `.logo-outer` (220px) plus
 * `.nav-outer` (961px) came to 1181px inside an `.auto-container` whose content
 * box is only 1170px after its 15px side padding. The nav floated onto a second
 * line and the header grew from 146px to 227px.
 *
 * 190px restores both: ~52px tall (near the original 50px) and 1151px total,
 * which fits. This is a property of the new logo's aspect ratio, so no markup
 * change can express it.
 */
.main-header .logo-outer .logo img {
	width: 190px;
}

.sticky-header .logo img {
	max-width: 175px;
}

/* 0b. Banner legibility ----------------------------------------------------
 * `.slide-item` has no ::before in the theme -- the demo's stock photos were
 * dark enough for white text to sit straight on them. Real solar photography
 * is shot in bright sun, so the headline and body copy lose contrast against
 * the sky. A scrim fixes it for every photo, including the client's own once
 * they supply job shots, which darkening the image files would not.
 *
 * ⚠️ STRENGTHENED, and the flat 0.48 it replaces was NOT ENOUGH. Composited
 * each of the three slide photos against the copy block's footprint in Python
 * and took the brightest pixel behind the text, per the playbook's rule for
 * glyphs. At 0.48 the worst case across the three was:
 *
 *     headline white   3.33 : 1      (needs 4.5)
 *     paragraph #e4e4e4 2.62 : 1     (needs 4.5)
 *
 * -- i.e. the hero's body copy has been failing WCAG on bright patches since
 * the rebrand. It reads acceptably most of the time because most of each photo
 * is darker than its brightest patch, which is exactly why eyeballing it
 * passed and measuring it did not.
 *
 * A GRADIENT rather than a stronger flat wash. The copy occupies the left half
 * (x 135-735 of 1425), so the scrim plateaus at ~0.70 across that half and then
 * falls to 0.25 at the right edge, where there is no text and the photograph is
 * the whole point. A flat 0.70 would have measured the same but dimmed the
 * picture everywhere.
 *
 * Worst case across all three photos with this ramp:
 *
 *     headline white    7.11 : 1
 *     paragraph #e4e4e4 5.59 : 1
 *     eyebrow #A5DFBA   4.69 : 1     (group `0n`; #8FD3A8 measured 4.08 and
 *                                     was lightened for exactly this reason)
 *
 * Mobile keeps a flat wash: the copy is full-width there, so a horizontal ramp
 * would darken one end of a line and not the other.
 */
.banner-section .slide-item {
	position: relative;
}

.banner-section .slide-item:before {
	content: "";
	position: absolute;
	top: 0;
	right: 0;
	bottom: 0;
	left: 0;
	background: rgba(9, 20, 14, 0.70);
}

@media (min-width: 768px) {
	.banner-section .slide-item:before {
		background: linear-gradient(
			90deg,
			rgba(9, 20, 14, 0.74) 0%,
			rgba(9, 20, 14, 0.70) 55%,
			rgba(9, 20, 14, 0.25) 100%
		);
	}
}

.banner-section .slide-item .auto-container {
	position: relative;
	z-index: 1;
}

/* 0c. Consult section legibility -------------------------------------------
 * Same root cause as the banner: white type over a bright daylight photo. This
 * section carries the contact form, so legibility matters more here than
 * anywhere else on the page -- the section title, the field placeholders and
 * the phone/email block all sit directly on the image. The theme ships no
 * overlay for `.consult-section`.
 */
.consult-section {
	position: relative;
}

.consult-section:before {
	content: "";
	position: absolute;
	top: 0;
	right: 0;
	bottom: 0;
	left: 0;
	background: rgba(9, 20, 14, 0.72);
}

.consult-section > .auto-container {
	position: relative;
	z-index: 1;
}

/* 0d. Banner slide heights -------------------------------------------------
 * `.slide-item` has no height of its own -- the theme sizes it from its copy
 * (`.content-box .inner` is `padding: 200px 0` plus headline, text and buttons)
 * and paints the photo as a `cover` background on that same box. Owl floats
 * `.owl-item` and clearfixes `.owl-stage`, so the stage takes the TALLEST
 * slide's height and every shorter slide's photo stops early, leaving a white
 * band -- 96px at 1425px, 21px at 375px. Slide 2 is the tall one only because
 * `.right-box` narrows it to 60% and wraps its headline onto a second line.
 *
 * Flexing the stage makes each item stretch to the stage's height instead of
 * its own content's, so the background always fills. Markup cannot fix this:
 * the heights depend on how each headline wraps at each viewport, which is
 * exactly what changes when the client's copy or photos change.
 *
 * `flex: 0 0 auto` preserves the inline widths the runtime bakes onto each
 * item (see the `bakedItems` guard in ConsoulRuntime.tsx) -- the default
 * `flex-shrink: 1` would squeeze them. The clearfix pseudo-element would
 * otherwise become a stray flex item, so it is switched off.
 */
.banner-carousel .owl-stage {
	display: flex;
	align-items: stretch;
}

.banner-carousel .owl-stage:after {
	display: none;
}

.banner-carousel .owl-item {
	flex: 0 0 auto;
}

.banner-carousel .owl-item > .slide-item {
	height: 100%;
}

/* 0e. Testimonial card heights ---------------------------------------------
 * `.client-block .inner-box` is the painted card (white fill, box-shadow, and
 * a `:before` outline offset -20/-20/+20/+20). The theme gives it no height of
 * its own, so it is sized by its quote: a 4-line quote made card 1 407px and a
 * 2-line quote made card 2 350px, leaving the shorter card's fill, shadow and
 * outline all stopping 57px early -- exactly two lines of 15px/1.9em copy.
 *
 * Bootstrap 4's `.row` is flex, so the `.client-block` COLUMNS are already
 * equal at 407/407; only the card inside them is short. Markup cannot fix
 * this: the height depends on how the copy wraps at each viewport, so any
 * hand-matched quote length would re-break at the next breakpoint or the next
 * testimonial.
 *
 * The card is flexed so the attribution row can be pushed to the bottom edge;
 * without that the reclaimed space lands below the name and the short card
 * reads as unfinished. `.inner-box:before` is `position: absolute`, so flexing
 * the card does NOT turn the outline into a stray flex item -- the trap group
 * `0d` hit with `.owl-stage:after`. Scoped to `.client-block`, per `0d`.
 */
.clients-section .client-block .inner-box {
	height: 100%;
	display: flex;
	flex-direction: column;
}

/* The trailing `.clearfix` holds `.pull-left` (name + designation) and
 * `.pull-right` (the site link). `margin-top: auto` pins it to the card's
 * bottom padding edge so both cards' name rows land on the same line. */
.clients-section .client-block .inner-box > .clearfix {
	margin-top: auto;
}

/* 0f. Call band ground -----------------------------------------------------
 * `.newsletter-section` has no background of its own -- the theme gives it
 * only `position: relative` -- so its band was plain white `<body>` showing
 * through. In the Consoul original that was invisible: the section directly
 * above it was `NewsSection`, which is ALSO transparent, so the band read as a
 * continuation of that 941px of white. `NewsSection` was deleted in the
 * rebrand (the client has no blog), which left the band orphaned between the
 * consult section's photo and the `#e6ebf0` sponsors band -- a white slab with
 * nothing above it to explain it.
 *
 * Painting the band in the sponsors grey makes one continuous ground from the
 * photo's bottom edge down to the footer, with the white card floating on it.
 * The colour is the literal value of `.sponsors-section` (style.css:8319); if
 * that band is ever recoloured, recolour this with it.
 *
 * The card's own overhang still comes from the theme and is untouched:
 * `.inner-container.margin-bottom` is `margin-bottom: -60px; z-index: 1`, and
 * `.sponsors-section` carries `padding-top: 125px` to clear it.
 *
 * The padding restores the breathing room the deleted NewsSection used to
 * supply (its `padding-bottom: 100px`). Without it the card's top edge butts
 * straight against the photo, since the section itself has no padding.
 */
.kc-css-362761 {
	background-color: #e6ebf0;
	padding-top: 70px;
}

/* 0g. Call band card balance -----------------------------------------------
 * The theme built this card for a newsletter signup: a wide email input filling
 * the 7-column form side, with an absolutely-positioned Subscribe button parked
 * in the `padding-right: 230px` that `.subscribe-form .form-group` reserves for
 * it. The rebrand replaced the whole form with a single 274px call button, so
 * the reserved strip has nothing in it and the button sits left-aligned in a
 * 630px column -- leaving 326px of dead white at the card's right edge while
 * the 5-column title side was so narrow the hours line wrapped to two.
 *
 * So the space is redistributed rather than merely relocated: right-aligning
 * the button alone would close the 326px gap but reopen it as a 356px hole in
 * the middle. Widening the title side to 8 columns lets the hours line fit on
 * one line (which also takes 19px off the card) and leaves an 86px gutter
 * between copy and button, so the card is anchored at both edges.
 *
 * Scoped to >=992px on purpose. Below that the columns are `col-md-12` and
 * stack full width, where a right-aligned button would read as a mistake; the
 * theme already zeroes the 230px reservation below 600px (responsive.css:1007)
 * and adds the stacked title's bottom margin below 767px.
 */
@media (min-width: 992px) {
	.newsletter-section .title-column {
		flex: 0 0 66.6667%;
		max-width: 66.6667%;
	}

	.newsletter-section .form-column {
		flex: 0 0 33.3333%;
		max-width: 33.3333%;
	}

	.newsletter-section .subscribe-form .form-group {
		padding-right: 0;
		text-align: right;
	}
}

/* 0h. Anchor landing offset ------------------------------------------------
 * The nav is now five on-page anchors (single-page site), and the header turns
 * into a 70px opaque `.sticky-header` as soon as the page scrolls past 55px.
 * A fragment jump aligns the target's top edge with the viewport's, so every
 * section landed with its first 65px -- its whole section title -- buried under
 * that bar. Measured on all four targets before this rule; identical each time.
 *
 * `scroll-margin-top` is the right tool: it offsets where a scroll-into-view
 * comes to rest without moving the element or adding padding that would show
 * as a gap in the layout. Markup cannot express it at all.
 *
 * 90px = the 60px the sticky bar actually occupies plus 30px of clearance, so
 * the section title lands clear of the bar rather than tight against it. If the
 * sticky header's height changes, re-measure and change this with it.
 */
#about,
#services,
#projects,
#contact {
	scroll-margin-top: 90px;
}

/* 0i. Phone CTA in the sticky header ---------------------------------------
 * The phone pill now appears in the sticky bar as well as the main header. The
 * markup is the theme's own `.outer-box > .btn-box > .theme-btn`, so it picks
 * up the existing float and pill styling, and the theme's own 1023px rule
 * (responsive.css:196) hides it there along with the sticky bar itself.
 *
 * Only the vertical metric needs changing. `.main-header .outer-box .btn-box`
 * pads 23px top and bottom, which suits the 146px main header but would make
 * the pill 44 + 46 = 90px inside a 70px sticky bar and grow the bar by 20px --
 * and the bar's height is what `scroll-margin-top` in group `0h` is set
 * against, so growing it would push every anchor landing off.
 *
 * 13px keeps the button box at exactly 70px (44px pill + 26px), matching the
 * nav's own 70px (`padding: 20px 0` on a 30px line, style.css:4083) so the bar
 * height is unchanged. The 30px gap matches the spacing the removed
 * `.option-box` used, rather than the main header's wider 50px.
 */
.sticky-header .outer-box .btn-box {
	padding: 13px 0;
	margin-left: 30px;
}

/* 0j. Mobile tap-to-call strip ---------------------------------------------
 * Below 1024px the theme hides `.header-top .value-text` AND the phone pill in
 * `.outer-box .btn-box` (responsive.css:196), which left the navy strip as 54px
 * carrying one Facebook icon -- and left the mobile header with no way to call
 * at all (measured: 0 visible `tel:` links in the header at 390px). Calling is
 * the primary action for a contractor site, so the strip carries it.
 *
 * `.top-call` is new markup in Header.tsx. It is hidden by default and shown
 * only where the value text is not, so the two never compete for the strip: the
 * desktop bar keeps its "Licensed & insured" line, the mobile bar becomes the
 * call button. The 1023px boundary is the theme's own, not a new one.
 *
 * The strip goes flex so the call sits left and the social icon right without
 * touching the theme's desktop floats. `.auto-container` carries `.clearfix`,
 * whose `:after` would become a stray flex item -- the same trap group `0d`
 * documented on `.owl-stage`.
 *
 * Padding moves from `.top-right` onto the two anchors so each is a ~48px tap
 * target (WCAG 2.5.8 asks 24px minimum; 44px is the platform convention) while
 * the strip's own height is unchanged.
 */
.header-top .top-call {
	display: none;
}

@media (max-width: 1023px) {
	.header-top .auto-container {
		display: flex;
		align-items: center;
		justify-content: space-between;
	}

	.header-top .auto-container:after {
		display: none;
	}

	/* Both sides size to their content: the default `flex-shrink: 1` squeezed
	 * the call link to 114px and wrapped its number onto three lines. */
	.header-top .top-call,
	.header-top .top-right {
		flex: 0 0 auto;
	}

	.header-top .top-call {
		display: block;
	}

	.header-top .top-call > a {
		white-space: nowrap;
	}

	/* responsive.css:724 gives `.top-right` `width: 100%; text-align: center`
	 * below 768px, for the full-width centred strip it used to be. Kept as a
	 * flex item that width became a literal 360px box running 152px past the
	 * viewport, parking the icon off-screen -- and an ancestor clips, so it read
	 * as `horizontalOverflow: 0` rather than as a scrollbar. Size it to content. */
	.header-top .top-right {
		float: none;
		padding: 0;
		width: auto;
	}

	.header-top .top-right .social-box {
		text-align: right;
	}

	.header-top .top-right .social-box li {
		margin-left: 0;
	}

	.header-top .top-call > a,
	.header-top .top-right .social-box li > a {
		display: inline-block;
		padding: 16px 0;
		color: #ffffff;
		font-size: 14px;
		font-weight: 500;
		line-height: 22px;
	}

	.header-top .top-call > a .icon {
		margin-right: 8px;
	}

	/* The Facebook glyph inherits `font-size: 14px` from the anchor and
	 * `line-height: 1` from fontawesome-all.css:1-12, so it renders 14x14 --
	 * illegible beside the 14px/22px call text on the other end of the strip.
	 * 20px matches that text's optical weight.
	 *
	 * `display: block` on the span is load-bearing, and `line-height: 22px`
	 * alone was MEASURED AND REJECTED: an inline glyph is baseline-aligned, and
	 * a 20px glyph's baseline does not sit where the 14px strut's does, so the
	 * line box grew to 24px and the strip to 56px even with both line-heights
	 * equal. A block box takes the anchor's height directly -- 22 + 16 + 16 =
	 * 54px, the strip's height unchanged. That matters beyond this strip: the
	 * mobile header's total feeds group `0k`'s hero offset.
	 *
	 * `min-width` rather than symmetric padding. The tap target has to reach
	 * 44px (WCAG 2.5.8 asks 24px; 44 is the platform convention) and it was
	 * ~14px, because the rule above pads `16px 0` -- vertical only. Padding on
	 * the right would inset the glyph from the container's 15px gutter, off the
	 * edge the call link opposite it sits flush to. `min-width` grows the box
	 * leftward instead (the box is border-box, and `.social-box`'s
	 * `text-align: right` keeps the glyph pinned to the right edge), so the
	 * icon stays optically where it is and the target grows into empty strip.
	 * Measured: anchor 44 x 54, glyph right edge at clientWidth - 15. */
	.header-top .top-right .social-box li > a {
		min-width: 44px;
	}

	.header-top .top-right .social-box li > a .icon {
		display: block;
		font-size: 20px;
		line-height: 22px;
	}
}

/* 0k. Full-screen mobile hero -----------------------------------------------
 * Nothing in the theme gives the banner a height -- it is the intrinsic height
 * of `.content-box .inner` (`padding: 120px 0`, responsive.css:77) plus the
 * headline, text and buttons. On a phone that lands short of the fold, so the
 * first screen shows a truncated hero with the next section's icon poking in
 * underneath. Diego asked for the hero to cover the screen.
 *
 * The header is in normal flow (only `.sticky-header` is fixed, and that is
 * `display: none` below 1024px, responsive.css:196), so it OCCUPIES 126px of
 * the viewport: 54px of strip (22px line-height + 16px padding twice, group
 * `0j`) plus a 72.25px logo bar -- 126.25 measured at 390px. It is emergent,
 * declared nowhere: it moves if the logo width (group `0`) or
 * `.inner-container`'s padding (responsive.css:929) changes. Hence the custom
 * property: one place to re-measure.
 *
 * The constant rounds DOWN, deliberately. Rounding up would end the hero
 * 0.75px short of the fold and show a hairline of the next section's white
 * ground; rounding down runs the hero 0.25px past it, which is invisible.
 *
 * MEASURED AND REJECTED: having ConsoulRuntime write the header's real height
 * into the property on mount and on resize. It removes the constant, but puts
 * a layout-affecting write into the file this project's two worst bugs came
 * from, for a number that has not moved in four sessions.
 *
 * `svh` (smallest viewport) rather than `vh` or `dvh`: on iOS `100vh` is the
 * viewport with the URL bar COLLAPSED, so `100vh - 126px` overflows the visible
 * area on first paint -- the exact defect being fixed. `dvh` tracks the bar and
 * would re-run the carousel's resize `layout()` mid-scroll. `svh` fills the
 * screen as loaded and never reflows. The plain `vh` line above it is the
 * fallback for anything without `svh`.
 *
 * The min-height goes on `.owl-stage` (the flex container group `0d` created),
 * NOT on `.slide-item`: `0d` ends in `.owl-item > .slide-item { height: 100% }`,
 * and that percentage only resolves because `align-items: stretch` gives the
 * item a definite height. Growing the container keeps that chain intact --
 * stage grows, items stretch, slides still resolve to 100%.
 *
 * Centring: `.slide-item` becomes a flex row so the copy sits mid-screen with
 * even photo above and below. `.auto-container` needs an explicit `width: 100%`
 * -- as a flex item its `margin: 0 auto` would otherwise shrink-wrap it. The
 * scrim (group `0b`) and the nav arrows are absolutely positioned, so they are
 * not flex items and grow/recentre on their own.
 *
 * `.inner`'s padding drops 120 -> 40 because centring now does the vertical
 * placement. Not cosmetic: at 120 the intrinsic height (~580px) exceeds a small
 * phone's available 541px (375x667), so the hero would overflow the screen on
 * exactly the devices this is meant to fit. At 40 the min-height governs on
 * every handset, and the padding's only remaining job is keeping copy off the
 * edges when a long headline outgrows the screen anyway. That rule is (0,3,0)
 * against responsive.css:75's (0,3,0) -- an order-only win, so re-check it if
 * the theme CSS is ever re-vendored.
 */
/* EXTENDED TO DESKTOP. At 1425x900 the header (146) plus the hero (724) came
 * to 870 against a 900px fold, so the visitor saw 30px of the 247px featured
 * strip: a sliver of white with the tops of three icons sheared off. Too
 * little to read as content, enough to chop the hero's bottom edge -- and the
 * exact amount is a lottery, since it is the window's height minus 870. On a
 * 1366x768 laptop the same arithmetic cuts the hero instead.
 *
 * The offset is per band because the header is three different heights, all
 * measured on the settled build:
 *
 *   >= 1024   146    54 strip + 92 logo bar (nav sits inline)
 *   768-1023  218    54 strip + 164        (responsive.css:232 gives
 *                                           `.nav-outer` width:100%, so the
 *                                           nav drops to its own row)
 *   <= 767    126    54 strip + 72 logo bar (nav is the drawer, group `0l`)
 *
 * `min-height`, not `height`, so nothing is ever clipped: on a window shorter
 * than the hero's intrinsic 724px the hero keeps its own height and simply runs
 * past the fold. That is still the desired outcome -- a hero continuing over
 * the edge reads as deliberate, a 30px sliver of the next section does not.
 *
 * ⚠️ THIS MOVES THE DESKTOP HEIGHT BASELINE. At 1440x900 the banner becomes
 * 754 (was 724) and `body.scrollHeight` 6983 (was 6953), and both now depend on
 * the window's HEIGHT as well as its width. Step 1 of the playbook has to be
 * run at a fixed 1440x900 from here on, and its table updated to match.
 */
.banner-section {
	--hero-header-offset: 146px;
}

@media (max-width: 1023px) {
	.banner-section {
		--hero-header-offset: 218px;
	}
}

@media (max-width: 767px) {
	.banner-section {
		--hero-header-offset: 126px;
	}
}

.banner-carousel .owl-stage {
	min-height: calc(100vh - var(--hero-header-offset));
	min-height: calc(100svh - var(--hero-header-offset));
}

.banner-carousel .owl-item > .slide-item {
	display: flex;
	align-items: center;
}

.banner-carousel .slide-item > .auto-container {
	width: 100%;
}

@media (max-width: 767px) {
	.banner-carousel .content-box .inner {
		padding: 40px 0;
	}
}

/* The theme's `padding: 200px 0` on `.inner` no longer does any positioning --
 * the flex centring above does that, and a centred block sits in the same place
 * whether its own padding is 200 or 100. All the padding still does is set the
 * hero's INTRINSIC height, i.e. the floor below which the min-height can no
 * longer shrink it. At 200 that floor was 724, which overshot a 900px window
 * once group `0n` grew the copy (759 intrinsic vs 754 available), and ran 170px
 * past the fold on a 1440x700 laptop, taking the CTA with it.
 *
 * 100 puts the floor at 559, so the window governs on anything taller than
 * ~705px and the hero fills it exactly. Measured: 1440x900 -> 754 (fold exact),
 * 1440x700 -> 554 (fold exact, CTA above it).
 *
 * MEASURED AND REJECTED: keeping 200 and scoping the reduction to a
 * `max-height` query. It worked, but it made the hero's height depend on a
 * breakpoint that had to be re-derived every time the copy changed -- which it
 * then did, one group later. */
@media (min-width: 768px) {
	.banner-carousel .content-box .inner {
		padding: 100px 0;
	}
}

/* 0l. Mobile navigation: sticky bar + slide-in drawer -----------------------
 * Two halves of one change.
 *
 * (a) THE STICKY BAR. `.sticky-header` is fully built and fully wired -- the
 * runtime's `setupHeader()` toggles `fixed-header` past 55px of scroll at every
 * width -- and responsive.css:196 simply switches it off below 1024px. So
 * "make the header sticky on mobile" is a display rule, not new behaviour.
 *
 * Crucially it is `position: fixed` at every width (style.css:3995), so it
 * NEVER occupies flow. Group `0k` subtracts the in-flow header (126px) from the
 * hero's `100svh`; showing this bar cannot disturb that, and the hero is
 * measured after this change to prove it.
 *
 * Its 5-item nav will not fit a phone, so the nav is hidden and a hamburger
 * takes its place. That button is deliberately NOT `.navbar-toggler`: bootstrap
 * hides that class at >=768px (`navbar-expand-md`) and this bar runs to 1023.
 *
 * Re-showing the phone pill needs to beat THREE theme rules that hide it --
 * responsive.css:40 (<=1100), :196 (<=1023) and :500 (<=767, on `.outer-box`
 * itself) -- hence the deeper `.main-header .sticky-header ...` selectors. It
 * is reduced to an icon: the number would not fit beside a logo and a
 * hamburger at 390px, which is why Header.tsx now gives that anchor an
 * `aria-label` -- hiding the `<i>` would otherwise leave it with no name.
 *
 * (b) THE DRAWER. The theme's Bootstrap collapse dropped five full-width green
 * blocks into normal flow and pushed the page down, with no way to close it.
 * `components/MobileNav.tsx` replaces it, portalled to <body> like the popup.
 *
 * z-index is explicit, not inherited from source order: this group sits BEFORE
 * group `7`, and the drawer must clear the sticky bar's own 9999
 * (style.css:4041). 10000 does that. The drawer and the popup never coexist --
 * the drawer closes as the popup opens -- so nothing here needs to out-rank
 * group `7`.
 *
 * The panel is navy `#161a33`, the top strip's colour, so the two chrome
 * surfaces read as the same material. White on it measures 15.9:1. The
 * dividers are deliberately faint: they separate rows, they do not identify a
 * control (each link's own text does that), so WCAG 1.4.11's 3:1 does not
 * apply to them and a heavier line would fight the type.
 */
.sticky-nav-toggle {
	display: none;
}

@media (max-width: 1023px) {
	.main-header .sticky-header {
		display: block;
	}

	/* Flex, so logo / phone / hamburger sit on one row without the theme's
	 * desktop floats. `.auto-container` carries `.clearfix`, whose `:after`
	 * would become a stray flex item -- the same trap groups `0d` and `0j`
	 * document on `.owl-stage` and the header strip. */
	/* 10px above and below the 44px controls = a 64px bar. Group `0h`'s
	 * `scroll-margin-top: 90px` was measured against the desktop bar's 70px;
	 * 64 keeps every anchor landing clear with 26px to spare, so that rule
	 * needs no mobile variant. Re-measure it if this number moves. */
	.main-header .sticky-header .auto-container {
		display: flex;
		align-items: center;
		gap: 10px;
		padding-top: 10px;
		padding-bottom: 10px;
	}

	.main-header .sticky-header .auto-container:after {
		display: none;
	}

	.main-header .sticky-header .logo,
	.fixed-header .sticky-header .logo {
		float: none;
		padding: 0;
		margin-right: auto;
	}

	.main-header .sticky-header .logo img {
		width: 120px;
		max-width: 120px;
	}

	/* The 5-item nav and its wrapper: no room for either on a phone. */
	.main-header .sticky-header .pull-right {
		display: none;
	}

	.main-header .sticky-header .outer-box {
		display: block;
		float: none;
	}

	/* Flex, not block: the pill is an inline-block, so a block parent gives it
	 * a line box with ~7px of descender space under it and the bar measured 51
	 * instead of 44. */
	.main-header .sticky-header .outer-box .btn-box {
		display: flex;
		float: none;
		padding: 0;
		margin: 0;
	}

	/* Icon-only pill. 7 + 30 + 7 = 44px tall, unchanged from the theme; only
	 * the number and the icon's trailing margin come off. */
	.main-header .sticky-header .outer-box .btn-box .theme-btn {
		float: none;
		width: 44px;
		padding: 7px 0;
		text-align: center;
	}

	.main-header .sticky-header .outer-box .btn-box .theme-btn i {
		display: none;
	}

	.main-header .sticky-header .outer-box .btn-box .theme-btn .icon {
		margin-right: 0;
	}

	.sticky-nav-toggle {
		display: block;
		width: 44px;
		height: 44px;
		padding: 0;
		background: transparent;
		border: 1px solid #2E7D4F;
		border-radius: 2px;
		color: #2E7D4F;
		cursor: pointer;
	}

	.sticky-nav-toggle .icon {
		font-size: 24px;
		line-height: 1;
	}

	.sticky-nav-toggle:focus-visible {
		outline: none;
		box-shadow: 0 0 0 3px rgba(46, 125, 79, 0.30);
	}
}

/* The theme's in-flow collapse panel is retired on phones. Scoped to
 * `.header-upper` and to <=767 so the desktop nav -- which bootstrap shows with
 * `display: flex !important` from >=768 -- is untouched. */
@media (max-width: 767px) {
	.main-header .header-upper .navbar-collapse {
		display: none;
	}
}

.mobile-nav {
	position: fixed;
	top: 0;
	right: 0;
	bottom: 0;
	left: 0;
	z-index: 10000;
	background: rgba(9, 20, 14, 0.55);
	opacity: 0;
	transition: opacity 300ms ease;
}

.mobile-nav.is-open {
	opacity: 1;
}

.mobile-nav-panel {
	position: absolute;
	top: 0;
	right: 0;
	display: flex;
	flex-direction: column;
	width: min(320px, 86vw);
	height: 100%;
	padding: 20px 22px calc(22px + env(safe-area-inset-bottom, 0px));
	overflow-y: auto;
	background: #161a33;
	box-shadow: -12px 0 30px rgba(0, 0, 0, 0.28);
	transform: translateX(100%);
	transition: transform 300ms ease;
}

.mobile-nav.is-open .mobile-nav-panel {
	transform: translateX(0);
}

/* The panel takes focus on open (see MobileNav.tsx) so no control's ring
 * lights up unprompted. It is a container, not a control, and the focus trap
 * keeps context, so it needs no ring of its own. */
.mobile-nav-panel:focus {
	outline: none;
}

.mobile-nav-close {
	align-self: flex-end;
	width: 44px;
	height: 44px;
	padding: 0;
	background: transparent;
	border: 0;
	color: #ffffff;
	font-size: 34px;
	line-height: 1;
	cursor: pointer;
}

.mobile-nav-close:focus-visible,
.mobile-nav a:focus-visible {
	outline: none;
	box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.45);
}

.mobile-nav-list {
	margin: 8px 0 0;
	padding: 0;
	list-style: none;
}

.mobile-nav-list li + li {
	border-top: 1px solid rgba(255, 255, 255, 0.16);
}

.mobile-nav-list a {
	display: block;
	padding: 16px 2px;
	color: #ffffff;
	font-family: "Poppins", sans-serif;
	font-size: 17px;
	font-weight: 500;
	line-height: 22px;
	text-decoration: none;
	transition: color 300ms ease;
}

.mobile-nav-list a:hover,
.mobile-nav-list a:focus {
	color: #9BD9B4;
}

.mobile-nav-actions {
	margin-top: 26px;
}

.mobile-nav-call,
.mobile-nav .mobile-nav-actions .theme-btn {
	display: block;
	width: 100%;
	padding: 13px 18px;
	border-radius: 50px;
	font-family: "Poppins", sans-serif;
	font-size: 15px;
	font-weight: 600;
	line-height: 24px;
	text-align: center;
	text-decoration: none;
}

.mobile-nav-call {
	border: 1px solid rgba(255, 255, 255, 0.45);
	color: #ffffff;
}

.mobile-nav-call .icon {
	margin-right: 10px;
}

.mobile-nav .mobile-nav-actions .theme-btn {
	margin-top: 12px;
	color: #ffffff;
}

/* Sits directly under the buttons rather than pinned to the panel's bottom
 * (`margin-top: auto`, measured and rejected): on an 844px screen that left
 * ~700px of empty navy between the CTAs and the hours, and the drawer read as
 * half-empty rather than as one block of content. */
.mobile-nav-meta {
	display: flex;
	align-items: center;
	justify-content: space-between;
	margin-top: 26px;
	padding-top: 22px;
	border-top: 1px solid rgba(255, 255, 255, 0.16);
}

.mobile-nav-hours {
	margin: 0;
	color: rgba(255, 255, 255, 0.78);
	font-size: 14px;
	line-height: 20px;
}

.mobile-nav-social {
	margin: 0;
	padding: 0;
	list-style: none;
}

.mobile-nav-social a {
	display: block;
	width: 44px;
	color: #ffffff;
	font-size: 20px;
	line-height: 44px;
	text-align: center;
	text-decoration: none;
}

@media (prefers-reduced-motion: reduce) {
	.mobile-nav,
	.mobile-nav-panel,
	.mobile-nav-list a {
		transition: none;
	}
}

/* 0m. About heading: the orphaned "the" ------------------------------------
 * On a phone the About heading read:
 *
 *     Energy-efficient homes
 *     and solar systems
 *     across the
 *     [ Rio Grande Valley ]
 *
 * -- with the green place-name badge dropped onto its own line, leaving a wide
 * hole to the right of "across the". Diego asked for the badge to sit up in
 * that hole.
 *
 * MEASURED AND REJECTED, in this order:
 *
 * 1. "It's the hard `<br/>`s." It is not. The markup carries two, but at 30px
 *    in a 360px column the text wraps at exactly the same points with the
 *    `<br/>`s removed from the DOM entirely -- byte-identical line boxes. They
 *    were a red herring.
 * 2. Shrinking the badge alone. To fit after "across the" (which ends at
 *    x=170) inside a 375px content edge, the badge has to come down to ~16px
 *    against a 30px heading. It reads as a footnote, and this badge is the
 *    client's own service area -- the emphasis of the sentence.
 *
 * What actually works is changing where the LINE breaks, not how big the badge
 * is. At 26px the sentence packs so the final line is a bare "the", leaving
 * ~300px for a 265px badge -- so it keeps its designed 24px and fits beside it:
 *
 *     Energy-efficient homes and
 *     solar systems across
 *     the [ Rio Grande Valley ]
 *
 * Both halves are required: the size sets up the short final line, and hiding
 * the `<br/>`s stops the one before the `<span>` from forcing the badge onto a
 * line of its own regardless. (The theme does the same to the banner's copy at
 * responsive.css:945.)
 *
 * ⚠️ WHY 26 AND NOT 28. Whether the badge lands beside the text depends on
 * which word happens to end the final line at a given width, so it is not a
 * property that holds continuously. Swept across content widths 300-737px:
 * 28px shares the line at 390 but NOT at 375, where it leaves a 215px hole --
 * the very defect being fixed. 26px shares at both 375 and 390, the two
 * commonest phone widths. At 412-430 it does not share, but there the text
 * fills its last line to within 55-110px of the edge.
 *
 * THE TAIL COMES OFF, which is what makes the remaining cases safe (Diego's
 * call). `span:before` is a 15px triangle hanging at `left: -14px`, and the
 * theme's `margin-left: 20px` exists to make room for it. That pairing is
 * built for a badge that always starts its own line: pointing back at the text
 * above it. Inline after "the" the tail collides with the word it follows, and
 * on its own line the 20px indent is what made the badge read as stranded
 * rather than as the sentence continuing.
 *
 * Without it the badge is a plain rectangle, and it reads correctly BOTH ways:
 * near-flush with the text's left edge when it starts a line, and clearly
 * separated when it follows "the". So the layout no longer depends on winning
 * the line-break lottery at every width -- 26px just wins it at the two
 * commonest ones.
 *
 * `margin-left: 8px`, not 0: relying on the markup's own word space was
 * measured and failed -- the space either side of the hidden `<br/>` collapses
 * away entirely and the badge rendered flush against "the", left edge exactly
 * on the word's right edge. 8px is the smallest value that separates them
 * while staying small enough to read as flush when it starts a line.
 *
 * Measured at 390: heading 172px -> ~118px, badge on the same line as "the".
 */
@media (max-width: 767px) {
	.business-section .content-column .inner-column h2 {
		font-size: 26px;
	}

	.business-section .content-column .inner-column h2 br {
		display: none;
	}

	.business-section .content-column .inner-column h2 span {
		margin-left: 8px;
	}

	.business-section .content-column .inner-column h2 span:before {
		display: none;
	}
}

/* 0n. Hero type scale -------------------------------------------------------
 * Measured at 1425 before this group: headline 80px/96, paragraph 18px/34,
 * buttons 15px. An 80:18 headline-to-body ratio is 4.4:1 -- the paragraph was
 * set at caption size under a very large headline, and because it inherits the
 * full 1170px `.auto-container` it ran ~60 characters a line even with the
 * theme's hard `<br/>`. Two extremes and nothing between them, which is why it
 * read as small and unfinished rather than as a composed block.
 *
 * The fix is a three-level hierarchy, not just a bigger paragraph:
 *
 *   eyebrow    14px, letterspaced, green      <- new, `.hero-eyebrow`
 *   headline   80 -> 68px
 *   paragraph  18 -> 24px / 38, capped at 600px (~55 characters)
 *   buttons    15 -> 16px, 50 -> 56px tall
 *
 * 68:24 is 2.8:1. The headline is still the loudest thing on the page; it just
 * stops being the only thing.
 *
 * THE HEADLINE'S GREEN DASH COMES OFF (`h2:before`, style.css:4483 -- 70x10px
 * at the bottom of its 40px padding). The eyebrow's own rule now plays that
 * part, and carrying both put two green horizontals in one block, three
 * horizontals deep. `padding-bottom` goes with it, since it existed only to
 * house the dash; the gap is re-made with `margin-bottom` so it can be tuned
 * per breakpoint.
 *
 * The eyebrow joins the theme's staged reveal rather than appearing instantly:
 * the theme runs h2 at 300ms, `.text` at 600ms and `.link-box` at 900ms off
 * `.active`, all `translateY(-50px)` + opacity. 150ms puts the eyebrow first,
 * so the block still assembles top-down.
 *
 * `.text br` is hidden from 768 up: with the measure capped the hard break
 * produced a short orphaned second line. Below 768 the theme already hides it
 * at 599 and the narrow column makes it moot.
 */
.banner-carousel .content-box .hero-eyebrow {
	position: relative;
	display: flex;
	align-items: center;
	gap: 12px;
	margin-bottom: 22px;
	color: #A5DFBA;
	font-family: "Poppins", sans-serif;
	font-size: 14px;
	font-weight: 600;
	line-height: 20px;
	letter-spacing: 2px;
	text-transform: uppercase;
	opacity: 0;
	transform: translateY(-50px);
	transition: all 1000ms ease;
}

.banner-carousel .content-box .hero-eyebrow:before {
	content: "";
	flex: 0 0 auto;
	width: 34px;
	height: 3px;
	border-radius: 3px;
	background-color: currentColor;
}

.banner-carousel .active .content-box .hero-eyebrow {
	opacity: 1;
	transform: translateY(0);
	transition-delay: 150ms;
}

.banner-carousel .content-box h2:before {
	display: none;
}

.banner-carousel .content-box h2 {
	padding-bottom: 0;
	margin-bottom: 24px;
}

@media (min-width: 768px) {
	.banner-carousel .content-box .text {
		max-width: 560px;
		font-size: 20px;
		line-height: 34px;
	}

	.banner-carousel .content-box .text br {
		display: none;
	}
}

@media (min-width: 1024px) {
	.banner-carousel .content-box h2 {
		font-size: 68px;
		margin-bottom: 26px;
	}

	.banner-carousel .content-box .text {
		max-width: 600px;
		font-size: 24px;
		line-height: 38px;
		margin-bottom: 38px;
	}

	.banner-carousel .link-box .theme-btn {
		padding: 13px 32px;
		font-size: 16px;
	}

	.banner-carousel .link-box a {
		font-size: 16px;
	}
}

@media (max-width: 767px) {
	.banner-carousel .content-box .hero-eyebrow {
		gap: 10px;
		margin-bottom: 16px;
		font-size: 12px;
		letter-spacing: 1.5px;
	}

	.banner-carousel .content-box .hero-eyebrow:before {
		width: 24px;
	}

	.banner-carousel .content-box h2 {
		margin-bottom: 18px;
	}
}

/* 1. Contact form message box ----------------------------------------------
 * The theme gives textarea a treatment that doesn't match its own inputs:
 * 125px tall with NO horizontal padding, so the text runs flush to the border,
 * and a different fill (#161a33) and border (10% white) from the inputs
 * (#191919, 30% white) sitting beside it.
 *
 * The message field now occupies its own full-width row, so it is sized to
 * invite a real answer -- the relay contract requires 10-5000 characters --
 * and its padding is aligned with the inputs above it. Group 6 supplies the
 * fill and border, so this rule only owns the box metrics.
 *
 * Markup can't fix this: the theme's own rule is what's wrong for this layout.
 */
.consult-form .form-group textarea {
	height: 132px;
	padding: 14px 20px;
	line-height: 26px;
}

/* 2. Contact form status states -------------------------------------------
 * `.form-sent` and `.form-error` are elements this site introduces -- the
 * original CF7 markup had a `.wpcf7-response-output` div that the theme never
 * styled because it was always empty in the static capture. There is no theme
 * rule to inherit.
 */
.consult-form .form-sent {
	padding: 40px 30px;
	text-align: center;
	background: rgba(255, 255, 255, 0.06);
	border: 1px solid rgba(255, 255, 255, 0.30);
	border-radius: 6px;
}

.consult-form .form-sent h3 {
	margin-bottom: 10px;
	color: #ffffff;
	font-size: 24px;
}

.consult-form .form-sent p,
.consult-form .form-sent a {
	color: #e2e8f0;
}

.consult-form .form-sent a {
	text-decoration: underline;
}

.consult-form .form-error {
	margin-top: 15px;
	padding: 12px 20px;
	color: #ffffff;
	background: rgba(220, 53, 69, 0.85);
}

/* 3. Consult section contact links -----------------------------------------
 * The phone and email in `.social-info` were plain text in the template, so
 * the theme never styled an <a> there. They are real links now and would
 * otherwise inherit the global link colour instead of the block's own.
 */
.consult-section .social-info .info a {
	color: inherit;
}

.consult-section .social-info .info a:hover {
	color: #2E7D4F;
}

/* 4. Footer contact links --------------------------------------------------
 * Same situation in the footer's contact widget: phone/email were static text
 * in the template and are now click-to-call / mailto links.
 */
.main-footer .service-list li a {
	color: inherit;
	transition: color 300ms ease;
}

.main-footer .service-list li a:hover {
	color: #2E7D4F;
}

/* 5. Font Awesome family hijack -------------------------------------------
 * KingComposer's icons.css declares
 *   [class*=" fa-"]:before { font-family:'FontAwesome'; font-weight:normal }
 * pointing at ITS OWN bundled copy of Font Awesome solid -- an older build
 * with 542 glyphs (max U+f4e3) against the theme's 915 (max U+f7e4). That
 * selector is specificity 0,1,1 ON THE PSEUDO-ELEMENT, while the theme's
 * `.fa, .fas { font-family:'Font Awesome 5 Free' }` is 0,1,0 on the element
 * and only reaches :before by inheritance -- so KingComposer wins for every
 * icon on the page regardless of stylesheet order. Reordering the cascade
 * cannot fix this; only a :before rule of equal-or-higher specificity can.
 *
 * Icons older than FA 5.0 survived (fa-bolt, fa-tint, and the header,
 * carousel and scroll-to-top chrome). Three FA5-only glyphs did not:
 * fa-solar-panel (U+f5ba), fa-car-battery (U+f5df) and fa-hammer (U+f6e3)
 * have no glyph in the older font and rendered as a zero-width nothing on
 * three of the six service cards.
 *
 * This restores the family and weight the theme intended. Matching that
 * rule's 0,1,1 and loading last is enough to win. Scoped to the literal
 * `fa`/`fas` classes, NOT `[class*="fa-"]`, so `.fab` brand icons keep
 * resolving to 'Font Awesome 5 Brands'. `.far`/`.fal` are left alone --
 * unused here, and the regular face 404s on the origin (documented benign).
 */
.fa:before,
.fas:before {
	font-family: "Font Awesome 5 Free";
	font-weight: 900;
}

/* 6. Consult form treatment ------------------------------------------------
 * The theme fills these fields with opaque #191919 and outlines them in 30%
 * white with square corners. Over the daylight rooftop photo -- already
 * darkened by the scrim in group 0c -- that reads as a row of hard black
 * boxes pasted onto the image, and nothing else on this site has square
 * corners.
 *
 * The first attempt at fixing it made the fields a flat rgba(255,255,255,0.07)
 * veil straight over the photo. Measured off a screenshot, that gave a field-
 * to-surround contrast of 1.24:1 against the 3:1 that WCAG 1.4.11 asks of a UI
 * component boundary -- because the backdrop's own luminance swings 2.2x
 * across the width of the form (0.033 to 0.072). A fixed veil cannot survive
 * that: where the roof is dark the field reads, where it is bright the field
 * matches its surround exactly and disappears.
 *
 * Darkening the fields is not the way out either. The scrimmed photo sits at
 * ~0.056 luminance, so even a pure black field tops out at 2.12:1 against it.
 * Contrast has to come from a bright border, and the border needs a stable
 * ground to sit on -- hence the panel below.
 *
 * Markup cannot do any of this: the theme's own field rules are what's wrong
 * here, and it ships no panel, label, focus-ring or reduced-motion styling.
 */

/* The panel. `.consult-form` is the theme's own wrapper (it only sets
 * `position: relative`), so this needs no new element. Blurring the backdrop
 * flattens the 2.2x swing into an even wash and gives every field and label a
 * predictable ground. Where `backdrop-filter` is unsupported the 0.78 fill
 * alone still cuts the swing to ~1.3x, so the fallback degrades sensibly.
 *
 * Padding lands the fields on a 40px inset: Bootstrap's `.row` pulls out -15px
 * and `.col-*` pads back +15px, so a field edge sits at the container padding.
 * The bottom is 25px because the last row already carries the theme's
 * `.form-group { margin-bottom: 15px }`.
 */
.consult-form {
	padding: 40px 40px 25px;
	border-radius: 10px;
	background: rgba(6, 14, 10, 0.78);
	border: 1px solid rgba(255, 255, 255, 0.10);
	-webkit-backdrop-filter: blur(18px);
	backdrop-filter: blur(18px);
}

@media only screen and (max-width: 767px) {
	.consult-form {
		padding: 24px 20px 10px;
	}
}

/* The panel does the blurring; the fields only tint. Nesting a second
 * backdrop-filter inside a backdrop-filtered parent is redundant and behaves
 * unpredictably. The 0.45 border is what actually carries the boundary --
 * ~4.4:1 against the panel -- with the fill supporting it. */
.consult-form .form-group input[type="text"],
.consult-form .form-group input[type="tel"],
.consult-form .form-group input[type="email"],
.consult-form .form-group select,
.consult-form .form-group textarea {
	background: rgba(255, 255, 255, 0.10);
	border: 1px solid rgba(255, 255, 255, 0.45);
	border-radius: 6px;
}

/* The theme's #7e8597 placeholder was tuned for the opaque black fill and
 * goes muddy on a translucent one. */
.consult-form .form-group input::placeholder,
.consult-form .form-group textarea::placeholder {
	color: rgba(255, 255, 255, 0.60);
	opacity: 1;
}

/* The theme sets a focus border on inputs and textarea but not on select, and
 * ships no focus ring anywhere -- so keyboard users get only the UA outline,
 * which is invisible against this background. */
.consult-form .form-group input[type="text"]:focus,
.consult-form .form-group input[type="tel"]:focus,
.consult-form .form-group input[type="email"]:focus,
.consult-form .form-group select:focus,
.consult-form .form-group textarea:focus {
	outline: none;
	background: rgba(255, 255, 255, 0.12);
	border-color: #2E7D4F;
	box-shadow: 0 0 0 3px rgba(46, 125, 79, 0.35);
}

/* An open <select> renders its options in the UA's own palette, which is white
 * on every platform and unreadable next to the field it drops out of. */
.consult-form .form-group select option {
	background: #14261c;
	color: #ffffff;
}

/* Placeholders were the only field identification and disappeared the moment
 * you typed. These labels are new elements, so there is no theme rule to
 * inherit. */
.consult-form .form-group label {
	display: block;
	margin-bottom: 8px;
	color: #ffffff;
	font-size: 12px;
	font-weight: 600;
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

/* `.consult-form button { border-radius: 0 }` in the theme flattens the pill
 * that `.btn-style-two` gives every other CTA on the site. Restored, sized to
 * its text rather than its column, and set at the end of the row so it reads
 * as the row's conclusion.
 *
 * The button shares its row with the service select, which carries a label
 * above it; the button does not, so it would float ~32px higher. Bootstrap 4
 * stretches columns to the tallest in the row, so aligning to the column's
 * bottom keeps the two bottom edges level whatever the label wraps to -- a
 * fixed top offset would drift the moment the label text changes. */
.consult-form .form-group.submit-group {
	display: flex;
	align-items: flex-end;
	justify-content: flex-end;
}

.consult-form .form-group button.theme-btn {
	width: auto;
	height: 60px;
	padding: 0 42px;
	border-radius: 50px;
}

.consult-form .form-group button.theme-btn:disabled {
	opacity: 0.65;
	cursor: default;
}

/* Below 768px the form's columns are `col-sm-12` and stack, so the button no
 * longer shares a row with the service select and the `flex-end` above stops
 * describing anything: it just pushed the pill against the right edge, sitting
 * 47px inside the left edge every other control lines up on (measured at 390px:
 * fields 318px wide from x36, button 271px from x83).
 *
 * Full width rather than merely left-aligned. Once the button owns a whole row
 * it is the only control on it, and matching the fields' width both restores
 * the shared left edge and makes the primary action a 318x60 tap target. This
 * is scoped to the stacked layout only -- from 768px up the button shares its
 * row again and `flex-end` is still correct. */
@media (max-width: 767px) {
	.consult-form .form-group.submit-group button.theme-btn {
		width: 100%;
	}
}

/* The theme transitions every field over 300ms. */
@media (prefers-reduced-motion: reduce) {
	.consult-form .form-group input,
	.consult-form .form-group select,
	.consult-form .form-group textarea,
	.consult-form .form-group button.theme-btn {
		transition: none;
	}
}

/* 7. Contact popup ----------------------------------------------------------
 * The primary CTAs open the contact form in a dialog instead of scrolling the
 * visitor to the bottom of the page.
 *
 * The first version reused group 6's dark glass panel unchanged, on the
 * reasoning that the two forms should look like one thing. Rejected on review:
 * group 6's palette is an answer to a problem the dialog does not have. Those
 * fields are dark because they sit on a daylight rooftop photo whose luminance
 * swings 2.2x, where contrast can only come from a bright border -- a scrimmed
 * photo at ~0.056 luminance caps even a pure black field at 2.12:1. A dialog
 * has no photo behind it. It owns its own ground, so it can simply be light,
 * and light is both easier to read and the convention for a modal.
 *
 * So everything here is scoped to `.contact-modal` and the page-bottom form is
 * deliberately untouched -- group 6 still owns that one, photo and all.
 *
 * Markup cannot do this: a dialog needs a fixed, full-viewport stacking context
 * above `.sticky-header` (z-index 999 in style.css), and the theme ships no
 * such element and no light variant of the form.
 */
.contact-modal {
	position: fixed;
	inset: 0;
	z-index: 9999;
	display: flex;
	align-items: flex-start;
	justify-content: center;
	padding: 40px 20px;
	overflow-y: auto;
	background: rgba(12, 20, 16, 0.66);
	opacity: 0;
	transition: opacity 220ms ease;
}

.contact-modal.is-open {
	opacity: 1;
}

.contact-modal-panel {
	position: relative;
	width: 100%;
	max-width: 720px;
	margin: auto;
}

/* The panel. Overrides group 6's translucent dark fill and its backdrop blur:
 * blurring is pointless over a flat dim, and it costs a compositing layer on
 * every scroll of the dialog's own overflow. */
.contact-modal .consult-form {
	padding: 34px 34px 22px;
	border-radius: 12px;
	background: #ffffff;
	border: 1px solid rgba(0, 0, 0, 0.08);
	box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35);
	-webkit-backdrop-filter: none;
	backdrop-filter: none;
}

/* The theme's section title is built for a full section: 45px of bottom margin
 * and a 36px headline. Inside a dialog that pushes the first field under the
 * fold on a laptop. */
.contact-modal .sec-title {
	margin-bottom: 22px;
	padding: 0 34px;
}

.contact-modal .sec-title h2 {
	font-size: 28px;
	line-height: 1.25em;
}

.contact-modal .sec-title .title {
	margin-bottom: 2px;
}

/* Fields, light. `#f6f8f7` rather than pure white so the field reads as an
 * inset against the panel it sits on; the border still carries the boundary.
 *
 * The border started at #9aa8a1, which *looked* right and measured 2.47:1
 * against the #ffffff panel -- under the 3:1 WCAG 1.4.11 asks of a UI
 * component boundary. On a white ground a border has nowhere near the headroom
 * it has on a dark one, so it has to go darker than it looks like it should.
 * #7d8c85 measures 3.52:1.
 *
 * Measured, all against their own ground: border 3.52:1, text #1c2622 on the
 * fill 14.59:1, placeholder 5.25:1, label 9.34:1, heading 15.91:1, the green
 * eyebrow and the submit's white-on-#2E7D4F both 5.05:1. */
.contact-modal .consult-form .form-group input[type="text"],
.contact-modal .consult-form .form-group input[type="tel"],
.contact-modal .consult-form .form-group input[type="email"],
.contact-modal .consult-form .form-group select,
.contact-modal .consult-form .form-group textarea {
	background: #f6f8f7;
	border: 1px solid #7d8c85;
	border-radius: 6px;
	color: #1c2622;
}

/* #667771 was 4.43:1 on the field fill -- just under the 4.5:1 for text. */
.contact-modal .consult-form .form-group input::placeholder,
.contact-modal .consult-form .form-group textarea::placeholder {
	color: #5c6b66;
	opacity: 1;
}

.contact-modal .consult-form .form-group input[type="text"]:focus,
.contact-modal .consult-form .form-group input[type="tel"]:focus,
.contact-modal .consult-form .form-group input[type="email"]:focus,
.contact-modal .consult-form .form-group select:focus,
.contact-modal .consult-form .form-group textarea:focus {
	outline: none;
	background: #ffffff;
	border-color: #2E7D4F;
	box-shadow: 0 0 0 3px rgba(46, 125, 79, 0.30);
}

/* Group 6 paints the dropdown's options dark to match its dark field. */
.contact-modal .consult-form .form-group select option {
	background: #ffffff;
	color: #1c2622;
}

/* Group 6's labels are white for the photo panel. #3b4a44 on #ffffff is
 * 9.1:1. */
.contact-modal .consult-form .form-group label {
	color: #3b4a44;
}

.contact-modal .consult-form .form-sent h3,
.contact-modal .consult-form .form-sent p {
	color: #1c2622;
}

.contact-modal .consult-form .form-error {
	color: #a8281f;
}

/* Written as `.contact-modal .consult-form button.contact-modal-close` (0,3,1)
 * on purpose. The theme's `.consult-form button` (0,1,1) sets `width: 100%`
 * and `padding: 14px 10px` for the submit button, and it outranks a lone
 * `.contact-modal-close` (0,1,0) no matter how late this file loads -- the
 * close button rendered 364px wide across the whole panel until this was
 * raised. Same shape as the KingComposer icon hijack in group 5: source order
 * does not settle a specificity fight. */
.contact-modal .consult-form button.contact-modal-close {
	position: absolute;
	top: 12px;
	right: 12px;
	z-index: 2;
	width: 40px;
	height: 40px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: transparent;
	color: #55635d;
	font-size: 30px;
	line-height: 1;
	cursor: pointer;
	transition: background 220ms ease, color 220ms ease;
}

.contact-modal .consult-form button.contact-modal-close:hover,
.contact-modal .consult-form button.contact-modal-close:focus-visible {
	background: rgba(46, 125, 79, 0.12);
	color: #1c2622;
}

.contact-modal .consult-form button.contact-modal-close:focus-visible {
	outline: 2px solid #2E7D4F;
	outline-offset: 2px;
}

/* --- Mobile: fit the whole form on one phone screen ------------------------
 * Stacked at the theme's metrics the panel measured 915px against an 844px
 * viewport, so the submit button sat below the fold and the dialog had to be
 * scrolled to be used at all -- the first thing Diego flagged.
 *
 * Three compressions, in order of how much they buy: pair the two name fields
 * onto one row (they hold short values, so halving them costs nothing), take
 * the field height from the theme's 60px to 50px, and shorten the textarea
 * from group 1's 132px to 96px -- still four lines at this line-height.
 *
 * Email and phone are deliberately NOT paired. A 160px email field on a 390px
 * screen is where typos happen, and the two rows saved were not needed once
 * the rest came down. */
@media only screen and (max-width: 767px) {
	.contact-modal {
		padding: 16px 12px;
	}

	.contact-modal .consult-form {
		padding: 22px 16px 6px;
	}

	.contact-modal .sec-title {
		margin-bottom: 16px;
		padding: 0 28px;
	}

	.contact-modal .sec-title h2 {
		font-size: 22px;
	}

	.contact-modal .sec-title .title {
		font-size: 12px;
		line-height: 20px;
	}

	/* First name + last name share a row. Bootstrap's `col-sm-12` sets
	 * `flex: 0 0 100%`, so both halves of it have to be overridden. */
	.contact-modal .consult-form .form-group:nth-child(1),
	.contact-modal .consult-form .form-group:nth-child(2) {
		flex: 0 0 50%;
		max-width: 50%;
	}

	.contact-modal .consult-form .form-group {
		margin-bottom: 10px;
	}

	.contact-modal .consult-form .form-group input[type="text"],
	.contact-modal .consult-form .form-group input[type="tel"],
	.contact-modal .consult-form .form-group input[type="email"],
	.contact-modal .consult-form .form-group select {
		height: 50px;
		padding: 8px 14px;
		line-height: 32px;
	}

	.contact-modal .consult-form .form-group textarea {
		height: 96px;
		padding: 10px 14px;
		line-height: 24px;
	}

	.contact-modal .consult-form .form-group label {
		margin-bottom: 5px;
		font-size: 11px;
	}

	/* Still a full-width tap target, just less tall than the theme's 60px. */
	.contact-modal .consult-form .form-group.submit-group button.theme-btn {
		height: 52px;
	}
}

@media (prefers-reduced-motion: reduce) {
	.contact-modal,
	.contact-modal-close {
		transition: none;
	}
}

/* 8. Project cards on touch devices -----------------------------------------
 * The seven project cards hide their title and their "Get a Quote" link inside
 * `.overlay-box`, which the theme reveals only on `:hover`
 * (style.css:6794). A touch screen has no hover, so on a phone all 14 of those
 * links were unreachable -- a quarter of every contact link on the page, and
 * the whole of the "recent work" section, silently inert.
 *
 * Worth knowing how this hides from a check: the resting state is
 * `transform: perspective(400px) rotateY(90deg)`, i.e. rotated edge-on, so the
 * links report a width of exactly 0 while still being present, visible to
 * `querySelector`, and clickable via `element.click()`. Only a hit test
 * (`elementFromPoint`) or a real tap shows the problem.
 *
 * `(hover: none)` is the precise condition -- the overlay is a hover
 * affordance, so it should be permanent exactly where hover does not exist.
 * Keyed to the input device rather than to a width, so a small laptop window
 * keeps the hover reveal and a large tablet still gets the static bar.
 *
 * ⚠️ CORRECTION (this comment was wrong for one session). It claimed
 * `.overlay-inner`'s green fill "lays a green caption bar across the bottom of
 * the card and leaves the photograph above it visible -- it is not a full-card
 * cover." Measured at 390: `.overlay-box` is the full 458px of the card and
 * carries `background-color: rgba(22,26,51,0.90)` (style.css:6760); only its
 * bottom 164px is the green `.overlay-inner`. So revealing it DOES cover the
 * whole card, and the photograph sat under a 90% navy wash on every phone --
 * which is what Diego saw.
 *
 * That 90% is right for the desktop behaviour it was written for: there the
 * overlay swings in on hover as a deliberate "cover the photo, show the
 * details" flip, and it is gone the moment the pointer leaves. Made permanent
 * it is no longer a transition, it is just the card's resting state -- and a
 * projects section whose whole job is showing the work should show the work.
 *
 * 0.25 keeps a tint that ties the photo to the green caption bar below it
 * without hiding it. Nothing depends on the darker value for legibility: all
 * three pieces of text (category, title, "Get a Quote") sit inside
 * `.overlay-inner`, on its opaque #2E7D4F, not on the wash.
 *
 * Markup cannot fix this: the reveal lives entirely in the theme's own
 * `:hover` rule.
 */
@media (hover: none) {
	.project-block .inner-box .image .overlay-box {
		opacity: 1;
		-webkit-transform: none;
		-moz-transform: none;
		-ms-transform: none;
		transform: none;
		background-color: rgba(22, 26, 51, 0.25);
	}
}

/* The same defect in a second shape. `.feature-block-two .plus-box` is the
 * round arrow button under each of the three cards in Featured Two. It rests at
 * `transform: scale(0,0)` and is grown to `scale(1,1)` by
 * `.inner-box:hover` (style.css:12507) -- so on touch it is a 60x60 button that
 * computes 60px wide and measures 0x0, and three more `#contact` links that
 * cannot be tapped.
 *
 * Found by sweeping for links whose computed width is non-zero while their
 * bounding rect is 0 -- the signature of a transform-collapsed control, and the
 * check that catches this class of bug in general.
 */
@media (hover: none) {
	.feature-block-two .inner-box .plus-box {
		-webkit-transform: scale(1, 1);
		-moz-transform: scale(1, 1);
		-ms-transform: scale(1, 1);
		transform: scale(1, 1);
	}
}

/* Revealing `.plus-box` was necessary but not sufficient. It is positioned
 * `bottom: -30px`, so half the button hangs outside its own card. On desktop
 * the three cards sit side by side and it hangs into empty space; stacked on a
 * phone it lands on top of the NEXT card, and the third one lands on the
 * section below.
 *
 * `.inner-box` carries a WOW animation, and an animated transform creates a
 * stacking context -- so the button's own `z-index: 1` is trapped inside its
 * card and cannot lift it above a later sibling. Later positioned siblings
 * paint on top by DOM order, so the order has to be reversed card by card, and
 * the section wrapper lifted above the one that follows it.
 *
 * Only reachable-on-touch is at stake here, so it stays inside the same query:
 * on a hover device the button is invisible at rest and overlaps nothing.
 */
@media (hover: none) {
	.kc-elm.kc-css-761980 {
		position: relative;
		z-index: 1;
	}

	.featured-section-two .feature-block-two:nth-child(1) {
		z-index: 3;
	}

	.featured-section-two .feature-block-two:nth-child(2) {
		z-index: 2;
	}

	.featured-section-two .feature-block-two:nth-child(3) {
		z-index: 1;
	}
}
