/* Feuille unique, sans theme ni changement saisonnier.
 *
 *
 * PALETTE
 *
 * Toutes les couleurs sont declarees une seule fois, dans :root, et reprise
 * par les variables. Avant, le bleu #337ab7 etait ecrit a la main dans une
 * dizaine de regles ; changer de teinte obligait a les relire toutes, et le
 * forgot est arrive — le bouton "generer un mot de passe" est reste bleu
 * longtemps apres que le reste du site eut change.
 *
 * Une variable CSS ne se herite pas dans le DOM comme une propriete : elle se
 * resout au moment de la cascade. Un navigateur qui ne les connait pas ignore
 * la declaration et retombe sur ce qui suit, ce qui est ici toujours une
 * valeur en dur : la feuille reste lisible meme sans elles.
 */

:root {
    /* Couleurs de marque
     *
     * --violet, --menthe et --texte-doux ne sont pas des teintes au gout :
     * elles sont le plus Clair qui reste lisible sur le fond le plus sombre
     * ou elles servent. Rapport calcule au dixieme (voir NOTES.md, piege 17).
     * Lesprendre plus vives donnerait un site plus gai et un nom latin
     * illisible. */
    --violet: #5b3fe0;
    --violet-clair: #a78bfa;
    --corail: #ff6b6b;
    --menthe: #0d9488;
    --jaune-favori: #fff3b0;

    /* Fonds et surfaces */
    --fond: #f4f1ff;
    --fond-chaud: #fdf4ec;
    --surface: #ffffff;
    --surface-douce: #faf8ff;

    /* Encre */
    --encre: #241b3a;
    --texte: #4a4360;
    --texte-doux: #726b8a;
    /* 3:1 sur le fond le plus defavorable ou elle sert. Sans cela les champs
       de saisie n'ont pas de contour visible : 1,23:1, c'est-a-dire une
       ligne qui ne se voit pas. */
    --bordure: #938ea1;

    /* Coins */
    --r-s: 10px;
    --r-m: 16px;
    --r-l: 24px;
    --r-pilule: 999px;

    /* Ombres : deux couches, une courte et une longue. Une seule ombre lange
       donne un effet plat qui colle a la page ; les deux donnent de la
       hauteur. */
    --ombre: 0 1px 2px rgba(36, 27, 58, 0.06), 0 8px 22px rgba(36, 27, 58, 0.07);
    --ombre-forte: 0 4px 10px rgba(36, 27, 58, 0.10), 0 18px 40px rgba(36, 27, 58, 0.14);
    --ombre-violet: 0 6px 18px rgba(108, 77, 246, 0.32);
}

body {
    /* system-ui d'abord : c'est la police de l'interface du systeme, donc
       deja optimisee pour l'ecran, et ca evite tout chargement externe. */
    font-family: system-ui, -apple-system, "Segoe UI Variable Text",
        "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    font-size: 16px;
    line-height: 1.55;
    color: var(--texte);
    /* Deux teintes de fond, en diagonale. Un aplat uniforme donne au site
       l'air d'un formulaire ; la leger degrade donne de la profondeur sans
       image ni ressource. */
    background: linear-gradient(160deg, var(--fond) 0%, var(--fond-chaud) 100%);
    background-attachment: fixed;
    margin: 0;
    -webkit-font-smoothing: antialiased;
}

/* Largeur du contenu.
 *
 * 560 px etait choisi pour que le formulaire tienne dans une colonne de
 * lecture. C'est trop etroit pour un ordinateur : le site y flottait au
 * centre d'un grand vide, alors qu'il y a de la place.
 *
 * 1100 px sur grand ecran, et la largeur de la fenetre en dessous. Les
 * FORMULAIRES, eux, restent plafonnes : un champ de saisie de 1100 px est
 * illisible, la ligne part dans les deux sens. Les deux plafonds sont donc
 * distincts, pour des raisons opposees.
 */
.page {
    max-width: 1100px;
    margin: 0 auto;
    padding: 28px 20px 40px;
}

/* Les champs s'arretent a mi-parcours. */
.corps form,
.corps .bouton-large {
    max-width: 460px;
}

/* Bandeau du titre. Un degrade violet vers rose : c'est le seul endroit ou la
   couleur sature, tout le reste reste clair. */
.bandeau {
    background: linear-gradient(120deg, var(--violet) 0%, #8b5cf6 55%, var(--corail) 145%);
    color: #fff;
    padding: 22px 26px;
    /* Tous les coins arrondis, et non seulement ceux du haut : le bandeau et
       le corps sont deux blocsposes l'un sur l'autre, chacun avec sa propre
       ombre. Un seul assemblage decoins est aujourd'hui ce qui fait tenir
       l'ensemble. */
    border-radius: var(--r-l);
    box-shadow: var(--ombre-violet);
}

.bandeau h1 {
    margin: 0;
    font-size: 26px;
    font-weight: 800;
    /* Un titre trop serre se colle aux lettres ; un peu d'espace entre elles
       le rend plus lourd sans changer sa taille. */
    letter-spacing: -0.02em;
    line-height: 1.2;
}

.bandeau { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.bandeau-droite {
    display: flex; align-items: center; gap: 12px; margin-left: auto;
}
.bienvenue {
    color: rgba(255,255,255,0.95); font-weight: 600; font-size: 15px;
}
.bandeau-deco {
    display: inline-flex; color: #fff; text-decoration: none; transition: opacity .15s ease;
}
.bandeau-deco:hover { opacity: .85; }

.corps {
    background-color: var(--surface);
    border-radius: var(--r-l);
    box-shadow: var(--ombre);
    padding: 26px;
    margin-top: 18px;
}

/* Menu, entre le bandeau et le corps. */
.menu {
    background: linear-gradient(120deg, #7959f5, #8455ed);
    border-radius: var(--r-m);
    box-shadow: var(--ombre);
    padding: 8px;
    margin-top: 14px;
}

.menu-liste {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.menu li {
    flex: 1 1 auto;
    text-align: center;
}

/* La deconnexion est a droite des autres : c'est une sortie, pas une
   navigation. */
.menu li.menu-fin {
    flex: 0 0 auto;
}

.menu a {
    display: block;
    padding: 10px 16px;
    color: #fff;
    text-decoration: none;
    font-size: 15px;
    font-weight: 600;
    /* Pilule plutot que rectangle : c'est ce qui donne au menu son aspect
       de nuage d'onglets plutot que de barre d'outils. */
    border-radius: var(--r-pilule);
    transition: background-color 0.15s ease, transform 0.15s ease;
}

.menu a:hover {
    background-color: rgba(255, 255, 255, 0.18);
}

/* Page courante, sinon rien ne dit ou l'on est. Blanc sur violet : le fond
   seul suffirait deja, le texte gras en plus porte la distinction jusqu'a
   la fin de la ligne, quand la couleur s'est estompée. */
.menu a.actif,
.menu li.actif a {
    background-color: #fff;
    color: var(--violet);
    font-weight: 700;
}

/* Bouton hamburger : masque sur grand ecran, visible seul sous 640 px.
 *
 * `.burger-texte` N'A PAS DE REGLE, ET C'EST VOULU
 *
 * Le bouton montre les trois barres ET le mot « Menu », côte à côte : c'est ce
 * que produit `.burger` en `display: flex` avec son `gap: 10px`. A cote de lui,
 * `.deconnexion` a fait l'inverse — le texte a disparu au profit du dessin, il
 * reste dans `aria-label`. Les deux choix sont deliberes et differents, parce
 * qu'ils repondent a deux questions differentes : « ou est le menu » et « qu'est
 * que je cherche ».
 *
 * La classe sert de point d'accroche, pas de style : un nettoyage automatique
 * qui laSUPPRIMERait ne casserait rien, et un qui lui ajouterait une regle
 * pourrait casser ce qui est voulu ici. Ce commentaire est la seule chose a
 * changer si un jour la question se repose. */
.burger {
    display: none;
    align-items: center;
    gap: 10px;
    width: 100%;
    margin: 0;
    padding: 10px 12px;
    font-size: 15px;
    font-weight: 600;
    color: #fff;
    background: none;
    border: none;
    border-radius: var(--r-s);
}

.burger:hover {
    background-color: rgba(255, 255, 255, 0.18);
}

.burger-barres {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    width: 20px;
    height: 14px;
}

.burger-barres i {
    display: block;
    height: 2px;
    border-radius: 1px;
    background-color: #fff;
}

/* Icone de deconnexion : le texte a disparu au profit du dessin, mais il
   reste dans aria-label. Une icone seule ne dit rien a un lecteur d'ecran,
   ni a un visiteur qui ne sait pas la lire.

 * `.icone-menu` est la meme regle pour l'engrenage de l'administration et la
 * maison du retour : trois icones de 20 px dans la meme barre se reglent
 * ensemble, sinon la deuxieme des trois finit avec un contour different des
 * deux autres. `fill: none` et le `viewBox` rempli par le dessin, pas par la
 * feuille : c'est ce qui permet a ces icones d'etre du HTML. */
.deconnexion svg,
.icone-menu svg {
    display: block;
    width: 20px;
    height: 20px;
    margin: 0 auto;
    fill: none;
    stroke: #fff;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* LE MENU DE L'ADMINISTRATION NE S'ETIRE PAS
 *
 * Sur le site, les entrees s'etirent pour que la deconnexion, a leur droite,
 * reste a droite de la barre. Avec une seule page d'administration, cet
 * etirement ne voudrait rien dire : « Editions » occuperait toute la largeur et
 * les deux icones seraient rejetees au bord de l'ecran, tres loin de leur
 * voisine. Ici les entrees gardent leur largeur, et la premiere icone part a
 * droite toute seule.
 *
 * La portee est `.menu-admin`, donc le menu public reste exactement ce qu'il
 * etait, sur telephone compris. */
.menu-admin .menu-liste li {
    flex: 0 0 auto;
    text-align: left;
}

.menu-admin .menu-retour {
    margin-left: auto;
}

/* TELEPHONE : le menu se replie derriere un bouton.
 *
 * La liste n'est pas retiree du DOM, seulement repliee. C'est ce qui permet
 * au bouton de la deployer : le CSS cache la liste, aria-expanded la montre.
 * Sans JavaScript, le menu reste dans la page, en pile, au lieu de disparaitre.
 */
@media (max-width: 640px) {
    .burger {
        display: flex;
    }

    .menu-liste {
        display: none;
        flex-direction: column;
        flex-wrap: nowrap;
    }

    /* Le bouton porte l'etat : ouvert, la liste apparait. */
    .burger[aria-expanded="true"] + .menu-liste {
        display: flex;
    }

    .menu li {
        flex: none;
        text-align: left;
    }

    .menu a {
        border-radius: var(--r-s);
    }

    .deconnexion svg,
    .icone-menu svg {
        margin: 0;
    }
}

/* Fiches cartes : boosters et collection partagent ce rendu. */
.cartes {
    display: grid;
    /* 290 px de minimum : en dessous, la photo et le resume se serrent trop.
       auto-fill donne trois colonnes sur la largeur du site et une seule sur
       un telephone, sans media query a ecrire. */
    grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
    gap: 20px;
    margin-top: 20px;
}

/* 5 colonnes fixes, pour la page Ma collection : 5 cartes par ligne.
   Le conteneur `.cartes-5` remplace `.cartes` sur cette page. */
.cartes-5 {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 20px;
    margin-top: 20px;
}

/* Sur la page Ma collection, chaque carte montre seulement : photo,
   titre (plus petit), nombre d'exemplaires, et album. Les trois
   informations s'empilent sur toute la largeur de la carte. */
.cartes-5 .carte-corps {
    grid-template-columns: 1fr;
}
.cartes-5 .carte-nom {
    font-size: 16px;
    grid-row: 1;
    grid-column: 1;
}
.cartes-5 .carte-exemplaires {
    grid-row: 2;
    grid-column: 1;
    justify-self: start;
    align-self: start;
    margin: 4px 0 0;
}
.cartes-5 .carte-latin {
    grid-row: 3;
    grid-column: 1;
    margin: 4px 0 0;
}

/* La bordure prend une couleur portee par une classe sur la fiche, si la
   collection en pose une. Sans classe, --teinte n'est pas definie et la
   bordure reste blanche.
   3 px et non 1 : une bordure de 1 px sur un coin arrondi est le plus
   difficile a russir proprement, et a cette epaisseur la couleur se voit
   vraiment — c'est tout l'objet du choix.

   AUCUNE TEINTE N'EST DEFINIE ICI, ET C'EST VOLONTAIRE. Sur le site d'origine,
   les huit couleurs venaient des logos de conservation UICN, qui n'ont pas
   d'equivalent ici. Le mecanisme reste en place : ajouter une classe
   `.carte-teinte-<nom>` qui pose --teinte suffit, et les fiches qui n'en ont pas
   gardent la bordure blanche. Rien d'autre a toucher. */
.carte {
    position: relative;
    display: flex;
    flex-direction: column;
    border: 3px solid var(--teinte, #ffffff);
    border-radius: var(--r-l);
    overflow: hidden;
    background: var(--surface);
    box-shadow: var(--ombre);
    transition: transform 0.18s ease, box-shadow 0.18s ease;
}

/* AUCUNE TEINTE N'EST DEFINIE ICI, ET C'EST VOLONTAIRE.
 *
 * Sur le site d'origine, les huit couleurs venaient des logos de conservation
 * UICN, qui n'ont pas d'equivalent ici. Le mecanisme reste en place : ajouter une
 * classe `.carte-teinte-<nom>` qui pose --teinte suffit, et les fiches qui n'en
 * ont pas gardent la bordure blanche. Rien d'autre a toucher. */

/* La fiche se soulleve et pivote un peu : c'est ce qui fait qu'on a envie de
   la toucher. Un degres suffit — au-dela, la grille parait desalignee. */
.carte:hover {
    transform: translateY(-6px) rotate(-0.7deg);
    box-shadow: var(--ombre-forte);
}

.tuile-favori {
    position: absolute;
    top: 6px;
    right: 6px;
    z-index: 2;
    font-size: 16px;
    line-height: 1;
    color: var(--violet);
    pointer-events: none;
}

/* Favoris : bouton étoile en haut à droite. */
.carte .favori-form {
    position: absolute;
    top: 8px;
    right: 8px;
    z-index: 10;
    background: var(--surface);
    border: 1px solid var(--bordure);
    border-radius: 50%;
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 2px 4px rgba(0,0,0,0.08);
}

.carte .favori-form button {
    border: none;
    background: none;
    padding: 0;
    margin: 0;
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    font-size: 18px;
    color: var(--texte-doux);
    transition: color 0.12s ease, transform 0.12s ease;
}

.carte .favori-form button:hover {
    transform: scale(1.15);
    color: var(--violet);
}

.carte .favori-form button.favori-on {
    color: var(--violet);
}

.carte .favori-form button:focus-visible {
    outline: 2px solid var(--violet);
    outline-offset: 2px;
}

/* Une fiche en favori se reconnait au jaune sur le bouton, pas sur la fiche. */
.carte:has(.favori-btn.favori-on) .favori-form {
    background: var(--jaune-favori);
    border-color: #ffeeb3;
}

.popin { position: fixed; inset: 0; z-index: 100; display: none; align-items: center; justify-content: center; }
.popin:not([hidden]) { display: flex; }
.popin-fond { position: absolute; inset: 0; background: rgba(36,27,58,0.45); }
.popin-contenu { position: relative; background: var(--surface); border: 1px solid var(--bordure); border-radius: var(--r-l, 12px); padding: 24px 28px; max-width: 420px; width: 92%; box-shadow: var(--ombre-forte); }
.popin-contenu h4 { margin: 0 0 10px; font-size: 18px; font-weight: 700; color: var(--violet); }
.popin-contenu p { margin: 0 0 18px; line-height: 1.45; color: #3a2b4a; }
.popin-boutons { display: flex; gap: 10px; flex-wrap: wrap; justify-content: flex-end; }
.popin-boutons .popin-btn, .popin-form .popin-btn { padding: 8px 18px; border-radius: 8px; border: 1px solid var(--bordure); background: var(--surface-douce); color: #3a2b4a; font-weight: 600; font-size: 14px; cursor: pointer; transition: background 0.15s ease, transform 0.08s ease; }
.popin-boutons .popin-btn:hover, .popin-form .popin-btn:hover { background: #ece6ff; transform: translateY(-1px); }

.carte-photo {
    display: block;
    width: 100%;
    aspect-ratio: 1 / 1;
    height: auto;
    /* Les images sont carrees : `object-fit` conserve le cadrage sans etirer.
       Le fond attend la photo : un aplat gris donne une tache grise sur la page
       coloree. */
    object-fit: cover;
    /* Le fond attend la photo : un aplat gris donne une tache grise sur la
       page coloree. */
    background: linear-gradient(135deg, #ece6ff, #f6e9e0);
}

/* La fiche est une GRILLE de deux colonnes, et non une pile.
   Deux lignes portent deux choses de chaque cote :
   
       [ Je m'en fous .............. 3 exemplaires ]
       [ coolisme infernal .......... Ecouter le morceau ]
   
   La colonne de droite est `auto` : elle prend la largeur de son contenu, et
   le reste va a gauche. C'est ce qui met le compteur d'exemplaires a droite
   du titre, et le lien d'ecoute a droite de l'album, sur la meme ligne.
   
   Les elements sans place dans ces deux lignes — la taxonomie, l'annee, le
   resume — occupent les deux colonnes (`1 / -1`) : ils sont en pleine largeur,
   comme avant. Ils sont places automatiquement, donc dans l'ordre du HTML. */
.carte-corps {
    padding: 14px 16px 16px;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-content: start;
    flex: 1;
}

.carte-nom {
    grid-column: 1;
    grid-row: 1;
    margin: 0;
    font-size: 18px;
    font-weight: 750;
    letter-spacing: -0.01em;
    color: var(--encre);
    line-height: 1.25;
}

.carte-latin {
    grid-column: 1;
    grid-row: 2;
    margin: 3px 0 0;
    font-style: italic;
    font-size: 13px;
    color: var(--texte-doux);
}

.carte-exemplaires {
    /* Affiche au-dessus du titre une pastille : le nombre d'exemplaires est
       une donnee, pas un sous-titre. */
    grid-column: 2;
    grid-row: 1;
    justify-self: end;
    align-self: center;
    margin: 0;
    padding: 2px 10px;
    font-size: 12px;
    font-weight: 700;
    color: var(--violet);
    background-color: #f0ebff;
    border-radius: var(--r-pilule);
}

/* L'annee de publication, au bas de la fiche : la seule date de la chanson.
   Elle est poussee en bas parce que c'est une donnee d'archive, pas ce qu'on
   regarde en premier. */
.carte-annee {
    grid-column: 1 / -1;
    margin: 10px 0 0;
    font-size: 12px;
    font-weight: 600;
    color: var(--texte-doux);
}

/* Le lien vers l'ecoutable. C'est le SEUL lien de la fiche : il part vers un
   site tiers, donc rel="noopener noreferrer" est pose, et il s'ouvre dans un
   nouvel onglet pour que le membre ne perde pas sa collection en partant. */
.carte-ecoute {
    grid-column: 2;
    grid-row: 2;
    justify-self: end;
    align-self: center;
    margin: 0;
    font-size: 13px;
}

.carte-ecoute a {
    color: var(--violet);
    font-weight: 600;
    text-decoration: none;
}

.carte-ecoute a:hover {
    text-decoration: underline;
}

/* La date de la chanson : une ligne sous le titre, en italique. Sur le site
   d'origine c'etait le nom latin de l'animal ; ici c'est l'album. Le nom de la
   classe est celui du site d'origine, pour que la comparaison des deux fiches
   reste lisible — un nom de propriete n'est pas une propriete de la donnee. */
.carte-hierarchie {
    margin: 8px 0 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--texte);
}

/* La liste en <dl> de la fiche, avec leurs libelles.
   Sur le site d'origine elle portait la famille et le genre de l'animal ; ici elle
   porte « Paroles de », « Avec », « Chœurs », « Genre » et « Thème ». Le titre
   generique ci-dessous ne dit rien : c'est chaque libelle qui nomme sa valeur. */
.carte-taxonomie {
    grid-column: 1 / -1;
    margin: 6px 0 0;
    font-size: 13px;
    color: var(--texte-doux);
}

/* dt et dd sur la meme ligne : un <dl> se lit "Famille Felidae", pas
   "Famille" puis une ligne vide avec "Felidae". */
.carte-taxonomie > div {
    margin-top: 3px;
}

.carte-taxonomie dt,
.carte-taxonomie dd {
    display: inline;
    margin: 0;
}

.carte-taxonomie dt {
    color: var(--texte-doux);
}

.carte-taxonomie dt::after {
    /* Espace insecable avant le deux-points : la typographie francaise l'exige,
       et un espace ordinaire serait marque par un retour a la ligne sur un
       telephone. */
    content: "\00a0:";
}

.carte-resume {
    grid-column: 1 / -1;
    margin: 10px 0 0;
    font-size: 13px;
    line-height: 1.55;
    color: var(--texte);
}

.note {
    color: var(--texte-doux);
    font-size: 14px;
}

.corps h2 {
    margin-top: 0;
    font-size: 20px;
    font-weight: 750;
    letter-spacing: -0.01em;
    color: var(--encre);
}

label {
    display: block;
    font-weight: 650;
    margin: 16px 0 6px;
    color: var(--encre);
}

/* LA CASE À COCHER DU PARTAGE
 *
 * Elle est au-dessus du premier champ du formulaire, et non dans une colonne à
 * côté : c'est une décision de la personne, pas une information, et elle se lit
 * avant le nom qu'elle concerne.
 *
 * `label` est un bloc partout ailleurs dans la feuille ; ici il doit s'aligner
 * sur la case, donc il perd ses marges et prend la place qui reste.
 *
 * `flex-start` et non `center` : le texte fait trois lignes sur un téléphone et
 * une seule sur un grand écran. Centré verticalement, il flotterait à côté d'une
 * case haute de vingt pixels au lieu de commencer dessus. */

.case-partage {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin: 0;
}

.case-partage input[type="checkbox"] {
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    margin: 2px 0 0;
    /* La case prend la couleur du site. Sur les navigateurs qui ne connaissent
     * pas cette propriété, la case reste celle du systeme — c'est pour cela
     * qu'aucune capture d'ecran ne doit servir de reference ici. */
    accent-color: var(--violet);
}

.case-partage label {
    flex: 1 1 auto;
    margin: 0;
    font-weight: 600;
}

input[type="text"],
input[type="password"],
input[type="email"] {
    width: 100%;
    padding: 11px 14px;
    font-size: 16px;
    /* 16px et non 14 : en dessous, iOS zoome automatiquement au focus, et la
       page devient illisible apres le premier champ rempli. */
    color: var(--encre);
    background-color: var(--surface);
    border: 2px solid var(--bordure);
    border-radius: var(--r-s);
    box-sizing: border-box;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

input[type="text"]:focus,
input[type="password"]:focus,
input[type="email"]:focus {
    border-color: var(--violet);
    /* Anneau coloured plutot qu'un contour noir : moins sec, et il double la
       bordure plutot que de la remplacer. */
    box-shadow: 0 0 0 4px rgba(108, 77, 246, 0.16);
    /* Le contour par defaut du navigateur dessine un deuxieme anneau en
       plus, plus moche que le notre. */
    outline: none;
}

button {
    margin-top: 20px;
    width: 100%;
    max-width: 460px;
    padding: 12px 20px;
    font-size: 16px;
    font-weight: 700;
    color: #fff;
    background: linear-gradient(120deg, var(--violet), #8b5cf6);
    border: none;
    border-radius: var(--r-pilule);
    box-shadow: var(--ombre-violet);
    cursor: pointer;
    transition: transform 0.12s ease, box-shadow 0.12s ease;
}

/* Le bouton se leve au survol et s'enfonce quand on appuie. C'est le seul
   endroit ou l'effet est tactile : partout ailleurs, le.site bouge. */
button:hover {
    transform: translateY(-2px);
    box-shadow: 0 10px 24px rgba(108, 77, 246, 0.40);
}

button:active {
    transform: translateY(0);
    box-shadow: var(--ombre-violet);
}

/* Texte d'aide sous un champ, et pastille d'info-bulle.
 *
 * Le texte d'aide est visible en permanence, pas seulement au survol : une
 * information de cette importance ne peut pas disparaitre quand la souris
 * bouge, et sur telephone il n'y a pas de survol du tout. La pastille sert
 * de rappel, et son aria-label porte la meme phrase pour un lecteur d'ecran.
 *
 * tabindex="0" sur la pastille : sans elle, l'info-bulle serait inaccessible
 * au clavier.
 */
.aide {
    margin: 0 0 8px;
    color: var(--texte-doux);
    font-size: 14px;
}

.info-bulle {
    display: inline-block;
    width: 20px;
    height: 20px;
    line-height: 20px;
    text-align: center;
    font-size: 12px;
    font-weight: 800;
    color: var(--violet);
    background-color: #f0ebff;
    border: 1px solid var(--violet-clair);
    border-radius: 50%;
    cursor: help;
}

/* Champ de mot de passe avec le bouton "oeil".
 *
 * position: relative sur le conteneur, et le bouton pose en absolu a droite :
 * c'est ce qui evite de bricoler la largeur du champ. Le input garde son
 * width:100%, et le padding droit reserve la place du bouton pour que le
 * texte passe dessous.
 */
.champ-mdp {
    position: relative;
}

.champ-mdp input[type="password"],
.champ-mdp input[type="text"] {
    padding-right: 46px;
}

/* La regle "button" ci-dessus donne width:100%, un fond violet et une ombre :
   on les neutralise tous les trois, sinon le bouton couvrirait le champ
   entier. Chaque annulation porte un commentaire, parce que les trois
   reglages se tiennent — en retirer un fait reapparaitre le fond.
 */
.oeil {
    position: absolute;
    top: 50%;
    right: 5px;
    transform: translateY(-50%);
    width: 36px;
    height: 36px;
    margin: 0;
    padding: 0;
    background: none;
    border: none;
    border-radius: var(--r-s);
    box-shadow: none;
    cursor: pointer;
}

.oeil:hover {
    background: rgba(108, 77, 246, 0.10);
    box-shadow: none;
    transform: translateY(-50%);
}

.oeil svg {
    width: 22px;
    height: 22px;
    fill: none;
    stroke: var(--texte-doux);
    stroke-width: 1.8;
    stroke-linecap: round;
}

.oeil:hover svg {
    stroke: var(--violet);
}

/* La barre oblique n'apparait que quand le mot de passe est visible : c'est
   elle, et non un changement d'icone, qui dit l'etat. */
.oeil .oeil-forme,
.oeil .oeil-pupille {
    opacity: 1;
}

.oeil .oeil-barre {
    opacity: 0;
}

.oeil[aria-pressed="true"] .oeil-barre {
    opacity: 1;
}

/* Jauge de robustesse : quatre segments qui se remplissent. Un progressbar
   natif n'aurait pas de couleur par palier, et le changement de teinte est
   justement l'information utile. */
.force {
    margin-top: 8px;
}

.force-jauge {
    display: flex;
    gap: 5px;
}

.force-jauge span {
    flex: 1;
    height: 8px;
    /* Pastille plutot que capsule : plus épais, plus jeu, et le remplissage
       parait moins fragile. */
    border-radius: var(--r-pilule);
    background-color: #eceaf4;
    transition: background-color 0.15s;
}

.force-txt {
    margin: 6px 0 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--texte-doux);
}

/* Les memes couleurs pour la jauge et son texte : une seule source de
   verite, sinon le texte finit par dire autre chose que la barre. */
.force-jauge.faible span,
.force-txt.faible {
    color: #c2333f;
    background-color: #ec504d;
}

.force-jauge.moyen span,
.force-txt.moyen {
    color: #96610b;
    /* L'or d'origine n'etait qu'a 1,70:1 sur la piste vide : un mot de passe
       moyen s'affichait presque exactement comme un mot de passe vide. La
       couleur du TEXTE n'a pas change, seule celle de la pastille. */
    background-color: #c27300;
}

.force-jauge.bon span,
.force-txt.bon {
    color: #0f766e;
    background-color: var(--menthe);
}

/* La note nulle : aucun segment rempli, gris. Distingue "trop court" d'un
   mot de passe moyen, que la couleur seule ne ferait pas. */
.force-jauge.vide span,
.force-txt.vide {
    background-color: #eceaf4;
    color: var(--texte-doux);
}

/* Le texte suit sa couleur, pas la barre : on ne repeint pas un paragraphe. */
.force-txt.faible,
.force-txt.moyen,
.force-txt.bon {
    background-color: transparent;
}

/* Bouton "generer un mot de passe".
 *
 * Il annule le style general des boutons (violet plein, pleine largeur) :
 * ici c'est une action secondaire, qui doit avoir l'air disponible sans
 * concurrencer le bouton de validation du formulaire.
 */
.generer {
    width: auto;
    margin-top: 12px;
    padding: 9px 18px;
    font-size: 14px;
    color: var(--violet);
    background-color: #f0ebff;
    box-shadow: none;
    border-radius: var(--r-pilule);
}

.generer:hover {
    background-color: #e6deff;
    transform: translateY(-1px);
    box-shadow: none;
}

.generer span {
    font-size: 15px;
}

.aide-genere {
    margin: 8px 0 0;
    color: #0f766e;
    font-weight: 600;
    font-size: 13px;
}

/* Focus visible au clavier. Sans cela, on ne sait pas quel champ est actif
   quand on navigue au clavier.
 *
 * :focus-visible et non :focus, pour que le clic a la souris ne dessine pas
 * d'anneau sur un bouton qu'on n'est pas en train d'utiliser.
 */
input:focus-visible,
button:focus-visible,
a:focus-visible,
[tabindex]:focus-visible {
    outline: 3px solid var(--violet);
    outline-offset: 3px;
}

/* Les champs ont deja leur propre anneau au focus : celui-ci s'y ajouterait
   et donnerait deux halos concentriques. */
input:focus-visible {
    outline: none;
}

.message {
    padding: 13px 16px;
    border-radius: var(--r-m);
    margin-bottom: 18px;
    font-weight: 600;
    border: none;
    border-left: 5px solid transparent;
}

.message-erreur {
    background-color: #fdecec;
    border-left-color: #ef5350;
    color: #a8202c;
}

.message-ok {
    background-color: #e3f8f4;
    border-left-color: var(--menthe);
    color: #0f6f66;
}

.pied {
    margin-top: 22px;
    text-align: center;
    font-size: 14px;
}

.pied a {
    color: var(--violet);
    font-weight: 650;
    text-decoration: none;
}

.pied a:hover {
    text-decoration: underline;
}

.version {
    display: inline-block;
    margin-top: 12px;
    padding: 3px 12px;
    font-size: 12px;
    font-weight: 700;
    color: var(--violet);
    background-color: #f0ebff;
    border-radius: var(--r-pilule);
}
/* ONGLETS
 *
 * Trois sections dans une page, sans passer par l'URL : c'est le choix fait
 * pour amis.php, et ce bloc en est la moitie visible.
 *
 * Le repli est ICI, pas dans le script. `.onglets` et le masquage des
 * panneaux n'existent que sous `.avec-onglets`, une classe que le script pose
 * sur `<html>`. Sans script, la classe n'est jamais posee : la barre
 * d'onglets reste invisible et les trois sections restent empilees, chacune
 * avec son titre. C'est complet, et c'est ce que doit voir une personne dont
 * le script ne s'execute pas.
 *
 * C'est aussi pourquoi le script ne cree aucun element : il se contente de
 * deplacer la classe `actif`, qui est deja dans le HTML pour l'onglet choisi.
 * Un rien au chargement, donc aucun clignotement.
 */

.onglets {
    display: none;
}
html.avec-onglets .onglets {
    display: flex;
    flex-wrap: wrap;
    /* La barre s ajuste aux onglets : elle ne doit pas laisser
       une piste vide de 700 px a droite du dernier onglet. */
    width: fit-content;
    max-width: 100%;
    gap: 6px;
    margin: 0 0 22px;
    padding: 5px;
    background-color: var(--surface-douce);
    border: 2px solid var(--bordure);
    border-radius: var(--r-pilule);
}

/* L'onglet est un lien, pas un bouton : il pointe vers une ancre reelle, ce
 * qui lui donne un sens au lecteur d'ecran et une destination de repli si le
 * script ne s'execute pas.
 *
 * Au repos, violet sur fond doux : 6,5:1. Selectionne, blanc sur le degrade
 * violet du site, la meme valeur que tous les autres boutons. */

.onglet {
    flex: 0 1 auto;
    min-width: 118px;
    padding: 10px 18px;
    text-align: center;
    text-decoration: none;
    font-size: 15px;
    font-weight: 700;
    color: var(--violet);
    background-color: transparent;
    border-radius: var(--r-pilule);
    transition: background-color 0.15s ease, color 0.15s ease;
}

.onglet:hover {
    background-color: #f0ebff;
}

.onglet.actif {
    color: #fff;
    background: linear-gradient(120deg, var(--violet), #8b5cf6);
    box-shadow: var(--ombre-violet);
}

/* Le compteur de demandes en attente.
 *
 * Un nombre dans une pastille, lu « Demandes reçues 2 ». C'est ce qu'un
 * lecteur d'ecran annonca, et c'est le sens voulu : il y a deux choses a
 * traiter. Une pastille vide n'apparait pas : un zero afficherait une
 * urgence qui n'existe pas. */

.onglet-compteur {
    display: inline-block;
    min-width: 22px;
    margin-left: 6px;
    padding: 1px 7px;
    font-size: 13px;
    font-weight: 800;
    color: #fff;
    background-color: var(--corail);
    border-radius: var(--r-pilule);
}

.onglet.actif .onglet-compteur {
    color: var(--corail);
    background-color: #fff;
}

.onglet-panneau {
    display: block;
}

/* SUR TELEPHONE
 *
 * Trois onglets ne tiennent pas sur une ligne de 360 px, et les empiler dans
 * le conteneur en pilule donne un demi-oeuf de 150 px de haut. On les garde
 * donc sur une seule ligne, avec un defilement horizontal.
 *
 * Le defilement se fait par le SCRIPT aussi : apres un changement d onglet au
 * clavier, l onglet choisi peut etre hors du champ visible, et on ne le
 * verrait pas. Voir activer() dans amis.php.
 *
 * Les coins arrondis tombent a r-m : la pilule suppose une seule ligne, et
 * il n y a plus de ligne unique. */
@media (max-width: 560px) {
    html.avec-onglets .onglets {
        flex-wrap: nowrap;
        overflow-x: auto;
        width: 100%;
        border-radius: var(--r-m);
    }

    .onglet {
        flex: 0 0 auto;
        min-width: 0;
        padding: 10px 14px;
    }

    /* L ombre portee de l onglet actif est un halo qui deborde de la barre ;
     * sur une barre qui defile, il donne l impression qu elle est plus large
     * qu elle ne l est. Le degrade suffit a dire quel onglet est choisi. */
    .onglet.actif {
        box-shadow: none;
    }
}
html.avec-onglets .onglet-panneau {
    display: none;
}
html.avec-onglets .onglet-panneau.actif {
    display: block;
}

/* LISTE DE MEMBRES
 *
 * Une ligne par personne : ce qu'on sait d'elle a gauche, ce qu'on peut lui
 * faire a droite. Les boutons sont a droite sur grand ecran et passent sous
 * le texte sur telephone, parce qu'un bouton de 130 px et un identifiant de
 * 200 px ne tiennent pas sur 360.
 */

.corps .titre-section {
    margin: 26px 0 4px;
    font-size: 17px;
    font-weight: 750;
    color: var(--encre);
}

.onglet-panneau .titre-section:first-child {
    margin-top: 0;
}

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

.ligne {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 14px;
    padding: 13px 16px;
    background: var(--surface);
    border: 1px solid var(--bordure);
    border-radius: var(--r-m);
    margin-bottom: 10px;
}

.ligne-corps {
    flex: 1 1 220px;
    min-width: 0;
}

.ligne-nom {
    display: block;
    font-weight: 750;
    color: var(--encre);
    word-break: break-word;
}

/* Le nom affiche, quand il existe : il ne se confond pas avec l'identifiant
 * ci-dessus, qui lui reste en gras. */

.ligne-detail {
    display: block;
    color: var(--texte);
}

/* LE CODE D'AMITIÉ DANS LA LISTE DES JOUEURS
 *
 * Même chasse fixe et même sélection en un clic que `.code-amitie-valeur` : ce
 * sont les mêmes douze chiffres, et les copier doit se faire exactement de la
 * même manière. La taille est en revanche plus petite — ici le code accompagne
 * un nom, il n'est pas seul sur la ligne, et douze chiffres à vingt-six pixels
 * prendraient la place de trois lignes de liste.
 *
 * La couleur est celle des liens : ce chiffre est ce qu'on vient chercher, et il
 * doit se distinguer du texte autour. */

.joueur-code {
    font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    color: var(--violet);
    user-select: all;
    -webkit-user-select: all;
}

.ligne-date {
    display: block;
    font-size: 13px;
    color: var(--texte-doux);
}

/* Le formulaire d'action d'une ligne.
 *
 * Deux choses a ecraser, sinon la regle generale des formulaires prend le
 * dessus : elle plafonne a 460 px et elle occupe toute la largeur. Ici on
 * veut un bouton, pas un formulaire.
 *
 * Le selecteur porte deux classes pour passer devant `.corps form`. */

.corps .ligne-actions {
    width: auto;
    max-width: none;
    margin: 0;
}

/* Bouton discret : une action possible, pas un envoi.
 *
 * Les boutons du site sont violets et pleine largeur : ils sont la
 * conclusion d'un formulaire. Ici, un bouton Acceter est une proposition
 * parmi trois autres, et cinq lignes de liste alignees de boutons violets
 *_DOMAINENT la page.
 *
 * Au survil, il prend la couleur du site : on voit alors que c'est bien une
 * action. */

.bouton-discret {
    width: auto;
    max-width: none;
    margin: 0;
    padding: 8px 16px;
    font-size: 14px;
    color: var(--violet);
    background: var(--surface);
    border: 2px solid var(--violet-clair);
    box-shadow: none;
    transition: color 0.15s ease, background-color 0.15s ease,
        border-color 0.15s ease;
}

.bouton-discret:hover {
    color: #fff;
    background: var(--violet);
    border-color: var(--violet);
    transform: none;
    box-shadow: none;
}

.bouton-discret:active {
    transform: none;
}

/* LE CODE D AMITIE
 *
 * Douze chiffres qu on doit pouvoir lire, copier et dicter. Ils sont donc
 * groupes par trois : `014 984 294 786`. C est ce qui distingue un code d une
 * suite de chiffres qu on relit trois fois pour verifier qu on a bien tout
 * recopie.
 *
 * `user-select: all` fait que le double-clic selectionne les douze chiffres
 * d un coup, sans JavaScript. Un bouton « copier » ferait la meme chose en
 * mieux, mais il ne marche que sur une page de securite — localhost ou HTTPS —
 * et il faudrait donc foresee une coupure pour la même boîte.
 */
.code-amitie {
    margin: 0 0 8px;
    padding: 16px 18px;
    background-color: var(--surface-douce);
    border: 2px solid var(--violet-clair);
    border-radius: var(--r-m);
}

.code-amitie-titre {
    margin: 0;
    font-size: 14px;
    font-weight: 700;
    color: var(--violet);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* Les chiffres en chasse fixe : la chasse proportionnelle change la largeur
 * des chiffres selon leur dessin, et une suite de douze chiffres se lit alors
 * mal. `font-variant-numeric: tabular-nums` fait la meme chose sans changer
 * de police, et couvre aussi le cas d une police deja fixe. */
.code-amitie-valeur {
    margin: 6px 0 10px;
    font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: 26px;
    font-weight: 700;
    color: var(--encre);
    word-break: break-all;
    user-select: all;
    -webkit-user-select: all;
}

/* Un membre sans code. Ne devrait jamais arriver : auth_creer_compte() le remplit
 * a la creation. Le dire a l ecran vaut mieux qu un cadre vide. */
.code-amitie-absent {
    margin: 6px 0 10px;
    font-weight: 700;
    color: #a8202c;
}

.code-amitie .aide {
    margin: 0;
}

/* Le compteur d'amis, au-dessus de la liste.
 *
 * Il rend la limite de 100 lisible avant qu'on la rencontre. Sans lui, on ne
 * sait qu'on est arrivé au bout qu'en essayant d'inviter quelqu un, et le
 * message d refus est alors une mauvaise nouvelle sans contexte.
 */
.compte-amis {
    margin: 22px 0 8px;
    font-size: 14px;
    font-weight: 700;
    color: var(--texte-doux);
}

/* Liste vide. Pas de cadre : ce n'est pas une erreur, c'est une liste qui
 * n'a rien encore. Le gris suffit, et le message dit quoi faire ensuite. */

.vide {
    margin: 6px 0 0;
    padding: 14px 16px;
    color: var(--texte-doux);
    background-color: var(--surface-douce);
    border-left: 5px solid var(--bordure);
    border-radius: var(--r-s);
}


/* Une carte deja promise : elle reste visible, c'est demande, mais elle se
 * distingue d'une carte disponible. Un bord, pas une attenuation : la carte
 * n'a pas disparu, et l'atténuer la ferait passer pour retirée. */

.ligne-proposee {
    border-left: 5px solid var(--violet);
}

.ligne-proposee .ligne-detail {
    color: var(--violet);
}

/* Le titre des deux listes dans « Proposer un echange ». Un niveau plus bas que
 * le titre de section : la liste des cartes est une subdivision, pas une
 * section de meme niveau. */

.titre-sous-section {
    margin: 22px 0 0;
    font-size: 15px;
    font-weight: 700;
    color: var(--texte);
}

/* ------------------------------------------------------------------ TUILES
 *
 * Les amis et les fiches se choisissent par des tuiles, et non dans une liste
 * deroulante : on reconnait une personne a son nom et une carte a son image, et
 * ni l'un ni l'autre ne se reconnait a une ligne de texte dans un menu deroule.
 *
 * LE FORMULAIRE NE DOIT PAS PLAFONNER LA GRILLE
 *
 * `.corps form` plafonne a 460 px, pour que les champs de saisie ne s'etirent
 * pas sur toute la largeur. C'est la bonne regle partout ailleurs, et elle
 * FAISSE ICI : les deux grilles sont dans le formulaire de proposition, donc
 * elles etaient bornees a 460 px sur une largeur utile de 1060 — deux colonnes
 * de tuiles et six cents pixels de vide a cote. La grille n'y etait pour rien :
 * `auto-fill` ne peut pas faire mieux que ce que son conteneur lui donne.
 *
 * D'ou l'exception ci-dessous, qui rend au formulaire la largeur du corps. Elle
 * est ecrite pour CE formulaire, avec une classe, et non pour tous les
 * formulaires : les autres pages gardent leur 460 px.
 */

.corps .formulaire-echange {
    max-width: none;
    width: 100%;
}

/* LE MINIMUM DE 170 px EST LE BON, ET IL NE FAUT PAS Y TOUCHER
 *
 * Mesuré sur la largeur du site : `.page` fait 1100 px moins 40 px de marge,
 * donc 1060 px utiles. Avec `minmax(170px, 1fr)`, `auto-fill` y case
 * exactement **cinq** colonnes de 202 px — quinze éléments en trois lignes.
 *
 * J'ai d'abord écrit 196 px, en pensant que 170 en casait six et que les
 * tuiles rapetisseraient. C'est faux, et ça n'aurait rien arrange : à 196 px
 * on reste à cinq colonnes sur grand écran, mais on perd une colonne sur
 * tablette (trois de 235 px au lieu de quatre de 173 px). Le minimum n'était
 * pas le fautif ; le plafond de 460 px, si.
 *
 * La photo garde ses 105 px de haut : une tuile de 202 px de large a exactement
 * l'aspect d'avant, et il y en a deux fois plus par ligne.
 *
 * Et toujours aucun media query : sur un téléphone `auto-fill` descend à 1
 * colonne tout seul.
 */

.tuiles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
    gap: 12px;
    margin: 12px 0 0;
}

.tuile {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 12px 14px;
    background: var(--surface);
    border: 2px solid var(--bordure);
    border-radius: var(--r-s);
    cursor: pointer;
}

/* La tuile choisie.
 *
 * Le contour est sur le LABEL et non sur le bouton : le bouton est transparent
 * et pose par-dessus, donc son contour ne se verrait pas sous les autres. Et
 * c'est le label qui porte l'etat choisi, parce que c'est lui qu'on voit.
 *
 * Deux selecteurs pour le meme effet, et ils ne se remplacent pas :
 * `.tuile-choisie` vient du serveur et survit au rechargement de la page —
 * apres un refus, la selection est reportee, et une regle CSS ne sait pas a qui
 * elle appartient ; `:has()` donne le retour immediat quand on clique, avant
 * meme d'envoyer. Un navigateur qui ignore `:has()` garde donc la selection
 * visible a chaque rechargement, et perd seulement le reflet pendant la
 * frappe. */
.tuile-choisie,
.tuile:has(input:checked) {
    border-color: var(--violet);
    background: var(--surface-douce);
    box-shadow: 0 0 0 3px var(--violet-clair);
}

/* Le focus clavier dessine l'anneau sur la TUILE.
 *
 * `:focus-within` et non `:focus`, parce que c'est le bouton qui recoit le
 * focus : la tuile ne l'est jamais. Sans cela, une tuile choisie au clavier ne
 * se distingue en rien d'une tuile choisie a la souris. */
.tuile:focus-within {
    outline: 3px solid var(--violet);
    outline-offset: 3px;
}

/* LE BOUTON EST TRANSPARENT ET POSE PAR-DESSUS
 *
 * `display: none` aurait ete plus court, mais un element ainsi retire n'est
 * plus atteignable au clavier : la tuile deviendrait decorative, et c'est tout
 * l'interet du label que d'etre un vrai controle. On le garde donc dans le flux
 * de lecture, transparent, et sa surface couvre toute la tuile — un clic sur le
 * nom ou sur la photo selectionne aussi. */
.tuile input[type="radio"] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
    z-index: 1;
}

/* UNE TUILE PRISE RESTE VISIBLE ET EST GRISEE
 *
 * Elle garde sa place et son nom : une carte retiree de la liste ferait croire
 * qu'elle a disparu de la collection, alors qu'elle y est tranquillement. La
 * bordure et le texte s'effacent, la photo aussi — c'est le seul endroit du site
 * ou une image est attenuationee, parce que c'est le seul ou elle porte une
 * information qui n'a plus cours. */
.tuile-prise {
    cursor: not-allowed;
    border-style: dashed;
    border-color: var(--bordure);
    background: var(--surface-douce);
    color: var(--texte-doux);
}

.tuile-prise .tuile-nom,
.tuile-prise .tuile-detail,
.tuile-prise .tuile-note {
    color: var(--texte-doux);
}

.tuile-prise .tuile-photo {
    opacity: .3;
    filter: grayscale(1);
}

.tuile-prise input[type="radio"] {
    cursor: not-allowed;
}

.tuile-nom {
    font-weight: 700;
    color: var(--encre);
    word-break: break-word;
}

.tuile-detail {
    font-size: 13px;
    color: var(--texte);
}

.tuile-note {
    font-size: 12px;
    font-style: italic;
    color: var(--texte-doux);
}

/* La photo d'une fiche, dans la tuile de choix.
 *
 * `cover` comme sur `.carte-photo` : les photos de Commons n'ont pas les memes
 * proportions. Une hauteur fixe etait necessaire ici, sinon une tuile haute et
 * une tuile large Alignent mal leurs noms — et les noms alignes, c'est ce qui
 * permet de balayer la grille du regard. */
.tuile-photo {
    display: block;
    width: 100%;
    aspect-ratio: 1 / 1;
    height: auto;
    margin: -4px 0 6px;
    object-fit: cover;
    border-radius: var(--r-s);
    background: var(--surface-douce);
}

/* ---------------------------------------------------------------- RECHERCHE
 *
 * Le champ et son bouton sont sur une ligne, le libelle au-dessus.
 * Le libelle est VISIBLE et pas seulement un `aria-label` : c'est la seule
 * chose qui dise a quoi sert le champ, et « Chercher » tout seul ne le dit pas.
 *
 * LES DEUX RECHERCHES ONT LE MEME NOMBRE DE PIXELS
 *
 * Il y en a deux sur la page — celle des amis, celle des fiches — et elles ne
 * doivent pas avoir une taille differente : deux champs de recherche de largeurs
 * differentes sur le meme ecran se lisent comme deux fonctions differentes.
 *
 * Elles partagent donc des classes a elles (`champ-recherche`,
 * `bouton-recherche`) et des LARGEURS ET DES HAUTEURS ECRITES, plutot que de
 * dependre de la regle generale des formulaires, qui plafonne a 460 px et
 * occupe toute la place. La regle generale s'applique au reste du site ; ici le
 * champ doit etre court, pas un bloc de saisie.
 */

.recherche {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 10px;
    margin: 10px 0 0;
    padding: 0;
    border: 0;
}

.recherche-libelle {
    flex: 1 0 100%;
    font-weight: 700;
    color: var(--encre);
}

.corps .recherche .champ-recherche {
    flex: 0 0 260px;
    width: 260px;
    max-width: 260px;
    margin: 0;
    padding: 9px 12px;
    font: inherit;
    line-height: 1.4;
    color: var(--encre);
    background: var(--surface);
    border: 1px solid var(--bordure);
    border-radius: var(--r-s);
    appearance: none;
    -webkit-appearance: none;
}

/* Le bouton : meme hauteur que le champ, meme rayon, memes marges.
 *
 * La hauteur vient de `align-items: center` sur la range et d'un `padding`
 * identique a celui du champ. Deux boutons de hauteurs differentes a cote de
 * champs identiques, ca se voit. */
/* Le bouton : meme hauteur que le champ, meme rayon, memes marges.
 *
 * La hauteur vient de `align-items: center` sur la range et d'un `padding`
 * identique a celui du champ. Deux boutons de hauteurs differentes a cote
 * d champs identiques, ca se voit. */
.corps .recherche .bouton-recherche {
    flex: 0 0 auto;
    width: auto;
    max-width: none;
    margin: 0;
    padding: 9px 20px;
    line-height: 1.4;
}

/* ---------------------------------------------------------------- ECHANGES
 *
 * Un dossier : ce que je propose, ce qu'on me propose, les actions.
 *
 * DEUX COLONNES, ET LES CARTES EN VERSION COMPLETE
 *
 * Les deux cartes sont rendues par le moteur de la reserve — bordure de statut,
 * photo, nom latin, taxonomie, résumé — exactement comme sur « Ma collection ».
 * Elles sont seulement posees dans une grille a deux termes au lieu d'une
 * grille de collection, et c'est la seule difference.
 *
 * LA GRILLE EST ELASTIQUE, PAS FIXE
 *
 * Deux colonnes de 290 px minimum feraient tenir l'ensemble sur 592 px, et le
 * reste du corps resterait vide — le meme defaut que la grille a 460 px du
 * formulaire, et pour la meme raison. Deux `1fr` occupent tout, et les cartes
 * grossissent sur un grand ecran au lieu de laisser du vide. Sur un telephone,
 * la regle du telephone les empile, ce qui est le bon ordre de lecture : ma
 * carte d'abord, celle de l'autre ensuite.
 *
 * `align-items: stretch` est le defaut d'une grille, et il est ici ce qui
 * donne aux deux cartes la meme hauteur. Sans lui, une fiche au long resume et
 * une fiche sans resume finiraient a des hauteurs differentes, et le bord du
 * dossier serait de travers.
 */

.echanges {
    display: grid;
    gap: 16px;
    margin: 14px 0 0;
}

.echange {
    padding: 16px 18px 18px;
    background: var(--surface);
    border: 1px solid var(--bordure);
    border-radius: var(--r-m);
}

.echange-entete {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 6px 14px;
    margin-bottom: 14px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--bordure);
}

.echange-partenaire {
    font-size: 16px;
    color: var(--texte);
}

.echange-entete .ligne-date {
    display: inline;
}

.echange-echelle {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
}

.echange-cot {
    min-width: 0;
}

.echange-legende {
    margin: 0 0 7px;
    font-size: 12px;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--texte-doux);
}

/* La carte herite de `.carte`, qui est deja une colonne a bordure teintee. Elle
 * se retire simplement de la grille du moteur, dont elle ne depend pas : le
 * conteneur est celui de l'echange. */
.echange-cot .carte {
    height: 100%;
}

.echange-cot .carte-photo {
    aspect-ratio: 1 / 1;
    height: auto;
}

/* LE SELECTEUR DE CONTRE-PROPOSITION, DANS LA MOITIE DU DOSSIER
 *
 * Le selecteur partage la grille du premier onglet — meme rendu, memes compteurs,
 * meme regle du « deja proposee » — mais il vit dans une colonne etroite, ou le
 * minimum de 170 px de la grille generale ne caserait que trois tuiles. Ici on en
 * case quatre, et la photo perd cinq pixels de haut pour qu'elles aient la meme
 * hauteur. `object-fit` vient de la regle generale : la photo est cadree, jamais
 * etiree.
 */
.echange-cot .tuiles {
    display: grid;
    gap: 6px;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
}

.echange-cot .tuiles .tuile-photo {
    aspect-ratio: 1 / 1;
    height: auto;
}

/* Le selecteur est un formulaire comme l'autre : meme classe, donc meme largeur.
 * La regle `.corps .formulaire-echange` le rend aussi large que la colonne, ce
 * qui est necessaire — un formulaire plafonne a 460 px ne ferait qu'une tuile
 * par ligne dans une colonne de 520. */

/* LE COTE VIDE
 *
 * Meme hauteur, memes coins, meme place que la carte de l'autre cote — et c est
 * tout l'objet. Un vide plus petit ferait chercher ce qui manque ; un cadre plus
 * grand, meme en pointilles, ferait croire qu'il y a une carte qu'on ne voit
 * pas. Le cadre a donc la meme place, et il le dit.
 */
.echange-vide {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 175px;
    margin: 0;
    padding: 20px;
    text-align: center;
    color: var(--texte-doux);
    background: var(--surface-douce);
    border: 2px dashed var(--bordure);
    border-radius: var(--r-l);
}

.echange-vide-texte {
    font-style: italic;
}

.echange-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 14px;
    margin-top: 16px;
    padding-top: 14px;
 }

/* « Refuser » est sur la ligne d'entete.
 *
 * Le formulaire est un bouton, pas un formulaire : `.corps form` le plafonnerait
 * a 460 px, ce qui dans une ligne d'entete prendrait la moitié de l'écran. Il
 * faut donc lui rendre sa largeur naturelle, comme dans `.echange-actions` plus
 * bas — et meme regle, meme raison.
 *
 * `margin-left: auto` le colle a droite. L'entete est en `space-between` : sans
 * cette marge, le troisieme enfant passerait AU MILIEU, et la date s'ecarterait
 * de son bord. Avec elle, « avec qui » et la date restent a gauche, le bouton a
 * droite. Le `flex-wrap` deja present fait passer le bouton a la ligne sur un
 * telephone.
 */
.echange-entete .ligne-actions {
    width: auto;
    max-width: none;
    margin: 0 0 0 auto;
    align-self: center;
}

/* Les actions gardent la meme regle que sur la page Amis : un bouton, pas un
 * formulaire qui prend toute la largeur. */
.echange-actions .ligne-actions {
    width: auto;
    max-width: none;
    margin: 0;
}

/* Sur un telephone, les deux cartes s empilent. L ordre du document est deja le
 * bon : ce que je propose, puis ce qu'on me propose. */
@media (max-width: 620px) {
    .echange-echelle {
        grid-template-columns: 1fr;
    }
}

.corps .recherche .aide {
    flex: 1 0 100%;
    margin: 0;
}

/* LE MORCEAU QUE LA RECHERCHE EN DIRECT REMPLACE
 *
 * `liste-recherche` n'est qu'une bordure de groupement : elle tient le compte,
 * la grille et la barre de pages, et c'est tout ce que le script échange. Elle ne
 * porte aucune mise en page, sinon une regle posee ici s'appliquerait aux trois
 * listes a des profondeurs de mise en page différentes.
 *
 * Pendant une requête, la liste reste affichée — la retirer ferait clignoter la
 * page a chaque frappe — et elle s'estompe. `aria-busy` fait la même chose pour
 * un lecteur d'écran : le contenu est en train de changer, et il ne doit pas
 * l'annoncer caractère par caractère.
 */
.liste-recherche[aria-busy="true"] {
    opacity: .55;
    transition: opacity .12s ease-out;
}

/* ------------------------------------------------------- BOOSTER : LES ÉDITIONS
 *
 * TROIS ÉDITIONS SUR UNE SEULE LIGNE, ET LE VISUEL QUI OUVRE
 *
 * Décision de la personne, le 2026-10-16. Une édition est une carte : la date de
 * fin, le nombre de boosters qui restent, et le visuel sur lequel on clique.
 *
 * `flex: 1 1 0` SUR CHAQUE CARTE, ET NON UNE LARGEUR FIXE
 *
 * Avec `1fr` chacune, une édition, deux ou trois ocupent toute la ligne de la
 * meme facon, et l'aire ne laisse jamais de trou quand il n'y en a qu'une. La carte
 * ne peut pas être plus étroite que son contenu : `min-width: 0` est mis, sinon
 * un nom d'édition sans espace — ou une image trop large — écartait les autres
 * et la ligne débordait.
 *
 * SUR TÉLÉPHONE, LA LIGNE DÉFILE
 *
 * Trois cartes sur 360 pixels feraient 110 pixels chacune, et un visuel à cette
 * taille ne se reconnaît plus. La ligne défile donc horizontalement, avec
 * `scroll-snap` : c'est le mécanisme de la barre d'onglets de `echanges.php`, et
 * le membre glisse comme il glisse partout ailleurs.
 */
.editions {
    display: flex;
    gap: 16px;
    align-items: stretch;
    margin: 0 0 1.4em;
    padding: 0 0 .4em;
    border: 0;
    scroll-snap-type: x proximity;
}

.edition {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    scroll-snap-align: start;
}

/* LE FORMULAIRE EST LA CARTE, ET IL FAUT LUI RETIRER LE CADRE DU SITE
 *
 * `.corps form` porte un `max-width: 460px` fait pour les formulaires de saisie :
 * sur une carte de tiers de largeur, il reduisait la carte et laissait un vide a droite.
 * D'où le sélecteur `.corps .edition-formulaire`, plus spécifique, et non un
 * `!important` — le site n'en a aucun. */
.corps .edition-formulaire {
    display: flex;
    flex-direction: column;
    gap: .3em;
    width: 100%;
    max-width: none;
    margin: 0;
    padding: .7em;
    text-align: center;
    background: var(--surface, #ffffff);
    border: 1px solid var(--trait, #d6d2c8);
    border-radius: var(--r-m, 16px);
}

.edition-fin,
.edition-reste {
    margin: 0;
    font-size: .9em;
    line-height: 1.35;
    color: var(--texte-doux, #726b8a);
    font-variant-numeric: tabular-nums;
    overflow-wrap: break-word;
}

.edition-fin {
    font-weight: 650;
    color: var(--encre, #241b3a);
}

/* LE VISUEL EST UN BOUTON, ET LE BOUTON DU SITE DOIT CÉDER LA PLACE
 *
 * La règle globale `button` du site est un pilule violet, pleine largeur, avec
 * ombre et texte blanc : c'est un bouton d'action. Ici le bouton EST l'image, et
 * la pastille violette autour d'un visuel serait un cadre de trop. Chaque
 * propriété est donc reposée ici, une par une, plutôt que neutralisée.
 *
 * L'ANIMATION EST EN CSS, ET C'EST TOUT
 *
 * Le site n'a pas de JavaScript testable sur ce poste, et une animation qui
 * dépendrait d'un script ne serait jamais vérifiée. Celle-ci est donc `:hover` et
 * `:active` : la carte se soulève au survol, s'enfonce à l'appui, en 0.15 s. Le
 * geste de cliquer est déjà perçu sans une seule ligne de script.
 *
 * LA HAUTEUR EST POSÉE ICI, ET L'IMAGE S'Y ADAPTE
 *
 * Demande de la personne, le 2026-10-17. Avant, la hauteur du visuel venait de
 * `aspect-ratio: 3 / 2` multiplié par la largeur de la carte : elle changeait donc
 * avec le nombre d'éditions sur la ligne et avec la largeur de l'écran. Elle est
 * maintenant posée une fois — 180 px, la hauteur que la page rendait déjà
 * (177 px mesurés sur une carte de 266 px de large, 163 px sur un téléphone) — et
 * c'est l'image qui vient s'y adapter en gardant son ratio, non la boîte qui
 * s'étire pour la contenir.
 *
 * Une hauteur en dur a l'air d'un réglage à la main, mais c'est la seule façon que
 * CSS ait de dire « voilà l'espace » : sans elle, la hauteur est une conséquence
 * de la largeur, et l'image n'a plus de référence. La variable est posée dans la
 * règle qui la consomme : un seul endroit à changer.
 *
 * Le `display: flex` et les deux `center` centrent l'image dans cet espace : une
 * image plus petite que la boîte ne doit pas rester collée au bord supérieur. */
.edition-bouton {
    --visuel-hauteur: 270px;

    display: flex;
    align-items: center;
    justify-content: center;
    height: var(--visuel-hauteur);
    width: 100%;
    max-width: none;
    margin: .5em 0 0;
    padding: 0;
    font-size: 0;
    color: var(--encre, #241b3a);
    background: none;
    border: 1px solid var(--trait, #d6d2c8);
    border-radius: var(--r-s, 10px);
    box-shadow: none;
    cursor: pointer;
    overflow: hidden;
    transition: transform .15s ease, box-shadow .15s ease;
}

/* L'IMAGE REMPLIT L'ESPACE ET GARDE SON RATIO
 *
 * `width: 100%` et `height: 100%` font occuper toute la boîte au SEUL element
 * `<img>`, et `object-fit: contain` décide, lui, de la taille PEINTE. C'est le
 * seul couple CSS qui agrandit une petite image et rétrécit une grande, sans
 * jamais déformer : le navigateur rend la plus grande version qui tienne dans les
 * deux sens, et centre le reste.
 *
 * Deux autres écritures ont été essayées avant, et chacune a un défaut mesuré :
 *
 *   - `max-width` / `max-height` a 100%, sans largeur fixe : une image de 240x160
 *     dans une boîte de 314x180 reste a 240x160, donc a sa taille naturelle, avec
 *     37 pixels de marge de chaque cote. Elle n'occupe pas l'espace, elle l'occupe
 *     en petit ;
 *   - `height: 100%` avec `width: auto` : la version verticale s'adapte bien, mais
 *     des qu'une image est plus large que la boite, `max-width` la retrecit pendant
 *     que `height: 100%` reste fixe — l'image est alors ETIREE. Une regle qui
 *     deforme est pire qu'une regle qui rogne.
 *
 * L'ancien `object-fit: cover` est retire pour une raison voisine : il remplissait
 * la boite en rognant, donc toute image qui n'etait pas en 3/2 perdait un coin —
 * le plus souvent celui du titre, en haut. Un booster doit se voir entier, et sa
 * forme n'est pas celle d'un autre. */
.edition-bouton img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.edition-bouton:hover {
    transform: translateY(-4px);
    box-shadow: var(--ombre, 0 1px 2px rgba(36, 27, 58, .06));
}

.edition-bouton:active {
    transform: translateY(-1px) scale(.98);
    box-shadow: none;
}

/* L'ÉDITION ÉPUISÉE : LE BOUTON NE CLIQUE PLUS, ET LE VISUEL S'ESTOMPE
 *
 * `disabled` et non `aria-disabled` : le bouton est réellement inerte, donc il
 * ne prend pas le focus au clavier et ne peut pas être envoyé. Le compte à
 * rebours reste écrit sur la ligne du dessus. */
.edition-bouton:disabled {
    cursor: default;
    transform: none;
    box-shadow: none;
}

.edition-epuisee .edition-bouton {
    opacity: .5;
    filter: grayscale(.6);
}

.edition-epuisee .edition-reste {
    color: var(--texte-doux, #726b8a);
}

/* UNE ÉDITION SANS IMAGE : LE NOM PREND LA PLACE DU VISUEL
 *
 * Elle remplit exactement l'espace réservé par `.edition-bouton`, pour que les
 * cartes d'une même ligne aient la même hauteur qu'elles aient ou non une image.
 * L'ancien `aspect-ratio: 3 / 2` est retiré pour la même raison que sur l'image :
 * c'est la boîte qui donnait la hauteur, et c'est l'image qui la doit donner. */
.edition-sans-visuel {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    padding: .6em;
    font-size: 15px;
    font-weight: 700;
    line-height: 1.3;
    text-align: center;
    background: var(--surface-douce, #faf8ff);
}

@media (max-width: 700px) {
    .editions {
        overflow-x: auto;
    }

    .edition {
        flex: 0 0 72%;
    }
}

/* ------------------------------------------------------------ ADMINISTRATION
 *
 * LES TROIS ÉTATS SONT DES MOTS DE CLASSE, ET LE CSS LE MONTE
 *
 * `edition_etat()` rend « en-ligne », « a-venir » ou « terminee » : sans espace
 * et sans accent, parce que la valeur est collée dans un nom de classe. Un état
 * écrit « en ligne » donnerait `etat-etat-en ligne`, c'est-à-dire deux classes
 * dont une invalide — et cette règle-ci ne s'appliquerait jamais, sans le moindre
 * message. C'est le genre de faute qui ne se voit qu'en ouvrant la page, donc
 * les trois couleurs sont posées ici explicitement pour chaque valeur.
 *
 * LA COULEUR N'EST PAS LA SEULE INFORMATION : le texte dit l'état en toutes
 * lettres dans la colonne. La couleur distingue, elle n'explique pas.
 */
.etat-etat-en-ligne {
    color: #2f6b32;
    font-weight: 600;
}

.etat-etat-a-venir {
    color: #7a6320;
}

.etat-etat-terminee {
    color: #7b7466;
}

.table-editions {
    width: 100%;
    border-collapse: collapse;
    margin: 1.2em 0 2em;
}

.table-editions caption {
    text-align: left;
    color: #6b6659;
    padding-bottom: .5em;
}

.table-editions th,
.table-editions td {
    text-align: left;
    padding: .5em .6em;
    border-bottom: 1px solid var(--trait, #d6d2c8);
    vertical-align: baseline;
}

.table-editions thead th {
    font-size: .85em;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: #6b6659;
}

.table-editions tbody tr.choisi {
    background: rgba(0, 0, 0, .04);
}

/* LA COLONNE DU COMPTE, PAR SON NOM ET PLUS PAR SA POSITION
 *
 * C'était `td:nth-child(3)`, c'est-à-dire « la troisième cellule de la ligne ».
 * La colonne du visuel est venue s'insérer avant celle de l'action, et un
 * sélecteur positionnel n'aurait pas bougé tout seul : il se serait mis à
 * aligner le mauvais contenu. Un nom de classe ne peut pas se tromper quand une
 * colonne arrive. La règle était écrite deux fois, à deux tabulations puis à
 * quatre : il n'en reste qu'une. */
.table-editions .colonne-compte {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* LE BOUTON DE SUPPRESSION, DANS LA LIGNE
 *
 * Il est dans une cellule a part, pas dans la cellule du nom : une cellule qui
 * porte a la fois un lien et un formulaire oblige le lien a englober le bouton,
 * ou l'inverse. Deux cellules, deux gestes distincts.
 *
 * La colonne est etroite et sans contenu lisible : l'en-tete porte un texte
 * cache, parce qu'une colonne de boutons sans titre se lit comme une colonne
 * sans nom dans un tableau de donnees. `.visuellement-cache` le dit aux lecteurs
 * d'ecran sans l'afficher. */
.table-editions .colonne-action {
	width: 1%;
	white-space: nowrap;
}

.table-editions .actions-ligne {
	text-align: right;
}

/* Reserve aux lecteurs d'ecran : present dans le HTML, absent de l'ecran.
 *
 * `clip-path` vient apres `clip` parce que les vieux navigateurs ne connaisent
 * que le second ; `clip-path` couvre les deux navigateurs qui ignorent
 * `clip-path` seul. Le `position: absolute` sort la boite du flux, donc la
 * colonne garde la largeur du bouton et non celle d'un mot invisible. */
.visuellement-cache {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* Le formulaire ne doit pas choisir la hauteur de la ligne : un `margin` de
 * paragraphe sous un bouton ferait descendre toutes les lignes de la table. */
.formulaire-ligne {
	margin: 0;
}

.bouton-danger {
	padding: .35em .8em;
	font-size: .85em;
	font-weight: 600;
	color: #a11d1d;
	background: transparent;
	border: 1px solid rgba(161, 29, 29, .45);
	border-radius: var(--r-s);
	cursor: pointer;
}

.bouton-danger:hover {
	color: #fff;
	background: #a11d1d;
	border-color: #a11d1d;
}

/* Un champ de nombre est plus etroit qu'un champ de texte, et une grille de
 * formulaire alignee sur la plus grande largeur donnerait des cases minuscules,
 * perdues dans la ligne. */
.champ-nombre {
	max-width: 9em;
}

/* LE QUOTA ÉPUISÉ, DANS LA CARTE DE L'ÉDITION
 *
 * Ce n'est pas une erreur : le membre a bien fait ce qu'il pouvait, et l'écran
 * lui dit quand revenir. Le ton est donc celui d'une information — le refus
 * reste en rouge, celui-ci garde la couleur du texte doux.
 *
 * LA RÈGLE EST SUR LE TEXTE, PAS SUR LA CARTE
 *
 * L'ancienne version posait un liseré et un fond sur tout le bloc du quota, ce
 * qui prenait la largeur de la ligne. Ici le quota tient sur une ligne de
 * quelques mots au-dessus du visuel : un liseré de trois pixels autour d'un mot
 * aurait été un cadre dans un cadre. */
.quota-epuise-texte {
    color: var(--texte-doux, #726b8a);
    font-weight: 650;
}

.edition-epuisee .edition-fin,
.edition-epuisee .edition-reste {
    color: var(--texte-doux, #726b8a);
    font-weight: 400;
}

.edition-detail {
	border-top: 1px solid var(--trait, #d6d2c8);
	padding-top: 1.4em;
	margin-top: 1.4em;
}

.formulaire-edition {
    margin: 0 0 1.4em;
}

.formulaire-edition fieldset {
    border: 1px solid var(--trait, #d6d2c8);
    border-radius: 6px;
    padding: .2em 1em 1em;
    margin: 0;
}

.formulaire-edition legend {
    padding: 0 .4em;
    font-weight: 600;
}

.formulaire-edition .champ {
    margin: .8em 0;
}

/* ---------------------------------------------------- DEUX COLONNES D'ÉDITIONS
 *
 * LE FORMULAIRE DE MODIFICATION À GAUCHE, CELUI DE CRÉATION À DROITE
 *
 * Cliquer le nom d'une édition dans le tableau ne doit pas coûter un défilement :
 * le formulaire qu'on cherche est déjà à l'écran, à côté de celui qui sert à en
 * créer une autre. On utilise les deux dans le même geste — regarder ce qui
 * existe, corriger ce qui cloche — et les mettre l'un au-dessus de l'autre
 * obligait à remonter en haut de la page entre les deux.
 *
 * `min-width: 0` SUR LES COLONNES, ET C'EST NÉCESSAIRE
 *
 * Une grille donne à ses colonnes `min-width: auto`, qui vaut la largeur du plus
 * long mot insécable : un nom d'édition ou un message d'erreur ferait déborder
 * la colonne par la droite et décalerait toute la ligne. La valeur est mise à
 * zéro pour que `overflow-wrap` puisse agir.
 *
 * LA GRILLE DES FICHES N'EST PAS DANS LA GRILLE
 *
 * Cinquante cases ont besoin de toute la largeur. Les partager en deux colonnes
 * les rendrait illisibles, et surtout : une grille de cases qui se réorganise
 * selon la largeur de l'écran oblige à recalculer où se trouve une case
 * entre deux chargements — c'est le chemin le plus court vers un enregistrement
 * de fiches sur la mauvaise édition. La grille de fiches reste donc en dessous,
 * sur toute la largeur, et ne bouge pas.
 */
.colonnes-edition {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 1.6em;
    align-items: start;
    margin-bottom: .4em;
}

.colonne-edition {
    min-width: 0;
}

.colonne-edition p,
.colonne-edition fieldset {
    overflow-wrap: break-word;
}

.colonne-vide {
    margin: 0;
}

.titre-edition {
    margin: 0 0 .5em;
    font-size: 1.05em;
    overflow-wrap: break-word;
}

@media (max-width: 820px) {
    .colonnes-edition {
        grid-template-columns: 1fr;
    }
}

/* ---------------------------------------------------------- LE CHOIX D'UN FICHIER
 *
 * LE CHAMP EST STYLÉ À PART, ET VOLONTAIREMENT PEU
 *
 * Les champs du site ont tous la même boîte : ce sont des champs de texte. Un
 * sélecteur de fichiers ne se style pas comme un champ de texte — le bouton
 * « Parcourir » est rendu par le système, et lui donner notre couleur ferait une
 * demi-boîte étrangère. On lui donne donc un cadre, une largeur, et rien de plus.
 */
.champ-image input[type="file"] {
    width: 100%;
    padding: 8px 0 0;
    font-size: .95em;
    color: var(--texte);
}

.champ-image .aide {
    font-weight: 400;
}

/* L'IMAGE DÉJÀ ENREGISTRÉE, ET LA CASE QUI LA RETIRE */
.image-courante {
    display: flex;
    align-items: center;
    gap: .7em;
    margin: .5em 0 0;
}

.image-courante img,
.table-editions .vignette {
    object-fit: cover;
    border: 1px solid var(--trait, #d6d2c8);
    border-radius: 6px;
    background: var(--surface-douce, #faf8ff);
}

.image-courante img {
    width: 64px;
    height: 64px;
}

.case-retirer {
    display: inline-flex;
    align-items: center;
    gap: .4em;
    margin: 0;
    font-weight: 400;
    font-size: .92em;
}

.case-retirer input[type="checkbox"] {
    margin: 0;
}

/* LA VIGNETTE DANS LE TABLEAU DES ÉDITIONS */
.table-editions .colonne-visuel {
    width: 1%;
    white-space: nowrap;
}

.table-editions .vignette {
    display: block;
    width: 52px;
    height: 52px;
}

/* ------------------------------------------------- BOOSTER : LES CARTES TIRÉES
 *
 * UNE SEULE LIGNE QUI DÉFILE, ET C'EST DÉJÀ UN MÉCANISME DU SITE
 *
 * Demande de la personne, le 2026-10-16 : les cartes d'un booster sur une seule
 * ligne, qu'on puisse faire glisser pour toutes les voir. Le défilement
 * horizontal par `overflow-x` est exactement ce que fait déjà la barre d'onglets
 * de `echanges.php` sur téléphone — c'est le défilement natif du navigateur, donc
 * le doigt, la molette et le clavier fonctionnent sans une ligne de script.
 *
 * LA BARRE DE DÉFILEMENT EST VISIBLE, ET C'EST DÉLIBÉRÉ
 *
 * Sans elle, un visiteur à la souris n'a aucun signe que la ligne continue : il
 * voit trois cartes et suppose que le booster en a trois. Le défilement natif
 * garde ses propres boutons, qu'on ne voit qu'au survol selon le système — d'où
 * `scrollbar-width: thin`, qui le rend toujours présent sans le rendre lourd.
 *
 * `scroll-snap` ARRÊTE LA CARTE AU BORD, ET NON AU PISTON
 *
 * Une carte qui s'arrête à un endroit quelconque donne l'impression que la
 * suivante est tronquée. `proximity` laisse la carte la plus proche aller au
 * bord, sans imposer un point d'arrêt à chaque pixel de mouvement.
 */
.cartes-defile {
    display: flex;
    gap: 20px;
    margin-top: 20px;
    padding-bottom: 10px;
    overflow-x: auto;
    scroll-snap-type: x proximity;
    scrollbar-width: thin;
}

.cartes-defile > .carte {
    /* `1 0 290px` : elles GRANDISSENT pour remplir la ligne, et ne rétrécissent
     * JAMAIS. Sans le zéro du milieu, un booster de vingt cartes se tasserait
     * dans la largeur de l'écran au lieu de défiler — et le membre n'en verrait
     * que huit, sans aucune raison. Avec `0 0 290px` (fixe), trois cartes
     * laissaient 120 px vides à droite : mesuré au navigateur. */
    flex: 1 0 290px;
    scroll-snap-align: start;
}

/* LA CARTE TIRÉE APPARAÎT, ET L'ANIMATION EST EN CSS
 *
 * Le boost vient d'être ouvert : la page est redessinée, et les cartes se posent
 * l'une après l'autre. 0.3 seconde, et `both` pour que le retard ne montre pas la
 * carte avant son tour — sans lui, la carte est visible pendant son délai, donc
 * l'effet de décalage n'existe pas.
 *
 * `prefers-reduced-motion` coupe tout cela en bas de fichier, comme le reste du
 * site : une partie des gens souffre des déplacements. */
@keyframes apparition-carte {
    from {
        opacity: 0;
        transform: translateY(12px) scale(.98);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

.cartes-defile > .carte {
    animation: apparition-carte .3s ease-out both;
}

.cartes-defile > .carte:nth-child(2) {
    animation-delay: .06s;
}

.cartes-defile > .carte:nth-child(3) {
    animation-delay: .12s;
}

.cartes-defile > .carte:nth-child(n + 4) {
    animation-delay: .18s;
}


/* LA GRILLE DE FICHES À COCHER
 *
 * Les tuiles du site sont faites pour une carte : photo, nom, compte
 * d'exemplaires. Ici il n'y a qu'un nom et une case, donc la tuile est plus
 * haute et moins large, et la photo est absente : cinquante vignettes
 * miniatures prendraient la place de cinquante noms, qui sont ce qu'on cherche.
 *
 * Le survol et la case cochée viennent de `.tuile` et de `.tuile:has(input:checked)`
 * qui existent déjà pour les échanges — on ne les redéfinit pas.
 */
.liste-fiches {
    margin: 0;
}

.compte-fiches {
    color: #6b6659;
    font-size: .92em;
    margin: 0 0 .7em;
}

.tuile-case {
    flex-direction: row;
    align-items: center;
    gap: .5em;
    padding: .5em .7em;
}

.tuile-case .tuile-nom {
    margin: 0;
    font-weight: 400;
}

.tuile-case input[type="checkbox"] {
    flex: none;
    margin: 0;
}

/* ------------------------------------------------- TOUT COCHER, TOUT DÉCOCHER
 *
 * IL N'EST MONTRÉ QUE SI LE SCRIPT S'EXÉCUTE, ET C'EST L'INVERSE DU BOUTON
 * « Filtrer »
 *
 * « Filtrer » est écrit dans le HTML et masqué par le script, parce qu'il a un
 * repli : sans script, le formulaire porteur renvoie la page entière. « Tout
 * cocher » n'a AUCUN repli possible — il n'y a pas de façon de cocher cinquante
 * cases avec un formulaire, seulement avec du script. Le montrer quand le script
 * manque laisserait donc un contrôle qui ne fait rien, ce qui est pire que de
 * ne pas l'avoir.
 *
 * D'où `.avec-selection`, posée sur `<html>` par le script de la page — le même
 * mécanisme que `.avec-onglets` dans `echanges.php`. La feuille cache ; la classe
 * montre. Aucun des deux ne dépend de l'ordre de chargement.
 *
 *
 * UN BOUTON QUI A L'ASPECT D'UN LIEN, ET PAS UN LIEN
 *
 * La règle générale des boutons s'écrase presque entièrement : le lien est posé
 * dans une range de texte, entre un libellé et une phrase d'aide, et une capsule
 * violette de 460 px y serait une bannière. Ce qui reste de la règle générale —
 * le contour au clavier, `button:focus-visible` — n'est pas touché : c'est le
 * seul endroit du site où l'on ne voit pas où l'on est, donc il doit se voir
 * double.
 *
 * Le `transform` du survol est neutralisé pour la même raison : se lever de deux
 * pixels sous une ligne de texte fait bouger le texte autour, et ce mouvement
 * n'apprend rien.
 */

.bouton-selection {
    display: none;
}

.avec-selection .bouton-selection {
    display: inline-block;
    width: auto;
    max-width: none;
    margin: 0;
    padding: 0;
    font-size: 14px;
    font-weight: 700;
    color: var(--violet);
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    text-decoration: underline;
    transition: none;
}

.avec-selection .bouton-selection:hover {
    transform: none;
    box-shadow: none;
    text-decoration: none;
}

.avec-selection .bouton-selection:active {
    transform: none;
    box-shadow: none;
}

/* Désactivé quand la grille est vide : il garde sa place, et ne promet rien. */
.avec-selection .bouton-selection:disabled {
    color: var(--texte-doux);
    cursor: default;
    text-decoration: none;
}

/* La range : le bouton, le compte réservé au lecteur d'écran, et la phrase qui
 * dit sur quoi le bouton agit. L'aide est en dessous sur un téléphone — une
 * range qui garde une place vide au milieu d'un texte qui se replie est pire
 * qu'une range qui se replie aussi. */
.selection-fiches {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px 12px;
    margin: 16px 0 0;
}

.selection-fiches .aide {
    flex: 1 1 260px;
    margin: 0;
}

.liste-fiches[aria-busy="true"] {
    opacity: .55;
    transition: opacity .12s ease-out;
}

/* -------------------------------------------------------------- PAGINATION
 *
 * Trois elements seulement : precedent, etat, suivant. Pas de numerotation de
 * toutes les pages — a quinze elements par page et cent amis au maximum, elle
 * ferait sept liens pour un ensemble qu'on peut parcourir en trois clics, et la
 * barre passerait plus de place que la liste.
 */

.pagination {
    display: flex;
    align-items: center;
    gap: 16px;
    margin: 14px 0 0;
}

.page-lien {
    font-weight: 700;
    color: var(--violet);
    text-decoration: none;
}

.page-lien:hover {
    text-decoration: underline;
}

.page-etat {
    font-size: 13px;
    color: var(--texte-doux);
}

/* Le rappel de la regle, avant le bouton. */
.aide-proposer {
    margin: 20px 0 8px;
}

/* MOUVEMENT REDUIT.
 *
 * Les fiches qui se soulèvent et pivotent, les boutons qui s'enfoncent, les
 * onglets qui changent de couleur : tout cela est du deplacement, et une
 * partie des gens en souffre. Le systeme le signale, on l'ecoute.
 *
 * Les couleurs, elles, restent : elles ne bougent pas et ne peuvent nuire.
 */
@media (prefers-reduced-motion: reduce) {
    .carte:hover {
        transform: none;
    }

    button:hover,
    .generer:hover,
    .menu a {
        transform: none;
    }

    /* LE VISUEL DU BOOSTER ET L'APPARITION DES CARTES
     *
     * Les deux mouvements du 2026-10-16 : la carte d'édition qui se soulève, et
     * les cartes tirées qui se posent. Le second est une animation, pas une
     * transition : sans `animation: none`, `both` garderait les cartes a
     * l'opacite zero. */
    .edition-bouton:hover,
    .edition-bouton:active {
        transform: none;
    }

    .cartes-defile > .carte {
        animation: none;
    }

    .carte,
    button,
    .generer,
    .menu a,
    input[type="text"],
    input[type="password"],
    input[type="email"] {
        transition: none;
    }
}
