/*
Theme Name: Southern Drive Collective
Theme URI: https://github.com/jeremyHixon/southern-car-collective
Author: Jeremy Hixon
Author URI: https://jeremyhixon.com/
Description: Warm-minimal theme for Southern Drive Collective. Cream background, near-black text, one hot-orange accent, geometric sans, generous whitespace, one signature motion moment on entry. Implements docs/design-handoff.md.
Version: 0.7.1
Requires at least: 6.0
Requires PHP: 7.4
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: sdc
Tags: custom-menu, custom-logo, featured-images, translation-ready
*/

/* ==========================================================================
   Fonts — self-hosted (handoff §4).
   General Sans ships as four static instances. JetBrains Mono is a single
   variable file covering the 400–500 range, so it is declared once.
   ========================================================================== */

/* Light is used only by the wordmark's second word. Handoff §4 lists 400-700;
   the thin half of the lockup needs a weight below Regular to read as thin. */
@font-face {
	font-family: 'General Sans';
	src: url('assets/fonts/GeneralSans-Light.woff2') format('woff2');
	font-weight: 300;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: 'General Sans';
	src: url('assets/fonts/GeneralSans-Regular.woff2') format('woff2');
	font-weight: 400;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: 'General Sans';
	src: url('assets/fonts/GeneralSans-Medium.woff2') format('woff2');
	font-weight: 500;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: 'General Sans';
	src: url('assets/fonts/GeneralSans-Semibold.woff2') format('woff2');
	font-weight: 600;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: 'General Sans';
	src: url('assets/fonts/GeneralSans-Bold.woff2') format('woff2');
	font-weight: 700;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: 'JetBrains Mono';
	src: url('assets/fonts/JetBrainsMono-Variable.woff2') format('woff2');
	font-weight: 400 500;
	font-style: normal;
	font-display: swap;
}

/* ==========================================================================
   Tokens (handoff §3) — the locked palette. Do not add colors, shadows, or
   gradients that are not defined here.
   ========================================================================== */

:root {
	--bg: #FAF6EF;        /* warm off-white — page background */
	--ink: #171717;       /* near-black — all primary text */
	--accent: #FF5C00;    /* hot orange — CTAs and single accents only */
	--accent-hover: #E85200;
	--tan: #EFE6D4;       /* warm neutral — card fills, dividers */
	--blue: #BADDF0;      /* cool contrast — one supporting element, sparingly */
	--ink-60: rgba( 23, 23, 23, 0.60 ); /* secondary text */
	--ink-12: rgba( 23, 23, 23, 0.12 ); /* hairline borders */

	--sans: 'General Sans', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
	--mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

	--pad-section: clamp( 5rem, 10vw, 9rem ); /* top/bottom padding per section */
	--nav-h: 76px;
	--radius: 4px;        /* buttons; most other corners stay square */

	--wrap: 1320px;                          /* content max-width (§6) */
	--gutter: clamp( 1.25rem, 4vw, 3rem );   /* side padding (§6) */

	/*
	 * Reading measure for long-form page copy (§4). A length, not a `ch` value:
	 * `ch` resolves against each element's own font-size, so the same 60ch put
	 * body text at 559px and an h2 at 1560px — i.e. headings went uncapped.
	 * 35rem holds body copy at ~74 characters, the top of the comfortable band.
	 */
	--measure: 35rem;
}

/* ==========================================================================
   Reset
   ========================================================================== */

*,
*::before,
*::after {
	box-sizing: border-box;
}

* {
	margin: 0;
	padding: 0;
}

html {
	scroll-behavior: smooth;
}

body {
	background: var(--bg);
	color: var(--ink);
	font-family: var(--sans);
	font-weight: 400;
	line-height: 1.5;
	min-height: 100vh;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

img,
picture,
video,
canvas,
svg {
	display: block;
	max-width: 100%;
	height: auto;
}

input,
button,
textarea,
select {
	font: inherit;
	color: inherit;
}

/* ==========================================================================
   Accessibility
   ========================================================================== */

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 3px;
	border-radius: 2px;
}

.screen-reader-text {
	position: absolute !important;
	width: 1px;
	height: 1px;
	padding: 0;
	overflow: hidden;
	clip: rect( 1px, 1px, 1px, 1px );
	white-space: nowrap;
	border: 0;
}

.skip-link {
	position: absolute;
	top: -100px;
	left: var(--gutter);
	z-index: 100;
	background: var(--accent);
	color: #fff;
	padding: 0.7rem 1.25rem;
	border-radius: var(--radius);
	font-weight: 600;
	text-decoration: none;
	transition: top 0.2s ease;
}

.skip-link:focus {
	top: 1rem;
}

/* ==========================================================================
   Typography (§4)
   ========================================================================== */

h1,
h2,
h3,
h4 {
	font-weight: 700;
	letter-spacing: -0.025em;
	line-height: 1.1;
}

h1 {
	font-size: clamp( 2.25rem, 5vw, 3.75rem );
}

/* Section headings step down from the hero, same weight and negative tracking. */
h2 {
	font-size: clamp( 1.75rem, 4vw, 3rem );
	line-height: 1.06;
}

h3 {
	font-size: clamp( 1.35rem, 2.4vw, 1.9rem );
}

h4 {
	font-size: 1.15rem;
}

p,
li {
	font-size: 1rem;
	line-height: 1.5;
}

a {
	color: var(--ink);
	text-decoration-color: var(--ink-12);
	text-underline-offset: 0.2em;
	transition: text-decoration-color 0.2s ease;
}

a:hover {
	text-decoration-color: var(--ink);
}

/* Eyebrow / labels / small technical text. */
.mono {
	font-family: var(--mono);
	text-transform: uppercase;
	letter-spacing: 0.14em;
	font-size: 0.72rem;
	font-weight: 400;
	color: var(--ink-60);
}

/* ==========================================================================
   Layout rhythm (§6)
   ========================================================================== */

.container {
	max-width: var(--wrap);
	margin-inline: auto;
	padding-inline: var(--gutter);
}

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

/* The bar is fixed, so an anchored section must clear it or land underneath. */
.section[id] {
	scroll-margin-top: var(--nav-h);
}

.section + .section {
	border-top: 1px solid var(--ink-12);
}

.section-eyebrow {
	margin-bottom: clamp( 1rem, 2vw, 1.5rem );
}

.section-lead {
	margin-top: 1.25rem;
	font-size: clamp( 1.05rem, 1.6vw, 1.3rem );
	color: var(--ink-60);
	max-width: 44ch;
}

/*
 * How it works (§12). The statement line carries the weight so the paragraph
 * under it can stay quiet — the alternative, a numbered step list, is exactly
 * the densification §11 rules out.
 */
.how-statement {
	font-size: clamp( 1.3rem, 2.4vw, 1.75rem );
	font-weight: 600;
	line-height: 1.35;
	letter-spacing: -0.01em;
	max-width: 24ch;
}

/* Wider measure than .section-lead: this is a full paragraph, not a lead-in,
   and 44ch would run it into a narrow column. */
.how-body {
	max-width: 58ch;
}

.how-body strong {
	color: var(--ink);
	font-weight: 600;
}

/*
 * A statement line that is a full sentence rather than a three-beat phrase.
 * At the base 24ch the About page's "More than a garage…" line broke to five
 * ragged lines; this is the widest measure that still reads as a statement and
 * not as a paragraph set in the wrong size.
 */
.how-statement--wide {
	max-width: 32ch;
}

/* ==========================================================================
   Buttons (§7)

   Two rules, both learned the hard way:

   1. Never let a broad descendant `a` rule reach a button. `.nav-links a`
      outranks `.btn-primary` and turns white button text near-black. The nav
      guards with `a:not(.btn)`.

   2. Never style a bare `.btn-primary`. WordPress copies a menu item's CSS
      classes onto its <li>, so `.btn-primary` alone also matches that <li> and
      paints a full-height orange block behind the real button. Every modifier
      below is therefore chained to `.btn`, which only ever lands on the <a>.
   ========================================================================== */

.btn {
	font-family: var(--sans);
	font-weight: 600;
	font-size: 0.9rem;
	border: none;
	cursor: pointer;
	text-decoration: none;
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	border-radius: var(--radius);
	transition: transform 0.15s ease, background 0.2s ease;
}

.btn.btn-primary,
.btn.btn-primary:hover,
.btn.btn-primary:focus,
.btn.btn-primary:visited {
	background: var(--accent);
	color: #fff;
	padding: 0.7rem 1.25rem;
	text-decoration: none;
}

.btn.btn-primary:hover {
	background: var(--accent-hover);
	transform: translateY( -1px );
}

.btn.btn-ghost,
.btn.btn-ghost:hover,
.btn.btn-ghost:visited {
	background: transparent;
	color: var(--ink);
	padding: 0.7rem 0;
	position: relative;
	text-decoration: none;
}

.btn.btn-ghost::after {
	content: "";
	position: absolute;
	left: 0;
	bottom: 0.35rem;
	width: 100%;
	height: 1px;
	background: var(--ink);
	transform: scaleX( 0 );
	transform-origin: left;
	transition: transform 0.25s ease;
}

.btn.btn-ghost:hover::after {
	transform: scaleX( 1 );
}

/* ==========================================================================
   Nav (§7)
   ========================================================================== */

.nav {
	position: fixed;
	inset: 0 0 auto 0;
	height: var(--nav-h);
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding-inline: var(--gutter);
	z-index: 50;
	background: transparent;
	border-bottom: 1px solid transparent;
	transition: background 0.35s ease, border-color 0.35s ease;
}

.nav.solid {
	background: rgba( 250, 246, 239, 0.92 );
	backdrop-filter: blur( 8px );
	-webkit-backdrop-filter: blur( 8px );
	border-bottom: 1px solid var(--ink-12);
}

/* Interior pages have no hero to sit over, so the bar is solid immediately. */
body:not(.home) .nav {
	background: rgba( 250, 246, 239, 0.92 );
	backdrop-filter: blur( 8px );
	-webkit-backdrop-filter: blur( 8px );
	border-bottom: 1px solid var(--ink-12);
}

/*
 * Wordmark: a heavy first half in ink, a thin second half in --ink-60, both
 * uppercase. The two halves are separate spans so the weight and tracking can
 * diverge; header.php derives the split from the site name.
 */
/*
 * Scales down on narrow screens. Uppercase plus tracking makes the lockup ~292px
 * at full size, and `nowrap` keeps it on one line — at 320px that pushed the menu
 * toggle off the right edge of the viewport, where it could not be tapped.
 */
.brand {
	font-size: clamp( 0.9rem, 4.4vw, 1.15rem );
	line-height: 1;
	text-transform: uppercase;
	color: var(--ink);
	text-decoration: none;
	display: flex;
	align-items: baseline;
	gap: 0.45em;
	white-space: nowrap;
}

.brand:hover {
	text-decoration: none;
}

.brand-strong {
	font-weight: 700;
	letter-spacing: -0.01em;
	color: var(--ink);
}

/* Looser tracking keeps the light weight from looking cramped beside the bold. */
.brand-light {
	font-weight: 300;
	letter-spacing: 0.06em;
	color: var(--ink-60);
}

.nav-links {
	display: flex;
	align-items: center;
	gap: clamp( 1.25rem, 2.5vw, 2.5rem );
	list-style: none;
}

/* :not(.btn) keeps this off the nav CTA — see the §7 guardrail. */
.nav-links a:not(.btn) {
	color: var(--ink);
	text-decoration: none;
	font-size: 0.95rem;
	font-weight: 500;
	opacity: 0.82;
	transition: opacity 0.2s ease;
}

.nav-links a:not(.btn):hover,
.nav-links .current-menu-item > a:not(.btn) {
	opacity: 1;
}

/*
 * Header cart. Sits after the links, so the links take the free space and push
 * the cart (and the mobile toggle) to the right edge. Ink, not orange: the nav
 * CTA stays the bar's one accent (§5).
 */
.nav-links {
	margin-left: auto;
}

.nav-cart {
	position: relative;
	display: inline-flex;
	flex-shrink: 0;
	align-items: center;
	margin-left: clamp( 1.25rem, 2.5vw, 2.5rem );
	padding: 8px;
	color: var(--ink);
	text-decoration: none;
}

.nav-cart[hidden] {
	display: none;
}

.nav-cart:hover {
	text-decoration: none;
}

.nav-cart-count {
	position: absolute;
	top: 0;
	right: -2px;
	min-width: 1.15rem;
	height: 1.15rem;
	padding-inline: 0.25rem;
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--ink);
	color: var(--bg);
	border-radius: var(--radius);
	font-size: 0.68rem;
	font-weight: 700;
	line-height: 1;
	font-variant-numeric: tabular-nums;
}

.menu-toggle {
	display: none;
	background: transparent;
	border: none;
	cursor: pointer;
	flex-direction: column;
	gap: 5px;
	padding: 8px;
}

/* Only the three decorative bars — not the screen-reader label span. */
.menu-toggle span[aria-hidden="true"] {
	width: 24px;
	height: 2px;
	background: var(--ink);
	transition: transform 0.3s ease, opacity 0.3s ease;
}

/* Collapse the bars into a close (×) mark while the drawer is open. */
.menu-toggle[aria-expanded="true"] span[aria-hidden="true"]:nth-of-type(1) {
	transform: translateY( 7px ) rotate( 45deg );
}

.menu-toggle[aria-expanded="true"] span[aria-hidden="true"]:nth-of-type(2) {
	opacity: 0;
}

.menu-toggle[aria-expanded="true"] span[aria-hidden="true"]:nth-of-type(3) {
	transform: translateY( -7px ) rotate( -45deg );
}

/* ==========================================================================
   Hero (§7) + intro animation (§8)

   Progressive enhancement: the resolved headline and supporting content are
   visible by default. Only when JS is present AND the intro has not already
   run this session do they start hidden and get revealed by intro.js. With
   JS off, or reduced motion, or on a return visit, the page renders resolved.
   ========================================================================== */

.hero {
	min-height: 100vh;
	padding: calc( var(--nav-h) + clamp( 2rem, 6vw, 4rem ) ) var(--gutter) clamp( 2rem, 5vw, 3.5rem );
	display: flex;
	flex-direction: column;
	justify-content: center;
	position: relative;
	max-width: var(--wrap);
	margin-inline: auto;
}

/*
 * The hero's upper block: text on the left, brand lockup on the right, centered
 * against each other. The copy leads; the logo is the secondary signature. On
 * narrow screens this stacks (see the 760px block) with the logo below the text.
 */
.hero-top {
	display: flex;
	align-items: center;
	gap: clamp( 2rem, 6vw, 4.5rem );
}

/* min-width:0 lets the text column shrink so a long headline wraps instead of
   pushing the logo off the row. */
.hero-body {
	flex: 1 1 auto;
	min-width: 0;
}

/*
 * Sized by width and capped so the three-line grunge lockup reads as a mark,
 * not a banner, and never out-weighs the headline. It stays visible through the
 * intro; only the headline, sub, CTA, and pillars are held for the reveal.
 */
.hero-logo {
	flex: 0 0 auto;
	display: block;
	width: clamp( 13rem, 26vw, 23rem );
	height: auto;
}

.hero-eyebrow {
	margin-bottom: clamp( 1.5rem, 3vw, 2.25rem );
}

/* Headline stage: the intro slogan and the resolved headline share the space. */
.stage {
	position: relative;
	min-height: clamp( 9rem, 26vw, 20rem );
}

.headline {
	font-weight: 700;
	font-size: clamp( 2.75rem, 7vw, 6.5rem );
	line-height: 1.02;
	letter-spacing: -0.025em;
	max-width: 16ch;
}

.headline .accent {
	color: var(--accent);
}

/* The intro overlay only exists once JS has opted in. */
.intro {
	display: none;
	position: absolute;
	inset: 0;
	flex-wrap: wrap;
	align-items: flex-start;
	/*
	 * Pack the wrapped lines at the top instead of letting them stretch to fill
	 * the (taller) stage. Without this the default align-content:stretch spreads
	 * "Drive. Build." and "Belong." apart, so the intro's line spacing no longer
	 * matches the resolved <h1>, which is tight-leading block text.
	 */
	align-content: flex-start;
	gap: 0 0.35em;
}

.js:not(.intro-done) .intro {
	display: flex;
}

/*
 * Scoped to .hero on purpose. `.intro-done` is only ever set by the front
 * page's intro script, so an unscoped `.pillars` rule leaves the same block
 * permanently invisible anywhere else it is reused — the About page renders
 * the slogan with this markup and would have shipped a blank row.
 */
.js:not(.intro-done) .headline-final,
.js:not(.intro-done) .hero-sub,
.js:not(.intro-done) .hero-cta,
.js:not(.intro-done) .hero .pillars {
	opacity: 0;
	transform: translateY( 12px );
}

.headline-final {
	transition: opacity 0.7s ease 0.05s, transform 0.7s ease 0.05s;
}

.intro.done {
	pointer-events: none;
}

.intro-word {
	font-weight: 700;
	font-size: clamp( 2.75rem, 7vw, 6.5rem );
	line-height: 1.02;
	letter-spacing: -0.025em;
	opacity: 0;
	transform: translateY( 30px );
	transition: opacity 0.5s cubic-bezier( 0.2, 0.7, 0.2, 1 ), transform 0.5s cubic-bezier( 0.2, 0.7, 0.2, 1 );
}

.intro-word.in {
	opacity: 1;
	transform: none;
}

/* The third word carries the accent (§8). */
.intro-word:nth-child(3) {
	color: var(--accent);
}

.intro.out .intro-word {
	opacity: 0;
	transform: translateY( -18px );
	transition: opacity 0.45s ease, transform 0.45s ease;
}

.hero-sub {
	margin-top: clamp( 1.5rem, 3vw, 2.25rem );
	font-size: clamp( 1.05rem, 1.6vw, 1.3rem );
	color: var(--ink-60);
	max-width: 44ch;
	transition: opacity 0.6s ease 0.15s, transform 0.6s ease 0.15s;
}

.hero-cta {
	margin-top: clamp( 1.75rem, 3.5vw, 2.75rem );
	display: flex;
	align-items: center;
	gap: 1.75rem;
	flex-wrap: wrap;
	transition: opacity 0.6s ease 0.25s, transform 0.6s ease 0.25s;
}

/*
 * The reveal works by removing `.intro-done` from the <html> element, not by
 * adding a class here. A `.headline-final.show` rule (0,2,0) would lose to the
 * hiding rule `.js:not(.intro-done) .headline-final` (0,3,0) and never apply.
 * Once the root class is set, these elements return to their default opacity
 * and the transition-delays below produce the stagger.
 */

/* ==========================================================================
   Pillars (§9) — the slogan lives here as structure, not as the headline.
   ========================================================================== */

.pillars {
	margin-top: clamp( 3rem, 7vw, 6rem );
	border-top: 1px solid var(--ink-12);
	padding-top: 1.75rem;
	display: grid;
	grid-template-columns: repeat( 3, 1fr );
	gap: clamp( 1rem, 3vw, 3rem );
	transition: opacity 0.6s ease 0.35s, transform 0.6s ease 0.35s;
}

/* An <h2> for document outline, but it must not inherit the h2 display scale. */
.pillar-k {
	font-weight: 600;
	font-size: 1.05rem;
	line-height: 1.5;
	letter-spacing: 0;
	margin-bottom: 0.4rem;
	display: flex;
	align-items: center;
	gap: 0.55rem;
}

.pillar-k .dot {
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: var(--accent);
	flex: none;
}

.pillar:nth-child(2) .dot {
	background: var(--ink);
}

/* The one cool accent, used exactly once (§5). */
.pillar:nth-child(3) .dot {
	background: var(--blue);
}

.pillar-d {
	font-size: 0.9rem;
	color: var(--ink-60);
	max-width: 26ch;
}

/* ==========================================================================
   Interior page shell
   ========================================================================== */

.site-main {
	padding-top: calc( var(--nav-h) + clamp( 2rem, 5vw, 4rem ) );
	padding-bottom: var(--pad-section);
}

.page-header {
	margin-bottom: clamp( 2rem, 4vw, 3.5rem );
}

.entry-content > * + * {
	margin-top: 1.25rem;
}

/*
 * Measure cap (§4). Interior pages render generic page content through
 * .entry-content, which had no max-width and so ran the full 1224px container —
 * ~150-160 characters per line on /racing-simulators/, /lounge/ and
 * /diy-service-bays/, against a comfortable 45-75. The theme already applies
 * this rule selectively (.section-lead at 44ch, .how-statement at 24ch); it was
 * simply never extended to the content that every interior page actually uses.
 *
 * Direct children only. Prose blocks nested inside a layout component — the
 * partner blocks' copy column, the pricing cards, the contact grid — set their
 * own width from their container and must not be capped twice.
 *
 * Headings are included deliberately: an h2 running to 1224px above a capped
 * paragraph reads as unmoored from the text it introduces.
 *
 * Inert on mobile — at 390px the measure is already ~40ch, well inside the cap,
 * so this needs no breakpoint.
 */
.entry-content > p,
.entry-content > ul,
.entry-content > ol,
.entry-content > h2,
.entry-content > h3,
.entry-content > h4,
.entry-content > blockquote {
	max-width: var(--measure);
}

/*
 * Layout, not prose. These are direct children of .entry-content on the pages
 * that use them, and they are grids/rows that need the full container.
 * .alignwide and .alignfull set their own max-width below.
 */
.entry-content > .partner,
.entry-content > .beanz,
.entry-content > .pricing-grid,
.entry-content > .contact-grid,
.entry-content > .partner-specs,
.entry-content > .media-split,
.entry-content > .pillars,
.entry-content > table,
.entry-content > figure {
	max-width: none;
}

/*
 * The pillars carry their own top margin, which `.entry-content > * + *` would
 * otherwise flatten to 1.25rem (equal specificity, later in the file) and
 * collapse the breathing room the hairline needs above it.
 */
.entry-content > .pillars {
	margin-top: clamp( 2.5rem, 5vw, 4rem );
}

/*
 * A closing call to action. At the default 1.25rem it tucked under whatever
 * precedes it — under the pillars it read as part of the first column rather
 * than as the page's last move.
 */
.entry-content > .entry-cta {
	margin-top: clamp( 2.5rem, 5vw, 3.5rem );
}

.entry-content > h2,
.entry-content > h3 {
	margin-top: 2.5rem;
}

.entry-content ul,
.entry-content ol {
	padding-left: 1.25rem;
}

.entry-content img {
	border-radius: 0;
}

/* ==========================================================================
   Shop photography

   The client's own photos of the space (handoff §13: "sells on the space
   looking capable and clean; avoid stock"). Square corners, no frame, no
   shadow (§11) — the photo is the content, so nothing is drawn around it.

   .entry-figure is a full-width photo set directly under the heading it
   illustrates. The files are pre-cropped to 16:9 so one never runs taller
   than the viewport; width/height come from the attachment, so the box is
   reserved before the image loads.
   ========================================================================== */

.entry-figure img {
	width: 100%;
}

/* More air than the 1.25rem prose rhythm on both sides, or the photo reads
   as wedged between heading and paragraph. A following <h2> keeps
   its own, larger top margin. */
.entry-content > .entry-figure,
.entry-content > .entry-figure + :not( h2 ) {
	margin-top: clamp( 1.5rem, 3vw, 2rem );
}

/* Copy beside a photo — the DIY Service Bays inventory list next to the
   workstation that holds it. Same flex shape as .partner, top-aligned
   because the list is longer than the photo is tall. */
.media-split {
	display: flex;
	align-items: flex-start;
	gap: clamp( 2rem, 6vw, 4.5rem );
}

.media-split-body {
	flex: 1 1 auto;
	min-width: 0;
}

.media-split-media {
	flex: 0 0 auto;
	width: clamp( 16rem, 36vw, 30rem );
}

.media-split-img {
	width: 100%;
}

/* A partner's photo sits under its logo in the media column. */
.partner-photo {
	width: 100%;
	margin-top: clamp( 1.5rem, 3vw, 2rem );
}

/*
 * Separator block. The theme never styled it, so core's default won — including
 * the "dots" style, which renders three centred bullets. Every other divider on
 * this site is a 1px --ink-12 hairline (partner blocks, pricing cards, the
 * footer, the account nav), so match that and ignore the style variation
 * rather than carry a second divider language.
 */
.wp-block-separator {
	border: 0;
	border-top: 1px solid var(--ink-12);
	height: 0;
	margin-block: clamp( 1.5rem, 4vw, 2.5rem );
}

.wp-block-separator.is-style-dots::before {
	content: none;
}

.alignwide {
	max-width: min( var(--wrap), 100% );
}

.alignfull {
	max-width: 100vw;
	margin-inline: calc( 50% - 50vw );
}

/* ==========================================================================
   Partner block — Boosted Beanz (Lounge), BendPak & Sonic Tools
   (Featured Partners, DIY Service Bays)

   The same shape as the hero's .hero-top, and deliberately so: the copy is
   the primary content and leads on the left, the partner lockup is the
   secondary mark on the right. At the theme's 760px breakpoint the row
   collapses to one column and the logo — placed after the text in the DOM —
   stacks underneath it.

   Each logo is that company's own brand asset, so Boosted Beanz's pinks,
   BendPak's blue and grey, and Sonic's red dot are third-party marks rather
   than a second SDC accent; §11's single-accent rule governs what WE draw,
   and everything around the images here is --ink-12 and existing spacing
   clamps. No new tokens. (If a future reader is about to "fix" the blue:
   don't — we have written permission to reproduce these marks as supplied.)

   .beanz is kept alongside .partner rather than renamed away. The Lounge
   page's markup lives in the database, not in git, so an environment where
   sdc-create-content-pages.php has not been re-run still holds the old
   .beanz classes — dropping the selector would silently unstyle that page
   on staging.
   ========================================================================== */

.beanz,
.partner {
	display: flex;
	align-items: center;
	gap: clamp( 2rem, 6vw, 4.5rem );
	/* Sets this block off as its own thing after the client's body copy,
	   rather than reading as a fifth paragraph with a picture. */
	margin-top: clamp( 2.5rem, 6vw, 4rem );
	padding-top: clamp( 2.5rem, 6vw, 4rem );
	border-top: 1px solid var(--ink-12);
}

/* Logo left, copy right for alternate entries. A modifier class, not
   :nth-child — these blocks are siblings of every paragraph and heading in
   .entry-content, so an nth-child rule would alternate off a count that
   shifts whenever the surrounding copy changes. */
.partner--reverse {
	flex-direction: row-reverse;
}

/* min-width:0 lets the text column shrink so the copy wraps instead of
   pushing the logo off the row (same reason as .hero-body). */
.beanz-body,
.partner-body {
	flex: 1 1 auto;
	min-width: 0;
}

/*
 * Partner copy takes the same measure as the rest of the page.
 *
 * This started as a fix for logo-less blocks, which have no second column to
 * be constrained by and so ran the full 1224px. Measuring the two-column
 * blocks to confirm they were fine showed they were not: the body column is
 * 784px, which is 111 characters a line — further past the comfortable band
 * than the .entry-content prose that prompted the cap in the first place. It
 * reads acceptably only because these blurbs are three lines long.
 *
 * So the cap applies to every partner block, with or without a logo.
 *
 * The spec rows are deliberately left full-width. They are a table of short
 * label-length values, not prose, and letting them run keeps them scannable
 * against the hairline rules that separate them.
 */
.partner-body > p,
.beanz-body > p {
	max-width: var(--measure);
}

/* .entry-content > * + * only reaches direct children, so the heading and
   paragraph inside this block need their own rhythm. */
.beanz-body > * + *,
.partner-body > * + * {
	margin-top: 1.25rem;
}

.beanz-logo {
	flex: 0 0 auto;
	display: block;
	width: clamp( 13rem, 26vw, 23rem );
	height: auto;
}

/* Partner logos are sized on their wrapper rather than the <img> itself. The
   wrapper is what wpautop cannot swallow into a <p> (see the note in
   sdc_content_pages_partner_block()), so it is the reliable flex item; the
   image then just fills it. This also stops sizes="auto" from feeding a
   collapsed layout width back into srcset selection. */
.partner-media {
	flex: 0 0 auto;
	width: clamp( 13rem, 26vw, 23rem );
}

/*
 * A fixed optical box, for two reasons.
 *
 * Layout stability: sonic-logo.svg carries a viewBox but no width/height
 * attributes and is lazy-loaded, so with `height: auto` it reserved no space
 * and the block reflowed as the file decoded — the logo was simply absent on
 * first paint. A declared height reserves the row whatever the source is.
 *
 * Optical parity: the marks are supplied at wildly different proportions
 * (BendPak lands at 368x43, Sonic at 368x88), so at 100% width one partner
 * read twice the weight of the other for no reason but its source file.
 * Containing both in the same box lets them sit on a common baseline.
 *
 * Colours are untouched — see the note above; these are licensed third-party
 * marks and §11's single-accent rule governs what we draw, not what they do.
 */
.partner-logo {
	display: block;
	width: 100%;
	height: clamp( 3.5rem, 7vw, 5.5rem );
	object-fit: contain;
	object-position: center;
}

/* The model name, sitting under the partner's name. It carries .mono, which
   already supplies the face, size, tracking and --ink-60 — all this adds is a
   tighter gap than the block's 1.25rem rhythm, because the model name belongs
   to the <h2> above it rather than standing as its own paragraph. Declared
   after the rhythm rule so it wins on order at equal specificity; no
   !important needed. */
.partner-product {
	margin-top: 0.4rem;
}

/* Specs read as a data list, not prose: hairline-separated rows, no bullets,
   no card fill or radius (§11).

   Two classes deep on purpose. `.entry-content ul` sets padding-left: 1.25rem
   and beats a single .partner-specs on specificity, not source order — the
   same trap .pricing-card .pricing-features documents below. Left at one class
   the specs sat indented 20px out of line with the paragraph above them.

   margin-top is restated here rather than left to `.partner-body > * + *`:
   this rule is more specific, so a bare `margin: 0` would silently cancel that
   rhythm and butt the list against the blurb. */
.partner .partner-specs {
	list-style: none;
	margin: 1.25rem 0 0;
	padding: 0;
	border-top: 1px solid var(--ink-12);
}

.partner .partner-specs li {
	margin: 0;
	padding: 0.6rem 0;
	border-bottom: 1px solid var(--ink-12);
	font-size: 0.9375rem;
}

/* ==========================================================================
   Membership pricing table (Phase 4)

   The three PAID tiers as a grid of cards, not a literal <table> — the feature
   count differs per tier (3-6 items), so forcing them into table rows would
   mean empty, meaningless cells. Each card still uses real heading + list
   semantics (h2 + ul), never bare divs, per the §11/accessibility rules.
   auto-fit + minmax lets the grid step itself down from 3 columns to 1 as
   the viewport narrows with no hard breakpoint required, so it can't
   overflow at a 375px width. Card fill/border reuse the .pmpro_card pattern
   (--bg / --ink-12) — no new colors, radii, or shadows (§11).

   "No Membership" is NOT in this grid — it renders after it as
   .pricing-card--payg, full width and quieter. As a fourth cell it read as a
   fourth tier of equal standing, and four cards at minmax(250px, 1fr) inside
   the 1320px wrap landed 3-up-plus-an-orphan between roughly 800 and 1100px.
   ========================================================================== */

/*
 * Monthly / Annual switch — CSS only, no JavaScript on this page.
 *
 * Every paid card carries both prices and both CTAs, tagged
 * data-period="month|year" by scripts/sdc-membership-pricing-page.php; the
 * rules below show the pair matching the checked radio. A JS toggle would have
 * meant a new asset, a SDC_VERSION bump to bust its cache, and annual pricing
 * that vanishes if the script fails to load.
 *
 * The radios themselves are .screen-reader-text — clipped, but still the real
 * focusable control, so focus is forwarded to the label with the same accent
 * ring the Accessibility section gives every other input.
 */
.pricing-billing {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	width: fit-content;
	margin: clamp( 2rem, 4vw, 3rem ) auto 0;
	padding: 0;
	border: 1px solid var(--ink-12);
	border-radius: 0;
}

.pricing-billing label {
	padding: 0.7rem 1.25rem;
	font-family: var(--mono);
	font-size: 0.72rem;
	text-transform: uppercase;
	letter-spacing: 0.14em;
	color: var(--ink-60);
	cursor: pointer;
}

.pricing-billing input:checked + label {
	background: var(--tan);
	color: var(--ink);
	font-weight: 600;
}

.pricing-billing input:focus-visible + label {
	outline: 2px solid var(--accent);
	outline-offset: -2px;
}

.pricing-billing-save {
	letter-spacing: 0.04em;
	text-transform: none;
}

/*
 * The switch itself. ANNUAL is the default — it is the plan the client wants
 * led with — so the annual pair is what shows with no rule applied, and a
 * browser without :has() support lands on annual too rather than on a period
 * nobody chose. Keep this in step with the `checked` radio in
 * scripts/sdc-membership-pricing-page.php: the CSS default and the markup
 * default must name the same period, or the first paint contradicts the
 * highlighted label.
 */
.entry-content [data-period="month"] {
	display: none;
}

body:has( #sdc-billing-month:checked ) .entry-content [data-period="month"] {
	display: block;
}

body:has( #sdc-billing-month:checked ) .entry-content [data-period="year"] {
	display: none;
}

.pricing-grid {
	display: grid;
	grid-template-columns: repeat( auto-fit, minmax( 250px, 1fr ) );
	gap: clamp( 1rem, 2.5vw, 1.75rem );
	margin: clamp( 2rem, 4vw, 3rem ) 0;
	align-items: stretch;
}

.pricing-card {
	display: flex;
	flex-direction: column;
	padding: clamp( 1.5rem, 3vw, 2rem );
	background: var(--bg);
	border: 1px solid var(--ink-12);
	border-radius: 0;
}

/*
 * Reserve the badge row on the cards that have no badge.
 *
 * Premier carries .pricing-recommended-badge and Founders carries
 * .pricing-cap-badge as their first child, so their tier names started 38px
 * lower than No Membership's and Core's — four cards, three different heading
 * baselines, in a row whose whole job is to be compared across. The spacer is
 * the badge's own box (26px + 12px margin) so the four names sit on one line.
 *
 * Keyed off :has() rather than a class on the markup, because the cards are
 * generated by scripts/sdc-membership-pricing-page.php and a presentational
 * class there would have to be kept in sync with this rule by hand.
 */
.pricing-card:not(.pricing-card--payg):not(:has( > .pricing-recommended-badge )):not(:has( > .pricing-cap-badge ))::before {
	content: "";
	display: block;
	height: 26px;
	margin-bottom: 12px;
}

/*
 * Premier is the client's recommended tier (§5 only allows one orange CTA
 * per view among the pricing cards, so Core/Founders switch to .btn-ghost
 * and Premier alone keeps .btn-primary — see sdc-membership-pricing-page.php
 * and the [sdc_founders_cta] shortcode). The stronger border is --ink, not
 * --accent, so the card frame itself doesn't add a second orange element;
 * the "Recommended" label is the one permitted accent word/mark for this
 * view, tying directly back to the orange CTA below it.
 */
.pricing-card--recommended {
	border: 2px solid var(--ink);
}

.pricing-recommended-badge {
	display: inline-block;
	font-family: var(--mono);
	font-size: 0.75rem;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--accent);
	background: var(--tan);
	padding: 0.25rem 0.6rem;
	margin: 0 0 0.75rem;
}

.pricing-card-tier {
	font-size: 1.25rem;
	font-weight: 600;
	letter-spacing: -0.015em;
	margin: 0 0 0.5rem;
}

.pricing-card-price {
	font-family: var(--mono);
	font-size: 1.75rem;
	margin: 0 0 1.25rem;
}

.pricing-card-price-unit {
	font-size: 1rem;
	color: var(--ink-60);
}

/* The saving, under the annual figure. Block so it drops to its own line
   without a <br> in the generated markup. */
.pricing-card-price-note {
	display: block;
	margin-top: 0.35rem;
	font-family: var(--sans);
	font-size: 0.8rem;
	line-height: 1.4;
	color: var(--ink-60);
}

/* Qualified with .pricing-card, not just .pricing-features, so this beats
   the entry-content ul/ol padding-left rule on specificity rather than
   source order (both apply to the same <ul>). */
.pricing-card .pricing-features {
	list-style: none;
	padding: 0;
	margin: 0 0 1.5rem;
	flex-grow: 1;
}

.pricing-features li {
	position: relative;
	padding-left: 1.25rem;
	margin-bottom: 0.65rem;
	font-size: 0.95rem;
	line-height: 1.5;
}

/*
 * Ink, not accent. Four cards' worth of features put roughly 25 orange marks
 * in a single viewport, against §5's "primary CTAs and at most one accent word
 * or mark per view" — and it left the actual CTA competing with the list it
 * was meant to close. The accent on this page now belongs to the RECOMMENDED
 * badge and the one filled button.
 */
.pricing-features li::before {
	content: "";
	position: absolute;
	left: 0;
	top: 0.55em;
	width: 6px;
	height: 6px;
	background: var(--ink);
}

.pricing-features del {
	color: var(--ink-60);
}

.pricing-cap-badge {
	display: inline-block;
	font-family: var(--mono);
	font-size: 0.75rem;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--ink-60);
	background: var(--tan);
	padding: 0.25rem 0.6rem;
	margin: 0 0 0.75rem;
}

.pricing-cap-status {
	font-size: 0.85rem;
	color: var(--ink-60);
	margin: -0.5rem 0 1rem;
}

.pricing-cap-status.is-full {
	color: var(--ink);
	font-weight: 600;
}

.pricing-cta {
	margin-top: auto;
}

/*
 * "No Membership" — its own row under the three paid tiers.
 *
 * Same card component, turned on its side: intro, then the rates as a
 * horizontal run, then the CTA pushed to the end. It is quieter by treatment
 * rather than by being shrunk — muted text, no big mono price, no badge-row
 * spacer — so it reads as the fallback rather than as a fourth, cheapest tier.
 * Below 760px (the theme's one breakpoint) it stacks back into a normal card.
 */
.pricing-card--payg {
	flex-direction: row;
	flex-wrap: wrap;
	align-items: center;
	gap: clamp( 1rem, 3vw, 2.5rem );
	margin-bottom: clamp( 2rem, 4vw, 3rem );
	color: var(--ink-60);
}

.pricing-card--payg .pricing-card-tier {
	margin: 0;
	font-size: 1rem;
	color: var(--ink);
}

.pricing-card--payg .pricing-card-price {
	margin: 0;
	font-size: 0.95rem;
	color: var(--ink-60);
}

.pricing-card--payg .pricing-features {
	display: flex;
	flex-wrap: wrap;
	gap: 0.35rem 1.5rem;
	margin: 0;
	flex-grow: 1;
}

.pricing-card--payg .pricing-features li {
	margin-bottom: 0;
	font-size: 0.9rem;
}

.pricing-card--payg .pricing-features li::before {
	background: var(--ink-60);
}

.pricing-card--payg .pricing-cta {
	margin-top: 0;
	margin-left: auto;
}

@media ( max-width: 760px ) {
	.pricing-card--payg {
		flex-direction: column;
		align-items: flex-start;
		gap: 1rem;
	}

	.pricing-card--payg .pricing-cta {
		margin-left: 0;
	}
}

.btn.btn-disabled {
	background: var(--tan);
	color: var(--ink-60);
	padding: 0.7rem 1.25rem;
	border: 1px solid var(--ink-12);
	cursor: not-allowed;
}

.site-footer {
	border-top: 1px solid var(--ink-12);
	padding-top: clamp( 2.5rem, 5vw, 4rem );

	/*
	 * myCred's loyalty launcher is fixed at bottom:24px, is 48px tall, and sits
	 * bottom-right at z-index 99999 — exactly where the footer menu ends. Reserve
	 * room for it at every width, not just on mobile: at the bottom of the page
	 * the viewport's bottom edge is the document's, so it lands on the last link.
	 */
	padding-bottom: 96px;
}

.site-footer-inner {
	display: flex;
	flex-wrap: wrap;
	gap: 1.5rem;
	align-items: center;
	justify-content: space-between;
}

/*
 * The brand mark takes its own full-width row above the copyright/menu line
 * (flex-basis:100% forces the wrap), so the lockup is not squeezed between two
 * text blocks. line-height:0 strips the inline-image descender gap.
 */
.site-footer-brand {
	flex-basis: 100%;
	display: block;
	line-height: 0;
}

.site-footer-logo {
	width: clamp( 8.5rem, 22vw, 12rem );
	height: auto;
	margin-bottom: clamp( 0.75rem, 2.5vw, 1.5rem );
}

.footer-menu {
	display: flex;
	flex-wrap: wrap;
	gap: 1.5rem;
	list-style: none;
}

.footer-menu a {
	font-size: 0.95rem;
	color: var(--ink-60);
	text-decoration: none;
}

.footer-menu a:hover {
	color: var(--ink);
}

/* ==========================================================================
   myCred loyalty launcher

   The React launcher and its panel style themselves with MUI/Emotion classes
   injected into a <style> tag late in <head>, so these need matching (or
   greater) specificity. Several corners the widget's settings can't reach: the
   launcher shadow is hardcoded, launcherRadius:0 is dropped by a `radius || 45`
   falsy-zero fallback, and the panel's borderRadius slider bottoms out at 9.
   All violate §11, so flatten everything — rounding and shadows are forbidden
   here, so squaring the panel cards and buttons is the intended result. Buttons
   take the theme's own --radius so they read as buttons, not cards. Colours and
   the logo toggle live in the widget's Design settings, not here.

   Boxes (the launcher) flatten with a plain rule; Paper cards need !important
   because MUI sets the elevation shadow that strongly. Round icon buttons
   (borderRadius:50%) are left alone — they aren't cards. */

#mycred-loyalty-widget-root [class*="MuiBox-root"],
#mycred-loyalty-widget-root [class*="MuiPaper-root"] {
	border-radius: 0;
}

#mycred-loyalty-widget-root [class*="MuiBox-root"] {
	box-shadow: none;
}

#mycred-loyalty-widget-root [class*="MuiPaper-root"] {
	box-shadow: none !important;
}

#mycred-loyalty-widget-root [class*="MuiButton-root"] {
	border-radius: var(--radius);
}

/*
 * The panel shell already draws a 1px border, but at ~10% ink it vanishes
 * against the cream page now that the shadow is gone. Darken it so the popup
 * reads as a distinct surface. The shell is the wrapper's second child (the
 * launcher is the first); when the panel is closed there is no second child, so
 * this self-limits to the open state.
 */
#mycred-loyalty-widget-root > div > div:nth-child(2) {
	border-color: var(--ink-60);
}

/*
 * At 152x48 the launcher covers 39% of a 390px viewport — enough to sit on two
 * lines of policy copy on /shop-rules/ and, at the wrong scroll offset, on the
 * booking calendar's month header and next-month arrow. The widget renders a
 * bare text label with no icon slot, so there is nothing to collapse to; shrink
 * the type and padding instead and tuck it further into the corner.
 *
 * Matched by position rather than the Emotion hash (css-evn37) because those
 * class names change on every widget build. Same first-child/second-child
 * convention the panel-border rule above relies on.
 */
@media ( max-width: 760px ) {
	#mycred-loyalty-widget-root > div > div:nth-child(1),
	#mycred-loyalty-widget-root > div > div:nth-child(1) [class*="MuiBox-root"] {
		height: auto;
		min-height: 0;
	}

	/* The widget's own inline padding is 0 20px; halve it. */
	#mycred-loyalty-widget-root > div > div:nth-child(1) [class*="MuiBox-root"] {
		padding: 0.5rem 0.625rem;
	}

	/* Padding lives on the box above, so keep it off the label or it stacks. */
	#mycred-loyalty-widget-root > div > div:nth-child(1) [class*="MuiTypography-root"] {
		font-size: 0.75rem;
		line-height: 1.2;
		padding: 0;
	}
}

/* ==========================================================================
   WooCommerce
   Ported from the retired scc-visual-polish.php mu-plugin and re-tokenized.
   Kept: the notice left-padding and the product grid layout — the theme
   supplies neither by default. Dropped: the #0072e5 blue, the 12px radius,
   and the drop shadows, all of which violate §11.
   ========================================================================== */

/* WooCommerce absolutely positions the notice status icon, so the notice
   needs left padding to reserve room for it or the icon lands on the first
   character of the message. */
.woocommerce-error,
.woocommerce-info,
.woocommerce-message {
	position: relative;
	padding: 1em 1.5em 1em 3.5em;
	margin: 0 0 1.5em;
	list-style: none;
	background: var(--tan);
	/* WooCommerce colours the top border per state (blue/red/green); reset it so
	   only the accent left border remains (§11 — a single accent per element). */
	border: 0;
	border-left: 2px solid var(--accent);
}

.woocommerce-error > li,
.woocommerce-info > li,
.woocommerce-message > li {
	list-style: none;
	margin-left: 0;
}

.woocommerce-error::before,
.woocommerce-info::before,
.woocommerce-message::before {
	left: 1.5em;
	color: var(--accent);
}

/* The product grid: square corners, tan hairline, no shadow, no lift. */
.woocommerce ul.products {
	display: grid;
	grid-template-columns: repeat( auto-fill, minmax( 260px, 1fr ) );
	gap: clamp( 1rem, 2.5vw, 2rem );
	margin: 2rem 0;
	padding: 0;
	list-style: none;
}

.woocommerce ul.products::before,
.woocommerce ul.products::after {
	display: none;
	content: none;
}

.woocommerce ul.products li.product {
	width: auto;
	margin: 0;
	float: none;
	list-style: none;
	display: flex;
	flex-direction: column;
	padding: 1.25rem;
	background: var(--bg);
	border: 1px solid var(--ink-12);
	border-radius: 0;
	box-shadow: none;
}

.woocommerce ul.products li.product img {
	width: 100%;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	background: var(--tan);
	border-radius: 0;
	margin: 0 0 1rem;
}

.woocommerce ul.products li.product .woocommerce-loop-product__title {
	font-size: 1.15rem;
	font-weight: 600;
	letter-spacing: -0.015em;
	color: var(--ink);
	padding: 0 0 0.35rem;
}

.woocommerce ul.products li.product .price {
	margin-bottom: 1rem;
	font-family: var(--mono);
	font-size: 0.9rem;
	color: var(--ink-60);
}

/*
 * Loop CTAs are ink, not accent.
 *
 * In a grid every card's button is equally primary, so filling them all with
 * the accent put seven orange elements in one viewport on /shop/ and five on
 * /cart/ — the opposite of §5, where the accent is what tells you which single
 * thing to do. Ink fill keeps the cards decisive and on-palette while leaving
 * the accent to mean something: the nav's "Book a Bay or Sim", and the one
 * primary action on a page that has one (single-product add to cart, place
 * order, log in), which keep the .alt treatment above.
 */
.woocommerce ul.products li.product .button {
	margin-top: auto;
	align-self: flex-start;
	background: var(--ink);
	color: var(--bg);
}

.woocommerce ul.products li.product .button:hover {
	background: var(--accent);
	color: #fff;
}

/*
 * The block product grid (empty-cart cross-sell, and any Products block) is
 * separate markup from ul.products — .wc-block-grid__product with a
 * .wp-block-button__link inside — so it needs the same treatment stated again
 * rather than inherited. Without it the empty cart still showed four orange
 * buttons. Block grids also centre their cards; the rest of the site is
 * flush-left.
 */
.wc-block-grid__product .wp-block-button__link,
.wc-block-grid__product .wp-block-button__link:visited {
	background: var(--ink);
	color: var(--bg);
}

.wc-block-grid__product .wp-block-button__link:hover {
	background: var(--accent);
	color: #fff;
}

.wc-block-grid__product,
.wc-block-grid__product-title,
.wc-block-grid__product-price,
.wc-block-grid__product .wc-block-grid__product-add-to-cart {
	text-align: left;
}

/*
 * Commerce CTAs adopt the primary button.
 *
 * The .alt variants are listed explicitly. WooCommerce styles them at
 * `.woocommerce button.button.alt` — one class more than the base rule — so
 * without a matching selector every "primary" Woo button (add to cart, place
 * order, and the Bookings submit) kept Woo's #7F54B3 purple while the
 * secondary ones correctly went orange. Matching specificity and relying on
 * source order (the theme sheet loads after woocommerce.css) is enough; no
 * !important needed.
 */
.woocommerce a.button,
.woocommerce button.button,
.woocommerce input.button,
.woocommerce #respond input#submit,
.woocommerce a.button.alt,
.woocommerce button.button.alt,
.woocommerce input.button.alt,
.woocommerce #respond input#submit.alt,
.wp-block-button__link {
	background: var(--accent);
	color: #fff;
	border: 0;
	border-radius: var(--radius);
	padding: 0.7rem 1.25rem;
	font-family: var(--sans);
	font-weight: 600;
	font-size: 0.9rem;
	box-shadow: none;
	text-decoration: none;
	transition: background 0.2s ease;
}

.woocommerce a.button:hover,
.woocommerce button.button:hover,
.woocommerce input.button:hover,
.woocommerce #respond input#submit:hover,
.woocommerce a.button.alt:hover,
.woocommerce button.button.alt:hover,
.woocommerce input.button.alt:hover,
.woocommerce #respond input#submit.alt:hover,
.wp-block-button__link:hover {
	background: var(--accent-hover);
	color: #fff;
}

/*
 * Disabled primaries. A disabled button reads as disabled through opacity and
 * cursor; it does not need a different hue.
 *
 * The `:disabled[disabled]` pair is not redundant. WooCommerce's winning
 * selector is `button.button.alt:disabled[disabled]` — the pseudo-class and the
 * attribute selector each count as a class, so that rule sits at (0,5,1) and
 * out-ranks a plain `.alt.disabled` override at (0,4,1). Matching its shape is
 * what makes this win on source order; dropping either half loses.
 */
.woocommerce a.button.alt.disabled,
.woocommerce a.button.alt.disabled:hover,
.woocommerce button.button.alt.disabled,
.woocommerce button.button.alt.disabled:hover,
.woocommerce button.button.alt:disabled[disabled],
.woocommerce button.button.alt:disabled[disabled]:hover,
.woocommerce input.button.alt.disabled,
.woocommerce input.button.alt.disabled:hover,
.woocommerce input.button.alt:disabled[disabled],
.woocommerce input.button.alt:disabled[disabled]:hover {
	background-color: var(--accent);
	color: #fff;
	cursor: not-allowed;
	opacity: 0.45;
}

/* --------------------------------------------------------------------------
   Empty cart (Cart block)

   WooCommerce draws a large black circular sad-face glyph above the empty-cart
   heading and centres the whole block. A rounded shape and an emoji
   illustration are both off-system (§11), and centring is a second alignment
   grammar on a site that is flush-left everywhere else. Drop the glyph and
   align the message with the page.

   The "New in store" cross-sell heading beneath it is block content in the
   cart page itself, not markup the theme can reach — see the note in the
   review; it needs a content edit, not CSS.
   -------------------------------------------------------------------------- */
.wc-block-cart__empty-cart__title.with-empty-cart-icon::before {
	content: none;
}

.wc-block-cart__empty-cart__title,
.wp-block-woocommerce-empty-cart-block .has-text-align-center {
	text-align: left;
}

/*
 * WooCommerce sizes this heading at 16px, which left the page's actual message
 * smaller than the "Also available" cross-sell heading below it — the
 * hierarchy exactly inverted. It is an h2; give it the h2 ramp.
 */
.wc-block-cart__empty-cart__title {
	font-size: clamp( 1.75rem, 4vw, 3rem );
	line-height: 1.06;
	letter-spacing: -0.025em;
	margin-bottom: 0.75rem;
}

/*
 * ...and the cross-sell heading is secondary to it, so it steps down rather
 * than matching the page title. Scoped to the block so ordinary h2s elsewhere
 * are untouched.
 */
.wp-block-woocommerce-empty-cart-block h2:not( .wc-block-cart__empty-cart__title ) {
	font-size: clamp( 1.35rem, 2.4vw, 1.9rem );
}

/* --------------------------------------------------------------------------
   Catalogue ordering select

   The only unstyled control left on the front end — a raw browser dropdown in
   a system font, next to CF7 selects that are fully themed. Matches the form
   input treatment rather than inventing a third one.
   -------------------------------------------------------------------------- */
.woocommerce .woocommerce-ordering select {
	appearance: none;
	background: transparent;
	border: 1px solid var(--ink-12);
	border-radius: var(--radius);
	color: var(--ink);
	font-family: var(--sans);
	font-size: 0.9rem;
	padding: 0.55rem 2.25rem 0.55rem 0.75rem;
	/* Chevron drawn inline so the control needs no image asset. */
	background-image: url( "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23171717' stroke-width='1.5'/%3E%3C/svg%3E" );
	background-repeat: no-repeat;
	background-position: right 0.75rem center;
}

.woocommerce .woocommerce-result-count {
	color: var(--ink-60);
	font-family: var(--mono);
	font-size: 0.75rem;
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

/* --------------------------------------------------------------------------
   My Account

   Dropping woocommerce-layout.css (§11) also strips the dashboard's two-column
   nav/content split, and dropping woocommerce-smallscreen.css strips the
   responsive-table stacking — so both are reimplemented here against the
   tokens rather than restored. The Subscriptions and Bookings endpoints ride
   the same .woocommerce-MyAccount-navigation-link markup, so no per-endpoint
   rules are needed.
   -------------------------------------------------------------------------- */
/*
 * Gated on the nav actually being there. Logged out there is no
 * .woocommerce-MyAccount-navigation, so the two-column rule handed column 1 to
 * the notices wrapper (squeezing any notice into a 240px sliver) and pushed the
 * login form into column 2 — leaving the form starting at 396px while the h1
 * stayed at 108px. Same failure mode as the ::before clearfix below: the grid
 * assumes two real children and gets one.
 */
.woocommerce-account .woocommerce:has( .woocommerce-MyAccount-navigation ) {
	display: grid;
	grid-template-columns: minmax( 0, 15rem ) 1fr;
	gap: clamp( 1.5rem, 4vw, 3rem );
	align-items: start;
}

/* The kept woocommerce.css adds clearfix `.woocommerce::before/::after`
   pseudo-elements; inside a grid container those become grid items, and the
   ::before grabs column 1 — shoving the nav into column 2. Neutralize them so
   the two real children (nav, then content) land in source order. Same trick
   the product grid uses above. */
.woocommerce-account .woocommerce::before,
.woocommerce-account .woocommerce::after {
	content: none;
}

.woocommerce-MyAccount-navigation ul {
	margin: 0;
	padding: 0;
	list-style: none;
	border-top: 1px solid var(--ink-12);
}

.woocommerce-MyAccount-navigation-link {
	border-bottom: 1px solid var(--ink-12);
}

.woocommerce-MyAccount-navigation-link a {
	display: block;
	padding: 0.75rem 0;
	color: var(--ink-60);
	font-weight: 600;
	font-size: 0.95rem;
	text-decoration: none;
	transition: color 0.2s ease;
}

.woocommerce-MyAccount-navigation-link a:hover,
.woocommerce-MyAccount-navigation-link.is-active a {
	color: var(--accent);
}

/* min-width:0 lets a wide table shrink/scroll instead of blowing out the grid. */
.woocommerce-MyAccount-content {
	min-width: 0;
}

/* Membership status card on the dashboard (sdc-consolidate-member-account.php):
   WooCommerce has no tier concept, so this surfaces the PMPro level plus the
   billing/tier actions. Tan fill + accent left border, matching the notices. */
.sdc-account-membership {
	margin: 0 0 1.5rem;
	padding: 1rem 1.25rem;
	background: var(--tan);
	border-left: 2px solid var(--accent);
}

.sdc-account-membership__status {
	margin: 0;
	font-size: 1.05rem;
}

/* Sans, not mono: the monospace read as inert technical text and clashed with
   the sans-serif account UI. The links keep their underline affordance. */
.sdc-account-membership__links {
	margin: 0.5rem 0 0;
	font-family: var(--sans);
	font-size: 0.9rem;
	color: var(--ink-60);
}

/* Account tables (orders, subscriptions, bookings): square, tan hairline. */
.woocommerce-account .woocommerce-MyAccount-content table.shop_table {
	width: 100%;
	border: 1px solid var(--ink-12);
	border-radius: 0;
	border-collapse: collapse;
}

.woocommerce-account .woocommerce-MyAccount-content table.shop_table th,
.woocommerce-account .woocommerce-MyAccount-content table.shop_table td {
	padding: 0.75rem;
	border: 1px solid var(--ink-12);
}

/* In-content section headings — "Subscription totals", "Related orders", the
   billing/shipping titles on the order, subscription, and address endpoints.
   They are plain <h2>/<h3>, so they inherit the display scale (§4) at ~51px
   with the reset's zero margins, which oversizes them and crushes them against
   the table below. Step them down to a subhead and restore vertical rhythm.
   The page <h1> title is a direct child of the header, not the panel, so it is
   untouched. */
.woocommerce-MyAccount-content h2,
.woocommerce-MyAccount-content h3,
.woocommerce-MyAccount-content .woocommerce-column__title {
	font-size: clamp( 1.35rem, 2vw, 1.6rem );
	line-height: 1.15;
	letter-spacing: -0.02em;
	margin: 2.5rem 0 1rem;
}

/* No top gap on the first heading in the panel — it already sits below the
   page title. */
.woocommerce-MyAccount-content > h2:first-child,
.woocommerce-MyAccount-content > h3:first-child {
	margin-top: 0;
}

/* The order-details and address section titles also appear OUTSIDE My Account —
   on the order-received / thank-you page, which has no
   .woocommerce-MyAccount-content wrapper — where they revert to the giant
   display h2. Tame them by their WooCommerce classes so the fix travels with the
   template rather than the wrapper. Same values as the panel rule above. */
.woocommerce .woocommerce-order-details__title,
.woocommerce .woocommerce-column__title,
.woocommerce .woocommerce-order-downloads__title {
	font-size: clamp( 1.35rem, 2vw, 1.6rem );
	line-height: 1.15;
	letter-spacing: -0.02em;
	margin: 2.5rem 0 1rem;
}

/* WooCommerce sets <address> italic by default, which reads as an aside next to
   the plain table text. Normalize it in the panel and on the thank-you page. */
.woocommerce-MyAccount-content address,
.woocommerce .woocommerce-customer-details address {
	font-style: normal;
	line-height: 1.6;
	color: var(--ink);
}

/* Addresses endpoint: woocommerce-layout.css (dropped, §11) supplied the
   col2-set grid, so billing and shipping stack full-width instead of sitting
   side by side. Reimplement the two-column set against the tokens. */
.woocommerce-MyAccount-content .woocommerce-Addresses.col2-set {
	display: grid;
	grid-template-columns: repeat( 2, minmax( 0, 1fr ) );
	gap: clamp( 1.5rem, 4vw, 3rem );
}

/* The edit link floats right in woocommerce.css, stranding it at the panel edge
   far from its heading. Stack it directly beneath the title instead: at ~440px
   per column the longer "Shipping address" can't sit inline with its link
   without wrapping, so stacking keeps the two columns symmetric. */
.woocommerce-MyAccount-content .woocommerce-Address-title {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 0.35rem;
	margin: 2.5rem 0 1rem;
}

.woocommerce-MyAccount-content .woocommerce-Address-title h2,
.woocommerce-MyAccount-content .woocommerce-Address-title h3 {
	margin: 0;
}

.woocommerce-MyAccount-content .woocommerce-Address-title .edit {
	float: none;
	flex: none;
	font-family: var(--mono);
	font-size: 0.78rem;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	white-space: nowrap;
}

/* Account-details "Password change" fieldset: the browser-default <fieldset>
   renders a grooved border with the <legend> notched into it, and the reset's
   `* { padding: 0 }` collapses the legend's spacing onto that border. Restyle it
   as a plain titled section with a hairline rule under the legend. */
.woocommerce-MyAccount-content fieldset {
	min-width: 0;
	margin: 2.5rem 0 0;
	padding: 0;
	border: 0;
}

.woocommerce-MyAccount-content fieldset legend {
	display: block;
	width: 100%;
	margin: 0 0 1.25rem;
	padding: 0 0 0.75rem;
	border-bottom: 1px solid var(--ink-12);
	font-weight: 700;
	font-size: 1.15rem;
	letter-spacing: -0.02em;
}

/* The show/hide password toggle. WooCommerce injects an empty
   <button class="show-password-input"> whose only accessible name is an
   aria-label — an eye-only control that reads as "no text". Render it instead as
   a compact "Show"/"Hide" word pinned inside the field, matching the labeled
   toggle on the membership forms. Scoped to .woocommerce so the login form and
   the account page (which live in different wrappers) get identical treatment. */
.woocommerce .password-input {
	position: relative;
	display: block;
}

/* Beat WooCommerce's own `.woocommerce form .form-row .input-text { padding: 0.5em }`
   (specificity 0,3,1) so the typed value never slides under the toggle word. */
.woocommerce form .password-input input.woocommerce-Input {
	padding-right: 4rem;
}

.woocommerce .show-password-input {
	position: absolute;
	top: 50%;
	right: 0.75rem;
	transform: translateY( -50% );
	width: auto;
	height: auto;
	margin: 0;
	padding: 0;
	border: 0;
	background: transparent;
	/* Ink-60, not accent: the login form's one accent belongs to "Log in". */
	color: var(--ink-60);
	font-size: 0.8rem;
	font-weight: 600;
	letter-spacing: 0.02em;
	line-height: 1;
	cursor: pointer;
}

/* WooCommerce toggles .display-password on the button while the value is shown. */
.woocommerce .show-password-input::after {
	content: 'Show';
}

.woocommerce .show-password-input.display-password::after {
	content: 'Hide';
}

.woocommerce .show-password-input:hover::after,
.woocommerce .show-password-input:focus-visible::after {
	text-decoration: underline;
}

/*
 * 760, not the 768 this block used to carry. Every other breakpoint in the
 * theme — and nav.js's matchMedia — is 760, so between 761 and 768px the
 * account page stacked while the nav was still in its desktop row.
 */
@media ( max-width: 760px ) {
	/* Stack the nav above the content (was woocommerce-layout's float grid). */
	.woocommerce-account .woocommerce:has( .woocommerce-MyAccount-navigation ) {
		grid-template-columns: 1fr;
	}

	/* Collapse the address set to a single column alongside the nav stack. */
	.woocommerce-MyAccount-content .woocommerce-Addresses.col2-set {
		grid-template-columns: 1fr;
	}

	/* Collapse each order/subscription row into a labelled card
	   (was woocommerce-smallscreen, driven by WooCommerce's data-title attrs). */
	.woocommerce-account table.shop_table_responsive thead {
		display: none;
	}

	.woocommerce-account table.shop_table_responsive tr td {
		display: block;
		text-align: right;
	}

	.woocommerce-account table.shop_table_responsive tr td::before {
		content: attr( data-title );
		float: left;
		font-weight: 600;
		color: var(--ink-60);
	}

	.woocommerce-account table.shop_table_responsive tr td.actions {
		text-align: center;
	}
}

/* ==========================================================================
   WooCommerce Bookings — the booking form and jQuery UI datepicker

   sdc_woocommerce_styles() keeps the general woocommerce.css sheet precisely so
   this calendar renders, but nothing had ever re-tokenized it, so the highest-
   intent page on the site was shipping stock jQuery UI: a #999-to-#aaa vertical
   gradient header (§6 forbids gradients outright), #176D3B green available days
   and a #7F54B3 WooCommerce-purple submit button. Three off-palette colours and
   a gradient on the page where someone actually books.

   The datepicker markup is generated by jQuery UI, so the .ui-* selectors are
   the only handles available; they are matched here rather than restyled at
   source. See design-handoff.md §5/§6/§11.
   ========================================================================== */

/*
 * Selectors are hung on .wc-bookings-date-picker, not .wc-bookings-booking-form,
 * because that is the ancestor the plugin's own rules use — matching it is what
 * makes these win on source order. Where the plugin sets a colour !important
 * (every day-state fill does) the override has to carry it too; that is the
 * plugin's escalation, not a preference of ours.
 */

/* Shell: the plugin draws a drop shadow here, which §6 forbids. */
.wc-bookings-date-picker .ui-widget-content {
	background: transparent;
	border: 1px solid var(--ink-12);
	box-shadow: none;
}

/* Header: flat tan, square, no gradient. */
.wc-bookings-date-picker .ui-datepicker-header {
	background-color: var(--tan);
	background-image: none;
	border: 0;
	border-bottom: 1px solid var(--ink-12);
	border-radius: 0;
	color: var(--ink);
}

.wc-bookings-date-picker .ui-datepicker .ui-datepicker-title {
	color: var(--ink);
	font-weight: 600;
}

.wc-bookings-date-picker .ui-datepicker .ui-datepicker-prev,
.wc-bookings-date-picker .ui-datepicker .ui-datepicker-next {
	background: transparent;
	border-radius: 0;
	cursor: pointer;
}

.wc-bookings-date-picker .ui-datepicker .ui-datepicker-prev.ui-state-hover,
.wc-bookings-date-picker .ui-datepicker .ui-datepicker-next.ui-state-hover {
	background: var(--ink-12);
}

/* Stock is #bbb on #fefefe — roughly 1.9:1, effectively unreadable. */
.wc-bookings-date-picker .ui-datepicker th {
	background: transparent;
	border-top: 0;
	color: var(--ink-60);
	font-family: var(--mono);
	font-size: 0.68rem;
	font-weight: 400;
	letter-spacing: 0.1em;
}

.wc-bookings-date-picker .ui-datepicker td {
	background: transparent;
	border-top: 1px solid var(--ink-12);
	border-right: 1px solid var(--ink-12);
}

.wc-bookings-date-picker .ui-datepicker td .ui-state-default {
	color: var(--ink);
}

/*
 * Availability reads through fill weight, not hue. Bookable days take the tan
 * fill already used by the pricing badges and account notices; the accent is
 * held back for the day actually selected — the single accent mark per view
 * that §5 allows. Replaces #1f874b green and its white text-shadow.
 */
.wc-bookings-date-picker .ui-datepicker td.bookable a,
.wc-bookings-date-picker .ui-datepicker td.partial_booked a {
	background-color: var(--tan) !important;
	background-image: none !important;
	border-color: var(--ink-12) !important;
	color: var(--ink) !important;
	font-weight: 600;
	text-shadow: none;
}

.wc-bookings-date-picker .ui-datepicker td.bookable a:hover,
.wc-bookings-date-picker .ui-datepicker td.partial_booked a:hover {
	background-color: var(--ink) !important;
	color: var(--bg) !important;
}

.wc-bookings-date-picker .ui-datepicker td.ui-datepicker-current-day a,
.wc-bookings-date-picker .ui-datepicker td.bookable-range .ui-state-default {
	background: var(--accent) !important;
	color: #fff !important;
}

/*
 * Selected day is already marked by the accent fill; drop the underline. The
 * .ui-state-hover pairing has to be spelled out — jQuery UI leaves that class
 * on the active cell, and the plugin's underline rule names both, so matching
 * only .ui-state-active loses by one class.
 */
.wc-bookings-date-picker .ui-datepicker td a.ui-state-active,
.wc-bookings-date-picker .ui-datepicker td a.ui-state-active.ui-state-hover,
.wc-bookings-date-picker .ui-datepicker td.bookable-range a.ui-state-default {
	color: #fff;
	text-decoration: none !important;
}

/*
 * Fully booked: stock is #792d2a, a dark red. Nothing here is an error, just an
 * unavailable day, so it recedes to the page rather than shouting. The
 * line-through the plugin adds carries the meaning.
 */
.wc-bookings-date-picker .ui-datepicker td.fully_booked:not(.not_bookable_by_rules) span,
.wc-bookings-date-picker .ui-datepicker td.fully_booked:not(.not_bookable_by_rules) a {
	background-color: transparent !important;
	border-color: var(--ink-12) !important;
	color: var(--ink-60) !important;
	text-shadow: none;
}

/* Days from the neighbouring month sit at #eee — a cool grey block. */
.wc-bookings-date-picker .ui-datepicker td.ui-datepicker-other-month {
	background: transparent;
	opacity: 0.35;
}

/* The running cost panel: #eee cool grey against the cream page. */
.wc-bookings-booking-form .wc-bookings-booking-cost {
	background: var(--tan);
	border-top: 1px solid var(--ink-12);
}

.wc-bookings-booking-form {
	border-color: var(--ink-12);
}

/*
 * The submit button takes its colour from the .alt rule in the WooCommerce
 * section above. Only the gated state is set here, and it is hung on the
 * cancellation-policy plugin's own .vcp-consent-blocked class rather than on
 * [disabled]: Bookings rewrites this element's disabled state on every cost
 * recalculation, so a rule keyed to Bookings' own state class would be undone
 * the moment the date changes.
 */
.woocommerce button.button.alt.disabled.vcp-consent-blocked,
.woocommerce button.button.alt.disabled.vcp-consent-blocked:hover,
.woocommerce button.button.alt:disabled.vcp-consent-blocked,
.woocommerce button.button.alt:disabled.vcp-consent-blocked:hover {
	background-color: var(--accent);
	color: #fff;
	cursor: not-allowed;
	opacity: 0.45;
}

/*
 * Price. Stock renders #958E09, a mustard-olive belonging to no palette here.
 */
.woocommerce div.product p.price,
.woocommerce div.product span.price {
	color: var(--ink);
	font-weight: 700;
}

/*
 * Product gallery. The single-product layout is one column here, so the gallery
 * image runs the full content width — a 1:1 source therefore renders over 600px
 * tall and pushes the title, price and booking form entirely below the fold.
 * That is tolerable for a photograph and absurd for the placeholder, which is
 * currently every product. Cap the height and crop; when real photography
 * lands this keeps the fold intact rather than needing to be revisited.
 */
.woocommerce div.product .woocommerce-product-gallery {
	margin-bottom: clamp( 1.5rem, 3vw, 2.5rem );
	max-height: 420px;
	overflow: hidden;
}

/*
 * `div.images` is in the selector for specificity, not structure: stock
 * woocommerce.css has `.woocommerce div.product div.images img { height: auto }`,
 * which outranked the plain gallery selector and let the image render at its
 * full 1:1 height inside the 420px box — invisible while every product showed
 * the flat tan placeholder, obvious once real photos showed only their top
 * third. Leave it in.
 */
.woocommerce div.product div.images.woocommerce-product-gallery img {
	height: 420px;
	object-fit: cover;
	width: 100%;
}

/* ==========================================================================
   Waiver (venue-waiver plugin)

   The waiver page is an ordinary page carrying a shortcode, so it gets none of
   WooCommerce's page-specific styling for free. The plugin wraps its output in
   `.woocommerce` and enqueues `woocommerce-general` there, which is what makes
   the login and registration forms match the ones on /my-account/ — including
   the Show/Hide password toggle. Only what is genuinely specific to the waiver
   is styled here.

   §11: square corners, hairline borders, no shadows, no new colours.
   ========================================================================== */

.vw-waiver {
	max-width: 46rem;
}

.vw-waiver__title {
	margin: 0 0 1.25rem;
}

/* The document itself. Bordered rather than filled: this is a legal text to be
   read, and a tan panel would read as a pull-quote or an aside — something
   skippable — which is the one impression it must not give. */
.vw-waiver__document {
	padding: clamp( 1.25rem, 3vw, 2rem );
	border: 1px solid var(--ink-12);
	border-left: 3px solid var(--ink);
	background: var(--bg);
}

.vw-waiver__document > *:first-child {
	margin-top: 0;
}

.vw-waiver__document > *:last-child {
	margin-bottom: 0;
}

.vw-waiver__version {
	margin: 0.75rem 0 2rem;
	font-family: var(--mono);
	font-size: 0.78rem;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--ink-60);
}

.vw-waiver__account-note,
.vw-waiver__revised,
.vw-waiver__password-note {
	color: var(--ink-60);
}

/* The revision notice is the one thing on the page a returning customer must
   not skim past — they have signed before and will assume they are done. */
.vw-waiver__revised {
	padding: 1rem 1.25rem;
	border-left: 3px solid var(--accent);
	background: var(--tan);
	color: var(--ink);
}

/* The acceptance row is the action, so it is given the weight of one. */
.vw-waiver__agree {
	margin: 1.5rem 0;
	padding: 1rem 1.25rem;
	border: 1px solid var(--ink-12);
	background: var(--tan);
}

.vw-waiver__agree label {
	display: flex;
	gap: 0.75rem;
	align-items: flex-start;
	margin: 0;
	font-weight: 600;
	cursor: pointer;
}

.vw-waiver__agree input[type="checkbox"] {
	flex: none;
	width: 1.15rem;
	height: 1.15rem;
	margin-top: 0.1rem;
	accent-color: var(--accent);
}

.vw-waiver__form {
	margin: 0 0 2rem;
}

/* The "already have an account?" form is an alternative to the one above it,
   not a continuation, so it is separated rather than merely spaced. */
.vw-waiver__login {
	margin-top: 3rem;
	padding-top: 2rem;
	border-top: 1px solid var(--ink-12);
}

.vw-waiver__login h3 {
	margin: 0 0 1.25rem;
}

.vw-waiver__reread {
	margin-top: 2.5rem;
}

.vw-waiver__reread summary {
	cursor: pointer;
	font-family: var(--mono);
	font-size: 0.78rem;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--ink-60);
}

.vw-waiver__reread .vw-waiver__document {
	margin-top: 1rem;
}

/* ==========================================================================
   Paid Memberships Pro

   PMPro 3.x ships its own design system. Every rule in its variation_1.css is
   nested inside `.pmpro { … }`, so a flat `.pmpro_btn` selector here (0,1,0)
   loses to its effective `.pmpro .pmpro_btn` (0,2,0) no matter how late this
   sheet loads. Rather than escalate with !important, feed PMPro's custom
   properties our tokens, and match its specificity for the handful of values
   it hard-codes instead of reading from a variable.

   The accent is NOT set here. PMPro prints an inline `<style id="pmpro_colors">`
   in wp_head after this sheet, re-declaring `:root`, so any accent override
   here would be dead code. It is supplied through the `option_pmpro_colors`
   filter in functions.php instead, which also lets PMPro derive its own hover
   variation. The variables below are declared only in PMPro's stylesheets, so
   this later sheet wins them on document order.

   Left alone on purpose: --pmpro--color--base stays white, because PMPro uses
   it for button *text* as well as card fill. The card is retinted below.
   ========================================================================== */

:root {
	--pmpro--base--border-radius: var(--radius);
	--pmpro--color--border: var(--ink-12);
	--pmpro--box-shadow: none;
}

/* box-shadow and typography are literals in PMPro's button rule, not variables. */
.pmpro .pmpro_btn {
	box-shadow: none;
	font-family: var(--sans);
	font-size: 0.9rem;
	font-weight: 600;
}

.pmpro .pmpro_btn:hover,
.pmpro .pmpro_btn:focus {
	background-color: var(--accent-hover);
	border-color: var(--accent-hover);
	color: #fff;
}

/* A white, rounded, shadowed card is three §11 violations at once. */
.pmpro .pmpro_card {
	background: var(--bg);
	border: 1px solid var(--ink-12);
	border-radius: 0;
	box-shadow: none;
}

/* The levels table paints itself with --pmpro--color--base, which has to stay
   white for button text. Let the cream card show through instead. */
.pmpro .pmpro_table,
.pmpro .pmpro_table th,
.pmpro .pmpro_table td {
	background: transparent;
}

/* ==========================================================================
   Contact page + Contact Form 7

   The theme had no text-input styling of its own — every form until now came
   from WooCommerce or PMPro with their own CSS. CF7 ships almost none, so the
   fields are built here from the tokens. Kept deliberately flat: square
   corners, hairline borders, no shadows or gradients (§11).
   ========================================================================== */

.contact-grid {
	display: grid;
	grid-template-columns: minmax( 0, 1fr ) minmax( 0, 1.25fr );
	gap: clamp( 2.5rem, 6vw, 5rem );
	align-items: start;
}

/*
 * .entry-content's rhythm rules (`> * + *` and `> h2`) only reach direct
 * children, and these live one level deeper inside the grid columns. Without
 * restating them the whole column collapses into unspaced text.
 */
.contact-details > * + *,
.contact-form > * + * {
	margin-top: 1rem;
}

.contact-details > h2,
.contact-form > h2 {
	margin-top: 2.5rem;
}

/* Both columns start level with each other at the top of the grid. */
.contact-grid > * > h2:first-child {
	margin-top: 0;
}

.contact-hours {
	margin-top: 0.75rem;
}

.contact-hours-row {
	display: flex;
	justify-content: space-between;
	gap: 1.5rem;
	padding: 0.6rem 0;
	border-bottom: 1px solid var(--ink-12);
}

.contact-hours-row dt {
	font-weight: 500;
}

.contact-hours-row dd {
	color: var(--ink-60);
	font-family: var(--mono);
	font-size: 0.85rem;
	white-space: nowrap;
}

.wpcf7 label {
	display: block;
	font-size: 0.9rem;
	font-weight: 500;
}

/* CF7 wraps every field in a <p>, and the reset leaves those with no margin,
   so the fields stack flush. The submit gets a little extra to read as the end
   of the form rather than another field. */
.wpcf7-form > p + p {
	margin-top: 1.5rem;
}

.wpcf7-form > p:last-of-type {
	margin-top: 2rem;
}

.wpcf7 input[type="text"],
.wpcf7 input[type="email"],
.wpcf7 input[type="tel"],
.wpcf7 select,
.wpcf7 textarea {
	display: block;
	width: 100%;
	margin-top: 0.4rem;
	padding: 0.7rem 0.85rem;
	border: 1px solid var(--ink-12);
	border-radius: 0;
	background: var(--bg);
	color: var(--ink);
	font-size: 1rem;
}

/* CF7 emits rows="10", which renders far taller than this column needs; the
   explicit height wins and the field stays user-resizable. */
.wpcf7 textarea {
	height: 10rem;
	min-height: 6rem;
	resize: vertical;
}

.wpcf7 input:hover,
.wpcf7 select:hover,
.wpcf7 textarea:hover {
	border-color: var(--ink-60);
}

/* CF7 renders the submit as a bare <input type="submit">, so it cannot pick up
   the .btn classes — it is matched on the element instead. */
.wpcf7 input[type="submit"] {
	display: inline-block;
	padding: 0.85rem 1.6rem;
	border: 0;
	border-radius: var(--radius);
	background: var(--accent);
	color: #fff;
	font-weight: 600;
	cursor: pointer;
}

.wpcf7 input[type="submit"]:hover {
	background: var(--accent-hover);
}

/* CF7's own status/validation output. Its default palette is a set of greens,
   reds and yellows that belong to no part of this design system. */
.wpcf7 .wpcf7-not-valid-tip {
	margin-top: 0.35rem;
	color: var(--accent);
	font-size: 0.85rem;
}

.wpcf7 .wpcf7-response-output {
	margin: 1.5rem 0 0;
	padding: 0.9rem 1.1rem;
	background: var(--tan);
	font-size: 0.95rem;
}

/*
 * CF7 colours this box per state with its own green/red/orange, at
 * `.wpcf7 form.sent .wpcf7-response-output` — one class more specific than a
 * plain `.wpcf7 .wpcf7-response-output`, so the state selectors have to be
 * restated here to beat it.
 *
 * The state still has to be legible without colour, so the two cases differ by
 * weight rather than by adding a second saturated accent (§11): a settled dark
 * rule for success, the orange for anything needing attention.
 */
.wpcf7 form.sent .wpcf7-response-output {
	border: 1px solid var(--ink-12);
	border-left: 3px solid var(--ink);
}

.wpcf7 form.invalid .wpcf7-response-output,
.wpcf7 form.unaccepted .wpcf7-response-output,
.wpcf7 form.payment-required .wpcf7-response-output,
.wpcf7 form.failed .wpcf7-response-output,
.wpcf7 form.aborted .wpcf7-response-output,
.wpcf7 form.spam .wpcf7-response-output {
	border: 1px solid var(--ink-12);
	border-left: 3px solid var(--accent);
}

/* ==========================================================================
   Responsive (§7)
   ========================================================================== */

@media ( max-width: 760px ) {
	.contact-grid {
		grid-template-columns: 1fr;
	}

	/* Stacked, the form's heading needs the same separation from the details
	   above it that the two columns get side by side. */
	.contact-form h2:first-child {
		margin-top: 2.5rem;
	}

	/*
	 * `bottom: auto` left the drawer only as tall as its links — a ~325px panel
	 * with the live page still painted beneath it, so the homepage's own hero
	 * CTA sat directly under the drawer's and the two read as one menu. The
	 * drawer has to own everything below the bar.
	 */
	.nav-links {
		position: fixed;
		inset: var(--nav-h) 0 0 0;
		flex-direction: column;
		align-items: flex-start;
		gap: 1.25rem;
		background: var(--bg);
		padding: 1.75rem var(--gutter) 2rem;

		/*
		 * Under .nav (50) so the bar and its close button stay above the panel,
		 * over page content. The myCred launcher sits at 99999 and cannot be
		 * out-stacked sanely — it is hidden via body.nav-open below instead.
		 */
		z-index: 40;

		/* Short viewports (landscape phones) must still reach the last link. */
		overflow-y: auto;
		overscroll-behavior: contain;

		/*
		 * The drawer starts at top:var(--nav-h), so lifting it by its own height
		 * alone still leaves it hanging into the bar. A percentage translate is
		 * relative to the element, not the offset — `-120%` left ~37px of drawer
		 * (and the orange CTA's edge) painted over the wordmark. Clear the offset
		 * explicitly so its bottom lands exactly on the viewport's top edge.
		 */
		transform: translateY( calc( -100% - var(--nav-h) ) );

		/* Also keeps the hidden links out of the tab order. Delayed on close so
		   the slide-out stays visible; immediate on open. */
		visibility: hidden;
		transition: transform 0.35s ease, visibility 0s linear 0.35s;
	}

	.nav-links.open {
		transform: none;
		visibility: visible;
		transition: transform 0.35s ease, visibility 0s;
	}

	/*
	 * nav.js sets this alongside .open. Without it a drag over the drawer scrolls
	 * the page behind it, which is what made the panel read as part of the page
	 * rather than over it. Scoped to the breakpoint: above 760px the drawer
	 * styles do not apply and the class must not lock anything.
	 */
	body.nav-open {
		overflow: hidden;
	}

	/*
	 * The drawer is a child of .nav, and `backdrop-filter` on an ancestor makes
	 * that ancestor the containing block for `position: fixed` descendants. So
	 * the bar's blur silently re-anchored `inset: var(--nav-h) 0 0 0` to the
	 * 76px-tall bar instead of the viewport: top and bottom cancelled out, the
	 * drawer collapsed to its own padding (60px), and only the first link was
	 * visible with the page showing through underneath. It looked like a
	 * subpage-only bug because interior pages are solid from the first paint,
	 * while the home page only picks the blur up past 20px of scroll — the same
	 * break, just harder to catch.
	 *
	 * Dropping the blur while the drawer is open restores the viewport as the
	 * containing block. Nothing is lost visually: the drawer covers the page, so
	 * there is no longer anything behind the bar worth blurring. Any future
	 * filter/transform/perspective on .nav would break this the same way — the
	 * drawer must stay free of filtered ancestors.
	 */
	body.nav-open .nav {
		background: var(--bg);
		backdrop-filter: none;
		-webkit-backdrop-filter: none;
	}

	/*
	 * The launcher is fixed at z-index 99999, so it paints over the open drawer
	 * no matter what the drawer is stacked at. Take it out while the drawer is
	 * open rather than fighting the stacking order.
	 */
	body.nav-open #mycred-loyalty-widget-root {
		visibility: hidden;
	}

	.menu-toggle {
		display: flex;
	}

	/* The links leave the flow for the drawer here, so the cart has to claim
	   the free space itself or space-between strands it mid-bar. */
	.nav-cart {
		margin-left: auto;
		margin-right: 6px;
		padding: 5px;
	}

	/* Tighter padding leaves less corner to hang the badge in. */
	.nav-cart-count {
		top: -3px;
		right: -7px;
	}

	/*
	 * The wordmark is `nowrap`, so a visible cart plus the toggle pushed the
	 * toggle past the right edge (off-screen at 320px, into the gutter at 390).
	 * Scale the lockup down only while the cart is showing; an empty cart keeps
	 * the full-size wordmark.
	 */
	.nav:has( .nav-cart:not([hidden]) ) .brand {
		font-size: clamp( 0.72rem, 3.9vw, 1.15rem );
	}

	/* Stack the hero: text first, logo beneath it, both left-aligned. */
	.hero-top {
		flex-direction: column;
		align-items: flex-start;
		gap: clamp( 2.5rem, 8vw, 3.5rem );
	}

	.hero-logo {
		width: clamp( 12rem, 54vw, 18rem );
	}

	/* .partner--reverse is included explicitly so it collapses to `column`
	   too, not `column-reverse`: the desktop flip is purely visual rhythm, and
	   on one column the copy must always come before its logo. */
	.beanz,
	.partner,
	.partner--reverse {
		flex-direction: column;
		align-items: flex-start;
		gap: clamp( 2rem, 8vw, 3rem );
	}

	.beanz-logo,
	.partner-media {
		width: clamp( 12rem, 54vw, 18rem );
	}

	/* List first, then the workstation photo at full width. */
	.media-split {
		flex-direction: column;
	}

	.media-split-media {
		width: 100%;
	}

	.pillars {
		grid-template-columns: 1fr;
		gap: 1.5rem;
	}

	.pillar-d {
		max-width: none;
	}
}

/* ==========================================================================
   Reduced motion (§8) — skip the intro, show the resolved headline at once.
   ========================================================================== */

@media ( prefers-reduced-motion: reduce ) {
	html {
		scroll-behavior: auto;
	}

	.intro {
		display: none !important;
	}

	.headline-final,
	.hero-sub,
	.hero-cta,
	.pillars {
		opacity: 1 !important;
		transform: none !important;
		transition: none !important;
	}

	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
	}
}
