/*
 * Whitehill Solution — redesigned sections.
 *
 * Loaded after main.css, so it can build on the component classes already
 * defined there rather than repeating them. Everything below uses tokens from
 * tokens.css and therefore follows the light/dark switch automatically.
 *
 * Contents:
 *   1. Decorative grid overlay
 *   2. Screenshot showcase
 *   3. Digital health section
 *   4. Client feedback
 *   5. Portfolio project cards
 *   6. About page
 *   7. Comments
 */

/* =========================================================================
   1. Decorative grid overlay
   ====================================================================== */

.wh-surface-deep {
	position: relative;
	isolation: isolate;
	overflow: hidden;
}

.wh-surface-deep > .wh-grid-lines {
	position: absolute;
	inset: 0;
	z-index: -1;
	mask-image: radial-gradient(ellipse at 50% 0%, #000, transparent 78%);
	pointer-events: none;
}

/* =========================================================================
   2. Screenshot showcase
   ====================================================================== */

.wh-shots {
	margin: 0;
	display: grid;
	gap: 0.875rem;
}

.wh-shots__frame {
	border: 1px solid color-mix(in oklab, #fff 16%, transparent);
	border-radius: var(--radius-2xl);
	background: color-mix(in oklab, #000 22%, transparent);
	box-shadow: var(--shadow-lift);
	overflow: hidden;
}

/* On a light surface the frame borrows the ordinary card treatment. */
.wh-section:not(.wh-surface-deep) .wh-shots__frame {
	border-color: var(--border);
	background: var(--card);
}

.wh-shots__chrome {
	display: flex;
	align-items: center;
	gap: 0.375rem;
	padding: 0.625rem 0.875rem;
	border-bottom: 1px solid color-mix(in oklab, #fff 12%, transparent);
	background: color-mix(in oklab, #000 25%, transparent);
}

.wh-section:not(.wh-surface-deep) .wh-shots__chrome {
	border-bottom-color: var(--border);
	background: var(--muted);
}

.wh-shots__dot {
	width: 0.5rem;
	height: 0.5rem;
	border-radius: 9999px;
	background: color-mix(in oklab, currentColor 30%, transparent);
}

.wh-shots__url {
	flex: 1;
	margin-inline-start: 0.5rem;
	border-radius: 9999px;
	background: color-mix(in oklab, currentColor 10%, transparent);
	padding: 0.1875rem 0.625rem;
	font-family: var(--font-mono);
	font-size: 0.6875rem;
	letter-spacing: 0.02em;
	opacity: 0.72;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.wh-shots__stage {
	position: relative;
	aspect-ratio: 16 / 10;
	background: color-mix(in oklab, #000 30%, transparent);
}

.wh-shots__panel img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: top center;
}

/*
 * Before the enhancement runs, panels simply stack — the first is on top and
 * visible, so the block never renders blank if JavaScript fails to load.
 */
.wh-shots__panel {
	position: absolute;
	inset: 0;
}

.wh-shots.is-enhanced .wh-shots__panel {
	opacity: 0;
	visibility: hidden;
	transition:
		opacity 0.35s ease,
		visibility 0.35s ease;
}

.wh-shots.is-enhanced .wh-shots__panel.is-active {
	opacity: 1;
	visibility: visible;
}

.wh-shots__rail {
	display: flex;
	gap: 0.625rem;
	overflow-x: auto;
	scrollbar-width: thin;
	padding-bottom: 0.25rem;
}

.wh-shots__thumb {
	position: relative;
	flex: none;
	width: 4.5rem;
	aspect-ratio: 16 / 10;
	border: 1px solid color-mix(in oklab, #fff 16%, transparent);
	border-radius: var(--radius-sm);
	overflow: hidden;
	opacity: 0.55;
	transition:
		opacity 0.2s ease,
		border-color 0.2s ease,
		transform 0.2s ease;
}

.wh-section:not(.wh-surface-deep) .wh-shots__thumb {
	border-color: var(--border);
}

.wh-shots__thumb img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.wh-shots__thumb:hover,
.wh-shots__thumb:focus-visible {
	opacity: 1;
	transform: translateY(-2px);
}

.wh-shots__thumb.is-active {
	opacity: 1;
	border-color: var(--cyan);
	box-shadow: 0 0 0 2px color-mix(in oklab, var(--cyan) 40%, transparent);
}

.wh-shots__caption {
	font-size: 0.8125rem;
	opacity: 0.7;
}

/* =========================================================================
   3. Digital health section
   ====================================================================== */

.wh-health__lead {
	display: grid;
	gap: 3rem;
	align-items: center;
}

@media (min-width: 1024px) {
	.wh-health__lead {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
		gap: 4rem;
	}
}

.wh-health__standards {
	margin-top: 2rem;
	padding-top: 1.75rem;
	border-top: 1px solid color-mix(in oklab, #fff 14%, transparent);
}

.wh-health__standards-label {
	font-size: 0.6875rem;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.14em;
	color: var(--cyan);
}

.wh-health__standards-list {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin-top: 0.875rem;
}

.wh-health__standards-list li {
	border: 1px solid color-mix(in oklab, #fff 18%, transparent);
	border-radius: 9999px;
	background: color-mix(in oklab, #fff 7%, transparent);
	padding: 0.3125rem 0.75rem;
	font-size: 0.75rem;
	font-weight: 500;
	letter-spacing: 0.01em;
}

/* Capability cards */

.wh-health__caps {
	display: grid;
	gap: 1.25rem;
	margin-top: 4rem;
	padding-top: 3.5rem;
	border-top: 1px solid color-mix(in oklab, #fff 12%, transparent);
}

@media (min-width: 640px) {
	.wh-health__caps {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (min-width: 1024px) {
	.wh-health__caps {
		grid-template-columns: repeat(4, minmax(0, 1fr));
	}
}

.wh-health__cap {
	position: relative;
	border: 1px solid color-mix(in oklab, #fff 14%, transparent);
	border-radius: var(--radius-2xl);
	background: color-mix(in oklab, #fff 5%, transparent);
	padding: 1.5rem;
	transition:
		border-color 0.25s ease,
		background 0.25s ease,
		transform 0.25s ease;
}

.wh-health__cap:hover {
	transform: translateY(-3px);
	border-color: color-mix(in oklab, var(--cyan) 50%, transparent);
	background: color-mix(in oklab, #fff 8%, transparent);
}

.wh-health__cap-index {
	font-family: var(--font-mono);
	font-size: 0.75rem;
	font-weight: 700;
	letter-spacing: 0.12em;
	color: var(--cyan);
}

.wh-health__cap-title {
	margin-top: 0.75rem;
	font-family: var(--font-display);
	font-size: 1.0625rem;
	line-height: 1.3;
	letter-spacing: -0.01em;
}

.wh-health__cap-text {
	margin-top: 0.625rem;
	font-size: 0.875rem;
	line-height: 1.65;
	color: color-mix(in oklab, var(--navy-foreground) 72%, transparent);
}

/* Proof points */

.wh-health__stats {
	display: grid;
	gap: 1.5rem;
	margin-top: 3.5rem;
	padding: 2rem;
	border: 1px solid color-mix(in oklab, var(--cyan) 30%, transparent);
	border-radius: var(--radius-3xl);
	background: linear-gradient(
		100deg,
		color-mix(in oklab, var(--cyan) 12%, transparent),
		transparent 65%
	);
	text-align: center;
}

@media (min-width: 640px) {
	.wh-health__stats {
		grid-auto-flow: column;
		grid-auto-columns: 1fr;
	}
}

.wh-health__stat-value {
	font-family: var(--font-display);
	font-size: clamp(1.75rem, 4vw, 2.5rem);
	line-height: 1;
	letter-spacing: -0.03em;
	background: var(--gradient-accent);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
}

.wh-health__stat-label {
	margin-top: 0.5rem;
	margin-inline-start: 0;
	font-size: 0.8125rem;
	line-height: 1.5;
	color: color-mix(in oklab, var(--navy-foreground) 72%, transparent);
}

/* =========================================================================
   4. Client feedback
   ====================================================================== */

/*
 * One quote at a time. The rail is a native scroll-snap carousel, so it works
 * by swipe and keyboard before any script runs; JS adds the arrows, the client
 * rail and the auto-advance on top of that.
 */

.wh-feedback {
	position: relative;
	isolation: isolate;
	overflow: hidden;
	background: color-mix(in oklab, var(--secondary) 40%, transparent);
}

.wh-feedback__glow {
	position: absolute;
	z-index: -1;
	top: -35%;
	inset-inline-start: 50%;
	width: min(60rem, 120%);
	aspect-ratio: 2 / 1;
	transform: translateX(-50%);
	border-radius: 9999px;
	background: radial-gradient(ellipse, color-mix(in oklab, var(--primary) 16%, transparent), transparent 70%);
	filter: blur(50px);
	pointer-events: none;
}

/* Faint dotted field: gives the stage depth without competing with the text. */
.wh-feedback__grid-lines {
	position: absolute;
	z-index: -1;
	inset: 0;
	background-image: radial-gradient(color-mix(in oklab, var(--foreground) 9%, transparent) 1px, transparent 1px);
	background-size: 28px 28px;
	mask-image: radial-gradient(ellipse at 50% 40%, #000 15%, transparent 72%);
	-webkit-mask-image: radial-gradient(ellipse at 50% 40%, #000 15%, transparent 72%);
	pointer-events: none;
}

.wh-feedback__summary {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: 0.75rem;
	margin-top: 1.75rem;
}

.wh-feedback__score {
	font-family: var(--font-display);
	font-size: 2rem;
	line-height: 1;
	letter-spacing: -0.03em;
}

.wh-feedback__stars {
	display: inline-flex;
	gap: 0.125rem;
}

.wh-feedback__meta {
	font-size: 0.875rem;
	color: var(--muted-foreground);
}

/* Slider shell */

.wh-feedback__slider {
	position: relative;
	margin-top: 3rem;
}

.wh-feedback__viewport {
	overflow-x: auto;
	overscroll-behavior-inline: contain;
	scroll-snap-type: x mandatory;
	scrollbar-width: none;
	/* Room for the hover lift and shadow of the card inside. */
	padding: 0.75rem 0 1.5rem;
	border-radius: var(--radius-2xl);
}

.wh-feedback__viewport::-webkit-scrollbar {
	display: none;
}

.wh-feedback__viewport:focus-visible {
	outline: 2px solid var(--primary);
	outline-offset: 4px;
}

.wh-feedback__track {
	display: flex;
	align-items: stretch;
	gap: 1.25rem;
}

.wh-feedback__slide {
	display: flex;
	flex: 0 0 100%;
	min-width: 0;
	scroll-snap-align: center;
	scroll-snap-stop: always;
}

@media (min-width: 1024px) {
	/* The stage narrows on wide screens so the line length stays readable. */
	.wh-feedback__slide {
		flex-basis: min(56rem, 100%);
		margin-inline: auto;
	}
}

/*
 * Off-stage quotes recede rather than disappear, which is what makes the rail
 * read as one moving strip instead of a set of separate boxes.
 */
.wh-feedback__slide > .wh-quote {
	width: 100%;
	transition:
		opacity 0.45s ease,
		transform 0.45s ease,
		box-shadow 0.25s ease,
		border-color 0.25s ease;
}

.wh-feedback__slider.is-enhanced .wh-feedback__slide:not(.is-active) > .wh-quote {
	opacity: 0.35;
	transform: scale(0.94);
}

/* Controls */

.wh-feedback__controls {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: 0.75rem 1rem;
	margin-top: 1.75rem;
}

.wh-feedback__controls[hidden] {
	display: none;
}

.wh-feedback__nav {
	display: grid;
	place-items: center;
	flex: none;
	width: 2.875rem;
	height: 2.875rem;
	padding: 0;
	border: 1px solid var(--border);
	border-radius: 9999px;
	background: color-mix(in oklab, var(--card) 82%, transparent);
	backdrop-filter: blur(8px);
	color: var(--foreground);
	cursor: pointer;
	transition:
		transform 0.2s ease,
		border-color 0.2s ease,
		background-color 0.2s ease,
		color 0.2s ease;
}

.wh-feedback__nav .wh-icon {
	width: 1.125rem;
	height: 1.125rem;
}

.wh-feedback__nav:hover {
	transform: scale(1.06);
	border-color: color-mix(in oklab, var(--primary) 45%, transparent);
	background: var(--gradient-accent);
	color: var(--cyan-foreground);
}

.wh-feedback__nav:focus-visible {
	outline: 2px solid var(--primary);
	outline-offset: 3px;
}

/* Client rail: a pill per quote, the active one widened and filling. */

.wh-feedback__rail {
	display: flex;
	align-items: center;
	gap: 0.5rem;
}

.wh-feedback__dot {
	position: relative;
	overflow: hidden;
	width: 1.5rem;
	height: 0.375rem;
	padding: 0;
	border: 0;
	border-radius: 9999px;
	background: color-mix(in oklab, var(--foreground) 18%, transparent);
	cursor: pointer;
	transition:
		width 0.35s ease,
		background-color 0.25s ease;
}

.wh-feedback__dot:hover {
	background: color-mix(in oklab, var(--primary) 45%, transparent);
}

.wh-feedback__dot:focus-visible {
	outline: 2px solid var(--primary);
	outline-offset: 3px;
}

.wh-feedback__dot[aria-selected="true"] {
	width: 3.25rem;
}

.wh-feedback__dot-fill {
	position: absolute;
	inset: 0;
	border-radius: inherit;
	background: var(--gradient-accent);
	transform: scaleX(0);
	transform-origin: left center;
}

.wh-feedback__dot[aria-selected="true"] .wh-feedback__dot-fill {
	transform: scaleX(1);
}

/*
 * While auto-advance is running the active pill fills in time with it, so the
 * motion is a readable progress cue rather than decoration.
 */
.wh-feedback__slider.is-playing .wh-feedback__dot[aria-selected="true"] .wh-feedback__dot-fill {
	animation: wh-feedback-fill var(--wh-feedback-delay, 7s) linear forwards;
}

@keyframes wh-feedback-fill {
	from {
		transform: scaleX(0);
	}

	to {
		transform: scaleX(1);
	}
}

.wh-feedback__counter {
	margin: 0;
	font-family: var(--font-display);
	font-size: 0.8125rem;
	font-variant-numeric: tabular-nums;
	letter-spacing: 0.04em;
	color: var(--muted-foreground);
}

.wh-feedback__counter-sep {
	margin-inline: 0.3125rem;
	opacity: 0.5;
}

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

	.wh-feedback__slide > .wh-quote,
	.wh-feedback__dot {
		transition: none;
	}

	.wh-feedback__slider.is-enhanced .wh-feedback__slide:not(.is-active) > .wh-quote {
		opacity: 1;
		transform: none;
	}

	.wh-feedback__slider.is-playing .wh-feedback__dot[aria-selected="true"] .wh-feedback__dot-fill {
		animation: none;
	}
}

/* Quote card */

.wh-quote {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: 1.25rem;
	border: 1px solid var(--border);
	border-radius: var(--radius-2xl);
	background: var(--card);
	box-shadow: var(--shadow-card);
	padding: 1.75rem;
	transition:
		transform 0.25s ease,
		box-shadow 0.25s ease,
		border-color 0.25s ease;
}

.wh-quote::before {
	content: "";
	position: absolute;
	inset-inline: 0;
	top: 0;
	height: 3px;
	border-start-start-radius: inherit;
	border-start-end-radius: inherit;
	background: var(--gradient-accent);
	opacity: 0;
	transition: opacity 0.25s ease;
}

.wh-quote:hover {
	box-shadow: var(--shadow-lift);
	border-color: color-mix(in oklab, var(--primary) 35%, transparent);
}

.wh-quote:hover::before,
.wh-feedback__slide.is-active > .wh-quote::before,
.wh-quote--featured::before {
	opacity: 1;
}

/*
 * Header row. The quotation mark sits in normal flow beside the rating. It was
 * previously absolutely positioned and ran across the first lines of the
 * quote — that overlap is what this row removes.
 */
.wh-quote__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	min-height: 2rem;
}

.wh-quote__mark {
	display: block;
	flex: none;
	font-family: var(--font-display);
	font-size: 3.5rem;
	line-height: 0.6;
	/* Optical alignment: the glyph hangs high inside its own box. */
	margin-block-start: 0.5rem;
	background: var(--gradient-accent);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
	pointer-events: none;
	user-select: none;
}

.wh-quote__stars {
	display: inline-flex;
	gap: 0.125rem;
	margin: 0;
}

.wh-quote__star .wh-icon {
	width: 1rem;
	height: 1rem;
	color: color-mix(in oklab, var(--muted-foreground) 40%, transparent);
}

.wh-quote__star.is-filled .wh-icon {
	color: var(--warning);
	fill: var(--warning);
}

.wh-quote__text {
	margin: 0;
	font-size: 1rem;
	line-height: 1.75;
	color: var(--foreground);
}

.wh-quote__text p:last-child {
	margin-bottom: 0;
}

.wh-quote__foot {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 0.75rem 1.5rem;
	margin-top: auto;
	padding-top: 1.125rem;
	border-top: 1px solid var(--border);
}

.wh-quote__author {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	min-width: 0;
}

.wh-quote__avatar,
.wh-quote__monogram {
	flex: none;
	width: 2.75rem;
	height: 2.75rem;
	border-radius: 9999px;
	object-fit: cover;
}

.wh-quote__monogram {
	display: grid;
	place-items: center;
	background: var(--gradient-accent);
	color: var(--cyan-foreground);
	font-family: var(--font-display);
	font-size: 0.875rem;
	font-weight: 700;
	letter-spacing: 0.02em;
}

.wh-quote__identity {
	display: grid;
	gap: 0.125rem;
	min-width: 0;
}

.wh-quote__name {
	font-size: 0.9375rem;
	font-weight: 600;
}

.wh-quote__role {
	font-size: 0.8125rem;
	color: var(--muted-foreground);
}

.wh-quote__project {
	display: inline-flex;
	align-items: center;
	gap: 0.375rem;
	font-size: 0.8125rem;
	font-weight: 600;
	color: var(--primary);
	text-decoration: none;
}

.wh-quote__project .wh-icon {
	width: 1rem;
	height: 1rem;
	transition: transform 0.2s ease;
}

.wh-quote__project:hover .wh-icon {
	transform: translateX(3px);
}

/* On the slider stage the quote is the whole composition, so it scales up. */

.wh-feedback__slide > .wh-quote {
	gap: 1.5rem;
	padding: 2rem 1.75rem;
	background: linear-gradient(
		140deg,
		color-mix(in oklab, var(--primary) 8%, var(--card)),
		var(--card) 58%
	);
}

@media (min-width: 768px) {
	.wh-feedback__slide > .wh-quote {
		padding: 2.75rem 3rem;
	}

	.wh-feedback__slide .wh-quote__mark {
		font-size: 5rem;
	}

	.wh-feedback__slide .wh-quote__text {
		font-size: 1.1875rem;
		line-height: 1.7;
		letter-spacing: -0.01em;
	}

	.wh-feedback__slide .wh-quote__avatar,
	.wh-feedback__slide .wh-quote__monogram {
		width: 3.25rem;
		height: 3.25rem;
		font-size: 1rem;
	}

	.wh-feedback__slide .wh-quote__name {
		font-size: 1rem;
	}
}

/* Featured variant — the card rendered as a lead outside the slider. */

.wh-quote--featured {
	padding: 2.25rem;
	background: linear-gradient(
		135deg,
		color-mix(in oklab, var(--primary) 7%, var(--card)),
		var(--card) 55%
	);
}

.wh-quote--featured .wh-quote__mark {
	font-size: 5rem;
}

@media (min-width: 1024px) {
	.wh-quote--featured {
		padding: 2.75rem;
	}

	.wh-quote--featured .wh-quote__text {
		font-size: 1.25rem;
		line-height: 1.65;
		letter-spacing: -0.01em;
	}
}

/* =========================================================================
   5. Portfolio project cards
   ====================================================================== */

.wh-project {
	display: flex;
	flex-direction: column;
	overflow: hidden;
}

.wh-project__media {
	position: relative;
	display: block;
	aspect-ratio: 16 / 9;
	overflow: hidden;
	background: var(--muted);
}

.wh-project__media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 0.4s ease;
}

.wh-project:hover .wh-project__media img {
	transform: scale(1.04);
}

/* Fallback plate when a project has no image of any kind. */
.wh-project__plate {
	display: grid;
	place-items: center;
	width: 100%;
	height: 100%;
	padding: 1.5rem;
	background: var(--gradient-hero);
	color: var(--navy-foreground);
	font-family: var(--font-display);
	font-size: 1.125rem;
	line-height: 1.3;
	text-align: center;
	letter-spacing: -0.01em;
}

.wh-project__shots {
	position: absolute;
	inset-inline-end: 0.75rem;
	bottom: 0.75rem;
	display: inline-flex;
	align-items: center;
	gap: 0.375rem;
	border-radius: 9999px;
	background: color-mix(in oklab, #000 62%, transparent);
	color: #fff;
	padding: 0.25rem 0.625rem;
	font-size: 0.6875rem;
	font-weight: 600;
	letter-spacing: 0.01em;
	backdrop-filter: blur(6px);
}

.wh-project__shots .wh-icon {
	width: 0.875rem;
	height: 0.875rem;
}

.wh-project .wh-card__body {
	display: flex;
	flex-direction: column;
	flex: 1;
}

/*
 * Whichever block comes first in the card footer carries the auto margin, so
 * the technology chips and the action row sit flush to the bottom as a group
 * and the buttons line up across a row of cards with uneven text.
 */
.wh-project .wh-project__foot {
	margin-top: auto;
}

.wh-project__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin-top: 1rem;
}

/* Two buttons share the row; a lone "View more" still spans it. */
.wh-project__actions .wh-btn {
	flex: 1 1 auto;
	justify-content: center;
	min-width: 7rem;
}

/* =========================================================================
   6. About page
   ====================================================================== */

.wh-about__facts {
	display: grid;
	gap: 1.25rem;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	border: 1px solid var(--border);
	border-radius: var(--radius-3xl);
	background: var(--card);
	box-shadow: var(--shadow-card);
	padding: 2rem 1.5rem;
	text-align: center;
}

@media (min-width: 768px) {
	.wh-about__facts {
		grid-auto-flow: column;
		grid-auto-columns: 1fr;
		grid-template-columns: none;
		padding: 2.25rem;
	}
}

.wh-about__fact-value {
	font-family: var(--font-display);
	font-size: clamp(1.875rem, 4vw, 2.75rem);
	line-height: 1;
	letter-spacing: -0.03em;
	background: var(--gradient-accent);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
}

.wh-about__fact-label {
	margin-top: 0.5rem;
	margin-inline-start: 0;
	font-size: 0.8125rem;
	line-height: 1.5;
	color: var(--muted-foreground);
}

/* Story + identity card */

.wh-about__story {
	display: grid;
	gap: 3rem;
	align-items: start;
}

@media (min-width: 1024px) {
	.wh-about__story {
		grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
		gap: 4rem;
	}
}

.wh-about__identity {
	position: sticky;
	top: calc(var(--header-height) + 1.5rem);
	border: 1px solid var(--border);
	border-radius: var(--radius-3xl);
	background: var(--card);
	box-shadow: var(--shadow-card);
	padding: 1.75rem;
}

.wh-about__identity-name {
	margin-top: 1.25rem;
	font-family: var(--font-display);
	font-size: 1.25rem;
	line-height: 1.25;
	letter-spacing: -0.02em;
}

.wh-about__identity-text {
	margin-top: 0.625rem;
	font-size: 0.875rem;
	line-height: 1.65;
	color: var(--muted-foreground);
}

.wh-about__identity .wh-specs {
	margin-top: 1.5rem;
}

.wh-about__social {
	margin-top: 1.5rem;
}

/* Purpose pillars */

.wh-about__purpose-grid {
	display: grid;
	gap: 1.5rem;
	margin-top: 3.5rem;
}

@media (min-width: 768px) {
	.wh-about__purpose-grid {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}
}

.wh-about__pillar {
	border: 1px solid color-mix(in oklab, #fff 14%, transparent);
	border-radius: var(--radius-2xl);
	background: color-mix(in oklab, #fff 5%, transparent);
	padding: 1.875rem;
	transition:
		border-color 0.25s ease,
		transform 0.25s ease;
}

.wh-about__pillar:hover {
	transform: translateY(-3px);
	border-color: color-mix(in oklab, var(--cyan) 50%, transparent);
}

.wh-about__pillar-icon {
	display: grid;
	place-items: center;
	width: 3rem;
	height: 3rem;
	border-radius: var(--radius-lg);
	background: color-mix(in oklab, var(--cyan) 18%, transparent);
	color: var(--cyan);
}

.wh-about__pillar-title {
	margin-top: 1.25rem;
	font-family: var(--font-display);
	font-size: 1.125rem;
	letter-spacing: -0.01em;
}

.wh-about__pillar-text {
	margin-top: 0.75rem;
	font-size: 0.9375rem;
	line-height: 1.7;
	color: color-mix(in oklab, var(--navy-foreground) 76%, transparent);
}

.wh-about__pillar--values .wh-checklist {
	margin-top: 1rem;
}

/* Delivery steps */

.wh-about__steps {
	display: grid;
	gap: 1.25rem;
	margin-top: 3.5rem;
	counter-reset: wh-step;
}

@media (min-width: 640px) {
	.wh-about__steps {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (min-width: 1024px) {
	.wh-about__steps {
		grid-template-columns: repeat(4, minmax(0, 1fr));
	}
}

.wh-about__step {
	position: relative;
	border: 1px solid var(--border);
	border-radius: var(--radius-2xl);
	background: var(--card);
	padding: 1.75rem;
}

/* A connecting rule between steps on the widest layout only. */
@media (min-width: 1024px) {
	.wh-about__step:not(:last-child)::after {
		content: "";
		position: absolute;
		inset-inline-end: -1.25rem;
		top: 3rem;
		width: 1.25rem;
		height: 1px;
		background: var(--border);
	}
}

.wh-about__step-number {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 2.25rem;
	height: 2.25rem;
	border-radius: var(--radius-md);
	background: color-mix(in oklab, var(--primary) 14%, transparent);
	color: var(--primary);
	font-family: var(--font-mono);
	font-size: 0.8125rem;
	font-weight: 700;
	letter-spacing: 0.06em;
}

.wh-about__step-title {
	margin-top: 1.125rem;
	font-family: var(--font-display);
	font-size: 1.0625rem;
	letter-spacing: -0.01em;
}

.wh-about__step-text {
	margin-top: 0.625rem;
	font-size: 0.875rem;
	line-height: 1.7;
	color: var(--muted-foreground);
}

/* Closing call to action */

.wh-about__cta {
	border: 1px solid var(--border);
	border-radius: var(--radius-3xl);
	background:
		radial-gradient(ellipse at 50% 0%, color-mix(in oklab, var(--primary) 14%, transparent), transparent 70%),
		var(--card);
	box-shadow: var(--shadow-card);
	padding: 3rem 1.5rem;
	text-align: center;
}

@media (min-width: 640px) {
	.wh-about__cta {
		padding: 4rem 3rem;
	}
}

.wh-about__cta-title {
	font-family: var(--font-display);
	font-size: clamp(1.5rem, 3.5vw, 2.25rem);
	line-height: 1.15;
	letter-spacing: -0.03em;
}

.wh-about__cta-text {
	max-width: 38rem;
	margin: 0.875rem auto 0;
	font-size: 1rem;
	line-height: 1.7;
	color: var(--muted-foreground);
}

/* =========================================================================
   7. Comments
   ====================================================================== */

.wh-comments {
	margin-top: 4rem;
	padding-top: 2.5rem;
	border-top: 1px solid var(--border);
}

.wh-comments__head {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 1rem;
	margin-bottom: 2.5rem;
}

.wh-comments__eyebrow {
	font-size: 0.6875rem;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.14em;
	color: var(--primary);
}

.wh-comments__title {
	margin-top: 0.375rem;
	margin-bottom: 0;
	font-family: var(--font-display);
	font-size: 1.5rem;
	letter-spacing: -0.02em;
}

.wh-comments__average {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	border: 1px solid var(--border);
	border-radius: 9999px;
	background: var(--card);
	padding: 0.375rem 0.875rem;
}

.wh-comments__average-score {
	font-family: var(--font-display);
	font-size: 1.125rem;
	font-weight: 700;
	line-height: 1;
}

.wh-comments__average-stars {
	display: inline-flex;
	gap: 0.0625rem;
}

.wh-comments__jump {
	margin-inline-start: auto;
}

/* Thread */

.wh-comments__list {
	display: grid;
	gap: 1.25rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.wh-comments__list .children {
	display: grid;
	gap: 1.25rem;
	margin-top: 1.25rem;
	margin-inline-start: 1.5rem;
	padding-inline-start: 1.5rem;
	border-inline-start: 2px solid var(--border);
	list-style: none;
}

@media (min-width: 640px) {
	.wh-comments__list .children {
		margin-inline-start: 2.5rem;
	}
}

.wh-comment {
	list-style: none;
}

.wh-comment__body {
	border: 1px solid var(--border);
	border-radius: var(--radius-2xl);
	background: var(--card);
	box-shadow: var(--shadow-card);
	padding: 1.375rem 1.5rem;
	transition: border-color 0.2s ease;
}

.wh-comment__body:hover {
	border-color: color-mix(in oklab, var(--primary) 30%, transparent);
}

/* The post author's replies read as answers, not as more comments. */
.wh-comment--author > .wh-comment__body {
	border-color: color-mix(in oklab, var(--primary) 40%, transparent);
	background: linear-gradient(
		120deg,
		color-mix(in oklab, var(--primary) 7%, var(--card)),
		var(--card) 60%
	);
}

.wh-comment__head {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	margin-bottom: 0.875rem;
}

.wh-comment__avatar img,
.wh-comment .avatar {
	display: block;
	width: 2.75rem;
	height: 2.75rem;
	border-radius: 9999px;
	object-fit: cover;
}

.wh-comment__identity {
	display: grid;
	gap: 0.125rem;
	min-width: 0;
}

.wh-comment__author {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	font-size: 0.9375rem;
	font-weight: 600;
}

.wh-comment__author a {
	color: inherit;
	text-decoration: none;
}

.wh-comment__author a:hover {
	color: var(--primary);
}

.wh-comment__badge {
	border-radius: 9999px;
	background: color-mix(in oklab, var(--primary) 16%, transparent);
	color: var(--primary);
	padding: 0.0625rem 0.5rem;
	font-size: 0.625rem;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.1em;
}

.wh-comment__date {
	font-size: 0.75rem;
	color: var(--muted-foreground);
	text-decoration: none;
}

.wh-comment__date:hover {
	color: var(--primary);
}

.wh-comment__rating {
	display: inline-flex;
	gap: 0.0625rem;
	margin-inline-start: auto;
}

.wh-comment__pending {
	border-radius: var(--radius-md);
	background: color-mix(in oklab, var(--warning) 16%, transparent);
	padding: 0.5rem 0.75rem;
	margin-bottom: 0.875rem;
	font-size: 0.75rem;
}

.wh-comment__text {
	font-size: 0.9375rem;
	line-height: 1.75;
	color: var(--foreground);
}

.wh-comment__text > :last-child {
	margin-bottom: 0;
}

.wh-comment__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 1rem;
	margin-top: 1rem;
	font-size: 0.8125rem;
	font-weight: 600;
}

.wh-comment__actions a {
	display: inline-flex;
	align-items: center;
	gap: 0.25rem;
	color: var(--primary);
	text-decoration: none;
}

.wh-comment__actions a:hover {
	text-decoration: underline;
}

.wh-comment__actions .wh-icon {
	width: 0.875rem;
	height: 0.875rem;
}

.wh-comment--ping {
	font-size: 0.875rem;
	color: var(--muted-foreground);
}

.wh-comment__ping-label {
	font-weight: 600;
	color: var(--foreground);
}

/* Response form */

.wh-comments .comment-respond {
	margin-top: 3rem;
	border: 1px solid var(--border);
	border-radius: var(--radius-3xl);
	background: var(--card);
	box-shadow: var(--shadow-card);
	padding: 1.75rem;
}

@media (min-width: 640px) {
	.wh-comments .comment-respond {
		padding: 2.25rem;
	}
}

.wh-comments .comment-reply-title {
	font-family: var(--font-display);
	font-size: 1.25rem;
	letter-spacing: -0.02em;
	margin-bottom: 0.5rem;
}

.wh-comments .comment-reply-title small {
	margin-inline-start: 0.75rem;
	font-size: 0.8125rem;
	font-weight: 500;
}

.wh-comments .comment-reply-title small a {
	color: var(--destructive);
	text-decoration: none;
}

.wh-comment-form__note {
	font-size: 0.8125rem;
	color: var(--muted-foreground);
	margin-bottom: 1.5rem;
}

.wh-comments .comment-form {
	display: grid;
	gap: 1.125rem;
	margin-top: 0;
}

@media (min-width: 640px) {
	.wh-comments .comment-form {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	.wh-comments .comment-form .comment-form-comment,
	.wh-comments .comment-form .comment-form-rating,
	.wh-comments .comment-form .comment-form-cookies-consent,
	.wh-comments .comment-form .form-submit,
	.wh-comments .comment-form .comment-notes,
	.wh-comments .comment-form .logged-in-as {
		grid-column: 1 / -1;
	}
}

.wh-comments .comment-form p {
	margin: 0;
}

.wh-comments .comment-form label {
	display: block;
	font-size: 0.875rem;
	font-weight: 500;
}

.wh-comments .comment-form input[type="text"],
.wh-comments .comment-form input[type="email"],
.wh-comments .comment-form input[type="url"],
.wh-comments .comment-form textarea,
.wh-comments .comment-form select {
	display: block;
	width: 100%;
	margin-top: 0.5rem;
	min-height: 2.75rem;
	padding: 0.5rem 0.75rem;
	border: 1px solid var(--input);
	border-radius: var(--radius-md);
	background: var(--background);
	color: var(--foreground);
	font-family: inherit;
	font-size: 0.9375rem;
	transition:
		border-color 0.2s ease,
		box-shadow 0.2s ease;
}

.wh-comments .comment-form textarea {
	min-height: 9rem;
	line-height: 1.7;
	resize: vertical;
}

.wh-comments .comment-form input:focus,
.wh-comments .comment-form textarea:focus,
.wh-comments .comment-form select:focus {
	border-color: var(--ring);
	outline: 2px solid transparent;
	box-shadow: 0 0 0 3px color-mix(in oklab, var(--ring) 25%, transparent);
}

.wh-comments .comment-form-cookies-consent {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	font-size: 0.8125rem;
	color: var(--muted-foreground);
}

.wh-comments .comment-form-cookies-consent input {
	width: 1rem;
	height: 1rem;
	accent-color: var(--primary);
}

.wh-comments .comment-form-cookies-consent label {
	font-weight: 400;
}

.wh-comments .comment-form .submit {
	justify-self: start;
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	height: 2.75rem;
	padding-inline: 1.5rem;
	border: 0;
	border-radius: var(--radius-md);
	background: var(--primary);
	color: var(--primary-foreground);
	font-size: 0.9375rem;
	font-weight: 600;
	cursor: pointer;
	transition: filter 0.2s ease;
}

.wh-comments .comment-form .submit:hover {
	filter: brightness(1.06);
}

.wh-comments .logged-in-as,
.wh-comments .comment-notes {
	font-size: 0.8125rem;
	color: var(--muted-foreground);
}

.wh-comments .logged-in-as a,
.wh-comments .comment-notes a {
	color: var(--primary);
}

/* =========================================================================
   Welcome popup

   Sits above the sticky header (z-index 50) and dropdowns (60), below the
   skip link (999). Hidden with the [hidden] attribute until the script opens
   it, so no JavaScript means no overlay.
   ====================================================================== */

.wh-popup[hidden] {
	display: none;
}

.wh-popup {
	position: fixed;
	inset: 0;
	z-index: 100;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 1.5rem;
}

.wh-popup__backdrop {
	position: absolute;
	inset: 0;
	background: rgb(0 0 0 / 55%);
	opacity: 0;
	transition: opacity 0.25s ease;
}

.wh-popup__dialog {
	position: relative;
	display: flex;
	flex-direction: column;
	width: 100%;
	max-width: 26rem;
	max-height: calc(100vh - 3rem);
	overflow: hidden auto;
	border: 1px solid var(--border);
	border-radius: var(--radius-3xl);
	background: var(--card);
	box-shadow: var(--shadow-lift);
	opacity: 0;
	transform: translateY(0.75rem) scale(0.98);
	transition:
		opacity 0.25s ease,
		transform 0.25s ease;
}

/* The script sets data-open once the element is painted, so the entry animates. */
.wh-popup[data-open="true"] .wh-popup__backdrop,
.wh-popup[data-open="true"] .wh-popup__dialog {
	opacity: 1;
}

.wh-popup[data-open="true"] .wh-popup__dialog {
	transform: translateY(0) scale(1);
}

.wh-popup__close {
	position: absolute;
	top: 0.75rem;
	right: 0.75rem;
	z-index: 2;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 2rem;
	height: 2rem;
	padding: 0;
	border: 1px solid var(--border);
	border-radius: 50%;
	background: var(--card);
	color: var(--muted-foreground);
	cursor: pointer;
	transition:
		background 0.15s ease,
		color 0.15s ease;
}

.wh-popup__close:hover,
.wh-popup__close:focus-visible {
	background: var(--secondary);
	color: var(--foreground);
}

.wh-popup__close .wh-icon {
	width: 1rem;
	height: 1rem;
}

.wh-popup__media {
	overflow: hidden;
	aspect-ratio: 16 / 9;
	background: var(--secondary);
}

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

.wh-popup__body {
	padding: 1.75rem;
}

.wh-popup__title {
	font-size: 1.375rem;
	font-weight: 700;
	line-height: 1.25;
}

.wh-popup__text {
	margin-top: 0.625rem;
	font-size: 0.9375rem;
	line-height: 1.65;
	color: var(--muted-foreground);
}

.wh-popup__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin-top: 1.25rem;
}

.wh-popup__actions .wh-btn {
	flex: 1 1 auto;
	justify-content: center;
}

/*
 * Countdown rail. The bar's transform and transition are set inline by the
 * script so that pausing and resuming continue from the frozen position — see
 * initWelcomePopup() in assets/js/main.js.
 */
.wh-popup__timer {
	height: 3px;
	background: var(--border);
}

.wh-popup__timer-bar {
	display: block;
	width: 100%;
	height: 100%;
	transform: scaleX(1);
	transform-origin: left center;
	background: var(--primary);
}

@media (prefers-reduced-motion: reduce) {
	.wh-popup__backdrop,
	.wh-popup__dialog {
		transition: none;
	}

	.wh-popup__dialog {
		transform: none;
	}
}

/* =========================================================================
   Animated hero visual (template-parts/parts/hero-motion.php)

   Used when the hero has no video and no image. Only transform, opacity,
   stroke-dashoffset and width animate, so it composites on the GPU. Sits on
   the hero's dark surface, so it uses the fixed light-on-dark values the rest
   of the hero uses rather than the theme tokens.
   ====================================================================== */

.wh-motion {
	overflow: hidden;
	border: 1px solid color-mix(in oklab, #fff 16%, transparent);
	border-radius: var(--radius-2xl);
	background: color-mix(in oklab, #000 26%, transparent);
	box-shadow: var(--shadow-lift);
	color: var(--cyan);
}

.wh-motion__chrome {
	display: flex;
	align-items: center;
	gap: 0.375rem;
	padding: 0.625rem 0.875rem;
	border-bottom: 1px solid color-mix(in oklab, #fff 12%, transparent);
	background: color-mix(in oklab, #000 22%, transparent);
}

.wh-motion__dot {
	width: 0.5rem;
	height: 0.5rem;
	border-radius: 9999px;
	background: color-mix(in oklab, #fff 26%, transparent);
}

.wh-motion__url {
	flex: 1;
	margin-inline-start: 0.5rem;
	padding: 0.1875rem 0.625rem;
	border-radius: 9999px;
	background: color-mix(in oklab, #fff 8%, transparent);
	color: color-mix(in oklab, var(--navy-foreground) 70%, transparent);
	font-family: var(--font-mono);
	font-size: 0.6875rem;
	letter-spacing: 0.02em;
}

.wh-motion__body {
	display: grid;
	gap: 1.25rem;
	padding: 1.25rem;
}

/* ---- Chart ---- */

.wh-motion__chart svg {
	display: block;
	width: 100%;
	height: 6rem;
}

.wh-motion__grid line {
	stroke: color-mix(in oklab, #fff 10%, transparent);
	stroke-width: 1;
}

/*
 * The line draws itself, then the fill fades in behind it. pathLength is not
 * set, so the dash values are generous enough to cover the real path length.
 */
.wh-motion__line {
	stroke-dasharray: 700;
	stroke-dashoffset: 700;
	animation: wh-motion-draw 3.2s ease-out forwards;
}

.wh-motion__area {
	opacity: 0;
	animation: wh-motion-fade 1.6s ease-out 1.9s forwards;
}

.wh-motion__pin {
	opacity: 0;
	animation:
		wh-motion-fade 0.5s ease-out 2.9s forwards,
		wh-motion-pulse 2.4s ease-in-out 3.4s infinite;
	transform-box: fill-box;
	transform-origin: center;
}

/* ---- Node rows ---- */

.wh-motion__nodes {
	display: grid;
	gap: 0.5rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.wh-motion__node {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr) 4.5rem;
	align-items: center;
	gap: 0.625rem;
	opacity: 0;
	animation: wh-motion-rise 0.5s ease-out forwards;

	/* Rows arrive in sequence rather than all at once. */
	animation-delay: calc(0.35s + (var(--i) * 0.14s));
}

.wh-motion__led {
	width: 0.4375rem;
	height: 0.4375rem;
	border-radius: 9999px;
	background: var(--cyan);
	box-shadow: 0 0 0 0 currentColor;
}

.wh-motion__led--ok {
	color: oklch(0.75 0.15 155);
	background: oklch(0.75 0.15 155);
	animation: wh-motion-blip 3s ease-in-out infinite;
	animation-delay: calc(var(--i) * 0.6s);
}

.wh-motion__led--busy {
	color: oklch(0.8 0.14 85);
	background: oklch(0.8 0.14 85);
	animation: wh-motion-blip 1.2s ease-in-out infinite;
}

.wh-motion__label {
	font-family: var(--font-mono);
	font-size: 0.75rem;
	color: color-mix(in oklab, var(--navy-foreground) 78%, transparent);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.wh-motion__bar {
	height: 3px;
	border-radius: 9999px;
	background: color-mix(in oklab, #fff 12%, transparent);
	overflow: hidden;
}

.wh-motion__bar-fill {
	display: block;
	height: 100%;
	width: 100%;
	border-radius: inherit;
	background: currentColor;
	transform-origin: left center;
	transform: scaleX(0);
	animation: wh-motion-load 2.6s ease-in-out infinite;
	animation-delay: calc(var(--i) * 0.45s);
}

/* ---- Deploy bar ---- */

.wh-motion__deploy {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	padding-top: 1rem;
	border-top: 1px solid color-mix(in oklab, #fff 10%, transparent);
}

.wh-motion__deploy-label {
	font-family: var(--font-mono);
	font-size: 0.6875rem;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: color-mix(in oklab, var(--navy-foreground) 62%, transparent);
}

.wh-motion__track {
	flex: 1;
	height: 5px;
	border-radius: 9999px;
	background: color-mix(in oklab, #fff 12%, transparent);
	overflow: hidden;
}

.wh-motion__progress {
	display: block;
	height: 100%;
	width: 40%;
	border-radius: inherit;
	background: linear-gradient(90deg, transparent, var(--cyan), transparent);
	animation: wh-motion-sweep 2.8s ease-in-out infinite;
}

@keyframes wh-motion-draw {
	to { stroke-dashoffset: 0; }
}

@keyframes wh-motion-fade {
	to { opacity: 1; }
}

@keyframes wh-motion-rise {
	from { opacity: 0; transform: translateY(0.4rem); }
	to   { opacity: 1; transform: translateY(0); }
}

@keyframes wh-motion-pulse {
	0%, 100% { transform: scale(1);   opacity: 1; }
	50%      { transform: scale(1.6); opacity: 0.55; }
}

@keyframes wh-motion-blip {
	0%, 100% { box-shadow: 0 0 0 0 color-mix(in oklab, currentColor 60%, transparent); }
	50%      { box-shadow: 0 0 0 4px color-mix(in oklab, currentColor 0%, transparent); }
}

@keyframes wh-motion-load {
	0%       { transform: scaleX(0); }
	55%, 75% { transform: scaleX(1); }
	100%     { transform: scaleX(1); }
}

@keyframes wh-motion-sweep {
	0%   { transform: translateX(-110%); }
	100% { transform: translateX(260%); }
}

/*
 * Reduced motion: show the finished state, not a frozen mid-animation frame.
 */
@media (prefers-reduced-motion: reduce) {
	.wh-motion__line,
	.wh-motion__area,
	.wh-motion__pin,
	.wh-motion__node,
	.wh-motion__led,
	.wh-motion__bar-fill,
	.wh-motion__progress {
		animation: none;
	}

	.wh-motion__line { stroke-dashoffset: 0; }

	.wh-motion__area,
	.wh-motion__pin,
	.wh-motion__node { opacity: 1; }

	.wh-motion__node { transform: none; }

	.wh-motion__bar-fill { transform: scaleX(0.72); }

	.wh-motion__progress { transform: translateX(0); width: 62%; }
}

/* The hero video fills the frame exactly as the image does. */
.wh-hero__video {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 16 / 10;
	object-fit: cover;
}

@media (prefers-reduced-motion: reduce) {
	/* Poster only — never unattended motion for a visitor who opted out. */
	.wh-hero__video { display: none; }

	.wh-hero__media-frame:has(.wh-hero__video) {
		aspect-ratio: 16 / 10;
		background-position: center;
		background-size: cover;
	}
}

/* =========================================================================
   Hero visual — developer at a desk

   A man types on a laptop; the change travels out to a phone and a desktop
   monitor and both screens switch to match. One 12-second loop, three passes
   of 4 seconds, running continuously.

   The timing is shared between three groups of animation and they have to stay
   in step, so all of it is expressed against the same two durations:
     4s  — one pass: code types on, then the signal travels
     12s — the full cycle of three interface states

   Like .wh-motion above, this sits on the hero dark surface and so uses fixed
   light-on-dark values rather than the light/dark theme tokens.
   ====================================================================== */

.wh-desk {
	--desk-pass: 4s;
	--desk-cycle: 12s;

	/* Figure and furniture. Deliberately desaturated: the screens are the
	   only things in the scene allowed to be bright. */
	--desk-skin: #e3ab84;
	--desk-skin-shade: #c88f68;
	--desk-hair: #2e3a4e;
	--desk-shirt: #2f6a7e;
	--desk-shirt-shade: #24576a;
	--desk-trouser: #1d2532;
	--desk-furniture: #16202c;
	--desk-surface: #1e2b3b;
	--desk-device: #0c1320;
	--desk-bezel: #26333f;
	--desk-glass: #05090f;

	overflow: hidden;
	border-radius: var(--radius-2xl);
	background:
		linear-gradient(180deg, color-mix(in oklab, #000 18%, transparent), color-mix(in oklab, #000 34%, transparent));
}

.wh-desk__svg {
	display: block;
	width: 100%;
	height: auto;
}

/* -------------------------------------------------------------------------
   Furniture
   ---------------------------------------------------------------------- */

.wh-desk__chair rect,
.wh-desk__chair ellipse {
	fill: var(--desk-furniture);
}

.wh-desk__frame rect {
	fill: var(--desk-furniture);
}

.wh-desk__apron { fill: var(--desk-furniture); }
.wh-desk__slab  { fill: var(--desk-surface); }
.wh-desk__edge  { fill: color-mix(in oklab, #fff 12%, transparent); }

/* The pool of screen light on the desk, breathing very slightly. */
.wh-desk__spill {
	animation: wh-desk-spill 6s ease-in-out infinite;
}

@keyframes wh-desk-spill {
	0%, 100% { opacity: 0.75; }
	50%      { opacity: 1; }
}

/* -------------------------------------------------------------------------
   The figure
   ---------------------------------------------------------------------- */

.wh-desk__legs polyline {
	fill: none;
	stroke: var(--desk-trouser);
	stroke-width: 22;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.wh-desk__shoe  { fill: #12181f; }
.wh-desk__shirt { fill: var(--desk-shirt); }
.wh-desk__neck  { fill: var(--desk-skin-shade); }
.wh-desk__face  { fill: var(--desk-skin); }
.wh-desk__nose  { fill: var(--desk-skin-shade); }
.wh-desk__hair  { fill: var(--desk-hair); }

.wh-desk__glasses circle,
.wh-desk__glasses path {
	fill: none;
	stroke: color-mix(in oklab, var(--cyan) 70%, #fff);
	stroke-width: 1.6;
	stroke-linecap: round;
}

/*
 * A slow nod, turning about the base of the neck rather than the middle of
 * the head, so the head pivots where a neck actually would.
 */
.wh-desk__head-group {
	transform-box: view-box;
	transform-origin: 169px 246px;
	animation: wh-desk-nod 5.5s ease-in-out infinite;
}

@keyframes wh-desk-nod {
	0%, 100% { transform: rotate(0deg); }
	50%      { transform: rotate(1.6deg); }
}

.wh-desk__arm {
	fill: none;
	stroke-width: 16;
	stroke-linecap: round;
	stroke-linejoin: round;
	transform-box: view-box;
}

.wh-desk__arm--far {
	stroke: var(--desk-shirt-shade, #24576a);
	stroke-width: 15;

	/* Rotates about its own elbow. */
	transform-origin: 184px 286px;
	animation: wh-desk-tap 0.46s ease-in-out infinite alternate;
}

.wh-desk__arm--near {
	stroke: var(--desk-shirt);
	transform-origin: 194px 284px;
	animation: wh-desk-tap 0.38s ease-in-out infinite alternate;
	animation-delay: -0.19s;
}

/* Small: the hands are tapping keys, not waving. */
@keyframes wh-desk-tap {
	from { transform: rotate(0deg); }
	to   { transform: rotate(-2.4deg); }
}

.wh-desk__hand {
	fill: var(--desk-skin);
	transform-box: view-box;
}

.wh-desk__hand--far {
	fill: var(--desk-skin-shade);
	transform-origin: 234px 266px;
	animation: wh-desk-hand 0.46s ease-in-out infinite alternate;
}

.wh-desk__hand--near {
	transform-origin: 250px 262px;
	animation: wh-desk-hand 0.38s ease-in-out infinite alternate;
	animation-delay: -0.19s;
}

@keyframes wh-desk-hand {
	from { transform: translateY(0); }
	to   { transform: translateY(-1.6px); }
}

/* -------------------------------------------------------------------------
   Devices
   ---------------------------------------------------------------------- */

.wh-desk__keyboard { fill: var(--desk-device); }
.wh-desk__keys     { fill: color-mix(in oklab, #fff 8%, transparent); }
.wh-desk__stand    { fill: var(--desk-device); }

.wh-desk__bezel {
	fill: var(--desk-bezel);
	stroke: color-mix(in oklab, #fff 14%, transparent);
	stroke-width: 1;
}

.wh-desk__screen { fill: var(--desk-glass); }
.wh-desk__chrome { fill: color-mix(in oklab, #fff 8%, transparent); }
.wh-desk__chrome-dot { fill: color-mix(in oklab, #fff 26%, transparent); }

/* -------------------------------------------------------------------------
   Code typing on the laptop

   Each line grows from its left edge, six of them in sequence, then the whole
   block clears and the next pass retypes it. transform-box: fill-box makes
   transform-origin resolve against each rect rather than the whole viewBox.
   ---------------------------------------------------------------------- */

.wh-desk__code {
	transform-box: fill-box;
	transform-origin: left center;

	/* Resting state is fully typed — that is what reduced motion falls back to. */
	transform: scaleX(1);
	animation: wh-desk-type var(--desk-pass) ease-out infinite;
	animation-delay: calc(var(--i) * 0.16s);
}

@keyframes wh-desk-type {
	0%, 3%    { transform: scaleX(0); }
	26%, 88%  { transform: scaleX(1); }
	96%, 100% { transform: scaleX(0); }
}

.wh-desk__code--kw  { fill: color-mix(in oklab, var(--chart-5) 85%, #fff); }
.wh-desk__code--fn  { fill: var(--cyan); }
.wh-desk__code--var { fill: color-mix(in oklab, #fff 72%, transparent); }
.wh-desk__code--str { fill: color-mix(in oklab, var(--chart-3) 85%, #fff); }
.wh-desk__code--num { fill: color-mix(in oklab, var(--chart-4) 88%, #fff); }
.wh-desk__code--op  { fill: color-mix(in oklab, #fff 45%, transparent); }

.wh-desk__caret {
	fill: var(--cyan);
	animation: wh-desk-blink 1s steps(2, end) infinite;
}

@keyframes wh-desk-blink {
	0%   { opacity: 1; }
	50%  { opacity: 0; }
	100% { opacity: 1; }
}

/* -------------------------------------------------------------------------
   The signal out to the devices
   ---------------------------------------------------------------------- */

.wh-desk__wire {
	fill: none;
	stroke: color-mix(in oklab, var(--cyan) 40%, transparent);
	stroke-width: 1;
	stroke-dasharray: 3 4;
	animation: wh-desk-wire 1.6s linear infinite;
}

@keyframes wh-desk-wire {
	to { stroke-dashoffset: -7; }
}

/*
 * The dots travel late in each pass, arriving just before the screens switch,
 * so the change looks like the cause of the switch rather than a coincidence.
 * Their paths are approximated in translate keyframes rather than offset-path,
 * which keeps them working everywhere without a fallback.
 */
.wh-desk__pulse {
	fill: #fff;
	opacity: 0;
	transform-box: view-box;
	animation: var(--desk-pass) ease-in-out infinite;
}

.wh-desk__pulse--phone {
	filter: drop-shadow(0 0 4px var(--cyan));
	animation-name: wh-desk-pulse-phone;
}

.wh-desk__pulse--monitor {
	filter: drop-shadow(0 0 4px var(--cyan));
	animation-name: wh-desk-pulse-monitor;
}

@keyframes wh-desk-pulse-phone {
	0%, 54%   { transform: translate(0, 0); opacity: 0; }
	58%       { opacity: 1; }
	70%       { transform: translate(30px, -14px); opacity: 1; }
	84%       { transform: translate(62px, 4px); opacity: 1; }
	88%, 100% { transform: translate(62px, 4px); opacity: 0; }
}

@keyframes wh-desk-pulse-monitor {
	0%, 54%   { transform: translate(0, 0); opacity: 0; }
	58%       { opacity: 1; }
	70%       { transform: translate(84px, -40px); opacity: 1; }
	84%       { transform: translate(168px, -38px); opacity: 1; }
	88%, 100% { transform: translate(168px, -38px); opacity: 0; }
}

/* -------------------------------------------------------------------------
   The interface states

   Three layouts per device. Each is visible for one pass, and the phone and
   the monitor switch on the same frame and to the same accent colour, which is
   what makes it read as one change landing on two screens.

   The delays look odd but are load-bearing. A negative delay starts the
   animation part-way in, so with a 12s cycle whose visible window is the first
   third: 0s shows during pass 1, -8s during pass 2, -4s during pass 3.
   ---------------------------------------------------------------------- */

.wh-desk__ui {
	opacity: 0;
	animation: wh-desk-swap var(--desk-cycle) steps(1, end) infinite;
}

/* Pass 1 is the resting state, so reduced motion has something to show. */
.wh-desk__ui--a {
	opacity: 1;
	animation-delay: 0s;
}

.wh-desk__ui--b { animation-delay: calc(var(--desk-cycle) / -1.5); }
.wh-desk__ui--c { animation-delay: calc(var(--desk-cycle) / -3); }

@keyframes wh-desk-swap {
	0%      { opacity: 1; }
	30%     { opacity: 1; }
	33.34%  { opacity: 0; }
	100%    { opacity: 0; }
}

/* The accent is what changes colour per pass; the greys stay put. */
.wh-desk__block { fill: color-mix(in oklab, #fff 12%, transparent); }
.wh-desk__rule  { fill: color-mix(in oklab, #fff 20%, transparent); }

.wh-desk__ui--a .wh-desk__accent { fill: var(--cyan); }
.wh-desk__ui--b .wh-desk__accent { fill: color-mix(in oklab, var(--chart-5) 85%, #fff); }
.wh-desk__ui--c .wh-desk__accent { fill: color-mix(in oklab, var(--chart-3) 85%, #fff); }

/* -------------------------------------------------------------------------
   Reduced motion

   Hold the finished first pass: code typed, first interface state on both
   screens, hands still, no travelling dots. The scene still explains itself
   as a picture — it simply stops moving.
   ---------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
	.wh-desk__spill,
	.wh-desk__head-group,
	.wh-desk__arm,
	.wh-desk__hand,
	.wh-desk__code,
	.wh-desk__caret,
	.wh-desk__wire,
	.wh-desk__ui {
		animation: none;
	}

	.wh-desk__head-group,
	.wh-desk__arm,
	.wh-desk__hand {
		transform: none;
	}

	/* The dots only exist as travel — remove them rather than park them. */
	.wh-desk__pulse {
		animation: none;
		display: none;
	}
}

/* =========================================================================
   Product modules

   A catalogue that has to stay legible at seventy-plus entries. The grid is
   six across at desktop and steps down to two on a phone; cards are light —
   icon, name, one line — because eighteen dashboard-weight cards on one screen
   read as noise. Every colour is a token, so the section inherits the site's
   palette in both themes rather than carrying its own.
   ====================================================================== */

.wh-modules__head {
	max-width: 44rem;
	margin-inline: auto;
	text-align: center;
}

/* Category filter */

.wh-modules__filters {
	display: flex;
	justify-content: center;
	margin-top: 2.5rem;
}

.wh-modules__filters .wh-filters {
	justify-content: center;
}

.wh-filter__count {
	margin-inline-start: 0.4rem;
	padding: 0.05rem 0.4rem;
	border-radius: 9999px;
	background: color-mix(in oklab, var(--foreground) 8%, transparent);
	font-size: 0.6875rem;
	font-variant-numeric: tabular-nums;
	color: var(--muted-foreground);
}

.wh-filter.is-active .wh-filter__count {
	background: color-mix(in oklab, var(--primary-foreground) 22%, transparent);
	color: inherit;
}

/* Grid */

.wh-modules__grid {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 0.75rem;
	margin-top: 2.5rem;
}

@media (min-width: 640px) {
	.wh-modules__grid {
		grid-template-columns: repeat(3, minmax(0, 1fr));
		gap: 1rem;
	}
}

@media (min-width: 1024px) {
	.wh-modules__grid {
		grid-template-columns: repeat(4, minmax(0, 1fr));
	}
}

@media (min-width: 1280px) {
	.wh-modules__grid {
		grid-template-columns: repeat(6, minmax(0, 1fr));
	}
}

/* Card */

.wh-module {
	display: flex;
}

.wh-module.is-hidden {
	display: none;
}

.wh-module__inner {
	position: relative;
	display: flex;
	flex-direction: column;
	width: 100%;
	height: 100%;
	border: 1px solid var(--border);
	border-radius: var(--radius-lg);
	background: var(--card);
	padding: 1.125rem 1rem 1.25rem;
	transition:
		border-color 0.25s ease,
		background 0.25s ease,
		transform 0.25s ease,
		box-shadow 0.25s ease;
}

/*
 * The whole card is the hit target when it links somewhere: the anchor covers
 * it via ::after rather than wrapping the heading, so the accessible name stays
 * the module name and the text remains selectable.
 */
.wh-module__link {
	position: static;
	color: inherit;
	text-decoration: none;
}

.wh-module__link::after {
	content: "";
	position: absolute;
	inset: 0;
	border-radius: inherit;
}

.wh-module__inner:has(.wh-module__link):hover {
	transform: translateY(-2px);
	border-color: color-mix(in oklab, var(--primary) 45%, transparent);
	background: color-mix(in oklab, var(--primary) 4%, var(--card));
	box-shadow: var(--shadow-card);
}

.wh-module__inner:has(.wh-module__link:focus-visible) {
	border-color: color-mix(in oklab, var(--primary) 55%, transparent);
}

.wh-module__icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.25rem;
	height: 2.25rem;
	border-radius: var(--radius-md);
	background: color-mix(in oklab, var(--primary) 12%, transparent);
	color: var(--primary);
	transition:
		transform 0.25s ease,
		background 0.25s ease;
}

.wh-module__icon .wh-icon {
	width: 1.125rem;
	height: 1.125rem;
}

.wh-module__inner:has(.wh-module__link):hover .wh-module__icon {
	transform: translateY(-1px) scale(1.06);
	background: color-mix(in oklab, var(--primary) 18%, transparent);
}

.wh-module__title {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.4rem;
	margin-top: 0.875rem;
	font-family: var(--font-display);
	font-size: 0.9375rem;
	line-height: 1.3;
	letter-spacing: -0.01em;
	color: var(--card-foreground);
}

.wh-module__badge {
	padding: 0.05rem 0.4rem;
	border-radius: 9999px;
	background: color-mix(in oklab, var(--primary) 14%, transparent);
	font-family: var(--font-sans);
	font-size: 0.625rem;
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--primary);
}

.wh-module__text {
	margin-top: 0.4rem;
	font-size: 0.8125rem;
	line-height: 1.55;
	color: var(--muted-foreground);
}

.wh-module__arrow {
	margin-top: auto;
	padding-top: 0.75rem;
	color: var(--primary);
	opacity: 0;
	transform: translateX(-0.25rem);
	transition:
		opacity 0.25s ease,
		transform 0.25s ease;
}

.wh-module__arrow .wh-icon {
	width: 1rem;
	height: 1rem;
}

.wh-module__inner:has(.wh-module__link):hover .wh-module__arrow,
.wh-module__inner:has(.wh-module__link:focus-visible) .wh-module__arrow {
	opacity: 1;
	transform: translateX(0);
}

/*
 * Without :has() the hover state simply does not fire — the card still reads
 * correctly, it just sits still. Nothing is lost but the flourish.
 */

/*
 * Compact variant — the homepage treatment.
 *
 * Icon centred above a short label, nothing else. At twelve to a screen the
 * description line is what turns a scannable index into a wall of text, so it
 * moves to the archive where people are reading rather than scanning.
 */

.wh-module--compact .wh-module__inner {
	align-items: center;
	padding: 1.5rem 0.75rem 1.375rem;
	text-align: center;
}

.wh-module--compact .wh-module__icon {
	width: 3rem;
	height: 3rem;
	border-radius: var(--radius-lg);
}

.wh-module--compact .wh-module__icon .wh-icon {
	width: 1.5rem;
	height: 1.5rem;
}

.wh-module--compact .wh-module__title {
	justify-content: center;
	margin-top: 0.875rem;
	font-size: 0.875rem;
	line-height: 1.35;
	text-wrap: balance;
}

@media (min-width: 640px) {
	.wh-module--compact .wh-module__title {
		font-size: 0.9375rem;
	}
}

/*
 * Inside a content column the six-across grid squeezes labels into four-line
 * slivers, so this variant caps it at three. Same cards, appropriate density
 * for the width they actually get.
 */

.wh-modules__grid--in-content {
	grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (min-width: 900px) {
	.wh-modules__grid--in-content {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}
}

@media (min-width: 1280px) {
	.wh-modules__grid--in-content {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}
}

/* View all, and the closing call to action */

.wh-modules__more {
	margin-top: 2rem;
	text-align: center;
}

.wh-modules__more .wh-icon {
	width: 1rem;
	height: 1rem;
}

.wh-modules__cta {
	display: flex;
	justify-content: center;
	margin-top: 2.5rem;
	padding-top: 2.5rem;
	border-top: 1px solid var(--border);
}

@media (prefers-reduced-motion: reduce) {
	.wh-module__inner,
	.wh-module__icon,
	.wh-module__arrow {
		transition: none;
	}

	.wh-module__inner:has(.wh-module__link):hover {
		transform: none;
	}

	.wh-module__inner:has(.wh-module__link):hover .wh-module__icon {
		transform: none;
	}
}

/* -------------------------------------------------------------------------
   Screenshot viewer

   A <dialog>, so the browser supplies the backdrop, the focus trap and Escape.
   Everything here is presentation: fill the viewport, keep the image whole
   rather than cropped, and put the controls where a thumb or a cursor already
   is.
   ---------------------------------------------------------------------- */

.wh-shots__open {
	position: relative;
	display: block;
	cursor: zoom-in;
}

.wh-shots__zoom {
	position: absolute;
	inset-block-start: 0.625rem;
	inset-inline-end: 0.625rem;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2rem;
	height: 2rem;
	border-radius: 9999px;
	background: color-mix(in oklab, var(--navy) 72%, transparent);
	color: #fff;
	opacity: 0;
	transition: opacity 0.2s ease;
}

.wh-shots__zoom .wh-icon {
	width: 1rem;
	height: 1rem;
}

.wh-shots__open:hover .wh-shots__zoom,
.wh-shots__open:focus-visible .wh-shots__zoom {
	opacity: 1;
}

.wh-lightbox {
	width: 100vw;
	max-width: 100vw;
	height: 100dvh;
	max-height: 100dvh;
	margin: 0;
	padding: 0;
	border: 0;
	background: oklch(0.14 0.03 258 / 0.97);
	color: #fff;
}

.wh-lightbox::backdrop {
	background: oklch(0.14 0.03 258 / 0.9);
}

.wh-lightbox__bar {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	padding: 0.75rem 1rem;
}

.wh-lightbox__count {
	margin: 0;
	font-family: var(--font-mono);
	font-size: 0.8125rem;
	font-variant-numeric: tabular-nums;
	color: color-mix(in oklab, #fff 72%, transparent);
}

.wh-lightbox__btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.75rem;
	height: 2.75rem;
	padding: 0;
	border: 1px solid color-mix(in oklab, #fff 22%, transparent);
	border-radius: 9999px;
	background: color-mix(in oklab, #fff 10%, transparent);
	color: #fff;
	font-size: 1.25rem;
	line-height: 1;
	cursor: pointer;
	transition:
		background 0.2s ease,
		transform 0.2s ease;
}

.wh-lightbox__btn:hover,
.wh-lightbox__btn:focus-visible {
	background: color-mix(in oklab, #fff 20%, transparent);
	transform: scale(1.05);
}

.wh-lightbox__btn:focus-visible {
	outline: 2px solid #fff;
	outline-offset: 2px;
}

.wh-lightbox__stage {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	height: calc(100dvh - 8.5rem);
	padding-inline: 1rem;
}

/* Contain, never crop: a cropped screenshot is the thing you opened it to avoid. */
.wh-lightbox__image {
	flex: 1;
	min-width: 0;
	max-width: 100%;
	max-height: 100%;
	height: 100%;
	object-fit: contain;
}

.wh-lightbox__caption {
	margin: 0;
	padding: 0.75rem 1rem 1.25rem;
	text-align: center;
	font-size: 0.875rem;
	color: color-mix(in oklab, #fff 76%, transparent);
}

@media (max-width: 640px) {
	.wh-lightbox__nav {
		position: fixed;
		inset-block-end: 1.25rem;
		z-index: 1;
	}

	.wh-lightbox__nav--prev {
		inset-inline-start: 1.25rem;
	}

	.wh-lightbox__nav--next {
		inset-inline-end: 1.25rem;
	}

	.wh-lightbox__stage {
		padding-inline: 0.5rem;
	}
}

@media (prefers-reduced-motion: reduce) {
	.wh-shots__zoom,
	.wh-lightbox__btn {
		transition: none;
	}

	.wh-lightbox__btn:hover,
	.wh-lightbox__btn:focus-visible {
		transform: none;
	}
}
