/* =============================================================
   Higher Performance — main.css
   Single source of truth for site styles.
   Customizer values override the :root variables via inline <style>
   emitted at wp_head priority 99, so the cascade wins predictably.
   ============================================================= */

/* Self-hosted Oceanwide (Semibold only — no other weights licensed).
   Mapped to weight 600 so the font-matching algorithm picks it up
   for any request between 500 and 700. Lighter and heavier weights
   fall back to system-ui. */
@font-face {
	font-family: 'Oceanwide';
	src: url('../fonts/Oceanwide-Semibold.woff2') format('woff2'),
		url('../fonts/Oceanwide-Semibold.woff') format('woff');
	font-weight: 600;
	font-style: normal;
	font-display: swap;
}
@font-face {
	font-family: 'Oceanwide';
	src: url('../fonts/Oceanwide-SemiboldOblique.woff2') format('woff2'),
		url('../fonts/Oceanwide-SemiboldOblique.woff') format('woff');
	font-weight: 600;
	font-style: italic;
	font-display: swap;
}

:root {
	/* Colours (fallbacks, Customizer wins) */
	--color-primary: #000010;
	--color-secondary: #0E2348;
	--color-accent: #FD2E98;
	--color-accent-light: #FF7BC0;
	--color-paper: #F4F1EA;
	--color-white: #FFFFFF;
	--color-radial: #5170FF;
	--color-button-bg: #FD2E98;
	/* Ink, not white: white on the brand pink is 3.48:1 and fails WCAG AA
	   for the 16px bold button label. Ink on pink is 5.9:1. Customizer
	   (Theme Options → Colour palette) can still override. */
	--color-button-text: #000010;
	--color-button-hover-bg: #FFFFFF;
	--color-button-hover-text: #000010;
	--color-link: #FD2E98;
	--color-link-hover: #FF7BC0;
	/* Brand pink darkened for SMALL text on light grounds — the raw accent
	   is only 3.05:1 on cream / 3.48:1 on white. Derived from the accent so
	   a Customizer re-brand carries through. Roughly #C52477. */
	--color-accent-ink: color-mix(in srgb, var(--color-accent) 78%, black);

	/* Typography (fallbacks) */
	--font-heading-family: 'Oceanwide', system-ui, sans-serif;
	--font-body-family: 'Oceanwide', system-ui, sans-serif;
	--font-h1-size: 6.5rem;
	--font-h1-size-mobile: 3.25rem;
	--font-h1-weight: 800;
	--font-h1-line-height: 0.95;
	--font-h1-letter-spacing: -0.03em;
	--font-h2-size: 4.5rem;
	--font-h2-size-mobile: 2.5rem;
	--font-h2-weight: 700;
	--font-h2-line-height: 1.02;
	--font-h2-letter-spacing: -0.02em;
	--font-h3-size: 2.5rem;
	--font-h3-size-mobile: 1.75rem;
	--font-h3-weight: 700;
	--font-h3-line-height: 1.1;
	--font-body-size: 1.0625rem;
	--font-body-size-mobile: 1rem;
	--font-body-weight: 400;
	--font-body-line-height: 1.6;

	/* Spacing */
	--space-1: 0.5rem;
	--space-2: 1rem;
	--space-3: 1.5rem;
	--space-4: 2rem;
	--space-5: 3rem;
	--space-6: 4rem;
	--space-7: 6rem;
	--space-8: 8rem;

	--radius-pill: 999px;
	--radius-lg: 32px;

	/* Button geometry (fallbacks, Theme Options → Buttons wins). Radius
	   defaults to the pill; colours live in the palette vars above. */
	--btn-radius: var(--radius-pill);
	--btn-padding-y: 1rem;
	--btn-padding-x: 1.875rem;
	--btn-border-width: 1px;
	--btn-font-weight: 700;
}

/* ---------- Page transition (brand-gradient circular reveal) ----------
   A full-screen brand-gradient overlay (pink → purple) with a circular HOLE
   punched out via a radial mask. On an internal page load the hole grows from
   the centre outward, so the page is revealed through an expanding circle
   while the gradient retreats to the edges and disappears. On an internal link
   click the hole shrinks back (gradient covers) before navigating, so pages
   hand off through the gradient. --hp-hole is the hole radius. */
@property --hp-hole {
	syntax: '<length>';
	inherits: false;
	initial-value: 0px;
}
.hp-page-transition {
	position: fixed;
	inset: 0;
	z-index: 9998;
	pointer-events: none;
	background: linear-gradient(115deg, #FD2E98 0%, #B026FF 52%, #6D4DFF 100%);
	--hp-hole: 150vmax; /* default: hole fully open = overlay invisible */
	-webkit-mask: radial-gradient(circle at 50% 50%, transparent var(--hp-hole), #000 calc(var(--hp-hole) + 2px));
	        mask: radial-gradient(circle at 50% 50%, transparent var(--hp-hole), #000 calc(var(--hp-hole) + 2px));
	will-change: --hp-hole;
}
/* Entrance reveal — internal pages only (the home hero is its own intro). The
   hole grows from 0 (gradient fully covers) to past the corners (revealed). */
body:not(.home) .hp-page-transition {
	animation: hp-page-reveal 1.15s cubic-bezier(0.65, 0, 0.35, 1) both;
}
@keyframes hp-page-reveal {
	from { --hp-hole: 0px; }
	to   { --hp-hole: 150vmax; }
}
/* Exit cover — JS adds .is-leaving on click; the hole shrinks so the gradient
   closes back over the page before navigating. */
body:not(.home) .hp-page-transition.is-leaving {
	pointer-events: auto;
	animation: hp-page-cover 0.55s cubic-bezier(0.7, 0, 0.25, 1) forwards;
}
@keyframes hp-page-cover {
	from { --hp-hole: 150vmax; }
	to   { --hp-hole: 0px; }
}
@media (max-width: 0) /* prefers-reduced-motion neutralised — see main.js header */ {
	.hp-page-transition { display: none; }
}

/* ---------- Reset / base ---------- */

*, *::before, *::after { box-sizing: border-box; }

/* scrollbar-gutter:stable reserves the scrollbar space at all times so the
   page width never changes when the scrollbar toggles (e.g. when the hero
   scroll-lock releases). Without it, every full-width section reflowed once
   on first scroll and the width-driven brand-thread paths rebuilt mid-scroll
   — the "jittery on scroll" hitch. */
html { scroll-behavior: smooth; overflow-x: clip; scrollbar-gutter: stable; }

body.hp-body {
	margin: 0;
	background: var(--color-primary);
	color: var(--color-paper);
	font-family: var(--font-body-family);
	font-size: var(--font-body-size);
	line-height: var(--font-body-line-height);
	font-weight: var(--font-body-weight);
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	text-rendering: optimizeLegibility;
	/* Kill the stray horizontal scroll caused by 100vw full-bleed elements
	   (100vw includes the scrollbar width) and off-canvas brand-thread tails.
	   `clip` (not `hidden`) does NOT create a scroll container, so the sticky
	   showreel / promise sections keep working. */
	overflow-x: clip;
}

img { max-width: 100%; height: auto; display: block; }

a { color: var(--color-link); text-decoration: none; transition: color 0.2s ease; }
a:hover { color: var(--color-link-hover); }

h1, h2, h3, h4, h5, h6 {
	font-family: var(--font-heading-family);
	margin: 0 0 var(--space-3);
}

h1 { font-size: var(--font-h1-size); font-weight: var(--font-h1-weight); line-height: var(--font-h1-line-height); letter-spacing: var(--font-h1-letter-spacing); }
h2 { font-size: var(--font-h2-size); font-weight: var(--font-h2-weight); line-height: var(--font-h2-line-height); letter-spacing: var(--font-h2-letter-spacing); }
h3 { font-size: var(--font-h3-size); font-weight: var(--font-h3-weight); line-height: var(--font-h3-line-height); }

p { margin: 0 0 var(--space-3); }

.screen-reader-text, .skip-link {
	position: absolute;
	left: -10000px;
	width: 1px; height: 1px; overflow: hidden;
}
.skip-link:focus { left: var(--space-3); top: var(--space-3); width: auto; height: auto; padding: var(--space-2); background: var(--color-accent); color: var(--color-white); z-index: 9999; }

.container { width: 100%; margin-inline: auto; padding-inline: var(--page-pad-x); }
.container--narrow { width: 100%; margin-inline: auto; padding-inline: var(--page-pad-x); }
.container--center { text-align: center; }

/* ---------- Buttons ----------
   Geek-style pill: heavy weight, generous horizontal padding, subtle lift
   on hover. Pink is the primary, navy/paper ghosts for secondary actions.
   ----------------------------------------------------- */

.hp-btn {
	display: inline-flex;
	align-items: center;
	gap: 0.625rem;
	padding: var(--btn-padding-y) var(--btn-padding-x);
	border-radius: var(--btn-radius);
	font-family: var(--font-heading-family);
	font-weight: var(--btn-font-weight);
	font-size: 1rem;
	letter-spacing: -0.005em;
	text-decoration: none;
	transition: background 0.25s ease, color 0.25s ease, transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
	cursor: pointer;
	border: var(--btn-border-width) solid transparent;
	white-space: nowrap;
}
.hp-btn:hover { transform: translateY(-2px); }

.hp-btn--pink {
	background: var(--color-button-bg);
	color: var(--color-button-text);
	box-shadow: 0 10px 30px rgba(253, 46, 152, 0.28);
}
.hp-btn--pink:hover {
	background: var(--color-button-hover-bg);
	color: var(--color-button-hover-text);
	box-shadow: 0 14px 36px rgba(0, 0, 0, 0.25);
}

.hp-btn--ghost {
	background: transparent;
	color: var(--color-paper);
	border-color: rgba(244, 241, 234, 0.35);
}
.hp-btn--ghost:hover {
	background: var(--color-paper);
	color: var(--color-primary);
	border-color: var(--color-paper);
}

/* Ghost button rendered on light grounds (paper) needs inverted contrast. */
.hp-btn--ghost-dark {
	background: transparent;
	color: var(--color-primary);
	border-color: rgba(8, 20, 39, 0.25);
}
.hp-btn--ghost-dark:hover {
	background: var(--color-primary);
	color: var(--color-paper);
	border-color: var(--color-primary);
}

.hp-btn--large {
	padding: 1.25rem 2.25rem;
	font-size: 1.125rem;
}

.hp-btn__arrow {
	display: inline-flex;
	transition: transform 0.25s ease;
}
.hp-btn:hover .hp-btn__arrow { transform: translateX(4px); }

/*
 * Phones: the pill is the same shape, just smaller.
 *
 * Hero actions stack to full width at 560px, and at desktop scale two
 * stacked full-bleed pills eat most of the first screen. The size comes off
 * the --btn-* tokens rather than a per-page override, so every button on the
 * site shrinks by the same amount and the pill identity is untouched.
 *
 * --large stops being large here on purpose: on a 390px screen a full-width
 * button is already the most prominent thing on the page, so the extra
 * padding buys nothing and costs vertical space.
 *
 * Floor is the 44px minimum tap target: 0.8rem padding top and bottom plus a
 * 0.9375rem label at 1.25 line-height lands at roughly 46px.
 */
@media (max-width: 560px) {
	:root {
		--btn-padding-y: 0.8rem;
		--btn-padding-x: 1.375rem;
	}
	.hp-btn {
		font-size: 0.9375rem;
		white-space: normal;
		text-align: center;
		line-height: 1.25;
	}
	.hp-btn--large {
		padding: 0.8rem 1.5rem;
		font-size: 0.9375rem;
	}
}

/* ---------- Site header (pills) ---------- */

.site-header {
	position: fixed;
	top: var(--space-3);
	left: var(--space-4);
	right: var(--space-4);
	display: flex;
	justify-content: space-between;
	align-items: center;
	z-index: 50;
	pointer-events: none;
}
/* Centred brand mark, parked in the middle of the header. Hidden until the
   hero/showreel is scrolled past (body.hp-header-logo-on), then fades in.
   Links home. */
.site-header__logo {
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%) translateY(-6px);
	display: inline-flex;
	align-items: center;
	pointer-events: none;
	opacity: 0;
	transition: opacity 0.4s ease, transform 0.4s cubic-bezier(0.34, 1.4, 0.64, 1);
}
.site-header__logo svg {
	height: clamp(20px, 2.4vw, 28px);
	width: auto;
	display: block;
}
.hp-body.hp-header-logo-on .site-header__logo {
	opacity: 1;
	pointer-events: auto;
	transform: translate(-50%, -50%) translateY(0);
}
/* While the menu panel is open, drop the centred logo so it doesn't sit over
   the open state. */
.hp-body.menu-open .site-header__logo {
	opacity: 0;
	pointer-events: none;
}
.site-header__pill {
	pointer-events: auto;
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.75rem 1.25rem;
	background: rgba(0, 0, 16, 0.78);
	backdrop-filter: blur(18px) saturate(140%);
	-webkit-backdrop-filter: blur(18px) saturate(140%);
	border: 1px solid rgba(244, 241, 234, 0.18);
	border-radius: var(--radius-pill);
	color: var(--color-paper);
	font-family: var(--font-heading-family);
	font-weight: 600;
	font-size: 0.9375rem;
	line-height: 1; /* same tight line box for both labels so they sit level */
	letter-spacing: -0.005em;
	box-shadow: 0 6px 24px rgba(0, 0, 0, 0.25);
	transition: background 0.25s ease, color 0.25s ease, transform 0.25s ease, border-color 0.25s ease;
}
.site-header__pill:hover {
	background: var(--color-accent);
	color: var(--color-white);
	border-color: var(--color-accent);
	transform: translateY(-1px);
}
.site-header__pill-dot {
	width: 8px; height: 8px; border-radius: 50%;
	background: var(--color-accent);
	/* Site-wide bullet lift (HP convention). See feedback memory. */
	transform: translateY(-0.15em);
	transition: background 0.25s ease;
}
/* On hover the pill fills with the accent pink, so the pink dot would vanish
   into it. Flip the dot to white so it stays visible (matches the open state). */
.site-header__pill:hover .site-header__pill-dot {
	background: var(--color-white);
}
.site-header__pill-arrow { transition: transform 0.2s ease; }
.site-header__contact:hover .site-header__pill-arrow { transform: translateX(2px); }

/* Roll container for the Menu / Close labels. Two faces stacked vertically
   inside an overflow:hidden window; on open both faces translate up by
   one face-height, so "Menu" rolls out the top and "Close" rolls into
   place from below. */
.site-header__pill-flip {
	position: relative;
	display: inline-block;
	overflow: hidden;
	min-width: 3.5em; /* reserve width so the pill doesn't jump when label swaps */
	height: 1em;
	line-height: 1;
	vertical-align: middle;
}
.site-header__pill-face {
	display: block;
	height: 1em;
	line-height: 1;
	white-space: nowrap;
	transition: transform 0.45s cubic-bezier(0.65, 0, 0.35, 1);
}
.site-header__pill-face--close {
	position: absolute;
	inset: 0;
	/* Parked one face-height below the window, plus a 2px buffer so sub-pixel
	   rounding on high-DPR mobile screens can't leave a sliver of the "C"
	   peeking under the "Menu" label. When open it still lands at exactly 0. */
	transform: translateY(calc(100% + 2px));
}
.hp-body.menu-open .site-header__pill-face--menu {
	transform: translateY(-100%);
}
.hp-body.menu-open .site-header__pill-face--close {
	transform: translateY(0);
}
.hp-body.menu-open .site-header__menu {
	background: var(--color-accent);
	border-color: var(--color-accent);
	color: var(--color-white);
}
.hp-body.menu-open .site-header__menu .site-header__pill-dot {
	background: var(--color-white);
}

/* Drop-down menu panel. A 400px-wide card anchored at the top-left with
   margin all around it and a generous border-radius. Hidden by default
   via translateY off the top of the viewport; .menu-open on body drops
   it in. The `inert` HTML attribute keeps it out of tab order / AT
   while closed (set by JS). */
.site-nav {
	position: fixed;
	top: var(--space-3);
	bottom: var(--space-3);
	left: var(--space-4);
	width: clamp(320px, 88vw, 480px);
	max-width: calc(100vw - 2 * var(--space-4));
	background: var(--color-primary);
	/* Top padding clears the floating Close pill so the first menu item is
	   never overlapped on short viewports. */
	padding: clamp(5.5rem, 14vw, 7rem) clamp(1.5rem, 5vw, 2.25rem) clamp(1.5rem, 4vw, 2rem);
	z-index: 40;
	border: 1px solid rgba(244, 241, 234, 0.18);
	border-radius: 28px;
	box-shadow: 0 30px 60px rgba(0, 0, 0, 0.4);
	transform: translateY(calc(-100% - var(--space-3) - 1rem));
	/* Default = the CLOSE transition: a clean, fairly quick slide back up. */
	transition: transform 0.4s cubic-bezier(0.65, 0, 0.35, 1);
	overflow-y: auto;
	overflow-x: hidden;
	overscroll-behavior: contain;
	scrollbar-width: thin;
	scrollbar-color: rgba(244, 241, 234, 0.3) transparent;
	/* Use flex-start + auto margins on the list so it centres when there
	   is space, but sticks to the top (no clipping) when content overflows. */
	display: flex;
	flex-direction: column;
	justify-content: flex-start;
}
.site-nav__list { margin: auto 0; }
.site-nav::-webkit-scrollbar { width: 6px; }
.site-nav::-webkit-scrollbar-thumb {
	background: rgba(244, 241, 234, 0.25);
	border-radius: 999px;
}
.hp-body.menu-open .site-nav {
	transform: translateY(0);
	/* OPEN = slower drop that overshoots and springs back into place. */
	transition: transform 0.72s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.site-nav__list,
.site-nav .sub-menu {
	list-style: none;
	margin: 0;
	padding: 0;
}
.site-nav__list > li {
	margin-bottom: clamp(0.35rem, 1.2vw, 0.75rem);
	/* Stagger reveal: each item slides up + fades in after the panel drops.
	   --i is set by JS (falls back to 0). */
	opacity: 0;
	transform: translateY(14px);
	transition: opacity 0.45s ease, transform 0.55s cubic-bezier(0.34, 1.4, 0.64, 1);
}
.hp-body.menu-open .site-nav__list > li {
	opacity: 1;
	transform: translateY(0);
	transition-delay: calc(0.18s + var(--i, 0) * 60ms);
}

/* Top-level link row. Flex row so the accordion chevron sits inline on
   parents — child <a> still gets the bullet-shove effect. */
.site-nav__list .site-nav__row {
	display: flex;
	align-items: center;
	gap: 0.5rem;
}
.site-nav__list a {
	position: relative;
	display: inline-flex;
	align-items: center;
	padding-left: 0; /* opens up on hover to make room for the bullet */
	font-family: var(--font-heading-family);
	font-size: clamp(1.75rem, 5.4vw, 3.25rem);
	font-weight: 600;
	line-height: 1.04;
	letter-spacing: -0.02em;
	color: var(--color-paper);
	/* Long labels (e.g. "Social Media & Content") wrap inside the panel
	   instead of clipping off the right edge. */
	white-space: normal;
	overflow-wrap: anywhere;
	transition: color 0.25s ease, padding-left 0.4s cubic-bezier(0.34, 1.5, 0.64, 1);
}
/* Accent bullet parked off to the left; on hover/focus it pops into the
   reserved column and the link turns pink. Mirrors the services-row dot. */
.site-nav__list a::before {
	content: "";
	position: absolute;
	left: 0;
	/* True vertical centring (HP convention): position the dot at 50% of
	   the link's rendered box and pull it back by half its own height via
	   translateY(-50%). Works for single-line and wrapped multi-line items
	   alike. Don't switch back to em-based offsets — they drift the moment
	   font / line-height / wrap changes.
	   Y offset of -0.15em applied site-wide on top of the centring: dyslexia-
	   readable headings have most visual mass above the line-box midline, so
	   the dot now sits closer to cap-height middle. See HP feedback memory. */
	top: 50%;
	width: 0.4em;
	height: 0.4em;
	border-radius: 50%;
	background: var(--color-accent);
	opacity: 0;
	transform: translate(-0.5em, calc(-50% - 0.15em)) scale(0.4);
	transition: opacity 0.2s ease, transform 0.4s cubic-bezier(0.34, 1.7, 0.64, 1);
}
.site-nav__list a:hover,
.site-nav__list a:focus-visible,
.site-nav__list .menu-item-has-children.is-open > .site-nav__row > a {
	color: var(--color-accent);
	padding-left: 0.8em;
}
.site-nav__list a:hover::before,
.site-nav__list a:focus-visible::before,
.site-nav__list .menu-item-has-children.is-open > .site-nav__row > a::before {
	opacity: 1;
	transform: translate(0, calc(-50% - 0.15em)) scale(1);
}

/* Accordion chevron for parents that have a sub-menu. Pure CSS, no SVG —
   a rotating arrow built from a 2px-stroke chevron shape. */
.site-nav__caret {
	flex-shrink: 0;
	margin-left: auto;
	display: inline-grid;
	place-items: center;
	width: 2.25rem;
	height: 2.25rem;
	border-radius: 999px;
	border: 1px solid rgba(244, 241, 234, 0.2);
	background: rgba(244, 241, 234, 0.04);
	color: var(--color-paper);
	cursor: pointer;
	padding: 0;
	transition: background 0.25s ease, border-color 0.25s ease, color 0.25s ease, transform 0.4s cubic-bezier(0.34, 1.5, 0.64, 1);
}
.site-nav__caret::before {
	content: "";
	width: 0.55rem;
	height: 0.55rem;
	border-right: 2px solid currentColor;
	border-bottom: 2px solid currentColor;
	transform: translateY(-2px) rotate(45deg);
	transition: transform 0.35s cubic-bezier(0.65, 0, 0.35, 1);
}
.site-nav__caret:hover,
.site-nav__caret:focus-visible {
	background: rgba(255, 79, 165, 0.18);
	border-color: var(--color-accent);
	color: var(--color-accent);
}
.site-nav__list .menu-item-has-children.is-open > .site-nav__row > .site-nav__caret::before {
	transform: translateY(2px) rotate(-135deg);
}

/* Nested submenu — collapsed by default, expands smoothly. Uses a
   max-height + opacity transition so it works on the raw WP <ul><li>
   markup without needing a wrapper element. */
.site-nav .sub-menu {
	max-height: 0;
	overflow: hidden;
	opacity: 0;
	margin-top: 0;
	transition: max-height 0.45s cubic-bezier(0.65, 0, 0.35, 1),
	            opacity 0.3s ease,
	            margin-top 0.35s ease;
}
.site-nav__list .menu-item-has-children.is-open > .sub-menu {
	max-height: 800px;
	opacity: 1;
	margin-top: 0.6rem;
}
.site-nav .sub-menu li {
	margin: 0;
	opacity: 0;
	transform: translateX(-6px);
	transition: opacity 0.3s ease, transform 0.35s ease;
}
.site-nav__list .menu-item-has-children.is-open > .sub-menu li {
	opacity: 1;
	transform: translateX(0);
	transition-delay: calc(0.08s + var(--si, 0) * 45ms);
}
.site-nav .sub-menu a {
	font-family: var(--font-body-family, var(--font-heading-family));
	font-size: clamp(1rem, 2.6vw, 1.15rem);
	font-weight: 500;
	letter-spacing: 0;
	line-height: 1.35;
	color: rgba(244, 241, 234, 0.78);
	padding: 0.45rem 0 0.45rem 1.5rem;
	border-left: 1px solid rgba(244, 241, 234, 0.14);
	width: 100%;
}
.site-nav .sub-menu a::before {
	left: -0.2em;
	top: 50%;
	width: 0.45em;
	height: 0.45em;
	transform: translate(-0.6em, calc(-50% - 0.15em)) scale(0.4);
}
.site-nav .sub-menu a:hover,
.site-nav .sub-menu a:focus-visible {
	color: var(--color-accent);
	border-left-color: var(--color-accent);
	padding-left: 1.9rem;
}

@media (max-width: 0) /* prefers-reduced-motion neutralised — see main.js header */ {
	.site-header__pill-face,
	.site-nav,
	.site-nav__list > li,
	.site-nav .sub-menu,
	.site-nav .sub-menu li,
	.site-nav__caret,
	.site-nav__caret::before {
		transition: none;
	}
}

/* ---------- Hero ----------
   Full-viewport video hero. The video uses object-fit: contain so the full
   wordmark is always visible on any aspect ratio (no cropping on mobile
   portrait or ultrawide). Any letterbox area blends into the section
   background which is exactly the navy of the video (#081427 via
   --color-primary), so the letterbox is invisible.
   ----------------------------------------------------- */

/* Scroll lock applied while the hero video is still playing. Released by
   main.js as soon as the video's `ended` event fires (or a safety timeout).
   scrollbar-gutter keeps layout stable when the scrollbar disappears. */
html.hp-scroll-lock,
html.hp-scroll-lock body {
	overflow: hidden;
	height: 100%;
	touch-action: none;
}
html.hp-scroll-lock { scrollbar-gutter: stable; }

.hp-hero {
	position: relative;
	min-height: 100vh;
	min-height: 100dvh;
	display: flex;
	align-items: center;
	justify-content: center;
	/* Flat #000010 across the whole hero — matches the video's encoded canvas
	   edge exactly so the letterbox bands and the plate read as one surface. */
	background: #000010;
	overflow: hidden;
	isolation: isolate;
}

/* Scroll cue — centred at the hero bottom. Hidden until the wordmark video
   finishes (body.hp-hero-cue), fades out again on the first scroll. */
.hp-hero__scroll {
	/* Fixed to the VIEWPORT bottom (not the hero box) so it's always on-screen
	   regardless of 100vh/100dvh differences; it only shows at the top and
	   hides on the first scroll anyway. */
	position: fixed;
	left: 50%;
	bottom: clamp(1.25rem, 4vh, 2.75rem);
	z-index: 30;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.7rem;
	color: var(--color-paper);
	pointer-events: none;
	opacity: 0;
	transform: translateX(-50%) translateY(10px);
	transition: opacity 0.6s ease, transform 0.6s ease;
}
.hp-body.hp-hero-cue .hp-hero__scroll {
	opacity: 0.85;
	transform: translateX(-50%) translateY(0);
}
.hp-hero__scroll-label {
	font-family: var(--font-heading-family);
	font-size: 0.6875rem;
	font-weight: 600;
	letter-spacing: 0.22em;
	text-transform: uppercase;
}
.hp-hero__scroll-mouse {
	width: 22px;
	height: 34px;
	border: 2px solid currentColor;
	border-radius: 12px;
	position: relative;
}
.hp-hero__scroll-mouse::after {
	content: "";
	position: absolute;
	left: 50%;
	top: 6px;
	width: 3px;
	height: 7px;
	border-radius: 2px;
	background: var(--color-accent);
	transform: translateX(-50%);
	animation: hp-hero-scroll-dot 1.6s ease-in-out infinite;
}
@keyframes hp-hero-scroll-dot {
	0%   { opacity: 0; transform: translate(-50%, 0); }
	30%  { opacity: 1; }
	60%  { opacity: 1; transform: translate(-50%, 11px); }
	100% { opacity: 0; transform: translate(-50%, 11px); }
}
@media (max-width: 0) /* prefers-reduced-motion neutralised — see main.js header */ {
	.hp-hero__scroll-mouse::after { animation: none; top: 9px; opacity: 1; }
	.hp-hero__scroll { transition: opacity 0.3s ease; }
}

/* Media wrapper locked to the video's native 16:9 aspect ratio. This is the
   positioning context for the overlay dot, so the dot tracks the wordmark
   at every viewport size, not the hero section's bounding box. The wrapper
   takes the largest 16:9 rectangle that fits the viewport (limited by 100%
   width AND 100dvh height), exactly matching where `object-fit: contain`
   would have rendered the video inside .hp-hero. */
.hp-hero__media {
	position: relative;
	aspect-ratio: 16 / 9;
	width: 100%;
	max-height: 100dvh;
	/* Cap width so we never exceed the height budget. Solving 16/9 against
	   100dvh gives a max width of (100dvh * 16/9). */
	max-width: calc(100dvh * 16 / 9);
	margin: auto;
	background: #000010;
	overflow: hidden; /* clip the video to the wordmark box */
}

/* First-frame LCP candidate — the poster as a real <img> painted beneath the
   video (see notes in section-hero.php). Same geometry as the video so the
   handoff is invisible; z-index keeps it under the video at all times. */
.hp-hero__poster {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
	z-index: 0;
}

.hp-hero__video {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover; /* wrapper already matches video AR, so cover = no crop */
	object-position: center;
	background: #000010; /* exact match to the video's encoded edge navy */
	z-index: 1;
}

/* ---------- Hero pink accent dot ----------
   Brand-signature pink dot that lands at the EXACT coordinates of the
   period after "performance" in the wordmark video. Sampled from the
   source frame (1920x1080):
     - period centre: (1686.5, 699) px = (87.81%, 64.77%) of frame
     - period width:  27 px = 1.46% of frame width
   The dot is positioned inside .hp-hero__media (a 16:9 box), so the
   same percentage coords resolve to the same point regardless of viewport.

   Geometry: width = 1.46% of the media width, aspect-ratio: 1 / 1 so it's
   a perfect square box, border-radius: 50% renders it as a circle.

   left/top point to the centre of the period; translate(-50%, -50%) then
   centres the dot box on that point. */
.hp-hero__dot {
	position: absolute;
	left: 87.81%;
	top: 64.77%;
	width: 1.46%; /* of wrapper width = matches the video's period exactly */
	aspect-ratio: 1 / 1;
	height: auto;
	background: #FD2E98;
	border-radius: 50%;
	z-index: 2;
	pointer-events: none;
	transform-origin: center center;
	/* Rest state: HIDDEN. The pop-in used to be a fixed 2.45s
	   animation-delay counted from stylesheet parse, which assumed the
	   video starts at ~0s. On real phones the video starts seconds later
	   (poster gate + network), so the dot popped over the static poster
	   with no visible entrance. wireHeroVideo now adds .hp-dot-in on the
	   media wrapper when PLAYBACK reaches the period's emergence (or on
	   any video fallback), and only then does the pop run. scale(0)
	   collapses the rect to the anchor point, so thread geometry that
	   reads the dot's centre stays correct; width consumers use
	   offsetWidth, which ignores the transform. */
	transform: translate(-50%, -50%) scale(0);
	opacity: 0;
}
.hp-hero__media.hp-dot-in .hp-hero__dot {
	/* Snaps to full size in 0.18s with a slight overshoot so it merges
	   with the video's baked-in period. */
	animation: hp-hero-dot-pop 0.18s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

@keyframes hp-hero-dot-pop {
	0% {
		transform: translate(-50%, -50%) scale(0);
		opacity: 0;
	}
	100% {
		transform: translate(-50%, -50%) scale(1);
		opacity: 1;
	}
}

@media (max-width: 0) /* prefers-reduced-motion neutralised — see main.js header */ {
	.hp-hero__dot {
		animation: none;
		transform: translate(-50%, -50%) scale(1);
		opacity: 1;
	}
}

/* ---------- Scroll-drawn pink line ----------
   Injected by main.js as a sibling of <body>, anchored to the document-space
   centre of .hp-hero__dot. Height tracks window.scrollY so the line appears
   to write itself downward out of the dot as the user scrolls. Drawn above
   page content but below the fixed pills via z-index. */
/* SVG path that weaves through the page, drawn progressively with scroll.
   Stroke width, position, and the path's d attribute are all set in JS so
   the path can be re-measured on resize / new content. */
.hp-scroll-line {
	position: absolute;
	left: 0;
	top: 0;
	pointer-events: none;
	z-index: 3;
	overflow: visible;
	opacity: 0; /* held hidden until the hero dot has popped in */
	animation: hp-scroll-line-in 0.18s linear 2.63s forwards;
	filter: drop-shadow(0 0 8px rgba(253, 46, 152, 0.45));
	transition: opacity 0.35s ease;
}
/* Hide the brand thread while the showreel is occupying the viewport,
   then let it fade back in once the user has scrolled past the video. */
.hp-body.hp-thread-hidden .hp-scroll-line {
	opacity: 0 !important;
}
.hp-scroll-line path {
	fill: none;
	stroke: #FD2E98;
	stroke-linecap: round;
	stroke-linejoin: round;
}

@keyframes hp-scroll-line-in {
	to { opacity: 1; }
}

@media (max-width: 0) /* prefers-reduced-motion neutralised — see main.js header */ {
	.hp-scroll-line { display: none; }
}

/* Narrow-viewport gutter for sections the brand thread runs alongside.
   The thread is pinned ~14-22px in from the edge on small screens (see
   sideInset in main.js); these paddings keep headings and body copy
   clear of the stroke so the thread frames the content instead of
   running across it. */
@media (max-width: 900px) {
	.hp-why,
	.hp-promise,
	.hp-featured,
	.hp-services,
	.hp-marketing {
		padding-left: clamp(3.25rem, 11vw, 4.5rem);
		padding-right: clamp(3.25rem, 11vw, 4.5rem);
	}
	.hp-promise__inner {
		padding-left: clamp(3.25rem, 11vw, 4.5rem);
		padding-right: clamp(3.25rem, 11vw, 4.5rem);
	}
}

/* Subtle vignette to keep pills legible against bright frames of the video.
   Rendered via ::after so we don't need an extra DOM node. */
.hp-hero::after {
	content: "";
	position: absolute;
	inset: 0;
	background:
		linear-gradient(180deg, rgba(0, 0, 16, 0.55) 0%, rgba(0, 0, 16, 0) 22%, rgba(0, 0, 16, 0) 78%, rgba(0, 0, 16, 0.45) 100%);
	pointer-events: none;
	z-index: 1;
}

/* Preserved rule (not currently rendered). Kept for an opt-in overlay if we
   want the blue radial back as a top-layer legibility aid. */
.hp-hero__radial {
	position: absolute;
	inset: -20%;
	background: radial-gradient(closest-side, color-mix(in oklab, var(--color-radial) 33%, transparent), transparent 70%);
	z-index: 1;
	pointer-events: none;
	opacity: 0.25;
	mix-blend-mode: screen;
}

/* Visually-hidden brand-name heading so screen readers / SEO still read the
   wordmark even though the visual is a video. */
.hp-hero__brand-sr {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* ---------- Marketing intro ----------
   Big bold headline with an Apple-style flowing gradient on the h1.
   The text colour is "painted" by a background-clip: text gradient that
   shifts horizontally on a loop. background-size:200% lets the gradient
   slide across the text without ever exposing the page background. */

.hp-marketing {
	position: relative;
	background: var(--color-primary);
	/* Extra right padding leaves room for the brand-thread vertical
	   drop that runs down the right side after the horizontal frame. */
	padding: clamp(6rem, 14vw, 12rem) clamp(5rem, 10vw, 10rem) clamp(6rem, 14vw, 12rem) clamp(1.25rem, 4vw, 3rem);
	color: var(--color-paper);
}
.hp-marketing__inner {
	width: 100%;
	margin-inline: auto;
	text-align: left;
}
.hp-marketing__h1 {
	font-family: var(--font-heading-family);
	font-weight: 800;
	/* Scales 3rem on mobile up to ~10rem on wide screens. */
	font-size: clamp(3rem, 9.5vw, 10rem);
	/* Slightly taller line-height + small bottom padding so descenders
	   on letters like g, p, y aren't clipped by background-clip: text. */
	line-height: 1.02;
	letter-spacing: -0.045em;
	margin-bottom: clamp(1.5rem, 3vw, 2.5rem);
	padding-bottom: 0.15em;
}
/* Each h1 line is its own block so the headline always renders as the
   intended number of lines (3 by default), no matter the viewport. */
.hp-marketing__h1-line {
	display: block;
}
/* Brand-pink period after the headline, decoupled from the gradient
   fill so it always renders solid accent-pink. aria-hidden because it
   reads as decorative punctuation rather than meaningful content. */
.hp-marketing__h1-dot {
	color: var(--color-accent);
	-webkit-text-fill-color: var(--color-accent);
}
.hp-marketing__h2 {
	font-family: var(--font-heading-family);
	font-weight: 700;
	font-size: clamp(1.5rem, 3.4vw, 3rem);
	line-height: 1.1;
	letter-spacing: -0.02em;
	color: var(--color-paper);
	margin-bottom: clamp(2rem, 4vw, 3.5rem);
	opacity: 1;
}
/* Contemporary asymmetric body layout. Lead statement (left, big and
   bold) sits opposite a smaller, lighter supporting line (right) with a
   short pink rule that marks it as the "trusted by" credential. Stacks
   to a single column on narrow viewports. */
.hp-marketing__body {
	display: grid;
	grid-template-columns: 1.4fr 1fr;
	gap: clamp(2rem, 5vw, 5rem);
	align-items: end;
	margin-top: clamp(2rem, 4vw, 4rem);
	max-width: 1200px;
}
.hp-marketing__body p {
	margin: 0;
}
.hp-marketing__body-lead {
	font-size: clamp(1.375rem, 2vw, 2rem);
	line-height: 1.3;
	font-weight: 600;
	color: var(--color-paper);
	letter-spacing: -0.005em;
	max-width: 30ch;
}
.hp-marketing__body-meta {
	display: flex;
	align-items: flex-start;
	gap: 1rem;
	font-size: clamp(0.9375rem, 1.05vw, 1.0625rem);
	line-height: 1.55;
	color: rgba(244, 241, 234, 0.62);
	letter-spacing: 0.01em;
	max-width: 36ch;
}
.hp-marketing__body-rule {
	flex: 0 0 auto;
	width: 2.5rem;
	height: 2px;
	background: var(--color-accent);
	margin-top: 0.65em; /* aligns with the cap-height of the first line */
	border-radius: 2px;
}
.hp-marketing__body-meta-text {
	flex: 1 1 auto;
}
@media (max-width: 768px) {
	.hp-marketing__body {
		grid-template-columns: 1fr;
		gap: clamp(1.5rem, 4vw, 2.5rem);
	}
	.hp-marketing__body-meta { max-width: 100%; }
}

/* Scroll-driven VERTICAL gradient text. As the user scrolls past the
   element, the gradient slides downward through the letters, drawing
   the eye down through the headline. JS sets --hp-gradient-y on the
   .hp-gradient-text container based on how far the element has moved
   through the viewport (0% when entering from the bottom, 100% when
   leaving out the top). */
.hp-gradient-text {
	display: block;
	--hp-gradient-y: 0%;
}
.hp-gradient-text__inner {
	/* Brand-palette gradient — uses the Customizer colour variables so
	   it adapts if the brand colours change: accent (pink) → accent-light
	   → radial (blue) → back. */
	background: linear-gradient(
		180deg,
		var(--color-accent) 0%,
		var(--color-accent-light) 25%,
		var(--color-radial) 55%,
		var(--color-accent-light) 80%,
		var(--color-accent) 100%
	);
	background-size: 100% 260%;
	background-position: 0% var(--hp-gradient-y, 0%);
	background-clip: text;
	-webkit-background-clip: text;
	color: transparent;
	-webkit-text-fill-color: transparent;
	display: inline-block;
	/* Padding on BOTH sides — descender room AND ascender room.
	   `background-clip: text` only paints inside the inline-block's
	   box (= line-box). With tight brutalist line-heights (0.92), the
	   glyph ascenders extend ABOVE the line-box top and the descenders
	   below the bottom — both ends were rendering transparent until
	   we expanded the box on both sides. */
	padding-top:    0.15em;
	padding-bottom: 0.15em;
}

@media (max-width: 0) /* prefers-reduced-motion neutralised — see main.js header */ {
	.hp-gradient-text__inner {
		background-position: 0% 50%;
	}
}

/* Horizontal variant: gradient flows LEFT → RIGHT through the letters
   as the user scrolls. Same scroll-progress value used as a vertical
   gradient elsewhere, just applied to the X axis. */
.hp-gradient-text--horizontal .hp-gradient-text__inner {
	background: linear-gradient(
		90deg,
		#FD2E98 0%,
		#FF7BC0 20%,
		#A570FF 40%,
		#5170FF 60%,
		#1FD0FF 80%,
		#FD2E98 100%
	);
	background-size: 260% 100%;
	background-position: var(--hp-gradient-y, 0%) 0%;
}

/* ---------- Cursor-tracked brand-gradient spotlight on display headings.
   The text sits in its normal paper colour; a small radial brand
   gradient follows the pointer, clipped to the text shape via
   background-clip, so only the glyphs UNDER the cursor light up in
   brand colour. JS (wireSpotlightText) writes --hp-spot-x / --hp-spot-y
   on pointermove. --hp-spot-r is 0 by default (no visible effect) and
   grows on :hover so the spotlight reveals smoothly when the pointer
   enters. Registered via @property so the radius transition animates
   instead of snapping. */
@property --hp-spot-r {
	syntax: '<length>';
	inherits: false;
	initial-value: 0px;
}
.hp-spotlight-text {
	--hp-spot-x: 50%;
	--hp-spot-y: 50%;
	--hp-spot-r: 0px;
	--hp-spot-base: var(--color-paper, #F4F1EA);
	/* The radial MUST terminate in the base colour: beyond a radial's ending
	   shape the LAST stop paints forever, so ending on #6D4DFF filled every
	   glyph solid blue whenever the radius was 0px — i.e. at rest, and
	   permanently on touch devices. Fading back to the base keeps rest =
	   flat base colour, hover = brand spotlight only under the cursor. */
	background-image:
		radial-gradient(
			circle var(--hp-spot-r) at var(--hp-spot-x) var(--hp-spot-y),
			#FD2E98 0%,
			#B026FF 55%,
			#6D4DFF 85%,
			var(--hp-spot-base) 100%
		),
		linear-gradient(var(--hp-spot-base), var(--hp-spot-base));
	-webkit-background-clip: text;
	        background-clip: text;
	-webkit-text-fill-color: transparent;
	color: transparent;
	transition: --hp-spot-r 0.22s ease;
}
.hp-spotlight-text:hover {
	--hp-spot-r: 0.6em;
}
/* No pointer = no spotlight. Touch devices get flat base-colour text —
   drop the radial layer entirely (belt and braces on top of the
   base-terminated radial above). */
@media (hover: none), (pointer: coarse) {
	.hp-spotlight-text,
	.hp-spotlight-text:hover {
		--hp-spot-r: 0px;
		background-image: linear-gradient(var(--hp-spot-base), var(--hp-spot-base));
	}
}
/* Featured Work sits on the scroll-shifting ground (navy at both scroll
   ends, paper mid-scroll), so a fixed base would vanish at one end. Track
   the same colour-mix the section uses for its text: navy on paper ground,
   paper on navy ground. */
.hp-work .hp-spotlight-text {
	--hp-spot-base: color-mix(in oklab, var(--color-primary) calc(var(--hp-work-shift) * 100%), var(--color-paper));
}

/* ---------- Promise statement ----------
   Single big bold line with the same scroll-driven animated gradient
   the marketing H1 uses. Pink period as a brand mark. */

.hp-promise {
	position: relative;
	background: var(--color-primary);
	color: var(--color-paper);
	/* Tall enough to scroll through while the inner sticks at viewport
	   top. The phrase-swap happens partway through the scroll, then the
	   user "passes" the section into the next one. */
	min-height: 200vh;
	min-height: 200svh;
}
.hp-promise__inner {
	position: sticky;
	top: 0;
	width: 100%;
	/* svh, NOT dvh: dvh tracks the mobile browser toolbar, so the pinned
	   box resized mid-scroll and the centred heading jumped. svh is fixed
	   for the whole gesture (desktop: svh == vh). The section's own navy
	   background fills any gap below the shorter box when the toolbar
	   collapses, so nothing shows through. */
	height: 100vh;
	height: 100svh;
	display: flex;
	flex-direction: column;
	justify-content: center;
	align-items: center;
	padding: 0 clamp(1.25rem, 4vw, 3rem);
	text-align: center;
	isolation: isolate;
}
/* Brand glow (from the logo brand image, 2026-07-29). The homepage hero
   is video so the glow lives here instead, on the pinned viewport box —
   it travels with the heading through the whole crossfade scroll. Static
   gradient, no blur/animation, so it is free on PSI. Sits on the section's
   navy via z-index -1 inside the inner's isolated stacking context. */
.hp-promise__inner::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	background: radial-gradient(45% 75% at 0% 42%,
		color-mix(in oklab, var(--color-radial) 34%, transparent) 0%,
		color-mix(in oklab, var(--color-radial) 13%, transparent) 45%,
		transparent 75%);
}
.hp-promise__heading {
	font-family: var(--font-heading-family);
	font-weight: 800;
	/* Very large — fills the section. Scales 3.15rem on mobile up to
	   ~14.4rem on widescreens (10% under the original). */
	font-size: clamp(3.15rem, 12.6vw, 14.4rem);
	line-height: 1.02;
	letter-spacing: -0.045em;
	margin: 0;
	padding-bottom: 0.15em;
	/* Layer the two phrases in the same grid cell so the heading auto-
	   sizes to whichever phrase is longer, and they can crossfade in
	   place without one pushing the other around. */
	display: inline-grid;
	grid-template-areas: 'swap';
}
.hp-promise__phrase {
	grid-area: swap;
	display: block;
	will-change: opacity;
	/* Timed, compositor-driven crossfade. JS toggles .is-swapped on the
	   section at the sticky midpoint; the transition does the fade. Do NOT
	   scrub opacity per scroll frame here — both phrases are giant
	   background-clip:text layers, and blending them at fractional opacity
	   every frame repaints both and stutters the brand thread that draws
	   through this section. */
	transition: opacity 0.6s ease;
}
/* Both phrases occupy the SAME grid cell at the SAME size and alignment
   and simply crossfade opacity in place — no offset, no movement. */
.hp-promise__phrase--lead {
	opacity: 1;
}
.hp-promise__phrase--accent {
	opacity: 0;
}
.hp-promise.is-swapped .hp-promise__phrase--lead {
	opacity: 0;
}
.hp-promise.is-swapped .hp-promise__phrase--accent {
	opacity: 1;
}
/* Each pipe-split chunk is its own block so both phrases render as two
   lines, keeping them the same height and position while crossfading. */
.hp-promise__line {
	display: block;
}
.hp-promise__dot {
	color: var(--color-accent);
	-webkit-text-fill-color: var(--color-accent);
}

/* Promise — MOBILE unpin. Same call as the WD outperform section: the
   200svh sticky pin fights the collapsing mobile browser toolbar, so the
   brand thread drawing through this beat stuttered and the pinned heading
   jumped. On ≤768px the pin is dropped entirely: the section is its
   natural height and BOTH phrases stack in flow, reading top-to-bottom as
   one statement ("We do one thing… Grow your business."). The thread's
   path3 frame shortens to the section's real height — see buildPath in
   main.js, which caps the drop at the section bottom. Desktop keeps the
   pinned crossfade unchanged. */
@media (max-width: 768px) {
	.hp-promise {
		min-height: 0;
		padding-block: clamp(4rem, 12vh, 7rem);
	}
	.hp-promise__inner {
		position: static;
		height: auto;
	}
	/* No sticky range → no crossfade. Drop the stacking grid so the two
	   phrases flow as separate blocks. */
	.hp-promise__heading { display: block; }
	/* JS may still toggle .is-swapped; force both phrases visible. */
	.hp-promise__phrase { opacity: 1 !important; }
	.hp-promise__phrase--accent { margin-top: 0.4em; }
}

/* ---------- Why Higher Performance / Our Process ----------
   Sits after the promise beat. Two stacked statements; each body paragraph
   lights up word-by-word as the user scrolls it through the viewport. JS sets
   --p (0 → 1, scroll progress through the block) on each [data-scroll-color]
   element. Per word: how lit it is = clamp( p·n − i , 0 , 1 ), so words turn
   on in reading order. Lit colour mixes from a dim navy-tinted paper toward
   full paper, with the brand pink bleeding through at the leading edge. */

.hp-why {
	position: relative;
	background: var(--color-primary);
	color: var(--color-paper);
	padding: clamp(5rem, 14vh, 12rem) clamp(1.25rem, 4vw, 3rem) 1.25rem;
}
.hp-why__inner {
	max-width: 70rem;
	margin: 0 auto;
	display: flex;
	flex-direction: column;
	gap: clamp(4rem, 10vh, 9rem);
}
.hp-why__block {
	display: flex;
	flex-direction: column;
	gap: clamp(1.5rem, 4vh, 3rem);
}
.hp-why__eyebrow {
	font-family: var(--font-heading-family);
	font-weight: 800;
	font-size: clamp(2rem, 5vw, 4.5rem);
	line-height: 1.04;
	letter-spacing: -0.03em;
	margin: 0;
}
.hp-why__body {
	font-family: var(--font-heading-family);
	font-weight: 700;
	font-size: clamp(1.6rem, 3.8vw, 3rem);
	line-height: 1.28;
	letter-spacing: -0.01em;
	margin: 0;
	--p: 0;
}

/* Scroll-driven word colour-fill. Each word's "lit" amount is the scroll
   progress (p) scaled across the word count (n) minus the word's index (i),
   clamped 0..1 — so the fill sweeps through the words in reading order.
   The pink leading-edge `text-shadow` glow was removed per user feedback —
   the colour transition alone reads as the lighting effect; the pink halo
   distracted from the word currently being read. */
.hp-scroll-color__word {
	--lit: clamp(0, calc(var(--p, 0) * var(--n, 1) - var(--i, 0)), 1);
	color: color-mix(
		in srgb,
		var(--color-paper) calc(var(--lit) * 100%),
		/* 0.52, not 0.45: the unlit floor must hold 4.5:1 on the #000010
		   ground (WCAG AA / Lighthouse color-contrast). 0.45 measured 4.06. */
		rgba(244, 241, 234, 0.52)
	);
	transition: color 0.08s linear;
}

/* No color-mix support, no JS, or reduced motion: show fully lit + readable. */
@supports not (color: color-mix(in srgb, white, black)) {
	.hp-scroll-color__word { color: var(--color-paper); }
}
@media (max-width: 0) /* prefers-reduced-motion neutralised — see main.js header */ {
	.hp-scroll-color__word {
		color: var(--color-paper);
		transition: none;
	}
}

/* ---------- Clients marquee ----------
   Infinite-scroll horizontal row of client names (or logos once images
   are dropped in). The track is duplicated in the markup so translating
   the wrapper by -50% loops seamlessly. Edge fades on the container
   soft-mask the ends so items appear/disappear smoothly. */

.hp-clients {
	position: relative;
	background: var(--color-primary);
	padding: clamp(3.5rem, 6vw, 6rem) 0;
	color: var(--color-paper);
	overflow: hidden;
}
/* Blue shadow at the left/right edges so the marquee reads as fading into the
   ground and only the centre logos are fully visible. */
.hp-clients::before,
.hp-clients::after {
	content: "";
	position: absolute;
	top: 0;
	bottom: 0;
	width: clamp(80px, 16vw, 240px);
	z-index: 2;
	pointer-events: none;
}
.hp-clients::before {
	left: 0;
	background: linear-gradient(90deg, var(--color-primary) 0%, var(--color-primary) 35%, transparent 100%);
}
.hp-clients::after {
	right: 0;
	background: linear-gradient(270deg, var(--color-primary) 0%, var(--color-primary) 35%, transparent 100%);
}
.hp-clients__inner {
	width: 100%;
	margin-inline: auto;
}
.hp-clients__label {
	font-family: var(--font-heading-family);
	font-size: clamp(0.75rem, 0.9vw, 0.9rem);
	font-weight: 600;
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: rgba(244, 241, 234, 0.45);
	margin: 0 0 clamp(1.5rem, 3vw, 2.5rem);
	padding-inline: clamp(1.5rem, 4vw, 3rem);
	text-align: center;
}

.hp-clients__marquee {
	display: flex;
	gap: clamp(2rem, 4vw, 4rem);
	width: max-content;
	/* Soft edges so items fade in/out rather than appearing abruptly. */
	mask-image: linear-gradient(90deg, transparent 0, #000 8%, #000 92%, transparent 100%);
	-webkit-mask-image: linear-gradient(90deg, transparent 0, #000 8%, #000 92%, transparent 100%);
	animation: hp-clients-marquee 40s linear infinite;
}
.hp-clients:hover .hp-clients__marquee {
	animation-play-state: paused;
}

.hp-clients__track {
	display: flex;
	align-items: center;
	gap: clamp(2rem, 4vw, 4rem);
	list-style: none;
	margin: 0;
	padding: 0;
}
/* No chip: logos sit straight on the navy ground. Sized by HEIGHT so the
   square source files all read at the same visual size (fixes "some too
   small"). */
.hp-clients__item {
	flex: 0 0 auto;
	display: flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	height: clamp(72px, 8vw, 112px);
	padding: 0;
	background: transparent;
	border-radius: 14px;
	transition:
		transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1),
		background-color 0.3s ease,
		padding 0.3s ease,
		box-shadow 0.3s ease;
}
.hp-clients__logo {
	height: 100%;
	width: auto;
	max-width: none;
	object-fit: contain;
	display: block;
	/* All white by default so every mark reads on the navy ground. */
	filter: brightness(0) invert(1);
	opacity: 0.82;
	transition: filter 0.3s ease, opacity 0.3s ease;
}
/* Hover reveals the logo's original colours on a paper chip, so even
   white-on-transparent or dark-coloured logos (Ground Tec, etc.) stay
   visible against the navy ground. Marquee already pauses on hover. */
.hp-clients__item:hover {
	background: var(--color-paper, #f4f1ea);
	/* Tight chip — hugs the logo at its original size. No vertical
	   padding so the chip never grows past the row height and can't
	   reach the "Proud to work with" label above. */
	padding: 0 clamp(0.5rem, 1vw, 0.9rem);
	box-shadow: 0 6px 18px -10px rgba(0, 0, 16, 0.4);
}
.hp-clients__item:hover .hp-clients__logo {
	filter: none;
	opacity: 1;
}

@keyframes hp-clients-marquee {
	from { transform: translateX(0); }
	to   { transform: translateX(-50%); }
}

@media (max-width: 0) /* prefers-reduced-motion neutralised — see main.js header */ {
	.hp-clients__marquee {
		animation: none;
		flex-wrap: wrap;
		justify-content: center;
		width: 100%;
		padding-inline: clamp(1.5rem, 4vw, 3rem);
	}
}

/* ---------- Featured work — stacking cards ----------
   Each .hp-portfolio__card is position: sticky at the top of the
   viewport. Because they're sequential, later cards scroll up and cover
   earlier ones; JS scales + dims the covered card to create a stacked-
   deck effect. The inner card is boxed (not full width) with rounded
   corners, matching the reference layout. */

.hp-portfolio {
	position: relative;
	/* Transparent so it sits on the work section's (scroll-shifting)
	   background; the cards carry their own colour. */
	background: transparent;
	padding: clamp(1rem, 2vw, 2rem) 0 0;
}
.hp-portfolio__eyebrow {
	font-family: var(--font-heading-family);
	font-size: clamp(0.75rem, 0.9vw, 0.9rem);
	font-weight: 600;
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: rgba(244, 241, 234, 0.45);
	margin: 0 0 clamp(1.5rem, 3vw, 2.5rem);
	padding-inline: clamp(1.5rem, 4vw, 3rem);
}
.hp-portfolio__card {
	position: sticky;
	top: 0;
	height: 100vh;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: clamp(1rem, 3vw, 2.5rem);
}
.hp-portfolio__card-inner {
	position: relative;
	width: 100%;
	max-width: 1500px;
	height: min(78vh, 720px);
	border-radius: clamp(20px, 2vw, 36px);
	overflow: hidden;
	box-shadow: 0 40px 90px rgba(0, 0, 0, 0.45);
	transform-origin: center top;
	will-change: transform, opacity;
}
/* Card background. The per-project brand gradient is the base layer / fallback;
   when real imagery is present a covering <img> sits over it, plus a scrim to
   keep the white overlay text legible over photography. */
.hp-portfolio__media {
	position: absolute;
	inset: 0;
	background: linear-gradient(135deg, var(--card-a) 0%, var(--card-b) 100%);
	overflow: hidden;
}
.hp-portfolio__img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center;
	display: block;
}
/* Contrast scrim over the photo: darker at the edges where the project name
   and services sit, lighter through the middle behind the View-work button. */
.hp-portfolio__card-inner.has-image .hp-portfolio__media::after {
	content: "";
	position: absolute;
	inset: 0;
	background:
		linear-gradient(90deg, rgba(0, 0, 16, 0.62) 0%, rgba(0, 0, 16, 0.12) 32%, rgba(0, 0, 16, 0.12) 68%, rgba(0, 0, 16, 0.62) 100%),
		linear-gradient(0deg, rgba(0, 0, 16, 0.45) 0%, rgba(0, 0, 16, 0) 45%);
	pointer-events: none;
}
/* Belt + braces over photography: a soft shadow guarantees the white name +
   services stay legible even on the lightest images (bright sky, pale walls). */
.hp-portfolio__card-inner.has-image .hp-portfolio__name,
.hp-portfolio__card-inner.has-image .hp-portfolio__service {
	text-shadow: 0 1px 14px rgba(0, 0, 16, 0.55);
}
.hp-portfolio__overlay {
	position: absolute;
	inset: 0;
	display: grid;
	grid-template-columns: 1fr auto 1fr;
	align-items: center;
	gap: clamp(1rem, 3vw, 3rem);
	padding: clamp(2rem, 5vw, 4.5rem);
}
.hp-portfolio__name {
	font-family: var(--font-heading-family);
	font-weight: 800;
	font-size: clamp(2rem, 4.5vw, 5rem);
	line-height: 1.0;
	letter-spacing: -0.03em;
	margin: 0;
	max-width: 12ch;
}
.hp-portfolio__card--light .hp-portfolio__name,
.hp-portfolio__card--light .hp-portfolio__service { color: var(--color-white); }
.hp-portfolio__card--dark .hp-portfolio__name,
.hp-portfolio__card--dark .hp-portfolio__service { color: var(--color-primary); }

/* Circular View-work button, centred over the media. */
.hp-portfolio__view {
	justify-self: center;
	display: flex;
	align-items: center;
	justify-content: center;
	width: clamp(7rem, 11vw, 10rem);
	height: clamp(7rem, 11vw, 10rem);
	border-radius: 50%;
	background: rgba(255, 255, 255, 0.16);
	backdrop-filter: blur(6px);
	-webkit-backdrop-filter: blur(6px);
	border: 1px solid rgba(255, 255, 255, 0.4);
	color: var(--color-white);
	font-family: var(--font-heading-family);
	font-weight: 700;
	font-size: clamp(0.875rem, 1.1vw, 1.0625rem);
	line-height: 1.05;
	text-align: center;
	text-transform: uppercase;
	letter-spacing: 0.02em;
	transition: transform 0.3s ease, background 0.3s ease;
}
.hp-portfolio__view:hover {
	transform: scale(1.08);
	background: rgba(255, 255, 255, 0.28);
	color: var(--color-white);
}

/* Services list, right-aligned with divider rules between items. */
.hp-portfolio__services {
	justify-self: end;
	list-style: none;
	margin: 0;
	padding: 0;
	text-align: right;
	max-width: 18ch;
}
.hp-portfolio__service {
	font-family: var(--font-heading-family);
	font-weight: 600;
	font-size: clamp(0.875rem, 1.2vw, 1.25rem);
	padding: clamp(0.5rem, 1vw, 0.85rem) 0;
	border-top: 1px solid rgba(255, 255, 255, 0.25);
}
.hp-portfolio__card--dark .hp-portfolio__service {
	border-top-color: rgba(8, 20, 39, 0.25);
}
.hp-portfolio__service:first-child { border-top: 0; }

@media (max-width: 768px) {
	.hp-portfolio__overlay {
		grid-template-columns: 1fr;
		grid-template-rows: auto 1fr auto;
		justify-items: start;
		text-align: left;
	}
	.hp-portfolio__view { justify-self: start; }
	.hp-portfolio__services { justify-self: start; text-align: left; }
	.hp-portfolio__service { border-top: 0; padding: 0.2rem 0; }
}

@media (max-width: 0) /* prefers-reduced-motion neutralised — see main.js header */ {
	.hp-portfolio__card { position: relative; height: auto; }
	.hp-portfolio__card-inner { transform: none !important; opacity: 1 !important; }
}

/* ---------- Our Insights (blog teaser) ----------
   Editorial layout: large featured post + compact list of the rest.
   Image-zoom on hover, card lift, animated arrow reveal. */

.hp-insights {
	position: relative;
	background: var(--color-primary);
	color: var(--color-paper);
	padding: clamp(5rem, 11vw, 9rem) clamp(1.5rem, 4vw, 3rem);
}
.hp-insights__inner {
	width: min(1400px, 100%);
	margin-inline: auto;
}
.hp-insights__header {
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
	gap: 2rem;
	flex-wrap: wrap;
	margin-bottom: clamp(2.5rem, 5vw, 4rem);
}
.hp-insights__eyebrow {
	font-family: var(--font-heading-family);
	font-size: clamp(0.75rem, 0.9vw, 0.9rem);
	font-weight: 600;
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: var(--color-accent);
	margin: 0 0 0.75rem;
}
.hp-insights__heading {
	font-family: var(--font-heading-family);
	font-weight: 800;
	font-size: clamp(2rem, 4.5vw, 3.75rem);
	line-height: 1.0;
	letter-spacing: -0.03em;
	margin: 0;
}

.hp-insights__layout {
	display: grid;
	grid-template-columns: 1.3fr 1fr;
	gap: clamp(2rem, 4vw, 4rem);
	align-items: start;
}

/* Shared media + chip styling */
.hp-insights__media,
.hp-insights__row-media {
	display: block;
	overflow: hidden;
	border-radius: clamp(16px, 1.5vw, 24px);
	background: var(--color-secondary);
}
.hp-insights__media img,
.hp-insights__row-media img,
.hp-insights__media-fallback {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 0.6s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.hp-insights__media-fallback {
	min-height: 100%;
	background: linear-gradient(135deg, var(--color-secondary), var(--color-accent));
}
.hp-insights__chip {
	display: inline-block;
	font-family: var(--font-heading-family);
	font-size: 0.6875rem;
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--color-accent-light);
	border: 1px solid rgba(253, 46, 152, 0.4);
	border-radius: var(--radius-pill);
	padding: 0.25rem 0.7rem;
}
.hp-insights__metarow {
	display: flex;
	align-items: center;
	gap: 0.85rem;
	margin-bottom: 0.85rem;
}
.hp-insights__meta {
	font-size: 0.8125rem;
	color: rgba(244, 241, 234, 0.55);
	letter-spacing: 0.01em;
}

/* Featured post */
.hp-insights__featured {
	display: block;
	color: inherit;
	text-decoration: none;
}
.hp-insights__featured .hp-insights__media {
	aspect-ratio: 16 / 10;
	margin-bottom: 1.5rem;
}
.hp-insights__body { display: block; }
.hp-insights__title {
	display: block;
	font-family: var(--font-heading-family);
	font-weight: 700;
	letter-spacing: -0.02em;
	line-height: 1.1;
	color: var(--color-paper);
	transition: color 0.25s ease;
}
.hp-insights__title--lg {
	font-size: clamp(1.5rem, 2.6vw, 2.5rem);
	margin-bottom: 0.75rem;
}
.hp-insights__excerpt {
	display: block;
	color: rgba(244, 241, 234, 0.7);
	line-height: 1.55;
	margin-bottom: 1rem;
	max-width: 52ch;
}
.hp-insights__more {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	font-family: var(--font-heading-family);
	font-weight: 600;
	color: var(--color-accent);
}
.hp-insights__featured:hover .hp-insights__media img,
.hp-insights__featured:hover .hp-insights__media-fallback {
	transform: scale(1.05);
}
.hp-insights__featured:hover .hp-insights__title { color: var(--color-accent-light); }
.hp-insights__featured:hover .hp-insights__more .hp-btn__arrow { transform: translateX(4px); }

/* Compact list */
.hp-insights__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: clamp(1rem, 2vw, 1.75rem);
}
.hp-insights__row {
	display: grid;
	grid-template-columns: 96px 1fr auto;
	align-items: center;
	gap: 1.25rem;
	padding-bottom: clamp(1rem, 2vw, 1.75rem);
	border-bottom: 1px solid rgba(244, 241, 234, 0.12);
	color: inherit;
	text-decoration: none;
}
.hp-insights__list li:last-child .hp-insights__row {
	border-bottom: 0;
	padding-bottom: 0;
}
.hp-insights__row-media {
	width: 96px;
	height: 72px;
	border-radius: 14px;
	flex: 0 0 auto;
}
.hp-insights__row .hp-insights__metarow { margin-bottom: 0.4rem; }
.hp-insights__row .hp-insights__title { font-size: clamp(1rem, 1.4vw, 1.25rem); }
.hp-insights__row-arrow {
	color: var(--color-accent);
	opacity: 0;
	transform: translateX(-6px);
	transition: opacity 0.25s ease, transform 0.25s ease;
}
.hp-insights__row:hover { border-bottom-color: rgba(253, 46, 152, 0.4); }
.hp-insights__row:hover .hp-insights__row-media img,
.hp-insights__row:hover .hp-insights__media-fallback { transform: scale(1.08); }
.hp-insights__row:hover .hp-insights__title { color: var(--color-accent-light); }
.hp-insights__row:hover .hp-insights__row-arrow { opacity: 1; transform: translateX(0); }

/* ---- Scroll-reveal entrance (staggered fade-up) ----
   wireRevealOnScroll() adds .is-pending immediately and swaps to
   .is-visible when the section enters the viewport. */
.hp-insights__header > div,
.hp-insights__header > .hp-btn,
.hp-insights__featured,
.hp-insights__list li {
	transition: opacity 0.6s ease, transform 0.6s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.hp-insights.is-pending .hp-insights__header > div,
.hp-insights.is-pending .hp-insights__header > .hp-btn,
.hp-insights.is-pending .hp-insights__featured,
.hp-insights.is-pending .hp-insights__list li {
	opacity: 0;
	transform: translateY(34px);
}
.hp-insights.is-visible .hp-insights__header > div,
.hp-insights.is-visible .hp-insights__header > .hp-btn,
.hp-insights.is-visible .hp-insights__featured,
.hp-insights.is-visible .hp-insights__list li {
	opacity: 1;
	transform: translateY(0);
}
/* Stagger: header → featured → list items in sequence. */
.hp-insights.is-visible .hp-insights__header > div      { transition-delay: 0s; }
.hp-insights.is-visible .hp-insights__header > .hp-btn  { transition-delay: 0.08s; }
.hp-insights.is-visible .hp-insights__featured          { transition-delay: 0.16s; }
.hp-insights.is-visible .hp-insights__list li:nth-child(1) { transition-delay: 0.24s; }
.hp-insights.is-visible .hp-insights__list li:nth-child(2) { transition-delay: 0.32s; }
.hp-insights.is-visible .hp-insights__list li:nth-child(3) { transition-delay: 0.40s; }
.hp-insights.is-visible .hp-insights__list li:nth-child(4) { transition-delay: 0.48s; }
.hp-insights.is-visible .hp-insights__list li:nth-child(5) { transition-delay: 0.56s; }

/* Lift the featured card on hover for a touch more depth. */
.hp-insights__featured { will-change: transform; }
.hp-insights.is-visible .hp-insights__featured:hover {
	transform: translateY(-5px);
	transition-delay: 0s;
}

@media (max-width: 0) /* prefers-reduced-motion neutralised — see main.js header */ {
	.hp-insights__header > div,
	.hp-insights__header > .hp-btn,
	.hp-insights__featured,
	.hp-insights__list li {
		opacity: 1 !important;
		transform: none !important;
		transition: none;
	}
}

/* Tablet: stack the featured post above the list, keep generous gaps. */
@media (max-width: 900px) {
	.hp-insights__layout {
		grid-template-columns: 1fr;
		gap: clamp(2rem, 5vw, 3rem);
	}
	.hp-insights__header {
		flex-direction: column;
		align-items: flex-start;
		gap: 1.25rem;
	}
	.hp-insights__excerpt { max-width: none; }
}
/* Mobile: tighten spacing, shrink the row thumbnails, ensure the arrow
   is always visible (no hover on touch) and nothing overflows. */
@media (max-width: 600px) {
	.hp-insights { padding-left: clamp(1.1rem, 5vw, 1.5rem); padding-right: clamp(1.1rem, 5vw, 1.5rem); }
	.hp-insights__featured .hp-insights__media { margin-bottom: 1.1rem; }
	.hp-insights__title--lg { font-size: clamp(1.35rem, 6vw, 1.75rem); }
	.hp-insights__row {
		grid-template-columns: 72px 1fr;
		gap: 0.9rem;
		align-items: start;
	}
	.hp-insights__row-media { width: 72px; height: 54px; }
	.hp-insights__row-arrow { display: none; }
	.hp-insights__metarow { flex-wrap: wrap; gap: 0.5rem; row-gap: 0.35rem; }
}

/* ---------- Blog index (home.php) ----------
   Featured first post spanning the grid, then a responsive card grid.
   Shares the Insights hover language: image-zoom, title colour, arrow. */

.hp-blog {
	background: var(--color-primary);
	color: var(--color-paper);
	padding: clamp(7rem, 12vw, 11rem) clamp(1.5rem, 4vw, 3rem) clamp(5rem, 10vw, 8rem);
	min-height: 100vh;
	position: relative;
	isolation: isolate;
}
/* Brand glow (logo brand image, 2026-07-29) on the blog page top. The
   wrapper spans the whole page, so cap the glow to the first viewport. */
.hp-blog::before {
	content: "";
	position: absolute;
	inset: 0 0 auto 0;
	height: 100svh;
	z-index: -1;
	pointer-events: none;
	background: radial-gradient(45% 75% at 0% 42%,
		color-mix(in oklab, var(--color-radial) 34%, transparent) 0%,
		color-mix(in oklab, var(--color-radial) 13%, transparent) 45%,
		transparent 75%);
}
.hp-blog__header {
	width: min(1400px, 100%);
	margin: 0 auto clamp(2.5rem, 5vw, 4rem);
}
.hp-blog__eyebrow {
	font-family: var(--font-heading-family);
	font-size: clamp(0.75rem, 0.9vw, 0.9rem);
	font-weight: 600;
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: var(--color-accent);
	margin: 0 0 0.75rem;
}
.hp-blog__heading {
	font-family: var(--font-heading-family);
	font-weight: 800;
	font-size: clamp(2.5rem, 6vw, 5.5rem);
	line-height: 1.0;
	letter-spacing: -0.035em;
	margin: 0 0 clamp(1.5rem, 3vw, 2.5rem);
}
.hp-blog__rail {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	list-style: none;
	margin: 0;
	padding: 0;
}
.hp-blog__rail li a {
	display: inline-block;
	font-family: var(--font-heading-family);
	font-size: 0.875rem;
	font-weight: 600;
	color: rgba(244, 241, 234, 0.7);
	border: 1px solid rgba(244, 241, 234, 0.18);
	border-radius: var(--radius-pill);
	padding: 0.4rem 1rem;
	transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
.hp-blog__rail li a:hover,
.hp-blog__rail li.is-active a {
	background: var(--color-accent);
	border-color: var(--color-accent);
	color: var(--color-white);
}

.hp-blog__grid {
	width: min(1400px, 100%);
	margin-inline: auto;
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: clamp(1.5rem, 3vw, 2.5rem);
}
.hp-blog__card { margin: 0; }
.hp-blog__card--featured { grid-column: 1 / -1; }

.hp-blog__link {
	display: block;
	color: inherit;
	text-decoration: none;
}
.hp-blog__card--featured .hp-blog__link {
	display: grid;
	grid-template-columns: 1.3fr 1fr;
	gap: clamp(1.5rem, 4vw, 3.5rem);
	align-items: center;
}
.hp-blog__media {
	display: block;
	overflow: hidden;
	border-radius: clamp(16px, 1.5vw, 24px);
	background: var(--color-secondary);
	aspect-ratio: 16 / 11;
	margin-bottom: 1.25rem;
}
.hp-blog__card--featured .hp-blog__media {
	aspect-ratio: 16 / 10;
	margin-bottom: 0;
}
.hp-blog__media img,
.hp-blog__media-fallback {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 0.6s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.hp-blog__media-fallback {
	background: linear-gradient(135deg, var(--color-secondary), var(--color-accent));
}
.hp-blog__metarow {
	display: flex;
	align-items: center;
	gap: 0.85rem;
	margin-bottom: 0.75rem;
}
.hp-blog__chip {
	display: inline-block;
	font-family: var(--font-heading-family);
	font-size: 0.6875rem;
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--color-accent-light);
	border: 1px solid rgba(253, 46, 152, 0.4);
	border-radius: var(--radius-pill);
	padding: 0.25rem 0.7rem;
}
.hp-blog__meta {
	font-size: 0.8125rem;
	color: rgba(244, 241, 234, 0.55);
}
.hp-blog__title {
	display: block;
	font-family: var(--font-heading-family);
	font-weight: 700;
	letter-spacing: -0.02em;
	line-height: 1.12;
	font-size: clamp(1.125rem, 1.6vw, 1.5rem);
	margin-bottom: 0.6rem;
	transition: color 0.25s ease;
}
.hp-blog__card--featured .hp-blog__title {
	font-size: clamp(1.75rem, 3.2vw, 3rem);
	margin-bottom: 0.85rem;
}
.hp-blog__excerpt {
	display: block;
	color: rgba(244, 241, 234, 0.7);
	line-height: 1.55;
	margin-bottom: 1rem;
	max-width: 56ch;
}
.hp-blog__more {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	font-family: var(--font-heading-family);
	font-weight: 600;
	color: var(--color-accent);
}
.hp-blog__card:not(.hp-blog__card--featured) .hp-blog__more { display: none; }
.hp-blog__link:hover .hp-blog__media img,
.hp-blog__link:hover .hp-blog__media-fallback { transform: scale(1.05); }
.hp-blog__link:hover .hp-blog__title { color: var(--color-accent-light); }
.hp-blog__link:hover .hp-blog__more .hp-btn__arrow { transform: translateX(4px); }

.hp-blog__pagination {
	width: min(1400px, 100%);
	margin: clamp(3rem, 6vw, 5rem) auto 0;
}
.hp-blog__pagination .page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 2.75rem;
	height: 2.75rem;
	padding: 0 0.5rem;
	margin: 0 0.2rem;
	border-radius: var(--radius-pill);
	font-family: var(--font-heading-family);
	font-weight: 600;
	color: var(--color-paper);
	border: 1px solid rgba(244, 241, 234, 0.18);
	transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
.hp-blog__pagination .page-numbers.current,
.hp-blog__pagination a.page-numbers:hover {
	background: var(--color-accent);
	border-color: var(--color-accent);
	color: var(--color-white);
}
.hp-blog__empty {
	width: min(1400px, 100%);
	margin-inline: auto;
	color: rgba(244, 241, 234, 0.7);
}

@media (max-width: 900px) {
	.hp-blog__grid { grid-template-columns: repeat(2, 1fr); }
	.hp-blog__card--featured .hp-blog__link { grid-template-columns: 1fr; }
	.hp-blog__card--featured .hp-blog__media { margin-bottom: 1.25rem; }
}
@media (max-width: 560px) {
	.hp-blog__grid { grid-template-columns: 1fr; }
}

/* ---------- Single post ----------
   Editorial article with a sticky right "floating" sidebar (share +
   latest posts) and a slim reading-progress bar. */

.hp-reading-progress {
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	height: 3px;
	background: transparent;
	z-index: 60;
	pointer-events: none;
}
.hp-reading-progress span {
	display: block;
	height: 100%;
	width: 0;
	background: var(--color-accent);
	transition: width 0.1s linear;
}

.hp-single {
	background: var(--color-primary);
	color: var(--color-paper);
	/* Top padding only needs to clear the fixed header pills (~5rem),
	   not the big hero allowance. */
	padding: clamp(5rem, 7vw, 6.5rem) clamp(1.5rem, 4vw, 3rem) clamp(5rem, 10vw, 8rem);
	position: relative;
	isolation: isolate;
}
/* Brand glow (logo brand image, 2026-07-29) behind the post header —
   capped to the first viewport of the page-long wrapper. */
.hp-single::before {
	content: "";
	position: absolute;
	inset: 0 0 auto 0;
	height: 100svh;
	z-index: -1;
	pointer-events: none;
	background: radial-gradient(45% 75% at 0% 42%,
		color-mix(in oklab, var(--color-radial) 34%, transparent) 0%,
		color-mix(in oklab, var(--color-radial) 13%, transparent) 45%,
		transparent 75%);
}
.hp-single__article { margin: 0; }

.hp-single__header {
	width: min(900px, 100%);
	margin: 0 auto clamp(1.75rem, 3.5vw, 2.5rem);
	text-align: center;
}
.hp-single__back {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	font-family: var(--font-heading-family);
	font-weight: 600;
	font-size: 0.875rem;
	color: rgba(244, 241, 234, 0.6);
	margin-bottom: 1.5rem;
}
.hp-single__back:hover { color: var(--color-accent); }
.hp-single__chip {
	display: inline-block;
	font-family: var(--font-heading-family);
	font-size: 0.6875rem;
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--color-accent-light);
	border: 1px solid rgba(253, 46, 152, 0.4);
	border-radius: var(--radius-pill);
	padding: 0.3rem 0.85rem;
	margin-bottom: 1.25rem;
}
.hp-single__chip:hover { background: rgba(253, 46, 152, 0.15); color: var(--color-accent-light); }
.hp-single__title {
	font-family: var(--font-heading-family);
	font-weight: 800;
	font-size: clamp(2rem, 5vw, 4rem);
	line-height: 1.05;
	letter-spacing: -0.03em;
	margin: 0 0 1.5rem;
}
.hp-single__meta {
	display: inline-flex;
	align-items: center;
	gap: 0.75rem;
	text-align: left;
}
.hp-single__avatar { border-radius: 50%; }
.hp-single__byline { display: flex; flex-direction: column; line-height: 1.2; }
.hp-single__author { font-family: var(--font-heading-family); font-weight: 600; }
.hp-single__sub { font-size: 0.8125rem; color: rgba(244, 241, 234, 0.55); }

.hp-single__feature {
	width: min(1280px, 100%);
	margin: 0 auto clamp(2.5rem, 5vw, 4rem);
}
.hp-single__feature img {
	width: 100%;
	border-radius: clamp(16px, 2vw, 28px);
}

/* Three columns: TOC (left) | content (centre, wide) | sidebar (right). */
.hp-single__layout {
	width: min(1320px, 100%);
	margin-inline: auto;
	display: grid;
	grid-template-columns: 220px minmax(0, 1fr) 300px;
	gap: clamp(2rem, 4vw, 3.5rem);
	align-items: start;
}

/* On-this-page table of contents.
   --toc-top sets how far below the viewport top it sticks; the matching
   max-height lets a long list scroll within its own thin scrollbar instead
   of overflowing past the screen. */
.hp-single__toc {
	--toc-top: clamp(3rem, 6vw, 5rem);
	position: sticky;
	top: var(--toc-top);
	align-self: start;
	max-height: calc(100vh - var(--toc-top) - 1.5rem);
	overflow-y: auto;
	overscroll-behavior: contain;
	padding-right: 0.5rem;
	scrollbar-width: thin;
	scrollbar-color: rgba(244, 241, 234, 0.3) transparent;
}
.hp-single__toc::-webkit-scrollbar { width: 6px; }
.hp-single__toc::-webkit-scrollbar-thumb { background: rgba(244, 241, 234, 0.25); border-radius: 3px; }
.hp-single__toc::-webkit-scrollbar-track { background: transparent; }
.hp-single__toc-nav { display: flex; flex-direction: column; gap: 0.1rem; }
.hp-single__toc-link {
	display: block;
	font-size: 0.875rem;
	line-height: 1.35;
	color: rgba(244, 241, 234, 0.55);
	padding: 0.4rem 0 0.4rem 0.85rem;
	border-left: 2px solid rgba(244, 241, 234, 0.14);
	transition: color 0.2s ease, border-color 0.2s ease;
}
.hp-single__toc-link:hover { color: var(--color-paper); }
.hp-single__toc-link--h3 { padding-left: 1.75rem; font-size: 0.8125rem; }
.hp-single__toc-link.is-active {
	color: var(--color-accent);
	border-left-color: var(--color-accent);
}

/* Article body typography */
.hp-single__content {
	font-size: clamp(1.0625rem, 1.25vw, 1.1875rem);
	line-height: 1.75;
	color: rgba(244, 241, 234, 0.88);
}
.hp-single__content > * { margin-bottom: 1.4em; }
/* Wide editor-inserted tables/code must scroll inside their own box; without
   this they'd be silently clipped by body{overflow-x:clip} on mobile. */
.hp-single__content :is(table, pre) { display: block; max-width: 100%; overflow-x: auto; }
.hp-single__content pre { white-space: pre; }
.hp-single__content h2 { font-size: clamp(1.5rem, 2.4vw, 2.25rem); line-height: 1.2; margin-top: 1.8em; color: var(--color-paper); }
.hp-single__content h3 { font-size: clamp(1.25rem, 1.8vw, 1.6rem); line-height: 1.25; margin-top: 1.6em; color: var(--color-paper); }
.hp-single__content a { color: var(--color-accent); text-decoration: underline; text-underline-offset: 3px; }
.hp-single__content a:hover { color: var(--color-accent-light); }
.hp-single__content img { border-radius: 16px; }
.hp-single__content blockquote {
	margin: 2em 0;
	padding: 0.5em 0 0.5em 1.5em;
	border-left: 3px solid var(--color-accent);
	font-size: 1.2em;
	font-style: italic;
	color: var(--color-paper);
}
.hp-single__content ul, .hp-single__content ol { padding-left: 1.4em; }
.hp-single__content li { margin-bottom: 0.5em; }

.hp-single__tags {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin-top: 2.5rem;
	padding-top: 2rem;
	border-top: 1px solid rgba(244, 241, 234, 0.12);
}
.hp-single__tag {
	font-size: 0.875rem;
	color: rgba(244, 241, 234, 0.6);
	border: 1px solid rgba(244, 241, 234, 0.18);
	border-radius: var(--radius-pill);
	padding: 0.3rem 0.8rem;
}
.hp-single__tag:hover { border-color: var(--color-accent); color: var(--color-accent); }

/* Floating sidebar */
.hp-single__sidebar-sticky {
	position: sticky;
	top: clamp(5rem, 9vw, 7rem);
	display: flex;
	flex-direction: column;
	gap: 2.5rem;
}
.hp-single__widget-label {
	font-family: var(--font-heading-family);
	font-size: 0.6875rem;
	font-weight: 700;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: rgba(244, 241, 234, 0.45);
	margin: 0 0 1rem;
}
.hp-single__share-row { display: flex; gap: 0.6rem; }
.hp-single__share-btn {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.75rem;
	height: 2.75rem;
	border-radius: 50%;
	background: rgba(244, 241, 234, 0.06);
	border: 1px solid rgba(244, 241, 234, 0.16);
	color: var(--color-paper);
	cursor: pointer;
	transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}
.hp-single__share-btn svg { width: 1.05rem; height: 1.05rem; }
.hp-single__share-btn:hover {
	background: var(--color-accent);
	border-color: var(--color-accent);
	color: var(--color-white);
	transform: translateY(-2px);
}
.hp-single__copied {
	position: absolute;
	bottom: calc(100% + 0.4rem);
	left: 50%;
	transform: translateX(-50%);
	font-size: 0.6875rem;
	font-weight: 600;
	white-space: nowrap;
	background: var(--color-accent);
	color: var(--color-white);
	padding: 0.2rem 0.5rem;
	border-radius: 6px;
	opacity: 0;
	transition: opacity 0.2s ease;
	pointer-events: none;
}
.hp-single__share-copy.is-copied .hp-single__copied { opacity: 1; }

.hp-single__latest ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1.1rem; }
.hp-single__latest-item {
	display: grid;
	grid-template-columns: 64px 1fr;
	gap: 0.85rem;
	align-items: center;
	color: inherit;
}
.hp-single__latest-media {
	width: 64px;
	height: 64px;
	border-radius: 12px;
	overflow: hidden;
	background: var(--color-secondary);
	flex: 0 0 auto;
}
.hp-single__latest-media img,
.hp-single__latest-fallback {
	width: 100%; height: 100%; object-fit: cover; display: block;
	transition: transform 0.4s ease;
}
.hp-single__latest-fallback { background: linear-gradient(135deg, var(--color-secondary), var(--color-accent)); }
.hp-single__latest-date { display: block; font-size: 0.75rem; color: rgba(244, 241, 234, 0.5); margin-bottom: 0.2rem; }
.hp-single__latest-title {
	display: block;
	font-family: var(--font-heading-family);
	font-weight: 600;
	font-size: 0.9375rem;
	line-height: 1.25;
	transition: color 0.2s ease;
}
.hp-single__latest-item:hover .hp-single__latest-title { color: var(--color-accent-light); }
.hp-single__latest-item:hover .hp-single__latest-media img,
.hp-single__latest-item:hover .hp-single__latest-fallback { transform: scale(1.08); }

/* Tablet: drop the left TOC, keep content + right sidebar. */
@media (max-width: 1100px) {
	.hp-single__layout { grid-template-columns: minmax(0, 1fr) 280px; }
	.hp-single__toc { display: none; }
}
/* Mobile: single column, right sidebar reflows below the article. */
@media (max-width: 900px) {
	.hp-single__layout { grid-template-columns: 1fr; }
	.hp-single__sidebar-sticky { position: static; flex-direction: row; flex-wrap: wrap; gap: 2rem; }
	.hp-single__latest { flex: 1 1 100%; }
}

/* ---------- Positioning ---------- */

.hp-positioning {
	position: relative;
	background: var(--color-primary);
	padding: 0 0 clamp(4rem, 8vw, 8rem);
}
.hp-positioning__inner {
	width: 100%;
	margin-inline: auto;
	display: grid;
	grid-template-columns: 1.25fr 1fr;
	gap: clamp(3rem, 6vw, 6rem);
	align-items: center;
}
.hp-positioning__heading {
	font-size: clamp(2.25rem, 5.8vw, 5.25rem);
	font-weight: 800;
	line-height: 0.98;
	letter-spacing: -0.035em;
	margin-bottom: var(--space-4);
}
.hp-positioning__body {
	font-size: clamp(1.0625rem, 1.2vw, 1.25rem);
	color: rgba(244, 241, 234, 0.78);
	max-width: 52ch;
	line-height: 1.55;
}
.hp-positioning__body p { margin-bottom: var(--space-3); }
.hp-positioning__collage {
	position: relative;
	height: 520px;
}
.hp-positioning__tile {
	position: absolute;
	border-radius: var(--radius-lg);
	overflow: hidden;
	box-shadow: 0 30px 80px rgba(0,0,0,0.4);
}
.hp-positioning__tile img { width: 100%; height: 100%; object-fit: cover; }
.hp-positioning__tile--1 { width: 60%; height: 70%; top: 0; left: 10%; transform: rotate(-4deg); }
.hp-positioning__tile--2 { width: 50%; height: 55%; bottom: 5%; right: 0; transform: rotate(5deg); }
.hp-positioning__tile--3 { width: 38%; height: 40%; top: 30%; right: 25%; transform: rotate(-2deg); }
.hp-positioning__tile--placeholder {
	background: linear-gradient(135deg, var(--color-secondary), var(--color-accent));
}

/* Showreel: a video that scales from a small box to full-width as the
   user scrolls past the section's midpoint. Driven by main.js setting
   --hp-reel-progress on the [data-showreel] block as 0 → 1.
   The frame is `position: sticky` so the video stays centred in the
   viewport while the surrounding section provides the scroll-room for
   the scale animation to play out. */
/* Full-bleed showreel: the video fills the whole viewport (no rounding,
   no inset) and the sticky frame pins it in place while the surrounding
   section provides the scroll-room. The pin reads as a brief "loading
   screen" — the brand thread keeps drawing across the bottom over the
   top of the video — until the user scrolls past and the page moves on.
   min-height controls how long the video stays pinned. */
.hp-positioning__showreel {
	position: relative;
	/* Gap above the video. Was 25vh, which combined with the sticky
	   100dvh frame centring the scale-0.3 video left ~650px of empty
	   navy between the hero and the visible video box (user-reported).
	   8vh still clears the brand-thread cascade (its exit horizontal
	   ends ~110px below the hero; the visible video box starts ~440px
	   below it) while pulling the showreel a full ~180px closer. The
	   pin + scale animation is scroll-driven and simply starts a touch
	   earlier — nothing about it changes. */
	margin-top: 8vh;
	min-height: 200vh; /* extra scroll-room so the slower scale plays out, then holds full-size while pinned */
	--hp-reel-progress: 0; /* JS drives 0 → 1 as the user scrolls in */
}
.hp-positioning__showreel-frame {
	position: sticky;
	top: 0;
	/* Break out to the VISIBLE viewport width. 100vw includes the scrollbar,
	   so it overflowed ~scrollbar-width and the sides got clipped; subtract the
	   measured scrollbar width (--sbw, set in JS) and re-centre. */
	width: calc(100vw - var(--sbw, 0px));
	margin-left: calc(50% - 50vw + var(--sbw, 0px) / 2);
	height: 100dvh;
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
}
/* The video always fills the frame; as the user scrolls in, --hp-reel-progress
   climbs 0 → 1 and we scale it up from a small, rounded, shadowed box to the
   full-bleed screen (radius and shadow ease away as it reaches full size). */
.hp-positioning__showreel-video {
	display: block;
	/* Size the element to the video's own 16:9 shape (largest that fits the
	   frame, capped by both width and height) so there's no letterbox box.
	   The element edges = the visible video edges, so border-radius rounds the
	   actual video corners instead of an invisible letterbox box. */
	width: min(100%, calc((100dvh) * 16 / 9));
	height: auto;
	aspect-ratio: 16 / 9;
	max-height: 100%;
	object-fit: cover; /* element already matches 16:9, so this never crops */
	background: var(--color-primary, #081427);
	transform-origin: center center;
	transform: scale(calc(0.3 + 0.6 * var(--hp-reel-progress))); /* scales up to 0.9 (90%) at full progress, leaving a margin so nothing reaches the screen edge */
	border-radius: calc(12px + (1 - var(--hp-reel-progress)) * 88px); /* eases from 100px down to a resting 12px so the showreel keeps a subtle rounding at full size */
	box-shadow: 0 40px 100px rgba(0, 0, 0, calc(0.5 * (1 - var(--hp-reel-progress))));
	will-change: transform, border-radius;
}
/* Mobile: pin the 16:9 strip itself, not a full-viewport box. The
   100dvh flex frame centred a ~66px-tall scale-0.3 video inside an
   ~844px viewport, which alone put ~390px of empty navy between the
   hero and the visible video (user-reported "huge gap"). The frame now
   wraps the video (height: auto) and the sticky top offset keeps the
   strip pinned at the viewport's vertical centre during the scroll
   scale-up — the animation reads identically; only the REST position
   changes, sitting directly below the hero. The video is width-limited
   16:9 on phones and tablets (viewport AR taller than 16:9) → height =
   56.25vw, so half is 28.125vw; the offset self-adjusts at every
   width-limited size, so tablets get the same fix. Above 1024px the
   viewport is wider than 16:9 and the video is height-limited — the
   original 100dvh frame stays there. */
@media (max-width: 1024px) {
	.hp-positioning__showreel-frame {
		height: auto;
		top: calc(50dvh - 28.125vw);
	}
}

@media (max-width: 0) /* prefers-reduced-motion neutralised — see main.js header */ {
	.hp-positioning__showreel { min-height: auto; margin-top: 10vh; --hp-reel-progress: 1; }
	.hp-positioning__showreel-frame { position: static; height: auto; }
	.hp-positioning__showreel-video { height: auto; aspect-ratio: 16 / 9; transform: none; border-radius: 0; box-shadow: none; }
}

/* ---------- As featured in (3D book) ----------
   The closed cover shows the magazine; on hover/focus it swings open on its
   left edge (the spine) to reveal an inside brand page. Built with CSS 3D
   transforms: the cover is a two-faced panel (front = magazine, back = paper)
   that rotates -158deg, uncovering the .hp-book__page sitting beneath it.
   ----------------------------------------------------- */
.hp-featured {
	position: relative; /* hosts the absolutely-positioned brand thread */
	background: var(--color-primary);
	color: var(--color-paper);
	/* Top padding tightened (was 7-12rem) now that no brand thread runs
	   between Why and Featured — the previous gap read as dead space
	   without the pink line filling it. */
	padding: clamp(3rem, 6vw, 6rem) 1.5rem clamp(4rem, 9vw, 7rem);
	overflow: hidden; /* contains the cover as it swings past the left edge,
	                     and clips the thread's off-canvas entry / exit runs */
}
/* Brand thread overlay — full-section SVG, path built by wireFeaturedThread
   (main.js) in real pixels so the vertical drop renders at the same stroke
   thickness as the horizontal runs (no preserveAspectRatio stretching). */
.hp-featured__thread {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	pointer-events: none;
	z-index: 0;
	overflow: visible;
}
.hp-featured__inner {
	position: relative;
	z-index: 1; /* copy + book sit above the thread */
	width: 100%;
	max-width: 1200px;
	margin-inline: auto;
	display: grid;
	grid-template-columns: 1.1fr 0.9fr;
	align-items: center;
	gap: clamp(2.5rem, 6vw, 6rem);
}
.hp-featured__copy {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
}
.hp-featured__kicker {
	font-size: 0.8125rem;
	text-transform: uppercase;
	letter-spacing: 0.22em;
	color: var(--color-accent);
	margin: 0 0 clamp(1rem, 2vw, 1.5rem);
	font-weight: 600;
}
.hp-featured__title {
	font-family: var(--font-heading-family);
	font-weight: 800;
	font-size: clamp(2.75rem, 7vw, 5.5rem);
	line-height: 0.98;
	letter-spacing: -0.035em;
	margin: 0;
}
.hp-featured__lead {
	font-family: var(--font-heading-family);
	font-weight: 600;
	font-size: clamp(1.125rem, 2.2vw, 1.625rem);
	line-height: 1.3;
	letter-spacing: -0.01em;
	color: color-mix(in srgb, currentColor 80%, transparent);
	max-width: 24ch;
	margin: clamp(1.25rem, 2.5vw, 1.75rem) 0 0;
}
/* The book sits in the right column. */
.hp-featured__media {
	display: flex;
	justify-content: center;
}
/* The book. Sized to the cover's 358:467 ratio; perspective gives the open
   swing its depth. extra right margin reserves room for the opened cover so
   the layout stays centred. */
.hp-book {
	position: relative;
	width: clamp(220px, 24vw, 320px);
	aspect-ratio: 358 / 467;
	perspective: 2200px;
	margin-bottom: clamp(1.75rem, 3vw, 2.5rem);
	cursor: pointer;
	border-radius: 4px 10px 10px 4px;
	outline: none;
	/* Soft ground shadow under the closed book. */
	filter: drop-shadow(0 24px 40px rgba(0, 0, 0, 0.45));
	transition: filter 0.6s ease;
}
/* Inside brand page, revealed when the cover opens. */
.hp-book__page {
	position: absolute;
	inset: 0;
	border-radius: 4px 10px 10px 4px;
	background:
		radial-gradient(120% 100% at 0% 50%, rgba(0, 0, 0, 0.28), transparent 38%),
		var(--color-secondary);
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: clamp(0.75rem, 2vw, 1.25rem);
	padding: clamp(1.25rem, 3vw, 2rem);
	box-shadow: inset 0 0 0 1px rgba(244, 241, 234, 0.08);
}
.hp-book__page-logo { width: clamp(80px, 9vw, 120px); height: auto; }
.hp-book__wordmark {
	font-family: var(--font-heading-family);
	font-weight: 600;
	font-size: clamp(1.25rem, 2.4vw, 1.75rem);
	line-height: 1.05;
	letter-spacing: -0.01em;
}
.hp-book__wordmark-dot { color: var(--color-accent); }
.hp-book__page-text {
	margin: 0;
	font-size: clamp(0.8125rem, 1.4vw, 0.95rem);
	line-height: 1.5;
	color: color-mix(in srgb, currentColor 75%, transparent);
	max-width: 24ch;
}
/* The swinging cover. */
.hp-book__cover {
	position: absolute;
	inset: 0;
	transform-origin: left center;
	transform-style: preserve-3d;
	transform: rotateY(0deg);
	transition: transform 1.1s cubic-bezier(0.6, 0.02, 0.1, 1);
	will-change: transform;
}
.hp-book:hover .hp-book__cover,
.hp-book:focus-visible .hp-book__cover,
.hp-book:focus-within .hp-book__cover,
.hp-book.is-open .hp-book__cover {
	/* .is-open is set by wireFeaturedBookOpen (main.js) when the book
	   scrolls into view, so the cover swings open on scroll as well as
	   on hover / focus / touch. */
	transform: rotateY(-158deg);
}
.hp-book__face {
	position: absolute;
	inset: 0;
	backface-visibility: hidden;
	-webkit-backface-visibility: hidden;
	border-radius: 4px 10px 10px 4px;
	overflow: hidden;
}
.hp-book__face--front { background: #000; }
.hp-book__cover-img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The back of the cover (seen once opened) reads as a blank paper leaf. */
.hp-book__face--back {
	transform: rotateY(180deg);
	background:
		linear-gradient(90deg, rgba(0, 0, 0, 0.18), transparent 22%),
		var(--color-paper);
}
/* Faux spine shading down the hinge edge of the cover. */
.hp-book__spine {
	position: absolute;
	top: 0;
	left: 0;
	width: 14%;
	height: 100%;
	background: linear-gradient(90deg, rgba(0, 0, 0, 0.35), transparent);
	pointer-events: none;
}
.hp-featured__caption {
	margin: 0;
	font-family: var(--font-heading-family);
	font-weight: 600;
	font-size: clamp(1rem, 2vw, 1.375rem);
	letter-spacing: -0.01em;
	max-width: 30ch;
}
.hp-featured__hint {
	margin-top: clamp(1.5rem, 3vw, 2.5rem);
	font-size: 0.75rem;
	text-transform: uppercase;
	letter-spacing: 0.18em;
	color: color-mix(in srgb, currentColor 45%, transparent);
}
/* Stack on narrow screens: copy on top, book below, both centred. */
@media (max-width: 820px) {
	.hp-featured__inner { grid-template-columns: 1fr; gap: clamp(2rem, 6vw, 3rem); }
	.hp-featured__copy { align-items: center; text-align: center; }
	.hp-featured__lead { max-width: 30ch; }
	.hp-featured__media { order: -1; } /* book first, then the headline below it */
}
@media (max-width: 0) /* prefers-reduced-motion neutralised — see main.js header */ {
	.hp-book__cover { transition: none; }
}

/* ---------- YouTube (latest uploads) ----------
   Auto-populated from the channel RSS feed. Cards are lazy thumbnail facades;
   the iframe is only injected on click (main.js). Grid collapses 3 → 2 → 1.
   ----------------------------------------------------- */
.hp-youtube {
	background: var(--color-primary);
	color: var(--color-paper);
}
/* Pin wrapper. Static by default (an accessible swipe row that works with no
   JS); main.js adds .hp-youtube--pinned to upgrade it to a sticky, scroll-
   driven horizontal carousel and sets the section height so the page resumes
   vertical scrolling once the last card arrives. */
.hp-youtube__pin {
	padding: clamp(4rem, 10vw, 8rem) 0 clamp(5rem, 12vw, 9rem);
	display: flex;
	flex-direction: column;
	gap: clamp(2rem, 4vw, 3rem);
}
.hp-youtube--pinned .hp-youtube__pin {
	position: sticky;
	top: 0;
	height: 100vh;
	padding-block: 0;
	justify-content: center;
	overflow: hidden;
}
.hp-youtube__header {
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
	gap: clamp(1rem, 3vw, 2rem);
	flex-wrap: wrap;
	width: 100%;
	max-width: 1400px;
	margin-inline: auto;
	padding-inline: clamp(1.5rem, 6vw, 5rem);
}
.hp-youtube__eyebrow {
	font-size: 0.8125rem;
	text-transform: uppercase;
	letter-spacing: 0.22em;
	color: color-mix(in srgb, currentColor 55%, transparent);
	margin: 0 0 0.5rem;
	font-weight: 600;
}
.hp-youtube__heading {
	font-family: var(--font-heading-family);
	font-weight: 800;
	font-size: clamp(1.875rem, 4.5vw, 3.25rem);
	letter-spacing: -0.02em;
	line-height: 1.02;
	margin: 0;
}
/* Horizontal carousel track. A single row of fixed-width cards that the user
   drives sideways. Baseline: native horizontal scroll (works without JS).
   Pinned mode: JS translates the whole row with --yt-x as the page scrolls. */
.hp-youtube__track {
	list-style: none;
	margin: 0;
	display: flex;
	gap: clamp(0.75rem, 1.5vw, 1.5rem);
	padding: 0.5rem clamp(1.5rem, 6vw, 5rem);
	/* Mirror padding-left so scrollLeft=0 is a valid mandatory-snap position.
	   Without it the track auto-snaps ~1.5rem on initial layout, and a scroll
	   before first paint permanently kills LCP measurement for the whole page
	   (see .hp-reviews__track for the full write-up). */
	scroll-padding-left: clamp(1.5rem, 6vw, 5rem);
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: none;
}
.hp-youtube__track::-webkit-scrollbar { display: none; }
.hp-youtube--pinned .hp-youtube__track {
	overflow: visible;
	scroll-snap-type: none;
	transform: translate3d(calc(var(--yt-x, 0) * -1px), 0, 0);
	will-change: transform;
}
.hp-youtube__item {
	flex: 0 0 auto;
	min-width: 0;
	width: clamp(360px, 46vw, 680px);
	scroll-snap-align: start;
}
.hp-youtube__card {
	position: relative;
	display: block;
	height: auto;
	aspect-ratio: 16 / 9;
	border-radius: clamp(14px, 1.5vw, 22px);
	overflow: hidden;
	background: #000;
	color: inherit;
	text-decoration: none;
	box-shadow: 0 16px 40px rgba(0, 0, 0, 0.35);
}
.hp-youtube__thumb {
	position: absolute;
	inset: 0;
	display: block;
	overflow: hidden;
}
.hp-youtube__thumb img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 0.6s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.hp-youtube__card:hover .hp-youtube__thumb img,
.hp-youtube__card:focus-visible .hp-youtube__thumb img {
	transform: scale(1.06);
}
/* Play button overlay, centred. */
.hp-youtube__play {
	position: absolute;
	inset: 0;
	z-index: 2;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: transform 0.25s ease;
}
.hp-youtube__play svg { filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.5)); width: clamp(48px, 5vw, 68px); height: auto; }
.hp-youtube__play-bg { fill: #212121; opacity: 0.85; transition: fill 0.25s ease, opacity 0.25s ease; }
.hp-youtube__card:hover .hp-youtube__play-bg,
.hp-youtube__card:focus-visible .hp-youtube__play-bg {
	fill: var(--color-accent);
	opacity: 1;
}
.hp-youtube__card:hover .hp-youtube__play,
.hp-youtube__card:focus-visible .hp-youtube__play {
	transform: scale(1.08);
}
/* Title overlaid at the bottom over a gradient scrim. */
.hp-youtube__title {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 2;
	padding: clamp(1.5rem, 3vw, 2.25rem) clamp(1rem, 2vw, 1.5rem) clamp(0.9rem, 1.6vw, 1.25rem);
	background: linear-gradient(to top, rgba(0, 0, 16, 0.88) 0%, rgba(0, 0, 16, 0.5) 45%, transparent 100%);
	color: var(--color-white);
	font-family: var(--font-heading-family);
	font-weight: 600;
	font-size: clamp(0.875rem, 1.2vw, 1.0625rem);
	line-height: 1.3;
	letter-spacing: -0.01em;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
/* Once clicked, the facade is replaced by the player iframe. */
.hp-youtube__thumb iframe {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	border: 0;
}
/* Prev/next arrows — desktop uses the pinned scroll, so they stay hidden here
   and only appear in the mobile swipe-row block below. */
.hp-youtube__nav {
	display: none;
}
.hp-youtube__arrow {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 3rem;
	height: 3rem;
	border-radius: 50%;
	border: 1px solid rgba(244, 241, 234, 0.3);
	background: rgba(244, 241, 234, 0.06);
	color: var(--color-paper);
	cursor: pointer;
	transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease, opacity 0.2s ease;
	-webkit-tap-highlight-color: transparent;
}
.hp-youtube__arrow:hover,
.hp-youtube__arrow:focus-visible {
	background: var(--color-accent);
	border-color: var(--color-accent);
	color: var(--color-white);
}
.hp-youtube__arrow[disabled] {
	opacity: 0.3;
	pointer-events: none;
}
@media (max-width: 900px) {
	/* Show the arrows and right-align them under the swipe row. */
	.hp-youtube__nav {
		display: flex;
		gap: 0.75rem;
		justify-content: flex-end;
		padding: 1.25rem clamp(1.5rem, 6vw, 5rem) 0;
	}
}
@media (max-width: 900px) {
	/* Touch: never pin. Fall back to a native horizontal swipe row even if JS
	   tried to enable the carousel, so the page scrolls naturally on mobile. */
	.hp-youtube--pinned .hp-youtube__pin {
		position: static;
		height: auto;
		padding-block: clamp(4rem, 12vw, 6rem);
		overflow: visible;
	}
	.hp-youtube--pinned .hp-youtube__track {
		overflow-x: auto;
		scroll-snap-type: x mandatory;
		transform: none;
	}
	.hp-youtube__item { width: clamp(260px, 78vw, 360px); }
}

/* ---------- Curve ----------
   Big rounded bridge between dark and light grounds. The container sits flush
   with the next section, the SVG arc carves the upper edge so the transition
   reads as a single rounded scoop, geek-style.
   ----------------------------------------------------- */

.hp-curve {
	position: relative;
	margin-top: -1px;
	background: var(--color-paper);
	line-height: 0;
}
.hp-curve__svg {
	display: block;
	width: 100%;
	height: clamp(80px, 14vw, 200px);
}
.hp-curve--invert { background: var(--color-primary); }

/* ---------- Red brand ribbon ----------
   Sits between the Why / Our Process beat and the In The Press beat. Both
   surrounding sections are navy, so the ribbon container shares that ground
   and only the SVG stroke shows. Path enters left, steps down twice (5% / 80%
   / 5%) and exits right; geometry lives in the template (section-ribbon.php).

   Draw-on: path has pathLength="1", so stroke-dasharray:1 + stroke-dashoffset
   form a 0–1 range. JS (wireScrollRibbon) sets --p (0 → 1) on .hp-ribbon
   as it scrolls through the viewport; CSS pipes that into dashoffset so the
   stroke draws on in path order. Reduced motion: --p is left at 1 (fully drawn).
   ----------------------------------------------------- */
.hp-ribbon {
	position: relative;
	background: var(--color-primary);
	line-height: 0;
	/* Top padding 0 — the path's y=9 entry + half-stroke extending up sits
	   the visible stroke top on the SVG top edge, so the gap above the
	   ribbon is just the .hp-why padding-bottom (~20px). Bottom padding
	   keeps the device clear of the In The Press section below. */
	padding-block: 0 clamp(3rem, 7vw, 6rem);
}
.hp-ribbon__svg {
	display: block;
	width: 100%;
	/* 35vh houses the path's 10vh + 20vh drops plus a little headroom
	   above and below. Floor / ceiling keep the device sensible on very
	   small viewports and very tall monitors. */
	height: clamp(280px, 35vh, 500px);
	overflow: visible;
}
/* JS (wireScrollRibbon) sets stroke-dasharray + stroke-dashoffset inline on
   the path, so the draw-on works without any CSS variable plumbing. */

/* ---------- Services ----------
   Oversized service rows on the paper ground. Each row is a wide grid: a
   small pink dot, the service title at display scale, and a one-line blurb
   right-aligned. Hover nudges the row inwards, swells the dot to pink, and
   draws a faint accent underline beneath the title.
   ----------------------------------------------------- */

/* Services + Work share a scroll-driven colour shift. --hp-work-shift runs
   0 (navy ground, light text) → 1 (paper ground, navy text); JS sets it on
   both sections from the work section's scroll position so the seam between
   them fades as one. Services rests at 0, Work rests at 1 when JS is absent
   (no-JS / reduced motion). Children use currentColor so they follow along. */
.hp-services {
	--hp-work-shift: 0;
	background: color-mix(in oklab, var(--color-paper) calc(var(--hp-work-shift) * 100%), var(--color-primary));
	color: color-mix(in oklab, var(--color-primary) calc(var(--hp-work-shift) * 100%), var(--color-paper));
	padding: clamp(5rem, 11vw, 9rem) 1.5rem clamp(6rem, 13vw, 11rem);
}
.hp-services__inner {
	width: 100%;
	margin-inline: auto;
}
.hp-services__eyebrow {
	font-size: 0.8125rem;
	text-transform: uppercase;
	letter-spacing: 0.22em;
	color: color-mix(in srgb, currentColor 55%, transparent);
	margin-bottom: clamp(1rem, 2vw, 1.5rem);
	font-weight: 600;
}
.hp-services__intro {
	font-family: var(--font-heading-family);
	font-weight: 700;
	font-size: clamp(1.25rem, 2.6vw, 2rem);
	line-height: 1.3;
	letter-spacing: -0.01em;
	max-width: 30ch;
	margin: 0 0 clamp(2.5rem, 5vw, 4rem);
	color: inherit;
}
.hp-services__list {
	list-style: none;
	margin: 0;
	padding: 0;
	border-top: 1px solid color-mix(in srgb, currentColor 18%, transparent);
}
.hp-services__item {
	display: grid;
	grid-template-columns: auto 1fr auto;
	align-items: center;
	gap: clamp(1rem, 2vw, 2rem);
	padding: clamp(1.5rem, 3vw, 2.5rem) 0;
	border-bottom: 1px solid color-mix(in srgb, currentColor 18%, transparent);
	cursor: default;
	transition: padding 0.35s ease, color 0.35s ease;
	position: relative;
}
.hp-services__item:hover {
	padding-left: clamp(0.75rem, 2vw, 1.5rem);
}
/* Service rows link to their service page. The <a> is display:contents so the
   title + blurb keep their original grid columns; only cursor + focus styling
   change. */
.hp-services__link {
	display: contents;
	color: inherit;
	text-decoration: none;
}
.hp-services__item:has(.hp-services__link) {
	cursor: pointer;
}
.hp-services__link:focus-visible .hp-services__title {
	color: var(--color-accent);
	text-decoration: underline;
	text-underline-offset: 0.12em;
}
/* Bullet dot is hidden by default and slides in from the left as the user
   hovers (or keyboard-focuses) the service row. The grid reserves its column
   so the title never shifts; only the dot's opacity + position animate. */
.hp-services__dot {
	width: clamp(36px, 3.6vw, 50px);
	height: clamp(36px, 3.6vw, 50px);
	border-radius: 50%;
	background: var(--color-accent);
	opacity: 0;
	/* Grid parent .hp-services__item uses align-items:center to centre the dot
	   on the row's geometric midline; we then lift it by 0.15em to match the
	   site-wide bullet convention (dyslexia-readable text reads better with
	   the dot near cap-height middle). See HP feedback memory. */
	/* Start tiny so it pops up to size on hover. */
	transform: translateY(-0.15em) scale(0);
	transition: opacity 0.18s ease, transform 0.18s ease;
}
.hp-services__item:hover .hp-services__dot,
.hp-services__item:focus-within .hp-services__dot {
	opacity: 1;
	/* Overshoot-then-settle "bubble pop": the back-ease curve pushes past
	   scale(1) and springs back, like a bubble popping into place. */
	transform: translateY(-0.15em) scale(1);
	transition: opacity 0.18s ease,
		transform 0.42s cubic-bezier(0.34, 1.8, 0.64, 1);
}
.hp-services__title {
	font-family: var(--font-heading-family);
	font-size: clamp(2.25rem, 7vw, 6rem);
	font-weight: 800;
	letter-spacing: -0.035em;
	line-height: 0.98;
	transition: color 0.25s ease;
}
.hp-services__item:hover .hp-services__title { color: var(--color-accent); }
.hp-services__blurb {
	color: color-mix(in srgb, currentColor 62%, transparent);
	font-size: 1rem;
	text-align: right;
	max-width: 30ch;
	line-height: 1.45;
}

/* ---------- Work ----------
   Edge-to-edge oversized "Our Work" with the inset image clipped to the
   letterforms via background-clip: text. The heading is set to crop at the
   viewport boundaries, geek-style.
   ----------------------------------------------------- */

.hp-work {
	--hp-work-shift: 1;
	background: color-mix(in oklab, var(--color-paper) calc(var(--hp-work-shift) * 100%), var(--color-primary));
	color: color-mix(in oklab, var(--color-primary) calc(var(--hp-work-shift) * 100%), var(--color-paper));
	padding: clamp(4rem, 10vw, 8rem) 0 0;
	text-align: center;
	/* NOTE: no overflow:hidden here — it would clip and break the
	   sticky stacking of the nested portfolio cards. */
}
.hp-work__inner {
	padding-inline: 1.5rem;
}
.hp-work__inner {
	width: 100%;
	margin-inline: auto;
}
.hp-work__eyebrow {
	font-size: 0.8125rem;
	text-transform: uppercase;
	letter-spacing: 0.22em;
	color: color-mix(in srgb, currentColor 55%, transparent);
	margin-bottom: clamp(1.5rem, 3vw, 2.5rem);
	font-weight: 600;
}
.hp-work__heading {
	font-size: clamp(5rem, 22vw, 20rem);
	line-height: 0.86;
	/* 600 is Oceanwide's only licensed face. Asking for 900 made iOS
	   synthesize a fake bold, which overlaps the tightly-tracked glyphs and
	   (with background-clip: text) knocks holes out of the overlap — the
	   "o" in Work rendered sliced on iPhones. Desktop engines already
	   served the 600 face for the 900 request, so this changes nothing there. */
	font-weight: 600;
	letter-spacing: -0.05em;
	color: currentColor;
	/* Vertical ink overhang: line-height < 1 pushes cap/ascender ink past the
	   box edge, where background-clip: text has nothing to paint (Safari clips
	   there). The padding extends the paint box; the margins hand it back. */
	padding: 0.08em 0;
	margin: -0.08em 0 calc(clamp(2rem, 4vw, 3rem) - 0.08em);
	background-position: center;
	background-size: cover;
	background-clip: text;
	-webkit-background-clip: text;
	text-transform: none;
}
.hp-work__heading[style*="background-image"] {
	color: transparent;
	-webkit-text-fill-color: transparent;
}
/* Brand pink dot after the heading. Forced solid even when the heading text is
   image-filled via background-clip, so the dot never goes transparent. */
.hp-work__dot {
	color: var(--color-accent);
	-webkit-text-fill-color: var(--color-accent);
}

/* ---------- Opinions ----------
   Navy ground card grid. Header is a two-column band: eyebrow + heading on
   the left, category rail (pill chips) on the right. Cards are softly
   rounded with the media bleeding to the top edges, geek-style.
   ----------------------------------------------------- */

.hp-opinions {
	background: var(--color-primary);
	padding: clamp(5rem, 12vw, 10rem) 1.5rem clamp(5rem, 12vw, 10rem);
}
.hp-opinions__inner {
	width: 100%;
	margin-inline: auto;
}
.hp-opinions__header {
	display: grid;
	grid-template-columns: 1fr auto;
	align-items: end;
	margin-bottom: clamp(2.5rem, 5vw, 4rem);
	gap: var(--space-4);
}
.hp-opinions__eyebrow {
	font-size: 0.8125rem;
	text-transform: uppercase;
	letter-spacing: 0.22em;
	color: rgba(244, 241, 234, 0.55);
	margin: 0 0 var(--space-2);
	font-weight: 600;
}
.hp-opinions__heading {
	font-size: clamp(2.25rem, 5vw, 4.5rem);
	font-weight: 800;
	letter-spacing: -0.035em;
	line-height: 1;
	margin: 0;
}

.hp-opinions__rail {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	list-style: none;
	margin: 0;
	padding: 0;
}
.hp-opinions__rail li a,
.hp-opinions__rail li span {
	display: inline-flex;
	align-items: center;
	padding: 0.5rem 0.9375rem;
	border-radius: var(--radius-pill);
	background: rgba(244, 241, 234, 0.06);
	border: 1px solid rgba(244, 241, 234, 0.15);
	color: rgba(244, 241, 234, 0.78);
	font-size: 0.8125rem;
	font-weight: 600;
	letter-spacing: -0.005em;
	transition: background 0.25s ease, color 0.25s ease, border-color 0.25s ease;
}
.hp-opinions__rail li a:hover,
.hp-opinions__rail li.is-active a,
.hp-opinions__rail li.is-active span {
	background: var(--color-accent);
	color: var(--color-white);
	border-color: var(--color-accent);
}

.hp-opinions__grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: clamp(1.25rem, 2vw, 2rem);
	margin-bottom: clamp(2.5rem, 5vw, 4rem);
}
.hp-opinions__card {
	background: var(--color-secondary);
	border-radius: 28px;
	padding: clamp(1.25rem, 2vw, 1.75rem);
	display: flex;
	flex-direction: column;
	transition: transform 0.35s ease, box-shadow 0.35s ease;
}
.hp-opinions__card:hover {
	transform: translateY(-4px);
	box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
}
.hp-opinions__media {
	display: block;
	margin: clamp(-1.75rem, -2vw, -1.25rem) clamp(-1.75rem, -2vw, -1.25rem) var(--space-3);
	border-radius: 28px 28px 0 0;
	overflow: hidden;
	aspect-ratio: 4 / 3;
}
.hp-opinions__media img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.6s ease; }
.hp-opinions__card:hover .hp-opinions__media img { transform: scale(1.04); }
.hp-opinions__media--placeholder {
	aspect-ratio: 4 / 3;
	background: linear-gradient(135deg, var(--color-radial), var(--color-accent));
}
.hp-opinions__meta {
	font-size: 0.75rem;
	text-transform: uppercase;
	letter-spacing: 0.18em;
	color: rgba(244, 241, 234, 0.5);
	margin: 0 0 var(--space-2);
	font-weight: 600;
}
.hp-opinions__title {
	font-size: clamp(1.375rem, 1.6vw, 1.625rem);
	font-weight: 700;
	line-height: 1.15;
	letter-spacing: -0.02em;
	margin: 0 0 var(--space-2);
}
.hp-opinions__title a { color: var(--color-paper); }
.hp-opinions__title a:hover { color: var(--color-accent); }
.hp-opinions__excerpt {
	color: rgba(244, 241, 234, 0.7);
	font-size: 0.9375rem;
	line-height: 1.55;
	margin: 0;
}

/* ---------- Outro ----------
   Closing statement at display scale, geek-style. Tight tracking, generous
   vertical space above and below, single CTA below.
   ----------------------------------------------------- */

.hp-outro {
	position: relative;
	background: var(--color-primary);
	padding: clamp(6rem, 14vw, 12rem) 1.5rem clamp(5rem, 10vw, 8rem);
	text-align: left;
	overflow: hidden;
}
.hp-outro__inner {
	width: 100%;
	margin-inline: auto;
	display: flex;
	justify-content: space-between;
	align-items: flex-end;
	gap: clamp(2rem, 5vw, 4rem);
	flex-wrap: wrap;
}
.hp-outro__statement {
	flex: 1 1 60%;
	min-width: 0;
	font-family: var(--font-heading-family);
	font-size: clamp(2.75rem, 9vw, 8rem);
	font-weight: 900;
	line-height: 0.96;
	letter-spacing: -0.045em;
	margin-bottom: 0;
}
.hp-outro .hp-btn { flex: 0 0 auto; }
.hp-outro__statement p { margin: 0; }
.hp-outro__statement em,
.hp-outro__statement strong .hp-accent { color: var(--color-accent); font-style: normal; }

/* Scroll-driven moving highlight. Each line sits white; JS sets --active
   (a float that climbs from -1 up to n-1 as the section scrolls in) on the
   container. A line is pink only when --active is near its --i, so a single
   pink highlight travels down the lines and rests on the last one. */
.hp-outro__sequence { display: flex; flex-direction: column; }
.hp-outro__line {
	display: block;
	--dist: max(calc(var(--i) - var(--active, 999)), calc(var(--active, 999) - var(--i)));
	--lit: clamp(0, calc(1 - var(--dist)), 1);
	color: color-mix(in srgb, var(--color-accent) calc(var(--lit) * 100%), var(--color-paper));
	transition: color 0.12s linear;
}
/* The <em> on "No filters." must follow the line's scroll colour rather than
   the always-pink rule above. */
.hp-outro__line em { color: inherit; font-style: normal; }
/* No color-mix / reduced motion / no JS: lines rest readable white. */
@supports not (color: color-mix(in srgb, red, blue)) {
	.hp-outro__line { color: var(--color-paper); }
}
@media (max-width: 0) /* prefers-reduced-motion neutralised — see main.js header */ {
	.hp-outro__line { color: var(--color-paper); transition: none; }
}

/* ---------- Footer ----------
   Brand-forward footer: oversized wordmark + studio details, a "Speak with
   us" CTA to the Contact page, the menu, and a base bar with social icons
   that bubble-wiggle on hover. No contact email is printed (scraper defence).
   ----------------------------------------------------- */

.site-footer {
	background: var(--color-primary);
	color: var(--color-paper);
	border-top: 1px solid rgba(244, 241, 234, 0.12);
	padding: clamp(4rem, 9vw, 7rem) 1.5rem var(--space-5);
}
.site-footer__inner {
	width: 100%;
	margin-inline: auto;
}
.site-footer__top {
	display: grid;
	grid-template-columns: 1.5fr 1fr 1fr 1fr;
	gap: var(--space-5);
	align-items: start;
}
/* Tablet: 4-up gets tight (brand + CTA + Menu + Services). Drop to a
   2-col layout where brand + CTA sit on row 1 and the two menu columns
   sit on row 2. */
@media (min-width: 900px) and (max-width: 1199px) {
	.site-footer__top {
		grid-template-columns: 1.5fr 1fr;
	}
}
/* Brand block: oversized logo + studio details. */
.site-footer__logo { display: inline-block; color: var(--color-paper); }
.site-footer__logo:hover { color: var(--color-paper); }
.site-footer__logo-img { max-width: 420px; height: auto; }
.site-footer__logo-svg { display: block; width: clamp(260px, 32vw, 440px); height: auto; }
.site-footer__wordmark {
	display: block;
	font-family: var(--font-heading-family);
	font-weight: 600;
	font-size: clamp(3.25rem, 7vw, 5.5rem);
	line-height: 0.9;
	letter-spacing: -0.02em;
	text-transform: lowercase;
}
.site-footer__wordmark-dot { color: var(--color-accent); }
.site-footer__address {
	font-style: normal;
	color: rgba(244, 241, 234, 0.7);
	font-size: 0.9375rem;
	line-height: 1.6;
	margin: var(--space-4) 0 var(--space-3);
}
.site-footer__phone a { color: var(--color-paper); font-size: 1.125rem; font-weight: 600; }
.site-footer__phone a:hover { color: var(--color-accent); }

/* "Speak with us" CTA. */
.site-footer__cta-text {
	font-family: var(--font-heading-family);
	font-size: clamp(1.5rem, 2.6vw, 2rem);
	font-weight: 700;
	letter-spacing: -0.02em;
	margin: 0 0 var(--space-3);
}

/* Menu column. */
.site-footer__col-title {
	font-size: 0.8125rem;
	text-transform: uppercase;
	letter-spacing: 0.18em;
	color: rgba(244, 241, 234, 0.5);
	margin: 0 0 var(--space-3);
	font-weight: 600;
}
.site-footer__list { list-style: none; margin: 0; padding: 0; }
.site-footer__list li { margin-bottom: var(--space-2); }
.site-footer__list a { color: var(--color-paper); }
.site-footer__list a:hover { color: var(--color-accent); }

/* Base bar. */
.site-footer__base {
	display: flex;
	justify-content: space-between;
	align-items: center;
	margin-top: var(--space-6);
	padding-top: var(--space-4);
	border-top: 1px solid rgba(244, 241, 234, 0.12);
	font-size: 0.8125rem;
	color: rgba(244, 241, 234, 0.5);
	flex-wrap: wrap;
	gap: var(--space-3);
}
.site-footer__legal { margin: 0; }
.site-footer__legal-menu { list-style: none; margin: 0; padding: 0; display: flex; gap: var(--space-3); }
.site-footer__legal-menu a { color: rgba(244, 241, 234, 0.5); }
.site-footer__legal-menu a:hover { color: var(--color-accent); }

/* Social icons with hover bubble-wiggle. */
.site-footer__social {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	gap: var(--space-2);
}
.site-footer__social-link {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	border-radius: 50%;
	border: 1px solid rgba(244, 241, 234, 0.25);
	color: var(--color-paper);
	transition: color 0.25s ease, border-color 0.25s ease;
}
.site-footer__social-icon {
	position: relative;
	z-index: 1;
	width: 20px;
	height: 20px;
}
.site-footer__social-bubble {
	position: absolute;
	inset: 0;
	border-radius: 50%;
	background: var(--color-accent);
	transform: scale(0);
	opacity: 0;
	transition: transform 0.3s ease, opacity 0.3s ease;
	z-index: 0;
}
.site-footer__social-link:hover,
.site-footer__social-link:focus-visible {
	color: var(--color-white);
	border-color: var(--color-accent);
	animation: hp-social-wiggle 0.6s ease-in-out infinite;
}
.site-footer__social-link:hover .site-footer__social-bubble,
.site-footer__social-link:focus-visible .site-footer__social-bubble {
	transform: scale(1);
	opacity: 1;
}
@keyframes hp-social-wiggle {
	0%, 100% { transform: translateY(0) rotate(0); }
	25%      { transform: translateY(-3px) rotate(-7deg); }
	50%      { transform: translateY(0) rotate(7deg); }
	75%      { transform: translateY(-2px) rotate(-4deg); }
}

/* ---------- Shared social icon row (footer + popup menu) ----------
   Circular icon buttons with the same bubble-wiggle hover as the footer.
   Reuses the @keyframes hp-social-wiggle defined above. */
.hp-social {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	gap: var(--space-2);
}
.hp-social__link {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	border-radius: 50%;
	border: 1px solid rgba(244, 241, 234, 0.25);
	color: var(--color-paper);
	transition: color 0.25s ease, border-color 0.25s ease;
}
.hp-social__icon {
	position: relative;
	z-index: 1;
	width: 20px;
	height: 20px;
}
.hp-social__bubble {
	position: absolute;
	inset: 0;
	border-radius: 50%;
	background: var(--color-accent);
	transform: scale(0);
	opacity: 0;
	transition: transform 0.3s ease, opacity 0.3s ease;
	z-index: 0;
}
.hp-social__link:hover,
.hp-social__link:focus-visible {
	color: var(--color-white);
	border-color: var(--color-accent);
	animation: hp-social-wiggle 0.6s ease-in-out infinite;
}
.hp-social__link:hover .hp-social__bubble,
.hp-social__link:focus-visible .hp-social__bubble {
	transform: scale(1);
	opacity: 1;
}

/* Popup menu placement: sit the icons below the nav links, left-aligned to
   match the menu items. */
.site-nav__social {
	justify-content: flex-start;
	margin-top: clamp(2rem, 5vh, 3.5rem);
}

/* ---------- Newsletter form ---------- */

.hp-newsletter {
	display: grid;
	gap: var(--space-3);
	background: var(--color-secondary);
	padding: var(--space-5);
	border-radius: var(--radius-lg);
}
.hp-newsletter__heading { font-size: 1.5rem; margin: 0 0 var(--space-1); }
.hp-newsletter__sub { color: rgba(244, 241, 234, 0.65); margin: 0; }
.hp-newsletter__fields { display: grid; grid-template-columns: 1fr auto; gap: var(--space-2); }
.hp-newsletter__fields input {
	padding: 0.875rem 1rem;
	background: rgba(244, 241, 234, 0.08);
	border: 1px solid rgba(244, 241, 234, 0.18);
	color: var(--color-paper);
	border-radius: var(--radius-pill);
	font: inherit;
}
.hp-newsletter__feedback { margin: 0; font-size: 0.875rem; min-height: 1.25em; }

/* ---------- Page / archive / post ---------- */

/* Scope to the MAIN content element, NOT the body. WordPress also puts a
   `page` class on <body> (incl. the static front page), so a bare `.page`
   selector padded the homepage body and shoved the hero down. Targeting the
   `.site-main` keeps the front page (main.hp-home) and single posts
   (main.hp-single, which has its own padding) untouched. */
.site-main.page, .site-main.archive, .site-main.error-404 {
	/* Top padding only needs to clear the fixed header pills (~70px), not the
	   old 10rem. */
	padding: clamp(5rem, 9vw, 7rem) 0 var(--space-7);
	background: var(--color-primary);
	min-height: 80vh;
	position: relative;
	isolation: isolate;
}
/* Brand glow (logo brand image, 2026-07-29) on archive + 404 page tops.
   The photo-background 404 variant is excluded: its navy overlay + photo
   own that ground, and a glow under the image would be invisible. */
.site-main.archive::before,
.site-main.error-404:not(.error-404--bg)::before {
	content: "";
	position: absolute;
	inset: 0 0 auto 0;
	height: 100svh;
	z-index: -1;
	pointer-events: none;
	background: radial-gradient(45% 75% at 0% 42%,
		color-mix(in oklab, var(--color-radial) 34%, transparent) 0%,
		color-mix(in oklab, var(--color-radial) 13%, transparent) 45%,
		transparent 75%);
}
.archive-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--space-4);
}
.archive-card { background: var(--color-secondary); border-radius: var(--radius-lg); padding: var(--space-4); }
.archive-card__media { display: block; aspect-ratio: 4/3; overflow: hidden; border-radius: var(--radius-lg); margin-bottom: var(--space-3); }
.archive-card__media img { width: 100%; height: 100%; object-fit: cover; }
.archive-card__title { font-size: 1.5rem; margin: 0 0 var(--space-2); }
.archive-card__title a { color: var(--color-paper); }

.error-404__title { font-size: clamp(5rem, 18vw, 14rem); margin: 0; }
.error-404__copy { color: rgba(244, 241, 234, 0.7); margin: var(--space-3) 0 var(--space-4); }

/* Optional 404 background photo (feature key `404-bg`). A navy overlay sits over
   the image so the white title, copy and pink button stay legible on any photo.
   Placed after the base .error-404 rule so it wins the background declaration. */
.site-main.error-404--bg {
	background-image:
		linear-gradient(rgba(8, 20, 39, 0.74), rgba(8, 20, 39, 0.84)),
		var(--hp-404-bg);
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
	/* Push the 404 heading, copy and button to the bottom so the "Nothing to
	   see here" text baked into the photo stays visible above them. */
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	min-height: 90vh;
	padding-bottom: clamp(3rem, 8vh, 6rem);
}

/* ---------- Responsive ---------- */

@media (max-width: 1024px) {
	/* Shrink bare editor headings + body across the 900-1024 tablet band too,
	   not only <900. A 6.5rem H1 flush to a tablet edge was the old gap. */
	h1 { font-size: var(--font-h1-size-mobile); }
	h2 { font-size: var(--font-h2-size-mobile); }
	h3 { font-size: var(--font-h3-size-mobile); }
	body.hp-body { font-size: var(--font-body-size-mobile); }
	.hp-positioning__inner { grid-template-columns: 1fr; }
	.hp-positioning__collage { height: 420px; max-width: 560px; margin-inline: auto; }
	.hp-opinions__header { grid-template-columns: 1fr; align-items: start; }
	.hp-opinions__grid { grid-template-columns: repeat(2, 1fr); }
	.archive-grid { grid-template-columns: repeat(2, 1fr); }
	.hp-services__blurb { text-align: left; max-width: none; }
	.hp-services__item { grid-template-columns: auto 1fr; }
	.hp-services__blurb { grid-column: 2; }
}

@media (max-width: 899px) {
	h1 { font-size: var(--font-h1-size-mobile); }
	h2 { font-size: var(--font-h2-size-mobile); }
	h3 { font-size: var(--font-h3-size-mobile); }
	body.hp-body { font-size: var(--font-body-size-mobile); }
	.site-header { left: var(--space-3); right: var(--space-3); top: var(--space-2); }
	/* Larger tap target on mobile (comfortably past the 44px minimum). */
	.site-header__pill { padding: 0.8rem 2rem; font-size: 0.9375rem; }
	.hp-opinions__grid { grid-template-columns: 1fr; }
	.archive-grid { grid-template-columns: 1fr; }
	.site-footer__top { grid-template-columns: 1fr; gap: var(--space-4); }
	.hp-services__title { font-size: clamp(1.875rem, 9.5vw, 3.75rem); }
	.hp-services__item { padding: 1.5rem 0; }
	.hp-services__blurb { font-size: 0.9375rem; }
	/* Slightly eased tracking on phones: at -0.05em the W and o in "Work"
	   touch, which reads as the o being cut off at small sizes. */
	.hp-work__heading { font-size: clamp(2.5rem, 16vw, 9rem); letter-spacing: -0.035em; }
	/* Each stacking card lives in a 100vh flex box that centres a 78vh card,
	   leaving ~11vh of empty ground between the heading and the first card
	   on phones. Pull the stack up by exactly that centring gap; the sticky
	   pin + cover math is all relative, so the stacking effect is unchanged. */
	.hp-portfolio__stack { margin-top: calc((min(78vh, 720px) - 100vh) / 2); }
}

@media (max-width: 480px) {
	.hp-positioning, .hp-services, .hp-work, .hp-opinions, .hp-outro, .site-footer { padding-left: 1rem; padding-right: 1rem; }
	.hp-newsletter__fields { grid-template-columns: 1fr; }
	.site-footer__base { flex-direction: column; align-items: flex-start; }
	/* Button sizing for phones lives with the button styles, in the
	   max-width: 560px block up beside .hp-btn--large. */
}


/* =====================================================================
   EDITORIAL BRUTALISM — internal-page layout system
   ---------------------------------------------------------------------
   Portable, project-agnostic body-layout system. Each section opts in
   via [data-eb-section] and declares its ground via data-eb-ground.
   Adjacent sections with different grounds cross-fade via a shared
   --eb-shift driven by wireEbColorShift in main.js (R-08).
   Plan: C:\Users\User\.claude\plans\lets-plan-out-how-effervescent-gosling.md
   ===================================================================== */

:root {
	--eb-ground-saturated: var(--color-primary);  /* navy on HP */
	--eb-ground-light:     var(--color-paper);    /* cream on HP */
	--eb-ink-on-saturated: var(--color-paper);
	--eb-ink-on-light:     var(--color-primary);
	--eb-thread:           var(--color-accent);   /* pink on HP */
	/* Thread channel — reserved gutter where the brand thread runs
	   without overlapping content (R-10). */
	--eb-channel-w: clamp(16px, 3vw, 48px);
}

/* Cross-fade chassis. Each section's rest background comes from its own
   data-eb-ground attribute (works without JS). When wireEbColorShift
   detects an active pair, it adds .hp-eb-fading on both sections and
   sets --eb-from-bg / --eb-to-bg / --eb-from-fg / --eb-to-fg / --eb-shift
   inline; the .hp-eb-fading recipe then mixes the two grounds so the
   seam reads as one continuously morphing surface. */
[data-eb-section] {
	--eb-shift: 0;
	position: relative;
	overflow: hidden;
	background: var(--color-primary);
	color: var(--color-paper);
}
[data-eb-section][data-eb-ground="saturated"] {
	background: var(--eb-ground-saturated);
	color: var(--eb-ink-on-saturated);
}
[data-eb-section][data-eb-ground="light"] {
	background: var(--eb-ground-light);
	color: var(--eb-ink-on-light);
}
[data-eb-section].hp-eb-fading {
	/* `in srgb` instead of `in oklab` for wider browser support (oklab
	   is only safe from Chrome 111+, Safari 16.4+, Firefox 113+). srgb
	   support is universal. */
	background: color-mix(in srgb,
		var(--eb-to-bg, var(--eb-ground-saturated)) calc(var(--eb-shift) * 100%),
		var(--eb-from-bg, var(--eb-ground-saturated)));
	color: color-mix(in srgb,
		var(--eb-to-fg, var(--eb-ink-on-saturated)) calc(var(--eb-shift) * 100%),
		var(--eb-from-fg, var(--eb-ink-on-saturated)));
}
/* Force descendants to inherit the section's morphing color so the
   H2 and body text track the bg shift. Guards against any inherited
   theme rule that might pin the text colour. */
[data-eb-section].hp-eb-fading h2,
[data-eb-section].hp-eb-fading h3,
[data-eb-section].hp-eb-fading p {
	color: inherit;
}

/* Brand-mark pink period — the trailing "." on every EB section H1 /
   H2 stays as the font's actual period glyph; only its colour changes
   to the thread accent. Output by hp_eb_title() as a real "." inside
   a span so screen readers / copy-paste get a natural sentence and
   the typography scales with the heading's font-size automatically.
   Overrides the gradient-text transparency so the dot stays solid
   even inside .hp-gradient-text__inner. */
.hp-eb-dot {
	color: var(--eb-thread);
	-webkit-text-fill-color: var(--eb-thread);
}

/* ============== Reviews slider (hp_review CPT) ==============
   Horizontal scroll-snap row modelled on the homepage "Watch our
   latest videos" YouTube section. 3 cards visible at a time on
   desktop, 4th card peeks past the right edge (negative end-
   margin), prev/next arrows scroll the track in increments of one
   card. Editorial Brutalism token-aligned: brutalist H2, hairline
   rules, pink accents, paper cards on cream / navy grounds. */

.hp-reviews {
	padding:
		clamp(5rem, 10vh, 9rem) 0;
	overflow: hidden; /* tame the negative end-margin so it doesn't
	                     trigger a horizontal page scrollbar */
}
.hp-reviews[data-eb-ground="light"] {
	background: var(--color-cream, #f5efe6);
	color: var(--color-primary, #081427);
}
.hp-reviews[data-eb-ground="saturated"] {
	background: var(--color-primary, #081427);
	color: var(--color-paper, #f5efe6);
}

.hp-reviews__head {
	max-width: 1480px;
	margin: 0 auto clamp(2.5rem, 5vw, 4rem);
	padding-inline: clamp(1.5rem, 6vw, 5rem);
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
	gap: clamp(1rem, 3vw, 2.5rem);
	flex-wrap: wrap;
}
.hp-reviews__head-text {
	min-width: 0;
	flex: 1 1 auto;
	max-width: 36rem;
}
.hp-reviews__eyebrow {
	font-family: var(--font-heading-family, sans-serif);
	font-size: clamp(0.78rem, 0.9vw, 0.95rem);
	font-weight: 600;
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: var(--color-accent, #ff4d8d);
	margin: 0 0 clamp(0.75rem, 1.4vw, 1.2rem);
	display: inline-flex;
	align-items: center;
	gap: 0.6rem;
	flex-wrap: wrap;
}
/* On light grounds the raw accent is 3.05:1 against the cream at this
   small size — swap to the darkened accent. On saturated (dark) grounds
   the raw pink already passes (5.9:1), so it stays. */
.hp-reviews[data-eb-ground="light"] .hp-reviews__eyebrow {
	color: var(--color-accent-ink, #C52477);
}
/* Platform rating badges — Google + Trustpilot profile totals under the
   H2. Each is a live link to the profile so the numbers are verifiable. */
.hp-reviews__badges {
	list-style: none;
	display: flex;
	flex-wrap: wrap;
	gap: 0.6rem 0.75rem;
	margin: clamp(1.1rem, 2vw, 1.6rem) 0 0;
	padding: 0;
}
.hp-reviews__badge {
	display: inline-flex;
	align-items: center;
	gap: 0.45rem;
	padding: 0.45rem 0.9rem;
	border-radius: 999px;
	border: 1px solid color-mix(in srgb, currentColor 25%, transparent);
	background: color-mix(in srgb, currentColor 6%, transparent);
	color: currentColor;
	text-decoration: none;
	font-family: var(--font-heading-family, sans-serif);
	font-size: clamp(0.82rem, 0.95vw, 0.95rem);
	line-height: 1;
	transition: border-color 0.2s ease, background 0.2s ease;
}
.hp-reviews__badge:hover,
.hp-reviews__badge:focus-visible {
	border-color: var(--color-accent, #ff4d8d);
	background: color-mix(in srgb, currentColor 10%, transparent);
	outline: none;
}
.hp-reviews__badge-logo {
	flex: 0 0 auto;
	display: block;
}
.hp-reviews__badge-rating {
	font-weight: 800;
}
.hp-reviews__badge-text {
	font-weight: 500;
	opacity: 0.75;
	letter-spacing: 0.01em;
}
.hp-reviews__h2 {
	font-family: var(--font-heading-family, sans-serif);
	font-weight: 800;
	font-size: clamp(2.25rem, 5.5vw, 5rem);
	line-height: 1.02;
	letter-spacing: -0.02em;
	margin: 0;
	max-width: 22ch;
}

/* Prev/next nav — sits to the right of the header on desktop, drops
   under the row on mobile. Style is borrowed from .hp-youtube__arrow. */
.hp-reviews__nav {
	display: flex;
	gap: 0.6rem;
	flex: 0 0 auto;
}
.hp-reviews__arrow {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 3rem;
	height: 3rem;
	border-radius: 50%;
	border: 1px solid color-mix(in srgb, currentColor 30%, transparent);
	background: color-mix(in srgb, currentColor 6%, transparent);
	color: currentColor;
	cursor: pointer;
	transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease, opacity 0.2s ease;
	-webkit-tap-highlight-color: transparent;
}
.hp-reviews__arrow:hover,
.hp-reviews__arrow:focus-visible {
	background: var(--color-accent, #ff4d8d);
	border-color: var(--color-accent, #ff4d8d);
	color: var(--color-white, #fff);
	outline: none;
}
.hp-reviews__arrow[disabled] {
	opacity: 0.3;
	pointer-events: none;
}

/* Horizontal track. Spans the full section width; the LEFT padding
   is computed to land flush with the centered header's left edge
   (so the first card's left edge sits directly under "What Our
   Clients Say."). The track has no right padding, so the trailing
   card visually cuts off at the viewport edge — the parent's
   overflow: hidden plus scroll-snap inside the track handles the
   peek + slider behavior with no negative margins needed. */
.hp-reviews__track {
	list-style: none;
	width: 100%;
	margin: 0;
	/* padding-left adapts: at viewports up to 1480px wide it matches
	   the header's clamp() padding; above 1480px it equals the
	   header's true left edge offset = (vw - 1480) / 2 + base
	   padding. The max() picks whichever is larger so the alignment
	   holds at every breakpoint. */
	padding: 0.5rem 0 0.5rem max(
		clamp(1.5rem, 6vw, 5rem),
		calc(50% - 740px + clamp(1.5rem, 6vw, 5rem))
	);
	/* scroll-padding MUST mirror padding-left. With mandatory snap and no
	   scroll-padding, scrollLeft=0 is not a valid snap position (card 1's
	   snap point sits past the padding), so Chrome auto-snapped the track
	   ~25px on initial layout — BEFORE first paint. Any scroll permanently
	   stops LCP measurement, so the whole page recorded NO Largest
	   Contentful Paint and PageSpeed errored with "!" instead of a score.
	   Mirroring the padding makes the at-rest position a snap point (no
	   load-time snap) and keeps cards aligned under the header's left edge
	   when the arrows drive the slider. */
	scroll-padding-left: max(
		clamp(1.5rem, 6vw, 5rem),
		calc(50% - 740px + clamp(1.5rem, 6vw, 5rem))
	);
	display: flex;
	gap: clamp(1rem, 1.8vw, 1.5rem);
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: none;
	scroll-behavior: smooth;
}
.hp-reviews__track::-webkit-scrollbar { display: none; }

.hp-reviews__item {
	flex: 0 0 auto;
	margin: 0;
	/* 3 cards visible on desktop. The 2-col internal layout (30%
	   portrait + 70% prose) needs enough width that the portrait
	   doesn't squish, so the lower bound bumps to ~360px and the
	   upper bound to ~540px. */
	width: clamp(360px, calc((100vw - clamp(3rem, 12vw, 10rem)) / 3 - clamp(1rem, 1.8vw, 1.5rem)), 540px);
	scroll-snap-align: start;
}
@media (max-width: 1024px) {
	.hp-reviews__item {
		width: clamp(360px, calc((100vw - clamp(3rem, 12vw, 10rem)) / 2 - clamp(1rem, 1.8vw, 1.5rem)), 540px);
	}
}
@media (max-width: 680px) {
	.hp-reviews__item {
		width: min(86vw, 380px);
	}
}

/* Card chassis — 2-column grid: 30% portrait + name, 70% review body.
   Falls back to a stacked single-column layout under 520px so the
   portrait still reads but the card doesn't collapse vertically too
   far. */
.hp-reviews__card {
	display: grid;
	grid-template-columns: minmax(0, 30%) minmax(0, 1fr);
	column-gap: clamp(1rem, 1.6vw, 1.5rem);
	row-gap: clamp(0.75rem, 1.2vw, 1rem);
	height: 100%;
	background: var(--color-paper, #f5efe6);
	color: var(--color-primary, #081427);
	border-radius: 18px;
	padding: clamp(1.4rem, 2vw, 1.85rem);
	box-shadow:
		0 28px 60px -28px rgba(0, 0, 16, 0.35),
		0 8px 20px -10px rgba(0, 0, 16, 0.18);
	position: relative;
}
.hp-reviews[data-eb-ground="light"] .hp-reviews__card {
	background: #fff;
}

/* Column 1 — full-bleed portrait then name underneath. */
.hp-reviews__person {
	display: flex;
	flex-direction: column;
	gap: clamp(0.6rem, 1vw, 0.85rem);
	min-width: 0;
}
.hp-reviews__portrait {
	margin: 0;
	width: 100%;
	aspect-ratio: 4 / 5;
	border-radius: 14px;
	overflow: hidden;
	background: color-mix(in srgb, var(--color-accent, #ff4d8d) 10%, transparent);
	box-shadow: inset 0 0 0 1px rgba(0, 0, 16, 0.06);
	position: relative;
}
.hp-reviews__portrait-img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
	transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.hp-reviews__card:hover .hp-reviews__portrait-img {
	transform: scale(1.04);
}
.hp-reviews__portrait-fallback {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	background: linear-gradient(
		135deg,
		color-mix(in srgb, var(--color-accent, #ff4d8d) 20%, transparent),
		color-mix(in srgb, var(--color-accent, #ff4d8d) 6%, transparent)
	);
}
.hp-reviews__portrait-initials {
	font-family: var(--font-heading-family, sans-serif);
	font-weight: 800;
	font-size: clamp(2.4rem, 5vw, 3.6rem);
	letter-spacing: 0.02em;
	color: var(--color-accent, #ff4d8d);
	line-height: 1;
}
.hp-reviews__who {
	display: flex;
	flex-direction: column;
	gap: 0.15rem;
	min-width: 0;
}
.hp-reviews__name {
	font-family: var(--font-heading-family, sans-serif);
	font-weight: 700;
	font-size: clamp(0.98rem, 1.1vw, 1.1rem);
	color: rgba(0, 0, 16, 0.95);
	letter-spacing: -0.005em;
	line-height: 1.2;
}
.hp-reviews__meta {
	font-size: 0.78rem;
	color: rgba(0, 0, 16, 0.6);
	display: inline-flex;
	gap: 0.35rem;
	flex-wrap: wrap;
}

/* Column 2 — review prose (stars, headline, body, foot). */
.hp-reviews__body-col {
	display: flex;
	flex-direction: column;
	gap: clamp(0.7rem, 1.2vw, 1rem);
	min-width: 0;
}

/* Below 520px the 30/70 split crushes the body column — stack the
   portrait above the prose. */
@media (max-width: 520px) {
	.hp-reviews__card {
		grid-template-columns: 1fr;
	}
	.hp-reviews__person {
		flex-direction: row;
		align-items: center;
		gap: 0.85rem;
	}
	.hp-reviews__portrait {
		width: 64px;
		aspect-ratio: 1 / 1;
		flex: 0 0 auto;
		border-radius: 50%;
	}
}

.hp-reviews__stars {
	display: inline-flex;
	align-items: center;
	gap: 2px;
}
.hp-reviews__star {
	fill: rgba(0, 0, 16, 0.18);
	transition: fill 0.2s ease;
}
.hp-reviews__star.is-on {
	/* Star yellow (Google's rating gold) on every card regardless of
	   source platform — one uniform rating colour site-wide. Platform
	   identity lives in the badge logos + "Read on …" links instead. */
	fill: #fbbc05;
}
.hp-reviews__verified {
	display: inline-flex;
	align-items: center;
	gap: 0.3rem;
	margin-left: 0.6rem;
	padding: 0.15rem 0.55rem;
	border-radius: 999px;
	font-size: 0.7rem;
	font-weight: 600;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	background: rgba(0, 182, 122, 0.12);
	color: #00875c;
}

.hp-reviews__headline {
	font-family: var(--font-heading-family, sans-serif);
	font-weight: 800;
	font-size: clamp(1.05rem, 1.3vw, 1.25rem);
	line-height: 1.25;
	margin: 0;
	color: rgba(0, 0, 16, 0.95);
	letter-spacing: -0.01em;
}
.hp-reviews__body {
	font-size: clamp(0.92rem, 1.05vw, 1rem);
	line-height: 1.6;
	color: rgba(0, 0, 16, 0.78);
	margin: 0;
	flex: 1 1 auto;
}

.hp-reviews__foot {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	padding-top: clamp(0.85rem, 1.4vw, 1.1rem);
	border-top: 1px solid rgba(0, 0, 16, 0.08);
	font-size: 0.8rem;
}
.hp-reviews__date {
	color: rgba(0, 0, 16, 0.55);
	font-variant-numeric: tabular-nums;
}
.hp-reviews__source,
.hp-reviews__source-tag {
	/* accent-ink: the link sits on the white review card, where the raw
	   accent is 3.48:1 at 12.8px — below the 4.5:1 AA floor. */
	color: var(--color-accent-ink, #C52477);
	font-weight: 600;
	letter-spacing: 0.02em;
	text-decoration: none;
	display: inline-flex;
	align-items: center;
	gap: 0.3rem;
	transition: gap 0.25s ease;
}
.hp-reviews__source:hover {
	gap: 0.55rem;
}

.hp-reviews__cta {
	margin: clamp(2.5rem, 5vw, 4rem) auto 0;
	max-width: 1480px;
	padding-inline: clamp(1.5rem, 6vw, 5rem);
	display: flex;
	justify-content: center;
	flex-wrap: wrap;
	gap: 1rem clamp(2rem, 4vw, 3.5rem);
}
.hp-reviews__cta-link {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	font-family: var(--font-heading-family, sans-serif);
	font-weight: 700;
	font-size: clamp(0.95rem, 1.1vw, 1.05rem);
	letter-spacing: -0.005em;
	color: currentColor;
	text-decoration: none;
	border-bottom: 2px solid var(--color-accent, #ff4d8d);
	padding-bottom: 0.2rem;
	transition: gap 0.25s ease;
}
.hp-reviews__cta-link:hover {
	gap: 0.7rem;
}

/* Saturated-ground colour pass — switch card foot / eyebrow tones so
   the cards still read well when the section's ground is navy. */
.hp-reviews[data-eb-ground="saturated"] .hp-reviews__h2 {
	color: var(--color-paper, #f5efe6);
}


/* ---------- Reduced motion ---------- */

@media (max-width: 0) /* prefers-reduced-motion neutralised — see main.js header */ {
	*, *::before, *::after {
		animation-duration: 0.001ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.001ms !important;
	}
}
