/**
 * Interaction foundation.
 *
 * theme.json emits the design tokens; the block stylesheets draw the
 * components. This file is the layer in between: the rules that must be the
 * same everywhere or they are wrong everywhere — focus, motion, tap targets,
 * and the pressed/disabled states of controls.
 *
 * Everything here is written with :where(), so it carries zero specificity. A
 * block that genuinely needs a different treatment overrides it with an
 * ordinary class selector and no !important. That is deliberate: this file sets
 * the default, it does not win arguments.
 *
 * The exceptions are the shared components further down (.pchs-on-dark, the
 * button, the card roles, the disclosure): plain classes, because a component
 * a block opts into by name has to beat core's element styles. Each says so.
 */

:root {

	/* ---- motion ------------------------------------------------------- */

	/*
	 * Three durations, not a scale. `fast` is for a colour or border swap the
	 * eye should not notice happening; `base` is for a control changing shape
	 * or elevation; `slow` is for something entering or leaving the page.
	 * Anything longer than `slow` on a brochure site is decoration.
	 */
	--pchs-duration-fast: 120ms;
	--pchs-duration: 180ms;
	--pchs-duration-slow: 260ms;

	/* Decelerating: fast off the mark, settling at the end. A symmetric
	   ease reads as floating, which is the wrong feel for this site. */
	--pchs-ease: cubic-bezier(0.2, 0, 0.2, 1);

	/* ---- focus -------------------------------------------------------- */

	/*
	 * One ring, two tones, because no single colour in the palette clears
	 * 3:1 on every ground the site actually puts a control on. Measured
	 * against the nine grounds in theme.json:
	 *
	 *                    base surface green greenDark greenTint  navy navyDeep dangerTint   ink
	 *   navy-deep       16.54  15.34   3.12      2.07     12.73  1.41     1.00      14.46  1.11
	 *   base (white)     1.00   1.08   5.30      7.98      1.30 11.69    16.54       1.14 18.42
	 *
	 * Whichever ground a control lands on, one of the two rings clears 3:1
	 * against it — worst case 5.30 (navy-deep on green). The two rings
	 * measure 16.54:1 against each other, so the indicator is legible over a
	 * photograph too, where the ground is unknown by definition.
	 *
	 * The halo is painted with box-shadow so it fills the outline-offset gap
	 * exactly. A control that already carries a box-shadow has to restate it
	 * alongside --pchs-focus-halo rather than inherit this rule; there are
	 * four of those on the site and each says so where it is written.
	 */
	--pchs-focus-color: var(--wp--preset--color--navy-deep);
	--pchs-focus-halo-color: var(--wp--preset--color--base);
	--pchs-focus-width: 2px;
	--pchs-focus-gap: 2px;
	--pchs-focus-halo: 0 0 0 var(--pchs-focus-gap) var(--pchs-focus-halo-color);

	/* ---- tap targets -------------------------------------------------- */

	/* WCAG 2.5.5 Target Size (Enhanced). 2.5.8 only asks for 24px, but this
	   site is sold on a phone held in one hand on a driveway. */
	--pchs-tap: 2.75rem;

	/* ---- control and chrome sizes ------------------------------------- */

	/* Three button heights, 44 / 48 / 56. Inputs are 3rem, like the
	   default button, so a field and its submit share a baseline. */
	--pchs-btn-h-sm: 2.75rem;
	--pchs-btn-h: 3rem;
	--pchs-btn-h-lg: 3.5rem;

	--pchs-callbar-h: 4rem;

	/* The client asked for the large logo (144px); the scroll shrink stays. */
	--pchs-logo-h: 9rem;
	--pchs-logo-h-scrolled: 6rem;
	--pchs-header-pad-y: var(--wp--preset--spacing--40);

	/* The header's height once scrolled: what an in-page anchor has to
	   clear. 129px at desktop, 73px below 80em. */
	--pchs-header-h-scrolled: calc(var(--pchs-logo-h-scrolled) + 2 * var(--pchs-header-pad-y) + 1px);

	--pchs-card-pad: var(--wp--preset--spacing--50);
}

/* Below the nav breakpoint (80em) the header is the compact drawer bar. */
@media ( max-width: 79.99em ) {

	:root {
		--pchs-logo-h: 5.5rem;
		--pchs-logo-h-scrolled: 3.5rem;
		--pchs-header-pad-y: var(--wp--preset--spacing--20);
	}
}

@media ( min-width: 48em ) {

	:root {
		--pchs-card-pad: var(--wp--preset--spacing--60);
	}
}

/* ---- dark grounds ----------------------------------------------------- */

/*
 * Any section root on navy, navy-deep or green-dark carries this class. It
 * does not paint anything itself: it flips the variables the type roles
 * (style.css), links and the focus ring read, so a heading, a lead or a ring
 * inside it is legible without each block restating the dark-ground colours.
 * The focus pair swaps too: a white ring with a navy-deep halo.
 */
.pchs-on-dark {
	--pchs-heading-color: var(--wp--preset--color--base);
	--pchs-lead-color: var(--wp--preset--color--border-cool);
	--pchs-link-color: var(--wp--preset--color--green-light);
	--pchs-eyebrow-color: var(--wp--preset--color--green-light);
	--pchs-focus-color: var(--wp--preset--color--base);
	--pchs-focus-halo-color: var(--wp--preset--color--navy-deep);

	/* Restated: a custom property that reads another resolves where it is
	   declared, so the :root halo stayed white here without this line. */
	--pchs-focus-halo: 0 0 0 var(--pchs-focus-gap) var(--pchs-focus-halo-color);
}

/* ---- page flow -------------------------------------------------------- */

/*
 * Sections meet edge to edge.
 *
 * Core's flow layout gives every child of .wp-site-blocks and of a flow
 * post-content a 24px margin-block-start (blockGap). Between two full-bleed
 * sections that paints a 24px white stripe: under the header, between every
 * pair of bands, and above the band that follows main. Sections carry their
 * own padding, so the gap is dropped for them only; core prose inside
 * post-content keeps blockGap.
 *
 * Blocks are matched on the block wrapper class, not on "pchs-": core
 * paragraphs carry pchs- classes too (.pchs-attribution, .pchs-cross-service)
 * and must keep their gap.
 *
 * Templates place sections straight into main as well (the FAQ, financing,
 * seals and chips after post-content on a service page), and main is a flow
 * group too. Those are matched on the block wrapper class, not on "pchs-",
 * because the page title (.pchs-page-title) is a main child that should keep
 * its gap under the breadcrumbs. Likewise a post-content that opens with a
 * section right after a section (a city-service page: breadcrumbs, then the
 * hero inside the content). After the page title it keeps the gap, or the
 * title's descenders sit on the next band.
 */
:where(.wp-site-blocks) > main,
:where(.wp-site-blocks) > main + *,
:where(.wp-site-blocks) > [class*="wp-block-pchs-"] {
	margin-block-start: 0;
}

.wp-block-post-content > .alignfull,
.wp-block-post-content > [class*="wp-block-pchs-"],
main > [class*="wp-block-pchs-"],
main > [class*="wp-block-pchs-"] + .wp-block-post-content:has(> [class*="wp-block-pchs-"]:first-child) {
	margin-block-start: 0;
}

/* ---- focus ------------------------------------------------------------ */

:where(
	a[href],
	area[href],
	button,
	input,
	select,
	textarea,
	summary,
	[contenteditable],
	[tabindex]:not( [tabindex="-1"] )
):focus-visible {
	outline: var(--pchs-focus-width) solid var(--pchs-focus-color);
	outline-offset: var(--pchs-focus-gap);
	box-shadow: var(--pchs-focus-halo);
}

/*
 * Some engines still paint their own ring under ours. Suppressing :focus
 * wholesale would take the ring away from keyboard users on browsers without
 * :focus-visible, so this is scoped to the case where ours is already showing.
 */
:where( a[href], button, input, select, textarea, summary ):focus:not( :focus-visible ) {
	outline: none;
}

/* ---- tap targets ------------------------------------------------------ */

:where(
	button,
	input[type="button"],
	input[type="submit"],
	input[type="reset"],
	select
) {
	min-height: var(--pchs-tap);
}

/*
 * Links that own their line.
 *
 * A blanket rule on `a` would be wrong: most links here sit inside a sentence,
 * where WCAG 2.5.8's inline exception applies and a 44px box tears the
 * paragraph apart. But a link that is the only child of a list item or a
 * heading is not in a sentence. It is a target with a line to itself, and it
 * should be the size of a target.
 *
 * Measured on the home page at 390px: of 75 focusable elements under 44x44,
 * 39 are `li > a:only-child` (city chips, footer link lists, nav lists) and 6
 * are `heading > a:only-child` (service and product card titles, 20px tall on
 * the primary navigation path). These two selectors are 45 of them. Most of the
 * remainder are false positives — the skip link and the visually-hidden radios
 * behind the form chips are 1x1 by design, and their real targets are elsewhere.
 *
 * A foundation rule rather than 45 block-level ones, because the next card
 * title somebody writes should not have to rediscover it.
 *
 * It does move layout in the blocks it reaches — a 20px title becomes 44px —
 * and that is deliberate. The alternative is the padding-cancelled-by-negative-
 * margin trick used for breadcrumbs and the announcement-bar link, which grows
 * a hit area inside existing space. That does not generalise to a stacked list:
 * two links whose padding is cancelled by negative margins overlap, and
 * whichever paints last silently takes the other's hit area. An honest 44px
 * that moves the layout beats a 44px that lies about which link you pressed.
 *
 * Unclassed links only. `a:only-child` scores (0,1,1), which beat every
 * single-class block rule on a link it reached: the nav's submenu links were
 * forced to inline-flex over their own display: block and could be clicked
 * across their text only (82 of 240px). A link with a class belongs to a
 * block, and the block sizes its own target.
 */
:where( li, h1, h2, h3, h4, h5, h6 ) > a:only-child:not( [class] ) {
	display: inline-flex;
	align-items: center;
	box-sizing: border-box;
	min-height: var(--pchs-tap);
}

/* ---- pressed, hover, disabled ----------------------------------------- */

/*
 * A button that moves 1px when pressed is the whole of the "physical tool"
 * idea, and it is the only motion on this site a visitor triggers on purpose.
 * It has no transition on the way down so it lands on the same frame the
 * finger does; the release is carried by the colour transition below.
 */
@media ( prefers-reduced-motion: no-preference ) {

	:where( button, input[type="submit"], .wp-element-button, .pchs-btn ):not( :disabled ):active {
		transform: translateY( 1px );
	}

	:where( button, input[type="submit"], .wp-element-button ) {
		transition: background-color var(--pchs-duration-fast) var(--pchs-ease),
			color var(--pchs-duration-fast) var(--pchs-ease),
			border-color var(--pchs-duration-fast) var(--pchs-ease),
			box-shadow var(--pchs-duration-fast) var(--pchs-ease);
	}

	:where( a ) {
		transition: color var(--pchs-duration-fast) var(--pchs-ease);
	}
}

/*
 * Disabled is a statement about the control, so it reads the same on every
 * one: faded, and a cursor that says "not now". WCAG 1.4.3 exempts inactive
 * components from the contrast floor, which is what makes 0.55 acceptable —
 * it lands under 4.5:1 against most of the palette, and that is the signal.
 *
 * `cursor: progress` is a different claim ("working"), and forms.css keeps it
 * on the submit button while a lead is in flight. That rule is a class
 * selector, so it wins here without argument.
 */
:where( button, input, select, textarea ):disabled,
:where( [aria-disabled="true"] ) {
	opacity: 0.55;
	cursor: not-allowed;
}

/*
 * A disabled fieldset already fades every control inside it. Fading the
 * fieldset as well compounds the two and takes the legend below legibility, so
 * the container stays at full opacity and the controls carry the state.
 */
:where( fieldset ):disabled {
	cursor: not-allowed;
}

/* ---- button ----------------------------------------------------------- */

/*
 * The one button. Every call to action on the site, whether an <a> or a
 * <button>, is `.pchs-btn` plus a look (--solid, --light, --outline) and at
 * most a size (--sm, --lg) and --block. Blocks keep only layout (width,
 * placement), never colour, padding, radius or type.
 *
 * Deliberately NOT :where(), unlike the rest of this file. The component has
 * to beat core's element styles for links (colour, the hover underline at
 * 0,1,1) without each block restating it, so it carries ordinary class
 * specificity. A block that truly needs a variant writes a modifier here.
 *
 * Hover never keys off :focus: focus is the global ring above, nothing else.
 * The 1px press is the shared rule in the motion section; disabled is the
 * shared rule above, with the cursor restated because .pchs-btn sets one.
 *
 * `.wp-element-button` (core search, core buttons) gets the same base and the
 * solid look, so the few core buttons that remain match the component.
 */
.pchs-btn,
.wp-element-button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--wp--preset--spacing--20);
	box-sizing: border-box;
	min-height: var(--pchs-btn-h);
	padding-block: 0;
	padding-inline: var(--wp--preset--spacing--50);
	border: 1px solid transparent;
	border-radius: var(--wp--custom--radius--xs);
	font-family: inherit;
	font-size: var(--wp--preset--font-size--base);
	font-weight: var(--wp--custom--weight--bold);
	line-height: var(--wp--custom--leading--none);
	text-align: center;
	text-decoration: none;
	text-wrap: balance;
	cursor: pointer;
	transition: background-color var(--pchs-duration-fast) var(--pchs-ease),
		border-color var(--pchs-duration-fast) var(--pchs-ease),
		color var(--pchs-duration-fast) var(--pchs-ease),
		box-shadow var(--pchs-duration-fast) var(--pchs-ease);
}

.pchs-btn:hover,
.wp-element-button:hover {
	text-decoration: none;
}

/*
 * :where() focus rings above have zero specificity, so a later class that
 * sets box-shadow (cards, dialogs) can drop the white halo on .pchs-btn.
 * Restate the halo here so keyboard focus stays two-tone on solid CTAs.
 */
.pchs-btn:focus-visible,
.wp-element-button:focus-visible {
	box-shadow: var(--pchs-focus-halo);
}

.pchs-btn--solid,
.wp-element-button {
	background-color: var(--wp--preset--color--green);
	color: var(--wp--preset--color--base);
}

.pchs-btn--solid:hover,
.wp-element-button:hover {
	background-color: var(--wp--preset--color--green-dark);
	color: var(--wp--preset--color--base);
}

/* For dark grounds. */
.pchs-btn--light {
	background-color: var(--wp--preset--color--base);
	color: var(--wp--preset--color--navy);
}

.pchs-btn--light:hover {
	background-color: var(--wp--preset--color--surface);
	color: var(--wp--preset--color--navy);
}

/* Takes the colour of the text around it, so one modifier serves light and
   dark grounds. */
.pchs-btn--outline {
	background-color: transparent;
	border-color: currentColor;
	color: inherit;
}

.pchs-btn--outline:hover {
	background-color: color-mix(in srgb, currentColor 10%, transparent);
	color: inherit;
}

.pchs-btn--sm {
	min-height: var(--pchs-btn-h-sm);
	padding-inline: var(--wp--preset--spacing--40);
	font-size: var(--wp--preset--font-size--sm);
}

/* Primary form submit and the hero's primary call to action only. */
.pchs-btn--lg {
	min-height: var(--pchs-btn-h-lg);
	font-size: var(--wp--preset--font-size--md);
}

.pchs-btn--block {
	width: 100%;
}

.pchs-btn:disabled,
.pchs-btn[aria-disabled="true"] {
	cursor: not-allowed;
}

/* A lead in flight: "working", not "not now". */
.pchs-btn.is-busy,
.is-busy .pchs-btn {
	cursor: progress;
}

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

/*
 * Three surfaces, by role. A block adds the class and keeps none of its own
 * border, radius, shadow, background or padding.
 *
 * .pchs-card   a link card (service, product, post, office). Flat at rest;
 *              --link lifts it on hover or when anything inside has focus.
 *              A link as its last child sits on the bottom edge, so a row of
 *              cards lines its links up.
 * .pchs-panel  a form (quote, booking, financing, careers, ZIP). Raised at
 *              rest, because it is the thing on the page to act on.
 * .pchs-quote  a testimonial: no border, sitting on a surface band.
 */
.pchs-card {
	display: flex;
	flex-direction: column;
	box-sizing: border-box;
	padding: var(--pchs-card-pad);
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--wp--custom--radius--md);
	background-color: var(--wp--preset--color--base);
	box-shadow: none;
	transition: border-color var(--pchs-duration-fast) var(--pchs-ease),
		box-shadow var(--pchs-duration) var(--pchs-ease),
		transform var(--pchs-duration) var(--pchs-ease);
}

.pchs-card > a:last-child {
	margin-top: auto;
}

.pchs-card--link:hover,
.pchs-card--link:focus-within {
	border-color: var(--wp--custom--color--border-cool-2);
	box-shadow: var(--wp--preset--shadow--raised);
}

@media ( prefers-reduced-motion: no-preference ) {

	.pchs-card--link:hover,
	.pchs-card--link:focus-within {
		transform: translateY(-2px);
	}
}

/* A panel is a light card, and it can sit inside a dark section (the booking
   card in the hero slot). It puts the light-ground values back, or its
   headings, links and focus ring would inherit white on white. */
.pchs-panel {
	--pchs-heading-color: var(--wp--preset--color--navy);
	--pchs-lead-color: var(--wp--preset--color--muted);
	--pchs-link-color: var(--wp--preset--color--green);
	--pchs-eyebrow-color: var(--wp--preset--color--green);
	--pchs-focus-color: var(--wp--preset--color--navy-deep);
	--pchs-focus-halo-color: var(--wp--preset--color--base);
	--pchs-focus-halo: 0 0 0 var(--pchs-focus-gap) var(--pchs-focus-halo-color);

	box-sizing: border-box;
	padding: var(--pchs-card-pad);
	border: 1px solid var(--wp--preset--color--border);
	border-radius: var(--wp--custom--radius--lg);
	background-color: var(--wp--preset--color--base);
	box-shadow: var(--wp--preset--shadow--card);
}

.pchs-quote {
	display: flex;
	flex-direction: column;
	box-sizing: border-box;
	padding: var(--pchs-card-pad);
	border: 0;
	border-radius: var(--wp--custom--radius--md);
	background-color: var(--wp--preset--color--base);
}

/* ---- disclosure ------------------------------------------------------- */

/*
 * One open/close pattern: FAQ, financing apply, date picker, the call-bar
 * Call menu. A <details class="pchs-disclosure"> with a
 * .pchs-disclosure__summary, or a <button aria-expanded>; either way the
 * .pchs-chevron inside turns over when it opens.
 *
 * The chevron is the chrome.php "chevron" path as a mask, painted in
 * currentColor, so it follows the text colour on any ground.
 */
.pchs-disclosure__summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--wp--preset--spacing--30);
	min-height: var(--pchs-tap);
	list-style: none;
	cursor: pointer;
}

.pchs-disclosure__summary::-webkit-details-marker {
	display: none;
}

.pchs-chevron {
	display: inline-block;
	flex: none;
	width: 1rem;
	height: 1rem;
	background-color: currentColor;
	-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6 9l6 6 6-6' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
	mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6 9l6 6 6-6' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
	transition: transform var(--pchs-duration) var(--pchs-ease);
}

[open] > summary .pchs-chevron,
[aria-expanded="true"] .pchs-chevron {
	transform: rotate(180deg);
}

/* ---- skip link and modals ------------------------------------------- */

/*
 * Core's skip link, shown on the first Tab, is a grey system box. It is the
 * first thing a keyboard visitor sees, so it gets the site's look. Core's
 * rule is (0,3,0) and prints earlier, so this matches it exactly.
 */
.skip-link.screen-reader-text:focus {
	top: var(--wp--preset--spacing--30);
	left: var(--wp--preset--spacing--30);
	padding: var(--wp--preset--spacing--30) var(--wp--preset--spacing--40);
	border-radius: var(--wp--custom--radius--xs);
	background-color: var(--wp--preset--color--navy-deep);
	box-shadow: var(--wp--preset--shadow--float), var(--pchs-focus-halo);
	color: var(--wp--preset--color--base);
	font-size: var(--wp--preset--font-size--base);
	font-weight: var(--wp--custom--weight--bold);
	line-height: var(--wp--custom--leading--title);
}

/* The page behind an open modal does not scroll. */
html:has( dialog[open] ) {
	overflow: hidden;
}

/* ---- motion opt-out --------------------------------------------------- */

/*
 * One place. Zeroing the duration tokens covers anything that reads them; the
 * blanket rule below covers everything written before they existed, including
 * core and block CSS this file does not own.
 *
 * 0.01ms rather than 0s so transitionend and animationend still fire — some
 * scripts, ours included, wait on them before cleaning up.
 */
@media ( prefers-reduced-motion: reduce ) {

	:root {
		--pchs-duration-fast: 0.01ms;
		--pchs-duration: 0.01ms;
		--pchs-duration-slow: 0.01ms;
	}

	*,
	*::before,
	*::after {
		animation-delay: 0.01ms !important;
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-delay: 0s !important;
		transition-duration: 0.01ms !important;
		scroll-behavior: auto !important;
	}
}
