/*
 * Softliro — section components.
 *
 * The pieces the Elementor widgets and the section template parts render.
 * Loaded only when one of them is on the page.
 */

/* ----------------------------------------------------------------- Hero */

.sl-hero__grid {
	display: grid;
	grid-template-columns: 1.15fr 0.85fr;
	gap: 56px;
	align-items: center;
	padding-block: calc(var(--sl-section) * 0.9) calc(var(--sl-section) * 0.7);
}

.sl-hero__eyebrow {
	display: inline-flex;
	align-items: center;
	gap: 10px;
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-radius: var(--sl-radius);
	padding: 6px 10px;
}

.sl-hero__heading {
	margin: 28px 0 0;
	font-size: var(--sl-h1);
	line-height: 1.02;
	max-width: 15ch;
}

.sl-hero__lead {
	margin: 26px 0 0;
	max-width: 52ch;
	color: var(--sl-text-soft);
}

/*
 * The intro column of a split hero. Capped rather than left to fill its half,
 * so the measure stays readable when the column is wide — the same reason the
 * lead paragraph carries a max-width everywhere else.
 */
/*
 * The column widths are authored in Elementor (55/45), so nothing is capped
 * here — a max-width on the column would strand its slack and pull the form
 * back off the section edge, which is what it used to do.
 *
 * The measure is held on the prose instead. The column can be 720px wide and
 * still hand the reader a comfortable line, while the heading and the contact
 * rules underneath take the column's full width, which is what makes them read
 * as a set.
 *
 * Capped on the paragraph, not on the widget: Elementor sets
 * `.elementor.elementor .e-con > .elementor-widget { max-width: 100% }` at
 * 0-4-0 from its own stylesheet, so a class on the widget never lands. Nothing
 * competes for the `p`.
 */
.sl-contact-intro .elementor-widget-text-editor p {
	max-width: 54ch;
}

.sl-hero__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
	margin-top: 36px;
}

.sl-hero__status {
	margin-top: 28px;
}

.sl-hero__figure {
	position: relative;
}

.sl-hero__plate {
	position: relative;
	aspect-ratio: 1 / 1;
	display: grid;
	place-items: center;
	padding: 12%;
	background: var(--sl-surface-2);
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-radius: var(--sl-radius);
	overflow: hidden;
}

.sl-hero__image {
	width: 100%;
	border-radius: var(--sl-radius);
}

.sl-hero__caption {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	gap: 6px 18px;
	margin-top: 14px;
	font-family: var(--sl-font-mono);
	font-size: 10px;
	letter-spacing: var(--sl-label-tracking);
	text-transform: uppercase;
	color: var(--sl-muted);
}

.sl-hero__caption-meta {
	color: var(--sl-text);
}

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

.sl-cta__inner {
	position: relative;
	overflow: hidden;
	padding: 80px 60px;
	background: var(--sl-surface);
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-radius: var(--sl-radius);
}

.sl-cta--flat .sl-cta__inner {
	background: none;
	border: 0;
	border-top: var(--sl-border-w) solid var(--sl-hairline);
	border-radius: 0;
	padding-inline: 0;
}

.sl-cta__stroke {
	position: absolute;
	right: -40px;
	bottom: -40px;
	width: 460px;
	color: var(--sl-hairline);
	pointer-events: none;
}

/*
 * Corner stroke — the two-path flow that runs out of the bottom-right of a
 * panel.
 *
 * Painted as a mask rather than as an inline SVG or a coloured data URI, for
 * two reasons: it can sit on any Elementor container without adding a widget
 * inside it, and the colour stays on --sl-text instead of being frozen as a
 * hex, so a palette change carries it. The second path is masked at .66 so the
 * two curves keep the depth they have in the design.
 */
.sl-stroke-corner {
	position: relative;
	overflow: hidden;
}

/* Content has to outrank the decoration, which paints after it in source. */
.sl-stroke-corner > * {
	position: relative;
	z-index: 1;
}

.sl-stroke-corner::after {
	content: "";
	position: absolute;
	right: -40px;
	bottom: -60px;
	z-index: 0;
	width: 520px;
	aspect-ratio: 2 / 1;
	background-color: var(--sl-text);
	opacity: 0.09;
	pointer-events: none;
	-webkit-mask-image: var(--sl-stroke-corner-mask);
	mask-image: var(--sl-stroke-corner-mask);
	-webkit-mask-repeat: no-repeat;
	mask-repeat: no-repeat;
	-webkit-mask-size: 100% 100%;
	mask-size: 100% 100%;
}

:root {
	--sl-stroke-corner-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 600 300' fill='none'%3E%3Cpath d='M40 260C40 160 300 220 320 140 340 60 480 120 560 40' stroke='%23fff' stroke-width='9' stroke-linecap='round'/%3E%3Cpath d='M40 290C40 190 300 250 320 170 340 90 480 150 560 70' stroke='%23fff' stroke-opacity='.66' stroke-width='9' stroke-linecap='round'/%3E%3C/svg%3E");
}

@media (max-width: 782px) {
	.sl-stroke-corner::after {
		width: 320px;
		right: -60px;
		bottom: -40px;
	}
}

.sl-cta__body {
	position: relative;
	max-width: 60ch;
}

.sl-cta__eyebrow {
	color: var(--sl-signal);
}

.sl-cta__heading {
	margin: 20px 0 18px;
	font-size: var(--sl-h1);
	max-width: 16ch;
}

.sl-cta__lead {
	margin: 0;
	max-width: 52ch;
	color: var(--sl-text-soft);
}

.sl-cta__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
	margin-top: 36px;
}

/* ------------------------------------------------------------- Services */

.sl-services {
	display: grid;
	grid-template-columns: repeat(var(--sl-services-columns, 2), minmax(0, 1fr));
	gap: var(--sl-border-w);
	background: var(--sl-hairline);
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-radius: var(--sl-radius);
	overflow: hidden;
}

.sl-service {
	display: flex;
	flex-direction: column;
	min-height: 280px;
	padding: 38px 34px 34px;
	background: var(--sl-surface);
	color: var(--sl-text);
	transition: background-color var(--sl-duration) var(--sl-ease);
}

a.sl-service:hover,
a.sl-service:focus-visible {
	background: var(--sl-surface-2);
	color: var(--sl-text);
}

.sl-service__top {
	display: flex;
	justify-content: space-between;
	align-items: flex-start;
	gap: 16px;
}

.sl-service__code {
	font-size: 10px;
	text-transform: none;
}

.sl-service__code--signal {
	color: var(--sl-signal);
}

.sl-service__title {
	margin: 44px 0 10px;
	font-family: var(--sl-font-display);
	font-weight: 700;
	font-size: 26px;
	letter-spacing: -0.03em;
	line-height: 1.15;
}

.sl-service__desc {
	margin: 0;
	color: var(--sl-text-soft);
	max-width: 42ch;
}

.sl-service__cta {
	margin-top: auto;
	padding-top: 24px;
	color: var(--sl-link);
}

/*
 * The featured tile behaves differently per ground, because the mockups do.
 * On Ink it is a violet-tinted plate. On Bone it inverts to Ink outright — a
 * stronger move that has to carry its text with it, which is why this is a
 * scheme-scoped block rather than one shared rule.
 */
.sl-service--featured {
	background: color-mix(in srgb, var(--sl-primary) 12%, var(--sl-surface));
}

a.sl-service--featured:hover {
	background: color-mix(in srgb, var(--sl-primary) 20%, var(--sl-surface));
}

.sl-service--featured .sl-service__cta {
	color: var(--sl-signal);
}

.sl-scheme-light .sl-service--featured {
	background: var(--sl-text);
	color: var(--sl-bg);
}

.sl-scheme-light a.sl-service--featured:hover {
	background: color-mix(in srgb, var(--sl-text) 93%, var(--sl-bg));
}

.sl-scheme-light .sl-service--featured .sl-service__desc {
	color: rgba(var(--sl-bg-rgb), 0.72);
}

.sl-scheme-light .sl-service--featured .sl-icon {
	color: var(--sl-bg);
}

/*
 * Inside an inverted band every component already reads the flipped tokens, so
 * the featured tile must not invert a second time — it is on Ink either way.
 */
.sl-scheme-light .sl-invert .sl-service--featured {
	background: color-mix(in srgb, var(--sl-primary) 12%, var(--sl-surface));
	color: var(--sl-text);
}

/* ----------------------------------------------------------- Statistics */

.sl-stats {
	display: grid;
	grid-template-columns: repeat(var(--sl-stats-columns, 4), minmax(0, 1fr));
}

.sl-stats--ruled {
	border-block: var(--sl-border-w) solid var(--sl-hairline);
}

.sl-stat {
	padding: 26px 24px;
}

.sl-stats--ruled .sl-stat + .sl-stat {
	border-left: var(--sl-border-w) solid var(--sl-hairline);
}

.sl-stats--ruled .sl-stat:first-child {
	padding-left: 0;
}

.sl-stats--ruled .sl-stat:last-child {
	padding-right: 0;
}

.sl-stat__value {
	font-family: var(--sl-font-display);
	font-weight: 700;
	font-size: 36px;
	letter-spacing: -0.03em;
	line-height: 1;
	font-variant-numeric: tabular-nums;
}

.sl-stat__label {
	margin-top: 8px;
	font-size: 10px;
}

.sl-stat__desc {
	margin: 10px 0 0;
	font-size: 15px;
	line-height: 1.45;
	color: var(--sl-text-soft);
}

/* -------------------------------------------------------------- Process */

.sl-process {
	position: relative;
}

.sl-process__thread {
	width: 100%;
	height: 84px;
	margin-bottom: 8px;
}

.sl-process__list {
	display: grid;
	grid-template-columns: repeat(var(--sl-process-steps, 7), minmax(0, 1fr));
	gap: 14px;
	list-style: none;
	margin: 0;
	padding: 0;
	counter-reset: none;
}

.sl-process__index {
	font-size: 10px;
}

.sl-process__title {
	margin: 14px 0 6px;
	font-family: var(--sl-font-display);
	font-weight: 700;
	font-size: 18px;
	letter-spacing: -0.02em;
}

.sl-process__desc {
	margin: 0;
	font-size: 15px;
	line-height: 1.45;
	color: var(--sl-text-soft);
}

.sl-process__step--terminal .sl-process__index {
	color: var(--sl-signal);
}

.sl-process--vertical .sl-process__list {
	grid-template-columns: minmax(0, 1fr);
	gap: 0;
}

.sl-process--vertical .sl-process__step {
	position: relative;
	padding: 0 0 32px 32px;
	border-left: 2px solid var(--sl-hairline-strong);
}

.sl-process--vertical .sl-process__step:last-child {
	border-left-color: transparent;
	padding-bottom: 0;
}

.sl-process--vertical .sl-process__step::before {
	content: "";
	position: absolute;
	left: -6px;
	top: 4px;
	width: 10px;
	height: 10px;
	border-radius: 50%;
	background: var(--sl-bg);
	border: 2px solid var(--sl-hairline-strong);
}

.sl-process--vertical .sl-process__step--terminal::before {
	background: var(--sl-signal);
	border-color: var(--sl-signal);
}

/* -------------------------------------------------------------- Marquee */

.sl-marquee {
	overflow: hidden;
	padding-block: 22px;
}

.sl-marquee--ruled {
	border-block: var(--sl-border-w) solid var(--sl-hairline);
}

.sl-marquee__track {
	display: flex;
	width: max-content;
	animation: sl-marquee 34s linear infinite;
}

.sl-marquee:hover .sl-marquee__track,
.sl-marquee:focus-within .sl-marquee__track {
	animation-play-state: paused;
}

.sl-marquee__group {
	display: flex;
	align-items: center;
	gap: 56px;
	padding-right: 56px;
	font-family: var(--sl-font-mono);
	font-size: 12px;
	letter-spacing: var(--sl-label-tracking);
	text-transform: uppercase;
	color: var(--sl-muted);
}

.sl-marquee__logo {
	height: 26px;
	width: auto;
	opacity: 0.65;
	filter: grayscale(1);
}

/* A theme mark in front of the name, for a strip of tools. */
.sl-marquee__item--icon {
	display: inline-flex;
	align-items: center;
	gap: 12px;
	white-space: nowrap;
}

.sl-marquee__icon {
	flex: none;
	color: var(--sl-link);
}

/*
 * The stack strip on a service page: names in the display face rather than the
 * mono utility label, so a tool reads as a name, and the ends fade out rather
 * than cutting a logo in half at the container edge.
 */
.sl-stack-marquee .sl-marquee__group {
	gap: 44px;
	padding-right: 44px;
	font-family: var(--sl-font-display);
	font-size: 22px;
	font-weight: 700;
	letter-spacing: -0.01em;
	text-transform: none;
	color: var(--sl-text);
}

/* The mark scales with the name it sits beside, overriding the SVG attrs. */
.sl-stack-marquee .sl-marquee__icon {
	width: 24px;
	height: 24px;
}

.sl-stack-marquee .sl-marquee {
	padding-block: 28px;
	mask-image: linear-gradient(90deg, transparent 0, #000 8%, #000 92%, transparent 100%);
}

@media (max-width: 700px) {
	.sl-stack-marquee .sl-marquee__group {
		gap: 34px;
		padding-right: 34px;
		font-size: 19px;
	}
}

@keyframes sl-marquee {
	from {
		transform: translateX(0);
	}

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

@media (prefers-reduced-motion: reduce) {
	.sl-marquee__track {
		animation: none;
		flex-wrap: wrap;
	}

	.sl-marquee {
		overflow-x: auto;
	}
}

/* ------------------------------------------------------ Work filmstrip */

/*
 * A strip of project shots that never stops moving. Same two-pass trick as the
 * marquee above: the track holds the list twice and travels exactly half its
 * width, so the point it loops back to is pixel-identical to the point it
 * left.
 */
.sl-shots {
	position: relative;
	overflow: hidden;
	/*
	 * Cards fade out at the two edges they travel through, so nothing starts
	 * or stops against a hard vertical line. The gradient runs at 90deg and so
	 * is uniform top to bottom: this softens the left and right only, and the
	 * shots keep their full contrast everywhere in between.
	 *
	 * A mask rather than a gradient overlay, because it takes whatever sits
	 * behind the strip rather than having to be told the page colour. An
	 * earlier version of this read as a dark wash purely because there was a
	 * panel behind it at the time, and the cards were dissolving into that
	 * instead of into the page.
	 */
	--sl-shots-fade: 7%;
	-webkit-mask-image: linear-gradient(90deg, transparent 0, #000 var(--sl-shots-fade), #000 calc(100% - var(--sl-shots-fade)), transparent 100%);
	mask-image: linear-gradient(90deg, transparent 0, #000 var(--sl-shots-fade), #000 calc(100% - var(--sl-shots-fade)), transparent 100%);
	/*
	 * overflow clips at the padding box, so the block padding is what gives
	 * each card's shadow somewhere to fall. Without it the shadow would be
	 * sliced off flat against the top and bottom of the strip.
	 */
	padding-block: 24px 30px;
	--sl-shot-radius: 14px;
}

.sl-shots__track {
	display: flex;
	width: max-content;
	animation: sl-shots-right var(--sl-shots-speed, 70s) linear infinite;
}

.sl-shots--left .sl-shots__track {
	animation-name: sl-shots-left;
}

/* Anyone reaching for a project should be able to stop it first. */
.sl-shots:hover .sl-shots__track,
.sl-shots:focus-within .sl-shots__track {
	animation-play-state: paused;
}

.sl-shots__group {
	display: flex;
	gap: 20px;
	padding-right: 20px;
}

.sl-shots__card {
	display: block;
	flex: none;
	width: var(--sl-shot-w, 480px);
	color: inherit;
	text-decoration: none;
}

.sl-shots__card picture {
	display: block;
}

/*
 * Two classes, not one. Elementor ships `.elementor img { border: none;
 * border-radius: 0 }` and a single class loses to it, so the corners and the
 * hairline never rendered at all until this outranked it.
 */
.sl-shots .sl-shots__img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-radius: var(--sl-shot-radius, 14px);
	background: var(--sl-surface);
	/* The shots carry their own depth now that nothing sits behind them. */
	box-shadow: 0 18px 36px rgb(0 0 0 / 0.5);
}

.sl-shots .sl-shots__card:hover .sl-shots__img,
.sl-shots .sl-shots__card:focus-visible .sl-shots__img {
	border-color: var(--sl-signal);
}

.sl-shots__caption {
	display: block;
	margin-top: 10px;
	font-family: var(--sl-font-mono);
	font-size: 11px;
	letter-spacing: var(--sl-label-tracking);
	text-transform: uppercase;
	color: var(--sl-muted);
}

.sl-shots__card:hover .sl-shots__caption {
	color: var(--sl-text);
}

@keyframes sl-shots-right {
	from {
		transform: translateX(-50%);
	}

	to {
		transform: translateX(0);
	}
}

@keyframes sl-shots-left {
	from {
		transform: translateX(0);
	}

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

@media (max-width: 1180px) {
	.sl-shots {
		--sl-shot-w: 400px;
	}
}

@media (max-width: 782px) {
	.sl-shots {
		--sl-shot-w: 280px;
	}
}

/*
 * With motion turned down the strip stops being an animation and becomes an
 * ordinary scroller: the work is still all reachable, it just waits to be
 * asked. The mask goes too, so nothing is dimmed at the edges of something a
 * person is now driving themselves.
 */
@media (prefers-reduced-motion: reduce) {
	.sl-shots__track {
		animation: none;
	}

	.sl-shots {
		overflow-x: auto;
		-webkit-mask-image: none;
		mask-image: none;
	}
}

/* --------------------------------------------------------- System panel */

.sl-panel {
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-radius: var(--sl-radius);
	background: var(--sl-surface-2);
	padding: 28px;
}

.sl-panel__head {
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: 16px;
}

.sl-panel__rows {
	display: grid;
	gap: var(--sl-border-w);
	background: var(--sl-hairline);
	border-radius: var(--sl-radius);
	overflow: hidden;
	margin-top: 22px;
}

.sl-panel__row {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 16px;
	padding: 16px 18px;
	background: var(--sl-surface);
}

.sl-panel__name {
	font-family: var(--sl-font-mono);
	font-size: 12px;
	letter-spacing: 0.06em;
	color: var(--sl-text-soft);
}

.sl-panel__value {
	font-family: var(--sl-font-display);
	font-weight: 700;
	font-size: 20px;
	letter-spacing: -0.02em;
	font-variant-numeric: tabular-nums;
}

.sl-panel__note {
	margin: 20px 0 0;
	font-size: 16px;
	color: var(--sl-text-soft);
}

.sl-panel .sl-link-arrow {
	margin-top: 14px;
}

/* ------------------------------------------------------------------ FAQ */

.sl-faq {
	border-top: var(--sl-border-w) solid var(--sl-hairline-strong);
}

.sl-faq__item {
	border-bottom: var(--sl-border-w) solid var(--sl-hairline-strong);
}

.sl-faq__question {
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: 24px;
	padding: 24px 0;
	cursor: pointer;
	font-family: var(--sl-font-ui);
	font-size: 19px;
	font-weight: 500;
	line-height: 1.35;
	list-style: none;
}

.sl-faq__question::-webkit-details-marker {
	display: none;
}

.sl-faq__sign {
	position: relative;
	flex: none;
	width: 14px;
	height: 14px;
}

.sl-faq__sign::before,
.sl-faq__sign::after {
	content: "";
	position: absolute;
	background: var(--sl-link);
	border-radius: 2px;
	transition: transform var(--sl-duration) var(--sl-ease);
}

.sl-faq__sign::before {
	inset: 6px 0 auto;
	height: 2px;
}

.sl-faq__sign::after {
	inset: 0 6px auto;
	width: 2px;
	height: 14px;
}

.sl-faq__item[open] .sl-faq__sign::after {
	transform: scaleY(0);
}

.sl-faq__answer {
	padding: 0 60px 26px 0;
	color: var(--sl-text-soft);
	max-width: 68ch;
}

/* ---------------------------------------------------------- First run */

.sl-firstrun {
	max-width: 60ch;
}

.sl-firstrun__title {
	font-size: var(--sl-h2);
	margin: 20px 0 18px;
}

.sl-firstrun__text {
	color: var(--sl-text-soft);
	margin-bottom: 32px;
}

.sl-firstrun__actions {
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
}

.sl-firstrun__stroke {
	width: 260px;
	margin-top: 64px;
	color: var(--sl-hairline-strong);
}

/* --------------------------------------------------- Scroll reveal, draw */

.sl-reveal {
	opacity: 0;
	transform: translateY(14px);
	transition:
		opacity 520ms var(--sl-ease),
		transform 520ms var(--sl-ease);
}

.sl-reveal.is-visible {
	opacity: 1;
	transform: none;
}

.sl-stroke--draw path {
	stroke-dasharray: var(--sl-stroke-length, 1200);
	stroke-dashoffset: var(--sl-stroke-length, 1200);
}

.sl-stroke--draw.is-visible path {
	animation: sl-draw 2.4s var(--sl-ease) forwards;
}

@keyframes sl-draw {
	to {
		stroke-dashoffset: 0;
	}
}

@media (prefers-reduced-motion: reduce) {
	.sl-reveal {
		opacity: 1;
		transform: none;
	}

	.sl-stroke--draw path {
		stroke-dasharray: none;
		stroke-dashoffset: 0;
	}
}

/* -------------------------------------------------------- Breakpoints */

@media (max-width: 1100px) {
	.sl-process__thread {
		display: none;
	}

	.sl-process--horizontal .sl-process__list {
		grid-template-columns: repeat(4, minmax(0, 1fr));
		gap: 32px 28px;
	}
}

@media (max-width: 1000px) {
	.sl-hero__grid {
		grid-template-columns: minmax(0, 1fr);
		gap: 40px;
	}

	.sl-hero__heading {
		max-width: 18ch;
	}
}

@media (max-width: 820px) {
	.sl-services {
		grid-template-columns: minmax(0, 1fr);
	}

	.sl-cta__inner {
		padding: 48px 28px;
	}

	.sl-cta__stroke {
		display: none;
	}

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

@media (max-width: 700px) {
	.sl-process--horizontal .sl-process__list {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	.sl-faq__answer {
		padding-right: 0;
	}
}

@media (max-width: 480px) {
	.sl-stats,
	.sl-process--horizontal .sl-process__list {
		grid-template-columns: minmax(0, 1fr);
	}

	.sl-stats--ruled .sl-stat + .sl-stat {
		border-left: 0;
		border-top: var(--sl-border-w) solid var(--sl-hairline);
	}

	.sl-stats--ruled .sl-stat {
		padding-inline: 0;
	}
}

/* ------------------------------------------------------ Inverted bands */

/*
 * .sl-invert re-declares the palette tokens for its subtree; the values are
 * generated in inc/tokens.php so `:root` and the inverted block cannot drift.
 * Everything here is layout, not colour.
 */
.sl-invert {
	border-radius: var(--sl-radius);
}

.sl-invert ::selection {
	background: var(--sl-primary);
	color: #fff;
}

/* ------------------------------------------------------------------ Team */

/*
 * An editorial roster, not a row of avatar cards: a hairline over each person,
 * the name in the display face, the role as a specification line.
 */
.sl-team {
	display: grid;
	grid-template-columns: repeat(var(--sl-team-columns, 3), minmax(0, 1fr));
	gap: calc(var(--sl-gap) * 1.5) var(--sl-gap);
	list-style: none;
	margin: 0;
	padding: 0;
}

.sl-team__member {
	display: flex;
	flex-direction: column;
	gap: 18px;
	min-width: 0;
}

.sl-team__photo {
	overflow: hidden;
	border-radius: var(--sl-radius);
	background: var(--sl-surface);
}

.sl-team__photo img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	filter: grayscale(1);
	transition:
		filter 400ms var(--sl-ease),
		transform 600ms var(--sl-ease);
}

.sl-team--portrait .sl-team__photo {
	aspect-ratio: 4 / 5;
}

.sl-team--square .sl-team__photo {
	aspect-ratio: 1 / 1;
}

.sl-team__member:hover .sl-team__photo img {
	filter: grayscale(0);
	transform: scale(1.02);
}

.sl-team__body {
	display: flex;
	flex-direction: column;
	gap: 6px;
	padding-top: 16px;
	border-top: 2px solid var(--sl-hairline-strong);
}

.sl-team--none .sl-team__body {
	padding-top: 20px;
}

.sl-team__name {
	margin: 0;
	font-family: var(--sl-font-display);
	font-weight: 700;
	font-size: 20px;
	letter-spacing: -0.02em;
	line-height: 1.2;
}

.sl-team__link {
	color: var(--sl-text);
}

.sl-team__link:hover {
	color: var(--sl-link);
}

/* The whole member becomes the hit area, without nesting interactive elements. */
.sl-team__member {
	position: relative;
}

.sl-team__link::after {
	content: "";
	position: absolute;
	inset: 0;
}

.sl-team__bio {
	margin: 6px 0 0;
	font-size: 16px;
	line-height: 1.5;
	color: var(--sl-text-soft);
}

.sl-team__cta {
	margin-top: 10px;
	color: var(--sl-link);
}

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

@media (max-width: 560px) {
	.sl-team {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* -------------------------------------------------------------- Projects */

/*
 * A lead project spanning the grid, then the rest in columns — the shape the
 * mockup uses for selected work. The lead is a two-up: media beside body.
 */
.sl-projects {
	display: grid;
	grid-template-columns: repeat(var(--sl-projects-columns, 2), minmax(0, 1fr));
	gap: var(--sl-gap);
}

.sl-project {
	position: relative;
	display: flex;
	flex-direction: column;
	min-width: 0;
	color: var(--sl-text);
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-radius: var(--sl-radius);
	overflow: hidden;
	background: var(--sl-surface);
	transition: border-color var(--sl-duration) var(--sl-ease);
}

.sl-project:hover,
.sl-project:focus-visible {
	border-color: var(--sl-hairline-strong);
	color: var(--sl-text);
}

.sl-project--lead {
	grid-column: 1 / -1;
	flex-direction: row;
	align-items: stretch;
}

.sl-project__media {
	position: relative;
	overflow: hidden;
	background: var(--sl-bg);
	aspect-ratio: 16 / 9;
	display: flex;
	align-items: center;
	justify-content: center;
}

.sl-project--lead .sl-project__media {
	flex: 1 1 55%;
	aspect-ratio: auto;
	/*
	 * The screenshots are 4:3 and the frame is 55% of the container, so ~560px
	 * is the height at which one shows whole. Below that the cover crop ate the
	 * top and bottom and the card read as cut off rather than cropped.
	 */
	min-height: 560px;
}

.sl-project__image {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 600ms var(--sl-ease);
}

.sl-project:hover .sl-project__image {
	transform: scale(1.02);
}

/*
 * No featured image is a legitimate state, not an error — show the title on the
 * technical hatch rather than a broken-image box.
 */
.sl-project__placeholder {
	padding: 24px;
	text-align: center;
	color: var(--sl-muted);
	background-image: repeating-linear-gradient(
		135deg,
		var(--sl-wash) 0 1px,
		transparent 1px 11px
	);
	width: 100%;
	height: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
}

.sl-project__body {
	display: flex;
	flex-direction: column;
	gap: 10px;
	padding: 28px;
}

.sl-project--lead .sl-project__body {
	flex: 1 1 45%;
	justify-content: center;
	padding: 44px 40px;
}

.sl-project__title {
	margin: 0;
	font-family: var(--sl-font-display);
	font-weight: 700;
	font-size: 22px;
	letter-spacing: -0.025em;
	line-height: 1.2;
}

.sl-project--lead .sl-project__title {
	font-weight: 300;
	font-size: clamp(28px, 3vw, 38px);
	letter-spacing: -0.035em;
	line-height: 1.08;
}

.sl-project__excerpt {
	margin: 0;
	font-size: 16px;
	color: var(--sl-text-soft);
	max-width: 46ch;
}

.sl-project__sector {
	color: var(--sl-signal);
}

/*
 * Reference code, metric and stack. All three are optional meta on the
 * project, so every rule here has to look deliberate when the field is empty —
 * hence the flex containers rather than a fixed grid.
 */

.sl-project__meta {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 16px;
}

.sl-project__ref {
	color: var(--sl-text-faint);
	flex: none;
}

.sl-project__footer {
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
	gap: 20px;
	margin-top: auto;
	padding-top: 20px;
	border-top: var(--sl-border-w) solid var(--sl-hairline);
}

/*
 * The way in on the left, the tags in the right corner.
 *
 * Both sit on the footer's baseline so the link and the last row of tags read
 * as one line. This only fits because the archive went two-up — at a third of
 * the container the same pair had to stack.
 */
.sl-project__footer {
	align-items: flex-end;
	gap: 20px;
}

.sl-project__footer .sl-project__stack {
	flex: 1 1 auto;
	min-width: 0;
	justify-content: flex-end;
}

.sl-project__footer .sl-project__cta {
	/* Printed after the tags so the reading order stays what-then-where, but
	   drawn before them. Order alone moves it; the markup is untouched. */
	order: -1;
	flex: none;
	margin-top: 0;
}

.sl-project__metric-value {
	font-family: var(--sl-font-display);
	font-size: 26px;
	font-weight: 700;
	letter-spacing: -0.03em;
	line-height: 1.1;
}

.sl-project__metric-label {
	display: block;
	margin-top: 4px;
	color: var(--sl-muted);
}

/* The lead card carries up to three metrics as hairline-joined tiles. */
.sl-project__metrics {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
	gap: var(--sl-border-w);
	background: var(--sl-hairline);
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-radius: var(--sl-radius);
	overflow: hidden;
	flex: 1 1 auto;
}

.sl-project__metrics .sl-project__metric-item {
	background: var(--sl-bg);
	padding: 18px;
}

/*
 * The lead card used to stack its footer, because it held a row of metric
 * tiles that could not share a line with the tags. The tiles are gone, so it
 * takes the same corner layout as every other card — the link on the left, the
 * tags on the right — and the two stop disagreeing about where they sit.
 */
.sl-project--lead .sl-project__footer {
	align-items: flex-end;
}

.sl-project__stack {
	display: flex;
	flex-wrap: wrap;
	justify-content: flex-end;
	gap: 6px;
	flex: 0 1 auto;
}

.sl-project__tag {
	border: var(--sl-border-w) solid var(--sl-hairline-strong);
	border-radius: var(--sl-radius);
	padding: 5px 8px;
	font-family: var(--sl-font-mono);
	font-size: 10px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--sl-muted);
}

.sl-project__cta {
	display: inline-flex;
	align-items: center;
	gap: 7px;
	margin-top: 14px;
	color: var(--sl-link);
}

/* The card is the link, so the arrow answers a hover anywhere on the tile
   rather than only over the words. */
.sl-project:hover .sl-project__cta .sl-arrow,
.sl-project:focus-visible .sl-project__cta .sl-arrow {
	transform: translateX(3px);
}

/* The card is one link, so the whole tile is the hit area already. */
.sl-project::after {
	content: "";
	position: absolute;
	inset: 0;
}

/* An editor-only note where a query returns nothing. */
.sl-widget-placeholder {
	padding: 28px;
	border: var(--sl-border-w) dashed var(--sl-hairline-strong);
	border-radius: var(--sl-radius);
	color: var(--sl-muted);
	font-family: var(--sl-font-mono);
	font-size: 12px;
	letter-spacing: 0.04em;
	text-align: center;
}

@media (max-width: 860px) {
	.sl-project--lead {
		flex-direction: column;
	}

	.sl-project--lead .sl-project__media {
		min-height: 0;
		aspect-ratio: 16 / 9;
	}

	.sl-project--lead .sl-project__body {
		padding: 28px;
	}
}

@media (max-width: 640px) {
	.sl-projects {
		grid-template-columns: minmax(0, 1fr);
	}

	/* Tags and the way in stop competing for one row once the card is narrow. */
	.sl-project__footer {
		display: block;
	}

	/*
	 * Matched on .sl-project__footer .sl-project__stack, not on the stack
	 * alone: the corner alignment below is set at that depth and would
	 * otherwise outrank this, leaving the tags pinned right while the link
	 * underneath them sat left.
	 */
	.sl-project__footer .sl-project__stack {
		margin-top: 0;
		margin-bottom: 16px;
		justify-content: flex-start;
	}

	.sl-project__footer .sl-project__cta {
		margin-top: 0;
	}
}

/* ------------------------------------------------- Projects archive */

/*
 * Two up, not three.
 *
 * Every card carries a screenshot of a whole website, and at a third of the
 * container those reduce to something you cannot read — the work is the
 * argument, so the image needs the width. Two columns also give the title room
 * to sit on one or two lines instead of three, and the tag row space for the
 * industry alongside the services.
 */
.sl-projects--archive {
	grid-template-columns: repeat(2, minmax(0, 1fr));
	margin-top: calc(var(--sl-gap) * 1.5);
}

/* Wider column, so the title returns to the card's normal size rather than the
   reduced one three-up needed. */
.sl-projects--archive .sl-project__title {
	font-size: 24px;
}

@media (max-width: 640px) {
	.sl-projects--archive {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* ------------------------------------------------- Single project */

/*
 * The case study layout. Reuses the panel, the metric tiles and the stack
 * chips from the project card, so a project looks like itself wherever it
 * appears. Only the page-level composition is new.
 */

.sl-case__header {
	padding-block: calc(var(--sl-section) * 0.7) 0;
}

.sl-case__intro--split {
	display: grid;
	grid-template-columns: 1.55fr 0.95fr;
	gap: 56px;
	align-items: end;
}

.sl-label.sl-case__eyebrow {
	display: block;
	margin-bottom: 16px;
	/* The sector, year and reference: the one Signal Lime moment on the page. */
	color: var(--sl-signal);
}

/*
 * Title and live-site link on one row. Baseline alignment rather than centring,
 * so the link sits on the title's first line however many lines the title runs
 * to — the row reads as one line of type, not as a heading with a button.
 */
.sl-case__headline--linked {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 48px;
}

/*
 * The call to action in one tag. The slot around it carries the title's own line
 * box — font size times its 1.02 leading — so the tag sits centred on the
 * title's first line and follows the clamp as the title scales, without the tag
 * itself having to be that tall.
 */
.sl-case__site-slot {
	/*
	 * It does not shrink. When the tag held a domain it was a fact, and a fact
	 * can trail off into an ellipsis without costing anything. It now holds the
	 * only instruction on the page, and "VIEW LIV…" is not one — so the tag
	 * keeps its width and the title wraps around it instead. The phrase is
	 * short and fixed, and below 782px the tag is not here at all.
	 */
	flex: none;
	display: flex;
	align-items: center;
	min-height: calc(var(--sl-h1) * 1.02);
}

/*
 * The tag is rendered in both places and shown in one.
 *
 * Above the phone breakpoint the tag sits beside the title, where the heading's
 * own line has room to spare, and the results row gives its live-site slot over
 * to another fact. On a phone the title needs its full width — sharing the line
 * breaks the heading to one word a row — so the tag drops away and the tile
 * carries the link as the last box under the image.
 *
 * Neither renders unless the project has a live URL, so a study without one
 * shows no empty box and no spare slot in either position.
 */
@media (max-width: 782px) {
	.sl-case__site-slot--title {
		display: none;
	}
}

/*
 * Above the phone breakpoint the tag beside the title carries the link, so the
 * tile in the results row stands down and leaves its slot free.
 *
 * Scoped through the row rather than written as a bare modifier: the tile's own
 * `display: block` is declared further down this file at the same specificity,
 * so a one-class rule here loses the cascade and both would show.
 */
@media (min-width: 783px) {
	.sl-case__results .sl-case__result--swap {
		display: none;
	}
}

.sl-case__site {
	display: inline-flex;
	align-items: center;
	min-width: 0;
	gap: 8px;
	padding: 9px 14px;
	border: var(--sl-border-w) solid var(--sl-hairline-strong);
	border-radius: var(--sl-radius);
	font-family: var(--sl-font-mono);
	font-size: 12px;
	color: var(--sl-text);
	transition: border-color var(--sl-duration) var(--sl-ease);
}

.sl-case__site:hover,
.sl-case__site:focus-visible {
	border-color: var(--sl-hairline-strong);
}

.sl-case__site-value {
	font-size: 13px;
	font-weight: 700;
	letter-spacing: var(--sl-label-tracking);
	text-transform: uppercase;
	color: var(--sl-text);
	/* One line, never broken mid-phrase. */
	white-space: nowrap;
}

/* The one piece of colour in the tag: the glyph, not the words. */
.sl-case__site .sl-arrow {
	flex: none;
	width: 16px;
	height: 16px;
	color: var(--sl-signal);
	transition: transform var(--sl-duration) var(--sl-ease);
}

.sl-scheme-light .sl-case__site .sl-arrow {
	color: var(--sl-primary);
}


/* Away from the page, in the direction the glyph points. */
.sl-case__site:hover .sl-arrow {
	transform: translate(2px, -2px);
}

.sl-case__title {
	margin: 0;
	font-size: var(--sl-h1);
	/* Wider than the blog measure: a project title carries the client name too. */
	max-width: 22ch;
}

.sl-case__lead {
	margin: 24px 0 0;
	max-width: 58ch;
	font-size: 21px;
	line-height: 1.45;
	color: var(--sl-text-soft);
}

.sl-case__media {
	position: relative;
	display: grid;
	place-items: center;
	margin: 48px 0 0;
	aspect-ratio: 16 / 7;
	overflow: hidden;
	background: var(--sl-bg);
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-radius: var(--sl-radius);
}

/* When results sit directly beneath, the two read as one object. */
.sl-case__media--joined {
	border-bottom: 0;
	border-radius: var(--sl-radius) var(--sl-radius) 0 0;
}

.sl-case__image {
	width: 100%;
	border-radius: var(--sl-radius);
}


/* The tile row is the card component, widened for the page. */






.sl-case__body {
	padding-block: var(--sl-section);
}

.sl-case__more {
	border-top: var(--sl-border-w) solid var(--sl-hairline);
}

@media (max-width: 1000px) {
	.sl-case__intro--split {
		grid-template-columns: minmax(0, 1fr);
		gap: 36px;
	}
}

/*
 * Sector filter. Real links to term archives — every filter state is a URL that
 * can be shared and indexed, which an AJAX filter throws away.
 */
.sl-filter {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	padding-bottom: 4px;
}

.sl-filter__item {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 8px 14px;
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-radius: var(--sl-radius);
	font-family: var(--sl-font-mono);
	font-size: 11px;
	letter-spacing: var(--sl-label-tracking);
	text-transform: uppercase;
	color: var(--sl-muted);
	transition:
		border-color var(--sl-duration) var(--sl-ease),
		color var(--sl-duration) var(--sl-ease);
}

.sl-filter__item:hover {
	border-color: var(--sl-hairline-strong);
	color: var(--sl-text);
}

.sl-filter__item.is-current {
	border-color: var(--sl-primary);
	color: var(--sl-text);
}

.sl-filter__count {
	font-size: 10px;
	color: var(--sl-text-faint);
}

.sl-filter__item.is-current .sl-filter__count {
	color: var(--sl-primary);
}

/* ------------------------------------------- Case study narrative */

/*
 * The pieces the editor writes inside a case study. These are styled from the
 * markup core blocks already produce, so an author uses the ordinary Quote and
 * Group blocks and gets the brand's treatment — no bespoke block types, and
 * nothing breaks if the theme is switched away.
 */

.sl-case__content > blockquote,
.sl-entry-content > blockquote {
	margin: 32px 0;
	padding: 26px 28px;
	border: 0;
	border-left: 3px solid var(--sl-primary);
	border-radius: 0 var(--sl-radius) var(--sl-radius) 0;
	background: var(--sl-surface);
	font-family: var(--sl-font-display);
	font-weight: 300;
	font-size: 24px;
	line-height: 1.28;
	letter-spacing: -0.025em;
}

.sl-case__content > blockquote p,
.sl-entry-content > blockquote p {
	margin: 0;
	font: inherit;
	letter-spacing: inherit;
}

/* A named attribution reads as a specification line, not as prose. */
.sl-case__content > blockquote cite,
.sl-case__content > blockquote footer,
.sl-entry-content > blockquote cite,
.sl-entry-content > blockquote footer {
	display: block;
	margin-top: 18px;
	font-family: var(--sl-font-mono);
	font-size: 10px;
	font-style: normal;
	font-weight: 400;
	letter-spacing: var(--sl-label-tracking);
	text-transform: uppercase;
	color: var(--sl-muted);
}

/*
 * The phase list: a hairline-joined column of stages. Built from ordinary Group
 * blocks so the pattern is editable in place.
 */
.sl-phases {
	display: grid;
	gap: var(--sl-border-w);
	margin: 28px 0;
	background: var(--sl-hairline);
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-radius: var(--sl-radius);
	overflow: hidden;
}

.sl-phase {
	background: var(--sl-surface);
	padding: 22px 24px;
}

.sl-phase__when {
	margin: 0;
	font-family: var(--sl-font-mono);
	font-size: 10px;
	letter-spacing: var(--sl-label-tracking);
	text-transform: uppercase;
	color: var(--sl-muted);
}

.sl-phase__title {
	margin: 8px 0 6px;
	font-family: var(--sl-font-display);
	font-weight: 700;
	font-size: 20px;
	letter-spacing: -0.02em;
	line-height: 1.2;
}

.sl-phase__desc {
	margin: 0;
	font-size: 16px;
	color: var(--sl-text-soft);
}

/* The closing phase is the handover — the one that earns the accent. */
.sl-phase--terminal {
	background: color-mix(in srgb, var(--sl-primary) 12%, var(--sl-surface));
}

.sl-scheme-light .sl-phase--terminal {
	background: color-mix(in srgb, var(--sl-primary) 8%, var(--sl-surface));
}

.sl-phase--terminal .sl-phase__when {
	color: var(--sl-signal);
}

.sl-scheme-light .sl-phase--terminal .sl-phase__when {
	color: var(--sl-primary);
}

/* A client quote, boxed rather than ruled — the closing note of the narrative. */
.sl-quote-card {
	margin: 32px 0 0;
	padding: 32px;
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-radius: var(--sl-radius);
	background: var(--sl-surface);
}

.sl-quote-card blockquote {
	margin: 0;
	padding: 0;
	border: 0;
	background: none;
	font-family: var(--sl-font-display);
	font-weight: 300;
	font-size: 26px;
	line-height: 1.25;
	letter-spacing: -0.03em;
}

.sl-quote-card blockquote p {
	margin: 0;
	font: inherit;
	letter-spacing: inherit;
}

.sl-quote-card figcaption,
.sl-quote-card cite {
	display: block;
	margin-top: 26px;
	font-family: var(--sl-font-ui);
	font-style: normal;
	font-size: 16px;
	font-weight: 500;
	color: var(--sl-text);
}

.sl-quote-card figcaption em,
.sl-quote-card cite em {
	display: block;
	margin-top: 2px;
	font-family: var(--sl-font-mono);
	font-style: normal;
	font-size: 10px;
	font-weight: 400;
	letter-spacing: var(--sl-label-tracking);
	text-transform: uppercase;
	color: var(--sl-muted);
}

/* ------------------------------------------ Case study — media frame */

.sl-case__hatch {
	position: absolute;
	inset: 0;
	background-image: repeating-linear-gradient(
		135deg,
		var(--sl-wash) 0 1px,
		transparent 1px 11px
	);
}

/*
 * The stroke is a placeholder mark, not the subject. Without a width it takes
 * the SVG's intrinsic ratio and grows to fill the frame.
 */
.sl-case__stroke {
	position: relative;
	width: 42%;
	max-width: 340px;
	height: auto;
	color: var(--sl-hairline-strong);
}

.sl-case__media-note {
	position: absolute;
	left: 22px;
	bottom: 18px;
	color: var(--sl-muted);
}

.sl-case__media .sl-case__image {
	grid-area: 1 / 1;
	width: 100%;
	height: 100%;
	object-fit: cover;
	border-radius: 0;
}

/*
 * A presentation board rather than a page screenshot is shown whole: fitted
 * inside the band and centred instead of cropped to it, with the sides filled
 * in the board's own edge colour so the letterbox reads as part of the image.
 * Set per project with _sl_media_fit = contain and _sl_media_bg = a hex colour.
 */
.sl-case__media--contain {
	background: var(--sl-case-media-bg, var(--sl-bg));
}

/* Pinned to the frame: as a grid item its height:100% does not resolve against
   an aspect-ratio box, and the image kept its own height and was clipped. */
.sl-case__media--contain .sl-case__image {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: contain;
}

/* ---------------------------------------------- Case study — results */

.sl-case__results {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
	gap: var(--sl-border-w);
	background: var(--sl-hairline);
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-top: 0;
	border-radius: 0 0 var(--sl-radius) var(--sl-radius);
	overflow: hidden;
}

.sl-case__result {
	background: var(--sl-surface);
	padding: 26px 24px;
}

/* The one tile that is a link. Same box, accent value, and it responds. */
.sl-case__result--link {
	display: block;
	color: var(--sl-text);
	transition: background-color var(--sl-duration) var(--sl-ease);
}

/* A domain is longer than a figure, so it sets a step down to sit level. */
.sl-case__result--link .sl-case__result-value {
	display: inline-flex;
	align-items: center;
	gap: 10px;
	font-size: 24px;
	letter-spacing: -0.02em;
	color: var(--sl-signal);
	overflow-wrap: anywhere;
}

.sl-scheme-light .sl-case__result--link .sl-case__result-value {
	color: var(--sl-primary);
}

.sl-case__result--link:hover,
.sl-case__result--link:focus-visible {
	background: var(--sl-surface-2);
	color: var(--sl-text);
}

.sl-case__result--link:hover .sl-arrow {
	transform: translateX(3px);
}

.sl-case__result-value {
	font-family: var(--sl-font-display);
	font-weight: 700;
	/*
	 * Smaller than it was. At 34px a two-word sector wrapped and a list of
	 * services wrapped twice, so the row read as four paragraphs rather than
	 * four facts.
	 */
	font-size: 26px;
	letter-spacing: -0.03em;
	line-height: 1;
	font-variant-numeric: tabular-nums;
}

/*
 * Below 1000px all four values share one size.
 *
 * Sizing each tile by the length of its own text made the row ragged — a
 * duration at 34px beside an industry at 20px reads as four unrelated things
 * rather than one set of facts. One fluid size holds them together and still
 * fits the longest value, because it tracks the viewport the tiles are
 * narrowing with. The link tile is included, so it stops being the odd one out
 * at its own fixed 24px.
 *
 * Desktop is untouched: there the display size fits and is the point.
 */
@media (max-width: 999px) {
	.sl-case__result .sl-case__result-value,
	.sl-case__result--link .sl-case__result-value {
		font-size: clamp(19px, 2.5vw, 26px);
		letter-spacing: -0.02em;
		line-height: 1.15;
	}
}

/* One part to a line, so a list of services reads as a list. */
.sl-case__result-item {
	display: block;
}

/*
 * Tools run on together, comma separated. They are a set of names rather than
 * a list of things done, and stacking them made three short words occupy three
 * lines beside a one-line neighbour.
 */
.sl-case__result-value--inline .sl-case__result-item {
	display: inline;
}

/*
 * A linked value keeps the tile's type and takes the accent only on hover —
 * the row is a set of facts, and one of them being clickable should not make
 * it look like a different kind of thing.
 */
.sl-case__result-link {
	color: inherit;
	text-decoration: none;
	transition: color var(--sl-duration) var(--sl-ease);
}

.sl-case__result-link:hover,
.sl-case__result-link:focus-visible {
	color: var(--sl-signal);
}

.sl-scheme-light .sl-case__result-link:hover,
.sl-scheme-light .sl-case__result-link:focus-visible {
	color: var(--sl-primary);
}

/* The label sits above the value now, so the gap moves with it. */
.sl-case__result-label {
	display: block;
	margin-bottom: 10px;
}

/* ------------------------------------------------- Case study — body */

.sl-case__grid--railed {
	display: grid;
	grid-template-columns: 0.34fr 0.66fr;
	gap: 72px;
	align-items: start;
}

.sl-case__content {
	max-width: 720px;
}

/*
 * A closing prompt written into a case study's own body: a panel after the
 * last section rather than a page section, so it reads as the end of the story
 * and leaves the page's own closing call to action alone. Its heading is not an
 * h2, so the "On this page" rail does not list it as a section.
 */
/* Two classes deep so it outranks the body's own `> * + *` spacing. */
.sl-entry-content .sl-case-cta {
	margin-top: 64px;
	padding: 40px;
	background: var(--sl-surface);
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-radius: var(--sl-radius);
}

/* The body's section labels open with a wide gap; this one opens the panel. */
.sl-entry-content .sl-case-cta .sl-label {
	margin-top: 0;
}

.sl-entry-content .sl-case-cta__heading {
	margin: 12px 0 0;
	font-size: clamp(26px, 3vw, 34px);
	color: var(--sl-text);
}

.sl-entry-content .sl-case-cta__lead {
	margin: 12px 0 0;
	color: var(--sl-text-soft);
}

.sl-entry-content .sl-case-cta__actions {
	margin: 28px 0 0;
}

@media (max-width: 767px) {
	.sl-entry-content .sl-case-cta {
		padding: 28px 24px;
	}

	.sl-case-cta .sl-button {
		width: 100%;
		justify-content: center;
	}
}

.sl-case__rail {
	position: sticky;
	/* --sl-header-h, not --sl-header-height: the latter has never existed, so
	   this silently pinned itself to the 76px fallback and will drift now that
	   the header is responsive. */
	top: calc(var(--sl-header-h, 76px) + 34px);
}

.sl-case__rail-title {
	display: block;
	padding-bottom: 12px;
	border-bottom: var(--sl-border-w) solid var(--sl-hairline-strong);
}

.sl-case__toc {
	display: flex;
	flex-direction: column;
}

.sl-case__toc-link {
	display: flex;
	gap: 10px;
	padding: 11px 0;
	border-bottom: var(--sl-border-w) solid var(--sl-wash);
	color: var(--sl-text);
	font-size: 16px;
	line-height: 1.35;
}

.sl-case__toc-link:hover,
.sl-case__toc-link:focus-visible {
	color: var(--sl-link);
}

.sl-case__toc-index {
	flex: none;
	font-family: var(--sl-font-mono);
	font-size: 11px;
	letter-spacing: var(--sl-label-tracking);
	color: var(--sl-muted);
	padding-top: 3px;
}

.sl-case__stack {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 8px;
	margin-top: 32px;
}

.sl-case__chip {
	padding: 7px 9px;
	text-align: center;
	border: var(--sl-border-w) solid var(--sl-hairline-strong);
	border-radius: var(--sl-radius);
	font-family: var(--sl-font-mono);
	font-size: 10px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--sl-muted);
}

@media (max-width: 1080px) {
	.sl-case__grid--railed {
		grid-template-columns: minmax(0, 1fr);
		gap: 48px;
	}

	.sl-case__rail {
		position: static;
	}

	.sl-case__stack {
		grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
	}
}

@media (max-width: 1000px) {
	.sl-case__intro--split {
		grid-template-columns: minmax(0, 1fr);
		gap: 36px;
	}

	.sl-case__media {
		aspect-ratio: 16 / 9;
	}
}

/*
 * On a phone the frame takes the screenshots' own 4:3, so the whole page being
 * shown is visible. The 16:7 band is a desktop composition — at 390px wide it
 * left a 170px strip that could have been any website.
 */
@media (max-width: 782px) {
	.sl-case__media {
		aspect-ratio: 4 / 3;
	}
}

@media (max-width: 640px) {
	.sl-case__results {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* ------------------------------------------------------ Numbered grid */

/*
 * Not cards. One border around the block and a single pixel of rule between
 * tiles, so six commitments read as one specification rather than six things.
 */
.sl-grid-items {
	display: grid;
	grid-template-columns: repeat(var(--sl-grid-columns, 2), minmax(0, 1fr));
	gap: var(--sl-border-w);
	background: var(--sl-hairline);
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-radius: var(--sl-radius);
	overflow: hidden;
}

.sl-grid-item {
	background: var(--sl-surface);
	padding: 30px 26px 34px;
}

/*
 * The tile header: icon left, reference right, on one line. Keeping them on a
 * shared row means a tile with an icon is exactly as tall as one without, so a
 * grid where only some items carry an icon still lines its titles up.
 */
.sl-grid-item__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	min-height: 26px;
}

.sl-grid-item__icon {
	color: var(--sl-link);
}

/* Smaller marks for a list that reads as items rather than headline cards. */
.sl-grid-icons-sm .sl-grid-item__icon {
	width: 20px;
	height: 20px;
}

/* A short numbered list where the number is the mark, so it carries weight. */
.sl-grid-index-lg .sl-grid-item__index {
	font-size: 24px;
	line-height: 1;
}

/* The index in the icon colour, for a list that numbers its items instead. */
.sl-grid-index-accent .sl-grid-item__index {
	font-size: 14px;
	color: var(--sl-link);
}

/*
 * The index in its own left column on wider screens: the number sits beside
 * the title and description instead of on a row above them. Phones keep the
 * stacked tile, where a side column would squeeze the text.
 */
@media (min-width: 768px) {
	.sl-grid-index-col .sl-grid-item {
		display: grid;
		grid-template-columns: 2.2em minmax(0, 1fr);
		column-gap: 16px;
		align-items: start;
	}

	.sl-grid-index-col .sl-grid-item > * {
		grid-column: 2;
	}

	.sl-grid-index-col .sl-grid-item > .sl-grid-item__head {
		grid-column: 1;
		grid-row: 1 / span 3;
		min-height: 0;
		padding-top: 4px;
	}

	.sl-grid-index-col .sl-grid-item__title {
		margin-top: 0;
	}
}

.sl-grid-item__index {
	display: block;
	font-size: 10px;
}

.sl-grid-item__title {
	margin: 16px 0 8px;
	font-family: var(--sl-font-display);
	font-weight: 700;
	font-size: 20px;
	letter-spacing: -0.02em;
	line-height: 1.2;
}

.sl-grid-item__desc {
	margin: 0;
	font-size: 16px;
	color: var(--sl-text-soft);
}

@media (max-width: 700px) {
	.sl-grid-items {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* -------------------------------------------------------- Testimonials */

.sl-quotes {
	display: grid;
	grid-template-columns: repeat(var(--sl-quotes-columns, 2), minmax(0, 1fr));
	gap: var(--sl-border-w);
	background: var(--sl-hairline);
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-radius: var(--sl-radius);
	overflow: hidden;
}

.sl-quote {
	display: flex;
	flex-direction: column;
	margin: 0;
	padding: 40px 36px;
	background: var(--sl-surface);
}

.sl-quote__text {
	margin: 0;
	padding: 0;
	border: 0;
	background: none;
	font-family: var(--sl-font-display);
	font-weight: 300;
	font-size: 26px;
	line-height: 1.25;
	letter-spacing: -0.03em;
}

.sl-quote__by {
	display: flex;
	align-items: center;
	gap: 14px;
	margin-top: auto;
	padding-top: 28px;
}

.sl-quote__photo {
	flex: none;
	display: grid;
	place-items: center;
	width: 44px;
	height: 44px;
	overflow: hidden;
	border-radius: var(--sl-radius);
	background: var(--sl-surface-2);
	border: var(--sl-border-w) solid var(--sl-hairline);
}

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

.sl-quote__mark {
	color: var(--sl-muted);
}

.sl-quote__who {
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 0;
}

.sl-quote__name {
	font-weight: 500;
	font-size: 16px;
}

.sl-quote__role {
	display: block;
	font-size: 10px;
}

@media (max-width: 820px) {
	.sl-quotes {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* ----------------------------------------------------------- Spec list */

/*
 * A flat specification: the discipline on the left, what it is built with on
 * the right, separated by rules. No border, no background — the panel widget
 * is the one with chrome.
 */
.sl-specs {
	margin: 0;
}

.sl-specs--ruled {
	border-top: var(--sl-border-w) solid var(--sl-hairline);
}

.sl-specs__row {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 20px;
	padding: 18px 0;
	border-bottom: var(--sl-border-w) solid var(--sl-hairline);
}

.sl-specs__label {
	margin: 0;
	font-weight: 500;
}

/*
 * The emphasised variant. Used where the list is the point of its section
 * rather than a footnote to one — a contact address people are meant to act on,
 * against a spec row people only scan.
 */
.sl-specs--lead .sl-specs__label {
	font-family: var(--sl-font-display);
	font-weight: 700;
	font-size: 22px;
	letter-spacing: -0.02em;
	line-height: 1.2;
}

.sl-specs--lead .sl-specs__row {
	padding: 14px 0;
}

/* The mirror: the answer carries the weight rather than the question. */
.sl-specs--lead-value .sl-specs__value {
	font-family: var(--sl-font-display);
	font-weight: 700;
	font-size: 22px;
	letter-spacing: -0.02em;
	line-height: 1.2;
	text-transform: none;
	color: var(--sl-text);
}

.sl-specs--lead-value .sl-specs__label {
	color: var(--sl-text-soft);
	font-weight: 400;
}

/*
 * The wide variant, for a list that has a whole section to itself rather than
 * a column. Space-between is right at 600px and wrong at 1400: the label ends
 * up a thousand pixels from its own value, with nothing but rule between them.
 * So the label takes a fixed column and the value follows immediately after,
 * both ranged left, while the rule still spans the full width. It reads as a
 * datasheet instead of two lists that happen to share a line.
 */
@media (min-width: 900px) {
	.sl-specs--wide .sl-specs__row {
		display: grid;
		grid-template-columns: minmax(0, 20ch) minmax(0, 1fr);
		column-gap: 56px;
		align-items: baseline;
	}

	.sl-specs--wide .sl-specs__value {
		justify-self: start;
		text-align: left;
	}
}

/*
 * Brand marks sit on the value's baseline, a shade back from the words so the
 * row still reads as text with marks rather than as a logo wall. They wrap with
 * the value on a narrow screen instead of squeezing it.
 */
.sl-specs__marks {
	display: inline-flex;
	align-items: center;
	gap: 10px;
	margin-right: 12px;
	vertical-align: middle;
	color: var(--sl-text-soft);
}

.sl-specs__mark {
	flex: none;
}

@media (max-width: 600px) {
	.sl-specs__marks {
		display: flex;
		margin: 0 0 8px;
	}
}

/* A linked value is the one action in an otherwise inert list, so it takes the
   signal accent — the same role the accent plays everywhere else on the site. */
.sl-specs__value-link {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	color: var(--sl-signal);
	transition: color var(--sl-duration) var(--sl-ease);
}

.sl-scheme-light .sl-specs__value-link {
	color: var(--sl-primary);
}

.sl-specs__value-link:hover,
.sl-specs__value-link:focus-visible {
	color: var(--sl-text);
}

.sl-specs__link {
	color: inherit;
	transition: color var(--sl-duration) var(--sl-ease);
}

.sl-specs__link:hover,
.sl-specs__link:focus-visible {
	color: var(--sl-signal);
}

.sl-scheme-light .sl-specs__link:hover,
.sl-scheme-light .sl-specs__link:focus-visible {
	color: var(--sl-primary);
}

.sl-specs__value {
	margin: 0;
	text-align: right;
	font-family: var(--sl-font-mono);
	font-size: 11px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--sl-muted);
}

@media (max-width: 480px) {
	.sl-specs__row {
		flex-direction: column;
		gap: 6px;
	}

	.sl-specs__value {
		text-align: left;
	}
}

/* Grid tiles can carry a mono footer line and an accent state. */
.sl-grid-item__meta {
	display: block;
	margin-top: 14px;
	font-size: 10px;
}

.sl-grid-item--featured {
	background: color-mix(in srgb, var(--sl-primary) 12%, var(--sl-surface));
}

.sl-scheme-light .sl-grid-item--featured {
	background: color-mix(in srgb, var(--sl-primary) 8%, var(--sl-surface));
}

.sl-grid-item--featured .sl-grid-item__index,
.sl-grid-item--featured .sl-grid-item__icon {
	color: var(--sl-signal);
}

.sl-scheme-light .sl-grid-item--featured .sl-grid-item__index,
.sl-scheme-light .sl-grid-item--featured .sl-grid-item__icon {
	color: var(--sl-primary);
}

/* ------------------------------------------- Services — detail layout */

/*
 * The services page card, as against the homepage tile. Separate cards with
 * their own border rather than a hairline-joined block, because each one is a
 * destination with a price rather than one cell of a specification.
 */
.sl-services--detail {
	display: grid;
	grid-template-columns: repeat(var(--sl-services-columns, 3), minmax(0, 1fr));
	gap: var(--sl-gap);
	background: none;
	border: 0;
	border-radius: 0;
	overflow: visible;
}

.sl-services--detail .sl-service {
	min-height: 0;
	padding: 30px;
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-radius: var(--sl-radius);
	background: var(--sl-surface);
	transition:
		border-color var(--sl-duration) var(--sl-ease),
		background-color var(--sl-duration) var(--sl-ease),
		transform var(--sl-duration) var(--sl-ease);
}

a.sl-services--detail .sl-service:hover,
.sl-services--detail a.sl-service:hover,
.sl-services--detail a.sl-service:focus-visible {
	border-color: var(--sl-hairline-strong);
	background: var(--sl-surface-2);
	transform: translateY(-4px);
}

.sl-services--detail .sl-service__top {
	align-items: baseline;
}

.sl-service__ref {
	color: var(--sl-signal);
}

.sl-service__timeline {
	color: var(--sl-muted);
}

.sl-services--detail .sl-service__title {
	margin: 20px 0 12px;
	font-size: 26px;
}

.sl-services--detail .sl-service__desc {
	margin: 0 0 22px;
	font-size: 16px;
	max-width: none;
}

.sl-service__list {
	list-style: none;
	margin: 0 0 24px;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.sl-service__list-item {
	display: flex;
	gap: 10px;
	align-items: baseline;
	padding-bottom: 8px;
	border-bottom: var(--sl-border-w) solid var(--sl-wash);
	font-size: 16px;
	color: var(--sl-text);
}

.sl-service__dash {
	flex: none;
	color: var(--sl-primary);
}

.sl-service__foot {
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: 16px;
	margin-top: auto;
	padding-top: 18px;
	border-top: var(--sl-border-w) solid var(--sl-hairline);
}

.sl-service__price {
	color: var(--sl-muted);
}

.sl-service__more {
	white-space: nowrap;
	font-size: 15px;
	font-weight: 500;
	color: var(--sl-signal);
}

.sl-service__more::after {
	content: " \2192";
}

/* A ruled label row: a section marker with a count or note opposite it. */
.sl-rule-row {
	display: flex;
	justify-content: space-between;
	align-items: flex-end;
	gap: 24px;
	padding-bottom: 12px;
	border-bottom: var(--sl-border-w) solid var(--sl-hairline);
}

@media (max-width: 1180px) {
	.sl-services--detail {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (max-width: 640px) {
	.sl-services--detail {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* ======================================= Small screens — section components

   These live here rather than in app.css on purpose. app.css is enqueued first,
   so a rule there for .sl-panel or .sl-service loses to the base rule in this
   file at equal specificity — the override applied silently to nothing.
   ------------------------------------------------------------------------ */

@media (max-width: 782px) {
	/* Standalone links need a real hit area, not the height of their text. */
	.sl-section-header__link,
	.sl-service__cta,
	.sl-service__more,
	.sl-project__cta,
	.sl-team__cta,
	.sl-filter__item {
		display: inline-flex;
		align-items: center;
		min-height: 44px;
	}
}

@media (max-width: 480px) {
	/* 28px each side of a 260px card is most of the card. */
	.sl-panel,
	.sl-cta__inner,
	.sl-quote {
		padding: 24px 20px;
	}

	.sl-service,
	.sl-grid-item,
	.sl-project__body,
	.sl-services--detail .sl-service {
		padding: 24px 20px;
	}

	.sl-case__media,
	.sl-case__results {
		border-radius: var(--sl-radius);
	}

	/* Long titles must break rather than push the page sideways. */
	.sl-service__title,
	.sl-project__title,
	.sl-case__title,
	.sl-grid-item__title {
		overflow-wrap: anywhere;
	}

	/* Theme-rendered action rows stack; Elementor rows already do. */
	.sl-hero__actions,
	.sl-cta__actions {
		flex-direction: column;
		align-items: stretch;
	}

	.sl-hero__actions > *,
	.sl-cta__actions > * {
		width: 100%;
	}

	/* The lead project drops its side-by-side split well before this. */
	.sl-project--lead .sl-project__body {
		padding: 24px 20px;
	}
}

/* --------------------------------------------------------- Pricing tiers */

/*
 * Packages are the one place on the site that uses separated bordered cards
 * rather than the hairline-joined grid. That is deliberate: a joined grid reads
 * as one continuous list, and these are meant to be compared as alternatives.
 */
.sl-tiers {
	display: grid;
	grid-template-columns: repeat(var(--sl-tiers-columns, 3), minmax(0, 1fr));
	gap: 24px;
}

.sl-tier {
	display: flex;
	flex-direction: column;
	padding: 32px;
	border: var(--sl-border-w) solid var(--sl-hairline-strong);
	border-radius: var(--sl-radius);
	background: var(--sl-surface);
}

.sl-tier__top {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 12px;
}

.sl-tier__title {
	margin: 18px 0 0;
	font-family: var(--sl-font-display);
	font-weight: 700;
	font-size: 26px;
	letter-spacing: -0.03em;
}

.sl-tier__price {
	margin: 10px 0 0;
	font-family: var(--sl-font-display);
	font-weight: 300;
	font-size: 40px;
	line-height: 1;
	letter-spacing: -0.04em;
}

.sl-tier__blurb {
	margin: 16px 0 0;
	font-size: 16px;
	color: var(--sl-text-soft);
}

.sl-tier__list {
	margin: 22px 0 0;
	padding: 0;
	list-style: none;
	display: flex;
	flex-direction: column;
	gap: 9px;
	font-size: 16px;
}

.sl-tier__list-item {
	display: flex;
	gap: 10px;
	align-items: baseline;
	padding-bottom: 9px;
	border-bottom: var(--sl-border-w) solid var(--sl-wash);
}

.sl-tier__dash {
	color: var(--sl-primary);
	flex-shrink: 0;
}

/* Pushes the button to the bottom so a row of cards lines its CTAs up
   regardless of how many inclusions each package lists. */
.sl-tier__foot {
	margin-top: auto;
	padding-top: 26px;
}

/*
 * Same two-ground treatment as the featured service tile — violet plate on Ink,
 * and on Bone a lighter tint rather than a full inversion, because unlike the
 * service tile this card contains a filled button that needs its own contrast.
 */
.sl-tier--featured {
	background: color-mix(in srgb, var(--sl-primary) 12%, var(--sl-surface));
	border-color: color-mix(in srgb, var(--sl-primary) 40%, transparent);
}

.sl-scheme-light .sl-tier--featured {
	background: color-mix(in srgb, var(--sl-primary) 8%, var(--sl-surface));
}

.sl-tier--featured .sl-tier__kicker,
.sl-tier--featured .sl-tier__term {
	color: var(--sl-signal);
}

.sl-scheme-light .sl-tier--featured .sl-tier__kicker,
.sl-scheme-light .sl-tier--featured .sl-tier__term {
	color: var(--sl-primary);
}

@media (max-width: 1024px) {
	.sl-tiers {
		grid-template-columns: minmax(0, 1fr);
	}
}

@media (max-width: 640px) {
	.sl-tier {
		padding: 26px 22px;
	}

	.sl-tier__price {
		font-size: 34px;
	}
}

/* -------------------------------------------------------- Enquiry form */

/*
 * The fields invert: light boxes on the dark page.
 *
 * A dark field on a dark background reads as decoration until it is focused,
 * and people hesitate over whether it is an input at all. The values are the
 * theme's own light palette (see the light scheme in inc/defaults.php) rather
 * than an arbitrary white, so the form stays inside the brand.
 */
.sl-form {
	/*
	 * Underlined fields, not filled ones.
	 *
	 * A rule under the text needs the text to sit on the panel, so the control
	 * is transparent and the type goes back to the panel's own colour. The
	 * `--sl-field-bg` token stays, because autofill still needs one solid
	 * colour to paint over Chrome's yellow.
	 */
	--sl-field-bg: var(--sl-surface);
	--sl-field-text: var(--sl-text);
	--sl-field-muted: var(--sl-text-faint);
	--sl-field-edge: var(--sl-hairline-strong);

	display: flex;
	flex-direction: column;
	gap: 20px;
	/* More room at the sides: the fields were sitting close to the border. */
	padding: 34px 60px 30px;
	border: var(--sl-border-w) solid var(--sl-hairline-strong);
	border-radius: var(--sl-radius);
	background: var(--sl-surface);
}

.sl-form__row {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 18px;
}

/* A row with one field in it takes the whole width rather than half. */
.sl-form__row--single {
	grid-template-columns: minmax(0, 1fr);
}

.sl-field {
	display: flex;
	flex-direction: column;
	gap: 8px;
	min-width: 0;
	margin: 0;
	padding: 0;
	border: 0;
}

.sl-field > .sl-label {
	/* A legend inside a fieldset ignores flex gap in several engines. */
	padding: 0;
	margin-bottom: 2px;
}

/*
 * Qualified by tag on purpose.
 *
 * app.css resets bare form controls with `input[type="text"], select, textarea`
 * — 0-1-1, which outranks a plain `.sl-input` class at 0-1-0. Written as
 * `.sl-input` alone this rule silently loses its padding and size to that
 * reset, and the field renders three pixels tighter and a point smaller than
 * designed. Matching the tag ties the specificity, and sections.css loads after
 * app.css, so this wins on order.
 */
input.sl-input,
select.sl-input,
textarea.sl-input {
	width: 100%;
	/* No side padding: the rule runs the full width of the field, so insetting
	   the text would leave it floating away from the line's left end. */
	padding: 10px 0 11px;
	font-family: inherit;
	font-size: 16px;
	color: var(--sl-field-text);
	caret-color: var(--sl-field-text);
	background: transparent;
	border: 0;
	/* Kept at one weight through every state — thickening it on focus moves the
	   field a pixel and nudges everything below it. Only the colour changes. */
	border-bottom: 2px solid var(--sl-field-edge);
	border-radius: 0;
	transition: border-color var(--sl-duration) var(--sl-ease);
}

.sl-input::placeholder {
	color: var(--sl-field-muted);
	opacity: 1; /* Firefox dims placeholders by default. */
}

/* Company is optional, so phones skip it to keep the form short. */
@media (max-width: 782px) {
	.sl-form .sl-field:has(> [name="sl_company"]) {
		display: none;
	}
}

/*
 * Chrome paints its own background on autofilled fields and ignores both
 * `background` and `color`. An inset shadow the size of the box is the only
 * thing that covers it, and the text colour has to be set separately.
 */
.sl-input:-webkit-autofill,
.sl-input:-webkit-autofill:hover,
.sl-input:-webkit-autofill:focus {
	-webkit-text-fill-color: var(--sl-field-text);
	-webkit-box-shadow: 0 0 0 100px var(--sl-field-bg) inset;
	caret-color: var(--sl-field-text);
}

.sl-input::selection {
	background: var(--sl-link);
	color: #fff;
}

/*
 * The underline is the entire focus state — no ring, no hover change.
 *
 * Colour alone is a weak indicator, so the rule also doubles in weight. The
 * extra 2px is an inset shadow rather than a wider border, because growing the
 * border would move the field and push everything below it down the form.
 */
.sl-input:focus,
.sl-input:focus-visible {
	outline: none;
	border-bottom-color: var(--sl-signal);
	box-shadow: inset 0 -2px 0 0 var(--sl-signal);
}

.sl-textarea {
	line-height: 1.55;
	resize: vertical;
}

/* The arrow is drawn rather than linked so it inherits the text colour and
   needs no asset request. */
.sl-select {
	appearance: none;
	padding-right: 28px;
	background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
		linear-gradient(135deg, currentColor 50%, transparent 50%);
	background-position: calc(100% - 6px) calc(50% + 1px), calc(100% - 1px) calc(50% + 1px);
	background-size: 5px 5px, 5px 5px;
	background-repeat: no-repeat;
}

/* The dropdown list is drawn by the OS, so its own colours have to be set or
   it inherits the page's cream on white and reads as blank. */
.sl-select option {
	color: #0e1113;
	background: #fff;
}

/*
 * The arrow again, one level more specific. `select.sl-input` above sets
 * `background: transparent`, and that shorthand wiped the arrow out, so the
 * dropdowns rendered with no arrow at all and read as typed text.
 */
select.sl-input.sl-select {
	padding-right: 28px;
	background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
		linear-gradient(135deg, currentColor 50%, transparent 50%);
	background-position: calc(100% - 6px) calc(50% + 1px), calc(100% - 1px) calc(50% + 1px);
	background-size: 5px 5px, 5px 5px;
	background-repeat: no-repeat;
}

/*
 * The form is a white card with underlined fields at every width.
 *
 * Dark type and a dark rule on white, dark mono titles over each line, the
 * dropdowns with an up-and-down chevron, and the send button as a dark pill.
 * Scoped away from the sent state, which keeps the dark panel.
 */
.sl-form:not(.sl-form--sent) {
	color: #0e1113;
	background: #fff;
	border-color: #fff;
	/* The same tokens drive the text, the rule, the autofill cover and the caret. */
	--sl-field-bg: #fff;
	--sl-field-text: #0e1113;
	--sl-field-muted: #6b6f73;
	--sl-field-edge: #0e1113;
}

.sl-form .sl-field > .sl-label {
	color: #0e1113;
}

/* A size up on desktop, where 11px read as small beside 16px field text. */
@media (min-width: 783px) {
	.sl-form .sl-field > .sl-label {
		font-size: 13px;
		font-weight: 500;
	}

	/* The sum needs one or two digits, not a full-height field. */
	.sl-form .sl-form__check .sl-input {
		padding-block: 4px 5px;
	}

	/*
	 * The message box starts its text a little way down rather than hard against
	 * the label. The height is fixed so the extra padding does not grow the box.
	 */
	.sl-form .sl-textarea {
		height: 147px;
		padding-top: 28px;
	}
}

.sl-form .sl-form__hint {
	color: #6b6f73;
}

.sl-form select.sl-input.sl-select {
	padding-right: 28px;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 14 14' fill='none'%3E%3Cpath d='M3.5 5.5 7 2l3.5 3.5M3.5 8.5 7 12l3.5-3.5' stroke='%230e1113' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	background-position: right 2px center;
	background-size: 14px 14px;
	background-repeat: no-repeat;
}

/* Violet rather than the signal lime, which barely shows on white. */
.sl-form .sl-input:focus,
.sl-form .sl-input:focus-visible {
	border-bottom-color: var(--sl-primary);
	box-shadow: none;
}

.sl-form .sl-field__flag {
	color: #6b6f73;
}

.sl-form .sl-field__flag.is-ok {
	color: var(--sl-primary);
}

.sl-form .sl-form__error {
	color: #0e1113;
	background: #efecfe;
}

.sl-form .sl-form__foot .sl-button {
	color: #fff;
	background: #0e1113;
	border-color: #0e1113;
	border-radius: 999px;
}

.sl-form .sl-form__foot .sl-button:hover,
.sl-form .sl-form__foot .sl-button:focus-visible {
	color: #fff;
	background: var(--sl-primary);
	border-color: var(--sl-primary);
}

/*
 * On phones the card tightens up. Each field reads top to bottom as a title,
 * then its line, so a title can only belong to the line directly under it,
 * and the example text is dropped because the title is enough.
 */
@media (max-width: 782px) {
	.sl-form:not(.sl-form--sent) {
		gap: 22px;
		padding: 26px 22px 24px;
	}

	.sl-form__row {
		row-gap: 22px;
	}

	.sl-form .sl-field {
		gap: 4px;
	}

	/* The brand's mono label, a size up so it holds on white. */
	.sl-form .sl-field > .sl-label {
		margin: 0;
		font-family: var(--sl-font-mono);
		font-size: 12px;
		font-weight: 500;
		line-height: 1.4;
		letter-spacing: var(--sl-label-tracking);
		text-transform: uppercase;
	}

	.sl-form .sl-field:has(:required) .sl-field__name::after {
		content: "*";
	}

	.sl-form input.sl-input,
	.sl-form select.sl-input,
	.sl-form textarea.sl-input {
		padding: 6px 0 8px;
		font-size: 17px;
	}

	.sl-form textarea.sl-input {
		height: 84px;
		min-height: 84px;
	}

	.sl-form .sl-input::placeholder {
		color: transparent;
		opacity: 0;
	}

	.sl-form .sl-form__foot .sl-button {
		min-height: 50px;
	}
}

/*
 * Chips as real checkboxes with a styled label. The input stays in the
 * accessibility tree and reachable by keyboard rather than being replaced by a
 * button whose pressed state has to be reimplemented.
 */
.sl-choices {
	display: flex;
	flex-wrap: wrap;
	gap: 9px;
}

.sl-choice__input {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

.sl-choice {
	display: inline-flex;
	align-items: center;
	padding: 10px 15px;
	font-size: 15px;
	font-weight: 500;
	color: var(--sl-text-soft);
	background: transparent;
	border: var(--sl-border-w) solid var(--sl-hairline-strong);
	border-radius: var(--sl-radius);
	cursor: pointer;
	transition: border-color var(--sl-duration) var(--sl-ease),
		background-color var(--sl-duration) var(--sl-ease),
		color var(--sl-duration) var(--sl-ease);
}

.sl-choice:hover {
	border-color: var(--sl-text);
	color: var(--sl-text);
}

.sl-choice__input:checked + .sl-choice {
	color: var(--sl-text);
	background: color-mix(in srgb, var(--sl-primary) 22%, transparent);
	border-color: var(--sl-link);
}

.sl-choice__input:focus-visible + .sl-choice {
	outline: 2px solid var(--sl-link);
	outline-offset: 2px;
}

/* A label that carries a live status on its right-hand end. */
.sl-label--split {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 8px;
}

.sl-field__flag {
	font-family: var(--sl-font-mono);
	font-size: 10px;
	letter-spacing: var(--sl-label-tracking);
	text-transform: uppercase;
	color: var(--sl-muted);
	transition: color var(--sl-duration) var(--sl-ease);
}

.sl-field__flag.is-ok {
	color: var(--sl-signal);
}

.sl-scheme-light .sl-field__flag.is-ok {
	color: var(--sl-primary);
}

/* Amber, not red: nothing has failed, the address just is not finished yet. */
.sl-field__flag.is-warn {
	color: var(--sl-warn);
}

/*
 * Progress. Starts at zero width, so with scripting off it is an inert hairline
 * rather than a bar stuck at empty — which would read as an error state.
 */
.sl-form__foot {
	display: flex;
	justify-content: flex-start;
	align-items: center;
	gap: 20px;
	flex-wrap: wrap;
}

.sl-form__hint {
	color: var(--sl-muted);
}

/* The anti-bot sum: a full-width field like the rest, on its own line above the button. */
.sl-form__check {
	flex-basis: 100%;
}

.sl-form__error {
	margin: 0;
	padding: 14px 16px;
	font-size: 16px;
	color: var(--sl-text);
	background: color-mix(in srgb, var(--sl-primary) 14%, var(--sl-surface));
	border: var(--sl-border-w) solid var(--sl-link);
	border-radius: var(--sl-radius);
}

/* Hidden from sight and from assistive technology, but still submitted. */
.sl-form__trap {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

.sl-form--sent {
	gap: 16px;
	justify-content: center;
	min-height: 460px;
	padding: 44px 40px;
	border-color: color-mix(in srgb, var(--sl-signal) 35%, transparent);
	background: color-mix(in srgb, var(--sl-signal) 6%, var(--sl-surface));
}

.sl-form--sent .sl-form__flag {
	color: var(--sl-signal);
}

.sl-scheme-light .sl-form--sent .sl-form__flag {
	color: var(--sl-primary);
}

.sl-form__heading {
	margin: 0;
	font-size: clamp(28px, 3.4vw, 42px);
	line-height: 1.06;
}

.sl-form__body {
	margin: 0 0 10px;
	max-width: 52ch;
	color: var(--sl-text-soft);
}

.sl-form--sent .sl-button {
	align-self: flex-start;
}

@media (max-width: 640px) {
	.sl-form {
		padding: 26px 20px;
	}

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

	.sl-form__foot {
		flex-direction: column;
		align-items: stretch;
	}

	.sl-form__foot .sl-button {
		justify-content: center;
	}

	.sl-form--sent {
		min-height: 0;
		padding: 32px 24px;
	}
}

/*
 * The ruled divider, stacked.
 *
 * align-items: flex-end is right for a row — it sits the note on the same
 * baseline as the label. The moment the container turns into a column below
 * 1024px that same declaration pushes both labels against the right edge, which
 * reads as an alignment mistake rather than a decision. Left-align them and
 * close the gap, because stacked they are a label and its note, not a pair
 * spread across a rule.
 */
@media (max-width: 1024px) {
	.sl-rule-row {
		align-items: flex-start;
		gap: 4px;
	}
}

/* ------------------------------------------------------- Team — split */

/*
 * The horizontal card: index and discipline across the top, the name at
 * display size, and a fixed-width portrait down the right with an accent bar
 * along its foot.
 *
 * Grid areas rather than source order, because the index row is printed first
 * (it reads first, and it is what a screen reader should hit first) but has to
 * render inside the left column beside the photo.
 */
.sl-team--split {
	gap: 24px;
}

.sl-team--split .sl-team__member {
	display: grid;
	grid-template-columns: minmax(0, 1fr) 220px;
	grid-template-rows: auto 1fr;
	grid-template-areas:
		"top   photo"
		"body  photo";
	gap: 0;
	min-height: 280px;
	padding: 32px 0 32px 28px;
	border: var(--sl-border-w) solid var(--sl-hairline-strong);
	border-radius: var(--sl-radius);
	background: var(--sl-surface);
	overflow: hidden;
}

.sl-team--split .sl-team__top {
	grid-area: top;
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 12px;
	padding-right: 28px;
}

.sl-team--split .sl-team__index {
	color: var(--sl-link);
}

.sl-team--split .sl-team__member--signal .sl-team__index,
.sl-team--split .sl-team__member--signal .sl-team__role {
	color: var(--sl-signal);
}

.sl-scheme-light .sl-team--split .sl-team__member--signal .sl-team__index,
.sl-scheme-light .sl-team--split .sl-team__member--signal .sl-team__role {
	color: var(--sl-primary);
}

.sl-team--split .sl-team__body {
	grid-area: body;
	display: flex;
	flex-direction: column;
	padding-right: 28px;
	min-width: 0;
}

/* The name is the loudest thing on the card. */
.sl-team--split .sl-team__name {
	margin: 22px 0 0;
	font-family: var(--sl-font-display);
	font-weight: 800;
	font-size: clamp(28px, 3vw, 42px);
	line-height: 0.98;
	letter-spacing: -0.04em;
	text-transform: uppercase;
	text-wrap: balance;
}

.sl-team--split .sl-team__role {
	display: block;
	margin-top: 12px;
	color: var(--sl-link);
}

/* Pushed to the foot so every card's bio sits on the same line. */
.sl-team--split .sl-team__bio {
	margin: auto 0 0;
	padding-top: 20px;
	max-width: 340px;
	color: var(--sl-text-soft);
}

.sl-team--split .sl-team__photo {
	grid-area: photo;
	position: relative;
	align-self: stretch;
	border-radius: 0;
	border-left: var(--sl-border-w) solid var(--sl-hairline);
	background: var(--sl-surface-2);
}

.sl-team--split .sl-team__photo::after {
	content: "";
	position: absolute;
	inset: auto 0 0 0;
	height: 4px;
	background: var(--sl-link);
}

.sl-team--split .sl-team__member--signal .sl-team__photo::after {
	background: var(--sl-signal);
}

.sl-team--split .sl-team__photo img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/*
 * Below 560px the portrait column cannot hold its width without squeezing the
 * name, so the card stacks and the photo takes a band of its own.
 */
@media (max-width: 560px) {
	.sl-team--split .sl-team__member {
		grid-template-columns: minmax(0, 1fr);
		grid-template-areas:
			"top"
			"body"
			"photo";
		padding: 26px 0 0 22px;
	}

	.sl-team--split .sl-team__top,
	.sl-team--split .sl-team__body {
		padding-right: 22px;
	}

	.sl-team--split .sl-team__photo {
		margin-top: 24px;
		min-height: 240px;
		border-left: 0;
		border-top: var(--sl-border-w) solid var(--sl-hairline);
	}
}

/* ---------------------------------------------------------------------------
   Founder
   A portrait column against a text column. The portrait is fixed at 4:5 so the
   two columns finish level regardless of how the bio wraps — the alternative,
   letting the image find its own height, leaves a ragged gap under whichever
   column loses.
   --------------------------------------------------------------------------- */

.sl-founder {
	display: grid;
	grid-template-columns: minmax(0, 45fr) minmax(0, 55fr);
	gap: 56px;
	align-items: start;
}

/* Elementor's kit writes .elementor-widget-heading .elementor-heading-title
   into the per-page stylesheet, which loads after this file — so matching its
   0-2-0 is not enough, the name has to outrank it. */
.elementor-widget.sl-founder__name .elementor-heading-title {
	color: var(--sl-text);
}

.sl-founder__body p {
	max-width: 600px;
}

/*
 * Elementor gives every container flex-wrap: wrap. This one is a column of
 * exactly two items, and a wrapping column whose height has already resolved
 * feeds back into the portrait's aspect-ratio instead of obeying it: the frame
 * collapsed from square to about 11:10 and the cover crop pulled in tight on
 * the face. The aside is a stack of two things and must never wrap.
 */
.sl-founder__aside {
	flex-wrap: nowrap;
}

/* The figure pair butts up under the portrait, so the frame is shared: the
   portrait loses its bottom corners and the tiles carry the closing edge. */
.sl-founder__aside .sl-portrait {
	border-radius: var(--sl-radius) var(--sl-radius) 0 0;
}

.sl-founder__figures .sl-stats {
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: var(--sl-border-w);
	background: var(--sl-hairline);
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-top: 0;
	border-radius: 0 0 var(--sl-radius) var(--sl-radius);
	overflow: hidden;
}

.sl-founder__figures .sl-stat {
	background: var(--sl-surface);
	padding: 16px 18px;
}

.sl-founder__figures .sl-stat__value {
	font-weight: 300;
	font-size: 26px;
	letter-spacing: -0.03em;
}

.sl-founder__figures .sl-stat__label {
	margin-top: 4px;
}

.sl-portrait {
	position: relative;
	aspect-ratio: 1 / 1;
	overflow: hidden;
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-radius: var(--sl-radius);
	background: var(--sl-surface);
}

/* Elementor nests the image inside its own container div, so the chain has to
   be carried all the way down or the object-fit has nothing to fit to. */
.sl-portrait .elementor-widget-container,
.sl-portrait figure,
.sl-portrait a {
	display: block;
	height: 100%;
}

.sl-portrait img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

/* The signal bar reads as a colour stop under the photo, so it sits on the
   frame rather than on the image, which may not reach the full width. */
.sl-portrait::after {
	content: "";
	position: absolute;
	inset: auto 0 0;
	height: 4px;
	background: var(--sl-signal);
}

.sl-pullquote,
.elementor-widget.sl-pullquote .elementor-widget-container {
	padding-left: 20px;
	border-left: 2px solid var(--sl-signal);
}

.sl-pullquote p,
.elementor-widget.sl-pullquote p {
	margin: 0;
	font-family: var(--sl-font-display);
	font-weight: 300;
	font-size: clamp(20px, 2.2vw, 26px);
	line-height: 1.28;
	letter-spacing: -0.02em;
	color: var(--sl-text);
	text-wrap: pretty;
}

/* .sl-label sets the inner title to muted at 0-3-1, so the accent has to
   outrank it on the same element rather than inherit down from the wrapper. */
.elementor-widget.elementor-widget-heading.sl-label.sl-founder__role .elementor-heading-title,
.sl-founder__role {
	color: var(--sl-signal);
}

/* ---------------------------------------------------------------------------
   Social row
   --------------------------------------------------------------------------- */

.sl-social-row__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
}

.sl-social-row__label {
	display: block;
	margin-bottom: 12px;
}

.sl-social-row__link {
	display: grid;
	place-items: center;
	color: var(--sl-muted);
	transition: color var(--sl-duration) var(--sl-ease),
		border-color var(--sl-duration) var(--sl-ease),
		background-color var(--sl-duration) var(--sl-ease);
}

.sl-social-row--tiles .sl-social-row__link {
	width: 44px;
	height: 44px;
	border: var(--sl-border-w) solid var(--sl-hairline);
	border-radius: var(--sl-radius);
	background: var(--sl-surface);
}

.sl-social-row--tiles .sl-social-row__link:hover,
.sl-social-row--tiles .sl-social-row__link:focus-visible {
	color: var(--sl-bg);
	background: var(--sl-signal);
	border-color: var(--sl-signal);
}

.sl-social-row--bare .sl-social-row__list {
	gap: 18px;
}

.sl-social-row--bare .sl-social-row__link:hover,
.sl-social-row--bare .sl-social-row__link:focus-visible {
	color: var(--sl-signal);
}

.sl-brand-icon {
	display: block;
}

/* ---------------------------------------------------------------------------
   Timeline
   The process widget with its marker promoted from a mono step number to a
   display-size year. Everything else — the threading stroke, the nodes, the
   Signal Lime terminal — already describes a sequence, which is what a history
   is; only the typography of the marker needed to change.
   --------------------------------------------------------------------------- */

.elementor-widget.sl-process-timeline .sl-process__index {
	font-family: var(--sl-font-display);
	font-weight: 300;
	font-size: 30px;
	line-height: 1;
	letter-spacing: -0.04em;
	text-transform: none;
	color: var(--sl-muted);
}

.elementor-widget.sl-process-timeline .sl-process__step--terminal .sl-process__index {
	color: var(--sl-signal);
}

.elementor-widget.sl-process-timeline .sl-process__title {
	font-family: var(--sl-font-body);
	font-weight: 500;
	font-size: 17px;
	letter-spacing: 0;
	margin: 12px 0 6px;
}

@media (max-width: 782px) {
	.elementor-widget.sl-process-timeline .sl-process__index {
		font-size: 26px;
	}
}

.elementor-widget.sl-grid-timeline .sl-grid-item__head {
	display: block;
}

.elementor-widget.sl-grid-timeline .sl-grid-item__index {
	font-family: var(--sl-font-display);
	font-weight: 300;
	font-size: 34px;
	line-height: 1;
	letter-spacing: -0.04em;
	text-transform: none;
	color: var(--sl-muted);
}

.elementor-widget.sl-grid-timeline .sl-grid-item--featured .sl-grid-item__index {
	color: var(--sl-signal);
}

.elementor-widget.sl-grid-timeline .sl-grid-item__title {
	font-family: var(--sl-font-body);
	font-weight: 500;
	font-size: 17px;
	line-height: 1.3;
	letter-spacing: 0;
	margin: 14px 0 6px;
	text-transform: none;
}

@media (max-width: 1024px) {
	.sl-founder {
		grid-template-columns: minmax(0, 1fr);
		gap: 36px;
	}

	.sl-founder__aside {
		max-width: 380px;
	}
}

/* ---------------------------------------------------------------------------
   Figure plate

   The hero stroke as an object with thickness. Three copies of one path sit at
   different depths inside a perspective, so the browser foreshortens the back
   layers itself — they shrink and drift as the stage turns, the way real
   parallax does. Drawing the same offset by hand gives you a sticker.
   --------------------------------------------------------------------------- */

.sl-figure {
	--sl-figure-ink-a: var(--sl-primary);
	--sl-figure-ink-b: color-mix(in srgb, var(--sl-primary) 62%, #fff);

	position: relative;
	display: flex;
	flex-direction: column;
	gap: 20px;
	margin: 0;
}

.sl-figure__stage {
	position: relative;
	aspect-ratio: 1 / 1;
	perspective: 1100px;
	perspective-origin: 62% 42%;
	transform-style: preserve-3d;
}

.sl-figure__layer {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	transform-style: preserve-3d;
	/* The tilt is applied to each layer rather than to the stage, because a
	   parent transform would flatten the children into its own plane. */
	transform:
		rotateX(var(--sl-figure-rx, 6deg))
		rotateY(var(--sl-figure-ry, -14deg))
		translateZ(var(--sl-figure-z, 0));
	transition: transform 420ms var(--sl-ease);
}

.sl-figure__layer--0 {
	--sl-figure-z: 72px;
	filter: drop-shadow(0 26px 34px rgb(0 0 0 / 0.45));
}

.sl-figure__layer--1 {
	--sl-figure-z: 0px;
	color: color-mix(in srgb, var(--sl-text) 13%, transparent);
}

.sl-figure__layer--2 {
	--sl-figure-z: -78px;
	color: color-mix(in srgb, var(--sl-text) 7%, transparent);
}

/* Gradient stops set here rather than on the element, so they follow the
   palette — var() inside a presentation attribute is not dependable. */
.sl-figure__stop--a {
	stop-color: var(--sl-figure-ink-a);
}

.sl-figure__stop--b {
	stop-color: var(--sl-figure-ink-b);
}

/* A sphere rather than a dot: the offset highlight is what gives it volume. */
.sl-figure__node {
	position: absolute;
	right: 13%;
	bottom: 14%;
	width: 13%;
	aspect-ratio: 1;
	border-radius: 50%;
	background:
		radial-gradient(circle at 32% 28%,
			color-mix(in srgb, var(--sl-signal) 55%, #fff) 0%,
			var(--sl-signal) 46%,
			color-mix(in srgb, var(--sl-signal) 68%, #000) 100%);
	box-shadow: 0 18px 30px rgb(0 0 0 / 0.5);
	/* Carries the same rotation as the layers, or it would sit still while the
	   stroke turns and read as a separate flat element. */
	transform:
		rotateX(var(--sl-figure-rx, 6deg))
		rotateY(var(--sl-figure-ry, -14deg))
		translateZ(var(--sl-figure-node-z, 96px));
	transition: transform 420ms var(--sl-ease);
}

.sl-figure__caption {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 16px;
	flex-wrap: wrap;
}

@media (prefers-reduced-motion: reduce) {
	.sl-figure__layer,
	.sl-figure__node {
		transition: none;
	}
}

@media (max-width: 782px) {
	.sl-figure__stage {
		perspective: 800px;
	}

	.sl-figure__layer--0 {
		--sl-figure-z: 46px;
	}

	.sl-figure__layer--2 {
		--sl-figure-z: -50px;
	}
}

/* ---------------------------------------------------------------------------
   Hero fit

   Above 1200 the hero keeps its authored padding — there is height to spend on
   a wide desktop and the rhythm is the point.

   Below it the screens are laptops: wide, but short. A 13-inch MacBook is
   1440x900 before browser chrome, so a fixed 132px of padding on top of the
   header pushes the lead paragraph toward the fold and the page opens on empty
   space. Here the section takes the height it actually has and centres in it,
   so the spacing is whatever is left over rather than a fixed number competing
   with the viewport.

   Two Elementor mechanics decide how this has to be written.

   The padding is not on this element. A boxed container carries it on the inner
   wrapper, fed by --padding-top / --padding-bottom set here — so `padding-block`
   on the outer does not replace the authored value, it stacks on top of it. The
   first attempt at this added 64px to the existing 132 and made the section
   taller, not shorter.

   And the kit writes those properties at `.elementor-N .elementor-element.
   elementor-element-ID`, which is 0-3-0 in a file that loads after this one.
   Matching it is not enough; these selectors carry four classes to outrank it.
   --------------------------------------------------------------------------- */

@media (max-width: 1200px) {
	.sl-hero-fit.sl-hero-fit.e-con.e-flex {
		--padding-top: 48px;
		--padding-bottom: 48px;

		/* svh, not vh: on mobile browsers vh is the tallest the viewport ever
		   gets, which would push the section under the address bar on load. */
		min-height: calc(100svh - var(--sl-header-h));
		justify-content: center;
	}
}

/* Short-but-wide screens have the same problem above 1200 — a laptop with a
   bookmarks bar open, or a stacked window on an external monitor. */
@media (min-width: 1201px) and (max-height: 900px) {
	.sl-hero-fit.sl-hero-fit.e-con.e-flex {
		--padding-top: 64px;
		--padding-bottom: 64px;

		min-height: calc(100svh - var(--sl-header-h));
		justify-content: center;
	}
}

/*
 * The plate is square, so on a short screen it — not the padding — is the thing
 * making the hero taller than the fold. Capping its width against the viewport
 * height caps the height with it, because the aspect ratio does the rest.
 */
@media (max-height: 900px) {
	.sl-hero-fit .sl-figure {
		max-width: 52svh;
		margin-inline: auto;
	}
}

@media (max-width: 1200px) and (max-height: 1000px) {
	.sl-hero-fit .sl-figure {
		max-width: 48svh;
	}
}

@media (max-width: 782px) {
	/*
	 * Stacked, the two columns are genuinely taller than the viewport, so a
	 * min-height would only add dead space under them, and the plate can have
	 * its full column width back.
	 */
	.sl-hero-fit.sl-hero-fit.e-con.e-flex {
		min-height: 0;
	}

	.sl-hero-fit .sl-figure {
		max-width: none;
	}
}

/* ---------------------------------------------------------------------------
   Process — ledger layout

   One row per phase: marker, title, description. The rows carry their own
   separators rather than sitting in a bordered table, so the section reads as a
   list of work rather than as data.
   --------------------------------------------------------------------------- */

.sl-process--ledger {
	--sl-ledger-rail: 3px;
}

/*
 * The span bar. Segment widths come from the widget, weighted by the real week
 * range in each phase, so the bar is a schedule rather than four equal blocks.
 */
/*
 * The header above a ledger drops its rule, because the span bar is the rule.
 *
 * A .sl-rule-row draws a hairline under its labels, which put a second
 * horizontal line 26px above the bar — two rules stacked with a gap between
 * them reads as a mistake rather than as structure. Scoped with :has() so
 * every other rule-row on the site keeps its line.
 */
.sl-rule-row:has(+ .elementor-widget-softliro-process .sl-process--ledger) {
	padding-bottom: 0;
	border-bottom: 0;
}

.sl-ledger__bar {
	display: flex;
	gap: 4px;
	margin: 22px 0 34px;
}

/* A segment is its line plus, on the first and last, the label under it. */
.sl-ledger__seg {
	/* Shrinkable, so the gaps between segments come out of the widths rather
	   than pushing the last one past the edge of the rows below. */
	flex: 0 1 var(--sl-seg, 25%);
	display: flex;
	flex-direction: column;
	gap: 10px;
	min-width: 0;
}

.sl-ledger__seg::before {
	content: "";
	display: block;
	height: 3px;
	border-radius: 2px;
	background: var(--sl-hairline-strong);
	transition: background-color var(--sl-duration) var(--sl-ease);
}

.sl-ledger__seg--terminal::before {
	background: color-mix(in srgb, var(--sl-signal) 62%, transparent);
}

/* Set from the row the pointer is on — see tieLedgers() in sections.js. */
.sl-ledger__seg.is-active::before {
	background: var(--sl-text);
}

/* Narrow segments on a phone: the label may run past its line, not wrap. */
.sl-ledger__seg-label {
	white-space: nowrap;
	transition: color var(--sl-duration) var(--sl-ease);
}

.sl-ledger__seg.is-active .sl-ledger__seg-label {
	color: var(--sl-text);
}

.sl-ledger__seg--terminal .sl-ledger__seg-label {
	color: var(--sl-signal);
}

/* The end label closes the bar: right-aligned under the last segment. */
.sl-ledger__seg:last-child:not(:first-child) .sl-ledger__seg-label {
	align-self: flex-end;
}

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

.sl-ledger__row {
	display: grid;
	grid-template-columns: minmax(0, 210px) minmax(0, 1fr) minmax(0, 1fr);
	align-items: start;
	gap: 0 40px;
	padding: 34px 24px 34px 0;
	border-top: var(--sl-border-w) solid var(--sl-hairline);
	/* The rail is drawn as an inset shadow rather than a left border, so the
	   row does not shift sideways by 3px when it lights up. */
	box-shadow: inset var(--sl-ledger-rail) 0 0 -3px transparent;
	transition: background-color var(--sl-duration) var(--sl-ease),
		box-shadow var(--sl-duration) var(--sl-ease);
}

.sl-ledger__row:last-child {
	border-bottom: var(--sl-border-w) solid var(--sl-hairline);
}

.sl-ledger__row:hover {
	background: color-mix(in srgb, var(--sl-signal) 6%, transparent);
	box-shadow: inset var(--sl-ledger-rail) 0 0 0 var(--sl-signal);
}

.sl-ledger__meta {
	display: flex;
	align-items: center;
	gap: 10px;
	/* Just clear of the 3px hover rail, so the weeks stay readable when the row
	   lights up. */
	padding-left: 5px;
	min-width: 0;
}

.sl-ledger__icon {
	flex: none;
	color: var(--sl-muted);
}

.sl-ledger__row--terminal .sl-ledger__icon,
.sl-ledger__row--terminal .sl-ledger__index {
	color: var(--sl-signal);
}

/* Same face, weight, size and tracking as .sl-tier__title, so a step heading
   and a package heading read as the same rank of thing. */
.sl-ledger__title {
	margin: 0;
	font-family: var(--sl-font-display);
	font-weight: 700;
	font-size: 26px;
	line-height: 1.15;
	letter-spacing: -0.03em;
	text-wrap: balance;
}

.sl-ledger__desc {
	margin: 0;
	font-size: 16px;
	line-height: 1.5;
	color: var(--sl-text-soft);
	max-width: 46ch;
}

@media (max-width: 1024px) {
	.sl-ledger__row {
		grid-template-columns: minmax(0, 180px) minmax(0, 1fr);
		gap: 12px 28px;
	}

	/* The description drops under the title rather than being squeezed into a
	   third column that is no longer wide enough to hold a readable line. */
	.sl-ledger__desc {
		grid-column: 2;
	}
}

@media (max-width: 782px) {
	.sl-ledger__row {
		grid-template-columns: minmax(0, 1fr);
		gap: 10px;
		padding: 22px 16px 22px 0;
	}

	.sl-ledger__meta,
	.sl-ledger__desc {
		grid-column: auto;
	}

	.sl-ledger__bar {
		gap: 3px;
	}
}

/* ---------------------------------------------------------------------------
   Work filters

   Services sit in the open. Industry and platform live in a panel that opens
   over the grid rather than pushing it down — a filter that reflows the page
   as you reach for it is a filter you misclick.
   --------------------------------------------------------------------------- */

/* Phones go straight to the work: the filter bar filled the first screen. */
@media (max-width: 767px) {
	.sl-filters {
		display: none;
	}
}

/* Two columns now the row has no legend: the chips, and the button. */
.sl-filters__row {
	display: grid;
	grid-template-columns: minmax(0, 1fr) auto;
	align-items: start;
	gap: 16px;
}

.sl-filters__row .sl-filter {
	min-width: 0;
}

/* A term with nothing behind it still filters; it just says so quietly. */
.sl-filter__item.is-empty {
	color: var(--sl-text-faint);
	border-color: var(--sl-hairline);
}

.sl-filters__control {
	position: relative;
}

.sl-filters__toggle {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 9px 16px;
	color: var(--sl-text);
	background: none;
	border: var(--sl-border-w) solid var(--sl-hairline-strong);
	border-radius: var(--sl-radius);
	cursor: pointer;
	white-space: nowrap;
	transition: border-color var(--sl-duration) var(--sl-ease),
		color var(--sl-duration) var(--sl-ease);
}

.sl-filters__toggle:hover,
.sl-filters__toggle[aria-expanded="true"] {
	border-color: var(--sl-signal);
	color: var(--sl-signal);
}

.sl-filters__badge {
	display: inline-grid;
	place-items: center;
	min-width: 18px;
	height: 18px;
	padding: 0 5px;
	font-size: 10px;
	color: var(--sl-bg);
	background: var(--sl-signal);
	border-radius: 9px;
}

/*
 * Anchored to the button and pulled out of flow. Right-aligned so it opens
 * inward from the page edge instead of off it.
 */
.sl-filters__panel {
	position: absolute;
	z-index: 20;
	top: calc(100% + 10px);
	right: 0;
	/*
	 * Wide enough that the chips wrap into a few rows rather than a column of
	 * them, which is what pushed the panel past the bottom of the screen —
	 * and no wider, or one short list ends up stretched across the page.
	 */
	width: min(92vw, 660px);
	padding: 22px 22px 18px;
	background: var(--sl-surface);
	border: var(--sl-border-w) solid var(--sl-hairline-strong);
	border-radius: var(--sl-radius);
	box-shadow: 0 24px 48px rgb(0 0 0 / 0.45);
	overflow-y: auto;
	overscroll-behavior: contain;
}

/* Opened upward, when there is more room over the button than under it. */
.sl-filters__panel.is-above {
	top: auto;
	bottom: calc(100% + 10px);
}

.sl-filters__panel[hidden] {
	display: none;
}

/*
 * The two facets sit side by side where there is room. Stacked, the second one
 * started below the fold and the reader had to scroll a floating panel to see
 * that it existed.
 */
@media (min-width: 783px) {
	.sl-filters__panel {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 26px;
		align-items: start;
	}

	/* Two lists side by side need the room; one does not. */
	.sl-filters__panel:has(.sl-filters__group + .sl-filters__group) {
		width: min(92vw, 1040px);
	}

	.sl-filters__group + .sl-filters__group {
		padding-left: 26px;
		border-left: var(--sl-border-w) solid var(--sl-hairline);
	}

	/* One facet takes the whole panel rather than half of it. */
	.sl-filters__group:only-child {
		grid-column: 1 / -1;
	}

	/* The clear link belongs under both columns, not inside the second one. */
	.sl-filters__clear {
		grid-column: 1 / -1;
	}
}

@media (max-width: 782px) {
	.sl-filters__group + .sl-filters__group {
		margin-top: 20px;
		padding-top: 20px;
		border-top: var(--sl-border-w) solid var(--sl-hairline);
	}

}

.sl-filters__group-title {
	display: block;
	margin-bottom: 12px;
	color: var(--sl-muted);
}

.sl-filters__clear {
	display: inline-block;
	margin-top: 20px;
	color: var(--sl-signal);
}

@media (max-width: 782px) {
	.sl-filters__row {
		grid-template-columns: minmax(0, 1fr);
	}

	.sl-filters__control {
		justify-self: start;
	}

	/* Anchored left on a narrow screen, or it hangs off the side. */
	.sl-filters__panel {
		right: auto;
		left: 0;
	}
}

/* ------------------------------------------------------- Work carousel */

/*
 * A row of project cards that scrolls sideways. Built on native overflow with
 * scroll snapping rather than a slider library: it works with a trackpad, a
 * touchscreen, a keyboard and the scrollbar before any script runs, and the
 * buttons are an enhancement on top rather than the only way through.
 */
.sl-carousel {
	position: relative;
}

.sl-carousel__viewport {
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scroll-behavior: smooth;
	scrollbar-width: none;
	/* Room for the card's focus ring and hover lift, which would clip otherwise. */
	padding-block: 4px;
	margin-inline: -4px;
	padding-inline: 4px;
}

.sl-carousel__viewport::-webkit-scrollbar {
	display: none;
}

.sl-carousel__viewport:focus-visible {
	outline: var(--sl-focus-w, 2px) solid var(--sl-signal);
	outline-offset: 4px;
	border-radius: var(--sl-radius);
}

.sl-projects--carousel {
	display: flex;
	grid-template-columns: none;
	gap: var(--sl-gap);
}

.sl-projects--carousel .sl-project {
	/* Show the configured number of columns, and part of the next card so it
	   is visible that the row continues. */
	flex: 0 0 calc((100% - (var(--sl-projects-columns, 3) - 1) * var(--sl-gap)) / var(--sl-projects-columns, 3));
	scroll-snap-align: start;
}

.sl-carousel__controls {
	display: flex;
	justify-content: flex-end;
	gap: 10px;
	margin-top: 22px;
}

.sl-carousel__button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	padding: 0;
	background: none;
	border: var(--sl-border-w) solid var(--sl-hairline-strong);
	border-radius: var(--sl-radius);
	color: var(--sl-text);
	cursor: pointer;
	transition: border-color var(--sl-duration) var(--sl-ease),
		color var(--sl-duration) var(--sl-ease),
		opacity var(--sl-duration) var(--sl-ease);
}

.sl-carousel__button:hover:not(:disabled),
.sl-carousel__button:focus-visible {
	border-color: var(--sl-signal);
	color: var(--sl-signal);
}

/* Disabled rather than hidden: the row should not change shape as you scroll. */
.sl-carousel__button:disabled {
	opacity: 0.35;
	cursor: default;
}

.sl-carousel__chevron {
	width: 9px;
	height: 9px;
	border-top: 2px solid currentColor;
	border-right: 2px solid currentColor;
	transform: translateX(-2px) rotate(45deg);
}

.sl-carousel__chevron--prev {
	transform: translateX(2px) rotate(-135deg);
}

@media (max-width: 782px) {
	.sl-projects--carousel .sl-project {
		/* One card at a time, with a sliver of the next showing. */
		flex-basis: 86%;
	}
}

@media (prefers-reduced-motion: reduce) {
	.sl-carousel__viewport {
		scroll-behavior: auto;
	}
}

/* Turnstile sits on its own line above the button, and only shows anything
   when a visitor needs to interact with it. */
.sl-form__turnstile {
	flex-basis: 100%;
}

/*
 * The empty Turnstile holder takes no room: the gap between it and the
 * button is dropped, and space above the button is added back only when
 * Cloudflare actually shows its checkbox (an iframe inside the holder).
 * In the stacked phone layout a 100% flex-basis stretched the empty holder
 * to a band of blank space, so there it sizes to its content.
 */
.sl-form .sl-form__foot {
	row-gap: 0;
}

.sl-form .sl-form__turnstile:has(iframe) {
	margin-bottom: 16px;
}

@media (max-width: 767px) {
	.sl-form .sl-form__turnstile {
		flex-basis: auto;
	}

	/* A little more room either side of the form card on a phone. */
	form.sl-form {
		margin-inline: 2px;
	}
}

/* When Cloudflare does show its checkbox, keep it inside the card. */
.sl-form .sl-form__turnstile,
.sl-form .sl-form__turnstile > div,
.sl-form .sl-form__turnstile iframe {
	max-width: 100% !important;
}

/*
 * Client logos in the strip are white, transparent files: shown a little
 * dimmed so they sit back from the content, full white on hover. The height
 * is fixed and the width follows the file's own proportions.
 */
.sl-marquee .sl-marquee__group .sl-marquee__logo {
	height: 30px;
	width: auto;
	max-width: 180px;
	object-fit: contain;
	opacity: 0.6;
	filter: none;
	transition: opacity var(--sl-duration) var(--sl-ease);
}

.sl-marquee .sl-marquee__group .sl-marquee__logo:hover {
	opacity: 1;
}

@media (max-width: 767px) {
	.sl-marquee .sl-marquee__group .sl-marquee__logo {
		height: 24px;
		max-width: 140px;
	}
}

/*
 * The client strip on the homepage: a centred title over two rows of logos,
 * the second running the other way, with the ends fading out rather than
 * cutting a logo in half. Set on the widgets as "sl-clients-title",
 * "sl-clients-marquee" and, for the second row, "sl-marquee-reverse".
 */
.sl-clients-title .elementor-heading-title {
	margin: 0;
	font-family: var(--sl-font-display);
	font-size: clamp(20px, 2vw, 26px);
	font-weight: 400;
	letter-spacing: -0.01em;
	line-height: 1.3;
	text-align: center;
	color: var(--sl-text-soft);
}

.sl-clients-marquee .sl-marquee {
	padding-block: 16px;
	-webkit-mask-image: linear-gradient(90deg, transparent 0, #000 12%, #000 88%, transparent 100%);
	mask-image: linear-gradient(90deg, transparent 0, #000 12%, #000 88%, transparent 100%);
}

.sl-clients-marquee .sl-marquee__group {
	gap: 88px;
	padding-right: 88px;
}

.sl-marquee-reverse .sl-marquee__track {
	animation-direction: reverse;
}

@media (max-width: 767px) {
	.sl-clients-marquee .sl-marquee {
		padding-block: 12px;
	}

	.sl-clients-marquee .sl-marquee__group {
		gap: 48px;
		padding-right: 48px;
	}
}

/* Elementor's default heading colour would win over the title above. */
.elementor .sl-clients-title .elementor-heading-title {
	color: var(--sl-text-soft);
}

/* Logos in the two client rows read a touch larger. */
.sl-clients-marquee .sl-marquee .sl-marquee__group .sl-marquee__logo {
	height: 34px;
	max-width: 200px;
}

@media (max-width: 767px) {
	.sl-clients-marquee .sl-marquee .sl-marquee__group .sl-marquee__logo {
		height: 26px;
		max-width: 150px;
	}
}

/* More air between the two client rows on wider screens. */
@media (min-width: 768px) {
	.sl-clients-marquee .sl-marquee {
		padding-block: 26px;
	}
}

/* The client strip's header: the standard section header, centred. */
.sl-clients-head .sl-section-header {
	justify-content: center;
	text-align: center;
}

.sl-clients-head .sl-section-header__main {
	display: flex;
	flex-direction: column;
	align-items: center;
	margin-inline: auto;
}

.sl-clients-head .sl-section-header__main,
.sl-clients-head .sl-section-header__heading {
	max-width: none;
}

/*
 * "At a glance" rows: the label stays on one line at the left, the value sits
 * against the right edge and, when it wraps, keeps every line right-aligned,
 * with a steady gap between the two.
 */
.sl-panel__row {
	gap: 24px;
}

.sl-panel__name {
	flex: none;
	white-space: nowrap;
}

.sl-panel__value {
	flex: 1 1 auto;
	min-width: 0;
	text-align: right;
}

/* Values carry their own line breaks (two words or two items a line). */
.sl-panel__value {
	white-space: pre-line;
}
