/* ============================================================
   ZebraPuma.Web : Shared component styles
   ============================================================ */

/* Hauteur de la poignee du pied, declaree UNE fois et lisible de partout.

   La poignee est un onglet ancre AU-DESSUS de la barre : elle deborde dans la zone de
   contenu, par dessein, et une page dont le contenu descend jusqu'en bas doit donc lui
   laisser la place. Constate le 29/07/2026 sur la page de contact de zebrapuma.be, ou
   12 px de bouton d'envoi passaient dessous.

   La reservation est un REMBOURRAGE de la derniere section, jamais une marge du pied.
   Une marge est transparente : elle laisse voir le fond du conteneur, ce qui dessinait une
   bande claire entre le degrade sombre de la page et le noir du pied (constat de Regis,
   29/07/2026). Un rembourrage garde le fond de la section, et la poignee se pose donc sur
   la page, ce qu'elle est censee faire. */
:root { --zp-footer-handle-h: 20px; }
@media (max-width: 600px) { :root { --zp-footer-handle-h: 30px; } }

/* Replie, seule la poignee reste visible : la ligne de copyright cede sa place au contenu.
   A TOUTES les largeurs, telephone comme bureau, et sur tout site qui le demande par le
   parametre HandleOnlyWhenCollapsed du composant. Sans lui, la ligne reste affichee, repliee
   comme depliee.
   Un parametre et non une regle generale, parce que la contrepartie depend de la page.
   Masquer la ligne retire sa hauteur au document : sur une page qui tient dans l'ecran, la
   zone de contenu de la colonne flex absorbe la difference et rien ne bouge ; sur une page
   plus longue, le document raccourcit au repli et rallonge a l'ouverture, d'au plus la
   hauteur que la barre reprend.
   Demande de Regis du 29/07/2026 : « un comportement identique sur tous les sites, juste la
   poignee, sur PC aussi ». La regle ne valait jusque-la que sous 600 px, ce qui faisait
   deux comportements la ou il en veut un. */
.zp-footer--poignee-seule.collapsed { padding: 0; }
.zp-footer--poignee-seule.collapsed .zp-footer-content--meta { display: none; }

/* --- ZpFooter : standard cross-site footer (compact, dynamic) ---

   Le pied se lit en deux morceaux, et un seul occupe de la place dans le document :

   - la BARRE (poignee mise a part), en flux, qui ne change jamais de hauteur ;
   - le PANNEAU, un calque ancre au bord haut de la barre, qui s'ouvre vers le haut
     par-dessus la page.

   Refonte du 29/07/2026. Avant elle, le tiroir vivait dans le flux : l'ouvrir allongeait
   le document, et le contenu ouvert se retrouvait SOUS la fenetre pour qui venait de
   descendre en bas de page. Mesure sur regis.scyeur.net, 360 x 640 : 216 px hors ecran.
   Deux versions d'un compensateur qui rattrapait ce debordement au defilement ont ete
   ecrites puis jetees. Un panneau qui ne pousse rien n'a rien a rattraper : la demande de
   Regis, « du propre et rock solid », se resout en supprimant la cause. */
.zp-footer {
    /* Rembourrage vertical des pastilles, declare une fois et consomme deux fois : par la
       pastille qui s'en dessine, et par la marge negative du selecteur qui le neutralise. */
    --zp-pilule-pad-y: 4px;
    /* Rythme vertical du panneau : l'ecart entre ses blocs, son rembourrage bas, et la marge
       negative qui recolle le panneau des onglets a son onglet. Une seule valeur repond de
       tout cela, sur la grille de 8.
       Declaree ICI, sur la racine du composant, et non sur le panneau : c'est la que les
       sites hotes posent deja leurs variables de theme, donc c'est la seule place ou un site
       peut la surcharger d'une ligne, sans toucher a la librairie. */
    --zp-footer-ecart: 8px;
    /* Ecart entre onglets. Il suit le rythme vertical par defaut, ce qui suffit tant qu'on ne
       veut pas les dissocier ; un site qui veut des onglets plus aeres que ses lignes ne
       redefinit que celle-ci. */
    --zp-footer-ecart-onglets: var(--zp-footer-ecart);
    position: relative;
    /* Garde sa hauteur dans la colonne : c'est la zone de contenu qui cede, jamais la
       signature. Voir le motif « sticky footer » commente sur .zps. */
    flex-shrink: 0;
    /* Au-dessus des barres collantes de ZpDataExplorer (z-index 20) et du panneau du
       selecteur de langue (30), sous les barres de navigation fixes (50) : le panneau
       s'ouvre par-dessus le contenu, jamais par-dessus la navigation. */
    z-index: 40;
    padding: 14px 24px 12px;
    border-top: 1px solid var(--border, #e5e7eb);
    color: var(--text-muted, #6b7280);
    text-align: center;
    font-size: 0.72rem;
    line-height: 1.5;
}
.zp-footer .zp-footer-content {
    max-width: 720px;
    margin: 0 auto;
}

/* Le panneau : calque ancre au bord haut de la barre, hors flux, donc sans effet sur la
   hauteur du document.

   Il porte le fond, et il le doit : un fond ne s'herite pas. Les trois sites peignent la
   BOITE du pied (background sur .zp-footer), ce qui suffisait tant que le tiroir etait
   dedans. Hors flux, cette boite se reduit a la barre et le panneau se retrouve dessine
   par-dessus la page : sans fond opaque a lui, le texte de la page passerait au travers.
   --bg-card plutot qu'une variable nouvelle : les trois sites la definissent deja a la
   couleur de leur pied, et c'est le materiau de la poignee (voir .zp-footer-handle).

   PAS d'overflow ici : la poignee est ancree au bord HAUT du panneau, donc en dehors de
   sa boite, et un rognage la ferait disparaitre. Le rognage vit un cran plus bas. */
.zp-footer .zp-footer-panneau {
    position: absolute;
    bottom: 100%;
    left: 0;
    right: 0;
    background: var(--bg-card, #1a1d27);
    /* Filets haut ET bas, du meme trait que celui de la barre.

       En haut, parce qu'un calque a besoin d'un bord pour se lire comme tel, et non de la
       chance d'un contraste de fond : zebrapuma.be, sombre sur sombre nuance, le montrait,
       tandis que sur regis.scyeur.net le creme sur creme ne dessinait rien sous la poignee.

       En bas, parce que le panneau MASQUE la bordure haute de la barre. Un element
       positionne en bottom: 100% se cale sur la boite de REMBOURRAGE de son conteneur, et
       la bordure vit juste au-dessus de cette boite : le panneau la recouvre donc
       exactement. Le premier jet supprimait le separateur d'antan en affirmant que cette
       bordure le remplacait, ce qui etait faux, et laissait 28 px de vide entre les
       mots-cles et la ligne de copyright (constat de Regis, 29/07/2026, « il y a une ligne
       blanche »).

       Les deux largeurs tombent a zero au repli : le panneau y mesure zero, et ses filets
       viendraient s'empiler sur celui de la barre, juste en dessous. */
    border-top: 1px solid var(--border, #e5e7eb);
    border-bottom: 1px solid var(--border, #e5e7eb);
    /* L'ouverture s'anime en grille et non en max-height : une piste de 0fr a 1fr va
       exactement a la hauteur naturelle du contenu. Le plafond de 200 px d'avant etait un
       nombre magique, commente « actual content ~100px », qui rognait en silence des que
       le contenu grandissait (mots-cles sur leur propre ligne, traduction plus longue) et
       qui faisait durer l'animation bien apres la fin du mouvement visible. */
    display: grid;
    grid-template-rows: 1fr;
    transition: grid-template-rows 0.35s ease;
}
.zp-footer.collapsed .zp-footer-panneau {
    grid-template-rows: 0fr;
    border-top-width: 0;
    border-bottom-width: 0;
}
/* Le rognage et la hauteur nulle vivent sur l'element de grille, jamais sur le panneau.
   Le padding vit un cran plus bas encore (.zp-footer-ecosystem) : pose ici, il donnerait
   au panneau replie la hauteur de son propre padding, puisqu'une piste de 0fr n'annule
   que la boite de contenu.
   Le contenu reste dans le DOM, sans display:none ni attribut hidden : les moteurs
   doivent continuer de l'indexer sans interaction. */
.zp-footer .zp-footer-content--ecosystem {
    overflow: hidden;
    min-height: 0;
    /* Aucun plafond de hauteur : celle du panneau est celle de son contenu, point.
       Un filet de 70svh figurait ici, invente pour parer un panneau plus haut que l'ecran.
       C'est un test qui tient cette garantie (Le_panneau_ouvert_tient_entier_dans_la_fenetre),
       pas une constante posee au juge. */
    /* L'opacite vit ICI et non sur le panneau : la poignee est un enfant du panneau, elle
       heriterait donc de son opacite et disparaitrait a l'etat replie, avec le seul
       moyen de rouvrir. Sur l'element de grille, elle ne touche que le contenu. */
    opacity: 1;
    /* visibility accompagne l'opacite, parce que l'opacite ne retire RIEN de l'ordre de
       tabulation : replie, le panneau gardait ses onglets, l'adresse du pilier et le lien de
       version joignables au clavier alors que plus rien ne se voyait, et le focus disparaissait
       dans un panneau ferme (WCAG 2.4.3 et 2.4.7). Releve par la review Copilot du 30/07/2026.
       Le delai est ce qui fait tenir les deux ensemble : a l'ouverture la visibilite revient
       tout de suite, sans quoi le fondu jouerait dans le vide ; a la fermeture elle attend la
       fin du repli du panneau, sans quoi le contenu disparaitrait d'un coup au lieu de fondre.
       visibility se prete a l'exercice la ou display ne le pourrait pas : elle est animable, et
       elle laisse les liens dans le document, donc suivis par les moteurs. */
    visibility: visible;
    transition: opacity 0.25s ease, visibility 0s;
}
.zp-footer.collapsed .zp-footer-content--ecosystem {
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.25s ease, visibility 0s 0.35s;
}
.zp-footer .zp-footer-ecosystem {
    padding: 16px 24px var(--zp-footer-ecart);
    /* UN seul ecart entre les blocs du panneau, au lieu de quatre marges a tenir en phase.
       Mesure du 30/07/2026 avant correction : ecarts de boite de 4, 8 et 8 px, et surtout
       ecarts d'ENCRE de 9,5, 19 et 20 px, l'oeil lisant l'encre et non les boites. Constate
       par Regis : « l'espace entre les lignes et les tags ne me parait pas reparti de
       maniere uniforme ». */
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--zp-footer-ecart);
}
/* Les liens sont soulignes en permanence : sur mobile il n'y a pas de survol,
   et la couleur seule ne peut pas porter l'information « ceci est un lien »
   (WCAG 1.4.1, technique G182). Les piliers font exception, ils portent deja
   un contour qui les designe comme actionnables. */
.zp-footer a {
    color: var(--accent, #6c8aff) !important;
    text-decoration: underline !important;
    text-underline-offset: 3px;
    text-decoration-thickness: 1px;
    transition: opacity 0.15s;
}
.zp-footer a:hover {
    opacity: 0.85;
}
/* Nom du site courant : « vous etes ici », dit avec le vocabulaire que ce bandeau
   a deja pose. Les pastilles non actives portent un CONTOUR, qui signale
   l'actionnable ; celle du site courant porte un REMPLISSAGE. Le nom recoit donc le
   meme remplissage, sans contour : il est courant, et il n'est pas cliquable.
   Rien n'est importe de l'exterieur, la grammaire du composant se suffit.

   Ni couleur ni graisse imposees : le mot herite du texte de sa ligne. L'italique a
   ete ecartee (planche /dev/site-courant.html, variantes G a I) : a 0.72rem elle
   couterait en lisibilite, et elle signifie l'emphase ou la mention, jamais la
   position courante.

   box-decoration-break : sans lui, un nom qui passe a la ligne verrait son fond se
   fendre en deux blocs, ce qui arrive des le premier ecran etroit. */
.zp-footer .zp-footer-self {
    text-decoration: none;
    background: var(--accent-soft, rgba(108, 138, 255, 0.13));
    border-radius: 4px;
    padding: 1px 6px;
    box-decoration-break: clone;
    -webkit-box-decoration-break: clone;
}
/* Remise a zero des marges du panneau : le gap de la colonne gouverne seul l'espacement.
   Declaree ICI, avant les variantes de rendu, qui posent legitimement leurs propres marges
   et doivent donc l'emporter. Les marges par element qu'elle devait battre ont ete retirees
   le 30/07/2026 : le panneau declarait un gap unique de 8 px et les ecarts mesures valaient
   8, 14 et 12, faute que cette regle prenne effet (constat de Regis, « l'interligne n'est pas
   tres harmonieux »).
   Le selecteur reste universel, sans nom d'element : ajouter « > p » l'aurait rendu PLUS
   specifique que les variantes, qu'il aurait alors ecrasees a son tour. Le panneau des
   onglets s'est ainsi retrouve detache de son onglet, sa marge negative annulee. */
.zp-footer .zp-footer-ecosystem > * {
    margin: 0;
}

.zp-footer .zp-footer-title {
    font-size: 0.62rem;
    text-transform: uppercase;
    letter-spacing: 1.8px;
    color: var(--text-muted, #6b7280) !important;
    font-weight: 600;
}
.zp-footer p { margin: 2px 0; }
.zp-footer .zp-footer-author {
    color: var(--text, #e5e7eb);
    font-weight: 500;
    font-size: 0.74rem;
}
.zp-footer .zp-footer-by {
    color: var(--text-muted, #6b7280);
    font-weight: 400;
    font-style: italic;
    margin-right: 2px;
}
/* Pillar selector : 3 buttons clickable on line 3 */
/* Marge haute plus genereuse que le reste du bandeau : la ligne au-dessus est
   soulignee, et un soulignement consomme l'espace optique sous sa ligne de base.
   Sans ce rattrapage, les piliers paraissent colles au texte qui les precede. */
.zp-footer .zp-pillar-selector {
    /* Le rembourrage vertical des pastilles est repris en marge negative : il dessine leur
       forme, il ne participe pas au rythme vertical. Sans cela, la rangee de controles
       ouvre un ecart d'encre pres du double des autres, parce que son texte commence
       8 px sous le haut de sa boite. */
    margin-block: calc(-1 * var(--zp-pilule-pad-y));
    font-size: 0.74rem;
}
/* Affordance : au repos, ces trois piliers ne se distinguaient d'un texte
   ordinaire que par leur couleur. WCAG 1.4.1 interdit de reposer sur la seule
   couleur pour signaler une information, ici « ceci est actionnable ». Ils
   portent donc un contour permanent, qui les lit comme des controles avant
   meme le premier clic. L'etat actif ajoute le remplissage.
   Le contour est pose a 70 % de l'accent : en dessous, son propre contraste
   passe sous le seuil de 3 pour 1 exige des elements d'interface (WCAG 1.4.11). */
.zp-footer .zp-pillar-link {
    display: inline-block;
    background: none;
    border: 1px solid var(--accent, #6c8aff);
    border-color: color-mix(in srgb, var(--accent, #6c8aff) 70%, transparent);
    border-radius: 999px;
    padding: var(--zp-pilule-pad-y) 10px;
    color: var(--accent, #6c8aff) !important;
    font-family: inherit;
    font-size: inherit;
    font-weight: 600;
    cursor: pointer;
    text-decoration: none !important;
    transition: background 0.15s, border-color 0.15s;
}
.zp-footer .zp-pillar-link:hover {
    background: var(--accent-soft, rgba(108, 138, 255, 0.13));
    border-color: var(--accent, #6c8aff);
    text-decoration: none !important;
}
.zp-footer .zp-pillar-link.zp-pillar-link-active {
    color: var(--accent, #6c8aff) !important;
    background: var(--accent-soft, rgba(108, 138, 255, 0.13));
    border-color: var(--accent, #6c8aff);
    text-decoration: none !important;
    font-weight: 700;
}

/* Dynamic detail line : space is ALWAYS reserved (min-height) so the layout never
   shifts when content appears on click. Empty by default = invisible blank space,
   filled on click = same vertical position. No twist effect. */
.zp-footer .zp-pillar-detail {
    /* Centre dans la hauteur reservee, horizontalement et verticalement. La reservation
       vaut une ligne au large et deux sous 600 px ; sans centrage, un contenu plus court
       que sa reserve se collerait en haut de la bande au lieu de s'y tenir au milieu. */
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-size: 0.72rem;
    color: var(--text-muted, #6b7280);
    /* 1.75 plutot que 1.5 : l'URL et les mots-cles se lisent comme deux informations
       distinctes, et le minimum de 1.5 exige par WCAG 1.4.12 laissait les deux lignes
       collees (demande Regis, 29/07/2026, « aerer l'url et les tags, question d'interligne »). */
    line-height: 1.75;
    /* Une ligne reservee, exprimee en lh, l'unite qui VAUT une hauteur de ligne : la
       reservation suit l'interligne d'elle-meme, au lieu d'un em a recalculer a chaque
       reglage. Sous 600 px elle passe a deux lignes, les mots-cles y prenant la leur.
       La reservation compte plus depuis que le panneau est un calque ancre par le bas :
       il grandit vers le HAUT, donc une hauteur qui varie ferait sauter le titre et les
       trois pastilles au lieu de pousser la page vers le bas.
       Dans l'em de l'element et non en rem d'un enfant : la reservation vide egale
       ainsi exactement la boite de ligne remplie. */
    min-height: 1lh;
}
/* L'URL et les mots-cles restent sur UNE ligne au-dela de 600 px, separes par le
   deux-points. Ils passent sur deux lignes en dessous : voir la media query en fin de
   bloc.
   Ecrite le 29/07/2026 pour toutes les largeurs, puis ramenee a la seule ou elle sert,
   sur constat de Regis : « sur pc c'est peut-etre mieux url et tags sur une seule ligne,
   c'est sur mobile que cela a du sens ». Il a raison, et c'etait sa decision d'origine du
   26/07. L'argument qui l'avait elargie, « une adresse et une enumeration sont deux
   natures d'information », est theorique ; au large il y a la place, et separer creuse un
   trou sous les tags. Le repli hasardeux des tags, lui, n'existe que quand la largeur
   manque. */

.zp-footer .zp-pillar-content:not([hidden]) {
    display: inline-block;
}
.zp-footer .zp-pillar-name {
    color: var(--text, #e5e7eb) !important;
    font-weight: 700 !important;
    letter-spacing: 0.4px;
    font-size: 0.7rem;
}
.zp-footer .zp-pillar-name:hover {
    color: var(--accent, #6c8aff) !important;
}
.zp-footer .zp-pillar-url {
    font-style: italic;
    font-size: 0.74rem;
    color: var(--accent, #6c8aff) !important;
    text-decoration: underline !important;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
    font-weight: 600;
}
.zp-footer .zp-pillar-url:hover {
    opacity: 0.85;
}
/* URL du site courant : elle reste affichee, elle situe le visiteur, mais elle
   cesse d'etre un lien vers la page ou il se trouve deja. Elle perd donc l'accent
   et le soulignement, garde l'italique qui la designe comme une adresse, et recoit
   le meme remplissage que le nom : c'est le signe « vous etes ici » du bandeau.
   Les !important repondent a ceux de la regle ci-dessus. */
.zp-footer .zp-pillar-url--self {
    color: var(--text-muted, #6b7280) !important;
    text-decoration: none !important;
    font-weight: 500;
    background: var(--accent-soft, rgba(108, 138, 255, 0.13));
    border-radius: 4px;
    padding: 1px 6px;
    box-decoration-break: clone;
    -webkit-box-decoration-break: clone;
}
.zp-footer .zp-pillar-keywords {
    color: var(--text-muted, #a5a8b3);
}
.zp-footer .zp-pillar-colon {
    color: var(--text-muted, #6b7280);
    margin: 0 5px;
    opacity: 0.5;
}
/* ============================================================
   Rendus des piliers (parametre PillarStyle du composant)

   Le comportement ne change JAMAIS d'un rendu a l'autre : un volet toujours ouvert, le clic
   passe de l'un a l'autre. Seul le dessin change, et aucun de ces rendus n'ajoute une ligne
   de script ni ne touche au balisage. Les <a href> que suivent les moteurs restent intacts.

   Issus de l'atelier du 30/07/2026 (docs/design/atelier-zpfooter-piliers.html), ne au constat
   de Regis : rien ne disait a l'oeil que la ligne d'URL et de mots-cles appartient au pilier
   selectionne. Les trois pastilles se lisaient comme des boutons autonomes, la ligne comme du
   texte qui flotte.

   Surface du panneau, OPAQUE : l'onglet actif la partage et recouvre donc franchement la
   bordure haute du panneau. Avec un fond translucide, cette bordure transparaissait et
   refermait l'onglet au lieu de l'ouvrir sur son contenu.
   ============================================================ */
.zp-footer {
    --zp-panneau-surface: color-mix(in srgb, var(--accent, #6c8aff) 8%, var(--bg-card, #1a1d27));
}

/* --- Onglets : l'onglet actif se rattache au panneau (ZpPillarStyle.Tabs) --- */
.zp-footer--onglets .zp-pillar-selector {
    /* Disposition flex explicite : sans elle, gap et align-items ne s'appliquent a rien, et
       les onglets se retrouvaient bord a bord, l'ecart mesure a zero. La rangee de base est
       un <p> dont les pastilles sont des inline-block, ou l'espacement vient du blanc du
       balisage ; un jeu d'onglets, lui, aligne ses onglets sur leur bas. */
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    /* Une unite pleine de la grille, reglable par --zp-footer-ecart-onglets. Les 4 px du
       premier jet ne se voyaient pas a cette taille de pastille : les onglets se lisaient
       colles (constat de Regis, 30/07/2026). Un jeu d'onglets classique les met bord a bord ;
       ici ils gardent leur forme de pastille, donc ils demandent l'air qui va avec. */
    gap: var(--zp-footer-ecart-onglets);
    margin-block: 0;
    align-items: flex-end;
}
.zp-footer--onglets .zp-pillar-selector .zp-sep { display: none; }
.zp-footer--onglets .zp-pillar-link {
    position: relative;
    z-index: 1;
    border-radius: 6px 6px 0 0;
    border-bottom-color: transparent;
    margin-bottom: -1px;
    padding: 5px 12px;
    background: transparent;
}
.zp-footer--onglets .zp-pillar-link.zp-pillar-link-active {
    background: var(--zp-panneau-surface);
    border-color: var(--accent, #6c8aff);
    border-bottom-color: transparent;
}
/* Le survol ne touche que les onglets FERMES : celui qui est deja ouvert n'a rien a
   promettre, et le voir changer sous le curseur laisserait croire qu'il reste a cliquer.
   La bordure basse reste transparente au survol, sinon l'onglet se refermerait a l'oeil. */
.zp-footer--onglets .zp-pillar-link:not(.zp-pillar-link-active):hover {
    background: color-mix(in srgb, var(--accent, #6c8aff) 12%, transparent);
    border-bottom-color: transparent;
}
.zp-footer--onglets .zp-pillar-link.zp-pillar-link-active:hover {
    background: var(--zp-panneau-surface);
}
.zp-footer--onglets .zp-pillar-detail {
    align-self: stretch;
    background: var(--zp-panneau-surface);
    border: 1px solid var(--accent, #6c8aff);
    /* Bord haut RECTILIGNE, bas arrondi. C'est la geometrie d'un panneau a onglets : son
       arete haute est la ligne sur laquelle les onglets se posent, et une ligne sur laquelle
       on pose quelque chose ne s'incurve pas.
       Deux jets ont precede celui-ci, chacun corrigeant a moitie. D'abord un seul coin carre
       en haut a gauche, convention d'un jeu d'onglets dont l'actif est LE PREMIER : des qu'un
       autre etait selectionne, cet angle ne repondait plus a rien. Puis quatre coins
       arrondis, qui reglaient la symetrie mais faisaient surplomber une courbe par l'angle
       droit d'un onglet place a un bout (constat de Regis, 30/07/2026, « Coach » selectionne
       sur mobile). Un bord droit vaut pour TOUTES les positions, sans rien avoir a savoir de
       l'onglet selectionne. */
    border-radius: 0 0 6px 6px;
    padding: 8px 12px;
    /* L'onglet actif TOUCHE son panneau : on reprend l'ecart que le panneau met entre ses
       blocs, sans quoi ils restent separes et le rattachement ne se voit pas. */
    margin-top: calc(-1 * var(--zp-footer-ecart));
}

/* --- Le slash ouvre chaque pilier (parametre PillarSlashPrefix) ---
   Decision d'IDENTITE et non de dessin : elle se combine avec n'importe lequel des rendus.
   Le signe est celui de « Slasher », et il ouvre deja chacun des tags du heros de
   regis.scyeur.net. Les separateurs s'effacent avec lui, sans quoi on lirait
   « / Slasher / / Digital Architect ». */
.zp-footer--slash .zp-pillar-selector .zp-sep { display: none; }
.zp-footer--slash .zp-pillar-link::before {
    content: "/ ";
    opacity: 0.55;
    font-weight: 400;
}
.zp-footer--slash .zp-pillar-link.zp-pillar-link-active::before { opacity: 0.8; }

/* --- Triangle : un ergot designe le panneau (ZpPillarStyle.Pointer) ---
   Le triangle appartient au PILIER, donc il le suit sans qu'aucun code ne le positionne. */
.zp-footer--triangle .zp-pillar-selector { margin-bottom: 0; }
.zp-footer--triangle .zp-pillar-link { position: relative; }
.zp-footer--triangle .zp-pillar-link.zp-pillar-link-active::after {
    content: "";
    position: absolute;
    top: calc(100% + 4px);
    left: 50%;
    transform: translateX(-50%);
    border-left: 7px solid transparent;
    border-right: 7px solid transparent;
    border-top: 7px solid var(--accent, #6c8aff);
}
.zp-footer--triangle .zp-pillar-detail {
    align-self: stretch;
    background: color-mix(in srgb, var(--accent, #6c8aff) 8%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent, #6c8aff) 35%, transparent);
    border-radius: 8px;
    padding: 8px 12px;
}

/* --- Encoche : le panneau se creuse (ZpPillarStyle.Well) ---
   Le lien passe par la contenance et non par la designation : c'est le plus discret, et le
   seul qui ne dise pas QUEL pilier commande le panneau. */
.zp-footer--encoche .zp-pillar-detail {
    align-self: stretch;
    background: color-mix(in srgb, var(--text, #e5e7eb) 9%, transparent);
    border: 1px solid color-mix(in srgb, var(--text, #e5e7eb) 14%, transparent);
    border-radius: 8px;
    padding: 8px 12px;
    box-shadow: inset 0 2px 4px color-mix(in srgb, var(--text, #e5e7eb) 16%, transparent);
}

/* --- Le copyright change de place avec l'etat du tiroir (parametre MetaInPanel) ---
   Replie, il est dans la barre, comme partout ailleurs. Ouvert, la barre s'efface et il
   reparait en tete du panneau : la mention reste donc visible en permanence, et jamais en
   double. C'est un deplacement, pas une disparition (demande Regis, 30/07/2026). */
.zp-footer--meta-dans-panneau:not(.collapsed) {
    padding: 0;
    border-top: 0;
}
.zp-footer--meta-dans-panneau:not(.collapsed) .zp-footer-content--meta {
    display: none;
}
.zp-footer--meta-dans-panneau.collapsed .zp-footer-copyright {
    display: none;
}
/* Nom de l'environnement, en TETE de ligne, suivi d'un « / » comme les autres segments : le
   vocabulaire du pied plutot qu'une pastille rapportee, et l'ordre du general au particulier,
   l'environnement qualifiant tout ce qui suit (choix Regis, 30/07/2026 : « plus discrete si
   simple slash », puis « avant le numero de version », puis « en premiere position, avant le
   copyright »). Il se lit ainsi avant meme qu'on ait lu la signature.
   Le repere est court, DEV ou TEST, jamais le nom brut d'ASP.NET : voir EnvironmentLabel.
   Il garde la casse et l'interlettrage, qui le font lire comme un REPERE et non comme du
   contenu : sans eux, « Development » passerait pour un mot de la signature.
   La couleur est l'ACCENT du pied, jamais une teinte inventee : un premier jet posait un
   ambre #B45309, choisi pour sa lisibilite mais etranger aux palettes des sites, ce qui
   denaturait la page (constat de Regis, 30/07/2026).
   Contrepartie assumee : plus discret, il se voit moins sur une capture prise de loin, ce
   qui etait pourtant son objet. */
.zp-footer .zp-footer-env {
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--accent, #6c8aff);
}

.zp-footer .zp-footer-copyright {
    font-size: 0.72rem;
    color: var(--text-muted, #6b7280);
}
.zp-footer .zp-footer-copyright-annee {
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.04em;
}

.zp-footer .zp-footer-meta {
    font-size: 0.92rem;
    opacity: 0.95;
    /* Ni marge ni padding haut, et surtout aucune variante selon l'etat replie : depuis
       que le tiroir est un calque, la barre ne contient plus que cette ligne et son
       espacement vient du padding de .zp-footer. Les 14 px de marge et 10 px de padding
       d'avant separaient la ligne du tiroir qui la surmontait DANS le flux ; la regle
       .collapsed qui les remettait a zero les compensait. Garder l'un ou l'autre ferait
       osciller la barre d'environ 39 px a chaque ouverture, soit exactement le defaut
       que cette refonte supprime.
       Aucune border-top non plus : celle de .zp-footer separe deja la page du pied. */
}
.zp-footer .zp-footer-version {
    opacity: 0.85;
}
/* Drawer-handle toggle : a small rounded tab that pokes up out of the footer's
   top border, centered, like a bottom-sheet pull handle. Click pulls the
   ecosystem drawer open / pushes it shut.
   - Expanded (drawer open)   : chevron points down (⌄)  = "push me down to close"
   - Collapsed (drawer closed): chevron points up   (⌃)  = "pull me up to open"
   The tab shares the --bg-card material so it reads as a panel lifted out of
   the footer edge. A subtle opacity pulse on the icon draws the eye to incite
   discovery; it pauses on hover to confirm interactivity. */
@keyframes zp-footer-handle-pulse {
    0%, 100% { opacity: 0.65; }
    50%      { opacity: 1; }
}
.zp-footer .zp-footer-handle {
    position: absolute;
    left: 50%;
    bottom: 100%;
    transform: translateX(-50%);
    margin-bottom: -1px; /* overlap the footer border so the tab merges into it */
    box-sizing: border-box;
    width: 54px;
    height: var(--zp-footer-handle-h);
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-card, #1a1d27);
    border: 1px solid var(--border, #e5e7eb);
    /* transparent (not none) so top and bottom borders stay symmetric : keeps
       the flex content-box centered, so the chevron sits dead-centre. The
       bottom edge overlaps the footer border via margin-bottom, staying invisible. */
    border-bottom-color: transparent;
    border-radius: 10px 10px 0 0;
    color: var(--accent, #6c8aff);
    cursor: pointer;
    padding: 0;
    transition: background 0.15s, color 0.15s;
}
.zp-footer .zp-footer-handle:hover {
    background: var(--bg-input, #22252f);
}
.zp-footer .zp-footer-handle:focus-visible {
    outline: 1px dotted var(--accent, #6c8aff);
    outline-offset: 2px;
}
/* Single SVG chevron that flips with the drawer state. SVG (not a Unicode
   arrowhead) so the V is symmetric, crisp and perfectly centered — no font
   metric quirks, no diagonal-corner artefact.
   - Expanded  : points down (⌄) = "push me down to close"
   - Collapsed : flipped 180° → points up (⌃) = "pull me up to open"
   A subtle opacity pulse draws the eye; it pauses on hover. */
.zp-footer .zp-footer-handle-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: transform 0.3s ease;
    animation: zp-footer-handle-pulse 2.5s ease-in-out infinite;
}
.zp-footer .zp-footer-handle-icon svg {
    display: block;
}
.zp-footer .zp-footer-handle:hover .zp-footer-handle-icon {
    animation-play-state: paused;
    opacity: 1;
}
.zp-footer.collapsed .zp-footer-handle-icon {
    transform: rotate(180deg);
}
.zp-footer .zp-sep {
    color: var(--text-muted, #6b7280);
    margin: 0 5px;
    opacity: 0.5;
}
/* Separateur des MOTS-CLES, distinct de .zp-sep (demande Regis 26/07/2026).
   La barre oblique est le signe identitaire du pilier « Slasher » : l'employer aussi
   comme separateur d'enumeration brouille son sens. Le point median est deja en usage
   dans les pastilles de la carte (« Belgique · Maroc »), l'ecosysteme reste coherent.
   Classe separee et non remplacement global : entre les trois piliers, la barre reste. */
.zp-footer .zp-kw-sep {
    color: var(--text-muted, #6b7280);
    margin: 0 5px;
    opacity: 0.5;
}
@media (max-width: 600px) {
    .zp-footer { padding: 20px 16px; }
    /* Le panneau porte son propre padding depuis qu'il est hors flux : il suit celui de
       la barre plutot que d'en heriter. */
    .zp-footer .zp-footer-ecosystem { padding: 16px 16px var(--zp-footer-ecart); }
    .zp-footer .zp-footer-author { font-size: 0.85rem; }


    /* Poignee plus haute et plus large au doigt qu'a la souris. Venue elle aussi de la
       feuille de zebrapuma.be, ou elle ne profitait qu'a un site. La hauteur passe par la
       variable, donc la marge qui lui reserve sa place suit sans etre recopiee. */
    .zp-footer .zp-footer-handle { width: 64px; }

    /* Pilier ouvert : l'URL sur une ligne, les mots-cles sur la suivante (demande Regis
       du 26/07/2026). Ils se repliaient sinon n'importe ou, coupant l'enumeration au
       milieu. Le deux-points disparait, il n'a plus rien a separer une fois les deux
       blocs sur des lignes distinctes.
       La reservation passe de 1.5em a 3em EN MEME TEMPS : le panneau reserve la place du
       detail pour ne pas sauter au clic (cf. commentaire de .zp-pillar-detail), et deux
       lignes affichees contre une reservee reintroduiraient exactement ce saut. */
    .zp-footer .zp-pillar-detail { min-height: 2lh; }
    .zp-footer .zp-pillar-keywords { display: block; }
    .zp-footer .zp-pillar-colon { display: none; }
}


/* --- Cascade control (shared base for all dropdowns) --- */
.cascaded-dropdown { display: flex; flex-direction: column; gap: 6px; }

.cascade-separator {
    border-bottom: 1px solid var(--border);
    margin: 2px 0;
}

/* --- Drag handle for reorderable facets --- */
.cascade-level.reorderable {
    position: relative;
}

.drag-handle {
    position: absolute;
    left: -3px;
    top: 4px;
    bottom: 4px;
    width: 3px;
    user-select: none;
    z-index: 1;
    border-left: 1px solid var(--border);
    border-right: 1px solid var(--border);
    border-radius: 1px;
    opacity: 0.3;
    transition: opacity 0.2s, border-color 0.2s;
}

.cascade-level.reorderable:hover .drag-handle {
    opacity: 0.6;
}

.drag-handle:hover {
    opacity: 1 !important;
    border-color: var(--accent);
}

/* SortableJS drag states */
.sortable-ghost {
    opacity: 0.3;
}

.sortable-chosen {
    outline: 1px solid var(--accent);
    outline-offset: 1px;
    border-radius: 6px;
}

.sortable-drag {
    opacity: 0.9;
}

.cascade-level.reorderable > .ms-dropdown,
.cascade-level.reorderable > .ss-dropdown,
.cascade-level.reorderable > .rs-container { width: 100%; }

.cascade-control {
    width: 100%;
    padding: 6px 10px;
    padding-right: 28px;
    font-size: 0.82rem;
    font-family: var(--font);
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--text);
    cursor: pointer;
    outline: none;
    box-sizing: border-box;
    height: 32px;
    appearance: none;
    -webkit-appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%236b7280' stroke-width='1.5' fill='none'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
}

.cascade-control:focus { border-color: var(--accent); }
.cascade-control:not(.has-value) { color: var(--text-muted); font-style: italic; }
.cascade-control.has-value { color: var(--text); font-style: normal; }
.cascade-control option:first-child { color: var(--text-muted); font-style: italic; }
.cascade-control option { color: var(--text); font-style: normal; }

/* --- Shared trigger base (details/summary dropdowns) --- */
.zp-trigger {
    list-style: none;
    user-select: none;
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    line-height: calc(32px - 12px - 2px);
}

.zp-trigger::-webkit-details-marker { display: none; }

/* --- Shared panel base (dropdown options container) --- */
.zp-panel {
    display: flex;
    flex-direction: column;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-top: none;
    border-radius: 0 0 6px 6px;
    max-height: 180px;
    overflow-y: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--border) transparent;
}

.zp-panel::-webkit-scrollbar { width: 5px; }
.zp-panel::-webkit-scrollbar-track { background: transparent; }
.zp-panel::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }

/* --- Shared option base --- */
.zp-option {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 10px;
    font-size: 0.8rem;
    color: var(--text);
    text-decoration: none;
    transition: background 0.1s;
    border-bottom: 1px solid rgba(255,255,255,0.03);
}

.zp-option:last-child { border-bottom: none; }
.zp-option:hover { background: rgba(255,255,255,0.04); }
.zp-option.on { color: var(--accent); }

/* --- MultiSelectDropdown --- */
.ms-dropdown { }
.ms-details { position: relative; }
.ms-details[open] .ms-trigger { border-radius: 6px 6px 0 0; }

.ms-check {
    width: 14px;
    height: 14px;
    border: 1.5px solid var(--border);
    border-radius: 3px;
    flex-shrink: 0;
    position: relative;
    transition: all 0.15s;
}

.ms-option.on .ms-check {
    background: var(--accent);
    border-color: var(--accent);
}

.ms-option.on .ms-check::after {
    content: '';
    position: absolute;
    left: 3px;
    top: 1px;
    width: 5px;
    height: 8px;
    border: solid #fff;
    border-width: 0 2px 2px 0;
    transform: rotate(40deg);
}

.ms-option.on .ms-label { color: var(--accent); }
.ms-label { flex: 1; }
.ms-count { color: var(--text-muted); font-size: 0.7rem; min-width: 20px; text-align: right; }

/* --- SearchableDropDown (reuses zp-trigger, zp-panel, zp-option, ss-* for DRY) --- */
.sd-details { position: relative; }
.sd-details[open] .ss-trigger { border-radius: 6px 6px 0 0; }
.sd-details .zp-panel { max-height: 300px; }

/* Overlay input: hidden when closed, covers the summary when open */
.sd-overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 32px;
    padding: 6px 10px;
    padding-right: 28px;
    border: 1px solid var(--accent);
    border-radius: 6px 6px 0 0;
    background: var(--bg-input);
    color: var(--text);
    font-size: 0.82rem;
    font-family: var(--font);
    outline: none;
    box-sizing: border-box;
    z-index: 2;
    opacity: 0;
    pointer-events: none;
}

.sd-details[open] .sd-overlay {
    opacity: 1;
    pointer-events: all;
}

.sd-overlay::placeholder { color: var(--text-muted); font-style: italic; }

/* Chevron click zone: invisible overlay on right side, pointer cursor */
.sd-chevron-zone {
    position: absolute;
    top: 0;
    right: 0;
    width: 28px;
    height: 32px;
    z-index: 3;
    cursor: pointer;
    opacity: 0;
    pointer-events: none;
}

.sd-details[open] .sd-chevron-zone {
    pointer-events: all;
}

/* Keyboard navigation highlight (lighter than active .on) */
.sd-highlight { background: rgba(255,255,255,0.06); color: var(--accent); opacity: 0.7; }
.sd-highlight.on { opacity: 1; }

/* Preview text in input during arrow navigation */
.sd-preview { color: var(--text-muted); font-style: italic; }

/* Chevron on overlay (mirrors cascade-control) */
.sd-overlay {
    background-image: url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%236b7280' stroke-width='1.5' fill='none'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
}

/* --- SingleSelectDropDown --- */
.ss-dropdown { }
.ss-details { position: relative; }
.ss-details[open] .ss-trigger { border-radius: 6px 6px 0 0; }

.ss-option.on { font-weight: 600; }
.ss-option.clear { color: var(--text-muted); font-style: italic; }
.ss-label { flex: 1; }
.ss-count { color: var(--text-muted); font-size: 0.7rem; min-width: 20px; text-align: right; }

/* --- FilterTag --- */
.filter-tag {
    display: inline-flex;
    align-items: stretch;
    gap: 0;
    padding: 0;
    border-radius: 20px;
    font-size: 0.82rem;
    font-weight: 500;
    text-decoration: none;
    cursor: pointer;
    transition: all 0.15s;
    overflow: hidden;
}

.filter-tag:hover { filter: brightness(1.2); }
.ft-key { padding: 6px 10px; opacity: 0.7; }
.ft-sep { width: 1px; align-self: stretch; }
.ft-val { padding: 6px 8px; color: #fff; font-weight: 600; }
.filter-tag .filter-x { padding: 6px 10px 6px 4px; }

.filter-tag .ft-word {
    padding: 6px 8px;
    font-weight: 600;
    text-decoration: none;
}
.filter-tag .ft-word:hover { color: #fff; }
.filter-tag .ft-word:hover .filter-x { color: var(--red); opacity: 1; }

.filter-x {
    font-size: 1.1rem;
    line-height: 1;
    opacity: 0.6;
    transition: opacity 0.15s;
}

.filter-tag:hover .filter-x { opacity: 1; color: var(--red); }

/* --- RangeSlider --- */
.rs-container {
    padding: 6px 10px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--bg-input);
    box-sizing: border-box;
}

.rs-bottom {
    display: flex;
    justify-content: space-between;
    align-items: center;
    position: relative;
    margin-top: 2px;
    height: 16px;
}

.rs-tick {
    font-size: 0.65rem;
    color: var(--text-muted);
}

.rs-selection {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    font-size: 0.72rem;
    color: var(--accent);
    font-weight: 600;
    white-space: nowrap;
}

.rs-track-wrapper {
    position: relative;
    height: 28px;
}

.rs-track {
    position: absolute;
    top: 12px;
    left: 0;
    right: 0;
    height: 4px;
    background: var(--border);
    border-radius: 2px;
}

.rs-fill {
    position: absolute;
    top: 0;
    height: 100%;
    background: var(--accent);
    border-radius: 2px;
}

.rs-input {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 28px;
    margin: 0;
    padding: 0;
    background: none;
    pointer-events: none;
    -webkit-appearance: none;
    appearance: none;
}

.rs-input::-webkit-slider-runnable-track {
    height: 4px;
    background: transparent;
}

.rs-input::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--accent);
    border: 2px solid var(--bg-card);
    cursor: pointer;
    pointer-events: all;
    margin-top: -6px;
    transition: box-shadow 0.15s;
}

.rs-input::-webkit-slider-thumb:hover {
    box-shadow: 0 0 0 4px rgba(108, 138, 255, 0.25);
}

.rs-input::-moz-range-track {
    height: 4px;
    background: transparent;
    border: none;
}

.rs-input::-moz-range-thumb {
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--accent);
    border: 2px solid var(--bg-card);
    cursor: pointer;
    pointer-events: all;
}

.rs-input::-moz-range-thumb:hover {
    box-shadow: 0 0 0 4px rgba(108, 138, 255, 0.25);
}

/* .rs-ticks removed — replaced by .rs-bottom */
.rs-ticks-deprecated {
    display: none;
    font-size: 0.68rem;
    color: var(--text-muted);
    margin-top: 2px;
}

/* ============================================================
   ZpDataExplorer (zpx-) : URL-driven data consultation building
   blocks (ADR-021) : ZpDataGrid / ZpGridColumn / ZpPaginationNav.
   Adapted COPY of the admin dashboard patterns (.ejp-table and
   .chip-* from zp-admin.css, .ejp-group health borders from the
   CatalogStatus page) under a non-colliding zpx- prefix, with
   fallback values on every variable. The .ejp-* / .chip-*
   originals stay untouched until every page migrates.
   ============================================================ */

/* --- ZpDataGrid : table (generalization of .ejp-table) --- */
/* Scroll guard around each table : with table-layout fixed the table grows to
   the sum of its colgroup widths when that exceeds the container, and the
   wrapper turns the overflow into a horizontal scroll (never an overlap). */
.zpx-table-scroll { overflow-x: auto; }
.zpx-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; table-layout: fixed; }
.zpx-table th {
    text-align: left;
    padding: 8px 12px;
    background: var(--bg-input, #f3f4f6);
    border-top: 1px solid var(--border, #e5e7eb);
    border-bottom: 1px solid var(--border, #e5e7eb);
    font-weight: 600;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-muted, #6b7280);
    white-space: nowrap;
    /* Fixed layout safety : a header wider than its column degrades to an
       ellipsis instead of overlapping the next header. */
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Opt-in utility (ZpGridColumn CssClass="zpx-wrap") : allow a multi-word header
   to wrap on two lines inside a narrow column (cells stay nowrap). */
.zpx-table th.zpx-wrap { white-space: normal; line-height: 1.25; }
.zpx-table td {
    padding: 8px 12px;
    border-bottom: 1px solid var(--border, #e5e7eb);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Dense variant (ZpDataGrid CssClass="zpx-table--dense") : tighter horizontal
   padding for wide numeric grids (row rhythm preserved). Declared AFTER the th
   and td base rules : same specificity, the variant must win. */
.zpx-table--dense th,
.zpx-table--dense td { padding-left: 8px; padding-right: 8px; }
.zpx-table tr:last-child td { border-bottom: none; }
.zpx-table tbody tr:hover td { background: var(--accent-soft, rgba(108, 138, 255, 0.05)); }
.zpx-table code { font-family: var(--mono, ui-monospace, monospace); font-size: 0.85rem; }

/* Numeric column (ZpAlign.Right) : right-aligned, mono digits in cells. */
.zpx-num { text-align: right; }
.zpx-table td.zpx-num { font-family: var(--mono, ui-monospace, monospace); }
/* Centered column (ZpAlign.Center). */
.zpx-center { text-align: center; }

/* Sortable header links + active direction indicator (ZpSortState). */
.zpx-sort-link { color: inherit; text-decoration: none; }
.zpx-sort-link:hover { color: var(--text, #111827); }
.zpx-sort-link.on { color: var(--accent, #6c8aff); }
.zpx-sort-indicator { margin-left: 4px; font-size: 0.6rem; }

/* Empty result set (EmptyText). */
.zpx-empty { color: var(--text-muted, #6b7280); padding: 12px; }

/* Navigable row (RowNavigateUrl) : the whole row is a drill-down click target.
   Hoisted from the History admin page local styles (xRefIQ #483). */
.zpx-table .zpx-row-clickable { cursor: pointer; }
/* Keyboard focus ring (tabindex=0 rows), same pattern as .zpx-group-summary. */
.zpx-table .zpx-row-clickable:focus-visible { outline: 2px solid var(--accent, #6c8aff); outline-offset: -2px; }
.zpx-table .zpx-row-clickable:hover td { background: var(--accent-soft, rgba(108, 138, 255, 0.06)); }
.zpx-table .zpx-row-expanded > td { background: var(--accent-soft-strong, rgba(108, 138, 255, 0.10)); }
.zpx-table .zpx-detail-row td { cursor: default; }

/* URL-driven detail row (RowDetail) : full-width drill-down under its row. */
.zpx-table .zpx-detail-row td {
    padding: 14px 16px;
    background: var(--bg-card, #ffffff);
    border-top: 1px solid var(--border, #e5e7eb);
    border-bottom: 2px solid var(--accent, #6c8aff);
    white-space: normal;
}

/* --- ZpDataGrid : groups. One <details> card per group, health signal
       as a 4px left border (green / orange / red / yellow). --- */
.zpx-group {
    background: var(--bg-card, #ffffff);
    border: 1px solid var(--border, #e5e7eb);
    border-left-width: 4px;
    border-radius: var(--radius, 8px);
    margin-bottom: 10px;
    overflow: hidden;
    transition: border-color 0.2s;
}

/* Sous-groupes (xRefIQ #484/#485, SubGroupBy) : carte imbriquee plus discrete,
   indentee, meme signal sante par bordure gauche que les groupes. */
.zpx-subgroup {
    background: var(--bg-card, #ffffff);
    border: 1px solid var(--border, #e5e7eb);
    border-left-width: 3px;
    border-radius: var(--radius, 8px);
    margin: 0 10px 8px 22px;
    overflow: hidden;
}
.zpx-subgroup > summary.zpx-group-summary { padding: 8px 12px; font-size: 0.9rem; }
.zpx-subgroup .zpx-group-title { font-size: 0.9rem; }

/* Sante PAR LIGNE (xRefIQ #484/#485) : liseret gauche via box-shadow inset
   (une bordure sur <tr> est inconsistante entre navigateurs). La page pose la
   classe via RowCssClass. */
.zpx-table tr.zpx-row-health-healthy { box-shadow: inset 3px 0 0 var(--green, #4ade80); }
.zpx-table tr.zpx-row-health-partial { box-shadow: inset 3px 0 0 var(--orange, #fb923c); }
.zpx-table tr.zpx-row-health-failed { box-shadow: inset 3px 0 0 var(--red, #f87171); }
.zpx-table tr.zpx-row-health-neverrun { box-shadow: inset 3px 0 0 var(--yellow, #facc15); }
.zpx-group-summary {
    padding: 12px 16px;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 16px;
    user-select: none;
}
.zpx-group-summary:hover { background: var(--accent-soft, rgba(108, 138, 255, 0.05)); }
/* Mouse click : no browser focus ring (it fights the health border) ;
   keyboard users keep it via :focus-visible (a11y). */
.zpx-group-summary:focus { outline: none; }
.zpx-group-summary:focus-visible { outline: 2px solid var(--accent, #6c8aff); outline-offset: -2px; }
.zpx-group-title strong { font-size: 1.05rem; }
.zpx-group-subtitle { color: var(--text-muted, #6b7280); font-size: 0.85rem; }
.zpx-health-healthy  { border-left-color: var(--green, #4ade80); }
.zpx-health-partial  { border-left-color: var(--orange, #fb923c); }
.zpx-health-failed   { border-left-color: var(--red, #f87171); }
.zpx-health-neverrun { border-left-color: var(--yellow, #facc15); }

/* --- ZpDataGrid : mode de groupement Flat (GroupLayout, xRefIQ #567) ---
       UNE table, UN thead, UN colgroup, puis un <tbody> par groupe : forme HTML
       canonique d'un groupement tabulaire. Additions pures : rien au-dessus n'est
       modifie, et chaque regle est ancree sur du markup que SEULE une page ayant
       demande le mode Flat (ou l'en-tete collant) fait apparaitre.

       REGLE DE PORTEE, non negociable ici : aucun selecteur de DESCENDANCE nu du
       genre `.zpx-table tbody th` ou `.zpx-table--sticky thead th`. Un consommateur
       rend sa propre table DANS une ligne de detail (RowDetail) : ses th sont alors
       des descendants du tbody de la grille, et une telle regle les capturerait en
       mode Cards par defaut, avec une specificite qui bat le style de la page.
       Chaque selecteur part donc d'une classe posee par ce mode (.zpx-group-row,
       .zpx-subgroup-row, tbody.zpx-group) ou descend en enfant direct. Verrouille
       par ZpDataGridFlatLayoutTests.CardsLayout_NestedTableInARowDetail_* --- */

/* En-tete collant (StickyHeader). Le box-shadow est OBLIGATOIRE : en
   border-collapse: collapse, la bordure basse d'un th appartient a la table et ne
   suit donc PAS la cellule collee au defilement. Le fond du th est deja opaque
   (var(--bg-input) plus haut) : ne jamais le rendre semi-transparent, les lignes
   defileraient au travers. L'offset vertical est une variable posee par la page
   (hauteur de son propre bandeau colle), pas un parametre du composant : le
   composant qui l'ignorerait serait le seul a ne pas savoir ou commence la page.
   Chaine d'enfants directs : une table imbriquee dans une ligne de detail ne doit
   pas voir ses propres en-tetes devenir collants. */
.zpx-table--sticky > thead > tr > th {
    position: sticky;
    top: var(--zpx-head-top, 0px);
    z-index: 5;
    box-shadow: inset 0 -1px 0 var(--border, #e5e7eb);
}

/* Le <tbody> porte .zpx-group : la carte (fond, contour, coins arrondis) n'a pas de
   sens sur un groupe de lignes, seul le regroupement compte. Le signal sante reste
   visible, porte par la ligne d'en-tete du groupe (.zpx-row-health-*). */
.zpx-table tbody.zpx-group {
    background: transparent;
    border: none;
    border-radius: 0;
    margin-bottom: 0;
}

/* Separation entre deux groupes consecutifs : la seule frontiere visible entre
   deux tbody, puisqu'il n'y a plus de carte pour les delimiter. */
.zpx-table tbody.zpx-group + tbody.zpx-group > tr:first-child > td,
.zpx-table tbody.zpx-group + tbody.zpx-group > tr:first-child > th {
    border-top: 2px solid var(--border, #e5e7eb);
}

/* Ligne d'en-tete d'un groupe, premiere ligne de son tbody. Les cellules viennent
   de la page (GroupRowCells) ou du repli pleine largeur. */
.zpx-table tr.zpx-group-row > td,
.zpx-table tr.zpx-group-row > th {
    background: var(--bg-input, #f3f4f6);
    font-weight: 600;
    white-space: nowrap;
}

/* Ligne d'en-tete d'un sous-groupe : meme tbody, un cran plus discret et indente. */
.zpx-table tr.zpx-subgroup-row > td,
.zpx-table tr.zpx-subgroup-row > th {
    background: var(--bg-card, #ffffff);
    font-weight: 600;
    white-space: nowrap;
}
.zpx-table tr.zpx-subgroup-row > :first-child { padding-left: 24px; }

/* Une ligne de groupe peut commencer par un <th scope="rowgroup"> : c'est la forme
   accessible d'un en-tete de groupe de lignes. Sans ce reset, il heriterait du style
   des en-tetes de COLONNES (majuscules, 0.72rem, gris) et hurlerait. Ancre sur les
   deux classes de ligne de groupe, jamais sur `tbody th` : ces classes n'existent
   que dans le mode Flat, la ou ce th est reellement emis. Le FOND n'est pas remis
   ici : il appartient aux deux regles ci-dessus, de meme specificite, qui gagneraient
   ou perdraient selon l'ordre d'ecriture. */
.zpx-table tr.zpx-group-row > th,
.zpx-table tr.zpx-subgroup-row > th {
    text-transform: none;
    font-size: inherit;
    letter-spacing: normal;
    color: inherit;
}

/* Chiffres a chasse fixe dans les lignes d'une grille aplatie : c'est la ou une
   colonne de 200 nombres ondule si la chasse varie. Restreint aux cellules filles
   des lignes d'un groupe : les grilles en mode Cards gardent leur typographie, et
   une table imbriquee dans une ligne de detail n'est pas concernee. */
.zpx-table > tbody.zpx-group > tr > .zpx-num { font-variant-numeric: tabular-nums; }

/* --- ZpPaginationNav (hoist of the History pager + .ejp-icon-btn) --- */
.zpx-pagination { display: flex; align-items: center; justify-content: center; gap: 8px; flex-wrap: wrap; }
.zpx-pagination-summary { color: var(--text-muted, #6b7280); }
.zpx-pagination-label { color: var(--text-muted, #6b7280); }
.zpx-page-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: calc(var(--radius, 8px) - 2px);
    background: var(--bg-input, #f3f4f6);
    border: 1px solid var(--border, #e5e7eb);
    color: var(--text-muted, #6b7280);
    text-decoration: none;
    transition: all 0.15s;
    cursor: pointer;
}
.zpx-page-btn:hover { color: var(--text, #111827); }
.zpx-page-btn svg { width: 14px; height: 14px; flex-shrink: 0; }
.zpx-page-btn.disabled,
.zpx-page-btn[aria-disabled="true"] { opacity: 0.3; pointer-events: none; }
.zpx-pagesize { display: inline-flex; align-items: center; gap: 8px; }
.zpx-pagesize .ss-dropdown { width: 80px; }

/* --- Badges : copy of the zp-admin.css .chip-* palette under a
       non-colliding name (the .chip facette collision is resolved
       by this rename, ADR-021 section 4). --- */
/* The tone modifiers only set the two --zpx-tone-* custom properties; the badge
   consumes them, and so do the colored facet chips (ValueCssClass) : one tone
   declaration, several renderings. */
.zpx-badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 12px;
    font-size: 0.8rem;
    font-weight: 600;
    background: var(--zpx-tone-bg, transparent);
    color: var(--zpx-tone-fg, inherit);
}
.zpx-badge--success  { --zpx-tone-bg: rgba(74, 222, 128, 0.15);  --zpx-tone-fg: var(--green, #4ade80); }
.zpx-badge--warning  { --zpx-tone-bg: rgba(251, 146, 60, 0.15);  --zpx-tone-fg: var(--orange, #fb923c); }
.zpx-badge--failed   { --zpx-tone-bg: rgba(248, 113, 113, 0.15); --zpx-tone-fg: var(--red, #f87171); }
.zpx-badge--progress { --zpx-tone-bg: rgba(108, 138, 255, 0.15); --zpx-tone-fg: var(--accent, #6c8aff); }
.zpx-badge--skipped  { --zpx-tone-bg: rgba(139, 141, 151, 0.15); --zpx-tone-fg: var(--text-muted, #6b7280); }
.zpx-badge--unknown  { --zpx-tone-bg: rgba(250, 204, 21, 0.15);  --zpx-tone-fg: var(--yellow, #facc15); }
/* Badge as a link (e.g. stats chips toggling a facet) : same visual, plus
   hover feedback and an "active" ring when the matching filter is on. */
a.zpx-badge { text-decoration: none; cursor: pointer; }
a.zpx-badge:hover { filter: brightness(1.2); }
.zpx-badge.active { outline: 1px solid currentColor; outline-offset: 1px; }

/* --- ZpDataExplorer : two-column layout (generalization of the Search
       .results-layout of app.css, sidebar width driven by a variable). --- */
.zpx-layout {
    display: grid;
    grid-template-columns: var(--zpx-sidebar-width, 240px) 1fr;
    gap: 24px;
    align-items: start;
}
.zpx-layout--nofacets { grid-template-columns: 1fr; }
@media (max-width: 768px) {
    .zpx-layout { grid-template-columns: 1fr; }
}
/* Full-width page MARKER (ZpDataExplorer FullWidth) : the explorer only tags
   itself (it already spans 100% of its parent) ; the HOST's page wrappers opt
   their max-width out via :has(.zpx-page--full) (see app.css / zp-admin.css in
   the consuming site), keeping their own lateral padding. Page-level modality :
   content pages keep their max-width. */
.zpx-page--full { width: 100%; }
/* Right column : toolbar / segmented / stats / pagination / active view stack. */
.zpx-content { display: flex; flex-direction: column; gap: 12px; min-width: 0; }

/* --- ZpFacetsPanel : neutral facets sidebar (adapted copy of the Search
       .facets-sidebar / .facet-* patterns of app.css, fallback variables ;
       the originals stay untouched until the Search migrates). --- */
.zpx-sidebar { display: flex; flex-direction: column; gap: 10px; }
.zpx-facet-group {
    background: var(--bg-card, #ffffff);
    border: 1px solid var(--border, #e5e7eb);
    border-radius: var(--radius, 8px);
    overflow: hidden;
    /* Positioning context for the GroupHelp icon (#468). */
    position: relative;
}
.zpx-facet-header {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    padding: 10px;
    list-style: none;
    user-select: none;
}
summary.zpx-facet-header { cursor: pointer; }
.zpx-facet-header::-webkit-details-marker { display: none; }
.zpx-facet-title {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--accent, #6c8aff);
    font-weight: 700;
}
.zpx-facet-chevron {
    color: var(--text-muted, #6b7280);
    font-size: 0.7rem;
    margin-left: auto;
    transition: transform 0.2s;
}
details.zpx-facet-group[open] .zpx-facet-chevron { transform: rotate(180deg); }
/* Collapsed group : the header recaps the active values ; open : hidden
   (the controls themselves show the selection). */
.zpx-facet-summary-chips {
    display: flex;
    gap: 4px;
    flex-wrap: wrap;
    align-items: center;
    width: 100%;
    padding-top: 6px;
}
details.zpx-facet-group[open] .zpx-facet-summary-chips { display: none; }

/* --- Group legend (GroupHelp, #468) : native <details>, zero server state.
       The icon cannot be a child of the group <summary> (nested summary =
       invalid HTML + double toggle) : it renders AFTER the summary and the CSS
       positions it into the header row, left of the chevron. Collapsing the
       group hides it naturally (non-summary child of a closed <details>). A
       fragment rendering nothing for the group key leaves the panel :empty and
       the :has() guard hides the whole legend. --- */
.zpx-facet-help { margin: 0; }
.zpx-facet-help:has(> .zpx-facet-help-panel:empty) { display: none; }
.zpx-facet-help-icon {
    position: absolute;
    top: 10px;
    right: 32px;
    width: 16px;
    height: 16px;
    border: 1px solid var(--text-muted, #6b7280);
    border-radius: 50%;
    color: var(--text-muted, #6b7280);
    font-family: Georgia, 'Times New Roman', serif;
    font-size: 0.68rem;
    font-style: italic;
    font-weight: 600;
    line-height: 14px;
    text-align: center;
    cursor: pointer;
    list-style: none;
    user-select: none;
    box-sizing: border-box;
}
.zpx-facet-help-icon::-webkit-details-marker { display: none; }
.zpx-facet-help-icon:hover,
.zpx-facet-help[open] > .zpx-facet-help-icon {
    color: var(--accent, #6c8aff);
    border-color: var(--accent, #6c8aff);
}
/* Static groups have no chevron : the icon takes the right edge. */
.zpx-facet-group--static .zpx-facet-help-icon { right: 10px; }
/* Discreet in-flow panel at the top of the group body area. */
.zpx-facet-help-panel {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 8px 10px;
    border-bottom: 1px dashed var(--border, #e5e7eb);
    font-size: 0.75rem;
    line-height: 1.35;
    color: var(--text-muted, #6b7280);
}

.zpx-facet-body { padding: 10px 10px 12px; }
/* Non-interactive fixed context chips (ZpFacetGroup.FixedContext). */
.zpx-facet-fixed {
    display: flex;
    gap: 5px;
    flex-wrap: wrap;
    margin: 0 0 10px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--border, #e5e7eb);
}
.zpx-facet-section { margin: 0 0 10px; }

/* Drag-reorder des facettes (xRefIQ #498) : poignee revelee au survol de la section,
   dans un groupe Reorderable uniquement (le markup n'emet la poignee que la).
   Les etats SortableJS (.sortable-ghost/chosen/drag) sont les globaux existants. */
.zpx-facet-sortable > .zpx-facet-section { position: relative; }
.zpx-drag-handle {
    position: absolute;
    top: 0;
    right: 0;
    cursor: grab;
    color: var(--text-muted, #6b7280);
    font-size: 0.75rem;
    line-height: 1;
    padding: 2px 4px;
    opacity: 0;
    transition: opacity 0.15s ease;
    user-select: none;
}
.zpx-facet-sortable > .zpx-facet-section:hover .zpx-drag-handle { opacity: 1; }
.zpx-drag-handle:active { cursor: grabbing; }
.zpx-facet-section:last-child { margin-bottom: 0; }
.zpx-facet-label {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-muted, #6b7280);
    margin-bottom: 5px;
}

/* Chips (links) + header recap chips (spans) share the same visual. */
.zpx-facet-chips { display: flex; flex-wrap: wrap; gap: 5px; }
.zpx-facet-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 3px 10px;
    border-radius: 12px;
    font-size: 0.75rem;
    background: var(--bg-input, #f3f4f6);
    border: 1px solid var(--border, #e5e7eb);
    color: var(--text, #111827);
    text-decoration: none;
    transition: all 0.15s;
}
a.zpx-facet-chip { cursor: pointer; }
a.zpx-facet-chip:hover { border-color: var(--accent, #6c8aff); color: var(--accent, #6c8aff); }
.zpx-facet-chip.active { background: var(--accent, #6c8aff); border-color: var(--accent, #6c8aff); color: #fff; }
.zpx-facet-chip .zpx-count { font-size: 0.68rem; color: var(--text-muted, #6b7280); }
.zpx-facet-chip.active .zpx-count { color: rgba(255, 255, 255, 0.7); }
/* Inert MaxVisible truncation hint (no URL-driven "show more" in v1). */
.zpx-facet-more { font-size: 0.72rem; color: var(--text-muted, #6b7280); align-self: center; }

/* --- Colored facet chips (ValueCssClass mapping to a .zpx-badge--* tone) : the
       chip SHAPE wins over the badge shape (explicit combined rules, no order
       dependency), the tone drives the color. Filtering legend states :
       inactive = default chip skin + leading tone dot ; active = full badge
       tone + accent ring (stays distinguishable from the tone itself). --- */
.zpx-facet-chip.zpx-badge {
    display: inline-flex;
    padding: 3px 10px;
    font-size: 0.75rem;
    font-weight: 600;
    background: var(--bg-input, #f3f4f6);
    border-color: var(--border, #e5e7eb);
    color: var(--text, #111827);
}
.zpx-facet-chip.zpx-badge::before {
    content: "";
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--zpx-tone-fg, currentColor);
    flex-shrink: 0;
}
a.zpx-facet-chip.zpx-badge:hover {
    border-color: var(--zpx-tone-fg, var(--accent, #6c8aff));
    color: var(--text, #111827);
}
.zpx-facet-chip.zpx-badge.active {
    background: var(--zpx-tone-bg, var(--accent, #6c8aff));
    border-color: transparent;
    color: var(--zpx-tone-fg, #fff);
    outline: 2px solid var(--accent, #6c8aff);
    outline-offset: 1px;
}
.zpx-facet-chip.zpx-badge .zpx-count { color: inherit; opacity: 0.75; }

/* ShowValueChips legend panel under a DropDown / MultiSelect control : reads as
   "one filter + its legend" (discreet control, tight pills grid below). */
.zpx-facet-legend { margin-top: 6px; gap: 4px; }

/* Vertical link list with counts (ZpFacetKind.List). */
.zpx-facet-list { display: flex; flex-direction: column; }
.zpx-facet-item {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 3px 0;
    font-size: 0.85rem;
    /* Tone-aware: a ValueCssClass tone modifier (zpx-badge--*) wins over the default text color. */
    color: var(--zpx-tone-fg, var(--text, #111827));
    text-decoration: none;
    cursor: pointer;
}
.zpx-facet-item:hover { color: var(--zpx-tone-fg, var(--accent, #6c8aff)); filter: brightness(1.15); }
.zpx-facet-item .zpx-count { margin-left: auto; color: var(--text-muted, #6b7280); font-size: 0.75rem; }
.zpx-facet-item.active { color: var(--zpx-tone-fg, var(--accent, #6c8aff)); font-weight: 600; }

/* Single on/off link (ZpFacetKind.Toggle). */
.zpx-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 6px 12px;
    font-size: 0.8rem;
    border: 1px solid var(--border, #e5e7eb);
    border-radius: 8px;
    background: var(--bg-input, #f3f4f6);
    color: var(--text, #111827);
    text-decoration: none;
    cursor: pointer;
    transition: all 0.15s;
}
.zpx-toggle:hover { background: var(--border, #e5e7eb); }
.zpx-toggle.active {
    background: var(--accent, #6c8aff);
    border-color: var(--accent, #6c8aff);
    color: #fff;
    font-weight: 600;
}

/* GET form facets (TextInput / NumberInput), hidden params preserve URL state.
   #468 : consecutive input facets share ONE form (--group modifier) : stacked
   sections, then an actions row (single OK + optional Reset link). */
.zpx-facet-form { display: flex; align-items: center; gap: 6px; }
.zpx-facet-form--group { flex-direction: column; align-items: stretch; gap: 0; }
.zpx-facet-input-row { display: flex; align-items: center; gap: 6px; }
.zpx-facet-actions { display: flex; align-items: center; gap: 10px; margin-top: 2px; }
.zpx-facet-reset {
    font-size: 0.75rem;
    color: var(--text-muted, #6b7280);
    text-decoration: none;
}
.zpx-facet-reset:hover { color: var(--accent, #6c8aff); text-decoration: underline; }
.zpx-facet-input {
    flex: 1;
    min-width: 0;
    padding: 6px 10px;
    font-size: 0.82rem;
    background: var(--bg-input, #f3f4f6);
    border: 1px solid var(--border, #e5e7eb);
    border-radius: 6px;
    color: var(--text, #111827);
}
.zpx-facet-input:focus { outline: none; border-color: var(--accent, #6c8aff); }
.zpx-facet-unit { color: var(--text-muted, #6b7280); font-size: 0.75rem; flex-shrink: 0; }
.zpx-facet-ok {
    padding: 6px 10px;
    font-size: 0.78rem;
    font-weight: 600;
    background: var(--bg-input, #f3f4f6);
    border: 1px solid var(--border, #e5e7eb);
    border-radius: 6px;
    color: var(--text-muted, #6b7280);
    cursor: pointer;
    transition: all 0.15s;
}
.zpx-facet-ok:hover { color: var(--text, #111827); border-color: var(--accent, #6c8aff); }

/* --- ZpDataExplorer : view switcher (copy of .ejp-segmented with fallbacks). --- */
.zpx-segmented {
    display: inline-flex;
    align-self: flex-start;
    border-radius: calc(var(--radius, 8px) - 2px);
    overflow: hidden;
    border: 1px solid var(--border, #e5e7eb);
}
.zpx-segmented a {
    padding: 4px 12px;
    font-size: 0.75rem;
    color: var(--text-muted, #6b7280);
    background: var(--bg-input, #f3f4f6);
    text-decoration: none;
    cursor: pointer;
    transition: all 0.15s;
    border-right: 1px solid var(--border, #e5e7eb);
    white-space: nowrap;
}
.zpx-segmented a:last-child { border-right: none; }
.zpx-segmented a:hover { background: var(--border, #e5e7eb); color: var(--text, #111827); }
.zpx-segmented a.active { background: var(--accent, #6c8aff); color: #fff; font-weight: 600; }

/* --- ZpDataExplorer : toolbar slot (sticky top, copy of .ejp-filters). --- */
.zpx-toolbar {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding: 6px 12px;
    background: var(--bg-card, #ffffff);
    border: 1px solid var(--border, #e5e7eb);
    border-radius: var(--radius, 8px);
    position: sticky;
    top: 0;
    z-index: 20;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

/* #468 : compact header (ZpDataExplorer CompactHeader), comportement HISTORIQUE
   (pages sans slots) : Toolbar slot LEFT and the full top pagination RIGHT on ONE
   row. The pager moves below as a unit when the row is too narrow. */
.zpx-toolbar--compact .zpx-toolbar-pager { margin-left: auto; display: flex; align-items: center; }
.zpx-toolbar--compact .zpx-toolbar-pager .zpx-pagination { flex-wrap: nowrap; }

/* #125 : pager CENTRÉ, OPT-IN via les slots standard .zpx-toolbar-left (colonne 1)
   et/ou .zpx-toolbar-actions (colonne 3) : le grid ne s'applique QU'AUX toolbars qui
   regroupent leur contenu dans ces conteneurs (review Copilot #127 : sans ce gate,
   des nœuds racine libres deviendraient autant d'items grid). minmax(0,1fr) laisse
   les colonnes latérales se rétrécir (min-width:auto piège du grid). Hoisted from
   the per-page CSS (History #494, price #473, customers #511). */
.zpx-toolbar--compact:has(> .zpx-toolbar-left, > .zpx-toolbar-actions) {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    align-items: center;
}
.zpx-toolbar--compact .zpx-toolbar-left { grid-row: 1; grid-column: 1; display: inline-flex; align-items: center; gap: 10px; }
.zpx-toolbar--compact:has(> .zpx-toolbar-left, > .zpx-toolbar-actions) .zpx-toolbar-pager { grid-row: 1; grid-column: 2; margin-left: 0; }
.zpx-toolbar--compact .zpx-toolbar-actions { grid-row: 1; grid-column: 3; display: inline-flex; justify-content: flex-end; align-items: center; gap: 10px; }
@media (max-width: 1100px) {
    .zpx-toolbar--compact:has(> .zpx-toolbar-left, > .zpx-toolbar-actions) { display: flex; flex-wrap: wrap; }
    /* Sous le seuil, le pager descend en ligne dédiée pleine largeur, centré. */
    .zpx-toolbar--compact:has(> .zpx-toolbar-left, > .zpx-toolbar-actions) .zpx-toolbar-pager { flex-basis: 100%; justify-content: center; }
}

/* #468 : « Lignes » page-size combo : the SearchableDropDown panel must OVERLAY
   the content below, not open in flow (it used to push the whole grid down).
   Same recipe as the zp-admin.css overrides (.ejp-pagesize-control) but scoped
   to the zpx page-size control ONLY : the Search sidebar usages keep their
   designed in-flow panel. The bottom bar renders no page-size selector, so no
   upward-opening variant is needed. */
.zpx-pagesize .sd-details { position: relative; }
.zpx-pagesize .sd-details .zp-panel {
    position: absolute;
    top: 100%;
    right: 0;
    min-width: 120px;
    z-index: 200;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
}

/* Stats slot (chips, counters). */
.zpx-stats { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* --- ZpDataExplorer : bottom pagination (copy of .ejp-sticky-bottom). The
       bottom bar renders page links only (a page-size panel would open
       downward off-screen), so no upward-opening override is needed. --- */
.zpx-sticky-bottom {
    position: sticky;
    bottom: 0;
    z-index: 20;
    padding: 6px 0;
    background: var(--bg-card, #ffffff);
    /* Barre DOCKEE assumee (xRefIQ #494) : la bordure haute marque la frontiere avec
       les lignes qu'elle recouvre pendant le scroll, l'ombre seule lisait comme un
       chevauchement accidentel. */
    border-top: 1px solid var(--border, #e5e7eb);
    box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.15);
    /* #125 : pager du bas centre, comme celui du header compact. */
    display: flex;
    justify-content: center;
}

/* ============================================================
   ZpSinglePage — carte de visite single page (23/07/2026).
   Theming par variables --zps-* : le site hôte les définit dans
   son app.css (pattern ZpFooter) ; fallbacks neutres ci-dessous.
   ============================================================ */

/* Lien d'evitement (WCAG 2.4.1 Bypass Blocks) : hors ecran tant qu'il n'a pas
   le focus, il apparait des la premiere tabulation et mene au contenu. */
.zps-skip {
    position: absolute;
    left: 8px;
    top: -64px;
    z-index: 100;
    padding: 10px 16px;
    border-radius: 10px;
    background: var(--zps-accent, #374151);
    color: var(--zps-cta-text, #ffffff);
    font-family: var(--zps-font-body, system-ui, sans-serif);
    font-size: 15px;
    font-weight: 600;
    text-decoration: none;
    transition: top 0.15s ease;
}
.zps-skip:focus {
    top: 8px;
}

.zps-nav {
    position: fixed;
    top: 0; left: 0; right: 0;
    z-index: 50;
    height: 68px;
    display: flex;
    align-items: center;
    backdrop-filter: blur(14px);
    background: var(--zps-nav-bg, rgba(255, 255, 255, 0.92));
    border-bottom: 1px solid var(--zps-border, #e5e7eb);
}
.zps-nav__inner {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    width: 100%;
    max-width: 1168px;
    margin: 0 auto;
    padding: 0 24px;
}
.zps-nav__brand {
    display: flex;
    align-items: baseline;
    gap: 10px;
    text-decoration: none;
}
.zps-nav__brand-logo { height: 32px; width: auto; }
.zps-nav__brand-name {
    font-family: var(--zps-font-display, Georgia, serif);
    font-size: 18px;
    font-weight: 700;
    letter-spacing: 0.04em;
    color: var(--zps-text, #1f2937);
}
.zps-nav__brand-sub {
    font-family: var(--zps-font-body, system-ui, sans-serif);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--zps-muted, #6b7280);
}
.zps-nav__contact {
    font-family: var(--zps-font-body, system-ui, sans-serif);
    font-size: 14px;
    font-weight: 500;
    text-decoration: none;
    color: var(--zps-muted, #6b7280);
    transition: color 0.3s;
}
.zps-nav__contact:hover { color: var(--zps-accent, #374151); }

.zps-hero {
    /* Prend la place restante dans la colonne, au lieu de reclamer une fenetre entiere pour
       lui seul. C'est ce qui fait tenir la carte dans l'ecran, pied compris. */
    flex: 1 0 auto;
    display: flex;
    align-items: center;
    padding: 96px 0 12px;
    background: var(--zps-hero-bg, transparent);
}
.zps-hero__inner { max-width: 1168px; margin: 0 auto; width: 100%; padding: 0 24px; }
.zps-hero__tags {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 26px;
}
.zps-hero__tag {
    font-family: var(--zps-font-body, system-ui, sans-serif);
    font-size: 13px;
    font-weight: 600;
    padding: 5px 14px;
    border-radius: 20px;
    border: 1px solid var(--zps-border, #e5e7eb);
    color: var(--zps-muted, #6b7280);
    background: var(--zps-tag-bg, rgba(0, 0, 0, 0.03));
}
.zps-hero__title {
    font-family: var(--zps-font-display, Georgia, serif);
    font-size: clamp(34px, 5.6vw, 64px);
    font-weight: 400;
    line-height: 1.12;
    color: var(--zps-title, var(--zps-text, #1f2937));
    margin: 0 0 22px;
}
.zps-accent { color: var(--zps-accent, #374151); font-style: italic; }
.zps-hero__subtitle {
    font-family: var(--zps-font-body, system-ui, sans-serif);
    font-size: clamp(16px, 1.8vw, 19px);
    line-height: 1.65;
    color: var(--zps-muted, #6b7280);
    max-width: 58ch;
    margin: 0 0 26px;
}
.zps-hero__actions { display: flex; gap: 12px; flex-wrap: wrap; }
.zps-hero__cta {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-family: var(--zps-font-body, system-ui, sans-serif);
    font-size: 16px;
    font-weight: 600;
    text-decoration: none;
    color: var(--zps-cta-text, #ffffff);
    background: var(--zps-accent, #374151);
    padding: 14px 30px;
    border-radius: 12px;
    transition: filter 0.3s, transform 0.3s;
}
.zps-hero__cta:hover { filter: brightness(0.92); transform: translateY(-1px); }
.zps-hero__quote {
    font-family: var(--zps-font-display, Georgia, serif);
    font-style: italic;
    font-size: 15px;
    color: var(--zps-muted, #6b7280);
    opacity: 0.85;
    margin: 38px 0 0;
    padding-left: 14px;
    border-left: 2px solid var(--zps-accent, #374151);
    max-width: 62ch;
}

.zps-contact {
    padding: 12px 0 48px;
    /* Laisse la place a la poignee du pied, qui deborde au-dessus de la barre. Dans le
       rembourrage de CETTE section, donc sur son fond, et non en marge du pied, qui
       laisserait voir une bande du fond de page */
    padding-bottom: calc(48px + var(--zp-footer-handle-h));
    text-align: center;
    background: var(--zps-contact-bg, transparent);
}
.zps-contact__inner { max-width: 908px; margin: 0 auto; padding: 0 24px; }
.zps-contact__title {
    font-family: var(--zps-font-display, Georgia, serif);
    font-size: clamp(26px, 3.6vw, 40px);
    font-weight: 400;
    color: var(--zps-contact-title, var(--zps-text, #1f2937));
    margin: 0 0 14px;
}
.zps-contact__text {
    font-family: var(--zps-font-body, system-ui, sans-serif);
    font-size: 17px;
    line-height: 1.65;
    color: var(--zps-contact-muted, var(--zps-muted, #6b7280));
    margin: 0 0 30px;
}
.zps-contact__links {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px;
    margin-bottom: 26px;
}
.zps-contact__link {
    font-family: var(--zps-font-body, system-ui, sans-serif);
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
    padding: 9px 18px;
    border-radius: 10px;
    border: 1px solid var(--zps-border, #e5e7eb);
    color: var(--zps-contact-muted, var(--zps-muted, #6b7280));
    background: var(--zps-tag-bg, rgba(0, 0, 0, 0.03));
    transition: transform 0.3s;
}
.zps-contact__link:hover { transform: translateY(-1px); }
.zps-contact__meta {
    font-family: var(--zps-font-body, system-ui, sans-serif);
    font-size: 15px;
    color: var(--zps-contact-muted, var(--zps-muted, #6b7280));
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 8px 22px;
    margin: 0;
}
.zps-contact__phone { color: inherit; text-decoration: none; }
.zps-contact__phone:hover { color: var(--zps-accent, #374151); }

@media (max-width: 640px) {
    .zps-hero { padding-top: 104px; }
    .zps-nav__brand-sub { display: none; }
}

/* Version cliquable du ZpFooter (VersionUrl -> changelog, pattern xRefIQ) */
.zp-footer .zp-footer-version-link {
    color: inherit;
    text-decoration: underline dotted;
    text-underline-offset: 3px;
}
.zp-footer .zp-footer-version-link:hover { color: var(--accent, #6c8aff); }

/* Respect de la preference systeme « moins d'animations » (WCAG 2.3.3, et
   confort des personnes sensibles au mouvement). La pulsation de la poignee et
   les transitions du tiroir sont purement decoratives : on les coupe. */
@media (prefers-reduced-motion: reduce) {
    .zp-footer .zp-footer-handle-icon { animation: none; }
    /* Le panneau et son contenu depuis le 29/07/2026 : le premier porte la transition de
       hauteur, le second celle d'opacite. */
    .zp-footer .zp-footer-panneau,
    .zp-footer .zp-footer-content--ecosystem,
    .zp-footer .zp-footer-handle,
    .zps-skip,
    .zps-hero__cta,
    .zps-contact__link { transition: none; }
}

/* Bouton principal de la rangee contact : style derive des pills, accentue */
.zps-contact__link--primary {
    background: var(--zps-accent, #374151);
    border-color: var(--zps-accent, #374151);
    color: var(--zps-cta-text, #ffffff);
    font-weight: 700;
}
.zps-contact__link--primary:hover { filter: brightness(0.92); }


/* Le mode calque a disparu le 29/07/2026, avec le script qui le posait.

   Il fixait le pied au bas de la fenetre quand le document y tenait, ce qui simulait par
   une mesure ce que la mise en page en colonne fait nativement : voir le motif « sticky
   footer » commente sur .zps. Les deux sont d'ailleurs incompatibles, et c'est ce qui a
   tranche. Un pied fixe SORT de la colonne : son flex-shrink devient inerte, la zone de
   contenu reprend la fenetre entiere, et le pied la recouvre. Le bloc contact n'ayant que
   48 px de rembourrage bas pour une barre de 53 px au large et 89 px au doigt, le bouton
   d'appel serait passe dessous.

   Avec lui disparait la derniere mesure du script du composant : celui-ci ne fait plus que
   basculer des classes. La preference qu'il retenait a disparu le lendemain, 29/07/2026 : un
   localStorage rouvrait le tiroir a l'arrivee pour qui l'avait ouvert une fois, donnant deux
   comportements selon l'historique du visiteur, dont l'un invisible a qui teste. */

/* ============================================================
   ZpSinglePage : variantes de composition (comparatif 23/07/2026)
   a = centre sobre | b = gauche + signature slash | c = immersif
   ============================================================ */
/* Mise en page « sticky footer » en colonne flex, motif standard, sans une seule constante
   (29/07/2026). Le conteneur fait au moins une fenetre, la zone de contenu prend ce qui
   RESTE une fois le pied place, et le pied garde sa hauteur. Une page dont le contenu tient
   fait donc exactement une hauteur d'ecran, pied compris, sans defilement ; une page plus
   longue grandit normalement.

   Ce qu'il remplace : chaque section reclamait une fenetre entiere pour elle seule, puis
   reservait EN PLUS la hauteur du pied en rembourrage. La hauteur du pied etait donc comptee
   deux fois, et la page depassait toujours d'exactement cette hauteur. Mesure du 29/07 sur
   la carte de zebrapuma.be : 53 px a defiler pour 53 px de barre en 1280x800, 89 px pour
   89 px de barre en 360x640. Le contenu tenait deja, seul le pied faisait deborder.

   Aucune hauteur n'est ecrite nulle part : le navigateur calcule. Les calc(100svh - 176px),
   - 200px, - 64px et - 140px qui parsemaient les sites, tous des nombres poses au juge,
   disparaissent avec ce motif, ainsi que la variable --zp-footer-h qui les nourrissait et
   qui se trompait dans les deux sens (32 px declares pour 89 reels au doigt, 92 pour 53 au
   large).

   align-items reste a sa valeur par defaut : en colonne, l'axe transversal est HORIZONTAL,
   et un centrage y ramenerait le heros, le contact et le pied a la largeur de leur contenu. */
.zps {
    position: relative;
    overflow: clip;
    min-height: 100svh;
    display: flex;
    flex-direction: column;
}
.zps, .zps *, .zps *::before, .zps *::after { box-sizing: border-box; }  /* clip X ET Y : le filigrane ne doit pas gonfler la hauteur du document */

/* Relais : le heros n'est pas enfant direct de .zps, le layout l'enveloppe dans un <main>.
   Sans ce relais, le contenu cesserait de grandir et les repartitions calibrees a l'interieur
   du heros (space-evenly, margin-top: auto) n'auraient plus d'espace libre a repartir : la
   composition se tasserait en haut sans qu'aucune regle paraisse fausse.
   Vise `> main` et non l'identifiant : il appartient au layout de chaque site, la selection
   par le role reste vraie partout. */
.zps > main {
    flex: 1 0 auto;
    display: flex;
    flex-direction: column;
}

/* Le combinateur d'enfant direct est tombe le 27/07/2026, quand le layout a enveloppe le
   contenu dans un <main> : `.zps > .zps-hero` ne matchait donc plus, et le z-index cense
   faire passer le texte au-dessus du filigrane etait inoperant depuis. Constat du relevé du
   29/07/2026. Descendant simple, la profondeur n'ayant jamais fait partie de l'intention. */
.zps .zps-hero__inner,
.zps .zps-contact__inner { position: relative; z-index: 1; }

/* --- A : tout centre --- */
.zps--a .zps-hero__inner { text-align: center; }
.zps--a .zps-hero__tags { justify-content: center; }
.zps--a .zps-hero__title { margin-left: auto; margin-right: auto; }
.zps--a .zps-hero__subtitle { margin-left: auto; margin-right: auto; }
.zps--a .zps-hero__actions { justify-content: center; }

/* --- B : tout a gauche + signature slash en filigrane --- */
.zps--b::before {
    content: "/";
    position: absolute;
    top: -6vh;
    right: 7vw;
    font-family: var(--zps-font-display, Georgia, serif);
    font-style: italic;
    font-size: 100vh;
    line-height: 1;
    color: var(--zps-accent, #374151);
    opacity: 0.06;
    pointer-events: none;
    z-index: 0;
}
.zps--b .zps-contact { text-align: left; }
.zps--b .zps-contact__inner { max-width: 1168px; margin: 0 auto; }
.zps--b .zps-contact__links { justify-content: flex-start; }
.zps--b .zps-contact__meta { justify-content: flex-start; }
/* couleur resserree : liens neutres, seul le CTA principal garde l'accent
   (!important : neutralise les styles inline issus du JSON pour le comparatif) */
.zps--b .zps-contact__link:not(.zps-contact__link--primary) {
    color: var(--zps-muted, #6b7280) !important;
    border-color: var(--zps-border, #e5e7eb) !important;
    background: var(--zps-tag-bg, rgba(0, 0, 0, 0.03)) !important;
}
@media (max-width: 900px) {
    .zps--b::before { display: none; }
}

/* --- C : filigrane rayures multicolores + centre --- */
.zps--c {
    background:
        repeating-linear-gradient(90deg,
            transparent 0 138px, rgba(42, 140, 130, 0.05) 138px 140px,
            transparent 140px 296px, rgba(231, 111, 81, 0.045) 296px 298px,
            transparent 298px 452px, rgba(232, 122, 46, 0.04) 452px 454px,
            transparent 454px 600px);
}
.zps--c .zps-hero__inner { text-align: center; }
.zps--c .zps-hero__tags { justify-content: center; }
.zps--c .zps-hero__title { margin-left: auto; margin-right: auto; }
.zps--c .zps-hero__subtitle { margin-left: auto; margin-right: auto; }
.zps--c .zps-hero__actions { justify-content: center; }

/* Fix variante C : les fonds opaques hero/contact masquaient le filigrane raye du wrapper */
.zps--c .zps-hero { background: transparent; }
.zps--c .zps-contact { background: transparent; }

/* --- D (exploration Regis 23/07) : nav epuree centree sans lien Contact,
   contenu centre, signature slash conservee, liens neutres --- */
.zps--d .zps-nav__inner { justify-content: center; }
.zps--d .zps-nav__contact { display: none; }
.zps--d .zps-hero__inner { text-align: center; }
.zps--d .zps-hero__tags { justify-content: center; }
.zps--d .zps-hero__title { margin-left: auto; margin-right: auto; }
.zps--d .zps-hero__subtitle { margin-left: auto; margin-right: auto; }
.zps--d .zps-hero__actions { justify-content: center; }
.zps--d .zps-contact__link:not(.zps-contact__link--primary) {
    color: var(--zps-muted, #6b7280) !important;
    border-color: var(--zps-border, #e5e7eb) !important;
    background: var(--zps-tag-bg, rgba(0, 0, 0, 0.03)) !important;
}

/* ============================================================================
   ZpToolbar + ZpLanguageSelector
   ----------------------------------------------------------------------------
   Monte du site vers la lib le 27/07/2026. Ces regles vivaient dans le CSS de
   zebrapuma.be alors que les composants vivent ici : regis.scyeur.net a pris le
   selecteur a son tour et l'a recu sans forme, les deux presentations empilees
   l'une sur l'autre. Ce qui sert plusieurs fois appartient a la lib.

   La lib porte la STRUCTURE (formes, cibles tactiles, point de rupture, bascule
   entre les deux presentations) ; le site ne porte plus que ses COULEURS, via les
   variables --zpls-*. Les valeurs de repli reproduisent la charte ZebraPuma, donc
   un site qui ne declare rien reste presentable.
   ========================================================================== */

/* Barre d'outils de l'en-tete. Les filets ne sont poses que si l'hote le demande,
   et a deux items il ne le demande pas : un separateur n'ordonne rien quand il n'y
   a rien a ordonner. */
.zpt {
    display: flex;
    align-items: center;
    gap: 14px;
    flex: none;
    min-width: 0;
}
.zpt__item { display: flex; align-items: center; min-width: 0; }
.zpt--separated .zpt__item + .zpt__item::before {
    content: "";
    width: 1px;
    height: 22px;
    margin-inline-end: 14px;
    background: var(--zpls-border, #C9C6C0);
    flex: none;
}

/* Selecteur de langue. Choix Regis 26/07/2026 : segmente quand la place existe,
   menu nomme en dessous de 900 px, « Contact » d'abord.

   NE JAMAIS poser « position » sur .zps-nav depuis un site : la lib la declare
   « fixed » et le hero passe dessous en reservant sa hauteur par padding-top. La
   repasser en « relative » la remet dans le flux, la barre est alors comptee deux
   fois et tout le contenu descend d'une hauteur de barre : c'est ce qui a fait
   ressurgir le bug de l'issue #6 le 26/07, attrape par le filet responsive. */
.zpls { display: flex; align-items: center; }

/* --- Presentation large : segmente encadre --- */
/* Forme du zpx-segmented de l'Explorer, portee a 44 px : le seuil tactile du
   projet (WCAG 2.5.8 « AAA », Material et Apple HIG s'accordent sur 44-48 px). */
.zpls__segmented {
    display: none;
    border: 1px solid var(--zpls-border, #C9C6C0);
    border-radius: 999px;
    overflow: hidden;
}
.zpls__seg-item {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    /* min-width autant que min-height : deux lettres et 12 px de marge ne font que
       41 px, sous le seuil. Mesure par le filet, pas estimee a l'oeil. */
    min-width: 44px;
    padding-inline: 12px;
    font-size: 13px;
    color: var(--zpls-text, #6B7679);
    background: var(--zpls-bg, #F7F5F1);
    text-decoration: none;
    border-right: 1px solid var(--zpls-border, #C9C6C0);   /* le filet entre les langues */
}
.zpls__seg-item:last-child { border-right: none; }
.zpls__seg-item[aria-current] {
    background: var(--zpls-current-bg, #3D474B);
    color: var(--zpls-current-text, #FFFFFF);
    font-weight: 600;
}

/* --- Presentation etroite : menu replie --- */
/* Ancre sur le bouton et non sur la barre, ce qui l'aligne mieux et evite d'avoir
   a toucher au positionnement de .zps-nav. */
.zpls__menu { position: relative; flex: none; }
.zpls__menu > summary { list-style: none; }                       /* Firefox */
.zpls__menu > summary::-webkit-details-marker { display: none; }  /* WebKit */

/* Bouton nomme, et non trois traits : atelier du 27/07/2026, option B. Le burger
   n'annoncait rien de ce qu'il contenait, alors qu'un « FR » dit ou l'on se trouve.
   Meme forme que le segmente pour que les deux presentations d'un meme selecteur se
   reconnaissent d'une largeur a l'autre. */
.zpls__toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: 44px;
    min-width: 44px;
    padding-inline: 14px;
    border: 1px solid var(--zpls-border, #C9C6C0);
    border-radius: 999px;
    background: var(--zpls-bg, #F7F5F1);
    font-size: 13px;
    font-weight: 600;
    color: var(--zpls-toggle-text, #3D474B);
    cursor: pointer;
}
.zpls__toggle-chevron {
    width: 9px;
    height: 9px;
    border-right: 1.6px solid currentColor;
    border-bottom: 1.6px solid currentColor;
    transform: translateY(-2px) rotate(45deg);
}
.zpls__menu[open] .zpls__toggle-chevron {
    transform: translateY(1px) rotate(-135deg);
}

.zpls__panel {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 30;
    min-width: 190px;
    padding: 6px;
    background: var(--zpls-panel-bg, #FFFFFF);
    border: 1px solid var(--zpls-panel-border, #DAD6CE);
    border-radius: 12px;
    box-shadow: 0 10px 28px rgba(0, 0, 0, 0.18);
}
.zpls__panel-link {
    display: flex;
    align-items: center;
    min-height: 44px;
    padding-inline: 12px;
    border-radius: 8px;
    color: var(--zpls-panel-text, #3D474B);
    text-decoration: none;
}
.zpls__panel-link[aria-current] {
    background: var(--zpls-panel-current-bg, #EEF1F2);
    font-weight: 600;
    color: var(--zpls-panel-current-text, #17191A);
}
.zpls__panel-sep {
    border: 0;
    border-top: 1px solid var(--zpls-panel-sep, #ECEAE5);
    margin: 6px 8px;
}

/* Bascule. Au large le menu disparait et le segmente prend sa place ; en dessous
   l'inverse. « display: none » retire aussi de l'arbre d'accessibilite, donc le jeu
   de liens cache n'est jamais annonce deux fois.
   900 px : la largeur ou une barre de navigation cesse de tenir ses items cote a
   cote, point de rupture « desktop » du projet. */
@media (min-width: 900px) {
    .zpls__segmented { display: inline-flex; }
    .zpls__menu { display: none; }
}

/* En dessous, le menu porte SEUL la navigation : le lien de la barre disparait,
   sans quoi « Contact » s'afficherait deux fois, dans la barre et dans le panneau.
   Le panneau prend alors toute la largeur sous l'en-tete, comme une barre depliee
   et non comme une bulle : a cette taille, une bulle de 190 px flottant sur le hero
   se lit mal et laisse un vide a sa droite.
   « fixed » et non « absolute » : la barre etant elle-meme fixed, le panneau s'y
   accroche sans qu'on ait a toucher au positionnement de .zps-nav. */
@media (max-width: 899px) {
    .zps-nav__contact { display: none; }

    .zpls__panel {
        position: fixed;
        top: var(--zp-nav-h, 68px);
        left: 0;
        right: 0;
        min-width: 0;
        padding: 8px;
        border-radius: 0;
        border-inline: 0;
        border-top: 0;
    }
}
