/* ==========================================================================
   Teconya — layout layer.

   Structural clone of the group system's home composition: twelve-column
   grids throughout, oversized section titles as absolutely positioned
   overlays, sticky difference-blended subtitles, asymmetric project rows.
   ========================================================================== */

/* --- Section titles ----------------------------------------------------- */

.section-title {
	--travel-clamped: 60dvh;
	--travel: 40dvh;
	--progress: 0;

	position: absolute;
	top: 0;
	inset-inline-start: 0;
	width: 100%;
	height: 100%;
	display: flex;
	flex-direction: column;
	justify-content: center;
	padding: var(--pad);
	pointer-events: none;
	z-index: 1;

	/* Rounded to 3vh steps so the parallax moves in discrete jumps rather
	   than sliding, matching the group system's stepped motion. */
	transform: translateY(
		round(
			nearest,
			calc( ( var(--travel-clamped) * -1 ) * var(--progress) + ( var(--travel-clamped) * 0.5 ) ),
			3vh
		)
	);
}

.section-title h1,
.section-title h2 {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	grid-auto-flow: row;
	gap: var(--gap);
	width: 100%;
	font-family: var(--font-sans);
	font-size: 10vw;
	line-height: 1em;
	color: var(--foreground-dim);
}

/* The group system scales section titles up only on the narrowest screens. */
@media only screen and (max-width: 584px) {
	.section-title h1,
	.section-title h2 { font-size: 15vw; }
}

/* Below this the longest word stops fitting even across all twelve columns —
   "Engagements" measures 318px against a 304px grid at 320px wide. The face
   comes down rather than the word running off the screen. */
@media only screen and (max-width: 380px) {
	.section-title h1,
	.section-title h2 { font-size: 13vw; }
}

.section-title h1 > span,
.section-title h2 > span {
	grid-column: span 4;
	align-self: start;
	transform: translateY(
		calc( ( var(--travel) * -1 ) * var(--progress) + ( var(--travel) * 0.5 ) )
	);
}

.section-title h1 > span > span,
.section-title h2 > span > span {
	display: inline-block;
	position: relative;
}

.section-title__link {
	position: absolute;
	top: 0;
	inset-inline-end: calc( var(--gap) * -1 );
	transform: translate( 100% );
	pointer-events: auto;
}

/* The base rule pushes the link one link-width past the word it follows. The
   manifesto's last word already reaches the grid's edge, so that push landed
   the link beyond the viewport at every width; here it sits straight after
   the word, in flow. */
.section-title--manifesto .section-title__link {
	inset-inline-end: 0;
	transform: none;
}

.section-title__link a {
	font-family: var(--font-sans);
	font-size: var(--font-sd);
	line-height: 1em;
	letter-spacing: 0;
	text-transform: none;
}

/* --- Prose ---------------------------------------------------------------
   Running copy is set in the mono face across the site; the sans is display
   type. These selectors each carried a size and a colour but no family, so
   they inherited the sans from the body and read as a different voice from
   the tables and labels around them. */
.projects-intro__text p,
.research-intro__text p,
.project-item .description,
.page--about .services ul li .inner p,
.publication-hero__abstract,
.publication-content p,
.publication-content li {
	font-family: var(--font-mono);
	font-variation-settings: "wght" var(--wght-light);
	line-height: 1.45;
}

/* --- Subtitle: sticky, difference-blended ------------------------------- */

.subtitle {
	position: sticky;
	bottom: 0;
	inset-inline-start: 0;
	width: 100%;
	padding: var(--pad);
	mix-blend-mode: difference;
	z-index: 100;
	pointer-events: none;
}

.subtitle h2 {
	display: flex;
	justify-content: space-between;
	width: 100%;
	margin: 0;
	font-family: var(--font-mono);
	font-size: var(--font-sd);
	font-variation-settings: "wght" var(--wght-medium);
	text-transform: uppercase;
	color: var(--ash);
}

/* --- Home: welcome / reel ----------------------------------------------- */

.page--home .welcome { width: 100%; }

.page--home .welcome .reel {
	/* Both are written by the hero effect on scroll; declared here so the
	   transforms below stay valid before the first scroll event. */
	--linear-progress: 0;
	--bezier-progress: 0;

	position: relative;
	height: 150vh;
}

.page--home .welcome .reel .footage {
	position: absolute;
	top: 0;
	inset-inline-start: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	height: 100dvh;
	z-index: 3;
	transform-origin: center center;
	transform: translate3d( 0, calc( -50vh * var(--bezier-progress, 0) ), 0 );
	will-change: transform;
}

.page--home .welcome .reel .footage .inner {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	width: calc( 100% - var(--pad) * 2 );
	height: calc( 100dvh - var(--nav-height) * 2 );
	border-radius: var(--border-radius);
	overflow: hidden;
	z-index: 10;
	will-change: transform;
}

.page--home .welcome .reel .footage .inner .video {
	width: 100%;
	height: 100%;
	border-radius: var(--border-radius);
	overflow: hidden;
}

.page--home .welcome .reel .footage video {
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center center;
}

.page--home .welcome .reel .text {
	position: relative;
	top: 80vh;
	inset-inline-start: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	width: 100%;
	height: 50vh;
	padding: var(--pad);
	z-index: 2;
	pointer-events: none;
	transform: translate3d(0, calc(10vh * var(--linear-progress)), 0);
}

.page--home .welcome .reel .text .printer { width: 100%; }
.page--home .welcome .reel .text .printer svg { width: 100%; height: auto; }
.page--home .welcome .reel .text .printer svg .line { fill: var(--foreground-dim); }

/* --- Home: experiments grid --------------------------------------------- */

.page--home .welcome .experiments {
	position: relative;
	padding: var(--pad);
}

.page--home .welcome .experiments__reel {
	position: relative;

	/* The grid must be at least as tall as the three-row title it carries,
	   so the words never spill past it onto the copy below. */
	min-height: calc( 3 * 10vw * 1.06 + 2 * var(--gap) + 12dvh );
}

/* Short travel, as the band-bounded titles use: the default parallax rests
   thirty viewport-heights below the grid at load, which is the copy and the
   papers table. */
.page--home .welcome .experiments__reel > .section-title { --travel-clamped: 10dvh; --travel: 6dvh; }

.page--home .welcome .experiments .list {
	position: relative;
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	grid-auto-flow: row;
	gap: var(--gap);

	/* The clips rise into their slots from below; whatever is still below
	   its slot is hidden at the grid's edge, so it never crosses the copy or
	   the papers table underneath. `clip` rather than `hidden`, which would
	   make this a scroll container. */
	overflow: clip;
	padding-bottom: 1px;
}

.page--home .welcome .experiments .list .experiment {
	--experiment-progress: 0;

	grid-column: span 3;
	line-height: 0;
}

.page--home .welcome .experiments .list .experiment:nth-child(4n + 1) { grid-column: 1 / span 3; }
.page--home .welcome .experiments .list .experiment:nth-child(4n + 2) { grid-column: 7 / span 3; }
.page--home .welcome .experiments .list .experiment:nth-child(4n + 3) { grid-column: 4 / span 3; }
.page--home .welcome .experiments .list .experiment:nth-child(4n)     { grid-column: 10 / span 3; }

.page--home .welcome .experiments .list .experiment .video {
	width: 100%;
	height: auto;
	/* A short rise, clipped at the grid's edge, rather than two viewports of
	   travel that ran over the section below. */
	transform: translate3d(0, calc(-50vh * var(--experiment-progress) + 25vh), 0);
	will-change: transform;
}

.page--home .welcome .experiments .list .experiment .video video {
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: center center;
	border-radius: var(--border-radius);
}

@media only screen and (max-width: 768px) {
	.page--home .welcome .experiments .list .experiment:nth-child(n) {
		grid-column: span 6;
		grid-row: auto;
	}
}

/* --- Inline footer link row --------------------------------------------- */

.page--home .welcome .footer { padding: 0 var(--pad); z-index: 5; position: relative; }

.footer ul {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	grid-auto-flow: row;
	gap: var(--gap);
	font-family: var(--font-mono);
	font-size: var(--font-md);
	font-variation-settings: "wght" var(--wght-regular);
	text-transform: uppercase;
	line-height: 1;
}

.footer ul li { grid-column: span 2; }

@media only screen and (max-width: 768px) {
	.footer ul li { grid-column: span 4; }
}

/* The profile icons share one cell of the row: four marks, no labels, drawn
   in the mono face's scanlines, one em tall and in the text colour. */
/* The cell is one line of the row's text tall, whatever its links measure, so
   the marks centre on that line at every size: in the grid on a desk and in
   the wrapped row on a phone. */
.footer .footer__social { display: flex; align-items: center; gap: 0.25em; height: 1em; }
/* A box a quarter larger than the mark keeps each target at 24px or more,
   even at the phone's smaller type; it overhangs the line evenly. */
.footer__social-link {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 1.25em;
	min-height: 1.25em;
	line-height: 0;

	/* The mono face's capitals sit 0.10–0.80em down its 1em line (ascent
	   0.80, cap height 0.70), so they centre at 0.446em while the mark
	   centres at 0.5em. Lift it by the difference. */
	top: -0.054em;
}
.footer__icon { display: block; width: 1em; height: 1em; fill: currentColor; }

/* A link can carry a shorter label for small screens; only one of the two
   shows, so screen readers hear only the one on screen. */
.footer__label--narrow { display: none; }

/* --- Manifesto ---------------------------------------------------------- */

.manifesto {
	position: relative;
	background: var(--background);
	color: var(--foreground);
	padding-top: var(--title-band);
}

.manifesto .texts {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	grid-auto-flow: row;
	gap: var(--gap);
	padding: 10vh var(--pad);
	position: relative;
	z-index: 2;
}

.manifesto .texts > div { grid-column: span 3; }

.manifesto .texts p,
.manifesto .texts li {
	max-width: 60ch;
	font-family: var(--font-mono);
	font-size: var(--font-sd);
	font-variation-settings: "wght" var(--wght-light);
	line-height: 1.45;
	color: var(--foreground-dim);
}

/* The about page fills this section with the_content(), which emits flat
   h2 / p / ul rather than the wrappers the home page builds — so nothing
   matched `.texts > div` and every element auto-placed into a single 1/12
   track, about 150px, which is what shredded the copy into one-word lines.
   Label on the left, prose in a readable measure beside it. Sparse
   auto-placement is what keeps a heading from sliding back up alongside the
   previous section's last paragraph: the cursor never moves backwards. */
.manifesto .texts.entry-content { row-gap: var(--gap); align-items: start; }

/* Six columns is the measure here, so the 60ch cap the home page's narrow
   wrappers need is dropped — left on, it applies to the list items but not to
   the paragraphs (which are grid children and reset above), and the lists come
   out half the width of the prose beside them. */
.manifesto .texts.entry-content > * { grid-column: 4 / span 6; margin: 0; max-width: none; }
.manifesto .texts.entry-content > h2 { grid-column: 1 / span 3; margin: 0; }
.manifesto .texts.entry-content li { max-width: none; }

@media only screen and (max-width: 768px) {
	.manifesto .texts > div,
	.manifesto .texts.entry-content > *,
	.manifesto .texts.entry-content > h2 { grid-column: 1 / -1; }
}

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

.projects-intro {
	position: relative;
	background: var(--background);
	color: var(--foreground);
	padding-top: var(--title-band);
}

/* These sections reserve a band at the top for the oversized title and put the
   content below it. `.section-title` is `height: 100%` and centres itself, so
   left alone it centres over the whole section — content included — and
   collides with it once that block grows (stats, buttons, more table rows).
   Bounding it to the reserved band makes the clearance independent of how tall
   the content becomes.

   The band drives both the section's padding and the title's height from one
   value, so the two can never drift apart. */
.projects-intro,
.research-intro,
.awards,
.clients,
.page--about .team,
.page--about .services,
.other-projects,
.other-publications,
.manifesto,
.services-teaser {
	/* Rows the title wraps to, and the font it is set in — see the
	   `.section-title h1, h2` font-size and its 584px override. */
	--title-rows: 2;
	--title-font: 10vw;

	/* The title is sized in vw but the band was in vh, so a short, wide
	   viewport grew the title past a band that had shrunk. Deriving the band
	   from the title's own metrics keeps them in step at any aspect ratio.
	   The 1.06 is the measured row height against the font size: line-height is
	   1em but the glyph box overruns it. The +20vh covers the parallax, which
	   rests at +half-travel (8vh here) while --progress is 0. */
	--title-band: max(
		60vh,
		calc( var(--title-rows) * var(--title-font) * 1.06 + ( var(--title-rows) - 1 ) * var(--gap) + var(--title-extra, 0px) + 20vh )
	);
}

/* Row counts match the `grid-row` placements each modifier declares below.
   `.projects-intro` is two different sections: the home page's "Selected Work"
   wraps to two rows, the archive's one-word "Projects" to one. */
.awards { --title-rows: 3; }
.clients { --title-rows: 3; }
.page--projects .projects-intro { --title-rows: 1; }
.manifesto {
	--title-rows: 4;

	/* Headroom over the row-height factor above; the rows no longer carry the
	   hand-tuned margin offsets they were sized around. */
	--title-extra: calc( var(--title-font) * 0.6 );
}

@media only screen and (max-width: 584px) {
	.projects-intro,
	.research-intro,
	.awards,
	.clients,
	.page--about .team,
	.page--about .services,
	.other-projects,
	.other-publications,
	.manifesto { --title-font: 15vw; }
}

/* Kept in step with the face above, or the reserved band no longer matches the
   title it is reserving for. */
@media only screen and (max-width: 380px) {
	.projects-intro,
	.research-intro,
	.awards,
	.clients,
	.page--about .team,
	.page--about .services,
	.other-projects,
	.other-publications,
	.manifesto { --title-font: 13vw; }
}

.projects-intro > .section-title,
.research-intro > .section-title,
.awards > .section-title,
.clients > .section-title,
.page--about .team > .section-title,
.page--about .services > .section-title,
.other-projects > .section-title,
.other-publications > .section-title,
.services-teaser > .section-title,
.manifesto > .section-title {
	height: var(--title-band);

	/* The parallax rests at +half-travel while --progress is 0, which is
	   exactly the state these sections load in, so the per-section defaults
	   would drop the title out of its band and back onto the content. Short
	   travel keeps the motion without the resting overhang. */
	--travel-clamped: 10dvh;
	--travel: 6dvh;
}

.projects-intro__text {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	gap: var(--gap);
	padding: 0 var(--pad) 10vh;
	position: relative;
	z-index: 2;
}

.projects-intro__text p { grid-column: 7 / span 6; font-size: var(--font-p); }

@media only screen and (max-width: 768px) {
	.projects-intro__text p { grid-column: 1 / -1; }
}

/* --- Projects list: asymmetric column placement ------------------------- */

.projects { position: relative; background: var(--background); color: var(--foreground); }

.projects .projects-list {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	grid-auto-flow: row;
	gap: var(--gap);
	padding: 10vh var(--pad);
	position: relative;
	z-index: 2;
}

.projects .projects-list .project-item { display: flex; flex-direction: column; }

.projects .projects-list .project-item:nth-child(5n + 1) { grid-column: 1 / span 2; }
.projects .projects-list .project-item:nth-child(5n + 2) { grid-column: 7 / span 4; }
.projects .projects-list .project-item:nth-child(5n + 3) { grid-column: 1 / span 2; }
.projects .projects-list .project-item:nth-child(5n + 4) { grid-column: 5 / span 4; }
.projects .projects-list .project-item:nth-child(5n + 5) { grid-column: 9 / span 3; }

@media only screen and (max-width: 768px) {
	.projects .projects-list .project-item,
	.projects .projects-list .project-item:nth-child(5n + 1),
	.projects .projects-list .project-item:nth-child(5n + 2),
	.projects .projects-list .project-item:nth-child(5n + 3),
	.projects .projects-list .project-item:nth-child(5n + 4),
	.projects .projects-list .project-item:nth-child(5n + 5) { grid-column: 1 / -1; }
}

.project-item .inner {
	position: relative;
	display: block;
	transform: translate3d(0, calc(20vh * var(--project-progress)), 0);
	will-change: transform;
}

.project-item .num {
	font-family: var(--font-mono);
	font-size: var(--font-sm);
	font-variation-settings: "wght" var(--wght-regular);
	color: var(--foreground-dim);
	margin: 0 0 var(--gap);
}

.project-item .title {
	font-family: var(--font-mono);
	font-size: var(--font-md);
	font-variation-settings: "wght" var(--wght-regular);
	text-transform: uppercase;
	margin-bottom: var(--gap);
}

.project-item .thumbnail { position: relative; line-height: 0; }
.project-item .thumbnail .quadtree { position: relative; overflow: hidden; }
.project-item .thumbnail img { width: 100%; height: auto; border-radius: var(--border-radius); }

.project-item .description {
	margin-top: var(--gap);
	font-size: var(--font-sm);
	color: var(--foreground-dim);
}

.project-item .description p { margin: 0; }

/* Hover card, difference-blended over the thumbnail. */
.card {
	position: absolute;
	top: 0;
	inset-inline-start: 0;
	width: 100%;
	padding: var(--gap);
	z-index: 3;
	pointer-events: none;
	mix-blend-mode: difference;
	opacity: 0;
	transition: opacity var(--duration-base) var(--ease-out);
}

.project-item:hover .card,
.project-item:focus-within .card { opacity: 1; }

.card .card__label {
	display: block;
	width: 100%;
	text-align: justify;
	text-align-last: justify;
	font-family: var(--font-sans);
	font-size: var(--font-lg);
	line-height: 1;
	color: var(--foreground);
}

/* --- Awards ------------------------------------------------------------- */

.awards { position: relative; padding-top: var(--title-band); }

.awards .awards__list {
	position: relative;
	z-index: 2;
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	grid-auto-flow: row;
	gap: var(--gap);
	row-gap: 0;
	padding: 0 var(--pad) 10vh;
	font-family: var(--font-mono);
	font-size: var(--font-md);
	font-variation-settings: "wght" var(--wght-regular);
	color: var(--foreground-dim);
	text-transform: uppercase;
	line-height: 1.2;
}

.awards .awards__list--header { padding-bottom: 0; color: var(--foreground-dim); }

.awards .awards__group { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; }

.awards .awards__item {
	position: relative;
	grid-column: 1 / -1;
	display: grid;
	grid-template-columns: subgrid;
	align-items: baseline;
	padding-block: calc(var(--gap) / 2);
}

.awards .awards__project { grid-column: span 3; }
.awards .awards__name { grid-column: span 3; }
.awards .awards__description { grid-column: span 4; }
.awards .awards__year { grid-column: span 2; text-align: end; }

@media only screen and (max-width: 768px) {
	.awards .awards__project,
	.awards .awards__name { grid-column: span 6; }
	.awards .awards__description { grid-column: span 8; }
	.awards .awards__year { grid-column: span 4; }
}

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

.site-footer {
	position: relative;
	padding: 10vh 0 var(--pad);
	background: var(--background);
	color: var(--foreground);
}

/* The game court sits on the wordmark: its ground line is the last cell row,
   so the ball bounces along the top of the name. */
.site-footer .footer-game {
	--game-ink: var(--foreground-dimmed);
	--game-hot: var(--foreground-dim);
	--game-accent: var(--accent);

	position: relative;
	width: 100%;
	height: clamp( 120px, 18vw, 240px );
	padding: 0 var(--pad);
	margin-bottom: calc( var(--gap) * -1 );
}

.site-footer .footer-game canvas {
	position: absolute;
	inset: 0 var(--pad);
	width: calc( 100% - var(--pad) * 2 );
	height: 100%;
}

/* The wordmark keeps the resting weight the masked-band version had — the ink
   is the same --foreground-dimmed — and the head flares to --foreground-dim as
   it strikes, so the pass reads without changing where the mark settles. The
   aspect ratio gives the canvas a height to measure before it has drawn. */
.site-footer .footer-dots {
	--dots-ink: var(--foreground-dimmed);
	--dots-hot: var(--foreground-dim);

	position: relative;
	width: 100%;
	aspect-ratio: 5 / 1;
	padding: 0 var(--pad);
	font-family: var(--font-sans);
}

.site-footer .footer-dots canvas {
	position: absolute;
	inset: 0 var(--pad);
	width: calc( 100% - var(--pad) * 2 );
	height: 100%;
}

/* --- Interior pages ----------------------------------------------------- */

.page--static .page-body,
.page--project .project-body {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	gap: var(--gap);
	padding: 0 var(--pad) 10vh;
}

.page--static .page-body > *,
.page--project .project-content { grid-column: 5 / span 6; }
.page--project .project-facts { grid-column: 1 / span 3; }

.page-hero,
.project-hero {
	padding: 30vh var(--pad) 10vh;
}

.page-hero__title,
.project-hero__title {
	font-family: var(--font-sans);
	font-size: 10vw;
	line-height: 1;
	color: var(--foreground);
}

.page-hero__excerpt,
.project-hero__excerpt {
	margin-top: var(--pad);
	max-width: 60ch;
	font-size: var(--font-p);
	color: var(--foreground-dim);
}

.u-mono-label {
	font-family: var(--font-mono);
	font-size: var(--font-sm);
	font-variation-settings: "wght" var(--wght-regular);
	text-transform: uppercase;
	color: var(--foreground-dim);
}

@media only screen and (max-width: 768px) {
	.page--static .page-body > *,
	.page--project .project-content,
	.page--project .project-facts { grid-column: 1 / -1; }
}

/* Body copy is set in the mono face, as the group system sets it. */
.entry-content p,
.entry-content li {
	font-family: var(--font-mono);
	font-variation-settings: "wght" var(--wght-light);
	line-height: 1.45;
	color: var(--foreground-dim);
}
.entry-content h2, .entry-content h3 {
	margin: calc(var(--pad) * 2) 0 var(--gap);
	font-family: var(--font-mono);
	font-size: var(--font-sd);
	font-variation-settings: "wght" var(--wght-medium);
	text-transform: uppercase;
	color: var(--foreground);
}
.entry-content ul { padding-inline-start: 1.2em; list-style: square; }
.entry-content a { color: var(--accent); text-decoration: underline; text-underline-offset: 0.2em; }

.project-facts__row { padding: var(--gap) 0; border-top: var(--border-width) solid var(--foreground-dimmed); }
.project-facts__row dd { margin: 0.2em 0 0; }
.project-nav { display: flex; justify-content: space-between; gap: var(--pad); padding: var(--pad); }

/* --- Generative tile ink ------------------------------------------------ */

.tile__canvas { --tile-ink: var(--foreground-dimmed); }



/* --- Section title compositions ----------------------------------------- */
/* Word placement per section, matching the group system. Each title's word
   count is fixed by these rules, so changing a heading's length means
   revisiting its grid placement. */

/* Experiments — three words. */
/* Same correction as the manifesto: the group system puts word two on the row
   above word one, which reads as "Machine Applied Systems". Staggered down and
   across instead, five columns each — the measured width of all three. */
.section-title--experiments h2 > span:nth-child(1) { grid-row: 1; grid-column: 1 / span 6; }
.section-title--experiments h2 > span:nth-child(2) { grid-row: 2; grid-column: 5 / span 3; }
.section-title--experiments h2 > span:nth-child(3) { grid-row: 3; grid-column: 3 / span 5; }

/* Manifesto — eight words, on a light ground. */
.section-title--manifesto h2 { color: var(--black); }
/* Words are staggered across the grid, but they still have to be read: where a
   row holds two of them the earlier word sits to the left, or the sentence
   comes apart. The group system's own placements put word three to the right of
   word four, which read as "AI Systems WordPress And Platforms" here. Spans are
   measured against the words themselves — "WordPress" needs seven of the twelve
   columns at 10vw.
   Order is Research / Custom / AI / And / WordPress / Built / To / Last. */
.section-title--manifesto h2 > span:nth-child(1) { grid-row: 1; grid-column: 1 / span 6; }
.section-title--manifesto h2 > span:nth-child(2) { grid-row: 2; grid-column: 2 / span 5; }
.section-title--manifesto h2 > span:nth-child(3) { grid-row: 2; grid-column: 8 / span 2; }
.section-title--manifesto h2 > span:nth-child(4) { grid-row: 3; grid-column: 1 / span 3; }
.section-title--manifesto h2 > span:nth-child(5) { grid-row: 3; grid-column: 5 / span 7; }
.section-title--manifesto h2 > span:nth-child(6) { grid-row: 4; grid-column: 1 / span 4; }
.section-title--manifesto h2 > span:nth-child(7) { grid-row: 4; grid-column: 6 / span 2; }
.section-title--manifesto h2 > span:nth-child(8) { grid-row: 4; grid-column: 9 / span 4; }

.section-title--manifesto .section-title__link { color: var(--black); }

/* The manifesto placements above are sized for the home page's short words.
   The about page's title carries "Related" and "Disciplines", which need five
   and seven of the twelve columns at 10vw — dropped into two- and three-column
   slots they overrun their tracks and print straight through each other. A
   grid item does not widen to fit its text, so the boxes stay put and only the
   glyphs collide; the spans below are measured against the actual words.
   Order is A / Small / Studio / Built / Around / Two / Related / Disciplines.

   Bounded to the wide layout on purpose. Below 768px every one of these words
   wants the full twelve columns and the narrow rules take over — and since a
   media query adds no specificity, leaving these unbounded would let them
   outrank the narrow ones and reinstate the collision. */
@media only screen and (min-width: 769px) {
	/* Five rows here against the home page's four; the band has to know. */
	.page--about .manifesto { --title-rows: 5; }

	.page--about .section-title--manifesto h2 > span:nth-child(1) { grid-row: 1; grid-column: 1 / span 1; margin-top: 0; }
	.page--about .section-title--manifesto h2 > span:nth-child(2) { grid-row: 2; grid-column: 3 / span 4; margin-top: 0; }
	.page--about .section-title--manifesto h2 > span:nth-child(3) { grid-row: 3; grid-column: 1 / span 4; margin-top: 0; }
	.page--about .section-title--manifesto h2 > span:nth-child(4) { grid-row: 3; grid-column: 6 / span 3; margin-top: 0; }
	.page--about .section-title--manifesto h2 > span:nth-child(5) { grid-row: 4; grid-column: 2 / span 5; margin-top: 0; }
	.page--about .section-title--manifesto h2 > span:nth-child(6) { grid-row: 4; grid-column: 8 / span 4; margin-top: 0; }
	.page--about .section-title--manifesto h2 > span:nth-child(7) { grid-row: 5; grid-column: 1 / span 5; margin-top: 0; }
	.page--about .section-title--manifesto h2 > span:nth-child(8) { grid-row: 5; grid-column: 6 / span 7; margin-top: 0; }
}

/* Projects — two words, shorter travel. */
.section-title--projects { --travel-clamped: 30dvh; --travel: 20dvh; }
.section-title--projects h2 > span:nth-child(1) { grid-row: 1; grid-column: 1 / 6; }
.section-title--projects h2 > span:nth-child(2) { grid-row: 2; grid-column: 4 / -1; }

/* Awards — three words, shorter travel. */
.section-title--awards { --travel-clamped: 30dvh; --travel: 20dvh; }
.section-title--awards h2 > span:nth-child(1) { grid-row: 1; grid-column: 1 / 6; }
.section-title--awards h2 > span:nth-child(2) { grid-row: 2; grid-column: 5 / -1; }
.section-title--awards h2 > span:nth-child(3) { grid-row: 3; grid-column: 1 / -1; }


/* Other projects — same travel budget as the home projects title. */
.section-title--other-projects { --travel-clamped: 30dvh; --travel: 20dvh; }
.section-title--other-projects h2 > span:nth-child(1) { grid-row: 1; grid-column: 1 / 6; }
.section-title--other-projects h2 > span:nth-child(2) { grid-row: 2; grid-column: 4 / -1; }

.other-projects { position: relative; margin-top: calc( var(--pad) * 6 ); padding-top: var(--title-band); }
.other-projects .projects-list { position: relative; z-index: 2; }

/* --- About: fields of work ---------------------------------------------- */

/* Each item travels a hundred viewport heights through its own trigger, which
   means at the start of the pass it is drawn a full screen above where it
   belongs — on top of the section before it. The group system carries the same
   travel; clipping keeps it inside this section rather than printing over the
   manifesto copy. `clip` rather than `hidden`, which would make this a scroll
   container. */
.page--about .services {
	position: relative;
	overflow: clip;
	padding-top: var(--title-band);
	padding-bottom: 10vh;
}

.page--about .services ul {
	position: relative;
	z-index: 2;
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	grid-auto-flow: row;
	gap: calc( var(--pad) * 3 ) var(--gap);
	padding: 10vh var(--pad) 0;
	list-style: none;
}

.page--about .services ul li {
	--service-progress: 0;

	grid-column: span 2;
	margin-bottom: var(--pad);
}

.page--about .services ul li .inner {
	/* A short travel. The progress runs 1 (below the viewport) to 0 (scrolled
	   past), so an item waits a little *below* its slot and settles as it
	   arrives — never up into the row above it. Kept small enough that a
	   row scrolled past and a row arriving cannot meet. */
	transform: translateY( calc( 8vh * var(--service-progress) - 4vh ) );
	will-change: transform;
}

.page--about .services ul li .inner h2 {
	font-family: var(--font-mono);
	font-size: var(--font-sd);
	font-variation-settings: "wght" var(--wght-regular);
	text-transform: uppercase;
	margin-bottom: var(--gap);
}

.page--about .services ul li .inner p {
	margin: 0;
	font-size: var(--font-sm);
	color: var(--foreground-dim);
}

@media only screen and (max-width: 1024px) { .page--about .services ul li { grid-column: span 4; } }
@media only screen and (max-width: 584px)  { .page--about .services ul li { grid-column: span 6; } }

/* --- About: team --------------------------------------------------------- */

/* The section carries theme--light, which swaps the ink to dark, but only the
   ground painted here makes it light. Without it the dark ink sat on the
   body's black and the names vanished. */
.page--about .team {
	position: relative;
	width: 100%;
	background: var(--background);
	color: var(--foreground);
	padding-top: var(--title-band);
	padding-bottom: 10vh;
}

/* The group system overlays these names on a 100dvh GLB scene, so its title
   sitting behind them is the point. No 3D asset exists here, so the names
   would just collide with the title — they get the band treatment instead.
   The z-index matches the group system, where content carries the index and
   the title is left at auto. */
.page--about .team .team-members { position: relative; z-index: 2; }

.page--about .team .team-members .members {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	grid-auto-flow: row;
	gap: var(--gap);
	row-gap: 0;
	width: 100%;
	padding: 20vh var(--pad);
	font-family: var(--font-mono);
	font-size: var(--font-md);
	font-variation-settings: "wght" var(--wght-regular);
	text-transform: uppercase;
	list-style: none;
}

.page--about .team .team-members .member {
	grid-column: 1 / -1;
	display: grid;
	grid-template-columns: subgrid;
	align-items: baseline;
	padding-block: calc( var(--gap) / 2 );
}

.page--about .team .member-name     { grid-column: span 3; }
.page--about .team .member-title    { grid-column: span 3; }
.page--about .team .member-role     { grid-column: span 4; }
.page--about .team .member-location { grid-column: span 2; text-align: end; }

.page--about .team .member > * {
	--hover-fill-background: var(--grey);

	position: relative;
	isolation: isolate;
	display: inline-block;
}

.page--about .team .member > *::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	border-radius: var(--border-radius-text);
	background: var(--hover-fill-background);
	transform: scaleX(0);
	transform-origin: right;
	transition: transform 0.3s var(--ease-out-expo);
}

.page--about .team .member:hover > *::after { transform: scaleX(1); transform-origin: left; }
.page--about .team .member:hover > * { color: var(--accent); }

@media only screen and (max-width: 768px) {
	.page--about .team .member-name,
	.page--about .team .member-title { grid-column: span 6; }
	.page--about .team .member-role { grid-column: span 8; }
	.page--about .team .member-location { grid-column: span 4; }
}

/* Title compositions for the about sections. */
.section-title--services h2 > span:nth-child(1) { grid-row: 1; grid-column: 1 / 6; }
/* `1 / 6` ends at column 5, so starting this word there made the two share a
   column and collide once the first one filled its span. */
.section-title--services h2 > span:nth-child(2) { grid-row: 1; grid-column: 6 / -1; margin-top: -0.5em; }
.section-title--services h2 > span:nth-child(3) { grid-row: 2; grid-column: 1 / -1; }

.section-title--team h2 > span:nth-child(1) { grid-row: 1; grid-column: 1 / -1; }
.section-title--team h2 > span:nth-child(2) { grid-row: 2; grid-column: 4 / -1; }

/* --- Contact ------------------------------------------------------------- */

.page--contact { position: relative; }

/* Probes are never seen; the canvas reads its font metrics from them, so they
   must carry real, computed typography rather than be display:none. */
.contact-canvas__obs-probe,
.contact-canvas__para-probe {
	position: absolute;
	width: 0;
	height: 0;
	overflow: hidden;
	opacity: 0;
	pointer-events: none;
}

.contact-canvas__obs-probe {
	font-family: var(--font-sans);

	/* 10vw is the base of the ladder; the 1024px and 768px rules step it to
	   12vw and 15vw. Capped against the height as well: on a wide, short
	   laptop screen the words would otherwise fill the whole text band and
	   leave the paragraphs nowhere to go. */
	font-size: min( 10vw, 16vh );
	font-weight: 400;

	/* --foreground-dimmed (#1a1a1a) on --background (#0f0f0f) is an 11-value
	   difference — the obstacle words were drawn but invisible. They are the
	   address the copy piles against, so they have to read. */
	color: var(--foreground-dim);
}

.contact-canvas__para-probe {
	font-family: var(--font-mono);
	font-size: var(--font-sd);
	font-weight: 400;
	color: var(--foreground);
}

.contact-canvas {
	position: fixed;
	top: 0;
	inset-inline-start: 0;
	width: 100%;
	height: 100dvh;
	z-index: 1;
	cursor: grab;
	touch-action: none;
}

.contact-canvas.is-grabbing { cursor: grabbing; }
.contact-canvas.is-over-obstacle { cursor: pointer; }

/* The canvas carries the content visually; this is its accessible twin. */
.contact-fallback {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* Shown when scripting is off, and when the canvas stands aside for a script
   it cannot render a character at a time. */
.no-js .contact-fallback,
.contact-fallback--plain {
	position: static;
	width: auto;
	height: auto;
	clip-path: none;
	white-space: normal;
	padding: 30vh var(--pad) 10vh;
	max-width: 68ch;
}

/* The link row is pinned to the viewport on contact because the canvas fills
   it and nothing scrolls. When the canvas stands aside the page scrolls like
   any other, so the row belongs at the end of it — pinned, it would sit over
   the copy at every scroll position, which no amount of padding fixes. */
.page--contact:has( .contact-fallback--plain ) + .site-footer,
body.page-template-template-contact:has( .contact-fallback--plain ) .site-footer {
	position: static;
	padding: var(--pad);
	background: var(--background);
}

.no-js .contact-canvas { display: none; }
.contact-fallback__list,
.entry-content ul.contact-fallback__list { list-style: none; padding: 0; margin-inline-start: 0; }
.no-js .contact-fallback__list li,
.contact-fallback--plain .contact-fallback__list li { margin-bottom: 1em; }

/* The big word the canvas draws, for the plain page. */
.contact-fallback p.contact-fallback__hello { display: none; margin: 0 0 var(--pad); font-family: var(--font-sans); font-size: min( 22vw, 7rem ); line-height: 0.95; letter-spacing: -0.03em; }
.contact-fallback__hello a { color: var(--foreground); text-decoration: none; border: 0; }
.contact-fallback__hello a:hover { color: var(--accent); }
.contact-fallback__hello a > span:not(.word-unit) { display: block; font-size: 0.42em; color: var(--foreground-dim); margin-top: 0.15em; } /* the word effect wraps each word in a span too; only the domain line shrinks */
.no-js .contact-fallback p.contact-fallback__hello,
.contact-fallback--plain.entry-content p.contact-fallback__hello { display: block; }

/* Phones get the plain page: one column that scrolls, the copy at a size that
   reads, the footer in the flow. Dragging single letters is no use on a
   thumb, and the canvas never had the room. */
@media only screen and (max-width: 768px) {
	.contact-canvas { display: none !important; }
	.contact-fallback,
	.no-js .contact-fallback,
	.contact-fallback--plain { position: static; width: auto; height: auto; clip-path: none; white-space: normal; padding: calc( var(--nav-height) + var(--pad) * 2 ) var(--pad) var(--pad); max-width: none; }
	.contact-fallback p.contact-fallback__hello { display: block; }
	.contact-fallback > p,
	.contact-fallback > ul > li { font-size: var(--font-p); line-height: 1.35; }
	.contact-fallback h2 { margin: calc( var(--pad) * 1.5 ) 0 var(--gap); font-family: var(--font-mono); font-size: var(--font-sm); text-transform: uppercase; color: var(--foreground-dim); }
	.contact-fallback__list li { margin-bottom: 0.8em; }
	.contact-fallback__actions { margin-top: var(--pad); }
	.page--contact + .site-footer,
	body.page-template-template-contact .site-footer { position: static; padding: var(--pad); background: var(--background); }
	.page--contact .contact-strip { position: static; padding: var(--pad) var(--pad) calc( var(--pad) * 2 ); pointer-events: auto; grid-template-columns: 1fr; }
	.page--contact .contact-strip .scene--contact { max-width: none; }
}

/* On contact the canvas fills the viewport, so the link row pins to the
   bottom edge instead of sitting after it in flow. */
.page--contact + .site-footer,
body.page-template-template-contact .site-footer {
	position: fixed;
	bottom: 0;
	inset-inline-start: 0;
	width: 100%;
	padding: 0 var(--pad) var(--pad);
	background: transparent;
	z-index: 5;
}

/* Matched on the article as well as the body class: the body class is synced
   by the navigation layer, but the sibling selector holds even if that sync
   ever fails, and the wordmark overlapping the canvas is very visible. */
.page--contact + .site-footer .footer-dots,
.page--contact + .site-footer .site-footer__widgets,
body.page-template-template-contact .site-footer .footer-dots,
body.page-template-template-contact .site-footer__widgets { display: none; }

/* --- Touch targets --------------------------------------------------------
   The header and footer links are set in small mono type, which leaves a hit
   area around 13px tall — well under the 24px minimum. The padding grows the
   target and the negative margin takes the space back, so nothing moves.
   ========================================================================== */

@media only screen and (max-width: 768px) {
	.header .nav a,
	.header .nav-toggle,
	.lang-switcher a,
	.section-title__link a,
	.services-teaser__title a,
	.research-intro__text p > a.u-mono-label,
	.site-footer a {
		padding-block: 8px;
		margin-block: -8px;
	}

	/* The mono face is narrow — four characters come to 23px — so these two
	   need the width as well as the height. */
	.lang-switcher a,
	.header .nav-toggle {
		padding-inline: 9px;
		margin-inline: -9px;
	}

	/* The grown targets must not overlap each other, or a tap between the
	   two languages lands on whichever is later in the DOM. */
	.lang-switcher { gap: calc( 0.5em + 18px ); }
}

/* ==========================================================================
   Responsive.
   Breakpoint behaviour taken from the group system, including the rules that
   drop content rather than reflow it: the manifesto sheds columns as space
   runs out, and the engagements table sheds its outcome column.
   ========================================================================== */

@media only screen and (max-width: 1512px) {
	.projects-intro__text > * { grid-column: span 4; }

	.projects .projects-list .project-item:nth-child(5n + 1) { grid-column: 1 / span 3; }
	.projects .projects-list .project-item:nth-child(5n + 2) { grid-column: 7 / span 4; }
	.projects .projects-list .project-item:nth-child(5n + 3) { grid-column: 1 / span 3; }
	.projects .projects-list .project-item:nth-child(5n + 4) { grid-column: 4 / span 4; }
	.projects .projects-list .project-item:nth-child(5n + 5) { grid-column: 11 / span 2; }
}

@media only screen and (max-width: 1280px) {
	.manifesto .texts > div { grid-column: span 4; }
	.manifesto .texts > div:nth-child(4) { display: none; }
}

@media only screen and (max-width: 1024px) {
	.manifesto .texts > div { grid-column: span 6; }
	.manifesto .texts > div:nth-child(3),
	.manifesto .texts > div:nth-child(4) { display: none; }

	.page--about .services ul li .inner h2 { text-align: start; }
	.page--about .services ul li:nth-child(1) { grid-column: 5 / span 4; grid-row: 1; }
	.page--about .services ul li:nth-child(2) { grid-column: 1 / span 4; grid-row: 2; }
	.page--about .services ul li:nth-child(3) { grid-column: 9 / span 4; grid-row: 2; }
	.page--about .services ul li:nth-child(4) { grid-column: 5 / span 4; grid-row: 3; }
	.page--about .services ul li:nth-child(5) { grid-column: 1 / span 4; grid-row: 4; }
	.page--about .services ul li:nth-child(6) { grid-column: 9 / span 4; grid-row: 4; }
	.page--about .services ul li:nth-child(7) { grid-column: 5 / span 4; grid-row: 5; }

	.contact-canvas__obs-probe { font-size: min( 12vw, 16vh ); }
}

@media only screen and (max-width: 768px) {
	/* Nav collapses from the twelve-column grid to a simple row. */
	.header .nav {
		display: flex;
		justify-content: space-between;
		align-items: center;
	}

	.header .nav .logo { grid-column: unset; }
	.header .nav .time { display: none; }

	.header .nav ul li { grid-column: unset; }

	/* The bar carries three things at this width — wordmark, language, menu —
	   so it can afford the larger step; at --font-sm the switcher in particular
	   was too small to read or to aim at. */
	.header .nav { font-size: var(--font-sd); gap: var(--gap); }
	.lang-switcher { grid-column: unset; }

	/* Link row keeps only the address and the last entry. */
	.footer ul {
		display: flex;
		flex-wrap: wrap;
		justify-content: space-between;
	}

	.footer ul li { width: auto; grid-column: unset; }
	.footer ul li:not(.footer__item--keep) { display: none; }

	/* The fixed WhatsApp button sits in the bottom corner, so the footer keeps a
	   band of empty space under its link row: at the page end the button floats
	   over nothing instead of over Privacy or Terms. */
	.site-footer,
	.page--contact + .site-footer,
	body.page-template-template-contact .site-footer { padding-bottom: calc( var(--pad) * 2 + 3.6rem ); }

	/* The address gives way to "Contact" on a phone. */
	.footer__label--wide { display: none; }
	.footer__label--narrow { display: inline; }

	/* No hover on touch, so the project card has no reason to exist. */
	.card { display: none; }

	/* The reel fills its frame at every width; `height: auto` here let the
	   video fall back to its own 16:9 box and letterbox inside the frame. */
	.page--home .welcome .reel .footage .inner .video { width: 100%; height: 100%; }

	.projects-intro__text > * { grid-column: 1 / -1; }
	.projects .projects-list .project-item:nth-child(n) { grid-column: 1 / -1; }
	.projects .projects-list .project-item .title { width: 100%; }

	.manifesto .texts { padding-top: 10vh; padding-bottom: 10vh; }
	.manifesto .texts > div { grid-column: 1 / -1; }
	.manifesto .texts > div:nth-child(2),
	.manifesto .texts > div:nth-child(3),
	.manifesto .texts > div:nth-child(4) { display: none; }

	.section-title__link a { font-size: var(--font-sd); }

	/* Twelve columns are barely wider than one word here, so the paired
	   placements above stop working for every one of these titles: a word set
	   two or three columns wide simply prints over its neighbour. One word per
	   row is the only arrangement that holds at this width. */
	.section-title--manifesto h2 > span:nth-child(1) { grid-row: 1; grid-column: 1 / -1; margin-top: 0; }
	.section-title--manifesto h2 > span:nth-child(2) { grid-row: 2; grid-column: 1 / -1; margin-top: 0; }
	.section-title--manifesto h2 > span:nth-child(3) { grid-row: 3; grid-column: 1 / -1; margin-top: 0; }
	.section-title--manifesto h2 > span:nth-child(4) { grid-row: 4; grid-column: 1 / -1; margin-top: 0; }
	.section-title--manifesto h2 > span:nth-child(5) { grid-row: 5; grid-column: 1 / -1; margin-top: 0; }
	.section-title--manifesto h2 > span:nth-child(6) { grid-row: 6; grid-column: 1 / -1; margin-top: 0; }
	.section-title--manifesto h2 > span:nth-child(7) { grid-row: 7; grid-column: 1 / -1; margin-top: 0; }
	.section-title--manifesto h2 > span:nth-child(8) { grid-row: 8; grid-column: 1 / -1; margin-top: 0; }
	.manifesto { --title-rows: 8; }

	.section-title--experiments h2 > span:nth-child(1) { grid-row: 1; grid-column: 1 / -1; }
	.section-title--experiments h2 > span:nth-child(2) { grid-row: 2; grid-column: 1 / -1; }
	.section-title--experiments h2 > span:nth-child(3) { grid-row: 3; grid-column: 1 / -1; }

	/* The link hangs off the right edge of the last word by design; at this
	   width that puts it past the viewport, where it is clipped and cannot be
	   tapped. Let it flow instead. */
	.section-title__link {
		position: static;
		transform: none;
		display: block;
	}
	.section-title--projects h2 > span:nth-child(2) { grid-column: 3 / -1; }
	.section-title--services h2 > span:nth-child(2) { grid-column: 7 / -1; }

	.page--about .services ul li:nth-child(n) { grid-column: 1 / -1; grid-row: auto; }

	/* Team sheds title and location, leaving name and role side by side. */
	.page--about .team .team-members .member { grid-template-columns: repeat(2, 1fr); gap: var(--gap); }
	.page--about .team .member-title,
	.page--about .team .member-location { display: none; }
	.page--about .team .member-name { grid-column: span 1; }
	.page--about .team .member-role { grid-column: span 1; text-align: end; }

	/* Engagements shed the outcome column and the header row. */
	.awards .awards__list { padding-top: 10vh; }
	.awards .awards__list--header { display: none; }
	.awards .awards__project { grid-column: span 3; }
	.awards .awards__name { grid-column: span 6; }
	.awards .awards__description { display: none; }
	.awards .awards__year { grid-column: span 3; }

	.contact-canvas__obs-probe { font-size: min( 15vw, 16vh ); }
}

@media only screen and (max-width: 584px) {
	.section-title--manifesto h2 > span:nth-child(2) { grid-column: 4 / -1; }
	/* "Systems" is seven characters at 15vw; from column seven it ran 17px
	   past the edge of a 375px screen. */
	.section-title--manifesto h2 > span:nth-child(3) { grid-column: 5 / -1; }
}

/* --- About: team scene --------------------------------------------------- */

.page--about .team .team-container {
	position: relative;
	width: 100%;
	padding: 0 var(--pad);
}

.page--about .team .team-scene {
	position: relative;
	width: 100%;
	height: 100dvh;
	border-radius: var(--border-radius);
	overflow: hidden;

	/* The accent stays as the ground the tile draws on, so the slot keeps its
	   colour instead of reading as an empty block. White ink because the tile
	   is resolved by the quadtree, which averages toward the ground — pure
	   accent is perceptually dark (luma ~29), so the ink has to be far from it
	   to survive that averaging. It is ruled in CSS too, so the slot still reads
	   as a surface rather than a blank panel when scripting is off; the tile
	   paints an opaque ground over that the moment it draws. */
	background:
		repeating-linear-gradient( to right, rgb( 255 255 255 / 0.5 ) 0 1px, transparent 1px 6.25% ),
		repeating-linear-gradient( to bottom, rgb( 255 255 255 / 0.5 ) 0 1px, transparent 1px 12.5% ),
		var(--accent);

	--tile-bg: var(--accent);
	--tile-ink: var(--white);
}

.page--about .team .team-scene__tile {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	--tile-bg: var(--accent);
	--tile-ink: var(--white);
}

.page--about .team .team-scene canvas {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
}

@media only screen and (max-width: 768px) {
	.page--about .team .team-scene { height: 100vw; }
}

/* ==========================================================================
   Research
   ========================================================================== */

/* Shorter sections use the reduced travel, as projects and awards do — at the
   default 60dvh the title's resting offset pushes it over the list below. */
.section-title--research { --travel-clamped: 30dvh; --travel: 20dvh; }
.section-title--research h1 > span:nth-child(1) { grid-row: 1; grid-column: 1 / 7; }
.section-title--research h1 > span:nth-child(2) { grid-row: 2; grid-column: 4 / -1; }

.research-intro {
	position: relative;
	padding-top: var(--title-band);
	padding-bottom: 10vh;
	background: var(--background);
	color: var(--foreground);
}

.research-intro__text {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	gap: var(--gap);
	padding: 0 var(--pad) 10vh;
	position: relative;
	z-index: 2;
}

.research-intro__text > * { grid-column: 7 / span 6; font-size: var(--font-p); }

.publications { position: relative; }

.publications__list {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	grid-auto-flow: row;
	gap: var(--gap);
	row-gap: 0;
	padding: 0 var(--pad);
	font-family: var(--font-mono);
	font-size: var(--font-sd);
	font-variation-settings: "wght" var(--wght-regular);
	color: var(--foreground-dim);
	text-transform: uppercase;
	line-height: 1.3;
}

.publications__list--header { color: var(--foreground-dim); font-size: var(--font-sm); }

.publications__group { position: relative; z-index: 2; padding-bottom: calc( var(--pad) * 3 ); }

.publications__year {
	padding: var(--pad) var(--pad) var(--gap);
	margin: 0 var(--pad);
	color: var(--accent);
}

/* Placement for the printed rules that stand in for the borders these blocks
   used to carry. */
.dot-rule--year { margin: 0 calc( var(--pad) * 2 ) var(--pad); }
.dot-rule--stats { margin-bottom: var(--pad); }
.dot-rule--awards { margin: 0 var(--pad); }

.publication { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; }

.publication__inner {
	grid-column: 1 / -1;
	display: grid;
	grid-template-columns: subgrid;
	align-items: baseline;
	padding-block: calc( var(--gap) / 2 );
}

.publication__num     { grid-column: span 1; color: var(--foreground-dim); }
.publication__title   { grid-column: span 5; color: var(--foreground); }
.publication__venue   { grid-column: span 4; }
.publication__cites   { grid-column: span 1; color: var(--foreground-dim); }
.publication__year    { grid-column: span 1; text-align: end; color: var(--accent); }

.research-stats {
	display: flex;
	flex-wrap: wrap;
	gap: calc( var(--pad) * 1.5 );
	margin: 0 0 var(--pad);
	padding: 0 0 var(--pad);
	border-bottom: var(--border-width) solid var(--foreground-dimmed);
	list-style: none;
}

.research-stats li { display: flex; flex-direction: column; gap: 0.2em; }

.research-stats__value {
	font-family: var(--font-mono);
	font-size: var(--font-lg);
	font-variation-settings: "wght" var(--wght-medium);
	color: var(--accent);
	line-height: 1;
}

/* Same hover sweep as the engagements table. */
.publication__inner > * {
	--hover-fill-background: var(--foreground-dim);

	position: relative;
	isolation: isolate;
}

.publication__inner > *::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	border-radius: var(--border-radius-text);
	background: var(--hover-fill-background);
	transform: scaleX(0);
	transform-origin: right;
	transition: transform 0.3s var(--ease-out-expo);
}

.publication__inner:hover > *::after { transform: scaleX(1); transform-origin: left; }
.publication__inner:hover > * { color: var(--background); }

.publication__status {
	display: inline-block;
	margin-inline-start: 0.5em;
	padding: 0.1em 0.4em;
	border: var(--border-width) solid var(--accent);
	border-radius: var(--border-radius-text);
	font-size: var(--font-xs, 0.75rem);
	color: var(--accent);
}

/* --- Single paper -------------------------------------------------------- */

.publication-hero { padding: 30vh var(--pad) 10vh; max-width: 78ch; }

.publication-hero__title {
	font-family: var(--font-sans);
	font-size: clamp( 1.75rem, 5vw, 3.25rem );
	line-height: 1.05;
	margin-block: var(--gap) var(--pad);
}

.publication-hero__authors {
	font-family: var(--font-mono);
	font-size: var(--font-sd);
	text-transform: uppercase;
	color: var(--accent);
}

.publication-hero__abstract { font-size: var(--font-sd); color: var(--foreground-dim); }
.publication-hero__eyebrow { display: flex; gap: 0.5em; flex-wrap: wrap; }

.publication-body {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	gap: var(--gap);
	padding: 0 var(--pad) 10vh;
}

.publication-facts { grid-column: 1 / span 3; margin: 0; }
.publication-content { grid-column: 5 / span 6; max-width: 72ch; }

.publication-facts__row {
	padding: var(--gap) 0;
	border-top: var(--border-width) solid var(--foreground-dimmed);
}

.publication-facts__row dd { margin: 0.2em 0 0; }
.publication-facts__action { margin-top: var(--pad); }

.other-publications { margin-top: calc( var(--pad) * 6 ); position: relative; padding-top: var(--title-band); }
.other-publications .publications__list { position: relative; z-index: 2; }

/* --- Research: responsive ------------------------------------------------ */

@media only screen and (max-width: 1024px) {
	.research-intro__text > * { grid-column: 4 / -1; }
	.publication__title   { grid-column: span 4; }
	.publication__venue   { grid-column: span 4; }
	.publication__cites   { grid-column: span 2; }
	.publication__year    { grid-column: span 1; }
}

/* Below the header row the columns stack: title and venue each take the full
   width so the venue stays readable rather than being squeezed or dropped. */
@media only screen and (max-width: 768px) {
	.research-intro__text > * { grid-column: 1 / -1; }
	.publications__list--header { display: none; }
	.publication__num { display: none; }
	.publication__title { grid-column: span 9; }
	.publication__venue { grid-column: span 9; }
	.publication__cites { display: none; }
	.publication__year { grid-column: span 3; }
	.publication-facts,
	.publication-content { grid-column: 1 / -1; }
}

.project-facts__actions { display: flex; flex-wrap: wrap; gap: var(--gap); margin-top: var(--pad); }

/* Publication hero figure — decorative texture, same treatment as projects. */
.publication-figure {
	aspect-ratio: 16 / 10;
	margin: 0 var(--pad) calc( var(--pad) * 3 );
	border: var(--border-width) solid var(--foreground-dimmed);
	border-radius: var(--border-radius);
	overflow: hidden;
}

.publication-figure .quadtree,
.publication-figure img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* --- Thumbnails ---------------------------------------------------------- */

.thumb { position: relative; width: 100%; }

.thumb .quadtree,
.thumb .quadtree img { width: 100%; height: 100%; object-fit: cover; }

/* Generative fallback: drawn per entry from a seed, so no asset is needed.
   It carries its own aspect ratio because the surrounding row takes its height
   from the image it no longer has — without this the tile measures zero and
   the canvas never sizes. */
.thumb__tile {
	position: relative;
	width: 100%;
	aspect-ratio: 16 / 10;
	background: var(--background);
	overflow: hidden;
}

/* The tile fills the wrapper; the quadtree paints its own canvas on top of it
   and removes that once the resolve finishes, revealing the tile beneath. */
.thumb__tile .tile__canvas {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	transition: opacity var(--duration-base) var(--ease-out);
}

/* Where the container already defines its own box, fill it instead. */
.project-figure .thumb,
.publication-figure .thumb { height: 100%; }

.project-figure .thumb__tile,
.publication-figure .thumb__tile { height: 100%; aspect-ratio: auto; }

.thumb__tile canvas {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
}

/* Three inks so adjacent entries do not read as a matched set. Each needs real
   contrast against the ground: --foreground-dimmed (#1a1a1a) on --background
   (#0f0f0f) is a 2% difference, invisible to both the eye and the quadtree. */
.thumb__tile,
.thumb__tile .tile__canvas { --tile-bg: var(--background); }
/* Bright enough to survive the quadtree's averaging. Pure --accent is
   perceptually very dark (luma ~29) despite being saturated, so it is mixed
   toward white rather than used neat. */
.thumb__tile--ink-0 { --tile-ink: var(--grey); }
.thumb__tile--ink-1 { --tile-ink: var(--blue-bright); }
.thumb__tile--ink-2 { --tile-ink: var(--white); }

/* The home page's project list paints light, where white ink on a white
   ground drew nothing (AutoEx) and grey barely more. The same three inks,
   inverted for that ground. */
.theme--light .thumb__tile--ink-0 { --tile-ink: var(--ash); }
.theme--light .thumb__tile--ink-2 { --tile-ink: var(--coal); }

.project-item .thumbnail,
.project-figure,
.publication-figure { background: var(--background); }

/* --- Services ------------------------------------------------------------
   Three offers, each a twelve-column block: number and title across the top,
   the price and the copy down the start side, the printed figure on the end
   side. Everything is logical, so the Arabic page mirrors on its own. */

.page--services .offers { padding: 0 var(--pad) 10vh; }

.offer {
	position: relative;
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	gap: var(--gap);
	padding: 6vh 0 10vh;
}

.offer__num { grid-column: 1 / span 12; margin: 0; }

.offer__title {
	grid-column: 1 / span 12;
	margin: var(--gap) 0 0;
	font-family: var(--font-sans);
	font-size: 5vw;
	line-height: 1.05;
	color: var(--foreground);
}

.offer__promise {
	grid-column: 1 / span 7;
	margin: var(--pad) 0 0;
	font-size: var(--font-p);
	line-height: 1.3;
	color: var(--foreground-dim);
}

.offer__price {
	grid-column: 1 / span 4;
	grid-row: 4;
	display: flex;
	flex-direction: column;
	gap: var(--pad);
	margin-top: calc( var(--pad) * 2 );
}

.price__amount {
	display: flex;
	align-items: center;
	gap: 0.35em;
	color: var(--foreground);
}

/* The riyal sign is a recent glyph: only the system faces carry it. */
.sar {
	font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
	font-size: calc( var(--font-md) * 1.15 );
	line-height: 1;
	color: var(--accent);
}

.sar--text {
	font-family: var(--font-mono);
	font-size: var(--font-sm);
	letter-spacing: 0.05em;
}

/* Two classes: the generic .dot-text rule below sets width 100%, and the
   price must not take the column. */
.price__amount .price__figure { height: calc( var(--font-md) * 1.6 ); width: auto; flex: none; }
.price__to { font-size: var(--font-md); color: var(--foreground-dim); }
.price__term { display: block; margin-top: 0.4em; }

.offer__body {
	grid-column: 1 / span 4;
	grid-row: 5;
	margin-top: var(--pad);
}

.offer__body > p:first-child { margin-top: 0; }
.offer__body h3 { margin: calc( var(--pad) * 1.5 ) 0 var(--gap); }
.offer__included { margin: 0; padding: 0; list-style: none; }

.offer__included li {
	position: relative;
	padding-inline-start: 1.4em;
	margin-bottom: 0.35em;
}

/* Each included line is struck with a cell of its own. */
.offer__included li::before {
	content: "";
	position: absolute;
	inset-inline-start: 0;
	top: 0.55em;
	width: 0.5em;
	height: 0.5em;
	background: var(--accent);
}

.offer__cta { margin-top: calc( var(--pad) * 1.5 ); }

.offer__figure {
	grid-column: 6 / span 7;
	grid-row: 4 / span 2;
	margin: calc( var(--pad) * 2 ) 0 0;
	color: var(--foreground);
}

.offer__figure figcaption { margin-bottom: var(--pad); }

/* Dot figures: struck cells in the ink, the accent where a cell is marked,
   the unstruck field faint behind them. */
.dot-grid, .dot-text { display: block; width: 100%; height: auto; direction: ltr; }
.dot-grid__field { opacity: 0.22; }
.dot-grid__accent { fill: var(--accent); }
.dot-grid__dim { opacity: 0.5; }

/* Hosting: the calendar has its row labels down the start side. */
.offer__calendar {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: var(--gap);
	align-items: center;
}

.offer__calendar-rows {
	display: grid;
	grid-auto-rows: 1fr;
	margin: 0;
	padding: 0;
	list-style: none;
	align-self: stretch;
}

.offer__calendar-rows li { display: flex; align-items: center; }

.offer__stats {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--gap);
	margin: calc( var(--pad) * 2 ) 0 0;
}

.offer__stats dd { margin: 0.5em 0 0; }
.offer__stats .dot-text { width: 100%; max-width: 12em; }

/* Build: the three tiers stack, each meter a little longer than the last. */
.offer__tiers { margin: 0; padding: 0; list-style: none; display: grid; gap: var(--pad); }
.offer__tier { display: grid; grid-template-columns: 1fr; gap: var(--gap); }
.offer__tier .dot-meter { width: 100%; }

.offer__tier-text {
	display: grid;
	grid-template-columns: auto auto 1fr;
	gap: var(--gap);
	align-items: baseline;
	font-family: var(--font-mono);
	font-size: var(--font-sm);
}

.offer__tier-name { text-transform: uppercase; }
.offer__tier-price { color: var(--accent); white-space: nowrap; }
.offer__tier-desc { color: var(--foreground-dim); }

.offer__phases { margin-top: calc( var(--pad) * 2 ); }
.offer__phases > p { margin-bottom: var(--gap); }

.offer__phase-list {
	display: flex;
	gap: var(--gap);
	margin: 0;
	padding: 0;
	list-style: none;
}

.offer__phase { flex: var(--weeks, 1) 1 0; min-width: 0; font-family: var(--font-mono); font-size: var(--font-sm); }
.offer__phase .dot-grid { width: 100%; }
.offer__phase-name { display: block; margin-top: 0.4em; text-transform: uppercase; }
.offer__phase-weeks { color: var(--foreground-dim); }

/* Boost: the comparison table prints a meter per cell. */
.offer__compare {
	width: 100%;
	border-collapse: collapse;
	font-family: var(--font-mono);
	font-size: var(--font-sm);
}

.offer__compare th, .offer__compare td { padding: 0.5em 0; text-align: start; vertical-align: middle; border-bottom: 1px solid var(--foreground-dimmed); }
.offer__compare th[scope="row"] { font-weight: normal; color: var(--foreground-dim); padding-inline-end: var(--pad); width: 46%; }
.offer__compare td { width: 27%; padding-inline-end: var(--gap); }
.offer__compare .dot-grid { width: 100%; }

.offer__vitals { margin-top: calc( var(--pad) * 2 ); }
.offer__vitals > p { margin-bottom: var(--gap); }
.offer__vitals dl { margin: 0; display: grid; gap: var(--gap); }
.offer__vital dt { font-family: var(--font-mono); font-size: var(--font-sm); color: var(--foreground-dim); margin-bottom: 0.3em; }
.offer__vital dd { margin: 0; display: grid; grid-template-columns: 1fr auto; gap: var(--gap); align-items: center; }
.offer__vital-target .dot-text { width: auto; height: 1.1em; }

@media only screen and (max-width: 1024px) {
	.offer__title { font-size: 7vw; }
	.offer__promise { grid-column: 1 / span 9; }
	.offer__price, .offer__body { grid-column: 1 / span 5; }
	.offer__figure { grid-column: 7 / span 6; }
}

@media only screen and (max-width: 768px) {
	.offer__title { font-size: 9vw; }
	.offer__promise, .offer__price, .offer__body, .offer__figure { grid-column: 1 / -1; }
	.offer__price { grid-row: auto; }
	.offer__body { grid-row: auto; }
	.offer__figure { grid-row: auto; margin-top: calc( var(--pad) * 2 ); }
	.offer__stats { grid-template-columns: 1fr; }
	.offer__tier-text { grid-template-columns: auto auto; }
	.offer__tier-desc { grid-column: 1 / -1; }
	.offer__compare th[scope="row"] { width: 40%; }
}

/* --- Services teaser (home) ---------------------------------------------
   The three offers by name and price, under a two-word title on its band. */

.services-teaser {
	position: relative;
	margin-top: calc( var(--pad) * 6 );
	padding-top: var(--title-band);
	background: var(--background);
	color: var(--foreground);
}

.section-title--services-teaser { --travel-clamped: 30dvh; --travel: 20dvh; }
.section-title--services-teaser h2 > span:nth-child(1) { grid-row: 1; grid-column: 1 / 6; }
.section-title--services-teaser h2 > span:nth-child(2) { grid-row: 2; grid-column: 4 / -1; }

.services-teaser__list {
	position: relative;
	z-index: 2;
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: var(--gap);
	margin: 0;
	padding: 0 var(--pad) 10vh;
	list-style: none;
}

.services-teaser__item {
	grid-column: auto;
	display: flex;
	flex-direction: column;
	gap: var(--gap);
	padding-top: var(--gap);
}

.services-teaser__item .dot-rule { margin: 0 0 var(--gap); }
.services-teaser__num { margin: 0; }

.services-teaser__title {
	margin: 0;
	font-family: var(--font-sans);
	font-size: var(--font-md);
	line-height: 1.15;
}

.services-teaser__title a { text-transform: none; }

.services-teaser__price {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 0.4em var(--gap);
	margin: var(--gap) 0 0;
	color: var(--foreground);
}

.services-teaser__price .price { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.4em var(--gap); max-width: 100%; }
/* Four columns leave each price about 300px: the figure must shrink to the
   column rather than run under its neighbour. */
.services-teaser__price .price__amount { max-width: 100%; }
.services-teaser__price .price__amount .price__figure { height: calc( var(--font-md) * 1.1 ); max-width: 100%; min-width: 0; flex: 0 1 auto; }
.services-teaser__price .sar { font-size: var(--font-md); }
.services-teaser__price .price__term { display: inline; margin: 0; }
.services-teaser__or { align-self: center; color: var(--foreground-dim); }

.services-teaser__promise {
	margin: 0;
	font-family: var(--font-mono);
	font-size: var(--font-p);
	font-variation-settings: "wght" var(--wght-light);
	line-height: 1.35;
	color: var(--foreground-dim);
	max-width: 34ch;
}

.services-teaser__more { margin: auto 0 0; padding-top: var(--gap); }

@media only screen and (max-width: 1024px) {
	.services-teaser__list { grid-template-columns: repeat(2, 1fr); }
	.service-others ul { grid-template-columns: repeat(2, 1fr); }
}

@media only screen and (max-width: 768px) {
	.services-teaser__list { grid-template-columns: 1fr; }
	.section-title--services-teaser h2 > span { grid-column: 1 / -1; }
}

/* --- Services: what we do, A to Z -----------------------------------------
   Printed for crawlers and screen readers only (the .screen-reader-text clip);
   the block rules below must not undo that clip.
   The capability index: ten groups in four columns, each term a link to the
   service that does it. Mono, dim, dense — an index, not a pitch. */
.expertise { position: relative; z-index: 2; padding: var(--pad) var(--pad) 8vh; }
.expertise.screen-reader-text { position: absolute; margin: 0; padding: 0; }
.expertise__num { margin: 0 0 var(--gap); }
.expertise__title { margin: 0 0 var(--gap); font-family: var(--font-sans); font-size: var(--font-lg); line-height: 1.1; }
.expertise__intro { max-width: 60ch; margin: 0 0 calc( var(--pad) * 2 ); font-family: var(--font-mono); font-variation-settings: "wght" var(--wght-light); line-height: 1.45; color: var(--foreground-dim); }
.expertise__groups { display: grid; grid-template-columns: repeat(4, 1fr); gap: calc( var(--pad) * 2 ) var(--gap); }
.expertise__group-title { margin: 0 0 var(--gap); padding-top: var(--gap); border-top: var(--border-width) solid var(--foreground-dimmed); color: var(--foreground); }
.expertise__group-title a:hover { color: var(--accent); }
.expertise__terms { margin: 0; padding: 0; list-style: none; font-family: var(--font-mono); font-size: var(--font-sm); font-variation-settings: "wght" var(--wght-light); line-height: 1.5; color: var(--foreground-dim); }
.expertise__terms li { padding: 0.15em 0; }
.expertise__terms a { text-transform: none; }
.expertise__terms a:hover { color: var(--accent); }

@media only screen and (max-width: 1024px) { .expertise__groups { grid-template-columns: repeat(2, 1fr); } }
@media only screen and (max-width: 584px)  { .expertise__groups { grid-template-columns: 1fr; } }

/* The Najdi band under a page hero keeps the hero's side padding. */
.dot-rule--hero { margin: 0 var(--pad); }

/* --- Service detail pages ---------------------------------------------------
   One offer, long form: a hero with the price and three numbers, a scene
   printed underneath, chapters that alternate copy and figure across the
   twelve columns, the questions, the coffee, and the other two offers. All
   logical, so the Arabic pages mirror on their own. */

.service-hero {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	gap: var(--gap);
	padding: 24vh var(--pad) 6vh;
}

.service-hero__eyebrow { grid-column: 1 / -1; display: flex; align-items: center; gap: var(--gap); margin: 0; }

.service-hero__title {
	grid-column: 1 / -1;
	margin: var(--gap) 0 0;
	font-family: var(--font-sans);
	font-size: 8vw;
	line-height: 1;
	color: var(--foreground);
}

.service-hero__promise {
	grid-column: 1 / span 7;
	margin: var(--pad) 0 0;
	font-size: var(--font-p);
	line-height: 1.3;
	color: var(--foreground-dim);
}

.service-hero__price {
	grid-column: 1 / span 6;
	display: flex;
	flex-wrap: wrap;
	gap: var(--pad) calc( var(--pad) * 2 );
	margin-top: calc( var(--pad) * 2 );
}

.service-hero__cta { grid-column: 1 / span 6; margin: var(--pad) 0 0; }

.service-facts {
	grid-column: 8 / span 5;
	align-self: end;
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: var(--gap);
	margin: calc( var(--pad) * 2 ) 0 0;
}
.service-facts dd { margin: 0.5em 0 0; }
.service-facts .dot-text { width: 100%; max-width: 8em; height: auto; }
.service-facts--inline { display: flex; gap: calc( var(--pad) * 2 ); margin-top: var(--pad); }
.service-facts--grid { grid-template-columns: repeat(2, 1fr); gap: var(--pad) var(--gap); }
.service-facts--grid .dot-text { max-width: 7em; }
.offer__cta { display: flex; flex-wrap: wrap; gap: var(--gap) var(--pad); align-items: center; }
.service-facts--inline .dot-text { max-width: 6em; }

/* The scenes: a canvas on the wordmark's grid, tokens shared with the skyline. */
.service-scene { padding: 0 var(--pad); }
.service-scene__legend { display: flex; justify-content: space-between; gap: var(--pad); margin: var(--gap) 0 0; direction: ltr; }
.scene--hero { height: clamp( 200px, 30vw, 440px ); }
.scene--figure { height: clamp( 110px, 16vw, 200px ); margin-bottom: var(--gap); }
.scene--invite { height: clamp( 140px, 16vw, 220px ); }

.service-chapters { padding: 0 var(--pad); }

.chapter {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	gap: var(--gap);
	padding: 8vh 0;
}
.chapter__num { grid-column: 1 / -1; margin: 0; }
.chapter__title {
	grid-column: 1 / span 10;
	margin: var(--gap) 0 0;
	font-family: var(--font-sans);
	font-size: 4vw;
	line-height: 1.05;
	color: var(--foreground);
}
.chapter__copy { grid-column: 1 / span 5; margin-top: var(--pad); }
.chapter__copy > p:first-child { margin-top: 0; }
.chapter__figure { grid-column: 7 / span 6; margin: var(--pad) 0 0; min-width: 0; }
.chapter__figure > figcaption { margin-bottom: var(--pad); }
.chapter--alt .chapter__copy { grid-column: 8 / span 5; }
.chapter--alt .chapter__figure { grid-column: 1 / span 6; grid-row: 4; }
.dot-rule--chapter { margin: 0; }

/* A labelled grid: labels down the start side, the figure beside them, an
   axis under it. Rows are sized to the grid's own rows so the labels line up
   with the cells at any width. */
.service-calendar {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: 0 var(--gap);
	align-items: stretch;
}
.service-calendar__rows { margin: 0; padding: 0; list-style: none; display: grid; grid-auto-rows: 1fr; }
.service-calendar__rows li { display: flex; align-items: center; white-space: nowrap; min-height: 0; line-height: 1; }
.service-calendar .dot-grid { width: 100%; height: auto; }
.service-calendar__axis { grid-column: 2; display: flex; justify-content: space-between; margin: var(--gap) 0 0; direction: ltr; }
.service-calendar--checks .dot-grid { max-width: 12em; }
.service-calendar--ladder .service-calendar__rows { text-align: end; }

/* Bars: one meter per row, labels at the start. */
.service-bars { margin: 0; display: grid; gap: var(--gap); }
.service-bars__row { display: grid; grid-template-columns: minmax(6em, 9em) 1fr; gap: var(--gap); align-items: center; }
.service-bars__row dd { margin: 0; min-width: 0; }
.service-bars .dot-grid { width: 100%; height: auto; }
.service-bars--scores dd { display: grid; grid-template-columns: 1fr auto; gap: 0.3em var(--gap); align-items: center; }
.service-bars--scores dd .dot-grid { grid-column: 1; }
.service-bars__values { grid-column: 2; grid-row: 1 / span 2; display: grid; gap: 0.3em; justify-items: end; }
.service-bars__values .dot-text { width: auto; height: 1em; }
.dot-text.is-accent { color: var(--accent); }
.service-bars-chart { width: 100%; max-width: 18em; height: auto; }
.service-strip { width: 100%; height: auto; }
.service-thousand { width: 100%; height: auto; }

/* Screenshots of the platforms: two side by side, or one full width, each
   framed by a hairline with a Sadu band across the top so they read as
   prints on the page rather than pasted images. The band is the header's. */
.service-shots { display: grid; grid-template-columns: 1fr; gap: var(--pad) var(--gap); margin: 0 0 var(--pad); }
.service-shots--2 { grid-template-columns: repeat(2, 1fr); align-items: start; }
.service-shot { position: relative; margin: 0; min-width: 0; }
.service-shot img {
	width: 100%;
	height: auto;
	border: var(--border-width) solid var(--foreground-dimmed);
	border-top-width: 6px;
	background: var(--black);
}
.service-shot::before {
	content: "";
	position: absolute;
	top: 0;
	inset-inline-start: 0;
	width: 100%;
	height: 6px;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='6' viewBox='0 0 6 3' shape-rendering='crispEdges'%3E%3Cpath fill='%23fff' d='M2 0h1v1H2zM1 1h3v1H1zM0 2h5v1H0z'/%3E%3C/svg%3E");
	background-size: 12px 6px;
	opacity: 0.16;
	pointer-events: none;
}
.service-shot > figcaption { margin-top: var(--gap); }
.service-shot--phone { max-width: 22rem; }
.service-shots + .service-bars, .service-shots + .service-calendar { margin-top: calc( var(--pad) * 2 ); }
.service-calendar--week .dot-grid, .service-calendar--month .dot-grid { width: 100%; height: auto; }

.service-steps { margin: var(--pad) 0 0; padding: 0; list-style: none; display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--pad); }
.service-steps li { display: grid; gap: 0.4em; min-width: 0; }
.service-steps .dot-text { width: auto; max-width: 100%; height: 1.1em; }

.service-matrix { width: 100%; border-collapse: collapse; font-family: var(--font-mono); font-size: var(--font-sm); color: var(--foreground-dim); }
.service-matrix th, .service-matrix td { padding: 0.5em 0; text-align: start; vertical-align: middle; border-bottom: 1px solid var(--foreground-dimmed); }
.service-matrix th[scope="row"] { font-weight: normal; padding-inline-end: var(--pad); width: 40%; }
.service-matrix th[scope="col"] { font-weight: normal; padding-inline-end: var(--gap); }
.service-matrix td { width: 20%; padding-inline-end: var(--gap); }
.service-matrix--two td { width: 30%; }
.service-matrix .dot-grid { width: 100%; }
.service-matrix__price { display: block; margin-top: 0.3em; color: var(--accent); white-space: nowrap; }

.service-faq {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	gap: var(--gap);
	padding: 8vh var(--pad);
}
.service-faq > h2 { grid-column: 1 / span 4; margin: 0; }
.service-faq dl { grid-column: 5 / span 6; margin: 0; }
.service-faq dt { padding-top: var(--pad); font-family: var(--font-mono); font-size: var(--font-sd); line-height: 1.3; color: var(--foreground); border-top: var(--border-width) solid var(--foreground-dimmed); }
.service-faq dd { margin: 0.5em 0 var(--pad); font-family: var(--font-mono); font-variation-settings: "wght" var(--wght-light); line-height: 1.45; color: var(--foreground-dim); }

.service-invite {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	gap: var(--gap);
	align-items: center;
	padding: 4vh var(--pad) 8vh;
}
.service-invite .scene--invite { grid-column: 1 / span 5; }
.service-invite__text { grid-column: 7 / span 6; display: grid; gap: var(--pad); }
.service-invite__text > p { margin: 0; }
.service-invite__title { margin: 0; font-family: var(--font-sans); font-size: 4vw; line-height: 1.05; color: var(--foreground); }
.service-invite__copy { font-family: var(--font-mono); font-variation-settings: "wght" var(--wght-light); line-height: 1.45; color: var(--foreground-dim); max-width: 48ch; }

.service-others { padding: 0 var(--pad) 8vh; }
.service-others > h2 { margin: 0 0 var(--pad); }
.service-others ul { margin: 0; padding: 0; list-style: none; display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--gap); }
.service-other {
	display: grid;
	gap: 0.5em;
	height: 100%;
	padding: var(--pad);
	border: var(--border-width) solid var(--foreground-dimmed);
	color: var(--foreground);
	text-decoration: none;
	transition: border-color var(--duration-base) var(--ease-out);
}
.service-other:hover { border-color: var(--foreground-dim); }
.service-other__title { font-family: var(--font-sans); font-size: var(--font-md); line-height: 1.1; }
.service-other__price { font-family: var(--font-mono); color: var(--accent); }
.service-other__promise { font-family: var(--font-mono); font-size: var(--font-sm); font-variation-settings: "wght" var(--wght-light); line-height: 1.45; color: var(--foreground-dim); }

@media only screen and (max-width: 1024px) {
	.service-others ul { grid-template-columns: repeat(2, 1fr); }
	.service-hero__title { font-size: 10vw; }
	.chapter__title, .service-invite__title { font-size: 6vw; }
	.service-hero__promise { grid-column: 1 / span 9; }
	.chapter__copy, .chapter--alt .chapter__copy { grid-column: 1 / span 6; }
	.chapter__figure, .chapter--alt .chapter__figure { grid-column: 7 / span 6; grid-row: auto; }
}

@media only screen and (max-width: 768px) {
	.service-hero { padding-top: 20vh; }
	.service-hero__title { font-size: 12vw; }
	.chapter__title, .service-invite__title { font-size: 8vw; }
	.service-hero__promise, .service-hero__price, .service-hero__cta, .service-facts,
	.chapter__title, .chapter__copy, .chapter__figure, .chapter--alt .chapter__copy, .chapter--alt .chapter__figure,
	.service-faq > h2, .service-faq dl, .service-invite .scene--invite, .service-invite__text { grid-column: 1 / -1; }
	.chapter__figure, .chapter--alt .chapter__figure { grid-row: auto; }
	.service-facts { margin-top: var(--pad); }
	.service-others ul { grid-template-columns: 1fr; }
	.service-bars__row { grid-template-columns: minmax(5em, 7em) 1fr; }
	.service-steps { grid-template-columns: 1fr 1fr; }
	.service-shots--2 { grid-template-columns: 1fr; }
	.service-calendar--month .service-calendar__rows { font-size: 1rem; }
}

@media only screen and (max-width: 584px) {
	.service-facts { grid-template-columns: repeat(3, 1fr); gap: var(--gap); }
	.service-facts .dot-text { max-width: none; }
	.service-facts--inline { flex-direction: row; }
	.service-matrix th[scope="row"] { width: 34%; }
	.service-calendar__rows li { font-size: 0.9375em; }
}

/* --- About: the founder ---------------------------------------------------
   A portrait printed on the dot matrix (effects/portrait.js) beside the
   facts. The team section paints light, so the portrait's ink is the dark
   foreground and dot size follows darkness. */

.founder {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	gap: var(--gap);
	padding: 4vh 0 10vh;
}
.founder__portrait { grid-column: 1 / span 5; align-self: start; }
.portrait {
	--portrait-ink: var(--foreground);
	--portrait-accent: var(--accent);
	--portrait-dark: 0;

	position: relative;
	width: 100%;
	aspect-ratio: 1;
}
.theme--light .portrait { --portrait-dark: 1; }
.portrait canvas { position: absolute; inset: 0; width: 100%; height: 100%; }

.founder__text { grid-column: 7 / span 6; display: grid; gap: var(--pad); align-content: start; }
.founder__text > p { margin: 0; }
.founder__name { margin: 0; font-family: var(--font-sans); font-size: 4vw; line-height: 1.05; color: var(--foreground); }
.founder__title { font-family: var(--font-mono); font-size: var(--font-sd); color: var(--foreground-dim); }
.founder__facts { margin: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap); }
.founder__facts dd { margin: 0.5em 0 0; }
.founder__facts .dot-text { width: 100%; max-width: 6em; height: auto; }
.founder__bio > p:first-child { margin-top: 0; }
.founder__lines { margin: 0; display: grid; gap: var(--gap); }
.founder__lines div { padding-top: var(--gap); border-top: var(--border-width) solid var(--foreground-dimmed); }
.founder__lines dd { margin: 0.3em 0 0; font-family: var(--font-mono); font-variation-settings: "wght" var(--wght-light); line-height: 1.45; color: var(--foreground-dim); }
.founder__links { display: flex; flex-wrap: wrap; gap: var(--gap); }

/* The rest of the team, under the managing director and across the full
   width: shorter, two to a row, the initials in the dot font where a portrait
   would be. */
.team-members {
	grid-column: 1 / -1;
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: var(--pad) var(--gap);
	margin: calc( var(--pad) * 2 ) 0 0;
	padding: 0;
	list-style: none;
}
.team-member {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: var(--pad);
	align-items: start;
	padding-top: var(--gap);
	border-top: var(--border-width) solid var(--foreground-dimmed);
}
.team-member__mark { padding: 0.6em; border: var(--border-width) solid var(--foreground-dimmed); line-height: 0; }
.team-member__mark .dot-text { width: 2.6em; height: auto; color: var(--foreground); }
.team-member__text > p { margin: 0; }
.team-member__name { margin: 0.35em 0 0; font-family: var(--font-sans); font-size: var(--font-md); line-height: 1.1; color: var(--foreground); }
.team-member__text > .team-member__title { margin-top: 0.45em; font-family: var(--font-mono); font-variation-settings: "wght" var(--wght-light); line-height: 1.4; color: var(--foreground-dim); }

/* --- Client sites ------------------------------------------------------------
   Every domain in the dot font. The About page lays them out as a grid of
   links; the home page rolls them past in two rows. */

.clients { position: relative; padding-top: var(--title-band); }
.section-title--clients h2 > span:nth-child(1) { grid-row: 1; grid-column: 1 / 6; }
.section-title--clients h2 > span:nth-child(2) { grid-row: 2; grid-column: 5 / -1; }
.section-title--clients h2 > span:nth-child(3) { grid-row: 3; grid-column: 1 / -1; }
.clients .dot-rule--clients { margin: 0 var(--pad); }
.clients__intro { position: relative; z-index: 2; margin: var(--pad) var(--pad) 0; max-width: 60ch; font-family: var(--font-mono); font-variation-settings: "wght" var(--wght-light); line-height: 1.45; color: var(--foreground-dim); }
.clients__grid {
	position: relative;
	z-index: 2;
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: var(--gap);
	margin: 0;
	padding: calc( var(--pad) * 2 ) var(--pad) 6vh;
	list-style: none;
}
.client__link {
	display: grid;
	gap: 0.4em;
	height: 100%;
	padding: var(--pad) 0;
	border-top: var(--border-width) solid var(--foreground-dimmed);
	color: var(--foreground);
	text-decoration: none;
	transition: border-color var(--duration-base) var(--ease-out);
}
.client__link:hover { border-color: var(--accent); }
.client__link:hover .client__domain { color: var(--accent); }
.client__domain { width: 100%; max-width: 14em; height: auto; color: var(--foreground); }
.client__name { font-family: var(--font-mono); font-size: var(--font-sd); }
.client__sector { color: var(--foreground-dim); }

.clients-ticker { position: relative; padding: 6vh 0 8vh; overflow: hidden; }
.clients-ticker .dot-rule--ticker { margin: 0 var(--pad); }
.clients-ticker__label { display: flex; justify-content: space-between; gap: var(--pad); margin: var(--pad) var(--pad) 0; }
.clients-ticker__row { display: flex; gap: calc( var(--pad) * 3 ); margin-top: var(--pad); overflow: hidden; }
.clients-ticker__track { display: flex; flex: none; gap: calc( var(--pad) * 3 ); padding-inline-end: calc( var(--pad) * 3 ); animation: teconya-ticker 70s linear infinite; }
.clients-ticker__row--reverse .clients-ticker__track { animation-direction: reverse; animation-duration: 84s; }
.clients-ticker__item { flex: none; }
.clients-ticker__track { will-change: transform; }

/* Each tile is its own layout and paint boundary: twenty-nine dot-font
   domains are a few thousand rectangles, and a scroll must not re-lay-out or
   re-paint the ones that did not change. (content-visibility was tried here
   and made scrolling worse — tiles switching in and out of rendering as
   they crossed the viewport edge.) */
.client { contain: layout paint style; }
.clients-ticker__item .dot-text { width: auto; height: clamp( 1.1rem, 1.6vw, 1.5rem ); color: var(--foreground-dim); }
.clients-ticker:hover .clients-ticker__track { animation-play-state: paused; }

@keyframes teconya-ticker {
	from { transform: translateX(0); }
	to   { transform: translateX(-100%); }
}

@media (prefers-reduced-motion: reduce) {
	.clients-ticker__row { flex-wrap: wrap; }
	.clients-ticker__track { animation: none; flex-wrap: wrap; }
	.clients-ticker__track + .clients-ticker__track { display: none; }
}

@media only screen and (max-width: 1024px) {
	.founder__name { font-size: 6vw; }
	.founder__portrait { grid-column: 1 / span 6; }
	.founder__text { grid-column: 7 / span 6; }
	.clients__grid { grid-template-columns: repeat(3, 1fr); }
	.service-others ul { grid-template-columns: repeat(2, 1fr); }
}

@media only screen and (max-width: 768px) {
	.founder__name { font-size: 9vw; }
	.founder__portrait, .founder__text { grid-column: 1 / -1; }
	.founder__portrait { max-width: 420px; }
	.clients__grid { grid-template-columns: repeat(2, 1fr); }
	.section-title--clients h2 > span { grid-column: 1 / -1; }
	.service-others ul { grid-template-columns: 1fr; }
}

@media only screen and (max-width: 584px) {
	.clients__grid { grid-template-columns: 1fr; }
	.client__domain { max-width: 18em; }
	.team-members { grid-template-columns: 1fr; }
}

/* Until a photo lands at assets/img/founder.jpg the portrait slot prints the
   name in the dot font instead. */
.founder__mark { display: grid; gap: var(--gap); align-content: start; padding: var(--pad); border: var(--border-width) solid var(--foreground-dimmed); }
.founder__mark .dot-text { width: 100%; max-width: 12em; height: auto; }

/* --- Project scenes and the research strip on the home page --------------- */
/* .page beats the .scene width rule in effects.css, which loads later. */
.page .scene--project { height: clamp( 180px, 28vw, 380px ); margin: 0 var(--pad) 6vh; width: auto; }
.page--home .welcome .experiments__text {
	position: relative;
	z-index: 2;
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	gap: var(--gap);
	padding: calc( var(--pad) * 3 ) var(--pad) 0;
}
.page--home .welcome .experiments__text > p { grid-column: span 6; margin: 0; }
.page--home .welcome .experiments__papers { position: relative; z-index: 2; padding: calc( var(--pad) * 3 ) 0 0; }
.page--home .welcome .experiments__more { margin: var(--pad) var(--pad) 0; }
@media only screen and (max-width: 768px) {
	.page--home .welcome .experiments__text > p { grid-column: 1 / -1; }
}

/* --- Contact: the dot-matrix strip above the footer --------------------------
   Two scenes pinned above the fixed link row: the Riyadh clock and the
   dallah. The canvas text keeps clear of both (contact-canvas.js measures
   the strip and the footer and shortens its grid to match). */
.contact-strip {
	position: fixed;
	inset-inline: var(--pad);
	bottom: calc( var(--contact-footer-h, 180px) + var(--gap) );
	z-index: 2;
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: var(--pad);
	pointer-events: none;
}
.contact-strip__cell { display: grid; gap: var(--gap); align-content: end; }
.contact-strip__cell > p { margin: 0; }
.contact-strip__cell--coffee { justify-items: end; text-align: end; }
.contact-strip .scene--contact { height: clamp( 80px, 12vh, 130px ); width: 100%; max-width: 420px; }
.contact-strip .scene { --art-ink: var(--foreground-dimmed); --art-hot: var(--foreground-dim); --art-accent: var(--accent); }
@media only screen and (max-width: 768px) {
	.contact-strip { grid-template-columns: 1fr; }
	.contact-strip__cell--coffee { display: none; }
	.contact-strip .scene--contact { height: clamp( 64px, 10vh, 90px ); }
}
/* The footer game keeps its full court on contact — a flat strip made the
   rally unreadable — and the clock and dallah sit well above it, so the
   space between the drifting copy and the footer is used rather than left
   empty. The canvas text ends above the strip (contact-canvas.js). */
.page--contact + .site-footer .footer-game,
body.page-template-template-contact .site-footer .footer-game { height: min( clamp( 160px, 18vw, 260px ), 22vh ); }
/* The fallback is the footer's own rough height, so the strip already sits
   about right in the moment before the canvas measures it exactly. */
.contact-strip { bottom: calc( var(--contact-footer-h, clamp( 220px, 24vw, 340px )) + 4vh ); }
.contact-strip .scene--contact { height: clamp( 80px, 11vh, 150px ); }

/* Arabic contact renders the plain, in-flow layout (the canvas cannot set
   cursive text cell by cell), so the strip drops into the flow after the
   copy instead of floating over it, and keeps its own distance from the
   footer. */
.page--contact:has( .contact-fallback--plain ) .contact-strip {
	position: static;
	padding: calc( var(--pad) * 3 ) var(--pad) calc( var(--pad) * 4 );
	pointer-events: auto;
}
.page--contact:has( .contact-fallback--plain ) .contact-strip .scene--contact { max-width: none; }

/* By the numbers: the figure column is set in the dot font. */
.awards .awards__figure { display: inline-block; width: auto; height: 1.1em; vertical-align: -0.1em; color: var(--foreground); }
.awards .awards__item:hover .awards__figure { color: var(--hover-fill-color, var(--background)); }
.awards .awards__item--child .awards__figure { color: var(--foreground-dim); }
/* Short windows — a laptop with the browser chrome taking its share — give
   the text band the room first: the court and the strip both shrink and the
   dallah steps aside, the clock and its hours line staying. */
@media only screen and (max-height: 800px) {
	.page--contact + .site-footer .footer-game,
	body.page-template-template-contact .site-footer .footer-game { height: min( clamp( 120px, 14vw, 200px ), 17vh ); }
	.contact-strip { bottom: calc( var(--contact-footer-h, 200px) + 2vh ); grid-template-columns: 1fr; }
	.contact-strip .scene--contact { height: clamp( 64px, 9vh, 100px ); }
	.contact-strip__cell--coffee { display: none; }
}
@media only screen and (max-height: 800px) {
	/* One step down for the copy on a short window: two more rows of text. */
	.contact-canvas__para-probe { font-size: var(--font-sm); }
}

/* ==========================================================================
   Phones: no parallax, no travel.
   Every scroll-driven offset above — the title parallax, the Fields of Work
   travel, the experiments rise — assumes a viewport wide enough for the
   words to sit beside each other. On a phone the same offsets stack words
   and paragraphs into one another. Below 768px everything rests in its
   row, and the title band is the title's own height plus a margin instead
   of the parallax reserve.
   ========================================================================== */
@media only screen and (max-width: 768px) {
	.section-title,
	.section-title--projects,
	.section-title--awards,
	.section-title--other-projects,
	.section-title--research,
	.section-title--services-teaser,
	.projects-intro > .section-title,
	.research-intro > .section-title,
	.awards > .section-title,
	.clients > .section-title,
	.page--about .team > .section-title,
	.page--about .services > .section-title,
	.other-projects > .section-title,
	.other-publications > .section-title,
	.services-teaser > .section-title,
	.manifesto > .section-title {
		--travel-clamped: 0px;
		--travel: 0px;
	}

	.projects-intro,
	.research-intro,
	.awards,
	.clients,
	.page--about .team,
	.page--about .services,
	.other-projects,
	.other-publications,
	.manifesto,
	.services-teaser {
		--title-band: calc( var(--title-rows) * var(--title-font) * 1.12 + ( var(--title-rows) - 1 ) * var(--gap) + var(--title-extra, 0px) + 8vh );
	}

	/* Research into Practice: on wide screens its title threads between the
	   clips; on a phone the words stack three high and were centred over the
	   copy and the papers below the grid. Bound it to a band like the rest. */
	.page--home .welcome .experiments__reel {
		--title-rows: 3;
		--title-font: 15vw;
		--title-band: calc( var(--title-rows) * var(--title-font) * 1.12 + ( var(--title-rows) - 1 ) * var(--gap) + 8vh );
		padding-top: var(--title-band);
	}
	.page--home .welcome .experiments__reel > .section-title { height: var(--title-band); }

	.page--about .services ul li .inner { transform: none; }
	.page--about .services ul li { margin-bottom: calc( var(--pad) * 2 ); }
	.page--home .welcome .experiments .list .experiment .video { transform: none; }

	/* The sticky subtitle band overprinted whatever scrolled under it on a
	   phone; here it closes its section in flow instead. */
	.subtitle { position: static; mix-blend-mode: normal; }
}

/* --- Also searched as: the page's own terms, one quiet line ---------------- */
.related-searches.screen-reader-text { position: absolute; margin: 0; padding: 0; border: 0; }
.related-searches { position: relative; z-index: 2; margin: calc( var(--pad) * 2 ) var(--pad) 0; padding-top: var(--gap); border-top: var(--border-width) solid var(--foreground-dimmed); }
.related-searches__title { margin: 0 0 0.5em; }
.related-searches__list { margin: 0; max-width: 90ch; font-family: var(--font-mono); font-size: var(--font-sm); font-variation-settings: "wght" var(--wght-light); line-height: 1.6; color: var(--foreground-dim); }
.related-searches__list a { text-transform: none; }
.related-searches__list a:hover { color: var(--accent); }
.contact-fallback .related-searches { margin-inline: 0; }

/* --- Footer index: the ten groups on every page ---------------------------- */
.site-footer__index { position: relative; z-index: 2; padding: var(--pad) var(--pad) 0; }
/* Printed for crawlers and screen readers only (the .screen-reader-text clip);
   a keyboard user tabbing into it gets it back on screen. */
.site-footer__index:focus-within { position: relative; width: auto; height: auto; clip-path: none; white-space: normal; }
.site-footer__index-title { margin: 0 0 var(--gap); }
.site-footer__index-list { display: flex; flex-wrap: wrap; gap: 0.4em var(--pad); margin: 0 0 var(--gap); padding: 0; list-style: none; font-family: var(--font-mono); font-size: var(--font-sm); font-variation-settings: "wght" var(--wght-light); color: var(--foreground-dim); }
.site-footer__index-list a { text-transform: none; }
.site-footer__index-list a:hover { color: var(--accent); }
.site-footer__index-places { margin: 0 0 var(--pad); }

/* --- WhatsApp -------------------------------------------------------------
   One fixed button on every page, and a solid link beside each "start a
   conversation". The fixed one sits clear of the link row's corner and
   hides while printing. */
.whatsapp-button {
	position: fixed;
	right: var(--pad);
	bottom: var(--pad);
	z-index: 40;
	display: inline-flex;
	align-items: center;
	gap: 0.6em;
	padding: 0.55em 0.9em;
	background: var(--accent);
	color: var(--white);
	border-radius: var(--border-radius-text);
	text-decoration: none;
	transition: transform 0.3s var(--ease-out-expo), background 0.3s var(--ease-out-expo);
}
.whatsapp-button:hover,
.whatsapp-button:focus-visible { color: var(--white); background: #000; box-shadow: 0 0 0 var(--border-width) var(--white); transform: translateY(-2px); }
.whatsapp-button__icon { display: block; width: 1.15em; height: 1.15em; fill: currentColor; }
@keyframes whatsapp-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
.rtl .whatsapp-button,
[dir="rtl"] .whatsapp-button { right: auto; left: var(--pad); }
@media print { .whatsapp-button { display: none; } }

.service-hero__cta { display: flex; flex-wrap: wrap; gap: var(--gap) var(--pad); align-items: center; }
.service-hero__check { grid-column: 1 / span 6; margin: var(--gap) 0 0; }
.service-hero__check a { text-transform: none; color: var(--foreground-dim); }
.service-hero__check a:hover { color: var(--accent); }
.service-invite__cta { display: flex; flex-wrap: wrap; gap: var(--gap) var(--pad); align-items: center; }
.contact-fallback__actions { display: flex; flex-wrap: wrap; gap: var(--gap) var(--pad); align-items: center; margin: var(--pad) 0 0; }

/* --- Site check ------------------------------------------------------------
   The form on the left, the result on the right; one column on a phone. */
.site-check {
	display: grid;
	grid-template-columns: repeat(12, 1fr);
	gap: var(--gap);
	padding: 0 var(--pad) 8vh;
}
.site-check__form { grid-column: 1 / span 5; display: grid; gap: var(--pad); align-content: start; }
.site-check__result,
.site-check__off { grid-column: 7 / span 6; display: grid; gap: var(--pad); align-content: start; padding: var(--pad); border: var(--border-width) solid var(--foreground-dimmed); border-radius: var(--border-radius-text); }
.site-check__off > p { margin: 0; }
.site-check [hidden] { display: none !important; }
.site-check__field { display: grid; gap: 0.5em; margin: 0; }
.site-check__field input {
	width: 100%;
	padding: 0.6em 0;
	background: transparent;
	border: 0;
	border-bottom: var(--border-width) solid var(--foreground-dimmed);
	border-radius: 0;
	font-family: var(--font-mono);
	font-size: var(--font-sd);
	color: var(--foreground);
	outline: none;
}
.site-check__field input:focus-visible { border-bottom-color: var(--accent); }
.site-check__field input::placeholder { color: var(--foreground-dimmed); }
.site-check__trap { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } /* clipped in place, not pushed off-canvas: a negative inset widened the RTL page */
.site-check__consent { margin: 0; }
.site-check__consent label { display: flex; gap: 0.7em; align-items: flex-start; font-family: var(--font-mono); font-size: var(--font-sm); font-variation-settings: "wght" var(--wght-light); line-height: 1.45; color: var(--foreground-dim); cursor: pointer; }
.site-check__consent input { flex: none; width: 1.15rem; height: 1.15rem; margin: 0.15em 0 0; accent-color: var(--accent); }
.site-check__submit { display: flex; flex-wrap: wrap; gap: var(--gap) var(--pad); align-items: center; margin: 0; }
.site-check__submit button { border: 0; cursor: pointer; font-size: inherit; line-height: inherit; }
.site-check__submit button:disabled { opacity: 0.5; cursor: wait; }
.site-check__note { color: var(--foreground-dim); }
.site-check__error { margin: 0; font-family: var(--font-mono); font-size: var(--font-sm); color: var(--accent); }
.site-check__status { margin: 0; color: var(--foreground-dim); min-height: 1.4em; }
.site-check__status.is-busy::after { content: "…"; animation: whatsapp-pulse 1.2s ease-in-out infinite; }
.site-check__scores { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--gap); }
.site-check__scores > div { display: grid; gap: 0.5em; min-width: 0; }
.site-check__scores .dot-text { width: auto; max-width: 100%; height: 1.6em; }
.site-check__facts { margin: 0; display: grid; gap: 0.4em; }
.site-check__facts > div { display: grid; grid-template-columns: 1fr auto; gap: var(--gap); padding: 0.4em 0; border-top: var(--border-width) solid var(--foreground-dimmed); }
.site-check__facts dt { color: var(--foreground-dim); }
.site-check__facts dd { margin: 0; font-family: var(--font-mono); font-size: var(--font-sm); color: var(--foreground); }
.site-check__findings > p { margin: 0 0 0.6em; }
.site-check__findings ol { margin: 0; padding: 0; list-style: none; display: grid; gap: 0.5em; }
.site-check__findings li { display: grid; grid-template-columns: auto 1fr; gap: var(--gap); align-items: baseline; font-family: var(--font-mono); font-size: var(--font-sm); font-variation-settings: "wght" var(--wght-light); line-height: 1.45; color: var(--foreground); }
.site-check__sev { color: var(--foreground-dim); }
.site-check__sev--high { color: var(--accent); }
.site-check__more { color: var(--foreground-dim); }
.site-check__next { display: flex; flex-wrap: wrap; gap: var(--gap) var(--pad); align-items: center; margin: 0; }

.site-check-list,
.site-check-steps { padding: 8vh var(--pad); border-top: var(--border-width) solid var(--foreground-dimmed); }
.site-check-list > h2,
.site-check-steps > h2 { margin: 0 0 var(--pad); }
.site-check-list__items { margin: 0; padding: 0; list-style: none; display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--pad) var(--gap); }
.site-check-list__items li { display: grid; gap: 0.5em; align-content: start; padding-top: var(--gap); border-top: var(--border-width) solid var(--foreground-dimmed); }
.site-check-list__items h3 { margin: 0; font-family: var(--font-sans); font-size: var(--font-md); line-height: 1.1; color: var(--foreground); }
.site-check-list__items p { margin: 0; font-family: var(--font-mono); font-size: var(--font-sm); font-variation-settings: "wght" var(--wght-light); line-height: 1.45; color: var(--foreground-dim); }
.site-check-list__index { color: var(--foreground-dim); }
.site-check-steps__list { grid-template-columns: repeat(3, 1fr); margin: 0; }
.site-check-steps__list p { margin: 0; font-family: var(--font-mono); font-size: var(--font-sm); font-variation-settings: "wght" var(--wght-light); line-height: 1.45; color: var(--foreground-dim); }

@media (max-width: 1024px) {
	.site-check__form { grid-column: 1 / span 6; }
	.site-check__result, .site-check__off { grid-column: 7 / span 6; }
	.site-check-list__items { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 768px) {
	.site-check__form, .site-check__result, .site-check__off { grid-column: 1 / -1; }
	.site-check__scores { grid-template-columns: repeat(2, 1fr); }
	.site-check-list__items, .site-check-steps__list { grid-template-columns: 1fr; }
	.whatsapp-button__label { display: none; }
	.whatsapp-button { padding: 0.7em; }
	.whatsapp-button__icon { width: 1.4em; height: 1.4em; }
}
