@charset "UTF-8";
/* ===========================================================================
   Aurora — call.css · LA FENETRE D'APPEL (chargee EN DERNIER)
   ---------------------------------------------------------------------------
   Ordre de chargement : style.css → themes.css → anim.css → polish.css →
   header.css → call.css. Ce fichier ne modifie AUCUN des precedents : il les
   surcharge. Quand une regle a besoin de battre un selecteur plus specifique
   (ex. `[data-theme="light"] .call-window` de style.css l.1018), le selecteur
   est repris a l'identique plutot que de sortir un `!important`.

   CE QUI N'EST PAS RETOUCHE ICI (deja traite par polish.css, section 2) :
     • le DOCK `.call-controls` (pilule flottante) ;
     • le bouton RACCROCHER `.control.danger` et toute la famille .control ;
     • les decalages `.tiles { bottom: … }` par mode, qui suivent la hauteur du
       dock — on ne touche donc qu'a la LARGEUR et a l'APPARENCE des vignettes.

   ---------------------------------------------------------------------------
   POURQUOI UNE COUCHE DEDIEE
   ---------------------------------------------------------------------------
   La fenetre d'appel est le seul endroit de l'application ou l'interface est
   posee SUR une image que l'on ne controle pas (une video peut etre blanche,
   noire, saturee…). Deux consequences qui expliquent tout le fichier :

     1. Le contraste ne peut pas venir d'un jeton de theme. Un « blanc a 60 % »
        disparait sur un partage d'ecran clair. Tout ce qui est pose sur la
        video porte donc son PROPRE fond sombre translucide (voile + flou), et
        jamais du texte nu.
     2. La fenetre reste SOMBRE en theme clair (style.css force deja
        `color: #eef1ff` l.1018, mais laissait la barre du haut en `--glass-2`
        clair : du blanc sur du blanc). On redefinit donc les jetons de surface
        DANS `.call-window` : tout composant generique qui y entre (`.icon-btn`,
        `.avatar`, `.muted`…) devient automatiquement sombre, quel que soit le
        theme de la page.

   ---------------------------------------------------------------------------
   COULEURS
   ---------------------------------------------------------------------------
   Aucune couleur de marque en dur. On n'utilise que les jetons du projet
   (--accent, --accent-2, --danger, --ok, --warn, --muted, --radius…) et deux
   jetons NEUTRES deja definis ailleurs, volontairement independants du theme :
     • --sur-accent (#ffffff, style.css l.940) = le blanc de reference ;
     • --pol-ink    (#05050c, polish.css l.52) = le noir profond de reference.
   Ce sont les seuls admis ici, et c'est l'EXCEPTION prevue : un voile pose sur
   la video ne peut pas emprunter la couleur du theme, sinon il devient blanc
   en theme clair et l'interface disparait. Un repli litteral accompagne
   `--pol-ink` au cas ou polish.css ne serait pas charge.

   ANIMATION : seules `transform` et `opacity` sont animees par keyframes,
   toutes les animations sont a UN SEUL passage (aucune boucle infinie, y
   compris pour le point « live » qui clignotait pendant tout l'appel), et
   l'ensemble est neutralise en fin de fichier sous prefers-reduced-motion.
   =========================================================================== */


/* ===========================================================================
   0. JETONS LOCAUX — la fenetre est une ILOT sombre dans la page
   ---------------------------------------------------------------------------
   Les trois selecteurs sont necessaires : style.css cible la fenetre d'appel
   via `[data-theme="light"] .call-window` (specificite 0,2,0) et
   `html[data-atheme="light"] .call-window` (0,1,1 + type). Une simple regle
   `.call-window` (0,1,0) perdrait en theme clair, ordre de chargement ou pas.
   =========================================================================== */
.call-window,
[data-theme="light"] .call-window,
html[data-atheme="light"] .call-window {
  /* --- Palette de l'ilot ---------------------------------------------------
     Redefinir ces jetons DANS la fenetre suffit a rendre sombre tout ce qui y
     entre sans avoir a lister les composants un par un.
     A noter : --pol-hover / --pol-danger-* (polish.css) sont declares sur
     :root, leur `var(--text)` interne est donc deja resolu la-bas ; les
     redefinitions ci-dessous ne les affectent pas et le dock reste intact. */
  --text: var(--sur-accent);
  --muted: color-mix(in srgb, var(--sur-accent) 64%, transparent);
  --border: color-mix(in srgb, var(--sur-accent) 16%, transparent);
  --glass: color-mix(in srgb, var(--sur-accent) 7%, transparent);
  --glass-2: color-mix(in srgb, var(--sur-accent) 11%, transparent);
  --glass-3: color-mix(in srgb, var(--sur-accent) 18%, transparent);
  --sunken: color-mix(in srgb, var(--pol-ink, #05050c) 34%, transparent);
  --sunken-2: color-mix(in srgb, var(--pol-ink, #05050c) 48%, transparent);

  /* --- Le noir de reference et ses voiles ---------------------------------- */
  --cw-void: var(--pol-ink, #05050c);                                    /* fond de scene */
  --cw-veil: color-mix(in srgb, var(--pol-ink, #05050c) 58%, transparent);   /* voile leger */
  --cw-veil-2: color-mix(in srgb, var(--pol-ink, #05050c) 74%, transparent); /* voile lisible */
  --cw-veil-3: color-mix(in srgb, var(--pol-ink, #05050c) 86%, transparent); /* panneau */

  /* --- Filets lumineux ------------------------------------------------------
     Un lisere clair est le seul moyen de detacher un element d'une video qui
     peut etre, elle aussi, tres sombre. */
  --cw-line: color-mix(in srgb, var(--sur-accent) 16%, transparent);
  --cw-line-2: color-mix(in srgb, var(--sur-accent) 30%, transparent);

  /* --- Etats : melanges avec du blanc -------------------------------------
     En theme clair, --ok/--danger/--warn sont des versions FONCEES (style.css
     l.1006) illisibles sur un fond noir. Les melanger a --sur-accent garantit
     une teinte claire dans les deux themes, donc lisible sur la video. */
  --cw-ok: color-mix(in srgb, var(--ok) 74%, var(--sur-accent));
  --cw-danger: color-mix(in srgb, var(--danger) 74%, var(--sur-accent));
  --cw-warn: color-mix(in srgb, var(--warn) 80%, var(--sur-accent));
  --cw-accent: color-mix(in srgb, var(--accent) 78%, var(--sur-accent));

  /* --- Ombre portee : elle seule detache une carte d'une video claire ------ */
  --cw-lift: 0 10px 28px color-mix(in srgb, var(--pol-ink, #05050c) 62%, transparent);
  --cw-lift-2: 0 18px 46px color-mix(in srgb, var(--pol-ink, #05050c) 72%, transparent);

  /* --- Rythme --------------------------------------------------------------
     Meme courbe que polish.css / anim.css : la fenetre doit bouger comme le
     reste de l'application. */
  --cw-ease: cubic-bezier(.22, 1, .36, 1);
  --cw-fast: 160ms;
  --cw-base: 220ms;

  /* --- Metriques pilotees par mode (mini / plein ecran / mobile) ------------
     Un seul endroit a changer par mode : la barre du haut, les vignettes et
     l'avatar suivent. */
  /* --cw-topbar-h est la hauteur REELLE de la barre du haut (remplissage
     compris : 8 + 32 de bouton + 8). Tout ce qui se pose sous elle
     (diagnostic, rappel « Caméra masquée », badge « muet ») s'y accroche : si
     le jeton mentait, ces elements passeraient sous la zone de prise de la
     barre — donc sous un `mousedown` qui deplace la fenetre au lieu de les
     atteindre. */
  --cw-topbar-h: 48px;
  --cw-tile-w: 150px;
  /* La fenetre de base fait 440 x 275 px : entre la barre du haut et le dock
     il reste ~165 px. L'avatar est dimensionne POUR CE BUDGET (voir section 3),
     le « grand portrait » etant le role du plein ecran. */
  --cw-avatar: 84px;
  --cw-pad: 12px;
  --cw-tile-r: 16px;
}


/* ===========================================================================
   1. LA FENETRE
   ---------------------------------------------------------------------------
   `.glass` (style.css l.67) lui donne un fond `var(--glass)` : en theme clair
   c'etait un cadre blanc autour d'une video noire. On peint donc le cadre en
   dur avec le noir de reference. Le flou de `.glass` reste utile : la fenetre
   flotte au-dessus de la conversation.
   =========================================================================== */
.call-window,
[data-theme="light"] .call-window,
html[data-atheme="light"] .call-window {
  background: var(--cw-void);
  border: 1px solid var(--cw-line);
  color: var(--text);
  /* Deux plans : une ombre large qui creuse l'espace, un lisere interieur qui
     dessine l'arete superieure du verre. */
  box-shadow:
    var(--cw-lift-2),
    0 2px 10px color-mix(in srgb, var(--pol-ink, #05050c) 50%, transparent),
    inset 0 1px 0 color-mix(in srgb, var(--sur-accent) 14%, transparent);
}

/* La scene : c'est elle qui porte le rayon de la fenetre, puisque la barre du
   haut et le dock flottent maintenant PAR-DESSUS (voir section 2). */
.call-videos {
  background:
    radial-gradient(120% 90% at 50% 0%,
      color-mix(in srgb, var(--accent) 10%, transparent), transparent 62%),
    var(--cw-void);
}

/* Les videos de scene ne portent plus de fond opaque : les bandes noires d'un
   partage d'ecran (`object-fit: contain`) laissent voir le degrade ci-dessus
   au lieu d'un aplat mort.
   ATTENTION : ne JAMAIS poser de `transform` ici — polish.css l.621 s'en sert
   pour l'effet miroir de `#local-stage.mirror`. */
.stage-video { background: transparent; }


/* ===========================================================================
   2. BARRE DU HAUT — lisible SUR la video
   ---------------------------------------------------------------------------
   Avant : une bande opaque `--glass-2` qui rognait la video, et cinq elements
   alignes sans hierarchie (le nom, le statut, le chrono et trois boutons se
   disputaient l'attention).
   Apres : la barre FLOTTE au-dessus de la video et sa lisibilite vient d'un
   degrade sombre qui s'eteint vers le bas — c'est la convention de tous les
   lecteurs video, et cela rend 46 px d'image a la scene.
   Hierarchie : le NOM domine ; le statut et le chrono sont secondaires ; les
   boutons sont des voiles discrets qui ne se lisent qu'au survol.
   `#call-drag` etant cette barre, la zone de prise reste identique.
   =========================================================================== */
.call-topbar {
  position: absolute;
  inset: 0 0 auto 0;
  z-index: 7;
  height: auto;
  min-height: var(--cw-topbar-h);
  /* La BOITE de la barre est exactement sa hauteur utile : elle est la zone de
     prise de la fenetre (`#call-drag`), et une zone de prise plus grande que
     ce que l'on voit vole les clics des elements poses juste dessous. */
  padding: 8px var(--cw-pad);
  /* Encoche / barre d'etat en plein ecran mobile. */
  padding-top: max(8px, env(safe-area-inset-top));
  gap: 8px;
  background: none;
  border-bottom: none;
}

/* Le degrade de lisibilite est porte par un pseudo-element qui DEBORDE vers le
   bas : la transition vers la video est invisible, alors qu'une bande nette se
   verrait comme un bandeau. Il ne prend aucun clic (`pointer-events: none`) et
   passe DERRIERE le contenu de la barre (`z-index: -1`, confine au contexte
   d'empilement que cree deja `.call-topbar`), donc il n'assombrit pas non plus
   ce qui vient s'ouvrir juste en dessous.
   Le degrade tient sa densite jusqu'aux trois quarts : c'est sur la DERNIERE
   ligne de texte qu'un partage d'ecran clair fait perdre le contraste. */
.call-topbar::after {
  content: "";
  position: absolute;
  inset: 0 0 -30px 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--pol-ink, #05050c) 80%, transparent) 0%,
    color-mix(in srgb, var(--pol-ink, #05050c) 66%, transparent) 42%,
    color-mix(in srgb, var(--pol-ink, #05050c) 26%, transparent) 74%,
    transparent 100%);
}

/* Ombre de texte commune : c'est elle qui garantit la lisibilite quand la
   video est blanche a l'endroit exact du texte. */
.call-topbar .call-peer-name,
.call-topbar .call-status,
.call-avatar #call-avatar-name,
.call-avatar-state {
  text-shadow:
    0 1px 3px color-mix(in srgb, var(--pol-ink, #05050c) 90%, transparent),
    0 0 12px color-mix(in srgb, var(--pol-ink, #05050c) 60%, transparent);
}

/* --- Le point « live » -----------------------------------------------------
   style.css lui donne `animation: pulse 1.5s infinite` : une animation en
   boucle sur un element visible PENDANT TOUT L'APPEL (cout permanent, et
   sautillement dans le coin de l'oeil). On la coupe : un point rouge cercle
   d'un halo fixe dit deja « en cours » sans bouger. */
.call-live {
  animation: none;
  width: 8px;
  height: 8px;
  flex: none;
  background: var(--cw-danger);
  box-shadow:
    0 0 0 3px color-mix(in srgb, var(--danger) 26%, transparent),
    0 0 10px color-mix(in srgb, var(--danger) 70%, transparent);
}

/* --- Nom du correspondant : l'element dominant ---------------------------- */
.call-peer-name {
  font-weight: 800;
  font-size: .92rem;
  letter-spacing: .1px;
  /* La barre est etroite (438 px utiles) et cinq elements s'y disputent la
     place. Arbitrage : le NOM est le seul a ceder, parce qu'il est repete en
     grand sur la scene des que la camera est coupee, et qu'un debut de nom
     suffit a s'y retrouver. `min-width` l'empeche de disparaitre tout a fait —
     sans lui, un nom long refuse meme de se tronquer et pousse les boutons
     hors de la fenetre. */
  flex: 1 1 auto;
  min-width: 3.5em;
  max-width: 44%;
}

/* --- Statut : secondaire, mais toujours lisible ---------------------------
   Il ne recoit PAS de pastille de fond : deux pastilles cote a cote (statut +
   chrono) feraient deux blocs concurrents. Seule l'ombre de texte le porte. */
.call-status {
  font-size: .74rem;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
  /* Il ne se comprime PAS : « Reconnexion… » tronque en « Reco… » ne veut plus
     rien dire, alors qu'un nom tronque reste identifiable. */
  flex: none;
  /* Puce ronde devant le libelle : elle porte l'etat en COULEUR (section 7),
     ce qui evite d'avoir a colorer le texte lui-meme. */
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.call-status::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex: none;
  background: currentColor;
  opacity: .7;
}
/* Le statut est vide entre deux appels : la puce seule ferait un point orphelin. */
.call-status:empty { display: none; }

/* --- Chrono : chiffres de largeur fixe -----------------------------------
   `tabular-nums` empeche la largeur de sauter a chaque seconde (les chiffres
   proportionnels font danser toute la barre). `min-width` reserve la place de
   « 00:00 » des le depart, et `ch` avec des chiffres tabulaires est une
   mesure exacte. */
.call-timer {
  margin-left: auto;
  flex: none;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  min-width: 5.4ch;
  text-align: center;
  font-size: .76rem;
  font-weight: 700;
  letter-spacing: .4px;
  color: var(--text);
  /* Petite pastille : le chrono est le seul element chiffre, il merite d'etre
     accroche a une surface pour rester lisible sur n'importe quelle image. */
  padding: 3px 8px;
  border-radius: 999px;
  background: var(--cw-veil);
  border: 1px solid var(--cw-line);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
}

/* --- Indicateur de qualite : meme pastille, couleur portee par les barres -- */
.call-quality {
  flex: none;
  gap: 5px;
  padding: 3px 7px;
  border-radius: 999px;
  background: var(--cw-veil);
  border: 1px solid var(--cw-line);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  font-size: .66rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  /* Il ouvre le diagnostic : le curseur doit le dire, le focus clavier aussi. */
  cursor: pointer;
  transition: background var(--cw-fast) ease, border-color var(--cw-fast) ease;
}
.call-quality:hover { background: var(--cw-veil-2); border-color: var(--cw-line-2); }
.call-quality:empty { display: none; }
.call-quality .bars i { border-radius: 2px; }

/* Hors plein ecran, le debit chiffre (« 820 kb/s ») coute 45 px pour une
   information que personne ne lit en continu — et ces 45 px sont exactement
   ce qui manque au nom du correspondant. On ne garde que les BARRES, qui
   disent l'essentiel d'un coup d'oeil ; le chiffre reste disponible dans
   l'infobulle et dans le panneau de diagnostic (un clic sur la pastille).
   `font-size: 0` supprime le noeud de texte sans toucher aux barres, qui ont
   des dimensions explicites (style.css l.713). */
.call-window:not(.fullscreen) .call-quality { font-size: 0; gap: 0; }

/* Les couleurs d'origine (style.css l.719) sont ecrites en dur ; on repasse
   sur les jetons d'etat, deja eclaircis pour tenir sur du noir. */
.call-quality.q1 { color: var(--cw-danger); }
.call-quality.q2 { color: var(--cw-warn); }
.call-quality.q3,
.call-quality.q4 { color: var(--cw-ok); }
/* Mauvaise qualite : la pastille se teinte, on ne compte pas sur les seules
   petites barres (trop fines pour etre vues du coin de l'oeil). */
.call-quality.q1 {
  background: color-mix(in srgb, var(--danger) 26%, var(--cw-veil-2));
  border-color: color-mix(in srgb, var(--danger) 50%, transparent);
}
.call-quality.q2 {
  background: color-mix(in srgb, var(--warn) 22%, var(--cw-veil-2));
  border-color: color-mix(in srgb, var(--warn) 44%, transparent);
}

/* --- Les trois boutons ----------------------------------------------------
   Ils ne doivent PAS se battre avec le contenu : voile tres discret au repos,
   surface franche au survol. */
.call-topbar-btns { flex: none; gap: 4px; }
.call-topbar .icon-btn.small {
  width: 32px;
  height: 32px;
  border-radius: 11px;
  background: var(--cw-veil);
  border: 1px solid transparent;
  color: var(--text);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  transition: transform var(--cw-fast) var(--cw-ease),
              background var(--cw-fast) ease,
              border-color var(--cw-fast) ease;
}
.call-topbar .icon-btn.small .ic { width: 16px; height: 16px; }
.call-topbar .icon-btn.small:hover {
  transform: translateY(-1px);
  background: var(--cw-veil-2);
  border-color: var(--cw-line-2);
}
.call-topbar .icon-btn.small:active { transform: scale(.92); transition-duration: 60ms; }
.call-topbar .icon-btn.small:focus-visible {
  outline: none;
  box-shadow:
    0 0 0 2px var(--sur-accent),
    0 0 0 4px color-mix(in srgb, var(--accent) 85%, transparent);
}


/* ===========================================================================
   3. LA SCENE SANS CAMERA — `#call-avatar`
   ---------------------------------------------------------------------------
   C'est l'ecran que l'on regarde le plus longtemps : pendant la sonnerie, et
   pendant tout un appel audio. Avant : un rond de 116 px au milieu du noir.
   Apres : un portrait pose dans son propre halo, aux couleurs de la personne.

   Le halo est obtenu SANS JavaScript : le pseudo-element herite du
   `background-image` de l'avatar (photo televersee OU degrade construit a
   partir de l'accent du contact par applyAvatar, app.js l.28), le floute, et
   un masque radial n'en garde que la couronne exterieure. La couleur vient
   donc reellement de la personne, sans qu'aucune valeur ne soit ecrite ici.
   =========================================================================== */
.call-avatar {
  /* Nappe douce derriere le portrait : la scene n'est plus un aplat noir. */
  background:
    radial-gradient(58% 52% at 50% 44%,
      color-mix(in srgb, var(--accent) 20%, transparent) 0%,
      color-mix(in srgb, var(--accent-2) 10%, transparent) 42%,
      transparent 72%);
  gap: 8px;
  /* Le remplissage reserve la barre du haut et le dock : le portrait est
     centre dans l'espace REELLEMENT libre, pas dans la fenetre entiere.
     Budget MESURE sur la fenetre de base (275 px de haut, dock haut de 92 px
     bord compris) : 275 - 24 - 96 = 155 px disponibles, pour 84 (portrait) +
     8 + 22 (nom) + 8 + 29 (etat) = 151 px. La ligne d'etat passait sous le
     dock avec un remplissage plus court. */
  padding: 24px 18px 96px;
  text-align: center;
  /* Filet de securite : si un nom tres long passait a la ligne, il serait
     coupe proprement plutot que de pousser l'avatar sous le dock. */
  overflow: hidden;
}

/* Quand la scene est l'avatar, une seule vignette peut s'afficher (celle du
   correspondant, masquee) — layoutCall met toute autre source en grand. Elle
   se pose en bas a DROITE : on decale la zone de centrage pour qu'un nom long
   ne passe pas dessous. Le portrait est alors centre dans l'espace reellement
   libre, ce qui est le comportement juste, pas un compromis. */
.call-videos:has(.tile.show) .call-avatar {
  padding-right: calc(var(--cw-tile-w) + 24px);
}

.call-avatar .avatar {
  width: var(--cw-avatar);
  height: var(--cw-avatar);
  font-size: calc(var(--cw-avatar) * .38);
  cursor: default;                 /* aucun clic n'y est attache */
  border: 2px solid color-mix(in srgb, var(--sur-accent) 22%, transparent);
  /* Anneau clair + ombre profonde : le portrait se decolle du fond. */
  box-shadow:
    0 0 0 8px color-mix(in srgb, var(--sur-accent) 5%, transparent),
    var(--cw-lift-2);
}

/* Le halo. `background-image: inherit` recopie la photo OU le degrade calcule
   pour le contact ; le masque radial n'affiche que ce qui deborde du portrait,
   donc aucun risque de recouvrir le visage. Le noir du masque est une valeur
   d'ALPHA (un masque ne lit que l'opacite), pas une couleur d'interface. */
.call-avatar .avatar::before {
  content: "";
  position: absolute;
  inset: -34%;
  border-radius: 50%;
  background-image: inherit;
  background-size: cover;
  background-position: center;
  filter: blur(30px) saturate(1.6);
  opacity: .55;
  -webkit-mask: radial-gradient(circle at 50% 50%, transparent 38%, #000 66%);
  mask: radial-gradient(circle at 50% 50%, transparent 38%, #000 66%);
  pointer-events: none;
}

.call-avatar #call-avatar-name {
  font-size: 1.05rem;
  font-weight: 800;
  letter-spacing: .2px;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Ligne d'etat sous le nom (« Appel en cours… », « Connecté »).
   Elle n'existe dans le DOM que si l'ajout decrit dans `integration` est
   applique ; sans lui, le statut reste visible dans la barre du haut et cette
   regle ne s'applique a rien. */
.call-avatar-state {
  font-size: .78rem;
  font-weight: 600;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--cw-veil);
  border: 1px solid var(--cw-line);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
.call-avatar-state:empty { display: none; }

/* Entree de scene : un seul passage, transform + opacity uniquement. Elle se
   rejoue quand l'avatar revient (le correspondant coupe sa camera), ce qui
   adoucit le basculement au lieu d'un remplacement sec. */
@keyframes cw-hero-in {
  from { opacity: 0; transform: scale(.92); }
}
@media (prefers-reduced-motion: no-preference) {
  .call-avatar:not(.hidden) {
    animation: cw-hero-in 340ms var(--cw-ease) backwards;
  }
}


/* ===========================================================================
   4. LES VIGNETTES — de vraies cartes video
   ---------------------------------------------------------------------------
   Avant : des rectangles ternes empiles a droite, sans hierarchie, sans dire
   ce qu'ils font, avec un libelle blanc nu qui disparaissait sur une image
   claire.
   Apres : des CARTES — rayon accorde a la fenetre, filet lumineux, ombre qui
   les detache de la video, libelle en pastille sombre lisible partout, et un
   survol qui annonce « clique pour mettre en grand ».

   `.tiles { bottom: … }` est laisse a polish.css (il suit la hauteur du dock,
   qui a change avec le bouton RACCROCHER) : on n'ajuste ici que le cote et
   l'espacement.
   =========================================================================== */
/* BANDEAU, pas colonne. Mesure faite sur la fenetre de base (440 x 275) :
   trois cartes empilees font 242 px de haut alors qu'il n'en reste que 179
   au-dessus du dock — la premiere sortait par le haut, coupee par la barre.
   Couchees, les memes trois cartes tiennent dans 374 px sur 414 disponibles.
   La fenetre d'appel est toujours plus LARGE que haute : le bandeau est donc
   la seule disposition qui tienne dans les trois modes. */
.tiles {
  flex-direction: row;
  align-items: flex-end;
  justify-content: flex-end;
  left: var(--cw-pad);
  right: var(--cw-pad);
  gap: 10px;
}

/* TROIS cartes peuvent coexister (camera du correspondant + ma camera + mon
   ecran) : a 150 px chacune le bandeau ferait 470 px pour 414 disponibles. On
   les retrecit donc des qu'elles sont trois. Le selecteur lit « une carte
   affichee, suivie d'une deuxieme, suivie d'une troisieme » : l'ordre du DOM
   est fixe, il est donc fiable.
   Le calcul est FLUIDE plutot que chiffre : `100%` se resout sur `.tiles`,
   qui est le bloc conteneur des cartes, donc « un tiers de la place moins les
   deux ecarts » est exact a toute largeur de fenetre — y compris sur un
   telephone, ou la fenetre tombe a 92vw. Le `min()` empeche seulement les
   cartes de grossir au-dela de leur taille nominale. */
.call-window:not(.mini):not(.fullscreen) .tiles:has(.tile.show ~ .tile.show ~ .tile.show) {
  --cw-tile-w: min(118px, calc((100% - 20px) / 3));
}
.call-window.fullscreen .tiles:has(.tile.show ~ .tile.show ~ .tile.show) {
  --cw-tile-w: min(170px, calc((100% - 28px) / 3));
}
.call-window.mini .tiles:has(.tile.show ~ .tile.show ~ .tile.show) {
  --cw-tile-w: min(68px, calc((100% - 12px) / 3));
}

.tile {
  width: var(--cw-tile-w);
  /* Jamais de compression : une carte ecrasee trahirait le cadrage video. */
  flex: none;
  border-radius: var(--cw-tile-r);
  /* Le rayon des cartes est volontairement plus petit que celui de la fenetre
     (--radius, 24px) : une petite surface avec un grand rayon parait molle. */
  border: 1px solid var(--cw-line-2);
  background: var(--cw-void);
  /* Trois plans : ombre large (detachement de la video), ombre courte
     (contact), lisere interieur (arete du verre). */
  box-shadow:
    var(--cw-lift),
    0 2px 6px color-mix(in srgb, var(--pol-ink, #05050c) 55%, transparent),
    inset 0 1px 0 color-mix(in srgb, var(--sur-accent) 16%, transparent);
  cursor: pointer;
  transition:
    transform var(--cw-base) var(--cw-ease),
    box-shadow var(--cw-base) ease,
    border-color var(--cw-base) ease;
}

/* Isolation : sans elle, le `filter` de la video masquee (section 5) creerait
   un contexte d'empilement qui passerait DEVANT le libelle. */
.tile { isolation: isolate; }
/* Meme raisonnement que pour le libelle « Agrandir » : tant qu'aucun
   gestionnaire de clic n'est attache, la main du curseur (posee par style.css
   l.903) annonce une action qui n'existe pas. Elle revient d'elle-meme des que
   la vignette devient focalisable. */
.tile:not([tabindex]) { cursor: default; }
.tile video { display: block; }
.tile-label, .tile-mask { z-index: 4; }

/* Ma propre camera est retournee, comme un miroir — c'est ce que fait deja la
   scene locale (polish.css l.621) et c'est ce que tout le monde attend d'une
   auto-vue. Le partage d'ecran, lui, ne doit surtout pas l'etre. */
#tile-local-cam video { transform: scaleX(-1); }

/* --- Survol : « clique pour mettre en grand » ------------------------------
   Le voile descend du haut (il ne cache pas le libelle, pose en bas) et porte
   le mot d'action. `::before` est reserve a ce role, `::after` a l'etat masque.

   POURQUOI `[tabindex]` : aujourd'hui AUCUN gestionnaire de clic n'est attache
   aux vignettes (app.js n'ecoute que `#btn-swap` et `#tile-mask-btn`). Afficher
   « Agrandir » au survol serait donc une PROMESSE NON TENUE — le pire defaut
   qu'une interface puisse avoir. Le libelle n'apparait donc que sur une
   vignette reellement actionnable, c'est-a-dire focalisable ; l'ajout d'un
   `tabindex="0"` + du gestionnaire est decrit dans `integration`. Sans lui, le
   survol garde le soulevement et le contour d'accent, qui disent « element
   vivant » sans rien promettre. */
.tile[tabindex]::before {
  content: "Agrandir";
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 22%;
  font-size: .66rem;
  font-weight: 800;
  letter-spacing: .3px;
  color: var(--sur-accent);
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--pol-ink, #05050c) 66%, transparent) 0%,
    color-mix(in srgb, var(--pol-ink, #05050c) 20%, transparent) 62%,
    transparent 100%);
  opacity: 0;
  transition: opacity var(--cw-fast) ease;
  pointer-events: none;
}
.tile:hover,
.tile:focus-visible {
  transform: translateY(-4px) scale(1.035);
  border-color: color-mix(in srgb, var(--accent) 70%, transparent);
  box-shadow:
    var(--cw-lift-2),
    0 0 0 1px color-mix(in srgb, var(--accent) 45%, transparent),
    0 0 26px color-mix(in srgb, var(--accent) 30%, transparent);
}
/* `:focus-visible` ne peut de toute facon matcher qu'un element focalisable :
   il se limite tout seul aux vignettes actionnables. */
.tile[tabindex]:hover::before,
.tile:focus-visible::before { opacity: 1; }
.tile:active { transform: translateY(-1px) scale(.99); transition-duration: 60ms; }
.tile:focus-visible { outline: none; }

/* --- Libelle : lisible SUR N'IMPORTE QUELLE image --------------------------
   Une pastille sombre floutee, jamais du texte blanc nu. Elle se tronque au
   lieu de deborder (« Toi · masquée chez Untel » est un libelle long). */
.tile-label {
  left: 7px;
  bottom: 6px;
  /* La pastille epouse son texte ; `max-width` (et non un `right`, qui serait
     ignore des que la largeur est fixee) l'empeche de sortir de la carte. */
  width: fit-content;
  max-width: calc(100% - 14px);
  font-size: .64rem;
  font-weight: 700;
  letter-spacing: .2px;
  line-height: 1.35;
  padding: 2px 7px;
  border-radius: 8px;
  color: var(--sur-accent);
  background: var(--cw-veil-2);
  border: 1px solid color-mix(in srgb, var(--sur-accent) 12%, transparent);
  backdrop-filter: blur(8px) saturate(140%);
  -webkit-backdrop-filter: blur(8px) saturate(140%);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tile-label:empty { display: none; }

/* --- Bouton « masquer du partage » ---------------------------------------- */
.tile-mask {
  right: 6px;
  top: 6px;
  width: 26px;
  height: 26px;
  border-radius: 9px;
  background: var(--cw-veil-2);
  border: 1px solid color-mix(in srgb, var(--sur-accent) 14%, transparent);
  color: var(--sur-accent);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: transform var(--cw-fast) var(--cw-ease), background var(--cw-fast) ease;
}
.tile-mask .ic { width: 14px; height: 14px; }
.tile-mask:hover { transform: scale(1.08); background: var(--cw-veil-3); }
.tile-mask:active { transform: scale(.9); transition-duration: 60ms; }
.tile-mask:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--sur-accent), 0 0 0 4px color-mix(in srgb, var(--accent) 80%, transparent);
}
/* Actif : le degrade ASSOMBRI (--degrade-txt, style.css l.939) et non l'accent
   vif — c'est le seul qui garde une icone blanche au-dessus de 4,5:1 sur les
   sept palettes. */
.tile-mask.on {
  background: var(--degrade-txt);
  border-color: color-mix(in srgb, var(--sur-accent) 30%, transparent);
  box-shadow: 0 0 16px color-mix(in srgb, var(--accent) 45%, transparent);
}

/* --- Entree d'une vignette -------------------------------------------------
   `.show` n'est pose qu'une fois (classList.toggle ne re-ajoute pas une classe
   deja presente) : l'animation joue a l'apparition, puis plus jamais. */
@keyframes cw-tile-in {
  from { opacity: 0; transform: translateY(12px) scale(.9); }
}
@media (prefers-reduced-motion: no-preference) {
  .tile.show { animation: cw-tile-in var(--cw-base) var(--cw-ease) backwards; }
}


/* ===========================================================================
   5. ETAT « MASQUEE » — explicite, pas juste flou
   ---------------------------------------------------------------------------
   Quand on partage son ecran entier, la camera du correspondant est masquee
   pour ne pas la lui renvoyer en boucle. Avant : `blur(18px)` + un emoji 🔒
   pose au milieu — on croyait a un bug d'image.
   Apres : l'image est brouillee ET recouverte d'un voile teinte a l'accent,
   avec un cartouche qui NOMME l'etat. Le contour passe en accent pour dire
   que c'est un choix, pas une panne.
   =========================================================================== */
.tile.masked {
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
}
.tile.masked video {
  /* `scale` compense les bords delaves que le flou laisse apparaitre. */
  filter: blur(16px) brightness(.42) saturate(.65);
  transform: scale(1.18);
}
/* Le miroir de l'auto-vue doit survivre au masquage (les deux touchent
   `transform` : la regle la plus specifique doit rejouer les deux).
   GARDE-FOU : aujourd'hui `.masked` n'est pose que sur `#tile-remote-cam`
   (app.js l.2149) — cette ligne ne sert donc a rien TANT QUE le masquage reste
   a sens unique. On la garde parce qu'elle coute une ligne et qu'elle evite un
   defaut silencieux (auto-vue qui se retourne) le jour ou le protocole
   masquera aussi notre propre camera, comme le libelle « Toi · masquée chez … »
   le laisse deja entendre. */
#tile-local-cam.masked video { transform: scaleX(-1) scale(1.18); }

.tile.masked::after {
  content: "🔒 Masquée";
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: .62rem;
  font-weight: 800;
  letter-spacing: .2px;
  color: var(--sur-accent);
  /* Voile teinte : la teinte accent signe « masquage volontaire ». */
  background:
    radial-gradient(70% 70% at 50% 50%,
      color-mix(in srgb, var(--accent) 22%, transparent), transparent 78%),
    color-mix(in srgb, var(--pol-ink, #05050c) 34%, transparent);
  /* Le cartouche est dessine par les proprietes de texte du pseudo-element
     lui-meme : pas besoin d'un second element. */
  text-shadow: 0 1px 4px color-mix(in srgb, var(--pol-ink, #05050c) 90%, transparent);
  pointer-events: none;
}
/* Sur une vignette masquee, « Agrandir » serait un mensonge : layoutCall
   (app.js) refuse justement de mettre une camera masquee en grand. */
.tile.masked:hover::before,
.tile.masked:focus-visible::before { opacity: 0; }
.tile.masked:hover {
  border-color: color-mix(in srgb, var(--accent) 80%, transparent);
}

/* --- Le rappel « Caméra masquée » -----------------------------------------
   Il reste en HAUT a gauche, mais sous le degrade de la barre : le bas de la
   fenetre appartient desormais au bandeau de vignettes et au dock. Sa largeur
   est bornee pour qu'il ne rencontre jamais le panneau de diagnostic, qui
   occupe le haut a droite (section 6). */
/* Un seul selecteur suffit : aucune regle de theme clair ne cible
   `.mask-notice` (style.css l.979 la traite a specificite egale, hors theme),
   l'ordre de chargement tranche donc. Reprendre ici les selecteurs de theme
   n'aurait rien battu de plus. */
.mask-notice {
  left: var(--cw-pad);
  top: calc(var(--cw-topbar-h) + 14px);
  bottom: auto;
  gap: 10px;
  align-items: flex-start;
  padding: 10px 12px;
  border-radius: 14px;
  background: var(--cw-veil-3);
  border: 1px solid var(--cw-line);
  box-shadow: var(--cw-lift);
  backdrop-filter: blur(14px) saturate(150%);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  color: var(--sur-accent);
  text-shadow: none;            /* le fond opaque suffit, l'ombre salissait */
  max-width: 208px;
}
.mask-notice .ic { color: var(--cw-accent); margin-top: 1px; }
.mask-notice b { font-size: .78rem; font-weight: 800; }
.mask-notice .muted { color: var(--muted); line-height: 1.4; }

/* Hors plein ecran, seul le TITRE reste : la phrase d'explication faisait
   trois lignes et le cartouche descendait sur le bandeau de vignettes. Entre
   la barre du haut et les vignettes, il n'y a que ~45 px — juste de quoi
   loger « Caméra masquée », qui suffit a comprendre. L'explication complete
   revient en plein ecran, ou la place existe. */
.call-window:not(.fullscreen) .mask-notice .muted { display: none; }
.call-window:not(.fullscreen) .mask-notice { align-items: center; }

/* Le rappel et le panneau de diagnostic occupent la MEME bande, juste sous la
   barre du haut, et rien n'empeche les deux d'etre affiches en meme temps
   (masquage automatique pendant un partage + clic sur la pastille de qualite).
   Hors plein ecran le panneau prend toute la largeur : ils se recouvraient.
   Arbitrage : le panneau vient d'etre DEMANDE d'un clic, il prime. Et
   l'information du rappel n'est pas perdue — la vignette concernee porte deja
   « 🔒 Masquée » et son libelle le repete. Le rappel revient de lui-meme des
   que le panneau se referme.
   Regle non conditionnee au mode : en plein ecran sur un telephone, les deux
   cartouches ne tiennent pas non plus cote a cote. */
.call-videos:has(.call-diag:not(.hidden)) .mask-notice { display: none; }


/* ===========================================================================
   6. PANNEAU DE DIAGNOSTIC
   ---------------------------------------------------------------------------
   Avant : un `<table>` brut sur un rectangle noir, sans rapport avec le reste
   de l'application.
   Apres : une carte de la meme famille que les autres panneaux (verre sombre,
   rayon, filet, ombre), un en-tete titre, des lignes separees par un filet, et
   des marqueurs ✓/✗ transformes en pastilles d'etat.
   Il s'ouvre d'un clic sur l'indicateur de QUALITE, en haut a droite : on le
   pose juste dessous, ce qui rend le lien evident. Le coin haut-gauche reste
   au rappel « Caméra masquée » et le bas au bandeau de vignettes.
   =========================================================================== */
/* Meme remarque que pour `.mask-notice` : rien en theme clair ne cible
   `.call-diag`, un selecteur simple suffit. */
.call-diag {
  left: auto;
  right: var(--cw-pad);
  top: calc(var(--cw-topbar-h) + 14px);
  padding: 0;
  border-radius: 16px;
  background: var(--cw-veil-3);
  border: 1px solid var(--cw-line);
  box-shadow: var(--cw-lift-2);
  backdrop-filter: blur(16px) saturate(150%);
  -webkit-backdrop-filter: blur(16px) saturate(150%);
  color: var(--sur-accent);
  /* Le panneau s'arrete AU DOCK, pas aux vignettes : c'est l'utilisateur qui
     vient de l'ouvrir, il est donc legitime qu'il recouvre une vignette le
     temps de la lecture — alors que recouvrir les commandes serait une faute.
     Au-dela, il defile. */
  max-height: calc(100% - var(--cw-topbar-h) - 104px);
  overflow: auto;
  font-size: .69rem;
}

/* Hors plein ecran, le panneau prend TOUTE la largeur utile. Les cellules du
   tableau sont en `white-space: nowrap` (style.css l.796) : dans une carte de
   214 px, la colonne « je reçois » sortait du cadre et le panneau se mettait a
   defiler horizontalement. La largeur disponible, elle, ne manque pas. */
/* … et il se compacte, pour que les trois pistes (audio, camera, ecran)
   tiennent d'un seul tenant entre la barre et le dock, sans defilement. */
.call-window:not(.fullscreen) .call-diag {
  left: var(--cw-pad);
  max-width: none;
  top: calc(var(--cw-topbar-h) + 8px);
  max-height: calc(100% - var(--cw-topbar-h) - 96px);
  font-size: .62rem;
  line-height: 1.35;
}
.call-window:not(.fullscreen) .call-diag .dg-t { padding: 6px 11px; font-size: .62rem; }
.call-window:not(.fullscreen) .call-diag td { padding: 2px 6px; }
.call-window:not(.fullscreen) .call-diag tr:first-child td { padding-top: 4px; font-size: .58rem; }
.call-window:not(.fullscreen) .call-diag .dg-foot { padding: 4px 11px 5px; font-size: .6rem; }
.call-window:not(.fullscreen) .call-diag .ok,
.call-window:not(.fullscreen) .call-diag .ko { width: 13px; height: 13px; font-size: .54rem; }

/* En-tete : titre + icone d'accent, separe du tableau par un filet. */
.call-diag .dg-t {
  margin: 0;
  padding: 9px 12px;
  font-size: .68rem;
  font-weight: 800;
  letter-spacing: .5px;
  text-transform: uppercase;
  color: var(--text);
  border-bottom: 1px solid var(--cw-line);
  gap: 7px;
}
.call-diag .dg-t .ic { width: 14px; height: 14px; color: var(--cw-accent); }

.call-diag table { margin: 0; padding: 0 6px; }
.call-diag td {
  padding: 5px 6px;
  border-bottom: 1px solid color-mix(in srgb, var(--sur-accent) 7%, transparent);
  vertical-align: middle;
}
.call-diag tr:last-child td { border-bottom: none; }
/* Premiere colonne = le nom de la piste : c'est la cle de lecture. */
.call-diag td:first-child { font-weight: 700; width: 42%; }
/* Les deux colonnes de valeurs sont chiffrees : alignees a droite et
   tabulaires, elles se comparent d'un coup d'oeil. */
.call-diag td + td { text-align: right; font-variant-numeric: tabular-nums; }
/* En-tete de colonnes (« j'envoie » / « je reçois »), marque `.na` par app.js. */
.call-diag tr:first-child td {
  font-size: .62rem;
  font-weight: 700;
  letter-spacing: .3px;
  text-transform: uppercase;
  color: var(--muted);
  padding-top: 8px;
}

/* Marqueurs : des pastilles rondes plutot que deux glyphes flottants. */
.call-diag .ok,
.call-diag .ko {
  display: inline-grid;
  place-items: center;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  font-size: .58rem;
  font-weight: 900;
  line-height: 1;
  margin-right: 3px;
  vertical-align: -2px;
}
.call-diag .ok {
  color: var(--cw-ok);
  background: color-mix(in srgb, var(--ok) 22%, transparent);
}
.call-diag .ko {
  color: var(--cw-danger);
  background: color-mix(in srgb, var(--danger) 24%, transparent);
}
.call-diag .na { color: var(--muted); }

/* Pied : la mesure detaillee, sur une surface creusee qui la met a part. */
.call-diag .dg-foot {
  margin: 0;
  padding: 7px 12px 9px;
  border-top: 1px solid var(--cw-line);
  background: color-mix(in srgb, var(--pol-ink, #05050c) 40%, transparent);
  color: var(--muted);
  font-size: .64rem;
  line-height: 1.45;
}
.call-diag .dg-foot:empty { display: none; }


/* ===========================================================================
   7. LES ETATS
   ---------------------------------------------------------------------------
   Trois etats se lisent SANS aucune modification de JavaScript, parce que les
   classes existent deja :
     • mauvaise qualite  → `.call-quality.q1/.q2` (section 2) ;
     • micro coupe       → `.control.off` (polish.css, deja traite) ;
     • camera masquee    → `.tile.masked` (section 5).

   Deux autres demandent un attribut que setCallStatus() ne pose pas encore :
   `[data-state]` sur `#call-status` et le badge `#call-peer-mute`. Les regles
   sont ecrites ici pour etre pretes ; l'ajout exact est decrit dans
   `integration`. Sans lui, le statut garde son apparence neutre — rien ne
   casse.
   =========================================================================== */

/* Connexion / reconnexion : la puce passe a l'accent puis a l'ambre. Aucune
   animation en boucle — c'est la COULEUR qui porte l'information, pas le
   mouvement (un clignotement pendant une reconnexion de 30 s serait un
   supplice, et l'element est visible en permanence). */
.call-status[data-state="connecting"] { color: var(--cw-accent); }
.call-status[data-state="connecting"]::before { opacity: 1; }

.call-status[data-state="reconnecting"] { color: var(--cw-warn); }
.call-status[data-state="reconnecting"]::before {
  opacity: 1;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn) 28%, transparent);
}

.call-status[data-state="connected"] { color: var(--cw-ok); }
.call-status[data-state="connected"]::before { opacity: 1; }

/* Panne franche (« Micro indisponible », « Caméra indisponible »…) : c'est le
   seul etat qui merite un fond, parce qu'il demande une action. */
.call-status[data-state="error"] {
  color: var(--sur-accent);
  padding: 3px 9px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--danger) 46%, var(--cw-veil-2));
  border: 1px solid color-mix(in srgb, var(--danger) 62%, transparent);
  text-shadow: none;
}
.call-status[data-state="error"]::before { background: var(--sur-accent); opacity: 1; }

/* Correspondant muet : un bandeau discret sous la barre du haut. Il ne prend
   la place de rien (la scene est derriere) et disparait des qu'il reparle. */
.call-peer-mute {
  position: absolute;
  z-index: 5;
  left: 50%;
  top: calc(var(--cw-topbar-h) + 10px);
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 12px;
  border-radius: 999px;
  font-size: .7rem;
  font-weight: 700;
  white-space: nowrap;
  color: var(--sur-accent);
  background: var(--cw-veil-2);
  border: 1px solid color-mix(in srgb, var(--danger) 45%, transparent);
  box-shadow: var(--cw-lift);
  backdrop-filter: blur(12px) saturate(140%);
  -webkit-backdrop-filter: blur(12px) saturate(140%);
  pointer-events: none;
}
.call-peer-mute .ic { width: 15px; height: 15px; color: var(--cw-danger); }


/* ===========================================================================
   8. MODE REDUIT — `.call-window.mini`
   ---------------------------------------------------------------------------
   248 px de large : tout doit retrecir ENSEMBLE, sinon la barre du haut
   deborde et les vignettes recouvrent le dock. On ne retire aucun bouton (le
   correspondant doit pouvoir passer en plein ecran ou revenir sans devoir
   d'abord agrandir) : on retire ce qui est REDONDANT — le statut, qui est
   secondaire, et l'indicateur de qualite, illisible a cette taille.
   Les positions verticales du dock et des vignettes restent celles de
   polish.css.
   =========================================================================== */
.call-window.mini {
  /* 6 + 26 (bouton) + 6 : la hauteur reelle de la barre reduite. */
  --cw-topbar-h: 38px;
  --cw-tile-w: 68px;
  --cw-avatar: 52px;
  --cw-pad: 8px;
  --cw-tile-r: 11px;
  border-radius: var(--radius-sm);
}
.call-window.mini .call-topbar { padding: 6px var(--cw-pad); gap: 6px; }
.call-window.mini .call-topbar::after { inset: 0 0 -18px 0; }
.call-window.mini .call-peer-name { font-size: .76rem; max-width: 72px; }
.call-window.mini .call-status,
.call-window.mini .call-quality,
.call-window.mini .call-peer-mute { display: none; }
.call-window.mini .call-timer {
  font-size: .68rem;
  padding: 2px 6px;
  min-width: 5ch;
}
.call-window.mini .call-topbar-btns { gap: 3px; }
.call-window.mini .call-topbar .icon-btn.small { width: 26px; height: 26px; border-radius: 9px; }
.call-window.mini .call-topbar .icon-btn.small .ic { width: 14px; height: 14px; }
/* Trois cartes de 68 px + les ecarts = 216 px sur 232 disponibles : le
   bandeau tient, tout juste. */
/* +6 px par rapport a style.css l.826 : le dock reduit monte a 60 px du bas,
   les vignettes n'avaient plus que 2 px de garde et leur etiquette passait
   derriere la pilule. On ne touche pas au dock, seulement a cette garde. */
.call-window.mini .tiles { gap: 6px; bottom: 68px; }
/* style.css fixe deux fois la largeur des vignettes reduites (l.723 : 74 px,
   l.916 : 92 px) avec la specificite (0,2,0) : une regle `.tile` simple ne
   peut pas la battre, l'ordre de chargement n'y change rien. On repasse donc
   par le jeton, a specificite egale. */
.call-window.mini .tile { width: var(--cw-tile-w); }
.call-window.mini .tile-label { font-size: .55rem; padding: 1px 5px; border-radius: 6px; }
/* A 78 px, le libelle d'action ne serait pas lisible et le bouton de masquage
   deviendrait une cible impossible a viser. */
.call-window.mini .tile::before { display: none; }
.call-window.mini .tile-mask { width: 20px; height: 20px; border-radius: 7px; }
.call-window.mini .tile-mask .ic { width: 11px; height: 11px; }
.call-window.mini .tile.masked::after { font-size: 0; }   /* le voile reste, le texte non */
/* Meme calcul qu'en taille normale : 186 - 16 - 66 = 104 px utiles pour
   52 + 5 + 16 + 5 + 20 = 98 px. */
.call-window.mini .call-avatar { padding: 16px 10px 66px; gap: 5px; }
.call-window.mini .call-avatar #call-avatar-name { font-size: .82rem; }
.call-window.mini .call-avatar-state { font-size: .66rem; padding: 3px 9px; }
.call-window.mini .call-diag { display: none; }   /* illisible a cette taille */


/* ===========================================================================
   9. PLEIN ECRAN — `.call-window.fullscreen`
   ---------------------------------------------------------------------------
   Correction d'un vrai defaut : la fenetre est deplacable, et makeDraggable()
   (app.js) ecrit `left/top` EN LIGNE. Ces valeurs battent le `inset: 0` de
   `.fullscreen` (style.css l.721) : une fenetre deplacee puis passee en plein
   ecran restait collee a sa derniere position, decalee hors de l'ecran. Le
   `!important` est ici le seul moyen de reprendre la main sur du style en
   ligne — c'est sa justification.
   =========================================================================== */
.call-window.fullscreen {
  /* 12 + 38 (bouton) + 12 : la hauteur reelle de la barre en plein ecran. */
  --cw-topbar-h: 62px;
  --cw-tile-w: 208px;
  --cw-avatar: 176px;
  --cw-pad: 22px;
  --cw-tile-r: 20px;
  left: 0 !important;
  top: 0 !important;
  right: 0 !important;
  bottom: 0 !important;
  width: 100vw;
  /* `dvh` suit la barre d'adresse mobile ; `vh` reste le repli. */
  height: 100vh;
  height: 100dvh;
  border-radius: 0;
  border: none;
}
.call-window.fullscreen .call-topbar {
  padding: 12px var(--cw-pad);
  padding-top: max(12px, env(safe-area-inset-top));
  gap: 12px;
}
.call-window.fullscreen .call-topbar::after { inset: 0 0 -44px 0; }
.call-window.fullscreen .call-peer-name { font-size: 1.05rem; max-width: 40vw; }
.call-window.fullscreen .call-status { font-size: .82rem; }
.call-window.fullscreen .call-timer { font-size: .86rem; padding: 4px 11px; }
.call-window.fullscreen .call-topbar .icon-btn.small { width: 38px; height: 38px; border-radius: 13px; }
.call-window.fullscreen .call-topbar .icon-btn.small .ic { width: 19px; height: 19px; }
.call-window.fullscreen .tiles { gap: 14px; }
.call-window.fullscreen .tile-label { font-size: .72rem; padding: 3px 9px; }
.call-window.fullscreen .tile::before { font-size: .76rem; }
.call-window.fullscreen .tile-mask { width: 32px; height: 32px; border-radius: 11px; }
.call-window.fullscreen .tile-mask .ic { width: 17px; height: 17px; }
/* Le dock est plus haut en plein ecran (RACCROCHER passe a 60 px) : le
   remplissage bas suit, sinon la ligne d'etat s'y glisse dessous. */
.call-window.fullscreen .call-avatar { gap: 14px; padding: 40px 32px 128px; }
.call-window.fullscreen .call-avatar #call-avatar-name { font-size: 1.7rem; }
.call-window.fullscreen .call-avatar-state { font-size: .92rem; padding: 7px 16px; }
.call-window.fullscreen .mask-notice { max-width: 320px; }
.call-window.fullscreen .call-diag { max-width: 330px; font-size: .76rem; }


/* ===========================================================================
   10. ECRANS ETROITS
   ---------------------------------------------------------------------------
   `.call-window` fait `min(440px, 92vw)` : sur un telephone, la fenetre occupe
   presque toute la largeur mais le dock reste large. On resserre les cartes
   pour qu'elles ne recouvrent jamais la scene, et on rend le libelle d'action
   inutile (il n'y a pas de survol au doigt).
   =========================================================================== */
@media (max-width: 560px) {
  .call-window:not(.mini):not(.fullscreen) {
    --cw-tile-w: 112px;
    --cw-avatar: 100px;
    /* --cw-topbar-h n'est PAS retouche : la barre ne change pas de hauteur
       ici (memes boutons, meme remplissage vertical), et un jeton qui mentirait
       ferait passer le diagnostic sous la zone de prise de la fenetre. */
    --cw-pad: 10px;
  }
  .call-window:not(.mini) .call-peer-name { max-width: 34%; }
  /* Le trio nom + statut + chrono ne tient plus : le statut cede la place,
     il est deja repete sur la scene quand la camera est coupee. */
  .call-window:not(.mini):not(.fullscreen) .call-status { display: none; }
  .call-window:not(.mini) .mask-notice { max-width: 46%; }
  /* Pas de plafond sur `.call-diag` ici : hors plein ecran il est deja borne
     par `left`+`right`, et un `max-width` le renverrait a une carte etroite ou
     les cellules en `white-space: nowrap` (style.css l.796) partent en
     defilement horizontal — exactement le defaut que la pleine largeur corrige. */
}

/* Ecran bas de plafond (fenetre en paysage sur telephone) : l'avatar ne doit
   pas pousser le dock hors du cadre. */
@media (max-height: 480px) {
  /* La barre garde ses boutons de 38 px : sa hauteur ne bouge pas, seul
     l'avatar et les vignettes cedent de la place. */
  .call-window.fullscreen { --cw-avatar: 96px; --cw-tile-w: 148px; }
  .call-window.fullscreen .tiles:has(.tile.show ~ .tile.show ~ .tile.show) { --cw-tile-w: 116px; }
  .call-window.fullscreen .call-avatar { padding-bottom: 104px; gap: 10px; }
  .call-window.fullscreen .call-avatar #call-avatar-name { font-size: 1.1rem; }
}

/* Au doigt, `:hover` reste « colle » apres un appui : la carte resterait
   soulevee et le libelle « Agrandir » affiche pour toujours. */
@media (hover: none) {
  .tile:hover { transform: none; }
  /* `[tabindex]` est repris a l'identique : sans lui cette regle serait MOINS
     specifique que celle qui allume le voile, et ne l'eteindrait pas. */
  .tile[tabindex]:hover::before { opacity: 0; }
  .tile-mask:hover { transform: none; }
  .call-topbar .icon-btn.small:hover { transform: none; }
  /* Le focus clavier, lui, reste pertinent (clavier bluetooth sur tablette). */
  .tile:focus-visible::before { opacity: 1; }
}


/* ===========================================================================
   11. MOUVEMENT REDUIT
   ---------------------------------------------------------------------------
   Tout ce qui bouge est neutralise : entrees de scene, soulevement des cartes,
   reactions a l'appui. Les CHANGEMENTS D'ETAT (couleur, voile, ombre) sont
   conserves — ce sont eux qui portent l'information, ils ne sont pas du
   mouvement.
   =========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .call-avatar:not(.hidden),
  .tile.show { animation: none; }

  .tile,
  .tile-mask,
  .call-topbar .icon-btn.small,
  .call-quality { transition: none; }

  .tile:hover,
  .tile:focus-visible,
  .tile:active,
  .tile-mask:hover,
  .tile-mask:active,
  .call-topbar .icon-btn.small:hover,
  .call-topbar .icon-btn.small:active { transform: none; }

  /* Le voile d'action apparait toujours, mais sans fondu. Meme remarque sur
     `[tabindex]` : a specificite egale, la derniere regle gagne. */
  .tile[tabindex]::before { transition: none; }
}

/* ---------------------------------------------------------------------------
   Le bandeau « Micro coupé » etait ancre sous la barre du haut : dans une
   petite fenetre il tombait EN PLEIN SUR le visage de l'avatar (les deux sont
   centres). On le pose en bas de la scene, juste au-dessus du dock, la ou rien
   d'autre ne se trouve — meme emplacement que l'avis « camera masquee ».
   --------------------------------------------------------------------------- */
#call-peer-mute {
  top: auto;
  bottom: calc(var(--pol-dock-h, 74px) + 12px);
  left: 50%;
  transform: translateX(-50%);
}
/* En mode reduit le dock est plus bas : on remonte d'autant. */
.call-window.mini #call-peer-mute { bottom: calc(var(--pol-dock-h, 58px) + 8px); font-size: .72rem; }
