/* ==========================================================================
   INFINITES SPORTS - page d'accueil
   Maquette statique autonome. Aucune ressource externe : police, images et
   script vivent dans ce dossier. Aucun chemin ne remonte au-dessus de
   infinites-sports/.

   Construction : la maquette de reference a ete relevee a la largeur 1440 px.
   Toutes les cotes issues du releve sont regroupees en jetons dans le bloc
   « 2. Cotes relevees sur la maquette » : un calage se fait donc en modifiant
   une valeur, jamais en fouillant la feuille.

   Sections livrees : en-tete, heros. La suite viendra section par section.
   ========================================================================== */

/* 1. Police auto-hebergee (licence OFL)
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Inter";
  src: url("../fonts/inter-var.woff2") format("woff2-variations");
  font-weight: 100 900; font-style: normal; font-display: swap;
}

/* 2. Cotes relevees sur la maquette (reference : largeur 1440 px)
   -------------------------------------------------------------------------- */
:root {
  /* Couleurs */
  --noir: #000000;          /* fond de page */
  --orange: #E8621F;        /* accent : logo, boutons, puces */
  --orange-vif: #F0721F;    /* survol */
  --blanc: #FFFFFF;
  --blanc-80: rgba(255,255,255,.82);
  --blanc-55: rgba(255,255,255,.55);
  --trait: rgba(255,255,255,.22);
  --filet: rgba(255,255,255,.14);   /* separation entre deux sections */
  --air-section: 10px;              /* air au-dessus et au-dessous d'une section */

  /* ESPACEMENT DES SECTIONS - deux valeurs, et deux seulement, pour toute
     la page.
     --gout : le retrait lateral. C'est celui de l'en-tete, ou se pose le
              logotype : il fixe le bord gauche de la page, et tout le reste
              s'y aligne.
     --air  : la respiration verticale, identique en haut et en bas de
              chaque section, quelle que soit ce qu'elle porte.
     Deux exceptions, toutes deux voulues : le heros, dont le texte est
     volontairement plus rentre que le logotype sur la maquette - 90 px
     contre 42 - et qui touche l'en-tete ; et les deux bandes d'images
     pleine largeur (heros, cartes), qui vont d'un bord a l'autre par
     construction et qu'un retrait vertical decollerait de la maquette. */
  --gout: 42px;
  --air: 40px;

  --marge: var(--gout);     /* ancien nom, garde pour l'en-tete et le heros */

  /* En-tete */
  --tete-h: 122px;
  /* L'ETAT COMPACT, quand l'en-tete colle et qu'on a quitte le haut de page.
     Les memes postes, a 69 % : la barre reste la barre, elle maigrit. Le
     logotype garde ses DEUX lignes - « INFINITE » seul n'est pas la marque. */
  --tete-h-compacte: 84px;
  --logo-h-compacte: 27px;
  /* HAUTEUR, ET NON LARGEUR : le logotype est une image de rapport fixe, et
     c'est sa hauteur qui doit s'accorder a celle de la barre. Poser une
     largeur reviendrait a deduire la hauteur du rapport, donc a la laisser
     changer si le fichier est un jour recadre. */
  --logo-h: 38px;
  --nav-taille: 11px;
  --nav-gout: 41.5px;       /* entre deux entrees */
  --nav-cta: 82px;          /* entre la derniere entree et le bouton */
  --cta-h: 39px;
  --langues-taille: 11px;     /* selecteur de langue */
  --langues-gout: 15px;       /* entre les deux codes */
  --langues-cta: 30px;        /* du selecteur au bouton d'appel */
  --langues-nav: 46px;        /* de la derniere entree de menu au selecteur */

  /* Heros */
  --heros-ratio: 2000 / 671;  /* ratio natif de la photographie fournie */
  --heros-gauche: 90px;       /* retrait du bloc de texte */
  --heros-haut: 111.3px;      /* du haut de l'image a la capitale du titre */
  --heros-bas: 31px;          /* du bas de l'image au bas des boutons */
  --titre-taille: 53px;
  --titre-inter: 1.18;
  /* Metriques d'Inter relevees non pas sur les tables de la fonte mais sur le
     rendu : on echantillonne les pixels du titre pour trouver le haut et le
     bas reels de l'encre. Les valeurs deduites de canvas (fontBoundingBox)
     donnaient 0,242 et 0,755 et decalaient le titre de 7 px. */
  --titre-cale: 0.11;         /* Inter : ascendante moins capitale, en em */
  --titre-cap: 0.736;         /* Inter : hauteur de capitale, en em */
  /* Filet qui coupe les lettres. Valeur absolue et non relative au corps :
     un filet reste un filet, il ne grossit pas avec le titre. 1 px tient a
     toutes les tailles d'ecran et reste net, la ou une valeur en em tombait
     sous le pixel sur mobile et se delavait. */
  --barre-ep: 1px;
  --chapo-taille: 15px;
  --chapo-large: 334px;       /* force la coupure apres « espaces » */
  --etiq-taille: 11px;
  --btn-h: 38px;
  --btn-taille: 11px;
  --btn-pad: 22px;            /* bouton ligne */
  --btn-plein-pad: 31px;      /* bouton plein du heros, elargi par la fleche */
  --cta-pad: 23px;            /* bouton de l'en-tete */
  --btn-gout: 14px;           /* entre le libelle et la fleche */
  --cta-gout: 10px;
  --btns-gout: 18px;          /* entre les deux boutons du heros */
  --fleche-l: 15px;
  --fleche-h: 9px;
  --chapo-haut: 7.1px;        /* du titre au paragraphe */
  --etapes-haut: 20px;        /* du paragraphe a la ligne d'etapes */
  --etapes-gout: 16px;

  /* Bandeau des univers */
  /* Les mots etant inclines, leur encre deborde de leur boite de la moitie
     de la course horizontale, soit corps x tan(10 degres) / 2. On ajoute
     cette avance au retrait, faute de quoi le bandeau demarrerait 4 px a
     gauche de tout le reste - et c'est justement ce genre d'ecart qui se
     voit quand on aligne une page. */
  --univers-marge: calc(var(--gout) + var(--univers-taille) * .0882);
  --univers-taille: 49px;     /* corps des quatre mots */
  /* Les mots de la maquette sont plus larges que ne le donne Inter a cette
     hauteur de capitale : la fonte d'origine est plus etendue. Plutot que de
     grossir le corps - ce qui casserait la hauteur relevee - on ouvre
     l'approche, qui elargit sans monter. */
  --univers-approche: .065em;
  --univers-trait: 1.5px;     /* epaisseur du contour des lettres */
  --univers-pente: -10deg;    /* inclinaison des mots */
  --univers-puce: 8px;        /* diametre des points de separation */
  --univers-sous-haut: 27.6px; /* du bas des capitales au sous-titre */
  --univers-sous: 13px;       /* corps du sous-titre */
  --univers-sous-ls: .18em;   /* approche du sous-titre */
  --univers-contour: rgba(255,255,255,.22);

  /* Les quatre cartes */
  --carte-gout: 16px;         /* entre deux cartes */
  --carte-ratio: 350 / 300;   /* largeur sur hauteur d'une carte */
  --carte-pad: 20px;
  --carte-titre: 18px;
  --carte-texte: 10.5px;
  --carte-fleche: 18px;
  --carte-titre-actif: 21px;  /* au survol : un peu plus gros */
  --carte-texte-actif: 12px;
  --carte-teinte: .92;        /* opacite du voile orange au survol */

  /* Bouton de retour en haut */
  --haut-taille: 48px;
  --haut-marge: 32px;         /* du bord de la fenetre */
  --haut-piste: 1.5px;        /* epaisseur du cercle de fond */
  --haut-jauge: 3px;          /* epaisseur de l'arc de progression */

  /* Conception 3D */
  --conc-gauche: 33%;         /* largeur du bloc de texte */
  --conc-ratio: 3 / 1;        /* largeur sur hauteur du comparateur */
  /* le retrait lateral vient de --gout */
  --conc-etiq: 11px;
  --conc-titre: 32px;
  --conc-texte: 12px;
  --sep-pente: 3%;            /* demi-course horizontale de la diagonale */
  --sep-trait: 2px;           /* epaisseur du trait, mesuree a l'horizontale */
  --sep-poignee: 34px;

  /* Cle en main */
  --cle-marge: var(--gout);
  --cle-titre: 26px;
  --cle-etiq: 11px;
  --cle-rond: 42px;           /* diametre du cercle d'icone */
  --cle-etape-titre: 12px;
  --cle-etape-texte: 10.5px;
  --cle-gout: 28px;           /* entre deux etapes */

  /* Realisations */
  --real-gauche: 26%;
  --real-pad: var(--gout);
  --real-etiq: 11px;
  --real-titre: 30px;
  --real-vue: 4;              /* nombre de vignettes visibles */
  --real-gout: 10px;
  --real-ratio: 4 / 5;        /* largeur sur hauteur d'une vignette */
  --real-nom: 15px;
  --real-meta: 10px;
  --real-fleches-h: 16px;     /* retrait des fleches dans le coin des vignettes */

  /* Innovation */
  --inno-gauche: 31%;
  /* Le retrait lateral vient de --gout, la respiration verticale de --air,
     comme partout ailleurs. */
  /* LE DUO SUIT DESORMAIS LA FORME DES PHOTOGRAPHIES, ET NON L'INVERSE.
     Les deux cliches sont en 1449 x 793, soit 1,83:1. Les afficher dans un
     panneau en 4:1 en jetait 54 % de la hauteur : la bande passait au milieu
     de la tablette Carmin - plus de logotype en haut, plus de barre de
     navigation en bas, un rectangle sombre qu'on ne reconnaissait pas - et
     coupait le Reformer sous le chassis. A 2,4:1 il reste 76 % de l'image, et
     « object-position » choisit LAQUELLE des bandes on garde plutot que de
     prendre celle du milieu par defaut. */
  --inno-ratio: 2.4 / 1;      /* largeur sur hauteur du duo */
  --inno-coupe: 45%;          /* ou tombe la diagonale, fixe */
  --inno-etiq: 11px;
  --inno-titre: 27px;
  --inno-texte: 12px;
  --inno-nom: 20px;
  --inno-legende: 13px;

  /* Appel final */
  --final-titre: 33px;

  --police: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
            Helvetica, Arial, sans-serif;
}

/* 3. Base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%; scroll-behavior: smooth;
  /* LE DECALAGE DES ANCRES, et il n'est pas cosmetique. Mesure sur une page
     d'univers : avec une barre collee et sans cette ligne, le titre du bloc
     vise se cale 72 px SOUS la barre - invisible. Avec elle, il tombe 66 px
     dessous. --colle-haut vaut zero sans script : la regle ameliore aussi
     l'existant, ou le bloc vise se collait au bord haut de la fenetre. */
  --colle-haut: 0px;
  scroll-padding-top: calc(var(--colle-haut) + 16px);
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}
body {
  margin: 0; background: var(--noir); color: var(--blanc);
  font-family: var(--police); font-size: 16px; font-weight: 400; line-height: 1.6;
  -webkit-font-smoothing: antialiased; overflow-x: hidden;
}
img { max-width: 100%; height: auto; display: block; }

/* Filet de separation entre deux sections. Pose sur la regle generale plutot
   que section par section : les sections a venir en heriteront sans qu'on y
   pense, et le heros, qui n'a rien au-dessus de lui, en est exclu de fait. */
main > section + section { border-top: 1px solid var(--filet); }

/* Un peu d'air autour de chaque section, pour que le filet ne soit pas colle
   au contenu. Les marges de deux sections voisines fusionnent : l'ecart
   entre elles vaut donc 10 px et non 20.

   Le heros fait exception EN HAUT seulement : il touche l'en-tete sur la
   maquette, et c'est de ce contact que part tout le calage vertical de la
   page. Lui ajouter une marge le decalerait de 10 px, ainsi que tout ce qui
   suit. */
/* Les pages en attente de contenu sont plus courtes que l'ecran : sans cela,
   le pied de page s'arreterait au milieu du vide. Le corps occupe donc au
   moins la hauteur de la fenetre, et c'est le contenu qui pousse le pied vers
   le bas - pas une hauteur fixe, qui laisserait un trou sur les pages
   longues. */
body { min-height: 100vh; display: flex; flex-direction: column; }
main { flex: 1 0 auto; }
.pied { flex: none; }

main > section { margin-block: var(--air-section); padding-block: var(--air); }
main > section:first-child { margin-top: 0; }
/* Les deux bandes d'images pleine largeur sont les seules a ne pas respirer
   verticalement : elles sont bord a bord sur la maquette. */
.heros, .cartes { padding-block: 0; }
a { color: inherit; text-decoration: none; }
h1, h2, h3 { margin: 0; font-weight: 900; line-height: 1.1; }
p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.saut {
  position: absolute; left: -9999px; top: 0; z-index: 300;
  background: var(--orange); color: var(--noir); padding: 12px 20px;
  font-size: 13px; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase;
}
.saut:focus { left: 0; }
:focus-visible { outline: 2px solid var(--orange); outline-offset: 3px; }

/* 4. Boutons
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--btn-gout);
  height: var(--btn-h); padding: 0 var(--btn-pad); border-radius: 2px;
  font-size: var(--btn-taille); font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; white-space: nowrap; cursor: pointer;
  border: 1px solid transparent; background: none; color: var(--blanc);
  transition: background .18s, border-color .18s, color .18s;
}
.btn .fl { width: var(--fleche-l); height: var(--fleche-h); flex: none; }
.btn--plein { background: var(--orange); border-color: var(--orange); }
.btn--plein:hover { background: var(--orange-vif); border-color: var(--orange-vif); }
.btn--ligne { border-color: var(--trait); }
.btn--ligne:hover { border-color: var(--blanc); background: rgba(255,255,255,.08); }
.btn--orange { border-color: var(--orange); }
.btn--orange:hover { background: var(--orange); }
.btn--orange .fl { color: var(--orange); transition: color .18s; }
.btn--orange:hover .fl { color: var(--blanc); }

/* 5. En-tete
   -------------------------------------------------------------------------- */
.tete {
  position: relative; z-index: 100; background: var(--noir);
  display: flex; align-items: center;
  height: var(--tete-h); padding-inline: var(--marge);
}

/* -----------------------------------------------------------------------------
   L'EN-TETE SUIT LE DEFILEMENT - et maigrit en chemin
   -----------------------------------------------------------------------------
   COLLE, PAS FIXE. « sticky » garde la barre dans le flux : elle occupe sa
   place en haut de page, puis s'y accroche. « fixed » l'en sortirait, et il
   faudrait alors compenser sa hauteur par un padding sur le corps - une
   seconde declaration de --tete-h, qu'on oublierait de suivre au premier
   changement d'echelle.

   SEULEMENT AVEC JAVASCRIPT, et ce n'est pas un detail. Sans script, sous
   1100 px, le menu ne se replie pas : il se deplie DANS la barre, qui passe a
   320 px sur un telephone - 38 % de l'ecran. Collee, elle le mangerait en
   permanence. Sans script l'en-tete reste donc dans le flux, et la page se
   comporte exactement comme avant.

   --colle-haut EST LA PLACE QUE LA BARRE PREND EN HAUT, et vaut zero quand
   elle ne colle pas. Tout ce qui doit se tenir sous elle le lit : le
   scroll-padding des ancres, les deux sommaires colles. Une seule valeur,
   donc rien a reprendre dans les deux blocs d'echelle.

   LA HAUTEUR COMPACTE N'EST PAS REPRISE PAR --colle-haut : les ancres et les
   sommaires se calent sur la barre PLEINE. C'est volontaire - au moment ou
   l'on clique une ancre depuis le haut de page, la barre est encore pleine,
   et viser trop bas ne masque rien tandis que viser trop haut masque le
   titre. On garde la marge.
   -------------------------------------------------------------------------- */
.js { --colle-haut: var(--tete-h); }
.js .tete { position: sticky; top: 0; transition: height .25s ease; }

/* L'etat compact ne redefinit que des variables : la regle de .tete ci-dessus
   ne bouge pas, et la transition porte sur la hauteur calculee. */
.js .tete.compacte {
  --tete-h: var(--tete-h-compacte);
  --logo-h: var(--logo-h-compacte);
}
.js .tete .logo-img { transition: height .25s ease; }

/* Mouvement reduit : la barre compacte toujours, mais d'un coup. Ce qui gene
   n'est pas qu'elle change de taille, c'est qu'elle glisse. */
@media (prefers-reduced-motion: reduce) {
  .js .tete, .js .tete .logo-img { transition: none; }
}

.logo { display: inline-block; margin-right: auto; line-height: 1; }
/* Le logotype etait compose en CSS - « INFINITE » en gras, « SPORT » justifie
   dessous en orange - tant que le fichier de la marque n'existait pas. Il est
   fourni depuis le 6 octobre 2026 : une image, blanche sur transparent, qui
   porte le monogramme et le mot. Les classes .logo-mot et .logo-sous ont ete
   retirees avec leurs variables, aucune page ne les emettant plus.

   width: auto et display: block - la hauteur commande, la largeur suit, et le
   bloc ne porte pas l'interligne d'un element en ligne, qui ajouterait
   quelques pixels sous l'image et desalignerait la barre. */
.logo-img { display: block; height: var(--logo-h); width: auto; }

.nav { display: flex; align-items: center; gap: var(--nav-gout); }
.nav a {
  font-size: var(--nav-taille); font-weight: 500; letter-spacing: .06em;
  text-transform: uppercase; color: var(--blanc-80); transition: color .18s;
}
.nav a:hover, .nav a[aria-current="page"] { color: var(--blanc); }
/* Les cotes des boutons sont relevees une a une sur la maquette : celui de
   l'en-tete y est plus etroit, et le bouton plein du heros, qui porte une
   fleche, plus large que le bouton ligne qui le suit. */
.tete-cta {
  margin-left: var(--langues-cta);
  height: var(--cta-h); padding-inline: var(--cta-pad); gap: var(--cta-gout);
}

/* Selecteur de langue.
   Il n'existe pas sur la maquette de reference : c'est un ajout. Il est pose
   entre le menu et le bouton d'appel, de sorte que le bouton reste cale sur
   la marge droite comme sur la maquette. Le menu, lui, se decale vers la
   gauche de la largeur du selecteur - c'est le seul poste du releve que cet
   ajout deplace. */
.langues {
  display: flex; align-items: center; gap: var(--langues-gout);
  margin-left: var(--langues-nav);
  font-size: var(--langues-taille); font-weight: 600; letter-spacing: .1em;
  text-transform: uppercase; line-height: 1;
}
.langues > * { position: relative; }
/* Filet de separation, centre dans la gouttiere. */
.langues > * + *::before {
  content: ""; position: absolute; top: 50%;
  left: calc(var(--langues-gout) / -2);
  width: 1px; height: 1.1em; transform: translateY(-50%);
  background: rgba(255,255,255,.3);
}
/* Deux lettres ne font pas une cible : on elargit la boite autour d'elles
   - 24 px au pointeur, 44 px au doigt - sans deplacer l'encre, qui reste
   centree. C'est la boite qui grandit, pas le dessin. */
.langues a {
  color: rgba(255,255,255,.5); transition: color .18s;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 24px; min-height: 24px;
}
.langues a:hover { color: var(--blanc); }
.langues-actif { color: var(--blanc); }

.burger {
  display: none; width: 44px; height: 44px; margin-left: auto;
  align-items: center; justify-content: center;
  background: none; border: 1px solid var(--trait); color: var(--blanc);
  border-radius: 2px; cursor: pointer;
}
.burger svg { width: 20px; height: 20px; }
.burger .x { display: none; }
.burger[aria-expanded="true"] .m { display: none; }
.burger[aria-expanded="true"] .x { display: block; }

/* 6. Heros
   -------------------------------------------------------------------------- */
.heros { position: relative; width: 100%; overflow: hidden; }

/* La photographie est affichee pleine largeur, a son ratio natif : c'est ce
   que fait la maquette, ou la bande de quatre univers occupe toute la largeur.
   Le fond degrade tient lieu de reserve tant que le cliche n'est pas depose. */
.heros-media {
  position: relative; width: 100%; aspect-ratio: var(--heros-ratio);
  background: radial-gradient(120% 140% at 72% 40%, #1C1C1E 0%, #0B0B0C 55%, #000 100%);
}
.heros-media img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: center;
}
/* Voile : la maquette assombrit franchement le tiers gauche pour que le titre
   se detache, puis relache vers la droite ou l'image doit rester lisible. */
.heros-voile {
  position: absolute; inset: 0;
  background:
    linear-gradient(90deg, rgba(0,0,0,.86) 0%, rgba(0,0,0,.78) 22%,
                           rgba(0,0,0,.42) 44%, rgba(0,0,0,.12) 66%, rgba(0,0,0,.06) 100%),
    linear-gradient(180deg, rgba(0,0,0,.34) 0%, rgba(0,0,0,0) 30%,
                            rgba(0,0,0,0) 72%, rgba(0,0,0,.42) 100%);
}

.heros-in {
  position: absolute; inset: 0;
  padding: var(--heros-haut) var(--marge) var(--heros-bas) var(--heros-gauche);
  display: flex; flex-direction: column; align-items: flex-start;
}

.heros h1, .titre-filet {
  font-size: var(--titre-taille); line-height: var(--titre-inter);
  font-weight: 900; letter-spacing: .02em; text-transform: uppercase;
  /* Le releve donne la position de la CAPITALE, pas celle de la boite de
     ligne. On remonte donc le titre de l'espace mort qui les separe :
     la demi-interligne, plus l'ecart entre l'ascendante et la capitale
     de la fonte (--titre-cale, mesure sur Inter). */
  margin-top: calc(var(--titre-taille) * -1 *
                   (var(--titre-cale) + (var(--titre-inter) - 1) / 2));

  /* Trait noir a mi-hauteur des capitales.
     Ce n'est pas un texte barre : un barre traverserait aussi les blancs
     entre les lettres. Ici le degrade - blanc, bande noire, blanc - est
     detoure par la forme des glyphes (background-clip: text), donc le trait
     n'existe que dans la matiere des lettres. Les blancs restent intacts.

     Le motif est haut d'une interligne et se repete : chaque ligne recoit sa
     bande au meme endroit, quel que soit le nombre de lignes.

     --barre-y place le milieu de la bande sur le milieu des capitales, et
     non au milieu de la boite de ligne : on descend de la demi-interligne,
     puis de l'ecart ascendante-capitale, puis d'une demi-capitale. */
  --barre-y: calc(((var(--titre-inter) - 1) / 2
                   + var(--titre-cale) + var(--titre-cap) / 2) * 1em);
  background-image: linear-gradient(180deg,
      var(--blanc) 0,
      var(--blanc) calc(var(--barre-y) - var(--barre-ep) / 2),
      var(--noir)  calc(var(--barre-y) - var(--barre-ep) / 2),
      var(--noir)  calc(var(--barre-y) + var(--barre-ep) / 2),
      var(--blanc) calc(var(--barre-y) + var(--barre-ep) / 2),
      var(--blanc) 100%);
  background-size: 100% calc(var(--titre-inter) * 1em);
  background-repeat: repeat-y;
}
/* Le detourage n'est applique que la ou il est reconnu : sans cette garde,
   un navigateur qui ignore background-clip afficherait un titre transparent,
   donc invisible. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .heros h1, .titre-filet {
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
  }
}

.heros-chapo {
  margin-top: var(--chapo-haut); max-width: var(--chapo-large);
  font-size: var(--chapo-taille); line-height: 1.55; color: var(--blanc-80);
}
.heros-chapo b { color: var(--orange); font-weight: 400; }

.heros-etapes {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--etapes-gout);
  margin-top: var(--etapes-haut);
  font-size: var(--etiq-taille); font-weight: 500; letter-spacing: .14em;
  text-transform: uppercase; color: var(--blanc-80);
}
.heros-etapes li { display: flex; align-items: center; gap: var(--etapes-gout); }
.heros-etapes li + li::before {
  content: ""; width: 4px; height: 4px; border-radius: 50%;
  background: var(--orange); flex: none;
}

.heros-btns { display: flex; flex-wrap: wrap; gap: var(--btns-gout); margin-top: auto; }
.heros-btns .btn--plein { padding-inline: var(--btn-plein-pad); }


/* 7. Bandeau des univers
   --------------------------------------------------------------------------
   Quatre mots en lettres creuses, separes par des points orange, cales d'une
   marge a l'autre, et une accroche centree dessous.

   Les mots ne sont pas dessines mais detoures : le remplissage est
   transparent et seul le contour est peint (-webkit-text-stroke). Sous
   @supports, faute de quoi un navigateur qui l'ignore afficherait un texte
   transparent, donc rien.

   La rangee est distribuee en space-between : les quatre mots et les trois
   points s'ecartent d'eux-memes pour occuper exactement la largeur
   disponible. La bande tient donc son cadrage quelle que soit la police
   servie, sans reglage a la main.
   -------------------------------------------------------------------------- */
.univers {
  background: var(--noir);
  padding-inline: var(--univers-marge);
  overflow: hidden;   /* l'inclinaison fait deborder l'encre des mots extremes */
}

.univers-mots {
  display: flex; align-items: center; justify-content: space-between;
  /* Le releve donne la position de la CAPITALE, pas celle de la boite de
     ligne : meme correction que pour le titre du heros. */
  margin-top: calc(var(--univers-taille) * -1 * var(--titre-cale));
}

.univers-mots li {
  font-size: var(--univers-taille); font-weight: 900; line-height: 1;
  letter-spacing: var(--univers-approche);
  /* L'approche traine apres la derniere lettre : on la reprend, sinon le
     dernier mot n'atteindrait pas la marge droite. */
  margin-right: calc(var(--univers-approche) * -1);
  text-transform: uppercase; white-space: nowrap;
  color: var(--univers-contour);
  transform: skewX(var(--univers-pente));
}
@supports ((-webkit-text-stroke: 1px black) or (text-stroke: 1px black)) {
  .univers-mots li {
    color: transparent;
    -webkit-text-stroke: var(--univers-trait) var(--univers-contour);
  }
}

/* Les points ne s'inclinent pas : seuls les mots penchent. */
.univers-mots .puce {
  flex: none; width: var(--univers-puce); height: var(--univers-puce);
  border-radius: 50%; background: var(--orange);
}

.univers-sous {
  margin-top: calc(var(--univers-sous-haut) - var(--univers-taille) * var(--titre-cale));
  text-align: center;
  font-size: var(--univers-sous); font-weight: 500;
  letter-spacing: var(--univers-sous-ls);
  /* L'approche laisse une gouttiere apres la derniere lettre. Centrer sans
     la reprendre decalerait le texte vers la gauche de la moitie de cette
     gouttiere : on retire la boite d'autant. */
  margin-right: calc(var(--univers-sous-ls) * -1);
  text-transform: uppercase; color: var(--blanc-80);
}

/* 8. Les quatre cartes
   --------------------------------------------------------------------------
   Une carte = une photographie plein cadre, un voile sombre en bas pour que
   le texte s'y detache, et le texte pose par-dessus, en bas a gauche.

   Au survol, la carte bascule : un voile orange couvre la photographie, le
   texte passe au noir, grossit, se centre et s'allonge.

   Le passage d'un etat a l'autre est un fondu croise entre deux calques
   superposes, et non un deplacement du meme bloc : ni justify-content ni
   text-align ne s'animent en CSS, un bloc unique sauterait donc d'une
   position a l'autre au milieu du fondu de couleur. Deux calques coutent un
   titre repete dans le balisage ; le calque de repos est retire de
   l'arborescence d'accessibilite (aria-hidden), de sorte qu'un lecteur
   d'ecran n'entende le titre qu'une fois - et entende le texte long, le plus
   complet des deux.

   Tout ce qui vaut au survol vaut au clavier : les regles portent aussi sur
   :focus-visible, sans quoi la carte serait muette pour qui navigue au
   clavier.
   -------------------------------------------------------------------------- */
.cartes {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: var(--carte-gout); background: var(--noir);
}

.carte {
  position: relative; display: block; overflow: hidden;
  aspect-ratio: var(--carte-ratio); color: var(--blanc);
  text-decoration: none; isolation: isolate;
}
.carte:focus-visible { outline: 2px solid var(--orange); outline-offset: -4px; }

/* La reserve degradee tient la place tant que la photographie n'est pas
   deposee. Une teinte par carte, pour qu'on distingue les quatre. */
.carte-media { position: absolute; inset: 0; background: var(--reserve, #141414); }
.carte-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.carte:nth-child(1) .carte-media { --reserve: radial-gradient(120% 100% at 40% 30%, #1B2A38, #0B1218 70%); }
.carte:nth-child(2) .carte-media { --reserve: radial-gradient(120% 100% at 50% 30%, #23211F, #0E0D0C 70%); }
.carte:nth-child(3) .carte-media { --reserve: radial-gradient(120% 100% at 55% 35%, #2A2118, #100C08 70%); }
.carte:nth-child(4) .carte-media { --reserve: radial-gradient(120% 100% at 45% 30%, #201A17, #0C0A09 70%); }

/* Voile sombre : sans lui, un texte clair poserait sur une zone claire de la
   photographie. */
.carte-voile {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(0,0,0,.12) 0%, rgba(0,0,0,.05) 38%,
                                      rgba(0,0,0,.62) 76%, rgba(0,0,0,.86) 100%);
}

/* Voile orange du survol. */
.carte-teinte {
  position: absolute; inset: 0; background: var(--orange);
  opacity: 0; transition: opacity .28s ease;
}
.carte:hover .carte-teinte,
.carte:focus-visible .carte-teinte { opacity: var(--carte-teinte); }

/* Les deux calques de texte, superposes. */
.carte-repos, .carte-actif {
  position: absolute; inset: var(--carte-pad);
  display: flex; flex-direction: column;
  transition: opacity .28s ease;
}
.carte-repos { justify-content: flex-end; align-items: flex-start; opacity: 1; }
.carte-actif { justify-content: center; align-items: center; text-align: center;
               color: var(--noir); opacity: 0; }
.carte:hover .carte-repos,
.carte:focus-visible .carte-repos { opacity: 0; }
.carte:hover .carte-actif,
.carte:focus-visible .carte-actif { opacity: 1; }

.carte-titre {
  display: block; font-size: var(--carte-titre); font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase; line-height: 1.1;
}
.carte-actif .carte-titre { font-size: var(--carte-titre-actif); }

.carte-desc {
  margin-top: .55em; font-size: var(--carte-texte); line-height: 1.55;
  color: var(--blanc-80); max-width: 26ch;
  /* Les quatre titres doivent former une ligne. Le bloc etant cale sur le bas
     de la carte, une phrase plus courte que les autres remonte son titre :
     c'est ce qui arrive en anglais, ou « Courts that change the game. » tient
     sur une ligne quand les trois autres en prennent deux. On reserve donc
     deux lignes, quelle que soit la langue et quel que soit le texte. */
  min-height: calc(2 * 1.55em);
}
.carte-actif .carte-desc { font-size: var(--carte-texte-actif); color: rgba(0,0,0,.88); }

.carte-fleche {
  width: var(--carte-fleche); height: calc(var(--carte-fleche) * .45);
  margin-top: .9em; color: var(--orange); flex: none;
}
.carte-actif .carte-fleche { color: var(--noir); }

/* 9. Retour en haut
   --------------------------------------------------------------------------
   Un bouton fixe en bas a droite, qui remonte la page - et qui, du meme
   geste, indique ou l'on en est : l'arc orange se referme a mesure qu'on
   descend, et boucle au bas de la page.

   L'arc est un cercle SVG dont on fait varier stroke-dashoffset. Il part de
   midi et tourne dans le sens des aiguilles ; c'est la rotation de -90
   degres qui l'y amene, un trace SVG commencant a 3 heures.

   Le bouton reste visible des le haut de la page : il vaut alors indicateur
   de progression, avant de valoir raccourci.
   -------------------------------------------------------------------------- */
.haut {
  position: fixed; right: var(--haut-marge); bottom: var(--haut-marge);
  z-index: 90; width: var(--haut-taille); height: var(--haut-taille);
  padding: 0; border: 0; border-radius: 50%;
  background: rgba(0,0,0,.35); backdrop-filter: blur(4px);
  color: var(--orange); cursor: pointer;
  display: grid; place-items: center;
  transition: background .2s, transform .2s;
}
.haut:hover { background: rgba(0,0,0,.6); transform: translateY(-2px); }
.haut:focus-visible { outline: 2px solid var(--orange); outline-offset: 3px; }
.haut svg { width: 100%; height: 100%; display: block; overflow: visible; }

.haut-piste, .haut-jauge {
  fill: none; stroke: currentColor;
  /* Le trace part de 3 heures : on le ramene a midi. */
  transform: rotate(-90deg); transform-origin: center;
}
.haut-piste { stroke-width: var(--haut-piste); opacity: .3; }
.haut-jauge {
  stroke-width: var(--haut-jauge); stroke-linecap: round;
  /* Le tour du cercle vaut 2 x pi x r, soit 125,664 pour r = 20. Le script
     ne touche qu'au decalage ; la longueur, elle, est posee ici. */
  stroke-dasharray: 125.664;
  stroke-dashoffset: 125.664;   /* arc nul tant que le script n'a pas mesure */
  transition: stroke-dashoffset .12s linear;
}
.haut-fleche { fill: none; stroke: currentColor; stroke-width: 2;
               stroke-linecap: round; stroke-linejoin: round; }

/* Mouvement reduit : la jauge saute d'une valeur a l'autre au lieu de glisser. */
@media (prefers-reduced-motion: reduce) {
  .haut-jauge { transition: none; }
}

/* 10. Conception 3D
   --------------------------------------------------------------------------
   Deux blocs : le texte a gauche sur fond noir, le comparateur a droite.

   Le comparateur superpose deux images et les separe par une diagonale que
   l'on deplace. Le decoupage se fait au clip-path : les abscisses d'un
   polygone s'exprimant en pourcentage de la LARGEUR, l'inclinaison reste
   constante tant que le rapport de la boite ne bouge pas - ce que garantit
   aspect-ratio.

   Le trait blanc n'est pas dessine a part : ce serait deux geometries a
   tenir synchronisees, et la moindre difference se verrait. C'est un calque
   blanc decoupe par le MEME polygone, decale de --sep-trait vers la droite ;
   l'image du dessus le recouvre partout sauf sur cette lisiere. Trait et
   coupe ne peuvent donc pas diverger.

   Le deplacement est confie a un <input type="range"> etendu sur toute la
   surface et rendu invisible. On herite ainsi du glisser a la souris, du
   toucher, des fleches du clavier, de Origine et Fin, et d'une semantique
   juste pour les lecteurs d'ecran - tout cela sans une ligne de script. La
   poignee ronde n'est qu'un decor, sans prise sur le pointeur.
   -------------------------------------------------------------------------- */
.conception {
  display: grid; grid-template-columns: var(--conc-gauche) 1fr;
  background: var(--noir); align-items: stretch;
}

.conception-texte {
  display: flex; flex-direction: column; justify-content: center;
  padding: 0 var(--gout); min-width: 0;
}
.conception-etiq {
  font-size: var(--conc-etiq); font-weight: 600; letter-spacing: .18em;
  text-transform: uppercase; color: var(--orange); margin-bottom: 1.4em;
}
.conception-titre {
  font-size: var(--conc-titre); font-weight: 900; line-height: 1.14;
  letter-spacing: -.005em; text-transform: uppercase; color: var(--blanc);
}
.conception-chapo {
  margin-top: 1.4em; font-size: var(--conc-texte); line-height: 1.65;
  color: var(--blanc-80); max-width: 34ch;
}
.conception-texte .btn { margin-top: 2.1em; align-self: flex-start; }

/* --- le comparateur --- */
.compare {
  --pos: 50%;
  position: relative; overflow: hidden;
  /* La largeur d'abord, le rapport ensuite. Sans width, l'element etant
     etire sur la hauteur de la rangee, aspect-ratio deduisait la largeur de
     cette hauteur - et le bloc sortait de sa colonne, de 168 px a 900 px de
     fenetre. La hauteur decoule maintenant de la largeur. */
  width: 100%; min-width: 0; aspect-ratio: var(--conc-ratio);
  /* Reserve du calque du dessous - l'espace realise, sombre. */
  background: radial-gradient(120% 160% at 65% 40%, #23201D 0%, #0D0C0B 70%);
}
.compare img { position: absolute; inset: 0; width: 100%; height: 100%;
               object-fit: cover; display: block; }

/* LE COMPARATEUR ETAIT LA TACHE CLAIRE DE LA PAGE, et il le restait quelle que
   soit la photographie : un plan technique est un trace sombre sur fond blanc,
   donc presque entierement blanc. Mesure sur les fichiers livres, en luminance
   moyenne sur 255 : le plan 205, le rendu 122, la ou les visuels d'innovation
   valides par la cliente sont a 109 et les vignettes de realisation entre 64 et
   75. Le bloc ne detonnait pas un peu, il detonnait du simple au double.

   LE FILTRE EST POSE SUR LES IMAGES, PAS SUR « .compare ». Sur le conteneur il
   aurait aussi terni la lisiere blanche et la poignee - les deux reperes qui
   disent qu'on peut tirer - et cree un contexte d'empilement de plus autour du
   curseur. Sur les images, il ne touche a rien de ce qui recoit les gestes :
   l'<input type="range"> et sa poignee sont au-dessus et restent intacts.

   DEUX DOSES, PARCE QUE LES DEUX CALQUES NE PARTENT PAS DU MEME POINT. Une dose
   unique assez forte pour ramener le plan dans le ton aurait enfonce le rendu
   bien en dessous des vignettes de realisation ; assez douce pour le rendu, elle
   aurait laisse le plan eblouissant. On vise la meme arrivee, pas le meme
   chemin. La desaturation legere est commune : elle suffit a faire tenir
   ensemble un trace bleute et une photographie de plein soleil. */
.compare img { filter: brightness(.70) saturate(.88); }
.compare-dessus img { filter: brightness(.55) saturate(.82) contrast(.96); }

.compare-trait, .compare-dessus { position: absolute; inset: 0; }
.compare-trait {
  background: var(--blanc);
  clip-path: polygon(0 0,
    calc(var(--pos) + var(--sep-pente) + var(--sep-trait)) 0,
    calc(var(--pos) - var(--sep-pente) + var(--sep-trait)) 100%, 0 100%);
}
/* Reserve du calque du dessus - le plan, clair. Sans elle, tant que les deux
   photographies manquent, la diagonale ne separerait rien de visible. */
.compare-dessus {
  background: repeating-linear-gradient(90deg, #C9CDD2 0 1px, #E4E7EA 1px 46px),
              repeating-linear-gradient(0deg, #C9CDD2 0 1px, #E4E7EA 1px 46px);
  clip-path: polygon(0 0,
    calc(var(--pos) + var(--sep-pente)) 0,
    calc(var(--pos) - var(--sep-pente)) 100%, 0 100%);
}

/* L'element qui recoit reellement les gestes. */
.compare-curseur {
  position: absolute; inset: 0; z-index: 3;
  width: 100%; height: 100%; margin: 0; padding: 0;
  opacity: 0; cursor: ew-resize; appearance: none; background: none;
}
/* Le pouce est etroit a dessein. Un <input type="range"> repartit ses
   valeurs sur la largeur moins celle du pouce : plus il est large, plus le
   point ou l'on presse s'ecarte de la valeur obtenue. A 40 px l'ecart
   atteignait 1,5 % ; a 6 px il tombe sous 0,3 %. La prise en main ne
   souffre pas : le curseur repond partout sur sa surface, et la poignee
   ronde visible, elle, est ailleurs. */
.compare-curseur::-webkit-slider-thumb { appearance: none; width: 6px; height: 100%; }
.compare-curseur::-moz-range-thumb { width: 6px; height: 100%; border: 0; }

/* La poignee ronde, purement decorative : elle ne doit pas intercepter le
   pointeur, sans quoi le curseur cesserait de repondre en son centre. */
.compare-poignee {
  position: absolute; top: 50%; left: var(--pos);
  transform: translate(-50%, -50%);
  z-index: 4; pointer-events: none;
  width: var(--sep-poignee); height: var(--sep-poignee); border-radius: 50%;
  background: var(--blanc); color: var(--noir);
  display: grid; place-items: center;
  box-shadow: 0 2px 12px rgba(0,0,0,.45);
}
.compare-poignee svg { width: 60%; height: 60%; }
.compare-curseur:focus-visible ~ .compare-poignee {
  outline: 2px solid var(--orange); outline-offset: 3px;
}

/* 11. Cle en main
   --------------------------------------------------------------------------
   Une bande basse : le titre a gauche, les quatre etapes alignees a droite,
   reliees par un filet. Le filet est un trait pose entre deux ronds, et non
   une bordure de conteneur : il ne doit ni deborder avant le premier rond ni
   apres le dernier.
   -------------------------------------------------------------------------- */
.cle {
  display: grid; grid-template-columns: auto 1fr;
  gap: clamp(30px, 4vw, 70px); align-items: center;
  background: var(--noir); padding-inline: var(--cle-marge);
}
.cle-etiq {
  font-size: var(--cle-etiq); font-weight: 600; letter-spacing: .18em;
  text-transform: uppercase; color: var(--orange); margin-bottom: 1em;
}
.cle-titre {
  font-size: var(--cle-titre); font-weight: 900; line-height: 1.12;
  letter-spacing: -.005em; text-transform: uppercase; color: var(--blanc);
}

.cle-etapes { display: flex; align-items: flex-start; gap: var(--cle-gout); }
.cle-etape {
  display: flex; align-items: flex-start; gap: 14px;
  flex: 1 1 0; min-width: 0; position: relative;
}
/* Le filet de liaison est dessine par l'etape, pas insere dans la liste :
   quatre etapes doivent se compter comme quatre, pas comme sept. */
.cle-etape:not(:last-child)::after {
  content: ""; position: absolute; right: calc(var(--cle-gout) * -1);
  top: calc(var(--cle-rond) / 2); width: var(--cle-gout); height: 1px;
  background: rgba(255,255,255,.2);
}
.cle-rond {
  flex: none; width: var(--cle-rond); height: var(--cle-rond);
  border: 1px solid rgba(255,255,255,.28); border-radius: 50%;
  display: grid; place-items: center; color: var(--blanc);
}
.cle-rond svg { width: 45%; height: 45%; }
.cle-etape > span { min-width: 0; }
.cle-etape h3 {
  font-size: var(--cle-etape-titre); font-weight: 700; letter-spacing: .14em;
  text-transform: uppercase; color: var(--blanc); line-height: 1.2;
}
.cle-etape p {
  margin-top: .5em; font-size: var(--cle-etape-texte); line-height: 1.5;
  color: var(--blanc-55);
}
/* 12. Realisations
   --------------------------------------------------------------------------
   Texte a gauche, carrousel a droite : cinq vignettes dont quatre visibles.

   Le defilement est celui du navigateur, avec scroll-snap : pas de calcul de
   position, pas d'etat a tenir. Les fleches appellent scrollBy d'une
   vignette. On garde ainsi le glisser au doigt, la molette horizontale, la
   navigation au clavier et le rattrapage automatique en fin de course.
   -------------------------------------------------------------------------- */
.realisations {
  display: grid; grid-template-columns: var(--real-gauche) 1fr;
  background: var(--noir); align-items: center;
}
.real-texte {
  padding-inline: var(--gout); display: flex; flex-direction: column;
  justify-content: center; min-width: 0;
}
.real-etiq {
  font-size: var(--real-etiq); font-weight: 600; letter-spacing: .18em;
  text-transform: uppercase; color: var(--orange); margin-bottom: 1.3em;
}
.real-titre {
  font-size: var(--real-titre); font-weight: 900; line-height: 1.12;
  letter-spacing: -.005em; text-transform: uppercase; color: var(--blanc);
}
.real-texte .btn { margin-top: 2em; align-self: flex-start; }

.real-cadre { position: relative; min-width: 0; }
.real-piste {
  display: flex; gap: var(--real-gout);
  overflow-x: auto; scroll-snap-type: x mandatory;
  scrollbar-width: none; -ms-overflow-style: none;
  scroll-behavior: smooth;
}
.real-piste::-webkit-scrollbar { display: none; }
@media (prefers-reduced-motion: reduce) { .real-piste { scroll-behavior: auto; } }

.real-vignette {
  flex: none; scroll-snap-align: start; position: relative; overflow: hidden;
  /* Quatre vignettes et leurs trois gouttieres tiennent dans la piste ; la
     cinquieme depasse et se decouvre en defilant. */
  width: calc((100% - (var(--real-vue) - 1) * var(--real-gout)) / var(--real-vue));
  aspect-ratio: var(--real-ratio);
  background: radial-gradient(120% 100% at 50% 30%, #22201E, #0C0B0A 72%);
}
.real-vignette img { width: 100%; height: 100%; object-fit: cover; display: block; }
.real-vignette::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(0,0,0,0) 45%, rgba(0,0,0,.82) 100%);
}
.real-legende {
  position: absolute; left: 18px; right: 18px; bottom: 16px; z-index: 2;
}
.real-legende b {
  display: block; font-size: var(--real-nom); font-weight: 800;
  letter-spacing: .05em; text-transform: uppercase; color: var(--blanc);
}
.real-legende span {
  display: block; font-size: var(--real-meta); letter-spacing: .1em;
  text-transform: uppercase; color: var(--blanc-55); margin-top: .5em;
}

/* Les fleches surplombent le coin haut droit des vignettes, comme sur la
   maquette. Posees au-dessus de la piste, dans une bande a elles, elles
   allongeaient la section de 46 px et empietaient sur la section
   precedente ; posees ici, elles ne coutent rien en hauteur. Le haut des
   photographies etant sombre, le cercle blanc s'y detache. */
.real-fleches {
  position: absolute; z-index: 3;
  top: var(--real-fleches-h); right: var(--real-fleches-h);
  display: flex; align-items: center; gap: 8px;
}
.real-fleche {
  width: 30px; height: 30px; border-radius: 50%; padding: 0;
  border: 1px solid rgba(255,255,255,.3); background: none; color: var(--blanc);
  display: grid; place-items: center; cursor: pointer;
  transition: background .18s, border-color .18s, color .18s;
}
.real-fleche:hover { background: var(--blanc); border-color: var(--blanc); color: var(--noir); }
.real-fleche:disabled { opacity: .3; cursor: default; }
.real-fleche:disabled:hover { background: none; border-color: rgba(255,255,255,.3); color: var(--blanc); }
.real-fleche svg { width: 13px; height: 13px; }
.real-fleche:focus-visible { outline: 2px solid var(--orange); outline-offset: 2px; }

/* 13. Innovation
   --------------------------------------------------------------------------
   Texte a gauche, deux panneaux a droite separes par la meme diagonale que
   la section 3D - mais figee : il n'y a rien a comparer ici, seulement deux
   sujets a presenter cote a cote. Le decoupage reprend donc le procede du
   comparateur, sans le curseur.

   La legende du panneau de gauche vit dans le calque decoupe : elle est
   posee assez loin de la diagonale pour ne pas etre rognee.
   -------------------------------------------------------------------------- */
.innovation {
  display: grid; grid-template-columns: var(--inno-gauche) 1fr;
  background: var(--noir); align-items: stretch;
}
.inno-texte {
  display: flex; flex-direction: column; justify-content: center;
  padding: 0 var(--gout); min-width: 0;
}
.inno-etiq {
  font-size: var(--inno-etiq); font-weight: 600; letter-spacing: .18em;
  text-transform: uppercase; color: var(--orange); margin-bottom: 1.3em;
}
.inno-titre {
  font-size: var(--inno-titre); font-weight: 900; line-height: 1.14;
  letter-spacing: -.005em; text-transform: uppercase; color: var(--blanc);
}
.inno-chapo {
  margin-top: 1.3em; font-size: var(--inno-texte); line-height: 1.65;
  color: var(--blanc-80); max-width: 34ch;
}
.inno-texte .btn { margin-top: 1.9em; align-self: flex-start; }

.duo {
  --pos: var(--inno-coupe);
  position: relative; overflow: hidden;
  width: 100%; min-width: 0; aspect-ratio: var(--inno-ratio);
  background: radial-gradient(120% 140% at 70% 40%, #1E1B19, #0B0A09 72%);
}
.duo img { position: absolute; inset: 0; width: 100%; height: 100%;
           object-fit: cover; display: block; }
/* QUELLE BANDE GARDE-T-ON. Sans cette regle, « cover » prend celle du milieu,
   qui n'est presque jamais la bonne : sur le Reformer elle coupait le chassis,
   sur la tablette elle traversait l'ecran. On descend a gauche pour tenir la
   machine entiere et son logotype, on remonte legerement a droite pour garder
   le haut de l'ecran, ou se lit le nom de la marque. */
.duo-dessus img { object-position: center 68%; }
.duo-fond img   { object-position: center 42%; }
.duo-trait, .duo-dessus { position: absolute; inset: 0; }
.duo-trait {
  background: var(--blanc);
  clip-path: polygon(0 0,
    calc(var(--pos) + var(--sep-pente) + var(--sep-trait)) 0,
    calc(var(--pos) - var(--sep-pente) + var(--sep-trait)) 100%, 0 100%);
}
.duo-dessus {
  background: radial-gradient(120% 140% at 40% 35%, #262220, #100E0D 72%);
  clip-path: polygon(0 0,
    calc(var(--pos) + var(--sep-pente)) 0,
    calc(var(--pos) - var(--sep-pente)) 100%, 0 100%);
}
/* Voile sombre sous les legendes. */
/* Le voile du panneau ALLEGE, parce qu'il n'a plus a porter le texte : c'est la
   plaque, plus bas, qui s'en charge. A .5 en haut et .6 en bas il noircissait
   les bords au point de dessiner un cadre autour de la photographie - invisible
   tant que le panneau etait une bande de 4:1, evident des qu'il en montre le
   double. Il ne fait plus que poser l'image dans le noir de la page. */
.duo-fond::after, .duo-dessus::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(0,0,0,.34) 0%, rgba(0,0,0,.08) 45%, rgba(0,0,0,.40) 100%);
}
.duo-fond { position: absolute; inset: 0; }

.duo-legende {
  position: absolute; z-index: 2; top: 50%; transform: translateY(-50%);
  max-width: 32%;
}
/* UNE PLAQUE SOUS LE TEXTE, PAS UN VOILE SUR LA PHOTOGRAPHIE.
   Le voile des deux panneaux est un degrade VERTICAL : .5 en haut, .18 au
   milieu, .6 en bas. Or les legendes sont posees a « top: 50% » - exactement
   dans sa bande la plus faible. Mesure faite sur la page servie, sous les deux
   titres : les 5 % de pixels les plus clairs ne laissaient au blanc que 1,5:1
   a gauche et 1,7:1 a droite. La moyenne etait bonne (6,9:1 et 4,4:1) et c'est
   precisement le piege : le texte ne disparait pas partout, il disparait sur le
   sol eclaire et sur l'ecran de la tablette, la ou l'oeil se pose.

   Assombrir le panneau entier aurait suffi, mais il aurait fallu 77 % de noir
   pour tenir 4,5:1 sur ces hautes lumieres - la photographie n'aurait plus rien
   montre. La plaque ne couvre donc que l'aire du texte, s'eteint en ellipse, et
   laisse le reste du cliche intact. Resultat calcule sur les memes pixels :
   8,3:1 au coeur a gauche, 9,0:1 a droite, et 4,6:1 au bord de la plaque. */
.duo-legende::before {
  content: ""; position: absolute; z-index: -1;
  /* LA BOITE DEBORDE LARGEMENT, ET LE DEGRADE S'ETEINT DEDANS. Premiere
     version : une ellipse de 115 % dans une boite a peine plus grande que le
     texte. Le stop transparent tombait donc HORS de la boite, qui se coupait a
     .28 d'opacite - un rectangle plus clair, aux bords francs, parfaitement
     visible sur la photographie. Le degrade doit atteindre zero avant le bord. */
  inset: -44% -48% -48% -42%;
  background: radial-gradient(74% 84% at 30% 50%,
              rgba(0,0,0,.90) 0%, rgba(0,0,0,.74) 34%,
              rgba(0,0,0,.34) 64%, rgba(0,0,0,0) 100%);
}
/* A droite, le texte occupe la gauche de sa boite comme a gauche : meme
   ancrage, la difference d'avant n'avait pas lieu d'etre. */
.duo-dessus .duo-legende { left: 6%; }
.duo-fond .duo-legende { left: 55%; }
.duo-legende b {
  display: block; font-size: var(--inno-nom); font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase; color: var(--blanc);
}
.duo-legende p {
  margin-top: .7em; font-size: var(--inno-legende); line-height: 1.55;
  /* Plein blanc, et non 82 % : sur un aplat sombre un gris passe, sur une
     photographie il se confond avec les demi-teintes. */
  color: var(--blanc);
}
/* Une ombre courte rattrape le pixel clair isole que la plaque n'eteint pas
   completement - un reflet, un bord de projecteur. Elle ne se voit pas, elle
   tient juste le contour de la lettre. */
.duo-legende b, .duo-legende p { text-shadow: 0 1px 3px rgba(0,0,0,.55); }
.duo-legende .fl { margin-top: 1.1em; width: 20px; height: 9px; color: var(--orange); display: block; }

/* 14. Appel final
   --------------------------------------------------------------------------
   Une bande pleine largeur, texte centre sur une photographie assombrie.
   -------------------------------------------------------------------------- */
.final {
  position: relative; overflow: hidden;
  padding-inline: var(--gout); text-align: center;
  background: #100E0D;
}
.final img { position: absolute; inset: 0; width: 100%; height: 100%;
             object-fit: cover; display: block; }
/* Le voile assombrit la photographie pour que le titre blanc s'y detache.
   Il etait regle beaucoup trop haut : .62 au centre donnait plus de 10:1 de
   contraste, la ou un grand titre gras en demande 3. La piece disparaissait
   pour un confort de lecture dont on avait quatre fois trop.

   Les valeurs ci-dessous ont ete mesurees sur le cliche, et non estimees a
   l'oeil : sous le titre, le centieme de pixels le plus clair garde 6,3:1, et
   le pixel le plus clair de tous - le reflet de la baie vitree - 4,1:1. La
   marge reste donc confortable au-dessus du seuil, la piece se voit, et le
   texte ne vacille nulle part.

   Les bords restent plus sombres que le centre : ils ne portent aucun texte,
   leur role est de raccorder la bande au noir de la page.

   Si la photographie est remplacee, ces valeurs sont a reprendre : elles
   dependent du cliche, et une vue plus claire - une salle en plein jour -
   redemanderait un voile plus dense pour tenir le meme contraste. */
.final::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, rgba(0,0,0,.72) 0%, rgba(0,0,0,.48) 50%, rgba(0,0,0,.72) 100%);
}
.final-in { position: relative; z-index: 2; }
.final-titre {
  font-size: var(--final-titre); font-weight: 900; line-height: 1.14;
  letter-spacing: -.005em; text-transform: uppercase; color: var(--blanc);
}
.final .btn { margin-top: 1.7em; }

/* 15. Adaptations
   --------------------------------------------------------------------------
   Le dessin est edge to edge : le logo touche la marge gauche, le bouton la
   marge droite. Il n'y a donc pas de conteneur a centrer. Au-dela de la
   largeur de reference, toutes les cotes sont reexprimees en vw a partir du
   meme releve (valeur / 1440 x 100) : la composition grandit a l'identique
   au lieu de se distendre. En dessous, elles sont bornees par des clamp().
   -------------------------------------------------------------------------- */
@media (min-width: 1441px) {
  :root {
    --gout: 2.9167vw;
    --air: 2.7778vw;
    --tete-h: 8.4722vw;
    --tete-h-compacte: 5.8333vw;
    --logo-h: 2.6389vw;
    --logo-h-compacte: 1.875vw;
    --nav-taille: .7639vw;
    --nav-gout: 2.8819vw;
    --nav-cta: 5.6944vw;
    --cta-h: 2.7083vw;
    --langues-taille: .7639vw;
    --langues-gout: 1.0417vw;
    --langues-cta: 2.0833vw;
    --langues-nav: 3.1944vw;
    --heros-gauche: 6.25vw;
    --heros-haut: 7.7292vw;
    --heros-bas: 2.1528vw;
    --titre-taille: 3.6806vw;
    --chapo-taille: 1.0417vw;
    --chapo-large: 23.1944vw;
    --chapo-haut: .4931vw;
    --etiq-taille: .7639vw;
    --etapes-haut: 1.3889vw;
    --etapes-gout: 1.1111vw;
    --btn-h: 2.6389vw;
    --btn-taille: .7639vw;
    --btn-pad: 1.5278vw;
    --btn-plein-pad: 2.1528vw;
    --cta-pad: 1.5972vw;
    --btn-gout: .9722vw;
    --cta-gout: .6944vw;
    --btns-gout: 1.25vw;
    --fleche-l: 1.0417vw;
    --fleche-h: .625vw;

    --univers-taille: 3.4028vw;
    --univers-trait: .1042vw;
    --univers-puce: .5556vw;
    --univers-sous-haut: 1.9167vw;
    --univers-sous: .9028vw;

    --carte-gout: 1.1111vw;
    --carte-pad: 1.3889vw;
    --carte-titre: 1.25vw;
    --carte-texte: .7292vw;
    --carte-fleche: 1.25vw;
    --carte-titre-actif: 1.4583vw;
    --carte-texte-actif: .8333vw;

    --haut-taille: 3.3333vw;
    --haut-marge: 2.2222vw;

    --conc-etiq: .7639vw;
    --conc-titre: 2.2222vw;
    --conc-texte: .8333vw;
    --sep-poignee: 2.3611vw;

    --inno-etiq: .7639vw;
    --inno-titre: 1.875vw;
    --inno-texte: .8333vw;
    --inno-nom: 1.3889vw;
    --inno-legende: .9028vw;
    --final-titre: 2.2917vw;
  }
}

/* Sous 1240 px, les cotes de la maquette sont ramenees a l'echelle de la
   fenetre : le rapport entre les elements est conserve. */
/* Sous la largeur de reference, les memes cotes sont exprimees avec le meme
   coefficient (valeur / 1440 x 100 en vw) : la composition retrecit a
   l'identique, et le bloc de texte continue donc de tenir dans une image dont
   la hauteur suit, elle aussi, la largeur. Les bornes basses des clamp()
   n'entrent en jeu que sous 860 px, ou la mise en page bascule de toute
   facon en flux (voir plus bas). */
@media (max-width: 1439px) {
  :root {
    --gout: clamp(20px, 2.9167vw, 42px);
    --air: clamp(26px, 2.7778vw, 40px);
    --tete-h: clamp(72px, 8.4722vw, 122px);
    --tete-h-compacte: clamp(60px, 5.8333vw, 84px);
    --logo-h: clamp(28px, 2.6389vw, 38px);
    --logo-h-compacte: clamp(22px, 1.875vw, 27px);
    --nav-taille: clamp(10px, .7639vw, 11px);
    --nav-gout: clamp(18px, 2.8819vw, 41.5px);
    --nav-cta: clamp(24px, 5.6944vw, 82px);
    --cta-h: clamp(34px, 2.7083vw, 39px);
    --langues-taille: clamp(10px, .7639vw, 11px);
    --langues-gout: clamp(11px, 1.0417vw, 15px);
    --langues-cta: clamp(16px, 2.0833vw, 30px);
    --langues-nav: clamp(18px, 3.1944vw, 46px);
    --heros-gauche: clamp(20px, 6.25vw, 90px);
    --heros-haut: clamp(34px, 7.7292vw, 111.3px);
    --heros-bas: clamp(16px, 2.1528vw, 31px);
    --titre-taille: clamp(28px, 3.6806vw, 53px);
    --chapo-taille: clamp(13px, 1.0417vw, 15px);
    --chapo-large: clamp(240px, 23.1944vw, 334px);
    --chapo-haut: clamp(6px, .4931vw, 7.1px);
    --etiq-taille: clamp(10px, .7639vw, 11px);
    --etapes-haut: clamp(14px, 1.3889vw, 20px);
    --etapes-gout: clamp(10px, 1.1111vw, 16px);
    --btn-h: clamp(34px, 2.6389vw, 38px);
    --btn-taille: clamp(10px, .7639vw, 11px);
    --btn-pad: clamp(14px, 1.5278vw, 22px);
    --btn-plein-pad: clamp(18px, 2.1528vw, 31px);
    --cta-pad: clamp(14px, 1.5972vw, 23px);
    --btn-gout: clamp(9px, .9722vw, 14px);
    --cta-gout: clamp(8px, .6944vw, 10px);
    --btns-gout: clamp(10px, 1.25vw, 18px);
    --fleche-l: clamp(11px, 1.0417vw, 15px);
    --fleche-h: clamp(7px, .625vw, 9px);

    --univers-taille: clamp(26px, 3.4028vw, 49px);
    --univers-trait: clamp(1px, .1042vw, 1.5px);
    --univers-puce: clamp(5px, .5556vw, 8px);
    --univers-sous-haut: clamp(16px, 1.9167vw, 27.6px);
    --univers-sous: clamp(10px, .9028vw, 13px);

    --carte-gout: clamp(8px, 1.1111vw, 16px);
    --carte-pad: clamp(15px, 1.3889vw, 20px);
    --carte-titre: clamp(16px, 1.25vw, 18px);
    /* Le texte des cartes ne descend pas sous sa taille de reference : sur
       une carte plus etroite il vaut mieux une ligne de plus qu'un corps
       illisible. */
    --carte-texte: 10.5px;
    --carte-fleche: clamp(14px, 1.25vw, 18px);
    --carte-titre-actif: clamp(16px, 1.4583vw, 21px);
    --carte-texte-actif: 12px;

    --haut-taille: clamp(42px, 3.3333vw, 48px);
    --haut-marge: clamp(18px, 2.2222vw, 32px);

    --conc-etiq: clamp(10px, .7639vw, 11px);
    --conc-titre: clamp(22px, 2.2222vw, 32px);
    --conc-texte: clamp(11.5px, .8333vw, 12px);
    --sep-poignee: clamp(30px, 2.3611vw, 34px);

    --inno-etiq: clamp(10px, .7639vw, 11px);
    --inno-titre: clamp(21px, 1.875vw, 27px);
    --inno-texte: clamp(11.5px, .8333vw, 12px);
    --inno-nom: clamp(17px, 1.3889vw, 20px);
    --inno-legende: clamp(12px, .9028vw, 13px);
    --final-titre: clamp(23px, 2.2917vw, 33px);
  }
}

/* Entre 860 et 1100 px, la bande au ratio natif devient plus basse que le
   bloc de texte : les corps de caractere y sont bornes par les clamp() et ne
   retrecissent plus. On donne donc a l'image une hauteur plancher. Elle est
   alors legerement recadree - quelques pour cent en hauteur, sur des vues
   d'interieur ou cela ne se voit pas - plutot que de laisser les boutons
   sortir sous l'image. */
@media (min-width: 860px) and (max-width: 1199px) {
  .heros-media { min-height: 350px; }
}

/* Navigation repliee : la barre de menus ne tient plus. */
@media (max-width: 1099px) {
  /* Le menu se replie. Le selecteur de langue, lui, reste dans la barre, a
     gauche du bouton d'ouverture : changer de langue ne doit pas obliger a
     ouvrir un menu. Le bouton d'appel de l'en-tete disparait - celui du
     heros, juste en dessous, le remplace. */
  .js .burger { display: flex; order: 3; margin-left: var(--langues-gout); }
  .langues { order: 2; margin-left: auto; }
  /* Au doigt, 24 px ne suffisent plus : la boite passe a 44. */
  .langues a { min-width: 44px; min-height: 44px; }
  .tete-cta { display: none; }
  /* Sans script, le menu ne se replie pas : il passe simplement a la ligne,
     sous l'en-tete, en liste verticale. Moins compact, mais entierement
     utilisable - ce qui n'etait pas le cas jusqu'ici. */
  html:not(.js) .tete { height: auto; flex-wrap: wrap; padding-bottom: 10px; }
  html:not(.js) .nav {
    flex: 0 0 100%; order: 9; flex-direction: column; align-items: stretch;
    gap: 0; border-top: 1px solid rgba(255,255,255,.1); margin-top: 8px;
  }
  html:not(.js) .nav a, html:not(.js) .deroulant > summary {
    padding: 14px 0; font-size: 14px;
  }

  .js .nav {
    position: absolute; left: 0; right: 0; top: 100%;
    flex-direction: column; align-items: stretch; gap: 0;
    background: #0A0A0A; border-top: 1px solid rgba(255,255,255,.1);
    padding: 6px var(--marge) 24px;
    visibility: hidden; opacity: 0; transform: translateY(-8px);
    transition: opacity .2s, transform .2s, visibility .2s;
    max-height: 76vh; overflow: auto;
  }
  .js .nav.ouvert { visibility: visible; opacity: 1; transform: none; }
  .nav a {
    padding: 16px 0; font-size: 15px; letter-spacing: .12em;
    border-bottom: 1px solid rgba(255,255,255,.08);
  }
}

/* En dessous de 860 px, la bande de quatre univers devient trop basse pour
   loger le texte. La photographie passe alors en plan de fond recadre et le
   heros reprend une hauteur commandee par son contenu : plus de bloc cale en
   bas d'une image au ratio fixe, donc plus de vide entre les etapes et les
   boutons. */
@media (max-width: 859px) {
  .heros { position: relative; }
  .heros-media {
    position: absolute; inset: 0; aspect-ratio: auto; height: 100%;
  }
  .heros-media img { object-position: 30% center; }
  .heros-voile {
    background:
      linear-gradient(180deg, rgba(0,0,0,.66) 0%, rgba(0,0,0,.5) 42%, rgba(0,0,0,.88) 100%),
      linear-gradient(90deg, rgba(0,0,0,.58) 0%, rgba(0,0,0,.28) 100%);
  }
  .heros-in {
    position: relative; inset: auto;
    min-height: 66vh;
    padding: 62px var(--marge) 62px var(--marge);
    justify-content: center;
  }
  .heros h1 { font-size: clamp(34px, 9.4vw, 56px); }
  .heros-chapo { margin-top: 18px; max-width: 34ch; font-size: 15px; }
  /* Les quatre etapes passent en deux colonnes : a la ligne, une puce isolee
     en tete de ligne se lirait comme une coquille. */
  .heros-etapes {
    display: grid; grid-template-columns: repeat(2, max-content);
    gap: 12px 30px; margin-top: 26px; font-size: 11px;
  }
  .heros-etapes li { gap: 12px; }
  .heros-etapes li::before {
    content: ""; width: 4px; height: 4px; border-radius: 50%;
    background: var(--orange); flex: none;
  }
  .heros-btns { margin-top: 34px; gap: 14px; }
  .heros-btns .btn { height: 46px; font-size: 11px; }
}

/* Sous 1100 px, le bloc de texte de l'innovation passe au-dessus du duo, qui
   prend toute la largeur dans un format un peu moins allonge. */
@media (max-width: 1099px) {
  .innovation { grid-template-columns: 1fr; row-gap: var(--air); }
  :root { --inno-ratio: 2 / 1; }
  .inno-titre { font-size: clamp(24px, 4.6vw, 34px); }
  .duo-legende { max-width: 36%; }
}

/* Sous 700 px, une diagonale qui porte deux textes ne se lit plus : les deux
   panneaux tombent l'un sous l'autre, chacun avec sa legende, et la
   separation disparait - elle n'aurait plus rien a separer. */
@media (max-width: 699px) {
  .duo { aspect-ratio: auto; display: grid; gap: 10px; background: none; }
  .duo-trait { display: none; }
  .duo-fond, .duo-dessus {
    position: relative; inset: auto; clip-path: none;
    aspect-ratio: 16 / 9; overflow: hidden;
  }
  .duo-legende { top: auto; bottom: 20px; transform: none; max-width: none; right: 20px; }
  .duo-dessus .duo-legende, .duo-fond .duo-legende { left: 20px; }
}

/* Sous 1200 px, quatre etapes de front deviennent quatre colonnes trop
   etroites : le titre passe au-dessus et les etapes en deux rangees de deux.
   Les filets de liaison disparaissent - ils relient une file, pas une
   grille, et un trait qui ne mene nulle part en fin de rangee se lirait
   comme une coquille. */
@media (max-width: 1199px) {
  .cle { grid-template-columns: 1fr; gap: clamp(26px, 3.4vw, 40px); }
  .cle-etapes { display: grid; grid-template-columns: repeat(2, 1fr);
                gap: clamp(22px, 3vw, 34px); }
  .cle-etape::after { display: none; }
  .cle-etape p { max-width: 32ch; }
}
@media (max-width: 659px) {
  .cle-etapes { grid-template-columns: 1fr; }
}

/* Le carrousel suit la meme logique que les cartes : moins de vignettes de
   front a mesure que la place manque. Sous 560 px on en montre une et demie,
   pour que la suivante se devine et qu'on comprenne qu'il y a a faire
   defiler. */
@media (max-width: 1199px) {
  .realisations { grid-template-columns: 1fr; row-gap: var(--air); }
  .real-titre { font-size: clamp(26px, 5.6vw, 38px); }
  .real-cadre { padding-inline: var(--real-pad); }
  .real-fleches { position: static; margin-bottom: 14px;
                  justify-content: flex-end; }
  :root { --real-vue: 3; }
}
@media (max-width: 799px)  { :root { --real-vue: 2; } }
@media (max-width: 559px)  { :root { --real-vue: 1.35; } }

/* Sous 1100 px, un bloc de texte a 33 % laisse trop peu de place au titre, et
   une image en 3:1 devient une meurtriere. Les deux blocs se superposent
   alors, le comparateur prenant toute la largeur dans un format moins
   allonge. */
@media (max-width: 1099px) {
  .conception { grid-template-columns: 1fr; row-gap: var(--air); }
  :root { --conc-ratio: 16 / 9; --sep-pente: 4%; }
  .conception-titre { font-size: clamp(26px, 6.6vw, 40px); }
  .conception-chapo { max-width: 46ch; }
}

/* Sous 1200 px, quatre cartes de front deviennent des colonnes trop etroites
   pour porter deux lignes de texte : elles passent a deux de front, puis a
   une seule sur telephone. Les cartes redevenant larges, les corps repassent
   a des valeurs fixes et confortables au lieu de continuer a retrecir avec la
   fenetre. */
@media (max-width: 1199px) {
  .cartes { grid-template-columns: repeat(2, 1fr); }
  :root {
    --carte-pad: 24px;
    --carte-titre: 20px;
    --carte-texte: 12px;
    --carte-fleche: 20px;
    --carte-titre-actif: 23px;
    --carte-texte-actif: 13.5px;
  }
}
@media (max-width: 559px) {
  .cartes { grid-template-columns: 1fr; }
  :root { --carte-ratio: 360 / 260; }
}

/* Sans survol - tablettes et telephones - la carte reste dans son etat de
   repos et le texte court suffit : le doigt ne survole pas, il ouvre. Le
   texte long est un enrichissement du bureau, pas une information qui
   manquerait ailleurs : il est aussi celui que lisent les lecteurs d'ecran,
   sur toutes les machines. */
@media (hover: none) {
  .carte:hover .carte-teinte { opacity: 0; }
  .carte:hover .carte-repos { opacity: 1; }
  .carte:hover .carte-actif { opacity: 0; }
}

/* Sous 860 px, quatre mots cales d'une marge a l'autre deviendraient un
   ruban de caracteres trop petits pour se lire. Ils passent donc sur deux
   rangees, a un corps qui tient debout. Les points de separation
   disparaissent : en bout de rangee, un point orange isole se lirait comme
   une coquille. */
@media (max-width: 859px) {
  .univers-mots {
    flex-wrap: wrap; justify-content: center;
    column-gap: clamp(18px, 5vw, 44px); row-gap: clamp(6px, 1.6vw, 14px);
    margin-top: 0;
  }
  /* Le corps est calcule pour que deux mots tiennent toujours sur une
     rangee, y compris les plus larges - « Pilates » et « Combat » - une fois
     comptees l'approche et la sur-largeur due a l'inclinaison. Au-dessus,
     le francais passait a trois rangees la ou l'anglais en gardait deux :
     la bande n'avait plus la meme silhouette d'une langue a l'autre. */
  .univers-mots li { font-size: clamp(26px, 7.6vw, 52px); }
  .univers-mots .puce { display: none; }
  .univers-sous { margin-top: clamp(16px, 4vw, 28px); }
}

@media (max-width: 519px) {
  .heros-in { min-height: 0; padding-block: 54px 58px; }
  .heros-etapes { grid-template-columns: max-content; gap: 10px; }
  .heros-btns { width: 100%; }
  .heros-btns .btn { width: 100%; }
}

/* =============================================================================
   16. RESERVE D'IMAGE
   -----------------------------------------------------------------------------
   Un emplacement dont le cliche n'est pas encore livre. Plutot qu'un trou ou
   une icone cassee, on montre la place exacte que prendra l'image, avec le nom
   du fichier attendu et ses cotes : la maquette se lit, et le photographe sait
   ce qu'on attend de lui. La rayure oblique dit « ceci n'est pas l'image ».
   Le bloc est produit par le serveur ; le jour ou le fichier arrive, il
   disparait de lui-meme.
   ========================================================================== */
.reserve {
  position: relative; display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%; min-height: 120px; overflow: hidden;
  text-align: center; padding: 22px;
  background-color: #221F1D;
  background-image: repeating-linear-gradient(
      -45deg, transparent 0 9px, rgba(255,255,255,.035) 9px 18px);
  outline: 1px dashed rgba(255,255,255,.16); outline-offset: -10px;
}
.reserve-etiq {
  position: absolute; top: 0; right: 0;
  background: var(--orange); color: var(--noir);
  font-size: 10px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
  padding: 7px 14px;
}
.reserve-in { display: flex; flex-direction: column; gap: .55em; max-width: 34ch; }
.reserve-nom {
  font-size: 12px; font-weight: 800; letter-spacing: .16em; text-transform: uppercase;
  color: var(--orange); word-break: break-word;
}
.reserve-sujet { font-size: 12.5px; line-height: 1.5; color: var(--blanc-80); }
.reserve-cote  { font-size: 11px; letter-spacing: .1em; color: var(--blanc-55); }
.reserve-etat  {
  font-size: 10px; font-weight: 600; letter-spacing: .22em; text-transform: uppercase;
  color: var(--blanc-55); margin-top: .3em;
}

/* =============================================================================
   17. CHIFFRES
   -----------------------------------------------------------------------------
   Quatre nombres, quatre filets, rien d'autre. Le chiffre porte tout : il est
   gros, l'unite est petite, et le « + » qui le precede reste discret pour ne
   pas voler la lecture au nombre lui-meme.
   Le compteur monte a l'entree dans la vue (voir site.js), mais la valeur
   finale est dans le HTML : sans script, ou avec un mouvement reduit demande,
   le chiffre est simplement affiche.
   ========================================================================== */
.chiffres { background: var(--noir); }
.chiffres-in {
  padding-inline: var(--gout);
  display: grid; grid-template-columns: repeat(4, 1fr);
}
.chiffre {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  padding-inline: clamp(8px, 1.5vw, 24px);
}
/* Le filet separe, il n'encadre pas : pas de trait avant le premier. */
.chiffre + .chiffre { border-left: 1px solid var(--filet); }
.chiffre-nb {
  display: flex; align-items: baseline; justify-content: center;
  font-size: clamp(38px, 3.8vw, 54px); font-weight: 900; line-height: 1;
  letter-spacing: -.02em; color: var(--blanc);
}
.chiffre-nb span { font-size: .5em; color: var(--orange); margin-right: .12em; }
.chiffre-lib {
  margin-top: .9em;
  font-size: var(--etiq-taille); font-weight: 600; line-height: 1.5;
  letter-spacing: .14em; text-transform: uppercase; color: var(--blanc-80);
}
.chiffre-lib b { display: block; font-weight: 600; }
.chiffre-lib i { display: block; font-style: normal; color: var(--blanc-55); }

@media (max-width: 859px) {
  .chiffres-in { grid-template-columns: repeat(2, 1fr); row-gap: clamp(26px, 5vw, 40px); }
  /* En deux rangees, le filet vertical ne doit separer que les colonnes. */
  .chiffre + .chiffre { border-left: 0; }
  .chiffre:nth-child(even) { border-left: 1px solid var(--filet); }
}

/* =============================================================================
   18. CONTACT
   -----------------------------------------------------------------------------
   Deux colonnes : a gauche ce qu'on peut faire tout de suite - appeler, ecrire,
   venir - a droite le formulaire, pour ceux qui preferent poser leur projet par
   ecrit. L'ordre compte : le visiteur presse ne doit pas avoir a lire un
   formulaire pour trouver un numero.
   ========================================================================== */
.contact { background: var(--noir); }
.contact-in {
  padding-inline: var(--gout);
  display: grid; grid-template-columns: minmax(0, 34%) minmax(0, 1fr);
  gap: clamp(32px, 5vw, 78px);
}
.contact-etiq {
  font-size: var(--etiq-taille); font-weight: 600; letter-spacing: .18em;
  text-transform: uppercase; color: var(--orange); margin-bottom: 1.3em;
}
.contact-titre {
  font-size: clamp(24px, 2.3vw, 33px); font-weight: 900; line-height: 1.12;
  letter-spacing: -.005em; text-transform: uppercase; color: var(--blanc);
}
.contact-chapo {
  margin-top: 1.5em; font-size: 12.5px; line-height: 1.75; color: var(--blanc-80);
  max-width: 42ch;
}
.contact-liste { margin-top: 2.2em; display: grid; gap: 18px; }
.contact-ligne { display: grid; grid-template-columns: 26px 1fr; gap: 12px; align-items: start; }
.contact-ligne svg { width: 16px; height: 16px; margin-top: 3px; color: var(--orange); }
.contact-cle {
  display: block; font-size: 10px; font-weight: 600; letter-spacing: .2em;
  text-transform: uppercase; color: var(--blanc-55); margin-bottom: .45em;
}
.contact-val { font-size: 13.5px; line-height: 1.5; color: var(--blanc); font-style: normal; }
.contact-val a { border-bottom: 1px solid var(--trait); transition: border-color .18s; }
.contact-val a:hover { border-color: var(--orange); }
.contact-delai {
  margin-top: 2em; padding-top: 1.4em; border-top: 1px solid var(--filet);
  font-size: 11.5px; letter-spacing: .04em; color: var(--blanc-55);
}
/* Une valeur que le client doit encore fournir. On l'affiche comme telle :
   un faux numero dans une maquette finit toujours par etre pris pour vrai. */
.attente {
  display: inline-block; font-size: 10px; font-weight: 600; letter-spacing: .18em;
  text-transform: uppercase; color: var(--blanc-55);
  border: 1px dashed rgba(255,255,255,.22); border-radius: 2px; padding: 4px 9px;
}

/* --- Formulaire ---------------------------------------------------------- */
.form-cadre { background: #131110; border: 1px solid var(--filet); padding: clamp(22px, 2.6vw, 36px); }
.form-titre {
  font-size: 15px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: var(--blanc); margin-bottom: 1.6em;
}
.form-grille { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px 20px; }
.champ { display: flex; flex-direction: column; }
.champ--large { grid-column: 1 / -1; }
.champ label {
  font-size: 10px; font-weight: 600; letter-spacing: .18em; text-transform: uppercase;
  color: var(--blanc-55); margin-bottom: .7em;
}
.champ label em { font-style: normal; text-transform: none; letter-spacing: .04em; opacity: .8; }
.champ input, .champ select, .champ textarea {
  font: inherit; font-size: 13px; color: var(--blanc);
  background: #0B0A09; border: 1px solid rgba(255,255,255,.16); border-radius: 2px;
  padding: 11px 13px; width: 100%; min-width: 0;
  transition: border-color .18s, background .18s;
}
.champ textarea { min-height: 118px; resize: vertical; line-height: 1.6; }
.champ select { appearance: none; padding-right: 34px;
  background-image: linear-gradient(45deg, transparent 50%, rgba(255,255,255,.5) 50%),
                    linear-gradient(135deg, rgba(255,255,255,.5) 50%, transparent 50%);
  background-position: right 15px top 52%, right 10px top 52%;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; }
.champ input:hover, .champ select:hover, .champ textarea:hover { border-color: rgba(255,255,255,.3); }
.champ input:focus, .champ select:focus, .champ textarea:focus {
  outline: none; border-color: var(--orange); background: #100E0D;
}
.champ input::placeholder, .champ textarea::placeholder { color: rgba(255,255,255,.3); }
.champ-aide { margin-top: .7em; font-size: 11px; line-height: 1.5; color: var(--blanc-55); }
.champ--erreur input, .champ--erreur select, .champ--erreur textarea { border-color: #E05A3A; }
.champ-err { margin-top: .6em; font-size: 11.5px; color: #FF8A66; }

/* Le piege a robots : un champ que personne ne voit et que seuls les
   automates remplissent. Il sort de l'ecran sans etre masque, pour qu'un
   robot le trouve et le remplisse ; aria-hidden et tabindex l'ecartent des
   lecteurs d'ecran et du clavier. */
.piege { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.form-accord { grid-column: 1 / -1; display: grid; grid-template-columns: 18px 1fr; gap: 12px; align-items: start; }
.form-accord input { width: 18px; height: 18px; margin-top: 2px; accent-color: var(--orange); }
.form-accord label { font-size: 11.5px; line-height: 1.6; color: var(--blanc-80);
                     letter-spacing: 0; text-transform: none; margin: 0; }
.form-accord a { color: var(--orange); border-bottom: 1px solid rgba(232,98,31,.4); }
.form-accord a:hover { border-color: var(--orange); }
.form-pied { grid-column: 1 / -1; display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.form-requis { font-size: 11px; color: var(--blanc-55); }

.form-message { padding: 18px 20px; margin-bottom: 24px; border-left: 2px solid var(--orange); background: rgba(232,98,31,.08); }
.form-message--ok { border-color: #4CAF7D; background: rgba(76,175,125,.09); }
.form-message b { display: block; font-size: 13.5px; margin-bottom: .5em; color: var(--blanc); }
.form-message p { font-size: 12px; line-height: 1.6; color: var(--blanc-80); }
.form-message ul { margin-top: .6em; }
.form-message li { font-size: 12px; line-height: 1.7; color: var(--blanc-80); }
.form-message li::before { content: "- "; color: var(--orange); }

@media (max-width: 999px) {
  .contact-in { grid-template-columns: 1fr; }
}
@media (max-width: 559px) {
  .form-grille { grid-template-columns: 1fr; }
}

/* =============================================================================
   19. PIED DE PAGE
   -----------------------------------------------------------------------------
   Quatre colonnes et une barre basse. C'est le dernier endroit ou le visiteur
   cherche une preuve d'existence : coordonnees, mentions, auteur du site. Il
   porte aussi la seule navigation disponible en bas d'une page longue.
   ========================================================================== */
.pied { background: #080807; border-top: 1px solid var(--filet); }
/* LA COLONNE CONTACT PORTE TROIS IMPLANTATIONS, elle ne peut plus valoir 1fr.
   Elle passe a 2,4 unites - la grille du pied garde ses quatre colonnes et son
   equilibre, mais France, Canada et Caraibes tiennent cote a cote au lieu de
   se casser en trois lignes chacune. */
.pied-in {
  padding: var(--air) var(--gout);
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 2.4fr);
  gap: clamp(28px, 3.4vw, 56px);
}
/* Le courriel est commun aux trois : il se pose au-dessus, une fois. */
.pied-courriel { margin-bottom: clamp(16px, 1.8vw, 24px); }
.pied-pays {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(14px, 1.6vw, 26px);
}
/* Un cran sous .pied-titre : c'est un intitule dans une colonne, pas un titre
   de colonne. Assez present pour qu'on sache de quel pays on lit l'adresse. */
.pied-pays-titre {
  font-size: 10px; font-weight: 700; letter-spacing: .18em; text-transform: uppercase;
  color: var(--blanc-80); margin-bottom: 1.1em;
}
/* Sous 1200 px la colonne n'a plus la largeur de trois blocs : ils passent a
   deux, puis a un quand le pied lui-meme s'empile. */
@media (max-width: 1199px) {
  .pied-pays { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 899px) {
  .pied-pays { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 639px) {
  .pied-pays { grid-template-columns: 1fr; gap: clamp(18px, 4vw, 26px); }
}
.pied-marque .logo { margin-bottom: 20px; }
.pied-base { font-size: 12px; line-height: 1.75; color: var(--blanc-55); max-width: 34ch; }
.pied-titre {
  font-size: 10px; font-weight: 700; letter-spacing: .22em; text-transform: uppercase;
  color: var(--blanc); margin-bottom: 1.6em;
}
.pied-liste { display: grid; gap: 11px; }
.pied-liste a, .pied-liste span { font-size: 12.5px; line-height: 1.4; color: var(--blanc-55); transition: color .18s; }
.pied-liste a:hover { color: var(--orange); }
/* L'adresse n'avait pas de taille et heritait du corps de la page : elle
   s'affichait plus grosse que les liens qui l'entourent. Invisible tant qu'il
   n'y en avait qu'une, voyant des qu'il y en a trois cote a cote - et la plus
   longue, celle de Montreal, se cassait en deux lignes pour rien. */
.pied-liste address { font-style: normal; font-size: 12.5px; line-height: 1.5; color: var(--blanc-55); }
.pied-reseaux { display: flex; gap: 12px; margin-top: 18px; }
.pied-reseaux a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border: 1px solid var(--trait); border-radius: 50%;
  color: var(--blanc-80); transition: border-color .18s, color .18s;
}
.pied-reseaux a:hover { border-color: var(--orange); color: var(--orange); }
.pied-reseaux svg { width: 15px; height: 15px; }

.pied-bas {
  border-top: 1px solid var(--filet);
  padding: 20px var(--gout);
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px 26px; flex-wrap: wrap;
}
/* 55 % de blanc, et pas moins : en dessous, ces 11 px passent sous le seuil
   AA de 4,5:1 une fois la transparence composee sur le fond. */
.pied-bas p, .pied-bas a { font-size: 11px; letter-spacing: .04em; color: var(--blanc-55); }
.pied-legal { display: flex; gap: 22px; flex-wrap: wrap; }
.pied-legal a { transition: color .18s; }
.pied-legal a:hover, .pied-bas a:hover { color: var(--orange); }
.pied-bas a[aria-current="page"] { color: var(--blanc-80); }

@media (max-width: 899px) {
  .pied-in { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pied-marque { grid-column: 1 / -1; }
}
@media (max-width: 519px) {
  .pied-in { grid-template-columns: 1fr; }
  .pied-bas { flex-direction: column; align-items: flex-start; }
}

/* =============================================================================
   20. PAGES ANNEXES (legales, accessibilite)
   -----------------------------------------------------------------------------
   De la lecture, et rien que de la lecture : une colonne de texte a la mesure
   de l'oeil - autour de 70 signes - et un sommaire qui reste sous les yeux
   pendant qu'on descend. Ces pages sont consultees pour trouver une
   information precise, pas pour etre lues d'un bout a l'autre.
   ========================================================================== */
.page-tete { background: var(--noir); }
.page-tete-in { padding-inline: var(--gout); max-width: 900px; }
.page-titre {
  font-size: clamp(28px, 3.2vw, 46px); font-weight: 900; line-height: 1.08;
  letter-spacing: -.01em; text-transform: uppercase; color: var(--blanc);
}
.page-chapo { margin-top: 1.2em; font-size: 13.5px; line-height: 1.8; color: var(--blanc-80); max-width: 62ch; }
.page-revision { margin-top: 1.8em; font-size: 11px; letter-spacing: .12em;
                 text-transform: uppercase; color: var(--blanc-55); }

.page-corps { background: var(--noir); }
.page-corps-in {
  padding-inline: var(--gout);
  display: grid; grid-template-columns: minmax(0, 220px) minmax(0, 1fr);
  gap: clamp(30px, 4vw, 70px);
}
.sommaire { align-self: start; position: sticky; top: calc(var(--colle-haut) + 30px); }
.sommaire-titre {
  font-size: 10px; font-weight: 700; letter-spacing: .22em; text-transform: uppercase;
  color: var(--blanc); margin-bottom: 1.4em;
}
.sommaire ol { display: grid; gap: 10px; counter-reset: s; }
.sommaire a {
  display: block; font-size: 12px; line-height: 1.45; color: var(--blanc-55);
  padding-left: 14px; border-left: 1px solid var(--filet); transition: color .18s, border-color .18s;
}
.sommaire a:hover { color: var(--blanc); border-color: var(--orange); }

.prose { max-width: 68ch; }
.prose > section + section { margin-top: clamp(30px, 3.4vw, 46px); }
.prose h2 {
  font-size: clamp(15px, 1.35vw, 19px); font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--blanc); margin-bottom: 1em;
  padding-bottom: .8em; border-bottom: 1px solid var(--filet);
  scroll-margin-top: 24px;
}
.prose p { font-size: 13px; line-height: 1.85; color: var(--blanc-80); }
.prose p + p { margin-top: 1.1em; }
.prose ul { margin-top: 1em; display: grid; gap: .7em; }
.prose li { font-size: 13px; line-height: 1.7; color: var(--blanc-80); padding-left: 18px; position: relative; }
.prose li::before { content: ""; position: absolute; left: 0; top: .62em;
                    width: 6px; height: 1px; background: var(--orange); }
.prose a { color: var(--orange); border-bottom: 1px solid rgba(232,98,31,.4); }
.prose a:hover { border-color: var(--orange); }

/* Le retrait de la mesure d'audience, sur la page de confidentialite.
   C'est le seul bouton d'action d'une page legale : il est encadre pour se
   distinguer de la prose, sans quoi il se lirait comme un lien de plus au fil
   du texte et personne ne le trouverait. Le fond change quand le retrait est
   actif, de sorte que l'etat se voie avant meme d'etre lu. */
.mesure-etat {
  margin-top: 1.6em; padding: 14px 18px; border: 1px solid var(--filet);
  border-left: 2px solid var(--orange); background: rgba(255,255,255,.03);
}
.prose .mesure-etat strong { color: var(--blanc); font-weight: 600; }
.mesure-retrait { margin-top: 14px; }
.mesure-note { margin-top: 12px !important; font-size: 12px !important; color: var(--blanc-55) !important; }

/* Le tableau d'identite : deux colonnes, sans bordure, lisible en diagonale. */
.fiche { margin-top: 1.4em; display: grid; gap: 0; }
.fiche div {
  display: grid; grid-template-columns: minmax(0, 190px) minmax(0, 1fr); gap: 8px 24px;
  padding: 11px 0; border-bottom: 1px solid rgba(255,255,255,.07);
}
.fiche dt { font-size: 11px; font-weight: 600; letter-spacing: .14em;
            text-transform: uppercase; color: var(--blanc-55); }
.fiche dd { margin: 0; font-size: 13px; line-height: 1.6; color: var(--blanc); }

@media (max-width: 899px) {
  .page-corps-in { grid-template-columns: 1fr; }
  .sommaire { position: static; }
  .sommaire ol { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
}
@media (max-width: 519px) {
  .fiche div { grid-template-columns: 1fr; gap: 4px; }
}

/* -----------------------------------------------------------------------------
   Reserve posee en fond, sous un texte ou derriere une decoupe.
   Au centre, son contenu entrerait en collision avec la legende du duo ou avec
   le titre de l'appel final ; il se range donc dans un angle, du cote qui reste
   visible apres decoupe : a gauche pour le panneau de gauche, a droite pour
   celui du dessous, qui n'apparait qu'a droite de la diagonale.
   -------------------------------------------------------------------------- */
.reserve--fond { align-items: flex-start; justify-content: flex-start; padding: 20px 24px; }
.reserve--fond .reserve-in { max-width: 30ch; text-align: left; gap: .3em; }
.duo-fond .reserve--fond { justify-content: flex-end; }
.duo-fond .reserve--fond .reserve-in { text-align: right; }

/* Dans l'appel final, l'image est un fond : la reserve prend sa place, et
   passe au-dessus du voile assombrissant, qui n'a plus rien a assombrir. */
.final .reserve { position: absolute; inset: 0; z-index: 1; }

/* Une valeur en attente ne doit pas s'etirer sur toute la colonne. */
.pied-liste .attente { justify-self: start; }

/* Dans un angle, la place manque : le nom et les cotes tiennent sur une seule
   ligne et l'etat disparait - la rayure dit deja que l'image n'est pas la. */
.reserve--fond .reserve-in {
  display: grid; grid-template-columns: auto auto;
  grid-template-areas: "nom cote" "sujet sujet";
  justify-content: start; column-gap: 16px; row-gap: .35em;
}
.duo-fond .reserve--fond .reserve-in { justify-content: end; }
.reserve--fond .reserve-nom   { grid-area: nom; }
.reserve--fond .reserve-cote  { grid-area: cote; align-self: baseline; }
.reserve--fond .reserve-sujet { grid-area: sujet; font-size: 12px; line-height: 1.45; }
.reserve--fond .reserve-etat  { display: none; }

/* Le duo n'a que 250 px de haut et sa legende occupe le milieu : dans ces
   deux angles, on s'en tient a ce qui est indispensable - le nom du fichier
   et ses cotes. La consigne de prise de vue reste lisible partout ailleurs. */
.duo .reserve--fond .reserve-sujet { display: none; }

/* =============================================================================
   21. EN-TETE DES PAGES INTERIEURES
   -----------------------------------------------------------------------------
   Le meme dispositif que le heros de l'accueil, en plus court : une image
   pleine largeur, un voile qui la fonce du cote du texte, et le titre coupe a
   mi-capitale par son filet. C'est ce qui fait qu'une page interieure se
   reconnait comme appartenant au meme site - pas une couleur, une grammaire.

   Le filet du titre n'est pas redessine : la regle du heros est etendue a
   .titre-filet, et la page se contente de redefinir --titre-taille pour son
   propre corps. Une seule mecanique, deux echelles.
   ========================================================================== */
.entete { position: relative; overflow: hidden; background: var(--noir); }
.entete-media { position: absolute; inset: 0; }
.entete-media img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
}
/* La reserve prend la place de l'image, mais reste une boite flexible :
   lui imposer display:block lui ferait perdre le centrage de son contenu.
   Elle passe au-dessus du voile - il n'y a pas de photographie a foncer -
   et se range a droite, la ou le titre n'est pas. */
.entete-media .reserve { position: absolute; inset: 0; z-index: 2; }
.entete-media .reserve--fond { justify-content: flex-end; }
.entete-media .reserve--fond .reserve-in { justify-content: end; text-align: right; }
/* LE VOILE DES EN-TETES INTERIEURS, ALIGNE SUR CELUI DE L'ACCUEIL.
   Il ne descendait jamais sous .34 a droite, la ou le heros de l'accueil va
   jusqu'a .06 : les vingt-deux pages interieures montraient donc deux tiers de
   leur photographie quand l'accueil en montre la quasi-totalite. C'est ce
   decalage qu'on lisait comme « les heros sont trop sombres ».

   MESURE SUR L'EN-TETE DES REALISATIONS, et non a l'oeil. Derriere le titre,
   le fond rendu vaut 0,0026 en luminance - la photographie nue, elle, vaut
   0,026 - ce qui donne au titre blanc 18,4:1. Un grand titre blanc gras en
   demande 3. Le voile etait six fois plus fort que necessaire.

   IL RESTE FORT LA OU LE TEXTE SE POSE, et c'est la seule contrainte : a .84
   sur le bord gauche, meme une photographie tres claire - luminance 0,7, un
   ciel, une baie vitree - laisserait au titre 6,5:1. Ailleurs il s'efface, et
   la photographie se voit. */
.entete-voile {
  position: absolute; inset: 0;
  background:
    linear-gradient(90deg, rgba(0,0,0,.84) 0%, rgba(0,0,0,.72) 28%,
                           rgba(0,0,0,.40) 56%, rgba(0,0,0,.16) 80%,
                           rgba(0,0,0,.08) 100%),
    linear-gradient(180deg, rgba(0,0,0,.24) 0%, rgba(0,0,0,0) 34%,
                            rgba(0,0,0,0) 66%, rgba(0,0,0,.36) 100%);
}
.entete-in {
  position: relative; z-index: 3;
  padding: clamp(46px, 5.4vw, 78px) var(--gout);
  min-height: clamp(230px, 22vw, 320px);
  display: flex; flex-direction: column; justify-content: center;
}
.entete-etiq {
  font-size: var(--etiq-taille); font-weight: 600; letter-spacing: .18em;
  text-transform: uppercase; color: var(--orange); margin-bottom: 1.6em;
}
.entete h1 { --titre-taille: clamp(30px, 3.6vw, 52px); }
.entete-chapo {
  margin-top: 1.5em; max-width: 52ch;
  font-size: clamp(12.5px, .95vw, 13.5px); line-height: 1.75; color: var(--blanc-80);
}

/* =============================================================================
   22. ACCES
   -----------------------------------------------------------------------------
   Meme partage que la section 3D : texte a gauche, image a droite, l'image
   allant jusqu'au bord. La colonne de texte garde le retrait commun.
   ========================================================================== */
.acces {
  display: grid; grid-template-columns: minmax(0, 38%) minmax(0, 1fr);
  background: var(--noir); align-items: center;
}
.acces-texte { padding-inline: var(--gout); min-width: 0; }
.acces-etiq {
  font-size: var(--etiq-taille); font-weight: 600; letter-spacing: .18em;
  text-transform: uppercase; color: var(--orange); margin-bottom: 1.3em;
}
.acces-titre {
  font-size: clamp(22px, 2.2vw, 32px); font-weight: 900; line-height: 1.14;
  letter-spacing: -.005em; text-transform: uppercase; color: var(--blanc);
}
.acces-chapo {
  margin-top: 1.5em; font-size: 12.5px; line-height: 1.75; color: var(--blanc-80);
  max-width: 44ch;
}
.acces-adresse {
  margin-top: 1.8em; padding-top: 1.4em; border-top: 1px solid var(--filet);
  font-style: normal; font-size: 13px; line-height: 1.7; color: var(--blanc-80);
}
.acces-adresse b { color: var(--blanc); font-weight: 800; letter-spacing: .04em; }
.acces-plan { position: relative; min-width: 0; aspect-ratio: 2 / 1; }
.acces-plan img { width: 100%; height: 100%; object-fit: cover; display: block; }
.acces-plan .reserve { width: 100%; height: 100%; }

/* LA SECONDE IMPLANTATION, EN MIROIR : carte a gauche, texte a droite. Deux
   sections alignees du meme cote se liraient comme une repetition ; inversees,
   elles se lisent comme un diptyque.

   LA REGLE DE LA RESPIRATION TIENT TOUTE SEULE. .acces-plan n'a pas de
   remplissage lateral : la carte touche donc le bord de la page du cote ou elle
   se trouve - a droite dans la section France, a gauche ici - sans qu'on ait
   rien a declarer. Seul l'ordre des colonnes change. */
.acces--inverse { grid-template-columns: minmax(0, 1fr) minmax(0, 38%); }
.acces--inverse .acces-plan  { order: 1; }
.acces--inverse .acces-texte { order: 2; }

@media (max-width: 999px) {
  .acces { grid-template-columns: 1fr; row-gap: var(--air); }
  .acces-plan { aspect-ratio: 16 / 9; }
  /* EMPILEES, LES DEUX SECTIONS SE LISENT DANS LE MEME ORDRE : le texte, puis
     la carte. C'est le texte qui dit de quel pays il s'agit - une carte seule
     en tete de section ne l'apprend a personne. L'ordre est donc remis a
     l'endroit, et « order » ne sert qu'en deux colonnes. */
  .acces--inverse { grid-template-columns: 1fr; }
  .acces--inverse .acces-plan  { order: 2; }
  .acces--inverse .acces-texte { order: 1; }
}

/* Sous 860 px, l'angle superieur droit de l'en-tete n'est plus un angle : il
   est juste au-dessus du titre. La reserve s'y reduit au nom du fichier et a
   ses cotes, sur une seule ligne, et le contenu prend l'air qu'il faut pour
   ne pas la toucher. */
@media (max-width: 859px) {
  .entete-media .reserve--fond .reserve-sujet { display: none; }
  .entete-media .reserve--fond .reserve-in {
    display: flex; flex-wrap: wrap; justify-content: flex-end;
    align-items: baseline; gap: 3px 12px; max-width: none;
  }
  .entete-in { padding-top: clamp(72px, 17vw, 104px); }
}

/* Un numero sans son intitule ne dit pas lequel des trois il est : l'etiquette
   passe au-dessus, en petit, et la ligne reste cliquable d'un bloc. */
.pied-liste .pied-cle {
  display: block; font-size: 10px; font-weight: 600; letter-spacing: .2em;
  text-transform: uppercase; color: var(--blanc-55); margin-bottom: 2px;
}
.pied-liste a:hover .pied-cle { color: inherit; }

/* Deux adresses empilees dans la meme fiche : chacune porte son pays, sinon on
   les devine. Le premier n'a pas de marge haute - il colle a son intitule. */
.contact-pays {
  display: block; margin-top: 1.1em; font-size: 10px; font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase; color: var(--blanc);
}
.contact-pays:first-child { margin-top: 0; }

/* -----------------------------------------------------------------------------
   La carte
   -----------------------------------------------------------------------------
   Un fond de carte est clair par nature, et la page est noire. On l'inverse
   donc, puis on fait pivoter la teinte d'un demi-tour : l'inversion seule
   virerait les verts en roses. Le resultat est une carte sombre dont les
   libelles restent lisibles - ils passent de noir sur blanc a blanc sur
   sombre, ce qui est exactement ce qu'on veut ici.
   -------------------------------------------------------------------------- */
.plan-carte {
  position: relative; z-index: 1;
  width: 100%; height: 100%; border: 0; display: block;
  /* Inversion puis desaturation, dans cet ordre : l'inversion seule virerait
     les verts des parcs en roses. Verifie sur les tuiles reelles - les rues
     et les libelles restent lisibles, en blanc sur sombre. */
  filter: invert(.92) grayscale(.6) brightness(.95) contrast(1.06);
}
/* Qui demande plus de contraste recoit la carte telle qu'elle est servie :
   noir sur blanc se lit mieux que n'importe quel traitement. */
@media (prefers-contrast: more) { .plan-carte { filter: none; } }

/* Si la carte ne charge pas - reseau coupe, bloqueur, cadre refuse - la zone
   ne doit pas devenir un trou noir : la rayure de reserve reste dessous, et
   le bouton d'itineraire, lui, ne depend de rien. */
.acces-plan {
  background-color: #221F1D;
  background-image: repeating-linear-gradient(
      -45deg, transparent 0 9px, rgba(255,255,255,.035) 9px 18px);
}

.plan-lien { position: absolute; left: 20px; bottom: 20px; z-index: 2; }
.plan-source {
  position: absolute; right: 12px; top: 12px; z-index: 2;
  font-size: 10px; letter-spacing: .04em; color: var(--blanc-55);
  background: rgba(11,11,11,.78); padding: 5px 9px; border-radius: 2px;
}

/* Une adresse de courriel ne contient aucun point de coupure naturel : sur
   une colonne etroite, elle deborde et pousse la page de deux pixels. On
   l'autorise a se couper n'importe ou - c'est laid sur une syllabe, mais
   invisible sur une adresse, qui se lit d'un bloc. */
.pied-liste a, .pied-liste address { overflow-wrap: anywhere; }

/* =============================================================================
   23. MENUS DEROULANTS
   -----------------------------------------------------------------------------
   Batis sur <details> : le navigateur fournit l'ouverture, la fermeture, le
   clavier et l'etat annonce aux lecteurs d'ecran. Le script ne fait qu'ajouter
   le confort. Une consequence qui vaut d'etre dite : le menu fonctionne
   maintenant sans JavaScript, y compris sur telephone, ce qui n'etait pas le
   cas de la barre precedente.

   Le marqueur natif du <summary> est retire des deux manieres possibles -
   ::-webkit-details-marker pour les navigateurs WebKit, list-style pour les
   autres - faute de quoi un triangle vient doubler le chevron.
   -------------------------------------------------------------------------- */
.deroulant { position: relative; }
.deroulant > summary {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  font-size: var(--nav-taille); font-weight: 600; letter-spacing: .14em;
  text-transform: uppercase; color: var(--blanc-55);
  list-style: none; transition: color .18s;
}
.deroulant > summary::-webkit-details-marker { display: none; }
.deroulant > summary::marker { content: ""; }
.deroulant > summary:hover,
.deroulant[open] > summary,
.deroulant > summary[aria-current="page"] { color: var(--blanc); }
.deroulant[open] > summary { color: var(--orange); }
.chevron { width: 9px; height: 6px; flex: none; transition: transform .18s; }
.deroulant[open] .chevron { transform: rotate(180deg); }

.deroulant-liste {
  position: absolute; top: calc(100% + 18px); left: -22px; z-index: 120;
  min-width: 246px; padding: 10px 0;
  background: #131110; border: 1px solid var(--filet);
  box-shadow: 0 18px 44px rgba(0,0,0,.55);
  display: flex; flex-direction: column;
}
/* Le pont invisible entre l'entree et sa liste : sans lui, la souris traverse
   dix-huit pixels de vide et un survol malheureux referme le menu. */
.deroulant-liste::before {
  content: ""; position: absolute; left: 0; right: 0; top: -18px; height: 18px;
}
.deroulant-liste a {
  padding: 11px 24px; font-size: 11.5px; font-weight: 600; letter-spacing: .1em;
  text-transform: uppercase; color: var(--blanc-80); white-space: nowrap;
  transition: color .18s, background .18s;
}
.deroulant-liste a:hover { color: var(--orange); background: rgba(255,255,255,.04); }
/* La premiere ligne mene a la page entiere, les suivantes a ses sections :
   un filet dit que ce n'est pas la meme chose. */
.deroulant-tout {
  color: var(--blanc) !important;
  border-bottom: 1px solid var(--filet); margin-bottom: 6px; padding-bottom: 14px !important;
}

/* Sous 1100 px le menu est un panneau : les deroulants deviennent des
   accordeons, empiles dans le flux, et la liste n'est plus flottante. */
@media (max-width: 1099px) {
  .deroulant { border-bottom: 1px solid rgba(255,255,255,.08); }
  .deroulant > summary {
    justify-content: space-between; width: 100%;
    padding: 16px 0; font-size: 15px; letter-spacing: .12em;
  }
  .chevron { width: 12px; height: 8px; }
  .deroulant-liste {
    position: static; min-width: 0; border: 0; box-shadow: none; padding: 0 0 14px;
    background: none;
  }
  .deroulant-liste::before { display: none; }
  .deroulant-liste a {
    padding: 10px 0 10px 18px; font-size: 12.5px; border-bottom: 0;
    border-left: 1px solid var(--filet); margin-left: 2px;
  }
  .deroulant-tout { margin-bottom: 0; padding-bottom: 10px !important; }
}

/* =============================================================================
   24. PAGES SOMMAIRES (expertises, services)
   -----------------------------------------------------------------------------
   Une grille de cartes, une par entree du menu. Chaque carte porte l'ancre que
   vise le deroulant : arriver par le menu, c'est arriver sur la bonne carte,
   pas en haut d'une page ou il faut ensuite chercher.
   ========================================================================== */
/* LA GRILLE DE CARTES A DISPARU. Les trois sommaires - univers, services,
   marques - partagaient une grille de cartes de texte numerotees ; ils
   partagent desormais la mise en page des pages de fond (hub_rendre), qui
   reutilise « .uni-* ». Plus aucune page n'emet « class="hub" », verifie
   sur les six pages servies avant de retirer ces regles. */

/* Une page sans image : la bande d'en-tete se pose sur le noir de la marque,
   avec un filet en bas pour qu'elle ne se confonde pas avec le pied de page
   quand la page ne porte encore rien d'autre. */
.entete--nue { background: var(--noir); border-bottom: 1px solid var(--filet); }
.entete--nue .entete-in { min-height: clamp(200px, 19vw, 280px); }
.entete-suite { margin-top: 2em; }

/* =============================================================================
   25. CORRECTIONS TELEPHONE
   -----------------------------------------------------------------------------
   Releve du 10 septembre 2026 sur 23 pages et quatre largeurs de telephone.
   Ce qui suit corrige ce qui ne tenait qu'a l'ecran large.

   Le declencheur n'est pas toujours la largeur : plusieurs de ces defauts
   tiennent au doigt, pas a la place. On interroge donc le pointeur -
   « pointer: coarse » designe un ecran tactile, quelle que soit sa taille,
   ce qui couvre aussi la tablette, ou la largeur seule ne dirait rien.
   -------------------------------------------------------------------------- */

/* --- 1. L'appel final : la reserve tombait sur le titre --------------------
   La section ne fait que 156 px sur telephone, et l'information de la reserve
   s'y superposait au titre. Tant que l'image manque, la section s'ouvre pour
   loger les deux ; le jour ou la photographie arrive, la reserve disparait et
   la section reprend sa hauteur. C'est :has() qui fait ce raccord, sous garde
   - un navigateur qui l'ignore garde le comportement actuel. */
@supports selector(:has(*)) {
  @media (max-width: 859px) {
    .final:has(.reserve) { padding-top: calc(var(--air) + 58px); }
    .final .reserve--fond .reserve-sujet { display: none; }
    .final .reserve--fond { align-items: flex-start; }
  }
}

/* --- 2. Les champs de saisie : iOS zoomait a chaque prise de focus ---------
   En dessous de 16 px, Safari agrandit la page quand un champ recoit le
   focus, et ne la remet pas : on remplit la suite du formulaire dans une page
   decalee. Seize pixels sur ecran tactile, treize a la souris. */
@media (pointer: coarse) {
  .champ input, .champ select, .champ textarea { font-size: 16px; }
  /* 24 px : le minimum de la norme. La case n'a pas besoin de plus, son
     libelle etant lui-meme cliquable sur toute sa largeur. */
  .form-accord input { width: 24px; height: 24px; }
}

/* --- 3. Les cartes : le texte long n'existait que pour la souris -----------
   Le survol devoilait une description complete. Un ecran tactile n'a pas de
   survol : ce texte etait invisible sur telephone, sur les quatre cartes. Il
   prend donc la place du texte court des qu'on est au doigt, et la couche de
   survol - devenue sans objet - ne se declenche plus. */
.carte-desc--long { display: none; }
@media (pointer: coarse) {
  .carte-desc--court { display: none; }
  .carte-desc--long  { display: block; }
  .carte-actif { display: none; }
  /* Le retrait deux lignes ne sert qu'a aligner quatre titres cote a cote ;
     empilees, les cartes n'ont plus rien a aligner. */
  .carte-desc { min-height: 0; }
}

/* --- 4. Le duo : l'ordre de lecture s'inversait ---------------------------
   Le panneau de droite est dessous dans le document - c'est ce qui permet a
   la diagonale de decouper celui du dessus. Empiles, ils se lisaient donc a
   l'envers : Carmin avant Reformer. On remet l'ordre du regard. */
@media (max-width: 699px) {
  .duo-dessus { order: 1; }
  .duo-trait  { order: 2; }
  .duo-fond   { order: 3; }
}

/* --- 6 et 7. Cibles et corps de texte au doigt -----------------------------
   Deux defauts qui vont ensemble : ce qui est trop petit a lire l'est aussi a
   viser. Les valeurs de l'ecran large - 10 px pour une etiquette, 18 px de
   haut pour un lien de pied - ont ete relevees sur la maquette, a la souris
   et a 60 cm. Au doigt et a 30 cm, elles ne tiennent plus.

   Le plancher retenu : 12 px de corps, 44 px de cible. Les etiquettes gardent
   leur approche large, qui fait leur caractere ; elles grossissent seulement. */
@media (pointer: coarse) {
  /* Corps minimum */
  .entete-etiq, .conception-etiq, .inno-etiq, .real-etiq, .cle-etiq, .acces-etiq,
  .contact-etiq, .chiffre-lib, .pied-titre, .sommaire-titre,
  .univers-sous, .reserve-etat, .reserve-nom { font-size: 12px; }
  .contact-cle, .champ label, .attente, .plan-source, .pied-cle,
  .reserve-cote, .fiche dt, .page-revision { font-size: 12px; }
  .champ-aide, .form-requis, .form-accord label, .pied-bas p, .pied-bas a,
  .pied-liste a, .pied-liste span, .pied-liste address, .sommaire a,
  .btn, .carte-desc { font-size: 12.5px; }
  .pied-base, .prose p, .prose li, .fiche dd { font-size: 13.5px; }

  /* Cibles : 44 px de haut, obtenus par la marge interieure quand le texte
     ne les fait pas a lui seul. La zone grandit, le dessin ne bouge pas. */
  .pied-liste a, .pied-legal a, .pied-bas a, .sommaire a {
    display: flex; align-items: center; min-height: 44px;
  }
  .pied-liste { gap: 0; }
  .btn { height: 46px; }
  .real-fleche { width: 44px; height: 44px; }
  .haut { width: 52px; height: 52px; }
  .langues a { min-width: 48px; min-height: 48px; }

  /* Le carrousel se pousse au doigt : les fleches deviennent secondaires,
     mais elles doivent rester atteignables quand on s'en sert. */
  .real-fleches { gap: 10px; }
}

/* --- 10. Details releves sur telephone ------------------------------------ */
@media (max-width: 859px) {
  /* Le voile du heros est calibre pour un ecran large, ou la photographie
     s'etale ; recadree sur un telephone, elle n'en garde que la part la plus
     sombre et disparaissait sous le voile. On l'allege. */
  .heros-voile {
    background:
      linear-gradient(180deg, rgba(0,0,0,.72) 0%, rgba(0,0,0,.52) 38%,
                              rgba(0,0,0,.6) 72%, rgba(0,0,0,.86) 100%);
  }
  /* Une carte de 219 px ne permet pas de se reperer. */
  .acces-plan { aspect-ratio: auto; height: clamp(300px, 62vw, 380px); }
}

/* En paysage sur telephone, la hauteur est la denree rare : le heros doit
   laisser voir ses boutons sans faire defiler. */
@media (orientation: landscape) and (max-height: 500px) {
  .heros-in { padding-block: 24px 26px; }
  .heros-etapes { display: none; }
  .heros-chapo { margin-top: 12px; }
  .heros-btns { margin-top: 18px; }
}

/* Second passage : les regles plus specifiques l'emportaient sur le bloc
   ci-dessus. Elles sont reprises ici, a leur propre niveau de specificite -
   corriger une specificite par une autre est plus honnete que de semer des
   !important, et cela reste lisible. */
@media (pointer: coarse) {
  .heros-etapes, .heros-btns .btn, .conception-chapo, .inno-chapo,
  .cle-etape p, .real-legende span, .duo-legende p,
  .pied-liste .pied-cle, .contact-chapo, .champ-aide { font-size: 12px; }
  .duo-legende p, .cle-etape p { line-height: 1.55; }

  /* Cibles restantes. Le logotype et les titres de cartes gagnent leur
     hauteur par la marge interieure ; les coordonnees, par un affichage en
     bloc qui leur donne la ligne entiere. */
  .logo { padding-block: 7px; }
  .contact-val a { display: inline-flex; align-items: center; min-height: 44px; }
  .form-accord label a { display: inline-block; padding-block: 6px; }
  .compare-curseur, .piege input { font-size: 16px; }
}

/* Le lien vers la politique de confidentialite est inline dans une phrase :
   la norme exempte ce cas de la taille minimale, et lui donner 44 px de haut
   ecarterait les lignes du paragraphe. Il garde une marge de confort. */

/* --- 9. Le rail de progression du carrousel --------------------------------
   Visible seulement au doigt : a la souris, les deux fleches suffisent et
   disent deja ou l'on en est en s'eteignant en bout de course. */
.real-jauge { display: none; }
@media (pointer: coarse) {
  .real-jauge {
    display: block; height: 2px; margin: 18px var(--gout) 0;
    background: var(--filet); border-radius: 2px; overflow: hidden;
  }
  .real-jauge span {
    display: block; height: 100%; width: 30%;
    background: var(--orange); border-radius: 2px;
    transform: translateX(0); transition: transform .12s linear;
  }
}

/* =============================================================================
   26. PAGES DE PRESTATION
   -----------------------------------------------------------------------------
   Trois blocs apres l'en-tete : un corps de lecture, les six univers en
   blocs de texte et d'image, un appel a l'action. Tout reprend des composants existants - .entete, .prose,
   .btn - et n'ajoute que ce qui manquait : une colonne de lecture sans
   sommaire, une liste de definitions, une rangee de vues.
   ========================================================================== */

/* Le corps d'une page de prestation : le titre a gauche, le texte a droite.
   Une colonne de lecture seule, calee a gauche, laissait la moitie d'un ecran
   large vide a cote d'elle - c'est ce qu'on voit tout de suite sur une
   capture en 1900 px. Le titre occupe donc la colonne libre, comme le font
   deja la section 3D, les realisations et l'acces. */
.corps-service {
  padding-inline: var(--gout);
  display: grid; grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
  gap: clamp(30px, 4vw, 70px); align-items: start;
}
.corps-titre {
  font-size: clamp(15px, 1.35vw, 19px); font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--blanc); scroll-margin-top: 24px;
  padding-bottom: .8em; border-bottom: 1px solid var(--filet);
}
/* 120ch ET NON 74. Le « ch » se mesure sur la police du CONTENEUR (~18 px),
   pas sur celle des paragraphes (13 px) : 74ch ne faisaient donc pas 74
   caracteres mais 749 px, et la colonne s'arretait au tiers d'un grand ecran.
   Le vide a droite n'etait pas une page trop large, c'etait une colonne trop
   etroite - diagnostic du client, verifie au pixel. */
.corps-texte { max-width: 120ch; }
.corps-texte p { font-size: 13px; line-height: 1.85; color: var(--blanc-80); }
.corps-texte p + p { margin-top: 1.1em; }

/* -----------------------------------------------------------------------------
   PAGES D'UNIVERS - LA GRAMMAIRE DE L'ACCUEIL
   -----------------------------------------------------------------------------
   Les pages d'univers portaient des titres de section de 15 px, ranges dans une
   colonne de 300 px. Correct, lisible - et d'une autre famille que la page
   d'accueil, qui ouvre chacune de ses sections par un sur-titre orange et un
   grand titre en display. C'est ce seul ecart qui les faisait paraitre plus
   pauvres qu'elles ne sont : le contenu etait la, la composition ne le portait
   pas.

   ON REPREND LA GRAMMAIRE, PAS LES REGLES : memes graisses, meme approche, meme
   orange, mais des classes propres a ces pages. Reutiliser .conception-titre
   ici lierait la mise en page de douze pages a celle d'une section de l'accueil,
   et la premiere retouche de l'une casserait l'autre.
   -------------------------------------------------------------------------- */
.uni-tete { padding-inline: var(--gout); }
.uni-tete--nu { padding-inline: 0; }   /* deja dans un conteneur a gouttieres */
.uni-tete-etiq {
  font-size: var(--etiq-taille); font-weight: 600; letter-spacing: .18em;
  text-transform: uppercase; color: var(--orange); margin-bottom: 1.3em;
}
.uni-tete-titre {
  font-size: clamp(26px, 2.9vw, 42px); font-weight: 900; line-height: 1.12;
  letter-spacing: -.005em; text-transform: uppercase; color: var(--blanc);
  max-width: 22ch; scroll-margin-top: 24px;
}

/* LE CORPS SOUS SON TITRE. Le titre ayant quitte la colonne de gauche, le texte
   commence a la gouttiere de la page - donc a l'aplomb du logotype, du titre de
   section et du pied. Trois pistes : lecture | GOUTTIERE | sommaire. La
   gouttiere en « 1fr » absorbe le surplus et cale le sommaire sur le bord droit.
   Sans elle, le sommaire collait au texte et laissait 380 px de noir a SA
   droite : le trou changeait de place, rien de plus. */
.uni-corps {
  padding-inline: var(--gout);
  margin-top: clamp(26px, 2.6vw, 44px);
  display: grid; align-items: start;
  gap: clamp(30px, 4vw, 70px);
  /* La piste de lecture suit la meme mesure que .corps-texte, sans quoi l'une
     brimerait l'autre : la colonne s'arreterait a 74ch quoi qu'en dise le
     texte, ou le texte flotterait dans une piste plus large que lui. */
  grid-template-columns: minmax(0, 120ch) minmax(0, 1fr) minmax(240px, 280px);
}
.somm {
  grid-column: 3;
  /* Sous la barre collee, pas dessous elle : --colle-haut vaut zero quand
     l'en-tete ne colle pas, donc la valeur d'origine est conservee. */
  position: sticky; top: calc(var(--colle-haut) + 24px);
  border-left: 1px solid var(--filet); padding-left: clamp(18px, 1.6vw, 28px);
}
.somm-titre {
  font-size: 10px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
  color: var(--blanc-55); padding-bottom: 1em; margin-bottom: .2em;
}
.somm-liste, .somm-sous { list-style: none; margin: 0; padding: 0; }
.somm-liste > li + li { margin-top: .85em; }
.somm-liste a {
  display: block; font-size: 12.5px; line-height: 1.5; font-weight: 700;
  color: var(--blanc-80); text-decoration: none; transition: color .15s ease;
}
.somm-liste a:hover, .somm-liste a:focus-visible { color: var(--orange); }
/* Les sports sont un cran en dessous : plus petits, plus clairs, decales par un
   filet plutot que par une puce - une puce ferait une liste a lire, alors que
   c'est une liste ou l'on pioche. */
.somm-sous { margin-top: .6em; border-left: 1px solid var(--filet); }
.somm-sous li + li { margin-top: .5em; }
.somm-sous a { font-size: 11.5px; font-weight: 600; color: var(--blanc-55); padding-left: 14px; }

/* SOUS 1400 px LE SOMMAIRE S'EFFACE, et le calcul le commande : il lui faut
   240 px, plus deux gouttieres de 70, ce qui ne laisse plus assez pour une
   colonne de lecture honnete. On ne l'empile pas au-dessus du texte : cela
   repousserait l'article d'un ecran entier, et rien n'y serait perdu puisqu'il
   ne mene qu'a des sections que l'on atteint en faisant defiler. */
@media (max-width: 1399px) {
  .uni-corps { grid-template-columns: minmax(0, 1fr); }
  .somm { display: none; }
}

/* LA FOIRE AUX QUESTIONS PREND LA LARGEUR. Sous un titre pleine largeur, une
   liste calee a 74 caracteres laissait le meme vide que le corps. Une ligne de
   question va donc d'un bord a l'autre, chevron a droite - c'est le dessin des
   accordeons de la maquette - et seule la REPONSE garde la mesure de lecture,
   parce qu'elle, on la lit. */
.uni-faq-in { padding-inline: var(--gout); margin-top: clamp(22px, 2.4vw, 38px); }
/* DOUBLE CLASSE, ET C'EST NECESSAIRE : « .faq { max-width: 74ch } » est declare
   plus bas dans la feuille et l'emportait a specificite egale. Le premier essai
   ne changeait donc rien, et rien ne le signalait - la liste gardait sagement
   ses 74 caracteres sous un titre pleine largeur. */
.faq.faq--large { max-width: none; }
.faq.faq--large > .faq-item > summary { font-size: clamp(13.5px, 1.15vw, 15.5px); }
.faq.faq--large .faq-rep { max-width: 74ch; }

/* LE PARAGRAPHE D'ATTAQUE. Les huit alineas du corps etaient typographiquement
   identiques : un mur, sans point d'entree. Le premier passe d'un cran - c'est
   du CSS seul, aucun texte a reecrire. */
.corps-texte > p:first-child { font-size: 15px; line-height: 1.7; color: var(--blanc); }

@media (max-width: 899px) {
  .corps-service { grid-template-columns: 1fr; gap: 22px; }
}
@media (pointer: coarse) { .corps-texte p { font-size: 13.5px; } }

/* LA GRILLE DES UNIVERS A DISPARU DES PAGES DE SERVICE. Les sept pages
   portaient la seule mise en page du site a ne pas suivre la regle commune :
   une grille de cartes la ou les univers et les marques posent des blocs
   alternes. Elles passent desormais par la meme grammaire (« .uni-* »), et
   plus aucune page n'emet « univers-grille » - verifie sur huit pages servies
   avant de retirer ces regles. */

/* L'apparition d'une sequence.
   Une suite de blocs semblables - les six univers - gagne a se poser l'un
   apres l'autre plutot qu'a tomber d'un seul tenant. Le principe : le
   document livre les cartes visibles, et c'est le script qui pose
   « surgit-pret » sur celles qu'il surveille, juste avant de les surveiller.
   Rien n'est donc cache par la feuille de style seule : sans script, sans
   IntersectionObserver, ou quand le systeme demande un mouvement reduit, la
   marque n'est jamais posee et les cartes sont simplement la.

   Le retard entre deux cartes est pose par le script sur transition-delay,
   dans l'ordre de lecture de la rangee qui entre. La transition ne porte que
   sur l'etat d'arrivee : c'est l'ajout de la classe qui la declenche. */
.surgit-pret { opacity: 0; transform: translateY(18px); }
.surgit-pret.surgi {
  opacity: 1; transform: none;
  transition: opacity .5s ease, transform .55s cubic-bezier(.22, .61, .36, 1);
}
@media (prefers-reduced-motion: reduce) {
  .surgit-pret { opacity: 1; transform: none; }
}
/* Sur papier, il n'y a pas de defilement : une carte que le lecteur n'a pas
   fait entrer dans la vue avant d'imprimer sortirait blanche. */
@media print {
  .surgit-pret { opacity: 1 !important; transform: none !important; }
}

/* Le passage au contact. Une bande sobre : la page a dit ce qu'elle avait a
   dire, il ne reste qu'a ouvrir la porte. */
.suite { background: var(--noir); }
.suite-in {
  padding-inline: var(--gout); max-width: 62ch;
}
.suite-titre {
  font-size: clamp(20px, 2vw, 28px); font-weight: 900; line-height: 1.16;
  letter-spacing: -.005em; text-transform: uppercase; color: var(--blanc);
}
.suite-texte {
  margin-top: 1.2em; font-size: 13px; line-height: 1.8; color: var(--blanc-80);
}
.suite .btn { margin-top: 2em; }

/* L'APPEL FINAL DES PAGES D'UNIVERS, compose comme celui de l'accueil : centre,
   en grand, sur toute la largeur. Meme texte, memes boutons - la page ne se
   termine plus sur un murmure dans une colonne de 62 caracteres. */
.suite--final { padding-block: clamp(12px, 2vw, 34px); }
/* LA LARGEUR EST EN PIXELS, PAS EN « ch » : un « ch » se mesure sur la police du
   CONTENEUR (~18 px), et 46 d'entre eux ne font que 460 px - un titre de 44 px y
   tombait sur quatre lignes et les deux boutons s'empilaient. */
/* 940 px ne suffisaient pas : les deux boutons mesurent 870 px a eux deux et
   passaient a la ligne. Mesure sur le rendu, pas estimee. */
.suite--final .suite-in { max-width: 1100px; margin-inline: auto; text-align: center; }
.suite--final .suite-titre { font-size: clamp(26px, 3.1vw, 44px); line-height: 1.14; }
.suite--final .suite-texte {
  margin-top: 1.4em; font-size: 14px; max-width: 58ch; margin-inline: auto;
}
.suite--final .suite-btns { justify-content: center; }

@media (pointer: coarse) {
  .suite-texte { font-size: 13.5px; }
}

/* =============================================================================
   27. PAGES D'UNIVERS
   -----------------------------------------------------------------------------
   Cinq pages de fond, une par famille de pratiques. Elles reprennent
   .entete, .corps-service et .suite des pages de prestation, et ajoutent
   deux choses : des blocs alternes pour les sports, et une foire aux
   questions.
   ========================================================================== */

.uni-blocs { background: var(--noir); }
.uni-blocs-in { padding-inline: var(--gout); }

/* Un sport, un bloc : l'image d'un cote, trois a cinq paragraphes de l'autre.
   Les pages de prestation avaient renonce a cette disposition - trente mots
   ne remplissaient pas leur demi-page et laissaient un vide au milieu. Le
   reproche ne tient plus ici : le texte fait quatre cents mots, la colonne se
   remplit, et c'est la vignette de 350 px qui deviendrait illisible. */
.uni-bloc {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  /* ALIGNES PAR LE HAUT, et c'est ce qui rend la barre possible. Centres, le
     texte et l'image ne partageaient aucune ligne : la barre serait tombee
     quelque part au milieu du cliche, ce qui n'aurait rien voulu dire. */
  gap: clamp(26px, 3.2vw, 56px); align-items: start;
  margin-top: clamp(36px, 4vw, 68px);
  padding-top: clamp(30px, 3.4vw, 54px); border-top: 1px solid var(--filet);
}
/* Un bloc sur deux renvoie l'image a droite. Sur une suite de huit
   disciplines, l'alternance donne le rythme qui manque a une colonne. */
.uni-bloc:nth-child(even) .uni-bloc-vue { order: 2; }

.uni-bloc-vue img, .uni-bloc-vue .reserve {
  width: 100%; aspect-ratio: var(--carte-ratio);
}
.uni-bloc-vue img { object-fit: cover; display: block; }

.uni-bloc-txt { min-width: 0; }
.uni-bloc-txt h3 {
  margin: 0 0 .9em; font-size: 13px; font-weight: 800; letter-spacing: .14em;
  text-transform: uppercase; color: var(--orange);
}
.uni-bloc-txt p { margin: 0; font-size: 13px; line-height: 1.85; color: var(--blanc-80); }
.uni-bloc-txt p + p { margin-top: 1.05em; }

/* Un lien au fil du texte doit se voir sans casser la ligne de lecture :
   il prend la couleur d'accent et un filet discret, qui s'affirme au
   survol. Pas de soulignement plein, qui hacherait un paragraphe long. */
/* NUMEROTER LES QUATRE PRATIQUES. Un compteur CSS, donc aucun chiffre ecrit
   nulle part : un sport ajoute se numerote seul, un sport retire ne laisse pas
   de trou. Sur une suite de blocs qui se ressemblent, le numero dit ou l'on en
   est - ce que l'image fera aussi, le jour ou elle sera la, mais autrement. */
.uni-blocs-in { counter-reset: pratique; }
.uni-bloc { counter-increment: pratique; }
/* LA BARRE ORANGE, A L'APLOMB DU HAUT DE L'IMAGE.
   Le numero et le nom du sport tiennent sur une seule ligne, en NOIR sur
   l'accent. Le noir et non le blanc, et ce n'est pas une preference : sur
   #E8621F le noir donne 6,2:1 quand le blanc n'en donne que 3,4 - presque le
   double, pour la meme couleur de fond.

   ELLE REMPLACE LE GRAND CHIFFRE EN CONTOUR, elle ne s'y ajoute pas. Les deux
   ensemble auraient fait deux reperes pour une seule discipline, et la page de
   combat en compte huit : le numero entre donc DANS la barre, a demi-ton, ou il
   se lit sans voler la vedette au nom.

   Il faut annuler explicitement le trait de contour : la regle @supports qui le
   posait vivait ici meme, et un chiffre transparent sur fond orange aurait
   simplement disparu. */
/* LE TITRE D'UNE DISCIPLINE : un numero orange, puis une barre orange a texte
   noir. Deux objets sur une meme ligne, calee sur le haut de l'image.

   POURQUOI LE NUMERO EST DEHORS. Il devait etre « en gras orange » : sur
   l'aplat orange cela vaut 1,00:1 - la meme couleur, donc rien. Sur le fond
   noir de la page il vaut 6,20:1. Il sort donc de la barre et se pose devant
   elle, ce qui a l'avantage de la laisser a l'aplomb du haut de l'image, ce
   qu'un numero pose AU-DESSUS aurait perdu. */
.uni-bloc-txt h3 {
  display: flex; align-items: center; gap: .6em;
  margin: 0 0 1.15em;
  font-size: clamp(16px, 1.4vw, 21px); font-weight: 900; line-height: 1.2;
  letter-spacing: .02em; text-transform: uppercase;
}
.uni-bloc-txt h3::before {
  content: counter(pratique, decimal-leading-zero);
  flex: none; color: var(--orange); font-weight: 900;
  /* Le trait de contour venait de la version precedente du numero et rendrait
     un chiffre creux : on l'annule ici, sinon la regle @supports d'en dessous
     le reprendrait. */
  -webkit-text-stroke: 0;
}

/* L'APLAT EST UN CALQUE, PAS UN FOND - c'est ce qui le rend animable. Un
   « background » ne se balaie pas ; un pseudo-element, si. Il se deploie de la
   gauche vers la droite quand le bloc entre dans la vue, comme un trait de
   surligneur, et le nom se decouvre avec lui.

   « isolation: isolate » est indispensable : sans elle, le calque en z-index
   negatif passerait derriere le FOND DE LA PAGE et non derriere le seul texte
   - il disparaitrait purement et simplement. */
.uni-bloc-txt h3 .nom {
  position: relative; isolation: isolate;
  padding: .5em clamp(12px, 1.1vw, 18px);
  color: var(--noir);
}
.uni-bloc-txt h3 .nom::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--orange);
  transform: scaleX(1); transform-origin: left center;
  transition: transform .55s cubic-bezier(.22, .61, .36, 1);
}
/* LE REPOS EST L'ETAT VISIBLE, et c'est la regle de la maison : le document
   livre la barre pleine, et c'est le script qui la retracte en posant
   « surgit-pret » - donc seulement s'il est en mesure de la redeployer. Sans
   JavaScript, sans IntersectionObserver ou en mouvement reduit, la barre est
   simplement la. */
.surgit-pret .uni-bloc-txt h3 .nom::after { transform: scaleX(0); }
.surgit-pret.surgi .uni-bloc-txt h3 .nom::after { transform: scaleX(1); }
/* Le nom attend que l'aplat soit passe sous lui : sur huit disciplines, du noir
   sur du noir pendant un quart de seconde se remarquerait. Le numero, lui, est
   orange sur le noir de la page : il n'a rien a attendre. */
.surgit-pret .uni-bloc-txt h3 .nom { color: transparent; }
.surgit-pret.surgi .uni-bloc-txt h3 .nom {
  color: var(--noir); transition: color .2s ease .3s;
}
@media (prefers-reduced-motion: reduce) {
  .surgit-pret .uni-bloc-txt h3 .nom::after { transform: none !important; }
  .surgit-pret .uni-bloc-txt h3 .nom { color: var(--noir) !important; }
}

.uni-bloc-txt a, .corps-texte a, .faq-rep a {
  color: var(--orange); text-decoration: none;
  border-bottom: 1px solid rgba(232, 98, 31, .42);
  transition: border-color .18s ease, color .18s ease;
}
.uni-bloc-txt a:hover, .corps-texte a:hover, .faq-rep a:hover {
  color: var(--orange-vif); border-bottom-color: var(--orange-vif);
}

/* La foire aux questions.
   Batie sur <details>, comme les deroulants du menu : l'ouverture, la
   fermeture et l'acces au clavier viennent du navigateur, et la section
   reste utilisable sans script - ce que demande la regle de degradation
   gracieuse du projet. Le marqueur natif se retire de deux facons, selon
   les navigateurs. */
.faq { max-width: 74ch; }
.faq-item { border-bottom: 1px solid var(--filet); }
.faq-item:first-child { border-top: 1px solid var(--filet); }
.faq-item > summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1.5em; padding: 1.25em 0;
  font-size: 13.5px; font-weight: 700; line-height: 1.55; color: var(--blanc);
  transition: color .18s ease;
}
.faq-item > summary::-webkit-details-marker { display: none; }
.faq-item > summary:hover { color: var(--orange); }
.faq-item > summary:focus-visible { outline: 2px solid var(--orange); outline-offset: 3px; }
.faq-item .chevron {
  flex: none; width: 12px; height: 8px; color: var(--orange);
  transition: transform .2s ease;
}
.faq-item[open] > summary .chevron { transform: rotate(180deg); }
.faq-rep { padding: 0 0 1.35em; max-width: 68ch; }
.faq-rep p { margin: 0; font-size: 13px; line-height: 1.85; color: var(--blanc-80); }
.faq-rep p + p { margin-top: .9em; }

/* Deux boutons en fin de page : parler du projet, ou aller voir ce qui a
   deja ete livre. Le second repond a l'objection que le premier ne leve pas. */
.suite-btns { display: flex; flex-wrap: wrap; gap: var(--btns-gout, 14px); }
.suite-btns .btn { margin-top: 2em; }

@media (max-width: 899px) {
  /* Deux colonnes n'ont plus de sens : l'image passe au-dessus de son texte,
     et l'alternance - qui ne se lit qu'en largeur - est annulee pour que
     chaque image reste au-dessus du texte qu'elle illustre. */
  .uni-bloc { grid-template-columns: 1fr; gap: 20px; }
  .uni-bloc:nth-child(even) .uni-bloc-vue { order: 0; }
}

/* =============================================================================
   28. ZONE D'ADMINISTRATION - thème clair, deux surfaces sombres
   -----------------------------------------------------------------------------
   Tout est scopé à body.admin-sombre : le site public ne voit rien de ce qui
   suit, et une règle ajoutée ici ne peut pas lui échapper dessus.

   La palette n'est pas celle du site. C'est voulu : l'administration est un
   outil, pas une vitrine, et elle doit se reconnaître au premier coup d'oeil.
   Seul l'orange de la marque subsiste, en accent, pour que les deux univers
   restent de la même maison.
   ========================================================================== */
body.admin-sombre {
  /* --- LA PALETTE, ET CE QUE CHAQUE VALEUR A ETE MESUREE DONNER -----------
     L'ADMINISTRATION EST PASSEE AU CLAIR LE 20 OCTOBRE 2026, sur maquette.
     Fond gris tres clair, panneaux blancs, et DEUX SURFACES QUI RESTENT
     SOMBRES : la barre laterale et la barre du haut. C'est ce que montre la
     maquette, et c'est ce qui donne a l'outil sa lecture - la navigation d'un
     cote, le travail de l'autre.

     CE QUI A CHANGE DE NATURE, ET POURQUOI CELA DEMANDE DES JETONS NEUFS.
     Dans le theme sombre, --a-panel servait A LA FOIS les cartes et la barre
     laterale : les deux etaient du meme bleu-noir. Ici elles divergent - les
     cartes sont blanches, la barre reste sombre - et un seul jeton ne peut
     plus porter les deux. D'ou la famille « --a-sombre-* » : elle decrit LA
     SURFACE SOMBRE, pas le theme, et c'est elle que lisent la barre laterale,
     la barre du haut et tout ce qui vit dessus.

     SANS CE DECOUPAGE LE DEFAUT EST SILENCIEUX ET TOTAL : --a-txt passe de
     #f2f5fa a #252B32, donc le fil d'Ariane, le nom du compte et les liens de
     navigation - qui le lisaient - deviennent du gris anthracite sur du gris
     anthracite. Mesure avant correction : 1.09 pour le texte courant. Rien ne
     plante, rien ne s'affiche.

     LES RAPPORTS SONT MESURES SUR LE FOND REEL DE CHAQUE USAGE - une carte
     blanche, le gris de la page, ou la surface sombre - jamais suppose. 4.5
     est le seuil du texte courant, 3 celui d'un composant d'interface
     (WCAG 1.4.11). ON CORRIGE LES JETONS, PAS LES PAGES : 52 regles lisent
     --a-bord et 23 --a-txt ; une exception par ecran aurait laisse les autres
     dans l'etat d'avant, et personne n'aurait su lesquels. */

  /* --- LES SURFACES CLAIRES ---------------------------------------------- */
  --a-bg:      #E9ECEF;   /* fond de page */
  --a-panel:   #FFFFFF;   /* cartes, tableaux, champs */
  --a-panel-2: #F2F4F7;   /* survol de ligne, zebrage, piste de jauge */

  /* Le filet d'une carte ne porte aucun texte : il separe deux surfaces
     claires et n'a pas a tenir le seuil d'un texte. 1.38 sur blanc. */
  --a-bord:    #D6DCE2;

  --a-txt:     #252B32;   /* 14.28 sur une carte, 12.04 sur la page */
  /* LE TEXTE SECONDAIRE PORTE L'ESSENTIEL DU VOLUME : 156 « inst-p » et 106
     « champ-aide », plus les en-tetes de tableau et les etats. La maquette
     proposait #66717D, qui donne 4.19 sur le gris de la page - sous le seuil.
     Assombri du minimum necessaire ; la hierarchie avec --a-txt tient. */
  /* DU NOIR FRANC, demande le 20 octobre apres avoir vu l'ecran. Le gris
     mesure tenait le seuil (5.34 sur une carte) mais restait un gris : sur une
     interface dense, ou ce jeton porte 156 « inst-p » et 106 « champ-aide »,
     la hierarchie avec --a-txt se paie en lisibilite. Elle est donc abandonnee
     volontairement - les deux se distinguent desormais par la graisse et la
     taille, pas par la couleur. 21.00 sur une carte, 17.71 sur la page. */
  --a-muet:    #000000;

  /* --- L'ACCENT, ET POURQUOI IL EN FAUT DEUX ------------------------------
     --a-accent est l'orange de la marque, celui de la maquette, et il sert de
     FOND : bouton d'action principal, onglet courant, filet d'accent.
     --a-accent-txt est le meme orange assombri, pour la quinzaine de regles ou
     l'accent est LUI-MEME DU TEXTE sur une surface claire - a #F15A24 il n'y
     donne que 3.37, et ces regles-la portent des libelles de 11 a 13 px. */
  --a-accent:     #D6420E;   /* fond plein ; blanc dessus : 4.52 - demande du 20 oct. */
  --a-accent-2:   #F15A24;   /* survol du fond plein - l'orange d'origine */
  --a-accent-txt: #D6420E;   /* l'accent EN TEXTE : 4.52 sur une carte */

  /* LES DEUX JETONS D'ACCENT PORTENT DESORMAIS LA MEME VALEUR, et on les garde
     tous les deux. Ce ne sont pas deux fois la meme chose : l'un est un FOND,
     l'autre un TEXTE, et ils ne bougeront pas ensemble le jour ou l'un des deux
     devra bouger - une teinte de fond se choisit a l'oeil, une teinte de texte
     se choisit au rapport de contraste. Les fusionner maintenant obligerait a
     les redecouper plus tard, sur une quinzaine de regles.

     CE QUE LE CHANGEMENT REGLE : les quatre derniers ecarts de contraste du
     theme - onglet de menu courant, sa pastille, bouton d'action principal,
     onglet actif d'une fiche - passent de 3.37 a 4.52. bin/verifier-contrastes.php
     ECHOUE desormais si leurs tolerances restent dans TOLERES : une tolerance
     qui ne protege plus rien est pire qu'une tolerance absente, et les quatre
     ont donc ete retirees.

     LE SURVOL PREND L'ANCIEN ORANGE, et ce n'est pas un repli : un survol doit
     s'ECLAIRCIR par rapport au repos, donc partir de #D6420E vers #F15A24.
     Garder #FF6A33 aurait donne un ecart de deux crans au survol, et un blanc a
     2.87 dessus. */

  /* --- LES QUATRE ETATS, ET UN SEUL JETON SUFFIT A CHACUN -----------------
     Le rapport de contraste est SYMETRIQUE : un vert assez fonce pour se lire
     sur du blanc porte donc aussi du blanc SUR lui. Les memes valeurs servent
     ainsi les libelles colores (une vingtaine de regles : etiquettes, jauges,
     compteurs, verdicts) ET les pastilles pleines de la maquette, ou le texte
     est blanc. Deux jeux auraient diverge a la premiere retouche. */
  --a-ok:      #237946;   /* 5.39 sur une carte, 4.55 sur la page */
  --a-err:     #CA2D23;   /* 5.37 / 4.53 */
  --a-attn:    #9B5C00;   /* 5.35 / 4.51 */
  --a-neutre:  #616B78;   /* 5.41 / 4.56 - la pastille « Brouillon » */

  /* La bordure d'un champ de saisie est un COMPOSANT d'interface au sens de
     WCAG 1.4.11 et demande 3:1, la ou un filet de carte n'en a pas besoin.
     Les champs vivent sur du blanc : 3.55. Pousser les 52 filets a cette
     valeur aurait transforme le theme en fil de fer. */
  --a-bord-champ: #7F8995;

  /* --- LES DEUX SURFACES SOMBRES ------------------------------------------
     Elles ne sont pas « le theme d'avant qui reste » : ce sont deux surfaces
     de la maquette, avec leurs propres valeurs de texte. Tout ce qui vit
     DESSUS lit ces jetons-la, jamais --a-txt ni --a-muet. */
  --a-sombre:      #303841;   /* la barre laterale */
  --a-sombre-haut: #252B32;   /* la barre du haut */
  --a-sombre-2:    #3C4650;   /* survol dans le sombre */
  --a-sombre-bord: #3C4650;   /* les separateurs dans le sombre */
  --a-sombre-txt:  #FFFFFF;   /* 11.88 sur la barre laterale, 14.28 sur celle du haut */
  --a-sombre-muet: #AEB8C2;   /* 5.90 / 7.10 */

  background: var(--a-bg); color: var(--a-txt);
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 14px; line-height: 1.55; margin: 0;
}
body.admin-sombre * { box-sizing: border-box; }

/* --- ANNULER CE QUI FUIT DU SITE PUBLIC ----------------------------------
   Trois noms de classe existent des deux cotes : « carte », « champ » et
   « btn ». Les regles publiques n'ont AUCUNE portee - elles s'appliquent donc
   aussi ici. Celle de « carte » est destructrice :

       .carte { aspect-ratio: var(--carte-ratio); overflow: hidden; }

   --carte-ratio est defini sur :root, donc partout. Chaque carte de
   l'administration recevait ainsi une hauteur imposee de 350/300 et rognait
   tout ce qui depassait. C'est ce qui donnait des onglets « vides » : le
   contenu etait bien la, ecrit dans le HTML, et coupe par une regle ecrite
   pour les vignettes de la page d'accueil. Rien dans les deux fichiers pris
   separement ne le montre - c'est leur rencontre qui casse.

   On annule explicitement plutot que de renommer les classes des huit ecrans :
   une regle qui dit ce qu'elle repare se relit, un renommage massif ne se
   relit pas. « btn » est laisse tel quel : la reprise y est voulue, et
   l'administration le restyle plus bas. */
body.admin-sombre .carte,
body.admin-nu .carte {
  aspect-ratio: auto; overflow: visible; display: block;
  isolation: auto; text-decoration: none; color: var(--a-txt);
}
/* Cote public, « champ » enveloppe un libelle et son input ; ici c'est l'input
   lui-meme qui la porte. Un input en display:flex n'a pas de sens. */
body.admin-sombre .champ,
body.admin-nu .champ { display: block; }

/* --- la coquille : deux colonnes --------------------------------------- */
.admin-shell { display: flex; gap: 2px; padding: 0; min-height: 100vh; }

/* La barre n'est plus une carte mais une COLONNE de cartes : une par section
   - Admin, Rank, Chat. Le decoupage se voit alors sans lire les libelles, et
   passer d'une section a l'autre reste un clic, sans menu general au-dessus. */
.admin-sidebar {
  flex: none; width: 234px; align-self: flex-start; position: sticky; top: 1px;
  display: flex; flex-direction: column; gap: 12px;
  /* LE FOND PASSE SUR LA COLONNE, plus seulement sur ses cartes. Avec un ecart
     de 2 px entre les blocs, la bande claire qui transparaissait entre eux se
     lisait comme une rayure ; la colonne porte donc la couleur, et les blocs
     s'y fondent. */
  background: var(--a-sombre);
}
.admin-bloc {
  background: var(--a-sombre); border: 1px solid var(--a-sombre-bord); border-radius: 14px;
  padding: 18px 12px;
}
/* La section ouverte se distingue par sa bordure, pas par sa couleur de fond :
   le fond sert deja a marquer la page courante a l'interieur de la carte, et
   deux niveaux de fond l'un dans l'autre ne se lisent plus. */
.admin-bloc.actif { border-color: var(--a-accent); }
.admin-logo { display: block; text-decoration: none; padding: 4px 10px 18px; }
.admin-logo-mot  { display: block; font-size: 19px; font-weight: 900; letter-spacing: -.01em; color: #fff; }
.admin-logo-sous { display: block; font-size: 10px; font-weight: 700; letter-spacing: .34em; color: var(--a-accent); }

.admin-nav { display: flex; flex-direction: column; gap: 2px; }
.admin-nav-lien {
  display: flex; align-items: center; gap: 10px; padding: 9px 10px; border-radius: 9px;
  color: var(--a-sombre-muet); text-decoration: none; font-size: 13px; font-weight: 600;
  transition: background .15s ease, color .15s ease;
}
.admin-nav-lien:hover { background: var(--a-sombre-2); color: var(--a-sombre-txt); }
/* L'ONGLET COURANT EST ORANGE PLEIN, comme la maquette : c'est la seule
   surface de la barre qui porte l'accent en fond, et elle le merite - c'est
   la reponse a « ou suis-je », celle qu'on cherche sans lire. */
.admin-nav-lien.actif { background: var(--a-accent); color: #fff; }
/* Le filet d'accent marque l'onglet courant sans compter sur la seule couleur
   de texte, qui passe mal en contraste réduit. Sur un fond deja orange il
   devient un liseré plus sombre, qui tient le bord de la pastille. */
.admin-nav-lien.actif { box-shadow: inset 3px 0 0 var(--a-accent-txt); }
.admin-nav-lien:focus-visible { outline: 2px solid var(--a-accent); outline-offset: 2px; }
.admin-ico { width: 18px; height: 18px; flex: none; }
.admin-nav-lien .pastille {
  margin-left: auto; background: var(--a-accent); color: #fff; font-size: 11px;
  font-weight: 800; padding: 1px 7px; border-radius: 999px;
}
.admin-nav-sep { display: block; height: 1px; background: var(--a-sombre-bord); margin: 10px 8px; }

/* Le marqueur « (S) » - reserve au super-administrateur.
   MEME COULEUR QUE LE BADGE « super » de la barre du haut (var(--a-accent)),
   pour qu'on relie les deux du premier coup d'oeil : le badge dit « vous etes
   super-administrateur », le marqueur dit « cet ecran l'exige ».
   Pas de pastille ici - le badge est un bloc isolé, celui-ci precede un
   libelle et doit rester un prefixe discret, sans decaler la ligne. */
body.admin-sombre .marque-super {
  color: var(--a-sombre-muet); font-weight: 800; font-size: .92em;
  margin-right: 5px; cursor: help;
}
/* Sur l'onglet courant le fond EST l'accent : le marqueur y passe donc en
   blanc. Le laisser orange l'aurait fait disparaitre precisement sur l'ecran
   ou l'on se trouve - le defaut que cette regle corrigeait deja, a l'envers. */
body.admin-sombre .admin-nav-lien.actif .marque-super { color: #fff; }

.admin-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.admin-topbar {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  background: var(--a-sombre-haut); border: 1px solid var(--a-sombre-bord); border-radius: 14px;
  padding: 14px 20px;
}
.admin-page-titre { margin: 0; font-size: 17px; font-weight: 800; color: #fff; }
.admin-topbar-user { font-size: 12px; color: var(--a-sombre-muet); display: flex; align-items: center; gap: 8px; }

/* --- LES OUTILS DE LA BARRE DU HAUT ---------------------------------------
   LA MAQUETTE MET TROIS CHOSES A DROITE DU FIL : une recherche, et le compte
   courant avec son rond d'initiales. La barre ne portait jusqu'ici que
   l'adresse de courriel en gris, ce qui repondait a « qui suis-je » et a rien
   d'autre.

   LA RECHERCHE EST UN CHAMP, PAS UN BOUTON : elle s'envoie a la touche Entree,
   donc elle fonctionne sans une ligne de JavaScript. La loupe est decorative -
   le libelle, lui, existe pour les lecteurs d'ecran et reste cache a l'oeil. */
body.admin-sombre .topbar-outils {
  display: flex; align-items: center; gap: 12px; flex: 0 1 auto; min-width: 0;
}
body.admin-sombre .topbar-rech {
  display: flex; align-items: center; gap: 8px;
  flex: 0 1 320px; min-width: 0;
  padding: 0 12px; height: 38px;
  background: var(--a-panel); border: 1px solid var(--a-bord); border-radius: 9px;
}
body.admin-sombre .topbar-rech:focus-within { border-color: var(--a-accent); }
/* LE CHAMP DE RECHERCHE EST CLAIR SUR UNE BARRE SOMBRE : son texte, son
   invite et sa loupe lisent donc les jetons CLAIRS, pas ceux de la surface
   sombre. C'est le seul endroit de la barre du haut dans ce cas. */
body.admin-sombre .topbar-rech svg { width: 16px; height: 16px; flex: none; color: var(--a-muet); }
body.admin-sombre .topbar-rech input {
  flex: 1 1 auto; min-width: 0;
  background: none; border: 0; outline: none;
  color: var(--a-txt); font: inherit; font-size: 13px;
}
body.admin-sombre .topbar-rech input::placeholder { color: var(--a-muet); }

/* LE CHIP : rond d'initiales, role, raison sociale. Il ne se clique pas - il
   n'y a pas de menu de compte dans cette application, et un bouton qui
   n'ouvre rien serait un controle qui ment. */
body.admin-sombre .topbar-moi { display: flex; align-items: center; gap: 10px; }
body.admin-sombre .topbar-moi-rond {
  display: grid; place-items: center; flex: none;
  width: 34px; height: 34px; border-radius: 50%;
  background: rgba(232,98,31,.16); border: 1px solid rgba(232,98,31,.42);
  color: var(--a-accent); font-size: 12.5px; font-weight: 800; letter-spacing: .02em;
}
body.admin-sombre .topbar-moi-txt { display: flex; flex-direction: column; line-height: 1.25; }
body.admin-sombre .topbar-moi-txt b { font-size: 12.5px; font-weight: 700; color: var(--a-sombre-txt); }
body.admin-sombre .topbar-moi-txt span { font-size: 11px; color: var(--a-sombre-muet); }

@media (max-width: 1100px) {
  /* La raison sociale et le role sortent avant la recherche : le rond suffit
     a dire de quel compte il s'agit, la recherche ne se remplace pas. */
  body.admin-sombre .topbar-moi-txt { display: none; }
}
@media (max-width: 820px) {
  body.admin-sombre .topbar-rech { display: none; }
}
.badge-super {
  background: rgba(232,98,31,.16); color: var(--a-accent); border: 1px solid rgba(232,98,31,.4);
  font-size: 10px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  padding: 2px 7px; border-radius: 999px;
}
.admin-contenu { display: flex; flex-direction: column; gap: 8px; }

/* --- cartes, KPI, tableaux ---------------------------------------------
   « carte » existe des deux cotes : c'est la carte media de l'accueil (section
   12) et le panneau de l'administration. Les deux regles portaient le meme
   selecteur, et celle-ci, ecrite plus bas, l'emportait : les cartes de la page
   d'accueil recevaient en silence un rayon de 14 px et 20 px de remplissage
   qui n'etaient pas pour elles. Les jetons de couleur, eux, n'existent pas
   hors de l'administration, si bien que le fond et le filet tombaient - la
   panne ne se voyait donc qu'a moitie, ce qui est la pire facon de se voir.

   Tout ce qui suit est donc porte par body.admin-sombre, comme le reste de
   cette section. Le nom reste partage, ce qui est sans importance : c'est la
   portee qui compte, pas le mot. */
body.admin-sombre .carte {
  background: var(--a-panel); border: 1px solid var(--a-bord); border-radius: 14px; padding: 18px 20px;
}
body.admin-sombre .carte > h2 {
  margin: 0 0 14px; font-size: 13px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--a-muet);
}

/* --- verdict des mots-cles ----------------------------------------------
   Un terme par pastille : une liste a puces de soixante entrees se lirait en
   colonne sur trois ecrans de haut. Ce qui manque est barre ET dit en toutes
   lettres - la couleur seule ne se voit ni en niveaux de gris, ni pour une
   partie des lecteurs. */
/* La jauge : « 16/16 » d'un coup d'oeil, et la couleur pour trier les lignes
   sans les lire. Le chiffre reste la verite - la couleur seule ne se voit ni en
   niveaux de gris, ni pour une partie des lecteurs. */
/* --- etiquettes de statut ------------------------------------------------
   Trois etats, trois couleurs, et le mot ecrit en toutes lettres : la couleur
   seule ne se voit ni en niveaux de gris, ni pour une partie des lecteurs. */
/* --- bloc replie : la liste des migrations, qu'on ne lit qu'en cas de doute */
/* --- choix de periode --------------------------------------------------- */
/* --- deux colonnes : un choix a gauche, son contenu a droite --------------
   Les onglets de langue sont verticaux et non horizontaux, a la difference de
   ceux du haut : les premiers changent de SECTION, ceux-ci changent de VERSION
   du meme contenu. Deux gestes differents, deux formes differentes - sans quoi
   on ne sait plus lequel on est en train de faire.

   Sous 900 px la colonne repasse au-dessus : un menu vertical de 190 px sur un
   telephone ne laisserait rien au formulaire. */
body.admin-sombre .deux-colonnes {
  display: grid; grid-template-columns: 190px minmax(0, 1fr); gap: 18px; align-items: start;
}
body.admin-sombre .cotes {
  display: flex; flex-direction: column; gap: 4px;
  background: var(--a-panel); border: 1px solid var(--a-bord);
  border-radius: 14px; padding: 12px;
  position: sticky; top: 18px;
}
body.admin-sombre .cote-lien {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  padding: 11px 14px; border-radius: 9px; border: 1px solid transparent;
  color: var(--a-muet); text-decoration: none; font-size: 13px;
}
body.admin-sombre .cote-lien b { font-weight: 600; }
body.admin-sombre .cote-lien i {
  font-style: normal; font-size: 11px; font-weight: 700; letter-spacing: .06em; opacity: .7;
}
body.admin-sombre .cote-lien:hover { color: var(--a-txt); }
body.admin-sombre .cote-lien.actif {
  color: var(--a-txt); background: rgba(232,98,31,.1); border-color: var(--a-accent);
}
body.admin-sombre .cotes-note {
  margin: 10px 2px 2px; font-size: 11px; line-height: 1.55; color: var(--a-muet);
}
body.admin-sombre .cotes-note b { color: var(--a-accent-txt); }
body.admin-sombre .cotes-corps { margin: 0; }
body.admin-sombre .cotes-corps .carte { margin: 0; }

/* Le repere des champs identiques dans les deux langues. Un signe, pas un mot :
   il se pose a cote d'un libelle sans le rallonger, et l'infobulle dit le
   reste. */
body.admin-sombre label .neutre {
  display: inline-block; margin-left: 6px; padding: 0 6px;
  border: 1px solid var(--a-accent); border-radius: 4px;
  color: var(--a-accent-txt); font-style: normal; font-size: 10px; font-weight: 700;
  cursor: help;
}

@media (max-width: 900px) {
  body.admin-sombre .deux-colonnes { grid-template-columns: 1fr; }
  body.admin-sombre .cotes { position: static; flex-direction: row; flex-wrap: wrap; }
  body.admin-sombre .cote-lien { flex: 1 1 auto; justify-content: center; }
  body.admin-sombre .cotes-note { flex: 1 1 100%; }
}

/* --- onglets a l'interieur d'une page -------------------------------------
   Distincts de la barre laterale : celle-ci change de page, ceux-ci changent
   de section dans la meme. D'ou un trait continu sous la rangee, et l'onglet
   actif qui l'interrompt - la forme dit a quoi elle sert. */
body.admin-sombre .vues {
  display: flex; flex-wrap: wrap; gap: 2px;
  border-bottom: 1px solid var(--a-bord); margin-bottom: 4px;
}
body.admin-sombre .vue-lien {
  padding: 10px 18px; margin-bottom: -1px;
  border: 1px solid transparent; border-bottom: 1px solid var(--a-bord);
  border-radius: 10px 10px 0 0;
  font-size: 13px; font-weight: 600; color: var(--a-muet); text-decoration: none;
}
body.admin-sombre .vue-lien:hover { color: var(--a-txt); }
body.admin-sombre .vue-lien.actif {
  color: var(--a-txt); background: var(--a-panel);
  border-color: var(--a-bord); border-bottom-color: var(--a-panel);
}

body.admin-sombre .periodes { display: flex; gap: 6px; flex-wrap: wrap; }
body.admin-sombre .periode-lien {
  padding: 6px 14px; border-radius: 8px; border: 1px solid var(--a-bord);
  font-size: 12px; font-weight: 600; color: var(--a-muet); text-decoration: none;
}
body.admin-sombre .periode-lien:hover { color: var(--a-txt); }
body.admin-sombre .periode-lien.actif { color: var(--a-txt); border-color: var(--a-accent); }

/* --- graphiques ----------------------------------------------------------
   Le SVG s'etire en largeur et garde sa hauteur : preserveAspectRatio="none"
   deformerait le trait si on ne compensait pas par vector-effect, ce que fait
   le trace lui-meme. */
body.admin-sombre .g-fig { margin: 0; }
body.admin-sombre .g-titre {
  font-size: 12px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--a-muet); margin-bottom: 12px;
}
body.admin-sombre .g-titre b { color: var(--a-txt); font-size: 15px; margin-left: 6px; }
body.admin-sombre .g-aire { line-height: 0; }
body.admin-sombre .g-svg { width: 100%; height: 150px; display: block; }
body.admin-sombre .g-legende {
  display: flex; justify-content: space-between; margin-top: 6px;
  font-size: 11px; color: var(--a-muet);
}
body.admin-sombre .g-vide {
  display: flex; align-items: center; justify-content: center;
  color: var(--a-muet); font-size: 13px; font-style: italic;
  border: 1px dashed var(--a-bord); border-radius: 10px; margin: 0;
}

/* Barres horizontales : le libelle peut etre long, la barre garde sa place, et
   la valeur reste alignee a droite pour se comparer d'une ligne a l'autre. */
body.admin-sombre .g-barres { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; }
body.admin-sombre .g-barre {
  display: grid; grid-template-columns: minmax(0, 150px) 1fr auto;
  align-items: center; gap: 10px; font-size: 12px;
}
body.admin-sombre .g-barre-lib {
  color: var(--a-txt); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
body.admin-sombre .g-barre-piste {
  height: 8px; border-radius: 4px; background: var(--a-bg); overflow: hidden;
}
body.admin-sombre .g-barre-rempli { display: block; height: 100%; background: var(--a-accent); }
body.admin-sombre .g-barre-val { color: var(--a-txt); font-variant-numeric: tabular-nums; white-space: nowrap; }
body.admin-sombre .g-barre-val i { color: var(--a-muet); font-style: normal; font-size: 11px; }

/* Anneau : le total au centre, la legende dessous avec sa pastille. */
body.admin-sombre .g-anneau { position: relative; max-width: 190px; margin: 0 auto; }
body.admin-sombre .g-anneau svg { width: 100%; display: block; }
body.admin-sombre .g-anneau-centre {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 20px; font-weight: 800; color: var(--a-txt);
}
body.admin-sombre .g-legende-liste { list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 6px; }
body.admin-sombre .g-legende-liste li {
  display: grid; grid-template-columns: 10px 1fr auto; align-items: center; gap: 9px; font-size: 12px;
}
body.admin-sombre .g-legende-liste i { width: 10px; height: 10px; border-radius: 3px; display: block; }
body.admin-sombre .g-legende-liste b { color: var(--a-txt); font-variant-numeric: tabular-nums; }

/* --- entonnoir -----------------------------------------------------------
   Les barres partent toutes de la gauche et ne sont pas centrees : un
   entonnoir centre fait joli et rend les ecarts illisibles. */
body.admin-sombre .entonnoir { list-style: none; margin: 16px 0 0; padding: 0; display: grid; gap: 18px; }
body.admin-sombre .entonnoir-tete {
  display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-bottom: 6px;
}
body.admin-sombre .entonnoir-lib { font-size: 13px; font-weight: 600; color: var(--a-txt); }
body.admin-sombre .entonnoir-lib i {
  display: block; font-style: normal; font-size: 11px; font-weight: 400; color: var(--a-muet);
}
body.admin-sombre .entonnoir-val { font-size: 13px; color: var(--a-muet); white-space: nowrap; }
body.admin-sombre .entonnoir-val b { color: var(--a-txt); margin-left: 6px; font-size: 15px; }
body.admin-sombre .entonnoir-piste {
  height: 14px; border-radius: 7px; background: var(--a-bg); overflow: hidden;
}
body.admin-sombre .entonnoir-rempli { height: 100%; background: var(--a-accent); border-radius: 7px; }
body.admin-sombre .entonnoir-perte { margin: 6px 0 0; font-size: 11px; color: var(--a-attn); }

/* Case a cocher alignee avec les champs de la meme ligne. */
body.admin-sombre .case {
  display: flex; align-items: center; gap: 8px; margin: 0;
  font-size: 12px; font-weight: 400; color: var(--a-txt); text-transform: none; letter-spacing: 0;
}
body.admin-sombre .case input { width: auto; margin: 0; }
body.admin-sombre input[type=file] { padding: 7px 10px; font-size: 12px; }

body.admin-sombre .repli { margin-top: 14px; border-top: 1px solid var(--a-bord); padding-top: 12px; }
body.admin-sombre .repli > summary {
  cursor: pointer; font-size: 12px; font-weight: 700; color: var(--a-muet);
  letter-spacing: .04em; list-style: none;
}
body.admin-sombre .repli > summary::before { content: "▸ "; }
body.admin-sombre .repli[open] > summary::before { content: "▾ "; }
body.admin-sombre .repli > summary::-webkit-details-marker { display: none; }
body.admin-sombre .repli > summary:hover { color: var(--a-txt); }
body.admin-sombre .repli > *:not(summary) { margin-top: 12px; }

/* Un troisieme etat pour les garde-fous : ni vert ni bloquant. NOINDEX avant
   la mise en ligne est une decision, pas une faute - l'afficher en rouge
   apprendrait a ignorer le rouge. */
body.admin-sombre .inst-etat li.note::before { content: "•"; color: var(--a-muet); }

/* Le journal : du texte technique, en chasse fixe, qui defile sur place plutot
   que d'allonger la page de deux cents lignes. */
body.admin-sombre .journal {
  margin: 10px 0 0; padding: 14px 16px; max-height: 340px; overflow: auto;
  border-radius: 10px; border: 1px solid var(--a-bord); background: rgba(0,0,0,.25);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 11.5px; line-height: 1.75; color: var(--a-muet); white-space: pre-wrap;
  overflow-wrap: anywhere;
}

body.admin-sombre .etiquette {
  display: inline-block; padding: 3px 9px; border-radius: 20px;
  border: 1px solid var(--a-bord); font-size: 11px; font-weight: 700;
  letter-spacing: .03em; white-space: nowrap;
}
body.admin-sombre .etiquette--nouveau { color: var(--a-accent-txt); border-color: var(--a-accent); }
body.admin-sombre .etiquette--traite  { color: var(--a-ok);     border-color: var(--a-ok); }
body.admin-sombre .etiquette--sans    { color: var(--a-muet); }
/* « manque » et « sans » ne disent pas la meme chose : l'un est un defaut a
   corriger, l'autre une absence sans consequence. Les rendre du meme gris
   revenait a ne plus voir le premier. */
body.admin-sombre .etiquette--manque  { color: var(--a-err);    border-color: var(--a-err); }

/* --- attente d'un bouton -------------------------------------------------
   Le disque tourne DANS le bouton, a la place de rien : un voile sur toute la
   page dirait « attendez » sans dire de quoi, et empecherait de lire ce qu'on
   vient de demander. L'animation est coupee pour qui a demande moins de
   mouvement - le bouton grise suffit alors a dire qu'il travaille. */
body.admin-sombre .bouton.occupe,
body.admin-sombre .btn.occupe { opacity: .72; cursor: progress; }
body.admin-sombre .bouton.occupe::before,
body.admin-sombre .btn.occupe::before {
  content: ""; display: inline-block; vertical-align: -2px;
  width: 13px; height: 13px; margin-right: 8px; border-radius: 50%;
  border: 2px solid currentColor; border-top-color: transparent;
  animation: admin-tourne .7s linear infinite;
}
@keyframes admin-tourne { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  body.admin-sombre .bouton.occupe::before,
  body.admin-sombre .btn.occupe::before { animation: none; }
}

/* --- ecran Pages : la liste facon WordPress -------------------------------
   Les compteurs de l'en-tete SONT les filtres, comme dans WordPress : on lit
   « A rediger (7) » et l'on clique dessus. Deux elements distincts - un
   compteur qui informe, un filtre qui agit - feraient lire deux fois la meme
   chose. */
body.admin-sombre .pages-etats {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 16px; font-size: 13px;
}
body.admin-sombre .pages-etat {
  color: var(--a-muet); text-decoration: none; padding: 3px 0;
  border-bottom: 2px solid transparent;
}
body.admin-sombre .pages-etat:hover { color: var(--a-txt); }
body.admin-sombre .pages-etat.actif { color: var(--a-txt); border-bottom-color: var(--a-accent); }
body.admin-sombre .pages-etat b { font-weight: 700; }
body.admin-sombre .pages-sep {
  flex: none; width: 1px; height: 22px; background: var(--a-bord); margin: 0 4px;
}

/* Lignes alternees : sur vingt-trois lignes de six colonnes, l'oeil perd la
   ligne en cours des qu'il traverse le tableau. */
body.admin-sombre .pages-table tbody tr:nth-child(odd) { background: var(--a-panel-2); }
body.admin-sombre .pages-table tbody tr:hover { background: var(--a-bg); }
body.admin-sombre .pages-table td { vertical-align: top; }
body.admin-sombre .pages-nom { display: block; color: var(--a-txt); font-size: 13.5px; }
body.admin-sombre .pages-fichier {
  display: block; font-size: 11px; color: var(--a-muet);
  font-family: ui-monospace, monospace; margin-top: 2px;
}
body.admin-sombre .pages-hors { color: var(--a-muet); }
body.admin-sombre .pages-rouge { color: var(--a-err); }
body.admin-sombre .pages-attn  { color: var(--a-attn); }
body.admin-sombre .pages-tri { color: inherit; text-decoration: none; }
body.admin-sombre .pages-tri:hover { color: var(--a-txt); }
body.admin-sombre .pages-tri.actif { color: var(--a-accent-txt); }

/* UN BOUTON PAR LIGNE, TOUT A DROITE - et un seul.

   Il y a eu quatre liens au survol, facon WordPress : modifier FR, modifier
   EN, voir FR, voir EN. Ils obligeaient a choisir sa langue AVANT d'avoir vu
   la page, alors que la fiche porte deja ce choix en gros, avec l'etat de
   chacune : on decidait a l'aveugle, et l'on revenait en arriere. Une seule
   porte d'entree, toujours visible et toujours au meme endroit, se vise sans
   reflechir et sans attendre que la souris reveille quelque chose.

   Toujours visible, donc : le survol cachait aussi le bouton a qui parcourt
   la liste au doigt sur une tablette. */
body.admin-sombre .pages-detail { text-align: right; white-space: nowrap; }
body.admin-sombre .pages-detail .bouton { margin: 0; }

/* L'intitule de colonne existe pour les lecteurs d'ecran et n'a rien a dire a
   l'oeil : le bouton se nomme lui-meme, et le repeter au-dessus ferait lire
   deux fois la meme chose. clip-path plutot que display:none, qui retirerait
   le texte de l'arbre d'accessibilite au lieu de le cacher. */
/* PAS DE « position: absolute » ICI, et c'est la lecon : sans ancetre
   positionne, l'element sort du conteneur defilant qui l'entoure - ici
   .tableau-enveloppe - et va se poser a son abscisse statique, quelque part
   au-dela du bord droit. Le document s'elargissait alors de 83 px, et la page
   entiere prenait une barre de defilement horizontale pour un mot invisible.
   Reste dans le flux : un pixel dans un en-tete ne gene personne, et le
   lecteur d'ecran lit toujours « Détail ». */
body.admin-sombre .pages-invisible {
  display: inline-block; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* Sous 900 px, le tableau devient des cartes empilees : six colonnes ne
   tiennent pas sur un telephone, et un defilement horizontal cache justement
   les colonnes d'etat qu'on vient lire. Chaque cellule porte son intitule en
   data-col, repris ici - le tableau reste un tableau pour un lecteur d'ecran. */
@media (max-width: 900px) {
  body.admin-sombre .pages-table thead { position: absolute; left: -9999px; }
  body.admin-sombre .pages-table,
  body.admin-sombre .pages-table tbody,
  body.admin-sombre .pages-table tr,
  body.admin-sombre .pages-table td { display: block; width: auto; }
  body.admin-sombre .pages-table tr {
    border: 1px solid var(--a-bord); border-radius: 10px;
    margin-bottom: 10px; padding: 10px 12px;
  }
  body.admin-sombre .pages-table td {
    border: 0; padding: 4px 0;
    display: grid; grid-template-columns: 130px 1fr; gap: 10px; align-items: baseline;
  }
  body.admin-sombre .pages-table td::before {
    content: attr(data-col); color: var(--a-muet); font-size: 11px;
  }
  body.admin-sombre .pages-table td[data-col="Page"] { grid-template-columns: 1fr; }
  body.admin-sombre .pages-table td[data-col="Page"]::before { content: none; }
  /* Sur une carte empilee, le bouton passe en pleine largeur et se detache du
     reste par un filet : c'est l'action de la carte, pas une ligne de plus. */
  /* QUALIFIE PAR « td », et il le faut : « .pages-table td » pese (0,2,3) et
     l'emportait sur « .pages-detail » (0,2,2). La colonne de 130 px reservee a
     l'intitule restait donc en place, avec un libelle vide, et le bouton se
     retrouvait pousse a droite de la carte au lieu de la barrer. Vu au rendu,
     pas a la relecture. */
  body.admin-sombre .pages-table td.pages-detail {
    display: block; text-align: left; margin-top: 8px; padding-top: 12px;
    border-top: 1px solid var(--a-bord);
  }
  body.admin-sombre .pages-table td.pages-detail::before { content: none; }
  /* Pleine largeur : sur un telephone, une cible tactile qui barre la carte se
     vise sans regarder. */
  body.admin-sombre .pages-table td.pages-detail .bouton {
    display: flex; justify-content: center; width: 100%;
  }
}

/* --- gestion des images : une ligne par emplacement -----------------------
   La vignette a gauche, les informations a droite. La vignette garde le
   rapport de l'emplacement - une image d'en-tete est panoramique, un bloc
   d'univers presque carre - parce que c'est cette forme-la qu'on reconnait
   d'un coup d'oeil sur la page. */
body.admin-sombre .img-ligne {
  display: grid; grid-template-columns: 300px 1fr; gap: 18px;
  padding: 16px 0; border-top: 1px solid var(--a-bord); align-items: start;
}
body.admin-sombre .img-ligne:first-of-type { border-top: 0; }
body.admin-sombre .img-vue {
  width: 300px; overflow: hidden; border-radius: 10px;
  border: 1px solid var(--a-bord); background: var(--a-bg);
}
body.admin-sombre .img-vue img { display: block; width: 100%; height: auto; }
/* La reserve du site est en width/height 100% : sans parent dimensionne elle
   s'effondre a sa hauteur minimale.

   LE RAPPORT EST UN MINIMUM, PAS UNE CONTRAINTE. Avec aspect-ratio, une
   reserve d'en-tete - 2000x500, donc 75 px de haut pour 300 de large - rognait
   ses propres cotes : le texte ne tient pas dans un bandeau de cette hauteur.
   On calcule donc une hauteur minimale a partir des cotes de l'emplacement, et
   la boite grandit quand le texte le demande. La forme reste reconnaissable,
   et rien n'est coupe. */
body.admin-sombre .img-vue .reserve {
  position: relative; height: auto;
  min-height: calc(300px * var(--h, 9) / var(--l, 16));
  padding: 14px; font-size: 12px;
}
body.admin-sombre .img-infos { min-width: 0; }
body.admin-sombre .img-infos dl {
  display: grid; grid-template-columns: max-content 1fr; gap: 4px 14px; margin: 0 0 12px;
  font-size: 12.5px;
}
body.admin-sombre .img-infos dt { color: var(--a-muet); }
body.admin-sombre .img-infos dd { margin: 0; color: var(--a-txt); word-break: break-word; }
body.admin-sombre .img-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
/* Discret : l'enregistrement d'une ligne n'est pas l'action principale de
   l'ecran, il ne doit pas crier plus fort que le contenu. */
body.admin-sombre .bouton--discret.btn-ligne { font-size: 12px; padding: 6px 12px; }
@media (max-width: 860px) {
  body.admin-sombre .img-ligne { grid-template-columns: 1fr; }
  body.admin-sombre .img-vue { width: 100%; }
}

/* La carte de compteur qui signale un probleme. Elle ne crie pas : un filet
   et un chiffre colore suffisent, le libelle dit le reste. */
body.admin-sombre .kpi-carte--alerte { border-color: var(--a-attn); }
body.admin-sombre .kpi-carte--alerte .kpi-val { color: var(--a-attn); }

/* --- pagination --------------------------------------------------------- */
body.admin-sombre .pagination { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 14px; }
body.admin-sombre .pagination-lien {
  min-width: 32px; text-align: center; padding: 5px 9px; border-radius: 7px;
  border: 1px solid var(--a-bord); color: var(--a-muet);
  font-size: 12px; font-variant-numeric: tabular-nums; text-decoration: none;
}
body.admin-sombre .pagination-lien:hover { color: var(--a-txt); }
body.admin-sombre .pagination-lien.actif { color: var(--a-txt); border-color: var(--a-accent); }

/* Le message d'une demande : du texte brut saisi par un visiteur. Il peut etre
   long, sans ponctuation, avec des mots a rallonge - d'ou le retour a la ligne
   force, sans quoi une URL collee etire le tableau sur trois ecrans. */
body.admin-sombre .demande-message {
  margin: 0; padding: 14px 16px; border-radius: 10px;
  background: var(--a-panel-2); border: 1px solid var(--a-bord);
  font-size: 13px; line-height: 1.7; white-space: pre-wrap; overflow-wrap: anywhere;
}

/* Les en-tetes de ligne d'un tableau en fiche - « Reçue le », « Adresse ». */
body.admin-sombre .donnees tbody th {
  text-align: left; font-weight: 600; color: var(--a-muet); font-size: 12px;
  vertical-align: top;
}

body.admin-sombre .rank-jauge {
  display: inline-block; min-width: 54px; text-align: center;
  padding: 3px 8px; border-radius: 6px; border: 1px solid var(--a-bord);
  font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums;
}
body.admin-sombre .rank-jauge--plein   { color: var(--a-ok);   border-color: var(--a-ok); }
body.admin-sombre .rank-jauge--partiel { color: var(--a-attn); border-color: var(--a-attn); }
body.admin-sombre .rank-jauge--absent  { color: var(--a-err); border-color: currentColor; }
/* Deux etats qui ne sont pas des echecs et ne doivent pas se lire comme tels :
   rien de declare, et pas encore verifie. */
body.admin-sombre .rank-jauge--vide,
body.admin-sombre .rank-jauge--inconnu {
  color: var(--a-muet); font-weight: 400; border-style: dashed;
}
/* La colonne des absents : plusieurs pastilles dans une cellule, qui doivent
   pouvoir passer a la ligne sans etirer le tableau. */
body.admin-sombre .rank-manque { max-width: 420px; }
body.admin-sombre .rank-manque .rank-terme {
  display: inline-block; margin: 2px 3px 2px 0; padding: 2px 7px; font-size: 11px;
}

body.admin-sombre .rank-liste {
  margin: 14px 0 0; padding: 0; list-style: none;
  display: flex; flex-wrap: wrap; gap: 7px;
}
body.admin-sombre .rank-terme {
  padding: 5px 10px; border-radius: 7px;
  border: 1px solid var(--a-bord); background: var(--a-panel-2);
  font-size: 12px; color: var(--a-txt);
}
body.admin-sombre .rank-terme b { font-weight: 600; }
body.admin-sombre .rank-terme i { color: var(--a-muet); font-size: 11px; }
body.admin-sombre .rank-absent { border-color: var(--a-attn); background: rgba(255,170,60,.07); }
body.admin-sombre .rank-absent b { text-decoration: line-through; color: var(--a-muet); }
body.admin-sombre .rank-etat {
  margin-left: 6px; font-size: 11px; font-style: italic; color: var(--a-ok);
}
body.admin-sombre .rank-absent .rank-etat { color: var(--a-attn); }
body.admin-sombre .rank-avalider b { font-style: italic; }
body.admin-sombre .ligne-active { background: rgba(232,98,31,.08); }

/* --- boutons d'action --------------------------------------------------
   Ils manquaient : les formulaires de l'administration rendaient des boutons
   bruts du navigateur au milieu d'un theme sombre. */
body.admin-sombre .bouton {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 16px; border: 1px solid transparent; border-radius: 9px;
  /* « --a-accent » ET NON « --orange » : c'est un bouton d'ADMINISTRATION, et
     l'orange public (#E8621F, 3.88 sous du blanc) n'a aucune raison de decider
     de son contraste. La regle lisait le jeton public depuis l'origine - un
     reste de l'epoque ou l'admin empruntait la palette du site -, si bien que
     passer --a-accent a #D6420E laissait ce bouton-la seul sous le seuil. */
  background: var(--a-accent); color: #fff;
  font: inherit; font-size: 12px; font-weight: 700; letter-spacing: .04em;
  cursor: pointer; text-decoration: none;
  transition: filter .15s ease, border-color .15s ease;
}
body.admin-sombre .bouton:hover { filter: brightness(1.12); }
/* Un bouton sans effet possible se desactive plutot que de mentir : « Purger »
   quand rien n'a atteint la retention ne supprimerait rien, et laisser cliquer
   ferait croire a une panne. */
/* UN ETAT DESACTIVE DOIT RESTER LISIBLE : il dit ce qu'on ne peut pas faire,
   et c'est une information. A 40 % d'opacite le libelle disparaissait dans le
   fond - on ne savait plus quel bouton etait grise. 62 % garde la difference
   visible avec un bouton actif sans effacer le texte. */
body.admin-sombre .bouton[disabled],
body.admin-sombre .btn[disabled] {
  opacity: .62; cursor: not-allowed; filter: none;
}
body.admin-sombre .bouton[disabled]:hover { filter: none; }
body.admin-sombre .bouton:focus-visible { outline: 2px solid var(--a-accent); outline-offset: 2px; }
/* Le bouton discret porte une action secondaire - reintegrer, annuler. Il doit
   se voir sans attirer : meme forme, sans la couleur d'accent. */
body.admin-sombre .bouton--discret {
  background: transparent; border-color: var(--a-bord); color: var(--a-muet);
}
body.admin-sombre .bouton--discret:hover { color: var(--a-txt); border-color: var(--a-muet); filter: none; }

/* Une ligne de saisie suivie de son bouton : les champs prennent la place
   disponible, le bouton garde la sienne, et le tout repasse en colonne quand
   la largeur ne suffit plus. */
body.admin-sombre .ligne-formulaire {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 12px;
}
body.admin-sombre .ligne-formulaire .champ { flex: 1 1 200px; margin: 0; }
.admin-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px; }
.kpi-carte { background: var(--a-panel); border: 1px solid var(--a-bord); border-radius: 14px; padding: 16px 18px; }
.kpi-lib { margin: 0; font-size: 11px; font-weight: 700; letter-spacing: .09em;
           text-transform: uppercase; color: var(--a-muet); }
.kpi-val { margin: .3em 0 0; font-size: 28px; font-weight: 900; color: var(--a-txt); line-height: 1.1; }
.kpi-sub { margin: .35em 0 0; font-size: 12px; color: var(--a-muet); }
.dash-grille { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: 18px; }

.tableau-enveloppe { overflow-x: auto; }
table.donnees { width: 100%; border-collapse: collapse; font-size: 13px; }
table.donnees th, table.donnees td { padding: 9px 12px; text-align: left; border-bottom: 1px solid var(--a-bord); }
table.donnees th { font-size: 11px; font-weight: 800; letter-spacing: .07em; text-transform: uppercase; color: var(--a-muet); }
table.donnees tbody tr:hover { background: var(--a-panel-2); }
table.donnees td.num { text-align: right; font-variant-numeric: tabular-nums; }

/* --- formulaires -------------------------------------------------------- */
body.admin-sombre label { display: block; margin: 14px 0 5px; font-size: 12px; font-weight: 700; color: var(--a-muet); }
/* Tous les champs de saisie, DESIGNES PAR EXCLUSION plutot que par
   enumeration. La liste precedente nommait search, date, text, email, password
   et number - elle avait oublie « url », et les champs LinkedIn et Instagram
   s'affichaient en style brut du navigateur au milieu d'un theme sombre.

   Une liste de types a tenir a jour finit toujours par en oublier un : le jour
   ou quelqu'un ecrit type="tel" ou type="month", le defaut revient. En
   excluant les quelques types qui ne sont PAS des zones de texte, le probleme
   ne peut plus se reproduire. */
body.admin-sombre input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=button]):not([type=image]):not([type=range]):not([type=color]),
body.admin-sombre select, body.admin-sombre textarea {
  width: 100%; padding: 9px 11px; font: inherit; color: var(--a-txt);
  /* --a-bord-champ et non --a-bord : le contour d'un champ est un composant
     d'interface, pas un filet de mise en page. A 1.33 on ne voyait pas ou
     commencait la zone cliquable - surtout sur un telephone, ou il n'y a pas
     de survol pour le decouvrir. */
  background: var(--a-bg); border: 1px solid var(--a-bord-champ); border-radius: 8px;
}
/* LE TEXTE DE SUBSTITUTION N'ETAIT PAS STYLE DU TOUT cote administration : il
   heritait de la regle publique, rgba(255,255,255,.3), soit 2.1 sur le fond de
   champ - illisible. On le pose a --a-muet, qui donne 7.5 sur ce fond, et en
   italique pour qu'il ne se confonde pas avec une valeur saisie. */
body.admin-sombre input::placeholder,
body.admin-sombre textarea::placeholder {
  color: var(--a-muet); opacity: .85; font-style: italic;
}
body.admin-sombre textarea { min-height: 120px; resize: vertical; line-height: 1.6; }
body.admin-sombre input:focus, body.admin-sombre select:focus, body.admin-sombre textarea:focus {
  outline: 2px solid var(--a-accent); outline-offset: 1px; border-color: transparent;
}
/* L'AIDE D'UN CHAMP NE DOIT PLUS DEPENDRE DU SITE PUBLIC.
   « .champ-aide » est une classe publique (--blanc-55, 11 px), reprise 106 fois
   dans l'administration. Elle y donnait 5.64 - a onze pixels. Elle lit
   desormais le jeton de l'administration et monte a 12 px : la lecon de la
   section 28 est qu'une classe partagee entre les deux feuilles doit etre
   redeclaree de ce cote-ci, pas subie. */
body.admin-sombre .champ-aide {
  color: var(--a-muet); font-size: 12px; line-height: 1.55;
}
/* Le libelle d'un champ est deja en --a-muet par la regle de « label », et il
   est en 700 : a 8.47 il se lit sans effort. Celui-la n'avait pas besoin
   d'etre touche, et il ne l'est pas. */

body.admin-sombre .btn {
  display: inline-flex; align-items: center; gap: 8px; margin-top: 16px;
  padding: 9px 16px; border: 1px solid var(--a-bord); border-radius: 8px;
  background: var(--a-panel-2); color: var(--a-txt); font: inherit; font-weight: 700;
  cursor: pointer; text-decoration: none;
}
body.admin-sombre .btn--plein { background: var(--a-accent); border-color: var(--a-accent); color: #fff; }
body.admin-sombre .btn--plein:hover { background: var(--a-accent-2); border-color: var(--a-accent-2); }
body.admin-sombre .btn--danger { background: rgba(214,53,94,.14); border-color: rgba(214,53,94,.45); color: #ff9db5; }
body.admin-sombre .btn:focus-visible { outline: 2px solid var(--a-accent); outline-offset: 2px; }

/* --- alertes ------------------------------------------------------------ */
.alerte { margin: 12px 0 0; padding: 10px 14px; border-radius: 9px; font-size: 13px; border: 1px solid; }
.alerte--ok   { background: rgba(47,169,104,.13);  border-color: #237946; color: #237946; }
.alerte--err  { background: rgba(214,53,94,.13);   border-color: #CA2D23; color: #CA2D23; }
/* « info » n'etait pas dans la demande et porte le MEME defaut - un bleu pale
   a 1.79 sur une carte. Le corriger seul aurait laisse la seule alerte bleue
   du depot illisible pendant que les trois autres redevenaient lisibles. */
.alerte--info { background: rgba(59,130,246,.13);  border-color: #1D4ED8; color: #1D4ED8; }
/* 2.60 SUR SA TEINTE, sous le seuil du texte courant - et ce type-la est le
   plus employe des quatre (17 appels). C'est la valeur demandee, appliquee
   telle quelle ; #B4480F tiendrait 4.49 en restant un orange. Voir DECISIONS.md. */
.alerte--attention { background: rgba(224,168,46,.13); border-color: #FF6A33; color: #FF6A33; }

/* --- pages nues : connexion, installation -------------------------------
   La boite est un <main>, et le site public pose « main { flex: 1 0 auto } »
   pour pousser son pied de page vers le bas des pages courtes. Heritee ici,
   cette regle etirait la boite de connexion sur toute la hauteur de l'ecran :
   un grand panneau presque vide, le formulaire colle en haut. On la
   neutralise, et justify-content fait alors ce qu'on lui demande depuis le
   debut - centrer la boite verticalement.

   C'est le genre de defaut qu'aucune relecture de CSS ne montre : les deux
   regles sont justes, c'est leur rencontre qui ne l'est pas. */
/* box-sizing sur le <body> LUI-MEME : la regle generale de l'administration
   vise « body.admin-sombre * », c'est-a-dire les descendants du corps, jamais
   le corps. Sans cette ligne, les 24 px de marge interne s'ajoutent au
   « min-height: 100vh » : le corps mesure un ecran plus 48 px, et la page
   porte une barre de defilement verticale permanente pour quarante-huit
   pixels de vide. */
body.admin-nu {
  box-sizing: border-box;
  display: flex; align-items: center; justify-content: center;
  min-height: 100vh; padding: 24px;
}
body.admin-nu .auth-boite { flex: none; }
.auth-boite {
  position: relative;   /* ancre le repere « Solar Admin » */
  width: 100%; max-width: 380px; background: var(--a-panel);
  border: 1px solid var(--a-bord); border-radius: 16px; padding: 30px 28px;
}

/* Le repere de l'outil, en haut a droite de la boite de connexion. Cette
   page-la s'annonce « InfiniteSport », comme le site public : dire qu'on est
   dans Solar Admin evite de la prendre pour un espace client. L'installateur,
   dont le titre est deja « SolarAdmin », ne le porte pas - le repeter a deux
   centimetres d'intervalle ne renseigne personne. */
.auth-marque {
  position: absolute; top: 12px; right: 16px; margin: 0;
  /* Meme defaut que .auth-titre, et non nomme dans la demande : la boite de
     connexion est blanche depuis la bascule. En muet plutot qu'en noir - c'est
     un repere d'outil, pas le titre. */
  font-size: 11px; font-weight: 700; letter-spacing: .03em; color: var(--a-muet);
}

/* Le bouton et les liens au centre. Sur une boite etroite, une action alignee
   a gauche se cherche : l'oeil descend la colonne des champs et passe a cote.
   Les libelles, eux, restent a gauche - ils se lisent avec leur champ. */
body.admin-nu form .btn { display: flex; width: fit-content; margin-inline: auto; }
body.admin-nu .auth-lien { text-align: center; }
.auth-boite--large { max-width: 620px; }
.auth-titre { margin: 0; font-size: 24px; font-weight: 900; color: #000; letter-spacing: -.02em; }
.auth-titre span { color: var(--a-accent-txt); }
.auth-sous { margin: .3em 0 20px; font-size: 13px; color: var(--a-muet); }
.auth-lien { margin: 14px 0 0; font-size: 12px; }
.auth-lien a { color: var(--a-muet); }
.auth-lien a:hover { color: var(--a-accent-txt); }
/* Le pot de miel : hors écran et hors du parcours au clavier. display:none
   serait plus simple, mais certains automates l'interprètent et évitent alors
   le champ - ce qui ruine le piège. */
.leurre { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.inst-h2 { margin: 26px 0 6px; font-size: 13px; font-weight: 800; letter-spacing: .06em;
           text-transform: uppercase; color: var(--a-muet); }
.inst-p { margin: 0 0 6px; font-size: 13px; color: var(--a-muet); }
.inst-etat { list-style: none; margin: 8px 0 0; padding: 0; font-size: 13px; }
.inst-etat li { padding: 5px 0 5px 22px; position: relative; }
.inst-etat li::before { position: absolute; left: 0; font-weight: 800; }
.inst-etat li.ok::before { content: "✓"; color: var(--a-ok); }
.inst-etat li.ko::before { content: "•"; color: var(--a-attn); }

@media (max-width: 980px) {
  .admin-shell { flex-direction: column; padding: 12px; gap: 12px; }
  .admin-sidebar { width: auto; position: static; }
  .admin-nav { flex-direction: row; flex-wrap: wrap; }
  .admin-nav-lien { flex: 1 1 auto; }
  /* Trois cartes empilees occuperaient tout l'ecran avant le contenu : sur
     petite largeur elles se rangent cote a cote et se deroulent en lignes. */
  .admin-bloc { flex: 1 1 260px; }
  .admin-sidebar { flex-direction: row; flex-wrap: wrap; }
}

/* --- fiche d'une page : l'en-tete -----------------------------------------
   Le fil d'Ariane, les deux langues, les liens de page a page, les onglets.
   Tout tient dans une carte : ce qui commande la fiche doit se lire d'un bloc,
   sans avoir a se demander ou finit l'en-tete et ou commence le contenu. */
body.admin-sombre .fiche-tete { padding-bottom: 0; }
body.admin-sombre .fiche-fil { margin: 0 0 12px; font-size: 12px; color: var(--a-muet); }
body.admin-sombre .fiche-fil a { color: var(--a-accent-txt); text-decoration: none; }
body.admin-sombre .fiche-fil a:hover { text-decoration: underline; }
body.admin-sombre .fiche-fil span { margin: 0 6px; color: var(--a-muet); }

body.admin-sombre .fiche-langues {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-bottom: 4px;
}
/* LES DEUX LANGUES SONT DES BOUTONS, PAS UN MENU DEROULANT. On passe de l'une
   a l'autre en permanence en redigeant, et chacune porte sa pastille d'etat :
   un menu cacherait justement l'information qu'on vient chercher - laquelle
   des deux reste a ecrire. */
body.admin-sombre .fiche-langue {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 14px; border-radius: 10px;
  border: 1px solid var(--a-bord); background: var(--a-panel-2);
  color: var(--a-muet); text-decoration: none; font-size: 13px;
}
body.admin-sombre .fiche-langue:hover { color: var(--a-txt); }
body.admin-sombre .fiche-langue b { font-weight: 800; letter-spacing: .05em; }
body.admin-sombre .fiche-langue.actif {
  border-color: var(--a-accent); color: var(--a-txt); background: rgba(232,98,31,.1);
}
/* Pousse les liens de navigation a droite : les deux langues restent collees a
   gauche, ou l'oeil les cherche. */
body.admin-sombre .fiche-espace { flex: 1 1 auto; }

body.admin-sombre .fiche-cible {
  margin-left: 8px; font-size: 11px; font-style: normal; font-weight: 400;
  color: var(--a-muet); letter-spacing: 0; text-transform: none;
}

/* --- fiche : le compteur de caracteres ------------------------------------
   Il vit DANS le <label>, a droite du nom du champ : c'est la qu'on le
   cherche, et il n'ajoute pas de ligne. La couleur dit l'etat, le chiffre dit
   combien - l'un sans l'autre ne suffirait pas, un daltonien ne lit que le
   chiffre et un pressé ne lit que la couleur. */
body.admin-sombre .compteur {
  display: inline-block; margin-left: 8px; padding: 1px 8px; border-radius: 20px;
  border: 1px solid var(--a-bord); font-size: 11px; font-weight: 700;
  font-variant-numeric: tabular-nums;
}
body.admin-sombre .compteur--bon   { color: var(--a-ok);   border-color: var(--a-ok); }
body.admin-sombre .compteur--court,
body.admin-sombre .compteur--long  { color: var(--a-attn); border-color: var(--a-attn); }
/* Vide n'est pas « trop court » : c'est un champ qu'on n'a pas encore rempli,
   et le peindre en orange ferait de l'ouverture d'une fiche neuve une alerte. */
body.admin-sombre .compteur--vide  { color: var(--a-muet); }

/* --- fiche : les deux colonnes de l'onglet SEO ----------------------------
   LE FORMULAIRE PREND LA PLACE, L'APERCU LA SUIT. C'est l'inverse de
   .deux-colonnes (Parametrages), dont la colonne etroite est a GAUCHE parce
   qu'elle porte une navigation. Ici la colonne etroite est a droite et ne porte
   qu'un miroir : reutiliser la grille des Parametrages ecrasait le formulaire
   a 190 px, et un champ de title de 190 px de large ne se relit pas.

   L'apercu est colle en haut : on le regarde en ecrivant, pas apres. */
body.admin-sombre .fiche-deux {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 420px);
  gap: 18px; align-items: start;
}
body.admin-sombre .fiche-form { margin: 0; }
body.admin-sombre .fiche-form .carte { margin: 0; }
body.admin-sombre .fiche-apercu { position: sticky; top: 18px; }

/* --- fiche : l'apercu Google ----------------------------------------------
   Un miroir, jamais un champ. Il reprend volontairement les couleurs de
   Google - bleu du lien, vert de l'adresse, gris du resume - parce que c'est
   la ressemblance qui rend l'apercu utile : on juge une accroche comme on la
   verra, pas comme une ligne de formulaire. */
body.admin-sombre .serp {
  background: var(--a-panel); border: 1px solid var(--a-bord);
  border-radius: 14px; padding: 16px 18px;
}
body.admin-sombre .serp-titre-h {
  margin: 0 0 12px; font-size: 11px; font-weight: 800; letter-spacing: .09em;
  text-transform: uppercase; color: var(--a-muet);
}
body.admin-sombre .serp-bloc {
  background: #fff; border-radius: 10px; padding: 14px 16px;
  font-family: Arial, Helvetica, sans-serif;
}
body.admin-sombre .serp-url   { margin: 0; font-size: 12px; color: #202124; overflow-wrap: anywhere; }
body.admin-sombre .serp-titre {
  margin: 2px 0 0; font-size: 19px; line-height: 1.3; color: #1a0dab;
  /* Deux lignes au plus, comme Google : au-dela il coupe, et un apercu qui
     montre trois lignes ferait accepter un title qui n'en aura que deux. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  line-clamp: 2; overflow: hidden;
}
body.admin-sombre .serp-desc {
  margin: 4px 0 0; font-size: 13.5px; line-height: 1.55; color: #4d5156;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  line-clamp: 3; overflow: hidden;
}
body.admin-sombre .serp-vide { color: var(--a-muet); font-style: italic; }

/* --- fiche : mots-cles ----------------------------------------------------- */
body.admin-sombre .mots-plafond {
  margin-left: 10px; padding: 2px 9px; border-radius: 20px;
  border: 1px solid var(--a-bord); font-size: 11px; font-weight: 700;
  color: var(--a-muet); font-variant-numeric: tabular-nums;
}
/* Le plafond atteint se voit AVANT de cliquer sur « Ajouter » : decouvrir au
   refus qu'il n'y a plus de place fait perdre la saisie et la patience. */
body.admin-sombre .mots-plafond.plein { color: var(--a-attn); border-color: var(--a-attn); }

/* TROIS TABLEAUX, UNE SEULE GRILLE. Chaque role a son bloc, donc son <table> :
   avec des largeurs calculees sur le contenu, « État » tombait a 700 px dans
   l'un et a 612 px dans l'autre, et l'oeil devait relire l'en-tete a chaque
   bloc pour savoir ce qu'il regardait. Les largeurs sont donc fixees, en
   pourcentage, et portees par un <colgroup> : les trois blocs se lisent alors
   comme un seul tableau coupe en trois. */
body.admin-sombre .mots-table { table-layout: fixed; }
body.admin-sombre .mots-table .c-terme     { width: 27%; }
body.admin-sombre .mots-table .c-etat      { width: 10%; }
body.admin-sombre .mots-table .c-zone      { width: 9%; }
body.admin-sombre .mots-table .c-collision { width: 22%; }
body.admin-sombre .mots-table .c-actions   { width: 23%; }
/* L'onglet Controle a ses propres colonnes - quatre zones au lieu de deux, et
   pas d'actions. Sans largeurs a lui, « table-layout: fixed » les rendrait
   toutes egales et reduirait le terme au sixieme de la ligne. */
body.admin-sombre .mots-table .c-terme-long { width: 38%; }
body.admin-sombre .mots-table .c-zone4      { width: 13%; }

/* Aligne en haut : la cellule des collisions peut faire cinq lignes, et
   centrer ferait flotter le terme au milieu de rien. */
body.admin-sombre .mots-table td { vertical-align: top; }
/* Un terme long ne doit pas elargir sa colonne - elle est fixe - ni deborder. */
body.admin-sombre .mots-table td b { overflow-wrap: anywhere; }
body.admin-sombre .mots-table .zone-tete { text-align: center; cursor: help; }
body.admin-sombre .mots-concurrents { margin: 6px 0 0; padding: 0; list-style: none; font-size: 12px; }
body.admin-sombre .mots-concurrents li { padding: 2px 0; }
body.admin-sombre .mots-concurrents a { color: var(--a-accent-txt); text-decoration: none; }
body.admin-sombre .mots-concurrents a:hover { text-decoration: underline; }
body.admin-sombre .mots-concurrents i { color: var(--a-muet); font-style: normal; }
body.admin-sombre .mots-arbitrer {
  display: inline-block; margin-top: 6px; font-size: 12px;
  color: var(--a-accent-txt); text-decoration: none;
}
body.admin-sombre .mots-arbitrer:hover { text-decoration: underline; }

/* Les actions d'un terme : des FORMULAIRES, parce qu'un lien qui supprime est
   suivi par le prechargement d'un navigateur. Ils doivent donc s'aligner comme
   des liens, sans quoi la cellule ferait six boutons empiles. */
/* LE FLEX EST SUR CE BLOC, PAS SUR LA CELLULE. « display: flex » pose sur un
   <td> le sort du modele de tableau : la cellule cesse d'etre une cellule, la
   ligne perd son alignement et son filet de separation s'arrete avant la
   derniere colonne. C'etait la cause reelle des colonnes qui sautaient d'un
   bloc a l'autre - vu au rendu, pas a la relecture. */
body.admin-sombre .mots-actions { display: flex; flex-wrap: wrap; gap: 4px 12px; }
body.admin-sombre .mots-actions form { margin: 0; }
body.admin-sombre .lien-bouton {
  padding: 0; border: 0; background: none; font: inherit; font-size: 11.5px;
  color: var(--a-accent-txt); cursor: pointer; text-decoration: none;
}
body.admin-sombre .lien-bouton:hover { text-decoration: underline; }
body.admin-sombre .lien-bouton--danger { color: var(--a-err); }
body.admin-sombre .lien-bouton:focus-visible { outline: 2px solid var(--a-accent); outline-offset: 2px; }

/* --- fiche : les quatre zones du controle ---------------------------------
   Une colonne par zone, une coche ou un point. Le point plutot qu'une croix :
   l'absence d'un terme dans une zone n'est pas une faute en soi - seul le
   principal doit etre partout - et quatre croix rouges par ligne feraient
   d'un tableau d'information un tableau d'echecs. */
body.admin-sombre .zone-case { text-align: center; }
body.admin-sombre .zone-oui { color: var(--a-ok); font-weight: 800; }
body.admin-sombre .zone-non { color: var(--a-muet); font-weight: 800; }
/* Ni oui ni non : la page n'a pas repondu. Un point gris dirait « absent », ce
   qui serait faux - on ne sait pas. */
body.admin-sombre .zone-inconnu { color: var(--a-attn); font-weight: 800; cursor: help; }
body.admin-sombre .zone-oui, body.admin-sombre .zone-non { cursor: help; }

/* --- arbitrage ------------------------------------------------------------- */
body.admin-sombre .arb-bloc h2 { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
/* La cible d'une ancre venue de la fiche : sans le decalage, le titre du bloc
   passe sous le bord haut de la fenetre et l'on croit avoir atterri ailleurs. */
body.admin-sombre .arb-bloc { scroll-margin-top: 20px; }

@media (max-width: 900px) {
  /* La colonne d'apercu passe SOUS le formulaire : cote a cote sur un
     telephone, les deux seraient illisibles. L'apercu garde son intérêt en
     dessous - on descend le voir apres avoir ecrit. */
  body.admin-sombre .fiche-deux { grid-template-columns: 1fr; }
  body.admin-sombre .fiche-apercu { position: static; }
  body.admin-sombre .fiche-langues { gap: 8px; }
  body.admin-sombre .fiche-espace { display: none; }

  /* LE PASSAGE EN CARTES VAUT POUR LES DEUX TABLEAUX, et ils sont nommes
     ensemble plutot que la regle recopiee : « .rea-table » est apparue avec les
     realisations et pose exactement le meme probleme - sept colonnes ne tiennent
     pas sur un telephone, et un defilement horizontal cacherait la derniere,
     celle qui porte le bouton. Deux blocs identiques auraient fini par diverger
     sur une marge. */
  body.admin-sombre .mots-table thead,
  body.admin-sombre .rea-table thead { position: absolute; left: -9999px; }
  body.admin-sombre .mots-table,
  body.admin-sombre .mots-table tbody,
  body.admin-sombre .mots-table tr,
  body.admin-sombre .mots-table td,
  body.admin-sombre .rea-table,
  body.admin-sombre .rea-table tbody,
  body.admin-sombre .rea-table tr,
  body.admin-sombre .rea-table td { display: block; width: auto; }
  body.admin-sombre .mots-table tr,
  body.admin-sombre .rea-table tr {
    border: 1px solid var(--a-bord); border-radius: 10px;
    margin-bottom: 10px; padding: 10px 12px;
  }
  body.admin-sombre .mots-table td,
  body.admin-sombre .rea-table td {
    border: 0; padding: 4px 0;
    display: grid; grid-template-columns: 110px 1fr; gap: 10px; align-items: baseline;
  }
  body.admin-sombre .mots-table td::before,
  body.admin-sombre .rea-table td::before { content: attr(data-col); color: var(--a-muet); font-size: 11px; }
  body.admin-sombre .zone-case { text-align: left; width: auto; }
  /* Les actions passent SOUS leur intitule plutot qu'a cote : en colonne de
     droite, six liens dans 200 px se renvoient a la ligne n'importe ou et l'on
     ne voit plus ou commence la suivante. */
  body.admin-sombre .mots-table td.mots-actions {
    display: block; margin-top: 4px; padding-top: 8px; border-top: 1px solid var(--a-bord);
  }
  body.admin-sombre .mots-table td.mots-actions::before { display: block; margin-bottom: 6px; }
}

/* --- choix d'une page pour ses images -------------------------------------
   Des pavés cliquables plutôt qu'une liste déroulante : on voit d'un coup
   quelles pages ont toutes leurs images et lesquelles n'en ont aucune, ce
   qu'un menu fermé ne montre pas. Le compteur vient du dernier relevé - un
   cache, d'où « jamais relevé » quand on n'a pas encore ouvert l'onglet. */
body.admin-sombre .img-choix {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 8px; margin-top: 10px;
}
body.admin-sombre .img-choix-lien {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 11px 14px; border-radius: 10px;
  border: 1px solid var(--a-bord); background: var(--a-panel-2);
  color: var(--a-muet); text-decoration: none; font-size: 13px;
}
body.admin-sombre .img-choix-lien:hover { color: var(--a-txt); border-color: var(--a-muet); }
body.admin-sombre .img-choix-lien b { font-weight: 600; color: var(--a-txt); }
body.admin-sombre .img-choix-lien:hover b { color: var(--a-txt); }

/* --- en-tete de la liste : les filtres a gauche, les relevés a droite -----
   Ce sont les relevés qui remplissent les colonnes « Images » et « Dernier
   contrôle », et qui font passer une page « à vérifier ». Les poser a cote des
   compteurs qu'ils alimentent evite d'aller les chercher ailleurs quand on
   constate qu'un chiffre a vieilli. */
body.admin-sombre .pages-tete {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 18px; align-items: start;
}
body.admin-sombre .pages-tete-gauche { min-width: 0; }
body.admin-sombre .pages-releves { display: flex; flex-direction: column; gap: 6px; }
body.admin-sombre .pages-releves form { margin: 0; }

/* Discrets par construction : on les lance quelques fois par semaine, pas a
   chaque visite. Un bouton plein ici tirerait l'oeil loin des compteurs, qui
   sont ce qu'on vient lire. */
body.admin-sombre .releve-bouton {
  display: flex; flex-direction: column; align-items: flex-end; gap: 1px;
  width: 100%; padding: 7px 12px; border-radius: 9px;
  border: 1px solid transparent; background: none; color: var(--a-muet);
  font: inherit; font-size: 12px; text-align: right; cursor: pointer;
}
body.admin-sombre .releve-bouton:hover {
  color: var(--a-txt); border-color: var(--a-bord); background: var(--a-panel-2);
}
body.admin-sombre .releve-bouton:focus-visible { outline: 2px solid var(--a-accent); outline-offset: 2px; }
body.admin-sombre .releve-bouton b { font-weight: 600; }
body.admin-sombre .releve-bouton i { font-style: normal; font-size: 11px; opacity: .75; }
/* « Lancer tout » est le seul qui engage une minute : il se distingue sans
   crier, par un filet permanent. */
body.admin-sombre .releve-bouton--tout { border-color: var(--a-bord); color: var(--a-txt); }
body.admin-sombre .releve-bouton--tout b { color: var(--a-accent-txt); }

/* --- l'ecran de relevé ---------------------------------------------------- */
body.admin-sombre .releve-carte { max-width: 620px; }
body.admin-sombre .releve-jauge {
  height: 10px; margin: 18px 0 10px; border-radius: 20px;
  background: var(--a-bg); border: 1px solid var(--a-bord); overflow: hidden;
}
body.admin-sombre .releve-jauge span {
  display: block; height: 100%; background: var(--orange);
  /* La transition adoucit le saut d'une etape a l'autre - sans elle, la barre
     clignote a chaque rechargement au lieu d'avancer. */
  transition: width .25s linear;
}
@media (prefers-reduced-motion: reduce) {
  body.admin-sombre .releve-jauge span { transition: none; }
}
body.admin-sombre .releve-compte {
  margin: 0 0 6px; font-size: 13px; color: var(--a-txt);
  font-variant-numeric: tabular-nums;
}
body.admin-sombre .releve-compte b { color: var(--a-txt); font-size: 17px; font-weight: 800; }

/* « À vérifier » : ni vert ni rouge. Ce n'est pas une faute - c'est une page
   qui se disait prete et sur laquelle un relevé a trouve quelque chose. La
   peindre en rouge la confondrait avec « à rédiger », qui est du travail non
   commence. */
body.admin-sombre .etiquette--verifier { color: var(--a-attn); border-color: var(--a-attn); }

@media (max-width: 1100px) {
  /* Les relevés repassent au-dessus des filtres : colles a droite sur une
     largeur moyenne, ils ecraseraient la barre de recherche. */
  body.admin-sombre .pages-tete { grid-template-columns: 1fr; }
  body.admin-sombre .pages-releves {
    flex-direction: row; flex-wrap: wrap;
    border-top: 1px solid var(--a-bord); padding-top: 12px;
  }
  body.admin-sombre .releve-bouton {
    width: auto; align-items: flex-start; text-align: left;
    border-color: var(--a-bord);
  }
}

/* LE PIEGE DU FLEX : un element flex a « min-width: auto », donc il s'elargit
   au contenu au lieu de le laisser deborder. « .tableau-enveloppe » avait beau
   porter « overflow-x: auto », c'est .admin-main qui s'etirait - et la page
   entiere prenait une barre de defilement horizontale des que le tableau
   demandait plus que la place disponible.

   Latent depuis toujours, revele par la colonne « Voir le détail » : entre 900
   et 1100 px, sept colonnes ne tenaient plus. Constate au rendu, a 1000 px. */
body.admin-sombre .admin-main { min-width: 0; }
body.admin-sombre .tableau-enveloppe { max-width: 100%; }

/* =============================================================================
   29. REALISATIONS - les trois ecrans d'administration
   -----------------------------------------------------------------------------
   Tout est prefixe « body.admin-sombre », comme l'exige la regle du depot : les
   feuilles publique et admin sont la meme, et une classe nue ici s'appliquerait
   au site vitrine. Rien n'est repris du public non plus - « .carte » y impose
   un aspect-ratio et un overflow: hidden qui rogneraient ces formulaires, ce
   que la section 28 neutralise deja pour l'admin.
   ========================================================================== */

/* La grille des champs. « auto-fit » et non « auto-fill » : a trois champs sur
   une rangee de quatre places, auto-fill laisserait un vide a droite tandis
   qu'auto-fit etale les trois. Sur un telephone, chaque champ prend la largeur. */
body.admin-sombre .rea-creer-champs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px 18px;
  align-items: end;
}
body.admin-sombre .rea-creer-champs .champ { margin: 0; }
body.admin-sombre .rea-creer-champs label { display: block; margin-bottom: 6px; }

/* Un formulaire qui ne porte qu'un bouton, dans une cellule ou une barre
   d'actions : sans cela, le <form> en bloc casse la ligne avant le bouton. */
body.admin-sombre .rea-inline { display: inline-block; margin: 0; }

/* Les sports, en cases a cocher groupees par univers. Le <fieldset> porte le
   nom de l'univers dans sa <legend> : c'est ce qu'un lecteur d'ecran annonce
   avant chaque case, et « padel » seul ne dit pas de quel univers il releve. */
body.admin-sombre .rea-sports {
  border: 1px solid var(--a-bord);
  border-radius: 10px;
  padding: 12px 14px 14px;
  margin: 0 0 14px;
}
body.admin-sombre .rea-sports legend {
  padding: 0 8px; font-size: 12.5px; letter-spacing: .04em;
  text-transform: uppercase; color: var(--a-muet);
}
body.admin-sombre .rea-sport {
  display: inline-flex; align-items: center; gap: 8px;
  margin: 0 16px 8px 0; font-size: 13.5px; cursor: pointer;
}
body.admin-sombre .rea-sport input { margin: 0; }

/* LES ACTIONS DESTRUCTRICES SONT REPLIEES DANS UN <details>, jamais derriere un
   confirm(). Sans JavaScript, un confirm() ne protege rien du tout ; un bouton
   nu, lui, se clique par erreur. Ouvrir le volet est le geste qui manquait, et
   il fonctionne partout. La bordure rouge dit ce qui attend a l'interieur. */
body.admin-sombre .rea-danger,
body.admin-sombre .rea-ajout {
  border: 1px solid var(--a-bord);
  border-radius: 10px;
  padding: 10px 14px;
  margin-top: 14px;
}
body.admin-sombre .rea-danger { border-color: color-mix(in srgb, var(--a-err) 45%, transparent); }
body.admin-sombre .rea-danger > summary { color: var(--a-err); }
body.admin-sombre .rea-danger > summary,
body.admin-sombre .rea-ajout > summary {
  cursor: pointer; font-size: 13.5px; font-weight: 600;
}
body.admin-sombre .rea-danger[open] > summary,
body.admin-sombre .rea-ajout[open] > summary { margin-bottom: 10px; }

/* Les champs poses dans les cellules du tableau des sports : sans cette remise
   a zero, la marge verticale des .champ ajoute 32 px par ligne et un tableau
   de huit sports devient une page entiere. */
body.admin-sombre .rea-table .champ {
  margin: 0; padding: 6px 8px; font-size: 13px; min-width: 110px;
}

/* Une photo et ses deux textes alternatifs, cote a cote. La vignette est bornee
   en largeur plutot qu'en hauteur : toutes les photos ont le meme rapport
   (3/2, impose a la conversion), donc une largeur fixe donne des lignes de
   hauteur egale sans qu'on ait a la calculer. */
body.admin-sombre .rea-photo {
  display: grid;
  grid-template-columns: minmax(0, 260px) minmax(0, 1fr);
  gap: 20px;
  align-items: start;
}
body.admin-sombre .rea-photo img {
  width: 100%; height: auto; display: block;
  border-radius: 10px; border: 1px solid var(--a-bord);
}
body.admin-sombre .rea-photo-txt > h2 { margin-top: 0; }

@media (max-width: 760px) {
  body.admin-sombre .rea-photo { grid-template-columns: 1fr; }
  body.admin-sombre .rea-photo img { max-width: 320px; }
}

/* =============================================================================
   30. EXPORT DU PLAN DE MOTS-CLES - bas de l'ecran Pages
   -----------------------------------------------------------------------------
   Un bloc replie, reserve au super-administrateur. Tout est prefixe
   « body.admin-sombre » : la feuille est commune au site vitrine, et une classe
   nue s'y appliquerait aussi.

   La zone est en police a chasse fixe parce que l'export est ALIGNE EN
   COLONNES - role, groupe, terme. Dans la police du site, les colonnes se
   decalent d'une ligne a l'autre et le bloc redevient illisible, ce qui lui
   fait perdre sa seule raison d'etre.
   ========================================================================== */
body.admin-sombre .pages-export {
  margin: 26px 0 0;
  padding: 14px 18px;
  border: 1px solid var(--a-bord);
  border-radius: 12px;
  background: var(--a-panel-2);
}
body.admin-sombre .pages-export > summary {
  cursor: pointer;
  font-weight: 600;
  letter-spacing: .01em;
}
body.admin-sombre .pages-export-aide {
  margin: 10px 0 12px;
  max-width: 70ch;
  opacity: .78;
  font-size: .92rem;
}
body.admin-sombre textarea.pages-export-zone {
  width: 100%;
  min-height: 320px;
  resize: vertical;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .82rem;
  line-height: 1.5;
  white-space: pre;
  overflow-wrap: normal;
  overflow-x: auto;
}

/* =============================================================================
   30bis. SOLAR-SHOP - LE VOLET LATERAL ET LA PREMIERE FORME DE LISTE
   -----------------------------------------------------------------------------
   Posee pour les gammes, et destinee aux treize autres ecrans de la boutique.
   Trois morceaux : la barre de l'ecran, le tableau triable et filtrable par ses
   en-tetes, et le volet lateral de saisie.

   TOUT EST SCOPE A body.admin-sombre, comme la section 28 l'impose : les
   feuilles publique et admin sont la meme, et une regle non scopee s'applique
   aussi au site vitrine. « .carte », « .champ » et « .btn » existent des deux
   cotes, et c'est la rencontre des deux qui casse - pas chaque fichier pris a
   part.
   ========================================================================== */

/* --- la barre de l'ecran : ce qu'on lit a gauche, ce qu'on fait a droite ---
   Les actions sont TOUJOURS au meme endroit, y compris quand la liste est
   vide : une action principale qui se deplace selon l'etat de l'ecran se
   cherche a chaque fois. */
body.admin-sombre .shop-barre {
  display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-end;
  justify-content: space-between; margin: 0 0 16px;
}
body.admin-sombre .shop-barre-titre { min-width: 0; flex: 1 1 320px; }
body.admin-sombre .shop-barre-titre h2 {
  margin: 0; font-size: 19px; font-weight: 800; color: var(--a-txt);
}
body.admin-sombre .shop-barre-titre p {
  margin: .35em 0 0; font-size: 12.5px; color: var(--a-muet); max-width: 68ch;
}
body.admin-sombre .shop-barre-titre strong { color: var(--a-txt); font-weight: 600; }
/* flex-end : les boutons s'alignent sur la derniere ligne du titre, pas sur sa
   premiere - sinon ils flottent au-dessus du texte explicatif. */
body.admin-sombre .shop-barre-actions {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
}
/* .btn porte un margin-top de 16px, utile dans un formulaire et de trop ici. */
body.admin-sombre .shop-barre-actions .btn { margin-top: 0; }

/* --- les en-tetes triables ------------------------------------------------
   Le libelle EST le lien : une petite fleche a cote aurait demande de viser.
   La colonne triee est en blanc, les autres restent en muet comme les autres
   en-tetes du tableau - c'est ce qui rend l'etat lisible d'un coup d'oeil. */
body.admin-sombre .shop-tri {
  display: inline-flex; align-items: center; gap: 5px;
  color: inherit; text-decoration: none; white-space: nowrap;
}
body.admin-sombre .shop-tri:hover { color: var(--a-txt); }
body.admin-sombre .shop-tri.actif { color: var(--a-txt); }
body.admin-sombre .shop-tri-sens { color: var(--a-accent-txt); font-size: 9px; font-weight: 400; }

/* --- la ligne de filtres, DANS le <thead> ---------------------------------
   Les champs sont tasses : ils doublent la hauteur de l'en-tete, et cette
   hauteur-la est volee a la liste. Leur libelle existe pour les lecteurs
   d'ecran - un « placeholder » n'est pas un libelle - et il est masque sans
   etre retire de l'arbre d'accessibilite. */
body.admin-sombre .shop-vide {
  padding: 22px 12px; text-align: center; color: var(--a-muet); font-style: italic;
}
body.admin-sombre .shop-note { color: var(--a-muet); font-size: 11.5px; font-style: normal; }
body.admin-sombre .shop-actions { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }

/* --- la vignette de la colonne Image -------------------------------------
   Carree et petite : la colonne sert a reconnaitre la ligne, pas a juger le
   cadrage. La reserve garde la meme boite, sans quoi les lignes sautent d'une
   hauteur a l'autre selon qu'un visuel est depose ou non. */
body.admin-sombre .shop-vignette {
  display: block; width: 52px; height: 52px; object-fit: cover;
  border-radius: 8px; border: 1px solid var(--a-bord); background: var(--a-bg);
}

/* --- le volet lateral ------------------------------------------------------
   IL ENTRE PAR LA DROITE EN CSS, a l'affichage de la page. Le serveur le rend
   quand l'adresse le demande (?nouveau, ?modifier) : il n'y a donc rien a
   animer en JavaScript, et rien ne depend d'un script qui n'aurait pas charge.
   Sans script, le volet s'ouvre, se remplit, s'enregistre et se ferme.

   LE FOND EST UN LIEN, pas un calque mort : cliquer a cote ferme le volet, ce
   qui est le geste attendu, et un lien se clique sans script. Il est APRES le
   volet dans le HTML - donc apres lui dans l'ordre de tabulation - et
   en-dessous par le z-index. */
body.admin-sombre .volet {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 60;
  width: min(520px, 100%); overflow-y: auto; overscroll-behavior: contain;
  background: var(--a-panel); border-left: 1px solid var(--a-bord);
  box-shadow: -18px 0 46px rgba(0, 0, 0, .45);
  animation: volet-entre .22s ease-out both;
}
@keyframes volet-entre { from { transform: translateX(100%); } to { transform: none; } }
/* Une animation qui entre par le cote est exactement ce qu'une preference de
   mouvement reduit demande d'eviter. Le volet s'affiche alors d'un coup - il
   est au meme endroit, il n'y arrive pas en glissant. */
@media (prefers-reduced-motion: reduce) {
  body.admin-sombre .volet { animation: none; }
}
body.admin-sombre .volet-fond {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(8, 11, 20, .62);
  /* Le libelle existe pour les lecteurs d'ecran et ne doit pas s'ecrire en
     travers de la page : on le sort du cadre plutot que de le masquer, pour
     qu'il reste annonce. */
  text-indent: -9999px; overflow: hidden; white-space: nowrap;
}
/* sticky : la tete reste visible quand le volet defile. Les deux boutons sont
   l'action de l'ecran - les chercher en remontant serait absurde. */
body.admin-sombre .volet-tete {
  position: sticky; top: 0; z-index: 2;
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
  justify-content: space-between;
  padding: 14px 18px; background: var(--a-panel-2);
  border-bottom: 1px solid var(--a-bord);
}
body.admin-sombre .volet-tete h2 {
  margin: 0; font-size: 16px; font-weight: 800; color: var(--a-txt);
  min-width: 0; overflow-wrap: anywhere;
}
body.admin-sombre .volet-actions { display: flex; gap: 8px; align-items: center; }
body.admin-sombre .volet-actions .btn { margin-top: 0; }
body.admin-sombre .volet-corps { padding: 4px 18px 18px; }
body.admin-sombre .volet-corps > label:first-child { margin-top: 10px; }
body.admin-sombre .volet-depot { border-top: 1px dashed var(--a-bord); margin-top: 14px; }
/* Dans un volet de 520 px, la vignette de 300 px de .img-ligne ne laisse plus
   rien au texte : on repasse en une colonne, comme le fait deja le telephone. */
body.admin-sombre .volet .img-ligne { grid-template-columns: 1fr; gap: 12px; }
body.admin-sombre .volet .img-vue { width: 100%; }
body.admin-sombre .volet .img-vue img { height: auto; }

@media (max-width: 720px) {
  body.admin-sombre .shop-barre { align-items: stretch; }
  body.admin-sombre .shop-barre-actions { justify-content: flex-start; }
  body.admin-sombre .volet { width: 100%; border-left: 0; }
}

/* --- le champ fige, l'obligatoire, et les trois etats d'une aide ----------
   Poses pour le code d'un referentiel, et destines a tous les champs engendres
   par le serveur que les ecrans suivants porteront. */

/* UN CHAMP EN LECTURE SEULE DOIT SE VOIR SANS SE LIRE COMME DESACTIVE : il
   porte une vraie valeur, qu'on veut pouvoir selectionner et copier. D'ou le
   fond de carte plutot que le fond de champ, la bordure en pointille, et le
   curseur par defaut - mais pas d'opacite reduite, qui le rendrait moins
   lisible que le reste alors qu'il dit quelque chose d'important. */
body.admin-sombre input[readonly].champ-fige {
  background: var(--a-panel-2); border-style: dashed; color: var(--a-txt);
  cursor: default; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
body.admin-sombre input[readonly].champ-fige:focus { outline: none; border-color: var(--a-bord-champ); }

/* L'asterisque d'un champ obligatoire. Elle est en aria-hidden : le « required »
   de l'input porte deja l'information aux lecteurs d'ecran, et une etoile lue a
   voix haute n'apprend rien. */
body.admin-sombre .req { color: var(--a-accent-txt); font-weight: 700; }

/* Les trois etats d'un texte d'aide. Ils ne reposent PAS sur la seule couleur :
   le texte change, et c'est lui qui porte le sens - une difference de teinte ne
   se voit pas pour tout le monde. La couleur ne fait que la souligner. */

/* Les huit ecrans vides de la boutique n'ont PLUS DE CLASSE A EUX. Ils ont
   porte un encart « .ecran-vide » qui expliquait pourquoi la page ne montrait
   rien ; la cliente l'a fait retirer, et la regle du depot veut qu'une classe
   que plus aucune page n'emet quitte la feuille - comme les .hub* et les
   .univers-* avant elle. Ces ecrans ne rendent desormais que l'ossature, qui
   est deja stylee. */

/* =============================================================================
   31. SOLAR-SHOP - LA FORME ERP : DENSE, TABULAIRE, SANS DECORATION
   -----------------------------------------------------------------------------
   POSEE SUR DEUX MAQUETTES, et c'est leur densite qui est le sujet. Un ecran de
   gestion de catalogue se juge au nombre de lignes qu'on lit sans defiler et au
   nombre de gestes qu'il faut pour corriger un prix - pas a son aeration. Les
   valeurs d'ici sont donc plus serrees que celles de la section 28 : 12,5 px de
   texte dans les tableaux, 7 px de hauteur de cellule, des champs de 30 px.

   ON NE TOUCHE PAS A LA CHARTE. Les memes jetons qu'ailleurs - --a-panel,
   --a-bord, --a-accent, --a-ok, --a-err, --a-attn -, le meme orange, le meme
   bleu-noir. Rien n'est emprunte a l'ERP qui a servi de reference : c'est sa
   DISPOSITION qu'on reprend, pas ses couleurs.

   TOUT EST SCOPE A body.admin-sombre, comme la section 28 l'impose : les
   feuilles publique et admin sont la meme, et une regle non scopee s'applique
   aussi au site vitrine.

   DEUX FAMILLES, et elles se suffisent : « .ecran-* » pour une liste, et
   « .fiche-* » pour un formulaire en page. Les volets lateraux de la section 30
   restent en place - ils servent encore aux ecrans qui n'ont pas de fiche a eux.
   ========================================================================== */

/* --- le fil d'Ariane, dans la barre du haut --------------------------------
   IL REMPLACE LE TITRE REPETE. La barre du haut portait le meme texte que le
   titre de l'ecran, trois centimetres plus haut : deux fois la meme information
   et aucune reponse a « ou suis-je ». Le fil la donne, et il est cliquable. */
body.admin-sombre .fil {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  font-size: 13px; color: var(--a-sombre-muet); min-width: 0;
}
body.admin-sombre .fil a { color: var(--a-sombre-muet); text-decoration: none; }
body.admin-sombre .fil a:hover { color: #fff; text-decoration: underline; }
body.admin-sombre .fil b { color: var(--a-sombre-txt); font-weight: 600; }
body.admin-sombre .fil-sep { color: var(--a-sombre-muet); }

/* --- l'en-tete d'un ecran de liste ----------------------------------------
   Titre, compte, et les actions TOUJOURS en haut a droite - y compris quand la
   liste est vide : une action principale qui se deplace selon l'etat se cherche
   a chaque fois. */
body.admin-sombre .ecran-tete {
  display: flex; flex-wrap: wrap; gap: 12px 16px;
  align-items: flex-end; justify-content: space-between; margin: 0 0 14px;
}
body.admin-sombre .ecran-tete-txt { min-width: 0; flex: 1 1 340px; }
body.admin-sombre .ecran-tete h2 {
  margin: 0; font-size: 24px; font-weight: 800; color: var(--a-txt); letter-spacing: -.01em;
}
body.admin-sombre .ecran-tete p {
  margin: 4px 0 0; font-size: 13px; color: var(--a-muet);
}
body.admin-sombre .ecran-tete p b { color: var(--a-txt); font-weight: 600; }
body.admin-sombre .ecran-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
body.admin-sombre .ecran-actions .btn,
body.admin-sombre .ecran-actions .bouton { margin-top: 0; }

/* --- la barre de filtres : UNE SEULE LIGNE --------------------------------
   ELLE A QUITTE L'EN-TETE DU TABLEAU. Les filtres vivaient dans une seconde
   ligne de <thead>, un champ par colonne : c'etait aligne, et cela volait a la
   liste la hauteur de deux lignes de donnees sur un ecran qui en montre dix.
   Au-dessus du tableau, sur une ligne, ils tiennent en 46 px et se lisent de
   gauche a droite comme une phrase - « chercher ceci, dans cette gamme, de
   cette marque, dans cet etat ».

   C'EST UN FORMULAIRE EN GET qui enveloppe la barre, pas le tableau : un
   resultat filtre se met en favori et se partage. Les tris et la page voyagent
   en champs caches. */
/* MEME GESTE SUR LA BARRE DE FILTRES : ni marge dessous, ni filet du bas. Elle
   touche le tableau qu'elle filtre, et les deux se lisent comme un seul bloc -
   les 12 px et le trait les separaient en deux objets sans rapport. Les trois
   autres cotes du filet restent : c'est lui qui tient la barre ensemble. */
body.admin-sombre .filtres {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  padding: 10px; margin: 0;
  background: var(--a-panel); border: 1px solid var(--a-bord); border-radius: 0;
  border-bottom: 0;
}
/* ANGLES DROITS, COMME TOUT LE RESTE DE L'ADMINISTRATION. La section 33 pose
   deja « border-radius: 0 » sur « select » et sur « input », mais ces deux
   regles-ci sont PLUS SPECIFIQUES - trois classes contre une - et gagnaient
   donc, quelle que soit leur place dans le fichier. Les listes deroulantes de
   la barre restaient seules arrondies au milieu d'un ecran a angles droits.
   C'est le genre d'ecart qu'on ne voit pas en relisant la section 33, puisque
   la regle qui l'annule est trois cents lignes plus haut. */
body.admin-sombre .filtres .champ,
body.admin-sombre .filtres select.champ {
  margin: 0; padding: 7px 10px; font-size: 13px; height: 34px; border-radius: 0;
}
/* UNE SEULE HAUTEUR POUR TOUTE LA LIGNE, et c'est la demande : les listes
   deroulantes faisaient 34 px, les boutons 24 - trois controles de meme rang,
   posees cote a cote, qui ne s'alignaient sur rien. La hauteur est imposee ICI
   et non sur « .bouton », qui sert cent fois ailleurs dans l'administration :
   une barre de filtres n'est pas une raison de grossir tous les boutons du
   depot. « padding-block: 0 » laisse la hauteur commander, sinon le
   rembourrage de la section 33 s'y ajoute. */
body.admin-sombre .filtres .bouton,
body.admin-sombre .filtres .btn {
  height: 34px; margin-top: 0; padding-block: 0;
}
/* La recherche prend ce qui reste ; les selects gardent une largeur lisible
   sans s'etirer - un « Toutes les gammes » large de six cents pixels se lit
   moins bien, pas mieux. */
body.admin-sombre .filtres-q { flex: 1 1 260px; min-width: 180px; }
body.admin-sombre .filtres-sel { flex: 0 1 190px; min-width: 150px; }
body.admin-sombre .filtres-fin {
  margin-left: auto; display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
}
/* LES TROIS ACTIONS SE LISENT COMME UN JEU, donc elles ont la meme largeur.
   « Exporter » fait huit caracteres et « Nouveau produit » quinze : a largeur
   libre, le plus important des trois paraissait simplement le plus long. La
   largeur minimale les egalise par le bas - le plus large commande - et
   « center » met les libelles d'aplomb. C'est la demande : « de meme taille ».

   ELLES RESTENT DANS LE FLUX DE LA BARRE, dans un <span> et non un bloc : la
   barre se replie deja toute seule sous 1 200 px, et un conteneur a part aurait
   ajoute un point de rupture de plus a accorder avec elle. */
body.admin-sombre .filtres-actions {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
}
body.admin-sombre .filtres-actions .bouton {
  min-width: 148px; justify-content: center;
}
/* Le bouton « Filtrer » ne parait que lorsque le script ne peut pas s'en
   charger : voir assets/js/admin.js. C'est la seule regle de la feuille qui
   depende d'une classe posee par du script, et elle va dans le bon sens -
   sans script, le bouton reste. */
body.admin-sombre .filtres--vif .filtres-envoi { display: none; }
body.admin-sombre .filtres-fin .bouton svg { width: 14px; height: 14px; }
/* CACHE A L'OEIL, PAS AU LECTEUR D'ECRAN. Deux noms, une seule regle : les
   libelles de la barre de filtres et celui de la recherche du haut ont le meme
   besoin, et deux declarations identiques finiraient par diverger. */
body.admin-sombre .filtres-lib,
body.admin-sombre .lecteur-seul {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* --- LES CLASSES QUI REMPLACENT DES STYLES EN LIGNE ------------------------
   LA FICHE PRODUIT EN PORTAIT NEUF, et un style en ligne est exactement le
   « correctif en cascade » qu'on refuse : il gagne sur toute la feuille, ne se
   retrouve pas en cherchant une classe, et se recopie a la ligne suivante.
   Ils deviennent donc des classes - les memes valeurs, a un endroit qu'on
   relit. Aucune apparence ne change.

   LA FUSION A FAILLI PASSER INAPERCUE : cinq « textarea » portaient deja
   « class="champ" », et poser un second attribut « class » aurait donne un
   balisage ou le navigateur garde le PREMIER et ignore le second, en silence.
   Les cinq ont ete fusionnes, et le controle de doublons est refait. */
body.admin-sombre .carte--nu { padding: 0; }
body.admin-sombre .champ--court { min-height: 70px; }
body.admin-sombre .inst-etat--nu { margin: 0; }
body.admin-sombre .cote-titre { font-size: 14px; }
body.admin-sombre .cote-note { margin-top: 10px; }
body.admin-sombre .form-nu { margin: 0; }
body.admin-sombre .repli--separe {
  border-top: 1px solid var(--a-bord); padding-top: 12px; margin-top: 14px;
}

/* --- LA COLONNE DE SELECTION, LA PASTILLE-BOUTON ET LA BARRE DE LOT --------
   Trois ajouts que la maquette impose, et qui partagent une regle : aucun ne
   doit RESSEMBLER a un bouton de plus. La case est une case, la pastille garde
   l'apparence d'une pastille, le nombre celle d'un nombre.
   -------------------------------------------------------------------------- */
body.admin-sombre .cell-case { width: 34px; padding-right: 0; text-align: center; }
body.admin-sombre .cell-case input[type="checkbox"] {
  width: 15px; height: 15px; margin: 0; vertical-align: middle;
  accent-color: var(--a-accent); cursor: pointer;
}

/* Le formulaire d'une pastille ou d'un nombre ne doit occuper AUCUNE place :
   sans cela il casserait la ligne du tableau en bloc. */
body.admin-sombre .etat-form { display: inline; margin: 0; padding: 0; border: 0; }

/* Le nombre de declinaisons : un lien, qu'il mene a une liste ou qu'il poste. */
body.admin-sombre .cell-lien {
  background: none; border: 0; padding: 0; font: inherit;
  color: var(--a-accent-txt); text-decoration: none; cursor: pointer;
}
body.admin-sombre .cell-lien:hover { text-decoration: underline; }

/* LA BARRE DE LOT. Le serveur la rend toujours ; le script lui pose
   « lot-barre--auto » et la masque tant que rien n'est coche, si bien que la
   page au repos est celle de la maquette. Sans script elle reste visible. */
body.admin-sombre .lot-barre {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin: 10px 0 0; padding: 10px 12px;
  background: var(--a-panel); border: 1px solid var(--a-bord); border-radius: 10px;
}
body.admin-sombre .lot-barre--auto { display: none; }
body.admin-sombre .lot-barre--auto.lot-barre--ouverte { display: flex; }
body.admin-sombre .lot-compte { font-size: 12.5px; color: var(--a-muet); margin-right: auto; }
body.admin-sombre .lot-barre--ouverte .lot-compte { color: var(--a-txt); font-weight: 600; }

/* --- le tableau dense ------------------------------------------------------
   .donnees existe deja (section 28) et sert partout ; on ne le redefinit pas,
   on le resserre quand il porte .donnees--dense. Une classe en plus plutot
   qu'une valeur changee : les tableaux des autres ecrans gardent leur hauteur,
   et rien ne bouge ailleurs sans qu'on l'ait demande. */
body.admin-sombre .donnees--dense { font-size: 12.5px; }
body.admin-sombre .donnees--dense th,
body.admin-sombre .donnees--dense td { padding: 7px 10px; vertical-align: middle; }
body.admin-sombre .donnees--dense thead th {
  font-size: 11.5px; font-weight: 700; letter-spacing: .02em;
  color: var(--a-muet); white-space: nowrap;
}
body.admin-sombre .donnees--dense tbody tr:hover { background: var(--a-panel-2); }
/* Le nom porte une seconde ligne - la reference fabricant - qui n'a pas besoin
   d'une colonne a elle : elle ne se trie pas et ne se lit qu'en passant. */
body.admin-sombre .cell-principal { color: var(--a-txt); font-weight: 600; }
body.admin-sombre .cell-sous { display: block; font-size: 11.5px; color: var(--a-muet); margin-top: 1px; }
body.admin-sombre .cell-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px; color: var(--a-muet); white-space: nowrap;
}
body.admin-sombre .cell-nombre { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
body.admin-sombre .cell-date { white-space: nowrap; color: var(--a-muet); font-size: 11.5px; }
body.admin-sombre .cell-serre { width: 1%; white-space: nowrap; }

/* La vignette de liste, plus petite que celle de la section 30 : la colonne
   sert a reconnaitre la ligne, pas a juger le cadrage. La reserve garde la
   meme boite, sans quoi les lignes sautent d'une hauteur a l'autre. */
body.admin-sombre .vignette {
  display: block; width: 44px; height: 44px; object-fit: cover;
  border-radius: 6px; border: 1px solid var(--a-bord); background: var(--a-bg);
}
body.admin-sombre .vignette--vide {
  display: flex; align-items: center; justify-content: center;
  border-style: dashed; color: var(--a-muet); font-size: 10px;
}

/* --- les pastilles d'etat --------------------------------------------------
   ELLES NE REPOSENT PAS SUR LA SEULE COULEUR : le mot est ecrit en toutes
   lettres, et la teinte ne fait que le souligner. Une difference de couleur ne
   se voit pas pour tout le monde, et « vert » ne dit pas « actif ». */
body.admin-sombre .etat {
  display: inline-block; padding: 3px 9px; border-radius: 999px;
  font-size: 11px; font-weight: 700; line-height: 1.5; white-space: nowrap;
  border: 1px solid transparent;
}
/* LE TEXTE D'UNE PASTILLE EST BLANC, ET C'EST LE THEME CLAIR QUI L'IMPOSE.
   En sombre les trois fonds etaient vifs et portaient un texte presque noir ;
   ils sont desormais assez fonces pour se lire sur du blanc, donc trop pour
   porter du noir. Le rapport etant symetrique, les memes valeurs donnent
   5.39, 5.37 et 5.35 avec du blanc dessus - c'est ce que montre la maquette.
   « Archive » reste la seule pastille vide : elle dit une absence. */
body.admin-sombre .etat--actif     { color: #fff; background: var(--a-ok);     border-color: var(--a-ok);     }
body.admin-sombre .etat--brouillon { color: #fff; background: var(--a-neutre); border-color: var(--a-neutre); }
body.admin-sombre .etat--inactif   { color: #fff; background: var(--a-err);    border-color: var(--a-err);    }
body.admin-sombre .etat--archive   { color: var(--a-muet); background: transparent; border-color: var(--a-bord); }
body.admin-sombre .etat--info      { color: #fff; background: var(--a-attn);   border-color: var(--a-attn);   }

/* --- les boutons d'action en bout de ligne --------------------------------
   Carres, bordes, et NOMMES : le titre et le libelle pour lecteur d'ecran
   disent ce que fait l'icone. Une icone seule sans nom est une devinette, et
   trois devinettes cote a cote en font trois. */
body.admin-sombre .ico-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 28px; padding: 0; border-radius: 6px;
  border: 1px solid var(--a-bord); background: var(--a-panel-2);
  color: var(--a-muet); cursor: pointer; text-decoration: none;
  transition: color .12s, border-color .12s, background .12s;
}
body.admin-sombre .ico-btn:hover { color: var(--a-txt); border-color: var(--a-bord-champ); background: var(--a-panel); }
body.admin-sombre .ico-btn:focus-visible { outline: 2px solid var(--a-accent); outline-offset: 1px; }
body.admin-sombre .ico-btn svg { width: 14px; height: 14px; }
body.admin-sombre .ico-btn--danger { color: var(--a-err); }
body.admin-sombre .ico-btn--danger:hover { color: #fff; background: var(--a-err); border-color: var(--a-err); }
/* Un bouton d'action est souvent dans son propre <form> (POST + jeton CSRF) :
   sans cela, trois formulaires cote a cote se rangeraient en colonne. */
body.admin-sombre .ligne-actions { display: flex; gap: 5px; justify-content: flex-end; }
body.admin-sombre .ligne-actions form { display: inline; margin: 0; }

/* --- le pied de liste : le compte a gauche, les pages a droite ------------- */
body.admin-sombre .liste-pied {
  display: flex; flex-wrap: wrap; gap: 10px 16px;
  align-items: center; justify-content: space-between; margin-top: 12px;
}
body.admin-sombre .liste-compte { font-size: 12.5px; color: var(--a-muet); margin: 0; }
body.admin-sombre .liste-pied-fin {
  display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center; margin-left: auto;
}
body.admin-sombre .liste-pied .pagination { margin-top: 0; }

/* --- « 50 par page », en bas a droite --------------------------------------
   IL PORTE LA CLASSE « .filtres » POUR LA MECANIQUE, PAS POUR L'HABILLAGE.
   assets/js/admin.js soumet au changement tout formulaire qui la porte et remet
   « p » a vide : reutiliser ce crochet evite une seconde implementation du meme
   geste, et c'est la regle du depot. Mais la barre du haut est un PANNEAU -
   fond, filet, dix pixels de rembourrage, douze de marge -, et cette poignee de
   deux champs n'en est pas un. On defait donc l'habillage, et lui seul.

   LE BOUTON « OK » EST LE MEME REPLI QUE « FILTRER » : rendu par le serveur,
   efface par le script (.filtres--vif .filtres-envoi). Sans JavaScript, changer
   un « select » ne soumet rien - le retirer du balisage rendrait le choix
   inutilisable, ce qu'interdit la regle 5. */
body.admin-sombre .liste-pp {
  padding: 0; margin: 0; background: none; border: 0; width: auto; gap: 6px;
}
body.admin-sombre .liste-pp .champ { min-width: 118px; }
body.admin-sombre .pagination-lien[aria-disabled="true"] { opacity: .4; cursor: default; }
body.admin-sombre .pagination-saut { padding: 4px 6px; color: var(--a-muet); }

/* =============================================================================
   LA FICHE - un formulaire en PAGE, plus dans un volet
   -----------------------------------------------------------------------------
   UN VOLET DE 520 PX NE TIENT PAS QUARANTE CHAMPS. Il convenait a une gamme,
   qui en a huit. Pour un produit, il obligeait a defiler dans une colonne
   etroite, et les deux boutons d'enregistrement se perdaient en haut. La fiche
   est donc une page : deux colonnes, des sections, et une barre d'action qui
   COLLE EN HAUT - c'est la demande, et c'est ce qui permet d'enregistrer depuis
   n'importe quel point du formulaire.
   ========================================================================== */

/* « .erp-tete » ET NON « .fiche-tete », ET C'EST LA CORRECTION D'UNE REGRESSION.
   La refonte ERP avait repris « .fiche-tete », un nom que la SECTION 28 possede
   deja : elle le pose sur l'en-tete de la fiche d'une page (solar-admin/page.php)
   ou il ne vaut qu'un « padding-bottom: 0 » a l'interieur d'une carte. La
   section 31 venant apres, c'est elle qui gagnait - et l'ecran Pages recevait
   donc, sans que personne ne l'ait demande, une barre collante avec son fond,
   son filet et son rembourrage asymetrique.

   LE DEFAUT ETAIT EN PRODUCTION. Il ne se corrige pas par une exception rendue
   a la section 28 - une regle de plus qui lutte contre une autre, et le prochain
   qui lit ne sait plus laquelle commande. Il se corrige en RENDANT SON NOM a
   chaque composant : l'en-tete d'une fiche de page et la barre d'action d'un
   formulaire ERP ne sont pas la meme chose et ne doivent pas s'appeler pareil.

   SEUL CE NOM-LA SE RECOUVRAIT : « .fiche-actions », « .fiche-cote »,
   « .fiche-grille », « .fiche-modif » et « .fiche-sous » n'existent que dans
   cette section, et les renommer aurait ete du bruit sans defaut derriere. */
body.admin-sombre .erp-tete {
  position: sticky; top: 0; z-index: 20;
  display: flex; flex-wrap: wrap; gap: 12px 16px;
  align-items: flex-start; justify-content: space-between;
  padding: 12px 0 12px 14px; margin: 0 0 14px;
  background: var(--a-bg); border-bottom: 1px solid var(--a-bord);
}
/* Le filet orange a gauche du titre : il tient lieu de reperage de section et
   remplace une icone, qui n'aurait rien dit de plus. */
body.admin-sombre .erp-tete::before {
  content: ""; position: absolute; left: 0; top: 12px; bottom: 12px;
  width: 4px; border-radius: 2px; background: var(--a-accent);
}
body.admin-sombre .erp-tete-txt { min-width: 0; flex: 1 1 320px; }
body.admin-sombre .erp-tete h2 {
  margin: 0; font-size: 23px; font-weight: 800; color: var(--a-txt);
  line-height: 1.2; overflow-wrap: anywhere;
}
body.admin-sombre .fiche-sous {
  display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center;
  margin: 6px 0 0; font-size: 12.5px; color: var(--a-muet);
}
body.admin-sombre .fiche-sous code {
  font-size: 12.5px; color: var(--a-txt); background: none; padding: 0;
}
body.admin-sombre .fiche-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
body.admin-sombre .fiche-actions .btn,
body.admin-sombre .fiche-actions .bouton { margin-top: 0; }

/* L'indicateur de modifications. Il est POSE PAR LE SERVEUR en [hidden] et
   decouvert par le script : sans JavaScript il ne parait jamais, ce qui est le
   comportement juste - sans script, rien ne peut detecter une frappe, et un
   avertissement permanent serait un mensonge permanent. */
body.admin-sombre .fiche-modif {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 4px 11px; border-radius: 999px;
  border: 1px solid var(--a-attn); color: var(--a-attn);
  font-size: 12px; font-weight: 600; white-space: nowrap;
}
body.admin-sombre .fiche-modif::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--a-attn);
}
body.admin-sombre .fiche-modif[hidden] { display: none; }

/* --- les onglets de la fiche ----------------------------------------------
   LA VUE PASSE PAR L'ADRESSE, sans une ligne de JavaScript : elle se partage,
   se met en favori, et survit a un script qui ne charge pas. C'est la meme
   regle que admin_vues() depuis le debut ; seule la forme change ici. */
/* --- LA BARRE D'UNE FICHE : LES ONGLETS A GAUCHE, LES BOUTONS A DROITE -----
   UNE SEULE LIGNE, demandee le 20 octobre. Il y en avait deux : un bandeau de
   titre puis la rangee d'onglets. Le titre y etait ecrit pour la troisieme fois
   de la page - fil d'Ariane, onglet du navigateur, bandeau - et la ligne gagnee
   est une ligne de formulaire de plus a l'ecran.

   ELLE RESTE COLLANTE, et c'est elle qui porte desormais le fond : sans lui, le
   contenu defilerait VISIBLEMENT derriere des onglets transparents. */
/* LA BARRE EST BLANCHE ET COLLEE A SON CONTENU (21 octobre, retouche fournie
   telle quelle). Elle portait le gris de la page et un filet, plus 14 px de
   marge : la rangee d'onglets flottait donc au-dessus du panneau qu'elle
   commande au lieu d'en faire partie. Fond blanc, aucune marge, aucun filet -
   la languette ouverte se confond avec le panneau, ce que la metaphore du
   dossier demandait depuis le debut.

   « var(--a-panel) » ET NON « #fff » ECRIT A LA MAIN : le jeton vaut
   exactement #FFFFFF, et c'est lui que lit la carte des surfaces de
   bin/verifier-contrastes.php. Une couleur ecrite en clair ici aurait fait
   mesurer le contraste des onglets sur le gris de la page, qu'ils n'ont plus. */
body.admin-sombre .fiche-barre {
  position: sticky; top: 0; z-index: 20;
  display: flex; flex-wrap: wrap; gap: 8px 16px;
  align-items: flex-end; justify-content: space-between;
  margin: 0; padding-top: 6px;
  background: var(--a-panel); border-bottom: 0;
}
body.admin-sombre .fiche-barre .onglets { margin: 0; flex: 1 1 auto; }
body.admin-sombre .fiche-barre .fiche-actions { padding-bottom: 7px; }

/* --- LES ONGLETS EN FORME DE DOSSIER ---------------------------------------
   LA MAQUETTE DU 20 OCTOBRE les montre ainsi : des languettes posees sur un
   filet, celle qui est ouverte se confondant avec le contenu en dessous. C'est
   une metaphore qui se lit sans legende - on voit QUEL onglet est ouvert, et
   non seulement lequel est colore.

   L'ONGLET COURANT N'EST PLUS UN APLAT D'ACCENT, et ce n'est pas qu'une
   question de gout : du blanc sur l'orange de la marque etait l'un des quatre
   ecarts de contraste que ce depot tolerait. Il porte maintenant la couleur du
   texte sur le fond des cartes, et son reperage vient de sa POSITION - collee
   au contenu, filet du bas efface - plus un trait d'accent en haut.

   ANGLES DROITS, alors que la maquette arrondit les coins superieurs : c'est la
   section 33 qui l'impose pour toute l'administration, sur demande ecrite. Y
   faire une exception aurait donne le seul objet arrondi de l'ecran. */
body.admin-sombre .onglets {
  display: flex; flex-wrap: wrap; gap: 0; margin: 0 0 14px;
  align-items: flex-end;
}
body.admin-sombre .onglets a,
body.admin-sombre .onglets label {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 14px; margin: 0 4px -1px 0; text-decoration: none;
  border: 1px solid var(--a-bord); border-bottom-color: transparent;
  background: var(--a-panel-2);
  color: var(--a-muet); font-size: 13px; font-weight: 600; white-space: nowrap;
}
body.admin-sombre .onglets a:hover,
body.admin-sombre .onglets label:hover {
  color: var(--a-txt); background: var(--a-panel);
}
body.admin-sombre .onglets label { cursor: pointer; }
/* Le filet du bas EFFACE, et c'est ce qui fait la languette ouverte : elle se
   confond avec la carte du dessous au lieu d'etre posee dessus. */
body.admin-sombre .onglets a.actif {
  background: var(--a-panel); color: var(--a-txt); font-weight: 700;
  border-bottom-color: var(--a-panel);
  box-shadow: inset 0 2px 0 var(--a-accent);
}
/* --- LES ONGLETS QUI NE SONT QUE DE L'AFFICHAGE ----------------------------
   LA FICHE D'UN PRODUIT N'A QU'UN FORMULAIRE depuis le 20 octobre : les sept
   panneaux sont rendus ensemble et l'onglet ne choisit que celui qu'on voit. Un
   LIEN aurait perdu la saisie en changeant d'onglet - une requete GET n'emporte
   rien de ce qui est tape -, d'ou des boutons radio caches et des <label>.

   SANS UNE LIGNE DE JAVASCRIPT, et c'est la regle 5. Le selecteur « ~ » lit un
   FRERE QUI SUIT : les radios sont donc posees avant la barre et avant les
   panneaux, et c'est pour cela qu'admin_onglets() et admin_onglets_barre() sont
   deux fonctions.

   « display: none » ET NON UNE HAUTEUR NULLE : un panneau cache ne doit pas
   etre atteignable au clavier. Ses champs restent dans le formulaire et
   partent avec l'envoi - c'est tout l'interet -, mais on ne tabule pas dedans.

   LA RADIO EST SORTIE DE L'ECRAN, pas « display: none » : une radio cachee de
   cette facon n'est plus focalisable, et la barre d'onglets cesserait d'etre
   utilisable au clavier. Ainsi posee, elle reste atteignable par Tab et les
   fleches, et le <label> la coche a la souris. */
body.admin-sombre .vue-radio {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
body.admin-sombre .vue-panneau { display: none; }
body.admin-sombre .vue-panneau--suite { margin-top: 14px; }

/* Un panneau parait quand sa radio est cochee. Sept regles, une par onglet :
   un selecteur generique aurait demande que le panneau connaisse le code de sa
   radio, ce que le CSS ne sait pas faire. */
body.admin-sombre #vue-informations:checked     ~ [data-vue="informations"],
body.admin-sombre #vue-description-fr:checked   ~ [data-vue="description-fr"],
body.admin-sombre #vue-description-en:checked   ~ [data-vue="description-en"],
body.admin-sombre #vue-caracteristiques:checked ~ [data-vue="caracteristiques"],
body.admin-sombre #vue-declinaisons:checked     ~ [data-vue="declinaisons"],
body.admin-sombre #vue-images:checked           ~ [data-vue="images"],
body.admin-sombre #vue-documents:checked        ~ [data-vue="documents"],
/* LES DEUX ONGLETS DU CLASSEUR. Ils reprennent la mecanique de la fiche sans
   rien y ajouter : deux codes de plus dans la liste, et deux dans celle des
   languettes. C'est le prix de l'enumeration explicite - un selecteur generique
   demanderait que le panneau connaisse le code de sa radio, ce que le CSS ne
   sait pas faire -, et il se paie une ligne a la fois. */
body.admin-sombre #vue-depot:checked            ~ [data-vue="depot"],
body.admin-sombre #vue-infos:checked            ~ [data-vue="infos"] { display: block; }

/* La languette de l'onglet ouvert, marquee comme l'etait « .actif ». */
body.admin-sombre #vue-informations:checked     ~ .fiche-barre .onglet-informations,
body.admin-sombre #vue-description-fr:checked   ~ .fiche-barre .onglet-description-fr,
body.admin-sombre #vue-description-en:checked   ~ .fiche-barre .onglet-description-en,
body.admin-sombre #vue-caracteristiques:checked ~ .fiche-barre .onglet-caracteristiques,
body.admin-sombre #vue-declinaisons:checked     ~ .fiche-barre .onglet-declinaisons,
body.admin-sombre #vue-images:checked           ~ .fiche-barre .onglet-images,
body.admin-sombre #vue-documents:checked        ~ .fiche-barre .onglet-documents,
body.admin-sombre #vue-depot:checked            ~ .fiche-barre .onglet-depot,
body.admin-sombre #vue-infos:checked            ~ .fiche-barre .onglet-infos {
  background: var(--a-panel); color: var(--a-txt); font-weight: 700;
  border-bottom-color: var(--a-panel);
  box-shadow: inset 0 2px 0 var(--a-accent);
}
body.admin-sombre #vue-informations:checked     ~ .fiche-barre .onglet-informations svg,
body.admin-sombre #vue-description-fr:checked   ~ .fiche-barre .onglet-description-fr svg,
body.admin-sombre #vue-description-en:checked   ~ .fiche-barre .onglet-description-en svg,
body.admin-sombre #vue-caracteristiques:checked ~ .fiche-barre .onglet-caracteristiques svg,
body.admin-sombre #vue-declinaisons:checked     ~ .fiche-barre .onglet-declinaisons svg,
body.admin-sombre #vue-images:checked           ~ .fiche-barre .onglet-images svg,
body.admin-sombre #vue-documents:checked        ~ .fiche-barre .onglet-documents svg,
body.admin-sombre #vue-depot:checked            ~ .fiche-barre .onglet-depot svg,
body.admin-sombre #vue-infos:checked            ~ .fiche-barre .onglet-infos svg {
  color: var(--a-accent);
}
/* Une languette au clavier doit se voir : le contour suit la radio focalisee. */
body.admin-sombre .vue-radio:focus-visible ~ .fiche-barre .onglets label {
  outline: 2px solid var(--a-accent); outline-offset: -2px;
}

/* UN ONGLET ETEINT : visible, lisible, et pas cliquable. Il annonce ce qui
   viendra - la fiche d'un produit en creation n'a qu'un onglet utilisable - au
   lieu de laisser croire que le reste n'existe pas. On ne descend PAS sous le
   seuil de contraste pour le griser : c'est du texte qu'on doit pouvoir lire,
   et « --a-muet » sur « --a-bg » le tient. Ce qui dit l'etat est le fond plus
   plat, le curseur et l'infobulle. */
body.admin-sombre .onglets svg { width: 15px; height: 15px; flex: none; }
/* L'icone d'un onglet ferme est plus discrete que son libelle : elle repere,
   elle ne crie pas. Celle de l'onglet ouvert prend l'accent. */
body.admin-sombre .onglets a.actif svg { color: var(--a-accent); }

/* SOUS 900 PX, LES ONGLETS DEFILENT plutot que de se replier sur quatre lignes :
   huit languettes empilees valent une seconde barre laterale. Le defilement
   horizontal est ici le moindre mal - il ne coute aucune hauteur, et la barre
   reste une barre. */
@media (max-width: 900px) {
  body.admin-sombre .fiche-barre { position: static; }
  body.admin-sombre .fiche-barre .onglets {
    flex-wrap: nowrap; overflow-x: auto; width: 100%;
  }
}

/* --- la grille de la fiche : le formulaire, et la colonne de droite -------- */
body.admin-sombre .fiche-grille {
  display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 14px; align-items: start;
}
body.admin-sombre .fiche-cote { display: grid; gap: 14px; }
/* UNE FICHE SANS COLONNE DE DROITE occupe toute la largeur. Sans cette regle,
   la grille reserverait les 320 px d'une colonne qui n'existe plus - un vide a
   droite du formulaire, et rien pour dire pourquoi. */
body.admin-sombre .fiche-grille--seule { grid-template-columns: minmax(0, 1fr); }

/* --- une section de formulaire --------------------------------------------
   Le titre est un filet et un mot : une carte par section aurait empile des
   bordures dans des bordures. */
body.admin-sombre .bloc-f { padding: 14px 16px 16px; }
body.admin-sombre .bloc-f + .bloc-f { border-top: 1px solid var(--a-bord); }
body.admin-sombre .bloc-f > h3 {
  margin: 0 0 12px; padding-bottom: 8px; border-bottom: 1px solid var(--a-bord);
  font-size: 14px; font-weight: 800; color: var(--a-txt);
}
body.admin-sombre .bloc-f > h3 .bloc-f-note {
  float: right; font-size: 11.5px; font-weight: 500; color: var(--a-muet);
}

/* LE LIBELLE EST A GAUCHE DU CHAMP, et c'est ce qui donne la densite : un
   libelle au-dessus coute une ligne par champ, soit dix lignes sur une fiche
   qui en porte dix. A cote, deux champs tiennent sur la hauteur d'un seul.
   En dessous de 900 px le libelle repasse au-dessus - a cette largeur, une
   colonne de libelles de 130 px ne laisse plus rien au champ. */
body.admin-sombre .champs {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(330px, 1fr));
  gap: 10px 20px;
}
body.admin-sombre .champs--1 { grid-template-columns: minmax(0, 1fr); }
body.admin-sombre .champ-l {
  display: grid; grid-template-columns: 132px minmax(0, 1fr);
  gap: 4px 12px; align-items: center;
}
body.admin-sombre .champ-l--haut { align-items: start; }
body.admin-sombre .champ-l--large { grid-column: 1 / -1; }
body.admin-sombre .champ-l > label {
  margin: 0; font-size: 12.5px; font-weight: 600; color: var(--a-muet); line-height: 1.3;
}
body.admin-sombre .champ-l .champ,
body.admin-sombre .champ-l select.champ { margin: 0; padding: 7px 10px; font-size: 13px; }
body.admin-sombre .champ-l input.champ,
body.admin-sombre .champ-l select.champ { height: 34px; }
body.admin-sombre .champ-l textarea.champ { min-height: 120px; line-height: 1.5; }
/* L'aide vit SOUS le champ, dans la seconde colonne : alignee sous le libelle
   elle aurait paru s'y rapporter. */
body.admin-sombre .champ-l .champ-aide {
  grid-column: 2; margin: 0; font-size: 11.5px; color: var(--a-muet);
}
body.admin-sombre .champ-l--haut > label { padding-top: 7px; }

/* Un champ avec son unite collee : le « € » d'un prix, le « mm » d'une cote.
   L'unite est dans le flux, pas en superposition : une valeur longue la pousse
   au lieu de passer dessous. */
body.admin-sombre .champ-unite { display: flex; align-items: stretch; }
body.admin-sombre .champ-unite .champ {
  border-top-right-radius: 0; border-bottom-right-radius: 0; flex: 1 1 auto; min-width: 0;
}
body.admin-sombre .champ-unite b {
  display: flex; align-items: center; padding: 0 11px;
  border: 1px solid var(--a-bord); border-left: 0;
  border-radius: 0 8px 8px 0; background: var(--a-panel-2);
  color: var(--a-muet); font-size: 13px; font-weight: 600;
}

/* --- l'image principale, dans la colonne de droite ------------------------ */
body.admin-sombre .img-principale {
  display: block; width: 100%; aspect-ratio: 1 / 1; object-fit: cover;
  border-radius: 10px; border: 1px solid var(--a-bord); background: var(--a-bg);
}
body.admin-sombre .img-principale--vide {
  display: flex; align-items: center; justify-content: center; text-align: center;
  border-style: dashed; color: var(--a-muet); font-size: 12px; padding: 16px;
}
body.admin-sombre .img-boutons { display: flex; gap: 8px; margin-top: 10px; }
body.admin-sombre .img-boutons .bouton { flex: 1 1 auto; margin-top: 0; }
body.admin-sombre .img-boutons form { display: flex; margin: 0; }

/* --- la galerie de l'onglet Images ---------------------------------------- */
body.admin-sombre .galerie {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px;
}

/* --- l'editeur de caracteristiques, groupe par groupe --------------------- */
body.admin-sombre .grp-titre {
  margin: 16px 0 6px; font-size: 12px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--a-muet);
}
body.admin-sombre .grp-titre:first-child { margin-top: 0; }
body.admin-sombre .attr-origine { font-size: 11px; color: var(--a-muet); white-space: nowrap; }

/* --- le selecteur multiple des associations -------------------------------
   UN <select multiple> PLUTOT QU'UN COMPOSANT : il se cherche au clavier, il
   fonctionne sans script, et il dit ce qui est choisi sans qu'on clique. Une
   boite a jetons avec recherche aurait demande du JavaScript pour une saisie
   qui doit marcher sans - regle 5 du depot. Le champ de recherche de la liste
   fait le reste quand le catalogue grossit. */
body.admin-sombre select.champ[multiple] {
  height: auto; min-height: 150px; padding: 6px; font-size: 12.5px;
}
body.admin-sombre select.champ[multiple] option { padding: 3px 6px; border-radius: 4px; }

@media (max-width: 1100px) {
  body.admin-sombre .fiche-grille { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 900px) {
  body.admin-sombre .champ-l { grid-template-columns: minmax(0, 1fr); align-items: start; }
  body.admin-sombre .champ-l .champ-aide { grid-column: 1; }
  body.admin-sombre .champ-l--haut > label { padding-top: 0; }
}
@media (max-width: 720px) {
  body.admin-sombre .ecran-tete { align-items: stretch; }
  body.admin-sombre .ecran-actions { justify-content: flex-start; }
  body.admin-sombre .filtres-fin { margin-left: 0; width: 100%; }
  body.admin-sombre .erp-tete { position: static; }
  body.admin-sombre .ecran-tete h2 { font-size: 20px; }
  body.admin-sombre .erp-tete h2 { font-size: 19px; }
}

/* --- 32. La valeur anglaise d'une caracteristique --------------------------
   UN SEUL JETON NOUVEAU, et pas une section de theme : le champ anglais est le
   champ standard, en retrait. Il existe parce que le classeur du catalogue
   porte « contenu_caracteristique_en » - une colonne qu'un import ecrit et
   qu'aucun ecran ne montre est une donnee qu'on ne peut ni verifier ni
   corriger. Il reste vide la plupart du temps, « 20 km/h » ne se traduisant
   pas : il doit donc se voir sans se mettre en avant.
   Scope body.admin-sombre, comme l'impose la section 28. */
body.admin-sombre .champ--en {
  margin-top: 4px; font-size: 12.5px; color: var(--a-muet);
  border-style: dashed;
}
body.admin-sombre .champ--en:focus { color: var(--a-txt); border-style: solid; }

/* =============================================================================
   33. L'ADMINISTRATION A PLAT - angles droits, ombres retirees, densite
   -----------------------------------------------------------------------------
   DEMANDE DU 20 OCTOBRE : plus un seul angle arrondi, plus une seule ombre, et
   des rembourrages reduits partout. C'est la suite logique de la forme ERP de
   la section 31 - un outil tabulaire, pas une vitrine - appliquee cette fois a
   l'ossature elle-meme : barre laterale, barre du haut, cartes, champs.

   « BORDER-RADIUS: 0 » ET NON « BORDER-RADIUS: NONE ». La demande ecrivait
   « none », qui n'est pas une valeur valide pour cette propriete : le
   navigateur jette la declaration ENTIERE et l'angle reste arrondi - sans une
   erreur, sans un avertissement, et la retouche parait simplement n'avoir rien
   fait. C'est exactement le genre de panne silencieuse que ce depot traque
   ailleurs, et elle se corrige en ecrivant la longueur.

   ECART ASSUME AVEC LA MAQUETTE, ET IL EST VISIBLE : la maquette porte des
   angles d'environ 6 px sur les boutons, les champs, les pastilles et les
   panneaux. La demande ecrite dit zero, deux fois, selecteur par selecteur.
   C'est la demande ecrite qui est suivie - revenir a la maquette se fait en
   retirant cette section, rien d'autre n'en depend.

   TOUT EST SCOPE « body.admin-sombre », ET POUR DEUX RAISONS. La regle du
   depot d'abord : la feuille est partagee, et « .carte », « .bouton »,
   « .etiquette » ou « table.donnees » nus toucheraient le site vitrine. La
   specificite ensuite : ces regles arrivent apres les sections 28 a 31, mais
   s'appuyer sur l'ordre du fichier obligerait le prochain qui deplace un bloc
   a redecouvrir pourquoi les angles sont revenus.
   ========================================================================== */

/* --- L'OSSATURE ---------------------------------------------------------- */
body.admin-sombre .admin-main   { padding: 0; }
body.admin-sombre .admin-bloc   { border-radius: 0; box-shadow: none; padding: 6px; }
body.admin-sombre .admin-logo   { padding: 4px 10px 10px; }
body.admin-sombre .admin-nav-lien { border-radius: 0; padding: 5px 8px; }
body.admin-sombre .admin-topbar { border-radius: 0; box-shadow: none; padding: 2px 10px; }

/* L'onglet courant garde SON ombre : ce n'est pas une ombre portee mais le
   filet d'accent de la section 28, pose en « inset ». Le retirer effacerait
   le reperage de la page courante, pas une decoration. */
body.admin-sombre .admin-nav-lien.actif { box-shadow: inset 3px 0 0 var(--a-accent-txt); }

/* --- LES SURFACES -------------------------------------------------------- */
body.admin-sombre .carte        { border-radius: 0; box-shadow: none; padding: 10px; }
body.admin-sombre .kpi-carte    { border-radius: 0; box-shadow: none; padding: 10px; }

/* --- LES CONTROLES ------------------------------------------------------- */
body.admin-sombre .bouton       { border-radius: 0; box-shadow: none; padding: 2px 10px; }
body.admin-sombre .etiquette    { border-radius: 0; padding: 2px 6px; }
body.admin-sombre .periode-lien { border-radius: 0; padding: 2px 10px; }

/* LES CHAMPS, ET LA LISTE D'EXCLUSIONS EST CELLE DE LA DEMANDE : une case a
   cocher, un bouton radio, un curseur ou un selecteur de couleur sont dessines
   par le navigateur, et leur imposer un rembourrage de 7 px les deforme. */
body.admin-sombre input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=submit]):not([type=button]):not([type=image]):not([type=range]):not([type=color]),
body.admin-sombre select,
body.admin-sombre textarea      { border-radius: 0; box-shadow: none; padding: 7px; }

/* LE CHAMP DE RECHERCHE DE LA BARRE DU HAUT N'A PAS DE FILET A LUI : c'est son
   enveloppe « .topbar-rech » qui porte la bordure et la met en valeur au focus
   (« :focus-within »). Un second filet a l'interieur du premier dessinait donc
   un cadre dans un cadre. */
body.admin-sombre #topbar-q     { border: 0; }
body.admin-sombre .topbar-rech  { border-radius: 0; }

/* --- LES TABLEAUX -------------------------------------------------------- */
/* « table.donnees » etait la seule regle de la section 28 restee sans scope.
   Verifie avant d'y toucher : aucune page publique n'emet cette classe, elle
   n'est donc pas une exception a conserver - juste un oubli qu'on ne reconduit
   pas ici. */
body.admin-sombre table.donnees th,
body.admin-sombre table.donnees td,
body.admin-sombre .donnees--dense th,
body.admin-sombre .donnees--dense td { padding: 4px 6px; }

/* =============================================================================
   34. LA FICHE D'UN PRODUIT - LIGNES DE CHAMPS, BASCULES, ZONE DE DEPOT
   -----------------------------------------------------------------------------
   TOUT EST SCOPE body.admin-sombre, comme l'impose la section 28 : la feuille
   est partagee avec le site public, et une classe nue s'y appliquerait aussi.

   POURQUOI DES LIGNES PLUTOT QUE LA GRILLE A DEUX COLONNES. La cliente a dicte
   la disposition champ par champ - « Ligne 1 : Code interne, Réf. catalogue,
   Réf. fabricant, Nom ». Quatre champs cote a cote ne tiennent pas avec un
   libelle a gauche de chacun : le libelle passe donc AU-DESSUS, en petit, et
   les champs se partagent la largeur. Le reste de l'administration ne change
   pas - .champs et .champ-l gardent leur forme.
   ========================================================================== */
body.admin-sombre .f-ligne {
  display: flex; flex-wrap: wrap; gap: 10px 16px; align-items: flex-start;
  margin-bottom: 10px;
}
body.admin-sombre .f-ligne:last-child { margin-bottom: 0; }
body.admin-sombre .champ-c { flex: 1 1 200px; min-width: 0; display: grid; gap: 4px; }
body.admin-sombre .champ-c--court { flex: 0 1 150px; }
body.admin-sombre .champ-c--large { flex: 2 1 320px; }
body.admin-sombre .champ-c > label {
  font-size: 11px; font-weight: 700; color: var(--a-muet);
  text-transform: uppercase; letter-spacing: .03em;
}
body.admin-sombre .champ-c .champ { margin: 0; padding: 7px 10px; font-size: 13px; }
body.admin-sombre .champ-c input.champ,
body.admin-sombre .champ-c select.champ { height: 34px; }
body.admin-sombre .champ-c .champ-aide { margin: 0; font-size: 11px; }

/* --- la bascule : une case a cocher qui se lit de loin --------------------
   C'EST UNE VRAIE CASE A COCHER, habillee. Un bouton qui imiterait une bascule
   en JavaScript ne s'enverrait pas avec le formulaire, ne se cocherait pas au
   clavier et ne dirait rien a un lecteur d'ecran. Ici la case EST le controle :
   elle est seulement rendue invisible a l'oeil, et la piste suit son etat. */
body.admin-sombre .bascule {
  display: inline-flex; align-items: center; gap: 9px; cursor: pointer;
  font-size: 12px; color: var(--a-txt); min-height: 34px;
}
body.admin-sombre .bascule input {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
body.admin-sombre .bascule-piste {
  flex: 0 0 auto; width: 38px; height: 20px; border-radius: 999px;
  background: var(--a-bord-champ); position: relative; transition: background .12s;
}
body.admin-sombre .bascule-piste::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px;
  border-radius: 999px; background: #fff; transition: transform .12s;
}
body.admin-sombre .bascule input:checked + .bascule-piste { background: var(--a-accent); }
body.admin-sombre .bascule input:checked + .bascule-piste::after { transform: translateX(18px); }
body.admin-sombre .bascule input:focus-visible + .bascule-piste {
  outline: 2px solid var(--a-accent); outline-offset: 2px;
}
body.admin-sombre .bascule-txt { line-height: 1.3; }
body.admin-sombre .bascule-fige { display: grid; gap: 2px; min-height: 34px; }

/* --- la zone de depot -----------------------------------------------------
   LE CHAMP DE FICHIER EST DANS LE <label>, donc cliquer n'importe ou dans le
   cadre ouvre le selecteur - sans une ligne de JavaScript. Il n'est pas
   « display:none » : un champ ainsi retire sort de l'ordre de tabulation et ne
   se declenche plus au clavier. Il est transparent et superpose, ce qui lui
   laisse aussi le glisser-deposer du navigateur. */
body.admin-sombre .depot {
  position: relative; display: flex; align-items: center; gap: 14px;
  padding: 20px 18px; margin-bottom: 12px; cursor: pointer;
  border: 2px dashed var(--a-bord-champ); background: var(--a-bg);
}
body.admin-sombre .depot:hover { border-color: var(--a-accent); }
body.admin-sombre .depot-ico { flex: 0 0 auto; color: var(--a-muet); }
body.admin-sombre .depot-ico svg { width: 28px; height: 28px; }
body.admin-sombre .depot-txt { display: grid; gap: 3px; font-size: 13px; }
body.admin-sombre .depot-champ {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; cursor: pointer;
}
body.admin-sombre .champ--fichier { max-width: 150px; padding: 4px; font-size: 11px; }
/* CE QUE LA ZONE A RECU. Le champ de fichier est transparent et superpose :
   le libelle que le navigateur ecrit dessus - « 3 fichiers » - n'est donc nulle
   part a l'ecran, et deposer ne repondait rien. Le script ecrit ici le nom des
   fichiers choisis ; « :empty » retire la ligne tant qu'il n'y a rien, sans quoi
   la grille du cadre garderait son interligne pour du vide. */
body.admin-sombre .depot-choix { font-weight: 600; color: var(--a-txt); }
body.admin-sombre .depot-choix:empty { display: none; }

/* --- la grille des visuels ------------------------------------------------ */
body.admin-sombre .img-grille {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px;
}
body.admin-sombre .img-case { margin: 0; display: grid; gap: 6px; }
body.admin-sombre .img-case img {
  width: 100%; height: auto; aspect-ratio: 1; object-fit: cover;
  border: 1px solid var(--a-bord); background: var(--a-bg);
}
body.admin-sombre .img-case figcaption {
  display: flex; align-items: center; justify-content: space-between; gap: 6px;
}
/* LA CASE DE SELECTION D'UN VISUEL. Elle vise par « form= » le formulaire pose
   APRES la grille : les visuels portent chacun leurs propres petits formulaires
   d'action, et deux formulaires ne s'imbriquent pas. */
body.admin-sombre .img-choix {
  display: inline-flex; align-items: center; gap: 4px; font-size: 12px; cursor: pointer;
}
body.admin-sombre .decl-img { display: inline-flex; align-items: center; gap: 4px; }
/* LA COLONNE SE DIMENSIONNE SUR SON CHAMP, DONC LE CHAMP DOIT AVOIR UNE
   LARGEUR. « cell-serre » (width: 1%) demande au tableau la largeur MINIMALE du
   contenu, et ces deux champs-la n'en declaraient aucune : « .champ-unite
   .champ » porte « flex: 1 1 auto; min-width: 0 », ce qui autorise le champ de
   prix a tomber a ZERO, et un champ « number » sans largeur se reduit a son
   compteur. Les deux se sont donc ecrases le jour ou la colonne a cesse d'etre
   large - signale a l'ecran, « les champs Prix HT et ordre sont trop petits ».
   D'ou une largeur DECLAREE et non un simple plafond : « max-width » ne retient
   que le haut, et il n'y a rien qui retienne le bas. Mesure sur le contenu
   reel : un prix a quatre chiffres et deux decimales, un ordre a quatre
   chiffres plus le compteur du navigateur. */
body.admin-sombre .cell-prix  { width: 96px; max-width: 96px; }
body.admin-sombre .cell-ordre { width: 78px; max-width: 78px; }
body.admin-sombre .carte--danger { border-color: var(--a-err); }

/* --- l'onglet des declinaisons suit la case, pas l'enregistrement ----------
   « QUAND JE CHOISIS AVEC DECLINAISON, L'ONGLET DOIT APPARAITRE DIRECTEMENT »
   (20 octobre). Les onglets sont des boutons radio poses AVANT les panneaux, et
   la case vit DANS le premier panneau : aucun selecteur de frere ne remonte,
   c'est pour cela que l'onglet attendait l'enregistrement.

   « :has() » REGARDE EN AVANT COMME EN ARRIERE, et il fait ce travail SANS UNE
   LIGNE DE JAVASCRIPT. La classe « onglet-masque » porte l'etat ENREGISTRE -
   c'est le repli d'un navigateur qui ignorerait « :has() », et il est juste :
   l'onglet reste alors ferme tant qu'on n'a pas enregistre, exactement comme
   avant. La regle suivante porte l'etat DU MOMENT, et elle gagne par sa
   specificite (l'identifiant compte, meme dans « :has() »). */
body.admin-sombre .onglets .onglet-masque,
body.admin-sombre .vue-panneau--masque { display: none; }
body.admin-sombre:has(#f-decl:checked) .onglets .onglet-masque { display: inline-flex; }
body.admin-sombre:has(#f-decl:checked) #vue-declinaisons:checked ~ .vue-panneau--masque {
  display: block;
}

/* --- le depot mince de l'image principale --------------------------------- */
body.admin-sombre .img-depot { display: grid; gap: 8px; margin-top: 10px; }
body.admin-sombre .depot--mince { padding: 12px 14px; gap: 10px; }
body.admin-sombre .depot--mince .depot-ico svg { width: 20px; height: 20px; }
body.admin-sombre .depot--mince .depot-txt { font-size: 12px; }
