/*
Theme Name: Dowman Plumbing Solutions
Theme URI:
Author: Dowman Plumbing Solutions
Author URI:
Description: A native WordPress block theme for Dowman Plumbing Solutions. Every brand decision (colour, type, spacing) lives in theme.json, so the theme can be copied and re-skinned for a client build by editing that one file. Sections are registered patterns, so pages stay editable from the block editor without touching code.
Requires at least: 6.5
Tested up to: 7.0
Requires PHP: 7.4
Version: 1.0.10
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: dowman-solutions
Tags: full-site-editing, block-patterns, blog, business, one-column, accessibility-ready, custom-colors, custom-logo, wide-blocks
*/

/* -------------------------------------------------------------------------
   This file holds only mechanism CSS - behaviour that block supports cannot
   express (sticky header, the hero scrim, the guarantee badge, the FAQ +/-
   marker, Gravity Forms integration). Every colour, size and space below
   resolves to a theme.json preset variable, so re-skinning a fork means
   editing theme.json and nothing else.
   ---------------------------------------------------------------------- */

/* --- Accessibility ------------------------------------------------------ */

.skip-link {
	position: absolute;
	left: -9999px;
	top: 0;
	z-index: 100000;
	padding: 12px 22px;
	background: var(--wp--preset--color--primary);
	color: var(--wp--preset--color--shell);
	font-weight: 700;
	text-decoration: none;
}

.skip-link:focus {
	left: 8px;
	top: 8px;
}

:where(a, button, input, textarea, select, summary, .wp-block-navigation-item__content):focus-visible {
	outline: 2px solid var(--wp--preset--color--primary);
	outline-offset: 3px;
}

/* Dark bands need the sky accent for a visible focus ring. */
.has-contrast-background-color :where(a, button, input, textarea, summary):focus-visible,
.dps-hero :where(a, button, input, textarea, summary):focus-visible {
	outline-color: var(--wp--preset--color--brand);
}

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

html {
	scroll-behavior: smooth;

	/* `clip` rather than `hidden` on purpose: `hidden` makes this a scroll
	   container and silently kills position: sticky on the header. */
	overflow-x: clip;
}

/* Sticky header must not cover the target of an in-page anchor link. The
   header is 86px plus the emergency bar, which does not stick. */
:target {
	scroll-margin-top: 104px;
}

/* --- Section stacking ---------------------------------------------------
   WordPress applies --wp--style--block-gap as margin-block-start between
   siblings at two levels, and both show the page background as a stripe
   between coloured bands, breaking the design's colour blocking:

     1. .wp-site-blocks - between header, main and footer.
     2. post-content    - between the stacked page sections.

   The shell (1) is always flush: vertical rhythm comes from each section's
   own padding, never from a gap between them. Inside the content (2) the gap
   is collapsed only where two full-width sections meet, so ordinary blocks
   the client inserts between sections still get their normal spacing.
   ---------------------------------------------------------------------- */

.wp-site-blocks > * + * {
	margin-block-start: 0;
}

.wp-block-post-content > .alignfull + .alignfull,
.block-editor-block-list__layout > .alignfull + .alignfull {
	margin-block-start: 0;
}

/* --- Emergency bar ------------------------------------------------------ */

.dps-emergency {
	font-weight: 500;
}

/* The live dot in front of the emergency message. Decorative, so it is drawn
   rather than typed: a character here would be read out by a screen reader. */
.dps-emergency__dot::before {
	content: "";
	display: inline-block;
	width: 8px;
	height: 8px;
	margin-right: 9px;
	border-radius: 50%;
	background: var(--wp--preset--color--brand);
	vertical-align: middle;
}

.dps-emergency__area {
	opacity: 0.5;
}

@media (max-width: 781px) {
	.dps-emergency__area {
		display: none;
	}
}

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

.dps-header {
	position: sticky;
	top: 0;
	z-index: 40;
	background: color-mix(in srgb, var(--wp--preset--color--base) 94%, transparent);
	border-bottom: 1px solid var(--wp--preset--color--line);
}

@supports (backdrop-filter: blur(10px)) {
	.dps-header {
		backdrop-filter: blur(10px);
	}

	/* backdrop-filter makes the header a containing block for any
	   position: fixed descendant. Core's mobile menu overlay is
	   `position: fixed; inset: 0`, so with the blur applied it gets confined
	   to the 86px header box instead of covering the viewport: the first link
	   shows and the rest paint underneath the hero, invisible and unclickable.
	   Drop the blur while the menu is open. There is nothing to blur at that
	   point, because the overlay is opaque. */
	.dps-header:has(.wp-block-navigation__responsive-container.is-menu-open) {
		backdrop-filter: none;
	}
}

.dps-logo img {
	height: auto;
	max-height: 52px;
	width: auto;
}

/* Nav links sit in body grey, not link-navy, until hovered. */
.dps-nav .wp-block-navigation-item__content {
	color: var(--wp--preset--color--ink-soft);
	font-weight: 500;
	text-decoration: none;
}

.dps-nav .wp-block-navigation-item__content:hover {
	color: var(--wp--preset--color--primary);
}

/* --- Navigation submenu -------------------------------------------------
   Core's default submenu is a square white box with its own borders. Round
   it to the theme's card radius so the dropdown looks like part of the site
   rather than part of WordPress.
   ---------------------------------------------------------------------- */

.dps-header .wp-block-navigation__submenu-container {
	background-color: var(--wp--preset--color--base);
	border: 1px solid var(--wp--preset--color--line);
	border-radius: var(--wp--custom--radius--field);
	box-shadow: 0 12px 28px rgba(20, 24, 31, 0.08);
	min-width: 250px;
	padding: var(--wp--preset--spacing--20) 0;
}

.dps-header .wp-block-navigation__submenu-container .wp-block-navigation-item__content {
	color: var(--wp--preset--color--ink-soft);
	font-weight: 500;
	padding: 0.5rem var(--wp--preset--spacing--60);
	text-decoration: none;
	width: 100%;
}

.dps-header .wp-block-navigation__submenu-container .wp-block-navigation-item__content:hover {
	background-color: var(--wp--preset--color--surface);
	color: var(--wp--preset--color--primary);
}

.dps-header .wp-block-navigation-submenu__toggle {
	padding-right: 0;
}

/* --- Hero ---------------------------------------------------------------
   A plain group with an ordinary core/image inside it, rather than a
   core/cover. Cover's overlay is a flat colour at a dim ratio, and the
   design calls for a two-stop diagonal scrim over a half-faded photo, which
   no cover attribute can express. Keeping it a normal image block means the
   client still swaps the photo with the usual image control, and the scrim
   is drawn here where it belongs.
   ---------------------------------------------------------------------- */

.dps-hero {
	position: relative;
	overflow: hidden;
}

.dps-hero__media {
	position: absolute;
	inset: 0;
	margin: 0;

	/* The hero is a constrained-layout group, so core caps every child at the
	   1280px content width and centres it with auto margins. `inset: 0` cannot
	   beat a max-width, so on any viewport wider than 1280px the photo became a
	   centred 1280px band with the dark background showing down both sides.
	   That reads as object-fit: contain even though the image is set to cover,
	   which sends you looking at the wrong property. This photo is a
	   background, not content, so it has to ignore the measure entirely. */
	max-width: none;
}

/* Qualified with .wp-block-image: core's own `.wp-block-image img` rule ties a
   bare `.dps-hero__media img` on specificity and wins on source order, so the
   height silently falls back to the image's natural ratio. */
.wp-block-image.dps-hero__media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	opacity: var(--wp--custom--hero--image-opacity);
}

.dps-hero::after {
	content: "";
	position: absolute;
	inset: 0;
	background: var(--wp--custom--hero--scrim);
	pointer-events: none;
}

/* The copy has to clear both the photo and the scrim painted over it. */
.dps-hero__body {
	position: relative;
	z-index: 1;
}

/* The eyebrow chips: outlined pills over the photo. */
.dps-chip {
	border: 1px solid color-mix(in srgb, var(--wp--preset--color--brand) 50%, transparent);
	border-radius: var(--wp--custom--radius--pill);
	padding: 8px 18px;
}

/* The hero's trust row sits under a hairline that has to read on the photo. */
.dps-hero-trust {
	border-top: 1px solid rgba(255, 255, 255, 0.16);
}

/* --- Guarantee badge ----------------------------------------------------
   A circle is not something block supports can hold: it needs a fixed
   aspect, a thick ring and centred stacking. Used twice, in the hero and in
   the guarantee band, so it is a class rather than two one-off groups.
   ---------------------------------------------------------------------- */

.dps-badge {
	width: var(--wp--custom--badge--size);
	height: var(--wp--custom--badge--size);
	max-width: 100%;
	border-radius: 50%;
	border: var(--wp--custom--badge--ring) solid var(--wp--preset--color--brand);
	background: var(--wp--preset--color--base);
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	text-align: center;

	/* Horizontal padding is deliberately much tighter than vertical. At 30px
	   extra bold, "Workmanship" is 209px wide, and 24px of side padding inside
	   a 250px circle with an 8px ring leaves a 186px content box. A flex item
	   wider than its line box cannot be centred: the browser clamps the
	   overflow to the start edge to avoid pushing content out of reach, so the
	   word sat 23px right of centre while the two shorter lines around it were
	   centred correctly. 10px leaves a 214px box, so the longest word fits and
	   every line centres on the same axis. */
	padding: 24px 10px;
	flex: none;
}

.dps-badge > * {
	margin: 0;
}

/* Only the hero's badge is a link, and it tips on hover. */
a.dps-badge {
	transition: transform 200ms ease;
	text-decoration: none;
}

a.dps-badge:hover {
	transform: rotate(-3deg);
}

@media (max-width: 781px) {
	.dps-badge {
		--wp--custom--badge--size: 210px;
	}
}

/* --- Cards --------------------------------------------------------------
   One card treatment across the "why choose us", services and reviews
   grids: white, hairline border, rounded. Hover lift is applied only to the
   cards that lead somewhere.
   ---------------------------------------------------------------------- */

.dps-card,
.wp-block-group.is-style-card {
	border: 1px solid var(--wp--preset--color--line);
	border-radius: var(--wp--custom--radius--card);
}

.wp-block-group.is-style-card {
	background-color: var(--wp--preset--color--base);
}

/* Cards that lead somewhere lift on hover and make the whole card the click
   target. A group cannot be an anchor without breaking block validity, so
   the card stays a group and its one link is stretched over it - which also
   keeps a single, correctly labelled link for screen readers rather than
   wrapping the heading and the body text in one enormous anchor. */
.dps-card--link {
	position: relative;
	overflow: hidden;
	transition: transform 200ms ease;
}

.dps-card--link:hover {
	transform: var(--wp--custom--hover--lift-card);
}

.dps-card--link .dps-cardlink a::after {
	content: "";
	position: absolute;
	inset: 0;
}

/* Service card media sits flush to the rounded top corners. */
.dps-card--link .wp-block-image {
	margin: 0;
}

.dps-card--link .wp-block-image img {
	display: block;
	width: 100%;
	height: 190px;
	object-fit: cover;
	border-radius: 0;
}

/* Two of the six service cards have no photo in the design: they carry a
   navy plate with the certification instead. */
.dps-card__plate {
	height: 190px;
	display: flex;
	align-items: center;
	justify-content: center;
	text-align: center;
	padding: 0 var(--wp--preset--spacing--60);
}

/* --- Buttons ------------------------------------------------------------
   Core's is-style-outline sets its own padding at a specificity that beats
   theme.json, so an outline button renders shorter than a filled one beside
   it. Restore the theme.json rhythm, then add the design's lift.
   ---------------------------------------------------------------------- */

.wp-block-buttons .wp-block-button.is-style-outline > .wp-block-button__link {
	padding-top: 1.125rem;
	padding-bottom: 1.125rem;
	padding-left: 2.125rem;
	padding-right: 2.125rem;
}

.wp-block-button__link {
	transition: background-color 200ms ease, border-color 200ms ease, color 200ms ease, transform 200ms ease;
}

/* The sky button and the pale outline buttons lift rather than darken. Text
   colour needs !important throughout: WordPress emits its preset colour
   utilities (.has-*-color) with !important, so a plain declaration loses. */
.dps-btn-sky .wp-block-button__link:hover,
.dps-btn-sky .wp-block-button__link:focus-visible,
.dps-btn-lift .wp-block-button__link:hover,
.dps-btn-lift .wp-block-button__link:focus-visible {
	transform: var(--wp--custom--hover--lift);
}

.dps-btn-sky .wp-block-button__link:hover,
.dps-btn-sky .wp-block-button__link:focus-visible {
	background-color: var(--wp--preset--color--brand) !important;
	border-color: var(--wp--preset--color--brand);
	color: var(--wp--preset--color--contrast) !important;
}

/* Ghost buttons on the hero photo veil rather than fill. */
.dps-btn-ghost .wp-block-button__link:hover,
.dps-btn-ghost .wp-block-button__link:focus-visible {
	background-color: var(--wp--custom--hover--veil) !important;
	color: var(--wp--preset--color--shell) !important;
}

/* The pale outline buttons beside section headings. */
.dps-btn-lift .wp-block-button__link:hover,
.dps-btn-lift .wp-block-button__link:focus-visible {
	background-color: var(--wp--preset--color--base) !important;
	border-color: var(--wp--preset--color--line-strong);
	color: var(--wp--preset--color--primary) !important;
}

/* --- Price guide --------------------------------------------------------
   A rounded panel whose rows are separated by hairlines, with the last row
   shaded. Built from groups rather than a table because each row is a label,
   a description and a price, not tabular data the client would sort.
   ---------------------------------------------------------------------- */

.dps-price {
	border: 1px solid var(--wp--preset--color--line);
	border-radius: var(--wp--custom--radius--card);
	overflow: hidden;
}

.dps-price__row + .dps-price__row {
	border-top: 1px solid var(--wp--preset--color--line);
}

/* Prices must never wrap mid-amount, however narrow the panel gets. */
.dps-price__amount {
	white-space: nowrap;
}

/* --- Reviews ------------------------------------------------------------ */

.dps-score {
	border: 1px solid var(--wp--preset--color--line);
	border-radius: var(--wp--custom--radius--pill);
}

/* Attribution sits on the bottom edge so the three cards line up regardless
   of how long each quote runs. The group keeps its constrained layout for
   the editor's sake, so the margins core emits for block gap still apply -
   only the distribution changes. */
.dps-review {
	display: flex;
	flex-direction: column;
	justify-content: space-between;
}

/* --- FAQ (core/details) ------------------------------------------------- */

.dps-faq .wp-block-details {
	border-top: 1px solid var(--wp--preset--color--line);
	padding: 22px 0;
}

.dps-faq .wp-block-details:last-child {
	border-bottom: 1px solid var(--wp--preset--color--line);
}

.dps-faq .wp-block-details summary {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 24px;
	list-style: none;
	cursor: pointer;
	font-size: var(--wp--preset--font-size--card-title);
	font-weight: 700;
	letter-spacing: var(--wp--custom--letter-spacing--tight);
}

.dps-faq .wp-block-details summary::-webkit-details-marker,
.dps-faq .wp-block-details summary::marker {
	display: none;
	content: "";
}

.dps-faq .wp-block-details summary::after {
	content: "+";
	font-size: 22px;
	font-weight: 500;
	line-height: 1;
	color: var(--wp--preset--color--primary);
	flex: none;
}

.dps-faq .wp-block-details[open] summary::after {
	content: "\2212"; /* minus */
}

.dps-faq .wp-block-details > *:not(summary) {
	margin-top: 12px;
	max-width: 70ch;
	color: var(--wp--preset--color--ink-soft);
	line-height: var(--wp--custom--line-height--relaxed);
}

/* --- Contact cards ------------------------------------------------------ */

.dps-contact-card {
	border: 1px solid var(--wp--preset--color--line);
	border-radius: 14px;
}

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

.dps-footer-logo img {
	height: 64px;
	width: auto;
	max-width: 100%;
}

.dps-footer-links .wp-block-navigation-item__content,
.dps-footer a {
	text-decoration: none;
}

.dps-footer a:hover {
	color: var(--wp--preset--color--base) !important;
}

/* --- Measure ------------------------------------------------------------
   The design constrains individual headings and paragraphs by character
   count rather than by column. Block supports have no max-width control, so
   these are exposed as utility classes - pick one in the block's Advanced >
   Additional CSS class field.
   ---------------------------------------------------------------------- */

:where(h1, h2, h3, h4) {
	text-wrap: balance;
}

:where(p) {
	text-wrap: pretty;
}

/* --- Word breaking ------------------------------------------------------
   Core's block library sets `word-break: break-word` on `.wp-block-column`,
   and it inherits. That is not a last-resort rule: it collapses an element's
   min-content width to a single character, so a flex or grid item shrinks to
   nothing and the text then has to break mid-word to fit the box it was just
   allowed to shrink into. It shattered "Workmanship" across two lines inside
   the guarantee badge and broke the footer email before its final character.

   Restoring `word-break: normal` puts min-content back to the longest word.
   `overflow-wrap: break-word` is left alone: that one only breaks when a word
   genuinely cannot fit, which is the behaviour actually wanted.
   ---------------------------------------------------------------------- */

.dps-badge p,
.dps-footer p,
.dps-price__amount,
.dps-card h3,
.dps-contact-card p {
	word-break: normal;
}

/* The footer email is one unbreakable 246px token in a column that is only a
   few pixels narrower at some widths. `word-break: normal` alone still lets
   `overflow-wrap: break-word` split it before the final character, which looks
   like a typo rather than a wrap. The design lets it sit slightly proud of its
   column instead, and the 44px gutter beside it has room, so do the same. */
.dps-footer a[href^="mailto:"],
.dps-contact-card a[href^="mailto:"] {
	overflow-wrap: normal;
}

.dps-measure-16 { max-width: 16ch; }
.dps-measure-20 { max-width: 20ch; }
.dps-measure-22 { max-width: 22ch; }
.dps-measure-30 { max-width: 30ch; }
.dps-measure-46 { max-width: 46ch; }
.dps-measure-52 { max-width: 52ch; }
.dps-measure-58 { max-width: 58ch; }
.dps-measure-60 { max-width: 60ch; }
.dps-measure-70 { max-width: 70ch; }

/* --- Media -------------------------------------------------------------- */

.dps-media img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	border-radius: var(--wp--custom--radius--card);
}

/* Qualified with .wp-block-image: core's own `.wp-block-image img` rule ties
   a bare `.dps-about-media img` on specificity and wins on source order, so
   the height silently falls back to the image's natural ratio. */
.wp-block-image.dps-about-media img {
	height: clamp(340px, 52vh, 520px);
	object-fit: cover;
	object-position: 50% 42%;
}

/* --- Gravity Forms -----------------------------------------------------
   GF 2.9 renders with the "Orbital" theme, which is driven by a documented
   CSS custom-property palette rather than by flat classes. Remapping those
   variables onto the theme.json presets is the upgrade-safe way to restyle
   it - GF can change its markup without breaking this. The explicit rules
   underneath only cover typography and the few things the palette does not
   reach.

   Unlike the starter, this form sits on a WHITE card on a pale band, not on
   a dark one, so the control palette is light throughout.
   ---------------------------------------------------------------------- */

.dps-form .gform_wrapper.gform-theme,
.dps-form .gform_wrapper.gform-theme--orbital {
	/* Accent - buttons, active states. */
	--gf-color-primary: var(--wp--preset--color--primary);
	--gf-color-primary-rgb: 36, 56, 107;
	--gf-color-primary-contrast: var(--wp--preset--color--shell);
	--gf-color-primary-contrast-rgb: 255, 255, 255;
	--gf-color-primary-darker: var(--wp--preset--color--primary-deep);
	--gf-color-primary-lighter: var(--wp--preset--color--brand);

	/* Inside controls - input wells, which are white here. */
	--gf-color-in-ctrl: var(--wp--preset--color--base);
	--gf-color-in-ctrl-rgb: 255, 255, 255;
	--gf-color-in-ctrl-contrast: var(--wp--preset--color--contrast);
	--gf-color-in-ctrl-contrast-rgb: 20, 24, 31;
	--gf-color-in-ctrl-darker: var(--wp--preset--color--surface);
	--gf-color-in-ctrl-lighter: var(--wp--preset--color--base);
	--gf-color-in-ctrl-light: var(--wp--preset--color--line-strong);
	--gf-color-in-ctrl-light-darker: var(--wp--preset--color--line-strong);
	--gf-color-in-ctrl-light-lighter: var(--wp--preset--color--line);
	--gf-color-in-ctrl-dark: var(--wp--preset--color--ink-muted);
	--gf-color-in-ctrl-dark-darker: var(--wp--preset--color--ink-soft);
	--gf-color-in-ctrl-dark-lighter: var(--wp--preset--color--ink-muted);
	--gf-color-in-ctrl-primary: var(--wp--preset--color--primary);
	--gf-color-in-ctrl-primary-contrast: var(--wp--preset--color--shell);
	--gf-color-in-ctrl-primary-darker: var(--wp--preset--color--primary-deep);

	/* Outside controls - labels, descriptions, rules. */
	--gf-color-out-ctrl: var(--wp--preset--color--contrast);
	--gf-color-out-ctrl-contrast: var(--wp--preset--color--base);
	--gf-color-out-ctrl-dark: var(--wp--preset--color--ink-muted);
	--gf-color-out-ctrl-dark-darker: var(--wp--preset--color--contrast);
	--gf-color-out-ctrl-dark-lighter: var(--wp--preset--color--ink-muted);
	--gf-color-out-ctrl-light: var(--wp--preset--color--line);
	--gf-color-out-ctrl-light-darker: var(--wp--preset--color--line-strong);
	--gf-color-out-ctrl-light-lighter: var(--wp--preset--color--line);

	--gf-color-danger: #B3261E;
	--gf-color-danger-contrast: var(--wp--preset--color--shell);
	--gf-color-danger-darker: #8C1D18;
	--gf-color-danger-lighter: #F2B8B5;

	/* Button controls read from their own --gf-ctrl-btn-* family. */
	--gf-ctrl-btn-bg-color-primary: var(--wp--preset--color--primary);
	--gf-ctrl-btn-bg-color-hover-primary: var(--wp--preset--color--primary-deep);
	--gf-ctrl-btn-bg-color-focus-primary: var(--wp--preset--color--primary-deep);
	--gf-ctrl-btn-color-primary: var(--wp--preset--color--shell);
	--gf-ctrl-btn-border-color-primary: var(--wp--preset--color--primary);
	--gf-ctrl-btn-border-color-hover-primary: var(--wp--preset--color--primary-deep);

	/* Rounded fields and pill buttons, matching every other control. */
	--gf-ctrl-radius: var(--wp--custom--radius--field);
	--gf-ctrl-btn-radius: var(--wp--custom--radius--pill);
	--gf-ctrl-checkbox-radius: 4px;
	--gf-field-gap-y: 1.125rem;
	--gf-field-gap-x: 1.125rem;
	--gf-form-gap-y: 1.125rem;
}

/* Labels are small bold sentence case, matching the design's field labels. */
.dps-form .gform_wrapper .gfield_label,
.dps-form .gform_wrapper .gform-field-label {
	font-family: var(--wp--preset--font-family--heading);
	font-size: var(--wp--preset--font-size--small);
	font-weight: 700;
	letter-spacing: 0;
	text-transform: none;
	color: var(--wp--preset--color--contrast);
	margin-bottom: 0.5rem;
}

.dps-form .gform_wrapper .gfield_required {
	color: var(--wp--preset--color--primary);
}

.dps-form .gform_wrapper.gform-theme--orbital :where(input, textarea, select) {
	background-color: var(--wp--preset--color--base);
	border: 1px solid var(--wp--preset--color--line-strong);
	border-radius: var(--wp--custom--radius--field);
	color: var(--wp--preset--color--contrast);
	font-family: var(--wp--preset--font-family--heading);
	font-size: var(--wp--preset--font-size--medium);
	font-weight: 500;
	min-height: 52px;

	/* Orbital ships a 38px line-height on its controls. Against a 52px box
	   with 15px of vertical padding that leaves a 22px content box holding a
	   38px line, so the text overflowed and was clipped: the selects rendered
	   with their labels sliced in half horizontally. Setting an explicit
	   line-height and trimming the padding to match keeps the whole glyph
	   inside the box. */
	line-height: 1.4;
	padding: 0.75rem 1rem;
}

.dps-form .gform_wrapper.gform-theme--orbital :where(input, textarea)::placeholder {
	color: var(--wp--preset--color--ink-muted);
	opacity: 1;
}

.dps-form .gform_wrapper.gform-theme--orbital :where(input, textarea, select):focus {
	border-color: var(--wp--preset--color--primary);
	outline: 2px solid var(--wp--preset--color--primary);
	outline-offset: 1px;
	box-shadow: none;
}

.dps-form .gform_wrapper.gform-theme--orbital textarea {
	min-height: 7.5rem;
	resize: vertical;
}

/* The design draws the photo upload as a dashed drop zone. Gravity Forms
   renders its multi-file uploader as a solid panel with its own button, so the
   panel is restyled rather than the field swapped for a single-file input:
   people attaching photos of a leak generally have more than one. */
.dps-form .gform_wrapper.gform-theme--orbital .gform_drop_area,
.dps-form .gform_wrapper.gform-theme--orbital .gfield--type-fileupload .gform-theme-field-control {
	background-color: var(--wp--preset--color--base);
	border: 1px dashed var(--wp--preset--color--line-strong);
	border-radius: var(--wp--custom--radius--field);
	color: var(--wp--preset--color--ink-muted);
	font-size: var(--wp--preset--font-size--card-body);
	padding: var(--wp--preset--spacing--60);
	text-align: center;
}

/* Same trap as the submit button: Orbital's theme buttons paint from a
   --gf-local-* trio set ON the element, so a plain background-color here loses
   and the control keeps Gravity Forms' default blue.

   Setting the locals is necessary but not sufficient. GF declares them on
   `.gform-theme.gform-theme--framework.gform_wrapper .button`, which is four
   classes, so the obvious three-class selector loses the variable and the
   button stays blue while every other rule here appears to work. Match GF's
   own selector shape and prefix it, which lands at five. */
.dps-form .gform-theme.gform-theme--framework.gform_wrapper .gform_button_select_files {
	/* !important on the locals, not on background-color. GF paints this button
	   from `background-color: var(--gf-local-bg-color)`, so the variable is the
	   real target, and its own declaration of that variable sits at a
	   specificity this cannot beat without matching GF's full
	   `:where(:not(...))` chain. Overriding the variable keeps GF's hover and
	   focus machinery working, which a flat background-color override would
	   break. */
	--gf-local-bg-color: var(--wp--preset--color--surface) !important;
	--gf-local-color: var(--wp--preset--color--primary) !important;
	--gf-local-border-color: var(--wp--preset--color--line-strong) !important;
	background-color: var(--wp--preset--color--surface);
	border: 1px solid var(--wp--preset--color--line-strong);
	border-radius: var(--wp--custom--radius--pill);
	color: var(--wp--preset--color--primary);
	font-family: var(--wp--preset--font-family--heading);
	font-size: var(--wp--preset--font-size--small);
	font-weight: 700;
	padding: 0.625rem 1.25rem;
}

.dps-form .gform-theme.gform-theme--framework.gform_wrapper .gform_button_select_files:hover,
.dps-form .gform-theme.gform-theme--framework.gform_wrapper .gform_button_select_files:focus {
	--gf-local-bg-color: var(--wp--preset--color--base) !important;
	--gf-local-border-color: var(--wp--preset--color--primary) !important;
	background-color: var(--wp--preset--color--base);
	border-color: var(--wp--preset--color--primary);
}

/* The "(Required)" marker is GF's own, and the design has no marker at all.
   Keep it (it is a usability gain) but stop it competing with the label. */
.dps-form .gform_wrapper .gfield_required_text {
	color: var(--wp--preset--color--ink-muted);
	font-weight: 500;
}

/* The submit button paints from --gf-local-bg-color set ON the button, so
   wrapper-level variables cannot reach it. Its WIDTH is a form setting
   (Submit Button > Appearance > Width), not something CSS can win, so the
   stretch below is belt and braces on top of setting it in the form. */
.dps-form .gform_wrapper.gform-theme--orbital .gform_footer :where(input, button).gform_button,
.dps-form .gform_wrapper.gform-theme--orbital input[type="submit"].gform_button,
.dps-form .gform_wrapper.gform-theme--orbital button.gform_button {
	--gf-local-bg-color: var(--wp--preset--color--primary);
	--gf-local-color: var(--wp--preset--color--shell);
	--gf-local-border-color: var(--wp--preset--color--primary);
	background-color: var(--wp--preset--color--primary);
	border: 1px solid var(--wp--preset--color--primary);
	border-radius: var(--wp--custom--radius--pill);
	color: var(--wp--preset--color--shell);
	cursor: pointer;
	font-family: var(--wp--preset--font-family--heading);
	font-size: var(--wp--preset--font-size--large);
	font-weight: 700;
	min-height: 56px;
	padding: 1.125rem 1.875rem;
	width: 100%;
	transition: background-color 200ms ease, border-color 200ms ease;
}

.dps-form .gform_wrapper.gform-theme--orbital .gform_footer :where(input, button).gform_button:hover,
.dps-form .gform_wrapper.gform-theme--orbital input[type="submit"].gform_button:hover,
.dps-form .gform_wrapper.gform-theme--orbital button.gform_button:hover,
.dps-form .gform_wrapper.gform-theme--orbital input[type="submit"].gform_button:focus,
.dps-form .gform_wrapper.gform-theme--orbital button.gform_button:focus {
	--gf-local-bg-color: var(--wp--preset--color--primary-deep);
	--gf-local-border-color: var(--wp--preset--color--primary-deep);
	background-color: var(--wp--preset--color--primary-deep);
	border-color: var(--wp--preset--color--primary-deep);
}

.dps-form .gform_wrapper.gform-theme--orbital .gform_footer {
	display: block;
	padding: 0;
	margin: 0.375rem 0 0;
}

/* Orbital nests the submit inside a shrink-to-fit wrapper, so the wrapper has
   to be stretched before width:100% on the button means anything. */
.dps-form .gform_wrapper.gform-theme--orbital .gform_footer > * {
	display: block;
	width: 100%;
	max-width: none;
}

.dps-form .gform_wrapper.gform-theme--orbital .gform_footer :where(input, button).gform_button {
	justify-content: center;
	max-width: none;
	text-align: center;
}

.dps-form .gform_wrapper .gform_validation_errors {
	background: var(--wp--preset--color--surface);
	border: 1px solid #B3261E;
	border-radius: var(--wp--custom--radius--field);
	box-shadow: none;
}

.dps-form .gform_wrapper .gform_validation_errors > h2,
.dps-form .gform_wrapper .validation_message {
	color: #B3261E;
	font-size: var(--wp--preset--font-size--small);
}

.dps-form .gform_confirmation_message {
	color: var(--wp--preset--color--contrast);
	font-size: var(--wp--preset--font-size--x-large);
	line-height: var(--wp--custom--line-height--body);
	border-left: 3px solid var(--wp--preset--color--primary);
	padding-left: var(--wp--preset--spacing--60);
}

/* --- Responsive ---------------------------------------------------------
   Core collapses core/columns to one column at 781px on its own. These are
   the places the design needs more than that: the hero badge has to stop
   being a right-hand column and become a centred crest, and the panels that
   put a label opposite a value have to stack rather than squeeze.
   ---------------------------------------------------------------------- */

@media (max-width: 781px) {
	.dps-hero__body .dps-badge-wrap {
		justify-content: center;
	}

	/* Stacked, the badge reads as a stamp under the copy rather than a
	   column beside it, so it needs its own breathing room. */
	.dps-hero__body .dps-badge-wrap {
		margin-top: var(--wp--preset--spacing--80);
	}

	.dps-price__row,
	.dps-price__head {
		row-gap: var(--wp--preset--spacing--30);
	}

	.dps-score {
		align-self: flex-start;
	}
}

/* The guarantee band's three points are core/columns, which core stacks at
   781px. Below that the hairline above each one is the only thing separating
   them, so they need vertical space too. */
@media (max-width: 781px) {
	.dps-guarantee-point + .dps-guarantee-point {
		margin-top: var(--wp--preset--spacing--60);
	}
}

/* --- Print -------------------------------------------------------------- */

@media print {
	.dps-header,
	.dps-emergency,
	.dps-form,
	.wp-block-navigation {
		display: none !important;
	}
}
