/*
 * Site chrome: the rules the header, the footer and the theme's own blocks need
 * and theme.json cannot express.
 *
 * Every value here is a token. There is no hex, no font stack, no raw pixel
 * spacing in this file — CLAUDE.md section 5. The literals that do appear are
 * media query breakpoints, which cannot read a custom property: a media query
 * is evaluated before the cascade, so var() is not available inside one. Where
 * a script also needs to know a breakpoint — product-filters' panel, for one —
 * the number is kept in step with that block's view.js by hand.
 */

/*
 * Visually hidden, still announced.
 *
 * WordPress ships .screen-reader-text for classic themes; a block theme cannot
 * count on it being loaded, and a label that silently stops being hidden is a
 * layout bug nobody notices until a screenshot. Own it here.
 */
.petpartners-visually-hidden {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	border: 0;
	overflow: hidden;
	clip-path: inset( 50% );
	white-space: nowrap;
}

/*
 * Focus, everywhere.
 *
 * theme.json reaches the link and button *elements*, which leaves every raw
 * <button> in the chrome and in the WooCommerce blocks with the browser
 * default. WCAG 2.2 AA is a legal exposure in US e-commerce (CLAUDE.md section
 * 8), so the ring is declared once, globally, rather than per component.
 */
:focus-visible {
	outline: 2px solid var( --wp--preset--color--purple );
	outline-offset: 2px;
}

/*
 * On brown, the ring turns white: purple on brown is 1.6:1 and would disappear
 * (CLAUDE.md section 5.3).
 */
.petpartners-on-brown :focus-visible {
	outline-color: var( --wp--preset--color--white );
}

/*
 * And no link on brown may reach for purple on any state. theme.json sends
 * links to purple-hover on hover, which is 1.3:1 here — worse than the 1.6:1
 * that made the rule. Anything on this surface stays white and relies on its
 * underline.
 */
.petpartners-on-brown a:hover,
.petpartners-on-brown a:focus-visible,
.petpartners-on-brown a:active {
	color: inherit;
}

/*
 * The skip link is WordPress's own — core inserts it into every block template
 * (see inc/landmarks.php). Core styles it too, but on the brand palette rather
 * than the browser default, and it has to clear the sticky header when it
 * appears.
 */
.skip-link.skip-link:focus {
	z-index: 999;
	padding: var( --wp--preset--spacing--sm ) var( --wp--preset--spacing--md );
	border-radius: var( --wp--preset--border-radius--md );
	background-color: var( --wp--preset--color--white );
	box-shadow: var( --wp--preset--shadow--md );
	color: var( --wp--preset--color--brown );
	font-family: var( --wp--preset--font-family--sans );
	font-size: var( --wp--preset--font-size--base );
	font-weight: 600;
	text-decoration: underline;
}

/*
 * The main landmark takes focus from the skip link without being a tab stop of
 * its own, so the outline it would otherwise draw is suppressed: the visitor
 * asked to move here, they did not focus a control.
 */
main[ tabindex="-1" ]:focus {
	outline: none;
}

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

/*
 * Sticky, opaque, always the same height. It does not hide on scroll and it does
 * not go translucent: a header that moves is a header that shifts layout, and
 * CLS under 0.1 is a target, not an aspiration (CLAUDE.md section 8). Anything
 * animated here belongs to P11.
 *
 * position:sticky also makes this the containing block for the mega menu panel
 * and the mobile search field, both of which are absolutely positioned against
 * it. Nothing between them may become positioned or gain overflow:hidden.
 */
/*
 * The header is `surface`, the same grey the home's hero opens on, and it
 * carries no bottom rule.
 *
 * The two go together. Against the hero the bar has no edge at all — the page
 * starts as one field of grey and the logo simply sits in it — which is what
 * the rule was fighting to hide badly.
 *
 * Against a white body it still has an edge, because #F7F6F6 on #FFFFFF is a
 * boundary you can see without it being a line. That is the whole reason this
 * works without the border: the bar is sticky, and a sticky bar passing over
 * white content still has to end somewhere.
 *
 * What is left unsolved, honestly: over a `surface` section further down the
 * page the bar has no edge again. The fix is an edge that appears only once the
 * bar is stuck, which needs a scroll observer or `animation-timeline: scroll()`
 * — behaviour this step has no reason to add, and a property not supported
 * widely enough for a store. Recorded for P11, which owns exactly this.
 */
/*
 * THE HEADER REBUILD (06/out/2026)
 *
 * Three bands were two: a primary bar (logo, search, tools) and the category
 * row beneath it, both opaque white — this reverses the transparent
 * background ADR-065 set, at the client's explicit request for this rebuild.
 * No announcement band: the client had no approved text for one, so
 * parts/header.html never renders a third band rather than rendering an
 * empty one.
 *
 * The header grew taller for it — two stacked bands instead of one. Nothing
 * reads the header's height to position anything else: the hero that used
 * to sit under the old single bar has no pull-up margin and no clip-path
 * maths tied to it (confirmed before this rebuild started), so a taller
 * header simply pushes the hero further down the page, which is the
 * client's own explicit instruction for this change. The hero's own CSS —
 * the wave clip-path, the photo box, the object-position — is untouched.
 */

.petpartners-header {
	position: sticky;
	top: 0;
	z-index: 70;
	background-color: var( --wp--preset--color--white );
}

/*
 * The page starts where the header ends.
 *
 * WordPress's flow layout puts the block gap between the header template part
 * and <main> — 20px of nothing, showing the body's own white. Breathing room
 * under the header belongs to the section's own padding, where it can be the
 * right amount for that section, not to a margin flow happens to leave.
 */
.wp-site-blocks > main {
	margin-block-start: 0;
}

/*
 * The primary bar: hamburger, logo, search, tools — in that DOM order, which
 * is also the reading and the tab order. A grid and not the flex the block
 * declares, same reason as before the rebuild: WordPress emits the block's
 * own flex layout inline, after this stylesheet and at the same specificity,
 * so a flex-basis or flex-grow declared here cannot be trusted to win it.
 * Grid sidesteps the fight entirely — two classes deep is what beats a
 * single-class inline rule regardless of source order.
 *
 * The third column is `1fr` and holds the search field on desktop: nothing
 * here sizes the field directly, petpartners/header-search's own stylesheet
 * does that, this column just gives it the room to do it in.
 */
/*
 * `align: full` + its own `padding-inline`, not `alignwide` (14/out/2026,
 * client request) — `alignwide` caps this row at the theme's `wideSize`
 * (1320px) and centres it, which made its right edge land short of the
 * category strip's own right edge below it (that row is `alignfull` with a
 * `spacing--lg` padding of its own) by however much viewport width exceeds
 * 1320px — 60px of drift at 1440px, the width this was reported on. Same
 * fix `.petpartners-hero-promises` already uses for the identical reason
 * (its own comment, a few hundred lines down): `alignfull` plus a matching
 * `padding-inline: spacing--lg` puts both rows' content on the exact same
 * inset from the true viewport edge at every width, not just the ones
 * narrower than `wideSize` where the drift happened not to show.
 */
.petpartners-header .petpartners-header__bar {
	display: grid;
	grid-template-columns: auto auto 1fr auto;
	align-items: center;
	gap: var( --wp--preset--spacing--md );
	min-block-size: var( --wp--custom--chrome--header-bar-h );
	padding-inline: var( --wp--preset--spacing--lg );
}

.petpartners-header .petpartners-header__tools {
	/*
	 * `justify-self: end` (14/out/2026) — WordPress's own block-editor layout
	 * attribute on this group is still `{"type":"flex"}` (header.html), so it
	 * injects a per-child inline `flex` shorthand sized to the block's OWN
	 * flex assumptions even though `.petpartners-header__bar` above is
	 * `display: grid`, not flex, in this stylesheet. That inline style was
	 * leaving this grid item a few pixels short of the bar's own right
	 * padding edge (measured 400px against a 408px target at 440px
	 * viewport) instead of flush with it — not visible at 1440px (the gap
	 * rounds away against a wider column), which is why the mismatch with
	 * the category strip's scroll-hint chevron (ADR-084) only showed up on
	 * mobile. Grid's own default for a block-level item is already
	 * `stretch`, which should have masked this; forcing `justify-self: end`
	 * here sidesteps relying on that default fighting the injected inline
	 * style, same reasoning `.petpartners-header__bar` itself already uses
	 * grid instead of flex for.
	 */
	justify-self: end;
	display: flex;
	align-items: center;
	gap: var( --wp--preset--spacing--xxs );
}

/* --- Search submit icon -------------------------------------------------
 *
 * Drawn inside petpartners/header-search's own stylesheet as the field's
 * leading icon (row-reverse puts the button first). What is set here is only
 * the colour: brown at rest, so the header does not spend the one purple the
 * page reserves for its actual call to action (CLAUDE.md section 5.3) on a
 * control that only searches; purple on hover and focus, where it is a state
 * and not a claim.
 */
.petpartners-header .wp-block-search__button {
	color: var( --wp--preset--color--brown );
}

.petpartners-header .wp-block-search__button:hover,
.petpartners-header .wp-block-search__button:focus-visible {
	color: var( --wp--preset--color--purple );
}

/* --- Contact link --------------------------------------------------------- */

.petpartners-header .petpartners-contact-link {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: var( --wp--custom--chrome--tap-target );
	min-height: var( --wp--custom--chrome--tap-target );
	border-radius: var( --wp--preset--border-radius--md );
	color: var( --wp--preset--color--brown );
	text-decoration: none;
}

/* --- Account, cart ------------------------------------------------------- */

/*
 * icon_and_text on desktop — the brief's "Login / Register beside the icon" —
 * so this is no longer the fixed icon-only square the header carried before
 * the rebuild: width is auto, height holds the tap target, and the label
 * WooCommerce's own block renders ("Login") gets the same brown as every
 * other tool in the row.
 */
.petpartners-header .wc-block-customer-account__link {
	display: inline-flex;
	align-items: center;
	gap: var( --wp--preset--spacing--xxs );
	min-height: var( --wp--custom--chrome--tap-target );
	padding-inline: var( --wp--preset--spacing--xs );
	border-radius: var( --wp--preset--border-radius--md );
	color: var( --wp--preset--color--brown );
	font-family: var( --wp--preset--font-family--sans );
	font-size: var( --wp--preset--font-size--base );
	font-weight: 500;
	text-decoration: none;
}

.petpartners-header .wc-block-mini-cart__button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: var( --wp--custom--chrome--tap-target );
	min-height: var( --wp--custom--chrome--tap-target );
	padding: 0;
	border-radius: var( --wp--preset--border-radius--md );
	color: var( --wp--preset--color--brown );
	text-decoration: none;
}

.petpartners-header .wc-block-mini-cart__badge {
	background-color: var( --wp--preset--color--brown );
	color: var( --wp--preset--color--white );
	font-family: var( --wp--preset--font-family--sans );
	font-variant-numeric: tabular-nums;
}

/* --- Mobile (below 1024px): two rows, category row scrolls -------------- */

@media ( max-width: 1023px ) {

	/*
	 * Row one keeps the hamburger, the logo, the account icon and the cart —
	 * four auto-sized columns with nothing stretching between them. Row two
	 * is the search field alone, spanning every column, full width. The
	 * search block moves there by grid placement; nothing in the markup or
	 * DOM order changes, so the tab order stays hamburger, logo, search,
	 * tools — unchanged from desktop, just read down two rows instead of
	 * across one.
	 *
	 * Third column stays `1fr`, sized to hold row two's search field.
	 */
	.petpartners-header .petpartners-header__bar {
		grid-template-columns: auto auto 1fr auto;
		grid-template-rows: minmax( var( --wp--custom--chrome--header-bar-h-mobile ), auto ) auto;
		column-gap: var( --wp--preset--spacing--xs );
		row-gap: var( --wp--preset--spacing--xs );
		min-block-size: 0;
	}

	.petpartners-header .wp-block-petpartners-header-search {
		grid-column: 1 / -1;
		grid-row: 2;
	}

	/*
	 * `grid-column: 4` here, explicit, not left to auto-placement
	 * (14/out/2026). Auto-placement only skips a cell a row's OWN explicit
	 * item already occupies — row one never explicitly puts anything in
	 * column three (the search field's `grid-column: 1 / -1` placement is
	 * row two's), so CSS Grid auto-flowed this item (the DOM's fourth
	 * child) into column three instead of four, sized to its own ~130px
	 * content rather than the intended edge column — leaving column four
	 * empty (0px) and the account/cart icons landing short of the bar's
	 * own right padding edge instead of flush with it. That is what broke
	 * the match with the category strip's scroll-hint chevron (ADR-084) on
	 * mobile specifically: at 1440px this column is never reached by the
	 * desktop grid rule above this media query, which has no row-two
	 * spanning item to create the same ambiguity. Explicit placement
	 * removes the ambiguity outright rather than fighting auto-flow.
	 */
	.petpartners-header .petpartners-header__tools {
		grid-column: 4;
		grid-row: 1;
	}

	/*
	 * The contact icon does not fit a 375px row with a hamburger, a logo, an
	 * account icon and a cart icon already in it, and the Contact page it
	 * links to is one tap away in the side panel regardless.
	 */
	.petpartners-header .petpartners-contact-link {
		display: none;
	}

	/*
	 * The account control keeps its icon and drops its "Login" text below
	 * the breakpoint — a fourth label-bearing control does not fit beside a
	 * hamburger, a logo and a cart in 375px. Visually hidden rather than
	 * display:none: the <a> has no accessible name of its own, only this
	 * span's text, and display:none removes text from that computation —
	 * axe caught exactly this as a "Links must have discernible text"
	 * failure before this rule used the same clip-path technique
	 * .petpartners-visually-hidden already uses everywhere else. Switching
	 * to a second, icon-only block instance was the other option; this
	 * keeps one WooCommerce block the only thing that ever has to agree
	 * with itself about where the visitor is signed in.
	 */
	.petpartners-header .wc-block-customer-account__link .label {
		position: absolute;
		inline-size: 1px;
		block-size: 1px;
		margin: -1px;
		padding: 0;
		overflow: hidden;
		clip-path: inset( 50% );
		white-space: nowrap;
	}

	.petpartners-header .wc-block-customer-account__link {
		min-width: var( --wp--custom--chrome--tap-target );
		padding-inline: 0;
		justify-content: center;
	}
}

/* --- Category strip (faixa de categorias) -------------------------------- */

.petpartners-header .wp-block-petpartners-category-strip {
	background-color: var( --wp--preset--color--white );
}

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

/*
 * Core's own global stylesheet puts `margin-block-start: var( --wp--style--
 * block-gap )` on every child of `.wp-site-blocks` after the first — meant to
 * space header/main/footer apart, and normally invisible because `<main>`'s
 * own background usually matches whatever sits above and below it. Here it
 * measured as a 20px strip of plain white between the CTA band's `surface`
 * background and the footer's purple (client-reported, 12/10): `<footer
 * class="wp-block-template-part">`, not `.petpartners-footer` itself, which
 * is why nothing inside `.petpartners-footer` could have zeroed it. The
 * header is unaffected — it is the first child, already exempt by core's own
 * `:first-child` rule — so this only ever needed to target the footer.
 */
footer.wp-block-template-part {
	margin-block-start: 0;
}

/*
 * Dark again, by client request (11/out/2026) — reverses the white footer
 * from 01/out/2026 (ADR-058) back to a coloured surface, this time the
 * heading purple-blue rather than the old brown. `#4d2096` is now the
 * `purple` token itself (ADR-075, 11/out/2026) — kept as a literal hex here
 * anyway, matching the hero CTA gradient it is copied from below, which
 * predates ADR-075 and was never converted to `var()` (tasks/todo.md).
 *
 * Background is the hero "Shop Now" button's own gradient (client request,
 * 12/out/2026) — same two stops, same direction, copied verbatim from
 * `.petpartners-hero__actions .wp-block-button__link` below: `purple-hover`
 * on the left, this `#4d2096` on the right. `background-color` stays as a
 * flat fallback for the rare case a browser can't paint the gradient.
 *
 * Every colour below exists because inheriting the site's defaults here
 * would fail outright: a title or link left at the global heading/link
 * purple measures 1.63:1 against this background (CLAUDE.md 5.3's "nunca
 * roxo sobre marrom" reasoning, just a different dark surface), and body
 * text left at `text-gray` measures 1.69:1. White measures 10.7:1 against
 * the gradient's darkest stop (`purple-hover`, the more conservative end).
 * The stacked lockup switches to its white file via `onDark` on
 * `petpartners/site-brand` (parts/footer.html) — the same mechanism
 * `petpartners/species-links` already uses for a logo on a dark field.
 */
.petpartners-footer {
	position: relative;
	padding-block: var( --wp--preset--spacing--xxl ) var( --wp--preset--spacing--lg );
	background-color: #4d2096;
	background-image: linear-gradient( 90deg, var( --wp--preset--color--purple-hover ), #4d2096 );
	color: var( --wp--preset--color--white );

	/*
	 * No `overflow: hidden` — `petpartners/footer-pets` (first child) is
	 * pulled up out of this box on purpose (its own style.css has the
	 * mechanism) and a clip here would cut the image off at this element's
	 * own top edge, undoing the whole effect.
	 */
}

.petpartners-footer :where( h1, h2, h3, h4, h5, h6 ),
.petpartners-footer a {
	color: var( --wp--preset--color--white );
}

/*
 * No underline at rest, underline on hover/focus — a deliberate footer-only
 * reversal of the sitewide "links are always underlined" default
 * (check-contrast.mjs's own note on the `link` pair), client request,
 * 12/out/2026. Scoped to `.petpartners-footer a`, never a body class
 * (CLAUDE.md section 7).
 *
 * This also reverses `petpartners/link-column`'s own default, which the
 * block's style.css deliberately sets to underline-always, for a WCAG 1.4.1
 * reason spelled out there ("colour is not the only signal that something is
 * a link"). The footer brief's instruction is specific to exactly this
 * column list, not a general preference, so the override here is scoped one
 * level more specifically (`.petpartners-footer
 * .wp-block-petpartners-link-column__list a`) than the generic `.petpartners-
 * footer a` two rules up would reach on its own, winning regardless of
 * stylesheet load order. 1.4.1 is not actually given up: the links are still
 * white text in a column of white text, but hover and focus keep the
 * underline, and read a few lines down for the focus ring.
 */
.petpartners-footer a {
	text-decoration: none;
}

.petpartners-footer .wp-block-petpartners-link-column__list a {
	text-decoration: none;
}

.petpartners-footer a:hover,
.petpartners-footer a:focus-visible,
.petpartners-footer a:active {
	color: var( --wp--preset--color--white );
	text-decoration: underline;
}

.petpartners-footer .wp-block-petpartners-link-column__list a:hover,
.petpartners-footer .wp-block-petpartners-link-column__list a:focus-visible {
	text-decoration: underline;
	text-decoration-thickness: 2px;
}

/*
 * Purple-on-purple would disappear, same reasoning as `.petpartners-on-brown`
 * above — a different surface, the same fix.
 */
.petpartners-footer :focus-visible {
	outline-color: var( --wp--preset--color--white );
}

/*
 * `md`, not `xl`, below the desktop grid (14/out/2026, client request —
 * tighten the gap between the brand block and everything under it on
 * mobile). The desktop row below (`@media (min-width: 1024px)`) keeps its
 * own `xl` gap unchanged; this is the stacked mobile/tablet rhythm only.
 */
.petpartners-footer__inner {
	display: flex;
	flex-direction: column;
	gap: var( --wp--preset--spacing--md );
}

.petpartners-footer .petpartners-footer__brand {
	max-width: 22rem;
}

/*
 * White, not gold — client revision, 12/10. Gold stayed too close to the
 * other gold text nearby (column titles, newsletter eyebrow) to read as
 * emphasis rather than just "more gold"; `color` is inherited from
 * `.petpartners-footer` (white) once this declaration is gone, `font-weight`
 * is what actually sets this line apart from `__subcopy` below it.
 */
.petpartners-footer__tagline {
	margin: var( --wp--preset--spacing--sm ) 0 0;
	font-weight: 700;
}

.petpartners-footer__subcopy {
	margin: 2px 0 0;
	color: rgb( 255 255 255 / 78% );
	font-size: var( --wp--preset--font-size--sm );
}

.petpartners-footer .wp-block-petpartners-social-links {
	margin-top: var( --wp--preset--spacing--md );
}

/*
 * Thin translucent rules between the three desktop blocks — same mechanism
 * as `.petpartners-testimonials-eyebrow__line`, a fixed-thickness bar rather
 * than a border (a border on a flex sibling would need its own width
 * negotiated against the row, a 1px-wide block does not). Hidden below the
 * desktop grid (see the breakpoint below): a vertical rule between two
 * stacked blocks reads as a stray mark, not a divider.
 */
.petpartners-footer__divider {
	display: none;
}

/*
 * One column below the desktop grid (14/out/2026, footer mobile
 * reference) — supersedes the earlier "two columns, tablet and mobile
 * both" brief this comment used to describe. The four columns are
 * `<details>` accordion rows now (render.php), each full width with its
 * own divider underneath; a 2-up grid would pair two of those dividers
 * side by side instead of stacking them, which does not match the new
 * reference at all. Single `1fr` track, same breakpoint the accordion
 * itself switches on.
 */
.petpartners-footer .petpartners-footer__columns {
	display: grid;
	grid-template-columns: 1fr;
	gap: 0;
}

/*
 * Bolder, white — gold was the first revision (12/out/2026) and was reverted
 * the same day: with the newsletter eyebrow and the brand tagline also gold,
 * three different gold elements on one screen stopped reading as emphasis.
 * `color` falls back to `.petpartners-footer :where( h1...h6 )`'s white (this
 * title IS one of those heading levels, blocks/link-column/render.php) now
 * that nothing here overrides it; `font-weight` is what still sets a title
 * apart from the links under it. Uppercase, tracking and the sans family all
 * stay the block's own default (blocks/link-column/style.css) — untouched,
 * scoped under `.petpartners-footer` only for what this selector still sets.
 */
.petpartners-footer .wp-block-petpartners-link-column__title {
	font-weight: 700;
	margin: 0;
}

/*
 * "Explore Pet+ Partners" (14/out/2026, footer mobile reference) — heading
 * above the four link columns, mobile/tablet only. Hidden at the same
 * `min-width: 1024px` breakpoint the columns themselves switch to a
 * static desktop row at, a few hundred lines down — the label exists to
 * introduce the accordion, and there is no accordion once that happens.
 */
.petpartners-footer__columns-heading {
	margin: 0 0 var( --wp--preset--spacing--sm );
	font-weight: 700;
}

@media ( min-width: 1024px ) {
	.petpartners-footer__columns-heading {
		display: none;
	}
}

/*
 * The four link columns as a native accordion below 1024px (14/out/2026,
 * client reference) — `<details>`/`<summary>` in render.php, no
 * JavaScript. `list-style: none` removes the default disclosure triangle
 * (Chrome/Firefox put it on the `<li>`-like marker box, not as a
 * pseudo-element reachable by `::marker` alone in every engine) so the
 * SVG chevron the markup already draws is the only one shown.
 */
.wp-block-petpartners-link-column__details {
	border-block-end: 1px solid rgb( 255 255 255 / 20% );
}

.wp-block-petpartners-link-column__summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var( --wp--preset--spacing--sm );
	padding-block: var( --wp--preset--spacing--sm );
	list-style: none;
	cursor: pointer;
}

.wp-block-petpartners-link-column__summary::-webkit-details-marker {
	display: none;
}

.wp-block-petpartners-link-column__summary .wp-block-petpartners-link-column__title {
	margin: 0;
}

.wp-block-petpartners-link-column__chevron {
	flex: 0 0 auto;
	color: currentColor;
	transition: transform var( --wp--custom--motion--duration--fast ) var( --wp--custom--motion--easing--standard );
}

.wp-block-petpartners-link-column__details[ open ] .wp-block-petpartners-link-column__chevron {
	transform: rotate( 180deg );
}

.wp-block-petpartners-link-column__details .wp-block-petpartners-link-column__list {
	padding-block-end: var( --wp--preset--spacing--sm );
}

/*
 * Desktop (1024px and up): the same static column this block always
 * rendered, not an accordion anyone can toggle.
 *
 * This is NOT pure CSS, unlike the rest of this block — tried first, and
 * disproven by measurement before shipping it: forcing `display: block`
 * on the list inside a `<details>` that lacks the `open` attribute does
 * NOT make it render. The child reports `display: flex` and
 * `content-visibility: visible` in computed styles, genuinely not
 * display:none — but the `<details>` element's OWN box still reports a
 * height equal to just the summary's, `overflow: visible`, with the
 * (correctly-displayed) list rendering below it anyway, overlapping the
 * next column's own summary. Chromium's native `<details>` layout
 * appears to size the element's own box from the `open` IDL state
 * directly, a step the CSS cascade never gets to run on — not something
 * an author stylesheet can override, confirmed by that exact overlap
 * rather than assumed from documentation. view.js (new) sets the real
 * `open` property at `min-width: 1024px` instead, with a
 * `matchMedia().addEventListener('change', …)` to stay correct across a
 * resize or rotation, not just on load. No-JS fallback: every column
 * renders closed, the same as mobile — a visitor can still open each one
 * by hand, not a broken page, just not the always-open desktop layout.
 * The styling below (no border, no pointer cursor, no chevron) still
 * applies purely by CSS once JS has done the one thing CSS could not.
 */
@media ( min-width: 1024px ) {
	.wp-block-petpartners-link-column__details {
		border-block-end: none;
	}

	.wp-block-petpartners-link-column__summary {
		padding-block: 0;
		pointer-events: none;
		cursor: default;
	}

	.wp-block-petpartners-link-column__summary .wp-block-petpartners-link-column__title {
		margin: 0 0 var( --wp--preset--spacing--sm );
	}

	.wp-block-petpartners-link-column__chevron {
		display: none;
	}

	.wp-block-petpartners-link-column__details .wp-block-petpartners-link-column__list {
		padding-block-end: 0;
	}
}

.petpartners-footer__newsletter {
	max-width: 24rem;
}

/*
 * White, not gold (client revision, 12/10 — see `__tagline` above for the
 * same change and the same reason). `color` is unset here on purpose, same
 * mechanism: this is a `<p>`, not a heading, so it does not inherit from
 * `.petpartners-footer`'s heading rule — but it is already inside an element
 * `.petpartners-footer` set to `color: white` directly, and nothing between
 * here and there overrides it, so plain inheritance already lands on white
 * without a declaration.
 */
.petpartners-footer__newsletter-eyebrow {
	display: flex;
	align-items: center;
	gap: var( --wp--preset--spacing--xs );
	margin: 0 0 var( --wp--preset--spacing--sm );
	font-size: var( --wp--preset--font-size--label );
	font-weight: 600;
	letter-spacing: var( --wp--custom--typography--label--letter-spacing );
	text-transform: uppercase;
}

.petpartners-footer__newsletter-eyebrow-line {
	display: inline-block;
	flex: 0 0 auto;
	inline-size: 24px;
	block-size: 1px;
	background-color: rgb( 255 255 255 / 40% );
}

/*
 * Serif at the `heading` token's size (24px), not the sans family that size
 * normally carries — the same deliberate pairing `.petpartners-about-intro__
 * subtitle` already uses for the same reason: a serif subhead at this size
 * reads as editorial without needing a new size token.
 */
.petpartners-footer__newsletter-title {
	margin: 0 0 var( --wp--preset--spacing--xs );
	font-family: var( --wp--preset--font-family--serif-display );
	font-size: var( --wp--preset--font-size--heading );
	font-weight: 600;
	line-height: var( --wp--custom--typography--heading--line-height );
}

.petpartners-footer__newsletter-text {
	margin: 0 0 var( --wp--preset--spacing--sm );
	color: rgb( 255 255 255 / 78% );
	font-size: var( --wp--preset--font-size--sm );
}

/*
 * Pill-shaped field, brown button embedded inside it — the form itself is
 * the pill (white background, full radius, a little padding), the input is
 * bare and the button is a second, smaller pill sitting inside. `gap` keeps
 * the button from touching the input's text.
 */
.petpartners-footer__newsletter-form {
	display: flex;
	align-items: center;
	gap: 4px;
	padding: 4px;
	border-radius: var( --wp--preset--border-radius--pill );
	background-color: var( --wp--preset--color--white );
}

.petpartners-footer__newsletter-input {
	flex: 1 1 auto;
	min-width: 0;
	padding: 10px var( --wp--preset--spacing--sm );
	border: none;
	background: none;
	color: var( --wp--preset--color--brown );
	font-size: 16px;
}

.petpartners-footer__newsletter-input::placeholder {
	color: var( --wp--preset--color--text-muted );
}

.petpartners-footer__newsletter-input:focus-visible {
	outline: 2px solid var( --wp--preset--color--purple );
	outline-offset: 2px;
}

/*
 * Same purple gradient as "Help Me Choose" just above this section
 * (`.petpartners-cta-band__action .wp-block-button__link`) — client asked
 * for the two buttons to match, 13/out/2026, after the brown/white pass
 * (this file's own history above this rule). Literal `#4d2096` for the same
 * reason as every other reuse of this exact gradient on the page: matching
 * an existing one-off exception rather than opening a new one — fourth reuse
 * now, after the hero button, `.petpartners-about-intro__cta` and
 * `.petpartners-cta-band__action`.
 */
.petpartners-footer__newsletter-submit {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 10px var( --wp--preset--spacing--md );
	border: none;
	border-radius: var( --wp--preset--border-radius--pill );
	background-color: #4d2096;
	background-image: linear-gradient( 90deg, var( --wp--preset--color--purple-hover ), #4d2096 );
	box-shadow: var( --wp--preset--shadow--sm );
	color: var( --wp--preset--color--white );
	font-size: var( --wp--preset--font-size--sm );
	font-weight: 600;
	cursor: not-allowed;
}

/*
 * Disabled rather than wired to a fake success state — no newsletter
 * platform is chosen yet (CLAUDE.md section 11, still open; tasks/todo.md).
 * `disabled` also removes the button from the Enter-key implicit-submit
 * path, so the (fully usable, required, type=email) input above cannot
 * actually submit anywhere either, without disabling the input itself.
 */
.petpartners-footer__newsletter-submit[disabled] {
	opacity: 0.85;
}

.petpartners-footer__newsletter-note {
	display: flex;
	align-items: center;
	gap: 6px;
	margin: var( --wp--preset--spacing--sm ) 0 0;
	color: rgb( 255 255 255 / 68% );
	font-size: var( --wp--preset--font-size--sm );
}

/*
 * White, not brown — client asked for the footer's gold swapped to brown
 * (13/out/2026), but this icon sits directly on the footer's own purple
 * background, not inside a brown box like the newsletter button does.
 * Brown-on-purple measures 1.6:1 and CLAUDE.md section 5.3 bans the pair
 * outright ("nunca roxo sobre marrom nem o inverso"); white is what every
 * other text/icon on this same purple surface already uses (tagline,
 * column titles, eyebrow — ADR-078's own Adendo already moved all three off
 * gold onto white for a related reason).
 */
.petpartners-footer__newsletter-note svg {
	flex: 0 0 auto;
	color: var( --wp--preset--color--white );
}

/*
 * Desktop: three blocks and two dividers in one row — brand narrow, columns
 * taking the middle span, newsletter narrow. `minmax()` on the two ends keeps
 * both from collapsing below a readable width before the row wraps.
 */
@media ( min-width: 1024px ) {
	.petpartners-footer__inner {
		display: grid;
		grid-template-columns: minmax( 220px, 290px ) 1px 1fr 1px minmax( 260px, 320px );
		gap: var( --wp--preset--spacing--xl );
		align-items: start;
	}

	.petpartners-footer .petpartners-footer__brand {
		max-width: none;
	}

	.petpartners-footer__divider {
		display: block;
		align-self: stretch;
		inline-size: 1px;
		background-color: rgb( 255 255 255 / 20% );
	}

	.petpartners-footer__newsletter {
		max-width: none;
	}

	/*
	 * Four across in one row, not a fixed 2-column grid — the middle track is
	 * wide enough here (`1fr` of a ~1320px row) that a flex-wrap row already
	 * lands at four, and letting it reflow by its own content width means a
	 * fifth column (should the footer ever need one) finds its own place
	 * instead of fighting a grid sized for exactly four.
	 */
	.petpartners-footer .petpartners-footer__columns {
		display: flex;
		flex-wrap: wrap;
		align-items: flex-start;
	}

	.petpartners-footer .wp-block-petpartners-link-column {
		flex: 0 1 10rem;
	}
}

/*
 * Three-column grid, not flex — the paw tagline is the one piece the client
 * asked to sit on the true horizontal center of the bar (13/out/2026), and
 * that has to hold regardless of how wide the copyright text or the (mostly
 * empty today) payment-badges block next to it happen to be. `1fr auto 1fr`
 * is the standard centered-header trick: the two `1fr` tracks are always
 * forced to equal width by the single shared fr unit, so the middle `auto`
 * track — sized to its own content, the tagline — lands exactly on centre no
 * matter what is or is not in the outer two tracks. This keeps working even
 * when `petpartners/payment-badges` renders nothing (no gateway configured
 * yet, P2b): the third track still reserves its 1fr share because it is
 * explicitly declared, an empty track is not collapsed.
 *
 * Specificity note: this is a core/group block with `layout:{"type":"flex"}`
 * in its markup (`parts/footer.html`), so WordPress's own block-supports
 * stylesheet auto-generates a `.wp-container-core-group-is-layout-xxxxxxxx
 * { display:flex; ... }` rule for it, loaded after this file. A bare
 * `.petpartners-footer__bottom` selector ties that rule on specificity and
 * loses on source order; scoping one class deeper
 * (`.petpartners-footer .petpartners-footer__bottom`) wins outright, same
 * technique already used elsewhere in this file for the same reason.
 */
.petpartners-footer .petpartners-footer__bottom {
	display: grid;
	grid-template-columns: 1fr auto 1fr;
	align-items: center;
	gap: var( --wp--preset--spacing--md );
	margin-top: var( --wp--preset--spacing--xl );
	padding-top: var( --wp--preset--spacing--md );
	/* The `border` token (CLAUDE.md 5.2) is a near-white gray, invisible on
	   this background — a translucent white divider instead, decorative only,
	   same as the token it replaces. */
	border-top: 1px solid rgb( 255 255 255 / 25% );
}

.petpartners-footer__bottom-tagline {
	display: flex;
	align-items: center;
	justify-self: center;
	gap: var( --wp--preset--spacing--xs );
	margin: 0;
	color: rgb( 255 255 255 / 85% );
	font-size: var( --wp--preset--font-size--sm );
}

/*
 * White, not brown — same reason as `.petpartners-footer__newsletter-note
 * svg` just above: this paw sits directly on the footer's own purple
 * background, and brown-on-purple is the one pair CLAUDE.md section 5.3
 * bans outright (1.6:1).
 */
.petpartners-footer__bottom-paw {
	flex: 0 0 auto;
	inline-size: 20px;
	block-size: 20px;
	color: var( --wp--preset--color--white );
}

@media ( max-width: 781px ) {
	.petpartners-footer .petpartners-footer__bottom {
		grid-template-columns: 1fr;
		justify-items: center;
		text-align: center;
	}
}

/* --- WooCommerce defects fixed from our side --------------------------- */

/*
 * The closed mini-cart drawer is keyboard reachable.
 *
 * WooCommerce renders it with aria-hidden="true" but leaves it display:block
 * and visibility:visible, holding four focusable controls. A keyboard visitor
 * tabs off the cart icon straight into a panel nobody can see — WCAG 2.4.3 and
 * 4.1.2, and axe rates it serious on every page of the site.
 *
 * visibility:hidden rather than display:none: it takes the subtree out of the
 * tab order and out of the accessibility tree exactly as display:none would,
 * but keeps the element in layout so WooCommerce's own slide-in still has
 * something to animate. Nothing in WooCommerce is edited — CLAUDE.md section 7
 * forbids that, and an override here survives their next release.
 *
 * Full drawer work is P9. This is the accessibility defect only.
 */
.wc-block-components-drawer__screen-overlay--is-hidden {
	visibility: hidden;
}

/*
 * Inactive product tabs fail contrast.
 *
 * WooCommerce puts opacity .65 on the tab list item, and the brand brown at
 * that opacity blends to #8d7f73 against white — 3.87:1, under the 4.5 that
 * 16px bold text owes. The fade is doing the job the active state already does
 * with weight and an underline, so dropping it costs nothing, and naming a
 * real colour is what makes the value checkable: text-muted measures 5.5:1.
 *
 * The selector is as long as it is because WooCommerce's own is, and matching
 * its specificity is the only way to win without !important. It is on the list
 * item rather than the link because that is where the opacity lives, and the
 * link inside it is set to color:inherit by the same stylesheet.
 *
 * The product page belongs to P8 and is not styled here; this is the
 * accessibility failure only, found by the P5 scan and fixed rather than
 * handed on.
 */
/* stylelint-disable selector-max-compound-selectors -- matching WooCommerce. */
html body .wp-block-woocommerce-product-details.is-style-minimal ul.tabs.wc-tabs li {
	opacity: 1;
	color: var( --wp--preset--color--text-muted );
}

html body .wp-block-woocommerce-product-details.is-style-minimal ul.tabs.wc-tabs li.active {
	color: var( --wp--preset--color--brown );
}
/* stylelint-enable selector-max-compound-selectors */

/* --- Home page -------------------------------------------------------- */

/*
 * The sections set their own vertical rhythm rather than leaning on blockGap,
 * because three of them carry a full-bleed background and a gap between two
 * coloured bands reads as a seam.
 */
.petpartners-section {
	padding-block: var( --wp--preset--spacing--xl );
}

.petpartners-section__inner > .wp-block-heading {
	margin-block: 0 var( --wp--preset--spacing--sm );
}

.petpartners-section__lead {
	margin-bottom: var( --wp--preset--spacing--lg );
	max-width: var( --wp--custom--measure--text );
}

/*
 * A centred lead needs its MEASURE centred too, not just its text. Left as a
 * plain max-width the box stays flush left and the centred lines sit under the
 * left third of the heading — which reads as a mistake rather than a choice.
 *
 * Keyed on the alignment class WordPress emits rather than on a section, so it
 * is true wherever a lead is centred and inert everywhere a lead is not. The
 * hero's lead needed the same fix and got it locally; this is the general form.
 */
.petpartners-section__lead.has-text-align-center {
	margin-inline: auto;
}

/*
 * Testimonials eyebrow — same label treatment as the hero's own
 * `.petpartners-hero__eyebrow` (one dash, left-aligned, ADR-056), centred
 * with a dash on each side instead of one: this section has no column of
 * left-aligned copy beside it to set an alignment from, the heading under
 * it is centred, and a single dash reads as an orphaned fragment once the
 * text it was leading into is centred too. The small filled paw between the
 * left line and the text was added in the 12/out/2026 redesign
 * (patterns/testimonials.php); colour is `brown`, matching the reference
 * rather than the `purple` the About section's own eyebrow icon uses — two
 * different eyebrows, two different accent colours already in that pattern.
 */
.petpartners-testimonials-eyebrow {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var( --wp--preset--spacing--xs );
	margin: 0 0 var( --wp--preset--spacing--sm );
	color: var( --wp--preset--color--brown );
	font-size: var( --wp--preset--font-size--label );
	font-weight: 600;
	line-height: var( --wp--custom--typography--label--line-height );
	letter-spacing: var( --wp--custom--typography--label--letter-spacing );
	text-transform: uppercase;
}

.petpartners-testimonials-eyebrow__line {
	display: inline-block;
	flex: 0 0 auto;
	inline-size: 32px;
	block-size: 1px;
	background-color: var( --wp--preset--color--gray-light );
}

.petpartners-testimonials-eyebrow__icon {
	display: inline-flex;
	flex: 0 0 auto;
	inline-size: 18px;
	block-size: 18px;
	color: var( --wp--preset--color--brown );
}

/*
 * Two-tone title — same mechanism as `.petpartners-about-intro__title-line`
 * (a coloured `<span>` per word group inside one `is-style-display-lg`
 * heading, wrapped in a real `<!-- wp:heading -->` comment so the style
 * variation's CSS actually generates — see about-intro.php's docblock for
 * the full account of that bug). Not shared with About's classes: every
 * coloured text block in the theme keeps its own scoped class rather than a
 * shared one, even though both titles sit on one line, inline spans, no
 * flex — About's own title dropped its `flex-direction: column` the same
 * day this comment was last touched (13/out/2026, client request).
 */
.petpartners-testimonials-title-word--purple {
	color: var( --wp--preset--color--purple );
}

.petpartners-testimonials-title-word--brown {
	color: var( --wp--preset--color--brown );
}

.petpartners-section__inner > .wp-block-petpartners-testimonials {
	margin-block-start: var( --wp--preset--spacing--lg );
}

/*
 * Section band — warm off-white background behind the whole testimonials
 * section, replacing the plain white it had before the 12/out/2026 redesign
 * (ADR-076, Adendo). `#fbf9f7` since 13/out/2026 — client colour, same one
 * applied to the hero's three promise cards the same day (ADR-068 Adendo 8),
 * reused here rather than a fourth near-white literal; replaces the earlier
 * `#FAF6F3` this section, `.petpartners-about-intro` and `.petpartners-cta-
 * band` all shared.
 */
.petpartners-testimonials-section {
	position: relative;
	overflow: hidden;
	background-color: #fbf9f7;
}

/*
 * `overflow: hidden` (14/out/2026 — real bug, not a cosmetic choice). The
 * generic `.petpartners-section` wrapper this pattern uses everywhere else
 * never needed it: most sections it wraps have no content wider than the
 * viewport. This one does — `petpartners/brand-strip`'s own `__track` is a
 * horizontal rail, deliberately wider than its box so it can scroll
 * sideways — and without a container somewhere in the chain clipping that,
 * the overflow was not staying inside the rail's own `overflow-x: auto`
 * track at all: it was reaching all the way up through `<main>` to
 * `<body>`, making the ENTIRE PAGE scroll sideways on mobile (confirmed
 * with a real 375px Puppeteer viewport — `document.documentElement.
 * scrollWidth` read 1289px against a 375px `clientWidth`, and tracing the
 * ancestor chain found this exact element reporting `overflow: visible`
 * where `.petpartners-testimonials-section` just above already has its
 * own `overflow: hidden` for the identical rail-containment reason). Not
 * applied to the shared `.petpartners-section` class itself — that would
 * risk clipping something else's intentional bleed elsewhere on the site
 * — scoped to this pattern's own class instead, same convention
 * `.petpartners-testimonials-section` already set.
 */
.petpartners-brand-index {
	overflow: hidden;
}

.petpartners-testimonials-section__inner {
	position: relative;
	z-index: 1;
}

/*
 * Two watermark paws — a small one upper-left, a larger one upper-right.
 * A third, partly clipped by the section's own `overflow: hidden` at the
 * bottom-right, matched the original reference image but was removed
 * 13/out/2026 at the client's request (ADR-076, Adendo) — rule and markup
 * gone together, not one without the other. The top-right paw was also
 * halved the same request, 260px → 130px (desktop) — it read as too large
 * next to the now-smaller testimonial cards. Same client-supplied SVG
 * markup as `.about-watermark` (about-intro.php's docblock has the full
 * provenance), same beige (`#F1EAE4`, no existing token sits this close to
 * the section background without being it) and the same `currentColor`
 * fill mechanism — two positioned instances of one shared shape, not two
 * different drawings.
 */
.testimonials-watermark {
	position: absolute;
	z-index: 0;
	color: #f1eae4;
	pointer-events: none;
}

.testimonials-watermark--top-left {
	inset-block-start: 24px;
	inset-inline-start: 40px;
	inline-size: 110px;
	block-size: 110px;
	transform: rotate( -15deg );
}

.testimonials-watermark--top-right {
	inset-block-start: 24px;
	inset-inline-end: 40px;
	inline-size: 130px;
	block-size: 130px;
	transform: rotate( 15deg );
}

/*
 * Hidden below 900px (14/out/2026, client request) — was shrunk, not
 * removed, until now; the client asked for both paws gone on mobile
 * outright.
 */
@media ( max-width: 899px ) {
	.testimonials-watermark--top-left,
	.testimonials-watermark--top-right {
		display: none;
	}
}

/*
 * The hero.
 *
 * White field, a fixed-width photo panel on the right with a crisp organic
 * wave cut into its left edge — no fade, no blur (ADR-063, Adendo 4,
 * 01/out/2026). The client sent exact numbers after comparing Adendo 3
 * (full-bleed photo, soft gradient, no visible edge) against the reference
 * again and finding it still short: the reference's edge is sharp, the
 * photo is a panel rather than full-bleed, and there is a small decorative
 * blob in the top-right corner this version had not accounted for.
 *
 * THE PHOTOGRAPH'S PROVENANCE IS STILL UNCONFIRMED. [CLIENT INPUT]
 *
 * Same open question as always — see the pattern file's own docblock
 * (patterns/hero-home.php) and tasks/todo.md.
 */

.petpartners-hero {
	position: relative;
	overflow: hidden;
	background-color: var( --wp--preset--color--white );
}

.petpartners-hero__content {
	position: relative;
	z-index: 2;
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	padding: var( --wp--preset--spacing--lg );
}

/*
 * PAW WATERMARKS — decoration in the blank white gap, not behind the text
 * (06/out/2026, repositioned the same day — ADR-068, Adendo 5).
 *
 * A sibling of .petpartners-hero__photo and .petpartners-hero__content
 * inside .petpartners-hero, not a child of either — see the long comment
 * in hero-home.php for why nesting it inside the content column was the
 * bug the client reported. z-index: 0, between the hero's own stacking
 * order (photo: 1, content: 2): a paw positioned where the photo's
 * rectangular box actually paints stays correctly hidden behind it — the
 * wave clip-path decides that, this file does not need to — and a paw
 * positioned in the blank gap beside a line of text reads as beside it,
 * never as a mark sitting under the letters.
 *
 * Positions below are percentages of the FULL hero box (.petpartners-hero
 * itself), not of the narrower text column — measured against the text's
 * own rendered bounding boxes at 1440px (eyebrow, title, lead, actions)
 * so every paw lands in the gap between or beside them, never inside one.
 * Every value here is a literal, not a token, same reasoning as the hero
 * title's font and colour (CLAUDE.md section 5): one-off numbers from a
 * reference composition, nothing else in the design system reads them.
 *
 * The image carries its own colour — a pastel beige raster the client sent
 * directly, cropped to its drawn area and resized the same way the hero's
 * other supplied-art images are (`scripts/alpha-bbox.mjs` + `cwebp`,
 * CLAUDE.md section 6's pattern for 3D/colour source art).
 */
.petpartners-hero__paws {
	position: absolute;
	inset: 0;
	z-index: 0;
	pointer-events: none;
}

.petpartners-hero__paw {
	position: absolute;
	height: auto;
	/*
	 * 0.055 (the client's original spec) read as invisible on her own
	 * screen; raised to 0.14, still not enough — this is the second raise.
	 * Still a watermark, not a graphic, just one a visitor can actually
	 * register without hunting for it.
	 */
	opacity: 0.22;
	user-select: none;
}

.petpartners-hero__paw--1 {
	top: calc( 4% + 20px );
	left: calc( 32% - 50px );
	width: clamp( 28px, 2.3vw, 44px );
	transform: rotate( -8deg );
}

/*
 * Beside "Your" specifically, not the upper curve of the photo it sat
 * against before — measured with Range.getBoundingClientRect() against that
 * exact word (top 24.6%–39.8%, ending at 32.9%, the last word on the
 * title's first line): this sits just past where "Your" ends, same row.
 */
.petpartners-hero__paw--2 {
	top: calc( 29% - 50px );
	left: 35%;
	width: clamp( 52px, 4.5vw, 82px );
	transform: rotate( 7deg );
}

.petpartners-hero__paw--3 {
	top: 46%;
	left: 40%;
	width: clamp( 30px, 2.6vw, 48px );
	transform: rotate( -10deg );
}

/*
 * Beside "support" — measured the same way as paw-2 beside "Your": the
 * lead's own text node, the word's range (top 53.2%–58.1%, ending at
 * 29.4%), this sits just past where it ends, same row.
 */
.petpartners-hero__paw--4 {
	top: calc( 52% - 45px );
	left: calc( 32% - 15px );
	width: clamp( 28px, 2.4vw, 44px );
	transform: rotate( 8deg );
}

.petpartners-hero__paw--5 {
	top: calc( 70% - 10px );
	left: calc( 28% + 55px );
	width: clamp( 60px, 5vw, 92px );
	transform: rotate( -7deg );
}

/*
 * Tablet (≤1100px): the gap beside the text is narrower than desktop but
 * the photo still sits beside it, not above it, so one paw comes out — #3,
 * the one sitting deepest into the photo's own rectangular box and
 * therefore most often fully masked by it at this narrower width — and the
 * rest dim slightly.
 */
@media ( max-width: 1100px ) {

	.petpartners-hero__paw {
		opacity: 0.16;
	}

	.petpartners-hero__paw--3 {
		display: none;
	}
}

/*
 * Mobile (≤768px): copy before photo, reference image from the client
 * (14/out/2026) — was photo-then-copy (plain document order: the photo
 * markup sits first in hero-home.php, so it painted first in normal flow).
 * `.petpartners-hero` becomes a flex column here and `order` swaps the two
 * visually without touching the DOM: the photo stays the first element in
 * markup (so it is still the thing a screen reader meets right after the
 * header landmark is a reasonable trade — but what matters for `order` is
 * that nothing here needed a markup change, only a visual one).
 * `.petpartners-hero__paws` needs no `order` of its own: it is `position:
 * absolute` against `.petpartners-hero`, already out of flex flow.
 *
 * Two paws only, pinned near the corners of the white content column the
 * brief named — small near its top, large near its bottom — rather than the
 * desktop five scaled down. Positions are now pixel offsets from the TOP of
 * the hero (content is the first flex item, so "near the top of the content
 * column" IS near the top of the box now) — before the reorder these were
 * measured from the BOTTOM instead (content used to follow a fixed-height
 * 340px photo, so a pixel offset only worked added onto that). Same
 * literal-pixel reasoning as before, flipped to the new anchor: the content
 * column's height is text-driven, not a fixed box, so there is still no
 * percentage that stays correct on its own.
 */
@media ( max-width: 768px ) {

	.petpartners-hero {
		display: flex;
		flex-direction: column;
	}

	.petpartners-hero__content {
		order: 1;
	}

	.petpartners-hero__photo {
		/*
		 * Only `order` here. The inset width/margin/border-radius for this
		 * same reorder live in the OTHER `.petpartners-hero__photo` rule
		 * further down this file instead (the unconditional one headed
		 * "Mobile (below 768px): stacked") — that rule has no media query
		 * of its own, applies at every width until the `min-width: 768px`
		 * block after it says otherwise, and sits LATER in the file than
		 * this `max-width: 768px` block. Equal specificity, later source
		 * position: it would win a `width`/`margin` fight against a
		 * declaration placed here regardless of which media query matches
		 * the viewport — confirmed by hand the first time this was written
		 * with the inset rules here instead, which this file's own cascade
		 * silently discarded. `order` does not have that problem (nothing
		 * later sets it), so it is the one property safe to put in this
		 * block.
		 */
		order: 2;
	}

	.petpartners-hero__paw--1,
	.petpartners-hero__paw--3,
	.petpartners-hero__paw--4 {
		display: none;
	}

	.petpartners-hero__paw {
		opacity: 0.16;
	}

	/*
	 * 15px down, 15px left of the previous position (14/out/2026, client
	 * request) — `top` and `right` both just add that literal onto what was
	 * already there: `right` grows BY 15px to move the paw left (it is
	 * measured from the right edge, so a larger offset means further
	 * inward), `calc()` keeps the existing `%` base instead of replacing it
	 * with a flat px value.
	 */
	.petpartners-hero__paw--2 {
		top: 19px;
		left: auto;
		right: calc( 6% + 15px );
		width: clamp( 26px, 7vw, 36px );
		transform: rotate( 7deg );
	}

	.petpartners-hero__paw--5 {
		top: 245px;
		bottom: auto;
		left: auto;
		right: calc( 4% + 15px );
		width: clamp( 48px, 14vw, 68px );
		transform: rotate( -7deg );
	}
}

/*
 * #4d2096, not the `purple` token (#742EDF). The client asked for the home
 * title and the Shop Now button to carry the logo's own purple specifically
 * — a scoped exception, not a palette change: every other heading and CTA
 * site-wide stays on the token. No custom property either, by the same
 * reasoning the hero cards' rgba() fill is a literal value (CLAUDE.md
 * section 5) — this colour belongs to the logo file, not to a design
 * decision meant to apply anywhere else a token would reach.
 */
/*
 * The eyebrow above the title. `gray-light` for the line is the token's own
 * documented use (CLAUDE.md 5.1: "só decorativo... divisores") — it is never
 * read as text, so the 2.5:1 contrast the token fails at is not a WCAG
 * question here. The label itself uses the `label` typography token
 * (CLAUDE.md 5.5: sans 600, uppercase, 0.06em tracking) exactly as specified
 * for "labels e badges", in `brown` rather than `text-gray` because this
 * reads as a small structural tag beside the title, not body copy.
 */
.petpartners-hero__eyebrow {
	display: flex;
	align-items: center;
	gap: var( --wp--preset--spacing--xs );
	margin: 0 0 var( --wp--preset--spacing--sm );
	color: var( --wp--preset--color--brown );
	font-size: var( --wp--preset--font-size--label );
	font-weight: 600;
	line-height: var( --wp--custom--typography--label--line-height );
	letter-spacing: var( --wp--custom--typography--label--letter-spacing );
	text-transform: uppercase;
}

.petpartners-hero__eyebrow-line {
	display: inline-block;
	flex: 0 0 auto;
	inline-size: 32px;
	block-size: 1px;
	background-color: var( --wp--preset--color--gray-light );
}

/*
 * Fraunces, not the `serif-display` token (Cormorant Garamond) — scoped to
 * this title only, 03/out/2026. The client asked whether the logo's
 * lettering was the same typeface as the hero title; it is not (the logo is
 * a vectorised, outlined wordmark with no linked font file), and a visual
 * comparison led to trying a bolder, lower-contrast serif here instead.
 * Explicitly scoped: every other heading site-wide (landing, category,
 * section titles — CLAUDE.md 5.5) stays on Cormorant Garamond. Same
 * reasoning as the #4d2096 colour below: a deliberate, local departure from
 * the token, not a typography-system change.
 *
 * Self-hosted per CLAUDE.md 5.5 (OFL.txt alongside the file, no Google
 * Fonts CDN), but declared here rather than as a theme.json fontFamily:
 * adding it to settings.typography.fontFamilies would offer it sitewide in
 * the editor, which overstates a choice that is this one element's only.
 *
 * Not preloaded, same reasoning functions.php already documents for
 * Cormorant: P6 measured the hero's LCP element as the Inter lead
 * paragraph, already preloaded on its own — a second preloaded serif can
 * only compete with it, not help it.
 */
@font-face {
	font-family: 'Fraunces';
	src: url( '../fonts/fraunces/fraunces-600.woff2' ) format( 'woff2' );
	font-weight: 600;
	font-style: normal;
	font-display: swap;
}

.petpartners-hero__title {
	margin: 0;
	color: #4d2096;
	font-family: 'Fraunces', var( --wp--preset--font-family--serif-display );
}

.petpartners-hero__lead {
	margin-block: var( --wp--preset--spacing--sm ) 0;
	max-width: var( --wp--custom--measure--text );
}

.petpartners-hero__actions {
	margin-block-start: var( --wp--preset--spacing--md );
}

/*
 * Same exception as the title above, now a gradient: left stop is the
 * existing `purple-hover` token, right stop is this button's own #4d2096 —
 * sampled off the reference image, and the right end already matched the
 * solid colour this button had, so only the left stop is new. Hover drops
 * the gradient for the flat #411b7f this button already used (#4d2096
 * darkened by the same ratio `purple-hover` darkens `purple`) — background-
 * image has to be reset to none explicitly, or the gradient (painted above
 * background-color) would keep covering the hover colour underneath it.
 */
.petpartners-hero__actions .wp-block-button__link {
	display: inline-flex;
	align-items: center;
	gap: var( --wp--preset--spacing--xs );
	background-color: #4d2096;
	background-image: linear-gradient( 90deg, var( --wp--preset--color--purple-hover ), #4d2096 );
}

.petpartners-hero__actions .wp-block-button__link:hover,
.petpartners-hero__actions .wp-block-button__link:focus-visible {
	background-image: none;
	background-color: #411b7f;
}

/*
 * The arrow rides the button's own white text colour, so it needs no colour
 * of its own — just space from the label.
 */
.petpartners-hero__cta-arrow {
	display: inline-block;
}

/*
 * Mobile (below 768px): stacked. The photo is a normal flow element below
 * the copy, not absolutely positioned — there is no right-hand panel to
 * place at this width. Its own wave moves from the left edge to the top
 * edge (heroWaveMobile, the pattern's second <clipPath>), because a cut
 * down the side of a full-width stacked photo reads as a diagonal slice,
 * not a wave.
 */
.petpartners-hero__photo {
	/*
	 * WordPress's flow-layout spacing adds a margin-block-start between
	 * consecutive children of a block (the gap it also had to be told not to
	 * add above <main>, elsewhere in this file) — this is the second place
	 * today it turned up somewhere it was never meant to be seen: with
	 * top: 0 and bottom: 0 both set, an inherited margin does not get
	 * absorbed, it eats into the computed height instead, which is what
	 * floated this panel 20px down from the hero's own top edge with a
	 * matching 20px gap short of its bottom. Block-axis zeroed here, once,
	 * rather than patched per breakpoint.
	 *
	 * Inline-axis margin is NOT zero, though, since the reference image for
	 * the content-before-photo reorder (14/out/2026) shows the photo as an
	 * inset card sharing the content column's own side margins, not a
	 * full-bleed panel — `spacing--lg` on each side is the same token
	 * `.petpartners-hero__content`'s own padding already uses, so the
	 * photo's edges land exactly under the title and button above it.
	 * `width: auto` (not `100%`) is what lets that margin actually shrink
	 * the box instead of overflowing it.
	 */
	margin-block: 0;
	margin-inline: var( --wp--preset--spacing--lg );
	position: relative;
	width: auto;
	height: 340px;
	border-radius: var( --wp--preset--border-radius--xl );
	clip-path: url( '#heroWaveMobile' );
}

.petpartners-hero__photo img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: 60% center;
}

/*
 * Tablet and up (768px): the photo becomes an absolute panel on the right,
 * the hero becomes a fixed-height field, and the wave moves to the photo's
 * left edge.
 */
@media ( min-width: 768px ) {

	.petpartners-hero {
		display: flex;
		align-items: center;
		min-height: 520px;
	}

	.petpartners-hero__content {
		z-index: 2;
		width: min( 100%, 520px );
		margin: 0;
		padding: 0;
		padding-inline-start: var( --wp--preset--spacing--xl );
	}

	.petpartners-hero__photo {
		position: absolute;
		inset-block: 0;
		inset-inline-end: 0;
		/*
		 * Explicit, not left to cascade from the mobile rule. inset-block: 0
		 * (top: 0, bottom: 0) only determines an absolutely positioned
		 * element's height when its own height is auto — the mobile rule's
		 * height: 340px is more specific than that default and would
		 * otherwise keep winning here, which is exactly what happened the
		 * first time this media query shipped without this line: the panel
		 * stayed 340px tall floating inside a 560px hero instead of filling it.
		 */
		height: auto;
		width: 55%;
		z-index: 1;
		clip-path: url( '#heroWave' );
	}

	.petpartners-hero__photo img {
		object-position: 60% center;
	}
}

/*
 * Desktop (1024px and up): the client measured the reference directly and
 * gave exact numbers (ADR-063, Adendo 8) — this tier's box geometry is a
 * full override of the tablet defaults above, not an adjustment to them.
 */
@media ( min-width: 1024px ) {

	.petpartners-hero {
		min-height: clamp( 460px, 34vw, 560px );
	}

	/*
	 * left: 29% / width: 71% replaces the tablet tier's inset-inline-end-only
	 * box — the panel now starts at a fixed fraction from the left rather
	 * than being sized purely by its own width against the right edge.
	 * inset-block-end: 23px, not 0: the client measured the reference's photo
	 * ending about 15px below the top edge of the cards that overlap it, and
	 * the cards' own stack (-70px margin, then this section's lg padding
	 * before the card itself starts) puts their top 38px above the hero's own
	 * bottom edge — 38 - 15 = 23px is how far up the photo's base has to lift
	 * to land exactly there instead of flush with the hero.
	 */
	.petpartners-hero__photo {
		top: 0;
		right: 0;
		left: 29%;
		bottom: 23px;
		width: 71%;
		height: auto;
	}

	.petpartners-hero__photo img {
		object-position: 70% center;
	}

	/*
	 * A scoped departure from the display-xl token's own fluid range
	 * (44-72px) — the client's clamp() is narrower at both ends, specifically
	 * so the title reliably breaks to 2-3 lines short of the curve at this
	 * box width rather than running the token's normal range. Local to this
	 * heading only; nothing about the token itself changes.
	 */
	.petpartners-hero__title {
		font-size: clamp( 40px, 4.2vw, 64px );
	}
}

/*
 * The promises row.
 *
 * Riding up over the photo's base at tablet and up, each promise its own
 * translucent card (ADR-063, Adendo 6) — a reversal of Adendo 5's solid
 * white section, which the client chose deliberately this time, reference
 * image in hand: the photo has to stay visible between and behind the
 * cards, not disappear behind a solid band the way the previous version
 * drew it. The per-pixel contrast risk that solid white was avoiding is
 * handled differently now — each card still carries its own light,
 * near-opaque fill behind its own text, so no glyph ever sits directly on
 * photograph pixels even though the section around the cards does.
 *
 * The row went edge-to-edge in Adendo 9: at 1024px and up the photo box is
 * a fixed fraction of the hero (left: 29%, Adendo 8), so its right edge
 * always lands flush with the viewport — a row narrower than that leaves a
 * straight, uncovered stretch of photo to the right of the cards. The
 * `.petpartners-section__inner` wrapper carries its own
 * `padding-inline` for this row instead of the usual `alignwide` cap, which
 * is what was producing that gap.
 */
.petpartners-hero-promises {
	position: relative;
	z-index: 2;
	padding-block: var( --wp--preset--spacing--lg );
	background-color: transparent;
}

.petpartners-hero-promises .petpartners-section__inner {
	padding-inline: var( --wp--preset--spacing--lg );
}

@media ( min-width: 768px ) {

	.petpartners-hero-promises {
		margin-block-start: -60px;
	}
}

.petpartners-hero-promises__list {
	display: flex;
	flex-direction: column;
	gap: 16px;
	margin: 0;
	padding: 0;
	list-style: none;
}

/*
 * The card. rgba rather than a palette token colour-mixed against a
 * background — this one is meant to stay translucent over whatever the
 * photograph behind it is doing, not resolve to one flat colour the way
 * color-mix() against a known background would (CLAUDE.md section 5's
 * token rule assumes a flat destination; there isn't one here on purpose).
 *
 * Short and horizontal as of Adendo 9: icon, text and a trailing arrow on
 * one row rather than icon-above-text, so `align-items: center` (not
 * `flex-start`, what the taller card used) keeps all three on the same
 * baseline.
 */
/*
 * Icon+text centred as a group in all three cards (06/out/2026) — first
 * tried scoped to "Fast & Reliable Delivery" alone, whose shorter note made
 * it read as off-balance left-aligned next to its two siblings; extended to
 * all three the same day so the row reads as one consistent treatment
 * rather than one card styled differently from the other two.
 *
 * `justify-content: center` alone does not do it: `.petpartners-hero-
 * promises__text` used to grow to fill the row (flex: 1 1 auto) so its copy
 * sat flush against the icon, and centring the row would do nothing while
 * that is still true — the text block would still claim the full remaining
 * width. Shrinking it to its own content width (flex: 0 1 auto) is what
 * lets the icon+text pair centre as a group. The title and note stay left-
 * aligned to EACH OTHER inside that block — no text-align: center here —
 * only the pair's position in the card changed, not how the two lines of
 * copy read against one another.
 */
.petpartners-hero-promises__item {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var( --wp--preset--spacing--xs );
	min-block-size: 90px;
	/* Literal `#fbf9f7`, client colour, 13/out/2026 — no matching token in the
	   palette (CLAUDE.md 5.1/5.2), same "flag rather than invent" reasoning
	   already given for the icon circle just below. */
	background-color: #fbf9f7;
	border-radius: 12px;
	padding: 16px 24px;
}

/*
 * The light circle the icon sits in. `surface` rather than a literal
 * "beige" the client asked for by name — the palette (CLAUDE.md 5.1/5.2)
 * has no beige token, and surface is the closest thing it has to a light
 * neutral card fill. Flagged here rather than inventing a hex off-palette.
 */
.petpartners-hero-promises__icon {
	display: flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	inline-size: 56px;
	block-size: 56px;
	border-radius: 50%;
	background-color: var( --wp--preset--color--surface );
}

/*
 * Unchanged mechanism from the old hero's badge: a mask, not a picture
 * (CLAUDE.md section 6). The grid shrank from 64px to 32px so the drawing
 * sits inside the new 56px circle with room around it — proportional, so
 * the mask-size/width ratio each icon was drawn at is untouched. Raised to
 * 35px (06/out/2026, client asked for 10% larger — 32 × 1.1 = 35.2,
 * rounded) with room to spare in the 56px circle either way.
 */
.petpartners-hero-promises__badge {
	--petpartners-badge-grid: 35px;

	inline-size: 35px;
	block-size: 35px;
	-webkit-mask-image: var( --petpartners-badge-mask );
	mask-image: var( --petpartners-badge-mask );
	-webkit-mask-repeat: no-repeat;
	mask-repeat: no-repeat;
	-webkit-mask-position: center;
	mask-position: center;
	-webkit-mask-size: auto var( --petpartners-badge-grid );
	mask-size: auto var( --petpartners-badge-grid );
	background-color: var( --wp--preset--color--purple );
}

/*
 * The colour-icon sibling of .petpartners-hero-promises__badge — same box,
 * an <img> instead of a mask, because this tier's whole point is to keep the
 * artwork's own shading rather than flatten it to a silhouette (see the
 * render.php comment at the call site). 35px for the same reason as the
 * mask above — matches the width/height attributes on the <img> itself, so
 * raising one without the other never has to happen.
 */
.petpartners-hero-promises__badge-image {
	display: block;
	inline-size: 35px;
	block-size: 35px;
	object-fit: contain;
}

.petpartners-hero-promises__text {
	display: flex;
	flex: 0 1 auto;
	flex-direction: column;
	justify-content: center;
	min-inline-size: 0;
}

.petpartners-hero-promises__title {
	color: var( --wp--preset--color--brown );
	font-size: var( --wp--preset--font-size--base );
	font-weight: 600;
	line-height: var( --wp--custom--typography--base--line-height );
}

/*
 * text-muted, not text-muted-strong: the old hero needed the darker, over-
 * photograph-safe token here because this row stood on a picture. It stands
 * on a plain card fill now, so the ordinary secondary-text token is enough.
 *
 * One line, truncated rather than wrapped: the card's own height is fixed
 * (~90px) to read as short and horizontal, and the copy itself is
 * unchanged (CLAUDE.md section 2, rule 9 — a visual ellipsis is not a
 * rewrite). At the row's current width none of the three notes are long
 * enough to actually clip; this is a floor against a future, longer one.
 */
.petpartners-hero-promises__note {
	overflow: hidden;
	color: var( --wp--preset--color--text-muted );
	font-size: var( --wp--preset--font-size--sm );
	line-height: var( --wp--custom--typography--sm--line-height );
	white-space: nowrap;
	text-overflow: ellipsis;
}

/*
 * No divider at either tier below (ADR-063, Adendo 6 removes the gray-light
 * hairlines the card treatment replaces) — a card's own edge is the
 * separation now, a border-inline-start between two cards with a gap and a
 * fill colour of their own would be a seam nobody asked for.
 */
@media ( min-width: 782px ) {

	.petpartners-hero-promises__list {
		display: grid;
		grid-template-columns: repeat( 2, 1fr );
		gap: 16px;
	}
}

@media ( min-width: 1024px ) {

	.petpartners-hero-promises__list {
		grid-template-columns: repeat( 3, 1fr );
	}
}

/*
 * Meet Pet+ Partners (About Us intro) — replaces "How this store works" in
 * the same slot (ADR-077, 12/out/2026). Photo left, copy right, from a
 * client-supplied reference layout.
 *
 * Three literal colours have no existing token to reuse. `#fbf9f7` (since
 * 13/out/2026, client colour — replaces the earlier `#FAF6F3` warm off-white
 * this section shared with `.petpartners-testimonials-section` and
 * `.petpartners-cta-band`, same reasoning: closer to the client's reference
 * than `surface` (`#F7F6F6`), which reads as neutral grey rather than warm.
 * `#F1EAE4` (the watermark, below) is the same family, one step darker — a
 * visible-but-quiet step up from the section background, not a token away
 * from it either. The icon circle's lavender tint is `color-mix()` against
 * the `purple` token rather than a third new hex: it stays correct
 * automatically if `purple` ever changes again (ADR-075 already changed it
 * once), the same reason a CSS custom property is preferred over a literal
 * everywhere else in this file.
 */
.petpartners-about-intro {
	position: relative;
	overflow: hidden;
	background-color: #fbf9f7;
}

/*
 * The watermark's own SVG markup is client-supplied, used exactly as given
 * (about-intro.php) — this is only the positioning, colour and sizing, same
 * division every other SVG in the pattern follows. Third attempt at this
 * shape (12/out/2026): the theme's existing filled `paw-watermark.svg`, then
 * an agency-redrawn arc-trick version, both read as loose circles instead of
 * a paw at this size. `color` carries the shape's fill — the markup paints
 * with `fill="currentColor"` — rather than opacity-on-a-darker-tone the
 * first two attempts used: a flat, slightly-darker-than-background beige
 * reads as a watermark on its own, without needing transparency to get
 * there. `#F1EAE4` has no token to reuse (closest, `surface`, is a cooler
 * grey built for card backgrounds, not this); z-index 0 sits it behind
 * `.petpartners-about-intro__inner` (z-index 1).
 */
.about-watermark {
	position: absolute;
	inset-block-start: 30px;
	inset-inline-end: 30px;
	z-index: 0;
	inline-size: 200px;
	block-size: 200px;
	color: #f1eae4;
	opacity: 1;
	transform: rotate( 18deg );
	pointer-events: none;
}

/*
 * Hidden below 900px (14/out/2026, client request) — the watermark reads
 * fine behind the wider two-column desktop layout it was sized for, but
 * on the single-column mobile stack (photo, then a now-tighter copy
 * block, ADR-086's Adendo above) there was no longer room for it to sit
 * without crowding the text under the photo.
 */
@media ( max-width: 899px ) {
	.about-watermark {
		display: none;
	}
}

/*
 * No padding-block here — `.petpartners-section` (the outer wrapper)
 * already applies it. The first build added a second, smaller padding-block
 * on top of that one and the two stacked, leaving far more empty space above
 * the eyebrow than the reference has. One padding source, not two.
 */
/*
 * `padding-inline` here, not left to `.alignwide` alone (14/out/2026) —
 * same gap `.petpartners-hero-promises .petpartners-section__inner` and
 * `product-showcase`'s own inner already work around: at narrow (mobile)
 * widths the global `.is-layout-constrained > .alignwide` rule this class
 * leans on was not actually padding the row away from the viewport edge,
 * so the photo, title and copy sat flush against it.
 */
/*
 * `grid-template-columns: minmax(0, 1fr)` below 900px (14/out/2026) — with
 * no explicit column track, this grid's one implicit column defaulted to
 * `auto`, sized to the widest item's own CONTENT width rather than the
 * container's available width. The photo's intrinsic size was wider than
 * that available width, so the column — and the photo inside it —
 * overflowed past the padding on the right (confirmed by
 * `getBoundingClientRect()`: the image's right edge landed past the
 * viewport itself, while the left edge sat correctly at the padding
 * boundary — a one-sided overflow, not a centring problem). `minmax(0,
 * 1fr)` pins the column to the grid's own content box instead of letting
 * content dictate it, the same fix already applied to the header bar's
 * grid earlier the same day for the same class of bug.
 */
.petpartners-about-intro__inner {
	position: relative;
	z-index: 1;
	display: grid;
	grid-template-columns: minmax( 0, 1fr );
	gap: var( --wp--preset--spacing--xl );
	padding-inline: var( --wp--preset--spacing--lg );
}

/*
 * Tighter gap below 900px (14/out/2026, client request) — the client asked
 * for the copy block to sit closer to the photo above it on mobile than
 * the `xl` gap this grid uses everywhere else gives. `sm`, not a literal:
 * still a real spacing step on the scale, just a smaller one, scoped to
 * this one breakpoint rather than changing the token every other width
 * already relies on.
 */
@media ( max-width: 899px ) {
	.petpartners-about-intro__inner {
		gap: var( --wp--preset--spacing--sm );
	}
}

/*
 * `align-items` left at its default (stretch), not `center`: the brief asks
 * for the photo to run the section's full height, which only happens if the
 * grid row's two columns are the same height and the image fills its own
 * column via `object-fit: cover` below. Centring the row would shrink the
 * photo item to its own content height instead.
 */
@media ( min-width: 900px ) {
	.petpartners-about-intro__inner {
		grid-template-columns: 45fr 55fr;
		gap: var( --wp--preset--spacing--xxl );
	}

	.petpartners-about-intro__content {
		align-self: center;
	}
}

.petpartners-about-intro__photo {
	margin: 0;
	block-size: 100%;
}

.petpartners-about-intro__photo img {
	display: block;
	inline-size: 100%;
	block-size: 100%;
	min-block-size: 320px;
	max-block-size: 640px;
	object-fit: cover;
	border-radius: var( --wp--preset--border-radius--xl );
	box-shadow: var( --wp--preset--shadow--md );
}

@media ( min-width: 900px ) {
	.petpartners-about-intro__photo img {
		max-block-size: none;
	}
}

/*
 * Mobile's photo (about-pets-mobile.webp, 14/out/2026) is not a plain
 * rectangle the way the desktop photo above is — the file itself already
 * carries its own rounded top corners and wave-cut bottom edge, baked in
 * as alpha transparency, the same wave motif the hero photo's `clip-path`
 * draws on top of a plain rectangle elsewhere in this file. Applying the
 * desktop rule's `object-fit: cover` to an already-shaped file crops and
 * rescales that baked-in shape unpredictably against this box's own
 * aspect ratio — confirmed by eye, the wave came out compressed and
 * misplaced compared to the client's reference. `contain`, no forced
 * block-size and no second border-radius/shadow let the file's own shape
 * render exactly as delivered instead of being reshaped on top of itself.
 */
@media ( max-width: 899px ) {
	.petpartners-about-intro__photo img {
		block-size: auto;
		min-block-size: 0;
		max-block-size: none;
		object-fit: contain;
		border-radius: 0;
		box-shadow: none;
	}
}

.petpartners-about-intro__eyebrow {
	display: flex;
	align-items: center;
	gap: var( --wp--preset--spacing--xs );
	margin: 0 0 var( --wp--preset--spacing--sm );
	color: var( --wp--preset--color--purple );
	font-size: var( --wp--preset--font-size--label );
	font-weight: 600;
	line-height: var( --wp--custom--typography--label--line-height );
	letter-spacing: var( --wp--custom--typography--label--letter-spacing );
	text-transform: uppercase;
}

.petpartners-about-intro__eyebrow-line {
	display: inline-block;
	flex: 0 0 auto;
	inline-size: 32px;
	block-size: 1px;
	background-color: var( --wp--preset--color--gray-light );
}

.petpartners-about-intro__title {
	margin: 0;
}

.petpartners-about-intro__title-line--brown {
	color: var( --wp--preset--color--brown );
}

.petpartners-about-intro__title-line--purple {
	color: var( --wp--preset--color--purple );
}

/*
 * Serif, like the display headings, but at the `heading` token's size
 * (24px) — the family/size pairing CLAUDE.md 5.5's table does not name
 * because nothing needed it before this section. A serif subhead this size
 * reads as a continuation of the title line above it, which is the point:
 * three lines of decreasing weight, one idea.
 */
.petpartners-about-intro__subtitle {
	margin: var( --wp--preset--spacing--sm ) 0 0;
	color: var( --wp--preset--color--brown );
	font-family: var( --wp--preset--font-family--serif-display );
	font-size: var( --wp--preset--font-size--heading );
	font-weight: 600;
	line-height: var( --wp--custom--typography--heading--line-height );
}

.petpartners-about-intro__lead {
	margin: var( --wp--preset--spacing--sm ) 0 0;
	max-width: var( --wp--custom--measure--text );
	color: var( --wp--preset--color--text-gray );
}

.petpartners-about-intro__features {
	display: grid;
	gap: var( --wp--preset--spacing--lg );
	margin: var( --wp--preset--spacing--lg ) 0 0;
	padding: 0;
	list-style: none;
}

@media ( min-width: 600px ) {
	.petpartners-about-intro__features {
		grid-template-columns: repeat( 3, 1fr );
	}
}

/*
 * No circle any more (client revision, 12/10) — the lavender badge
 * (background-colour, border-radius, the fixed 76px box that centred the
 * icon inside it) is gone, on the client's instruction that it read as
 * mismatched now that two of the three icons carry their own colour. The
 * icon sits loose on the section background, flush left with the title
 * below it: with no fixed-width box forcing `justify-content: center`, the
 * image's own left edge is the span's left edge, which is the card's own
 * left edge. `color` stays — it is dead for the three images in place
 * today (they carry their own colour, not currentColor) but still feeds
 * the dormant outline-SVG branch in about-intro.php's render loop if that
 * ever comes back for one of them.
 */
.petpartners-about-intro__feature-icon {
	display: block;
	margin-bottom: var( --wp--preset--spacing--sm );
	color: var( --wp--preset--color--purple );
}

/*
 * 72px, up from the 52px the icon was capped at while it had a 76px circle
 * around it — with the circle gone, the icon itself carries the badge's old
 * visual footprint instead of the padding doing it (client revision, 12/10).
 */
.petpartners-about-intro__feature-icon-image {
	display: block;
	max-inline-size: 72px;
	max-block-size: 72px;
	inline-size: auto;
	block-size: auto;
}

.petpartners-about-intro__feature-title {
	margin: 0 0 var( --wp--preset--spacing--xs );
	color: var( --wp--preset--color--brown );
	font-family: var( --wp--preset--font-family--serif-display );
	font-size: var( --wp--preset--font-size--heading );
	font-weight: 600;
	line-height: var( --wp--custom--typography--heading--line-height );
}

.petpartners-about-intro__feature-text {
	margin: 0;
	color: var( --wp--preset--color--text-muted );
	font-size: var( --wp--preset--font-size--sm );
	line-height: var( --wp--custom--typography--sm--line-height );
}

.petpartners-about-intro__footer {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var( --wp--preset--spacing--md );
	margin-top: var( --wp--preset--spacing--lg );
}

/*
 * The hero CTA's own gradient (site-chrome.css, `.petpartners-hero__actions
 * .wp-block-button__link`), copied verbatim onto a pill radius — third reuse
 * of the same two stops, after the footer background (ADR-061 Adendo 2).
 * Literal `#4d2096` for the same reason as both of those: matching an
 * existing one-off exception rather than opening a fourth one.
 */
.petpartners-about-intro__cta .wp-block-button__link {
	display: inline-flex;
	align-items: center;
	gap: var( --wp--preset--spacing--xs );
	border-radius: var( --wp--preset--border-radius--pill );
	background-color: #4d2096;
	background-image: linear-gradient( 90deg, var( --wp--preset--color--purple-hover ), #4d2096 );
	box-shadow: var( --wp--preset--shadow--sm );
}

.petpartners-about-intro__cta .wp-block-button__link:hover,
.petpartners-about-intro__cta .wp-block-button__link:focus-visible {
	background-image: none;
	background-color: #411b7f;
}

/*
 * Shares a row with the "Learn More" button (client revision, 12/10) rather
 * than sitting underneath it — `.petpartners-about-intro__footer`'s flex-wrap
 * puts it on its own full-width row only once it no longer fits beside the
 * button, which is what gives the mobile stack for free. 11px / regular
 * weight is deliberately lighter than `is-style-label` (13px / 600) — see
 * the file docblock for why this stopped using that shared variation.
 * `white-space: nowrap` keeps the sentence from breaking mid-line while
 * there IS room for it; once there isn't, the whole line moves to the next
 * row instead.
 */
.petpartners-about-intro__tagline {
	display: flex;
	flex: 1 1 auto;
	align-items: center;
	gap: var( --wp--preset--spacing--xs );
	margin: 0;
	color: var( --wp--preset--color--text-muted );
	font-size: 11px;
	font-weight: 400;
	letter-spacing: var( --wp--custom--typography--label--letter-spacing );
	text-transform: uppercase;
	white-space: nowrap;
}

/*
 * Hidden below 900px (14/out/2026, client request) — "Healthier Pets.
 * Happier Moments. Better Days Together." sits beside the "Learn More"
 * button on desktop, where there is a wide row to share; stacked on
 * mobile it was just one more line of small print under the button, not
 * something the client wanted kept there.
 */
@media ( max-width: 899px ) {
	.petpartners-about-intro__tagline {
		display: none;
	}
}

.petpartners-about-intro__tagline-line {
	display: inline-block;
	flex: 0 0 auto;
	inline-size: 24px;
	block-size: 1px;
	background-color: var( --wp--preset--color--border );
}

@media ( prefers-reduced-motion: reduce ) {
	.petpartners-about-intro__cta .wp-block-button__link {
		transition: none;
	}
}

/* --- Product archive -------------------------------------------------- */

.petpartners-archive__head {
	padding-block: var( --wp--preset--spacing--md ) var( --wp--preset--spacing--sm );
}

.petpartners-archive__head .wc-block-breadcrumbs {
	margin-bottom: var( --wp--preset--spacing--sm );
	color: var( --wp--preset--color--text-muted );
}

.petpartners-archive__head .wp-block-query-title {
	margin-block: 0 var( --wp--preset--spacing--xs );
}

.petpartners-archive__description {
	margin-block: 0 var( --wp--preset--spacing--sm );
	max-width: var( --wp--custom--measure--text );
}

/*
 * The result count is a live region — WooCommerce gives it role="status" — so
 * it is set quietly and kept where a filter change will be announced from
 * (P7b).
 */
.petpartners-archive .woocommerce-result-count {
	margin: 0;
	color: var( --wp--preset--color--text-muted );
	font-size: var( --wp--preset--font-size--sm );
	font-variant-numeric: tabular-nums;
}

.petpartners-archive__body {
	padding-block: var( --wp--preset--spacing--md ) var( --wp--preset--spacing--xl );
}

/*
 * The grid.
 *
 * Two columns on a phone, three from the tablet breakpoint, four from the wide
 * one. It is defined here rather than in the card, because a card should not
 * know how many of it there are — the same card is used by the archives and by
 * whatever P8 puts in a cross-sell row.
 *
 * The selector carries .petpartners-archive so it outranks the responsive
 * column rules WooCommerce ships with product-template, which are written
 * against its own columns-N class.
 */
.petpartners-archive .wc-block-product-template {
	display: grid;
	grid-template-columns: repeat( 2, minmax( 0, 1fr ) );
	gap: var( --wp--preset--spacing--md ) var( --wp--preset--spacing--sm );
	margin: 0;
	padding: 0;
	list-style: none;
}

.petpartners-archive .wc-block-product {
	/* The card fills its cell, so a row of uneven names keeps a common baseline
	   for the price, which is pinned to the bottom of the card. */
	display: flex;
	margin: 0;
}

.petpartners-archive .wc-block-product > * {
	width: 100%;
}

@media ( min-width: 768px ) {
	.petpartners-archive .wc-block-product-template {
		grid-template-columns: repeat( 3, minmax( 0, 1fr ) );
		gap: var( --wp--preset--spacing--lg ) var( --wp--preset--spacing--md );
	}
}

@media ( min-width: 1100px ) {
	.petpartners-archive .wc-block-product-template {
		grid-template-columns: repeat( 4, minmax( 0, 1fr ) );
	}
}

/*
 * Filters beside results.
 *
 * One column on a phone, where the panel is a drawer and the grid has the page
 * to itself. A sidebar from the desktop breakpoint, which is the same width the
 * category menu switches at — one breakpoint for the whole site chrome.
 */
.petpartners-archive__layout {
	display: grid;
	grid-template-columns: 1fr;
	gap: var( --wp--preset--spacing--md );
	align-items: start;
}

.petpartners-archive__results {
	min-width: 0;
}

/*
 * The result count is the focus target after a filtered load, so it has to
 * show a focus ring — it is not otherwise interactive, and an outline that
 * appears on a paragraph is exactly the confirmation the visitor needs that
 * this is the part that answered them.
 */
.petpartners-archive .woocommerce-result-count:focus-visible {
	outline: 2px solid var( --wp--preset--color--purple );
	outline-offset: 2px;
}

@media ( min-width: 900px ) {
	.petpartners-archive__layout {
		grid-template-columns: 15rem minmax( 0, 1fr );
		gap: var( --wp--preset--spacing--lg );
	}

	.petpartners-archive__layout .wp-block-petpartners-product-filters {
		position: sticky;
		top: var( --wp--preset--spacing--sm );
	}
}

/* --- Product page ------------------------------------------------------ */

.petpartners-product__head {
	padding-block: var( --wp--preset--spacing--md ) var( --wp--preset--spacing--sm );
}

.petpartners-product__head .wc-block-breadcrumbs {
	color: var( --wp--preset--color--text-muted );
}

.petpartners-product__top {
	padding-bottom: var( --wp--preset--spacing--lg );
}

/*
 * Gallery and purchase side by side from the desktop breakpoint; stacked below
 * it, gallery first. On a phone the page is long, and that is fine precisely
 * because the purchase area is at the top of it.
 */
.petpartners-product__layout {
	display: grid;
	grid-template-columns: 1fr;
	gap: var( --wp--preset--spacing--md );
	align-items: start;
}

@media ( min-width: 900px ) {
	.petpartners-product__layout {
		grid-template-columns: minmax( 0, 1fr ) minmax( 0, 1fr );
		gap: var( --wp--preset--spacing--lg );
	}
}

/* --- Pagination ------------------------------------------------------- */

.petpartners-archive__pagination {
	margin-top: var( --wp--preset--spacing--xl );
	gap: var( --wp--preset--spacing--xxs );
	align-items: center;
}

/*
 * Every page number is a target under a thumb, so each one takes the full box
 * rather than leaning on WCAG 2.2's spacing exception.
 */
.petpartners-archive__pagination .page-numbers,
.petpartners-archive__pagination .wp-block-query-pagination-previous,
.petpartners-archive__pagination .wp-block-query-pagination-next {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: var( --wp--custom--chrome--tap-target );
	min-height: var( --wp--custom--chrome--tap-target );
	padding-inline: var( --wp--preset--spacing--xxs );
	border-radius: var( --wp--preset--border-radius--md );
	font-variant-numeric: tabular-nums;
	text-decoration: none;
}

.petpartners-archive__pagination a:hover {
	background-color: var( --wp--preset--color--surface );
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

/*
 * The current page is not a link, so it cannot rely on the link colour. It
 * carries aria-current="page" for a screen reader and a filled chip for
 * everyone else — two signals, never colour alone.
 */
.petpartners-archive__pagination [aria-current="page"] {
	background-color: var( --wp--preset--color--brown );
	color: var( --wp--preset--color--white );
	font-weight: 600;
}

/* --- Empty state ------------------------------------------------------ */

.petpartners-empty-state {
	padding-block: var( --wp--preset--spacing--lg ) var( --wp--preset--spacing--xl );
}

.petpartners-empty-state > .wp-block-heading {
	margin-block: 0 var( --wp--preset--spacing--xs );
}

.petpartners-empty-state > p {
	max-width: var( --wp--custom--measure--text );
}

.petpartners-empty-state .wp-block-buttons {
	margin-block: var( --wp--preset--spacing--md ) var( --wp--preset--spacing--xl );
}

/* --- Base patterns ---------------------------------------------------- */

/*
 * Extra bottom padding only — the footer's own `petpartners/footer-pets`
 * block overlaps up into this section by design (its docblock has the full
 * mechanism), and without this the photo would sit on top of the "Help Me
 * Choose" button instead of above it.
 *
 * 280px/150px covers 90.9% of the photo's own 304px/163px desktop/mobile
 * height — the fraction of the image that sits ABOVE the footer's top
 * border, body included, since only the paws (the remaining 9.1%) are meant
 * to dip into the purple (12/out/2026 revision; `footer-pets/style.css` has
 * the alpha measurement the 9.1% comes from) — plus a line of breathing
 * room, on top of the section's normal `padding-block`, not instead of it —
 * same "one padding source stacks, don't invent a second" lesson
 * about-intro.php's own docblock already names, applied here by adding to the
 * existing source rather than introducing a competing one.
 *
 * `background-color` is `#fbf9f7` (client colour, 13/out/2026) — was the
 * warm `#FAF6F3` about-intro.php and testimonials.php also used until the
 * same day, when the client gave this exact hex for the hero's three promise
 * cards (ADR-068 Adendo 8) and asked for it on this band too, along with
 * About Us and Testimonials — third reuse of the new literal, not a fourth
 * hex. `position: relative` is new: the two paw watermarks below are
 * `position: absolute` against it, same mechanism `.petpartners-about-intro`
 * and `.petpartners-testimonials-section` already give their own watermark
 * layers.
 */
.petpartners-cta-band {
	position: relative;
	padding-block: var( --wp--preset--spacing--xl );
	padding-block-end: calc( var( --wp--preset--spacing--xl ) + 280px );
	background-color: #fbf9f7;
}

@media ( max-width: 599px ) {
	.petpartners-cta-band {
		padding-block-end: calc( var( --wp--preset--spacing--xl ) + 150px );
	}
}

.petpartners-cta-band .wp-block-buttons {
	margin-top: var( --wp--preset--spacing--md );
}

/*
 * Eyebrow — same dash/label/dash shape as `.petpartners-about-intro__
 * eyebrow` and `.petpartners-testimonials-eyebrow`, centred like the
 * latter (this band has no left-aligned column to set an alignment from),
 * purple like the former — About and this closing band share the warm
 * cream background and the purple accent; Testimonials and the hero use
 * brown instead. Three eyebrows, two accent colours, both already true
 * before this one existed.
 */
.petpartners-cta-band__eyebrow {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var( --wp--preset--spacing--xs );
	margin: 0 0 var( --wp--preset--spacing--sm );
	color: var( --wp--preset--color--purple );
	font-size: var( --wp--preset--font-size--label );
	font-weight: 600;
	line-height: var( --wp--custom--typography--label--line-height );
	letter-spacing: var( --wp--custom--typography--label--letter-spacing );
	text-transform: uppercase;
}

.petpartners-cta-band__eyebrow-line {
	display: inline-block;
	flex: 0 0 auto;
	inline-size: 32px;
	block-size: 1px;
	background-color: var( --wp--preset--color--gray-light );
}

/*
 * Two-tone title, one line — the client's reference originally read as two
 * lines (`about-intro.php`'s own `display:flex;flex-direction:column`
 * mechanism was copied for it), but the client asked for one line instead
 * (13/out/2026). No flex needed for that: the two `<span class="...__title-
 * line">` elements are inline by default, so they sit side by side with a
 * single space between them (the literal whitespace already in the markup
 * between the two `</span>`/`<span>` tags) — only the colour split from each
 * span's own class stays.
 */
.petpartners-cta-band__title {
	margin: 0 0 var( --wp--preset--spacing--sm );
}

.petpartners-cta-band__title-line--purple {
	color: var( --wp--preset--color--purple );
}

.petpartners-cta-band__title-line--brown {
	color: var( --wp--preset--color--brown );
}

.petpartners-cta-band__lead {
	margin-inline: auto;
}

/*
 * The button's purple gradient — `.petpartners-hero__actions .wp-block-
 * button__link`'s own treatment (site-chrome.css has the original and the
 * contrast math), reused a third time after `.petpartners-about-intro__cta`.
 * Literal `#4d2096` for the same reason as both of those: matching an
 * existing one-off exception rather than opening a new one.
 */
.petpartners-cta-band__action .wp-block-button__link {
	display: inline-flex;
	align-items: center;
	gap: var( --wp--preset--spacing--xs );
	border-radius: var( --wp--preset--border-radius--pill );
	background-color: #4d2096;
	background-image: linear-gradient( 90deg, var( --wp--preset--color--purple-hover ), #4d2096 );
	box-shadow: var( --wp--preset--shadow--sm );
}

.petpartners-cta-band__action .wp-block-button__link:hover,
.petpartners-cta-band__action .wp-block-button__link:focus-visible {
	background-image: none;
	background-color: #411b7f;
}

.petpartners-cta-band__cta-arrow {
	display: inline-block;
}

/*
 * Flanking paw watermarks — the same client-supplied shape as
 * `.about-watermark` and `.testimonials-watermark` (both docblocks have the
 * provenance), same pale beige literal. Flanking the dog/cat photo rather
 * than the title — client swap, 13/out/2026: these (the larger of the two
 * paw sizes in this band) moved down to where `petpartners/footer-pets`'s
 * own smaller paws already sat, and those moved in closer to the photo in
 * turn (see that block's own style.css). `inset-block-end`, not `-start`:
 * the photo is anchored to the BOTTOM of this band (it dips into the
 * footer's purple border), so measuring from the bottom keeps these paws
 * level with it regardless of how tall the copy above ends up being — a
 * `-start` percentage would drift against the photo the moment the lead
 * paragraph wrapped to a different number of lines.
 */
.petpartners-cta-band__paw {
	position: absolute;
	inset-block-end: 260px;
	z-index: 0;
	inline-size: 120px;
	block-size: 120px;
	color: #f1eae4;
	pointer-events: none;
}

.petpartners-cta-band__paw--left {
	inset-inline-start: calc( 50% - 500px );
	transform: rotate( -18deg );
}

.petpartners-cta-band__paw--right {
	inset-inline-end: calc( 50% - 500px );
	transform: rotate( 14deg );
}

.petpartners-cta-band__eyebrow,
.petpartners-cta-band__title,
.petpartners-cta-band__lead,
.petpartners-cta-band__action {
	position: relative;
	z-index: 1;
}

/*
 * Tablet: the `calc(50% - 500px)` desktop offset would push these past a
 * ~980px-and-under viewport's own edge (500px is more than half of it) —
 * scaled down to match how much narrower the photo itself has already gone
 * (`max-inline-size: 90vw` on `.wp-block-petpartners-footer-pets__image`,
 * footer-pets/style.css) by this width. Not pixel-measured against the
 * photo the way the desktop offset is — same unverified-at-this-breakpoint
 * note the rest of this band's responsive behaviour already carries.
 */
@media ( max-width: 899px ) {
	.petpartners-cta-band__paw {
		inset-block-end: 170px;
		inline-size: 64px;
		block-size: 64px;
	}

	.petpartners-cta-band__paw--left {
		inset-inline-start: calc( 50% - 280px );
	}

	.petpartners-cta-band__paw--right {
		inset-inline-end: calc( 50% - 280px );
	}
}

@media ( max-width: 599px ) {
	.petpartners-cta-band__paw {
		display: none;
	}
}

/* --- Mini-cart drawer target size -----------------------------------------
 *
 * The same WCAG 2.2 AA 2.5.8 failure the cart page has, and the same cause:
 * WooCommerce's quantity stepper overflows its container and covers all but a
 * couple of pixels of the "Remove item" button next to it. The fix is the same
 * — size the stepper to its content so its buttons stay inside it.
 *
 * It lives here rather than in checkout.css because the drawer opens from the
 * header on every page of the site, and that stylesheet loads on two.
 */

.wc-block-components-drawer .wc-block-cart-item__quantity .wc-block-components-quantity-selector {
	flex: 0 0 auto;
	inline-size: auto;
	min-inline-size: max-content;
}

.wc-block-components-drawer .wc-block-cart-item__quantity .wc-block-cart-item__remove-link {
	min-block-size: 32px;
	min-inline-size: 32px;
}
