/* =============================================================================
   Base layer — resets, layout primitives, buttons, header, footer, prose.
   Every value here comes from tokens.css. No literal colours or font sizes.

   Scope note: this file styles what every page has — the chrome from
   _partials/nav.html and _partials/footer.html, the shared button, and the
   document-page components used by /blog/, /docs/, /our-work/, /services/
   and /design-system/. A page's own sections belong in assets/css/pages/.
   ========================================================================== */

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

/* The layout viewport is established by the ROOT element, not by <body> — and
   `position: fixed` boxes are sized off that viewport, not off body. So the
   clip on body (below) stops a horizontal spill from PAINTING but still lets
   the viewport widen, which is exactly what stretched the fixed header across
   the screen on mobile after scrolling, and pushed the fixed chat sticker to
   the far right with it. Clipping here is what actually holds the viewport at
   the screen width; the body rule alone never could.

   `clip`, not `hidden`: `hidden` would turn the root into a scroll container,
   which changes what page scrolling and `position: sticky` resolve against
   (the /blog/ sidebar depends on that). `clip` sets no scroll container. */
html {
	overflow-x: clip;
}

body {
	margin: 0;
	font-family: var(--font-sans);
	font-size: var(--text-base);
	line-height: var(--leading-normal);
	color: var(--color-ink);
	background: var(--color-surface);
	-webkit-font-smoothing: antialiased;

	/* The header is fixed, so every page reserves its height. Pages that draw
	   their own space behind it — the home hero — opt out below. */
	padding-top: var(--header-offset);

	/* Second line of defence: keeps a stray spill from painting outside the page
	   box. It does NOT hold the layout viewport — that is the `html` rule at the
	   top of this file, and assuming this one did the job is why the mobile
	   "header gets wider" report survived the first attempt at fixing it. */
	overflow-x: clip;
}

h1,
h2,
h3 {
	line-height: var(--leading-tight);
	letter-spacing: -0.02em;
	margin: 0 0 var(--space-4);
}

h1 {
	font-size: var(--text-3xl);
}
h2 {
	font-size: var(--text-2xl);
}
h3 {
	font-size: var(--text-xl);
}

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

a {
	color: var(--color-brand-strong);
	text-decoration-thickness: 1px;
	text-underline-offset: 2px;
}

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

img,
video {
	max-width: 100%;
	height: auto;
}

code {
	font-family: var(--font-mono);
	font-size: 0.9em;
	background: var(--color-surface-sunken);
	padding: 0.15em 0.4em;
	border-radius: var(--radius-sm);
}

pre {
	background: var(--color-surface-sunken);
	border: 1px solid var(--color-line);
	border-radius: var(--radius-md);
	padding: var(--space-4);
	overflow-x: auto;
}

pre code {
	background: none;
	padding: 0;
}

/* --- Utilities ------------------------------------------------------------ */

.u-visually-hidden {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
}

/* A <br> that only takes effect below 768. Several mobile frames break a heading
   at a specific word; above that width the line has room and the break would be
   wrong. Turned on in the mobile block at the end of this file. */
.u-br-mobile {
	display: none;
}

.u-skip-link {
	position: absolute;
	left: var(--space-4);
	top: calc(-1 * var(--space-16));
	z-index: var(--z-skip-link);
	background: var(--color-surface);
	color: var(--color-ink);
	padding: var(--space-2) var(--space-4);
	border-radius: var(--radius-sm);
	transition: top var(--transition);
}

.u-skip-link:focus {
	top: var(--space-4);
}

/* Eyebrow / label. The design writes these in square brackets — [SERVICES] —
   with the wide Foundations tracking. Bracket characters are content, not
   decoration, so they live in the markup. */
.eyebrow {
	font-family: var(--font-label);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: var(--ls-label);
}

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

/* --- Buttons -------------------------------------------------------------- */
/* Seven variants, all pills, all uppercase. Six are taken from the Figma
   Components page (file `Orange Loops — Home-Page`, page `Components`, node
   42:29); the seventh, Secondary / Muted, is not on that page and says so where
   it is defined.

   READ THIS BEFORE "FIXING" THE TYPE BELOW. That Components page and the actual
   page design disagree, and the disagreement is not resolved:

     · The Components symbols set Plus Jakarta Sans Bold 14px / 0.04em tracking,
       padding 16/32, and stand 50px tall.
     · Every real instance overrides all of that. The CTA band (node 241:56)
       uses Eurostile Bold 15px / 0.15em and pins the height to 33px. The modal
       submit is 37px. No two agree.

   The home frame (154:1268) is the artifact this site was actually ported from,
   and it is too large to read through the Figma API — it returns no child IDs.
   So the type and geometry here are left exactly as the port measured them, and
   only the things both sources agree on, or that the Components page is the sole
   authority for, were changed. Those are called out per rule.

   Hover is the one thing only the Components page specifies, so hover states are
   taken from it verbatim except where noted. */

.btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	font: var(--label-s);
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	text-decoration: none;
	white-space: nowrap;
	cursor: pointer;
	border: none;
	padding: var(--space-2) var(--space-4);
	border-radius: var(--radius-pill);
	transition:
		background var(--transition),
		color var(--transition),
		border-color var(--transition),
		box-shadow var(--transition);
}

/* Primary / Orange — orange fill. Hovers to a black pill with a cream ring.
   The ring is an inset shadow rather than a border because this variant has no
   border at rest, and adding one on hover would grow the box by 3px and jog the
   layout. Same geometry, same picture. */
.btn--pill {
	background: var(--color-brand);
	color: var(--color-ink-on-orange);
}

.btn--pill:hover {
	background: var(--color-black);
	color: var(--color-cream);
	box-shadow: inset 0 0 0 1.5px var(--color-cream);
}

/* Primary / Light — cream fill on a dark ground. */
.btn--solid-cream {
	background: var(--color-cream);
	color: var(--color-black);
}

/* Primary / Dark — black fill with a cream ring, hovers to orange. The ring
   goes transparent rather than away, so the box does not resize. */
.btn--solid-dark {
	background: var(--color-black);
	color: var(--color-white);
	border: 1.5px solid var(--color-cream);
}

.btn--solid-dark:hover {
	background: var(--color-brand);
	border-color: transparent;
	color: var(--color-white);
}

/* Secondary / Dark — outline for use on cream. Hovers to a black fill with
   orange text. */
.btn--outline {
	font-size: var(--text-2xs);
	letter-spacing: 0.14em;
	color: var(--color-carbon);
	border: 1.5px solid var(--color-carbon);
}

.btn--outline:hover {
	background: var(--color-black);
	border-color: var(--color-black);
	color: var(--color-brand);
}

/* Secondary / Light — outline for use on black or orange.
   Both Figma sources agree the border and label are cream (#f0eee9), not white;
   this used to be --color-white over an 85% white border. That is the one colour
   change here that is backed by both.

   Deviation, deliberate: the Components page draws the hover as a black fill
   with the border dropped. It reads there because that page sits on #1c1c1c, but
   every place this variant is actually used is black — dropping the border would
   make the button vanish. The border is kept. */
.btn--outline-light {
	letter-spacing: 0.12em;
	color: var(--color-cream);
	border: 1.5px solid var(--color-cream);
}

.btn--outline-light:hover {
	background: var(--color-black);
	color: var(--color-cream);
}

/* Secondary / Muted — the same geometry as Secondary / Light in --color-sand-soft
   instead of cream. A seventh variant, and the Components page is not its source:
   it appears on the Our Work frame (node 961:5941) as an instance-level colour
   override, not as a published variant, which is why the label sits back from the
   card's own cream heading rather than competing with it.

   No hover pair is drawn there either. Rather than invent one, it borrows the
   Secondary / Light hover — the only difference between the two variants is the
   resting colour, and both resolve to the same thing once hovered. */
.btn--outline-muted {
	letter-spacing: 0.12em;
	color: var(--color-sand-soft);
	border: 1.5px solid var(--color-sand-soft);
}

.btn--outline-muted:hover {
	background: var(--color-black);
	color: var(--color-cream);
	border-color: var(--color-cream);
}

/* Secondary / Orange — orange outline. The Figma file draws no hover pair for
   this one or for Primary / Light, so neither gets a hover rule; inventing one
   would put a value in the design system that no design specifies. */
.btn--outline-orange {
	color: var(--color-brand);
	border: 1.5px solid var(--color-brand);
}

/* --- Site header ---------------------------------------------------------- */
/* A floating cream bar, pinned at every breakpoint. */

.site-header {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	z-index: var(--z-header);
	padding: 28px 45px 0;
}

/* Three columns, not a flex row, so the nav sits in the middle of the BAR rather
   than in the middle of whatever the logo and the CTA leave over.

   It used to be `display: flex` with `margin: 0 auto` on the nav and
   `margin-left: auto` on the CTA. That is three auto margins in one flex line,
   and flexbox splits free space equally between all of them — so the nav landed
   a third of the way into the gap instead of halfway, 80px left of centre at
   1440. Equal `1fr` side columns cannot drift that way whatever the logo and CTA
   end up measuring.

   `minmax(0, 1fr)` rather than `1fr` so the side columns may shrink below their
   content instead of overflowing the bar at the in-between widths.

   Residual: the bar's padding is asymmetric (26 left / 20 right, optical
   alignment for the logo mark), so the grid centres the nav on the content box
   and lands 3px right of the bar's true centre. Left alone deliberately —
   chasing it would mean re-tuning the logo offset at all three breakpoints,
   which each re-declare this padding, to remove an offset nobody can see. */
.site-header__bar {
	max-width: 1350px; /* the 1440 frame minus its 45px gutters */
	height: 86px;
	margin: 0 auto;
	padding: 0 20px 0 26px;
	background: var(--color-cream);
	border-radius: var(--radius-header);
	box-shadow: var(--shadow-header);
	display: grid;
	grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
	align-items: center;
	gap: var(--space-8);
}

.site-logo {
	display: inline-flex;
	align-items: center;
	text-decoration: none;
	color: var(--color-carbon);
	/* A grid item stretches by default. Without this the link's box would span
	   its whole column and swallow ~300px of empty bar as a click target. */
	justify-self: start;
}

.site-logo__mark {
	display: block;
	height: 46px;
	width: auto;
}

/* Default (cream bar): the dark lockup shows, the cream one is hidden. The dark
   theme flips these — see "Dark theme" below. */
.site-logo__mark--light {
	display: none;
}

.site-nav {
	display: flex;
	align-items: center;
	gap: 38px;
	margin: 0; /* the grid centres it now — an auto margin here would fight it */
}

.site-nav a {
	font: var(--label-s);
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--color-carbon);
	text-decoration: none;
	transition: color var(--transition);
}

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

/* --- Submenus ------------------------------------------------------------ */
/* A nav item with `children` in site/_data/site.json renders as a button plus a
   panel: floating under the bar here, expanded inline inside the menu on mobile.
   The open item turns orange from aria-expanded, so the state lives in one
   place and the styling follows it. */

.site-nav__sub-toggle {
	font: var(--label-s);
	letter-spacing: var(--ls-label);
	text-transform: uppercase;
	color: var(--color-carbon);
	background: none;
	border: none;
	padding: 0;
	cursor: pointer;
	display: inline-flex;
	align-items: center;
	gap: 7px;
	transition: color var(--transition);
}

.site-nav__sub-toggle:hover,
.site-nav__sub-toggle[aria-expanded="true"] {
	color: var(--color-brand);
}

/* The caret is a mobile-only affordance; on desktop the panel is a hover-height
   drop and the frame does not draw one. */
.site-nav__caret {
	display: none;
	flex: none;
	transition: transform 0.2s ease;
}

.site-nav__sub-toggle[aria-expanded="true"] .site-nav__caret {
	transform: rotate(180deg);
}

.site-nav__group {
	position: relative;
	display: flex;
	align-items: center;
}

/* Hangs 8px below the bar. The group is centred on the 86px bar, so from the
   group's own centre (50%) the bar's bottom edge is 43px down. It used to be
   `100% + 22px` from the group, which landed 13px above the bar's bottom and
   cut across the dark theme's ring. */
.site-nav__submenu {
	position: absolute;
	top: calc(50% + 43px + 8px);
	left: 50%;
	transform: translateX(-50%);
	min-width: 144px;
	display: flex;
	flex-direction: column;
	background: var(--color-cream);
	border-radius: var(--radius-lg);
	padding: var(--space-2) 0;
	box-shadow: var(--shadow-popover);
	z-index: 5;
}

.site-nav__submenu[hidden] {
	display: none;
}

/* The submenu opens on hover and closes when the pointer leaves the bar
   (nav.js). This transparent strip fills the 8px gap between the two — and the
   dark theme's 2px ring — so the pointer never crosses empty space on the way
   down. Desktop only: on mobile the submenu is inline and this would sit over
   the Company row. */
@media (min-width: 768px) {
	.site-nav__submenu::before {
		content: "";
		position: absolute;
		left: 0;
		right: 0;
		bottom: 100%;
		height: 12px;
	}
}

.site-nav__submenu a {
	padding: var(--space-3) var(--space-6);
	white-space: nowrap;
	text-align: center;
}

.site-nav__submenu a + a {
	border-top: 1px solid var(--color-hairline);
}

/* The in-panel contact CTA is mobile-only; desktop uses the header's own. */
.site-nav__cta {
	display: none;
}

.site-header__cta {
	justify-self: end;
}

.site-nav__toggle {
	display: none;
	flex-direction: column;
	justify-content: center;
	gap: 7px;
	width: 42px;
	height: 42px;
	padding: 0;
	background: none;
	border: none;
	cursor: pointer;
}

.site-nav__toggle span {
	display: block;
	width: 32px;
	height: 3px;
	background: var(--color-carbon);
	border-radius: 2px;
	transform-origin: center;
	transition:
		transform 0.25s ease,
		opacity 0.2s ease;
}

/* Open panel: the three bars fold into an X (Figma mobile states). */
.site-nav__toggle[aria-expanded="true"] span:nth-child(1) {
	transform: translateY(10px) rotate(45deg);
}
.site-nav__toggle[aria-expanded="true"] span:nth-child(2) {
	opacity: 0;
}
.site-nav__toggle[aria-expanded="true"] span:nth-child(3) {
	transform: translateY(-10px) rotate(-45deg);
}

/* --- Header: dark theme --------------------------------------------------- */
/* `headerTheme: dark` in a page's front matter (Labs, Careers, Privacy). Same
   markup and layout; only the theme changes. The bar goes black with a cream
   ring — the ring is what keeps a black bar visible over a black hero — the
   lockup swaps to its cream cut and the hamburger turns cream.

   This used to be copied into each of those three pages' CSS, and the copies
   had already drifted (Careers had lost the shadow). */
.site-header--dark .site-header__bar {
	background: var(--color-black);
	border: 2px solid var(--color-cream);
}

.site-header--dark .site-logo__mark--dark {
	display: none;
}

.site-header--dark .site-logo__mark--light {
	display: block;
}

.site-header--dark .site-nav__toggle span {
	background: var(--color-cream);
}

/* Links go cream on desktop only: below 768 they live in the shared cream
   drop-panel, so they stay dark there. The hover and current-page colours are
   restated because the cream rule would otherwise outrank them. */
@media (min-width: 768px) {
	.site-header--dark .site-nav a,
	.site-header--dark .site-nav__sub-toggle {
		color: var(--color-cream);
	}

	.site-header--dark .site-nav a:hover,
	.site-header--dark .site-nav a[aria-current="page"],
	.site-header--dark .site-nav__sub-toggle:hover,
	.site-header--dark .site-nav__sub-toggle[aria-expanded="true"] {
		color: var(--color-brand);
	}

	/* The Company submenu matches the bar it hangs from: black with the same
	   cream ring. Its links are already cream from the rule above — on the
	   default cream panel that made them invisible. */
	.site-header--dark .site-nav__submenu {
		background: var(--color-black);
		border: 2px solid var(--color-cream);
	}

	.site-header--dark .site-nav__submenu a + a {
		border-top-color: var(--color-sand);
	}
}

@media (max-width: 1280px) {
	.site-header {
		padding: 24px var(--space-6) 0;
	}

	.site-header__bar {
		gap: 18px;
		padding: 0 var(--space-4) 0 18px;
	}

	.site-nav {
		gap: var(--space-6);
	}

	.site-nav a,
	.site-nav__sub-toggle {
		font-size: var(--text-2xs);
		white-space: nowrap;
	}
}

@media (max-width: 980px) {
	.site-header__bar {
		gap: var(--space-3);
	}

	.site-nav {
		gap: var(--space-3);
	}

	.site-nav a,
	.site-nav__sub-toggle {
		font-size: var(--text-3xs);
		letter-spacing: 0.05em;
	}

	.site-logo__mark {
		height: 42px;
	}

	.site-header__cta {
		font-size: var(--text-2xs);
		padding: var(--space-2) var(--space-3);
	}
}

@media (max-width: 767px) {
	body {
		padding-top: var(--header-offset-sm);
	}

	.site-header {
		padding: 18px var(--page-gutter-sm) 0;
	}

	.site-header__bar {
		height: 61px;
		gap: var(--space-2);
		padding: 0 14px 0 var(--space-3);
		border-radius: var(--radius-header-sm);
		/* Stays a grid — only the template changes — so the bar never flips its
		   layout system across the breakpoint. Nav and CTA are display:none here,
		   leaving two items: the logo, pinned left by its own justify-self in the
		   1fr column, and the hamburger in the auto column at the right. Keeping
		   one layout system at every width also means the desktop three-column
		   template can never linger under a flex bar during a reflow. */
		grid-template-columns: 1fr auto;
	}

	.site-logo__mark {
		height: 40px;
	}

	/* The header's own CTA gives way to the one inside the menu panel, which is
	   where the mobile frame puts it. */
	.site-header__cta {
		display: none;
	}

	.u-br-mobile {
		display: inline;
	}

	.site-nav__toggle {
		display: flex;
		margin-left: auto;
	}

	.site-nav {
		display: none;
		position: absolute;
		top: 87px;
		left: var(--page-gutter-sm);
		right: var(--page-gutter-sm);
		flex-direction: column;
		align-items: stretch;
		gap: 0;
		margin: 0;
		padding: 10px 27px 24px;
		background: var(--color-cream);
		border-radius: 30px;
	}

	.site-nav.is-open {
		display: flex;
	}

	.site-nav a,
	.site-nav__sub-toggle {
		display: block;
		width: 100%;
		font-size: var(--text-md);
		letter-spacing: var(--ls-label);
		padding: 17px 0;
		border-bottom: 1px solid var(--color-sand-soft);
	}

	.site-nav__sub-toggle {
		justify-content: flex-start;
		text-align: left;
	}

	/* The caret is drawn at this width only. */
	.site-nav__caret {
		display: inline-block;
	}

	/* Company expands inside the panel rather than floating over it. */
	.site-nav__group {
		display: flex;
		flex-direction: column;
		align-items: stretch;
		width: 100%;
	}

	.site-nav__submenu {
		position: static;
		transform: none;
		min-width: 0;
		background: none;
		box-shadow: none;
		border-radius: 0;
		padding: 0 0 0 30px;
	}

	.site-nav__submenu a {
		text-align: left;
	}

	.site-nav__submenu a + a {
		border-top: none;
	}

	/* Contact pill at the foot of the panel. The type selector is needed to beat
	   `.site-nav a` above, which gives every item a full-width row and a rule. */
	.site-nav a.site-nav__cta {
		display: inline-flex;
		align-self: flex-end;
		width: auto;
		margin-top: 20px;
		padding: 9px var(--space-8);
		border-bottom: none;
		font-size: var(--text-xs);
		letter-spacing: var(--ls-label);
	}
}

/* --- Document-page sections ----------------------------------------------- */
/* Used by /blog/, /docs/, /our-work/, /services/, /design-system/. The home
   page does not use these — see assets/css/pages/home.css. */

/* padding-block, not the shorthand: these always sit on a .container, and a
   shorthand here zeroed its side gutter — /docs/ and /design-system/ ran to
   the screen edge below 72rem. */
.hero {
	padding-block: var(--space-24) var(--space-16);
}

.hero__eyebrow {
	color: var(--color-brand-strong);
	font-size: var(--text-sm);
	text-transform: uppercase;
	letter-spacing: 0.08em;
	margin-bottom: var(--space-3);
}

.hero h1 {
	font-size: var(--text-4xl);
	letter-spacing: var(--ls-display);
	max-width: 22ch;
}

/* 55px does not fit a one-word title ("Documentation") in a phone's column once
   the container keeps its gutter. */
@media (max-width: 767px) {
	.hero h1 {
		font-size: var(--text-3xl);
	}
}

.hero__lede {
	font-size: var(--text-lg);
	color: var(--color-ink-muted);
	max-width: 52ch;
	/* A file path in <code> is one unbreakable word, wider than a 320 column. */
	overflow-wrap: anywhere;
}

.section {
	padding-block: var(--space-16);
	border-top: 1px solid var(--color-line);
}

.card-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
	gap: var(--space-6);
	list-style: none;
	margin: 0;
	padding: 0;
}

.card {
	border: 1px solid var(--color-line);
	border-radius: var(--radius-lg);
	padding: var(--space-6);
	background: var(--color-surface);
	transition: box-shadow var(--transition);
}

.card:hover {
	box-shadow: var(--shadow-md);
}

.card h3 {
	margin-bottom: var(--space-2);
}

.card p {
	color: var(--color-ink-muted);
	font-size: var(--text-sm);
	margin: 0;
}

.card__meta {
	font-size: var(--text-xs);
	color: var(--color-ink-muted);
	text-transform: uppercase;
	letter-spacing: 0.06em;
	margin-bottom: var(--space-2);
}

.callout {
	border-left: 3px solid var(--color-brand);
	background: var(--color-brand-subtle);
	padding: var(--space-4) var(--space-6);
	border-radius: 0 var(--radius-md) var(--radius-md) 0;
	margin: var(--space-6) 0;
}

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

/* --- Prose (blog posts, docs) --------------------------------------------- */

.prose {
	max-width: var(--prose-width);
	margin-inline: auto;
	padding: var(--space-16) var(--space-6);
}

.prose__header {
	margin-bottom: var(--space-8);
	padding-bottom: var(--space-6);
	border-bottom: 1px solid var(--color-line);
}

.prose__eyebrow,
.prose__meta {
	font-size: var(--text-sm);
	color: var(--color-ink-muted);
	margin-bottom: var(--space-2);
}

.prose__lede {
	font-size: var(--text-lg);
	color: var(--color-ink-muted);
}

/* The post header image.

   NOT cropped to a fixed ratio, which was the first attempt and was wrong. All
   88 migrated covers are banner-shaped — measured 1.92 to 3.61, clustering at
   2.53 — and most carry the post title as baked-in artwork. Forcing them into a
   1200x630 box with `object-fit: cover` sliced the words off both ends. Their
   own ratio is the only one that shows what the designer drew, so the height
   follows the image. In practice that varies between about 175px and 335px at
   the prose width, which is a smaller spread than it sounds. */
.prose__hero {
	display: block;
	width: 100%;
	height: auto;
	border-radius: var(--radius-lg);
	margin-bottom: var(--space-8);
	background: var(--color-surface-sunken);
}

/* The 20 posts that never had a featured image fall back to a shared photo,
   which is 3:2 rather than banner-shaped and would tower over every real cover.
   Cropping is safe here precisely because it is generic stock with no text in
   it — the reason the rule above exists does not apply. 5/2 matches the modal
   ratio of the real covers, so the top of a post looks the same either way. */
.prose__hero--fallback {
	aspect-ratio: 5 / 2;
	object-fit: cover;
}

.prose h2 {
	margin-top: var(--space-12);
}

/* Migrated WordPress posts embed YouTube and Vimeo with the width the editor
   happened to be, usually 1080 — wider than the prose column, so the whole page
   scrolls sideways. `img, video` above already handle this, because a browser
   derives an intrinsic ratio from their width/height attributes and `height:
   auto` then keeps it. An iframe gets no such ratio, so shrinking it alone would
   letterbox the player; the ratio has to be stated.

   Only two iframe sizes exist across all 108 posts, so this is exhaustive rather
   than a guess: 1080x608 (16:9, 15 embeds) and 1080x810 (4:3, 4 embeds). */
.prose iframe {
	max-width: 100%;
	height: auto;
	aspect-ratio: 16 / 9;
}

.prose iframe[width="1080"][height="810"] {
	aspect-ratio: 4 / 3;
}

/* A table is the one block that cannot be made narrower without destroying it:
   a 4-column conference listing needs its width, and wrapping every cell to fit
   375px would be unreadable. So it scrolls sideways within its own box, the way
   `pre` already does, rather than widening the page and taking the prose with
   it. Measured before this rule: the climate-tech table pushed the document
   309px past the viewport at 375, and 20px past the prose column at 1280.

   `display: block` is what enables overflow on a table element. Cells still lay
   out under table rules; only the outer box changes. */
.prose table {
	display: block;
	max-width: 100%;
	overflow-x: auto;
}

.prose ul,
.prose ol {
	padding-left: var(--space-6);
	margin-bottom: var(--space-4);
}

.prose li {
	margin-bottom: var(--space-2);
}

.prose__footer {
	margin-top: var(--space-16);
	padding-top: var(--space-6);
	border-top: 1px solid var(--color-line);
	font-size: var(--text-sm);
}

.post-list {
	list-style: none;
	margin: 0;
	padding: 0;
}

.post-list li {
	padding: var(--space-6) 0;
	border-bottom: 1px solid var(--color-line);
}

.post-list h2 {
	font-size: var(--text-xl);
	margin-bottom: var(--space-2);
}

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

.post-list p {
	color: var(--color-ink-muted);
	margin: 0;
}

/* --- Pagination ----------------------------------------------------------- */
/* Three slots on one row: newer, position, older. The two ends keep their space
   even when there is nowhere to go — on page 1 and the last page the step is a
   dimmed span rather than a link — so the counter stays centred instead of
   jumping sideways as a reader moves through the archive. */

.pagination {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-4);
	margin-top: var(--space-8);
	padding-top: var(--space-6);
	border-top: 1px solid var(--color-line);
}

.pagination__step {
	font-size: var(--text-sm);
	text-decoration: none;
	white-space: nowrap;
}

.pagination__step--off {
	color: var(--color-ink-muted);
	opacity: 0.4;
}

.pagination__count {
	font-size: var(--text-sm);
	color: var(--color-ink-muted);
	margin: 0;
	text-align: center;
}

/* The two labels are the widest things here and would wrap into the counter
   before the viewport got narrow. Stacking keeps all three readable. */
@media (max-width: 30rem) {
	.pagination {
		flex-direction: column;
		align-items: center;
		gap: var(--space-2);
	}
}

/* --- Site footer ---------------------------------------------------------- */
/* Cream, on every page. Fixed brand colours only: it must not invert with the
   OS colour scheme, because the home page places it under a black section. */

.site-footer {
	position: relative;
	margin-top: var(--space-24);
	padding: 56px var(--page-gutter) 40px;
	background: var(--color-cream);
	color: var(--color-carbon);
}

.site-footer__top {
	max-width: var(--content-max);
	margin: 0 auto;
	display: flex;
	justify-content: space-between;
	align-items: flex-start;
}

.site-footer__cols {
	display: flex;
	gap: 56px;
}

.site-footer__col {
	display: flex;
	flex-direction: column;
	gap: var(--space-3);
}

.site-footer__col h2 {
	margin: 0 0 var(--space-1);
	font-family: var(--font-label);
	font-weight: 700;
	font-size: var(--text-sm);
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--color-carbon);
}

.site-footer__col a {
	font-family: var(--font-sans);
	font-weight: 400;
	font-size: var(--text-md);
	color: var(--color-carbon-soft);
	text-decoration: none;
}

.site-footer__col a:hover {
	color: var(--color-brand);
}

.site-footer__home {
	display: inline-flex;
	flex: none;
}

.site-footer__logo {
	width: 84px;
	height: auto;
}

/* Default (cream footer): dark mark shows, cream one hidden. The dark theme
   flips these — see "Footer: dark theme" below. */
.site-footer__logo--light {
	display: none;
}

.site-footer__divider {
	max-width: var(--content-max);
	margin: 40px auto 26px;
	border: none;
	border-top: 1px solid var(--color-rule-on-cream);
}

.site-footer__bottom {
	max-width: var(--content-max);
	margin: 0 auto;
	display: flex;
	align-items: center;
	justify-content: flex-end;
	gap: 40px;
}

.site-footer__locations {
	display: flex;
	gap: 60px;
}

.site-footer__loc {
	display: flex;
	flex-direction: column;
	gap: var(--space-1);
}

.site-footer__city {
	font-family: var(--font-label);
	font-weight: 700;
	font-size: var(--text-sm);
	letter-spacing: 0.08em;
	text-transform: uppercase;
}

.site-footer__addr {
	font-family: var(--font-sans);
	font-size: var(--text-sm);
	color: var(--color-carbon-soft);
}

/* The icons are painted through a mask of their own file, not shown as the
   <img>: the files are not drawn in one colour (LinkedIn and GitHub are white,
   invisible on cream), and a mask paints every one in the theme's colour. */
.site-footer__social {
	display: flex;
	gap: 22px;
}

.site-footer__social a {
	width: 22px;
	height: 22px;
	background-color: var(--footer-icon-color, var(--color-black));
	-webkit-mask: var(--icon) center / 22px 22px no-repeat;
	mask: var(--icon) center / 22px 22px no-repeat;
}

.site-footer__social a:hover {
	background-color: var(--color-brand);
}

.site-footer__social img {
	display: none;
}

/* --- Footer: dark theme --------------------------------------------------- */
/* `footerTheme: dark` in a page's front matter (Labs, Privacy). Colours and the
   mark only: each dark frame lays the footer out differently (Labs drops the
   divider and moves the offices; Privacy keeps the divider), so layout stays in
   that page's CSS. This used to be copied into both pages. */
.site-footer--dark {
	background: var(--color-black);
	color: var(--color-cream);
}

.site-footer--dark .site-footer__col h2 {
	color: var(--color-cream);
}

.site-footer--dark .site-footer__col a,
.site-footer--dark .site-footer__addr {
	color: var(--color-cream-dimmer);
}

.site-footer--dark .site-footer__col a:hover {
	color: var(--color-brand);
}

.site-footer--dark .site-footer__divider {
	border-top-color: var(--color-sand);
}

.site-footer--dark .site-footer__logo--dark {
	display: none;
}

.site-footer--dark .site-footer__logo--light {
	display: block;
}

/* Brand orange on black; hover goes cream. */
.site-footer--dark {
	--footer-icon-color: var(--color-brand);
}

.site-footer--dark .site-footer__social a:hover {
	background-color: var(--color-cream);
}

@media (max-width: 1280px) {
	.site-footer {
		padding: 48px var(--space-8) 36px;
	}

	.site-footer__cols {
		flex-wrap: wrap;
		gap: 36px;
	}

}

@media (max-width: 767px) {
	/* Mobile inverts the arrangement: a black band of orange social icons sits
	   above the cream footer, per the mobile frame (28:4856). Content insets 37px.
	   No top margin: the 70px above the icons is the band's own padding, and a
	   margin here would stack on top of it. */
	.site-footer {
		display: flex;
		flex-direction: column;
		margin-top: 0;
		padding: 0 37px var(--space-2);
	}

	.site-footer__social {
		order: -1;
		position: static;
		align-items: center;
		justify-content: center;
		gap: 14px; /* Figma node 1421:566 */
		margin: 0 -37px;
		/* 70px above and below the icons. The band owns that space, so it stays
		   black whatever the page above it ends on. */
		padding: 70px 37px;
		background-color: var(--color-black);
		background-image: url(../media/home/grid-tile.svg);
		background-size: var(--grid-tile) var(--grid-tile);
	}

	/* Masked rather than filtered: a filter could only approximate the brand
	   orange, and the frame specifies it exactly. --icon is set per link in
	   _partials/footer.html, so this stays one rule however many links there are. */
	.site-footer__social a {
		width: 27px;
		height: 27px;
		background-color: var(--color-brand);
		-webkit-mask-image: var(--icon);
		mask-image: var(--icon);
		-webkit-mask-repeat: no-repeat;
		mask-repeat: no-repeat;
		-webkit-mask-position: center;
		mask-position: center;
		-webkit-mask-size: 27px 27px;
		mask-size: 27px 27px;
	}

	.site-footer__social img {
		display: none;
	}

	/* Left-align at the gutter (the columns are a single stacked block, not the
	   centred content band the desktop uses); 47px of cream above SERVICES (frame). */
	.site-footer__top {
		max-width: none;
		margin-inline: 0;
		padding-top: 47px;
		align-items: flex-start;
	}

	.site-footer__cols {
		flex-direction: column;
		gap: 17px; /* between column groups (frame) */
	}

	.site-footer__col {
		gap: 4px; /* heading→links and link→link (frame) */
	}

	.site-footer__col h2 {
		font-size: var(--text-md); /* 15 — Label/M */
		letter-spacing: 0.15em;
		margin-bottom: 0;
	}

	.site-footer__col a {
		font-size: var(--text-sm); /* 14 — Body/S */
	}

	/* The link, not the image, is the flex item of .site-footer__top now. */
	.site-footer__home {
		align-self: flex-end;
		margin: auto 0 0;
	}

	.site-footer__logo {
		width: 67px;
	}

	.site-footer__divider {
		margin: 18px 0 var(--space-4);
	}

	/* The row dissolves so the icons are a direct child of the footer again,
	   which is what lets `order: -1` lift them into the band above. */
	.site-footer__bottom {
		display: contents;
	}

	.site-footer__locations {
		margin: 0;
		gap: 31px;
		flex-wrap: wrap;
	}

	.site-footer__loc {
		min-width: 132px;
	}

	/* Mobile frame puts Boston first. */
	.site-footer__loc:first-child {
		order: 2;
	}

	.site-footer__city {
		font-size: var(--text-md); /* 15 — Label/M */
		letter-spacing: 0.15em;
	}

	.site-footer__addr {
		font-size: var(--text-sm); /* 14 — Body/S */
	}

}
