/* ════════════════════════════════════════════════════════════════════
   MY SERENYPET — SOCLE VISUEL

   Direction : rassurant sans être froid, sérieux sans être premium.
   Le produit n'est pas un bijou, c'est une assurance.

   Règle de couleur : le doré ne sert QU'AUX ACTIONS et aux points
   d'attention. Utilisé partout, il ne veut plus rien dire ; utilisé
   rarement, il devient un repère.

   Le rouge et le vert sont réservés aux états « perdu » et « retrouvé ».
   Ce sont les deux seuls moments où la couleur porte une information
   vitale, elle ne doit jamais servir de décoration.
   ════════════════════════════════════════════════════════════════════ */

:root {
    /* ── Fonds ─────────────────────────────────────────────────────── */
    --fond:            #0b0c0f;  /* jamais du noir pur : trop dur à l'œil */
    --fond-carte:      #111318;
    --fond-eleve:      #191c22;  /* survol, éléments actifs */
    --bordure:         #23262e;

    /* ── Texte ─────────────────────────────────────────────────────── */
    --texte:           #e8e6e3;  /* blanc cassé, moins agressif que #fff */
    --texte-doux:      #9ba0ab;
    --texte-faible:    #6b7280;

    /* ── Accent, avec parcimonie ───────────────────────────────────── */
    --or:              #d9a441;
    --or-clair:        #eab84f;
    --or-sombre:       #a67c00;

    /*
     * Le doré en composantes, pour teinter anneaux et ombres sans
     * multiplier les variables de couleur.
     */
    --or-rvb:          217, 164, 65;

    /* ── États, jamais décoratifs ──────────────────────────────────── */
    --perdu:           #e5484d;

    /* La même couleur en composantes, pour les fonds translucides : une
       bordure d'alerte à pleine opacité crierait plus fort que le
       chiffre qu'elle entoure. */
    --perdu-rvb:       229, 72, 77;
    --retrouve:        #30a46c;
    --attention:       #f5a524;
    --info:            #4a90d9;

    /* ── Rythme ────────────────────────────────────────────────────── */
    --r1: 4px;  --r2: 8px;  --r3: 12px; --r4: 16px;
    --r5: 24px; --r6: 32px; --r7: 48px; --r8: 64px;

    /*
     * ARRONDI D'ATELIER.
     *
     * La direction retenue veut des angles francs, ceux d'une console
     * d'établi. 8 px adoucit assez pour que le bouton se prenne en main
     * sans devenir une pastille : c'est le compromis entre le trait net
     * de la direction et l'arrondi demandé.
     *
     * Pour trancher d'un côté ou de l'autre, une seule valeur à changer :
     * 3 px pour l'atelier strict, 14 px pour un bouton nettement rond.
     */
    --arrondi:       8px;
    --arrondi-large: 12px;

    /* Ombre unique : plusieurs niveaux d'ombre alourdissent sans
       apporter d'information. */
    --ombre: 0 2px 12px rgba(0, 0, 0, .35);

    /*
     * ── LE GESTE DE SURVOL ─────────────────────────────────────────
     *
     * Un anneau doré fin et une ombre de la même teinte.
     *
     * L'OMBRE EST DORÉE, PAS NOIRE. Une ombre noire sur fond noir ne se
     * voit pas : elle assombrit un fond déjà sombre. Teintée du doré de
     * l'anneau, elle donne l'impression que le bouton est éclairé par sa
     * propre bordure, ce qui est le seul moyen de créer du relief ici.
     *
     * L'ANNEAU EST UNE OMBRE, PAS UNE BORDURE. Une bordure occupe de la
     * place : la faire apparaître au survol décalerait le texte d'un
     * pixel et tout tremblerait. Un box-shadow de rayon nul se pose
     * par-dessus sans rien pousser.
     */
    --anneau:         0 0 0 1px rgba(var(--or-rvb), .45);
    --anneau-large:   0 0 0 2px rgba(var(--or-rvb), .35);
    --halo:           0 4px 16px rgba(var(--or-rvb), .16);
    --sortie:         cubic-bezier(.22, 1, .36, 1);

    /* ── Typographie ───────────────────────────────────────────────── */
    --police-titre: 'Bricolage Grotesque', 'Segoe UI', system-ui, sans-serif;
    --police-texte: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --police-mono:  ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
}

/* ════════════════════════════════════════════════════════════════════
   POLICES

   Servies depuis NOTRE serveur, jamais depuis le CDN de Google.
   Charger une police depuis fonts.googleapis.com transmet l'adresse IP
   de chaque visiteur à une société américaine sans son consentement.
   Un tribunal allemand a condamné cette pratique en 2022, et la CNIL
   suit la même direction : pour une boutique française, ce n'est pas un
   détail juridique lointain.

   Si les fichiers .woff2 sont absents, le navigateur passe simplement à
   la police suivante de la pile. Le site reste lisible, il perd
   seulement son caractère. Voir public/assets/fonts/LISEZMOI.md.
   ════════════════════════════════════════════════════════════════════ */

/*
 * ── LES FICHIERS DE POLICE NE SONT PAS DANS L'ARCHIVE ────────────────
 *
 * Ils pèsent trop pour être livrés à chaque version, et ils ne changent
 * jamais. Voir public/assets/fonts/LISEZMOI.md pour les déposer une
 * fois.
 *
 * Tant qu'ils manquent, le navigateur signale un échec de
 * téléchargement dans sa console et retombe sur la police suivante de
 * la liste. La page reste lisible, mais ce n'est pas la typographie
 * prévue.
 *
 * La liste de repli est ordonnée : une police système approchante
 * d'abord, la générique en dernier. « sans-serif » seul donnerait du
 * Times sur certains appareils.
 */
/*
 * ── LES @font-face SONT SERVIES PAR /assets/polices-site.css ─────────
 *
 * Elles étaient déclarées ici, en dur, avec « inter.woff2 ». Déposer un
 * TTF obligeait donc à éditer cette feuille, ce que l'écran
 * d'administration ne pouvait pas faire.
 *
 * La route regarde ce qui est réellement sur le disque et déclare le
 * format trouvé. Un WOFF2 ou un TTF conviennent également : le WOFF2 est
 * deux fois plus léger, mais mieux vaut un TTF en place qu'un WOFF2
 * qu'on ne trouve pas.
 */

/* ════════════════════════════════════════════════════════════════════
   RÉINITIALISATION
   ════════════════════════════════════════════════════════════════════ */

*, *::before, *::after { box-sizing: border-box; }

body, h1, h2, h3, h4, p, figure, blockquote, dl, dd { margin: 0; }

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }

    /*
     * Le geste de survol se tait, mais l'anneau reste : c'est lui qui
     * dit où l'on est. Seul le mouvement disparaît, pas l'information.
     */
    .bouton,
    .champ input,
    .champ textarea,
    .champ select { transition: none; }

    .bouton:hover { transform: none; }
    *, *::before, *::after {
        animation-duration: .01ms !important;
        transition-duration: .01ms !important;
    }
}

/* ─────────────────────────────────────────────────────────────────────
   LA BARRE DE DÉFILEMENT

   ── DEUX ÉCRITURES POUR TROIS NAVIGATEURS ─────────────────────────────

   « scrollbar-color » est la propriété normalisée : Firefox la comprend
   depuis longtemps, Safari depuis peu. Chrome, lui, ne suit que les
   pseudo-éléments « ::-webkit-scrollbar ».

   Les deux sont donc écrites. Un navigateur qui comprend les deux
   applique la normalisée, ce qui est le bon ordre : c'est elle qui
   survivra.

   ── UN OR SOMBRE, PAS L'OR DES BOUTONS ────────────────────────────────

   La barre accompagne la lecture, elle ne la dispute pas. En doré plein,
   elle attirerait l'œil plus qu'un bouton d'achat, et sur une page
   longue elle deviendrait le premier élément vu.

   Elle s'éclaircit au survol : à ce moment-là, on la cherche.

   ── LES BARRES LOCALES GARDENT LEUR STYLE ─────────────────────────────

   La galerie de l'accueil masque la sienne, le menu d'administration a
   la sienne, plus fine. Ces règles sont plus spécifiques et passent
   après : elles continuent de s'appliquer. (1.104.0)
   ───────────────────────────────────────────────────────────────────── */

html {
    scrollbar-color: var(--or-sombre) var(--fond-eleve);
    scrollbar-width: auto;
}

::-webkit-scrollbar {
    width: 12px;
    height: 12px;
}

::-webkit-scrollbar-track {
    background: var(--fond-eleve);
}

::-webkit-scrollbar-thumb {
    background: var(--or-sombre);
    border-radius: 6px;

    /* Une bordure de la couleur du rail détache le curseur du bord :
       collé, il paraît plus large qu'il n'est et touche le contenu. */
    border: 3px solid var(--fond-eleve);
    background-clip: padding-box;
}

::-webkit-scrollbar-thumb:hover {
    background: var(--or);
    background-clip: padding-box;
}

/* L'angle entre deux barres, visible sur un tableau qui défile dans les
   deux sens. Laissé au blanc par défaut, il ferait une tache. */
::-webkit-scrollbar-corner {
    background: var(--fond-eleve);
}

body {
    min-height: 100vh;
    background: var(--fond);
    color: var(--texte);
    font-family: var(--police-texte);

    /* 16px minimum : en dessous, les navigateurs mobiles zooment
       automatiquement à la mise au point d'un champ. */
    font-size: 16px;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;

    /* ── UN FOND QUI DÉBORDE NE DOIT PAS FAIRE DÉFILER ────────────────

       Les fonds pleine largeur se calent sur « 100vw », qui inclut la
       barre de défilement verticale : quelques pixels dépassent, et une
       barre horizontale apparaît sur toute la page.

       « clip » coupe sans créer de zone défilante, contrairement à
       « hidden » : un en-tête collant reste collant. (1.103.1) */
    overflow-x: clip;
}

img, picture, svg { max-width: 100%; display: block; }

input, button, textarea, select { font: inherit; color: inherit; }

/* ════════════════════════════════════════════════════════════════════
   TITRES
   ════════════════════════════════════════════════════════════════════ */

h1, h2, h3, h4 {
    font-family: var(--police-titre);
    font-weight: 600;
    line-height: 1.2;
    letter-spacing: -.015em;
}

h1 { font-size: clamp(1.5rem, 4vw, 2rem); }
h2 { font-size: clamp(1.25rem, 3vw, 1.5rem); }
h3 { font-size: 1.125rem; }

a { color: var(--or); text-decoration: none; }
a:hover { color: var(--or-clair); text-decoration: underline; }

/*
 * UN BOUTON N'EST PAS UN LIEN, MÊME QUAND C'EST UNE BALISE <a>.
 *
 * « a:hover » a une spécificité de (0,1,1), supérieure à celle de
 * « .bouton--principal » qui n'est que (0,1,0). La couleur du lien
 * l'emportait donc au survol : le texte du bouton principal virait au
 * doré clair, sur un fond doré. Il disparaissait purement et
 * simplement.
 *
 * Douze boutons du site sont écrits en <a> : « SVG pour la gravure »,
 * « Retour à la liste », « Exporter le stock ». Tous étaient touchés,
 * et le défaut ne se voyait qu'en promenant la souris dessus.
 *
 * La correction ne neutralise QUE le soulignement, et laisse la couleur
 * à chaque variante.
 *
 * Une première version posait « color: inherit » ici : sa spécificité
 * de (0,2,1) écrasait alors aussi « .bouton--danger:hover » et
 * « .bouton--discret:hover », qui ne valent que (0,2,0). Le remède
 * cassait les variantes qu'il devait protéger.
 *
 * Chaque variante affirme donc sa propre couleur, y compris au survol,
 * avec un sélecteur qui inclut « a » pour battre « a:hover ».
 */
a.bouton,
a.bouton:hover,
a.bouton:focus,
a.bouton:active {
    text-decoration: none;
}

/*
 * Les variantes portent LES DEUX classes dans leur sélecteur.
 *
 * « a.bouton:hover » et « a.bouton--principal:hover » ont exactement la
 * même spécificité : c'est alors l'ordre d'écriture qui tranche, et un
 * déplacement de bloc suffirait à refaire disparaître le texte des mois
 * plus tard, sans que personne fasse le lien.
 *
 * En nommant les deux classes, chaque variante devient strictement plus
 * forte : l'ordre ne compte plus.
 */
a.bouton:hover                        { color: var(--texte); }
a.bouton.bouton--principal:hover      { color: #14100a; }
a.bouton.bouton--discret:hover        { color: var(--or); }
a.bouton.bouton--danger:hover         { color: #f4a9ac; }

/* Repère de mise au point visible : indispensable pour naviguer au
   clavier, et souvent supprimé par erreur. */
:focus-visible {
    outline: 2px solid var(--or);
    outline-offset: 2px;
    border-radius: var(--r1);
}

/* ════════════════════════════════════════════════════════════════════
   MISE EN PAGE
   ════════════════════════════════════════════════════════════════════ */

.conteneur {
    width: 100%;
    max-width: 1280px;
    margin: 0 auto;
    padding: 0 var(--r4);
}

.carte {
    background: var(--fond-carte);
    border: 1px solid var(--bordure);
    border-radius: var(--arrondi-large);
    padding: var(--r5);

    /*
     * DEUX CARTES QUI SE SUIVENT NE DOIVENT PAS SE TOUCHER.
     *
     * Il n'y avait aucune marge : les écrans qui empilent plusieurs
     * cartes donnaient un bloc continu où l'on ne voyait plus où finit
     * une section et où commence la suivante. Les bordures se collaient
     * et formaient un trait double.
     *
     * La marge est portée par la carte plutôt que par un conteneur :
     * elle vaut alors partout, y compris dans les écrans qui n'ont pas
     * pensé à en prévoir un.
     */
    margin-bottom: var(--r5);
}

/* La dernière ne pousse pas le bas de page. */
.carte:last-child { margin-bottom: 0; }

/*
 * Le titre d'une carte se détache de son contenu.
 *
 * Il était collé au premier paragraphe : le titre et le texte d'aide se
 * lisaient comme une seule phrase coupée en deux.
 */
.carte > h2 {
    margin: 0 0 var(--r3);
    font-family: var(--police-titre);
    font-size: 1.125rem;
}

/*
 * Le texte d'aide qui suit immédiatement un titre garde sa respiration.
 *
 * Chaque écran répétait « style="margin-top:0" » sur ce paragraphe. Une
 * règle vaut mieux qu'un attribut recopié : le jour où l'espacement
 * change, il n'y a qu'un endroit à modifier.
 */
.carte > h2 + .champ__aide { margin-top: 0; }

/* Le premier champ après une aide reprend l'espacement normal. */
.carte > .champ__aide + form,
.carte > .champ__aide + .champ { margin-top: var(--r4); }

/* ════════════════════════════════════════════════════════════════════
   BOUTONS

   Hauteur minimale de 44px : en dessous, une cible tactile devient
   difficile à atteindre sur mobile.
   ════════════════════════════════════════════════════════════════════ */

.bouton {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--r2);
    min-height: 44px;
    padding: 0 var(--r5);
    border: 1px solid transparent;
    border-radius: var(--arrondi);
    background: var(--fond-eleve);
    border-color: var(--bordure);

    /* Chaque variante réaffirme sa couleur au survol : c'est ce qui
       rend le bouton insensible à ce qui l'entoure. */
    color: var(--texte);
    font-weight: 600;
    letter-spacing: .005em;
    cursor: pointer;

    /* Une seule transition sur les trois propriétés du geste. Les
       séparer les désynchronise, et le mouvement paraît mou. */
    transition: box-shadow .18s var(--sortie),
                transform .18s var(--sortie),
                border-color .18s var(--sortie);
}

.bouton:hover {
    text-decoration: none;
    color: var(--texte);
    border-color: rgba(var(--or-rvb), .5);
    box-shadow: var(--anneau), var(--halo);
    transform: translateY(-1px);
}

/* Enfoncé : le bouton revient à plat, l'anneau reste. La couleur ne
   bouge pas, sans quoi le texte clignote à chaque clic. */
.bouton:active {
    transform: translateY(0);
    box-shadow: var(--anneau);
}

/*
 * Le focus clavier est DÉTACHÉ du bouton par un liseré sombre.
 *
 * On navigue au clavier sans savoir où est la souris : il ne peut pas
 * ressembler au survol, qui lui colle au bouton.
 */
.bouton:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--fond), 0 0 0 4px var(--or);
}

/*
 * Désactivé : plus rien ne réagit.
 *
 * Une simple opacité laissait le bouton se soulever et s'entourer de
 * doré au survol alors qu'il ne fait rien, ce qui laisse croire à une
 * panne. On éteint le geste, pas seulement la couleur.
 */
.bouton:disabled,
.bouton[disabled] {
    background: #15171e;
    border-color: #1e222b;
    color: #5f6570;
    cursor: not-allowed;
    transform: none;
    box-shadow: none;
}

/* L'action principale d'un écran, une seule à la fois. C'est le seul
   aplat doré de tout l'écran. */
.bouton--principal {
    background: var(--or);
    border-color: var(--or);
    color: #14100a;
    font-weight: 700;
}

.bouton--principal:hover {
    background: var(--or-clair);
    border-color: var(--or-clair);
    color: #14100a;
    box-shadow: var(--anneau-large), 0 4px 18px rgba(var(--or-rvb), .3);
}

.bouton--principal:disabled {
    background: #4d3d0c;
    border-color: #4d3d0c;
    color: #b8ae95;
}

.bouton--discret {
    background: transparent;
    border-color: var(--bordure);
    color: var(--texte-doux);
}
.bouton--discret:hover { color: var(--or); }

/* Réservé aux actions irréversibles. */
/* ── UNE SEULE DÉFINITION DU BOUTON DE DANGER ──────────────────────
   Une seconde existait plus bas, avec un rouge plus vif : c'est elle qui
   s'appliquait, et corriger celle-ci ne faisait rien. Les deux valeurs
   sont fusionnées ici. (1.36.2) */
.bouton--danger {
    background: transparent;
    border-color: rgba(240, 90, 90, .4);
    color: #f05a5a;
}

/*
 * Le rouge S'ÉCLAIRCIT au survol.
 *
 * Le foncer est le réflexe qu'on prend sur fond clair : sur fond noir,
 * il fait disparaître le texte au moment précis où l'on s'apprête à
 * cliquer sur une action irréversible.
 */
.bouton--danger:hover {
    border-color: #a8434a;
    color: #f4a9ac;
    box-shadow: 0 0 0 1px rgba(229, 72, 77, .3),
                0 4px 16px rgba(229, 72, 77, .14);
}

/* ════════════════════════════════════════════════════════════════════
   FORMULAIRES
   ════════════════════════════════════════════════════════════════════ */

.champ { margin-bottom: var(--r4); }

.champ label {
    display: block;
    margin-bottom: var(--r2);
    font-size: .875rem;
    font-weight: 500;
    color: var(--texte-doux);
}

/*
 * ── UNE ÉTIQUETTE QUI EST UN BOUTON RESTE UN BOUTON ───────────────────
 *
 * « .champ label » impose display:block et la couleur douce. Une
 * étiquette portant « .bouton » — le procédé qui masque un champ de
 * fichier derrière un bouton lisible — héritait des deux : elle
 * s'étirait sur toute la largeur, et son texte devenait gris sur fond
 * doré.
 *
 * La règle vient APRÈS et vise les deux classes : elle ne peut pas être
 * placée plus haut, l'ordre décide en CSS à spécificité égale.
 */
.champ label.bouton {
    display: inline-flex;
    width: auto;
    color: inherit;
    font-size: .9375rem;
}

/* Le bouton principal garde son texte sombre sur fond doré, quelle que
   soit la couleur héritée du champ. */
.champ label.bouton--principal { color: #0a0f0d; }

.champ label.bouton--discret { color: var(--texte); }

/*
 * ── LE STYLE VISE LES CHAMPS, PAS SEULEMENT CEUX DANS .champ ─────────
 *
 * Les règles ne s'appliquaient qu'à « .champ input ». Un champ posé
 * ailleurs — dans une barre d'actions, dans une ligne de tableau —
 * recevait le style du navigateur : fond blanc, et une couleur de texte
 * héritée du thème sombre.
 *
 * Blanc sur blanc. On tapait sans rien voir, et le défaut ne se
 * remarquait qu'en sélectionnant le texte.
 *
 * La couleur est désormais DÉCLARÉE, pas héritée : c'est ce qui manquait
 * vraiment. Un champ qui pose son fond doit poser sa couleur de texte,
 * sinon il dépend de ce qui l'entoure.
 */
input[type="text"],
input[type="email"],
input[type="password"],
input[type="search"],
input[type="tel"],
input[type="url"],
input[type="number"],
input[type="date"],
textarea,
select {
    min-height: 44px;
    padding: var(--r3) var(--r4);
    background: var(--fond);
    color: var(--texte);
    border: 1px solid var(--bordure);
    border-radius: var(--arrondi);
    font: inherit;
    font-size: .9375rem;
    transition: box-shadow .18s var(--sortie), border-color .18s var(--sortie);
}

/* Dans un bloc .champ, le champ prend toute la largeur. Ailleurs, il
   garde la sienne : un champ de renommage dans une ligne de tableau ne
   doit pas pousser les boutons hors de l'écran.

   ── SAUF LES CASES ET LES BOUTONS RADIO ────────────────────────────
   Une case étirée sur toute la largeur devient un rectangle blanc de
   huit cents pixels : elle ne ressemble plus à une case, et l'on ne
   sait plus où cliquer. (1.35.1) */
.champ input:not([type="checkbox"]):not([type="radio"]),
.champ textarea,
.champ select { width: 100%; }

/* ── LES LIGNES DE TEXTE NE S'ÉTIRENT PAS SANS FIN ─────────────────
   Au-delà d'une soixantaine de caractères, l'œil perd la ligne en
   revenant à la gauche. Sur une carte de huit cents pixels, un champ
   pleine largeur en affiche cent-vingt.

   Les zones de texte long gardent la pleine largeur : on y écrit des
   paragraphes, et c'est le retour à la ligne automatique qui règle la
   longueur. */
.champ input[type="text"],
.champ input[type="email"],
.champ input[type="url"],
.champ input[type="tel"],
.champ input[type="search"] { max-width: 34rem; }

/* Les champs courts n'ont pas besoin de plus : un prix, un poids, une
   quantité tiennent en six caractères. */
.champ input[type="number"],
.champ input[type="date"] { max-width: 14rem; }

/* Dans une grille de colonnes, la largeur est déjà contrainte par la
   colonne : le plafond ferait un champ plus étroit que sa case. */
.rang .champ input,

input:hover,
textarea:hover,
select:hover { border-color: #333846; }

/* Le texte d'invite doit se distinguer d'une valeur saisie, sans
   devenir illisible pour autant. */
input::placeholder,
textarea::placeholder {
    /*
     * 4,58 de contraste. La couleur « faible » du thème n en donnait que
     * 3,4 : un texte d invite reste du texte, et il doit se lire.
     *
     * « opacity: 1 » est nécessaire : Firefox applique une transparence
     * par défaut qui annulerait le calcul.
     */
    color: #868b96;
    opacity: 1;
}

/*
 * Le champ reçoit le même anneau que le bouton au survol.
 *
 * C'est ce qui fait qu'un formulaire et une barre d'actions ont l'air
 * d'appartenir au même objet, plutôt que d'avoir été dessinés
 * séparément.
 */
.champ input:focus,
.champ textarea:focus,
.champ select:focus {
    outline: none;
    border-color: rgba(var(--or-rvb), .55);
    box-shadow: var(--anneau), var(--halo);
}

.champ input::placeholder { color: var(--texte-faible); }

.champ__aide {
    margin-top: var(--r2);
    font-size: .8125rem;
    color: var(--texte-faible);
}

/* ════════════════════════════════════════════════════════════════════
   MESSAGES

   La barre latérale colorée porte l'information : elle reste lisible
   pour une personne qui distingue mal les couleurs, contrairement à un
   fond teinté seul.
   ════════════════════════════════════════════════════════════════════ */

.message {
    padding: var(--r3) var(--r4);
    margin-bottom: var(--r4);
    border-left: 3px solid;
    border-radius: var(--r1);
    font-size: .9375rem;
}

.message--succes  { border-color: var(--retrouve);  background: rgba(48, 164, 108, .1); }
.message--erreur  { border-color: var(--perdu);     background: rgba(229, 72, 77, .1); }
.message--info    { border-color: var(--info);      background: rgba(74, 144, 217, .1); }
.message--attention { border-color: var(--attention); background: rgba(245, 165, 36, .1); }

/* ════════════════════════════════════════════════════════════════════
   ÉTIQUETTES D'ÉTAT
   ════════════════════════════════════════════════════════════════════ */

/* ── UNE SEULE DÉFINITION DES ÉTATS ────────────────────────────────
   J'en avais écrit une seconde plus bas, qui écrasait celle-ci : elle
   avait perdu « .etat--encours » et « .etat--probleme », employées dix
   fois dans les gabarits. Ces deux états n'avaient plus de couleur.

   Les valeurs utiles de la seconde sont reportées ici, et elle est
   retirée : deux règles pour le même élément, c'est la seconde qui
   s'applique, et l'on corrige toujours la première. (1.36.1) */
.etat {
    display: inline-flex;
    align-items: center;
    gap: var(--r1);

    /* Un peu plus de retrait latéral : la pastille respire, et le texte
       ne touche pas le bord arrondi. */
    padding: 2px 9px;
    border-radius: 999px;
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .02em;
    white-space: nowrap;
}

.etat--attente  { background: rgba(245, 165, 36, .15); color: var(--attention); }
.etat--encours  { background: rgba(74, 144, 217, .15); color: var(--info); }
.etat--fait     { background: rgba(48, 164, 108, .15); color: var(--retrouve); }
.etat--probleme { background: rgba(229, 72, 77, .15);  color: var(--perdu); }

/* « erreur » est un synonyme de « probleme » : les deux existent dans
   les gabarits selon leur âge. Le déclarer ici évite qu'un état passe
   en texte nu selon le mot employé. */

/* ════════════════════════════════════════════════════════════════════
   TABLEAUX

   Sur mobile, un tableau large déborde. Le conteneur permet un
   défilement horizontal plutôt qu'une mise en page cassée.
   ════════════════════════════════════════════════════════════════════ */

.tableau-conteneur {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.tableau {
    width: 100%;
    border-collapse: collapse;
    font-size: .9375rem;
}

/* ── UNE SEULE DÉFINITION DES TITRES DE TABLEAU ────────────────────
   J'en avais écrit deux autres plus bas, qui se contredisaient sur le
   retrait : la dernière gagnait, et corriger la première ne faisait
   rien.

   ── PLUS PETITS ET PLUS PÂLES ──────────────────────────────────────
   Ils étaient de la même taille que le contenu : l'œil les lisait comme
   une première ligne de données. (1.36.1) */
.tableau th {
    padding: var(--r3) var(--r5);
    text-align: left;
    font-size: .6875rem;
    font-weight: 600;
    color: var(--texte-faible);
    text-transform: uppercase;
    letter-spacing: .06em;
    border-bottom: 1px solid var(--bordure);
    white-space: nowrap;
}

.tableau td {
    padding: var(--r4) var(--r5);
    border-bottom: 1px solid var(--bordure);
    vertical-align: middle;
}

.tableau tbody tr:hover { background: var(--fond-eleve); }

/* ════════════════════════════════════════════════════════════════════
   UTILITAIRES
   ════════════════════════════════════════════════════════════════════ */

.pile      { display: flex; flex-direction: column; gap: var(--r4); }
/* ── UNE LIGNE QUI SAIT PASSER À LA LIGNE ──────────────────────────
   « .rang » était un flex SANS retour à la ligne. Sur un écran large,
   trois champs côte à côte tiennent sans peine ; sur un téléphone, ils
   se compressent jusqu'à ce qu'un menu déroulant affiche « Chi » pour
   « Chien » et « Inc » pour « Inconnu ».

   Le défaut touchait trente-deux gabarits, dont les formulaires de
   déclaration d'animal perdu : ceux qu'on remplit debout, dehors, sur un
   téléphone. (0.99.0)

   « align-items: center » est conservé pour les rangs de boutons, où
   c'est ce qu'on veut. Les rangs de CHAMPS s'alignent en haut, plus bas :
   un champ avec sa phrase d'aide est plus haut que son voisin, et
   centrer les deux décale les cadres de saisie. */
.rang {
    display: flex;
    align-items: center;
    gap: var(--r3);
    flex-wrap: wrap;
}

/* ── LES CHAMPS DANS UN RANG ───────────────────────────────────────
   Chacun prend sa part de la largeur, mais jamais moins de 12rem :
   c'est la largeur sous laquelle un menu déroulant tronque son
   libellé. En dessous, ils passent à la ligne plutôt que de se serrer.

   « min-width: 0 » sur la base flex évite qu'un contenu long refuse de
   se réduire et déborde de la carte, ce que fait un input par défaut. */
.rang > .champ {
    flex: 1 1 12rem;
    min-width: 0;
    margin-bottom: 0;
}

/* Un rang de champs s'aligne en HAUT : les phrases d'aide n'ont pas
   toutes la même hauteur, et un alignement centré décalerait les
   cadres de saisie les uns par rapport aux autres. */
.rang:has(> .champ) { align-items: flex-start; }

.rang--fin { justify-content: flex-end; }
.entre     { display: flex; align-items: center; justify-content: space-between; gap: var(--r4); }

.doux    { color: var(--texte-doux); }
.faible  { color: var(--texte-faible); font-size: .875rem; }
/*
 * Les valeurs en chasse fixe sont dorées.
 *
 * C'est la signature de la direction retenue : dans un atelier, ce qui
 * est gravé se lit différemment du reste. Un code de médaille, un
 * numéro de commande, une clé d'export : autant de choses qu'on recopie
 * ou qu'on dicte, et que l'œil doit retrouver d'un coup dans un
 * tableau.
 */
.mono {
    font-family: var(--police-mono);
    font-size: .875rem;
    color: var(--or);
    letter-spacing: .01em;
}

/* Sauf à l'intérieur d'un texte déjà atténué, où le doré ferait
   ressortir un détail secondaire plus que la phrase qui le porte. */
.faible .mono,
.mono.faible,
td.faible .mono { color: inherit; }
.centre  { text-align: center; }

/* Visible pour les lecteurs d'écran, invisible à l'œil. */
.lecteur-seul {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ── Pastille du panier ─────────────────────────────────────────────
   Toujours dans le document, masquée quand le panier est vide : après
   un ajout sans rechargement, il faut qu'il y ait quelque chose à
   mettre à jour, sans quoi le PREMIER article n'apparaîtrait pas. Or
   c'est justement celui qui compte. */

.pastille[hidden] { display: none; }

/* Une brève pulsation au changement : sans elle, le chiffre change en
   haut de page pendant que le regard est en bas, et personne ne le
   voit. */
.pastille--change { animation: pastille-pulse .45s var(--sortie); }

@keyframes pastille-pulse {
    0%   { transform: scale(1); }
    35%  { transform: scale(1.35); }
    100% { transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
    .pastille--change { animation: none; }
}

/* ════════════════════════════════════════════════════════════════════
   MINI-PANIER

   Un <dialog>, parce que le navigateur sait déjà gérer la touche Échap,
   le clic à l'extérieur, le piégeage du clavier et l'annonce aux
   lecteurs d'écran. Réécrire tout cela prendrait cent lignes, et on
   oublierait le clavier — c'est toujours ce qu'on oublie.
   ════════════════════════════════════════════════════════════════════ */

.mini-panier {
    /*
     * Plus large qu'avant : 26rem laissait le détail de gravure se
     * replier ligne après ligne, et une médaille à trois motifs
     * occupait vingt lignes au lieu de dix.
     */
    width: min(32rem, 100vw);
    max-width: none;
    max-height: 100dvh;
    height: 100dvh;

    /* Collé à droite, pas centré : le lien qu'on vient de cliquer est
       en haut à droite, et le volet naît sous le doigt. */
    margin: 0 0 0 auto;
    padding: 0;

    background: var(--fond-carte);
    border: none;
    border-left: 1px solid var(--bordure);
    color: var(--texte);

    animation: mini-panier-entre .28s var(--sortie);
}

.mini-panier::backdrop {
    background: rgba(0, 0, 0, .55);
    animation: mini-panier-fond .28s var(--sortie);
}

@keyframes mini-panier-entre {
    from { transform: translateX(100%); }
    to   { transform: translateX(0); }
}

@keyframes mini-panier-fond {
    from { opacity: 0; }
    to   { opacity: 1; }
}

.mini-panier__entete {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--r3);
    padding: var(--r5);
    border-bottom: 1px solid var(--bordure);
}

.mini-panier__entete h2 {
    margin: 0;
    font-family: var(--police-titre);
    font-size: 1.125rem;
}

.mini-panier__fermer {
    width: 40px;
    height: 40px;
    display: grid;
    place-items: center;
    background: transparent;
    border: 1px solid var(--bordure);
    border-radius: var(--arrondi);
    color: var(--texte-doux);
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
    transition: box-shadow .18s var(--sortie), border-color .18s var(--sortie),
                color .18s var(--sortie);
}

.mini-panier__fermer:hover {
    color: var(--or);
    border-color: rgba(var(--or-rvb), .5);
    box-shadow: var(--anneau);
}

.mini-panier__fermer:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--fond-carte), 0 0 0 4px var(--or);
}

.mini-panier__corps {
    padding: var(--r5);
    overflow-y: auto;

    /* Le corps occupe ce qui reste : sans cette hauteur, un panier long
       dépasserait la fenêtre sans jamais défiler. */
    height: calc(100dvh - 5.5rem);
}

/* Dans le volet, le résumé n'a plus besoin de son propre cadre : il EST
   le volet. */
.mini-panier .panier-resume {
    margin: 0;
    padding: 0;
    background: none;
    border: none;
}

.panier-resume__vide { color: var(--texte-doux); }

@media (prefers-reduced-motion: reduce) {
    .mini-panier,
    .mini-panier::backdrop { animation: none; }
}

/* ════════════════════════════════════════════════════════════════════
   CASES À COCHER ET BOUTONS RADIO

   Ils étaient laissés au navigateur : bleu système, minuscules, hors du
   thème. Sur un fond sombre, une case blanche à bord gris passe pour un
   défaut d'affichage plutôt que pour un choix.

   ── LA VRAIE CASE RESTE DANS LE DOCUMENT ─────────────────────────────

   On la rend transparente et on dessine par-dessus, plutôt que de la
   masquer avec display:none. Masquée, elle sort de l'ordre de
   tabulation : impossible de cocher au clavier, et les lecteurs d'écran
   ne l'annoncent plus.

   C'est l'erreur la plus courante sur ce composant, et elle ne se voit
   pas tant qu'on utilise une souris.
   ════════════════════════════════════════════════════════════════════ */

.champ--case {
    display: inline-flex;
    align-items: center;
    gap: var(--r3);
    cursor: pointer;
    user-select: none;

    /* La zone cliquable dépasse la case : on vise mal sur un téléphone,
       et 44 px est la taille sous laquelle on manque sa cible. */
    min-height: 44px;
}

.champ--case input[type="checkbox"],
.champ--case input[type="radio"] {
    appearance: none;
    -webkit-appearance: none;

    flex: 0 0 auto;
    width: 22px;
    height: 22px;
    margin: 0;

    background: var(--fond);

    /*
     * Une bordure PLUS CLAIRE que celle des cartes.
     *
     * La bordure ordinaire, #23262e, ne donne que 1,29 de contraste sur
     * le fond : une case décochée serait presque invisible, et le client
     * ne verrait pas qu'''il y a quelque chose à cocher.
     *
     * 3 est le minimum pour un contour d'''élément, et c'''est une règle
     * différente de celle du texte : ici ce n'''est pas la lisibilité qui
     * est en jeu, c'''est le fait de repérer le composant.
     */
    border: 1px solid #606779;
    border-radius: 6px;
    cursor: pointer;
    position: relative;

    transition: background .18s var(--sortie), border-color .18s var(--sortie),
                box-shadow .18s var(--sortie);
}

.champ--case input[type="radio"] { border-radius: 50%; }

.champ--case:hover input {
    border-color: rgba(var(--or-rvb), .5);
    box-shadow: var(--anneau);
}

/* Cochée : le même doré que l'action principale. C'est le seul aplat de
   couleur de la ligne, donc l'œil le trouve sans chercher. */
.champ--case input:checked {
    background: var(--or);
    border-color: var(--or);
}

/* La coche est dessinée en CSS plutôt qu'avec une image ou une police
   d'icônes : elle suit la couleur, s'agrandit sans flou, et ne demande
   aucun fichier de plus. */
.champ--case input[type="checkbox"]:checked::after {
    content: "";
    position: absolute;
    left: 7px;
    top: 3px;
    width: 6px;
    height: 11px;
    border: solid #14100a;
    border-width: 0 2.5px 2.5px 0;
    transform: rotate(45deg);
}

.champ--case input[type="radio"]:checked::after {
    content: "";
    position: absolute;
    inset: 5px;
    background: #14100a;
    border-radius: 50%;
}

/* Le focus clavier est détaché, comme sur les boutons : on navigue au
   clavier sans savoir où est la souris. */
.champ--case input:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--fond), 0 0 0 4px var(--or);
}

.champ--case input:disabled {
    background: #15171e;
    border-color: #1e222b;
    cursor: not-allowed;
}

.champ--case input:disabled + span,
.champ--case:has(input:disabled) { color: var(--texte-faible); cursor: not-allowed; }

@media (prefers-reduced-motion: reduce) {
    .champ--case input { transition: none; }
}

/* Lignes du mini-panier : le nom à gauche, les chiffres à droite, le
   texte gravé sous le nom. Deux médailles identiques sans leur gravure
   sont indiscernables, et c'est justement ce que le client vient
   vérifier. */

/*
 * Le nom occupe la première colonne, la croix et le prix la seconde.
 * « min-width: 0 » est nécessaire : sans lui, un nom long refuse de se
 * couper et élargit la colonne au-delà de la place disponible.
 */
.panier-resume__nom {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    overflow-wrap: anywhere;
}


.panier-resume__chiffres {
    display: flex;
    align-items: baseline;
    gap: var(--r3);
    white-space: nowrap;
}

.panier-resume__prix { font-weight: 600; }

.panier-resume__total {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--r3);
    padding-top: var(--r3);
    border-top: 1px solid var(--bordure);
}

.panier-resume__total strong { font-size: 1.125rem; }

/* Le logo, quand il remplace le texte. La hauteur est fixe et la
   largeur suit : un logo carré et un logo long doivent occuper la même
   place verticale, sinon l'en-tête change de hauteur d'un site à
   l'autre. */
/*
 * La hauteur est posée EN LIGNE par le gabarit, depuis un réglage.
 *
 * Elle était fixée ici à 2rem : le logo paraissait trop petit et rien
 * ne permettait de le changer sans éditer la feuille.
 *
 * La largeur reste bornée : un logo très large repousserait le menu
 * hors du centre, et la grille perdrait son équilibre.
 */
.entete__logo { display: block; width: auto; max-width: 260px; }

/* ════════════════════════════════════════════════════════════════════
   CHAMP DE FICHIER

   Il était laissé au navigateur : un bouton gris clair, une police
   système, un fond blanc. Sur un thème sombre, il ressemblait à un
   élément étranger tombé dans la page.

   ── LE BOUTON INTERNE SE STYLE, LE RESTE NON ─────────────────────────

   « ::file-selector-button » atteint le bouton « Parcourir… ». Le texte
   « Aucun fichier sélectionné » à côté, lui, n'est atteignable par
   aucun sélecteur : il appartient au navigateur, et le masquer
   supprimerait le seul retour indiquant qu'un fichier est choisi.

   On habille donc ce qu'on peut, et on laisse le reste lisible plutôt
   que de le cacher.
   ════════════════════════════════════════════════════════════════════ */

.champ input[type="file"] {
    width: 100%;
    padding: var(--r3);
    background: var(--fond);
    border: 1px dashed #606779;   /* 3,46 de contraste : sous 3, la zone de dépôt ne se voit pas */
    border-radius: var(--arrondi);
    color: var(--texte-doux);
    font: inherit;
    font-size: .875rem;
    cursor: pointer;
    transition: border-color .18s var(--sortie), box-shadow .18s var(--sortie);
}

/* Le trait est en pointillés : c'est le repère habituel d'une zone où
   l'on dépose quelque chose, et il distingue ce champ des autres au
   premier regard. */
.champ input[type="file"]:hover {
    border-color: rgba(var(--or-rvb), .55);
    box-shadow: var(--anneau);
}

.champ input[type="file"]:focus-visible {
    outline: none;
    border-color: transparent;
    box-shadow: 0 0 0 2px var(--fond-carte), 0 0 0 4px var(--or);
}

.champ input[type="file"]::file-selector-button {
    margin-right: var(--r3);
    padding: var(--r2) var(--r4);
    background: var(--fond-eleve);
    border: 1px solid var(--bordure);
    border-radius: var(--r1);
    color: var(--texte);
    font: inherit;
    font-size: .875rem;
    font-weight: 600;
    cursor: pointer;
    transition: background .18s var(--sortie), border-color .18s var(--sortie);
}

.champ input[type="file"]:hover::file-selector-button {
    background: var(--bordure);
    border-color: rgba(var(--or-rvb), .5);
}

/* Un paragraphe d'état, quand rien n'est encore déposé. Sans marge, il
   se collait au libellé suivant et les deux se lisaient comme une seule
   ligne. */
.carte > .doux { margin: 0 0 var(--r4); color: var(--texte-doux); }

@media (prefers-reduced-motion: reduce) {
    .champ input[type="file"],
    .champ input[type="file"]::file-selector-button { transition: none; }
}

/* ════════════════════════════════════════════════════════════════════
   EN-TÊTE : TROIS ZONES

   ── POURQUOI UNE GRILLE ET PAS UN space-between ──────────────────────

   Avec « space-between », le menu se décale dès que le logo ou le nombre
   d'articles change de largeur : il bouge d'une page à l'autre, et l'œil
   le suit au lieu de lire.

   Une grille dont les colonnes de bord ont la même largeur garde le
   menu au centre exact, quoi qu'elles contiennent.
   ════════════════════════════════════════════════════════════════════ */

.entete__grille {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: var(--r4);
}

.entete__marque { justify-self: start; text-decoration: none; }

.entete__nav {
    justify-self: center;

    /*
     * Reprise de « boutique.css » en 1.168.0 : une feuille de boutique
     * n'a pas à décider de la navigation du site, et sa position APRÈS
     * celle-ci écrasait le « display: none » du bloc mobile.
     */
    display: flex;
    align-items: center;
    gap: var(--r5);
}

.entete__actions {
    justify-self: end;
    display: flex;
    align-items: center;
    gap: var(--r4);
}

.entete__compte {
    font-size: .8125rem;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    white-space: nowrap;
}

.entete__separateur {
    width: 1px;
    height: 20px;
    background: var(--bordure);
}

.entete__panier {
    display: inline-flex;
    align-items: center;
    gap: var(--r2);

    /*
     * Reprise de « boutique.css » en 1.168.0. Sans elle, la pastille du
     * nombre d'articles se positionne par rapport à la PAGE et non au
     * panier : elle part dans un coin de l'écran.
     */
    position: relative;
}

.entete__panier-icone { font-size: 1.125rem; line-height: 1; }

/* ── Le menu ──────────────────────────────────────────────────────── */

.menu {
    display: flex;
    align-items: center;
    gap: var(--r5);
    list-style: none;
    margin: 0;
    padding: 0;
}

.menu__item { position: relative; }

.menu__lien {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: var(--r3) 0;
    font-size: .8125rem;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    white-space: nowrap;
}

.menu__fleche { font-size: .75rem; opacity: .7; }

/*
 * ── LE SOUS-MENU EST DANS LE DOCUMENT, MASQUÉ PAR LE STYLE ───────────
 *
 * Le construire au survol le rendrait invisible aux moteurs de recherche
 * et inatteignable au clavier. Ici, il est simplement transparent et
 * hors de portée du pointeur tant qu'on ne le demande pas.
 */
.menu__sous {
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%) translateY(-4px);
    min-width: 12rem;
    padding: var(--r2);
    margin: 0;
    list-style: none;

    background: var(--fond-carte);
    border: 1px solid var(--bordure);
    border-radius: var(--arrondi);
    box-shadow: var(--ombre), var(--anneau);

    opacity: 0;
    visibility: hidden;
    transition: opacity .18s var(--sortie), transform .18s var(--sortie),
                visibility .18s;
    z-index: 40;
}

/*
 * « :focus-within » ouvre au clavier.
 *
 * Sans lui, on atteint bien les liens du sous-menu en tabulant, mais ils
 * restent invisibles : le focus part sur un élément qu'on ne voit pas, et
 * l'on ne sait plus où l'on est dans la page.
 */
.menu__item--parent:hover .menu__sous,
.menu__item--parent:focus-within .menu__sous {
    opacity: 1;
    visibility: visible;
    transform: translateX(-50%) translateY(0);
}

/*
 * Un pont invisible entre l'entrée et son sous-menu.
 *
 * Le sous-menu est décollé de 4 px : sans ce pont, le survol se rompt en
 * traversant l'espace, et le menu se referme au moment où l'on descend
 * vers lui.
 */
.menu__item--parent::after {
    content: "";
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    height: 8px;
}

.menu__sous a {
    display: block;
    padding: var(--r2) var(--r3);
    border-radius: var(--r1);
    font-size: .875rem;
    color: var(--texte-doux);
}

.menu__sous a:hover,
.menu__sous a:focus-visible {
    background: var(--fond-eleve);
    color: var(--or);
    text-decoration: none;
}

/*
 * Sur un écran étroit, la grille se replie en deux lignes : marque et
 * actions au-dessus, menu en dessous sur toute la largeur. Le menu
 * défile latéralement plutôt que de se replier sur plusieurs lignes, qui
 * feraient grandir l'en-tête à chaque entrée ajoutée.
 */
/*
 * Caché par défaut : sur grand écran, la barre du haut suffit. Le bloc
 * ci-dessous les montre sur petit écran, et il vient APRÈS : c'est ce
 * qui le fait gagner.
 */
.menu-bouton { display: none; }
.menu-mobile { display: none; }

@media (max-width: 62rem) {
    .entete__grille {
        grid-template-columns: 1fr auto;
        row-gap: var(--r3);
    }

    .entete__nav {
        grid-column: 1 / -1;
        justify-self: stretch;
        overflow-x: auto;
    }

    .menu { gap: var(--r4); justify-content: center; }

    /*
     * ── LE COMPTE PASSE DANS LE MENU ──────────────────────────────────
     *
     * Il était masqué sans rien pour le remplacer : impossible de se
     * connecter depuis un téléphone, sauf à connaître l'adresse.
     *
     * Il reparaît dans le panneau, avec la connexion ET l'inscription.
     * (1.164.0)
     */
    .entete__compte { display: none; }

    /*
     * La barre du haut disparaît : ses entrées sont dans le panneau. La
     * garder donnerait deux navigations pour les mêmes liens, dont une
     * qui défile horizontalement — le geste que personne ne trouve.
     */
    .entete__nav { display: none; }

    .menu-bouton { display: flex; }

    /* Ouvert, le panneau prend la largeur sous l'entête. */
    .menu-bascule:checked ~ .menu-mobile { display: block; }
}

@media (prefers-reduced-motion: reduce) {
    .menu__sous { transition: none; }
}

/* ════════════════════════════════════════════════════════════════════
   DÉTAIL DE GRAVURE

   Ce que le client a composé, avec le prix de chaque option. Repris à
   l'identique dans le mini-panier et dans la page du panier : c'est le
   même gabarit, donc la même mise en forme.
   ════════════════════════════════════════════════════════════════════ */

.detail-gravure {
    display: flex;
    gap: var(--r4);
    align-items: flex-start;
    margin-top: var(--r3);
}

/* L'aperçu sur un damier : sans lui, une médaille claire sur fond
   transparent paraît absente. */
.detail-gravure__apercu {
    flex: 0 0 auto;
    width: 72px;
    height: 72px;
    object-fit: contain;
    border-radius: var(--arrondi);
    background-image:
        linear-gradient(45deg, #2a2d36 25%, transparent 25%),
        linear-gradient(-45deg, #2a2d36 25%, transparent 25%),
        linear-gradient(45deg, transparent 75%, #2a2d36 75%),
        linear-gradient(-45deg, transparent 75%, #2a2d36 75%);
    background-size: 12px 12px;
    background-position: 0 0, 0 6px, 6px -6px, -6px 0;
}

/*
 * ── DEUX COLONNES, PAS UN EMPILEMENT ─────────────────────────────────
 *
 * C'était un « flex » avec retour à la ligne : dès que le libellé et la
 * valeur ne tenaient pas ensemble, la valeur passait dessous. « Police :
 * » sur une ligne, « Bungee Regular » sur la suivante.
 *
 * Une médaille à trois motifs occupait vingt lignes au lieu de dix, et
 * l'œil ne retrouvait plus quelle valeur allait avec quel libellé.
 *
 * Une grille garde le libellé à gauche et la valeur à droite, quoi qu'il
 * arrive. La colonne des libellés est dimensionnée sur le plus long,
 * sans dépasser 40 % : au-delà, un libellé bavard écraserait la valeur,
 * qui est ce qu'on vient lire.
 */
.detail-gravure__liste {
    /*
     * Une seule règle pour ce sélecteur.
     *
     * Il y en avait deux, à quelques lignes d écart : c est l ordre
     * d écriture qui décidait, et déplacer un bloc aurait suffi à
     * défaire la grille sans que personne fasse le lien.
     */
    margin: 0;
    min-width: 0;
    flex: 1 1 auto;

    display: grid;
    /*
     * ── LES COLONNES SE SERRENT SUR LEUR CONTENU ──────────────────────
     *
     * C'était « minmax(auto, 40%) 1fr » : la seconde colonne prenait
     * toute la place restante, et la valeur se retrouvait plaquée au
     * bord droit. Sur une ligne courte comme « Halo : 10 px », l'œil
     * traversait cinq centimètres de vide entre les deux.
     *
     * « max-content » demande à chaque colonne la largeur de son plus
     * long contenu, et rien de plus. Le tableau se serre, et les deux
     * parties restent liées visuellement.
     *
     * « minmax(0, max-content) » sur la seconde : sans le minimum à
     * zéro, une valeur très longue refuserait de se couper et
     * déborderait du volet.
     */
    grid-template-columns: max-content minmax(0, max-content);
    gap: 2px var(--r2);
    align-items: baseline;

    /*
     * La grille ne s'étire pas non plus horizontalement : posée dans un
     * conteneur large, elle laisserait sinon les colonnes s'écarter
     * malgré « max-content ».
     */
    justify-content: start;
}

.detail-gravure__ligne {
    display: contents;
    font-size: .8125rem;
}

.detail-gravure__ligne dt {
    color: var(--texte-faible);
    font-size: .8125rem;

    /*
     * ── LES LIBELLÉS SONT ALIGNÉS À DROITE ────────────────────────────
     *
     * La colonne prend la largeur du plus long, « Zones de texte
     * supplémentaires ». Alignés à gauche, les libellés courts laissent
     * un vide avant leurs deux-points : « Halo :          10 px ».
     *
     * À droite, les deux-points touchent leur valeur quelle que soit la
     * longueur du libellé, et la colonne de gauche reste lisible parce
     * qu on la parcourt du regard, sans la lire mot à mot.
     */
    justify-self: end;
    text-align: right;

    /*
     * Le libellé peut se couper si nécessaire.
     *
     * « nowrap » le rendait insécable : sur un écran étroit, il poussait
     * la valeur hors du volet au lieu de céder.
     */
    overflow-wrap: anywhere;
}

.detail-gravure__ligne dt::after { content: " :"; }

.detail-gravure__ligne dd {
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    gap: var(--r2);
    align-items: baseline;
    min-width: 0;
    font-size: .8125rem;
}

/* La valeur gravée en doré : c'est la signature du site, et c'est ce
   que le client vient vérifier. */
.detail-gravure__valeur {
    color: var(--or);
    overflow-wrap: anywhere;
}

.detail-gravure__prix { color: var(--texte-doux); white-space: nowrap; }

/* « Offert » se distingue d'un prix sans crier : c'est une bonne
   nouvelle, pas une alerte. */
.detail-gravure__note {
    color: var(--texte-faible);
    font-size: .75rem;
    font-style: italic;
}

/* ── Aperçu agrandi ─────────────────────────────────────────────────
   La vignette fait 72 pixels : on y distingue qu'une médaille a été
   gravée, pas ce qui est écrit dessus. Or c'est exactement ce que le
   client vient vérifier avant de payer. */

.detail-gravure__lien {
    position: relative;
    display: block;
    flex: 0 0 auto;
    border-radius: var(--arrondi);
    line-height: 0;
    transition: box-shadow .18s var(--sortie), transform .18s var(--sortie);
}

.detail-gravure__lien:hover {
    box-shadow: var(--anneau), var(--halo);
    transform: translateY(-1px);
}

.detail-gravure__lien:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--fond-carte), 0 0 0 4px var(--or);
}

/* La loupe dit que l'image s'agrandit. Sans elle, rien n'indique que la
   vignette est cliquable, et personne ne l'essaie. */
.detail-gravure__loupe {
    position: absolute;
    right: 4px;
    bottom: 4px;
    width: 20px;
    height: 20px;
    display: grid;
    place-items: center;
    background: rgba(0, 0, 0, .65);
    border-radius: 4px;
    color: var(--or);
    font-size: .75rem;
    line-height: 1;
}

.apercu-agrandi {
    max-width: min(90vw, 40rem);
    max-height: 90dvh;
    padding: 0;
    background: var(--fond-carte);
    border: 1px solid var(--bordure);
    border-radius: var(--arrondi-large);
    box-shadow: var(--ombre), var(--anneau);
}

.apercu-agrandi::backdrop { background: rgba(0, 0, 0, .8); }

/* Le damier derrière l'image : l'aperçu porte le fond du produit, mais
   ses bords peuvent être transparents, et une médaille claire y
   paraîtrait absente. */
.apercu-agrandi img {
    display: block;
    max-width: 100%;
    max-height: 85dvh;
    border-radius: var(--arrondi-large);
    background-image:
        linear-gradient(45deg, #2a2d36 25%, transparent 25%),
        linear-gradient(-45deg, #2a2d36 25%, transparent 25%),
        linear-gradient(45deg, transparent 75%, #2a2d36 75%),
        linear-gradient(-45deg, transparent 75%, #2a2d36 75%);
    background-size: 20px 20px;
    background-position: 0 0, 0 10px, 10px -10px, -10px 0;
}

.apercu-agrandi__fermer {
    position: absolute;
    top: var(--r3);
    right: var(--r3);
    width: 36px;
    height: 36px;
    display: grid;
    place-items: center;
    background: rgba(0, 0, 0, .7);
    border: 1px solid var(--bordure);
    border-radius: var(--arrondi);
    color: var(--texte);
    font-size: 1.25rem;
    line-height: 1;
    cursor: pointer;
}

.apercu-agrandi__fermer:hover {
    color: var(--or);
    border-color: rgba(var(--or-rvb), .5);
}

@media (prefers-reduced-motion: reduce) {
    .detail-gravure__lien { transition: none; }
    .detail-gravure__lien:hover { transform: none; }
}

/* ── Croix de retrait dans le mini-panier ───────────────────────────
   Dorée, comme tout ce qui agit sur ce site. Le rouge est réservé aux
   messages d'erreur : une croix rouge sur chaque ligne donnerait un
   panier qui a l'air en panne. */

.panier-resume__retrait { display: inline-flex; margin-left: var(--r2); }

.panier-retirer {
    width: 26px;
    height: 26px;
    display: grid;
    place-items: center;
    padding: 0;
    background: transparent;
    border: 1px solid var(--bordure);
    border-radius: var(--r1);
    color: var(--texte-faible);
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
    transition: color .18s var(--sortie), border-color .18s var(--sortie),
                box-shadow .18s var(--sortie);
}

.panier-retirer:hover {
    color: var(--or);
    border-color: rgba(var(--or-rvb), .55);
    box-shadow: var(--anneau);
}

.panier-retirer:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--fond-carte), 0 0 0 4px var(--or);
}

/* Désactivée pendant l'envoi : sans ce retour, on clique une seconde
   fois en croyant que rien ne s'est passé. */
.panier-retirer:disabled {
    opacity: .4;
    cursor: not-allowed;
    box-shadow: none;
}

@media (prefers-reduced-motion: reduce) {
    .panier-retirer { transition: none; }
}

/* Logo sur la page de connexion. Centré, avec la même contrainte de
   largeur : la carte de connexion est plus étroite que l'en-tête. */

.nu__logo {
    display: block;
    width: auto;
    max-width: 220px;
    margin: 0 auto;
}

/* ════════════════════════════════════════════════════════════════════
   REMPLISSAGE AUTOMATIQUE

   Quand le navigateur remplit un champ depuis son gestionnaire de mots
   de passe, il impose un fond jaune pâle et un texte noir. Sur un thème
   sombre, deux champs sur trois deviennent des rectangles jaunes.

   ── ON NE PEUT PAS CHANGER CE FOND DIRECTEMENT ───────────────────────

   « background-color » est ignoré sur ces champs, délibérément : le
   navigateur veut que l'utilisateur voie ce qu'il a rempli lui-même.

   Le contournement retenu est une ombre intérieure massive, qui recouvre
   le fond sans le remplacer. Et « transition-delay » très long empêche
   le jaune d'apparaître une fraction de seconde avant d'être couvert.
   ════════════════════════════════════════════════════════════════════ */

input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
textarea:-webkit-autofill,
select:-webkit-autofill {
    -webkit-box-shadow: 0 0 0 1000px var(--fond) inset;
    box-shadow: 0 0 0 1000px var(--fond) inset;
    -webkit-text-fill-color: var(--texte);
    caret-color: var(--texte);
    transition: background-color 100000s ease-in-out 0s;
}

/* Le focus garde son anneau : l'ombre intérieure ne doit pas le
   remplacer, sinon on ne voit plus où l'on est. */
input:-webkit-autofill:focus {
    -webkit-box-shadow: 0 0 0 1000px var(--fond) inset,
                        var(--anneau), var(--halo);
    box-shadow: 0 0 0 1000px var(--fond) inset,
                var(--anneau), var(--halo);
}

/* Firefox utilise un sélecteur différent et accepte, lui, une couleur
   de fond ordinaire. */
input:autofill,
textarea:autofill {
    background-color: var(--fond);
    color: var(--texte);
}

/* ── Espace client ──────────────────────────────────────────────────── */



.medailles { list-style: none; margin: 0; padding: 0; }

.medaille {
    display: flex;
    align-items: center;
    gap: var(--r4);
    flex-wrap: wrap;
    padding: var(--r3) 0;
    border-bottom: 1px solid var(--bordure);
}

.medaille:last-child { border-bottom: none; }

.medaille__photo {
    flex: 0 0 auto;
    width: 56px;
    height: 56px;
    object-fit: cover;
    border-radius: 50%;
    border: 1px solid var(--bordure);
}

/* Sans photo, une empreinte plutôt qu'un carré vide : un cadre vide
   ressemble à une image qui n'a pas chargé. */
.medaille__photo--vide {
    display: grid;
    place-items: center;
    background: var(--fond);
    font-size: 1.5rem;
}

.medaille__corps {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 1 1 12rem;
    min-width: 0;
}

.medaille__corps .mono { font-size: .8125rem; }

/* ════════════════════════════════════════════════════════════════════
   ESPACE CLIENT : MENU À GAUCHE, CONTENU À DROITE
   ════════════════════════════════════════════════════════════════════ */

/*
 * ── LA COLONNE DU MENU EST FIXE ──────────────────────────────────────
 *
 * Elle vaut 16rem quel que soit le contenu de la section. Une colonne
 * dimensionnée sur son contenu changerait de largeur d une section à
 * l autre, et le menu sauterait à chaque clic.
 *
 * Le conteneur porte « margin: 0 auto » par la classe « conteneur ».
 * C est sans danger ici : cet élément est le conteneur de la grille, pas
 * un élément DANS une grille. Sur un élément de grille, la même marge
 * annulerait l étirement et le ferait rétrécir à son contenu.
 */
.espace { display: grid; gap: var(--r5); align-items: start; }

@media (min-width: 56rem) {
    .espace { grid-template-columns: 16rem 1fr; }

    /* Le menu suit le défilement, fixé à sa propre hauteur : « sticky »
       seul le ferait suivre la hauteur du contenu, et il sauterait
       d'une section à l'autre. */
    .espace__menu {
        position: sticky;
        top: var(--r5);
        align-self: start;
        max-height: calc(100dvh - var(--r7));
        overflow-y: auto;
    }
}

.espace__menu {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--r3);
    background: var(--fond-carte);
    border: 1px solid var(--bordure);
    border-radius: var(--arrondi-large);
}

.espace__bonjour {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--r3);
    margin-bottom: var(--r2);
    border-bottom: 1px solid var(--bordure);
    min-width: 0;
}

.espace__bonjour .faible {
    font-size: .8125rem;
    overflow-wrap: anywhere;
}

.espace__lien {
    display: flex;
    align-items: center;
    gap: var(--r3);
    padding: var(--r3);
    border-radius: var(--arrondi);
    color: var(--texte-doux);
    font-size: .9375rem;
    text-decoration: none;
    transition: background .18s var(--sortie), color .18s var(--sortie);
}

.espace__lien:hover,
.espace__lien:focus-visible {
    background: var(--fond-eleve);
    color: var(--or);
    text-decoration: none;
}

.espace__lien:focus-visible { outline: 2px solid var(--or); outline-offset: 1px; }

.espace__lien--actif {
    background: var(--fond-eleve);
    color: var(--or);
    font-weight: 600;
}

.espace__icone { font-size: 1rem; line-height: 1; }

/* La pastille des messages non lus se pousse à droite. */
.espace__lien .pastille { margin-left: auto; }

.espace__sortie {
    margin-top: var(--r3);
    padding-top: var(--r3);
    border-top: 1px solid var(--bordure);
}

.espace__sortie .bouton { width: 100%; }

.espace__contenu { min-width: 0; }

.espace__contenu h1 { margin: 0 0 var(--r4); }

/* ── Scans ──────────────────────────────────────────────────────────── */

.scans { list-style: none; margin: 0; padding: 0; }

.scan {
    display: flex;
    flex-wrap: wrap;
    gap: var(--r2) var(--r4);
    justify-content: space-between;
    align-items: center;
    padding: var(--r3) 0;
    border-bottom: 1px solid var(--bordure);
}

.scan:last-child { border-bottom: none; }

.scan__corps { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

.scan__lieu {
    display: flex;
    align-items: center;
    gap: var(--r3);
    flex-wrap: wrap;
}

/* ── Fils de discussion ─────────────────────────────────────────────── */

.fils { list-style: none; margin: 0; padding: 0; }

.fil {
    padding: var(--r4) 0;
    border-bottom: 1px solid var(--bordure);
}

.fil:last-child { border-bottom: none; }

/* Un fil avec une réponse non lue porte un filet doré : c'est ce qu'on
   vient chercher en ouvrant la messagerie. */



/* ════════════════════════════════════════════════════════════════════
   VOLET DE CONNEXION

   Le même geste que les sous-menus : dans le document, masqué par le
   style, ouvert au survol ET au clavier.
   ════════════════════════════════════════════════════════════════════ */

.compte-menu { position: relative; }

.compte-volet {
    position: absolute;
    top: 100%;
    right: 0;
    transform: translateY(-4px);
    min-width: 15rem;
    padding: var(--r2);

    background: var(--fond-carte);
    border: 1px solid var(--bordure);
    border-radius: var(--arrondi);
    box-shadow: var(--ombre), var(--anneau);

    opacity: 0;
    visibility: hidden;
    transition: opacity .18s var(--sortie), transform .18s var(--sortie),
                visibility .18s;
    z-index: 45;
}

/*
 * « :focus-within » ouvre au clavier.
 *
 * Sans lui, on atteint les liens du volet en tabulant mais ils restent
 * invisibles : le focus part sur un élément qu'on ne voit pas.
 */
.compte-menu:hover .compte-volet,
.compte-menu:focus-within .compte-volet {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

/*
 * Un pont invisible entre le lien et son volet, décollé de 4 px.
 *
 * Sans lui, le survol se rompt en traversant l'espace, et le volet se
 * referme au moment où l'on descend vers lui.
 */
.compte-menu::after {
    content: "";
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    height: 8px;
}

.compte-volet__action {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--r3);
    border-radius: var(--r1);
    color: var(--texte);
    text-decoration: none;
    transition: background .15s var(--sortie);
}

.compte-volet__action:hover,
.compte-volet__action:focus-visible {
    background: var(--fond-eleve);
    text-decoration: none;
}

.compte-volet__action strong { color: var(--or); font-size: .9375rem; }

.compte-volet__action .faible { font-size: .8125rem; }

.compte-volet__oubli {
    display: block;
    padding: var(--r2) var(--r3);
    margin-top: var(--r2);
    border-top: 1px solid var(--bordure);
    font-size: .8125rem;
    color: var(--texte-doux);
}

/*
 * Sur un écran étroit, le volet ne s'ouvre pas : il n'y a pas de survol
 * au doigt, et l'ouvrir au premier appui empêcherait d'atteindre le lien
 * lui-même. Le lien mène à la page de connexion, qui porte les deux
 * choix.
 */
@media (max-width: 62rem) {
    .compte-volet { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    .compte-volet { transition: none; }
}

/* ── Écrans d'une médaille dans l'espace client ─────────────────────── */

.conteneur--etroit { max-width: 52rem; }

.medaille-entete {
    display: flex;
    align-items: center;
    gap: var(--r4);
    flex-wrap: wrap;
    margin-bottom: var(--r4);
}

.medaille-entete > div {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.medaille-entete .mono { font-size: .8125rem; }

.chiffre { display: flex; flex-direction: column; gap: 2px; }

.chiffre strong { font-size: 1.5rem; color: var(--or); }

.chiffre .faible { font-size: .8125rem; }

/* Une médaille perdue ou désactivée se signale dans la liste : sans
   marque, on ne voit pas qu'une alerte court encore. */
.medaille--perdu { border-left: 2px solid var(--or); padding-left: var(--r3); }

.medaille__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--r2);
    width: 100%;
    margin-top: var(--r2);
}

/* La carte des scans. Une hauteur fixe : sans elle, le conteneur d'une
   carte Google reste à zéro et la carte ne s'affiche pas. */
.carte-scans {
    width: 100%;
    height: 22rem;
    border-radius: var(--arrondi);
    overflow: hidden;
}

@media (max-width: 40rem) {
    .carte-scans { height: 16rem; }
}

/* ════════════════════════════════════════════════════════════════════
   L'ÉDITEUR DE FICHE

   Chaque section porte une couleur d'accent différente sur son filet
   gauche. Quatre cartes identiques se confondent au défilement ; une
   couleur par sujet permet de retrouver « la partie contact » d'un coup
   d'œil, sans relire les titres.
   ════════════════════════════════════════════════════════════════════ */

.fiche-section {
    position: relative;
    border-left: 3px solid var(--bordure);
}

.fiche-section h2 {
    display: flex;
    align-items: center;
    gap: var(--r3);
}

.fiche-puce {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    background: var(--fond-eleve);
    font-size: 1rem;
}

.fiche-section--animal  { border-left-color: var(--or); }
.fiche-section--photo   { border-left-color: #7dd3a0; }
.fiche-section--contact { border-left-color: #f0a05a; }
.fiche-section--savoir  { border-left-color: #8ab4f8; }

.fiche-section--animal .fiche-puce  { background: rgba(var(--or-rvb), .16); }
.fiche-section--photo .fiche-puce   { background: rgba(125, 211, 160, .16); }
.fiche-section--contact .fiche-puce { background: rgba(240, 160, 90, .16); }
.fiche-section--savoir .fiche-puce  { background: rgba(138, 180, 248, .16); }

/* ── La photo ───────────────────────────────────────────────────────── */

.photo-zone {
    display: flex;
    gap: var(--r5);
    align-items: flex-start;
    flex-wrap: wrap;
}

.photo-apercu {
    flex: 0 0 auto;
    width: 11rem;
    height: 11rem;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;

    background: var(--fond);
    border: 1px dashed var(--bordure);
    border-radius: var(--arrondi);
}

.photo-apercu img {
    width: 100%;
    height: 100%;

    /* « cover » recadre sans déformer : un animal étiré est plus gênant
       qu'un animal rogné. */
    object-fit: cover;
}

.photo-vide { font-size: 2.5rem; opacity: .35; }

.photo-actions {
    flex: 1 1 14rem;
    display: flex;
    flex-direction: column;
    gap: var(--r3);
    min-width: 0;
}

/* Le champ de fichier natif est illisible et impossible à styler. On le
   masque sans le retirer du document : il reste atteignable au clavier,
   et son étiquette lui sert de bouton. */
.champ-fichier {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    overflow: hidden;
}

.champ-fichier:focus-visible + .bouton,
label.bouton:focus-within { outline: 2px solid var(--or); outline-offset: 2px; }

/* ── Les cases en pastilles ─────────────────────────────────────────── */

.cases-rang { display: flex; flex-wrap: wrap; gap: var(--r3); }

.case-pastille {
    display: inline-flex;
    align-items: center;
    gap: var(--r2);
    padding: var(--r2) var(--r4);

    background: var(--fond);
    border: 1px solid var(--bordure);
    border-radius: 999px;
    cursor: pointer;
    font-size: .875rem;
    transition: border-color .15s var(--sortie), background .15s var(--sortie);
}

.case-pastille:hover { border-color: var(--or); }

/* La pastille cochée se colore entièrement : une petite case à cocher se
   repère mal sur un fond sombre. */
.case-pastille:has(input:checked) {
    background: rgba(var(--or-rvb), .14);
    border-color: var(--or);
    color: var(--or);
}

.case-pastille--danger:has(input:checked) {
    background: rgba(240, 90, 90, .14);
    border-color: #f05a5a;
    color: #f05a5a;
}

/* La section santé : rouge tempéré, la couleur qu'on associe au soin
   sans en faire une alerte. */
.fiche-section--sante { border-left-color: #e08a8a; }
.fiche-section--sante .fiche-puce { background: rgba(224, 138, 138, .16); }

/* ── Onglets de la santé ────────────────────────────────────────────── */

.onglets {
    display: flex;
    gap: var(--r2);
    flex-wrap: wrap;
    margin: var(--r5) 0 var(--r4);
    border-bottom: 1px solid var(--bordure);
}

.onglet {
    padding: var(--r3) var(--r4);
    color: var(--texte-doux);
    text-decoration: none;
    border-bottom: 2px solid transparent;

    /* Le filet est décalé d'un pixel pour recouvrir celui du conteneur :
       sans cela, l'onglet actif porte deux traits superposés. */
    margin-bottom: -1px;
    transition: color .15s var(--sortie), border-color .15s var(--sortie);
}

.onglet:hover { color: var(--or); text-decoration: none; }

.onglet--actif {
    color: var(--or);
    border-bottom-color: var(--or);
    font-weight: 600;
}

.bloc-preavis {
    padding: var(--r4);
    margin: var(--r4) 0;
    border: 1px solid var(--bordure);
    border-radius: var(--arrondi);
}

.bloc-preavis legend {
    padding: 0 var(--r2);
    color: var(--or);
    font-size: .875rem;
}

/* ── Listes de rappels et de carnet ─────────────────────────────────── */

/* ── COURBE DE POIDS ──────────────────────────────────────────────────
   Un tracé SVG, sans bibliothèque : le site n'a pas d'étape de
   compilation, et une bibliothèque de graphiques pèserait plus que la
   page entière pour dessiner une ligne brisée.

   La hauteur est fixe et le viewBox s'étire en largeur : sur un
   téléphone tenu à la verticale, une courbe qui garderait ses
   proportions serait haute de trois centimètres et illisible. */
.courbe-poids { margin: 0; }

.courbe-poids__trace {
    display: block;
    width: 100%;
    height: 180px;
    overflow: visible;
}

/* « vector-effect » garde le trait à un pixel malgré l'étirement du
   viewBox : sans lui, la ligne s'épaissirait à l'horizontale et
   s'amincirait à la verticale, et le trait paraîtrait tordu. */
.courbe-poids__trace polyline {
    fill: none;
    stroke: var(--or);
    stroke-width: 2;
    stroke-linejoin: round;
    stroke-linecap: round;
    vector-effect: non-scaling-stroke;
}

/* Les points sont dessinés : une pesée est une mesure prise un jour
   donné, pas une valeur continue. Sans eux, on lirait la ligne comme un
   suivi quotidien. */
.courbe-poids__trace circle {
    fill: var(--fond-carte);
    stroke: var(--or);
    stroke-width: 2;
    vector-effect: non-scaling-stroke;
}

.courbe-poids__bornes,
.courbe-poids__dates {
    display: flex;
    justify-content: space-between;
    color: var(--texte-faible);
    font-size: .82rem;
}

.courbe-poids__bornes {
    /* Le minimum est en bas de la courbe, le maximum en haut : l'ordre
       visuel doit suivre, sinon la légende contredit le dessin. */
    flex-direction: row-reverse;
    margin-top: var(--r1);
}

.courbe-poids__dates { margin-top: 2px; }

.liste-sante { list-style: none; margin: 0; padding: 0; }

.ligne-sante {
    display: flex;
    flex-wrap: wrap;
    gap: var(--r3);
    align-items: center;
    justify-content: space-between;
    padding: var(--r4) 0;
    border-bottom: 1px solid var(--bordure);
}

.ligne-sante:last-child { border-bottom: none; }

/* Un rappel fait s'efface sans disparaître : « le vaccin a-t-il été fait
   en mars ? » se pose souvent. */
.ligne-sante--faite { opacity: .55; }

.ligne-sante__corps {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    flex: 1 1 14rem;
}

.ligne-sante__note {
    margin: var(--r2) 0 0;
    color: var(--texte-doux);
    font-size: .875rem;
}

/* ════════════════════════════════════════════════════════════════════
   DISCUSSION : CONVERSATIONS À GAUCHE, ÉCHANGE À DROITE
   ════════════════════════════════════════════════════════════════════ */

.discussion {
    display: grid;
    gap: var(--r4);
    align-items: start;
}

@media (min-width: 52rem) {
    /* Colonne de liste FIXE : dimensionnée sur son contenu, elle
       changerait de largeur selon les noms affichés. */
    .discussion { grid-template-columns: 17rem 1fr; }
}

.discussion__liste {
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-height: 32rem;
    overflow-y: auto;
    padding: var(--r2);
    background: var(--fond-carte);
    border: 1px solid var(--bordure);
    border-radius: var(--arrondi-large);
}

/* Sur mobile, ouvrir une conversation remplace la liste : deux colonnes
   de dix centimètres ne se lisent ni l'une ni l'autre. */
@media (max-width: 52rem) {
    .discussion__liste--repliee { display: none; }
}

.conversation {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--r3);
    border-radius: var(--arrondi);
    color: var(--texte-doux);
    text-decoration: none;
    transition: background .15s var(--sortie);
}

.conversation:hover { background: var(--fond-eleve); text-decoration: none; }

.conversation--active { background: var(--fond-eleve); }

.conversation--active strong,
.conversation--neuve strong { color: var(--or); }

.conversation__entete {
    display: flex;
    justify-content: space-between;
    gap: var(--r2);
    align-items: baseline;
}

.conversation__heure { font-size: .75rem; white-space: nowrap; }

.conversation__sujet { font-size: .8125rem; color: var(--or); }

.conversation__extrait {
    font-size: .8125rem;

    /* Une ligne, coupée : trois lignes d'extrait par conversation et la
       liste ne tient plus à l'écran. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.conversation .pastille { position: absolute; top: var(--r3); right: var(--r3); }

/* ── Le fil ─────────────────────────────────────────────────────────── */

.discussion__fil {
    display: flex;
    flex-direction: column;
    min-width: 0;
    background: var(--fond-carte);
    border: 1px solid var(--bordure);
    border-radius: var(--arrondi-large);
}

.discussion__entete {
    display: flex;
    align-items: center;
    gap: var(--r3);
    padding: var(--r3) var(--r4);
    border-bottom: 1px solid var(--bordure);
}

@media (min-width: 52rem) {
    /* Le bouton « Retour » ne sert que sur mobile, où la liste est
       masquée. */
    .discussion__retour { display: none; }
}

.discussion__messages {
    display: flex;
    flex-direction: column;
    gap: var(--r3);
    padding: var(--r4);
    min-height: 18rem;
    max-height: 28rem;
    overflow-y: auto;
}

.discussion__vide { border: none; margin: 0; }

.messages__jour {
    align-self: center;
    padding: 2px var(--r3);
    background: var(--fond);
    border-radius: 999px;
    color: var(--texte-doux);
    font-size: .75rem;
}

/* ── Les bulles ─────────────────────────────────────────────────────── */

.bulle {
    max-width: min(80%, 34rem);
    padding: var(--r3);
    background: var(--fond);
    border: 1px solid var(--bordure);
    border-radius: var(--arrondi);
    align-self: flex-start;
}

/* Ses propres messages à droite, en doré : c'est le repère qui permet de
   suivre un échange sans lire les noms. */
.bulle--moi {
    align-self: flex-end;
    background: rgba(var(--or-rvb), .12);
    border-color: rgba(var(--or-rvb), .3);
}

.bulle__auteur {
    display: block;
    margin-bottom: 2px;
    color: var(--or);
    font-size: .75rem;
    font-weight: 600;
}

.bulle__texte {
    margin: 0;
    font-size: .9375rem;
    overflow-wrap: anywhere;
}

.bulle__piece {
    display: inline-block;
    margin-top: var(--r2);
    font-size: .8125rem;
}

/* ── UNE IMAGE JOINTE SE REGARDE DANS LA BULLE ────────────────────────
   Neuf pièces sur dix sont la photo d'un animal trouvé, et il faut la
   voir pour répondre. Sous forme de lien, il fallait ouvrir un onglet,
   regarder, revenir. Le PDF garde son lien : il n'y a rien à montrer
   d'un PDF dans une bulle. (1.61.0) */
.bulle__piece--image {
    display: block;
    margin-top: var(--r2);
    line-height: 0;
}

/* La hauteur est bornée, pas la largeur : une photo de téléphone tenue
   à la verticale occuperait sinon tout le fil, et le message suivant
   disparaîtrait sous la ligne de flottaison. */
.bulle__piece--image img {
    max-width: 100%;
    max-height: 220px;
    border-radius: var(--rayon-2);

    /* « contain » et non « cover » : recadrer la photo d'un animal
       perdu couperait justement le détail qui permet de le reconnaître,
       une tache, un collier. */
    object-fit: contain;
}

/* ── LE FICHIER SE JOINT DEPUIS LA LIGNE DE SAISIE ────────────────────
   Le bouton et l'envoi restent sur la même ligne : un champ de fichier
   sur sa propre ligne repousserait « Envoyer » hors de vue sur un
   téléphone. */
.discussion__piece {
    display: flex;
    gap: var(--r2);
    align-items: center;
}

/* ── L'APERÇU DE LA PIÈCE EN COURS D'ENVOI ────────────────────────────
   Rien ne changeait à l'écran une fois la photo choisie : le champ de
   fichier natif est masqué au profit d'un bouton stylé. On ne savait pas
   si le choix avait pris, ni laquelle partirait. (1.65.0) */
.piece-apercu {
    display: none;
    gap: var(--r2);
    align-items: center;

    /* « flex: 1 0 100% » et non « width » : dans une rangée flex, une
       largeur de cent pour cent se laisse comprimer par les voisins.
       C'est la BASE qui force le passage à la ligne. */
    flex: 1 0 100%;

    margin-top: var(--r2);
    color: var(--texte-doux);
    font-size: .8125rem;
}

.piece-apercu--visible { display: flex; }

/* Petite : c'est un accusé de réception, pas une galerie. Assez grande
   pour reconnaître qu'on a choisi la bonne photo. */
.piece-apercu__image {
    width: 44px;
    height: 44px;
    border-radius: var(--rayon-1);
    object-fit: cover;
}

/* Les boutons restent VISIBLES pendant l'envoi, seulement inertes : les
   masquer ferait sauter la ligne de saisie sous le doigt, au moment
   précis où l'on vient d'appuyer. */
.est-desactive {
    opacity: .5;
    pointer-events: none;
}

.bulle__heure {
    display: block;
    margin-top: 2px;
    color: var(--texte-doux);
    font-size: .6875rem;
    text-align: right;
}

.discussion__saisie {
    display: flex;

    /* ── L'APERÇU DESCEND SOUS LA LIGNE ───────────────────────────────
       Sans « wrap », l'aperçu inséré à l'envoi devenait un élément de
       la rangée : il se glissait entre le bouton et le bord, écrasait
       la zone de texte et décalait tout. Il lui faut sa propre ligne.
       (1.69.0) */
    flex-wrap: wrap;

    gap: var(--r3);
    align-items: flex-end;
    padding: var(--r3) var(--r4) var(--r4);
    border-top: 1px solid var(--bordure);
}

.discussion__saisie textarea {
    /* Une base de 12rem plutôt que « auto » : pendant l'envoi, la zone
       de texte ne se réduit plus à un timbre-poste parce qu'un aperçu
       est apparu à côté. */
    flex: 1 1 12rem;
    min-width: 0;
    resize: vertical;
}

.discussion__saisie .bouton { flex: 0 0 auto; }

/* Le bouton « Nouveau message » coiffe la liste des conversations : il
   doit rester visible quand on fait défiler une longue liste. */
.discussion__nouveau {
    position: sticky;
    top: 0;
    z-index: 1;
    margin-bottom: var(--r2);
    text-align: center;
}

/* Les actions sous l'échange, séparées du champ de saisie : « Supprimer »
   à côté d'« Envoyer » se clique par erreur. */
.discussion__outils {
    display: flex;
    flex-wrap: wrap;
    gap: var(--r3);
    padding: var(--r3) var(--r4) var(--r4);
}

/* ── LA BARRE DE CLASSEMENT, AU-DESSUS DE LA CONVERSATION ─────────────
   Elle ne sert pas à la conversation, elle la classe : elle tient donc
   sur UNE ligne, et une bordure la sépare du fil. Sans cette bordure,
   les menus semblent flotter au-dessus des bulles et l'on cherche à
   quoi ils se rapportent. (1.68.0) */
.discussion__outils--barre {
    align-items: center;
    justify-content: space-between;
    padding: var(--r3) var(--r4);
    border-bottom: 1px solid var(--bordure);
}

/* Les deux groupes — classer d'un côté, archiver et supprimer de
   l'autre — restent chacun sur leur ligne interne, et se repoussent aux
   deux bords. */
.discussion__outils--barre > form,
.discussion__outils--barre > .rang {
    margin: 0;
    align-items: center;
}

/* ── LE LIBELLÉ PASSE À CÔTÉ DU MENU ──────────────────────────────────
   Empilé au-dessus, comme dans un formulaire, il doublait la hauteur
   d'une barre qui doit rester discrète : deux libellés, deux menus, et
   la conversation commençait cent pixels plus bas. */
.discussion__outils--barre .champ {
    display: flex;
    align-items: center;
    gap: var(--r2);

    /* Ne s'étire pas : « .rang > .champ » leur donne 12rem de base et
       les fait grandir, ce qui poussait « Enregistrer » hors de la
       ligne. Un menu d'état n'a pas besoin de toute la largeur. */
    flex: 0 0 auto;
    margin-bottom: 0;
}

.discussion__outils--barre .champ label {
    margin-bottom: 0;

    /* « État » et « Couleur » ne se coupent pas en deux lignes : ils
       feraient sauter la hauteur de la barre entière. */
    white-space: nowrap;
}

/* « width: 100% » vient de « .champ select » : ici le menu prend la
   largeur de son contenu, avec un plancher pour que « En attente de
   réponse » ne soit pas tronqué. */
.discussion__outils--barre .champ select {
    width: auto;
    min-width: 9rem;
}

/* Sur un écran étroit, les deux groupes passent l'un sous l'autre
   plutôt que de se serrer : un menu tronqué ne se lit pas. */
@media (max-width: 640px) {
    .discussion__outils--barre {
        align-items: stretch;
        gap: var(--r2);
    }
}



/* ════════════════════════════════════════════════════════════════════
   ANIMAUX PERDUS ET TROUVÉS
   ════════════════════════════════════════════════════════════════════ */


.perdus__grille {
    display: grid;
    gap: var(--r4);
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    margin-top: var(--r4);
}

.annonce {
    display: flex;
    flex-direction: column;

    /* Repère pour la pastille « À relire », qui se pose sur la photo.
       Sans lui, elle se placerait par rapport à la page et suivrait le
       défilement. */
    position: relative;

    overflow: hidden;
    background: var(--fond-carte);
    border: 1px solid var(--bordure);
    border-radius: var(--arrondi-large);
    color: inherit;
    text-decoration: none;
    transition: border-color .15s var(--sortie), transform .15s var(--sortie);
}

.annonce:hover {
    border-color: var(--or);
    text-decoration: none;
    transform: translateY(-2px);
}

.annonce__photo {
    /* « relative » était déclaré à part, plus bas : deux règles pour
       le même élément, dont la seconde ne portait qu'une ligne.
       (1.36.2) */
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 4 / 3;
    background: var(--fond);
    overflow: hidden;
}

.annonce__photo img {
    width: 100%;
    height: 100%;

    /* ── « contain », PAS « cover » ───────────────────────────────────

       « cover » remplit le cadre en rognant. Sur une photo de portrait,
       il coupait la tête ou les pattes — précisément ce qu'on regarde
       pour reconnaître un animal.

       « contain » montre l'image ENTIÈRE et laisse deux bandes. Le cadre
       a déjà un fond et centre son contenu : les bandes se lisent comme
       une marge, pas comme un défaut.

       Une grille un peu moins nette contre un animal reconnaissable :
       c'est le but de la page qui tranche. (1.108.0) */
    object-fit: contain;
}

.annonce__sans-photo { font-size: 2.5rem; opacity: .3; }

.annonce__corps {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--r3) var(--r4) var(--r4);
    min-width: 0;
}

.annonce__lieu {
    font-size: .8125rem;
    color: var(--or);

    /* Une ligne : une adresse longue casserait l'alignement de la
       grille. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.annonce-detail__photo {
    display: block;
    width: 100%;
    max-height: 26rem;
    object-fit: contain;
    border-radius: var(--arrondi);
    margin: var(--r4) 0;
    background: var(--fond);
}

.annonce-detail__faits {
    display: grid;
    gap: var(--r3);
    grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
    margin: var(--r4) 0;
}

.annonce-detail__faits dt {
    color: var(--texte-doux);
    font-size: .75rem;
    text-transform: uppercase;
    letter-spacing: .04em;
}

.annonce-detail__faits dd { margin: 0; }

.annonce-detail__texte { margin-top: var(--r4); }

.rappel-medaille { border-left: 3px solid var(--or); }

.carte--note {
    background: var(--fond);
    border-style: dashed;
}

.obligatoire { color: #f05a5a; }

.pagination {
    display: flex;
    flex-wrap: wrap;
    gap: var(--r2);
    justify-content: center;
    margin-top: var(--r5);
}

/* ── Carte et recherche des annonces ────────────────────────────────── */

.perdus__titre { margin-bottom: var(--r4); }

.perdus__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--r3);
    justify-content: center;
    margin-bottom: var(--r4);
}

/* Le bouton « trouvé » est vert, comme les marqueurs de la carte : les
   deux désignent la même chose, et une couleur différente obligerait à
   refaire le lien mentalement. */
.bouton--trouve {
    background: #7dd3a0;
    border-color: #7dd3a0;
    color: #0a0f0d;
    font-weight: 600;
}

.bouton--trouve:hover {
    background: #6bc190;
    border-color: #6bc190;
    color: #0a0f0d;
}

.carte-annonces {
    width: 100%;

    /* Une hauteur fixe : sans elle, le conteneur d'une carte Google reste
       à zéro et la carte ne s'affiche pas. */
    height: 26rem;
    border-radius: var(--arrondi);
    overflow: hidden;
}

@media (max-width: 40rem) {
    /*
     * ── LA CARTE A BESOIN DE HAUTEUR, PAS DE LARGEUR ──────────────────
     *
     * Sur un écran de 360, la carte fait 296 pixels de large. À 18rem de
     * haut elle était presque carrée : on voyait un département, et il
     * fallait faire glisser pour trouver quoi que ce soit.
     *
     * Une carte se lit en hauteur sur un téléphone — c'est le sens dans
     * lequel on tient l'appareil, et celui où l'écran a de la place.
     * (1.171.0)
     */
    .carte-annonces { height: 65vh; min-height: 20rem; max-height: 32rem; }
}

.carte-bulle { color: #1a1a1a; font-size: .875rem; line-height: 1.5; }

/* La photo de la bulle. Bornée en hauteur : une image de portrait
   pousserait le lien « voir la fiche » hors de la bulle, et l'on
   croirait qu'il n'y en a pas. (1.106.0) */
.carte-bulle__photo {
    /* ── LA TAILLE VIENT DES ATTRIBUTS, PAS D'ICI ──────────────────────

       Depuis la 1.171.0 le script calcule la largeur selon l'écran et
       la pose en attributs « width » et « height ». Cette règle fixait
       encore 208 sur 148 : une propriété CSS l'emporte sur un attribut,
       et l'adaptation n'avait aucun effet. Sur 360 pixels la vignette
       restait à sa taille de bureau.

       Sans taille ici, les attributs réservent la place avant le
       chargement, et « height: auto » garde leur proportion si
       « max-width » doit réduire l'image. (1.173.0) */
    height: auto;

    /* Entière, comme dans la liste : la bulle sert à reconnaître
       l'animal, pas à composer une image. */
    object-fit: contain;

    /* Un fond derrière les bandes. Sans lui, elles prendraient le blanc
       de la bulle et l'image paraîtrait flotter. */
    background: #f0f1f3;

    border-radius: 6px;
    margin-bottom: 6px;

    /* Une image plus large que la bulle la ferait déborder, et Google
       ajouterait une barre horizontale par-dessus la verticale. */
    max-width: 100%;
}

.carte-bulle a { display: inline-block; margin-top: 4px; }

.recherche-perdus { margin: var(--r4) 0; }


/* ── LA PASTILLE N'AVAIT NI FOND NI COULEUR ───────────────────────────

   Ses deux variantes, « --perdu » et « --trouve », étaient posées dans le
   gabarit mais n'ont jamais été écrites ici. Le mot flottait donc sur la
   photo, dans la couleur héritée de la carte : lisible sur un fond
   sombre, invisible sur un pelage clair ou un mur blanc.

   C'est la première chose qu'on cherche dans une grille qui mêle les
   deux, et c'était la moins visible. (1.106.0)

   ── UN FOND PLEIN, PAS UNE TEINTE ─────────────────────────────────────

   Une photo peut être de n'importe quelle couleur. Seul un fond opaque
   garantit le contraste ; une teinte translucide dépendrait de ce qu'il y
   a dessous, et l'on retomberait sur le même défaut un jour de photo
   claire. */
.annonce__marque {
    position: absolute;
    top: var(--r2);
    right: var(--r2);
    padding: 2px var(--r3);
    border-radius: 999px;
    font-size: .75rem;
    font-weight: 600;

    /* Texte sombre sur fond vif : c'est le sens le plus lisible, et le
       même que sur les boutons de la page. */
    color: #1a1a1a;

    /* Une ombre portée détache la pastille d'une photo chargée : sans
       elle, un pelage doré la ferait disparaître. */
    box-shadow: 0 1px 4px rgba(0, 0, 0, .35);
}

/* Les deux couleurs sont celles des boutons de la page : « Mon animal est
   perdu » en doré, « J'ai trouvé un animal » en vert. Le lien se fait
   sans qu'on ait à l'expliquer. */
.annonce__marque--perdu  { background: var(--or); }
.annonce__marque--trouve { background: var(--retrouve); color: #f6f7f9; }


.perdus__rappel { margin-top: var(--r5); }

/* La page de clôture : sobre et centrée, c'est une bonne nouvelle qui se
   lit en trois secondes. */
.annonce-close { text-align: center; }

.annonce-close__marque { font-size: 3rem; display: block; }

.annonce-close .rang { justify-content: center; margin-top: var(--r4); }

/* ── Liens légaux du pied de page ───────────────────────────────────── */

.pied__legal {
    display: flex;
    flex-wrap: wrap;
    gap: var(--r3) var(--r5);
    justify-content: center;
    margin-bottom: var(--r4);
}

.pied__legal a {
    color: var(--texte-doux);
    font-size: .875rem;
    text-decoration: none;
}

.pied__legal a:hover { color: var(--or); text-decoration: underline; }

/* ── Pages légales ──────────────────────────────────────────────────
   Un texte de loi se lit mal en pleine largeur : la ligne est bornée,
   et les titres respirent pour qu'on retrouve un article. */

.page-legale { line-height: 1.7; }

.page-legale h2 {
    margin: var(--r6) 0 var(--r3);
    padding-top: var(--r4);
    border-top: 1px solid var(--bordure);
    font-size: 1.25rem;
}

.page-legale h3 {
    margin: var(--r5) 0 var(--r2);
    color: var(--or);
    font-size: 1rem;
}

.page-legale h4 {
    margin: var(--r4) 0 var(--r2);
    font-size: .9375rem;
}

.page-legale p, .page-legale li { color: var(--texte-doux); }

.page-legale strong { color: var(--texte); }

.page-legale ul, .page-legale ol { padding-left: var(--r5); }

.page-legale li { margin-bottom: var(--r2); }

.page-legale hr {
    margin: var(--r6) 0;
    border: none;
    border-top: 1px solid var(--bordure);
}

/* Un tableau de politique de retour dépasse sur téléphone : il défile
   plutôt que de comprimer les colonnes jusqu'à l'illisible. */
.tableau-defilant {
    overflow-x: auto;
    margin: var(--r4) 0;
}

/* ── Le pied de page ─────────────────────────────────────────────────
   Seuls les liens étaient centrés, par leur « justify-content ». Les
   deux lignes en dessous restaient alignées à gauche : le bloc paraissait
   de travers, sans qu'on voie d'où venait le décalage. */

.pied {
    padding: var(--r6) 0 var(--r5);
    border-top: 1px solid var(--bordure);
    text-align: center;
}

/* Les paragraphes du pied gardent leur couleur douce mais suivent le
   centrage : sans cette règle, « text-align » posé sur le conteneur
   suffirait, mais une classe « faible » posée ailleurs pourrait le
   réaligner sans qu'on comprenne pourquoi. */
.pied p {
    margin: 0 0 var(--r2);
}

.pied p:last-child { margin-bottom: 0; }

/* ── Tableaux et images dans une page éditoriale ─────────────────────── */

.page-legale table {
    width: 100%;
    border-collapse: collapse;
    margin: var(--r4) 0;
}

.page-legale th,
.page-legale td {
    padding: var(--r3);
    border-bottom: 1px solid var(--bordure);
    text-align: left;
    vertical-align: top;
}

.page-legale th {
    color: var(--texte);
    font-weight: 600;
}

.page-legale img {
    max-width: 100%;

    /* « auto » avec les dimensions posées sur la balise : le navigateur
       réserve la place avant le chargement, et la page ne saute pas. */
    height: auto;
    border-radius: var(--arrondi);
    margin: var(--r4) 0;
}

/* Un tableau large déborde sur téléphone : il défile plutôt que de
   comprimer les colonnes jusqu'à l'illisible. */
@media (max-width: 40rem) {
    .page-legale table { display: block; overflow-x: auto; }
}

/* ── Réservé aux lecteurs d'écran ──────────────────────────────────
   Un équivalent en toutes lettres pour ce que l'œil comprend seul : une
   note en étoiles, un compteur dans un menu. « 3 » ne dit rien à qui ne
   voit pas le contexte, et c'est justement l'information qui compte.

   Dans base.css et non dans une feuille de section : elle est utilisée
   sur le site public ET dans l'administration, qui ne charge pas les
   mêmes feuilles. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ── Préférences de notification ───────────────────────────────────
   Chaque bloc est une carte, avec sa bascule en tête. La bascule est
   une vraie case à cocher habillée : un interrupteur en JavaScript ne
   s'enregistrerait pas sans script, et cette page se règle une fois
   pour toutes. */

.notifs__intro { margin: 0 0 var(--r4); }

.notifs__bloc { margin-bottom: var(--r4); }

/* La tête du bloc : la case, le titre et la phrase d'explication.
   « align-items: start » plutôt que « center » : sur deux lignes de
   texte, une case centrée flotte au milieu du paragraphe. */
.notifs__tete {
    display: flex;
    align-items: start;
    gap: var(--r3);
    cursor: pointer;
}

.notifs__tete input { margin-top: 3px; flex: 0 0 auto; }

.notifs__tete > span { display: flex; flex-direction: column; gap: 2px; }

.notifs__titre { margin: 0 0 var(--r3); }

/* Les explications de bas de bloc : plus discrètes que le texte
   courant, mais lisibles. Elles répondent à « pourquoi », pas à
   « comment », et se lisent une seule fois. */
.notifs__note {
    margin: var(--r3) 0 0;
    padding-top: var(--r3);
    border-top: 1px solid var(--bordure);
    color: var(--texte-doux);
    font-size: .875rem;
    line-height: 1.5;
}

/* Un service indisponible se dit, il ne se devine pas : sans cela, on
   coche une case et on attend une alerte qui n'arrivera jamais. */
.notifs__alerte {
    margin: var(--r3) 0 0;
    padding: var(--r3);
    background: rgba(251, 191, 36, .08);
    border-left: 3px solid var(--or);
    border-radius: var(--arrondi);
    font-size: .875rem;
}

.notifs__types { list-style: none; margin: 0; padding: 0; }

.notifs__type + .notifs__type {
    margin-top: var(--r2);
    padding-top: var(--r2);
    border-top: 1px solid var(--bordure);
}

.notifs__type label {
    display: flex;
    align-items: start;
    gap: var(--r3);
    cursor: pointer;
}

.notifs__type input { margin-top: 3px; flex: 0 0 auto; }

.notifs__type > label > span {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* Une case désactivée garde le curseur par défaut : le pointeur de main
   promettrait une action qui n'arrivera pas. */
.notifs__type input:disabled { cursor: default; }

.notifs__type input:disabled + span { opacity: .85; }

.notifs__toujours {
    align-self: start;
    padding: 1px var(--r2);
    background: rgba(251, 191, 36, .14);
    border-radius: 999px;
    color: var(--or);
    font-size: .75rem;
    font-weight: 600;
}

.notifs__aide { margin-top: var(--r5); }

.notifs__etapes {
    margin: 0;
    padding-left: var(--r5);
    display: flex;
    flex-direction: column;
    gap: var(--r3);
}

.notifs__etapes strong { display: block; }

/* ── Champs obligatoires et facultatifs ────────────────────────────
   L'astérisque est en couleur d'alerte pour se voir, mais reste une
   information secondaire : un formulaire couvert de rouge se lit comme
   une suite de reproches avant même d'être rempli. */
.requis {
    color: var(--perdu);
    font-weight: 700;
}

/* Le contraire de l'astérisque : « facultatif » écrit en toutes lettres
   dispense de deviner. Marquer les deux cas évite d'avoir à compter les
   astérisques pour savoir ce qui reste à remplir. */
.facultatif {
    color: var(--texte-faible);
    font-size: .8125rem;
    font-weight: 400;
}

/* ── UN TABLEAU QUI DEVIENT DES CARTES ─────────────────────────────
   Six colonnes réclament 750 pixels. Sur un téléphone de 390, il
   fallait défiler sur plus d'une largeur d'écran pour voir la dernière,
   celle qui porte la facture. Un défilement horizontal dans une page
   qui défile déjà verticalement ne se remarque pas : la colonne
   n'existait pas, pour qui ne pense pas à balayer de côté. (0.99.1)

   Chaque ligne devient une carte, chaque cellule une paire libellé et
   valeur. Le balisage reste un tableau : c'est bien un tableau de
   données, et le transformer en div le retirerait de la navigation par
   lecteur d'écran. */
@media (max-width: 48rem) {

    .tableau-cartes { overflow-x: visible; }

    .tableau-cartes .tableau,
    .tableau-cartes thead,
    .tableau-cartes tbody,
    .tableau-cartes tr,
    .tableau-cartes td {
        display: block;
        width: 100%;
    }

    /* L'en-tête est retiré de l'affichage mais reste dans le document :
       les libellés sont repris cellule par cellule ci-dessous, et un
       tableau sans en-tête serait mal annoncé par un lecteur d'écran. */
    .tableau-cartes thead {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
    }

    .tableau-cartes tr {
        margin-bottom: var(--r3);
        padding: var(--r3);
        background: var(--fond-carte);
        border: 1px solid var(--bordure);
        border-radius: var(--arrondi);
    }

    .tableau-cartes td {
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: var(--r3);
        padding: var(--r2) 0;
        border: 0;
    }

    .tableau-cartes td + td { border-top: 1px solid var(--bordure); }

    /* Le libellé vient de l'attribut, pas d'un texte dupliqué dans le
       gabarit : deux sources pour un même mot finissent par diverger. */
    .tableau-cartes td::before {
        content: attr(data-libelle);
        flex: 0 0 auto;
        color: var(--texte-faible);
        font-size: .8125rem;
        text-transform: uppercase;
        letter-spacing: .04em;
    }

    /* La valeur est poussée à droite et peut se replier : un numéro de
       suivi long déborderait sinon de sa carte. */
    .tableau-cartes td > * {
        text-align: right;
        min-width: 0;
        overflow-wrap: anywhere;
    }
}

/* ── Bandeau d'observation ─────────────────────────────────────────
   Affiché tant qu'un administrateur regarde le site à la place d'un
   client. En rouge, fixe, en haut : c'est le seul cas où l'on veut
   qu'un bandeau soit impossible à ignorer.

   Sans lui, on oublie qu'on y est, on écrit un message en croyant être
   soi-même, et rien ne distingue ces actions de celles du client. */
.observation {
    position: sticky;
    top: 0;
    z-index: 200;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: var(--r3);
    padding: var(--r3) var(--r4);
    background: var(--perdu);
    color: #fff;
    font-size: .9375rem;
    text-align: center;
}

.observation strong { text-decoration: underline; }

/* Le bouton de sortie est un aplat clair sur le rouge : il doit se voir
   avant le texte, parce que c'est ce qu'on cherche quand on réalise
   qu'on est encore dans le compte de quelqu'un. */
.observation button {
    padding: var(--r2) var(--r4);
    background: #fff;
    border: 0;
    border-radius: var(--arrondi);
    color: var(--perdu);
    font: 600 .875rem/1 inherit;
    cursor: pointer;
}

.observation button:hover { background: rgba(255, 255, 255, .88); }

.observation form { margin: 0; }

/* ── Page d'invitation ─────────────────────────────────────────────
   Ce qu'on obtient en acceptant, en liste : trois lignes se parcourent
   d'un regard là où un paragraphe se saute. */
.invitation__liste {
    margin: var(--r4) 0;
    padding-left: var(--r5);
    display: flex;
    flex-direction: column;
    gap: var(--r2);
    line-height: 1.5;
}

/* ── Détail réseau d'un scan ───────────────────────────────────────
   Adresse, nom d'hôte et pays. Conservés pour un cas précis : un animal
   volé, dont le voleur scanne la médaille. En chasse fixe, comme les
   autres valeurs techniques : une adresse se lit caractère par
   caractère quand on la recopie pour une plainte. */
.scan__reseau {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--r2) var(--r3);
    margin-top: var(--r2);
    font-size: .8125rem;
}

.scan__reseau .mono {
    font-family: var(--police-mono);

    /* Sélectionnable d'un double-clic : on la recopie pour un dépôt de
       plainte, et une adresse mal recopiée ne sert à rien. */
    user-select: all;
}

/* ── Infobulle d'un point de scan ──────────────────────────────────
   Elle s'affiche dans une fenêtre Google, dont le fond est blanc et ne
   se change pas : les couleurs sont donc fixes, et non prises au thème
   du site. Les y prendre donnerait du texte clair sur blanc. */
.bulle-scan {
    color: #17181c;
    font-size: .8125rem;
    line-height: 1.5;
    min-width: 13rem;
}

.bulle-scan strong { display: block; margin-bottom: 2px; }

.bulle-scan__ligne {
    display: flex;
    gap: var(--r2);
    margin-top: 2px;
}

/* L'étiquette a une largeur fixe : les valeurs s'alignent, et on
   parcourt la colonne de droite d'un regard au lieu de lire chaque
   ligne en entier. */
.bulle-scan__quoi {
    flex: 0 0 5rem;
    color: #63676f;
}

/* ── Effacement de l'historique des scans ──────────────────────────
   La case et le bouton sont empilés, pas côte à côte : sur une ligne,
   la case se lit après le bouton, donc trop tard. */
/* ── L'EFFACEMENT SE LIT AVANT DE SE CLIQUER ───────────────────────
   Aligné à droite, le bloc poussait sa phrase de confirmation en haut,
   à côté du titre : on lisait « J'ai compris que les 11 scans seront
   perdus » avant même de savoir de quoi il s'agissait.

   Aligné à gauche et posé sous la liste, il se lit dans l'ordre : le
   titre, les scans, puis ce qu'on peut en faire. (1.35.2) */
.effacer-scans {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--r3);

    /* Le trait le sépare de la liste : une action destructrice ne doit
       pas paraître faire partie de ce qu'elle détruit. */
    width: 100%;
    margin-top: var(--r5);
    padding-top: var(--r4);
    border-top: 1px solid var(--bordure);
}

.effacer-scans__case {
    display: flex;

    /* « center » plutôt que « baseline » : sur deux lignes de texte, la
       case se retrouvait alignée sur la PREMIÈRE, donc en haut du bloc.
       Centrée, elle reste en face du texte. */
    align-items: center;
    gap: var(--r3);
    max-width: 32rem;
    color: var(--texte-doux);
    font-size: .8125rem;
    line-height: 1.4;
    text-align: right;
    cursor: pointer;
}

/* ── Annonce en attente de relecture ───────────────────────────────
   Visible des seuls relecteurs. Sans marquage, on la croit publiée et
   on ne la valide jamais : elle reste invisible du public pendant que
   son auteur attend. */
.annonce--attente {
    border-color: var(--attention);
}

.annonce__attente {
    position: absolute;
    top: var(--r3);
    left: var(--r3);
    z-index: 2;
    padding: 2px var(--r3);
    background: var(--attention);
    border-radius: var(--arrondi);

    /* Texte sombre sur l'orangé : c'est la couleur du fond qui porte le
       message, le texte doit s'y lire sans effort. */
    color: #17181c;
    font-size: .75rem;
    font-weight: 600;
}

/* ── Menu de suggestions Google (Places) ───────────────────────────
   Il est créé par la bibliothèque, en dehors de notre balisage, et
   n'avait AUCUN style : blanc sur un site sombre, et surtout sans
   profondeur d'empilement déclarée.

   Google lui donne « z-index: 1000 » par défaut, mais il est posé sur
   « body » : à l'intérieur d'une carte qui crée son propre contexte
   d'empilement, il passe derrière. C'est ce qui le rendait invisible.
   (1.15.3) */
.pac-container {
    z-index: 300;
    background: var(--fond-carte);
    border: 1px solid var(--bordure);
    border-radius: var(--arrondi);
    box-shadow: 0 8px 24px rgba(0, 0, 0, .45);
    font-family: inherit;

    /* Le logo « powered by Google » est imposé par les conditions
       d'utilisation : on ne le cache pas, on l'accorde au thème. */
    color: var(--texte);
}

.pac-item {
    padding: var(--r2) var(--r3);
    border-top: 1px solid var(--bordure);
    color: var(--texte-doux);
    cursor: pointer;
    font-size: .9375rem;
}

.pac-item:first-child { border-top: 0; }

.pac-item:hover,
.pac-item-selected {
    background: rgba(var(--or-rvb), .12);
}

/* La partie déjà tapée, mise en gras par Google : elle doit ressortir,
   c'est ce qui permet de voir pourquoi la suggestion correspond. */
.pac-item-query,
.pac-matched {
    color: var(--texte);
}

.pac-icon { display: none; }

/* ── Photos d'une annonce ──────────────────────────────────────────
   Trois au plus. La première est plus grande : c'est celle qui montre
   l'animal en entier, les suivantes montrent un détail. */
.annonce-detail__photos {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: var(--r3);
    margin-bottom: var(--r5);
}

.annonce-detail__vignette {
    display: block;
    border-radius: var(--arrondi);
    overflow: hidden;
    transition: transform .15s var(--sortie);
}

.annonce-detail__vignette:hover,
.annonce-detail__vignette:focus-visible {
    outline: none;
    transform: scale(1.02);
}

.annonce-detail__vignette:focus-visible {
    box-shadow: 0 0 0 2px var(--fond), 0 0 0 4px var(--or);
}

/* ── Champs de photo du formulaire ─────────────────────────────────
   Les trois côte à côte sur écran large, empilés sur téléphone. */

.apercu {
    display: flex;
    align-items: center;
    gap: var(--r3);
    margin-top: var(--r3);
    font-size: .8125rem;
}

.apercu__image {
    width: 3.5rem;
    height: 3.5rem;
    object-fit: cover;
    border-radius: var(--arrondi);
    border: 1px solid var(--bordure);
}

/* Le nom se réduit avant le bouton : on peut se passer de lire le nom
   du fichier, pas du moyen de le retirer. */
.apercu__nom {
    flex: 1 1 auto;
    min-width: 0;
    color: var(--texte-faible);
    overflow-wrap: anywhere;
}

.apercu__retirer {
    flex: 0 0 auto;
    padding: 2px var(--r2);
    background: none;
    border: 1px solid var(--bordure);
    border-radius: var(--arrondi);
    color: var(--texte-doux);
    cursor: pointer;
    font: inherit;
    font-size: .75rem;
}

.apercu__retirer:hover { border-color: var(--perdu); color: var(--perdu); }

.apercu__erreur {
    margin-top: var(--r3);
    color: var(--perdu);
    font-size: .8125rem;
}

/* ── Photo agrandie ────────────────────────────────────────────────
   Ces règles vivaient dans « qr.css », qui n'est chargée que sur la
   fiche d'une médaille. La page d'une annonce s'en sert aussi depuis la
   1.16.0, et n'avait donc AUCUN style : l'agrandissement ne pouvait pas
   fonctionner, et le bloc caché restait dans le flux. (1.16.1)

   Elles sont ici plutôt que dupliquées : deux copies d'un même style
   divergent au premier ajustement. */
.qr-loupe {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 400;
    align-items: center;
    justify-content: center;
    padding: var(--r4);
    background: rgba(0, 0, 0, .92);
}

.qr-loupe:target { display: flex; }

/* Le fond entier ferme : sur un téléphone tenu d'une main, viser une
   croix de coin est le geste le plus difficile. */
.qr-loupe__fond {
    position: absolute;
    inset: 0;
    cursor: zoom-out;
}

.qr-loupe__image {
    position: relative;
    max-width: 100%;

    /* De la place sous l'image pour la mention de fermeture : à 100 %,
       elle passerait derrière sur un écran large. */
    max-height: 82vh;
    object-fit: contain;
    border-radius: var(--arrondi);
    box-shadow: 0 0 0 1px rgba(var(--or-rvb), .35);
}

.qr-loupe__aide {
    position: absolute;
    bottom: var(--r5);
    left: 0;
    right: 0;
    text-align: center;
    color: rgba(255, 255, 255, .72);
    font-size: .8125rem;

    /* Elle ne doit pas intercepter le toucher : c'est le fond qui ferme,
       et une bande morte en bas d'écran se remarquerait. */
    pointer-events: none;
}

/* ── Champ de photos, natif et visible ─────────────────────────────
   Trois versions ont essayé de le cacher derrière un bouton. Chacune a
   échoué autrement, et à chaque fois le sélecteur ne s'ouvrait pas.

   Il est donc dessiné par le navigateur. On l'accorde au thème sans
   toucher à son fonctionnement : pas de « display », pas de « position »,
   rien qui puisse l'empêcher de réagir au clic. (1.16.4) */
.champ-photos {
    display: block;
    width: 100%;
    margin-top: var(--r3);
    padding: var(--r3);
    background: var(--fond);
    border: 1px dashed var(--bordure);
    border-radius: var(--arrondi);
    color: var(--texte-doux);
    font: inherit;
    font-size: .875rem;
    cursor: pointer;
}

.champ-photos:hover { border-color: var(--or); }

.champ-photos:focus-visible {
    outline: 2px solid var(--or);
    outline-offset: 2px;
}

/* Le bouton interne du navigateur : accordé au thème, sans changer sa
   nature. Chaque navigateur le dessine à sa façon, et c'est ce qui le
   rend fiable. */
.champ-photos::file-selector-button {
    margin-right: var(--r3);
    padding: var(--r2) var(--r4);
    background: var(--or);
    border: 0;
    border-radius: var(--arrondi);
    color: #17181c;
    cursor: pointer;
    font: inherit;
    font-size: .875rem;
    font-weight: 600;
}

.champ-photos::file-selector-button:hover { opacity: .9; }

/* ── Annonce en attente de relecture ───────────────────────────────
   Vue par son auteur, qui vient de la déposer, et par les relecteurs.
   Rien ne le disait : on voyait sa propre annonce affichée normalement
   et on la croyait publiée. */
.annonce-relecture {
    border-left: 3px solid var(--attention);
    margin-bottom: var(--r4);
}

.annonce-relecture p { margin: 0; }

.annonce-relecture .champ__aide { margin-top: var(--r3); }

/* ── Avis sur un produit ───────────────────────────────────────────
   Ils se lisent au moment de décider. La note d'abord, les textes
   ensuite, le formulaire replié en bas : la plupart des visiteurs
   viennent lire, pas écrire. */
.avis {
    max-width: 46rem;
    margin: var(--r7) auto 0;
    padding: 0 var(--r4);
}

.avis__entete {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--r3);
    margin-bottom: var(--r5);
}

.avis__note { margin: 0; }

/* ── Les étoiles ───────────────────────────────────────────────────
   Grises par défaut, dorées quand elles comptent. La note est aussi
   écrite en clair : cinq caractères « étoile » lus par une synthèse
   vocale ne disent rien. */
.etoile { color: var(--bordure); font-size: 1.05em; letter-spacing: -.05em; }

.etoile--pleine { color: var(--or); }

.avis__liste { list-style: none; margin: 0; padding: 0; }

.avis__un {
    padding: var(--r4) 0;
    border-top: 1px solid var(--bordure);
}

.avis__un:first-child { border-top: 0; }

.avis__ligne {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--r2) var(--r3);
    margin-bottom: var(--r2);
}

.avis__date { margin-left: auto; font-size: .8125rem; }

/* ── Le badge d'achat vérifié ──────────────────────────────────────
   Il affirme quelque chose de précis : cette personne a payé ce
   produit. Vert comme les états réussis du site, pour être lu comme une
   confirmation et non comme une étiquette commerciale. */
.avis__verifie {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 1px var(--r2);
    border-radius: var(--arrondi);
    background: rgba(48, 164, 108, .12);
    color: var(--retrouve);
    font-size: .75rem;
    font-weight: 600;
    white-space: nowrap;
}

.avis__titre { margin: 0 0 var(--r2); font-weight: 600; }

.avis__texte { margin: 0; line-height: 1.6; }

.avis__photos {
    display: flex;
    flex-wrap: wrap;
    gap: var(--r2);
    margin-top: var(--r3);
}

.avis__vignette {
    display: block;
    width: 5rem;
    height: 5rem;
    border-radius: var(--arrondi);
    overflow: hidden;
}

.avis__vignette img { width: 100%; height: 100%; object-fit: cover; }

/* La réponse de l'atelier est en retrait : répondre à un avis négatif
   compte plus que l'avis pour celui qui lit, mais elle ne doit pas se
   faire passer pour un second avis. */
.avis__reponse {
    margin: var(--r3) 0 0 var(--r4);
    padding: var(--r3) var(--r4);
    border-left: 2px solid var(--or);
    background: var(--fond-carte);
    border-radius: 0 var(--arrondi) var(--arrondi) 0;
    font-size: .9375rem;
}

.avis__reponse p { margin: var(--r2) 0 0; }

.avis__depot { margin-top: var(--r5); }

/* ── Le « summary » qui porte la classe « bouton » ─────────────────
   « display: inline-block » écrasait le « inline-flex » de « .bouton »,
   et avec lui le centrage vertical : le texte se collait en haut des 44
   pixels de hauteur. (1.20.5)

   On garde donc l'affichage du bouton, et on retire seulement ce qui
   distingue un « summary » : son triangle. */
.avis__depot summary {
    cursor: pointer;

    /* Le marqueur se retire en deux fois : « list-style » pour les
       navigateurs récents, la pseudo-classe pour les anciens Safari et
       Chrome, qui ignorent le premier. */
    list-style: none;
}

.avis__depot summary::-webkit-details-marker { display: none; }

.avis__depot summary::marker { content: ''; }

.avis__formulaire { margin-top: var(--r4); }

/* ── Le choix de la note ───────────────────────────────────────────
   Une seule ligne d'étoiles. Cinq lignes empilées prenaient la moitié
   d'un écran de téléphone pour un seul choix.

   Les boutons sont écrits de 5 à 1 et retournés ici : « ~ » ne sait
   remonter que vers l'avant en CSS, et c'est ce qui permet d'allumer les
   étoiles précédentes sans une ligne de JavaScript. */
.etoiles-choix {
    display: inline-flex;
    flex-direction: row-reverse;
    justify-content: flex-end;
    margin-top: var(--r2);
}

.etoiles-choix input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
}

.etoiles-choix label {
    /* 44 pixels : la cible tactile la plus petite qu'un doigt vise sans
       se tromper d'étoile. */
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    color: var(--bordure);
    cursor: pointer;
    font-size: 1.75rem;
    line-height: 1;
    transition: color .1s var(--sortie);
}

/* L'étoile choisie et toutes celles qui la précèdent. En ordre inversé,
   « ~ » désigne bien les étoiles de gauche à l'écran. */
.etoiles-choix input:checked ~ label,
.etoiles-choix label:hover,
.etoiles-choix label:hover ~ label { color: var(--or); }

/* Le clavier doit voir où il se trouve : sans cela, on tabule dans un
   groupe d'étoiles sans savoir laquelle est visée. */
.etoiles-choix input:focus-visible + label {
    outline: 2px solid var(--or);
    outline-offset: 2px;
    border-radius: var(--arrondi);
}

/* ── Les avis sur un téléphone ─────────────────────────────────────
   Le bloc se réduit tout seul : ses largeurs sont relatives et ses
   rangées passent à la ligne. Deux points demandaient une correction. */
@media (max-width: 40rem) {

    .avis { padding: 0 var(--r3); }

    /* La date était poussée à droite par « margin-left: auto ». Sur une
       ligne qui passe à la ligne, elle se retrouvait seule, collée au
       bord, détachée de l'avis qu'elle date. */
    .avis__date {
        margin-left: 0;
        flex-basis: 100%;
    }

    /* ── LA CIBLE TACTILE FAIT 48 PIXELS ───────────────────────────
       C'est la hauteur au-dessous de laquelle un doigt vise mal, et
       celle que Google mesure. */
    .etoiles-choix label {
        min-width: 48px;
        min-height: 48px;
        font-size: 2rem;
    }

    /* La réponse de l'atelier garde son retrait, mais réduit : seize
       pixels sur trois cent cinquante coûtent une demi-ligne de texte à
       chaque ligne. */
    .avis__reponse {
        margin-left: var(--r2);
        padding: var(--r3);
    }
}

/* ── Le bandeau de consentement ────────────────────────────────────
   Écrit dans la page, pas ajouté par script : un bandeau posé après
   coup laisse une fraction de seconde pour cliquer derrière. */
.cookies {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 500;
    padding: var(--r3);
    display: flex;
    justify-content: center;
}

.cookies__boite {
    width: 100%;
    max-width: 44rem;
    max-height: 88vh;
    overflow-y: auto;
    padding: var(--r5);
    background: var(--fond-carte);
    border: 1px solid var(--or);
    border-radius: var(--arrondi-large);
    box-shadow: 0 12px 40px rgba(0, 0, 0, .6);
}

.cookies__marque {
    margin: 0 0 var(--r3);
    font-size: 1.125rem;
    font-weight: 700;
}

.cookies__marque span { color: var(--or); }

.cookies h2 { margin: 0 0 var(--r3); font-size: 1.125rem; }

.cookies p { margin: 0 0 var(--r3); font-size: .9375rem; line-height: 1.55; }

/* ── Les deux volets ───────────────────────────────────────────────
   Le détail n'apparaît qu'à la demande. Sans script : « Personnaliser »
   reste un lien qui fonctionne. */
.cookies__detail { display: none; }

.cookies__detail:target { display: block; }

/* Quand le détail est visible, le volet court s'efface. « :has » le
   permet sans une ligne de JavaScript. */
.cookies__boite:has(.cookies__detail:target) .cookies__court { display: none; }

/* ── Les boutons ───────────────────────────────────────────────────
   Refuser a la même taille et la même place qu'accepter : c'est la
   règle sur laquelle la CNIL a sanctionné le plus. */
.cookies__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--r3);
    margin-top: var(--r4);
}

.cookies__actions form { flex: 1 1 12rem; }

.cookies__actions .bouton { width: 100%; }

.cookies__lien {
    flex: 1 1 8rem;
    padding: var(--r3);
    color: var(--texte-doux);
    text-align: center;
    text-decoration: none;
}

.cookies__lien:hover { color: var(--or); }

/* ── Les catégories ────────────────────────────────────────────────── */
.cookies__cat {
    margin-bottom: var(--r3);
    padding: var(--r4);
    background: var(--fond);
    border: 1px solid var(--bordure);
    border-radius: var(--arrondi);
}

.cookies__cat-tete {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--r3);
}

.cookies__cat h3 { margin: 0; font-size: .9375rem; }

.cookies__toujours {
    color: var(--retrouve);
    font-size: .8125rem;
    font-weight: 600;
    white-space: nowrap;
}

.cookies__sous { margin: var(--r2) 0 0; color: var(--texte-doux); font-size: .875rem; }

.cookies__detail-texte {
    margin: var(--r2) 0 0;
    color: var(--texte-faible);
    font-size: .8125rem;
    line-height: 1.5;
}

.cookies__pied {
    margin: var(--r4) 0 0;
    color: var(--texte-faible);
    font-size: .75rem;
    text-align: center;
}

/* ── La bascule ────────────────────────────────────────────────────
   48 pixels de haut : la cible tactile au-dessous de laquelle un doigt
   vise mal. */
.bascule {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    cursor: pointer;
}

.bascule input { position: absolute; width: 1px; height: 1px; opacity: 0; }

.bascule__piste {
    position: relative;
    width: 46px;
    height: 26px;
    background: var(--fond-eleve);
    border: 1px solid var(--bordure);
    border-radius: 13px;
    transition: background .15s var(--sortie);
}

.bascule__piste::after {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 20px;
    height: 20px;
    background: #fff;
    border-radius: 50%;
    transition: transform .15s var(--sortie);
}

.bascule input:checked + .bascule__piste { background: var(--or); border-color: var(--or); }

.bascule input:checked + .bascule__piste::after { transform: translateX(20px); }

.bascule input:focus-visible + .bascule__piste {
    outline: 2px solid var(--or);
    outline-offset: 2px;
}

@media (max-width: 40rem) {
    .cookies { padding: 0; }

    .cookies__boite {
        max-width: none;
        max-height: 92vh;
        border-radius: var(--arrondi-large) var(--arrondi-large) 0 0;
        border-bottom: 0;
    }

    /* Empilés sur téléphone : côte à côte, chaque bouton devient trop
       étroit pour être visé sans hésiter. */
    .cookies__actions form { flex-basis: 100%; }
}

/* ── La section du réseau ──────────────────────────────────────────
   Un fond différent des sections voisines : c'est le seul endroit de
   l'accueil qui ne parle pas de produit, et la rupture visuelle le dit
   avant qu'on ait lu. */
/* ── LE RÉSEAU SUR LE FOND DE PAGE ─────────────────────────────────
   Il précède l'application depuis la 1.41.0, qui est sur fond relevé.
   Deux sections relevées qui se suivent se lisent comme un seul bloc :
   le réseau reprend le fond de page et devient la respiration entre
   les atouts et l'application.

   Les filets restent : ils disent où il commence et finit, ce que le
   fond ne dit plus. */
.reseau {
    padding: var(--r7) var(--r4);

    /* ── PLUS DE FILETS ICI ───────────────────────────────────────────

       Un filet en haut et un en bas encadraient la section. Depuis la
       1.98.0, un séparateur en losange la précède : le trait du haut
       passait juste dessous, et l'on voyait deux marques pour une seule
       frontière.

       Celui du BAS partait aussi, pour la même raison : la section qui
       suit est sur fond relevé, et le changement de fond sépare déjà.

       Ce style vit dans « base.css » alors que le reste de l'accueil est
       dans « accueil.css » : c'est pourquoi il a échappé aux deux
       nettoyages précédents. (1.101.0) */
}

.reseau__dedans {
    max-width: 60rem;
    margin: 0 auto;
    text-align: center;
}

.reseau__intro {
    max-width: 42rem;
    margin: var(--r4) auto 0;
    color: var(--texte-doux);
    line-height: 1.6;
}

/* ── Les chiffres ──────────────────────────────────────────────────
   « auto-fit » plutôt qu'un nombre fixe de colonnes : selon les seuils
   atteints, il y en a un, trois ou cinq. Une grille figée laisserait
   des trous. */
.reseau__chiffres {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: var(--r5);
    margin: var(--r6) 0;
}

.reseau__nombre {
    color: var(--or);
    font-size: 2.25rem;
    font-weight: 700;
    line-height: 1;

    /* Les chiffres s'alignent en colonne : sans cela, « 1 » et « 8 »
       n'ont pas la même largeur et les nombres dansent. */
    font-variant-numeric: tabular-nums;
}

.reseau__quoi {
    margin-top: var(--r2);
    font-weight: 600;
}

.reseau__sous {
    margin-top: var(--r1);
    color: var(--texte-faible);
    font-size: .8125rem;
}

/* ── Les deux portes ───────────────────────────────────────────────
   Celui qui a perdu son animal maintenant ne veut pas acheter : les
   deux liens mènent au réseau, pas à la boutique. */
.reseau__portes {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--r3);
    margin-top: var(--r5);
}

.reseau__gratuit {
    margin-top: var(--r4);
    color: var(--texte-faible);
    font-size: .875rem;
}

@media (max-width: 40rem) {
    .reseau { padding: var(--r6) var(--r4); }

    .reseau__chiffres {
        grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
        gap: var(--r4);
    }

    .reseau__nombre { font-size: 1.75rem; }

    /* Les boutons prennent toute la largeur : côte à côte sur un
       téléphone, chacun devient trop étroit pour être visé sans
       hésiter. */
    .reseau__portes .bouton { width: 100%; }
}

/* ═══════════════════════════════════════════════════════════════════
   HARMONISATION DES COMPOSANTS PARTAGÉS

   Quarante-trois écrans d'administration, bâtis sur six composants :
   carte, tableau, en-tête, champ, bouton, état.

   Les reprendre un par un aurait été le meilleur moyen d'en casser dix.
   On ajuste les composants ; les écrans suivent. (1.34.0)
   ═══════════════════════════════════════════════════════════════════ */

/* ── L'EN-TÊTE D'UNE CARTE ─────────────────────────────────────────
   Un titre, une phrase qui l'explique, une action à droite. C'est la
   forme que prend chaque bloc du tableau de bord, et les autres écrans
   la portaient sans le sous-titre. */
.carte > .entre { margin-bottom: var(--r4); }

.carte > .entre h2 { margin: 0; font-size: 1rem; }

.carte > .entre .champ__aide { margin: var(--r1) 0 0; }


/* ── LE RETRAIT DES CELLULES EST GARDÉ PARTOUT ─────────────────────
   J'avais retiré celui des colonnes de bord, en supposant qu'une carte
   apportait le sien. C'était faux : les listes de clients et de
   commandes posent leur carte avec « padding:0 » pour que le tableau
   aille d'un bord à l'autre.

   Résultat : plus aucun retrait, et le texte collé à la bordure.

   Une règle conditionnée à un attribut de style aurait marché, mais
   elle se serait cassée au premier gabarit écrit autrement. Le retrait
   reste donc partout : le gain esthétique était mineur, le risque non.
   (1.34.1) */

/* La dernière ligne n'a pas de trait : il doublait le bord de la carte
   juste en dessous. */
.tableau tbody tr:last-child td { border-bottom: 0; }

/* ── LES ÉTATS SONT DES PASTILLES ──────────────────────────────────
   Ils étaient carrés et pleins. Arrondis et pâles, ils se lisent sans
   attirer l'œil plus que la donnée qu'ils qualifient. */

/* ── LES NOMBRES S'ALIGNENT ────────────────────────────────────────
   Dans un tableau, « 1 » et « 8 » n'ont pas la même largeur avec une
   police proportionnelle : les colonnes de montants dansent d'une ligne
   à l'autre, et l'on ne peut plus les comparer d'un regard. */
.tableau td,
.compteur__valeur,
.stats-liste__nombre { font-variant-numeric: tabular-nums; }

/* ── LES COMPTEURS DES AUTRES ÉCRANS ───────────────────────────────
   Ceux du tableau de bord ont leur propre style. Ceux-ci le rejoignent :
   même arrondi, même bordure, même façon de poser le chiffre. */
/* ── LA GRILLE DES COMPTEURS ───────────────────────────────────────
   Elle n'existait que dans « admin.css », qui n'est pas chargé côté
   client : les compteurs du suivi de médaille s'empilaient l'un sous
   l'autre, sans cadre ni fond.

   Ma règle de la 1.34.0 posait un retrait et un arrondi sans fond ni
   bordure : elle rendait les cartes invisibles au lieu de les
   harmoniser. (1.35.2) */
.compteurs {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
    gap: var(--r3);
    margin-bottom: var(--r4);
}

.compteur {
    padding: var(--r4);
    background: var(--fond-carte);
    border: 1px solid var(--bordure);
    border-radius: var(--arrondi-large);
}

/* Un compteur qui mène quelque part se distingue au survol : les
   autres ne sont que des chiffres. */
a.compteur { color: inherit; text-decoration: none; }

a.compteur:hover { border-color: rgba(var(--or-rvb), .4); }

.compteur__valeur {
    font-size: 1.625rem;
    font-weight: 700;
    line-height: 1.1;
}

.compteur__libelle {
    margin-top: var(--r2);
    color: var(--texte-doux);
    font-size: .8125rem;
}

/* ── LES BOUTONS D'UNE MÊME LIGNE ONT LA MÊME HAUTEUR ──────────────
   Un lien stylé en bouton et un vrai bouton ne se calculaient pas
   pareil : dans une barre d'actions, ils se décalaient d'un pixel ou
   deux, ce qui se voit. */
.bouton--petit {
    min-height: 34px;
    padding: 0 var(--r4);
    font-size: .8125rem;
}

/* ── La rupture de stock ───────────────────────────────────────────
   Le bouton est retiré, pas grisé : un bouton grisé sans explication
   laisse chercher, et reste cliquable au clavier dans certains
   navigateurs. (1.35.0) */
.rupture {
    margin: 0;
    padding: var(--r4);
    background: rgba(229, 72, 77, .1);
    border: 1px solid rgba(229, 72, 77, .3);
    border-radius: var(--arrondi);
    color: #ff8a8d;
    font-weight: 600;
    text-align: center;
}

/* « Sur commande » se dit AVANT de payer, sinon le client attend une
   expédition qui ne vient pas. */
.sur-commande {
    margin: 0 0 var(--r3);
    padding: var(--r3) var(--r4);
    background: rgba(var(--or-rvb), .1);
    border: 1px solid rgba(var(--or-rvb), .3);
    border-radius: var(--arrondi);
    color: var(--or);
    font-size: .875rem;
}

/* ── LE TEXTE DU MANDAT ───────────────────────────────────────────────

   Lisible, mais visiblement contractuel : le fond relevé le détache du
   reste de la page, et l'on comprend qu'on lit un engagement.

   Borné en hauteur, car un contrat qui pousse le bouton de signature
   hors de l'écran ne se signe pas : on abandonne avant d'y arriver.
   (1.127.0) */
.mandat {
    padding: var(--r4);
    margin: var(--r4) 0;
    background: var(--fond-eleve);
    border-radius: var(--arrondi);
    font-size: .875rem;
    line-height: 1.7;
    max-height: 22rem;
    overflow-y: auto;
}

.mandat p { margin: 0 0 var(--r3); }

/* ── LES CANAUX D'UNE NOTIFICATION ────────────────────────────────────

   Deux marques côte à côte, sous le réglage qu'elles concernent. Elles
   disent ce qui MARCHE aujourd'hui : l'application demande un projet
   FCM, le navigateur des clés VAPID.

   L'écart est celui des cases voisines : plus serré, les deux marques se
   liraient comme une seule. (1.155.0) */
.canaux {
    display: flex;
    flex-wrap: wrap;
    gap: var(--r2);
    margin-top: var(--r3);
}


/* ════════════════════════════════════════════════════════════════════
   LE MENU MOBILE

   ── UNE CASE À COCHER, PAS UN SCRIPT ──────────────────────────────────

   Le panneau s'ouvre sans JavaScript : il fonctionne même si un script
   échoue, et il répond avant que la page ait fini de charger.

   C'est la seule construction qui tienne cette promesse en CSS seul.
   (1.164.0)
   ════════════════════════════════════════════════════════════════════ */

/*
 * ── L'ORDRE COMPTE PLUS QUE L'INTENTION ───────────────────────────────
 *
 * Ces deux lignes vivaient à la FIN du fichier, après le bloc mobile qui
 * pose « display: flex ». À spécificité égale, la dernière l'emporte :
 * le bouton était masqué à TOUTES les tailles, et le menu n'existait
 * pour personne.
 *
 * C'est exactement la faute de « boutique.css » corrigée en 1.168.0 — je
 * l'avais commise dans le même fichier, deux versions plus tôt, sans la
 * voir.
 *
 * Les deux règles sont maintenant déclarées AVANT le bloc mobile, où
 * elles servent de valeur par défaut. (1.170.0)
 */

.menu-bouton {
    flex-direction: column;
    justify-content: center;
    gap: 5px;

    /* Quarante-quatre pixels : la taille d'un doigt. En dessous, on
       touche à côté, et l'on croit que le bouton ne marche pas. */
    width: 44px;
    height: 44px;

    cursor: pointer;
    padding: 0 10px;
}

.menu-bouton__trait {
    display: block;
    height: 2px;
    background: var(--or);
    border-radius: 2px;
    transition: transform .2s var(--sortie), opacity .2s var(--sortie);
}

/*
 * Ouvert, les trois traits deviennent une croix : on sait qu'un second
 * appui referme, sans avoir à l'essayer.
 */
.menu-bascule:checked ~ .menu-bouton .menu-bouton__trait:nth-child(1) {
    transform: translateY(7px) rotate(45deg);
}

.menu-bascule:checked ~ .menu-bouton .menu-bouton__trait:nth-child(2) {
    opacity: 0;
}

.menu-bascule:checked ~ .menu-bouton .menu-bouton__trait:nth-child(3) {
    transform: translateY(-7px) rotate(-45deg);
}

.menu-mobile {
    /*
     * ── LE PANNEAU EST UN ENFANT DE LA GRILLE ─────────────────────────
     *
     * Il doit l'être : le sélecteur « ~ » ne franchit pas un parent, et
     * la case à cocher vit dans cette grille. Placé après « </div> », il
     * ne s'ouvrait JAMAIS — la croix s'affichait, et rien ne venait.
     *
     * Étant dans une grille, il lui faut occuper toute la largeur : sans
     * cela il se glisserait dans une colonne, à côté du logo.
     * (1.172.0)
     */
    grid-column: 1 / -1;

    padding: var(--r4) 0 var(--r5);
    border-top: 1px solid var(--bordure);
}

.menu-mobile__liste,
.menu-mobile__sous {
    list-style: none;
    margin: 0;
    padding: 0;
}

.menu-mobile__liste > li > a {
    display: block;
    padding: var(--r3) var(--r4);
    color: var(--texte);
    text-decoration: none;
    font-weight: 600;
}

/* Les sous-entrées sont dépliées : sur un téléphone, un sous-menu à
   ouvrir demande un geste de plus pour une liste de trois lignes. */
.menu-mobile__sous > li > a {
    display: block;
    padding: var(--r2) var(--r4) var(--r2) var(--r6);
    color: var(--texte-doux);
    text-decoration: none;
    font-size: .9375rem;
}

.menu-mobile__liste a:hover,
.menu-mobile__sous a:hover { color: var(--or); }

.menu-mobile__compte {
    display: flex;
    flex-direction: column;
    gap: var(--r2);
    margin-top: var(--r4);
    padding: var(--r4) var(--r4) 0;
    border-top: 1px solid var(--bordure);
}

/* ════════════════════════════════════════════════════════════════════
   LES CARTES SUR TÉLÉPHONE

   ── VINGT-QUATRE PIXELS DE CHAQUE CÔTÉ ────────────────────────────────

   Sur un écran de 1280, une carte de produit fait 400 pixels de large :
   24 de marge intérieure, c'est six pour cent. Sur un téléphone de 360,
   la carte en fait 328, et les mêmes 24 pixels en mangent quinze.

   L'image rétrécit d'autant, et c'est elle qu'on regarde : personne
   n'achète une médaille sur son libellé.

   Aucune règle mobile ne touchait « .carte » — les deux qui semblaient le
   faire visent « .carte-scans » et « .carte-annonces », des noms
   voisins. (1.166.0)
   ════════════════════════════════════════════════════════════════════ */

@media (max-width: 40rem) {
    .carte { padding: var(--r4); }

    /*
     * ── L'IMAGE VA D'UN BORD À L'AUTRE ────────────────────────────────
     *
     * La marge intérieure autour d'une photo ne sert à rien, alors
     * qu'elle coûte la moitié de ce qu'on est venu voir. Personne
     * n'achète une médaille sur son libellé.
     *
     * Les classes sont « __visuel », « __nom » et « __prix ». J'avais
     * écrit « img », « h3 » et « .prix » — des noms plausibles pris dans
     * ma tête, pas dans le gabarit. La règle n'aurait rien touché, sans
     * la moindre erreur.
     */
    .produit-carte { padding: 0 0 var(--r4); }

    .produit-carte__visuel {
        border-radius: var(--arrondi-large) var(--arrondi-large) 0 0;
        overflow: hidden;
    }

    .produit-carte__nom,
    .produit-carte__prix { padding: 0 var(--r4); }
}

/* ── ACTIONS SUR UNE COMMANDE DU COMPTE (1.192.0) ──────────────────────

   Régler et Annuler étaient collés : l'un est un lien, l'autre un
   formulaire (bloc), sans conteneur pour les aligner. On les met côte à
   côte, espacés, sous le libellé d'état. Le formulaire perd sa marge de
   bloc pour ne pas décaler son bouton. */
.commande__etat-libelle { display: block; }

.commande__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--r2, .5rem);
    margin-top: var(--r2, .5rem);
}

.commande__actions form { margin: 0; }

/* ── ESPACE PRÊTEUR (1.205.0) ──────────────────────────────────────────

   Le suivi que voit le prêteur : résumé chiffré et barre de progression. */
.pret-resume { margin: var(--r4, 16px) 0; }
.pret-resume__ligne {
    display: flex; justify-content: space-between; align-items: baseline;
    padding: var(--r2, 8px) 0;
    border-bottom: 1px solid var(--bordure, #eee);
}
.pret-resume__ligne dt { color: var(--texte-doux, #666); margin: 0; }
.pret-resume__ligne dd { margin: 0; }
.pret-resume__ligne--fort dt,
.pret-resume__ligne--fort dd { font-size: 1.1rem; }
.pret-resume__gain { color: var(--trouve, #2e7d32); }

.pret-barre {
    height: 14px; border-radius: 999px;
    background: var(--bordure, #eee); overflow: hidden;
    margin: var(--r3, 12px) 0 var(--r2, 8px);
}
.pret-barre__jauge {
    display: block; height: 100%;
    background: var(--trouve, #2e7d32);
    transition: width .4s ease;
}

/* ── PAGE DEVENIR AMBASSADEUR (1.207.0) ────────────────────────────────

   Une vitrine claire : un hero, des blocs aérés, deux appels à l'action. */
.ambassadeur__hero {
    text-align: center;
    padding: var(--r5, 24px) 0 var(--r4, 16px);
}
.ambassadeur__accroche {
    font-size: 1.2rem;
    color: var(--texte-doux, #666);
    max-width: 34rem;
    margin: var(--r3, 12px) auto var(--r4, 16px);
}
.ambassadeur__cta { display: inline-block; margin-top: var(--r2, 8px); }

.ambassadeur__bloc,
.ambassadeur__final {
    padding: var(--r4, 16px) 0;
    border-top: 1px solid var(--bordure, #eee);
}
.ambassadeur__final { text-align: center; }

.ambassadeur__avantages,
.ambassadeur__etapes {
    padding-left: var(--r4, 16px);
    line-height: 1.7;
}
.ambassadeur__avantages li,
.ambassadeur__etapes li { margin-bottom: var(--r2, 8px); }

/* ── INTRODUCTIONS SEO (1.209.0) ───────────────────────────────────────

   Un paragraphe de présentation en haut des pages de liste, pour donner
   du contexte au lecteur et du contenu aux moteurs. Discret, lisible. */
.catalogue__intro,
.perdus__intro {
    max-width: 46rem;
    color: var(--texte-doux, #555);
    line-height: 1.6;
    margin: 0 0 var(--r4, 16px);
}
