/* Extrait de templates/index.html le 2026-08-31. Aucune variable de
   gabarit ici : le fichier est servi tel quel. */

  /* ═══════════════════════════════════════════════════════════════════
     Police. Inter 4.1, version VARIABLE (SIL OFL 1.1 ; le texte de la licence
     est servi a cote du fichier, dans /static/fonts/OFL-Inter.txt).

     Variable et non statique, et ce n'est pas un detail : les regles plus bas
     demandent des graisses intermediaires (520, 545, 570, 620, 640, 660, 680,
     720). Des fichiers statiques 400/500/600/700 les arrondiraient toutes au
     palier le plus proche et le reglage typographique serait perdu. Ici l'axe
     wght couvre 100 a 900 en continu : chaque valeur est rendue telle quelle.

     Le fichier est sous-ensemble au latin (197 Ko au lieu de 352). Un titre en
     alphabet non latin retombe donc sur la police du systeme — c'est assume :
     embarquer tous les alphabets couterait plusieurs mega-octets.
     ═══════════════════════════════════════════════════════════════════ */
  @font-face {
    font-family: 'Inter';
    src: url('/static/fonts/InterVariable-latin.woff2') format('woff2');
    font-weight: 100 900;   /* la PLAGE de l'axe, pas une graisse unique */
    font-style: normal;
    font-display: swap;     /* le texte s'affiche tout de suite, en systeme */
  }
  /* ══════════════════════════════════════════════════════════════════
     Jetons. Le chrome reste neutre et sombre : ce sont les affiches qui
     apportent la couleur. L'accent ambre ne sert qu'aux reperes (onglet
     actif, progression, focus, note) — jamais a de grands aplats.
     ══════════════════════════════════════════════════════════════════ */
  :root {
    --bg:         #08090c;
    --bg-soft:    #0e1116;
    --card:       #13171e;
    --card-hi:    #1b202a;
    --line:       #222834;
    --line-soft:  #191e27;
    --txt:        #f3f5f9;
    --txt-dim:    #98a1b2;
    --txt-faint:  #646d7e;
    --accent:     #ffb340;
    --ok:         #46c76a;
    --warn:       #e0a12c;
    --err:        #ff6257;
    --movie:      #b48bff;
    --tv:         #37dcc4;
    --radius:     14px;
    --radius-sm:  10px;
    /* Les courbes natives manquent de nerf : ces deux-la donnent la frappe. */
    --ease:       cubic-bezier(.23, 1, .32, 1);
    --ease-io:    cubic-bezier(.77, 0, .175, 1);
    --shadow:     0 10px 30px rgba(0,0,0,.45);
    --shadow-lg:  0 24px 64px rgba(0,0,0,.6);
    /* Marges de securite du materiel. La page est en viewport-fit=cover et la
       barre d'etat d'iOS est translucide : sans ces quatre valeurs, l'en-tete
       passe sous l'heure et l'encoche mange le bord gauche en paysage. */
    --safe-t:     env(safe-area-inset-top, 0px);
    --safe-b:     env(safe-area-inset-bottom, 0px);
    --safe-l:     env(safe-area-inset-left, 0px);
    --safe-r:     env(safe-area-inset-right, 0px);
    /* Gouttiere laterale de la page. Les rangees qui doivent filer d'un bord a
       l'autre s'en servent pour annuler puis reprendre le retrait. */
    /* Le carre du logo. Le glyphe est ajoure : c'est ce degrade qui remplit le
       triangle de lecture, d'ou la paire indissociable. Valeurs relevees sur
       branding/icone-source-1254.png. */
    --mark-grad:  linear-gradient(225deg, #ffd11a, #fd8f08 52%, #fd4409);
    --mark-creux: #0b0d10;
    --pad:        20px;
    /* Largeur du rail lateral (refonte du 2026-08-25). Une seule valeur :
       le rail s'y dimensionne et le contenu s'en decale d'autant. */
    --rail:       74px;
    /* Largeur d'une affiche. Les trois familles de listes — rangee, rangee
       16/9, grille — puisent ici : le mode televiseur redefinit ces trois
       jetons et retaille toute la page sans toucher une regle de plus. */
    --col:        160px;
    --col-wide:   272px;
    --col-min:    158px;
    /* Largeur de la colonne de contenu. En-tete, onglets et corps partagent la
       meme valeur : les trois doivent bouger ensemble sinon ils se desalignent. */
    --maxw:       1600px;
    --tabbar:     62px;   /* hauteur reelle de la barre du bas, sur mobile */
  }

  * { box-sizing: border-box; }
  html {
    -webkit-text-size-adjust: 100%;
    /* Les barres de navigation flottent au-dessus du contenu sans jamais lui
       ceder de place : l'en-tete est colle en haut, et sur mobile les onglets
       sont fixes en bas. Tout ce que le navigateur fait defiler pour l'amener
       « a l'ecran » — une carte atteinte a la telecommande, un ancrage, une
       rangee qu'on remonte — se garait donc DERRIERE elles : on croyait etre
       revenu au bon endroit et la ligne choisie etait a moitie cachee. Ces deux
       reserves reculent les bords du champ de defilement d'autant. Les hauteurs
       sont mesurees a l'execution (cf. mesurerBarres) : l'en-tete passe a deux
       lignes selon la largeur, gagne l'encoche du telephone, change avec le
       nombre d'onglets, et n'est meme plus collant en dessous de 760 px. */
    scroll-padding-top: calc(var(--head-h, 0px) + 14px);
    scroll-padding-bottom: calc(var(--tail-h, 0px) + 14px);
  }
  body {
    margin: 0; background: var(--bg); color: var(--txt);
    font: 15px/1.55 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    font-optical-sizing: auto;   /* Inter porte aussi un axe opsz (14-32) */
    -webkit-font-smoothing: antialiased;
    overflow-wrap: break-word;
  }
  /* Barre de defilement de la page masquee, comme celle de la fiche. Le
     defilement n'est pas touche : molette, doigt, telecommande et touches
     Page precedente/suivante fonctionnent a l'identique — seule la poignee
     n'est plus dessinee. Les deux selecteurs sont necessaires : le navigateur
     prend la barre du document sur html OU sur body selon le contexte. */
  html, body { scrollbar-width: none; -ms-overflow-style: none; }
  html::-webkit-scrollbar, body::-webkit-scrollbar { display: none; }
  a { color: inherit; }
  button, input, select { font: inherit; color: inherit; }
  .wrap {
    max-width: var(--maxw); margin: 0 auto;
    padding-left: calc(var(--pad) + var(--safe-l));
    padding-right: calc(var(--pad) + var(--safe-r));
  }
  .hide { display: none !important; }

  /* Un anneau de focus visible au clavier, invisible a la souris. */
  :focus-visible {
    outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px;
  }
  /* Sur une affiche, l'anneau generique coupait les angles au carre au ras de
     l'image. On l'ecarte et on lui donne le rayon de la carte : a trois metres,
     c'est ce trait qui dit ou l'on est. */
  .card:focus-visible {
    outline-width: 3px; outline-offset: 5px; border-radius: 15px;
  }

  /* ══ boutons ══ */
  .btn {
    background: var(--txt); color: #0a0c10; border: 0; border-radius: 10px;
    padding: 11px 18px; font-weight: 620; font-size: 14.5px; cursor: pointer;
    display: inline-flex; align-items: center; gap: 8px; justify-content: center;
    transition: transform .16s var(--ease), background-color .16s ease, opacity .16s ease;
  }
  /* Sans dimension explicite, un SVG s'etire a la taille du conteneur flex :
     minuscule sur un bouton etroit, enorme sur un bouton pleine largeur. */
  .btn svg { width: 16px; height: 16px; flex: none; }
  .btn:hover { background: #fff; }
  .btn:active { transform: scale(.97); }
  .btn:disabled { opacity: .45; cursor: default; transform: none; }
  .btn.ghost {
    background: rgba(255,255,255,.07); color: var(--txt);
    border: 1px solid var(--line); backdrop-filter: blur(8px);
  }
  .btn.ghost:hover { background: rgba(255,255,255,.12); }
  .btn.danger { background: #3a1b1b; color: #ffb0a8; border: 1px solid #5c2b2b; }
  .btn.danger:hover { background: #4a2222; }
  .btn.full { width: 100%; }
  /* Le code d'appairage se lit a trois metres, sur un televiseur : gros,
     espace, et en chiffres a largeur fixe pour qu'il ne danse pas quand il
     change. */
  .code-big {
    font: 700 clamp(38px, 7vw, 64px)/1.1 ui-monospace, "SF Mono", Menlo, monospace;
    letter-spacing: .18em; text-align: center; margin: 18px 0 14px;
    color: #fff; font-variant-numeric: tabular-nums;
  }
  .msg { font-size: 13px; margin-top: 12px; min-height: 18px; }
  .msg.err { color: var(--err); }
  .msg.ok { color: var(--ok); }

  /* ══════════════ connexion ══════════════ */
  /* Defilable : clavier ouvert ou telephone en paysage, la boite ne tient plus
     dans la hauteur — sans cela son haut se coupait sans recours. Le centrage
     passe par margin:auto sur la boite, qui lui ne rogne pas au debordement. */
  #login, #join {
    position: fixed; inset: 0; z-index: 100; display: flex; overflow-y: auto;
    align-items: flex-start; justify-content: center;
    padding: calc(22px + var(--safe-t)) calc(22px + var(--safe-r))
             calc(22px + var(--safe-b)) calc(22px + var(--safe-l));
    background:
      radial-gradient(900px 520px at 50% -8%, rgba(255,179,64,.10), transparent 70%),
      radial-gradient(700px 500px at 85% 105%, rgba(120,110,255,.09), transparent 70%),
      var(--bg);
  }
  .login-box {
    width: 100%; max-width: 380px; margin: auto; background: rgba(19,23,30,.85);
    border: 1px solid var(--line); border-radius: 20px; padding: 30px 28px 26px;
    box-shadow: var(--shadow-lg); backdrop-filter: blur(20px);
    animation: rise .45s var(--ease) both;
  }
  .login-box .mark {
    width: 42px; height: 42px; border-radius: 12px; display: grid; place-items: center;
    background: var(--mark-grad);
    color: var(--mark-creux); margin-bottom: 16px;
  }
  .login-box .mark svg { width: 23px; height: 23px; }
  .login-box h1 { font-size: 23px; margin: 0 0 5px; letter-spacing: -.025em; font-weight: 660; }
  .login-box p { color: var(--txt-dim); font-size: 13.5px; margin: 0 0 22px; }
  .field { margin-bottom: 13px; }
  .field label {
    display: block; font-size: 12px; color: var(--txt-dim); margin-bottom: 6px;
    font-weight: 550; letter-spacing: .01em;
  }
  .field input {
    width: 100%; background: var(--bg-soft); border: 1px solid var(--line);
    border-radius: 10px; padding: 11px 13px; outline: none;
    transition: border-color .16s ease, box-shadow .16s ease;
  }
  .field input:focus {
    border-color: var(--accent); box-shadow: 0 0 0 3px rgba(255,179,64,.14);
  }
  .field input:focus-visible { outline: none; }
  @keyframes rise { from { opacity: 0; transform: translateY(14px) scale(.985); } }

  /* ══════════════ en-tete ══════════════ */
  header {
    position: sticky; top: 0; z-index: 40;
    /* Le fond remonte sous la barre d'etat : en mode application, l'en-tete
       commence sous l'heure au lieu de se glisser derriere. */
    padding-top: var(--safe-t);
    background: rgba(8,9,12,.72); backdrop-filter: blur(18px) saturate(150%);
    border-bottom: 1px solid var(--line-soft);
  }
  .head {
    display: flex; align-items: center; gap: 16px;
    max-width: var(--maxw); margin: 0 auto;
    padding: 12px calc(var(--pad) + var(--safe-r)) 12px calc(var(--pad) + var(--safe-l));
  }
  .brand {
    font-size: 16.5px; font-weight: 700; letter-spacing: -.03em; white-space: nowrap;
    display: flex; align-items: center; gap: 9px;
  }
  .brand .mark {
    width: 27px; height: 27px; border-radius: 8px; display: grid; place-items: center;
    background: var(--mark-grad); color: var(--mark-creux); flex: none;
  }
  .brand .mark svg { width: 15px; height: 15px; }
  .brand span { color: var(--txt-dim); font-weight: 500; }

  .searchbox { position: relative; flex: 1; max-width: 480px; }
  .searchbox input {
    width: 100%; background: var(--bg-soft); border: 1px solid var(--line);
    border-radius: 11px; padding: 9px 34px 9px 36px; outline: none; font-size: 14px;
    transition: border-color .16s ease, box-shadow .16s ease, background-color .16s ease;
  }
  .searchbox input::placeholder { color: var(--txt-faint); }
  .searchbox input:focus {
    border-color: var(--accent); background: var(--card);
    box-shadow: 0 0 0 3px rgba(255,179,64,.12);
  }
  .searchbox input:focus-visible { outline: none; }
  .searchbox .ico {
    position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
    width: 15px; height: 15px; color: var(--txt-faint); pointer-events: none;
  }
  .searchbox .clear {
    position: absolute; right: 7px; top: 50%; transform: translateY(-50%);
    background: none; border: 0; color: var(--txt-faint); cursor: pointer;
    font-size: 17px; line-height: 1; padding: 5px; border-radius: 6px;
    transition: color .15s ease;
  }
  .searchbox .clear:hover { color: var(--txt); }

  .who {
    margin-left: auto; display: flex; align-items: center; gap: 10px;
    font-size: 13.5px; color: var(--txt-dim); white-space: nowrap;
  }
  .avatar {
    width: 30px; height: 30px; border-radius: 50%;
    background: linear-gradient(150deg, var(--card-hi), var(--card));
    display: grid; place-items: center; font-size: 12.5px; font-weight: 680;
    color: var(--accent); border: 1px solid var(--line); flex: none;
  }
  .who button {
    background: none; border: 0; color: var(--txt-faint); cursor: pointer;
    font-size: 12.5px; padding: 4px 6px; border-radius: 6px;
    transition: color .15s ease;
  }
  .who button:hover { color: var(--txt); }

  /* ══════════════ onglets ══════════════ */
  nav.tabs {
    display: flex; gap: 4px; max-width: var(--maxw); margin: 0 auto;
    padding: 0 calc(16px + var(--safe-r)) 0 calc(16px + var(--safe-l));
    overflow-x: auto; scrollbar-width: none;
  }
  nav.tabs::-webkit-scrollbar { display: none; }
  nav.tabs button {
    position: relative; background: none; border: 0;
    color: var(--txt-dim); padding: 10px 13px 12px; cursor: pointer;
    font-size: 14px; font-weight: 520; white-space: nowrap;
    display: flex; align-items: center; gap: 7px;
    transition: color .18s ease;
  }
  nav.tabs button svg { width: 17px; height: 17px; flex: none; }
  nav.tabs button:hover { color: var(--txt); }
  nav.tabs button.on { color: var(--txt); font-weight: 600; }
  /* Le trait actif est un pseudo-element : il glisse au lieu de clignoter. */
  nav.tabs button:after {
    content: ""; position: absolute; left: 12px; right: 12px; bottom: 0; height: 2px;
    background: var(--accent); border-radius: 2px 2px 0 0;
    transform: scaleX(0); transform-origin: center;
    transition: transform .22s var(--ease);
  }
  nav.tabs button.on:after { transform: scaleX(1); }
  nav.tabs button .pill {
    background: var(--card-hi); border-radius: 20px; padding: 1px 7px;
    font-size: 11px; color: var(--txt-dim);
  }
  nav.tabs .lbl { display: inline; }

  /* ══════════════ barre unique + rail lateral ══════════════
     Refonte du 2026-08-25, d'apres la maquette de ~/mediateque-streaming :
     les onglets remontent sur la ligne de la marque, la recherche et le compte
     filent a droite, et un rail d'icones longe le bord gauche.

     Tout tient dans « au-dela de 760 px » — deliberement. Le telephone garde
     sa barre du bas, qui reste ce qu'il y a de mieux sous le pouce, et le
     televiseur declare un canevas de 1280 px (cf. detecterTeleviseur) : il
     tombe donc du bon cote sans avoir besoin d'une regle a lui. */
  .rail { display: none; }

  @media (min-width: 761px) {
    /* Plus de colonne centree : la maquette va d'un bord a l'autre, et sur un
       televiseur c'est aussi ce qui evite deux bandes noires sur les cotes. */
    .head { max-width: none; gap: 20px; }
    /* Les onglets disparaissent du haut : le rail mene deja aux memes six
       sections, les garder ici etait une navigation en double. Ils restent
       dans le DOM — c'est le MEME element que la barre du bas en mobile, ou la
       media query 760 px les remet en position:fixed. Les masquer ici et les
       supprimer sont donc deux choses tres differentes.
       mesurerBarres() lit la hauteur calculee et non une valeur recopiee : la
       barre du haut rapetrissant, `--head-h` suit toute seule et le rail
       remonte d'autant. */
    nav.tabs { display: none; }

    /* La recherche prend la place liberee et se centre entre la marque et le
       compte. Marges automatiques des deux cotes plutot qu'un centrage absolu :
       aucun risque de chevaucher la marque quand le nom du compte s'allonge. */
    .searchbox { margin: 0 auto; flex: 0 1 560px; max-width: 560px; }
    .who { margin-left: 0; }

    /* Le rail commence sous l'en-tete. `--head-h` est publiee par
       mesurerBarres() a chaque rendu des onglets : aucune hauteur n'est
       recopiee ici, rien ne peut donc se desynchroniser. */
    .rail {
      position: fixed; z-index: 30;
      left: 0; top: var(--head-h, 60px); bottom: 0;
      width: calc(var(--rail) + var(--safe-l)); padding: 14px 0 14px var(--safe-l);
      display: flex; flex-direction: column; align-items: center; gap: 4px;
      background: var(--bg); border-right: 1px solid var(--line-soft);
    }
    .rail button {
      width: 46px; height: 46px; flex: none; border: 0; border-radius: 12px;
      background: none; color: var(--txt-faint); cursor: pointer;
      display: grid; place-items: center;
      transition: color .16s ease, background-color .16s ease;
    }
    .rail button svg { width: 21px; height: 21px; }
    .rail button:hover { color: var(--txt); background: var(--bg-soft); }
    .rail button.on { color: var(--accent); background: var(--bg-soft); }

    /* Le texte du bandeau demarre APRES la fleche de gauche, sinon celle-ci
       se pose sur la ligne de la note et de l'annee.
       `.spot .spot-in` et non `.spot-in` : ce bloc est ecrit AVANT la
       definition de .spot-in, et a specificite egale c'est la derniere ecrite
       qui l'emporte — le rembourrage y serait donc revenu a 34 px. */
    .spot .spot-in { padding-left: 72px; }

    .wrap { max-width: none; margin-left: calc(var(--rail) + var(--safe-l)); }
  }

  /* ══════════════ rangees ══════════════ */
  section { margin: 30px 0; }
  h2 {
    font-size: 15.5px; font-weight: 640; letter-spacing: -.015em; color: var(--txt);
    margin: 0 0 13px; display: flex; align-items: center; gap: 9px;
  }
  h2 .count {
    background: var(--card-hi); border: 1px solid var(--line); border-radius: 20px;
    padding: 1px 8px; font-size: 11.5px; font-weight: 550; color: var(--txt-dim);
  }
  /* ═══════════════════════════════════════════════════════════════════
     Titres de rangees. Ils etaient a 15,5px pour un corps a 15px : un demi
     pixel d'ecart, donc aucune hierarchie — l'oeil ne distinguait pas un
     titre de section d'une ligne de texte, et la page paraissait plate.
     Classe dediee et NON `h2` global : le gabarit compte 19 autres h2,
     dont ceux des fiches (.fiche-h2), qui doivent garder leur echelle.
     La graisse 680 n'est lisible que depuis Inter variable : avec les
     polices systeme d'avant, 640 et 680 tombaient tous deux sur le gras.
     ═══════════════════════════════════════════════════════════════════ */
  section > h2.rangee-titre {
    font-size: 19px; font-weight: 680; letter-spacing: -.022em;
    margin-bottom: 15px;
  }
  section > h2.rangee-titre .count { font-size: 12.5px; padding: 2px 9px; }
  /* Le conteneur d'une rangee, plus deux fleches posees sur ses bords.
     Classe `rangee` et non `rail` : `.rail`, c'est la barre d'icones a gauche.
     La collision de noms a fait heriter aux rangees le `position: fixed` de la
     barre laterale, et a la barre laterale le `position: relative` des rangees
     — la page entiere se decalait et le rail disparaissait.
     STRICTEMENT a l'interieur : left:0 et right:0, jamais de decalage negatif.
     La premiere version debordait de la gouttiere pour mordre sur le bord de
     page comme le fait Netflix — resultat, la page s'elargissait au-dela de
     l'ecran, un defilement horizontal apparaissait et toute la mise en page se
     decalait. Le gain esthetique ne valait pas ce risque. */
  .rangee { position: relative; }
  .row {
    display: grid; grid-auto-flow: column; grid-auto-columns: var(--col);
    gap: 16px; overflow-x: auto; padding: 4px 0 14px;
    scroll-snap-type: x proximity;
    /* Barre masquee : ce sont les fleches qui disent desormais qu'il reste du
       contenu sur le cote. */
    scrollbar-width: none; -ms-overflow-style: none;
  }
  .row::-webkit-scrollbar { display: none; }

  /* La fleche se centre sur L'AFFICHE, pas sur la rangee entiere : celle-ci
     comprend le titre et le sous-titre sous l'image, et une fleche centree
     dessus paraissait basse d'une trentaine de pixels. La hauteur vient de
     `--haut-affiche`, mesuree par majFleches() sur la premiere carte — les
     rangees d'affiches (2/3) et celles en 16/9 n'ont pas la meme, et aucune
     valeur ecrite en dur ne conviendrait aux deux. */
  .rangee-fleche {
    position: absolute; top: 4px; height: var(--haut-affiche, 180px);
    width: 58px; z-index: 3;
    display: none; place-items: center; cursor: pointer;
    border: 0; color: var(--txt); font: inherit; padding: 0;
    opacity: 0; transition: opacity .18s ease;
  }
  .rangee-fleche.prev { left: 0;  background: linear-gradient(to right, var(--bg) 38%, transparent); }
  .rangee-fleche.next { right: 0; background: linear-gradient(to left,  var(--bg) 38%, transparent); }
  .rangee-fleche svg {
    width: 30px; height: 30px; transition: transform .16s var(--ease);
    filter: drop-shadow(0 2px 6px rgba(0,0,0,.8));
  }
  /* Une rangee qui tient entierement a l'ecran n'a aucune fleche ; a chaque
     bout, celle qui ne mene nulle part s'efface. Classes posees par
     majFleches() au defilement. */
  .rangee.glissable .rangee-fleche { display: grid; }
  .rangee.au-debut .rangee-fleche.prev,
  .rangee.a-la-fin .rangee-fleche.next { display: none; }
  /* Au survol seulement : une fleche permanente sur chaque rangee ferait six
     commandes a l'ecran sur l'accueil. */
  @media (hover: hover) and (pointer: fine) {
    .rangee:hover .rangee-fleche { opacity: 1; }
    .rangee-fleche:hover svg { transform: scale(1.15); }
  }
  /* Sur televiseur, la telecommande fait deja defiler la rangee en deplacant le
     focus d'affiche en affiche : des fleches n'y serviraient a rien et
     s'intercaleraient dans ce parcours. */
  html.tv .rangee-fleche { display: none !important; }
  .row.wide { grid-auto-columns: var(--col-wide); }
  .row > * { scroll-snap-align: start; }
  .row::-webkit-scrollbar { height: 8px; }
  .row::-webkit-scrollbar-thumb { background: var(--line); border-radius: 4px; }
  .row::-webkit-scrollbar-track { background: transparent; }
  .grid {
    display: grid; gap: 20px 16px;
    grid-template-columns: repeat(auto-fill, minmax(var(--col-min), 1fr));
  }

  /* ══════════════ carte ══════════════ */
  .card {
    cursor: pointer; position: relative;
    animation: cardin .38s var(--ease) both;
  }
  @keyframes cardin { from { opacity: 0; transform: translateY(10px); } }
  /* Cascade courte : au-dela de la 12e carte tout arrive ensemble, sinon la
     page donne l'impression de ramer. */
  .card:nth-child(1)  { animation-delay: 0ms; }
  .card:nth-child(2)  { animation-delay: 26ms; }
  .card:nth-child(3)  { animation-delay: 52ms; }
  .card:nth-child(4)  { animation-delay: 78ms; }
  .card:nth-child(5)  { animation-delay: 104ms; }
  .card:nth-child(6)  { animation-delay: 130ms; }
  .card:nth-child(7)  { animation-delay: 156ms; }
  .card:nth-child(8)  { animation-delay: 182ms; }
  .card:nth-child(9)  { animation-delay: 208ms; }
  .card:nth-child(10) { animation-delay: 234ms; }
  .card:nth-child(11) { animation-delay: 260ms; }
  .card:nth-child(n+12) { animation-delay: 286ms; }

  .poster {
    position: relative; width: 100%; aspect-ratio: 2/3; border-radius: 12px;
    overflow: hidden; background: var(--card-hi);
    box-shadow: 0 2px 10px rgba(0,0,0,.4);
    transition: transform .26s var(--ease), box-shadow .26s var(--ease);
  }
  .card.land .poster { aspect-ratio: 16/9; }
  .poster img {
    width: 100%; height: 100%; object-fit: cover; display: block;
    transition: transform .5s var(--ease);
  }
  /* ═══════════════════════════════════════════════════════════════════
     Halo tire de l'affiche. `--lueur` reste transparent tant que personne
     n'a survole la carte : la page au repos ne change pas d'un pixel, et
     la transition sur box-shadow, deja declaree sur .poster, s'en charge.

     Pourquoi cet effet et pas un fond colore : le principe pose en tete de
     ce fichier veut que le chrome reste neutre et que ce soient LES
     AFFICHES qui apportent la couleur. Ici chaque film colore la page a son
     tour, avec sa propre teinte, et tout redevient noir des qu'on s'en va.

     Le focus a la meme regle que le survol : a la telecommande il n'y a pas
     de curseur, et sans cette ligne le boitier du salon n'aurait jamais le
     halo. Elle est HORS du bloc @media (hover: hover) pour cette raison.
     ═══════════════════════════════════════════════════════════════════ */
  .card { --lueur: transparent; }
  .card:focus-visible .poster {
    box-shadow: var(--shadow), 0 16px 46px -12px var(--lueur);
  }

  .poster:after {
    content: ""; position: absolute; inset: 0; border-radius: 12px;
    box-shadow: inset 0 0 0 1px rgba(255,255,255,.07); pointer-events: none;
  }
  .poster .ph {
    position: absolute; inset: 0; display: grid; place-items: center;
    color: var(--txt-faint); font-size: 26px;
  }
  .card:active .poster { transform: scale(.985); }
  @media (hover: hover) and (pointer: fine) {
    .card:hover .poster {
      transform: translateY(-5px);
      box-shadow: var(--shadow), 0 16px 46px -12px var(--lueur);
    }
    .card:hover .poster img { transform: scale(1.07); }
    .card:hover h3 { color: #fff; }
  }

  .bar { position: absolute; left: 0; right: 0; bottom: 0; height: 3px; background: rgba(0,0,0,.6); }
  .bar i { display: block; height: 100%; background: var(--accent); }
  .badge {
    position: absolute; top: 7px; left: 7px; background: rgba(8,10,15,.78);
    border-radius: 6px; padding: 2px 7px; font-size: 10.5px; font-weight: 640;
    letter-spacing: .01em; backdrop-filter: blur(6px);
    box-shadow: inset 0 0 0 1px rgba(255,255,255,.08);
  }
  .badge.tv { color: var(--tv); }
  .badge.movie { color: var(--movie); }
  .badge.right { left: auto; right: 7px; }
  .badge.ok { color: var(--ok); }
  .badge.warn { color: var(--warn); }
  .card h3 {
    font-size: 13.5px; font-weight: 545; margin: 9px 0 2px; line-height: 1.32;
    letter-spacing: -.005em; color: var(--txt);
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden; transition: color .16s ease;
    /* Deux lignes reservees : l'annee reste alignee d'une carte a l'autre,
       que le titre tienne sur une ligne ou deux. */
    min-height: 2.64em;
  }
  /* Une seule ligne : le titre d'un episode passait sur deux et creusait un
     trou entre le nom de la serie et lui, en decalant les cartes voisines. */
  .card small {
    display: block; color: var(--txt-faint); font-size: 11.5px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }

  /* Pastille de lecture : un vrai bouton blanc, pas un caractere. */
  .play-hint {
    position: absolute; inset: 0; display: grid; place-items: center;
    background: linear-gradient(to top, rgba(6,8,12,.72), rgba(6,8,12,.18));
    opacity: 0; transition: opacity .22s var(--ease);
  }
  .play-hint i {
    width: 46px; height: 46px; border-radius: 50%; background: rgba(255,255,255,.95);
    display: grid; place-items: center; color: #0a0c10;
    transform: scale(.86); transition: transform .26s var(--ease);
    box-shadow: 0 6px 20px rgba(0,0,0,.4);
  }
  .play-hint svg { width: 17px; height: 17px; margin-left: 2px; }
  @media (hover: hover) and (pointer: fine) {
    .card:hover .play-hint { opacity: 1; }
    .card:hover .play-hint i { transform: scale(1); }
  }

  /* ══════════════ pleins feux (accueil) ══════════════ */
  /* La banniere empile desormais jusqu'a cinq titres qui se relaient. Hauteur
     FIXE et non plus min-height : les diapositives sont en position absolue —
     seule facon de les faire se fondre l'une dans l'autre sans saut — et ne
     portent donc plus la hauteur du bloc. Chaque palier la redefinit. */
  .spot {
    position: relative; margin: 20px 0 34px; border-radius: 20px; overflow: hidden;
    height: 390px;
    background: var(--card); box-shadow: var(--shadow-lg);
    animation: rise .55s var(--ease) both;
  }
  .spot-piste { position: absolute; inset: 0; }
  /* visibility en plus de l'opacite : a 0 seule, la diapositive masquee garde
     ses boutons cliquables ET focalisables — a la telecommande, on se serait
     retrouve a naviguer dans un titre invisible. */
  .diapo {
    position: absolute; inset: 0; display: flex; align-items: flex-end;
    opacity: 0; visibility: hidden;
    transition: opacity .5s var(--ease), visibility .5s;
  }
  .diapo.on { opacity: 1; visibility: visible; }
  .spot .bgimg {
    position: absolute; inset: 0; background-size: cover; background-position: center 22%;
    transform: scale(1.03);
  }

  /* ═══════════════════════════════════════════════════════════════════
     Vie du bandeau. Deux mouvements, aucune couleur ajoutee : le chrome
     reste neutre, ce sont toujours les affiches qui colorent la page.

     1. LE TEXTE ARRIVE EN CASCADE. Le fondu de .diapo faisait apparaitre
        les cinq lignes exactement ensemble : le changement se lisait comme
        un remplacement, pas comme une arrivee. Chaque ligne entre donc avec
        un leger retard sur la precedente.

        On reutilise `rise`, defini plus haut, plutot que d'ecrire un second
        keyframe : meme vocabulaire de mouvement que le reste de la page.

        `both` retient l'etat de depart PENDANT le delai. Sans lui la ligne
        s'afficherait nette, puis sauterait en arriere pour s'animer.

        Les rangs sont volontairement souples : `.ov` n'existe que si le film
        a un synopsis, les suivants remontent alors d'un cran — c'est sans
        consequence, seuls les retards glissent.

     2. L'IMAGE RESPIRE. Un zoom tres lent, de 1.03 a 1.09. La duree (11 s)
        depasse exprès SPOT_DELAI (9 s) : la diapositive change toujours
        avant la fin, on ne voit donc jamais le mouvement s'arreter net.
        C'est une animation de transform pur, prise en charge par le
        compositeur — rien a repeindre, y compris sur le boitier du salon.

     Rien a prevoir pour prefers-reduced-motion : la regle globale sur *
     ramene deja toute duree d'animation a 0,01 ms.
     ═══════════════════════════════════════════════════════════════════ */
  .diapo.on .spot-in > *            { animation: rise .5s var(--ease) both; }
  .diapo.on .spot-in > :nth-child(1) { animation-delay: .06s; }
  .diapo.on .spot-in > :nth-child(2) { animation-delay: .13s; }
  .diapo.on .spot-in > :nth-child(3) { animation-delay: .20s; }
  .diapo.on .spot-in > :nth-child(4) { animation-delay: .27s; }
  .diapo.on .spot-in > :nth-child(5) { animation-delay: .34s; }

  .diapo.on .bgimg { animation: spot-souffle 11s linear both; }
  @keyframes spot-souffle { from { transform: scale(1.03); } to { transform: scale(1.09); } }
  .diapo:before {
    content: ""; position: absolute; inset: 0; z-index: 1;
    background:
      linear-gradient(to top, rgba(8,9,12,.97) 4%, rgba(8,9,12,.55) 46%, rgba(8,9,12,.18) 100%),
      linear-gradient(to right, rgba(8,9,12,.92) 0%, rgba(8,9,12,.35) 58%, transparent 100%);
  }
  .spot:after {
    content: ""; position: absolute; inset: 0; z-index: 3; pointer-events: none;
    border-radius: 20px; box-shadow: inset 0 0 0 1px rgba(255,255,255,.08);
  }
  .spot-in { position: relative; z-index: 2; padding: 34px 34px 30px; max-width: 620px; }
  .spot .kicker {
    font-size: 11px; font-weight: 700; letter-spacing: .13em; text-transform: uppercase;
    color: var(--accent); margin-bottom: 11px;
  }
  .spot h1 {
    font-size: clamp(28px, 4.4vw, 44px); line-height: 1.06; margin: 0 0 12px;
    letter-spacing: -.035em; font-weight: 700;
    text-shadow: 0 2px 22px rgba(0,0,0,.5);
  }
  .spot .meta {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    font-size: 13px; color: var(--txt-dim); margin-bottom: 13px;
  }
  .spot .ov {
    font-size: 14.5px; color: var(--txt-dim); line-height: 1.6; margin: 0 0 20px;
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
  }
  .spot .actions { margin: 0; }
  /* Au-dessus d'une affiche, un fond translucide prend sa teinte : le bouton
     secondaire virait au rouge sur un fond orange. On l'opacifie ici. */
  .spot .actions .btn.ghost { background: rgba(16,19,26,.78); }
  .spot .actions .btn.ghost:hover { background: rgba(27,32,42,.9); }

  /* Fleches et pastilles. Visibles en permanence mais discretes : sur un
     televiseur il n'y a pas de survol, des commandes qui n'apparaissent qu'au
     pointeur n'existeraient tout simplement pas. */
  .spot-fleche {
    position: absolute; top: 50%; transform: translateY(-50%); z-index: 4;
    width: 44px; height: 44px; border-radius: 50%; cursor: pointer;
    /* Assez contrastee pour se voir SUR LE NOIR : la fleche de gauche tombe
       du cote sombre du degrade, la ou un fond a 58 % et un trait a 10 %
       disparaissaient completement. */
    background: rgba(12,15,20,.78); border: 1px solid rgba(255,255,255,.22);
    color: var(--txt); display: grid; place-items: center; opacity: .8;
    transition: opacity .2s ease, background-color .16s ease;
  }
  .spot-fleche svg { width: 20px; height: 20px; }
  .spot:hover .spot-fleche { opacity: 1; }
  .spot-fleche:hover, .spot-fleche:focus { opacity: 1; background: rgba(22,26,34,.9); }
  .spot-fleche.prev { left: 14px; }
  .spot-fleche.next { right: 14px; }

  .spot-pastilles {
    position: absolute; z-index: 4; bottom: 14px; left: 50%;
    transform: translateX(-50%); display: flex; gap: 7px;
  }
  .spot-pastilles button {
    width: 8px; height: 8px; padding: 0; border: 0; border-radius: 5px;
    background: rgba(255,255,255,.32); cursor: pointer;
    transition: width .25s var(--ease), background-color .25s ease;
  }
  .spot-pastilles button.on { width: 22px; background: var(--accent); }
  .note {
    display: inline-flex; align-items: center; gap: 4px; color: var(--accent);
    font-weight: 620; font-variant-numeric: tabular-nums;
  }
  .note svg { width: 13px; height: 13px; }
  .dot { color: var(--txt-faint); }

  /* ══════════════ etats vides / squelettes ══════════════ */
  .empty {
    color: var(--txt-dim); font-size: 14px; background: var(--bg-soft);
    border: 1px dashed var(--line); border-radius: var(--radius);
    padding: 46px 26px; text-align: center;
  }
  .empty .ic {
    width: 46px; height: 46px; border-radius: 14px; margin: 0 auto 14px;
    display: grid; place-items: center; background: var(--card-hi);
    border: 1px solid var(--line); color: var(--txt-faint);
  }
  .empty .ic svg { width: 22px; height: 22px; }
  .empty b { display: block; color: var(--txt); font-size: 15.5px; font-weight: 600; margin-bottom: 5px; }
  .empty .btn { margin-top: 18px; }

  /* Squelettes : la page garde sa forme pendant le chargement, elle ne
     saute plus d'un bloc de texte a une grille. */
  .sk { background: var(--card); border-radius: 12px; position: relative; overflow: hidden; }
  .sk:after {
    content: ""; position: absolute; inset: 0;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,.045), transparent);
    transform: translateX(-100%);
    animation: shimmer 1.35s linear infinite;
  }
  @keyframes shimmer { to { transform: translateX(100%); } }
  .sk.poster-sk { aspect-ratio: 2/3; }
  .sk.line { height: 12px; margin-top: 9px; border-radius: 5px; }
  .sk.line.short { width: 55%; }
  .sk.spot-sk { height: 340px; border-radius: 20px; margin: 20px 0 34px; }
  .sk.h2 { height: 17px; width: 178px; border-radius: 6px; margin-bottom: 14px; }
  .loading { color: var(--txt-faint); font-size: 13px; padding: 20px 2px; }

  /* ══════════════ filtres ══════════════ */
  .filters { display: flex; gap: 9px; flex-wrap: wrap; align-items: center; margin-bottom: 20px; }
  /* max-width : un genre a rallonge (« Science-Fiction & Fantastique ») etirait
     le menu deroulant au-dela de l'ecran et faisait glisser toute la page. */
  .filters select, .filters .chip {
    background: var(--card); border: 1px solid var(--line); border-radius: 10px;
    padding: 8px 12px; font-size: 13px; cursor: pointer; outline: none;
    max-width: 100%; min-width: 0;
    transition: border-color .16s ease, background-color .16s ease, color .16s ease, transform .16s var(--ease);
  }
  .filters select:hover, .filters .chip:hover { background: var(--card-hi); }
  .filters .chip:active { transform: scale(.97); }
  .chip.on { border-color: var(--accent); color: var(--accent); background: rgba(255,179,64,.09); }

  /* ══════════════ fiche ══════════════ */
  /* C'est le panneau interieur qui defile, pas le calque. Un ancetre porteur de
     backdrop-filter devient le bloc de reference des position:fixed qu'il
     contient : quand le calque defilait lui-meme, la croix de fermeture partait
     avec le contenu et devenait introuvable sur un long resume. */
  #sheet {
    position: fixed; inset: 0; z-index: 60; overflow: hidden;
    background: rgba(4,5,8,.72); backdrop-filter: blur(8px);
    animation: fade .22s ease-out both;
  }
  @keyframes fade { from { opacity: 0; } }
  .sheet-in {
    max-width: 980px; margin: 0 auto; height: 100%;
    overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
    background: var(--bg); border-left: 1px solid var(--line-soft);
    border-right: 1px solid var(--line-soft);
    animation: sheetin .34s var(--ease) both;
    /* Barre de defilement masquee. Elle se collait au trait de bord droit de
       la fiche et coupait l'image large en deux — signale du salon le
       2026-08-26. Le defilement lui-meme n'est pas touche : molette, doigt et
       telecommande fonctionnent pareil, seule la poignee disparait. Meme
       procede que la barre d'onglets, qui se masque deja ainsi. */
    scrollbar-width: none;        /* Firefox */
    -ms-overflow-style: none;     /* anciens Edge */
  }
  .sheet-in::-webkit-scrollbar { display: none; }
  @keyframes sheetin { from { opacity: 0; transform: translateY(16px); } }
  .hero { position: relative; aspect-ratio: 16/7; background: var(--card-hi); }
  .hero img { width: 100%; height: 100%; object-fit: cover; }
  .hero:after {
    content: ""; position: absolute; inset: 0;
    background: linear-gradient(180deg, rgba(8,9,12,.1), var(--bg) 96%);
  }
  .close-x {
    position: fixed; top: calc(16px + var(--safe-t)); right: calc(16px + var(--safe-r));
    z-index: 70;
    width: 38px; height: 38px; border-radius: 50%; border: 1px solid var(--line);
    background: rgba(8,9,12,.8); color: var(--txt); font-size: 19px;
    cursor: pointer; line-height: 1; backdrop-filter: blur(10px);
    transition: transform .16s var(--ease), background-color .16s ease;
  }
  .close-x:hover { background: var(--card-hi); }
  .close-x:active { transform: scale(.94); }
  .sheet-body { padding: 0 26px 46px; margin-top: -76px; position: relative; z-index: 2; }
  /* Sans image large, le corps de fiche est le premier enfant : le retrait
     negatif hissait alors le titre au-dessus du panneau, hors de portee du
     defilement. C'est aussi le cas des etats « Chargement… » et erreur, qui
     etaient purement invisibles. On rend la place, croix comprise. */
  .sheet-in > .sheet-body:first-child {
    margin-top: 0; padding-top: calc(66px + var(--safe-t));
  }
  .sheet-body h1 { font-size: 30px; margin: 0 0 8px; letter-spacing: -.03em; font-weight: 680; }
  .meta { color: var(--txt-dim); font-size: 13.5px; display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 16px; align-items: center; }
  .meta b { color: var(--accent); font-weight: 620; }
  .overview { color: var(--txt-dim); font-size: 15px; line-height: 1.65; max-width: 68ch; margin-bottom: 22px; }
  .actions { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 24px; }
  .tags { display: flex; gap: 7px; flex-wrap: wrap; margin-bottom: 18px; }
  .tag {
    background: var(--card); border: 1px solid var(--line); border-radius: 20px;
    padding: 3px 11px; font-size: 12px; color: var(--txt-dim);
  }
  /* ══════════════ fiche detaillee ══════════════
     Reprise de la maquette du 2026-08-26 (~/echange/nouveau.png). La MEME mise
     en page sert la fiche locale et la fiche de demande : seul le bloc
     d'actions change. L'accent orange du site est conserve — la maquette de
     Codex etait en noir et blanc, ce qui aurait jure avec le reste.
     La barre du haut de la maquette n'est pas reprise : la fiche reste une
     surcouche, elle ferait doublon avec l'en-tete du site. */
  .fiche-titre {
    font-size: clamp(30px, 3.6vw, 52px); line-height: 1.02;
    letter-spacing: -.035em; font-weight: 720; margin: 0 0 14px;
  }
  .fiche-meta {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    font-size: 16px; color: var(--txt-dim); margin-bottom: 20px;
  }
  .fiche-meta .note { color: var(--accent); font-weight: 680; font-size: 17px; }
  /* La classification dans un cadre, comme le « TV-MA » de la maquette : sans
     bordure elle se confondait avec l'annee juste a cote. */
  .fiche-classif {
    border: 1px solid var(--line); border-radius: 8px; padding: 4px 10px;
    font-size: 13px; color: var(--txt-dim); letter-spacing: .02em;
  }
  .fiche-accroche {
    color: var(--soft, var(--txt-dim)); font-size: 17px; line-height: 1.6;
    max-width: 62ch; margin: 0 0 22px;
  }
  .fiche-saison {
    display: flex; align-items: center; gap: 11px;
    font-size: 15px; color: var(--txt-dim); margin-bottom: 26px;
  }
  .fiche-saison svg { width: 20px; height: 20px; flex: none; color: var(--txt-faint); }
  .fiche-saison b { color: var(--txt); font-weight: 660; }

  .fiche-h2 {
    font-size: 18px; font-weight: 660; letter-spacing: -.015em;
    color: var(--txt); margin: 32px 0 15px;
  }

  .fiche-genres { display: flex; flex-wrap: wrap; gap: 10px; }
  .fiche-genres span {
    display: inline-flex; align-items: center; gap: 9px;
    background: var(--bg-soft); border: 1px solid var(--line);
    border-radius: 14px; padding: 12px 17px; font-size: 14px; color: var(--txt-dim);
  }
  .fiche-genres svg { width: 17px; height: 17px; flex: none; color: var(--txt-faint); }

  /* Trois cases d'egale largeur, separees par un trait. En dessous de 640 px
     elles s'empilent et le trait passe au-dessus : trois colonnes de 90 px
     tronquaient « ~24 min / ep. » au milieu d'un mot. */
  .fiche-infos {
    margin-top: 18px; border: 1px solid var(--line); border-radius: 18px;
    background: var(--bg-soft); display: grid; grid-template-columns: repeat(3, 1fr);
    overflow: hidden;
  }
  .fiche-infos > div {
    padding: 20px; display: grid; grid-template-columns: auto 1fr;
    column-gap: 13px; align-items: center;
  }
  .fiche-infos > div + div { border-left: 1px solid var(--line); }
  .fiche-infos svg { grid-row: 1 / 3; width: 24px; height: 24px; color: var(--txt-faint); }
  .fiche-infos small { color: var(--txt-faint); font-size: 12.5px; }
  .fiche-infos b { font-size: 15px; font-weight: 620; color: var(--txt); }
  @media (max-width: 640px) {
    .fiche-infos { grid-template-columns: 1fr; }
    .fiche-infos > div + div { border-left: 0; border-top: 1px solid var(--line); }
  }

  .fiche-apropos { color: var(--txt-dim); font-size: 15.5px; line-height: 1.7; max-width: 72ch; }

  /* Le bloc de demande, encadre comme dans la maquette : il ne se confond plus
     avec le reste de la fiche et l'on voit d'un coup ce qu'on va reclamer. */
  .fiche-demande {
    margin-top: 26px; padding: 20px; border: 1px solid var(--line);
    border-radius: 18px; background: var(--bg-soft);
  }
  .fiche-demande .fiche-h2 { margin: 0 0 4px; font-size: 17px; }
  .fiche-demande .sous { color: var(--txt-faint); font-size: 13.5px; margin: 0 0 16px; }

  /* Choix des saisons a demander. Meme pastille que les filtres de la
     bibliotheque, mais leur mise en forme est portee par `.filters` : on la
     redit ici plutot que d'elargir un selecteur qui sert ailleurs. L'etat
     actif, lui, vient de la regle globale `.chip.on`. */
  .saisons { display: flex; gap: 8px; flex-wrap: wrap; margin: 2px 0 16px; }
  .saisons .chip {
    background: var(--bg-soft); border: 1px solid var(--line); color: var(--txt-dim);
    border-radius: 10px; padding: 7px 12px; font-size: 13px; cursor: pointer;
    transition: background-color .15s ease, border-color .15s ease, color .15s ease;
  }
  .saisons .chip:hover { background: var(--card-hi); color: var(--txt); }
  /* Une saison deja la, demandee ou en cours reste VISIBLE mais inerte : la
     masquer ferait croire que la serie n'a que trois saisons. */
  .saisons .chip.inerte {
    opacity: .55; cursor: default; background: none;
    border-style: dashed;
  }
  .saisons .chip.inerte:hover { background: none; color: var(--txt-dim); }
  .saisons .chip i {
    font-style: normal; font-size: 11px; color: var(--txt-faint);
    margin-left: 4px;
  }
  /* `.chip.on` (global, plus haut) et `.saisons .chip` ont la meme specificite :
     a egalite, c'est la derniere ecrite qui gagne, et l'etat actif disparaissait
     donc sous la mise en forme. On le redit ici, un cran plus specifique. */
  .saisons .chip.on {
    border-color: var(--accent); color: var(--accent);
    background: rgba(255,179,64,.09);
  }

  .eplist { display: flex; flex-direction: column; gap: 9px; }
  .ep {
    display: flex; gap: 13px; background: var(--card); border: 1px solid var(--line);
    border-radius: 12px; padding: 11px; cursor: pointer; align-items: center;
    transition: background-color .16s ease, border-color .16s ease, transform .16s var(--ease);
  }
  .ep:hover { border-color: var(--line); background: var(--card-hi); }
  .ep:active { transform: scale(.995); }
  .ep .thumb { width: 116px; aspect-ratio: 16/9; border-radius: 8px; object-fit: cover; background: var(--card-hi); flex: none; }
  .ep .no { font-size: 12px; color: var(--txt-faint); }
  .ep h4 { margin: 0 0 3px; font-size: 14px; font-weight: 570; }
  .ep p { margin: 0; font-size: 12.5px; color: var(--txt-faint); line-height: 1.5;
          display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
  .ep .done { color: var(--ok); font-size: 15px; margin-left: auto; flex: none; }

  /* ══════════════ lecteur ══════════════
     L'image occupe tout l'ecran. Les commandes natives restent en bas (elles
     savent gerer plein ecran, PiP et le tactile) ; notre barre a nous se pose
     en haut, la ou elles ne vont jamais, et s'efface quand on ne bouge plus. */
  #player { position: fixed; inset: 0; z-index: 90; background: #000; }
  #player video { width: 100%; height: 100%; background: #000; display: block; }

  /* ═══════════════════════════════════════════════════════════════════
     Barre de transport : Plyr 3.8.4 (MIT, licence dans /static/plyr/).
     Il ne prend QUE lecture, progression, temps et volume. Qualite, piste
     audio, sous-titres, plein ecran et fermeture restent a .pchrome — son
     menu de qualite travaille sur les niveaux hls.js alors que le notre
     passe par la re-signature serveur, les deux ne parlent pas de la meme
     chose. Sur televiseur Plyr n'est pas monte du tout : les controles
     natifs restent, ce sont eux que la telecommande sait piloter.
     ═══════════════════════════════════════════════════════════════════ */

  /* ---- Panneau de mesure (touche « d ») ---------------------------------
     Meme instrument que celui de l'application Android (`Statistiques.java`),
     memes lignes, meme vocabulaire : on doit pouvoir comparer un salon et un
     PC sans traduire. Il ne s'affiche JAMAIS tout seul.

     `pointer-events:none` : il flotte au-dessus de la video, il ne doit pas
     voler un clic destine au lecteur. */
  .pstats {
    position: absolute; top: 18px; left: 18px; z-index: 40;
    margin: 0; padding: 12px 16px;
    font: 12px/1.6 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    color: #fff; background: rgba(0,0,0,.78);
    border: 1px solid rgba(255,255,255,.18); border-radius: 8px;
    white-space: pre; pointer-events: none;
    text-shadow: 0 1px 2px rgba(0,0,0,.9);
  }
  .pstats[hidden] { display: none; }
  @media (max-width: 640px) { .pstats { font-size: 10px; top: 8px; left: 8px; } }
  #player .plyr, #player .plyr__video-wrapper { width: 100%; height: 100%; }
  #player .plyr video { width: 100%; height: 100%; }
  #player .plyr {
    --plyr-color-main:                var(--accent);
    --plyr-video-control-color:       var(--txt);
    --plyr-video-control-color-hover: #fff;
    --plyr-video-controls-background: linear-gradient(to top,
        rgba(4,6,10,.92), rgba(4,6,10,.5) 60%, transparent);
    --plyr-range-thumb-background:    var(--accent);
    --plyr-range-track-height:        5px;
    --plyr-font-family:               inherit;
    --plyr-control-spacing:           12px;
  }
  /* Le bandeau de Plyr est colle en bas : sans ce retrait il passe sous la
     barre gestuelle des telephones, exactement comme .pchrome en haut. */
  #player .plyr__controls {
    padding-bottom: calc(var(--plyr-control-spacing) + var(--safe-b));
    padding-left:   calc(var(--plyr-control-spacing) + var(--safe-l));
    padding-right:  calc(var(--plyr-control-spacing) + var(--safe-r));
  }

  .pchrome {
    position: absolute; left: 0; right: 0; top: 0; z-index: 2;
    padding: calc(14px + var(--safe-t)) calc(16px + var(--safe-r))
             calc(14px + 34px) calc(16px + var(--safe-l));
    background: linear-gradient(to bottom, rgba(4,6,10,.88), rgba(4,6,10,.45) 55%, transparent);
    display: flex; align-items: flex-start; gap: 14px;
    opacity: 0; transform: translateY(-8px); pointer-events: none;
    transition: opacity .25s var(--ease), transform .25s var(--ease);
  }
  .pchrome.on { opacity: 1; transform: none; pointer-events: auto; }
  .pchrome .t {
    font-size: 16px; font-weight: 620; letter-spacing: -.02em; min-width: 0;
    text-shadow: 0 1px 12px rgba(0,0,0,.6);
  }
  /* Deux lignes au plus : coince entre les pastilles, un titre d'episode long
     partait sur trois ou quatre lignes et couvrait le haut de l'image. */
  .pchrome .t small {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden; color: rgba(255,255,255,.66); font-weight: 400;
    font-size: 12px; margin-top: 2px; letter-spacing: 0;
  }
  .pacts { margin-left: auto; display: flex; align-items: center; gap: 8px; flex: none; }

  /* Pastilles en verre a la place des <select> du systeme, qui juraient. */
  .ppill {
    display: inline-flex; align-items: center; gap: 7px;
    background: rgba(16,19,26,.72); border: 1px solid rgba(255,255,255,.14);
    color: var(--txt); border-radius: 10px; padding: 8px 12px; cursor: pointer;
    font-size: 13px; font-weight: 520; backdrop-filter: blur(14px);
    transition: background-color .16s ease, transform .16s var(--ease), border-color .16s ease;
    white-space: nowrap;
  }
  .ppill svg { width: 16px; height: 16px; flex: none; opacity: .85; }
  .ppill b { font-weight: 640; }
  .ppill:hover { background: rgba(32,38,50,.85); border-color: rgba(255,255,255,.24); }
  .ppill:active { transform: scale(.96); }
  .ppill.on { border-color: var(--accent); color: var(--accent); }
  .ppill.ico { padding: 8px; }

  /* Menu deroulant maison : liste lisible, choix courant marque. */
  .pmenu {
    position: absolute; top: calc(60px + var(--safe-t));
    right: calc(16px + var(--safe-r)); z-index: 4; min-width: 210px;
    max-width: min(360px, calc(100vw - 32px)); max-height: 60vh; overflow-y: auto;
    background: rgba(14,17,23,.94); border: 1px solid var(--line);
    border-radius: 14px; padding: 6px; box-shadow: var(--shadow-lg);
    backdrop-filter: blur(20px); animation: rise .2s var(--ease) both;
  }
  .pmenu .head {
    font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
    color: var(--txt-faint); padding: 8px 10px 6px;
  }
  .pmenu button {
    display: flex; align-items: center; gap: 9px; width: 100%; text-align: left;
    background: none; border: 0; color: var(--txt); border-radius: 9px;
    padding: 9px 10px; cursor: pointer; font-size: 13.5px; line-height: 1.35;
    transition: background-color .14s ease;
  }
  .pmenu button:hover { background: rgba(255,255,255,.07); }
  .pmenu button .ck { width: 15px; flex: none; color: var(--accent); }
  .pmenu button .ck svg { width: 15px; height: 15px; }
  .pmenu button.sel { color: var(--accent); font-weight: 600; }

  /* ═══════════ Sous-titres : dessines par nous ═══════════
     Le rendu natif du navigateur ne se regle pas : ni la taille, ni la
     hauteur. Et il tombait derriere la barre de transport — la deuxieme
     ligne passait sous la progression (constate le 2026-08-29).
     hls.js garde la piste en mode « hidden » : il la charge, il la decode,
     il annonce chaque changement, mais il laisse l'affichage a la page.
     Les deux reglages sont donc de simples variables CSS. */
  .psubs {
    position: absolute; left: 0; right: 0; z-index: 2;
    bottom: calc(var(--st-bas, 12%) + var(--safe-b));
    display: flex; justify-content: center;
    padding: 0 max(6%, calc(16px + var(--safe-l)));
    /* Le calque ne doit jamais intercepter un clic : sous lui il y a la
       video, et un double-clic dessus bascule en plein ecran. */
    pointer-events: none;
    font-size: 3.4vh;
    transition: bottom .18s var(--ease);
  }
  .psubs p {
    margin: 0; text-align: center; text-wrap: balance;
    font-size: var(--st-taille, 100%);
    line-height: 1.3; font-weight: 500; color: #fff;
    /* Une ombre portee plutot qu'un bandeau noir : le bandeau mange l'image
       a chaque replique, l'ombre ne se voit que la ou il faut. */
    text-shadow: 0 2px 8px rgba(0,0,0,.95), 0 0 4px rgba(0,0,0,.9),
                 0 1px 2px rgba(0,0,0,.9);
  }
  .psubs i, .psubs em { font-style: italic; }

  /* Reglages, sous la liste des pistes du menu. Des boutons, pas des
     curseurs : a la telecommande un curseur ne se vise pas. */
  .preglages { border-top: 1px solid var(--line); margin-top: 6px; padding-top: 4px; }
  .pregl {
    display: flex; align-items: center; gap: 6px; padding: 4px 10px 4px 10px;
    font-size: 13px; color: var(--txt-dim);
  }
  .pregl > span { flex: 1; }
  .pregl > b {
    min-width: 52px; text-align: center; color: var(--txt);
    font-size: 12.5px; font-weight: 600; font-variant-numeric: tabular-nums;
  }
  .pregl button {
    width: 30px; height: 28px; flex: none; padding: 0;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--card-hi); border: 1px solid var(--line);
    border-radius: 8px; color: var(--txt); cursor: pointer;
    font: inherit; font-size: 14px; line-height: 1;
    transition: background-color .14s ease, border-color .14s ease;
  }
  .pregl button:hover:not([disabled]) { background: rgba(255,255,255,.1); }
  .pregl button:focus-visible { outline: none; border-color: var(--accent); }
  .pregl button[disabled] { opacity: .35; cursor: default; }

  .pmenu .pajout .ck { font-size: 15px; font-weight: 700; color: var(--accent); }
  .pmenu .pajout em { margin-left: auto; font-style: normal;
                      color: var(--txt-faint); font-size: 11.5px; }
  .pmenu .pajout[disabled] { opacity: .5; cursor: progress; }
  .pattente { padding: 8px 10px; font-size: 12.5px; color: var(--txt-faint); }

  /* Attente de transcodage : sur un 4K, le premier segment se fait attendre.
     Un ecran noir muet laisse croire a une panne. */
  .pload {
    position: absolute; inset: 0; z-index: 3; display: grid; place-items: center;
    background: rgba(4,6,10,.72); backdrop-filter: blur(3px); text-align: center;
  }
  .pload .spin {
    width: 42px; height: 42px; margin: 0 auto 16px; border-radius: 50%;
    border: 2.5px solid rgba(255,255,255,.16); border-top-color: var(--accent);
    animation: turn .8s linear infinite;
  }
  @keyframes turn { to { transform: rotate(360deg); } }
  .pload p { margin: 0; font-size: 14.5px; font-weight: 550; }
  .pload small { display: block; color: var(--txt-dim); font-size: 12.5px; margin-top: 5px; }

  /* Retour visuel des raccourcis clavier : bref, centre, sans clic. */
  .pflash {
    position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);
    z-index: 3; background: rgba(4,6,10,.78); border: 1px solid rgba(255,255,255,.12);
    border-radius: 14px; padding: 14px 20px; font-size: 15px; font-weight: 600;
    backdrop-filter: blur(10px); pointer-events: none;
    display: flex; align-items: center; gap: 9px;
    animation: flash .7s var(--ease) forwards;
  }
  .pflash svg { width: 19px; height: 19px; }
  @keyframes flash {
    0% { opacity: 0; transform: translate(-50%,-50%) scale(.9); }
    18% { opacity: 1; transform: translate(-50%,-50%) scale(1); }
    70% { opacity: 1; }
    100% { opacity: 0; transform: translate(-50%,-50%) scale(1.04); }
  }

  /* Enchainement d'episode : carte en bas a droite, avec compte a rebours. */
  .pnext {
    position: absolute; right: calc(20px + var(--safe-r));
    bottom: calc(84px + var(--safe-b)); z-index: 4; width: 300px;
    max-width: calc(100vw - 40px - var(--safe-l) - var(--safe-r));
    background: rgba(14,17,23,.94); border: 1px solid var(--line); border-radius: 14px;
    padding: 15px; box-shadow: var(--shadow-lg); backdrop-filter: blur(18px);
    animation: rise .3s var(--ease) both;
  }
  .pnext .k {
    font-size: 10.5px; font-weight: 700; letter-spacing: .11em; text-transform: uppercase;
    color: var(--accent); margin-bottom: 7px;
  }
  .pnext h4 { margin: 0 0 4px; font-size: 15px; font-weight: 600; letter-spacing: -.01em; }
  .pnext p { margin: 0 0 13px; font-size: 12.5px; color: var(--txt-dim); }
  .pnext .actions { margin: 0; gap: 8px; }
  .pnext .btn { flex: 1; padding: 9px 12px; font-size: 13.5px; }

  .pstat { font-size: 11.5px; color: var(--txt-faint); }

  /* Grand ecran. Elargir la colonne ne suffit pas : a 158px de minimum la
     grille fabrique une dizaine de colonnes minuscules et la page parait plus
     vide qu'avant. On grossit donc les affiches en meme temps, et la banniere
     suit pour ne pas laisser sa moitie droite dans le noir. */
  @media (min-width: 1500px) {
    :root { --col-min: 196px; --col-wide: 320px; }
    .grid { gap: 24px 18px; }
    .spot { height: 420px; }
    .spot-in { max-width: 760px; padding: 40px 40px 36px; }
  }

  /* ══════════════ televiseur ══════════════
     Le boitier du salon (Google TV) rend la page dans une fenetre de 960 px de
     large puis l'etale sur l'ecran entier : un pixel CSS en vaut deux ou quatre
     a l'arrivee, et les affiches doublent de taille. Aucune media query ne peut
     l'attraper — de son point de vue la fenetre est celle d'une petite tablette,
     largeur qui ne declenche ni le palier 1500 px ni celui de 760 px. D'ou la
     classe posee par l'user-agent (cf. detecterTeleviseur), et --tv-echelle :
     un seul chiffre a bouger si le prochain salon a une autre densite.
     La specificite de html.tv (0,1,1) passe devant les :root des media queries,
     donc ce bloc gagne quelle que soit la largeur annoncee par le boitier. */
  html.tv {
    /* Depuis que le canevas passe a 1280 px (cf. detecterTeleviseur), tout est
       deja rendu a 75 % : les affiches n'ont plus a etre rapetissees une
       seconde fois. Le facteur reste la pour un reglage fin au salon — 1
       garde exactement la taille physique qu'avaient les affiches du temps ou
       le canevas faisait 960 px et l'echelle .72. */
    --tv-echelle: 1;
    --col:      calc(160px * var(--tv-echelle));
    --col-wide: calc(272px * var(--tv-echelle));
    --col-min:  calc(158px * var(--tv-echelle));
  }
  /* Les affiches ayant retrouve leur largeur de base, les titres n'ont plus
     besoin d'etre rentres : ils reprennent 13,5 px, que le canevas ramene a
     environ 20 px reels — lisible a trois metres. */
  /* Les rangees filent d'un bord a l'autre, comme sur mobile : retranchee a
     la gouttiere de 20 px, la carte du bord etait coupee net et il restait
     une bande noire jusqu'au bord de l'ecran — signale du salon le
     2026-08-25 comme « une barre noire qui cache la moitie ». La marge
     negative rend cette gouttiere a la boite de defilement, le rembourrage
     la rend aux cartes : rien ne bouge a l'oeil, mais plus rien n'est
     tranche a 20 px du vide.
     Le scroll-padding des DEUX cotes : a la telecommande la selection se
     recentre et l'anneau de focus depasse de 8 px (outline-offset 5 + 3 de
     trait) ; sans marge de defilement il est rogne par la boite elle-meme
     des que la carte touche un bord. */
  html.tv .row {
    gap: 13px;
    margin-left: calc(-1 * (var(--pad) + var(--safe-l)));
    margin-right: calc(-1 * (var(--pad) + var(--safe-r)));
    padding-left: calc(var(--pad) + var(--safe-l));
    padding-right: calc(var(--pad) + var(--safe-r));
    scroll-padding-left: calc(var(--pad) + var(--safe-l) + 8px);
    scroll-padding-right: calc(var(--pad) + var(--safe-r) + 8px);
  }
  html.tv .grid { gap: 16px 13px; }
  html.tv .play-hint i { width: 34px; height: 34px; }
  html.tv .play-hint svg { width: 13px; height: 13px; }
  /* La banniere d'accueil est une affiche elle aussi : la laisser a 340 px de
     haut, c'est reprendre d'un cote ce qu'on vient de gagner de l'autre. */
  /* Assez haute pour tenir la scene, assez basse pour qu'une rangee
     d'affiches apparaisse sous le pli — c'est ce qui manquait au salon. */
  html.tv .spot { height: 330px; }
  html.tv .spot-in { max-width: 560px; }

  /* ── ce que le boitier ne peut pas se payer ──
     Un backdrop-filter fait relire au GPU tout ce qui se trouve DERRIERE
     l'element, a chaque image. Sur un ordinateur cela ne se voit pas ; ici la
     page est composee en 960 px mais peinte en 4K, donc un flou de 18 px porte
     en realite sur 72 pixels physiques, sur toute la largeur de l'ecran — et
     l'en-tete etant collant, l'operation recommence a chaque frame de
     defilement. La pastille « Film / Série » est pire encore : elle est petite,
     mais il y en a une ou deux par affiche, et le chargement automatique en
     accumule des centaines dans la page. On rend donc les deux fonds opaques :
     a trois metres, personne ne verra la difference entre un flou et un noir a
     92 %. (Le mobile fait deja ce troc pour l'en-tete, pour une autre raison —
     voir la media query 760 px.) */
  html.tv header { backdrop-filter: none; background: rgba(8,9,12,.97); }
  html.tv .badge { backdrop-filter: none; background: rgba(8,10,15,.92); }

  /* L'entree en fondu des affiches coutait une animation par carte. Une page
     de 48 d'un coup, declenchee en plein defilement par le chargement
     automatique, se voyait passer. */
  html.tv .card { animation: none; }

  @media (max-width: 760px) {
    .pchrome {
      padding: calc(12px + var(--safe-t)) calc(12px + var(--safe-r))
               calc(12px + 30px) calc(12px + var(--safe-l));
      gap: 10px;
    }
    .pchrome .t { font-size: 14px; }
    .ppill { padding: 10px 11px; font-size: 12.5px; }
    .ppill .lbl { display: none; }
    .pmenu {
      right: calc(12px + var(--safe-r)); left: calc(12px + var(--safe-l));
      top: calc(56px + var(--safe-t)); min-width: 0; max-width: none;
    }
    .pnext {
      right: calc(12px + var(--safe-r)); left: calc(12px + var(--safe-l));
      width: auto; bottom: calc(76px + var(--safe-b)); max-width: none;
    }
  }

  /* ══════════════ suivi ══════════════ */
  .tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(152px,1fr)); gap: 12px; margin-bottom: 10px; }
  .tile {
    background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
    padding: 15px 17px; transition: border-color .16s ease;
  }
  .tile b { display: block; font-size: 25px; font-weight: 680; letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
  .tile span { display: block; font-size: 12px; color: var(--txt-dim); margin-top: 3px; }
  .tile.hot b { color: var(--accent); }
  .tile.bad b { color: var(--warn); }
  .dl { display: flex; gap: 14px; background: var(--card); border: 1px solid var(--line);
        border-radius: var(--radius); padding: 14px; margin-bottom: 10px; }
  .dl .pv { width: 48px; aspect-ratio: 2/3; border-radius: 7px; object-fit: cover; background: var(--card-hi); flex: none; }
  .dl .main { flex: 1; min-width: 0; }
  .dl .tl { font-size: 14px; font-weight: 570; margin-bottom: 2px; }
  .dl .sb { font-size: 12px; color: var(--txt-faint); margin-bottom: 8px; }
  .prog { height: 6px; background: var(--bg-soft); border-radius: 3px; overflow: hidden; }
  .prog i { display: block; height: 100%; background: var(--accent); border-radius: 3px; transition: width .4s var(--ease); }
  .prog.warn i { background: var(--warn); }
  .dlmeta { display: flex; gap: 13px; font-size: 11.5px; color: var(--txt-dim); margin-top: 7px; flex-wrap: wrap; }
  .list-row {
    display: flex; align-items: center; gap: 12px; background: var(--card);
    border: 1px solid var(--line); border-radius: 12px; padding: 11px 13px; margin-bottom: 9px;
    transition: border-color .16s ease, background-color .16s ease;
  }
  .list-row:hover { background: var(--card-hi); }
  .list-row .main { flex: 1; min-width: 0; }
  .list-row .tl { font-size: 13.5px; }
  .list-row .sb { font-size: 12px; color: var(--txt-faint); }
  .mini { font-size: 12.5px; padding: 6px 11px; border-radius: 8px; }
  .disk { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); padding: 16px; }
  .disk .prog { height: 9px; margin: 10px 0 7px; }
  .banner {
    background: #241a12; border: 1px solid #4a2f24; color: #ffca7a;
    border-radius: 12px; padding: 12px 14px; font-size: 13.5px; margin-bottom: 14px;
  }
  .banner.err { background: #2a1616; border-color: #5a2a2a; color: #ffb4ad; }
  .banner.ok  { background: #13251a; border-color: #2b5638; color: #a6e6bd; }

  /* ══════════════ invitations ══════════════ */
  .invite-new {
    display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap;
    margin-bottom: 14px;
  }
  .invite-new .field { margin-bottom: 0; flex: 1 1 190px; }
  .invite-new select {
    background: var(--bg-soft); border: 1px solid var(--line); color: inherit;
    border-radius: 10px; padding: 11px 13px; font: inherit; outline: none;
  }
  /* Le lien tient rarement dans la largeur d'un telephone : une zone de texte
     selectionnable vaut mieux qu'une ligne tronquee qu'on ne peut pas copier. */
  .linkbox {
    display: block; width: 100%; margin: 10px 0 8px; font-size: 12.5px;
    background: rgba(0,0,0,.35); border: 1px solid var(--line); color: inherit;
    border-radius: 9px; padding: 9px 11px; font-family: ui-monospace, monospace;
  }
  .btn.small { padding: 7px 12px; font-size: 12.5px; }
  .pstat.ok { color: var(--ok, #7ddb9c); }
  .empty-note { font-size: 12.5px; line-height: 1.5; margin-top: 4px; }

  /* ══════════════ fiches de compte ══════════════ */
  .compte {
    background: var(--bg-soft); border: 1px solid var(--line);
    border-radius: 14px; padding: 14px 16px; margin-bottom: 10px;
  }
  .compte.bloque { opacity: .68; border-color: #5a2a2a; }
  .compte-tete {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 12px; flex-wrap: wrap;
  }
  .compte-btns { display: flex; gap: 7px; flex-wrap: wrap; }
  .compte-faits {
    margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line-soft);
    font-size: 13px; line-height: 1.6; display: grid; gap: 6px;
  }
  .compte-bloc { margin-top: 10px; font-size: 12.5px; }
  .compte-bloc > b {
    display: block; font-size: 11.5px; text-transform: uppercase;
    letter-spacing: .05em; color: var(--txt-faint); margin-bottom: 4px;
  }
  .mini { padding: 2px 0; }
  .tag {
    display: inline-block; font-size: 10.5px; font-weight: 650; padding: 2px 7px;
    border-radius: 999px; margin-left: 6px; vertical-align: middle;
    text-transform: uppercase; letter-spacing: .04em;
  }
  .tag.bad  { background: #3a1b1b; color: #ffb0a8; }
  .tag.warn { background: #3a2f14; color: #ffd479; }
  .tag.live { background: #13251a; color: #a6e6bd; }
  /* Pastilles du suivi — reprises telles quelles du tableau de bord : c'est
     la meme information, elle doit avoir la meme couleur des deux cotes. */
  .tag.movie { background: rgba(163,113,247,.16); color: #c9a5ff; }
  .tag.tv    { background: rgba(45,212,191,.14); color: #6fe3d3; }
  .tag.ok    { background: rgba(63,185,80,.15);  color: #6fd67d; }
  .tag.run   { background: rgba(79,156,249,.15); color: #86bdff; }
  .tag.wait  { background: rgba(210,153,34,.15); color: #e3bc5c; }
  .tag.err   { background: rgba(248,81,73,.15);  color: #ff9d96; }
  /* Grille de cartes du suivi — la meme que le tableau de bord : les demandes
     se lisent d'un coup d'oeil par leur affiche, pas ligne par ligne.
     `auto-fill` et non un nombre fixe de colonnes : la barre laterale et les
     petits ecrans changent la largeur utile, la grille s'y adapte seule. */
  .cartes { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 10px; }
  .carte {
    display: flex; gap: 12px; background: var(--card); border: 1px solid var(--line);
    border-radius: 12px; padding: 12px;
    /* Proprietes NOMMEES, jamais `all` : la carte contient une image dont on
       ne veut animer ni le chargement ni le decodage. */
    transition: border-color .16s ease, background-color .16s ease;
  }
  /* Derriere `hover: hover` : sur un ecran tactile, un appui declenche le
     survol et laisse la carte allumee apres le doigt. */
  @media (hover: hover) and (pointer: fine) {
    .carte:hover { border-color: var(--line-soft); background: var(--card-hi); }
  }
  .carte-corps { min-width: 0; flex: 1; }
  .carte-titre {
    font-weight: 600; font-size: 14px; line-height: 1.3; overflow: hidden;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  }
  .carte-annee { color: var(--txt-faint); font-weight: 400; }
  .carte-pastilles { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 7px; }
  .carte-note { font-size: 12px; color: var(--txt-dim); margin-top: 5px; line-height: 1.4; }
  /* Hauteur EXPLICITE, pas seulement un `aspect-ratio` : la carte est un
     conteneur flex, dont les enfants s'etirent par defaut (align-items:
     stretch). Sans hauteur fixe l'affiche suivait donc la hauteur de la carte
     — d'autant plus haute que le titre passe sur deux lignes — et se
     deformait. `flex: 0 0 54px` l'empeche aussi d'etre comprimee en largeur. */
  .carte .pv {
    width: 54px; height: 80px; flex: 0 0 54px; border-radius: 6px;
    object-fit: cover; background: var(--card-hi); border: 1px solid var(--line);
    align-self: flex-start;
  }
  /* Affiche absente : un carre vide ne dit rien, l'icone dit au moins le type. */
  .carte .pv.vide {
    display: flex; align-items: center; justify-content: center;
    font-size: 22px; color: var(--txt-faint);
  }
  @media (max-width: 620px) { .cartes { grid-template-columns: 1fr; } }

  /* Cartes de la fiche — deux colonnes, comme la maquette. Tailles LISIBLES :
     le televiseur declare un canevas de 1280 px, ou les 9-10 px de la maquette
     seraient illisibles. */
  .cartes-fiche { display: grid; gap: 12px; margin: 18px 0 4px; }
  .cartes-fiche.deux { grid-template-columns: 1fr 1fr; }
  @media (max-width: 720px) { .cartes-fiche.deux { grid-template-columns: 1fr; } }

  .carte-lang {
    background: var(--card); border: 1px solid var(--line);
    border-radius: var(--radius); padding: 16px 18px;
    display: flex; flex-direction: column; gap: 12px;
  }
  .carte-lang h3 { margin: 0; font-size: 15px; font-weight: 600; }
  .carte-lang > .sb { margin: -6px 0 2px; }

  /* Les <select> du systeme ouvraient un menu dessine par l'OS — fond blanc,
     surlignage bleu — qui ignorait le theme et jurait au milieu de la fiche.
     Meme grammaire que les pastilles du lecteur (.ppill / .pmenu) : ce sont
     les memes pistes, elles doivent se ressembler d'un ecran a l'autre. */
  .lang-ligne { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
  .lang-quoi {
    display: inline-flex; align-items: center; gap: 7px; flex: none;
    color: var(--txt-dim); font-size: 13px;
  }
  .lang-quoi svg { width: 15px; height: 15px; flex: none; opacity: .65; }

  .lang-choix { position: relative; min-width: 0; }
  .lang-pill {
    display: inline-flex; align-items: center; gap: 8px; max-width: 100%;
    padding: 7px 9px; border-radius: var(--radius-sm);
    border: 1px solid var(--line); background: var(--card-hi);
    color: var(--txt); font: inherit; font-size: 13px; cursor: pointer;
    transition: border-color .16s ease, background-color .16s ease,
                transform .16s var(--ease);
  }
  .lang-pill .nom { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .lang-pill .tech { flex: none; color: var(--txt-faint); font-size: 11.5px; white-space: nowrap; }
  .lang-pill .chev { flex: none; opacity: .5; transition: transform .18s var(--ease); }
  .lang-pill .chev svg { display: block; width: 14px; height: 14px; }
  .lang-pill:focus-visible { outline: none; border-color: var(--accent); }
  .lang-pill:active { transform: scale(.98); }
  .lang-pill[aria-expanded="true"] { border-color: var(--accent); background: var(--card); }
  .lang-pill[aria-expanded="true"] .chev { transform: rotate(180deg); opacity: .9; }
  @media (hover: hover) and (pointer: fine) {
    .lang-pill:hover { border-color: var(--line-soft); background: var(--card); }
  }

  /* Le code langue en pastille : dans « FR VFF AC3 5.1 » l'oeil cherche
     d'abord la langue et bute sur le reste. Le mot n'est pas jete, il est
     deplace — l'etiquette garde VFF, qui distingue les deux pistes FR. */
  .lg-code {
    flex: none; padding: 3px 5px; border-radius: 5px; line-height: 1;
    font-size: 10.5px; font-weight: 700; letter-spacing: .06em;
    background: rgba(255,255,255,.08); color: var(--txt-dim);
  }
  .lg-code.fr { background: rgba(255,179,64,.16); color: var(--accent); }

  .lang-menu {
    position: absolute; z-index: 30; top: calc(100% + 6px); right: 0;
    min-width: 100%; width: max-content; max-width: min(320px, 76vw);
    max-height: 46vh; overflow-y: auto;
    background: rgba(14,17,23,.96); border: 1px solid var(--line);
    border-radius: 12px; padding: 5px; box-shadow: var(--shadow-lg);
    backdrop-filter: blur(20px); animation: rise .18s var(--ease) both;
  }
  .lang-menu button {
    display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
    background: none; border: 0; color: var(--txt); border-radius: 8px;
    padding: 8px 9px; cursor: pointer; font: inherit; font-size: 13px;
    line-height: 1.3; transition: background-color .14s ease;
  }
  .lang-menu button .ck { width: 14px; flex: none; color: var(--accent); }
  .lang-menu button .ck svg { display: block; width: 14px; height: 14px; }
  .lang-menu button .nom {
    flex: 1; min-width: 0; overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap;
  }
  .lang-menu button .tech { flex: none; color: var(--txt-faint); font-size: 11.5px; }
  .lang-menu button:hover, .lang-menu button:focus-visible {
    background: rgba(255,255,255,.07); outline: none;
  }
  .lang-menu button.sel .nom { color: var(--accent); font-weight: 600; }

  .saisons-tags { display: flex; gap: 6px; flex-wrap: wrap; }
  .btn.petit { align-self: flex-start; padding: 8px 14px; font-size: 13px; }

  .champ {
    width: 100%; margin: 0 0 10px; padding: 9px 12px; border-radius: 10px;
    /* --bord et --fond2 n'existent pas dans ce theme : le champ tombait sur
       les couleurs de repli, ecrites en dur, et jurait avec le reste. */
    border: 1px solid var(--line); background: var(--card);
    color: inherit; font: inherit;
    transition: border-color .16s ease, background-color .16s ease;
  }
  .champ::placeholder { color: var(--txt-faint); }
  .champ:focus { outline: none; border-color: var(--accent); background: var(--card-hi); }
  /* Present sur le site sans rien lire. Meme vert que .live, mais borde et
     pastille : les deux etats se distinguent au coup d'oeil sans se lire. */
  .tag.actif {
    background: #13251a; color: #a6e6bd; border: 1px solid #2c5c3d;
    text-transform: none; letter-spacing: 0;
  }
  .tag.actif::before {
    content: ''; display: inline-block; width: 6px; height: 6px;
    border-radius: 50%; background: #46c76a; margin-right: 5px;
    vertical-align: middle; position: relative; top: -1px;
  }
  /* Meme vert que le badge : dans la liste des sessions, on distingue d'un
     coup d'oeil celle qui lit vraiment quelque chose des simples onglets
     ouverts. */
  .txt-live { color: #a6e6bd; }
  @media (max-width: 560px) {
    .compte-btns { width: 100%; }
    .compte-btns .btn { flex: 1; }
  }

  /* ══════════════ toasts ══════════════ */
  #toasts { position: fixed; bottom: calc(18px + var(--safe-b)); left: 50%; transform: translateX(-50%);
            z-index: 120; display: flex; flex-direction: column; gap: 9px; align-items: center;
            width: 100%; padding: 0 14px; pointer-events: none; }
  /* Un message d'erreur bavard fabriquait un bandeau plus large que l'ecran. */
  .toast {
    background: rgba(27,32,42,.94); border: 1px solid var(--line); border-radius: 12px;
    padding: 11px 17px; font-size: 13.5px; box-shadow: var(--shadow-lg);
    max-width: min(100%, 420px); text-align: center;
    backdrop-filter: blur(14px); animation: pop .26s var(--ease) both;
  }
  .toast.ok { border-color: #245139; color: #86e8a2; }
  .toast.err { border-color: #5a2a2a; color: #ffb4ad; }
  @keyframes pop { from { opacity: 0; transform: translateY(12px) scale(.97); } }

  /* ══════════════ mobile ══════════════ */
  @media (max-width: 760px) {
    :root { --pad: 14px; }
    /* Un ancetre porteur de backdrop-filter devient le bloc de reference des
       descendants en position:fixed — la barre d'onglets restait alors collee
       sous l'en-tete au lieu de descendre en bas de l'ecran. Ici l'en-tete est
       de toute facon quasi opaque, le flou ne manque pas. */
    header { backdrop-filter: none; background: rgba(8,9,12,.97); }
    /* L'en-tete fait deux lignes (identite, puis recherche) : colle en haut, il
       confisquait 100 px sur les 640 d'un petit telephone, et la barre du bas
       en prenait 60 de plus. Il defile donc avec la page — la navigation reste
       en bas, sous le pouce, et chaque changement d'onglet remonte en haut.
       Pour le recoller : remettre position:sticky ci-dessous. */
    header { position: static; }
    /* Deux lignes : identite + compte, puis la recherche en pleine largeur.
       Avant, le champ etait comprime au point de tronquer son propre libelle. */
    .head {
      flex-wrap: wrap; gap: 10px;
      padding: 10px calc(var(--pad) + var(--safe-r)) 10px calc(var(--pad) + var(--safe-l));
    }
    .brand { font-size: 15px; }
    .searchbox { order: 3; flex: 1 0 100%; max-width: none; }
    .who { font-size: 12.5px; }
    .who .name { display: none; }
    /* iOS agrandit la page des qu'on touche un champ dont le texte est sous
       16 px — la mise en page partait de travers et n'y revenait pas seule. */
    input, select, textarea { font-size: 16px; }

    /* La navigation descend en barre fixe : le pouce y arrive, et le haut
       de l'ecran est rendu au contenu. */
    nav.tabs {
      position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
      justify-content: space-around; gap: 0;
      padding: 6px calc(4px + var(--safe-r)) calc(6px + var(--safe-b))
                   calc(4px + var(--safe-l));
      background: rgba(8,9,12,.9); backdrop-filter: blur(20px) saturate(150%);
      border-top: 1px solid var(--line); overflow: visible;
    }
    nav.tabs button {
      flex-direction: column; gap: 3px; padding: 5px 4px; font-size: 10.5px;
      flex: 1; min-width: 0; font-weight: 520;
    }
    nav.tabs button svg { width: 21px; height: 21px; }
    nav.tabs button:after { display: none; }
    nav.tabs button.on { color: var(--accent); }
    nav.tabs button .pill { display: none; }
    /* La barre du bas mesure 62 px plus la marge du menton : les 60 px reserves
       laissaient la derniere ligne de chaque page glissee dessous. */
    body { padding-bottom: calc(var(--tabbar) + var(--safe-b) + 10px); }
    #toasts { bottom: calc(var(--tabbar) + var(--safe-b) + 14px); }

    /* Les rangees filent d'un bord a l'autre : retranchees a la gouttiere de la
       page, la carte suivante etait coupee net a 14 px du bord et l'on ne
       devinait plus qu'il y avait a faire glisser. */
    :root { --col: 128px; --col-wide: 208px; --col-min: 96px; }
    .row {
      gap: 12px;
      margin-left: calc(-1 * (var(--pad) + var(--safe-l)));
      margin-right: calc(-1 * (var(--pad) + var(--safe-r)));
      padding-left: calc(var(--pad) + var(--safe-l));
      padding-right: calc(var(--pad) + var(--safe-r));
      scroll-padding-left: calc(var(--pad) + var(--safe-l));
    }
    /* 96 px de plancher (jeton --col-min ci-dessus) : la grille tient trois
       colonnes de 320 a 430 px de large au lieu de basculer a deux affiches
       geantes sous 380 px. */
    .grid { gap: 15px 10px; }
    .spot { height: 300px; border-radius: 16px; margin: 14px 0 26px; }
    /* Pas de survol sur un ecran tactile, et 44 px de fleche mangeraient
       le titre : on ne garde que les pastilles.
       Elles deviennent alors le SEUL moyen de changer de diapositive au doigt,
       et un point de 8 px ne se vise pas. On agrandit donc la cible tactile a
       34 px sans toucher au point visible : le bouton devient transparent et
       c'est un pseudo-element centre qui porte le dessin. Il faut redire la
       regle pour `.on` — la version de base est plus specifique que celle-ci
       et rendrait sinon sa largeur de 22 px au bouton lui-meme. */
    .spot-fleche { display: none; }
    .spot-pastilles { gap: 0; bottom: 6px; }
    .spot-pastilles button,
    .spot-pastilles button.on {
      width: 34px; height: 34px; border-radius: 50%;
      background: none; position: relative;
    }
    .spot-pastilles button:before {
      content: ""; position: absolute; left: 50%; top: 50%;
      transform: translate(-50%, -50%);
      width: 8px; height: 8px; border-radius: 5px;
      background: rgba(255,255,255,.32);
      transition: width .25s var(--ease), background-color .25s ease;
    }
    .spot-pastilles button.on:before { width: 22px; background: var(--accent); }
    .spot-in { padding: 22px 20px 20px; }
    .spot .ov { -webkit-line-clamp: 2; font-size: 13.5px; }
    .spot .actions .btn { flex: 1; }
    .sk.spot-sk { height: 300px; border-radius: 16px; margin: 14px 0 26px; }
    .sheet-body {
      padding: 0 calc(17px + var(--safe-r)) 40px calc(17px + var(--safe-l));
      margin-top: -56px;
    }
    .sheet-in > .sheet-body:first-child { padding-top: calc(62px + var(--safe-t)); }
    .sheet-body h1 { font-size: 23px; }
    .ep .thumb { width: 88px; }
    .pbar select { max-width: 124px; }
    .empty { padding: 34px 18px; }
    h2 { font-size: 14.5px; }
    /* `section > h2.rangee-titre` l'emporte sur le `h2` ci-dessus : il faut
       donc le redescendre explicitement, sinon 19px tiennent sur mobile. */
    section > h2.rangee-titre { font-size: 16.5px; margin-bottom: 12px; }
    /* Deux boutons plus un titre sur une seule ligne : le titre se reduisait a
       trois mots. Les commandes passent a la ligne, alignees a droite. */
    .list-row { flex-wrap: wrap; justify-content: flex-end; }
    .list-row .main { flex: 1 0 100%; }
  }

  /* ══════════════ telephone en paysage ══════════════
     Le seuil de largeur ne voit pas ce cas : un telephone couche fait 844 px de
     large, il recoit donc la mise en page de bureau dans 390 px de haut, ou
     l'en-tete, les onglets et le pleins-feux ne laissaient rien voir. */
  @media (max-height: 500px) and (orientation: landscape) {
    header { position: static; }
    .head { padding-top: 8px; padding-bottom: 8px; }
    nav.tabs button { padding: 7px 13px 9px; }
    .spot { min-height: 210px; margin: 12px 0 20px; }
    .sk.spot-sk { height: 210px; margin: 12px 0 20px; }
    .spot-in { padding: 20px 24px 18px; }
    .spot .ov { -webkit-line-clamp: 2; margin-bottom: 14px; }
    .spot h1 { font-size: clamp(24px, 3vw, 32px); margin-bottom: 8px; }
    section { margin: 20px 0; }
    .hero { aspect-ratio: 21/6; }
  }

  /* ══════════════ cibles tactiles ══════════════
     Au doigt, en dessous de 40 px de haut on vise sans jamais toucher. La
     question porte sur le pointeur, pas sur la largeur de l'ecran : une
     tablette et un telephone couche ont le meme doigt. Les libelles gardent
     leur taille, seule la zone sensible grandit. */
  @media (hover: none) and (pointer: coarse) {
    .who button { padding: 9px 10px; min-height: 40px; }
    .close-x { width: 44px; height: 44px; }
    .mini { min-height: 40px; }
    .filters select, .filters .chip { min-height: 42px; padding: 9px 12px; }
    .ppill { min-height: 42px; }
    .ppill.ico { min-width: 42px; justify-content: center; }
    .pmenu button { min-height: 42px; }
    .pregl button { width: 40px; height: 40px; }
    .lang-pill { min-height: 42px; }
    .lang-menu button { min-height: 42px; }
    nav.tabs button { min-height: 44px; }
  }

  /* Le mouvement reduit garde les fondus (ils expliquent) et retire les
     deplacements (ils donnent le vertige). */
  @media (prefers-reduced-motion: reduce) {
    *, *:before, *:after {
      animation-duration: .01ms !important; animation-iteration-count: 1 !important;
      transition-duration: .06s !important;
    }
    .card { animation: none; }
    .poster img, .card:hover .poster { transform: none !important; }
  }

  /* Utilitaires : le CSP interdit l'attribut style="…", ces classes le remplacent. */
  .mt6 { margin-top: 6px; }
  .mt20 { margin-top: 20px; }
  .more { text-align: center; margin-top: 26px; }
  /* Bas de liste quand la suite se charge seule : un temoin, pas un bouton. Il
     tient la meme place que l'ancien « Charger la suite » — sans rien du tout,
     la grille se terminait sur un blanc et on croyait etre au bout du
     catalogue. */
  .suite {
    display: inline-flex; align-items: center; gap: 10px;
    color: var(--txt-faint); font-size: 13px; padding: 10px 4px;
  }
  .suite i {
    width: 15px; height: 15px; border-radius: 50%; flex: none;
    border: 2px solid rgba(255,255,255,.14); border-top-color: var(--accent);
    animation: turn .8s linear infinite;
  }
  .clickable { cursor: pointer; }
  .pv.sm { width: 38px; }
  .txt-faint { color: var(--txt-faint); }
  .txt-warn { color: var(--warn); }

/* ══════════════════════════════════════════════════════════════════
   MÉDIATHÈQUE · MÉNAGE — la liste où l'on choisit ce qu'on supprime.
   Ajouté le 2026-08-31.

   Une ligne se coche EN ENTIER, pas seulement sur la case : à la
   télécommande comme au doigt, viser un carré de 22 px est une
   punition. La case reste affichée parce qu'elle dit ce qui va se
   passer, mais toute la ligne est la cible.

   Le badge reprend `--movie` et `--tv`, déjà utilisés partout ailleurs
   pour distinguer un film d'une série : une troisième convention de
   couleur pour la même distinction serait une occasion de se tromper.
   ══════════════════════════════════════════════════════════════════ */
.lib-row {
  display: flex; align-items: center; gap: 16px;
  padding: 10px 16px 10px 10px; margin-bottom: 8px;
  background: var(--card); border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm); cursor: pointer;
  transition: background .18s var(--ease), border-color .18s var(--ease);
}
.lib-row:hover      { background: var(--card-hi); }
.lib-row:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
/* Sélectionnée : la teinte d'alerte, dosée. Le rouge plein viendra
   à la confirmation, pas avant. */
.lib-row.sel {
  background: rgba(255, 98, 87, .09);
  border-color: rgba(255, 98, 87, .38);
}

.lib-aff {
  position: relative; flex: 0 0 auto;
  width: 118px; height: 66px; border-radius: 8px; overflow: hidden;
  background: var(--bg-soft);
}
.lib-aff img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lib-vide    { width: 100%; height: 100%; background: var(--line-soft); }

.lib-genre {
  position: absolute; top: 6px; right: 6px;
  padding: 2px 8px; border-radius: 6px;
  font-size: 11px; font-weight: 600; line-height: 1.5;
  color: #0b0d11;
}
.lib-genre.movie { background: var(--movie); }
.lib-genre.tv    { background: var(--tv); }

/* `min-width: 0` : sans lui, un titre long refuse de se tronquer et
   pousse la taille et la case hors de la ligne. */
.lib-tl {
  flex: 1 1 auto; min-width: 0;
  font-size: 15px; color: var(--txt);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lib-taille {
  flex: 0 0 auto; color: var(--txt-dim); font-size: 14px;
  font-variant-numeric: tabular-nums;
}

.lib-case {
  flex: 0 0 auto; width: 22px; height: 22px;
  border: 2px solid var(--line); border-radius: 6px;
  background: transparent; position: relative;
  transition: background .15s var(--ease), border-color .15s var(--ease);
}
.lib-case.on { background: var(--err); border-color: var(--err); }
/* La coche est dessinée en CSS : une image de plus à charger pour deux
   traits n'a pas de sens. */
.lib-case.on::after {
  content: ''; position: absolute; left: 6px; top: 2px;
  width: 5px; height: 10px;
  border: solid #fff; border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

.lib-barre {
  display: flex; align-items: center; justify-content: center; gap: 20px;
  margin-top: 14px; padding: 14px 18px;
  background: rgba(255, 98, 87, .10);
  border: 1px solid rgba(255, 98, 87, .34);
  border-radius: var(--radius-sm);
}
.lib-barre.hide { display: none; }
.lib-suppr {
  display: inline-flex; align-items: center; gap: 10px;
  background: none; border: 0; cursor: pointer;
  color: var(--err); font-size: 15px; font-weight: 600;
  font-family: inherit; padding: 0;
}
.lib-suppr svg { width: 18px; height: 18px; }
.lib-suppr:hover { text-decoration: underline; }
.lib-barre-o {
  color: var(--err); font-size: 15px; opacity: .85;
  padding-left: 20px; border-left: 1px solid rgba(255, 98, 87, .34);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 640px) {
  .lib-aff { width: 84px; height: 48px; }
  .lib-tl  { font-size: 14px; }
  .lib-barre { flex-direction: column; gap: 8px; }
  .lib-barre-o { border-left: 0; padding-left: 0; }
}

/* ══════════════════════════════════════════════════════════════════
   EN ATTENTE — ce qui est réclamé mais pas encore là.
   Ajouté le 2026-08-31, même famille visuelle que le ménage.

   La ligne n'est PAS cliquable, contrairement à celle du ménage : elle
   porte deux actions opposées dont une destructive, et un clic sur la
   ligne devrait en choisir une — la mauvaise une fois sur deux.

   « Abandonner » est le seul bouton plein de la page. Le rouge plein
   est réservé à ce qui détruit, et il n'apparaît qu'une fois par
   ligne : s'il servait aussi à « Rechercher », il ne voudrait plus
   rien dire.
   ══════════════════════════════════════════════════════════════════ */
.att-row {
  display: flex; align-items: center; gap: 18px;
  padding: 12px 16px 12px 10px; margin-bottom: 10px;
  background: var(--card); border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
}
.att-aff {
  flex: 0 0 auto; width: 130px; height: 74px;
  border-radius: 8px; overflow: hidden; background: var(--bg-soft);
}
.att-aff img { width: 100%; height: 100%; object-fit: cover; display: block; }
.att-vide    { width: 100%; height: 100%; background: var(--line-soft); }

/* `min-width: 0` : sans lui, un titre long pousse les deux boutons hors
   de la ligne au lieu de se tronquer. */
.att-main { flex: 1 1 auto; min-width: 0; }
.att-tl {
  font-size: 17px; color: var(--txt); margin-bottom: 5px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.att-sb {
  display: flex; align-items: center; gap: 7px;
  font-size: 14px; color: var(--txt-faint);
}
.att-sb svg { width: 15px; height: 15px; flex: 0 0 auto; }

.att-btn {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 9px;
  padding: 11px 18px; border-radius: 9px;
  background: var(--bg-soft); border: 1px solid var(--line);
  color: var(--txt); font-size: 14px; font-family: inherit;
  cursor: pointer; white-space: nowrap;
  transition: background .16s var(--ease), border-color .16s var(--ease);
}
.att-btn svg { width: 16px; height: 16px; }
.att-btn:hover { background: var(--card-hi); border-color: var(--txt-faint); }
.att-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.att-btn.danger {
  background: var(--err); border-color: var(--err); color: #fff;
}
.att-btn.danger:hover { filter: brightness(1.1); border-color: var(--err); }

/* Sous 900 px les deux boutons ne tiennent plus à côté du titre : ils
   passent dessous, sur toute la largeur, plutôt que d'être rognés. */
@media (max-width: 900px) {
  .att-row { flex-wrap: wrap; }
  .att-main { flex: 1 1 60%; }
  .att-btn { flex: 1 1 auto; justify-content: center; }
}
@media (max-width: 640px) {
  .att-aff { width: 92px; height: 52px; }
  .att-tl  { font-size: 15px; }
}

/* ══════════════════════════════════════════════════════════════════
   EN-TÊTE DU SUIVI — tuiles, disque, état vide.
   Ajouté le 2026-08-31.

   Chaque tuile porte SA teinte, et ce n'est pas décoratif : on lit
   cette bande d'un coup d'œil, et la couleur est ce qui permet de
   retrouver « en attente » sans lire les six libellés. Les teintes
   sont déclarées ici, jamais dans le JavaScript, pour qu'un
   changement de palette n'ait qu'un seul endroit à toucher.
   ══════════════════════════════════════════════════════════════════ */
.tiles {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}
.tuile {
  display: flex; align-items: center; gap: 14px;
  padding: 16px 18px;
  background: var(--card); border: 1px solid var(--line-soft);
  border-radius: var(--radius);
}
.tuile-ico {
  flex: 0 0 auto;
  width: 46px; height: 46px; border-radius: 12px;
  display: grid; place-items: center;
  background: var(--t-fond); color: var(--t-trait);
  /* La lueur reprend la teinte de la tuile : sans elle les pastilles
     paraissent posées sur le fond au lieu d'en émerger. */
  box-shadow: 0 0 18px -6px var(--t-trait);
}
.tuile-ico svg { width: 23px; height: 23px; }
.tuile-txt { display: flex; flex-direction: column; min-width: 0; }
.tuile-txt b {
  font-size: 26px; font-weight: 700; line-height: 1.15;
  color: var(--t-trait);
  font-variant-numeric: tabular-nums;
}
.tuile-txt span {
  font-size: 13px; color: var(--txt-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.t-rouge  { --t-trait: #ff4d4f; --t-fond: rgba(255,  77,  79, .12); }
.t-violet { --t-trait: #a78bfa; --t-fond: rgba(167, 139, 250, .12); }
.t-or     { --t-trait: #f5b027; --t-fond: rgba(245, 176,  39, .12); }
.t-vert   { --t-trait: #2fd4a7; --t-fond: rgba( 47, 212, 167, .12); }
.t-bleu   { --t-trait: #3b9dff; --t-fond: rgba( 59, 157, 255, .12); }
.t-mauve  { --t-trait: #b48bff; --t-fond: rgba(180, 139, 255, .12); }

/* ---- Disque ---------------------------------------------------- */
.dq {
  display: flex; align-items: flex-start; gap: 20px;
  padding: 20px 22px;
  background: var(--card); border: 1px solid var(--line-soft);
  border-radius: var(--radius);
}
.dq-ico {
  flex: 0 0 auto; width: 62px; height: 62px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(255, 77, 79, .12); color: #ff4d4f;
}
.dq-ico svg { width: 27px; height: 27px; }
.dq-corps { flex: 1 1 auto; min-width: 0; }
.dq-ligne { display: flex; align-items: center; gap: 16px; margin-bottom: 14px; }
.dq-jauge {
  flex: 1 1 auto; height: 11px; border-radius: 99px;
  background: var(--line-soft); overflow: hidden;
}
.dq-jauge i {
  display: block; height: 100%; width: 0; border-radius: 99px;
  background: linear-gradient(90deg, #e0393b, #ff6b6d);
  transition: width .5s var(--ease);
}
.dq-pct {
  flex: 0 0 auto; padding: 4px 11px; border-radius: 7px;
  background: rgba(255, 77, 79, .14); color: #ff6b6d;
  font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums;
}
.dq-txt   { font-size: 16px; color: var(--txt); margin-bottom: 4px; }
.dq-pt    { color: var(--txt-faint); margin: 0 6px; }
.dq-sous  { font-size: 14px; color: var(--txt-faint); }

/* ---- Rien à afficher ------------------------------------------- */
/* Un cadre en pointillés plutôt qu'une ligne de texte seule : il dit
   qu'il y a bien une zone, et qu'elle est vide — pas que la page
   s'est arrêtée de charger. */
.vide-boite {
  display: grid; place-items: center; gap: 12px;
  padding: 46px 20px;
  border: 1px dashed var(--line); border-radius: var(--radius);
  color: var(--txt-faint);
}
.vide-boite svg { width: 40px; height: 40px; opacity: .55; }
.vide-boite p   { margin: 0; font-size: 15px; }

@media (max-width: 640px) {
  .tiles { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
  .tuile { padding: 13px 14px; gap: 11px; }
  .tuile-ico { width: 40px; height: 40px; }
  .tuile-txt b { font-size: 22px; }
  .dq { padding: 16px; gap: 14px; }
  .dq-ico { width: 50px; height: 50px; }
}

/* Sur le téléviseur, la page est dessinée dans un canevas de 1280 px : les six
   tuiles ne tiennent plus à 190 px minimum et la sixième tombe seule à la
   ligne — constaté à l'écran le 2026-08-31. On resserre juste assez pour que
   la bande reste sur UNE ligne, ce qui est tout son intérêt : on la lit d'un
   coup d'œil, pas en deux. */
:root.tv .tiles {
  grid-template-columns: repeat(6, 1fr);
  gap: 10px;
}
:root.tv .tuile     { padding: 14px 12px; gap: 11px; }
:root.tv .tuile-ico { width: 42px; height: 42px; border-radius: 11px; }
:root.tv .tuile-txt b { font-size: 23px; }
:root.tv .tuile-txt span { font-size: 12px; }

/* ══════════════════════════════════════════════════════════════════
   SUIVI — téléchargements, demandes, ajouts récents.
   Ajouté le 2026-08-31, même famille que le ménage et l'attente.

   Les trois partagent la carrure : affiche à gauche, contenu au
   milieu, information de droite. Ce qui change d'une section à
   l'autre, c'est ce qu'on met à droite — et c'est précisément ce
   qu'on vient y chercher.
   ══════════════════════════════════════════════════════════════════ */

/* ---- Téléchargements ------------------------------------------- */
.tel-row {
  display: flex; align-items: center; gap: 18px;
  padding: 14px 20px 14px 10px; margin-bottom: 10px;
  background: var(--card); border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
}
/* Un téléchargement en difficulté se signale par sa bordure, pas par un
   texte de plus : on parcourt cette liste des yeux, on ne la lit pas. */
.tel-row.pb { border-color: rgba(224, 161, 44, .45); }

.tel-aff {
  flex: 0 0 auto; width: 104px; height: 60px;
  border-radius: 8px; overflow: hidden; background: var(--bg-soft);
}
.tel-aff img { width: 100%; height: 100%; object-fit: cover; display: block; }
.tel-vide    { width: 100%; height: 100%; background: var(--line-soft); }

.tel-main { flex: 1 1 auto; min-width: 0; }
.tel-tl {
  font-size: 15px; color: var(--txt); margin-bottom: 3px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tel-sb { font-size: 13px; color: var(--txt-dim); margin-bottom: 8px; }
.tel-jauge {
  height: 7px; border-radius: 99px; background: var(--line-soft);
  overflow: hidden; margin-bottom: 7px;
}
.tel-jauge i {
  display: block; height: 100%; width: 0; border-radius: 99px;
  background: linear-gradient(90deg, #2f8fe0, #3b9dff);
  transition: width .5s var(--ease);
}
.tel-row.pb .tel-jauge i { background: linear-gradient(90deg, #c98a1e, #e0a12c); }
.tel-meta { font-size: 12.5px; color: var(--txt-faint); }
.tel-alerte {
  display: flex; align-items: center; gap: 7px;
  margin-top: 6px; font-size: 12.5px; color: var(--warn);
}
.tel-alerte svg { width: 14px; height: 14px; flex: 0 0 auto; }

.tel-pct {
  flex: 0 0 auto; min-width: 66px; text-align: right;
  font-size: 25px; font-weight: 700; color: #3b9dff;
  font-variant-numeric: tabular-nums;
}
.tel-pct span { font-size: 14px; font-weight: 500; opacity: .7; margin-left: 2px; }
.tel-row.pb .tel-pct { color: var(--warn); }

/* ---- Demandes --------------------------------------------------- */
.dem-row {
  display: flex; align-items: center; gap: 18px;
  padding: 12px 20px 12px 10px; margin-bottom: 10px;
  background: var(--card); border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
}
.dem-aff {
  position: relative; flex: 0 0 auto; width: 104px; height: 60px;
  border-radius: 8px; overflow: hidden; background: var(--bg-soft);
}
.dem-aff img { width: 100%; height: 100%; object-fit: cover; display: block; }
.dem-vide    { width: 100%; height: 100%; background: var(--line-soft); }
.dem-genre {
  position: absolute; top: 5px; right: 5px;
  padding: 2px 7px; border-radius: 5px;
  font-size: 10.5px; font-weight: 600; color: #0b0d11;
}
.dem-genre.movie { background: var(--movie); }
.dem-genre.tv    { background: var(--tv); }

.dem-main { flex: 1 1 auto; min-width: 0; }
.dem-tl {
  font-size: 15px; color: var(--txt); margin-bottom: 6px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dem-annee { color: var(--txt-faint); font-weight: 400; }
.dem-etat  { margin-bottom: 5px; }
.dem-note  { font-size: 12.5px; color: var(--txt-faint); }

/* ---- Ajouts récents --------------------------------------------- */
.rec-row {
  display: flex; align-items: center; gap: 14px;
  padding: 11px 18px; margin-bottom: 8px;
  background: var(--card); border: 1px solid var(--line-soft);
  border-radius: var(--radius-sm);
}
.rec-genre {
  flex: 0 0 auto; padding: 3px 10px; border-radius: 6px;
  font-size: 11px; font-weight: 600; color: #0b0d11;
}
.rec-genre.movie { background: var(--movie); }
.rec-genre.tv    { background: var(--tv); }
.rec-main { flex: 1 1 auto; min-width: 0; }
.rec-tl {
  font-size: 14.5px; color: var(--txt);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rec-sub { color: var(--txt-faint); }
.rec-q   { font-size: 12.5px; color: var(--txt-faint); margin-top: 2px; }
.rec-date {
  flex: 0 0 auto; font-size: 13px; color: var(--txt-dim);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 640px) {
  .tel-aff, .dem-aff { width: 80px; height: 46px; }
  .tel-pct { font-size: 20px; min-width: 54px; }
}

/* Le pied de liste quand le ménage est tronqué à 40 lignes. */
.lib-reste {
  padding: 12px; text-align: center;
  font-size: 13px; color: var(--txt-faint);
}

/* Pastille d'état d'une invitation : elle prend la place que l'affiche occupe
   sur les autres lignes. Une invitation n'a rien à montrer, mais son état est
   exactement ce qu'on vient y lire. */
.inv-etat {
  flex: 0 0 auto; min-width: 78px; text-align: center;
  padding: 4px 10px; border-radius: 6px;
  font-size: 11.5px; font-weight: 600;
  background: var(--card-hi); color: var(--txt-dim);
}
.inv-etat.e-en-cours { background: rgba(255, 179, 64, .16); color: var(--accent); }
.inv-etat.e-utilisée { background: rgba(70, 199, 106, .16); color: var(--ok); }

/* Une demande dont on peut ouvrir la fiche : le curseur doit le dire avant
   le clic, sinon rien ne distingue les deux cas. */
.dem-main.clickable { cursor: pointer; }
.dem-main.clickable:hover .dem-tl { color: var(--accent); }

/* ══════════════════════════════════════════════════════════════════
   COMPTES — une carte par personne.
   Ajouté le 2026-08-31.

   La carte répond à trois questions dans cet ordre : qui, quand
   l'ai-je vu, sur quoi. D'où l'avatar puis deux lignes datées, puis
   deux colonnes côte à côte. Provenance et statistiques se replient :
   on ne les consulte qu'en cas de doute, et les laisser ouvertes
   noyait ce qui se lit tous les jours.
   ══════════════════════════════════════════════════════════════════ */
.cp {
  padding: 22px 24px; margin-bottom: 16px;
  background: var(--card); border: 1px solid var(--line-soft);
  border-radius: var(--radius);
}
/* Un compte bloqué se voit à sa bordure, pas seulement à son étiquette :
   c'est l'état qu'on cherche des yeux en parcourant la liste. */
.cp.bloque { border-color: rgba(255, 98, 87, .4); }

.cp-tete { display: flex; align-items: flex-start; gap: 18px; }
.cp-avatar {
  flex: 0 0 auto; width: 62px; height: 62px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 26px; font-weight: 700; color: var(--err);
  background: radial-gradient(circle at 35% 30%,
              rgba(255, 98, 87, .30), rgba(255, 98, 87, .09));
}
.cp-ident { flex: 1 1 auto; min-width: 0; }
.cp-nom {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  font-size: 19px; font-weight: 600; color: var(--txt); margin-bottom: 8px;
}
.cp-info {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 13.5px; color: var(--txt-dim); margin-bottom: 4px;
}
.cp-info svg { width: 15px; height: 15px; flex: 0 0 auto; opacity: .8; }
.cp-pct { color: var(--err); }
.cp-sep { color: var(--txt-faint); }

.cp-actions { flex: 0 0 auto; display: flex; align-items: center; gap: 9px; }
.cp-plus {
  width: 38px; height: 38px; border-radius: 9px;
  display: grid; place-items: center;
  background: none; border: 1px solid transparent;
  color: var(--txt-dim); cursor: pointer;
}
.cp-plus svg { width: 17px; height: 17px; }
.cp-plus:hover { background: var(--card-hi); border-color: var(--line); }
.cp-plus:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.cp-cols {
  display: grid; grid-template-columns: 1fr 1fr; gap: 28px;
  margin-top: 20px; padding-top: 18px;
  border-top: 1px solid var(--line-soft);
}
.cp-col h4 {
  margin: 0 0 11px; font-size: 11.5px; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase; color: var(--txt-faint);
}
.cp-ligne {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 13.5px; color: var(--txt-dim); margin-bottom: 7px;
}
.cp-ligne svg { width: 16px; height: 16px; flex: 0 0 auto; opacity: .75; }
.cp-ligne b { color: var(--txt); font-weight: 500; }
.cp-ip { color: var(--txt-faint); font-variant-numeric: tabular-nums; }
.cp-quand { color: var(--txt-faint); }
.cp-echec { color: var(--err); }
.cp-rien  { font-size: 13.5px; color: var(--txt-faint); }

.cp-repli {
  margin-top: 8px; padding: 6px 13px; border-radius: 7px;
  background: var(--bg-soft); border: 1px solid var(--line);
  color: var(--txt-dim); font-size: 12.5px; font-family: inherit;
  cursor: pointer;
}
.cp-repli:hover { background: var(--card-hi); color: var(--txt); }
.cp-repli:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.cp-details, .cp-tous {
  margin-top: 16px; padding-top: 14px;
  border-top: 1px solid var(--line-soft);
  font-size: 13.5px; color: var(--txt-dim);
}
.cp-details > div { margin-bottom: 6px; }
.cp-details.hide, .cp-tous.hide { display: none; }

/* Le bouton d'invitation : le seul plein de cet onglet. Créer un lien est
   l'action qu'on vient y faire. */
.inv-creer {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 12px 22px; border-radius: 9px;
  background: var(--err); border: 1px solid var(--err); color: #fff;
  font-size: 14.5px; font-weight: 600; font-family: inherit; cursor: pointer;
  white-space: nowrap;
}
.inv-creer svg { width: 17px; height: 17px; }
.inv-creer:hover { filter: brightness(1.1); }
.inv-creer:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

@media (max-width: 900px) {
  .cp-cols { grid-template-columns: 1fr; gap: 18px; }
  .cp-tete { flex-wrap: wrap; }
  .cp-actions { width: 100%; }
  .cp-actions .att-btn { flex: 1 1 auto; justify-content: center; }
}

/* ══════════════════════════════════════════════════════════════════
   MENU DE CHOIX — le remplaçant du <select> du système.
   Ajouté le 2026-08-31.

   Le menu déroulé d'un <select> natif est dessiné par le navigateur :
   liste blanche, surlignage bleu, aucune règle CSS ne l'atteint. Sur
   fond sombre il détonne, et sur le téléviseur il s'ouvre en plein
   écran. Ici tout est du HTML ordinaire, donc tout se style.
   ══════════════════════════════════════════════════════════════════ */
.choix { position: relative; display: inline-block; }

.choix-btn {
  display: inline-flex; align-items: center; gap: 10px;
  min-width: 170px; padding: 11px 14px;
  background: var(--bg-soft); border: 1px solid var(--line);
  border-radius: 10px; color: var(--txt);
  font: inherit; cursor: pointer; text-align: left;
}
.choix-val { flex: 1 1 auto; }
.choix-btn svg {
  width: 16px; height: 16px; flex: 0 0 auto; opacity: .7;
  transition: transform .18s var(--ease);
}
.choix-btn[aria-expanded="true"] svg { transform: rotate(180deg); }
.choix-btn:hover { background: var(--card-hi); border-color: var(--txt-faint); }
.choix-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.choix-liste {
  position: absolute; z-index: 60; top: calc(100% + 6px); left: 0;
  min-width: 100%; padding: 6px;
  background: var(--card-hi); border: 1px solid var(--line);
  border-radius: 10px; box-shadow: 0 14px 34px -10px rgba(0, 0, 0, .8);
}
.choix-liste.hide { display: none; }
.choix-liste button {
  display: block; width: 100%; padding: 9px 12px;
  background: none; border: 0; border-radius: 7px;
  color: var(--txt-dim); font: inherit; text-align: left;
  cursor: pointer; white-space: nowrap;
}
.choix-liste button:hover { background: var(--card); color: var(--txt); }
.choix-liste button:focus-visible {
  outline: 2px solid var(--accent); outline-offset: -2px;
}
/* L'option retenue se marque par sa couleur et son poids, pas par un aplat :
   un aplat de sélection en plus du survol donne deux lignes surlignées et
   on ne sait plus laquelle est laquelle. */
.choix-liste button.sel { color: var(--accent); font-weight: 600; }

@media (max-width: 640px) {
  .choix, .choix-btn { width: 100%; }
}
