/* ============================================================
   POPOTE WAR — LA COQUILLE
   ------------------------------------------------------------
   Ce qui entoure les écrans : la toile de fond, la barre
   d'onglets, les calques. Rien de spécifique à un écran donné.
   ============================================================ */

/* ------------------------------------------------------------
   LA TOILE DE FOND
   Le fond n'est pas une couleur unie. Un tissage fin, une lueur
   haute qui donne un ciel à l'écran, et une ombre basse qui
   l'assoit. C'est ce qui fait qu'une zone sans contenu reste une
   MATIÈRE plutôt qu'un trou.
   ------------------------------------------------------------ */
#app {
  position: relative;
  min-height: 100dvh;
  padding-left: var(--safe-l);
  padding-right: var(--safe-r);
  background:
    var(--tissage),
    radial-gradient(120% 55% at 50% -8%, #23261A 0%, transparent 62%),
    linear-gradient(180deg, var(--nuit) 0%, #101109 100%);
  background-attachment: fixed;
}

/* Le tampon de la caserne, imprimé en filigrane derrière tout.
   Il tourne légèrement, comme un vrai coup de tampon donné à la
   main. Traversant : il ne capte jamais le toucher. */
#app::before {
  content: "POPOTE WAR";
  position: fixed;
  /* Bas de l'écran, derrière le bouton d'action : un tampon se pose en
     bas d'un document, pas en travers. Placé au milieu il coupait
     l'écusson en deux et se lisait comme un défaut de rendu.
     Une seule ligne : `white-space: nowrap` empêche « WAR » de passer
     dessous et de doubler la hauteur du filigrane. */
  inset: auto 0 7% 0;
  z-index: 0;
  font-family: var(--display);
  font-size: 15vw;
  letter-spacing: .12em;
  white-space: nowrap;
  text-align: center;
  color: rgba(217, 207, 168, .018);
  transform: rotate(-3deg);
  pointer-events: none;
  user-select: none;
}
/* ------------------------------------------------------------
   LA TOILE, EN VRAIE MATIÈRE
   ------------------------------------------------------------
   Le fond était un empilement de dégradés — bon marché, et
   honnête pour ce que c'était. Il porte maintenant une vraie
   toile de tente, tuilée sans couture.

   Pourquoi un calque séparé plutôt qu'une couche de plus dans le
   `background` de `#app` : parce qu'une couche de fond n'a pas
   d'opacité propre. Il aurait fallu passer par un mode de fusion,
   et fondre une image sombre sur un fond déjà sombre l'aurait
   simplement noirci. Ici, l'opacité et le mode de fusion sont
   réglables indépendamment, et le résultat se prévoit.

   `mix-blend-mode: overlay` éclaircit ce qui est clair dans la
   toile et assombrit ce qui est sombre, sans déplacer la valeur
   d'ensemble : le tissage apparaît, la couleur du jeu ne bouge pas.

   Les dégradés d'origine RESTENT dessous. Ils ne coûtent rien et
   ils tiennent l'écran le jour où l'image ne charge pas.
   ------------------------------------------------------------ */
#app::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: url("/img/matiere/toile-tente.webp") 0 0 / 260px 260px repeat;
  mix-blend-mode: overlay;
  opacity: .55;
}

.screen, .tabs { position: relative; z-index: 1; }

/* ------------------------------------------------------------
   ÉCRANS
   Tous présents dans le document, un seul visible. On ne
   construit ni ne détruit : c'est ce qui rend le changement
   instantané, et c'est ce qu'attend un joueur d'application.
   ------------------------------------------------------------ */
.screen {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
  padding-top: var(--safe-t);
  padding-bottom: calc(var(--tabbar) + var(--safe-b));
}

/* Le combat et la victoire prennent tout : ni onglets, ni bandeau.

   `height` ET NON SEULEMENT `min-height` — c'est ce qui manquait.

   `.screen` pose `min-height: 100dvh` : un MINIMUM. L'écran de combat
   pouvait donc grandir au-delà de la fenêtre, et sa rangée souple se
   calculait sur son contenu au lieu de la place disponible. Sur un
   téléphone de 640 pixels, il en réclamait 793 : les quatre réponses
   tombaient sous la ligne, sur le seul écran où il faut répondre en treize
   secondes.

   Avec une hauteur DÉFINIE, `minmax(0, 1fr)` reprend son sens : l'arène
   absorbe ce qui reste, le combattant rapetisse, et la question comme les
   réponses ne bougent plus. */
.screen--full {
  height: 100dvh;
  max-height: 100dvh;
  padding-bottom: var(--safe-b);
}

/* ------------------------------------------------------------
   BARRE D'ONGLETS
   Cinq entrées depuis que la boutique y figure. La limite n'est
   pas un principe mais une mesure : à 320 px, cinq colonnes
   donnent 64 px et « Collection » en occupe 48. Six ne
   tiendraient pas.
   ------------------------------------------------------------ */
.tabs {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 40;
  display: grid;
  /* CINQ DEPUIS QUE LA BOUTIQUE Y FIGURE.
     Le commentaire d'origine posait « quatre onglets, pas un de plus —
     au-delà, les libellés deviennent illisibles ». La limite était juste,
     la mesure manquait : à 360 px, cinq colonnes donnent 72 px chacune, et
     « Collection » — le plus long — occupe 48 px à 11 px de corps. Il
     reste donc de la marge, et la règle passe de quatre à cinq. Six ne
     tiendraient pas. */
  grid-template-columns: repeat(5, 1fr);
  height: calc(var(--tabbar) + var(--safe-b));
  padding-bottom: var(--safe-b);
  background: linear-gradient(180deg, rgba(30,32,22,.96), rgba(20,21,15,.99));
  backdrop-filter: blur(14px);
  border-top: 1px solid var(--kaki);
  box-shadow: 0 -6px 18px rgba(0,0,0,.4);
}
.tab {
  position: relative;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 3px;
  min-height: var(--tap);
  color: var(--sable-b);
  transition: color var(--fast);
}
/* ------------------------------------------------------------
   L'EMBLÈME DE L'ONGLET
   ------------------------------------------------------------
   La barre du bas est la seule chose visible sur TOUS les écrans,
   et elle portait cinq tracés au trait — corrects, sans caractère.

   Chaque onglet a maintenant son objet rendu : deux sabres croisés,
   un fanion, une médaille, une caisse cerclée, un auvent. Vingt et
   un kilo-octets pour les cinq.

   LE TRACÉ RESTE DESSOUS, et reprend la main si l'image manque —
   au premier lancement hors connexion, par exemple. C'est la même
   règle que partout ailleurs dans ce jeu : jamais de trou.

   LES NON-CHOISIS SONT ÉTEINTS. Cinq objets peints allumés en même
   temps feraient une guirlande, et on ne verrait plus où l'on est.
   ------------------------------------------------------------ */
.tab__ico { width: 23px; height: 23px; display: block; }
.tab__img {
  width: 26px; height: 26px;
  display: block;
  object-fit: contain;
  filter: saturate(.35) brightness(.62);
  transition: filter var(--fast);
}
.tab--on .tab__img { filter: none; }
/* Quand l'image est là, le tracé s'efface — mais il reste dans le document,
   prêt à reprendre si l'image ne charge pas. */
.tab:has(.tab__img) .tab__ico { display: none; }
.tab__lbl {
  font-size: 0.6875rem;
  letter-spacing: .04em;
  /* À cinq colonnes sur un écran de 360 px, « Collection » et
     « Intendance » frôlent la largeur de leur case. On interdit la coupure
     plutôt que de la subir : un libellé sur deux lignes déforme la rangée
     entière, et un onglet plus haut que ses voisins se lit comme un
     défaut. */
  white-space: nowrap;
}
.tab--on { color: var(--laiton); }

/* LA BOUTIQUE QUITTE LE JEU, ET ÇA SE VOIT.
   Elle ne s'allume jamais — aucun écran ne lui correspond — et elle porte
   une petite flèche de sortie. Un onglet qui ouvre un autre site sans le
   dire est le genre de surprise qui fait perdre confiance à un joueur qui
   vient de gagner une remise. */
.tab--dehors { text-decoration: none; }
/* LA FLÈCHE EST POSÉE SUR LA CASE, PAS SUR LE MOT.
   Accrochée au libellé, elle sortait de sa boîte : « Boutique » se mesurait
   alors comme un texte coupé, et sur une case de soixante-quatre pixels
   elle serait tôt ou tard venue mordre le voisin. La case, elle, a la
   place — et la flèche marque le coin, comme partout ailleurs. */
.tab--dehors::after {
  content: "↗";
  position: absolute;
  top: 7px;
  right: 22%;
  font-size: 9px;
  line-height: 1;
  opacity: .65;
  pointer-events: none;
}
/* Une ligne de séparation à sa gauche : elle marque la frontière entre le
   jeu et le magasin, là où les quatre premiers onglets sont un même lieu. */
.tab--dehors::before {
  content: "";
  position: absolute;
  left: 0; top: 22%; bottom: 22%;
  width: 1px;
  background: var(--kaki);
}

/* Le repère de l'onglet actif : une barrette de galon, en haut. */
.tab--on::before {
  content: "";
  position: absolute;
  top: 0; left: 26%; right: 26%;
  height: 3px;
  border-radius: 0 0 3px 3px;
  background: linear-gradient(90deg, transparent, var(--laiton), transparent);
}
.tab__dot {
  position: absolute;
  top: 9px;
  right: 24%;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--alerte);
  box-shadow: 0 0 0 2px var(--nuit);
}

/* La barre disparaît pendant un duel : rien ne doit rappeler au
   joueur qu'un navigateur existe. */
body[data-screen="combat"] .tabs,
body[data-screen="victory"] .tabs,
body[data-screen="offline"] .tabs { display: none; }

/* ------------------------------------------------------------
   FEUILLE (à la place des fenêtres modales)
   Elle monte du bas, sous le pouce. Aucune ne s'affiche avant la
   première question : chaque fenêtre avant le premier duel coûte
   des joueurs.
   ------------------------------------------------------------ */
.sheet { position: fixed; inset: 0; z-index: 60; }
.sheet__back { position: absolute; inset: 0; background: rgba(0,0,0,.7); }
.sheet__box {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  max-height: 88dvh;
  overflow-y: auto;
  padding: 1.4rem var(--pad) calc(1.4rem + var(--safe-b));
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  background: var(--toile-2);
  border-top: 3px solid var(--laiton);
  box-shadow: 0 -12px 40px rgba(0,0,0,.6);
  animation: monte var(--mid) var(--ease);
}
@keyframes monte { from { transform: translateY(100%); } }
.sheet h2 {
  font-family: var(--display);
  font-size: var(--fs-xl);
  letter-spacing: .03em;
  color: var(--sable);
  margin-bottom: .5rem;
}
.sheet p { color: var(--sable-b); font-size: var(--fs-sm); margin-bottom: 1rem; }
.sheet .field {
  width: 100%;
  min-height: var(--tap);
  margin-bottom: .6rem;
  padding: 0 .9rem;
  border-radius: var(--r-sm);
  border: 1px solid var(--kaki);
  background: var(--nuit);
  color: var(--sable);
  font: inherit;
}
.sheet .field:focus { border-color: var(--laiton); outline: none; }
.sheet .err { color: var(--alerte-c); font-size: var(--fs-xs); min-height: 1.2em; }

/* ------------------------------------------------------------
   TOAST
   ------------------------------------------------------------ */
.toast {
  position: fixed;
  left: 50%;
  bottom: calc(var(--tabbar) + var(--safe-b) + 1rem);
  z-index: 70;
  max-width: 88vw;
  padding: .75rem 1.05rem;
  border-radius: var(--r-sm);
  border-left: 3px solid var(--laiton);
  background: var(--kaki);
  color: var(--sable);
  font-size: var(--fs-xs);
  box-shadow: 0 8px 24px rgba(0,0,0,.5);
  transform: translate(-50%, 180%);
  /* `visibility` ET `opacity`, pas seulement la translation.
     Un toast VIDE se réduit à son remplissage : un petit rectangle olive
     d'une trentaine de pixels, que 180 % de sa propre hauteur ne suffit
     pas à sortir de l'écran. Il restait donc posé sur la barre d'onglets
     en permanence, et se lisait comme un défaut d'affichage.
     `visibility` est aussi ce qui le retire des lecteurs d'écran tant
     qu'il n'a rien à dire. */
  opacity: 0;
  visibility: hidden;
  transition: transform var(--mid) var(--ease),
              opacity var(--mid) var(--ease),
              visibility 0s linear var(--mid);
  pointer-events: none;
}
.toast--on {
  transform: translate(-50%, 0);
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}

/* ------------------------------------------------------------
   BANDEAU DE NOUVELLE VERSION
   Présent dès le premier jour. Sans lui, une correction urgente
   met des jours à atteindre les joueurs, et on ne le voit pas
   depuis son propre navigateur.
   ------------------------------------------------------------ */
.update {
  position: fixed;
  left: 0; right: 0; top: 0;
  z-index: 80;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap);
  padding: calc(.6rem + var(--safe-t)) var(--pad) .6rem;
  background: var(--laiton);
  color: var(--nuit);
  font-size: var(--fs-xs);
  font-weight: 600;
}
.update__btn {
  /* Trouvé en lisant, pas en mesurant : ce bouton n'apparaît que le jour
     d'une mise à jour, et la sonde ne le voit donc jamais. C'est pourtant
     le plus important de tous — celui qui sort un joueur d'une version
     cassée. Il passe à la taille de cible du jeu comme les autres. */
  min-height: var(--tap);
  padding: 0 .8rem;
  border-radius: var(--r-sm);
  background: var(--nuit);
  color: var(--laiton);
  font-weight: 700;
  letter-spacing: .06em;
}

/* ------------------------------------------------------------
   BARRE DE CHARGEMENT
   ------------------------------------------------------------ */
.loading {
  position: fixed;
  top: 0; left: 0;
  z-index: 90;
  height: 3px;
  width: 100%;
  background: linear-gradient(90deg, transparent, var(--laiton), transparent);
  animation: balayage 1.1s linear infinite;
  pointer-events: none;
}
@keyframes balayage {
  from { transform: translateX(-100%); }
  to { transform: translateX(100%); }
}
