/*
 * MEI design tokens, ported verbatim from the Next.js app's globals.css so the
 * two stacks look identical during the migration.
 *
 * Hand-written rather than compiled: this project has no Node, so there is no
 * Tailwind build step. Everything here is plain CSS served straight from
 * public/ — no bundler, no manifest, nothing to build before deploying.
 *
 * Two themes over one set of variable names, so components read
 * var(--foreground) and adapt without knowing which theme is active.
 * Light is the default; `data-theme="dark"` on <html> switches.
 */

:root,
:root[data-theme="light"] {
	color-scheme: light;

	--background: #f4f7fa;
	--foreground: #0e1620;
	/* Deep teal: the bright cyan is unreadable as text on a light surface. */
	--mei-cyan: #0b8f7a;
	--mei-green: #2f9e63;
	--mei-steel: #2f6fa5;
	--mei-muted: #5a6b80;

	--surface-1: rgba(255, 255, 255, 0.9);
	--surface-2: rgba(255, 255, 255, 0.98);
	--overlay-weak: rgba(14, 22, 32, 0.04);
	--overlay: rgba(14, 22, 32, 0.07);
	--hairline: rgba(14, 22, 32, 0.11);
	--hairline-strong: rgba(14, 22, 32, 0.18);
	--panel-shadow: 0 1px 0 0 rgba(255, 255, 255, 0.7) inset,
		0 14px 34px -24px rgba(12, 24, 40, 0.5);
	--on-accent: #ffffff;
	--panel-radius: 14px;
	/*
	 * Opaque, for surfaces that float over the page: fixed and sticky panels.
	 * --surface-1/2 are translucent overlays meant to sit *on* the background,
	 * which is invisible for anything the page scrolls underneath.
	 */
	--surface-solid: #ffffff;
}

:root[data-theme="dark"] {
	color-scheme: dark;

	--background: #070b11;
	--foreground: #e8eef5;
	--mei-cyan: #4dd9c0;
	--mei-green: #7fd9a3;
	--mei-steel: #3f7fb5;
	--mei-muted: #8d9bb0;

	--surface-1: rgba(255, 255, 255, 0.028);
	--surface-2: rgba(255, 255, 255, 0.055);
	--overlay-weak: rgba(255, 255, 255, 0.05);
	--overlay: rgba(255, 255, 255, 0.09);
	--hairline: rgba(255, 255, 255, 0.09);
	--hairline-strong: rgba(255, 255, 255, 0.14);
	--panel-shadow: 0 1px 0 0 rgba(255, 255, 255, 0.05) inset,
		0 18px 40px -28px rgba(0, 0, 0, 0.9);
	--on-accent: #0a0e14;
	/*
	 * Matches what --surface-2 resolves to over --background, so a floating
	 * panel reads the same as an inline one. At 5.5% white the translucent
	 * value was effectively see-through here, which is why the chat widget
	 * vanished in dark mode.
	 */
	--surface-solid: #141a22;
}

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

html,
body {
	margin: 0;
	padding: 0;
}

body {
	background: var(--background);
	color: var(--foreground);
	font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
	line-height: 1.6;
	min-height: 100dvh;
	display: flex;
	flex-direction: column;
	-webkit-font-smoothing: antialiased;
}

main {
	flex: 1;
	/*
	 * Breathing room above the footer. Pages whose last element is a .section
	 * already carry its 3rem, but ones ending in a panel or grid — contact,
	 * inquiry, the catalogues — ran flush into the footer's top border.
	 */
	padding-block-end: 3.5rem;
}

a {
	color: inherit;
}

/* ---------- layout ---------- */

.wrap {
	max-width: 80rem;
	margin-inline: auto;
	width: 100%;
	padding-inline: 1.5rem;
}

/* ---------- typography ---------- */

.eyebrow {
	font-size: 0.75rem;
	letter-spacing: 0.16em;
	color: var(--mei-muted);
	margin: 0 0 1rem;
}

h1 {
	font-size: clamp(2.5rem, 6vw, 4.25rem);
	line-height: 1.05;
	margin: 0 0 1.5rem;
	max-width: 20ch;
	font-weight: 600;
}

h1 em {
	font-style: normal;
	color: var(--mei-cyan);
}

.lede {
	color: var(--mei-muted);
	font-size: 1.125rem;
	max-width: 46rem;
	margin: 0;
}

.hero {
	padding-block: 5rem;
}

/* ---------- the one card surface ---------- */

.panel {
	position: relative;
	background: linear-gradient(180deg, var(--surface-2), var(--surface-1));
	border: 1px solid var(--hairline);
	border-radius: var(--panel-radius);
	box-shadow: var(--panel-shadow);
}

.accent {
	color: var(--mei-cyan);
}

/* ---------- header / footer ---------- */

.site-header,
.site-footer {
	border-block: 1px solid var(--hairline);
	background: var(--overlay-weak);
}

.site-header {
	border-top: 0;
}

/*
 * Header only. This used to cover .site-footer .wrap as well, but at 0-2-0 it
 * outranked .footer-grid's own 0-1-0 rules and quietly won every property they
 * shared — including display, which is why the footer's grid-template-columns
 * never applied. Both footer .wrap elements (.footer-grid, .footer-base) lay
 * themselves out.
 */
.site-header .wrap {
	display: flex;
	align-items: center;
	gap: 1.5rem;
	padding-block: 1rem;
}

.site-header nav {
	display: flex;
	gap: 1.25rem;
	margin-left: auto;
	flex-wrap: wrap;
}

.site-header nav a {
	text-decoration: none;
	color: var(--mei-muted);
	font-size: 0.9375rem;
}

.site-header nav a:hover,
.site-header nav a[aria-current="page"] {
	color: var(--mei-cyan);
}

.brand {
	font-weight: 700;
	letter-spacing: 0.04em;
	text-decoration: none;
}

.site-footer {
	margin-top: auto;
	border-bottom: 0;
	color: var(--mei-muted);
	font-size: 0.875rem;
}

/* ---------- page furniture ---------- */

.page-title {
	font-size: 2.25rem;
	margin: 2.5rem 0 1.5rem;
}

.section {
	padding-block: 3rem;
	border-top: 1px solid var(--hairline);
}

.section-head {
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
	gap: 1rem;
	margin-bottom: 2rem;
	flex-wrap: wrap;
}

.section-head h2 {
	margin: 0;
	font-size: 1.75rem;
}

.muted {
	color: var(--mei-muted);
}

.crumbs {
	font-size: 0.8125rem;
	color: var(--mei-muted);
	padding-block: 1rem;
}

.crumbs a {
	text-decoration: none;
}

.crumbs a:hover {
	color: var(--mei-cyan);
}

/* ---------- grid + cards ---------- */

.grid {
	display: grid;
	gap: 1.25rem;
	align-items: stretch;
}

.grid-3 {
	grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}

/*
 * Cards are full-height flex columns so every card in a row ends level, and
 * the body grows to absorb the difference. Without this a two-line title makes
 * one card taller than its neighbours and the badges stop lining up.
 */
.card {
	display: flex;
	flex-direction: column;
	height: 100%;
	text-decoration: none;
	overflow: hidden;
	transition: border-color 0.25s, transform 0.25s, box-shadow 0.25s;
}

.card:hover {
	transform: translateY(-2px);
	border-color: color-mix(in srgb, var(--mei-cyan) 40%, transparent);
}

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

.card-body {
	padding: 1.25rem;
	display: flex;
	flex-direction: column;
	flex: 1;
	gap: 0.5rem;
}

.card-body h3 {
	margin: 0;
	font-size: 1rem;
	line-height: 1.35;
}

.card-body p {
	margin: 0;
	font-size: 0.875rem;
	/* Clamped so a long excerpt cannot stretch one card past the others. */
	display: -webkit-box;
	-webkit-line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

/* Pushed to the bottom edge so badges align across a row. */
.card-body .badge {
	align-self: flex-start;
	margin-top: auto;
}

.badge {
	display: inline-block;
	font-size: 0.6875rem;
	letter-spacing: 0.05em;
	background: var(--overlay);
	padding: 0.125rem 0.5rem;
	border-radius: 999px;
}

/* ---------- detail ---------- */

.detail {
	display: grid;
	grid-template-columns: 1fr;
	gap: 2.5rem;
	padding-block: 2rem;
}

@media (min-width: 48rem) {
	.detail {
		grid-template-columns: 1fr 1fr;
	}
}

.media img {
	width: 100%;
	display: block;
	border-radius: var(--panel-radius);
}

.media.wide img {
	aspect-ratio: 16 / 9;
	object-fit: cover;
}

.thumbs {
	display: flex;
	gap: 0.625rem;
	margin-top: 0.75rem;
	flex-wrap: wrap;
}

.thumbs img {
	width: 4rem;
	height: 4rem;
	object-fit: cover;
	border-radius: 0.5rem;
	border: 1px solid var(--hairline);
}

.video iframe,
.video video {
	width: 100%;
	aspect-ratio: 16 / 9;
	border: 0;
	display: block;
	border-radius: var(--panel-radius);
	background: #000;
}

/* ---------- forms + lists ---------- */

.filters {
	display: flex;
	gap: 0.625rem;
	flex-wrap: wrap;
	margin-bottom: 2rem;
}

.input {
	padding: 0.625rem 0.875rem;
	border-radius: 0.5rem;
	border: 1px solid var(--hairline);
	background: var(--overlay-weak);
	color: var(--foreground);
	font: inherit;
	min-width: 12rem;
}

.input:focus {
	outline: none;
	border-color: var(--mei-cyan);
}

.btn {
	display: inline-block;
	background: var(--mei-cyan);
	color: var(--on-accent);
	border: 0;
	padding: 0.625rem 1.25rem;
	border-radius: 0.5rem;
	font: inherit;
	font-weight: 500;
	cursor: pointer;
	text-decoration: none;
}

.btn:hover {
	filter: brightness(1.1);
}

.feature-list,
.thread-list,
.chips {
	list-style: none;
	padding: 0;
	margin: 0;
}

.feature-list li {
	padding: 0.625rem 0;
	border-top: 1px solid var(--hairline);
	color: var(--mei-muted);
}

.thread-list li {
	margin-bottom: 0.75rem;
}

.thread-list a {
	text-decoration: none;
}

.chips {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
}

.chips li,
.chips.inline span {
	background: var(--overlay-weak);
	border: 1px solid var(--hairline);
	border-radius: 999px;
	padding: 0.25rem 0.75rem;
	font-size: 0.8125rem;
}

.specs {
	width: 100%;
	border-collapse: collapse;
}

.specs th,
.specs td {
	text-align: left;
	padding: 0.75rem 0;
	border-bottom: 1px solid var(--hairline);
}

.specs th {
	color: var(--mei-muted);
	font-weight: 500;
}

/*
 * A CMS page holds prose *and* layout blocks. Capping the whole container at a
 * reading measure squeezed card grids into half the page; capping only the
 * text keeps prose readable and lets grids and accordions use the width.
 */
.rich {
	max-width: none;
}

.rich > p,
.rich > h2,
.rich > h3,
.rich > h4,
.rich > ul,
.rich > ol,
.rich > blockquote,
.rich > table {
	max-width: 46rem;
}

/* Long-form lists of short items read better in columns than one tall stack. */
.rich > ul.feature-list {
	max-width: none;
	columns: 2;
	column-gap: 2.5rem;
}

.rich > ul.feature-list li {
	break-inside: avoid;
}

@media (max-width: 52rem) {
	.rich > ul.feature-list {
		columns: 1;
	}
}

/* =========================================================================
   RESTORED BLOCKS
   Recovered verbatim from earlier reads in this session after a bad edit
   deleted them. See the note at the end of this file for what is still gone.
   ========================================================================= */

/* ---------- visitor chat widget ---------- */

/*
 * The [hidden] attribute is only `display: none` in the UA stylesheet, so any
 * rule setting display wins over it. Everything below uses display, so this has
 * to reassert it - otherwise the chat panel sits open on every page.
 */
[hidden] {
	display: none !important;
}

/* The spam honeypot. Must stay off-screen: visible, real visitors fill it in
   and every genuine enquiry is silently discarded. */
.hp {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	opacity: 0;
	pointer-events: none;
}

.chat-launcher {
	position: fixed;
	right: 1.25rem;
	bottom: 1.25rem;
	z-index: 50;
	width: 3.5rem;
	height: 3.5rem;
	border: 0;
	border-radius: 50%;
	background: var(--mei-cyan);
	color: var(--on-accent);
	font-size: 1.5rem;
	line-height: 1;
	cursor: pointer;
	box-shadow: 0 8px 28px -6px rgba(0, 0, 0, 0.45);
}

.chat-panel {
	position: fixed;
	right: 0.75rem;
	bottom: 6rem;
	z-index: 50;
	width: calc(100vw - 1.5rem);
	max-height: calc(100dvh - 9rem);
	display: flex;
	flex-direction: column;
	overflow: hidden;
	/* Fixed over the page, so it cannot use the translucent panel surface. */
	background: var(--surface-solid);
}

@media (min-width: 30rem) {
	.chat-panel {
		right: 1.25rem;
		width: 23rem;
		max-height: 32rem;
	}
}

.chat-header {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	padding: 0.875rem 1rem;
	border-bottom: 1px solid var(--hairline);
	flex-shrink: 0;
}

.chat-title {
	margin: 0;
	font-weight: 600;
	font-size: 0.9375rem;
}

.chat-status {
	margin: 0;
	font-size: 0.6875rem;
	color: var(--mei-muted);
}

.chat-header .linkish {
	margin-left: auto;
	font-size: 0.75rem;
}

.chat-header #chat-close {
	margin-left: 0;
	font-size: 1.25rem;
}

.chat-body {
	padding: 1rem;
	overflow-y: auto;
}

.chat-thread {
	flex: 1;
	overflow-y: auto;
	padding: 1rem;
	min-height: 10rem;
}

.chat-composer {
	display: flex;
	gap: 0.5rem;
	padding: 0.75rem;
	border-top: 1px solid var(--hairline);
	flex-shrink: 0;
}

.chat-composer .input {
	flex: 1;
	min-width: 0;
	border-radius: 999px;
}

.chat-error {
	color: #d63638;
	font-size: 0.8125rem;
	margin: 0;
}

/* ---------- lightbox ---------- */

.lightbox {
	position: fixed;
	inset: 0;
	z-index: 100;
	background: rgba(0, 0, 0, 0.85);
	display: grid;
	place-items: center;
	padding: 2rem;
}

.lightbox[hidden] {
	display: none;
}

.lightbox img {
	max-width: 100%;
	max-height: 100%;
	object-fit: contain;
}

/* ---------- hero ---------- */

.hero-bg {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
	/*
	 * Below the scrim, which sits at -1. Without this the image painted over
	 * the scrim, so the darkening gradient did nothing and the slide's white
	 * title and copy were invisible against a bright photograph — the hero
	 * looked like it was showing pictures and no text.
	 */
	z-index: -2;
}

.hero-scrim {
	position: absolute;
	inset: 0;
	z-index: -1;
	background:
		linear-gradient(90deg, rgba(7, 11, 17, 0.94) 0%, rgba(7, 11, 17, 0.6) 45%, rgba(7, 11, 17, 0.15) 100%),
		linear-gradient(0deg, rgba(7, 11, 17, 0.85) 0%, transparent 45%);
}

.hero-content {
	padding-block: 5rem;
	color: #e8eef5;
	max-width: 100%;
}

.hero-content .eyebrow,
.hero-content .lede {
	color: rgba(232, 238, 245, 0.72);
}

.hero-content h1 {
	font-size: clamp(2.75rem, 6vw, 5rem);
	max-width: 16ch;
	margin-bottom: 1.25rem;
}

.hero-content .lede {
	max-width: 38rem;
	margin-bottom: 2rem;
}

.hero-controls {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 2rem;
	display: flex;
	align-items: center;
	gap: 1rem;
}

.hero-model {
	position: absolute;
	inset-block: 0;
	right: 0;
	width: min(52%, 40rem);
	z-index: 0;
	cursor: grab;
}

.hero-model:active {
	cursor: grabbing;
}

.hero-model canvas {
	width: 100%;
	height: 100%;
	display: block;
}

@media (max-width: 60rem) {
	.hero-model { display: none; }
}

/* ---------- clients marquee ---------- */

.clients {
	padding-block: 3rem;
	border-top: 1px solid var(--hairline);
}

.marquee {
	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);
}

.marquee-track {
	display: flex;
	gap: 3rem;
	width: max-content;
	animation: marquee 42s linear infinite;
	color: var(--mei-muted);
	font-size: 0.8125rem;
	letter-spacing: 0.14em;
}

.marquee:hover .marquee-track {
	animation-play-state: paused;
}

@keyframes marquee {
	to { transform: translateX(-50%); }
}

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

/* ---------- about grid ---------- */

.about-grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: 2.5rem;
	align-items: start;
}

/* ---------- footer detail ---------- */

.footer-strong {
	display: block;
	color: var(--foreground);
}

.footer-address {
	display: block;
	white-space: pre-line;
}

.footer-compliance {
	margin-top: 0.75rem;
	font-size: 0.8125rem;
}

/* ---------- search palette ---------- */

.search-input {
	width: 100%;
	margin-bottom: 0.75rem;
}

.search-results {
	max-height: 22rem;
	overflow-y: auto;
	color: var(--mei-muted);
	font-size: 0.875rem;
}

/* ---------- form primitives (restored verbatim) ---------- */

.stack label {
	display: flex;
	flex-direction: column;
	gap: 0.375rem;
	font-weight: 500;
}

/*
 * A checkbox belongs beside its text, not stacked above it — the column
 * direction above is for a field label sitting over its input.
 *
 * The class is what guarantees it; the :has() selectors are a safety net so a
 * label that forgets the class still lines up on any current browser.
 */
.stack label.check,
.stack label:has(> input[type="checkbox"]),
.stack label:has(> input[type="radio"]) {
	flex-direction: row;
	align-items: center;
	gap: 0.5rem;
	font-weight: 400;
}

.stack label.check input[type="checkbox"],
.stack label.check input[type="radio"] {
	flex: 0 0 auto;
	margin: 0;
}

/* =========================================================================
   RECONSTRUCTED — these blocks were deleted and I had no copy of the
   originals, so they are rebuilt to match the surrounding design system.
   They will not be pixel-identical to what was there before.
   ========================================================================= */

/* A button that reads as a link: used for destructive and secondary actions
   inside table rows and panels, where a full button would shout. */
.linkish {
	background: none;
	border: 0;
	padding: 0;
	font: inherit;
	color: var(--mei-cyan);
	cursor: pointer;
	text-decoration: none;
}

.linkish:hover {
	text-decoration: underline;
}

.linkish.danger,
.danger {
	color: #d63638;
}

:root[data-theme="dark"] .linkish.danger,
:root[data-theme="dark"] .danger {
	color: #f2777a;
}

/* Monospace, for fields holding markup, paths or IDs. */
.mono {
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: 0.875rem;
}

.notice-error {
	border-color: color-mix(in srgb, #d63638 45%, transparent);
	background: color-mix(in srgb, #d63638 10%, transparent);
	color: inherit;
}

ul.notice-error {
	list-style: none;
	margin: 0 0 1rem;
	padding: 0.75rem 1rem;
	display: flex;
	flex-direction: column;
	gap: 0.25rem;
}

.captcha-field,
.cv-field {
	display: flex;
	flex-direction: column;
	gap: 0.375rem;
	font-size: 0.875rem;
}

.captcha-field .input {
	max-width: 9rem;
}

.center {
	text-align: center;
}

/* Live dot in the homepage eyebrow. */
.pulse {
	display: inline-block;
	width: 0.5rem;
	height: 0.5rem;
	margin-right: 0.4rem;
	border-radius: 50%;
	background: var(--mei-green);
	box-shadow: 0 0 0 0 color-mix(in srgb, var(--mei-green) 70%, transparent);
	animation: pulse 2s ease-out infinite;
}

@keyframes pulse {
	to { box-shadow: 0 0 0 0.6rem transparent; }
}

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

/* ---------- hero controls ---------- */

.hero-arrow {
	width: 2.5rem;
	height: 2.5rem;
	border: 1px solid rgba(232, 238, 245, 0.35);
	border-radius: 50%;
	background: rgba(7, 11, 17, 0.4);
	color: #e8eef5;
	cursor: pointer;
	font-size: 1rem;
	line-height: 1;
	transition: background 0.2s, border-color 0.2s;
}

.hero-arrow:hover {
	background: var(--mei-cyan);
	border-color: var(--mei-cyan);
	color: var(--on-accent);
}

.slider-dots {
	display: flex;
	gap: 0.4rem;
	align-items: center;
}

.slider-dots button {
	width: 0.5rem;
	height: 0.5rem;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: rgba(232, 238, 245, 0.4);
	cursor: pointer;
	transition: background 0.2s, width 0.2s;
}

.slider-dots button[data-active] {
	width: 1.4rem;
	border-radius: 999px;
	background: var(--mei-cyan);
}

/* ---------- product viewer ---------- */

.zoomable {
	cursor: zoom-in;
}

.model-hint {
	position: absolute;
	left: 50%;
	bottom: 0.75rem;
	transform: translateX(-50%);
	margin: 0;
	padding: 0.3rem 0.7rem;
	border-radius: 999px;
	background: var(--overlay);
	color: var(--mei-muted);
	font-size: 0.6875rem;
	pointer-events: none;
}

/* ---------- header interest badge ---------- */

.interest-button {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
}

.interest-count {
	display: inline-grid;
	place-items: center;
	min-width: 1.25rem;
	height: 1.25rem;
	padding-inline: 0.3rem;
	border-radius: 999px;
	background: var(--mei-cyan);
	color: var(--on-accent);
	font-size: 0.6875rem;
	font-weight: 700;
}

/* ---------- forum ---------- */

.thread {
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
}

.reply {
	margin-bottom: 0.75rem;
}

/* ---------- admin: media library ---------- */

.media-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
	gap: 0.875rem;
}

.media-tile {
	display: flex;
	flex-direction: column;
	gap: 0.4rem;
	padding: 0.5rem;
	border: 1px solid var(--hairline);
	background: var(--surface-2);
	cursor: pointer;
	text-align: left;
	font: inherit;
	color: inherit;
	overflow: hidden;
}

.media-tile:hover {
	border-color: var(--mei-cyan);
}

.media-tile img {
	width: 100%;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	border-radius: 0.35rem;
	display: block;
}

.media-tile figcaption {
	font-size: 0.6875rem;
	color: var(--mei-muted);
	overflow-wrap: anywhere;
	line-height: 1.35;
}

/* Stands in for the thumbnail when the file is not an image. */
.media-ext {
	display: grid;
	place-items: center;
	aspect-ratio: 4 / 3;
	border-radius: 0.35rem;
	background: var(--overlay);
	color: var(--mei-muted);
	font-size: 0.8125rem;
	font-weight: 700;
	letter-spacing: 0.08em;
}

.media-field {
	display: flex;
	flex-direction: column;
	gap: 0.4rem;
}

.media-field-label {
	font-weight: 500;
	font-size: 0.875rem;
}

.media-preview {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
}

.media-preview img {
	max-height: 4.5rem;
	border-radius: 0.35rem;
	border: 1px solid var(--hairline);
}

.media-actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.5rem;
}

/* ---------- admin: chat inbox ---------- */

.convo {
	display: grid;
	grid-template-columns: 1fr;
	gap: 1.25rem;
	align-items: start;
}

@media (min-width: 60rem) {
	.convo {
		grid-template-columns: minmax(14rem, 20rem) 1fr;
	}
}

.convo-list {
	display: flex;
	flex-direction: column;
	gap: 0.25rem;
	max-height: 32rem;
	overflow-y: auto;
	overscroll-behavior: contain;
}

.convo-person {
	display: grid;
	gap: 0.15rem;
	padding: 0.7rem 0.85rem;
	border: 1px solid transparent;
	border-radius: 0.5rem;
	text-decoration: none;
	color: inherit;
	font-size: 0.875rem;
}

.convo-person:hover {
	background: var(--overlay-weak);
}

.convo-person.is-active {
	border-color: color-mix(in srgb, var(--mei-cyan) 45%, transparent);
	background: var(--overlay);
}

.convo-person .muted {
	font-size: 0.75rem;
}

.convo-panel {
	display: flex;
	flex-direction: column;
	min-width: 0;
}

.bubble-row {
	display: flex;
	margin-bottom: 0.6rem;
}

/* Staff replies sit right, the visitor left — the usual convention. */
.bubble-row.mine {
	justify-content: flex-end;
}

.bubble {
	display: flex;
	flex-direction: column;
	gap: 0.3rem;
	max-width: min(34rem, 80%);
	padding: 0.6rem 0.85rem;
	border-radius: 0.85rem;
	background: var(--overlay-weak);
	border: 1px solid var(--hairline);
	font-size: 0.875rem;
	line-height: 1.5;
	overflow-wrap: anywhere;
}

.bubble-row.mine .bubble {
	background: color-mix(in srgb, var(--mei-cyan) 14%, transparent);
	border-color: color-mix(in srgb, var(--mei-cyan) 35%, transparent);
}

.bubble-meta {
	font-size: 0.6875rem;
	color: var(--mei-muted);
}

/* The command palette sits at the top rather than centred, so the results list
   does not jump as it grows. .lightbox provides the scrim. */
.search-modal {
	align-items: start;
	padding-top: 12vh;
}

/* hCaptcha renders its own iframe; this only reserves the space. */
.h-captcha {
	min-height: 5rem;
}

/* ---------- content page: layout blocks (restored) ---------- */

/*
 * The accordion and any panel sitting beside it share one measure. Without
 * this the closing CTA ran the full content width while the accordion above it
 * stopped at 60rem, so their right edges did not line up.
 */
.rich .accordion,
.rich > .panel {
	max-width: 60rem;
}

/*
 * Card text is clamped to three lines so a grid of teasers stays level. Inside
 * a content page the card *is* the content, so the clamp just truncates it.
 */
.rich .card-body p {
	display: block;
	-webkit-line-clamp: none;
	overflow: visible;
}

.rich .grid-3 {
	grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}

/* ---------- hero container (restored verbatim) ---------- */

.hero-slider {
	position: relative;
	/*
	 * Phones keep a capped hero so the sections below stay discoverable;
	 * desktop goes full-viewport (see the 52rem rule elsewhere), which is
	 * how the site opened before the rewrite. dvh, not vh, so collapsing
	 * browser chrome does not make the hero jump on scroll.
	 */
	min-height: min(34rem, 78dvh);
	display: grid;
	isolation: isolate;
	background: #070b11;
}

.hero-slide {
	grid-area: 1 / 1;
	position: relative;
	display: none;
	min-height: inherit;
}

.hero-slide[data-active] {
	display: grid;
	align-items: center;
	animation: hero-in 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes hero-in {
	from { opacity: 0; transform: scale(1.03); }
	to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
	.hero-slide[data-active] { animation: none; }
}

/* ---------- RECONSTRUCTED utilities and JS-built elements ---------- */

/* Small button variant, used in table rows, filters and card actions. */
.btn-sm {
	padding: 0.4rem 0.85rem;
	font-size: 0.8125rem;
}

/* Square icon-only control: theme toggle, interest link, admin bar. */
.icon-button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.4rem;
	min-width: 2.25rem;
	height: 2.25rem;
	padding-inline: 0.6rem;
	border: 1px solid var(--hairline);
	border-radius: 0.5rem;
	background: transparent;
	color: var(--mei-muted);
	font: inherit;
	font-size: 0.8125rem;
	text-decoration: none;
	cursor: pointer;
	transition: border-color 0.2s, color 0.2s;
}

.icon-button:hover {
	border-color: var(--mei-cyan);
	color: var(--mei-cyan);
}

.small {
	font-size: 0.8125rem;
}

.lightbox-close {
	position: absolute;
	top: 1.25rem;
	right: 1.25rem;
	width: 2.5rem;
	height: 2.5rem;
	border: 1px solid rgba(255, 255, 255, 0.3);
	border-radius: 50%;
	background: rgba(0, 0, 0, 0.4);
	color: #fff;
	font-size: 1.25rem;
	line-height: 1;
	cursor: pointer;
}

.lightbox-close:hover {
	background: rgba(255, 255, 255, 0.15);
}

/* Individual dot inside .slider-dots, built by ui.js. */
.slider-dot {
	width: 0.5rem;
	height: 0.5rem;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: rgba(232, 238, 245, 0.4);
	cursor: pointer;
	transition: background 0.2s, width 0.2s;
}

.slider-dot[data-active] {
	width: 1.4rem;
	border-radius: 999px;
	background: var(--mei-cyan);
}

/* One row in the command palette results. */
.search-result {
	display: flex;
	flex-direction: column;
	gap: 0.125rem;
	padding: 0.6rem 0.75rem;
	border-radius: 0.5rem;
	text-decoration: none;
	color: var(--foreground);
}

.search-result:hover,
.search-result:focus-visible {
	background: var(--overlay-weak);
	outline: none;
}

/* Media picker overlay, built by media.js on top of .lightbox. */
.media-modal {
	position: fixed;
	inset: 0;
	z-index: 120;
	display: grid;
	place-items: center;
	padding: 2rem;
	background: rgba(0, 0, 0, 0.7);
}

.media-modal-panel {
	width: min(60rem, 92vw);
	max-height: 80vh;
	overflow-y: auto;
	padding: 1.25rem;
	background: var(--surface-solid);
	border: 1px solid var(--hairline);
	border-radius: var(--panel-radius);
}

/* Chosen file shown under a media field. */
.media-thumb {
	position: relative;
	display: inline-block;
}

.media-thumb img {
	max-height: 4.5rem;
	border-radius: 0.35rem;
	border: 1px solid var(--hairline);
	display: block;
}

.media-thumb-remove {
	position: absolute;
	top: -0.4rem;
	right: -0.4rem;
	width: 1.25rem;
	height: 1.25rem;
	border: 0;
	border-radius: 50%;
	background: #d63638;
	color: #fff;
	font-size: 0.75rem;
	line-height: 1;
	cursor: pointer;
}

/* ---------- footer (restored verbatim) ---------- */

/*
 * Flex, not grid: the number of columns varies (certifications are optional),
 * and auto-fit built tracks from the container width rather than from the
 * children, so the last column was left stranded against an empty track.
 */
.footer-grid {
	display: flex;
	flex-wrap: wrap;
	gap: 2rem 3rem;
	/* Columns start at the top rather than centring on the tallest one. */
	align-items: flex-start;
	padding-block: 3rem;
	font-size: 0.875rem;
}

/* The brand blurb is prose, so it earns more width than a list of links. */
.footer-brand {
	flex: 1 1 20rem;
	min-width: 0;
}

.footer-col {
	flex: 1 1 11rem;
	min-width: 0;
	max-width: 18rem;
}

.footer-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
	color: var(--mei-muted);
}

.footer-list a {
	color: inherit;
	text-decoration: none;
}

.footer-list a:hover {
	color: var(--mei-cyan);
}

.footer-note {
	margin: 0;
	color: var(--mei-muted);
}

/* ---------- RECONSTRUCTED base rules ---------- */

.stack {
	display: flex;
	flex-direction: column;
	gap: 1rem;
	align-items: stretch;
}

.hint {
	display: block;
	font-weight: 400;
	font-size: 0.75rem;
	color: var(--mei-muted);
}

.notice {
	margin: 0 0 1rem;
	padding: 0.75rem 1rem;
	border: 1px solid color-mix(in srgb, var(--mei-cyan) 40%, transparent);
	border-radius: 0.5rem;
	background: color-mix(in srgb, var(--mei-cyan) 8%, transparent);
	font-size: 0.875rem;
}

.table {
	width: 100%;
	border-collapse: collapse;
	font-size: 0.875rem;
}

.table th {
	text-align: left;
	font-weight: 600;
	color: var(--mei-muted);
	border-bottom: 1px solid var(--hairline-strong);
	padding: 0.6rem 0.75rem;
}

.table td {
	padding: 0.75rem;
	border-bottom: 1px solid var(--hairline);
	vertical-align: top;
}

.form-wide {
	max-width: 46rem;
}

/* Button variants: same geometry as .btn, quieter presence. */
.button-outline,
.button-secondary {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.4rem;
	padding: 0.6rem 1.1rem;
	border-radius: 0.5rem;
	border: 1px solid var(--hairline-strong);
	background: transparent;
	color: inherit;
	font: inherit;
	font-size: 0.875rem;
	text-decoration: none;
	cursor: pointer;
	transition: border-color 0.2s, color 0.2s, background 0.2s;
}

.button-outline:hover,
.button-secondary:hover {
	border-color: var(--mei-cyan);
	color: var(--mei-cyan);
}

.button-secondary {
	padding: 0.4rem 0.8rem;
	font-size: 0.8125rem;
}

.button-secondary.is-saved {
	border-color: var(--mei-cyan);
	color: var(--mei-cyan);
	background: color-mix(in srgb, var(--mei-cyan) 12%, transparent);
}

.header-actions {
	display: flex;
	align-items: center;
	gap: 0.6rem;
	margin-left: 1rem;
}

.header-search {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.4rem 0.7rem;
	border: 1px solid var(--hairline);
	border-radius: 0.5rem;
	background: transparent;
	color: var(--mei-muted);
	font: inherit;
	font-size: 0.8125rem;
	cursor: pointer;
}

.header-search:hover {
	border-color: var(--mei-cyan);
	color: var(--mei-cyan);
}

.header-search kbd {
	padding: 0.05rem 0.3rem;
	border: 1px solid var(--hairline);
	border-radius: 0.25rem;
	font-size: 0.6875rem;
	font-family: inherit;
}

/* Homepage standing statement, under the hero. */
.intro {
	padding-block: 4.5rem 3rem;
}

.intro-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem;
	margin-top: 2rem;
}

/* Dashboard figure tile. */
.stat {
	gap: 0.25rem;
}

.stat-value {
	font-size: 1.75rem;
	font-weight: 600;
	line-height: 1;
	margin: 0;
}

.capability-list {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
	gap: 1.5rem;
}

.capability-list h4 {
	margin: 0 0 0.35rem;
	font-size: 1rem;
}

.capability-list p {
	margin: 0;
	font-size: 0.875rem;
}

.media {
	position: relative;
}

.video {
	position: relative;
}

/* 3D viewer stage on a product page. */
.model-stage {
	position: relative;
	width: 100%;
	height: 100%;
	min-height: 18rem;
	cursor: grab;
}

.model-stage:active {
	cursor: grabbing;
}

.model-stage canvas {
	width: 100%;
	height: 100%;
	display: block;
}

.model-stage[data-loading]::after {
	content: 'Loading model…';
	position: absolute;
	inset: 0;
	display: grid;
	place-items: center;
	color: var(--mei-muted);
	font-size: 0.8125rem;
}

.model-stage[data-error]::after {
	content: 'The model could not be loaded.';
}

/* ---------- sticky site header (RECONSTRUCTED) ---------- */

/*
 * The header follows the page. Its base background is --overlay-weak, which is
 * translucent by design — fine for a band sitting in the flow, but content
 * scrolls *under* a sticky header, so it needs something solid behind it.
 */
.site-header {
	position: sticky;
	top: 0;
	z-index: 40;
	background: color-mix(in srgb, var(--surface-solid) 88%, transparent);
	backdrop-filter: saturate(140%) blur(10px);
	-webkit-backdrop-filter: saturate(140%) blur(10px);
}

/* No blur available: fall back to a fully opaque bar rather than letting the
   page show through the text. */
@supports not (backdrop-filter: blur(1px)) {
	.site-header {
		background: var(--surface-solid);
	}
}

.site-nav {
	display: flex;
	align-items: center;
	gap: 1.25rem;
	margin-left: auto;
	flex-wrap: wrap;
}

.site-nav a {
	text-decoration: none;
	color: var(--mei-muted);
	font-size: 0.9375rem;
	white-space: nowrap;
}

.site-nav a:hover,
.site-nav a[aria-current="page"] {
	color: var(--mei-cyan);
}

/* ---------- display type (restored verbatim) ---------- */

.display,
h1.display,
.section-head h2,
.page-title {
	font-family: Fraunces, "Iowan Old Style", Georgia, serif;
	font-weight: 600;
	letter-spacing: -0.01em;
}

/* ---------- command palette panel (restored verbatim) ---------- */

.search-panel {
	width: min(36rem, 92vw);
	padding: 1rem;
	background: var(--surface-solid);
}

/* ---------- about grid: desktop columns (RECONSTRUCTED) ---------- */

/*
 * The text column and the ops tile sit side by side from 60rem up. Without
 * this the tile ran full width, and since it is aspect-ratio 4/3 that made it
 * about 940px tall with its centred label stranded far below the fold — it
 * looked like the tile had lost its text.
 */
@media (min-width: 60rem) {
	.about-grid {
		grid-template-columns: 1.35fr 1fr;
		gap: 3rem;
	}
}

/* Belt and braces: never taller than a screen, whatever the column width. */
.ops-panel {
	max-height: 70vh;
}

/* ---------- accordion ---------- */

/*
 * Cards, not a bordered list.
 *
 * This was a stack of rules separated by hairlines, which left a rule hanging
 * under the last item and made an open panel read as a flat grey block. Each
 * item is now a discrete card: nothing trails off the end, and open/closed is
 * carried by the border and elevation rather than a fill.
 *
 * All of the accordion's spacing lives in this one block. It was previously
 * split across two, with a later rule setting only padding-inline, and the
 * vertical rhythm was impossible to read off the source.
 */
.accordion {
	display: flex;
	flex-direction: column;
	gap: 0.625rem;
	margin-top: 1.5rem;
}

.accordion details {
	border: 1px solid var(--hairline);
	border-radius: var(--panel-radius);
	background: var(--surface-2);
	overflow: hidden;
	transition: border-color 0.25s, box-shadow 0.25s, transform 0.25s;
}

.accordion details:hover {
	border-color: color-mix(in srgb, var(--mei-cyan) 40%, var(--hairline));
}

.accordion details[open] {
	border-color: color-mix(in srgb, var(--mei-cyan) 55%, transparent);
	box-shadow: var(--panel-shadow);
}

.accordion summary {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	padding: 0.95rem 1.1rem;
	cursor: pointer;
	font-weight: 600;
	font-size: 0.9375rem;
	list-style: none;
	transition: color 0.2s;
}

.accordion summary::-webkit-details-marker {
	display: none;
}

.accordion summary:hover {
	color: var(--mei-cyan);
}

.accordion summary:focus-visible {
	outline: 2px solid var(--mei-cyan);
	outline-offset: -2px;
}

/*
 * One glyph that rotates, rather than swapping + for ×. A cross reads as
 * "dismiss"; a rotating plus reads as "this expands", and the movement is the
 * feedback that the click registered.
 */
.accordion summary::after {
	content: '+';
	flex: 0 0 auto;
	display: grid;
	place-items: center;
	width: 1.5rem;
	height: 1.5rem;
	border: 1px solid var(--hairline);
	border-radius: 50%;
	color: var(--mei-muted);
	font-size: 0.95rem;
	line-height: 1;
	transition: transform 0.25s ease, border-color 0.25s, color 0.25s, background 0.25s;
}

.accordion summary:hover::after {
	border-color: var(--mei-cyan);
	color: var(--mei-cyan);
}

.accordion details[open] summary::after {
	transform: rotate(45deg);
	border-color: var(--mei-cyan);
	background: var(--mei-cyan);
	color: var(--on-accent);
}

/* A hairline between question and answer, inside the card. */
.accordion details[open] summary {
	border-bottom: 1px solid var(--hairline);
}

.accordion details > *:not(summary) {
	padding: 0.95rem 1.1rem;
	margin: 0;
	color: var(--mei-muted);
	font-size: 0.9375rem;
	line-height: 1.65;
}

/* Consecutive blocks in one answer must not double the padding between them. */
.accordion details > *:not(summary) + *:not(summary) {
	padding-top: 0;
}

.accordion details ul {
	padding-left: 2.4rem;
}

/* <details> cannot animate its own height, so the inner block is what moves. */
.accordion details[open] > *:not(summary) {
	animation: accordion-in 0.28s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes accordion-in {
	from { opacity: 0; transform: translateY(-0.35rem); }
	to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
	.accordion details[open] > *:not(summary) { animation: none; }
	.accordion details,
	.accordion summary,
	.accordion summary::after { transition: none; }
}



.ops-panel {
	aspect-ratio: 4 / 3;
	display: grid;
	place-content: center;
	text-align: center;
	gap: 0.5rem;
	background-image:
		linear-gradient(var(--hairline) 1px, transparent 1px),
		linear-gradient(90deg, var(--hairline) 1px, transparent 1px);
	background-size: 44px 44px;
}

.ops-title {
	font-size: 1.75rem;
	margin: 0;
}

.ops-panel .eyebrow {
	margin: 0;
}

/* ---------- partners, capabilities, news, CTA ---------- */

.section-desc {
	margin: 0.5rem 0 0;
}

.partner-row {
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem;
}

.partner-chip {
	display: grid;
	place-items: center;
	min-width: 9rem;
	padding: 1rem 1.25rem;
	font-size: 0.875rem;
	color: var(--mei-muted);
}

.partner-chip img {
	max-height: 2.25rem;
	max-width: 8rem;
	object-fit: contain;
}

.capability h3 {
	margin: 0 0 0.5rem;
	font-size: 1rem;
}

.news-card {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	text-decoration: none;
	color: inherit;
	transition: border-color 0.2s;
}

.news-card:hover {
	border-color: color-mix(in srgb, var(--mei-cyan) 40%, transparent);
}

.news-card h3 {
	margin: 0;
	font-size: 1rem;
	line-height: 1.4;
	flex: 1;
}

.cta-panel .card-body {
	padding: 3rem;
	text-align: center;
	display: grid;
	justify-items: center;
	gap: 0.5rem;
}

.cta-panel h2 {
	margin: 0.25rem 0 0.5rem;
}

.cta-panel .lede {
	margin: 0 0 1.5rem;
	max-width: 40rem;
}

/* ---------- product catalogue ---------- */

.catalog-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	flex-wrap: wrap;
	/* Space between the layout toggle and the grid below it. */
	margin-bottom: 1.25rem;
}

.col-toggle {
	display: flex;
	gap: 0.375rem;
	/* Stays right even when it is the only child, which it is until a filter
	   is applied and the results count appears beside it. */
	margin-left: auto;
}

.col-toggle button {
	width: 2.25rem;
	height: 2.25rem;
	border: 1px solid var(--hairline);
	border-radius: 0.5rem;
	background: transparent;
	color: var(--mei-muted);
	cursor: pointer;
	font-size: 0.9rem;
}

.col-toggle button[data-active] {
	border-color: var(--mei-cyan);
	color: var(--mei-cyan);
}

/* Both breakpoints show the toggle; each hides the options that do not apply
   at its width. site.js clamps the stored choice to the same sets. */
@media (max-width: 52rem) {
	.col-toggle button[data-cols="3"],
	.col-toggle button[data-cols="4"],
	.col-toggle button[data-cols="5"],
	.col-toggle button[data-cols="list"] { display: none; }
}

@media (min-width: 52rem) {
	.col-toggle button[data-cols="1"] { display: none; }
}

.catalog {
	display: grid;
	grid-template-columns: 1fr;
	gap: 1.5rem;
	align-items: start;
}

@media (min-width: 52rem) {
	.catalog {
		grid-template-columns: 15rem 1fr;
	}

	/*
	 * The whole panel scrolls, rather than each list scrolling inside it. The
	 * per-list max-height cut the last category in half with no hint that it
	 * was scrollable, and a sticky panel taller than the viewport made Apply
	 * and the lower filters unreachable.
	 */
	.filter-panel {
		max-height: calc(100dvh - 3rem);
		overflow-y: auto;
		overscroll-behavior: contain;
	}
}

.filter-panel {
	padding: 1.25rem;
	position: sticky;
	top: 1.5rem;
}

.filter-panel h4 {
	margin: 1.25rem 0 0.5rem;
	font-size: 0.8125rem;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--mei-muted);
}

.filter-panel .input {
	width: 100%;
	min-width: 0;
}

.filter-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 0.45rem;
	font-size: 0.875rem;

	/*
	 * Long taxonomies scroll inside the panel rather than pushing Apply off
	 * the screen. This existed before and was removed because it cut the last
	 * row in half with nothing to say it was scrollable — the cap is back, but
	 * only alongside the affordances below.
	 *
	 * max-height is set in rem rather than a row count: a label can wrap to
	 * two lines, so there is no row height to align to. A part-shown row is
	 * fine, and is in fact the clearest signal that more follows, provided the
	 * scrollbar is visible.
	 */
	max-height: 15rem;
	overflow-y: auto;
	/* Reaching the end of the list must not start scrolling the page. */
	overscroll-behavior: contain;
	/* Reserves the track, so a list that grows past the cap does not suddenly
	   jump narrower as the scrollbar appears. */
	scrollbar-gutter: stable;
	scrollbar-width: thin;
	scrollbar-color: var(--hairline-strong) transparent;
	padding-right: 0.25rem;
}

/* WebKit needs the track drawn explicitly, or the scrollbar only fades in on
   scroll — which is precisely the missing cue that made this look broken. */
.filter-list::-webkit-scrollbar {
	width: 0.5rem;
}

.filter-list::-webkit-scrollbar-track {
	background: var(--overlay-weak);
	border-radius: 999px;
}

.filter-list::-webkit-scrollbar-thumb {
	background: var(--hairline-strong);
	border-radius: 999px;
}

.filter-list::-webkit-scrollbar-thumb:hover {
	background: var(--mei-muted);
}

.filter-list label {
	display: flex;
	gap: 0.5rem;
	align-items: flex-start;
	cursor: pointer;
}

/* Pinned to the foot of the scrolling panel so Apply is always in reach. */
.filter-actions {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	margin-top: 1.25rem;
	position: sticky;
	bottom: 0;
	padding-top: 0.75rem;
	/* Sticky over the scrolling filter list, so this must be opaque. */
	background: var(--surface-solid);
}

/* Density is set by the toggle; falls back to 3 with JS off. */
.product-grid {
	grid-template-columns: repeat(auto-fill, minmax(min(100%, calc((100% - (var(--cols, 3) - 1) * 1.25rem) / var(--cols, 3))), 1fr));
}

.product-card {
	display: flex;
	flex-direction: column;
	overflow: hidden;
}

.product-media {
	display: block;
}

.product-media img {
	width: 100%;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	display: block;
}

.product-card h3 {
	margin: 0;
	font-size: 1rem;
	line-height: 1.35;
}

.product-card h3 a {
	text-decoration: none;
	color: inherit;
}

.product-card h3 a:hover {
	color: var(--mei-cyan);
}

.badge-row {
	display: flex;
	flex-wrap: wrap;
	gap: 0.3rem;
	margin: 0;
}

.product-actions {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.5rem;
	flex-wrap: wrap;
	margin-top: auto;
	padding-top: 0.75rem;
}

.product-actions .accent {
	text-decoration: none;
	font-size: 0.875rem;
}

.product-actions .button-secondary {
	font-size: 0.75rem;
	padding: 0.3rem 0.6rem;
}

/* ---------- product viewer & tabs ---------- */

.viewer-stage {
	position: relative;
	height: 22rem;
	overflow: hidden;
}

.viewer-pane {
	position: absolute;
	inset: 0;
}

.viewer-pane[hidden] {
	display: none !important;
}

.viewer-pane[data-pane="image"] img {
	position: absolute;
	inset: 1.5rem;
	width: calc(100% - 3rem);
	height: calc(100% - 3rem);
	object-fit: contain;
}

.viewer-pane .model-stage {
	height: 100%;
	margin: 0;
	border: 0;
	border-radius: 0;
}

.viewer-arrow {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	width: 2.25rem;
	height: 2.25rem;
	border-radius: 50%;
	border: 1px solid var(--hairline);
	background: var(--surface-2);
	color: var(--foreground);
	cursor: pointer;
	font-size: 1.1rem;
	line-height: 1;
}

.viewer-arrow.left { left: 0.6rem; }
.viewer-arrow.right { right: 0.6rem; }

.viewer-arrow:hover {
	border-color: var(--mei-cyan);
	color: var(--mei-cyan);
}

.viewer-count {
	position: absolute;
	bottom: 0.6rem;
	left: 50%;
	transform: translateX(-50%);
	font-size: 0.75rem;
	color: var(--mei-muted);
}

.thumb {
	width: 4rem;
	height: 4rem;
	padding: 0;
	border: 1px solid var(--hairline);
	border-radius: 0.5rem;
	overflow: hidden;
	background: var(--surface-1);
	cursor: pointer;
	display: grid;
	place-items: center;
}

.thumb[data-active] {
	border-color: var(--mei-cyan);
}

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

.thumb-tile {
	flex-direction: column;
	gap: 0.1rem;
	font-size: 1rem;
	color: var(--mei-muted);
}

.thumb-tile span {
	font-size: 0.5625rem;
	letter-spacing: 0.1em;
}

.meta-row {
	list-style: none;
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem 1.5rem;
	padding: 1.25rem 0 0;
	margin: 1.25rem 0 0;
	border-top: 1px solid var(--hairline);
	color: var(--mei-muted);
	font-size: 0.875rem;
}

.tabbar {
	position: sticky;
	top: 0;
	z-index: 20;
	display: flex;
	gap: 0.25rem;
	overflow-x: auto;
	border-block: 1px solid var(--hairline);
	background: var(--background);
	margin-top: 2rem;
}

.tabbar button {
	padding: 1rem 1.25rem;
	border: 0;
	border-bottom: 2px solid transparent;
	background: none;
	color: var(--mei-muted);
	font: inherit;
	font-size: 0.75rem;
	letter-spacing: 0.1em;
	white-space: nowrap;
	cursor: pointer;
}

.tabbar button[data-active] {
	color: var(--mei-cyan);
	border-bottom-color: var(--mei-cyan);
}

.tab-panel {
	padding-block: 3rem;
}

.tab-panel[hidden] {
	display: none !important;
}

.tab-head {
	text-align: center;
	max-width: 42rem;
	margin: 0 auto 2.5rem;
}

.tab-head h2 {
	margin: 0 0 0.75rem;
	font-size: 1.875rem;
}

.tab-subhead {
	text-align: center;
	margin: 3rem 0 1.5rem;
	font-size: 1.5rem;
}

.feature-grid {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
	gap: 1rem 2rem;
	color: var(--mei-muted);
	font-size: 0.9375rem;
}

.feature-grid li {
	display: flex;
	gap: 0.6rem;
	align-items: flex-start;
}

.tick {
	color: var(--mei-cyan);
	flex-shrink: 0;
}

.capability-list > div {
	display: grid;
	gap: 0.5rem 2rem;
	padding-top: 1.5rem;
	margin-top: 1.5rem;
	border-top: 1px solid var(--hairline);
}

@media (min-width: 48rem) {
	.capability-list > div {
		grid-template-columns: 15rem 1fr;
	}
}

.capability-list h4 {
	margin: 0;
	font-size: 1rem;
}

.capability-list p {
	margin: 0;
}

/* ==========================================================================
   Responsive
   ==========================================================================
   Written after the desktop layout rather than alongside it, so this section
   corrects the places that assumed a wide viewport. Anything that must never
   push the page sideways is clamped here.
   ========================================================================== */

/* A single wide element (a table, a marquee, a code block) must scroll inside
   its own box, never widen the document. */
html,
body {
	max-width: 100%;
	overflow-x: hidden;
}

img,
video,
iframe,
canvas,
table {
	max-width: 100%;
}

/* ---------- header ---------- */

.nav-toggle {
	display: none;
	margin-left: auto;
	width: 2.75rem;
	height: 2.75rem;
	border: 1px solid var(--hairline);
	border-radius: 0.5rem;
	background: transparent;
	cursor: pointer;
	padding: 0.7rem 0.6rem;
	flex-direction: column;
	justify-content: space-between;
}

.nav-toggle span {
	display: block;
	height: 2px;
	background: var(--foreground);
	border-radius: 2px;
}

.site-menu {
	display: contents;
}

@media (max-width: 68rem) {
	/*
	 * Below the breakpoint the menu becomes an off-canvas drawer rather than a
	 * panel that pushes the page down. The header keeps a fixed height, so the
	 * hero never jumps when the menu opens.
	 */
	/*
	 * backdrop-filter makes an element a containing block for fixed-position
	 * descendants, which would anchor the drawer to the header instead of the
	 * viewport. Below the breakpoint the bar goes fully opaque instead — the
	 * blur is a desktop nicety and nothing is lost on a phone.
	 */
	.site-header {
		backdrop-filter: none;
		-webkit-backdrop-filter: none;
		background: var(--surface-solid);
	}

	.site-header .wrap {
		flex-wrap: nowrap;
		gap: 0.75rem;
	}

	.nav-toggle {
		display: flex;
		position: relative;
		z-index: 60;
	}

	/* Hamburger folds into a close cross while the drawer is open. */
	.nav-toggle span {
		transition: transform 0.22s ease, opacity 0.16s ease;
		transform-origin: center;
	}

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

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

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

	/*
	 * A floating card hung under the header, not a full-height drawer. Height is
	 * content-driven and capped, so a seven-item menu occupies about half the
	 * screen instead of all of it.
	 *
	 * Kept in the layout rather than display:none so the open/close can animate;
	 * inert/visibility keep a closed panel out of the tab order.
	 */
	.site-menu {
		display: flex;
		position: fixed;
		top: 4rem;
		inset-inline-end: 0.75rem;
		z-index: 55;
		width: min(15.5rem, 72vw);
		max-height: min(30rem, calc(100dvh - 6rem));
		flex-direction: column;
		gap: 0;
		padding: 0.625rem;
		overflow-y: auto;
		overscroll-behavior: contain;
		background: var(--surface-solid, var(--background));
		border: 1px solid var(--hairline);
		border-radius: 0.875rem;
		box-shadow: 0 1.25rem 2.5rem rgba(0, 0, 0, 0.18), 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1);
		transform: translateY(-0.5rem) scale(0.97);
		transform-origin: top right;
		opacity: 0;
		visibility: hidden;
		transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.16s ease,
			visibility 0s linear 0.2s;
	}

	.site-menu[data-open] {
		transform: translateY(0) scale(1);
		opacity: 1;
		visibility: visible;
		transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.16s ease,
			visibility 0s;
	}

	/* Scrim. Sits under the drawer, over the page. */
	.nav-scrim {
		position: fixed;
		inset: 0;
		z-index: 50;
		/* Lighter than a drawer scrim — this panel covers little of the page, so
		   a heavy dim would look disproportionate. It is still the click-catcher. */
		background: rgba(4, 8, 14, 0.28);
		opacity: 0;
		visibility: hidden;
		transition: opacity 0.28s ease, visibility 0s linear 0.28s;
	}

	.nav-scrim[data-open] {
		opacity: 1;
		visibility: visible;
		transition: opacity 0.28s ease, visibility 0s;
	}

	/*
	 * align-items must be reset: the desktop rule centres items on a row, and
	 * left as-is it shrink-wraps every link to its text width once the axis
	 * turns vertical.
	 */
	.site-nav {
		flex-direction: column;
		align-items: stretch;
		gap: 0;
		margin-left: 0;
		width: 100%;
	}

	/*
	 * 3rem minimum height keeps every row at a comfortable thumb target. The
	 * current page gets an accent rail rather than only a colour change, which
	 * survives both themes and colour-blind viewing.
	 */
	.site-nav a {
		display: flex;
		align-items: center;
		justify-content: space-between;
		text-align: left;
		min-height: 2.625rem;
		padding: 0.375rem 0.625rem;
		border-radius: 0.5rem;
		font-size: 0.9375rem;
		font-weight: 500;
		letter-spacing: 0.005em;
		color: var(--foreground);
		border-inline-start: 3px solid transparent;
		transition: background-color 0.18s ease, border-color 0.18s ease,
			color 0.18s ease, padding-inline-start 0.18s ease;
	}

	/*
	 * Chevron marks each row as a destination. Purely decorative, so it is a
	 * pseudo-element and never reaches the accessibility tree.
	 */
	.site-nav a::after {
		content: "";
		width: 0.35rem;
		height: 0.35rem;
		margin-inline-start: 0.625rem;
		border-inline-end: 1.5px solid currentColor;
		border-block-start: 1.5px solid currentColor;
		transform: rotate(45deg);
		opacity: 0.32;
		transition: opacity 0.18s ease, transform 0.18s ease;
	}

	/*
	 * Dividers go between rows, not under each one — a trailing rule above the
	 * actions block would double up with that block's own top border.
	 */
	.site-nav a + a {
		border-top: 1px solid var(--hairline);
		border-start-start-radius: 0;
		border-start-end-radius: 0;
	}

	.site-nav a:has(+ a) {
		border-end-start-radius: 0;
		border-end-end-radius: 0;
	}

	.site-nav a:active {
		background: var(--surface-1);
		padding-inline-start: 1.125rem;
	}

	.site-nav a:active::after {
		opacity: 0.6;
		transform: rotate(45deg) translate(0.1rem, -0.1rem);
	}

	.site-nav a[aria-current="page"] {
		border-inline-start-color: var(--mei-cyan);
		background: var(--surface-1);
		color: var(--mei-cyan);
		font-weight: 600;
	}

	.site-nav a[aria-current="page"]::after {
		opacity: 0.75;
	}

	/*
	 * Rows fade up in sequence behind the drawer's slide. Seven items at 28ms
	 * apart finishes well inside the 280ms transition, so it reads as one
	 * motion rather than a queue.
	 */
	.site-menu[data-open] .site-nav a {
		animation: nav-row-in 0.32s ease both;
	}

	.site-menu[data-open] .site-nav a:nth-child(1) { animation-delay: 0.04s; }
	.site-menu[data-open] .site-nav a:nth-child(2) { animation-delay: 0.068s; }
	.site-menu[data-open] .site-nav a:nth-child(3) { animation-delay: 0.096s; }
	.site-menu[data-open] .site-nav a:nth-child(4) { animation-delay: 0.124s; }
	.site-menu[data-open] .site-nav a:nth-child(5) { animation-delay: 0.152s; }
	.site-menu[data-open] .site-nav a:nth-child(6) { animation-delay: 0.18s; }
	.site-menu[data-open] .site-nav a:nth-child(n + 7) { animation-delay: 0.208s; }

	@keyframes nav-row-in {
		from {
			opacity: 0;
			transform: translateX(0.75rem);
		}
	}

	/*
	 * Actions sit at the foot of the drawer. margin-block-start:auto pins them
	 * to the bottom on a tall screen instead of leaving them floating directly
	 * under the last link.
	 */
	.header-actions {
		display: flex;
		flex-wrap: wrap;
		align-items: center;
		gap: 0.5rem;
		width: 100%;
		margin-block-start: auto;
		padding-block-start: 0.875rem;
		border-block-start: 1px solid var(--hairline);
	}

	.header-search {
		flex: 1 1 100%;
		justify-content: flex-start;
		min-height: 2.5rem;
		padding-inline: 0.75rem;
		border-radius: 0.5rem;
		background: var(--surface-1);
		border: 1px solid var(--hairline);
		color: var(--mei-muted);
	}

	/* Pushes the Ctrl K hint to the far edge of the field. */
	.header-search kbd {
		margin-inline-start: auto;
	}

	.header-actions .icon-button,
	.header-actions .btn {
		min-height: 2.5rem;
		border-radius: 0.5rem;
	}

	.header-actions .icon-button {
		flex: 0 0 auto;
		aspect-ratio: 1;
		display: inline-flex;
		align-items: center;
		justify-content: center;
	}

	.header-actions .btn {
		flex: 1 1 auto;
		justify-content: center;
		font-weight: 600;
		font-size: 0.875rem;
		padding-inline: 0.625rem;
		/* "Client Portal" wrapped to two lines and pushed the row taller. */
		white-space: nowrap;
	}

	.header-actions .icon-button.interest-button {
		padding-inline: 0.625rem;
		aspect-ratio: auto;
		font-size: 0.875rem;
	}

	/*
	 * The chat bubble is fixed at z-50 and would float over the open drawer,
	 * landing on the search field. It has nothing to do while the menu is up.
	 */
	body[data-nav-open] .chat-launcher {
		opacity: 0;
		visibility: hidden;
		transition: opacity 0.2s ease, visibility 0s linear 0.2s;
	}

	/* The desktop rule hid the label and swapped in a glyph; in the panel
	   there is room for the real thing. */
	.header-search span,
	.header-search kbd {
		display: inline;
	}

	.header-search::after {
		content: none;
	}

	/* Page must not scroll behind an open drawer. */
	body[data-nav-open] {
		overflow: hidden;
	}
}

/* Honour reduced-motion: the drawer still works, it just does not slide. */
@media (max-width: 68rem) and (prefers-reduced-motion: reduce) {
	.site-menu,
	.nav-scrim,
	.nav-toggle span,
	.site-nav a {
		transition-duration: 0.01ms;
	}

	/* The staggered entrance is decoration; drop it entirely rather than
	   speeding it up, or rows still flicker. */
	.site-menu[data-open] .site-nav a {
		animation: none;
	}
}

/* ---------- marquee ---------- */

.marquee {
	max-width: 100%;
}

/* ---------- tables ---------- */

/* Admin tables are wide by nature. They scroll inside a wrapper rather than
   stretching the page. */
.table-scroll {
	overflow-x: auto;
	-webkit-overflow-scrolling: touch;
	margin-bottom: 1.5rem;
}

.table-scroll .table {
	min-width: 34rem;
	margin-bottom: 0;
}

/* ---------- phones ---------- */

@media (max-width: 48rem) {
	.wrap {
		padding-inline: 1rem;
	}

	main {
		padding-block-end: 2.25rem;
	}

	/* Hero height is the phone value by default; see .hero-slider. */

	.hero-content {
		padding-block: 3.5rem;
	}

	.hero-content h1,
	.hero-slide .display {
		font-size: clamp(1.9rem, 8vw, 2.6rem);
		max-width: 100%;
	}

	.intro {
		padding-block: 2.5rem 2rem;
	}

	.intro h1 {
		font-size: clamp(1.9rem, 8vw, 2.6rem);
	}

	.intro-actions {
		flex-direction: column;
		align-items: stretch;
	}

	.intro-actions .btn,
	.intro-actions .button-outline {
		text-align: center;
	}

	.section {
		padding-block: 2.25rem;
	}

	.section-head {
		flex-direction: column;
		align-items: flex-start;
		gap: 0.5rem;
	}

	.page-title {
		font-size: 1.75rem;
		margin-block: 1.5rem 1rem;
	}

	.grid {
		gap: 1rem;
	}

	/* .product-grid is driven by --cols at every width now that the toggle is
	   available on phones too; only the other grids are pinned to one column. */
	.grid-3 {
		grid-template-columns: 1fr;
	}

	.detail {
		gap: 1.5rem;
		padding-block: 1rem;
	}

	.viewer-stage {
		height: 15rem;
	}

	.tabbar {
		margin-inline: -1rem;
		padding-inline: 1rem;
	}

	.tab-panel {
		padding-block: 2rem;
	}

	.tab-head {
		margin-bottom: 1.5rem;
	}

	.cta-panel .card-body {
		padding: 1.75rem 1.25rem;
	}

	.filter-panel {
		position: static;
	}

	/* No clamp on the lists here either: the panel is a collapsed <details> on
	   a phone, so it costs nothing closed and cuts nothing off when open. */

	/* Footer reads as one column rather than cramped halves. */
	.footer-grid {
		flex-direction: column;
		gap: 1.75rem;
		padding-block: 2.25rem;
	}

	.footer-brand,
	.footer-col {
		flex: 1 1 auto;
		max-width: none;
	}

	.footer-list {
		gap: 0.6rem;
	}

	.crumbs {
		font-size: 0.75rem;
	}

	/* Admin */
	.form-wide {
		max-width: 100%;
	}

	.media-actions {
		flex-direction: column;
		align-items: stretch;
	}

	.media-actions .button-secondary {
		width: 100%;
	}

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

	.stat-value {
		font-size: 1.5rem;
	}
}

/* Very narrow phones. */
@media (max-width: 24rem) {
	.header-actions .btn-sm,
	.header-actions .icon-button {
		flex: 1;
		justify-content: center;
	}
}

/* ---------- filter panel as a disclosure on phones ---------- */

.filter-panel > summary {
	cursor: pointer;
	font-weight: 600;
	display: flex;
	align-items: center;
	gap: 0.5rem;
	list-style: none;
}

.filter-panel > summary::-webkit-details-marker {
	display: none;
}

.filter-panel > summary::after {
	content: '▾';
	margin-left: auto;
	color: var(--mei-muted);
}

.filter-panel[open] > summary::after {
	content: '▴';
}

@media (min-width: 52rem) {
	/* Always open on a wide screen; the summary is phone-only affordance. */
	.filter-panel > summary {
		display: none;
	}

	.filter-panel {
		display: block;
	}
}

/* ---------- footer polish ---------- */

.footer-brand .brand {
	display: inline-block;
	margin-bottom: 0.75rem;
	font-size: 1rem;
}

.footer-brand .footer-note {
	max-width: 22rem;
}

.footer-base {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	flex-wrap: wrap;
}

.footer-links {
	display: flex;
	gap: 1.25rem;
	margin: 0;
}

.footer-links a {
	color: inherit;
	text-decoration: none;
}

.footer-links a:hover {
	color: var(--mei-cyan);
}

@media (max-width: 48rem) {
	.footer-base {
		flex-direction: column;
		align-items: flex-start;
		gap: 0.75rem;
	}
}

/* ---------- desktop hero ---------- */

/*
 * Full viewport from tablet up, matching how the site opened before the
 * rewrite. Phones keep the capped height set on .hero-slider so the sections
 * below the fold stay discoverable on a small screen.
 */
@media (min-width: 52rem) {
	.hero-slider {
		min-height: 100dvh;
	}
}

/* Landscape phones are short enough that a full-height hero hides everything. */
@media (min-width: 52rem) and (max-height: 34rem) {
	.hero-slider {
		min-height: 34rem;
	}
}

/* ---------- page banner / page head ---------- */

.page-banner {
	margin-block: 0 2.5rem;
	padding-block: 2.75rem 2rem;
	border-bottom: 1px solid var(--hairline);
}

.page-banner-inner {
	max-width: 46rem;
}

.page-banner .page-title {
	margin: 0.5rem 0 0.75rem;
	font-size: clamp(2rem, 5vw, 3rem);
}

.page-banner .lede,
.page-head .lede {
	margin: 0 0 0.5rem;
	max-width: 42rem;
}

.page-head {
	margin-block: 0 2.5rem;
}

.page-head .page-title {
	margin: 0.5rem 0 0.75rem;
}

.results-count {
	margin: 0;
}

/* ---------- shared form grid ---------- */

/*
 * Used by the inquiry page and the document-request form. Single column on a
 * phone, two from 36rem up; .field-wide spans both.
 */
.field-grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: 1rem;
}

@media (min-width: 36rem) {
	.field-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

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

.field-grid label {
	display: flex;
	flex-direction: column;
	gap: 0.35rem;
	font-size: 0.8125rem;
	color: var(--mei-muted);
	min-width: 0;
}

.field-grid .input {
	width: 100%;
}

.inquiry-form {
	padding: clamp(1.25rem, 4vw, 2rem);
	margin-top: 2rem;
	display: flex;
	flex-direction: column;
	gap: 1.25rem;
	align-items: flex-start;
}

.inquiry-form .field-grid {
	width: 100%;
}

/* ---------- contact page ---------- */

.contact-layout {
	display: grid;
	grid-template-columns: 1fr;
	gap: 2.5rem;
	align-items: start;
}

@media (min-width: 60rem) {
	.contact-layout {
		/* Form is a fixed-ish sidebar; the offices take the remaining room. */
		grid-template-columns: minmax(20rem, 26rem) 1fr;
	}
}

.contact-form {
	padding: clamp(1.25rem, 4vw, 1.75rem);
}

.office-section > h2 {
	margin-top: 0;
}

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

@media (min-width: 44rem) {
	.office-grid {
		grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
	}
}

.office-card {
	display: flex;
	flex-direction: column;
	overflow: hidden;
}

.office-body {
	padding: 1.25rem 1.25rem 0.75rem;
}

.office-body h3 {
	margin: 0 0 0.5rem;
}

.office-body p {
	margin: 0 0 0.4rem;
	font-size: 0.875rem;
}

.office-address {
	white-space: pre-line;
}

/* The stored embed is a bare iframe with fixed width/height attributes, so
   the sizing has to be forced here rather than on the element. */
.office-map {
	margin-top: auto;
}

.office-map iframe {
	display: block;
	width: 100%;
	height: 14rem;
	border: 0;
}

.office-map-empty {
	display: grid;
	place-items: center;
	height: 8rem;
	background: var(--overlay-weak);
}

/* ---------- document request ---------- */

.doc-request {
	margin-top: 1.5rem;
	padding: 0;
	overflow: hidden;
}

.doc-request-summary {
	list-style: none;
	cursor: pointer;
	padding: 1rem;
}

.doc-request-summary::-webkit-details-marker {
	display: none;
}

.doc-request[open] .doc-request-summary {
	border-bottom: 1px solid var(--hairline);
}

.doc-request-form {
	padding: 1.25rem;
	display: flex;
	flex-direction: column;
	gap: 1rem;
	align-items: flex-start;
}

.doc-request-form .field-grid,
.doc-request-form .notice {
	width: 100%;
}

.doc-request-form > p {
	margin: 0;
}

.detail-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 0.75rem;
	margin-top: 2rem;
}

/* ---------- mobile polish ---------- */

/*
 * .input carries min-width: 12rem for the standalone filter bars. Inside any
 * grid or flex form that floor wins over the track width and pushes the field
 * past the screen edge, so it is released wherever the container sets the size.
 */
.field-grid .input,
.contact-form .input,
.filter-panel .input,
.search-panel .input {
	min-width: 0;
	max-width: 100%;
}

/* Two columns from small-phone width up: a catalogue of one card per screen
   makes the visitor scroll past everything to compare anything. */
@media (min-width: 26rem) and (max-width: 48rem) {
	.grid-3 {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	.grid-3,
	.product-grid {
		gap: 0.875rem;
	}

	.card-body,
	.product-card .card-body {
		padding: 0.875rem;
	}
}

@media (max-width: 48rem) {
	.page-banner {
		padding-block: 1.75rem 1.25rem;
		margin-bottom: 1.75rem;
	}

	.catalog-head {
		margin-bottom: 1rem;
	}

	/* Stacked buttons rather than a link and a button squeezed side by side. */
	.product-actions {
		flex-direction: column;
		align-items: flex-start;
		gap: 0.5rem;
	}

	.office-map iframe {
		height: 11rem;
	}

	.inquiry-form,
	.doc-request-form {
		gap: 1rem;
	}

	/* Full-height sheet: a centred modal on a phone leaves the results list
	   about three rows tall once the keyboard is up. */
	.search-panel {
		width: 100%;
		max-height: 100dvh;
		border-radius: 0;
	}

	.search-results {
		max-height: 60dvh;
	}
}

/* Comfortable touch targets on anything tappable in the chrome. */
@media (pointer: coarse) {
	.site-nav a,
	.footer-list a,
	.filter-list label {
		padding-block: 0.35rem;
	}
}

/* ---------- admin: camera position ---------- */

.camera-fields {
	border: 1px solid var(--hairline);
	border-radius: var(--panel-radius);
	padding: 1rem 1.25rem 1.25rem;
	margin: 0;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
	gap: 0.75rem 1rem;
}

.camera-fields legend {
	padding-inline: 0.35rem;
	font-size: 0.875rem;
}

.camera-fields label {
	display: flex;
	flex-direction: column;
	gap: 0.3rem;
	font-size: 0.8125rem;
	color: var(--mei-muted);
	min-width: 0;
}

.camera-fields .input {
	min-width: 0;
	width: 100%;
}

/* ---------- forum ---------- */

.forum-start {
	margin-bottom: 1.5rem;
}

.thread-list li {
	/* Was 0.75rem, which read as a cramped stack rather than distinct cards. */
	margin-bottom: 1rem;
}

.thread-card {
	gap: 0.35rem;
	transition: border-color 0.25s, transform 0.25s;
}

.thread-card:hover {
	transform: translateY(-2px);
	border-color: color-mix(in srgb, var(--mei-cyan) 40%, transparent);
}

.thread-card h3 {
	margin: 0;
	font-size: 1.0625rem;
}

.thread-card h3 a {
	text-decoration: none;
}

.thread-card h3 a:hover {
	color: var(--mei-cyan);
}

.thread-meta {
	margin: 0;
	font-size: 0.8125rem;
}

/* ---------- partners carousel ---------- */

/*
 * Reuses .marquee/.marquee-track from the clients row, but the track there is
 * styled for bare text — these are panel chips, so the letter-spacing and
 * muted type have to be undone and the gap tightened.
 */
.partner-marquee .marquee-track {
	gap: 0.75rem;
	letter-spacing: normal;
	font-size: inherit;
	align-items: stretch;
	/* Slower than the clients strip: chips are wider, so the same duration
	   reads as a rush. */
	animation-duration: 60s;
}

.partner-marquee .partner-chip {
	flex: 0 0 auto;
}

/* ---------- admin: content type tabs ---------- */

.content-tabs {
	display: flex;
	flex-wrap: wrap;
	gap: 0.25rem;
	margin-top: 1.5rem;
	padding-bottom: 0.75rem;
	border-bottom: 1px solid var(--hairline);
}

.content-tabs a {
	padding: 0.4rem 0.85rem;
	border-radius: 999px;
	text-decoration: none;
	font-size: 0.875rem;
	color: var(--mei-muted);
}

.content-tabs a:hover {
	background: var(--overlay-weak);
	color: var(--foreground);
}

.content-tabs a[aria-current="page"] {
	background: var(--overlay);
	color: var(--mei-cyan);
}

/* ---------- catalogue list view ---------- */

/*
 * The list option from the layout toggle: one full-width row per product with
 * the image alongside the text, rather than a denser grid of cards.
 */
.product-grid[data-layout="list"] {
	grid-template-columns: 1fr;
}

.product-grid[data-layout="list"] .product-card {
	flex-direction: row;
}

.product-grid[data-layout="list"] .product-media {
	flex: 0 0 14rem;
}

.product-grid[data-layout="list"] .product-media img {
	height: 100%;
	aspect-ratio: auto;
}

.product-grid[data-layout="list"] .card-body {
	justify-content: center;
}

/* The excerpt has room to breathe on a wide row, so the 3-line clamp that
   keeps grid cards level is not needed here. */
.product-grid[data-layout="list"] .card-body p {
	-webkit-line-clamp: none;
	display: block;
}

.product-grid[data-layout="list"] .product-actions {
	margin-top: 0.75rem;
}

/* Stacks below the toggle's breakpoint, where the toggle is hidden anyway. */
@media (max-width: 52rem) {
	.product-grid[data-layout="list"] .product-card {
		flex-direction: column;
	}

	.product-grid[data-layout="list"] .product-media {
		flex: 0 0 auto;
	}

	.product-grid[data-layout="list"] .product-media img {
		height: auto;
		aspect-ratio: 4 / 3;
	}
}

/* ---------- pagination ---------- */

.pagination {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 0.375rem;
	margin-top: 2rem;
}

.page-numbers {
	display: flex;
	flex-wrap: wrap;
	gap: 0.375rem;
}

.page-link {
	display: grid;
	place-items: center;
	min-width: 2.25rem;
	height: 2.25rem;
	padding-inline: 0.6rem;
	border: 1px solid var(--hairline);
	border-radius: 0.5rem;
	text-decoration: none;
	font-size: 0.875rem;
	color: var(--mei-muted);
}

a.page-link:hover {
	border-color: var(--mei-cyan);
	color: var(--mei-cyan);
}

.page-link.is-current {
	border-color: var(--mei-cyan);
	color: var(--mei-cyan);
	background: var(--overlay-weak);
	font-weight: 600;
}

.page-link.is-disabled {
	opacity: 0.4;
}

.page-gap {
	display: grid;
	place-items: center;
	min-width: 1.5rem;
	height: 2.25rem;
	color: var(--mei-muted);
}

/* Pushed to the far end on a wide row, but wraps under on a phone. */
.page-status {
	margin-left: auto;
	font-size: 0.8125rem;
	color: var(--mei-muted);
}

@media (max-width: 48rem) {
	.page-status {
		margin-left: 0;
		width: 100%;
		order: 1;
	}
}

/* ---------- admin: content shortcuts ---------- */

.content-shortcuts {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
	gap: 1rem;
}

.content-shortcut h3 {
	margin: 0;
	font-size: 1rem;
}

.shortcut-actions {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.75rem;
	margin: 0;
	margin-top: auto;
}

.shortcut-actions a {
	text-decoration: none;
	font-size: 0.875rem;
}

/* Uploaded logo in the header; capped so a large upload cannot blow out the bar. */
.brand-logo {
	display: block;
	max-height: 2.25rem;
	max-width: 12rem;
	width: auto;
	object-fit: contain;
}

/* Shares the camera fieldset's framing; one column, since these are long. */
.seo-fields {
	border: 1px solid var(--hairline);
	border-radius: var(--panel-radius);
	padding: 1rem 1.25rem 1.25rem;
	margin: 0;
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
}

.seo-fields legend {
	padding-inline: 0.35rem;
	font-size: 0.875rem;
}

.seo-fields label {
	display: flex;
	flex-direction: column;
	gap: 0.3rem;
	font-size: 0.8125rem;
	color: var(--mei-muted);
}

/* ---------- admin: enquiry handling ---------- */

.lead-filters {
	margin-bottom: 0.75rem;
}

.status-chips {
	display: flex;
	flex-wrap: wrap;
	gap: 0.375rem;
	margin-bottom: 1.5rem;
}

.status-chips a {
	padding: 0.35rem 0.8rem;
	border: 1px solid var(--hairline);
	border-radius: 999px;
	text-decoration: none;
	font-size: 0.8125rem;
	color: var(--mei-muted);
}

.status-chips a:hover {
	border-color: var(--mei-cyan);
	color: var(--mei-cyan);
}

.status-chips a[aria-current="page"] {
	border-color: var(--mei-cyan);
	background: var(--overlay-weak);
	color: var(--mei-cyan);
}

.lead-detail {
	display: grid;
	grid-template-columns: 1fr;
	gap: 1.5rem;
	align-items: start;
}

@media (min-width: 60rem) {
	.lead-detail {
		grid-template-columns: 1fr minmax(16rem, 22rem);
	}
}

.lead-detail h2 {
	margin: 0 0 0.5rem;
	font-size: 1.0625rem;
}

.lead-detail h3 {
	margin: 1rem 0 0.5rem;
	font-size: 0.9375rem;
}

/* Preserves the line breaks the visitor typed. */
.lead-message {
	white-space: pre-line;
	margin: 0;
}

.lead-facts {
	margin: 0;
	display: grid;
	grid-template-columns: minmax(6rem, auto) 1fr;
	gap: 0.4rem 1rem;
	font-size: 0.875rem;
}

.lead-facts dt {
	color: var(--mei-muted);
}

.lead-facts dd {
	margin: 0;
	overflow-wrap: anywhere;
}

/* ---------- careers ---------- */

.job-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 1rem;
}

.job-card {
	flex-direction: row;
	align-items: center;
	justify-content: space-between;
	gap: 1.25rem;
	transition: border-color 0.25s, transform 0.25s;
}

.job-card:hover {
	transform: translateY(-2px);
	border-color: color-mix(in srgb, var(--mei-cyan) 40%, transparent);
}

.job-card h3 {
	margin: 0 0 0.25rem;
	font-size: 1.0625rem;
}

.job-card h3 a {
	text-decoration: none;
}

.job-card h3 a:hover {
	color: var(--mei-cyan);
}

.job-meta {
	display: flex;
	flex-wrap: wrap;
	gap: 0.375rem;
	margin: 0.5rem 0 0;
}

.job-cta {
	flex: 0 0 auto;
	text-decoration: none;
	font-size: 0.875rem;
}

/* A closing date near enough to matter. */
.badge-warn {
	background: color-mix(in srgb, #d98324 24%, transparent);
	color: #b46a12;
}

:root[data-theme="dark"] .badge-warn {
	color: #f0b46b;
}

@media (max-width: 40rem) {
	.job-card {
		flex-direction: column;
		align-items: flex-start;
		gap: 0.75rem;
	}
}

/* ---------- admin shell ---------- */

/*
 * Sidebar admin, WordPress-style: a fixed left column from 64rem up, an
 * off-canvas overlay below that. The old top nav ran out of room at nine
 * items and had no way to show a section's sub-pages.
 */
.admin-shell {
	min-height: 100dvh;
}

.admin-bar {
	position: sticky;
	top: 0;
	z-index: 40;
	display: flex;
	align-items: center;
	gap: 0.75rem;
	padding: 0.6rem 1rem;
	border-bottom: 1px solid var(--hairline);
	background: var(--surface-solid);
}

.admin-bar-actions {
	margin-left: auto;
	display: flex;
	align-items: center;
	gap: 0.75rem;
}

.admin-sidebar {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	padding: 1rem 0.75rem;
	background: var(--surface-solid);
	border-right: 1px solid var(--hairline);
}

.admin-sidebar-brand {
	display: none;
	padding: 0.5rem 0.75rem 1rem;
}

.admin-menu {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 0.125rem;
}

.admin-menu > li > a {
	display: flex;
	align-items: center;
	gap: 0.7rem;
	padding: 0.6rem 0.75rem;
	border-radius: 0.5rem;
	text-decoration: none;
	color: var(--mei-muted);
	font-size: 0.9375rem;
}

.admin-menu > li > a:hover {
	background: var(--overlay-weak);
	color: var(--foreground);
}

.admin-menu > li > a[aria-current="page"] {
	background: var(--overlay);
	color: var(--mei-cyan);
	font-weight: 600;
}

.admin-menu-icon {
	width: 1.25rem;
	text-align: center;
	flex: 0 0 auto;
}

/* Submenus are in the DOM always; only the open section reveals one. */
.admin-submenu {
	list-style: none;
	margin: 0.125rem 0 0.5rem;
	padding: 0 0 0 2.6rem;
	display: none;
	flex-direction: column;
	gap: 0.125rem;
}

.admin-menu-item.is-open .admin-submenu {
	display: flex;
}

.admin-submenu a {
	display: block;
	padding: 0.35rem 0.5rem;
	border-radius: 0.375rem;
	text-decoration: none;
	color: var(--mei-muted);
	font-size: 0.875rem;
}

.admin-submenu a:hover {
	color: var(--foreground);
}

.admin-submenu a[aria-current="page"] {
	color: var(--mei-cyan);
	font-weight: 600;
}

.admin-signout {
	margin-top: auto;
	padding: 0.75rem;
	border-top: 1px solid var(--hairline);
}

.admin-content {
	padding: 1.5rem 1.25rem 3rem;
	max-width: 76rem;
	margin-inline: auto;
	width: 100%;
}

/* ---------- phones and tablets: off-canvas ---------- */

@media (max-width: 64rem) {
	.admin-sidebar {
		position: fixed;
		inset-block: 0;
		left: 0;
		z-index: 60;
		width: min(17rem, 82vw);
		overflow-y: auto;
		transform: translateX(-100%);
		transition: transform 0.2s ease;
	}

	.admin-shell[data-sidebar-open] .admin-sidebar {
		transform: none;
	}

	.admin-scrim {
		position: fixed;
		inset: 0;
		z-index: 50;
		background: rgba(0, 0, 0, 0.5);
	}

	/* Every section's submenu is visible in the drawer: on a phone there is no
	   hover, and hunting for a sub-page behind two taps is worse than a
	   slightly longer list. */
	.admin-submenu {
		display: flex;
	}
}

@media (prefers-reduced-motion: reduce) {
	.admin-sidebar {
		transition: none;
	}
}

/* ---------- desktop: fixed column ---------- */

@media (min-width: 64rem) {
	.admin-shell {
		display: grid;
		grid-template-columns: 15rem 1fr;
		/*
		 * Explicit rows. Without these both rows are auto and, because the
		 * shell is min-height: 100dvh with align-content stretching by
		 * default, they split the leftover height between them — which left
		 * the toolbar several hundred pixels tall and the page starting
		 * halfway down.
		 */
		grid-template-rows: auto 1fr;
		grid-template-areas:
			"sidebar bar"
			"sidebar main";
		/*
		 * Paints the sidebar column its full height. The nav itself is
		 * 100dvh so it can stick, which left the column bare below the fold
		 * on a long page.
		 */
		background: linear-gradient(to right,
			var(--surface-solid) 0 15rem,
			transparent 15rem);
	}

	.admin-bar {
		grid-area: bar;
	}

	.admin-sidebar {
		grid-area: sidebar;
		/* Sticky, not fixed: it scrolls with a long page but stays put while
		   the content column scrolls past it. */
		position: sticky;
		top: 0;
		height: 100dvh;
		overflow-y: auto;
	}

	.admin-sidebar-brand {
		display: block;
	}

	.admin-main {
		grid-area: main;
		min-width: 0;
	}

	.admin-bar-toggle,
	.admin-scrim {
		display: none;
	}

	/* The bar keeps only the theme toggle and View site on desktop. */
	.admin-bar .brand {
		display: none;
	}

	.admin-content {
		padding-inline: 2rem;
	}
}

/* ---------- admin polish ---------- */

.admin-menu-badge {
	margin-left: auto;
	min-width: 1.4rem;
	padding: 0 0.4rem;
	border-radius: 999px;
	background: var(--mei-cyan);
	color: var(--on-accent);
	font-size: 0.75rem;
	line-height: 1.4rem;
	text-align: center;
	font-weight: 600;
}

/* Page heading row: title left, primary action right. */
.admin-head {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 1rem;
	flex-wrap: wrap;
	margin-bottom: 1.5rem;
}

.admin-head .page-title {
	margin: 0 0 0.25rem;
	font-size: 1.75rem;
}

.admin-head p {
	margin: 0;
	font-size: 0.875rem;
}

/*
 * Admin forms were an undifferentiated column of inputs — every field the same
 * weight, no grouping, nothing to scan. These give them a card and a rhythm.
 */
.admin .form-wide {
	max-width: 52rem;
}

.admin .stack > label {
	font-size: 0.875rem;
}

.admin .stack > label > .input {
	font-size: 0.9375rem;
}

.admin .hint {
	display: block;
	font-weight: 400;
	font-size: 0.75rem;
	color: var(--mei-muted);
	margin-top: 0.1rem;
}

/* Tables: denser rows, a header that reads as one, and hover feedback. */
.admin .table {
	border-collapse: collapse;
	width: 100%;
	font-size: 0.875rem;
}

.admin .table th {
	text-align: left;
	font-size: 0.6875rem;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--mei-muted);
	border-bottom: 1px solid var(--hairline-strong);
	padding: 0.6rem 0.75rem;
	white-space: nowrap;
}

.admin .table td {
	padding: 0.75rem;
	border-bottom: 1px solid var(--hairline);
	vertical-align: top;
}

.admin .table tr:hover td {
	background: var(--overlay-weak);
}

.admin .table a {
	color: var(--mei-cyan);
	text-decoration: none;
}

.admin .table a:hover {
	text-decoration: underline;
}

/* Several small forms sitting on one row without stacking. */
.row-actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.75rem;
}

.row-actions form {
	margin: 0;
}

/* ---------- auth screens ---------- */

.auth-wrap {
	display: grid;
	place-items: center;
	padding-block: clamp(2rem, 8vh, 5rem);
}

.auth-card {
	width: min(28rem, 100%);
	padding: clamp(1.5rem, 5vw, 2.25rem);
	display: flex;
	flex-direction: column;
	gap: 1.25rem;
}

.auth-head h1 {
	margin: 0.25rem 0 0.5rem;
	font-size: 1.75rem;
	max-width: none;
}

.auth-head p {
	margin: 0;
	font-size: 0.875rem;
}

.auth-card .stack {
	gap: 0.9rem;
}

.auth-card .btn {
	width: 100%;
	justify-content: center;
	text-align: center;
}

.auth-alt {
	margin: 0;
	text-align: center;
	font-size: 0.875rem;
}

/* ---------- admin: professional chrome ---------- */

/*
 * Admin headings are UI, not editorial. The public site's serif display face at
 * 2.25rem reads as a magazine masthead on a settings screen.
 */
.admin .page-title {
	font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
	font-size: 1.5rem;
	font-weight: 600;
	letter-spacing: -0.01em;
	margin: 0 0 1.25rem;
}

.admin .section-head h2,
.admin h2 {
	font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
	font-size: 1.0625rem;
	font-weight: 600;
}

.admin-bar {
	/* Content-height, so the toolbar cannot grow into the page. */
	min-height: 3.25rem;
}

/* Group related settings into cards instead of one endless column. */
.admin fieldset.panel {
	padding: 1.25rem;
	margin: 0;
}

.admin fieldset.panel legend {
	padding-inline: 0.5rem;
	font-size: 0.8125rem;
	font-weight: 600;
	color: var(--mei-muted);
}

/*
 * A settings screen is a form, not a document: cap the measure so inputs do
 * not run the width of a 27-inch monitor, which is what made this feel
 * unfinished.
 */
.admin .form-wide,
.admin .stack.form-wide {
	max-width: 46rem;
}

.admin .form-wide .input,
.admin .form-wide textarea {
	width: 100%;
}

/* Section headings inside a long form, so it can be scanned. */
.admin .form-wide > h2 {
	margin: 2rem 0 0.25rem;
	padding-top: 1.25rem;
	border-top: 1px solid var(--hairline);
}

.admin .form-wide > h2:first-of-type {
	margin-top: 0.5rem;
	padding-top: 0;
	border-top: 0;
}

/* The submit row sticks to the bottom on long forms. */
.admin .form-wide > .filters:last-child {
	position: sticky;
	bottom: 0;
	margin-top: 1.5rem;
	padding: 0.875rem 0;
	background: var(--surface-solid);
	border-top: 1px solid var(--hairline);
}

/* Partner credit on a product page. */
.product-partner {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	margin: 1.25rem 0 0;
	padding-top: 1rem;
	border-top: 1px solid var(--hairline);
	font-size: 0.875rem;
}

.product-partner img {
	max-height: 1.75rem;
	max-width: 6rem;
	object-fit: contain;
}

/* A shorter cap on phones, where the panel is a <details> and screen height
   is scarcer. */
@media (max-width: 52rem) {
	.filter-list {
		max-height: 12rem;
	}
}

/* Solutions search, in the toolbar rather than a sidebar of its own. */
.catalog-search {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	flex: 1 1 18rem;
	/* Capped so it does not stretch the width of a wide screen; the toggle
	   still sits hard right because it carries margin-left: auto. */
	max-width: 28rem;
}

.catalog-search .input {
	flex: 1;
	min-width: 0;
}

@media (max-width: 40rem) {
	.catalog-search {
		flex: 1 1 100%;
		max-width: none;
	}
}

/* Numbered capability cards on a content page. */
.rich .card-body {
	gap: 0.6rem;
}

.rich .grid-3 > .panel {
	transition: transform 0.25s, border-color 0.25s, box-shadow 0.25s;
}

.rich .grid-3 > .panel:hover {
	transform: translateY(-3px);
	border-color: color-mix(in srgb, var(--mei-cyan) 45%, transparent);
}

.rich .card-body .eyebrow {
	margin: 0;
	color: var(--mei-cyan);
	font-weight: 600;
}

.rich .card-body h3 {
	font-size: 1.0625rem;
	line-height: 1.35;
}

/* Ticked capability list, rather than bare bullets. */
.rich .feature-list {
	list-style: none;
	padding: 0;
}

.rich .feature-list li {
	position: relative;
	padding: 0.5rem 0 0.5rem 1.75rem;
	border-top: 0;
	color: var(--foreground);
	font-size: 0.9375rem;
}

.rich .feature-list li::before {
	content: '✓';
	position: absolute;
	left: 0;
	top: 0.5rem;
	color: var(--mei-cyan);
	font-weight: 700;
}

/* ---------- content page: reveal ---------- */

/*
 * Set by site.js only once it has decided to animate, so a page rendered
 * without JavaScript is never left invisible waiting for a class that will
 * not arrive.
 */
[data-reveal] {
	opacity: 0;
	transform: translateY(0.75rem);
	transition: opacity 0.5s ease, transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
	transition-delay: var(--reveal-delay, 0ms);
}

[data-reveal][data-revealed] {
	opacity: 1;
	transform: none;
}

@media (prefers-reduced-motion: reduce) {
	[data-reveal] {
		opacity: 1;
		transform: none;
		transition: none;
	}
}

/* ---------- stat band ---------- */

.stat-band {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
	gap: 1px;
	margin: 2.5rem 0 3rem;
	background: var(--hairline);
	border: 1px solid var(--hairline);
	border-radius: var(--panel-radius);
	overflow: hidden;
}

.stat-band .stat {
	display: flex;
	flex-direction: column;
	gap: 0.35rem;
	padding: 1.5rem 1.25rem;
	background: var(--surface-solid);
}

.stat-value {
	font-family: Fraunces, "Iowan Old Style", Georgia, serif;
	font-size: clamp(1.85rem, 4vw, 2.6rem);
	font-weight: 600;
	line-height: 1;
	color: var(--mei-cyan);
	/* Tabular figures, so a counting number does not jiggle its neighbours. */
	font-variant-numeric: tabular-nums;
}

.stat-label {
	font-size: 0.8125rem;
	color: var(--mei-muted);
	line-height: 1.4;
}

/* ---------- timeline ---------- */

.timeline {
	list-style: none;
	margin: 2rem 0 0;
	padding: 0 0 0 1.75rem;
	position: relative;
	max-width: 46rem;
}

/* The rail, drawn behind the markers. */
.timeline::before {
	content: '';
	position: absolute;
	left: 0.32rem;
	top: 0.5rem;
	bottom: 0.5rem;
	width: 2px;
	background: linear-gradient(to bottom,
		var(--mei-cyan),
		color-mix(in srgb, var(--mei-cyan) 20%, transparent));
}

.timeline li {
	position: relative;
	padding-bottom: 2rem;
}

.timeline li:last-child {
	padding-bottom: 0;
}

.timeline li::before {
	content: '';
	position: absolute;
	left: -1.75rem;
	top: 0.45rem;
	width: 0.75rem;
	height: 0.75rem;
	border-radius: 50%;
	background: var(--background);
	border: 2px solid var(--mei-cyan);
}

.timeline-year {
	display: inline-block;
	font-size: 0.75rem;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--mei-cyan);
	font-weight: 600;
	margin-bottom: 0.35rem;
}

.timeline h3 {
	margin: 0 0 0.35rem;
	font-size: 1.0625rem;
}

.timeline p {
	margin: 0;
	color: var(--mei-muted);
	font-size: 0.9375rem;
}

/* ---------- content CTA ---------- */

.cta-block {
	margin-top: 3rem;
	text-align: center;
	align-items: center;
	background: linear-gradient(160deg,
		color-mix(in srgb, var(--mei-cyan) 10%, var(--surface-2)),
		var(--surface-1));
}

.cta-block h3 {
	font-size: 1.25rem;
	margin: 0;
}

.cta-block p:last-child {
	display: flex;
	gap: 0.75rem;
	flex-wrap: wrap;
	justify-content: center;
	margin-top: 0.5rem;
}

/* Section rhythm on a long content page. */
.rich > h2 {
	margin-top: 3.25rem;
	padding-top: 2rem;
	border-top: 1px solid var(--hairline);
	font-size: clamp(1.4rem, 3vw, 1.85rem);
}

.rich > h2:first-of-type {
	border-top: 0;
	padding-top: 0;
}

/* ---------- brand ---------- */

.brand-name {
	/* The company name is long, so it needs a tighter setting than a short
	   wordmark did — and must not wrap the header onto two lines. */
	font-size: 0.9375rem;
	font-weight: 700;
	letter-spacing: 0.01em;
	line-height: 1.2;
	display: block;
	max-width: 14rem;
}

.brand-footer {
	display: inline-block;
	margin-bottom: 0.75rem;
}

.brand-footer .brand-logo {
	max-height: 2.75rem;
	max-width: 14rem;
}

.brand-footer .brand-name {
	max-width: 18rem;
	font-size: 1rem;
}

@media (max-width: 68rem) {
	.brand-name {
		max-width: 11rem;
		font-size: 0.875rem;
	}
}

/* Access state on a portal document row. */
.badge-ok {
	background: color-mix(in srgb, var(--mei-green) 22%, transparent);
	color: var(--mei-green);
}

/* ---------- homepage about tile with a photo ---------- */

/*
 * With an image the tile becomes a photo with an overlaid caption; without
 * one it keeps the drawn grid. The grid background is dropped in the image
 * case so it cannot show through a transparent PNG.
 */
.ops-panel.has-image {
	position: relative;
	overflow: hidden;
	background-image: none;
	place-content: stretch;
	padding: 0;
}

.ops-panel.has-image img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.ops-panel.has-image .ops-caption {
	position: relative;
	z-index: 1;
	align-self: end;
	width: 100%;
	padding: 2.5rem 1.25rem 1.25rem;
	text-align: center;
	color: #e8eef5;
	/* Gradient rather than a flat overlay: keeps the top of the photo clear
	   while guaranteeing contrast behind the words. */
	background: linear-gradient(to top, rgba(7, 11, 17, 0.88), transparent);
}

.ops-panel.has-image .ops-title {
	margin: 0 0 0.25rem;
}

.ops-panel.has-image .eyebrow {
	margin: 0;
	color: rgba(232, 238, 245, 0.75);
}

/* ---------- admin: tick lists ---------- */

.check-list-field {
	display: flex;
	flex-direction: column;
	gap: 0.35rem;
}

.check-list-label {
	font-weight: 500;
	font-size: 0.875rem;
}

.check-list {
	display: grid;
	grid-template-columns: 1fr;
	gap: 0.15rem;
	max-height: 15rem;
	overflow-y: auto;
	overscroll-behavior: contain;
	scrollbar-gutter: stable;
	scrollbar-width: thin;
	padding: 0.5rem;
	border: 1px solid var(--hairline);
	border-radius: 0.5rem;
	background: var(--overlay-weak);
}

/* Two columns once there is room; short labels waste a full-width row. */
@media (min-width: 48rem) {
	.check-list {
		grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
		gap: 0.15rem 1rem;
	}
}

.check-list-item {
	display: flex;
	align-items: flex-start;
	gap: 0.5rem;
	padding: 0.3rem 0.4rem;
	border-radius: 0.35rem;
	font-size: 0.875rem;
	font-weight: 400;
	cursor: pointer;
	line-height: 1.4;
}

.check-list-item:hover {
	background: var(--overlay);
}

.check-list-item input {
	flex: 0 0 auto;
	margin: 0.15rem 0 0;
}

/* A ticked row stands out, so the current selection is readable at a glance. */
.check-list-item:has(input:checked) {
	color: var(--mei-cyan);
	font-weight: 600;
}

.check-list-item input:focus-visible {
	outline: 2px solid var(--mei-cyan);
	outline-offset: 2px;
}

/* ---------- mobile bottom dock ---------- */

/*
 * Floating bottom dock for phones. Carries the whole primary nav, so it scrolls
 * horizontally rather than dropping items. Hidden above the drawer breakpoint,
 * where the header nav already covers what it does.
 */
.navbar-dock {
	display: none;
}

@media (max-width: 68rem) {
	.navbar-dock {
		display: block;
		position: fixed;
		z-index: 45;
		inset-inline: 0;
		bottom: 0;
		padding: 0.5rem 0.75rem;
		/* Clears the iOS home indicator. */
		padding-bottom: calc(0.5rem + env(safe-area-inset-bottom, 0px));
		pointer-events: none;
	}

	/*
	 * Horizontal scroller. The mask fades items out at both edges so a
	 * half-scrolled row reads as "more this way" instead of a hard cut.
	 */
	.navbar-pill {
		display: flex;
		align-items: center;
		gap: 0.125rem;
		width: max-content;
		max-width: 100%;
		margin-inline: auto;
		padding: 0.25rem;
		border-radius: 999px;
		background: var(--dock-bg, #12181f);
		border: 1px solid rgba(255, 255, 255, 0.08);
		box-shadow: 0 0.625rem 1.75rem rgba(0, 0, 0, 0.3), 0 0.125rem 0.375rem rgba(0, 0, 0, 0.2);
		pointer-events: auto;
		overflow-x: auto;
		overscroll-behavior-inline: contain;
		scroll-behavior: smooth;
		scroll-snap-type: x proximity;
		scrollbar-width: none;
		-webkit-overflow-scrolling: touch;
		mask-image: linear-gradient(to right, transparent 0, #000 1.25rem,
			#000 calc(100% - 1.25rem), transparent 100%);
	}

	.navbar-pill::-webkit-scrollbar {
		display: none;
	}

	/*
	 * Icon-only at rest; the active item grows to fit its label. 2.375rem keeps
	 * the dock compact while staying a usable target.
	 */
	.navtab {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		gap: 0.35rem;
		flex: 0 0 auto;
		scroll-snap-align: center;
		min-width: 2.375rem;
		height: 2.375rem;
		padding-inline: 0.5rem;
		border: 0;
		border-radius: 999px;
		background: transparent;
		color: rgba(255, 255, 255, 0.6);
		font: inherit;
		font-size: 0.8125rem;
		font-weight: 600;
		text-decoration: none;
		cursor: pointer;
		transition: background-color 0.22s ease, color 0.22s ease;
	}

	.navtab-icon {
		display: inline-flex;
		flex: 0 0 auto;
	}

	.navtab-icon svg {
		width: 1.125rem;
		height: 1.125rem;
		display: block;
	}

	/*
	 * grid-template-columns animates where width:auto cannot, so the label can
	 * slide open instead of popping in.
	 */
	.navtab-label {
		display: grid;
		grid-template-columns: 0fr;
		overflow: hidden;
		white-space: nowrap;
		opacity: 0;
		transition: grid-template-columns 0.22s ease, opacity 0.18s ease;
	}

	.navtab-label > * {
		min-width: 0;
	}

	.navtab[aria-current="page"] {
		background: rgba(255, 255, 255, 0.14);
		color: #fff;
		padding-inline: 0.625rem;
	}

	.navtab[aria-current="page"] .navtab-label {
		grid-template-columns: 1fr;
		opacity: 1;
	}

	.navtab:active {
		background: rgba(255, 255, 255, 0.09);
	}

	.navtab:focus-visible {
		outline: 2px solid var(--mei-cyan);
		outline-offset: 2px;
	}

	/*
	 * The dock floats over the page, so the footer needs room underneath or its
	 * last row sits behind it.
	 */
	.site-footer {
		padding-bottom: calc(4.25rem + env(safe-area-inset-bottom, 0px));
	}

	/* Chat launcher would otherwise sit on top of the dock. */
	.chat-launcher {
		width: 3rem;
		height: 3rem;
		font-size: 1.25rem;
		right: 0.875rem;
		bottom: calc(4.25rem + env(safe-area-inset-bottom, 0px));
	}

	/* Drawer open: the dock would float above the scrim, so take it out. */
	body[data-nav-open] .navbar-dock {
		opacity: 0;
		visibility: hidden;
		transition: opacity 0.2s ease, visibility 0s linear 0.2s;
	}
}

@media (max-width: 68rem) and (prefers-reduced-motion: reduce) {
	.navtab,
	.navtab-label,
	.navbar-dock {
		transition-duration: 0.01ms;
	}

	.navbar-pill {
		scroll-behavior: auto;
	}
}
