/**
 * HouseBro Component CSS
 *
 * Covers only what Elementor's own widget/Style controls can't cleanly
 * achieve — sticky/behavioral patterns, native-HTML accordion styling,
 * and accessibility states. Everything here reads its values from
 * global-tokens.css custom properties, never a hardcoded hex/px value
 * that duplicates DESIGN_SYSTEM.md.
 */

/* Typography — DESIGN_SYSTEM.md Section 3. Generic tag selectors so every
   heading/paragraph gets the locked scale by default, whether it comes
   from an Elementor Heading widget's own markup or raw shortcode HTML —
   a widget-specific style set via Elementor's own controls still wins
   (later in the cascade, higher specificity), this only fills the gap
   where none was set. */
body {
	font-family: var(--hb-font-family);
	font-size: var(--hb-font-size-body);
	line-height: var(--hb-line-height-body);
	color: var(--hb-color-ink);
}
/* Premium consistency pass (2026-07-13) — same reset.css-override pattern
   as the heading fix below: the parent theme's default paragraph margin
   is only 0.9rem (~14px) bottom, 0 top. Homepage sections read as
   premium/breathing because their shortcode wrappers each carry their own
   48-96px padding-block; pillar/city pages mix plain <p> intro text
   directly into post_content with no such wrapper, so it looked visibly
   cramped next to the Homepage by comparison. One paragraph rhythm
   sitewide instead of two different ones by accident of which template
   a page happens to use. */
body p {
	margin-block: 0 var(--hb-space-24);
}
/* Batch 8 fix (2026-07-12): the parent Hello Elementor theme's own
   reset.css sets h1-h4 font-size/weight/line-height/margin too (a fixed
   2.5rem/2rem/1.75rem/1.5rem, non-responsive, weight 500) and loads AFTER
   this stylesheet — at equal specificity (bare tag selectors on both
   sides), source order wins, so reset.css was silently overriding this
   entire type scale everywhere on the site. Adding "body" to each
   selector here is the minimal specificity bump needed to reliably win
   regardless of load order, without resorting to !important. */
body h1, body h2, body h3, body h4 {
	font-family: var(--hb-font-family);
	color: var(--hb-color-ink);
	margin: 0;
}
body h1 {
	font-size: var(--hb-font-size-h1);
	font-weight: 700;
	line-height: var(--hb-line-height-h1);
}
body h2 {
	font-size: var(--hb-font-size-h2);
	font-weight: 700;
	line-height: var(--hb-line-height-h2);
}
body h3 {
	font-size: var(--hb-font-size-h3);
	font-weight: 600;
	line-height: var(--hb-line-height-h3);
}
body h4 {
	font-size: var(--hb-font-size-h4);
	font-weight: 600;
	line-height: var(--hb-line-height-h4);
}

/* Design System Lock (Batch 14, 2026-07-12) — Hello Elementor's default
   page template gives .entry-title (the page's own <h1>, e.g. "About")
   a fixed 600px width with auto margins, centering it while every other
   heading and all body content on the same page is left-aligned. One
   heading alignment sitewide — matches how [hb_hero]'s <h1> and every
   .hb-main h2 already behave. */
.hb-main .page-header .entry-title {
	width: auto;
	max-width: none;
	margin-inline: 0;
}
/* Premium Brand Consistency Pass (2026-07-13) — static pages (About,
   Contact, Gallery, FAQ, Delhi NCR) had zero padding around the page
   title, so it sat flush at the very top of the content area with no
   breathing room — the single biggest "default WordPress page" tell
   compared to the Homepage/pillar pages' full Hero card. Doesn't add a
   Hero (no new section/markup), just gives the existing title the same
   generous vertical rhythm every other section on the site already has. */
.hb-main .page-header {
	padding-block: var(--hb-space-32) var(--hb-space-48);
}
@media (min-width: 1024px) {
	.hb-main .page-header {
		padding-block: var(--hb-space-48) var(--hb-space-64);
	}
}

/* Section headings (Elementor Heading widgets placed before each shortcode
   section) get consistent spacing/hierarchy regardless of which Elementor
   style was picked per-widget — the missing "section hierarchy" rhythm. */
.hb-main h2 {
	margin-block: var(--hb-space-64) var(--hb-space-32);
	position: relative;
	padding-block-end: var(--hb-space-16);
}
.hb-main h2::after {
	content: '';
	position: absolute;
	inset-block-end: 0;
	inset-inline-start: 0;
	width: 48px;
	height: 3px;
	background: var(--hb-color-brass);
	border-radius: var(--hb-radius-pill);
}
/* Premium Brand Consistency Pass (2026-07-13) — removed a zero-margin
   override that read as ".hb-main > *:first-child h2" (intended to mean
   "an h2 that is itself the first thing on the page") but CSS's
   descendant combinator actually matched "any h2 anywhere inside
   whatever .hb-main's first child happens to be" — and every page
   template (WP's default page.php, Elementor's canvas output) wraps its
   entire content in exactly one direct child of .hb-main, so this
   silently zeroed the 64px top margin on literally every page's first
   h2, sitewide. Invisible on Homepage/pillar pages (a full Hero card
   already provides enough visual separation regardless), but on
   About/Contact/Gallery/FAQ/Delhi NCR — whose plain WP page title has no
   such card — it made the first section heading look glued directly to
   the page title. ".hb-main h1 + h2" never matched anything in practice
   (verified: no h1 on the site has an h2 as its literal next DOM
   sibling), so removing both restores the intended 64px rhythm
   everywhere with no loss of a real behavior. */

/* Layout */
.hb-section {
	padding-block: var(--hb-space-48);
}
@media (min-width: 1024px) {
	.hb-section {
		padding-block: var(--hb-space-96);
	}
}

.hb-container {
	max-width: 1280px;
	margin-inline: auto;
	padding-inline: var(--hb-space-16);
}
@media (min-width: 1024px) {
	.hb-container {
		padding-inline: var(--hb-space-48);
	}
}

/* Card */
.hb-card {
	background: var(--hb-color-marble-white);
	border: 1px solid var(--hb-color-divider);
	border-radius: var(--hb-radius-card-lg);
	box-shadow: var(--hb-shadow-premium-resting);
	transition: box-shadow var(--hb-transition-duration) var(--hb-transition-easing),
		transform var(--hb-transition-duration) var(--hb-transition-easing),
		border-color var(--hb-transition-duration) var(--hb-transition-easing);
}
.hb-card:hover {
	box-shadow: var(--hb-shadow-premium-hover);
	transform: translateY(-4px);
	border-color: var(--hb-color-brass);
}

/* Buttons */
.hb-btn {
	border-radius: var(--hb-radius-button);
	/* Premium Brand Consistency Pass (2026-07-13) — bumped from 44px (the
	   bare accessibility minimum) to 48px, Material Design's recommended
	   touch target size, since Call/WhatsApp are the single most-clicked
	   elements on the site and should feel unmistakably tappable, not
	   just technically compliant. */
	min-height: 48px;
	padding-inline: var(--hb-space-24);
	font-size: var(--hb-font-size-button);
	font-weight: 600;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--hb-space-8);
	transition: filter var(--hb-transition-duration) var(--hb-transition-easing),
		transform var(--hb-transition-duration) var(--hb-transition-easing);
	text-decoration: none;
}
/* Icon System (Brand Asset Phase, 2026-07-14) — Call/WhatsApp icons,
   sized to sit comfortably inside the 48px button without dominating the
   label text. */
.hb-btn .hb-icon {
	width: 18px;
	height: 18px;
	flex-shrink: 0;
}
/* Design System Lock (Batch 14, 2026-07-12) — Hello Elementor's default
   page template underlines links inside its own content area (a higher-
   specificity ".page-content a" / ".site-main a" rule than the bare
   ".hb-btn" above), so Call/WhatsApp buttons dropped into plain page
   content (About, Contact, etc. — anywhere outside Elementor-rendered
   sections) rendered as underlined text instead of a button. Confirmed
   via computed style: the same button showed text-decoration:none in the
   header but underline inside .page-content on the same page. */
.hb-main .hb-btn {
	text-decoration: none;
}
/* Premium Brand Consistency Pass (2026-07-13) — the same Hello Elementor
   default (".page-content a { color: #c36 }") that used to hit
   .hb-pillar-link/.hb-sibling-service/.hb-before-after-link also hit the
   tel:/mailto: links on the Contact page, which carry no class of their
   own (they're plain prose links, not a styled component).
   Rescoped from a ".page-content a" catch-all to this exact selector (UI
   Polish Pass, 2026-07-14) — the broad version, even after excluding
   .hb-btn/.hb-card by name, kept silently overriding other classless-
   anchor components nested in .page-content (.hb-areas-we-serve-item a,
   .hb-service-areas a — both already set their own ink/no-underline pill
   styling, but with lower specificity than a ".page-content a" rule, so
   they lost that fight without any visible error, sitewide, since Batch
   4). A catch-all with a growing exclusion list has no natural end —
   every future component nested in .page-content is a new potential
   victim. Scoping directly to where the real bug actually lives (the
   Contact card's phone/email rows) fixes it permanently: nothing else
   inside .page-content is affected by this rule ever again. */
.hb-contact-card-row a {
	color: var(--hb-color-brass);
	text-decoration: underline;
}
.hb-contact-card-row a:hover {
	text-decoration: none;
}
/* Bare CTA pairs (About/Gallery/FAQ/Delhi NCR end with a plain
   <p><a class="hb-btn-call">…</a><br><a class="hb-btn-whatsapp">…</a></p>,
   no wrapping band) — styled to match .hb-final-cta-band's premium
   card treatment used everywhere else, targeting the exact existing
   markup rather than changing any content. */
.page-content p:has(> a.hb-btn-call) {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: center;
	gap: var(--hb-space-16);
	padding: var(--hb-space-32) var(--hb-space-24);
	margin-block: var(--hb-space-48) 0;
	background: linear-gradient(135deg, var(--hb-color-marble-white) 0%, #fff 100%);
	border-radius: var(--hb-radius-card);
	box-shadow: var(--hb-shadow-resting);
}
@media (min-width: 1024px) {
	.page-content p:has(> a.hb-btn-call) {
		padding: var(--hb-space-64) var(--hb-space-48);
	}
}
.page-content p:has(> a.hb-btn-call) br {
	display: none;
}
.page-content p:has(> a.hb-btn-call) .hb-btn {
	flex: 1 1 160px;
	max-width: 280px;
	min-height: 52px;
	box-shadow: var(--hb-shadow-resting);
}
.hb-btn:active {
	transform: translateY(1px);
}
.hb-btn-call {
	background: var(--hb-color-brass);
	/* Ink text, not white: white-on-brass measures ~4:1 contrast (fails
	   WCAG AA's 4.5:1 for normal-weight button text) — ink-on-brass
	   measures ~4.3:1. Same locked palette, just the correct pairing. */
	color: var(--hb-color-ink);
}
.hb-btn-whatsapp {
	background: var(--hb-color-whatsapp-green);
	/* White-on-WhatsApp-green measures ~2:1 — badly fails legibility for
	   what is likely the single most-clicked element on the site.
	   Ink-on-green measures ~8.6:1. */
	color: var(--hb-color-ink);
}
.hb-btn:hover {
	filter: brightness(0.92);
}
.hb-btn:focus-visible {
	outline: 2px solid var(--hb-color-ink);
	outline-offset: 2px;
}

/* Badge */
.hb-badge-pill {
	border-radius: var(--hb-radius-pill);
	padding: var(--hb-space-4) var(--hb-space-16);
	font-size: var(--hb-font-size-small);
	font-weight: 600;
	display: inline-block;
}

/* Trust Badge (COMPONENT_LIBRARY.md #13) — extends .hb-badge-pill above.
   Icon (optional) + label sit inline; icon never carries meaning alone. */
.hb-trust-badge {
	display: inline-flex;
	align-items: center;
	gap: var(--hb-space-8);
	background: #fff;
	color: var(--hb-color-ink);
	border: 1px solid var(--hb-color-divider);
	box-shadow: var(--hb-shadow-premium-resting);
}
.hb-trust-badge-icon {
	width: 16px;
	height: 16px;
	flex-shrink: 0;
}

/* Accessibility utility */
.hb-visually-hidden {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
}

/* Front page's temporary fallback <h1> ("Home") — kept in the DOM so the
   page always has a real <h1> (see functions.php's hello_elementor_page_title
   filter), but visually hidden so it doesn't compete with [hb_hero]'s own
   heading area above the fold. Stops applying naturally the moment a real
   Hero Heading is entered, since the fallback title stops rendering at all
   at that point (suppressed server-side, not just hidden here). */
body.home .page-header {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
}

/* Skip link (Milestone 9 — WCAG 2.4.1 Bypass Blocks) — hidden until focused */
.hb-skip-link {
	position: absolute;
	top: -100px;
	left: var(--hb-space-16);
	z-index: 1100;
	background: var(--hb-color-ink);
	color: #fff;
	padding: var(--hb-space-8) var(--hb-space-16);
	border-radius: var(--hb-radius-button);
	text-decoration: none;
}
.hb-skip-link:focus {
	top: var(--hb-space-16);
}

/* Sticky CTA bar (mobile/tablet only — Header carries this role on desktop, DESIGN_SYSTEM.md Section 15) */
.hb-sticky-cta {
	position: fixed;
	inset-inline: 0;
	bottom: 0;
	z-index: 999;
	display: flex;
	gap: var(--hb-space-8);
	padding: var(--hb-space-8) var(--hb-space-16);
	background: var(--hb-color-marble-white);
	border-top: 1px solid var(--hb-color-divider);
	box-shadow: var(--hb-shadow-elevated);
}
.hb-sticky-cta .hb-btn {
	flex: 1;
}
@media (min-width: 1024px) {
	.hb-sticky-cta {
		display: none;
	}
}
body.hb-has-sticky-cta {
	padding-bottom: 72px; /* reserves space so page content is never hidden behind the bar */
}
@media (min-width: 1024px) {
	body.hb-has-sticky-cta {
		padding-bottom: 0;
	}
}

/* Mobile Header / Navigation base sizing — full rules (display, panel
   transform/transition) consolidated in the Header & Footer section below
   to avoid the two blocks fighting over the same selectors. */

/* Breadcrumb */
.hb-breadcrumb {
	font-size: var(--hb-font-size-small);
	color: var(--hb-color-stone-grey);
	padding-block: var(--hb-space-16);
}
.hb-breadcrumb a {
	color: var(--hb-color-stone-grey);
	text-decoration: underline;
}
.hb-breadcrumb [aria-current='page'] {
	color: var(--hb-color-ink);
	font-weight: 600;
}

/* FAQ Accordion — native <details>/<summary>, zero JS (COMPONENT_LIBRARY.md #11) */
.hb-faq-accordion {
	padding-block: var(--hb-space-48);
	max-width: 800px;
	margin-inline: auto;
}
@media (min-width: 1024px) {
	.hb-faq-accordion {
		padding-block: var(--hb-space-96);
	}
}
.hb-faq-item {
	border-bottom: 1px solid var(--hb-color-divider);
}
.hb-faq-item summary {
	padding-block: var(--hb-space-16);
	font-weight: 600;
	cursor: pointer;
	list-style: none;
	transition: color var(--hb-transition-duration) var(--hb-transition-easing);
}
.hb-faq-item summary:hover {
	color: var(--hb-color-brass);
}
.hb-faq-item summary::-webkit-details-marker {
	display: none;
}
.hb-faq-item summary::after {
	content: '+';
	float: inline-end;
}
.hb-faq-item[open] summary::after {
	content: '\2212'; /* minus sign */
}
.hb-faq-item summary:focus-visible {
	outline: 2px solid var(--hb-color-ink);
	outline-offset: 2px;
}
.hb-faq-item .hb-faq-answer {
	padding-block-end: var(--hb-space-16);
	color: var(--hb-color-stone-grey);
}

/* Offer Banner */
.hb-offer-banner {
	background: var(--hb-color-ink);
	color: #fff;
	text-align: center;
	padding: var(--hb-space-8) var(--hb-space-16);
	font-size: var(--hb-font-size-small);
}
.hb-offer-banner-dismiss {
	background: none;
	border: none;
	color: #fff;
	margin-inline-start: var(--hb-space-16);
	cursor: pointer;
	text-decoration: underline;
	min-height: 44px;
	min-width: 44px;
	padding-inline: var(--hb-space-8);
}
.hb-offer-banner[hidden] {
	display: none;
}

/* Empty / Loading / Error states (COMPONENT_LIBRARY.md #22-24) */
.hb-empty-state,
.hb-error-state {
	text-align: center;
	padding: var(--hb-space-48) var(--hb-space-16);
	color: var(--hb-color-stone-grey);
}
/* Homepage instances (Before & After / Pricing empty states) get a framed
   "placeholder" card instead of a lone floating sentence, so the temporary
   no-content state reads as intentionally designed rather than broken. */
.hb-main .hb-empty-state {
	max-width: 480px;
	margin-inline: auto;
	border: 1px dashed var(--hb-color-divider);
	border-radius: var(--hb-radius-card);
	padding: var(--hb-space-32) var(--hb-space-24);
}
.hb-main .hb-empty-state::before {
	content: '';
	display: block;
	width: 40px;
	height: 40px;
	margin: 0 auto var(--hb-space-16);
	border-radius: 50%;
	background: var(--hb-color-marble-white);
	border: 1px solid var(--hb-color-divider);
}
.hb-error-state {
	color: var(--hb-color-error);
}
.hb-loading-placeholder {
	background: var(--hb-color-divider);
	border-radius: var(--hb-radius-card);
	aspect-ratio: var(--hb-aspect, 4 / 3);
}

/* Homepage shortcodes (Milestone 4) — inc/homepage-shortcodes.php */

.hb-faq-link {
	margin-block-start: var(--hb-space-16);
}
.hb-faq-link a {
	color: var(--hb-color-brass);
	font-weight: 600;
	text-decoration: none;
}
.hb-faq-link a:hover {
	text-decoration: underline;
}

/* Hero — a self-contained, elevated "card" band rather than plain text on
   the page background, so it reads as designed even before a real photo
   is uploaded. Locked palette only (Marble White tint, existing shadow/
   radius tokens) — no new colors, no invented content. */
.hb-hero {
	position: relative;
	overflow: hidden;
	padding: var(--hb-space-32) var(--hb-space-24);
	background: linear-gradient(135deg, var(--hb-color-marble-white) 0%, #fff 100%);
	border-radius: var(--hb-radius-card-lg);
	box-shadow: var(--hb-shadow-elevated);
}
@media (min-width: 1024px) {
	.hb-hero {
		padding: var(--hb-space-96) var(--hb-space-48);
	}
}
/* Premium refinement pass (2026-07-13) — decorative-only depth layers.
   Abstract marble-vein line art (brand brass, low opacity) plus a soft
   radial glow, both purely CSS/SVG decoration behind the real content —
   no fabricated photo, no new markup, no new shortcode. Hidden entirely
   once a real Hero photo exists so it never competes with real content. */
.hb-hero::before {
	content: '';
	position: absolute;
	inset: 0;
	background-image: url('../images/hero-marble-veins.svg');
	background-repeat: no-repeat;
	background-position: bottom right;
	background-size: 65% auto;
	pointer-events: none;
	z-index: 0;
}
/* Service-specific Hero illustrations (Brand Asset Phase, 2026-07-13) —
   the pillar page and every city page for a given service (e.g. every
   "Marble Polishing in {City}" hero, not just the pillar) get a richer,
   service-themed variant instead of the generic Homepage texture: an
   elevated marble-vein pattern with a polish-shine highlight for Marble
   Polishing, a tile-grid + clean-sweep pattern for Tile Cleaning. Keyed
   off the real hb_service slug already added to the Hero markup — a new
   service slug simply falls back to the generic marble-vein texture
   above until it gets its own illustration. */
.hb-hero--marble-polishing::before,
.hb-hero--tile-cleaning::before {
	/* Pillar/city Hero cards are much shorter than the Homepage Hero (no
	   subheading/badges/cities line), so these two illustrations use a
	   2:1 viewBox and a smaller background-size than the generic Homepage
	   texture — tuned so the art stays inside the visible card instead of
	   being cropped off above a short box. */
	background-size: 55% auto;
	background-position: bottom right;
}
.hb-hero--marble-polishing::before {
	background-image: url('../images/hero-marble-illustration.svg');
}
.hb-hero--tile-cleaning::before {
	background-image: url('../images/hero-tile-illustration.svg');
}
.hb-hero::after {
	content: '';
	position: absolute;
	inset-block-start: -15%;
	inset-inline-end: -8%;
	width: 420px;
	height: 420px;
	max-width: 60%;
	background: radial-gradient(circle, rgba(156, 122, 60, 0.10) 0%, rgba(156, 122, 60, 0) 70%);
	pointer-events: none;
	z-index: 0;
}
.hb-hero:has(.hb-hero-bg)::before,
.hb-hero:has(.hb-hero-bg)::after {
	display: none;
}
/* Two-column Hero — activates automatically only once a real background
   image exists (:has(.hb-hero-bg), no fabricated placeholder graphic).
   Until then the Hero stays centered/single-column exactly as today —
   a right-hand image column with nothing real to put in it would just be
   empty space, which is worse than not having the column at all. The
   moment a real photo is uploaded via Hero Background Image, this takes
   over with zero further code changes. */
@media (min-width: 1024px) {
	.hb-hero:has(.hb-hero-bg) {
		display: grid;
		grid-template-columns: 1fr 1fr;
		align-items: center;
		gap: var(--hb-space-48);
		text-align: left;
	}
	.hb-hero:has(.hb-hero-bg) .hb-hero-bg {
		order: 2;
		margin-block-end: 0;
	}
	.hb-hero:has(.hb-hero-bg) .hb-hero-content {
		order: 1;
		max-width: 100%;
	}
	.hb-hero:has(.hb-hero-bg) .hb-hero-cta,
	.hb-hero:has(.hb-hero-bg) .hb-hero-badges {
		justify-content: flex-start;
	}
}
.hb-hero-bg {
	position: relative;
	z-index: 1;
	width: 100%;
	border-radius: var(--hb-radius-card);
	aspect-ratio: 16 / 9;
	object-fit: cover;
	box-shadow: var(--hb-shadow-elevated);
	margin-block-end: var(--hb-space-32);
}
.hb-hero-content {
	position: relative;
	z-index: 1;
	max-width: 640px;
}
.hb-hero-content h1 {
	margin-block-end: var(--hb-space-16);
	letter-spacing: -0.02em;
	/* Hero-specific fluid scale, larger than the sitewide h1 token used on
	   pillar/city page titles — the Hero is the one place a bigger, more
	   dramatic headline earns its place. Does not touch --hb-font-size-h1,
	   so every other h1 on the site is unaffected. */
	font-size: clamp(2rem, 1.35rem + 3vw, 3.25rem);
}
.hb-hero-trust-line {
	color: var(--hb-color-stone-grey);
	margin-block-end: var(--hb-space-32);
	font-size: 1.125rem;
}
.hb-hero-cta {
	display: flex;
	flex-wrap: wrap;
	gap: var(--hb-space-16);
}
.hb-hero-cta .hb-btn {
	flex: 1 1 160px;
	min-height: 52px;
	box-shadow: var(--hb-shadow-resting);
}
/* Trust badges row, right under the primary CTA — reinforces "why
   HouseBro" immediately after the ask, without competing with it. */
.hb-hero-badges {
	display: flex;
	flex-wrap: wrap;
	gap: var(--hb-space-8) var(--hb-space-16);
	margin-block-start: var(--hb-space-24);
}

/* Temporary state (hb_hero_heading/hb_hero_subheading not entered yet) —
   a tighter, centered card instead of a tall band with a large empty area
   beside the buttons. Reverts automatically once real copy is entered
   (see the hb-hero--minimal class logic in inc/homepage-shortcodes.php). */
.hb-hero--minimal {
	padding: var(--hb-space-32) var(--hb-space-24);
	text-align: center;
}
@media (min-width: 1024px) {
	.hb-hero--minimal {
		padding: var(--hb-space-48) var(--hb-space-24);
	}
}
.hb-hero--minimal .hb-hero-content {
	max-width: 100%;
}
.hb-hero--minimal .hb-hero-cta {
	justify-content: center;
}
.hb-hero--minimal .hb-hero-badges {
	justify-content: center;
}

/* Static "more below" cue — reinforces to a first-time visitor that the
   Service Cards (which answer "which services") are the very next thing,
   without any motion (locked animation rule: subtle only, never
   attention-seeking). Pure decoration, no new markup. */
.hb-hero-content::after {
	content: '';
	display: block;
	width: 24px;
	height: 24px;
	margin: var(--hb-space-32) auto 0;
	border-inline-end: 2px solid var(--hb-color-stone-grey);
	border-block-end: 2px solid var(--hb-color-stone-grey);
	opacity: 0.35;
	transform: rotate(45deg);
}

/* Service Cards */
.hb-service-cards {
	display: grid;
	gap: var(--hb-space-16);
	grid-template-columns: 1fr;
	padding-block: var(--hb-space-48);
	list-style: none;
	margin: 0;
	padding-inline-start: 0;
}
@media (min-width: 640px) {
	.hb-service-cards {
		grid-template-columns: 1fr 1fr;
		gap: var(--hb-space-24);
	}
}
@media (min-width: 1024px) {
	.hb-service-cards {
		padding-block: var(--hb-space-96);
	}
	/* Scales to 3 columns only once enough services are actually shown
	   here (:has(> :nth-child(5)) — 5th card present) — today's 2 real
	   Homepage services stay in the existing 2-column layout untouched.
	   Prepares for Marble Polishing/Diamond Grinding/Diamond Polishing/
	   Silicate Treatment/Granite Polishing potentially all appearing here
	   later (hb_show_on_homepage) without a tall, narrow 2-wide stack. */
	.hb-service-cards:has(> :nth-child(5)) {
		grid-template-columns: repeat(3, 1fr);
	}
}
.hb-service-card {
	display: flex;
	flex-direction: column;
	height: 100%;
	padding: var(--hb-space-24);
	color: inherit;
	text-decoration: none;
}
/* Specificity bump vs Hello Elementor's theme.css ".page-content a" — was
   latent/unnoticed because [hb_service_cards] had only ever been used on
   the Homepage (Elementor front-page, no .page-content wrapper); reusing
   it on the About page (plain .page-content-wrapped template) exposed it
   (Product Director systems audit, 2026-07-15). Same recurring cascade
   fight as .hb-main .hb-service-areas a elsewhere in this file. */
.hb-main .hb-service-card {
	text-decoration: none;
}
/* Plain prose links (Contact/Gallery/FAQ cross-links, Product Director
   systems audit, 2026-07-15) — Hello Elementor's default .page-content a
   color (#cc3366, a pink unrelated to the brand palette) had never been
   overridden before because no plain in-paragraph link existed anywhere
   on the site until now. Underline stays (correct for inline text
   links) — only the color needs to match the brand. Dedicated class
   instead of a broad ".page-content a" color rule, so this can never
   collide with already-specific component link colors (breadcrumb, etc).
*/
.hb-main .hb-prose-link {
	color: var(--hb-color-brass);
}
.hb-main .hb-prose-link:hover {
	color: var(--hb-color-ink);
}
@media (min-width: 1024px) {
	.hb-service-card {
		padding: var(--hb-space-32);
	}
}
/* Icon + category tag share a row (Premium UI Refinement, 2026-07-13) —
   the icon now carries the category's visual identity (distinct gem vs.
   tile-grid glyph, see housebro_get_service_category_icon()), so it sits
   beside the label instead of stacked above it, reading as one unit that
   answers "what kind of service is this" before the eye even reaches the
   heading. */
/* Service Card media band (2026-08-23 visual pass) — replaces the old
   .hb-service-card-header icon row. A fixed 16:9 band, so cards keep
   identical heights and the grid never goes ragged, and so a real photo
   dropped into hb_service_icon later fills exactly the same space with no
   layout change. Uses the brand's own marble-vein asset as texture rather
   than stock imagery. */
.hb-service-card-media {
	position: relative;
	aspect-ratio: 16 / 9;
	margin-block-end: var(--hb-space-16);
	border-radius: var(--hb-radius-card);
	overflow: hidden;
	display: flex;
	align-items: center;
	justify-content: center;
	border: 1px solid var(--hb-color-divider);
	background:
		url('../images/hero-marble-veins.svg') no-repeat bottom right / 72% auto,
		linear-gradient(135deg, #fff 0%, var(--hb-color-marble-white) 100%);
}
/* A real uploaded photo fills the band edge to edge. */
.hb-service-card-photo {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}
/* The SVG glyph fallback sits centred and much larger than the old 40px
   inline icon — this is what gives the card its visual weight while no
   photography exists. */
.hb-service-card-glyph {
	display: flex;
	align-items: center;
	justify-content: center;
}
.hb-service-card-glyph .hb-service-card-icon {
	width: 64px;
	height: 64px;
}
.hb-service-card-category {
	background: var(--hb-color-divider);
	color: var(--hb-color-stone-grey);
	font-size: 0.75rem;
}
/* Pill overlays the band's top-inline-end corner, freeing the text row it
   used to occupy. Semi-opaque so it stays legible over either a photo or
   the texture. */
.hb-service-card-media .hb-service-card-category {
	position: absolute;
	inset-block-start: var(--hb-space-8);
	inset-inline-end: var(--hb-space-8);
	background: rgba(255, 255, 255, 0.92);
	border: 1px solid var(--hb-color-divider);
	color: var(--hb-color-ink);
}
.hb-service-card-icon {
	width: 40px;
	height: 40px;
	object-fit: contain;
	color: var(--hb-color-brass);
}
.hb-service-card h3 {
	margin-block-end: var(--hb-space-8);
}
/* The term description is real, already-approved copy (see
   housebro_service_cards_shortcode() docblock) that states the actual
   differentiator — inspect-first, condition-matched technique, not a
   fixed package. Styled as a standout line (ink color, not muted grey)
   since it is the card's answer to "why is this premium", not filler
   supporting text. */
.hb-service-card-tagline {
	color: var(--hb-color-ink);
	font-size: var(--hb-font-size-small);
	line-height: var(--hb-line-height-body);
}
/* Technique/step chips (Premium UI Refinement, 2026-07-13) — replaces the
   old "Includes: X • Y • Z" sentence with scannable chips, same real
   hb_process_steps data. A 5-second scan of chip count + labels answers
   "does this cover the technique I need" far faster than reading a
   run-on sentence, and the sheer number of chips on the Marble Restoration
   card (7 real techniques) visually communicates technique breadth — part
   of why HouseBro's one service costs more than a single-technique
   competitor, without any new claim being written. */
.hb-service-card-chips-label {
	margin-block-start: var(--hb-space-16);
	color: var(--hb-color-stone-grey);
	font-size: 0.75rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
}
.hb-service-card-chips {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--hb-space-8);
	margin-block-start: var(--hb-space-8);
}
/* Tile Cleaning's 3 real steps are a genuine sequence (clean, then grout,
   then stains) — a connecting arrow between chips reads as "complete
   process", distinct from the Marble toolkit cluster below which
   deliberately has no arrows (order-independent techniques). */
.hb-service-card-chips--sequence .hb-chip:not(:last-child)::after {
	content: '\2192';
	margin-inline-start: var(--hb-space-8);
	color: var(--hb-color-divider);
	font-weight: 700;
}
.hb-chip {
	display: inline-flex;
	align-items: center;
	gap: var(--hb-space-4);
	padding: var(--hb-space-4) var(--hb-space-16);
	border-radius: var(--hb-radius-pill);
	background: #fff;
	border: 1px solid var(--hb-color-divider);
	color: var(--hb-color-stone-grey);
	font-size: 0.75rem;
	font-weight: 600;
	line-height: 1.4;
}
/* Icon System (Brand Asset Phase, 2026-07-14) — each chip's matching
   technique icon (housebro_get_technique_icon()), sized small and set to
   brass so it reads as an accent mark, not competing with the label. */
.hb-chip .hb-icon {
	width: 14px;
	height: 14px;
	flex-shrink: 0;
	color: var(--hb-color-brass);
}
/* Flagship technique (Execution Mode, Brand Repositioning Batch 1,
   2026-07-14) — Diamond Polishing and Premium Silicate Polishing are
   HouseBro's named flagship expertise. A solid brass fill (vs. the plain
   white/outline chip every other technique gets) makes the two pop out of
   the toolkit cluster at a glance, without adding new copy. */
.hb-chip--flagship {
	background: var(--hb-color-brass);
	border-color: var(--hb-color-brass);
	color: #fff;
}
.hb-chip--flagship .hb-icon {
	color: #fff;
}
.hb-service-card-footer {
	display: inline-flex;
	align-items: center;
	gap: var(--hb-space-4);
	margin-block-start: auto;
	padding-block-start: var(--hb-space-24);
	color: var(--hb-color-brass);
	font-weight: 700;
	font-size: var(--hb-font-size-small);
}

/* Why Choose HouseBro */
.hb-why-choose-grid {
	display: grid;
	gap: var(--hb-space-16);
	grid-template-columns: 1fr;
	list-style: none;
	padding: 0;
	padding-block: var(--hb-space-48);
}
@media (min-width: 640px) {
	.hb-why-choose-grid {
		grid-template-columns: 1fr 1fr;
	}
}
@media (min-width: 1024px) {
	.hb-why-choose-grid {
		grid-template-columns: 1fr 1fr 1fr;
		gap: var(--hb-space-24);
		padding-block: var(--hb-space-96);
	}
}
.hb-why-choose-item {
	display: flex;
	align-items: flex-start;
	gap: var(--hb-space-8);
	padding: var(--hb-space-16);
	background: var(--hb-color-marble-white);
	border-radius: var(--hb-radius-card);
	border-inline-start: 3px solid var(--hb-color-brass);
	font-weight: 600;
	transition: transform var(--hb-transition-duration) var(--hb-transition-easing),
		box-shadow var(--hb-transition-duration) var(--hb-transition-easing);
}
/* Icon System (Brand Asset Phase, 2026-07-14) — the plain Unicode
   checkmark character is now the drawn "Completion" icon from the
   registry, matching the same glyph used in Quality Workflow. */
.hb-why-choose-item .hb-icon {
	width: 18px;
	height: 18px;
	flex-shrink: 0;
	color: var(--hb-color-brass);
	margin-block-start: 2px;
}
.hb-why-choose-item:hover {
	transform: translateY(-2px);
	box-shadow: var(--hb-shadow-resting);
}

/* Before & After preview */
.hb-before-after-grid {
	display: grid;
	gap: var(--hb-space-16);
	grid-template-columns: 1fr;
	padding-block: var(--hb-space-48);
}
@media (min-width: 640px) {
	.hb-before-after-grid {
		grid-template-columns: 1fr 1fr;
		gap: var(--hb-space-24);
	}
}
@media (min-width: 1024px) {
	.hb-before-after-grid {
		padding-block: var(--hb-space-96);
	}
}
/* Premium UI Refinement (2026-07-13) — now shares .hb-card (see markup),
   so real and placeholder pairs both get the same elevated radius/shadow/
   hover-lift as every other premium card on the site, instead of a flatter
   bespoke treatment. Only the padding is pair-specific. */
.hb-before-after-pair {
	padding: var(--hb-space-16);
}
.hb-before-after-images {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: var(--hb-space-8);
	position: relative;
}
/* Icon System (Brand Asset Phase, 2026-07-14) — "Before"/"After" are now
   real markup with a matching drawn icon (housebro_get_icon('before')/
   ('after'), aliased to the Inspection/Completion glyphs), not a
   CSS-generated text-only pseudo-element. Absolutely positioned exactly
   like the pseudo-elements they replace, so they sit outside the 2-column
   image grid regardless of DOM order. */
.hb-before-after-label {
	position: absolute;
	top: var(--hb-space-8);
	z-index: 1;
	display: inline-flex;
	align-items: center;
	gap: var(--hb-space-4);
	background: var(--hb-color-ink);
	color: #fff;
	font-size: var(--hb-font-size-small);
	font-weight: 600;
	padding: var(--hb-space-4) var(--hb-space-8);
	border-radius: var(--hb-radius-pill);
}
.hb-before-after-label .hb-icon {
	width: 12px;
	height: 12px;
}
.hb-before-after-label--before {
	inset-inline-start: var(--hb-space-8);
}
.hb-before-after-label--after {
	inset-inline-end: var(--hb-space-8);
}
.hb-before-after-images img {
	width: 100%;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	border-radius: var(--hb-radius-card);
}
.hb-before-after-caption {
	margin-block-start: var(--hb-space-8);
	color: var(--hb-color-stone-grey);
	font-size: var(--hb-font-size-small);
}

/* Placeholder tiles (Batch 6, 2026-07-12; illustrations replaced Brand
   Asset Phase, 2026-07-14) — previews the real before/after layout above
   (same .hb-before-after-pair/-images classes, so the "Before"/"After"
   pill labels already show correctly). housebro_get_before_illustration()
   / housebro_get_after_illustration() fill the exact same frame a real
   uploaded photo would (width:100%, 4:3, same border-radius) — sized like
   content, not floated like an icon, so an unfinished portfolio still
   looks like a portfolio. Solid card border, not dashed — a "coming
   soon" state should still read as intentional/premium, not unfinished. */
.hb-restoration-illustration {
	display: block;
	width: 100%;
	aspect-ratio: 4 / 3;
	border-radius: var(--hb-radius-card);
}
/* The caption below the placeholder grid just needs to read as a simple
   note, not its own separate framed card — the grid above already
   provides that framing, so the default .hb-empty-state dashed-card
   treatment is switched off here specifically. */
/* "Sample illustration" badge on placeholder Before/After tiles
   (2026-08-23) — deliberately quiet: muted stone-grey on the marble-white
   surface, normal weight, so it states the disclaimer clearly without
   competing with the Before/After labels or making the section look
   broken. Reuses .hb-badge-pill's existing pill geometry rather than
   introducing a second badge shape. */
.hb-sample-badge {
	display: block;
	margin-block-start: var(--hb-space-8);
	background: var(--hb-color-marble-white);
	border: 1px solid var(--hb-color-divider);
	color: var(--hb-color-stone-grey);
	font-weight: 500;
	text-align: center;
}

.hb-before-after-grid--placeholder + .hb-empty-state {
	max-width: none;
	border: none;
	padding: 0;
	margin-block-start: var(--hb-space-16);
}
.hb-before-after-grid--placeholder + .hb-empty-state::before {
	content: none;
}

/* Quality Workflow (Batch 17, 2026-07-12; icons + connecting line added
   Premium UI Refinement Batch 3, 2026-07-13) — [hb_quality_workflow].
   Each stage now shows a distinct pictogram (magnifying glass, polishing
   pad, checklist, two people, checkmark) instead of a bare number, and a
   line connects the 5 circles into one continuous flow — Inspection →
   Restoration → Self-Inspection → Walkthrough → Completion reads as a
   single deliberate sequence, not five unrelated bullet points. Still no
   elaborated marketing copy, just the stage names and the flow itself. */
.hb-quality-workflow {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: var(--hb-space-24);
	list-style: none;
	padding: 0;
	margin: 0 0 var(--hb-space-48);
}
.hb-quality-workflow li {
	position: relative;
	z-index: 1;
	display: flex;
	align-items: center;
	gap: var(--hb-space-16);
	font-weight: 600;
}
.hb-quality-workflow li:not(:last-child)::after {
	content: '';
	position: absolute;
	inset-inline-start: 15px;
	top: 32px;
	width: 2px;
	height: calc(100% - 32px + var(--hb-space-24));
	background: var(--hb-color-divider);
	z-index: -1;
}
.hb-quality-workflow-icon {
	flex-shrink: 0;
	width: 32px;
	height: 32px;
	border-radius: 50%;
	background: var(--hb-color-brass);
	color: var(--hb-color-ink);
	display: flex;
	align-items: center;
	justify-content: center;
}
.hb-quality-workflow-icon svg {
	width: 18px;
	height: 18px;
}
@media (min-width: 1024px) {
	.hb-quality-workflow {
		flex-direction: row;
		gap: var(--hb-space-16);
	}
	.hb-quality-workflow li {
		flex-direction: column;
		flex: 1;
		text-align: center;
		gap: var(--hb-space-8);
	}
	.hb-quality-workflow li:not(:last-child)::after {
		inset-inline-start: 50%;
		top: 16px;
		width: 100%;
		height: 2px;
	}
}

/* Process Brief */
.hb-process-brief {
	display: grid;
	gap: var(--hb-space-32);
	grid-template-columns: 1fr;
	padding-block: var(--hb-space-48);
}
@media (min-width: 640px) {
	.hb-process-brief {
		grid-template-columns: 1fr 1fr;
	}
}
@media (min-width: 1024px) {
	.hb-process-brief {
		padding-block: var(--hb-space-96);
	}
}
.hb-process-brief-service h3 {
	margin-block-end: var(--hb-space-16);
}
.hb-process-brief-service ol {
	list-style: none;
	padding-inline-start: 0;
}
.hb-process-brief-service li {
	margin-block-end: var(--hb-space-16);
	padding-inline-start: var(--hb-space-48);
	position: relative;
}
/* Icon System (Brand Asset Phase, 2026-07-14) — the badge now shows the
   real matching technique icon (housebro_get_technique_icon()) instead of
   a bare step number, same brass-circle size/position as before. */
.hb-process-brief-icon {
	position: absolute;
	inset-inline-start: 0;
	top: 0;
	width: 32px;
	height: 32px;
	border-radius: 50%;
	background: var(--hb-color-brass);
	color: var(--hb-color-ink);
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 1;
}
.hb-process-brief-icon .hb-icon {
	width: 18px;
	height: 18px;
}
.hb-process-brief-service li:not(:last-child)::after {
	content: '';
	position: absolute;
	inset-inline-start: 15px;
	top: 32px;
	width: 2px;
	height: calc(100% - 32px + var(--hb-space-16));
	background: var(--hb-color-divider);
}

/* --------------------------------------------------------------------------
   Process Brief with ONE service - city pages (2026-08-31)

   [hb_process_brief] was built for the Homepage, where it prints two
   services side by side in the two-column grid above. A city page passes
   service="..." and therefore always renders exactly one child, so on
   every city page the band spent half its width on an empty column and,
   at >=1024px, 96px of padding above and below three short lines. It was
   the emptiest section on the page.

   Scoped with :has(> ... :only-child) so it can ONLY ever apply to the
   single-service case. The Homepage's two-column rendering is untouched,
   and so is this component's markup - no PHP change, no extra class, no
   second component.

   :has() is already load-bearing in this file (.hb-hero:has(.hb-hero-bg)
   drives the entire two-column hero), so this introduces no new support
   requirement.

   From 768px the steps become a horizontal run instead of a stacked
   list: icons on a row, the connecting rule between them turned from
   vertical to horizontal. That is the shape a process actually has, it
   fills the band it is given, and it matches the step strip in the
   supplied Pune reference. Below 768px nothing changes - the stacked
   list with its vertical spine is already the right answer on a phone.
   -------------------------------------------------------------------------- */
.hb-process-brief:has(> .hb-process-brief-service:only-child) {
	grid-template-columns: minmax(0, 1fr);
	padding-block: var(--hb-space-48);
}

@media (min-width: 768px) {
	.hb-process-brief:has(> .hb-process-brief-service:only-child) .hb-process-brief-service ol {
		display: grid;
		/* auto-flow rather than repeat(3): the shortcode slices to three
		   steps today, but a service with two would leave a hole in a
		   fixed three-track grid. */
		grid-auto-flow: column;
		grid-auto-columns: minmax(0, 1fr);
		gap: var(--hb-space-24);
		margin: 0;
	}
	.hb-process-brief:has(> .hb-process-brief-service:only-child) .hb-process-brief-service li {
		/* The stacked list indents past a left-anchored icon; here the icon
		   sits above the label instead, so the indent becomes headroom. */
		padding-inline-start: 0;
		padding-block-start: 44px;
		margin-block-end: 0;
		text-align: center;
	}
	.hb-process-brief:has(> .hb-process-brief-service:only-child) .hb-process-brief-icon {
		inset-inline-start: 50%;
		transform: translateX(-50%);
	}
	/* The spine, rotated. Runs from 20px past this icon's centre to 20px
	   short of the next one - the icon is 32px across, so that leaves an
	   even 4px of air at each end rather than touching the glyphs. */
	.hb-process-brief:has(> .hb-process-brief-service:only-child) .hb-process-brief-service li:not(:last-child)::after {
		top: 15px;
		inset-inline-start: calc(50% + 20px);
		inset-inline-end: calc(-50% - var(--hb-space-24) + 20px);
		width: auto;
		height: 2px;
	}
}

/* Areas We Serve */
/* Continuous-story pass (Batch 15, 2026-07-12) — a pill list is
   inherently compact (true at 4 cities, still true at today's 6, and
   still true after future city additions — it never grows "dense" the
   way a card grid does), so it doesn't need the same padding-block as
   content-heavy sections. Full section padding here left it looking like
   an unintentional gap in the page rather than a deliberate beat in the
   story; this tightens that transition specifically, not the shared
   section-rhythm scale used everywhere else. */
.hb-areas-we-serve {
	display: flex;
	flex-wrap: wrap;
	gap: var(--hb-space-16);
	list-style: none;
	padding: 0;
	padding-block: var(--hb-space-24);
}
@media (min-width: 1024px) {
	.hb-areas-we-serve {
		padding-block: var(--hb-space-48);
	}
}
.hb-areas-we-serve-item {
	padding: var(--hb-space-8) var(--hb-space-24);
	background: var(--hb-color-marble-white);
	border-radius: var(--hb-radius-pill);
	transition: background var(--hb-transition-duration) var(--hb-transition-easing);
}
.hb-areas-we-serve-item:has(a):hover {
	background: var(--hb-color-divider);
}
.hb-areas-we-serve-item a {
	/* Fills the LI's full padded area via negative-margin reclaim, so the
	   entire visible pill is tappable — previously only the text glyphs
	   inside it were, a real tap-target-vs-visual-size mismatch. */
	display: block;
	margin: calc(var(--hb-space-8) * -1) calc(var(--hb-space-24) * -1);
	padding: var(--hb-space-8) var(--hb-space-24);
	min-height: 28px;
	color: var(--hb-color-ink);
	text-decoration: none;
	font-weight: 600;
}

/* Pricing Cards */
.hb-pricing-cards {
	display: grid;
	gap: var(--hb-space-16);
	grid-template-columns: 1fr;
	padding-block: var(--hb-space-48);
	list-style: none;
	margin: 0;
	padding-inline-start: 0;
}
@media (min-width: 640px) {
	.hb-pricing-cards {
		grid-template-columns: 1fr 1fr;
		gap: var(--hb-space-24);
	}
}
@media (min-width: 1024px) {
	.hb-pricing-cards {
		padding-block: var(--hb-space-96);
	}
	.hb-pricing-cards:has(> :nth-child(5)) {
		grid-template-columns: repeat(3, 1fr);
	}
}
.hb-pricing-card {
	display: flex;
	flex-direction: column;
	height: 100%;
	padding: var(--hb-space-24);
}
@media (min-width: 1024px) {
	.hb-pricing-card {
		padding: var(--hb-space-32);
	}
}
.hb-pricing-card-price {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: var(--hb-space-8);
	margin-block: var(--hb-space-16);
	font-size: 1.25rem;
	font-weight: 700;
}
.hb-price-original {
	text-decoration: line-through;
	color: var(--hb-color-stone-grey);
	font-weight: 400;
	font-size: 1rem;
}
.hb-price-discounted {
	color: var(--hb-color-success-green);
}
.hb-offer-badge {
	background: var(--hb-color-brass);
	color: #fff;
}
.hb-pricing-card-included {
	list-style: none;
	padding: 0;
	margin-block-end: var(--hb-space-24);
}
.hb-pricing-card-included li {
	padding-block: var(--hb-space-4);
}
.hb-pricing-card-included li::before {
	content: '\2713 ';
	color: var(--hb-color-success-green);
}
.hb-pricing-card-cta {
	display: flex;
	flex-wrap: wrap;
	gap: var(--hb-space-8);
	/* Pushes the CTA row to the bottom of the card regardless of how much
	   "What's Included" content precedes it, so CTAs align across a row of
	   cards even when their content lengths differ. */
	margin-block-start: auto;
	padding-block-start: var(--hb-space-16);
}
.hb-pricing-card-cta .hb-btn {
	flex: 1 1 140px;
	max-width: 280px;
	min-height: 52px;
	box-shadow: var(--hb-shadow-resting);
}
/* The empty-state fallback (no card wrapper around it) needs its own
   centering — inside a real Pricing Card the card's own width already
   constrains this row. */
.hb-main .hb-empty-state + .hb-pricing-card-cta {
	justify-content: center;
}

/* Service Page shortcodes (Milestone 5) — inc/service-page-shortcodes.php */

.hb-benefits-grid {
	display: grid;
	gap: var(--hb-space-16);
	grid-template-columns: 1fr;
	padding-block: var(--hb-space-48);
	list-style: none;
	margin: 0;
	padding-inline-start: 0;
}
@media (min-width: 640px) {
	.hb-benefits-grid {
		grid-template-columns: 1fr 1fr;
		gap: var(--hb-space-24);
	}
}
@media (min-width: 1024px) {
	.hb-benefits-grid {
		padding-block: var(--hb-space-96);
	}
}
/* Design System Lock (Batch 14, 2026-07-12) — one card style sitewide.
   .hb-benefit-item previously had no background/border/shadow at all
   (a bare list item) while Service Cards and Pricing Cards elsewhere use
   .hb-card; now shares the exact same visual language and padding scale. */
.hb-benefit-item {
	padding: var(--hb-space-24);
}
@media (min-width: 1024px) {
	.hb-benefit-item {
		padding: var(--hb-space-32);
	}
}
.hb-benefit-item h3 {
	margin-block-end: var(--hb-space-8);
}
.hb-benefit-item p {
	color: var(--hb-color-stone-grey);
}
/* Icon System applied to Benefits (Execution Mode, Brand Repositioning
   Batch 2, 2026-07-14) — this was the one remaining plain, icon-less card
   block on the Service Pillar pages, reading as "template" next to the
   icon-rich Service Card / Process / Quality Workflow sections around it.
   Same brass-circle language as .hb-process-brief-icon /
   .hb-service-process-icon, sized up slightly since this card has more
   room and no connecting line to align with. */
.hb-benefit-icon {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	border-radius: 50%;
	background: var(--hb-color-brass);
	color: var(--hb-color-ink);
	margin-block-end: var(--hb-space-16);
}
.hb-benefit-icon .hb-icon {
	width: 20px;
	height: 20px;
}
/* City Quick Select (UX Audit, Finding 2, 2026-07-15) — a slim wayfinding
   strip right under the Pillar Hero, for visitors who already know their
   city and would otherwise scroll past 8 sections to reach the full Areas
   We Serve list. */
.hb-city-quick-select {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--hb-space-8) var(--hb-space-16);
	padding-block: var(--hb-space-24);
	border-block-end: 1px solid var(--hb-color-divider);
	margin-block-end: var(--hb-space-24);
}
.hb-city-quick-select-label {
	font-weight: 700;
	color: var(--hb-color-ink);
	font-size: var(--hb-font-size-small);
}
/* nowrap only where there is room for it (2026-08-23 mobile audit). This
   label interpolates the service name — "Find Marble Polishing near you:"
   fits at 320px, but "Find Granite Grinding & Polishing near you:" does
   not, and an unbreakable label is one of the few things that can force
   horizontal page scroll rather than just looking cramped. Above 640px
   the nowrap still does its job of keeping the label on one line. */
@media (min-width: 640px) {
	.hb-city-quick-select-label {
		white-space: nowrap;
	}
}
.hb-city-quick-select-list {
	display: flex;
	flex-wrap: wrap;
	gap: var(--hb-space-8);
	list-style: none;
	margin: 0;
	padding: 0;
}
.hb-city-quick-select-list a {
	display: inline-flex;
	padding: var(--hb-space-4) var(--hb-space-16);
	border-radius: var(--hb-radius-pill);
	background: #fff;
	border: 1px solid var(--hb-color-divider);
	color: var(--hb-color-ink);
	font-weight: 600;
	font-size: 0.8125rem;
	transition: border-color var(--hb-transition-duration) var(--hb-transition-easing),
		color var(--hb-transition-duration) var(--hb-transition-easing);
}
/* Specificity bump vs Hello Elementor's theme.css ".page-content a"
   (same recurring cascade fight as .hb-main .hb-service-areas a above). */
.hb-main .hb-city-quick-select-list a {
	text-decoration: none;
}

/* Cities We Serve (UX Audit, Finding — Homepage city discovery,
   2026-07-15) — same card grid rhythm as .hb-benefits-grid/.hb-service-cards. */
.hb-city-cards {
	display: grid;
	gap: var(--hb-space-16);
	grid-template-columns: 1fr;
	list-style: none;
	margin: 0;
	padding: 0;
}
@media (min-width: 640px) {
	.hb-city-cards {
		grid-template-columns: 1fr 1fr;
		gap: var(--hb-space-24);
	}
}
@media (min-width: 1024px) {
	.hb-city-cards {
		grid-template-columns: repeat(3, 1fr);
	}
}
.hb-city-card {
	padding: var(--hb-space-24);
	display: flex;
	flex-direction: column;
}
/* Real-photo-ready image slot — fixed circle, overflow hidden, so a
   future <img src="..." alt="..."> dropped in here fills the exact same
   space an object-fit: cover photo would, no layout change needed. */
.hb-city-card-image {
	width: 64px;
	height: 64px;
	border-radius: 50%;
	overflow: hidden;
	background: var(--hb-color-brass);
	color: var(--hb-color-ink);
	display: flex;
	align-items: center;
	justify-content: center;
	margin-block-end: var(--hb-space-16);
	flex-shrink: 0;
}
.hb-city-card-image .hb-icon {
	width: 28px;
	height: 28px;
}
.hb-city-card h3 {
	margin-block-end: var(--hb-space-8);
}
.hb-city-card-services {
	display: flex;
	flex-wrap: wrap;
	gap: var(--hb-space-8);
	list-style: none;
	margin: 0;
	padding: 0;
}
.hb-city-card-services a {
	display: inline-flex;
	padding: var(--hb-space-4) var(--hb-space-16);
	border-radius: var(--hb-radius-pill);
	background: #fff;
	border: 1px solid var(--hb-color-divider);
	color: var(--hb-color-ink);
	font-weight: 600;
	font-size: 0.8125rem;
}
.hb-city-card-services a:hover {
	border-color: var(--hb-color-brass);
	color: var(--hb-color-brass);
}
/* Specificity bump vs Hello Elementor's theme.css ".page-content a". */
.hb-main .hb-city-card-services a {
	text-decoration: none;
}

/* ==========================================================================
   SERVICES WE PROVIDE IN {CITY} (city-first IA, 2026-08-31)

   Was a compact "also available" strip of icon+name rows. It is now the
   site's primary service-discovery component — the header has no Services
   menu, so this is where a visitor learns what HouseBro does in the city
   they chose. It has to carry a service's icon, name, description, real
   sub-services and a CTA, and still look like the rest of the site.

   ONE DESIGN, NOT TWO. The mobile layout is the desktop layout with fewer
   columns: same card, same internal order, same type scale steps, same
   chips. Nothing is hidden on one and shown on the other.

   COLUMN COUNTS ARE EXPLICIT, never auto-fit. auto-fit with a minmax
   floor was what the old strip used, and with a description and a chip
   row inside, a long service name could push one track wider than its
   sibling — cards in the same row rendering at different widths. Every
   breakpoint below states repeat(N, minmax(0, 1fr)); the minmax(0,...) is
   what stops a long chip from widening its own track and, through it, the
   page.

   Two columns is the deliberate maximum. Every market currently offers
   exactly two services, and a 3- or 4-up grid would leave a visible hole
   beside them. A third service added later fills the second row.
   ========================================================================== */
.hb-city-services-available {
	display: grid;
	gap: var(--hb-space-16);
	grid-template-columns: minmax(0, 1fr);
	list-style: none;
	margin: 0 0 var(--hb-space-32);
	padding: 0;
}
@media (min-width: 768px) {
	.hb-city-services-available {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: var(--hb-space-24);
	}
}

/* The card is a column, not a row: icon+name, then description, then the
   feature chips, then the CTA pinned to the bottom edge. Pinning the CTA
   is what keeps two cards of unequal text length reading as a matched
   pair — without it the arrows land at different heights and the row
   looks broken. */
.hb-city-service-card {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--hb-space-8);
	padding: var(--hb-space-24);
	color: var(--hb-color-ink);
	height: 100%;
}
@media (min-width: 1024px) {
	.hb-city-service-card {
		padding: var(--hb-space-32);
	}
}

/* Icon and name sit on one line and stay on it — the icon never wraps
   under the name, and a long service name wraps beside the icon rather
   than pushing it out of the card. */
.hb-city-service-head {
	display: flex;
	align-items: center;
	gap: var(--hb-space-16);
	min-width: 0;
	width: 100%;
}
.hb-city-service-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	width: 48px;
	height: 48px;
	border-radius: var(--hb-radius-card);
	background: #fff;
	border: 1px solid var(--hb-color-divider);
	color: var(--hb-color-brass);
}
.hb-city-service-icon .hb-icon {
	width: 28px;
	height: 28px;
	flex-shrink: 0;
}
.hb-city-service-name {
	display: block;
	min-width: 0;
	font-size: var(--hb-font-size-h4);
	font-weight: 700;
	line-height: 1.3;
	/* A two-word service name must be allowed to break rather than force
	   the card wider than its track. */
	overflow-wrap: anywhere;
}

.hb-city-service-desc {
	display: block;
	color: var(--hb-color-stone-grey);
	font-size: var(--hb-font-size-small);
	line-height: 1.55;
}

/* Feature chips — the hb_sub_services rows. They WRAP, never scroll: this
   is a list of what a service includes, and a horizontally scrolling list
   hides half of it behind a gesture nobody is prompted to make. */
.hb-city-service-features {
	display: flex;
	flex-wrap: wrap;
	gap: var(--hb-space-4);
	width: 100%;
	min-width: 0;
	margin-block-start: var(--hb-space-4);
}
.hb-city-service-feature {
	/* Tighter than a stock .hb-chip: eleven of these sit in one card, so
	   the pill padding that suits a lone technique chip would turn the
	   block into a wall. */
	padding: 2px var(--hb-space-8);
	font-size: 0.6875rem;
	max-width: 100%;
	overflow-wrap: anywhere;
}

/* margin-block-start:auto is the pin — it eats whatever vertical slack
   the shorter card has, so both CTAs sit on the same line. */
.hb-city-service-cta {
	display: block;
	margin-block-start: auto;
	padding-block-start: var(--hb-space-8);
	color: var(--hb-color-brass);
	font-size: var(--hb-font-size-small);
	font-weight: 600;
}
.hb-city-service-current-label {
	display: block;
	margin-block-start: auto;
	padding-block-start: var(--hb-space-8);
	color: var(--hb-color-stone-grey);
	font-size: var(--hb-font-size-small);
	font-weight: 600;
}
/* A card with no description AND no features yet — a service whose copy
   has not been entered. Cards in a row stretch to match their tallest
   sibling, so an empty one pinned its CTA to the bottom and left a
   visible hole in the middle of an otherwise finished row.

   Centring the content turns that into breathing room rather than a gap:
   the card still matches its sibling's height (they are a matched pair
   and must stay one), but nothing is pushed to an edge with emptiness
   above it. The moment a description or a sub-service is entered in
   wp-admin the selector stops matching and the normal pinned layout
   returns, with no CSS change.

   :has() is already the technique used for the Hero's photo variants in
   this file, so this is the established pattern here, not a new one. */
.hb-city-service-card:not(:has(.hb-city-service-desc)):not(:has(.hb-city-service-features)) {
	justify-content: center;
}
.hb-city-service-card:not(:has(.hb-city-service-desc)):not(:has(.hb-city-service-features)) .hb-city-service-cta,
.hb-city-service-card:not(:has(.hb-city-service-desc)):not(:has(.hb-city-service-features)) .hb-city-service-current-label {
	margin-block-start: 0;
}

.hb-city-service-item--current .hb-city-service-card {
	border-color: var(--hb-color-brass);
	background: var(--hb-color-marble-white);
}
/* The current card is a <span>, not a link — it must not lift or glow on
   hover the way its siblings do, or it reads as clickable. */
.hb-city-service-item--current .hb-city-service-card:hover {
	box-shadow: var(--hb-shadow-premium-resting);
	transform: none;
	border-color: var(--hb-color-brass);
}
.hb-main .hb-city-service-card {
	text-decoration: none;
}

/* ==========================================================================
   SERVICE OFFERINGS GRID — [hb_service_offerings] (2026-08-31)

   "Our Interior Painting Services in Pune": one icon tile per
   sub-service. This is the band that turns a service page from a
   paragraph into a catalogue a visitor can scan in five seconds.

   TWO COLUMNS FROM 320px, not one. A ten-item single-column list is a
   very tall page on a phone, and the tiles do not need the width: the
   approved mobile catalogue already runs tiles at ~83px wide, while two
   columns at 320px gives each one 140px. So the phone gets the same grid
   the desktop gets, at a density the design language already uses —
   which is the whole "same website, fewer columns" requirement rather
   than a separate mobile treatment.

   Explicit repeat(N, minmax(0, 1fr)) at every step, never auto-fit: a
   long label like "Complete Home Painting" must wrap inside its track,
   never widen it and leave its neighbours narrower.
   ========================================================================== */
.hb-offerings-grid {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: var(--hb-space-8);
	list-style: none;
	margin: 0;
	padding: 0;
}
@media (min-width: 768px) {
	.hb-offerings-grid {
		grid-template-columns: repeat(3, minmax(0, 1fr));
		gap: var(--hb-space-16);
	}
}
@media (min-width: 1024px) {
	.hb-offerings-grid {
		grid-template-columns: repeat(4, minmax(0, 1fr));
	}
}

.hb-offering-card {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: flex-start;
	gap: var(--hb-space-8);
	padding: var(--hb-space-16) var(--hb-space-8);
	text-align: center;
	min-width: 0;
}
@media (min-width: 768px) {
	.hb-offering-card {
		padding: var(--hb-space-24) var(--hb-space-16);
	}
}
/* No hover lift: these tiles are not links (the sub-services have no
   pages of their own), so they must not offer the affordance the
   clickable .hb-card siblings do. */
.hb-offering-card:hover {
	box-shadow: var(--hb-shadow-premium-resting);
	transform: none;
	border-color: var(--hb-color-divider);
}
.hb-offering-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	width: 44px;
	height: 44px;
	border-radius: var(--hb-radius-card);
	background: #fff;
	border: 1px solid var(--hb-color-divider);
	color: var(--hb-color-brass);
}
.hb-offering-icon .hb-icon {
	width: 26px;
	height: 26px;
}
.hb-offering-name {
	font-size: var(--hb-font-size-small);
	font-weight: 700;
	line-height: 1.35;
	color: var(--hb-color-ink);
	/* A single long word must break rather than push the track wider. */
	overflow-wrap: anywhere;
}
.hb-offering-note {
	font-size: 0.75rem;
	line-height: 1.4;
	color: var(--hb-color-stone-grey);
}
@media (max-width: 359.98px) {
	.hb-offering-card {
		padding: var(--hb-space-16) var(--hb-space-4);
	}
	.hb-offering-icon {
		width: 38px;
		height: 38px;
	}
	.hb-offering-icon .hb-icon {
		width: 22px;
		height: 22px;
	}
}

/* ==========================================================================
   OFFERING CARDS WITH PHOTOGRAPHY - .hb-offerings-grid--media (2026-08-31)

   The Pune image pack gave the painting and plumbing offerings real
   trade photography, so those grids stop being an icon list and become a
   card grid: a 16:9 frame bleeding to the card edges, the offering name
   underneath as live text.

   TWO THINGS THIS DELIBERATELY DOES NOT DO.

   1. It does not apply everywhere. The modifier is only emitted when a
      row in THAT grid resolved a photograph, so Marble Polishing and
      Tile Cleaning keep the centred-glyph tile they already have. No NCR
      page changes.
   2. It does not leave the un-photographed rows looking broken. "2 BHK"
      and "Repainting" have no honest photograph, so they get the same
      16:9 frame filled with a brand-tinted panel and their own glyph -
      identical footprint, deliberately quieter. A grid that mixed
      photographs with bare 44px glyphs would read as half-loaded.

   Left-aligned, unlike the centred glyph tile. A caption under a picture
   is read from its left edge; centring it under an image whose subject
   is off-centre looks like a mistake.
   ========================================================================== */
.hb-offerings-disclaimer {
	margin: 0 0 var(--hb-space-16);
	font-size: 0.75rem;
	line-height: 1.5;
	color: var(--hb-color-stone-grey);
}

.hb-offering-card--media {
	/* Zero padding at the top so the frame reaches the card's own rounded
	   corners; overflow clips it to them. */
	padding: 0 0 var(--hb-space-16);
	gap: var(--hb-space-8);
	align-items: stretch;
	text-align: left;
	overflow: hidden;
}

.hb-offering-media {
	display: block;
	/* Every crop in housebro_get_offering_image() is exported at 960x540,
	   so 16:9 here crops nothing that the crop itself did not already
	   decide. */
	aspect-ratio: 16 / 9;
	background: var(--hb-color-marble-white);
	border-block-end: 1px solid var(--hb-color-divider);
}
.hb-offering-media img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* The no-photograph case. Brand tint rather than flat white, so the tile
   still reads as a deliberate panel and not as a failed image.

   THE BADGE IS SIZED UP TO 64px HERE, from the 44px it uses on the flat
   icon tile, and that is not decoration. At 44px the glyph sat as a
   speck in a 16:9 frame and the tile read as an image that had failed to
   load - the precise impression this panel exists to prevent. Filling
   roughly a third of the frame's height makes it read as a chosen mark.

   Reduced again below 360px, where the frame itself is only ~90px tall
   and a 64px badge would crowd it. */
.hb-offering-media--glyph {
	display: flex;
	align-items: center;
	justify-content: center;
	background: linear-gradient(135deg, var(--hb-color-marble-white) 0%, var(--hb-color-divider) 100%);
}
.hb-offering-media--glyph .hb-offering-icon {
	width: 64px;
	height: 64px;
	box-shadow: var(--hb-shadow-premium-resting);
}
.hb-offering-media--glyph .hb-offering-icon .hb-icon {
	width: 36px;
	height: 36px;
}
@media (max-width: 359.98px) {
	.hb-offering-media--glyph .hb-offering-icon {
		width: 48px;
		height: 48px;
	}
	.hb-offering-media--glyph .hb-offering-icon .hb-icon {
		width: 28px;
		height: 28px;
	}
}

.hb-offering-card--media .hb-offering-name,
.hb-offering-card--media .hb-offering-note {
	padding-inline: var(--hb-space-8);
}
@media (min-width: 768px) {
	.hb-offering-card--media {
		padding: 0 0 var(--hb-space-16);
	}
	.hb-offering-card--media .hb-offering-name,
	.hb-offering-card--media .hb-offering-note {
		padding-inline: var(--hb-space-16);
	}
}

/* ==========================================================================
   WHAT WE TAKE CARE OF — [hb_service_care] (2026-08-31)

   Checklist left, illustrative drawing right. One column on a phone with
   the list first, because the list is the content and the drawing is
   support — a visitor scrolling past on a 320px screen should reach the
   words before the picture.
   ========================================================================== */
.hb-care {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: var(--hb-space-24);
	align-items: center;
}
@media (min-width: 900px) {
	.hb-care {
		grid-template-columns: minmax(0, 1fr) minmax(0, 0.9fr);
		gap: var(--hb-space-64);
	}
	/* With no drawing the checklist should not sit in a half-width column
	   with a hole beside it. */
	.hb-care--no-media {
		grid-template-columns: minmax(0, 1fr);
	}
}
.hb-care-list {
	display: grid;
	gap: var(--hb-space-8);
	list-style: none;
	margin: 0;
	padding: 0;
}
@media (min-width: 560px) {
	.hb-care-list {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: var(--hb-space-8) var(--hb-space-24);
	}
}
@media (min-width: 900px) {
	.hb-care-list {
		grid-template-columns: minmax(0, 1fr);
	}
}
.hb-care-list li {
	display: flex;
	align-items: center;
	gap: var(--hb-space-8);
	min-width: 0;
	padding: var(--hb-space-8) 0;
	font-weight: 600;
	color: var(--hb-color-ink);
	border-block-end: 1px solid var(--hb-color-divider);
}
.hb-care-check {
	display: inline-flex;
	flex-shrink: 0;
	color: var(--hb-color-brass);
}
.hb-care-check .hb-icon {
	width: 20px;
	height: 20px;
}
.hb-care-media img {
	display: block;
	width: 100%;
	height: auto;
	border-radius: var(--hb-radius-card-lg);
	border: 1px solid var(--hb-color-divider);
	background: var(--hb-color-marble-white);
}

/* 320-374px. The narrowest phones get a tighter card rather than a
   different one: same order, same elements, less padding. */
@media (max-width: 374.98px) {
	.hb-city-service-card {
		padding: var(--hb-space-16);
		gap: var(--hb-space-4);
	}
	.hb-city-service-icon {
		width: 40px;
		height: 40px;
	}
	.hb-city-service-icon .hb-icon {
		width: 24px;
		height: 24px;
	}
	.hb-city-service-head {
		gap: var(--hb-space-8);
	}
	.hb-city-service-name {
		font-size: var(--hb-font-size-body);
	}
}
.hb-city-quick-select-list a:hover {
	border-color: var(--hb-color-brass);
	color: var(--hb-color-brass);
}

/* Premium consistency pass (2026-07-13) — [hb_service_process] (pillar
   pages' full process list) was plain browser-default numbering (1. 2. 3.)
   with no padding-block, while every other numbered sequence on the site
   (.hb-process-brief-service, .hb-quality-workflow) uses brass numbered
   circles. Same visual language now, one icon style sitewide. */
.hb-service-process {
	list-style: none;
	padding-inline-start: 0;
	padding-block: var(--hb-space-48);
}
@media (min-width: 1024px) {
	.hb-service-process {
		padding-block: var(--hb-space-96);
	}
}
.hb-service-process-step {
	margin-block-end: var(--hb-space-24);
	padding-inline-start: var(--hb-space-48);
	position: relative;
}
/* Icon System (Brand Asset Phase, 2026-07-14) — the matching technique
   icon (housebro_get_technique_icon()) replaces the bare step number,
   same brass-circle size/position as before. Reads as "this is what
   Diamond Grinding/Honing/etc. actually looks like" rather than an
   arbitrary sequence number. */
.hb-service-process-icon {
	position: absolute;
	inset-inline-start: 0;
	top: 0;
	width: 32px;
	height: 32px;
	border-radius: 50%;
	background: var(--hb-color-brass);
	color: var(--hb-color-ink);
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 1;
}
.hb-service-process-icon .hb-icon {
	width: 18px;
	height: 18px;
}
/* Connecting line between numbered circles (Premium UI Refinement,
   2026-07-13) — visually ties each technique into one continuous,
   deliberate process instead of a loose list of separate items, which is
   the "craftsmanship and quality control" read the numbered-checklist
   style alone didn't give. */
.hb-service-process-step:not(:last-child)::after {
	content: '';
	position: absolute;
	inset-inline-start: 15px;
	top: 32px;
	width: 2px;
	height: calc(100% - 32px + var(--hb-space-24));
	background: var(--hb-color-divider);
}
.hb-service-process-step p {
	color: var(--hb-color-stone-grey);
	margin-block-start: var(--hb-space-4);
}
/* Flagship technique (Execution Mode, Brand Repositioning Batch 1,
   2026-07-14) — Diamond Polishing and Premium Silicate Polishing get a
   brass halo ring on their icon plus an explicit "Flagship Technique"
   tag, so the two named flagship expertise areas read as distinct from
   the other 5 steps in the same list, without restructuring the list. */
.hb-service-process-step--flagship .hb-service-process-icon {
	box-shadow: 0 0 0 5px rgba(156, 122, 60, 0.18);
}
.hb-flagship-badge {
	display: inline-block;
	margin-inline-start: var(--hb-space-8);
	padding: 2px var(--hb-space-8);
	border-radius: var(--hb-radius-pill);
	background: var(--hb-color-brass);
	color: #fff;
	font-size: 0.625rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	vertical-align: middle;
}

/* Inspection-first framing (Premium UI Refinement, 2026-07-13) — see
   housebro_service_process_shortcode(). */
.hb-process-intro {
	color: var(--hb-color-ink);
	max-width: 640px;
	margin-block-end: var(--hb-space-16);
}
.hb-process-criteria {
	display: flex;
	flex-wrap: wrap;
	gap: var(--hb-space-8);
	list-style: none;
	padding: 0;
	margin: 0 0 var(--hb-space-32);
}
.hb-process-criteria li {
	display: inline-flex;
	align-items: center;
	gap: var(--hb-space-8);
	padding: var(--hb-space-8) var(--hb-space-16);
	border-radius: var(--hb-radius-pill);
	background: var(--hb-color-marble-white);
	border: 1px solid var(--hb-color-divider);
	color: var(--hb-color-ink);
	font-weight: 600;
	font-size: var(--hb-font-size-small);
}
.hb-process-criteria li::before {
	content: '';
	width: 6px;
	height: 6px;
	border-radius: 50%;
	background: var(--hb-color-brass);
	flex-shrink: 0;
}

.hb-types-covered {
	display: flex;
	flex-wrap: wrap;
	gap: var(--hb-space-8);
	list-style: none;
	padding: 0;
}
.hb-types-covered li {
	padding: var(--hb-space-8) var(--hb-space-16);
	background: var(--hb-color-marble-white);
	border-radius: var(--hb-radius-pill);
	font-size: var(--hb-font-size-small);
	font-weight: 600;
}

.hb-service-areas {
	display: flex;
	flex-wrap: wrap;
	gap: var(--hb-space-16);
	list-style: none;
	padding: 0;
}
.hb-service-areas a {
	display: inline-block;
	padding: var(--hb-space-16) var(--hb-space-24);
	background: var(--hb-color-marble-white);
	border-radius: var(--hb-radius-pill);
	color: var(--hb-color-ink);
	text-decoration: none;
	font-weight: 600;
}
/* UI Polish Pass (2026-07-14) — Hello Elementor's theme.css sets
   ".page-content a { text-decoration: underline }" at the exact same
   specificity as the rule above and loads after components.css, so it
   won every tie sitewide (this was accidentally masked until now by the
   ".page-content a" catch-all removed above, which had incidentally
   higher specificity). Same specificity-bump pattern already used
   elsewhere in this file rather than !important. */
.hb-main .hb-service-areas a {
	text-decoration: none;
}

/* City Page template (Milestone 6) — single-service_area.php */

.hb-local-introduction {
	font-size: 1.125rem;
	margin-block-end: var(--hb-space-32);
}
/* Same fix as .hb-faq-link a (2026-07-12) applied here too — these links
   were falling through to the parent theme's default link color (a
   bright pink/magenta, reset.css) instead of the site's brass accent,
   found during the premium-consistency pass on pillar/city pages.
   .hb-before-after-link had no rule at all previously. */
.hb-pillar-link a,
.hb-sibling-service a,
.hb-before-after-link a {
	color: var(--hb-color-brass);
	font-weight: 600;
	text-decoration: none;
}
.hb-pillar-link a:hover,
.hb-sibling-service a:hover,
.hb-before-after-link a:hover {
	text-decoration: underline;
}
.hb-pillar-link {
	margin-block-start: var(--hb-space-16);
}
.hb-nearby-areas,
.hb-sibling-cities {
	display: flex;
	flex-wrap: wrap;
	gap: var(--hb-space-8);
	list-style: none;
	padding: 0;
	margin-block-end: var(--hb-space-16);
}
.hb-nearby-areas li {
	padding: var(--hb-space-4) var(--hb-space-16);
	background: var(--hb-color-marble-white);
	border-radius: var(--hb-radius-pill);
	font-size: var(--hb-font-size-small);
}
.hb-sibling-cities a {
	display: inline-block;
	padding-block: var(--hb-space-8);
	color: var(--hb-color-ink);
	font-weight: 600;
	text-decoration: underline;
}
.hb-sibling-service {
	margin-block-end: var(--hb-space-16);
}
.hb-final-cta-band {
	display: flex;
	flex-wrap: wrap;
	flex-direction: column;
	align-items: center;
	gap: var(--hb-space-16);
	justify-content: center;
	text-align: center;
	padding: var(--hb-space-32) var(--hb-space-24);
	margin-block: var(--hb-space-16);
	background: linear-gradient(135deg, var(--hb-color-marble-white) 0%, #fff 100%);
	border-radius: var(--hb-radius-card);
	box-shadow: var(--hb-shadow-resting);
}
@media (min-width: 640px) {
	.hb-final-cta-band {
		flex-direction: row;
	}
}
@media (min-width: 1024px) {
	.hb-final-cta-band {
		padding: var(--hb-space-64) var(--hb-space-48);
	}
}
.hb-final-cta-band .hb-btn {
	flex: 1 1 160px;
	max-width: 280px;
	min-height: 52px;
	box-shadow: var(--hb-shadow-resting);
}

/* Global Header & Footer (Milestone 7) — header.php / footer.php */

.hb-header {
	position: sticky;
	top: 0;
	z-index: 998;
	background: var(--hb-color-marble-white);
	border-bottom: 1px solid var(--hb-color-divider);
}
.hb-header-inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--hb-space-16);
	padding-block: var(--hb-space-16);
}
.hb-header-logo {
	color: var(--hb-color-ink);
	text-decoration: none;
	font-size: 1.5rem;
	transition: opacity var(--hb-transition-duration) var(--hb-transition-easing);
}
.hb-header-logo:hover {
	opacity: 0.75;
}

/* Wordmark — typography-only brand treatment (housebro_get_wordmark(),
   inc/helpers.php), shared by the header and the footer's dark
   background. currentColor on "House" and ".in" means it automatically
   matches whichever context it's in (Ink text on the light header, Marble
   White on the dark footer) without two separate color rules; brass on
   "Bro" is the one constant accent, already proven legible against both
   the light header and dark footer backgrounds elsewhere on the site. */
.hb-wordmark {
	display: inline-flex;
	align-items: center;
	gap: var(--hb-space-8);
	font-weight: 700;
	letter-spacing: -0.02em;
}
.hb-wordmark-icon {
	width: 1.375em;
	height: 1.375em;
	flex-shrink: 0;
	/* currentColor, not a fixed colour (2026-08-26). The mark is three-tone
	   and only one of the three is constant. The H inherits the surrounding
	   text colour so it is charcoal on the light header and marble-white on
	   the ink footer; the other two roles are driven by the properties
	   below. Pinning a colour here would flatten the mark and lose the H
	   against the footer. */
	color: inherit;

	/* Structure = the gable roof and the green half of the swoosh.
	   Gold = chimney, B, and the gold half of the swoosh. Set here rather
	   than baked into the SVG so the footer can reverse the structure
	   without a second copy of the artwork. */
	--hb-mark-structure: var(--hb-brand-green);
	--hb-mark-gold: var(--hb-brand-gold);
}
/* Footer reversal. The brand green on the ink footer is about 1.3:1 —
   the roof would simply not be there. The approved artwork solves this the
   same way on its own dark green and black app icons: the structure goes
   white and the gold stays put. This is that rule, not a new decision. */
.hb-footer-brand .hb-wordmark-icon {
	--hb-mark-structure: var(--hb-color-marble-white);
}
.hb-wordmark-house {
	color: currentColor;
}
/* Artwork gold, not brass (2026-08-26). The approved lockup sets "BRO"
   in the same gold as the mark's B, and the two sit about 3px apart in the
   header — brass beside artwork gold reads as a rendering fault rather than
   a palette. Scoped to the wordmark only; the other 57 brass uses on the
   site are unchanged. */
.hb-wordmark-bro {
	color: var(--hb-brand-gold);
}
.hb-wordmark-tld {
	font-size: 0.65em;
	font-weight: 600;
	color: currentColor;
	opacity: 0.65;
	margin-inline-start: 1px;
}
.hb-nav-menu {
	display: flex;
	gap: var(--hb-space-24);
	list-style: none;
	padding: 0;
	margin: 0;
}
.hb-nav-menu a {
	display: block;
	padding-block: var(--hb-space-8);
	color: var(--hb-color-ink);
	text-decoration: none;
	font-weight: 600;
}
.hb-nav-menu .current-menu-item a,
.hb-nav-menu a:hover {
	color: var(--hb-color-brass);
}

/* Services dropdown (Batch 7, 2026-07-12) — desktop: CSS-only hover/focus
   reveal, no JS. Scales cleanly as more services are added under this
   same parent later; nothing here assumes exactly 2 children. */
.hb-nav-menu .menu-item-has-children {
	position: relative;
}
.hb-nav-menu .menu-item-has-children > a::after {
	content: '';
	display: inline-block;
	width: 6px;
	height: 6px;
	margin-inline-start: var(--hb-space-4);
	border-inline-end: 1.5px solid currentColor;
	border-block-end: 1.5px solid currentColor;
	transform: rotate(45deg) translateY(-2px);
}
.hb-nav-menu .sub-menu {
	position: absolute;
	top: 100%;
	inset-inline-start: 0;
	min-width: 200px;
	list-style: none;
	margin: var(--hb-space-4) 0 0;
	padding: var(--hb-space-8);
	background: #fff;
	border: 1px solid var(--hb-color-divider);
	border-radius: var(--hb-radius-card);
	box-shadow: var(--hb-shadow-elevated);
	opacity: 0;
	visibility: hidden;
	transform: translateY(-4px);
	transition: opacity var(--hb-transition-duration) var(--hb-transition-easing),
		transform var(--hb-transition-duration) var(--hb-transition-easing),
		visibility var(--hb-transition-duration) var(--hb-transition-easing);
	z-index: 10;
}
.hb-nav-menu .menu-item-has-children:hover > .sub-menu,
.hb-nav-menu .menu-item-has-children:focus-within > .sub-menu,
/* data-open is set by js/nav-dropdown.js — the tap/keyboard path for
   desktop-width devices with no hover, where :hover never fires. Purely
   additive: the two selectors above are unchanged and still do all the
   work for pointer users, with no JS involved. */
.hb-nav-menu .menu-item-has-children[data-open='true'] > .sub-menu {
	opacity: 1;
	visibility: visible;
	transform: translateY(0);
}

/* A dropdown parent that exists only to open its sub-menu ("Cities" has
   no page of its own) is rendered as a <button>, not an href="#" link —
   this makes it look and sit exactly like the sibling nav links. */
.hb-nav-menu .hb-nav-parent-toggle {
	display: flex;
	align-items: center;
	/* Deliberately mirrors .hb-nav-menu a above — a <button> resets font,
	   colour and weight, so without this the dropdown parents would read
	   as lighter, differently-coloured text sitting next to the real nav
	   links. min-height gives the 44px touch target this control exists
	   for, since the device that needs it is a touch one. */
	padding-block: var(--hb-space-8);
	min-height: 44px;
	background: none;
	border: none;
	font-family: inherit;
	font-size: inherit;
	font-weight: 600;
	line-height: inherit;
	color: var(--hb-color-ink);
	cursor: pointer;
}
.hb-nav-menu .hb-nav-parent-toggle:hover,
.hb-nav-menu .menu-item-has-children[data-open='true'] > .hb-nav-parent-toggle {
	color: var(--hb-color-brass);
}
.hb-nav-menu .menu-item-has-children > .hb-nav-parent-toggle::after {
	content: '';
	display: inline-block;
	width: 6px;
	height: 6px;
	margin-inline-start: var(--hb-space-4);
	border-inline-end: 1.5px solid currentColor;
	border-block-end: 1.5px solid currentColor;
	transform: rotate(45deg) translateY(-2px);
}
.hb-nav-menu .sub-menu a {
	padding: var(--hb-space-8) var(--hb-space-16);
	border-radius: var(--hb-radius-card);
	white-space: nowrap;
}
.hb-nav-menu .sub-menu a:hover {
	background: var(--hb-color-marble-white);
}

/* Secondary nav group (Gallery/FAQ/About/Contact) — smaller, muted text
   so it doesn't compete with Services/Cities for attention, separated by
   a thin divider rather than a whole new header row. The divider targets
   whichever secondary item immediately follows a non-secondary one, so it
   stays correct regardless of how many secondary items exist. */
.hb-nav-menu .hb-nav-secondary a {
	font-size: var(--hb-font-size-small);
	font-weight: 500;
	color: var(--hb-color-stone-grey);
}
.hb-nav-menu .hb-nav-secondary a:hover {
	color: var(--hb-color-brass);
}
.hb-nav-menu li:not(.hb-nav-secondary) + .hb-nav-secondary {
	margin-inline-start: var(--hb-space-8);
	padding-inline-start: var(--hb-space-24);
	border-inline-start: 1px solid var(--hb-color-divider);
}
.hb-header-cta {
	display: flex;
	gap: var(--hb-space-8);
}
.hb-mobile-nav-toggle {
	display: none;
	min-width: 44px;
	min-height: 44px;
	background: none;
	border: none;
	font-size: 1.5rem;
	cursor: pointer;
}
.hb-mobile-nav-close {
	background: none;
	border: none;
	font-size: 1.5rem;
	cursor: pointer;
	min-width: 44px;
	min-height: 44px;
	align-self: flex-end;
}
.hb-mobile-nav-panel {
	position: fixed;
	inset: 0;
	z-index: 1000;
	display: flex;
	flex-direction: column;
	padding: var(--hb-space-24);
	background: var(--hb-color-marble-white);
	transform: translateX(100%);
	visibility: hidden;
	transition: transform var(--hb-transition-duration) var(--hb-transition-easing);
}
.hb-mobile-nav-panel[data-open='true'] {
	transform: translateX(0);
	visibility: visible;
}
.hb-mobile-nav-menu {
	flex-direction: column;
	gap: var(--hb-space-24);
	margin-block-start: var(--hb-space-24);
}
/* Mobile: no hover, so the Services sub-menu is always expanded inline
   underneath it rather than requiring a tap-to-open interaction — one
   less step than a JS accordion, and the touch target for Marble
   Polishing/Tile Cleaning is immediately visible and reachable. */
.hb-mobile-nav-menu .sub-menu {
	position: static;
	opacity: 1;
	visibility: visible;
	transform: none;
	box-shadow: none;
	border: none;
	background: none;
	margin: var(--hb-space-8) 0 0;
	padding-inline-start: var(--hb-space-16);
	display: flex;
	flex-direction: column;
	gap: var(--hb-space-8);
}
.hb-mobile-nav-menu .menu-item-has-children > a::after,
.hb-mobile-nav-menu .menu-item-has-children > .hb-nav-parent-toggle::after {
	display: none;
}
/* The nowrap on .hb-nav-menu .sub-menu a exists to stop a floating
   desktop dropdown from wrapping. Inside the mobile panel the sub-menu is
   static and indented, so at 320px a long service name would push the
   panel wider than the viewport instead — let it wrap here
   (2026-08-23 mobile audit). */
.hb-mobile-nav-menu .sub-menu a {
	white-space: normal;
}
/* Inside the mobile panel the sub-menu is already expanded inline, so a
   dropdown parent has nothing left to toggle — it reads as the group's
   label ("Services", "Cities") rather than a control, and is styled to
   match. js/nav-dropdown.js scopes itself to .hb-header-nav and never
   touches these, so they stay inert here by design. */
.hb-mobile-nav-menu .hb-nav-parent-toggle {
	cursor: default;
	font-weight: 600;
	color: var(--hb-color-stone-grey);
	min-height: 0;
}
.hb-mobile-nav-menu .hb-nav-parent-toggle:hover {
	color: var(--hb-color-stone-grey);
}
.hb-mobile-nav-menu li:not(.hb-nav-secondary) + .hb-nav-secondary {
	margin-inline-start: 0;
	padding-inline-start: 0;
	border-inline-start: none;
	border-block-start: 1px solid var(--hb-color-divider);
	padding-block-start: var(--hb-space-16);
}

/* City-first IA (2026-08-31): .hb-header-cta is NO LONGER HIDDEN here.
   The Call and WhatsApp buttons now render at every width — icon-only
   below 1024px — which is the whole point of removing the Services
   control: the width it was spending is returned to the two actions a
   visitor actually takes. See the MOBILE HEADER — ICON-ONLY CTA block
   further down for the icon-only treatment and its touch targets.

   .hb-header-nav and .hb-mobile-nav-toggle are left in this rule
   deliberately. Neither element is rendered any more (header.php), so
   the rule matches nothing — but if either template is ever reverted,
   the mobile breakpoint still behaves the way it was designed to rather
   than dumping a desktop dropdown into a 56px phone header. */
@media (max-width: 1023px) {
	.hb-header-nav {
		display: none;
	}
	.hb-mobile-nav-toggle {
		display: inline-flex;
		align-items: center;
		justify-content: center;
	}
}

@media (min-width: 1024px) {
	.hb-mobile-nav-panel {
		display: none;
	}
}

.hb-main {
	min-height: 50vh;
}

/* Homepage section container cap (Batch 8, 2026-07-12) — every Homepage
   section (Hero, Service Cards, Why Choose, Before & After, Process,
   Areas We Serve, Pricing, FAQ, Final CTA) is rendered via Elementor's
   Shortcode widget, which always wraps its output in .elementor-shortcode
   — one rule here reaches every section uniformly instead of repeating a
   max-width on each section's own class. Reuses .hb-container's existing
   1280px value (never a second, drifting content-width number). Without
   this, sections stretched edge-to-edge on wide monitors (confirmed at
   1920px: Hero and Service Cards spanned ~1900px with only 10px margins)
   — the opposite of the "avoid long empty horizontal space" premium goal. */
.hb-main .elementor-shortcode {
	max-width: 1280px;
	margin-inline: auto;
	padding-inline: var(--hb-space-16);
}
@media (min-width: 1024px) {
	.hb-main .elementor-shortcode {
		padding-inline: var(--hb-space-24);
	}
}

/* Design System Lock (Batch 14, 2026-07-12) — one container system,
   sitewide. About, Contact, Gallery, FAQ, Delhi NCR, and both pillar
   pages use Hello Elementor's own default page template (.site-main),
   which constrains content to ~1140px via the parent theme's own Kit
   settings — narrower than, and misaligned with, the 1280px
   .hb-container/.elementor-shortcode system already used by the header,
   footer, Homepage, and all 8 city pages. This brings every page onto
   the exact same width and edge alignment. box-sizing: border-box
   matches how .hb-container already computes its own width. */
body.page .hb-main .site-main {
	max-width: 1280px;
	margin-inline: auto;
	padding-inline: var(--hb-space-16);
	box-sizing: border-box;
}
@media (min-width: 1024px) {
	body.page .hb-main .site-main {
		padding-inline: var(--hb-space-24);
	}
}

.hb-footer {
	background: var(--hb-color-ink);
	color: var(--hb-color-marble-white);
	margin-block-start: var(--hb-space-64);
}
.hb-footer-inner {
	display: grid;
	gap: var(--hb-space-32);
	grid-template-columns: 1fr;
	/* Every column reads from its own top edge; the brand column is much
	   the tallest and must not centre the three link columns against it. */
	align-items: start;
	padding-block: var(--hb-space-48);
}
/* Tablet: the brand block spans the full width and the three link columns
   share the row beneath it (2026-08-27). A 2-track grid left the four
   blocks in a 2x2 with Company sitting alone under Areas We Serve, which
   at 768px is where the row has the most spare width, not the least. */
@media (min-width: 640px) {
	.hb-footer-inner {
		grid-template-columns: repeat(3, 1fr);
		column-gap: var(--hb-space-32);
	}
	.hb-footer-brand {
		grid-column: 1 / -1;
	}
}
@media (min-width: 1024px) {
	/* Four columns across the full container (2026-08-27).
	   Brand+Contact / Services / Areas We Serve / Company.

	   The previous ratios (1.7fr 1fr 1.4fr 0.95fr) were written for four
	   filled columns and then kept after the row was reduced to two, so
	   tracks 3 and 4 — over half the width — painted nothing. The row has
	   four real columns again, and the ratios are retuned for them: the
	   brand column stays widest because it carries the address and the
	   WhatsApp button, Areas We Serve takes the next share because
	   "Greater Noida" and "Delhi NCR Overview" are the longest labels in
	   the footer, and the two short lists take an equal, unsqueezed
	   share each. At 1280px that is roughly 372 / 232 / 267 / 232px —
	   no track narrow enough to wrap a two-word label. */
	.hb-footer-inner {
		grid-template-columns: 1.6fr 1fr 1.15fr 1fr;
		gap: var(--hb-space-48);
	}
	.hb-footer-brand {
		grid-column: auto;
	}
}
.hb-footer h3 {
	color: var(--hb-color-marble-white);
	margin-block-end: var(--hb-space-16);
	font-size: 1rem;
}
.hb-footer-brand .hb-wordmark {
	font-size: 1.375rem;
	display: inline-block;
}
.hb-footer-brand p {
	color: var(--hb-color-divider);
	margin-block-start: var(--hb-space-8);
}
.hb-footer-social {
	list-style: none;
	padding: 0;
	margin: var(--hb-space-16) 0 0;
	display: flex;
	/* Circles, not words, so the row is much narrower than the text links
	   it replaced — 8px reads as a deliberate group where 16px read as
	   four separate items. */
	gap: var(--hb-space-8);
}
/* Circular icon buttons (2026-08-26, approved footer). The label is still
   in the DOM inside .hb-visually-hidden, so these are icon-only to the eye
   only — every link still announces its platform. */
.hb-footer-social a {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	/* 40px circle inside a 44px touch target: the ring is the visible
	   affordance, the padding-free box is the tappable one, and 40px is
	   what the approved footer draws. */
	width: 40px;
	height: 40px;
	border: 1px solid rgba(250, 247, 242, 0.28);
	border-radius: var(--hb-radius-pill);
	color: var(--hb-color-divider);
	text-decoration: none;
	transition: color var(--hb-transition-duration) var(--hb-transition-easing),
		border-color var(--hb-transition-duration) var(--hb-transition-easing),
		background-color var(--hb-transition-duration) var(--hb-transition-easing);
}
.hb-footer-social a .hb-icon {
	width: 18px;
	height: 18px;
}
.hb-footer-social a:hover,
.hb-footer-social a:focus-visible {
	color: var(--hb-color-ink);
	border-color: var(--hb-color-marble-white);
	background: var(--hb-color-marble-white);
}
.hb-footer-column ul {
	list-style: none;
	padding: 0;
	margin: 0;
	display: flex;
	flex-direction: column;
	gap: var(--hb-space-8);
}
.hb-footer-column a:not(.hb-btn) {
	display: block;
	padding-block: var(--hb-space-4);
	color: var(--hb-color-divider);
	text-decoration: none;
}
.hb-footer-column a:not(.hb-btn):hover {
	color: var(--hb-color-marble-white);
}
/* UI Polish Pass (2026-07-14) — real bug: the plain-link rule above
   (display:block) has higher specificity than .hb-btn's own
   display:inline-flex, so the "Message on WhatsApp" button inside the
   footer's Contact card lost its flex layout — icon and (wrapped,
   2-line) text stacked in normal flow instead of staying centered as one
   row, leaving the icon pinned to the top instead of vertically centered
   against the full button height. :not(.hb-btn) above stops the
   override at the source; this re-affirms the button keeps its own
   layout regardless of rule order. */
.hb-footer-column .hb-btn {
	display: inline-flex;
}
.hb-footer-column .hb-areas-we-serve-item {
	background: none;
	padding: 0;
}
/* UI Polish Pass (2026-07-14) — real bug: .hb-areas-we-serve-item:hover
   sets a near-white background (var(--hb-color-divider)) intended for the
   light-background "Areas We Serve" section elsewhere on the site. The
   base rule above already cancels the background at rest inside the dark
   footer, but not on :hover, so hovering a footer city link produced a
   near-white pill behind text that also turns near-white on hover
   (.hb-footer-column a:hover) — unreadable white-on-white. Cancelled here
   so the footer's city links hover exactly like every other footer link
   (color change only, no background). */
.hb-footer-column .hb-areas-we-serve-item:hover {
	background: none;
}
.hb-footer-column .hb-areas-we-serve-item a {
	/* The footer's plain-link presentation has no pill padding to reclaim
	   via negative margin — cancel that part of the base rule here so the
	   link doesn't overflow its now-zero-padding parent. */
	margin: 0;
	padding-block: var(--hb-space-4);
	padding-inline: 0;
	/* UI Polish Pass (2026-07-14) — real bug: the base .hb-areas-we-serve-
	   item a rule sets font-weight:600 for its pill-badge context
	   elsewhere on the site. Inside the footer that made cities with a
	   live page (rendered as a link) appear bold while cities without one
	   (plain text, regular weight) did not — an arbitrary-looking
	   inconsistency with no design intent behind it, and unlike every
	   other footer column (Services/Company links are all regular
	   weight). Normalized to match. */
	font-weight: 400;
}
.hb-footer-bottom {
	border-top: 1px solid rgba(250, 247, 242, 0.15);
	padding-block: var(--hb-space-16);
	text-align: center;
	font-size: var(--hb-font-size-small);
	color: var(--hb-color-divider);
}

/* Contact Card & Google Map (Milestones 7-8) — inc/contact-shortcodes.php */

.hb-contact-card-row {
	margin-block-end: var(--hb-space-8);
	display: flex;
	align-items: center;
	gap: var(--hb-space-8);
}
/* Icon System (Brand Asset Phase, 2026-07-14). */
.hb-contact-card-row .hb-icon {
	width: 18px;
	height: 18px;
	flex-shrink: 0;
	color: var(--hb-color-brass);
}
.hb-google-map {
	aspect-ratio: 16 / 9;
	border-radius: var(--hb-radius-card);
	overflow: hidden;
}
.hb-google-map iframe {
	width: 100%;
	height: 100%;
	border: 0;
}
/* Empty state (Premium Brand Consistency Pass, 2026-07-13) — same
   .hb-card-adjacent visual language as the Before & After placeholder
   tiles: a real card, not a dashed "broken" box. */
.hb-google-map--placeholder {
	width: 100%;
	max-width: 480px;
	aspect-ratio: 16 / 9;
	background: var(--hb-color-marble-white);
	border: 1px solid var(--hb-color-divider);
	box-shadow: var(--hb-shadow-premium-resting);
	display: flex;
	align-items: center;
	justify-content: center;
}
.hb-google-map--placeholder .hb-photo-placeholder-icon {
	width: 25%;
	color: var(--hb-color-divider);
}

/* ==========================================================================
   Flagship Techniques — [hb_flagship_techniques]
   Homepage composition pass (2026-08-23). Diamond Polishing and Premium
   Silicate Polishing previously appeared only as brass chips inside a
   Service Card. This gives the two techniques that carry HouseBro's
   positioning a section of their own, with the remaining real techniques
   as a supporting strip beneath. All content is CMS-driven; nothing here
   assumes exactly two flagship cards or exactly five supporting chips.
   ========================================================================== */
.hb-flagship-cards {
	display: grid;
	gap: var(--hb-space-16);
	grid-template-columns: 1fr;
	list-style: none;
	margin: 0;
	padding: 0;
}
@media (min-width: 640px) {
	.hb-flagship-cards {
		grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
		gap: var(--hb-space-24);
	}
}
.hb-flagship-card {
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--hb-space-8);
	padding: var(--hb-space-24);
	/* Brass top edge — the one visual cue that separates a flagship card
	   from every other .hb-card on the page, without introducing a new
	   colour or shadow into the locked system. */
	border-block-start: 3px solid var(--hb-color-brass);
}
.hb-flagship-card-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 56px;
	height: 56px;
	border-radius: 50%;
	background: var(--hb-color-marble-white);
	border: 1px solid var(--hb-color-divider);
	color: var(--hb-color-brass);
}
.hb-flagship-card-icon .hb-icon {
	width: 28px;
	height: 28px;
}
/* The shared .hb-flagship-badge is written for inline use after a heading
   (it carries an inline-start margin); as a standalone block here that
   margin would push it off the card's left edge. */
.hb-flagship-card .hb-flagship-badge {
	margin-inline-start: 0;
}
.hb-flagship-card h3 {
	margin: 0;
}
.hb-flagship-card p {
	margin: 0;
	color: var(--hb-color-stone-grey);
}
.hb-flagship-strip-label {
	margin-block: var(--hb-space-32) var(--hb-space-16);
	color: var(--hb-color-stone-grey);
	font-size: var(--hb-font-size-small);
	font-weight: 600;
}
.hb-flagship-strip {
	display: flex;
	flex-wrap: wrap;
	gap: var(--hb-space-8);
	list-style: none;
	margin: 0;
	padding: 0;
}

/* Flagship card media band (2026-08-23). Full-bleed to the card edges —
   unlike the Service Card's inset band — because these two cards are the
   page's premium moment and the image should carry the card, not sit
   inside it. Fixed 16:9 keeps both cards identical in height whatever
   their description length. A technique with no image simply omits this
   block and the card renders as before. */
.hb-flagship-card--has-media {
	padding-block-start: 0;
	overflow: hidden;
}
.hb-flagship-card-media {
	/* Cancel the card's own inline padding so the image reaches the edges,
	   then restore normal flow for the content below it. */
	margin-inline: calc(var(--hb-space-24) * -1);
	margin-block-end: var(--hb-space-16);
	aspect-ratio: 16 / 9;
	overflow: hidden;
}
.hb-flagship-card-media img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}
/* With a photo above it the round glyph would be a second competing
   visual, so it shrinks to a quiet inline marker beside the badge. */
.hb-flagship-card--has-media .hb-flagship-card-icon {
	width: 40px;
	height: 40px;
}
.hb-flagship-card--has-media .hb-flagship-card-icon .hb-icon {
	width: 20px;
	height: 20px;
}

/* ==========================================================================
   MOBILE HERO — overlay composition (2026-08-23)

   Below 1024px the Hero previously stacked: a 16:9 image, a 32px gap, then
   the heading, copy, CTAs and badges underneath. That is the "shrunken
   desktop" pattern — the photo ends up ~219px tall on a 390px screen, the
   fold lands somewhere in the middle of the text, and the most expensive
   asset on the page carries almost no weight.

   This makes the image the full background of a single composed panel and
   lays the content over it, bottom-aligned, behind a gradient scrim. Same
   markup, same content, same CTAs, same badges — only the composition
   changes, and only where a real image exists (:has(.hb-hero-bg)), so a
   Hero without one is untouched. Desktop's two-column grid is untouched:
   everything here is inside a max-width query.
   ========================================================================== */
@media (max-width: 1023.98px) {
	.hb-hero:has(.hb-hero-bg) {
		position: relative;
		display: flex;
		flex-direction: column;
		justify-content: flex-end;
		/* Tall enough to read as a landing panel, capped so it never eats
		   a short screen whole and pushes the CTAs out of reach. */
		min-height: min(78vh, 32rem);
		padding: var(--hb-space-48) var(--hb-space-24) var(--hb-space-32);
		text-align: left;
	}
	.hb-hero:has(.hb-hero-bg) .hb-hero-bg {
		position: absolute;
		inset: 0;
		z-index: 0;
		width: 100%;
		height: 100%;
		/* Cancel the stacked-layout treatment: no fixed ratio, no gap
		   below, no card shadow — it is the panel now, not a figure in it. */
		aspect-ratio: auto;
		margin-block-end: 0;
		border-radius: 0;
		box-shadow: none;
	}
	/* Scrim. Re-enables the ::after that the top-level
	   `.hb-hero:has(.hb-hero-bg)::after { display:none }` rule switches off,
	   and resets the decorative brass-glow geometry it was carrying. Equal
	   specificity, later in the file, so this wins. Weighted to the bottom
	   where the text sits, so the top of the photo stays clear. */
	.hb-hero:has(.hb-hero-bg)::after {
		content: '';
		display: block;
		position: absolute;
		inset: 0;
		width: auto;
		height: auto;
		max-width: none;
		z-index: 1;
		pointer-events: none;
		background: linear-gradient(
			to bottom,
			rgba(28, 28, 30, 0.10) 0%,
			rgba(28, 28, 30, 0.45) 42%,
			rgba(28, 28, 30, 0.82) 100%
		);
	}

	/* Vector hero art (2026-08-31). A drawing is nearly white and thin-
	   lined, so the photo scrim above leaves white copy on a pale, muddy
	   field. Two changes, both scoped to .hb-hero--illustration so no
	   photo hero is affected:

	     1. A deeper, earlier gradient, so the type sits on a deliberate
	        dark panel rather than a wash.
	     2. The drawing is dimmed and shifted toward the right of the
	        frame, where its own subject sits — it then reads as quiet
	        texture behind the copy instead of competing with it. */
	.hb-hero--illustration:has(.hb-hero-bg)::after {
		background: linear-gradient(
			to bottom,
			rgba(28, 28, 30, 0.62) 0%,
			rgba(28, 28, 30, 0.78) 45%,
			rgba(28, 28, 30, 0.92) 100%
		);
	}
	.hb-hero--illustration:has(.hb-hero-bg) .hb-hero-bg {
		opacity: 0.55;
		object-position: 78% center;
	}

	.hb-hero:has(.hb-hero-bg) .hb-hero-content {
		position: relative;
		z-index: 2;
		max-width: 100%;
	}
	.hb-hero:has(.hb-hero-bg) .hb-hero-content h1 {
		color: #fff;
	}
	.hb-hero:has(.hb-hero-bg) .hb-hero-trust-line {
		color: rgba(255, 255, 255, 0.92);
		margin-block-end: var(--hb-space-24);
	}
	/* Buttons go full-width and stack — a 52px target spanning the panel is
	   easier to hit one-handed than two half-width buttons at 320px, where
	   flex: 1 1 160px would otherwise wrap them awkwardly anyway. */
	.hb-hero:has(.hb-hero-bg) .hb-hero-cta {
		gap: var(--hb-space-8);
	}
	.hb-hero:has(.hb-hero-bg) .hb-hero-cta .hb-btn {
		flex: 1 1 100%;
	}
	/* Badges stay the existing white pills — they read as intended against
	   the scrim — but tighten up so three of them do not consume the panel. */
	.hb-hero:has(.hb-hero-bg) .hb-hero-badges {
		gap: var(--hb-space-8);
		margin-block-start: var(--hb-space-16);
	}
	/* The scroll cue is charcoal-on-light by default; invert it over the
	   scrim and left-align it with the rest of the content. */
	.hb-hero:has(.hb-hero-bg) .hb-hero-content::after {
		margin-inline: 0;
		border-color: rgba(255, 255, 255, 0.75);
	}
}

/* Flagship cards go horizontal on the narrowest screens (2026-08-23) — a
   full-width 16:9 image above each card stacks two very tall cards on a
   phone. Thumbnail beside the text is far more compact and lets both
   flagship techniques be compared without scrolling between them. */
@media (max-width: 559.98px) {
	/* Vertical card on mobile (2026-08-24). The previous horizontal variant
	   set the media to `flex: 0 0 38%` with `aspect-ratio: auto` while the
	   card's base rule kept `align-items: flex-start` — so the image had no
	   height to resolve against, collapsed to a sliver, and the card grew
	   tall and empty around it. Image on top with a fixed ratio is what the
	   approved reference shows and it cannot collapse. */
	.hb-flagship-card--has-media {
		flex-direction: column;
		align-items: stretch;
		gap: var(--hb-space-8);
		padding: 0 0 var(--hb-space-16);
	}
	.hb-flagship-card-media {
		margin-inline: 0;
		margin-block-end: var(--hb-space-8);
		aspect-ratio: 16 / 9;
		flex: 0 0 auto;
	}
	/* Everything below the image gets the padding the card no longer has. */
	.hb-flagship-card--has-media > :not(.hb-flagship-card-media) {
		margin-inline: var(--hb-space-16);
	}
	.hb-flagship-card--has-media .hb-flagship-card-icon {
		display: none; /* the photo already carries the visual */
	}
	.hb-flagship-card--has-media .hb-badge-pill {
		align-self: flex-start;
		margin-block-start: 0;
	}
	/* Long technique names must wrap inside the card, never spill past it. */
	.hb-flagship-card h3,
	.hb-flagship-card p {
		overflow-wrap: anywhere;
	}
}

/* ==========================================================================
   SERVICE CATALOGUE — [hb_service_catalogue] (2026-08-24)
   Mobile-first: 2 columns at 320px, 3 from 400px, 4 from 560px. Tiles are
   equal-height flex columns so a two-line name never misaligns a row.
   ========================================================================== */
.hb-catalogue-group + .hb-catalogue-group {
	margin-block-start: var(--hb-space-32);
}
.hb-catalogue-group-title {
	margin-block-end: var(--hb-space-16);
	font-size: var(--hb-font-size-small);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--hb-color-stone-grey);
}
.hb-catalogue-group-title a {
	color: inherit;
	text-decoration: none;
}
.hb-catalogue-group-title a:hover {
	color: var(--hb-color-brass);
}
.hb-catalogue-grid {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: var(--hb-space-8);
	list-style: none;
	margin: 0;
	padding: 0;
}
@media (min-width: 400px) {
	.hb-catalogue-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 560px) {
	.hb-catalogue-grid {
		grid-template-columns: repeat(4, minmax(0, 1fr));
		gap: var(--hb-space-16);
	}
}
.hb-catalogue-tile {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: flex-start;
	gap: var(--hb-space-8);
	height: 100%;
	/* 44px+ touch target with room to spare, and enough padding that a
	   two-word name still breathes at 320px. */
	min-height: 104px;
	padding: var(--hb-space-16) var(--hb-space-8);
	text-align: center;
	text-decoration: none;
	color: var(--hb-color-ink);
}
.hb-catalogue-tile-icon {
	display: inline-flex;
	color: var(--hb-color-brass);
}
.hb-catalogue-tile-icon .hb-icon {
	width: 28px;
	height: 28px;
}
.hb-catalogue-tile-name {
	font-size: var(--hb-font-size-small);
	font-weight: 600;
	line-height: 1.3;
	/* Long names like "Granite Grinding & Polishing" must wrap, never push
	   the grid wider than its column. */
	overflow-wrap: anywhere;
}
.hb-catalogue-tile-note {
	font-size: 0.75rem;
	color: var(--hb-color-stone-grey);
	line-height: 1.3;
}
/* Flagship emphasis is defined once, at the end of this file, and applies at
   every width (Phase 7 cleanup, 2026-08-25).

   What stood here was a brass border plus a brass-tinted gradient on the
   card itself. Below 1024px it was already being overridden; above it, it
   survived — so the same two techniques rendered as restrained white cards
   on a phone and as brass-edged, brass-tinted cards with a filled pill on a
   desktop. Removed rather than overridden a third time. */
.hb-catalogue-tile--flagship .hb-flagship-badge {
	margin-inline-start: 0;
	margin-block-start: auto;
}

/* ==========================================================================
   SERVICE QUICK NAV — [hb_service_quicknav] (2026-08-24)
   One horizontally scrollable line. The scroll is contained to the strip:
   min-width:0 on the list items plus overflow-x on the wrapper means the
   strip can never widen the page, which is the usual failure mode of a
   horizontal nav at 320px.
   ========================================================================== */
.hb-quicknav {
	max-width: 100%;
	overflow-x: auto;
	overscroll-behavior-x: contain;
	scrollbar-width: none;
	-webkit-overflow-scrolling: touch;
	scroll-snap-type: x proximity;
	border-block: 1px solid var(--hb-color-divider);
	background: var(--hb-color-marble-white);
}
.hb-quicknav::-webkit-scrollbar {
	display: none;
}
.hb-quicknav-list {
	display: flex;
	gap: var(--hb-space-8);
	/* max-content stops flex items shrinking into each other; the wrapper's
	   overflow-x is what makes the row scrollable instead of overflowing. */
	width: max-content;
	min-width: 100%;
	list-style: none;
	margin: 0;
	padding: var(--hb-space-8) var(--hb-space-16);
}
.hb-quicknav-item {
	display: inline-flex;
	align-items: center;
	gap: var(--hb-space-8);
	white-space: nowrap;
	scroll-snap-align: start;
	min-height: 44px;
	padding-inline: var(--hb-space-16);
	border-radius: var(--hb-radius-pill);
	font-size: var(--hb-font-size-small);
	font-weight: 600;
	color: var(--hb-color-ink);
	text-decoration: none;
	background: #fff;
	border: 1px solid var(--hb-color-divider);
}
.hb-quicknav-item .hb-icon {
	width: 16px;
	height: 16px;
	color: var(--hb-color-brass);
	flex-shrink: 0;
}
.hb-quicknav-item:hover {
	border-color: var(--hb-color-brass);
	color: var(--hb-color-brass);
}

/* ==========================================================================
   CITY SELECTOR — first-visit overlay + header indicator (2026-08-24)
   Mobile-first: the panel is a bottom sheet on phones (thumb-reachable,
   the standard mobile pattern) and a centred dialog from 640px up.
   ========================================================================== */
.hb-city-overlay {
	position: fixed;
	inset: 0;
	z-index: 1200; /* above .hb-mobile-nav-panel (1000) and .hb-sticky-cta (999) */
	display: flex;
	align-items: flex-end;
	justify-content: center;
}
.hb-city-overlay[hidden] {
	display: none;
}
.hb-city-overlay-backdrop {
	position: absolute;
	inset: 0;
	background: rgba(28, 28, 30, 0.55);
}
body.hb-city-overlay-open {
	overflow: hidden; /* stop the page scrolling behind the sheet */
}
.hb-city-overlay-panel {
	position: relative;
	width: 100%;
	max-width: 34rem;
	max-height: 92vh;
	overflow-y: auto;
	overscroll-behavior: contain;
	padding: var(--hb-space-32) var(--hb-space-16) var(--hb-space-24);
	background: #fff;
	border-start-start-radius: var(--hb-radius-card-lg);
	border-start-end-radius: var(--hb-radius-card-lg);
	box-shadow: var(--hb-shadow-elevated);
}
@media (min-width: 640px) {
	.hb-city-overlay {
		align-items: center;
	}
	.hb-city-overlay-panel {
		padding-inline: var(--hb-space-32);
		border-radius: var(--hb-radius-card-lg);
	}
}
.hb-city-overlay-close {
	position: absolute;
	inset-block-start: var(--hb-space-8);
	inset-inline-end: var(--hb-space-8);
	min-width: 44px;
	min-height: 44px;
	background: none;
	border: none;
	font-size: 1.5rem;
	line-height: 1;
	color: var(--hb-color-stone-grey);
	cursor: pointer;
}
.hb-city-overlay-title {
	margin: 0 0 var(--hb-space-8);
	font-size: clamp(1.35rem, 1.1rem + 1.2vw, 1.75rem);
	letter-spacing: -0.01em;
	text-align: center;
}
.hb-city-overlay-subtitle {
	margin: 0 0 var(--hb-space-24);
	text-align: center;
	color: var(--hb-color-stone-grey);
	font-size: var(--hb-font-size-small);
}
.hb-city-choice-list {
	display: grid;
	/* One column at 320px so the name and action never crowd; two from
	   400px where there is room for a comfortable pair. */
	grid-template-columns: 1fr;
	gap: var(--hb-space-8);
	list-style: none;
	margin: 0;
	padding: 0;
}
@media (min-width: 400px) {
	.hb-city-choice-list {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: var(--hb-space-16);
	}
}
.hb-city-choice {
	display: flex;
	align-items: center;
	gap: var(--hb-space-16);
	width: 100%;
	min-height: 72px; /* generous tap target */
	padding: var(--hb-space-16);
	text-align: start;
	cursor: pointer;
	font: inherit;
	color: var(--hb-color-ink);
	transition: border-color var(--hb-transition-duration) var(--hb-transition-easing),
		box-shadow var(--hb-transition-duration) var(--hb-transition-easing);
}
.hb-city-choice:hover,
.hb-city-choice:focus-visible {
	border-color: var(--hb-color-brass);
	box-shadow: var(--hb-shadow-elevated);
}
.hb-city-choice-glyph {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: 0 0 48px;
	width: 48px;
	height: 48px;
	border-radius: var(--hb-radius-card);
	background: var(--hb-color-marble-white);
	border: 1px solid var(--hb-color-divider);
	color: var(--hb-color-brass);
}
.hb-city-glyph {
	width: 26px;
	height: 26px;
}
.hb-city-choice-text {
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 0; /* lets long names wrap instead of widening the card */
}
.hb-city-choice-name {
	font-weight: 700;
	line-height: 1.25;
	overflow-wrap: anywhere;
}
.hb-city-choice-action {
	font-size: 0.75rem;
	font-weight: 600;
	color: var(--hb-color-brass);
}

/* Header indicator + change-city control.

   Phase 1 header pass (2026-08-25): re-proportioned to the approved
   reference, which renders this as "pin + city name + disclosure
   chevron" sitting directly on the header surface — no border, no fill,
   no "CHANGE" word.

   The shrink behaviour matters as much as the look. This control used to
   be unshrinkable, so a long city name pushed the menu button past the
   viewport edge and pinned the document to 393px wide at every screen
   below 430px. It now shrinks and ellipses instead, which is what keeps
   the row intact at 320px. */
.hb-city-indicator {
	display: inline-flex;
	align-items: center;
	gap: var(--hb-space-4);
	/* min-width:0 is what actually permits the ellipsis — without it a
	   flex item refuses to shrink below its content's intrinsic width. */
	min-width: 0;
	flex-shrink: 1;
	min-height: 44px; /* full touch target, inside a 56px header */
	padding-inline: var(--hb-space-4);
	background: none;
	border: none;
	font: inherit;
	font-size: var(--hb-font-size-small);
	font-weight: 700;
	color: var(--hb-color-ink);
	cursor: pointer;
}
.hb-city-indicator[hidden] {
	display: none;
}
.hb-city-indicator .hb-icon {
	width: 16px;
	height: 16px;
	color: var(--hb-color-brass);
	flex-shrink: 0;
}
/* Two classes deep so it outranks the 16px rule above, which would
   otherwise size the chevron like the brass location pin. The chevron is
   a quiet affordance, not a brand accent — stone grey, not brass. */
.hb-city-indicator .hb-city-indicator-chevron {
	width: 13px;
	height: 13px;
	color: var(--hb-color-stone-grey);
}
.hb-city-indicator-name {
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	min-width: 0;
}

/* ==========================================================================
   REFERENCE-COMPOSITION PASS (2026-08-24)

   Reproduces the approved mobile reference's composition, proportions and
   density using HouseBro's locked tokens. The reference is green-primary;
   HouseBro is charcoal/brass/marble-white, and the brand palette wins
   (explicit decision). The dark full-bleed bar the reference renders in
   forest green is rendered here in --hb-color-ink with brass accents —
   same visual role, same weight, HouseBro's colour.

   CSS only. No markup, data or taxonomy changes.
   ========================================================================== */

/* Safety net for the full-bleed break-outs below: clip, not hidden, so no
   scroll container is created and position:sticky elsewhere keeps working. */
.hb-main {
	overflow-x: clip;
}

@media (max-width: 1023.98px) {

	/* ---- 1. Service quick nav — PHASE 3 (2026-08-25) -------------------

	   Measured off the approved reference, scaled to a 390px screen:

	     band height        56.2dp   (this build: 56)
	     content inset      ~24dp from the screen edge
	     icon glyph         16.3dp square
	     icon-to-label gap  ~8dp
	     item-to-item gap   ~32dp
	     label cap height   10.2dp  -> ~14px type
	     active underline   flush to the band's bottom edge, under the
	                        label only (~56dp), not under the icon

	   The band was never too tall — 60px against the reference's 56. What
	   made it read as "a giant black rectangle" was the treatment: a 12px
	   card radius plus the container's own side padding turned an
	   edge-to-edge navigation bar into an inset black box floating on the
	   page. Square corners and a true full-bleed fix that without changing
	   the height meaningfully.

	   Full-bleed note: this is the same 100vw technique the hero uses as of
	   Phase 2, where it measures dead-on (left offset exactly 0 at all four
	   widths). The earlier note here warned it was risky inside Elementor's
	   nested containers; that risk is identical for the hero, so the two
	   now behave consistently rather than one bar being inset and the
	   other not. Worth re-checking on the real site. */
	.hb-quicknav {
		width: 100vw;
		margin-inline: calc(50% - 50vw);
		background: var(--hb-color-ink);
		border-block: none;
		border-radius: 0;
		/* Must track the list's inset below. The hardening pass pins this to
		   16px, and because the first item carries scroll-snap-align:start
		   the browser was snapping the row 8px to the left to satisfy it —
		   which silently ate a third of the intended 24px inset. */
		scroll-padding-inline-start: var(--hb-space-24);
	}
	.hb-quicknav-list {
		gap: var(--hb-space-32);
		padding-block: 0;
		padding-inline: var(--hb-space-24);
	}
	.hb-quicknav-item {
		gap: var(--hb-space-8);
		min-height: 56px;
		padding-inline: 0;
		border: none;
		border-radius: 0;
		background: none;
		font-size: 0.875rem;
		font-weight: 600;
		color: var(--hb-color-marble-white);
	}
	/* Icons match the label rather than carrying brass. Three brass glyphs
	   across a dark bar made the accent the loudest thing in it; the
	   reference keeps icon and label the same weight of white and spends
	   its accent on the active marker alone. */
	.hb-quicknav-item .hb-icon,
	/* Outranks the sitewide .hb-quicknav-item[aria-current] .hb-icon rule,
	   which tints the active glyph brass. That was the older "brass icon +
	   brass underline" active state; Phase 3 spends the accent once, on the
	   underline, so the active icon matches its neighbours. */
	.hb-quicknav-item[aria-current] .hb-icon {
		width: 16px;
		height: 16px;
		color: var(--hb-color-marble-white);
	}
	/* The underline hangs off the label and is pinned to the band's bottom
	   edge, so it reads as a tab marker rather than as a border on the
	   text. Reserved at rest in transparent so switching items cannot
	   shift the row. */
	.hb-quicknav-label {
		display: inline-flex;
		align-items: center;
		align-self: stretch;
		/* 3px, not the reference's ~2px. The reference marks the active item
		   in white on green, which carries at any thickness; brass on ink is
		   a ~4:1 pair, and at 2px (1.6px after sub-pixel rounding) the line
		   was measurably present and visually invisible. Thickening it keeps
		   the accent brass instead of spending a second colour on it. */
		border-block-end: 3px solid transparent;
	}
	.hb-quicknav-item:hover .hb-quicknav-label,
	.hb-quicknav-item:focus-visible .hb-quicknav-label,
	.hb-quicknav-item[aria-current='page'] .hb-quicknav-label,
	.hb-quicknav-item[aria-current='true'] .hb-quicknav-label {
		border-block-end-color: var(--hb-color-brass);
	}
	.hb-quicknav-item:hover,
	.hb-quicknav-item:focus-visible {
		color: #fff;
	}

	/* ---- 2. Hero: edge to edge, no card treatment --------------------- */
	.hb-hero:has(.hb-hero-bg) {
		width: 100vw;
		margin-inline: calc(50% - 50vw);
		border-radius: 0;
		box-shadow: none;
		padding-inline: var(--hb-space-24);
	}

	/* ---- 3. Content sheet lifts over the hero's bottom edge ------------ */
	.e-con:has(.hb-hero) + .e-con {
		position: relative;
		z-index: 2;
		margin-block-start: calc(var(--hb-space-24) * -1);
		border-start-start-radius: var(--hb-radius-card-lg);
		border-start-end-radius: var(--hb-radius-card-lg);
		background: #fff;
		padding-block-start: var(--hb-space-24);
	}

	/* ---- 4. Catalogue: horizontal scroll-snap row ---------------------- */
	.hb-catalogue-grid {
		display: flex;
		flex-wrap: nowrap;
		gap: var(--hb-space-8);
		/* No 100vw break-out here (2026-08-24 fix). margin-inline:
		   calc(50% - 50vw) only centres when the element's containing block
		   is itself centred in the viewport; inside Elementor's nested,
		   padded flex containers it is not, so the row was shifted further
		   left than intended and .hb-main's overflow-x:clip then cut the
		   first card ("Premium Silicate Polishing", "House Floor Cleaning"
		   sliced from the left). Staying inside the container guarantees
		   the row starts at the content edge with the first card whole. */
		padding-inline: 0;
		scroll-padding-inline-start: 0;
		overflow-x: auto;
		overscroll-behavior-x: contain;
		scroll-snap-type: x proximity;
		scrollbar-width: none;
	}
	.hb-catalogue-grid::-webkit-scrollbar {
		display: none;
	}
	.hb-catalogue-grid > li {
		flex: 0 0 auto;
		/* Sized so a 4th tile is partly visible at 320-430px — the cue that
		   tells a visitor the row scrolls, exactly as the reference does. */
		width: 8.5rem;
		scroll-snap-align: start;
	}

	/* ---- 7. Sticky CTA: equal-width dominant pair ---------------------- */
	.hb-sticky-cta .hb-btn {
		flex: 1 1 0;
		min-height: 52px;
		font-weight: 700;
	}
}

/* ---- 5. Catalogue tiles: taller, larger icon, more air --------------- */
.hb-catalogue-tile {
	min-height: 132px;
	gap: var(--hb-space-8);
	padding: var(--hb-space-24) var(--hb-space-8);
	justify-content: center;
	box-shadow: var(--hb-shadow-premium-resting);
}
.hb-catalogue-tile-icon .hb-icon {
	width: 36px;
	height: 36px;
}
.hb-catalogue-tile-name {
	font-size: 0.8125rem;
}

/* ---- 3b. Group headings: large, bold, left-aligned ------------------- */
.hb-catalogue-group-title {
	font-size: 1.125rem;
	font-weight: 700;
	text-transform: none;
	letter-spacing: -0.01em;
	/* Ink, not brass (Phase 7, 2026-08-25). Brass on white measures ~4:1 —
	   under the AA floor for text — and as a heading fill it made "Marble
	   Polishing" read washed-out beside the ink card labels beneath it.
	   Brass stays an accent here: the flagship glyphs, the quick nav's
	   active marker. Corrected at the source rather than overridden again
	   in the mobile block below. */
	color: var(--hb-color-ink);
}

/* ---- 6. Section headings: centred with flanking rules ---------------- */
body.home .elementor-shortcode > h2 {
	display: flex;
	align-items: center;
	gap: var(--hb-space-16);
	justify-content: center;
	text-align: center;
	margin-block-end: var(--hb-space-24);
}
body.home .elementor-shortcode > h2::before,
body.home .elementor-shortcode > h2::after {
	content: '';
	flex: 1 1 auto;
	height: 1px;
	background: var(--hb-color-divider);
}

/* ==========================================================================
   HERO CAROUSEL — [hb_hero_carousel] (2026-08-24)
   A CSS scroll-snap row, so swipe/momentum/keyboard are native and the
   component degrades to a readable, swipeable strip with JS disabled.
   Each slide reuses .hb-hero wholesale — no second hero design exists.
   ========================================================================== */
.hb-hero-carousel {
	position: relative;
}
.hb-hero-track {
	display: flex;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scroll-behavior: smooth;
	overscroll-behavior-x: contain;
	scrollbar-width: none;
	-webkit-overflow-scrolling: touch;
}
.hb-hero-track::-webkit-scrollbar {
	display: none;
}
.hb-hero-slide {
	flex: 0 0 100%;
	/* min-width:0 stops a long headline widening the flex item, which is the
	   usual way a slider starts scrolling the whole page sideways. */
	min-width: 0;
	scroll-snap-align: start;
}
/* Service label above the headline. */
.hb-hero-eyebrow {
	display: inline-block;
	margin-block-end: var(--hb-space-8);
	font-size: var(--hb-font-size-small);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--hb-color-brass);
}
/* Shared by the <h1> on slide 1 and the <h2> on slides 2-3 so the two are
   visually identical while the page keeps exactly one <h1>. */
.hb-hero-title {
	margin-block-end: var(--hb-space-16);
	letter-spacing: -0.02em;
	font-size: clamp(1.75rem, 1.2rem + 2.6vw, 3rem);
	line-height: 1.15;
}
.hb-hero-dots {
	position: absolute;
	inset-inline: 0;
	inset-block-end: var(--hb-space-16);
	display: flex;
	justify-content: center;
	gap: var(--hb-space-8);
	z-index: 3;
	pointer-events: none; /* only the dots themselves are clickable */
}
.hb-hero-dot {
	pointer-events: auto;
	/* 8px visual dot inside a 44px hit area — the target stays thumb-sized
	   without the design showing a row of fat buttons. */
	width: 44px;
	height: 24px;
	padding: 0;
	border: none;
	background: none;
	cursor: pointer;
	display: inline-flex;
	align-items: center;
	justify-content: center;
}
.hb-hero-dot::before {
	content: '';
	width: 8px;
	height: 8px;
	border-radius: var(--hb-radius-pill);
	background: rgba(255, 255, 255, 0.55);
	transition: width var(--hb-transition-duration) var(--hb-transition-easing),
		background-color var(--hb-transition-duration) var(--hb-transition-easing);
}
.hb-hero-dot[aria-selected='true']::before {
	width: 22px; /* active dot elongates into a pill, as in the reference */
	background: #fff;
}
.hb-hero-dot:focus-visible {
	outline: 2px solid var(--hb-color-brass);
	outline-offset: 2px;
	border-radius: var(--hb-radius-pill);
}
/* Extra bottom room so the dots never sit on top of the CTA buttons. */
@media (max-width: 1023.98px) {
	.hb-hero-carousel .hb-hero:has(.hb-hero-bg) {
		padding-block-end: var(--hb-space-48);
	}
	/* The slide is the full-bleed element here; the track must not break out
	   a second time or the two offsets compound. */
	.hb-hero-carousel .hb-hero-slide.hb-hero:has(.hb-hero-bg) {
		width: 100%;
		margin-inline: 0;
	}
	.hb-hero-carousel {
		width: 100vw;
		margin-inline: calc(50% - 50vw);
	}
}
@media (prefers-reduced-motion: reduce) {
	.hb-hero-track {
		scroll-behavior: auto;
	}
	.hb-hero-dot::before {
		transition: none;
	}
}

/* ==========================================================================
   VISUAL CORRECTION PASS (2026-08-24)
   Fixes for problems visible in rendered screenshots. No new colours, no
   new components — existing tokens only.
   ========================================================================== */

/* --- Catalogue tiles: text must wrap inside its own card ---------------- */
.hb-catalogue-tile-name,
.hb-catalogue-tile-note {
	/* break-word, not anywhere (Phase 7, 2026-08-25): "anywhere" splits a
	   word at any character, so "Polishing" could render as "Polishin/g".
	   break-word only breaks a word that genuinely cannot fit, which is all
	   these names ever needed. */
	overflow-wrap: break-word;
}
.hb-catalogue-tile {
	overflow: hidden; /* nothing may paint outside the card */
}

/* The mobile block that stood here is gone (Phase 7 cleanup). It sized
   .hb-catalogue-grid > li at min(9.5rem, 42vw) and re-declared the scroll
   snapping — every line of it superseded by the Phase 4 services block,
   which sets the card width to 6rem and owns the snap behaviour. It was
   scoped to max-width:1023.98px, so removing it changes nothing at desktop
   either. */

/* ==========================================================================
   PHASE 2 — MOBILE HERO (2026-08-25)

   Rebuilds the mobile hero to the approved reference
   (13_REFERENCES/APPROVED_MOBILE_REFERENCE.png, panel 2): a compact
   premium banner, not a full-screen landing hero.

   Measured off the reference, scaled to a 390px screen: the hero band is
   ~262px tall, the headline ~23px over two lines, the supporting line
   ~13px, and the pagination dots sit ~15px off the bottom edge. The photo
   stays clearly legible — the reference's scrim darkens the text side
   rather than flooding the whole frame.

   This block replaces the earlier "more compact on mobile" pass, which
   only trimmed min-height and left the real cost in place: the two
   full-width CTAs inside the hero. Those are the reason the band still
   measured ~503px. On mobile the sticky bar already carries Call and
   WhatsApp at all times, so the in-hero pair was pure duplication eating
   half the banner. They are hidden here, NOT removed from the markup —
   above 1024px the sticky bar is display:none and the hero CTAs are the
   page's primary call to action.

   Height is expressed as an aspect-ratio rather than a vh figure so the
   band holds the same proportion at every width instead of reacting to
   device height. aspect-ratio is a target, not a cap: if a headline wraps
   to a third line the box still grows, so nothing can clip.
   ========================================================================== */
@media (max-width: 1023.98px) {
	/* Full-bleed applied once, to the carousel — not to every slide. The
	   per-slide breakout at .hb-hero:has(.hb-hero-bg) made each slide its
	   own 100vw box inside a flex track, which is what shifted the whole
	   component ~8px left of the viewport. */
	.hb-hero-carousel {
		width: 100vw;
		margin-inline: calc(50% - 50vw);
	}
	.hb-hero-carousel .hb-hero:has(.hb-hero-bg) {
		width: auto;
		margin-inline: 0;
		aspect-ratio: 3 / 2;
		min-height: 0;
		/* Bottom padding clears the dots and seats the text block ~80px off
		   the bottom edge, which is where the reference puts it. */
		padding: var(--hb-space-16) var(--hb-space-16)
			calc(var(--hb-space-48) + var(--hb-space-8));
		justify-content: flex-end;
	}

	/* Scrim, re-weighted to the reference. The old single bottom-up ramp
	   reached 0.82 opacity and buried the photograph — the most expensive
	   asset on the page — under near-black. Two lighter layers instead:
	   one across to keep the text side legible, one up from the bottom to
	   seat the band against the content sheet below. */
	.hb-hero-carousel .hb-hero:has(.hb-hero-bg)::after {
		background:
			linear-gradient(
				to right,
				rgba(28, 28, 30, 0.62) 0%,
				rgba(28, 28, 30, 0.30) 55%,
				rgba(28, 28, 30, 0.10) 100%
			),
			linear-gradient(
				to bottom,
				rgba(28, 28, 30, 0) 40%,
				rgba(28, 28, 30, 0.55) 100%
			);
	}

	/* Eyebrow: brass is a mid-tone and disappeared against bright marble
	   photography, so on-image it takes the palette's light neutral
	   instead. Brass stays the accent everywhere it sits on a light
	   surface. */
	.hb-hero-carousel .hb-hero-eyebrow {
		margin-block-end: var(--hb-space-4);
		font-size: 0.6875rem;
		letter-spacing: 0.12em;
		color: var(--hb-color-marble-white);
		opacity: 0.92;
	}

	/* One size for both, deliberately. The <h1> on slide 1 was picking up
	   .hb-hero-content h1 (higher specificity) while slides 2-3 used
	   .hb-hero-title, so the first slide rendered visibly larger than the
	   others mid-swipe. */
	.hb-hero-carousel .hb-hero-title,
	.hb-hero-carousel .hb-hero-content h1 {
		font-size: clamp(1.375rem, 1.05rem + 1.6vw, 1.75rem);
		line-height: 1.18;
		margin-block-end: var(--hb-space-8);
	}

	.hb-hero-carousel .hb-hero-trust-line {
		margin-block-end: 0;
		font-size: 0.875rem;
		line-height: 1.45;
		/* Two lines maximum: supporting copy, not a paragraph. The term
		   description behind it is business-owned and can be any length,
		   so the cap lives here rather than in PHP. */
		display: -webkit-box;
		-webkit-line-clamp: 2;
		-webkit-box-orient: vertical;
		overflow: hidden;
	}

	/* Call/WhatsApp live only in the sticky bar at this width. */
	.hb-hero-carousel .hb-hero-cta,
	.hb-hero-carousel .hb-hero-badges {
		display: none;
	}

	/* The "more below" chevron earned its place under a full-height hero.
	   In a 260px banner with the content sheet already visible beneath it,
	   it is clutter competing with the dots. */
	.hb-hero-carousel .hb-hero-content::after {
		display: none;
	}

	/* The dots must clear the content sheet, which is pulled 24px up over
	   the hero's bottom edge. Sitting at the measured ~16px put white dots
	   on top of that white sheet — present, hit-testable, and completely
	   invisible. Offsetting past the overlap puts them back inside the
	   photograph, which is where the reference has them. */
	.hb-hero-carousel .hb-hero-dots {
		inset-block-end: calc(var(--hb-space-24) + var(--hb-space-8));
	}
}

/* --- Before & After: reduce dominance while it holds no real work ------- */
.hb-before-after-grid--placeholder {
	max-width: 34rem;
	margin-inline: auto;
}
.hb-before-after-grid--placeholder .hb-before-after-images {
	opacity: 0.85;
}

/* --- Desktop lower-page rhythm ----------------------------------------- */
@media (min-width: 1024px) {
	/* Elementor gives each shortcode section generous block padding, which
	   compounds with each section's own margins — the gap between Before &
	   After, Pricing, Nearby Areas and FAQ read as unfinished rather than
	   airy. Tightened, not removed. */
	body.home .elementor-shortcode > h2 {
		margin-block: var(--hb-space-16) var(--hb-space-24);
	}
	body.home .hb-before-after-grid,
	body.home .hb-quality-workflow,
	body.home .hb-faq {
		margin-block-end: 0;
	}
}

/* --- Quote block (replaces the old "pricing pending" empty state) ------- */
.hb-quote-block {
	max-width: 34rem;
	margin-inline: auto;
	padding: var(--hb-space-32) var(--hb-space-24);
	text-align: center;
	background: linear-gradient(180deg, #fff 0%, var(--hb-color-marble-white) 100%);
	border: 1px solid var(--hb-color-divider);
	border-radius: var(--hb-radius-card-lg);
	box-shadow: var(--hb-shadow-premium-resting);
}
.hb-quote-block-title {
	margin: 0 0 var(--hb-space-8);
	font-size: 1.375rem;
	letter-spacing: -0.01em;
}
.hb-quote-block-text {
	margin: 0 0 var(--hb-space-24);
	color: var(--hb-color-stone-grey);
}
.hb-quote-block .hb-pricing-card-cta {
	display: flex;
	flex-wrap: wrap;
	gap: var(--hb-space-8);
	justify-content: center;
}
.hb-quote-block .hb-btn {
	flex: 1 1 12rem;
	min-height: 52px;
}

/* --- Quick nav hardening (2026-08-24) ----------------------------------
   Scrolls within its own bounds at every width; nothing here can move the
   bar horizontally or widen the page. */
.hb-quicknav {
	max-width: 100%;
	/* The scroll must never start mid-item. Must equal the list's own
	   inline padding, which is 24px on mobile as of Phase 3 — when the two
	   disagreed the browser snapped the row sideways by the difference to
	   satisfy the first item's scroll-snap-align:start. Desktop restores
	   16px alongside its own padding, below. */
	scroll-padding-inline-start: var(--hb-space-24);
}
.hb-quicknav-list {
	/* max-content lets the row exceed the bar and scroll; min-width:100%
	   keeps it filling the bar when the items are narrower than it. */
	width: max-content;
	min-width: 100%;
	margin: 0;
}
.hb-quicknav-list > li {
	flex: 0 0 auto;
	min-width: 0;
}
.hb-quicknav-list > li:first-child .hb-quicknav-item {
	scroll-snap-align: start;
}
/* Explicit reset of Hello Elementor's button/link colours so this can never
   inherit #c36 again, independent of stylesheet order. */
.hb-quicknav-item,
.hb-quicknav-item:hover,
.hb-quicknav-item:focus,
.hb-quicknav-item:visited {
	background-color: transparent;
	text-decoration: none;
}
/* Active-item colouring lives in the desktop block below as of Phase 3.
   On mobile the reference spends its accent exactly once — on the
   underline — and keeps the active item's icon and label the same white as
   its neighbours. These rules sat here, after the mobile @media block and
   at equal specificity, so they silently won and kept re-tinting the
   active glyph brass. */
.hb-quicknav-item:focus-visible {
	outline: 2px solid var(--hb-color-brass);
	outline-offset: 2px;
	border-radius: var(--hb-radius-button);
}
@media (min-width: 1024px) {
	/* On desktop the bar is a light contained strip, matching the rest of
	   the page rather than a dark band across a wide layout. */
	.hb-quicknav {
		background: var(--hb-color-marble-white);
		border: 1px solid var(--hb-color-divider);
		scroll-padding-inline-start: var(--hb-space-16);
	}
	.hb-quicknav-item {
		color: var(--hb-color-ink);
	}
	/* Brass reads as the accent against this light strip, where it has the
	   contrast it lacked on the dark mobile bar. */
	.hb-quicknav-item[aria-current] {
		color: var(--hb-color-brass);
	}
	.hb-quicknav-item[aria-current] .hb-icon {
		color: var(--hb-color-brass);
	}
}

/* ==========================================================================
   PHASE 1 — MOBILE HEADER (2026-08-25)

   Re-proportions the header to the approved mobile reference
   (13_REFERENCES/APPROVED_MOBILE_REFERENCE.png, panel 2): a compact bar
   carrying logo · selected city · menu, measured at ~57px in the
   reference and built to 56px here.

   Before this pass the row measured 79px and — more seriously — could not
   compress. At 320px the menu button landed at x=392.6, which pinned
   documentElement.scrollWidth to 393px and gave every viewport below
   430px a horizontal scrollbar. The header, not the hero, was the source
   of the sitewide overflow.

   Three changes do the work:
     a) less vertical padding around controls that keep a full 44px touch
        height (6 + 44 + 6 = 56), so the bar gets shorter without any
        target getting smaller;
     b) a smaller wordmark, proportioned like the reference's;
     c) a city control that shrinks and ellipses rather than shoving its
        neighbours out of the viewport.

   Scoped to mobile/tablet — the desktop header is deliberately untouched.
   ========================================================================== */
@media (max-width: 1023.98px) {
	.hb-header-inner {
		flex-wrap: nowrap;
		gap: var(--hb-space-8);
		padding-block: 6px;
	}

	/* The brand block never shrinks and never wraps; the city control
	   absorbs the horizontal pressure instead. Priority order is
	   deliberate — the logo is the one thing that must always render
	   whole. */
	.hb-header-logo {
		flex-shrink: 0;
		font-size: 1.25rem;
		line-height: 1;
	}
	.hb-wordmark {
		gap: var(--hb-space-4);
	}
	.hb-wordmark-icon {
		width: 1.35em;
		height: 1.35em;
	}

	.hb-mobile-nav-toggle {
		flex-shrink: 0;
		min-width: 40px;
		min-height: 44px;
		padding: 0;
		font-size: 1.375rem;
		color: var(--hb-color-ink);
	}
}

/* 320–359px. The wordmark steps down one notch so the full brand, a
   two-word city name and the menu button all still fit on one line —
   verified at 320px with "Greater Noida" selected, the longest real
   city name in the taxonomy. */
@media (max-width: 359.98px) {
	/* Tighten the row's own gaps and the logo mark before touching the
	   city name — reclaiming ~11px here is what lets "Delhi NCR" render
	   in full at 320px instead of ellipsing by a hair. */
	.hb-header-inner {
		gap: var(--hb-space-4);
	}
	.hb-header-logo {
		font-size: 1.0625rem;
	}
	.hb-wordmark-icon {
		width: 1.2em;
		height: 1.2em;
	}
	.hb-city-indicator {
		font-size: 0.8125rem;
		padding-inline: 0;
	}
	/* A name longer than this ("Greater Noida") still ellipses rather than
	   pushing the menu button off the row — a deliberate, contained
	   degradation at the narrowest supported width, not a break. */
}

/* Narrowest screens (Phase 2, 2026-08-25). A 3:2 band is only 213px tall
   at 320px, and HouseBro's real hero copy needs more room than the
   reference's did: the reference slide carries a two-line headline and a
   one-line supporting sentence, while HouseBro adds a service eyebrow
   above and runs a two-line service description below. At 320px that
   content filled the band to its top edge, leaving 3px of clear
   photograph — the image stopped reading as an image.

   A floor on the height buys the composition back without abandoning the
   reference's proportion at the sizes it was drawn for: 375px and up keep
   the 3:2 ratio exactly, and only 320-359px trades a little extra height
   for a hero that still looks like a photograph. */
@media (max-width: 359.98px) {
	.hb-hero-carousel .hb-hero:has(.hb-hero-bg) {
		min-height: 15rem;
	}
	.hb-hero-carousel .hb-hero-trust-line {
		font-size: 0.8125rem;
		line-height: 1.4;
	}
}

/* ==========================================================================
   PHASE 4 — SERVICES SECTION (2026-08-25)

   Measured off the approved reference, scaled to a 390px screen:

     "Our Services"      ~18-20px bold, centred, a ~72dp hairline rule
                         either side with ~16dp clear of the text
     group heading       ~17-18px bold, left aligned at the content edge
     card                82.7dp wide x ~110dp tall, ~12dp radius
     gap between cards   11.2dp
     icon glyph          ~30dp square, centred, ~17dp below the card top
     label               ~13-14px, centred, tight leading

   Deliberately appended at the end of the file. The catalogue already had
   rules in four separate places (base grid, the 2026-08-24 composition
   pass, a hardening pass, and a narrow-screen pass); since media queries
   add no specificity, anything written earlier loses to whichever of those
   comes last. This block is the one that decides how the section looks.

   Two deviations from the reference, both requested:
     - Card heights are equalised. The reference lets each card size to its
       own label, so its 3-line "Normal Polishing (Ghisai)" card hangs
       ~10dp below its neighbours.
     - Cards are wider than 82.7dp. HouseBro's real offerings have longer
       names than the reference's placeholders ("Normal Polishing / Marble
       Ghisai" against "Normal Polishing (Ghisai)"); at the reference width
       the longest wrapped to five lines. Wider cards keep it to four and
       produce the intended peek at the row's edge.
   ========================================================================== */

/* Section heading: rule — text — rule, centred as a group.
   The rules are capped rather than flexible: the reference draws them at
   ~72dp each, not stretched to the content edges. */
.hb-main .hb-section-heading,
body.home .elementor-shortcode > h2 {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--hb-space-16);
	margin-block-end: var(--hb-space-24);
	/* .hb-main h2 reserves 16px below every heading for the brass accent
	   bar that this treatment replaces. Left in, it reads as a stray gap
	   under the rules. */
	padding-block-end: 0;
	font-size: 1.25rem;
	font-weight: 700;
	line-height: 1.25;
	letter-spacing: -0.01em;
	text-align: center;
	color: var(--hb-color-ink);
}
/* Both selectors carry the reset because the real site's heading is an
   Elementor widget (body.home .elementor-shortcode > h2) while the theme's
   own markup uses the class. They must not drift apart. */
.hb-main .hb-section-heading::before,
.hb-main .hb-section-heading::after,
body.home .elementor-shortcode > h2::before,
body.home .elementor-shortcode > h2::after {
	content: '';
	/* .hb-main h2::after is an absolutely-positioned 48x3 brass bar pinned
	   to the heading's bottom-left. Unset that geometry or it never joins
	   the flex row — it just floats under the text as a stray dash. */
	position: static;
	inset: auto;
	flex: 1 1 auto;
	/* Capped, not stretched. The reference draws each rule at ~72dp; letting
	   them flex to the content edges is a different, much wider look. */
	width: auto;
	max-width: 4.5rem;
	height: 1px;
	border-radius: 0;
	background: var(--hb-color-divider);
}

@media (max-width: 1023.98px) {
	/* ---- Group heading: "Marble Polishing" / "Tile Cleaning" ---------
	   Was small uppercase stone-grey, which read as a label above the
	   cards rather than as the service it names. The reference gives it
	   real weight in the primary ink. */
	.hb-catalogue .hb-catalogue-group-title {
		margin-block-end: calc(var(--hb-space-8) + var(--hb-space-4));
		font-size: 1.125rem;
		font-weight: 700;
		text-transform: none;
		letter-spacing: -0.01em;
		color: var(--hb-color-ink);
	}
	.hb-catalogue .hb-catalogue-group + .hb-catalogue-group {
		margin-block-start: var(--hb-space-32);
	}

	/* ---- The row: a real swipe strip, never a wrapping grid ---------- */
	.hb-catalogue .hb-catalogue-grid {
		display: flex;
		flex-wrap: nowrap;
		gap: var(--hb-space-8);
		margin: 0;
		padding: 0;
		overflow-x: auto;
		overscroll-behavior-x: contain;
		scroll-snap-type: x proximity;
		scroll-padding-inline-start: 0;
		scrollbar-width: none;
		-webkit-overflow-scrolling: touch;
	}
	.hb-catalogue .hb-catalogue-grid::-webkit-scrollbar {
		display: none;
	}
	/* Four-up (2026-08-25). Marble Polishing has four offerings and they
	   are the business — seeing three with a fourth sliced off understates
	   the range at the exact moment a visitor is deciding what HouseBro
	   does. The width is derived, not fixed: exactly a quarter of the row
	   minus the three gaps, so four cards land inside the container at
	   every width instead of only at the one it was tuned for. Capped at
	   84px so they do not inflate on the widest phones.

	   Tile Cleaning has three offerings and inherits the same width, which
	   leaves trailing space on its row — which is what the reference shows
	   too, rather than three stretched cards. */
	.hb-catalogue .hb-catalogue-grid > li {
		flex: 0 0 auto;
		/* Below 375px the row stays a carousel: a fixed 82px card with the
		   fourth peeking past the edge. Deriving the width from a quarter of
		   the row instead would give 66px cards at 320px, which is too
		   cramped to read as premium — the peek communicates "more here"
		   without shrinking anything. Four-up takes over at 375px, below. */
		width: 5.125rem;
		scroll-snap-align: start;
	}

	/* ---- The card ---------------------------------------------------- */
	.hb-catalogue .hb-catalogue-tile {
		display: flex;
		flex-direction: column;
		align-items: center;
		/* Centred, not flex-start. Equalising heights means the shorter
		   labels inherit the tallest card's height, and with flex-start all
		   of that slack pooled under the label as dead space. */
		justify-content: center;
		gap: var(--hb-space-8);
		/* height:100% against a stretched flex item is what equalises the
		   row — the tallest label sets the height for all of them. */
		height: 100%;
		min-height: 0;
		/* Block padding trimmed from 16 to 12 (2026-08-25): the narrower
		   four-up cards give the longest name more lines, and this buys
		   back most of the height that would otherwise add. */
		padding: calc(var(--hb-space-8) + var(--hb-space-4)) var(--hb-space-8);
		background: #fff;
		border: 1px solid var(--hb-color-divider);
		border-radius: var(--hb-radius-card);
		box-shadow: none;
		text-align: center;
		text-decoration: none;
		color: var(--hb-color-ink);
	}
	.hb-catalogue .hb-catalogue-tile-icon {
		color: var(--hb-color-ink);
	}
	.hb-catalogue .hb-catalogue-tile-icon .hb-icon {
		width: 30px;
		height: 30px;
	}
	.hb-catalogue .hb-catalogue-tile-name {
		/* 12px, down from 13. The real names are long and must never be
		   shortened or truncated, so the type gives way before the words
		   do — never below 11px (see the narrow-screen block). */
		font-size: 0.75rem;
		font-weight: 600;
		line-height: 1.3;
		/* break-word, not anywhere: only split a word that genuinely cannot
		   fit, instead of hyphen-free mid-word breaks like "Polishin/g". */
		overflow-wrap: break-word;
	}

	/* Flagship emphasis now lives in one width-independent block at the end
	   of this file — see "FLAGSHIP EMPHASIS". */
	/* Flagship caption: see the FLAGSHIP EMPHASIS block at the end. */
}


/* ==========================================================================
   PHASE 6 — STICKY CALL / WHATSAPP BAR (2026-08-25)

   Measured off the approved reference, scaled to a 390px screen:

     button height      44.9dp   (this build: 48 — HouseBro's own documented
                                  touch-target size, and the bar's job is to
                                  be tapped)
     button width       ~175dp each, equal
     gap between        9.2dp
     icon               18-20dp
     label              ~16px, bold, one line, centred with the icon
     radius             modest, not a pill

   Two problems this fixes, both visible in the rendered screenshots:

   1. "WhatsApp Us" wrapped to two lines in a half-width button, which made
      the bar 69px tall and the two actions look unequal. The label is now
      "WhatsApp" (set at the call site in footer.php) and the buttons are
      nowrap, so a long label can never silently re-break the bar again.

   2. Call Now was a brass-filled block — the single largest area of brass
      on the page, sitting on the most-seen element on the site. Brass is an
      accent in this system, not a fill at this size. The reference gives
      its primary action the darkest brand colour and the secondary the
      WhatsApp green; ink plays the dark role here.

   Contrast is better than what it replaces, not worse: white on ink is
   ~17:1, against ~4.3:1 for the ink-on-brass pairing it replaces. WhatsApp
   keeps ink-on-green (~8.6:1) — white on #25D366 is ~2:1 and was correctly
   rejected earlier; that reasoning still holds.
   ========================================================================== */
@media (max-width: 1023.98px) {
	.hb-sticky-cta {
		gap: var(--hb-space-8);
		padding-block: var(--hb-space-8);
		padding-inline: var(--hb-space-16);
		/* Home-indicator phones: keep the buttons above the gesture area
		   without padding the bar on devices that have no inset. */
		padding-block-end: calc(var(--hb-space-8) + env(safe-area-inset-bottom, 0px));
	}
	.hb-sticky-cta .hb-btn {
		flex: 1 1 0;
		/* min-width:0 lets the pair stay exactly equal; without it each
		   button refuses to shrink below its label's intrinsic width and
		   the wider label steals space from the other. */
		min-width: 0;
		min-height: 48px;
		gap: var(--hb-space-8);
		/* The base 24px inline padding is for a standalone button. Here the
		   button is already half the viewport and the padding was what
		   pushed "WhatsApp Us" into a second line. */
		padding-inline: var(--hb-space-8);
		border-radius: var(--hb-radius-button);
		font-size: var(--hb-font-size-button);
		font-weight: 700;
		white-space: nowrap;
	}
	.hb-sticky-cta .hb-btn .hb-icon {
		width: 18px;
		height: 18px;
	}
	/* Ink, not brass — see the note above. */
	.hb-sticky-cta .hb-btn-call {
		background: var(--hb-color-ink);
		color: #fff;
	}
	.hb-sticky-cta .hb-btn-call .hb-icon {
		color: #fff;
	}
}

/* Must track the bar's real height (8 + 48 + 8 + 1px border = 65) or the
   last section on the page sits underneath it. */
body.hb-has-sticky-cta {
	padding-bottom: calc(65px + env(safe-area-inset-bottom, 0px));
}
@media (min-width: 1024px) {
	body.hb-has-sticky-cta {
		padding-bottom: 0;
	}
}

/* ==========================================================================
   PHASE 7 — SPACING, HIERARCHY & FINAL POLISH (2026-08-25)

   Measured off the approved reference at 390px, from the top of the white
   content sheet:

     sheet top -> "Our Services"        27.6dp to the cap, ~24dp to the box
     "Our Services" -> "Marble Polish"  32.7dp cap to cap
     "Marble Polishing" -> first card   17.4dp cap to card
     sheet top -> first card            ~101dp for the whole heading block

   Only the first of those was wrong. Measured before touching anything, the
   build already matched the reference on the other two — 24px box-to-box
   between the headings is ~34dp cap-to-cap, and 12px above the cards is
   ~17dp from the cap. Those are left exactly as they were.

   The gap was 136px, and it was three stacked sources, not one:

       24px   .e-con:has(.hb-hero) + .e-con  padding-block-start
     + 48px   .hb-section                    padding-block
     + 64px   .hb-main h2                    margin-block-start
     ------
      136px

   The sheet's own 24px is already the reference's separation. The other two
   are what pushed "Our Services" a full screen-height away from the hero and
   made the section read as a disconnected slab floating below it.
   ========================================================================== */

/* The 64px belongs to the sitewide "section rhythm" rule, which predates
   these headings having their own section padding. On a section heading the
   two stack, so the heading's own margin goes. Applies at every width —
   this is the cleanup, not a mobile patch. */
.hb-main .hb-section-heading,
body.home .elementor-shortcode > h2 {
	margin-block-start: 0;
}

@media (max-width: 1023.98px) {
	/* The sheet already supplies the reference's ~24px above the heading, so
	   the first section inside it must not add its own 48px on top. Scoped
	   to the section that actually follows the hero: later sections keep
	   their normal top padding, and desktop keeps its 96px. */
	.e-con:has(.hb-hero) + .e-con > .hb-section:first-child {
		padding-block-start: 0;
	}
}

/* Hero carousel headline, desktop (Phase 7). Slide 1 is an <h1> and slides
   2-3 are <h2>; .hb-hero-content h1 outranks .hb-hero-title, so the first
   slide rendered 52px against the others' 48px and the headline visibly
   jumped mid-swipe. Phase 2 fixed this below 1024px; this closes it above.
   One declaration for both elements, so they cannot drift again. */
@media (min-width: 1024px) {
	.hb-hero-carousel .hb-hero-title,
	.hb-hero-carousel .hb-hero-content h1 {
		font-size: clamp(2rem, 1.2rem + 2.6vw, 3rem);
	}
}

/* ==========================================================================
   FLAGSHIP EMPHASIS — Premium Silicate Polishing, Diamond Polishing
   (Phase 5, consolidated to all widths in Phase 7, 2026-08-25)

   Defined once, with no media query, because the two flagship techniques
   should not look like different things on a phone and on a desktop. This
   replaces three separate treatments that had accumulated: a brass border
   plus brass gradient on the card, a filled brass pill, and a mobile-only
   override of both.

   The card keeps exactly the shape, surface and border of its neighbours.
   The only two differences are a brass glyph and a small caption — enough
   to mark them, not enough to shout.
   ========================================================================== */
.hb-catalogue .hb-catalogue-tile--flagship .hb-catalogue-tile-icon {
	color: var(--hb-color-brass);
}
/* Not a pill: no background, no border, no radius. Ink rather than brass —
   brass on white measures ~4:1, under the AA floor at this size, the same
   arithmetic that put ink on the brass button elsewhere in this file. The
   glyph above carries the premium cue; the caption only has to be read. */
.hb-catalogue .hb-catalogue-tile--flagship .hb-flagship-badge {
	margin: 0;
	padding: 0;
	border: 0;
	border-radius: 0;
	background: none;
	font-size: 0.625rem;
	font-weight: 700;
	line-height: 1;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--hb-color-ink);
}

/* Four-up from 375px (2026-08-25). Marble Polishing has four offerings and
   all four should be in view together on a normal phone — three plus a
   sliver understates the range at the moment a visitor is deciding what
   HouseBro does.

   The width is a derived quarter of the row rather than a fixed number, so
   four cards land inside the container at whatever width the device is,
   capped at 84px so they do not inflate on the widest phones. At 375px the
   arithmetic yields ~80px: 82px would need 352px of row against the 343px
   available, so the cards give up two pixels rather than the names giving
   up a character.

   Below 375px this rule simply does not apply and the base carousel with
   its peeking fourth card stands. Scoped with max-width too, because the
   Phase 4 block it belongs to is mobile-only and plain CSS has no nesting
   here. */
@media (min-width: 375px) and (max-width: 1023.98px) {
	.hb-catalogue .hb-catalogue-grid > li {
		width: min(5.25rem, calc((100% - 3 * var(--hb-space-8)) / 4));
	}
}

/* ==========================================================================
   FOOTER CONTACT BLOCK (2026-08-25)

   The contact details now sit inside the brand column instead of a fifth
   column of their own. In that narrow column the phone number, the email
   and the three-line address all wrapped mid-value; here they have the
   width to sit on one line each.

   Presentation only — same [hb_contact_card] shortcode, same Global
   Settings, same WhatsApp number, same markup. The Contact page renders
   the identical card and is untouched.
   ========================================================================== */
.hb-footer-brand .hb-contact-card {
	display: flex;
	flex-direction: column;
	gap: var(--hb-space-8);
	margin-block-start: var(--hb-space-24);
	color: var(--hb-color-divider);
}
.hb-footer-brand .hb-contact-card-row {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: var(--hb-space-4) var(--hb-space-8);
	margin: 0;
	line-height: var(--hb-line-height-small);
}
.hb-footer-brand .hb-contact-card-row strong {
	color: var(--hb-color-marble-white);
	font-weight: 600;
	flex: 0 0 auto;
}
/* A phone number or an email must never break across lines — they are read
   and dialled as one token. Long values scroll their own overflow rather
   than widening the footer. */
.hb-footer-brand .hb-contact-card-row a {
	color: var(--hb-color-divider);
	overflow-wrap: anywhere;
}
.hb-footer-brand .hb-contact-card-row a[href^='tel:'] {
	white-space: nowrap;
	overflow-wrap: normal;
}
/* Scoped to the contact ROWS, not the whole card (2026-08-30). The card's
   last child is the "Message on WhatsApp" button, and its own glyph is a
   .hb-icon too — a card-wide selector resized and recoloured that glyph
   as though it were a contact-detail marker. Every icon this rule and
   the badge rule below are written for is a direct child of a
   .hb-contact-card-row, so the child combinator says exactly that and
   leaves the button's icon to the .hb-btn rules that own it. */
.hb-footer-brand .hb-contact-card-row > .hb-icon {
	width: 16px;
	height: 16px;
	flex: 0 0 auto;
	color: var(--hb-color-brass);
}
/* The card renders WhatsApp between email and address; in the footer it
   reads better as the closing action under the details it belongs to.
   Ordering only — the button, its link and its label are unchanged. */
.hb-footer-brand .hb-contact-card .hb-btn-whatsapp {
	order: 9;
	align-self: flex-start;
	margin-block-start: var(--hb-space-8);
}

@media (min-width: 1024px) {
	/* Keeps the address and the button from stretching the full column on
	   wide desktops, where the brand column is at its widest. */
	.hb-footer-brand .hb-contact-card {
		max-width: 22rem;
	}
}

/* ==========================================================================
   FOOTER — PREMIUM PASS (2026-08-26)

   Brings the footer up to the approved reference: a brass rule under each
   column heading, brass markers on the link lists, and the contact details
   as icon badges with the label above the value.

   Presentation only. Every link, URL, shortcode and phone/WhatsApp target
   is untouched; no markup was added for any of this — it hangs off the
   elements footer.php and [hb_contact_card] already render.
   ========================================================================== */

/* ---- Column headings: short brass rule beneath ------------------------- */
.hb-footer h3 {
	position: relative;
	padding-block-end: var(--hb-space-8);
}
.hb-footer h3::after {
	content: '';
	position: absolute;
	inset-inline-start: 0;
	inset-block-end: 0;
	width: 2.25rem;
	height: 2px;
	border-radius: var(--hb-radius-pill);
	background: var(--hb-color-brass);
}

/* ---- Link lists: brass chevron markers --------------------------------- */
.hb-footer-column ul li {
	position: relative;
	padding-inline-start: var(--hb-space-24);
}
.hb-footer-column ul li::before {
	content: '';
	position: absolute;
	inset-inline-start: 3px;
	top: 0.95em;
	width: 5px;
	height: 5px;
	border-inline-end: 1.6px solid var(--hb-color-brass);
	border-block-end: 1.6px solid var(--hb-color-brass);
	transform: rotate(-45deg);
}

/* The shared .hb-areas-we-serve-item rule zeroes padding (it cancels the
   pill styling this component carries on light backgrounds). That left the
   pin sitting underneath the city name instead of beside it — the marker
   was painting, just behind the text. Restoring the inline padding the
   other footer lists get. */
.hb-footer-column .hb-areas-we-serve-item {
	padding-inline-start: var(--hb-space-24);
}

/* Areas We Serve gets a pin instead of a chevron, as the reference has it.

   Drawn in CSS rather than as a masked data-URI. The mask version computed
   correctly — the right size, the brass background, mask-image set — and
   still rendered nothing, because a mask whose image fails to decode masks
   the element away entirely rather than falling back. A circle with one
   square corner, rotated, is the same shape with nothing to decode, and it
   takes the brass token directly. */
.hb-footer-column .hb-areas-we-serve-item::before {
	inset-inline-start: 3px;
	top: 0.82em;
	width: 9px;
	height: 9px;
	border: 2px solid var(--hb-color-brass);
	border-radius: 50% 50% 50% 0;
	transform: rotate(-45deg);
	background: none;
}

/* ---- Contact block: icon badge, label above value ---------------------- */
.hb-footer-brand .hb-contact-card-row {
	display: grid;
	grid-template-columns: 34px 1fr;
	column-gap: var(--hb-space-16);
	row-gap: 2px;
	align-items: center;
}
/* The badge spans both rows so the label/value pair sits beside it. Same
   row-scoping as the sizing rule above, and for the same reason:
   unscoped, this rule put a 34px solid-brass disc behind the WhatsApp
   button's glyph — a brown circle inside a green button, in the footer
   of every page on the site. */
.hb-footer-brand .hb-contact-card-row > .hb-icon {
	grid-row: 1 / span 2;
	grid-column: 1;
	box-sizing: border-box;
	width: 34px;
	height: 34px;
	padding: 8px;
	border-radius: var(--hb-radius-pill);
	background: var(--hb-color-brass);
	color: var(--hb-color-ink);
}
.hb-footer-brand .hb-contact-card-row strong {
	grid-column: 2;
	align-self: end;
}
/* The value — an <a> for phone/email, a bare text node for the address —
   falls into column 2, row 2 on its own. */
.hb-footer-brand .hb-contact-card-row > a,
.hb-footer-brand .hb-contact-card-row > span {
	grid-column: 2;
	align-self: start;
}

/* ==========================================================================
   MOBILE HEADER — SERVICES CONTROL (2026-08-26)
   ==========================================================================
   Replaces the hamburger below 1024px. Written as a deliberate sibling of
   .hb-city-indicator above, not as a new pattern: same 44px touch target
   in a 56px header, same 16px brass leading glyph, same 13px stone-grey
   chevron, same font size and weight. The pair has to read as one set of
   controls, so the shared values are copied rather than approximated.

   Where it differs from the city chip, and why:
   - flex-shrink: 0. The city name can ellipse under pressure; "Services"
     cannot, because a truncated verb is not a label. This continues the
     header's existing priority order — logo never shrinks, city absorbs
     the pressure — and adds Services above city, below logo.
   - it owns a popover, so it needs position: relative.
   ========================================================================== */
.hb-services-selector {
	position: relative;
	flex-shrink: 0;
}
.hb-services-toggle {
	display: inline-flex;
	align-items: center;
	gap: var(--hb-space-4);
	min-height: 44px;
	padding-inline: var(--hb-space-4);
	background: none;
	border: none;
	font: inherit;
	font-size: var(--hb-font-size-small);
	font-weight: 700;
	color: var(--hb-color-ink);
	cursor: pointer;
	white-space: nowrap;
}
.hb-services-toggle .hb-icon {
	width: 16px;
	height: 16px;
	color: var(--hb-color-brass);
	flex-shrink: 0;
}
/* Two classes deep so it outranks the 16px rule above — same trick, same
   reason, as the city chip's chevron. Quiet affordance, not brand accent. */
.hb-services-toggle .hb-services-chevron {
	width: 13px;
	height: 13px;
	color: var(--hb-color-stone-grey);
	transition: transform var(--hb-transition-duration) var(--hb-transition-easing);
}
.hb-services-selector[data-open='true'] .hb-services-chevron {
	transform: rotate(180deg);
}

/* The popover. Right-aligned because the control sits at the end of the
   header row — a left-aligned menu would hang off the viewport at 320px. */
.hb-services-menu {
	position: absolute;
	inset-block-start: calc(100% + var(--hb-space-4));
	inset-inline-end: 0;
	z-index: 60;
	display: none;
	min-width: 190px;
	margin: 0;
	padding: var(--hb-space-8);
	list-style: none;
	background: var(--hb-color-marble-white);
	border: 1px solid var(--hb-color-divider);
	border-radius: var(--hb-radius-card);
	box-shadow: 0 12px 28px rgba(28, 28, 30, 0.16);
}
/* Two openers. [data-open] is js/services-selector.js; :focus-within is
   the no-JS path, so a keyboard user still reaches both links if the
   script fails to load. Neither is a modal — the page behind stays
   scrollable, which is the difference from the panel this replaced. */
.hb-services-selector[data-open='true'] .hb-services-menu,
.hb-services-selector:focus-within .hb-services-menu {
	display: block;
}
.hb-services-menu li + li {
	margin-block-start: 2px;
}
.hb-services-menu a {
	display: block;
	padding: var(--hb-space-8) var(--hb-space-8);
	border-radius: var(--hb-radius-button);
	font-size: var(--hb-font-size-small);
	font-weight: 600;
	color: var(--hb-color-ink);
	text-decoration: none;
}
.hb-services-menu a:hover,
.hb-services-menu a:focus-visible {
	background: rgba(156, 122, 60, 0.1);
	color: var(--hb-color-brass);
}

/* Desktop keeps the navigation it already had. .hb-header-nav carries its
   own Services dropdown at this width, and two Services controls in one
   header is a worse header. */
@media (min-width: 1024px) {
	.hb-services-selector {
		display: none;
	}
}

/* 320-359px. The row is brand + city + Services here, and Services is the
   widest of the three additions the hamburger did not make. The label
   holds; the padding and the gap give way first, and the city name keeps
   its existing ellipsis as the final release valve. Measured at 320px with
   "Greater Noida" selected, the longest real city name in the taxonomy. */
@media (max-width: 359.98px) {
	.hb-services-toggle {
		font-size: 0.8125rem;
		padding-inline: 0;
		gap: 3px;
	}
	.hb-services-toggle .hb-icon {
		width: 14px;
		height: 14px;
	}
	.hb-services-toggle .hb-services-chevron {
		width: 11px;
		height: 11px;
	}
}

/* ==========================================================================
   APPROVED DESIGN — SERVICES GRID & TESTIMONIALS (2026-08-26)

   Both sections come from the approved reference in 13_REFERENCES/. The
   two components below are new; the heading treatment is a homepage-only
   override of an existing one, explained at .hb-section-head.
   ========================================================================== */

/* Section head: title on the left, optional link on the right.

   The existing .hb-section-heading treatment (rule — text — rule, centred)
   is NOT reused here, because the approved design left-aligns every
   homepage section title and hangs "View All" off the opposite edge. A
   centred heading has no opposite edge to hang anything from.

   Deliberately a separate class rather than an edit to .hb-section-heading:
   that selector also styles the service and city pages, and nothing was
   approved about changing those. The two treatments coexist — this one is
   used by the sections that have a link, the centred one keeps the rest. */
.hb-section-head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--hb-space-16);
	margin-block-end: var(--hb-space-16);
}
.hb-main .hb-section-head .hb-section-title {
	margin: 0;
	padding-block-end: 0;
	font-size: 1.25rem;
	font-weight: 700;
	line-height: 1.25;
	letter-spacing: -0.01em;
	text-align: start;
	color: var(--hb-color-ink);
}
/* .hb-main h2::after is an absolutely-positioned brass accent bar. In a
   baseline-aligned flex row it lands under the link rather than the title,
   so it is unset here rather than left to drift. */
.hb-main .hb-section-head .hb-section-title::after {
	content: none;
}
.hb-section-link {
	flex: 0 0 auto;
	font-size: var(--hb-font-size-small);
	font-weight: 600;
	text-decoration: none;
	color: var(--hb-brand-green);
	/* The link is a 14px word in a heading row; the touch target has to
	   come from padding, not from the text box. */
	padding: var(--hb-space-8) 0;
}
.hb-section-link:hover,
.hb-section-link:focus-visible {
	text-decoration: underline;
}

/* Services grid — [hb_services_grid].

   Three across from 360px up, which is what the approved reference shows
   and what a six-service catalogue needs to stay two tidy rows. Two across
   below that so a name like "Marble Crystallization" is not wrapped to
   four lines on the narrowest phones still in use. */
.hb-services-grid {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: var(--hb-space-8);
	list-style: none;
	margin: 0;
	padding: 0;
}
@media (min-width: 360px) {
	.hb-services-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 768px) {
	.hb-services-grid { gap: var(--hb-space-16); }
}
.hb-service-tile {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: var(--hb-space-8);
	height: 100%;
	min-height: 104px;
	padding: var(--hb-space-16) var(--hb-space-8);
	text-align: center;
	text-decoration: none;
	color: var(--hb-color-ink);
}
.hb-service-tile-icon {
	display: inline-flex;
	color: var(--hb-brand-green);
}
.hb-service-tile-icon .hb-icon {
	width: 28px;
	height: 28px;
}
.hb-service-tile-name {
	font-size: var(--hb-font-size-small);
	font-weight: 600;
	line-height: 1.3;
	/* "Marble Crystallization" is 21 characters with no break opportunity
	   in a ~100px column. Without this it sets the grid's minimum width and
	   pushes the whole page into a horizontal scroll. */
	overflow-wrap: anywhere;
}

/* Testimonials — [hb_testimonials].

   One card per review, stacked on mobile, two across once there is room.
   No carousel: with two or three reviews a carousel hides most of the
   evidence behind a control, which is the opposite of what the section is
   for. */
.hb-testimonials {
	display: grid;
	gap: var(--hb-space-16);
	list-style: none;
	margin: 0;
	padding: 0;
}
@media (min-width: 768px) {
	.hb-testimonials { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.hb-testimonial {
	display: flex;
	flex-direction: column;
	gap: var(--hb-space-8);
	padding: var(--hb-space-24) var(--hb-space-16);
}
.hb-testimonial-rating {
	display: inline-flex;
	gap: 2px;
	/* Brand gold, not brass: this is the one place on the site where a
	   gold mark sits alone with no wordmark beside it to clash with, and
	   a rating star reads as gold or it reads as decoration. */
	color: var(--hb-brand-gold);
}
.hb-testimonial-star {
	width: 16px;
	height: 16px;
}
.hb-testimonial-quote {
	margin: 0;
	font-size: var(--hb-font-size-body);
	line-height: var(--hb-line-height-body);
	color: var(--hb-color-ink);
}
.hb-testimonial-author {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: var(--hb-space-8);
	margin-block-start: auto;
	padding-block-start: var(--hb-space-8);
	font-size: var(--hb-font-size-small);
}
.hb-testimonial-name {
	font-weight: 600;
	color: var(--hb-color-ink);
}
.hb-testimonial-name::before {
	/* An em dash before the name, drawn in CSS so the attribution reads as
	   an attribution without the markup carrying a decorative character
	   that a screen reader would announce. */
	content: '\2014\00a0';
	color: var(--hb-color-stone-grey);
}
.hb-testimonial-city {
	color: var(--hb-color-stone-grey);
}

/* ==========================================================================
   APPROVED DESIGN — BRAND PROCESS TIMELINE (2026-08-26)
   [hb_brand_process]. Vertical timeline: ringed icon marker on the left,
   connected top-to-bottom by a line that must stop at the last step.
   ========================================================================== */

.hb-brand-process {
	list-style: none;
	margin: 0;
	padding: 0;
}
.hb-brand-process-step {
	position: relative;
	display: flex;
	align-items: flex-start;
	gap: var(--hb-space-16);
	/* The gap between steps is padding on the step itself, not a grid gap,
	   so the connector line below can span it. */
	padding-block-end: var(--hb-space-24);
}
/* The connector. Drawn from the marker's vertical centre downwards, behind
   the marker (which has its own opaque background), and suppressed on the
   last step so the line does not trail off into empty space. */
.hb-brand-process-step::before {
	content: '';
	position: absolute;
	inset-block: 24px 0;
	inset-inline-start: 23px;
	width: 1px;
	background: var(--hb-color-divider);
}
.hb-brand-process-step:last-child {
	padding-block-end: 0;
}
.hb-brand-process-step:last-child::before {
	content: none;
}
.hb-brand-process-marker {
	position: relative;
	z-index: 1;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: 0 0 auto;
	width: 48px;
	height: 48px;
	border: 1px solid var(--hb-color-divider);
	border-radius: var(--hb-radius-pill);
	/* Opaque so the connector passes behind the ring, not through it. */
	background: var(--hb-color-marble-white);
	color: var(--hb-brand-green);
}
.hb-brand-process-marker .hb-icon {
	width: 22px;
	height: 22px;
}
.hb-brand-process-body {
	/* min-width:0 lets a long stage name wrap inside the flex row instead
	   of forcing the row wider than the page. */
	min-width: 0;
	padding-block-start: var(--hb-space-4);
}
.hb-main .hb-brand-process-title {
	margin: 0;
	padding-block-end: 0;
	font-size: var(--hb-font-size-body);
	font-weight: 600;
	line-height: 1.3;
	color: var(--hb-brand-green);
}
.hb-main .hb-brand-process-title::after {
	/* Unset the global h3 brass accent bar — in a timeline row it reads as
	   a stray dash between the title and its description. */
	content: none;
}
.hb-brand-process-text {
	margin: var(--hb-space-4) 0 0;
	font-size: var(--hb-font-size-small);
	line-height: var(--hb-line-height-small);
	color: var(--hb-color-stone-grey);
}

/* ==========================================================================
   APPROVED DESIGN — HERO (2026-08-26)

   Eyebrow pill, brand-green headline accent, and the four-mark trust row
   that replaced the three long text pills (.hb-hero-badges, whose rules
   remain above for the pillar and city Heroes that still use it).
   ========================================================================== */

.hb-hero-eyebrow {
	display: inline-flex;
	align-items: center;
	gap: var(--hb-space-8);
	margin: 0 0 var(--hb-space-16);
	padding: 6px var(--hb-space-16);
	border: 1px solid var(--hb-color-divider);
	border-radius: var(--hb-radius-pill);
	background: var(--hb-color-marble-white);
	font-size: 0.75rem;
	font-weight: 700;
	/* Uppercased in CSS, not in the field, so the admin types normal
	   sentence case and the stored value stays readable everywhere else
	   it might be reused. */
	text-transform: uppercase;
	letter-spacing: 0.08em;
	line-height: 1.3;
	color: var(--hb-brand-green);
}
.hb-hero-eyebrow .hb-icon {
	width: 14px;
	height: 14px;
	flex: 0 0 auto;
}

/* The one phrase an admin marks with [hl]...[/hl]. Brand green is the
   only green permitted for brand use (see global-tokens.css) — the
   success and WhatsApp greens are functional colours and must not be
   borrowed for emphasis. */
.hb-hero-highlight {
	color: var(--hb-brand-green);
}

/* Four marks across, icon over label. Grid rather than flex so all four
   columns are equal regardless of label length — "Professional
   Restoration" wrapping to two lines must not make its neighbour narrower. */
.hb-hero-trust-row {
	display: grid;
	grid-template-columns: repeat(4, minmax(0, 1fr));
	gap: var(--hb-space-8);
	list-style: none;
	margin: var(--hb-space-24) 0 var(--hb-space-24);
	padding: 0;
}
.hb-hero-trust-row li {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 6px;
	text-align: center;
}
.hb-hero-trust-icon {
	display: inline-flex;
	color: var(--hb-color-brass);
}
.hb-hero-trust-icon .hb-icon {
	width: 24px;
	height: 24px;
}
.hb-hero-trust-label {
	font-size: 0.6875rem;
	font-weight: 600;
	line-height: 1.25;
	color: var(--hb-color-ink);
	overflow-wrap: anywhere;
}

/* Over a photo Hero the row sits on the dark scrim, so both the glyph and
   the label invert. Scoped with :has() exactly like the existing scrim
   overrides above, so a Hero with no image keeps the light treatment. */
.hb-hero:has(.hb-hero-bg) .hb-hero-eyebrow {
	border-color: rgba(250, 247, 242, 0.35);
	background: rgba(28, 28, 30, 0.35);
	color: var(--hb-color-marble-white);
}
.hb-hero:has(.hb-hero-bg) .hb-hero-highlight {
	/* Brand green at #0F4D3A is far too dark to read on the scrim. The
	   brand gold is the approved second accent and clears contrast here,
	   and no wordmark sits beside it in the Hero to clash with. */
	color: var(--hb-brand-gold);
}
.hb-hero:has(.hb-hero-bg) .hb-hero-trust-icon {
	color: var(--hb-color-marble-white);
}
.hb-hero:has(.hb-hero-bg) .hb-hero-trust-label {
	color: rgba(255, 255, 255, 0.92);
}

@media (max-width: 1023.98px) {
	/* Side by side, not stacked (approved design). The rule above at this
	   same breakpoint sets flex: 1 1 100% on photo Heroes, which stacks
	   them; the approved Hero shows one row of two. 8px gap and half-width
	   buttons still clear 44px at 320px. */
	.hb-hero:has(.hb-hero-bg) .hb-hero-cta .hb-btn {
		flex: 1 1 0;
		min-width: 0;
	}
}

/* 359px and below the four labels get too narrow to read. Two rows of two
   is the release valve — still four marks, still one block, just folded. */
@media (max-width: 359.98px) {
	.hb-hero-trust-row {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: var(--hb-space-16) var(--hb-space-8);
	}
}

/* ==========================================================================
   APPROVED DESIGN — WORK IN ACTION, CLOSING CTA, FLOATING CHAT (2026-08-26)
   ========================================================================== */

/* Full-bleed dark band around the before/after evidence. The band breaks
   out of the page container with the 50vw margin trick so the colour
   reaches both edges while its contents stay on the same measure as every
   other section. */
.hb-work-in-action {
	margin-inline: calc(50% - 50vw);
	padding-block: var(--hb-space-48);
	background: var(--hb-brand-green);
	/* 100vw includes the scrollbar's width on desktop; without this the
	   band is a few pixels wider than the viewport and the page gains a
	   horizontal scroll. */
	max-width: 100vw;
	overflow-x: hidden;
}
.hb-work-in-action-inner {
	max-width: 1200px;
	margin-inline: auto;
	padding-inline: var(--hb-space-16);
}
.hb-main .hb-work-in-action .hb-section-title {
	margin: 0;
	padding-block-end: 0;
	font-size: 1.25rem;
	font-weight: 700;
	line-height: 1.25;
	color: var(--hb-color-marble-white);
}
.hb-main .hb-work-in-action .hb-section-title::after {
	content: none;
}
.hb-work-in-action-subtitle {
	margin: var(--hb-space-8) 0 var(--hb-space-24);
	font-size: var(--hb-font-size-small);
	line-height: var(--hb-line-height-small);
	color: rgba(250, 247, 242, 0.78);
}
/* The gallery inside keeps its own card styling; only the text that sits
   directly on the green needs reversing out. */
.hb-work-in-action .hb-empty-state,
.hb-work-in-action .hb-before-after-caption {
	color: rgba(250, 247, 242, 0.78);
}
.hb-work-in-action .hb-before-after-link a {
	color: var(--hb-color-marble-white);
}

/* Closing CTA panel. */
.hb-cta-block {
	padding: var(--hb-space-32) var(--hb-space-24);
	border-radius: var(--hb-radius-card-lg);
	background: var(--hb-brand-green);
	text-align: center;
	color: var(--hb-color-marble-white);
}
.hb-main .hb-cta-block-title {
	margin: 0;
	padding-block-end: 0;
	font-size: var(--hb-font-size-h3);
	font-weight: 700;
	line-height: 1.25;
	color: var(--hb-color-marble-white);
}
.hb-main .hb-cta-block-title::after {
	content: none;
}
.hb-cta-block-subtitle {
	margin: var(--hb-space-8) 0 var(--hb-space-24);
	font-size: var(--hb-font-size-small);
	color: rgba(250, 247, 242, 0.82);
}
.hb-cta-block-actions {
	display: flex;
	flex-direction: column;
	gap: var(--hb-space-8);
}
@media (min-width: 560px) {
	.hb-cta-block-actions {
		flex-direction: row;
		justify-content: center;
	}
	.hb-cta-block-actions .hb-btn {
		flex: 0 1 240px;
	}
}
/* On the green panel both buttons invert to solid marble-white. The green
   WhatsApp button in particular is close enough to the panel colour to
   disappear into it at a glance. */
.hb-cta-block-actions .hb-btn {
	justify-content: center;
	min-height: 52px;
	background: var(--hb-color-marble-white);
	border-color: var(--hb-color-marble-white);
	color: var(--hb-brand-green);
}
.hb-cta-block-actions .hb-btn:hover,
.hb-cta-block-actions .hb-btn:focus-visible {
	background: #fff;
	border-color: #fff;
	color: var(--hb-brand-green);
}
.hb-cta-block-phone {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--hb-space-8);
	margin: var(--hb-space-16) 0 0;
	font-size: var(--hb-font-size-small);
	color: rgba(250, 247, 242, 0.82);
}
.hb-cta-block-phone .hb-icon {
	width: 16px;
	height: 16px;
	flex: 0 0 auto;
}
.hb-cta-block-phone a {
	font-weight: 700;
	color: var(--hb-color-marble-white);
	text-decoration: none;
}
.hb-cta-block-phone a:hover {
	text-decoration: underline;
}

/* Floating WhatsApp disc. */
.hb-float-whatsapp {
	position: fixed;
	inset-inline-end: var(--hb-space-16);
	bottom: var(--hb-space-24);
	/* Below .hb-sticky-cta (999) so it can never cover the primary bar,
	   and below the city overlay (1200) so it does not float over a modal. */
	z-index: 998;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 52px;
	height: 52px;
	border-radius: var(--hb-radius-pill);
	background: var(--hb-color-whatsapp-green);
	color: #fff;
	box-shadow: var(--hb-shadow-overlay);
	transition: transform var(--hb-transition-duration) var(--hb-transition-easing);
}
.hb-float-whatsapp .hb-icon {
	width: 26px;
	height: 26px;
}
.hb-float-whatsapp:hover,
.hb-float-whatsapp:focus-visible {
	transform: translateY(-2px);
	color: #fff;
}
/* Lift clear of the sticky bar wherever that bar is actually drawn — the
   bar is ~68px tall and is hidden at 1024px and up, so the offset is
   scoped to the same breakpoint rather than applied always. */
@media (max-width: 1023.98px) {
	body.hb-has-sticky-cta .hb-float-whatsapp {
		bottom: calc(68px + var(--hb-space-16));
	}
}
@media (prefers-reduced-motion: reduce) {
	.hb-float-whatsapp {
		transition: none;
	}
	.hb-float-whatsapp:hover,
	.hb-float-whatsapp:focus-visible {
		transform: none;
	}
}

.hb-footer-social-title {
	margin-block-start: var(--hb-space-24);
}


/* ==========================================================================
   FOOTER — COLUMN BALANCE PASS (2026-08-27)

   Companion to the grid retune above and to footer.php's three restored
   columns. Typography and rhythm only: no link, URL, shortcode, phone or
   WhatsApp target is touched by anything in this block, and no new markup
   was added for it.

   The one substantive fix here is the type scale. .hb-footer h3 is 1rem
   while --hb-font-size-body resolves to 1.0625rem above 1024px, so the
   column headings were rendering SMALLER than the links underneath them —
   the opposite of the intended hierarchy, and the reason the old footer
   read as a flat list of text rather than as named groups.
   ========================================================================== */

/* ---- Headings sit above their links, not beside them ------------------- */
.hb-footer h3 {
	font-size: 1.0625rem;
	font-weight: 700;
	letter-spacing: -0.01em;
	line-height: var(--hb-line-height-h4);
}

/* ---- Link lists: one step down from body, set on the LI ---------------- */
/* On the LI, not the A: the brass chevron and pin markers are positioned in
   em units against the list item, so sizing the anchor alone would slide
   every marker out of line with its own label. */
.hb-footer-column ul li,
.hb-footer-column .hb-areas-we-serve-item {
	font-size: 0.9375rem;
	line-height: var(--hb-line-height-small);
}

/* A city with no live page yet renders as a <span>, not an <a>. It still
   belongs to the same list visually — the footer states coverage — so it
   takes the link colour and spacing and simply isn't clickable. */
.hb-footer-column .hb-areas-we-serve-item span {
	display: block;
	padding-block: var(--hb-space-4);
	color: var(--hb-color-divider);
}

/* ---- Brand column ------------------------------------------------------ */
/* The blurb is one sentence; letting it run the full width of the widest
   column in the row makes it read as body copy rather than as a strapline
   under the mark. */
.hb-footer-brand p {
	max-width: 22rem;
	font-size: 0.9375rem;
	line-height: var(--hb-line-height-small);
}

@media (min-width: 640px) {
	/* Tablet only — the brand block spans the full row here, so without a
	   cap the one-sentence strapline would stretch edge to edge. */
	.hb-footer-brand p {
		max-width: 30rem;
	}
}
@media (min-width: 1024px) {
	.hb-footer-brand p {
		max-width: 22rem;
	}
}



/* ==========================================================================
   HEADER — COMPACT COMPOSITION PASS (2026-08-27, desktop only)

   The header was functional but read as four items scattered across
   1248px rather than as a composed bar: .hb-header-inner is
   justify-content: space-between with four children, so the row's spare
   width was divided into three equal gaps and the brand, the city chip,
   the navigation and the CTAs each floated alone in the middle of one.

   The fix is grouping, not new elements. Left group: brand + city, which
   together answer "who is this and where do they work"; right group:
   navigation + the two CTAs, which are the things a visitor acts on. One
   auto margin does it — nothing is reordered, hidden or added, and no
   markup changed.

   Scoped to >=1024px. The mobile/tablet header (brand, city chip,
   Services control, 56px row) was rebuilt to its own reference on
   2026-08-26 and is deliberately untouched here.
   ========================================================================== */
@media (min-width: 1024px) {
	.hb-header-inner {
		justify-content: flex-start;
		gap: var(--hb-space-24);
		/* 8 + 44 + 8 = 60px, down from 80. The CTA buttons still set the
		   row's height and still clear the 44px touch minimum; the bar
		   loses padding, not tap area. */
		padding-block: var(--hb-space-8);
	}

	/* The right-hand group starts here. The nav used to be the element
	   that carried the auto margin, with the CTA slot travelling along
	   behind it — so when the nav was withdrawn (city-first IA,
	   2026-08-31) the buttons lost their push and collapsed against the
	   city chip on the left. The margin moves to the CTA slot, which is
	   now the whole right-hand group.

	   The .hb-header-nav rule is kept beneath it: the element is no
	   longer rendered, so it matches nothing, but a revert of header.php
	   restores the original desktop layout without a CSS change. */
	.hb-header-cta {
		margin-inline-start: auto;
	}
	.hb-header-nav {
		margin-inline-start: auto;
	}

	/* Brand and city read as one lockup, separated by a hairline rather
	   than by a gap wide enough to look accidental. The rule only paints
	   when the city control is actually rendered: the button ships hidden
	   and is revealed by js/city-selector.js, and the shortcode returns
	   nothing at all when no city is selectable. */
	.hb-header-logo + .hb-city-indicator {
		margin-inline-start: var(--hb-space-8);
		padding-inline-start: var(--hb-space-24);
		border-inline-start: 1px solid var(--hb-color-divider);
	}
	.hb-city-indicator {
		font-size: 0.9375rem;
	}

	/* A 24px gap between the last nav item and the first button is what
	   separates "navigate" from "act"; 8px between the two buttons keeps
	   them a pair. */
	.hb-header-cta {
		gap: var(--hb-space-8);
	}

	/* Header CTAs are one notch smaller than the page's primary buttons.
	   At the full 48px/1.0625rem they were the heaviest thing in a bar
	   whose job is to stay out of the way, and they set the height of the
	   whole row. The .hb-btn defaults are unchanged everywhere else on the
	   site — this is scoped to the header slot. */
	.hb-header-cta .hb-btn {
		min-height: 44px;
		padding-inline: var(--hb-space-16);
		font-size: 0.9375rem;
	}

	.hb-header-logo {
		font-size: 1.375rem;
	}
}

/* ==========================================================================
   MOBILE HEADER — CITY NAME LEGIBILITY (2026-08-30)

   "Greater Noida" was ellipsing to "Greater…" at every width up to 390px.
   Measured, the row is simply over budget: at 375px the three controls
   want 419px of a 375px viewport, so the city chip — the only flexible
   item in the row — absorbed the whole 43px deficit and truncated. A
   visitor cannot confirm the city they are being served in, which is the
   one job that chip has.

   What gives way, in order of how little it costs:

   1. The two decorative leading glyphs — the brass pin on the city chip
      and the grid mark on the Services control. Both are aria-hidden
      SVGs, so nothing is lost semantically, and the labels beside them
      already say "Greater Noida" and "Services" better than a 16px glyph
      can. This is a deliberate reversal of the pin/grid pairing set up in
      the 2026-08-26 pass: that pairing was worth its width when the
      alternative was an unlabelled hamburger, and is not worth it when
      the price is a truncated city name. Both glyphs return above 430px,
      where the row has room for them, so the pairing survives everywhere
      it fits.
   2. The row gap, 8px -> 6px.
   3. Both control labels drop one step, together, so the chip and the
      Services control stay the matched pair they were designed as —
      never one shrunk and the other not.

   The wordmark is untouched at every width. The logo was never the thing
   to trade away, and the approved lockup renders at exactly the size it
   did before.

   Scoped to <=430px. The tablet and desktop headers are not reached by
   anything in this block.
   ========================================================================== */

@media (max-width: 430.98px) {
	/* Decorative only — see note above. The chevrons stay: they are what
	   says these two are controls rather than labels. */
	.hb-city-indicator .hb-icon--location,
	.hb-services-toggle .hb-icon--grid {
		display: none;
	}
}

@media (min-width: 360px) and (max-width: 430.98px) {
	.hb-header-inner {
		column-gap: 6px;
	}
	/* Stepped together, never separately — the two controls read as one
	   set and a size difference between them would look like a bug. The
	   320-359px band already sets this same size on both. */
	.hb-city-indicator,
	.hb-services-toggle {
		font-size: 0.8125rem;
	}
}

@media (max-width: 359.98px) {
	/* 320px is the tightest supported width and has no slack left once
	   the glyphs are gone, so the last two small reserves are spent here:
	   the row gap and the chip's chevron, which is a hair larger than the
	   Services chevron it is paired with anyway. */
	.hb-header-inner {
		column-gap: 3px;
	}
	.hb-city-indicator .hb-city-indicator-chevron {
		width: 11px;
		height: 11px;
	}
}

/* ==========================================================================
   MOBILE HEADER — ICON-ONLY CTA (city-first IA, 2026-08-31)

   The header is now four things at every width:

     HouseBro.in | City Selector | Call | WhatsApp

   Below 1024px the two buttons drop their labels and become square icon
   targets. This is what the Services control paid for: removing it
   returned roughly 90-110px to the row, and two 44px squares plus their
   gap spend about the same — so the city chip keeps the width it had,
   and the row gains two actions it did not have before.

   ACCESSIBILITY. The label is hidden, never the accessible name. Each
   button carries an explicit aria-label + title set in header.php, so a
   screen reader still announces "Call HouseBro now", not an unlabelled
   link wrapping an aria-hidden SVG. That is why the label is a real
   span (.hb-btn-label) that CSS can hide, rather than the text node the
   shortcodes emitted before.

   The sticky bottom bar is unchanged and still carries the full-label
   buttons. These two serve the first screen; that one serves the rest of
   a long city page.
   ========================================================================== */
@media (max-width: 1023.98px) {
	.hb-header-cta {
		display: inline-flex;
		align-items: center;
		gap: var(--hb-space-8);
		flex-shrink: 0;
		margin-inline-start: auto;
	}

	/* Square, not pill-wide: no label means no reason for the 24px
	   inline padding, and the 48px .hb-btn floor would make two buttons
	   96px of a 320px row. 44px is the accessibility minimum and is met
	   on both axes. */
	.hb-header-cta .hb-btn {
		min-width: 44px;
		min-height: 44px;
		padding-inline: 0;
		gap: 0;
	}

	.hb-header-cta .hb-btn-label {
		/* Visually hidden, not display:none — the buttons name themselves
		   via aria-label, but keeping the text out of the layout while
		   still in the DOM means a copy/translate pass or a UA that
		   ignores aria still has the words. */
		position: absolute;
		width: 1px;
		height: 1px;
		margin: -1px;
		padding: 0;
		overflow: hidden;
		clip: rect(0 0 0 0);
		clip-path: inset(50%);
		white-space: nowrap;
		border: 0;
	}

	/* The glyph is the whole button now, so it steps up from the 18px it
	   uses beside a label. */
	.hb-header-cta .hb-btn .hb-icon {
		width: 20px;
		height: 20px;
	}
}

/* 320-430px: the row is at its tightest. The buttons hold their 44px
   touch target — that is not negotiable — so the reserves are spent on
   the gap between them and the CTA group's own leading gap. */
@media (max-width: 430.98px) {
	.hb-header-cta {
		gap: var(--hb-space-4);
	}
}

@media (max-width: 359.98px) {
	/* At 320px the four controls want more than the viewport, and the
	   city name is the one that must stay readable — it is the single
	   thing telling the visitor which market they are being served in.
	   So the CTA group gives up its icon size and its internal gap
	   before the chip gives up a character. Targets stay 44px. */
	.hb-header-cta {
		gap: 2px;
	}
	.hb-header-cta .hb-btn .hb-icon {
		width: 18px;
		height: 18px;
	}
}

/* ==========================================================================
   MOBILE HEADER — WIDTH RECOVERY FOR THE CITY NAME (2026-08-31)

   Measured, not guessed. With the two icon CTAs added, the header row at
   375px was: 16+16 container padding, 134 wordmark, 92 CTA group, 12 in
   gaps — leaving 65px for a city name that needs 86px to render whole.
   "Greater Noida", the longest real city in the taxonomy, ellipsed at
   every width up to 390px. A visitor cannot confirm which market they are
   being served in, which is the one job that control has, and under a
   city-first IA it is the most important word in the header.

   WHAT GIVES WAY, and what deliberately does not:

   The CITY PILL is not touched. Its font size, padding, chevron, glyph
   rules and behaviour are exactly as approved — requirement 9 protects
   that control, and the fix must not come out of it.

   The 44x44 CTA TARGETS are not touched either. They are the locked
   minimum and are met on both axes at every width.

   So the two remaining reserves are spent instead:

   1. The header's own horizontal padding, 16px -> 10px (and 6px at
      320-359). This is scoped to .hb-header-inner alone — .hb-container
      keeps its 16px everywhere else on the site, so no page content
      moves. A header is a fixed 56px bar of controls, not a text
      measure; it can sit closer to the edge than a paragraph can.
   2. One step off the wordmark below 391px. The 2026-08-30 pass held the
      logo untouched at every width, and that was the right call when the
      alternative was trading it against an unlabelled hamburger. It is
      the wrong call against a truncated city name: the brand is also the
      page title, the tab title and the whole top-left corner, so one
      type step costs recognition almost nothing, while the city name has
      no second place to be read.
   ========================================================================== */
@media (max-width: 430.98px) {
	.hb-header .hb-header-inner {
		padding-inline: 10px;
	}
}

/* Each step below is the amount measured as still missing at that band,
   not a round number: 391-430 needed nothing further, 360-390 was 11px
   short, and 320-359 was 10px short. */
@media (max-width: 390.98px) {
	.hb-header .hb-header-inner {
		padding-inline: 6px;
	}
	.hb-header .hb-header-logo {
		font-size: 1.0625rem;
	}
}

@media (max-width: 359.98px) {
	.hb-header .hb-header-inner {
		padding-inline: 4px;
	}
	.hb-header .hb-header-logo {
		font-size: 0.9375rem;
	}
}

/* ==========================================================================
   DESKTOP HEADER — ONE CITY CONTROL (2026-08-31, revised)

   The desktop header must name the current city exactly once. There were
   two controls doing it: the city pill beside the logo, and the Cities
   dropdown in the nav.

   The PILL is the one that stays, at every width.

   It is the only control that opens the city selector overlay — the
   card-based "Where can we serve you?" sheet with the per-city
   architectural glyphs. Hiding the pill on desktop (the first attempt at
   this de-duplication) did remove the duplicate name, but it also made
   that overlay unreachable on desktop after the first visit, because the
   pill is its only trigger. A visitor who picked the wrong city had no
   obvious way back to the selector. That is a worse header than a
   repeated word.

   So the duplicate is resolved from the other end: the Cities dropdown
   leaves the primary navigation (housebro_nav_menu_fallback() and the
   assigned-menu filter, inc/nav-fallback.php), and the pill — which shows
   the current city AND opens the full selector — is the single city
   control at every width. Nothing about the pill's own styling changed at
   any point; it renders exactly as it always has.
   ========================================================================== */

/* The city currently being answered for, inside the Cities menu. Brass and
   a weight step, no background: this is a state marker on a menu row, not a
   button, and the row must still read as one of a list. */
.hb-nav-menu .sub-menu .hb-nav-current-city > a,
.hb-nav-menu .sub-menu li.hb-nav-current-city > a {
	color: var(--hb-color-brass);
	font-weight: 600;
}

/* City-aware filtering hides menu rows via the hidden attribute. Nothing in
   this file forces display on a nav <li>, so the UA rule already applies —
   this states it explicitly so a future layout rule on these rows cannot
   silently resurrect a service the visitor's city does not offer. */
.hb-nav-menu li[hidden],
.hb-services-menu li[hidden],
.hb-catalogue-group[hidden],
/* The footer's Services rows joined the filter on 2026-08-31 — they are
   flex/list children with their own layout rules, and a display value set
   by one of those rules would otherwise beat the UA's [hidden] and keep
   showing a service the visitor's city does not offer. */
.hb-footer-column li[hidden] {
	display: none !important;
}


/* ==========================================================================
   FOOTER — AREAS WE SERVE, GROUPED BY MARKET (2026-08-31)

   HouseBro does not offer the same services in every city, so the footer
   names each market rather than running every city into one list. The
   sub-heading is deliberately quieter than the column's own <h3>: it
   groups, it does not introduce a new column.
   ========================================================================== */
.hb-footer-area-group {
	margin-block: var(--hb-space-16) var(--hb-space-8);
	font-size: 0.8125rem;
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--hb-color-divider);
	opacity: 0.75;
}
/* The first group sits directly under the column heading, which already
   carries its own spacing and brass rule. */
.hb-footer-areas .hb-footer-area-group:first-of-type {
	margin-block-start: 0;
}


/* ==========================================================================
   SERVICE PAGE DENSITY + HIERARCHY PASS (2026-09-01)

   Measured against the Gurugram Marble Polishing page, which rendered
   5,507px tall at 1536px wide with only six real sections in it. The page
   was not missing content — it was spending its height on padding.

   The cause is stacked vertical padding, each rule reasonable alone:

     .hb-section        96 + 96
     .hb-hero           96 + 96   (so 192px above the H1)
     .hb-benefits-grid  96 + 96   (694px tall to hold three cards)
     .hb-faq-accordion  96 + 96
     .hb-final-cta-band 64 + 64
     every h2           64 above, 32 below

   This block reduces that spend and fixes two composition faults found in
   the same pass. It is deliberately additive and lives at the end of the
   file so the whole density change can be read, reviewed and reverted as
   one unit rather than hunted across six separate rules.

   NOTHING about colour, border, radius, shadow, font or content changes —
   this is spacing and grid composition only.
   ========================================================================== */

/* --- 1. Section rhythm ---------------------------------------------------
   96px between sections is a landing-page cadence; a service page is a
   reference document a visitor scans. 64px still separates sections
   clearly at desktop and removes ~64px per boundary. */
@media (min-width: 1024px) {
	.hb-section {
		padding-block: var(--hb-space-64);
	}
}

/* Headings keep their brass rule and their hierarchy, and give back a
   third of their vertical cost. 48/24 still reads as a section break
   because the rule beneath the heading does that work. */
.hb-main h2 {
	margin-block: var(--hb-space-48) var(--hb-space-24);
}

/* --- 2. Hero: balanced columns ------------------------------------------
   THE COMPOSITION FAULT. At desktop the hero is a two-column grid with
   `align-items: center`, and .hb-hero-bg carries `aspect-ratio: 16/9`.
   In a 520px column that fixes the photo at 293px tall while the text
   column runs 492px — so the image floated in the middle of its column
   with ~100px of dead space above and below it, and the hero read as
   half-empty.

   Stretching the row and letting the photo fill its own column fixes it:
   object-fit: cover was already set, so the crop stays centred and no
   part of the machine or the floor is lost. The min-height stops a very
   short text column (a service with no description yet) from squashing
   the photo into a letterbox. */
@media (min-width: 1024px) {
	.hb-hero {
		padding-block: var(--hb-space-48);
	}
	.hb-hero:has(.hb-hero-bg) {
		align-items: stretch;
	}
	.hb-hero:has(.hb-hero-bg) .hb-hero-bg {
		height: 100%;
		min-height: 380px;
		aspect-ratio: auto;
		align-self: stretch;
	}
}

/* --- 3. Why Choose: a real benefit grid ----------------------------------
   THE SECOND COMPOSITION FAULT. .hb-benefits-grid is two columns at every
   width above 640px, so this service's THREE benefits rendered as a row
   of two and then one lone card beside a 580px hole — the emptiest block
   on the page, in the section whose whole job is to build confidence.

   Three columns from 900px makes three benefits a single balanced row.
   Grid's default `stretch` already equalises card heights, and
   minmax(0,1fr) keeps a long benefit title from widening its own track.
   A future fourth benefit wraps to a second row rather than breaking the
   layout, and two benefits still centre acceptably.

   The 96px block padding goes entirely: this grid sits directly under an
   h2 that already carries 48px of its own margin. */
.hb-benefits-grid {
	padding-block: 0;
}
@media (min-width: 900px) {
	.hb-benefits-grid {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}
}
@media (min-width: 1024px) {
	.hb-benefits-grid {
		padding-block: 0;
	}
	/* 32px of padding in a card that holds a short title and no body copy
	   left each one mostly empty. 24px keeps the premium feel and drops
	   ~60px from the section. */
	.hb-benefit-item {
		padding: var(--hb-space-24);
	}
}

/* --- 4. Process: stronger numbered steps ---------------------------------
   The brass numbered circles already exist; they were simply small and
   swimming in 48px of block padding. Tightening the padding and firming
   up the connector makes the sequence read as a sequence. */
.hb-process-brief {
	padding-block: var(--hb-space-24);
}
@media (min-width: 1024px) {
	.hb-process-brief {
		padding-block: var(--hb-space-24);
	}
}

/* --- 5. FAQ ---------------------------------------------------------------
   Same reasoning as the benefits grid: it follows its own h2, so 96px of
   additional padding above it is a second section break. */
.hb-faq-accordion {
	padding-block: 0;
}

/* --- 6. CTA: prominent, not oversized ------------------------------------
   64px of block padding made the CTA band the tallest single element on
   the page after the hero. 40px keeps it unmissable — it still has its
   own background, its own width and two full-size buttons — while
   letting the content around it breathe. */
@media (min-width: 1024px) {
	.hb-final-cta-band {
		padding-block: var(--hb-space-32);
	}
}

/* --- 7. Offerings + Care: close the seam ---------------------------------
   Both sections render back to back with no separation of their own, and
   the care band's own heading provides the break. A small margin stops
   the offerings grid from touching the care heading's brass rule. */
.hb-offerings {
	margin-block-end: var(--hb-space-32);
}
/* The care illustration must have layout size BEFORE it decodes.
   .hb-care-media img is loading="lazy" and its SVG reports no intrinsic
   size to the lazy loader, so `height: auto` resolved to 0. A zero-height
   element never intersects the viewport, so the loader never fetched it,
   so it never gained an intrinsic size - a deadlock that rendered the
   whole illustration as a 2px sliver. Declaring the ratio gives the box a
   real height from first layout, which both breaks the deadlock and
   removes the layout shift when the file arrives. Both care illustrations
   are authored at 640x480; any future one should match. */
.hb-care-media img {
	aspect-ratio: 640 / 480;
	height: auto;
}

/* Column balance. With a short checklist - Marble Polishing lists three
   items - a half-width illustration stood 398px tall against roughly
   250px of heading and list, and the row read as a picture with some text
   next to it. Narrowing the media column brings the two halves within
   sight of each other without capping the image (which is what caused
   the lazy-load deadlock above). */
@media (min-width: 900px) {
	.hb-care {
		grid-template-columns: minmax(0, 1fr) minmax(0, 0.72fr);
	}
}

/* NO max-height / width:auto CAP ON .hb-care-media img.
   One was added in this pass and withdrawn the same day. The image is
   loading="lazy", so constraining its width to `auto` made it size from
   its intrinsic width — which is 0 until the browser decodes it, which it
   never did, because the element was now 2px tall and never entered the
   viewport. A deadlock: 2px because undecoded, undecoded because 2px.
   The column was already balanced (384px of media against a 398px row),
   so the cap solved nothing and is simply gone. */
