/* ═══════════════════════════════════════════════════════════════════════
   FONDATIONS — un seul endroit où sont décidés couleurs, échelle
   typographique, espacements, rayons et durées. Toutes les vues piochent
   ici : c'est ce qui fait que l'application se tient d'un écran à l'autre.
   Thème sombre uniquement, polices système uniquement (aucun
   téléchargement, l'application doit s'ouvrir instantanément en 4G).
   ═══════════════════════════════════════════════════════════════════════ */
:root {
  /* — Surfaces. Trois niveaux seulement : le fond, la carte posée dessus,
       la carte survolée/active. Au-delà, les plans deviennent illisibles
       sur un écran de téléphone en plein jour. */
  --fond: #0f1117;
  --fond-creux: #0a0c11;          /* champs de saisie, cases où déposer */
  --carte: #141821;
  --carte-haute: #1a1f2b;
  --bord: #232a38;
  --bord-doux: #1b2130;           /* séparateur discret à l'intérieur d'une carte */

  /* — Texte. Trois niveaux de voix : le titre, le corps, l'annotation. */
  --texte: #e6e8ee;
  --texte-fort: #f4f6fb;
  --doux: #8b93a7;
  --tres-doux: #626b7f;

  --accent: #f5c518;
  --accent-creux: #f5c5181f;      /* fond d'un état actif à l'accent */
  --danger: #ff8f8f;

  /* — Les sept couleurs de verdict, reprises telles quelles de modele.js.
       Les vues les posent aussi en style inline (elles viennent du modèle
       JS) ; les avoir aussi en variables permet à la CSS seule de teinter
       un fond, une bordure ou un halo sans repasser par le JavaScript. */
  --tier-S: #f5c518;
  --tier-A: #8bc34a;
  --tier-B: #4a9fd8;
  --tier-C: #7e8bd8;
  --tier-D: #a678c8;
  --tier-E: #8a8a9a;
  --tier-F: #c86a6a;
  --sur-tier: #10121a;            /* texte posé sur une pastille de verdict */

  /* — Échelle typographique. Sept pas, pas un de plus : chaque taille a un
       emploi nommé, sinon on retombe sur « tout en 15px ». */
  --t-micro: 11px;                /* sur-titre, compteur, mention légale */
  --t-mini: 12px;                 /* en-tête de tableau, étiquette de tag */
  --t-annot: 13px;                /* annotation, texte secondaire */
  --t-corps: 15px;                /* corps de texte, valeur par défaut */
  --t-corps-fort: 16px;           /* saisie (16px = pas de zoom auto iOS) */
  --t-titre-s: 17px;              /* titre de section */
  --t-titre-m: 20px;              /* titre de fenêtre */
  --t-titre-l: 26px;              /* titre de fiche, accueil */
  --t-affiche: 32px;              /* chiffre d'accueil, lettre de tier */

  --interligne-serre: 1.25;
  --interligne: 1.55;
  --interligne-lu: 1.65;          /* blocs qu'on lit vraiment (ressenti, résumé) */

  /* — Espacement : une seule suite, pour que les marges se répondent. */
  --e1: 4px; --e2: 8px; --e3: 12px; --e4: 16px; --e5: 24px; --e6: 32px; --e7: 48px;

  --r1: 6px; --r2: 9px; --r3: 12px; --r4: 16px; --r-plein: 999px;

  --ombre-1: 0 1px 2px #0006;
  --ombre-2: 0 6px 18px -6px #000a;
  --ombre-3: 0 20px 50px -12px #000c;

  /* — Mouvement : court et discret. Une seule courbe pour tout. */
  --duree: 170ms;
  --duree-courte: 120ms;
  --duree-fond: 420ms;             /* fondu du fond d'univers, plus lent :
                                      une atmosphère ne doit pas claquer */
  --courbe: cubic-bezier(.2, .7, .3, 1);
}

* { box-sizing: border-box; }

body { margin: 0; background: var(--fond); color: var(--texte);
       font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
       font-size: var(--t-corps); line-height: var(--interligne);
       -webkit-text-size-adjust: 100%;
       /* Aucune vue ne doit jamais déborder latéralement : le geste central
          du produit est de remplir une fiche sur un téléphone. */
       overflow-x: hidden; }

img { max-width: 100%; }
button, input, select, textarea { font-family: inherit; }

/* Hiérarchie de base : les vues n'ont plus qu'à choisir le bon élément. */
h1, h2, h3 { margin: 0; line-height: var(--interligne-serre); color: var(--texte-fort);
             letter-spacing: -.01em; }
h1 { font-size: var(--t-titre-l); font-weight: 700; letter-spacing: -.02em; }
h2 { font-size: var(--t-titre-m); font-weight: 700; }
h3 { font-size: var(--t-titre-s); font-weight: 650; }

/* Sur-titre : la plus petite voix, celle qui nomme une section sans crier. */
.surtitre { font-size: var(--t-micro); font-weight: 700; letter-spacing: .09em;
            text-transform: uppercase; color: var(--tres-doux); }

/* Visible pour les lecteurs d'écran seulement (nommer une section dont le
   titre est porté par un dessin plutôt que par du texte). */
.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; }

/* Le clavier doit voir où il est, la souris ne doit pas être encadrée. */
:focus { outline: none; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Un mouvement est un confort, jamais une information : quand le système
   demande moins d'animation, tout devient instantané. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important; scroll-behavior: auto !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   COQUILLE — l'en-tête collant (catégories + bouton d'ajout), la barre de
   modes, et le cadre de la zone d'écran.
   ═══════════════════════════════════════════════════════════════════════ */

/* Avant : tout l'en-tête défilait horizontalement, si bien qu'à 375 px
   « + Ajouter » sortait de l'écran dès la troisième catégorie — le geste
   principal du produit devenait invisible. Désormais seule la liste des
   catégories défile ; le bouton reste ancré à droite. */
#entete { display: flex; align-items: center; gap: var(--e3);
          padding: var(--e2) max(var(--e3), env(safe-area-inset-right))
                   var(--e2) max(var(--e3), env(safe-area-inset-left));
          border-bottom: 1px solid var(--bord); position: sticky; top: 0; z-index: 10;
          background: color-mix(in srgb, var(--fond) 88%, transparent);
          backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); }

#onglets { display: flex; gap: var(--e1); flex: 1 1 auto; min-width: 0;
           overflow-x: auto; scrollbar-width: none;
           /* Le dernier onglet s'estompe au lieu d'être tranché net : on
              comprend qu'il reste des catégories à droite. */
           -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 20px), transparent);
                   mask-image: linear-gradient(to right, #000 calc(100% - 20px), transparent); }
#onglets::-webkit-scrollbar { display: none; }

.onglet { background: none; border: 0; color: var(--doux); font-size: var(--t-corps);
          font-weight: 550; padding: var(--e2) var(--e3) calc(var(--e2) + 2px);
          border-radius: var(--r1); cursor: pointer; white-space: nowrap;
          position: relative; flex: 0 0 auto;
          transition: color var(--duree-courte) var(--courbe); }
.onglet:hover { color: var(--texte); }
/* La catégorie courante est la navigation de premier rang : un trait sous
   le mot, pas une pastille — les pastilles pleines sont réservées aux
   modes, un cran en dessous dans la hiérarchie. */
.onglet::after { content: ''; position: absolute; left: var(--e3); right: var(--e3); bottom: 0;
                 height: 2px; border-radius: 2px; background: var(--accent);
                 transform: scaleX(0); opacity: 0;
                 transition: transform var(--duree) var(--courbe), opacity var(--duree) var(--courbe); }
.onglet.actif { color: var(--texte-fort); font-weight: 700; }
.onglet.actif::after { transform: scaleX(1); opacity: 1; }

#bouton-ajout { background: var(--accent); color: var(--sur-tier); border: 0; font-weight: 700;
                font-size: var(--t-annot); letter-spacing: .01em;
                padding: var(--e2) var(--e4); border-radius: var(--r-plein); cursor: pointer;
                white-space: nowrap; flex: 0 0 auto; box-shadow: var(--ombre-1);
                transition: transform var(--duree-courte) var(--courbe),
                            filter var(--duree-courte) var(--courbe); }
#bouton-ajout:hover { filter: brightness(1.08); }
#bouton-ajout:active { transform: scale(.96); }

/* Sur grand écran, une colonne mesurée vaut mieux qu'un contenu étiré sur
   toute la largeur : les rangées de tier list deviennent sinon des rubans
   de trente vignettes illisibles. */
#ecran { padding: var(--e4) max(var(--e4), env(safe-area-inset-left)) var(--e7)
                  max(var(--e4), env(safe-area-inset-right));
         max-width: 1120px; margin: 0 auto; }

.vide { color: var(--doux); text-align: center; padding: var(--e7) var(--e4);
        font-size: var(--t-annot); }

/* ═══════════════════════════════════════════════════════════════════════
   ACCUEIL D'UNE CATÉGORIE VIDE (vue-vide.js) — le tout premier écran.
   Il doit répondre à « c'est quoi, cette application ? » sans une ligne de
   documentation : l'échelle S→F dessinée montre à quoi ressemblera la
   collection remplie, les trois étapes disent ce qu'on attend, le bouton
   évite d'aller chercher celui de l'en-tête.
   ═══════════════════════════════════════════════════════════════════════ */
.accueil { max-width: 30rem; margin: 0 auto; padding: var(--e6) 0 var(--e7);
           text-align: center; }

/* L'illustration : sept rangées aux couleurs de verdict, avec des fentes
   d'affiche vides. Dessinée en CSS — aucune image à télécharger sur le
   premier écran d'une application qu'on ouvre en 4G. */
.acc-echelle { display: flex; flex-direction: column; gap: var(--e1);
               width: max-content; margin: 0 auto var(--e5);
               -webkit-mask-image: linear-gradient(#000 70%, #0008);
                       mask-image: linear-gradient(#000 70%, #0008); }
.acc-rangee { display: flex; align-items: center; gap: var(--e2);
              /* Chaque rangée s'efface un peu plus que la précédente : le
                 regard part du haut, là où se trouve le titre. F reste
                 lisible — l'échelle doit se lire en entier, S à F. */
              opacity: calc(1 - var(--rang) * .07);
              animation: acc-monte 420ms var(--courbe) backwards;
              animation-delay: calc(var(--rang) * 45ms); }
.acc-tier { width: 26px; height: 26px; border-radius: var(--r1); flex: 0 0 26px;
            display: flex; align-items: center; justify-content: center;
            font-size: var(--t-annot); font-weight: 800; color: var(--sur-tier);
            background: var(--teinte); }
.acc-fentes { display: flex; gap: var(--e1); }
.acc-fentes i { display: block; width: 22px; height: 33px; border-radius: 3px;
                background: color-mix(in srgb, var(--teinte) 14%, var(--carte));
                border: 1px solid color-mix(in srgb, var(--teinte) 26%, transparent); }
@keyframes acc-monte { from { opacity: 0; transform: translateY(6px); } }

.acc-titre { font-size: var(--t-titre-l); margin-top: var(--e2); }
.acc-texte { color: var(--doux); font-size: var(--t-corps);
             line-height: var(--interligne-lu); margin: var(--e3) 0 var(--e5);
             text-wrap: pretty; }
.acc-cta { width: auto; min-width: 15rem; padding: var(--e3) var(--e5); }

/* Les trois étapes : alignées à gauche même si le bloc est centré — une
   liste numérotée centrée ne se lit pas. */
.acc-etapes { list-style: none; margin: var(--e6) 0 0; padding: 0;
              text-align: left; display: flex; flex-direction: column; gap: var(--e4); }
.acc-etapes li { display: flex; gap: var(--e3); align-items: flex-start;
                 font-size: var(--t-annot); line-height: var(--interligne); }
.acc-etapes b { font-size: var(--t-corps); font-weight: 650; color: var(--texte-fort); }
.acc-num { flex: 0 0 26px; width: 26px; height: 26px; border-radius: var(--r-plein);
           display: flex; align-items: center; justify-content: center;
           background: var(--accent-creux); color: var(--accent);
           font-size: var(--t-mini); font-weight: 700; margin-top: 2px; }

/* ═══════════════════════════════════════════════════════════════════════
   RANGÉES DE TIER LIST (vue-globale.js, vue-tierlist.js) et AFFICHES.
   Une œuvre se reconnaît à son affiche : c'est le contenu du produit, pas
   une décoration. Elles ont donc grandi (48 → 60 px sur téléphone, 92 px
   sur grand écran), respectent leur format 2/3 et gardent un traitement
   digne quand elles manquent.
   Chaque rangée reçoit `--teinte`, la couleur de son verdict, posée par le
   JavaScript depuis modele.js : étiquette, case de dépôt et halo de survol
   y puisent tous.
   ═══════════════════════════════════════════════════════════════════════ */
:root { --vign: 60px; --etiq: 50px; }
@media (min-width: 560px) { :root { --vign: 78px; --etiq: 68px; } }
@media (min-width: 900px) { :root { --vign: 92px; --etiq: 76px; } }

.rangee { display: flex; gap: var(--e2); margin-bottom: var(--e2); align-items: stretch; }
.rangee .etiquette { width: var(--etiq); flex: 0 0 var(--etiq); border-radius: var(--r2);
                     color: var(--sur-tier);
                     display: flex; flex-direction: column; align-items: center;
                     justify-content: center; gap: 2px; padding: var(--e2) 4px;
                     text-align: center; }
/* Deux voix dans l'étiquette : la lettre se lit de loin, le nom du verdict
   se lit quand on le cherche. Avant, les deux étaient dans le même bloc de
   texte gras et se disputaient l'attention. */
.rangee .etiquette b { font-size: var(--t-titre-m); font-weight: 800; line-height: 1;
                       letter-spacing: -.02em; }
.rangee .etiquette small { font-size: 8px; font-weight: 700; letter-spacing: .04em;
                           line-height: 1.15; opacity: .72; }
@media (min-width: 560px) {
  .rangee .etiquette b { font-size: var(--t-affiche); }
  .rangee .etiquette small { font-size: var(--t-micro); }
}

.rangee .contenu { flex: 1; min-width: 0; background: var(--carte);
                   border: 1px solid var(--bord-doux); border-radius: var(--r2);
                   padding: var(--e2); display: flex; gap: var(--e2); flex-wrap: wrap;
                   min-height: calc(var(--vign) * 1.5 + var(--e4));
                   transition: background var(--duree-courte) var(--courbe),
                               border-color var(--duree-courte) var(--courbe); }
/* Rangée vide d'une tier list : elle reste affichée puisque c'est là qu'on
   dépose, mais elle doit se lire comme une case en attente, pas comme un
   bloc plein. */
.rangee .contenu:empty { background: color-mix(in srgb, var(--teinte, var(--bord)) 5%, var(--fond-creux));
                         border-style: dashed; }

/* — L'affiche. Le conteneur porte le fond de secours (le titre, via
     data-titre) ; l'image se pose par-dessus. Une image qui ne charge pas
     est masquée par app.js et laisse simplement réapparaître ce fond :
     jamais d'icône d'image cassée. */
.vignette { position: relative; width: var(--vign); flex: 0 0 var(--vign);
            aspect-ratio: 2 / 3; border-radius: var(--r1); overflow: hidden;
            cursor: pointer; isolation: isolate;
            background: linear-gradient(155deg,
                        color-mix(in srgb, var(--teinte, var(--doux)) 12%, var(--carte-haute)),
                        var(--carte-haute));
            box-shadow: inset 0 0 0 1px #ffffff14, var(--ombre-1);
            transition: transform var(--duree) var(--courbe),
                        box-shadow var(--duree) var(--courbe); }
.vignette::after { content: attr(data-titre); position: absolute; inset: 0; z-index: -1;
                   display: flex; align-items: center; justify-content: center;
                   padding: var(--e1) 5px; text-align: center; text-wrap: balance;
                   font-size: 10px; line-height: 1.25; font-weight: 600;
                   color: var(--doux); overflow: hidden; }
.vignette img { position: absolute; inset: 0; width: 100%; height: 100%;
                object-fit: cover; display: block; }

/* Toutes les « affiches » ne sont pas des jaquettes verticales. Wikipédia
   sert régulièrement le logo du jeu à la place de la boîte (500x163 pour
   Wuthering Waves, 546x182 pour Pokémon Jaune) et certaines sources donnent
   des visuels carrés. Rogner ça au format 2:3 n'en montre qu'une bande
   centrale illisible — souvent deux lettres d'un titre.
   Ces images-là sont donc affichées entières, centrées sur un fond neutre.
   La classe est posée par l'écouteur global de app.js, qui mesure l'image
   une fois chargée : le serveur ne connaît pas ses dimensions, et les
   vignettes sont créées et détruites en permanence. */
/* Deux boites cote a cote : les pages Wikipedia des Pokemon presentent les
   deux versions d'un jeu sur une seule image (446x223 pour Epee et
   Bouclier, 476x209 pour X et Y). On cadre sur la gauche, qui est toujours
   la version nommee en premier — donc celle de la fiche. La case est
   remplie, au lieu d'une bande fine perdue au milieu du vide. */
img.affiche-double { object-fit: cover; object-position: 15% 50%; }

/* Au-dela, aucun recadrage n'a de sens : le logo de Minecraft fait 763x131,
   en rogner un tiers ne donnerait que deux lettres. Image entiere. */
img.affiche-large { object-fit: contain !important; padding: 8%; }

/* Le vide autour est comble par une copie floutee et agrandie de l'image
   elle-meme, recouverte d'un voile. Deux bandes noires donnaient
   l'impression d'une vignette cassee ; la, le cadre parait voulu.
   Le voile n'est PAS fixe : il est choisi contre la luminosite mesuree de
   l'image (voir luminanceAffiche dans app.js). Le logo de Wuthering Waves
   est noir sur transparent — sur un panneau sombre, et sur une copie
   floutee de lui-meme donc noire aussi, il etait invisible. */
.a-fond-flou::before {
  content: ''; position: absolute; inset: 0; z-index: 0;
  background-image: linear-gradient(var(--voile, rgba(9, 11, 16, .58)),
                                    var(--voile, rgba(9, 11, 16, .58))),
                    var(--fond-affiche);
  background-size: cover; background-position: center;
  filter: blur(16px) saturate(1.3);
  /* L'agrandissement evite que le flou laisse voir les bords de l'image. */
  transform: scale(1.35);
}
.a-fond-flou img.affiche-large { z-index: 1; }
/* Marquée par l'écouteur global d'app.js quand le chargement échoue.
   `visibility` et non `display` : la place de l'image reste occupée, donc
   aucune ligne ne se réorganise sous les yeux, et le fond de secours du
   conteneur (le titre) redevient visible. */
img.image-cassee { visibility: hidden; }

.vignette:hover { transform: translateY(-3px);
                  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--teinte, var(--accent)) 60%, transparent),
                              0 8px 20px -6px #000c; }
.vignette:active { transform: translateY(-1px) scale(.98); }
/* Une entrée qui pointe vers une fiche supprimée : visiblement inerte. */
.vignette-fantome { opacity: .5; border-style: dashed; }

/* Sur téléphone, un doigt ne survole pas : garder l'effet donnerait une
   vignette « collée » en haut après chaque tap. */
@media (hover: none) {
  .vignette:hover { transform: none; box-shadow: inset 0 0 0 1px #ffffff14, var(--ombre-1); }
}

/* ═══════════════════════════════════════════════════════════════════════
   LA FICHE (vue-fiche.js) — l'écran où Guillaume passe le plus de temps.
   Elle doit se lire comme une page : identité en tête, sections annoncées
   par un sur-titre, zones de saisie qui ne crient pas « formulaire » tant
   qu'on ne les touche pas.
   ═══════════════════════════════════════════════════════════════════════ */
.boite-fiche { max-width: 620px; }

.fiche-tete { display: flex; gap: var(--e4); margin-bottom: var(--e5); align-items: flex-start; }
/* Conteneur, plus une balise <img> nue : il porte le titre en fond de
   secours (data-titre), comme les vignettes, et tient sa place même sans
   affiche ou quand elle ne charge pas. */
.fiche-affiche { position: relative; width: 104px; flex: 0 0 104px; aspect-ratio: 2 / 3;
                 border-radius: var(--r3); overflow: hidden; isolation: isolate;
                 background: linear-gradient(155deg, var(--carte-haute), var(--fond-creux));
                 box-shadow: inset 0 0 0 1px #ffffff14, var(--ombre-2); }
.fiche-affiche::after { content: attr(data-titre); position: absolute; inset: 0; z-index: -1;
                        display: flex; align-items: center; justify-content: center;
                        padding: var(--e2); text-align: center; text-wrap: balance;
                        font-size: var(--t-mini); line-height: 1.3; color: var(--doux); }
.fiche-affiche img { position: absolute; inset: 0; width: 100%; height: 100%;
                     object-fit: cover; display: block; }
@media (min-width: 480px) { .fiche-affiche { width: 132px; flex: 0 0 132px; } }

.fiche-identite { min-width: 0; padding-top: 2px; }
.fiche-titre { font-size: var(--t-titre-m); text-wrap: balance; }
@media (min-width: 480px) { .fiche-titre { font-size: var(--t-titre-l); } }
/* Année et durée : la ligne qu'on lit en diagonale, en chiffres alignés. */
.fiche-meta { margin: var(--e2) 0 0; color: var(--doux); font-size: var(--t-annot);
              font-variant-numeric: tabular-nums; }
.fiche-createurs { margin: var(--e1) 0 0; font-size: var(--t-annot);
                   line-height: var(--interligne); color: var(--doux); }

/* Le menu de verdict prend la couleur du tier choisi, et la suit (peinte
   par vue-fiche.js) : c'est le seul écran où le verdict se décide, il
   serait absurde qu'il y soit la seule chose en gris. */
.champ-verdict { font-weight: 650; color: var(--texte-fort); margin-bottom: 0;
                 border-color: color-mix(in srgb, var(--teinte, var(--bord)) 55%, transparent);
                 background-color: color-mix(in srgb, var(--teinte, var(--carte)) 13%, var(--fond-creux));
                 transition: border-color var(--duree) var(--courbe),
                             background-color var(--duree) var(--courbe); }
.champ-verdict:hover { border-color: color-mix(in srgb, var(--teinte, var(--bord)) 80%, transparent); }

/* Zone de lecture-écriture (résumé, ressenti) : tant qu'on la lit, ce n'est
   qu'un bloc de texte posé sur la page ; le cadre n'apparaît qu'au survol
   et au focus, quand on s'apprête à écrire. */
.champ-lecture { background: color-mix(in srgb, var(--carte-haute) 55%, transparent);
                 border-color: transparent; font-size: var(--t-corps);
                 line-height: var(--interligne-lu); padding: var(--e3) var(--e3); }
.champ-lecture:hover { border-color: var(--bord); background: var(--carte-haute); }
.champ-lecture:focus { background: var(--fond-creux); border-color: var(--accent); }

/* Interrupteur « À revoir » : une ligne cliquable entière, pas une case à
   cocher perdue à côté de deux mots. */
.bascule { display: flex; gap: var(--e3); align-items: flex-start; cursor: pointer;
           margin: var(--e5) 0 0; padding: var(--e3); border-radius: var(--r2);
           border: 1px solid var(--bord-doux); background: var(--fond-creux);
           font-size: var(--t-annot); line-height: var(--interligne);
           transition: border-color var(--duree-courte) var(--courbe); }
.bascule:hover { border-color: var(--tres-doux); }
.bascule input { width: 19px; height: 19px; accent-color: var(--accent);
                 flex: 0 0 auto; margin: 1px 0 0; }
.bascule b { font-size: var(--t-corps); font-weight: 650; color: var(--texte-fort); }
.bascule:has(input:checked) { border-color: color-mix(in srgb, var(--accent) 45%, transparent);
                              background: var(--accent-creux); }

.fiche-etat { margin: var(--e3) 0 0; text-align: center; min-height: 1.2em; }
/* ═══════════════════════════════════════════════════════════════════════
   COMPOSANTS COMMUNS — étiquettes, boutons, fenêtres, champs de saisie.
   Tout ce qui apparaît dans plus d'une vue vit ici, une seule fois.
   ═══════════════════════════════════════════════════════════════════════ */

/* — Tags (genres) : une annotation, jamais un bouton. */
.tags { margin: var(--e3) 0 0; display: flex; flex-wrap: wrap; gap: var(--e1); }
.tag { display: inline-block; background: var(--carte-haute); color: var(--doux);
       font-size: var(--t-mini); line-height: 1.4; padding: 3px var(--e2);
       border-radius: var(--r-plein); border: 1px solid var(--bord-doux); }

/* — Étiquette d'un champ. Une section de fiche s'annonce par un sur-titre
     discret, pas par une phrase de la même taille que son contenu : c'est
     ce qui permet de balayer une fiche du regard sans la lire. */
.etiquette-champ { display: block; font-size: var(--t-micro); font-weight: 700;
                   letter-spacing: .09em; text-transform: uppercase; color: var(--tres-doux);
                   margin: var(--e5) 0 var(--e2); }
label.doux { display: block; margin: var(--e3) 0 var(--e1); }

/* — Boutons. Trois rôles : l'action principale, l'action destructrice,
     l'action secondaire (.petit). */
.actions { display: flex; gap: var(--e2); margin-top: var(--e5); }
.principal { flex: 1; background: var(--accent); color: var(--sur-tier); border: 0;
             font-weight: 700; font-size: var(--t-corps); padding: var(--e3) var(--e4);
             border-radius: var(--r2); cursor: pointer;
             transition: filter var(--duree-courte) var(--courbe),
                         transform var(--duree-courte) var(--courbe); }
.principal:hover { filter: brightness(1.08); }
.principal:active { transform: scale(.98); }
.principal:disabled { opacity: .55; cursor: default; transform: none; filter: none; }
.danger { background: none; border: 1px solid #5a2b2b; color: var(--danger);
          padding: var(--e3) var(--e4); border-radius: var(--r2); cursor: pointer;
          font-size: var(--t-annot); font-weight: 600;
          transition: background var(--duree-courte) var(--courbe),
                      border-color var(--duree-courte) var(--courbe); }
.danger:hover { background: #ff8f8f14; border-color: #7a3a3a; }

/* — Fenêtres. Feuille qui monte du bas sur téléphone (le pouce est en
     bas), boîte centrée dès 640 px. */
.voile { position: fixed; inset: 0; background: #000000b8; display: flex;
         align-items: flex-end; justify-content: center; z-index: 50;
         backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
         animation: voile-apparait var(--duree) var(--courbe); }
@keyframes voile-apparait { from { opacity: 0; } }

.boite { background: var(--carte); border: 1px solid var(--bord); width: 100%;
         max-width: 520px; max-height: 92dvh; overflow-y: auto;
         border-radius: var(--r4) var(--r4) 0 0; box-shadow: var(--ombre-3);
         /* La feuille monte : c'est le seul mouvement qui dit d'où elle
            vient et où le geste de fermeture la renverra. */
         animation: boite-monte 220ms var(--courbe);
         padding-bottom: env(safe-area-inset-bottom); }
@keyframes boite-monte { from { transform: translateY(16px); opacity: .4; } }

.boite-entete { display: flex; justify-content: space-between; align-items: center;
                gap: var(--e3); padding: var(--e3) var(--e4);
                border-bottom: 1px solid var(--bord-doux);
                font-size: var(--t-corps); font-weight: 650; color: var(--texte-fort);
                position: sticky; top: 0; z-index: 2;
                background: color-mix(in srgb, var(--carte) 92%, transparent);
                backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); }
/* Le titre d'une fenêtre peut être long (« Ajouter à "Films de Nolan" ») :
   il se coupe proprement plutôt que de pousser la croix hors de l'écran. */
.boite-entete > span { min-width: 0; overflow: hidden; text-overflow: ellipsis;
                       white-space: nowrap; }
.boite-entete .fermer { background: none; border: 1px solid transparent; color: var(--doux);
                        font-size: var(--t-annot); line-height: 1; cursor: pointer;
                        width: 30px; height: 30px; border-radius: var(--r-plein);
                        flex: 0 0 auto;
                        transition: background var(--duree-courte) var(--courbe),
                                    color var(--duree-courte) var(--courbe); }
.boite-entete .fermer:hover { background: var(--carte-haute); color: var(--texte); }
.boite-corps { padding: var(--e4) var(--e4) var(--e6); }

/* — Champs de saisie. 16 px : en dessous, iOS zoome au premier tap et ne
     dézoome jamais. */
.champ { width: 100%; background: var(--fond-creux); border: 1px solid var(--bord);
         color: var(--texte); border-radius: var(--r2); padding: var(--e3);
         font-size: var(--t-corps-fort); line-height: var(--interligne);
         margin-bottom: var(--e3);
         transition: border-color var(--duree-courte) var(--courbe),
                     background var(--duree-courte) var(--courbe); }
.champ::placeholder { color: var(--tres-doux); }
.champ:hover { border-color: var(--tres-doux); }
.champ:focus { outline: none; border-color: var(--accent); background: var(--fond); }
textarea.champ { resize: vertical; font: inherit; font-size: var(--t-corps-fort);
                 line-height: var(--interligne-lu); }
select.champ { appearance: none; -webkit-appearance: none; cursor: pointer;
               /* Chevron dessiné en dégradés : aucune image à charger. */
               background-image:
                 linear-gradient(45deg, transparent 50%, var(--doux) 50%),
                 linear-gradient(135deg, var(--doux) 50%, transparent 50%);
               background-position: right 18px center, right 13px center;
               background-size: 5px 5px, 5px 5px;
               background-repeat: no-repeat; padding-right: var(--e6); }

/* — Ligne de candidat (résultat de recherche, œuvre à ajouter, action de
     menu). Réutilisée telle quelle par quatre vues. */
.candidat { display: flex; gap: var(--e3); align-items: center; width: 100%;
            background: var(--fond-creux); border: 1px solid var(--bord); color: var(--texte);
            border-radius: var(--r2); padding: var(--e2); margin-bottom: var(--e2);
            cursor: pointer; text-align: left; font-size: var(--t-corps);
            transition: background var(--duree-courte) var(--courbe),
                        border-color var(--duree-courte) var(--courbe),
                        transform var(--duree-courte) var(--courbe); }
.candidat:hover { background: var(--carte-haute); border-color: var(--tres-doux); }
.candidat:active { transform: scale(.99); }
.candidat:disabled { opacity: .5; cursor: default; }
.candidat img, .sans-image-mini { width: 38px; height: 57px; object-fit: cover;
                                  border-radius: var(--r1); background: var(--carte-haute);
                                  flex: 0 0 38px;
                                  box-shadow: inset 0 0 0 1px #ffffff14; }
.candidat-texte { min-width: 0; }
.candidat-texte b { font-weight: 650; }
.candidat-texte small { color: var(--doux); font-size: var(--t-annot); }
.candidat.manuel { justify-content: center; color: var(--doux); border-style: dashed;
                   background: none; }
.candidat.manuel:hover { color: var(--texte); }

/* — Les sept verdicts en boutons pleins (choix à la création, menu
     « déplacer vers » d'une tier list, tri des personnages). */
.verdicts { display: flex; flex-direction: column; gap: var(--e2); }
.verdict { border: 0; border-radius: var(--r2); padding: var(--e3) var(--e4);
           font-size: var(--t-corps); font-weight: 700; color: var(--sur-tier);
           cursor: pointer; text-align: left;
           transition: transform var(--duree-courte) var(--courbe),
                       filter var(--duree-courte) var(--courbe); }
.verdict:hover { filter: brightness(1.1); transform: translateX(3px); }
.verdict:disabled { opacity: .45; cursor: default; transform: none; filter: none; }

@media (min-width: 640px) {
  .voile { align-items: center; }
  .boite { border-radius: var(--r4); animation-name: boite-grandit; }
  @keyframes boite-grandit { from { transform: scale(.97); opacity: .4; } }
}

/* Barre de modes : Vue globale, une pastille par tier list, Vue liste,
   + nouvelle. Navigation de second rang, d'où la pastille plutôt que le
   trait souligné des onglets de catégorie. */
#modes { display: flex; gap: var(--e2); overflow-x: auto; scrollbar-width: none;
         padding: var(--e3) max(var(--e4), env(safe-area-inset-left))
                  var(--e3) max(var(--e4), env(safe-area-inset-right));
         border-bottom: 1px solid var(--bord-doux);
         -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 20px), transparent);
                 mask-image: linear-gradient(to right, #000 calc(100% - 20px), transparent); }
#modes::-webkit-scrollbar { display: none; }
.mode { background: var(--carte); border: 1px solid var(--bord); color: var(--doux);
        font-size: var(--t-annot); font-weight: 550;
        padding: var(--e2) var(--e3); border-radius: var(--r-plein); cursor: pointer;
        white-space: nowrap; flex: 0 0 auto;
        transition: color var(--duree-courte) var(--courbe),
                    background var(--duree-courte) var(--courbe),
                    border-color var(--duree-courte) var(--courbe),
                    transform var(--duree-courte) var(--courbe); }
.mode:hover { color: var(--texte); border-color: var(--tres-doux); }
.mode:active { transform: scale(.97); }
.mode.actif { color: var(--sur-tier); background: var(--accent); border-color: var(--accent); font-weight: 700; }
/* Le compteur d'œuvres est une annotation, pas une valeur : il se lit
   après le nom de la tier list, jamais avant. */
.mode small { font-size: var(--t-micro); font-weight: 700; opacity: .7; margin-left: var(--e1);
              font-variant-numeric: tabular-nums; }
.mode-nouvelle { border-style: dashed; background: none; color: var(--tres-doux); }
.mode-nouvelle:hover { color: var(--accent); border-color: var(--accent); }

/* Vue d'une tier list : barre d'actions, rangées toujours affichées y
   compris vides (case où déposer), glisser-déposer natif. */
.barre-tierliste { display: flex; gap: var(--e2); margin-bottom: var(--e4); flex-wrap: wrap; }
.barre-tierliste .principal { flex: 0 1 auto; }

/* Bouton secondaire, employé partout : ajouter un moment, un acte, un
   personnage, un lien. Jusqu'ici `class="petit"` seul ne portait ni fond ni
   bordure — le navigateur servait son bouton gris par défaut, au milieu
   d'une interface sombre. */
.petit { padding: var(--e2) var(--e3); font-size: var(--t-annot); font-weight: 600;
         border-radius: var(--r-plein); cursor: pointer;
         background: var(--carte-haute); border: 1px solid var(--bord); color: var(--doux);
         transition: color var(--duree-courte) var(--courbe),
                     border-color var(--duree-courte) var(--courbe); }
.petit:hover { color: var(--texte); border-color: var(--tres-doux); }
/* …sauf quand il précise un rôle déjà défini (.principal.petit, .danger.petit) :
   c'est alors une variante de taille, pas un bouton secondaire. */
.principal.petit { background: var(--accent); border: 0; color: var(--sur-tier); }
.danger.petit { background: none; border-color: #5a2b2b; color: var(--danger); }
/* La case survolée pendant un glissé prend la couleur de son tier : on voit
   dans quel verdict on est en train de déposer, pas seulement qu'on dépose. */
.rangee .contenu.survole { border-style: solid;
                           border-color: color-mix(in srgb, var(--teinte, var(--accent)) 70%, transparent);
                           background: color-mix(in srgb, var(--teinte, var(--accent)) 12%, var(--carte)); }
.vignette[draggable="true"] { cursor: grab; }
.vignette[draggable="true"]:active { cursor: grabbing; }
/* Point « classé à la main » : posé sur l'affiche, il lui faut son propre
   contraste — un cerne sombre plutôt que la couleur de la carte, l'affiche
   pouvant être de n'importe quelle teinte. */
.vignette .point-manuel { position: absolute; top: 4px; right: 4px; width: 8px; height: 8px;
                           border-radius: 50%; background: var(--accent); z-index: 1;
                           box-shadow: 0 0 0 2px #000000a6; }

/* Vue liste : barre de filtres cumulables + tableau, cartes empilées sur téléphone. */
.barre-filtres { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; align-items: center; }
.barre-filtres .champ { width: auto; flex: 1 1 140px; margin-bottom: 0; }
#vl-recherche { flex: 2 1 200px; }
.filtre-case { display: flex; align-items: center; gap: 6px; color: var(--doux); font-size: 14px;
               white-space: nowrap; cursor: pointer; }
.filtre-case input { width: 17px; height: 17px; accent-color: var(--accent); }
.tableau-liste { width: 100%; border-collapse: collapse; }
.tableau-liste th { text-align: left; color: var(--doux); font-weight: 600; font-size: 12px;
                     padding: 6px 8px; border-bottom: 1px solid var(--bord); white-space: nowrap; }
.tableau-liste th.triable { cursor: pointer; user-select: none; }
.tableau-liste th.tri-actif { color: var(--texte); }
.tableau-liste td { padding: 6px 8px; border-bottom: 1px solid var(--bord); vertical-align: middle; }
.tableau-liste tbody tr { cursor: pointer; }
.tableau-liste tbody tr:hover { background: var(--carte); }
.vignette-mini { width: 32px; height: 47px; object-fit: cover; border-radius: 3px; display: block; }
.tag-verdict { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px;
               border-radius: 5px; color: #111; font-weight: 800; font-size: 12px; }

@media (max-width: 640px) {
  .tableau-liste thead { display: none; }
  .tableau-liste, .tableau-liste tbody, .tableau-liste tr, .tableau-liste td { display: block; width: 100%; }
  .tableau-liste tr { border: 1px solid var(--bord); border-radius: 8px; margin-bottom: 8px; padding: 6px 8px; }
  .tableau-liste td { border-bottom: 0; padding: 3px 0; display: flex; justify-content: space-between; align-items: center; }
  .tableau-liste td[data-label]:not([data-label=""])::before { content: attr(data-label); color: var(--doux); font-size: 12px; }
  .tableau-liste td:first-child { justify-content: flex-start; }
}

/* Petit bouton icône réutilisé par plusieurs sections de la fiche
   (histoire, personnages, moments) : monter/descendre/supprimer une ligne. */
.icone-btn { background: none; border: 1px solid var(--bord); color: var(--doux);
             border-radius: var(--r1); width: 32px; height: 32px; cursor: pointer;
             font-size: var(--t-annot); line-height: 1; flex: 0 0 auto;
             transition: color var(--duree-courte) var(--courbe),
                         border-color var(--duree-courte) var(--courbe); }
.icone-btn:hover:not(:disabled) { color: var(--texte); border-color: var(--tres-doux); }
.icone-btn:disabled { opacity: .3; cursor: default; }

/* Section histoire (fiche-histoire.js) : barre dépliant + édition, frise
   chronologique par acte fermée par défaut. */
/* Une section repliable (histoire, personnages) s'annonce comme une
   section : un filet la sépare de ce qui précède, son titre a le poids
   d'un titre. Avant, le bouton dépliant avait la taille du texte courant
   et rien ne disait où commençait la section. */
.section-histoire, .section-personnages { margin: var(--e5) 0 0;
                                          border-top: 1px solid var(--bord-doux);
                                          padding-top: var(--e4); }
.histoire-barre { display: flex; gap: var(--e2); align-items: center; margin-bottom: var(--e2); }
.histoire-toggle { background: none; border: 0; color: var(--texte-fort); font-weight: 650;
                   font-size: var(--t-titre-s); letter-spacing: -.01em;
                   padding: var(--e1) 0; cursor: pointer; flex: 1; text-align: left;
                   transition: color var(--duree-courte) var(--courbe); }
.histoire-toggle:hover { color: var(--accent); }

.frise { margin-top: var(--e4); }
.acte { margin-bottom: var(--e5); }
.acte:last-child { margin-bottom: 0; }
.acte-tete { display: flex; align-items: center; gap: var(--e2); margin-bottom: var(--e3); }
.acte-couleur { width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto; }
.acte-titre { font-weight: 700; font-size: var(--t-corps); color: var(--texte-fort); }
/* La frise : un trait vertical à la couleur de l'acte, un point par
   moment. C'est la seule chose de la fiche qui ressemble à une lecture
   plutôt qu'à un formulaire — elle mérite de l'air. */
.acte-beats { border-left: 2px solid var(--bord); margin-left: 4px; padding-left: var(--e5);
              display: flex; flex-direction: column; gap: var(--e4); }
.beat { position: relative; }
.beat::before { content: ''; position: absolute; left: -30px; top: 6px; width: 9px; height: 9px;
                 border-radius: 50%; background: var(--couleur-beat, var(--doux));
                 box-shadow: 0 0 0 3px var(--carte); }
.beat-titre { font-weight: 650; font-size: var(--t-corps); color: var(--texte-fort);
              margin: 0 0 var(--e1); line-height: var(--interligne-serre); }
.beat-texte { font-size: var(--t-annot); color: var(--doux); margin: 0;
              line-height: var(--interligne-lu); text-wrap: pretty; }

.acte-edit { background: var(--carte); border: 1px solid var(--bord); border-radius: 8px;
             padding: 10px; margin-bottom: 12px; }
.acte-edit-tete { display: flex; gap: 6px; align-items: center; margin-bottom: 8px; }
.acte-edit-tete input.champ { margin-bottom: 0; flex: 1; }
.beat-edit { display: flex; gap: 6px; margin-bottom: 8px; align-items: flex-start; }
.beat-edit .beat-champs { flex: 1; }
.beat-edit input.champ, .beat-edit textarea.champ { margin-bottom: 6px; }
.beat-edit .beat-actions { display: flex; flex-direction: column; gap: 4px; }

/* Section personnages (fiche-personnages.js) : mini tier list interne à la
   fiche, cartes rondes plutôt que des affiches. */
.perso-actions { display: flex; gap: var(--e2); flex-wrap: wrap; margin-bottom: var(--e3); }
/* Les personnages sont des visages, pas des affiches : cartes rondes et
   rangées plus basses que celles des œuvres. */
.perso-tierlist .rangee { margin-bottom: var(--e2); }
.perso-tierlist .rangee .contenu { min-height: 0; }
.perso-tierlist .rangee .etiquette b { font-size: var(--t-corps); }
.perso-carte { width: 66px; text-align: center; cursor: pointer; flex: 0 0 auto;
               transition: transform var(--duree-courte) var(--courbe); }
.perso-carte:hover { transform: translateY(-2px); }
.perso-carte[draggable="true"] { cursor: grab; }
/* Qui est ce personnage, dans le menu ouvert au clic sur sa vignette. La
   grille ne peut pas la porter — soixante pixels de large — mais la fiche
   perdrait tout son interet sans elle : une liste de visages ne rappelle
   rien d'un jeu fini il y a des annees. */
.perso-description { margin: 0 0 var(--e3); color: var(--texte);
                     line-height: var(--interligne-lu); font-size: var(--t-annot); }

.perso-carte img, .perso-sans-image { width: 60px; height: 60px; border-radius: 50%;
                                       object-fit: cover; display: block; margin: 0 auto;
                                       box-shadow: inset 0 0 0 1px #ffffff1a, var(--ombre-1); }
/* Sur un portrait, le visage occupe le haut de l'image, jamais son centre :
   un recadrage centre montrait le buste de Geralt et l'epaule de Yennefer.
   Verifie sur les portraits reels de The Witcher 3. */
.perso-carte img { object-position: 50% 26%; }
/* --teinte-perso, et non --teinte : ce dernier existe deja plus haut et
   contient une COULEUR (celle du verdict d'une rangee). Y glisser un nombre
   aurait casse toute regle color-mix heritee. La teinte vient du nom (voir
   teinteDuNom, fiche-personnages.js). Elle est
   volontairement basse en saturation : il s'agit de distinguer des pastilles
   entre elles sur un thème sombre, pas de colorer la page. Le repli 220
   couvre le cas où la variable n'est pas posée. */
.perso-sans-image { position: relative;
                     /* Une lumière en haut à gauche et un anneau intérieur : un
                        aplat de couleur se lisait comme une case vide, un
                        emblème se lit comme un choix. */
                     background:
                       radial-gradient(120% 95% at 30% 16%,
                                       hsl(var(--teinte-perso, 220) 52% 44%), transparent 64%),
                       linear-gradient(155deg,
                                       hsl(var(--teinte-perso, 220) 32% 29%),
                                       hsl(var(--teinte-perso, 220) 30% 14%));
                     display: flex; align-items: center; justify-content: center;
                     font-size: var(--t-titre-s); font-weight: 700; letter-spacing: .02em;
                     color: hsl(var(--teinte-perso, 220) 48% 91%);
                     text-shadow: 0 1px 2px hsl(var(--teinte-perso, 220) 40% 8% / .6); }
.perso-sans-image::after {
  content: ''; position: absolute; inset: 5px; border-radius: 50%;
  border: 1px solid hsl(var(--teinte-perso, 220) 50% 74% / .26);
}
.perso-nom { font-size: var(--t-micro); margin-top: var(--e1); line-height: 1.25;
             max-height: 2.5em; overflow: hidden; color: var(--doux); }

/* Moments marquants (vue-fiche.js) : liste de textes courts, flèches pour
   réordonner. */
.moments-liste { display: flex; flex-direction: column; gap: var(--e2); margin: var(--e2) 0 var(--e3); }
.moment-ligne { display: flex; gap: var(--e2); align-items: center; }
.moment-ligne .champ { margin-bottom: 0; flex: 1; min-width: 0; }
.moment-actions { display: flex; gap: var(--e1); flex: 0 0 auto; }

/* Œuvres liées (vue-fiche.js) : une ligne par lien, cliquable quand l'œuvre
   cible est dans la collection. */
.lien-ligne { display: flex; align-items: center; gap: var(--e2); padding: var(--e2) 0;
              border-bottom: 1px solid var(--bord-doux); font-size: var(--t-annot);
              color: var(--doux); }
.lien-oeuvre { background: none; border: 0; color: var(--texte); cursor: pointer; text-align: left;
               padding: 0; flex: 1; font-size: var(--t-annot); font-family: inherit;
               transition: color var(--duree-courte) var(--courbe); }
.lien-oeuvre:hover { color: var(--accent); }
.lien-oeuvre b { font-weight: 650; }
.lien-ligne > *:not(.icone-btn) { flex: 1; min-width: 0; }

/* Créateur cliquable (vue-fiche.js) : ouvre sa fiche personne
   (vue-personne.js). Seuls les créateurs venus de Wikidata en ont un — un
   développeur ou éditeur Steam reste en texte simple. */
.lien-createur { background: none; border: 0; color: var(--accent); cursor: pointer;
                 padding: 0; font: inherit; text-decoration: underline; text-underline-offset: 2px; }

/* Fiche personne (vue-personne.js) : photo + biographie, puis la
   filmographie croisée avec la collection en grille chronologique. Chaque
   œuvre déjà vue porte son affiche et son verdict et s'ouvre au clic ; le
   reste est grisé, avec un bouton d'ajout rapide — c'est le compteur
   « x vues sur y » qui donne envie de compléter (spec §6/§10). */
.boite-large { max-width: 760px; }
/* En-tête de la fiche personne : le nom, puis le rafraîchissement manuel
   (spec §6/§10 — sans lui, un nouveau film sorti pendant les trente jours
   de cache reste invisible) à côté du bouton de fermeture existant. */
.entete-actions { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }
.rafraichir-personne { background: none; border: 1px solid var(--bord); color: var(--doux);
                        font-size: 12px; padding: 5px 10px; border-radius: 20px; cursor: pointer;
                        white-space: nowrap; }
.rafraichir-personne:disabled { opacity: .6; cursor: default; }
.personne-cache { margin: -10px 0 14px; }
.personne-tete { display: flex; gap: 14px; margin-bottom: 10px; }
.personne-photo { width: 96px; height: 128px; object-fit: cover; object-position: 50% 22%;
                   border-radius: 7px; flex: 0 0 96px; background: #222836; }
.personne-photo.sans-image { display: flex; align-items: center; justify-content: center;
                              font-size: 11px; color: var(--doux); text-align: center; padding: 6px; }
.personne-compteur { font-weight: 700; margin: 2px 0 16px; }
.filmo-grille { display: grid; grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); gap: 12px; }
.filmo-carte { background: none; border: 0; color: var(--texte); text-align: left; cursor: pointer;
               padding: 0; display: flex; flex-direction: column; gap: 3px; font: inherit; }
.filmo-affiche { width: 100%; aspect-ratio: 2 / 3; object-fit: cover; border-radius: 6px;
                 background: #222836; display: block; }
.filmo-affiche.sans-image { display: flex; align-items: center; justify-content: center;
                             font-size: 10px; color: var(--doux); text-align: center; padding: 6px; }
.filmo-info { display: flex; align-items: center; gap: 5px; }
.filmo-titre { font-size: 12px; font-weight: 600; line-height: 1.25; max-height: 2.5em; overflow: hidden; }
.filmo-carte-grisee { opacity: .55; }
.filmo-carte-grisee .filmo-affiche { filter: grayscale(1); }
.filmo-carte-grisee:hover { opacity: .8; }
.filmo-ajouter { margin-top: 2px; align-self: flex-start; }

/* ═══════════════════════════════════════════════════════════════════════
   AFFINAGE — la couche qui fait la différence entre « propre » et
   « soigné ». Rien de fonctionnel ici : de l'atmosphère, de la matière et
   du rythme de lecture. Tout est en CSS pur, aucune image, aucune police
   à télécharger — l'application doit rester instantanée en 4G.
   ═══════════════════════════════════════════════════════════════════════ */

:root {
  /* Une famille à empattements, prise dans les polices système, réservée
     aux textes qu'on lit vraiment. Elle n'apparaît nulle part ailleurs :
     c'est ce qui distingue la lecture de l'interface. */
  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia,
           "Times New Roman", serif;
}

/* — L'atmosphère. Un halo très sourd en haut de page, plus un grain à
     peine perceptible : sans eux, un fond sombre uni paraît plat et un peu
     mort sur un grand écran. Les deux sont posés en couche fixe derrière
     tout le reste, et ne captent jamais le pointeur. */
body::before {
  content: ''; position: fixed; inset: 0; z-index: -2; pointer-events: none;
  background:
    radial-gradient(90rem 42rem at 50% -18rem,
                    color-mix(in srgb, var(--u1) 20%, transparent), transparent 70%),
    radial-gradient(60rem 34rem at 88% 8%,
                    color-mix(in srgb, var(--u2) 16%, transparent), transparent 72%),
    radial-gradient(52rem 30rem at 6% 92%,
                    color-mix(in srgb, var(--u2) 12%, transparent), transparent 74%),
    /* Le voile. Dernière couche de la pile, donc la plus basse : c'est elle
       qui recouvre la mosaïque d'affiches et rend le texte lisible. Sans
       elle, un fond de jaquettes colorées mange les titres. */
    linear-gradient(180deg,
                    color-mix(in srgb, var(--fond) 38%, transparent),
                    color-mix(in srgb, var(--fond) 70%, transparent) 42%,
                    color-mix(in srgb, var(--fond) 90%, transparent) 100%);
}

/* ── L'UNIVERS DE LA CATÉGORIE ────────────────────────────────────────
   Une mosaïque des affiches de la collection elle-même, floutée jusqu'à
   n'être plus qu'une atmosphère colorée. L'idée tient en une phrase : le
   fond des jeux vidéo est fait des jeux qu'on a joués, celui des films des
   films qu'on a vus. Aucune image n'est téléchargée pour l'occasion — ce
   sont les affiches que la grille vient d'afficher.

   Débordement volontaire (inset négatif) : un flou s'estompe vers la
   transparence sur ses bords, et une mosaïque calée sur le viewport
   montrerait quatre lisières claires.                                    */
#univers {
  position: fixed; inset: -14%; z-index: -3; pointer-events: none;
  display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: 1fr;
  filter: blur(52px) saturate(1.45);
  opacity: .5;
  /* Composition sur le GPU : sans ça, un flou de cette taille sur douze
     images se recalcule à chaque défilement. */
  transform: translateZ(0);
  will-change: transform;
}
#univers img { width: 100%; height: 100%; object-fit: cover; display: block; }
@media (min-width: 720px)  { #univers { grid-template-columns: repeat(4, 1fr); } }
@media (min-width: 1100px) { #univers { grid-template-columns: repeat(6, 1fr); } }

/* Sur téléphone, un flou de 52px coûte le plus cher là où il se voit le
   moins : l'écran est presque entièrement couvert par la grille opaque. */
@media (max-width: 719px) { #univers { filter: blur(34px) saturate(1.4); } }

/* Une affiche morte laisse une tuile vide : invisible sous ce flou, mais
   autant ne pas afficher l'icône d'image cassée du navigateur. */
#univers .image-cassee { visibility: hidden; }

/* Les six univers. Chacun garde le même thème sombre — seules changent la
   couleur d'accent et les deux teintes du halo. L'accent sert de FOND à du
   texte presque noir (var(--sur-tier)) : toutes ces couleurs sont donc
   choisies claires, sinon les libellés deviendraient illisibles. */
:root { --u1: #f5c518; --u2: #4a9fd8; }              /* repli, avant tout choix */
[data-univers="film"]  { --accent: #f5c518; --u1: #f5c518; --u2: #d8763a; }
[data-univers="serie"] { --accent: #5ec8f5; --u1: #4a9fd8; --u2: #7e8bd8; }
[data-univers="jeu"]   { --accent: #b98cff; --u1: #8b5cf6; --u2: #4a9fd8; }
[data-univers="anime"] { --accent: #ff8fb8; --u1: #ff5c8a; --u2: #b98cff; }
[data-univers="manga"] { --accent: #dfe4ee; --u1: #8b93a7; --u2: #4a9fd8; }
[data-univers="comic"] { --accent: #ff9066; --u1: #ff5c3a; --u2: #f5c518; }

/* L'accent-creux est dérivé de l'accent, sinon il resterait doré partout. */
[data-univers] { --accent-creux: color-mix(in srgb, var(--accent) 12%, transparent); }

/* Le changement d'univers se fait en fondu plutôt que d'un coup : passer
   d'un onglet à l'autre est un déplacement, pas un clignotement. */
#univers, body::before { transition: opacity var(--duree-fond) ease; }

/* Un fond animé ou trop présent gêne la lecture pour qui y est sensible :
   on le réduit à un halo. */
@media (prefers-reduced-motion: reduce) {
  #univers { opacity: .22; }
  #univers, body::before { transition: none; }
}
body::after {
  content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  opacity: .035; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* — Les étiquettes de verdict. Un aplat de couleur est plat, justement :
     un dégradé et un liseré clair en haut leur donnent l'épaisseur d'une
     pastille physique, et la lettre gagne une ombre portée pour tenir sur
     les teintes claires (S est jaune vif). */
.rangee .etiquette {
  background-image: linear-gradient(160deg,
                    color-mix(in srgb, #fff 16%, transparent), transparent 62%);
  box-shadow: inset 0 1px 0 #ffffff40, var(--ombre-1);
}
.rangee .etiquette b { text-shadow: 0 1px 0 #0000001f; }

/* — Les affiches. Le survol ne fait plus que soulever : il allume un halo
     à la couleur du verdict de la rangée. C'est la seule endroit où la
     couleur d'un tier touche l'œuvre elle-même, et ça relie enfin les deux. */
@media (hover: hover) {
  .vignette:hover {
    transform: translateY(-4px) scale(1.02);
    box-shadow:
      inset 0 0 0 1px color-mix(in srgb, var(--teinte, var(--accent)) 70%, transparent),
      0 10px 26px -8px color-mix(in srgb, var(--teinte, #000) 45%, #000),
      0 0 30px -12px color-mix(in srgb, var(--teinte, var(--accent)) 60%, transparent);
  }
}
/* Le titre affiché faute d'affiche méritait mieux qu'un petit texte gris
   centré : il devient le sujet de la vignette. */
.vignette::after {
  font-size: var(--t-mini); font-weight: 700; letter-spacing: -.01em;
  color: color-mix(in srgb, var(--texte) 78%, transparent);
  align-items: flex-end; padding: var(--e2) 7px;
  background: linear-gradient(180deg, transparent 35%, #0000004d);
}

/* ── LA FICHE ──────────────────────────────────────────────────────────
   L'ouverture d'une fiche doit donner l'impression d'entrer dans l'œuvre.
   L'en-tête reçoit donc un fond teinté par le verdict (--teinte est déjà
   posée par vue-fiche.js), qui déborde de la boîte et s'éteint en
   descendant : la couleur vient d'en haut, comme une lumière.            */
/* Surtout PAS d'overflow ici : `.boite` est l'élément qui défile
   (`overflow-y: auto`), et le masquer fige la fiche entière — c'est
   exactement ce qui est arrivé. Le halo est découpé par `.boite-corps`,
   qui ne défile pas. */
.boite-fiche { position: relative; }
.boite-fiche .boite-corps { position: relative; overflow: hidden; }
.boite-fiche .boite-corps::before {
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 300px;
  z-index: 0; pointer-events: none;
  background:
    radial-gradient(70% 90% at 22% 0%,
                    color-mix(in srgb, var(--teinte, var(--accent)) 26%, transparent), transparent 68%),
    linear-gradient(180deg,
                    color-mix(in srgb, var(--teinte, var(--accent)) 9%, transparent), transparent 76%);
}
.boite-fiche .boite-corps > * { position: relative; z-index: 1; }

/* L'affiche de la fiche se détache du fond : ombre portée profonde, et un
   reflet oblique très léger qui suggère une surface. */
.fiche-affiche {
  box-shadow:
    inset 0 0 0 1px #ffffff1f,
    0 18px 40px -14px #000d,
    0 4px 12px -6px #0009;
}
.fiche-affiche::before {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(118deg, #ffffff1a, transparent 42%);
}

.fiche-titre { letter-spacing: -.025em; }

/* — La lecture. Résumé, ressenti et frise passent aux empattements : c'est
     le seul signal typographique qui dit « ceci se lit » plutôt que
     « ceci s'utilise ». Mesure limitée à 62 caractères, au-delà l'œil perd
     sa ligne. */
/* `textarea.champ` porte `font: inherit` et une spécificité d'élément +
   classe : il faut donc nommer l'élément ici aussi, sinon la police à
   empattements ne s'applique jamais. */
textarea.champ-lecture,
.beat-texte,
.acc-texte {
  font-family: var(--serif);
  font-size: 16.5px;
  line-height: 1.62;
  letter-spacing: .002em;
}
textarea.champ-lecture { max-width: 62ch; }
.beat-texte { color: color-mix(in srgb, var(--texte) 74%, transparent); max-width: 60ch; }

/* Le titre d'un acte : petites capitales espacées plutôt qu'un gras de
   plus. Une frise se lit comme un sommaire, pas comme une liste de
   paragraphes. */
.acte-titre {
  font-size: var(--t-mini); font-weight: 800; letter-spacing: .12em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--couleur-acte, var(--accent)) 82%, var(--texte));
}
.beat-titre { font-family: var(--serif); font-size: var(--t-corps-fort); font-weight: 700;
              letter-spacing: -.01em; }
/* Le trait de la frise s'éteint vers le bas : il guide sans encadrer. */
.acte-beats { border-left-color: transparent;
              background: linear-gradient(180deg, var(--bord), transparent) left / 2px 100% no-repeat; }

/* — L'accueil. Le titre gagne une taille et un dégradé qui l'ancre : c'est
     le premier écran, il a le droit d'avoir de la voix. */
.acc-titre {
  font-size: clamp(26px, 7vw, 34px); letter-spacing: -.03em;
  background: linear-gradient(180deg, var(--texte-fort), color-mix(in srgb, var(--texte) 62%, transparent));
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}
.acc-texte { max-width: 34ch; margin-inline: auto; }

/* — Les pastilles de verdict de la vue liste : mêmes matières que les
     étiquettes de rangée, pour que le même objet se reconnaisse partout. */
.tag-verdict {
  background-image: linear-gradient(160deg, #ffffff26, transparent 60%);
  box-shadow: inset 0 1px 0 #ffffff40;
}

/* — Les cartes de filmographie : elles étaient nues. Un cadre discret qui
     s'allume au survol suffit à en faire des objets. */
.filmo-affiche { box-shadow: inset 0 0 0 1px #ffffff14, var(--ombre-1);
                 transition: box-shadow var(--duree) var(--courbe),
                             transform var(--duree) var(--courbe); }
@media (hover: hover) {
  .filmo-carte:hover .filmo-affiche {
    transform: translateY(-3px);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 55%, transparent),
                0 10px 24px -10px #000c;
  }
}

/* — Les visages des personnages : un cerne net plutôt qu'un bord flou. */
.perso-carte img, .perso-sans-image { box-shadow: inset 0 0 0 1px #ffffff26, 0 4px 12px -6px #000b; }

/* — Détail qui se voit partout : les nombres s'alignent en colonnes. */
.fiche-meta, .personne-compteur, .mode small, .tableau-liste td { font-variant-numeric: tabular-nums; }

/* ── EN-TÊTE DE PAGE ───────────────────────────────────────────────────
   Le nom de la catégorie, son compte, et la répartition des verdicts.
   Avant, une catégorie peu remplie donnait une rangée seule en haut d'un
   grand vide : rien ne disait où on était ni ce qu'on regardait.        */
.page-tete { margin: var(--e2) 0 var(--e5); }
.page-tete-haut { display: flex; align-items: baseline; gap: var(--e3); flex-wrap: wrap; }
.page-titre {
  font-size: clamp(28px, 5.5vw, 40px); font-weight: 800; letter-spacing: -.035em;
  background: linear-gradient(175deg, var(--texte-fort),
              color-mix(in srgb, var(--texte) 55%, transparent));
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}
.page-compte { color: var(--tres-doux); font-size: var(--t-annot); font-weight: 600;
               font-variant-numeric: tabular-nums; }

/* Compteur de la file d'attente de notation, à côté du total. Volontairement
   discret et non coloré comme un verdict : « pas encore noté » n'est pas un
   rang de l'échelle, c'est une absence de rang. */
.page-a-noter { color: var(--tres-doux); font-size: var(--t-annot); font-weight: 600;
                font-variant-numeric: tabular-nums; padding: 2px 9px; border-radius: 999px;
                border: 1px dashed var(--bord); background: none; cursor: pointer;
                transition: color var(--duree-courte) var(--courbe),
                            border-color var(--duree-courte) var(--courbe); }
.page-a-noter:hover { color: var(--texte); border-color: var(--accent); border-style: solid; }

/* La barre : un segment par verdict, large en proportion de ce qu'on
   possède. Les verdicts sans aucune œuvre gardent un filet mince — on doit
   lire l'échelle entière, pas seulement la partie déjà remplie. */
/* Un seul ruban continu, sans espaces : les segments se touchent comme les
   parts d'un tout. Chaque verdict garde une part plancher (le `+ .3`),
   sinon une collection d'une seule œuvre affichait un immense trait de
   couleur suivi de six points perdus — un graphique qui ment sur ce qu'il
   montre. La barre n'apparaît qu'à partir de trois œuvres : en dessous,
   une répartition ne dit rien. */
.repartition { display: flex; height: 8px; margin-top: var(--e4);
               border-radius: var(--r-plein); overflow: hidden;
               box-shadow: inset 0 0 0 1px #ffffff0f; }
.repartition i { flex: calc(var(--part) + .3) 1 0;
                 background: var(--teinte);
                 background-image: linear-gradient(180deg, #ffffff30, transparent);
                 animation: barre-pousse 520ms var(--courbe) backwards; }
.repartition i.vide { background: color-mix(in srgb, var(--bord) 60%, transparent);
                      background-image: none; }
@keyframes barre-pousse { from { flex-grow: .01; opacity: 0; } }

/* Une rangée qui ne contient qu'une ou deux affiches ne doit pas se lire
   comme un grand rectangle vide : le fond s'efface, seul le liseré reste. */
.rangee .contenu { background: color-mix(in srgb, var(--carte) 55%, transparent); }

/* Zone de lecture : la hauteur suit le texte au lieu d'enfermer un long
   résumé dans une boîte de trois lignes avec sa propre barre de défilement
   (constaté sur la fiche de Dune). Repli par min-height pour les
   navigateurs qui ignorent encore field-sizing. */
textarea.champ-lecture { field-sizing: content; min-height: 6.5em; max-height: 24em; }

/* Fiche en attente de rédaction par Claude : le bouton dit son état plutôt
   que son action, pour qu'on voie d'un coup d'œil ce qui est déjà demandé. */
.petit.demande-posee { color: var(--accent);
                       border-color: color-mix(in srgb, var(--accent) 50%, transparent);
                       background: var(--accent-creux); }
.histoire-etat { margin: var(--e2) 0 0; }

/* ── LARGEUR DES FENÊTRES SUR GRAND ÉCRAN ──────────────────────────────
   Une fenêtre de 520 px au milieu d'un écran de 1900 px est une carte de
   visite : on lit une fiche à travers une fente. Les fenêtres s'élargissent
   donc avec l'écran, sans jamais dépasser une mesure de lecture confortable
   (les blocs de texte gardent leur propre limite en `ch`).              */
@media (min-width: 800px) {
  .boite { max-width: 640px; }
  .boite-fiche { max-width: 800px; }
  .boite-large { max-width: 900px; }
  .boite-fiche .boite-corps, .boite-large .boite-corps { padding: var(--e5) var(--e6) var(--e7); }
  .boite-fiche .boite-entete, .boite-large .boite-entete { padding: var(--e4) var(--e6); }
  /* L'affiche a la place de respirer, et la colonne d'identité aussi. */
  .fiche-affiche { width: 168px; flex: 0 0 168px; }
  .fiche-tete { gap: var(--e5); }
}
@media (min-width: 1280px) {
  .boite-fiche { max-width: 920px; }
  .boite-large { max-width: 1060px; }
  .fiche-affiche { width: 196px; flex: 0 0 196px; }
  /* La filmographie profite de la largeur : des cartes plus grandes plutôt
     que davantage de colonnes minuscules. */
  .filmo-grille { grid-template-columns: repeat(auto-fill, minmax(124px, 1fr)); }
}

/* — Bandeau de mise à jour. Discret, en bas, jamais au milieu de la lecture :
     il annonce que du code plus récent attend le prochain chargement. */
#bandeau-maj {
  position: fixed; left: 50%; bottom: var(--e4); transform: translateX(-50%);
  z-index: 60; display: flex; align-items: center; gap: var(--e3);
  padding: var(--e2) var(--e3); border-radius: 999px;
  background: color-mix(in srgb, var(--carte-haute) 92%, transparent);
  border: 1px solid var(--bord); box-shadow: var(--ombre-2, 0 12px 30px -12px #000c);
  font-size: var(--t-annot); color: var(--texte);
  backdrop-filter: blur(8px);
  max-width: calc(100vw - 2 * var(--e4));
}
#bandeau-maj button {
  border: 0; border-radius: 999px; padding: 5px 12px; cursor: pointer;
  background: var(--accent); color: var(--sur-tier); font-weight: 700;
  font-size: var(--t-mini);
}
#bandeau-maj button.discret { background: transparent; color: var(--doux); font-weight: 600; }

/* ── NOTATION EN SÉRIE (vue-a-noter.js) ───────────────────────────────
   167 jeux importés d'un coup attendent leur verdict. Les noter fiche par
   fiche demande six gestes chacun ; ici il en reste un, et la file avance
   seule. La fenêtre est donc dessinée pour la répétition : la même chose
   toujours au même endroit, et sept cibles assez grandes pour le pouce. */
.boite-a-noter { max-width: 560px; }

.a-noter-jauge { height: 2px; background: var(--bord-doux); }
.a-noter-jauge i { display: block; height: 100%; background: var(--accent);
                   transition: width var(--duree) var(--courbe); }

.a-noter-oeuvre { display: flex; gap: var(--e4); margin-bottom: var(--e5); }
.a-noter-affiche { width: 88px; flex: 0 0 88px; aspect-ratio: 2 / 3; position: relative;
                   border-radius: var(--r2); overflow: hidden; background: var(--fond-creux);
                   display: flex; align-items: center; justify-content: center;
                   padding: var(--e2); text-align: center;
                   font-size: var(--t-micro); color: var(--doux); }
.a-noter-affiche img { position: absolute; inset: 0; width: 100%; height: 100%;
                       object-fit: cover; }
.a-noter-texte { min-width: 0; }
.a-noter-titre { margin: 0; font-size: var(--t-titre-m); line-height: 1.2;
                 letter-spacing: -.02em; color: var(--texte-fort); }
.a-noter-meta { margin: var(--e1) 0 var(--e2); font-size: var(--t-annot); color: var(--doux); }
/* Le résumé est là pour se rappeler du jeu, pas pour être lu en entier :
   quatre lignes, puis on coupe. */
.a-noter-resume { margin: 0; font-size: var(--t-annot); line-height: var(--interligne);
                  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical;
                  overflow: hidden; }

/* Sept boutons de même largeur : la position de chaque verdict ne bouge
   jamais, c'est ce qui permet de noter sans regarder. */
.a-noter-verdicts { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--e1); }
.a-noter-verdict {
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: var(--e2) 2px; border-radius: var(--r2); cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--teinte) 35%, transparent);
  background: color-mix(in srgb, var(--teinte) 12%, var(--carte-haute));
  color: var(--texte); transition: background var(--duree-courte) var(--courbe),
                                   transform var(--duree-courte) var(--courbe);
}
.a-noter-verdict b { font-size: var(--t-titre-s); font-weight: 800; color: var(--teinte); }
.a-noter-verdict small { font-size: 9px; color: var(--doux); line-height: 1.1;
                         text-align: center; }
.a-noter-verdict:hover { background: color-mix(in srgb, var(--teinte) 26%, var(--carte-haute));
                         transform: translateY(-1px); }

.a-noter-pied { display: flex; align-items: center; gap: var(--e3); flex-wrap: wrap;
                margin-top: var(--e4); }
.a-noter-aide { font-size: var(--t-micro); margin-left: auto; }
.a-noter-fini { font-size: var(--t-titre-m); font-weight: 700; color: var(--texte-fort);
                margin: var(--e5) 0 var(--e2); }

/* Pendant l'enregistrement : on éteint sans faire sauter la mise en page. */
.a-noter-attente { opacity: .55; pointer-events: none; }

@media (max-width: 430px) {
  .a-noter-verdict small { display: none; }     /* la lettre suffit au pouce */
  .a-noter-affiche { width: 68px; flex-basis: 68px; }
}
