/* Vedmi theme layer.
   Ported from the former SvelteKit source (src/lib/styles) in the same cascade
   order: tokens -> base -> components -> sections -> responsive.
   Loads after main.css (the untouched geometry baseline) and overrides it. */

/* ------------------------------------------------------------------ tokens */

:root {
	color-scheme: light;
	background: #e0e1e5;

	--font-sans: 'Geist', 'PT Sans', sans-serif;
	--color-bg: #e0e1e5;
	--color-accent: #0e3eb1;
	--color-accent-hover: #0b328f;
	--color-nav: #134a92;
	--color-highlight: #ddf2a4;
	--color-text: #121212;
	--color-on-accent: #fff;
	--color-button-border: rgb(14 62 177 / 55%);
	/* Recolours a flat icon to --color-accent; the leading brightness(0) flattens
	   whatever the source fill was, so it works on any single-colour SVG. */
	--filter-accent: brightness(0) saturate(100%) invert(3.8%) sepia(100%) saturate(5056%) hue-rotate(225deg)
		brightness(260%) contrast(89%);
	--motion-fast: 400ms;
	--motion-media: 1200ms;
	--ease-standard: cubic-bezier(0.22, 1, 0.36, 1);
}

/* -------------------------------------------------------------------- base */

html {
	background: var(--color-bg);
	scroll-behavior: auto;
}

body {
	font-family: var(--font-sans);
	color: var(--color-text);
	background: var(--color-bg);
}

/* One selection colour for the whole site, the lime the buttons and the address
   are painted in. Both halves are named: the ink is set as well as the field,
   so white copy over the blue contact card stays legible once it is picked out
   rather than dropping to white on lime. */
::selection {
	color: var(--color-text);
	background: var(--color-highlight);
}

main {
	display: block;
	width: 100%;
	max-width: 1440px;
	margin-inline: auto;
	container-type: inline-size;
}

.header,
.footer {
	width: 100%;
	max-width: 1440px;
	margin-inline: auto;
	container-type: inline-size;
}

button,
input {
	font: inherit;
}

[data-text-split] .char,
[data-text-split] .word {
	display: inline-block;
}

[data-text-split] .word {
	overflow: hidden;
	vertical-align: top;
}

.first-page__title .char,
.first-page__title .word {
	display: inline-block;
}

.first-page__title .word {
	overflow: hidden;
	line-height: 1.08;
}

.section__title .word,
.section__pretitle .word {
	padding-bottom: 0;
	margin-bottom: 0;
}

@media (width > 1440px) {
	.footer {
		padding-top: 112.5px;
		padding-bottom: 75px;
	}

}

/* -------------------------------------------------------------- components */

.brand-lockup {
	display: inline-flex;
	align-items: center;
}

.brand-logo {
	display: block;
	height: auto;
}

.header__logo.brand-lockup {
	width: auto;
}

.header__logo .brand-logo--header {
	width: 8.5cqw;
	max-width: 10.5rem;
}

/* The bar closes on nothing: no rule, and the tint runs out before the box
   ends, so there is no edge left to read as a line. */
.header-inner {
	position: relative;
	border: 0;
	background: linear-gradient(
		to bottom,
		rgb(224 225 229 / 34%) 0%,
		rgb(224 225 229 / 27%) 58%,
		rgb(224 225 229 / 0%) 100%
	);
	backdrop-filter: none;
	-webkit-backdrop-filter: none;
}

.header-inner > .container {
	position: relative;
	z-index: 1;
}

/* Progressive blur. Three backdrop layers over one box that hangs below the
   bar by 46% of its height; each layer blurs what the one beneath it made, so
   the radii add up towards the top and unwind to nothing inside the tail.
   Saturation rides the base layer only - stacking it three deep would bleach
   the page. */
.header-glass,
.header-glass::before,
.header-glass::after {
	position: absolute;
	inset: 0;
	pointer-events: none;
}

.header-glass {
	bottom: -46%;
	backdrop-filter: blur(0.6cqw) saturate(1.7);
	-webkit-backdrop-filter: blur(0.6cqw) saturate(1.7);
	-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 62%, transparent 100%);
	mask-image: linear-gradient(to bottom, #000 0%, #000 62%, transparent 100%);
}

.header-glass::before {
	content: '';
	backdrop-filter: blur(0.85cqw);
	-webkit-backdrop-filter: blur(0.85cqw);
	-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 34%, transparent 74%);
	mask-image: linear-gradient(to bottom, #000 0%, #000 34%, transparent 74%);
}

.header-glass::after {
	content: '';
	backdrop-filter: blur(1.25cqw);
	-webkit-backdrop-filter: blur(1.25cqw);
	-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 12%, transparent 46%);
	mask-image: linear-gradient(to bottom, #000 0%, #000 12%, transparent 46%);
}

/* Hiding on scroll: the bar clears its own height plus the tail, so no smear
   of glass is left hanging at the top edge. Percentages rather than cqw -
   .header is its own query container, and a cq unit read off the container it
   defines resolves to zero, which is why the bar used to stay put. */
.header.header-out {
	transform: translateY(-146%) !important;
}

/* A pointer at the top edge outranks the hide: the nav comes back on a flick
   towards it, no scrolling up. Source order puts this after .header-out, so
   at equal specificity the peek wins. */
.header.header-peek {
	transform: none !important;
}

.header-list,
.header__contacts,
.first-page__title,
.contacts__title,
.footer-list,
.footer__policy,
.section__title,
.careers__name,
.careers__text,
.careers__link {
	color: var(--color-text);
}

.section__pretitle,
.footer-list__name {
	color: var(--color-accent) !important;
}

.footer-list__name {
	opacity: 0.8 !important;
}

.section__text,
.mission .section__text {
	color: var(--color-text) !important;
	opacity: 1;
}

/* The burger and close controls are real <button> elements in the static
   rewrite; strip the UA button chrome so the original anchor styling holds. */
.header__burger {
	padding: 0;
	border: 0;
	background: none;
	cursor: pointer;
}

/* Both marks ship flat in the body ink. Everything else in the bar is drawn in
   the accent, so they are recoloured to match rather than reissued as assets. */
.header__burger img {
	filter: var(--filter-accent);
}

/* The two primary controls are one thing in two places: glass, a tint laid
   over a blurred backdrop, with the light of the pane doing the work a
   hairline would. Nothing on either is opaque. The hero pours white frost
   over the artwork; the bar sets the same pane in the palette's one green,
   the lime the address and the selection already answer in — so the pair
   reads as two cuts of one material, not two buttons that happen to agree. */
/* Both controls answer the pointer in material rather than colour: the pane
   under the hand goes denser — more blur, more of its own tint — and a
   streak of light crosses it. The label never changes, because nothing is
   being covered: it is the same glass, thicker. Two tints each, resting and
   dense; the blur step lives in the hover rules, since the two panes season
   their backdrops differently. */
/* Light instead of a line. A hairline states an edge; glass shows one, so the
   edge is built out of what light does to a pane sitting on something: a
   bright rim along the top where it lands, a wash pooling under that rim —
   the thickness of the pane — a thread of it down either side, a thin shade
   along the foot, and a soft cast underneath that lifts the whole thing off
   the page. The bloom is appended to this on hover rather than replacing it,
   so the edge survives the wipe.

   Each pane carries the set in its own material. The hero's light is
   neutral — white rims, a grey cast to hold it off the film. The pill's
   foot shade and cast run olive rather than grey, because the shadow under
   coloured glass is the colour the glass keeps out of it. */
.first-page__btn {
	--btn-light:
		inset 0 1px 0 rgb(255 255 255 / 72%),
		inset 0 12px 22px -12px rgb(255 255 255 / 34%),
		inset 1px 0 0 rgb(255 255 255 / 20%),
		inset -1px 0 0 rgb(255 255 255 / 20%),
		inset 0 -1px 0 rgb(18 18 18 / 14%),
		0 1px 2px rgb(18 18 18 / 10%),
		0 10px 24px -10px rgb(18 18 18 / 28%);
}

.header .header__contacts {
	--btn-light:
		inset 0 1px 0 rgb(255 255 255 / 78%),
		inset 0 12px 22px -12px rgb(255 255 255 / 46%),
		inset 1px 0 0 rgb(255 255 255 / 30%),
		inset -1px 0 0 rgb(255 255 255 / 30%),
		inset 0 -1px 0 rgb(94 122 32 / 26%),
		0 1px 2px rgb(46 60 16 / 12%),
		0 10px 22px -10px rgb(124 160 42 / 38%);
}

/* The hero keeps its white frost — over the artwork the pane needs no colour
   of its own. Under the pointer the frost thickens from 20% to 30%, which
   reads as the pane condensing rather than a state being painted over it.
   Its bloom is lime, the highlight the site answers in everywhere else —
   the house blue under this pane would read as a different lamp being
   switched on. */
.first-page__btn {
	--btn-glass: rgb(255 255 255 / 20%);
	--btn-glass-dense: rgb(255 255 255 / 30%);
	--halation: 0 16px 38px -14px rgb(221 242 164 / 60%), 0 4px 12px -6px rgb(221 242 164 / 32%);
	--halation-press: 0 6px 16px -10px rgb(221 242 164 / 50%);
}

/* The pill is the same pane cut in green: lime at 62% over the bar, pale
   enough to carry the body ink at 15.0:1 and still read as colour. It
   densifies to 74% — still glass, the backdrop keeps moving under it, the
   ink still clears 15:1 — and its bloom is cut from the same olive as its
   cast: shadow and light of one material. */
.header .header__contacts {
	--btn-glass: color-mix(in srgb, var(--color-highlight) 62%, transparent);
	--btn-glass-dense: color-mix(in srgb, var(--color-highlight) 74%, transparent);
	--halation: 0 14px 34px -14px rgb(124 160 42 / 50%), 0 3px 10px -6px rgb(124 160 42 / 28%);
	--halation-press: 0 6px 16px -10px rgb(124 160 42 / 45%);
}

/* The streak needs a pane to stay inside and the bloom needs a box to hang
   off, so each control clips and isolates itself. Stated here rather than
   on a script-added class: the whole response is CSS now, and it must not
   depend on one. */
.first-page__btn,
.header .header__contacts {
	position: relative;
	overflow: hidden;
	isolation: isolate;
}

.first-page__btn {
	width: 14.8611cqw;
	min-width: 0;
	height: 40px;
	padding: 0 1.3889cqw;
	border: 0;
	border-radius: 2px;
	color: var(--color-on-accent) !important;
	background: var(--btn-glass);
	box-shadow: var(--btn-light);
	font-size: 0.9722cqw;
	font-weight: 600;
	line-height: 1.1;
	text-transform: uppercase;
	/* The frost does more than blur: it gathers the artwork's colour and a
	   little of its light into the pane, the way real frosted glass goes the
	   colour of whatever stands behind it. */
	backdrop-filter: blur(0.7813cqw) saturate(1.5) brightness(1.06);
	-webkit-backdrop-filter: blur(0.7813cqw) saturate(1.5) brightness(1.06);
}

/* Flex centring centres the line box, which hangs the caps a little high
   because the font's descent space rides along. Trimming the label down to the
   cap band centres what is actually visible, the way the comp draws it. */
.first-page__btn > span {
	text-box: trim-both cap alphabetic;
}

/* Social and e-mail links ship as pre-coloured chip icons, so the SVG tile is
   the button itself: no accent recolour over it, no plate behind it. */
.footer-soc__item img {
	filter: none;
}

.footer-soc .footer-soc__item img {
	width: 1.45833cqw;
	height: auto;
}

.contacts-soc__item {
	width: auto;
	height: auto;
	background: none;
	backdrop-filter: none;
}

.contacts-soc__item:hover {
	background: none;
	backdrop-filter: none;
}

.contacts-soc .contacts-soc__item img {
	width: 2.29167cqw;
	height: auto;
}

/* =========================================================================
   Header and footer links run one gesture at two sizes. The label ROLLS —
   the resting word leaves upward while its double arrives from below — and a
   hairline WIPES in left to right underneath it.

   The rule lives on the anchor, outside the clipped roll, and that is what
   lets the two coexist. The footer had to switch its underline off before
   because the roll's own overflow was shaving it away; the roll has moved
   into a span of its own, so the anchor is free to draw again.

   The incoming face lags the outgoing one by 40ms on the way in and snaps
   back level on the way out, so the pair reads as a departure board flap
   rather than one rigid slab, and it carries a faint bloom — the halation a
   lit character leaves on film.

   main.js builds both faces from the link's own text and marks the anchor
   .is-rolled. A link that never got its second face still works and still
   answers the pointer with colour.
   ========================================================================= */

.header-list li a,
.footer-list li a:not(.footer-soc__item),
.modal-menu .header__contacts,
.modal-menu__policy {
	/* The colour the link resolves to. The rule, the incoming face and the
	   face's bloom all read it, so each list states its resolution once and
	   the three can never drift apart. */
	--link-resolve: var(--color-nav);

	position: relative;
	display: inline-block;
}

/* Drawn in full rather than tweaked: this replaced an older underline that
   grew its width from zero and swapped its anchor edge mid-transition, so
   nothing of it is inherited here. */
.header-list li a::after,
.footer-list li a:not(.footer-soc__item)::after,
.modal-menu .header__contacts::after,
.modal-menu__policy::after {
	content: '';
	position: absolute;
	top: auto;
	right: 0;
	left: 0;
	width: auto;
	height: 1px;
	margin: 0;
	background: var(--link-resolve);
	opacity: 1;
	transform: scaleX(0);
	transform-origin: right center;
	transition: transform var(--motion-fast) var(--ease-standard);
}

/* In from the left, out to the right: the rule leaves the way it came rather
   than collapsing back on itself. */
.header-list li a:hover::after,
.header-list li a:focus-visible::after,
.footer-list li a:not(.footer-soc__item):hover::after,
.footer-list li a:not(.footer-soc__item):focus-visible::after,
.modal-menu .header__contacts:hover::after,
.modal-menu .header__contacts:focus-visible::after,
.modal-menu__policy:hover::after,
.modal-menu__policy:focus-visible::after {
	transform: scaleX(1);
	transform-origin: left center;
}

/* The roll clips to the face box, so the face has to hold the whole glyph
   box — at the lists' 1.15 leading the descenders in "Privacy Policy" and the
   address would be shaved off. Each list closes up by the leading it gains,
   so the column keeps the rhythm it had. */
.link-roll {
	position: relative;
	display: block;
	overflow: hidden;
}

.link-roll__face {
	display: block;
	line-height: 1.45;
	white-space: nowrap;
	transition: transform var(--motion-fast) var(--ease-standard);
}

.link-roll__face--in {
	position: absolute;
	inset: 0;
	color: var(--link-resolve);
	transform: translateY(106%);
}

.is-rolled:hover .link-roll__face,
.is-rolled:focus-visible .link-roll__face {
	transform: translateY(-106%);
}

.is-rolled:hover .link-roll__face--in,
.is-rolled:focus-visible .link-roll__face--in {
	transform: translateY(0);
	/* The lag is on the way in only — leaving, the pair snaps back level. */
	transition-delay: 40ms;
}

/* A link that never got its second face — JavaScript unavailable, or a label
   that came back empty — still has to answer the pointer. */
.header-list li a:not(.is-rolled):is(:hover, :focus-visible),
.footer-list li a:not(.footer-soc__item):not(.is-rolled):is(:hover, :focus-visible),
.modal-menu .header__contacts:not(.is-rolled):is(:hover, :focus-visible),
.modal-menu__policy:not(.is-rolled):is(:hover, :focus-visible) {
	color: var(--link-resolve);
}

/* ---- header: nav blue resolving to the deeper accent -------------------- */

/* main.css fades the whole anchor to 70% on hover, which dims both faces and
   the rule with them and reads as a fade rather than a flap. The roll carries
   the state instead. Its `all` transition narrows at the same time: the
   anchor only has colour left to animate, and `all` would double-ease the
   transforms the roll drives. */
.header-list li a {
	transition-property: color;
	transition-duration: var(--motion-fast);
	transition-timing-function: var(--ease-standard);
}

.header-list li a:is(:hover, :focus-visible) {
	opacity: 1;
}

/* 7px at 1440. The rule hangs clear of the label rather than underlining it,
   so it reads against the depth of the bar. */
.header-list li a::after {
	bottom: -0.4861cqw;
}

/* The link whose section is showing keeps its rule down, at 42% so it reads
   as a marker rather than a permanent hover. Nothing in the site sets
   aria-current yet — see design/build/links.md before wiring it. */
.header-list li a[aria-current]:not([aria-current='false'])::after {
	background: color-mix(in srgb, var(--link-resolve) 42%, transparent);
	transform: scaleX(1);
	transform-origin: left center;
}

/* ---- footer: body ink resolving to nav blue ----------------------------- */

.footer-list li a:not(.footer-soc__item) {
	color: var(--color-text);
}

/* A hairline one pixel under the face box, close enough to read as an
   underline. At 1px it stays 1px at every width, so it stays a hairline. */
.footer-list li a:not(.footer-soc__item)::after {
	bottom: -1px;
}

/* ---- social chips: the same halation the buttons bloom ------------------ */

/* The chips answered a hover with a bare step up in size. They keep a smaller
   step and gain the bloom, so the response is light rather than scale. The
   transition already covers transform and filter — the interaction section
   above sets it for both chip families. */
.footer-soc__item:hover,
.footer-soc__item:focus-visible,
.contacts-soc__item:hover,
.contacts-soc__item:focus-visible {
	transform: scale(1.06);
	filter: drop-shadow(0 0.3472cqw 0.9722cqw color-mix(in srgb, var(--color-accent) 42%, transparent));
}

/* On the accent field an accent bloom would disappear into the background,
   the way the accent focus ring does; the highlight carries it there. */
.contacts-soc__item:hover,
.contacts-soc__item:focus-visible {
	filter: drop-shadow(0 0.3472cqw 0.9722cqw color-mix(in srgb, var(--color-highlight) 42%, transparent));
}

/* ---- reduced motion ----------------------------------------------------- */

/* The roll depends on a clipped box, which a reduced-motion visitor never
   sees move — hold both faces still and let colour do the work. The resting
   face has to be pinned as well as the incoming one hidden: left free to
   travel it would leave the clip and the label would vanish on hover. */
@media (prefers-reduced-motion: reduce) {
	.link-roll__face--in {
		display: none;
	}

	.is-rolled:hover .link-roll__face,
	.is-rolled:focus-visible .link-roll__face {
		transform: none;
	}

	.header-list li a:is(:hover, :focus-visible),
	.footer-list li a:not(.footer-soc__item):is(:hover, :focus-visible) {
		color: var(--link-resolve);
	}

	/* The chips give up the step in size and keep the bloom, which is light
	   rather than movement. */
	.footer-soc__item:hover,
	.footer-soc__item:focus-visible,
	.contacts-soc__item:hover,
	.contacts-soc__item:focus-visible {
		transform: none;
	}
}

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

@media (width >= 768px) {
	/* The bar is nav blue and resolves to the deeper accent. The list's own
	   colour, size and case are set higher up in this file and stand. */
	.header-list li a {
		--link-resolve: var(--color-accent);
	}
}

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

@media (width <= 767px) {
	/* In the popover menu the list is body ink, so it resolves to nav blue
	   the way the footer columns do — the shared default already says so.

	   The face gained 0.45em of leading over the menu's 100% line-height, so
	   the list gives exactly that back: baseline to baseline the menu keeps
	   the rhythm it has now, and the leading fills the gap it used to hold
	   open with margin. */
	.modal-menu .header-list li {
		margin-bottom: 1.0133cqw;
	}

	/* At 36px the longest label sits within a hair of the column width, so it
	   is left free to break. The two faces wrap identically — the incoming one
	   is inset into the same box — and the pair still rolls in step. */
	.modal-menu .header-list .link-roll__face {
		white-space: normal;
	}

	/* Under the label rather than clear of it: at this size the bar's 7px
	   stand-off would land in the next item's space, so the rule tucks into
	   the leading the face already carries. 4px at 375. */
	.header-list li a::after {
		bottom: 1.0667cqw;
	}

	/* The same 5px and 14px the bloom uses on the desktop rail. */
	.footer-soc__item:hover,
	.footer-soc__item:focus-visible {
		filter: drop-shadow(0 1.3333cqw 3.7333cqw color-mix(in srgb, var(--color-accent) 42%, transparent));
	}

	.contacts-soc__item:hover,
	.contacts-soc__item:focus-visible {
		filter: drop-shadow(0 1.3333cqw 3.7333cqw color-mix(in srgb, var(--color-highlight) 42%, transparent));
	}
}

/* Replaces the card's comment, .contacts-bg and .contacts-bg__panel; inserts
   one rule after .contacts__eyebrow; replaces .contacts-mail__text. Exact
   ranges in contacts.md — the sheet is moving under several ports at once. */

/* The contact card is one exposure rather than two halves. The accent field
   runs past centre and dissolves into the photograph over its last third, the
   photograph is graded to meet it, grain lies over the pair of them, and four
   register ticks say plainly what the card is — a frame off a roll. Nothing
   is set over the interference pattern even so: the copy column ends at 46.4%
   of the card (a 78.6979cqw container centred in a 92.7083cqw card, carrying
   a 36cqw column) and the field is still solid at 47.52%.

   Five layers stack inside the scrubbed element in tree order — photograph,
   vignette, field, grain, ticks — so not one of them needs a z-index to hold
   its place. The scrub in assets/js/main.js writes width, height and
   border-radius on .contacts-bg every frame, and nothing here declares those
   three: the box belongs to the scrub. Every layer is pinned to it with inset
   instead, so the whole stack follows the card for free, and .contacts-bg
   clips it to whatever corner radius the scrub is holding that frame. */

.contacts-bg {
	/* The ticks are marks on the frame, not measurements of it, so the arms
	   stay 24px and the inset 22px at both breakpoints; only the unit changes,
	   1cqw being 14.4px at 1440 and 3.75px at 375. They are declared here
	   rather than in the breakpoint blocks because a viewport can land between
	   768 and 767 — fractional widths do exist — and an undefined length would
	   invalidate background-size and flood the card with eight full-bleed
	   rectangles. */
	--tick-mark: linear-gradient(#000, #000);
	--tick-arm: 1.6667cqw;
	--tick-inset: 1.5278cqw;

	/* The elbow is a fixed share of the corner the marks sit inside, so it
	   opens as the scrub rounds the card and is square while the card is
	   square. The share is the one that makes the mark concentric with the
	   frame at the desktop resting radius: a corner 22px in from a 32px curve
	   follows it at 10px, and 10/32 carries the same mark through every other
	   frame of the scrub and through the mobile card's smaller corner. */
	--tick-curve: 0.3125;

	overflow: hidden;

	/* The grade has to reach the photograph and nothing else, so the picture
	   moved off this element and onto ::before, where a filter can find it
	   alone. One !important settles the three declarations that used to race
	   for the element's own background — the inline one in index.html, the
	   mobile art in main.css, and the override that undid it — the inline
	   style included, so the card is right whether or not index.html has been
	   tidied yet. */
	background-image: none !important;
}

/* The photograph, cross-processed to meet the field: saturation pulled back so
   the accent stays the most saturated thing on the card, a little contrast so
   the blacks do not go flat under the grain, and a hair off the exposure. */
.contacts-bg::before {
	content: '';
	position: absolute;
	inset: 0;
	background-image: url('../img/contacts-vedmi.png?v=575d8e6584');
	background-position: center;
	background-size: cover;
	filter: saturate(0.8) contrast(1.06) brightness(0.98);
}

/* Lomo: the corner furthest from the copy falls into a cool shadow, and the
   accent bleeds a short way into the emulsion from the side the field is on.
   It lies under the field rather than over it — the radial darkens everything
   far from its centre, and over the field that would be the copy's ground. */
.contacts-bg__lomo {
	position: absolute;
	inset: 0;
	background:
		radial-gradient(92% 104% at 72% 48%, rgb(12 30 66 / 0) 38%, rgb(12 30 66 / 44%) 100%),
		linear-gradient(to right, rgb(14 62 177 / 22%), rgb(14 62 177 / 0) 54%);
	pointer-events: none;
}

.contacts-bg__panel {
	position: absolute;
	background: var(--color-accent);
}

/* Grain over the whole card, the field included: the card is one frame, and a
   solid field that stayed clean would read as a rectangle pasted onto a
   photograph. Soft-light keeps the texture in the midtones instead of fogging
   the blacks, and it reaches only the card — .contacts-bg is a stacking
   context twice over (z-index 0 and a transform, both from main.css), so the
   blend cannot see the fixed film field behind the page. The drift is the
   standard cheat: one 180px tile jumped between five positions on whole
   frames, never tweened, so it reads as flicker rather than movement. The
   -14% inset gives it room to move without walking an edge into view.

   The card is heavier than the page field on purpose — the field carries the
   room, this carries a photograph — so the opacity is set here rather than
   taken from --grain-alpha. */
.contacts-bg__grain {
	position: absolute;
	inset: -14%;
	background-image: var(--grain-url);
	opacity: 0.42;
	mix-blend-mode: soft-light;
	animation: contacts-grain 6s steps(1) infinite;
	pointer-events: none;
}

@keyframes contacts-grain {
	0%,
	100% {
		transform: translate3d(0, 0, 0);
	}

	20% {
		transform: translate3d(-8%, 2%, 0);
	}

	40% {
		transform: translate3d(-3%, 6%, 0);
	}

	60% {
		transform: translate3d(6%, 0, 0);
	}

	80% {
		transform: translate3d(2%, 6%, 0);
	}
}

/* Four register ticks, 2px arms 24px long. They were eight straight gradients
   in one background, which is the cheapest way to draw a square corner and no
   way at all to draw a curved one — a background arm cannot bend. So the marks
   are one box instead: the whole rectangle 22px inside the card, stroked 1px,
   and masked down to a square at each corner. What survives the mask is the
   corner of that stroke, arms and elbow together, and the elbow is a real
   border radius that can be animated.

   It follows the card's own. assets/js/main.js hands the scrub's radius out as
   --card-radius, so while the card is a full-bleed rectangle with square
   corners the marks are square too, and they open into their curve on the same
   frames the card opens into its own — the mark rounding as the frame rounds
   rather than arriving already round on a corner that is still sharp. Without
   the script there is no radius on either, and the fallback keeps them square.

   They belong on ::after because that is the only layer above the field: the
   marks are a property of the frame, and two of the four corners sit on solid
   accent. */
.contacts-bg::after {
	content: '';
	position: absolute;
	inset: var(--tick-inset);
	border: 2px solid rgb(255 255 255 / 72%);
	border-radius: calc(var(--card-radius, 0px) * var(--tick-curve));
	mask-image: var(--tick-mark), var(--tick-mark), var(--tick-mark), var(--tick-mark);
	mask-position: left top, right top, left bottom, right bottom;
	mask-size: var(--tick-arm) var(--tick-arm);
	mask-repeat: no-repeat;
	pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
	/* The blanket rule at the foot of this sheet collapses the duration, which
	   makes the drift instant rather than absent; this stops it outright and
	   pins the tile at its resting offset, so the grain cannot be left frozen
	   wherever the last frame happened to put it. */
	.contacts-bg__grain {
		transform: none;
		animation: none;
	}
}

.contacts__lead {
	opacity: 0.72;
}

.contacts-mail__link {
	display: inline-flex;
	align-items: center;
	color: var(--color-highlight);
}

/* main.css resets every a:hover to inherit, and a type-plus-pseudo outranks the
   link's own class — so the address dropped to the body ink the moment it was
   pointed at. The lime is the whole of it; it is spelled out for both states. */
.contacts-mail__link:hover,
.contacts-mail__link:focus-visible {
	color: var(--color-highlight);
}

/* The address anchors the rule that wipes under it. */
.contacts-mail__text {
	position: relative;
}

/* The rule under the address wipes in from the left and back out to the
   right, the same gesture the nav links use. */
.contacts-mail__text::after {
	content: '';
	position: absolute;
	right: 0;
	bottom: -0.14em;
	left: 0;
	height: 0.05em;
	background: currentcolor;
	transform: scaleX(0);
	transform-origin: right center;
	transition: transform var(--motion-fast) var(--ease-standard);
}

.contacts-mail__link:hover .contacts-mail__text::after,
.contacts-mail__link:focus-visible .contacts-mail__text::after {
	transform: scaleX(1);
	transform-origin: left center;
}

.contacts-mail__arrow {
	flex: 0 0 auto;
	transition: transform var(--motion-fast) var(--ease-standard);
}

.contacts-mail__link:hover .contacts-mail__arrow {
	transform: translate3d(0.12em, -0.12em, 0);
}

@media (width >= 768px) {
	.header-inner {
		padding: 1.4583cqw 0;
	}

	.header .container {
		width: 90.6944cqw;
		max-width: none;
		margin-right: 0;
		margin-left: 4.6528cqw;
	}

	.header .header-row {
		position: relative;
		justify-content: flex-start;
	}

	.header .header__logo {
		flex: 0 0 auto;
	}

	.header .header-list {
		position: absolute;
		top: 50%;
		left: 50%;
		transform: translate(-50%, -50%);
	}

	.header-list {
		gap: 4.5833cqw;
		color: var(--color-nav);
		font-family: var(--font-sans);
		font-size: 1.0417cqw;
		font-weight: 500;
		line-height: 1.15;
		text-transform: uppercase;
	}

	.header-list li {
		margin-right: 0;
	}

	.header .header__contacts {
		position: absolute;
		top: -5px;
		right: 0;
		gap: 0.6944cqw;
		height: 40px;
		margin-left: 0;
		padding: 0 1.3889cqw;
		border: 0;
		border-radius: 2px;
		color: var(--color-text);
		background: var(--btn-glass);
		box-shadow: var(--btn-light);
		font-size: 0.9722cqw;
		font-weight: 600;
		line-height: 1.1;
		text-transform: uppercase;
		/* Saturate rides along so whatever scrolls under the bar blooms
		   through the lime instead of greying it. No transition list of its
		   own any more: the narrower one that lived here outranked the shared
		   manners in the interaction section and left the bloom and the press
		   snapping in rather than easing. */
		backdrop-filter: blur(0.7813cqw) saturate(1.6);
		-webkit-backdrop-filter: blur(0.7813cqw) saturate(1.6);
	}

	.header .header__contacts > span {
		text-box: trim-both cap alphabetic;
	}

}

/* ------------------------------------------------------------- interaction */

/* One set of manners for every control on the page: a press that settles the
   thing under the finger, a keyboard ring that does not depend on the hover
   skin, and arrows that travel the way they point. Colour and fill stay with
   each control's own rules — this layer only adds the response. */

.first-page__btn,
.header__contacts,
.contacts-mail__link,
.careers__link,
.footer__email,
.header__burger,
.contacts-soc__item,
.footer-soc__item,
.footer-list li a:not(.footer-soc__item) {
	transition-property: color, background-color, border-color, box-shadow, transform, filter, opacity, backdrop-filter;
	transition-duration: var(--motion-fast);
	transition-timing-function: var(--ease-standard);
}

.first-page__btn:active,
.header__contacts:active,
.footer__email:active {
	transform: translateY(1px);
	transition-duration: 80ms;
}

.header__burger:active,
.contacts-soc__item:active,
.footer-soc__item:active {
	transform: scale(0.94);
	transition-duration: 80ms;
}

/* Every arrow on the page points up and to the right, so it leaves that way. */
.careers__link:hover img,
.contacts-mail__link:hover .contacts-mail__arrow {
	transform: translate3d(0.14em, -0.14em, 0);
}

.careers__link img {
	transition: transform var(--motion-fast) var(--ease-standard);
}

/* main.css fades the whole row to 60% on hover, which dims the arrow with it
   and reads as disabled rather than live. Colour carries the state instead. */
.careers__link:hover {
	opacity: 1;
	color: var(--color-nav);
}

.careers__link:hover .careers__mob {
	transform: translate3d(0.14em, -0.14em, 0);
}

:where(a, button):focus-visible {
	outline: 2px solid var(--color-accent);
	outline-offset: 3px;
}

/* On the accent field the accent ring would disappear into the background. */
.contacts-mail__link:focus-visible,
.contacts-soc__item:focus-visible {
	outline-color: var(--color-highlight);
}

@media (prefers-reduced-motion: reduce) {
	.first-page__btn:active,
	.header__contacts:active,
	.footer__email:active,
	.header__burger:active,
	.contacts-soc__item:active,
	.footer-soc__item:active,
	.careers__link:hover img,
	.careers__link:hover .careers__mob,
	.contacts-mail__link:hover .contacts-mail__arrow {
		transform: none;
	}
}

/* ---------------------------------------------------------------- sections */

.first-page {
	position: relative;
	isolation: isolate;
}

/* The heading keeps its own colour and sits just a little open, so the clip
   behind it reads through the letters. Frosted glass on the glyphs
   themselves is not reachable: Chrome clips backdrop-filter to the element
   box, never to the letterforms, and background-clip: text does not change
   that. */
.first-page__title {
	color: color-mix(in srgb, var(--color-accent) 80%, transparent);
}

/* The reveal rides the span, not the heading: `[data-aos].aos-animate` sets
   `transform: translateZ(0)`, which would wipe the translateX(-50%) the
   heading is centred with. */
.first-page__title > span {
	display: block;
}

/* A shallow fall from light to deep down the line, for weight. It rides the
   span rather than the heading because the span is what the reveal moves:
   a gradient on the heading would stay put while the letters travelled
   across it. The flat colour above is what a browser without a text
   background-clip keeps. */
@supports (background-clip: text) or (-webkit-background-clip: text) {
	.first-page__title > span {
		color: transparent;
		background-image: linear-gradient(
			180deg,
			color-mix(in srgb, var(--color-accent) 66%, transparent) 0%,
			color-mix(in srgb, var(--color-accent) 82%, transparent) 52%,
			color-mix(in srgb, var(--color-accent-hover) 90%, transparent) 100%
		);
		-webkit-background-clip: text;
		background-clip: text;
	}
}

.media-reveal[data-aos] {
	--media-tone: brightness(1);

	filter: var(--media-tone) blur(28px);
	transform: translate3d(0, 5.2083cqw, 0) scale(1.02);
	transition-property: opacity, transform, filter;
	transition-duration: var(--motion-media);
	transition-timing-function: var(--ease-standard);
	will-change: opacity, transform, filter;
}

.media-reveal--hero[data-aos] {
	--media-tone: brightness(0.993);

	/* The hero opens standing in the middle of the viewport, sized against
	   its height, and gives both back over the first third of a screen of
	   scroll; main.js scrubs --hero-scale and --hero-drop between the open
	   and resting states.

	   How tall it opens stays here as the design value: the art's height as
	   a share of the viewport's. The scale that produces it is geometry —
	   share times viewport height over the resting frame — and CSS cannot
	   divide two lengths, so the script derives the number; what it means
	   lives in this file. The centring distance is likewise measured, not
	   designed: where the resting frame happens to sit against the
	   viewport's middle is nobody's tuning knob.

	   It rides the independent `scale` property rather than being folded into
	   the `transform` above, for two reasons. The reveal owns that transform
	   and transitions it, so a scrubbed value inside it would trail the scroll
	   by the whole transition duration. And the other obvious place to put it
	   — a transform on .first-page__bg — would open a stacking context on the
	   wrapper, which cuts the video's `darken` off from the page behind it and
	   leaves the artwork's white plate sitting on the field.

	   1.08 is about as far as this goes: the crown sits about six percent
	   into the frame, so past roughly 1.13 a centred head puts it through
	   the top of the viewport and gets it cut square, with no vignette fade
	   left there to hide the edge. */
	--hero-open-share: 1.08;

	/* `translate` sits outside `scale` in the transform chain, so the ride
	   between the resting frame and the viewport's centre is flat page
	   pixels and not a distance multiplied by the zoom. Both variables come
	   from the scrub; without it — no JS, reduced motion, a script that
	   never ran — the defaults leave the art standing in its resting frame. */
	scale: var(--hero-scale, 1);
	translate: 0 var(--hero-drop, 0px);
	will-change: opacity, transform, translate, scale, filter;
}

.media-reveal[data-aos].aos-animate {
	filter: var(--media-tone) blur(0);
	transform: translateZ(0) scale(1);
}

.pricing__note {
	margin-inline: auto;
	text-wrap: balance;
}

.pricing .careers-table {
	max-width: 52.0833cqw;
	margin-top: 2.6042cqw;
}

.pricing .careers-col-1 {
	width: 10.9375cqw;
}

.pricing .careers-col-2 {
	width: 22.3958cqw;
	padding-right: 1.5625cqw;
}

.vision {
	background: var(--color-bg);
}

.mission-photo img,
.mission-photo video,
.vision-photo img,
.vision-photo video {
	mix-blend-mode: normal;
}

/* The mission slot holds the looping clip — or its poster, before playback
   starts. Both keep their own aspect ratio and sit centered in a slot that is
   wider than they are; inset plus auto margins center a replaced element
   without touching transform, which the scroll reveal owns. */
.mission-photo img,
.mission-photo video {
	position: absolute;
	inset: 0;
	margin: auto;
	width: 100%;
	height: auto;
	max-width: none;
}

/* The vision slot holds a clip too, sized exactly like the still it replaced —
   main.css gives that width to the image alone. */
.vision-photo video {
	width: 100cqw;
}

.contacts__title,
.contacts__lead {
	color: var(--color-on-accent);
}

.careers-item {
	border-color: rgb(18 18 18 / 16%);
}

.careers__name {
	opacity: 0.5;
}

.careers__link {
	color: var(--color-accent);
	white-space: nowrap;
}

.section__pretitle {
	color: var(--color-accent) !important;
	font-family: var(--font-sans);
	text-transform: lowercase;
	opacity: 1;
}

.section__title {
	color: var(--color-accent);
}

@media (width >= 768px) {
	.first-page {
		height: 64.0278cqw;
		padding: 0;
	}

	.first-page .container {
		position: relative;
		height: 100%;
		max-width: none;
	}

	.first-page__title {
		position: absolute;
		top: 28.6806cqw;
		left: 51.2729cqw;
		z-index: 2;
		max-width: none;
		margin: 0;
		transform: translateX(-50%);
		font-size: 7.4142cqw;
		font-weight: 600;
		line-height: 1.15;
		text-transform: uppercase;
		white-space: nowrap;
	}

	.first-page__title .word {
		line-height: inherit;
	}

	.first-page__bg {
		top: 1.0583cqw;
		left: 29.4417cqw;
		width: 43.7324cqw;
		height: 49.3115cqw;
		max-width: none;
		transform: none;

		/* No overflow clip here. The clip was redundant — object-fit: cover
		   crops inside the video's content box, so nothing ever spilled — and
		   on the composited video layer Chrome cut it at a fractional pixel,
		   leaving a hairline down the box edge that the mask could not reach.
		   The mask ends the art well inside the box on every side, so there
		   is nothing left to clip. */
	}

	.first-page .btn-wrapper {
		position: absolute;
		top: 46.9444cqw;
		left: 0;
		z-index: 2;
		display: flex;
		width: 100cqw;
		justify-content: center;
	}

	.section__pretitle {
		margin-bottom: 0.3472cqw;
		font-size: 1.3253cqw;
		font-weight: 600;
		line-height: 1.15;
	}

	.mission .section__pretitle,
	.vision .section__pretitle {
		margin-bottom: 0.3472cqw;
	}

	.section__title,
	.mission .section__title {
		font-size: 3.3333cqw;
		font-weight: 500;
		line-height: 1.25;
	}

	.section__text,
	.mission .section__text {
		width: 100%;
		max-width: none;
		margin-top: 2.5cqw;
		font-size: 1.6667cqw;
		line-height: 1.45;
	}

	.mission {
		height: 70.1493cqw;
		padding: 0;
		margin: 0;
	}

	.mission > .container,
	.vision > .container {
		height: 100%;
		width: 78.6979cqw;
		max-width: none;
		margin-right: 0;
		margin-left: 10.651cqw;
	}

	.mission-photo-wrap {
		top: 18.8993cqw;
		bottom: auto;
		left: 52.434cqw;
		width: 63.9583cqw;
		height: 41.7361cqw;
		overflow: hidden;

		/* The mask is a single top-to-bottom fade, so the clip's left and right
		   edges were cut square: the streaks in the artwork ran into the
		   boundary and stopped dead, which is exactly where the illusion that
		   the frame is part of the page breaks. A second layer fades the sides
		   and is intersected with the first, so the vertical falloff the
		   artwork was composed around is untouched. Both edges sit inside the
		   viewport on a wide screen, which is where this showed. */
		mask:
			url('../img/mission-mask-vedmi.svg?v=d52fe42d4f') center / 100% 100% no-repeat,
			linear-gradient(to right, transparent, #000 13%, #000 87%, transparent) center / 100% 100%
				no-repeat;
		mask-composite: intersect;
	}

	.mission-photo {
		display: block;
		width: 100%;
		height: 100%;
	}

	.mission::after {
		display: none;
	}

	.vision {
		height: 74.7917cqw;
		padding: 0;
		margin: 0;
	}

	.vision-photo {
		top: 15.2083cqw;
		left: 51.2882cqw;
		width: 91.1111cqw;
		height: 50.8333cqw;
		overflow: hidden;

		/* Same square cut as the mission slot above, and more visible here
		   because this frame is wider and its bands run further out. */
		mask:
			url('../img/vision-mask-vedmi.svg?v=52aba44735') center / 100% 100% no-repeat,
			linear-gradient(to right, transparent, #000 11%, #000 89%, transparent) center / 100% 100%
				no-repeat;
		mask-composite: intersect;
	}

	.vision-photo img,
	.vision-photo video {
		position: absolute;
		inset: 0;
		width: 100%;
		height: 100%;
		max-width: none;
		object-fit: cover;
	}

	.pricing {
		min-height: 0;
		padding: 0 0 12.5cqw;
		margin: 0;
	}

	.pricing > .container {
		width: 78.6979cqw;
		max-width: none;
		margin-right: 0;
		margin-left: 10.651cqw;
	}

	/* ---- the footer sits in the header's frame ------------------------ */

	/* The footer kept the narrow 78.7cqw measure while the header ran edge
	   to edge. Both are 1440-wide inline-size containers, so the header's
	   own numbers transfer verbatim and the two rails line up. */
	.footer > .container {
		width: 90.6944cqw;
		max-width: none;
	}

	/* Widening the rail opened a gap between the mark and the two columns.
	   Spreading the columns over the right half restores the header's
	   rhythm — mark, then two evenly spaced clusters. */
	.footer-lists {
		justify-content: space-between;
	}

	.footer-list .footer-list__name {
		margin-bottom: 1.1806cqw;
	}

	.footer-list {
		margin-right: 0;
		font-size: 1.1806cqw;
		line-height: 1.15;
	}

	.footer-list li {
		margin-bottom: 0.8333cqw;
	}

	.footer__policy {
		font-size: 0.9722cqw;
	}

	/* ---- prices ------------------------------------------------------- */

	.pricing__note {
		max-width: 46cqw;
	}

	.pricing .careers-table {
		max-width: 64cqw;
		margin-top: 3.4722cqw;
	}

	/* Descriptions run to two lines now, so cells start together at the top
	   instead of hanging off a shared baseline. */
	.pricing .careers-item {
		align-items: flex-start;
		padding-bottom: 1.3889cqw;
		margin-bottom: 1.3889cqw;
	}

	.pricing .careers-item-top {
		padding-bottom: 0.8333cqw;
	}

	.pricing .careers-col-wrap {
		align-items: flex-start;
	}

	.pricing .careers-col-1 {
		width: 11cqw;
	}

	.pricing .careers-col-2 {
		width: 31cqw;
		padding-right: 2cqw;
	}

	.pricing .careers__name {
		font-size: 0.9722cqw;
	}

	.pricing .careers__text,
	.pricing .careers__link {
		font-size: 1.1806cqw;
		line-height: 1.45;
	}

	/* ---- contacts: one card, split down the middle -------------------- */

	/* Replaces the .contacts-bg__panel rule and its comment, under the existing
   ---- contacts: one card, split down the middle ---- banner. */

	/* The field runs to 66% of the card and dissolves into the photograph over
	   its last third, so the seam is a bleed instead of a butt joint. Solid
	   ground therefore ends at 0.72 x 66% = 47.52%, and the copy column ends
	   at 46.39%: the container is 78.6979cqw centred in a 92.7083cqw card and
	   holds a 36cqw column, so the column runs 7.0052cqw to 43.0052cqw. That
	   is 15px of clearance at 1440, and it survives the scrub — while the card
	   is full-bleed the column's edge sits at 46.65% of it, because the copy
	   is anchored to the card's resting box while the mask is anchored to the
	   card. The ink is nowhere near either figure: the longest line, the
	   address, gives out around 37%. The stops are percentages here because
	   the panel's width tracks the card's, so the boundary keeps the same
	   place on the card at every point in the scrub. */
	.contacts-bg__panel {
		inset: 0 34% 0 0;
		mask-image: linear-gradient(to right, #000 0 72%, rgb(0 0 0 / 0) 100%);
	}

	.contacts .container {
		height: 100%;
	}

	/* space-between was set for six items and now holds three, which left two
	   equal 120px holes and read as a stack that had lost something rather
	   than as a composition. The column is two masses instead, one on each of
	   the card's horizontal margins: what the card says at the top, what it
	   offers at the bottom, and the field open between them. Both sit 60px in,
	   the margin the copy already keeps from the card's left edge, so the type
	   lands on the same frame the register ticks mark out at the corners —
	   which is what makes the card feel occupied rather than printed on.

	   The air in the middle is not a gap left by what was removed: it is where
	   the accent field dissolves into the photograph, so the quiet part of the
	   column sits over the brightest part of the frame. */
	.contacts-content {
		display: flex;
		height: 100%;
		max-width: 36cqw;
		flex-direction: column;
		padding: 4.1667cqw 0;
	}

	.contacts__title {
		margin-bottom: 1.3889cqw;
		font-size: 3.4722cqw;
		font-weight: 500;
		line-height: 1.05;
	}

	.contacts__lead {
		max-width: 26cqw;
		font-size: 1.1806cqw;
		line-height: 1.5;
	}

	/* The break between the two masses is whatever the card has left over —
	   about 240px at 1440 — while the intervals inside them stay tight: 20px
	   under the heading, 28px under the address. Four lines, two groups. */
	.contacts-mail {
		margin-top: auto;
	}

	/* The address is the point of the card, so it is set a step under the
	   heading rather than a step over the lead — 36px against the heading's
	   50px. It cannot go much further: the ink ends at 42.8% of the card and
	   the field is solid to 47.5%, so the line has to stay clear of the bleed
	   it would otherwise run into. */
	.contacts-mail__link {
		gap: 0.5556cqw;
		font-size: 2.5cqw;
		font-weight: 500;
		line-height: 1.15;
	}

	.contacts-mail__arrow {
		width: 1.7361cqw;
	}

	.contacts-meta {
		display: flex;
		align-items: center;
		gap: 1.6667cqw;
		margin-top: 1.9444cqw;
	}
}

/* -------------------------------------------------------------- responsive */

@media (width <= 767px) {
	.first-page {
		height: min(120cqw, 540px);
		padding-top: min(38.1333cqw, 150px);
		padding-bottom: 0;
	}

	.first-page__title {
		width: 100cqw;
		max-width: none;
		font-size: 14.4cqw;
		line-height: 1.08;
	}

	/* The menu takes the contact card's pairing rather than a pale sheet with
	   blue type on it: the accent field carrying lime. It is the only
	   full-screen surface on the site, so it should read like the one other
	   place the brand goes solid. */
	.modal-menu {
		border-color: rgb(255 255 255 / 16%);
		background: var(--color-accent);
	}

	/* The mark and the close control ship flat in the body ink and are
	   recoloured to the accent for the bar. Over the accent field itself that
	   would hide them, so here they go the other way. */
	.modal-menu .brand-logo--header,
	.modal-menu .header__burger img {
		filter: brightness(0) invert(1);
	}

	/* The menu was the one place the navigation dropped out of the bar's
	   register: body ink, sentence case and a regular weight, at a size that
	   read as page copy rather than as a list of destinations. It takes the
	   header's own treatment instead — the nav blue, upright, tracked and set
	   in the medium — only larger, because here it is the whole screen. The
	   size comes down from 36px as uppercase is the wider setting; the roll
	   still resolves each line to the deeper accent as it turns. */
	.modal-menu .header-list {
		color: var(--color-highlight);
		font-size: 7.4667cqw;
		font-weight: 500;
		letter-spacing: 0.02em;
		line-height: 1.15;
		text-transform: uppercase;
	}

	/* On the field the roll resolves to white; the deeper accent it turns to
	   in the bar would vanish into the ground here. One token drives the
	   incoming face, the rule and the bloom together. All three links on the
	   sheet answer the same way — the two below the list are set in its
	   register, so a second resolution would read as a mistake rather than a
	   distinction. */
	.modal-menu .header-list li a,
	.modal-menu .header__contacts,
	.modal-menu__policy {
		--link-resolve: #fff;
	}

	/* The list's rule stands 4px off a 28px face. Stated as a share of the
	   type rather than the viewport it carries to the policy link too, which
	   is set at 16px and would otherwise take that same flat 4px — clear of
	   its descenders instead of tucked into the leading the way the lines
	   above it are. */
	.modal-menu .header__contacts::after,
	.modal-menu__policy::after {
		bottom: 0.1429em;
	}

	/* Contact us was set apart from the list by everything at once — 26px
	   against 28, the regular weight against the medium, sentence case and no
	   tracking — so it read as a different kind of thing rather than as the
	   last item. It joins the register the list is in.

	   The rule that used to sit above it goes too. It was drawn to separate an
	   action from three destinations, but on a sheet that now closes on a foot
	   of its own it was the second horizontal division in a screen holding
	   four lines of type — and the one that fell in the middle of them. The
	   space alone carried the separation — except main.css sizes that space
	   for a rule to sit in the middle of: a padding-top over the border plus
	   an equal margin, 40px of it at 375. With nothing drawn in it, it read as
	   the list having simply stopped.

	   Both halves go. The line takes the list's own 1.0133cqw instead and
	   becomes the fourth in the column, which is what it now looks like — it
	   gained the list's register when it lost the rule, and it gains the same
	   leading from the roll, so the rhythm carries baseline to baseline. */
	/* The line is a direct child of the column, so the inline-block the rolled
	   links are set in blockifies and it stretches the full width. Its rule
	   would then run the whole column while the three above it stop at their
	   labels. Held to its own width, it underlines what it says. */
	.modal-menu .header__contacts {
		align-self: flex-start;
		padding-top: 0;
		margin-top: 1.0133cqw;
		border-top: 0;
		color: var(--color-highlight);
		font-size: 7.4667cqw;
		font-weight: 500;
		letter-spacing: 0.02em;
		line-height: 1.15;
		text-transform: uppercase;
	}

	/* The sheet used to stop after Contact us and leave better than half a
	   screen of flat accent under it. It closes on a foot instead, pushed down
	   by the column so it sits on the bottom however few destinations the list
	   holds: the policy link the site footer keeps in its navigation — set as
	   a legal link rather than a fourth destination, since at the list's
	   7.4667cqw it would read as one — and the notice under it. */
	.modal-menu .container {
		display: flex;
		flex-direction: column;
		min-height: 100%;
	}

	.modal-menu__foot {
		margin-top: auto;
		padding-top: 12.8cqw;
		padding-bottom: 8cqw;
	}

	.modal-menu__policy {
		display: inline-block;
		color: var(--color-highlight);
		font-size: 4.2667cqw;
		font-weight: 500;
		letter-spacing: 0.02em;
		line-height: 1.15;
		text-transform: uppercase;
	}

	/* The notice sits under the link and stays the quieter of the two lines,
	   which is the whole job of it — but 0.7 is where that stops costing
	   legibility: white at 0.6 over the accent field lands at 4.23:1, and at
	   16px the text is held to 4.5. This clears it at 5.19. */
	.modal-menu__note {
		margin-top: 3.2cqw;
		color: var(--color-on-accent);
		font-size: 3.7333cqw;
		line-height: 1.4;
		opacity: 0.7;
	}

	/* The accent ring would disappear into the field the menu now carries. */
	.modal-menu :where(a, button):focus-visible {
		outline-color: var(--color-highlight);
	}

	.header__logo .brand-logo--header {
		width: 29cqw;
		max-width: 7.75rem;
	}

	.first-page__bg {
		top: 0;
		width: 100cqw;
		height: 112cqw;
		max-width: none;
	}

	/* Everything about this control scales with the viewport across the
	   breakpoint except the one measurement that decides its shape. A flat
	   40px against a label that reaches 28.6px by 767px ran the box from
	   5.3:1 out to 10.8:1 and closed the cap band to 1.3px of its own edge —
	   a sliver with the type about to burst out of it.

	   The height joins the scale the rest of it is on. 10.6667cqw is the 40px
	   the comp draws at 375, which is also where the desktop rail lands, so
	   one proportion now carries from 320 through to 767 and hands over to
	   desktop unchanged. The floor keeps the tap target on the narrow phones
	   the scale would otherwise take it below. */
	.first-page__btn {
		width: auto;
		min-width: 49.0667cqw;
		height: max(40px, 10.6667cqw);
		padding: 0 5.3333cqw;
		border-radius: 2px;
		font-size: 3.7333cqw;
	}

	.vision {
		padding-bottom: 64cqw;
	}

	.mission {
		overflow: hidden;
	}

	.vision-photo {
		top: auto;
		bottom: 0;
	}

	.pricing__note {
		max-width: 100%;
	}

	.pricing .careers__text,
	.pricing .careers__link {
		font-size: 4.5333cqw;
	}

	/* The chip was the odd one out. Every link on the page sets a bare
	   diagonal arrow beside its label — this one included, one breakpoint up —
	   and only here did the phone answer with a filled accent pill carrying a
	   straight arrow of its own, so the same control read as two different
	   components depending on the width.

	   The desktop mark comes down instead and the chip stands down. .pc-true
	   is display:none !important from main.css, so revealing it takes the same
	   weight back; hiding the chip does not, its own selector already outranks
	   the one that laid it out. The arrow is sized in em rather than cqw so it
	   tracks the label exactly the way the desktop one does — 0.44em and
	   0.176em are the ratios that rule resolves to at 1440. */
	.pricing .careers__link .careers__mob {
		display: none;
	}

	.pricing .careers__link img.pc-true {
		display: block !important;
		width: 0.44em;
		margin-left: 0.176em;
	}

	.pricing .careers-col-1 .careers__text {
		font-size: 5.6cqw;
	}

	.footer-list {
		font-size: 4.5333cqw;
	}

	.footer__policy,
	.footer__logo .footer__policy {
		font-size: 3.7333cqw;
	}

	/* ---- contacts: the split turns horizontal ------------------------- */

	/* Stacked, the seam runs across instead of down: artwork on top, the
	   solid field under it carrying the address. The card grows to hold the
	   taller stack — the old height was cut for a three-field form. */
	/* Stacked, the card was cut for more than it holds: the copy ran out about
	   95px above the bottom edge and the rest was dead accent. The height now
	   follows the content and keeps a bottom margin in the register of the
	   card's other insets. main.js's mobile scrub frame comes down with it, so
	   the full-bleed start stays the same proportion of the settled card.

	   Losing the eyebrow, the copy row and the city took 35cqw out of the
	   stack and put every one of them back into that dead field — 190px of
	   flat accent under the socials. The height comes down to leave 48px, four
	   times the 12px the stack is built on and the same interval the copy
	   keeps from the card's sides doubled. 143.28cqw in main.js holds the
	   1.116 ratio the full-bleed frame had against the settled card. */

	/* That figure was measured against one stack at one width, and a fixed
	   height cannot hold a stack whose lead re-wraps: the copy needs 142cqw at
	   375 and 132cqw by 600, so 139cqw was short at the narrow end and the
	   socials fell straight out of the bottom of the card — 52px clear of it at
	   375, still 23px at 600, and across the register ticks the whole way.

	   The height follows the content instead, which is the one measure that
	   tracks the re-wrap for free; 139cqw stays on as the floor it was always
	   meant to be. .contacts-vw resolves its own 100% against this box, so it
	   comes with it, and the card is absolutely positioned inside that — a
	   percentage height on an absolute box still resolves against a containing
	   block whose height is auto, so the scrub keeps working untouched. */
	.contacts {
		height: auto;
		min-height: 139cqw;
	}

/* Replaces the .contacts-bg__panel rule; the other three are new. The mobile
   .contacts-bg background-image override goes as well — the base rule in part
   1 now clears the element's background outright. */

	/* Stacked, the dissolve has to run down the card rather than across it:
	   the field comes up out of the bottom and thins away into the photograph
	   at its top edge. The edge moves up from 44cqw to 30cqw to give the bleed
	   somewhere to live, exactly as the desktop edge moved out from 50% to
	   66%, and the field is solid again by 46cqw — 4cqw clear of the copy,
	   which starts at the 50cqw the content is padded down by, and the lime
	   hairline is the first thing to land there. The old hard seam at 44cqw
	   now sits inside the bleed, three quarters of the way along it.

	   The stops are lengths rather than percentages because the panel is
	   measured from the top of a box the scrub keeps resizing: its height
	   moves by a sixth between the full-bleed frame and the resting one, and a
	   percentage fade would grow with it and creep under the copy. In cqw the
	   bleed is 16cqw wherever the scrub has the card, and the copy stays on
	   solid ground at both ends of it. */
	.contacts-bg__panel {
		inset: 30cqw 0 0;
		mask-image: linear-gradient(to bottom, rgb(0 0 0 / 0) 0, #000 16cqw);
	}

	/* Turned a quarter turn with the split: the shadow closes the top of the
	   frame instead of the far corner, and the accent bleeds up out of the
	   seam instead of in from the left. The bleed's stops are in cqw because
	   they belong to the seam, which sits at a fixed 30cqw from the card's top
	   edge; 54% of a card this tall would put the whole ramp under the opaque
	   part of the field, where nobody would ever see it. */
	.contacts-bg__lomo {
		background:
			radial-gradient(104% 92% at 50% 72%, rgb(12 30 66 / 0) 38%, rgb(12 30 66 / 44%) 100%),
			linear-gradient(to bottom, rgb(14 62 177 / 0) 35cqw, rgb(14 62 177 / 22%) 46cqw);
	}

	/* The arms are marks on the frame and stay 24px, but the inset does not
	   survive the change of scale with them. 22px inside a 1335px desktop card
	   is 1.65% of its width and reads as a register mark sitting just off the
	   corner; the same 22px inside a 384px phone card is 6.6%, four times as
	   far in, and the marks stopped describing a corner at all — they stood in
	   the middle of an empty margin with the copy pressed onto the very line
	   they were drawn on.

	   They go out to 3.2cqw, 3.6% of the card, near enough the corner to read
	   as its register again. The copy keeps 5.8667cqw, so the line it used to
	   share is now its own with 10px of air behind it.

	   The elbow follows them out. Concentric means the mark's radius is the
	   card's less the inset, and 20 - 12 over 20 is 0.4 where a 22px inset
	   wanted the 0.3125 the base rule carries. */
	.contacts-bg {
		--tick-arm: 6.4cqw;
		--tick-inset: 3.2cqw;
		--tick-curve: 0.4;
	}

	/* Stacked, the copy took its horizontal position from the container while
	   the card took its own from .contacts' padding, and on a phone those are
	   the same 20px — so the copy sat exactly on the card's edge, and further
	   out than the register ticks that mark the frame's inner margin. It is
	   inset onto that line instead: 22px from the card on both sides, the same
	   figure the ticks use, so the column and the marks share an edge. */
	/* The column had no bottom padding at all, so the socials sat flush on the
	   end of the content box and the ticks — which live 22px in from the card
	   edge and run 24px from there — came up underneath them. The foot of the
	   column now clears that band: 60px is five of the 12px the stack is built
	   on, and it leaves 14px of air between the last icon and the top of the
	   mark rather than the 2px the old 48px figure would have. */
	.contacts-content {
		align-items: flex-start;
		max-width: 100%;
		padding-top: 50cqw;
		padding-bottom: 16cqw;
		padding-inline: 5.8667cqw;
		text-align: left;
	}

	.contacts__title {
		margin-bottom: 3.2cqw;
		font-size: 9.6cqw;
		text-align: left;
		line-height: 1.05;
	}

	.contacts__lead {
		font-size: 4.2667cqw;
		line-height: 1.5;
	}

	/* The same three intervals the desktop column keeps, in the unit this
	   breakpoint measures with: 12px, 36px, 24px. */
	.contacts-mail {
		margin-top: 9.6cqw;
	}

	/* The address is one unbreakable token, so it sets the floor for the
	   column: at the old size it would have run past the narrower measure the
	   inset leaves. A step down keeps it on one line with room to spare. */
	.contacts-mail__link {
		gap: 1.6cqw;
		font-size: 5.6cqw;
		font-weight: 500;
		line-height: 1.15;
	}

	.contacts-mail__arrow {
		width: 4.2667cqw;
	}

	.contacts-meta {
		display: flex;
		flex-wrap: wrap;
		align-items: center;
		gap: 4.2667cqw;
		margin-top: 6.4cqw;
	}

	.pricing .careers-table {
		max-width: 100%;
		margin-top: 8cqw;
	}

	.pricing .careers-col-1 {
		width: 32cqw;
		padding-right: 4cqw;
	}

	.pricing .careers-col-wrap {
		flex: 1;
		min-width: 0;
	}

	.pricing .careers-col-2 {
		width: auto;
		padding-right: 0;
	}

	.pricing .careers-col-1 .careers__text {
		font-size: 4.8cqw;
	}

	.mission-photo-wrap {
		bottom: 0;
		width: 110cqw;
		height: 86cqw;
		max-width: none;
		overflow: hidden;
		mask-image: linear-gradient(to bottom, #000 72%, transparent 100%);
	}

	.mission-photo {
		display: block;
		width: 100%;
		height: 100%;
	}

	.mission .section__text br.mob-true {
		display: none;
	}

	.section__pretitle {
		font-family: var(--font-sans);
		text-transform: lowercase;
		opacity: 1;
	}

	.section__title {
		color: var(--color-accent);
		font-weight: 500;
		line-height: 1.25;
	}

	.pricing .section__title {
		margin-bottom: 0;
	}

	.section__text,
	.mission .section__text {
		line-height: 1.45;
		opacity: 1;
	}

	.careers__link .careers__mob,
	.footer__email {
		color: var(--color-on-accent);
		background: var(--color-accent);
	}

	.footer__email:hover,
	.careers__link:hover .careers__mob {
		background: var(--color-accent-hover);
	}

	/* 10px of arrow in a 50px bar read as a speck rather than as the mark the
	   rest of the page sets beside an address. It takes the 16px the contacts
	   card gives its own arrow, which is the same glyph doing the same job;
	   the bar keeps its height, so only the mark grows. */
	.footer__email img {
		width: 4.2667cqw;
	}

	.contacts-soc .contacts-soc__item img {
		width: 10.66667cqw;
	}

	.footer-soc .footer-soc__item img {
		width: 6.4cqw;
	}

	/* Four chips no longer fit the mobile Social Media column at the desktop
	   gutter, so they get a tighter gap and the column drops its trailing one. */
	.footer-lists .footer-list.mob-true {
		margin-right: 0;
	}

	.footer-soc.mob-true {
		gap: 3.2cqw;
	}

	.footer-soc.mob-true .footer-soc__item {
		flex-shrink: 0;
		margin-right: 0;
	}

	.media-reveal[data-aos] {
		filter: var(--media-tone) blur(20px);
		transform: translate3d(0, 12cqw, 0) scale(1.02);
	}

	/* No hero override here any more: the open state is a share of the
	   viewport height centred in the viewport on every breakpoint, so the
	   phone stopped needing numbers of its own the day the numbers stopped
	   being widths. */

	.media-reveal[data-aos].aos-animate {
		filter: var(--media-tone) blur(0);
		transform: translateZ(0) scale(1);
	}
}

@media (prefers-reduced-motion: reduce) {
	*,
	*::before,
	*::after {
		scroll-behavior: auto !important;
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
	}
}

/* ---------------------------------------------------------- control motion */

/* No field and no second face any more: both buttons answer in their own
   material — the pane densifies, the bloom gathers, and a streak of light
   crosses the glass. The whole response lives in this stylesheet, so a
   visitor without JavaScript loses nothing at all. The press and the focus
   ring in the section above still own the manners; each button's blooms
   ride in its token block beside the tints they light. */

/* The bloom hangs off a control that is 40px tall at every width, so it is set
   in px like that height and the hairline rather than in cqw — at 375px wide a
   cqw bloom would all but vanish under the same button. The buttons carry
   their own blooms; the e-mail bar keeps the house blue. */
.footer__email {
	--halation: 0 14px 34px -14px rgb(14 62 177 / 55%), 0 3px 10px -6px rgb(14 62 177 / 30%);
	--halation-press: 0 6px 16px -10px rgb(14 62 177 / 50%);
}

/* The pane also catches light of its own. Every nine seconds a specular
   streak crosses each control once — the flare a window gives when
   something bright moves past — and the two are set half a cycle apart, so
   there is a glint somewhere but never a blink everywhere. The pointer does
   not wait for the cycle: hover fires the same band at once, once per
   arrival, and leaving hands it back to the ambient loop — which is what
   re-arms it for the next arrival. It rides above the label because light
   lands on the surface of glass, not behind it. Transform-only, so it costs
   the compositor and nothing else; the reduced-motion blanket above
   collapses both passes to a single invisible frame, and the pointer is
   still answered there by the density step, flat. */
.first-page__btn::before,
.header .header__contacts::before {
	content: '';
	position: absolute;
	top: -20%;
	bottom: -20%;
	left: 0;
	z-index: 1;
	width: 36%;
	background: linear-gradient(
		100deg,
		rgb(255 255 255 / 0%) 0%,
		var(--glint) 45%,
		rgb(255 255 255 / 16%) 62%,
		rgb(255 255 255 / 0%) 100%
	);
	transform: translateX(-180%) skewX(-16deg);
	pointer-events: none;
	animation: btn-glint 9s var(--ease-standard) 3.2s infinite;
}

/* The frost is nearly white, so its streak is held low; the lime pane is
   denser and can carry more before it reads as a flash. */
.first-page__btn::before {
	--glint: rgb(255 255 255 / 42%);
}

.header .header__contacts::before {
	--glint: rgb(255 255 255 / 72%);
	animation-delay: 7.7s;
}

.first-page__btn:hover::before,
.first-page__btn:focus-visible::before,
.header .header__contacts:hover::before,
.header .header__contacts:focus-visible::before {
	animation: btn-glint-hover 850ms var(--ease-standard);
}

@keyframes btn-glint {
	0% {
		transform: translateX(-180%) skewX(-16deg);
	}

	12% {
		transform: translateX(480%) skewX(-16deg);
	}

	100% {
		transform: translateX(480%) skewX(-16deg);
	}
}

/* The hover pass is the ambient travel cut to its own clock, so it can
   start the instant the pointer lands and finish while it is still there. */
@keyframes btn-glint-hover {
	from {
		transform: translateX(-180%) skewX(-16deg);
	}

	to {
		transform: translateX(480%) skewX(-16deg);
	}
}

/* The dense state: the same tint at a higher alpha over half again the
   blur, with the bloom appended. Background and bloom ease on the shared
   manners, and backdrop-filter is in that transition list for exactly this
   rule — a pane that thickens in one frame reads as a swap, not as glass.
   The filter chains are restated whole because the two panes season their
   backdrops differently. */
.first-page__btn:hover,
.first-page__btn:focus-visible {
	background: var(--btn-glass-dense);
	box-shadow: var(--btn-light), var(--halation);
	backdrop-filter: blur(1.1719cqw) saturate(1.5) brightness(1.06);
	-webkit-backdrop-filter: blur(1.1719cqw) saturate(1.5) brightness(1.06);
}

.header .header__contacts:hover,
.header .header__contacts:focus-visible {
	background: var(--btn-glass-dense);
	box-shadow: var(--btn-light), var(--halation);
	backdrop-filter: blur(1.1719cqw) saturate(1.6);
	-webkit-backdrop-filter: blur(1.1719cqw) saturate(1.6);
}

/* The mobile e-mail bar is mob-true markup, so it never reaches the desktop
   rail and needs no width query of its own. It takes the press and the bloom
   but not the wipe: a field rising behind a label and an arrow set at opposite
   ends of a full-width bar would read as the background changing rather than
   as the control answering. */
/* The same trap the contacts address fell into, and it wants the same answer:
   main.css resets every a:hover to inherit, a type-plus-pseudo outranks the
   bar's own class, and the label dropped out of white into body ink — black,
   on a solid accent field — the moment it was pointed at. It answers in the
   lime the address answers in.

   The arrow goes with it. It ships as a near-white glyph in an <img>, which
   currentcolor cannot reach, so the tint is a filter: the chain zeroes the
   source and rebuilds it, landing on rgb(222 241 164) against the
   rgb(221 242 164) the token holds — the same lime to the eye. */
.footer__email:hover,
.footer__email:focus-visible {
	color: var(--color-highlight);
	box-shadow: var(--halation);
}

.footer__email img {
	transition: filter var(--motion-fast) var(--ease-standard);
}

.footer__email:hover img,
.footer__email:focus-visible img {
	filter: brightness(0) saturate(100%) invert(91.9%) sepia(23.8%) saturate(488%)
		hue-rotate(20deg) brightness(98.8%) contrast(95%);
}

/* Each control's own hover rule outranks the shared :active in the section
   above, so the press is spelled out per control to land after it. The bloom
   tightens under the finger the way a real one would; the 1px and its 80ms
   come from that section. */
.first-page__btn:active,
.header .header__contacts:active {
	box-shadow: var(--btn-light), var(--halation-press);
}

/* The e-mail bar carries no glass, so there is no light to keep under its
   press — and folding an undefined --btn-light into the list here would
   invalidate the whole declaration rather than contribute nothing to it. */
.footer__email:active {
	box-shadow: var(--halation-press);
}

/* The inline link keeps its arrow but takes the rule the address and the nav
   links use — in from the left, out to the right — so the family reads as one
   set of manners. The rule sits under the label alone, never under the arrow
   beside it, which is why main.js gives the label a box of its own. A hairline
   stays a hairline at every width, so it is 1px rather than cqw. */
.tlink__label {
	position: relative;
}

.tlink__label::after {
	content: '';
	position: absolute;
	right: 0;
	bottom: -0.18em;
	left: 0;
	height: 1px;
	background: currentcolor;
	transform: scaleX(0);
	transform-origin: right center;
	transition: transform var(--motion-fast) var(--ease-standard);
}

.tlink:hover .tlink__label::after,
.tlink:focus-visible .tlink__label::after {
	transform: scaleX(1);
	transform-origin: left center;
}

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

/* The page does not stop at the footer, it settles into it. Three things carry
   that: the field deepens toward the bottom edge the way the last frame of a
   reel loses its light, the divider becomes a film edge that fades out at both
   ends instead of butting into the margins, and the wordmark — the one thing
   down here that is purely image — sits fractionally out of focus and resolves
   when you reach for it. The links keep the roll and the chips keep their lift;
   this layer only does the ground, the edge, the mark and the column heads. */

/* ---- the ground the footer settles onto --------------------------- */

/* The footer box is capped at 1440px like the header, so a ground pinned to
   it stopped dead at that edge while the page field carried on to the corners
   of a wider screen. It breaks out to the viewport instead; html and body
   already hide the horizontal overflow, so the overhang costs no scrollbar.

   A local deepening, not a background. Two low-alpha washes lie over whatever
   the page-wide film field has already painted, so the bottom of the page loses
   its light without the footer reading as a second panel dropped on top of one.
   The top third of the gradient stays fully clear, which is what keeps the
   footer's upper edge from showing as a seam.
   .footer already carries container-type: inline-size, and that containment
   makes it both the containing block for the pseudo and a stacking context of
   its own; position and isolation are stated anyway so the ground stays where
   it belongs if the container ever moves elsewhere. */
.footer {
	position: relative;
	isolation: isolate;
}

.footer::before {
	content: '';
	position: absolute;
	z-index: -1;
	inset: 0 auto;
	left: 50%;
	width: 100vw;
	transform: translateX(-50%);
	background:
		linear-gradient(to bottom, rgb(19 74 146 / 0%) 34%, rgb(19 74 146 / 7.5%) 100%),
		radial-gradient(120% 90% at 50% 100%, rgb(18 18 18 / 6%), rgb(18 18 18 / 0%) 62%);
	pointer-events: none;
}

/* ---- the divider becomes a film edge ------------------------------ */

/* A border runs the full measure and stops dead against both margins. The rule
   the footer wants fades up out of nothing and back into it the way the edge of
   a frame does, so it has to be drawn rather than bordered. The hairline stays
   in px: it is an optical constant, not a measure that should grow with the
   container the way the type around it does. */
.footer .top {
	position: relative;
	border-bottom: 0;
}

.footer .top::after {
	content: '';
	position: absolute;
	right: 0;
	bottom: 0;
	left: 0;
	height: 1px;
	background: linear-gradient(
		to right,
		rgb(18 18 18 / 0%) 0%, rgb(18 18 18 / 18%) 7%,
		rgb(18 18 18 / 18%) 93%, rgb(18 18 18 / 0%) 100%
	);
}

/* ---- the wordmark resolves ---------------------------------------- */

/* The mark is the only purely photographic thing down here, so it gets the one
   gesture this area owns: it rests a hair out of focus behind a soft accent
   bloom and pulls sharp when a pointer or the keyboard reaches for it.
   The blur and the shadow stay in px because they are lens amounts tied to the
   mark's rendered size, and that size barely moves between the 300px desktop
   mark and the 251px mobile one — in cqw the bloom would vanish on a phone.
   Two classes deep because main.css owns `.footer__logo img`, which outranks a
   bare `.brand-logo--footer` and has been quietly winning all along. */
.footer__logo .brand-logo--footer {
	opacity: 0.86;
	filter: blur(0.45px) drop-shadow(0 0 24px rgb(14 62 177 / 16%));
	transition:
		opacity var(--motion-fast) var(--ease-standard),
		filter var(--motion-fast) var(--ease-standard);
}

.brand-lockup--footer:hover .brand-logo--footer,
.brand-lockup--footer:focus-visible .brand-logo--footer {
	opacity: 1;
	filter: blur(0) drop-shadow(0 6px 26px rgb(14 62 177 / 30%));
}

/* ---- column heads read as a timetable ----------------------------- */

/* Sentence case reads as a caption sitting on the list. Uppercase and tracked,
   set a step smaller than the links it opens, it reads as the heading of a
   schedule — which is what a column of destinations is. main.css owns the 400
   weight at the two-class `.footer-list .footer-list__name`, so the 500 has to
   be reclaimed at the same weight of selector. */
.footer-list .footer-list__name {
	font-weight: 500;
	letter-spacing: 0.1em;
	text-transform: uppercase;
}

@media (width >= 768px) {
	/* ---- the wordmark takes the left side back -------------------- */

	/* Under 200px the mark sat in the corner and left a dead gap between
	   itself and the columns. At 300px it holds the left of the row the way
	   the lockup holds the left of the header, and the two columns give back
	   32px so the row balances instead of spreading to fill the rail. */
	.footer__logo .brand-logo--footer {
		width: 20.8333cqw;
	}

	.footer-lists {
		width: 52.7778cqw;
	}

	.footer-list .footer-list__name {
		font-size: 0.8333cqw;
	}
}

@media (width <= 767px) {
	/* The mark keeps its size but sits in focus. There is no hover on a phone,
	   so a permanently soft 251px wordmark has no way to resolve and reads as a
	   rendering fault rather than as a lens. The accent bloom stays behind it —
	   that part is a treatment, not a gesture, and it survives the loss. */
	.footer__logo .brand-logo--footer {
		width: 66.9333cqw;
		opacity: 1;
		filter: drop-shadow(0 0 24px rgb(14 62 177 / 16%));
	}

	/* Uppercase and tracking widen the head, and "Social Media" sits beside the
	   navigation column inside a 335px measure, so the head comes down a step
	   to keep the two columns off each other. */
	.footer-list .footer-list__name {
		font-size: 3.4667cqw;
	}

	/* main.css drops the divider on mobile and hangs the whole 40px gap below
	   .top. The film edge earns its place here — the bottom row is a loose
	   stack of a map pin and four chips with nothing marking where it begins —
	   so the gap splits in half and the hairline lands in the middle of it. The
	   distance from the last row of .top to .bottom is unchanged. */
	.footer .top {
		padding-bottom: 5.3333cqw;
		margin-bottom: 5.3333cqw;
	}
}

/* A 0.45px blur needs sub-pixel rendering to read as softness. At 1x it is a
   one-pixel edge ramp, and under the resting opacity that reads as a mark that
   failed to load rather than as one held out of focus. The bloom and the
   resolve both stay; only the blur stands down. */
@media (resolution < 2dppx) {
	.footer__logo .brand-logo--footer {
		filter: drop-shadow(0 0 24px rgb(14 62 177 / 16%));
	}

	.brand-lockup--footer:hover .brand-logo--footer,
	.brand-lockup--footer:focus-visible .brand-logo--footer {
		filter: drop-shadow(0 6px 26px rgb(14 62 177 / 30%));
	}
}

/* A visitor who has asked for less motion never sees the mark pull into focus,
   so it should not be sitting soft and waiting for a gesture that will not
   arrive — it starts sharp and stays there, hover included. */
@media (prefers-reduced-motion: reduce) {
	.footer__logo .brand-logo--footer,
	.brand-lockup--footer:hover .brand-logo--footer,
	.brand-lockup--footer:focus-visible .brand-logo--footer {
		opacity: 1;
		filter: none;
	}
}
