/* ════════════════════════════════════════════════════════════════════
   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;
    }
}

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;
}

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. */
.bouton--danger {
    background: transparent;
    border-color: #43242a;
    color: #ef8f93;
}

/*
 * 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. */
.champ input,
.champ textarea,
.champ select { width: 100%; }

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
   ════════════════════════════════════════════════════════════════════ */

.etat {
    display: inline-flex;
    align-items: center;
    gap: var(--r1);
    padding: 3px var(--r2);
    border-radius: 999px;
    font-size: .75rem;
    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); }

/* ════════════════════════════════════════════════════════════════════
   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;
}

.tableau th {
    padding: var(--r3) var(--r4);
    text-align: left;
    font-size: .8125rem;
    font-weight: 600;
    color: var(--texte-doux);
    text-transform: uppercase;
    letter-spacing: .04em;
    border-bottom: 1px solid var(--bordure);
    white-space: nowrap;
}

.tableau td {
    padding: var(--r4);
    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__grave {
    font-family: var(--police-mono);
    font-size: .8125rem;
    color: var(--or);
}

.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; }

.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); }

.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.
 */
@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; }

    .entete__compte { display: none; }
}

@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 ──────────────────────────────────────────────────── */

.compte__entete {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--r4);
    flex-wrap: wrap;
    margin-bottom: var(--r4);
}

.compte__entete h1 { margin: 0; }

.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. */
.fil--nouveau {
    padding-left: var(--r4);
    border-left: 2px solid var(--or);
}

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

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

/* ════════════════════════════════════════════════════════════════════
   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 ─────────────────────────────────── */

.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;
}

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

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

.discussion__saisie textarea {
    flex: 1 1 auto;
    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);
}

.bouton--danger {
    border-color: rgba(240, 90, 90, .4);
    color: #f05a5a;
}

.bouton--danger:hover {
    background: rgba(240, 90, 90, .12);
    border-color: #f05a5a;

    /* La couleur est redite : un fond qui change sans couleur explicite
       hérite de celle du bouton ordinaire, et le texte disparaît. */
    color: #f05a5a;
}

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

.perdus__entete {
    display: flex;
    flex-wrap: wrap;
    gap: var(--r4);
    align-items: center;
    justify-content: space-between;
}

.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 {
    display: flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 4 / 3;
    background: var(--fond);
    overflow: hidden;
}

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

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

.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) {
    .carte-annonces { height: 18rem; }
}

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

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

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

.annonce__photo { position: relative; }

.annonce__marque {
    position: absolute;
    top: var(--r2);
    right: var(--r2);
    padding: 2px var(--r3);
    border-radius: 999px;
    font-size: .75rem;
    font-weight: 600;
}

.annonce__marque--perdu  { background: var(--or); color: #0a0f0d; }
.annonce__marque--trouve { background: #7dd3a0; color: #0a0f0d; }

.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. */
.effacer-scans {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--r2);
}

.effacer-scans__case {
    display: flex;
    align-items: baseline;
    gap: var(--r2);
    max-width: 22rem;
    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. */
.photos-champs {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: var(--r4);
    margin-top: var(--r3);
}

.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. */
.reseau {
    padding: var(--r7) var(--r4);
    background: var(--fond-eleve);
    border-top: 1px solid var(--bordure);
    border-bottom: 1px solid var(--bordure);
}

.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%; }
}
