/* ============================================================
   cg-missions.css — composants CSS spécifiques aux MISSIONS
   Territoire B1 (codeur site). Ne PAS remettre dans global.css.
   Les composants PARTAGÉS provenance & ressort ont été extraits vers
   cg-provenance.css le 2026-07-25 (partagés guides + missions).
   ============================================================ */

/* ============================================================
   LA FAMILLE .cg-card — CE QUI EST COMMUN, ÉCRIT UNE FOIS
   ============================================================
   🔴 CES CINQ DÉCLARATIONS ÉTAIENT RECOPIÉES PAR MODIFICATEUR DE FAMILLE.
   Mesuré sélecteur par sélecteur : `.cg-card--espace`, `--club`, `--restaurant` et
   `--aliment` portaient la MÊME chaîne au caractère près pour la boîte, le lien
   étiré, la localisation, la liste d'étiquettes et l'étiquette. Une sixième famille
   — les professionnels — aurait dû en écrire une sixième copie, et c'est exactement
   la duplication que ce projet combat ailleurs : cinq endroits où changer un rayon
   de bordure, dont un qu'on oublie.

   🔒 CE QUI DIFFÈRE VRAIMENT RESTE PAR FAMILLE, et gagne par l'ordre : les blocs de
   famille sont tous écrits après celui-ci. Mesuré : refuge a sa propre marge de
   localisation et son propre titre, aliment aussi — ils les gardent.

   🔴 ET UNE INCOHÉRENCE SORT DE LA MESURE : le lien étiré manquait à REFUGE. Quatre
   cartes sur cinq étaient cliquables en entier, la cinquième seulement sur son titre,
   dans la même grille et pour le même geste. Ce n'était pas un choix — aucune ligne
   ne le disait — c'était la copie qu'on avait oublié de faire. La factorisation la
   répare par construction. */
.cg-card {
    position: relative;
    display: flex;
    flex-direction: column;
}

/* Toute la carte est cliquable : la cible d'un doigt est la carte, pas les vingt
   pixels du titre. Le pseudo-élément couvre la carte entière ; tout élément qui doit
   rester atteignable par-dessus se pose en `position: relative`. */
.cg-card .cg-card-title a::after { content: ""; position: absolute; inset: 0; }

.cg-card .cg-card-loc {
    margin: 0;
    font-size: var(--cg-text-sm, .9375rem);
    color: var(--cg-text-soft, var(--cg-text-soft));
}
.cg-card .cg-card-loc-region { color: var(--cg-text-muted, var(--cg-text-muted)); }

/* 🔄 `margin: auto 0 0` retiré : il poussait les étiquettes au bas de la carte, donc
   il logeait le blanc en trop AU-DESSUS d'elles — mesuré 30 à 33 px entre la commune
   et les étiquettes sur les cartes pro les plus courtes d'une rangée. Le blanc va
   sous le dernier élément (A7/A8). */
.cg-card .cg-card-tags {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}
.cg-card .cg-card-tag {
    font-size: .78rem;
    padding: 2px var(--cg-space-2);
    border-radius: var(--cg-radius-pill, var(--cg-radius-pill));
    background: var(--cg-bg-soft, var(--cg-bg-soft));
    border: 1px solid var(--cg-border, var(--cg-border));
    color: var(--cg-text-soft, var(--cg-text-soft));
}

/* ============================================================
   Carte mission — espaces de promenade (étend la famille .cg-card)
   Vignette = icône de type dans une boîte média AU MÊME format que
   .cg-card-image (aspect-ratio 16/10) → zéro CLS, hauteur uniforme
   en archive mixte icône/image (cadrage B2 1d).
   ============================================================ */

/* Hauteur FIXE plutôt que 16/10 (F4). Le ratio 16/10 était calé sur la boîte
   photo, mais aucune fiche ne porte de photo : il donnait 246px de gris pour une
   icône de 80px, soit 60% d'une carte de 417px. Une hauteur fixe garde l'invariant
   d'origine (boîte identique icône/photo -> hauteur uniforme, zéro CLS) tout en
   ramenant la carte à ~210px : le média redevient une bande de repérage, pas le
   sujet. Une photo posée y sera recadrée en bandeau (object-fit: cover). */
/* 🔄 LA HAUTEUR FIXE DE 104 PX EST RETIRÉE. Elle faisait de l'emplacement média des
   espaces une TROISIÈME anatomie : 104 px là où les quatre autres familles servent le
   bandeau 16/10 du socle, soit 191 px à 307 px de carte. Le patron 25 est UN
   emplacement gracieux dont le CONTENU varie par famille, pas trois bandeaux.
   Ce qui reste ici est du contenu, et c'est déclaré comme tel : l'encre du dessin.
   🔴 Le commentaire qui portait ces 104 px disait « le média redevient une bande de
   repérage, pas le sujet ». C'était un arbitrage local, rendu avant que les cinq
   familles partagent un composant ; il tombe avec l'anatomie qu'il justifiait. */
.cg-card--espace .cg-card-media--espace {
    color: var(--cg-primary, var(--cg-primary));
}
/* 🔒 LE CONTENU VARIE, LA PLACE QU'IL OCCUPE NON. La pastille-initiale des trois
   familles d'entités nommées mesure `--cg-rail-replie` ; le dessin des espaces tenait
   40 px, une taille choisie quand le bandeau en faisait 104. Dans un bandeau de 191 px
   il flottait. Il prend la même boîte optique que la pastille : c'est l'emplacement qui
   est commun, pas ce qu'on y met. */
.cg-card--espace .cg-card-icon {
    width: auto;
    max-width: none;
    height: var(--cg-rail-replie);
    opacity: .82;
}

/* Slot photo (§B.2) : remplit la boîte média 16/10 quand une photo de claim est
   posée ; sinon l'illustration d'origine (icône/avatar) reste. */
.cg-card-media .cg-card-photo {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
}

/* 🔴 EXCEPTION DÉCLARÉE — « ILLUSTRATION ÉDITORIALE ». Les illustrations des
   guides portent un TITRE INCRUSTÉ et sont en 1,90:1 (mesuré : 314×165 naturel) ;
   la boîte de la famille est en 3/2. En `cover`, elle rogne les côtés et COUPE le
   titre de l'image — c'est ce que Jordan a vu sur /guides/. On rend donc l'image
   ENTIÈRE, posée sur le papier de la boîte : aucun recadrage ne coupe du contenu,
   et cette règle-là passe avant l'uniformité du cadrage.

   🔒 ELLE EST ICI, À CÔTÉ DE LA RÈGLE QU'ELLE EXCEPTE, et pas dans la feuille de
   la carte éditoriale — qui n'est mise en file NULLE PART. Écrite là-bas, elle
   n'avait aucun effet, et la mesure d'après l'a dit.
   🔒 DEUX CLASSES SUR LE MÊME ÉLÉMENT, donc (0,3,0) : elle gagne par la STRUCTURE
   et non par l'ordre des feuilles, qui est ce qui l'avait fait perdre au premier jet.

   ⚠️ ELLE A UNE FIN : elle tombe quand les illustrations sont régénérées au ratio
   de la famille — item de la liste de Jordan. */
.cg-card-media.cg-card-media--guide .cg-card-photo--illustration,
.cg-card-media.cg-card-media--guides-achat .cg-card-photo--illustration {
    object-fit: contain;
    background: var(--cg-bg-alt);
}

/* 🔴 ET LA BOÎTE PREND LE RATIO NATIF, SINON `contain` LAISSE DEUX BANDES.
   Première version : image entière dans la boîte 3/2 de la famille — le titre
   incrusté était sauvé, mais chaque carte gagnait deux bandes crème et s'allongeait
   d'un vide. Corriger le recadrage avait déplacé le défaut, pas fermé.

   🔒 UN SEUL RATIO SUFFIT, ET C'EST MESURÉ, PAS SUPPOSÉ. Les 12 illustrations
   éditoriales publiées sont TOUTES en 1200×630 (ratio 1,905) — le format Open
   Graph. Une population uniforme : une seule valeur ne bande personne. Si elle
   cessait de l'être, la garde le dirait, parce qu'elle compare le ratio de la
   BOÎTE au ratio NATUREL de l'image.

   🔒 `contain` EST GARDÉ MÊME À RATIOS ÉGAUX, où il ne se distingue pas de `cover`.
   La différence n'apparaît que le jour où une image sort du format : `contain` la
   bande — ça se voit —, `cover` la rogne — ça ne se voit pas. Entre deux pannes,
   on garde celle qui se signale.

   ⚠️ FORMAT DE VARIANTE, PAS DE FAMILLE : la variante revient au 3/2 le jour où
   les illustrations sont régénérées au ratio de la famille (item de Jordan). */
.cg-card--guide .cg-card-media,
.cg-card--guides-achat .cg-card-media {
    aspect-ratio: 1200 / 630;
}

/* Lien étiré : toute la carte est cliquable via le titre */

/* 🔄 Cette règle neutralisait le rythme <h2> du corps de page pour QUATRE variantes,
   et sa propre note disait que les cartes éditoriales « relèvent d'autres chantiers ».
   Elles relèvent de la même famille : la remise à zéro est maintenant portée par
   `.cg-card :is(h1..h6)` dans global.css, et l'écart titre → suivant par la gouttière
   de `.cg-card-content`. Une variante n'a plus à le redire. */

.cg-card--espace .cg-card-attrs {
    list-style: none; margin: 0; padding: 0;
    display: flex; flex-wrap: wrap; gap: var(--cg-space-1) var(--cg-space-4);
}
.cg-card--espace .cg-card-attrs li { display: flex; gap: 6px; font-size: var(--cg-text-sm, .9375rem); }
.cg-card--espace .cg-card-attrs .attr-label { color: var(--cg-text-muted, var(--cg-text-muted)); }
.cg-card--espace .cg-card-attrs .attr-value { font-weight: 600; }

/* Badge de règle pour les chiens — couleur ET libelle texte (jamais la couleur seule) */
/* ═══ BADGE DE L'EMPLACEMENT MÉDIA — une seule position, un seul dessin ═════════
   🔴 Trois règles quasi identiques le posaient, une par famille : espaces, clubs,
      restaurants. Elles s'accordaient encore (10 px / 10 px, pastille, fond, ombre),
      mais rien ne les y obligeait, et trois copies finissent par diverger.
   🔒 La POSITION et la FORME sont communes. La couleur reste par famille : elle porte
      un statut, et sa grammaire n'est pas écrite — c'est un delta design consigné, pas
      un choix à faire ici. */
/* ══ 🔄 LE BADGE VIT DANS LA ZONE DE CONTENU, PLUS SUR LE MÉDIA ══════════════
   Il était posé en absolu à 10 px du coin du bandeau. Sur les cartes de club et
   de restaurant, dont le média sert un DISQUE d'initiale centré, il mordait
   dedans : distance 18,1 px pour un rayon de 32, aux deux largeurs. Et les trois
   contraintes du patron — badge au coin, jeton centré, ratio de bandeau fixe —
   sont conjointement insatisfiables avec un badge de 127 px et un disque de 64 :
   le chevauchement venait du patron, pas d'une erreur d'application.
   🔒 RÈGLE UNIFORME : rien ne recouvre le média. Le badge rejoint le haut du
   contenu, en chip de la famille — le patron des cartes guides. On ne négocie pas
   les trois contraintes, on retire le quatrième acteur. Elle vaut pour les clubs,
   les restaurants ET les badges de règle des promenades : trois cartes qui posent
   leur badge à trois endroits redeviennent trois grammaires.
   ⚠️ La seule exception prévue est la pastille d'initiale d'A35, dans sa zone
   réservée — déclarée ici pour qu'on ne la prenne pas plus tard pour un oubli. */
.cg-card-content .cg-badge {
    display: inline-flex;
    align-items: center;
    align-self: flex-start;
    margin: 0;
    padding: 3px 9px;
    border-radius: var(--cg-radius-pill);
    font-size: .78rem;
    font-weight: 700;
    line-height: 1.2;
    background: var(--cg-bg);
}
.cg-badge--regle-libre    { color: #1f7a3d; border: 1px solid #1f7a3d; }
.cg-badge--regle-laisse   { color: #9a6a00; border: 1px solid var(--cg-status-mid); }
.cg-badge--regle-interdit { color: #b23b3b; border: 1px solid #b23b3b; }
.cg-badge--regle-zone     { color: #b5651d; border: 1px solid #b5651d; }
.cg-badge--regle-inconnu  { color: var(--cg-text-soft);    border: 1px solid #c9c9c9; }

/* ============================================================
   Fiche espace de promenade — bloc règle + tables de faits
   ============================================================ */
/* 🔒 PROPRIÉTÉ LONGUE, PAS RACCOURCI. Écrit `padding: 0 0 …`, ce bloc AFFIRMAIT
   un haut à zéro et écrasait la règle unique de global.css, qui vient avant.
   Un raccourci ne cède rien : il déclare les quatre côtés. */
.single-espace { padding-bottom: var(--cg-space-9); }
/* 🔒 PLUS DE MARGE SOUS LA BANDE. Le vide bande → contenu est porté UNE FOIS par le
   rembourrage haut du conteneur (règle « vide sous la bande », global.css). Trois
   bandes ajoutaient la leur par-dessus — 22 px, 24 px et 28 px, dont deux en dur —
   ce qui donnait trois fiches à 70+ px là où les quatre autres étaient à 48. */
.single-espace-head { margin: var(--cg-space-2) 0 0; }
.single-espace-title { font-size: var(--cg-text-3xl, var(--cg-text-3xl)); margin: 0 0 6px; color: var(--cg-heading, var(--cg-heading)); }
/* Sous-titre de contexte : le type et la commune étaient enterrés deux sections
   plus bas, alors qu'ils situent la fiche au premier coup d'œil. */
.single-espace-sub { margin: 0; color: var(--cg-text-soft, var(--cg-text-soft)); }

.espace-regle-block {
    margin: 0 0 28px;
    padding: var(--cg-space-4) 18px;
    border: 1px solid var(--cg-border, var(--cg-border));
    border-left-width: 5px;
    border-radius: var(--cg-radius-sm, var(--cg-radius-sm));
    background: var(--cg-bg-alt, var(--cg-bg-alt));
}
.espace-regle-block--libre    { border-left-color: #1f7a3d; }
.espace-regle-block--laisse   { border-left-color: var(--cg-status-mid); }
.espace-regle-block--interdit { border-left-color: #b23b3b; }
.espace-regle-block--zone     { border-left-color: #b5651d; }
.espace-regle-block--inconnu  { border-left-color: #c9c9c9; }

.espace-regle-head { display: flex; align-items: center; gap: 10px; margin: 0 0 10px; flex-wrap: wrap; }
.espace-regle-head .cg-badge { position: static; box-shadow: none; }
.espace-regle-label { font-weight: 700; color: var(--cg-heading, var(--cg-heading)); }
.espace-regle-inconnu { margin: 0; color: var(--cg-text-soft, var(--cg-text-soft)); }

/* Contrainte d'ACCÈS (periode_restriction). Elle vivait dans la liste de
   provenance, au même poids visuel que « Constaté le » — or c'est la seule ligne
   du bloc qui peut faire renoncer à la sortie du jour. */
.espace-regle-periode {
    margin: var(--cg-space-3) 0 0;
    padding: 10px var(--cg-space-3);
    border: 1px solid #e4cf9f;
    border-left: 4px solid var(--cg-status-mid);
    border-radius: var(--cg-radius-xs, var(--cg-radius-xs));
    background: #fdf8ec;
    color: var(--cg-text-soft, #555555);
    font-size: var(--cg-text-sm, .9375rem);
}
.espace-regle-periode-label {
    display: block;
    font-weight: 700;
    color: #7a5a12;
    margin-bottom: 2px;
}
.espace-regle-block .cg-provenance-meta { margin-top: var(--cg-space-3); }

.espace-section { margin: 30px 0 0; }
.single-espace h2 { margin: 0 0 var(--cg-space-3); }

/* Grille de faits en CELLULES autonomes. Avant : des lignes à filet inférieur
   dans une grille auto-fill — la dernière ligne d'une colonne incomplète laissait
   un filet qui s'arrêtait au milieu du vide, et un fait seul (le cas courant :
   « Parking / Oui ») pendait sous son titre. Chaque fait porte désormais sa
   propre boîte : lisible à 1 comme à 6 valeurs, sans bord orphelin. */
.espace-facts {
    list-style: none; margin: 0; padding: 0;
    display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 10px;
}
.espace-facts li {
    display: flex; flex-direction: column; gap: 2px;
    padding: 10px var(--cg-space-3);
    border: 1px solid var(--cg-border, var(--cg-border));
    border-radius: var(--cg-radius-xs, var(--cg-radius-xs));
    background: var(--cg-bg);
}
.espace-facts li { min-width: 0; }   /* sans ça, une cellule de grille refuse de passer sous la largeur de son contenu */
.espace-facts .attr-label { min-width: 0; font-size: var(--cg-text-sm, .9375rem); color: var(--cg-text-muted, var(--cg-text-muted)); }
/* F4-1 · Débordement. Une valeur INSÉCABLE (e-mail, nom de domaine, URL) est plus
   large que sa cellule : elle débordait sur la colonne voisine. `min-width: 0`
   lève le plancher `min-content` de l'élément flex, `overflow-wrap: anywhere`
   autorise la coupure là où aucune césure naturelle n'existe. Posé sur la CLASSE
   -> vaut pour les 4 missions (le cas vécu est le bloc Contact des clubs). */
.espace-facts .attr-value {
    min-width: 0;
    overflow-wrap: anywhere;
    font-weight: 700;
    color: var(--cg-heading, var(--cg-heading));
}

/* Carte de fiche — « Où et comment y aller » se tenait à un nom de commune. */
.espace-geo--map .espace-facts { margin-bottom: var(--cg-space-3); }
/* Sélecteur porté par `.single-espace` : la règle générique `.cg-mission-map`
   (440px, calée pour l'archive) est déclarée plus BAS dans ce fichier et
   l'emporterait à spécificité égale. */
.single-espace .espace-map { height: 300px; }
.espace-map-wrap .cg-map-note { margin-top: var(--cg-space-2); }
.espace-map-link { color: var(--cg-info, var(--cg-info)); white-space: nowrap; }

/* Ressort de maillage : un <h2> pour un seul lien pesait autant qu'une section
   de données ; il devient un encart, le lien reste l'élément fort. */
.espace-ref-callout {
    margin: 30px 0 0;
    padding: 14px var(--cg-space-4);
    border: 1px solid var(--cg-border, var(--cg-border));
    border-radius: var(--cg-radius-sm, var(--cg-radius-sm));
    background: var(--cg-bg-alt, var(--cg-bg-alt));
}
.espace-ref-link { font-weight: 700; }

/* ============================================================
   Archive espaces de promenade (grille minimale ; carte + filtres post-import)
   ============================================================ */
.espaces-archive { padding: 0 0 var(--cg-space-9); }
.espaces-archive-header { margin: var(--cg-space-2) 0 var(--cg-space-6); }
.espaces-archive-title { font-size: var(--cg-text-3xl, var(--cg-text-3xl)); margin: 0 0 var(--cg-space-2); color: var(--cg-heading, var(--cg-heading)); }
.espaces-archive-intro { margin: 0; color: var(--cg-text-soft, var(--cg-text-soft)); }
.espaces-archive-empty { color: var(--cg-text-soft, var(--cg-text-soft)); }
/* .espaces-grid : la grille est mutualisée dans .cg-facets-grid */

/* ============================================================
   Slot de NOTE COMMUNAUTAIRE sur les fiches missions
   Structure posée, vote non ouvert (inc/note-communaute-slot.php).
   🔒 Scopé au modificateur `--slot` : `cg-reviews.css` n'est chargé que sur les
   fiches professionnel, et le vrai bloc avis des pros ne porte jamais ce
   modificateur — ces règles ne peuvent donc pas le rehabiller.
   🔒 Aucune étoile, aucun chiffre, aucune couleur de verdict : à 0 vote, une
   note absente ne s'annonce pas (spec §5.1, garde-fou 3).
   ============================================================ */
.cg-reviews--slot {
    margin: 36px 0 0;
    padding: 18px var(--cg-space-5);
    border: 1px solid var(--cg-border, var(--cg-border));
    border-radius: var(--cg-radius-sm, var(--cg-radius-sm));
    background: var(--cg-bg-alt, var(--cg-bg-alt));
}
.cg-reviews--slot .cg-reviews__title {
    margin: 0 0 var(--cg-space-2);
    font-size: var(--cg-text-xl, var(--cg-text-xl));
    color: var(--cg-heading, var(--cg-heading));
}
.cg-reviews--slot .cg-reviews__empty { margin: 0; color: var(--cg-text-soft, var(--cg-text-soft)); }
.cg-reviews--slot .cg-reviews__soon {
    margin: 6px 0 0;
    font-size: var(--cg-text-sm, .9375rem);
    color: var(--cg-text-muted, var(--cg-text-muted));
}

/* ============================================================
   Carte mission — clubs canins (avatar-initiale + badge axe)
   ============================================================ */
/* ═══ ZONE MÉDIA DES CARTES DE CATALOGUE — un seul bloc ═══════════════════
   🔴 Club et restaurant portaient DEUX RÈGLES OCTET POUR OCTET IDENTIQUES, à leur
      modificateur de famille près ; aliments la répétait une troisième fois avec une
      ligne de plus. En ajouter une quatrième pour les refuges aurait fait quatre
      copies d'une même bande.
   🔒 Le bandeau et la vignette-initiale sont donc communs. Ce qui reste par famille
      est ce qui DIFFÈRE réellement : l'encre des familles qui posent un dessin, et la
      hauteur fixe des espaces. Les valeurs sont celles qui étaient servies. */
/* ═══ SIGNATURE ÉDITORIALE DU CATALOGUE ══════════════════════════════════════════
   🔄 Elle avait été retirée des pages catalogue, à tort. Retour de Jordan : « c'est
      important, ça doit être mentionné », et sa place sous les boutons de pré-filtre
      AÉRAIT le héros de l'espace des cartes. Sans respiration, elle recolle les deux.
   🔒 La couture est le jeton d'espace du socle, la même valeur des deux côtés : le
      geste vaut pour les six catalogues, et il n'y a rien à réaccorder par famille.
   🔒 Le composant lui-même (.editorial-meta) n'est pas retouché : il est servi aussi
      par les fiches et par les guides, et cette règle ne place que son enveloppe. */
.cg-catalogue-signature,
.cg-signature-discrete {
    /* 🔒 `flow-root` : la boîte CONTIENT les marges de son enfant. Sans lui, la marge
       haute de `.editorial-meta` s'échappait du conteneur et s'ajoutait au rembourrage
       du catalogue : la couture mesurait 34 px au lieu de 24 sur les familles où la
       signature est le premier enfant. Contenir les marges est le geste général ;
       neutraliser celle de l'enfant aurait traité ce cas-ci et laissé le suivant. */
    display: flow-root;
    margin: var(--cg-space-6) 0;
}

/* 🔴 SANS BANDE DE RACCOURCIS, LA COUTURE DOUBLAIT. Sur les familles dont le vivier
   n'offre pas de choix (refuges, restaurants), la signature est le PREMIER enfant du
   conteneur de catalogue : sa marge haute s'ajoutait au rembourrage du conteneur, et
   l'écart passait de 24 à 48 px. Mesuré sur les six, et attrapé par la garde de
   couture, pas par une relecture.
   🔒 La règle porte sur la CONDITION STRUCTURELLE — être le premier enfant, donc être
   déjà décalé par le conteneur — et non sur les deux familles où le cas se présente
   aujourd'hui. Une famille qui gagne ou perd sa bande reste juste. */
.cg-catalogue-signature:first-child,
.cg-signature-discrete:first-child {
    margin-top: 0;
}

/* ═══ A23 — LA SIGNATURE DE CATALOGUE SE FAIT DISCRÈTE ═══════════════════════
   🔴 CE QUI EST MESURÉ, PAS CE QU'ON RETIRE. Relevé à 1440 sur trois catalogues :
   53 px de haut, sur DEUX lignes, corps 15 px, encre #555 — la même que le texte
   courant. Elle pèse autant qu'un paragraphe alors qu'elle en dit vingt mots.
   🔒 ON RÉDUIT LE COMPOSANT, PAS SA MARGE. Retirer la marge seule la ferait
   recoller au bloc voisin : mesuré ailleurs, un parent sans `gap` la recolle
   partout. C'est le corps, l'encre et le retour à la ligne qu'on resserre ; la
   couture de 24 px reste celle du socle.
   🔒 ET LA RÈGLE EST SCOPÉE AU CATALOGUE. `.editorial-meta` sert aussi les fiches
   et les guides, où la signature est une information de tête : la rendre
   discrète là serait une autre décision, prise par quelqu'un d'autre.
   🔒 P5 — L'ALIAS `.cg-signature-discrete` NOMME LE RÔLE, PAS LE LIEU. La fiche
   guide reçoit la même réduction depuis que sa signature est descendue sous
   l'image ; lui faire porter « catalogue » l'aurait fait mentir sur ce qu'elle
   est, et renommer la classe aurait touché trois pages qu'on ne regardait pas. */
.cg-catalogue-signature .editorial-meta,
.cg-signature-discrete .editorial-meta,
/* 🔒 LA CLASSE PEUT ÊTRE SUR L'ÉLÉMENT LUI-MÊME. La fiche actualité porte sa
   signature en un seul nœud : `.cg-signature-discrete .editorial-meta` ne l'y
   atteignait pas, et la réduction restait sans effet — mesuré, 15 px là où les
   catalogues servent 13. Un composant qui ne s'applique que d'une manière n'est
   pas partagé, il est recopié. */
.cg-signature-discrete.editorial-meta {
    /* 🔒 L'ENROBAGE PART D'ABORD. Mesuré : la ligne elle-même fait 19 px, et
       l'enveloppe 49 — trente pixels de marges de l'enfant, contenues par le
       `flow-root` mais bien présentes. C'est là qu'était le poids, pas dans le
       texte. */
    margin: 0;
    font-size: var(--cg-text-xs, 0.8125rem);
    line-height: var(--cg-leading-snug);
    /* 🔒 L'ENCRE NE DESCEND PAS PLUS BAS QUE LA PALETTE. À 13 px, le seuil de
       contraste est 4,5:1 ; aucun gris plus clair n'est déclaré au socle, et en
       inventer un ici mettrait une valeur hors palette dans le seul endroit qui
       parle de l'auteur. La discrétion vient du corps et du poids, pas d'un
       gris qu'on ne pourrait plus lire. */
    color: var(--cg-text-soft);
    font-weight: 400;
}

/* 🔒 UNE SEULE LIGNE QUAND LA PLACE LE PERMET, un retour PROPRE sinon. `nowrap`
   sur les segments empêche « Mis à jour le » de se couper au milieu ; le
   conteneur, lui, garde le droit de passer à la ligne — sur un écran étroit,
   forcer une ligne produirait un débordement, et un débordement est pire qu'un
   retour. */
.cg-catalogue-signature .editorial-meta,
.cg-signature-discrete .editorial-meta,
.cg-signature-discrete.editorial-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0 var(--cg-space-2);
}
.cg-catalogue-signature .editorial-meta > *,
.cg-signature-discrete .editorial-meta > *,
.cg-signature-discrete.editorial-meta > * {
    white-space: nowrap;
}

/* ═══ LE RATIO DU MÉDIA DE CARTE EST UN JETON, ET IL N'Y EN A QU'UN ══════════════
   🔄 3/2, PARTOUT. Le modèle portait 3/2 parce que c'est le format réel de ses photos
      de race (720 × 480, add_image_size 'race-card') ; les cinq annuaires portaient
      16/10. Deux boîtes pour un seul patron, et la gate l'avait relevé comme une
      variante déclarée sur la RÉFÉRENCE.
   🔒 Recadrer 363 photos en 16/10 aurait été une décision de CONTENU : on ferait
      perdre du sujet en haut et en bas de chacune. Les emplacements des annuaires, eux,
      n'ont aucune photo — ils accueillent une pastille, un dessin, une pilule, tous
      centrés. Les passer en 3/2 ne coûte rien et referme l'écart du bon côté :
      brief §15, « ratio identique partout ».
   🔒 UN JETON, PAS DEUX VALEURS QUI SE RESSEMBLENT. Le modèle et les cinq lisent la
      même déclaration ; le jour où elle bouge, elle bouge pour les six. */
:root {
    --cg-ratio-carte-media: 3 / 2;
}

.cg-card-media {
    position: relative; width: 100%; aspect-ratio: var(--cg-ratio-carte-media);
    display: flex; align-items: center; justify-content: center;
    background: var(--cg-bg-alt, var(--cg-bg-alt));
}

/* 🔴 UN RATIO DE PHOTO SUR UN EMPLACEMENT SANS PHOTO. Le 3/2 partagé donne
   338 px de crème sur une carte club de 507 px de large, pour une pastille de
   64 px. Mesuré le 2026-08-30 sur /clubs-canins/ et /refuges/ : 48 cartes,
   ZÉRO photo — et ce n'est pas un manque de remplissage, c'est la règle du
   projet (aucune image sans consentement, donc aucune au lancement). Le ratio
   décrivait donc un cas qui n'existe sur aucune carte servie.

   🔒 L'EMPLACEMENT PARTAGÉ RESTE ; C'EST SA HAUTEUR QUI SUIT SON CONTENU. Avec
   photo, le 3/2 s'applique comme avant, pour les six familles. Sans photo, la
   bande se dimensionne sur la pastille et son air — aucune hauteur écrite à la
   main : `--cg-rail-replie` pour la pastille, `--cg-space-5` pour l'air. Le
   jour où la pastille change de taille, la bande suit toute seule. */
/* 🔒 LA DÉCISION SE PREND SUR LA GRILLE, PAS SUR LA CARTE. Le bandeau existe
   même sans image parce qu'une grille dont les cartes n'ont pas la même hauteur
   perd son peigne — c'est écrit dans card-editorial et ça reste vrai. Réduire
   carte par carte remplacerait le rectangle crème par une grille en dents de
   scie. `:has()` porte donc la condition à l'échelle où elle est juste : la
   bande ne se resserre que si AUCUNE carte de la grille ne porte de photo, et
   elle reprend son 3/2 dès qu'une seule en porte une.
   🔒 LA DÉGRADATION EST L'ÉTAT ACTUEL. Un navigateur sans `:has()` sert le
   bandeau 3/2 qu'il servait hier : on ne casse rien, on n'améliore pas. */
.cg-facets-grid:not(:has(.cg-card-photo)):not(:has(.cg-card-media img)) .cg-card-media,
.cg-related-grid:not(:has(.cg-card-photo)):not(:has(.cg-card-media img)) .cg-card-media {
    aspect-ratio: auto;
    padding-block: var(--cg-space-5);
}
.cg-card-avatar--mono {
    width: var(--cg-rail-replie); height: var(--cg-rail-replie); border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    background: var(--cg-primary, var(--cg-primary)); color: #fff;
    font-size: 1.6rem; font-weight: 800;
}
.cg-card-media--aliment { color: var(--cg-primary, var(--cg-primary)); }

/* ═══ CARTE PROFESSIONNEL — ce qui lui est PROPRE, et rien de plus ═══════════
   🔒 Elle n'a ni boîte, ni lien étiré, ni localisation, ni étiquettes à elle : tout
   cela vient de `.cg-card`, écrit une fois pour les six familles. Ce bloc ne porte
   que ce qu'aucune autre famille n'a — un badge d'état, une ligne d'espèces, et un
   pied qui tient la note et le téléphone. */

/* Le badge dit un fait vérifié. Vert sobre, pas d'accent : l'accent est réservé à
   l'action, et une mention n'est pas une action. */
.cg-badge--verifie { color: #1f7a3d; border: 1px solid #1f7a3d; }

.cg-card--pro .cg-card-pro-especes {
    margin: 0;
    font-size: var(--cg-text-sm, .9375rem);
    color: var(--cg-text-soft, var(--cg-text-soft));
}
.cg-card--pro .cg-card-pro-especes-label { color: var(--cg-text-muted, var(--cg-text-muted)); }

/* Le pied vient APRÈS les étiquettes, qui portent `margin-top: auto` : il se pose
   donc en bas de carte sans hauteur imposée, et une carte sans pied n'en garde pas
   la place. */
.cg-card--pro .cg-card-pro-pied {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--cg-space-2);
    /* Le filet du pied se pose sur la gouttière de la colonne : le rembourrage
       reste (il sépare le trait du texte), la marge s'en va (elle doublait). */
    padding-top: var(--cg-space-2);
    border-top: 1px solid var(--cg-border, var(--cg-border));
}

/* 🔒 AU-DESSUS DU LIEN ÉTIRÉ. Le pseudo-élément du titre couvre la carte entière ;
   sans `position: relative` et un rang d'empilement, ce lien serait recouvert et
   le numéro deviendrait décoratif. C'est la contrepartie de la carte cliquable, et
   elle se paie ici, une fois. */
.cg-card--pro .cg-card-pro-tel {
    position: relative;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    /* 🔑 UNE CIBLE TACTILE, PAS UNE TAILLE DE PAGINATION. Ce `min-height` prenait
       le jeton de la pagination parce qu'il valait 44px ; le jeton porte
       désormais son vrai nom et la pagination en dérive. La valeur servie ne
       change pas — c'est le nom qui cesse de mentir. */
    min-height: var(--cg-cible-tactile);
    font-size: var(--cg-text-sm, .9375rem);
    font-weight: 600;
    color: var(--cg-primary, var(--cg-primary));
    text-decoration: none;
}
.cg-card--pro .cg-card-pro-tel:hover { text-decoration: underline; }

/* ══ LE « +N » EST UNE CHIP DE LA FAMILLE ════════════════════════════════════
   🔴 Il vivait dans une feuille annexe (cg-pro-card.css) : le jour où cette
   feuille a cessé d'être enfilée sur les archives de métier, sept d'entre elles
   ont servi un bouton d'Astra, 74x50 px orange, placé devant les etiquettes. Un
   composant dont l'apparence depend d'une feuille qui n'est pas celle de sa
   famille attend son accident.
   🔒 La chip est portee par le <li>, comme toutes les autres etiquettes ; le
   bouton n'est plus qu'un declencheur transparent a l'interieur. Il reste
   au-dessus du lien etire, parce qu'il ouvre quelque chose. */
/* 🔴 ET LA CHIP VIT SUR L'ÉLÉMENT CLIQUABLE, PAS AUTOUR DE LUI. Premier
   correctif : j'avais posé le gabarit sur le <li> et laissé le <button>
   transparent dedans. Visuellement ça pouvait passer ; en vérité l'anneau de
   focus, la cible de clic et la surface peinte n'étaient plus le même élément,
   et un bouton nu redevient le bouton du thème parent au premier oubli. Ce que
   l'œil reçoit et ce que le doigt touche doivent être la même boîte. */
.cg-card--pro .cg-card-tag--more,
.cg-card-tag--more {
    position: relative;
    z-index: 1;
    padding: 0;
    border: 0;
    background: none;
    display: inline-flex;
}

.cg-card-tag__more-btn {
    /* le gabarit de .cg-card-tag, porté par le bouton lui-même */
    font-family: inherit;
    font-size: .78rem;
    /* 🔴 J'AVAIS FIGÉ 1.4 ; l'étiquette voisine, elle, HÉRITE. Deux pixels de
       hauteur d'écart sur six vues, invisibles à l'œil et rendus par la garde :
       une chip et son bouton doivent partager la même boîte, pas s'en approcher. */
    line-height: inherit;
    padding: 2px var(--cg-space-2);
    border-radius: var(--cg-radius-pill);
    background: var(--cg-bg-soft);
    border: 1px solid var(--cg-border);
    color: var(--cg-text-soft);
    margin: 0;
    cursor: pointer;
    appearance: none;
}
.cg-card-tag__more-btn:hover {
    background: var(--cg-bg);
    color: var(--cg-text);
}
.cg-card-tag__more-btn:focus-visible {
    outline: var(--cg-focus-outline);
    outline-offset: var(--cg-focus-outline-offset);
}
.cg-card-tag__more-pop {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    left: auto;
    z-index: 5;
    width: max-content;
    max-width: 220px;
    padding: var(--cg-space-2) 11px;
    background: var(--cg-bg);
    border: 1px solid var(--cg-border);
    border-radius: var(--cg-radius-sm);
    box-shadow: var(--cg-shadow-md);
    font-size: 0.76rem;
    font-weight: 600;
    line-height: 1.4;
    color: var(--cg-text);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px);
    transition: opacity 0.15s ease, visibility 0.15s ease, transform 0.15s ease;
    pointer-events: none;
}
/* Pont invisible : couvre le gap de 6px pour que le survol ne retombe pas
   entre le bouton et la bulle. */
.cg-card-tag__more-pop::before {
    content: '';
    position: absolute;
    top: -6px;
    left: 0;
    right: 0;
    height: 6px;
}
.cg-card-tag--more:hover .cg-card-tag__more-pop,
.cg-card-tag--more:focus-within .cg-card-tag__more-pop {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    pointer-events: auto;
}

.cg-card--club .cg-card-tag {
    font-size: .78rem; padding: 2px var(--cg-space-2); border-radius: var(--cg-radius-pill, var(--cg-radius-pill));
    background: var(--cg-bg-soft, var(--cg-bg-soft)); border: 1px solid var(--cg-border, var(--cg-border)); color: var(--cg-text-soft, var(--cg-text-soft));
}
/* 🔴 DEUX DES TROIS AXES ÉTAIENT HORS PALETTE. Mesuré sur une carte club :
   `#6b4d9a`, un violet qu'aucun jeton ne porte, et `#1f7a3d`, un vert écrit à la
   main à côté d'un `--cg-info` qui, lui, passait par le jeton. Une couleur de
   catégorie se déclare avec sa raison ou reprend les jetons ; celle-ci n'était
   déclarée nulle part, elle reprend donc les jetons.
   🔒 LE CONTRASTE DÉCIDE DU JETON, PAS LA TEINTE. L'étiquette est servie à
   .78rem — texte courant, seuil 4,5:1 sur blanc. Mesuré : --cg-success vaut
   4,12 et --cg-warning 4,04, tous deux SOUS le seuil ; ils sont écartés malgré
   leur sémantique. Retenus : --cg-info 6,62 · --cg-accent-ink 5,94 ·
   --cg-primary 12,81.
   🔒 LA COULEUR N'EST PAS LE PORTEUR. Le libellé de l'axe est écrit en toutes
   lettres dans l'étiquette, et `aria-label` le redit : trois encres distinctes
   restent un confort de lecture, jamais la seule distinction. */
/* 🔄 LE BLEU SORT DE LA PALETTE, MÊME EN ÉTANT UN JETON. `--cg-info` (#245f99)
   passait le contraste à 6,62 et c'est pour ça qu'il avait été retenu — mais le
   contraste dit si une encre est LISIBLE, pas si elle appartient à la palette du
   site. Sur une carte de club servie parmi des bruns et des ocres, ce bleu se
   lisait comme un accident. Même famille que le violet et le vert d'A22.
   🔒 Les trois encres restent au-dessus de 4,5:1 et deviennent une RAMPE CHAUDE :
   ocre 5,94 · brun moyen 9,85 · brun foncé 12,81. Et la règle du bloc au-dessus
   tient toujours — la couleur n'est pas le porteur : le libellé de l'axe est écrit
   en toutes lettres et `aria-label` le redit. */
.cg-badge--axe-sport     { color: var(--cg-accent-ink); border: 1px solid var(--cg-accent-ink); }
.cg-badge--axe-education { color: var(--cg-primary-2); border: 1px solid var(--cg-primary-2); }
.cg-badge--axe-mixte     { color: var(--cg-primary); border: 1px solid var(--cg-primary); }

.clubs-archive { padding: 0 0 var(--cg-space-9); }
/* .clubs-grid : la grille est mutualisée dans .cg-facets-grid */
.club-disciplines { columns: 2; margin: 0 0 var(--cg-space-2); }
.club-debutants { color: var(--cg-text-soft, var(--cg-text-soft)); margin: 0 0 var(--cg-space-3); }
/* Valeur inconnue : l'information reste dite (3-états tenu sur le fond) mais au
   poids d'une note de bas de fiche, pas d'une section (F4-3). */
.club-debutants--inconnu {
    margin: var(--cg-space-6) 0 0;
    font-size: var(--cg-text-sm, .9375rem);
    color: var(--cg-text-muted, var(--cg-text-muted));
}

/* ============================================================
   Carte d'archive de mission (Leaflet + attribution ODbL)
   ============================================================ */
.cg-mission-map-wrap { margin: 0 0 28px; }
.cg-mission-map {
    height: 440px;
    border-radius: var(--cg-radius-sm, var(--cg-radius-sm));
    border: 1px solid var(--cg-border, var(--cg-border));
}
.cg-map-legend {
    list-style: none; display: flex; flex-wrap: wrap; gap: var(--cg-space-2) 18px;
    margin: 10px 0 var(--cg-space-1); padding: 0; font-size: var(--cg-text-sm, .9375rem);
}
.cg-map-legend-item { display: flex; align-items: center; gap: 6px; }
.cg-map-legend-swatch { width: 14px; height: 14px; border-radius: 50%; display: inline-block; border: 1px solid rgba(0,0,0,.15); }
.cg-map-note { margin: 0; font-size: .9em; color: var(--cg-text-muted, var(--cg-text-muted)); }
.cg-map-popup-badge { color: var(--cg-text-soft, var(--cg-text-soft)); }

/* Recadrage EXPLICITE (F4). La carte se recadrait toute seule à chaque case
   cochée : le repère géographique changeait sous l'œil au moment précis où on
   lisait la carte. Le recadrage est proposé, plus imposé — sauf si le cadre
   courant ne montre plus aucun résultat (voir cg-mission-map.js). */
.cg-map-refit {
    padding: 6px 10px;
    border: 1px solid var(--cg-border, var(--cg-border));
    border-radius: var(--cg-radius-xs, var(--cg-radius-xs));
    background: var(--cg-bg);
    color: var(--cg-heading, var(--cg-heading));
    font-size: var(--cg-text-sm, .9375rem);
    font-weight: 600;
    cursor: pointer;
    box-shadow: var(--cg-shadow-sm, 0 1px 2px rgba(0,0,0,.04));
}
.cg-map-refit:hover { border-color: rgba(69, 45, 22, .35); }
.cg-map-refit[hidden] { display: none; }

/* ============================================================
   Layout d'archive de mission : sidebar facettes + grille
   ============================================================ */
/* 🔒 224 px, LE JETON DU MODÈLE, ET C'EST LE LEVIER DE LA GRILLE. Le brief §12 dit
   « sidebar ~210–230px ». Elle en faisait 260 : ces 36 px de trop ne coûtent pas un
   peu de place, ils repoussent le seuil de la colonne suivante. La largeur de la
   barre latérale est un réglage de la GRILLE, pas de la barre.
   `minmax(0, 1fr)` : sans le 0, la colonne principale déborde au lieu de rétrécir. */
.cg-mission-layout { display: grid; grid-template-columns: 224px minmax(0, 1fr); gap: 22px; align-items: start; }
.cg-mission-main { min-width: 0; }
.cg-facets-summary { margin: 0 0 var(--cg-space-3); font-weight: 600; color: var(--cg-text-soft, var(--cg-text-soft)); }
/* ═══ LA COUTURE DES COLONNES COLLANTES SE DÉRIVE DE L'EN-TÊTE ══════════════════
   🔴 DEUX NOMBRES ESTIMÉS, ET LES DEUX FAUX. La barre latérale se collait à 90 px et
      la colonne carte à 16 px, avec en commentaire « le header Astra ne se sticke pas
      ici ». Mesuré à 1440 px après 1600 px de défilement : l'en-tête collant occupe
      0 -> 99 px. La carte se posait donc à y=16, soit 83 px DERRIÈRE lui, et la barre
      latérale à y=90, encore 9 px dessous. C'est le « collé au header » de Jordan, et
      ce n'était pas un défaut de dessin : les deux nombres avaient été devinés.
   🔒 LA HAUTEUR N'EST PAS À DEVINER, ELLE EST PUBLIÉE. `assets/js/nav.js` écrit
      `--cg-header-bottom` sur la racine à chaque changement d'état (déconnecté,
      connecté, barre d'admin). La couture s'y ajoute, et c'est le même jeton d'espace
      que celui posé entre le héros et le contenu. Une seule expression, deux colonnes,
      et elle suit l'en-tête au lieu de le supposer.
   🔒 Le repli de 80 px est celui que nav.css emploie déjà pour la même variable : il
      ne s'agit pas d'inventer une seconde valeur de secours. */
:root {
    /* 🔄 07/09 — LA COUTURE RÉSERVE POUR CE QUI RESTE, PLUS POUR CE QUI EXISTE.
       Elle lisait `--cg-header-bottom`, c'est-à-dire la HAUTEUR de l'en-tête. En
       mobile, cet en-tête n'est pas collant : il part au défilement, et la couture
       gardait quand même 105 px de vide sous le bord — le bouton de filtres flottait
       au milieu de rien. `--cg-header-collant` vaut la même hauteur QUAND l'en-tête
       tient, et 0 sinon (posée par nav.js, qui lit son `position` calculé).
       🔒 Le repli garde l'ancien comportement si le JS ne tourne pas : mieux vaut
       trop d'espace qu'un bouton sous l'en-tête. */
    --cg-colonne-collante-top: calc(var(--cg-header-collant, var(--cg-header-bottom, 80px)) + var(--cg-space-6));
}

/* ══ 🔴 A38 — LE PANNEAU COLLANT LÂCHAIT, ET SON BAS ÉTAIT INATTEIGNABLE ══════
   Mesuré EN DÉFILANT (un sticky lu à l'arrêt ne dit rien : il n'a pas encore
   collé) sur 56 vues, aux quatre largeurs : 165 relevés, deux familles.
     · le panneau mesure 1364 à 1857 px pour un écran de 900 — le bas des facettes
       n'est atteignable par aucun geste ;
     · et parce qu'il dépasse l'écran, son collant LÂCHE : son haut descend à
       -180 puis -404 px, il passe donc sous l'en-tête, qui est lui-même collant.
   🔒 UNE SEULE CAUSE, UN SEUL CORRECTIF. Le décalage était bon — `top: 123px`
   pour un en-tête haut de 99 : ce n'est pas lui qu'il fallait régler. Un élément
   collant plus haut que la fenêtre défile avec la page jusqu'à ce que son bas
   arrive : le borner à la fenêtre le rend collant POUR DE BON, et lui rend son
   bas par un défilement interne. Le débordement du header disparaît par voie de
   conséquence, sans qu'on touche au décalage.
   🔒 `overscroll-behavior: contain` : sans lui, arriver au bout du panneau
   relance le défilement de la PAGE — le lecteur croit avoir atteint la fin des
   filtres et se retrouve ailleurs. */
/* 🔴 LE PANNEAU DE FILTRES NE COLLAIT PAS, ET SA RÈGLE `sticky` ÉTAIT POURTANT BONNE
   (J1, salve du 07/09). Mesuré à 1440 sur /refuges/ et /clubs-canins/ : le wrapper
   rendait bien `position: sticky; top: 123px`, et il partait quand même à y = −1160
   après 1 800 px de défilement. Un `sticky` qui ne colle pas n'est presque jamais le
   `sticky` : c'est son CONTENANT.

   LA CAUSE : la grille `.cg-mission-layout` porte `align-items: start`, donc la
   colonne du panneau ne fait que la hauteur de son contenu — 499 px — quand la rangée
   en fait 4 286. Un élément collant ne voyage que DANS son bloc conteneur : le sien
   se terminait avec lui, il n'avait aucune place où coller.

   🔒 ON ÉTIRE LA COLONNE, ON NE TOUCHE PAS À LA GRILLE. `align-items: start` est le
   bon réglage pour les deux autres colonnes (les cartes et la carte géographique, qui
   a son propre collant) ; seule celle du panneau a besoin de la hauteur de la rangée.
   `align-self` sur l'enfant dit exactement cela, sans rien changer aux voisins. */
.cg-facets-sidebar {
    align-self: stretch;
}

/* 🔒 ET LE FORMULAIRE AUSSI, SINON L'ÉTIREMENT S'ARRÊTE UN CRAN TROP TÔT. Mesuré
   après le premier correctif : l'aside faisait bien 4 286 px, mais le `<form>` qui
   la remplit en faisait toujours 499 — et c'est LUI le bloc conteneur du wrapper
   collant. Un `sticky` ne voyage que dans son parent immédiat qui a une hauteur ;
   étirer le grand-parent ne suffit pas. Deux déclarations parce qu'il y a deux
   boîtes, pas parce qu'on tâtonne. */
.cg-facets-sidebar > form {
    height: 100%;
}

.cg-facets-sidebar .filters-wrapper {
    position: sticky;
    top: var(--cg-colonne-collante-top);
    max-height: calc(100vh - var(--cg-colonne-collante-top) - var(--cg-space-6));
    overflow-y: auto;
    overscroll-behavior: contain;
}

/* ═══ LA PEAU DU PANNEAU DE FILTRES — celle du modèle, sur les six ══════════════
   🔴 Le modèle servait un panneau BLANC, encadré, rayon 18 px, avec ombre ; les cinq
      annuaires une colonne NUE. « Pas le même encadré » n'était pas une nuance de
      couleur : c'était l'absence du cadre. Ces règles vivaient dans page-races-liste.css,
      que les cinq ne chargent pas.
   🔒 Valeurs du modèle, à l'identique. Les deux lignes qui suivaient chez les annuaires
      (marge sous l'en-tête, graisse 800 du titre) tombent : elles habillaient une colonne
      sans cadre, et elles contredisent maintenant le composant. */
.filters-wrapper {
    background: var(--cg-bg);
    border: 1px solid var(--cg-border);
    border-radius: var(--cg-radius-lg);
    overflow: hidden;
    box-shadow: var(--cg-shadow-sm);
}

.filters-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--cg-space-3);
    padding: 14px var(--cg-space-4);
    border-bottom: 1px solid #f3f4f6;
    background: var(--cg-bg);
}

.filters-title {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--cg-primary);
    letter-spacing: -0.01em;
}


/* Le compteur de facette est POUSSÉ À DROITE, et le texte prend la place restante.
   🔴 Ces deux règles vivaient aussi dans la feuille du modèle. Sans elles, le compteur
      suit simplement le texte : mesuré, l'écart entre le bord droit du libellé et celui
      du compteur valait 6 px sur le modèle, mais 11 sur les clubs, 38 sur les aliments,
      53 sur les refuges et 100 sur les restaurants. Les chiffres ne formaient plus de
      colonne, et c'est le « compteurs pas alignés à droite » de Jordan. */
/* Texte du label : prend la largeur restante, compteur poussé à droite via margin-left:auto */
.filter-options label > span:not(.filter-option-count) {
    flex: 1;
    min-width: 0;
    color: var(--cg-text);
}

.filter-option-count {
    margin-left: auto;
    flex-shrink: 0;
    color: var(--cg-text-muted);
    font-size: 0.78rem;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    user-select: none;
}
/* `padding: 0` : la règle neutralisait le fond et la bordure du bouton Astra mais
   pas son padding (15px 30px), d'où une zone de 138x54px pour 78px de texte —
   l'en-tête des filtres s'en trouvait déformé et le soulignement flottait. */
.cg-facets-sidebar .btn-reset { background: none; border: none; padding: 0; line-height: 1.4; color: var(--cg-info, var(--cg-info)); cursor: pointer; font-size: var(--cg-text-sm, .9375rem); text-decoration: underline; }
/* =========================================================
   Groupes de filtres — COMPOSANT PARTAGÉ par les six catalogues.

   🔴 Il vivait dans page-races-liste.css, que seul /races/ charge ; les cinq annuaires
      en avaient une seconde version ici même, avec d'autres graisses et surtout une
      AUTRE RÉPONSE à la même question — le modèle replie derrière un <summary>, eux
      servaient tout déplié avec un ascenseur interne. Deux grammaires, deux fichiers,
      qui vieillissaient séparément.

   🔒 Les valeurs sont celles du MODÈLE, à l'identique : /races/ est la référence, et
      l'harmonisation ne se paie pas d'un changement sur la référence.

   🔒 UNE SEULE ENVELOPPE. J'ai d'abord porté chaque règle sur « .filters-wrapper » ET
      « .cg-facets-sidebar », croyant à deux enveloppes concurrentes. Mesuré sur les six
      pages SERVIES : .filters-wrapper entoure les groupes partout, y compris dans les
      annuaires où il est imbriqué dans .cg-facets-sidebar. Le second préfixe ne
      sélectionnait rien de plus — c'était un second nom pour un seul composant.
   ========================================================= */


.filters-wrapper .filter-group {
    padding: var(--cg-space-3) var(--cg-space-4);
    border-bottom: 1px solid #f3f4f6;
}
.filters-wrapper .filter-group:last-child {
    border-bottom: none;
}

/* Cas A — sidebar_group "Profil du maître" : titre direct, pas d'accordéon. */
.filters-wrapper .filter-group .filter-label {
    display: block;
    margin: 0 0 var(--cg-space-2);
    font-size: 0.86rem;
    font-weight: 600;
    color: var(--cg-primary);
    letter-spacing: -0.005em;
}

/* Cas B/C — <details> natifs : pas de bordure individuelle, pas de fond. */
.filters-wrapper .filter-group details {
    border: none;
    background: transparent;
    border-radius: 0;
    overflow: visible;
}

.filters-wrapper .filter-group summary {
    list-style: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--cg-space-1) 0;
    background: transparent;
    font-size: 0.86rem;
    font-weight: 600;
    color: var(--cg-primary);
    letter-spacing: -0.005em;
    user-select: none;
}
.filters-wrapper .filter-group summary::-webkit-details-marker { display: none; }
.filters-wrapper .filter-group summary::after {
    content: "›";
    display: inline-block;
    font-size: 1.1rem;
    line-height: 1;
    color: #9ca3af;
    transition: transform 0.18s ease;
    transform: rotate(90deg);
}
.filters-wrapper .filter-group details[open] summary::after {
    transform: rotate(-90deg);
}

/* Liste d'options : label flex avec compteur droite-aligné (pattern Booking). */
.filters-wrapper .filter-options {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: var(--cg-space-2);
}

.filters-wrapper .filter-options label {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 5px 6px 5px var(--cg-space-1);
    margin: 0;
    border-radius: var(--cg-radius-xs);
    cursor: pointer;
    font-size: 0.86rem;
    line-height: 1.4;
    color: var(--cg-text);
    transition: background-color 0.12s ease;
}
.filters-wrapper .filter-options label:hover {
    background: #f9fafb;
}

.filters-wrapper .filter-options input[type="checkbox"] {
    flex: 0 0 16px;
    width: 16px;
    height: 16px;
    margin: 0;
    accent-color: var(--cg-primary);
    cursor: pointer;
}
/* ── ÉCART DÉCLARÉ des annuaires : l'ascenseur interne ────────────────────────
   Le modèle n'a jamais eu de groupe au-delà de 14 options. Les annuaires en ont
   de 75 (communes de promenades), 50 (communes de clubs), 23, 18. Un <details>
   qui s'ouvre sur 75 cases pousse tout le reste hors de l'écran. Les deux
   dispositifs ne se concurrencent donc plus, ils se superposent : le repli sert à
   VOIR TOUS LES CRITÈRES d'un coup, l'ascenseur à ouvrir un critère énorme sans
   perdre les suivants. Il ne borne que ce qui dépasse : un groupe court n'y touche pas. */
.cg-facets-sidebar .filter-options { max-height: 220px; overflow: auto; }

/* Aucun résultat : la grille se vidait en silence, seul le compteur à 0 le
   signalait. Injecté par cg-facets.js -> les 4 archives en héritent. */
.cg-facets-empty {
    margin: 0;
    padding: 18px;
    border: 1px dashed var(--cg-border, var(--cg-border));
    border-radius: var(--cg-radius-sm, var(--cg-radius-sm));
    color: var(--cg-text-soft, var(--cg-text-soft));
}
.cg-facets-empty-reset {
    background: none; border: none; padding: 0;
    color: var(--cg-info, var(--cg-info)); text-decoration: underline;
    cursor: pointer; font-size: inherit;
}

@media (max-width: 860px) {
    .cg-mission-layout { grid-template-columns: 1fr; }
    .cg-facets-sidebar .filters-wrapper { position: static; }

    /* Le pied du tiroir : collé en bas, au-dessus de la liste qui défile. */
    .cg-facets-sidebar.open .filters-footer {
        display: flex;
        position: sticky;
        bottom: 0;
        align-items: center;
        gap: var(--cg-space-3);
        margin: var(--cg-space-3) 0 0;
        padding: var(--cg-space-3) 0 0;
        border-top: 1px solid var(--cg-border);
        background: #fff;
    }
    .cg-facets-sidebar.open .filters-footer .filters-reset {
        flex: 0 0 auto;
        font-size: var(--cg-text-sm, .9375rem);
        font-weight: 600;
        color: var(--cg-text-soft);
        text-decoration: underline;
    }
    .cg-facets-sidebar.open .filters-footer .btn-voir-resultats {
        flex: 1 1 auto;
        min-height: var(--cg-cible-tactile, 44px);
        padding: var(--cg-space-3) 14px;
        border: 1px solid var(--cg-primary);
        border-radius: var(--cg-radius-pill);
        background: var(--cg-primary);
        color: #fff;
        font-size: 0.92rem;
        font-weight: 600;
        cursor: pointer;
    }

    /* Facettes en PASTILLES sous 860px (F4). Empilées verticalement, les trois
       groupes occupaient ~590px : sur mobile la carte (558px) puis les filtres
       repoussaient la première fiche à ~1500px du haut — on scrollait un écran et
       demi avant de voir un seul lieu. Les pastilles rendent la même information
       sur ~340px. La case reste un vrai checkbox (masqué visuellement, pas pour
       le clavier ni pour les lecteurs d'écran). */
    .cg-facets-sidebar .filter-options {
        flex-direction: row; flex-wrap: wrap; gap: var(--cg-space-2);
        max-height: 132px;                       /* la commune monte à ~40 options */
    }
    .cg-facets-sidebar .filter-options label {
        gap: 5px;
        padding: 5px 11px;
        border: 1px solid var(--cg-border, var(--cg-border));
        border-radius: var(--cg-radius-pill, var(--cg-radius-pill));
        background: var(--cg-bg);
    }
    .cg-facets-sidebar .filter-options input[type="checkbox"] {
        position: absolute; width: 1px; height: 1px;
        opacity: 0; margin: 0; pointer-events: none;
    }
    .cg-facets-sidebar .filter-options label:has(input:checked) {
        border-color: var(--cg-primary, var(--cg-primary));
        background: var(--cg-bg-alt, var(--cg-bg-alt));
        font-weight: 700;
    }
    .cg-facets-sidebar .filter-options label:has(input:focus-visible) {
        outline: 2px solid var(--cg-info, var(--cg-info));
        outline-offset: 2px;
    }

    /* La carte prenait 558px de haut avant le moindre résultat. */
    .cg-mission-layout--map .cg-mission-map { height: 300px; }
}

/* ============================================================
   Layout CO-VISIBLE (§B.3) : facettes | grille | CARTE STICKY.
   La carte reste dans le champ de vision pendant le filtrage
   (filtres + carte co-visibles) ; la grille défile entre les deux.
   ============================================================ */
/* Par défaut (mobile/tablette) : empilé, carte EN PREMIER, non sticky. */
.cg-mission-layout--map { grid-template-columns: 1fr; }
.cg-mission-layout--map .cg-mission-map-col { order: -1; }
.cg-mission-layout--map .cg-mission-map-col .cg-mission-map-wrap { margin: 0 0 var(--cg-space-5); }

@media (min-width: 1100px) {
    /* Largeur des archives de mission (F4). Le conteneur ÉDITORIAL (1000px) ne
       laissait que 394px à la colonne du milieu une fois les facettes (210) et la
       carte (380) déduites : sous les 540px qu'il faut à deux cartes de 260, la
       grille retombait en UNE colonne — 162 espaces = 71 000px de défilement, et
       le même symptôme sur les clubs et les restaurants. Une archive filtrable est
       une page OUTIL, pas un article : elle prend la largeur outil (comme le
       comparateur, déjà en container-xl). Sélecteur doublé pour passer devant
       `.cg-container-lg` quel que soit l'ordre des feuilles. */
    .cg-container-lg.espaces-archive,
    .cg-container-lg.clubs-archive,
    .cg-container-lg.restaurants-archive,
    .cg-container-lg.refuges-archive {
        max-width: calc(var(--cg-container-xl, var(--cg-container-xl)) + 40px);
    }

    /* La cause n'est pas le NOM de l'archive, c'est la présence d'une COLONNE
       CARTE : sans les 336px de carte, 1040 suffit (croquettes y tient ses deux
       colonnes). Portée par la cause, la règle attrape d'elle-même la prochaine
       archive géo — refuges est arrivée avec une carte et une grille retombée à
       une colonne, exactement le symptôme décrit ci-dessus, parce qu'elle
       manquait à la liste. Les noms restent au-dessus comme repli si `:has()`
       n'est pas disponible. */
    .cg-container-lg:has(.cg-mission-layout--map) {
        max-width: calc(var(--cg-container-xl, var(--cg-container-xl)) + 40px);
    }

    /* 3 colonnes : facettes étroites | grille | carte sticky.
       🔒 LA BARRE PREND LE JETON DU MODÈLE (224), COMME PARTOUT AILLEURS. Elle était à
       200 ici et à 260 sans carte : trois largeurs pour une même barre sur un même site.
       224 + 22 + 596 + 22 + 336 = 1200.
       ⚑ ET CE N'EST PAS LA BARRE QUI PLAFONNE LA GRILLE ICI, C'EST LA CARTE. Mesuré :
       même en ramenant la barre à zéro, la colonne du milieu atteindrait 820 px, soit à
       peine les 809 px qu'il faut à trois cartes de 255. Le passage de 200 à 224 ne
       change donc PAS le nombre de colonnes — vérifié, pas supposé. La différence de
       densité avec /races/ vient du troisième panneau, qui est un comportement propre
       à cette famille et qui se conserve. */
    .cg-mission-layout--map { grid-template-columns: 224px minmax(0, 1fr) 336px; gap: 22px; }
    .cg-mission-layout--map .cg-mission-map-col {
        order: 0;
        position: sticky;
        /* 🔄 « Le header Astra ne se sticke pas ici » : c'était faux au moment où on l'a
           écrit, ou ça a cessé de l'être. Mesuré, il occupe 0 -> 99 px et la carte se
           posait à 16. Même couture que la barre latérale, dérivée de la hauteur
           publiée par le script plutôt que devinée. */
        top: var(--cg-colonne-collante-top);
        align-self: start;
    }
    /* Preview connecté : dégager la barre d'admin WP fixe (32px desktop). */
    .admin-bar .cg-mission-layout--map .cg-mission-map-col { top: 48px; }
    .cg-mission-layout--map .cg-mission-map-col .cg-mission-map-wrap { margin: 0; }
    .cg-mission-layout--map .cg-mission-map-col .cg-mission-map { height: 70vh; max-height: 640px; }
}

/* ============================================================
   Restaurants — carte (avatar-initiale + badge politique chien)
   ============================================================ */
.cg-card--restaurant .cg-card-tag {
    font-size: .78rem; padding: 2px var(--cg-space-2); border-radius: var(--cg-radius-pill, var(--cg-radius-pill));
    background: var(--cg-bg-soft, var(--cg-bg-soft)); border: 1px solid var(--cg-border, var(--cg-border)); color: var(--cg-text-soft, var(--cg-text-soft));
}
/* Badge politique chien — couleur ET libellé texte (jamais la couleur seule). */
.cg-badge--pol-chiens-acceptes { color: #1f7a3d; border: 1px solid #1f7a3d; }
.cg-badge--pol-terrasse        { color: #b5651d; border: 1px solid #b5651d; }
.cg-badge--pol-a-confirmer     { color: var(--cg-text-soft);    border: 1px solid #c9c9c9; }
.cg-badge--pol-refuse          { color: #b23b3b; border: 1px solid #b23b3b; }

.restaurants-archive { padding: 0 0 var(--cg-space-9); }
/* .restaurants-grid : la grille est mutualisée dans .cg-facets-grid */

/* ============================================================
   Restaurants — fiche (bloc politique chien + provenance par champ)
   ============================================================ */
/* 🔒 PROPRIÉTÉ LONGUE, PAS RACCOURCI. Écrit `padding: 0 0 …`, ce bloc AFFIRMAIT
   un haut à zéro et écrasait la règle unique de global.css, qui vient avant.
   Un raccourci ne cède rien : il déclare les quatre côtés. */
.single-restaurant { padding-bottom: var(--cg-space-9); }
.single-restaurant-title { font-size: var(--cg-text-3xl, var(--cg-text-3xl)); margin: var(--cg-space-2) 0 var(--cg-space-1); color: var(--cg-heading, var(--cg-heading)); }
.single-restaurant-sub { margin: 0 0 var(--cg-space-5); color: var(--cg-text-soft, var(--cg-text-soft)); }

.resto-policy {
    margin: 0 0 28px; padding: var(--cg-space-4) 18px;
    border: 1px solid var(--cg-border, var(--cg-border)); border-left-width: 5px;
    border-radius: var(--cg-radius-sm, var(--cg-radius-sm)); background: var(--cg-bg-alt, var(--cg-bg-alt));
}
.resto-policy--chiens-acceptes { border-left-color: #1f7a3d; }
.resto-policy--terrasse        { border-left-color: #b5651d; }
.resto-policy--a-confirmer     { border-left-color: #c9c9c9; }
.resto-policy--refuse          { border-left-color: #b23b3b; }
.resto-policy-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin: 0 0 var(--cg-space-2); font-size: var(--cg-text-xl, var(--cg-text-xl)); color: var(--cg-heading, var(--cg-heading)); }
.resto-policy-niveau { font-size: var(--cg-text-sm, .9375rem); font-weight: 500; color: var(--cg-text-soft, var(--cg-text-soft)); }
.resto-policy-cite { margin: var(--cg-space-2) 0; font-style: italic; color: var(--cg-text-soft, #555555); }
.resto-policy-perim { margin: var(--cg-space-1) 0; color: var(--cg-text-soft, var(--cg-text-soft)); }
.resto-policy-source { margin: var(--cg-space-1) 0; font-size: var(--cg-text-sm, .9375rem); }
.resto-freshness { margin: 6px 0 0; font-size: var(--cg-text-sm, .9375rem); color: var(--cg-text-muted, var(--cg-text-muted)); }
.resto-freshness.is-stale { color: #9a6a00; font-weight: 600; }

.cg-champ-prov { font-size: .86em; color: var(--cg-text-muted, var(--cg-text-muted)); }
.cg-champ-prov a { color: var(--cg-info, var(--cg-info)); }

.cg-odbl-attribution { margin: var(--cg-space-6) 0 0; font-size: .86rem; color: var(--cg-text-muted, var(--cg-text-muted)); }

/* ============================================================
   Catalogue aliments — archive par recette + fiche
   ============================================================ */
.aliments-archive { padding: 0 0 var(--cg-space-9); }
/* .aliments-grid : la grille est mutualisée dans .cg-facets-grid */

.cg-card--aliment { position: relative; display: flex; flex-direction: column; }
.cg-card--aliment .cg-card-title a::after { content: ""; position: absolute; inset: 0; }
.cg-card--aliment .cg-card-eyebrow { margin: 0; font-size: .78rem; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--cg-text-muted, var(--cg-text-muted)); }
.cg-card--aliment .cg-card-meta { margin: 0; font-size: var(--cg-text-sm, .9375rem); color: var(--cg-text-soft, var(--cg-text-soft)); }

/* Type de produit = libellé SOBRE (l'info sans emoji ni vitrine — persona méfiant). */
.cg-alim-icon { padding: 6px 14px; border-radius: var(--cg-radius-pill, var(--cg-radius-pill)); background: var(--cg-bg); border: 1px solid var(--cg-border, var(--cg-border)); font-size: .8rem; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--cg-text-soft, var(--cg-text-soft)); }

/* Badge de note — chiffre TOUJOURS écrit (couleur jamais seule porteuse). */
.cg-card--aliment .cg-badge {
    position: absolute; top: 10px; left: 10px; z-index: 1;
    padding: 3px 9px; border-radius: var(--cg-radius-pill, var(--cg-radius-pill));
    font-size: .78rem; font-weight: 700; background: var(--cg-bg); box-shadow: var(--cg-shadow-sm, 0 1px 2px rgba(0,0,0,.04));
}
.cg-badge--note-haute   { color: #1f7a3d; border: 1px solid #1f7a3d; }
.cg-badge--note-moyenne { color: #9a6a00; border: 1px solid var(--cg-status-mid); }
.cg-badge--note-basse   { color: #b23b3b; border: 1px solid #b23b3b; }

/* Fiche : cotation transparente */
/* 🔒 PROPRIÉTÉ LONGUE, PAS RACCOURCI. Écrit `padding: 0 0 …`, ce bloc AFFIRMAIT
   un haut à zéro et écrasait la règle unique de global.css, qui vient avant.
   Un raccourci ne cède rien : il déclare les quatre côtés. */
.single-aliment { padding-bottom: var(--cg-space-9); }
.single-aliment-marque { margin: var(--cg-space-2) 0 2px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--cg-text-muted, var(--cg-text-muted)); font-size: .8rem; }
.single-aliment-title { font-size: var(--cg-text-3xl, var(--cg-text-3xl)); margin: 0 0 var(--cg-space-1); color: var(--cg-heading, var(--cg-heading)); }
.single-aliment-format { margin: 0 0 var(--cg-space-5); color: var(--cg-text-soft, var(--cg-text-soft)); }

.alim-cotation { margin: 0 0 28px; padding: var(--cg-space-4) 18px; border: 1px solid var(--cg-border, var(--cg-border)); border-left-width: 5px; border-radius: var(--cg-radius-sm, var(--cg-radius-sm)); background: var(--cg-bg-alt, var(--cg-bg-alt)); }
.alim-cotation--haute   { border-left-color: #1f7a3d; }
.alim-cotation--moyenne { border-left-color: var(--cg-status-mid); }
.alim-cotation--basse   { border-left-color: #b23b3b; }
.alim-cotation-head { margin: 0 0 6px; font-size: var(--cg-text-xl, var(--cg-text-xl)); color: var(--cg-heading, var(--cg-heading)); }
.alim-cotation-val { font-weight: 800; }
.alim-cotation-formule { margin: var(--cg-space-1) 0; color: var(--cg-text-soft, #555555); }
.alim-cotation-base { margin: 6px 0; padding-left: 18px; font-size: var(--cg-text-sm, .9375rem); color: var(--cg-text-soft, var(--cg-text-soft)); }
.alim-base-k { font-weight: 700; }
.alim-cotation-perim, .alim-cotation-hors { margin: var(--cg-space-1) 0; font-size: var(--cg-text-sm, .9375rem); color: var(--cg-text-muted, var(--cg-text-muted)); }

.cg-champ-cite { color: var(--cg-text-muted, var(--cg-text-muted)); font-style: italic; }

/* Provenance produit + relevé prix (2 horloges) */
.cg-provenance--produit .cg-provenance-note { color: var(--cg-text-muted, var(--cg-text-muted)); font-size: .9em; }
/* 🔄 N-bis-1 — DEUX BALISAGES, UNE SEULE MISE EN PAGE.
   Le composant du plugin marketing rend la même chose que le relevé de prix du thème,
   sous d'autres noms de classes (`cg-vendeurs*`). Mesuré sans ces règles : « Maxi
   Zoo38,50 € · 7,70 €/kgrelevé le 09/09/2026Voir chez Maxi Zoo » — tout collé, la ligne
   n'était plus lisible.
   🔒 LES DEUX FAMILLES DE NOMS PARTAGENT LES MÊMES DÉCLARATIONS, dans les mêmes règles.
   Recopier le bloc sous les nouveaux noms aurait fait deux mises en page d'une seule
   chose, qui auraient divergé à la première retouche — et le rendu de repli (plugin
   désactivé) aurait fini par ne plus ressembler au rendu normal.
   🔒 `.cg-lien-sortant` est SCOPÉ à la ligne de vendeur : c'est une classe générique du
   plugin, portée aussi par les liens de contact des fiches de lieu, qui ne doivent rien
   recevoir d'ici. */
.cg-releve-prix { margin: var(--cg-space-6) 0; padding: var(--cg-space-4) 18px; border: 1px solid var(--cg-border, var(--cg-border)); border-radius: var(--cg-radius-sm, var(--cg-radius-sm)); }
.cg-releve-prix-title { margin: 0 0 10px; font-size: var(--cg-text-xl, var(--cg-text-xl)); color: var(--cg-heading, var(--cg-heading)); }
.cg-releve-prix-list,
.cg-vendeurs { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--cg-space-2); }
.cg-releve-prix-item,
.cg-vendeurs-item { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 14px; padding: 6px 0; border-bottom: 1px solid var(--cg-border, var(--cg-border)); }
.cg-releve-prix-dist,
.cg-vendeurs-nom { font-weight: 700; min-width: 90px; }
.cg-releve-prix-val,
.cg-vendeurs-prix { font-weight: 600; }
.cg-releve-prix-date,
.cg-vendeurs-date { font-size: var(--cg-text-sm, .9375rem); color: var(--cg-text-muted, var(--cg-text-muted)); }
.cg-releve-prix-link,
.cg-vendeurs-item .cg-lien-sortant { margin-left: auto; color: var(--cg-info, var(--cg-info)); }
/* La mention « lien rémunéré » se lit AVEC son lien, jamais séparée de lui. */
.cg-vendeurs-item .cg-lien-sortant-groupe { margin-left: auto; display: inline-flex; align-items: baseline; gap: 6px; }
.cg-lien-sortant-mention { font-size: var(--cg-text-sm, .9375rem); color: var(--cg-text-muted, var(--cg-text-muted)); }
.cg-affiliation-pied-zone { padding: 0 18px var(--cg-space-4); text-align: center; }
.cg-affiliation-pied { margin: 0; font-size: var(--cg-text-sm, .9375rem); color: var(--cg-text-muted, var(--cg-text-muted)); }
.cg-affiliation-encart { margin: var(--cg-space-6) 0; padding: var(--cg-space-4) 18px; border: 1px solid var(--cg-border, var(--cg-border)); border-radius: var(--cg-radius-sm, var(--cg-radius-sm)); background: var(--cg-surface-2, transparent); }
.cg-affiliation-encart p { margin: 0 0 var(--cg-space-2); font-size: var(--cg-text-sm, .9375rem); }
.cg-affiliation-encart p:last-child { margin-bottom: 0; }

.alim-achat { margin: var(--cg-space-6) 0 0; }
.alim-achat-link { font-weight: 700; }

/* ============================================================
   Mission REFUGES — archive + fiche
   🔒 Des ORGANISATIONS : aucune vignette d'animal, aucun média de bête.
   La carte est volontairement TEXTE SEUL — un refuge se choisit sur sa
   commune, ses espèces et sa joignabilité, pas sur une photo.
   ============================================================ */
.refuges-archive { padding: 0 0 var(--cg-space-9); }
.refuges-archive-header { margin: var(--cg-space-2) 0 var(--cg-space-4); }
.refuges-archive-title { font-size: var(--cg-text-3xl, var(--cg-text-3xl)); margin: 0 0 var(--cg-space-2); color: var(--cg-heading, var(--cg-heading)); }
.refuges-archive-intro { margin: 0; max-width: 68ch; color: var(--cg-text-soft, var(--cg-text-soft)); }

/* Le compteur DIT son périmètre : il se lit, il ne se devine pas. */
.refuges-compteur {
    margin: 0 0 var(--cg-space-6);
    font-weight: 600;
    color: var(--cg-text-soft, var(--cg-text-soft));
}

/* .refuges-grid : la grille est mutualisée dans .cg-facets-grid.
   Elle était la SEULE en `auto-fit` et en 280 px : trois écarts, jamais décidés. */
/* 🔴 CETTE RÈGLE RECOPIAIT LE SOCLE, AVEC UNE DIFFÉRENCE. `.cg-card` (global.css)
   pose déjà display, direction, bordure, rayon, fond, ombre et transition ; ce bloc
   les redéclarait tous — et ajoutait un `padding` sur la CARTE que le socle n'a pas.
   Conséquence mesurée en posant le bandeau : la zone média du refuge sortait à
   265 × 166 px là où club et restaurant, à largeur de carte identique (307 px),
   servaient 305 × 191. Le rembourrage appartient à `.cg-card-content`, où le socle
   le met, pas à la carte : sinon le bandeau ne peut pas toucher le bord.
   🔒 Ne reste ici que ce qui DIFFÈRE vraiment : l'ancre de survol, qui prend
   l'accent au lieu de l'ombre du socle. */
.cg-card--refuge { position: relative; }
.cg-card--refuge:hover {
    border-color: var(--cg-accent, var(--cg-accent));
}
.cg-card--refuge .cg-card-title { margin: 0; font-size: var(--cg-text-md, var(--cg-text-md)); line-height: 1.3; }
.cg-card--refuge .cg-card-title a { color: var(--cg-primary, var(--cg-primary)); text-decoration: none; }
.cg-card--refuge .cg-card-title a:hover { text-decoration: underline; }
.cg-card--refuge .cg-card-loc { margin: 0; font-size: var(--cg-text-sm, .9375rem); color: var(--cg-text-soft, var(--cg-text-soft)); }
.refuge-distance { color: var(--cg-text-muted, var(--cg-text-muted)); }
.refuge-reseau {
    margin: 0;
    font-size: var(--cg-text-xs, .8125rem);
    font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
    color: var(--cg-text-muted, var(--cg-text-muted));
}
/* 🔄 `margin: auto 0 0` retiré : mesuré 30 px (1440) et jusqu'à 50 px (390) de blanc
   entre la commune et la ligne d'espèces, sur les cartes plus courtes que leur
   rangée. Le blanc en trop va sous le dernier élément (A7/A8). */
.refuge-card-especes { margin: 0; font-size: var(--cg-text-sm, .9375rem); color: var(--cg-text-muted, var(--cg-text-muted)); }
.refuges-vide { color: var(--cg-text-soft, var(--cg-text-soft)); }

/* Proches hors Bruxelles : section DISTINCTE, jamais fondue dans la liste. */
.refuges-proches { margin: var(--cg-space-8) 0 0; padding-top: 28px; border-top: 1px solid var(--cg-border, var(--cg-border)); }
.refuges-proches h2 { margin: 0 0 6px; }
.refuges-proches-note { margin: 0 0 var(--cg-space-5); max-width: 68ch; font-size: var(--cg-text-sm, .9375rem); color: var(--cg-text-muted, var(--cg-text-muted)); }

/* ── Fiche refuge ── */
/* 🔒 PROPRIÉTÉ LONGUE, PAS RACCOURCI. Écrit `padding: 0 0 …`, ce bloc AFFIRMAIT
   un haut à zéro et écrasait la règle unique de global.css, qui vient avant.
   Un raccourci ne cède rien : il déclare les quatre côtés. */
.single-refuge { padding-bottom: var(--cg-space-9); }
/* 🔒 PLUS DE MARGE SOUS LA BANDE. Le vide bande → contenu est porté UNE FOIS par le
   rembourrage haut du conteneur (règle « vide sous la bande », global.css). Trois
   bandes ajoutaient la leur par-dessus — 22 px, 24 px et 28 px, dont deux en dur —
   ce qui donnait trois fiches à 70+ px là où les quatre autres étaient à 48. */
.single-refuge-head { margin: var(--cg-space-2) 0 0; }
.single-refuge-title { font-size: var(--cg-text-3xl, var(--cg-text-3xl)); margin: 0 0 6px; color: var(--cg-heading, var(--cg-heading)); }
.single-refuge-sub { margin: 0; color: var(--cg-text-soft, var(--cg-text-soft)); }
.refuge-section { margin: 30px 0 0; }
.refuge-section h2 { margin: 0 0 var(--cg-space-3); }
.refuge-horaires { margin: var(--cg-space-3) 0 0; color: var(--cg-text-soft, var(--cg-text-soft)); }

.refuge-especes { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--cg-space-2); }
.refuge-especes li {
    padding: 5px 11px;
    border: 1px solid var(--cg-border, var(--cg-border));
    border-radius: var(--cg-radius-pill, var(--cg-radius-pill));
    background: var(--cg-bg-soft, var(--cg-bg-soft));
    font-size: var(--cg-text-sm, .9375rem);
    color: var(--cg-text-soft, var(--cg-text-soft));
}

/* 3-états : la phrase de repli est le cas NOMINAL (inconnu sur les 58).
   Elle se lit comme une information, pas comme une donnée manquante. */
.refuge-etats { list-style: none; margin: 0; padding: 0; }
.refuge-etat {
    display: flex; flex-direction: column; gap: 2px;
    padding: var(--cg-space-3) 0;
    border-bottom: 1px solid var(--cg-border, var(--cg-border));
}
.refuge-etat-label { font-weight: 700; color: var(--cg-heading, var(--cg-heading)); }
.refuge-etat-valeur { color: var(--cg-text-soft, var(--cg-text-soft)); }
.refuge-etat--inconnu .refuge-etat-valeur { color: var(--cg-text-muted, var(--cg-text-muted)); font-size: var(--cg-text-sm, .9375rem); }

.refuge-provenance { margin-top: 36px; }

/* ============================================================
   Tiroir de filtres + pagination — mode SERVEUR (parcs ①, clubs ②)
   🔒 Scopé sur `.cg-archive-serveur` (posée par les seules archives en mode
   l'URL) et sur `.cg-facets-sidebar--url` : les archives dont le filtre NE passe PAS par l'URL
   ne portent ni l'un ni l'autre, leur mise en page ne peut pas bouger.
   Scopé sur le MODE et non sur `.espaces-archive` : sinon chaque passe suivante
   redécouvre le même défaut — clubs a rendu son bouton d'ouverture en plein
   desktop parce que la règle ne le voyait pas.
   ============================================================ */

/* Le bouton d'ouverture n'existe qu'en dessous du seuil du tiroir. */
.cg-archive-serveur .filters-toggle-btn { display: none; }

.filters-submit { margin: var(--cg-space-4, var(--cg-space-4)) 0 0; }

/* ══ B58 — LE PIED DU TIROIR. Invisible hors tiroir : sur le panneau de bureau, la
   grille est déjà à côté des cases et « Tout réinitialiser » vit avec les chips. */
.filters-footer { display: none; }
.btn-filters-apply {
    width: 100%;
    padding: 11px var(--cg-space-4);
    border: 1px solid var(--cg-primary, var(--cg-primary));
    border-radius: var(--cg-radius-sm, var(--cg-radius-sm));
    background: var(--cg-primary, var(--cg-primary));
    color: #fff;
    font-weight: 700;
    cursor: pointer;
}
.btn-filters-apply:hover { background: var(--cg-primary-2, var(--cg-primary-2)); }

.cg-facets-sidebar--url .btn-reset {
    text-decoration: underline;
    color: var(--cg-info, var(--cg-info));
}
/* 🔴 LE « × » ÉTAIT CLAVETÉ SUR LE MODE DE FILTRAGE, PAS SUR LE COMPOSANT.
   `.cg-facets-sidebar--url` désigne les archives dont le filtre passe par l'URL — une
   propriété de la REQUÊTE, sans rapport avec le fait qu'un tiroir se ferme. Le jour
   où /races/ a reçu le même bouton (08/09), la règle ne l'a pas atteint : mesuré,
   il rendait un bouton ORANGE PLEIN de 69 × 56 px, padding 15/30, visible au bureau
   au milieu du panneau, et il poussait l'entête de 53 à 85 px en décalant les
   quatorze groupes de 31 px. Le composant arrivait sans son habillage.
   🔒 LE SÉLECTEUR NOMME LES DEUX PANNEAUX, c'est-à-dire l'objet, pas le régime. */
.cg-facets-sidebar .filters-close-btn,
.races-sidebar .filters-close-btn { display: none; }

/* Liens de pagination — crawlables, donc de vrais <a>. */
/* ═══ PAGINATION HYBRIDE — composant partagé par TOUTES les surfaces paginées ═══
   🔴 SES RÈGLES DE BASE VIVAIENT DANS LA FEUILLE DU SEUL /races/, et deux surfaces sur
   trois n'en recevaient qu'une part. Mesuré sur le servi : six boutons empilés
   verticalement à x=64 sur la page de résultats, et une rangée calée à gauche sur une
   archive de métier, quand le modèle la centre.
   🔒 La peau des jetons vient du socle (global.css) : forme, bordure, encre et état
   courant y sont décidés une fois pour tous les contrôles du site. Ici, le placement. */
.races-pagination-row {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 18px;
    margin: 38px 0 28px;
}

.races-pagination-seo {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 2px;
    font-size: 0.86rem;
    color: var(--cg-text-muted);
}

.races-pagination-seo .page-numbers {
    padding: 0 var(--cg-space-2);
    font-size: 0.86rem;
    font-variant-numeric: tabular-nums;
    transition: background-color 0.18s ease, border-color 0.18s ease, color 0.18s ease;
}

.cg-archive-serveur .races-pagination-row { margin: var(--cg-space-7, var(--cg-space-7)) 0 0; }
.cg-archive-serveur .races-pagination-seo {
    display: flex; flex-wrap: wrap; gap: var(--cg-space-2, var(--cg-space-2)); align-items: center;
}
/* 🔄 CETTE SURCHARGE REPEIGNAIT LE COMPOSANT PARTAGÉ, elle est retirée.
   Elle redéclarait forme, taille (40 px), rayon (`xs` au lieu de `sm`) et surtout une
   page courante en BRUN plein là où le socle et le modèle servent l'ACCENT. Trois
   dessins pour une seule pagination, dont deux qui se croyaient d'accord.
   Le socle porte déjà tout : forme, bordure, encre, page courante en accent, et
   désormais la taille au jeton. Ne reste ici que le placement de la rangée. */
.cg-archive-serveur .races-pagination-seo .page-numbers.dots { border: 0; min-width: 0; }

/* ── Tiroir sous 1024px ──────────────────────────────────── */
/* 🔴 UNE COLONNE FIXE QUI SURVIT À LA DISPARITION DE SON OCCUPANT. Sous 1025 px la
   barre latérale passe en `display: none` — tiroir pour les annuaires, masquée pour le
   catalogue. Un élément de grille en `display: none` est RETIRÉ de la grille : le
   contenu principal devient alors le premier élément et atterrit dans la piste de
   224 px, pendant que celle de 674 reste vide. Mesuré sur /aliments-chien/ à 1024 :
   grille de 224 px pour des cartes de 255, une par rangée, sur une page de 1024.
   Les quatre annuaires y échappaient par accident — leur variante « avec carte » passe
   déjà en une colonne sous 1100 px. Le défaut n'était donc visible que sur la seule
   famille qui n'a pas de carte, et il attendait la prochaine.
   🔒 La règle est portée par la CAUSE (la barre n'est plus dans le flux), pas par le
   nom d'une archive : la prochaine famille sans carte l'hérite. */
@media (max-width: 1024px) {
    .cg-mission-layout {
        grid-template-columns: 1fr;
    }

    /* 🔄 A38 — LA BARRE MOBILE DEVIENT COLLANTE. Mesurée en défilant : elle était
       `static` et sortait de l'écran dès 1 500 px (top -14, puis -436). Le lecteur
       qui descend une liste de 24 cartes n'avait plus aucun moyen de rouvrir les
       filtres sans remonter en haut. Elle colle sous l'en-tête, à la MÊME couture
       que le panneau et la carte : une seule règle de décalage pour les trois. */
    /* 🔄 07/09 — /races/ REJOINT CETTE RÈGLE (A13/A14/A15/B20). Son enveloppe est
       `.races-wrapper` : elle ne porte pas `.cg-archive-serveur`, donc la règle
       partagée ne l'atteignait pas et la page avait écrit la sienne. Ajouter son
       enveloppe ici coûte un sélecteur ; laisser deux écritures d'un même bouton
       coûtait une barre brune collante qui recouvrait les cartes. */
    .cg-archive-serveur .filters-toggle-btn,
    .races-wrapper .filters-toggle-btn {
        display: block;
        position: sticky;
        top: var(--cg-colonne-collante-top);
        z-index: 2;
        width: 100%;
        margin: 0 0 var(--cg-space-5, var(--cg-space-5));
        padding: var(--cg-space-3) var(--cg-space-4);
        border: 1px solid var(--cg-primary, var(--cg-primary));
        border-radius: var(--cg-radius-sm, var(--cg-radius-sm));
        background: var(--cg-bg, var(--cg-bg));
        color: var(--cg-primary, var(--cg-primary));
        font-weight: 700;
        cursor: pointer;
    }

    /* Hors ouverture, la sidebar serveur est retirée du flux : c'est le bouton
       qui y donne accès. Les archives en mode client gardent leur sidebar
       empilée — elles n'ont pas ce modificateur. */
    .cg-facets-sidebar--url {
        position: fixed;
        inset: 0;
        /* 🔒 AU-DESSUS DE LEAFLET. `.leaflet-top`/`.leaflet-bottom` sont à 1000 :
           à valeur égale c'est l'ordre du DOM qui tranche, et la carte vient
           après la sidebar — les boutons +/− de zoom traversaient donc le voile
           et restaient cliquables par-dessus le tiroir ouvert. */
        z-index: 1200;
        background: rgba(0, 0, 0, .45);
        display: none;
    }
    .cg-facets-sidebar--url.open { display: block; }
    .cg-facets-sidebar--url > form {
        position: absolute; inset: 0 auto 0 0;
        width: min(86vw, 340px);
        overflow-y: auto;
        padding: var(--cg-space-5, var(--cg-space-5));
        background: var(--cg-bg, var(--cg-bg));
        box-shadow: var(--cg-shadow-lg, 0 16px 40px rgba(0,0,0,.1));
    }
    .cg-facets-sidebar--url .filters-wrapper { position: static; }

    /* `space-between` sur trois enfants colle le titre au lien de reset : entre
       eux il n'y a rien à répartir. /races/ porte déjà `gap: 12px` sur ce même
       en-tête — on s'aligne dessus. Scopé au mode SERVEUR : restos et croquettes
       n'ont que deux enfants et ne bougent pas. */
    .cg-facets-sidebar--url .filters-header { gap: var(--cg-space-3); }
    /* Même raison qu'au-dessus : le tiroir et sa fermeture sont le composant, le
       mode serveur est la requête. Les sept catalogues servent le même « × ». */
    /* ⚠️ NE PAS « NETTOYER » LE PADDING ICI. Il n'est pas écrit dans ce bloc : il
       vient du bouton par défaut du thème (12px 24px), et il porte la LARGEUR RÉELLE
       du contrôle. Mesuré le 08/09 à 390 sur /refuges/ : la boîte fait 48 × 40, pas
       40 × 40 — le `width: 40px` ne gagne pas contre un padding en boîte de contenu.
       Ajouter `padding: 0` par souci de propreté rétrécit les six de 8 px et déplace
       le glyphe de 17 à 13 px du bord. C'est exactement le genre de correction qui a
       l'air neutre et qui change six surfaces servies. */
    .cg-facets-sidebar .filters-close-btn,
    .races-sidebar .filters-close-btn {
        display: inline-flex; align-items: center; justify-content: center;
        width: 40px; height: 40px; margin-left: auto;
        border: 0; background: none; font-size: 1.6rem; line-height: 1;
        color: var(--cg-text-soft, var(--cg-text-soft)); cursor: pointer;
    }
    body.no-scroll { overflow: hidden; }
}

/* ══ APERÇU DE CARTE — rien ne part avant le clic ══════════════════════════════
   🔒 JUSTE ET SANS STYLE. La conformité est ici (aucune ressource distante avant
   le clic) ; l'apparence appartient au design. Aucune image, aucune police
   distante, aucun dégradé chargé : le fond est une couleur du thème. */
/* 🔴 TOKENS FANTÔMES — DÉCISION ATTENDUE DU DESIGN, NE PAS ALIGNER.
   Les six noms employés ci-dessous n'existent NULLE PART dans le socle :
   --cg-gris-clair, --cg-bordure, --cg-radius, --cg-texte-secondaire,
   --cg-sur-accent, --cg-radius-petit. Ce bloc a été écrit dans le vocabulaire
   d'un AUTRE projet, et il est servi au public sur les quatre archives de lieux
   (/refuges/, /clubs-canins/, /restaurants/, /espaces-promenade/) : chaque var()
   y tombe sur son repli, qui est donc la SEULE valeur jamais rendue.

   🔒 POURQUOI CE MARQUAGE EXISTE. Une passe d'alignement de replis — la mienne
   ou celle d'un autre — les verra « divergents » et voudra les corriger. Les
   corriger CASSERAIT le rendu, puisqu'il n'y a pas de token derrière. Et
   remplacer un fantôme par un token de la même teinte propagerait la palette
   d'un autre projet sous notre nom, en silence et pour toujours. Le rôle se lit
   dans la déclaration qui consomme, pas dans le nom : voir la table des six
   rôles remise au design.

   Propriétaire de la décision : DESIGN. Extinction : quand les six sont
   remplacés par des tokens du socle, ce commentaire part avec eux.
   ⚠ Contraste SERVI aujourd'hui, à égaler au minimum :
        texte du bloc  #4a4a44 sur #f2f2f0 = 7,96:1
        libellé bouton #2f2419 sur #EA7D1F = 5,37:1  ← relevé le 2026-08-26 ;
                       c'était #ffffff à 2,82:1, sous 3:1. Seul le REPLI a changé,
                       le nom fantôme et la décision du design sont intacts. */
.cg-map-apercu {
	display: flex;
	flex-direction: column;
	gap: .75rem;
	align-items: center;
	justify-content: center;
	min-height: 220px;
	height: 100%;
	padding: 1.5rem;
	text-align: center;
	/* 🔴 CINQ NOMS FANTÔMES, ET LEUR REPLI EST LA SEULE VALEUR JAMAIS RENDUE.
	   `--cg-gris-clair`, `--cg-bordure`, `--cg-radius`, `--cg-texte-secondaire` et
	   `--cg-radius-petit` ne sont déclarés NULLE PART — ni en CSS, ni en JS. Ils
	   viennent de la palette d'un autre projet : le nom a l'air d'une décision de
	   design, la valeur est un littéral en dur, et rien ne le signale.

	   🔒 LE NOM NE BOUGE PAS, LE REPLI PASSE AU SOCLE — c'est la décision déjà
	   rendue dans ce fichier le 2026-08-26 pour `--cg-sur-accent`. Remplacer le nom
	   propagerait la palette d'un autre projet ; brancher le repli fait servir la
	   nôtre. Mesuré sur /espaces-promenade/crh-loryhan/, avant → après :
	     fond     #f2f2f0 (gris froid)  →  #f2ece2  même clarté, teinte de la famille
	     bordure  #d9d9d4              →  #d6c9b6  contraste 1,26:1 → 1,39:1
	     rayon    8px                  →  10px     le socle n'a pas de 8 : xs=6, sm=10
	   Le rayon est le SEUL écart de valeur, +2 px, et il est dit ici plutôt que
	   dissimulé dans un jeton qui aurait l'air exact. */
	background: var(--cg-gris-clair, var(--cg-bg-soft));
	border: 1px solid var(--cg-bordure, var(--cg-border-strong));
	border-radius: var(--cg-radius, var(--cg-radius-sm));
}

.cg-map-apercu__texte {
	margin: 0;
	max-width: 46ch;
	font-size: .9375rem;
	line-height: 1.5;
	/* Repli au socle. Contraste mesuré sur le fond ci-dessus : 7,96:1 → 6,34:1,
	   au-dessus du plancher de 4,5:1 que garde-encres-fond-clair exige. */
	color: var(--cg-texte-secondaire, var(--cg-text-soft));
}

.cg-map-apercu__bouton {
	padding: .625rem 1.25rem;
	font: inherit;
	font-weight: 600;
	/* 🔴 LE NOM FANTÔME RESTE, LA VALEUR RENDUE CHANGE.
	   `--cg-sur-accent` n'est déclaré nulle part : son REPLI est la seule valeur
	   jamais rendue, et ce repli était #fff — donc 2,82:1 sur l'accent, ce que
	   le commentaire ci-dessus signale lui-même comme « DÉJÀ sous 3:1 ».
	   L'interdiction d'aligner porte sur le NOM (remplacer un fantôme par un
	   token de la même teinte propagerait la palette d'un autre projet). Ici le
	   nom ne bouge pas et la décision du design reste entière : c'est le repli
	   qui passe à l'encre du socle, appliquant l'écriture on-accent rendue le
	   2026-08-26. Servi : 5,37:1, au-dessus du minimum que le commentaire exige. */
	color: var(--cg-sur-accent, var(--cg-on-accent));
	background: var(--cg-accent, #EA7D1F);
	border: 1px solid transparent;
	border-radius: var(--cg-radius-petit, var(--cg-radius-xs));   /* 6px, valeur exacte */
	cursor: pointer;
}

/* 🔒 L'anneau de focus vient du jeton partagé — il n'est pas redéfini ici, sinon
   il divergerait de celui des 200 autres éléments focalisables. */

/* =========================================================
   CHIPS DE FILTRES ACTIFS — le PLACEMENT côté annuaires
   =========================================================
   L'apparence du composant `.race-chip` vit dans global.css, partagée avec
   /races/. Ce qui est propre à ces pages, c'est la position : ici les chips
   forment une rangée simple au-dessus du compteur, pas la ligne 2 d'une
   grille de barre d'outils comme sur /races/.
   🔒 `:empty` : sans filtre actif, le conteneur ne doit pas laisser une marge
   fantôme au-dessus du compteur. */
.cg-facets-chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--cg-space-2);
    align-items: center;
}

.cg-facets-chips:empty {
    display: none;
}

.cg-facets-chips:not(:empty) {
    margin-bottom: var(--cg-space-4);
}

/* =========================================================
   « AFFICHER PLUS » — la moitié confort de la pagination hybride
   =========================================================
   🔒 `[hidden]` DOUBLÉ D'UNE RÈGLE. Le bouton part caché du serveur parce qu'il ne
   ferait rien sans JavaScript. Une propriété `display` bat l'attribut `hidden` sans
   le retirer de l'arbre d'accessibilité : déjà mesuré sur le point de rappel du
   consentement, où le bouton restait visible en permanence alors que l'attribut
   annonçait « caché ». */
.load-more-container[hidden] {
    display: none;
}

.load-more-container {
    display: flex;
    justify-content: center;
    margin: var(--cg-space-6) 0 var(--cg-space-4);
}

/* ═══ « AFFICHER PLUS » : UN SEUL DESSIN, CELUI DU MODÈLE ════════════════════════
   🔴 IL Y EN AVAIT DEUX. Le modèle sert un bouton PLEIN, marron, encre blanche, sans
      bordure, 236 x 48 px en 15,2 px/700 ; les annuaires servaient un bouton CREUX,
      fond blanc, filet marron, 52 px de haut en 16 px/600. Deux réponses au même
      geste, sur deux feuilles, à trois écrans d'intervalle.
   🔒 CELUI QUI RESTE EST CELUI DU MODÈLE, valeurs comprises, et il vit ici : le porter
      depuis une feuille de page obligeait à le recopier par famille. La règle
      correspondante de page-races-liste.css est retirée. */
.btn-load-more {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 46px;
    padding: var(--cg-space-3) 28px;
    border: none;
    border-radius: var(--cg-radius-pill);
    background: var(--cg-primary);
    color: #fff;
    font-size: 0.95rem;
    font-weight: 700;
    cursor: pointer;
    transition: background-color 0.22s ease, transform 0.22s ease;
}

.btn-load-more:hover {
    background: var(--cg-primary-2);
    transform: translateY(-1px);
}

.btn-load-more:disabled { cursor: default; opacity: .6; }

.btn-load-more:focus-visible {
    outline: 2px solid var(--cg-primary);
    outline-offset: 2px;
}

/* 🔴 LE CREUX DU HAUT A ÉTÉ RETIRÉ DES CINQ ENVELOPPES. Depuis que la bande du héros
   sort des conteneurs et commence sous l'en-tête, ces 24 px la repoussaient vers le bas
   et laissaient une bande blanche entre l'en-tête et elle. Mesuré : le fil d'Ariane
   tombait à 44 px du header sur les annuaires contre 20 px sur les six autres familles,
   et la garde d'égalité des distances l'a dit tout de suite. Le creux du haut appartient
   maintenant au héros, qui le porte déjà. */

/* =========================================================
   LA GRILLE DES ANNUAIRES — la GRAMMAIRE du modèle, pas ses colonnes
   =========================================================
   🔴 J'AI ÉCRIT « CINQ RÈGLES IDENTIQUES » AVANT DE LES LIRE. Quatre l'étaient
   (auto-fill, minmax 260, gap --cg-space-5) ; les refuges, non : `auto-fit`,
   minmax 280, gap --cg-space-4. Trois écarts sur la même page, dont un qui change le
   COMPORTEMENT — `auto-fit` replie les pistes vides et étire les cartes sur toute la
   largeur quand il y en a peu, `auto-fill` les garde. Le markup pose déjà
   `.cg-facets-grid` sur les cinq : la classe de famille reste pour ce qui lui sera
   propre un jour, la grille est ici, et les cinq se comportent enfin pareil.

   🔒 ON ALIGNE LA GRAMMAIRE, PAS LE NOMBRE DE COLONNES. Le minimum devient celui du
   modèle, 255 px, et le gap 22. Le nombre de colonnes qui en résulte n'est PAS décidé :
   il est mesuré. Le §12 du brief interdit de forcer une colonne de plus au prix de la
   lisibilité, et une carte de lieu porte une adresse et une règle, donc elle est plus
   large qu'une carte de race. */
/* ═══ FIL DE NAVIGATION PAR CATÉGORIE — composant partagé ═══
   🔴 IL VIVAIT DANS archive-guides.css, QUE SEULES LES ARCHIVES ÉDITORIALES CHARGENT.
   L'archive de métier des pros sert le même composant depuis toujours, avec les mêmes
   classes, et le rendait en LIENS NUS : neuf intitulés soulignés à la file sous le champ
   de recherche, sans pastille et sans état actif visible. Rien ne pouvait le signaler —
   le balisage était juste, la règle était simplement ailleurs.
   🔒 Même grammaire que le tri et les raccourcis : pastille, bordure, accent sur l'état
   courant, et un anneau de focus qui ne repose pas que sur la couleur. */
/* 🔄 Déplacé dans global.css : une seule source pour un composant que cinq
   gabarits servent. Voir la note là-bas. */

.cg-chip:hover {
    background: #fffaf5;
    border-color: rgba(234, 125, 31, 0.35);
    color: var(--cg-accent);
}



.cg-chip:focus-visible {
    outline: 2px solid var(--cg-accent);
    outline-offset: 2px;
}

/* Le fil lui-même : il vit dans les outils de la bande d'en-tête, il se met donc en
   ligne et revient à la ligne plutôt que de déborder. */
.cg-entete__outils .professionnels-type-nav {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--cg-space-2);
    margin-top: var(--cg-space-4);
}

.cg-facets-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(255px, 1fr));
    gap: 22px;
}

/* 🔴 LE PALIER MOBILE FAIT PARTIE DE LA GRAMMAIRE, et je l'avais oublié. Sans lui, le
   minimum de 255 px donne UNE colonne à 390, quand le modèle en sert DEUX. J'allais
   justifier l'écart par « la carte de lieu porte une adresse, elle a besoin de largeur »
   — mesuré en forçant deux colonnes dans le navigateur : carte à 176 px, ZÉRO élément
   plus large que sa carte, ZÉRO texte tronqué, sur douze cartes. La justification était
   fausse, et elle aurait tenu lieu de mesure. */
@media (max-width: 768px) {
    .cg-facets-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 14px;
    }
}

@media (max-width: 480px) {
    .cg-facets-grid {
        gap: var(--cg-space-3);
    }
}

/* ═══ CHAMP DE RECHERCHE — composant partagé par les six catalogues ═══
   🔴 Il vivait dans page-races-liste.css, que seul /races/ charge. En donnant le
      champ aux cinq annuaires, il serait resté NU chez eux : input sans bordure ni
      fond, loupe sans couleur. Les valeurs déplacées sont celles du modèle.
   🔒 ÉCART DÉCLARÉ : chez les annuaires, .hero-search est un <form> et la loupe un
      <button> de soumission — le champ doit marcher sans script. Les deux règles
      ci-dessous neutralisent l'apparence de bouton ; rien d'autre ne change. */
/* PILOTE ⑥ · `.hero-search` EST UNE COMPOSITION SUR LE PATRON 26, pas un
   composant à part : le kit l'a tranché, et le piège des deux autres lectures y
   est écrit — une « variante » aurait dupliqué le style de saisie, un « septième
   composant » aurait imposé un appareil de libellé à une boîte de recherche qui
   n'en veut pas.
   Ici le CONTENANT porte les métriques du contrôle et l'input est nu : les deux
   valeurs non ambiguës du patron 26 y sont donc portées — bordure
   --cg-border-strong au lieu de --cg-border, et rayon `sm` (10 px) au lieu de
   `md` (14 px), qui est le rayon des CARTES et faisait ressembler ce contrôle à
   une carte.
   🔴 LA HAUTEUR N'EST PAS PORTÉE, et c'est le kit qui l'en empêche : il déclare
   le patron 26 en « flottement non instancié — hauteurs 46 / 47 / 80,
   remplissages 0/16 et 12/16, valeur à mesurer ». Unifier maintenant figerait une
   valeur que personne n'a arrêtée. Mesuré : 66 px de contenant (12+40+12+2)
   contre 46 px au socle. À l'inventaire. */
.hero-search {
    display: flex;
    align-items: center;
    gap: 10px;
    max-width: 480px;
    margin: 22px auto 0;
    padding: var(--cg-space-3) var(--cg-space-4);
    background: var(--cg-bg);
    border: 1px solid var(--cg-border-strong);
    border-radius: var(--cg-radius-sm);
    box-shadow: var(--cg-shadow-sm);
}

.hero-search input {
    width: 100%;
    border: none;
    background: transparent;
    outline: none;
    font-size: 0.96rem;
    color: var(--cg-text);
}

.hero-search input::placeholder {
    color: #9ca3af;
}

.search-icon {
    /* Le glyphe emoji cede au dessin `recherche` de la famille. L'encre passe
       du gris #9ca3af, hors socle, au jeton d'encre secondaire : le SVG suit
       currentColor, donc la couleur se decide ici et une seule fois. */
    display: inline-flex;
    align-items: center;
    color: var(--cg-text-muted);
    flex: 0 0 auto;
}
.hero-search button.search-icon {
    border: none;
    background: none;
    padding: 0;
    cursor: pointer;
}
.hero-search button.search-icon:focus-visible {
    outline: 2px solid var(--cg-info);
    outline-offset: 2px;
}

/* ── La note sur une carte — un seul élément, toutes les familles ──────────
   🔒 Elle n'existe dans le DOM que si la fiche a des avis approuvés : il n'y a donc
   pas d'état « vide » à styler, et pas de place réservée qui ferait un trou sur les
   cartes non notées. */
.cg-card-note {
    display: inline-flex;
    align-items: baseline;
    gap: var(--cg-space-1);
    font-size: var(--cg-text-sm);
    line-height: 1.2;
    color: var(--cg-text);
}
.cg-card-note__etoile { color: var(--cg-accent-ink); }
.cg-card-note__val    { font-weight: 700; }
.cg-card-note__compte { color: var(--cg-text-muted); }


/* ══ 🔶 LE BADGE COUVRE LE ROND D'INITIALE — CONSTAT, PAS CORRECTIF ══════════
   Mesuré sur les cartes de club et de restaurant : le badge est posé en absolu à
   10 px du coin, la vignette est un DISQUE centré de rayon 32 ; la distance du
   badge au centre vaut 18,1 px. Il mord dedans, aux deux largeurs.
   🔒 Et le chevauchement d'un disque ne se lit PAS sur sa boîte — les coins d'un
   cercle sont transparents ; c'est la distance au centre qui tranche.

   🔴 J'AI CORRIGÉ, ET LA GARDE DU GABARIT A REFUSÉ — À RAISON. Passer la média en
   deux rangées (badge sous la vignette) supprimait bien le chevauchement, mais
   cassait TROIS propriétés que le patron de catalogue tient : le ratio du bandeau
   (0,472 contre celui du patron), le coin du badge (89 contre 92) et le centrage
   du jeton. Un correctif qui échange un défaut contre trois régressions n'est pas
   un correctif.

   🔴 LES CONTRAINTES SONT CONJOINTEMENT INSATISFIABLES, ET C'EST LE VRAI CONSTAT.
   Le patron exige à la fois : badge au coin, jeton centré, ratio de bandeau fixe.
   Avec un badge de 127 px de large et un disque de 64 px centré dans 305x144, les
   trois ne peuvent pas tenir ensemble — le chevauchement est produit par le patron
   lui-même, pas par une erreur d'application. Les voisines n'ont pas le problème
   parce qu'elles servent une IMAGE, pas un disque : il n'y a rien à couvrir.
   Sortir de là demande de céder l'une des trois (badge hors média, badge plus
   court, jeton décentré ou bandeau plus haut pour ces deux types) — c'est un
   arbitrage de design, pas un geste de lot. ROUTÉ.
   L'assertion de garde-hub-commune reste en place et rougira tant que ce n'est
   pas tranché : un défaut mesuré ne se range pas parce qu'on ne sait pas encore
   quoi en faire. */

/* ══════════════════════════════════════════════════════════════════════════
   A35 — LA PASTILLE D'INITIALE PAR-DESSUS L'ILLUSTRATION DE TYPE
   ══════════════════════════════════════════════════════════════════════════
   🔒 ELLE NE SE POSE QUE SUR UNE ILLUSTRATION, jamais sur un visuel propre :
   quand l'image montre CET établissement, elle l'identifie déjà, et une lettre
   par-dessus ne ferait que masquer une partie de ce qu'on est venu voir.

   🔒 EN HAUT À GAUCHE, PARCE QUE LES 22 IMAGES Y ONT ÉTÉ COMPOSÉES POUR. Le
   document de génération l'exige de chaque prompt (« zone supérieure gauche
   dégagée ») : la place n'est pas un choix de mise en page, c'est une
   contrainte que les images portent déjà.

   🔒 L'ANNEAU N'EST PAS UNE DÉCORATION. Les 22 illustrations partagent une
   palette beige/orange, et le disque de la pastille est dans cette même famille :
   sans séparation, deux tons proches se touchent et la lettre perd son bord. */
.cg-card-avatar--sur-illustration {
    position: absolute;
    top: var(--cg-space-3);
    left: var(--cg-space-3);
    box-shadow: 0 0 0 3px var(--cg-bg), var(--cg-shadow-sm);
}

/* 🔒 Le média redevient un conteneur de position pour la pastille — il l'était
   déjà (`position: relative` plus haut) ; on ne le redéclare pas, on s'appuie
   dessus. L'image, elle, ne doit pas déborder du coin arrondi de la carte. */
.cg-card-media .cg-card-photo--illustration {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* ══════════════════════════════════════════════════════════════════════════
   P7 — LA SIGNATURE D'UNE ACTUALITÉ SUIT L'AXE DE SON TITRE
   ══════════════════════════════════════════════════════════════════════════
   🔴 ET « CENTRÉE » SE MESURE SUR L'ENCRE, PAS SUR LA BOÎTE. Premier relevé :
   centre de la signature 752, centre du titre 752 — apparemment aligné. Mais la
   signature est un conteneur `flex` pleine largeur (152..1352) dont les segments
   se tassent à gauche (152..474) : c'est la BOÎTE qui était centrée, et l'œil,
   lui, reçoit une ligne collée au bord gauche sous un titre centré. Un centre de
   rectangle ne dit rien de ce qu'on voit quand le rectangle est plus large que
   son contenu.

   🔒 SCOPÉ À L'EN-TÊTE D'ACTUALITÉ. Les signatures de catalogue sont alignées à
   gauche par décision — centrer là serait changer une chose qu'on ne regarde pas. */
.single-news-header .single-news-meta {
    justify-content: center;
}

/* ══════════════════════════════════════════════════════════════════════════
   P12 — LE BADGE DU TYPE SUR L'IMAGE (décision Jordan, 01/09)
   ══════════════════════════════════════════════════════════════════════════
   🔒 LA RÈGLE, ÉNONCÉE UNE FOIS POUR TOUTES LES FAMILLES : toute carte dont
   l'image est une ILLUSTRATION DE TYPE porte le badge de ce type sur l'image.
   Elle vient de la carte d'aliment, qui la servait déjà — une nature morte de
   croquettes ne se distingue pas d'une nature morte de friandises. Une forêt
   générique et un parc générique ont exactement le même problème, et c'est
   pourquoi la règle est générale et non un cas d'espèce.

   🔒 EN HAUT À GAUCHE, parce que les 22 illustrations y ont été composées
   dégagées — le document de génération l'exige de chaque prompt. La place n'est
   pas un choix de mise en page, c'est une contrainte que les images portent.

   🔒 L'ANNEAU N'EST PAS UNE DÉCORATION : les illustrations partagent la palette
   beige/orange du badge, et deux tons proches qui se touchent effacent son bord.

   🔒 ET RIEN NE RECOUVRE UNE VRAIE PHOTO. La condition est posée dans le PHP,
   sur la NATURE du visuel : sur la photo d'un lieu, le badge cacherait ce qu'on
   est venu voir et n'apprendrait rien que la fiche ne dise déjà. */
.cg-card-badge-type,
.cg-card-media .cg-alim-icon {
    position: absolute;
    top: var(--cg-space-3);
    left: var(--cg-space-3);
    z-index: 1;
    padding: var(--cg-space-1) var(--cg-space-3);
    border-radius: var(--cg-radius-pill);
    background: var(--cg-bg);
    border: 1px solid var(--cg-border);
    box-shadow: 0 0 0 3px var(--cg-bg), var(--cg-shadow-sm);
    font-size: var(--cg-text-xs);
    font-weight: 700;
    letter-spacing: .03em;
    text-transform: uppercase;
    color: var(--cg-text-soft);
    white-space: nowrap;
}
