/*
 * kiosque-web : styles propres au-dessus de Bulma 1.0.4.
 * Thème sombre unique (`<html data-theme="dark">`). Toute couleur passe par les jetons
 * de `:root` ci-dessous, jamais en dur dans les règles.
 */

/* `box-sizing: border-box` : Bulma le pose (`html { box-sizing: border-box }` puis hérité
   partout), mais `lecteur.html`, plus léger, ne charge pas Bulma ; sans ce reset, tout
   élément combinant `padding`/`border` et une largeur ou une hauteur (`.button`, par
   exemple) se dimensionne en `content-box` par défaut du navigateur, plus grand que prévu
   (rendu différent des pages qui chargent Bulma pour le même balisage). Posé ici, pas
   seulement sur `body.app`, pour ne dépendre d'aucune page en particulier. */
*, *::before, *::after { box-sizing: border-box; }

:root, :root[data-theme="dark"] {
  /* Jetons du site */
  --fond: #0d0c11;
  --fond-releve: #16151c;
  --fond-carte: #1b1a22;
  --fond-carte-survol: #232230;
  --texte: #d9d6e0;
  --texte-fort: #ece9f1;
  --texte-doux: #9d98a8;
  --texte-discret: #6f6a7b;
  --filet: #2a2833;
  --filet-fort: #3a3845;
  --accent: #c9a96e;
  --accent-doux: rgba(201, 169, 110, .14);
  --plaque-logo: #e9e6ee;
  --ombre-couverture: 0 10px 30px -12px rgba(0, 0, 0, .8), 0 2px 6px rgba(0, 0, 0, .5);
  --ombre-lecture: 0 0 50px rgba(233, 230, 238, .14), 0 0 14px rgba(233, 230, 238, .1);
  --infobulle-fond: #ece9f1;
  --infobulle-texte: #16151c;
  --rayon: 12px;
  --police: "Manrope", system-ui, -apple-system, "Segoe UI", sans-serif;
  --police-titres: "Fraunces", Georgia, "Times New Roman", serif;

  /* Variables Bulma alignées sur les jetons */
  --bulma-family-primary: var(--police);
  --bulma-body-family: var(--police);
  --bulma-body-background-color: var(--fond);
  --bulma-scheme-main: var(--fond);
  --bulma-body-color: var(--texte);
  --bulma-text: var(--texte);
  --bulma-text-strong: var(--texte-fort);
  --bulma-text-weak: var(--texte-doux);
  --bulma-border: var(--filet-fort);
  --bulma-link-text: var(--accent);
  --bulma-dropdown-content-background-color: var(--fond-carte);
  --bulma-dropdown-item-color: var(--texte);
  --bulma-dropdown-item-hover-background-color: var(--fond-carte-survol);
  --bulma-dropdown-item-hover-color: var(--texte-fort);
  --bulma-dropdown-divider-background-color: var(--filet);
  --bulma-breadcrumb-item-color: var(--texte-doux);
  --bulma-breadcrumb-item-hover-color: var(--texte-fort);
  --bulma-breadcrumb-item-active-color: var(--texte);
  --bulma-breadcrumb-item-separator-color: var(--texte-discret);
  color-scheme: dark;
}

/* Jetons de mise en page, ajustés par taille d'écran en fin de fichier. */
body {
  --gouttiere: clamp(16px, 2.5vw, 48px);
  --respiration: clamp(1rem, 2.5vh, 2rem);
  --hauteur-barre: 52px;
  --largeur-panneau: 260px;
  --couverture-min: 132px;
  --univers-min: 150px;
  --espace-grille: 1rem;
}

/* Le document ne défile jamais nulle part (voir « Coque d'application » plus bas) : contrainte
   posée sur `html` lui-même, pas seulement sur `body.app`, sinon un débordement d'un pixel
   (barre d'adresse mobile, zone de sécurité, arrondi de `dvh`) laisse passer une barre de
   défilement du navigateur autour de toute la page. */
html { background: var(--fond); height: 100%; overflow: hidden; overscroll-behavior: none; }
body {
  /* `margin: 0` : Bulma le fait sur les pages qui la chargent, mais `lecteur.html` ne la
     charge pas (page légère, sans besoin de ses composants) ; sans ce reset, la marge par
     défaut du navigateur (8px) décale toute la liseuse, ce qui laissait par exemple
     dépasser un fin bandeau de la pellicule fermée derrière la barre d'outils. */
  margin: 0; background: var(--fond); color: var(--texte); font-family: var(--police);
  height: 100%; overflow: hidden; overscroll-behavior: none; -webkit-font-smoothing: antialiased;
}
a { color: var(--accent); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

.visuellement-cache {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* --- Boutons ------------------------------------------------------------------------
 * Autonome, sans dépendre de Bulma pour la mise en page (`display`, `padding`…) : les
 * pages qui la chargent (édition, univers, accueil) n'en profitaient que par ses couleurs
 * ici, tout le reste venant du `.button` de Bulma ; `lecteur.html`, plus léger, ne la
 * charge pas, et le même balisage (`<a class="button is-small">`, cadre de couverture, voir
 * `page.md`) rendait alors comme un simple lien souligné, sans forme de bouton du tout.
 * Dimensions calquées sur celles de Bulma (`.is-small` : `.75rem`) pour un rendu identique
 * partout, avec ou sans elle.
 */
.button {
  display: inline-flex; align-items: center; justify-content: center; gap: .35em;
  padding: calc(.5em - 1px) 1em; border: 1px solid var(--filet-fort); border-radius: 999px;
  min-height: 2.5em; background: var(--fond-carte); color: var(--texte);
  font: 600 1rem/1.5 var(--police); text-decoration: none; text-align: center;
  cursor: pointer; white-space: nowrap; vertical-align: top;
}
.button.is-small { font-size: .75rem; padding: calc(.5em - 1px) .85em; }
.button:hover, .button:focus-visible { background: var(--fond-carte-survol); color: var(--texte-fort); border-color: var(--texte-discret); }
.button.is-primary { background: var(--accent); color: var(--fond); border-color: var(--accent); }
.button.is-primary:hover { background: var(--texte-fort); color: var(--fond); }
.icon { display: inline-flex; align-items: center; justify-content: center; }

/* `.dropdown*` : autonome comme `.button` ci-dessus, même raison (`lecteur.html` ne charge
   pas Bulma, voir la note en tête de fichier). Valeurs calquées sur le rendu réel de Bulma
   1.0.4 pour le menu du compte (mesuré aux devtools) : mêmes dimensions/couleurs partout,
   avec ou sans elle. */
.dropdown { position: relative; display: inline-flex; }
.dropdown-trigger { display: block; }
.dropdown-menu {
  display: none; position: absolute; top: 100%; left: 0; z-index: 20; min-width: 12rem; padding-top: 4px;
}
.dropdown.is-active .dropdown-menu { display: block; }
.dropdown-content { padding: 8px 0; border-radius: 6px; background: var(--fond); }
.dropdown-item {
  display: block; width: 100%; padding: 6px 16px; border: 0; background: none;
  color: inherit; font: inherit; text-align: left; text-decoration: none; white-space: nowrap; cursor: pointer;
}
.dropdown-item:hover, .dropdown-item:focus-visible { background: var(--fond-carte-survol); color: var(--texte-fort); }
.dropdown-divider { display: block; height: 1px; margin: 8px 0; border: 0; background: var(--filet); }

/* --- Info-bulles : attribut data-tooltip, sous l'élément ------------------------------ */
[data-tooltip] { position: relative; }
[data-tooltip]:hover::after, [data-tooltip]:focus-visible::after {
  content: attr(data-tooltip); position: absolute; top: calc(100% + 6px); left: 50%; transform: translateX(-50%);
  z-index: 60; padding: .3rem .55rem; border-radius: 6px; white-space: nowrap; pointer-events: none;
  background: var(--infobulle-fond); color: var(--infobulle-texte);
  font-size: .75rem; font-weight: 600; line-height: 1.3;
}
[data-tooltip].bulle-gauche:hover::after, [data-tooltip].bulle-gauche:focus-visible::after { left: auto; right: 0; transform: none; }
[data-tooltip].bulle-droite:hover::after, [data-tooltip].bulle-droite:focus-visible::after { left: 0; transform: none; }
[data-tooltip].bulle-haut:hover::after, [data-tooltip].bulle-haut:focus-visible::after { top: auto; bottom: calc(100% + 6px); }
@media (hover: none) { [data-tooltip]:hover::after { display: none; } }

/* --- Coque d'application ----------------------------------------------------------------------
 * `<body class="app">` : la page tient exactement dans la vue (barre en haut, vue dessous),
 * sans défilement du document ; chaque vue fait défiler ses propres zones (`.vue-defilante`).
 * Les pages de réponse du serveur n'ont pas cette classe et défilent normalement.
 */
body.app {
  height: 100vh; height: 100dvh; min-height: 0; overflow: hidden;
  display: flex; flex-direction: column;
}
body.app > .barre { flex: none; }
body.app > .vue { flex: 1; min-height: 0; min-width: 0; }

.vue-defilante {
  overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: var(--filet-fort) transparent;
}

/* Contenu d'une vue : pleine largeur, gouttières fluides, marges des encoches. */
.cadre {
  width: 100%; margin: 0 auto;
  padding: var(--respiration) max(var(--gouttiere), env(safe-area-inset-right)) calc(var(--respiration) + env(safe-area-inset-bottom)) max(var(--gouttiere), env(safe-area-inset-left));
}

/* --- Barre du haut ------------------------------------------------------------------------ */
.barre {
  position: relative; z-index: 30; background: var(--fond);
  border-bottom: 1px solid var(--filet); padding-top: env(safe-area-inset-top);
}
.barre-contenu {
  padding: 0 max(var(--gouttiere), env(safe-area-inset-right)) 0 max(var(--gouttiere), env(safe-area-inset-left));
  min-height: var(--hauteur-barre); display: flex; align-items: center; justify-content: space-between; gap: 1rem; min-width: 0;
}
.barre-logo { display: inline-flex; align-items: center; min-height: 40px; flex: none; }
.barre-logo img { height: 22px; width: auto; display: block; }
.barre-logo:hover img { opacity: .8; }
.barre-compte { display: flex; align-items: center; justify-content: flex-end; min-width: 0; }
.barre-compte .dropdown, .barre-compte .dropdown-trigger { min-width: 0; max-width: 100%; }
.bouton-compte, .bouton-connexion { background: transparent; border-color: transparent; color: var(--texte); max-width: 100%; }
.bouton-connexion { border-color: var(--filet-fort); }
.nom-compte { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.bouton-compte { display: inline-flex; min-width: 0; }
.email-compte { color: var(--texte-doux); font-size: .8rem; overflow-wrap: anywhere; }
.barre-compte .dropdown-menu { min-width: 13rem; left: auto; right: 0; }
.barre-compte .dropdown-content { border: 1px solid var(--filet); box-shadow: 0 12px 30px rgba(0, 0, 0, .5); }
.barre-compte .dropdown-item { min-height: 40px; display: flex; align-items: center; gap: .25rem; }

/* --- Éléments communs ------------------------------------------------------------------- */
.surtitre {
  color: var(--accent); font-size: .72rem; font-weight: 700; letter-spacing: .18em; text-transform: uppercase;
  margin-bottom: .5rem;
}
.titre-page {
  font-family: var(--police-titres); font-weight: 500; color: var(--texte-fort);
  font-size: clamp(1.6rem, 4vw, 3rem); line-height: 1.1; letter-spacing: -.01em;
}
.fil { margin-bottom: 1rem !important; font-size: .85rem; }
.fil a { color: var(--texte-doux); min-height: 40px; }
.fil a:hover { color: var(--texte-fort); }
.fil li.is-active a { color: var(--texte); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 60vw; display: inline-block; line-height: 40px; }
/* Chargement et messages d'état : centrés dans la vue, sous la barre du haut. */
.chargement, .etat {
  position: fixed; inset: var(--hauteur-barre) 0 0; z-index: 10;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .6rem;
  padding: 1rem; text-align: center; color: var(--texte-doux);
}
.chargement { pointer-events: none; }
.chargement p, .etat p { margin: 0; }
.etat .icon { color: var(--texte-discret); }

.loader-kiosque {
  display: inline-block; width: 28px; height: 28px; border-radius: 50%;
  border: 2px solid var(--filet-fort); border-top-color: var(--accent);
  animation: tourner .8s linear infinite;
}
@keyframes tourner { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .loader-kiosque { animation-duration: 2.4s; } }

/* --- Logos ---------------------------------------------------------------------------------- */
.logo { display: block; max-width: 100%; }
svg.logo-mono { color: var(--texte-fort); }
svg.logo-couleurs, img.logo-image { background: var(--plaque-logo); border-radius: 8px; padding: .5rem .75rem; }

/* --- Accueil : univers ------------------------------------------------------------------------ */
.accueil { min-height: 100%; display: flex; flex-direction: column; gap: var(--respiration); }
.bienvenue { flex: none; color: var(--texte-doux); font-size: clamp(.95rem, 1.2vw, 1.1rem); line-height: 1.4; overflow-wrap: anywhere; }
/* Rangées défilantes de l'accueil (univers, magazines) : défilent à l'horizontale (elles seules), sans barre visible ;
   `barre-defilante.js` pose `--largeur-carte` pour que la carte de droite dépasse toujours. Elles vont jusqu'au bord
   droit de l'écran (marge négative = gouttière), le padding droit laissant l'air voulu une fois au bout. */
.barre-defilante {
  position: relative; min-width: 0;
  margin-right: calc(-1 * max(var(--gouttiere), env(safe-area-inset-right)));
}
.rail-defilant {
  display: flex; gap: var(--espace-grille); overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
  padding: 8px max(var(--gouttiere), env(safe-area-inset-right)) 8px 0;
}
.rail-defilant::-webkit-scrollbar { display: none; }
.rail-univers > .carte-univers { flex: 0 0 var(--largeur-carte, 240px); height: clamp(160px, 28dvh, 360px); min-width: 0; }
/* Flèches : mêmes gestes que celles du bandeau des autres numéros, estompées au bout de la rangée. */
.fleche-defilante {
  position: absolute; z-index: 2; top: 50%; transform: translateY(-50%);
  display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; padding: 0;
  border: 1px solid var(--filet-fort); border-radius: 999px; background: var(--fond-carte-survol);
  color: var(--texte); font-size: .95rem; cursor: pointer; box-shadow: 0 4px 14px rgba(0, 0, 0, .4);
}
.fleche-defilante[hidden] { display: none; }
.fleche-gauche { left: -.5rem; }
.fleche-droite { right: max(var(--gouttiere), env(safe-area-inset-right)); }
.fleche-defilante:hover, .fleche-defilante:focus-visible { color: var(--texte-fort); }
.fleche-defilante:disabled { opacity: .25; pointer-events: none; }

/* Rangée des magazines (« Mes magazines » ou « Les derniers numéros »), sous celle des univers : cartes de la
   mosaïque d'un univers (`carte-edition`), chargées par pages au fil du défilement. */
.rangee-magazines[hidden] { display: none; }
.titre-rangee {
  margin: 0 0 .6rem; font-family: var(--police-titres); font-size: clamp(1.15rem, 1.6vw, 1.5rem); font-weight: 500;
  color: var(--texte-fort); line-height: 1.2;
}
.rail-magazines { align-items: flex-start; }
.rail-magazines > .carte-edition { flex: 0 0 var(--largeur-carte, 150px); }
.rail-magazines > .sentinelle { flex: none; width: 1px; height: 1px; }
.etat-rangee { margin-top: .5rem; color: var(--texte-discret); font-size: .8rem; letter-spacing: .04em; }
.etat-rangee[hidden] { display: none; }
.carte-univers { flex: 0 0 var(--largeur-carte, 240px); height: clamp(160px, 28dvh, 360px); min-width: 0; }
/* Flèches : mêmes gestes que celles du bandeau des autres numéros, estompées au bout de la rangée. */
.barre-univers-fleche {
  position: absolute; z-index: 2; top: 50%; transform: translateY(-50%);
  display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; padding: 0;
  border: 1px solid var(--filet-fort); border-radius: 999px; background: var(--fond-carte-survol);
  color: var(--texte); font-size: .95rem; cursor: pointer; box-shadow: 0 4px 14px rgba(0, 0, 0, .4);
}
.barre-univers-fleche[hidden] { display: none; }
.barre-univers-gauche { left: -.5rem; }
.barre-univers-droite { right: max(var(--gouttiere), env(safe-area-inset-right)); }
.barre-univers-fleche:hover, .barre-univers-fleche:focus-visible { color: var(--texte-fort); }
.barre-univers-fleche:disabled { opacity: .25; pointer-events: none; }
.carte-univers {
  display: flex; flex-direction: column; align-items: center; justify-content: space-between; gap: 1rem;
  min-height: 0; padding: clamp(1rem, 2.5vh, 1.75rem) 1.25rem clamp(.9rem, 2vh, 1.5rem); border-radius: var(--rayon);
  background: var(--accent); border: 1px solid var(--accent); color: var(--fond); text-align: center;
  transition: border-color .2s, transform .2s, background .2s, color .2s;
}
/* Fond et bord dorés, textes noirs ; au survol (ou au focus) textes clairs et petit soulèvement. Les textes et les
   logos monochromes suivent la couleur de la carte (`currentColor`). */
.carte-univers:hover, .carte-univers:focus-visible {
  color: var(--texte-fort); transform: translateY(-2px);
}
.carte-univers svg.logo-mono { color: inherit; transition: color .2s; }
.nom-univers {
  font-family: var(--police-titres); font-size: clamp(1.25rem, 1.8vw, 1.7rem); font-weight: 500; line-height: 1.15;
  color: inherit; overflow-wrap: anywhere;
}
.logo-univers { flex: 1; display: flex; align-items: center; justify-content: center; width: 100%; min-height: 0; }
.logo-univers .logo { width: auto; max-width: min(260px, 90%); height: clamp(44px, 9dvh, 88px); object-fit: contain; }
.logo-univers svg.logo { width: 260px; max-width: 90%; }
.initiale {
  width: 64px; height: 64px; border-radius: 50%; display: grid; place-items: center;
  background: rgba(13, 12, 17, .12); color: inherit; font-family: var(--police-titres); font-size: 1.8rem;
}
.compte-univers { font-size: .75rem; color: inherit; opacity: .72; letter-spacing: .06em; text-transform: uppercase; }

/* --- Univers : panneau (retour, logo, onglets) et zone des couvertures ------------------------------
 * Mobile et tablette en portrait : panneau en haut, onglets en barre défilante.
 * Bureau, ou paysage peu haut (téléphone couché) : panneau en colonne à gauche, onglets en liste.
 */
.vue-univers { display: flex; flex-direction: column; }
.panneau { flex: none; display: flex; flex-direction: column; min-width: 0; border-bottom: 1px solid var(--filet); }
.panneau-tete {
  display: flex; align-items: center; gap: .75rem; min-width: 0; min-height: 52px;
  padding: .25rem max(var(--gouttiere), env(safe-area-inset-right)) .25rem max(var(--gouttiere), env(safe-area-inset-left));
}
.retour {
  display: inline-flex; align-items: center; gap: .15rem; min-height: 40px; padding-right: .75rem;
  color: var(--texte-doux); font-size: .85rem; font-weight: 600; white-space: nowrap;
  border-right: 1px solid var(--filet);
}
.retour .icon { width: 1.25rem; margin-left: -.3rem; }
.retour:hover { color: var(--texte-fort); }
.entete-univers { flex: 1; min-width: 0; display: flex; align-items: center; }
.titre-univers { margin: 0; min-width: 0; width: 100%; display: flex; }
.entete-univers .titre-page { font-size: 1.3rem; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.logo-entete { height: 30px; width: min(200px, 100%); }
img.logo-entete { width: auto; max-width: 100%; object-fit: contain; object-position: left center; padding: .2rem .5rem; }

.onglets-supports {
  display: flex; gap: .25rem; min-width: 0; overflow-x: auto; scrollbar-width: none;
  padding: 0 max(var(--gouttiere), env(safe-area-inset-right)) 0 max(var(--gouttiere), env(safe-area-inset-left));
}
.onglets-supports[hidden] { display: none; }
.onglets-supports::-webkit-scrollbar { display: none; }
.onglet {
  flex: none; display: inline-flex; align-items: center; gap: .5rem; min-height: 44px; padding: 0 .75rem;
  background: none; border: 0; border-bottom: 2px solid transparent;
  color: var(--texte-doux); font: 600 .9rem var(--police); cursor: pointer; white-space: nowrap;
}
.onglet:first-child { margin-left: -.75rem; }
.onglet:hover { color: var(--texte-fort); }
.onglet.is-active { color: var(--texte-fort); border-bottom-color: var(--accent); }
.onglet-total {
  flex: none; font-size: .7rem; padding: .05rem .45rem; border-radius: 999px; background: var(--fond-carte); color: var(--texte-doux);
}
.onglet.is-active .onglet-total { background: var(--accent-doux); color: var(--accent); }

.zone-couvertures { flex: 1; min-height: 0; min-width: 0; }

@media (min-width: 1024px), (orientation: landscape) and (min-width: 640px) and (max-height: 540px) {
  .vue-univers { flex-direction: row; }
  .panneau {
    width: var(--largeur-panneau); border-bottom: 0; border-right: 1px solid var(--filet);
    background: var(--fond-releve);
  }
  .panneau-tete {
    flex-direction: column; align-items: stretch; gap: clamp(.25rem, 2vh, 1.25rem);
    padding: clamp(.5rem, 2vh, 1.25rem) 1rem clamp(.75rem, 2.5vh, 1.5rem) max(1rem, env(safe-area-inset-left));
    border-bottom: 1px solid var(--filet);
  }
  .retour { border-right: 0; align-self: flex-start; }
  .entete-univers .titre-page { font-size: 1.5rem; white-space: normal; }
  .logo-entete { height: clamp(32px, 8dvh, 64px); width: 100%; }
  .onglets-supports {
    flex: 1; min-height: 0; flex-direction: column; gap: 2px;
    padding: .75rem .75rem calc(.75rem + env(safe-area-inset-bottom)) max(.75rem, env(safe-area-inset-left));
    overflow-x: hidden; overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--filet-fort) transparent;
  }
  .onglet {
    width: 100%; justify-content: space-between; text-align: left; white-space: normal;
    border-bottom: 0; border-radius: 8px; padding: .4rem .75rem;
  }
  .onglet:first-child { margin-left: 0; }
  .onglet:hover { background: var(--fond-carte); }
  .onglet.is-active { background: var(--accent-doux); box-shadow: inset 3px 0 0 var(--accent); }

  /* Univers à un seul support : pas d'onglets, donc pas de colonne (bandeau en haut). */
  .vue-univers.sans-onglets { flex-direction: column; }
  .sans-onglets .panneau { width: 100%; background: var(--fond); border-right: 0; border-bottom: 1px solid var(--filet); }
  .sans-onglets .panneau-tete {
    flex-direction: row; align-items: center; gap: .75rem; border-bottom: 0;
    padding: .25rem max(var(--gouttiere), env(safe-area-inset-right)) .25rem max(var(--gouttiere), env(safe-area-inset-left));
  }
  .sans-onglets .retour { border-right: 1px solid var(--filet); align-self: auto; }
  .sans-onglets .logo-entete { height: clamp(30px, 5dvh, 44px); width: min(260px, 100%); }
  .sans-onglets .entete-univers .titre-page { font-size: 1.4rem; white-space: nowrap; }
}

.grille-couvertures {
  display: grid; gap: calc(var(--espace-grille) * 1.5) var(--espace-grille);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--couverture-min)), 1fr));
}
.carte-edition { display: flex; flex-direction: column; gap: .6rem; color: var(--texte); min-width: 0; }
.couverture {
  display: block; position: relative; width: 100%; overflow: hidden; border-radius: 4px;
  background: var(--fond-carte); box-shadow: var(--ombre-couverture);
  transition: transform .25s ease, box-shadow .25s ease;
}
.couverture img { display: block; width: 100%; height: 100%; object-fit: cover; }
.couverture-absente { position: absolute; inset: 0; display: grid; place-items: center; color: var(--texte-discret); font-size: 1.5rem; }
.carte-edition:hover .couverture, .carte-edition:focus-visible .couverture { transform: translateY(-4px); }
.legende { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.legende-titre { font-size: .85rem; font-weight: 600; color: var(--texte); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.legende-date { font-size: .75rem; color: var(--texte-discret); }
.carte-edition:hover .legende-titre { color: var(--texte-fort); }
/* Édition en préparation (ZIP pas encore prêt) : bouton habillé comme une carte, mention discrète
   sur la couverture, ligne d'explication au clic. */
button.carte-edition {
  width: 100%; padding: 0; border: 0; background: none; font: inherit; text-align: left; cursor: pointer; appearance: none;
}
.mention-preparation {
  position: absolute; left: .5rem; bottom: .5rem; max-width: calc(100% - 1rem);
  padding: .2rem .55rem; border-radius: 999px; background: var(--fond-releve); color: var(--accent);
  font-size: .7rem; font-weight: 600; letter-spacing: .02em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  box-shadow: var(--ombre-couverture);
}
.carte-edition.en-preparation .couverture img { opacity: .82; }
.note-preparation { font-size: .75rem; line-height: 1.35; color: var(--texte-doux); }

.suite { min-height: 80px; display: flex; align-items: center; justify-content: center; padding-top: 1.5rem; }
.suite:empty { min-height: 0; padding-top: 0; }
.fin-liste { color: var(--texte-discret); font-size: .8rem; letter-spacing: .04em; text-align: center; }

/* --- Édition ------------------------------------------------------------------------------------
 * Portrait : couverture au-dessus du texte, à la mesure de la hauteur de la vue.
 * Paysage : couverture à gauche sur toute la hauteur disponible, texte à droite.
 */
.cadre-edition { min-height: 100%; display: flex; flex-direction: column; }
.fiche-edition {
  flex: 1; display: flex; flex-direction: column; align-items: center; gap: var(--respiration);
}
.fiche-couverture {
  flex: none; width: min(100%, calc(52dvh * .75)); background: var(--fond-carte); border-radius: 4px;
  overflow: hidden; box-shadow: var(--ombre-couverture);
}
.fiche-couverture img { display: block; width: 100%; height: 100%; object-fit: cover; }
.fiche-texte { width: 100%; max-width: 40rem; text-align: center; }
.bouton-admin {
  display: inline-flex; align-items: center; justify-content: center; margin-top: .5rem;
  width: 40px; height: 40px; border-radius: 999px; border: 1px solid var(--filet-fort);
  background: var(--fond-carte); color: var(--texte-doux);
}
.bouton-admin:hover, .bouton-admin:focus-visible { background: var(--fond-carte-survol); color: var(--texte-fort); }
.fiche-actions { margin-top: 1rem; display: flex; flex-direction: column; align-items: center; gap: .6rem; }
.fiche-actions [aria-disabled="true"] { opacity: .5; cursor: default; }
.fiche-actions [aria-disabled="true"]:hover { background: var(--fond-carte); color: var(--texte); }
/* Retour au support de rattachement : même taille que « Retour aux univers » (`.is-small`),
   mais bordure et texte dorés (contour, pas rempli comme le CTA principal). */
.fiche-support { background: transparent; border-color: var(--accent); color: var(--accent); }
.fiche-support:hover, .fiche-support:focus-visible { background: var(--accent-doux); color: var(--accent); border-color: var(--accent); }

/*
 * Mosaïque des pages : grille fixe (JS, `public/js/mosaique.js`), jamais plus haute ni plus
 * large que sa boîte. Chaque planche est positionnée en absolu (`left`/`top`/`width`/`height`
 * posés en ligne) : la boîte ne sert que de repère et de clôture visuelle. Aperçu décoratif,
 * pas la lecture (`lecteur.html`) : chaque image remplit tout son bloc, recadrée au besoin
 * (`object-fit: cover`), jamais étirée.
 */
.fiche-pages {
  --marge-mosaique: 8px;
  order: 3; flex: none; width: 100%; height: 38dvh; min-width: 0;
  position: relative; overflow: hidden;
}
.planche {
  position: absolute; display: flex;
  box-shadow: var(--ombre-couverture); border-radius: 2px; overflow: hidden; background: var(--fond-carte);
}
.planche img { display: block; flex: 1 1 0; min-width: 0; width: 100%; height: 100%; object-fit: cover; }

/* `.fiche-milieu` est transparente pour la disposition (`display: contents`) : ses enfants
   (`.fiche-pages`, `.fiche-texte`) participent directement au flex de `.fiche-edition`, avec
   leurs propres `order` (portrait : couverture, texte, mosaïque). En paysage seulement
   (ci-dessous), elle redevient un vrai conteneur flex (ligne, mosaïque et texte côte à côte) :
   `display: contents` ne le permettrait pas. */
.fiche-milieu { display: contents; }
/* Bandeau « Les autres numéros de [support] » (`edition.js`, ouvert par `#bouton-autres`) : fixé
   en bas de la fenêtre et superposé à la page, jamais dans le flux. Même famille visuelle et
   mêmes interactions que la pellicule de la liseuse (`.lecteur-pellicule-*`) : rail sans barre de
   défilement visible, flèches estompées en bout de course, molette pour défiler. Glisse depuis
   le bas (`translateY(100%)` au repos : sort entièrement de la vue, `inert` en plus) ; pas
   d'`overflow: hidden` sur le bandeau, pour que les info-bulles des flèches restent visibles. */
.autres-numeros {
  position: fixed; z-index: 30; left: 0; right: 0; bottom: 0;
  display: flex; align-items: center; gap: .35rem;
  padding: .6rem max(.5rem, env(safe-area-inset-right)) calc(.7rem + env(safe-area-inset-bottom)) max(.5rem, env(safe-area-inset-left));
  background: linear-gradient(to top, color-mix(in srgb, var(--fond) 96%, transparent), color-mix(in srgb, var(--fond) 88%, transparent));
  border-top: 1px solid var(--filet); box-shadow: 0 -8px 24px rgba(0, 0, 0, .35);
  transform: translateY(100%); transition: transform .28s ease; pointer-events: none;
}
.autres-numeros.ouverte { transform: none; pointer-events: auto; }
.autres-numeros-rail {
  display: flex; gap: .4rem; overflow-x: auto; scroll-behavior: smooth; scrollbar-width: none;
  flex: 1 1 auto; min-width: 0;
}
.autres-numeros-rail::-webkit-scrollbar { display: none; }
.autres-numeros-fleche {
  flex: none; display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border: 0; border-radius: 999px; background: var(--fond-carte-survol);
  color: var(--texte); font-size: .85rem; cursor: pointer;
}
.autres-numeros-fleche:hover, .autres-numeros-fleche:focus-visible { color: var(--texte-fort); }
.autres-numeros-fleche:disabled { opacity: .25; pointer-events: none; }
.autres-numeros-vignette {
  position: relative; flex: none; display: block; height: 96px; border: 2px solid transparent; border-radius: 4px; overflow: hidden;
  background: var(--fond-carte); padding: 0; cursor: pointer;
}
.autres-numeros-vignette img { display: block; width: 100%; height: 100%; object-fit: cover; }
.autres-numeros-vignette.is-active { border-color: var(--accent); pointer-events: none; }
.autres-numeros-vignette:hover, .autres-numeros-vignette:focus-visible { border-color: var(--texte-discret); }
.autres-numeros-numero {
  position: absolute; left: 4px; bottom: 4px; min-width: 18px; height: 18px; padding: 0 4px;
  display: flex; align-items: center; justify-content: center; border-radius: 999px;
  background: color-mix(in srgb, var(--fond) 82%, transparent); color: var(--texte-fort); font: 700 .65rem/1 var(--police);
}
@media (min-width: 1024px) {
  .autres-numeros-vignette { height: 114px; }
}
/* `hidden` seul ne suffit pas : `.button` (Bulma) pose son propre `display`. */
.fiche-support[hidden], .fiche-shopify[hidden], .fiche-actions [aria-disabled][hidden] { display: none; }

@media (orientation: landscape) and (min-width: 640px) {
  .cadre-edition { height: 100%; }
  /* `--marge-planches` : UNE seule marge pour l'espace entre la couverture et la mosaïque et pour
     l'espace entre deux planches de la mosaïque (`--marge-mosaique` de `.fiche-pages`, lue par
     `mosaique.js`). Avant, la couverture s'écartait de la mosaïque d'un gap fluide (jusqu'à 48 px)
     bien plus large que la marge des planches (10 px). */
  .fiche-edition {
    --marge-planches: 10px;
    min-height: 0; flex-direction: row; align-items: stretch; gap: var(--marge-planches);
  }
  /* `max-height` (pas `height` fixe) : avec `align-items: stretch` du parent, une hauteur
     figée à 100 % couplée à `max-width` cassait le ratio de la couverture dès que la
     largeur calculée à partir du ratio dépassait `max-width` (la largeur se retrouvait
     plafonnée, mais la hauteur restait à 100 % sans être recalculée en retour : image
     visiblement déformée, écrasée horizontalement). `max-height` laisse `aspect-ratio`
     (posé en ligne par `edition.js`, sur le vrai ratio de la couverture) dériver la bonne
     hauteur à partir de la largeur une fois celle-ci contrainte, comme `object-fit:
     contain`. La mosaïque (`.fiche-pages`, `flex: 1 1 auto`) absorbe l'espace ainsi
     libéré ou repris : elle est la variable d'ajustement, pas la couverture. */
  .fiche-couverture { width: auto; max-height: 100%; max-width: 46vw; }
  /* `.fiche-milieu` (mosaïque | texte) redevient une vraie ligne flex, directement enfant de
     `.fiche-edition` au même niveau que `.fiche-couverture` (plus de conteneur `.fiche-centre`
     intermédiaire). La mosaïque reste la seule variable d'ajustement
     (`flex: 1 1 auto`, `height: auto` : pas de hauteur fixe, sinon même dépendance circulaire
     que le bug de la couverture réglé plus haut). `order: 0` sur la mosaïque (au lieu de
     l'ordre 3 du portrait, hérité par défaut sinon) : dans ce contexte de ligne, sans
     repositionnement explicite, le texte (ordre par défaut 0 lui aussi, mais après la
     mosaïque dans le DOM) passerait quand même après elle par égalité d'ordre et priorité au
     DOM, mais autant fixer les deux intentions explicitement plutôt que de compter sur un
     ordre implicite. */
  .fiche-milieu { display: flex; flex-direction: row; align-items: stretch; flex: 1 1 auto; min-width: 0; min-height: 0; gap: clamp(1rem, 2.5vw, 3rem); }
  .fiche-milieu .fiche-pages { --marge-mosaique: var(--marge-planches); order: 0; flex: 1 1 auto; width: auto; height: auto; min-width: 0; min-height: 0; }
  /* Fondu vers le texte : la mosaïque ne s'arrête pas net contre la colonne de titre, elle
     se fond dans le fond de page, même couleur que `body`. */
  .fiche-pages::after {
    content: ''; position: absolute; inset: 0 0 0 auto; width: clamp(2rem, 25%, 5rem);
    background: linear-gradient(to right, transparent, var(--fond)); pointer-events: none;
  }
  .fiche-texte { flex: 0 0 clamp(210px, 20vw, 340px); margin-left: 0; align-self: center; text-align: left; }
  /* Le titre global (`clamp(1.6rem, 4vw, 3rem)`) suit la largeur de tout l'écran, pas celle
     de cette colonne, bien plus étroite (20 % de la largeur au plus, 340px max) : réduit
     ici pour rester proportionné à la place réelle disponible. */
  .fiche-texte .titre-page { font-size: clamp(1.3rem, 2.4vw, 2.2rem); }
  .fiche-actions { align-items: flex-start; }
}

/* --- Liseuse (lecteur.html) ---------------------------------------------------------------------------
 * Une page à la fois, plein cadre. Pas de `.barre` du site ici : une barre d'outils discrète à elle,
 * en overlay. Recherche et pellicule sont des panneaux en plus, par-dessus les pages (jamais de
 * réserve d'espace pour eux : la pellicule glisse depuis la barre, voir `.lecteur-pellicule`).
 */
.lecteur { position: relative; height: 100%; min-height: 0; display: flex; flex-direction: column; background: var(--fond); }

.lecteur-barre {
  flex: none; z-index: 30; display: flex; align-items: center; gap: .75rem; min-width: 0;
  min-height: 48px; padding: env(safe-area-inset-top) max(.75rem, env(safe-area-inset-right)) 0 max(.75rem, env(safe-area-inset-left));
  background: linear-gradient(to bottom, rgba(13, 12, 17, .92), rgba(13, 12, 17, .7));
  border-bottom: 1px solid var(--filet);
}
.lecteur-bouton {
  flex: none; display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  width: 40px; height: 40px; padding: 0 .5rem; border: 0; border-radius: 999px; background: transparent;
  color: var(--texte-doux); font: 600 .85rem var(--police); cursor: pointer;
  text-decoration:none;

}
.lecteur-bouton[aria-pressed="true"], .lecteur-bouton[aria-expanded="true"] { background: var(--accent-doux); color: var(--accent); }
.lecteur-bouton:hover, .lecteur-bouton:focus-visible { background: var(--fond-carte-survol); color: var(--texte-fort); }
.lecteur-bouton[aria-pressed="true"]:hover, .lecteur-bouton[aria-expanded="true"]:hover { background: var(--accent-doux); color: var(--accent); }
.lecteur-logo { flex: none; height: 26px; width: auto; max-width: 60px; }
.lecteur-logo.logo-couleurs { padding: 3px 5px; border-radius: 6px; background: var(--plaque-logo); box-sizing: content-box; }
/* Logo SoPress, avant la flèche de retour (comme `.barre-logo` des autres pages, mais posé
   ici : `lecteur.html` a sa propre barre, jamais `installerBarre`, voir plus bas
   `.lecteur-compte`). */
.lecteur-logo-sopress { flex: none; display: inline-flex; align-items: center; }
.lecteur-logo-sopress img { height: 18px; width: auto; display: block; }
.lecteur-logo-sopress:hover img { opacity: .8; }
.lecteur-titre { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; line-height: 1.25; }
.lecteur-titre-texte { color: var(--texte-fort); font-weight: 600; font-size: .9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lecteur-page-num { color: var(--texte-discret); font-size: .75rem; }
.lecteur-actions { flex: none; display: flex; align-items: center; gap: .15rem; }
/* Indicateur de connexion, tout à droite de la barre : même composant que sur les autres
   pages (`installerCompte` dans `commun.js`), qui porte aussi la classe `barre-compte` pour
   hériter directement de ses règles (menu déroulant aligné à droite, etc., plus bas dans ce
   fichier) sans les dupliquer ici. `flex: none` : ne doit jamais rétrécir dans une barre
   déjà chargée en boutons. */
.lecteur-compte { flex: none; margin-left: .35rem; }

/* Petite zone de saisie accrochée au bouton « # » (aller à une page) : même famille visuelle
   que `.lecteur-recherche`, mais ancrée sur son propre bouton plutôt que sous toute la barre. */
.lecteur-aller-page { position: relative; }
.lecteur-aller-page-panneau {
  position: absolute; z-index: 30; top: calc(100% + .5rem); right: 0;
  display: flex; align-items: center; gap: .35rem; padding: .35rem;
  background: var(--fond-releve); border: 1px solid var(--filet); border-radius: var(--rayon); box-shadow: var(--ombre-couverture);
}
.lecteur-aller-page-panneau[hidden] { display: none; }
.lecteur-aller-page-panneau input {
  width: 5.5rem; min-height: 36px; background: var(--fond-carte); border: 1px solid var(--filet-fort); border-radius: 8px;
  color: var(--texte-fort); padding: .3rem .55rem; font: 500 .9rem var(--police);
}
.lecteur-aller-page-panneau input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.lecteur-aller-page-panneau .lecteur-bouton { width: 36px; height: 36px; }

/* Corps de la liseuse, sous la barre d'outils et la pellicule : dans l'ordre du DOM (et
   visuel), le volet des articles (largeur 0 au repos, occupe aucune place), la barre
   verticale, la vue. Un vrai troisième enfant flex, pas un overlay (voir `.lecteur-articles`
   plus bas) : `.lecteur-vue` (déjà `min-width: 0`) se rétrécit réellement à l'ouverture du
   volet, plutôt que de se faire recouvrir. */
.lecteur-corps { position: relative; flex: 1 1 auto; min-height: 0; display: flex; flex-direction: row; }

.lecteur-vue {
  position: relative; flex: 1 1 auto; min-width: 0; min-height: 0; display: flex; align-items: center; justify-content: center;
  padding: .75rem max(.75rem, env(safe-area-inset-right)) .75rem max(.75rem, env(safe-area-inset-left));
  overflow: hidden; touch-action: none;
}

/* Barre verticale des articles : cliquable sur toute sa hauteur (`.lecteur-corps` l'étire
   par défaut, `align-items` non redéfini = `stretch`). Posée entre le volet et la vue (pas à
   l'extrémité gauche du corps) : au repos, le volet a une largeur nulle, donc la barre reste
   visuellement à gauche, comme avant ; à l'ouverture, la largeur croissante du volet
   (`.lecteur-articles.ouverte`, transition CSS) repousse la barre vers la droite au même
   rythme, qui glisse ainsi jusqu'à devenir la bordure droite du volet (son propre
   `border-right` marque alors la limite avec la vue), sans aucune animation JS séparée :
   un seul changement de largeur suffit à faire glisser les trois zones ensemble. Largeur et
   taille d'icône réduites une fois ouverte (`[aria-expanded="true"]`), en transition elles
   aussi. */
.lecteur-barre-verticale {
  flex: none; width: 44px; display: flex; flex-direction: column; align-items: center; padding-top: 1rem;
  border: 0; border-right: 1px solid var(--filet); background: var(--fond-carte); color: var(--texte-doux);
  font-size: 1.1rem; cursor: pointer; transition: width .3s ease, font-size .3s ease;
}
.lecteur-barre-verticale:hover, .lecteur-barre-verticale:focus-visible { background: var(--fond-carte-survol); color: var(--texte-fort); }
.lecteur-barre-verticale[aria-expanded="true"] { width: 32px; font-size: .95rem; background: var(--accent-doux); color: var(--accent); }

/* Volet des articles : vrai enfant flex du corps (`flex: none`, largeur nulle au repos,
   `overflow: hidden` pour n'en rien laisser dépasser), pas un overlay en position absolue :
   voir `.lecteur-corps` plus haut. `box-sizing: border-box` : le padding fait partie de la
   largeur transitionnée, sinon il resterait visible (et cliquable) même à largeur nulle.
   `inert` (posé par `lecteur.js`) tant qu'il n'est pas ouvert : rien dedans n'est focusable ni
   annoncé au lecteur d'écran pendant qu'il est réduit à rien. Image principale contrainte
   dans un carré à bords arrondis, sans recadrage extrême (`object-fit: cover` malgré tout,
   comme la mosaïque, pour remplir le carré proprement) ; liste seulement pour l'instant,
   aucune action au clic (mode article pas encore intégré, voir todo.md). */
.lecteur-articles {
  /* Le padding est sur `.lecteur-articles-liste` (plus bas), pas ici : avec `box-sizing:
     border-box` et un padding non nul directement sur l'élément dont la largeur transitionne
     vers 0, la zone de contenu deviendrait négative, et le navigateur agrandit alors la boîte
     au minimum du padding (elle ne descendait jamais sous `2 * .85rem`, même avec `width: 0`
     forcé en ligne). Sans padding propre, `width: 0` amène vraiment la boîte à rien ; le
     padding de l'enfant, en `content-box` par défaut, peut légitimement déborder de son
     parent à 0 (comportement normal d'un dépassement), aussitôt coupé par `overflow: hidden`
     ci-dessous. */
  box-sizing: border-box; flex: none; width: 0; min-width: 0; min-height: 0; overflow: hidden;
  background: var(--fond-releve); box-shadow: var(--ombre-couverture);
  transition: width .3s ease;
}
.lecteur-articles.ouverte { width: min(340px, 84vw); overflow-y: auto; }
/* Largeur fixe (celle du volet ouvert), jamais celle, animée, de `.lecteur-articles` : sinon
   le texte des cartes se recomposait à chaque frame de la transition (lignes qui se cassent
   différemment à chaque largeur intermédiaire), donnant l'impression que les articles se
   font écraser plutôt que dévoilés. Ici, seule la part visible change (le parent les
   découpe, `overflow: hidden`), jamais leur propre mise en page : les cartes sont déjà
   dimensionnées à l'ouverture comme à la fermeture. */
.lecteur-articles-liste { width: min(340px, 84vw); display: flex; flex-direction: column; gap: 1rem; padding: .85rem; }
/* Carte en ligne (image miniature à gauche, texte à droite) : plus de texte à montrer
   (surtitre, titre, chapeau, page) qu'une simple vignette, une image plus grande n'aurait
   fait que pousser le texte hors de vue sans rien apporter de plus. */
.lecteur-article-carte { display: flex; gap: .6rem; align-items: flex-start; }
/* Sans image (pas rare, l'API n'en donne pas toujours une) : icône Font Awesome centrée sur
   le même fond que le reste du carré, plutôt qu'un carré vide. */
.lecteur-article-image {
  flex: none; width: 56px; aspect-ratio: 1; border-radius: 8px; overflow: hidden; background: var(--fond-carte);
  display: flex; align-items: center; justify-content: center; color: var(--texte-discret); font-size: 1.1rem;
}
.lecteur-article-image img { display: block; width: 100%; height: 100%; object-fit: cover; }
.lecteur-article-texte { min-width: 0; }
.lecteur-article-surtitre {
  margin: 0 0 .15rem; color: var(--accent); font-size: .65rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
}
.lecteur-article-titre { margin: 0 0 .2rem; font: 600 .85rem/1.25 var(--police); color: var(--texte-fort); }
.lecteur-article-chapo {
  margin: 0 0 .3rem; font-size: .78rem; line-height: 1.35; color: var(--texte-doux);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.lecteur-article-page { margin: 0; font-size: .7rem; color: var(--texte-discret); }
/* Mobile portrait (même seuil que `mqMobile` dans `lecteur.js`) : la colonne réelle n'a de
   sens qu'en bureau/paysage, où il reste de la place à côté de la vue. En dessous, la barre
   passe en bas (pouce), horizontale plutôt que verticale ; le volet redevient un overlay
   (`position: absolute`, comme la pellicule) qui se superpose à la vue au lieu de la
   rétrécir : la vue de lecture reste la plus grande zone possible sur petit écran. Hauteur
   fixe une fois ouvert (comme la largeur fixe du volet en bureau, plus haut) : seule la part
   visible change, jamais la mise en page du texte des cartes. */
@media (orientation: portrait) and (max-width: 640px) {
  .lecteur-corps { flex-direction: column; }
  .lecteur-vue { order: 1; }
  .lecteur-barre-verticale {
    order: 2; width: 100%; height: 44px; flex-direction: row; padding-top: 0;
    border-right: 0; border-top: 1px solid var(--filet);
    transition: background-color .2s ease, color .2s ease;
  }
  .lecteur-barre-verticale[aria-expanded="true"] { width: 100%; height: 44px; font-size: 1.1rem; }
  .lecteur-articles {
    position: absolute; z-index: 26; left: 0; right: 0; bottom: 44px; width: auto; height: 0;
    transition: height .3s ease;
  }
  .lecteur-articles.ouverte { width: auto; height: min(60dvh, 420px); overflow-y: auto; }
  .lecteur-articles-liste { width: auto; }
}
/* Planche (couverture seule, ou double page) : deux pages collées sans marge, comme une
   vraie reliure. Zoom au double clic (`.zoom`) : agrandie autour du point cliqué, glissable à
   la souris ou au doigt et pilotable à la molette une fois zoomée (`lecteur.js`, `panX`/`panY`) ;
   sans transition pendant le glissement (`.glisse`), sinon la translation prend du retard sur
   le pointeur. Le reste de la planche sort du cadre (`overflow: hidden` sur `.lecteur-vue`)
   plutôt que de pousser la mise en page. */
/* `min-width: 0; min-height: 0` sur le spread lui-même (pas seulement `.lecteur-page`,
   plus bas) : lui aussi est un enfant flex (de `.lecteur-vue`), et ses propres enfants
   utilisent `height: 100%` (référençant SA hauteur) ; sans lever la contrainte de taille
   minimale implicite d'un flex item, cette dépendance circulaire empêchait `max-width` /
   `max-height` de contraindre correctement sa taille selon `aspect-ratio` (planche bien
   plus large que prévu, décentrée dans la vue). */
.lecteur-spread {
  position: relative; display: flex; min-width: 0; min-height: 0; max-width: 100%; max-height: 100%;
  background: var(--fond-carte); box-shadow: var(--ombre-couverture), var(--ombre-lecture);
  border-radius: 2px; overflow: hidden;
  transition: transform .2s ease, box-shadow .25s ease;
}
/* Pas de halo clair sur la planche de la couverture (cadre + couverture) : elle n'est pas
   vraiment une double page de lecture. */
.lecteur-spread.couverture { box-shadow: var(--ombre-couverture); }
.lecteur-spread.zoom { cursor: grab; }
.lecteur-spread.zoom.glisse { cursor: grabbing; transition: none; }
/* Zoomé : le tourné de page au clic cède la place au glissement (pan), déjà géré par
   `.lecteur-spread` lui-même (curseur main ci-dessus) ; sans ce `pointer-events: none`, les
   zones l'emporteraient sur ce curseur et intercepteraient le clic avant le glissement. */
.lecteur-spread.zoom .lecteur-zone-nav { pointer-events: none; }
/* `min-width: 0` : un enfant flex ne rétrécit jamais, par défaut, sous la largeur minimale
   de son contenu (texte non coupé des boutons du cadre, par exemple) ; sans ça, un texte un
   peu long pourrait élargir la planche de la couverture au-delà de son `aspect-ratio`,
   la décentrant de la vue. */
.lecteur-page { position: relative; height: 100%; min-width: 0; flex: none; }
/* `-webkit-user-drag: none` : sans ça, un glissement pour le pan (une fois zoomé) est
   interrompu au bout d'un instant par le glisser-déposer natif de l'image du navigateur. */
.lecteur-page img { display: block; height: 100%; width: auto; object-fit: contain; -webkit-user-drag: none; user-select: none; }
.lecteur-page-vide { background: var(--fond-carte); }
/* `appearance: none` et le reste : un lien de type « page » est un `<button>` (zone
   cliquable, pas de vraie destination d'URL) ; sans ce reset, `color-scheme: dark` (posé
   sur `:root`) lui donne le style natif sombre et OPAQUE d'un bouton de formulaire, pas
   une zone cliquable discrète sur une image. Devinable au repos par un contour clair très
   discret (pas de fond), plus marqué au survol. */
.lecteur-lien {
  position: absolute; display: block; appearance: none; background: transparent; border: 0; padding: 0; margin: 0;
  border-radius: 2px; outline: 1px solid rgba(233, 230, 238, .22); outline-offset: -1px; cursor: pointer;
}
.lecteur-lien:hover, .lecteur-lien:focus-visible { background: rgba(201, 169, 110, .16); outline: 2px solid var(--accent-doux); outline-offset: -2px; }

/* Zones de clic « tourner la page » (tiers extérieur de chaque page, bureau/paysage
   seulement, voir `zoneTournePage` dans `lecteur.js`) : même effet qu'un clic sur la flèche
   de navigation correspondante. Placées avant les liens de la page dans le DOM (juste au-
   dessus) : un `.lecteur-lien` qui chevauche cette zone reste cliquable en priorité. Curseur
   en flèche directionnelle (SVG en ligne : aucun curseur natif « flèche simple » vers un
   seul sens n'existe) pour indiquer le sens sans ambiguïté avec le survol normal d'une page.
   Pas de `data-tooltip`/`aria-label` propres (`tabindex="-1"`, voir le balisage) : un
   raccourci souris redondant avec les boutons `.lecteur-nav`, déjà correctement annoncés. */
.lecteur-zone-nav { position: absolute; top: 0; bottom: 0; width: 33.34%; border: 0; padding: 0; margin: 0; background: transparent; }
.lecteur-zone-precedente {
  left: 0;
  cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='34' height='34'%3E%3Ccircle cx='17' cy='17' r='15' fill='rgba(13,12,17,.6)' stroke='rgba(233,230,238,.55)'/%3E%3Cpath d='M20 10l-7 7 7 7' stroke='%23e9e6ee' stroke-width='2.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 17 17, w-resize;
}
.lecteur-zone-suivante {
  right: 0;
  cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='34' height='34'%3E%3Ccircle cx='17' cy='17' r='15' fill='rgba(13,12,17,.6)' stroke='rgba(233,230,238,.55)'/%3E%3Cpath d='M14 10l7 7-7 7' stroke='%23e9e6ee' stroke-width='2.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 17 17, e-resize;
}

/* Cadre de la couverture (voir `page.md`) : à gauche de la couverture (traitée comme une page
   de droite) à l'arrivée sur la liseuse, reprend la fiche de l'édition sans le bouton de
   lecture. Mêmes classes que la fiche (`.surtitre`, `.titre-page`) pour un rendu cohérent. */
.lecteur-cadre { display: flex; align-items: center; justify-content: center; background: var(--fond-carte); padding: 1.75rem; text-align: left; }
.lecteur-cadre-contenu { max-width: 100%; }
.lecteur-cadre .titre-page { font-size: clamp(1.1rem, 3vw, 1.6rem); margin: .35rem 0 1.1rem; }
/* Bouton vers l'univers de rattachement : doré (`.is-primary`), un cran plus gros que
   « Retour aux univers » sous lui (celui-ci reste `.is-small`). */
.lecteur-cadre-univers { display: flex; margin-bottom: .6rem; }

/* Fausse page de fin (voir `chargerBonus`/`contenuBonus` dans `lecteur.js`) : dernière page
   du numéro seule en planche, plutôt qu'une case vide à côté d'elle. Même famille visuelle
   que le cadre de couverture ci-dessus (fond plein, contenu centré). */
.lecteur-bonus { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1.1rem; background: var(--fond-carte); padding: 1.5rem; }
.lecteur-bonus-mosaique { display: grid; grid-template-columns: repeat(3, 1fr); gap: .4rem; width: 100%; max-width: 300px; }
.lecteur-bonus-vignette { display: block; aspect-ratio: 3 / 4; border-radius: 4px; overflow: hidden; background: var(--fond-carte-survol); box-shadow: var(--ombre-couverture); }
/* Priorité sur `.lecteur-page img` (plus haut) : les vignettes de la mosaïque, décoratives,
   remplissent leur case (`cover`), contrairement à une vraie page de lecture (`contain`). */
.lecteur-bonus-vignette img { display: block; width: 100%; height: 100%; object-fit: cover; }
.lecteur-bonus-vignette:hover, .lecteur-bonus-vignette:focus-visible { outline: 2px solid var(--accent-doux); outline-offset: 1px; }

/* Feuille (voir `page.md`) : la seule page qui tourne au changement de planche (`tournerPage`
   dans `lecteur.js`), posée par-dessus la vue, à la position exacte de la case d'origine.
   Deux faces (`preserve-3d`, `backface-visibility: hidden`) : l'avant montre son contenu de
   départ, l'arrière (pré-tournée à 180°) le contenu qui doit apparaître une fois arrivée à
   plat. Le calque `.lecteur-reveal`, dessous, découvre la case que la feuille libère. */
.lecteur-reveal {
  position: absolute; z-index: 15; overflow: hidden; pointer-events: none; background: var(--fond-carte); box-shadow: var(--ombre-couverture);
}
.lecteur-reveal .lecteur-page, .lecteur-reveal .lecteur-cadre, .lecteur-reveal .lecteur-bonus { height: 100%; width: 100%; }

/* Tourné de page en 3D (`poserScene` de lecteur.js) : scène sous perspective, feuille en tranches imbriquées qui se
   plient (courbure maximale à mi-course), ombrage progressif, calque `.lecteur-reveal` avec ombre portée. `--p`
   (progression du demi-tour, 0 à 1) est animée par WAAPI ; `--s` vaut 1 (page de droite qui part vers la gauche)
   ou -1. Ni pointeur ni focus : la scène est inerte, la page dessous reste cliquable. */
@property --p { syntax: '<number>'; inherits: true; initial-value: 0; }
.lecteur-scene {
  position: fixed; inset: 0; z-index: 60; pointer-events: none;
  perspective: var(--profondeur); perspective-origin: 50% 50%; transform-style: preserve-3d;
  --profondeur: 7500px; /* distance de la perspective : plus elle est courte, plus l'effet 3D est marqué */
  --courbure: 1.15deg;
}
.lecteur-scene .lecteur-feuille { position: absolute; transform-style: preserve-3d; pointer-events: none; }
.lecteur-tranche { position: absolute; top: 0; transform-style: preserve-3d; }
.lecteur-tranche-base { transform: rotateY(calc(var(--p) * var(--s) * -180deg)); }
.lecteur-tranche:not(.lecteur-tranche-base) {
  transform: rotateY(calc(var(--s) * -1 * sin(var(--p) * 180deg) * var(--courbure)));
}
.lecteur-tranche-face { position: absolute; inset: 0; overflow: hidden; backface-visibility: hidden; }
.lecteur-tranche-arriere { transform: rotateY(180deg); }
.lecteur-tranche-contenu { position: absolute; top: 0; }
.lecteur-tranche-contenu .lecteur-page, .lecteur-tranche-contenu .lecteur-cadre, .lecteur-tranche-contenu .lecteur-bonus { height: 100%; width: 100%; }
/* Ombrage des faces : plus sombre à mi-course et vers le bord libre, pour lire la courbure. */
.lecteur-tranche-face::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; background: #000;
  opacity: calc(sin(var(--p) * 180deg) * 0.10);
}
.lecteur-scene .lecteur-reveal::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  opacity: calc(sin(var(--p) * 180deg) * 0.85);
}
.lecteur-reveal-droite::after { background: linear-gradient(to right, rgba(0, 0, 0, .55), rgba(0, 0, 0, .12) 45%, transparent 75%); }
.lecteur-reveal-gauche::after { background: linear-gradient(to left, rgba(0, 0, 0, .55), rgba(0, 0, 0, .12) 45%, transparent 75%); }

.lecteur-nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 10;
  width: 44px; height: 44px; border: 0; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  background: rgba(22, 21, 28, .6); color: var(--texte); font-size: 1.05rem; cursor: pointer;
}
.lecteur-nav:hover, .lecteur-nav:focus-visible { background: var(--fond-carte-survol); color: var(--texte-fort); }
.lecteur-nav:disabled { opacity: .25; pointer-events: none; }
.lecteur-nav-precedente { left: max(.5rem, env(safe-area-inset-left)); }
.lecteur-nav-suivante { right: max(.5rem, env(safe-area-inset-right)); }

.lecteur-recherche {
  position: absolute; z-index: 30; top: calc(48px + env(safe-area-inset-top) + .5rem); right: max(.75rem, env(safe-area-inset-right));
  left: max(.75rem, env(safe-area-inset-left)); margin: 0 auto; width: min(100%, 26rem);
  background: var(--fond-releve); border: 1px solid var(--filet); border-radius: var(--rayon); box-shadow: var(--ombre-couverture);
  display: flex; flex-direction: column; max-height: min(70dvh, 32rem); overflow: hidden;
}
.lecteur-recherche[hidden] { display: none; }
.lecteur-recherche-champ { flex: none; display: flex; align-items: center; gap: .5rem; padding: .6rem .75rem; border-bottom: 1px solid var(--filet); color: var(--texte-discret); }
.lecteur-recherche-champ input {
  flex: 1 1 auto; min-width: 0; background: none; border: 0; color: var(--texte-fort); font: 500 .9rem var(--police);
}
.lecteur-recherche-champ input:focus { outline: 0; }
.lecteur-recherche-champ input::placeholder { color: var(--texte-discret); }
.lecteur-resultats { flex: 1 1 auto; min-height: 0; overflow-y: auto; list-style: none; margin: 0; padding: .35rem; }
.lecteur-resultat {
  display: flex; flex-direction: column; gap: .15rem; width: 100%; text-align: left; border: 0; background: none; border-radius: 8px;
  padding: .55rem .6rem; min-height: 40px; color: var(--texte); font: 500 .875rem var(--police); cursor: pointer;
}
.lecteur-resultat:hover, .lecteur-resultat:focus-visible { background: var(--fond-carte-survol); color: var(--texte-fort); }
.lecteur-resultat .page-resultat { color: var(--texte-discret); font-size: .75rem; margin-left: .4rem; }
.lecteur-resultat-extrait { color: var(--texte-discret); font-size: .78rem; font-weight: 400; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lecteur-etat-recherche { padding: 1rem .75rem; color: var(--texte-discret); font-size: .85rem; text-align: center; }

/* Pellicule en overlay sous la barre (jamais dans le flux : ne change jamais la hauteur des
   pages). Glisse depuis la barre (`translateY`, comme si elle en descendait) plutôt que d'être
   montrée/masquée net (`display`), pour l'effet demandé ; toujours dans le DOM, invisible et
   hors du chemin des clics tant que `.ouverte` n'est pas posée (`togglePellicule`). Posée à
   `top: 0` (pas sous la barre) : au repos, `translateY(-100%)` la sort entièrement par le haut
   de la vue, quelle que soit sa hauteur (un pourcentage est toujours exact pour sa propre
   boîte) ; ouverte, elle descend exactement de la hauteur de la barre. Un repos calé plus bas
   (`top` sous la barre, `translateY` en pourcentage un peu supérieur à 100 %) laissait un fin
   bandeau de vignettes dépasser derrière la barre, elle-même partiellement transparente. */
.lecteur-pellicule {
  position: absolute; z-index: 25; top: 0; left: 0; right: 0;
  display: flex; align-items: center; gap: .35rem;
  max-height: min(42dvh, 230px); overflow: hidden;
  padding: .5rem max(.5rem, env(safe-area-inset-right)) .6rem max(.5rem, env(safe-area-inset-left));
  background: linear-gradient(to bottom, rgba(13, 12, 17, .96), rgba(13, 12, 17, .88));
  border-bottom: 1px solid var(--filet); box-shadow: var(--ombre-couverture);
  transform: translateY(-100%); transition: transform .28s ease; pointer-events: none;
}
.lecteur-pellicule.ouverte { transform: translateY(calc(48px + env(safe-area-inset-top))); pointer-events: auto; }
/* Pas de barre de défilement visible : des flèches à la place (`.lecteur-pellicule-fleche`),
   estompées et inactives en bout de course (`majFlechesPellicule`, `:disabled`). */
.lecteur-pellicule-rail {
  display: flex; gap: .4rem; overflow-x: auto; scroll-behavior: smooth;
  scrollbar-width: none;
}
.lecteur-pellicule-rail::-webkit-scrollbar { display: none; }
.lecteur-pellicule-fleche {
  flex: none; display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border: 0; border-radius: 999px; background: var(--fond-carte-survol);
  color: var(--texte); font-size: .85rem; cursor: pointer;
}
.lecteur-pellicule-fleche:hover, .lecteur-pellicule-fleche:focus-visible { color: var(--texte-fort); }
.lecteur-pellicule-fleche:disabled { opacity: .25; pointer-events: none; }
.lecteur-vignette {
  position: relative; flex: none; display: block; height: 96px; border: 2px solid transparent; border-radius: 4px; overflow: hidden;
  background: var(--fond-carte); padding: 0; cursor: pointer;
}
.lecteur-vignette img { display: block; height: 100%; width: auto; }
.lecteur-vignette.is-active { border-color: var(--accent); }
.lecteur-vignette:hover, .lecteur-vignette:focus-visible { border-color: var(--texte-discret); }
.lecteur-vignette.is-active:hover { border-color: var(--accent); }
.lecteur-vignette-numero {
  position: absolute; left: 4px; bottom: 4px; min-width: 18px; height: 18px; padding: 0 4px;
  display: flex; align-items: center; justify-content: center; border-radius: 999px;
  background: rgba(13, 12, 17, .82); color: var(--texte-fort); font: 700 .65rem/1 var(--police);
}

@media (min-width: 1024px) {
  .lecteur-barre { min-height: 52px; }
  .lecteur-titre-texte { font-size: .95rem; }
  .lecteur-pellicule.ouverte { transform: translateY(calc(52px + env(safe-area-inset-top))); }
  .lecteur-vignette { height: 114px; }
}

/* --- Pages de réponse du serveur (connexion, erreurs) ------------------------------------------------ */
.message-page { max-width: 40rem; }
.message-page .title { color: var(--texte-fort); font-family: var(--police-titres); font-weight: 500; }
.logo-page { height: 1.75rem; }

/* --- Tailles d'écran ---------------------------------------------------------------------------------
 * Mobile par défaut, tablette (≥ 600 px), bureau (≥ 1024 px), grand bureau (≥ 1600 px, ≥ 2200 px),
 * et vue peu haute (téléphone en paysage).
 */
@media (max-width: 480px) {
  .barre-logo img { height: 18px; }
}
@media (max-height: 540px) {
  body { --hauteur-barre: 44px; --respiration: .75rem; }
  .panneau-tete { min-height: 44px; }
}
@media (min-width: 600px) {
  body { --couverture-min: 150px; --univers-min: 240px; --espace-grille: 1.25rem; }
}
@media (min-width: 1024px) {
  body { --couverture-min: 165px; --univers-min: 280px; --espace-grille: 1.5rem; --hauteur-barre: 60px; }
}
@media (min-width: 1600px) {
  body { --couverture-min: 190px; --univers-min: 320px; --espace-grille: 1.75rem; --largeur-panneau: 300px; }
  .legende-titre { font-size: .95rem; }
  .legende-date { font-size: .8rem; }
  .onglet { font-size: 1rem; min-height: 48px; }
  .retour, .bienvenue { font-size: 1.05rem; }
}
@media (min-width: 2200px) {
  body { --couverture-min: 220px; --univers-min: 380px; --espace-grille: 2rem; --largeur-panneau: 340px; }
  .barre-logo img { height: 26px; }
  .bouton-compte, .bouton-connexion { font-size: .95rem; }
}
