/* ── Here — socle partagé : header, menu, repères carrés. Extrait sans modification
   du prototype validé (Home). Aucune valeur en dur : tout vient de tokens.css. ── */

  /* ── PROTOTYPE Home — intro « portail » AUTOMATIQUE puis scrollytelling GSAP. Charte Here. ── */
  *,*::before,*::after{box-sizing:border-box;margin:0}
  body{background:var(--color-bg); color:var(--color-text); font-family:var(--font-body);
    font-weight:var(--fw-body); letter-spacing:var(--ls-body); -webkit-font-smoothing:antialiased; overflow-x:clip}

  /* Scroll verrouillé pendant l'intro auto (levé dès que la vidéo est plein écran)
     et pendant l'ouverture du menu */
  html.is-intro, html.is-intro body{overflow:hidden; height:100%; touch-action:none}

  /* Repère carré de la charte (header, séparateurs de menu, pied de menu) */
  /* Le repère carré de la charte. En `rem` depuis le 30/08/2026 : 0,5625rem
     valent exactement 9px au corps de référence, et le repère grandit avec le
     reste du dessin sur les grands écrans. Écrit en pixels, il devenait un
     grain de poussière à côté d'un titre mis à l'échelle. */
  .mark{display:block;width:.5625rem;height:.5625rem;background:currentColor}

  /* Header fixe (apparaît en fin d'intro) */
  .site-head{position:fixed; inset:0 0 auto 0; z-index:90; opacity:0;
    display:grid; grid-template-columns:1fr auto 1fr; align-items:center;
    padding:clamp(1.1rem,2.2vw,1.9rem) var(--gutter);
    color:var(--color-text-invert); mix-blend-mode:difference}
  .wordmark{font-family:var(--font-display); font-weight:var(--fw-display);
    font-size:clamp(1.1rem, .9rem + .7vw, 1.6rem); text-transform:uppercase;
    letter-spacing:var(--ls-wordmark-compact); padding-left:var(--ls-wordmark-compact); justify-self:start}
  /* Repère du header : carré (maquette Figma), pas un rond */
  .head-dot{width:.4375rem;height:.4375rem;background:currentColor;justify-self:center}
  /* sur le vert du menu, le header repasse en blanc franc (le mode difference sert au hero) */
  body.head-plain .site-head{mix-blend-mode:normal;color:var(--color-text-invert)}
  .nav{justify-self:end;display:flex;align-items:center;gap:clamp(1rem,2vw,2.2rem)}
  .nav a{color:currentColor;text-decoration:none;font-size:var(--fs-label);
    text-transform:uppercase;letter-spacing:var(--ls-label);transition:opacity var(--dur-fast) var(--ease-out)}
  .nav a:hover{opacity:.6}
  /* Icône menu : fermée 20 / 13 (calée à gauche) / 20 ;
     ouverte 20 / 9 calée à droite + le point-carré qui apparaît au bout du filet du bas. */
  .menu-btn{position:relative;display:flex;flex-direction:column;gap:4px;background:none;border:0;
    cursor:pointer;padding:6px 0;color:currentColor;width:20px}
  .menu-btn span{display:block;height:1.5px;background:currentColor;
    transition:width var(--dur-base) var(--ease-in-out), transform var(--dur-base) var(--ease-in-out)}
  .menu-btn span:nth-child(1){width:20px}.menu-btn span:nth-child(2){width:13px}.menu-btn span:nth-child(3){width:20px}
  .menu-btn i{position:absolute;right:-6px;bottom:6px;width:3px;height:3px;background:currentColor;
    opacity:0;transform:scale(.2);
    transition:opacity var(--dur-fast) var(--ease-out) 120ms, transform var(--dur-base) var(--ease-out) 120ms}
  /* ⚠️ LE GESTE DE L'ICÔNE SE JOUE AU SURVOL, PAS À L'OUVERTURE (22/08/2026).
     Il était accroché à `body.is-open` : l'icône changeait de dessin une fois le
     menu ouvert, c'est-à-dire au moment où plus personne ne la regarde — l'aplat
     vert occupe tout l'écran. Guillaume : « je veux que cette animation se
     produise uniquement au survol, et que ça redevienne normal dès que la souris
     part. » Le geste devient donc une réponse au doigt qui approche, pas un
     état.

     ⚠️ `@media (hover:hover)` n'est pas une précaution de style : sur un écran
     tactile, le navigateur SIMULE un survol au toucher et ne le retire qu'au
     toucher suivant, ailleurs. Sans ce garde-fou, l'icône resterait figée dans
     son dessin d'ouverture après chaque tape.

     `:focus-visible` reçoit le même dessin : au clavier il n'y a pas de survol,
     et l'anneau seul ne dit pas que le bouton répond. */
  @media (hover: hover) and (pointer: fine) {
    .menu-btn:hover span:nth-child(2){width:9px;transform:translateX(11px)}
    .menu-btn:hover i{opacity:1;transform:scale(1)}
  }
  .menu-btn:focus-visible span:nth-child(2){width:9px;transform:translateX(11px)}
  .menu-btn:focus-visible i{opacity:1;transform:scale(1)}

  /* ── MENU — aplat vert qui se referme depuis les 4 bords, entrées centrées ──
     Volets de 50.4 % (léger recouvrement) : à la fin, le vert couvre toute la page.

     ⚠️ 16/08/2026 — GÉOMÉTRIE REMISE À L'ORIGINE. Les volets avaient été élargis
     (débord de 2 px, `calc(50% + 1px)`) pour supprimer un liseré pâle sur le bord
     de l'écran. Ce liseré-là était réel et mesuré, mais ce n'était PAS ce que
     Guillaume voyait, et l'élargissement n'a rien changé pour lui. On ne garde
     pas une complication qui ne corrige rien de constaté. */
  .veil{position:fixed;inset:0;z-index:80;pointer-events:none;visibility:hidden}
  .veil.is-active{pointer-events:auto;visibility:visible}
  .veil__p{position:absolute;background:var(--color-surface-green);will-change:transform}
  .veil__p--t{top:0;left:0;width:100%;height:50.4%;transform-origin:top center;transform:scaleY(0)}
  .veil__p--b{bottom:0;left:0;width:100%;height:50.4%;transform-origin:bottom center;transform:scaleY(0)}
  .veil__p--l{top:0;left:0;height:100%;width:50.4%;transform-origin:left center;transform:scaleX(0)}
  .veil__p--r{top:0;right:0;height:100%;width:50.4%;transform-origin:right center;transform:scaleX(0)}

  /* ⚠️ `grid-template-columns:minmax(0,1fr)` — 30/08/2026. Sans piste déclarée,
     la colonne est en `auto`, donc dimensionnée sur son contenu : la liste
     prenait la largeur de son plus long libellé, même quand celui-ci dépassait
     la fenêtre. Mesuré en 390 px : « Lofts and Art Residences » faisait 465 px,
     la liste aussi, et TOUTES les entrées s'en trouvaient poussées vers la
     droite — y compris les courtes, qui tenaient pourtant largement. Le `minmax`
     borne la piste à la fenêtre ; `place-items:center` continue de centrer la
     liste dedans, donc le rendu ne change pas là où la place ne manque pas. */
  /* ── LA VIGNETTE DU SURVOL ──────────────────────────────────
     Un CARRÉ unique, centré sur la PAGE et non sur la liste : il ne bouge jamais,
     seul son contenu change avec l'entrée survolée. Posé à 82, entre l'aplat vert
     (80) et les entrées (85) — c'est ce que « sous les valeurs du menu » veut
     dire : en profondeur, pas en dessous.

     UN SEUL NOMBRE dit sa taille, `--vign-cote`, et il est écrit en `rem` : le
     côté suit ainsi l'échelle du dessin sur les grands écrans, comme le repère
     carré et le germe de l'intro. 40rem = 640px au corps de référence, soit
     quatre fois le côté de la vignette de l'intro (160px). Valeur choisie par
     Guillaume au banc le 07/09/2026.

     ⚠️ `visibility` et non `display` : la vignette doit exister dans la mise en
     page AVANT l'ouverture. Créée au premier survol, elle ferait naître ses trois
     couches de composition en plein geste — le ressaut du piège 6 de
     `here-hero-intro.md`. Elle est là en permanence, simplement invisible.

     ⚠️ LE BORNAGE EST DANS LA LARGEUR, PAS DANS DEUX PLAFONDS — mesuré le
     07/09/2026. La première écriture posait `width:640px` avec un `max-width` ET
     un `max-height` : sur un écran bas (1440 × 700 mesurés), le plafond de
     hauteur rabotait la hauteur à 617px pendant que la largeur restait à 640, et
     `aspect-ratio` ne pouvait rien contre une largeur explicite. La vignette
     rendait 640 × 617 — un rectangle, alors que le carré est la demande même.
     Un seul `min()` sur la LARGEUR, qui prend déjà la hauteur disponible en
     compte, et `aspect-ratio` n'a plus qu'à suivre : le carré rétrécit, il ne
     s'aplatit jamais. */
  .vign{position:fixed;left:50%;top:50%;translate:-50% -50%;z-index:82;
    width:min(var(--vign-cote),
              calc(100vw - 2*var(--gutter)),
              calc(100vh - 2*var(--gutter)));
    aspect-ratio:1/1;
    overflow:hidden;pointer-events:none;visibility:hidden}
  .vign.is-active{visibility:visible}

  /* Le film. `object-fit:cover` : un film 16/9 dans un carré se recadre par les
     côtés, jamais de bandes.
     ⚠️ LE FONDU PORTE SUR LA `<video>` ELLE-MÊME, et c'est délibéré. Le piège 6
     de `here-hero-intro.md` dit qu'un fondu sur un élément CONTENANT une vidéo
     crée puis détruit une couche de composition — deux ressauts, un à chaque
     bout. Ici les trois films gardent leur couche pendant toute la durée
     d'ouverture du menu : elle est créée une fois à l'ouverture, détruite une
     fois à la fermeture, et le survol ne la paie jamais. */
  .vign__f{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
    opacity:0;transition:opacity var(--vign-duree) var(--ease-out)}
  .vign__f.is-on{opacity:1}

  /* LE VOILE — du vert de marque posé SUR le film. Les entrées sont blanches et
     se perdaient sur une image claire ; à 0,60 elles se lisent partout. Il vit
     DANS la vignette : l'aplat, lui, reste le vert franc de la charte.

     ⚠️ IL EST SOLIDAIRE DU FILM, PAS DE L'OUVERTURE DU MENU — et c'est ce qui
     rend la vignette sûre. Le voile est un aplat OPAQUE à 60 % : accroché à la
     seule visibilité du conteneur, il suffisait qu'un `is-active` traîne — une
     timeline tuée par une réouverture rapide, un rappel de fin non joué — pour
     qu'un carré vert reste planté au milieu de la page. Accroché à `is-on`, il
     ne peut se voir que là où un film se voit déjà : le défaut n'a plus de
     chemin pour arriver. */
  .vign__voile{position:absolute;inset:0;background:var(--color-surface-green);
    opacity:0;pointer-events:none;transition:opacity var(--vign-duree) var(--ease-out)}
  .vign.is-on .vign__voile{opacity:var(--vign-voile)}

  /* Les trois réglages en un seul endroit — c'est ici que le banc écrit. */
  :root{--vign-cote:40rem; --vign-duree:.42s; --vign-voile:.6}

  .menu{position:fixed;inset:0;z-index:85;visibility:hidden;color:var(--color-text-invert);
    display:grid;grid-template-columns:minmax(0,1fr);place-items:center;
    padding-inline:var(--gutter)}
  .menu.is-active{visibility:visible}
  /* `max-width:100%` : la piste est bornée, encore faut-il que la liste accepte
     de l'être. Sans lui elle reste dimensionnée sur son contenu et déborde. */
  .menu__list{display:flex;flex-direction:column;align-items:center;max-width:100%;
    gap:clamp(.7rem,1.7vh,1.35rem);text-align:center}
  /* ⚠️ `overflow:hidden` retiré le 15/08/2026 : il servait à couper le libellé
     pendant qu'il montait de sous sa ligne. Les entrées ne montent plus — elles
     paraissent avec le vert — et ce reste rognait l'anneau de focus, qui est
     dessiné 6 px EN DEHORS de l'entrée. */
  /* `max-width:100%` + `min-width:0` : une entrée est un enfant de flex, donc
     dimensionnée sur son contenu et libre de dépasser son parent. Les deux
     ensemble l'obligent à tenir dans la liste — c'est ce qui rend le retour à la
     ligne du libellé effectif, plus bas, sous 860 px. */
  .menu__item{display:block;text-decoration:none;color:currentColor;
    max-width:100%;min-width:0;
    transition:opacity var(--dur-base) var(--ease-out)}
  .menu__label{font-family:var(--font-display);font-weight:var(--fw-display);text-transform:uppercase;
    font-size:clamp(1.5rem, .45rem + 3.9vw, 4.5rem);line-height:1.12;letter-spacing:.03em;
    display:block;white-space:nowrap}
  body.is-open .menu__label, html.arrive .menu__label{will-change:opacity}
  .menu__list:hover .menu__item{opacity:.42}
  .menu__list .menu__item:hover{opacity:1}
  /* Le conteneur reçoit le focus à l'ouverture : il ne doit rien dessiner.
     L'anneau reste sur les entrées, où il sert vraiment — à la tabulation. */
  .menu:focus, .menu:focus-visible{outline:none}
  .menu__list .menu__item:focus-visible{opacity:1;outline:2px solid var(--color-text-invert);outline-offset:6px}
  .menu__sep{opacity:0}
  .menu__foot{position:fixed;left:50%;translate:-50% 0;bottom:clamp(1.6rem,4vh,2.6rem);z-index:85;
    opacity:0;visibility:hidden;color:var(--color-text-invert)}
  .menu__foot.is-active{visibility:visible}

  /* Sans GSAP / reduced-motion : bascule directe, pilotée par CSS */
  .no-tween .veil__p{transition:transform var(--dur-slow) var(--ease-in-out)}
  .no-tween body.is-open .veil__p--t, .no-tween body.is-open .veil__p--b{transform:scaleY(1)}
  .no-tween body.is-open .veil__p--l, .no-tween body.is-open .veil__p--r{transform:scaleX(1)}
  .no-tween body.is-open .menu__label{opacity:1}
  .no-tween body.is-open .menu__sep, .no-tween body.is-open .menu__foot{opacity:1}

  /* ── SORTIE DE MENU « la reprise » ────────────────────────────────────────
     On quitte une page par le menu : les mots s'effacent, le vert reste, la
     navigation se fait DERRIÈRE l'aplat. La page d'arrivée le retrouve fermé et
     le rouvre — le geste d'ouverture du menu, joué à l'envers.

     La classe est posée dans <head>, avant le premier rendu : sans cela on
     verrait un éclair de la page d'arrivée avant que le vert ne se replace.
     `transition:none` parce qu'en repli CSS (.no-tween) les volets sont animés
     par transition : sans ce garde-fou, ils se fermeraient à l'arrivée au lieu
     d'y être déjà. */
  html.arrive .veil__p{transition:none}
  html.arrive .veil__p--t, html.arrive .veil__p--b{transform:scaleY(1)}
  html.arrive .veil__p--l, html.arrive .veil__p--r{transform:scaleX(1)}
  /* `arrive-part` : le repli sans GSAP. La classe qui tenait les volets fermés
     disparaît, la transition CSS fait le reste — mais le voile doit rester
     visible jusqu'au bout du mouvement. */
  html.arrive .veil, html.arrive-part .veil{visibility:visible}

  /* ⚠️ LA LIGNE BLANCHE D'UNE IMAGE, TOUT EN HAUT — signalée par Guillaume le
     15/08. Quand le navigateur remplace un document par un autre, il peint
     d'abord la TOILE du nouveau document — le fond de page, papier #FCFCFC —
     puis, l'image suivante, l'aplat vert par-dessus. Pendant cette image-là une
     bande de papier reste visible en haut de la fenêtre : mesuré (156,157,158)
     à y=0 sur toute la largeur, sur une seule capture, puis plus rien.
     Le test de Guillaume donne la preuve : cliquer la rubrique où l'on est déjà
     ne change pas de document (le menu se referme, c'est tout) — aucune toile
     repeinte, aucune ligne. Cliquer une autre rubrique : elle revient.
     Remède : pendant le rideau, la toile est verte. Si le navigateur la peint
     avant l'aplat, il peint du vert sur du vert. Sur `html` seulement — le corps
     garde son papier, donc aucune section sans fond propre ne peut virer au vert
     pendant la fin de l'ouverture. */
  html.arrive, html.arrive-part{background:var(--color-surface-green)}

  /* ── LA BASCULE DE LANGUE : UN FONDU, PAS UN RIDEAU ──────────────────────
     22/08/2026. Guillaume : « on a l'impression que tu recharges tout le site
     quand je clique sur EN/FR. » C'était exact, et ce n'était pas un défaut de
     code : `/` et `/en/` sont deux documents — la bascule EST un chargement de
     page. Partout ailleurs on ne le voit pas, parce que le rideau vert du menu
     couvre l'échange ; le lien de langue était le SEUL endroit du site où une
     navigation n'était couverte par rien. D'où la coupure franche : trois
     dixièmes de seconde d'immobilité, puis tout l'écran change d'un coup.

     On ne remet pas le rideau vert : c'est la signature du menu, et changer de
     langue est une opération de service, pas un événement. Un fondu court
     suffit — 180 ms pour partir, 220 ms pour revenir.

     ⚠️ Les deux moitiés ne sont pas écrites de la même façon, et c'est
     volontaire. Le départ est une TRANSITION déclenchée en JavaScript : il faut
     bien retarder la navigation pour qu'on ait le temps de voir le fondu. Le
     retour est une ANIMATION CSS pure : la page arrive à l'opacité 0, et rien
     — pas même un script en échec — ne peut la laisser invisible, puisque
     c'est le navigateur seul qui joue la remontée. Une page blanche définitive
     pour un clic sur « EN » serait un prix bien trop élevé.
     `both` garde l'état final ; l'opacité revenue à 1 ne crée aucun contexte
     d'empilement, donc rien ne change pour l'en-tête fixe ni pour le voile. */
  /* ── ET FINALEMENT : PLUS AUCUN EFFET. « Je veux l'effet en brut »
     (Guillaume, 23/08/2026). Il y a eu ici, successivement, un fondu de départ
     (`part-langue`), un fondu d'arrivée, puis une baisse d'encre pendant
     l'échange (`langue-echange`). Les trois sont partis, et c'est le bon
     aboutissement : quand l'échange devient réellement instantané — les mots
     sont déjà là quand on appuie, voir le pré-chargement dans langue.js — il
     n'y a plus rien à couvrir. Une transition ne sert qu'à masquer une attente ;
     sans attente, elle ne fait qu'ajouter de l'attente.

     Ce qui reste de `arrive-langue` est une CLASSE SANS STYLE ici, et elle a
     toujours deux emplois : sauter la séquence d'arrivée sur l'accueil
     (home.js), et neutraliser la transition de page native sur le repli
     (pages.css). Ne pas la supprimer en croyant nettoyer. */

  /* ⚠️ LES MOTS SONT REPEINTS SUR LA PAGE D'ARRIVÉE. Ils ne peuvent pas
     s'éteindre sur la page de départ (le vert n'y bouge pas) ni sur une page
     qui n'existe pas encore : la seule façon qu'ils partent AVEC le rideau est
     de les redessiner ici, à l'identique, et de les éteindre pendant
     l'ouverture. Le menu n'est pas cliquable : il n'est plus là pour ça. */
  html.arrive .menu, html.arrive-part .menu,
  html.arrive .menu__foot, html.arrive-part .menu__foot{visibility:visible}
  html.arrive .menu, html.arrive-part .menu{pointer-events:none}
  html.arrive .menu__label, html.arrive .menu__sep, html.arrive .menu__foot{opacity:1}
  html.arrive-part .menu, html.arrive-part .menu__foot{opacity:0;
    transition:opacity var(--dur-slow) var(--ease-in-out)}
  html.arrive-fondu .menu, html.arrive-fondu .menu__foot{opacity:0;
    transition:opacity 140ms linear}

  /* ⚠️ L'EN-TÊTE PENDANT L'OUVERTURE. La page d'arrivée impose déjà SA couleur :
     sur une page claire, c'est de l'encre sombre — posée sur l'aplat vert, donc
     illisible pendant les sept dixièmes de seconde du rideau. On lui rend
     l'aspect qu'il avait à l'instant du clic, blanc et sans mélange ; il
     retrouve sa couleur de page à la fin, par la transition de 420 ms déjà
     prévue en §06. L'en-tête ne bouge donc pas d'un pixel d'une page à l'autre. */
  html.arrive .site-head, html.arrive-part .site-head{mix-blend-mode:normal}
  html.arrive body.head-static .site-head,
  html.arrive-part body.head-static .site-head{color:var(--color-text-invert)}

  /* Mouvement refusé : pas de rideau, un fondu court — l'enchaînement reste
     lisible, le mouvement disparaît. */
  html.arrive-fondu .veil{opacity:0;transition:opacity 140ms linear}

  /* ── LE REPÈRE DE VERSION (mise au point seulement) ───────────────────────
     Au-dessus de tout, y compris de l'aplat vert : il doit être lisible même
     menu ouvert, sinon il ne sert à rien pendant qu'on règle le menu. Jamais
     cliquable. Disparaît entièrement avec Site::AFFICHER_VERSION. */
  .repere-version{position:fixed;left:6px;bottom:4px;z-index:9999;pointer-events:none;
    font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:10px;line-height:1;
    letter-spacing:.04em;color:#FCFCFC;mix-blend-mode:difference;opacity:.55}
  @media print{ .repere-version{display:none} }


  @media (max-width:860px){
    /* ⚠️ Cette règle était SANS EFFET jusqu'au 30/08/2026, et c'est instructif :
       autoriser le retour à la ligne ne sert à rien s'il n'existe aucun endroit
       où couper. Le libellé est découpé en un `<span>` par signe pour la
       chorégraphie, et l'espace entre les mots était une espace INSÉCABLE —
       le seul point de coupure du libellé était donc explicitement interdit.
       Corrigé dans `menu.php`, qui pose désormais une espace ordinaire.
       Il fallait aussi que la liste et les entrées acceptent d'être bornées :
       voir les deux commentaires plus haut. Les trois ensemble, pas moins. */
    .menu__label{white-space:normal}          /* les libellés longs passent sur 2 lignes */
    /* Un libellé sur deux lignes doit rester lisible comme un bloc, pas comme
       deux entrées voisines : on resserre l'interligne du display. */
    .menu__label{line-height:1.06}
  }
  @media (max-width:700px){
    .hero-word{ --hgap:1.6em }
    /* ⚠️ `.nav a{display:none}` a été RETIRÉ le 14/08/2026.
       Il masquait « réserver. » et « contact. » sous 700 px — or ce sont les deux
       seuls appels à l'action du site, et le menu ne les propose pas : sur
       téléphone, écrire à Here n'était plus possible que par le lien du pied de
       page. Mesuré : les deux libellés tiennent dès 360 px de large. On resserre
       simplement l'espacement pour leur laisser la place. */
    .nav{ gap:.9rem }
    .nav a{ letter-spacing:.14em }
    /* Le repère carré central est décoratif (aria-hidden) et l'en-tête est une
       grille 1fr / auto / 1fr : une fois les deux liens rendus, la colonne de
       droite grossit, la colonne du milieu se décale et le carré venait coller
       le « R » de RÉSERVER. On le retire sur téléphone plutôt que de rogner les
       liens — c'est le seul des trois éléments qui ne sert à rien. */
    .head-dot{ display:none }
    /* Le carré retiré, il ne reste que deux éléments : on repasse la grille à
       deux colonnes et on impose une gouttière, sinon le wordmark et le premier
       lien se touchent — « HERE.RÉSERVER. ». */
    .site-head{ grid-template-columns:auto 1fr; column-gap:1.2rem }
  }
  /* ⚠️ 22/08/2026 — le sélecteur de langue est arrivé dans l'en-tête : trois
     liens et un bouton là où il y en avait deux. Mesuré en 360 px, la barre
     débordait de 5 px (le bouton du menu sortait du cadre). On resserre l'écart
     et l'interlettrage plutôt que de masquer un lien : « EN » ne coûte que
     19 px, ce n'est pas lui qui pose problème, c'est l'espacement des trois. */
  @media (max-width:420px){
    .nav{ gap:.62rem }
    .nav a{ letter-spacing:.10em }
  }
  @media (max-width:355px){
    /* En dessous, on garde l'action et on lâche le doublon : « réserver. » et
       « contact. » mènent aujourd'hui à la MÊME page (voir Site::ROUTES), donc
       en masquer un ne retire aucune destination. */
    .nav{ gap:.75rem }
    .nav a:first-child{ display:none }
  }

  @media (prefers-reduced-motion:reduce){
    *,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important}
  }

  /* Lien d'évitement — hors de l'écran, ramené au premier coup de tabulation. */
  .saut{position:fixed;left:var(--gutter);top:-4rem;z-index:200;
    background:var(--color-text);color:var(--color-text-invert);
    padding:.7rem 1.1rem;font-size:var(--fs-label);text-transform:uppercase;
    letter-spacing:var(--ls-label);text-decoration:none;
    transition:top var(--dur-fast) var(--ease-out)}
  .saut:focus-visible{top:var(--gutter);outline:2px solid var(--color-focus);outline-offset:3px}
  #contenu:focus{outline:none}
