/* CE QUE TOUTES LES VUES EMPRUNTENT : avatars, feuilles et voiles,
   utilitaires de mise en page, clavier de mots du chat.
   Charge apres jeu.css (cf. l'en-tete de base.css). */

/* ============================ avatars ============================ */

/* Les portraits sont detoures : le fond n'est pas dans l'image, il est pose
   ici. En mode equipe il prend la couleur de la troupe (--av-bg), ce qui
   permet de reconnaitre un equipier a la pastille avant de lire son nom. Le
   degrade donne du relief a n'importe quelle teinte, sans avoir a preparer une
   variante claire et une variante foncee de chaque couleur. */
.avatar {
  width: 44px; height: 44px; border-radius: 14px; overflow: hidden; flex: none;
  box-shadow: var(--shadow-1), var(--bevel);
  border: 1px solid rgba(238,241,230,0.14);
  background-color: var(--av-bg, rgba(12, 30, 22, 0.8));
  background-image:
    radial-gradient(circle at 50% 22%, rgba(255,255,255,0.28), rgba(255,255,255,0) 62%),
    linear-gradient(180deg, rgba(0,0,0,0) 48%, rgba(0,0,0,0.26));
}
.avatar img, .avatar svg { width: 100%; height: 100%; display: block; object-fit: cover; }
.avatar.round { border-radius: 50%; }
.avatar.lg { width: 76px; height: 76px; border-radius: 22px; }

/* Les portraits sont detoures : sans plaque lisible dessous, le choix d'avatar
   avait l'air de servir les anciennes vignettes a fond noir — le fond par
   defaut se confond avec le panneau. Tant qu'aucune troupe n'est choisie, la
   pastille prend une pierre a lichen : neutre, elle ne se fait pas passer pour
   une couleur d'equipe. La variable se pose sur le CONTENEUR, jamais sur la
   case : posee sur la case, elle battrait la couleur de la troupe. */
.avatar-picker { display: grid; grid-template-columns: repeat(6, 1fr); gap: 7px; padding: 2px; --av-bg: #6d7a63; }
.avatar-picker .avatar { width: 100%; height: auto; aspect-ratio: 1; cursor: pointer; transition: transform 0.16s var(--ease), box-shadow 0.2s; }
.avatar-picker .avatar.sel { box-shadow: 0 0 0 2px var(--moss), var(--glow-moss); transform: translateY(-3px); }

/* Inscription : le volet visible descend jusqu'au bas de l'ecran, marge
   comprise. Les choix (portrait, couleur, motif) se paginent lateralement au
   lieu de s'empiler, ce qui garde les boutons a portee de pouce au lieu de les
   repousser hors champ. */
#screen-onboard .scroller { display: flex; flex-direction: column; }
#screen-onboard .brand-head { flex: none; }
/* Le nom du terrain se lit comme la seconde ligne du logo : colle dessous. */
#screen-onboard .wordmark-sub { margin-top: -6px; }
/* Sur un ecran court, le logo prend la place des choix : il se rabat sur le
   titre seul, le sous-titre du terrain etant deja repete plus bas. */
@media (max-height: 660px) {
  #screen-onboard .brand-head { margin-bottom: 8px; }
  #screen-onboard .brand-title { width: min(48%, 176px); }
  #screen-onboard .wordmark-sub { font-size: 0.78rem; margin-top: -4px; }
}
#ob-self, #ob-create, #ob-build, #ob-team, #ob-new, #ob-code {
  display: flex; flex-direction: column;
  flex: 1; min-height: 0;
}
/* Les boutons du volet sont amarres au bas du panneau ; seul le corps defile.
   Sur un petit telephone, "Creer l'equipe" se retrouvait sinon deux ecrans
   plus bas que le dernier champ. */
.pane-body {
  display: flex; flex-direction: column;
  flex: 1; min-height: 0;
  overflow-y: auto; -webkit-overflow-scrolling: touch;
}
.pane-foot { flex: none; }
/* Une case a cocher a description tient sur trois lignes : centree, la case se
   retrouvait au milieu du paragraphe, en face de rien. Elle se cale sur la
   premiere ligne, celle qu'elle commande. */
#n-preset-wrap { align-items: flex-start; }
#n-preset-wrap input { margin-top: 2px; }

/* Champ centre : sur un ecran ou tout le reste est centre, un libelle et sa
   rangee de pastilles alignes a gauche cassent l'axe. */
.center-field { text-align: center; }
.center-field .chip-row { justify-content: center; }
.center-field input { text-align: center; }

/* Un corps plus court que la place disponible se centre au lieu de se coller
   en haut : sur "Fonder une aventure", six lignes flottaient sous la pancarte
   et laissaient les deux tiers du panneau vides.

   Deux entretoises plutot qu'un `justify-content: center` : dans un conteneur
   qui defile, centrer par justify-content rend le debordement du HAUT
   inatteignable — en paysage court, la pancarte disparaissait et rien ne
   permettait d'y remonter. Les entretoises, elles, se compriment a zero des
   que le contenu depasse, et le corps redefile du haut. */
.pane-body.centered::before,
.pane-body.centered::after { content: ''; flex: 1 0 0; min-height: 0; }
#screen-onboard .grow-field { display: flex; flex-direction: column; flex: 1; min-height: 96px; }
#screen-onboard .grow-field .pager { flex: 1; min-height: 0; }

/* Aucun ascenseur dans l'inscription : chaque volet tient dans sa hauteur (le
   corps grandit, les boutons restent amarres). La barre du navigateur, epaisse
   sur un fond sombre, passait pour un element de l'interface a cote de la fine
   ligne de separation. */
#screen-onboard .scroller, #screen-onboard .pane-body {
  scrollbar-width: none; -ms-overflow-style: none;
  /* Le volet ne glisse QUE verticalement. `overflow-y: auto` seul rend
     l'horizontale automatique elle aussi : au moindre depassement d'un rail de
     pages, le panneau entier partait de droite a gauche des qu'on posait le
     doigt sur la pancarte. Les rails, eux, gardent leur propre defilement —
     pas de `touch-action: pan-y` ici, il bloquerait leur glissement lateral,
     un parent restreignant l'intersection des gestes de ses descendants. */
  overflow-x: hidden;
}
/* Le debordement venait de la pancarte : elle mord de 6 px de chaque cote du
   retrait du panneau (`.wood-sign`, width: 100% + 12px), et dans un conteneur
   qui defile ces 6 px de droite sont du defilement — le volet entier partait
   alors sous le doigt. On elargit donc la boite du meme montant, avec un
   retrait interieur qui rend sa place au contenu : la pancarte garde ses
   feuilles, sans rien a faire glisser. */
#screen-onboard .pane-body { padding: 0 6px; margin: 0 -6px; }
#screen-onboard .scroller::-webkit-scrollbar,
#screen-onboard .pane-body::-webkit-scrollbar { display: none; }

/* Portrait choisi, en grand : deux a trois cases de la grille. Le pseudo tient
   a cote, faute d'avoir besoin de toute la largeur. */
.self-head { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
/* min-width: 0 : sans lui, la largeur minimale native d'un <input> impose sa
   loi a la colonne et deborde du panneau sur 360 pt. */
.self-head .field { margin-bottom: 0; min-width: 0; }
/* Le libelle partage la ligne avec le portrait : sur une dalle de 320 pt il
   passait sur deux lignes et repoussait le champ. Il se serre plutot. */
.self-head .field > span {
  white-space: nowrap; letter-spacing: 0.1em;
  font-size: clamp(0.6rem, 2.6vw, 0.7rem);
}
.self-preview {
  width: clamp(88px, 26vw, 124px); height: auto; aspect-ratio: 1;
  border-radius: 22px;
  /* Tant qu'aucune troupe n'est choisie, la meme pierre a lichen que la grille :
     un portrait detoure sur le fond du panneau ne se voit pas. */
  --av-bg: #6d7a63;
}
/* Ecran court : le portrait cede la place a la grille plutot qu'aux boutons. */
@media (max-height: 660px) {
  .self-preview { width: clamp(66px, 20vw, 88px); border-radius: 18px; }
  .self-head { gap: 10px; margin-bottom: 10px; }
}
/* Paysage court : le logo saute et la pancarte se serre, comme aux consignes.
   Le volet passe en deux colonnes — qui l'on est a gauche, la planche de
   portraits a droite — parce qu'empiles, l'entete et le pseudo ne laissaient
   qu'une demi-rangee de visages sur 360 px de haut. */
@media (orientation: landscape) and (max-height: 560px) {
  #screen-onboard .brand-head { display: none; }
  #screen-onboard .scroller { padding-top: calc(var(--safe-top) + 10px); }
  #screen-onboard .wood-sign { max-width: 260px; margin: -2px auto 8px; font-size: 0.94rem; }
  #ob-self .pane-body {
    display: grid; column-gap: 14px;
    /* minmax(0, 1fr) : sans le zero, le rail des portraits impose sa largeur
       minimale a la colonne et la planche deborde du panneau. */
    grid-template-columns: minmax(140px, 32%) minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
  }
  #ob-self .wood-sign { grid-column: 1 / -1; }
  #ob-self .self-head {
    flex-direction: column; align-items: stretch;
    gap: 8px; margin-bottom: 0;
  }
  #ob-self .self-head .field > span { font-size: 0.58rem; letter-spacing: 0.08em; }
  #ob-self .self-head input { padding: 9px 12px; }
  #ob-self .self-preview { width: clamp(52px, 15vh, 76px); border-radius: 16px; align-self: center; }
  /* Fonder une aventure : les pastilles et leurs marges se resserrent, sinon
     les deux modes poussent la duree hors du corps. */
  #ob-new .mode-card { padding: 7px 8px; gap: 3px; }
  #ob-new .mode-card .mode-ic img { width: 30px; height: 30px; }
  #ob-new .mode-card b { font-size: 0.84rem; }
  #ob-new .mode-card i { font-size: 0.66rem; }
  #ob-new .mode-cards { margin-bottom: 6px; }
  #ob-new .check { font-size: 0.78rem; }
  #ob-new .check i { font-size: 0.66rem; }
  #ob-new .wood-sign { margin-bottom: 2px; }
  /* Le sous-titre saute : les deux pastilles disent la meme chose, en dessous. */
  #ob-new .pane-body > p { display: none; }
  #ob-new #n-preset-wrap { margin-bottom: 8px; }
  #ob-new .center-field { margin-bottom: 0; }
  #ob-self .grow-field { min-height: 0; margin-bottom: 0; }
}

/* ---------------------- pages qui glissent ----------------------
   Une pile de soixante-quatre portraits ne tient pas sur un telephone : on la
   coupe en pages qui glissent sous le pouce, avec des points pour dire combien
   il en reste. Le decoupage est fait en JS (UI.pager), qui mesure la place
   disponible ; le CSS ne fait que poser le rail et l'accroche. */
.pager { display: flex; flex-direction: column; gap: 8px; overflow: visible; max-height: none; }
/* min-width: 0 est vital : un element flex se refuse par defaut a descendre
   sous la largeur minimale de son contenu, et le rail des motifs debordait a
   750 px dans un panneau de 294 — la grille se croyait alors treize colonnes. */
.pager-track {
  display: flex; flex: 1; min-height: 0; min-width: 0; width: 100%;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  scrollbar-width: none; -ms-overflow-style: none;
}
.pager-track::-webkit-scrollbar { display: none; }
/* align-content : sans lui la grille etiree repartit sa hauteur entre les
   rangees et etire les portraits en rectangles, l'aspect-ratio des cases ne
   pesant rien face au stretch par defaut. Centre plutot que colle en haut :
   le reste de la division rangees/hauteur se partage alors des deux cotes au
   lieu de creuser un trou sous la derniere rangee. */
.pager-page {
  flex: 0 0 100%; width: 100%; min-width: 0;
  display: grid; gap: 7px; align-content: center;
  scroll-snap-align: start; scroll-snap-stop: always;
  padding: 2px;
}
.pager-dots { display: flex; justify-content: center; align-items: center; gap: 7px; flex: none; }
/* La cible tactile fait 22 px, la pastille visible 6 : un point qu'on rate
   n'aide personne, un point qui creve l'ecran non plus. */
.pager-dot {
  width: 22px; height: 22px; padding: 0; border: 0; background: none;
  cursor: pointer; position: relative; -webkit-tap-highlight-color: transparent;
}
.pager-dot::after {
  content: ''; position: absolute; inset: 7px;
  border-radius: 50%; background: rgba(238, 241, 230, 0.34);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
  transition: background 0.2s var(--ease), transform 0.2s var(--ease);
}
.pager-dot.on::after { background: var(--moss); transform: scale(1.45); box-shadow: var(--glow-moss); }

/* ============================ overlays ============================ */

.sheet-bg {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(3, 10, 7, 0.74);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  display: flex; align-items: flex-end; justify-content: center;
  animation: fadeIn 0.24s var(--ease);
}
.sheet {
  width: 100%; max-width: 560px; max-height: 88vh; max-height: 88dvh;
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  background: linear-gradient(180deg, rgba(24, 54, 40, 0.97), rgba(8, 22, 15, 0.98));
  border: var(--line); border-bottom: 0;
  box-shadow: 0 -20px 60px rgba(0,0,0,0.7);
  padding: 18px 18px calc(22px + var(--safe-bot));
  animation: sheetUp 0.36s var(--ease);
}
@keyframes sheetUp { from { transform: translateY(100%); } to { transform: none; } }
.sheet .grabber { width: 44px; height: 4px; border-radius: 99px; background: rgba(238,241,230,0.22); margin: -6px auto 14px; }

#toasts {
  position: fixed; z-index: 80;
  /* Sous la barre d'entete : le chrono reste lisible. */
  top: calc(var(--safe-top) + var(--header-h) + 8px);
  left: 12px; right: 12px;
  display: flex; flex-direction: column; gap: 8px;
  pointer-events: none;
}
/* Un message ne VOLE PAS l'appui de ce qu'il recouvre. Les toasts s'empilent
   sous l'entete, et trois gestes d'affilee posaient une pile assez haute pour
   couvrir le milieu de la carte : le cartographe visait un sentier et touchait
   un message parti quatre secondes plus tard, sans que rien ne l'explique. Un
   toast informatif n'a rien a intercepter — il s'en va tout seul. Seul celui
   qui porte un bouton reste attrapable. */
.toast {
  pointer-events: none;
  padding: 12px 15px; border-radius: var(--radius);
  background: linear-gradient(165deg, rgba(30, 66, 46, 0.97), rgba(10, 28, 19, 0.97));
  border: var(--line);
  box-shadow: var(--shadow-3);
  backdrop-filter: blur(10px);
  animation: toastIn 0.34s var(--ease);
  display: flex; gap: 11px; align-items: flex-start;
}
.toast.success { border-color: rgba(127,208,127,0.45); box-shadow: var(--shadow-3), var(--glow-moss); }
.toast.alert { border-color: rgba(217,105,92,0.45); }
.toast svg { width: 20px; height: 20px; flex: none; margin-top: 1px; }
.toast .t-title { font-weight: 800; font-size: 0.86rem; }
.toast .t-body { font-size: 0.8rem; color: var(--parch-dim); }
/* Toast a bouton : il reste a l'ecran, d'ou la croix de fermeture — et d'ou le
   pointeur qu'on lui rend, puisqu'il attend qu'on le touche. */
.toast.act { pointer-events: auto; align-items: flex-start; padding-right: 10px; }
.toast.act .t-act { margin-top: 10px; min-height: 34px; }
.toast .t-close {
  margin-left: auto; flex: none;
  width: 24px; height: 24px; padding: 0;
  border: 0; border-radius: 50%;
  background: rgba(238, 241, 230, 0.08);
  color: var(--parch-dim);
  font: inherit; font-size: 1.1rem; line-height: 1;
  cursor: pointer;
}
@keyframes toastIn { from { opacity: 0; transform: translateY(-16px) scale(0.96); } to { opacity: 1; transform: none; } }
.toast.out { animation: toastOut 0.3s var(--ease) forwards; }
@keyframes toastOut { to { opacity: 0; transform: translateY(-12px) scale(0.96); } }

/* -------------------- la carte du troll, en grand --------------------
   Elle prend tout ce que l'ecran lui laisse : 94 % de la largeur, ou la
   hauteur disponible quand c'est elle qui manque (paysage). Pas de cadre, pas
   de panneau — la carte porte deja le sien, peint dans l'image. */

.card-zoom {
  position: fixed; inset: 0; z-index: 70;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 14px;
  background: rgba(3, 10, 7, 0);
  backdrop-filter: blur(0px); -webkit-backdrop-filter: blur(0px);
  transition: background 0.3s var(--ease), backdrop-filter 0.3s var(--ease);
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.card-zoom.on {
  background: rgba(3, 10, 7, 0.82);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
}
.cz-card {
  /* aspect-ratio : la taille finale est connue avant que l'image ne charge,
     ce dont l'animation d'ouverture a besoin pour partir de la vignette. */
  width: min(97vw, calc((88vh - 40px) * 950 / 1393));
  height: auto; aspect-ratio: 950 / 1393;
  filter: drop-shadow(0 22px 46px rgba(0, 0, 0, 0.75));
  opacity: 0;
  /* Un soupcon de rebond a l'arrivee : la carte se pose, elle ne s'arrete pas. */
  transition: transform 0.44s cubic-bezier(0.16, 1.06, 0.3, 1), opacity 0.24s var(--ease);
  will-change: transform;
}
.card-zoom.on .cz-card { opacity: 1; }
.cz-hint {
  font-size: 0.74rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--parch-faint);
  opacity: 0; transition: opacity 0.3s var(--ease) 0.35s;
}
.card-zoom.on .cz-hint { opacity: 1; }

/* -------------------- une image a fouiller, en grand -----------------
   La vignette d'une mission mesure quelques centimetres, et c'est dedans qu'on
   cherche la pierre ou la marque sur l'ecorce. L'image prend donc l'ecran
   entier et se zoome comme la carte : pincer, double-appui, glisser. */

.img-zoom {
  position: fixed; inset: 0; z-index: 72;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  background: rgba(3, 10, 7, 0);
  backdrop-filter: blur(0px); -webkit-backdrop-filter: blur(0px);
  transition: background 0.24s var(--ease), backdrop-filter 0.24s var(--ease);
  /* La page ne defile pas sous les doigts : ils appartiennent a l'image. */
  touch-action: none; -webkit-tap-highlight-color: transparent;
  opacity: 0;
}
.img-zoom.on { opacity: 1; background: rgba(3, 10, 7, 0.92); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); }
.iz-img {
  max-width: 100%; max-height: 100%;
  /* Le zoom part du centre du cadre : c'est l'hypothese du calcul de zoomAt,
     qui compense ensuite pour agrandir autour du doigt. */
  transform-origin: 50% 50%;
  user-select: none; -webkit-user-drag: none;
  will-change: transform;
}
/* Agrandie, l'image se promene : le curseur le dit sur un ecran a souris. */
.img-zoom.zoomed { cursor: grab; }
.iz-note {
  position: absolute; left: 0; right: 0; bottom: calc(16px + var(--safe-bot));
  text-align: center; font-size: 0.72rem; color: var(--parch-faint);
  pointer-events: none;
  transition: opacity 0.2s var(--ease);
}
/* Une fois l'image agrandie, la consigne a fait son office et gene la lecture. */
.img-zoom.zoomed .iz-note { opacity: 0; }
.iz-close {
  position: absolute; top: calc(10px + var(--safe-top)); right: 12px;
  width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center;
  color: var(--parchment);
  background: rgba(9, 22, 16, 0.8);
  border: 1px solid rgba(238, 241, 230, 0.18);
}
.iz-close svg { width: 18px; height: 18px; }

/* Les eclats passent devant le voile le temps de l'ouverture (Particles.front). */
#particles.front { z-index: 71; }

/* La carte s'ouvre depuis le PORTRAIT, pas depuis une vignette a cote : une
   image de plus encombrait la ligne pour dire ce que la tete disait deja. Un
   coin de carte dore, glisse dans l'angle du portrait, annonce qu'il y a
   quelque chose derriere — sans lui, personne ne penserait a toucher. */
[data-card] {
  position: relative; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 0.16s var(--ease);
}
[data-card]::after {
  content: ''; position: absolute; right: 3px; bottom: 3px;
  width: 22%; max-width: 17px; aspect-ratio: 950 / 1393;
  border-radius: 2px;
  /* Dos de carte : cadre dore, coeur sombre. Un aplat dore seul passait pour
     un bout de papier oublie sur le portrait. */
  background: linear-gradient(160deg, rgba(20, 40, 28, 0.96), rgba(8, 20, 14, 0.96));
  border: 1px solid var(--gold);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.6), inset 0 0 0 1px rgba(229, 169, 78, 0.35);
  transform: rotate(-10deg);
  pointer-events: none;
}
[data-card]:active { transform: scale(0.96); }

/* ============================ divers ============================ */

.stack { display: flex; flex-direction: column; gap: 12px; }
.row { display: flex; align-items: center; gap: 10px; }
.row.wrap { flex-wrap: wrap; }
.spacer { flex: 1; }

.list-empty { padding: 40px 18px; text-align: center; color: var(--parch-faint); font-size: 0.88rem; }
.list-empty svg { width: 46px; height: 46px; opacity: 0.35; margin-bottom: 10px; }

::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-thumb { background: rgba(238,241,230,0.16); border-radius: 99px; }
::-webkit-scrollbar-track { background: transparent; }

.shake { animation: shake 0.42s var(--ease); }
@keyframes shake {
  10%,90% { transform: translateX(-2px); }
  20%,80% { transform: translateX(4px); }
  30%,50%,70% { transform: translateX(-7px); }
  40%,60% { transform: translateX(7px); }
}

/* ---------------------- mode paysage : rail + chat ---------------------- */
/* La regle de base de .chat-dock est declaree plus haut : cet ordre compte,
   sinon le panneau de chat ne s'ouvre jamais en paysage. */

@media (orientation: landscape) and (max-height: 700px) {
  .shell {
    grid-template-rows: auto auto auto 1fr;
    grid-template-columns: var(--rail-w) 1fr var(--chat-w);
    grid-template-areas:
      "head  head head"
      "role  role role"
      "rally rally rally"
      "nav   main dock";
  }
  .topbar { padding: calc(var(--safe-top) + 6px) 12px 8px; min-height: 52px; }
  /* En paysage la zone principale partage sa rangee avec le rail et le chat :
     l'arrondi et le chevauchement du portrait la feraient flotter au milieu. */
  main { margin-top: 0; border-radius: 0; border-top: 0; }
  .tabbar {
    grid-area: nav;
    grid-template-columns: 1fr;
    grid-auto-rows: min-content;
    align-content: start; gap: 6px;
    padding: 10px 8px calc(10px + var(--safe-bot)) calc(8px + env(safe-area-inset-left, 0px));
    border-top: 0; border-right: var(--line-soft);
    border-radius: 0 22px 22px 0;
    box-shadow: 8px 0 26px rgba(0,0,0,0.40);
    min-height: 0; overflow-y: auto;
  }
  .tab { padding: 9px 2px; font-size: 0.56rem; }
  .tab .badge-dot { top: 4px; right: 12px; margin-right: 0; }
  .tab .badge-bubble { top: 2px; right: 8px; margin-right: 0; }
  .chat-dock { grid-area: dock; display: flex; }
  /* Ici seulement le dock atteint le bas de l'ecran : le clavier de mots,
     toujours dernier element du chat, reprend la marge de securite. */
  .chat-dock .word-deck { padding-bottom: var(--safe-bot); }
  .tab[data-view="chat"] { display: none; }
  #view-chat.view { display: none !important; }
  .mission-split { grid-template-columns: 1fr 1fr; }
  .sheet-bg { align-items: center; padding: 12px; }
  .sheet { border-radius: var(--radius-lg); border-bottom: var(--line); max-height: 92vh; max-height: 92dvh; animation: viewIn 0.3s var(--ease); }
}

/* Ecrans etroits : la barre a 6 onglets doit rester lisible. */
@media (max-width: 360px) {
  .tab { font-size: 0.54rem; }
  .tab svg { width: 21px; height: 21px; }
}

/* --- Retouches de calage : ecrans etroits et vignettes sans photo --- */

.hint-card .flag svg { width: 11px; height: 11px; }

/* Une balise sans photo de decor garde une vignette vivante plutot qu'un aplat. */
.ph-none {
  background-image:
    radial-gradient(60% 60% at 50% 40%, rgba(140, 200, 140, 0.16), transparent 70%),
    linear-gradient(150deg, #33503a, #1a2c20) !important;
  background-size: cover;
  position: relative;
}
.ph-none::after {
  content: "";
  position: absolute; inset: 22%;
  background: url("../assets/items/it-01.webp") center/contain no-repeat;
  opacity: 0.22;
}
.mcard .m-photo.ph-none {
  background-image:
    radial-gradient(60% 60% at 50% 40%, rgba(255, 255, 255, 0.22), transparent 70%),
    linear-gradient(150deg, #b7c4ad, #8e9c88) !important;
}

/* ---- scanner plein ecran ----------------------------------------------

   Sur cette vue seulement, la camera passe derriere l'entete et la barre
   d'onglets : le viseur gagne toute la hauteur et les barres flottent
   par-dessus. La scene sort donc du flux (fixed) et les elements qu'elle
   contient se decalent de la hauteur des barres pour rester atteignables. */

.shell.scanning .scan-stage {
  position: fixed; inset: 0; margin: 0; z-index: 0;
  background: #06120c;
}
.shell.scanning .view.active { animation: fadeIn 0.3s var(--ease); }
.shell.scanning .scan-page { pointer-events: none; }
.shell.scanning .scan-page > * { pointer-events: auto; }
.shell.scanning .scan-head { position: relative; z-index: 2; }
.shell.scanning .topbar,
.shell.scanning .tabbar { position: relative; z-index: 3; }

/* Le chrono encombre un viseur ; il reste lisible partout ailleurs. */
.shell.scanning .timer-pill { display: none; }
/* Logo centre : la cale compense le bouton de menu reste a droite. */
.shell.scanning .topbar .tb-brand { flex: 1; align-items: center; }
.shell.scanning .topbar::before { content: ''; width: 44px; flex: none; }

/* Entete et barres translucides pour laisser voir l'image. */
/* L'image de la camera monte jusqu'au bord haut de l'ecran : l'entete n'est
   plus qu'un voile, juste assez dense pour que le logo et le bouton de menu
   restent lisibles sur n'importe quelle scene. */
.shell.scanning .topbar {
  background: linear-gradient(180deg, rgba(4, 12, 8, 0.50), rgba(4, 12, 8, 0.08));
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
  border-bottom-color: rgba(238, 241, 230, 0.13);
}
.shell.scanning .tabbar {
  background: linear-gradient(0deg, rgba(3, 11, 7, 0.97), rgba(6, 18, 12, 0.90));
}
/* Memes teinte et ombre que la barre d'onglets : les deux panneaux flottent
   sur l'image de la camera sans se dissoudre dedans. */
.shell.scanning .scan-head,
.shell.scanning .detect-card {
  background: linear-gradient(165deg, rgba(9, 26, 18, 0.88), rgba(5, 16, 11, 0.82));
  border: 1px solid rgba(168, 205, 172, 0.34);
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.55), var(--bevel);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
}

/* Elements du viseur remontes au-dessus de la barre d'onglets. */
.shell.scanning .detect-card { bottom: calc(var(--nav-h) + 10px + var(--nav-lap)); }
.shell.scanning .scan-hint { bottom: calc(var(--nav-h) + 104px + var(--nav-lap)); }
.shell.scanning .scan-side { top: 50%; transform: translateY(-50%); }


/* ====================== clavier de mots du chat ====================== */

.word-deck {
  border-top: var(--line-soft);
  background: linear-gradient(180deg, rgba(10, 30, 20, 0.94), rgba(6, 18, 12, 0.96));
  flex: none;
}
/* Poignee toujours visible : le clavier de mots reste replie par defaut pour
   laisser la place au fil de discussion, et se deroule a la demande. */
.word-toggle {
  width: 100%; display: flex; align-items: center; gap: 8px;
  padding: 11px 14px; border: 0; background: none;
  color: var(--parch-dim); font: inherit; font-size: 0.78rem; font-weight: 800;
  letter-spacing: 0.02em; text-align: left; cursor: pointer;
}
.word-toggle .ic { width: 16px; height: 16px; color: var(--gold); flex: none; }
.word-toggle .chev { width: 16px; height: 16px; margin-left: auto; color: var(--parch-faint); flex: none; transition: transform 0.2s var(--ease); }
.word-deck.open .word-toggle { color: var(--parchment); }
.word-deck.open .word-toggle .chev { transform: rotate(90deg); color: var(--moss); }
.word-body { max-height: 0; overflow: hidden; transition: max-height 0.28s var(--ease); }
.word-deck.open .word-body { max-height: 300px; overflow-y: auto; -webkit-overflow-scrolling: touch; }
.word-tabs { display: flex; gap: 6px; overflow-x: auto; padding: 0 10px 8px; scrollbar-width: none; }
.word-tabs::-webkit-scrollbar { display: none; }
.word-tab {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 7px 10px; border-radius: 999px; flex: none;
  border: 1px solid rgba(214, 231, 210, 0.18);
  background: rgba(12, 34, 24, 0.85);
  color: var(--parch-dim);
  font: inherit; font-size: 0.7rem; font-weight: 700;
  cursor: pointer;
  transition: all 0.2s var(--ease);
}
.word-tab svg { width: 14px; height: 14px; }
.word-tab.on {
  background: linear-gradient(180deg, #4f9b5b, #2b6a3e);
  color: #f2fbef; border-color: rgba(160, 232, 168, 0.5);
  box-shadow: 0 4px 14px rgba(60, 150, 80, 0.35);
}

.word-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; padding: 0 10px 10px; }
.word {
  border: 0; border-radius: 14px;
  padding: 13px 6px;
  font: inherit; font-size: 0.86rem; font-weight: 800;
  color: #10281c;
  cursor: pointer;
  box-shadow: var(--shadow-1), inset 0 1px 0 rgba(255, 255, 255, 0.45);
  transition: transform 0.13s var(--ease), filter 0.15s;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.word:active { transform: scale(0.94); filter: brightness(0.94); }
.word.c0 { background: linear-gradient(180deg, #9fe6a2, #6cc978); }
.word.c1 { background: linear-gradient(180deg, #8fe3d0, #58bfae); }
.word.c2 { background: linear-gradient(180deg, #7fdcc9, #46b8a6); }
.word.c3 { background: linear-gradient(180deg, #a9e6ff, #6fc2e8); }
.word.c4 { background: linear-gradient(180deg, #ffb3ae, #ef8880); }
.word.c5 { background: linear-gradient(180deg, #cdb6f7, #a488e6); }

/* En paysage le dock est etroit : deux colonnes de mots suffisent. */
@media (orientation: landscape) and (max-height: 700px) {
  .word-grid { grid-template-columns: repeat(2, 1fr); }
  .word { padding: 10px 6px; font-size: 0.78rem; }
  .word-deck.open .word-body { max-height: 280px; }
}


/* ================================================== file d envoi

   La pastille dit d un coup d oeil ce qui attend encore. Elle vit ici et non
   dans une feuille de profil : joueur, organisateur et cartographe la posent
   au meme endroit du code, avec la meme question.

   Les quatre etats se lisent SANS COULEUR — nuage plein, nuage a fleche,
   anneau qui tourne, nuage barre. Sur une carte de sous-bois le rouge et le
   vert tombent tous les deux sur du feuillage, et une pastille qui ne se
   distinguerait que par sa teinte ne dirait rien au soleil. */

.sync-badge {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; flex: none;
  border: var(--line); border-radius: 50%;
  background: var(--glass-2);
  color: var(--parch-dim);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: color 0.2s, border-color 0.2s, transform 0.13s var(--ease);
}
.sync-badge:active { transform: scale(0.92); }
.sync-badge svg { width: 20px; height: 20px; }

/* Tout est passe : la pastille s efface presque. Elle reste visible — savoir
   que rien n attend fait partie de ce qu on vient verifier — mais elle ne
   reclame plus l oeil. */
.sync-badge.calm { color: var(--parch-faint); opacity: 0.55; }
.sync-badge.wait { color: var(--gold); border-color: rgba(226, 201, 141, 0.42); }
.sync-badge.busy { color: var(--moss); border-color: rgba(127, 208, 127, 0.45); }
.sync-badge.bad  { color: var(--rot); border-color: rgba(217, 105, 92, 0.55); }

/* Le compte de ce qui attend, en chiffres : « ca envoie » ne dit pas s il
   reste un element ou quinze, et c est la seule chose qu on veut savoir sans
   ouvrir la feuille. */
.sync-bubble {
  position: absolute; top: -3px; right: -3px;
  min-width: 17px; height: 17px; padding: 0 4px;
  border-radius: 999px;
  background: var(--ember); color: #1a1008;
  border: 1.5px solid var(--ink-800);
  font-size: 0.58rem; font-weight: 900; line-height: 14px; text-align: center;
}
.sync-badge.bad .sync-bubble { background: var(--rot); color: #2a0f0c; }

/* L anneau de progression : un `conic-gradient` pilote par --pct, sans image
   ni canevas. Il tourne autour du nuage pendant qu une photo monte — une
   barre de progression sur 38 px serait illisible, un anneau se lit a la
   proportion remplie. */
.sync-ring {
  position: absolute; inset: -2px;
  border-radius: 50%;
  background: conic-gradient(var(--moss) calc(var(--pct) * 1%), transparent 0);
  -webkit-mask: radial-gradient(circle, transparent 16px, #000 17px);
  mask: radial-gradient(circle, transparent 16px, #000 17px);
  pointer-events: none;
}

/* ------------------------------------------------ la feuille des taches */

.sync-summary { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.ss-cell {
  background: var(--glass-2); border: var(--line); border-radius: var(--radius-sm);
  padding: 10px 6px; text-align: center;
}
.ss-cell b { display: block; font-size: 1.15rem; font-weight: 900; color: var(--gold); }
.ss-cell i { font-style: normal; font-size: 0.68rem; color: var(--parch-faint); }

.sync-net {
  margin: 10px 0 0; padding: 8px 12px;
  border: var(--line); border-radius: var(--radius-sm);
  background: rgba(127, 208, 127, 0.08);
  font-size: 0.76rem; color: var(--parch-dim);
}
.sync-net.bad { background: rgba(217, 105, 92, 0.12); color: #f0c3bd; }

/* La liste defile toute seule : trente photos en attente ne doivent pas
   pousser les boutons du bas hors de la feuille. */
.sync-list { max-height: 46vh; overflow-y: auto; overflow-x: hidden; margin-top: 8px; }
.sync-empty { padding: 18px 0; text-align: center; color: var(--parch-faint); font-size: 0.8rem; }

.sync-task {
  border: var(--line); border-radius: var(--radius-sm);
  background: var(--glass-3);
  padding: 10px 12px; margin-bottom: 8px;
}
.sync-task.sending { border-color: rgba(127, 208, 127, 0.45); }
.sync-task.error   { border-color: rgba(217, 105, 92, 0.5); }
.sync-task.done    { opacity: 0.45; }

.st-head { display: flex; align-items: baseline; gap: 8px; }
.st-head b { flex: 1; min-width: 0; font-size: 0.86rem; font-weight: 800; color: var(--parchment);
             overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.st-state { font-size: 0.64rem; color: var(--parch-faint); text-transform: uppercase;
            letter-spacing: 0.05em; flex: none; }
.sync-task.sending .st-state { color: var(--moss); }
.sync-task.error .st-state { color: var(--rot); }
.sync-task.done .st-state { color: var(--moss-soft); }

.st-detail { display: block; font-style: normal; font-size: 0.7rem; color: var(--parch-faint); margin-top: 2px; }
.st-err    { display: block; font-style: normal; font-size: 0.72rem; color: #f0c3bd; margin-top: 4px; }

/* La barre de progression d un envoi en cours. Elle ne s anime que par sa
   largeur : le compositeur la traite sans repeindre le reste de la feuille. */
.st-bar { height: 5px; border-radius: 999px; background: rgba(0, 0, 0, 0.35); margin-top: 7px; overflow: hidden; }
.st-bar i { display: block; height: 100%; border-radius: 999px;
            background: linear-gradient(90deg, var(--moss-deep), var(--moss));
            transition: width 0.25s var(--ease); }

.st-acts { display: flex; gap: 8px; margin-top: 8px; }
.st-acts .btn { flex: 1; }
