/* ==========================================================================
   medias7 — composants reutilisables
   ========================================================================== */

/* ---------- Boutons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  min-height: var(--touch);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-pill);
  background: #fff;
  color: #141735;
  cursor: pointer;
  font: inherit;
  font-size: 0.78rem;
  font-weight: 900;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-align: center;
  padding: 0.7rem 1.5rem;
  box-shadow: 0 16px 32px rgba(0, 0, 0, 0.26);
  transition: transform 120ms ease, filter 120ms ease;
}
.btn:hover { filter: brightness(1.08); text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn[disabled], .btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  box-shadow: none;
}

/* Le glyphe du gabarit devient explicite : .btn--icon[data-icon="+"] */
.btn--icon[data-icon]::before {
  content: attr(data-icon);
  color: var(--accent);
  font-size: 1.1em;
  line-height: 1;
}

.btn--primary {
  border-color: transparent;
  background: linear-gradient(135deg, var(--accent-fond), var(--accent-fond-2));
  color: #fff;
}
.btn--primary.btn--icon[data-icon]::before { color: #fff; }

.btn--ghost {
  background: rgba(255, 255, 255, 0.06);
  color: #fff;
  backdrop-filter: blur(12px);
  box-shadow: none;
}

/* Bouton bascule enfoncé : l'état d'aria-pressed doit se voir, pas seulement
   s'entendre. Une coche et un liseré d'accent, sans changer le libellé. */
.btn[aria-pressed="true"] {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.btn[aria-pressed="true"]::before {
  content: "\2713";
  margin-right: 0.45em;
  color: var(--accent);
}
.btn[aria-busy="true"] { cursor: progress; opacity: 0.7; }

/* Choix d'un état posé sur une œuvre (partial/choix-d-etat : appréciation,
   suivi) : la valeur actuelle, et au clic la liste des états posée
   par-dessus ce qui suit. Le panneau qui la porte est élevé par
   .has-dropdown-ouvert (vendor.css) : son backdrop-filter en fait un contexte
   d'empilement, et le panneau suivant passerait devant. */
/* Le trophée vide du non noté, en retrait (9,4:1 sur le fond, 4,8:1 sur un
   bouton de carte posé sur une jaquette blanche). */
.icon-appreciation { color: var(--muted); }
.choix { position: relative; display: inline-block; max-width: 100%; }
.choix__actuelle { gap: 0.5rem; }
.choix__icone { display: inline-flex; }
.choix__chevron { margin-left: 0.15rem; transition: transform 140ms ease; }
.choix__actuelle[aria-expanded="true"] .choix__chevron { transform: rotate(180deg); }
.choix__liste {
  position: absolute;
  top: calc(100% + 0.35rem);
  left: 0;
  z-index: var(--z-dropdown);
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  min-width: 14rem;
  padding: 0.5rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-soft);
  box-shadow: var(--shadow);
}
.choix__liste[hidden] { display: none; }
.choix__liste .btn { justify-content: flex-start; }
/* Un état défini : son libellé, puis sa définition en dessous, en casse
   ordinaire et en gris (Echelle::definition()). Les définitions font deux ou
   trois lignes : la liste prend une largeur de lecture, bornée à l'écran, et
   le texte passe à la ligne. */
.choix__liste:has(.choix__etat--defini) { width: min(22rem, calc(100vw - 3rem)); }
.choix--en-place .choix__liste:has(.choix__etat--defini) { width: auto; }
.choix__etat--defini {
  align-items: center;
  gap: 0.75rem;
  /* Sur trois lignes, la pilule de .btn devient un ovale. */
  border-radius: var(--radius-sm);
  padding-top: 0.55rem;
  padding-bottom: 0.55rem;
  text-align: left;
  white-space: normal;
}
/* Icônes alignées dans une même colonne, centrées sur leur texte. */
.choix__etat--defini > svg, .choix__etat--defini > img { flex: 0 0 auto; }
.choix__option { display: flex; flex-direction: column; align-items: flex-start; gap: 0.1rem; }
.choix__definition {
  color: var(--muted);
  font-size: 0.74rem;
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: 0;
  text-transform: none;
}
/* Dans la boîte d'une carte, la liste est le contenu : en place, ouverte,
   sans le bouton qui l'ouvrirait (choix-d-etat.js pose la classe). */
.choix--en-place { display: block; }
.choix--en-place .choix__actuelle { display: none; }
.choix--en-place .choix__liste {
  position: static;
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  box-shadow: none;
}

.btn--danger {
  border-color: transparent;
  background: linear-gradient(135deg, #ff3153, #be1436);
  color: #fff;
}
.btn--danger.btn--icon[data-icon]::before { color: #fff; }

.btn--sm { min-height: 2.25rem; padding: 0.4rem 1rem; font-size: 0.72rem; }
.btn--block { width: 100%; }

.button-row, .badge-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.7rem;
}

/* Quantité et format sur une seule ligne : les deux champs portent
   width: 100%, et le flex-wrap de .button-row les empilait. La quantité
   garde une largeur fixe, le select prend le reste. */
.button-row--qty { flex-wrap: nowrap; }
.button-row--qty .qty-wrap { flex: 0 0 4.5rem; }
.button-row--qty .qty-wrap input { text-align: center; }
.button-row--qty select { flex: 1 1 auto; width: auto; min-width: 0; }

/* Champ suivi du bouton qui le lit (« Chercher sur Internet » a cote du nom).
   Le champ prend la place, le bouton garde la sienne. Quand la cellule est
   trop etroite pour les deux — une demi-grille sous 800px, un telephone —
   le bouton passe dessous au lieu de deborder sur la cellule voisine : c'est
   le flex-wrap, et non une media query, qui en decide, car la largeur utile
   depend de la grille et pas de l'ecran. min-width: 0 est indispensable, le
   .field etant une grille dont la piste prendrait sinon la largeur du contenu. */
.input-action { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: stretch; min-width: 0; }
.input-action > input { flex: 1 1 12rem; width: auto; min-width: 0; }
.input-action > .btn { flex: none; white-space: nowrap; }
/* Variante soudée : champ et bouton en une seule pièce, à la hauteur des
   petits boutons voisins (.btn--sm). Sans elle, align-items: stretch portait
   le bouton à la hauteur du champ (--touch), plus haut que les autres boutons
   de la ligne. Le bouton ne passe plus dessous : les deux ne font qu'un. */
.input-action--soude { flex-wrap: nowrap; gap: 0; }
.input-action--soude > input {
  height: 2.25rem;
  min-height: 0;
  padding: 0 0.9rem;
  font-size: 0.85rem;
  border-right: 0;
  border-radius: var(--radius-pill) 0 0 var(--radius-pill);
}
.input-action--soude > .btn {
  height: 2.25rem;
  min-height: 0;
  border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
}

/* ---------- Barre d'actions de formulaire ---------- */

.form-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.7rem;
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
}
/* L'action principale est toujours la derniere, poussee a droite. */
.form-actions > .form-actions__primary { margin-left: auto; }
/* Une action destructrice reste detachee a l'extreme gauche. */
.form-actions > .form-actions__destructive { margin-right: auto; }

.form-actions--sticky {
  position: sticky;
  bottom: 0;
  z-index: var(--z-sticky);
  margin-top: var(--space-4);
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: rgba(10, 13, 36, 0.94);
  backdrop-filter: blur(14px);
  box-shadow: 0 -12px 40px rgba(0, 0, 0, 0.45);
}

/* ---------- Pictogrammes de machines (partial/picto) ----------
   Des SVG EN COULEURS, au plus pres des logos officiels : un logotype
   (« PS2 ») est plus large que haut, seule la hauteur est fixee. Dessines
   pour le fond sombre du site. */

.picto { display: inline-flex; align-items: center; gap: 0.3rem; vertical-align: middle; color: inherit; line-height: 1.2; }
.picto__img { --picto-taille: 1rem; flex: none; height: var(--picto-taille); width: auto; max-width: calc(var(--picto-taille) * 3.2); }
.picto--md .picto__img { --picto-taille: 1.5rem; }
.picto--lg .picto__img { --picto-taille: 2rem; }
/* Secours d'un pictogramme generique : « GG », « Lynx ». */
.picto__texte { font-size: 0.68rem; font-weight: 800; letter-spacing: 0.02em; }
.picto--md .picto__texte { font-size: 0.75rem; }
a.picto { text-decoration: underline; text-underline-offset: 2px; }
/* Une liste de plateformes (fiche d'un jeu, resultats IGDB). */
.picto-liste { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.7rem; list-style: none; margin: 0; padding: 0; }

/* ---------- Badges ---------- */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.06);
  color: #dbe0ff;
  font-size: 0.72rem;
  font-weight: 800;
  padding: 0.3rem 0.65rem;
  white-space: nowrap;
}
.badge--primary { background: rgba(255, 11, 88, 0.16); color: #ff8bae; border-color: rgba(255, 11, 88, 0.35); }
.badge--success { background: rgba(76, 214, 168, 0.13); color: #8ff2cf; border-color: rgba(76, 214, 168, 0.32); }
.badge--warning { background: rgba(255, 193, 90, 0.14); color: #ffd994; border-color: rgba(255, 193, 90, 0.34); }
.badge--danger  { background: rgba(255, 78, 99, 0.14); color: #ffa6b1; border-color: rgba(255, 78, 99, 0.34); }
/* Information de rangement, pas un état : l'étagère d'une copie ou d'un coffret.
   Le cyan est la seule teinte franchement distincte à la fois du rose de
   --primary (le badge « Coffret ») et du gris-bleu du badge nu (le format), qui
   voisinent avec lui sur la même carte d'inventaire. Texte à 7,3:1 sur le fond de
   carte ; la bordure reste sous 3:1, comme TOUTES les pastilles du site — c'est
   la dette --line de mise-en-page.md, un chantier de tokens et non d'écran. */
.badge--info    { background: rgba(80, 200, 255, 0.15); color: #a9e2ff; border-color: rgba(80, 200, 255, 0.38); }

/* « Base ⇄ Dossier » de la comparaison d'un dossier (partial/sync-badge) : une
   teinte par section, pour savoir où l'on est dans une longue liste sans
   remonter au titre. Ni le rose de --primary, trop proche du rouge qui marque
   le côté absent, ni le cyan, réservé au rangement. Le côté absent est rouge
   ET barré : la couleur seule ne se voit pas partout. Ce rouge est plus clair
   que --danger : sur la teinte ambre, --danger tombait à 4,31:1 ; #ff8a98
   tient 6,42:1 au pire des quatre teintes et des deux fonds de panneau. */
.sync-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  margin-right: 0.45rem;
  padding: 0.1rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  font-size: 0.75rem;
  font-weight: 800;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  vertical-align: 0.1em;
  white-space: nowrap;
}
.sync-badge__lien { opacity: 0.8; }
.sync-badge__icone { flex: none; display: block; width: 1.2rem; height: 1.2rem; }
.sync-badge__icone--absente { color: #ff8a98; }
/* Le trait qui barre l'icône absente : plus épais que le dessin, pour se lire
   même à cette taille. */
.sync-badge__barre { stroke-width: 2.6; }
.sync-badge--dossier  { background: rgba(181, 140, 255, 0.15); color: #d6c2ff; border-color: rgba(181, 140, 255, 0.4); }
.sync-badge--espace   { background: rgba(255, 193, 90, 0.14); color: #ffd994; border-color: rgba(255, 193, 90, 0.38); }
.sync-badge--retrouve { background: rgba(76, 214, 168, 0.13); color: #8ff2cf; border-color: rgba(76, 214, 168, 0.36); }
.sync-badge--verifier { background: rgba(170, 177, 208, 0.12); color: #d5daf0; border-color: rgba(170, 177, 208, 0.36); }

/* ---------- Conteneurs ---------- */

/* Un média absent de la collection — un film créé sans copie, ni possédé ni
   souhaité — n'apparaît qu'à la recherche, et se distingue de ce qu'on possède
   vraiment. L'opacité seule ne suffirait pas à un lecteur d'écran : la carte
   porte aussi une pastille qui le dit en toutes lettres. */
.media-card--absent { opacity: 0.5; }
.media-card--absent:hover,
.media-card--absent:focus-within { opacity: 1; }

.panel, .card, .notice {
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: linear-gradient(145deg, rgba(20, 25, 59, 0.88), rgba(17, 20, 48, 0.62));
  box-shadow: var(--shadow-soft);
  backdrop-filter: blur(18px);
}
.panel { padding: clamp(1.1rem, 3vw, 1.8rem); }

.section-header { margin-bottom: 1.3rem; }
.section-header > h2, .section-header > h3 { display: flex; align-items: center; gap: 0.7rem; margin-bottom: 0.3rem; }
.section-header > h2::before, .section-header > h3::before {
  content: "";
  flex: 0 0 auto;
  width: 0.72rem;
  height: 0.72rem;
  border-radius: 3px;
  background: var(--accent);
  box-shadow: 0 0 24px rgba(255, 11, 88, 0.7);
}
.section-header .meta { margin-top: 0; }

.card { overflow: hidden; display: flex; flex-direction: column; }
.card-header, .card-body, .card-footer { padding: 1rem 1.1rem; }
.card-header { border-bottom: 1px solid var(--line); }
.card-header h3 { margin-bottom: 0.2rem; }
.card-body { flex: 1; }
.card-body > :last-child { margin-bottom: 0; }
.card-footer {
  border-top: 1px solid var(--line);
  color: var(--faint);
  font-size: 0.82rem;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.card-footer .btn { margin-left: auto; }
/* Pied d'actions en icônes (exemplaires d'un jeu) : une seule ligne, à droite. */
.card-footer--actions { flex-wrap: nowrap; justify-content: flex-end; }
.card-footer--actions .btn { margin-left: 0; }
/* Bouton réduit à son icône : carré, la cible garde la hauteur de .btn--sm. */
.btn--carre { padding-left: 0; padding-right: 0; min-width: 2.25rem; justify-content: center; }

.notice { padding: 1rem 1.15rem; }
.notice > :last-child { margin-bottom: 0; }
/* Un lien dans le texte d'un avis ne se distingue pas du texte par la seule
   couleur (1.4.1) : souligné au repos, pas seulement au survol. */
.notice p a { text-decoration: underline; text-underline-offset: 0.15em; }
.notice--info { border-color: rgba(120, 150, 255, 0.34); }
.notice--warning { border-color: rgba(255, 193, 90, 0.32); background: linear-gradient(145deg, rgba(75, 57, 30, 0.65), rgba(27, 26, 57, 0.62)); }
.notice--danger { border-color: rgba(255, 78, 99, 0.34); background: linear-gradient(145deg, rgba(74, 26, 36, 0.62), rgba(27, 26, 57, 0.62)); }
.notice--success { border-color: rgba(76, 214, 168, 0.32); background: linear-gradient(145deg, rgba(24, 66, 55, 0.6), rgba(27, 26, 57, 0.62)); }

.empty-state {
  padding: clamp(2rem, 6vw, 3.5rem) 1.5rem;
  border: 1px dashed var(--line);
  border-radius: var(--radius-md);
  background: rgba(8, 10, 30, 0.35);
  text-align: center;
}
.empty-state h3 { color: #fff; }
.empty-state p { max-width: 38rem; margin-inline: auto; }

/* ---------- Grilles generiques ---------- */

.grid { display: grid; gap: 1rem; }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); }

/* ---------- Barre d'outils de liste ---------- */

/* La barre ne porte plus que la recherche, le bouton « Filtres », le compteur
   et l'action : facettes et tri vivent dans le tiroir .filter-drawer. Sur
   telephone, elle empilait six controles, 320px avant le premier resultat ;
   elle en tient deux rangees. */
.toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.7rem;
  padding: 0.8rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: rgba(8, 10, 30, 0.38);
}
.toolbar__search { flex: 1 1 14rem; min-width: 0; }
.toolbar select, .toolbar input { min-height: var(--touch); }
.toolbar > .btn { margin-left: auto; }
.toolbar > .toolbar__filtres { margin-left: 0; }
.toolbar__count { color: var(--faint); font-size: 0.82rem; white-space: nowrap; }
/* Filtre posé depuis une fiche (partial/toolbar, clé epingles) : la pastille
   nomme la valeur et se retire d'un clic. Cible tactile pleine, et un nom long
   (« Société : Bandai Namco Entertainment ») se coupe au lieu d'élargir la
   barre au-delà de l'écran. */
.toolbar__epingles { display: contents; }
.toolbar__epingle { min-height: var(--touch); max-width: 100%; font-size: 0.82rem; }
.toolbar__epingle > span:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; }

@media (max-width: 680px) {
  .toolbar { gap: 0.5rem; padding: 0.6rem; }
  /* La recherche et le bouton de creation gardent toute la largeur : ce sont
     les deux cibles qu on vise sans regarder. Le compteur passe a droite du
     bouton « Filtres ». */
  .toolbar__search, .toolbar > .btn:not(.toolbar__filtres) { flex: 1 1 100%; margin-left: 0; }
  .toolbar__count { margin-left: auto; }
}

/* ---------- Tiroir des filtres ---------- */

/* Decision de l'utilisateur, pour toutes les listes : facettes et tri dans un
   tiroir a droite — le menu de navigation s'ouvre deja a gauche —, sur tous
   les ecrans, 90 % de la largeur sur telephone pour garder la liste en vue
   derriere, et des choix appliques a chaque changement, comme avant.
   <dialog> ouvert par showModal() (ui.js, initFiltres()) : arriere-plan
   inerte, Echap et retour du focus sont ceux du navigateur. */
.filter-drawer {
  position: fixed;
  inset: 0 0 0 auto;
  width: min(22rem, 90vw);
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  border-left: 1px solid var(--line);
  background: linear-gradient(160deg, rgba(16, 21, 52, 0.99), rgba(9, 10, 30, 0.99));
  box-shadow: var(--shadow);
  color: var(--text);
}
.filter-drawer[open] { display: flex; flex-direction: column; }
.filter-drawer::backdrop {
  background: rgba(3, 4, 14, 0.6);
  backdrop-filter: blur(2px);
}
.filter-drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--line);
}
.filter-drawer__head h2 { margin: 0; font-size: 1.05rem; }
.filter-drawer__body {
  flex: 1 1 auto;
  overflow-y: auto;
  display: grid;
  align-content: start;
  gap: 1rem;
  padding: 1.25rem;
}
/* Libelles visibles : dans la barre, ils etaient caches faute de place, et
   la premiere option tenait lieu d'etiquette. Une liste par rangee, donc
   aucune troncature quelle que soit la longueur des options. */
.filter-drawer__field { display: grid; gap: 0.35rem; min-width: 0; }
.filter-drawer__field label { color: var(--muted); font-size: 0.82rem; font-weight: 700; }
.filter-drawer__field select { width: 100%; }
.filter-drawer__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  padding: 1rem 1.25rem;
  border-top: 1px solid var(--line);
}
/* Compteur et « Reinitialiser » sur une ligne, l'action principale pleine
   largeur dessous : cote a cote, « Voir les resultats » passait sur deux
   lignes a 375px. */
/* --muted et non --faint : 4,43:1 seulement en haut du pied. */
.filter-drawer__count { flex: 1 1 auto; margin: 0; color: var(--muted); font-size: 0.82rem; }
.filter-drawer__foot .btn--primary { flex: 1 1 100%; }

/* Entree depuis la droite. @starting-style : sans lui, un element qui passe
   de display: none a visible ne transitionne pas. */
@media (prefers-reduced-motion: no-preference) {
  .filter-drawer[open] { transition: transform 180ms ease-out; }
  @starting-style {
    .filter-drawer[open] { transform: translateX(100%); }
  }
}

/* Sans script, le tiroir est rendu ouvert, non modal et --en-ligne : il se
   pose dans le flux de la barre, sous la recherche, et ses listes se
   soumettent par le bouton « Filtrer ». Une classe et non :not(:modal), qui
   est un selecteur invalide la ou :modal n'existe pas : la regle entiere
   tombait, et le tiroir s'affichait en panneau fixe sans fermeture.
   Avec script, html.js (pose dans <head>) le cache avant le premier rendu,
   sans quoi il apparaitrait puis disparaitrait ; ui.js retire la classe
   une fois qu'il en a pris la charge, et pose --repli s'il ne le peut pas
   (pas de showModal) pour le rendre a la barre. */
.filter-drawer--en-ligne {
  position: static;
  flex: 1 1 100%;
  width: auto;
  height: auto;
  border: 0;
  background: none;
  box-shadow: none;
}
.filter-drawer--en-ligne .filter-drawer__head,
.filter-drawer--en-ligne .filter-drawer__foot { display: none; }
.filter-drawer--en-ligne .filter-drawer__body { padding: 0; grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr)); }
.js .filter-drawer--en-ligne:not(.filter-drawer--repli) { display: none; }

/* ---------- Pagination de liste ---------- */

.pagination { margin-top: var(--space-4); }
.pagination__list {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 0.4rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.pagination__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--touch);
  min-height: var(--touch);
  padding: 0 0.8rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-pill);
  background: rgba(8, 10, 30, 0.38);
  color: var(--text);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
}
a.pagination__link:hover { border-color: var(--accent-2); text-decoration: none; }
.pagination__link[aria-current="page"] {
  border-color: var(--accent-fond);
  background: var(--accent-fond);
  color: #fff;
  font-weight: 700;
}
.pagination__link[aria-disabled="true"] {
  color: var(--faint);
  border-style: dashed;
  background: transparent;
  cursor: default;
}
.pagination__gap { color: var(--faint); padding: 0 0.2rem; }

/* Pendant qu une page arrive, la grille reste en place mais s estompe : la
   remplacer par un indicateur ferait sauter la mise en page a chaque frappe. */
[data-collection][aria-busy="true"] { opacity: 0.55; transition: opacity 0.15s; }
/* Apres un changement de page, collection.js pose le focus sur la liste : le
   contour dit au clavier ou il est arrive (WCAG 2.4.7). */
[data-collection]:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 4px; border-radius: var(--radius-sm); }

/* Sur telephone, pastilles resserrees : sept numeros et deux fleches doivent
   tenir sur deux lignes au plus dans 375px. */
@media (max-width: 680px) {
  .pagination__list { gap: 0.3rem; }
  .pagination__link { padding: 0 0.6rem; }
}

/* ---------- Collection : grille de cartes ---------- */

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--card-min), 1fr));
  gap: 1rem;
}

.media-card {
  position: relative;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: linear-gradient(145deg, rgba(20, 25, 59, 0.88), rgba(17, 20, 48, 0.62));
  box-shadow: var(--shadow-soft);
  color: inherit;
  transition: transform 140ms ease, border-color 140ms ease;
}
.media-card:hover {
  transform: translateY(-3px);
  border-color: rgba(255, 11, 88, 0.45);
  text-decoration: none;
}
.media-card__cover {
  position: relative;
  aspect-ratio: 2 / 3;
  max-width: 100%;
  overflow: hidden;
  background: rgba(5, 7, 24, 0.6);
}
.media-card__cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.media-card__body { padding: 0.85rem; display: grid; gap: 0.35rem; }
.media-card__title { color: #fff; font-weight: 700; font-size: 0.95rem; line-height: 1.3; margin: 0; }
.media-card__subtitle { color: var(--faint); font-size: 0.8rem; font-style: italic; margin: 0; }
.media-card__meta { color: var(--muted); font-size: 0.8rem; margin: 0; }
/* Les machines sur lesquelles le jeu est possédé, en pictogrammes nommés. */
.media-card__icones { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem 0.5rem; color: var(--muted); font-size: 0.78rem; margin: 0; }
.media-card__badges { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.15rem; }
/* Carte munie du bouton de pose rapide des mots-cles : l enveloppe tient la
   place de la carte dans la grille, et le bouton se pose sur la jaquette, hors
   du lien. La carte remplit l enveloppe, qui s etire comme elle le faisait. */
.media-card-shell { position: relative; display: grid; }
.media-card-shell > .media-card { min-width: 0; }
/* Les deux boutons d'etat de la fiche d'un amiibo (« Je l'ai », « Souhaite »),
   avec leur libelle : sur la liste, ils sont des icones posees sur l'image
   (.media-card__controles). */
.media-card__toggles { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.4rem; }
/* Une figurine detouree sur fond transparent, ni carree ni au format 2/3 :
   inscrite et non recadree, sinon la tete ou le socle sautent. */
.media-card--contain .media-card__cover img,
.detail__cover--contain img { object-fit: contain; padding: 0.5rem; }
/* Le catalogue des amiibo montre TOUT par defaut : presque toutes ses cartes
   sont en retrait, et une opacite de 0.5 sur le texte le ferait passer sous
   4.5:1. Seule l'image s'efface ; la pastille « Manquant » dit le reste. */
.media-card--contain.media-card--absent { opacity: 1; }
.media-card--contain.media-card--absent .media-card__cover { opacity: 0.4; }
.media-card--contain.media-card--absent:hover .media-card__cover,
.media-card--contain.media-card--absent:focus-visible .media-card__cover { opacity: 1; }
/* La boite de la fiche est reservee avant l'image : sans elle, la page
   sautait a son arrivee. Carree, comme les boites de variante. */
.detail__cover--contain { aspect-ratio: 1; }
.detail__cover--contain img { height: 100%; }
/* Logo d'une plateforme relevé sur IGDB : une marque, souvent noire sur fond
   transparent et bien plus large que haute. Recadrée sur le fond sombre d'une
   carte, elle disparaissait ; elle est inscrite entière sur une plaque claire. */
.media-card--logo .media-card__cover,
.detail__cover--logo { background: #eef0fa; }
.media-card--logo .media-card__cover img,
.detail__cover--logo img { object-fit: contain; padding: 12%; }
.detail__cover--logo { aspect-ratio: 3 / 2; }
.detail__cover--logo img { width: 100%; height: 100%; }
/* Boutons d'une carte (partial/card-controles) : actions (exclure du mur),
   bascules d'etat (« Je l'ai », « Souhaite » d'un amiibo ou d'un tome), puis
   mots-cles. Un seul conteneur les aligne, quel que soit leur nombre ; hors
   du lien, un <button> ne vivant pas dans un <a>.
   C'est un popover : sous 680px, le bouton « ⋯ » l'ouvre en feuille
   d'actions. Au-dela, les regles d'auteur l'emportent sur celles du
   navigateur ([popover]:not(:popover-open) { display: none }, position
   fixed, fond Canvas) et le posent sur la jaquette. */
.media-card__controles {
  position: absolute;
  inset: 0.5rem 0.5rem auto auto;
  z-index: 2;
  display: flex;
  gap: 0.4rem;
  width: auto;
  height: auto;
  margin: 0;
  padding: 0;
  overflow: visible;
  border: 0;
  background: none;
  color: inherit;
}
.media-card__controles-titre,
.media-card__plus { display: none; }
/* Sur la jaquette, l'icone suffit : le libelle (le « title » du bouton,
   affiche dans la feuille d'actions) est replie a zero. Le nom accessible
   reste l'aria-label. */
.media-card__controles > .btn {
  width: 2.25rem;
  padding: 0;
  gap: 0;
  font-size: 0;
  background: rgba(8, 10, 30, 0.78);
  backdrop-filter: blur(6px);
  color: #fff;
}
/* Decision de l'utilisateur : toujours visibles, les icones faisaient
   tache. Revelees au survol de la carte et au focus clavier ; sur un ecran
   tactile sans survol, elles restent affichees. */
@media (hover: hover) {
  /* Sur chaque bouton et non sur le conteneur : un enfant ne remonte pas
     l'opacité de son parent, et un état posé (appréciation, suivi) reste
     affiché (décision de l'utilisateur) — l'icône du vide, elle, ne vient
     qu'au survol comme les autres. */
  .media-card__controles > .btn { opacity: 0; transition: opacity 140ms ease; }
  .media-card-shell:hover > .media-card__controles > .btn,
  .media-card-shell:focus-within > .media-card__controles > .btn,
  .media-card__controles > .media-card__choix--pose { opacity: 1; }
}
/* Bascule enfoncee : un bouton-icone n'a pas la place de la coche de
   .btn[aria-pressed="true"], il se remplit de l'accent. */
.media-card__controles > .btn[aria-pressed="true"] {
  border-color: var(--accent);
  background: var(--accent);
  box-shadow: none;
}
.media-card__controles > .btn[aria-pressed="true"]::before { content: none; }
/* Jeu de liens de filtrage d'une fiche (Tous / Manquants / Possedes) :
   des liens, pas des onglets ARIA, la page se recharge. */
.filter-links { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.media-card__actions {
  margin-top: auto;
  padding: 0.6rem 0.85rem;
  border-top: 1px solid var(--line);
  display: flex;
  gap: 0.5rem;
}

/* Variante sans jaquette : une pochette d'attente plutot qu'un trou. Le format
   2/3 est conserve, sans quoi une carte sans image casserait l'alignement des
   rangees partout ou elle voisine une carte illustree. */
.media-card--nocover .media-card__cover {
  display: grid;
  place-items: center;
  background:
    radial-gradient(circle at 50% 35%, rgba(255, 11, 88, 0.18), transparent 60%),
    linear-gradient(160deg, rgba(43, 53, 113, 0.55), rgba(8, 10, 30, 0.75));
}
.media-card__placeholder {
  font-size: 2.6rem;
  font-weight: 900;
  line-height: 1;
  /* 0.5 et pas moins : sous 0.46 l initiale tombe sous 4.5:1 sur le fond
     sombre de la vignette, mesure faite sur la page rendue. */
  color: rgba(255, 255, 255, 0.5);
}

/* ---------- Collection : liste de lignes ---------- */

.data-list {
  display: grid;
  gap: 0.5rem;
  list-style: none;
  padding: 0;
  margin: 0;
}
.data-list__row {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: rgba(8, 10, 30, 0.38);
  color: inherit;
  transition: border-color 140ms ease, background 140ms ease;
}
a.data-list__row:hover { border-color: rgba(255, 11, 88, 0.45); background: rgba(255, 255, 255, 0.04); text-decoration: none; }
.data-list__cover {
  flex: 0 0 auto;
  width: var(--cover-w);
  aspect-ratio: 2 / 3;
  overflow: hidden;
  border-radius: 0.5rem;
  background: rgba(5, 7, 24, 0.6);
}
.data-list__cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.data-list__main { flex: 1 1 12rem; min-width: 0; }
.data-list__title { color: #fff; font-weight: 700; margin: 0; }
.data-list__sub { color: var(--faint); font-size: 0.82rem; font-style: italic; margin: 0; }
.data-list__aside {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  color: var(--muted);
  font-size: 0.85rem;
}
.data-list__actions { display: flex; gap: 0.4rem; margin-left: auto; }
/* Une liste de choix dans les actions d'une ligne : sans min-width, ni l'élément
   de grille ni l'élément flex ne descendent sous la largeur de la plus longue
   option, et un titre de film élargissait toute la liste au-delà de l'écran
   d'un téléphone. Les deux niveaux doivent céder. */
.data-list > li { min-width: 0; }
.data-list__actions select, .data-list__actions input { flex: 1 1 12rem; width: auto; min-width: 0; }

/* La date passait à la ligne sous le nom : 92px de haut pour deux informations
   courtes, sur des écrans qui en alignent des milliers. Elle tient désormais à
   droite du nom, comme les métadonnées d'une ligne de collection. Seules les
   actions, qui sont des boutons, gardent leur propre ligne. */
@media (max-width: 680px) {
  .data-list__row { flex-wrap: wrap; gap: 0.7rem; padding: 0.55rem 0.8rem; }
  .data-list__main { flex: 1 1 8rem; }
  .data-list__aside { flex: 0 0 auto; margin-left: auto; font-size: 0.78rem; white-space: nowrap; }
  .data-list__actions { width: 100%; margin-left: 0; }
  .data-list__title, .data-list__sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* Ligne a vignette (partial/media-row) : la colonne de droite, pastilles
     comprises, passe sous le titre au lieu de lui prendre sa largeur. La
     vignette de 6rem de haut laisse la place de cette seconde ligne sans
     agrandir la rangee. */
  .data-list__row:has(> .data-list__cover) {
    display: grid;
    grid-template-columns: var(--cover-w) minmax(0, 1fr);
    align-items: center;
    align-content: center;
    gap: 0.25rem 0.7rem;
  }
  .data-list__row:has(> .data-list__cover) > .data-list__cover { grid-row: 1 / span 2; }
  .data-list__row:has(> .data-list__cover) > .data-list__aside {
    grid-column: 2;
    align-self: start;
    margin-left: 0;
    white-space: normal;
    gap: 0.35rem 0.5rem;
  }
  .data-list__row:has(> .data-list__cover) > .data-list__main { align-self: end; }
  .data-list__row:has(> .data-list__cover):not(:has(> .data-list__aside)) > .data-list__main { grid-row: 1 / span 2; align-self: center; }
  .data-list__row:has(> .data-list__cover) > .data-list__actions { grid-column: 1 / -1; }
}

/* Copies et souhaits d'une fiche (movie/index/view.phtml) : une ligne compacte
   par copie, pastilles puis actions, là où une carte empilait des phrases
   coupées et un pied de carte dans la colonne étroite de la fiche. Les
   actions passent sous les pastilles d'un bloc ; « Modifier » et
   « Supprimer » tiennent sur une ligne, le troisième bouton d'un souhait
   (« Je l'ai ») peut passer dessous plutôt que déborder. */
.copy-list { display: grid; gap: 0.5rem; list-style: none; padding: 0; margin: 0 0 1rem; }
.copy-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 0.75rem;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: rgba(8, 10, 30, 0.38);
}
.copy-row > .badge-row { gap: 0.35rem; min-width: 0; }
/* Un nom long (espace, compilation) est coupé plutôt que d'élargir la
   colonne ; le nom complet reste au survol (title). min-width: 0 et flex 0 1
   auto : sans eux, l'élément flex refusait de rétrécir et l'ellipse ne
   jouait jamais. */
.copy-row .badge { display: inline-block; flex: 0 1 auto; min-width: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }

/* Le coffret : un cadre dont la légende porte le nom — il passe à la ligne
   au lieu de déborder —, puis son étagère une fois, puis ses copies. Un vrai
   <fieldset> : le navigateur ouvre la bordure sous la légende sans qu'il faille
   imiter le fond, translucide, du panneau. */
.copy-group {
  min-width: 0;
  margin: 0;
  padding: 0.3rem 0.5rem 0.5rem;
  border: 1px solid rgba(255, 11, 88, 0.35);
  border-radius: var(--radius-sm);
}
/* La légende reste dans la bordure même sur deux lignes : un bloc à largeur
   bornée, et non un texte qui déborde au-dessus du cadre. */
.copy-group__titre {
  display: block;
  float: none;
  max-width: calc(100% - 0.5rem);
  padding: 0 0.35rem;
  font-size: 0.78rem;
  font-weight: 800;
  line-height: 1.35;
}
/* Le nom du coffret est le lien vers sa fiche : souligné, pour qu'on voie
   qu'il se clique. */
.copy-group__titre a { color: #ff8bae; overflow-wrap: anywhere; text-decoration: underline; text-underline-offset: 2px; }
.copy-group__titre a:hover { color: #ffb3ca; }
.copy-group__titre a:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 2px; border-radius: 4px; }
.copy-group__rangement { margin: 0.2rem 0 0.4rem; }
.copy-group .copy-list { gap: 0; margin-bottom: 0; }
/* Dans le cadre, les copies n'ont plus de cadre à elles : un trait les sépare,
   et la largeur rendue suffit à « Modifier » et « Supprimer » côte à côte. */
.copy-group .copy-row { padding: 0.45rem 0 0; border: 0; border-radius: 0; background: none; }
.copy-group .copy-row + .copy-row { margin-top: 0.45rem; border-top: 1px solid var(--line); }
.copy-row__actions { flex: 1 1 100%; flex-wrap: wrap; gap: 0.4rem; min-width: 0; }
/* Un peu moins de marge intérieure qu'ailleurs : « Modifier » et « Supprimer »
   manquaient d'un pixel pour tenir côte à côte dans la colonne de la fiche. */
.copy-row .btn--sm { padding-left: 0.8rem; padding-right: 0.8rem; }
/* Une pastille qui est un lien (le coffret) : un focus clavier franc, que le
   contour global, discret sur une si petite cible, ne suffisait pas à donner. */
a.badge:focus-visible { outline: 2px solid var(--accent-2); outline-offset: 3px; }
a.badge:hover { text-decoration: none; filter: brightness(1.12); }

/* ---------- Fiche detail ---------- */

.detail {
  display: grid;
  grid-template-columns: minmax(0, 18rem) minmax(0, 1fr);
  gap: 1.4rem;
  align-items: start;
}
/* minmax(0, 1fr) et min-width: 0 : sans eux, la piste implicite de cette
   grille prenait la largeur du contenu le plus large et l'affiche comme les
   panneaux débordaient sous la colonne de droite. */
.detail__media { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1rem; min-width: 0; }
.detail__body { display: grid; gap: 1.4rem; min-width: 0; }
.detail__cover {
  position: relative;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: rgba(5, 7, 24, 0.6);
}
.detail__cover img { width: 100%; display: block; }
/* Pastille posée sur la jaquette (« DLC ») : même dessin que celle d'une carte
   (.media-card__kind, plus bas), seule sa place est déclarée ici. */
.detail__cover-badge {
  position: absolute;
  top: 0.6rem;
  left: 0.6rem;
}

/* Boutons vers les pages du jeu chez Steam, IGDB, HowLongToBeat (fiche d'un jeu). */
.liens-externes { display: flex; flex-wrap: wrap; gap: 0.4rem; }
/* Libellé centré sur une valeur plus haute qu'une ligne de texte : boutons
   « Sur Internet », pictogrammes « Sorti sur ». */
.props dt.props__dt--centre { align-self: center; }

/* Liste de proprietes cle / valeur */
.props { display: grid; grid-template-columns: max-content 1fr; gap: 0.5rem 1rem; margin: 0; }
.props dt { color: var(--muted); font-size: 0.82rem; font-weight: 700; }
.props dd { margin: 0; color: #fff; }
/* Propriétés empilées, libellé au-dessus de la valeur : pour une colonne
   étroite (panneau .detail__avis sous la jaquette, 18rem). Même rythme que
   .props sous 560 px, où toutes les fiches s'empilent ainsi. */
.props--empile { grid-template-columns: minmax(0, 1fr); gap: 0.35rem; }
.props--empile dd { margin-bottom: 0.6rem; }
.props--empile dd:last-child { margin-bottom: 0; }
.props dd:empty::before { content: "\2014"; color: var(--faint); }
/* Les valeurs d'une fiche mènent à ce qui les partage (partial/liste-de-liens) :
   le rose du lien ne fait que 2,5:1 contre le blanc du texte voisin, la couleur
   seule ne les distingue pas (WCAG 1.4.1). Souligné, comme .copy-group__titre a.
   Les pastilles de mots-clés et les boutons, qui ont leur cadre, en sont exclus. */
.props dd a:not(.badge):not(.btn),
.copy-link { text-decoration: underline; text-underline-offset: 2px; }

/* Une colonne : la colonne de gauche prend toute la largeur, ses panneaux
   (copies, « Dans ma collection ») aussi. Seule la jaquette est bornee, pour
   ne pas devenir une affiche de 900 px de haut sur tablette, et centree :
   bornee et collee a gauche, elle laissait une marge vide a droite. Sur un
   telephone, 24rem depassent la largeur utile et elle la remplit. Le sizes
   du contexte 'detail' de mediaCover() suit cette borne. */
@media (max-width: 900px) {
  .detail { grid-template-columns: 1fr; }
  .detail__cover { width: 100%; max-width: 24rem; justify-self: center; }
}
@media (max-width: 560px) {
  .props { grid-template-columns: 1fr; gap: 0.15rem; }
  .props dd { margin-bottom: 0.6rem; }
}

/* ---------- Libellé de chiffre (chrono/partie.phtml) ---------- */

.stat__label { color: var(--muted); font-size: 0.8rem; }

/* ---------- Formulaires ---------- */

/* align-content: start est ce qui empêche un champ de s'étirer sur la hauteur
   de sa rangée. Sans lui, un <input> voisin d'un <textarea> dans la même
   .field-grid hérite de la hauteur du plus grand : la saisie d'un nom faisait
   82px de haut à côté d'une description. */
.field { display: grid; gap: 0.45rem; min-width: 0; align-content: start; }
.field-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 1rem; }
/* Formulaire à un ou deux champs — un style, un rôle, un support. Sans
   plafond, la saisie d'un nom de trois lettres s'étirait sur 1300px. */
.panel--narrow { max-width: 46rem; }
/* Bloc a l'interieur d'un panneau (les acces API de « Mon compte ») : un
   fond plus soutenu et un liseré plus franc que le panneau qui le porte,
   sans quoi deux degradés superposés se confondent. Pas d'ombre ni de flou :
   le panneau parent les porte deja. */
.panel--inset {
  background: var(--surface-strong);
  border-color: var(--line-strong);
  box-shadow: none;
  backdrop-filter: none;
}
.panel--inset .form-actions:last-child { margin-bottom: 0; }
.field--wide { grid-column: 1 / -1; }

label, legend, .field__label { font-weight: 800; color: #fff; font-size: 0.84rem; }
.field__label { display: block; }
.field > label { color: #fff; }

input, select, textarea {
  width: 100%;
  min-height: var(--touch);
  border: 1px solid var(--line);
  border-radius: 0.9rem;
  background: rgba(5, 7, 24, 0.58);
  color: #fff;
  font: inherit;
  font-size: 0.92rem;
  padding: 0.65rem 0.9rem;
  outline: none;
}
select {
  appearance: none;
  padding-right: 2.2rem;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 1.1rem) 55%, calc(100% - 0.75rem) 55%;
  background-size: 0.35rem 0.35rem, 0.35rem 0.35rem;
  background-repeat: no-repeat;
}
select option { background: #101534; color: #fff; }
textarea { min-height: 7.5rem; resize: vertical; }
input::placeholder, textarea::placeholder { color: #687298; }
input:focus, select:focus, textarea:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 4px rgba(255, 11, 88, 0.16);
}
input[type="file"] { padding: 0.5rem; }
input[type="checkbox"], input[type="radio"] {
  width: 1.05rem;
  height: 1.05rem;
  flex: 0 0 auto;
  min-height: 0;
  accent-color: var(--accent);
  margin: 0;
}
input[type="range"] {
  padding: 0;
  background: transparent;
  border: 0;
  accent-color: var(--accent);
}
input[type="date"]::-webkit-calendar-picker-indicator { filter: invert(1) opacity(0.6); cursor: pointer; }

.field__error, .input-error {
  list-style: none;
  padding: 0;
  margin: 0;
  color: #ffa6b1;
  font-size: 0.8rem;
}
.field--invalid input, .field--invalid select, .field--invalid textarea { border-color: var(--danger); }
.helper { font-size: 0.8rem; }

fieldset {
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 1rem;
  margin: 0;
  min-width: 0;
}
legend { padding-inline: 0.4rem; }
/* Groupe de choix : le fieldset est la pour l annonce du groupe, pas pour
   dessiner un cadre de plus a l interieur d un panneau. */
.fieldset--bare {
  display: block;
  border: 0;
  padding: 0;
}
.fieldset--bare > legend { padding-inline: 0; margin-bottom: 0.45rem; }

.choice-row { display: grid; gap: 0.5rem; margin-top: 0.6rem; }
.choice-row--inline {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
/* Chaque choix devient une pastille : plus compact qu une ligne par option, et
   l etat coche se lit sans suivre la colonne des cases. La case elle-meme est
   masquee a l oeil mais reste dans l arbre d accessibilite — la pastille est la
   commande — et une coche double la couleur, qui ne doit jamais porter seule
   une information. Trente-quatre styles de film passent ainsi de 333 a 180px. */
.choice-row--inline > .choice > input {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}
.choice-row--inline > .choice::before {
  content: "";
  width: 0;
  height: 0.45rem;
  border-bottom: 2px solid var(--accent-2);
  border-right: 2px solid var(--accent-2);
  transform: rotate(45deg) translateY(-0.12rem);
  opacity: 0;
  transition: opacity 140ms ease, width 140ms ease, margin-right 140ms ease;
}
.choice-row--inline > .choice:has(input:checked)::before {
  width: 0.26rem;
  opacity: 1;
  margin-right: 0.3rem;
}
.choice-row--inline > .choice {
  min-height: 2.25rem;
  font-size: 0.82rem;
  padding: 0.25rem 0.75rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.04);
  transition: border-color 140ms ease, background 140ms ease, color 140ms ease;
}
.choice-row--inline > .choice:hover { border-color: var(--line-strong); color: #fff; }
.choice-row--inline > .choice:has(input:checked) {
  border-color: rgba(255, 11, 88, 0.5);
  background: rgba(255, 11, 88, 0.16);
  color: #fff;
}
/* Le contour de focus doit suivre la pastille et non la seule case. */
.choice-row--inline > .choice:has(input:focus-visible) {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(255, 11, 88, 0.45);
}

/* [hidden] ne l'emporte pas sur le display de .choice : le filtre de la pose
   rapide des mots-cles (quick-tags.js) masque des pastilles par l'attribut. */
.choice-row--inline > .choice[hidden] { display: none; }

/* Pose rapide des mots-cles : la liste d'un compte peut compter des dizaines
   de pastilles, la boite reste a hauteur d'ecran et c'est la liste qui
   defile, le champ de creation restant visible. */
.quick-tags [data-quick-tags-liste] { max-height: 40vh; overflow-y: auto; padding: 0.2rem; }
.btn .icon-tag { flex: none; }

/* Au doigt, la pastille reprend la cible tactile de reference. */
@media (max-width: 680px) {
  .choice-row--inline { gap: 0.5rem; }
  .choice-row--inline > .choice { min-height: var(--touch); padding-inline: 0.9rem; }
}
.choice {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  min-height: var(--touch);
  color: var(--muted);
  font-weight: 650;
  font-size: 0.88rem;
  cursor: pointer;
}
.choice input { flex: 0 0 auto; }

/* Grille de choix illustres (captcha de l'inscription).
   Les icones sont des PNG sombres : on les pose sur une pastille claire
   plutot que de parier sur leur couleur. */
.choice-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(5rem, 1fr)); gap: 0.6rem; margin-top: 0.6rem; }
.choice-grid .choice {
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.7rem 0.4rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.9);
  text-align: center;
}
.choice-grid .choice:hover { border-color: rgba(255, 11, 88, 0.5); }
.choice-grid .choice:has(input:checked) {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(255, 11, 88, 0.22);
}
.choice-grid img { width: 2.5rem; height: 2.5rem; object-fit: contain; }

@media (max-width: 680px) {
}

/* ---------- Assistant multi-etapes ---------- */

.steps { display: grid; gap: 1rem; counter-reset: step; }
.step { position: relative; }
.step > .section-header > h2 { counter-increment: step; }
.step > .section-header > h2::before {
  content: counter(step);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.7rem;
  height: 1.7rem;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--accent-fond), var(--accent-fond-2));
  color: #fff;
  font-size: 0.85rem;
  font-weight: 900;
  box-shadow: 0 0 24px rgba(255, 11, 88, 0.5);
}

/* ---------- Tableaux (fragments et ecrans denses) ---------- */

.table-wrap { overflow-x: auto; border-radius: var(--radius-md); border: 1px solid var(--line); }
.table-wrap table { width: 100%; border-collapse: collapse; background: rgba(7, 9, 27, 0.42); }
.table-wrap caption { caption-side: bottom; padding: 0.8rem; text-align: left; color: var(--muted); font-size: 0.82rem; }
.table-wrap th, .table-wrap td { padding: 0.8rem; border-bottom: 1px solid var(--line); text-align: left; color: var(--muted); }
.table-wrap th { color: #fff; font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.1em; background: rgba(255, 255, 255, 0.05); }
.table-wrap tbody tr:last-child td { border-bottom: 0; }
.table-wrap tbody tr:hover { background: rgba(255, 255, 255, 0.04); }

/* ---------- Onglets ---------- */

.tabs__list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  list-style: none;
  padding: 0.35rem;
  margin: 0 0 1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: rgba(8, 10, 30, 0.38);
}
.tabs__tab {
  min-height: var(--touch);
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 0.85rem;
  font-weight: 700;
  padding: 0.5rem 1.1rem;
  cursor: pointer;
}
.tabs__tab[aria-selected="true"] { background: rgba(255, 255, 255, 0.08); color: #fff; }

/* ---------- Boite de dialogue native ---------- */

dialog.dialog {
  width: min(38rem, calc(100vw - 2rem));
  max-height: min(80vh, 48rem);
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: linear-gradient(145deg, rgba(23, 28, 65, 0.98), rgba(14, 17, 42, 0.98));
  color: var(--text);
  box-shadow: var(--shadow);
  overflow: hidden;
}
dialog.dialog::backdrop { background: rgba(3, 4, 14, 0.7); backdrop-filter: blur(3px); }
.dialog__head { padding: 1rem 1.2rem; border-bottom: 1px solid var(--line); }
.dialog__head h2 { margin: 0; font-size: 1.05rem; }
.dialog__body { padding: 1.2rem; overflow-y: auto; max-height: 55vh; }
.dialog__body > :last-child { margin-bottom: 0; }
.dialog__foot {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.9rem 1.2rem;
  border-top: 1px solid var(--line);
}
.dialog__foot > .btn:last-child { margin-left: auto; }

/* Liste de resultats d'une recherche Internet, injectee dans le dialogue */
.choice-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 0.5rem; }
.choice-list li {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: rgba(8, 10, 30, 0.38);
}
.choice-list label {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.7rem 0.9rem;
  cursor: pointer;
  font-weight: 600;
  color: #fff;
}
.choice-list img { width: 2.6rem; border-radius: 0.35rem; }
.choice-list .meta { display: block; font-weight: 400; }

/* Une ligne de resultat porte ses actions : importer la fiche, ou, quand la
   collection contient deja un film ressemblant, aller lui ajouter une copie. */
.choice-list__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.7rem;
  padding: 0.7rem 0.9rem;
  font-weight: 600;
  color: #fff;
}
.choice-list__head > span:first-child { flex: 1 1 12rem; min-width: 0; }
.choice-list__head .btn { flex: 0 0 auto; }
/* Vignette accolee au titre (recherche IGDB) : le bloc garde la place que
   la regle precedente donne au premier enfant, la vignette ne s'ecrase pas. */
.choice-list__media { display: flex; align-items: center; gap: 0.7rem; }
.choice-list__media > img { flex: 0 0 auto; height: auto; }
.choice-list__media > span { min-width: 0; }
.choice-list__item--owned { border-color: var(--warning); }
/* Le fond teinte ne separe la zone de la ligne qui la porte que de 1,12:1 :
   c'est une nuance, pas une limite. Le liseré, lui, ressort à 10,24:1 sur
   le fond de la boite. L'information elle-meme ne repose de toute facon
   pas sur la couleur : la zone s'annonce « Deja dans la collection ». */
.choice-list__owned {
  padding: 0.6rem 0.9rem 0.7rem;
  border-top: 1px solid var(--line);
  border-left: 3px solid var(--warning);
  background: rgba(255, 193, 90, 0.12);
}
.choice-list__owned > .meta { margin: 0 0 0.4rem; }

.owned-movie {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.7rem;
  padding: 0.35rem 0;
}
.owned-movie__cover { width: 2.6rem; border-radius: 0.35rem; }
.owned-movie__body { flex: 1 1 12rem; min-width: 0; font-weight: 600; color: #fff; }
.owned-movie .btn { flex: 0 0 auto; margin-left: auto; }

/* ---------- Jauge de difficulte (jeux de societe) ---------- */

.gauge {
  position: relative;
  display: inline-block;
  flex: 0 0 auto;
  width: 3.2rem;
  height: 1.6rem;
  overflow: hidden;
  border-radius: 3.2rem 3.2rem 0 0;
  background: rgba(255, 255, 255, 0.08);
}
.gauge__value {
  position: absolute;
  inset: 0;
  transform-origin: center bottom;
  border-radius: 3.2rem 3.2rem 0 0;
  background: linear-gradient(90deg, var(--success), var(--warning) 55%, var(--danger));
}
.gauge__mask {
  position: absolute;
  left: 0.5rem; right: 0.5rem; bottom: 0;
  height: 1.1rem;
  border-radius: 2.2rem 2.2rem 0 0;
  background: #131832;
}
.gauge__label {
  position: absolute;
  inset: auto 0 0;
  text-align: center;
  color: #fff;
  font-size: 0.7rem;
  font-weight: 800;
}

/* ---------- Icones chiffrees (age / joueurs / duree) ---------- */

.game-icons { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.game-icon {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.25rem 0.55rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.05);
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 700;
}
.game-icon img { width: 1rem; height: 1rem; object-fit: contain; opacity: 0.85; }

/* ---------- Divers ---------- */

.cover-preview {
  max-width: 12rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: rgba(5, 7, 24, 0.6);
}
.cover-preview img { width: 100%; display: block; }

.mt-3 { margin-top: var(--space-3); }
.mt-4 { margin-top: var(--space-4); }
.mt-5 { margin-top: var(--space-5); }

/* ---------- Chapitres depliants (aide) ---------- */

details.panel > summary {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  min-height: var(--touch);
  cursor: pointer;
  list-style: none;
}
details.panel > summary::-webkit-details-marker { display: none; }
details.panel > summary h2 { margin: 0; font-size: 1.05rem; }
details.panel > summary::before {
  content: "";
  flex: 0 0 auto;
  width: 0.72rem;
  height: 0.72rem;
  border-radius: 3px;
  background: var(--accent);
  box-shadow: 0 0 24px rgba(255, 11, 88, 0.7);
}
details.panel > summary::after {
  content: "\25BE";
  margin-left: auto;
  color: var(--faint);
  transition: transform 160ms ease;
}
details.panel[open] > summary::after { transform: rotate(180deg); }

.help-item {
  display: grid;
  grid-template-columns: minmax(0, 12rem) minmax(0, 1fr);
  gap: 1.2rem;
  margin-top: 1.2rem;
  padding-top: 1.2rem;
  border-top: 1px solid var(--line);
}
.help-item__media img { border-radius: var(--radius-sm); }
.help-item__body > :last-child { margin-bottom: 0; }
.help-item blockquote span { display: block; }
.help-item blockquote .c { color: #fff; font-style: italic; }
.help-item blockquote .a { color: var(--faint); font-size: 0.8rem; margin-top: 0.3rem; }
/* Sans illustration (aide des jeux vidéo) : le texte prend toute la largeur. */
.help-item--texte { grid-template-columns: minmax(0, 1fr); }

@media (max-width: 700px) {
  .help-item { grid-template-columns: 1fr; }
  .help-item__media { width: 100%; max-width: 12rem; justify-self: center; }
}

/* Enchainement de sous-listes dans un meme panneau (casting, filmographie) */
.data-list + h3 { margin-top: 1.5rem; }

/* ---------- Liste d'episodes (fiche serie) ---------- */

.episode-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 0.2rem; }
.episode-list li {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  min-height: 2rem;
  padding: 0.35rem 0.5rem;
  border-radius: 0.5rem;
  color: var(--muted);
  font-size: 0.86rem;
  cursor: pointer;
}
.episode-list li:hover { background: rgba(255, 255, 255, 0.05); }
.episode-list__number { color: var(--faint); font-variant-numeric: tabular-nums; }
.episode-list__name { flex: 1; min-width: 0; }
.episode-list__details {
  width: 100%;
  color: var(--accent-2);
  font-size: 0.78rem;
  font-weight: 700;
}

/* Indicateur de chargement AJAX */
.load { width: 1.2rem; height: 1.2rem; }

/* ---------- Paires min / max (caracteristiques d'un jeu) ---------- */

.range-grid {
  display: grid;
  grid-template-columns: minmax(5rem, 1fr) minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: 0.5rem 0.7rem;
}
.range-grid__label { color: #fff; font-size: 0.84rem; font-weight: 800; }

/* ---------- Paire de curseurs (recherche d'un jeu) ---------- */

.range-pair { display: grid; gap: 0.35rem; }

/* Un jeu qui ne rentre qu'approximativement dans les criteres */
.warningGame { border-color: rgba(255, 193, 90, 0.45); }

/* ---------- Tableau de bord ---------- */

/* En-tete de panneau portant une action a droite. */
.section-header--row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.6rem;
}
.section-header--row > h2, .section-header--row > h3 { margin-bottom: 0; }

/* Les derniers ajouts : une vedette et quatre satellites, sur une seule
   rangee. La vedette occupait d abord deux colonnes sur deux rangees, ce qui
   empilait deux jaquettes au format 2/3 : a 1920 de large le bloc montait a
   1100px et le panneau ne tenait pas dans un ecran 1080. Une rangee unique
   plafonne la hauteur a celle d une seule jaquette, et la vedette garde son
   rang en occupant une colonne plus large que les autres.

   Cinq colonnes, donc jamais de rangee orpheline : c est ce que .card-grid,
   dont le nombre de colonnes suit la largeur, ne peut pas garantir.
   Trois dispositions, chacune remplissant un rectangle plein :
     - au dela de 900px, la rangee de cinq ;
     - de 560 a 900px, trois colonnes, la vedette en couvrant deux : la premiere
       rangee porte la vedette et un satellite, la seconde les trois autres ;
     - sous 560px, deux colonnes, la vedette en tete et deux rangees de deux.
   Les deux bornes viennent de la largeur de carte : a 1024px les satellites
   font encore 130px, a 768px ils tombent a 92px et le titre se disloque.
   Basculer directement sur deux colonnes etait un contresens : a 1024px elles
   donnaient des cartes de 470px, donc des jaquettes de 705px de haut, plus
   hautes que la disposition qu on cherchait a fuir. */
.feature-grid {
  display: grid;
  grid-template-columns: 1.45fr repeat(4, minmax(0, 1fr));
  gap: 1rem;
}

/* La vedette prend la hauteur que la grille lui donne, ce sont les satellites
   qui la fixent. flex-basis a zero est le point cle : avec `auto`, la boite
   tirait sa hauteur de depart de l image qu elle contient — une affiche de
   326px de large y imposait 489px de haut et faisait grimper toute la rangee.
   A zero, elle ne se contente que de l espace restant, et object-fit recadre. */
.media-card--feature .media-card__cover {
  aspect-ratio: auto;
  flex: 1 1 0;
  min-height: 8rem;
}
.media-card--feature .media-card__cover img { min-height: 0; }
.media-card--feature .media-card__body { padding: 1.1rem; gap: 0.45rem; }
.media-card--feature .media-card__title { font-size: 1.25rem; }
.media-card--feature .media-card__subtitle, .media-card--feature .media-card__meta { font-size: 0.88rem; }
.media-card--feature .media-card__placeholder { font-size: 5rem; }

@media (max-width: 900px) {
  .feature-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .feature-grid > .media-card--feature { grid-column: span 2; }
}

@media (max-width: 560px) {
  .feature-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Sur deux colonnes la vedette est aussi large que la grille : une jaquette
     2/3 y ferait la hauteur de l ecran, d ou le recadrage en 16/9. */
  .media-card--feature .media-card__cover { aspect-ratio: 16 / 9; flex: 0 0 auto; }
}

/* Carte couchee : la suggestion du panneau Au hasard, qui dispose d'une demi
   largeur. Debout, sa jaquette au format 2/3 y serait haute d'un ecran. */
.media-card--wide { flex-direction: row; align-items: stretch; }
.media-card--wide .media-card__cover { flex: 0 0 8.5rem; aspect-ratio: 2 / 3; }
.media-card--wide .media-card__body { align-content: start; padding: 1rem; }
.media-card--wide .media-card__placeholder { font-size: 2.2rem; }

/* Les pastilles posées sur la jaquette (« Jeu vidéo », « DLC ») se mesurent à
   toute la carte : couchée, elles débordaient sur le titre. Bornées à la
   jaquette, moins leurs deux retraits, elles passent à la ligne. */
.media-card--wide .media-card__pastilles { max-width: calc(8.5rem - 1.1rem); }

@media (max-width: 420px) {
  .media-card--wide .media-card__cover { flex-basis: 6rem; }
  .media-card--wide .media-card__pastilles { max-width: calc(6rem - 1.1rem); }
}

/* Pastille de collection, posee sur la jaquette. Elle est hors du conteneur
   decoratif : sur un flux qui melange films, series et jeux, le type est une
   information et doit rester lue. La nature de l'objet (« DLC », clé marque)
   s'y range à côté, dans le même conteneur, pour ne jamais la recouvrir. */
.media-card__pastilles {
  position: absolute;
  top: 0.55rem;
  left: 0.55rem;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
}
.media-card__kind,
.detail__cover-badge {
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-pill);
  background: rgba(8, 10, 30, 0.78);
  backdrop-filter: blur(6px);
  color: #fff;
  font-size: 0.68rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 0.25rem 0.6rem;
}

/* Compteurs de collection : un volume et un acces, pas un tableau de chiffres. */
.count-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 0.2rem; }
.count-list__item { border-bottom: 1px solid var(--line); }
.count-list__item:last-child { border-bottom: 0; }
.count-list__link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: var(--touch);
  padding: 0.5rem 0.2rem;
  border-radius: var(--radius-sm);
  color: inherit;
}
.count-list__link:hover { background: rgba(255, 255, 255, 0.05); text-decoration: none; }
.count-list__label { color: #fff; font-weight: 700; font-size: 0.95rem; }
/* Chevron dessine plutot qu'ecrit : un ::after portant le caractere U+203A est
   lu a voix haute par une partie des lecteurs d'ecran, qui annoncent alors un
   signe de ponctuation apres chaque libelle. */
.count-list__label::after {
  content: "";
  display: inline-block;
  width: 0.34rem;
  height: 0.34rem;
  margin-left: 0.45rem;
  vertical-align: 0.1em;
  border-top: 2px solid var(--faint);
  border-right: 2px solid var(--faint);
  transform: rotate(45deg);
}
.count-list__value { color: #fff; font-size: 1.5rem; font-weight: 800; line-height: 1; }
.count-list__detail { color: var(--muted); font-size: 0.78rem; margin: 0 0 0.5rem 0.2rem; }
.count-list__total {
  margin: 1rem 0 0;
  padding-top: 0.8rem;
  border-top: 1px solid var(--line);
  color: var(--faint);
  font-size: 0.8rem;
}

/* Le survol d'une carte la souleve de trois pixels. C'est un mouvement declenche
   par l'interaction : on le supprime pour qui a demande moins d'animation, la
   bordure d'accentuation suffisant alors a marquer le survol. */
@media (prefers-reduced-motion: reduce) {
  .media-card { transition: border-color 140ms ease; }
  .media-card:hover { transform: none; }
}

/* ---------- Collection sur telephone : la carte devient une ligne ---------- */

/* Ces ecrans servent a verifier une collection en magasin : il faut voir
   beaucoup de titres d un coup et lire vite, pas admirer les jaquettes. La
   carte prenait pres de 500px par film — deux films par ecran. La ligne en
   prend une centaine.

   Le basculement est purement visuel : le balisage de partial/media-card ne
   bouge pas, et collection.js continue de lire les memes attributs data-*.
   Il ne vise que .card-grid ; .feature-grid, sur le tableau de bord, garde
   ses vignettes. */
@media (max-width: 680px) {
  .card-grid {
    grid-template-columns: 1fr;
    gap: 0.4rem;
  }

  .card-grid > .media-card,
  .card-grid > .media-card-shell > .media-card {
    flex-direction: row;
    align-items: stretch;
    border-radius: var(--radius-sm);
  }
  /* En ligne, un seul bouton « ⋯ » ouvre les boutons de la carte en feuille
     d'actions (decision de l'utilisateur : les icones alignees faisaient
     tache). C'est une bande sur tout le bord droit de la ligne, en echo a la
     jaquette du bord gauche, plus etroite qu'elle : la cible tactile de
     reference en largeur, toute la hauteur. Le corps entier, titre compris,
     reserve --reserve a droite. */
  .card-grid > .media-card-shell {
    grid-template-columns: minmax(0, 1fr);
    --reserve: 0px;
  }
  .card-grid > .media-card-shell:has(> .media-card__plus) { --reserve: var(--touch); }
  /* A 1px des bords : la bande se pose dans la bordure de la carte, qui est
     celle de l'enveloppe. */
  .media-card__plus {
    display: inline-flex;
    position: absolute;
    top: 1px;
    right: 1px;
    bottom: 1px;
    z-index: 2;
    width: var(--touch);
    min-height: 0;
    padding: 0;
    border: 0;
    border-left: 1px solid var(--line);
    border-radius: 0 calc(var(--radius-sm) - 1px) calc(var(--radius-sm) - 1px) 0;
    background: rgba(255, 255, 255, 0.03);
    color: var(--muted);
  }
  .media-card__plus:hover { background: rgba(255, 255, 255, 0.08); color: #fff; }
  .media-card__plus:focus-visible { outline: 2px solid var(--accent-2); outline-offset: -3px; }
  .media-card__controles { opacity: 1; }
  .media-card__controles:not(:popover-open) { display: none; }
  /* Feuille d'actions : en bas de l'ecran, le nom du media puis une action
     par rangee, libelle visible. */
  .media-card__controles:popover-open {
    position: fixed;
    inset: auto 0 0 0;
    display: grid;
    gap: 0.5rem;
    width: 100%;
    padding: 1rem 1rem calc(1rem + env(safe-area-inset-bottom));
    border-top: 1px solid var(--line);
    border-radius: var(--radius-md) var(--radius-md) 0 0;
    background: linear-gradient(160deg, rgba(16, 21, 52, 0.99), rgba(9, 10, 30, 0.99));
    box-shadow: var(--shadow);
    color: var(--text);
  }
  .media-card__controles:popover-open::backdrop { background: rgba(3, 4, 14, 0.6); }
  .media-card__controles:popover-open > .media-card__controles-titre {
    display: block;
    margin: 0 0 0.25rem;
    overflow: hidden;
    color: #fff;
    font-weight: 700;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .media-card__controles:popover-open > .btn {
    /* Le survol cache les boutons un par un (hover: hover) : ouverte, la
       feuille les montre tous. */
    opacity: 1;
    justify-content: flex-start;
    gap: 0.7rem;
    width: 100%;
    min-height: var(--touch);
    padding: 0 1rem;
    font-size: 0.82rem;
    background: rgba(255, 255, 255, 0.05);
    backdrop-filter: none;
  }
  /* Apres la regle de feuille, qui l'ecrasait a specificite egale : une
     bascule enfoncee garde son fond plein (fond de bouton, texte blanc a
     4,6:1), et la coche revient devant le libelle, l'etat ne tenant pas a
     la seule couleur. */
  .media-card__controles:popover-open > .btn[aria-pressed="true"] {
    border-color: var(--accent-fond);
    background: var(--accent-fond);
  }
  .media-card__controles:popover-open > .btn[aria-pressed="true"]::after {
    content: "\2713";
    margin-left: auto;
    font-size: 1rem;
  }
  /* Sans popover (navigateur ancien), pas de « ⋯ » qui n'ouvrirait rien :
     les boutons restent dans le coin, en icones. */
  @supports not selector(:popover-open) {
    .media-card__plus { display: none; }
    .card-grid > .media-card-shell:has(> .media-card__controles) { --reserve: calc(var(--touch) + 0.8rem); }
    .media-card__controles { inset: auto 0.4rem 0.4rem auto; }
  }
  /* Pied de carte (jauge de difficulte d'un jeu de societe) : en colonne a
     droite du corps, il coupait les titres a 200px et passait sous le bouton
     des mots-cles. Il va a la ligne, sous la jaquette et le corps, et reserve
     la place des boutons comme les lignes du corps. */
  .card-grid > .media-card,
  .card-grid > .media-card-shell > .media-card { flex-wrap: wrap; }
  .card-grid > .media-card > .media-card__body,
  .card-grid > .media-card-shell > .media-card > .media-card__body { flex: 1 1 0; min-width: 0; }
  .card-grid > .media-card > .media-card__actions,
  .card-grid > .media-card-shell > .media-card > .media-card__actions {
    flex: 1 1 100%;
    padding-right: calc(0.85rem + var(--reserve, 0px));
  }
  /* Pas de soulevement au survol sur une liste dense : le doigt n en profite
     pas et le decalage gene la lecture. */
  .card-grid > .media-card:hover,
  .card-grid > .media-card-shell > .media-card:hover { transform: none; }

  /* Toute la hauteur de la ligne, a largeur fixe, recadree : centree a son
     format, la jaquette laissait un vide dessus et dessous des que les
     pastilles passaient sous le texte (decision de l'utilisateur). 4rem et
     non 3.5 : une ligne bavarde (jeu de societe, 130px) la recadrait sinon
     en meurtriere. min-height garde le format 2/3 a une ligne courte. Une
     image detouree (amiibo, --contain) est inscrite et non recadree. */
  .card-grid > .media-card > .media-card__cover,
  .card-grid > .media-card-shell > .media-card > .media-card__cover {
    flex: 0 0 4rem;
    align-self: stretch;
    aspect-ratio: auto;
    min-height: 6rem;
  }
  .card-grid > .media-card .media-card__placeholder,
  .card-grid > .media-card-shell > .media-card .media-card__placeholder { font-size: 1.3rem; }

  /* Une colonne : le titre a toute la largeur, les pastilles passent sous
     les textes. En colonne de droite, elles mangeaient la ligne du titre. */
  .card-grid > .media-card > .media-card__body,
  .card-grid > .media-card-shell > .media-card > .media-card__body {
    grid-template-columns: minmax(0, 1fr);
    align-content: center;
    column-gap: 0.6rem;
    row-gap: 0.1rem;
    padding: 0.45rem 0.7rem;
  }
  /* Apres le remplissage general, qu'elle complete : la bande « ⋯ » du bord
     droit occupe toute la hauteur, le corps entier lui laisse sa place. */
  .card-grid > .media-card-shell > .media-card > .media-card__body { padding-right: calc(0.7rem + var(--reserve)); }
  /* Une ligne chacun, coupee net : la hauteur de la rangee doit rester
     previsible quelle que soit la longueur du titre. */
  .card-grid > .media-card .media-card__title,
  .card-grid > .media-card .media-card__subtitle,
  .card-grid > .media-card .media-card__meta,
  .card-grid > .media-card-shell > .media-card .media-card__title,
  .card-grid > .media-card-shell > .media-card .media-card__subtitle,
  .card-grid > .media-card-shell > .media-card .media-card__meta {
    grid-column: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .card-grid > .media-card .media-card__title,
  .card-grid > .media-card-shell > .media-card .media-card__title { font-size: 0.92rem; }
  .card-grid > .media-card .media-card__subtitle,
  .card-grid > .media-card .media-card__meta,
  .card-grid > .media-card-shell > .media-card .media-card__subtitle,
  .card-grid > .media-card-shell > .media-card .media-card__meta { font-size: 0.76rem; }

  .card-grid > .media-card .media-card__badges,
  .card-grid > .media-card-shell > .media-card .media-card__badges { margin-top: 0.15rem; }

  /* Une ligne, comme le sous-titre : la rangée garde sa hauteur. */
  .card-grid > .media-card .media-card__icones,
  .card-grid > .media-card-shell > .media-card .media-card__icones {
    grid-column: 1;
    flex-wrap: nowrap;
    overflow: hidden;
    font-size: 0.76rem;
  }
}

/* ---------- Formulaire dense : deux colonnes et panneaux repliables ---------- */

/* Un formulaire long empile ses panneaux sur toute la largeur et descend sur
   deux ecrans et demi. On le compose en rangees explicites, chacune donnant a
   ses panneaux la largeur que leur contenu merite : une colonne etroite pour la
   jaquette a cote d une large pour l identite, puis des tiers pour ce qui
   s equivaut. Rangees plutot que colonnes libres, pour que l ordre de lecture
   reste celui du document et que chaque panneau garde une largeur choisie. */
.form-rows { display: grid; gap: 1.4rem; }
.form-row {
  display: grid;
  gap: 1.4rem;
  align-items: start;
}
.form-row > * { min-width: 0; }

/* Premiere rangee : la jaquette, l identite, l appreciation. L identite porte
   onze champs et merite la part du lion ; les deux autres n en ont que deux ou
   trois, empiles en une colonne. */
/* 3.6fr et non 3 : a 765px de panneau, remplissage deduit, la grille dense ne
   tenait que trois champs par rangee et l identite en demandait quatre. */
.form-row--head { grid-template-columns: minmax(0, 1fr) minmax(0, 3.6fr) minmax(0, 1.1fr); }

@media (max-width: 1100px) {
  /* L appreciation passe sous les deux autres plutot que de les etrangler. */
  .form-row--head { grid-template-columns: minmax(0, 1fr) minmax(0, 3fr); }
  .form-row--head > :nth-child(3) { grid-column: 1 / -1; }
}

/* Deux cinquiemes, trois cinquiemes, et les deux colonnes a la meme hauteur :
   le casting s etire sur celle du groupe d en face, et sa liste d acteurs prend
   l espace ainsi gagne au lieu de le laisser vide. */
.form-row--casting {
  grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
  align-items: stretch;
}
.form-stack { display: grid; gap: 1rem; align-content: start; }

/* Panneau dont la liste remplit la hauteur que la rangee lui donne.

   Le contenu d un <details> vit dans un emplacement interne au navigateur : ses
   enfants ne sont pas les elements flexibles du details, et un flex-grow pose
   sur la liste reste sans effet — verifie, elle restait a sa hauteur minimale
   pendant que le panneau en faisait 815. Il faut passer par ::details-content,
   qui designe cet emplacement. La declaration de repli, au-dessus, garde un
   plafond fixe pour les navigateurs qui ne le connaissent pas encore. */
.panel--fill > .scroll-list { max-height: 24rem; }

@supports selector(::details-content) {
  .panel--fill { display: grid; grid-template-rows: auto 1fr; }
  .panel--fill::details-content { display: flex; flex-direction: column; min-height: 0; }
  .panel--fill > .scroll-list { flex: 1 1 0; min-height: 6rem; max-height: none; }
}

@media (max-width: 1000px) {
  .form-row--casting { grid-template-columns: minmax(0, 1fr); }
  /* En une colonne la hauteur commune n a plus d objet : la liste reprend un
     plafond fixe au lieu de s etirer. */
  .panel--fill { display: block; }
  .panel--fill > .scroll-list { max-height: 13rem; flex: none; }
}
@media (max-width: 800px) {
  .form-row--head { grid-template-columns: minmax(0, 1fr); }
}

/* Liste logee dans un fieldset (les joueurs d un jeu video, dont la legende
   donne la consigne) : les regles ci-dessus ne visent que la .scroll-list
   enfant direct du panneau, et celle-ci gardait son plafond de 13rem dans un
   panneau de 1018px — un cinquieme de la hauteur servait. La chaine de flex
   doit donc traverser le fieldset. */
.panel--fill > .fieldset--bare > .scroll-list { max-height: 24rem; }

@supports selector(::details-content) {
  .panel--fill > .fieldset--bare { display: flex; flex-direction: column; flex: 1 1 0; min-height: 0; }
  .panel--fill > .fieldset--bare > .scroll-list { flex: 1 1 0; min-height: 6rem; max-height: none; }
}

/* En une colonne, pas de defilement dans le defilement : les modes sont un
   referentiel court (une dizaine), la liste se deploie en entier. */
@media (max-width: 1000px) {
  .panel--fill > .fieldset--bare { display: block; }
  .panel--fill > .fieldset--bare > .scroll-list { max-height: none; flex: none; }
}

/* Joueurs par mode : minimum et maximum cote a cote. Deux champs de 111px
   plus l ecart depassaient de 2px la place laissee par le nom du mode, et
   passaient l un sous l autre — des lignes de 196px pour deux nombres. */
.data-list--joueurs .data-list__aside { flex-wrap: nowrap; }
.data-list--joueurs .data-list__aside > .field { flex: 0 0 5.5rem; }

/* Sur telephone, le nom du mode passait sur sa propre ligne au-dessus des
   deux champs (140px par mode) : il reste a gauche, quitte a tenir sur deux
   lignes, plutot que d etre coupe — « Cooperation en ligne » tronque ne se
   distingue plus de « Cooperation locale ». */
@media (max-width: 680px) {
  .data-list--joueurs .data-list__row { flex-wrap: nowrap; }
  .data-list--joueurs .data-list__main { flex: 1 1 0; min-width: 0; }
  .data-list--joueurs .data-list__title { white-space: normal; overflow: visible; }
  .data-list--joueurs .data-list__aside > .field { flex-basis: 4.75rem; }
}
/* Sous 400px les deux champs ne laissent plus au nom que quelques dizaines
   de pixels, et un mot long (« environnement ») deborderait sur eux : la
   ligne se replie de nouveau, le nom au-dessus. */
@media (max-width: 400px) {
  .data-list--joueurs .data-list__row { flex-wrap: wrap; }
  .data-list--joueurs .data-list__main { flex-basis: 100%; }
}

/* Grille de champs resserree, pour les panneaux places en colonne ou tout
   simplement denses. La grille par defaut reserve 15rem par champ, ce qui ne
   tient plus quand le panneau ne fait que la moitie de la largeur. */
.field-grid--dense { grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); }

/* Grille de champs tenue sur une seule colonne, quelle que soit la place
   disponible. La grille par defaut remplit la largeur, ce qui est juste pour des
   champs sans ordre entre eux mais faux pour une suite numerotee : les titres
   d episodes 01 a 08 se lisaient en deux colonnes, l oeil descendant la premiere
   avant de remonter, alors qu une saison est une liste qui se lit d un bout a
   l autre. */
.field-grid--single { grid-template-columns: minmax(0, 1fr); }

/* Zone de texte courte. Les elements Laminas portent un attribut rows qui fixe
   la hauteur a une dizaine de lignes : min-height ne peut rien contre lui, il
   faut imposer height. La poignee de redimensionnement reste, qui veut ecrire
   long agrandit. */
.field--short textarea { height: 5.5rem; min-height: 0; }
/* Variante haute : le synopsis est le seul texte long du formulaire, et il a
   desormais une colonne pour lui. */
.field--tall textarea { height: 9.5rem; min-height: 0; }

/* Liste qui defile dans un panneau. Sans plafond, le casting d un film pousse
   la barre de validation hors de l ecran des la dixieme ligne. */
.scroll-list {
  max-height: 13rem;
  overflow-y: auto;
  scrollbar-width: thin;
  padding-right: 0.2rem;
}

/* Panneau repliable : meme apparence qu un .panel, avec un en-tete cliquable.
   C est le tiroir du site, deja utilise par l aide ; les formulaires longs s en
   servent pour que chacun ferme ce dont il ne se sert pas, et public/js/app/ui.js
   referme d office ceux marques data-collapse-small sur telephone. */
details.panel > summary > h2 { margin: 0; }
details.panel > summary .meta { margin: 0 auto 0 0; }
details.panel[open] > summary { margin-bottom: 1.3rem; }

/* Un formulaire dense paie cher le confort d un panneau isole : six panneaux a
   1.8rem de remplissage vertical, c est 345px de marge interieure, et l ecart
   sous chaque en-tete en ajoute 126. On resserre, dans les formulaires
   seulement — une page de lecture garde son air. */
.form-rows .panel { padding: 1.1rem 1.25rem; }
.form-rows details.panel[open] > summary { margin-bottom: 0.9rem; }
.form-rows { gap: 1rem; }
.form-row { gap: 1rem; }

/* Apercu de jaquette et champ d envoi. Empiles mais avec un apercu bride a
   8rem : le panneau fait la meme hauteur que cote a cote, et le champ de
   fichier — un controle natif qui ne se laisse pas comprimer — recupere toute
   la largeur de la colonne au lieu d une centaine de pixels ou le nom du
   fichier choisi etait coupe. */
.cover-field {
  display: grid;
  gap: 0.75rem;
  justify-items: start;
}
.cover-field > * { min-width: 0; max-width: 100%; }
/* 6.5rem suffit a reconnaitre la jaquette attachee avant de la remplacer, et
   c est ce que coute la hauteur du panneau dans une colonne etroite. */
.cover-field > .cover-preview { max-width: 4.5rem; }
.cover-field > div:not(.cover-preview) { width: 100%; }

/* ---------- Rangee de tete : trois panneaux de meme hauteur ---------- */

/* Les trois panneaux s alignent sur le plus haut. L identite fixe la mesure ;
   la jaquette et l appreciation, plus courtes, remplissent au lieu de laisser
   un creux sous elles. */
.form-row--head { align-items: stretch; }

/* Un peu d air avant le bloc casting : la rangee de tete decrit le film, celle
   d en dessous le detaille. */
.form-row--casting { margin-top: 0.6rem; }

/* Champ qui vaut deux colonnes de la grille. Un titre de film et son titre
   original ont besoin de place ; une annee ou une date n en demandent pas. */
.field--span2 { grid-column: span 2; }

@media (max-width: 800px) {
  .field--span2 { grid-column: auto; }
}

/* Panneau de jaquette : l apercu prend toute la hauteur laissee libre et se
   centre. Meme mecanique que pour la liste de casting — le contenu d un
   <details> vit dans un emplacement interne, il faut le viser explicitement. */
@supports selector(::details-content) {
  .panel--cover { display: grid; grid-template-rows: auto 1fr; }
  .panel--cover::details-content { display: flex; flex-direction: column; min-height: 0; }
  .panel--cover .cover-field {
    flex: 1 1 auto;
    min-height: 0;
    grid-template-rows: minmax(0, 1fr) auto;
    justify-items: center;
  }
  .panel--cover .cover-preview {
    max-width: 100%;
    min-height: 0;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 0;
  }
  /* Plafond en rem : sans lui l apercu prend la hauteur naturelle du fichier
     et c est lui qui fixe la rangee, donc une hauteur differente a chaque film.
     Borne, il remplit ce qu on lui laisse et la rangee reste previsible. */
  .panel--cover .cover-preview img {
    height: 100%;
    width: auto;
    max-width: 100%;
    max-height: 14rem;
    object-fit: contain;
    border-radius: var(--radius-sm);
  }
  .panel--cover .cover-field > div:not(.cover-preview) { justify-self: stretch; }
}

/* En une colonne il n y a plus de hauteur commune a remplir : la hauteur du
   panneau redevient indefinie, height: 100% se resout a zero et l apercu
   disparait — pire, son chargement differe ne se declenche jamais puisque la
   boite ne fait aucun pixel. On revient donc a un apercu simple, borne en
   largeur. Cette regle suit le bloc @supports, elle le neutralise a egalite
   de specificite. */
@media (max-width: 800px) {
  .panel--cover { display: block; }
  .panel--cover .cover-field { grid-template-rows: auto auto; justify-items: start; }
  .panel--cover .cover-preview { height: auto; display: block; }
  .panel--cover .cover-preview img {
    height: auto;
    width: 100%;
    max-width: 10rem;
    max-height: none;
  }
}

/* ---------- Mosaique de jaquettes ---------- */

/* Illustre un lot — une saga, un coffret — par les medias qu il contient. La
   disposition dit combien : une jaquette remplit le cadre, deux le partagent en
   hauteur, trois donnent une grande a gauche et deux petites a droite, quatre
   ou plus donnent un carre. Le conteneur remplit ce qu on lui donne, c est
   .media-card__cover qui porte le format 2/3. */
.cover-mosaic {
  display: grid;
  width: 100%;
  height: 100%;
  gap: 2px;
  background: rgba(5, 7, 24, 0.6);
}
.cover-mosaic > img,
.cover-mosaic > .cover-mosaic__placeholder {
  min-width: 0;
  min-height: 0;
  width: 100%;
  height: 100%;
  display: block;
}
.cover-mosaic > img { object-fit: cover; }
.cover-mosaic__placeholder {
  display: grid;
  place-items: center;
  background:
    radial-gradient(circle at 50% 35%, rgba(255, 11, 88, 0.18), transparent 60%),
    linear-gradient(160deg, rgba(43, 53, 113, 0.55), rgba(8, 10, 30, 0.75));
  /* 0.5 et pas moins : sous 0.46 l initiale tombe sous 4.5:1 sur le fond
     sombre de la vignette, mesure faite sur la page rendue. */
  color: rgba(255, 255, 255, 0.5);
  font-size: 1.4rem;
  font-weight: 900;
  line-height: 1;
}

.cover-mosaic--1 { grid-template-columns: 1fr; }
.cover-mosaic--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.cover-mosaic--3,
.cover-mosaic--4 {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: repeat(2, minmax(0, 1fr));
}
/* A trois, la premiere jaquette tient toute la colonne de gauche. */
.cover-mosaic--3 > :first-child { grid-row: span 2; }

/* En ligne de liste, la vignette fait 3.5rem : les tuiles y tombent a une
   quinzaine de pixels. L ecart disparait et l initiale de repli retrecit, sinon
   elle deborde de sa tuile. */
@media (max-width: 680px) {
  .cover-mosaic { gap: 1px; }
  .cover-mosaic__placeholder { font-size: 0.7rem; }
}

/* ---------- Panneau latéral de suivi ----------
   Les actions à gauche, un bloc qui les suit à droite — l'import Steam : deux
   façons d'importer, une seule progression. Le bloc de droite reste collé en
   haut de l'écran pendant le défilement, pour que la progression ne sorte
   jamais de la vue. Sous 900 px, tout s'empile. */
.side-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 24rem);
  gap: var(--space-4);
  align-items: start;
}
.side-layout__main { display: grid; gap: var(--space-4); min-width: 0; }
.side-layout__aside { position: sticky; top: var(--space-3); min-width: 0; }

/* Une zone neutralisée pendant un traitement : le script pose aussi `inert`,
   qui seul retire clics et focus — l'opacité ne fait que le montrer. */
.is-occupe { opacity: 0.45; filter: grayscale(0.6); transition: opacity 0.2s ease, filter 0.2s ease; }

.steam-progression { width: 100%; }

@media (max-width: 900px) {
  .side-layout { grid-template-columns: minmax(0, 1fr); }
  .side-layout__aside { position: static; }
}
@media (prefers-reduced-motion: reduce) {
  .is-occupe { transition: none; }
}

/* ---------- Page de résultats de la recherche globale ---------- */

/* Une section par type ; les œuvres reprennent .card-grid, le reste est une
   liste de liens à deux lignes (nom, contexte). */
.search-section { display: grid; gap: 0.9rem; }
.search-section__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.6rem;
}
.search-section__head h2 { display: flex; align-items: center; gap: 0.6rem; margin: 0; font-size: 1.2rem; }
.search-section__count {
  padding: 0.1rem 0.55rem;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.08);
  color: var(--muted);
  font-size: 0.78rem;
  font-weight: 700;
}
.search-links {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.search-links a {
  display: flex;
  flex-direction: column;
  min-height: var(--touch);
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: rgba(8, 10, 30, 0.38);
  color: #fff;
  text-decoration: none;
}
.search-links a:hover,
.search-links a:focus-visible { border-color: var(--accent); background: rgba(255, 11, 88, 0.1); }
.search-links__name { font-weight: 700; }
.search-links__meta { color: var(--muted); font-size: 0.8rem; }

/* ---------- Fiche d'un amiibo : jeux compatibles ---------- */

/* Un jeu par ligne, ses usages en liste dessous ; la pastille « Dans ma
   collection » est un lien vers la fiche du jeu du compte. */
.amiibo-jeux { list-style: none; margin: 0 0 1.2rem; padding: 0; display: grid; gap: 0.8rem; }
.amiibo-jeux > li > p { margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; }
.amiibo-jeux .meta { margin: 0.3rem 0 0; padding-left: 1.1rem; display: grid; gap: 0.25rem; }

/* ---------- Fiche d'un jeu : amiibo compatibles, groupes par usage ---------- */

.amiibo-usage + .amiibo-usage { margin-top: 0.8rem; }
.amiibo-usage > summary {
  cursor: pointer;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  font-weight: 700;
}
.amiibo-usage > summary .meta { flex-basis: 100%; font-weight: 400; margin: 0; }
/* display: flex retire le triangle natif : un chevron le remplace, seul
   signe visible qu'un groupe se deplie. */
.amiibo-usage > summary::before { content: "\25B8"; color: var(--accent); transition: transform 0.15s; }
.amiibo-usage[open] > summary::before { transform: rotate(90deg); }
.amiibo-usage > summary::-webkit-details-marker { display: none; }
.amiibo-puces { list-style: none; margin: 0.6rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.35rem 1rem; }
.amiibo-puces > li { display: inline-flex; align-items: center; gap: 0.35rem; }

/* ---------- Chronomètre des parties de jeux de société ---------- */

/* Écran pensé pour un téléphone posé sur la table : le temps de l'étape en
   très grand, le bouton « Au tour de … » sur toute la largeur, et des
   cibles tactiles d'au moins var(--touch). Les chiffres sont tabulaires,
   sans quoi le texte tremble à chaque seconde. */
.chrono { display: grid; gap: 1.2rem; }
.chrono__ecran {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
}
.chrono__etape, .chrono__total { display: grid; gap: 0.2rem; }
.chrono__total { text-align: right; }
.chrono__libelle { color: var(--muted); font-size: 1rem; font-weight: 700; }
.chrono__temps {
  color: var(--text);
  font-size: clamp(3rem, 14vw, 5.5rem);
  font-weight: 800;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.chrono__temps--petit { font-size: clamp(1.6rem, 6vw, 2.2rem); }
.chrono--pause .chrono__temps { opacity: 0.55; }
.chrono__principal { display: grid; gap: 0.6rem; }
.chrono__suivant { width: 100%; min-height: calc(var(--touch) * 2); font-size: 1.05rem; }
.chrono__phases, .chrono__commandes {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 8.5rem), 1fr));
  gap: 0.6rem;
}
.chrono__commandes { padding-top: 1rem; border-top: 1px solid var(--line); }
.chrono[aria-busy="true"] [data-geste] { cursor: progress; opacity: 0.7; }
.chrono__colonne-duree { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.chrono__colonne-duree .field { justify-items: end; }
.chrono__colonne-duree input { max-width: 7rem; text-align: right; }
.chrono__statut:empty { display: none; }

/* Accueil : parties à reprendre, une ligne cliquable chacune. */
.chrono-reprises { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.6rem; }
.chrono-reprises > li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
  padding: 0.8rem 1rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
}
.chrono-reprises__lien { display: grid; gap: 0.2rem; min-width: 0; color: var(--text); }
.chrono-reprises__lien::after { content: ""; position: absolute; inset: 0; }
.chrono-reprises > li { position: relative; }

/* Fiche d'un jeu : moyennes par nombre de joueurs. */
.chrono-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.chrono-moyennes td, .chrono-moyennes th[scope="row"] { white-space: nowrap; font-variant-numeric: tabular-nums; }
.chrono-moyennes .meta { margin: 0; font-size: 0.75rem; }

/* Série d'un univers (game/serie, fiche d'un univers) : branches, puis leurs
   jeux, puis sous chacun ses versions en lignes liées par un filet. */
.serie-ajout { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-2); margin-bottom: var(--space-4); }
.serie-ajout .field { flex: 1 1 16rem; min-width: 0; margin: 0; }
.serie-ajout--jeu { display: grid; gap: var(--space-2); }
/* L'erreur passe sous le champ et le bouton, sur toute la ligne ; vide, elle
   ne prend aucune place, sans quoi le bouton aligné en bas se décale. */
.serie-ajout__erreur { flex-basis: 100%; margin: 0; }
.serie-ajout__erreur:empty { display: none; }
/* Ligne de jeu d'une série ou d'un groupe de versions (game/partial/
   version-ligne) : les machines de sortie sous le nom, et la colonne des
   machines possédées, dernière du DOM comme de l'écran (après les boutons) ;
   dans l'arbre d'une série, sa largeur fixe l'aligne d'une ligne à l'autre. */
.version-ligne__sub { display: flex; flex-wrap: wrap; align-items: center; gap: 0.2rem 0.6rem; white-space: normal; }
/* « Dans … » / « Contient … » : une liste de liens ne se coupe jamais par
   « … », même sur téléphone — un lien au-delà de la coupure resterait
   focalisable sans être visible. Double classe pour l'emporter sur la règle
   mobile de .data-list__sub. */
.data-list__sub.version-ligne__compilations { white-space: normal; overflow: visible; }
.version-ligne__sortie { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 0.3rem 0.45rem; font-style: normal; color: var(--muted); }
.version-ligne__possede {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 0.3rem 0.5rem;
}
/* Largeur fixe dans l'arbre d'une série seulement, où les lignes se lisent
   en colonne ; dans la colonne étroite d'une fiche, elle écraserait le titre. */
.serie-tetes .version-ligne__possede { flex-basis: 10rem; }
@media (max-width: 680px) {
  .version-ligne__sub { overflow: visible; }
  /* Le titre passe à la ligne au lieu d'être coupé : un nom de version
     (« … - Nintendo Switch 2 Edition ») se lit en entier. */
  .version-ligne .data-list__title { white-space: normal; overflow: visible; }
  /* Sur téléphone le titre a toute la largeur : les machines possédées
     passent en seconde ligne (pleine ligne en flex, colonne du texte dans la
     grille d'une ligne à vignette). */
  .version-ligne__possede, .serie-tetes .version-ligne__possede { flex: 1 1 100%; justify-content: flex-start; grid-column: 2; }
}
/* Fiche d'un jeu : l'univers et son bouton « Changer » sur une ligne. */
.univers-ligne { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.serie-branche + .serie-branche { margin-top: var(--space-4); }
.serie-branche__tete { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2); margin-bottom: var(--space-2); }
.serie-branche__tete > h3 { margin: 0; }
.serie-branche__actions { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.serie-tete { display: grid; gap: 0.35rem; }
.serie-versions { margin-left: 1.25rem; padding-left: 0.9rem; border-left: 2px solid var(--line); gap: 0.35rem; }
.serie-versions .data-list__row { padding-top: 0.45rem; padding-bottom: 0.45rem; }
/* Boutons et liste « Branche » / « Type » d'une ligne : la liste passe à la
   ligne plutôt que de s'écraser à quelques caractères à côté des boutons. */
.serie-tetes .data-list__actions, [data-versions] .data-list__actions { flex-wrap: wrap; }
@media (max-width: 680px) {
  .serie-versions { margin-left: 0.5rem; padding-left: 0.6rem; }
}
