/**
 * SwiftWorking — global layer.
 * Tokens come from theme.json (--wp--preset--*, --wp--custom--*); nothing is hard-coded here
 * except the metric overrides of the fallback face, which are measured values, not design values.
 */

/* Size-matched fallback so the swap to Archivo does not reflow text (Arial measured against Archivo 400/100). */
@font-face {
	font-family: "Archivo Fallback";
	src: local("Arial"), local("ArialMT"), local("Liberation Sans"), local("Helvetica");
	size-adjust: 101.96%;
	ascent-override: 86.11%;
	descent-override: 20.6%;
	line-gap-override: 0%;
}

:root {
	color-scheme: light;
	--sw-focus-ring: var(--wp--preset--color--violet);
	--sw-rule: color-mix(in srgb, currentColor 22%, transparent);
	--sw-control-border: color-mix(in srgb, var(--wp--preset--color--ink) 62%, var(--wp--preset--color--surface));
	--sw-press: scale(var(--wp--custom--motion--press-scale));
	scrollbar-color: var(--wp--preset--color--green) var(--wp--preset--color--surface-tint);
	-webkit-tap-highlight-color: transparent;
	text-size-adjust: 100%;
}

/* Surfaces that carry green: light focus ring, muted text tinted from the hue. */
.has-green-background-color,
.has-green-deep-background-color,
.has-violet-background-color,
.is-style-tag {
	--sw-focus-ring: var(--wp--preset--color--surface);
}

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

body {
	font-synthesis: none;
	text-rendering: optimizeLegibility;
	-webkit-font-smoothing: antialiased;
}

::selection {
	background: var(--wp--preset--color--violet);
	color: var(--wp--preset--color--surface);
}

/* Type details */
:where(h1, h2, h3, h4, h5, h6) {
	overflow-wrap: break-word;
	text-wrap: balance;
}

:where(p, li, figcaption, blockquote) {
	text-wrap: pretty;
	overflow-wrap: break-word;
}

/*
 * Hyphenate only long words (12+ characters, 5 kept on each side): German compounds and long
 * names break with a hyphen instead of at a random letter, while ordinary English display
 * words ("maintained.") never split. Headings included, so an editor's long title stays tidy.
 */
:where(.wp-block-group, .wp-block-column, .wp-block-post-template) :where(p, li, h1, h2, h3, h4) {
	hyphenate-limit-chars: 12 5 5;
	hyphens: auto;
	-webkit-hyphens: auto;
}

/* Values typed by editors take their direction from their own text (an Arabic client name). */
.is-style-field > p:not(.is-style-label),
.sw-remark__quote {
	unicode-bidi: plaintext;
}

:where(table, .is-style-log) {
	font-variant-numeric: tabular-nums;
}

a {
	text-decoration-thickness: 1px;
	text-underline-offset: 0.2em;
	transition: color var(--wp--custom--motion--duration--hover) var(--wp--custom--motion--ease), text-decoration-thickness var(--wp--custom--motion--duration--hover) var(--wp--custom--motion--ease);
}

@media (hover: hover) and (pointer: fine) {
	a:hover {
		text-decoration-thickness: 2px;
	}
}

/* Links on green surfaces inherit the surface colour. */
:where(.has-green-background-color, .has-green-deep-background-color, .is-style-tag) a:where(:not(.wp-element-button)) {
	color: inherit;
}

/* Focus: always visible, always 3:1 against the surface it sits on. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
	outline: var(--wp--custom--focus--width) solid var(--sw-focus-ring);
	outline-offset: var(--wp--custom--focus--offset);
}

:where(a, button, input, select, textarea, summary, [tabindex]):focus:not(:focus-visible) {
	outline: none;
}

/* Skip link that core adds to block themes */
.skip-link.screen-reader-text:focus {
	background: var(--wp--preset--color--violet);
	color: var(--wp--preset--color--surface);
	clip-path: none;
	font-weight: 700;
	height: auto;
	left: var(--wp--custom--gutter);
	padding: 0.875rem 1.25rem;
	text-decoration: none;
	top: 0.75rem;
	width: auto;
	z-index: 100000;
}

/* Native form controls (used by the contact block and any core search) */
:where(input:not([type="checkbox"], [type="radio"], [type="submit"], [type="button"], [type="hidden"]), textarea, select) {
	background: var(--wp--preset--color--surface);
	border: 1px solid var(--sw-control-border);
	border-radius: var(--wp--custom--radius--control);
	caret-color: var(--wp--preset--color--violet);
	color: var(--wp--preset--color--ink);
	font: inherit;
	font-size: max(1rem, var(--wp--preset--font-size--small));
	line-height: 1.4;
	min-height: 3rem;
	padding: 0.75rem 0.875rem;
	width: 100%;
	transition: border-color var(--wp--custom--motion--duration--hover) var(--wp--custom--motion--ease);
}

:where(textarea) {
	min-height: 9rem;
	resize: vertical;
}

:where(input, textarea)::placeholder {
	color: var(--wp--preset--color--ink-muted);
	opacity: 1;
}

@media (hover: hover) and (pointer: fine) {
	:where(input:not([type="checkbox"], [type="radio"], [type="submit"], [type="button"]), textarea, select):hover {
		border-color: var(--wp--preset--color--ink);
	}
}

:where(input[type="checkbox"], input[type="radio"]) {
	accent-color: var(--wp--preset--color--green);
	block-size: 1.25rem;
	inline-size: 1.25rem;
}

:where(input, textarea, select)[aria-invalid="true"] {
	border-color: var(--wp--preset--color--violet);
	border-width: 2px;
}

/* Buttons and anything pressable confirm the press. */
:where(.wp-element-button, button, [type="submit"]) {
	min-block-size: 2.75rem;
	transition:
		background-color var(--wp--custom--motion--duration--hover) var(--wp--custom--motion--ease),
		color var(--wp--custom--motion--duration--hover) var(--wp--custom--motion--ease),
		transform var(--wp--custom--motion--duration--press) var(--wp--custom--motion--ease-out);
}

@media (prefers-reduced-motion: no-preference) {
	:where(.wp-element-button, button, [type="submit"]):active {
		transform: var(--sw-press);
	}
}

:where(.wp-element-button, button)[disabled],
:where(.wp-element-button, button)[aria-disabled="true"] {
	cursor: not-allowed;
	opacity: 0.55;
	transform: none;
}

/*
 * Template chrome. A short page still ends with the footer at the bottom of the window. svh is
 * the smallest viewport, so the page never jumps when a phone's address bar collapses.
 */
.wp-site-blocks {
	display: flex;
	flex-direction: column;
	min-block-size: 100svh;
}

/*
 * Safe areas: the viewport extends under a notch (viewport-fit=cover), so the root padding
 * and the header grow to the inset where it is wider than the gutter. Full-width bands keep
 * painting edge to edge; their content stays clear of the notch and the home indicator.
 */
:root:root {
	--wp--style--root--padding-left: max(var(--wp--custom--gutter), env(safe-area-inset-left, 0px));
	--wp--style--root--padding-right: max(var(--wp--custom--gutter), env(safe-area-inset-right, 0px));
}

.sw-footer::after {
	block-size: env(safe-area-inset-bottom, 0px);
	content: "";
	display: block;
}

/* Touch: no double-tap-zoom wait on controls; a long press on a control never selects its label. */
:where(a, button, input, select, textarea, label, summary, [role="button"]) {
	touch-action: manipulation;
}

:where(.wp-element-button, button, .sw-contact__topic, .wp-block-navigation__responsive-container-open, .wp-block-navigation__responsive-container-close) {
	-webkit-touch-callout: none;
	user-select: none;
	-webkit-user-select: none;
}

.wp-site-blocks > * + * {
	margin-block-start: 0;
}

.wp-site-blocks > main {
	flex-grow: 1;
}

.sw-header {
	border-block-end: 1px solid color-mix(in srgb, var(--wp--preset--color--ink) 16%, transparent);
	gap: var(--wp--preset--spacing--40);
	padding-inline:
		max(var(--wp--custom--gutter), env(safe-area-inset-left, 0px), (100% - 80rem) / 2)
		max(var(--wp--custom--gutter), env(safe-area-inset-right, 0px), (100% - 80rem) / 2);
}

.sw-header .wp-block-button__link {
	padding: 0.7rem 1.1rem;
	white-space: nowrap;
}

/* The header CTA says "Request a free first call" with room, "Free call" on phones; the visible text is always the accessible name. */
.sw-cta__short {
	display: none;
}

@media (max-width: 47.99rem) {
	.sw-cta__long {
		display: none;
	}

	.sw-cta__short {
		display: inline;
	}

	.sw-header .wp-block-button__link::after {
		display: none;
	}

	.sw-header__actions {
		gap: var(--wp--preset--spacing--30);
	}
}

/* The smallest phones (320px): tighter gaps and side padding keep the header on one row. It wraps only at large text sizes. */
@media (max-width: 23.99rem) {
	.sw-header .wp-block-site-title :where(a, span)::before {
		display: none;
	}

	.sw-header {
		column-gap: var(--wp--preset--spacing--20);
	}

	.sw-header .wp-block-button__link {
		padding-inline: 0.8rem;
	}

	.sw-header .sw-nav .wp-block-navigation__responsive-container-open {
		padding-inline: 0.65rem;
	}
}

.sw-footer .wp-block-site-title :where(a, span)::before {
	background: var(--wp--preset--color--surface);
}

/* "More" link: text plus the system arrow (never a Unicode glyph). */
.sw-more a {
	font-weight: 600;
}

.sw-more a::after {
	background: currentColor;
	block-size: 0.75em;
	content: "";
	display: inline-block;
	inline-size: 0.95em;
	margin-inline-start: 0.45em;
	mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 14'%3E%3Cpath d='M1 7h15M10 1l6 6-6 6' fill='none' stroke='%23000' stroke-width='2'/%3E%3C/svg%3E") center / contain no-repeat;
	vertical-align: -0.04em;
}

/* Display heading with a lead line set inside the same H1. */
.sw-lead {
	display: block;
	font-size: var(--wp--preset--font-size--display-lead);
	letter-spacing: -0.01em;
	line-height: 1.15;
	margin-block-end: 0.25em;
}

/* Keep long German compounds and e-mail addresses inside narrow columns. */
:where(.wp-block-column, .is-style-tag, .is-style-paper, .is-style-field) {
	min-width: 0;
	overflow-wrap: anywhere;
}

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

.sw-hero__aside > * + * {
	margin-block-start: var(--wp--preset--spacing--40);
}

.sw-hero__aside .is-style-index {
	margin-block-start: var(--wp--preset--spacing--30);
}

/* The index already closes with a rule; the field after it shares that rule instead of drawing a second one. */
@media (min-width: 782px) {
	.sw-hero__aside .is-style-index + .is-style-field {
		border-block-start: 0;
		margin-block-start: 0;
	}
}

/* Remarks (testimonials): the quote in real quotation marks; empty bound fields collapse. */
.sw-remark__quote {
	font-weight: 500;
	letter-spacing: -0.005em;
	line-height: 1.4;
	quotes: "“" "”";
}

.sw-remark__quote::before {
	content: open-quote;
}

.sw-remark__quote::after {
	content: close-quote;
}

.sw-remark p:empty,
.sw-projects p:empty {
	display: none;
}

/* A remark is a record, but the attribution needs air below the quote and the mark hangs. */
.wp-block-group.is-style-field.sw-remark > .sw-remark__by {
	margin-block-start: var(--wp--preset--spacing--40);
}

.sw-remark__quote {
	text-indent: -0.42em;
}

/* Peter's words, at reading size; the opening quotation mark hangs so the text keeps its edge. */
.sw-team__quote {
	max-inline-size: 40rem;
}

.sw-team__quote p {
	font-weight: 500;
	line-height: 1.45;
	text-indent: -0.42em;
}

/* A portrait dropped into a person row takes the plate's place and proportions. */
.sw-person > .wp-block-image {
	flex: none;
	inline-size: 6rem;
	margin: 0;
}

.sw-person--lead > .wp-block-image {
	inline-size: clamp(6.5rem, 4.5rem + 6vw, 9.5rem);
}

.sw-crew .sw-person > .wp-block-image {
	inline-size: 3.75rem;
}

.sw-person > .wp-block-image img {
	aspect-ratio: 5 / 6;
	block-size: auto;
	border-radius: var(--wp--custom--radius--control);
	inline-size: 100%;
	object-fit: cover;
}

/* When the columns stack, a hanging tag keeps the proportions of a tag instead of becoming a panel. */
@media (max-width: 781px) {
	.sw-rail > .is-style-paper {
		max-inline-size: 32rem;
	}
}

.sw-person .wp-block-heading {
	margin-block: 0;
}

/*
 * A fact only Peter can supply. Deliberately loud: it must never survive to launch unnoticed.
 * Never styled as part of the design system; it marks a gap in the content.
 */
.sw-needs-input {
	background: var(--wp--preset--color--violet);
	border-radius: var(--wp--custom--radius--small);
	box-decoration-break: clone;
	color: var(--wp--preset--color--surface);
	font-size: 0.8em;
	font-stretch: var(--wp--custom--label--stretch);
	font-weight: 700;
	letter-spacing: 0.04em;
	padding: 0.1em 0.4em;
	-webkit-box-decoration-break: clone;
}

/* Preparation list on service pages: things to have, so the boxes are empty (to do), not ticked (done). */
.sw-prepare ul.wp-block-list.is-style-log > li::before {
	mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 28 28'%3E%3Crect x='1' y='1' width='26' height='26' rx='2' fill='none' stroke='%23000' stroke-width='2'/%3E%3C/svg%3E");
}

/* The hero tag hangs from the header rule on narrow screens: its string is exactly the top padding. */
.sw-hero {
	padding-block-start: max(var(--wp--custom--tag--string), var(--wp--preset--spacing--60));
}

/*
 * Contact page: intro, form, details in source order. On phones that is the reading order;
 * from 782px the form takes the right column beside both the intro and the details.
 */
.sw-contact-page__grid {
	display: grid;
	gap: var(--wp--preset--spacing--60);
	grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 782px) {
	.sw-contact-page__grid {
		column-gap: var(--wp--preset--spacing--70);
		grid-template-columns: minmax(0, 42fr) minmax(0, 58fr);
		grid-template-rows: auto 1fr;
	}

	.sw-contact-page__intro {
		grid-area: 1 / 1;
	}

	.sw-contact-page__form {
		align-self: start;
		grid-area: 1 / 2 / span 2;
	}

	.sw-contact-page__details {
		grid-area: 2 / 1;
	}
}

/* Beside the form, the steps are context, not the headline: their titles step down a size. */
.sw-contact-page__details .is-style-log > li > strong:first-child {
	font-size: var(--wp--preset--font-size--large);
}

/* The crew's rows close with a hairline on the last row (not the column, which stretches to Peter's height). */
.sw-crew > .is-style-field:last-child {
	border-block-end: 1px solid var(--sw-rule);
}

/* Secondary navigation inside a page (the hero's index of service lines) is header work on phones. */
@media (max-width: 781px) {
	.sw-hide-narrow {
		display: none;
	}
}

/* A stamp that links to the action it names: the stamp is the link, not an underlined word inside it. */
.is-style-stamp a {
	color: inherit;
	text-decoration: none;
}

.is-style-stamp a::after {
	content: "";
	inset: 0;
	position: absolute;
}

/* Zero specificity: a pinned stamp keeps its absolute position, which is also a containing block. */
:where(.is-style-stamp:has(a)) {
	position: relative;
}

.is-style-stamp:has(a:focus-visible) {
	outline: var(--wp--custom--focus--width) solid var(--wp--preset--color--violet);
	outline-offset: var(--wp--custom--focus--offset);
}

.is-style-stamp a:focus-visible {
	outline: none;
}

/* The form's submit carries the same arrow as every other primary action. */
.sw-contact__submit::after {
	background: currentColor;
	block-size: 0.78em;
	content: "";
	display: inline-block;
	inline-size: 1em;
	margin-inline-start: 0.6em;
	mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 14'%3E%3Cpath d='M1 7h15M10 1l6 6-6 6' fill='none' stroke='%23000' stroke-width='2'/%3E%3C/svg%3E") center / contain no-repeat;
	transition: translate var(--wp--custom--motion--duration--hover) var(--wp--custom--motion--ease-out);
	vertical-align: -0.06em;
}

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
	.sw-contact__submit:hover::after {
		translate: 0.15em 0;
	}
}

/* Footer addresses are flex items: let a long address break instead of widening the page at large text sizes. */
.sw-footer p {
	min-width: 0;
	overflow-wrap: anywhere;
}

/* Footer: the record's last line, under a hairline. */
.sw-footer__legal {
	border-block-start: 1px solid var(--sw-rule);
	padding-block-start: var(--wp--preset--spacing--40);
}

/*
 * Fields bound to optional data hide themselves when the data is missing, so a project
 * without a named client never shows a lone "Client" label.
 */
.wp-block-group.is-style-field:not(:has(> :not(.is-style-label))),
.wp-block-group.is-style-field:has(> p:not(.is-style-label):empty) {
	display: none;
}

/* The live-site button appears only when the project has an address. */
.sw-live-site:not(:has(a[href]:not([href=""]))) {
	display: none;
}

/* Work: each project is a record row; its title is the link, in ink, with the system arrow. */
.sw-work-row .wp-block-post-title a {
	color: var(--wp--preset--color--ink);
	text-decoration: none;
}

.sw-work-row .wp-block-post-title a::after {
	background: currentColor;
	block-size: 0.6em;
	content: "";
	display: inline-block;
	inline-size: 0.75em;
	margin-inline-start: 0.3em;
	mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 14'%3E%3Cpath d='M1 7h15M10 1l6 6-6 6' fill='none' stroke='%23000' stroke-width='2'/%3E%3C/svg%3E") center / contain no-repeat;
	vertical-align: 0.05em;
}

@media (hover: hover) and (pointer: fine) {
	.sw-work-row .wp-block-post-title a:hover {
		text-decoration: underline;
		text-decoration-thickness: 2px;
	}
}

/* The search count speaks in the label voice. */
.wp-block-query-total.is-style-label {
	color: var(--wp--preset--color--green);
	font-size: var(--wp--preset--font-size--label);
	font-stretch: var(--wp--custom--label--stretch);
	font-weight: var(--wp--custom--label--weight);
	letter-spacing: var(--wp--custom--label--tracking);
	line-height: 1.35;
	margin-block-end: var(--wp--preset--spacing--30);
	text-transform: uppercase;
}

/* With nothing found, the empty state says so in words; "0 results found" above it would say it twice. */
.wp-block-query:has(> .wp-block-query-no-results) > .wp-block-query-total {
	display: none;
}

/* ---------- Motion (approved in Phase 4, D-054). Every entry has a reduced-motion variant. ---------- */

/*
 * Home: the "Free first call" stamp is pressed onto the tag once, just after the hero paints.
 * Delight, once per visit. The stamp keeps its own `rotate`; only `scale` and `opacity` animate.
 */
@keyframes sw-stamp-press {
	from {
		opacity: 0;
		scale: var(--wp--custom--motion--stamp-scale);
	}
}

@keyframes sw-fade-in {
	from {
		opacity: 0;
	}
}

@media (prefers-reduced-motion: no-preference) {
	.sw-hero .is-style-stamp {
		animation: sw-stamp-press var(--wp--custom--motion--duration--stamp) var(--wp--custom--motion--ease-out) var(--wp--custom--motion--delay--stamp) both;
	}
}

@media (prefers-reduced-motion: reduce) {
	.sw-hero .is-style-stamp {
		animation: sw-fade-in var(--wp--custom--motion--duration--stamp) var(--wp--custom--motion--ease) var(--wp--custom--motion--delay--stamp) both;
	}
}

/*
 * Home: the three paper tags take one small swing from their strings when the rail scrolls
 * into view, so the "hanging" reads as hanging. They pivot at the top of the string (the
 * rail), start from rest and settle with decreasing swings. Wide screens only, where every
 * tag hangs on a string; rail-swing.js adds .is-swinging once and never again.
 * 600ms is over the 300ms UI budget on purpose: this is explanatory motion on a marketing
 * page, seen once per visit, and three decaying swings need the time. Nothing waits on it.
 */
/*
 * Each segment runs between two extremes, so a sine ease-in-out on the animation gives every
 * segment zero speed at its ends and the most speed through the middle: a pendulum. The
 * strong UI ease-in-out whips through the centre and reads as a twitch at this amplitude.
 */
@keyframes sw-swing {
	0% {
		rotate: 0deg;
	}

	25% {
		rotate: var(--wp--custom--motion--swing);
	}

	55% {
		rotate: calc(var(--wp--custom--motion--swing) * -0.4);
	}

	80% {
		rotate: calc(var(--wp--custom--motion--swing) * 0.15);
	}

	100% {
		rotate: 0deg;
	}
}

@media (min-width: 782px) and (prefers-reduced-motion: no-preference) {
	.sw-services .sw-rail > .wp-block-column > .is-style-paper {
		transform-origin: 50% calc(-1 * (var(--wp--custom--tag--string) + var(--sw-drop, 0px)));
	}

	.sw-services .sw-rail.is-swinging > .wp-block-column > .is-style-paper {
		animation: sw-swing var(--wp--custom--motion--duration--swing) var(--wp--custom--motion--ease-swing) both;
	}

	.sw-services .sw-rail.is-swinging > .wp-block-column:nth-child(2) > .is-style-paper {
		animation-delay: var(--wp--custom--motion--stagger--rail);
	}

	.sw-services .sw-rail.is-swinging > .wp-block-column:nth-child(3) > .is-style-paper {
		animation-delay: calc(var(--wp--custom--motion--stagger--rail) * 2);
	}
}

/* Pagination reads like the "more" links: weight and the system arrow, pointing the way it goes. */
.wp-block-query-pagination a {
	font-weight: 600;
}

.wp-block-query-pagination-next::after,
.wp-block-query-pagination-previous::before {
	background: currentColor;
	block-size: 0.75em;
	content: "";
	display: inline-block;
	inline-size: 0.95em;
	mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 14'%3E%3Cpath d='M1 7h15M10 1l6 6-6 6' fill='none' stroke='%23000' stroke-width='2'/%3E%3C/svg%3E") center / contain no-repeat;
	vertical-align: -0.04em;
}

.wp-block-query-pagination-next::after {
	margin-inline-start: 0.45em;
}

.wp-block-query-pagination-previous::before {
	margin-inline-end: 0.45em;
	scale: -1 1;
}

/*
 * Reading pages (legal notice, privacy policy): long text in sections. Section headings step
 * down to the title size and get more air above than below.
 */
.sw-reading :where(h2) {
	font-size: var(--wp--preset--font-size--x-large);
	margin-block: var(--wp--preset--spacing--60) var(--wp--preset--spacing--30);
}

.sw-reading :where(h3) {
	font-size: var(--wp--preset--font-size--large);
	margin-block: var(--wp--preset--spacing--50) var(--wp--preset--spacing--20);
}
