/* ════════════════════════════════════════════════════════════════════
   topbar.css — barre persistante unifiée de Cizi.fr  (VERSION CONSOLIDÉE)
   ────────────────────────────────────────────────────────────────────
   Refonte "franc" du 2026-06-14 : un seul bloc par sélecteur, valeurs
   finales identiques au rendu précédent (calculées depuis la cascade des
   ~30 patchs empilés). Aucune intention visuelle changée.

   topbar-layout-v3-marker — 20260705 · consolidation n°2 : UNE SEULE LIGNE.
   Layout : [burger][logo /catégorie /section▾][🔍] ─ [HH:MM] ─ [mini-player][zoom][⛶]
   · La ligne 2 fixe (top:40px) est supprimée : ses choix (Accueil, Films,
     Séries — Sono retiré) deviennent un SECOND fil d'Ariane "/Section" à la
     suite de la catégorie, MÊME LOGIQUE que /Catégorie : bouton + dropdown
     (classes .topbar-cat / .topbar-dropdown réutilisées, cf.
     section-dropdown-marker). Vision browse uniquement.
   · La loupe a un emplacement FIXE en fin de groupe gauche : la préférence
     Recherche centre/droite est morte (cizi-menu.js v4.4). Compat PHP
     transitoire conservée plus bas (bloc topbar-layout-v2) tant
     qu'entete.php n'a pas suivi.
   · Loupe + onglets ne s'effacent plus en plein écran QUE si l'horloge y est
     réellement affichée (bloc fs-hide-when-clock) — l'ancien masquage FS
     inconditionnel (dont le patch fs-search-class-fix) est SUPPRIMÉ.

   Conventions :
   - Les variables --cizi-*-outline portent le contour noir cartoon.
   - Les blocs "ÉTAT" (body[data-*], :fullscreen, .is-*) pilotent le
     comportement (FS vidéo/musique, iframe Vision, auto-hide) : NE PAS
     toucher sans tester les 6 scénarios (cf. checklist en pied de fichier).
   ════════════════════════════════════════════════════════════════════ */

@import url('https://fonts.googleapis.com/css2?family=Baloo+2:wght@700;800&display=swap');

:root {
  /* Police cartoon des catégories/labels. Un seul point de changement :
     remplacer cette valeur (+ l'@import ci-dessus) pour changer partout. */
  --cizi-font-cartoon: 'Baloo 2', system-ui, sans-serif;
  --cizi-text-outline:
    -1px -1px 0 #000,  1px -1px 0 #000,
    -1px  1px 0 #000,  1px  1px 0 #000,
     0    0   4px rgba(0, 0, 0, 0.75);
  --cizi-img-outline:
    drop-shadow( 1px  1px 0 rgba(0, 0, 0, 0.95))
    drop-shadow(-1px -1px 0 rgba(0, 0, 0, 0.95))
    drop-shadow(-1px  1px 0 rgba(0, 0, 0, 0.95))
    drop-shadow( 1px -1px 0 rgba(0, 0, 0, 0.95))
    drop-shadow( 0    0   3px rgba(0, 0, 0, 0.7));
  /* Rattrapage optique vertical du label catégorie vs logo.
     0 par défaut avec Baloo 2 ; ajuster de -1 à -3px si besoin de remonter,
     ou +1/+2px pour descendre le label. */
  --cizi-cat-fine-tune: 0px;

  /* largeur-contenu-marker (11/09/2026) — « étendre cette nouvelle présentation
     dans toutes les catégories du site, dont vision et sono ».
     LE réglage unique de la largeur du contenu, pour les DEUX dépôts. Il vit
     ici et pas dans base.css parce que Vision charge ce fichier-ci
     (app/layout.tsx : <link href="https://cizi.fr/styles/topbar.css">) et pas
     l'autre : une variable posée ici traverse l'iframe, et les classes
     Tailwind de Vision la lisent par max-w-[var(--cizi-contenu-max,1080px)].
     1080 px est la valeur historique de .bloc-page — donc celle de l'accueil
     cizi.fr. C'était ma valeur au 11/09 au matin — j'avais aligné Vision sur le
     shell pour ne pas déplacer la page qui servait d'étalon.
     Tu as inversé la référence le soir même : « la page de l'accueil Vision doit
     servir de modèle pour toutes les pages internes à cizi.fr ». C'est donc le
     1280 de Vision qui gouverne maintenant, et le shell qui s'élargit — accueil
     cizi.fr, jeux, outils et cartes compris. Une seule ligne à rebasculer si
     l'accueil te paraît trop étalé. */
  --cizi-contenu-max: 1280px;
}

/* ════════════════════════ STRUCTURE GÉNÉRALE ════════════════════════ */

/* Topbar : flex, transparente, contour géré par les enfants. Hissée au
   sommet du stacking (INT_MAX-7) pour passer au-dessus de toute vignette
   Vision. isolation + translateZ = stacking context racine garanti. */
.topbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 48px;
  display: flex;
  align-items: center;
  gap: 0;
  /* barre-trois-zones-marker — le rembourrage devient deux variables : la zone
     centrale s'en sert pour viser le centre de l'ECRAN et non celui de la
     boite de contenu, que ce rembourrage asymetrique decale (mesure : 6 px a
     gauche sur PC, 3 px sur mobile). */
  --topbar-pad-g: 16px;
  --topbar-pad-d: 28px;
  padding: 0 var(--topbar-pad-d) 0 var(--topbar-pad-g);
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-bottom: none;
  font-family: monospace;
  color: rgba(240, 235, 220, 0.85);
  z-index: 2147483640;
  isolation: isolate;
  transform: translateZ(0);
}

/* Compense la hauteur de la topbar fixe (sauf Vision/Sono en iframe, voir ÉTATS). */
body { padding-top: var(--topbar-h, 56px); }

/* ════════════════════════ ZONE GAUCHE ════════════════════════ */

.topbar-left {
  display: flex;
  align-items: flex-end;   /* bas du logo = bas des labels */
  gap: 4px;
  min-width: 0;
  padding-bottom: 0;
}

/* [1] Burger (réglages — cf. cizi-menu.js). Historique : topbar-layout-v2. */
.topbar-burger {
  background: none;
  border: 0;
  cursor: pointer;
  color: #fff;
  padding: 4px 6px;
  margin: 0;
  display: inline-flex;
  align-items: center;
  align-self: center;
  line-height: 0;
  opacity: .9;
}
.topbar-burger:hover { opacity: 1; }

.topbar-logo {
  display: flex;
  align-items: center;
  height: 32px;
  flex-shrink: 0;
  transition: filter 0.2s, transform 0.2s;
}
.topbar-logo img {
  height: 100%;
  width: auto;
  display: block;
  flex-shrink: 0;
}
.topbar-logo:hover {
  filter: drop-shadow(0 0 6px rgba(240, 235, 220, 0.6));
  transform: scale(1.05);
}

/* Ancien séparateur <span>/</span> : retiré du flux (le "/" est désormais
   un ::before du label catégorie). Tout son style était mort. */
.topbar-sep { display: none; }

/* Bouton catégorie (label + icône). Le "/" est injecté en ::before. */
.topbar-cat {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  padding: 0 8px 0 0;
  border: none;
  border-radius: 8px;
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  color: rgba(240, 235, 220, 1);
  font-family: var(--cizi-font-cartoon);
  font-weight: normal;
  font-size: 30px;
  letter-spacing: 0.02em;
  line-height: 1;
  cursor: pointer;
  text-shadow: var(--cizi-text-outline);
  transform: translateY(var(--cizi-cat-fine-tune));
  transition: background 0.2s;
}
.topbar-cat:hover   { background: rgba(0, 0, 0, 0.25); }
.topbar-cat.is-open { background: rgba(0, 0, 0, 0.35); }

.topbar-cat::before {
  content: '/';
  margin-right: 3px;
  font-family: var(--cizi-font-cartoon);
  font-weight: normal;
  font-size: 30px;
  color: rgba(240, 235, 220, 1);
  text-shadow: var(--cizi-text-outline);
}

.topbar-cat-label {
  font-family: var(--cizi-font-cartoon);
  font-size: 30px;
  color: rgba(240, 235, 220, 1);
  white-space: nowrap;
  overflow: visible;
  text-overflow: clip;
  text-shadow: var(--cizi-text-outline);
}

/* Icône de catégorie (homogène PHP/Vision). 32px = hauteur du logo Cizi,
   recentrée sur la ligne (la zone gauche est en flex-end). */
.topbar-cat-icon {
  height: 32px;
  width: auto;
  margin-left: 6px;
  display: inline-block;
  vertical-align: middle;
  align-self: center;
  position: relative;
  top: 2px;
  border-radius: 4px;
  flex-shrink: 0;
  filter: var(--cizi-img-outline);
  /* logo-cat-ecrase-marker (08/09/2026) — BUG 20260908-234914 « le logo vision
     dans la barre est écrasé ».
     Le coupable n'est pas dans ce fichier : c'est la preflight de Tailwind,
     qui s'applique DANS l'iframe Vision et pose img{max-width:100%;height:auto}
     sur toutes les images. height:32px ci-dessus l'emporte sur height:auto,
     mais rien ne neutralisait max-width — alors dès que le bouton est serré
     par ses voisins, la largeur est rabotée pendant que la hauteur reste a 32.
     Reproduit au banc d'essai : bouton large 64x32 (ratio 2.00, correct),
     bouton serré 36x32 (ratio 1.12, écrasé), et 64x32 de nouveau avec la ligne
     ci-dessous. flex-shrink:0 ne suffisait pas : il empêche le rétrécissement
     FLEX, pas le plafonnement par max-width. */
  max-width: none;
  object-fit: contain;
}

/* Flèche ▾ retirée (display:none). Tout son style était mort. */
.topbar-cat-arrow { display: none; }

/* section-dropdown-marker — SECOND FIL D'ARIANE "/Section" (Accueil, Films,
   Séries) : le bouton réutilise .topbar-cat en entier ("/" en ::before,
   police cartoon, hover, is-open) et son dropdown réutilise .topbar-dropdown.
   Ce modificateur ne porte que les micro-ajustements propres au segment. */
.topbar-cat--section { margin-left: 2px; }

/* Boutons ronds loupe + plein écran. */
.topbar-search-btn,
.topbar-fullscreen {
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  color: rgba(240, 235, 220, 1);
  cursor: pointer;
  transition: background 0.2s, color 0.2s;
}
.topbar-search-btn { margin-left: 18px; }
.topbar-search-btn:hover,
.topbar-fullscreen:hover {
  background: rgba(0, 0, 0, 0.25);
  color: rgba(255, 255, 255, 1);
}
.topbar-search-btn svg,
.topbar-fullscreen svg {
  width: 22px;
  height: 22px;
  filter: var(--cizi-img-outline);
}

/* topbar-layout-v3-marker — [4] loupe en FIN DE GROUPE GAUCHE : recentrée
   verticalement, resserrée (le 18px historique visait la zone droite),
   incompressible. */
.topbar-left .topbar-search-btn {
  margin-left: 8px;
  align-self: center;
  flex-shrink: 0;
}

/* Sous-label Vision (ex "/ Films") — rendu par le React, stylé ici. */
.topbar-sublabel {
  font-family: var(--cizi-font-cartoon);
  font-size: 24px;
  color: rgba(240, 235, 220, 1);
  letter-spacing: 0.06em;
  padding: 0 6px;
  line-height: 1;
  text-shadow: var(--cizi-text-outline);
}

/* ════════════════════════ ZONE CENTRE : HORLOGE ════════════════════════ */

/* Conteneur invisible occupant toute la barre : l'horloge est calée au
   STRICT centre de l'écran (topbar-layout-v2, conservé en v3 — le centre ne
   contient plus QUE l'horloge côté Vision). */
.topbar-center {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
}
.topbar-clock {
  display: inline-block;
  pointer-events: auto;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  font-family: monospace;
  font-size: 18px;
  font-weight: 600;
  letter-spacing: 0.1em;
  font-variant-numeric: tabular-nums;
  color: rgba(240, 235, 220, 1);
  text-shadow: var(--cizi-text-outline);
  transition: opacity 120ms ease;
}
.topbar-clock.is-hidden { visibility: hidden; opacity: 0; }

/* topbar-layout-v2 : bloc compat PHP SUPPRIMÉ le 20260711 (entete.php est passé au layout v3, loupe en zone gauche). */

/* ════════════════════════ ZONE DROITE ════════════════════════ */

.topbar-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  min-width: 0;
}
.topbar-modules {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 32px;
}

/* ════════════════════════ DROPDOWN CATÉGORIES ════════════════════════ */

.topbar-dropdown {
  position: fixed;
  top: 58px;
  left: 16px;
  z-index: 11;
  min-width: 320px;
  /* menu-cartes-deborde-marker (08/09/2026) — BUG 20260907-221607. Filet de
     securite pour TOUS les menus deroulants, pas seulement celui des cartes :
     min-width:320px sur un ecran de 360 px ne laisse que 40 px de jeu, et il
     suffit que le bouton d'ancrage ne soit pas tout a gauche pour que le
     panneau sorte de l'ecran. Le calage precis se fait en JS (carte/index.php),
     mais cette borne protege meme si un menu oublie de le faire. */
  max-width: calc(100vw - 16px);
  box-sizing: border-box;
  padding: 10px;
  background: rgba(20, 20, 20, 0.95);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(240, 235, 220, 0.25);
  border-radius: 16px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
  display: flex;
  flex-direction: column;
  gap: 4px;
  opacity: 0;
  transform: translateY(-6px);
  pointer-events: none;
  transition: opacity 0.15s, transform 0.15s;
}
.topbar-dropdown.is-open {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.topbar-dropdown-item {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 10px 14px;
  color: rgba(240, 235, 220, 1);
  text-decoration: none;
  font-family: monospace;
  font-size: 13px;
  letter-spacing: 0.06em;
  border-radius: 10px;
  transition: background 0.15s, color 0.15s;
}
.topbar-dropdown-item:hover {
  background: rgba(240, 235, 220, 0.12);
  color: rgba(240, 235, 220, 1);
}
.topbar-dropdown-item:hover .topbar-dropdown-icon {
  filter: var(--cizi-img-outline) brightness(1.15);
}
/* Icône dropdown agrandie (64px), forcée à gauche (order:-1). */
.topbar-dropdown-icon {
  width: 64px;
  height: 64px;
  order: -1;
  object-fit: contain;
  flex-shrink: 0;
  border-radius: 10px;
  filter: var(--cizi-img-outline);
  transition: filter 0.15s;
}
.topbar-dropdown-label {
  flex: 1;
  font-family: var(--cizi-font-cartoon);
  font-size: 30px;
  letter-spacing: 0.02em;
  text-shadow: var(--cizi-text-outline);
}
/* Catégorie courante : encadrée (box-shadow inset = pas de shift), pas grisée. */
.topbar-dropdown-item.is-current {
  color: rgba(240, 235, 220, 1);
  pointer-events: none;
  background: rgba(240, 235, 220, 0.06);
  box-shadow: inset 0 0 0 2px rgba(240, 235, 220, 1);
}
.topbar-dropdown-item.is-current::before { content: none; }
.topbar-dropdown-item.is-current .topbar-dropdown-icon { opacity: 1; }

/* Sous-menu Vision (Films / Séries) dans le dropdown. */
.topbar-dropdown-vision-wrap { display: flex; flex-direction: column; gap: 2px; }
.topbar-dropdown-vision-row  { display: flex; align-items: center; gap: 4px; }
.topbar-dropdown-vision-row .topbar-dropdown-item--main { flex: 1; }
.topbar-dropdown-sub-toggle {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  border-radius: 6px;
  color: rgba(240, 235, 220, 0.6);
  font-size: 14px;
  cursor: pointer;
  transition: background 0.15s, transform 0.15s, color 0.15s;
}
.topbar-dropdown-sub-toggle:hover {
  background: rgba(240, 235, 220, 0.12);
  color: rgba(240, 235, 220, 1);
}
.topbar-dropdown-sub-toggle.is-open {
  transform: rotate(90deg);
  color: rgba(240, 235, 220, 1);
}
.topbar-dropdown-submenu {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-left: 16px;
  margin-left: 4px;
  border-left: 1px solid rgba(240, 235, 220, 0.15);
}
.topbar-dropdown-item--sub {
  font-size: 24px;
  opacity: 0.85;
  padding: 8px 12px;
}
.topbar-dropdown-item--sub:hover { opacity: 1; }

/* ══════════════════════════════════════════════════════════════════════════
   menu-structure-marker (13/09/2026) — BUG 20260913-191732
   « Le menu ne ressemble à rien. Rend le plus structuré. » (/vision, 360×699)

   TROIS CHOSES, dans l'ordre où elles sautent aux yeux sur la capture.

   1. LE MENU DÉBORDE. Relevé au banc à 360 × 699 : 720 px de contenu pour 595
      de place. La huitième catégorie, « Aide », est sous la ligne de
      flottaison, et rien ne dit qu'il faut faire défiler. Les deux crans
      ci-dessous resserrent les rangées jusqu'à ce que tout tienne ; c'est
      scripts/pastille-joystick.js qui mesure et qui pose la classe, parce que
      la place disponible ne se déduit pas de la largeur de l'écran.

   2. LE MENU N'A PAS DE TITRE, alors que les deux panneaux de gauche en ont
      un. Trois boîtes voisines dont une seule est nommée : rien n'annonce ce
      qu'on lit. Le titre est posé en ::before et non dans le balisage, parce
      que ce menu est écrit à DEUX endroits — gabarits/entete.php et
      vision/components/topbar.tsx — et qu'une règle de style les sert tous les
      deux sans rebuild côté Vision.

   3. LES MOTS DES PANNEAUX DÉBORDENT DE LEUR CADRE. « Réglages » dépassait de
      56 px sur la droite, donc par-dessus le menu des catégories : c'est ce
      qui fait le plus désordre sur la capture. La cause est bête — le libellé
      garde la taille du menu principal (30 px, 22 sur téléphone) dans un
      panneau qui fait 106 px de large. Il prend ici une taille à sa mesure, et
      une coupure propre en dernier recours.
   ══════════════════════════════════════════════════════════════════════════ */

/* 1. LES DEUX CRANS DE RESSERREMENT. Rien ne s'applique tant que le JS n'a pas
      constaté que ça ne tenait pas : sur un écran normal le menu ne bouge pas.
      Premier cran : l'icône passe de 64 à 48, la rangée de 84 à 60 px, les huit
      catégories tiennent dans 512 px au lieu de 720. Second cran, pour les
      écrans vraiment courts (un téléphone couché) : 36 px d'icône, 44 de
      rangée, 384 px en tout. */
#topbar-dropdown.pj-menu-serre .topbar-dropdown-item { padding: 6px 12px; gap: 12px; }
#topbar-dropdown.pj-menu-serre .topbar-dropdown-icon { width: 48px; height: 48px; }
#topbar-dropdown.pj-menu-serre .topbar-dropdown-label { font-size: 24px; }
#topbar-dropdown.pj-menu-serre .topbar-dropdown-item--sub { font-size: 18px; padding: 5px 10px; }

#topbar-dropdown.pj-menu-tres-serre .topbar-dropdown-item { padding: 4px 10px; gap: 10px; }
#topbar-dropdown.pj-menu-tres-serre .topbar-dropdown-icon { width: 36px; height: 36px; border-radius: 8px; }
#topbar-dropdown.pj-menu-tres-serre .topbar-dropdown-label { font-size: 19px; }
#topbar-dropdown.pj-menu-tres-serre .topbar-dropdown-item--sub { font-size: 15px; padding: 3px 10px; }

/* Troisième cran, réservé aux écrans COUCHÉS : à 699 × 274, huit rangées ne
   tiennent à aucune taille lisible (190 px de place pour 352 de contenu), mais
   la largeur, elle, est disponible. Le menu passe en deux colonnes. C'est le
   seul cas où il s'élargit ; scripts/pastille-joystick.js le recale ensuite
   horizontalement, sans quoi il sortirait par la droite. */
#topbar-dropdown.pj-menu-colonnes {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2px 6px;
  min-width: min(420px, calc(100vw - 16px));
}
#topbar-dropdown.pj-menu-colonnes .topbar-dropdown-item { padding: 3px 8px; gap: 8px; }
#topbar-dropdown.pj-menu-colonnes .topbar-dropdown-icon { width: 30px; height: 30px; border-radius: 7px; }
#topbar-dropdown.pj-menu-colonnes .topbar-dropdown-label { font-size: 17px; }
#topbar-dropdown.pj-menu-colonnes::before,
#topbar-dropdown.pj-menu-colonnes .topbar-dropdown-sep { grid-column: 1 / -1; }
#topbar-dropdown.pj-menu-colonnes .topbar-dropdown-vision-wrap { grid-column: 1 / -1; }

/* Sur un écran étroit, le menu resserré laisse aussi plus de place à la colonne
   de gauche : 208 px au lieu de 240, et les panneaux gagnent les trente pixels
   qui manquaient au mot « Réglages ». Borné aux petits écrans — sur un
   ordinateur le menu garde ses 320 px, seule la hauteur de ses rangées change. */
@media (max-width: 520px) {
  #topbar-dropdown.pj-menu-serre { min-width: 208px; }
}

/* 2. LE TITRE. Même écriture que .topbar-dropdown-sep — c'est le même objet,
      un intertitre de menu — mais sans le filet du haut : il n'y a rien
      au-dessus de lui à séparer. */
#topbar-dropdown::before {
  content: "Catégories";
  margin: 0 0 2px;
  padding: 2px 14px 6px;
  font-family: monospace;
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(240, 235, 220, 0.42);
  pointer-events: none;
}
#topbar-dropdown.pj-menu-tres-serre::before { padding-bottom: 3px; }

/* ════════════════════════ OVERLAY RECHERCHE ════════════════════════ */

.topbar-search-overlay {
  position: fixed;
  inset: 0;
  z-index: 12;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 80px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s;
}
.topbar-search-overlay.is-open { opacity: 1; pointer-events: auto; }
.topbar-search-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.topbar-search-panel {
  position: relative;
  width: min(720px, 92vw);
  padding: 18px;
  background: rgba(20, 20, 20, 0.95);
  border: 1px solid rgba(240, 235, 220, 0.2);
  border-radius: 16px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
  transform: translateY(-8px);
  transition: transform 0.2s;
}
.topbar-search-overlay.is-open .topbar-search-panel { transform: translateY(0); }
.topbar-search-panel .search-wrap { margin: 0; padding: 0; }

/* topbar-layout-v3-marker — l'ancienne barre fixe .topbar-tabs (top:40px,
   ligne 2) et ses items .topbar-tab sont SUPPRIMÉS : topbar.tsx ne les rend
   plus, le dropdown de section réutilise .topbar-dropdown-item. */

/* ════════════════════════ MINI-LECTEUR AUDIO (topbar-right) ════════════════════════ */

/* --- Pastille piste (ancien format .cizi-mini-track) --- */
.cizi-mini-track {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: min(60vw, 480px);
  height: 32px;
  padding: 0 12px;
  border-radius: 16px;
  font-family: monospace;
  font-size: 13px;
  color: rgba(240, 235, 220, 0.85);
  text-decoration: none;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(240, 235, 220, 0.1);
  cursor: pointer;
  overflow: hidden;
  transition: background 150ms ease, border-color 150ms ease, color 150ms ease;
}
.cizi-mini-track[hidden] { display: none !important; }
.cizi-mini-track:hover {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(240, 235, 220, 0.25);
  color: rgba(240, 235, 220, 1);
}
.cizi-mini-icon { display: inline-flex; align-items: center; flex-shrink: 0; opacity: 0.85; }
.cizi-mini-icon svg { width: 16px; height: 16px; }
.cizi-mini-title, .cizi-mini-artist {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
.cizi-mini-title  { flex: 0 1 auto; font-weight: 600; }
.cizi-mini-artist { flex: 0 1 auto; opacity: 0.7; }
.cizi-mini-sep    { opacity: 0.4; flex-shrink: 0; }

/* --- Bouton-icône GIF (toggle BottomPlayer) --- */
.cizi-mini-toggle {
  width: 36px;
  height: 36px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: transform 150ms ease, background 150ms ease;
}
.cizi-mini-toggle[hidden] { display: none !important; }
.cizi-mini-toggle:hover  { transform: scale(1.1); background: rgba(255, 255, 255, 0.08); }
.cizi-mini-toggle:active { transform: scale(0.95); }
.cizi-mini-toggle img { width: 28px; height: 28px; display: block; object-fit: contain; }

/* --- Barre 4 boutons + zone tray (.cizi-mini-bar) --- */
.cizi-mini-bar {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-right: 4px;
  transition: opacity 0.3s ease;
}
.cizi-mini-bar[hidden] { display: none !important; }

/* Zone tray (titre + play/pause) : visible seulement si trayMode actif (voir ÉTATS). */
.cizi-mini-bar .cizi-tray-zone {
  display: none;
  align-items: center;
  gap: 6px;
  margin-right: 8px;
}
/* Boutons play/pause et titre : visibles seulement en mode étendu. */
.cizi-mini-bar .cizi-mini-pp,
.cizi-mini-bar .cizi-mini-title { display: none; }
.cizi-mini-bar.is-extended .cizi-mini-pp    { display: inline-flex; }
.cizi-mini-bar.is-extended .cizi-mini-title { display: inline-block; }
.cizi-mini-bar .cizi-mini-pp {
  width: 32px;
  height: 32px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  align-items: center;
  justify-content: center;
  color: rgba(240, 235, 220, 0.85);
  cursor: pointer;
  transition: background 150ms ease, color 150ms ease, transform 150ms ease;
}
.cizi-mini-bar .cizi-mini-pp:hover  { background: rgba(255, 255, 255, 0.08); color: rgba(240, 235, 220, 1); transform: scale(1.05); }
.cizi-mini-bar .cizi-mini-pp:active { transform: scale(0.95); }
.cizi-mini-bar .cizi-mini-pp svg    { width: 18px; height: 18px; display: block; }
.cizi-mini-bar .cizi-mini-title {
  max-width: 200px;
  padding: 0 6px;
  font-family: monospace;
  font-size: 13px;
  color: rgba(240, 235, 220, 0.85);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
  transition: color 150ms ease;
}
.cizi-mini-bar .cizi-mini-title:hover { color: rgba(240, 235, 220, 1); }

/* Les 4 boutons d'action (.cizi-act). */
.cizi-mini-bar .cizi-act {
  width: 32px;
  height: 32px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: rgba(240, 235, 220, 0.65);
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease, transform 0.12s ease;
}
.cizi-mini-bar .cizi-act:hover         { background: rgba(255, 255, 255, 0.08); color: rgba(240, 235, 220, 1); transform: scale(1.05); }
.cizi-mini-bar .cizi-act:active        { transform: scale(0.95); }
.cizi-mini-bar .cizi-act svg           { width: 18px; height: 18px; display: block; }
.cizi-mini-bar .cizi-act.is-active     { color: rgba(240, 235, 220, 1); }
.cizi-mini-bar .cizi-act-cast.is-active{ color: #60a5fa; } /* bleu Cast */

/* ════════════════════════════════════════════════════════════════════
   ÉTATS / COMPORTEMENT — ne pas refactorer sans tester la checklist.
   ════════════════════════════════════════════════════════════════════ */

/* --- Vision/Sono montés en iframe : le body ne réserve plus la topbar,
       et le <main> PHP est masqué (l'iframe occupe l'écran). --- */
body[data-categorie="vision"],
body[data-categorie="sono"] {
  padding-top: 0 !important;
  overflow: hidden;
}
body[data-categorie="vision"] main,
body[data-categorie="sono"] main {
  display: none !important;
}

/* --- FS vidéo Vision : topbar masquée, padding body annulé. Les onglets
       (désormais DANS la topbar) suivent d'office. --- */
body[data-vision-fs="true"] .topbar {
  opacity: 0 !important;
  pointer-events: none !important;
  transition: opacity 300ms ease !important;
}
body[data-vision-fs="true"] { padding-top: 0 !important; }

/* --- FS musique Vision --- */
body[data-music-fs="true"] { padding-top: 0 !important; }
/* Topbar hissée au-dessus de l'overlay FS Vision (sinon la mini-bar visible
   resterait cachée derrière). */
body[data-music-fs="true"] .topbar { z-index: 100 !important; }
/* Mini-bar EXTRAITE du stacking topbar (position:fixed) pour rester visible
   et cliquable malgré .topbar opacity géré côté React. Décalée à gauche du
   bouton [  ] de Vision (right:72px).
   NB : NEUTRALISÉE par le bloc fs-sync-topbar plus bas (conservée en trace,
   cf. avertissement de tête de section). */
body[data-music-fs="true"] .cizi-mini-bar {
  position: fixed !important;
  top: 6px !important;
  right: 72px !important;
  z-index: 2147483641 !important;
  display: inline-flex !important;
  opacity: 1 !important;
  pointer-events: auto !important;
  transition: opacity 0.3s ease !important;
}
/* Auto-hide souple en FS musique : la mini-bar disparaît quand les contrôles
   sont masqués (timer 10s côté Vision), réveillable au mouvement. */
body[data-music-fs="true"][data-music-controls-hidden="true"] .cizi-mini-bar {
  opacity: 0 !important;
  pointer-events: none !important;
  transition: opacity 0.4s ease !important;
}

/* --- Auto-hide générique de la mini-bar (hors FS) --- */
body[data-music-controls-hidden="true"] .cizi-mini-bar {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
}
/* Zone tray affichée si trayMode signalé. */
body[data-tray-active="true"] .cizi-mini-bar .cizi-tray-zone { display: inline-flex; }

/* --- fs-hide-when-clock-marker — 20260705 ---
   La loupe ne s'efface en plein écran QUE si l'horloge y est réellement
   affichée (préférence data-cz-clock ≠ "off", posée sur <html> par
   cizi-menu.js ; "auto" = visible en FS, "always" = visible partout, donc
   dans les deux cas elle occupe le centre en FS → on dégage).
   Horloge sur "Masquée" : le centre est vide, la loupe RESTE.
   Le fil d'Ariane /Catégorie/Section n'est PAS concerné : il vit à gauche,
   comme la catégorie qui n'a jamais été masquée en FS.
   Remplace les masquages FS inconditionnels historiques
   (#topbar-search-btn / #topbar-tabs / patch fs-search-class-fix), tous
   SUPPRIMÉS de ce fichier.
   Filet : attribut absent (script pas encore chargé) ⇒ :not() matche ⇒
   comportement historique (masqué) le temps d'un instant, sans flash gênant. */
html:not([data-cz-clock="off"]) body[data-music-fs="true"]  .topbar-search-btn,
html:not([data-cz-clock="off"]) body[data-vision-fs="true"] .topbar-search-btn,
html:fullscreen:not([data-cz-clock="off"]) .topbar-search-btn {
  display: none !important;
}
/* lecteur-portrait-marker (18/09/2026) — « il faut supprimer la loupe dans ce
   mode lecteur de musique en pleine page ».

   La règle ci-dessus ne masquait la loupe que SI l'horloge était affichée —
   elle réglait un conflit de place au centre de la barre, pas la question que
   tu poses. Ton horloge est sur « Masquée » (le défaut), donc la condition ne
   se déclenchait jamais et la loupe restait.

   En plein écran musique, elle part maintenant sans condition : chercher un
   film pendant qu'on écoute un morceau en grand n'a pas de sens, et la loupe
   reste à un geste — fermer le lecteur. Le plein écran VIDÉO garde la règle
   d'origine, qui n'a rien à voir et que tu n'as pas demandé de changer. */
body[data-music-fs="true"] .topbar-search-btn { display: none !important; }

/* --- Horloge : visible uniquement en plein écran sur PC (sinon .is-hidden
       gère la visibilité). Taille + contour renforcés en FS. --- */
@media (min-width: 721px) {
  :fullscreen .topbar-clock,
  :-webkit-full-screen .topbar-clock {
    display: block !important;
    font-size: 36px !important;
    background: transparent;
    padding: 0;
    text-shadow:
      -2px -2px 0 #000,  2px -2px 0 #000,
      -2px  2px 0 #000,  2px  2px 0 #000,
      0 0 8px rgba(0, 0, 0, 0.8);
  }
}

/* ════════════════════════ RESPONSIVE MOBILE (≤720px) ════════════════════════ */

@media (max-width: 720px) {
  .topbar { --topbar-pad-g: 10px; --topbar-pad-d: 16px; padding: 0 var(--topbar-pad-d) 0 var(--topbar-pad-g); gap: 6px; }
  .topbar-left { gap: 3px; min-width: 0; padding-bottom: 0; }
  .topbar-logo { height: 32px; }

  .topbar-cat { font-size: 22px; padding-right: 6px; }
  .topbar-cat::before { font-size: 22px; }
  .topbar-cat-label {
    font-size: 22px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 50vw;
  }
  /* Catégorie = icône seule sur mobile : on masque le label SI une icône est
     présente (sinon on garderait un "/" tout seul, ex. Accueil). */
  .topbar-cat:has(.topbar-cat-icon) .topbar-cat-label { display: none !important; }
  .topbar-cat-icon { height: 32px; }

  /* section-dropdown-marker — le segment /Section suit la catégorie mobile
     (22px via .topbar-cat) ; son label s'ellipse plus court, et il garde son
     TEXTE même en mode data-cz-cat=logo (pas d'icône ⇒ filet :has inopérant). */
  .topbar-cat--section .topbar-cat-label { max-width: 26vw; }

  /* etiquette-compacte-marker (08/09/2026) — BUG 20260908-234914 « change le
     texte "Accueil" par "ision" ».
     L'idée n'a de sens que dans CE bloc : c'est ici, et seulement ici, que la
     règle :has juste au-dessus masque le libellé de catégorie et ne laisse que
     le logo en V. Le fil se lit alors « Cizi.fr / [V]ision ». Au-delà de
     720 px le libellé « Vision » réapparaît, et « Vision / ision » n'aurait
     aucun sens : la variante longue reprend donc la main.
     Deux libellés sont rendus dans le DOM et c'est le CSS qui tranche, plutôt
     qu'un matchMedia en JS — une valeur qui diffère entre serveur et client
     casserait l'hydratation, on s'y est déjà brûlé. */
  .topbar-cat--section .etiq-longue { display: none; }
  .topbar-cat--section .etiq-compacte { display: inline; }
  .topbar-left .topbar-search-btn { margin-left: 2px; }

  .topbar-sublabel {
    font-size: 18px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 80px;
  }

  .topbar-search-btn { margin-left: 0; }
  .topbar-clock { font-size: 15px; }

  .topbar-dropdown { min-width: 240px; }
  .topbar-dropdown-label { font-size: 22px; }
  .topbar-dropdown-item--sub { font-size: 18px; }

  .cizi-mini-track { max-width: 55vw; padding: 0 10px; }
  .cizi-mini-artist, .cizi-mini-sep { display: none; }
  .cizi-mini-bar.is-extended .cizi-mini-title { display: none; }
}

@media (max-width: 480px) {
  /* Très petit écran : on cache la zone modules pour respirer. */
  .topbar-modules { display: none; }
  /* ══════════════════════════════════════════════════════════════════════
     lecteur-portrait-marker (18/09/2026)
     « il faut rajouter le logo musique et cast en mode portrait ; ils
       apparaissent bien en mode paysage. »

     VOILÀ POURQUOI ILS DISPARAISSAIENT, et ce n'était pas dans Vision : c'est
     la règle juste au-dessus. La zone des modules — le GIF du lecteur et le
     bouton Cast — est masquée sous 480 px de large pour laisser respirer la
     barre. Un téléphone DEBOUT fait 360 px : masqué. COUCHÉ il en fait 699 :
     affiché. D'où « ça marche en paysage ».

     Le raisonnement reste bon partout ailleurs : une barre de 360 px n'a pas
     la place de tout porter. Mais quand le lecteur de musique occupe l'écran
     entier, la barre n'a plus rien d'autre à montrer — et ce sont justement
     les deux commandes qu'on veut sous la main. L'exception est donc bornée à
     ce cas-là, et à lui seul. */
  body[data-music-fs="true"] .topbar-modules { display: flex !important; }
}

/* unified-shell-marker — 20260703
   TRANCHÉ : sur les pages Vision/Sono, le shell NE DESSINE PLUS sa topbar.
   La topbar React de Vision (iframe full-window) est la SEULE interface :
   plus de doublons, plus d'interception de clics, plus de hacks pointer-events.
   Inerte côté Vision (son body n'a jamais data-categorie).
   (.topbar-tabs : sélecteur défensif conservé pour le shell PHP.) */
/* bouton-categorie-bas-marker (13/09/2026) — #topbar-cat a quitté <header> :
   il faut le nommer partout où la barre était masquée, sinon il resterait seul
   à l'écran là où toute la barre disparaît. */
body[data-categorie="vision"] #topbar-cat,
body[data-categorie="sono"] #topbar-cat,
body[data-categorie="vision"] .topbar,
body[data-categorie="sono"] .topbar,
body[data-categorie="vision"] .topbar-tabs,
body[data-categorie="sono"] .topbar-tabs,
body[data-categorie="vision"] #topbar-dropdown,
body[data-categorie="sono"] #topbar-dropdown,
body[data-categorie="vision"] #topbar-search-overlay,
body[data-categorie="sono"] #topbar-search-overlay {
  display: none !important;
}

/* fs-sync-topbar-marker — 20260704
   FS musique : TOUTE la topbar suit la visibilité des commandes du lecteur
   (attribut data-music-controls-hidden posé par music-player.tsx, timer
   d'inactivité côté Vision, réveil au mouvement). L'extraction fixe de la
   mini-bar (règle historique de la section ÉTATS, top:6px/right:72px) est
   neutralisée : c'est elle qui superposait cast et zoom ; la mini-bar reste
   désormais à sa place dans la barre, qui apparaît/disparaît d'un seul bloc. */
body[data-music-fs="true"] .cizi-mini-bar {
  position: static !important;
  top: auto !important;
  right: auto !important;
  z-index: auto !important;
}
body[data-music-fs="true"] .topbar { transition: opacity .3s ease; }
body[data-music-fs="true"][data-music-controls-hidden="true"] .topbar,
body[data-music-fs="true"][data-music-controls-hidden="true"] #topbar-cat {
  opacity: 0 !important;
  pointer-events: none !important;
}

/* video-hide-topbar-marker — 20260704 : vidéo en plein cadre → topbar Cizi
   masquée en permanence (le lecteur vidéo a sa propre barre intégrée).
   Classe posée par topbar.tsx selon le store media-player. Les onglets étant
   dans la topbar depuis le layout v3, le sélecteur .topbar-tabs a giclé. */
.topbar.cz-video-hide {
  display: none !important;
}
/* bouton-categorie-bas-marker — le pendant pour le bouton de catégorie, devenu
   un voisin de la barre : une vidéo en plein cadre ne doit pas garder une
   pastille flottante dans le coin.

   lecteur-devant-marker (13/09/2026) — BUG 20260913-211309.
   CETTE RÈGLE MARCHAIT PAR CHANCE. `~` ne voit que les frères qui SUIVENT :
   elle ne fonctionne que parce que #topbar-cat est écrit après la barre dans
   le gabarit. Sa jumelle pour la couronne (plus bas) ne marchait pas du tout,
   la couronne étant ajoutée au <body> par JavaScript AVANT que la barre
   n'existe. On cesse donc de faire dépendre le masquage de l'ordre du DOM :
   `:has()` interroge le document entier, et il est déjà employé dans cette
   feuille (body:not(:has(#topbar-cat))), donc rien de neuf à demander au
   navigateur.
   Le second sélecteur couvre l'instant où topbar.tsx n'a pas encore posé
   cz-video-hide mais où le document porte déjà data-vision-fs. */
body:has(.topbar.cz-video-hide) #topbar-cat,
body[data-vision-fs="true"] #topbar-cat { display: none !important; }

/* ════════════════════════════════════════════════════════════════════
   CHECKLIST DE NON-RÉGRESSION (à re-vérifier après déploiement) :
   1. PC large : [burger][logo][/Catégorie][/Section ▾][🔍] à gauche,
      ⛶ collé au bord droit, PLUS de ligne 2 sous la barre.
   2. Mobile : icône catégorie seule (label masqué), segment /Section en 22px
      ellipsé court, pas de "/" orphelin sur Accueil.
   3. Dropdowns : catégories (icône 64px, courante encadrée) ET section
      (Accueil/Films/Séries texte, courante encadrée), alignés sous leur
      bouton, un seul ouvert à la fois.
   4. Section (Vision browse only, PAS sur /sono ni settings/login) :
      libellé courant sur le bouton, PLUS de Sono dans les choix.
   5. FS vidéo (data-vision-fs) : topbar entière disparaît.
   6. FS musique (data-music-fs) : barre entière suit l'auto-hide 10s
      (fs-sync), horloge FS visible/grosse sur PC si pref auto/toujours.
   7. fs-hide-when-clock : horloge "Masquée" → la loupe RESTE en FS ;
      horloge "Auto" ou "Toujours" → elle s'efface en FS (centre occupé).
      Le fil d'Ariane, lui, reste dans tous les cas.
   8. Pages PHP : loupe toujours au centre (compat v2) tant qu'entete.php
      n'est pas passé au layout v3 — cliquable, recentrée si horloge masquée.
   ════════════════════════════════════════════════════════════════════ */

/* dropdown-above-content-marker — 20260705 : les dropdowns de la barre
   (catégories ET section) passaient SOUS le contenu de page (héros du hub,
   titres avec bouton play…) : leur z-index 11 historique datait d'avant la
   surenchère de stacking de Vision. Hissés juste au-dessus de la topbar
   (2147483640), comme le panneau du burger (INT_MAX). L'overlay recherche
   suit, même famille de calques. */
.topbar-dropdown { z-index: 2147483641; }
.topbar-search-overlay { z-index: 2147483641; }

/* topbar-align-tune-marker — 20260705 : resserrage + alignement du groupe
   gauche. · Burger épaissi façon cartoon (stroke CSS > attribut SVG) avec le
   contour noir du logo. · La zone gauche passe de flex-end (bas du logo = bas
   des labels, obsolète) à un CENTRAGE vertical de tous les segments ; le
   rattrapage optique du texte reste --cizi-cat-fine-tune dans :root.
   · Espaces internes du fil d'Ariane purgés : marges du "/" (::before) et de
   l'icône supprimées, le gap flex (6px) suffit ; padding du bouton resserré.
   S'applique aussi aux pages PHP (mêmes classes). */
.topbar-burger {
  color: rgba(240, 235, 220, 1);
  opacity: 1;
  padding: 4px;
}
.topbar-burger svg {
  width: 26px;
  height: 26px;
  stroke-width: 3.6;
  filter: var(--cizi-img-outline);
}
.topbar-left { align-items: center; gap: 3px; }
.topbar-cat { padding: 0 6px 0 2px; margin-left: -2px; }
.topbar-cat::before { margin-right: 0; }
.topbar-cat-icon { top: 0; margin-left: 0; }
.topbar-cat--section { margin-left: -2px; }


/* separateur-section-marker (06/09/2026) — BUG 20260906-112201
   « j'aimerais le logo > à la place de / entre vision et accueil;films;séries ».
   Le premier séparateur (entre le logo Cizi.fr et la catégorie) reste un « / » :
   il marque le passage du site à sa section. Le second devient un chevron, qui
   dit mieux la descente d'un niveau dans le fil d'Ariane.
   Le chevron « › » est plus fin qu'un « > » de clavier : on compense d'un poil
   de graisse et de taille pour qu'il pèse autant que le « / » voisin. */
.topbar-cat--section::before {
  content: '›';
  font-weight: 600;
  font-size: 34px;
  margin-right: 5px;
}
@media (max-width: 700px) {
  .topbar-cat--section::before { font-size: 25px; }
}

/* etiquette-compacte-marker (08/09/2026) — par défaut (écrans larges) c'est la
   variante longue qui s'affiche ; la bascule vit dans le @media (max-width:720px)
   plus haut. Défini ici, en fin de fichier, pour ne dépendre d'aucun ordre. */
.topbar-cat--section .etiq-longue { display: inline; }
.topbar-cat--section .etiq-compacte { display: none; }

/* ══════════════════════════════════════════════════════════════════════════
   marque-fusionnee-marker (11/09/2026) — BUG 20260911-220053
   « ce pourrait être un seul bouton », puis « le radical mais sans contour ».

   Ce qui a décidé la forme, ce sont les mesures faites à 360 px sur le groupe
   de gauche : actuel 246 px ; fusion seule 242 (le cadre reprend ce que
   l'écart rend) ; suppression du « / » 231 ; pictogramme à la place du mot
   188 ; les deux ensemble 173. Le poste lourd est le LOGOTYPE — 88 px à lui
   seul à 32 px de haut (cizi_mir.gif fait 400x145). D'où : un seul bouton,
   pas de cadre, pas de séparateur, et le mot qui cède au pictogramme quand
   l'écran est étroit.
   ══════════════════════════════════════════════════════════════════════════ */

/* Le bouton fusionné : c'est le logotype, rien autour. Il porte la classe
   .topbar-burger pour que la délégation de cizi-menu.js continue de le voir. */
.topbar-marque {
  display: inline-flex;
  align-items: center;
  height: 32px;
  padding: 0;
  margin: 0;
  border: 0;
  background: none;
  cursor: pointer;
  flex-shrink: 0;
  transition: filter .2s, transform .2s;
}
.topbar-marque img {
  height: 100%;
  width: auto;
  max-width: none;          /* cf. logo-cat-ecrase-marker : la preflight Tailwind
                               plafonne toutes les images dans l'iframe Vision */
  display: block;
  flex-shrink: 0;
}
.topbar-marque:hover {
  filter: drop-shadow(0 0 6px rgba(240, 235, 220, 0.6));
  transform: scale(1.05);
}

/* Plus de « / » devant la catégorie : il ne sépare plus rien maintenant que
   le logotype et le ☰ sont un seul objet. */
.topbar-cat::before { content: none; }
.topbar-left .topbar-cat { margin-left: 6px; }


/* ══════════════════════════════════════════════════════════════════════════
   barre-trois-zones-marker (12/09/2026) — BUG 20260912-140109
   « Barre cizi : le logo de catégorie doit être au centre. Les icônes admin
     collées à droite, le bouton plein écran reste à l'extrême droite. Dans
     les pages où y a l'icône loupe : la loupe doit être entre cizi et le
     logo catégorie. »

   La barre cesse d'être « tout à gauche + une horloge posée au centre » pour
   devenir trois zones réelles :
     gauche  : le logotype Cizi.fr (qui ouvre le menu) puis la loupe
     centre  : le bouton catégorie
     droite  : modules, icônes d'administration, zoom, plein écran

   POURQUOI flex:1 1 0 DES DEUX CÔTÉS plutôt qu'un centre en position absolue.
   À base 0 et croissance égale, les deux zones latérales reçoivent exactement
   la même largeur quel que soit leur contenu : le centre tombe donc au milieu
   de l'écran, tout en restant DANS le flux — un centre absolu recouvrirait
   ses voisins et volerait leurs clics.
   Limite assumée : si le contenu d'une zone latérale dépasse la moitié
   disponible, il empiète sur le centre. Mesuré à 360 px avant livraison
   (cf. banc d'essai du 12/09) : le pire cas est l'accueil, zone droite
   136 px contre 151 disponibles.

   L'HORLOGE. Elle occupait le strict centre ; elle s'y superposerait
   maintenant à la catégorie en plein écran. Elle est donc ANCRÉE À DROITE DU
   CENTRE, en position absolue par rapport à la zone centrale : elle ne coûte
   aucune place, donc la catégorie reste exactement au milieu qu'elle soit
   affichée ou non — c'est ce qui évite la boucle « je me masque, donc la
   place change, donc je me ré-affiche ».
   Le garde-fou de collision vit dans scripts/topbar.js (réécrit le 12/09 :
   il mesurait le bord des ZONES, qui sont désormais larges et vides ; il
   mesure maintenant le bord du CONTENU).
   ══════════════════════════════════════════════════════════════════════════ */

.topbar-left {
  flex: 1 1 0;
  justify-content: flex-start;
  min-width: 0;
}

.topbar-center {
  position: relative;   /* repère de l'horloge (cf. ci-dessous) */
  inset: auto;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  pointer-events: auto; /* annule le centre « décoratif » de la v2 */
  z-index: 1;
  /* Recentrage sur l'ECRAN : les deux zones latérales se partagent la boîte de
     CONTENU, dont le milieu est décalé de (pad gauche − pad droit)/2. Cette
     marge rend ce décalage — vérifié au banc d'essai, écart ramené à 0. */
  margin-left: calc(var(--topbar-pad-d) - var(--topbar-pad-g));
}

.topbar-right {
  flex: 1 1 0;
  margin-left: 0;       /* le flex:1 remplace le margin-left:auto historique */
}

/* La catégorie n'est plus collée au logotype : les rattrapages de gauche
   (topbar-align-tune-marker, marque-fusionnee-marker) n'ont plus d'objet. */
.topbar-center .topbar-cat { margin-left: 0; }

/* Horloge : hors flux, juste à droite du bloc centré. */
.topbar-center .topbar-clock {
  position: absolute;
  left: 100%;
  top: 50%;
  transform: translateY(-50%);
  margin-left: 12px;
  white-space: nowrap;
}

@media (max-width: 720px) {
  .topbar-center .topbar-clock { margin-left: 8px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   bouton-categorie-bas-marker (13/09/2026)
   « Même sur PC il ne faut que le logo par défaut. Ce logo doit être 2 fois
     plus gros et en bas de page désormais. »

   Le bouton de catégorie quitte la barre. Il devient une pastille fixe en bas
   d'écran, réduite à son logo, deux fois plus grande qu'avant (32 → 64 px).

   AU MILIEU ET TOUT EN BAS — quatrième position, « encore plus bas ».
   La pastille se pose à 24 px du bord (28 au-delà de 1024 px), au ras de
   l'écran, avec ses trois boutons disposés en croix autour d'elle.

   ET ELLE SE SOULÈVE TOUTE SEULE quand le mini-lecteur Sono est là. Il fait
   64 px de haut (.cizi-bottom-player, components/music-player.tsx), collé au
   bord sur téléphone, en îlot à 8 px au-delà de 1024 : à 24 px, la pastille
   serait dessous. La règle body:has(.cizi-bottom-player) plus bas la remonte
   alors à 76 px — la position d'avant, celle qui laissait douze pixels de jeu.
   C'est du CSS seul : pas de script à tenir au courant, et sur les pages du
   shell, où ce lecteur n'existe pas, la pastille reste basse.

   #scroll-top, le bouton « remonter en haut », reste masqué : le geste ↑ et le
   bouton HAUT font la même chose, et il occupait le même centre d'écran. Une
   ligne à retirer en bas de ce bloc pour le revoir. Son pendant Vision est la
   constante MONTRER_HAUT de components/nav-buttons.tsx.

   Les deux autres bords, pour mémoire : #nav-back au milieu à gauche (58 px),
   et le gros bouton rouge de signalement en bas à droite (52 px à 18 px des
   bords) quand le réglage l'active — celui-là fait doublon avec le bouton
   « Signaler » du joystick, à toi de voir si tu veux le garder.
   ══════════════════════════════════════════════════════════════════════════ */

/* Les mesures du groupe, en un seul endroit : la pastille ET ses trois boutons
   s'en servent, c'est ce qui garantit qu'ils restent d'équerre quoi qu'on
   change. safe-area-inset-bottom : la barre gestuelle d'un iPhone mangerait
   sinon le bas du groupe. */
:root {
  --joy-bas: calc(24px + env(safe-area-inset-bottom, 0px));
  --joy-taille: 74px;     /* diamètre de la pastille : 64 de logo + 4 de
                             rembourrage + 1 de liseré, de chaque côté */
  /* couronne-marker (13/09/2026) — « collés harmonieusement, les contours
     arrondis pour coller au joystick ».
     Les trois boutons sont devenus des secteurs d'anneau autour de la pastille
     (cf. scripts/pastille-joystick.js). Ce diamètre est celui du GRAND arc,
     l'extérieur du groupe. Le rapport avec le bord intérieur est figé dans le
     SVG (46 sur 100), ce qui fait tomber le bord intérieur pile sur la
     pastille : 178 x 0,46 = 82 pour une pastille de 74, soit quatre pixels de
     jeu tout autour. Si tu changes --joy-taille, change ce nombre dans la même
     proportion. */
  --joy-couronne: 178px;
}
@media (min-width: 1024px) {
  :root { --joy-bas: calc(28px + env(safe-area-inset-bottom, 0px)); }
}
@media (max-width: 720px) {
  :root { --joy-taille: 64px; --joy-couronne: 156px; }
}
/* Le mini-lecteur Sono est là : tout le groupe monte au-dessus de lui.
   Attention au point à dégager : ce n'est plus le bas de la pastille mais le
   point le plus bas des secteurs latéraux, qui descendent sous elle. Il est à
   R_EXT x sin(OUVERTURE/2) sous le centre, soit 94/100 x sin 29° = 0,228 fois
   le diamètre de la couronne. D'où, pour laisser ~10 px de jour au-dessus d'un
   lecteur de 64 px :
     téléphone  0,228 x 156 = 36  →  64 + 10 + 36 - 32 = 78
     PC (8 px de marge sous le lecteur)  0,228 x 178 = 41  →  72 + 10 + 41 - 37 = 86
   Les valeurs d'origine (76 / 84) dataient des boutons carrés et laissaient les
   secteurs mordre de 6 à 8 px sur le lecteur. Si R_EXT ou OUVERTURE bougent
   dans le script, ces deux nombres bougent avec eux — le banc les remesure. */
body:has(.cizi-bottom-player) {
  --joy-bas: calc(78px + env(safe-area-inset-bottom, 0px));
}
@media (min-width: 1024px) {
  body:has(.cizi-bottom-player) {
    --joy-bas: calc(86px + env(safe-area-inset-bottom, 0px));
  }
}

#topbar-cat {
  position: fixed;
  left: 50%;
  bottom: var(--joy-bas);
  z-index: 2147483641;      /* même famille que les menus déroulants */
  margin: 0;
  /* contour-rond-marker (13/09/2026) — « il doit être arrondi et pas très
     large ». Le cadre passe du rectangle arrondi (rayon 20) au ROND, et le
     rembourrage de 6 à 4 px : il ne reste qu'un liseré fin autour du logo, au
     lieu d'un cadre. La pastille fait 72 px pour un logo de 64.
     Contrepartie à connaître : un rond découpe les quatre coins du logo, qui
     est carré. Sur des logos centrés ça ne se voit pas ; si l'un d'eux perd
     quelque chose, il faut agrandir le rembourrage pour que le carré tienne
     tout entier dans le cercle (il faudrait 14 px, et une pastille de 92). */
  padding: 4px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(240, 235, 220, 0.28);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.55);
  /* pastille-joystick-marker — sans ça, le navigateur prend le glissement
     vertical pour un défilement de page et nous coupe le geste. */
  touch-action: none;
  /* ET SURTOUT : le logo est une <img>, donc glissable nativement. Dès qu'on
     bougeait le pointeur bouton enfoncé, le navigateur démarrait un
     glisser-déposer d'image et nous envoyait un pointercancel — le geste
     mourait au premier pixel. Diagnostiqué au banc, pas deviné : le journal
     montrait « terminer type=pointercancel » juste après le premier
     pointermove. L'attribut draggable="false" fait le même travail pour
     Firefox, qui ne connaît pas -webkit-user-drag (cf. gabarits/entete.php et
     components/topbar.tsx). */
  -webkit-user-drag: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  /* translateX(-50%) fait le centrage ; il remplace aussi le rattrapage optique
     du fil d'Ariane, qui n'a plus lieu d'être hors de la barre. Le survol doit
     donc le répéter, sinon la pastille sauterait d'une demi-largeur. */
  transform: translateX(-50%);
  transition: background .2s, border-color .2s, transform .15s;
}
#topbar-cat:hover,
#topbar-cat.is-open {
  background: rgba(0, 0, 0, 0.75);
  border-color: rgba(240, 235, 220, 0.5);
  transform: translateX(-50%) scale(1.04);
}

/* Deux fois plus gros : 64 px contre 32. */
#topbar-cat .topbar-cat-icon {
  height: 64px;
  width: 64px;
  -webkit-user-drag: none;
  pointer-events: none;     /* le geste appartient au bouton, pas à l'image */
  top: 0;
  margin: 0;
  border-radius: 50%;
  object-fit: cover;
}

/* Le logo par défaut, À TOUTES LES LARGEURS — c'était déjà le cas sous 720 px,
   ça l'est maintenant aussi sur ordinateur. La garde :has reste : un bouton
   sans icône (une catégorie qui n'en a pas) garderait son mot, sans quoi il
   deviendrait invisible. */
#topbar-cat:has(.topbar-cat-icon) .topbar-cat-label { display: none !important; }

@media (max-width: 720px) {
  #topbar-cat { padding: 3px; }
  #topbar-cat .topbar-cat-icon { height: 56px; width: 56px; }
}

/* bouton-categorie-bas-marker — #scroll-top cède la place : cf. la note plus
   haut. UNE LIGNE à retirer pour le revoir, en sachant qu'il se superposera
   alors à la pastille. */
#scroll-top { display: none !important; }

/* ══════════════════════════════════════════════════════════════════════════
   joystick-encombrement-marker (13/09/2026) — BUG 20260913-135034
   « le scroll s'arrête trop tôt, je ne peux pas cliquer sur toutes les icônes
     à cause du joystick. À corriger et vérifier les autres pages. »

   Le groupe est en position:fixed : il ne participe pas au flux, donc la page
   s'arrête de défiler comme s'il n'existait pas, et les dernières icônes
   restent définitivement dessous. Il lui faut sa place réservée en bas de page.

   LA HAUTEUR QU'IL OCCUPE, calculée une fois ici plutôt que recopiée :
     bas du groupe          = --joy-bas
     centre de la pastille  = + --joy-taille / 2
     sommet de la couronne  = + R_EXT x rayon, soit 0,47 x --joy-couronne
                              (R_EXT vaut 94 sur 100, et --joy-couronne est un
                               diamètre : 94/100 / 2 = 0,47)
     et 16 px de jour pour ne pas coller la dernière icône au bouton.

   Posé sur <body>, qui est le conteneur qui défile côté shell (base.css :
   html{overflow:hidden}, body{overflow-y:auto}) — un padding sur un enfant
   n'aurait pas rallongé la zone de défilement. Conditionné à la même classe
   que le masquage du bouton retour : pas de couronne, pas de place réservée. */
:root {
  --joy-encombrement: calc(var(--joy-bas) + var(--joy-taille) / 2
                           + var(--joy-couronne) * 0.47 + 16px);
}
body.pj-couronne-visible { padding-bottom: var(--joy-encombrement); }

/* nav-back-doublon-marker (13/09/2026) — BUG 20260913-124112 : « le bouton
   retour doit disparaître, il fait doublon avec l'extension du joystick ».
   Conditionné à la classe posée par scripts/pastille-joystick.js, qui MESURE
   la couronne au lieu de la supposer : là où elle ne s'affiche pas — Vision et
   Sono côté shell, plein écran vidéo, page sans barre — le bouton retour reste,
   sinon ces pages-là n'auraient plus de retour du tout.
   Le sélecteur vaut pour les deux dépôts : le bouton de Vision porte le même
   id (components/nav-buttons.tsx), et Vision charge cette feuille. */
body.pj-couronne-visible #nav-back { display: none !important; }

/* ══════════════════════════════════════════════════════════════════════════
   nav-back-iframe-marker (20/09/2026) — BUG 20260920-002319 : « flèche de
   retour en double à supprimer sur Sono (peut-être sur Vision aussi) ».

   Il y en a bien DEUX, et elles portent le même id, dans deux documents
   différents : gabarits/pied.php en pose une pour toutes les catégories sauf
   l'accueil, et components/nav-buttons.tsx en pose une pour toutes les routes
   de Vision. Sur /vision et /sono, les deux documents sont à l'écran en même
   temps — le shell et son iframe — donc les deux boutons aussi.

   La règle du dessus ne les départageait pas : elle n'efface le bouton que là
   où la couronne du joystick est visible, et sur Vision/Sono elle ne l'est
   pas. C'est justement le seul endroit où le doublon pouvait survivre.

   POURQUOI C'EST CELLE DU SHELL QUI PART. Sur ces pages le <main> du shell est
   masqué : il n'y a rien derrière l'iframe, et `history.back()` sur la fenêtre
   extérieure ferait SORTIR de Vision au lieu de revenir à l'écran précédent.
   Celle de Vision, elle, appelle router.back() dans l'application qu'on est en
   train de regarder — c'est le geste attendu. Elle est aussi centrée sur
   l'iframe, donc au bon endroit.

   Le sélecteur ne peut pas se tromper de document : `data-categorie` est posé
   sur le <body> du shell et Vision ne l'écrit nulle part (vérifié : zéro
   occurrence dans tout le dépôt Vision). La même feuille, chargée dans
   l'iframe, n'y trouve donc rien à masquer.

   Pour revenir en arrière : supprimer ces deux lignes, et remettre `z-20` à la
   place de `z-[95]` dans components/nav-buttons.tsx. */
body[data-categorie="vision"] #nav-back,
body[data-categorie="sono"]   #nav-back { display: none !important; }

/* retours-cizi-marker (13/09/2026) — BUG 20260913-143642 : « il reste le bouton
   retour en trop à gauche de l'écran, et le menu maison et menu qui sont
   apparus ?! Pourquoi ça ?! »

   Parce que les deux boutons demandés la veille pour Vision se sont ajoutés
   PARTOUT, y compris là où le bouton retour est encore présent — trois pastilles
   empilées dans la même colonne. La règle devient exclusive : la colonne de
   gauche montre le bouton retour, OU les deux boutons Cizi, jamais les deux à
   la fois. Le critère est le même que ci-dessus, la visibilité réelle de la
   couronne : quand elle est là, le retour est déjà dans le joystick et la place
   est libre ; quand elle n'y est pas (Sono, plein écran vidéo), le bouton
   retour reprend sa place et les deux autres s'effacent. */
/* ══════════════════════════════════════════════════════════════════════════
   colonne-gauche-marker (13/09/2026) — BUG 20260913-141447 / -143642

   « Je voulais que ça occupe l'espace que j'ai délimité en jaune, le plus petit
     volume pour le retour à l'accueil Cizi ou F5. »
   « Je ne veux pas des boutons mais des menus dans le même style que les
     catégories, mais adapté. »

   Deux PANNEAUX dans la colonne de gauche, de la mi-hauteur au bas de l'écran —
   l'espace que le menu des catégories, qui s'ouvre à droite, laisse libre. Ils
   n'existent que pendant que ce menu est ouvert : même clic pour les appeler,
   même fermeture pour les remporter.

   Ils portent la classe .topbar-dropdown : fond, cadre, rayon, rangées et
   survol sont ceux du menu des catégories, sans les recopier. « Adapté » tient
   en deux points, écrits ci-dessous : la largeur (la colonne de gauche n'a pas
   les 320 px du menu principal) et le pictogramme (un SVG au trait, là où une
   catégorie a son logo carré — il n'existe pas de logo pour « rafraîchir »).

   Les deux volumes sont dictés : le petit en haut pour l'accueil et le
   rafraîchissement, le grand en bas pour le menu.
   ══════════════════════════════════════════════════════════════════════════ */
.pj-lat {
  position: fixed;
  left: 0;
  top: 0;
  bottom: 0;
  width: 0;
  z-index: 2147483641;   /* même famille que le menu des catégories */
  pointer-events: none;
}

/* On reprend .topbar-dropdown et on ne redresse que ce qui doit l'être : la
   position, la largeur, et le fait qu'il s'ouvre par une classe sur <body> et
   non par la sienne. */
.pj-lat-panneau.topbar-dropdown {
  position: fixed;
  left: 10px;
  top: auto;
  min-width: 0;
  width: 150px;
  max-width: calc(50vw - 16px);
  padding: 7px;
  gap: 2px;
  opacity: 0;
  transform: translateX(-12px);
  pointer-events: none;
  transition: opacity .18s ease, transform .18s ease;
}
/* La POSITION est posée en JS (placerLaterales) : elle dépend de celle du menu
   des catégories, que la feuille de style ne connaît pas. Ces valeurs ne sont
   qu'un repli, le temps du premier calcul. */
.pj-lat-panneau.pj-lat-cizi     { top: 50%; bottom: auto; }
.pj-lat-panneau.pj-lat-reglages { bottom: var(--joy-encombrement); top: auto; }

/* COLLÉS, sur trois côtés. Chaque bord qui en touche un autre perd son arrondi
   et sa bordure : le côté droit des deux panneaux contre le menu des
   catégories, et le bord qu'ils partagent entre eux. Le menu perd
   symétriquement l'arrondi de son bord gauche, mais seulement tant que les
   panneaux sont là — sans ça il resterait un croissant de fond entre les deux.
   Résultat : une seule surface en L, au lieu de trois boîtes voisines. */
.pj-lat-panneau.topbar-dropdown {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
  border-right: 0;
}
/* Le petit est AU-DESSUS du grand : il ne se referme pas en bas, et le grand
   ne se rouvre pas en haut. */
.pj-lat-panneau.pj-lat-cizi {
  border-bottom-left-radius: 0;
  border-bottom: 0;
}
.pj-lat-panneau.pj-lat-reglages {
  border-top-left-radius: 0;
}
body.pj-menu-ouvert #topbar-dropdown {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}

/* Le petit panneau est vraiment plus petit : rangées resserrées et texte d'un
   cran en dessous. Le grand garde la mesure du menu des catégories. */
.pj-lat-cizi .topbar-dropdown-item { padding: 5px 8px; font-size: 11px; gap: 7px; }
.pj-lat-cizi .pj-lat-ico { width: 20px; height: 20px; }
.pj-lat-cizi .pj-lat-ico svg { width: 16px; height: 16px; }
.pj-lat-cizi.topbar-dropdown { padding: 5px; }
.pj-lat-reglages .topbar-dropdown-item { padding: 12px 12px; font-size: 14px; gap: 12px; }

/* Sur téléphone, le menu des catégories prend presque toute la largeur
   (min-width 240 pour 360 d'écran) : les panneaux se resserrent pour rester
   dans la bande libre de gauche au lieu de mordre dessus.

   menu-structure-marker (13/09/2026) — CE BLOC NE SERVAIT À RIEN. Il était
   écrit AVANT la règle générale ci-dessus, à spécificité égale : c'est donc la
   générale qui gagnait, et les mesures « téléphone » n'ont jamais été
   appliquées. Remis dans le bon ordre, il reprend son rôle — et c'est une
   partie de la réponse au débordement de « Réglages » : sur 106 px de colonne,
   26 d'icône et 24 de marges intérieures ne laissaient que 30 px au mot. */
@media (max-width: 480px) {
  /* La largeur est calculée en JS ; on ne borne plus ici. */
  /* 7 px de marge et 6 de gouttière : mesuré au banc sur /carte, « Réglages »
     demande 66 px et la rangée ne lui en laissait que 62. */
  .pj-lat-reglages .topbar-dropdown-item { padding: 8px 7px; font-size: 12px; gap: 6px; }
  .pj-lat-reglages .pj-lat-ico { width: 20px; height: 20px; }
  .pj-lat-reglages .pj-lat-ico svg { width: 17px; height: 17px; }
}

/* menu-structure-marker (13/09/2026) — BUG 20260913-191732, point 3.
   Le libellé héritait de la taille du menu des catégories : 30 px, 22 sur
   téléphone, dans un panneau large de 106. « Réglages » sortait de son cadre de
   56 px et se posait sur le menu voisin. Il prend ici la taille de sa rangée
   (les deux valeurs sont celles des .topbar-dropdown-item de chaque panneau),
   et si un mot plus long arrivait un jour, il serait coupé proprement à
   l'intérieur plutôt que d'aller déborder chez le voisin. */
.pj-lat-panneau .topbar-dropdown-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pj-lat-reglages .topbar-dropdown-label { font-size: 15px; }
.pj-lat-cizi .topbar-dropdown-label { font-size: 13px; }
/* 14 et non 15 : sur /carte la colonne libre tombe à 123 px, et « Réglages » à
   15 px y passait à un pixel près — mesuré au banc, coupé. Après les règles
   ci-dessus et non dans le bloc téléphone plus haut : à spécificité égale,
   c'est l'ordre du fichier qui tranche, et c'est exactement le piège qui avait
   rendu ce bloc-là inopérant. */
@media (max-width: 480px) {
  .pj-lat-reglages .topbar-dropdown-label { font-size: 14px; }
}
@media (orientation: landscape) and (max-height: 480px) {
  .pj-lat-reglages .topbar-dropdown-label { font-size: 13px; }
  .pj-lat-cizi .topbar-dropdown-label { font-size: 11px; }
}

.pj-lat-panneau .topbar-dropdown-sep {
  margin: 0 0 2px;
  padding: 2px 9px 4px;
  border-top: 0;
}

/* Le pictogramme prend la place de l'image d'une catégorie : même position à
   gauche de la ligne (order:-1), taille réduite à celle d'un trait. */
.pj-lat-ico {
  order: -1;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  color: rgba(240, 235, 220, 0.85);
}
.pj-lat-ico svg { width: 20px; height: 20px; }
.pj-lat-panneau .topbar-dropdown-item:hover .pj-lat-ico { color: #111; }

/* Le menu est ouvert : les deux panneaux entrent. Le décalage de douze pixels
   fait lire l'apparition comme une venue plutôt que comme un allumage. */
body.pj-menu-ouvert .pj-lat-panneau.topbar-dropdown {
  opacity: 1;
  transform: translateX(0);
  pointer-events: auto;
}

/* Pas de couronne, pas de colonne : c'est le même objet. */
body:not(.pj-couronne-visible) .pj-lat { display: none !important; }

@media (orientation: landscape) and (max-height: 480px) {
  .pj-lat-panneau.topbar-dropdown { padding: 5px; }
  .pj-lat-cizi .topbar-dropdown-item { padding: 4px 8px; font-size: 10px; }
  .pj-lat-reglages .topbar-dropdown-item { padding: 7px 9px; font-size: 11px; }
}


/* ══════════════════════════════════════════════════════════════════════════
   fan-joystick-marker (18/09/2026) — L'ÉVENTAIL DE L'APPUI LONG
   « voilà l'idée graphique sur l'emplacement des logos et menus » : des
   pastilles RONDES posées sur un arc au-dessus du joystick.

   Il y avait ici un panneau rectangulaire de tuiles carrées. Il tenait, il
   était mesuré, mais il ne parlait pas le langage de ce qui l'entoure — la
   pastille est ronde, la couronne est un anneau, et un rectangle par-dessus
   ressemblait à un menu d'ordinateur tombé là. Surtout : sur un arc, chaque
   pastille est au bout d'une DIRECTION, donc à portée du même geste que les
   trois secteurs. C'est tout l'intérêt du mode joystick.

   TROIS NOMBRES, ET ILS VIVENT ICI. Le script les LIT plutôt que de les redire
   (voir placerTuiles), pour qu'il n'y ait qu'un endroit où les corriger.

     --joy-fan-r   rayon, du centre de la pastille au centre des ronds. Il doit
                   dépasser la couronne, qui monte à 0,47 x --joy-couronne, soit
                   84 px sur --joy-couronne: 178px. D'où 124 : 84 de couronne,
                   16 de respiration, 24 de demi-rond.
     --joy-fan-d   diamètre des ronds.
     (l'ouverture, 160° centrés sur le haut, est dans le script : elle se
      répartit sur le NOMBRE d'entrées, que la feuille de style ignore.)

   L'HÔTE EST UN POINT. Il se cale sur le centre de la pastille — mêmes
   variables que .pj-couronne, qui vise le même point — et n'a aucune taille :
   les left/top des ronds se lisent alors directement comme des écarts au
   centre, et le script n'a pas à compenser une largeur de conteneur.
   ══════════════════════════════════════════════════════════════════════════ */
/* fan-plus-pres-marker (18/09/2026) — « ces 5 menus doivent être un peu plus
   proches du logo catégorie ».

   LE RAYON N'EST PLUS UN NOMBRE, C'EST UNE FORMULE, et c'est ce qui le rend
   juste partout. Il se déduit de la couronne : bord extérieur de celle-ci
   (0,47 x --joy-couronne), plus un demi-rond, plus 4 px de jeu. Les ronds se
   posent donc TOUJOURS au ras de la couronne, quelle que soit la taille
   d'écran, au lieu d'être réglés à la main par palier — c'est comme ça que
   j'avais laissé un pixel de chevauchement en paysage, et 16 px de vide de
   trop sur grand écran.

   Seul le diamètre reste un choix par palier : c'est une question de doigt, pas
   de géométrie. */
:root {
  --joy-fan-d: 46px;
  --joy-fan-r: calc(var(--joy-couronne) * 0.47 + var(--joy-fan-d) / 2 + 4px);
}
@media (max-width: 380px) { :root { --joy-fan-d: 42px; } }
@media (orientation: landscape) and (max-height: 480px) { :root { --joy-fan-d: 40px; } }

.pj-fan {
  position: fixed;
  left: 50%;                 /* repli : la vraie valeur est posée en JS */
  top: 50%;
  width: 0;
  height: 0;
  z-index: 2147483642;       /* au-dessus du menu des catégories */
  pointer-events: none;
}
.pj-fan[hidden] { display: none !important; }

/* Une réglette invisible, dont la SEULE raison d'être est d'être mesurable.
   getComputedStyle sur une propriété personnalisée rend le texte tel qu'il est
   écrit — « calc(...) », que parseFloat ne sait pas lire. Une largeur, elle,
   est toujours rendue résolue en pixels. Le script lit donc celle-ci plutôt que
   la variable (voir placerTuiles). */
.pj-fan-mesure {
  position: absolute;
  left: 0; top: 0;
  width: var(--joy-fan-r);
  height: 0;
  visibility: hidden;
  pointer-events: none;
}

.pj-tuile {
  position: absolute;
  left: 0;
  top: 0;
  width: var(--joy-fan-d);
  height: var(--joy-fan-d);
  margin: 0;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.62);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(240, 235, 220, 0.28);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.55);
  color: rgba(240, 235, 220, 0.85);
  cursor: pointer;
  pointer-events: none;
  /* AU REPOS, TOUS AU CENTRE, sous la pastille : c'est de là qu'ils sortent.
     L'échelle à 0,3 plutôt qu'à 0 garde un repère de rendu et évite le saut de
     sous-pixel au démarrage de la transition. */
  transform: translate(-50%, -50%) scale(0.3);
  opacity: 0;
  transition: transform .22s cubic-bezier(.2, .9, .3, 1.2), opacity .16s ease,
              background .14s ease, border-color .14s ease, color .14s ease;
}
/* Le déploiement : les coordonnées sont déjà posées en JS (left/top), il ne
   reste qu'à rendre les ronds visibles et à leur pleine taille. */
.pj-fan.is-open .pj-tuile {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
  pointer-events: auto;
}

.pj-tuile-ico { display: block; width: calc(var(--joy-fan-d) * 0.46); height: calc(var(--joy-fan-d) * 0.46); }
.pj-tuile-ico svg { display: block; width: 100%; height: 100%; }

/* tuiles-geste-marker — LA PASTILLE SOUS LE POUCE. Même langage que le secteur
   visé de la couronne : elle se détache franchement, c'est elle qui dit ce qui
   va se produire au relâchement. */
.pj-fan.is-open .pj-tuile.is-vise {
  background: rgba(240, 235, 220, 0.94);
  border-color: #fff;
  color: #111;
  transform: translate(-50%, -50%) scale(1.18);
}
.pj-tuile:hover:not(:disabled),
.pj-tuile:focus-visible:not(:disabled) {
  background: rgba(240, 235, 220, 0.92);
  border-color: #fff;
  color: #111;
}

/* ÉTEINTE : une condition non remplie. Le rond garde sa place — un trou dans
   l'arc ferait bouger tous les autres d'une ouverture à l'autre. */
.pj-tuile-eteinte,
.pj-tuile:disabled { cursor: default; }
.pj-fan.is-open .pj-tuile-eteinte { opacity: .34; }

/* VIERGE : le contour pointillé dit qu'il y a une place, pas une panne. Elle
   s'allume faiblement au passage du pouce — elle existe, elle ne promet rien. */
.pj-tuile-vierge { border-style: dashed; }
.pj-fan.is-open .pj-tuile-vierge { opacity: .5; }
.pj-fan.is-open .pj-tuile-vierge.is-vise {
  opacity: .8;
  background: rgba(0, 0, 0, 0.62);
  border-color: rgba(240, 235, 220, 0.6);
  color: rgba(240, 235, 220, 0.85);
  transform: translate(-50%, -50%) scale(1.06);
}

/* LE NOM, à un seul endroit — au-dessus de l'arc. Un libellé sous chacun des
   six ronds aurait doublé la hauteur de l'éventail et rendu le tout illisible
   sur un téléphone. */
.pj-fan-nom {
  position: absolute;
  left: 0;
  top: 0;
  transform: translate(-50%, -50%);
  white-space: nowrap;
  font-size: 12px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: rgba(240, 235, 220, 0.75);
  background: rgba(0, 0, 0, 0.62);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(240, 235, 220, 0.18);
  border-radius: 999px;
  padding: 3px 10px;
  opacity: 0;
  transition: opacity .16s ease;
  pointer-events: none;
}
.pj-fan.is-open .pj-fan-nom { opacity: 1; }

/* Pendant que l'éventail est là, la couronne s'efface : ses gestes sont
   désactivés de toute façon, et deux anneaux concentriques allumés en même
   temps, c'est un objet qu'on ne lit plus. */
body.pj-tuiles-ouvert .pj-couronne { opacity: .16; }
.pj-couronne { transition: opacity .18s ease; }

/* Pas de couronne, pas d'éventail : il s'ouvre par la pastille, qui disparaît
   avec elle. */
body:not(.pj-couronne-visible) .pj-fan { display: none !important; }


/* ══════════════════════════════════════════════════════════════════════════
   fan-partager-marker (18/09/2026) — LE PANNEAU DE PARTAGE, EN REPLI
   « partager mènera à pouvoir partager sur WhatsApp, mail, texto etc… ou
     copier l'URL ».

   Sur téléphone, c'est la feuille de partage DU SYSTÈME qui s'ouvre
   (navigator.share) : elle a déjà WhatsApp, Mail et Messages, dans l'ordre de
   la personne et avec ses comptes. Ce panneau-ci ne sert que là où cette
   feuille n'existe pas — les ordinateurs, pour l'essentiel.

   Il reprend .topbar-dropdown et ses rangées, comme les deux panneaux de
   gauche : même fond, même cadre, mêmes lignes. Seule la position est à
   redresser, elle est posée en JS (centrée sur la pastille). */
.pj-partage.topbar-dropdown {
  position: fixed;
  left: 10px;                 /* repli : la vraie valeur est posée en JS */
  top: auto;
  min-width: 0;
  /* 232 et non 210 : à 210, « Copier le lien » se coupait en « Copier le l… ».
     Un libellé tronqué dans un menu de quatre entrées, c'est une entrée qu'on
     ne lit plus — relevé au banc à 360 px de large. */
  width: 232px;
  max-width: calc(100vw - 16px);
  /* Cinq rangées et un titre font environ 230 px : sur un téléphone couché
     (274 px de haut) il ne reste presque rien. On borne à la place réellement
     disponible plutôt que de déborder sous l'écran. */
  max-height: calc(100vh - 16px);
  overflow-y: auto;
  padding: 7px;
  gap: 2px;
  z-index: 2147483643;        /* au-dessus de l'éventail, qui l'a ouvert */
}
.pj-partage.topbar-dropdown.is-open { opacity: 1; transform: none; pointer-events: auto; }
.pj-partage .topbar-dropdown-item { padding: 9px 10px; font-size: 13px; gap: 10px; }
.pj-partage .topbar-dropdown-label {
  font-size: 13px;
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pj-partage .pj-lat-ico { width: 22px; height: 22px; }
.pj-partage .pj-lat-ico svg { width: 19px; height: 19px; }
.pj-partage .topbar-dropdown-item:hover .pj-lat-ico { color: #111; }


/* ══════════════════════════════════════════════════════════════════════════
   couronne-marker (13/09/2026) — LES TROIS SECTEURS AUTOUR DU JOYSTICK
   « collés harmonieusement, les contours arrondis pour coller au joystick :
     réfère-toi aux plus grands arcs de cercle. »

   Les boutons sont des SECTEURS D'ANNEAU dessinés en SVG autour de la pastille
   (scripts/pastille-joystick.js) : bord intérieur épousant son cercle, bord
   extérieur suivant le grand arc. Aucune combinaison de border-radius ne
   dessine une forme concave — d'où le SVG, qui donne aussi la zone cliquable
   exacte plutôt qu'un carré invisible débordant sur les voisins.

   La couronne se place SOUS la pastille dans l'empilement : c'est le cercle du
   logo qui doit rester net par-dessus la jointure.
   ══════════════════════════════════════════════════════════════════════════ */

.pj-couronne {
  position: fixed;
  left: 50%;
  /* Centrée sur le centre de la pastille : on part du bas du groupe, on monte
     d'un demi-diamètre, et le translate ramène le carré du SVG autour de ce
     point. */
  bottom: calc(var(--joy-bas) + var(--joy-taille) / 2);
  width: var(--joy-couronne);
  height: var(--joy-couronne);
  transform: translate(-50%, 50%);
  z-index: 2147483640;      /* juste SOUS la pastille */
  pointer-events: none;     /* seuls les secteurs sont cliquables */
  overflow: visible;
}

.pj-secteur {
  pointer-events: auto;
  cursor: pointer;
  outline: none;
  opacity: 1;
  transition: opacity .18s ease;
}

/* haut-au-besoin-marker (13/09/2026) — « le bouton retour en haut ne doit
   apparaître que quand nécessaire : après avoir scrollé la page ». Fondu plutôt
   que display:none : le secteur garde sa place, il n'y a pas de saut quand il
   revient, et le fondu dit bien qu'il vient d'apparaître. */
.pj-secteur.pj-inutile {
  opacity: 0;
  pointer-events: none;
}

.pj-secteur .pj-fond {
  fill: rgba(0, 0, 0, 0.55);
  stroke: rgba(240, 235, 220, 0.28);
  stroke-width: 1;
  /* Le trait garde son épaisseur réelle quelle que soit la taille du groupe :
     sans ça, il maigrirait sur téléphone et épaissirait sur grand écran. */
  vector-effect: non-scaling-stroke;
  transition: fill .15s ease, stroke .15s ease;
}

.pj-secteur .pj-icone {
  fill: none;
  stroke: rgba(240, 235, 220, 0.85);
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: stroke .15s ease;
}

.pj-secteur:hover .pj-fond,
.pj-secteur:focus-visible .pj-fond {
  fill: rgba(0, 0, 0, 0.82);
  stroke: rgba(240, 235, 220, 0.55);
}
.pj-secteur:hover .pj-icone,
.pj-secteur:focus-visible .pj-icone { stroke: #fff; }

/* Le secteur VISÉ par le geste : il se détache franchement, c'est lui qui dit
   ce qui va se produire au relâchement. */
.pj-secteur.is-vise .pj-fond {
  fill: rgba(240, 235, 220, 0.94);
  stroke: #fff;
}
.pj-secteur.is-vise .pj-icone { stroke: #111; }

/* La pastille pendant le geste : elle suit le doigt (transform posé en JS),
   donc pas de transition dessus — elle traînerait derrière. */
#topbar-cat.pj-actif {
  transition: background .2s, border-color .2s;
  border-color: rgba(240, 235, 220, 0.6);
  cursor: grabbing;
}

/* La couronne suit le sort de la pastille : pas de pastille, pas de couronne.
   Même liste de cas que pour #topbar-cat, plus la garde « la page n'a pas de
   barre du tout » (Vision pas encore monté, page sans topbar). */
/* lecteur-devant-marker (13/09/2026) — BUG 20260913-211309 et -211204.

   DEUX TROUS, et ils avaient la même conséquence : les secteurs « retour » et
   « signaler » restaient posés SUR la barre de commandes du lecteur, par-dessus
   Stop, −10 s, Lecture et +10 s.

   1. VIDÉO. La règle était écrite `.topbar.cz-video-hide ~ .pj-couronne`, et ce
      sélecteur ne pouvait jamais correspondre : `~` ne regarde que les frères
      SUIVANTS, or la couronne est ajoutée au <body> par
      scripts/pastille-joystick.js — donc AVANT que la barre n'existe dans le
      document. Relevé sur la fiche d'un film, lecteur ouvert : la couronne est
      le 18e enfant du <body>, la barre le 20e. La pastille disparaissait bien
      (sa règle à elle a l'ordre pour elle, par chance), la couronne restait.
      `:has()` ne dépend d'aucun ordre.

   2. MUSIQUE. Le plein écran musique n'a JAMAIS eu de règle à lui. La seule
      qui parlait de la couronne exigeait les DEUX attributs, dont
      data-music-controls-hidden — que music-player.tsx ne pose qu'après dix
      secondes d'inactivité. Autrement dit : tout le temps où l'on regarde le
      lecteur, la couronne est là. C'est l'inverse de ce qu'il faut.
      data-music-fs suffit désormais, seul.

   Les panneaux latéraux suivent sans règle supplémentaire : leur affichage est
   conditionné à body.pj-couronne-visible, une classe que le script pose
   d'après la visibilité RÉELLE de la couronne. Elle tombe avec elle, et le
   padding-bas de 145 px qu'elle réserve tombe aussi. */
body:not(:has(#topbar-cat)) .pj-couronne,
body[data-categorie="vision"] .pj-couronne,
body[data-categorie="sono"] .pj-couronne,
body:has(.topbar.cz-video-hide) .pj-couronne,
body[data-vision-fs="true"] .pj-couronne,
body[data-music-fs="true"] .pj-couronne,
body[data-music-fs="true"] #topbar-cat,
body[data-music-fs="true"] .pj-lat {
  display: none !important;
}
/* Gardée en trace : elle ne sert plus à rien depuis que la règle ci-dessus
   masque la couronne dès l'entrée en plein écran musique, mais elle documente
   ce qui était prévu à l'origine. */
body[data-music-fs="true"][data-music-controls-hidden="true"] .pj-couronne {
  opacity: 0 !important;
  pointer-events: none !important;
}


/* carte-dans-pastille-marker (13/09/2026) — BUG 20260913-123940.
   Le menu de la pastille accueille un second groupe sur les pages de cartes :
   les catégories, puis les cartes. Le séparateur porte le mot « Cartes » pour
   que la coupure se lise, plutôt qu'un simple trait qui laisserait croire à
   une liste unique. Même écriture que les séparateurs du menu des moteurs de
   recherche, pour ne pas inventer une deuxième façon de dire la même chose. */
.topbar-dropdown-sep {
  margin: 6px 0 2px;
  padding: 6px 14px 4px;
  border-top: 1px solid rgba(240, 235, 220, 0.14);
  font-family: monospace;
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(240, 235, 220, 0.42);
  pointer-events: none;
}
