@charset "UTF-8";
/**
 * Catcha 2026 — the component library
 *
 * 10 Chapter cards (home directory) / 11 Index rows / 12 Pillars
 * 13 Figure grids (logos) / 14 Roster (team) / 15 Spec grid
 * 16 Quotes / 17 Article cards (news)
 *
 * One slice of the stylesheet. Load order and what lives where:
 * docs/MAP.md at the repo root.
 */

/* =====================================================================
 * 10  CHAPTER CARDS  (home directory)
 * ================================================================== */
/* A ruled block, not a tray of floating tiles: the directory reads as one
   table of contents. Ruling comes from the shared rule in §03. */
.chapter-grid {
	display: grid;
	grid-template-columns: 1fr;
}
@media (min-width: 640px) { .chapter-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 980px) { .chapter-grid { grid-template-columns: repeat(3, 1fr); } }

.chapter {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: 10px;
	min-height: 176px;
	padding: clamp(22px, 2.2vw, 32px);
	background: var(--surface);
	text-decoration: none;
	color: inherit;
	overflow: hidden;
	transition: background-color var(--dur) var(--ease);
}

.chapter__num {
	font-family: var(--label);
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.16em;
	color: var(--accent);
	font-variant-numeric: lining-nums tabular-nums;
}
.chapter__main { display: block; margin-top: auto; }
.chapter__title {
	display: block;
	font-family: var(--serif);
	font-size: clamp(22px, 1.9vw, 27px);
	font-weight: 700;
	line-height: 1.14;
	letter-spacing: -0.018em;
	color: var(--ink);
	transition: color var(--dur) var(--ease);
}
.chapter:hover .chapter__title { color: var(--accent); }
.chapter__sub { display: block; margin-top: 8px; font-size: 13.5px; line-height: 1.5; color: var(--ink-3); }

.chapter__go {
	position: absolute;
	right: clamp(20px, 2vw, 26px);
	top: clamp(20px, 2vw, 26px);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 32px; height: 32px;
	border: 1px solid var(--line);
	background: transparent;
	color: var(--ink-3);
	font-size: 15px;
	transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.chapter:hover .chapter__go { background: var(--accent); border-color: var(--accent); color: #fff; }

/* One column on a phone: every card reads as the desktop Investor Relations
   row does — numeral, title, arrow on one line — instead of seven 176px
   boxes that are mostly empty space above a title. */
@media (max-width: 639px) {
	.chapter {
		flex-direction: row;
		align-items: center;
		gap: 18px;
		min-height: 0;
		padding-block: 20px;
	}
	.chapter__main { margin-top: 0; }
	.chapter__go { position: static; margin-left: auto; flex: none; }
}

@media (min-width: 980px) {
	.chapter:last-child {
		grid-column: 1 / -1;
		flex-direction: row;
		align-items: center;
		gap: 24px;
		min-height: 0;
		padding-block: clamp(22px, 2vw, 28px);
	}
	.chapter:last-child .chapter__main { margin-top: 0; }
	.chapter:last-child .chapter__go { position: static; margin-left: auto; }
}

/* =====================================================================
 * 11  INDEX ROWS
 * ================================================================== */
.index {
	background: var(--surface);
	border: 1px solid var(--line);
	overflow: hidden;
}

.index-row {
	position: relative;
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
	gap: 12px clamp(18px, 2.4vw, 36px);
	align-items: start;
	padding: clamp(20px, 2.2vw, 28px) clamp(20px, 2.2vw, 30px);
	border-bottom: 1px solid var(--line-soft);
	text-decoration: none;
	color: inherit;
	transition: background-color var(--dur) var(--ease);
}
.index-row:last-child { border-bottom: 0; }

@media (min-width: 860px) {
	.index-row { grid-template-columns: 54px minmax(0, 5fr) minmax(0, 6fr) auto; align-items: center; }
	.index-row--top { align-items: baseline; }

	/* A listing whose rows should read as one repeating unit — job roles, where
	   a long title wraps to two lines and a short one does not. `1fr` auto rows
	   in an auto-height grid all take the size of the tallest, so the list keeps
	   its rhythm whatever the titles are. Not for `.index-row--top`, whose rows
	   carry paragraphs of genuinely different length. */
	.index--even { display: grid; grid-auto-rows: 1fr; }
}
@media (max-width: 859px) {
	.index-row > :nth-child(1) { grid-column: 1; grid-row: 1; }
	.index-row > :nth-child(2) { grid-column: 2; grid-row: 1; }
	.index-row > :nth-child(3) { grid-column: 2; grid-row: 2; }
	.index-row > :nth-child(4) { grid-column: 2; grid-row: 3; }
	.index-row > :nth-child(3):empty { display: none; }
}


.index-row__num {
	font-family: var(--label);
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.12em;
	color: var(--accent);
	font-variant-numeric: lining-nums tabular-nums;
}
.index-row__title {
	font-family: var(--serif);
	font-size: clamp(19px, 1.7vw, 24px);
	font-weight: 700;
	line-height: 1.18;
	letter-spacing: -0.016em;
	color: var(--ink);
	transition: color var(--dur) var(--ease);
}
a.index-row:hover .index-row__title { color: var(--accent); }
.index-row__body { color: var(--ink-2); font-size: var(--fs-sm); line-height: 1.62; max-width: 60ch; }
.index-row__meta {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	font-family: var(--label);
	font-size: 10.5px;
	font-weight: 600;
	letter-spacing: 0.13em;
	text-transform: uppercase;
	color: var(--ink-3);
	white-space: nowrap;
}
a.index-row:hover .index-row__meta { color: var(--accent); }

/* =====================================================================
 * 12  PILLARS
 * ================================================================== */
.pillar-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(268px, 1fr));
}
.pillar {
	display: flex;
	flex-direction: column;
	gap: 12px;
	padding: clamp(24px, 2.6vw, 34px);
	background: var(--surface);
}
.pillar__num {
	font-family: var(--label);
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.16em;
	color: var(--accent);
}
.pillar__title {
	font-family: var(--serif);
	font-size: clamp(19px, 1.6vw, 23px);
	font-weight: 700;
	line-height: 1.2;
	letter-spacing: -0.016em;
	color: var(--ink);
}
.pillar__list { list-style: none; margin: 4px 0 0; padding: 0; }
.pillar__list li {
	position: relative;
	padding-left: 22px;
	margin-bottom: 10px;
	font-size: 15px;
	line-height: 1.55;
	color: var(--ink-2);
}
.pillar__list li::before {
	content: "";
	position: absolute;
	left: 0; top: 0.55em;
	width: 12px; height: 1.5px;
	background: var(--accent-soft);
}

/* =====================================================================
 * 13  FIGURE GRIDS  (logos)
 * ================================================================== */
/* Hairline rules make 33 marks of wildly different weight read as one even
   matrix — the standard portfolio treatment. */
.figure-grid {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 560px) { .figure-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 860px) { .figure-grid { grid-template-columns: repeat(4, 1fr); } }
@media (min-width: 1100px) { .figure-grid { grid-template-columns: repeat(6, 1fr); } }

.figure-cell {
	display: flex;
	align-items: center;
	justify-content: center;
	padding: clamp(14px, 1.6vw, 22px);
	min-height: clamp(92px, 9vw, 122px);
	background: var(--surface);
	transition: background-color var(--dur) var(--ease);
}

/* Marks range from 1.0 to 6.6 in aspect ratio — fit each to an identical
   box so the grid reads evenly. */
.figure-cell img {
	width: 100%;
	height: clamp(36px, 3.6vw, 52px);
	object-fit: contain;
	object-position: center;
	opacity: 0.92;
	transition: opacity var(--dur) var(--ease);
}
a.figure-cell:hover img { opacity: 1; }

.figure-group + .figure-group { margin-top: clamp(38px, 4vw, 64px); }
.figure-group__head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 18px;
	padding-bottom: 14px;
	margin-bottom: 18px;
	border-bottom: 1px solid var(--line);
}
.figure-group__name {
	font-family: var(--serif);
	font-size: clamp(18px, 1.6vw, 23px);
	font-weight: 700;
	letter-spacing: -0.014em;
	color: var(--ink);
}
.figure-group__count {
	font-family: var(--label);
	font-size: 10.5px;
	font-weight: 600;
	letter-spacing: 0.14em;
	color: var(--ink-4);
	white-space: nowrap;
}

/* ---------------------------------------------------------------------
 * 13b  MARQUEE  (home — the portfolio as one mono strip)
 * ------------------------------------------------------------------ */
/* The portfolio marks in a single slow, continuous line, set in mono so
   33 brand palettes read as one proof line rather than 33 colours arguing
   with the red accent. Colour returns on hover — the mark is the link.
   Two identical runs, each shifted by its own width, loop seamlessly. */
.marquee-band__head {
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
	gap: 18px;
	margin-bottom: clamp(20px, 2.4vw, 32px);
}
.marquee-band__head .label { margin: 0; }

.text-link {
	font-family: var(--label);
	font-size: var(--fs-label);
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--ink);
	text-decoration: none;
	white-space: nowrap;
}
.text-link:hover { color: var(--accent); }

/* Phone: the label and the link no longer share a line — at this width they
   ran into each other. The label takes its own line and the link becomes a
   visible outlined button beneath it, the same furniture as the header's
   Get in Touch. */
@media (max-width: 620px) {
	.marquee-band__head {
		flex-direction: column;
		align-items: flex-start;
		gap: 16px;
	}
	.marquee-band__head .text-link {
		display: inline-flex;
		align-items: center;
		gap: 10px;
		padding: 11px 18px;
		border: 1px solid var(--line-firm);
		background: var(--surface);
		transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
	}
	.marquee-band__head .text-link:hover,
	.marquee-band__head .text-link:focus-visible {
		background: var(--ink);
		border-color: var(--ink);
		color: #fff;
	}
}

.marquee {
	--marquee-gap: clamp(28px, 3.4vw, 52px);
	--marquee-cell: clamp(124px, 12.5vw, 184px);
	/* Held to the shell like every other section, not bled to the viewport:
	   the fades land on the same edges as the headings above and below. */
	display: flex;
	overflow: hidden;
	position: relative;
	border-block: 1px solid var(--line);
}
/* Fade the ends so marks enter and leave instead of being guillotined.
   Painted in the band's own tone rather than as a mask-image: a mask makes
   the strip an isolated group, and the logos' multiply blend then has
   nothing beneath it to multiply against. */
.marquee::before,
.marquee::after {
	content: "";
	position: absolute;
	inset-block: 0;
	z-index: 1;
	width: 12%;
	pointer-events: none;
}
.marquee::before { left: 0;  background: linear-gradient(90deg, var(--band, var(--ground)), transparent); }
.marquee::after  { right: 0; background: linear-gradient(-90deg, var(--band, var(--ground)), transparent); }
.marquee__run {
	display: flex;
	flex: none;
	align-items: center;
	gap: var(--marquee-gap);
	margin: 0;
	padding: clamp(26px, 3vw, 40px) calc(var(--marquee-gap) / 2);
	list-style: none;
	/* The transform makes each run its own stacking context, so the logos'
	   multiply can only reach the run itself — give it the band's tone to
	   multiply against. */
	background: var(--band, var(--ground));
	/* Its own compositor layer, moved in 3D space, so the loop runs on the
	   GPU instead of repainting each frame — the source of the judder on
	   phones. */
	will-change: transform;
	backface-visibility: hidden;
	animation: marquee 110s linear infinite;
}
/* Pause only where a real pointer hovers. On touch, :hover sticks to the
   last tap, and the strip would stop and start under the user's thumb. */
@media (hover: hover) {
	.marquee:hover .marquee__run { animation-play-state: paused; }
}
.marquee:focus-within .marquee__run { animation-play-state: paused; }
@keyframes marquee {
	from { transform: translate3d(0, 0, 0); }
	to   { transform: translate3d(-100%, 0, 0); }
}

/* Every mark gets an identical fixed box, so the run's width is known before
   a single image has loaded. With `width: auto` the run grew as logos
   arrived, and the -100% the loop travels changed mid-animation — the
   visible jump, worst on a slow mobile connection. */
.marquee__item { flex: none; width: var(--marquee-cell); }
.marquee__item a { display: block; }
.marquee__item img {
	display: block;
	width: 100%;
	height: clamp(40px, 4.2vw, 60px);
	object-fit: contain;
	filter: grayscale(1) contrast(1.1);
	/* Several marks ship on a white or solid plate; multiply drops the white
	   into the band so they sit on the page rather than as grey tiles. */
	mix-blend-mode: multiply;
	opacity: 0.6;
	transition: filter var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.marquee__item a:hover img,
.marquee__item a:focus-visible img { filter: none; opacity: 1; }

/* =====================================================================
 * 14  ROSTER  (team)
 * ================================================================== */
.roster {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
}

.person {
	display: flex;
	flex-direction: column;
	background: var(--surface);
	overflow: hidden;
	transition: background-color var(--dur) var(--ease);
}

.person__btn {
	display: block;
	width: 100%;
	padding: 0 0 20px;
	background: none; border: 0;
	text-align: left;
	cursor: pointer;
}

.person__portrait, .person__name, .person__role { display: block; }

/* Square, matching the source files. The portraits are square uploads; a 4/5
   frame cropped them and forced a bigger upscale of already-small images. */
.person__portrait {
	position: relative;
	aspect-ratio: 1;
	overflow: hidden;
	background: var(--tint);
}
.person__portrait img {
	width: 100%; height: 100%;
	object-fit: cover;
	object-position: center top;
	transition: transform 620ms var(--ease-o);
}
.person__btn:hover .person__portrait img { transform: scale(1.04); }

.person__name {
	font-family: var(--serif);
	font-size: 18px;
	font-weight: 700;
	line-height: 1.22;
	letter-spacing: -0.014em;
	color: var(--ink);
	margin: 18px 20px 4px;
	transition: color var(--dur) var(--ease);
}
.person__btn:hover .person__name { color: var(--accent); }
.person__role { font-size: 13px; line-height: 1.5; color: var(--ink-3); margin: 0 20px; }

.person__more {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	margin: 12px 20px 0;
	font-family: var(--label);
	font-size: 10px;
	font-weight: 600;
	letter-spacing: 0.13em;
	text-transform: uppercase;
	color: var(--accent);
}
.person__more .sign { transition: transform var(--dur) var(--ease); }
.person__btn:hover .sign { transform: translateX(3px); }
.person__btn--static { cursor: default; }

/* The bio text is rendered into the page and borrowed by the modal. It is
   only hidden once script has confirmed the modal is wired up, so with JS
   off the bios simply read as text under each portrait. */
.person__bio p {
	font-size: 14px;
	line-height: 1.66;
	color: var(--ink-2);
	margin: 0 20px 16px;
	padding-top: 14px;
	border-top: 1px solid var(--line-soft);
}
.person__bio p + p { border-top: 0; padding-top: 0; }
.has-bio-modal .person__bio { display: none; }

/* ── Bio modal ───────────────────────────────────────────────────────
 * Portrait on the left, bio on the right; a single column on small
 * screens with the panel filling the viewport.
 * ------------------------------------------------------------------ */
.modal {
	position: fixed;
	inset: 0;
	z-index: 200;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: clamp(12px, 3vw, 44px);
}
.modal[hidden] { display: none; }

.modal__scrim {
	position: absolute;
	inset: 0;
	background: rgba(43, 22, 27, 0.55);
	opacity: 0;
	transition: opacity 200ms var(--ease);
}
.modal.is-open .modal__scrim { opacity: 1; }

.modal__panel {
	position: relative;
	display: grid;
	width: 100%;
	max-width: 940px;
	/* One window for everyone. The panel used to take its height from the bio
	   inside it, so each person opened a differently-shaped dialog — the
	   shortest bio is 412 characters and the longest 969, which swung the
	   panel through roughly 230px of height and made the portrait jump size
	   and position from one person to the next. Fixed here, capped by the
	   viewport; the difference is absorbed by the bio column scrolling. */
	height: min(580px, 100%);
	max-height: 100%;
	overflow: hidden;
	background: var(--surface);
	border: 1px solid var(--line-firm);
	box-shadow: 0 30px 80px rgba(43, 22, 27, 0.28);
	opacity: 0;
	transform: translateY(10px);
	transition: opacity 220ms var(--ease-o), transform 220ms var(--ease-o);
}
.modal.is-open .modal__panel { opacity: 1; transform: none; }
@media (min-width: 760px) {
	.modal__panel { grid-template-columns: minmax(0, 4fr) minmax(0, 7fr); }
}

.modal__media {
	display: flex;
	align-items: center;      /* the portrait sits on the column's centre line */
	justify-content: center;
	background: var(--tint);
	overflow: hidden;
}
.modal__media[hidden] { display: none; }
/* No portrait: give the bio the whole panel rather than leaving it in the
   narrow first column of a two-column grid. */
.modal__panel:has(.modal__media[hidden]) { grid-template-columns: 1fr; }
.modal__media img { width: 100%; object-fit: cover; }

/* Single column: the panel is height-capped, so the media row has to be given
   an explicit height or the grid squeezes it to a sliver — and the rows have
   to be named, so the bio row is the one that takes the remaining space and
   scrolls rather than pushing the portrait off the top. */
@media (max-width: 759px) {
	.modal__panel { grid-template-rows: auto minmax(0, 1fr); }
	/* The bio scrolls under this edge, so it needs one — the same hairline the
	   media column carries between the two on wide screens. */
	.modal__media { flex: none; border-bottom: 1px solid var(--line-soft); }
	.modal__media img { height: 250px; object-position: center 22%; }
}
/* Two columns: the portrait is held at the 1/1 of the sources rather than
   stretched to the column's height. Covering the full height scaled a square
   source into a ~340×580 frame — magnified past the size the roster itself
   uses, and cropped to about 60% of its width, which cut the head off. So it
   keeps its square and is centred on the column instead, the tint carrying
   the space above and below. */
@media (min-width: 760px) {
	.modal__media { border-right: 1px solid var(--line-soft); }
	.modal__media img { aspect-ratio: 1; height: auto; max-height: 100%; }
}

/* The bio is the scroller — not the panel, and not the whole column. Scrolling
   the panel carried the portrait and the close button with it; scrolling the
   column carried the name and role, so a long bio was read under a heading
   that had slid away. The head is pinned and only the paragraphs move.
   `min-height: 0` is what allows it: a flex item defaults to `min-height:
   auto` and refuses to shrink below its content, which pushes the overflow
   back out to the panel. */
.modal__body {
	display: flex;
	flex-direction: column;
	min-height: 0;
	overflow: hidden;
	/* No bottom padding — the scroller carries it, so the text clips at the
	   panel's own edge rather than part-way up a white band. */
	padding: clamp(26px, 3.4vw, 46px) clamp(26px, 3.4vw, 46px) 0;
}
.modal__head {
	flex: none;
	padding-bottom: clamp(15px, 1.7vw, 21px);
	border-bottom: 1px solid var(--line-soft);
}
/* The two properties above are Chrome 121+, Firefox and Safari 18.2+. These
   are the fallback for older WebKit, kept behind @supports because where both
   are understood the standard properties win and these are dropped entirely —
   which is why styling the bar through them alone did nothing. */
@supports not (scrollbar-width: thin) {
	.modal__bio::-webkit-scrollbar { width: 11px; }
	.modal__bio::-webkit-scrollbar-track { background: transparent; }
	.modal__bio::-webkit-scrollbar-thumb {
		background: var(--line);
		/* Border, not width: it insets the thumb to a 3px rule without the
		   track changing width and shifting the text under it. */
		border: 4px solid var(--surface);
	}
	.modal__bio::-webkit-scrollbar-thumb:hover { background: var(--line-firm); }
}
.modal__name {
	font-family: var(--serif);
	font-size: clamp(23px, 2.4vw, 32px);
	font-weight: 700;
	line-height: 1.12;
	letter-spacing: -0.02em;
	color: var(--ink);
}
.modal__role {
	margin: 12px 0 0;
	padding-top: 12px;
	border-top: 2px solid var(--accent);
	display: inline-block;
	font-family: var(--label);
	font-size: 10.5px;
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--ink-3);
}
.modal__bio {
	flex: 1 1 auto;
	min-height: 0;
	overflow-y: auto;
	overscroll-behavior: contain;
	/* Inside the scroller, not above it: the first line then passes under the
	   rule as it scrolls, rather than leaving a gap the text floats through. */
	padding: clamp(18px, 2vw, 26px) 0 clamp(26px, 3.4vw, 46px);
	margin-top: 0;
	max-width: none;
	/* Reserve the bar's width whether or not this bio is long enough to need
	   it, so the measure is identical for every person — the point of fixing
	   the panel size at all. End edge only: `both-edges` also inset the text
	   from the left, which put every line 8px right of the name and the rule
	   above it. The right edge is ragged anyway; the left one is the edge the
	   eye reads down. (A no-op where the browser draws overlay scrollbars:
	   those take no space.) */
	scrollbar-gutter: stable;
	/* A hairline, not chrome: the tone the rest of the page is ruled with. */
	scrollbar-width: thin;
	scrollbar-color: var(--line-firm) transparent;
}
.modal__bio p:last-child { margin-bottom: 0; }

/* Text is cut mid-letterform where the scroller clips it — at the rule above
   and at the panel's bottom edge. Fade it out over the last few pixels
   instead, and only on the side that actually has more text behind it: a
   permanent fade would wash out the first line of a short bio that never
   scrolls at all. Script sets the two classes. */
.modal__bio.is-cut-top {
	-webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 24px);
	mask-image: linear-gradient(to bottom, transparent 0, #000 24px);
}
.modal__bio.is-cut-bottom {
	-webkit-mask-image: linear-gradient(to top, transparent 0, #000 24px);
	mask-image: linear-gradient(to top, transparent 0, #000 24px);
}
.modal__bio.is-cut-top.is-cut-bottom {
	-webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 24px, #000 calc(100% - 24px), transparent 100%);
	mask-image: linear-gradient(to bottom, transparent 0, #000 24px, #000 calc(100% - 24px), transparent 100%);
}
.modal__bio p { font-size: var(--fs-sm); line-height: 1.7; color: var(--ink-2); }

.modal__close {
	position: absolute;
	top: 0; right: 0;
	z-index: 1;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 46px; height: 46px;
	padding: 0;
	background: var(--surface);
	border: 0;
	border-left: 1px solid var(--line);
	border-bottom: 1px solid var(--line);
	color: var(--ink-2);
	cursor: pointer;
	transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.modal__close:hover { background: var(--ink); color: #fff; }
.modal__close svg { width: 17px; height: 17px; }

/* =====================================================================
 * 15  SPEC GRID  (acquisition criteria)
 * ================================================================== */
.spec-grid {
	display: grid;
	grid-template-columns: 1fr;
}
@media (min-width: 620px) { .spec-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .spec-grid { grid-template-columns: repeat(3, 1fr); } }

.spec {
	display: flex;
	flex-direction: column;
	gap: 12px;
	padding: clamp(24px, 2.6vw, 32px);
	background: var(--surface);
}
.spec__icon { width: 88px; height: 88px; object-fit: contain; object-position: left center; }
.spec__title {
	font-family: var(--serif);
	font-size: 20px;
	font-weight: 700;
	letter-spacing: -0.014em;
	color: var(--ink);
}
.spec__body { font-size: var(--fs-sm); line-height: 1.6; color: var(--ink-2); }

/* =====================================================================
 * 16  QUOTES
 * ================================================================== */
.quote-grid {
	display: grid;
	grid-template-columns: 1fr;
}
@media (min-width: 860px) { .quote-grid { grid-template-columns: repeat(2, 1fr); } }

.quote {
	display: flex;
	flex-direction: column;
	gap: 16px;
	margin: 0;
	padding: clamp(26px, 2.8vw, 38px);
	background: var(--surface);
}
/* The decorative element is a &rdquo; in the markup. An oversized curly
   quote is stock-template furniture, so it is collapsed into the same short
   accent rule used by section labels — the glyph is aria-hidden, so nothing
   is lost. */
.quote__mark {
	display: block;
	width: 32px; height: 2px;
	background: var(--accent);
	font-size: 0;
	line-height: 0;
	/* Explicit, so a contrast audit reads this as intentionally hidden rather
	   than as low-contrast text. */
	color: transparent;
	overflow: hidden;
}
.quote__text {
	font-family: var(--serif);
	font-size: var(--fs-body);
	font-weight: 400;
	line-height: 1.6;
	color: var(--ink);
	flex: 1;
	margin: 0;
}
.quote__who {
	display: flex;
	align-items: center;
	gap: 12px;
	padding-top: 14px;
	border-top: 1px solid var(--line-soft);
	font-family: var(--label);
	font-size: 10px;
	font-weight: 600;
	letter-spacing: 0.13em;
	text-transform: uppercase;
	color: var(--ink-3);
}
.quote__who img {
	width: 40px; height: 40px;
	object-fit: cover;
	flex: none;
	border: 1px solid var(--line);
}

/* =====================================================================
 * 17  ARTICLE CARDS  (news)
 * ================================================================== */
.lead-story {
	display: grid;
	gap: clamp(20px, 2.6vw, 40px);
	margin-bottom: clamp(32px, 4vw, 56px);
	padding: clamp(18px, 2vw, 26px);
	background: var(--surface);
	border: 1px solid var(--line);
	text-decoration: none;
	color: inherit;
	transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.lead-story:hover { border-color: var(--line-firm); }
@media (min-width: 860px) {
	.lead-story { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); align-items: center; }
}
.lead-story > span, .lead-story__media, .lead-story__title,
.card__media, .card__date, .card__title { display: block; }

.lead-story__media { overflow: hidden; background: var(--tint-deep); }
.lead-story__media img {
	width: 100%;
	aspect-ratio: 16 / 10;
	object-fit: cover;
	transition: transform 640ms var(--ease-o);
}
.lead-story:hover .lead-story__media img { transform: scale(1.03); }
.lead-story__title {
	font-family: var(--serif);
	font-size: clamp(23px, 2.4vw, 34px);
	font-weight: 700;
	line-height: 1.14;
	letter-spacing: -0.02em;
	color: var(--ink);
	margin-top: 12px;
	transition: color var(--dur) var(--ease);
}
.lead-story:hover .lead-story__title { color: var(--accent); }
.lead-story > span:last-child { padding-inline: clamp(4px, 1vw, 14px); }

.card-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}

.card {
	display: flex;
	flex-direction: column;
	padding: clamp(20px, 2vw, 28px);
	background: var(--surface);
	text-decoration: none;
	color: inherit;
	transition: background-color var(--dur) var(--ease);
}
.card__media { overflow: hidden; background: var(--tint-deep); margin-bottom: 15px; }
.card__media img {
	width: 100%;
	aspect-ratio: 16 / 10;
	object-fit: cover;
	transition: transform 640ms var(--ease-o);
}
.card:hover .card__media img { transform: scale(1.04); }
.card__date {
	font-family: var(--label);
	font-size: 10px;
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--ink-4);
	margin-bottom: 8px;
	padding-inline: 4px;
}
.card__title {
	font-family: var(--serif);
	font-size: 18.5px;
	font-weight: 700;
	line-height: 1.26;
	letter-spacing: -0.014em;
	color: var(--ink);
	padding: 0 4px 6px;
	transition: color var(--dur) var(--ease);
}
.card:hover .card__title { color: var(--accent); }

.pagination {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	margin-top: clamp(36px, 4vw, 60px);
}
.pagination a, .pagination span {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 40px; height: 40px;
	padding: 0 14px;
	font-family: var(--label);
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--ink-3);
	text-decoration: none;
	background: var(--surface);
	border: 1px solid var(--line);
	transition: background-color 150ms var(--ease), color 150ms var(--ease), border-color 150ms var(--ease);
}
.pagination a:hover { background: var(--ink); border-color: var(--ink); color: #fff; }
.pagination .is-current { background: var(--accent); border-color: var(--accent); color: #fff; }

