/*
Theme Name: Kybix Theme
Author URI: https://www.kybix.com
Description: Wordpress Theme for the Kybix marketing website
Version: 1.1.0
Text Domain: kybix-theme
*/

/* ============================================================= *
 * Fonts
 *
 * The same two faces the portal uses, self-hosted so the site makes
 * no request off-site for them. Both are variable fonts covering
 * their whole weight range in one file, which is why each @font-face
 * declares a range rather than a single weight -- every weight the
 * stylesheet asks for is interpolated from it, and none is synthesised.
 * Ship a static instance instead and that range becomes a lie.
 * ============================================================= */
@font-face {
	font-family: "Figtree";
	src: url("assets/fonts/Figtree-Variable.woff2") format("woff2");
	font-weight: 300 900;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: "ZalandoSans";
	src: url("assets/fonts/ZalandoSans-Variable.woff2") format("woff2");
	font-weight: 200 900;
	font-style: normal;
	font-display: swap;
}

/* ============================================================= *
 * Design tokens
 * ============================================================= */
:root {
	--purple: #6a24d6;
	--purple-bright: #7c4dff;
	--purple-deep: #4e14c0;
	/* The flat purple the newer designs draw: buttons, accents, card borders. */
	--purple-flat: #7747e8;
	--navy: #1d0837;
	/*
	 * The ground the dark wave artwork is painted on, and the plate every band
	 * that carries it sits on. It is NOT --navy: wave-purple.jpg fades out to a
	 * flat border of exactly this colour on all four sides, and a plate five
	 * units off it draws a line wherever the artwork stops. Change the artwork
	 * and this has to follow it.
	 */
	--wave-ground: #180731;
	/* The home page's own ground, a shade below --navy, and the text on it. */
	--ink: #10041f;
	--ink-text: #ddd7fa;
	--heading: #1b1233;
	--text: #4b5563;
	--text-muted: #6b7280;
	--bg: #ffffff;
	--bg-light: #f4f5f7;
	--border: #e6e7eb;
	--teal: #05a394;
	--teal-dark: #037e72;

	/*
	 * The design's own content box. Home.png and Contact.png are 1710px frames
	 * that centre their content in 120px gutters -- x=120..1590, 1470 wide --
	 * so the container is that 1470 plus .container's own 24px padding either
	 * side. 1710 is the frame, not the box: a container that wide would run the
	 * copy out to within 24px of the viewport edge, which the design never does.
	 * This replaces a deliberate 1200px narrowing, so every section on every
	 * page is now wider than it was.
	 */
	--container: 1518px;
	--radius: 16px;
	--radius-sm: 12px;

	--font-body: "Figtree", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
	--font-head: "ZalandoSans", var(--font-body);
}

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

body {
	margin: 0;
	padding: 0;
	font-family: var(--font-body);
	font-size: 17px;
	line-height: 1.6;
	color: var(--text);
	background: var(--bg);
	-webkit-font-smoothing: antialiased;
}

img {
	max-width: 100%;
	height: auto;
	display: block;
}

a {
	color: var(--purple);
	text-decoration: none;
}

a:hover {
	text-decoration: underline;
}

h1,
h2,
h3 {
	font-family: var(--font-head);
	color: var(--heading);
	line-height: 1.15;
	letter-spacing: -0.02em;
	margin: 0;
}

p {
	margin: 0 0 1rem;
}

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

.strong {
	color: var(--heading);
	font-weight: 600;
}

.sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	border: 0;
}

.container {
	width: 100%;
	max-width: var(--container);
	margin: 0 auto;
	padding-left: 24px;
	padding-right: 24px;
}

.section {
	padding: 88px 0;
	background: var(--bg);
}

.section--light {
	background: var(--bg-light);
}

.section--light-b {
	background: linear-gradient(180deg, #eceef3 0%, #f4f5f7 100%);
}

/*
 * Wave-animated section background. The JPEG is set here rather than in the
 * canvas script so the stylesheet stays the one place the artwork is named —
 * assets/js/wave-bg.js reads this rule's background-image back off the element.
 * The canvas redraws the same artwork warped, painting over the CSS copy; if
 * the script never runs the still image is simply what shows.
 */
.section--wave {
	position: relative;
	overflow: hidden;
	background-image: url("assets/img/bgr-grey-round.jpg");
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
}

.wave-bg {
	position: absolute;
	inset: 0;
	z-index: 0;
	width: 100%;
	height: 100%;
	pointer-events: none;
}

.section--wave > .container {
	position: relative;
	z-index: 1;
}

.section__title {
	font-size: clamp(1.9rem, 3.5vw, 3rem);
	font-weight: 400;
}

/*
 * The home page's sections breathe more than the rest of the site's do, which
 * is the one place the design's own rhythm is followed rather than the theme's
 * narrower default. Scoped to .home so the team and contact pages keep theirs.
 */
.home .section {
	padding: 104px 0;
}

/*
 * Faint circuit-trace artwork behind two of the light sections. Decorative
 * only: nothing in the layout depends on where it lands, and it is never
 * tiled -- each copy is one image at its own size.
 */
.section--trace-a,
.section--trace-b {
	position: relative;
	overflow: hidden;
}

/*
 * Every copy is laid on a band the width of the container rather than of the
 * section, so on a wide screen the artwork stays with the column it sits
 * behind instead of drifting out to the viewport edge -- each one starts at
 * the container's own edge, the same line .split__inner starts on.
 * `left: 0; right: 0` plus `margin-inline: auto` does the centring, which
 * leaves `transform` free for the mirror below.
 */
.section--trace-a::before,
.section--trace-b::before,
.section--trace-b::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	width: 100%;
	max-width: var(--container);
	height: 375px;
	margin-inline: auto;
	background-size: 640px 375px;
	background-repeat: no-repeat;
	pointer-events: none;
}

.section--trace-a::before {
	top: 0;
	background-image: url("assets/img/circuit-a.jpg");
	background-position: left top;
}

/*
 * Two copies of the same artwork, mirrored against each other: one at the top
 * of the section on the right, one lower down on the left. The right-hand copy
 * is the flipped one, which is why its background sits at `left` -- scaleX(-1)
 * turns that into the band's right edge.
 */
.section--trace-b::before {
	top: 0;
	transform: scaleX(-1);
	background-image: url("assets/img/circuit-b.jpg");
	background-position: left top;
}

.section--trace-b::after {
	top: 200px;
	background-image: url("assets/img/circuit-b.jpg");
	background-position: left top;
}

.section--trace-a > .container,
.section--trace-b > .container {
	position: relative;
	z-index: 1;
}

/* The design's centred headings run up to 1230px at 50px, which is what this
   is -- not the container, which is now wider than any of them wants to be.
   The copy under one is held to its own, narrower measure by the rule below. */
.center-narrow {
	max-width: 1230px;
	margin-inline: auto;
	text-align: center;
}

.center-narrow > p {
	max-width: 980px;
	margin-inline: auto;
	color: var(--text-muted);
	font-size: 1rem;
	line-height: 1.75;
}

.center-narrow .section__title {
	margin-bottom: 1.25rem;
}

/* ============================================================= *
 * Buttons & links
 * ============================================================= */
.btn {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.85rem 1.6rem;
	border-radius: 999px;
	font-family: var(--font-head);
	font-weight: 600;
	font-size: 0.98rem;
	line-height: 1;
	cursor: pointer;
	border: 1px solid transparent;
	transition: filter 0.15s ease, transform 0.15s ease, background 0.15s ease;
}

.btn:hover {
	text-decoration: none;
	filter: brightness(1.05);
}

/* Flat, not the site's older gradient: the design draws one tone. */
.btn--purple {
	background: var(--purple-flat);
	color: #fff;
}

/* The hero's pair, which the design draws taller than the site's standard
   button: 56px rather than ~45. */
.btn--lg {
	padding: 1.15rem 1.75rem;
	font-size: 1rem;
}

.btn--light {
	background: #fff;
	color: var(--heading);
	border-color: var(--border);
}

.btn--contact {
	background: var(--navy);
	color: #fff;
	padding: 0.7rem 1.5rem;
}

.btn--teal {
	background: var(--teal);
	color: #06251f;
}

.btn--teal:hover {
	background: var(--teal-dark);
	color: #fff;
	filter: none;
}

.i-arrow {
	flex: none;
}

.link-arrow {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	font-weight: 600;
	color: var(--purple);
}

.link-arrow:hover {
	text-decoration: none;
	gap: 0.6rem;
}

/* ============================================================= *
 * Header / nav
 * ============================================================= */
.site-header {
	position: sticky;
	top: 0;
	z-index: 50;
	background: rgba(255, 255, 255, 0.92);
	backdrop-filter: saturate(180%) blur(8px);
	border-bottom: 1px solid var(--border);
}

.site-header__inner {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1.5rem;
	min-height: 76px;
}

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

.site-logo {
	width: auto;
	height: 34px;
}

.site-nav {
	display: flex;
	align-items: center;
}

/*
 * The gap is fluid rather than flat 2rem because the bar now carries seven
 * items and a button. At the design's width the clamp lands on 2rem and
 * nothing has moved; below it the row gives ground gradually instead of
 * overflowing, which is what it used to do between the 720px hamburger
 * breakpoint and about 1100px.
 */
.site-nav__list {
	display: flex;
	align-items: center;
	gap: clamp(1rem, 1.9vw, 2rem);
	list-style: none;
	margin: 0;
	padding: 0;
}

.site-nav__list a {
	font-family: var(--font-head);
	font-weight: 500;
	font-size: 0.98rem;
	color: var(--heading);
}

.site-nav__list a:hover,
.site-nav__list .current-menu-item > a,
.site-nav__list a[aria-current="page"] {
	color: var(--purple);
	text-decoration: none;
}

.site-nav__toggle {
	display: none;
	background: none;
	border: 0;
	cursor: pointer;
	width: 44px;
	height: 44px;
	position: relative;
}

.site-nav__bars,
.site-nav__bars::before,
.site-nav__bars::after {
	content: "";
	position: absolute;
	left: 10px;
	width: 24px;
	height: 2px;
	background: var(--heading);
	transition: transform 0.2s ease;
}

.site-nav__bars {
	top: 21px;
}

.site-nav__bars::before {
	top: -7px;
}

.site-nav__bars::after {
	top: 7px;
}

/* ============================================================= *
 * Dark header variant
 * ============================================================= *
 * The home page runs its own ground colour up behind the nav bar, so the
 * header has to invert. Every other page keeps the light bar, which is why
 * this is a modifier rather than a change to .site-header: kybix_header_skin()
 * decides which one the page gets, and kybix_logo() is handed the matching
 * lockup at the same time.
 */
.site-header--dark {
	background: var(--ink);
	backdrop-filter: none;
	border-bottom-color: transparent;
}

.site-header--dark .site-nav__list a {
	color: #e2e0e4;
}

.site-header--dark .site-nav__list a:hover,
.site-header--dark .site-nav__list .current-menu-item > a,
.site-header--dark .site-nav__list a[aria-current="page"] {
	color: #9064f7;
}

.site-header--dark .btn--contact {
	background: #fff;
	color: var(--navy);
}

.site-header--dark .site-nav__bars,
.site-header--dark .site-nav__bars::before,
.site-header--dark .site-nav__bars::after {
	background: #fff;
}

/*
 * The bar collapses to the toggle at 960, not at 720 where the rest of the
 * site's narrow rules live. Seven nav items plus the lockup and the Contact
 * button stop fitting on one line somewhere around 900px, and a row that wraps
 * to two lines makes the sticky bar taller than the space every hero leaves it.
 * Everything below is the drawer the toggle opens.
 */
@media (max-width: 960px) {
	.site-nav__toggle {
		display: block;
	}

	.site-nav__list {
		position: absolute;
		top: 76px;
		left: 0;
		right: 0;
		flex-direction: column;
		align-items: flex-start;
		gap: 0;
		background: #fff;
		border-bottom: 1px solid var(--border);
		padding: 0.5rem 24px 1rem;
		display: none;
	}

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

	.site-header--dark .site-nav__list {
		background: var(--ink);
		border-bottom-color: rgba(255, 255, 255, 0.16);
	}

	.site-header--dark .site-nav__list li {
		border-bottom-color: rgba(255, 255, 255, 0.12);
	}

	.site-nav__list li {
		width: 100%;
		padding: 0.6rem 0;
		border-bottom: 1px solid var(--border);
	}
}

/* ============================================================= *
 * Hero
 * ============================================================= *
 * The hero is one flat #10041f field with two things on it: the node-mesh
 * artwork, and the icon network drawn over it. .hero__net is the box both are
 * registered to -- it carries the design's own 750x710 aspect ratio, so every
 * node position and the line overlay can be expressed as a percentage of it
 * and stay in step at any width. The mesh deliberately overhangs that box to
 * the left, running behind the headline the way the design does; .hero's
 * overflow: hidden is what stops it reaching the page edge.
 */
.hero {
	position: relative;
	overflow: hidden;
	background: var(--ink);
	color: var(--ink-text);
}

.hero__inner {
	position: relative;
	z-index: 1;
	display: grid;
	grid-template-columns: 1fr 1fr;
	align-items: center;
	gap: 2rem;
	padding-top: 104px;
	padding-bottom: 32px;
}

.hero__title {
	font-size: clamp(2.4rem, 5.2vw, 4.2rem);
	font-weight: 400;
	color: var(--ink-text);
	margin-bottom: 1.75rem;
}

.hero .accent {
	color: var(--purple-flat);
}

/*
 * The first headline is coloured in three runs rather than as accents on a
 * plain line: "The trusted platform" and "KYB information exchange" in
 * #9064f7, the phrase between them in white. Its own classes rather than
 * .accent, because the second slide's "Share securely." still takes the
 * shared accent purple.
 */
.hero-title__lit {
	color: #9064f7;
}

.hero-title__plain {
	color: #fff;
}

/*
 * The hero leads are set justified -- the one place on the site that is, and a
 * deliberate exception to the ragged-right everything else uses. They are the
 * only paragraphs that sit alone in a measured column beside a display
 * headline, where a ragged edge reads as untidy against the artwork next to it.
 *
 * text-wrap: pretty is what keeps that safe. Justification alone can leave a
 * last line of one or two words and open rivers through a short paragraph;
 * pretty tightens the last lines and is ignored by engines that do not have it,
 * which fall back to plain justification rather than to anything broken. No
 * hyphenation: breaking "infrastructure" across a line in a hero is worse than
 * the gaps it would close.
 *
 * Only on wide screens. Below 1200px the heroes either stack -- the lead is no
 * longer beside anything, so the reason for justifying it is gone -- or squeeze
 * their copy column, and a narrow justified column with no hyphenation opens
 * gaps between the words that read as a fault. Ragged-right is what they fall
 * back to, like everything else on the site.
 */
.hero__lead,
.platform-hero__lead,
.about-hero__lead,
.solutions-hero__lead,
.why-hero__lead {
	text-wrap: pretty;
}

@media (min-width: 1200px) {
	.hero__lead,
	.platform-hero__lead,
	.about-hero__lead,
	.solutions-hero__lead,
	.why-hero__lead {
		text-align: justify;
	}
}

.hero__lead {
	font-size: 1.125rem;
	line-height: 1.95;
	color: var(--ink-text);
	max-width: 41rem;
	margin-bottom: 2.5rem;
}

.hero__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 1rem;
}

/* ---- The rotating headline and lead ------------------------------------ *
 * The hero holds three slides of copy, and both blocks carry every variant of
 * their own text at once. The variants are stacked in one grid cell rather
 * than swapped in and out, which is what reserves the height: a grid track is
 * as tall as its tallest item, so the block is sized for the longest variant
 * from the first paint and nothing under it moves when the text changes -- the
 * buttons in particular, which are targets and must not shift under a cursor,
 * and the page below them, which would otherwise shift every few seconds.
 *
 * The trade is slack on the shorter slides, and how much of it there is comes
 * down to the width of the copy column: at the container's full width both
 * headlines are three lines and there is none, and the column has to narrow to
 * about 600px before the long one takes a fourth. align-items splits whatever
 * is left above and below rather than leaving it all underneath, which turns a
 * hole between the headline and the lead into wider spacing around both -- and
 * gives the two variants of a block the same axis to turn about, since the flip
 * is about each item's own middle.
 *
 * Only .is-active is visible, and it is set in the markup, so with the script
 * absent or under prefers-reduced-motion the first slide is simply the copy.
 * The flip itself is not here: assets/js/hero-rotate.js animates the transform
 * these items are given perspective for.
 * ------------------------------------------------------------------------ */
.hero-rotate {
	display: grid;
	align-items: center;
	perspective: 1200px;
}

.hero-rotate__item {
	grid-area: 1 / 1;
	visibility: hidden;
	opacity: 0;
	backface-visibility: hidden;
}

.hero-rotate__item.is-active {
	visibility: visible;
	opacity: 1;
}

/* ---- The icon network ------------------------------------------------- */
.hero__net {
	position: relative;
	width: 100%;
	max-width: 750px;
	aspect-ratio: 750 / 710;
	margin-left: auto;
}

/*
 * The mesh is a still image, not a redraw: it is the artwork's own dense end,
 * trimmed to its content so it can be placed by percentage. Its left overhang
 * is the point -- the trail has to reach back behind the headline, which is
 * also why it is z-index: -1. The file is a JPEG painted on the hero's own
 * ground rather than a PNG with an alpha channel (a tenth of the bytes), so it
 * is opaque, and anything it overlaps has to paint on top of it.
 *
 * This is the box, not the picture: the artwork and the canvas that lights it
 * (assets/js/hero-spark.js) both fill it, which is what keeps the light
 * registered with the dots it is lighting. The aspect ratio is the JPEG's.
 */
.hero__net-mesh {
	position: absolute;
	z-index: -1;
	left: -52%;
	top: 14%;
	width: 152%;
	max-width: none;
	aspect-ratio: 1150 / 453;
}

.hero__net-mesh img,
.hero__net-spark {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
}

/*
 * A second copy of the mesh, for narrow screens only: the same artwork and the
 * same lit canvas, behind the headline and the lead rather than beside them.
 * It is a copy rather than the one in .hero__net because that one is placed
 * against the net's box -- as are the rings, the glyphs and the line overlay,
 * all in the same 750x710 units -- so moving it out from under them to cover
 * the band would take the icon network with it, and the icon network stays
 * where it is. Off above the breakpoint; hero-spark.js attaches its observers
 * to every canvas regardless of size and starts this one when it appears.
 */
.hero__copy-mesh {
	display: none;
}

.hero__net-lines {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	overflow: visible;
}

/*
 * The rings. Empty: the glyphs are siblings, not children, because they travel
 * between rings -- see .hero-glyph below and assets/js/hero-net.js.
 */
.hero-node {
	position: absolute;
	transform: translate(-50%, -50%);
	aspect-ratio: 1;
	border-radius: 50%;
	background: rgba(119, 71, 232, 0.2);
	box-shadow: 0 0 34px rgba(119, 71, 232, 0.3);
}

/*
 * TWO rings, drawn inside the plate rather than on its rim, and that is what
 * the design draws -- a single ring reads as a plainer node than the artwork.
 * Measured off it as ring-pixel density per radius, the pair sits at 0.65 and
 * 0.75 of the plate's radius (people 0.64/0.76 on an 88px plate, doc 0.71/0.74
 * on 113, shield 0.695/0.735 on 100), which is an inset of 17.5% and 12.5% of
 * the diameter. The inner one is the stronger of the two in every profile, so
 * it carries the weight and the outer is thinner and dimmer. The plate under
 * both is translucent purple over the hero's ground, not the white wash it was.
 */
.hero-node::before,
.hero-node::after {
	content: "";
	position: absolute;
	border-radius: 50%;
}

/*
 * The inner ring is the only part of a node that moves. hero-net.js sets
 * --ring-scale on the node as a glyph lands in it, and it is read here rather
 * than applied to the node itself so that the outer ring and the glow, which
 * belong to the element, are left out of it.
 */
.hero-node::before {
	inset: 17.5%;
	border: 2px solid rgba(119, 71, 232, 0.9);
	transform: scale(var(--ring-scale, 1));
}

.hero-node::after {
	inset: 12.5%;
	border: 1px solid rgba(119, 71, 232, 0.55);
}

/*
 * The hub is the exception: its ring measures at 0.93 of the radius, so it
 * reads as sitting just inside the rim rather than well in from it, and the rim
 * itself is the one place a border survives.
 */
.hero-node--hub {
	left: 62.2%;
	top: 33.38%;
	width: 28%;
	background: rgba(255, 255, 255, 0.06);
	border: 2px solid rgba(119, 71, 232, 0.9);
	box-shadow: 0 0 60px rgba(119, 71, 232, 0.35), inset 0 0 40px rgba(119, 71, 232, 0.12);
}

/*
 * The hub already reads as a pair -- its own rim above and this ring just
 * inside it -- so it takes no third circle.
 */
.hero-node--hub::before {
	inset: 3.5%;
	border-width: 1px;
}

.hero-node--hub::after {
	content: none;
}

.hero-node--people {
	left: 19.13%;
	top: 13.38%;
	width: 9.33%;
}

.hero-node--shield {
	left: 90.33%;
	top: 9.01%;
	width: 13.33%;
}

.hero-node--doc {
	left: 30.73%;
	top: 57.04%;
	width: 16%;
}

.hero-node--folder-r {
	left: 89.93%;
	top: 59.15%;
	width: 9.33%;
}

.hero-node--cloud {
	left: 79.93%;
	top: 73.38%;
	width: 16%;
}

.hero-node--folder-b {
	left: 9%;
	top: 91.13%;
	width: 13.33%;
}

/*
 * The glyphs, parked in their home rings. Each width is that glyph's share of
 * its ring -- the fraction the design draws it at -- resolved against the box
 * rather than the ring, since a travelling glyph has no ring to measure
 * against. hero-net.js takes over left/top/width/transform once it runs; these
 * rules are the resting state, and the whole of it if the script never does.
 */
.hero-glyph {
	position: absolute;
	transform: translate(-50%, -50%);
	transform-origin: 0 0;
	transition: opacity 0.45s ease;
	will-change: transform;
}

.hero-glyph--hub {
	left: 62.2%;
	top: 33.38%;
	width: 10.08%;
}

.hero-glyph--people {
	left: 19.13%;
	top: 13.38%;
	width: 4.2%;
}

.hero-glyph--shield {
	left: 90.33%;
	top: 9.01%;
	width: 5.33%;
}

.hero-glyph--doc {
	left: 30.73%;
	top: 57.04%;
	width: 5.44%;
}

.hero-glyph--folder-r {
	left: 89.93%;
	top: 59.15%;
	width: 3.83%;
}

.hero-glyph--cloud {
	left: 79.93%;
	top: 73.38%;
	width: 7.2%;
}

.hero-glyph--folder-b {
	left: 9%;
	top: 91.13%;
	width: 5.47%;
}

/* The hub's K stands aside while a visiting glyph is in the hub ring. */
.hero-glyph--yield {
	opacity: 0.12;
}

/* ============================================================= *
 * Backers strip
 * ============================================================= *
 * Sits on the hero's ground rather than a band of its own: in the design the
 * dark field runs unbroken from the nav bar to the first light section, so
 * .backers only contributes its own padding.
 */
.backers {
	background: var(--ink);
	color: var(--ink-text);
	padding: 80px 0 80px;
}

.backers__inner {
	display: flex;
	align-items: flex-start;
	justify-content: center;
	gap: 46px;
}

.backers__logo {
	flex: none;
	width: 102px;
	height: 102px;
	border-radius: 14px;
}

.backers__copy {
	max-width: 62rem;
}

.backers__copy p {
	margin: 0 0 1.5rem;
	line-height: 1.75;
}

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

.backers__copy strong {
	font-weight: 700;
}

/* ============================================================= *
 * Split sections (copy + media)
 * ============================================================= */
.split__inner {
	display: grid;
	grid-template-columns: 1fr 1fr;
	align-items: center;
	gap: 4rem;
}

/*
 * Even halves and the design's own gutter. These two sections used to give the
 * artwork the wider track and a tighter gutter, because at the old 1200px
 * container the design's ratio rendered the diagrams around 80% of their drawn
 * size -- small enough to start losing their labels. The container is the
 * design's now, so the compensation has gone with it.
 *
 * This section's closing line sits under the diagram rather than at the foot of
 * the copy, which makes the media column a stack -- and both columns start at
 * the top, because centring one that has copy in it pushes the diagram back
 * down.
 */
.split--reverse .split__inner {
	align-items: start;
}

.split--reverse .split__media {
	order: -1;
}

.split__copy .section__title {
	margin-bottom: 1.5rem;
}

.split__copy p {
	color: var(--text-muted);
	font-size: 1rem;
	line-height: 1.75;
}

.split__media img {
	width: 100%;
	border-radius: var(--radius);
}

/* Copy that has followed the diagram into the media column, held to the same
   measure the copy column uses. */
.split__media .strong {
	margin: 2rem 0 0;
	font-size: 1rem;
	line-height: 1.75;
}

/* ============================================================= *
 * Animated diagrams
 * ============================================================= *
 * Both diagrams in the split sections are flat exports, and both are animated
 * the same way: a second, inline SVG laid over the <img> in front-page.php,
 * sharing the export's own viewBox so every coordinate in it is the export's
 * own. That is the whole of the registration -- the overlay stretches with the
 * image and the two cannot drift apart. The image must be display:block, or
 * the inline baseline gap under it makes this box taller than the artwork and
 * everything in the overlay rides a few pixels high.
 *
 * The movement is declarative (SMIL) in both, so neither needs a script or a
 * rAF loop, and both are built on the same two rules: a path an overlay draws
 * along is the export's own line trimmed to where the artwork actually stops,
 * because these exports run their connectors on underneath the nodes they end
 * at; and nothing is ever seen to start or stop in mid-air, so everything
 * fades in off a rim and out at the next one.
 * ============================================================= */
.diagram {
	position: relative;
	margin-top: -50px;
}

.diagram-text {
	margin-top: -25px;
	text-align: center;
	font-weight: bold;
}

.kyb__slowed {
	margin-top:50px;
}

.diagram > img {
	display: block;
}

.diagram__anim {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	pointer-events: none;
}

/* Decorative, and both diagrams read the same without them. */
@media (prefers-reduced-motion: reduce) {
	.diagram__anim {
		display: none;
	}
}

/* ============================================================= *
 * "Not starting from scratch"
 * ============================================================= *
 * The heading spans the section, and under it two independent stacks, not
 * halves -- 765:597, the design's own ratio -- on the same dark wave the "new
 * exchange layer" section runs on. The photograph is the left, wider column;
 * the two commentary cards are the right.
 *
 * The photo column is sized by min-height rather than by the artwork's aspect
 * ratio, because the caption cards are laid out inside the box against its
 * bottom edge. That height has to leave the photograph something to show: the
 * captions eat about 300px of it, so a shorter box turns the picture into a
 * strip. Add a fourth caption and this needs raising again.
 */
.scratch {
	position: relative;
	overflow: hidden;
	background-color: var(--wave-ground);
	background-image: url("assets/img/wave-purple.jpg");
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
	color: #fff;
}

/*
 * The purple tint over the artwork. It used to be a second background layer,
 * but the .wave-bg canvas paints over the host's background entirely, so it
 * has to stack above the canvas instead. With no JS this composites exactly as
 * the two-layer background did.
 */
.scratch::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	background-image: linear-gradient(120deg, rgba(78, 20, 192, 0.3), rgba(29, 8, 55, 0));
	pointer-events: none;
}

.scratch > .container {
	position: relative;
	z-index: 2;
}

.scratch__inner {
	display: grid;
	grid-template-columns: 600fr 597fr;
	align-items: stretch;
	gap: 72px;
}

.scratch__title {
	color: #fff;
	margin-bottom: 3rem;
}

.scratch .accent {
	color: var(--purple-bright);
}

/*
 * Centred against the photo column rather than top-aligned: the grid stretches
 * both columns to the taller one, which is the photograph, and two cards left
 * hanging at the top of a column half again their height read as unfinished.
 */
.scratch__cards {
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 18px;
}

.scratch-card {
	padding: 30px 48px;
	border-radius: 10px;
	background: linear-gradient(90deg, #3b1981, #502ca0);
	border: 1px solid rgba(139, 101, 232, 0.55);
}

.scratch-card p {
	margin: 0;
	color: rgba(255, 255, 255, 0.92);
	font-size: 1rem;
	line-height: 1.75;
}

.scratch-card strong {
	color: #fff;
	font-weight: 700;
}

/*
 * Stretched to the copy column's height, as in the design, rather than to the
 * photograph's own aspect ratio. The photograph is the box's backdrop and the
 * captions are laid out inside it against the bottom edge, so a caption that
 * runs long makes the box taller instead of spilling out of it.
 */
.scratch__media {
	position: relative;
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	min-height: 460px;
	padding: 20px 20px 48px;
	border-radius: var(--radius);
	overflow: hidden;
}

/* Direct child only -- the caption inside carries its own <img>. */
.scratch__media > img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.scratch__notes {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: 23px;
}

.scratch-note {
	display: flex;
	align-items: flex-start;
	gap: 16px;
	padding: 14px 18px;
	border-radius: 10px;
	background: rgba(255, 255, 255, 0.1);
	border: 1px solid rgba(255, 255, 255, 0.16);
	-webkit-backdrop-filter: blur(32px);
	backdrop-filter: blur(32px);
}

.scratch-note--stepped {
	margin-left: 17px;
}

.scratch-note img {
	flex: none;
	width: 52px;
	height: 52px;
	border-radius: 8px;
}

.scratch-note p {
	margin: 0;
	color: #fff;
	font-size: 1rem;
	line-height: 1.35;
}

.scratch-note strong {
	font-weight: 700;
}

/* ============================================================= *
 * Feature cards (2x2)
 * ============================================================= */
.feature-cards {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 44px;
	margin-top: 3.5rem;
}

.feature-card {
	display: grid;
	grid-template-columns: 36% 1fr;
	min-height: 316px;
	overflow: hidden;
	border-radius: var(--radius);
	background: #f7f8fa;
	box-shadow: 0 2px 6px -2px rgba(27, 18, 51, 0.12);
}

.feature-card__media {
	position: relative;
}

.feature-card__media img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/*
 * The two product screenshots are laid in as a background rather than covered
 * by an <img>, because covering scales their UI past legibility: at the strip's
 * width "cover" would blow a 1765px dashboard up to fill 199px of it. Sized to
 * the box's height and anchored top-left instead, the screenshot reads at
 * roughly the scale the design draws it at, and the card's own grey shows
 * wherever it does not reach. The template sets background-image per card.
 */
.feature-card__media--shot {
	background-size: auto 100%;
	background-position: left top;
	background-repeat: no-repeat;
}

/*
 * The grey veil the design draws over the two screenshots, solved off it rather
 * than chosen: on the "maintain" card the dark purple panel (44,23,71) comes
 * out at (68,53,97) and white (255,255,255) at (234,235,239), and one overlay
 * satisfies both -- rgba(157,163,185) at 0.22. The "reduce" card carries it
 * harder (its white lands at 223,226,230, implying 0.33), so 0.28 sits between
 * the two. Without it a bright white UI shot reads as a hole punched in the
 * card's grey rather than as part of the card.
 */
.feature-card__media--shot::after {
	content: "";
	position: absolute;
	inset: 0;
	background: rgba(157, 163, 185, 0.28);
}

.feature-card__body {
	align-self: center;
	padding: 1.5rem 1.6rem;
}

.feature-card__body h3 {
	font-size: 1.05rem;
	color: #101828;
	margin-bottom: 0.75rem;
}

.feature-card__body p {
	margin: 0;
	color: var(--text-muted);
	font-size: 1rem;
	line-height: 1.5;
}

/* ============================================================= *
 * Audience cards ("By organisation type" / "By team")
 * ============================================================= *
 * Three to a row, and the rows that do not fill are centred -- seven cards
 * become 3 + 3 + 1, five become 3 + 2. That is why this is a wrapping flex
 * row with an explicit track width rather than a grid: a grid would leave the
 * orphans hanging on the left.
 */
.audience {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 17px;
	margin-top: 1.75rem;
}

.audience__head {
	margin: 3.5rem 0 0;
	font-family: var(--font-head);
	font-weight: 500;
	font-size: 1.3rem;
	color: var(--heading);
	text-align: center;
}

.audience-card {
	flex: 0 1 calc((100% - 34px) / 3);
	display: flex;
	align-items: flex-start;
	gap: 16px;
	padding: 24px;
	border-radius: var(--radius-sm);
	background: rgba(255, 255, 255, 0.85);
	box-shadow: 0 6px 18px -12px rgba(27, 18, 51, 0.35);
}

/* The node glyph is the team card's, at its own 12x63: it must not stretch to
   the card's height or squash when the copy runs long. */
.audience-card__node {
	flex: none;
	align-self: flex-start;
	width: 12px;
	height: auto;
}

.audience-card h4 {
	font-family: var(--font-head);
	font-size: 1.05rem;
	font-weight: 700;
	line-height: 1.15;
	letter-spacing: -0.02em;
	color: #101828;
	margin: 0 0 0.4rem;
}

.audience-card p {
	margin: 0;
	color: var(--text-muted);
	font-size: 0.94rem;
	line-height: 1.5;
}

/* ============================================================= *
 * Team page
 * ============================================================= *
 * A plain two-track grid, so the two cards on a row share a height: the taller
 * one sizes the row and the shorter one stretches to match it.
 *
 * This replaced a masonry layout — two column stacks, each card butted up under
 * the one above it, which is what the design draws and what left row-mates
 * ending at different heights. The rows were asked to line up instead. Cards
 * are direct children of the grid now and render in source order, which puts
 * exactly the same people in each column as the old alternating deal did; only
 * the heights changed.
 *
 * The cost of the decision is slack under the shorter card of a row, and the
 * bios are uneven enough for that to be visible. Trimming a long bio is what
 * closes a gap; nothing in the CSS can.
 */

/* Wider than .center-narrow so the heading breaks after "handle", as designed. */
.team-page__head {
	max-width: 1020px;
	margin: 0 auto;
	text-align: center;
}

.team-page__title {
	font-size: clamp(2.2rem, 4.4vw, 4rem);
	font-weight: 400;
}

/* No align-items: the default stretch is what equalises a row. */
.team-cards {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 1.7rem;
	margin-top: 4rem;
}

.team-card {
	padding: 2rem;
	border-radius: var(--radius);
	background: #fff;
	box-shadow: 0 18px 34px -28px rgba(27, 18, 51, 0.55);
}

.team-card__head {
	display: flex;
	align-items: center;
	gap: 1.25rem;
	margin-bottom: 1.75rem;
}

/* The ring is a gradient in the design, so it is a 2px band of background
   showing through the padding rather than a border, which can only be flat. */
.team-card__avatar {
	flex: none;
	display: block;
	width: 92px;
	height: 92px;
	padding: 2px;
	border-radius: 50%;
	background: linear-gradient(135deg, #2f1458 10%, #6a30d1 75%);
}

.team-card__avatar img {
	width: 100%;
	height: 100%;
	border-radius: 50%;
	object-fit: cover;
	/* Every portrait ships opaque, on its own studio ground; this is only what
	   shows in the ring if one of them fails to load. */
	background: #eceef1;
}

.team-card__node {
	flex: none;
	width: 12px;
	height: auto;
	margin-right: -0.75rem;
}

.team-card__id {
	min-width: 0;
}

.team-card__name {
	display: inline-block;
	padding: 0.3rem 0.85rem;
	border-radius: 999px;
	background: #f1eeff;
	font-size: clamp(1.15rem, 1.9vw, 1.45rem);
	font-weight: 700;
	color: #101828;
	letter-spacing: -0.01em;
}

.team-card__role {
	margin: 0.35rem 0 0;
	padding-left: 0.85rem;
	font-family: var(--font-head);
	font-weight: 600;
	font-size: 1rem;
	color: var(--purple-bright);
}

.team-card__bio p {
	font-size: 0.95rem;
	line-height: 1.62;
}

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

/* ============================================================= *
 * Dark "new exchange layer" section
 * ============================================================= */
.section--dark {
	background-color: var(--navy);
	background-image: linear-gradient(120deg, rgba(78, 20, 192, 0.35), rgba(29, 8, 55, 0.1));
	color: rgba(255, 255, 255, 0.78);
}

.exchange-layer {
	position: relative;
	overflow: hidden;
	background-color: var(--wave-ground);
	background-image: url("assets/img/wave-purple.jpg");
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
}

/* Same trade as .scratch::after: the tint stacks above the canvas because the
   canvas replaces the host's background rather than sitting under it. */
.exchange-layer::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	background-image: linear-gradient(120deg, rgba(78, 20, 192, 0.35), rgba(29, 8, 55, 0));
	pointer-events: none;
}

.exchange-layer > .container {
	position: relative;
	z-index: 2;
}

.section--dark .split__copy p {
	color: rgba(255, 255, 255, 0.78);
}

.section__title--light {
	color: #fff;
}

.exchange-layer .accent {
	color: var(--purple-bright);
}

/*
 * The pills are sized by their own copy and centred, not stretched: four of
 * them stack in the column beside the heading and the remaining three wrap
 * across the full width underneath. Both are the same centred flex row, which
 * is what gives the design's staggered look without any per-pill offsets.
 */
.layer-pills {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 1.25rem;
}

.layer-pills--wide {
	flex-direction: row;
	flex-wrap: wrap;
	justify-content: center;
	gap: 23px;
	margin-top: 23px;
}

.layer-pill {
	display: flex;
	align-items: center;
	gap: 12px;
	max-width: 100%;
	padding: 20px 28px;
	border-radius: var(--radius-sm);
	background: linear-gradient(90deg, #3b1981, #502ca0);
	border: 1px solid rgba(139, 101, 232, 0.55);
	color: #fff;
	font-size: 1rem;
	line-height: 1.4;
}

.layer-pill svg {
	flex: none;
}

/* ============================================================= *
 * Pillars
 * ============================================================= */
.pillars {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 2.5rem;
	margin-top: 3.5rem;
	text-align: center;
}

.pillar__icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 52px;
	height: 52px;
	border-radius: 50%;
	background: linear-gradient(135deg, var(--purple-bright), var(--purple-deep));
	box-shadow: 0 0 0 6px rgba(119, 71, 232, 0.08);
	margin-bottom: 1.25rem;
}

.pillar h3 {
	font-size: 1.05rem;
	color: #101828;
	margin-bottom: 0.6rem;
}

.pillar p {
	margin: 0;
	color: var(--text-muted);
	font-size: 0.94rem;
	line-height: 1.5;
}

/* ============================================================= *
 * FAQ
 * ============================================================= *
 * Plain cards, not <details>: the design shows every answer open and draws no
 * disclosure affordance, so there is nothing for a summary to toggle.
 */
.faq {
	max-width: 900px;
	margin: 3rem auto 0;
	display: flex;
	flex-direction: column;
	gap: 1.25rem;
}

/*
 * Each item is a <details>, so the open state is the element's own and there is
 * no script here. The padding moved off the card and onto the summary and the
 * answer: a collapsed card is then the height of its question, which is the
 * whole point of collapsing them.
 */
.faq-item {
	background: #fff;
	border-radius: var(--radius-sm);
	box-shadow: 0 10px 26px -22px rgba(27, 18, 51, 0.5);
}

.faq-item__q {
	padding: 20px 24px;
	cursor: pointer;
	/*
	 * Two ways to drop the default triangle, because engines disagree on which
	 * one they take: list-style for Firefox and current Chrome, the pseudo
	 * element for older WebKit. Neither is harmful where it is not needed.
	 */
	list-style: none;
}

.faq-item__q::-webkit-details-marker {
	display: none;
}

/*
 * The heading is INSIDE the summary and carries the row, rather than sitting
 * beside the chevron. <summary> takes either phrasing content or a single
 * heading element, so a heading next to an <svg> would not be valid -- putting
 * the glyph inside the heading keeps both the outline and the markup right.
 */
.faq-item__q h3 {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	font-family: var(--font-head);
	font-weight: 600;
	font-size: 1.05rem;
	color: var(--purple-flat);
	margin: 0;
}

.faq-item__q:hover h3,
.faq-item__q:focus-visible h3 {
	color: var(--purple-deep);
}

.i-chevron {
	flex: none;
	transition: transform 0.2s ease;
}

.faq-item[open] .i-chevron {
	transform: rotate(180deg);
}

.faq-item__a {
	padding: 0 24px 24px;
}

.faq-item p {
	margin: 0;
	color: var(--text-muted);
	font-size: 0.98rem;
	line-height: 1.5;
}

.faq-item p + p {
	margin-top: 0.9rem;
}

/*
 * The answer arrives rather than appearing. Opening a <details> is not a
 * transition -- the content goes from not rendered to rendered -- so this is a
 * keyframe on the open state, which runs once each time it opens and simply
 * does not run on close.
 */
@keyframes faq-open {
	from {
		opacity: 0;
		transform: translateY(-4px);
	}
}

.faq-item[open] .faq-item__a {
	animation: faq-open 0.22s ease both;
}

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

	.faq-item[open] .faq-item__a {
		animation: none;
	}
}

/* ============================================================= *
 * Footer band (CTA + footer share one artwork)
 * ============================================================= *
 * In the design the purple swoosh runs unbroken from the top of the CTA to the
 * bottom of the page, so it belongs to the wrapper around both rather than to
 * either one — .cta and .site-footer are transparent and only contribute the
 * hairline between them.
 *
 * Figma paints the artwork at 40% over a #16072a plate. The exported PNG has
 * that 40% baked into its alpha channel, which is what made it an 834 KB
 * 1710x803 PNG; the same result comes out of a 56 KB JPEG with the plate
 * re-applied here as a 60%-opaque flat gradient over it. Keep the two in step:
 * the gradient's alpha is 1 minus the artwork's, so changing one without the
 * other shifts the whole band's tone.
 */
.footer-band {
	background-color: #16072a;
	background-image:
		linear-gradient(rgba(22, 7, 42, 0.6), rgba(22, 7, 42, 0.6)),
		url("assets/img/footer-bg.jpg");
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
}

/* ============================================================= *
 * CTA band
 * ============================================================= */
.cta {
	color: #fff;
	text-align: center;
}

/* Wide enough that the heading stays on one line as in the design; the lead
   under it is held to its own, narrower measure so it still breaks in two. */
.cta__inner {
	padding: 96px 24px;
	max-width: 1180px;
}

.cta__title {
	font-size: clamp(1.9rem, 3.6vw, 3rem);
	font-weight: 400;
	color: #fff;
	margin-bottom: 1.25rem;
}

.cta__text {
	max-width: 800px;
	margin: 0 auto 2rem;
	color: rgba(255, 255, 255, 0.8);
	font-size: 1.1rem;
}

/* ============================================================= *
 * Footer
 * ============================================================= */
.site-footer {
	border-top: 1px solid rgba(255, 255, 255, 0.16);
	color: rgba(255, 255, 255, 0.7);
	padding-top: 56px;
}

.site-footer__inner {
	display: grid;
	grid-template-columns: 1.6fr 1fr 1fr 1.4fr;
	gap: 2.5rem;
	padding-bottom: 48px;
}

.site-footer__brand .site-logo {
	height: 34px;
	margin-bottom: 1.25rem;
}

/*
 * text-wrap: balance on both of the footer's prose blocks. Each is a short
 * sentence in a narrow column, which is exactly the case that leaves its last
 * word alone on a line — "exchange" here, and whatever falls last in the Backed
 * By note. Balancing evens the lines out instead, and an engine without it
 * simply wraps as before.
 */
.site-footer__brand p {
	max-width: 18rem;
	font-size: 0.95rem;
	margin: 0;
	text-wrap: balance;
}

/*
 * The footer's one social link. The glyph is currentColor, so the anchor's own
 * colour and hover are the whole of its styling.
 */
.site-footer__social {
	display: inline-flex;
	margin-top: 1.25rem;
	color: rgba(255, 255, 255, 0.7);
}

.site-footer__social:hover {
	color: #fff;
}

.site-footer__col h3 {
	color: #fff;
	font-size: 1rem;
	margin-bottom: 1rem;
}

.site-footer__col ul {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 0.7rem;
}

.site-footer__col a {
	color: rgba(255, 255, 255, 0.7);
	font-size: 0.95rem;
}

.site-footer__col a:hover {
	color: #fff;
	text-decoration: none;
}

.site-footer__col p {
	margin: 0;
	font-size: 0.95rem;
	max-width: 16rem;
	text-wrap: balance;
}

.site-footer__bottom {
	border-top: 1px solid rgba(255, 255, 255, 0.16);
	padding-top: 28px;
	padding-bottom: 40px;
	text-align: center;
}

.site-footer__bottom p {
	margin: 0;
	font-size: 0.88rem;
	color: rgba(255, 255, 255, 0.5);
}

/*
 * The copyright line and the two legal links share a row; the company
 * disclosure runs under both, smaller, because it is there to be findable
 * rather than read.
 */
.site-footer__legal {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: center;
	gap: 0.5rem 1.75rem;
}

.site-footer__links {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 0.5rem 1.5rem;
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: 0.88rem;
}

/* "Cookie settings" is a button, since it opens something rather than going
   anywhere, but it sits among links and has to read as one. */
.site-footer__links a,
.site-footer__links button {
	padding: 0;
	border: 0;
	background: none;
	font: inherit;
	color: rgba(255, 255, 255, 0.7);
	cursor: pointer;
}

.site-footer__links a:hover,
.site-footer__links button:hover {
	color: #fff;
	text-decoration: underline;
}

.site-footer__bottom .site-footer__company {
	max-width: 56rem;
	margin: 0.9rem auto 0;
	font-size: 0.78rem;
	line-height: 1.6;
	color: rgba(255, 255, 255, 0.4);
	text-wrap: balance;
}

/* ============================================================= *
 * Contact page
 * ============================================================= *
 * Two columns that are not halves: the design gives the form more room than
 * the pitch beside it, and the tracks carry that ratio rather than a rounded
 * percentage. They are independent stacks, not rows — nothing lines up across
 * the gap, and the columns are meant to end at different heights.
 */
.contact-page {
	padding: 104px 0 120px;
}

.contact-page__title {
	font-size: clamp(2.2rem, 4.4vw, 4rem);
	font-weight: 400;
	text-align: center;
	margin-bottom: 4rem;
}

.contact-grid {
	display: grid;
	grid-template-columns: 626fr 683fr;
	align-items: start;
	gap: 24px;
}

.contact-col {
	display: flex;
	flex-direction: column;
	gap: 24px;
}

/*
 * The design's card carries a particle-wave render that exists nowhere in this
 * repo — it is not a crop of any artwork here, so this rebuilds the effect from
 * wave-purple.jpg, the same wave every dark band on the site now carries. The
 * gradient is the point of the pairing: it wipes the artwork out from the left,
 * reproducing the design's clean-left, dense-right weighting and keeping the
 * heading off the busy part. Both the plate and the gradient's opaque end are
 * the artwork's own ground, which is what stops the card banding across the
 * wipe — that colour and the artwork travel together.
 */
.contact-intro {
	padding: 32px 48px;
	border-radius: var(--radius);
	color: #fff;
	background-color: var(--wave-ground);
	background-image:
		linear-gradient(100deg, var(--wave-ground) 10%, rgba(24, 7, 49, 0.7) 40%, rgba(24, 7, 49, 0) 74%),
		url("assets/img/wave-purple.jpg");
	background-size: auto, cover;
	/*
	 * Anchored left, which is where the artwork's busy middle is: the card is
	 * tall and narrow, so "cover" scales the wave by height and crops most of
	 * its width away, and centring that crop leaves the quiet right-hand end of
	 * the artwork under the only part of the card the gradient does not wipe.
	 */
	background-position: center, left center;
	background-repeat: no-repeat;
	overflow: hidden;
}

.contact-intro__title {
	font-size: clamp(1.9rem, 3.2vw, 3rem);
	font-weight: 400;
	line-height: 1.125;
	color: #fff;
	margin-bottom: 40px;
}

.contact-intro .accent {
	color: var(--purple-bright);
}

/*
 * "trust-led" opens the headline's second line and holds together there: the
 * break before it is in the markup, and nowrap stops the hyphen being taken as
 * a place to break it in two. Its own class and colour rather than .accent's.
 */
.contact-intro__trust {
	color: #9064f7;
	white-space: nowrap;
}

.contact-intro p {
	margin: 0 0 1rem;
	line-height: 1.75;
}

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

/* The node glyph is the team card's, at its own 12x63: a flex item that must
   not stretch to the card's height or squash when the text runs long. */
.contact-note {
	display: flex;
	gap: 16px;
	padding: 32px;
	border-radius: var(--radius);
	background: #fff;
	box-shadow: 0 6px 18px -6px rgba(27, 18, 51, 0.22);
}

.contact-note__node,
.contact-card__node {
	flex: none;
	align-self: flex-start;
	width: 12px;
	height: auto;
}

.contact-note p {
	margin: 0;
	color: var(--navy);
	line-height: 1.75;
}

/* ---- The form card ---------------------------------------------------- */
.contact-card {
	padding: 32px;
	border-radius: var(--radius);
	background: #fff;
	box-shadow: 0 6px 18px -6px rgba(27, 18, 51, 0.22);
}

.contact-card__head {
	display: flex;
	align-items: flex-start;
	gap: 24px;
}

.contact-card__title {
	font-size: 1.5rem;
	font-weight: 400;
	color: #101828;
}

.contact-card__lead {
	margin: 0.4rem 0 0;
	color: var(--navy);
}

/*
 * The form is inset inside the card's padding, as designed: the fields stop
 * short of the heading's left edge on both sides.
 */
.contact-form {
	margin: 48px 16px 0;
}

.contact-form__row + .contact-form__row {
	margin-top: 32px;
}

.contact-form__row--split {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 16px;
}

/*
 * Off-screen rather than display:none, because a bot reading the markup will
 * skip a field it can see is hidden — and a person never reaches this one, so
 * anything in it came from something that filled the form without looking.
 */
.contact-form__bait {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

.contact-field {
	margin: 0;
}

.contact-field__input {
	width: 100%;
	padding: 10px 16px;
	border: 0;
	border-radius: 10px;
	background: var(--bg-light);
	color: var(--heading);
	font-family: inherit;
	font-size: 14px;
	line-height: 18px;
}

.contact-field__input::placeholder {
	color: var(--text-muted);
	opacity: 1;
}

.contact-field__input:focus-visible {
	outline: 2px solid var(--purple);
	outline-offset: 2px;
}

.contact-field__input--area {
	height: 200px;
	padding: 12px 16px;
	resize: vertical;
}

/*
 * The select has to read as one of the same grey pills, so the platform widget
 * is taken off it and the chevron drawn back on as a background image. Its
 * height then comes from the same padding/line-height the inputs use, which is
 * what keeps the two field kinds the same 38px.
 */
.contact-field__input--select {
	appearance: none;
	-webkit-appearance: none;
	padding-right: 40px;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5 6 6.5l5-5' stroke='%236b7280' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 16px center;
	cursor: pointer;
}

/* Grey the placeholder option the way a real placeholder is greyed. Without
   :has() support the text simply shows in the normal colour. */
.contact-field__input--select:has(option[value=""]:checked) {
	color: var(--text-muted);
}

.contact-field--error .contact-field__input {
	box-shadow: inset 0 0 0 1px #c0392b;
}

.contact-field__error {
	display: block;
	margin-top: 6px;
	padding-left: 16px;
	font-size: 0.82rem;
	color: #c0392b;
}

.contact-form__actions {
	margin-top: 32px;
}

/* The processing notice, and reCAPTCHA's required attribution. Quiet, but
   present — the HubSpot consent record is a record of this being shown. */
.contact-form__consent {
	margin: 20px 0 0;
	font-size: 0.78rem;
	line-height: 1.5;
	color: var(--text-muted);
}

.contact-form__consent + .contact-form__consent {
	margin-top: 8px;
}

.contact-form__consent a {
	color: inherit;
	text-decoration: underline;
}

/*
 * reCAPTCHA v3 floats a badge over the bottom-right of the page. Google allows
 * hiding it *only* if the attribution it carries is shown instead, which is the
 * second .contact-form__consent line — remove that and this rule stops being
 * allowed. visibility, not display: the widget still has to lay out for the
 * token exchange to work.
 */
.grecaptcha-badge {
	visibility: hidden;
}

/* Flat, unlike the site's gradient buttons — the design draws it as one tone,
   and smaller than the site's standard button: 101x40 rather than ~110x43. */
.contact-form__submit {
	background: #7747e8;
	color: #fff;
	padding: 0.7rem 1.5rem;
}

.contact-notice {
	margin: 24px 0 0;
	padding: 12px 16px;
	border-radius: 10px;
	font-size: 0.95rem;
}

.contact-notice--ok {
	background: #e7f7f4;
	color: var(--teal-dark);
}

.contact-notice--error {
	background: #fdeceb;
	color: #a5342a;
}

/* ============================================================= *
 * Our Platform page
 * ============================================================= *
 * Seven bands, alternating light and dark, each one a centred heading over a
 * row of cards. Three things about it are worth knowing before editing.
 *
 * The design draws three different content widths, and they are deliberate
 * rather than drift: the full container (1470) for the eight feature cards and
 * the trust row, 1342 for the two dark bands and the foundation split, and
 * 1200 for the pair of notes under the first heading. Each is named below.
 *
 * The two dark bands lay 2, 3 and 4 cards across on the same column rhythm --
 * their widths in the export are 659, 431.33 and 317.5 with a 24px gutter,
 * which is exactly 6, 4 and 3 columns of a twelve-column grid with that
 * gutter. .platform-grid is that grid and each card carries its own --span, so
 * both bands are one rule and the arrays in page-platform.php decide the shape.
 *
 * The headings here track at their natural width rather than the theme's
 * -0.02em, which is what this export draws. It is scoped to the page because
 * the older exports the other pages follow do not show it.
 *
 * Weight is deliberately NOT scoped: every display title on the site is 400,
 * including this page's, even though the export draws these at 600 (measured
 * off the stem widths, not guessed). That is a site-wide decision about how
 * headings should read, taken after the page was built against the export.
 */
.page-platform .section__title,
.platform-hero__title {
	letter-spacing: 0;
}

/* ---- Hero -------------------------------------------------------------- *
 * One photograph across the whole band, copy on the clear left half of it.
 * The image is the band's only background: it carries its own soft gradient,
 * so there is nothing stacked over it and nothing for the copy to sit on
 * beyond the artwork's own light end.
 */
.platform-hero {
	position: relative;
	background-color: #eef1f4;
	background-image: url("assets/img/platform-hero.jpg");
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
}

.platform-hero__inner {
	padding-top: 104px;
	padding-bottom: 186px;
}

/*
 * The copy is capped, not the container: the column has to start on the
 * container's own left edge, which is the line every other section starts on.
 * The two caps are the widths that give the design's own line breaks -- wide
 * enough for "An AI-enabled platform" to hold a line of its own, narrow enough
 * that "for" cannot join it, and tighter still on the lead, where one word
 * either way changes where all three of its lines break.
 */
.platform-hero__title {
	max-width: 740px;
	font-size: clamp(2.4rem, 4.7vw, 4rem);
	line-height: 1.17;
	/* Explicit: an h1 with no weight of its own falls to the UA's bold. */
	font-weight: 400;
	color: var(--navy);
	margin-bottom: 44px;
}

.platform-hero__title .accent {
	color: var(--purple-flat);
}

.platform-hero__lead {
	max-width: 684px;
	margin-bottom: 48px;
	color: #50545c;
	font-size: 18px;
	line-height: 32px;
}

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

.platform-hero__lead p + p {
	margin-top: 24px;
}

/* ---- Shared section furniture ------------------------------------------ */
.page-platform .section {
	padding: 120px 0;
}

/*
 * Wider than .center-narrow's own 1230, which is the home page's measure and
 * stays that way — this only lifts the cap on the five headings of this page.
 *
 * "One trusted platform for the information KYB depends on" wants 1235px on one
 * line and was losing "on" to a second line by five pixels. The other four
 * headings here are already single-line and the widest of them needs 1202, so
 * nothing else's break moves; the headroom above 1235 is for the fact that this
 * display face renders a few per cent narrower than the export drew it, and a
 * browser measuring it slightly wider should not put the word back.
 *
 * The lead under a heading is unaffected — .page-platform .center-narrow > p
 * caps it at 1010 on its own, below.
 */
.platform-head {
	max-width: 1280px;
	margin-bottom: 80px;
}

/*
 * Wider than .center-narrow's own measure, which was cut for the home page's
 * shorter leads: this export runs its centred copy to 991px before it breaks.
 */
.page-platform .center-narrow > p {
	max-width: 1010px;
	color: #50545c;
	font-size: 15px;
	line-height: 28px;
}

.platform-head > p:last-child {
	margin-bottom: 0;
}

/* The heading's own gap to the lead under it, wider than .center-narrow's. */
.platform-head .section__title {
	margin-bottom: 44px;
}

/* ---- "One exchange layer": the pair of notes ---------------------------- */
.platform-notes {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 32px;
	max-width: 1200px;
	margin-inline: auto;
}

.platform-note {
	display: flex;
	align-items: flex-start;
	gap: 17px;
	padding: 32px;
	background: #fff;
	border-radius: 14px;
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
}

/* The Team page's node glyph, at its own 12x63. */
.platform-note__node {
	flex: none;
	width: 12px;
	height: 63px;
}

.platform-note p {
	margin: 0;
	color: #50545c;
	font-size: 15px;
	line-height: 24px;
}

/* ---- The two dark bands ------------------------------------------------ *
 * Both are the particle-wave artwork over the ground it was painted on, so
 * nothing needs a plate under it, and both carry a .wave-bg canvas like the
 * home page's dark bands do -- same treatment, same script, a different render
 * of the same artwork. The CSS copy named here is what wave-bg.js reads the
 * URL off, and the still it falls back to.
 *
 * (The hero mesh is the one artwork on the site that must not be displaced:
 * it is thin straight lines, and shifting those in bands shears them. These
 * are soft gradient curves, which is what the displacement is for.)
 */
.platform-dark {
	position: relative;
	overflow: hidden;
	background-color: var(--wave-ground);
	background-image: url("assets/img/wave-purple.jpg");
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
	color: var(--ink-text);
}

/* The canvas covers the host's own background, so the content has to stack
   above it. No tint layer here, so there is nothing between the two. */
.platform-dark > .container {
	position: relative;
	z-index: 1;
}

/* Lighter than the light bands' accent: --purple-flat reads as a shadow here. */
.platform-dark .accent {
	color: #9064f7;
}

.page-platform .platform-dark .center-narrow > p {
	color: var(--ink-text);
}

/*
 * Twelve columns on the design's 24px gutter, which is what makes 3, 4 and 6
 * column spans land on its 317.5, 431.33 and 659px cards exactly. Change the
 * gutter and every one of those widths moves.
 */
.platform-grid {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	gap: 24px;
	max-width: 1342px;
	margin-inline: auto;
}

/*
 * The fill is translucent so the artwork behind it still shows through, which
 * is why background-clip stops it reaching under the border. The design's
 * border is itself a gradient, from #7646E7 to #9C71FF across 1.5px -- flat is
 * indistinguishable at that width, and keeps the card to one element.
 */
.platform-card {
	grid-column: span var(--span, 4);
	padding: 28px 32px 24px;
	border: 1.5px solid rgba(140, 92, 243, 0.6);
	border-radius: 10px;
	background: linear-gradient(90deg, rgba(78, 35, 178, 0.6), rgba(119, 71, 232, 0.6));
	background-clip: padding-box;
}

.platform-card h3,
.platform-feature h3,
.platform-trust-card h3 {
	font-family: var(--font-body);
	font-size: 15px;
	line-height: 22px;
	font-weight: 600;
	letter-spacing: normal;
	margin: 0;
}

.platform-card h3 {
	color: #fff;
	margin-bottom: 8px;
}

.platform-card p {
	margin: 0;
	color: var(--ink-text);
	font-size: 14px;
	line-height: 23px;
}

/* ---- "What the KYBIX platform supports" -------------------------------- */
.platform-features {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 22px;
}

/*
 * The pale ring is a 5px border rather than an outline or a shadow, because
 * the design draws it as part of the card: the gradient starts where the ring
 * ends. Its radius is the outer one, so the fill's own corner comes out 5px
 * tighter, as drawn.
 */
.platform-feature,
.platform-statement {
	padding: 32px;
	border: 5px solid #ddd7fa;
	border-radius: 21px;
	background: linear-gradient(148deg, var(--navy) 0%, var(--purple-flat) 100%);
	color: var(--ink-text);
}

.platform-feature__head {
	display: flex;
	align-items: center;
	gap: 16px;
}

.platform-feature__icon {
	flex: none;
	display: grid;
	place-items: center;
	width: 40px;
	height: 40px;
	border: 1px solid #b9a9f6;
	border-radius: 10px;
	color: #b9a9f6;
}

.platform-feature h3 {
	color: #fff;
}

.platform-feature p,
.platform-statement p {
	margin: 0;
	font-size: 14px;
	line-height: 23px;
}

.platform-feature p {
	margin-top: 18px;
}

/* ---- "An existing foundation" ------------------------------------------ *
 * Two independent stacks on the design's own 690:557 split. Nothing lines up
 * across the gap and the columns are meant to end at different heights.
 */
.platform-foundation__inner {
	display: grid;
	grid-template-columns: 0.9fr 1.1fr;
	column-gap: 80px;
	align-items: start;
	/* .container's own 24px padding either side of the design's 1347 box. */
	max-width: 1395px;
}

.platform-foundation__copy .section__title {
	margin-bottom: 46px;
}

.platform-foundation__copy p {
	color: #50545c;
	font-size: 15px;
	line-height: 28px;
}

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

.platform-foundation__copy strong {
	color: var(--navy);
	font-weight: 600;
}

.platform-foundation__cards {
	display: grid;
	gap: 22px;
	margin-top: 0px;
}

.platform-statement {
	padding: 30px 32px;
}

/* ---- "Modern technology, built for institutional trust" ----------------- *
 * A wide card of its own above a pair. The first spans both columns and is
 * held to the design's 913px, centred over the two under it.
 */
.platform-trust__cards {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 24px;
}

.platform-trust-card {
	padding: 27px 32px 22px;
	background: #fff;
	border-radius: 14px;
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
}

.platform-trust-card--lead {
	grid-column: 1 / -1;
	width: 100%;
	max-width: 913px;
	margin-inline: auto;
}

.platform-trust-card__head {
	display: flex;
	align-items: center;
	gap: 8px;
}

.platform-trust-card__head .i-check {
	flex: none;
}

.platform-trust-card h3 {
	color: var(--navy);
}

/*
 * Indented past the tick to sit under the title rather than under the icon --
 * the icon's 24px plus the row's 8px gap, so the two travel together.
 */
.platform-trust-card p {
	margin: 6px 0 0 32px;
	color: #50545c;
	font-size: 15px;
	line-height: 28px;
}

/*
 * The hero's photograph is cropped to cover, and below about 1400px that crop
 * has eaten the clear left end the copy is drawn on -- the tablet slides in
 * under the words. This is the wash that keeps them legible: the hero's own
 * ground colour, near-opaque across the copy column and thinning out over the
 * photograph. Its stops are in px rather than percentages because what it has
 * to cover is the copy, which is capped at a fixed width; a percentage would
 * drift off it at every viewport. Above the breakpoint the tablet starts to
 * the right of where the copy ends, exactly as designed, so there is no scrim
 * at all and the artwork is untouched.
 */
@media (max-width: 1400px) {
	.platform-hero::before {
		content: "";
		position: absolute;
		inset: 0;
		background: linear-gradient(
			90deg,
			rgba(231, 234, 237, 0.96) 0,
			rgba(231, 234, 237, 0.92) 660px,
			rgba(231, 234, 237, 0.45) 980px,
			rgba(231, 234, 237, 0.15) 1240px
		);
		pointer-events: none;
	}

	.platform-hero__inner {
		position: relative;
		z-index: 1;
	}
}

/* ============================================================= *
 * About page
 * ============================================================= *
 * Six bands under the light header: a hero on the page's own light grey, two
 * on the dark ground, one white, one on the light wave and one flat. Three
 * things about it are worth knowing before editing.
 *
 * The dark bands take the same accent as each other and it is NOT the theme's
 * --purple-flat. The export draws "trusted" at #7747E8 and "infrastructure" at
 * #9F75FF inside one heading; the darker of the two barely separates from
 * --navy behind it, so both are set to the lighter one here and the export is
 * treated as the inconsistency it is.
 *
 * Two of the bands are wave-animated, one light and one dark, so wave-bg.js is
 * enqueued for this page as well (see kybix_enqueue). Each names its own
 * artwork in the rule below, which is both what the script reads the URL off
 * and the still it falls back to.
 *
 * The design's own content box is 1470 wide, which --container already is, so
 * every width in here is the export's own number.
 * ============================================================= */

/* Roomier than the theme's default, matching the design's rhythm — the same
   decision .home .section makes, for the same reason. */
.page-about .section {
	padding: 104px 0;
}

/* ---- Hero -------------------------------------------------------------- *
 * One light band with the constellation artwork across the whole of it and the
 * copy beside the platform collage. The artwork is decorative and covers the
 * band, so it needs no registration with anything.
 */
.about-hero {
	position: relative;
	overflow: hidden;
	background: var(--bg-light);
}

.about-hero__net {
	position: absolute;
	inset: 0;
	z-index: 0;
	width: 100%;
	height: 100%;
	max-width: none;
	object-fit: cover;
	object-position: center;
	pointer-events: none;
}

/*
 * The canvas that replaces the still. Both fill the band and both sit under the
 * copy; only one is ever visible, and about-net.js decides which by setting
 * .is-net-live on the section once it has a frame to show.
 */
.about-net {
	position: absolute;
	inset: 0;
	z-index: 0;
	width: 100%;
	height: 100%;
	opacity: 0;
	pointer-events: none;
}

.about-hero.is-net-live .about-net {
	opacity: 1;
}

.about-hero.is-net-live .about-hero__net {
	opacity: 0;
}

/*
 * The design's 704:694 either side of a 72px gutter, as a ratio rather than a
 * fixed second column: between this page's 960px breakpoint and the width at
 * which the container stops growing, a 694px collage would take everything and
 * leave the headline a few words wide.
 */
.about-hero__inner {
	position: relative;
	z-index: 1;
	display: grid;
	grid-template-columns: minmax(0, 704fr) minmax(0, 694fr);
	align-items: center;
	gap: 72px;
	padding-top: 120px;
	padding-bottom: 190px;
}

/*
 * Capped at less than its column: the design breaks this headline after
 * "trusted" and again after "infrastructure", and at the column's full width
 * "global" joins the first line. The cap is what holds those breaks.
 */
.about-hero__title {
	max-width: 600px;
	font-family: var(--font-head);
	font-size: clamp(2.4rem, 4.6vw, 4rem);
	font-weight: 400;
	line-height: 1.17;
	margin-bottom: 44px;
}

.about-hero__lead {
	max-width: 600px;
	font-size: 17px;
	line-height: 32px;
	color: var(--text);
}

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

.about-hero__lead p + p {
	margin-top: 25px;
}

/* ---- The hero collage -------------------------------------------------- *
 * Four overlapping screenshots, not one picture, and every one of them is
 * placed as a percentage of .about-hero__stage -- which carries the design's
 * own 694x493 -- so the whole composition stays registered at any width. Two
 * things follow from that:
 *
 * - the three panels are sized by their WIDTH alone, and their height is what
 *   their image plus their padding comes to. A padding percentage resolves
 *   against the stage's width, not the panel's, which is why 15px of inset on
 *   a 132px panel is written as 2.16% of 694 rather than 11.4% of 132.
 * - laptop.png is mostly transparent margin -- its artwork is 587x426 inside a
 *   1024x1013 canvas -- so it is placed by its whole box and the stage's
 *   overflow takes the margin off. Without that clip it reaches back under the
 *   copy column. The export clips it to a rect for the same reason, and that
 *   rect takes off nothing but margin.
 *
 * The clip does the second job too: the three panels slide in from the stage's
 * own edges on load, and it is what they slide in from.
 */
.about-hero__stage {
	position: relative;
	width: 100%;
	max-width: 694px;
	aspect-ratio: 694 / 493;
	overflow: hidden;
	justify-self: end;
}

.about-hero__laptop {
	position: absolute;
	left: -22.161%;
	top: -50.831%;
	width: 131.311%;
	max-width: none;
	height: auto;
}

/* The design's own glass card: 0.625rem of radius, a half-white fill and a
   white hairline. All three panels are the same card at different sizes. */
.about-hero__panel {
	position: absolute;
	border: 1px solid rgba(255, 255, 255, 0.8);
	border-radius: 10px;
	background: rgba(255, 255, 255, 0.5);
}

.about-hero__panel img {
	display: block;
	width: 100%;
	height: auto;
}

.about-hero__panel--questionnaire {
	left: 0;
	top: 0.609%;
	width: 19.02%;
	padding: 2.161% 1.009%;
	animation: about-hero-drop 0.8s cubic-bezier(0.22, 1, 0.36, 1) 0.15s both;
}

.about-hero__panel--assistant {
	left: 73.487%;
	top: 0;
	width: 26.513%;
	padding: 0 0 2.305%;
	animation: about-hero-slide 0.8s cubic-bezier(0.22, 1, 0.36, 1) 0.3s both;
}

.about-hero__panel--actions {
	left: 31.124%;
	top: 89.452%;
	width: 55.62%;
	padding: 1.153% 2.305%;
	animation: about-hero-rise 0.7s cubic-bezier(0.22, 1, 0.36, 1) 0.5s both;
}

/*
 * Each panel arrives from the edge the design has it nearest, one after
 * another, with the laptop already in place: it is the thing the other three
 * are about, so it does not move. The distances are shares of each panel's own
 * box and are all far enough to clear the stage, so what is seen is a panel
 * coming in from off the picture rather than fading up in the middle of it.
 */
@keyframes about-hero-drop {
	from {
		transform: translateY(-130%);
	}
}

@keyframes about-hero-slide {
	from {
		transform: translateX(130%);
	}
}

@keyframes about-hero-rise {
	from {
		transform: translateY(200%);
	}
}

/* The composition is the point, not the arrival: everything sits where it
   lands with no motion at all. */
@media (prefers-reduced-motion: reduce) {
	.about-hero__panel {
		animation: none;
	}
}

/* ---- The two dark bands ------------------------------------------------ *
 * --wave-ground rather than --navy, and this is the page where it matters most:
 * the two bands are contiguous, so the first one's artwork stops against the
 * second one's flat colour with nothing between them. Every band on the site
 * that carries the wave sits on this plate for the same reason.
 */
.about-dark {
	background-color: var(--wave-ground);
	color: rgba(255, 255, 255, 0.92);
}

.about-dark .accent {
	color: #9f75ff;
}

/*
 * The particle wave behind the first dark band. Named here rather than in the
 * script, like every other wave on the site, so this rule is both the source of
 * the URL and the still that shows if wave-bg.js never runs.
 *
 * This artwork is the one the site's other dark waves are not: its wave is
 * inset and fades to a flat border, rather than running off the edge to be cut
 * against whatever is behind it. That is what lets it sit in a band without a
 * seam -- given a ground the same colour, which .about-dark above is.
 */
.about-mission {
	position: relative;
	overflow: hidden;
	background-image: url("assets/img/wave-purple.jpg");
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
}

/* No tint layer over the canvas here, so the content only has to clear it. */
.about-mission > .container {
	position: relative;
	z-index: 1;
}

/*
 * Wider than .center-narrow's own measure, which was cut for the home page's
 * shorter copy: at 980 these two paragraphs take three ragged lines where the
 * design gives each of them two. The heading above is deliberately left
 * uncapped -- this display face is lighter than the one Figma drew with, so no
 * cap exists that both fits the design's second line and keeps "financial" off
 * its first, and two lines breaking a word late is the better of the two.
 */
.about-mission__head > p {
	max-width: 840px;
}

.about-mission__head > p:last-child {
	margin-bottom: 0;
}

.about-dark .center-narrow > p {
	color: rgba(255, 255, 255, 0.92);
}

.about-mission__line {
	margin: 116px 0 0;
	text-align: center;
	font-size: 17px;
	font-weight: 600;
	color: #b9a9f6;
}

/* ---- The four stages --------------------------------------------------- *
 * Four 192px rings on the design's own 266px pitch, each holding its icon at
 * the 128px it was exported at. The dashed run between them is one line across
 * the row rather than a rule per gap, which is what puts a stub outside the
 * first and last ring the way the design draws it.
 */
.about-flow {
	position: relative;
	display: flex;
	justify-content: center;
	gap: 74px;
	list-style: none;
	margin: 68px 0 0;
	padding: 0;
}

.about-flow::before {
	content: "";
	position: absolute;
	left: 50%;
	top: 96px;
	width: 1024px;
	max-width: calc(100% + 34px);
	transform: translateX(-50%);
	border-top: 1.6px dashed rgba(167, 139, 250, 0.25);
	pointer-events: none;
}

.about-flow__stage {
	position: relative;
	z-index: 1;
	display: flex;
	flex-direction: column;
	align-items: center;
	width: 192px;
}

/*
 * The ring, and the wider halo around it drawn as a second ring on ::before.
 * Both are the design's own diameters; the halo is 262px, so it overhangs the
 * 192px stage box and the gap between stages is what it runs into.
 */
.about-flow__ring {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 192px;
	height: 192px;
	border-radius: 50%;
	background: rgba(119, 71, 232, 0.5);
	box-shadow: inset 0 0 0 1.5px rgba(167, 139, 250, 0.45);
}

.about-flow__ring::before {
	content: "";
	position: absolute;
	top: -18.2%;
	left: -18.2%;
	width: 136.5%;
	height: 136.5%;
	border-radius: 50%;
	border: 1.4px solid rgba(167, 139, 250, 0.18);
	pointer-events: none;
	animation: about-ring-pulse 6s ease-out infinite;
}

/*
 * The flow's motion: a point of light runs the dashed line left to right on a
 * 6s loop, passing under each ring, and each ring's halo swells and brightens
 * as the light reaches it. Both are on the one 6s cycle, so a ring's delay is
 * simply when the light gets there -- its centre sits at 0.105, 0.368, 0.632
 * and 0.895 of the run's travel, which is 0.63s, 2.21s, 3.79s and 5.37s, less
 * the 0.48s the pulse takes to reach its peak. The light is background-position
 * across a box laid exactly over the dashed run, so it scales with the run and
 * needs no numbers of its own. In the 2x2 layout the run is gone and so is the
 * light; the halos go on pulsing in turn.
 */
.about-flow__stage:nth-child(1) .about-flow__ring::before { animation-delay: 0.15s; }
.about-flow__stage:nth-child(2) .about-flow__ring::before { animation-delay: 1.73s; }
.about-flow__stage:nth-child(3) .about-flow__ring::before { animation-delay: 3.31s; }
.about-flow__stage:nth-child(4) .about-flow__ring::before { animation-delay: 4.89s; }

.about-flow::after {
	content: "";
	position: absolute;
	left: 50%;
	top: 90px;
	width: 1024px;
	max-width: calc(100% + 34px);
	height: 14px;
	transform: translateX(-50%);
	background: radial-gradient(circle, rgba(236, 231, 255, 0.95) 0 2px, rgba(167, 139, 250, 0.55) 3.5px, rgba(167, 139, 250, 0) 7px) no-repeat;
	background-size: 14px 14px;
	animation: about-flow-light 6s linear infinite;
	pointer-events: none;
}

@keyframes about-flow-light {
	0% {
		background-position: 0% 50%;
		opacity: 0;
	}

	3%,
	97% {
		opacity: 1;
	}

	100% {
		background-position: 100% 50%;
		opacity: 0;
	}
}

@keyframes about-ring-pulse {
	0%,
	30%,
	100% {
		transform: scale(1);
		border-color: rgba(167, 139, 250, 0.18);
	}

	8% {
		transform: scale(1.09);
		border-color: rgba(196, 176, 255, 0.6);
	}
}

@media (prefers-reduced-motion: reduce) {
	.about-flow::after {
		display: none;
	}

	.about-flow__ring::before {
		animation: none;
	}
}

/* The halo and the icon are both shares of the ring -- 262/192 and 128/192 --
   so narrow screens can shrink the ring and nothing else has to follow. */
.about-flow__ring img {
	width: 66.7%;
	height: auto;
}

.about-flow__label {
	margin-top: 29px;
	font-size: 16px;
	color: #fff;
}

/* ---- "Built from within trusted financial infrastructure" -------------- *
 * Copy beside the photograph, on the design's 750:600 split. The two columns
 * are independent stacks and are meant to end at different heights, so the
 * grid starts them together rather than centring either.
 */
.about-origin__inner {
	display: grid;
	grid-template-columns: minmax(0, 750fr) minmax(0, 600fr);
	align-items: start;
	gap: 120px;
}

/* Capped so "financial" drops to the second line, as drawn. */
.about-origin__copy .section__title {
	max-width: 600px;
	margin-bottom: 48px;
}

.about-origin__copy p {
	font-size: 16px;
	line-height: 28px;
	color: rgba(255, 255, 255, 0.92);
	margin-bottom: 16px;
}

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

.about-origin__close {
	margin-top: 26px;
	font-weight: 600;
	color: #fff;
}

/*
 * The photograph is the box's background rather than an <img> so the two
 * badges can sit inside it without a stacking context of their own, and the
 * box keeps the design's 600x460 whatever the column does.
 */
.about-origin__media {
	display: flex;
	flex-direction: column;
	justify-content: flex-end;
	gap: 13px;
	aspect-ratio: 600 / 460;
	padding: 24px;
	border-radius: 20px;
	overflow: hidden;
	background-color: var(--navy);
	background-image: url("assets/img/about-building.jpg");
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
}

.about-badge {
	display: flex;
	align-items: center;
	gap: 13px;
	padding: 12px 16px;
	border-radius: 12px;
	background: rgba(255, 255, 255, 0.16);
	border: 1px solid rgba(255, 255, 255, 0.28);
	backdrop-filter: blur(8px);
}

.about-badge__mark {
	flex: none;
	width: 50px;
	height: 50px;
	border-radius: 8px;
}

.about-badge__copy h3 {
	font-size: 16px;
	font-weight: 600;
	color: #fff;
}

.about-badge__copy p {
	margin: 2px 0 0;
	font-size: 15px;
	line-height: 1.4;
	color: rgba(255, 255, 255, 0.85);
}

/* ---- "Focused on the exchange layer KYB depends on" -------------------- *
 * The wheel and the copy on the design's 544:732, with the whole of the slack
 * between them: space-between holds the two columns at their drawn widths on a
 * full container and closes the gutter to 48 once there is no room for it.
 */
.about-focus__inner {
	display: grid;
	grid-template-columns: minmax(0, 544px) minmax(0, 732px);
	justify-content: space-between;
	align-items: start;
	gap: 48px;
}

.about-diagram {
	position: relative;
	justify-self: center;
	width: 424px;
	max-width: 100%;
}

/* The overlay is registered to the artwork's own box, and an inline image's
   baseline gap would make that box taller than the artwork. */
.about-diagram > img {
	display: block;
	width: 100%;
	height: auto;
}

.about-focus__copy .section__title {
	margin-bottom: 34px;
}

.about-focus__copy p {
	font-size: 16px;
	line-height: 28px;
	color: var(--text);
	margin-bottom: 16px;
}

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

.about-focus__close {
	margin-top: 30px;
	font-weight: 600;
	color: var(--heading);
}

/* ---- "Modern technology, with trust at the centre" --------------------- *
 * A different artwork on the same wave treatment as the home page's light
 * sections; the rule below is the one place it is named.
 */
.about-tech {
	background-image: url("assets/img/wave-light.jpg");
}

.about-tech__head {
	margin-bottom: 56px;
}

/*
 * Two rows that are not halves and do not agree with each other: the design
 * draws 425:1035 above 773:687. Each row is its own flex line and each card's
 * --grow is its drawn width, so the ratio is the only thing the stylesheet has
 * to know and a third row would need nothing here.
 */
.about-tech__rows {
	display: flex;
	flex-direction: column;
	gap: 10px;
}

.about-tech__row {
	display: flex;
	gap: 10px;
}

.about-tech-card {
	display: flex;
	gap: 16px;
	flex: var(--grow, 1) 1 0;
	min-width: 0;
	padding: 24px 32px;
	border-radius: var(--radius-sm);
	background: #fff;
}

.about-tech-card__node {
	flex: none;
	width: 12px;
	height: 63px;
}

.about-tech-card h3 {
	font-size: 18px;
	font-weight: 600;
	margin-bottom: 6px;
}

.about-tech-card p {
	margin: 0;
	font-size: 16px;
	line-height: 1.6;
	color: var(--text);
}

/* ---- "Towards trusted global infrastructure for KYB information" ------- *
 * Four gradient cards, and their pale ring is drawn OUTSIDE the content box,
 * not inside it: the design's row of four measures 1480 across a 1470 content
 * box, overhanging it by exactly the ring's 5px at each end. The negative
 * margin is that overhang, which is what leaves the four fills on the 330px
 * the design gives them.
 */
.about-future__head {
	margin-bottom: 52px;
}

.about-future__cards {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 40px;
	margin-inline: -5px;
}

/*
 * The gradient's angle and its two stops are the export's own: it runs from the
 * card's top-left corner to (514.3, 4527.7) on the page, which over a 330x402
 * box is 129.4deg and exactly the length CSS gives that angle -- so the two
 * describe the same ramp rather than an approximation of it.
 */
.about-future-card {
	display: flex;
	flex-direction: column;
	padding: 30px;
	border: 5px solid #ddd7fa;
	border-radius: 21px;
	background: linear-gradient(129.4deg, #1d0837, #7747e8);
	color: #fff;
}

/*
 * A fixed box rather than one sized to its artwork: the design centres every
 * illustration on the same line and starts every card's text on the same one,
 * and the four artworks are different heights. The tallest of them overhangs
 * this box, which is why nothing here clips.
 */
.about-future-card__art {
	display: flex;
	align-items: center;
	justify-content: center;
	height: 123px;
	margin-bottom: 34px;
}

.about-future-card__art img {
	max-width: 100%;
	height: auto;
}

.about-future-card p {
	margin: 0;
	font-size: 16px;
	line-height: 28px;
	color: rgba(255, 255, 255, 0.95);
}

/*
 * The closing CTA is shared chrome, and this page's heading is one word too
 * long for .cta__inner: the sentence measures 1213 against the 1132 available,
 * so it wrapped after "information" and left "exchange" alone on the second
 * line. This cap breaks it on the accent boundary instead -- "Learn how Kybix
 * is expanding" over "KYB information exchange" -- so the second line is
 * exactly the purple phrase and the first is exactly the white one.
 *
 * That is a word earlier than the design draws it: About.svg keeps "KYB" up on
 * the first line, which splits the accent across the break. Putting the whole
 * accented phrase on its own line reads better than the export here, and this
 * page is the only one it applies to.
 *
 * The cap is in em rather than px because .cta__title's size is a clamp on vw:
 * a fixed cap stays put while the type shrinks, so at narrower viewports "KYB"
 * would climb back onto the first line. In em the cap tracks the type, and
 * 14.2em sits between the 13.16em the first line measures and the 15.36em it
 * would take to pull "KYB" up onto it -- at every size in the clamp's range.
 */
.page-about .cta__title {
	max-width: 14.2em;
	margin-inline: auto;
}

/* ============================================================= *
 * Responsive
 * ============================================================= */
@media (max-width: 960px) {
	.hero__inner,
	.split__inner,
	.scratch__inner {
		grid-template-columns: 1fr;
		gap: 2.5rem;
	}

	.hero__inner {
		padding-top: 55px;
	}

	.split--reverse .split__media {
		order: 0;
	}

	/*
	 * Stacked, the net has the column to itself and the mesh's left overhang
	 * has nothing to run behind, so it is pulled back inside the box.
	 */
	.hero__net {
		margin-inline: auto;
	}

	/*
	 * The mesh behind the copy. Wide enough to run past both edges of a phone,
	 * sitting over the headline and the lead, and faint enough that type reads
	 * through it -- the artwork is painted on the hero's own ground, so what
	 * the opacity dims is the dots and their light, not a plate behind the
	 * words. The box carries the artwork's aspect ratio because hero-spark.js
	 * takes the canvas's parent as its host and works in thousandths of it.
	 */
	.hero__copy-mesh {
		display: block;
		position: absolute;
		top: 6%;
		left: 50%;
		width: 210%;
		max-width: none;
		aspect-ratio: 1150 / 453;
		transform: translateX(-50%);
		opacity: 0.4;
		z-index: 0;
		pointer-events: none;
	}

	.hero__copy-mesh img,
	.hero__copy-mesh canvas {
		position: absolute;
		inset: 0;
		width: 100%;
		height: 100%;
	}

	.hero__net-mesh {
		left: -18%;
		width: 128%;
	}

	/*
	 * "Not another KYB tool": stacked, the pills stop sizing to their own copy
	 * -- which gave seven pills of seven widths -- and both groups take the same
	 * grid of equal columns, two here and one on a phone. Both groups sit in a
	 * .container, so their columns line up across the join.
	 */
	.layer-pills,
	.layer-pills--wide {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 23px;
	}

	.backers {
		padding: 72px 0 80px;
	}

	.backers__inner {
		flex-wrap: wrap;
		gap: 24px;
	}

	.audience-card {
		flex-basis: calc((100% - 17px) / 2);
	}

	.pillars {
		grid-template-columns: 1fr 1fr;
		gap: 2rem;
	}

	.site-footer__inner {
		grid-template-columns: 1fr 1fr;
	}

	/*
	 * One column. The cards are already in reading order in the markup, so
	 * collapsing the second track is the whole of it.
	 */
	.team-cards {
		grid-template-columns: 1fr;
	}

	/* One column, form last — the pitch is what should be read first. */
	.contact-grid {
		grid-template-columns: 1fr;
	}

	.contact-intro {
		padding: 32px;
	}

	/*
	 * The platform page's splits collapse. .platform-grid keeps its twelve
	 * columns but every card takes all of them, so the source order -- which is
	 * the reading order of the journey band -- is what comes out.
	 */
	.platform-hero__inner {
		padding-bottom: 96px;
	}

	.platform-notes,
	.platform-trust__cards {
		grid-template-columns: 1fr;
	}

	/* Two up rather than one: the journey band is seven short cards. */
	.platform-card {
		grid-column: span 6;
	}

	.platform-features {
		grid-template-columns: 1fr 1fr;
	}

	.platform-foundation__inner {
		grid-template-columns: 1fr;
		row-gap: 48px;
	}

	.platform-foundation__copy .section__title {
		margin-bottom: 32px;
	}

	/*
	 * The about page's three splits collapse. The hero's collage stops being a
	 * column of its own and is capped rather than pinned to its drawn width, and
	 * the wheel keeps the row it is centred in.
	 */
	.about-hero__inner,
	.about-origin__inner,
	.about-focus__inner {
		grid-template-columns: minmax(0, 1fr);
		gap: 48px;
	}

	.about-hero__inner {
		padding-top: 72px;
		padding-bottom: 96px;
	}

	.about-hero__title,
	.about-hero__lead {
		max-width: none;
	}

	.about-hero__stage {
		justify-self: center;
	}

	/*
	 * Stacked, the band is far taller than it is wide, so the constellation's
	 * cover crop scales it by height -- dots and lines at twice their drawn size,
	 * right under the paragraphs. It stays behind the copy but steps back to a
	 * texture. Both the still and the canvas, so the page reads the same with
	 * the script off.
	 */
	.about-hero__net,
	.about-hero.is-net-live .about-net {
		opacity: 0.3;
	}

	.about-origin__media {
		max-width: 600px;
	}

	/*
	 * Two rings a row rather than four -- a grid, not a wrapping flex line,
	 * which would leave three on the first row and one on its own. The dashed
	 * run is drawn across a single row and has nothing to say about a second,
	 * so it goes.
	 */
	.about-flow {
		display: grid;
		grid-template-columns: repeat(2, 192px);
		justify-content: center;
		gap: 48px;
	}

	.about-flow::before,
	.about-flow::after {
		display: none;
	}

	.about-tech__row {
		flex-direction: column;
	}

	.about-future__cards {
		grid-template-columns: 1fr 1fr;
	}
}

@media (max-width: 720px) {
	.section {
		padding: 64px 0;
	}

	.contact-page {
		padding: 64px 0 72px;
	}

	.contact-page__title {
		margin-bottom: 2.5rem;
	}

	.contact-form {
		margin-inline: 0;
	}

	.contact-form__row--split {
		grid-template-columns: 1fr;
		gap: 32px;
	}

	/*
	 * The design's 14px would make iOS Safari zoom the page in on focus, which
	 * it only does below 16px. Desktop keeps the drawn size; touch gets the one
	 * that does not move the layout under the visitor's thumb.
	 */
	.contact-field__input {
		font-size: 16px;
	}

	.feature-cards {
		grid-template-columns: 1fr;
		gap: 1.75rem;
	}

	.layer-pills,
	.layer-pills--wide {
		grid-template-columns: minmax(0, 1fr);
	}

	.audience-card {
		flex-basis: 100%;
	}

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

	.scratch-card {
		padding: 24px;
	}

	.scratch-note--stepped {
		margin-left: 0;
	}

	.scratch__media {
		min-height: 320px;
		padding-bottom: 20px;
	}

	.section--trace-a::before,
	.section--trace-b::before,
	.section--trace-b::after {
		display: none;
	}

	.feature-card {
		grid-template-columns: 1fr;
		min-height: 0;
	}

	.feature-card__body {
		padding: 1.5rem;
	}

	.feature-card__media {
		height: 180px;
	}

	.feature-card__media img {
		position: static;
		height: 180px;
	}

	/*
	 * The two screenshot strips cover here, the way the photographs do. The
	 * desktop rule sizes them to the strip's height from the top-left, which is
	 * what keeps a 750px-tall dashboard legible in a 199px column beside the
	 * copy -- but in a band 180px tall and the full width of the card it leaves
	 * a 128px thumbnail with the card's grey stretching away beside it. Top
	 * anchored, so what survives the crop is the head of each screen.
	 */
	.feature-card__media--shot {
		background-size: cover;
		background-position: left top;
	}

	/*
	 * The "maintain" screenshot is taken from 65% down instead of the top. In a
	 * band this shallow the window is a quarter of the image, and anchored top
	 * it lands on that screenshot's dark window chrome and the empty rows under
	 * it -- the part carrying nothing. 65% is the lowest window that still
	 * clears the dark band across the image's own foot, and it catches both
	 * halves of the product at once: the record fields with their Verified/Due
	 * badge on the left, the assistant conversation on the right. The class
	 * comes from the file's own name, so a card gets one whether or not it
	 * needs a rule here.
	 */
	.feature-card__media--maintain {
		background-position: left 65%;
	}

	.team-card {
		padding: 1.5rem;
	}

	.team-card__head {
		gap: 1rem;
	}

	.team-card__avatar {
		width: 76px;
		height: 76px;
	}

	.team-card__node {
		width: 10px;
		margin-right: -0.6rem;
	}

	.page-platform .section {
		padding: 64px 0 72px;
	}

	.platform-head {
		margin-bottom: 48px;
	}

	.platform-head .section__title {
		margin-bottom: 28px;
	}

	.platform-features {
		grid-template-columns: 1fr;
	}

	.platform-card {
		grid-column: span 12;
	}

	.platform-note,
	.platform-feature,
	.platform-statement,
	.platform-trust-card {
		padding: 24px;
	}

	.platform-card {
		padding: 24px;
	}

	.page-about .section {
		padding: 64px 0 72px;
	}

	.about-mission__line {
		margin-top: 64px;
	}

	.about-future__cards {
		grid-template-columns: 1fr;
	}

	.about-tech-card {
		padding: 24px;
	}

	.about-future-card {
		padding: 24px;
	}

	/* Two rings still, but sized by the column rather than fixed at 192px. */
	.about-flow {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 32px;
	}

	.about-flow__stage {
		width: auto;
	}

	.about-flow__ring {
		width: 100%;
		max-width: 192px;
		height: auto;
		aspect-ratio: 1;
		margin-inline: auto;
	}
}

/* ============================================================= *
 * Solutions page
 * ============================================================= *
 * Six bands under the light header: a hero on the page's own light grey with
 * the constellation across it, one dark band, two on the light wave, one flat
 * and a white one. Four things are worth knowing before editing it.
 *
 * The design draws THREE content widths and they are all deliberate: the full
 * container (1470) carries the hero, the organisation cards and the challenge
 * cloud; 1342 carries the dark band, which is the same twelve-column measure
 * the platform page's dark bands use; and 1274 carries the use-case cards. Each
 * is named on the rule that needs it.
 *
 * Every band is padded 120px top and bottom -- measured, not chosen, and the
 * same on all six -- which is what .page-solutions .section sets below.
 *
 * Two of the light bands are wave-animated, so wave-bg.js is enqueued for this
 * page as well (see kybix_enqueue). Both name their artwork in the rule below,
 * which is both what the script reads the URL off and the still it falls back
 * to. Unlike the home page's light sections these two wash the artwork out to
 * half strength, and that wash CANNOT be a second background layer: the canvas
 * paints over the host's background entirely, so it stacks above the canvas as
 * an ::after and the content goes to z-index 2. Same arrangement as .scratch.
 *
 * Both diagrams move. The hero's is the flat export with an SMIL overlay on it
 * (parts/solutions-exchange.php); the outcomes diagram is drawn from scratch
 * and animated in CSS here (parts/solutions-outcomes.php). The two use
 * different techniques for one reason -- what prefers-reduced-motion can switch
 * off -- and both files say so at the top.
 * ============================================================= */

.page-solutions .section {
	padding: 120px 0;
}

/* This export tracks its display type at its natural width rather than the
   theme's -0.02em, the same thing the platform page's export does. Scoped to
   the hero because the section headings below it do carry the tracking. */
.solutions-hero__title {
	letter-spacing: normal;
}

/* ---- Hero -------------------------------------------------------------- *
 * The constellation runs across the whole band and is decorative, so it needs
 * no registration with anything. It is the about page's artwork, animated by
 * the about page's script: the canvas keeps the .about-net class because that
 * is the selector about-net.js looks for, and the script takes whatever holds
 * the canvas as its host. The PNG is the still, and only one is ever visible.
 */
.solutions-hero {
	position: relative;
	overflow: hidden;
	background: var(--bg-light);
}

.solutions-hero__net {
	position: absolute;
	inset: 0;
	z-index: 0;
	width: 100%;
	height: 100%;
	max-width: none;
	object-fit: cover;
	object-position: center;
	pointer-events: none;
}

.solutions-hero.is-net-live .about-net {
	opacity: 1;
}

.solutions-hero.is-net-live .solutions-hero__net {
	opacity: 0;
}

/*
 * The copy and the diagram overlap in the design, and the ratio below is how.
 * The diagram is 673 wide and its right edge sits on the container's, so its
 * column starts at 797 -- but the headline's first line needs 880 to hold
 * "Private KYB information" together. Rather than let the headline overflow,
 * the copy takes the 880 it needs and the diagram's column is the 590 that are
 * left, with the artwork overhanging it to the LEFT by the difference. That
 * overhang lands in the gap above the top-left label, which is empty in the
 * design too. Both columns start at the same line: the band is exactly 120px of
 * padding either side of the 652-tall diagram, and the copy is shorter.
 */
.solutions-hero__inner {
	position: relative;
	z-index: 1;
	display: grid;
	grid-template-columns: minmax(0, 880fr) minmax(0, 590fr);
	align-items: start;
	padding-top: 120px;
	padding-bottom: 120px;
}

/*
 * 760px is a break, not a measure. The design sets this headline in four lines
 * -- "Private KYB information / exchange for financial / institutions and /
 * companies" -- and at 400 the four candidates that must not fit are 803 and
 * up while the widest line that must fit is 710. Anything between those two
 * holds all four breaks; anything above 803 turns the last two lines into one.
 */
.solutions-hero__title {
	max-width: 760px;
	font-family: var(--font-head);
	font-size: clamp(2.4rem, 4.6vw, 4rem);
	font-weight: 400;
	line-height: 1.17;
	margin-bottom: 48px;
}

/* 745px is where the design breaks this paragraph, which is narrower than the
   column it sits in -- the headline is what sets that column's width. */
.solutions-hero__lead {
	max-width: 745px;
	font-size: 18px;
	line-height: 32px;
	color: var(--text);
}

.solutions-hero__lead p {
	margin: 0;
}

.solutions-hero__lead p + p {
	margin-top: 28px;
}

/* 673 in a 590 column: the width and the negative margin are the same number,
   so the artwork's right edge stays on the column's and the overhang is all on
   the left. Percentages here resolve against the grid area, not the image. */
.solutions-hero__diagram {
	position: relative;
	width: 114.068%;
	max-width: none;
	margin-left: -14.068%;
}

.solutions-hero__diagram > img {
	display: block;
	width: 100%;
	height: auto;
}

/* Shares the export's own viewBox, so the two scale together and cannot drift. */
.solutions-diagram__anim {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	pointer-events: none;
}

/* ---- "Supporting the teams under pressure" ----------------------------- *
 * The dark band, on the same plate and the same artwork as every other dark
 * band on the site (see "The dark wave" in CLAUDE.md). It does .section--wave's
 * job by hand rather than taking that class, because that class names the light
 * artwork; the container is lifted to z-index 1 and there is no tint between.
 */
.solutions-teams {
	position: relative;
	overflow: hidden;
	background-color: var(--wave-ground);
	background-image: url("assets/img/wave-purple.jpg");
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
	color: rgba(255, 255, 255, 0.92);
}

.solutions-teams > .container {
	position: relative;
	z-index: 1;
}

.solutions-teams .section__title {
	color: #fff;
}

.solutions-teams .accent {
	color: #8b5cf6;
}

.solutions-teams__head {
	margin-bottom: 80px;
}

/* The design's 1342 across three cards on a 24px gutter -- 432 each, which is
   the platform page's dark bands to the pixel. Three equal columns rather than
   twelve, because nothing here spans anything else. */
.solutions-teams__grid {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	grid-auto-rows: 1fr;
	gap: 24px;
	max-width: 1342px;
	margin-inline: auto;
}

/*
 * The fill is translucent so the artwork still shows through it, which is why
 * background-clip keeps it off the border. None of the three paddings is the
 * same as another: 28 above, the design's own 48 at the sides, and 26 below,
 * which is what puts the four-line card -- the tallest, and so the one that
 * sets both rows through grid-auto-rows above -- on the design's 174.
 */
.solutions-team-card {
	padding: 28px 48px 26px;
	border: 1px solid rgba(140, 92, 243, 0.55);
	border-radius: 10px;
	background: linear-gradient(100deg, rgba(35, 23, 77, 0.94), rgba(108, 66, 198, 0.78));
	background-clip: padding-box;
}

.solutions-team-card h3 {
	font-family: var(--font-body);
	font-size: 16px;
	line-height: 22px;
	font-weight: 600;
	letter-spacing: normal;
	color: #fff;
	margin: 0 0 6px;
}

.solutions-team-card p {
	margin: 0;
	font-size: 14px;
	line-height: 23px;
	color: var(--ink-text);
}

/* ---- The two washed light bands ---------------------------------------- *
 * wave-light.jpg at half strength. The wash has to stack ABOVE the canvas --
 * see the note at the top of this section -- so it is an ::after and the
 * container goes to 2 rather than .section--wave's 1.
 */
.solutions-orgs,
.solutions-challenges {
	background-color: var(--bg-light);
	background-image: url("assets/img/wave-light.jpg");
}

.solutions-orgs::after,
.solutions-challenges::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	background: rgba(244, 245, 247, 0.5);
	pointer-events: none;
}

.solutions-orgs > .container,
.solutions-challenges > .container {
	z-index: 2;
}

/* ---- "Built for the organizations managing KYB complexity" ------------- *
 * The export puts the intro and its "By organisation type" label in the grid's
 * first cell, level with the cards and ragged-left against them, which reads as
 * a card that lost its background rather than as a lead. Both are centred under
 * the heading here instead, and the seven cards are dealt 2 + 3 + 2 -- the row
 * of two taking the same two-column grid the last row always had.
 *
 * Both selectors below are written as children of the head because
 * .center-narrow > p already claims a colour, a size and a measure for anything
 * sitting there, and only equal specificity later in the file takes them back.
 */
.solutions-orgs__head {
	margin-bottom: 64px;
}

/* Narrower than .center-narrow's 980 so the lead reads as two centred lines
   rather than one long one. */
.solutions-orgs__head > .solutions-orgs__intro {
	margin: 0 auto;
	font-size: 15px;
	line-height: 28px;
	color: var(--text);
}

.solutions-orgs__head > .solutions-orgs__label {
	max-width: none;
	margin: 28px 0 0;
	font-family: var(--font-body);
	font-size: 18px;
	line-height: 1.4;
	font-weight: 600;
	color: var(--heading);
}

/* Three columns on the design's 24px gutter across the full container. */
.solutions-orgs__grid {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 32px 24px;
}

/* A row of two, on the wider gutter the design gives that row. */
.solutions-orgs__grid--wide {
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 32px;
}

/* The rows are separate grids, so the gap between them is theirs to set. */
.solutions-orgs__grid + .solutions-orgs__grid {
	margin-top: 32px;
}

/* The same idiom as .about-tech-card -- a white card with the node glyph beside
   the copy -- at this design's own padding and type sizes. */
.solutions-org-card {
	display: flex;
	gap: 16px;
	padding: 32px;
	border-radius: var(--radius-sm);
	background: #fff;
}

.solutions-org-card__node {
	flex: none;
	width: 12px;
	height: 63px;
}

.solutions-org-card h3 {
	font-family: var(--font-body);
	font-size: 18px;
	line-height: 1.2;
	font-weight: 600;
	letter-spacing: normal;
	margin: 0 0 8px;
}

.solutions-org-card p {
	margin: 0;
	font-size: 15px;
	line-height: 24px;
	color: var(--text);
}

/* ---- "Designed around practical KYB use cases" ------------------------- */
.solutions-cases {
	background: var(--bg-light);
}

/* 74 rather than 80 because the ring below is drawn outside the card: the fill
   lands on the same 80 every other head on this page leaves. */
.solutions-cases__head {
	margin-bottom: 74px;
}

/*
 * Six columns on a 22px gutter across 1284. The design's cards measure 403 and
 * 621 wide with 32 between them, which is 2 and 3 of those columns once the
 * 5px ring at each end is counted in -- the ring is a border, so 22 between
 * boxes is 32 between fills.
 */
.solutions-cases__grid {
	display: grid;
	grid-template-columns: repeat(6, minmax(0, 1fr));
	gap: 22px;
	max-width: 1284px;
	margin-inline: auto;
}

/*
 * The pale ring is a 5px BORDER, not an outline or a shadow -- the design draws
 * the gradient starting where the ring ends, and the radius given is the outer
 * one. Same decision as the platform page's cards.
 */
.solutions-case-card {
	grid-column: span var(--span, 2);
	padding: 32px;
	border: 5px solid #ddd7fa;
	border-radius: 18px;
	background: linear-gradient(to bottom right, #1e0937, #7747e8);
}

/* `to bottom right` rather than an angle: it is what puts the same colour on
   the two corners the export puts it on, whatever the card's aspect. */
.solutions-case-card__head {
	display: flex;
	align-items: center;
	gap: 18px;
	margin-bottom: 14px;
}

.solutions-case-card__icon {
	flex: none;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	border: 1px solid rgba(221, 215, 250, 0.4);
	border-radius: 10px;
	color: #ddd7fa;
}

.solutions-case-card h3 {
	font-family: var(--font-body);
	font-size: 15px;
	line-height: 22px;
	font-weight: 600;
	letter-spacing: normal;
	color: #fff;
	margin: 0;
}

.solutions-case-card p {
	margin: 0;
	font-size: 14px;
	line-height: 24px;
	color: rgba(255, 255, 255, 0.8);
}

/* ---- "The KYB challenges organisations are still managing manually" ---- *
 * One wrapping, centred cloud of pills in the design -- but the first four
 * share their rows with the copy column and so centre on a narrower box than
 * the five below it. That is the whole of the split: two lists, each centred on
 * its own, and the row rhythm carries across the join because both use the same
 * gap and the second starts one gap below the first.
 */
.solutions-challenges__top {
	display: grid;
	grid-template-columns: minmax(0, 500fr) minmax(0, 970fr);
	align-items: start;
}

/* Inset from the container's edge like the design, but on the left only: the
   paragraph needs every one of the remaining 476px to break where it is drawn,
   and it has the whole gutter between the columns to its right anyway. */
.solutions-challenges__copy {
	padding-left: 24px;
}

.solutions-challenges__copy .section__title {
	margin-bottom: 44px;
}

.solutions-challenges__copy p {
	margin: 0;
	font-size: 15px;
	line-height: 28px;
	color: var(--text);
}

.solutions-pills {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 24px 27px;
	list-style: none;
	margin: 0;
	padding: 0;
}

/* The pills start below the heading's first line, not level with it. */
.solutions-pills--beside {
	margin-top: 48px;
}

.solutions-pills--below {
	margin-top: 24px;
}

.solutions-pill {
	display: flex;
	align-items: center;
	gap: 14px;
	min-height: 71px;
	padding: 12px 32px;
	border-radius: 12px;
	background: #fff;
	box-shadow: 0 2px 14px rgba(29, 8, 55, 0.06);
	font-size: 16px;
	line-height: 1.4;
	color: var(--heading);
}

.solutions-pill__tick {
	flex: none;
}

/* ---- "A more efficient way to handle KYB information" ------------------ */
.solutions-outcomes {
	background: var(--bg);
}

/* 884 and 554 either side of a 32px gutter: the diagram is 700 wide and sits
   centred in the first, which is exactly where the design puts it. */
.solutions-outcomes__inner {
	display: grid;
	grid-template-columns: minmax(0, 884fr) minmax(0, 554fr);
	gap: 32px;
	align-items: center;
}

.solutions-outcomes__diagram {
	justify-self: center;
	width: 100%;
	max-width: 700px;
}

.solutions-outcomes__svg {
	display: block;
	width: 100%;
	height: auto;
	font-family: var(--font-body);
}

.solutions-outcomes__label {
	font-size: 12px;
	font-weight: 500;
	fill: #1d0837;
}

.solutions-outcomes__core {
	font-size: 14px;
	font-weight: 400;
	fill: #fff;
}

/*
 * The dots orbit their ring; the badges sway on theirs. A badge takes two
 * rotations that cancel -- the orbit turns it about the diagram's centre and
 * .solutions-outcomes__upright turns it back about its own -- which composes to
 * a pure translation along the arc, so the pill travels without ever tilting.
 * The two must keep the same duration and delay or it starts to lean, which is
 * why both read the same --phase.
 *
 * transform-box is declared rather than left to the initial value: the initial
 * changed to view-box only in 2019 and older engines resolve transform-origin
 * against the element's own box, which would turn every badge on the spot.
 */
.solutions-outcomes__ring,
.solutions-outcomes__orbit {
	transform-box: view-box;
	transform-origin: 345.14px 253.036px;
}

.solutions-outcomes__ring {
	animation: solutions-orbit var(--turn, 60s) linear infinite;
}

.solutions-outcomes__ring--back {
	animation-direction: reverse;
}

.solutions-outcomes__orbit {
	animation: solutions-sway 17s ease-in-out infinite;
	animation-delay: var(--phase, 0s);
}

.solutions-outcomes__upright {
	transform-box: view-box;
	transform-origin: var(--bx) var(--by);
	animation: solutions-sway-back 17s ease-in-out infinite;
	animation-delay: var(--phase, 0s);
}

@keyframes solutions-orbit {
	to {
		transform: rotate(360deg);
	}
}

@keyframes solutions-sway {
	0%,
	100% {
		transform: rotate(-3deg);
	}

	50% {
		transform: rotate(3deg);
	}
}

@keyframes solutions-sway-back {
	0%,
	100% {
		transform: rotate(3deg);
	}

	50% {
		transform: rotate(-3deg);
	}
}

.solutions-outcomes__copy .section__title {
	margin-bottom: 46px;
}

/* Narrower than the column the heading needs: 518px is where the design breaks
   all three of these paragraphs, and the heading needs 528 to keep "handle KYB
   information" on one line. */
.solutions-outcomes__copy p {
	max-width: 518px;
	margin: 0 0 16px;
	font-size: 15px;
	line-height: 28px;
	color: var(--text);
}

.solutions-outcomes__close {
	font-weight: 600;
	color: var(--heading);
}

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

/*
 * Both diagrams settle rather than stop halfway: the hero's overlay is hidden
 * and its export -- which draws every node at rest and no dot at all -- is what
 * shows, and the outcomes diagram simply holds the frame the export draws.
 */
@media (prefers-reduced-motion: reduce) {
	.solutions-diagram__anim {
		display: none;
	}

	.solutions-outcomes__ring,
	.solutions-outcomes__orbit,
	.solutions-outcomes__upright {
		animation: none;
	}
}

/*
 * The closing CTA is shared chrome, but this page's heading is the one that has
 * to be told where to break: at .cta__inner's own width the design's two lines
 * come out as one and a half. 960 is between the 910 the first line measures
 * and the 1015 it would take to pull "KYB" up onto it.
 */
.page-solutions .cta__title {
	max-width: 960px;
	margin-inline: auto;
}

/* ---- Solutions, narrow ------------------------------------------------- */

/* The headline's break cap is only meaningful while the column is wider than
   it; below that it would just stop the copy using the room it has. */
@media (max-width: 1120px) {
	.solutions-hero__title {
		max-width: none;
	}
}

@media (max-width: 960px) {
	.page-solutions .section {
		padding: 72px 0;
	}

	.solutions-hero__inner {
		grid-template-columns: minmax(0, 1fr);
		gap: 48px;
		padding-top: 72px;
		padding-bottom: 72px;
	}

	/* Back to its own size, centred, with nothing to overhang. */
	.solutions-hero__diagram {
		width: 100%;
		max-width: 673px;
		margin-inline: auto;
	}

	.solutions-hero__lead {
		max-width: none;
	}

	/* The about page's constellation, stepped back for the same reason it is
	   there: stacked, the cover crop blows it up under the copy. */
	.solutions-hero__net,
	.solutions-hero.is-net-live .about-net {
		opacity: 0.3;
	}

	.solutions-teams__head,
	.solutions-orgs__head,
	.solutions-cases__head {
		margin-bottom: 48px;
	}

	.solutions-teams__grid,
	.solutions-orgs__grid,
	.solutions-orgs__grid--wide,
	.solutions-cases__grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	/* The six-column spans mean nothing once there are two columns. */
	.solutions-case-card {
		grid-column: span 1;
	}

	.solutions-challenges__top {
		grid-template-columns: minmax(0, 1fr);
	}

	.solutions-challenges__copy {
		padding-left: 0;
	}

	.solutions-pills--beside {
		margin-top: 40px;
	}

	/*
	 * Stacked, the two pill lists stop wrapping to their own widths and share
	 * one grid of equal columns -- two here, one on a phone. Once the copy is
	 * above them both lists are the container's width, so their columns line
	 * up across the join.
	 */
	.solutions-pills {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	.solutions-outcomes__inner {
		grid-template-columns: minmax(0, 1fr);
		gap: 48px;
	}

	.solutions-outcomes__copy p {
		max-width: none;
	}
}

@media (max-width: 720px) {
	.solutions-teams__grid,
	.solutions-orgs__grid,
	.solutions-orgs__grid--wide,
	.solutions-cases__grid {
		grid-template-columns: minmax(0, 1fr);
	}

	.solutions-team-card {
		padding: 24px 24px 22px;
	}

	.solutions-org-card,
	.solutions-case-card {
		padding: 24px;
	}

	.solutions-pills {
		grid-template-columns: minmax(0, 1fr);
	}

	.solutions-pill {
		padding: 12px 20px;
	}
}

/* ============================================================= *
 * Why Kybix page
 * ============================================================= *
 * Six bands: a dark hero whose ground runs up behind the nav, a flat light
 * band, one long dark wave carrying both the difference cards and the trust
 * diagram, a flat light band, a washed light wave band, and the shared closing
 * band. Four things are worth knowing before editing it.
 *
 * THE HERO'S GROUND BELONGS UNDER THE NAV BAR. designs/WHY_KYBIX.svg paints the
 * node artwork at 35% over a solid #10041F bar, which reads as the artwork
 * being on top of the menu. That is the export being literal about layer order,
 * not a design: the site's bar is transparent, so the ground simply runs up
 * behind it exactly as it does on the home page. kybix_header_skin() is what
 * makes this page take the dark bar, and it is the whole of the change.
 *
 * The design draws TWO content widths: the full container (1470) carries the
 * hero, the light bands and the trust row, and 1342 carries the difference
 * cards -- the platform page's dark-band measure to the pixel, which is why
 * they lay out on that page's twelve-column grid.
 *
 * Every band is padded 120px top and bottom, measured off the export.
 *
 * BOTH DIAGRAMS ARE ANIMATED IN CSS, NOT SMIL, and that is deliberate: unlike
 * the home page's and the Solutions hero's, neither is a flat export with a
 * layer over it -- they are drawn from scratch in parts/why-exchange.php and
 * parts/why-trust.php, so the thing that moves IS the diagram and
 * prefers-reduced-motion has to be able to stop it where it stands. SMIL cannot
 * be switched off by a media query; these rules can.
 * ============================================================= */

.page-why .section {
	padding: 120px 0;
}

/* ---- Hero -------------------------------------------------------------- *
 * The band's own ground is the artwork's, so the mesh box falling short of an
 * edge is invisible -- why-net.jpg fades to within a unit or two of --ink on
 * all four sides, which is what lets the box be sized by height and pinned
 * right rather than stretched to cover.
 *
 * The band starts ABOVE the nav bar. The header is sticky and so in flow, and
 * pulling the whole main up by its height is what puts the artwork behind it --
 * the bar keeps its flow space and its z-index, so it still paints on top and
 * still sticks. header.php and assets/js/nav.js carry the other half of this:
 * the bar is only clear while the page is at the top.
 */
.page-why .site-main {
	margin-top: -77px;
}

.site-header--clear-top.is-at-top {
	background: transparent;
}

.why-hero {
	position: relative;
	overflow: hidden;
	background: var(--ink);
	color: var(--ink-text);
}

/*
 * The picture and the canvas that lights it both fill this box, and the box
 * carries the artwork's own 1619x990 -- that is the whole of the registration
 * between the light and the dots it is lighting (assets/js/hero-spark.js reads
 * its node positions as thousandths of this box). Sized by the band's height so
 * it always covers it, pinned right, and overhanging to the left, which .why-
 * hero's overflow: hidden keeps off the page edge.
 *
 * min-width is the wide-and-short case: past about 1710 the band is wider than
 * the artwork's own aspect gives it, and rather than leave a strip of bare
 * ground the box is stretched to fit. That is deliberate -- covering the band is
 * worth more than the aspect here, and the canvas fills the same box, so the
 * light stays registered with the dots however the box is shaped. The trade is
 * a horizontal stretch that reaches about 1.2x at 1920 and is only obvious well
 * beyond it.
 */
.why-hero__mesh {
	position: absolute;
	top: 0;
	right: 0;
	z-index: 0;
	height: 100%;
	width: auto;
	min-width: 100%;
	max-width: none;
	aspect-ratio: 1619 / 990;
	pointer-events: none;
}

.why-hero__mesh img,
.why-hero__mesh .node-mesh__spark {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	max-width: none;
}

/*
 * The copy takes the width its headline needs and the diagram the rest. The
 * diagram's own ink box is 652 wide and its right edge sits on the container's,
 * so it gets 652 of the design's 1470 and the copy the 818 that are left --
 * with no gutter between them, because the diagram's labels leave one.
 *
 * The padding is the design's: it draws 244 above the diagram and 229 below it
 * on a 101px nav bar, and this bar is 77, so the top is that 244 less the 24 the
 * bar gives back. The band is measured from the top of the PAGE, not from under
 * the bar -- see .page-why .site-main above.
 */
.why-hero__inner {
	position: relative;
	z-index: 1;
	display: grid;
	grid-template-columns: minmax(0, 818fr) minmax(0, 652fr);
	align-items: center;
	gap: 0;
	padding-top: 220px;
	padding-bottom: 229px;
}

/*
 * 700px is a break, not a measure. The design sets this headline in two lines --
 * "KYB needs more than / another workflow tool" -- and at 400 each of them
 * measures about 663 while the shortest line that must NOT fit is nearer 880.
 * Anything between the two holds the break.
 */
.why-hero__title {
	max-width: 700px;
	font-size: clamp(2.4rem, 4.6vw, 4rem);
	font-weight: 400;
	line-height: 1.17;
	color: #fff;
	margin-bottom: 40px;
}

.why-hero__title .accent {
	color: #9064f7;
}

.why-hero__lead {
	max-width: 620px;
	font-size: 18px;
	line-height: 32px;
	color: var(--ink-text);
}

.why-hero__lead p {
	margin: 0;
}

.why-hero__lead p + p {
	margin-top: 25px;
}

/* ---- The hero's exchange diagram --------------------------------------- */
.why-hero__diagram {
	position: relative;
	width: 100%;
}

.why-exchange {
	display: block;
	width: 100%;
	height: auto;
	overflow: visible;
}

.why-exchange__hub-name {
	font-family: var(--font-body);
	font-size: 15px;
	font-weight: 700;
	fill: #fff;
}

.why-exchange__hub-kind {
	font-family: var(--font-body);
	font-size: 11px;
	fill: rgba(255, 255, 255, 0.86);
}

.why-exchange__label {
	font-family: var(--font-body);
	font-size: 11px;
	font-weight: 700;
	fill: #fff;
}

.why-exchange__note {
	font-family: var(--font-body);
	font-size: 9px;
	fill: rgba(255, 255, 255, 0.42);
}

/*
 * Scaling about the node's own centre: the group is already translated there,
 * so the viewBox's origin in ITS coordinate space is that centre.
 * transform-box is declared rather than left to the initial value -- the
 * initial only became view-box in 2019, and an engine that still resolves
 * transform-origin against the element's own bounding box would turn each node
 * about the corner of its glyph instead.
 */
.why-exchange__node,
.why-trust__node,
.why-trust__caption {
	transform-box: view-box;
	transform-origin: 0 0;
}

/*
 * One keyframe set for all six nodes, each on a delay of one sixth of the
 * cycle, which is what keeps a node's swell locked to the dot's arrival without
 * six sets of numbers to keep in step. 1.42 puts the disc at r=19.7, still
 * inside the node's own 23.091 halo, so nothing outside a node ever moves.
 */
.why-exchange__node {
	animation: why-exchange-swell 15.6s linear infinite;
	animation-delay: calc(var(--order) * 2.6s);
}

@keyframes why-exchange-swell {
	0%,
	4.487% {
		transform: scale(1);
	}

	5.833%,
	7.853% {
		transform: scale(1.42);
	}

	8.974%,
	100% {
		transform: scale(1);
	}
}

/*
 * The dot's circuit, and the only thing on this page that needs offset-path.
 * It is hidden unless that is supported, so an engine without it shows the
 * diagram standing still rather than a dot parked in the corner of the viewBox.
 *
 * The path and the stops below are generated from the same geometry the markup
 * carries -- hub rim at r=69.273, each run stopping 25 short of a node centre
 * so the dot never covers a glyph, and the arcs between spokes on that same
 * rim. offset-distance is measured along the whole path, so the percentages are
 * lengths, not angles: they cannot be adjusted by eye.
 */
.why-exchange__dot {
	visibility: hidden;
}

@supports (offset-path: path("M0 0L1 1")) {
	.why-exchange__dot {
		visibility: visible;
		offset-rotate: 0deg;
		offset-path: path("M368.000 185.727 L368.000 109.127 L368.000 185.727 A69.273 69.273 0 0 1 437.273 255.000 L513.870 255.000 L437.273 255.000 A69.273 69.273 0 0 1 368.000 324.273 L368.000 428.872 L368.000 324.273 A69.273 69.273 0 0 1 305.182 284.199 L64.511 396.067 L305.182 284.199 A69.273 69.273 0 0 1 298.727 255.000 L222.130 255.000 L298.727 255.000 A69.273 69.273 0 0 1 309.228 218.331 L84.050 77.838 L309.228 218.331 A69.273 69.273 0 0 1 368.000 185.727");
		animation: why-exchange-dot 15.6s infinite;
	}
}

@keyframes why-exchange-dot {
	0% { offset-distance: 0%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	4.487% { offset-distance: 3.537%; animation-timing-function: linear; }
	8.974% { offset-distance: 3.537%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	13.462% { offset-distance: 7.074%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	16.667% { offset-distance: 12.099%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	21.154% { offset-distance: 15.635%; animation-timing-function: linear; }
	25.641% { offset-distance: 15.635%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	30.128% { offset-distance: 19.172%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	33.333% { offset-distance: 24.197%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	37.821% { offset-distance: 29.027%; animation-timing-function: linear; }
	42.308% { offset-distance: 29.027%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	46.795% { offset-distance: 33.857%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	50.000% { offset-distance: 37.489%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	54.487% { offset-distance: 49.744%; animation-timing-function: linear; }
	58.974% { offset-distance: 49.744%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	63.462% { offset-distance: 61.999%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	66.667% { offset-distance: 63.391%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	71.154% { offset-distance: 66.928%; animation-timing-function: linear; }
	75.641% { offset-distance: 66.928%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	80.128% { offset-distance: 70.465%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	83.333% { offset-distance: 72.249%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	87.821% { offset-distance: 84.504%; animation-timing-function: linear; }
	92.308% { offset-distance: 84.504%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	96.795% { offset-distance: 96.760%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	100% { offset-distance: 100%; }
}

/* ---- "The problem is not just access to data" -------------------------- */
.why-problem {
	position: relative;
	background: var(--bg-light);
}

/*
 * The 2px gradient rule the design draws twice: once on the seam between the
 * hero and this band, and once under the "Technology matters" copy. Both are
 * the same 910-wide bar centred on the page, so both are this one rule -- as an
 * ::before on the seam, where there is no element to hang it on, and as a real
 * element where there is.
 */
.why-problem::before,
.why-rule {
	content: "";
	display: block;
	width: 100%;
	max-width: 910px;
	height: 2px;
	background: linear-gradient(90deg, #f1eeff 0%, #7747e8 50%, #f1eeff 100%);
}

.why-problem::before {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	margin-inline: auto;
}

/*
 * The second one sits on this band's bottom edge, the way the first sits on
 * the hero's -- a direct child of the section rather than of its container, so
 * it lands on the seam and not under the last paragraph.
 */
.why-rule {
	position: absolute;
	bottom: 0;
	left: 0;
	right: 0;
	margin-inline: auto;
}

/* The design's closing sentence in each light band is set darker and heavier
   than the copy above it -- it is the claim the section is making. */
.why-emphasis {
	color: var(--heading);
	font-weight: 600;
}

/* ---- The dark band ----------------------------------------------------- *
 * One artwork behind two sections, which is why the wave canvas and the plate
 * live on the wrapper: giving each section its own restarts the image at the
 * join. Same plate and same artwork as every other dark band on the site (see
 * "The dark wave" in CLAUDE.md), and there is no tint over the canvas, so the
 * sections sit at z-index 1 rather than the 2 a tinted band needs.
 */
.why-dark {
	position: relative;
	overflow: hidden;
	background-color: var(--wave-ground);
	background-image: url("assets/img/wave-purple.jpg");
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
	color: var(--ink-text);
}

.why-dark > .section {
	position: relative;
	z-index: 1;
	background: transparent;
}

/* The two share a band, so the join between them carries one padding, not two. */
.why-dark .why-different {
	padding-bottom: 60px;
}

.why-dark .why-trust-band {
	padding-top: 60px;
}

.why-dark .section__title {
	color: #fff;
}

.why-dark .accent {
	color: #9064f7;
}

.why-different__title {
	text-align: center;
	margin-bottom: 80px;
}

/*
 * The platform page's dark-band grid: the design's 1342 across cards measuring
 * 317.5 and 431.33 on a 24px gutter, which is exactly 3 and 4 columns of twelve
 * with that gutter. Each card carries its own --span, so the 4 + 3 shape lives
 * in page-why.php and not here.
 */
.why-cards {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	grid-auto-rows: 1fr;
	gap: 24px;
	max-width: 1342px;
	margin-inline: auto;
}

/*
 * The fill is translucent so the artwork still shows through it, which is why
 * background-clip keeps it off the border; the design's border is itself a
 * gradient across 1.5px, and flat is indistinguishable at that width while
 * keeping the card to one element.
 */
.why-card {
	grid-column: span var(--span);
	padding: 24px 32px 28px;
	border: 1.5px solid rgba(140, 94, 240, 0.6);
	border-radius: 16px;
	background: linear-gradient(90deg, rgba(78, 35, 178, 0.6), rgba(119, 71, 232, 0.6));
	background-clip: padding-box;
}

.why-card h3 {
	font-family: var(--font-body);
	font-size: 16px;
	font-weight: 600;
	line-height: 24px;
	letter-spacing: normal;
	color: #fff;
	margin: 0 0 8px;
}

.why-card p {
	margin: 0;
	font-size: 15px;
	line-height: 23px;
	color: var(--ink-text);
}

/* ---- "Shared KYB cannot scale without trust" --------------------------- *
 * The design puts the diagram in the left half and the copy in the right, with
 * the copy starting at 971 of the 1470 -- the split below is that ratio.
 */
.why-trust__inner {
	display: grid;
	grid-template-columns: minmax(0, 851fr) minmax(0, 619fr);
	align-items: center;
	gap: 32px;
}

/* The design draws this 510 wide inside a column half again as wide, and it is
   left there: the diagram is legible at that size, and the labels grow to about
   1.45 as their node is reached, which is where the reading actually happens.
   590 rather than 510 because the viewBox carries 40 units of headroom at each
   side for a caption at full size -- see parts/why-trust.php. */
.why-trust__diagram {
	width: 100%;
	max-width: 590px;
	margin-inline: auto;
}

.why-trust {
	display: block;
	width: 100%;
	height: auto;
	overflow: visible;
}

.why-trust__copy .section__title {
	max-width: 620px;
	margin-bottom: 44px;
}

.why-trust__copy p {
	margin: 0;
	font-size: 16px;
	line-height: 28px;
	color: var(--ink-text);
}

.why-trust__copy p + p {
	margin-top: 16px;
}

.why-trust__copy .why-emphasis {
	color: #fff;
	font-weight: 600;
}

.why-trust__hub-name {
	font-family: var(--font-body);
	font-size: 11px;
	font-weight: 700;
	fill: #fff;
	letter-spacing: 0.02em;
}

.why-trust__hub-kind {
	font-family: var(--font-body);
	font-size: 9px;
	fill: rgba(255, 255, 255, 0.9);
}

.why-trust__number {
	font-family: var(--font-body);
	font-size: 9px;
	font-weight: 600;
	fill: #fff;
}

.why-trust__label {
	font-family: var(--font-body);
	font-size: 10px;
	font-weight: 700;
	fill: #fff;
}

.why-trust__note {
	font-family: var(--font-body);
	font-size: 9px;
	fill: #9064f7;
}

/*
 * The node and its caption take the same keyframe timing on the same delay, a
 * quarter-cycle apart, so a node and the words beside it always grow together.
 * The caption's growth is the readable part of this diagram: at the design's
 * 10px and 9px these are legible at the export's own width and not much below
 * it, and each group is anchored on the line nearest its node so growing always
 * moves type away from the diagram.
 */
.why-trust__node,
.why-trust__caption {
	animation-duration: 12s;
	animation-timing-function: linear;
	animation-iteration-count: infinite;
	animation-delay: calc(var(--order) * 3s);
}

.why-trust__node {
	animation-name: why-trust-swell;
}

.why-trust__caption {
	animation-name: why-trust-read;
}

@keyframes why-trust-swell {
	0%,
	5.833% {
		transform: scale(1);
	}

	8.083%,
	11.458% {
		transform: scale(1.45);
	}

	13.333%,
	100% {
		transform: scale(1);
	}
}

@keyframes why-trust-read {
	0%,
	5.833% {
		transform: scale(1);
	}

	8.083%,
	11.458% {
		transform: scale(1.45);
	}

	13.333%,
	100% {
		transform: scale(1);
	}
}

.why-trust__dot {
	visibility: hidden;
}

@supports (offset-path: path("M0 0L1 1")) {
	.why-trust__dot {
		visibility: visible;
		offset-rotate: 0deg;
		offset-path: path("M257.500 142.752 L257.500 79.790 L257.500 142.752 A57.248 57.248 0 0 1 314.748 200.000 L377.710 200.000 L314.748 200.000 A57.248 57.248 0 0 1 257.500 257.248 L257.500 320.210 L257.500 257.248 A57.248 57.248 0 0 1 200.252 200.000 L137.280 200.000 L200.252 200.000 A57.248 57.248 0 0 1 257.500 142.752");
		animation: why-trust-dot 12s infinite;
	}
}

@keyframes why-trust-dot {
	0% { offset-distance: 0%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	5.833% { offset-distance: 7.292%; animation-timing-function: linear; }
	13.333% { offset-distance: 7.292%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	19.167% { offset-distance: 14.584%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	25.000% { offset-distance: 24.999%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	30.833% { offset-distance: 32.292%; animation-timing-function: linear; }
	38.333% { offset-distance: 32.292%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	44.167% { offset-distance: 39.584%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	50.000% { offset-distance: 49.999%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	55.833% { offset-distance: 57.291%; animation-timing-function: linear; }
	63.333% { offset-distance: 57.291%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	69.167% { offset-distance: 64.583%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	75.000% { offset-distance: 74.998%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	80.833% { offset-distance: 82.292%; animation-timing-function: linear; }
	88.333% { offset-distance: 82.292%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	94.167% { offset-distance: 89.585%; animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1); }
	100% { offset-distance: 100%; }
}

/*
 * The moving parts here ARE the diagrams, so this stops them where they stand
 * rather than hiding a layer -- which is the whole reason none of it is SMIL.
 */
@media (prefers-reduced-motion: reduce) {
	.why-exchange__node,
	.why-exchange__dot,
	.why-trust__node,
	.why-trust__caption,
	.why-trust__dot {
		animation: none;
	}

	.why-exchange__dot,
	.why-trust__dot {
		visibility: hidden;
	}
}

/* ---- "Technology matters. Access is the multiplier." ------------------- */
.why-access {
	position: relative;
	background: #eceef1;
}

/* ---- "Built towards perpetual KYB" ------------------------------------- *
 * wave-light.jpg at half strength, the same treatment the solutions page's two
 * light bands take: the canvas paints over the host's background completely, so
 * the wash has to stack ABOVE it as an ::after and the container goes to 2.
 */
.why-perpetual {
	background-color: var(--bg-light);
	background-image: url("assets/img/wave-light.jpg");
}

.why-perpetual::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
	background: rgba(244, 245, 247, 0.5);
	pointer-events: none;
}

.why-perpetual > .container {
	z-index: 2;
}

/*
 * The design's 670 of copy beside 735 of notes. The notes get 760 rather than
 * 735 because the wrap is what draws that block: at the design's own measure the
 * second and third notes come to 742 together here -- eight pixels over, and the
 * row breaks into four singles. 760 is wide enough for that pair and still far
 * short of any other two.
 */
.why-perpetual__inner {
	display: grid;
	grid-template-columns: minmax(0, 645fr) minmax(0, 760fr);
	align-items: start;
	gap: 65px;
}

.why-perpetual__copy .section__title {
	margin-bottom: 40px;
}

.why-perpetual__copy p {
	margin: 0;
	font-size: 16px;
	line-height: 28px;
	color: var(--text-muted);
}

.why-perpetual__copy p + p {
	margin-top: 16px;
}

/*
 * A centred wrapping row, not a grid: at the design's 735 measure the four
 * notes fall 1 + 2 + 1, because the second and third are the only pair that
 * fits a line together. A grid would strand them in columns.
 */
.why-notes {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 24px;
	max-width: 760px;
	margin: 0;
	padding: 0;
	list-style: none;
}

.why-note {
	display: flex;
	align-items: center;
	gap: 18px;
	min-height: 71px;
	padding: 24px 33px;
	border-radius: 12px;
	background: #fff;
	box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1);
	font-size: 16px;
	line-height: 23px;
	color: #50545c;
}

.why-note .i-check {
	flex: none;
	width: 21px;
	height: 21px;
}

@media (max-width: 1120px) {
	.why-hero__inner,
	.why-trust__inner,
	.why-perpetual__inner {
		grid-template-columns: minmax(0, 1fr);
		gap: 56px;
	}

	.why-hero__title,
	.why-hero__lead,
	.why-trust__copy .section__title {
		max-width: none;
	}

	/* Both diagrams are drawn at their design size; on one column they have a
	   whole container to fill, which is more than either wants. */
	.why-hero__diagram,
	.why-trust__diagram {
		max-width: 652px;
		margin-inline: auto;
	}

	/*
	 * Stacked, the notes stop wrapping to their own text widths -- which left
	 * four cards of four different sizes -- and sit on a grid of equal columns,
	 * two here and one on a phone.
	 */
	.why-notes {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		max-width: none;
	}
}

@media (max-width: 960px) {
	/*
	 * On a narrow screen the dark band drops its artwork altogether and is the
	 * flat --wave-ground plate. Two parts to that. The canvas is hidden, and
	 * wave-bg.js stops drawing into a canvas nobody can see. The still image is
	 * covered by a plate of its own ground colour rather than removed with
	 * background-image: none -- the script reads the image's URL off that
	 * property once, on load, so a tablet that loaded narrow and was then
	 * turned past 960px would never animate again.
	 */
	.why-dark > .wave-bg {
		display: none;
	}

	.why-dark::before {
		content: "";
		position: absolute;
		inset: 0;
		background: var(--wave-ground);
	}

	.page-why .section {
		padding: 80px 0;
	}

	.why-hero__inner {
		padding-top: 148px;
		padding-bottom: 80px;
	}

	.why-cards {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	/* The twelve-column spans mean nothing once there are two columns. */
	.why-card {
		grid-column: span 1;
	}

	.why-different__title {
		margin-bottom: 48px;
	}
}

@media (max-width: 720px) {
	.why-cards {
		grid-template-columns: minmax(0, 1fr);
	}

	.why-card {
		padding: 24px;
	}

	.why-notes {
		grid-template-columns: minmax(0, 1fr);
	}

	.why-note {
		padding: 20px 24px;
	}
}

/* ============================================================= *
 * Resources: the posts index, the archives and an article
 * ============================================================= *
 * The only part of this site whose words come out of wp_posts. Everything else
 * is copy in a template, which is what lets update-theme.sh be the whole
 * deploy; this is the one place an editor's markup lands on the page, and the
 * rules below are the only ones in the stylesheet dressing markup the theme did
 * not write (.entry-content, at the bottom).
 *
 * Three templates share what is here. page-resources.php lists the posts,
 * index.php wears the same head band and card grid for the views nobody
 * designed -- category and tag archives, search, 404 -- and single.php is the
 * article. The card itself is parts/resource-card.php, one file for all three.
 *
 * A card with no featured image falls back to the dark wave rather than to an
 * empty box. It is the same artwork every dark band on the site carries, so a
 * thin index still reads as this site; it is a flat background here rather than
 * a .wave-bg canvas, because a grid of nine animated canvases would cost more
 * than the movement is worth on a card 300px wide.
 * ============================================================= */
.resources-head {
	padding-bottom: 64px;
}

.resources-head__title {
	font-size: clamp(2.2rem, 4.4vw, 4rem);
	font-weight: 400;
}

.resources-list {
	padding-top: 64px;
}

.resource-grid {
	display: grid;
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: 32px;
}

.resource-card {
	display: flex;
	background: var(--bg);
	border: 1px solid var(--border);
	border-radius: var(--radius);
	overflow: hidden;
	transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}

.resource-card:hover {
	border-color: var(--purple-flat);
	box-shadow: 0 18px 40px rgba(29, 8, 55, 0.1);
	transform: translateY(-2px);
}

/* The whole card is the link (see parts/resource-card.php), so the anchor is
   what has to be the column -- not a wrapper inside it. */
.resource-card__link {
	display: flex;
	flex-direction: column;
	width: 100%;
	color: inherit;
	text-decoration: none;
}

.resource-card__link:hover {
	text-decoration: none;
}

.resource-card__media {
	position: relative;
	aspect-ratio: 16 / 9;
	background: var(--bg-light);
	overflow: hidden;
}

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

/*
 * No featured image: the site's own dark wave, cropped by the card. --wave-ground
 * sits under it for the same reason every dark band declares it -- the artwork
 * fades out to exactly that colour, so a crop that runs past the wave meets no
 * edge (see "The dark wave").
 */
.resource-card__media--blank {
	background-color: var(--wave-ground);
	background-image: url("assets/img/wave-purple.jpg");
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
}

.resource-card__body {
	display: flex;
	flex-direction: column;
	flex: 1;
	padding: 24px 24px 26px;
}

.resource-card__meta {
	margin: 0 0 0.6rem;
	font-size: 0.8rem;
	color: var(--text-muted);
	display: flex;
	align-items: center;
	gap: 0.4rem;
}

.resource-card__dot {
	color: var(--border);
}

/* A card heading, not a display title: at 20px the site's 400 stops reading as
   a heading, which is the same reason .team-card__name and .faq-item h3 keep
   their weight. */
.resource-card__title {
	font-size: 1.25rem;
	font-weight: 600;
	line-height: 1.3;
	margin: 0 0 0.7rem;
}

.resource-card__excerpt {
	margin: 0 0 1.25rem;
	font-size: 0.94rem;
	line-height: 1.7;
	color: var(--text-muted);
}

/* Pushed to the bottom of the card so the row of links is level however
   different the excerpts above them are. */
.resource-card__more {
	margin-top: auto;
	display: inline-flex;
	align-items: center;
	gap: 0.45rem;
	font-family: var(--font-head);
	font-weight: 600;
	font-size: 0.94rem;
	color: var(--purple-flat);
}

.resource-card__more .i-arrow {
	transition: transform 0.15s ease;
}

.resource-card:hover .resource-card__more .i-arrow {
	transform: translateX(3px);
}

.resources-empty {
	max-width: 640px;
	margin-inline: auto;
	text-align: center;
}

.resources-empty p {
	color: var(--text-muted);
	margin-bottom: 1.75rem;
}

/* ------------------------------------------------------------- *
 * Pagination
 *
 * page-resources.php builds its own with paginate_links() because its posts do
 * not come out of the main query; index.php lets the_posts_pagination() do it,
 * because there they do. Both are asked for the plain form inside .nav-links,
 * so one rule dresses the two.
 * ------------------------------------------------------------- */
.resource-pagination {
	margin-top: 56px;
}

.resource-pagination .nav-links {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: center;
	gap: 0.5rem;
}

.resource-pagination .page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 42px;
	height: 42px;
	padding: 0 0.75rem;
	border: 1px solid var(--border);
	border-radius: 999px;
	font-family: var(--font-head);
	font-weight: 500;
	font-size: 0.94rem;
	color: var(--heading);
}

.resource-pagination a.page-numbers:hover {
	border-color: var(--purple-flat);
	color: var(--purple-flat);
	text-decoration: none;
}

.resource-pagination .page-numbers.current {
	background: var(--purple-flat);
	border-color: var(--purple-flat);
	color: #fff;
}

.resource-pagination .page-numbers.dots {
	border-color: transparent;
	min-width: 0;
	padding: 0 0.25rem;
}

/* the_posts_pagination() prints a heading for screen readers only; the theme's
   own name for that is .sr-only, and this is the alias WordPress expects. */
.screen-reader-text {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	border: 0;
}

/* ------------------------------------------------------------- *
 * A single article
 * ------------------------------------------------------------- */
.post-head {
	padding-bottom: 56px;
}

.post-head__inner {
	max-width: 860px;
	margin-inline: auto;
	text-align: center;
}

.post-head__back {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	font-family: var(--font-head);
	font-weight: 500;
	font-size: 0.9rem;
	color: var(--purple-flat);
	margin-bottom: 1.5rem;
}

.post-head__meta {
	display: flex;
	justify-content: center;
	align-items: center;
	gap: 0.4rem;
	margin: 0 0 0.9rem;
	font-size: 0.85rem;
	color: var(--text-muted);
}

/* A display title, so 400 -- the site-wide weight every hero and section title
   carries (see "Fonts" in CLAUDE.md). */
.post-head__title {
	font-size: clamp(2rem, 3.8vw, 3.2rem);
	font-weight: 400;
}

.post-head__lead {
	margin: 1.25rem 0 0;
	font-size: 1.15rem;
	line-height: 1.65;
	color: var(--text-muted);
}

/* The featured image straddles the seam between the light head band and the
   white body, which is what stops the article opening on two flat bands. */
.post-figure {
	background: linear-gradient(180deg, var(--bg-light) 0, var(--bg-light) 120px, var(--bg) 120px, var(--bg) 100%);
}

.post-figure > .container {
	max-width: 1020px;
}

/*
 * Cropped to a fixed band rather than left at its own proportions: a featured
 * image is whatever an editor uploaded, and a tall one would otherwise push the
 * article's first paragraph most of a screen down the page.
 */
.post-figure img {
	width: 100%;
	aspect-ratio: 16 / 9;
	object-fit: cover;
	border-radius: var(--radius);
}

.post-body {
	padding-top: 56px;
}

.post-nav {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 24px;
	max-width: 820px;
	margin: 72px auto 0;
	padding-top: 32px;
	border-top: 1px solid var(--border);
}

.post-nav__link {
	display: flex;
	flex-direction: column;
	gap: 0.35rem;
	color: var(--heading);
}

.post-nav__link:hover {
	text-decoration: none;
}

.post-nav__link:hover .post-nav__title {
	color: var(--purple-flat);
}

/* One column is missing whenever the article is first or last, and the
   remaining one has to stay on its own side of the rule. */
.post-nav__link--next {
	grid-column: 2;
	text-align: right;
}

.post-nav__label {
	font-size: 0.8rem;
	color: var(--text-muted);
}

.post-nav__title {
	font-family: var(--font-head);
	font-weight: 600;
	font-size: 1rem;
	line-height: 1.35;
	transition: color 0.15s ease;
}

/* ------------------------------------------------------------- *
 * .entry-content
 *
 * The editor's own markup. This is the only block in the stylesheet that has to
 * dress elements the theme did not write, so it names them all rather than
 * relying on a class -- an editor gets no say in what classes the block editor
 * emits, and a post written in 2027 has to land on its feet.
 * ------------------------------------------------------------- */
.entry-content {
	max-width: 820px;
	margin-inline: auto;
	font-size: 1.05rem;
	line-height: 1.8;
}

.entry-content > *:first-child {
	margin-top: 0;
}

.entry-content p {
	margin: 0 0 1.4rem;
}

.entry-content h2 {
	font-size: 1.9rem;
	font-weight: 400;
	margin: 2.75rem 0 1rem;
}

.entry-content h3 {
	font-size: 1.3rem;
	font-weight: 600;
	margin: 2.25rem 0 0.75rem;
}

.entry-content h4 {
	font-family: var(--font-head);
	color: var(--heading);
	font-size: 1.1rem;
	font-weight: 600;
	line-height: 1.3;
	letter-spacing: -0.02em;
	margin: 2rem 0 0.6rem;
}

.entry-content a {
	color: var(--purple-flat);
	text-decoration: underline;
	text-underline-offset: 0.18em;
}

.entry-content ul,
.entry-content ol {
	margin: 0 0 1.4rem;
	padding-left: 1.4rem;
}

.entry-content li {
	margin-bottom: 0.5rem;
}

.entry-content li::marker {
	color: var(--purple-flat);
}

.entry-content blockquote {
	margin: 2rem 0;
	padding: 0.25rem 0 0.25rem 1.5rem;
	border-left: 3px solid var(--purple-flat);
	color: var(--heading);
	font-size: 1.15rem;
	line-height: 1.7;
}

.entry-content blockquote p:last-child {
	margin-bottom: 0;
}

.entry-content img,
.entry-content video,
.entry-content iframe {
	max-width: 100%;
	border-radius: var(--radius-sm);
}

.entry-content figure {
	margin: 2rem 0;
}

/*
 * An illustration narrower than the column sits in the middle of it rather than
 * against the left edge. The diagram exports carry their own intrinsic width
 * (424-643) against an 820 measure, so without this every one of them looks
 * accidentally ranged left. The base `img` rule is display: block, which is why
 * this is a margin and not text-align.
 */
.entry-content figure img {
	margin-inline: auto;
}

.entry-content figcaption {
	margin-top: 0.65rem;
	font-size: 0.85rem;
	color: var(--text-muted);
	text-align: center;
}

.entry-content hr {
	margin: 2.75rem 0;
	border: 0;
	border-top: 1px solid var(--border);
}

.entry-content code {
	padding: 0.15em 0.4em;
	border-radius: 6px;
	background: var(--bg-light);
	font-size: 0.9em;
}

/* A code block scrolls inside itself; the page never scrolls sideways. */
.entry-content pre {
	margin: 0 0 1.4rem;
	padding: 20px 24px;
	border-radius: var(--radius-sm);
	background: var(--ink);
	color: var(--ink-text);
	overflow-x: auto;
	font-size: 0.9rem;
	line-height: 1.6;
}

.entry-content pre code {
	padding: 0;
	background: none;
	color: inherit;
}

/* Same rule for a wide table: the wrapper scrolls, not the document. */
.entry-content table {
	width: 100%;
	margin: 0 0 1.4rem;
	border-collapse: collapse;
	font-size: 0.95rem;
}

.entry-content th,
.entry-content td {
	padding: 0.7rem 0.9rem;
	border: 1px solid var(--border);
	text-align: left;
}

.entry-content th {
	background: var(--bg-light);
	color: var(--heading);
	font-weight: 600;
}

.entry-content .wp-block-table {
	overflow-x: auto;
}

.entry-content .alignwide {
	max-width: none;
}

.entry-content .aligncenter {
	margin-inline: auto;
}

@media (max-width: 1120px) {
	.resource-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (max-width: 720px) {
	.resource-grid {
		grid-template-columns: minmax(0, 1fr);
		gap: 24px;
	}

	.resources-head,
	.post-head {
		padding-bottom: 40px;
	}

	.resources-list,
	.post-body {
		padding-top: 40px;
	}

	.post-nav {
		grid-template-columns: minmax(0, 1fr);
		margin-top: 48px;
	}

	/* One column, so the second link stops being a right-hand one. */
	.post-nav__link--next {
		grid-column: 1;
		text-align: left;
	}

	.entry-content {
		font-size: 1rem;
	}

	.entry-content h2 {
		font-size: 1.55rem;
	}
}

/* ============================================================= *
 * Cookie bar
 * ============================================================= *
 * Printed by consent.php and shown by assets/js/consent.js. It floats over
 * the bottom of the page rather than pushing it up, so showing or hiding it
 * moves no layout. It sits above the sticky header's z-index for the case of a
 * short viewport where the two could meet.
 *
 * Reject and Accept share one style on purpose: a refusal that is less
 * prominent than acceptance does not count as a free choice (see consent.php).
 * Nothing here sets `display` on .cookie-bar itself, which is what lets the
 * `hidden` attribute hide it.
 */
.cookie-bar {
	position: fixed;
	left: 16px;
	right: 16px;
	bottom: calc(16px + env(safe-area-inset-bottom, 0px));
	z-index: 60;
	max-width: 1040px;
	margin-inline: auto;
	border-radius: var(--radius);
	/* The hairline is for the page's end, where the bar lies over the dark
	   footer band and its own ground would otherwise vanish into it. */
	border: 1px solid rgba(255, 255, 255, 0.14);
	background: var(--ink);
	color: var(--ink-text);
	box-shadow: 0 12px 40px rgba(16, 4, 31, 0.35);
}

.cookie-bar__inner {
	display: flex;
	align-items: center;
	gap: 16px 32px;
	padding: 20px 24px;
}

.cookie-bar__text {
	flex: 1;
	margin: 0;
	font-size: 0.92rem;
	line-height: 1.55;
}

.cookie-bar__text a {
	color: #fff;
	text-decoration: underline;
	text-underline-offset: 0.18em;
}

.cookie-bar__actions {
	display: flex;
	gap: 12px;
	flex-shrink: 0;
}

.cookie-bar__btn {
	justify-content: center;
	min-width: 7rem;
	background: transparent;
	color: #fff;
	border-color: rgba(255, 255, 255, 0.55);
}

.cookie-bar__btn:hover {
	background: #fff;
	color: var(--heading);
	filter: none;
}

@media (max-width: 720px) {
	.cookie-bar__inner {
		flex-direction: column;
		align-items: stretch;
		padding: 18px 20px;
	}

	.cookie-bar__actions {
		display: grid;
		grid-template-columns: 1fr 1fr;
	}

	.cookie-bar__btn {
		min-width: 0;
	}
}
