/**
 * Pixal Media — main stylesheet.
 *
 * Contents
 *   1.  Design tokens
 *   2.  Reset and base
 *   3.  Typography
 *   4.  Layout primitives
 *   5.  Buttons and links
 *   6.  Header and navigation
 *   7.  Mobile navigation
 *   8.  Hero
 *   9.  Section headers
 *   10. Cards and grids
 *   11. Statistics
 *   12. Process
 *   13. Split and compare layouts
 *   14. Accordion and FAQ
 *   15. Testimonials
 *   16. CTA panel
 *   17. Forms
 *   18. Inner page furniture
 *   19. Footer
 *   20. Prose (editor content)
 *   21. Motion and reveals
 *   22. Utilities
 */

/* ==========================================================================
   1. Design tokens
   ========================================================================== */

:root {
	/* Colour — a deep indigo base with an electric violet-to-cyan accent. */
	--ink-900: #06070f;
	--ink-800: #0a0b14;
	--ink-700: #0f1120;
	--ink-600: #151831;
	--ink-500: #1d2140;

	--surface: rgba(255, 255, 255, 0.03);
	--surface-2: rgba(255, 255, 255, 0.05);
	--line: rgba(255, 255, 255, 0.09);
	--line-strong: rgba(255, 255, 255, 0.16);

	--text: #eef0f8;
	--text-muted: #9ba2be;
	/* Lightened from #6f768f, which measured 4.36:1 — just under the 4.5:1 AA
	   threshold. This is the colour used for small print and placeholders, so
	   it is exactly where the margin matters most. Now 5.1:1. */
	--text-dim: #7b8299;

	/*
	 * Primary is amber; buttons and links are orange. Both are measured against
	 * the page background: amber reaches 11.8:1 and orange 5.5:1, so each is
	 * comfortably past the 4.5:1 AA threshold for body-sized text.
	 */
	--accent: #fbbf24;
	--accent-rgb: 251, 191, 36;
	--accent-2: #ea580c;
	--accent-2-rgb: 234, 88, 12;

	/* Hover brightens rather than darkens, which reads as "active" on a dark
	   ground and raises contrast rather than lowering it. */
	--accent-hover: #f97316;
	--link-hover: #fbbf24;

	/* White on these fills would only manage 3.6:1, so filled buttons and
	   badges take near-black text instead — 5.2:1 on the darker orange end. */
	--on-accent: #1a1205;

	--accent-soft: rgba(251, 191, 36, 0.14);

	--gradient: linear-gradient(120deg, var(--accent) 0%, var(--accent-2) 100%);
	--gradient-hover: linear-gradient(120deg, #fcd34d 0%, var(--accent-hover) 100%);
	--gradient-soft: linear-gradient(140deg, rgba(251, 191, 36, 0.16), rgba(234, 88, 12, 0.08));

	/* Typography */
	--font-sans: "Poppins", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;

	/* Headings stop at 600 — the theme never uses a heavier weight. */
	--fw-body: 400;
	--fw-medium: 500;
	--fw-heading: 600;

	/*
	 * The whole scale is tuned so the hero — heading, copy and the enquiry form
	 * beside it — fits inside a 1366 x 768 laptop viewport without scrolling,
	 * which is the most common screen this site will be judged on.
	 * Display tops out at 44px rather than 52px, body at 16px rather than 17px.
	 */
	--fs-display: clamp(1.875rem, 1.15rem + 2.4vw, 2.75rem);
	--fs-h1: clamp(1.75rem, 1.2rem + 2.2vw, 2.5rem);
	--fs-h2: clamp(1.5rem, 1.1rem + 1.6vw, 2.125rem);
	--fs-h3: clamp(1.0625rem, 0.98rem + 0.4vw, 1.1875rem);
	--fs-body: clamp(0.9375rem, 0.91rem + 0.12vw, 1rem);
	--fs-sm: 0.875rem;
	--fs-xs: 0.75rem;

	--lh-tight: 1.12;
	--lh-heading: 1.22;
	--lh-body: 1.65;

	/* Spacing — a 4px base scale. */
	--sp-1: 0.25rem;
	--sp-2: 0.5rem;
	--sp-3: 0.75rem;
	--sp-4: 1rem;
	--sp-5: 1.5rem;
	--sp-6: 2rem;
	--sp-7: 2.5rem;
	--sp-8: 3rem;
	--sp-9: 4rem;
	--sp-10: 5rem;

	--section-y: clamp(3rem, 1.8rem + 4.6vw, 5.5rem);

	/* Containers */
	--container: 1240px;
	--container-narrow: 780px;
	--gutter: clamp(1rem, 0.5rem + 2vw, 2.5rem);

	/* Radius */
	--radius-sm: 8px;
	--radius: 14px;
	--radius-lg: 22px;
	--radius-xl: 32px;
	--radius-pill: 999px;

	/* Shadows */
	--shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.25);
	--shadow: 0 18px 40px -20px rgba(0, 0, 0, 0.7);
	--shadow-lg: 0 40px 80px -40px rgba(0, 0, 0, 0.85);
	--shadow-accent: 0 20px 50px -24px rgba(var(--accent-2-rgb), 0.75);

	/* Motion */
	--ease: cubic-bezier(0.22, 0.61, 0.36, 1);
	--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
	--dur-fast: 150ms;
	--dur: 280ms;
	--dur-slow: 600ms;

	/* Layout constants */
	--header-h: 70px;

	color-scheme: dark;
}

/* ==========================================================================
   2. Reset and base
   ========================================================================== */

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

html {
	-webkit-text-size-adjust: 100%;
	scroll-behavior: smooth;
	/* Anchor targets clear the sticky header. */
	scroll-padding-top: calc(var(--header-h) + 1rem);
}

body {
	margin: 0;
	background-color: var(--ink-800);
	color: var(--text);
	font-family: var(--font-sans);
	font-size: var(--fs-body);
	line-height: var(--lh-body);
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	/* Nothing in the layout may cause sideways scrolling. */
	overflow-x: hidden;
}

body.nav-open {
	overflow: hidden;
}

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

img {
	height: auto;
}

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

button {
	cursor: pointer;
}

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

p {
	margin: 0 0 var(--sp-4);
}

p:last-child {
	margin-bottom: 0;
}

a {
	color: var(--accent-2);
	text-decoration-thickness: 1px;
	text-underline-offset: 3px;
	transition: color var(--dur-fast) var(--ease);
}

a:hover {
	color: var(--link-hover);
}

hr {
	border: 0;
	border-top: 1px solid var(--line);
	margin: var(--sp-7) 0;
}

/* One consistent, always-visible focus ring. */
:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 3px;
	border-radius: var(--radius-sm);
}

:focus:not(:focus-visible) {
	outline: none;
}

::selection {
	background: rgba(var(--accent-rgb), 0.35);
	color: var(--text);
}

.skip-link {
	position: absolute;
	top: -100px;
	left: var(--sp-4);
	z-index: 999;
	padding: var(--sp-3) var(--sp-5);
	border-radius: var(--radius);
	background: var(--accent);
	color: var(--on-accent);
	font-weight: 600;
	text-decoration: none;
	transition: top var(--dur) var(--ease);
}

.skip-link:focus {
	top: var(--sp-4);
	color: var(--on-accent);
}

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

/* ==========================================================================
   3. Typography
   ========================================================================== */

h1,
h2,
h3,
h4,
h5,
h6 {
	margin: 0 0 var(--sp-4);
	font-weight: var(--fw-heading);
	line-height: var(--lh-heading);
	letter-spacing: -0.022em;
	text-wrap: balance;
}

h1 {
	font-size: var(--fs-h1);
}

h2 {
	font-size: var(--fs-h2);
}

h3 {
	font-size: var(--fs-h3);
}

.accent-text {
	background: var(--gradient);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
	/* Keeps the gradient readable if background-clip is unsupported. */
	-webkit-text-fill-color: transparent;
}

/* Sentence case rather than uppercase: at this size uppercase forces a much
   wider pill and reads louder than the heading it introduces. */
.eyebrow {
	display: inline-flex;
	align-items: center;
	gap: var(--sp-2);
	margin: 0 0 var(--sp-3);
	padding: 0.375rem 0.875rem;
	border: 1px solid var(--line);
	border-radius: var(--radius-pill);
	background: var(--surface);
	color: var(--text-muted);
	font-size: 0.8125rem;
	font-weight: var(--fw-medium);
	letter-spacing: 0;
}

.eyebrow__dot {
	width: 6px;
	height: 6px;
	border-radius: 50%;
	background: var(--accent);
	box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.18);
}

/* ==========================================================================
   4. Layout primitives
   ========================================================================== */

.site-wrap {
	display: flex;
	min-height: 100vh;
	/*
	 * On a phone, 100vh is the height with the browser's address bar fully
	 * collapsed — taller than what is actually visible most of the time. That
	 * extra reserved space is what reads as a scroll glitch: a phantom gap
	 * that appears and disappears as the chrome shows and hides while
	 * scrolling. dvh tracks the real visible viewport continuously instead.
	 * Kept as a second declaration, not a replacement, so a browser that does
	 * not understand dvh simply keeps the vh value above.
	 */
	min-height: 100dvh;
	flex-direction: column;
}

.site-main {
	flex: 1 0 auto;
}

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

.container--narrow {
	max-width: var(--container-narrow);
}

.section {
	position: relative;
	padding-block: var(--section-y);
}

.section--intro {
	padding-block-start: clamp(2.5rem, 1.5rem + 4vw, 5rem);
}

.section__footer {
	margin-top: var(--sp-8);
	text-align: center;
}

/* Alternating band so long pages do not read as one flat surface. */
.section--why,
.section--process,
.section--faq,
.section--benefits,
.section--compare {
	background: linear-gradient(180deg, transparent, var(--ink-900) 12%, var(--ink-900) 88%, transparent);
}

/* ==========================================================================
   5. Buttons and links
   ========================================================================== */

.btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--sp-2);
	padding: 0.875rem 1.5rem;
	border: 1px solid transparent;
	border-radius: var(--radius-pill);
	font-size: var(--fs-sm);
	font-weight: 600;
	line-height: 1.2;
	text-align: center;
	text-decoration: none;
	white-space: nowrap;
	transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
		background-color var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}

.btn__icon {
	flex: none;
	transition: transform var(--dur) var(--ease);
}

.btn:hover .btn__icon {
	transform: translateX(3px);
}

.btn--primary {
	background: var(--gradient);
	color: var(--on-accent);
	box-shadow: var(--shadow-accent);
}

/* Hover brightens the fill and lifts the card, so the state change is
   obvious without relying on colour alone. */
.btn--primary:hover {
	background: var(--gradient-hover);
	color: var(--on-accent);
	transform: translateY(-2px);
	box-shadow: 0 26px 60px -24px rgba(var(--accent-2-rgb), 0.95);
}

.btn--ghost {
	border-color: var(--line-strong);
	background: var(--surface);
	color: var(--text);
}

.btn--ghost:hover {
	border-color: rgba(var(--accent-rgb), 0.55);
	background: var(--surface-2);
	color: var(--text);
	transform: translateY(-2px);
}

.btn--sm {
	padding: 0.625rem 1.125rem;
	font-size: var(--fs-xs);
}

.btn--lg {
	padding: 1.0625rem 1.875rem;
	font-size: 1rem;
}

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

.btn:active {
	transform: translateY(0);
}

/* ==========================================================================
   6. Header and navigation
   ========================================================================== */

.announcement {
	position: relative;
	z-index: 60;
	background: var(--gradient);
	color: var(--on-accent);
	font-size: var(--fs-xs);
	font-weight: 600;
	text-align: center;
}

.announcement p {
	margin: 0;
	padding-block: var(--sp-2);
}

/*
 * The header keeps a constant outer height at every scroll position. Only the
 * bar inside it changes — it contracts, rounds into a pill and turns to glass.
 * Holding the outer box still is what stops the page jumping as the state
 * flips, which a shrinking header would otherwise cause on every scroll.
 */
.site-header {
	position: sticky;
	top: 0;
	z-index: 50;
	/* Constant in both states, so the bar's own height is the only thing that
	   ever changes — and it does not. */
	padding-block: 0.625rem;
	padding-inline: var(--gutter);
}


/* The overlay header sits above the hero, which supplies its own background. */
.site-header--overlay:not(.is-stuck) {
	background: transparent;
}

.has-overlay-header .site-main {
	margin-top: calc(var(--header-h) * -1);
}

.site-header__inner {
	display: flex;
	align-items: center;
	gap: var(--sp-5);
	/* Near enough full width at rest, so the logo and actions sit close to the
	   edges; the stuck state pulls this in to form the floating pill. */
	max-width: 1560px;
	/* 70px in both states. Holding the bar's height still is what keeps the
	   header's footprint constant, so nothing below it moves on scroll. */
	min-height: 70px;
	margin-inline: auto;
	padding-inline: 0;
	border: 1px solid transparent;
	border-radius: var(--radius-pill);
	/* A longer, softer curve on the width so the contraction reads as a
	   deliberate movement rather than a snap. */
	transition: max-width 620ms cubic-bezier(0.22, 1, 0.36, 1),
		padding 420ms cubic-bezier(0.22, 1, 0.36, 1),
		background-color 420ms var(--ease), border-color 420ms var(--ease),
		box-shadow 420ms var(--ease), backdrop-filter 420ms var(--ease);
}

/* The floating glass pill. */
.site-header.is-stuck .site-header__inner,
.site-header--solid .site-header__inner {
	padding-inline: 1rem;
	border-color: var(--line-strong);
	/*
	 * Opacity is set by contrast, not taste. The nav sits in muted grey, and
	 * the pill can pass over the hero's brightest area — at 0.62 the composite
	 * drops the nav to about 3.8:1. At 0.76 the worst case stays above 4.5:1
	 * while the blur still carries the glass effect.
	 */
	background: rgba(12, 13, 26, 0.76);
	box-shadow: 0 20px 45px -26px rgba(0, 0, 0, 0.95),
		inset 0 1px 0 rgba(255, 255, 255, 0.07);
	/*
	 * Applied only in this state. A backdrop-filter creates a containing block
	 * for fixed and absolute descendants, which would otherwise trap the
	 * dropdown panels inside the pill.
	 */
	/*
	 * Saturation stays close to neutral. At 170% the blurred amber glow behind
	 * the hero came through the glass vividly enough to wash out the logo on a
	 * narrow screen — the backdrop filter amplifies whatever it samples, so a
	 * warm page needs a gentler hand than a cool one.
	 */
	backdrop-filter: blur(22px) saturate(115%);
	-webkit-backdrop-filter: blur(22px) saturate(115%);
}

.site-header.is-stuck .site-header__inner {
	max-width: 1160px;
}

/*
 * Flex, not a plain block. The logo is an inline-flex anchor, so in a block
 * parent it sits on the text baseline and the descender space below pushes it
 * upward — it measured 7px above centre and 14px below. As a flex item it
 * centres against the bar like everything else.
 */
.site-header__brand {
	display: flex;
	align-items: center;
	flex: none;
}

.site-logo {
	display: inline-flex;
	align-items: center;
	gap: var(--sp-3);
	color: var(--text);
	font-weight: var(--fw-heading);
	letter-spacing: -0.02em;
	text-decoration: none;
}

.site-logo__img {
	max-height: 40px;
	width: auto;
}

.site-logo__mark {
	position: relative;
	width: 27px;
	height: 27px;
	flex: none;
	border-radius: 9px;
	background: var(--gradient);
	/*
	 * A tight, evenly spread glow. The shared --shadow-accent is offset 20px
	 * downward, which on a 27px mark reads as the logo sitting high in the bar.
	 */
	box-shadow: 0 2px 12px -4px rgba(var(--accent-2-rgb), 0.85);
}

.site-logo__mark::after {
	position: absolute;
	inset: 8px;
	content: "";
	border-radius: 3px;
	background: var(--ink-900);
}

.site-logo__name {
	font-size: 1.0625rem;
	white-space: nowrap;
}

.site-nav {
	flex: 1 1 auto;
	display: none;
}

.site-nav .menu {
	display: flex;
	align-items: center;
	flex-wrap: nowrap;
	gap: var(--sp-1);
}

.site-nav .menu-item {
	position: relative;
}

.menu-link {
	display: inline-flex;
	align-items: center;
	white-space: nowrap;
	padding: var(--sp-2) var(--sp-3);
	border-radius: var(--radius-sm);
	color: var(--text-muted);
	font-size: var(--fs-sm);
	font-weight: 500;
	text-decoration: none;
	transition: color var(--dur-fast) var(--ease);
}

.menu-link:hover,
.site-nav .current-menu-item > .menu-link,
.site-nav .current-menu-ancestor > .menu-link {
	color: var(--text);
}

/* Underline grows from the centre on hover and on the current page. */
.menu-link span {
	position: relative;
}

.menu-link span::after {
	position: absolute;
	bottom: -5px;
	left: 50%;
	width: 0;
	height: 2px;
	content: "";
	border-radius: 2px;
	background: var(--gradient);
	transform: translateX(-50%);
	transition: width var(--dur) var(--ease);
}

.menu-link:hover span::after,
.site-nav .current-menu-item > .menu-link span::after,
.site-nav .current-menu-ancestor > .menu-link span::after {
	width: 100%;
}

.submenu-toggle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 24px;
	height: 24px;
	padding: 0;
	border: 0;
	border-radius: var(--radius-sm);
	background: none;
	color: var(--text-muted);
	transition: transform var(--dur) var(--ease), color var(--dur-fast) var(--ease);
}

/* Same baseline problem as the logo: the toggle is an inline-flex button, so
   next to a text link it rides up on the baseline instead of sitting level
   with the word it belongs to. */
.site-nav .has-dropdown {
	display: flex;
	align-items: center;
}

.has-dropdown > .submenu-toggle {
	margin-left: -6px;
}

.submenu-toggle[aria-expanded="true"] {
	color: var(--accent-2);
	transform: rotate(45deg);
}

.sub-menu {
	position: absolute;
	top: calc(100% + 12px);
	left: 0;
	z-index: 20;
	min-width: 260px;
	padding: var(--sp-3);
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
	background: rgba(15, 17, 32, 0.96);
	backdrop-filter: blur(18px);
	-webkit-backdrop-filter: blur(18px);
	box-shadow: var(--shadow-lg);
	opacity: 0;
	visibility: hidden;
	transform: translateY(-8px);
	transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease), visibility var(--dur);
}

.sub-menu .menu-link {
	display: block;
	width: 100%;
	padding: var(--sp-3);
	border-radius: var(--radius-sm);
}

.sub-menu .menu-link:hover {
	background: var(--surface-2);
}

.sub-menu .menu-link span::after {
	display: none;
}

/* Opened by the toggle button, by keyboard focus, or by hover — in that order
   of priority, so pointer users get convenience without anyone else losing
   access to the submenu. */
.has-dropdown.is-open > .sub-menu,
.has-dropdown:focus-within > .sub-menu,
.has-dropdown:hover > .sub-menu {
	opacity: 1;
	visibility: visible;
	transform: translateY(0);
}

.site-header__actions {
	display: flex;
	align-items: center;
	gap: var(--sp-3);
	margin-left: auto;
}

/* Direct-contact shortcuts, shown alongside the CTA once the details exist.
   Hidden below the desktop breakpoint, where the same links live in the
   mobile menu and the bar has no room to spare. */
/* Shown at every width now: on a phone these replace the text links that
   used to sit at the foot of the open menu. */
.site-header__quick {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
}

.icon-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 36px;
	height: 36px;
	flex: none;
	border: 1px solid var(--line-strong);
	border-radius: 50%;
	color: var(--text-muted);
	text-decoration: none;
	transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease),
		background-color var(--dur-fast) var(--ease), transform var(--dur) var(--ease);
}

.icon-btn:hover {
	border-color: rgba(var(--accent-rgb), 0.6);
	background: rgba(var(--accent-rgb), 0.12);
	color: var(--accent);
	transform: translateY(-1px);
}

.icon-btn:active {
	transform: translateY(0);
}

.site-header__cta {
	display: none;
}

.nav-toggle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 36px;
	height: 36px;
	padding: 0;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--surface);
	color: var(--text);
}

.nav-toggle__bars {
	display: grid;
	gap: 4px;
	width: 16px;
}

.nav-toggle__bars span {
	display: block;
	height: 2px;
	border-radius: 2px;
	background: currentColor;
	transition: transform var(--dur) var(--ease), opacity var(--dur-fast) var(--ease);
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(1) {
	transform: translateY(6px) rotate(45deg);
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(2) {
	opacity: 0;
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(3) {
	transform: translateY(-6px) rotate(-45deg);
}

/* ==========================================================================
   7. Mobile navigation
   ========================================================================== */

.mobile-nav {
	position: fixed;
	inset: 0;
	z-index: 45;
	overflow-y: auto;
	padding-top: calc(var(--header-h) + var(--sp-4));
	background: var(--ink-900);
	opacity: 0;
	transition: opacity var(--dur) var(--ease);
}

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

.mobile-nav.is-open {
	opacity: 1;
}

.mobile-nav__inner {
	padding: var(--sp-5) var(--gutter) var(--sp-9);
}

.menu--mobile {
	display: grid;
	gap: var(--sp-1);
	margin-bottom: var(--sp-7);
}

.menu--mobile .menu-item {
	border-bottom: 1px solid var(--line);
}

.menu--mobile .menu-link {
	padding: var(--sp-4) 0;
	color: var(--text);
	font-size: 1.125rem;
	font-weight: 600;
}

.menu--mobile .menu-link span::after {
	display: none;
}

.menu--mobile .has-dropdown {
	display: grid;
	grid-template-columns: 1fr auto;
	align-items: center;
}

.menu--mobile .submenu-toggle {
	width: 44px;
	height: 44px;
}

.menu--mobile .sub-menu {
	position: static;
	display: grid;
	grid-column: 1 / -1;
	min-width: 0;
	max-height: 0;
	padding: 0;
	border: 0;
	border-radius: 0;
	background: none;
	box-shadow: none;
	opacity: 1;
	overflow: hidden;
	visibility: visible;
	transform: none;
	transition: max-height var(--dur) var(--ease), padding var(--dur) var(--ease);
}

.menu--mobile .has-dropdown.is-open > .sub-menu {
	max-height: 32rem;
	padding-bottom: var(--sp-3);
}

/* Hover must not open anything on touch. */
.menu--mobile .has-dropdown:hover > .sub-menu {
	max-height: 0;
}

.menu--mobile .has-dropdown.is-open:hover > .sub-menu {
	max-height: 32rem;
}

.menu--mobile .sub-menu .menu-link {
	padding: var(--sp-3) 0 var(--sp-3) var(--sp-4);
	color: var(--text-muted);
	font-size: 1rem;
	font-weight: 500;
}

.mobile-nav__footer {
	display: grid;
	gap: var(--sp-4);
}

@media (min-width: 1140px) {
	/*
	 * Three tracks with equal outer columns, so the menu is centred on the
	 * bar itself rather than on whatever space the logo happens to leave.
	 */
	.site-header__inner {
		display: grid;
		grid-template-columns: 1fr auto 1fr;
	}

	.site-header__brand {
		justify-self: start;
	}

	.site-nav {
		display: block;
		justify-self: center;
	}

	.site-header__actions {
		justify-self: end;
		margin-left: 0;
	}

	.site-header__cta {
		display: inline-flex;
	}

	.nav-toggle {
		display: none;
	}

	.mobile-nav {
		display: none !important;
	}
}

/* ==========================================================================
   8. Hero
   ========================================================================== */

.hero {
	position: relative;
	overflow: hidden;
	padding-block: calc(var(--header-h) + clamp(1.5rem, 1rem + 1.6vw, 2rem)) clamp(2rem, 1.5rem + 2.2vw, 3rem);
	background: var(--ink-900);
}

.hero__bg,
.page-hero__bg {
	position: absolute;
	inset: 0;
	pointer-events: none;
}

.hero__glow {
	position: absolute;
	border-radius: 50%;
	filter: blur(90px);
	opacity: 0.55;
}

.hero__glow--one {
	top: -10%;
	left: -5%;
	width: min(620px, 70vw);
	height: min(620px, 70vw);
	background: radial-gradient(circle, rgba(var(--accent-rgb), 0.3), transparent 68%);
}

.hero__glow--two {
	right: -8%;
	bottom: -20%;
	width: min(520px, 60vw);
	height: min(520px, 60vw);
	background: radial-gradient(circle, rgba(var(--accent-2-rgb), 0.4), transparent 68%);
}

/* A faint grid gives the dark field some structure without adding an image. */
.hero__grid {
	position: absolute;
	inset: 0;
	background-image: linear-gradient(var(--line) 1px, transparent 1px),
		linear-gradient(90deg, var(--line) 1px, transparent 1px);
	background-size: 64px 64px;
	mask-image: radial-gradient(ellipse 80% 60% at 50% 30%, #000 30%, transparent 75%);
	-webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 30%, #000 30%, transparent 75%);
	opacity: 0.6;
}

/* Nebula backdrop — layered radial gradients rather than an image, so the
   hero has depth without a large download.

   These are kept deliberately faint. Amber is a light colour, so a glow that
   looked right in a violet palette lifts the background luminance far enough
   to break contrast for the muted grey text sitting over it. Atmosphere, not
   a light source. */
.hero__nebula {
	position: absolute;
	border-radius: 50%;
	filter: blur(100px);
}

.hero__nebula--one {
	top: -22%;
	left: -10%;
	width: min(820px, 90vw);
	height: min(680px, 76vw);
	background: radial-gradient(ellipse at center, rgba(var(--accent-rgb), 0.34) 0%, rgba(var(--accent-rgb), 0.14) 38%, transparent 70%);
	opacity: 0.55;
}

.hero__nebula--two {
	top: -6%;
	right: -12%;
	width: min(780px, 86vw);
	height: min(720px, 80vw);
	/* Orange is darker than amber, so it can carry a little more weight. */
	background: radial-gradient(ellipse at center, rgba(var(--accent-2-rgb), 0.62) 0%, rgba(234, 120, 12, 0.26) 40%, transparent 70%);
	opacity: 0.7;
}

.hero__nebula--three {
	bottom: -30%;
	left: 26%;
	width: min(680px, 76vw);
	height: min(520px, 60vw);
	background: radial-gradient(ellipse at center, rgba(var(--accent-2-rgb), 0.4) 0%, rgba(var(--accent-2-rgb), 0.14) 45%, transparent 72%);
	opacity: 0.5;
}

/* A scatter of faint stars, drawn with repeating radial gradients. */
.hero__stars {
	position: absolute;
	inset: 0;
	background-image:
		radial-gradient(1.5px 1.5px at 12% 22%, rgba(255, 255, 255, 0.65), transparent),
		radial-gradient(1.5px 1.5px at 38% 12%, rgba(255, 255, 255, 0.45), transparent),
		radial-gradient(2px 2px at 62% 30%, rgba(255, 255, 255, 0.5), transparent),
		radial-gradient(1.5px 1.5px at 82% 16%, rgba(255, 255, 255, 0.4), transparent),
		radial-gradient(1.5px 1.5px at 24% 62%, rgba(255, 255, 255, 0.35), transparent),
		radial-gradient(2px 2px at 71% 68%, rgba(255, 255, 255, 0.4), transparent),
		radial-gradient(1.5px 1.5px at 48% 82%, rgba(255, 255, 255, 0.3), transparent),
		radial-gradient(1.5px 1.5px at 91% 54%, rgba(255, 255, 255, 0.35), transparent);
	opacity: 0.9;
}

/* An uploaded hero visual sits behind both columns in the form layout. */
.hero__backdrop {
	position: absolute;
	inset: 0;
}

.hero__backdrop-img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	/* Full opacity: the scrim below does the darkening, which keeps one place
	   to tune rather than two competing controls. */
	opacity: 1;
}

/* Sits on top of .hero__backdrop-img, which stays in the DOM underneath as
   the fallback — nothing extra to do if the video fails or never starts. */
.hero__backdrop-video {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* A visitor who has asked for less motion gets the still image only — the
   video stays loaded (so nothing breaks) but is simply not shown. */
@media (prefers-reduced-motion: reduce) {
	.hero__backdrop-video {
		display: none;
	}
}

/*
 * Readability scrim. Three stacked gradients, each doing one job:
 *
 *   1. Horizontal — heaviest on the left, where the heading and body copy sit,
 *      easing off toward the form. Light text over a bright photo is the whole
 *      risk here, so the text column is protected first.
 *   2. Vertical — darkens the top so the header bar stays legible, and the
 *      bottom so the hero blends into the section beneath instead of ending
 *      on a hard edge.
 *   3. Flat tint — a floor under the whole thing, so a very bright image
 *      cannot wash the hero out even where the other two are weakest.
 *
 * --hero-scrim scales all three together; the strength setting in the Hero
 * panel sets it, because how much cover an image needs depends on the image.
 */
.hero__backdrop::after {
	position: absolute;
	inset: 0;
	content: "";
	background:
		linear-gradient(100deg,
			rgba(6, 7, 15, calc(var(--hero-scrim) * 1.06)) 0%,
			rgba(6, 7, 15, calc(var(--hero-scrim) * 0.98)) 30%,
			rgba(6, 7, 15, calc(var(--hero-scrim) * 0.72)) 62%,
			rgba(6, 7, 15, calc(var(--hero-scrim) * 0.6)) 100%),
		linear-gradient(180deg,
			rgba(6, 7, 15, 0.82) 0%,
			rgba(6, 7, 15, 0.25) 34%,
			rgba(6, 7, 15, 0.55) 78%,
			var(--ink-800) 100%),
		rgba(6, 7, 15, calc(var(--hero-scrim) * 0.3));
}

.hero--form,
.hero--visual {
	--hero-scrim: 0.2;
}

/*
 * Only safe over an image that is genuinely dark. Measured against the
 * brightest pixel of a bright photo this setting drops muted body text to
 * about 3.5:1 — under the 4.5:1 AA threshold — which is why medium is the
 * default and this one carries a warning in the admin.
 */
.hero--scrim-light {
	--hero-scrim: 0.8;
}

.hero--scrim-strong {
	--hero-scrim: 0.94;
}

/*
 * Narrow screens stack the copy over the middle of the image rather than
 * sitting in the protected left column, so the scrim goes up a step.
 */
@media (max-width: 999px) {
	.hero--form,
	.hero--visual {
		--hero-scrim: 0.2;
	}

	.hero--scrim-light {
		--hero-scrim: 0.82;
	}

	.hero--scrim-strong {
		--hero-scrim: 0.97;
	}

	.hero__backdrop::after {
		/* Straight down on a phone: there is no left column to protect. */
		background:
			linear-gradient(180deg,
				rgba(6, 7, 15, calc(var(--hero-scrim) * 1.02)) 0%,
				rgba(6, 7, 15, calc(var(--hero-scrim) * 0.88)) 45%,
				rgba(6, 7, 15, calc(var(--hero-scrim) * 0.95)) 80%,
				var(--ink-800) 100%),
			rgba(6, 7, 15, calc(var(--hero-scrim) * 0.35));
	}
}

.hero__inner {
	position: relative;
	display: grid;
	gap: clamp(2.5rem, 1rem + 6vw, 4rem);
	align-items: center;
}

.hero__title {
	margin-bottom: var(--sp-4);
	font-size: var(--fs-display);
	line-height: var(--lh-tight);
	letter-spacing: -0.03em;
	/* The scrim is deliberately light, so the photo shows through — this
	   picks up the slack for legibility wherever the image underneath it
	   happens to be bright. */
	text-shadow: 0 2px 20px rgba(2, 2, 6, 0.6), 0 1px 4px rgba(2, 2, 6, 0.5);
}

/*
 * The gradient-clipped word needs a different, lighter treatment: the title's
 * own dark text-shadow paints a halo right behind each glyph, which reads as
 * muddying the amber into brown rather than helping it. A soft drop-shadow
 * respects the glyph outlines instead of the text box, so it separates the
 * word from a bright photo without dulling the gradient itself — the same
 * two accent colours the primary button uses.
 */
.hero__title .accent-text {
	text-shadow: none;
	filter: drop-shadow(0 2px 10px rgba(2, 2, 6, 0.55));
}

.hero__text {
	max-width: 54ch;
	margin-bottom: var(--sp-6);
	color: #fff;
	font-size: var(--fs-body);
	text-shadow: 0 2px 16px rgba(2, 2, 6, 0.65), 0 1px 3px rgba(2, 2, 6, 0.55);
}

.hero__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sp-3);
}

.hero__points {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sp-3) var(--sp-5);
	margin-top: var(--sp-7);
	padding-top: var(--sp-6);
	border-top: 1px solid var(--line);
}

.hero__points li {
	display: inline-flex;
	align-items: center;
	gap: var(--sp-2);
	color: var(--text-muted);
	font-size: var(--fs-sm);
}

.hero__check {
	flex: none;
	color: var(--accent-2);
}

.hero__visual {
	position: relative;
}

.hero__image-frame {
	position: relative;
	border: 1px solid var(--line);
	border-radius: var(--radius-xl);
	overflow: hidden;
	box-shadow: var(--shadow-lg);
	/* Reserving the ratio prevents the hero from jumping as the image loads. */
	aspect-ratio: 16 / 11;
	max-width: 100%;
}

.hero__image {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* Generated hero graphic, used until a real visual is uploaded. */
.hero__figure {
	position: relative;
	display: grid;
	place-items: center;
	aspect-ratio: 1;
	max-width: 100%;
}

.orbit {
	position: relative;
	display: grid;
	place-items: center;
	width: min(420px, 80%);
	aspect-ratio: 1;
}

.orbit__ring {
	position: absolute;
	border: 1px solid var(--line-strong);
	border-radius: 50%;
	inset: 0;
}

.orbit__ring--2 {
	inset: 14%;
	border-color: rgba(var(--accent-rgb), 0.35);
}

.orbit__ring--3 {
	inset: 28%;
	border-color: rgba(var(--accent-2-rgb), 0.3);
}

.orbit__core {
	width: 34%;
	aspect-ratio: 1;
	border-radius: 50%;
	background: var(--gradient);
	filter: blur(2px);
	box-shadow: 0 0 80px rgba(var(--accent-rgb), 0.7);
	animation: pulse 4.5s var(--ease) infinite;
}

.orbit__dot {
	position: absolute;
	width: 12px;
	height: 12px;
	border-radius: 50%;
	background: var(--accent-2);
	box-shadow: 0 0 18px var(--accent-2);
	transform-origin: center;
	animation: spin 18s linear infinite;
}

.orbit__dot--1 {
	inset: 0 auto auto 50%;
	margin-left: -6px;
}

.orbit__dot--2 {
	inset: 14% 14% auto auto;
	background: var(--accent);
	box-shadow: 0 0 18px var(--accent);
	animation-duration: 24s;
	animation-direction: reverse;
}

.orbit__dot--3 {
	inset: auto auto 28% 28%;
	animation-duration: 30s;
}

.hero__chip {
	position: absolute;
	display: inline-flex;
	align-items: center;
	gap: var(--sp-2);
	padding: var(--sp-3) var(--sp-4);
	border: 1px solid var(--line);
	border-radius: var(--radius-pill);
	background: rgba(15, 17, 32, 0.9);
	backdrop-filter: blur(10px);
	-webkit-backdrop-filter: blur(10px);
	box-shadow: var(--shadow);
	color: var(--text);
	font-size: var(--fs-xs);
	font-weight: 600;
	white-space: nowrap;
	animation: float 6s var(--ease) infinite;
}

.hero__chip svg {
	color: var(--accent-2);
}

.hero__chip--1 {
	top: 8%;
	left: 0;
}

.hero__chip--2 {
	right: 0;
	bottom: 26%;
	animation-delay: -2s;
}

.hero__chip--3 {
	bottom: 4%;
	left: 6%;
	animation-delay: -4s;
}

/* --- Hero enquiry form ------------------------------------------------- */

.hero-form-card {
	position: relative;
	padding: clamp(1.125rem, 0.9rem + 1.1vw, 1.75rem);
	border: 1px solid var(--line-strong);
	border-radius: var(--radius-xl);
	background: rgba(10, 11, 22, 0.72);
	backdrop-filter: blur(18px) saturate(130%);
	-webkit-backdrop-filter: blur(18px) saturate(130%);
	box-shadow: var(--shadow-lg);
}

/*
 * Animated border: two light arcs travelling the card's outline in opposite
 * directions, meeting and parting at the corners.
 *
 * Each arc is a conic gradient rotated by an angle custom property, then masked
 * so only the 1.5px ring is painted — that is what lets the light follow the
 * rounded corners instead of sliding across a straight edge. The property has
 * to be registered with @property, because an unregistered custom property
 * cannot be interpolated and the animation would jump rather than sweep.
 *
 * Where @property is unsupported the arcs simply sit still, which leaves a
 * static two-tone gradient edge — the card still looks finished.
 */
@property --pixal-arc {
	syntax: "<angle>";
	initial-value: 0deg;
	inherits: false;
}

.hero-form-card::before,
.hero-form-card::after {
	position: absolute;
	inset: 0;
	content: "";
	padding: 1.5px;
	border-radius: inherit;
	pointer-events: none;
	-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
	mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
	-webkit-mask-composite: xor;
	mask-composite: exclude;
}

/* Amber, travelling clockwise. */
.hero-form-card::before {
	background: conic-gradient(
		from var(--pixal-arc),
		transparent 0deg,
		rgba(var(--accent-rgb), 0.15) 12deg,
		var(--accent) 42deg,
		rgba(var(--accent-rgb), 0.15) 72deg,
		transparent 96deg,
		transparent 360deg
	);
	animation: pixal-arc-cw 6s linear infinite;
}

/* Orange, starting from the opposite corner and travelling the other way. */
.hero-form-card::after {
	background: conic-gradient(
		from var(--pixal-arc),
		transparent 0deg,
		transparent 180deg,
		rgba(var(--accent-2-rgb), 0.15) 192deg,
		var(--accent-hover) 222deg,
		rgba(var(--accent-2-rgb), 0.15) 252deg,
		transparent 276deg,
		transparent 360deg
	);
	animation: pixal-arc-ccw 6s linear infinite;
}

@keyframes pixal-arc-cw {
	to {
		--pixal-arc: 360deg;
	}
}

@keyframes pixal-arc-ccw {
	to {
		--pixal-arc: -360deg;
	}
}

.hero-form-card .lead-form {
	padding: 0;
	border: 0;
	background: none;
	box-shadow: none;
}

/* The card heading is quiet on purpose — the fields are the point. */
.lead-form__title {
	margin-bottom: var(--sp-4);
	color: var(--text-dim);
	font-size: 1.25rem;
	font-weight: var(--fw-medium);
	letter-spacing: -0.01em;
}

.lead-form--compact .lead-form__grid {
	grid-template-columns: 1fr;
	gap: 0.625rem;
}

/*
 * Controls stay at a 44px touch target by default. They only tighten once
 * there is a pointer and enough width for the two-column hero, which is
 * exactly where the vertical budget is tight.
 */
.lead-form--compact .field__control {
	padding: 0.6875rem 0.875rem;
	border-color: var(--line-strong);
	border-radius: var(--radius);
	background: rgba(0, 0, 0, 0.3);
	font-size: 0.9375rem;
}

@media (min-width: 1000px) {
	.lead-form--compact .field__control {
		padding: 0.5rem 0.8125rem;
		font-size: var(--fs-sm);
	}

	.lead-form--compact .lead-form__grid {
		gap: 0.5rem;
	}

	.lead-form--compact .lead-form__actions {
		margin-top: 0.625rem;
	}

	.lead-form--compact [data-form-submit],
	.lead-form--compact .form-status {
		padding-block: 0.6875rem;
	}

	.hero-form-card {
		padding: 1.5rem;
	}
}

/* The full form's roomy textarea is wrong for the hero card, where height is
   the scarce resource. Three rows is enough to start a conversation. */
.lead-form--compact textarea.field__control {
	min-height: 5.25rem;
}

.lead-form--compact .field__control::placeholder {
	color: var(--text-dim);
	opacity: 1;
}

.lead-form--compact .field__consent {
	gap: var(--sp-2);
	font-size: var(--fs-xs);
	line-height: 1.45;
}

.lead-form--compact .field__consent input {
	width: 16px;
	height: 16px;
	margin-top: 1px;
}

.lead-form--compact .lead-form__actions {
	margin-top: var(--sp-4);
	flex-direction: column;
	align-items: flex-start;
	gap: var(--sp-2);
}

/* 44px tall by default so it is a comfortable tap target; it only shrinks at
   laptop widths, where the pointer is a mouse and height is the constraint. */
.lead-form--compact [data-form-submit],
.lead-form--compact .form-status {
	min-width: 9.5rem;
	padding-block: 0.875rem;
	padding-inline: 1.75rem;
}

.field__fieldset {
	margin: 0;
	padding: 0;
	border: 0;
	min-width: 0;
}

/* --- Dialling code + number ------------------------------------------- */

.phone-group {
	display: flex;
	align-items: stretch;
	border: 1px solid var(--line-strong);
	border-radius: var(--radius);
	background: rgba(0, 0, 0, 0.3);
	transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}

.phone-group:focus-within {
	border-color: var(--accent);
	box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.25);
}

.phone-group__select {
	position: relative;
	flex: none;
	display: flex;
	align-items: stretch;
	border-right: 1px solid var(--line);
}

.phone-group__code {
	flex: none;
	width: auto;
	padding: 0.6875rem 1.75rem 0.6875rem 0.875rem;
	border: 0;
	border-radius: 0;
	background-color: transparent;
	background-image: linear-gradient(45deg, transparent 50%, var(--text-muted) 50%),
		linear-gradient(135deg, var(--text-muted) 50%, transparent 50%);
	background-position: calc(100% - 14px) center, calc(100% - 8px) center;
	background-size: 6px 6px, 6px 6px;
	background-repeat: no-repeat;
	color: var(--text);
	font-size: 0.9375rem;
	appearance: none;
	cursor: pointer;
}

.phone-group__code:focus {
	outline: none;
}

.phone-group__code option {
	background: var(--ink-700);
	color: var(--text);
}

/* Enhanced state: the flag row is what you see, and the native select lies
   invisibly on top of it so it stays the real, keyboard-operable control and
   opens the platform's own picker on touch. */
.phone-group__display {
	display: none;
	align-items: center;
	gap: var(--sp-2);
	padding: 0.6875rem 0.875rem;
	color: var(--text);
	font-size: 0.9375rem;
	white-space: nowrap;
	pointer-events: none;
}

.phone-group__select--enhanced .phone-group__display {
	display: flex;
}

.phone-group__select--enhanced .phone-group__code {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	padding: 0;
	background-image: none;
	opacity: 0;
	cursor: pointer;
}

.phone-group__flags {
	display: inline-flex;
	flex: none;
}

.phone-group__flag[hidden] {
	display: none;
}

.phone-group__flag .flag {
	display: block;
	width: 20px;
	height: 14px;
	border-radius: 2px;
	/* A hairline keeps a white flag from bleeding into the dark field. */
	box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22);
}

.phone-group__dial {
	font-variant-numeric: tabular-nums;
}

.phone-group__caret {
	width: 0;
	height: 0;
	flex: none;
	margin-left: 2px;
	border-top: 4px solid var(--text-muted);
	border-right: 3.5px solid transparent;
	border-left: 3.5px solid transparent;
}

/* The focus ring is drawn on the shell, since the select itself is invisible. */
.phone-group__select--enhanced:focus-within .phone-group__display {
	color: var(--text);
}

/* The number input borrows the group's shell instead of drawing its own. */
.lead-form--compact .phone-group__number {
	flex: 1;
	min-width: 0;
	border: 0;
	border-radius: 0;
	background: transparent;
}

.lead-form--compact .phone-group__number:focus {
	box-shadow: none;
}

/* --- Budget range slider ---------------------------------------------- */

.field--budget {
	padding-block: var(--sp-1);
}

.range {
	display: flex;
	align-items: center;
	gap: var(--sp-3);
}

.range__value {
	flex: none;
	min-width: 4.5rem;
	color: var(--text);
	font-size: 0.8125rem;
	font-weight: var(--fw-heading);
	white-space: nowrap;
}

.range__value:last-child {
	text-align: right;
}

.range__track {
	position: relative;
	flex: 1;
	min-width: 0;
	height: 20px;
}

.range__rail,
.range__fill {
	position: absolute;
	top: 50%;
	height: 6px;
	border-radius: var(--radius-pill);
	transform: translateY(-50%);
}

.range__rail {
	left: 0;
	right: 0;
	background: rgba(255, 255, 255, 0.12);
}

/* Defaults match the handles' starting positions, so the track looks right
   before app.js takes over — and stays right if it never runs. */
.range__fill {
	left: 0;
	width: 100%;
	background: var(--gradient);
}

/* Both handles are real range inputs stacked on the one track. The input
   itself ignores pointer events so the track beneath stays visible; only the
   thumbs accept them. */
.range__track input[type="range"] {
	position: absolute;
	left: 0;
	top: 0;
	width: 100%;
	height: 22px;
	margin: 0;
	padding: 0;
	background: none;
	pointer-events: none;
	appearance: none;
	-webkit-appearance: none;
}

.range__track input[type="range"]:focus {
	outline: none;
}

.range__track input[type="range"]::-webkit-slider-runnable-track {
	height: 22px;
	background: none;
	border: none;
}

.range__track input[type="range"]::-moz-range-track {
	height: 22px;
	background: none;
	border: none;
}

.range__track input[type="range"]::-webkit-slider-thumb {
	width: 18px;
	height: 18px;
	border: 0;
	border-radius: 50%;
	background: #fff;
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
	cursor: grab;
	pointer-events: auto;
	appearance: none;
	-webkit-appearance: none;
	margin-top: 2px;
}

.range__track input[type="range"]::-moz-range-thumb {
	width: 18px;
	height: 18px;
	border: 0;
	border-radius: 50%;
	background: #fff;
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
	cursor: grab;
	pointer-events: auto;
}

.range__track input[type="range"]:focus-visible::-webkit-slider-thumb {
	box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.65);
}

.range__track input[type="range"]:focus-visible::-moz-range-thumb {
	box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.65);
}

.range__track input[type="range"]:active::-webkit-slider-thumb {
	cursor: grabbing;
}

/* Stack the figures above the track when there is no room beside it. */
@media (max-width: 400px) {
	.range {
		display: grid;
		grid-template-columns: auto 1fr;
		gap: var(--sp-2) var(--sp-3);
	}

	.range__track {
		grid-column: 1 / -1;
		order: 3;
	}

	.range__value:last-child {
		text-align: right;
	}
}

@media (min-width: 900px) {
	.hero--visual .hero__inner {
		grid-template-columns: 1.05fr 0.95fr;
	}
}

/* Stacked on a narrow screen, the hero copy and a five-field form add up to a
   very tall first screen. Below the split the proof points are the most
   expendable element — the form itself is the proof of intent — so they step
   aside and return once the two columns sit side by side. */
@media (max-width: 999px) {
	.hero--form {
		padding-block: calc(var(--header-h) + clamp(2rem, 1.5rem + 3vw, 3.5rem)) clamp(2.5rem, 2rem + 3vw, 4rem);
	}

	.hero--form .hero__points {
		display: none;
	}

	.hero--form .hero__inner {
		gap: var(--sp-7);
	}

	.hero--form .hero__text {
		margin-bottom: var(--sp-6);
	}
}

/* The form layout needs more room before it splits, so the card never gets
   squeezed into an unusable column. */
@media (min-width: 1000px) {
	.hero--form .hero__inner {
		grid-template-columns: 1.08fr 0.92fr;
		align-items: center;
	}

	.hero--form .hero__content {
		max-width: 36rem;
	}
}

@media (min-width: 1200px) {
	.hero--form .hero__inner {
		gap: clamp(3rem, 1rem + 5vw, 5.5rem);
	}
}

/* Floating chips are decorative and crowd a narrow screen. */
@media (max-width: 560px) {
	.hero__chip--2,
	.hero__chip--3 {
		display: none;
	}
}

/* ==========================================================================
   9. Section headers
   ========================================================================== */

.section-header {
	max-width: 44rem;
	margin-bottom: clamp(2.5rem, 1.5rem + 3vw, 4rem);
}

.section-header--center {
	margin-inline: auto;
	text-align: center;
}

.section-header__title {
	margin-bottom: var(--sp-4);
	font-size: var(--fs-h2);
}

.section-header__text {
	margin: 0;
	color: var(--text-muted);
}

/* ==========================================================================
   10. Cards and grids
   ========================================================================== */

.card-grid {
	display: grid;
	gap: var(--sp-5);
	grid-template-columns: 1fr;
}

.card {
	position: relative;
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
	background: linear-gradient(160deg, var(--ink-700), var(--ink-800));
	overflow: hidden;
	transition: transform var(--dur) var(--ease), border-color var(--dur) var(--ease),
		box-shadow var(--dur) var(--ease);
}

.card > article {
	position: relative;
	z-index: 1;
	display: flex;
	height: 100%;
	flex-direction: column;
}

/* A soft accent wash that fades in on hover. */
.card::before {
	position: absolute;
	inset: 0;
	z-index: 0;
	content: "";
	background: var(--gradient-soft);
	opacity: 0;
	pointer-events: none;
	transition: opacity var(--dur) var(--ease);
}

.card:hover,
.card:focus-within {
	border-color: rgba(var(--accent-rgb), 0.45);
	transform: translateY(-4px);
	box-shadow: var(--shadow-lg);
}

.card:hover::before,
.card:focus-within::before {
	opacity: 1;
}

.card--service,
.card--why {
	padding: var(--sp-6);
}

.card__icon {
	position: relative;
	z-index: 1;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 52px;
	height: 52px;
	margin-bottom: var(--sp-5);
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--surface-2);
	color: var(--accent-2);
	transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease),
		transform var(--dur) var(--ease);
}

.card:hover .card__icon {
	border-color: rgba(var(--accent-rgb), 0.5);
	color: var(--accent);
	transform: translateY(-2px);
}

.card__icon--sm {
	width: 42px;
	height: 42px;
	margin-bottom: var(--sp-4);
}

.card__number {
	position: absolute;
	top: var(--sp-5);
	right: var(--sp-5);
	color: var(--text-dim);
	font-size: var(--fs-xs);
	font-weight: var(--fw-heading);
	letter-spacing: 0.14em;
}

.card__title {
	margin-bottom: var(--sp-3);
	font-size: var(--fs-h3);
}

.card__link {
	color: var(--text);
	text-decoration: none;
}

.card__link:hover {
	color: var(--text);
}

/* Stretch the title link over the card so the whole surface is clickable,
   while keeping exactly one tab stop per card. */
.card__link::after {
	position: absolute;
	inset: 0;
	z-index: 3;
	content: "";
}

.card__text {
	position: relative;
	z-index: 1;
	margin-bottom: var(--sp-5);
	color: var(--text-muted);
	font-size: var(--fs-sm);
}

.card__more {
	position: relative;
	z-index: 1;
	display: inline-flex;
	align-items: center;
	gap: var(--sp-2);
	margin-top: auto;
	color: var(--accent-2);
	font-size: var(--fs-sm);
	font-weight: 600;
}

.card__more svg {
	transition: transform var(--dur) var(--ease);
}

.card:hover .card__more svg {
	transform: translateX(4px);
}

.card__media {
	position: relative;
	overflow: hidden;
	aspect-ratio: 16 / 11;
	background: var(--ink-600);
}

.card__media--short {
	aspect-ratio: 16 / 8;
}

.card__image {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform var(--dur-slow) var(--ease-out);
}

.card:hover .card__image {
	transform: scale(1.05);
}

/* Deterministic gradient stand-in when a post has no featured image. */
.card__image--placeholder {
	display: block;
	width: 100%;
	height: 100%;
	background: linear-gradient(135deg, var(--ink-600), var(--ink-500));
}

.card__image--placeholder[data-seed="1"] { background: linear-gradient(135deg, #1a1740, #101a33); }
.card__image--placeholder[data-seed="2"] { background: linear-gradient(135deg, #101f38, #14163a); }
.card__image--placeholder[data-seed="3"] { background: linear-gradient(135deg, #221a42, #0f1a2e); }
.card__image--placeholder[data-seed="4"] { background: linear-gradient(135deg, #10233a, #1b1640); }
.card__image--placeholder[data-seed="5"] { background: linear-gradient(135deg, #1c1c44, #0d1b30); }

.card__body {
	display: flex;
	flex: 1;
	flex-direction: column;
	padding: var(--sp-5);
}

.card__meta {
	margin-bottom: var(--sp-2);
	color: var(--text-dim);
	font-size: var(--fs-xs);
	font-weight: 600;
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

.tag-list {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sp-2);
	margin-bottom: var(--sp-5);
}

.tag {
	padding: 0.25rem 0.625rem;
	border: 1px solid var(--line);
	border-radius: var(--radius-pill);
	color: var(--text-muted);
	font-size: 0.75rem;
	text-decoration: none;
}

.tag-list--post {
	margin-top: var(--sp-7);
}

/* Card is hidden by the JS filter rather than removed, to keep the DOM stable. */
.card[hidden] {
	display: none;
}

.filters {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sp-2);
	margin-bottom: var(--sp-7);
}

.filters__btn {
	padding: 0.5rem 1.125rem;
	border: 1px solid var(--line);
	border-radius: var(--radius-pill);
	background: var(--surface);
	color: var(--text-muted);
	font-size: var(--fs-sm);
	font-weight: 500;
	text-decoration: none;
	transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease),
		color var(--dur) var(--ease);
}

.filters__btn:hover {
	border-color: var(--line-strong);
	color: var(--text);
}

.filters__btn.is-active {
	border-color: transparent;
	background: var(--gradient);
	color: var(--on-accent);
}

.filters__empty {
	padding: var(--sp-8) 0;
	color: var(--text-muted);
	text-align: center;
}

@media (min-width: 640px) {
	.card-grid--services,
	.card-grid--why,
	.card-grid--work,
	.card-grid--industries,
	.card-grid--posts {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (min-width: 1024px) {
	.card-grid--services,
	.card-grid--why,
	.card-grid--industries {
		grid-template-columns: repeat(3, 1fr);
	}

	.card-grid--work,
	.card-grid--posts {
		grid-template-columns: repeat(3, 1fr);
	}
}

@media (min-width: 1280px) {
	/*
	 * Deliberately not .card-grid--services: the site publishes exactly six
	 * services, and four columns leaves an unbalanced 4+2 last row. Three
	 * columns keeps every row full at 3+3.
	 */
	.card-grid--industries {
		grid-template-columns: repeat(4, 1fr);
	}
}

/* ==========================================================================
   11. Statistics
   ========================================================================== */

.stats-grid {
	display: grid;
	gap: var(--sp-5);
	grid-template-columns: repeat(2, 1fr);
}

.stat {
	padding: var(--sp-6);
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
	background: var(--surface);
	text-align: center;
}

.stat__value {
	display: flex;
	align-items: baseline;
	justify-content: center;
	margin-bottom: var(--sp-2);
	font-size: clamp(2.25rem, 1.5rem + 3vw, 3.5rem);
	font-weight: var(--fw-heading);
	letter-spacing: -0.03em;
	line-height: 1;
}

.stat__number,
.stat__suffix {
	background: var(--gradient);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
	-webkit-text-fill-color: transparent;
}

.stat__label {
	margin: 0;
	font-weight: 600;
}

.stat__note {
	margin: var(--sp-2) 0 0;
	color: var(--text-dim);
	font-size: var(--fs-xs);
}

@media (min-width: 900px) {
	.stats-grid {
		grid-template-columns: repeat(4, 1fr);
	}
}

/* ==========================================================================
   12. Process
   ========================================================================== */

.process {
	display: grid;
	gap: var(--sp-5);
	counter-reset: step;
}

.process__step {
	position: relative;
	display: flex;
	gap: var(--sp-5);
	padding: var(--sp-6);
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
	background: linear-gradient(160deg, var(--ink-700), var(--ink-800));
	transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.process__step:hover {
	border-color: rgba(var(--accent-rgb), 0.4);
	transform: translateY(-3px);
}

.process__number {
	flex: none;
	font-size: 1.75rem;
	font-weight: var(--fw-heading);
	letter-spacing: -0.02em;
	background: var(--gradient);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
	-webkit-text-fill-color: transparent;
}

.process__title {
	margin-bottom: var(--sp-2);
	font-size: var(--fs-h3);
}

.process__text {
	margin: 0;
	color: var(--text-muted);
	font-size: var(--fs-sm);
}

@media (min-width: 700px) {
	.process {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (min-width: 1100px) {
	.process {
		grid-template-columns: repeat(3, 1fr);
	}
}

/* ==========================================================================
   13. Split and compare layouts
   ========================================================================== */

.split {
	display: grid;
	gap: clamp(2rem, 1rem + 4vw, 4rem);
	align-items: center;
}

.split__frame {
	position: relative;
	overflow: hidden;
	border: 1px solid var(--line);
	border-radius: var(--radius-xl);
	aspect-ratio: 4 / 3;
	box-shadow: var(--shadow-lg);
}

.split__image {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.split__frame--pattern {
	display: grid;
	place-items: center;
	background: radial-gradient(circle at 30% 30%, rgba(var(--accent-rgb), 0.25), transparent 60%),
		linear-gradient(160deg, var(--ink-700), var(--ink-900));
}

.pattern-dots {
	position: absolute;
	inset: 0;
	background-image: radial-gradient(var(--line-strong) 1.5px, transparent 1.5px);
	background-size: 24px 24px;
	opacity: 0.5;
}

.pattern-arc {
	width: 55%;
	aspect-ratio: 1;
	border: 2px solid transparent;
	border-top-color: var(--accent);
	border-right-color: var(--accent-2);
	border-radius: 50%;
	animation: spin 22s linear infinite;
}

.split__title {
	margin-bottom: var(--sp-4);
	font-size: var(--fs-h2);
}

.split__text {
	margin-bottom: var(--sp-6);
	color: var(--text-muted);
}

.feature-list,
.check-list,
.stack-list {
	display: grid;
	gap: var(--sp-4);
	margin-bottom: var(--sp-6);
}

.feature-list__item,
.check-list li {
	display: flex;
	gap: var(--sp-3);
	align-items: flex-start;
}

.feature-list__icon,
.check-list__icon {
	flex: none;
	margin-top: 4px;
	color: var(--accent-2);
}

.feature-list__title {
	margin-bottom: var(--sp-1);
	font-size: 1rem;
}

.feature-list__text {
	margin: 0;
	color: var(--text-muted);
	font-size: var(--fs-sm);
}

.check-list {
	padding: var(--sp-6);
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
	background: var(--surface);
	margin-bottom: 0;
}

.compare {
	display: grid;
	gap: var(--sp-5);
}

.compare__col {
	padding: clamp(1.5rem, 1rem + 2vw, 2.5rem);
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
	background: var(--surface);
}

.compare__col--accent {
	border-color: rgba(var(--accent-rgb), 0.35);
	background: var(--gradient-soft);
}

.compare__title {
	margin-bottom: var(--sp-5);
	font-size: var(--fs-h3);
}

.stack-list {
	margin-bottom: 0;
}

.stack-list__item {
	padding-bottom: var(--sp-4);
	border-bottom: 1px solid var(--line);
}

.stack-list__item:last-child {
	padding-bottom: 0;
	border-bottom: 0;
}

.stack-list__title {
	margin-bottom: var(--sp-1);
	font-size: 1rem;
}

.stack-list__text {
	margin: 0;
	color: var(--text-muted);
	font-size: var(--fs-sm);
}

.intro-grid {
	display: grid;
	gap: var(--sp-5);
	align-items: start;
	margin-bottom: var(--sp-8);
}

/* The bottom margin exists to separate the copy from the logo strip. With no
   logos uploaded the grid is the last thing in the section, and the margin
   would stack onto the section padding as a dead gap. */
.intro-grid:last-child {
	margin-bottom: 0;
}

.intro-grid__title {
	margin: 0;
	font-size: var(--fs-h2);
}

.intro-grid__body {
	color: var(--text-muted);
}

.logo-strip {
	padding-top: var(--sp-7);
	border-top: 1px solid var(--line);
}

.logo-strip__label {
	margin-bottom: var(--sp-5);
	color: var(--text-dim);
	font-size: var(--fs-xs);
	font-weight: 600;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	text-align: center;
}

.logo-strip__list {
	display: flex;
	flex-wrap: wrap;
	gap: clamp(1.5rem, 1rem + 3vw, 3.5rem);
	align-items: center;
	justify-content: center;
}

.logo-strip__img {
	max-height: 34px;
	width: auto;
	filter: grayscale(1) brightness(2);
	opacity: 0.55;
	transition: opacity var(--dur) var(--ease), filter var(--dur) var(--ease);
}

.logo-strip__item:hover .logo-strip__img {
	filter: none;
	opacity: 1;
}

@media (min-width: 900px) {
	.split {
		grid-template-columns: 1fr 1fr;
	}

	.split--reverse .split__content {
		order: 2;
	}

	.compare {
		grid-template-columns: 1fr 1fr;
	}

	.intro-grid {
		grid-template-columns: 1fr 1fr;
		gap: clamp(2rem, 1rem + 4vw, 4rem);
	}
}

/* ==========================================================================
   14. Accordion and FAQ
   ========================================================================== */

.faq-layout {
	display: grid;
	gap: clamp(2rem, 1rem + 4vw, 4rem);
	align-items: start;
}

.faq-layout__head .btn {
	margin-top: var(--sp-5);
}

.accordion {
	display: grid;
	gap: var(--sp-3);
}

.accordion__item {
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--surface);
	overflow: hidden;
	transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}

.accordion__item.is-open {
	border-color: rgba(var(--accent-rgb), 0.4);
	background: var(--surface-2);
}

.accordion__heading {
	margin: 0;
	font-size: 1rem;
}

.accordion__trigger {
	display: flex;
	width: 100%;
	align-items: center;
	justify-content: space-between;
	gap: var(--sp-4);
	padding: var(--sp-5);
	border: 0;
	background: none;
	color: var(--text);
	font-size: 1.0625rem;
	font-weight: 600;
	text-align: left;
	line-height: 1.4;
}

.accordion__icon {
	position: relative;
	flex: none;
	width: 22px;
	height: 22px;
	border: 1px solid var(--line-strong);
	border-radius: 50%;
	transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease),
		transform var(--dur) var(--ease);
}

.accordion__icon::before,
.accordion__icon::after {
	position: absolute;
	top: 50%;
	left: 50%;
	content: "";
	background: currentColor;
	transform: translate(-50%, -50%);
	transition: opacity var(--dur-fast) var(--ease);
}

.accordion__icon::before {
	width: 10px;
	height: 1.5px;
}

.accordion__icon::after {
	width: 1.5px;
	height: 10px;
}

.accordion__trigger[aria-expanded="true"] .accordion__icon {
	border-color: transparent;
	background: var(--gradient);
	transform: rotate(180deg);
}

.accordion__trigger[aria-expanded="true"] .accordion__icon::after {
	opacity: 0;
}

/* Panels are open by default, so the answers are readable with JavaScript
   disabled. app.js closes them on load and takes over the toggling. */
.accordion__panel {
	display: grid;
	grid-template-rows: 1fr;
	transition: grid-template-rows var(--dur) var(--ease);
}

.accordion__panel[hidden] {
	display: grid;
	grid-template-rows: 0fr;
}

.accordion__content {
	min-height: 0;
	overflow: hidden;
	padding: 0 var(--sp-5) var(--sp-5);
	color: var(--text-muted);
	font-size: var(--fs-sm);
}

.accordion__panel[hidden] .accordion__content {
	padding-block-end: 0;
}

@media (min-width: 900px) {
	.faq-layout {
		grid-template-columns: 0.8fr 1.2fr;
	}
}

/* ==========================================================================
   15. Google reviews
   ========================================================================== */

.google-reviews {
	min-height: 4rem;
}

/* ==========================================================================
   16. CTA panel
   ========================================================================== */

.cta-panel {
	position: relative;
	overflow: hidden;
	padding: clamp(2.5rem, 1.5rem + 5vw, 5rem) clamp(1.5rem, 1rem + 4vw, 4rem);
	border: 1px solid rgba(var(--accent-rgb), 0.35);
	border-radius: var(--radius-xl);
	background: linear-gradient(150deg, rgba(var(--accent-rgb), 0.2), rgba(var(--accent-2-rgb), 0.1)), var(--ink-700);
	text-align: center;
}

.cta-panel__glow {
	position: absolute;
	z-index: 1;
	top: -60%;
	left: 50%;
	width: 70%;
	aspect-ratio: 1;
	background: radial-gradient(circle, rgba(var(--accent-rgb), 0.55), transparent 65%);
	filter: blur(70px);
	transform: translateX(-50%);
	pointer-events: none;
}

.cta-panel__content {
	position: relative;
	z-index: 2;
}

.cta-panel__title {
	margin-bottom: var(--sp-4);
	font-size: var(--fs-h2);
}

.cta-panel__text {
	max-width: 52ch;
	margin: 0 auto var(--sp-7);
	color: var(--text-muted);
}

.cta-panel__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sp-3);
	justify-content: center;
}

/* ==========================================================================
   17. Forms
   ========================================================================== */

.form-layout {
	display: grid;
	gap: clamp(2rem, 1rem + 4vw, 4rem);
	align-items: start;
}

.form-layout__title {
	font-size: var(--fs-h2);
}

.form-layout__text {
	margin-bottom: var(--sp-6);
	color: var(--text-muted);
}

.contact-list {
	display: grid;
	gap: var(--sp-5);
}

.contact-list li {
	display: flex;
	gap: var(--sp-4);
	align-items: flex-start;
}

.contact-list__icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	flex: none;
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--surface);
	color: var(--accent-2);
}

.contact-list__body {
	display: grid;
	gap: 2px;
}

.contact-list__label {
	color: var(--text-dim);
	font-size: var(--fs-xs);
	font-weight: 600;
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

.contact-list__body a {
	color: var(--text);
	text-decoration: none;
	font-weight: 500;
}

.contact-list__body a:hover {
	color: var(--accent-2);
}

.lead-form {
	padding: clamp(1.5rem, 1rem + 2vw, 2.5rem);
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
	background: linear-gradient(160deg, var(--ink-700), var(--ink-800));
	box-shadow: var(--shadow);
}

/* Off-screen rather than display:none, which some bots skip. */
.lead-form__trap {
	position: absolute !important;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

.lead-form__grid {
	display: grid;
	gap: var(--sp-4);
}

.field--full {
	grid-column: 1 / -1;
}

.field__label {
	display: block;
	margin-bottom: var(--sp-2);
	font-size: var(--fs-sm);
	font-weight: 600;
}

.field__required {
	color: var(--accent-2);
}

.field__optional {
	color: var(--text-dim);
	font-weight: 400;
}

.field__control {
	width: 100%;
	padding: 0.875rem 1rem;
	border: 1px solid var(--line-strong);
	border-radius: var(--radius);
	background: rgba(0, 0, 0, 0.25);
	color: var(--text);
	font-size: 1rem;
	transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}

.field__control::placeholder {
	color: var(--text-dim);
}

.field__control:focus {
	border-color: var(--accent);
	box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.25);
	outline: none;
}

textarea.field__control {
	min-height: 8rem;
	resize: vertical;
}

select.field__control {
	appearance: none;
	background-image: linear-gradient(45deg, transparent 50%, var(--text-muted) 50%),
		linear-gradient(135deg, var(--text-muted) 50%, transparent 50%);
	background-position: calc(100% - 20px) center, calc(100% - 14px) center;
	background-size: 6px 6px, 6px 6px;
	background-repeat: no-repeat;
	padding-right: 2.75rem;
}

select.field__control option {
	background: var(--ink-700);
	color: var(--text);
}

.field__desc {
	margin: var(--sp-2) 0 0;
	color: var(--text-dim);
	font-size: var(--fs-xs);
}

.field__error {
	margin: var(--sp-2) 0 0;
	color: #ff8a8a;
	font-size: var(--fs-xs);
	font-weight: 500;
}

.field--error .field__control {
	border-color: #ff8a8a;
}

.field__consent {
	display: flex;
	gap: var(--sp-3);
	align-items: flex-start;
	color: var(--text-muted);
	font-size: var(--fs-sm);
	cursor: pointer;
}

.field__consent input {
	width: 20px;
	height: 20px;
	flex: none;
	margin-top: 2px;
	accent-color: var(--accent);
}

/*
 * Sits in the submit button's own slot in .lead-form__actions — the two
 * are never visible together, app.js hides one exactly as it shows the
 * other — sized to match the button (same padding, same pill shape) so it
 * reads as the button itself turning into the result, not a separate
 * element appearing somewhere else on the page.
 *
 * Both need an explicit [hidden] rule: .btn and .form-status each set
 * their own `display`, and a class's display value otherwise wins the
 * cascade over the hidden attribute's own (equal-specificity) UA style,
 * leaving a "hidden" element still visible.
 */
[data-form-submit][hidden] {
	display: none;
}

.form-status {
	display: inline-flex;
	align-items: center;
	max-width: 100%;
	padding: 0.875rem 1.5rem;
	border: 1px solid var(--line);
	border-radius: var(--radius-pill);
	font-size: var(--fs-sm);
	font-weight: 600;
	line-height: 1.2;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	animation: pixal-status-in 260ms var(--ease);
}

.form-status[hidden] {
	display: none;
}

@keyframes pixal-status-in {
	from {
		opacity: 0;
		transform: translateY(4px);
	}

	to {
		opacity: 1;
		transform: translateY(0);
	}
}

@media (prefers-reduced-motion: reduce) {
	.form-status {
		animation: none;
	}
}

.form-status--success {
	border-color: rgba(52, 211, 153, 0.45);
	background: rgba(52, 211, 153, 0.12);
	color: #6ee7b7;
}

.form-status--error {
	border-color: rgba(255, 138, 138, 0.45);
	background: rgba(255, 138, 138, 0.1);
	color: #ff8a8a;
}

.lead-form__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sp-4);
	align-items: center;
	margin-top: var(--sp-6);
}

.lead-form__note {
	margin: 0;
	color: var(--text-dim);
	font-size: var(--fs-xs);
}

.lead-form.is-sending [data-form-submit] {
	opacity: 0.7;
	pointer-events: none;
}

.search-form {
	display: flex;
	gap: var(--sp-2);
	margin-bottom: var(--sp-7);
}

.search-form__input {
	flex: 1;
	/* A flex item defaults to min-width:auto, which stops it shrinking below
	   its intrinsic width and pushed the button off-screen at 320px. */
	min-width: 0;
	padding: 0.875rem 1rem;
	border: 1px solid var(--line-strong);
	border-radius: var(--radius);
	background: rgba(0, 0, 0, 0.25);
	color: var(--text);
}

@media (min-width: 560px) {
	.lead-form__grid {
		grid-template-columns: 1fr 1fr;
	}

	.field--half {
		grid-column: span 1;
	}
}

@media (min-width: 900px) {
	.form-layout {
		grid-template-columns: 0.85fr 1.15fr;
	}
}

/* ==========================================================================
   18. Inner page furniture
   ========================================================================== */

.page-hero {
	position: relative;
	overflow: hidden;
	padding-block: calc(var(--header-h) + clamp(2.5rem, 1.5rem + 4vw, 4.5rem)) clamp(2.5rem, 1.5rem + 4vw, 4.5rem);
	background: var(--ink-900);
}

.page-hero__inner {
	position: relative;
	max-width: 52rem;
}

.page-hero--center .page-hero__inner {
	margin-inline: auto;
	text-align: center;
}

.page-hero--center .breadcrumbs ol {
	justify-content: center;
}

.page-hero__title {
	margin-bottom: var(--sp-4);
	font-size: var(--fs-h1);
}

.page-hero__text {
	margin: 0;
	color: var(--text-muted);
	font-size: 1.0625rem;
}

.page-hero__image {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	opacity: 0.25;
}

.page-hero--image::after {
	position: absolute;
	inset: 0;
	content: "";
	background: linear-gradient(180deg, rgba(6, 7, 15, 0.6), var(--ink-800));
}

.page-hero--image .page-hero__inner {
	z-index: 1;
}

.breadcrumbs {
	margin-bottom: var(--sp-5);
	font-size: var(--fs-xs);
}

.breadcrumbs ol {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sp-2);
	align-items: center;
}

.breadcrumbs li:not(:last-child)::after {
	margin-left: var(--sp-2);
	content: "/";
	color: var(--text-dim);
}

.breadcrumbs a {
	color: var(--text-muted);
	text-decoration: none;
}

.breadcrumbs a:hover {
	color: var(--accent-2);
}

.breadcrumbs [aria-current] {
	color: var(--text-dim);
}

.project-meta {
	display: grid;
	gap: var(--sp-5);
	padding: var(--sp-6);
	margin-top: clamp(2rem, 1.4rem + 2vw, 3rem);
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
	background: var(--surface);
}

.project-meta__item dt {
	margin-bottom: var(--sp-1);
	color: var(--text-dim);
	font-size: var(--fs-xs);
	font-weight: 600;
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

.project-meta__item dd {
	margin: 0;
	font-weight: 500;
}

.story-block {
	margin-bottom: var(--sp-8);
}

.story-block__title {
	margin-bottom: var(--sp-4);
	font-size: var(--fs-h3);
	color: var(--accent-2);
}

.metric-row {
	display: grid;
	gap: var(--sp-4);
	grid-template-columns: repeat(2, 1fr);
	margin-top: clamp(1.5rem, 1rem + 1.5vw, 2.5rem);
	padding: var(--sp-6);
	border: 1px solid rgba(var(--accent-rgb), 0.3);
	border-radius: var(--radius-lg);
	background: var(--gradient-soft);
}

.metric {
	text-align: center;
}

.metric__value {
	display: block;
	font-size: clamp(1.75rem, 1.2rem + 2vw, 2.5rem);
	font-weight: var(--fw-heading);
	letter-spacing: -0.02em;
}

.metric__label {
	color: var(--text-muted);
	font-size: var(--fs-xs);
}

.gallery-grid {
	display: grid;
	gap: var(--sp-4);
}

.gallery-grid__img {
	width: 100%;
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
}

.result-list {
	display: grid;
	gap: var(--sp-5);
	margin-bottom: var(--sp-7);
}

.result {
	padding-bottom: var(--sp-5);
	border-bottom: 1px solid var(--line);
}

.result__type {
	margin-bottom: var(--sp-1);
	color: var(--accent-2);
	font-size: var(--fs-xs);
	font-weight: 600;
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

.result__title {
	margin-bottom: var(--sp-2);
	font-size: var(--fs-h3);
}

.result__title a {
	color: var(--text);
	text-decoration: none;
}

.result__title a:hover {
	color: var(--accent-2);
}

.result__text {
	margin: 0;
	color: var(--text-muted);
	font-size: var(--fs-sm);
}

.empty-state {
	padding: var(--sp-9) 0;
	color: var(--text-muted);
	text-align: center;
}

.pagination {
	margin-top: var(--sp-9);
	padding-top: var(--sp-8);
	border-top: 1px solid var(--line);
	text-align: center;
}

.pagination__summary {
	margin: 0 0 var(--sp-5);
	color: var(--text-dim);
	font-size: var(--fs-xs);
	font-weight: var(--fw-heading);
	letter-spacing: 0.1em;
	text-transform: uppercase;
}

.pagination ul {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sp-2);
	align-items: center;
	justify-content: center;
	padding-left: 0;
	list-style: none;
}

.pagination__item {
	display: flex;
}

.pagination .page-numbers {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--sp-2);
	min-width: 44px;
	height: 44px;
	padding-inline: var(--sp-3);
	border: 1px solid var(--line);
	border-radius: var(--radius-pill);
	background: var(--surface);
	color: var(--text-muted);
	font-size: var(--fs-sm);
	font-weight: 600;
	text-decoration: none;
	transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease),
		color var(--dur) var(--ease), transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.pagination a:hover,
.pagination a:focus-visible {
	border-color: rgba(var(--accent-rgb), 0.45);
	background: var(--surface-2);
	color: var(--text);
	transform: translateY(-2px);
}

/* The current page is the one moment this list borrows the brand gradient —
   everything else stays neutral so the active page is unmistakable at a
   glance rather than one pill among several similar ones. */
.pagination .current {
	border-color: transparent;
	background: var(--gradient);
	box-shadow: 0 8px 20px -10px rgba(var(--accent-2-rgb), 0.85);
	color: var(--on-accent);
}

.pagination .dots {
	border-color: transparent;
	background: transparent;
	color: var(--text-dim);
}

.pagination__item--arrow a {
	padding-inline: var(--sp-4);
}

.pagination__icon svg {
	transition: transform var(--dur) var(--ease);
}

.pagination__item--arrow a:hover .pagination__icon svg {
	transform: translateX(3px);
}

.pagination__item--arrow:first-child .pagination__icon svg {
	transform: scaleX(-1);
}

.pagination__item--arrow:first-child a:hover .pagination__icon svg {
	transform: scaleX(-1) translateX(3px);
}

@media (max-width: 480px) {
	.pagination__text {
		display: none;
	}

	.pagination__item--arrow a {
		padding-inline: 0;
		width: 44px;
	}
}

.post-nav {
	display: grid;
	gap: var(--sp-4);
	margin-top: var(--sp-9);
	padding-top: var(--sp-7);
	border-top: 1px solid var(--line);
}

.post-nav__link {
	padding: var(--sp-5);
	border: 1px solid var(--line);
	border-radius: var(--radius);
	text-decoration: none;
	transition: border-color var(--dur) var(--ease);
}

.post-nav__link:hover {
	border-color: rgba(var(--accent-rgb), 0.4);
}

.post-nav__label {
	display: block;
	margin-bottom: var(--sp-1);
	color: var(--text-dim);
	font-size: var(--fs-xs);
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

.post-nav__title {
	color: var(--text);
	font-weight: 600;
}

.post-nav__link--next {
	text-align: right;
}

.error-page {
	position: relative;
	padding-block: clamp(3rem, 2rem + 5vw, 6rem);
	text-align: center;
}

.error-page__code {
	margin: 0 0 var(--sp-4);
	font-size: clamp(5rem, 3rem + 12vw, 10rem);
	font-weight: var(--fw-heading);
	line-height: 1;
	letter-spacing: -0.05em;
	background: var(--gradient);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
	-webkit-text-fill-color: transparent;
	opacity: 0.9;
}

.error-page__title {
	font-size: var(--fs-h2);
}

.error-page__text {
	max-width: 48ch;
	margin: 0 auto var(--sp-7);
	color: var(--text-muted);
}

.error-page__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sp-3);
	justify-content: center;
	margin-bottom: var(--sp-9);
}

.error-page__subtitle {
	font-size: var(--fs-h3);
}

.link-list {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sp-3);
	justify-content: center;
}

.link-list a {
	padding: 0.5rem 1rem;
	border: 1px solid var(--line);
	border-radius: var(--radius-pill);
	color: var(--text-muted);
	font-size: var(--fs-sm);
	text-decoration: none;
}

.link-list a:hover {
	border-color: rgba(var(--accent-rgb), 0.4);
	color: var(--text);
}

@media (min-width: 700px) {
	.project-meta {
		grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
	}

	.metric-row {
		grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
	}

	.gallery-grid {
		grid-template-columns: repeat(2, 1fr);
	}

	.post-nav {
		grid-template-columns: 1fr 1fr;
	}
}

/* ==========================================================================
   19. Footer
   ========================================================================== */

.site-footer {
	--footer-brand-h: 42px;
	position: relative;
	margin-top: var(--sp-10);
	padding-block: var(--sp-9) var(--sp-6);
	border-top: 1px solid var(--line);
	background: var(--ink-900);
}

.footer-cta {
	display: grid;
	gap: var(--sp-5);
	align-items: center;
	margin-bottom: var(--sp-9);
	padding: clamp(1.75rem, 1rem + 3vw, 3rem);
	border: 1px solid rgba(var(--accent-rgb), 0.3);
	border-radius: var(--radius-xl);
	background: var(--gradient-soft);
}

.footer-cta__title {
	margin-bottom: var(--sp-2);
	font-size: var(--fs-h2);
}

.footer-cta p {
	margin: 0;
	color: var(--text-muted);
}

.footer-grid {
	display: grid;
	gap: var(--sp-7);
	padding-bottom: var(--sp-8);
}

/*
 * Column headings sit on the same optical line as the logo wordmark. Both
 * boxes are given the same height and centre their own contents, so the
 * alignment holds whether the logo is the text mark or an uploaded image.
 */
.site-footer .site-logo {
	min-height: var(--footer-brand-h);
}

.footer-col__title {
	margin-bottom: var(--sp-5);
	font-size: 0.9375rem;
	font-weight: var(--fw-heading);
	letter-spacing: 0.06em;
	text-transform: uppercase;
}

@media (min-width: 700px) {
	.footer-col__title {
		display: flex;
		align-items: center;
		min-height: var(--footer-brand-h);
	}
}

.footer-col__text {
	margin-block: var(--sp-5);
	color: var(--text-muted);
	font-size: var(--fs-sm);
}

.footer-menu,
.footer-contact {
	display: grid;
	gap: var(--sp-3);
}

.footer-menu a {
	color: var(--text-muted);
	font-size: var(--fs-sm);
	text-decoration: none;
}

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

.footer-contact li {
	display: flex;
	gap: var(--sp-3);
	align-items: flex-start;
	color: var(--text-muted);
	font-size: var(--fs-sm);
}

/*
 * A flex item's default min-width is auto, not 0 — it will not shrink
 * below its own content's width, which is what let the longer address
 * push past the edge on a narrow screen instead of wrapping. This is the
 * text/link half of the row (the icon next to it is flex: none already).
 */
.footer-contact li > :last-child {
	min-width: 0;
	overflow-wrap: break-word;
}

.footer-contact svg {
	flex: none;
	margin-top: 3px;
	color: var(--accent-2);
}

.footer-contact a {
	color: var(--text-muted);
	text-decoration: none;
}

.footer-contact a:hover {
	color: var(--text);
}

.social-list {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sp-2);
}

.social-list__link {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	border: 1px solid rgba(255, 255, 255, 0.7);
	border-radius: var(--radius);
	background: var(--surface);
	color: #fff;
	text-decoration: none;
	transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease),
		box-shadow var(--dur) var(--ease);
}

.social-list__link svg {
	position: relative;
	z-index: 1;
	flex: none;
}

/*
 * On hover the flat white ring is replaced by the same travelling two-colour
 * border used on the hero form card (registered once, reused here), and the
 * icon itself pulses between the gradient's two stops rather than jumping to
 * a single flat colour — background stays as it is, so this reads as the
 * icon "catching" the brand colour rather than the button being filled in.
 */
a.social-list__link:hover,
a.social-list__link:focus-visible {
	border-color: transparent;
	transform: translateY(-2px);
	box-shadow: 0 10px 22px -12px rgba(var(--accent-2-rgb), 0.7);
}

a.social-list__link:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 2px;
}

a.social-list__link:hover svg,
a.social-list__link:focus-visible svg {
	animation: pixal-social-pulse 1.4s ease-in-out infinite alternate;
}

a.social-list__link::before,
a.social-list__link::after {
	position: absolute;
	inset: 0;
	z-index: 0;
	content: "";
	padding: 1.5px;
	border-radius: inherit;
	opacity: 0;
	pointer-events: none;
	-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
	mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
	-webkit-mask-composite: xor;
	mask-composite: exclude;
	transition: opacity var(--dur) var(--ease);
}

a.social-list__link::before {
	background: conic-gradient(
		from var(--pixal-arc),
		transparent 0deg,
		rgba(var(--accent-rgb), 0.25) 30deg,
		var(--accent) 90deg,
		rgba(var(--accent-rgb), 0.25) 150deg,
		transparent 180deg,
		transparent 360deg
	);
}

a.social-list__link::after {
	background: conic-gradient(
		from var(--pixal-arc),
		transparent 0deg,
		transparent 180deg,
		rgba(var(--accent-2-rgb), 0.25) 210deg,
		var(--accent-2) 270deg,
		rgba(var(--accent-2-rgb), 0.25) 330deg,
		transparent 360deg
	);
}

a.social-list__link:hover::before,
a.social-list__link:focus-visible::before {
	opacity: 1;
	animation: pixal-arc-cw 3.2s linear infinite;
}

a.social-list__link:hover::after,
a.social-list__link:focus-visible::after {
	opacity: 1;
	animation: pixal-arc-ccw 3.2s linear infinite;
}

@keyframes pixal-social-pulse {
	from {
		color: var(--accent);
	}
	to {
		color: var(--accent-2);
	}
}

@media (prefers-reduced-motion: reduce) {
	a.social-list__link:hover::before,
	a.social-list__link:focus-visible::before,
	a.social-list__link:hover::after,
	a.social-list__link:focus-visible::after {
		animation: none;
	}

	a.social-list__link:hover svg,
	a.social-list__link:focus-visible svg {
		animation: none;
		color: var(--accent-2);
	}
}


/*
 * A large, faint brand wordmark between the footer columns and the
 * copyright line — decorative only (aria-hidden in the markup; the real
 * brand name is already announced by the logo). Outline only, no fill, so
 * it reads as a watermark rather than a second heading. Sized in vw rather
 * than a fixed rem scale as a starting point so the text-to-container
 * ratio is already close at every width; app.js then measures the actual
 * rendered text and scales it to match the container width exactly —
 * bigger on a wide desktop, smaller on a phone, but always spanning the
 * same proportion of the row rather than sitting at one fixed size.
 */
.footer-watermark {
	position: relative;
	margin: var(--sp-8) 0 0;
	/* Fixed, not fluid — the same 20px above and below the text at any
	   width, phone or desktop, rather than the section rhythm's usual
	   scaling spacing. */
	padding-block: 20px;
	border-bottom: 1px solid var(--line);
	overflow: hidden;
	color: transparent;
	font-family: var(--font-sans);
	/* 600 is the heaviest weight this theme actually loads (see fonts.css)
	   — 800 would just fall back to a synthesised bold, a different and
	   less predictable shape to measure against for the fit script below. */
	font-weight: 600;
	/*
	 * A vw-based estimate, not an exact fit: how much width "Pixal Media"
	 * actually needs at a given font-size depends on the browser's own font
	 * metrics, which varies enough that a fixed ratio guessed here could
	 * still clip on some combination of font and width. app.js measures the
	 * real rendered width after this and rescales it to match the
	 * container exactly, so this value only has to be a reasonable
	 * starting point — not exact. On a page with JS disabled this is what
	 * shows, so it still errs a little small rather than risk overflow
	 * with no safety net behind it.
	 */
	font-size: clamp(2rem, 11vw, 11rem);
	line-height: 1;
	letter-spacing: -0.02em;
	text-align: center;
	white-space: nowrap;
	-webkit-text-stroke: 1.5px rgba(var(--accent-rgb), 0.4);
	user-select: none;
}

.footer-watermark::before {
	content: "";
	position: absolute;
	left: 50%;
	bottom: -35%;
	width: 55%;
	height: 90%;
	transform: translateX(-50%);
	background: radial-gradient(ellipse at center, rgba(var(--accent-2-rgb), 0.3), transparent 70%);
	filter: blur(50px);
	pointer-events: none;
}

.footer-bottom {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sp-4);
	align-items: center;
	justify-content: space-between;
	padding-top: var(--sp-6);
}

.footer-bottom__copy {
	margin: 0;
	color: var(--text-dim);
	font-size: var(--fs-xs);
}

.footer-legal__menu {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sp-5);
}

.footer-legal a {
	color: var(--text-dim);
	font-size: var(--fs-xs);
	text-decoration: none;
}

.footer-legal a:hover {
	color: var(--text);
}

/* Below the width the columns stack, "space-between" leaves a single-item
   row sitting at the left edge rather than the between-two-items gap it
   was written for — centre both rows explicitly instead. */
@media (max-width: 699px) {
	.footer-bottom {
		justify-content: center;
		text-align: center;
	}

	.footer-legal__menu {
		justify-content: center;
	}
}

/* Same right offset and diameter as .side-rail__link (the contact icons
   above it), so the button reads as the last item in that same column
   rather than an unrelated floating element. */
.to-top {
	position: fixed;
	right: var(--sp-4);
	bottom: var(--sp-5);
	z-index: 40;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 38px;
	height: 38px;
	border: 1px solid var(--line-strong);
	border-radius: 50%;
	background: rgba(15, 17, 32, 0.9);
	backdrop-filter: blur(10px);
	-webkit-backdrop-filter: blur(10px);
	color: var(--text);
	box-shadow: var(--shadow);
	transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.to-top[hidden] {
	display: none;
}

.to-top:hover {
	border-color: rgba(var(--accent-rgb), 0.5);
	transform: translateY(-3px);
}

@media (max-width: 480px) {
	.to-top {
		right: var(--sp-3);
		width: 34px;
		height: 34px;
	}
}

@media (min-width: 700px) {
	.footer-cta {
		grid-template-columns: 1fr auto;
	}

	.footer-grid {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (min-width: 1024px) {
	.footer-grid {
		grid-template-columns: 1.6fr 1fr 1fr 1.3fr;
		gap: var(--sp-8);
	}
}

/* ==========================================================================
   20. Prose (content written in the editor)
   ========================================================================== */

.prose {
	color: var(--text-muted);
}

.prose > * + * {
	margin-top: var(--sp-5);
}

.prose h2,
.prose h3,
.prose h4 {
	margin-top: var(--sp-8);
	margin-bottom: var(--sp-3);
	color: var(--text);
}

.prose h2 {
	font-size: var(--fs-h3);
}

.prose > :first-child {
	margin-top: 0;
}

.prose a {
	color: var(--accent-2);
}

.prose strong {
	color: var(--text);
}

.prose ul,
.prose ol {
	padding-left: var(--sp-5);
}

.prose ul {
	list-style: disc;
}

.prose ol {
	list-style: decimal;
}

.prose li + li {
	margin-top: var(--sp-2);
}

.prose blockquote {
	margin: var(--sp-6) 0;
	padding: var(--sp-5) var(--sp-6);
	border-left: 3px solid var(--accent);
	border-radius: 0 var(--radius) var(--radius) 0;
	background: var(--surface);
	color: var(--text);
	font-size: 1.0625rem;
}

.prose img,
.prose figure {
	border-radius: var(--radius-lg);
}

.prose figcaption {
	margin-top: var(--sp-2);
	color: var(--text-dim);
	font-size: var(--fs-xs);
	text-align: center;
}

.prose code {
	padding: 0.15em 0.4em;
	border-radius: 4px;
	background: var(--surface-2);
	font-size: 0.9em;
}

.prose pre {
	padding: var(--sp-5);
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--ink-900);
	overflow-x: auto;
}

.prose table {
	width: 100%;
	border-collapse: collapse;
	font-size: var(--fs-sm);
}

.prose th,
.prose td {
	padding: var(--sp-3);
	border-bottom: 1px solid var(--line);
	text-align: left;
}

/* Wide content inside prose may scroll on its own; the page never does. */
.prose .wp-block-table,
.prose figure.wp-block-table {
	overflow-x: auto;
}

/* ==========================================================================
   21. Motion and reveals
   ========================================================================== */

/* Elements start visible. app.js adds .reveal-ready only when it is safe to
   animate, so no content is ever hidden by CSS that JavaScript failed to undo. */
.reveal-ready .reveal {
	opacity: 0;
	transform: translateY(22px);
	transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
}

.reveal-ready .reveal.is-visible {
	opacity: 1;
	transform: none;
}

/* Cards in a grid cascade slightly. */
.reveal-ready .card-grid > .reveal:nth-child(2),
.reveal-ready .process > .reveal:nth-child(2) {
	transition-delay: 70ms;
}

.reveal-ready .card-grid > .reveal:nth-child(3),
.reveal-ready .process > .reveal:nth-child(3) {
	transition-delay: 140ms;
}

.reveal-ready .card-grid > .reveal:nth-child(4),
.reveal-ready .process > .reveal:nth-child(4) {
	transition-delay: 210ms;
}

@keyframes spin {
	to {
		transform: rotate(360deg);
	}
}

@keyframes pulse {
	0%,
	100% {
		transform: scale(1);
		opacity: 0.9;
	}
	50% {
		transform: scale(1.08);
		opacity: 1;
	}
}

@keyframes float {
	0%,
	100% {
		transform: translateY(0);
	}
	50% {
		transform: translateY(-10px);
	}
}

/* Respect a stated preference for reduced motion: stop the ambient animation,
   collapse transitions, and make sure everything is visible. */
@media (prefers-reduced-motion: reduce) {
	html {
		scroll-behavior: auto;
	}

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

	.reveal-ready .reveal {
		opacity: 1 !important;
		transform: none !important;
	}

	/* The travelling arcs would otherwise freeze wherever the near-zero
	   duration left them. A calm static two-tone edge instead. */
	.hero-form-card::before {
		background: linear-gradient(140deg, var(--accent), transparent 55%);
	}

	.hero-form-card::after {
		background: linear-gradient(320deg, var(--accent-2), transparent 55%);
	}
}

/* ==========================================================================
   22. Utilities
   ========================================================================== */

.alignwide {
	max-width: var(--container);
}

.alignfull {
	max-width: none;
}

.alignleft {
	float: left;
	margin: 0 var(--sp-5) var(--sp-4) 0;
}

.alignright {
	float: right;
	margin: 0 0 var(--sp-4) var(--sp-5);
}

.aligncenter {
	margin-inline: auto;
}

.wp-caption {
	max-width: 100%;
}

.page-links {
	display: flex;
	gap: var(--sp-2);
	margin-top: var(--sp-6);
}

/* Print: drop the dark ground and the chrome. */
@media print {
	body {
		background: #fff;
		color: #000;
	}

	.site-header,
	.mobile-nav,
	.site-footer,
	.to-top,
	.filters,
	.hero__bg,
	.page-hero__bg {
		display: none !important;
	}

	.reveal {
		opacity: 1 !important;
		transform: none !important;
	}
}

/* Laptop refinements for the hero form: the phone shell and the slider match
   the tighter control height so the card clears a 768px-tall screen. */
@media (min-width: 1000px) {
	.phone-group__code,
	.phone-group__display {
		padding-block: 0.5rem;
		font-size: var(--fs-sm);
	}

	.range__value {
		font-size: var(--fs-xs);
		min-width: 4rem;
	}
}

/* The 404 has no hero band of its own, so it reserves the header height here
   rather than sliding underneath the overlay header. */
.section--404 {
	position: relative;
	/* The nebula glows are deliberately oversized and rely on their parent
	   clipping them — .hero normally provides that, but this section isn't
	   a .hero, so it has to declare it itself or the glows push the page
	   wider than the viewport. */
	overflow: hidden;
	padding-block-start: calc(var(--header-h) + var(--section-y));
}

/*
 * Below 420px the bar has to carry the logo, both contact icons and the menu
 * toggle inside about 250px. Everything tightens except the toggle, which stays
 * a 44px touch target — it is the control people actually reach for.
 */
@media (max-width: 420px) {
	.site-header__inner {
		gap: var(--sp-2);
	}

	.site-logo {
		gap: var(--sp-2);
	}

	.site-logo__name {
		font-size: 0.9375rem;
	}

	.site-header__actions {
		gap: 0.375rem;
	}

	.site-header__quick {
		gap: 0.25rem;
	}

	.icon-btn {
		width: 30px;
		height: 30px;
	}
}

/* ==========================================================================
   23. Platform logo strip
   ========================================================================== */

.section--platforms {
	padding-block: clamp(2.5rem, 1.8rem + 2.6vw, 4rem);
	overflow: hidden;
}

.section--platforms .section-header {
	margin-bottom: clamp(1.75rem, 1.2rem + 2vw, 2.75rem);
}

/*
 * Full-bleed strip. The mask fades both ends into the page background so logos
 * enter and leave rather than being chopped off at a hard edge.
 */
.marquee {
	position: relative;
	width: 100%;
	overflow: hidden;
	-webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
	mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
	--per-view: 2;
}

.marquee__track {
	display: flex;
	width: max-content;
	animation: pixal-marquee var(--marquee-duration, 40s) linear infinite;
	/* Promote to its own layer so the loop stays smooth under load. */
	will-change: transform;
}

/* Hovering anywhere on the strip pauses it. Pausing the animation rather than
   stopping it means it resumes from the same point, with no jump. */
.marquee:hover .marquee__track,
.marquee:focus-within .marquee__track {
	animation-play-state: paused;
}

.marquee__group {
	display: flex;
	flex: 0 0 auto;
	align-items: center;
	margin: 0;
	padding: 0;
	list-style: none;
}

.marquee__item {
	display: flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	/* Width is set by app.js from the container, so every logo occupies an
	   identical slot no matter its own aspect ratio. */
	width: var(--marquee-item, 190px);
	padding-inline: clamp(0.75rem, 0.5rem + 1vw, 1.5rem);
}

/*
 * Each logo sits in an identical box and is scaled to fit inside it, so a wide
 * wordmark and a compact glyph carry the same visual weight. The source files
 * are cropped tight to their artwork, so the box is the only thing deciding
 * size — no logo is quietly shrunk by its own transparent margin.
 *
 * Brand colours are kept as they are. The WordPress logotype is the official
 * white variant, because the black one is the only mark in the set that would
 * be invisible against a dark strip.
 */
.marquee__logo {
	width: 128px;
	height: 40px;
	object-fit: contain;
	opacity: 0.78;
	transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}

.marquee__item:hover .marquee__logo {
	opacity: 1;
	transform: scale(1.06);
}

@keyframes pixal-marquee {
	to {
		/* Exactly one group's width — the second copy lands where the first
		   started, so the seam is invisible. */
		transform: translateX(calc(var(--marquee-shift, 50%) * -1));
	}
}

@media (min-width: 640px) {
	.marquee {
		--per-view: 3;
	}
}

@media (min-width: 1024px) {
	.marquee {
		--per-view: 5;
	}

	.marquee__logo {
		width: 150px;
		height: 46px;
	}
}

/* Without JavaScript there is one static row; let it scroll by hand instead of
   clipping. Same for anyone who has asked for less motion. */
.marquee:not([data-marquee-ready]) {
	overflow-x: auto;
	scrollbar-width: none;
}

.marquee:not([data-marquee-ready])::-webkit-scrollbar {
	display: none;
}

@media (prefers-reduced-motion: reduce) {
	.marquee {
		overflow-x: auto;
	}

	.marquee__track {
		animation: none;
	}
}

/* ==========================================================================
   24. Pricing packages
   ========================================================================== */

.package-grid {
	display: grid;
	gap: var(--sp-5);
	grid-template-columns: 1fr;
	align-items: stretch;
}

.package {
	position: relative;
	height: 100%;
	padding: clamp(1.5rem, 1.2rem + 1vw, 2rem);
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
	background: linear-gradient(160deg, var(--ink-700), var(--ink-800));
	transition: transform var(--dur) var(--ease), border-color var(--dur) var(--ease),
		box-shadow var(--dur) var(--ease);
}

.package > article {
	display: flex;
	height: 100%;
	flex-direction: column;
}

.package:hover {
	border-color: rgba(var(--accent-rgb), 0.4);
	transform: translateY(-4px);
	box-shadow: var(--shadow-lg);
}

/* The highlighted tier. Marked by carrying a badge, not by its position. */
.package--featured {
	border-color: rgba(var(--accent-rgb), 0.55);
	background: linear-gradient(160deg, rgba(var(--accent-rgb), 0.1), var(--ink-800) 55%);
	box-shadow: var(--shadow-lg);
}

.package__badge {
	position: absolute;
	z-index: 2;
	top: 0;
	left: 50%;
	margin: 0;
	padding: 0.3rem 0.85rem;
	transform: translate(-50%, -50%);
	white-space: nowrap;
	box-shadow: 0 8px 20px -10px rgba(var(--accent-2-rgb), 0.95);
	border-radius: var(--radius-pill);
	background: var(--gradient);
	color: var(--on-accent);
	font-size: 0.6875rem;
	font-weight: var(--fw-heading);
	letter-spacing: 0.06em;
	text-transform: uppercase;
}

.package__name {
	margin-bottom: var(--sp-1);
	font-size: var(--fs-h3);
}

.package__tagline {
	margin-bottom: var(--sp-4);
	color: var(--text-muted);
	font-size: var(--fs-sm);
	line-height: 1.6;
}

@media (min-width: 700px) {
	.package__tagline {
		min-height: calc(3 * 1.6em);
	}
}

/* Four narrow columns wrap the taglines further, so the floor rises with them. */
@media (min-width: 1200px) {
	.package__tagline {
		min-height: calc(4 * 1.6em);
	}
}

.package__price {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: var(--sp-2);
	margin-bottom: var(--sp-5);
	padding-bottom: var(--sp-5);
	border-bottom: 1px solid var(--line);
}

.package__amount {
	font-size: clamp(1.75rem, 1.4rem + 1.4vw, 2.25rem);
	font-weight: var(--fw-heading);
	letter-spacing: -0.02em;
	background: var(--gradient);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
	-webkit-text-fill-color: transparent;
}

.package__period {
	flex-basis: 100%;
	color: var(--text-dim);
	font-size: var(--fs-sm);
}

.package__cta {
	margin-bottom: var(--sp-5);
}

.package__features {
	display: grid;
	gap: var(--sp-3);
	margin-bottom: var(--sp-4);
}

.package__feature {
	display: flex;
	gap: var(--sp-3);
	align-items: flex-start;
	color: var(--text-muted);
	font-size: var(--fs-sm);
	line-height: 1.5;
}

.package__tick {
	flex: none;
	margin-top: 3px;
	color: var(--accent-2);
}

/* Excluded items are dimmed and carry a different mark, so the distinction
   does not rest on colour alone. */
.package__feature--muted {
	color: var(--text-dim);
}

.package__feature--muted .package__tick {
	color: var(--text-dim);
	transform: rotate(45deg);
}

.package__note {
	margin-top: auto;
	padding-top: var(--sp-4);
	border-top: 1px solid var(--line);
	color: var(--text-dim);
	font-size: var(--fs-xs);
}

.package-note {
	margin-top: var(--sp-7);
	color: var(--text-muted);
	font-size: var(--fs-sm);
	text-align: center;
}

@media (min-width: 700px) {
	.package-grid {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (min-width: 1200px) {
	.package-grid {
		grid-template-columns: repeat(4, 1fr);
		gap: var(--sp-4);
	}
}

/* ==========================================================================
   25. Dialling code listbox
   ========================================================================== */

/*
 * A native <option> cannot hold an image, so the picker is rebuilt as a
 * listbox once JavaScript is available. The <select> stays put as the value
 * the form submits — it is simply moved out of the tab order.
 */
.phone-group__select--listbox .phone-group__code {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	border: 0;
	opacity: 0;
	pointer-events: none;
}

.phone-group__button {
	display: flex;
	align-items: center;
	padding: 0;
	border: 0;
	background: none;
	cursor: pointer;
}

.phone-group__select--listbox .phone-group__display {
	display: flex;
	pointer-events: none;
}

.dial-list {
	position: absolute;
	top: calc(100% + 8px);
	left: 0;
	z-index: 40;
	width: max-content;
	min-width: 264px;
	max-height: 17rem;
	margin: 0;
	padding: var(--sp-2);
	border: 1px solid var(--line-strong);
	border-radius: var(--radius);
	background: var(--ink-700);
	box-shadow: var(--shadow-lg);
	overflow-y: auto;
	overscroll-behavior: contain;
	list-style: none;
}

.dial-list[hidden] {
	display: none;
}

.dial-list:focus {
	outline: none;
}

.dial-list__row {
	display: flex;
	align-items: center;
	gap: var(--sp-3);
	padding: 0.5rem 0.625rem;
	border-radius: var(--radius-sm);
	color: var(--text-muted);
	font-size: var(--fs-sm);
	cursor: pointer;
	white-space: nowrap;
}

/* One highlight style for both pointer hover and keyboard navigation, so the
   two never disagree about which row is current. */
.dial-list__row.is-active {
	background: rgba(var(--accent-rgb), 0.14);
	color: var(--text);
}

.dial-list__row[aria-selected="true"] {
	color: var(--text);
	font-weight: var(--fw-medium);
}

.dial-list__flag {
	display: inline-flex;
	flex: none;
}

.dial-list__flag .flag {
	width: 20px;
	height: 14px;
	border-radius: 2px;
	box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22);
}

.dial-list__name {
	flex: 1;
}

.dial-list__dial {
	flex: none;
	color: var(--accent-2);
	font-variant-numeric: tabular-nums;
}

.dial-list__row.is-active .dial-list__dial {
	color: var(--accent);
}

/* The shell no longer clips its children — that would cut off the dropdown —
   so the inner edges are rounded individually instead. */
.phone-group__select {
	border-radius: var(--radius) 0 0 var(--radius);
}

.lead-form--compact .phone-group__number {
	border-radius: 0 var(--radius) var(--radius) 0;
}

/* ==========================================================================
   26. Comments
   ========================================================================== */

/*
 * WordPress renders the comment form with a cols attribute, which made the
 * textarea wider than the page at 320px. Everything here is shrinkable and
 * full width instead.
 */
.comments-area {
	margin-top: var(--sp-9);
	padding-top: var(--sp-7);
	border-top: 1px solid var(--line);
}

.comments-title,
.comment-reply-title {
	margin-bottom: var(--sp-5);
	font-size: var(--fs-h3);
}

.comment-list {
	display: grid;
	gap: var(--sp-5);
	margin-bottom: var(--sp-8);
	list-style: none;
}

.comment-list .children {
	display: grid;
	gap: var(--sp-5);
	margin-top: var(--sp-5);
	margin-left: var(--sp-5);
	padding-left: var(--sp-5);
	border-left: 1px solid var(--line);
	list-style: none;
}

.comment-body {
	padding: var(--sp-5);
	border: 1px solid var(--line);
	border-radius: var(--radius);
	background: var(--surface);
	color: var(--text-muted);
	font-size: var(--fs-sm);
}

.comment-author {
	display: flex;
	align-items: center;
	gap: var(--sp-3);
	margin-bottom: var(--sp-2);
	color: var(--text);
	font-weight: var(--fw-medium);
}

.comment-author img {
	border-radius: 50%;
}

.comment-metadata,
.comment-metadata a {
	color: var(--text-dim);
	font-size: var(--fs-xs);
	text-decoration: none;
}

.comment-form {
	display: grid;
	gap: var(--sp-4);
}

.comment-form p {
	margin: 0;
	min-width: 0;
}

.comment-form label {
	display: block;
	margin-bottom: var(--sp-2);
	font-size: var(--fs-sm);
	font-weight: var(--fw-medium);
}

.comment-form input[type="text"],
.comment-form input[type="email"],
.comment-form input[type="url"],
.comment-form textarea {
	display: block;
	width: 100%;
	/* Overrides the cols attribute, which is what caused the overflow. */
	max-width: 100%;
	min-width: 0;
	padding: 0.75rem 1rem;
	border: 1px solid var(--line-strong);
	border-radius: var(--radius);
	background: rgba(0, 0, 0, 0.25);
	color: var(--text);
	font-family: inherit;
	font-size: 1rem;
}

.comment-form textarea {
	min-height: 8rem;
	resize: vertical;
}

.comment-form input:focus,
.comment-form textarea:focus {
	border-color: var(--accent);
	box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.25);
	outline: none;
}

.comment-form .form-submit {
	margin: 0;
}

.comment-form input[type="submit"] {
	padding: 0.875rem 1.75rem;
	border: 0;
	border-radius: var(--radius-pill);
	background: var(--gradient);
	color: var(--on-accent);
	font-weight: var(--fw-heading);
	cursor: pointer;
}

.comment-form .comment-notes,
.comment-form .comment-form-cookies-consent {
	color: var(--text-dim);
	font-size: var(--fs-xs);
}

.comment-form-cookies-consent {
	display: flex;
	gap: var(--sp-3);
	align-items: flex-start;
}

.comment-form-cookies-consent input {
	margin-top: 3px;
	accent-color: var(--accent);
}

.no-comments,
.nocomments {
	color: var(--text-dim);
	font-size: var(--fs-sm);
}

/* ==========================================================================
   27. Article layout (blog posts and case studies)
   ========================================================================== */

.article__header {
	position: relative;
	overflow: hidden;
	padding-block: calc(var(--header-h) + clamp(1.75rem, 1.2rem + 2vw, 3rem)) clamp(1.5rem, 1rem + 2vw, 2.5rem);
	background: var(--ink-900);
}

.article__header-bg {
	position: absolute;
	inset: 0;
	pointer-events: none;
}

.article__header-inner {
	position: relative;
	text-align: center;
}

/* A case study leads with facts, so its header reads left like a document. */
.article__header-inner--wide {
	max-width: 52rem;
	margin-inline: auto;
	text-align: left;
}

.article__header-inner .breadcrumbs ol {
	justify-content: center;
}

.article__header-inner--wide .breadcrumbs ol {
	justify-content: flex-start;
}

.article__category {
	display: inline-block;
	margin-bottom: var(--sp-3);
	padding: 0.3125rem 0.875rem;
	border: 1px solid rgba(var(--accent-rgb), 0.4);
	border-radius: var(--radius-pill);
	background: rgba(var(--accent-rgb), 0.1);
	color: var(--accent);
	font-size: var(--fs-xs);
	font-weight: var(--fw-medium);
	text-decoration: none;
	transition: background-color var(--dur-fast) var(--ease);
}

.article__category:hover {
	background: rgba(var(--accent-rgb), 0.2);
	color: var(--accent);
}

.article__title {
	margin-bottom: var(--sp-4);
	font-size: var(--fs-h1);
	text-wrap: balance;
}

.article__title--project {
	font-size: clamp(1.875rem, 1.3rem + 2.4vw, 2.75rem);
}

.article__lead {
	max-width: 46rem;
	margin-inline: auto;
	margin-bottom: var(--sp-5);
	color: var(--text-muted);
	font-size: 1.0625rem;
}

.article__header-inner--wide .article__lead {
	margin-inline: 0;
}

/* Date, reading time and author on one quiet row. */
.article__meta {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sp-3) var(--sp-5);
	justify-content: center;
	/* The figure below is pulled up by exactly the header's own bottom
	   padding — without this, that cancels out completely and the meta row
	   ends up touching the image. */
	margin-bottom: var(--sp-6);
	color: var(--text-dim);
	font-size: var(--fs-xs);
}

.article__meta-item {
	display: inline-flex;
	align-items: center;
	gap: var(--sp-2);
}

.article__meta-item svg {
	color: var(--accent-2);
}

/*
 * The featured image straddles the header and the body, which is what stops
 * the article opening with a large empty band between the two.
 */
.article__figure-wrap {
	margin-top: calc(clamp(1.5rem, 1rem + 2vw, 2.5rem) * -1);
	position: relative;
	z-index: 1;
}

.article__figure {
	margin: 0;
	overflow: hidden;
	border: 1px solid var(--line);
	border-radius: var(--radius-xl);
	box-shadow: var(--shadow-lg);
	aspect-ratio: 16 / 9;
}

.article__image {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.article__figure figcaption {
	padding: var(--sp-3);
	color: var(--text-dim);
	font-size: var(--fs-xs);
	text-align: center;
}

.article__body {
	padding-block: clamp(2.5rem, 1.8rem + 2.6vw, 4rem);
}

.article__body .prose {
	font-size: 1.0625rem;
}

.article__share {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sp-4);
	align-items: center;
	margin-top: var(--sp-8);
	padding-top: var(--sp-6);
	border-top: 1px solid var(--line);
}

.article__share-label {
	margin: 0;
	color: var(--text-dim);
	font-size: var(--fs-sm);
}

.article__share-list {
	display: flex;
	gap: var(--sp-2);
}

.article__share-list .icon-btn {
	font-size: 0.6875rem;
	font-weight: var(--fw-heading);
	letter-spacing: 0.02em;
}

/* Numbered spine on a case study. */
.story-block {
	margin-bottom: var(--sp-8);
}

.story-block:last-child {
	margin-bottom: 0;
}

.story-block__head {
	display: flex;
	align-items: baseline;
	gap: var(--sp-3);
	margin-bottom: var(--sp-4);
	padding-bottom: var(--sp-3);
	border-bottom: 1px solid var(--line);
}

.story-block__number {
	font-size: 0.875rem;
	font-weight: var(--fw-heading);
	letter-spacing: 0.08em;
	background: var(--gradient);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
	-webkit-text-fill-color: transparent;
}

.story-block__title {
	margin: 0;
	font-size: var(--fs-h3);
	color: var(--text);
}

.section--related {
	background: linear-gradient(180deg, transparent, var(--ink-900) 12%, var(--ink-900) 88%, transparent);
}

@media (max-width: 600px) {
	.article__figure {
		aspect-ratio: 4 / 3;
		border-radius: var(--radius-lg);
	}
}

/* The case study intro sits above the numbered spine and needs to be clearly
   separate from it, not butted against the first heading. */
.article--project .article__body > .prose {
	margin-bottom: var(--sp-7);
}

/* ==========================================================================
   28. Vertical rhythm
   ========================================================================== */

/*
 * Sections carry symmetrical padding, which doubles up wherever two stack.
 * Rather than hand-tune each pairing, the first section after a hero band
 * loses part of its top padding — the hero has already provided that space.
 */
.page-hero + .section,
.article__header + .section {
	padding-block-start: clamp(2rem, 1.4rem + 2.4vw, 3.5rem);
}

/* Two bands of the same tone reading as one block get a single gap between
   them rather than two stacked paddings. */
.section--why + .section--process,
.section--benefits + .section--deliverables,
.section--related + .section--cta {
	padding-block-start: 0;
}

/* A section header immediately followed by its own footer button (an empty
   grid) should not leave a void. */
.section > .container > .section__footer:first-child {
	margin-top: 0;
}

/* The closing CTA sits tight to the footer, which has its own top spacing. */
.section--cta:last-of-type {
	padding-block-end: clamp(2rem, 1.4rem + 2.4vw, 3.5rem);
}

/* ==========================================================================
   29. Detail pages
   ========================================================================== */

/* --- Jump links under a service hero ------------------------------------ */

.jump-bar {
	position: relative;
	z-index: 2;
	border-block: 1px solid var(--line);
	background: rgba(var(--accent-rgb), 0.03);
}

.jump-bar__list {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sp-2) var(--sp-5);
	padding-block: var(--sp-4);
}

.jump-bar__link {
	position: relative;
	display: inline-block;
	padding-block: 0.25rem;
	color: var(--text-muted);
	font-size: var(--fs-sm);
	font-weight: 600;
	text-decoration: none;
	transition: color var(--dur) var(--ease);
}

.jump-bar__link::after {
	position: absolute;
	bottom: 0;
	left: 0;
	width: 0;
	height: 2px;
	content: "";
	border-radius: 2px;
	background: var(--gradient);
	transition: width var(--dur) var(--ease);
}

.jump-bar__link:hover,
.jump-bar__link:focus-visible {
	color: var(--text);
}

.jump-bar__link:hover::after,
.jump-bar__link:focus-visible::after {
	width: 100%;
}

/* --- Text paired with a picture ----------------------------------------- */

.feature {
	display: grid;
	gap: var(--sp-6);
	align-items: center;
}

.feature--text-only {
	max-width: var(--container-narrow);
	margin-inline: auto;
}

.feature__title {
	margin-bottom: var(--sp-3);
	font-size: var(--fs-h2);
}

.feature__lead {
	margin-bottom: var(--sp-5);
	color: var(--text-muted);
}

.feature__text .check-list {
	margin-bottom: var(--sp-6);
}

.feature__media {
	position: relative;
	margin: 0;
	overflow: hidden;
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
	background: var(--ink-700);
}

/* A soft accent edge so the picture belongs to the page rather than sitting
   on it like a stock photo. */
.feature__media::after {
	position: absolute;
	inset: 0;
	content: "";
	border-radius: inherit;
	background: linear-gradient(200deg, rgba(var(--accent-rgb), 0.14), transparent 55%);
	pointer-events: none;
}

.feature__img {
	display: block;
	width: 100%;
	height: 100%;
	aspect-ratio: 4 / 3;
	object-fit: cover;
}

@media (min-width: 860px) {
	.feature {
		gap: clamp(2rem, 1rem + 4vw, 4rem);
		grid-template-columns: 1fr 1fr;
	}

	.feature--text-only {
		grid-template-columns: 1fr;
	}

	/* Reversed feature: the picture keeps its DOM position after the text,
	   so the source order still reads content first. */
	.feature--reverse .feature__text {
		order: 2;
	}

	.feature__media--tall .feature__img {
		aspect-ratio: 3 / 4;
	}
}

/* --- Case study story spine --------------------------------------------- */

.article__body--project > .prose,
.story-block:not(.story-block--media) .prose,
.story-block:not(.story-block--media) .story-block__head {
	max-width: 68ch;
}

.story-block--media {
	display: grid;
	gap: var(--sp-5);
	align-items: center;
}

.story-block__figure {
	margin: 0;
	overflow: hidden;
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
	background: var(--ink-700);
}

.story-block__img {
	display: block;
	width: 100%;
	aspect-ratio: 16 / 11;
	object-fit: cover;
}

@media (min-width: 860px) {
	.story-block--media {
		gap: clamp(2rem, 1rem + 3vw, 3.5rem);
		grid-template-columns: 1.15fr 1fr;
	}

	.story-block--media.story-block--reverse .story-block__body {
		order: 2;
	}
}

/* --- Article layout with a contents rail -------------------------------- */

.article__layout {
	display: grid;
	gap: var(--sp-6);
	padding-block-start: var(--sp-7);
}

.article__main {
	min-width: 0;
	max-width: var(--container-narrow);
	margin-inline: auto;
}

.article__rail-title {
	margin-bottom: var(--sp-3);
	color: var(--text-dim);
	font-size: var(--fs-xs);
	font-weight: var(--fw-heading);
	letter-spacing: 0.14em;
	text-transform: uppercase;
}

.article__toc {
	display: grid;
	gap: var(--sp-2);
	padding-left: 0;
	border-left: 1px solid var(--line);
	list-style: none;
	counter-reset: toc;
}

.article__toc-link {
	display: block;
	padding: 0.2rem 0 0.2rem var(--sp-4);
	margin-left: -1px;
	border-left: 2px solid transparent;
	color: var(--text-muted);
	font-size: var(--fs-sm);
	line-height: 1.45;
	text-decoration: none;
	transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.article__toc-link:hover,
.article__toc-link.is-current {
	border-left-color: var(--accent-2);
	color: var(--text);
}

.article__rail-share {
	margin-top: var(--sp-6);
	padding-top: var(--sp-5);
	border-top: 1px solid var(--line);
}

@media (min-width: 1100px) {
	.article__layout {
		gap: clamp(2.5rem, 1rem + 4vw, 4.5rem);
		grid-template-columns: 230px minmax(0, 1fr);
	}

	.article__main {
		margin-inline: 0;
	}

	.article__rail-inner {
		position: sticky;
		/* Clears the sticky header's 90px footprint. */
		top: calc(90px + var(--sp-5));
	}
}

/* Below the two-column breakpoint the rail would only repeat what the reader
   can already see, so it stays out of the way. */
@media (max-width: 1099px) {
	.article__rail {
		display: none;
	}
}

/* --- Key takeaways ------------------------------------------------------ */

.takeaways {
	margin-bottom: var(--sp-7);
	padding: clamp(1.25rem, 1rem + 1vw, 1.75rem);
	border: 1px solid rgba(var(--accent-rgb), 0.28);
	border-radius: var(--radius-lg);
	background: var(--gradient-soft);
}

.takeaways__title {
	display: flex;
	gap: var(--sp-2);
	align-items: center;
	margin-bottom: var(--sp-4);
	color: var(--text);
	font-size: var(--fs-sm);
	font-weight: var(--fw-heading);
	letter-spacing: 0.06em;
	text-transform: uppercase;
}

.takeaways__title svg {
	flex: none;
	color: var(--accent-2);
}

.takeaways__list {
	display: grid;
	gap: var(--sp-3);
	padding-left: 0;
	list-style: none;
}

.takeaways__list li {
	position: relative;
	padding-left: var(--sp-5);
	color: var(--text-muted);
	font-size: var(--fs-sm);
	line-height: 1.6;
}

.takeaways__list li::before {
	position: absolute;
	top: 0.55em;
	left: 0;
	width: 7px;
	height: 7px;
	content: "";
	border-radius: 50%;
	background: var(--gradient);
}

/* --- Pull quote --------------------------------------------------------- */

.pull-quote {
	position: relative;
	margin: var(--sp-8) 0;
	padding: var(--sp-6) 0 var(--sp-6) clamp(1.5rem, 1rem + 2vw, 2.5rem);
	border-left: 3px solid transparent;
	border-image: var(--gradient) 1;
}

.pull-quote blockquote {
	margin: 0;
}

.pull-quote p {
	margin: 0;
	color: var(--text);
	font-size: clamp(1.125rem, 1rem + 0.8vw, 1.5rem);
	font-weight: 500;
	line-height: 1.45;
	letter-spacing: -0.01em;
}

/* --- Supporting figure in the body -------------------------------------- */

.article__inline-figure {
	margin: var(--sp-8) 0 0;
	overflow: hidden;
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
	background: var(--ink-700);
}

.article__inline-img {
	display: block;
	width: 100%;
	aspect-ratio: 16 / 9;
	object-fit: cover;
}

.article__inline-figure figcaption {
	padding: var(--sp-3) var(--sp-4);
	border-top: 1px solid var(--line);
	color: var(--text-dim);
	font-size: var(--fs-xs);
}

/* --- Detail page refinements -------------------------------------------- */

/*
 * With a picture beside each story block, sp-8 between them reads as a gap
 * rather than a rhythm. The image already separates one block from the next.
 */
.story-block--media {
	margin-bottom: clamp(2.5rem, 1.5rem + 3vw, 4rem);
}

.story-block:last-child {
	margin-bottom: 0;
}

/*
 * The picture fills its column rather than floating in the middle of it, so a
 * long overview does not leave a hole beside the text.
 */
@media (min-width: 860px) {
	.feature {
		align-items: stretch;
	}

	.feature__media {
		align-self: stretch;
		/* A very long column of text should not produce a billboard. */
		max-height: 620px;
	}

	.feature__img,
	.feature__media--tall .feature__img {
		height: 100%;
		min-height: 320px;
		aspect-ratio: auto;
	}

	.story-block--media {
		align-items: center;
	}
}

/* ==========================================================================
   30. About page: story and CTA-with-photo
   ========================================================================== */

.split__prose {
	margin-bottom: var(--sp-2);
}

/* A smaller, tighter pull-quote for use beside an image rather than running
   the full width of an article column. */
.pull-quote--tight {
	margin: var(--sp-6) 0 0;
	padding: var(--sp-4) 0 var(--sp-4) clamp(1.125rem, 0.9rem + 1vw, 1.5rem);
}

.pull-quote--tight p {
	font-size: clamp(1rem, 0.9rem + 0.5vw, 1.1875rem);
}

/*
 * The closing CTA can carry an optional background photo instead of the
 * plain gradient panel. Rather than a flat dark scrim over the whole image —
 * measured against this image, that either crushed the photo to near-black
 * or left the brightest patch of sky under the AA contrast minimum — the
 * overlay is a vignette centred on the text column: darkest where the
 * heading and button sit, easing off toward the edges so the photo itself
 * stays genuinely visible either side of the copy.
 */
.cta-panel--photo {
	background: var(--ink-800);
}

.cta-panel__bg {
	position: absolute;
	inset: 0;
	z-index: 0;
}

.cta-panel__bg-img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.cta-panel__bg::after {
	position: absolute;
	inset: 0;
	content: "";
	background:
		radial-gradient(120% 130% at 50% 42%,
			rgba(4, 4, 8, 0.72) 0%,
			rgba(4, 4, 8, 0.58) 32%,
			rgba(4, 4, 8, 0.34) 62%,
			rgba(4, 4, 8, 0.12) 100%),
		linear-gradient(180deg, rgba(4, 4, 8, 0.3) 0%, transparent 22%, transparent 78%, rgba(4, 4, 8, 0.45) 100%);
}

.cta-panel--photo .cta-panel__glow {
	opacity: 0.65;
}

/* A light scrim still leaves patches of bright sky right behind the text on
   some photos — the shadow is what keeps the heading and paragraph legible
   there without darkening the image further. */
.cta-panel--photo .cta-panel__title,
.cta-panel--photo .cta-panel__text {
	text-shadow: 0 2px 18px rgba(0, 0, 0, 0.75), 0 1px 4px rgba(0, 0, 0, 0.6);
}

/* ==========================================================================
   31. Location map
   ========================================================================== */

.map-card {
	position: relative;
	overflow: hidden;
	border: 1px solid var(--line);
	border-radius: var(--radius-xl);
	background: var(--surface);
	box-shadow: var(--shadow-lg);
}

.map-card__frame {
	position: relative;
	aspect-ratio: 21 / 9;
	background: var(--ink-700);
}

.map-card__frame iframe {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	border: 0;
	/*
	 * The public embed only ships Google's light map skin — there is no dark
	 * variant without the paid Maps JS API. Inverting the whole frame and
	 * rotating the hue back gives a passable dark map that sits with the rest
	 * of the page instead of a bright rectangle interrupting it; labels and
	 * roads stay legible.
	 */
	filter: invert(92%) hue-rotate(180deg) brightness(0.95) contrast(0.92);
}

.map-card__footer {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sp-4);
	align-items: center;
	justify-content: space-between;
	padding: var(--sp-5) clamp(1.25rem, 1rem + 1vw, 1.75rem);
	border-top: 1px solid var(--line);
}

.map-card__title {
	display: flex;
	align-items: center;
	gap: var(--sp-2);
	margin: 0 0 0.25rem;
	color: var(--text);
	font-weight: var(--fw-heading);
}

.map-card__title svg {
	flex: none;
	color: var(--accent-2);
}

.map-card__address {
	margin: 0;
	color: var(--text-muted);
	font-size: var(--fs-sm);
	line-height: 1.5;
}

.map-card__link {
	flex: none;
}

@media (max-width: 640px) {
	.map-card__frame {
		aspect-ratio: 4 / 3;
	}

	.map-card__footer {
		flex-direction: column;
		align-items: flex-start;
	}

	.map-card__link {
		width: 100%;
		justify-content: center;
	}
}

/* ==========================================================================
   32. Above-the-fold entrance motion
   ========================================================================== */

/*
 * The homepage hero and every inner-page hero band sit in the initial
 * viewport, so they cannot wait on IntersectionObserver the way the rest of
 * the page's .reveal elements do — that would either play instantly (no
 * benefit) or, worse, risk a flash of hidden content if JavaScript is slow to
 * attach. A pure CSS animation runs unconditionally on paint, needs no
 * script, and costs nothing extra to load.
 *
 * Scoped entirely inside the no-preference query, so a reduced-motion visitor
 * gets the plain static hero with no opacity dip at all — never a version
 * that depends on an animation to become visible.
 */
@media (prefers-reduced-motion: no-preference) {
	@keyframes pixal-rise-in {
		from {
			opacity: 0;
			transform: translateY(18px);
		}
		to {
			opacity: 1;
			transform: none;
		}
	}

	.hero__content > *,
	.hero__aside > *,
	.page-hero__inner > * {
		animation: pixal-rise-in 0.7s var(--ease-out) both;
	}

	.hero__content > *:nth-child(1),
	.page-hero__inner > *:nth-child(1) {
		animation-delay: 0.02s;
	}
	.hero__content > *:nth-child(2),
	.page-hero__inner > *:nth-child(2) {
		animation-delay: 0.1s;
	}
	.hero__content > *:nth-child(3),
	.page-hero__inner > *:nth-child(3) {
		animation-delay: 0.18s;
	}
	.hero__content > *:nth-child(4),
	.page-hero__inner > *:nth-child(4) {
		animation-delay: 0.26s;
	}
	.hero__content > *:nth-child(5) {
		animation-delay: 0.34s;
	}

	.hero__aside > * {
		animation-delay: 0.22s;
	}

	/*
	 * Named explicitly rather than "> *": the 404 page also has two blocks
	 * further down (the quick-link grid, the popular-services list) that use
	 * the ordinary scroll-triggered .reveal system instead, since they sit
	 * lower on the page. Animating every direct child here would fight that
	 * second system for the same elements.
	 */
	.error-page__eyebrow,
	.error-page__figure,
	.error-page__title,
	.error-page__text,
	.error-page > form,
	.error-page__actions {
		animation: pixal-rise-in 0.7s var(--ease-out) both;
	}

	.error-page__figure {
		animation-delay: 0.06s;
	}
	.error-page__title {
		animation-delay: 0.14s;
	}
	.error-page__text {
		animation-delay: 0.22s;
	}
	.error-page > form {
		animation-delay: 0.3s;
	}
	.error-page__actions {
		animation-delay: 0.38s;
	}
}

/* ==========================================================================
   33. 404 page
   ========================================================================== */

.error-page__eyebrow {
	margin-bottom: var(--sp-5);
}

/* The orbit graphic sits centred behind the numeral, low enough in opacity
   that the numeral stays the dominant shape — it reads as depth, not as a
   second focal point. */
.error-page__figure {
	position: relative;
	display: grid;
	place-items: center;
	min-height: clamp(9rem, 6rem + 10vw, 13rem);
	margin-bottom: var(--sp-3);
}

.error-page__figure .orbit {
	position: absolute;
	inset: 0;
	width: min(360px, 90%);
	margin: auto;
	opacity: 0.5;
	pointer-events: none;
}

.error-page__code {
	position: relative;
	z-index: 1;
	margin: 0;
}

.error-page__links {
	margin-top: var(--sp-9);
	padding-top: var(--sp-8);
	border-top: 1px solid var(--line);
}

.error-page__links-title {
	margin-bottom: var(--sp-5);
	color: var(--text-dim);
	font-size: var(--fs-xs);
	font-weight: var(--fw-heading);
	letter-spacing: 0.14em;
	text-transform: uppercase;
}

.error-page__grid {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: var(--sp-3);
	max-width: 640px;
	margin: 0 auto;
	padding: 0;
	list-style: none;
}

.error-page__grid a {
	display: flex;
	align-items: center;
	gap: var(--sp-3);
	padding: var(--sp-4);
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
	background: var(--surface);
	color: var(--text);
	font-weight: 600;
	text-decoration: none;
	transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease),
		transform var(--dur) var(--ease);
}

.error-page__grid svg {
	flex: none;
	color: var(--accent-2);
}

.error-page__grid a:hover,
.error-page__grid a:focus-visible {
	border-color: rgba(var(--accent-rgb), 0.45);
	background: var(--surface-2);
	transform: translateY(-2px);
}

.error-page__suggestions {
	margin-top: var(--sp-8);
}

@media (min-width: 560px) {
	.error-page__grid {
		grid-template-columns: repeat(4, 1fr);
	}
}

/* ==========================================================================
   34. Section divider background (grid + glow + corner accents)
   ========================================================================== */

/*
 * Applied to one or two sections per page — never all of them — so a run of
 * plain dark sections gets a visual break without the whole page turning
 * busy. The section itself only needs position:relative, overflow:hidden and
 * a background colour; this layer sits behind its content at z-index: 0.
 */
.section--glow {
	position: relative;
	overflow: hidden;
	background: var(--ink-900);
}

.section--glow > .container,
.section--glow > .section-header {
	position: relative;
	z-index: 1;
}

.section-glow {
	position: absolute;
	inset: 0;
	z-index: 0;
	overflow: hidden;
	pointer-events: none;
}

.section-glow__grid {
	position: absolute;
	inset: 0;
	background-image: linear-gradient(var(--line) 1px, transparent 1px),
		linear-gradient(90deg, var(--line) 1px, transparent 1px);
	background-size: 56px 56px;
	mask-image: radial-gradient(ellipse 70% 65% at 50% 45%, #000 25%, transparent 78%);
	-webkit-mask-image: radial-gradient(ellipse 70% 65% at 50% 45%, #000 25%, transparent 78%);
	opacity: 0.55;
}

.section-glow__orb {
	position: absolute;
	border-radius: 50%;
	filter: blur(90px);
}

.section-glow__orb--one {
	top: 8%;
	left: 14%;
	width: min(420px, 42vw);
	aspect-ratio: 1;
	background: radial-gradient(circle, rgba(var(--accent-rgb), 0.4), transparent 70%);
}

.section-glow__orb--two {
	bottom: 4%;
	right: 12%;
	width: min(380px, 38vw);
	aspect-ratio: 1;
	background: radial-gradient(circle, rgba(var(--accent-2-rgb), 0.38), transparent 70%);
}

/* Two thin parallel rays crossing each corner, the way a cut corner panel
   reads on an equipment housing — a recurring accent rather than a one-off,
   so it reads as part of the design system. */
.section-glow__corner {
	position: absolute;
	width: clamp(140px, 16vw, 220px);
	height: clamp(140px, 16vw, 220px);
}

.section-glow__corner--tl {
	top: 0;
	left: 0;
}

.section-glow__corner--br {
	right: 0;
	bottom: 0;
	transform: rotate(180deg);
}

.section-glow__corner-line {
	position: absolute;
	top: -10%;
	left: -10%;
	width: 3px;
	height: 155%;
	background: linear-gradient(180deg, transparent 4%, rgba(var(--accent-rgb), 0.9) 40%, rgba(var(--accent-2-rgb), 0.75) 60%, transparent 96%);
	box-shadow: 0 0 16px rgba(var(--accent-rgb), 0.55);
	transform: rotate(-42deg);
	transform-origin: top left;
}

.section-glow__corner-line--b {
	left: 4%;
	opacity: 0.5;
}

@media (max-width: 640px) {
	.section-glow__orb--one,
	.section-glow__orb--two {
		width: 60vw;
		filter: blur(70px);
	}

	.section-glow__corner {
		width: 120px;
		height: 120px;
	}
}

/* ==========================================================================
   35. Hero eyebrow: brighter and narrower than the default pill
   ========================================================================== */

/*
 * The neutral .eyebrow colour reads fine on a plain dark section; sitting on
 * top of a bright, busy photo it needs real contrast instead. White text plus
 * a tighter, blurred dark pill background keeps it legible over any part of
 * the image, and the shorter default copy now fits one line even at 320px.
 */
.eyebrow--hero {
	background: rgba(8, 9, 18, 0.55);
	backdrop-filter: blur(6px);
	-webkit-backdrop-filter: blur(6px);
	color: #fff;
	white-space: nowrap;
}

@media (max-width: 420px) {
	.eyebrow--hero {
		padding-inline: 0.7rem;
		font-size: 0.75rem;
	}
}

/* ==========================================================================
   36. Side rails (social + quick contact)
   ========================================================================== */

/*
 * Two fixed strips — social links on the left, quick-contact links (chat,
 * email, call) on the right — hidden until the visitor scrolls past the
 * hero, then slide in. Positioned with top and bottom both at 0 and
 * centred with flexbox rather than the usual top:50%/translateY(-50%)
 * trick: on a phone the visible viewport height changes as the browser's
 * address bar shows and hides while scrolling, and a percentage-based 50%
 * recalculates against that moving target, which reads as the rail jumping
 * mid-scroll. Anchoring to both edges and centring with flex tracks the
 * real viewport continuously instead, which removes that jump.
 */
.side-rail {
	position: fixed;
	top: 0;
	bottom: 0;
	z-index: 45;
	display: flex;
	align-items: center;
	opacity: 0;
	pointer-events: none;
	transition: opacity 420ms var(--ease), transform 420ms var(--ease);
}

.side-rail--social {
	left: var(--sp-4);
	transform: translateX(-18px);
}

.side-rail--contact {
	right: var(--sp-4);
	transform: translateX(18px);
}

/* The rail is a full-height strip only so it can centre its icons; only the
   icon list takes pointer input, or the empty strip would swallow clicks on
   the back-to-top button and on page content along the edge. */
.is-scrolled .side-rail {
	opacity: 1;
	transform: none;
}

.is-scrolled .side-rail__list {
	pointer-events: auto;
}

.side-rail__list {
	display: flex;
	flex-direction: column;
	gap: 0.625rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.side-rail__link {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 38px;
	height: 38px;
	flex: none;
	border: 1px solid var(--line-strong);
	border-radius: 50%;
	background: rgba(12, 13, 26, 0.78);
	backdrop-filter: blur(8px);
	-webkit-backdrop-filter: blur(8px);
	color: var(--text-muted);
	text-decoration: none;
	transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease),
		background-color var(--dur-fast) var(--ease), transform var(--dur) var(--ease);
}

.side-rail__link:hover,
.side-rail__link:focus-visible {
	border-color: rgba(var(--accent-rgb), 0.6);
	background: rgba(var(--accent-rgb), 0.16);
	color: var(--accent);
	transform: translateY(-2px);
}

.side-rail__link:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 2px;
}

/* Below the width the header itself switches to the hamburger menu, so the
   two rails would otherwise compete with the mobile nav for the same edges
   — the social rail steps aside and only the contact rail (chat, email,
   call) stays, since those are the links most likely to convert a mobile
   visitor into an enquiry. */
@media (max-width: 1023px) {
	.side-rail--social {
		display: none;
	}
}

@media (max-width: 480px) {
	.side-rail--contact {
		right: var(--sp-3);
	}

	.side-rail__link {
		width: 34px;
		height: 34px;
	}
}

/* prefers-reduced-motion: skip the slide, just fade — the reveal system
   elsewhere in this file follows the same rule. */
@media (prefers-reduced-motion: reduce) {
	.side-rail {
		transition-property: opacity;
	}

	.side-rail--social,
	.side-rail--contact {
		transform: none;
	}
}


/* ==========================================================================
   Case studies — moving rows (homepage section 6, "marquee" layout)
   ========================================================================== */

/* The rows run edge to edge; the section clips them so the page itself never
   scrolls sideways. */
.section--work-marquee {
	overflow: hidden;
}

/* Space above "View all case studies" — set on its wrapper so it can't
   collapse away against the rows. */
.section--work-marquee > .container:last-child {
	padding-top: var(--sp-6);
}

.section--work-marquee > .container:last-child .section__footer {
	margin-top: 0;
}

.work-marquee {
	--wm-h: 240px;
	--wm-gap: 1.25rem;
	--wm-fade: 7%;
	display: grid;
	gap: var(--wm-gap);
	margin-top: var(--sp-8);
	/* Room for a hovered card to grow without being clipped. */
	padding-block: 0.75rem;
	overflow: hidden;
	/* Cards fade in and out at the edges. A mask only affects painting, so
	   pointer and keyboard interaction are untouched. */
	-webkit-mask-image: linear-gradient(90deg, transparent, #000 var(--wm-fade), #000 calc(100% - var(--wm-fade)), transparent);
	mask-image: linear-gradient(90deg, transparent, #000 var(--wm-fade), #000 calc(100% - var(--wm-fade)), transparent);
}

.work-marquee__row {
	display: flex;
}

/* One set of cards. The trailing padding matches the gap, so copies placed
   end to end keep the same spacing across the seam. */
.work-marquee__track {
	display: flex;
	flex: none;
	gap: var(--wm-gap);
	margin: 0;
	padding: 0 var(--wm-gap) 0 0;
	list-style: none;
}

/* app.js wraps each row's sets in a belt and moves it by exactly one set's
   width, so the loop restarts on an identical frame. Transform only — no
   layout work per frame. */
.work-marquee__belt {
	display: flex;
	flex: none;
	width: max-content;
	animation: pixal-work-left var(--wm-duration, 60s) linear infinite;
	animation-delay: var(--wm-delay, 0s);
	will-change: transform;
}

.work-marquee__row[data-direction="right"] .work-marquee__belt {
	animation-name: pixal-work-right;
}

@keyframes pixal-work-left {
	from { transform: translate3d(0, 0, 0); }
	to { transform: translate3d(calc(-1 * var(--wm-shift, 0px)), 0, 0); }
}

@keyframes pixal-work-right {
	from { transform: translate3d(calc(-1 * var(--wm-shift, 0px)), 0, 0); }
	to { transform: translate3d(0, 0, 0); }
}

/* Pointer or keyboard inside the section pauses every row where it is; it
   resumes from the same spot. Also paused while scrolled out of view. */
.work-marquee:hover .work-marquee__belt,
.work-marquee:focus-within .work-marquee__belt,
.work-marquee.is-offscreen .work-marquee__belt {
	animation-play-state: paused;
}

/* --- Card ------------------------------------------------------------- */

.work-card {
	--wm-w: 460px;
	position: relative;
	display: flex;
	flex: none;
	flex-direction: column;
	justify-content: flex-end;
	width: var(--wm-w);
	height: var(--wm-h);
	padding: var(--sp-5) var(--sp-5) 1.25rem;
	overflow: hidden;
	isolation: isolate;
	border: 1px solid var(--line);
	border-radius: var(--radius-lg);
	background: var(--ink-700);
	transition: transform var(--dur) var(--ease-out), border-color var(--dur) var(--ease);
}

/* Four fixed widths, assigned by position in the template. */
.work-card--s { --wm-w: 320px; }
.work-card--m { --wm-w: 390px; }
.work-card--l { --wm-w: 460px; }
.work-card--xl { --wm-w: 520px; }

.work-card__media {
	position: absolute;
	inset: 0;
	z-index: -1;
	background: var(--gradient-soft), var(--ink-600);
}

.work-card__img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 700ms var(--ease-out);
}

/* Darkens the lower part of the picture so the title always reads. */
.work-card__media::after {
	position: absolute;
	inset: 0;
	content: "";
	background: linear-gradient(to top, rgba(6, 7, 15, 0.9) 0%, rgba(6, 7, 15, 0.45) 42%, rgba(6, 7, 15, 0.08) 78%);
}

/* Never transform this block or the title: a transform turns it into the
   box the full-card link overlay measures from, leaving only the text
   clickable while hovered. */
.work-card__body {
	display: grid;
	gap: 0.3rem;
	min-width: 0;
}

.work-card__title {
	margin: 0;
	font-size: clamp(1.15rem, 0.95rem + 0.55vw, 1.5rem);
	font-weight: var(--fw-heading);
	line-height: var(--lh-heading);
	letter-spacing: -0.015em;
	color: var(--text);
	text-wrap: balance;
}

.work-card__link {
	color: inherit;
	text-decoration: none;
}

/* The title link covers the whole card, so anywhere on it opens the case
   study while there is still only one link to tab to. */
.work-card__link::after {
	position: absolute;
	inset: 0;
	z-index: 1;
	content: "";
}

.work-card__site,
.work-card__label {
	display: inline-flex;
	align-items: center;
	gap: 0.35rem;
	width: fit-content;
	max-width: 100%;
	margin: 0;
	font-size: var(--fs-xs);
	font-weight: var(--fw-medium);
	color: var(--text-muted);
	letter-spacing: 0.01em;
}

/* The project's own website sits above the card link so it stays clickable. */
.work-card__site {
	position: relative;
	z-index: 2;
	text-decoration: none;
	transition: color var(--dur-fast) var(--ease);
}

.work-card__site:hover {
	color: var(--accent);
}

.work-card__arrow {
	font-size: 0.85em;
	transition: transform var(--dur) var(--ease-out);
}

.work-card__go {
	position: absolute;
	top: 1rem;
	right: 1rem;
	display: grid;
	place-items: center;
	width: 2.25rem;
	height: 2.25rem;
	border: 1px solid var(--line-strong);
	border-radius: 50%;
	background: rgba(6, 7, 15, 0.55);
	color: var(--text);
	opacity: 0;
	transform: translate(-6px, 6px);
	transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease-out), background var(--dur) var(--ease);
}

.work-card__go svg {
	transform: rotate(-45deg);
}

.work-card:hover {
	transform: scale(1.025);
	border-color: rgba(var(--accent-rgb), 0.45);
}

.work-card:hover .work-card__img {
	transform: scale(1.06);
}

.work-card:hover .work-card__arrow {
	transform: translate(2px, -2px);
}

.work-card:hover .work-card__go,
.work-card:focus-within .work-card__go {
	opacity: 1;
	transform: none;
	background: var(--gradient);
	border-color: transparent;
	color: var(--on-accent);
}

.work-card:focus-within {
	border-color: var(--accent);
	box-shadow: inset 0 0 0 1px var(--accent);
}

.work-card__link:focus-visible,
.work-card__site:focus-visible {
	outline: none;
}

.work-card__site:focus-visible {
	color: var(--accent);
	text-decoration: underline;
}

/* --- Responsive ------------------------------------------------------- */

@media (max-width: 1023px) {
	.work-marquee {
		--wm-h: 210px;
		--wm-gap: 1rem;
	}

	.work-card--s { --wm-w: 280px; }
	.work-card--m { --wm-w: 330px; }
	.work-card--l { --wm-w: 380px; }
	.work-card--xl { --wm-w: 430px; }
}

@media (max-width: 700px) {
	.work-marquee {
		--wm-h: 172px;
		--wm-gap: 0.75rem;
		--wm-fade: 4%;
		margin-top: var(--sp-6);
	}

	.work-card {
		padding: var(--sp-4);
		border-radius: var(--radius);
	}

	.work-card--s { --wm-w: 250px; }
	.work-card--m { --wm-w: 270px; }
	.work-card--l { --wm-w: 300px; }
	.work-card--xl { --wm-w: 320px; }

	/* Two rows on phones; a third that only repeats work is dropped. */
	.work-marquee__row--wide-only {
		display: none;
	}

	.work-card__go {
		top: 0.75rem;
		right: 0.75rem;
		width: 2rem;
		height: 2rem;
	}
}

/* Reduced motion: no movement. The repeated rows go, and the remaining row
   becomes a plain sideways scroll holding every case study. */
@media (prefers-reduced-motion: reduce) {
	.work-marquee {
		-webkit-mask-image: none;
		mask-image: none;
	}

	.work-marquee__belt {
		animation: none;
	}

	.work-marquee__row[aria-hidden="true"] {
		display: none;
	}

	.work-marquee__row {
		overflow-x: auto;
		padding-inline: var(--gutter);
		scroll-snap-type: x proximity;
		scrollbar-width: thin;
	}

	.work-card {
		scroll-snap-align: start;
		transition: none;
	}

	.work-card:hover,
	.work-card:hover .work-card__img {
		transform: none;
	}
}
