/* ==========================================================================
   LE CHROME DE L'ARBRE `/` — marque, boussole, accès rapides, décor.
   Posé le 23/08 sur décision PO (« le boulot maintenant c'est de contribuer
   la version Nu ; on commence par logo, navigation, plan »).

   ⚠️ CE FICHIER N'INVENTE RIEN. Chaque bloc est repris de `calques.css`, où
   il était lui-même une COPIE VERBATIM de la règle du site. Ce qui change ici
   est UNIQUEMENT le déclencheur : `calques.css` gate chaque calque derrière
   une lettre de `<html data-c="…">` — c'était un harnais de mesure, une
   lettre par palier. Les quatre calques ci-dessous ont passé leur palier, ils
   n'ont plus à être gatés : ils sont le chrome.

   ── LE MOTIF ÉCRIT ET LE VERDICT PO DATÉ (règle de la reconstruction) ─────
   Verdict du 22/08, consigné dans `suivi.md` : la pièce propre a été
   rechargée de calques UN PAR UN et le PO a validé chaque palier SUR
   L'APPAREIL — **marque fixe → accès rapides → décor → navigation** : les
   quatre passent, captures à l'appui. Ni `position: fixed`, ni
   `env(safe-area-inset-*)`, ni la couleur portée par `<html>`, ni l'embossé
   en z-index négatif, ni la boussole calée aux quatre bords ne posent le
   moindre problème.

   ⛔ CE QUI NE REMONTE PAS, ET C'EST LE CŒUR DU VERDICT : **LES DEUX VOILES**
   (`.voile--haut`, `.voile--bas`). Ils étaient LE coupable — quatre correctifs
   ratés le 09/08, un cinquième le 22/08, sept heures d'hypothèses. Leurs
   règles restent dans `calques.css`, intactes et réactivables en une lettre,
   parce qu'on ne jette pas ce qui a coûté cher à écrire. On cesse de le poser.
   ⛔ NE REMONTE PAS NON PLUS : `nav.css` (12 772 lignes), les metas
   `theme-color` / `color-scheme` de barre (Safari ne les honore pas — mesuré
   deux fois ; ce qui teinte, c'est le `background` de la RACINE, posé par
   `nu-film.js`), l'écran d'attente, la machinerie de préchargement.

   ── OÙ CE FICHIER SE CHARGE, ET POURQUOI LÀ ──────────────────────────────
   Après `nu.css` (il doit battre son `html, body { background: #fff }`, même
   spécificité → c'est l'ordre qui tranche) et AVANT `nu-film.css` (dont le
   `html:has(.ft) { background: transparent }` doit pouvoir faire taire le
   canevas sur une page à film — il gagne de toute façon par spécificité,
   mais l'ordre dit l'intention). `nu-arbre.css`, le contenu, reste dernier.
   ========================================================================== */

/* Les tokens que le chrome consomme — copie de src/styles/tokens.css, réduite
   à ce qui sert ici (reprise telle quelle de calques.css:36-46). */
:root {
    --accent: #b4cd00;
    --fg: #ffffff;
    --fg-muted: rgba(255, 255, 255, 0.55);
    --mobile-grad-top: #58534d;
    --shadow-text: 0 1px 10px rgba(0, 0, 0, 0.22);
    /* Halo COURT : il détache la micro-typo de la boussole de l'embossé,
       sans l'empâter (revue Hélène 08/08). */
    --shadow-text-strong: 0 1px 4px rgba(0, 0, 0, 0.45);
}

/* ==========================================================================
   LE DÉCOR — LE SOL             source : calques.css:207+ / nav.css:56+
   ==========================================================================
   ⚠️ IL VIENT AVEC LE LOGO, ET CE N'EST PAS UN CALQUE DE PLUS QU'ON GLISSE :
   c'est sa DÉPENDANCE. La marque et la boussole du site sont encrées en
   `--fg` (blanc) parce que le sol est taupe DE BOUT EN BOUT. Sans sol, un
   logo blanc en `position: fixed` disparaît dès qu'on défile dans du
   rédactionnel clair — on aurait posé le calque demandé et livré un logo
   invisible. `calques.css` avait la même contrainte et l'a réglée par un
   « ⚖️ ÉCART assumé » (encre sombre tant que `d` est absent) ; ici le décor
   est disponible et validé, on prend la vraie réponse plutôt que l'écart.
   Le décor a son propre verdict PO du 22/08 — il ne se faufile pas, il est
   dû.
   ⚠️ HORS CONDITION DE LARGEUR (desktop, increment 1) : ce bloc vivait sous
   `(max-width: 1023px)` ; au-delà, `nu.css` repeignait html et body en blanc
   sous une encre blanche (1:1). Une seule source pour le sol, à toutes les
   largeurs. Ce qui ne vaut que pour le doigt passe sous `(pointer: coarse)`.
   -------------------------------------------------------------------------- */
html {
    background: var(--mobile-grad-top);
    min-height: 100%;
    -webkit-text-size-adjust: 100%;
}
@media (pointer: coarse) {
    html {
        /* none : l'élastique iOS est coupé — en butée, ce sont NOS gestes qui
           possèdent l'axe (constat iPhone 08/08). */
        overscroll-behavior-y: none;
        /* Barre masquée au doigt seulement : à la souris (et au zoom 200 %),
           la barre classique reste. */
        scrollbar-width: none;
    }
    html::-webkit-scrollbar { display: none; }
}
body { background: none; color: var(--fg); }
body::after {
    content: '';
    /* ⚠️ L'EMBOSSÉ NE MONTE QUE PENDANT UN GLISSEMENT — et c'est toute
       l'histoire de cette règle (PO 28/08, en deux temps).
       ① « il doit rester affiché en sticky, même pendant les transitions
          de navigation ». Défaut prouvé sur l'appareil : le calque du
          geste (`.pane`) est en z-index 5 ET OPAQUE — `elementFromPoint`
          au milieu de l'embossé rendait `pane`, jamais le sol. L'embossé
          s'éteignait donc sur toute la durée du geste, seul élément du
          décor à le faire alors que le logo et la boussole ne bougent
          jamais. Premier correctif : `z-index: 6` en permanence.
       ② « passe les portes au-dessus de l'embossement » — constat du PO
          sur la capture. Le 6 permanent avait un prix caché qu'il a vu
          tout de suite : AU REPOS aussi l'embossé passait devant TOUT le
          contenu (les portes de verre de l'accueil, celles du hub, le
          texte), puisque rien d'autre ne porte de z-index.
       D'où la règle ci-dessous : `-1` AU REPOS — l'apparence validée
       depuis des semaines revient à l'identique, partout, et pas
       seulement sur les portes de l'accueil — et `6` UNIQUEMENT tant
       qu'un calque de glissement existe. On ne relève pas les portes
       au-dessus du décor : on redescend le décor quand il n'a rien à
       survoler. `pointer-events: none` reste dans les deux régimes. */
    position: fixed; z-index: -1; pointer-events: none;
    inset: 0;
    /* TIRAGE ALLÉGÉ (PO 28/08) : le PNG source pesait 623 Ko — la moitié
       de la home — pour un relief de 53 niveaux d'alpha affiché à 0,7
       d'opacité derrière toute la page. Le poids était dans le canal
       alpha (la qualité de couleur n'y changeait rien : 295 Ko à q90
       comme 277 Ko à q75). WebP 1400 px, alpha-q60 → 81 Ko, et la
       comparaison à l'échelle d'affichage réelle (2178 px sur l'iPhone)
       est indiscernable de l'original. Source intacte, tirage
       reconstructible : outils/allege-medias.py.
       165 % à toutes les largeurs ici, comme avant le bureau : la borne à
       ses 1400 px ne vaut qu'à partir de 1024 px (règle `html.nu` plus bas).
       Bornée partout, elle rétrécissait le relief dès 849 px, iPhone Pro
       Max en paysage compris (1577 → 1400 px à 956 de large). */
    background: url(/assets/allege/emboss-crealab-alpha.webp) center bottom 10svh / 165% auto no-repeat;
    opacity: 0.7;
    /* Le fondu sert au masquage sur les pages à film, juste dessous : à
       z-index 6 l'embossé est un calque de chrome, il entre et sort comme
       tel — pas en claquant. */
    transition: opacity 0.28s ease;
}
/* ⚠️ MASQUÉ SUR UNE PAGE À FILM (PO 28/08 : « il doit être masqué sur le
   corner sculpture »). Conséquence directe du passage en z-index 6 : le
   film vit en `.ft-pin { z-index: 0 }`, donc l'embossé — qui était sous
   tout — se peindrait désormais PAR-DESSUS la vidéo. Le décor du sol n'a
   rien à faire sur une image.
   `html:has(.ft)` et non `.page--film` : c'est l'idiome que la feuille du
   film emploie déjà pour faire taire le canevas (nu-film.css:913), et il
   dit la bonne chose — « dès qu'un film est dans le document ». Il couvre
   donc aussi le glissement : le calque entrant porte son `.ft`, l'embossé
   s'efface avant même que le geste commence, et revient en fondu à
   l'arrivée sur une page sans film. */
html:has(.ft) body::after { opacity: 0; }
/* LE SEUL MOMENT OÙ IL MONTE : tant qu'un `.pane` vit dans le document.
   Le calque n'existe QUE pendant un glissement — vérifié dans les deux
   moteurs qui en fabriquent (nu.js:214, nu-gestes.js:277 et :433 le
   retirent tous), et 0 résiduel mesuré sur l'appareil après navigation.
   `:has()` est déjà l'idiome de la maison (`html:has(.ft)` juste au
   dessus) : le décor lit l'état du document, il n'attend pas qu'un
   script pense à le prévenir. */
html:has(.pane) body::after { z-index: 6; }

/* Le relief PNG appartient désormais uniquement à l'expérience mobile.
   Sur bureau, où chaque univers porte déjà son propre décor, il ne doit
   jamais réapparaître au repos ni traverser une transition. */
@media (min-width: 1024px) {
    html.nu body::after { content: none; display: none; }
}
/* L'EMBOSSÉ BORNÉ À SA SOURCE, AU BUREAU SEULEMENT (desktop, increment 1) :
   jamais plus que ses 1400 px. Sous 1024 px, 165 % comme avant (téléphone et
   paysage intacts). `html.nu` : la classe de portée gagne par la
   spécificité, un raccourci `background` plus tardif ne l'écrase pas.
   `not all and (max-width: 1023px)` : le complément EXACT du mobile, sans
   trou entre 1023 et 1024 px CSS (zoom fractionnaire). */
@media not all and (max-width: 1023px) {
    html.nu body::after { background-size: min(165%, 1400px) auto; }
}
h1, div:where(.nu-h1), h2 { text-shadow: var(--shadow-text); }
.sur, .repere { color: var(--fg-muted); }
/* La pane du glissement prend la couleur du sol : blanche, elle ferait un
   flash de papier au milieu d'un site sombre (calques.css:252). */
.pane { background: var(--mobile-grad-top); }

/* ==========================================================================
   LA MARQUE FIXE                source : calques.css:166+ / nav.css:1152+
   ==========================================================================
   Le logo de la home, PARTOUT, en calque fixe — immobile au défilement comme
   pendant les glissements. Le premier `position: fixed` et le premier
   `env(safe-area-inset-top)` de la reconstruction : tous deux validés le
   22/08 (le logo ne saute pas quand Safari replie ses barres).
   -------------------------------------------------------------------------- */
html { --brand-h: calc(56px + min(46vw, 230px) * 0.30); }
.site-brand {
    position: fixed; top: 0; left: 0; right: 0;
    z-index: 8;
    /* 44px de tête : le chevron nord de la boussole vit AU-DESSUS du logo */
    padding: calc(44px + env(safe-area-inset-top)) 20px 12px;
    pointer-events: none;
}
.site-brand a { display: block; width: fit-content; margin: 0 auto; pointer-events: auto; text-decoration: none; }
.sb-logo { display: block; width: min(46vw, 230px); margin: 0 auto; color: var(--fg); }
.sb-logo svg { width: 100%; height: auto; display: block; }
.sb-sig { display: block; width: min(23vw, 118px); margin: -6px auto 0; transform: translateX(26%); color: var(--accent); }
.sb-sig svg { width: 100%; height: auto; display: block; }
/* SUR L'ACCUEIL, LA SIGNATURE EST L'IDENTITÉ (PO 27/08) : « Jocelyn
   Bouget » en texte a quitté l'écran de la home (sr-only, nu-arbre.css) —
   c'est l'écriture manuscrite qui porte le nom, donc elle parle plus fort.
   Scopé par :has sur la page courante : le chrome est FIXE et survit aux
   bascules du routeur — à la sortie de la home, la classe disparaît et la
   transition la ramène en douceur à sa taille de partout (demande PO :
   « attention aux transitions vers les autres pages »). */
.sb-sig { transition: width 0.45s ease, transform 0.45s ease; }
/* 2e cran PO : encore plus grande, et CENTRÉE — le translateX(26%) qui la
   cale sous la queue du logo partout ailleurs revient en douceur à la
   sortie de la home. */
/* Calage OPTIQUE sur le logo (PO 27/08, deux temps) : la barre du t de
   « Bouget » doit tomber ENTRE le A et le B de CREA_LAB — le centrage
   géométrique la laissait sous le A. */
/* ET LES CORNERS L'ADOPTENT (PO 28/08) : le tier sous l'accueil — les deux
   hubs et leurs corners — porte la MÊME grande signature que la home. Pas
   les fiches : là, c'est la pièce qui doit dominer, la marque redevient la
   petite calée sous la queue du logo. La transition de largeur posée plus
   haut joue donc aussi de corner à fiche.
   ⚠️ AUCUNE RÉSERVE À AJOUTER, et c'est mesuré, pas supposé : la signature
   grandie descend à 121 px, `--brand-h` en réserve 117 — mais `.nu-corps`
   pose 40 px de padding par-dessus, donc le premier texte est à 157 px et
   rien ne se touche. On NE surcharge PAS `--brand-h` : interdit d'archi
   (univers-galerie.css:28), et il pilote aussi le héros des films et la
   géométrie des fiches. Sur un corner à FILM, `.page--film` annule de toute
   façon la réserve — la marque s'y pose par-dessus, comme sur la home. */
html:has(.page--accueil) .sb-sig,
html:has(.page--corner) .sb-sig { width: min(44vw, 220px); transform: translateX(18px); }
/* ⚠️ LA MARQUE S'EFFACE PENDANT LE DÉFILEMENT — correctif du 23/08, sur
   captures PO : `CREA_LAB` s'affichait EN DOUBLE, le logo fixe posé sur le
   titre de la page. Au repos il n'y a pas de conflit (`.page` réserve
   `--brand-h`) ; c'est en défilant que le rédactionnel passe dessous.
   Sur le site, c'était le VOILE DU HAUT qui effaçait ce qui passe derrière la
   marque — et les voiles sont hors reconstruction (décision PO du 22/08,
   c'étaient eux le coupable). On prend donc le mécanisme que la boussole
   utilise DÉJÀ, à la valeur près : `html.is-scrolling`, posé par
   `nu-gestes.js`, et 0.3 (0.12 rendait la boussole invisible pendant l'acte
   même de naviguer — même arbitrage ici).
   ⚠️ CE N'EST PAS UN CALQUE DE PLUS : aucun élément nouveau, aucune couleur
   nouvelle, une propriété sur un élément qui existe. Réversible en une ligne
   si le PO préfère rappeler le voile. */
.site-brand { transition: opacity 0.35s ease; }
html.is-scrolling .site-brand { opacity: 0.12; }
/* ⚠️ ET ELLE RESTE EFFACÉE TANT QU'ON N'EST PAS REVENU EN HAUT — second cran,
   28/08, mesuré au navigateur pendant la validation du hub Boutique.
   `is-scrolling` tombe 500 ms après l'ARRÊT du geste : la marque revenait
   donc à pleine opacité (0,85 relevé) juste au moment où l'on s'arrête pour
   LIRE — et le logo, plus la grande signature verte, se reposaient en travers
   du rédactionnel. Constaté sur le hub (« la fabrication, elle, est une
   machine » barré par la signature) et sur Qui suis-je (« 16 ans d'expérience
   en tant que » illisible) : c'est TOUT l'arbre qui était concerné, pas une
   page — la home ne le montrait pas, faute d'assez de texte à faire défiler.
   La bonne condition n'est donc pas le MOUVEMENT mais la POSITION.
   `en-haut` est posée par `etat()` (nu-gestes.js) et se mesure au PLANCHER
   publié, pas à zéro : rien de neuf à câbler, et sur une page qui tient dans
   l'écran (la home) elle ne quitte jamais le haut — la marque y reste pleine,
   comme validé.
   ⚠️ SCOPÉ PAR `.defilable`, ET C'EST LE REPLI SANS JS : les deux classes
   sont posées par le même `etat()`. Script coupé, aucune des deux n'existe,
   `html:not(.en-haut)` seul aurait matché en permanence et effacé la marque
   sur toute page. Avec `.defilable` en tête, pas de JS = pas de règle = la
   marque reste pleine, comme aujourd'hui. */
html.defilable:not(.en-haut) .site-brand { opacity: 0.12; }
/* ⚠️ ET ELLE CESSE D'ÊTRE CLIQUABLE EN MÊME TEMPS QU'ELLE S'EFFACE.
   Constat PO 30/08 : « les CTA de sculptures deviennent non cliquables quand
   la vidéo du dessous commence à passer par-dessus ». Mesuré à
   `elementFromPoint` sur le CTA « Voir Dentelle de près », scroll par scroll :
   ce n'est PAS la vidéo — le correctif du 23/08 tient, `.ft`, `.ft-pin` et
   `.ft-flux` ne reçoivent rien. C'est **le lien de la marque** : à 1 700 px le
   tap est reçu par `SPAN.sb-logo`, à 1 600 px par un `path` de la signature.
   `.site-brand` est bien en `pointer-events: none`, mais son `<a>` repasse à
   `auto` (il doit rester le chemin du retour) et la marque vit à z-index 8,
   au-dessus de tout. Un cartel de film qui remonte traverse donc une bande
   morte de ~100 px de haut sur 200 de large, au centre du chrome.
   La cause a grandi le 28/08, quand les corners ont adopté la GRANDE
   signature de l'accueil : la zone cliquable a suivi.
   La règle : une marque à 12 % d'opacité n'est plus un contrôle, c'est du
   décor — et le décor ne reçoit rien (c'est déjà ce que dit nu-film.css du
   film). MÊME CONDITION que l'effacement, écrite juste en dessous pour
   qu'elles ne puissent pas diverger : tant qu'on est en haut, la marque est
   pleine ET cliquable ; dès qu'on descend, elle s'efface ET se retire.
   ⚠️ Conséquence assumée : on ne peut plus taper le logo pour rentrer quand on
   est au milieu d'une page. Le chevron ouest, le plan et le pied y mènent
   toujours — et taper un logo qu'on distingue à peine n'était pas un geste. */
html.defilable:not(.en-haut) .site-brand a { pointer-events: none; }

/* ==========================================================================
   LES ACCÈS RAPIDES             source : calques.css:192+ / nav.css:295+
   ==========================================================================
   La « tab bar » du PO : deux pictogrammes flottants, SANS fond de barre
   (décision PO 08/08). Leur `bottom` porte l'`env(safe-area-inset-bottom)`
   qui bascule au repliement des barres — validé le 22/08, ils ne sautent pas.
   ⚠️ LE PICTO DE GAUCHE MÈNE À UNE VRAIE PAGE, PAS À UN CALQUE. Le site ouvre
   `PlanOverlay` (122 lignes, un cinquième calque plein écran) ; il n'a pas
   passé de palier, il ne remonte donc pas ici. `/plan/` est une page
   ordinaire — crawlable, sans JS, et elle dit ce qu'elle est. La leçon du
   `hint-defiler` (Base.astro) vaut ici : **un élément qui ment sur ce qu'il
   est, c'est pire que pas d'élément.** L'overlay remontera avec son motif.
   -------------------------------------------------------------------------- */
.qa {
    position: fixed;
    bottom: calc(10px + env(safe-area-inset-bottom));
    z-index: 10;
    display: flex; align-items: center; justify-content: center;
    width: 48px; height: 48px; padding: 12px;
    border: 0; background: none; cursor: pointer;
    color: var(--fg); opacity: 0.85;
    filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.45));
    transition: opacity 0.2s ease, transform 0.15s ease;
}
.qa svg { width: 100%; height: 100%; }
/* Le ✣ grossi sur téléphone (PO 06/10) : la boîte reste à 48 px, à 10 px du
   bord (le créneau du sélecteur de caisse est calculé dessus), seule la marge
   intérieure passe de 12 à 6 px : le dessin fait 36 px au lieu de 24. Le
   bureau a ses propres cotes plus bas, il n'est pas touché. */
.qa--plan { left: 10px; padding: 6px; color: var(--accent); opacity: 1; }
.qa--contact { right: 10px; }
.qa:active { transform: scale(0.9); }

/* ==========================================================================
   LA BOUSSOLE                   source : calques.css:255+ / nav.css:295+
                                          src/components/Compass.astro
   ==========================================================================
   Quatre chevrons aux quatre bords, chacun annonçant le voisin RÉEL du
   graphe. C'est le calque le plus proche des bords de tout le site — plus
   près que la marque, plus près que les pictogrammes. Validé le 22/08 : les
   chevrons entrent dans les bandes réservées sans provoquer de bande, et ne
   sautent pas au repliement.
   -------------------------------------------------------------------------- */
/* ⚠️ NEUTRALISER DEUX ARTEFACTS DE `nu.css`, ET RIEN D'AUTRE. La pièce propre
   style ses liens de contenu par le sélecteur d'élément — `nav { display:flex;
   margin: 28px 0 8px }` et `nav a { border: 1px solid; border-radius: 999px }`.
   La boussole EST un `<nav>` de `<a>` : elle hérite donc de 36 px de vide
   défilable ET d'une capsule autour de chaque chevron, avec les SVG à leur
   taille naturelle. Le site n'a ni l'un ni l'autre — il ne déclare aucune
   règle sur l'élément `nav`. On RÉTABLIT l'état du site, on ne le redessine
   pas.
   ⚠️ ET CES QUATRE LIGNES NE SONT PAS COSMÉTIQUES : sans elles la boussole
   s'affiche en capsules géantes au milieu de la page (constat PO 23/08 15:59,
   après que j'aie tronqué cette feuille par un découpage fautif). */
.compass { display: block; margin: 0; }
.cp { border: 0; border-radius: 0; background: none; }

.cp {
    position: fixed; z-index: 9;   /* au-dessus de la marque fixe (8) */
    display: flex; align-items: center; gap: 3px;
    color: var(--fg); opacity: 0.85;
    text-decoration: none;
    font-size: 0.7rem; font-weight: 700;
    letter-spacing: 0.1em; text-transform: uppercase;
    text-shadow: var(--shadow-text-strong);
    transition: opacity 0.35s ease;
}
/* Le nord vit au-dessus du logo fixe. Cibles tactiles étendues par padding
   (≥44px), visuel inchangé (Hélène). */
.cp--n {
    top: calc(0px + env(safe-area-inset-top));
    left: 50%; transform: translateX(-50%);
    flex-direction: column;
    padding: 8px 18px;
}
.cp--s {
    bottom: calc(8px + env(safe-area-inset-bottom));
    left: 50%; transform: translateX(-50%);
    flex-direction: column;
    padding: 6px 18px;
}
/* Les flancs affichent le NOM du voisin, pas seulement la direction
   (arbitrage PO 08/08 soir : le chevron nu a été essayé puis refusé). */
.cp--w { left: 0; top: 50%; transform: translateY(-50%); padding: 18px 8px 18px 7px; }
.cp--e { right: 0; top: 50%; transform: translateY(-50%); padding: 18px 7px 18px 8px; }
.cp--w span, .cp--e span { writing-mode: vertical-rl; }
.cp--w span { transform: rotate(180deg); }
.cp--n svg, .cp--s svg { width: 30px; height: 12px; }
.cp--w svg, .cp--e svg { width: 12px; height: 30px; }
/* Pendant le défilement la boussole s'efface — 0.3 : discrète mais encore
   repérable (0.12 la rendait invisible pendant l'acte même de naviguer).
   Les deux classes sont posées sur <html> par `nu-gestes.js`. */
html.is-scrolling .cp { opacity: 0.3; }
/* Le sud n'apparaît qu'au bout du contenu — c'est le moment précis où Safari
   a replié ses barres. Déclaré APRÈS .is-scrolling : à spécificité égale,
   ces règles gagnent. */
.cp--s {
    pointer-events: none;
    transform: translateX(-50%) translateY(8px);
    transition: opacity 0.35s ease, transform 0.35s ease;
}
html:not(.at-bottom) .cp--s { opacity: 0; }
html.at-bottom .cp--s {
    opacity: 0.85; pointer-events: auto;
    transform: translateX(-50%) translateY(0);
}

/* ==========================================================================
   LA PLACE QUE LE CHROME PREND DANS LE FLUX
   ==========================================================================
   Deux réservations, reprises de `calques.css` (une par calque) — sans elles
   le rédactionnel se lit SOUS le logo et DERRIÈRE les libellés verticaux de
   la boussole, et on jugerait un défaut qui n'est pas le sien.
   Hors condition de largeur (desktop, increment 1) : au-delà de 1023 px,
   `.page` retombait à 24 px de haut (nu.css) et le titre passait sous la
   marque fixe.
   -------------------------------------------------------------------------- */
/* Le couloir des flancs : le libellé vertical coûte 44 px, le site en
   réserve 50 dans `.pg-main` (calques.css:336). */
.page { padding-left: 50px; padding-right: 50px; }

/* La place de la marque (calques.css:180). */
.page { padding-top: calc(var(--brand-h) + env(safe-area-inset-top)); }

/* ⚠️ SAUF SUR UNE PAGE À FILM, ET C'EST NON NÉGOCIABLE. Consigne PO du
   22/08 : « elle doit déjà être présente et déjà sous les infos iOS ».
   On n'arrive pas DEVANT le film, on arrive DEDANS — le premier bloc
   commence à zéro. Réserver la hauteur du logo le pousserait vers le bas
   et rouvrirait la bande qu'on vient de fermer : le logo se pose PAR-
   DESSUS le film, comme il se pose par-dessus le héros sur le site.
   Le film, lui, ressort du couloir tout seul : `.ft` est en `width:100vw`
   avec `margin-left: calc(50% - 50vw)` (nu-film.css), qui se calcule sur
   la boîte de contenu — il retrouve donc les deux bords physiques quel
   que soit le couloir. */
.page--film { padding-top: 0; }

/* ==========================================================================
   ?filet — LA SONDE DU FILET DE SOL. GRÉEMENT DE RECETTE, à retirer.
   ==========================================================================
   Le filet de sol, le décor et le rédactionnel ont **exactement la même
   couleur** : une bande taupe en haut est donc INDÉCIDABLE. `?filet` met la
   racine des pages sans film en MAGENTA (nu-film.js) et repeint la page en
   taupe — la page a l'air normale, et le magenta ne peut apparaître QUE là
   où aucun élément ne peut peindre.
   -------------------------------------------------------------------------- */
html[data-filet] .nu-corps,
html[data-filet] .nu-retour,
html[data-filet] .vide,
html[data-filet] .ft-pin { background: var(--mobile-grad-top); }

/* ==========================================================================
   ?sonde — LE RELEVE AUTO-RAPPORTE. GREEMENT DE RECETTE, a retirer.
   ==========================================================================
   ⚠️ IL NE TOUCHE AUCUN BORD, ET C'EST UNE LEÇON PAYÉE. Première version :
   posé sous la barre d'état, c'est-à-dire SUR l'objet mesuré. Verdict PO :
   « sans la sonde j'arrive à voir, avec c'est pas simple ». Pire, ses trois
   captures montraient une bande NOIRE alors que la racine était taupe — et le
   seul objet noir de la page était ce bandeau. **Une sonde posée sur ce
   qu'elle mesure ne mesure plus rien.** Elle vit désormais au MILIEU de
   l'écran, loin des deux zones réservées, et en semi-transparent.
   Pour interroger le haut, il y a une sonde dédiée : `?haut` (plus bas).
   -------------------------------------------------------------------------- */
.sonde {
    position: fixed; z-index: 99;
    top: 50%; left: 8px; transform: translateY(-50%);
    max-width: calc(100vw - 16px);
    padding: 6px 8px; border-radius: 3px;
    font: 700 10px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
    letter-spacing: .02em;
    background: rgba(0, 0, 0, .82); color: #b4cd00;
    white-space: pre; overflow: hidden;
    pointer-events: none;
}
.sonde b { color: #fff; font-weight: 700; }
.sonde i { display: inline-block; width: 9px; height: 9px; vertical-align: -1px;
           border: 1px solid #fff; margin-right: 2px; }

/* ⛔ LA SONDE `?haut` A SERVI ET EST RETIRÉE — 23/08. Un bandeau magenta
   opaque de 60 px collé au bord supérieur, pour savoir si la barre
   échantillonne la page. **Verdict : NON** — la barre est restée à la couleur
   de la racine, nette, et le magenta commençait sous elle. L'épaisseur n'y
   était pour rien (le test du 23/08 faisait 2 px, celui-ci 60). ⇒ La loi tient
   et devient ferme : **seule la couleur de la racine atteint les barres.**
   On retire le gréement, on garde le résultat. */

/* ===== L'indice de défilement — porté de la référence /boutique/ ==========
   (nav.css:253, à l'identique ; seule la position s'adapte — nu n'a pas de
   --bottom-reserve, l'indice se pose au-dessus du créneau du chevron sud,
   qui n'apparaît de toute façon qu'en bas de page.)
   Il ne s'affiche que si la page défile ET qu'on est en haut — les deux
   classes sont posées par nu-gestes.js. Invisible = intouchable. */
.hint-defiler {
    position: fixed; z-index: 9;
    left: 50%; transform: translateX(-50%);
    bottom: calc(26px + env(safe-area-inset-bottom));
    width: 48px; height: 48px;
    display: grid; place-items: center;
    padding: 0; border: 0; background: none;
    color: var(--accent, #b4cd00);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.35s ease;
    filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.55));
    cursor: pointer;
}
html.defilable.en-haut .hint-defiler { opacity: 0.9; pointer-events: auto; }
.hint-defiler svg { width: 44px; height: auto; display: block; }
/* Une respiration, pas un clignotement — c'est le CHEVRON qui bouge, jamais
   le bouton : une cible perpétuellement mobile est incliquable (leçon du
   site, confirmée par Playwright lui-même). */
@media (prefers-reduced-motion: no-preference) {
    html.defilable.en-haut .hint-defiler svg { animation: hint-respire 2.4s ease-in-out infinite; }
}
@keyframes hint-respire {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(5px); }
}

/* ==========================================================================
   LA NAV DU BUREAU (≥ 1024 px) : décisions PO du 26/09, 09:20
   ==========================================================================
   ① La boussole reste, mais À L'ÉCHELLE : « minuscule » sur le 32".
   ② L'index s'affiche en permanence à droite (NuIndex.astro), centré en
      hauteur comme le menu de dev, en dedans du chevron est.
   ③ La marque (logo et signature) passe en haut à gauche, comme sur dev.
   Sous 1024 px, RIEN ne change : tout est dans la branche ordinateur, qui est
   le complément EXACT de `(max-width: 1023px)`, sous la portée `html.nu`.

   L'ÉCHELLE (norme fluid-scale du PO) : `--ui-scale` par paliers de largeur,
   `--density` à 1, `--k` = leur produit. Toute taille de la nav s'écrit
   `calc(base * var(--k))` À PLAT (un calc() dans un calc() fait segfaulter le
   minifieur : build en exit 139 muet). Bordures à 1px, rayons fixes.
   Les bases sont celles du 16" (1728, k = 1) ; à 1440 (k = 0,92), la marque
   retrouve exactement ses tailles d'avant (230, 118, 220 px).
   -------------------------------------------------------------------------- */
.nu-index { display: none; }
@media not all and (max-width: 1023px) {
    html.nu { --ui-scale: 0.92; --density: 1; --k: calc(var(--ui-scale) * var(--density)); }
}
@media (min-width: 1512px) { html.nu { --ui-scale: 0.96; } }
@media (min-width: 1728px) { html.nu { --ui-scale: 1; } }
@media (min-width: 2056px) { html.nu { --ui-scale: 1.08; } }
@media (min-width: 2560px) { html.nu { --ui-scale: 1.16; } }
@media (min-width: 3000px) { html.nu { --ui-scale: 1.26; } }

@media not all and (max-width: 1023px) {
    /* ── ③ LA MARQUE, EN HAUT À GAUCHE ─────────────────────────────────────
       La boîte se resserre sur la marque (`right: auto`) : pleine largeur,
       elle couvrait le chevron nord. Le chevron nord n'est plus au-dessus
       d'elle, les 44 px de tête tombent. `--brand-h` (la réserve de `.page`)
       ne bouge pas : la marque tient dedans jusqu'au 32". */
    html.nu .site-brand {
        right: auto;
        padding: calc(24px * var(--k) + env(safe-area-inset-top)) calc(28px * var(--k)) calc(12px * var(--k));
    }
    html.nu .site-brand a { margin: 0; }
    html.nu .sb-logo { width: calc(250px * var(--k)); margin: 0; }
    html.nu .sb-sig { width: calc(128px * var(--k)); margin-top: calc(-6px * var(--k)); }
    html.nu:has(.page--accueil) .sb-sig,
    html.nu:has(.page--corner) .sb-sig { width: calc(239px * var(--k)); transform: translateX(calc(19.5px * var(--k))); }

    /* ── ① LA BOUSSOLE À L'ÉCHELLE ─────────────────────────────────────────
       Même dessin, mêmes places ; libellé, chevron, écarts et cible grandissent
       avec l'écran. À 1728 : libellé 15 px (11,2 avant), chevron 48 px de long
       (30 avant), cible ≥ 48 px. */
    html.nu .cp { font-size: calc(15px * var(--k)); gap: calc(4px * var(--k)); }
    html.nu .cp--n { padding: calc(10px * var(--k)) calc(22px * var(--k)); }
    html.nu .cp--s { padding: calc(8px * var(--k)) calc(22px * var(--k)); bottom: calc(8px * var(--k) + env(safe-area-inset-bottom)); }
    /* Les flancs restent MINCES : le libellé vertical serré (interligne 1,2)
       et 4 px (× k) de marge, soit 48 px (× k) de large, ce que le couloir
       de 50 px de `.page` loge jusqu'au 16". */
    html.nu .cp--w,
    html.nu .cp--e { padding: calc(22px * var(--k)) calc(4px * var(--k)); min-width: calc(48px * var(--k)); justify-content: center; }
    html.nu .cp--w span, html.nu .cp--e span { line-height: 1.2; }
    html.nu .cp--n svg, html.nu .cp--s svg { width: calc(48px * var(--k)); height: calc(19.2px * var(--k)); }
    html.nu .cp--w svg, html.nu .cp--e svg { width: calc(19.2px * var(--k)); height: calc(48px * var(--k)); }

    /* Les deux pictogrammes suivent la même échelle (48 px à 1728). */
    html.nu .qa {
        width: calc(48px * var(--k)); height: calc(48px * var(--k)); padding: calc(12px * var(--k));
        bottom: calc(10px * var(--k) + env(safe-area-inset-bottom));
    }
    html.nu .qa--plan { left: calc(10px * var(--k)); }
    /* HORS DE L'ACCUEIL, LE ✣ EN HAUT À DROITE (PO, 30/09). Lui seul bouge :
       la boussole directionnelle reste aux quatre bords, l'enveloppe reste
       en bas à droite et le mobile conserve sa position historique. */
    html.nu:not([data-ici="/"]) .qa--plan {
        left: auto; bottom: auto;
        top: calc(18px * var(--k) + env(safe-area-inset-top));
        right: calc(18px * var(--k));
        width: calc(68px * var(--k)); height: calc(68px * var(--k)); padding: calc(10px * var(--k));
        transform: none;
    }
    html.nu:not([data-ici="/"]) .qa--plan svg { width: 100%; height: 100%; }
    html.nu .qa--contact { right: calc(10px * var(--k)); }
    /* Le focus du clavier : l'anneau lime de la maison (4,2:1 sur le sol). */
    html.nu :is(.cp, .qa, .site-brand a, .nu-index a):focus-visible { outline: 2px solid var(--accent); outline-offset: calc(3px * var(--k)); }

    /* ── ② L'INDEX, À DROITE ───────────────────────────────────────────────
       Centré en hauteur, son bord droit à 20 px (× k) du chevron est
       (48 px × k de large) : en dedans, jamais dessus. La boîte ne reçoit
       rien, seuls les liens. Même corps pour tous les rangs (dev : 20,8 px à
       1440, ici 20,2) ; l'Accueil et le premier niveau atténués
       (`--fg-muted`), les sous-pages pleines. `nu.css` style l'élément
       `nav` (flex, marges) et `nav a` (capsule) : on rétablit, comme pour la
       boussole plus haut. */
    /* ⚠️ SUR L'ACCUEIL SEULEMENT (PO, 26/09 · 09:50 : « on devait juste poser
       une arbo sur la page d'accueil ») : l'index vit hors de `.page` et
       survit aux glissements, c'est donc `html[data-ici]` (posé au rendu par
       NuBase, puis par les bascules) qui décide, pas la page d'entrée. Les
       autres pages le recevront au déroulé, une par une. */
    html.nu:not([data-ici="/"]) .nu-index { display: none; }
    html.nu[data-ici="/"] .nu-index {
        display: block; position: fixed; z-index: 9;
        top: 50%; right: calc(68px * var(--k)); transform: translateY(-50%);
        margin: 0; text-align: right;
        pointer-events: none;
    }
    /* LA COLONNE (maquette PO du 27/09) : la croix du plan repliée. Chaque
       ligne est une ancre de largeur nulle posée sur l'AXE ; son contenu
       déborde de part et d'autre selon sa forme (NuIndex.astro) :
       nord : le nom puis le point, centrés ; porte et sud : le point puis
       le nom, centrés ; centre : l'Accueil à gauche du ✣, le ✣ sur l'axe ;
       ouest : le nom puis le point, qui suit sa fin ; est : le point à
       gauche de l'axe, puis le nom. */
    html.nu .nu-index { --axe: 5.2em; width: 12.5em; font-size: calc(22px * var(--k)); text-align: left; }
    html.nu .nu-index ol { display: block; margin: 0; padding: 0; list-style: none; }
    html.nu .nu-index li {
        display: flex; align-items: center; width: 0; margin-left: var(--axe);
        line-height: 1.25; white-space: nowrap;
    }
    html.nu .nu-index .nx-groupe { margin-top: 0.55em; }
    html.nu .nu-index :is(.nx-nord, .nx-porte, .nx-sud) { flex-direction: column; }
    html.nu .nu-index :is(.nx-porte, .nx-sud) { flex-direction: column-reverse; }
    html.nu .nu-index .nx-centre { justify-content: flex-end; }
    html.nu .nu-index .nx-ouest { transform: translateX(-1.4em); }
    html.nu .nu-index .nx-est .nx-pt { order: -1; margin: 0 0.35em 0 -2.2em; }
    html.nu .nu-index .nx-ouest .nx-pt { margin-left: 0.35em; }
    html.nu .nu-index .nx-pt {
        flex: none; width: 0.36em; height: 0.36em; border-radius: 50%;
        background: var(--fg); box-shadow: 0 1px 6px rgba(0, 0, 0, 0.25);
    }
    html.nu .nu-index :is(.nx-nord, .nx-porte, .nx-sud) .nx-pt { margin: 0.12em 0; }
    html.nu .nu-index .nx-croix {
        flex: none; width: 1.7em; height: 1.7em; margin: 0.1em -0.85em 0.1em 0.3em;
        fill: currentColor; color: var(--fg-muted);
    }
    html.nu .nu-index a {
        position: relative; display: inline-block; padding: 0.06em 0;
        border: 0; border-radius: 0; background: none;
        font-size: 1em; line-height: 1.25; font-weight: 400;
        color: var(--fg); text-decoration: none;
        text-shadow: var(--shadow-text);
        pointer-events: auto;
    }
    /* Les stations de rang 1 atténuées, leurs pages pleines (comme avant). */
    html.nu .nu-index :is(.nx-nord, .nx-porte, .nx-sud, .nx-centre) a { color: var(--fg-muted); }
    html.nu .nu-index a:hover { color: var(--fg); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 0.2em; }
    /* Les ancêtres du lieu se rallument. */
    html.nu .nu-index a[data-lieu="ancetre"] { color: var(--fg); }
    /* LE LIEU : en lime (aria-current="page") ; la station d'une fiche,
       d'une salle ou d'un article pleine (aria-current="true"). La couleur
       ne parle pas seule (WCAG 1.4.1) : les deux passent en 600, et le point
       de la station prend la lime du « vous êtes ici » du plan. */
    html.nu .nu-index li a[data-lieu="ici"] { color: var(--accent); font-weight: 600; }
    html.nu .nu-index li a[data-lieu="dedans"] { color: var(--fg); font-weight: 600; }
    html.nu .nu-index li:has(a:is([data-lieu="ici"], [data-lieu="dedans"])) .nx-pt { background: var(--accent); }
}
