/* ==========================================================================
   funact — feuille de style partagee
   Reprend le systeme de design de results-template.html et l'applique a tout
   le site (donc a l'app mobile, qui n'est qu'un wrapper d'URL sur celui-ci).

   Deux regles a ne pas casser :
   1. Toute couleur passe par un token defini sur :root. Une couleur declaree
      uniquement dans un bloc @media ou [data-theme] ne s'applique pas dans
      l'etat "systeme", ou aucun attribut n'est pose sur <html> — la page
      rendrait alors le texte d'un theme sur le fond de l'autre.
   2. Les anciens noms de tokens (--text, --text-dim, --border, --error, --ok,
      --danger) restent definis comme alias des nouveaux. Les feuilles de style
      propres a chaque page continuent donc de fonctionner sans etre reecrites.
   ========================================================================== */

/* Les @font-face sont dans fonts.css, a charger a cote de cette feuille :
   results.html en a besoin sans vouloir des tokens d'ici. */

/* --- Tokens : clair par defaut -------------------------------------------- */
:root {
  color-scheme: light;
  --bg:#f5f5f2;
  --surface:#ffffff;
  --surface-2:#faf9f8;
  --surface-3:#efeeec;
  --ink:#1c1e26;
  --ink-2:#454a59;
  --ink-dim:#6b7080;
  --line:#e3e2e9;
  --line-strong:#cfced9;
  --accent:#6d5bff;
  --accent-ink:#5344d6;
  --accent-soft:#efecff;
  --live:#0e9f6e;
  /* Vert LISIBLE sur --live-soft, la ou --live ne l'est pas : #0e9f6e sur
     #e3f5ee ne donne que 2,98:1, sous le 4,5:1 qu'exige un texte de 11 px en
     capitales. Meme role que --accent-ink par rapport a --accent, et meme
     sens : plus sombre en clair, plus clair en sombre. --live reste la couleur
     des aplats et des pastilles pleines, ou le texte est blanc. */
  --live-ink:#08744f;
  --live-soft:#e3f5ee;
  --warn:#a86a12;
  --warn-soft:#f9f0dd;
  --stop:#c23b3b;
  --stop-soft:#fbe9e9;
  --gold:#a9791f;
  --silver:#6f758a;
  --bronze:#96562a;
  --shadow-sm:0 1px 2px rgba(20,20,35,.06);
  --shadow-lg:0 1px 2px rgba(20,20,35,.05), 0 12px 32px rgba(20,20,35,.10);
  --radius:12px;
  --radius-sm:8px;
  --on-accent:#ffffff;
  --font-display:'Barlow Condensed','Arial Narrow',Haettenschweiler,'Segoe UI',sans-serif;
  --font-ui:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;

  /* Alias des anciens noms : le CSS deja ecrit dans chaque page continue de
     fonctionner tel quel, il pointe simplement sur la nouvelle palette. */
  --text:var(--ink);
  --text-dim:var(--ink-dim);
  --border:var(--line);
  --error:var(--stop);
  --ok:var(--live);
  --danger:var(--stop);
}

/* --- Tokens : sombre ------------------------------------------------------
   Trois etats, pas deux. Un choix explicite pose data-theme sur <html> ; le
   reglage "systeme" (par defaut) n'en pose aucun, et seul prefers-color-scheme
   distingue alors le clair du sombre. Le bloc @media est donc garde par
   :not([data-theme="light"]) pour qu'un choix clair explicite batte un OS
   sombre, et le bloc [data-theme="dark"] rejoue les memes tokens pour que le
   choix sombre batte un OS clair. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg:#121218;
    --surface:#1a1a21;
    --surface-2:#20202a;
    --surface-3:#282833;
    --ink:#eeedf4;
    --ink-2:#c3c2d0;
    --ink-dim:#918fa2;
    --line:#2c2c37;
    --line-strong:#3f3f4d;
    --accent:#9184ff;
    --accent-ink:#b3a9ff;
    --accent-soft:#262046;
    --live:#3cc492;
    --live-ink:#5fd6a9;
    --live-soft:#12312a;
    --warn:#d9a03f;
    --warn-soft:#332711;
    --stop:#e57373;
    --stop-soft:#3a1c1c;
    --gold:#d3a445;
    --silver:#a3a8ba;
    --bronze:#c07b45;
    --shadow-sm:0 1px 2px rgba(0,0,0,.45);
    --shadow-lg:0 1px 2px rgba(0,0,0,.4), 0 12px 36px rgba(0,0,0,.5);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg:#121218;
  --surface:#1a1a21;
  --surface-2:#20202a;
  --surface-3:#282833;
  --ink:#eeedf4;
  --ink-2:#c3c2d0;
  --ink-dim:#918fa2;
  --line:#2c2c37;
  --line-strong:#3f3f4d;
  --accent:#9184ff;
  --accent-ink:#b3a9ff;
  --accent-soft:#262046;
  --live:#3cc492;
  --live-ink:#5fd6a9;
  --live-soft:#12312a;
  --warn:#d9a03f;
  --warn-soft:#332711;
  --stop:#e57373;
  --stop-soft:#3a1c1c;
  --gold:#d3a445;
  --silver:#a3a8ba;
  --bronze:#c07b45;
  --shadow-sm:0 1px 2px rgba(0,0,0,.45);
  --shadow-lg:0 1px 2px rgba(0,0,0,.4), 0 12px 36px rgba(0,0,0,.5);
}

/* --- Socle commun ---------------------------------------------------------
   Ces regles remplacent celles qui etaient copiees a l'identique dans chaque
   page. Une page ne garde dans son <style> que ce qui lui est propre.
   ------------------------------------------------------------------------- */
* { box-sizing:border-box; }
html { -webkit-text-size-adjust:100%; }

body {
  margin:0;
  min-height:100vh;
  padding:16px;
  display:flex;
  align-items:center;
  justify-content:center;
  background:var(--bg);
  color:var(--ink);
  font-family:var(--font-ui);
  font-size:15px;
  line-height:1.45;
  -webkit-font-smoothing:antialiased;
}

/* Le condense est le vernaculaire du dossard et de la liste de depart : il
   porte les titres et, dans les pages qui en ont, les grands chiffres. */
h1, h2, h3 {
  font-family:var(--font-display);
  font-weight:600;
  letter-spacing:.005em;
  text-wrap:balance;
  margin:0;
}
h1 { font-size:1.45rem; margin:0 0 4px; }
h2 { font-size:1.15rem; }
h3 { font-size:1rem; }
p.lead { color:var(--ink-dim); font-size:.9rem; margin:0 0 24px; }

.card {
  background:var(--surface);
  border:1px solid var(--line);
  border-radius:var(--radius);
  padding:32px;
  max-width:480px;
  width:100%;
  margin:16px;
  box-shadow:var(--shadow-lg);
}

/* --- Mise en page large -----------------------------------------------------
   480px est la bonne largeur pour un formulaire de connexion, pas pour un
   espace de travail. Les pages de "Mon espace" ne sont plus une carte du tout :
   elles vivent dans la coquille de shell.js (rail + contenu), et ce sont les
   trois classes ci-dessous qui organisent ce contenu.

   Elles restent ici, et non dans shell.css, parce qu'elles ne dependent pas de
   la coquille : .grid-cards sert aussi a register.html, et .field-row a
   n'importe quel formulaire. shell.css ne decrit que le rail et l'ossature. */

/* Grille de cartes, exactement le motif de .cards sur index.html : les
   elements se rangent en colonnes tant qu'il y a la place, et retombent en
   liste sur mobile sans media query. align-items:start pour qu'une carte plus
   haute que ses voisines ne les etire pas a sa hauteur.

   Le min() n'est pas decoratif : auto-fill retombe bien a une colonne, mais
   minmax(300px, 1fr) garde un PLANCHER de 300px et la colonne deborde des que
   le cadre est plus etroit — mesure a 300px de piste dans 290px de cadre sur
   un ecran de 360px, vue Organisateur. Meme piege que .rows sur
   race-participants.html et race-staff.html. */
.grid-cards {
  display:grid; gap:12px; align-items:start;
  grid-template-columns:repeat(auto-fill, minmax(min(300px, 100%), 1fr));
}

/* Deux champs courts sur une ligne — prenom et nom, date et sexe. C'est le
   remede direct au cadre de saisie qui traverse la page pour une seule
   information. En dessous de 700px, retour a une colonne. */
.field-row { display:grid; gap:0 16px; grid-template-columns:1fr; }
@media (min-width:700px) { .field-row { grid-template-columns:1fr 1fr; } }

/* Un champ SEUL sur sa ligne (une adresse e-mail, un nom d'organisation) ne
   doit pas s'etirer sur toute la coquille : au-dela d'une quarantaine de
   caracteres, la largeur du cadre ne dit plus rien de ce qu'on attend dedans,
   et le curseur se perd loin de l'intitule. Meme borne pour les boutons pleine
   largeur, qui deviendraient sinon des bandes de commande sans rapport avec
   leur importance.

   Portee par .shell-body : sur une carte de 480px (login, organisation) un
   champ pleine largeur est deja a la bonne taille, il n'y a rien a borner. */
.shell-body input:not([type=checkbox]):not([type=radio]),
.shell-body select,
.shell-body textarea,
.shell-body button.primary,
.shell-body .field-cap { max-width:440px; }
/* Sauf dans une grille de champs ou une carte, ou la colonne fait deja la
   largeur voulue. */
.shell-body .field-row input, .shell-body .field-row select,
.shell-body .grid-cards input, .shell-body .grid-cards select { max-width:none; }

/* Longueur de ligne. Elargir la page ne doit pas elargir le texte : au-dela
   d'environ 70 caracteres, l'oeil rate le debut de la ligne suivante. C'est la
   contrepartie de la coquille — la largeur gagnee sert a poser des blocs cote
   a cote, pas a etirer des phrases. Les cartes d'une grille sont exclues :
   leur colonne fait deja moins que ca. */
.shell-body p, .shell-body .lead { max-width:70ch; }
.shell-body .grid-cards p, .shell-body .panel p { max-width:none; }

label { display:block; font-size:.85rem; font-weight:600; margin-bottom:6px; }

input, select, textarea {
  width:100%;
  padding:12px;
  border-radius:var(--radius-sm);
  border:1px solid var(--line);
  font-family:inherit;
  font-size:1rem;
  margin-bottom:12px;
  background:var(--surface);
  color:var(--ink);
}
input:focus, select:focus, textarea:focus { outline:2px solid var(--accent); border-color:var(--accent); }
/* Pose par forms.js sur un champ refuse a la validation. */
.field-invalid { border-color:var(--stop) !important; }

button { font-family:inherit; }
button.primary {
  width:100%; padding:12px; border-radius:var(--radius-sm); border:none;
  background:var(--accent); color:var(--on-accent);
  font-size:1rem; font-weight:600; cursor:pointer;
}
button.primary:hover:not(:disabled) { background:var(--accent-ink); }
button.primary:disabled { opacity:.6; cursor:default; }
button.secondary {
  width:100%; padding:12px; border-radius:var(--radius-sm); border:1px solid var(--line);
  background:transparent; color:var(--ink);
  font-size:.95rem; font-weight:600; cursor:pointer; margin-bottom:12px;
}
button.secondary:hover:not(:disabled) { border-color:var(--line-strong); }
button.secondary:disabled { opacity:.6; cursor:default; }
button.link {
  border:none; background:none; padding:0; color:var(--accent-ink);
  font-size:.85rem; font-weight:600; cursor:pointer; white-space:nowrap;
}
button.link:hover { text-decoration:underline; }

.error { color:var(--stop); font-size:.85rem; margin:-4px 0 12px; }
.notice { color:var(--live); font-size:.85rem; margin:-4px 0 12px; }

/* --- Bulle de confirmation -------------------------------------------------
   Meme bulle que le bouton Partager de la page resultats. Elle sert aux
   confirmations qui n'apprennent rien de plus que « c'est fait » : les
   redire dans la page obligerait a la reconstruire, donc a perdre la place
   du lecteur et l'etat du formulaire pour un texte lu en une seconde.
   Affichee via toast.js (FunactToast.show).

   EN HAUT, et elle descend en apparaissant : c'est de la-haut que vient le
   reste de ce qui s'adresse a l'usager (le titre de la page, les erreurs sous
   les champs), et sur telephone le bas de l'ecran est occupe par le clavier
   des qu'un formulaire est en cours — une confirmation posee dessous s'y
   cacherait a l'instant precis ou on la lit.

   --stack-h : les pages a en-tete collante (resultats, accueil) y publient sa
   hauteur, et la bulle se pose juste dessous plutot que sur la marque. Les
   pages en carte ne la definissent pas et retombent sur la seule marge. */
.toast {
  position:fixed; left:50%; top:calc(var(--stack-h, 0px) + 18px); z-index:60;
  transform:translate(-50%,-14px); opacity:0; pointer-events:none;
  background:var(--ink); color:var(--bg); font-size:13.5px; font-weight:500;
  padding:10px 16px; border-radius:999px; box-shadow:var(--shadow-lg);
  transition:opacity .18s ease, transform .18s ease;
}
.toast.show { opacity:1; transform:translate(-50%,0); }

/* --- Boite de confirmation -------------------------------------------------
   Affichee via confirm.js (FunactConfirm.ask), pour les gestes qu'on ne peut
   pas defaire.

   Un <dialog> natif plutot qu'un <div> pose par-dessus : le navigateur donne
   alors gratuitement, et correctement, tout ce qu'une boite modale doit avoir
   et qu'on redige mal a la main — le piege a focus (Tab ne s'echappe pas dans
   la page derriere), la fermeture a Echap, l'inertie du reste du document pour
   les lecteurs d'ecran, et la « couche superieure » qui la place au-dessus des
   cartes MapLibre sans avoir a surencherir sur les z-index.

   Elle ne ressemble pas a la bulle ci-dessus, et c'est voulu : la bulle
   constate ce qui vient d'arriver, celle-ci demande la permission de le faire.
   L'une s'efface toute seule, l'autre attend une reponse.
   -------------------------------------------------------------------------- */
dialog.dialog {
  padding:0;
  border:1px solid var(--line);
  border-radius:var(--radius);
  background:var(--surface);
  color:var(--ink);
  width:min(400px, calc(100vw - 32px));
  box-shadow:var(--shadow-lg);
}
/* Le fond assombri est le seul element de l'interface qui doit rester lisible
   par-dessus une carte plein ecran : d'ou un noir propre plutot qu'un token de
   la palette, qui s'eclaircirait en theme sombre precisement la ou il doit
   assombrir. */
dialog.dialog::backdrop { background:rgba(10,10,18,.55); }

.dialog-body { padding:24px 24px 4px; }
.dialog-body h2 { font-size:1.2rem; margin:0 0 8px; }
.dialog-body p { color:var(--ink-dim); font-size:.9rem; line-height:1.5; margin:0; }

.dialog-actions { display:flex; gap:10px; padding:20px 24px 24px; }
.dialog-actions button {
  flex:1; padding:11px; border-radius:var(--radius-sm);
  font-family:inherit; font-size:.92rem; font-weight:600; cursor:pointer;
}
.dialog-cancel { border:1px solid var(--line); background:transparent; color:var(--ink); }
.dialog-cancel:hover { border-color:var(--line-strong); }
/* Rouge PLEIN ici, alors que le bouton qui a ouvert la boite n'etait qu'un
   contour : a ce stade la question n'est plus « ou est l'action dangereuse »
   mais « est-ce bien celle-la que je confirme ». */
/* L'encre est --bg, pas du blanc : en theme sombre --stop est un rouge CLAIR
   (#e57373), sur lequel du blanc ne donne que 2,99:1 — sous le 4,5:1 exige, et
   precisement sur le mot qu'il ne faut pas lire de travers. --bg suit le theme
   dans le bon sens (4,83:1 en clair, 6,25:1 en sombre). Meme idiome que la
   bulle .toast, qui inverse deja ses deux tokens de la meme facon. */
.dialog-confirm { border:1px solid var(--stop); background:var(--stop); color:var(--bg); }
.dialog-confirm:hover { filter:brightness(1.08); }

/* L'ouverture est animee, la fermeture non : une boite qui s'attarde en
   disparaissant retarde ce qu'on vient de declencher.

   Le fond sombre, lui, arrivait d'un bloc pendant que la boite montait : deux
   temps pour un seul geste, et l'aplat instantane est la signature d'une
   fenetre de navigateur. Il monte maintenant avec elle. La courbe et les
   durees viennent des jetons de motion.css, comme les tiroirs — le repli
   couvre les pages qui ne chargent pas cette feuille. */
dialog.dialog[open] {
  animation:dialog-in var(--dur-enter, 220ms) var(--ease-emphasized, cubic-bezier(.32,.72,0,1));
}
@keyframes dialog-in {
  from { opacity:0; transform:translateY(6px) scale(.98); }
}
dialog.dialog[open]::backdrop { animation:dialog-backdrop-in var(--dur-enter, 220ms) ease-out; }
@keyframes dialog-backdrop-in {
  from { opacity:0; }
}

@media (prefers-reduced-motion: reduce) {
  dialog.dialog[open], dialog.dialog[open]::backdrop { animation:none; }
}

/* .back-link a disparu avec les liens "Retour" poses dans le corps des pages :
   le retour vit desormais dans .app-bar (voir plus bas et nav.js). Seule la
   deconnexion de settings.html garde ce discret niveau de gris. */
.logout-link {
  display:inline-block; margin-top:16px; color:var(--ink-dim);
  text-decoration:none; font-size:.85rem; cursor:pointer;
}
.logout-link:hover { color:var(--ink); }

/* La barre de navigation et le tiroir vivent dans nav.css : results.html en a
   besoin sans pouvoir charger cette feuille-ci. */

/* --- Plein ecran sur mobile ------------------------------------------------
   Sous cette largeur, la carte cesse d'etre une carte : elle EST l'ecran. Plus
   de marge, plus de coins, plus d'ombre, plus de bordure.

   Le compte est vite fait sur un telephone de 360 px : 16 px de padding de
   body + 16 px de marge de carte + 32 px de padding de carte de chaque cote ne
   laissaient que 232 px de contenu. Un tiers de la largeur partait en decor —
   un decor qui, a cette taille, ne se voit meme pas comme tel : la « carte »
   remplit deja tout l'ecran a la bordure pres.

   Le seuil : 480 (max-width de la carte) + 2x16 de marge + 2x16 de padding de
   body = 544 px. Au-dela, la carte flotte vraiment et le decor a un sens.

   DEUX PIEGES DE CASCADE, tous deux dus au fait que le <style> de chaque page
   est charge APRES cette feuille et gagne donc a specificite egale :
     - le selecteur porte `body .card` (0,1,1) et non `.card` seul (0,1,0),
       parce que cinq pages redefinissent `.card` chez elles ;
     - l'etirement passe par `align-self` sur l'element, pas par `align-items`
       sur le conteneur, parce que race-staff.html et race-participants.html
       posent `body { align-items:flex-start }`. align-self l'emporte quelle
       que soit la valeur choisie par la page. */
@media (max-width: 560px) {
  body { padding:0; }
  body .card {
    /* Occupe toute la hauteur : sans ca, une page courte laisserait une bande
       de --bg sous elle, et la carte redeviendrait une carte. */
    align-self:stretch;
    margin:0;
    max-width:none;
    border:none;
    border-radius:0;
    box-shadow:none;
    /* 16 px, pas 0 : le decor part, la gouttiere reste. Un titre ou un champ
       colle au verre est illisible, et sur un ecran aux coins arrondis il
       passe carrement sous le rayon. */
    padding:16px;
  }
}

:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
.tnum { font-variant-numeric:tabular-nums; }

/* Les selecteurs .switch (langue et theme) sont passes dans nav.css : le tiroir
   du menu les pose, et results.html en a donc besoin sans charger cette
   feuille-ci. Une seule definition, la ou tous les consommateurs la voient. */

/* --- Selecteur de coureurs suivis -----------------------------------------
   Utilise par runner-picker.js, donc par viewer.html (carte live) et
   replay.html (rejeu). Pose dans la barre du haut de ces deux pages : la
   question « qui est-ce que je regarde ? » se repond au meme endroit qu'on
   soit en direct ou en differe.

   Le panneau est en position:absolute sous le bouton, jamais en flux : il
   s'ouvre au-dessus d'une carte plein ecran et ne doit pas pousser la barre. */
.rp { position:relative; display:flex; align-items:center; gap:6px; min-width:0; flex-wrap:wrap; }
.rp-chips { display:flex; align-items:center; gap:5px; flex-wrap:wrap; min-width:0; }
.rp-none { font-size:.78rem; color:var(--text-dim); }

/* La couleur du coureur porte l'identite : c'est elle qu'on retrouve sur sa
   trace et sur son pion. En bordure et non en fond, pour que la pastille reste
   lisible sur les deux themes. */
.rp-chip {
  display:inline-flex; align-items:center; max-width:190px;
  border:1px solid var(--rp-color, var(--accent)); border-radius:999px;
  background:var(--surface); overflow:hidden;
}
.rp-chip.focus { background:var(--rp-color, var(--accent)); }
.rp-chip-name {
  font-family:inherit; font-size:.78rem; font-weight:600; line-height:1;
  padding:5px 4px 5px 9px; border:none; background:none; color:var(--rp-color, var(--accent));
  max-width:150px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
button.rp-chip-name { cursor:pointer; }
.rp-chip.focus .rp-chip-name { color:var(--on-accent); }
.rp-chip-x {
  border:none; background:none; cursor:pointer; padding:4px 8px 5px 4px; line-height:1;
  font-size:1rem; color:var(--rp-color, var(--accent)); opacity:.7;
}
.rp-chip.focus .rp-chip-x { color:var(--on-accent); }
.rp-chip-x:hover { opacity:1; }

.rp-add {
  flex:0 0 auto; border:1px dashed var(--line-strong); background:var(--surface); color:var(--text-dim);
  border-radius:999px; padding:5px 10px; font-family:inherit; font-size:.78rem; cursor:pointer;
}
.rp-add:hover, .rp-add.open { border-style:solid; border-color:var(--accent); color:var(--accent); }

.rp-panel {
  position:absolute; top:calc(100% + 8px); left:0; z-index:20; width:min(320px, calc(100vw - 48px));
  background:var(--surface); border:1px solid var(--border); border-radius:var(--radius);
  box-shadow:var(--shadow-lg, 0 12px 32px rgba(20,20,30,.18)); padding:10px;
}
.rp-search {
  width:100%; box-sizing:border-box; font-family:inherit; font-size:.85rem; padding:7px 10px;
  border:1px solid var(--border); border-radius:var(--radius-sm); background:var(--bg); color:var(--text);
}

/* Liste bornee en hauteur et defilante : un classement de 400 coureurs sortirait
   de l'ecran, et le panneau flotte au-dessus d'une carte sans page qui defile. */
.rp-list { max-height:min(46vh, 300px); overflow-y:auto; margin-top:2px; }
.rp-row {
  display:flex; align-items:center; gap:8px; width:100%; text-align:left;
  border:none; background:none; color:var(--text); font-family:inherit; font-size:.83rem;
  padding:6px 6px; border-radius:var(--radius-sm); cursor:pointer;
}
.rp-row:hover { background:var(--accent-soft); }
.rp-row.on { font-weight:600; }
.rp-swatch {
  flex:0 0 auto; width:10px; height:10px; border-radius:50%;
  border:1px solid var(--line-strong); background:transparent;
}
.rp-row.on .rp-swatch { border-color:transparent; }
.rp-bib { flex:0 0 auto; font-size:.76rem; color:var(--text-dim); min-width:2.2em; }
.rp-name { flex:1 1 auto; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.rp-live {
  flex:0 0 auto; font-size:.66rem; text-transform:uppercase; letter-spacing:.04em;
  color:var(--live-ink); background:var(--live-soft); padding:2px 6px; border-radius:999px;
}
.rp-empty { padding:12px 6px; font-size:.8rem; color:var(--text-dim); text-align:center; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration:.001ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
  }
}
