/*
 * portail.css — l'habillage du portail d'aiguillage.
 *
 * Il ne decide aucune couleur : tout vient de tokens.css. Il suppose composants.css charge avant
 * lui pour .champ, .bouton et .message.
 *
 * CE QU'IL REMPLACE, ET POURQUOI :
 *
 *   - une video de plage en autoplay (1,33 Mo) et son poster (844 Ko), soit 2,1 Mo de decor sur
 *     une page qu'un technicien ouvre plusieurs fois par jour. Ici : un aplat et un degrade
 *     radial tres faible, zero octet ;
 *   - du glassmorphism a tous les etages — `backdrop-filter: blur()` sur le voile de fond, sur
 *     les cartes, sur les pastilles. Chaque couche coute une composition a chaque defilement, et
 *     son rendu depend de ce qui passe dessous, donc il n'est jamais deux fois le meme. Ici :
 *     des surfaces opaques, une bordure d'un pixel, une ombre courte ;
 *   - six palettes de client tirees par HACHAGE DU NOM (sky, emerald, indigo, amber, rose,
 *     violet) plus trois couleurs d'application. Renommer un client changeait sa couleur. Ici,
 *     l'ecran est monochrome et l'accent ne sert qu'a ce sur quoi on peut agir ;
 *   - des rayons de 28 a 32 px. Ici : 8 px pour une tuile, 12 pour un service, 16 pour un
 *     groupe, 20 pour le panneau. Un outil de travail ne gagne rien a des coins tres arrondis.
 *
 * LE REGISTRE EST MONOCHROME, ET C'EST UN CHOIX A SURVEILLER. Les trois applications ne se
 * distinguent plus que par leur pictogramme. Si l'usage montre une hesitation, le retour en
 * arriere coute UN jeton par application, pose sur .orbite__icone — rien d'autre ne bouge.
 */

/* ==========================================================================================
 * 1. La page
 * ========================================================================================== */

.hub-ecran {
    min-height: 100vh;
    min-height: 100dvh;
    background: var(--voile);
    color: var(--encre);
    font-family: var(--police-texte);
    font-size: var(--taille-base);
    line-height: var(--hauteur-texte);
    -webkit-font-smoothing: antialiased;
}

/* Le fond est un canvas (grille-formes.js), pose en position fixe derriere tout le reste.
 * La premiere version peignait ici un halo en degrade radial ; il est remplace, pas cumule —
 * deux fonds superposes se seraient annules en une soupe grise. */

.hub-contenu {
    position: relative;
    z-index: 1; /* au-dessus de la trame en canvas */
    /* Le portail tient desormais dans un ecran : la carte se centre verticalement au lieu de
     * flotter en haut d une page aux trois quarts vide. Le panneau de choix, quand il s ouvre,
     * repousse le tout vers le haut — c est le comportement voulu, on vient de changer de
     * question. */
    min-height: 100dvh;
    justify-content: center;
    /*
     * FLEX-SHRINK: 0, ET C'EST LA CORRECTION PRINCIPALE DU PORTAIL SUR TELEPHONE.
     *
     * Ce bloc est un element flexible de #hubContent, qui a une hauteur fixe — celle de l'ecran.
     * Par defaut un element flexible RETRECIT pour tenir dans son parent, jusqu'a sa hauteur
     * minimale : il etait donc bloque a 100dvh au lieu de grandir avec son contenu. Le manque
     * se reportait sur ses propres enfants, et c'est le panneau de choix — en overflow: hidden,
     * donc de hauteur minimale nulle — qui l'absorbait en entier.
     *
     * Mesure a 375 x 812 : apres un tap sur « Planning », le panneau s'ouvrait bien, mais ne
     * mesurait que 66 px de haut pour vingt-trois services. On voyait une bande vide, et faire
     * defiler la page ne revelait rien, puisque le contenu etait rogne A L'INTERIEUR de la boite.
     * Rapporte tel quel : « je n'ai strictement rien qui s'affiche, et je ne peux pas scroller ».
     *
     * Sur un grand ecran le defaut existait aussi, simplement moins visible : le bas du panneau
     * etait coupe, ce que j'avais attribue a tort au nombre de services.
     */
    flex-shrink: 0;
    width: 100%;
    max-width: 1080px;
    margin: 0 auto;
    padding: var(--espace-10) var(--espace-5) var(--espace-16);
    display: flex;
    flex-direction: column;
    gap: var(--espace-8);
}

@media (min-width: 768px) {
    .hub-contenu { padding: var(--espace-16) var(--espace-8) var(--espace-20); }
}

/* --- Barre d'actions flottante ----------------------------------------------------------- */

.hub-flottant {
    position: fixed;
    top: var(--espace-4);
    right: var(--espace-4);
    z-index: 40;
    display: flex;
    align-items: center;
    gap: var(--espace-2);
}

.action {
    display: inline-flex;
    align-items: center;
    gap: var(--espace-2);
    padding: var(--espace-2) var(--espace-4);
    font-family: inherit;
    font-size: var(--taille-xs);
    font-weight: var(--graisse-appuyee);
    color: var(--encre-attenuee);
    background: var(--surface-flottante);
    border: 1px solid var(--trait);
    border-radius: var(--rayon-plein);
    box-shadow: var(--ombre-1);
    cursor: pointer;
    transition: color var(--duree) var(--courbe),
                border-color var(--duree) var(--courbe),
                background-color var(--duree) var(--courbe);
}

.action:hover {
    color: var(--encre-forte);
    border-color: var(--trait-fort);
}

.action--danger:hover { color: var(--alerte); border-color: var(--alerte); }

.action svg { width: 15px; height: 15px; }

/* ==========================================================================================
 * 2. En-tete : salutation et recherche
 * ========================================================================================== */

.recherche { position: relative; }

.recherche__icone {
    position: absolute;
    left: var(--espace-4);
    top: 50%;
    transform: translateY(-50%);
    width: 17px;
    height: 17px;
    color: var(--encre-faible);
    pointer-events: none;
}

/* Le champ est celui de composants.css : seul le retrait gauche change, pour laisser la loupe. */
.recherche .champ__saisie { padding-left: var(--espace-12); }

.recherche__resultats {
    position: absolute;
    top: calc(100% + var(--espace-2));
    left: 0;
    right: 0;
    z-index: 30;
    background: var(--papier);
    border: 1px solid var(--trait);
    border-radius: var(--rayon-md);
    box-shadow: var(--ombre-3);
    overflow: hidden;
}

/* ==========================================================================================
 * 3. Apparition au chargement
 * ==========================================================================================
 * Un fondu et huit pixels de montee, decales de 30 ms d'une carte a l'autre. Le decalage est
 * PLAFONNE par le JS : au-dela d'une dizaine de cartes, l'attente cumulee se verrait, et la
 * derniere arriverait apres que l'oeil a fini de parcourir la page.
 */
.apparait {
    animation: apparait var(--duree-longue) var(--courbe) both;
    animation-delay: calc(var(--i, 0) * 30ms);
}

@keyframes apparait {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}

/* ==========================================================================================
 * 4. Sur telephone
 * ==========================================================================================
 * Deux defauts constates a 375 px, et leurs corrections.
 *
 * 1. LA BARRE D'ACTIONS RECOUVRAIT LE TITRE. Elle est `fixed` en haut a droite : la place
 *    existe sur un ecran large, pas sur un telephone ou elle prend toute la largeur. Elle rentre
 *    donc dans le flux, au-dessus du reste.
 *
 * 2. LES REMBOURRAGES S'ADDITIONNAIENT. Le tableau de bord empilait page, panneau, groupe et
 *    service : il ne restait que 215 px de large a l'interieur d'un service, pour des tuiles
 *    d'au moins 132 — une seule par rangee. Ces niveaux ont disparu avec lui ; il ne reste ici
 *    que le resserrement de la page elle-meme. C'est le genre de calcul qu'on ne fait qu'en
 *    mesurant : chaque niveau pris isolement paraissait raisonnable.
 */
@media (max-width: 640px) {
    /*
     * LA BARRE PASSE EN ICONES SEULES. Avec leurs libelles, ses six boutons se repliaient sur
     * quatre rangees et occupaient 186 px — un quart d'ecran de boutons avant meme le titre.
     * En icones de 40 px elles tiennent sur une ligne.
     *
     * Les libelles NE SONT PAS retires : ils restent dans l'arbre d'accessibilite, masques
     * visuellement par la meme technique que .hors-ecran. Un lecteur d'ecran annonce toujours
     * « Gestion Clients », et un test qui vise le bouton par son texte le trouve toujours.
     */
    .hub-flottant {
        position: static;
        flex-wrap: nowrap;
        justify-content: flex-end;
        gap: var(--espace-1);
        padding: var(--espace-3) var(--espace-4) 0;
    }

    .hub-flottant #adminTools { gap: var(--espace-1); flex-wrap: nowrap; }

    .hub-flottant .action {
        width: 40px;
        height: 40px;
        padding: 0;
        justify-content: center;
    }

    .hub-flottant .action svg { width: 17px; height: 17px; }

    .hub-flottant .action > span {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
        border: 0;
    }

    .hub-contenu {
        padding-top: var(--espace-5);
        /* Sur telephone le contenu depasse toujours l'ecran : le centrer verticalement n'a plus
         * d'objet, et poussait le titre vers le bas sans raison. */
        justify-content: flex-start;
    }
}

/* ==========================================================================================
 * 5. Mouvement reduit
 * ==========================================================================================
 * L'apparition des cartes ne se joue plus, et le halo du pointeur disparait : il suit un
 * mouvement, il n'a pas de forme au repos.
 */
@media (prefers-reduced-motion: reduce) {
    .apparait { animation: none; }

    .choix::before { display: none; }
}

/* ==========================================================================================
 * 6. La carte orbitale
 * ==========================================================================================
 * Mise en page en deux parties : le texte et la recherche a gauche, une carte a droite ou les
 * six destinations du portail tournent autour d'un centre.
 *
 * CE QU'ELLE MONTRE, ET CE QU'ELLE NE PEUT PAS MONTRER. Les trois applications ne s'ouvrent pas
 * depuis l'orbite : Planning, Stock et Carioca s'ouvrent POUR UN SERVICE, et le portail en
 * compte plusieurs. Un noeud d'application emmene donc a la liste et y designe ses tuiles —
 * meme geste que l'alerte de couverture du planning, qui ne se contente pas de signaler le jour
 * manquant mais vous y depose, surligne. Les trois noeuds d'administration, eux, mènent
 * directement a leur ecran : ils n'ont pas de service a choisir.
 *
 * LE FOND EST UN CANVAS (grille-formes.js), pose derriere toute la page. Il remplace le halo en
 * degrade de la premiere version.
 */

.orbite {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--espace-10);
    align-items: center;
}

@media (min-width: 900px) {
    .orbite { grid-template-columns: minmax(0, 1fr) minmax(0, 460px); gap: var(--espace-16); }
}

/* --- Le texte ----------------------------------------------------------------------------- */

.orbite__surtitre {
    display: flex;
    align-items: center;
    gap: var(--espace-2);
    margin: 0 0 var(--espace-4);
    font-size: var(--taille-xs);
    font-weight: var(--graisse-appuyee);
    letter-spacing: var(--interlettre-menu);
    text-transform: uppercase;
    color: var(--encre-faible);
}

.orbite__titre {
    margin: 0 0 var(--espace-4);
    font-size: var(--taille-2xl);
    line-height: var(--hauteur-serree);
    font-weight: var(--graisse-forte);
    letter-spacing: var(--interlettre-titre);
    color: var(--encre-forte);
    max-width: 18ch;
}

.orbite__chapo {
    margin: 0 0 var(--espace-6);
    max-width: 46ch;
    color: var(--encre-attenuee);
}

/* L'annonce de la nouvelle organisation : un lien, donc l'accent — c'est sur quoi on agit. */
.annonce {
    display: inline-flex;
    align-items: center;
    gap: var(--espace-3);
    max-width: 100%;
    margin: 0 0 var(--espace-2);
    padding: var(--espace-2) var(--espace-4) var(--espace-2) var(--espace-2);
    border: 1px solid var(--trait);
    border-radius: var(--rayon-plein);
    background: var(--surface-flottante);
    color: var(--encre-forte);
    font-size: var(--taille-xs);
    font-weight: var(--graisse-appuyee);
    text-decoration: none;
    transition: border-color var(--duree) var(--courbe), color var(--duree) var(--courbe);
}

.annonce:hover { border-color: var(--accent); color: var(--accent); }
.annonce svg { width: 15px; height: 15px; flex: none; }

.annonce__pastille {
    flex: none;
    padding: 2px var(--espace-2);
    border-radius: var(--rayon-plein);
    background: var(--accent);
    color: var(--accent-contraste);
}

.orbite__etat {
    display: inline-flex;
    align-items: center;
    gap: var(--espace-2);
    margin: var(--espace-4) 0 0;
    padding: var(--espace-1) var(--espace-3);
    border: 1px solid var(--trait);
    border-radius: var(--rayon-plein);
    font-size: var(--taille-xs);
    color: var(--encre-attenuee);
}

.orbite__pastille {
    width: 7px;
    height: 7px;
    border-radius: var(--rayon-plein);
    background: var(--succes);
}

/* --- La carte ------------------------------------------------------------------------------
 * Un carre : la hauteur suit la largeur, donc les noeuds restent sur un cercle a toutes les
 * tailles d'ecran. Le rayon des noeuds est en pourcentage pour la meme raison.
 */

/* Un <fieldset> : il regroupe les destinations pour les lecteurs d'écran, mais n'apporte
 * ni cadre, ni marge intérieure, ni largeur minimale. */
.orbite__carte {
    position: relative;
    width: 100%;
    max-width: 460px;
    min-width: 0;
    margin: 0 auto;
    padding: 0;
    border: 0;
    aspect-ratio: 1;
}

.orbite__anneau {
    position: absolute;
    top: 50%;
    left: 50%;
    border-radius: var(--rayon-plein);
    /* En tirets : un cercle plein qui tourne ne se voit pas tourner. C'est le seul interet du
     * pointille ici — il rend le mouvement lisible sans rien ajouter de voyant. */
    border: 1px dashed color-mix(in srgb, var(--encre) 14%, transparent);
    transform: translate(-50%, -50%);
    animation: orbite-tourne 90s linear infinite;
}

.orbite__anneau--exterieur { width: 74%; height: 74%; }

.orbite__anneau--interieur {
    width: 42%;
    height: 42%;
    border-style: solid;
    opacity: .7;
    animation-duration: 140s;
    animation-direction: reverse;
}

@keyframes orbite-tourne {
    to { transform: translate(-50%, -50%) rotate(360deg); }
}

/* --- Le centre ------------------------------------------------------------------------------ */

.orbite__centre {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 76px;
    height: 76px;
    display: grid;
    place-items: center;
    transform: translate(-50%, -50%);
    border-radius: var(--rayon-lg);
    background: var(--papier);
    border: 1px solid var(--trait);
    box-shadow: var(--ombre-2);
    font-size: var(--taille-md);
    font-weight: var(--graisse-forte);
    letter-spacing: .04em;
    color: var(--encre-forte);
}

/* La pulsation : un anneau qui grandit et s'efface. Il part de derriere le centre, ce qui evite
 * qu'il ne passe par-dessus le sigle. */
.orbite__pouls {
    position: absolute;
    inset: -1px;
    border-radius: inherit;
    border: 1px solid var(--accent);
    opacity: 0;
    animation: orbite-pouls 3.2s var(--courbe) infinite;
    pointer-events: none;
}

@keyframes orbite-pouls {
    0%   { transform: scale(1);   opacity: .45; }
    70%  { transform: scale(1.9); opacity: 0; }
    100% { transform: scale(1.9); opacity: 0; }
}

.orbite__centre-nom {
    position: absolute;
    top: calc(50% + 52px);
    left: 50%;
    transform: translateX(-50%);
    margin: 0;
    font-size: var(--taille-xs);
    font-weight: var(--graisse-appuyee);
    letter-spacing: var(--interlettre-menu);
    text-transform: uppercase;
    color: var(--encre-faible);
    white-space: nowrap;
}

/* --- Les noeuds ----------------------------------------------------------------------------
 * Positionnes par le JavaScript en pourcentages (cosinus/sinus). Le flottement vit sur un
 * element INTERIEUR : le noeud porte deja un `translate(-50%, -50%)` de centrage, et une
 * animation sur le meme element l'ecraserait.
 */

.orbite__noeud {
    position: absolute;
    transform: translate(-50%, -50%);
}

.orbite__jeton {
    display: flex;
    flex-direction: column;
    align-items: center;
    /* Le nom respire un peu plus bas : la plaque inclinee deborde en haut, pas en bas, mais la
     * face qui avance a besoin d'air sous elle. */
    gap: var(--espace-3);
    padding: 0;
    border: 0;
    background: none;
    font-family: inherit;
    cursor: pointer;
    animation: orbite-flotte 6s ease-in-out infinite;
    animation-delay: var(--retard, 0s);
}

/*
 * LES NOEUDS S IMMOBILISENT DES QU ON APPROCHE.
 *
 * Le flottement est joli tant qu on regarde la carte, et genant des qu on veut cliquer : une
 * cible qui derive de six pixels se vise moins bien, et Playwright a refuse net le clic —
 * « element is not stable ». Ce que l automate constate, un utilisateur presse le ressent.
 *
 * La pause est declenchee par le survol de la CARTE, pas du noeud : au moment ou le pointeur
 * entre dans la carte, les six noeuds se posent, et on vise ensuite une cible fixe. La pauser
 * au survol du noeud lui-meme arriverait trop tard — il aurait deja fallu l attraper.
 */
.orbite__carte:hover .orbite__jeton,
.orbite__carte:focus-within .orbite__jeton { animation-play-state: paused; }
@keyframes orbite-flotte {
    0%, 100% { transform: translateY(-3px); }
    50%      { transform: translateY(3px); }
}

.orbite__nom {
    font-size: var(--taille-xs);
    font-weight: var(--graisse-appuyee);
    color: var(--encre-attenuee);
    white-space: nowrap;
    transition: color var(--duree-courte) var(--courbe);
}

/* --- Le fond en canvas ---------------------------------------------------------------------- */

.hub-trame {
    position: fixed;
    inset: 0;
    z-index: 0;
    width: 100%;
    height: 100%;
    display: block;
    /* Le masque evite que la trame ne vienne concurrencer le contenu en bas de page, la ou les
     * cartes sont les plus denses. */
    -webkit-mask-image: linear-gradient(to bottom, #000 0, #000 45%, transparent 95%);
    mask-image: linear-gradient(to bottom, #000 0, #000 45%, transparent 95%);
}

/* ==========================================================================================
 * 7. Mouvement reduit — la carte orbitale
 * ========================================================================================== */
@media (prefers-reduced-motion: reduce) {
    .orbite__anneau,
    .orbite__pouls,
    .orbite__jeton { animation: none; }

    /* Le pouls disparait au lieu de rester fige a 45 % d'opacite, ou il ressemblerait a une
     * bordure qu'on aurait oublie de retirer. */
    .orbite__pouls { display: none; }
}

/* ==========================================================================================
 * 8. Le choix du service
 * ==========================================================================================
 * Ce qui remplace le tableau de bord deplie. Une application ne s'ouvre pas dans l'absolu :
 * cliquer « Planning » demande encore POUR QUEL SERVICE. Ce panneau pose cette question, et
 * elle seule.
 */

/*
 * LE HALO QUI SUIT LE POINTEUR, deplace ici depuis le panneau du tableau de bord quand celui-ci
 * a disparu. C'est le seul panneau qui reste, et monterHaloPointeur() y ecrit deja ses deux
 * variables : sans ces regles, il les posait sur un element que plus aucune declaration ne
 * lisait — un effet monte, cable, et invisible.
 */
.choix {
    /* Meme raison que .hub-contenu : un element flexible en overflow: hidden a une hauteur
     * minimale nulle, et c'est lui qui paie le premier quand la place manque. */
    flex-shrink: 0;
    /* Quand le tap sur une application fait defiler jusqu'ici, on s'arrete un peu avant le
     * bord : colle au haut de l'ecran, l'en-tete du panneau se lit mal. */
    scroll-margin-top: var(--espace-4);
    position: relative;
    background: var(--papier);
    border: 1px solid var(--trait);
    border-radius: var(--rayon-xl);
    box-shadow: var(--ombre-2);
    overflow: hidden;
    animation: apparait var(--duree-longue) var(--courbe) both;
}

.choix::before {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: radial-gradient(280px circle at var(--spot-x, 50%) var(--spot-y, 0%),
                var(--spot), transparent 72%);
    opacity: 0;
    transition: opacity var(--duree-longue) var(--courbe);
}

.choix:hover::before { opacity: 1; }

/* Le contenu passe devant le halo sans qu'aucun z-index n'ait a sortir du composant. */
.choix > * { position: relative; }

.choix__entete {
    display: flex;
    align-items: center;
    gap: var(--espace-4);
    padding: var(--espace-5) var(--espace-6);
    border-bottom: 1px solid var(--trait);
}

.choix__icone {
    flex: none;
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    border-radius: var(--rayon-sm);
    /* La pastille reprend la teinte de l'application choisie, pour qu'on reconnaisse d'ou l'on
     * vient ; l'accent sert de repli. */
    border: 1px solid var(--teinte, var(--accent));
    background: color-mix(in srgb, var(--teinte, var(--accent)) 14%, transparent);
    color: var(--teinte, var(--accent));
}

.choix__icone svg { width: 20px; height: 20px; }

.choix__titre { flex: 1; min-width: 0; }

.choix__nom {
    display: block;
    font-size: var(--taille-md);
    font-weight: var(--graisse-appuyee);
    letter-spacing: var(--interlettre-titre);
    color: var(--encre-forte);
    line-height: 1.3;
}

.choix__legende {
    display: block;
    font-size: var(--taille-sm);
    color: var(--encre-attenuee);
}

.choix__fermer {
    flex: none;
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--rayon-xs);
    background: none;
    color: var(--encre-faible);
    cursor: pointer;
    transition: color var(--duree) var(--courbe), border-color var(--duree) var(--courbe);
}

.choix__fermer:hover { color: var(--encre-forte); border-color: var(--trait); }
.choix__fermer svg { width: 16px; height: 16px; }

/* LA LISTE DEFILE AU-DELA D UNE DIZAINE D ENTREES.
 * Constate sur des donnees reelles : un compte administrateur voit tous les services, et la
 * base de test en comptait vingt-trois — le panneau sortait de l ecran par le bas, sans qu on
 * puisse atteindre ses dernieres entrees ni son bouton de fermeture. Les donnees de
 * demonstration, avec leurs six services, ne montraient rien de ce probleme. */
.choix__liste {
    max-height: 46vh;
    overflow-y: auto;
    scrollbar-width: thin;
    margin: 0;
    padding: var(--espace-3);
    list-style: none;
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--espace-2);
}

@media (min-width: 720px) {
    .choix__liste { grid-template-columns: 1fr 1fr; }
}

.choix__item {
    display: flex;
    align-items: center;
    gap: var(--espace-3);
    width: 100%;
    padding: var(--espace-3) var(--espace-4);
    font-family: inherit;
    text-align: left;
    background: var(--papier);
    border: 1px solid var(--trait);
    border-radius: var(--rayon-md);
    cursor: pointer;
    transition: border-color var(--duree-courte) var(--courbe),
                background-color var(--duree-courte) var(--courbe),
                transform var(--duree-courte) var(--courbe);
}

.choix__item:hover {
    border-color: var(--accent);
    background: var(--papier-enfonce);
    transform: translateX(2px);
}

.choix__identite { flex: 1; min-width: 0; }

.choix__service {
    display: block;
    font-size: var(--taille-base);
    font-weight: var(--graisse-appuyee);
    color: var(--encre-forte);
    line-height: 1.3;
}

/* Le client passe APRES le service et en plus petit : dans ce panneau, on cherche un service.
 * L'entite ne sert qu'a lever une ambiguite entre deux services homonymes. */
.choix__client {
    display: block;
    font-size: var(--taille-xs);
    color: var(--encre-faible);
}

.choix__fleche {
    flex: none;
    width: 16px;
    height: 16px;
    color: var(--encre-faible);
    transition: color var(--duree-courte) var(--courbe), transform var(--duree-courte) var(--courbe);
}

.choix__item:hover .choix__fleche { color: var(--accent); transform: translateX(2px); }

.choix .vide { padding: var(--espace-6); }

/* ==========================================================================================
 * 9. La commande de mouvement
 * ==========================================================================================
 * `data-mouvement` est pose sur <html> par le JavaScript, et vaut toujours « oui » ou « non ».
 *
 * POURQUOI CETTE SORTIE EXISTE. « Animations reduites » est actif par defaut sur beaucoup de
 * postes d'entreprise, pour des raisons qui n'ont rien a voir avec la sensibilite au mouvement.
 * Sans commande, ces postes voyaient un fond et une carte figes, sans explication — le defaut a
 * ete remonte tel quel : « l'animation ne fonctionne pas ». La preference systeme reste la
 * valeur par defaut ; elle cesse d'etre une sentence.
 *
 * Les regles de la media query plus haut n'utilisent qu'UNE classe, pour etre battues sans
 * `!important` par les selecteurs ci-dessous, qui en comptent deux.
 */

[data-mouvement="non"] .orbite__anneau,
[data-mouvement="non"] .orbite__jeton,
[data-mouvement="non"] .apparait,
[data-mouvement="non"] .choix { animation: none; }

[data-mouvement="non"] .orbite__pouls { display: none; }

[data-mouvement="oui"] .orbite__anneau {
    animation: orbite-tourne 90s linear infinite;
}

[data-mouvement="oui"] .orbite__anneau--interieur {
    animation-duration: 140s;
    animation-direction: reverse;
}

[data-mouvement="oui"] .orbite__pouls {
    display: block;
    animation: orbite-pouls 3.2s var(--courbe) infinite;
}

[data-mouvement="oui"] .orbite__jeton {
    animation: orbite-flotte 6s ease-in-out infinite;
    animation-delay: var(--retard, 0s);
}

/* ==========================================================================================
 * 10. Les pastilles de verre, et le grossissement au survol
 * ==========================================================================================
 * Deux effets recrees, tous deux venus de composants React qui ne s'installent pas ici.
 *
 * LA PASTILLE, reprise de GlassIcons (reactbits.dev, source ouverte) dont voici la mecanique
 * reelle — la premiere version de ce fichier l'avait approximee et manquait l'essentiel :
 *
 *   - DEUX COUCHES SUPERPOSEES dans un espace en perspective. Derriere, une plaque de couleur
 *     pleine, INCLINEE DE 15 DEGRES autour de son coin inferieur droit : c'est elle qui depasse
 *     et donne la silhouette reconnaissable. Devant, une face de verre droite, translucide.
 *   - AU SURVOL, la plaque s'incline davantage (25 degres) et recule en diagonale, pendant que
 *     la face de verre avance VERS LE REGARD — un `translateZ`, pas un agrandissement. C'est la
 *     perspective posee sur le parent qui transforme ce deplacement en relief.
 *
 * `backdrop-filter` EST UTILISE ICI, alors que je l'avais ecarte du reste de la refonte. Ce
 * n'est pas une contradiction : ce qui coutait cher, c'etaient des voiles translucides de la
 * taille de l'ecran, recomposes a chaque defilement. Six carres de 52 px ne pesent rien, et sans
 * le flou la face de verre n'est qu'un voile blanc — la plaque coloree derriere reste nette, et
 * l'effet tombe.
 *
 * DEUX ECARTS ASSUMES avec le composant d'origine :
 *
 *   - SON ETIQUETTE N'APPARAIT QU'AU SURVOL. Ici elles restent visibles en permanence : c'est un
 *     portail. Un ecran ou il faut survoler six pastilles pour savoir ou elles menent n'est pas
 *     utilisable, surtout au clavier.
 *   - SES DEGRADES SONT A 90 % DE SATURATION. Les notres viennent des jetons --app-*, desatures
 *     d'un cran : six aplats satures sur un ecran ouvert plusieurs fois par jour fatiguent.
 *
 * LE GROSSISSEMENT, lui, vient d'un dock : la pastille survolee grandit, et ses voisines
 * grandissent un peu selon leur distance au pointeur. Il se cumule avec ce qui precede — le
 * facteur est pose sur la boite exterieure, les deux couches gardent leurs transformations.
 */

.orbite__icone {
    position: relative;
    display: block;
    width: 52px;
    height: 52px;
    border: 0;
    background: none;
    /* La perspective vit sur le parent : c'est elle qui donne son relief au translateZ des
     * enfants. Sans elle, avancer vers le regard ne produit strictement rien. */
    perspective: 280px;
    transform-style: preserve-3d;
    transform: scale(var(--grossissement, 1));
    transform-origin: center;
    transition: transform var(--duree-courte) var(--courbe);
    will-change: transform;
}

.orbite__plaque,
.orbite__vitre {
    position: absolute;
    inset: 0;
    border-radius: 14px;
    /* La courbe du composant d'origine, conservee : un demarrage et une fin tres freines, ce qui
     * donne au mouvement son caractere « mecanique » plutot que mou. */
    transition: transform .3s cubic-bezier(.83, 0, .17, 1),
                box-shadow .3s cubic-bezier(.83, 0, .17, 1);
    will-change: transform;
}

/* La plaque de couleur, derriere. Son coin inferieur droit sert de pivot : c'est ce qui la fait
 * depasser en haut a gauche plutot que tourner sur elle-meme. */
.orbite__plaque {
    /* Du haut vers le bas, sans angle : les deux butees ont la meme luminosite, c est
     * l ecart de teinte qui fait le degrade. */
    background: linear-gradient(var(--teinte, var(--accent)), var(--teinte-2, var(--teinte, var(--accent))));
    box-shadow: 6px -6px 10px rgba(11, 15, 19, .16);
    transform: rotate(15deg);
    transform-origin: 100% 100%;
}

/* La face de verre, devant. Le liseré blanc intérieur est ce qui lit comme une épaisseur. */
.orbite__vitre {
    display: grid;
    place-items: center;
    background: hsla(0, 0%, 100%, .15);
    box-shadow: 0 0 0 1.5px hsla(0, 0%, 100%, .3) inset;
    -webkit-backdrop-filter: blur(9px);
    backdrop-filter: blur(9px);
    transform-origin: 80% 50%;
}

/* L'icone est blanche : elle se detache sur la plaque teintee, vue a travers le verre. */
.orbite__vitre svg { width: 21px; height: 21px; color: #fff; }

.orbite__jeton:hover .orbite__plaque,
.orbite__jeton:focus-visible .orbite__plaque {
    transform: rotate(25deg) translate3d(-6px, -6px, 6px);
    box-shadow: 8px -8px 14px rgba(11, 15, 19, .22);
}

.orbite__jeton:hover .orbite__vitre,
.orbite__jeton:focus-visible .orbite__vitre {
    transform: translate3d(0, 0, 22px);
}

/* L'etiquette prend la teinte de sa destination : c'est la seule facon de relier le mot a la
 * pastille maintenant qu'elles ont chacune leur couleur. */
.orbite__jeton:hover .orbite__nom,
.orbite__jeton:focus-visible .orbite__nom { color: var(--teinte, var(--accent)); }

/* Sans mouvement : ni grossissement, ni inclinaison au survol. La plaque garde ses 15 degres —
 * c'est la forme au repos, pas une animation. */
[data-mouvement="non"] .orbite__icone {
    --grossissement: 1;
    transition: none;
}

[data-mouvement="non"] .orbite__plaque,
[data-mouvement="non"] .orbite__vitre { transition: none; }

[data-mouvement="non"] .orbite__jeton:hover .orbite__plaque { transform: rotate(15deg); }
[data-mouvement="non"] .orbite__jeton:hover .orbite__vitre { transform: none; }

/* ==========================================================================================
 * Pied de page : les trois pages legales
 * ==========================================================================================
 * Elles etaient liees depuis l'ecran de connexion, et depuis nulle part une fois connecte. Un
 * collaborateur qui cherche la politique de confidentialite ne devrait pas avoir a se
 * deconnecter pour la trouver.
 *
 * Pose APRES le panneau de choix, dans le flux : sur telephone c'est le bas de la page, qu'on
 * atteint en defilant ; sur grand ecran, il suit la carte.
 */
.hub-pied {
    flex-shrink: 0;
    margin-top: var(--espace-4);
    padding-top: var(--espace-5);
    border-top: 1px solid var(--trait);
}

.hub-pied nav {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--espace-2) var(--espace-6);
    font-size: var(--taille-sm);
}

.hub-pied a {
    color: var(--encre-attenuee);
    text-decoration: none;
    /* Une cible de 44 px de haut au doigt, sans agrandir la ligne a l'oeil. */
    padding: var(--espace-2) 0;
    transition: color var(--duree) var(--courbe);
}

.hub-pied a:hover { color: var(--encre-forte); text-decoration: underline; }
