/*
Theme Name: Bactronix
Theme URI: https://bactronixsc.com/
Author: Secret Squirrel Marketing
Author URI: https://secretsquirrel.marketing/
Description: Full site editing block theme for Bactronix of the Lowcountry (HVAC mold treatment and air duct cleaning, Charleston SC). This theme PORTS the client approved look of the existing site rather than redesigning it: brand red bands, flat service cards with a short red rule, and the measured type ladder. All design tokens live in theme.json. Two self hosted OFL families (Poppins for headings, Open Sans for body) ship as WOFF2, so no third party font CDN is contacted. The theme carries no business facts; phone numbers, addresses and service copy come from the companion plugin.
Requires at least: 6.5
Tested up to: 6.8
Requires PHP: 8.0
Version: 0.1.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: bactronix-theme
Tags: full-site-editing, block-patterns, block-styles, custom-colors, custom-menu, editor-style, one-column, wide-blocks
*/

/*
  SOURCE OF THE LOOK
  ------------------
  Ported from design/reference/ (captured 2026-09-01 from the live Divi site).
  Every value here traces to design/reference/tokens.md or to a pixel measurement
  taken off the captured desktop screenshots. Where a value was measured rather
  than cited, the comment says "measured". Where a value was derived because the
  reference did not carry one, the comment says "derived" and THEME-NOTES.md
  records it.

  CLASS NAMING
  ------------
  Every class authored here is bx-*. Not one Divi builder class name survives
  the port; the shipped CSS, PHP, JS and JSON contain zero of them. The bx-*
  prefix continues the convention already used by the site's existing custom
  snippet (.bx-bar-btn, #bx-lead), whose styles are ported near the bottom of
  this file.

  PALETTE (theme.json is the source of truth)
    brand red    #BB2B1E    brand coral #BB2B1E    amber #FCBF04
    accent grey  #969696    heading ink #333333    body  #666666
    link blue    #1878B9    mist        #F3F3F3    hairline #E2E2E2

  No em dashes anywhere per house style.
*/

/* ------------------------------------------------------------------ *
 * 1 . TOKEN INDIRECTION
 * theme.json cannot hold CSS variables inside fontFamily strings, so the
 * same stacks are restated here for the rules below. Swapping a family
 * later means editing theme.json fontFamilies and these two values; no
 * markup and no class name changes.
 * ------------------------------------------------------------------ */

:root {
	--bx-ff-heading: var(--wp--preset--font-family--bx-heading, 'Poppins', 'Trebuchet MS', system-ui, sans-serif);
	--bx-ff-body: var(--wp--preset--font-family--bx-body, 'Open Sans', Arial, Helvetica, sans-serif);

	--bx-red: var(--wp--preset--color--bx-red, #BB2B1E);
	--bx-red-dark: var(--wp--preset--color--bx-red-dark, #A8271B);
	--bx-coral: var(--wp--preset--color--bx-coral, #BB2B1E);
	--bx-ink: var(--wp--preset--color--bx-ink, #333333);
	--bx-body-color: var(--wp--preset--color--bx-body, #666666);
	--bx-white: var(--wp--preset--color--bx-white, #FFFFFF);
	--bx-hairline: var(--wp--preset--color--bx-hairline, #E2E2E2);
	--bx-mist: var(--wp--preset--color--bx-mist, #F3F3F3);
	/* Cited table furniture: 1px #eee frame and rules, #555 header text. */
	--bx-table-line: #EEEEEE;
	--bx-table-head: #555555;

	/* Section padding, cited: 4% 0 above 981px, 50px 0 below, 56px flex default. */
	--bx-section-pad: clamp(3.125rem, 4vw, 3.5rem);
	/* Short red rule above a card heading, measured 51px by 4px.
	   Values live once in theme.json settings.custom.rule; this just aliases
	   the generated custom property, with the same literal as a fallback. */
	--bx-rule-w: var(--wp--custom--rule--width, 51px);
	--bx-rule-h: var(--wp--custom--rule--height, 4px);
	/* Header bar height, measured 79px on the captured 1383px desktop shot.
	   Sourced from theme.json settings.custom.nav.height. */
	--bx-nav-height: var(--wp--custom--nav--height, 79px);
	/* Radii sourced from theme.json settings.custom.radius. */
	--bx-radius-sm: var(--wp--custom--radius--sm, 3px);
	--bx-radius-pill: var(--wp--custom--radius--pill, 100px);
}

/* ------------------------------------------------------------------ *
 * 2 . BASE
 * ------------------------------------------------------------------ */

body {
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	text-rendering: optimizeLegibility;
}

/* Cited: h1 to h6 carry padding-bottom 10px on the reference site. Kept as a
   margin so block gap arithmetic stays predictable in the editor. */
:where(h1, h2, h3, h4, h5, h6) {
	margin-block-end: 0.625rem;
}

/* Cited: .entry-content ul {list-style-type:disc; padding:0 0 23px 1em;
   line-height:26px}. Ported at the theme's own body line height so the list
   tracks the surrounding prose. */
.wp-block-post-content ul,
.wp-block-post-content ol,
.entry-content ul,
.entry-content ol {
	padding-inline-start: 1.25em;
	line-height: 1.7;
}

.wp-block-post-content ul,
.entry-content ul {
	list-style-type: disc;
}

.wp-block-post-content ol,
.entry-content ol {
	list-style-type: decimal;
}

.wp-block-post-content li,
.entry-content li {
	margin-block-end: 0.35em;
}

/* Cited table treatment: table{border:1px solid #eee;width:100%;text-align:left},
   th{color:#555;font-weight:700;padding:9px 24px}, td{border-top:1px solid #eee;
   padding:6px 24px}. Ported exactly, including the #eee frame colour, which is
   a different grey from the site's 1px divider hairline. */
.wp-block-table table {
	width: 100%;
	border: 1px solid var(--bx-table-line);
	border-collapse: collapse;
	text-align: left;
}

.wp-block-table th {
	color: var(--bx-table-head);
	font-weight: 700;
	padding: 9px 24px;
	background: transparent;
	border: 0;
	border-top: 1px solid var(--bx-table-line);
}

.wp-block-table td {
	border: 0;
	border-top: 1px solid var(--bx-table-line);
	padding: 6px 24px;
}

.wp-block-table figcaption {
	color: var(--bx-body-color);
}

/* Narrow screens: give a table a floor width and let its own wrapper scroll,
   rather than letting three columns squeeze until words break mid syllable.
   Not a port; the reference has no data tables. */
@media (max-width: 600px) {
	.wp-block-table table {
		min-width: 480px;
	}
}

/* ------------------------------------------------------------------ *
 * 3 . SECTIONS AND THE RED BAND
 * The reference has exactly two full width section grounds: white and the
 * brand red band. There is no alternating light grey section anywhere in
 * the eight captured pages (tokens.md, "Background greys").
 * ------------------------------------------------------------------ */

.bx-section {
	padding-block: var(--bx-section-pad);
}

/* The band. Cited ground #BB2B1E, live getComputedStyle padding 50px, white
   heading. Everything inside it flips to white, including list markers, table
   rules and the inline link colour, so a band never needs per block colour
   settings in the editor. */
.bx-band,
.is-style-bx-band {
	background-color: var(--bx-red);
	color: var(--bx-white);
	padding-block: var(--bx-section-pad);
}

.bx-band :where(h1, h2, h3, h4, h5, h6),
.is-style-bx-band :where(h1, h2, h3, h4, h5, h6),
.bx-band :where(p, li, td, th, dd, dt, figcaption, cite),
.is-style-bx-band :where(p, li, td, th, dd, dt, figcaption, cite) {
	color: var(--bx-white);
}

.bx-band :where(a):not(.wp-element-button):not(.wp-block-button__link),
.is-style-bx-band :where(a):not(.wp-element-button):not(.wp-block-button__link) {
	color: var(--bx-white);
	text-decoration-color: rgba(255, 255, 255, 0.6);
}

.bx-band :where(a):not(.wp-element-button):not(.wp-block-button__link):hover,
.is-style-bx-band :where(a):not(.wp-element-button):not(.wp-block-button__link):hover {
	color: var(--bx-white);
	text-decoration-color: var(--bx-white);
}

.bx-band .wp-block-table table,
.is-style-bx-band .wp-block-table table,
.bx-band .wp-block-table th,
.is-style-bx-band .wp-block-table th,
.bx-band .wp-block-table td,
.is-style-bx-band .wp-block-table td {
	border-color: rgba(255, 255, 255, 0.28);
}

.bx-band .wp-block-table th,
.is-style-bx-band .wp-block-table th {
	background: transparent;
	color: var(--bx-white);
}

/* Cited: the one divider instance inside the footer band uses a 2px rule at
   rgba(255,255,255,0.2). */
.bx-band hr.wp-block-separator:not(.is-style-dots),
.is-style-bx-band hr.wp-block-separator:not(.is-style-dots),
.bx-band hr:not([class]),
.is-style-bx-band hr:not([class]) {
	border: 0;
	border-top: 2px solid rgba(255, 255, 255, 0.2);
	opacity: 1;
	width: 100%;
	margin-block: var(--wp--preset--spacing--bx-lg, 2rem);
}

/* The band CTA heading. Measured on the captured footer band: Poppins, cap
   height 36px giving a 52px face, line height 1.1, white. */
.bx-band .bx-band-title,
.is-style-bx-band .bx-band-title {
	font-family: var(--bx-ff-heading);
	font-size: var(--wp--preset--font-size--bx-title, clamp(1.875rem, 1.06rem + 3.48vw, 3.25rem));
	font-weight: 600;
	line-height: 1.1;
	color: var(--bx-white);
}

/* ------------------------------------------------------------------ *
 * 4 . CARDS
 * The service cards render flat in every captured screenshot: white ground,
 * a short red rule above the heading, no elevation. tokens.md confirms zero
 * box-shadow on any card like module in all eight captured CSS files.
 * ------------------------------------------------------------------ */

.bx-card,
.is-style-bx-card {
	background: var(--bx-white);
	box-shadow: none;
	border: 0;
}

/* The short red rule. Measured 51px wide by 4px tall, brand red, sitting one
   line above the card heading. Usable on a heading block directly (the
   "Red rule" block style) or on a card wrapper. */
.bx-rule::before,
.is-style-bx-ruled::before {
	content: "";
	display: block;
	width: var(--bx-rule-w);
	height: var(--bx-rule-h);
	background: var(--bx-red);
	margin-block-end: 1.75rem;
}

.bx-band .bx-rule::before,
.is-style-bx-band .bx-rule::before,
.bx-band .is-style-bx-ruled::before,
.is-style-bx-band .is-style-bx-ruled::before {
	background: var(--bx-white);
}

/* Centred variant for a section heading that sits over a centred column. */
.has-text-align-center.is-style-bx-ruled::before {
	margin-inline: auto;
}

/* The inline lead panel is the one card shaped element in the reference that
   carries elevation. Cited verbatim. */
.bx-panel {
	max-width: 560px;
	padding: 24px;
	border: 1px solid #e5e5e5;
	border-radius: 8px;
	background: var(--bx-white);
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
	text-align: left;
}

/* ------------------------------------------------------------------ *
 * 5 . BUTTONS
 * Four treatments exist on the reference site. The default core button takes
 * the solid brand red rectangle seen on every service card; the other three
 * ship as block style variations registered in functions.php.
 *
 * Hover is DERIVED, not cited. The reference captures two hover colours on the
 * pill buttons (#236299 blue and #27d8d2 teal), which are unedited Divi demo
 * defaults and clearly off brand, so they are not ported. The hover used here
 * is the brand red darkened 10 percent: #BB2B1E to #A8271B. See THEME-NOTES.md.
 * ------------------------------------------------------------------ */

.wp-block-button__link,
.wp-element-button {
	transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
	text-decoration: none;
}

/* Accessibility addition, not a port: the reference buttons measure 36px tall,
   below the 44px target guideline. The extra height is applied only on coarse
   pointers so the desktop rendering still matches the reference pixel for
   pixel. */
@media (pointer: coarse) {
	.wp-block-button__link,
	.wp-element-button {
		min-height: 44px;
		display: inline-flex;
		align-items: center;
		justify-content: center;
	}
}

/* Brand pill. Cited: 100px radius on all corners, 600 weight, 13px, 1px
   letter spacing, uppercase, brand red ground, white text, zero border. The
   cited font is Libre Franklin, which this theme does not ship; the cited
   fallback stack for the same component is 'Libre Franklin','Open Sans',
   sans-serif, so Open Sans is the licensed stand in. */
.wp-block-button.is-style-bx-pill .wp-block-button__link,
.bx-btn-pill {
	background-color: var(--bx-red);
	color: var(--bx-white);
	font-family: var(--bx-ff-body);
	font-size: 0.8125rem;
	font-weight: 600;
	letter-spacing: 1px;
	text-transform: uppercase;
	border: 0;
	border-radius: var(--bx-radius-pill);
	padding: 14px 26px;
}

.wp-block-button.is-style-bx-pill .wp-block-button__link:hover,
.wp-block-button.is-style-bx-pill .wp-block-button__link:focus,
.bx-btn-pill:hover,
.bx-btn-pill:focus {
	background-color: var(--bx-red-dark);
	color: var(--bx-white);
}

/* Secondary coral. Cited as the secondary brand button ground #CA564D, but
   that ground under white 600 weight text at 16px is 4.24:1, which fails
   WCAG AA (4.5:1 required below large text). Shipped instead at the brand
   red #BB2B1E, 6.04:1. See THEME-NOTES.md for the ratio and the rejected
   alternative. */
.wp-block-button.is-style-bx-secondary .wp-block-button__link {
	background-color: var(--bx-coral);
	color: var(--bx-white);
	border: 0;
	border-radius: var(--bx-radius-sm);
}

.wp-block-button.is-style-bx-secondary .wp-block-button__link:hover,
.wp-block-button.is-style-bx-secondary .wp-block-button__link:focus {
	background-color: var(--bx-red-dark);
	color: var(--bx-white);
}

/* On band. The one button in the reference red band: white ground, black text,
   square corners, 16px by 24px padding, 700 weight, 14px. Measured box 137px
   by 55px, which this reproduces. */
.wp-block-button.is-style-bx-on-band .wp-block-button__link {
	background-color: var(--bx-white);
	color: #000000;
	font-family: var(--bx-ff-body);
	font-size: 0.875rem;
	font-weight: 700;
	border: 0;
	border-radius: 0;
	padding: 16px 24px;
	text-transform: uppercase;
}

.wp-block-button.is-style-bx-on-band .wp-block-button__link:hover,
.wp-block-button.is-style-bx-on-band .wp-block-button__link:focus {
	background-color: var(--bx-mist);
	color: #000000;
}

/* Outline. Cited base Divi button treatment: transparent ground, 2px border,
   3px radius, brand red text. */
.wp-block-button.is-style-bx-outline .wp-block-button__link {
	background-color: transparent;
	color: var(--bx-red);
	border: 2px solid var(--bx-red);
	border-radius: var(--bx-radius-sm);
}

.wp-block-button.is-style-bx-outline .wp-block-button__link:hover,
.wp-block-button.is-style-bx-outline .wp-block-button__link:focus {
	background-color: var(--bx-red);
	color: var(--bx-white);
}

.bx-band .wp-block-button.is-style-bx-outline .wp-block-button__link,
.is-style-bx-band .wp-block-button.is-style-bx-outline .wp-block-button__link {
	color: var(--bx-white);
	border-color: var(--bx-white);
}

.bx-band .wp-block-button.is-style-bx-outline .wp-block-button__link:hover,
.bx-band .wp-block-button.is-style-bx-outline .wp-block-button__link:focus,
.is-style-bx-band .wp-block-button.is-style-bx-outline .wp-block-button__link:hover,
.is-style-bx-band .wp-block-button.is-style-bx-outline .wp-block-button__link:focus {
	background-color: var(--bx-white);
	color: var(--bx-red);
}

/* ------------------------------------------------------------------ *
 * 6 . HEADER AND NAVIGATION ADAPTERS
 * Templates and parts land in a later task. These are the measured chrome
 * values so the header part inherits them rather than restating numbers.
 * ------------------------------------------------------------------ */

.bx-header {
	background: var(--bx-white);
	min-height: var(--bx-nav-height);
}

.bx-header .wp-block-navigation a:where(:not(.wp-element-button)) {
	text-decoration: none;
}

/* The nav is a native details element, server rendered open, so every link is
   reachable with JavaScript off. assets/js/nav.js collapses it below 980px,
   which is the reference site's own tablet breakpoint. Above that the toggle
   is hidden and the menu simply sits in the bar.

   The element is the core details block (parts/header.html), whose summary
   carries no class of its own, so the toggle is addressed structurally as
   well as by class. */
.bx-navtoggle,
.bx-navwrap > summary {
	display: none;
	cursor: pointer;
	list-style: none;
	font-family: var(--bx-ff-body);
	font-size: 0.875rem;
	font-weight: 600;
	color: var(--bx-ink);
	padding: 0.5rem 0;
	min-height: 44px;
	align-items: center;
}

.bx-navtoggle::-webkit-details-marker,
.bx-navwrap > summary::-webkit-details-marker {
	display: none;
}

@media (max-width: 980px) {
	.bx-navtoggle,
	.bx-navwrap > summary {
		display: flex;
	}
}

.bx-header .wp-block-navigation .current-menu-item a,
.bx-header .wp-block-navigation [aria-current="page"] {
	color: var(--wp--preset--color--bx-link, #1878B9);
}

/* ------------------------------------------------------------------ *
 * 8 . EXISTING CUSTOM COMPONENTS, PORTED VERBATIM
 * The live site already carries a hand written lead form and a sticky
 * mobile CTA bar under bx-* class names (home.css lines 75 to 83). Their
 * markup will be emitted by the companion plugin, so the theme owns only
 * how they look. Values below are cited, not redrawn, except that the
 * Libre Franklin reference resolves to the theme's Open Sans stack and the
 * brand red hover is the derived #A8271B.
 * ------------------------------------------------------------------ */

.bx-bar {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 99998;
	display: flex;
	gap: 10px;
	padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
	background: var(--bx-white);
	box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.12);
}

.bx-bar-btn {
	flex: 1;
	text-align: center;
	padding: 14px 10px;
	border-radius: var(--bx-radius-pill);
	font-family: var(--bx-ff-body);
	font-size: 0.8125rem;
	font-weight: 600;
	letter-spacing: 1px;
	text-transform: uppercase;
	text-decoration: none;
	border: 1px solid var(--bx-red);
}

.bx-bar-primary {
	background: var(--bx-red);
	color: var(--bx-white);
}

.bx-bar-secondary {
	background: var(--bx-white);
	color: var(--bx-red);
}

.bx-lead {
	max-width: 560px;
	margin: 18px 0 0;
	padding: 24px;
	border: 1px solid #e5e5e5;
	border-radius: 8px;
	background: var(--bx-white);
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
	font-family: var(--bx-ff-body);
	text-align: left;
}

.bx-lead h3 {
	margin: 0 0 6px;
	font-family: var(--bx-ff-heading);
	font-size: 1.25rem;
	font-weight: 600;
	color: var(--bx-ink);
}

.bx-lead p {
	margin: 0 0 14px;
	font-size: 0.9375rem;
	color: var(--bx-body-color);
	line-height: 1.5;
}

.bx-lead label {
	display: block;
	font-size: 0.875rem;
	font-weight: 600;
	color: var(--bx-ink);
	margin: 10px 0 4px;
}

.bx-lead input[type="text"],
.bx-lead input[type="tel"],
.bx-lead input[type="email"],
.bx-lead input[type="number"] {
	width: 100%;
	padding: 10px 12px;
	border: 1px solid #cccccc;
	border-radius: 4px;
	font-size: 1rem;
	color: var(--bx-ink);
	background: var(--bx-white);
	box-sizing: border-box;
}

.bx-lead button {
	width: 100%;
	margin-top: 14px;
	padding: 14px;
	border: none;
	border-radius: var(--bx-radius-pill);
	background: var(--bx-red);
	color: var(--bx-white);
	font-family: var(--bx-ff-body);
	font-size: 0.8125rem;
	font-weight: 600;
	letter-spacing: 1px;
	text-transform: uppercase;
	cursor: pointer;
}

.bx-lead button:hover,
.bx-lead button:focus {
	background: var(--bx-red-dark);
}

.bx-lead .bx-hp {
	position: absolute;
	left: -5000px;
	top: -5000px;
}

.bx-lead-ok {
	margin-top: 10px;
	padding: 14px;
	border-left: 4px solid var(--bx-red);
	background: #F9F9F9;
	color: var(--bx-ink);
	font-size: 0.9375rem;
}

/* ------------------------------------------------------------------ *
 * 9 . ACCESSIBILITY BASE
 * Kept from the house block theme baseline. None of this alters the ported
 * visual; it only makes the port usable by keyboard and by readers who ask
 * for less motion.
 * ------------------------------------------------------------------ */

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
	outline: 2px solid var(--bx-red);
	outline-offset: 2px;
	border-radius: 2px;
}

.bx-band :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible,
.is-style-bx-band :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
	outline-color: var(--bx-white);
}

.screen-reader-text {
	border: 0;
	clip-path: inset(50%);
	height: 1px;
	margin: -1px;
	overflow: hidden;
	padding: 0;
	position: absolute;
	width: 1px;
	word-wrap: normal !important;
}

.bx-skip-link:focus {
	position: fixed;
	top: 0.5rem;
	left: 0.5rem;
	z-index: 100000;
	padding: 0.75rem 1rem;
	background: var(--bx-red);
	color: var(--bx-white);
	clip-path: none;
	height: auto;
	width: auto;
	margin: 0;
}

@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
		scroll-behavior: auto !important;
	}
}

/* ------------------------------------------------------------------ *
 * 10 . TEMPLATE AND PART CHROME
 * The classes the templates, parts and patterns in this theme author.
 * Nothing here carries a business fact; the header, the footer columns and
 * the CTA band all render their words from block content, from site
 * settings, or from a companion plugin block.
 * ------------------------------------------------------------------ */

/* FULL BLEED, AND WHY IT NEEDS NO ARITHMETIC HERE. Under
   useRootPaddingAwareAlignments WordPress puts the vertical root padding on
   .wp-site-blocks but the HORIZONTAL root padding on each .has-global-padding
   block, not on the container. The header band, the CTA band and the footer
   band are constrained groups, so they carry .has-global-padding themselves:
   they already run the full width of the viewport with their content inset by
   exactly the root gutter. A negative margin here would push them past the
   viewport edge and give the page a horizontal scrollbar, so there is none.
   Verified against the generated stylesheet on a rendered page rather than
   assumed. */

/* Header bar. Measured 79px tall on the reference desktop capture. */
.bx-header {
	padding-block: var(--wp--preset--spacing--bx-sm, 1rem);
	border-bottom: 1px solid transparent;
}

.bx-header__inner {
	gap: var(--wp--preset--spacing--bx-md, 1.5rem);
}

.bx-brand {
	gap: var(--wp--preset--spacing--bx-xs, 0.75rem);
}

/* One brand mark, not two. The header and the footer both offer a site logo
   and a site title so the theme renders something on a site that has set only
   one of them; when a logo image is actually present the text title steps
   aside. */
.bx-brand .wp-block-site-logo:has(img) + .wp-block-site-title,
.bx-footer__brand .wp-block-site-logo:has(img) + .wp-block-site-title {
	display: none;
}

.bx-header .wp-block-navigation {
	gap: var(--wp--preset--spacing--bx-md, 1.5rem);
}

/* Below the nav breakpoint the disclosure panel drops under the bar rather
   than squeezing the logo. */
@media (max-width: 980px) {
	.bx-navwrap[open] .wp-block-navigation {
		flex-basis: 100%;
		width: 100%;
		padding-block-start: var(--wp--preset--spacing--bx-sm, 1rem);
	}

	.bx-navwrap[open] .wp-block-navigation ul {
		flex-direction: column;
		align-items: flex-start;
		gap: var(--wp--preset--spacing--bx-xs, 0.75rem);
	}
}

/* Page shell. Each template stacks .bx-section groups inside main, so the
   heading block and the body block would otherwise contribute a section pad
   each at the seam between them. The head drops its bottom pad and the body
   keeps its top pad, which leaves exactly one section gap. */
.bx-page__head {
	padding-block-end: 0;
}

.bx-page--front .bx-hero:not(.bx-hero--cover),
.bx-page--hub .bx-hero:not(.bx-hero--cover) {
	padding-block-end: 0;
}

.bx-page__head .wp-block-post-title,
.bx-hero .wp-block-post-title {
	margin-block: 0 var(--wp--preset--spacing--bx-sm, 1rem);
}

/* The CTA band and the footer band stack into the ONE continuous red region
   the reference site shows at the bottom of every page. Three things have to
   go for that to happen, and all three were measured on a rendered page:
   the 24px block gap core puts between top level children of .wp-site-blocks
   (which paints as a white stripe between two red grounds), the CTA band's
   own bottom pad, and the footer band's top pad. The footer is wrapped in a
   .wp-block-template-part element, so the sibling combinator has to reach
   through it. */
.bx-cta-row + .wp-block-template-part,
.bx-cta-row + .bx-footer {
	margin-block-start: 0;
}

.bx-cta-row + .wp-block-template-part .bx-footer,
.bx-cta-row + .bx-footer {
	padding-block-start: 0;
}

/* Measured on the reference: the CTA button sits about 30px above the rule
   that opens the footer columns, not a full section pad. */
.bx-cta-row:has(+ .wp-block-template-part),
.bx-cta-row:has(+ .bx-footer) {
	padding-block-end: var(--wp--preset--spacing--bx-md, 1.5rem);
}

/* The rule that opens the footer columns, cited: 2px at rgba(255,255,255,0.2)
   across the content column. Section 3 already styles a separator inside a
   band; this only removes the block gap above the first one so it sits on the
   seam rather than below it. */
.bx-band.bx-footer > hr.wp-block-separator:first-child {
	margin-block-start: 0;
}

/* Footer columns. Ported from the reference: a wide brand column, then
   contact details, then quick links, all on the red ground. */
/* space-between keeps the row balanced in both states: while the NAP column is
   collapsed (the companion plugin block is not registered yet) the quick links
   sit at the right edge rather than leaving a third of the row blank, and once
   the NAP column fills, the three columns already total the full width. */
.bx-footer__cols {
	gap: var(--wp--preset--spacing--bx-lg, 2rem);
	justify-content: space-between;
}

.bx-footer__list {
	list-style: none;
	padding-inline-start: 0;
	margin-block: 0;
}

.bx-footer__list li {
	margin-block-end: 0.35em;
}

.bx-footer .wp-block-site-title a {
	color: var(--bx-white);
	text-decoration: none;
}

.bx-footer__legal {
	gap: var(--wp--preset--spacing--bx-sm, 1rem);
	width: 100%;
}

.bx-footer__legal p {
	margin-block: 0;
}

/* The NAP column renders a companion plugin block. Until that block exists the
   column would be a heading over nothing, so the whole column collapses when
   the block contributes no element of its own. Same rule for the contact
   template's NAP section. */
.bx-footer__nap:not(:has(p, ul, ol, address, div, table)),
.bx-contact-nap:not(:has(p, ul, ol, address, div, table)) {
	display: none;
}

/* The cross link rail and the grids: flat cards on a white ground, the
   reference card treatment, with the columns collapsing to one on a phone. */
.bx-rail__cols,
.bx-grid__cols {
	gap: var(--wp--preset--spacing--bx-lg, 2rem);
}

.bx-rail .wp-block-column,
.bx-grid .wp-block-column {
	min-width: 0;
}

/* The rail card is the reference service card: red rule, ink heading, prose,
   coral button. The heading is plain text and the button carries the link, so
   nothing in the card depends on colour alone to read as clickable. */
.bx-rail .wp-block-column,
.bx-grid .wp-block-column {
	display: flex;
	flex-direction: column;
}

.bx-rail .wp-block-buttons,
.bx-grid .wp-block-buttons {
	margin-block-start: auto;
	padding-block-start: var(--wp--preset--spacing--bx-sm, 1rem);
}

.bx-locations {
	list-style: none;
	padding-inline-start: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
	gap: var(--wp--preset--spacing--bx-xs, 0.75rem) var(--wp--preset--spacing--bx-lg, 2rem);
}

/* The vent calculator slot on the hub template. The block is registered by the
   companion plugin; the section collapses while it is missing so the page does
   not open on an empty band. */
.bx-calculator:not(:has(p, ul, ol, form, table, div, fieldset)) {
	display: none;
}

/* ------------------------------------------------------------------ *
 * 11 . COMPANION PLUGIN BLOCKS
 * The plugin renders answer-first, faq, faq-item, comparison-table and
 * vent-calculator, all under bx-* class names, plus the contact form under
 * [ss_contact_form] and the NAP seam in the footer and on the contact page.
 * Its block stylesheets were written against a different house theme and
 * read token names this theme does not define (--wp--preset--color--cream,
 * --dark-brown, --light-brown, --sand, --base, --font-family--display,
 * --font-family--subhead, and an h2/h3/h6/body/body-lg/caption/small size
 * scale), so every one of them currently resolves to that other brand's
 * literal fallback: a cream card, a brown rule, a serif heading.
 *
 * The bridge below DEFINES those names and maps them onto the Bactronix
 * palette and type scale. Because the plugin reads them as
 * var(--name, fallback), defining the name wins no matter which stylesheet
 * loads first, so this needs no !important and no edit to the plugin.
 *
 * THE BRIDGE IS SCOPED TO THE PLUGIN'S OWN SELECTORS, NOT :root. The names
 * being defined, --wp--preset--color--base, --black, --font-size--body,
 * --spacing--md and the rest, are exactly the shape WordPress generates
 * from any theme.json preset (--wp--preset--<type>--<slug>). Declaring them
 * on bare :root would put them in that same reserved, global namespace, so
 * the day a future theme.json preset happens to take the slug body, small,
 * md, base or black, this bridge and that preset collide and stylesheet
 * order decides which one wins. Scoping the declarations to the selectors
 * that actually need them keeps the bridge working without reserving any
 * part of the global preset namespace.
 *
 * The rules after the bridge cover what the plugin hardcodes rather than
 * tokenises (a 16px radius, an rgba brown hairline). They are written with a
 * :root prefix purely to raise specificity above the plugin's single class
 * selectors, again so load order cannot decide the outcome; that prefix is
 * an ordinary specificity trick, not a custom property declaration, so it
 * carries none of the namespace risk above.
 * ------------------------------------------------------------------ */

.bx-answer-first,
.bx-faq,
.bx-faq-item,
.bx-comparison-table,
.bx-vent-calc,
.ss-contact,
.bx-footer__nap,
.bx-contact-nap {
	/* Colour names the plugin blocks read. */
	--wp--preset--color--base: var(--wp--preset--color--bx-white, #FFFFFF);
	--wp--preset--color--black: var(--wp--preset--color--bx-ink, #333333);
	--wp--preset--color--cream: var(--wp--preset--color--bx-mist, #F3F3F3);
	--wp--preset--color--sand: var(--wp--preset--color--bx-hairline, #E2E2E2);
	--wp--preset--color--dark-brown: var(--wp--preset--color--bx-ink, #333333);
	--wp--preset--color--light-brown: var(--wp--preset--color--bx-red, #BB2B1E);

	/* Family names the plugin blocks read. */
	--wp--preset--font-family--display: var(--bx-ff-heading);
	--wp--preset--font-family--subhead: var(--bx-ff-heading);

	/* Size names the plugin blocks read, mapped onto this theme's ladder. */
	--wp--preset--font-size--h2: var(--wp--preset--font-size--bx-xxl, clamp(1.625rem, 1rem + 1.9vw, 2.375rem));
	--wp--preset--font-size--h3: var(--wp--preset--font-size--bx-xl, clamp(1.25rem, 1.1rem + 0.5vw, 1.375rem));
	--wp--preset--font-size--h6: var(--wp--preset--font-size--bx-sm, 0.875rem);
	--wp--preset--font-size--body: var(--wp--preset--font-size--bx-body, 1rem);
	--wp--preset--font-size--body-lg: var(--wp--preset--font-size--bx-lg, 1.125rem);
	--wp--preset--font-size--small: var(--wp--preset--font-size--bx-sm, 0.875rem);
	--wp--preset--font-size--caption: var(--wp--preset--font-size--bx-sm, 0.875rem);

	/* Spacing names the plugin blocks read. */
	--wp--preset--spacing--xs: var(--wp--preset--spacing--bx-xs, 0.75rem);
	--wp--preset--spacing--sm: var(--wp--preset--spacing--bx-sm, 1rem);
	--wp--preset--spacing--md: var(--wp--preset--spacing--bx-md, 1.5rem);
	--wp--preset--spacing--lg: var(--wp--preset--spacing--bx-lg, 2rem);
}

/* Answer-first. The plugin ships a 16px radius cream card with a brown left
   rule; the Bactronix card is flat and square with the brand red rule. */
:root .bx-answer-first {
	border: 1px solid var(--bx-hairline);
	border-left: var(--bx-rule-h) solid var(--bx-red);
	border-radius: var(--bx-radius-sm);
	background: var(--bx-mist);
}

:root .bx-answer-first .bx-eyebrow {
	font-family: var(--bx-ff-body);
	font-weight: 600;
	color: var(--bx-red);
}

:root .bx-answer-first .bx-last-updated {
	color: var(--bx-body-color);
}

:root .bx-answer-first__text {
	color: var(--bx-ink);
	line-height: 1.6;
}

/* The band inverts the card so an answer-first inside a red section still
   reads. Not used by any template today; it costs two rules and stops a
   future editor putting an invisible paragraph on a red ground. */
.bx-band .bx-answer-first,
.is-style-bx-band .bx-answer-first {
	background: rgba(255, 255, 255, 0.08);
	border-color: rgba(255, 255, 255, 0.28);
	border-left-color: var(--bx-white);
}

/* FAQ. Native details and summary, so the only work here is type and rules. */
:root .bx-faq__heading {
	font-family: var(--bx-ff-heading);
	color: var(--bx-ink);
}

:root .bx-faq-item {
	border-bottom: 1px solid var(--bx-table-line);
}

:root .bx-faq-item .bx-faq-question::before {
	color: var(--bx-red);
}

:root .bx-faq-item .bx-faq-question__text {
	font-family: var(--bx-ff-heading);
	color: var(--bx-ink);
}

:root .bx-faq-item .bx-faq-answer {
	color: var(--bx-body-color);
	line-height: 1.7;
}

.bx-band .bx-faq-item,
.is-style-bx-band .bx-faq-item {
	border-bottom-color: rgba(255, 255, 255, 0.28);
}

/* Comparison table. The site's own table treatment is a 1px #eee frame with no
   header fill (section 2), so the plugin's dark header band and zebra striping
   are flattened to it. */
:root .bx-comparison-table .bx-ctable {
	border: 1px solid var(--bx-table-line);
}

:root .bx-comparison-table .bx-ctable thead th {
	background: transparent;
	color: var(--bx-table-head);
	font-weight: 700;
}

:root .bx-comparison-table .bx-ctable tbody th[scope="row"] {
	background: transparent;
	color: var(--bx-ink);
}

:root .bx-comparison-table .bx-ctable tbody tr:nth-child(even) td {
	background: transparent;
}

:root .bx-comparison-table .bx-ctable th,
:root .bx-comparison-table .bx-ctable td {
	border: 0;
	border-top: 1px solid var(--bx-table-line);
}

:root .bx-comparison-table .bx-ctable__caption {
	font-family: var(--bx-ff-heading);
	color: var(--bx-ink);
}

:root .bx-comparison-table .bx-ctable__source {
	color: var(--bx-body-color);
}

/* The contact form the plugin renders under [ss_contact_form]. Its markup
   contract is fixed in the plugin (section#ss-contact.ss-contact, its h2, and
   form.ss-contact-form), so the theme borrows the ported lead panel look
   rather than restating a second form design. */
.bx-contact-form .ss-contact__heading {
	font-family: var(--bx-ff-heading);
	color: var(--bx-ink);
}

.bx-contact-form .ss-contact-form {
	max-width: 560px;
	padding: 24px;
	border: 1px solid #e5e5e5;
	border-radius: 8px;
	background: var(--bx-white);
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}

.bx-contact-form .ss-contact-form label {
	display: block;
	font-size: 0.875rem;
	font-weight: 600;
	color: var(--bx-ink);
	margin: 10px 0 4px;
}

.bx-contact-form .ss-contact-form input[type="text"],
.bx-contact-form .ss-contact-form input[type="tel"],
.bx-contact-form .ss-contact-form input[type="email"],
.bx-contact-form .ss-contact-form textarea,
.bx-contact-form .ss-contact-form select {
	width: 100%;
	padding: 10px 12px;
	border: 1px solid #cccccc;
	border-radius: 4px;
	font-family: var(--bx-ff-body);
	font-size: 1rem;
	color: var(--bx-ink);
	background: var(--bx-white);
	box-sizing: border-box;
}

.bx-contact-form .ss-contact-form button,
.bx-contact-form .ss-contact-form input[type="submit"] {
	width: 100%;
	margin-top: 14px;
	padding: 14px;
	border: none;
	border-radius: var(--bx-radius-pill);
	background: var(--bx-red);
	color: var(--bx-white);
	font-family: var(--bx-ff-body);
	font-size: 0.8125rem;
	font-weight: 600;
	letter-spacing: 1px;
	text-transform: uppercase;
	cursor: pointer;
}

.bx-contact-form .ss-contact-form button:hover,
.bx-contact-form .ss-contact-form button:focus,
.bx-contact-form .ss-contact-form input[type="submit"]:hover,
.bx-contact-form .ss-contact-form input[type="submit"]:focus {
	background: var(--bx-red-dark);
}

.bx-contact-form .ss-contact--done {
	max-width: 560px;
	margin-top: 10px;
	padding: 14px;
	border-left: 4px solid var(--bx-red);
	background: #F9F9F9;
	color: var(--bx-ink);
	font-size: 0.9375rem;
}

/* The plugin's footer social line wears the same quiet register as the legal
   line, which is where it sits. */
.bx-footer .foot-socials {
	margin-block: 0;
	font-size: 0.875rem;
}

/* ------------------------------------------------------------------ *
 * 12 . PHOTO HERO COVER
 * The front page and the hub open on a full bleed core/cover block that
 * pulls the page's own featured image (templates/front-page.html and
 * templates/page-hub.html, both "useFeaturedImage": true). No image path
 * is written into any template or into this file: the image arrives as
 * post meta, set at import time from tools/md-to-wxr/featured-image-map.json.
 * ------------------------------------------------------------------ */

/* The duotone. Cited from the reference's own hero rule, which layers a
   radial gradient over the photograph:
     background-image:radial-gradient(circle at top left,
       var(--gcid-primary-color) 0%, rgba(0,56,94,0.75) 50% 100%), url(...)
   (design/reference/home.html, the hero section's inline style; the same
   rule with a different photo is on hvac-mold-treatment-50-per-vent.html).
   --gcid-primary-color resolves to #bb2b1e, the brand red this theme already
   ships as bx-red, and #00385E is the navy it fades into.

   The gradient itself is carried on the block (customGradient), because the
   cover block is what paints it and an editor changing the hero should see
   it. What is NOT on the block is everything below: the type treatment, the
   viewport heights, and the no-image fallback. */

/* Full bleed. The cover is alignfull inside a constrained <main> that carries
   .has-global-padding, so WordPress's own alignfull rule already pulls it out
   to the viewport edges by exactly the root gutter. Nothing is added here;
   this comment exists because a negative margin looks like the obvious fix
   and would give the page a horizontal scrollbar (THEME-NOTES, "Full bleed
   needs no negative margins here"). */

.bx-hero--cover {
	/* The reference hero runs edge to edge under the bar with no rule, no
	   band and no divider beneath it: home-desktop-scroll2.png shows the
	   white service card grid starting immediately. So there is no red
	   divider strip here, deliberately. */
	margin-block: 0;
}

/* Vertical centring plus the page's own content column. The cover's inner
   container is a constrained layout, so it is already capped at contentSize
   and inset by the root gutter; the title just needs to sit left inside it
   rather than inheriting the cover's centred text. */
.bx-hero--cover .wp-block-cover__inner-container {
	text-align: left;
}

.bx-hero--cover .wp-block-post-title {
	margin-block: 0;
	color: var(--bx-white);
}

/* Overlay strength, and the contrast arithmetic behind it. The cited hero
   rule fades to rgba(0,56,94,0.75); the cover block's dimRatio is the same
   knob, so it ships at 70, the nearest step under the cited figure.

   Measured against the brightest pixel either photograph offers, the open sky
   in the fleet frame at about #E8EEF5: 70 percent of #00385E over it composites
   to rgb(70,111,139), which holds 5.3:1 against white. The same sum at
   dimRatio 60 gives rgb(93,129,154) and 4.2:1, which still clears AA for large
   text but not for anything smaller that might later be dropped into the hero.

   The text shadow is the second line rather than the first. It is a soft, low
   opacity black offset by a single pixel: enough to hold the letterforms apart
   from a bright patch, not enough to read as an effect on the dark majority of
   either image. */
.bx-hero--cover .wp-block-post-title,
.bx-hero--cover .wp-block-cover__inner-container :where(p, h2, a) {
	text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

/* Height. The block carries min-height: 70vh inline, which is what the editor
   shows and what desktop renders. The phone step down to 55vh has to be
   !important for the one reason !important is legitimate: it is overriding an
   inline style attribute, which no amount of selector specificity can beat.
   781px is WordPress's own mobile breakpoint. */
@media (max-width: 781px) {
	.bx-hero--cover {
		min-height: 55vh !important;
	}
}

/* THE NO IMAGE FALLBACK, and why it is here rather than in the template.
   A block template cannot branch, so page-hub.html renders its cover on every
   page that uses the hub template whether or not that page has a featured
   image. With no thumbnail, core's render callback injects no <img> and the
   cover paints as a 70vh slab of red-to-navy gradient with a title floating
   in it: not a broken block, but not an intended one either.

   The 41 content pages with no hero use the other templates and never reach
   this code. This rule is the safety net for the case where somebody assigns
   the hub or front template to a page that has no featured image: the cover
   collapses back to exactly the plain hero group those templates carried
   before, a white ground with an ink title and ordinary section padding.

   :has() is already load bearing in section 10 for the site logo, so it is
   not a new dependency. */
.bx-hero--cover:not(:has(.wp-block-cover__image-background)):not(:has(video)) {
	min-height: 0 !important;
	padding-block: var(--bx-section-pad);
	padding-block-end: 0;
}

.bx-hero--cover:not(:has(.wp-block-cover__image-background)):not(:has(video)) .wp-block-cover__background {
	display: none;
}

/* !important, and it is not optional. The title block carries the bx-white
   preset colour, and WordPress generates every preset colour class as
   `color: var(--wp--preset--color--bx-white) !important`. Without a matching
   !important here the fallback title stays white on the white ground and the
   page renders with no visible H1 at all: caught on a probe page assigned the
   hub template with no featured image, where the heading was invisible until
   this rule could actually win. */
.bx-hero--cover:not(:has(.wp-block-cover__image-background)):not(:has(video)) .wp-block-post-title {
	color: var(--bx-ink) !important;
	text-shadow: none;
	margin-block: 0 var(--wp--preset--spacing--bx-sm, 1rem);
}

/* Motion and print. A 70vh photograph is furniture, not content: the title is
   in the DOM either way, so printing the cover only spends ink. */
@media print {
	.bx-hero--cover {
		min-height: 0 !important;
		padding-block: 0;
	}

	.bx-hero--cover .wp-block-cover__background,
	.bx-hero--cover .wp-block-cover__image-background {
		display: none !important;
	}

	.bx-hero--cover .wp-block-post-title {
		color: #000000 !important;
		text-shadow: none !important;
	}
}

/* ------------------------------------------------------------------ *
 * 13 . PRINT
 * ------------------------------------------------------------------ */

@media print {
	.bx-bar,
	.bx-header .wp-block-navigation,
	.bx-cta-row,
	.bx-rail {
		display: none !important;
	}

	.bx-band,
	.is-style-bx-band {
		background: none !important;
		color: #000000 !important;
	}

	.bx-band :where(h1, h2, h3, h4, h5, h6, p, li),
	.is-style-bx-band :where(h1, h2, h3, h4, h5, h6, p, li) {
		color: #000000 !important;
	}
}
