/**
 * PlayAwale — Les deux panneaux de joueur
 * -----------------------------------------------------------------------------
 * Portrait et score de chaque camp, ancrés aux coins de l'écran : l'adversaire
 * en haut à gauche, le joueur en bas à droite. Ils se répondent en diagonale,
 * dans le sens du plateau — c'est la disposition de la version 1.
 *
 * COMPOSANT, ET NON PAGE
 * Ces panneaux ne concernent pas le jeu solo en particulier : une partie à deux
 * les montrera de la même façon, et un rejeu de partie aussi. Ils étaient
 * écrits dans pages/solo.css, où duo.php et replay.php n'auraient pas pu les
 * atteindre sans les recopier — la dette exacte dont la refonte cherche à
 * sortir.
 *
 * UTILISATION
 * La page porte la classe « game-page », qui apporte les mesures dont tout le
 * reste découle, et déclare ses panneaux :
 *
 *     <main class="page game-page …">
 *       <div class="ai-panel">…</div>
 *       <div class="player-panel">…</div>
 *
 * CE QU'IL EXPOSE AU RESTE DE LA PAGE
 *   --avatar-panel-size   côté d'un portrait
 *   --panel-reach         largeur qu'un panneau occupe depuis le bord
 *   --score-ratio         taille du score, en fraction du portrait
 *   --panel-clock-height  hauteur du chronomètre, logée dans la place déjà prise
 *
 * « --panel-reach » est ce dont la page a besoin pour réserver au plateau la
 * place qu'il ne doit pas prendre — voir pages/solo.css. Le chronomètre n'y
 * ajoute rien : il se loge dans la bande que cette mesure laisse déjà libre
 * au-dessus du portrait.
 *
 * @package   PlayAwale
 * @version   2.0
 * @author    Nicolas Lion — Développeur web — https://nlion.fr
 * @copyright 2026 Nicolas Lion
 */

/* =============================================================================
   LES MESURES DONT TOUT DÉCOULE
   ============================================================================= */

.game-page {
  /* Côté des deux portraits — celui de l'adversaire en haut à gauche, celui du
     joueur en bas à droite.

     UNE SEULE MESURE POUR LES DEUX
     Ils se font face en diagonale : s'ils n'avaient pas exactement la même
     taille, l'un paraîtrait dominer l'autre. Deux déclarations séparées
     finissent toujours par diverger ; celle-ci les rend indissociables.

     PLUS PETITS QU'EN VERSION 1
     Celle-ci les fixait à 10vw, soit 192 px sur un écran de 1920 — deux
     portraits qui pesaient lourd autour d'un plateau qu'ils finissaient par
     recouvrir sur un téléphone couché. Chaque pixel repris ici est rendu au
     plateau, puisque la réserve découle de leur taille.

     Le plancher de 72 px les garde identifiables sur un petit écran, là où les
     40 px de la version 1 laissaient le bouton d'ouverture recouvrir le
     visage. */
  --avatar-panel-size: clamp(72px, 7.5vw, 140px);

  /* Largeur réellement occupée par un panneau, depuis le bord de l'écran.

     Le portrait est à 20 px du bord, et son score déborde vers le centre : de
     0,45 em pour une taille de 0,4 fois le portrait, soit un peu moins de
     0,2 fois sa largeur. D'où le facteur 1,2. Les 12 px finaux sont l'écart
     minimal qu'on veut voir entre un panneau et le plateau — sans lui, ils se
     toucheraient, ce qui se remarque autant qu'un chevauchement. */
  --panel-reach: calc(20px + var(--avatar-panel-size) * 1.2 + 12px);

  /* Taille du score, en fraction du portrait — la même pour les deux camps.

     Le score est ancré par son bord extérieur : un deuxième chiffre s'ajoute
     donc VERS le portrait, jamais vers le plateau. Mesuré : le débordement
     reste de 23 px à un chiffre comme à deux. Il ne menace pas le plateau,
     mais il couvre une bonne part du visage sur un petit portrait. */
  --score-ratio: 0.36;

  /* Hauteur du chronomètre, écart au portrait compris.

     ELLE NE COÛTE RIEN AU PLATEAU, ET C'EST LE POINT
     --panel-reach vaut 20 + 1,2 × portrait + 12, alors qu'un panneau n'occupe
     verticalement que 20 + portrait : le facteur 1,2 sert au score, qui déborde
     sur le CÔTÉ. Il reste donc, au-dessus du portrait et à l'intérieur de la
     place déjà réservée, une bande libre de 0,2 × portrait + 12 px — 26 px sur
     le plus petit téléphone, 40 px sur un grand écran.

     Le chronomètre se loge exactement dans cette bande. La réserve du plateau
     n'a pas à bouger, et le plateau ne perd pas un pixel. */
  --panel-clock-height: calc(var(--avatar-panel-size) * 0.2);
}

/* Écran couché et bas : le score se fait plus discret. Sur un portrait de
   72 px, deux chiffres à 36 % recouvrent le visage ; à 30 %, ils le laissent
   voir. */
@media (orientation: landscape) and (max-height: 34rem) {

  .game-page {
    --score-ratio: 0.3;
  }
}


/* =============================================================================
   L'ADVERSAIRE
   Portrait, score et bouton d'ouverture, ancrés en haut à gauche de l'écran.
   Reprend la disposition et les couleurs de la version 1, dont les valeurs ont
   été relevées à la mesure : carré de 10vw à 20 px des bords, score en blanc
   cassé à 4vw, triangle ambré à 3vw.
   ============================================================================= */

.ai-panel {
  /* Côté du portrait : mesure dont tout le panneau découle, partagée avec
     celui du joueur — voir --avatar-panel-size sur .solo-page. */
  --ai-panel-size: var(--avatar-panel-size);

  position: fixed;
  top: 20px;
  left: 20px;
  z-index: var(--z-content);

  width: var(--ai-panel-size);
  height: var(--ai-panel-size);
}

.ai-panel__avatar {
  width: 100%;
  height: 100%;

  /* Le portrait n'est jamais déformé : il s'inscrit dans le carré. */
  object-fit: contain;
}

/* -----------------------------------------------------------------------------
   Deux silhouettes identiques, distinguées par la teinte
   -----------------------------------------------------------------------------
   En partie à deux, aucun des deux joueurs n'est identifié : ils partagent donc
   le même portrait anonyme. Leur donner deux dessins différents laisserait
   croire qu'on sait quelque chose d'eux.

   Une rotation de teinte suffit à les séparer d'un coup d'œil, et ne demande ni
   second fichier ni retouche du dessin : le jour où l'on voudra une autre
   couleur, c'est ce seul angle qu'on changera. Le portrait passe des terres
   cuites du plateau à un ton plus froid, tout en gardant ses ombres et son
   reflet.

   Elle ne s'applique qu'à une silhouette anonyme : un joueur identifié aura son
   propre portrait, qu'il ne faudrait surtout pas repeindre.
   -------------------------------------------------------------------------- */
.ai-panel--tinted .ai-panel__avatar {
  filter: hue-rotate(150deg) saturate(0.85);
}

/* Score de l'ordinateur, posé en bas à droite du portrait et débordant
   légèrement — c'est ainsi qu'il se présente en version 1. */
.ai-panel__score {
  position: absolute;
  right: -0.45em;   /* aligne le score sur celui de la v1 */
  bottom: 0;

  font-family: var(--font-body);

  /* Voir --score-ratio sur .solo-page. La version 1 en donnait 40 % — un score
     qui, à côté d'un portrait désormais plus petit, prenait toute la place. */
  font-size: calc(var(--ai-panel-size) * var(--score-ratio));
  font-weight: 700;
  line-height: 1;
  color: #f0f0f0;

  /* Chiffres de largeur fixe : le score ne déplace rien en passant de 9 à 10. */
  font-variant-numeric: tabular-nums;

  /* Le blanc cassé se perd sur un fond clair : une ombre discrète le détache. */
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.45);
}

/* Bouton laissant l'ouverture à l'ordinateur, au-dessus et à droite du
   portrait. Sans fond ni bordure : le triangle ambré se suffit.

   Toutes ses mesures dérivent du portrait, y compris son rembourrage. La
   version 1 les donnait en pixels — 12/24 de rembourrage, décalé de −60 px —
   ce qui, sur un portrait rétréci, faisait déborder le bouton jusque sur le
   visage. En proportions, la disposition tient à toutes les tailles. */
.ai-panel__start {
  position: absolute;
  top: -10.4%;   /* −20 px sur 192 */
  right: -31%;   /* −60 px sur 192 */

  display: inline-flex;
  align-items: center;
  justify-content: center;

  padding: calc(var(--ai-panel-size) * 0.0625)   /* 12 px sur 192 */
           calc(var(--ai-panel-size) * 0.125);   /* 24 px sur 192 */

  color: #f0c551;
  background: transparent;
  border: 0;
  cursor: pointer;

  transition: color var(--duration-base) ease;
}

.ai-panel__start:hover,
.ai-panel__start:focus-visible {
  color: #157425;
}

.ai-panel__start-icon {
  /* 30 % du portrait : le rapport de la version 1, 3vw pour 10vw. */
  height: calc(var(--ai-panel-size) * 0.3);
  width: auto;
}


/* =============================================================================
   LE JOUEUR
   -----------------------------------------------------------------------------
   Portrait et score, ancrés en bas à droite de l'écran comme en version 1, et
   en miroir de l'adversaire qui occupe le haut à gauche : les deux camps se
   répondent en diagonale, dans le sens du plateau.

   MÊME MESURE DE RÉFÉRENCE QUE L'ADVERSAIRE
   Le panneau découle d'une seule mesure, --player-panel-size, calquée sur
   celle de l'adversaire. Les deux portraits font donc exactement la même
   taille à toutes les largeurs d'écran — un joueur ne paraît pas plus petit
   que son adversaire.

   LE PORTRAIT N'EST QU'UNE ADRESSE D'IMAGE
   Silhouette anonyme aujourd'hui, portrait du joueur reconnu demain : rien ici
   ne dépend de ce qui est affiché. Seul --identified ajoute le cerne qui
   distingue un joueur identifié.
   ============================================================================= */

.player-panel {
  --player-panel-size: var(--avatar-panel-size);

  position: fixed;
  right: 20px;

  /* Au-dessus de la barre système des téléphones, qui mange le bas de l'écran
     sur les modèles sans bouton d'accueil. */
  bottom: calc(20px + env(safe-area-inset-bottom, 0px));

  z-index: var(--z-content);

  width: var(--player-panel-size);
  height: var(--player-panel-size);
}

.player-panel__avatar {
  width: 100%;
  height: 100%;

  /* Le portrait n'est jamais déformé : il s'inscrit dans le carré. */
  object-fit: contain;
}

/* Score du joueur, posé en bas à GAUCHE du portrait et débordant légèrement.

   L'adversaire pose le sien à droite du sien : chacun vers le centre de
   l'écran, donc vers le plateau. Placés vers l'extérieur, ils sortiraient de
   la fenêtre sur un téléphone. */
/* -----------------------------------------------------------------------------
   Le chronomètre de réflexion
   -----------------------------------------------------------------------------
   Posé juste au-dessus du portrait, comme la pendule d'un joueur d'échecs est
   posée à côté de lui. Il n'avance que pendant le tour de son joueur, et ne
   repart de zéro qu'à une nouvelle partie — c'est le moteur qui en décide, pas
   cette feuille.

   IL EST ÉCRIT DANS LE PANNEAU, PAS DANS LA PAGE
   La classe ne nomme aucun camp : la partie à deux en posera un dans chacun des
   deux panneaux, sans rien ajouter ici.

   IL NE PREND AUCUNE PLACE
   Ni en largeur — il tient dans celle du portrait, si bien que --panel-reach
   reste ce qu'il était —, ni en hauteur : il se loge dans la bande déjà
   réservée au-dessus du portrait. Voir --panel-clock-height.

   UN CONTOUR, PAS UN CADRE
   La version 1 posait un rectangle blanc à coins arrondis. Un cadre, si petit
   soit-il, ferait une tache sur le décor et attirerait l'œil autant que le
   plateau. Le chiffre se détache donc par un contour sombre, comme les scores
   des deux camps : il se lit sur le sable clair comme sur le feuillage, et ne
   dessine rien autour de lui.
   -------------------------------------------------------------------------- */
.panel-clock {
  position: absolute;

  /* Adossé au bord supérieur du portrait, sur toute sa largeur. */
  bottom: 100%;
  left: 0;
  right: 0;

  /* Ce qui sépare le chiffre du sommet du crâne. Compris dans la bande libre. */
  margin-bottom: var(--space-2xs);

  font-family: var(--font-body);
  font-size: var(--panel-clock-height);
  font-weight: 700;

  /* Interligne exactement égal au corps : la hauteur occupée est alors celle
     annoncée par --panel-clock-height, sans supplément invisible. */
  line-height: 1;
  text-align: center;
  color: #f0f0f0;

  /* Le compteur ne doit pas tressauter d'une seconde à l'autre : chaque « 1 »
     est plus étroit qu'un « 8 » dans une fonte ordinaire. */
  font-variant-numeric: tabular-nums;

  /* Le contour : quatre ombres portées d'un pixel, une par direction. Elles
     cernent chaque chiffre d'un liseré sombre, ce qui le détache de n'importe
     quel fond — c'est ce que « -webkit-text-stroke » ferait, mais celui-ci
     ronge l'intérieur des lettres et n'est pas partout reconnu. */
  text-shadow:
     1px  1px 0 rgba(0, 0, 0, 0.75),
    -1px  1px 0 rgba(0, 0, 0, 0.75),
     1px -1px 0 rgba(0, 0, 0, 0.75),
    -1px -1px 0 rgba(0, 0, 0, 0.75),
     0    2px 4px rgba(0, 0, 0, 0.45);

  /* Il ne reçoit jamais le clic : rien ne doit devenir inatteignable parce
     qu'un chiffre passe devant. */
  pointer-events: none;
}

/* Éteint — le tour de l'autre camp —, il s'efface sans disparaître : le joueur
   peut toujours lire son temps, mais c'est au plateau qu'il doit regarder. */
.panel-clock:not(.panel-clock--running) {
  opacity: 0.55;
}

/* CELUI DU CAMP DU HAUT PASSE SOUS SON PORTRAIT
   -----------------------------------------------------------------------------
   Celui du bas se pose AU-DESSUS de son portrait ; celui du haut ne le peut pas
   — il sortirait de l'écran, le portrait étant déjà à 20 px du bord. Il passe
   donc DESSOUS, ce qui le met du même coup entre les deux joueurs, là où il se
   lit de part et d'autre.

   Rien d'autre ne change : c'est le même composant, aux mêmes mesures.

   La règle vit ici plutôt que dans la feuille d'un mode : le camp du haut a une
   pendule dès qu'un humain le tient — à deux sur un même appareil comme en
   réseau —, et les deux modes n'ont pas à la redécouvrir chacun de leur côté.
   En solo, le panneau du haut n'en porte aucune et la règle ne trouve rien.
   -------------------------------------------------------------------------- */
.ai-panel .panel-clock {
  top: 100%;
  bottom: auto;

  margin-top: var(--space-2xs);
  margin-bottom: 0;
}

.player-panel__score {
  position: absolute;
  left: -0.45em;
  bottom: 0;

  font-family: var(--font-body);

  /* Le même rapport que le score de l'adversaire. */
  font-size: calc(var(--player-panel-size) * var(--score-ratio));
  font-weight: 700;
  line-height: 1;
  color: #f0f0f0;

  /* Chiffres de largeur fixe : le score ne déplace rien en passant de 9 à 10. */
  font-variant-numeric: tabular-nums;

  /* Le blanc cassé se perd sur un fond clair : une ombre discrète le détache. */
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.45);
}

/* Nom du joueur, AU-DESSUS du portrait.

   Il n'est affiché que pour un joueur identifié : tant qu'on joue sans compte,
   la silhouette se suffit. La règle est écrite d'avance, avec celle du cerne,
   pour que la clé sage n'ait rien à ajouter ici.

   Au-dessus et non en dessous : le panneau touche déjà le bas de l'écran, un
   nom placé sous le portrait tomberait hors de la fenêtre.

   Sa largeur est bornée par celle du portrait, et un nom trop long est coupé
   plutôt que de déborder sur le plateau. */
.player-panel__name {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);

  max-width: calc(var(--player-panel-size) * 1.1);
  margin-bottom: 4px;

  font-family: var(--font-body);
  font-size: calc(var(--player-panel-size) * 0.11);
  line-height: 1.2;
  color: var(--color-text-muted);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;

  /* Le nom n'est pas cliquable : il ne doit pas intercepter un clic destiné au
     plateau sur un écran étroit. */
  pointer-events: none;
}

/* Joueur identifié : un cerne le distingue de la silhouette anonyme. La règle
   est écrite d'avance ; elle ne s'appliquera qu'avec la clé sage. */
.player-panel--identified .player-panel__avatar {
  border-radius: 50%;
  box-shadow: 0 0 0 calc(var(--player-panel-size) * 0.02) var(--color-brand-amber);
}


/* -----------------------------------------------------------------------------
   Écran étroit
   -----------------------------------------------------------------------------
   Le portrait ne rétrécit pas : il garde la taille de celui de l'adversaire,
   sans quoi le joueur paraîtrait le plus petit des deux. Seules les marges se
   resserrent, pour laisser au plateau toute la largeur qu'il peut prendre.

   La barre d'outils occupe le bas à gauche, le panneau le bas à droite : sur un
   écran de 390 px, la barre s'arrête vers 210 px et le portrait commence vers
   282 px. Ils ne se rejoignent pas.
   -------------------------------------------------------------------------- */
@media (max-width: 34rem) {

  .player-panel {
    right: 12px;
    bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  }
}


/* =============================================================================
   LA PLACE LAISSÉE AU PLATEAU
   -----------------------------------------------------------------------------
   Les portraits, les scores et les boutons sont posés en position fixe aux
   quatre coins de l'écran : ils flottent au-dessus de la page, et rien ne les
   empêche de recouvrir le plateau. C'est ici qu'on les en empêche, en disant au
   plateau la place qu'il ne doit pas prendre.

   POURQUOI RÉSERVER SUR UN SEUL AXE À LA FOIS
   Un élément posé dans un coin ne recouvre le plateau que s'il le croise SUR
   LES DEUX AXES. Il suffit donc de le dégager sur un seul — et mieux vaut
   choisir celui où la place ne manque pas, pour ne pas rapetisser le plateau
   sans raison.

   Écran couché : le plateau est large, la hauteur est comptée. Les panneaux se
   trouvent alors de part et d'autre, on dégage en LARGEUR.
   Écran debout : le plateau est bas, la largeur est comptée. Les panneaux se
   trouvent au-dessus et au-dessous, on dégage en HAUTEUR.

   Sur un écran de bureau, aucune de ces réserves n'est contraignante : le
   plateau garde son plafond de 120 px par trou.
   ============================================================================= */

/* -----------------------------------------------------------------------------
   Téléphone couché : marges et pastilles resserrées.
   -----------------------------------------------------------------------------
   Le plateau reste centré, la bulle reste en haut : c'est la disposition qu'on
   lit le mieux. La place lui est rendue autrement — par des portraits, des
   pastilles et un texte plus petits, dont découlent les réserves.
   -------------------------------------------------------------------------- */
@media (orientation: landscape) and (max-height: 34rem) {

  .game-page {
    /* Marge resserrée : sur un écran de 390 px de haut, les 48 px de marge
       d'un écran de bureau coûteraient un huitième de la hauteur. */
    padding: 12px;
  }

  /* Le plateau penche légèrement vers la droite.

     Les deux côtés ne sont pas également encombrés : à gauche se tiennent le
     portrait de l'adversaire ET la colonne de boutons, sur toute la hauteur ;
     à droite, seulement le portrait du joueur, et seulement en bas.

     DIX PIXELS, ET PAS DAVANTAGE
     La marge disponible à droite vaut la portée d'un panneau moins ce qu'il
     occupe vraiment : 118 px de portée pour 102 px occupés, soit 17 px. Ce
     chiffre ne dépend pas de la taille de l'écran, les deux mesures découlant
     du portrait. Dix pixels de décalage en laissent donc sept, quel que soit
     le téléphone — et le plateau reste manifestement centré, seulement
     déporté. */
  .board-container {
    left: 10px;
  }
}

@media (orientation: landscape) {

  .board-container {
    /* Un panneau de chaque côté. */
    --board-reserve-x: calc(2 * var(--panel-reach));
  }
}

@media (orientation: portrait) {

  .board-container {
    /* Un panneau au-dessus, un panneau et la barre d'outils au-dessous.

       Le chronomètre du joueur n'entre pas dans ce calcul, et c'est voulu : il
       se loge dans la bande que le facteur 1,2 de --panel-reach laisse déjà
       libre au-dessus du portrait. Il ne coûte donc pas un pixel au plateau. */
    --board-reserve-y: calc(2 * var(--panel-reach));
  }
}

