/*
 * base.css — tokens, reset, container, notice bar, site header, shared pill button.
 *
 * Figma: "Hero" frame node 1:811 (Top Header 1:872, header row 1:813).
 * Loaded on every page; every other stylesheet depends on this one.
 */

/*
 * Display type is scaled to this site's 1280px column. The artboard sets its
 * sizes against a 1634px content width, so every heading here is that size
 * multiplied by 1280/1634 (~0.78) at the top of its clamp, with the slope
 * recomputed so the curve still reaches that maximum at 1920. Body copy and
 * button labels are untouched — 16px is a floor, not a scaled value.
 */

/* ---------------------------------------------------------------- Font */

/* One request, latin subset only, swap so text paints before the font lands. */
@font-face {
	font-family: "Marcellus";
	src: url("../fonts/marcellus-latin-400.woff2") format("woff2");
	font-weight: 400;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: "Lato";
	src: url("../fonts/lato-latin-400.woff2") format("woff2");
	font-weight: 400;
	font-style: normal;
	font-display: swap;
}

/* -------------------------------------------------------------- Tokens */

:root {
	--bg: #ffffff;
	--surface-light: #f5f4f1;
	--surface: #10221f;

	--text: #14171a;
	/* 6.1:1 on --bg and 5.6:1 on --surface-light, so it stays legible on both
	   light surfaces. A lighter grey would pass on white and fail on the off-white. */
	--text-muted: #5c6360;

	--white: #f1f0f0;

	/* Booking-card palette, measured off the artboard (node 45:2105):
	   the card body, the 20px light frame around it, and the deep slate used
	   for the submit-button circle and the client count badge. */
	--forest-ink: #12312c;
	--surface-deep: #1f3d3f;
	/* Figma calls this "bg color". It is the light surface behind the intro
	   section, the booking-card frame and the approach tabs. */
	--surface-frame: #dde7dd;

	/* Figma "Warm Sand" — the card copy that sits on a photograph. */
	--warm-sand: #f6f2ea;
	/* Figma "light" — hairline rules drawn over imagery. */
	--hairline: #afafaf;

	/* Type colours on the light surface. Figma "Black" and "Gray". */
	--ink: #1c1c1c;
	/* 6.7:1 on --surface-frame — checked against that surface, not white,
	   because it never appears on white. */
	--ink-muted: #4d4d4d;

	/* Brand teal. 3.7:1 against white — fine as a FILL behind white text and for
	   large display type, but never as body copy on a light background. */
	--accent: #00967e;
	--accent-hover: #00b294;

	/* The notice bar is white 16px text on a solid fill, which needs 4.5:1.
	   --accent only reaches 3.7:1 there, so the bar uses this deepened teal
	   (5.0:1). It reads as the same brand colour at a glance. */
	--accent-deep: #007e6a;

	/* --accent as text on the hero photograph measures ~2.8:1 — under even the
	   3:1 large-text bar. This lightened teal clears 4.4:1 over the hero scrim,
	   so the headline emphasis is legible without repainting every button. */
	--accent-on-dark: #2fb89c;

	--font: "Marcellus", Georgia, "Times New Roman", serif;
	/* Lato carries the running copy and every form control. Marcellus is a
	   display face and is unreadable at 16px in a long paragraph. */
	--font-body: "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

	/* One measure for the whole site. The Figma artboard lays the header and
	   hero out to a wider 1634px box, but everything here is deliberately
	   pulled in to 1280px so every section shares a single content edge. */
	--container: 1280px;
	--gutter: clamp(16px, 2.2vw, 24px);

	--notice-h: 59px;
	/*
	 * The header's REAL height, not a nominal one: .siteHeader cancels
	 * exactly this much with a negative margin so the banner starts
	 * underneath it, so any pixel this understates shows through as a strip
	 * of page background between the notice bar and the banner.
	 *
	 * It has to be an expression rather than a number because the header is
	 * as tall as its logo, and the logo is fluid — .headerLogoImg's clamped
	 * width, scaled by that image's own 212x180 aspect ratio, plus
	 * .headerInner's 10px of padding top and bottom, over a 90px floor the
	 * shorter end of that clamp never reaches. A flat 90px was right only
	 * below ~1500px wide; above it the logo grows and the header was
	 * silently up to 20px taller than the margin giving that height back.
	 * Keep this in step with .headerLogoImg's own clamp.
	 */
	--header-h: max(90px, calc(clamp(72px, 5.5vw, 106px) * 180 / 212 + 20px));

	--radius-pill: 30px;
	--ease: cubic-bezier(0.4, 0, 0.2, 1);
}

/* --------------------------------------------------------------- Reset */

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

html {
	-webkit-text-size-adjust: 100%;
	scroll-behavior: smooth;
}

body {
	margin: 0;
	background: var(--bg);
	color: var(--text);
	font-family: var(--font);
	font-size: 16px;
	line-height: 1.6;
	-webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
h4,
p,
figure,
ul,
ol {
	margin: 0;
}

ul,
ol {
	padding: 0;
	list-style: none;
}

img,
picture,
svg {
	display: block;
	max-width: 100%;
}

img {
	height: auto;
}

a {
	color: inherit;
	text-decoration: none;
}

button {
	margin: 0;
	padding: 0;
	border: 0;
	background: none;
	color: inherit;
	font: inherit;
	cursor: pointer;
}

/* A visible focus ring on everything keyboard-reachable. Never removed. */
:focus-visible {
	outline: 2px solid var(--accent-on-dark);
	outline-offset: 3px;
	border-radius: 2px;
}

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

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

/* ------------------------------------------------------------ Utilities */

/* Visually hidden but read aloud — used for icon-only control labels.
   .screen-reader-text is the same thing under the name WordPress core markup
   uses; without it the pagination nav's own heading renders as visible text. */
.srOnly,
.screen-reader-text {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

.skipLink {
	position: absolute;
	top: 0;
	left: 50%;
	z-index: 999;
	transform: translate(-50%, -110%);
	padding: 12px 24px;
	background: var(--accent-deep);
	color: #fff;
	font-size: 15px;
	border-radius: 0 0 8px 8px;
	transition: transform 0.2s var(--ease);
}

.skipLink:focus {
	transform: translate(-50%, 0);
}

/* The one layout primitive. min() rather than max-width plus padding so the
   content box is exactly --container once the viewport clears 1280 + gutters. */
.headerInner,
.footerInner,
.pageInner,
.heroInner {
	width: min(100% - (var(--gutter) * 2), var(--container));
	margin-inline: auto;
}

.pageInner {
	padding-block: clamp(48px, 7vw, 96px);
}

/* ---------------------------------------------------------- Notice bar */

.noticeBar {
	background: var(--accent-deep);
	color: #fff;
}

.noticeBarInner {
	width: min(100% - (var(--gutter) * 2), var(--container));
	margin-inline: auto;
	min-height: var(--notice-h);
	display: flex;
	align-items: center;
	justify-content: center;
	padding-block: 10px;
}

.noticeBarText {
	font-size: clamp(0.8125rem, 0.72rem + 0.4vw, 1rem);
	line-height: 1.45;
	text-align: center;
}

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

/*
 * Sticky, not absolute: the header stays pinned to the viewport top while
 * scrolling, on every template. Sticky keeps its flow footprint though (unlike
 * absolute), which would push the hero down by the header's own height — the
 * negative margin below gives that height back so the hero still starts
 * underneath the header and shows through it, exactly as before.
 */
.siteHeader {
	position: sticky;
	top: 0;
	left: 0;
	right: 0;
	z-index: 20;
	color: var(--white);
	margin-bottom: calc(-1 * var(--header-h));
	transition: background-color 0.35s ease;
}

/*
 * The photograph behind the header has bright sky in it. This scrim guarantees
 * the nav keeps its contrast ratio wherever the image happens to be light,
 * which a fixed text colour alone cannot do.
 */
.siteHeader::before {
	content: "";
	position: absolute;
	/* Starts at the header's own top edge. It used to begin --notice-h above
	   it, which painted this dark gradient straight over the teal notice bar
	   and made the bar look like the header was sitting on top of it. */
	inset: 0 0 auto 0;
	height: calc(var(--header-h) + 60px);
	background: linear-gradient(to bottom, rgba(6, 26, 22, 0.62), rgba(6, 26, 22, 0));
	pointer-events: none;
	transition: opacity 0.35s ease;
}

/*
 * The header starts transparent over the banner photo; header.js only marks it
 * .isScrolled once that banner has scrolled fully out of view (see header.js),
 * at which point it becomes a solid, compact bar — opacity/background/size all
 * transition so the pinned bar eases into place rather than snapping.
 */
.siteHeader.isScrolled::before {
	opacity: 0;
}

.siteHeader.isScrolled {
	background: var(--surface-deep);
}

.headerInner {
	position: relative;
	z-index: 1;
	display: flex;
	align-items: center;
	gap: 24px;
	min-height: var(--header-h);
	padding-block: 10px;
	transition: min-height 0.35s ease, padding-block 0.35s ease;
}

/* The pinned header reads as a compact utility bar rather than the full
   hero-sized band it overlays the photo with. */
.siteHeader.isScrolled .headerInner {
	min-height: 60px;
	padding-block: 5px;
}

.headerLogo {
	flex: 0 0 auto;
	display: block;
}

.headerLogoImg {
	width: clamp(72px, 5.5vw, 106px);
	height: auto;
	transition: width 0.35s ease;
}

.siteHeader.isScrolled .headerLogoImg {
	width: 60px;
}

/* The CTA shrinks to match the rest of the compact pinned header. */
.headerCta {
	transition: min-height 0.35s ease;
}

.headerCta .pillBtnLabel {
	transition: font-size 0.35s ease;
}

.headerCta .pillBtnCircle {
	transition: width 0.35s ease, height 0.35s ease;
}

.siteHeader.isScrolled .headerCta {
	min-height: 36px;
}

.siteHeader.isScrolled .headerCta .pillBtnLabel {
	font-size: 13px;
}

.siteHeader.isScrolled .headerCta .pillBtnCircle {
	width: 30px;
	height: 30px;
}

/*
 * The nav fills everything between the logo and the container's right edge, so
 * the CTA — its last child — lands flush on that edge, as in Figma. Centring
 * is done by the auto margins on .navList below, not on this element: putting
 * them here centred the nav AND the CTA as one group and left the button
 * ~160px short of the right edge.
 */
.headerNav {
	flex: 1 1 auto;
	display: flex;
	align-items: center;
	justify-content: flex-end;
	gap: clamp(24px, 3vw, 64px);
}

/* Auto margins absorb the free space either side, so the link row sits centred
   in the gap between the logo and the CTA. */
.navList {
	display: flex;
	align-items: center;
	gap: clamp(20px, 2.1vw, 40px);
	margin-inline: auto;
}

.navItem {
	position: relative;
	display: flex;
	align-items: center;
	gap: 6px;
}

.navLink {
	font-size: 16px;
	line-height: 1.25;
	color: rgba(241, 240, 240, 0.85);
	transition: color 0.18s var(--ease);
}

.navLink:hover,
.navLink:focus-visible,
.navLink.isCurrent {
	color: var(--white);
}

.navToggle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 20px;
	height: 20px;
	color: rgba(241, 240, 240, 0.85);
	transition: transform 0.2s var(--ease), color 0.18s var(--ease);
}

.navToggle:hover,
.navToggle:focus-visible {
	color: var(--white);
}

.navToggle[aria-expanded="true"] {
	transform: rotate(180deg);
	color: var(--white);
}

/*
 * The dropdown sits 18px below the item, but header.js opens/closes it on
 * mouseenter/mouseleave of .navItem — with nothing rendered in that 18px gap,
 * the pointer briefly leaves every hoverable element while crossing it, which
 * fires mouseleave and closes the menu before it ever reaches the submenu.
 * This invisible bridge fills the gap so .navItem stays "hovered" the whole
 * way down.
 */
.navItem.hasChildren::after {
	content: "";
	position: absolute;
	top: 100%;
	left: 0;
	right: 0;
	height: 18px;
}

.navSubmenu {
	position: absolute;
	top: calc(100% + 18px);
	left: -20px;
	min-width: 208px;
	padding: 12px 0;
	background: var(--surface);
	border-radius: 10px;
	box-shadow: 0 18px 40px rgba(0, 0, 0, 0.28);
}

.navSubmenu .navItem {
	display: block;
}

.navSubmenu .navLink {
	display: block;
	padding: 10px 22px;
}

.navBurger {
	display: none;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	margin-left: auto;
	color: var(--white);
}

.navCloseIcon {
	display: none;
}

.navBurger[aria-expanded="true"] .navBurgerIcon {
	display: none;
}

.navBurger[aria-expanded="true"] .navCloseIcon {
	display: block;
}

/* ------------------------------------------------- Pill button (shared) */

/*
 * One component, two call sites: the header CTA (Figma 1:858, 250x48) and the
 * hero CTA (48:2574, 478x48). Identical markup and identical class names, so
 * they share these rules rather than each owning a near-identical copy.
 * Geometry comes straight from the artboard: 48px tall, 30px radius, a 40px
 * circle inset 4px from the right edge.
 */
.pillBtn {
	display: inline-flex;
	align-items: center;
	gap: 13px;
	min-height: 48px;
	padding: 4px 4px 4px 24px;
	border: 1px solid var(--white);
	border-radius: var(--radius-pill);
	color: var(--white);
	transition: background-color 0.2s var(--ease), border-color 0.2s var(--ease);
}

.pillBtn:hover,
.pillBtn:focus-visible {
	background: rgba(241, 240, 240, 0.12);
}

.pillBtnLabel {
	font-size: 16px;
	line-height: 1.2;
}

.pillBtnCircle {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	border-radius: 50%;
	background: var(--accent);
	color: var(--white);
	transition: background-color 0.2s var(--ease), transform 0.2s var(--ease);
}

.pillBtn:hover .pillBtnCircle,
.pillBtn:focus-visible .pillBtnCircle {
	background: var(--accent-hover);
	transform: translateX(2px);
}

/* The dark-on-light variant used by the 404 and any future light section. */
.pillBtn.isDark {
	border-color: rgba(20, 23, 26, 0.25);
	color: var(--text);
}

.pillBtn.isDark:hover,
.pillBtn.isDark:focus-visible {
	background: rgba(20, 23, 26, 0.05);
}

/*
 * Filled variant — the intro's "Learn More" (node 45:1996).
 *
 * The artboard fills it with --accent, but white on that measures 3.7:1 and
 * this label is 16px, so it uses the deepened teal (5.0:1) like the notice bar
 * and the booking form's submit.
 */
.pillBtn.isSolid {
	border-color: transparent;
	background: var(--accent-deep);
	color: #fff;
}

.pillBtn.isSolid:hover,
.pillBtn.isSolid:focus-visible {
	background: var(--accent);
}

/* Outlined variant on a light surface — the intro's phone button (45:2010). */
.pillBtn.isMuted {
	border-color: var(--ink-muted);
	color: var(--ink-muted);
}

.pillBtn.isMuted:hover,
.pillBtn.isMuted:focus-visible {
	background: rgba(28, 28, 28, 0.06);
	border-color: var(--ink);
	color: var(--ink);
}

/*
 * No trailing circle — the service-area CTA (node 45:1495) is a plain filled
 * pill. Without this the 4px right padding that makes room for the circle
 * leaves the label visibly off-centre.
 */
.pillBtn.isPlain {
	gap: 0;
	padding-right: 24px;
}

/* Dark circle, used when the button itself is already teal. */
.pillBtnCircle.isInk {
	background: var(--surface-deep);
}

.pillBtn:hover .pillBtnCircle.isInk,
.pillBtn:focus-visible .pillBtnCircle.isInk {
	background: var(--forest-ink);
}

/*
 * The drawer's own CTA (header.php) — a separate element rather than this one
 * restyled, since it needs a plain link to Contact even on templates where
 * this CTA opens the consultation modal. Hidden here — after .pillBtn, whose
 * own display: inline-flex otherwise wins the cascade at equal specificity —
 * and shown again in its place by the mobile block below.
 */
.headerCtaMobile {
	display: none;
}

/* ------------------------------------------------- Generic page content */

/* The page heading lives in the banner band (.heroTitle), so what is left here
   is only the body copy that follows it. */
.pageIntro,
.pageContent {
	margin-top: 20px;
	max-width: 68ch;
	color: var(--text-muted);
}

.pageActions {
	margin-top: 32px;
}

.textLink {
	color: var(--accent-deep);
	text-decoration: underline;
	text-underline-offset: 3px;
}

/* ---------------------------------------------------- Header — tablet */

@media (max-width: 1200px) {
	.navList {
		gap: 20px;
	}

	.navLink,
	.pillBtnLabel {
		font-size: 15px;
	}

	/* The header CTA label is long; below the desktop breakpoint it collapses
	   to the circle alone and the label is kept for screen readers only. */
	.headerCta.isCompact {
		padding-left: 4px;
		gap: 0;
	}

	.headerCta.isCompact .pillBtnLabel {
		position: absolute;
		width: 1px;
		height: 1px;
		margin: -1px;
		overflow: hidden;
		clip-path: inset(50%);
		white-space: nowrap;
	}
}

/* ---------------------------------------------------- Header — mobile */

@media (max-width: 960px) {
	.navBurger {
		display: inline-flex;
		/* Stays above the drawer it opens (see .headerNav below) — both are
		   positioned children of .headerInner's own stacking context (that
		   context is what .headerInner's own z-index: 1 establishes), so the
		   higher value here is what keeps the burger/close toggle clickable
		   instead of sliding underneath the panel it controls. */
		position: relative;
		z-index: 3;
	}

	/*
	 * While the drawer is open, the toggle (now showing its close icon)
	 * leaves its spot in the header row and pins to the drawer's own top-right
	 * corner instead — the header row it normally sits in is not fixed itself,
	 * only sticky, so at the top of a page the still-visible notice bar pushes
	 * that row down by a variable amount .navList's own padding cannot predict.
	 * Anchoring to the viewport instead of the row sidesteps that entirely.
	 */
	.headerInner:has(.headerNav.isOpen) .navBurger {
		position: fixed;
		top: 16px;
		right: 16px;
	}

	/*
	 * A full-height panel fixed to the right edge, slid off-screen and drawn
	 * back in with a transform rather than shown/hidden with display — a
	 * transform is what makes the slide an animation instead of a snap.
	 * visibility follows one transition step behind transform on the way out
	 * only, so the panel is still painted (and slides) for the full 0.35s
	 * before it leaves the accessibility tree and the tab order.
	 *
	 * 90% wide with a cap: on this site's largest supported phone width the
	 * artboard's own gutter would otherwise stretch to an unreasonable
	 * measure for a stack of nav links.
	 */
	.headerNav {
		position: fixed;
		inset: 0 0 0 auto;
		width: 90%;
		max-width: 380px;
		display: flex;
		flex-direction: column;
		align-items: stretch;
		gap: 0;
		margin: 0;
		padding: 0;
		background: var(--surface);
		transform: translateX(100%);
		visibility: hidden;
		transition: transform 0.35s var(--ease), visibility 0s linear 0.35s;
		z-index: 2;
		overflow-y: auto;
	}

	.headerNav.isOpen {
		transform: translateX(0);
		visibility: visible;
		transition: transform 0.35s var(--ease);
	}

	/*
	 * The scrim behind the drawer (markup in header.php, toggled in step with
	 * the panel by header.js). Fixed and full-viewport regardless of the
	 * drawer's own 90% width, so the remaining 10% sliver of page reads as
	 * dimmed-and-inert rather than a bright gap beside the panel.
	 */
	.navBackdrop {
		position: fixed;
		inset: 0;
		z-index: 1;
		background: rgba(6, 26, 22, 0.6);
		opacity: 0;
		transition: opacity 0.35s var(--ease);
	}

	.navBackdrop.isOpen {
		opacity: 1;
	}

	.navList {
		flex: 1 1 auto;
		flex-direction: column;
		align-items: stretch;
		gap: 0;
		overflow-y: auto;
		/* Clears the close toggle, now fixed at top: 16px regardless of the
		   header row beneath it (see .navBurger above) — 76px is that 16px
		   plus the toggle's own 44px plus a comfortable gap. */
		padding: 76px 0 20px;
		/* Cancels the desktop centring — in a stacked panel auto margins would
		   shrink the list to its content width and float it in the middle. */
		margin-inline: 0;
	}

	.navItem {
		flex-wrap: wrap;
		justify-content: space-between;
		padding-inline: var(--gutter);
		border-bottom: 1px solid rgba(241, 240, 240, 0.1);
	}

	.navLink {
		flex: 1 1 auto;
		padding-block: 14px;
		font-size: 16px;
	}

	.navToggle {
		width: 44px;
		height: 44px;
	}

	.navSubmenu {
		position: static;
		flex-basis: 100%;
		min-width: 0;
		padding: 0 0 10px;
		background: none;
		border-radius: 0;
		box-shadow: none;
	}

	.navSubmenu .navItem {
		padding-inline: 14px;
		border-bottom: 0;
	}

	.navSubmenu .navLink {
		padding: 9px 0;
		font-size: 15px;
		color: rgba(241, 240, 240, 0.78);
	}

	/*
	 * Pinned to the foot of the drawer rather than trailing the last link: a
	 * flex-shrink: 0 sibling of a flex: 1 scrollable list always lands at the
	 * bottom of the column, whether the list is short (the list's own growth
	 * pushes it down) or long enough to need its own scrollbar (the list
	 * scrolls internally and this never moves).
	 */
	/*
	 * border: none comes before border-top below, not after — border is the
	 * shorthand and resets all four sides (including top) to none, so it has
	 * to lose that fight for border-top's own 1px divider to survive. That
	 * divider is a separator between the scrollable list and this pinned
	 * footer, not the button's own edge; the button's own edge is what
	 * border: none removes.
	 *
	 * border-radius is left alone, so it falls back to .pillBtn's own
	 * var(--radius-pill) — the same fully-rounded shape every other pill on
	 * the site draws, desktop header CTA included. Only the outline-vs-fill
	 * treatment changes: a 1px white border reads fine against a photograph,
	 * but stretched full-width against the drawer's own flat surface it
	 * looks like an empty outline rather than a button, so it becomes a
	 * solid fill instead.
	 */
	/* The header's own CTA still opens the consultation modal elsewhere on the
	   page; inside the drawer it is .headerCtaMobile's job instead (below), so
	   this copy of the button is dropped rather than shown twice. */
	.headerCta.isCompact {
		display: none;
	}

	/*
	 * Pinned to the foot of the drawer rather than trailing the last link: a
	 * flex-shrink: 0 sibling of a flex: 1 scrollable list always lands at the
	 * bottom of the column, whether the list is short (the list's own growth
	 * pushes it down) or long enough to need its own scrollbar (the list
	 * scrolls internally and this never moves).
	 *
	 * A compact pill centred in the row, not the full-width stretch the
	 * original CTA used here — matches the desktop button's own proportions,
	 * per the redesign request.
	 */
	.headerCtaMobile {
		display: inline-flex;
		flex: 0 0 auto;
		align-self: center;
		margin: 16px 0;
		margin-bottom: max(16px, env(safe-area-inset-bottom));
	}
}

/*
 * Locks the page behind the drawer — set by header.js in step with .isOpen
 * above. The drawer overlays rather than pushes the page down, so without
 * this a touch drag anywhere on the dimmed sliver still scrolls the body
 * underneath it.
 */
html.hasOpenNav {
	overflow: hidden;
}
