/* =============================================================================
   YOU x ONE Properties — système de design
   -----------------------------------------------------------------------------
   Toutes les cotes viennent du Figma (frame source 540 pt), ramenées à une
   maquette de référence de 1440 px : facteur 8/3.

   L'unité --u vaut « 1 pixel de la maquette ». En desktop elle est exprimée en
   vw, donc la mise en page se dilate proportionnellement sans plafond de
   largeur : une cote Figma de 163 px s'écrit calc(163 * var(--u)) et reste
   juste à 1280, 1440, 1920 ou 2560 px de large.

   La typographie, elle, ne suit PAS --u : la maquette d'origine descend à
   ~10 px de corps de texte, illisible sur écran. Elle a sa propre échelle
   clamp(), calée sur la hiérarchie et l'interlettrage de Figma.
   ============================================================================= */

:root {
	/* 1 px maquette = 1/14,4 vw (1440 px de référence).
	   Attention : `vw` inclut la largeur de la barre de défilement. Sur une
	   fenêtre dont la zone de contenu fait 1440 px, --u vaudrait donc 1,0104 px
	   et toute la grille dériverait de 1 % — la gouttière et les images
	   grossiraient, et les colonnes en `1fr` absorberaient l'écart en
	   rétrécissant. La valeur est recalculée plus bas sur la largeur réelle du
	   contenu ; celle-ci ne sert que de repli et suffit pour l'en-tête, le menu
	   et le pied de page, dont la mise en page ne dépend pas de cotes fines. */
	--u: 0.069444vw;

	/* Grille relevée dans Figma. */
	--yxo-gutter: calc(163 * var(--u)); /* marge latérale */
	--yxo-content: calc(1114 * var(--u)); /* colonne de contenu */
	--yxo-gap: calc(44 * var(--u)); /* gouttière image/texte */
	--yxo-hero-h: calc(702 * var(--u));
	--yxo-header-h: calc(84 * var(--u));

	/* Échelle typographique — indépendante de --u pour rester lisible. */
	--fs-eyebrow: clamp(0.625rem, 0.55rem + 0.35vw, 0.8125rem);
	--fs-body-s: clamp(0.8125rem, 0.78rem + 0.2vw, 0.9375rem);
	--fs-body: clamp(0.9375rem, 0.88rem + 0.3vw, 1.0625rem);
	--fs-h4: clamp(1.125rem, 1rem + 0.6vw, 1.5rem);
	--fs-h3: clamp(1.375rem, 1.1rem + 1.1vw, 2rem);
	--fs-h2: clamp(1.625rem, 1.15rem + 1.9vw, 2.75rem);
	/* Le plancher est calé pour que « YOUXONE PROPERTIES » tienne sur une
	   ligne dans 390 px de large, gouttières déduites. */
	--fs-h1: clamp(1.625rem, 1.05rem + 2.6vw, 3.75rem);
	--fs-stat: clamp(2rem, 1.4rem + 2.4vw, 3.25rem);

	--ls-eyebrow: 0.22em;
	--ls-display: 0.06em;
	--ls-wide: 0.12em;

	/* Courbe à forte décélération : le mouvement part vite et se pose lentement,
	   ce qui donne aux éléments une impression de masse plutôt que de glissement
	   mécanique. Une courbe `ease-in-out` produit l'effet inverse. */
	--ease: cubic-bezier(0.32, 0.72, 0, 1);
	--t-base: 620ms var(--ease);
	--t-fast: 260ms var(--ease);

	/* Balayage des boutons. Volontairement long : le remplissage traverse le
	   bouton et doit se suivre à l'œil, pas se deviner. Le libellé change de
	   teinte sur la même durée, sans quoi il virerait avant que le remplissage
	   ne l'ait rejoint et les dernières lettres se perdraient un instant sur le
	   fond resté sombre. */
	--t-balayage: 1420ms var(--ease);
}

/* Recalcul de --u sur la largeur réelle de la zone de contenu, barre de
   défilement exclue : 100cqw vaut la largeur du conteneur, pas celle de la
   fenêtre. Les cotes du Figma tombent alors au pixel près.
   `.entry-content` n'héberge aucun élément en position fixe, la mise en
   conteneur est donc sans effet de bord. */
@supports (container-type: inline-size) {
	.entry-content {
		container-type: inline-size;
	}

	/* Uniquement au-dessus de 1024 px : en dessous, --u est figé à 1 px et la
	   mise en page suit une grille mobile dédiée. */
	@media (min-width: 1024px) {
		.entry-content > * {
			--u: calc(100cqw / 1440);

			/* Les variables dérivées sont déclarées sur :root, où var(--u)
			   résout la valeur en vw. Il faut donc les recalculer ici pour
			   qu'elles suivent le --u corrigé. */
			--yxo-gutter: calc(163 * var(--u));
			--yxo-content: calc(1114 * var(--u));
			--yxo-gap: calc(44 * var(--u));
			--yxo-hero-h: calc(702 * var(--u));
			--yxo-header-h: calc(84 * var(--u));
		}
	}
}

/* Sous 1024 px on quitte la grille de la maquette : --u se fige et la mise en
   page passe en colonne unique avec des marges au pouce. */
@media (max-width: 1023px) {
	:root {
		--u: 1px;
		--yxo-gutter: clamp(20px, 5.5vw, 48px);
		--yxo-content: 100%;
		--yxo-gap: clamp(24px, 5vw, 40px);
		/* Borné : sur une tablette en portrait, 78svh laisserait un héros
		   quasi vide de plus de 800 px de haut. */
		--yxo-hero-h: clamp(460px, 78svh, 720px);
		--yxo-header-h: 64px;
	}
}

@media (prefers-reduced-motion: reduce) {
	:root {
		--t-base: 1ms;
		--t-fast: 1ms;
	}
}

/* ---------------------------------------------------------------- Base --- */

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

/* Garde-fou anti-débordement horizontal posé sur la racine, et non sur body :
   lorsqu'un axe vaut `clip` et l'autre `visible`, ce dernier est recalculé en
   `auto` et l'élément devient un conteneur de défilement. Sur `body`, cela
   détournait les animations liées au défilement, qui s'accrochaient à lui
   plutôt qu'à la fenêtre et ne progressaient jamais. */
html {
	scroll-behavior: smooth;
	-webkit-text-size-adjust: 100%;
	overflow-x: clip;
}

body {
	margin: 0;
	font-size: var(--fs-body);
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
	/* Ligatures et chiffres proportionnels : rendu plus soigné sur les titres
	   très interlettrés de la charte. */
	font-kerning: normal;
	font-variant-ligatures: common-ligatures contextual;
	font-optical-sizing: auto;
}

/* Grain de film. Sur un projet immobilier haut de gamme, cette texture très
   discrète casse le côté « aplat numérique » des grandes zones de couleur.
   Couche fixe et non cliquable : elle ne participe ni à la mise en page ni au
   défilement, donc aucun coût de recalcul. Pour la retirer, supprimer ce bloc. */
body::after {
	content: "";
	position: fixed;
	inset: 0;
	z-index: 200;
	pointer-events: none;
	opacity: 0.035;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

@media print {
	body::after {
		display: none;
	}
}

/* Sélection et barre de défilement aux couleurs de la charte. */
::selection {
	background: var(--wp--preset--color--sand);
	color: var(--wp--preset--color--wine);
}

@supports (scrollbar-color: auto) {
	html {
		scrollbar-color: var(--wp--preset--color--taupe) transparent;
		scrollbar-width: thin;
	}
}

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

:where(a):focus-visible,
:where(button):focus-visible,
:where(input, textarea, select):focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 3px;
}

/* Lien d'évitement clavier. */
.yxo-skip {
	position: absolute;
	left: -9999px;
	top: 0;
	z-index: 999;
	padding: 0.75em 1.25em;
	background: var(--wp--preset--color--wine);
	color: var(--wp--preset--color--sand);
	font-size: var(--fs-body-s);
}
.yxo-skip:focus {
	left: 0;
}

/* -------------------------------------------------------- Typographie --- */

.yxo-eyebrow,
.is-style-yxo-eyebrow {
	font-size: var(--fs-eyebrow);
	font-weight: 500;
	letter-spacing: var(--ls-eyebrow);
	text-transform: uppercase;
	line-height: 1.4;
	margin: 0;
}

/* Titres de section : graisse légère + interlettrage large, comme la maquette. */
.yxo-title {
	font-size: var(--fs-h2);
	font-weight: 300;
	letter-spacing: var(--ls-display);
	line-height: 1.12;
	margin: 0;
	text-transform: uppercase;
}

.yxo-lede {
	font-size: var(--fs-body);
	line-height: 1.75;
	margin: 0;
}

.yxo-meta {
	font-size: var(--fs-eyebrow);
	letter-spacing: var(--ls-wide);
	text-transform: uppercase;
	opacity: 0.85;
	margin: 0;
}

/* Le « x » de YOUxONE reste en bas de casse. Titres, sur-titres et intitulés
   passent en capitales par la feuille de style : sans cette règle, la marque
   se lirait YOUXONE partout. `text-transform` posé sur l'élément l'emporte sur
   celui hérité du parent. */
.yxo-x {
	text-transform: lowercase;
	/* Le x est aussi centré sur la hauteur des capitales qui l'entourent, comme
	   dans le logotype. Posé sur la ligne de base il tombait trop bas : en
	   Gilroy une capitale monte à 0,719 em et un x à 0,498 em, il manquait donc
	   la moitié de l'écart, soit 0,11 em. La valeur retenue est celle relevée au
	   pixel sur le titre du héros une fois rendu — les métriques de la police
	   sont arrondies à l'entier et menaient un demi-pixel trop haut. En em, elle
	   suit la taille du texte, du héros au copyright du pied. */
	position: relative;
	top: -0.104em;
}

/* Réservé aux lecteurs d'écran (et aux libellés d'onglets lus par le JS). */
.yxo-sr {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* ------------------------------------------------------------ Boutons --- */

.yxo-btn,
.wp-block-button.is-style-yxo-arrow .wp-block-button__link {
	display: inline-flex;
	align-items: center;
	gap: 0.9em;
	padding: 1.05em 2.1em;
	border: 1px solid currentColor;
	background: none;
	color: inherit;
	font-size: var(--fs-eyebrow);
	font-weight: 500;
	letter-spacing: var(--ls-eyebrow);
	text-transform: uppercase;
	text-decoration: none;
	cursor: pointer;
	transition: background-color var(--t-fast), color var(--t-fast);
}

/* Chevron et non plus filet : la maquette montre une petite marque compacte
   après le libellé, pas une barre. Dessiné en bordures plutôt qu'en caractère,
   pour ne dépendre d'aucune fonte. Il avance d'un cheveu au survol —
   `transform` seule est animée, la géométrie ne bouge pas. */
.yxo-btn::after,
.wp-block-button.is-style-yxo-arrow .wp-block-button__link::after {
	content: "";
	width: 0.4em;
	height: 0.4em;
	border-top: 1.5px solid currentColor;
	border-right: 1.5px solid currentColor;
	transform: rotate(45deg);
	transition: transform var(--t-fast);
}

.yxo-btn:hover::after,
.wp-block-button.is-style-yxo-arrow .wp-block-button__link:hover::after {
	transform: rotate(45deg) translate(0.12em, -0.12em);
}

/* Enfoncement au clic : un léger retrait donne au bouton une réponse
   physique, là où un simple changement de couleur reste inerte.
   Seule `transform` est animée, jamais la géométrie. */
.yxo-btn:active,
.wp-block-button.is-style-yxo-arrow .wp-block-button__link:active,
.yxo-form__submit:active,
.yxo-totop:active {
	transform: scale(0.985);
}

.yxo-totop {
	transition:
		background-color var(--t-fast),
		color var(--t-fast),
		transform var(--t-fast);
}

/* Le libellé change de teinte au rythme du balayage, pas à celui d'un survol
   ordinaire. */
.yxo-btn,
.wp-block-button.is-style-yxo-arrow .wp-block-button__link {
	transition:
		color var(--t-balayage),
		transform var(--t-fast);
}

/* Au survol le bouton se remplit de la couleur de son propre filet, et le
   libellé passe au bordeaux. La couleur du filet est tenue dans une variable :
   `currentColor` ne conviendrait pas ici, puisqu'il se résoudrait sur la
   nouvelle couleur de texte au moment même où on la change. */
.yxo-btn,
.wp-block-button.is-style-yxo-arrow .wp-block-button__link {
	--yxo-btn-line: var(--wp--preset--color--sand);
	--yxo-btn-ink: var(--wp--preset--color--wine);
}

/* Sur fond clair le filet est brun : le bordeaux par-dessus donnerait deux
   bruns sombres, contraste 1,3 pour 1, illisible. Le libellé passe donc au
   sable. Le rapport de couleurs s'inverse, la lisibilité est conservée. */
.yxo-on-light .yxo-btn,
.yxo-on-light .wp-block-button.is-style-yxo-arrow .wp-block-button__link {
	--yxo-btn-line: var(--wp--preset--color--brown);
	--yxo-btn-ink: var(--wp--preset--color--sand);
}

.yxo-btn:hover,
.wp-block-button.is-style-yxo-arrow .wp-block-button__link:hover {
	color: var(--yxo-btn-ink);
}

/* Le remplissage balaie le bouton de la gauche vers la droite au lieu
   d'apparaître d'un bloc. Il est porté par un calque à part, mis à l'échelle
   depuis son bord gauche : `transform` s'anime sur le compositeur, là où une
   largeur ou un dégradé forceraient un recalcul à chaque image.
   `isolation` crée le contexte d'empilement qui retient le `z-index: -1` à
   l'intérieur du bouton — sans elle le calque passerait derrière le fond de la
   section. `overflow` le taille au trait du bouton.

   L'origine reste au bord gauche dans les deux temps : le calque grandit vers
   la droite à l'arrivée du curseur, et se rétracte vers la gauche à son
   départ. Le remplissage revient donc sur ses pas, de droite à gauche. */
.yxo-btn,
.wp-block-button.is-style-yxo-arrow .wp-block-button__link {
	position: relative;
	overflow: hidden;
	isolation: isolate;
}

.yxo-btn::before,
.wp-block-button.is-style-yxo-arrow .wp-block-button__link::before {
	content: "";
	position: absolute;
	z-index: -1;
	inset: 0;
	background: var(--yxo-btn-line);
	transform: scaleX(0);
	transform-origin: left center;
	transition: transform var(--t-balayage);
}

.yxo-btn:hover::before,
.wp-block-button.is-style-yxo-arrow .wp-block-button__link:hover::before {
	transform: scaleX(1);
}

/* ----------------------------------------------------------- Sections --- */

/* Dans la maquette les sections sont jointives, bord à bord. WordPress
   applique pourtant son écart de bloc global entre les éléments de premier
   niveau (en-tête, contenu, pied de page) et entre les sections du contenu,
   ce qui glissait une bande de 24 px entre chacune. On le neutralise à ce
   seul niveau : chaque section porte son propre rythme vertical, et l'écart
   de bloc reste disponible à l'intérieur des sections. */
.wp-site-blocks > *,
.entry-content > * {
	margin-block-start: 0;
}

/* L'écart de bloc étant nul (voir theme.json), on rétablit celui dont les
   blocs natifs ont réellement besoin : l'espace entre plusieurs boutons. */
.wp-block-buttons {
	gap: calc(16 * var(--u));
}

/* Les visuels de contenu sont des blocs Image natifs : le client les remplace
   depuis la médiathèque. La figure que WordPress ajoute autour ne doit ni
   introduire de marge ni empêcher l'image de remplir son cadre. */
.yxo-split__media > .wp-block-image,
.yxo-founder__portrait > .wp-block-image,
.yxo-band > .wp-block-image,
.yxo-card__media > .wp-block-image {
	margin: 0;
	height: 100%;
}

/* Toutes les sections sont full-bleed ; seule la grille interne est bornée. */
.yxo-section {
	position: relative;
	width: 100%;
	padding-inline: var(--yxo-gutter);
}

.yxo-section--flush {
	padding-inline: 0;
}

.yxo-shell {
	width: 100%;
	max-width: var(--yxo-content);
	margin-inline: auto;
}

.yxo-on-dark {
	background: var(--wp--preset--color--wine);
	color: var(--wp--preset--color--sand);
}

.yxo-on-sand {
	background: var(--wp--preset--color--sand);
	color: var(--wp--preset--color--wine);
}

.yxo-on-light {
	background: var(--wp--preset--color--white);
	color: var(--wp--preset--color--brown);
}

/* ------------------------------------------------------------ En-tête --- */

.yxo-header {
	position: fixed;
	inset: 0 0 auto 0;
	z-index: 100;
	display: grid;
	grid-template-columns: 1fr auto 1fr;
	align-items: center;
	gap: var(--yxo-gap);
	min-height: var(--yxo-header-h);
	padding-inline: calc(20 * var(--u));
	color: var(--wp--preset--color--sand);
	transition: background-color var(--t-base), backdrop-filter var(--t-base);
}

/* Fond appliqué au scroll par le JS, pour rester lisible sur fond clair. */
.yxo-header.is-stuck {
	background: color-mix(in srgb, var(--wp--preset--color--wine) 92%, transparent);
	backdrop-filter: blur(10px);
}

.yxo-header__mark svg,
.yxo-header__mark img {
	width: calc(80 * var(--u));
	min-width: 48px;
	height: auto;
}

.yxo-header__logo {
	justify-self: center;
}

.yxo-header__logo svg,
.yxo-header__logo img {
	width: calc(150 * var(--u));
	min-width: 104px;
	height: auto;
}

.yxo-header__end {
	display: flex;
	align-items: center;
	justify-content: flex-end;
	gap: calc(18 * var(--u));
}

/* Sélecteur de langue. Une seule langue : simple mention. Plusieurs langues
   (Polylang actif) : menu déroulant accessible au clavier. */
.yxo-lang {
	position: relative;
	display: inline-flex;
	align-items: center;
	font-size: var(--fs-eyebrow);
	letter-spacing: var(--ls-wide);
	text-transform: uppercase;
	color: inherit;
}

.yxo-lang__toggle {
	display: inline-flex;
	align-items: center;
	gap: 0.45em;
	padding: 0.35em 0;
	border: 0;
	background: none;
	color: inherit;
	font: inherit;
	letter-spacing: inherit;
	text-transform: inherit;
	cursor: pointer;
}

/* Chevron, qui pivote à l'ouverture. */
.yxo-lang__toggle::after {
	content: "";
	width: 0.34em;
	height: 0.34em;
	border-right: 1px solid currentColor;
	border-bottom: 1px solid currentColor;
	transform: translateY(-0.12em) rotate(45deg);
	transition: transform var(--t-fast);
}

.yxo-lang__toggle[aria-expanded="true"]::after {
	transform: translateY(0.08em) rotate(-135deg);
}

.yxo-lang__list {
	position: absolute;
	top: calc(100% + 0.7em);
	right: 0;
	z-index: 2;
	min-width: max-content;
	margin: 0;
	padding: 0.4em 0;
	list-style: none;
	background: color-mix(in srgb, var(--wp--preset--color--wine) 96%, transparent);
	border: 1px solid color-mix(in srgb, var(--wp--preset--color--sand) 22%, transparent);
	opacity: 0;
	visibility: hidden;
	transform: translateY(calc(-6 * var(--u)));
	transition:
		opacity var(--t-fast),
		transform var(--t-fast),
		visibility var(--t-fast);
}

.yxo-lang__toggle[aria-expanded="true"] + .yxo-lang__list {
	opacity: 1;
	visibility: visible;
	transform: none;
}

/* Sans JavaScript, la liste reste consultable au survol et au clavier plutôt
   que d'être inaccessible. */
html:not(.yxo-js) .yxo-lang:hover .yxo-lang__list,
html:not(.yxo-js) .yxo-lang:focus-within .yxo-lang__list {
	opacity: 1;
	visibility: visible;
	transform: none;
}

.yxo-lang__list a {
	display: flex;
	align-items: baseline;
	gap: 0.7em;
	padding: 0.5em 1.1em;
	color: inherit;
	text-decoration: none;
	white-space: nowrap;
	transition: background-color var(--t-fast);
}

.yxo-lang__list a:hover {
	background: color-mix(in srgb, var(--wp--preset--color--sand) 14%, transparent);
}

.yxo-lang__list a[aria-current] .yxo-lang__code {
	text-decoration: underline;
	text-underline-offset: 0.3em;
}

.yxo-lang__name {
	font-size: 0.92em;
	text-transform: none;
	letter-spacing: 0.04em;
	opacity: 0.65;
}

/* Bouton menu : trois filets, comme le rond hamburger de la maquette.
   Flex plutôt que grid : les lignes d'une grille auto s'étirent pour remplir
   la hauteur du bouton, ce qui rend le pas entre les traits imprévisible et
   empêche la croix de se former à l'ouverture. Ici le pas vaut exactement
   1 px de trait + 4 px de gap. */
.yxo-burger {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 4px;
	width: calc(40 * var(--u));
	height: calc(40 * var(--u));
	min-width: 38px;
	min-height: 38px;
	padding: 0;
	border: 1px solid currentColor;
	border-radius: 50%;
	background: none;
	color: inherit;
	cursor: pointer;
}

.yxo-burger span {
	display: block;
	width: calc(15 * var(--u));
	min-width: 15px;
	height: 1px;
	margin: 0;
	background: currentColor;
	transition: transform var(--t-fast), opacity var(--t-fast);
}

/* À l'ouverture, les trois traits forment une croix. Le décalage doit valoir
   le pas réel entre les traits — hauteur 1 px + 2 px de marge de chaque côté,
   soit 5 px — sinon ils ne se superposent pas et dessinent un chevron. */
.yxo-burger[aria-expanded="true"] span:nth-child(1) {
	transform: translateY(5px) rotate(45deg);
}
.yxo-burger[aria-expanded="true"] span:nth-child(2) {
	opacity: 0;
}
.yxo-burger[aria-expanded="true"] span:nth-child(3) {
	transform: translateY(-5px) rotate(-45deg);
}

/* ----------------------------------------- Menu plein écran (TOP BAR) --- */

/* Cotes relevées dans « YOUXONE TOP BAR.pdf », rendu exactement à 1440 px :
   nav à 41 px du bord gauche et centrée verticalement, interligne 41 px,
   sous-niveau plus petit, bloc de contact aligné à droite à 65 px du bord
   et à 92 px du bas. */
.yxo-nav {
	position: fixed;
	inset: 0;
	z-index: 99;
	overflow: hidden;
	color: var(--wp--preset--color--sand);
	opacity: 0;
	visibility: hidden;
	transition: opacity var(--t-base), visibility var(--t-base);
}

.yxo-nav[data-open="true"] {
	opacity: 1;
	visibility: visible;
}

.yxo-nav__media {
	position: absolute;
	inset: 0;
}

.yxo-nav__media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.yxo-nav__veil {
	position: absolute;
	inset: 0;
	background:
		linear-gradient(
			to right,
			color-mix(in srgb, var(--wp--preset--color--ink) 72%, transparent) 0%,
			color-mix(in srgb, var(--wp--preset--color--ink) 38%, transparent) 55%,
			color-mix(in srgb, var(--wp--preset--color--ink) 52%, transparent) 100%
		),
		color-mix(in srgb, var(--wp--preset--color--wine) 74%, transparent);
}

/* Nav : calée à gauche, centrée dans la hauteur de la fenêtre. */
.yxo-nav__panel {
	position: absolute;
	left: calc(41 * var(--u));
	top: 50%;
	translate: 0 -50%;
}

/* Ouverture en cascade : les entrées ne surgissent pas d'un bloc, elles
   montent l'une après l'autre depuis une position basse. Le décalage se fait
   par `transition-delay`, donc sans JavaScript et sans coût de mise en page. */
.yxo-nav__item,
.yxo-nav__contact > * {
	opacity: 0;
	transform: translateY(calc(26 * var(--u)));
	transition:
		opacity 620ms var(--ease),
		transform 620ms var(--ease);
}

.yxo-nav[data-open="true"] .yxo-nav__item,
.yxo-nav[data-open="true"] .yxo-nav__contact > * {
	opacity: 1;
	transform: none;
}

.yxo-nav[data-open="true"] .yxo-nav__item:nth-child(1) { transition-delay: 90ms; }
.yxo-nav[data-open="true"] .yxo-nav__item:nth-child(2) { transition-delay: 160ms; }
.yxo-nav[data-open="true"] .yxo-nav__item:nth-child(3) { transition-delay: 230ms; }
.yxo-nav[data-open="true"] .yxo-nav__item:nth-child(4) { transition-delay: 300ms; }
.yxo-nav[data-open="true"] .yxo-nav__contact > *:nth-child(1) { transition-delay: 260ms; }
.yxo-nav[data-open="true"] .yxo-nav__contact > *:nth-child(2) { transition-delay: 300ms; }
.yxo-nav[data-open="true"] .yxo-nav__contact > *:nth-child(3) { transition-delay: 340ms; }
.yxo-nav[data-open="true"] .yxo-nav__contact > *:nth-child(4) { transition-delay: 380ms; }
.yxo-nav[data-open="true"] .yxo-nav__contact > *:nth-child(5) { transition-delay: 420ms; }

/* La cascade est un agrément : si le mouvement est réduit, ou si le panneau
   n'a pas d'état ouvert (rendu sans JavaScript), tout reste affiché. */
@media (prefers-reduced-motion: reduce) {
	.yxo-nav__item,
	.yxo-nav__contact > * {
		opacity: 1;
		transform: none;
		transition: none;
	}
}

html:not(.yxo-js) .yxo-nav__item,
html:not(.yxo-js) .yxo-nav__contact > * {
	opacity: 1;
	transform: none;
}

.yxo-nav__list,
.yxo-nav__sub {
	margin: 0;
	padding: 0;
	list-style: none;
}

/* Pas de 41 px relevé dans la maquette, entre sommets de capitales :
   41 − 20 px de corps = 21 px de marge. */
.yxo-nav__item + .yxo-nav__item {
	margin-top: calc(21 * var(--u));
}

.yxo-nav__link {
	display: inline-block;
	color: inherit;
	font-size: clamp(1.0625rem, 0.85rem + 0.7vw, 1.25rem);
	font-weight: 300;
	letter-spacing: 0.1em;
	line-height: 1;
	text-transform: uppercase;
	text-decoration: none;
	transition: opacity var(--t-fast);
}

/* Sous-niveau : « KARENGA » sous « PROJECTS », plus petit et discret.
   Replié par défaut dès que le script a pris la main, déplié sinon.

   Le dépliage interpole `grid-template-rows` de 0fr à 1fr plutôt qu'une
   hauteur maximale arbitraire : ajouter un quatrième projet dans le menu ne
   casse donc pas l'animation et ne tronque rien. */
.yxo-nav__subwrap {
	display: grid;
	grid-template-rows: 1fr;
	margin-top: calc(22 * var(--u));
	opacity: 1;
	transition:
		grid-template-rows 420ms var(--ease),
		opacity 300ms var(--ease),
		margin-top 420ms var(--ease);
}

.yxo-nav__subwrap > .yxo-nav__sub {
	min-height: 0;
	overflow: hidden;
}

.yxo-nav__item--parent[data-open="false"] .yxo-nav__subwrap {
	grid-template-rows: 0fr;
	opacity: 0;
	margin-top: 0;
}

.yxo-nav__subwrap + .yxo-nav__item,
.yxo-nav__item:has(.yxo-nav__subwrap) + .yxo-nav__item {
	margin-top: calc(22 * var(--u));
}

/* Pas d'indicateur visuel sur l'entrée qui porte un sous-menu : la maquette
   n'en montre aucun. L'état reste porté par `aria-expanded`, donc restitué
   aux lecteurs d'écran, et par le dépliage lui-même. */

.yxo-nav__sublink {
	display: inline-block;
	color: inherit;
	font-size: var(--fs-eyebrow);
	font-weight: 400;
	letter-spacing: var(--ls-wide);
	line-height: 1;
	text-transform: uppercase;
	text-decoration: none;
	opacity: 0.75;
	transition: opacity var(--t-fast);
}

.yxo-nav__link:hover,
.yxo-nav__sublink:hover,
.yxo-nav__link[aria-current],
.yxo-nav__sublink[aria-current] {
	opacity: 1;
	text-decoration: underline;
	text-underline-offset: 0.35em;
	text-decoration-thickness: 1px;
}

.yxo-nav__link:hover {
	opacity: 0.7;
	text-decoration: none;
}

/* Coordonnées, alignées à droite en bas de fenêtre. */
.yxo-nav__contact {
	position: absolute;
	right: calc(65 * var(--u));
	bottom: calc(92 * var(--u));
	text-align: right;
	font-size: var(--fs-eyebrow);
	line-height: 1.9;
}

.yxo-nav__contact p {
	margin: 0;
}

.yxo-nav__contact a {
	color: inherit;
	text-decoration: none;
}

.yxo-nav__contact a:hover {
	text-decoration: underline;
	text-underline-offset: 0.3em;
}

/* Sélecteurs portés au même niveau de spécificité que `.yxo-nav__contact p`,
   plutôt que forcés par !important. */
.yxo-nav__contact .yxo-nav__brand {
	letter-spacing: var(--ls-wide);
	text-transform: uppercase;
	margin-bottom: calc(6 * var(--u));
}

/* Icônes et mention légale sur une même ligne, les icônes tenant le bord
   gauche et la mention le bord droit — c'est ce que fait la maquette, et ça
   donne au bloc une assise que l'empilement précédent n'avait pas. */
.yxo-nav__meta {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: calc(24 * var(--u));
	margin-top: calc(16 * var(--u));
}

.yxo-nav__social {
	display: flex;
	gap: calc(8 * var(--u));
}

/* Disques pleins, pas cercles vides. Les états sont inversés par rapport à
   l'usage courant : le disque est plein au repos, et c'est au survol que la
   couleur se retire — la valeur atténuée (sable 55 %) est celle relevée sur
   YOUXONE TOP BAR.pdf.

   Le fichier livré évide le glyphe au lieu de le peindre : ici le menu
   s'ouvre sur une photo voilée, et le glyphe laisserait voir l'image. Le lien
   pose donc un disque bordeaux derrière, que le creux du tracé révèle. */
.yxo-nav__social a {
	display: block;
	width: calc(1.7em + 1px);
	height: calc(1.7em + 1px);
	border: 0;
	border-radius: 50%;
	background: var(--wp--preset--color--wine);
	color: var(--wp--preset--color--sand);
	transition: color var(--t-fast);
}

.yxo-nav__social a:hover {
	color: color-mix(in srgb, var(--wp--preset--color--sand) 55%, transparent);
}

.yxo-nav__social svg {
	display: block;
	width: 100%;
	height: 100%;
}

/* La mention est désormais dans la ligne : son décalage vertical d'antan
   la désalignerait des icônes. */
.yxo-nav__contact .yxo-nav__legal {
	margin: 0;
	opacity: 0.7;
}

/* Sous 1024 px la nav quitte le centrage vertical : elle se pose sous la
   barre et les coordonnées passent en pied de panneau, alignées à gauche,
   pour rester atteignables au pouce. */
@media (max-width: 1023px) {
	.yxo-nav__panel {
		left: var(--yxo-gutter);
		right: var(--yxo-gutter);
		top: calc(var(--yxo-header-h) + 8vh);
		translate: none;
	}

	.yxo-nav__item + .yxo-nav__item {
		margin-top: clamp(18px, 3.4vh, 30px);
	}

	.yxo-nav__contact {
		left: var(--yxo-gutter);
		right: var(--yxo-gutter);
		bottom: clamp(24px, 6vh, 56px);
		text-align: left;
	}

	.yxo-nav__social {
		justify-content: flex-start;
	}
}

/* Écran court (paysage sur téléphone) : le panneau devient défilable plutôt
   que de superposer la nav et les coordonnées. */
@media (max-height: 620px) and (max-width: 1023px) {
	.yxo-nav {
		overflow-y: auto;
	}

	.yxo-nav__panel,
	.yxo-nav__contact {
		position: static;
		translate: none;
		padding-inline: var(--yxo-gutter);
	}

	.yxo-nav__panel {
		padding-top: calc(var(--yxo-header-h) + 24px);
	}

	.yxo-nav__contact {
		padding-block: 32px 40px;
	}
}

/* -------------------------------------------------------------- Héros --- */

/* Figma : 1440x702, image plein cadre + voile bordeaux/#1B0000. */
.yxo-hero {
	position: relative;
	display: grid;
	align-content: center;
	justify-items: center;
	min-height: var(--yxo-hero-h);
	padding: var(--yxo-header-h) var(--yxo-gutter) calc(60 * var(--u));
	overflow: hidden;
	text-align: center;
	color: var(--wp--preset--color--sand);
	isolation: isolate;
}

.yxo-hero__media {
	position: absolute;
	inset: 0;
	z-index: -2;
}

/* Le bloc Image ajoute un <figure> autour du visuel : il doit remplir le
   cadre sans marge, sinon le héros se décale de la hauteur du bloc. */
.yxo-hero__media > .wp-block-image {
	margin: 0;
	height: 100%;
}

.yxo-hero__media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* Recadrage ancré en haut : le sujet du visuel est dans sa partie haute, que
   le cadrage centré coupait. */
.yxo-hero--focus-top .yxo-hero__media img {
	object-position: 50% 0;
}

/* Voile du héros : sans teinte par défaut. L'accueil est le seul héros qui ne
   nomme pas de variante, il s'affiche donc désormais sur l'image nue — le
   client ne veut plus de masque sur cette section. La règle reste en place :
   c'est elle qui pose le calque que les variantes colorent, et le balisage des
   pages contient déjà le `<div>`. */
.yxo-hero__veil {
	position: absolute;
	inset: 0;
	z-index: -1;
}

/* Masque brun chaud, plus léger que celui de l'accueil : il sert aux héros de
   Karenga et de Contact. La variante porte le nom de la teinte et non d'une
   page — elle en couvre deux, et rien n'interdit qu'une troisième s'y ajoute.
   Valeur hors charte, donc littérale plutôt qu'en jeton de palette. */
.yxo-hero--brun .yxo-hero__veil {
	background: color-mix(in srgb, #2A1C0E 50%, transparent);
}

.yxo-hero__eyebrow {
	font-size: clamp(0.9rem, 0.6rem + 1.3vw, 1.75rem);
	font-weight: 300;
	letter-spacing: 0.3em;
	text-transform: uppercase;
	margin: 0 0 calc(6 * var(--u));
}

.yxo-hero__title {
	font-size: var(--fs-h1);
	font-weight: 500;
	letter-spacing: 0.03em;
	line-height: 1.08;
	text-transform: uppercase;
	margin: 0;
	/* Garde-fou : un titre plus long que prévu passe à la ligne au lieu
	   de déborder de la fenêtre. */
	max-width: 100%;
	overflow-wrap: break-word;
	text-wrap: balance;
}

/* Logotype en lieu et place du titre, sur la page projet. */
.yxo-hero__logo {
	width: min(calc(430 * var(--u)), 78vw);
	margin: calc(6 * var(--u)) auto 0;
}

.yxo-hero__logo svg {
	width: 100%;
	height: auto;
}

/* La couleur est posée ici, et non laissée au fichier. Le tracé porte bien son
   #DFD1BB en attribut `style`, mais la duplication d'une page vers sa
   traduction passe le contenu par kses, qui supprime ces attributs : la copie
   anglaise en avait perdu 431 sur 431 et le logo sortait en noir. Une règle
   CSS survit à cette copie. */
.yxo-hero__logo svg path {
	fill: #DFD1BB;
}

/* Le bloc se cale sur la largeur du titre : tout ce qu'il contient peut alors
   être positionné en pourcentage de cette largeur, donc rester juste quand la
   police change. */
.yxo-hero__block {
	width: fit-content;
	max-width: 100%;
	margin-inline: auto;
}

/* Sans cette ligne, le bloc se dimensionnerait sur le sous-texte — dont la
   largeur « sur une seule ligne » est bien supérieure à celle du titre. Le
   titre, centré, se retrouvait alors plus étroit que sa boîte, et les
   pourcentages du sous-texte se calculaient sur la mauvaise référence : il
   débordait de 110 px au-delà du « S » final.
   Le confinement en ligne rend la largeur du sous-texte indépendante de son
   contenu : il ne participe plus au dimensionnement du bloc, qui se cale donc
   bien sur le titre seul. */
.yxo-hero__sub {
	contain: inline-size;
}

/* Le sur-titre et le titre sont centrés, mais le sous-texte ne l'est pas :
   dans la maquette il commence sous le « P » de PROPERTIES et se compose
   aligné à gauche, son bord droit retombant sur celui du titre.
   Pourcentages rapportés à la largeur du TEXTE du titre : le « P » se trouve
   à 46,1 % de cette largeur, et le « S » final en marque exactement la fin —
   d'où une largeur de 53,9 %, complément du retrait. Le sous-texte occupe
   ainsi rigoureusement la portion « PROPERTIES » du titre.
   À revérifier au passage à Gilroy, dont les proportions de casse diffèrent. */
.yxo-hero__sub {
	width: 53.9%;
	max-width: 100%;
	margin: calc(10 * var(--u)) 0 0 46.1%;
	font-size: var(--fs-body-s);
	/* Interlignage serré et graisse semi-grasse, comme la maquette : le rapport
	   relevé dans Figma est de 1,05, remonté ici à 1,4 pour rester lisible au
	   corps réellement affiché (la maquette descend à l'équivalent de 10 px). */
	font-weight: 500;
	line-height: 1.4;
	text-align: left;
	opacity: 0.92;
}

/* Le héros de la page projet affiche un logotype : sa ligne de localisation
   reste centrée sous le logo. */
.yxo-hero:has(.yxo-hero__logo) .yxo-hero__sub {
	width: auto;
	margin-inline: auto;
	text-align: center;
}

@media (max-width: 1023px) {
	.yxo-hero__block {
		width: 100%;
	}

	.yxo-hero__sub {
		width: 100%;
		margin-inline: auto;
		text-align: center;
	}
}

.yxo-hero__note {
	margin: calc(46 * var(--u)) 0 0;
	font-size: var(--fs-eyebrow);
	letter-spacing: var(--ls-wide);
	opacity: 0.75;
}

/* ------------------------------------------- Bloc résidence (alterné) --- */

/* Figma : image 706x436, texte 363 de large, gouttière 44. */
/* La section découpe ce qui sort du cadre. Sans cela, le bloc inversé, poussé
   à 560 vers la droite au départ de son animation, dépasse la fenêtre et lui
   ajoute une barre de défilement horizontale. `clip` et non `hidden` : le
   second ferait de la section un conteneur de défilement. */
.yxo-section:has(.yxo-split) {
	overflow-x: clip;
}

.yxo-split {
	display: grid;
	grid-template-columns: calc(706 * var(--u)) 1fr;
	gap: var(--yxo-gap);
	align-items: center;
	padding-block: calc(80 * var(--u));
}

.yxo-split--reverse {
	grid-template-columns: 1fr calc(706 * var(--u));
}

.yxo-split--reverse .yxo-split__media {
	order: 2;
}

.yxo-split__media {
	position: relative;
	aspect-ratio: 706 / 436;
	overflow: hidden;
}

.yxo-split__media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 900ms var(--ease);
}

.yxo-split:hover .yxo-split__media img {
	transform: scale(1.03);
}

.yxo-split__body {
	display: grid;
	gap: calc(14 * var(--u));
	align-content: center;
}

/* Figma : 10,8 px sur une frame de 540 pt, soit 28,8 px à 1440, avec un
   interlettrage de 0,42 px — 0,039 em. Au-delà, le titre passe à la ligne
   dans la colonne de 363 px, ce que la maquette ne fait pas. */
.yxo-split__title {
	font-size: clamp(1.25rem, 0.95rem + 1.35vw, 1.8rem);
	font-weight: 400;
	letter-spacing: 0.04em;
	line-height: 1.2;
	text-transform: uppercase;
	margin: 0;
}

.yxo-split__text {
	font-size: var(--fs-body-s);
	line-height: 1.9;
	margin: 0;
	opacity: 0.9;
}

/* Un filet sépare le paragraphe du bouton, sur toute la largeur de la colonne
   de texte — c'est ce que fait la maquette dans les deux blocs. */
.yxo-split__cta {
	margin-top: calc(30 * var(--u));
	padding-top: calc(24 * var(--u));
	border-top: 1px solid currentColor;
	border-top-color: color-mix(in srgb, currentColor 32%, transparent);
}

@media (max-width: 1023px) {
	.yxo-split,
	.yxo-split--reverse {
		grid-template-columns: 1fr;
		gap: calc(24 * var(--u));
		padding-block: calc(56 * var(--u));
	}
	.yxo-split--reverse .yxo-split__media {
		order: 0;
	}
}

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

/* Figma : bandeau plein cadre 1440x771, image + voile sable à 80 %,
   quatre colonnes décalées en diagonale. */
/* Figma : bandeau de 771 px, marge latérale propre de 186 px (et non la
   gouttière standard de 163). */
.yxo-services {
	position: relative;
	min-height: calc(771 * var(--u));
	padding: calc(70 * var(--u)) calc(186 * var(--u)) calc(90 * var(--u));
	/* `clip` et non `hidden` : `hidden` ferait de la section un conteneur de
	   défilement, et les animations liées au défilement des colonnes s'y
	   accrocheraient au lieu de la fenêtre — leur progression resterait figée.
	   `clip` découpe sans créer de conteneur de défilement. */
	overflow: clip;
	color: var(--wp--preset--color--wine);
	isolation: isolate;
}

.yxo-services__media {
	position: absolute;
	inset: 0;
	z-index: -2;
}

.yxo-services__media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	/* Cadrage bas, comme la maquette : on garde la terrasse et la piscine,
	   pas la bande de ciel en haut de la photo source. */
	object-position: center 68%;
}

.yxo-services__veil {
	position: absolute;
	inset: 0;
	z-index: -1;
	background: color-mix(in srgb, var(--wp--preset--color--sand) 80%, transparent);
}

/* Figma : titre à 71 px du haut de section, colonnes démarrant 404 px plus
   bas — les textes se posent bas dans le bandeau, sur les transats. */
.yxo-services__head {
	display: flex;
	align-items: center;
	margin-bottom: calc(301 * var(--u));
}

/* Figma : colonnes à x = 186, 463, 741 et 1018, soit un pas de 277 px pour
   des blocs de 192 px de large. */
.yxo-services__grid {
	display: grid;
	grid-template-columns: repeat(4, calc(192 * var(--u)));
	gap: calc(85 * var(--u));
	justify-content: start;
	align-items: start;
}

/* La diagonale de la maquette (décalages de 48 px) est l'état de DÉPART de
   l'animation, pas la mise en page : les quatre colonnes montent l'une après
   l'autre et finissent alignées sur une même ligne. Le décalage vit donc dans
   la règle d'animation plus bas, et la mise en page est alignée — c'est aussi
   l'état affiché sans JavaScript ou en mouvement réduit. */
.yxo-service {
	display: grid;
	gap: calc(10 * var(--u));
	max-width: calc(200 * var(--u));
}

.yxo-service__title {
	font-size: clamp(1rem, 0.75rem + 1.1vw, 1.6875rem);
	font-weight: 400;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	margin: 0;
}

.yxo-service__text {
	font-size: var(--fs-body-s);
	line-height: 1.8;
	margin: 0;
}

@media (max-width: 1023px) {
	.yxo-services {
		min-height: 0;
		/* La marge de 186 px relevée dans le Figma ne vaut que pour la grille
		   desktop : conservée telle quelle, elle ne laisserait que 18 px de
		   contenu sur un écran de 390. On repasse à la gouttière mobile. */
		padding-block: calc(56 * var(--u)) calc(64 * var(--u));
		padding-inline: var(--yxo-gutter);
	}
	.yxo-services__head {
		margin-bottom: calc(36 * var(--u));
	}
	.yxo-services__grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: calc(32 * var(--u)) calc(24 * var(--u));
	}
	.yxo-service {
		max-width: none;
	}
}

@media (max-width: 559px) {
	.yxo-services__grid {
		grid-template-columns: 1fr;
	}
}

/* ---------------------------------------------------------- Fondateur --- */

/* Figma : texte à x=266 (368 de large), portrait à x=746 (428 de large),
   gouttière 112 — soit un bloc de 908 px centré, avec des marges de 266 px
   propres à cette section. */
.yxo-founder {
	display: grid;
	grid-template-columns: calc(368 * var(--u)) calc(428 * var(--u));
	gap: calc(112 * var(--u));
	justify-content: center;
	align-items: center;
	padding-block: calc(99 * var(--u));
}

.yxo-founder__body {
	position: relative;
	display: grid;
	gap: calc(12 * var(--u));
}

/* Filet vertical entre le texte et le portrait, placé au milieu exact de la
   gouttière : l'espace est ainsi le même de part et d'autre, alors qu'il
   collait auparavant au texte et laissait un grand vide côté image. */
.yxo-founder__body::after {
	content: "";
	position: absolute;
	right: calc(-56 * var(--u));
	top: 0;
	bottom: calc(60 * var(--u));
	width: 1px;
	background: currentColor;
	opacity: 0.35;
}

.yxo-founder__text {
	font-size: var(--fs-body-s);
	line-height: 1.85;
	margin: 0;
}

.yxo-founder__name {
	margin: calc(20 * var(--u)) 0 0;
	font-size: var(--fs-body);
	font-weight: 500;
	letter-spacing: 0.09em;
}

.yxo-founder__role {
	margin: 0;
	font-size: var(--fs-eyebrow);
	letter-spacing: var(--ls-wide);
	color: var(--wp--preset--color--taupe);
	text-transform: uppercase;
}

/* Signature : le SVG livré quand il est là, sinon le repli typographique.
   La police cursive n'est qu'un filet de sécurité — un tracé manuscrit ne se
   remplace pas par une fonte système sans que ça se voie. */
.yxo-founder__sign {
	margin-top: calc(12 * var(--u));
	font-family: "Segoe Script", "Brush Script MT", cursive;
	font-size: clamp(1.5rem, 1rem + 1.6vw, 2.25rem);
	line-height: 1;
}

.yxo-founder__sign svg {
	display: block;
	width: calc(150 * var(--u));
	height: auto;
	/* Le fichier est carré avec de la marge autour du tracé : on recadre au
	   plus juste pour que la signature s'aligne sur le texte au-dessus. */
	margin-left: calc(-18 * var(--u));
}

/* Teinte du tracé. Le SVG français la porte en attribut `style` et l'emporte
   donc sur cette règle ; la copie anglaise l'a perdue en passant par kses, et
   se dessinait en noir. Les deux signatures s'écrivent maintenant de la même
   encre. */
.yxo-founder__sign svg path {
	fill: #401A1C;
}

.yxo-founder__portrait {
	aspect-ratio: 428 / 616;
	/* `clip` et non `hidden` : évite d'en faire un conteneur de défilement,
	   qui perturberait les animations liées au défilement de la page. */
	overflow: clip;
	/* Ombre portée large et très diffuse : le portrait se détache du fond
	   sable sans que l'ombre se voie comme telle. */
	box-shadow: 0 24px 48px rgba(0, 0, 0, 0.18);
}

.yxo-founder__portrait img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	/* Même traitement que les autres visuels de contenu : léger rapprochement
	   au survol, contenu par le `overflow: clip` du cadre. */
	transition: transform 900ms var(--ease);
}

.yxo-founder__portrait:hover img {
	transform: scale(1.03);
}

@media (max-width: 1023px) {
	.yxo-founder {
		grid-template-columns: 1fr;
		gap: calc(32 * var(--u));
		padding-block: calc(56 * var(--u));
	}
	.yxo-founder__body {
		max-width: none;
		order: 2;
	}
	.yxo-founder__body::after {
		display: none;
	}
	.yxo-founder__portrait {
		aspect-ratio: 4 / 5;
		max-width: 420px;
	}
}

/* -------------------------------------------------------------- Stats --- */

/* Figma : bandeau blanc 1440x228, six cellules séparées par un filet. */
.yxo-stats {
	display: grid;
	grid-template-columns: repeat(6, minmax(0, 1fr));
	min-height: calc(228 * var(--u));
	align-items: center;
	padding-block: calc(46 * var(--u));
}

/* Le filet séparateur est défini une seule fois : il est repris tel quel aux
   points de rupture, où il repart à zéro en début de ligne. La teinte est
   hors charte, d'où la valeur littérale. */
.yxo-stat {
	--yxo-stat-filet: 2px solid #DFD1BB;

	padding-inline: calc(28 * var(--u));
	border-left: var(--yxo-stat-filet);
}

.yxo-stat:first-child {
	border-left: 0;
	padding-left: 0;
}

/* Familles nommées plutôt que graisses demandées : le navigateur ne négocie
   plus, il charge le fichier désigné. « Gilroy » reste en repli derrière. */
.yxo-stat__value {
	display: block;
	font-family: "Gilroy-Bold", "Gilroy", sans-serif;
	font-size: var(--fs-stat);
	font-weight: 700;
	letter-spacing: 0.02em;
	line-height: 1;
	margin: 0 0 calc(14 * var(--u));
	font-variant-numeric: tabular-nums;
}

.yxo-stat__label {
	display: block;
	font-family: "Gilroy-Regular", "Gilroy", sans-serif;
	font-size: var(--fs-eyebrow);
	font-weight: 400;
	letter-spacing: var(--ls-wide);
	text-transform: uppercase;
	margin: 0;
}

@media (max-width: 1023px) {
	.yxo-stats {
		grid-template-columns: repeat(3, minmax(0, 1fr));
		gap: calc(28 * var(--u)) 0;
		min-height: 0;
	}

	/* Chiffres et libellés centrés sur l'axe de leur colonne. Les filets ne
	   bougent pas : ils sont portés par la bordure gauche de la cellule, que
	   l'alignement du texte ne touche pas.
	   Le retrait reste identique sur les cellules de début de ligne — seule
	   leur bordure disparaît. Leur enlever aussi le retrait, comme le faisait
	   la mise en page alignée à gauche, décalerait leur contenu de 14 px par
	   rapport à celui de la colonne voisine. */
	.yxo-stat {
		padding-inline: calc(14 * var(--u));
		text-align: center;
	}
	.yxo-stat:nth-child(3n + 1) {
		border-left: 0;
	}
}

@media (max-width: 479px) {
	.yxo-stats {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
	.yxo-stat:nth-child(n) {
		border-left: var(--yxo-stat-filet);
	}
	.yxo-stat:nth-child(2n + 1) {
		border-left: 0;
	}
}

/* ------------------------------------------------- Bandeaux d'images --- */

/* Emplacements où la maquette prévoit un carrousel. Le thème n'y pose qu'une
   image fixe, au bon format : le carrousel sera branché par un plugin dédié.
   Le ratio est conservé pour que le gabarit ne bouge pas au remplacement. */
.yxo-band {
	position: relative;
	width: 100%;
	overflow: hidden;
}

.yxo-band img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.yxo-band--tall { aspect-ratio: 1299 / 1739; }  /* façade pleine hauteur */
.yxo-band--lobby { aspect-ratio: 1299 / 609; }
.yxo-band--wide { aspect-ratio: 1440 / 678; }
.yxo-band--gallery { aspect-ratio: 1440 / 686; }

/* Bandeau occupé par un module Slider Revolution. Le module calcule lui-même
   sa hauteur — ses réglages sont calés sur le même rapport 1440/686 — donc on
   lui laisse la main et on réserve la place pour éviter un saut au chargement.
   Les règles d'image du bandeau sont neutralisées : `height: 100%` déformerait
   les visuels que le module positionne à sa façon. */
.yxo-band--slider {
	aspect-ratio: auto;
}

/* La réservation de hauteur est réservée au bureau. En dessous de 1024, `--u`
   est figé à 1 px : les 686 devenaient 686 px fixes alors que le module, calé
   sur sa propre grille, n'en fait que 186 à 390 de large. Le bandeau gardait
   donc 500 px de vide sous l'image — et comme les cinq panneaux sont empilés
   dans une même cellule, ce vide s'ajoutait à la page entière. */
@media (min-width: 1024px) {
	.yxo-band--slider {
		min-height: calc(686 * var(--u));
	}
}

.yxo-band--slider img {
	width: auto;
	height: auto;
	object-fit: initial;
}

.yxo-band--slider .wp-block-shortcode {
	margin: 0;
}

/* Slider Revolution dimensionne le fond de ses modules en pixels entiers. Dès
   que la hauteur tombe sur une fraction — celle de la façade vaut 1928,89 px,
   imposée par le rapport de son image — le fond s'arrête à 1928 et laisse voir
   0,89 px du blanc de la page sous la bande. Mesuré : `sr7-module-bg` fait
   1440 × 1928 dans un conteneur de 1440 × 1928,89.

   On le fait suivre son conteneur au lieu d'un arrondi. La règle vaut pour
   tous les modules : ailleurs la hauteur est entière, elle n'y change donc
   rien, mais le même liseré ne pourra plus apparaître. */
sr7-module-bg {
	width: 100% !important;
	height: 100% !important;
}

/* Les bandeaux passent à un cadrage plus vertical sur petit écran — sauf ceux
   qui portent un module, qui calculent leur hauteur eux-mêmes. Le bandeau des
   appartements cumule les deux classes : sans l'exception, le 4/3 lui imposait
   293 px là où son module n'en fait que 186, et laissait 106 px de vide. */
@media (max-width: 1023px) {
	.yxo-band--tall:not(.yxo-band--slider) { aspect-ratio: 3 / 4; }
	.yxo-band--lobby:not(.yxo-band--slider),
	.yxo-band--wide:not(.yxo-band--slider),
	.yxo-band--gallery:not(.yxo-band--slider) { aspect-ratio: 4 / 3; }
}

/* --------------------------------------------- Onglets appartements --- */

.yxo-tabs__bar {
	display: flex;
	justify-content: center;
	gap: calc(56 * var(--u));
	padding-block: calc(24 * var(--u));
}

/* Sur la maquette les onglets reposent sur un filet continu de 587 de large,
   dont seul le segment actif passe en or. Les onglets sont répartis dessus,
   pas simplement centrés.
   Réservé au bureau : en dessous, `--u` vaut 1 px et cette largeur fixe
   débordait une fenêtre de 390 — mesuré, la page passait à 587 de large. Le
   petit écran garde sa barre défilante. */
@media (min-width: 1024px) {
	.yxo-apartments .yxo-tabs__bar {
		position: relative;
		width: calc(587 * var(--u));
		max-width: 100%;
		margin-inline: auto;
		justify-content: space-between;
		gap: 0;
		padding-block: calc(24 * var(--u)) 0;
		margin-bottom: calc(24 * var(--u));
	}

	.yxo-apartments .yxo-tabs__bar::after {
		content: "";
		position: absolute;
		inset-inline: 0;
		bottom: 0;
		height: 1px;
		background: currentColor;
		opacity: 0.28;
	}

	/* Le trait de l'onglet actif se pose sur ce filet, et le couvre. */
	.yxo-apartments .yxo-tab::after {
		height: 2px;
		z-index: 1;
	}
}

.yxo-tab {
	position: relative;
	padding: 0.35em 0;
	border: 0;
	background: none;
	color: inherit;
	font-size: var(--fs-eyebrow);
	font-weight: 500;
	letter-spacing: var(--ls-eyebrow);
	text-transform: uppercase;
	cursor: pointer;
	opacity: 0.6;
	transition: opacity var(--t-fast);
}

.yxo-tab::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 1px;
	background: currentColor;
	transform: scaleX(0);
	transition: transform var(--t-fast);
}

.yxo-tab[aria-selected="true"] {
	opacity: 1;
	color: var(--wp--preset--color--gold);
}

.yxo-tab[aria-selected="true"]::after {
	transform: scaleX(1);
}

.yxo-tabs__panel[hidden] {
	display: none;
}

/* Onglets portant un module Slider Revolution.
   Un module posé dans un panneau en `display: none` ne s'initialise pas : il
   se mesure à zéro et attend un redimensionnement. Mesuré, ce réveil coûtait
   2,3 s à chaque premier passage sur un onglet. On empile donc les panneaux
   dans une même cellule de grille et on masque par `visibility` : chacun garde
   sa mise en page, tous les modules s'initialisent au chargement, et le
   changement d'onglet devient immédiat. `visibility: hidden` sort bien le
   panneau de l'arbre d'accessibilité — le contenu masqué reste inaudible. */
.yxo-tabs:has(.yxo-band--slider) {
	display: grid;
	/* minmax(0, …) et non 1fr : un module SR7 a une largeur intrinsèque
	   minimale qui, sinon, remonte jusqu'au rail et élargit toute la page —
	   mesuré 915 px sur une fenêtre de 390. */
	grid-template-columns: minmax(0, 1fr);
}

.yxo-band--slider {
	min-width: 0;
}

/* Le bandeau bordeaux d'introduction. Le style d'espacement posé sur le bloc
   n'était pas appliqué — mesuré padding-top à 0 — et « Exclusivité » venait
   coller au bord. On le pose ici, où rien ne peut l'écarter. */
.yxo-apartments > .yxo-section {
	padding-block: calc(66 * var(--u)) calc(40 * var(--u));
}

/* Bloc d'introduction centré, comme sur KARENGA APARTMENT TYPES.pdf : surtitre,
   titre et paragraphe alignés sur l'axe de la page, le texte tenu dans une
   colonne de 755 et non sur toute la largeur. */
.yxo-apartments .yxo-shell {
	text-align: center;
}

.yxo-apartments .yxo-lede {
	max-width: calc(755 * var(--u));
	margin-inline: auto;
}

/* Les puces sont celles du module — le préréglage « youxone-hermes » réglé
   dans Slider Revolution, et non une règle du thème : leur apparence se change
   depuis l'éditeur du plugin. */

.yxo-tabs:has(.yxo-band--slider) .yxo-tabs__bar {
	grid-row: 1;
}

.yxo-tabs:has(.yxo-band--slider) .yxo-tabs__panel {
	grid-row: 2;
	grid-column: 1;
}

/* Panneau inactif : présent, dimensionné, et surtout **visible au sens du
   navigateur**. `visibility: hidden` le sortait du champ pour Slider
   Revolution, qui refusait alors de démarrer son module : l'onglet s'ouvrait
   sur une image morte. En passant par l'opacité, les cinq modules se lancent
   normalement au défilement et changer d'onglet ne fait plus que dévoiler.
   L'attribut `inert`, posé par le script, les retire du clavier et des
   lecteurs d'écran — ce que l'opacité seule ne fait pas. */
.yxo-tabs:has(.yxo-band--slider) .yxo-tabs__panel[hidden] {
	display: block;
	opacity: 0;
	pointer-events: none;
}

@media (max-width: 1023px) {
	.yxo-tabs__bar {
		gap: calc(28 * var(--u));
		overflow-x: auto;

		/* Centrés, mais `safe` : si un jour les onglets débordent, le mot-clé
		   fait retomber l'alignement sur le début plutôt que de rogner le
		   premier, ce qu'un `center` seul provoque dans un conteneur
		   défilant. Mesuré à 390 px : cinq onglets pour 226 px, il reste de la
		   place. */
		justify-content: safe center;
		padding-inline: var(--yxo-gutter);
		scrollbar-width: none;
	}
	.yxo-tabs__bar::-webkit-scrollbar {
		display: none;
	}
	.yxo-tab {
		flex: 0 0 auto;
	}
}

/* --------------------------------------------------- Cartes services --- */

/* Figma : texte à gauche, trois vignettes de 231x231 à droite
   (x = 561, 824 et 1088, gouttière 32). Titre en or. */
.yxo-servicecards {
	padding-block: calc(74 * var(--u)) calc(70 * var(--u));

	/* Cotes relevées sur la maquette : carte carrée de 265, gouttière de 35.
	   Le carrousel montre 2,5 cartes — la troisième coupée en deux annonce
	   la suite sans avoir besoin de flèches. */
	--yxo-card: calc(265 * var(--u));
	--yxo-card-gap: calc(35 * var(--u));
}

.yxo-cards__grid {
	display: grid;
	grid-template-columns: calc(360 * var(--u)) auto;
	gap: calc(100 * var(--u));
	justify-content: center;
	align-items: start;
}

.yxo-cards__intro {
	display: grid;
	gap: calc(14 * var(--u));
}

.yxo-servicecards .yxo-title,
.yxo-apartments .yxo-title {
	color: var(--wp--preset--color--gold);
}

.yxo-cards__cta {
	display: grid;
	justify-items: center;
	gap: calc(24 * var(--u));
	max-width: calc(620 * var(--u));
	margin: calc(64 * var(--u)) auto 0;
	text-align: center;
}

/* Fenêtre du carrousel : 2 cartes entières, leurs deux gouttières, et la
   moitié de la troisième. Le conteneur ne défile pas — il sert d'ancrage au
   rail de progression posé en pseudo-éléments. */
.yxo-cards__viewport {
	position: relative;
	width: calc(2.5 * var(--yxo-card) + 2 * var(--yxo-card-gap));
	max-width: 100%;

	/* La piste est un conteneur défilant : sa largeur intrinsèque ne doit
	   jamais dicter celle de son parent, sinon la section s'élargit. */
	min-width: 0;
	padding-bottom: calc(62 * var(--u));
}

.yxo-cards {
	display: flex;
	gap: var(--yxo-card-gap);
	min-width: 0;
	overflow-x: auto;
	overscroll-behavior-x: contain;
	scroll-snap-type: x mandatory;
	scroll-behavior: smooth;

	/* La barre de défilement native est masquée : le rail la remplace. */
	scrollbar-width: none;
	-ms-overflow-style: none;
}

.yxo-cards::-webkit-scrollbar {
	display: none;
}

.yxo-card {
	flex: 0 0 var(--yxo-card);
	scroll-snap-align: start;
}

/* Glisser à la souris. Pendant le glissement le calage est suspendu, sinon
   chaque pixel parcouru serait aussitôt ramené au point d'ancrage. */
.yxo-js .yxo-cards {
	cursor: grab;
}

.yxo-cards.is-dragging {
	cursor: grabbing;
	scroll-snap-type: none;
	scroll-behavior: auto;
	user-select: none;
}

.yxo-cards.is-dragging img {
	pointer-events: none;
}

/* Rail de progression : un segment par carte, l'actif en or.
   --yxo-n (nombre de cartes) et --yxo-i (position) sont posés par le script ;
   sans JS le rail reste discret et le carrousel défile quand même. */
.yxo-cards__viewport::before,
.yxo-cards__viewport::after {
	content: "";
	position: absolute;
	bottom: 0;
	pointer-events: none;
}

/* Le rail ne court pas sur toute la largeur du carrousel : il occupe la moitié
   centrale, comme sur la maquette. `--yxo-rail` tient cette proportion en un
   seul endroit, dont dérivent le filet et le segment actif. */
.yxo-cards__viewport {
	--yxo-rail: 50%;
}

.yxo-cards__viewport::before {
	left: 50%;
	width: var(--yxo-rail);
	transform: translateX(-50%);
	height: 2px;
	background: var(--wp--preset--color--wine);
}

.yxo-cards__viewport::after {
	/* Bord gauche du rail : la moitié de ce qui reste de part et d'autre.
	   Même épaisseur que le filet, donc posé exactement dessus : sur la
	   maquette le segment actif ne se distingue que par sa couleur. Je l'avais
	   fait dépasser d'un pixel pour le détacher — il flottait au-dessus de la
	   ligne au lieu d'en faire partie. */
	left: calc((100% - var(--yxo-rail)) / 2);
	height: 2px;
	width: calc(var(--yxo-rail) / var(--yxo-n, 1));
	background: var(--wp--preset--color--gold);
	transform: translateX(calc(var(--yxo-i, 0) * 100%));
}

/* Un seul écran de cartes : plus rien à annoncer, le rail s'efface. */
.yxo-cards__viewport.is-static {
	padding-bottom: 0;
}

.yxo-cards__viewport.is-static::before,
.yxo-cards__viewport.is-static::after {
	display: none;
}

.yxo-card__media {
	aspect-ratio: 1;
	overflow: hidden;
}

.yxo-card__media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 900ms var(--ease);
}

.yxo-card:hover .yxo-card__media img {
	transform: scale(1.05);
}

.yxo-card__label {
	margin: calc(14 * var(--u)) 0 0;
	font-size: var(--fs-eyebrow);
	letter-spacing: var(--ls-wide);
	text-transform: uppercase;
	color: var(--wp--preset--color--taupe);
	transition: opacity var(--t-fast);
}

/* La carte coupée par le bord voit son intitulé s'estomper : c'est ce que
   fait la maquette, et cela dit « il y en a d'autres » sans une flèche. */
.yxo-card.is-partial .yxo-card__label {
	opacity: 0.45;
}

/* Et elle se clique pour avancer d'un cran : le curseur l'annonce. */
.yxo-js .yxo-card.is-partial {
	cursor: pointer;
}

@media (max-width: 1023px) {
	.yxo-cards__grid {
		/* minmax(0, …) et non 1fr : sinon la largeur intrinsèque de la piste
		   défilante remonte jusqu'au rail de grille et élargit la section. */
		grid-template-columns: minmax(0, 1fr);
		gap: calc(32 * var(--u));
	}
	.yxo-servicecards {
		/* En colonne unique la carte se mesure en part de la piste : 62 %
		   laisse toujours dépasser la suivante, quelle que soit la largeur. */
		--yxo-card: 62%;
		--yxo-card-gap: calc(16 * var(--u));
	}
	.yxo-cards__viewport {
		width: 100%;
		padding-bottom: calc(40 * var(--u));
	}
	.yxo-cards__cta {
		max-width: none;
		margin-top: calc(40 * var(--u));
	}
}

@media (max-width: 559px) {
	.yxo-servicecards {
		--yxo-card: 78%;
	}
}

/* ------------------------------------------------------ Plan / carte --- */

/* Le plan se comporte comme le bloc de référence : au survol il s'agrandit
   lentement pendant qu'un voile sombre monte. Valeurs relevées sur le site
   donné en exemple — zoom 1,2 et voile noir à 30 %, tous deux en 2 s ease.
   Une durée pareille n'est tenable que parce que rien d'autre ne bouge. */
.yxo-map {
	position: relative;
	display: block;
	width: 100%;
	overflow: hidden;
	background: var(--wp--preset--color--sand);
	color: inherit;
	cursor: pointer;
}

.yxo-map__plan {
	display: block;
	transition: transform 2s ease;
}

/* Le plan porte quatre teintes réparties sur 375 tracés — brun pour les îlots,
   sable pour les voies, plus deux accents. Aucune règle uniforme ne peut les
   rétablir une fois les attributs `style` perdus : c'est le balisage lui-même
   qu'on recopie depuis la version française (voir tools/patch-svg-traduits.php). */

/* Puces du carrousel projet : posées sur le visuel et non sous lui. Slider
   Revolution les place à 10 px du bas du module, c'est-à-dire juste sous
   l'image. On les remonte à l'intérieur du cadre.
   `top` est écrit en ligne par le plugin, il faut le neutraliser. */
sr7-module[data-alias="karenga_project"] sr7-bullets {
	top: auto !important;
	bottom: calc(40 * var(--u)) !important;
	z-index: 6;
}

/* Cadre sable du carrousel projet.

   Il n'est pas obtenu en rentrant le module : Slider Revolution repeint ses
   fonds de diapo bord à bord et recalcule son échelle dès qu'on touche à sa
   boîte — trois tentatives mesurées, aucune concluante. Le cadre est donc un
   liseré posé par-dessus, en ombre intérieure. Rien n'est redimensionné, donc
   rien ne se réagence : la transition ne coûte qu'un repeint.

   Sur la dernière diapo le liseré s'efface et le visuel gagne toute la
   largeur — l'ouverture accompagne le zoom au lieu de le subir. */
.wp-block-themepunch-revslider:has(sr7-module[data-alias="karenga_project"]) {
	position: relative;
}

.wp-block-themepunch-revslider:has(sr7-module[data-alias="karenga_project"])::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 5;
	pointer-events: none;
	box-shadow: inset 0 0 0 calc(36 * var(--u)) var(--wp--preset--color--sand);
	transition: box-shadow 1200ms var(--ease);
}

/* Posée par le script quand la dernière diapo entre en scène. Le sélecteur
   reprend le `:has()` : sans lui il perdrait, `:has()` empruntant la
   spécificité de son argument — ici un attribut de plus. */
.wp-block-themepunch-revslider.yxo-cadre-ouvert:has(sr7-module[data-alias="karenga_project"])::after {
	box-shadow: inset 0 0 0 0 var(--wp--preset--color--sand);
}

/* Mouvement réduit : le cadre ne s'anime plus, il change d'état. */
@media (prefers-reduced-motion: reduce) {
	.wp-block-themepunch-revslider:has(sr7-module[data-alias="karenga_project"])::after {
		transition: none;
	}
}

.yxo-map:hover .yxo-map__plan,
.yxo-map:focus-visible .yxo-map__plan {
	transform: scale(1.2);
}

.yxo-map__veil {
	position: absolute;
	inset: 0;
	background: rgba(0, 0, 0, 0);
	transition: background-color 2s ease;
	pointer-events: none;
}

.yxo-map:hover .yxo-map__veil,
.yxo-map:focus-visible .yxo-map__veil {
	background: rgba(0, 0, 0, 0.3);
}

.yxo-map img,
.yxo-map svg {
	display: block;
	width: 100%;
	height: auto;
}

/* Mouvement réduit : le plan ne bouge plus, seul le voile signale le survol. */
@media (prefers-reduced-motion: reduce) {
	.yxo-map__plan {
		transition: none;
	}

	.yxo-map:hover .yxo-map__plan,
	.yxo-map:focus-visible .yxo-map__plan {
		transform: none;
	}
}

/* ------------------------------------------- Fenêtre modale brochure --- */

/* Élément `dialog` natif : le fond, le focus piégé et la touche Échap sont
   fournis par le navigateur. La règle `:target` sert le cas sans JavaScript,
   où l'ancre suffit à faire apparaître le panneau. */
.yxo-modal {
	width: min(calc(520 * var(--u)), calc(100vw - 2rem));
	max-width: none;
	padding: 0;
	border: 0;
	background: none;
	color: var(--wp--preset--color--wine);
	overflow: visible;
}

.yxo-modal::backdrop {
	background: color-mix(in srgb, var(--wp--preset--color--wine) 72%, transparent);
	backdrop-filter: blur(3px);
}

/* Repli réservé aux pages sans JavaScript, où l'ancre est le seul moyen
   d'ouvrir la fenêtre.
   Quand le script est là, l'affichage ne dépend que de l'état du dialogue.
   Autrement `:target` continuait de s'appliquer après la fermeture : le
   navigateur ne réévalue pas la cible quand l'adresse est réécrite sans
   navigation, et le panneau restait à l'écran malgré `close()`. C'est ce qui
   se passait après l'envoi de la brochure, l'adresse de retour finissant
   par `#yxo-brochure`. */
html:not(.yxo-js) .yxo-modal:target {
	display: block;
}

.yxo-modal__panel {
	position: relative;
	display: grid;
	gap: calc(14 * var(--u));
	padding: calc(48 * var(--u)) calc(44 * var(--u)) calc(40 * var(--u));
	background: var(--wp--preset--color--sand);
	box-shadow: 0 24px 64px rgba(0, 0, 0, 0.32);
}

.yxo-modal__close {
	position: absolute;
	top: calc(16 * var(--u));
	right: calc(16 * var(--u));
	display: grid;
	place-items: center;
	width: 2.2em;
	height: 2.2em;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: none;
	color: inherit;
	cursor: pointer;
	transition: background-color var(--t-fast);
}

.yxo-modal__close:hover {
	background: color-mix(in srgb, var(--wp--preset--color--wine) 12%, transparent);
}

.yxo-modal__eyebrow {
	margin: 0;
	font-size: var(--fs-eyebrow);
	letter-spacing: var(--ls-eyebrow);
	text-transform: uppercase;
	opacity: 0.75;
}

.yxo-modal__title {
	margin: 0;
	font-size: clamp(1.25rem, 1rem + 1vw, 1.75rem);
	font-weight: 300;
	letter-spacing: var(--ls-wide);
	text-transform: uppercase;
}

.yxo-modal__lede {
	margin: 0 0 calc(6 * var(--u));
	font-size: var(--fs-eyebrow);
	line-height: 1.6;
	opacity: 0.85;
}

.yxo-modal__alert {
	margin: 0;
	padding: calc(10 * var(--u)) calc(14 * var(--u));
	background: color-mix(in srgb, var(--wp--preset--color--wine) 10%, transparent);
	border-left: 2px solid var(--wp--preset--color--wine);
	font-size: var(--fs-eyebrow);
	line-height: 1.5;
}

.yxo-modal__field {
	display: grid;
	gap: calc(6 * var(--u));
	font-size: var(--fs-eyebrow);
	letter-spacing: var(--ls-wide);
	text-transform: uppercase;
}

.yxo-modal__field input {
	padding: calc(12 * var(--u)) calc(14 * var(--u));
	border: 0;
	background: var(--wp--preset--color--field);
	color: inherit;
	font: inherit;
	font-size: 1rem;
	letter-spacing: normal;
	text-transform: none;
}

.yxo-modal__field input:focus-visible {
	outline: 2px solid var(--wp--preset--color--wine);
	outline-offset: 1px;
}

.yxo-modal__submit {
	justify-self: start;
	margin-top: calc(8 * var(--u));
}

@media (max-width: 559px) {
	.yxo-modal__panel {
		padding: calc(40 * var(--u)) calc(24 * var(--u)) calc(32 * var(--u));
	}
}

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

.yxo-contact {
	display: grid;
	grid-template-columns: calc(420 * var(--u)) 1fr;
	gap: calc(90 * var(--u));
	padding-block: calc(120 * var(--u)) calc(140 * var(--u));
	align-items: start;

	/* Queue du formulaire : sa dernière gouttière plus le bouton d'envoi. Le
	   bloc de coordonnées s'arrête au bas de la zone de message, comme sur la
	   maquette — pas au bas de la colonne. La hauteur du bouton est
	   entièrement déterminée : deux fois son remplissage vertical plus son
	   interligne, épinglé plus bas justement pour que ce calcul reste juste à
	   toutes les largeurs. */
	--yxo-form-tail: calc(20 * var(--u) + 24 * var(--u) + 1.2 * var(--fs-eyebrow));
}

.yxo-contact__aside {
	display: grid;
	gap: calc(16 * var(--u));
	align-self: end;
	margin-bottom: var(--yxo-form-tail);
	font-size: var(--fs-body-s);

	/* 1,85 aérait le bloc au point de le désolidariser : l'adresse se lisait
	   comme quatre paragraphes. La maquette empile ses lignes serré et ne
	   respire qu'entre les groupes, d'où le `gap` ci-dessus. */
	line-height: 1.5;
}

.yxo-contact__aside p {
	margin: 0;
}

.yxo-contact__heading {
	margin: 0;
	font-size: var(--fs-eyebrow);
	font-weight: 600;
	letter-spacing: var(--ls-eyebrow);
	text-transform: uppercase;
}

/* Deux tons dans le bloc : l'intitulé porte le bordeaux hérité, l'information
   passe en taupe. C'est la hiérarchie de la maquette — tout était au bordeaux,
   donc rien ne distinguait « Téléphone : » de son numéro. #867263 est le taupe
   de la charte, on prend le jeton plutôt que la valeur. */
.yxo-contact__address,
.yxo-contact__map,
.yxo-contact__value {
	color: var(--wp--preset--color--taupe);
}

.yxo-contact__map a {
	text-decoration: underline;
	text-underline-offset: 0.3em;
	text-decoration-thickness: 1px;
}

/* Intitulés et valeurs vivent dans une même grille : tous les intitulés
   s'alignent entre eux, et une valeur sur deux lignes — les horaires —
   reste calée sous la première, au lieu de repartir à la marge. */
.yxo-contact__rows {
	display: grid;
	grid-template-columns: max-content minmax(0, 1fr);
	column-gap: calc(14 * var(--u));
	row-gap: calc(6 * var(--u));
	align-items: baseline;
	margin-top: calc(4 * var(--u));
}

/* Intitulés en semi-gras, valeurs en romain : c'est la hiérarchie de la
   maquette, et elle rend le bloc lisible d'un coup d'œil. */
/* Intitulés du bloc coordonnées : la famille est nommée en clair plutôt que
   demandée par la graisse. `font-weight: 600` laisse le navigateur choisir
   parmi les faces déclarées ; nommer « Gilroy-Semibold » ne lui laisse rien à
   négocier. Le 600 reste, pour le repli si le fichier venait à manquer. */
.yxo-contact__heading,
.yxo-contact__label {
	font-family: "Gilroy-Semibold", "Gilroy", sans-serif;
	font-weight: 600;
}

.yxo-contact__label {
	white-space: nowrap;
}

/* Le deux-points appartient à l'intitulé sur la maquette (« Phone : »). Il est
   posé en CSS pour rester hors du texte : le client modifie « Téléphone »
   sans avoir à penser à la ponctuation, et la traduction ne l'emporte pas. */
.yxo-contact__label::after {
	content: " :";
}

.yxo-contact__value {
	font-weight: 400;
}

@media (max-width: 559px) {
	/* Sur écran étroit, la colonne des valeurs deviendrait trop mince :
	   l'intitulé passe au-dessus de sa valeur. */
	.yxo-contact__rows {
		grid-template-columns: 1fr;
		row-gap: calc(4 * var(--u));
	}

	.yxo-contact__label:not(:first-child) {
		margin-top: calc(14 * var(--u));
	}
}

.yxo-contact__aside a {
	color: inherit;
}

.yxo-form {
	display: grid;
	gap: calc(20 * var(--u));
}

/* Le formulaire collait au texte d'introduction : « NAME » démarrait sur la
   ligne suivante, sans respiration. Les blocs de la section sont empilés sans
   gouttière, l'écart se pose donc ici. */
.yxo-contact__form .yxo-form {
	margin-top: calc(40 * var(--u));
}

/* Chaque champ est enveloppé dans un <p> : sa marge par défaut s'ajouterait
   au gap de la grille et doublerait l'espacement. */
.yxo-form p {
	margin: 0;
}

.yxo-form__label {
	display: block;
	margin-bottom: calc(8 * var(--u));
	font-size: var(--fs-eyebrow);
	letter-spacing: var(--ls-wide);
	text-transform: uppercase;
}

.yxo-form input,
.yxo-form textarea {
	/* En ligne par défaut, un champ s'assoit sur la ligne de base et laisse
	   sous lui la place des jambages : mesuré, ce talon ajoutait 8,9 px sous
	   chaque champ, en plus de la gouttière déclarée. En bloc, la hauteur du
	   `<p>` vaut exactement celle du champ. */
	display: block;
	width: 100%;
	padding: calc(14 * var(--u)) calc(16 * var(--u));
	border: 0;
	border-radius: 0;
	background: var(--wp--preset--color--field);
	color: var(--wp--preset--color--brown);
	font: inherit;
	font-size: var(--fs-body-s);
}

.yxo-form textarea {
	min-height: calc(284 * var(--u));
	resize: vertical;
}

.yxo-form input::placeholder,
.yxo-form textarea::placeholder {
	color: color-mix(in srgb, var(--wp--preset--color--brown) 55%, transparent);
}

.yxo-form__submit {
	justify-self: start;
	padding: calc(12 * var(--u)) calc(46 * var(--u));
	border: 0;
	background: var(--wp--preset--color--taupe);
	color: var(--wp--preset--color--white);
	font-size: var(--fs-eyebrow);

	/* Interligne épinglé : c'est lui qui rend la hauteur du bouton calculable,
	   et `--yxo-form-tail` s'en sert pour caler le bloc de coordonnées. */
	line-height: 1.2;
	letter-spacing: var(--ls-eyebrow);
	text-transform: uppercase;
	cursor: pointer;

	/* Pas de balayage ici, à la demande : le bouton d'envoi garde son
	   changement de fond direct. */
	transition: background-color var(--t-fast), transform var(--t-fast);
}

.yxo-form__submit:hover {
	background: var(--wp--preset--color--brown);
}

/* Champ pot de miel : masqué visuellement, jamais rempli par un humain. */
.yxo-form__trap {
	position: absolute;
	left: -9999px;
}

.yxo-form__notice {
	padding: calc(12 * var(--u)) calc(16 * var(--u));
	border-left: 2px solid currentColor;
	font-size: var(--fs-body-s);
}

@media (max-width: 1023px) {
	.yxo-contact {
		grid-template-columns: 1fr;
		gap: calc(40 * var(--u));
		padding-block: calc(56 * var(--u)) calc(64 * var(--u));
	}
	.yxo-contact__aside {
		order: 2;

		/* En colonne unique il n'y a plus de bouton à côté duquel se caler :
		   la marge de compensation deviendrait un trou. */
		margin-bottom: 0;
	}
	.yxo-form textarea {
		min-height: 180px;
	}
}

/* ------------------------------------------------------ Pied de page --- */

/* Figma : bandeau bordeaux de 344 px sur les trois pages. */
.yxo-footer {
	background: var(--wp--preset--color--wine);
	color: var(--wp--preset--color--sand);
	padding: calc(60 * var(--u)) calc(20 * var(--u)) calc(35 * var(--u));
}

.yxo-footer__logo {
	display: grid;
	place-items: center;
	margin-bottom: calc(40 * var(--u));
}

.yxo-footer__logo svg,
.yxo-footer__logo img {
	width: calc(150 * var(--u));
	min-width: 120px;
}

.yxo-footer__main {
	display: grid;
	grid-template-columns: calc(120 * var(--u)) 1fr auto auto;
	gap: var(--yxo-gap);
	align-items: center;
	position: relative;
	padding-bottom: calc(28 * var(--u));
}

/* Le trait est un pseudo-élément et non une bordure : Chrome arrondit les
   largeurs de bordure à l'entier, alors qu'une hauteur passe telle quelle.
   Sable plein, non plus la couleur du texte diluée à 22 % qui se voyait à
   peine sur le bordeaux.

   1 px, l'épaisseur du filet des boutons : à 1,5 px le trait pesait plus lourd
   qu'eux alors qu'il ne fait que séparer deux rangées. */
.yxo-footer__main::after {
	content: "";
	position: absolute;
	inset-inline: 0;
	bottom: 0;
	height: 1px;
	background: var(--wp--preset--color--sand);
}

/* Un cran plus petit, un cran plus gras, et resserré : à 13 px en 400 avec
   28 px entre deux entrées, le menu flottait. */
.yxo-footer__nav {
	display: grid;
	gap: calc(5 * var(--u));
	font-size: calc(var(--fs-eyebrow) - 1px);
	font-weight: 500;
	letter-spacing: calc(var(--ls-wide) - 0.02em);
	text-transform: uppercase;
}

.yxo-footer__nav a {
	color: inherit;
	text-decoration: none;
	opacity: 0.85;
	transition: opacity var(--t-fast);
}

.yxo-footer__nav a:hover {
	opacity: 1;
}

/* Un court tiret sépare deux entrées, comme sur la maquette. Il est porté par
   l'entrée qui suit, donc il n'apparaît jamais avant la première ni après la
   dernière, quel que soit le nombre de liens du menu. */
.yxo-footer__nav a + a::before {
	content: "";
	display: block;
	width: calc(16 * var(--u));
	min-width: 12px;
	height: 1px;
	margin-block: calc(5 * var(--u));
	background: currentColor;
	opacity: 0.55;
}

.yxo-footer__claim {
	font-size: clamp(0.875rem, 0.7rem + 0.7vw, 1.25rem);
	font-weight: 400;
	letter-spacing: 0.11em;
	text-transform: uppercase;
	margin: 0 0 calc(8 * var(--u));
}

.yxo-footer__sub {
	margin: 0;
	font-size: var(--fs-eyebrow);
	line-height: 1.7;
	opacity: 0.7;
}

/* Rectangle de largeur fixe : 257 px dans la grille de 1440, relevé au pixel
   sur WEBSITE.pdf. La largeur suivait le libellé, si bien que le bouton
   changeait de taille d'une page à l'autre — 171 px sur Contact, 193 px sur
   Karenga, 181 px sur l'accueil — là où la maquette n'en montre qu'un. */
.yxo-footer .yxo-btn {
	width: calc(257 * var(--u));
	max-width: 100%;
	justify-content: center;
}

/* Bouton « retour en haut » du coin droit : disque plein couleur sable, repère
   bordeaux. Il était en cercle filaire, c'est-à-dire l'inverse de la maquette —
   celle-ci le remplit et évide le triangle, comme les icônes sociales. */
.yxo-totop {
	display: grid;
	place-items: center;
	width: calc(44 * var(--u));
	height: calc(44 * var(--u));
	min-width: 40px;
	min-height: 40px;

	/* Un bouton porte un remplissage par défaut (1px 6px sous Chrome) : le
	   pourcentage du triangle se mesurerait sur la boîte de contenu et non sur
	   le disque, ce qui le rapetissait de 24 % à 18 %. */
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: var(--wp--preset--color--sand);
	color: var(--wp--preset--color--wine);
	cursor: pointer;
	transition: background-color var(--t-fast), color var(--t-fast);
}

/* Le disque s'éclaircit au survol, comme les icônes : l'inversion servait
   d'état de survol tant que l'état normal était filaire, elle n'a plus lieu
   d'être. */
.yxo-totop:hover {
	background: color-mix(in srgb, var(--wp--preset--color--sand) 55%, transparent);
}

/* 24 % du diamètre, mesuré sur la maquette. */
.yxo-totop svg {
	width: 24%;
	height: auto;
}

.yxo-footer__legal {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: calc(16 * var(--u));
	padding-top: calc(18 * var(--u));
	font-size: var(--fs-eyebrow);
	opacity: 0.7;
}

/* La signature du studio est un lien, mais ne doit pas se donner des airs
   d'appel à l'action : elle garde la couleur et la discrétion de la ligne, et
   ne se souligne qu'au survol. */
.yxo-footer__credits a {
	color: inherit;
	text-decoration: none;
	text-underline-offset: 0.3em;
	transition: opacity var(--t-fast);
}

.yxo-footer__credits a:hover,
.yxo-footer__credits a:focus-visible {
	text-decoration: underline;
}

.yxo-footer__legal p {
	margin: 0;
}

.yxo-footer__social {
	display: flex;
	gap: calc(14 * var(--u));
}

/* Les fichiers livrés par le studio sont des disques entiers, glyphe évidé :
   le cercle et le fond appartiennent au tracé. Le lien ne dessine donc plus
   rien lui-même — un cercle CSS autour d'un disque en ferait deux. */
.yxo-footer__social a {
	display: block;
	width: 1.6em;
	height: 1.6em;
	color: var(--wp--preset--color--sand);
	transition: color var(--t-fast);
}

.yxo-footer__social a:hover {
	color: color-mix(in srgb, var(--wp--preset--color--sand) 55%, transparent);
}

.yxo-footer__social svg {
	display: block;
	width: 100%;
	height: 100%;
}

@media (max-width: 1023px) {
	.yxo-footer__main {
		grid-template-columns: 1fr;
		gap: calc(28 * var(--u));
		text-align: center;
		justify-items: center;
	}
	.yxo-footer__nav {
		justify-items: center;
	}
	.yxo-footer__legal {
		justify-content: center;
		text-align: center;
	}
}

/* ------------------------------------------- Apparition au défilement --- */

/* Les flèches dessinées dans la maquette ne sont pas des ornements : elles
   indiquent le sens d'entrée de chaque section. Relevé sur WEBSITE.pdf,
   page 1 (seule page annotée) :
     « → » à gauche du 1er bloc résidence   -> entre par la gauche
     « ← » à droite du 2e bloc résidence    -> entre par la droite
     « → » à gauche de « OUR SERVICES »     -> entre par la gauche
     « ↑ » à droite de la grille services   -> monte depuis le bas
   Les flèches elles-mêmes ne sont donc pas rendues.

   L'état masqué n'est appliqué que si le JS a confirmé qu'il saura le lever
   (classe yxo-anim-on posée par theme.js, uniquement quand IntersectionObserver
   est disponible et que l'utilisateur n'a pas demandé moins d'animations).
   Sans JS, sans IntersectionObserver, à l'impression ou en mouvement réduit,
   tout reste visible : aucun effet ne peut masquer du contenu. */
html.yxo-anim-on .yxo-anim {
	opacity: 0;
	transition:
		opacity 900ms var(--ease),
		transform 900ms var(--ease);
}

/* Les deux blocs RESIDENCE KARENGA entrent plus posément que le reste :
   900 ms + 2 s de course, et une demi-seconde d'attente avant de partir.
   Réglé ici et non sur `.yxo-anim` en général, pour que la cascade de
   OUR SERVICES garde son propre tempo. */
html.yxo-anim-on .yxo-split.yxo-anim {
	transition-duration: 2900ms;
	transition-delay: 500ms;
}

html.yxo-anim-on .yxo-anim--left {
	transform: translateX(calc(-64 * var(--u)));
}

html.yxo-anim-on .yxo-anim--right {
	transform: translateX(calc(64 * var(--u)));
}

/* Les deux blocs RÉSIDENCE KARENGA partent de bien plus loin : le client veut
   voir l'image arriver, plus de la moitié hors cadre au départ. Sur la grille
   de la maquette, l'image fait 706 et commence à 163 : sa moitié atteint le
   bord de l'écran à 516 de course. 560 la dépasse — il en reste 44 % de
   visible, donc 56 % dehors. Le bloc inversé est symétrique.

   Le décalage porte le texte avec lui : c'est le bloc entier qui glisse, comme
   auparavant, et non l'image seule. */
html.yxo-anim-on .yxo-split.yxo-anim--left {
	transform: translateX(calc(-560 * var(--u)));
}

html.yxo-anim-on .yxo-split.yxo-anim--right {
	transform: translateX(calc(560 * var(--u)));
}

/* En colonne unique l'image tient toute la largeur : 560 la sortirait tout
   entière, et le bloc surgirait de nulle part. 220 en met un peu plus de la
   moitié dehors sur un écran de 390. */
@media (max-width: 1023px) {
	html.yxo-anim-on .yxo-split.yxo-anim--left {
		transform: translateX(calc(-220 * var(--u)));
	}

	html.yxo-anim-on .yxo-split.yxo-anim--right {
		transform: translateX(calc(220 * var(--u)));
	}
}

html.yxo-anim-on .yxo-anim--up {
	transform: translateY(var(--yxo-rise, calc(44 * var(--u))));
}

/* Le titre « OUR SERVICES » glisse depuis la gauche sur une course plus
   longue que les autres entrées : c'est un glissement franc, pas un simple
   décalage. La règle de remise à zéro `.yxo-anim.is-in` reste plus spécifique,
   elle l'emporte donc bien à l'arrivée. */
html.yxo-anim-on .yxo-services__head {
	transform: translateX(calc(-150 * var(--u)));
}

/* OUR SERVICES : chaque colonne part de la position qu'elle occupe dans la
   diagonale de la maquette (pas de 48 px) et remonte jusqu'à l'alignement.

   Le décalage passe par une variable et non par `transform` : une règle
   `…__grid .yxo-service:nth-child(n)` serait plus spécifique que la remise à
   zéro `.yxo-anim.is-in`, qui ne s'appliquerait alors jamais et laisserait la
   diagonale en place à la fin de l'animation. */
.yxo-services__grid .yxo-service:nth-child(1) { --yxo-rise: calc(44 * var(--u)); }
.yxo-services__grid .yxo-service:nth-child(2) { --yxo-rise: calc(92 * var(--u)); }
.yxo-services__grid .yxo-service:nth-child(3) { --yxo-rise: calc(140 * var(--u)); }
.yxo-services__grid .yxo-service:nth-child(4) { --yxo-rise: calc(188 * var(--u)); }

/* Note : la section n'est plus épinglée et les colonnes ne sont plus pilotées
   par le défilement. Elles entrent automatiquement, l'une après l'autre, dès
   que la section arrive à l'écran — voir la cascade d'entrée plus bas et
   `CASCADE_MS` dans theme.js. La section retrouve donc la hauteur de 771 px
   relevée dans le Figma. */

/* Les quatre colonnes de OUR SERVICES montent 2 s plus lentement que le
   reste : 900 ms + 2 s. C'est la course de chaque colonne qui s'allonge, pas
   l'écart entre deux départs — `CASCADE_MS` dans theme.js tient ce décalage et
   ne bouge pas. Le titre de la section garde lui aussi son tempo. */
html.yxo-anim-on .yxo-services__grid .yxo-anim {
	transition-duration: 2900ms;
}

html.yxo-anim-on .yxo-anim.is-in {
	opacity: 1;
	transform: none;
}

@media print {
	.yxo-anim {
		opacity: 1 !important;
		transform: none !important;
	}
}

/* ------------------------------------------------------------ Éditeur --- */

/* Dans l'éditeur, l'en-tête fixe et les voiles ne doivent pas gêner. */
.editor-styles-wrapper .yxo-header {
	position: relative;
}

.editor-styles-wrapper .yxo-nav {
	display: none;
}
