/* ============ ACCUEIL ============ */
.hero {
  margin:14px var(--pad) 0; background:var(--surface);
  border:1px solid var(--line); border-radius:var(--r-lg); padding:16px 18px 14px;
}
.hero .cap { font-size:11px; letter-spacing:.13em; text-transform:uppercase; color:var(--ink-2); font-weight:600; }
.hero .row1 { display:flex; justify-content:space-between; align-items:center; margin-top:8px; gap:12px; }
.hero .score {
  font-family:var(--mono); font-size:44px; font-weight:600;
  letter-spacing:-.03em; line-height:.9; color:var(--accent);
  min-width:0;   /* le score peut se serrer plutôt que pousser l'icône dehors */
}
.hero .score small { font-size:15px; color:var(--ink-2); font-weight:500; font-family:var(--sans); }
.hero .sub { font-size:12.5px; color:var(--ink-2); margin-top:9px; line-height:1.45; }
/* La cible est DÉCORATIVE : elle cède la place au chiffre au lieu de sortir de
   la carte. À 220 px de large — un zoom fort — un score long la poussait 25 px
   au-delà du bord, où `.hero { overflow:hidden }` la coupait en deux. Une icône
   à moitié tranchée fait croire à un écran cassé ; réduite, elle ne dit rien de
   moins, puisqu'elle ne dit rien. */
.hero .tgt { width:52px; height:52px; flex:0 1 auto; min-width:0; color:var(--accent); opacity:.85; }

.curvecap { font-size:11px; letter-spacing:.13em; text-transform:uppercase; color:var(--ink-2); font-weight:600; margin:20px 0 2px; }
svg.spark { display:block; width:100%; height:94px; overflow:visible; }
.xaxis { display:flex; justify-content:space-between; font-size:11px; color:var(--ink-3); font-family:var(--mono); margin-top:4px; }
/* UNE COURBE QU'ON PEUT INTERROGER (06/10/2026). Le repère vit DANS le svg :
   `.card` et `.hero` portent `overflow:hidden`, donc une bulle flottante posée
   près du point serait coupée par la carte. Et `.pointable` n'est posée que par
   `brancherAppui`, jamais par les deux fonctions de tracé : une courbe muette
   garde exactement son rendu d'avant, au pixel. */
svg.spark.pointable { cursor:pointer; }
.sparkmark line { opacity:.5; }

/* Deux colonnes tant qu'elles tiennent, une seule ensuite. En `1fr 1fr` en dur,
   une colonne ne peut pas descendre sous la largeur intrinsèque de son contenu
   (« 9,50 » en chiffres de 26 px + son libellé) : les cartes débordaient de
   l'écran sous ~250 px et à fort zoom. Même correctif que `.duo` pour les
   formulaires — un chiffre coupé en deux ne se lit pas. */
.grid2 { display:grid; grid-template-columns:repeat(auto-fit, minmax(132px, 1fr)); gap:10px; margin:12px var(--pad) 0; }
.grid2 > * { min-width:0; }
.stat { background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md); padding:13px 15px; }
.stat .k {
  font-size:11px; letter-spacing:.11em; text-transform:uppercase; color:var(--ink-2);
  font-weight:600; display:flex; align-items:center; justify-content:space-between; gap:8px;
}
.stat .k .ic { color:var(--accent); font-size:14px; }
.stat .v { font-family:var(--mono); font-size:26px; font-weight:600; margin-top:6px; line-height:1.15; }
/* le complément passe sous le chiffre : il ne doit jamais couper le nombre en deux */
.stat .v small { display:block; margin-top:3px; font-size:12px; color:var(--ink-2); font-family:var(--sans); font-weight:500; line-height:1.35; }

.insight {
  margin:12px var(--pad) 0; width:calc(100% - var(--pad) * 2);
  background:var(--surface); border:1px solid var(--line); border-inline-start:3px solid var(--accent);
  border-radius:14px; padding:14px 15px; display:flex; align-items:center; gap:11px;
  color:var(--ink); font-family:var(--sans); text-align:start;
}
.insight .txt { flex:1; }
.insight .k { display:block; font-size:11px; letter-spacing:.13em; text-transform:uppercase; color:var(--ink-2); font-weight:600; }
.insight .b { font-size:13.5px; margin-top:5px; line-height:1.5; }
.insight .b em { color:var(--accent); font-style:normal; font-weight:700; }

.goal { margin:12px var(--pad) 0; background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md); padding:14px 16px; }
.goal .gl { display:flex; justify-content:space-between; align-items:baseline; font-size:12.5px; color:var(--ink-2); }
.goal .gl b { font-family:var(--mono); color:var(--ink); font-size:15px; }
.goal .bar { height:8px; border-radius:20px; background:var(--surface-3); margin-top:10px; overflow:hidden; }
.goal .bar i { display:block; height:100%; background:var(--accent); border-radius:20px; }

/* ============ SAISIE — commun ============ */
/* Plein écran (onboarding, déverrouillage) : pas de marge basse — SAUF ce que la bande
   « Nouvelle version prête » occupe. C'est précisément ici que le défaut du 13/09/2026
   se produisait : cette règle écrasait celle de `base.css`, et le bouton « Continuer »
   de l'étape 2 devenait inatteignable dès qu'une mise à jour s'annonçait. */
.app[data-chrome="off"] .view { padding-bottom:var(--maj-barre, 0px); }
.scoreview { display:flex; flex-direction:column; min-height:100%; }
.sctop { padding:14px var(--pad) 0; display:flex; align-items:center; gap:12px; }
.sctop .disc { flex:1; min-width:0; }
.sctop .disc b { font-size:15px; font-weight:700; display:block; }
.sctop .disc small { display:block; font-size:11.5px; color:var(--ink-2); font-weight:500; margin-top:2px; }

.serie { padding:16px var(--pad) 0; }
.serie .lab {
  display:flex; justify-content:space-between; font-size:11px; letter-spacing:.11em;
  text-transform:uppercase; color:var(--ink-2); font-weight:600; margin-bottom:9px;
}
.serie .lab b { color:var(--accent); font-family:var(--mono); }
.shots { display:grid; grid-template-columns:repeat(10, 1fr); gap:5px; }
.shots .sh {
  aspect-ratio:1; border-radius:8px; background:var(--surface-2); border:1px solid var(--line);
  display:flex; align-items:center; justify-content:center;
  font-family:var(--mono); font-size:11px; font-weight:600; color:var(--ink-3);
}
.shots .sh.done { background:var(--accent-soft); color:var(--accent); border-color:transparent; }
.shots .sh.done.zero { background:transparent; color:var(--miss); border-color:var(--miss); }
.shots .sh.cur { border:2px solid var(--accent); color:var(--accent); }

/* Marge latérale `--pad` comme partout ailleurs (constat Bilel, 13/08, capture
   à l'appui : « le texte est bouffé »). Le padding horizontal était à ZÉRO :
   la phrase du compte à rebours touchait le bord gauche à 0 px et se lisait
   comme tranchée. Mesuré avant correction : .readsub de 0 à 375 sur un écran
   de 375. Un texte collé au bord n'a pas besoin de déborder pour paraître
   coupé — c'est pour ça que les sondes de DÉBORDEMENT ne l'avaient jamais vu. */
.readout { flex:0 0 auto; display:flex; flex-direction:column; align-items:center; padding:18px var(--pad) 4px; }
.readval { font-family:var(--mono); font-size:56px; font-weight:600; letter-spacing:-.03em; line-height:1; color:var(--accent); }
.readsub { font-size:12.5px; color:var(--ink-2); margin-top:6px; text-align:center; }

.inzone { flex:1 1 auto; display:flex; flex-direction:column; justify-content:flex-end; padding-bottom:10px; }
.scfoot {
  flex:0 0 auto; display:flex; gap:10px; padding:12px var(--pad) calc(14px + env(safe-area-inset-bottom));
  border-top:1px solid var(--line); background:var(--surface);
  /* Toujours visible : Annuler et Terminer ne doivent jamais demander de
     défiler (la saisie Élec. dépasse l'écran, le tireur restait « coincé »). */
  position:sticky; bottom:0; z-index:5;
}
/* `min-width:0` : sans lui, un élément flex refuse de descendre sous la largeur
   intrinsèque de son contenu — les deux boutons du pied débordaient donc sur un
   écran très étroit ou à fort zoom, et « Terminer », le bouton le plus important
   de la saisie, se faisait rogner. */
.scfoot .btn { padding:15px; font-size:15px; min-width:0; }

/* pavé numérique — nom distinct de la classe utilitaire .pad (marge intérieure) */
.numpad { display:grid; grid-template-columns:repeat(4, 1fr); gap:9px; padding:4px var(--pad); }
.numpad button {
  border:1px solid var(--line); background:var(--surface-2); color:var(--ink);
  font-family:var(--mono); font-size:20px; font-weight:600; border-radius:var(--r-sm);
  padding:16px 0; cursor:pointer; min-height:56px;
}
.numpad button:active { background:var(--accent-soft); border-color:var(--accent); color:var(--accent); }

/* Le pavé d'écriture au doigt (`.writepad` et ses voisins) a été RETIRÉ le
   14/08/2026 avec sa méthode de saisie — son CSS part avec lui. Une règle
   orpheline n'est pas inoffensive : elle se recopie, et elle laisse croire
   que l'écran existe encore. */
.decimals { display:grid; grid-template-columns:repeat(5, 1fr); gap:7px; padding:10px var(--pad) 0; }
.decimals button {
  border:1px solid var(--line); background:var(--surface-2); color:var(--ink-2);
  font-family:var(--mono); font-size:14px; font-weight:600; border-radius:10px;
  padding:12px 0; cursor:pointer; min-height:46px;
}
.decimals button[aria-pressed="true"] { background:var(--accent-soft); border-color:var(--accent-line); color:var(--accent); }

/* cible + plot d'impacts */
.plotwrap { padding:8px var(--pad) 0; display:flex; flex-direction:column; align-items:center; }
/* Aussi large que l'écran le permet : chaque pixel gagné est de la précision
   gagnée sous le doigt. 290 px donnaient 14 px par anneau pour un doigt qui en
   couvre 45 — la loupe fait le reste, mais la taille aide d'abord.
   En POURCENTAGE et non en `vw` : `vw` mesure la fenêtre, pas la carte — la
   cible débordait donc de son conteneur dès que l'app était plus étroite que
   l'écran (fenêtre large, ou fort zoom texte). */
/* LA CIBLE EST UN CARTON, pas un motif dans le thème (07/09/2026, demande
   Bilel : « le centre avec le gris n'est pas du tout lisible — il faudrait se
   rapprocher de la cible officielle »).
   Elle porte donc ses propres couleurs, quel que soit le thème de l'app —
   même raison que la carte du directeur de tir, forcée en blanc depuis le
   11/08 : une feuille de stand ne se lit pas dans le thème du carnet.
   Les deux teintes viennent de la photo d'un GEF 50 Match, mesurée
   (ORACLE-CARTONS §8) : papier crème, encre noire, et ~150 niveaux d'écart.
   Le carré, lui, n'est pas une licence : la zone RÉELLEMENT cliquable a
   toujours été le carré du SVG (le `border-radius` ne clippait que le fond,
   jamais les événements), et un appui dans un coin vaut zéro — comme sur le
   carton, hors de la zone cotée. Le papier montre donc enfin ce qu'on touche. */
.plot {
  /* Les QUATRE couleurs du carton, ensemble et figées : la cible ne doit pas
     changer d'aspect selon le thème de l'app, sinon le commentaire ci-dessus
     serait faux. L'ambre et le vert valent aujourd'hui ceux de `--warn` et
     `--good`, mais ils sont écrits ici pour que retoucher une palette du
     carnet ne repeigne jamais un carton. */
  --papier:#F2EFE6; --encre:#141210; --ambre:#D9A441; --vert:#2FA97E;
  width:min(100%, 400px); aspect-ratio:1; touch-action:none; cursor:crosshair;
  background:var(--papier); border-radius:6px;
}

/* --- visée et loupe : viser, ajuster, relâcher --- */
/* Le réticule marque le point exact sous le doigt, la loupe le montre ailleurs.
   `pointer-events:none` sur les deux : ils ne doivent jamais intercepter le
   geste qu'ils servent à guider. */
/* Les mêmes règles servent la cible tactile ET la photo du carton : c'est le
   MÊME geste (viser, ajuster, relâcher) et il doit se voir pareil. Deux jeux
   de styles auraient fini par diverger. */
.plot .vise, .plot .loupe,
.photoplot .vise, .photoplot .loupe { opacity:0; pointer-events:none; transition:opacity .09s; }
.plot .vise.on, .plot .loupe.on,
.photoplot .vise.on, .photoplot .loupe.on { opacity:1; }
.photoplot .visering { fill:none; stroke:var(--accent); stroke-width:2; vector-effect:non-scaling-stroke; }
.photoplot .visecross { stroke:var(--accent); stroke-width:1.5; vector-effect:non-scaling-stroke; }
.photoplot .loupebg { fill:var(--surface); }
/* Sur la CIBLE, le viseur ne peut pas être en `--accent` : il vaut presque du
   blanc en thème sombre, donc il disparaîtrait sur le papier. L'ambre tranche
   sur les deux fonds du carton — c'est déjà pour ça qu'il marque les repères
   de la photo — et il ne se confond ni avec un impact (blanc) ni avec le point
   moyen (vert). La loupe montre du carton : son fond est du papier. */
.plot .visering { fill:none; stroke:var(--ambre); stroke-width:2; vector-effect:non-scaling-stroke; }
.plot .visecross { stroke:var(--ambre); stroke-width:1.5; vector-effect:non-scaling-stroke; }
.plot .loupebg { fill:var(--papier); }
/* Dans la loupe de la photo, c'est la GRILLE posée par le tireur qui doit
   ressortir — c'est elle qu'il aligne sur les anneaux imprimés du carton. */
.photoplot .loupe circle.gridring { stroke:var(--accent); stroke-opacity:.9; }
.photoplot .loupe .photoimpact { stroke:var(--accent); }
.photoplot .loupering { fill:none; stroke:var(--accent); stroke-width:2; vector-effect:non-scaling-stroke; }
.photoplot .loupecross { stroke:var(--accent); stroke-width:1.5; vector-effect:non-scaling-stroke; }
/* La loupe ne repeint PLUS la cible (07/09/2026). Trois règles y forçaient un
   trait gris et un voile de noir plus dense : elles compensaient une cible sans
   contraste, et elles écraseraient maintenant le vrai rendu — un anneau blanc
   sur du noir n'a besoin d'aucune aide pour ressortir. La loupe montre
   exactement le carton, en plus grand. */
.plot .loupering { fill:none; stroke:var(--ambre); stroke-width:2; vector-effect:non-scaling-stroke; }
.plot .loupecross { stroke:var(--ambre); stroke-width:1.5; vector-effect:non-scaling-stroke; }
/* Les traits sont en pixels, pas en unités du viewBox : sans vector-effect,
   une épaisseur de 1 couvrirait la moitié de la cible. */
/* LE TRAIT S'INVERSE AU BORD DU VISUEL, et c'est la seule règle qui compte.
   Mesuré sur la photo d'un GEF 50 Match (ORACLE-CARTONS §8) : dans le noir le
   trait est BLANC (fond 50, trait 211-237), sur le papier il est NOIR (fond
   202, trait 31-73) — ~150 niveaux d'écart des deux côtés, sous le même
   éclairage. Un rendu fidèle se règle donc sur « papier = blanc, visuel =
   noir », jamais sur les valeurs absolues de cette photo-là.
   L'ÉPAISSEUR n'a jamais été le problème : le trait mesure 0,3 à 0,5 mm pour
   un pas d'anneau de 4 mm, soit 1/10 — à 335 px de cible et dix anneaux, un
   trait fidèle ferait 1,6 px. Le 1 px en place est donc déjà un peu fin, et
   c'était bien la COULEUR qui rendait le centre illisible. */
.plot circle.ringline { fill:none; stroke:var(--encre); stroke-width:1; vector-effect:non-scaling-stroke; }
.plot circle.ringline.sur-noir { stroke:var(--papier); }
/* Le visuel est NOIR, pas un voile de 14 % : c'est ce qui rendait le centre
   illisible (« le centre avec le gris n'est pas du tout lisible »). */
.plot circle.black { fill:var(--encre); stroke:none; }
/* Valeur de chaque anneau (demande Bilel, 10/08 : « savoir à quoi correspond
   le cercle »). Taille en unités du viewBox, donc elle suit la cible quelle
   que soit la place à l'écran. `pointer-events:none` est indispensable : un
   chiffre qui intercepte l'appui, c'est un coup perdu au pas de tir.
   Mesuré : à 0,075 le chiffre sortait à 4 × 9 px — présent mais illisible.
   0,105 le porte à ~12 px de haut pour un écart d'anneau de 10 px, ce qui
   tient sans se chevaucher. */
.plot text.ringnum {
  fill:var(--encre); font-family:var(--mono); font-size:0.105px;
  font-weight:700; pointer-events:none; user-select:none;
}
/* Sur le noir, le chiffre doit rester lisible — c'est ce que fait la vraie
   cible, où les zones du noir sont numérotées en clair. */
.plot text.ringnum.sur-noir { fill:var(--papier); }
/* Lignes de visée du vitesse olympique (ISSF 6.3.4.4) : elles REMPLACENT les
   chiffres latéraux sur ce carton, et le tireur s'en sert pour aligner son arme
   entre deux séries. Blanches sur le noir de la cible, donc en `--bg` : le noir
   couvre ici toute la surface de comptage. `pointer-events:none` pour la même
   raison que les chiffres — une ligne qui intercepte l'appui coûte un coup. */
.plot line.aimline {
  stroke:var(--papier); stroke-width:3; vector-effect:non-scaling-stroke;
  pointer-events:none;
}
/* UN MARQUEUR DOIT SE VOIR SUR LES DEUX FONDS, et c'est ce que le carton
   impose : le tireur groupe ses coups dans le noir et en pose aussi sur le
   papier. Un aplat d'accent était près du blanc en thème sombre (invisible sur
   le papier) et près du noir en thème clair (invisible dans le visuel).
   Un coup compté est donc un disque de PAPIER cerné d'ENCRE ; un coup d'essai
   est son INVERSE — disque d'encre cerné de papier. Les deux se lisent des
   deux côtés, et l'un ne se confond jamais avec l'autre. C'est le principe du
   double trait de la photo (v493), tenu ici avec un seul cercle. */
.plot .impact { fill:var(--papier); stroke:var(--encre); stroke-width:1.4; vector-effect:non-scaling-stroke; }
/* Coup d'essai : visible pour corriger son réglage, en vidéo inverse pour
   qu'on ne le confonde jamais avec un coup compté. */
.plot .impact.essai { fill:var(--encre); stroke:var(--papier); stroke-width:1.4; vector-effect:non-scaling-stroke; }
/* Le dernier coup posé : l'ambre le désigne sans changer sa nature, et c'est
   la seule teinte de cet écran qui tranche sur le papier comme dans le noir.
   Le viseur la partage, mais il ne vit que le temps du geste. */
.plot .impact.last { stroke:var(--ambre); stroke-width:2.6; }
.plot .mpoi { fill:none; stroke:var(--vert); stroke-width:2; vector-effect:non-scaling-stroke; }
.plothint { font-size:12px; color:var(--ink-2); margin-top:10px; text-align:center; line-height:1.5; }

/* LE PAD DE RÉGLAGE FIN (06/09/2026) — quatre flèches autour de la valeur du
   coup qu'elles déplacent. Les boutons reprennent `.iconbtn.sq` : sa cible de
   44 px est déjà éprouvée, et une taille écrite ici en aurait fait une
   seconde, qui aurait divergé. Une seule icône (`chev`) tournée par le CSS —
   c'est déjà l'idiome des volets, et le sprite n'a pas de flèche à quatre
   sens.
   `[hidden]` est EXPLICITE : une règle d'auteur bat toujours la feuille de
   l'agent utilisateur, quelle que soit la spécificité (payé le 17/08 sur le
   widget de la barre du bas). */
.padfin { margin-top:12px; }
.padfin[hidden] { display:none; }
.padgrid { display:grid; grid-template-columns:repeat(3, 44px); grid-template-rows:repeat(3, 44px);
  gap:8px; justify-content:center; align-items:center; }
.padf.n { grid-column:2; grid-row:1; }
.padf.w { grid-column:1; grid-row:2; }
.padf.e { grid-column:3; grid-row:2; }
.padf.s { grid-column:2; grid-row:3; }
.padf.n .i { transform:rotate(-90deg); }
.padf.w .i { transform:rotate(180deg); }
.padf.s .i { transform:rotate(90deg); }
.padval { grid-column:2; grid-row:2; text-align:center; font-size:17px; font-weight:800;
  font-variant-numeric:tabular-nums; color:var(--accent); }

/* saisie sur photo du carton */
/* `min(100%, …)` et non `76vw` : `vw` mesure la FENÊTRE, pas la carte — dès que
   l'app est plus étroite que l'écran (fenêtre large, ou fort zoom texte), la
   photo débordait de son conteneur. C'est le piège corrigé le 10/08 sur la
   cible tactile puis sur les trois voisins de cet écran (sélecteur de mode,
   curseur de taille, « Changer de photo ») — l'image elle-même l'avait gardé,
   et c'est la plus grande des quatre. */
/* 400 px ET NON 290 (07/09/2026) — la même largeur que la cible tactile
   depuis le 10/08, et pour la même raison. Le carton ENTIER tient dans cette
   case : à 290 px, un trou de .22 sur un C50 fait 1,1 % du diamètre, donc
   3 px — on ne pouvait pas le voir, encore moins juger si l'anneau blanc
   tombait dessus. À 400 px il en fait 4,8, et l'écart entre deux propositions
   grandit d'autant. `min(100%, …)` reste : sur un téléphone la carte fait
   moins que 400, et c'est elle qui borne. */
.photoplot {
  width:min(100%, 400px); aspect-ratio:1; touch-action:none; cursor:crosshair;
  border-radius:18px; background:#000; border:1px solid var(--line);
}
/* LES DEUX REPÈRES DE LECTURE (06/09/2026) — le bord de la cible et le bord
   du visuel noir. Ambre plutôt que vert : c'est la couleur du cercle de
   TargetScan, et surtout elle ne se confond avec aucun impact. Le trait est
   plus fin qu'avant (1.2 -> 1) et moins couvrant : on doit voir le CARTON à
   travers, c'est tout l'objet du changement. */
.photoplot .gridring { fill:none; stroke:var(--warn); stroke-width:1; vector-effect:non-scaling-stroke; opacity:.75; }
.photoplot .gridcenter { fill:var(--warn); }
/* SUR UNE PHOTO, TOUT MARQUEUR EST CREUX (09/09/2026, demande de Bilel :
   « pourquoi certains sont transparents et d'autres blancs ? ils doivent tous
   être transparents »).

   Un coup posé était un DISQUE PLEIN — l'idiome de la cible tactile, où il
   s'oppose à l'anneau d'un coup d'essai. Sur une PHOTO, ce disque cache le trou
   qu'il désigne, et c'est le carton qu'on relit. Il devient donc un anneau, et
   ce qui le distingue d'une simple proposition n'est plus le remplissage mais
   l'ÉPAISSEUR : un coup compté porte un trait franc, une proposition un trait
   fin. C'est le même raisonnement qu'au 07/09 pour les propositions — on doit
   voir le CARTON à travers — poussé jusqu'au bout.

   La cible tactile, elle, ne change pas : là-bas il n'y a pas de photo dessous,
   et le disque plein reste la meilleure marque d'un coup compté.

   ET TOUS LES TRAITS ONT LA MÊME ÉPAISSEUR (09/09/2026, seconde demande de
   Bilel : « pourquoi certains ont le contour plus épais ? ils doivent être tous
   pareils »). L'épaisseur avait remplacé le remplissage pour distinguer un coup
   posé d'une proposition ; elle saute à l'œil et fait croire à deux natures
   d'impact. Ce qui distingue reste le MOTIF — plein trait pour ce qui est
   certain, tireté pour un essai, pointillé pour un amas —, et il ne coûte
   aucune épaisseur. La confusion est d'ailleurs rare par construction :
   `photo.lecture = null` après « Ajouter », donc un coup posé et sa proposition
   ne coexistent que lors d'une SECONDE lecture. */
.photoplot .photoimpact { fill:none; stroke:var(--accent); stroke-width:1.4; vector-effect:non-scaling-stroke; }
/* Coup d'essai : le même anneau, tireté — il ne compte pas. */
.photoplot .photoimpact.essai { stroke-dasharray:3 2; }
.photoplot .photoimpact.last { stroke:var(--good); }
/* IMPACT PROPOSÉ PAR LA LECTURE — un anneau BLANC FIN, cerné de sombre.
   (07/09/2026, constat Bilel : « le centre de la cible est totalement
   illisible, on ne voit rien — et le tireur veut quand même savoir où il a
   tiré ». Il demandait « les impacts entourés comme des petites pastilles
   blanches ». REMPLACE la décision du 06/09 qui les avait passés en ROUGE
   comme chez TargetScan : la couleur n'était pas le problème, l'épaisseur du
   trait l'était.)

   CE QUI RENDAIT LE CENTRE ILLISIBLE, MESURÉ : le plot fait 290 px pour
   100 unités de viewBox, donc un anneau de r = 1,9 u fait 11 px de diamètre —
   et il portait un trait de 2 px (36 % du rayon) dont les tirets, eux, sont à
   l'échelle (3 u = 8,7 px pour une circonférence de 35 px). Quatre gros arcs
   épais sur onze pixels : à l'œil le pointillé se REFERME en un disque plein,
   et trente propositions groupées font une tache d'un seul tenant.

   Trois leviers, et le premier fait presque tout : le trait passe à 1 px, le
   pointillé disparaît des impacts isolés, et la couleur devient le blanc — on
   doit voir le CARTON à travers, c'est tout l'objet du changement. C'est le
   même raisonnement que la grille en v482, un cran plus loin.

   LE HALO N'EST PAS UNE DÉCORATION : un carton se photographie visuel noir au
   centre et papier blanc autour. Le blanc seul disparaîtrait sur le papier, le
   sombre seul disparaîtrait dans le noir. Le double trait — sombre dessous,
   blanc dessus — se voit sur les deux fonds et ne coûte qu'un cercle de plus.
   Il se dessine en DEUX PASSES (tous les halos, puis tous les anneaux) : sinon
   le halo d'un impact mangerait l'anneau de son voisin, ce qui est exactement
   le cas d'un groupement serré.

   Le pointillé reste aux AMAS, et à eux seuls : il n'est plus là pour dire
   « proposition » (l'anneau blanc s'oppose déjà au disque plein d'un coup
   posé) mais pour dire « je ne sais pas combien de balles il y a là ». */
.photoplot .photohalo { fill:none; stroke:#000; stroke-opacity:.55; stroke-width:2.6; vector-effect:non-scaling-stroke; }
.photoplot .photoimpact.propose { fill:none; stroke:#fff; stroke-width:1.4; stroke-dasharray:none; }
/* Un AMAS — plusieurs balles dans une seule tache. L'app ne sait pas les
   séparer : elle le dit en portant le nombre qu'il contient, plutôt que de
   poser un point qui vaudrait pour tous. */
/* SON TRAIT EST À ÉPAISSEUR CONSTANTE, COMME LES AUTRES (09/09/2026 — « pourquoi
   certains sont transparents et d'autres blancs ? »).
   Il lui manquait `non-scaling-stroke` : son trait d'UNE UNITÉ de viewBox était
   donc dessiné à l'échelle — quatorze pixels sur un cercle de rayon 1,1 —, et le
   pointillé se refermait en DISQUE PLEIN. C'est exactement le défaut corrigé le
   07/09 pour les propositions, resté ici parce que la règle des amas avait été
   écrite à côté, sans hériter de `.photoimpact`. Le tiret suit maintenant le
   même trait : on voit le carton à travers un amas comme à travers le reste. */
.photoplot .photoamas { fill:none; stroke:#fff; stroke-width:1.4; stroke-dasharray:3 2.4; vector-effect:non-scaling-stroke; }
/* Le nombre d'un amas : AU-DESSUS du cercle, jamais en travers des trous
   qu'on cherche justement à voir. Son contour était à l'échelle (0,8 u =
   2,3 px sur un texte de 10) : il bavait au point d'en faire un pavé noir.
   Non-scaling, il ne fait plus que cerner. */
.photoplot .photoamasnum { fill:#fff; font-size:2.8px; font-weight:700; text-anchor:middle; paint-order:stroke; stroke:#000; stroke-opacity:.8; stroke-width:2px; vector-effect:non-scaling-stroke; }

/* zones IPSC */
.zones { display:grid; grid-template-columns:repeat(2, 1fr); gap:10px; padding:6px var(--pad) 0; }
.zones button {
  border:1px solid var(--line); background:var(--surface-2); color:var(--ink);
  border-radius:var(--r-md); padding:18px 10px; cursor:pointer; min-height:74px;
  font-family:var(--sans); display:flex; flex-direction:column; align-items:center; gap:3px;
}
.zones button b { font-family:var(--mono); font-size:22px; font-weight:600; }
.zones button span { font-size:11.5px; color:var(--ink-2); }
.zones button:active { border-color:var(--accent); background:var(--accent-soft); }
.zones button.neg b { color:var(--miss); }
/* Les quatre pénalités IDPA (3, 5, 10 et 20 s). `auto-fit` + `minmax` avec un
   plancher qui se lève : deux par ligne sur un téléphone, quatre au large, et
   jamais de colonne plus étroite que la carte — le piège corrigé le 13/08 sur
   `.duo` / `.trio`, où un `minmax(128px, 1fr)` débordait au zoom fort. */
.penrow { display:grid; grid-template-columns:repeat(auto-fit, minmax(min(140px, 100%), 1fr)); gap:8px; padding:6px var(--pad) 0; }
.penrow button { min-height:44px; font-size:13px; }
.timer { text-align:center; padding:4px var(--pad) 0; }
.timer .tv { font-family:var(--mono); font-size:40px; font-weight:600; color:var(--accent); letter-spacing:-.02em; }
.timer .tl { font-size:11.5px; color:var(--ink-2); letter-spacing:.1em; text-transform:uppercase; font-weight:600; }
/* Carte d'accueil feuilletable : une page par contexte comparable.
   `overflow:hidden` sur la carte, `will-change` sur la piste — le glissement
   suit le doigt sans repeindre toute la page. */
.hero { overflow:hidden; }
.heropiste { will-change:transform; transition:transform .18s ease-out; }
/* La pastille est petite à l'ŒIL — 7 px — mais sa CIBLE fait 44 × 44 : gants
   aux mains, c'est la règle partout dans l'app.
   Elle ne les faisait pas. Le commentaire annonçait 44 px et la règle posait
   `width:28px` : seule la hauteur y était, et c'est ce qui a fait passer les
   audits successifs (mesurer la boîte donnait « 44 » en hauteur, on s'arrêtait
   là). Défaut trouvé à l'audit du 10/08 au soir, zone de contact sondée à
   `elementFromPoint` : 28 × 44, sans aucun pseudo-élément d'extension.
   Étendre la zone par `::after` comme `.tog` était exclu : 2 px séparent deux
   pastilles, les zones se seraient chevauchées et on aurait touché la voisine.
   La rangée S'ENROULE donc — même idiome que `.seg` depuis le 10/08 : à huit
   contextes elle passe sur deux lignes au lieu de rogner la cible. */
.heropoints { display:flex; flex-wrap:wrap; justify-content:center; gap:2px 0; margin-top:10px; }
.heropoint {
  width:44px; height:44px; padding:0; border:0; background:none; cursor:pointer;
  display:flex; align-items:center; justify-content:center;
}
.heropoint i { width:7px; height:7px; border-radius:50%; background:var(--ink-3); transition:background .15s, transform .15s; }
.heropoint[aria-current="true"] i { background:var(--accent); transform:scale(1.3); }
/* Beaucoup de contextes (le TAR en a quinze) : deux flèches et la position lue
   en clair remplacent une rangée de pastilles qui déborderait sans rien dire. */
.heropoints.nav { gap:8px; align-items:center; }
.heropoints.nav .heronum {
  font-family:var(--mono); font-size:12.5px; color:var(--ink-2);
  min-width:56px; text-align:center; font-variant-numeric:tabular-nums;
}
.heropoints.nav .iconbtn.sq:disabled { opacity:.3; }

/* Compte à rebours : le chiffre doit se lire depuis le pas de tir, à trois
   pas de l'écran posé sur le banc. */
.timer.count .tv { font-size:76px; line-height:1.05; }

/* Réglage du temps de préparation — visible seulement chrono à l'arrêt. */
/* Cet écran empile plus que les autres (zones, pénalité, préparation, départ) :
   les marges y sont resserrées pour que le bouton de départ reste au-dessus du
   pied collant, sans avoir à défiler pour lancer son exercice. */
.prephead { display:flex; align-items:center; gap:10px; font-size:12.5px; font-weight:700; color:var(--ink-2); margin-bottom:6px; }
.prephead span { flex:1; min-width:0; }
.prepnote { font-size:12px; color:var(--ink-2); line-height:1.45; margin:6px 0 0; }
/* Temps de préparation choisi à la main. Le champ prend la place restante et le
   bouton garde la sienne ; sous ~200 px de large ils passent l'un sous l'autre
   plutôt que de se faire rogner par la carte (même principe que `.segcell`). */
.prepperso { display:flex; flex-wrap:wrap; gap:8px; margin-top:8px; }
.prepperso input { flex:1 1 90px; min-width:90px; }
.prepperso .chip { flex:0 0 auto; }
.scoreview.practical .readout { padding-top:12px; }
.scoreview.practical .inzone { padding-bottom:6px; }

/* Chrono lancé ou compte à rebours en cours : une seule action, et elle prend
   toute la place restante. Gants aux mains, arme fraîchement posée, une
   seconde passée à viser un petit bouton entre dans le temps mesuré. */
.bigact {
  flex:1 1 auto; margin:8px var(--pad) 0; min-height:190px;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:8px;
  border-radius:var(--r-lg); cursor:pointer; font-family:var(--sans); text-align:center; padding:20px;
  border:2px solid var(--line); background:var(--surface-2); color:var(--ink);
}
.bigact b { font-size:30px; font-weight:700; letter-spacing:-.02em; }
.bigact small { font-size:13px; font-weight:500; color:var(--ink-2); line-height:1.45; }
.bigact:active { filter:brightness(.94); }
.bigact.stop { background:var(--miss-soft); color:var(--miss); border-color:color-mix(in srgb, var(--miss) 45%, transparent); }
.bigact.stop small { color:var(--miss); opacity:.85; }

/* LES CINQ DERNIÈRES SECONDES DU COMPTE À REBOURS, en rouge (constat Bilel,
   13/08). Même idiome et mêmes couleurs que `.directeur.rouge` : le cadre
   ENTIER vire, pas seulement le chiffre — au stand le téléphone est posé
   derrière le tireur et ne se voit que du coin de l'œil. Le décompte reste en
   place jusqu'à « ATTENTION », après quoi la règle 8.3.3 interdit d'afficher
   quoi que ce soit qui annoncerait le départ : la couleur, elle, ne le trahit
   pas — elle dit « c'est imminent », pas « c'est dans N secondes ». */
.scoreview.practical.rouge .bigact {
  border-color:var(--miss);
  background:color-mix(in srgb, var(--miss) 14%, var(--surface-2));
}
.scoreview.practical.rouge .readval { color:var(--miss); }

/* plateau : touché / manqué */
.bigpair { display:grid; grid-template-columns:1fr 1fr; gap:12px; padding:6px var(--pad) 0; }
.bigpair button {
  border:2px solid var(--line); border-radius:var(--r-lg); background:var(--surface-2);
  color:var(--ink); font-family:var(--sans); font-weight:700; font-size:18px;
  padding:30px 10px; cursor:pointer; min-height:120px;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:8px;
}
.bigpair button .gi { font-size:30px; }
.bigpair button.hit { border-color:color-mix(in srgb, var(--good) 55%, transparent); color:var(--good); background:var(--good-soft); }
.bigpair button.miss { border-color:color-mix(in srgb, var(--miss) 55%, transparent); color:var(--miss); background:var(--miss-soft); }
.bigpair button:active { filter:brightness(1.12); }

.clayrow { display:grid; grid-template-columns:repeat(13, 1fr); gap:4px; padding:0 var(--pad); margin-top:10px; }
.clayrow i, .clayrow button {
  aspect-ratio:1; border-radius:50%; background:var(--surface-2); border:1px solid var(--line); display:block; padding:0;
}
.clayrow button { cursor:pointer; }
.clayrow i.hit, .clayrow button.hit { background:var(--good); border-color:transparent; }
.clayrow i.miss, .clayrow button.miss { background:transparent; border-color:var(--miss); border-width:2px; }
/* Coup d'essai : même règle que sur la cible — dessiné en creux, visible pour
   se régler, jamais confondu avec un coup compté. */
.clayrow i.essai.hit, .clayrow button.essai.hit { background:transparent; border-color:var(--good); border-width:2px; }

/* groupement mm */
.groupin { padding:10px var(--pad) 0; }
.groupin label { display:block; font-size:12px; letter-spacing:.11em; text-transform:uppercase; color:var(--ink-2); font-weight:600; margin-bottom:8px; }
.groupin .big {
  font-family:var(--mono); font-size:34px; font-weight:600; text-align:center;
  padding:18px 14px; letter-spacing:-.02em;
}
.groupin .hint { font-size:12.5px; color:var(--ink-2); line-height:1.55; margin-top:10px; }

/* ============ HISTORIQUE ============ */
.hitem {
  width:calc(100% - var(--pad) * 2); margin:0 var(--pad) 10px;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md);
  padding:14px 16px; display:flex; align-items:center; gap:13px;
  color:var(--ink); font-family:var(--sans); text-align:start; cursor:pointer;
}
.hitem:active { background:var(--surface-2); }
.hitem .hv { font-family:var(--mono); font-size:22px; font-weight:600; color:var(--accent); min-width:74px; }
.hitem .hv small { font-size:12px; color:var(--ink-2); font-family:var(--sans); display:block; font-weight:500; margin-top:2px; }
.hitem .hm { flex:1; min-width:0; }
.hitem .hm b { font-size:14px; font-weight:600; display:block; }
.hitem .hm span { font-size:12px; color:var(--ink-2); display:block; margin-top:3px; }
.hitem[aria-pressed="true"] { border-color:var(--accent); background:var(--accent-soft); }
/* Séance hors comparaison : estompée pour se voir d'un coup d'œil, mais
   toujours cliquable — l'appui explique pourquoi, plutôt que de ne rien faire. */
.hitem.off { opacity:.4; }

/* Glisser une séance vers la gauche pour la supprimer — le geste du téléphone.
   La suppression apparaît DESSOUS et il faut la toucher : deux gestes voulus,
   parce que sur une liste qui défile un simple glissement part trop facilement
   tout seul, et qu'une séance ne se rattrape pas. */
.swipe { position:relative; margin:0 var(--pad) 10px; border-radius:var(--r-md); overflow:hidden; }
/* Rappels de l'accueil : mêmes marges que la carte qu'ils enveloppent. */
.swipe.top { margin:12px var(--pad) 0; border-radius:14px; }
.swipe > .hitem,
.swipe > .insight,
.swipe > .notifitem {
  /* `position:relative; z-index:1` est ce qui fait que la ligne RECOUVRE
     l'action : sans lui, `.swact` (positionné) se peint par-dessus et
     l'action reste visible en permanence. */
  width:100%; margin:0; position:relative; z-index:1;
  transition:transform .18s ease;
  /* pan-y : le doigt fait toujours défiler la page verticalement ; seul le
     mouvement horizontal nous revient. */
  touch-action:pan-y;
}
.swipe > .hitem.dragging,
.swipe > .insight.dragging,
.swipe > .notifitem.dragging { transition:none; }
.swact { position:absolute; inset:0; display:flex; justify-content:flex-end; }
.swdel {
  width:104px; border:0; cursor:pointer; font-family:var(--sans);
  font-size:12.5px; font-weight:700; color:var(--miss); background:var(--miss-soft);
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:5px;
}
.swdel .i { font-size:19px; }
.swdel:active { filter:brightness(1.15); }
/* Action réversible (écarter un rappel) : neutre, pas rouge — le rouge est
   réservé à ce qui détruit. */
.swdel.soft { background:var(--surface-3); color:var(--ink-2); }
.swdel.soft .i { transform:rotate(180deg); }

.tag {
  display:inline-block; font-size:10.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
  padding:3px 8px; border-radius:6px; background:var(--surface-3); color:var(--ink-2); margin-top:6px;
}

/* ============ DÉTAIL DE SÉANCE ============ */
.serieline {
  display:flex; align-items:center; gap:12px; padding:12px 16px; border-bottom:1px solid var(--line);
}
.serieline:last-child { border-bottom:0; }
.serieline .sn { font-size:12px; color:var(--ink-2); font-weight:600; width:56px; flex:0 0 auto; }
.serieline .sv { font-family:var(--mono); font-size:16px; font-weight:600; width:82px; flex:0 0 auto; white-space:nowrap; }
.serieline .sd { flex:1; font-family:var(--mono); font-size:11.5px; color:var(--ink-3); text-align:end; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

.kv { display:flex; justify-content:space-between; gap:12px; padding:12px 16px; border-bottom:1px solid var(--line); }
.kv:last-child { border-bottom:0; }
.kv .k { font-size:13.5px; color:var(--ink-2); }
.kv .v { font-family:var(--mono); font-size:14px; font-weight:600; text-align:end; }

/* HISTOGRAMME DE RÉPARTITION PAR ZONE (05/09/2026).
   La barre est en `flex:1 1 0` + `min-width:0` : c'est ce qui la fait CÉDER
   quand la carte se resserre, au lieu de pousser le compte hors du cadre —
   le piège des grilles à colonne fixe, payé plusieurs fois. Les deux
   colonnes de chiffres sont en `em` pour suivre la police du lecteur. */
.zrow { display:flex; align-items:center; gap:10px; padding:5px 16px; }
.zrow:first-of-type { padding-top:12px; }
.zrow:last-child { padding-bottom:12px; }
.zval { font-family:var(--mono); font-size:14px; font-weight:600; min-width:2.2em; text-align:end; }
.zbar { flex:1 1 0; min-width:0; height:10px; border-radius:5px; background:var(--line); overflow:hidden; }
.zbar i { display:block; height:100%; background:var(--accent); border-radius:5px; }
.zn { font-family:var(--mono); font-size:13.5px; color:var(--ink-2); min-width:2.4em; text-align:end; }

/* ============ ACCUEIL — carte espace ============ */
.spacecard {
  width:calc(100% - var(--pad) * 2); margin:12px var(--pad) 0;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md);
  padding:15px 16px; display:flex; align-items:center; gap:13px; cursor:pointer;
  color:var(--ink); font-family:var(--sans); text-align:start;
}
.spacecard .si {
  width:42px; height:42px; border-radius:12px; background:var(--accent-soft);
  color:var(--accent); display:flex; align-items:center; justify-content:center; font-size:20px; flex:0 0 auto;
}
.spacecard .st { flex:1; min-width:0; }
.spacecard .st b { font-size:14.5px; font-weight:600; display:block; }
.spacecard .st span { font-size:12px; color:var(--ink-2); display:block; margin-top:3px; line-height:1.4; }

/* chevrons de fin de ligne : taille explicite, sinon ils héritent d'un 1em minuscule */
.spacecard > .i:last-child,
.hitem > .i:last-child,
.insight > .i:last-child { font-size:18px; color:var(--ink-3); }

/* ============ VERROUILLAGE ============ */
.lockscreen {
  min-height:100%; display:flex; flex-direction:column; align-items:center;
  justify-content:center; padding:24px var(--pad) 40px; text-align:center;
}
.lockhead .locktgt { font-size:44px; color:var(--accent); opacity:.9; }
.lockhead h1 { font-size:21px; font-weight:700; letter-spacing:-.02em; margin:14px 0 0; }
.lockhead p { font-size:13px; color:var(--ink-2); line-height:1.55; margin:8px 0 0; max-width:280px; }
.lockdots { display:flex; gap:12px; margin:26px 0 4px; min-height:14px; }
.lockdots i { width:13px; height:13px; border-radius:50%; border:1.5px solid var(--ink-3); display:block; }
.lockdots i.on { background:var(--accent); border-color:var(--accent); }
.lockmsg { font-size:12.5px; color:var(--miss); min-height:18px; margin:6px 0 10px; }
/* `min(100%, …)` et non `76vw` : le pavé du code vit DANS la carte de l'écran
   de verrouillage, pas dans la fenêtre. Même piège que la cible tactile (10/08)
   et que la photo du carton (13/08) — c'est le troisième endroit, et le seul
   que le garde-fou ne pouvait pas voir tant qu'il ne lisait que le JS. */
.lockpad { grid-template-columns:repeat(3, 1fr); width:min(100%, 280px); padding:0; }
.lockpad button { font-size:22px; min-height:62px; border-radius:16px; }
.lockpad button.go { background:var(--accent); color:var(--on-accent); border-color:transparent; }
.lockpad button[disabled] { opacity:.35; }

/* ============ ONBOARDING ============ */
.ob { padding:0 var(--pad); }
/* Retour d'étape : présent dès qu'il y a une étape précédente. */
.ob .obback { padding:18px 0 0; }
.ob .obback + .obhead { padding-top:14px; }
.ob .obhead { padding:38px 0 10px; }
/* Étape 1 resserrée (13/08) : le logo fait déjà l'espace du haut, et tout doit
   tenir sans défiler — y compris sur un écran de 667 px, où les pastilles de
   pays et leur note passaient sous le pied collant. Les étapes 2 et 3 gardent
   leurs marges : elles n'ont pas de logo et leur liste défile de toute façon. */
.ob.court .obhead { padding:2px 0 0; }
.ob.court .obhead h1 { font-size:20px; margin-top:6px; }
.ob.court .obhead p { margin-top:6px; font-size:13px; line-height:1.5; }
.ob.court .obfoot { padding:4px 0 8px; }
/* RESSERRÉ UNE SECONDE FOIS LE 17/09/2026, quand la LANGUE a pris sur cette page la place du
   pays (demande de Bilel). Mesuré à 320 x 667 — le pire cas : 894 px de contenu pour 667
   visibles. Les commandes sont incompressibles (treize pastilles à 44 px sur cinq rangs, le
   champ, les deux boutons : 400 px à elles seules, et 44 px est le plancher de la charte). Ce
   qui se gagne ici ne coûte AUCUN mot : les gouttières entre pastilles, la taille des notes et
   les marges du bloc. */
.ob.court .chips { gap:6px; }
.ob.court .note { margin-top:6px; font-size:12px; line-height:1.5; }
.ob.court .sect { margin-bottom:6px; }
.ob .obhead .k { font-size:11px; letter-spacing:.22em; text-transform:uppercase; color:var(--accent); font-weight:600; }
.ob .obhead h1 { font-size:25px; line-height:1.25; font-weight:700; letter-spacing:-.02em; margin:10px 0 0; }
.ob .obhead p { font-size:14px; color:var(--ink-2); line-height:1.6; margin:10px 0 0; }
.ob .oblist { margin-top:22px; display:flex; flex-direction:column; gap:9px; }
.ob .obopt {
  display:flex; align-items:center; gap:13px; padding:16px;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md);
  cursor:pointer; color:var(--ink); font-family:var(--sans); text-align:start; min-height:var(--tap);
}
.ob .obopt[aria-pressed="true"] { border-color:var(--accent); background:var(--accent-soft); }
.ob .obopt .ot { flex:1; }
.ob .obopt .ot b { font-size:15px; font-weight:600; display:block; }
.ob .obopt .ot span { font-size:12.5px; color:var(--ink-2); display:block; margin-top:3px; line-height:1.45; }
/* Pied COLLANT de l'onboarding — même idiome que le pied de séance.
   Mesuré à l'étape 2 : 1 078 px de contenu pour 812 px d'écran, « Continuer »
   à 992 px. On voyait huit pratiques et rien qui dise comment avancer. Le
   défaut avait été corrigé sur l'écran de séance le 10/08 ; il survivait ici. */
.ob .obfoot {
  /* `bottom` suit ce que les bandes flottantes occupent (voir `reserverBasDEcran`
     dans app.js) : un pied COLLANT ne se déplace pas avec une marge sur le
     conteneur qui défile — il faut lui dire de remonter. Sans bande, `--maj-barre`
     vaut 0 et ce pied est exactement où il était. */
  position:sticky; bottom:var(--maj-barre, 0px); z-index:2;
  padding:14px 0 18px;
  background:linear-gradient(to bottom, transparent, var(--bg) 22%);
}

/* ============ VOLET DE NOTIFICATIONS ============ */
/* Le geste d'Android et d'iOS (demande Bilel, 10/08) : on tire du haut vers le
   bas pour voir ce qu'il y a à régler, on glisse une notification vers la
   DROITE pour l'écarter. Le glissement vers la droite est propre à ce volet —
   ailleurs dans l'app, l'action se découvre à gauche. */
.notifwrap { margin:12px var(--pad) 0; }
/* La barre reste visible volet fermé : elle porte le compte et la couleur.
   Un volet qui cacherait TOUT laisserait croire que le carnet est en ordre
   alors qu'une licence est périmée — c'est ce que la charte interdit. */
.notifbar {
  width:100%; display:flex; align-items:center; gap:11px;
  background:var(--surface); border:1px solid var(--line);
  border-inline-start:3px solid var(--warn, var(--miss));
  border-radius:14px; padding:12px 14px; min-height:52px;
  color:var(--ink); font-family:var(--sans); font-size:13.5px; font-weight:600;
  cursor:pointer; text-align:start; touch-action:pan-y;
}
.notifbar.due { border-inline-start-color:var(--miss); }
/* La pastille de compte : le repère qu'on lit sans lire. */
.notifdot {
  flex:0 0 auto; min-width:24px; height:24px; padding:0 7px; border-radius:12px;
  background:var(--warn, var(--miss)); color:var(--on-accent, #fff);
  font-family:var(--mono); font-size:13px; font-weight:700;
  display:flex; align-items:center; justify-content:center;
}
.notifbar.due .notifdot { background:var(--miss); }
.notiftxt { flex:1; min-width:0; }
.notifchev { flex:0 0 auto; color:var(--ink-3); display:flex; transition:transform .16s; }
.notifwrap.open .notifchev { transform:rotate(90deg); }

/* Le volet : fermé il n'occupe rien, ouvert il s'anime jusqu'à sa hauteur. */
.notifvolet { max-height:0; overflow:hidden; transition:max-height .2s ease; }
.notiflist { padding-top:8px; }
/* Les notifications du volet n'ont pas les marges de l'accueil : elles sont
   déjà dans un bloc qui les porte. */
.notiflist .swipe { margin:0 0 8px; }
.notiflist .swipe:last-child { margin-bottom:0; }
.notifitem {
  width:100%; display:flex; align-items:center; gap:11px;
  background:var(--surface-2); border:1px solid var(--line);
  border-inline-start:3px solid var(--warn, var(--miss));
  border-radius:14px; padding:13px 14px;
  color:var(--ink); font-family:var(--sans); text-align:start; cursor:pointer;
}
.notifitem.due { border-inline-start-color:var(--miss); }
.notifitem .txt { flex:1; min-width:0; }
.notifitem .k { display:block; font-size:11px; letter-spacing:.13em; text-transform:uppercase; color:var(--ink-2); font-weight:600; }
.notifitem .b { display:block; font-size:13.5px; margin-top:5px; line-height:1.5; }
.notifitem .b em { color:var(--accent); font-style:normal; font-weight:700; }
.notifitem .i { flex:0 0 auto; color:var(--ink-3); }
/* Glissement vers la DROITE : l'action se découvre à gauche de la ligne. */
.swipe.rtl .swact { justify-content:flex-start; }

/* FOND BLANC, TOUJOURS (constat Bilel, 11/08/2026 : « le directeur de tir
   doit être en fond blanc, ce n'est pas assez clair »). Une feuille de
   commandements d'arbitre est en noir sur blanc, quel que soit le thème
   choisi pour le reste du carnet — sombre, clair, ou teinté. Les valeurs
   ci-dessous sont recopiées TELLES QUELLES du thème clair de l'app
   (tokens.css, `:root[data-theme="light"]`), jamais recalculées depuis la
   teinte personnalisée : ce panneau ne doit pas se fondre dans le décor, et
   il doit rester identique d'un carnet à l'autre.
   Les propriétés DÉRIVÉES (`--accent-soft`, `--accent-line`, `--miss-soft`)
   sont réécrites ICI aussi : déclarées une fois pour toutes sur `:root` dans
   tokens.css, elles ne se recalculent PAS toutes seules depuis un `--accent`
   redéfini plus bas dans l'arbre — sans cette ligne, une pastille de
   chargement « active » aurait affiché la teinte du thème sombre par-dessus
   ce fond blanc. */
.directeur {
  --bg:#E9ECF1; --surface:#FFFFFF; --surface-2:#F4F6F9; --surface-3:#EDF0F4;
  --ink:#10151D; --ink-2:#5C6673; --ink-3:#98A1AE; --line:#E1E5EC;
  --accent:#111417; --on-accent:#FFFFFF;
  --accent-soft:color-mix(in srgb, var(--accent) 15%, transparent);
  --accent-line:color-mix(in srgb, var(--accent) 40%, transparent);
  --good:#1E9A70; --miss:#C9504D;
  --miss-soft:color-mix(in srgb, var(--miss) 15%, transparent);
  --shadow:0 2px 5px rgba(15,21,29,.15), 0 14px 38px rgba(15,21,29,.28);
  background:var(--surface); border-color:var(--line); color:var(--ink);
  /* Le thème sombre n'ajoute jamais d'ombre à une carte : sans elle, un fond
     blanc flotterait à plat sur un fond sombre. Un peu plus appuyée que la
     valeur du thème clair (pensée pour un fond clair autour d'elle). */
  box-shadow:var(--shadow);
}

/* Le chronomètre du directeur de tir (TAR). Gros et tabulaire : il se lit d'un
   coup d'œil depuis le poste, et les chiffres ne dansent pas d'une seconde à
   l'autre. Même parti que le grand chiffre de la saisie. */
.directeur .dirtemps {
  font-family:var(--mono); font-size:52px; font-weight:800; line-height:1.1;
  text-align:center; letter-spacing:-1px; font-variant-numeric:tabular-nums;
  margin:6px 0 12px;
}

/* Le commandement que la voix vient de dire (11/08/2026, constat Bilel : « à
   aucun moment la voix apparaît avec le chronomètre »). Au-dessus du temps,
   dans la couleur d'accent : on lit le mot, puis le temps qu'il donne — comme
   un shot-timer qui affiche « READY » avant de compter. */
.directeur .dirmot {
  text-align:center; font-weight:800; font-size:19px; letter-spacing:.02em;
  color:var(--accent); text-transform:uppercase; margin:2px 0 0;
}
.directeur.rouge .dirmot { color:var(--miss); }

/* Les cinq dernières secondes de la série (demande Bilel, 11/08/2026).
   Aide d'ENTRAÎNEMENT, pas une consigne de règlement : aucun juge n'annonce
   la fin d'une série. La carte entière vire, pas seulement le chiffre — au
   stand, le téléphone est posé derrière le tireur et se voit du coin de
   l'œil : c'est la tache de couleur qui porte, pas la lecture. */
.directeur.rouge {
  border-color:var(--miss);
  background:color-mix(in srgb, var(--miss) 14%, var(--surface));
}
.directeur.rouge .dirtemps { color:var(--miss); }

/* Arrêt de sécurité (11/08/2026) : seul état qui n'enchaîne plus tout seul,
   potentiellement pour longtemps — le liseré doit donc rester visible sans
   fatiguer l'œil, à la différence du rouge plein des 5 dernières secondes. */
.directeur.attente-reprise {
  border-color:var(--miss);
  border-width:2px;
}

/* « ATTENTION » du chrono de tir pratique : le mot remplace le décompte, il
   doit donc tenir dans la même case sans la faire grandir (règle IPSC 8.3.3 —
   le signal tombe 1 à 4 s plus tard, sans prévenir). */
.timer .tv.standby { font-size:34px; letter-spacing:.06em; }

/* Les coups lus sur une photo de moniteur — UNE PASTILLE PAR VALEUR (demande
   Bilel, 03/09/2026). « 8,9 9,4 9,1 9,1 9,4 » sur une seule ligne ne se relit
   pas, et c'est la relecture du tireur qui fait le score. Le rang s'affiche en
   petit devant la valeur quand il est sûr (lecture recoupée) ; un trou est une
   pastille en pointillé, jamais un zéro. */
.coupslus { display:flex; flex-wrap:wrap; gap:8px 10px; margin:10px 0 0; }
.coupslus .lu {
  display:inline-flex; align-items:baseline; justify-content:center; gap:6px;
  min-height:44px; min-width:44px;
  padding:7px 12px; border:1px solid var(--line); border-radius:12px;
  font-size:17px; font-variant-numeric:tabular-nums;
  /* c'est un BOUTON depuis le 03/09 (la valeur se corrige en la touchant) :
     on retire la mise en forme par défaut, on garde la cible à 44 px */
  background:transparent; color:inherit; font-family:inherit; cursor:pointer;
}
.coupslus .lu[aria-pressed="true"] { border-color:var(--accent); box-shadow:0 0 0 2px var(--accent-line); }
.coupslus .lu small { font-size:11px; color:var(--ink-2); }
.coupslus .lu b { font-weight:600; }
.coupslus .lu.trou { border-style:dashed; color:var(--ink-2); }

/* LA LIGNE MÉTÉO de l'écran de saisie (22/09/2026).
   Une rangée sobre sous le nom de l'épreuve : elle doit se voir sans jamais
   voler la place de la saisie. Cible tactile pleine largeur, donc trouvable
   avec des gants sans viser. */
.meteoline { padding:10px var(--pad) 0; }
.meteoline .row {
  width:100%; display:flex; align-items:center; gap:10px; text-align:left;
  min-height:var(--tap); padding:10px 12px; border-radius:var(--r);
  border:1px solid var(--line); background:var(--surface-2); color:var(--ink);
  font-family:var(--sans); cursor:pointer;
}
.meteoline .row .gi { font-size:18px; color:var(--ink-2); display:flex; }
.meteoline .row b { font-size:13.5px; font-weight:700; }
.meteoline .row small { font-size:11.5px; color:var(--ink-2); line-height:1.4; }
.meteoline .row .grow { flex:1; min-width:0; }

/* LA LIGNE OÙ LE TIREUR AJOUTE UNE PIÈCE À SON INVENTAIRE (22/09/2026).
 *
 * `.btn` est en `width:100%` : dans un conteneur `flex`, c'est sa BASE, donc il
 * réclamait toute la carte et le champ tombait à **29 px de large pour un bouton
 * de 395** — mesuré dans l'app avant d'écrire une ligne. C'est le défaut du
 * 13/09 sur la barre du bas dans l'autre sens : là, `flex:1` sans `min-width:0` ;
 * ici, une largeur de 100 % qui sert de base. Le bouton prend désormais la
 * largeur de son mot, et le champ tout le reste.
 *
 * ET LA LIGNE S'ENROULE, comme `.segcell` : « Ajouter » ne fait pas la même
 * longueur dans les douze langues. Plutôt que d'écraser le champ sous un long
 * libellé, le bouton passe DESSOUS quand la place manque — un champ de saisie
 * rogné est un champ qu'on ne vise pas. */
.row.ajoutpiece { flex-wrap:wrap; row-gap:10px; }
.row.ajoutpiece > .grow { flex:1 1 150px; min-width:0; }
.row.ajoutpiece > .btn { width:auto; flex:0 0 auto; padding-left:18px; padding-right:18px; }

/* LE VOLET DES SOURCES DE CATÉGORIES (22/09/2026, capture de Bilel).
 *
 * Un bouton discret : ce n'est pas une action du tireur, c'est une référence
 * qu'il consulte s'il veut. Mais il reste un vrai bouton, à la cible de la
 * charte — `--tap` et non 44, comme la barre du bas depuis le 13/09.
 *
 * Le chevron pivote comme celui des cartes repliables ; la règle existante
 * (`.row[aria-expanded=true] .chev`) ne s'applique pas ici, ce bouton n'étant
 * pas une `.row` — lui donner cette classe lui aurait posé le padding et le
 * trait de séparation d'une ligne de carte. */
.srcvolet { display:block; }
.srcbtn {
  display:flex; align-items:center; gap:6px; width:100%;
  background:none; border:0; padding:8px 0; margin-top:4px;
  min-height:var(--tap); cursor:pointer;
  font-family:var(--sans); font-size:12.5px; color:var(--ink-2); text-align:start;
}
.srcbtn .chev { color:var(--ink-3); font-size:15px; transition:transform .15s; }
.srcbtn[aria-expanded=true] .chev { transform:rotate(90deg); }
