/* style.css — direction artistique du portfolio, appliquée à l'instrument.

   CE QUI A CHANGÉ, ET POURQUOI
   L'outil est né en thème clair, « scientific instrument, light », avec une
   palette OKLCH qui lui était propre. Il vit désormais DANS le site de
   Sébastien Oger : il en reprend la palette et la typographie, pour qu'on ne
   change pas d'univers en ouvrant l'application. Les jetons ci-dessous sont
   ceux de portfolio/assets/style.css, repris à l'identique — et ce sont les
   mêmes que ceux de l'autre application du site. Ils ne se réinventent pas
   ici : si la palette du site bouge, c'est CETTE liste qu'on met à jour.

   CE QUI N'A PAS CHANGÉ
   Le calcul, les poids, les seuils. Aucune valeur métier ne vit dans cette
   feuille. Et la règle du projet d'origine tient : un score n'est JAMAIS porté
   par la seule couleur — le rang, le pourcentage chiffré et la longueur de
   barre le portent ensemble.

   CE QUI EST NOUVEAU DANS LES FORMES
   Arrondis plus généreux que la V1, une barre de saisie proéminente, des
   résultats en LIGNES posées sur le fond — jamais des cartes dans une carte —
   une pastille de rang ronde, et le score traité comme le chiffre principal
   de la ligne. Demande de Sébastien, 22 septembre 2026, sur une référence
   visuelle qu'il a fournie : les formes se modernisent, la couleur ne bouge
   pas.
   -------------------------------------------------------------------------- */

:root {
  /* Architecture — jetons du portfolio */
  --bg: #0E1A25;          /* --canvas du site */
  --surface: #192938;
  --surface-2: #1F3346;
  --ink: #EEF4F9;
  --ink-soft: #C3D3E0;
  --muted: #8FA6B8;
  --border: #2B4760;
  --border-strong: #3A5C79;

  /* Accent interactif — le bleu du site */
  --accent: #85C9F4;
  --accent-hover: #A3D7F8;
  --accent-deep: #4E9FD8;
  --accent-ink: #0A1622;  /* encre POSÉE SUR l'accent : sombre, pas claire */
  --accent-soft: color-mix(in srgb, var(--accent) 14%, var(--bg));

  /* Sémaphore. --ok et --alert sont les jetons --good et --warn du site.
     L'ambre intermédiaire n'existe pas dans la palette du portfolio : il a été
     créé pour l'autre application, dans la même famille, et il est repris ici
     à l'identique plutôt que réinventé. */
  --ok: #B8E0C5;
  --warn: #F2C078;
  --alert: #FA584C;

  --ok-soft: color-mix(in srgb, var(--ok) 13%, var(--bg));
  --warn-soft: color-mix(in srgb, var(--warn) 16%, var(--bg));
  --alert-soft: color-mix(in srgb, var(--alert) 17%, var(--bg));

  /* LES ARRONDIS. --radius est celui du site ; les deux autres l'encadrent.
     C'est le jeton qui porte l'essentiel de la demande de modernisation :
     un panneau à 22 px et une pastille en pilule ne se lisent pas comme un
     formulaire de 2010. */
  --radius-lg: 22px;
  --radius: 14px;
  --radius-sm: 10px;
  --pill: 999px;

  /* Sur fond sombre, une ombre portée ne se voit presque pas : c'est la
     bordure qui détache un bloc. L'ombre ne sert qu'à asseoir les grands. */
  --shadow: 0 1px 2px rgb(0 0 0 / 0.30), 0 10px 30px rgb(0 0 0 / 0.26);

  --font-ui: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  /* Le monospace ne sert QU'aux valeurs mesurées et aux colonnes de chiffres,
     jamais comme costume « technique » sur du texte courant. */
  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", Consolas, monospace;

  --step--1: 0.8125rem;
  --step-0: 0.875rem;
  --step-1: 1rem;
  --step-2: 1.25rem;
  --step-3: clamp(1.4rem, 1.1rem + 1.2vw, 1.75rem);
  --step-4: clamp(1.9rem, 1.4rem + 2.2vw, 2.6rem);

  --ease: cubic-bezier(0.22, 1, 0.36, 1);

  color-scheme: dark;
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

/* Le maillage de fond, repris de l'autre application pour que les deux outils
   du site se ressemblent : un halo bleu très large en haut et une trame de
   points de 1 px. Purement décoratif, posé en background-image du body pour
   qu'aucun élément supplémentaire n'entre dans le DOM. */
body {
  margin: 0;
  padding: 0 clamp(14px, 3vw, 28px) 34px;
  background-color: var(--bg);
  background-image:
    radial-gradient(1200px 520px at 50% -10%,
                    color-mix(in srgb, var(--accent) 9%, transparent), transparent 70%),
    radial-gradient(circle at 1px 1px,
                    color-mix(in srgb, var(--accent) 16%, transparent) 1px, transparent 0);
  background-size: 100% 100%, 34px 34px;
  background-attachment: fixed, fixed;
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: var(--step-1);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

/* EMBARQUÉE, L'APPLICATION NE DÉFILE PAS.
   La classe est posée par shell.js au moment même où il met en route la
   mesure de hauteur : si le script ne s'exécute pas, la classe n'existe pas,
   la barre revient et le contenu reste atteignable. On ne masque jamais un
   défilement dont on n'a pas remplacé la fonction. */
html.is-embedded, html.is-embedded body { overflow: hidden; }

h1, h2, h3 { line-height: 1.2; text-wrap: balance; margin: 0; }
p { margin: 0; }

a { color: var(--accent); text-underline-offset: 3px; }
a:hover { color: var(--accent-hover); }

/* LES SURFACES DU NAVIGATEUR.
   Sélection, curseur de saisie, barres de défilement et anneau de focus
   arrivent avec des valeurs par défaut qui n'appartiennent à aucune charte.
   Les thémer coûte huit lignes et se voit partout. */
::selection { background: color-mix(in srgb, var(--accent) 34%, transparent); color: var(--ink); }
:root { accent-color: var(--accent); caret-color: var(--accent); scrollbar-color: var(--border-strong) transparent; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm); }

.sr-only {
  position: absolute; width: 1px; height: 1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

.shell { max-width: 1080px; margin: 0 auto; }

/* LA MESURE DES LIGNES D'AIDE.
   Elle était à 68ch, la mesure de confort d'un texte suivi. Ce n'en est pas
   un : ce sont des phrases de service d'une ou deux lignes, et à 68ch elles
   se coupaient bien avant le bord du panneau — le retour tombait au milieu de
   la phrase alors qu'il restait un tiers de la largeur libre. 112ch laisse
   passer la plus longue d'un trait sur un écran large, et en fait deux lignes
   pleines ailleurs. */
.hint { color: var(--muted); font-size: var(--step-0); max-width: 112ch; }
.hint strong { color: var(--ink-soft); font-weight: 600; }

/* Une phrase de service posée sous un champ : l'invite du champ de recherche,
   ou le fait qu'aucune nuance ne porte ce nom. Elle vivait dans un encadré en
   pointillés, seule au milieu d'un grand bloc — beaucoup de place pour peu de
   propos, et une mise en page qui sautait à chaque frappe. */
.hint-line {
  padding: 10px 18px 2px;
  font-size: var(--step--1);
  color: var(--muted);
}
.hint-line[data-tone="warn"] { color: var(--warn); }

/* ====================== ONGLETS ET BARRE DE BASE ========================= */

.controls {
  position: relative;
  padding-top: clamp(16px, 3vw, 26px);
  display: grid;
  gap: 14px;
}

/* LES ONGLETS — LE NIVEAU PRIMAIRE : DE QUEL TRAVAIL S'AGIT-IL ?
   Ils étaient une pilule segmentée, c'est-à-dire exactement la forme du
   sélecteur de base juste dessous. Deux contrôles de natures opposées avec la
   même apparence : l'un navigue, l'autre filtre, et rien ne le disait.

   Ce sont donc des onglets au sens propre — posés sur un filet, l'actif porté
   par un trait d'accent, le texte d'un cran plus grand, et chacun avec son
   icône. Le vocabulaire est celui que tout le monde connaît, ce qui est
   précisément ce qu'on veut du niveau primaire d'un outil. */
.tabbar {
  display: flex;
  gap: 6px;
  max-width: 100%;
  overflow-x: auto;
  scrollbar-width: none;
  border-bottom: 1px solid var(--border);
}
.tabbar::-webkit-scrollbar { display: none; }

.tab {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--muted);
  font: 600 var(--step-1)/1 var(--font-ui);
  letter-spacing: -0.01em;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 13px 4px 14px;
  margin-bottom: -1px;          /* le trait recouvre le filet, pas l'inverse */
  border-bottom: 2px solid transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: color 160ms var(--ease), border-color 160ms var(--ease);
}
.tab + .tab { margin-left: 22px; }
.tab:hover { color: var(--ink-soft); }
.tab:hover .tab__icon { stroke: var(--ink-soft); }
.tab[aria-selected="true"] {
  color: var(--ink);
  border-bottom-color: var(--accent);
}
.tab[aria-selected="true"] .tab__icon { stroke: var(--accent); }
.tab[aria-selected="true"] .tab__icon-dot { fill: var(--accent); stroke: none; }

/* Icônes dessinées, même graisse de trait que celles du site : elles disent la
   nature du travail, pas sa décoration. */
.tab__icon {
  width: 19px; height: 19px;
  flex: none;
  fill: none;
  stroke: var(--muted);
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: stroke 160ms var(--ease);
}
.tab__icon-dot { fill: none; }

.dbbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 14px;
}
/* Capitales : c'est une étiquette de champ, pas un intitulé de navigation.
   Trois mots, l'usage admis — et une marque de plus que cette barre n'est pas
   de la même famille que les onglets du dessus. */
.dbbar__label {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
}
.dbbar__meta {
  font-size: var(--step--1);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* LA BASE — LE NIVEAU SECONDAIRE : SUR QUELLE MATIÈRE ?
   Des puces encadrées, plus petites que les onglets, chacune portant SON
   nombre de nuances. C'est ce chiffre qui fait le plus gros du travail de
   distinction : un onglet nomme une action, une puce nomme un volume de
   données. La différence tient même en noir et blanc, et même pour qui ne
   verrait pas les deux barres en même temps. */
.seg { display: flex; gap: 7px; flex-wrap: wrap; }

.seg__btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  appearance: none;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--ink-soft);
  font: 500 var(--step--1)/1 var(--font-ui);
  padding: 8px 9px 8px 14px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  white-space: nowrap;
  transition: background 160ms var(--ease), color 160ms var(--ease),
              border-color 160ms var(--ease);
}
.seg__btn:hover { background: var(--surface-2); color: var(--ink); border-color: var(--border-strong); }
.seg__btn[aria-pressed="true"] {
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 52%, transparent);
  color: var(--ink);
}

.seg__name { min-width: 0; }

/* Le compte de nuances, dans sa propre capsule : c'est une donnée, pas un
   morceau du libellé, et il doit se lire comme telle. */
.seg__count {
  padding: 3px 8px;
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--muted);
  font: 600 0.6875rem/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
}
.seg__btn[aria-pressed="true"] .seg__count {
  background: color-mix(in srgb, var(--accent) 24%, var(--bg));
  color: var(--accent);
}

/* ============================== PANNEAUX ================================= */

.panel-tab { display: grid; gap: clamp(22px, 4vw, 34px); padding-top: 22px; }
.panel-tab[hidden] { display: none; }

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: clamp(18px, 3vw, 28px);
  box-shadow: var(--shadow);
}
.panel__head { display: grid; gap: 7px; margin-bottom: 20px; }
.panel__head h2 { font-size: var(--step-2); font-weight: 600; letter-spacing: -0.01em; }

/* Les résultats ne vivent PAS dans un panneau : ce sont des lignes posées sur
   le fond. Une carte dans une carte est toujours une erreur de composition,
   et à dix résultats elle rendrait la page illisible. */
.results-block { display: grid; gap: 14px; }
.results-head { display: grid; gap: 6px; }
.results-head h2 { font-size: var(--step-2); font-weight: 600; letter-spacing: -0.01em; }

/* ========================= BARRE DE SAISIE =============================== */

/* La barre proéminente. Deux usages : reporter une ligne de spectromètre dans
   la grille (onglet 1), et chercher une nuance (onglet 2). Le bouton vit
   DANS le champ, parce que l'action ne concerne que lui. */
.searchbar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 6px 6px 16px;
  background: var(--bg);
  border: 1px solid var(--border-strong);
  border-radius: var(--pill);
  transition: border-color 160ms var(--ease), box-shadow 160ms var(--ease);
}
.searchbar:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
.searchbar--lg { padding: 9px 9px 9px 18px; }

.searchbar__icon {
  flex: none;
  width: 19px; height: 19px;
  fill: none;
  stroke: var(--muted);
  stroke-width: 2;
  stroke-linecap: round;
}
.searchbar:focus-within .searchbar__icon { stroke: var(--accent); }

.searchbar input {
  flex: 1 1 auto;
  min-width: 0;
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--ink);
  font: 400 var(--step-1)/1.4 var(--font-ui);
  padding: 10px 0;
}
.searchbar--lg input { font-size: var(--step-2); }
.searchbar input::placeholder { color: var(--muted); }
.searchbar input:focus { outline: none; }

.searchbar__hint {
  margin: 9px 0 22px 18px;
  font-size: var(--step--1);
  color: var(--muted);
}
.searchbar__hint[data-tone="ok"] { color: var(--ok); }
.searchbar__hint[data-tone="warn"] { color: var(--warn); }

/* ======================== GRILLE D'ÉLÉMENTS ============================== */

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: 10px;
}

.field { display: grid; gap: 5px; }
.field label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px;
  padding: 0 3px;
}
.field__sym {
  font: 600 var(--step-0)/1 var(--font-mono);
  color: var(--ink);
  letter-spacing: 0.02em;
}
/* Le poids métallurgique est une information de second plan qui doit rester
   lisible : il explique pourquoi deux écarts identiques ne coûtent pas le
   même score. */
.field__w {
  font: 500 0.6875rem/1 var(--font-mono);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.field input {
  width: 100%;
  appearance: none;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font: 500 var(--step-0)/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  padding: 11px 10px;
  transition: border-color 140ms var(--ease), box-shadow 140ms var(--ease);
}
.field input::placeholder { color: color-mix(in srgb, var(--muted) 62%, transparent); }
.field input:hover { border-color: var(--border-strong); }
.field input:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
.field input.input--err {
  border-color: var(--alert);
  background: var(--alert-soft);
}
.field__err {
  min-height: 1em;
  padding: 0 3px;
  font-size: 0.6875rem;
  color: var(--alert);
}

/* ============================== BOUTONS ================================== */

/* LES ACTIONS SONT EN TÊTE DE PANNEAU.
   Elles suivent le titre et sa ligne d'aide, et précèdent la saisie : c'est
   « Charger un exemple » qui doit se voir en premier, pas après dix-huit
   champs vides. La marge est donc dessous, plus dessus. */
.actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-bottom: 22px;
}

.btn {
  appearance: none;
  cursor: pointer;
  font: 600 var(--step-0)/1 var(--font-ui);
  padding: 13px 22px;
  border-radius: var(--pill);
  border: 1px solid var(--border-strong);
  background: var(--surface-2);
  color: var(--ink);
  transition: background 160ms var(--ease), border-color 160ms var(--ease),
              color 160ms var(--ease), transform 120ms var(--ease);
}
.btn:hover { background: color-mix(in srgb, var(--accent) 12%, var(--surface-2)); border-color: var(--accent-deep); }
.btn:active { transform: translateY(1px); }

.btn--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}
.btn--primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: var(--accent-ink); }

.btn--ghost { background: transparent; border-color: var(--border); color: var(--ink-soft); }
.btn--ghost:hover { background: var(--surface-2); color: var(--ink); }

/* L'APPEL DU BOUTON D'EXEMPLE.
   Quelqu'un qui découvre la page n'a pas de composition sous la main : sans
   ce bouton, il regarde une grille vide et s'en va. L'appel est une lueur qui
   monte et redescend lentement — pas un anneau qui pulse, qui est le tic de
   toutes les visites guidées, et pas un mouvement, qui attirerait l'œil bien
   au-delà de ce que mérite un bouton secondaire.

   Il s'éteint définitivement à la première action : clic sur le bouton, ou
   saisie ailleurs. C'est calmerInvite(), dans main.js.

   La bordure accentuée, elle, ne dépend d'aucune animation : le bouton reste
   distingué des deux autres même sans mouvement — voir le bloc de mouvement
   réduit plus bas. */
.btn--invite {
  border-color: var(--accent-deep);
  color: var(--accent);
  animation: invite 3.2s ease-in-out infinite;
}
.btn--invite:hover { color: var(--ink); }

@keyframes invite {
  0%, 100% { box-shadow: 0 0 0 0 transparent; }
  50%      { box-shadow: 0 0 18px -3px color-mix(in srgb, var(--accent) 42%, transparent); }
}

.btn--inset { flex: none; padding: 11px 20px; }
.btn--sm { padding: 8px 14px; font-size: var(--step--1); }

.form-error {
  flex: 1 1 100%;
  margin: 0;
  font-size: var(--step-0);
  color: var(--alert);
}

/* ============================== NOTICES ================================== */

.notice {
  padding: 14px 18px;
  border-radius: var(--radius);
  border: 1px solid;
  font-size: var(--step-0);
  line-height: 1.5;
}
.notice strong { font-weight: 700; }
.notice--warn { background: var(--warn-soft); border-color: color-mix(in srgb, var(--warn) 45%, transparent); color: var(--ink); }
.notice--warn strong { color: var(--warn); }
.notice--alert { background: var(--alert-soft); border-color: color-mix(in srgb, var(--alert) 50%, transparent); color: var(--ink); }

.empty {
  padding: 26px 20px;
  border: 1px dashed var(--border);
  border-radius: var(--radius-lg);
  color: var(--muted);
  font-size: var(--step-0);
  text-align: center;
}

/* ========================= LIGNES DE RÉSULTAT ============================ */

.results { display: grid; gap: 9px; }

/* Trois colonnes : le rang, l'identité, le score. Sous 720 px la grille se
   replie en deux lignes, le score passant sous l'identité — c'est le seul
   repli nécessaire, la ligne ayant été pensée à 390 px d'abord. */
.result {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 16px;
  padding: 16px 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  animation: arrivee 420ms var(--ease) both;
  animation-delay: var(--d, 0ms);
}

/* La ligne de tête se distingue par sa teinte ET par son badge : la couleur
   ne porte jamais seule une information. */
.result--top { border-color: color-mix(in srgb, var(--accent) 52%, transparent); background: var(--accent-soft); }
.result--pass { border-color: color-mix(in srgb, var(--ok) 45%, transparent); background: var(--ok-soft); }

.result__rank {
  display: grid;
  place-items: center;
  width: 38px; height: 38px;
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--ink-soft);
  font: 700 var(--step-0)/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
}
.result--top .result__rank { background: var(--accent); color: var(--accent-ink); }
.result--pass .result__rank { background: var(--ok); color: var(--accent-ink); }

.result__id { display: grid; gap: 7px; min-width: 0; }
.result__std {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 9px;
  font: 600 var(--step-2)/1.15 var(--font-ui);
  letter-spacing: -0.01em;
}
.result__sub { font-size: var(--step--1); color: var(--muted); }
.result__sub--none { font-style: italic; }
.result__meta { font-size: var(--step--1); color: var(--ink-soft); font-variant-numeric: tabular-nums; }

/* Le score : c'est le chiffre principal de la ligne, il en a la taille.
   La barre sous lui n'est pas un ornement — elle rend l'écart entre deux
   scores voisins visible sans les lire. */
.result__score {
  display: grid;
  justify-items: end;
  gap: 7px;
  min-width: 108px;
}
.result__pct {
  font: 700 var(--step-3)/1 var(--font-ui);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.result--top .result__pct { color: var(--accent); }
.result--pass .result__pct { color: var(--ok); }

.bar {
  display: block;
  width: 108px;
  height: 5px;
  border-radius: var(--pill);
  background: var(--surface-2);
  overflow: hidden;
}
.bar__fill {
  display: block;
  height: 100%;
  width: var(--w, 0%);
  border-radius: inherit;
  background: var(--accent-deep);
}
.result--top .bar__fill { background: var(--accent); }
.result--pass .bar__fill { background: var(--ok); }

/* ============================= PASTILLES ================================= */

.chips { display: flex; flex-wrap: wrap; gap: 5px; }

.chip {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  padding: 4px 10px;
  border-radius: var(--pill);
  border: 1px solid var(--border);
  background: var(--bg);
  font: 500 0.75rem/1.25 var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
  cursor: default;
}
.chip b { font-weight: 700; color: var(--ink); }
.chip__v { color: var(--muted); }

.chip--ok { border-color: color-mix(in srgb, var(--ok) 42%, transparent); background: var(--ok-soft); }
.chip--ok b { color: var(--ok); }
.chip--bad { border-color: color-mix(in srgb, var(--warn) 45%, transparent); background: var(--warn-soft); }
.chip--bad b { color: var(--warn); }
.chip--na { border-style: dashed; opacity: 0.68; }

.badge {
  display: inline-block;
  padding: 4px 11px;
  border-radius: var(--pill);
  font: 700 0.6875rem/1 var(--font-ui);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}
.badge--best { background: var(--accent); color: var(--accent-ink); }
.badge--pass { background: var(--ok); color: var(--accent-ink); }
.badge--fail { background: var(--alert-soft); color: var(--alert); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--alert) 48%, transparent); }

/* ======================== CARTE DE NUANCE ================================ */

/* La composition d'une fiche de nuance : l'identité à gauche, les teneurs
   nominales en grand à droite, séparées par un filet. C'est la forme de la
   référence fournie, transposée sur fond sombre. */
.gradecard {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(18px, 4vw, 40px);
  align-items: center;
  padding: clamp(18px, 3vw, 26px);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}
.gradecard__id { display: grid; gap: 8px; justify-items: start; min-width: 0; }
.gradecard__std {
  font: 700 var(--step-4)/1 var(--font-ui);
  letter-spacing: -0.03em;
}
.gradecard__type { font-size: var(--step-0); color: var(--ink-soft); }
.gradecard__count {
  font-size: var(--step--1);
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

.gradecard__figures {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(16px, 3vw, 30px);
  padding-left: clamp(18px, 4vw, 40px);
  border-left: 1px solid var(--border);
}
.figure { display: grid; justify-items: center; gap: 2px; }
.figure__el { font: 600 var(--step-0)/1 var(--font-mono); color: var(--muted); }
.figure__v {
  font: 700 var(--step-3)/1 var(--font-ui);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.figure__u { font-size: 0.6875rem; color: var(--muted); }

/* ======================== TABLEAU DES LIMITES ============================ */

.spec {
  width: 100%;
  border-collapse: collapse;
  margin-top: 12px;
  font-size: var(--step-0);
}
.spec th {
  text-align: left;
  padding: 10px 14px;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--muted);
  border-bottom: 1px solid var(--border);
}
.spec td {
  padding: 10px 14px;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 55%, transparent);
  font-variant-numeric: tabular-nums;
}
.spec tr:last-child td { border-bottom: 0; }
.spec__el { font-family: var(--font-mono); font-weight: 600; color: var(--ink); }
.spec__lim { font-family: var(--font-mono); color: var(--accent); }
.spec__nom { font-family: var(--font-mono); color: var(--ink-soft); }

/* ============================ PIED DE L'APP ============================== */

/* Ce bloc n'existe QUE hors de la page hôte — shell.js lui retire son attribut
   `hidden` uniquement si l'application est ouverte seule. Embarquée, elle n'a
   aucun pied : la page du site porte déjà tout ce qu'il contenait. */
.appfoot {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  margin-top: clamp(28px, 5vw, 44px);
  padding-top: 18px;
  border-top: 1px solid var(--border);
  font-size: var(--step--1);
  color: var(--muted);
}
.appfoot[hidden] { display: none; }

/* ============================== MOUVEMENT ================================ */

/* UN SEUL MOMENT ANIMÉ DANS TOUTE L'APPLICATION : l'arrivée des résultats.
   Il porte une information — un nouveau classement vient d'être calculé — et
   part d'un état déjà visible si l'animation ne s'exécute pas. */
@keyframes arrivee {
  from { opacity: 0; transform: translateY(9px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-delay: 0ms !important;
    transition-duration: 1ms !important;
  }
  /* L'appel du bouton d'exemple se coupe franchement plutôt que de tourner
     à 1 ms, ce qui serait un scintillement. Sa bordure accentuée reste : le
     bouton continue de se distinguer, simplement sans respirer. */
  .btn--invite { animation: none !important; }
}

/* ============================= RESPONSIVE ================================ */

@media (max-width: 720px) {
  .result {
    grid-template-columns: auto minmax(0, 1fr);
    gap: 12px 14px;
  }
  /* Le score passe sous l'identité, aligné à gauche sous elle et non sous la
     pastille de rang, sinon il flotte au milieu de rien. */
  .result__score {
    grid-column: 2;
    justify-items: start;
  }
  .bar { width: 100%; min-width: 140px; }

  .gradecard { grid-template-columns: minmax(0, 1fr); }
  .gradecard__figures {
    padding-left: 0;
    padding-top: 18px;
    border-left: 0;
    border-top: 1px solid var(--border);
    width: 100%;
    justify-content: space-between;
  }
}

@media (max-width: 420px) {
  .grid { grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); }
  .tab { font-size: var(--step-0); }
  .tab + .tab { margin-left: 14px; }
  /* Le nom d'une base fait près de quarante caractères : à cette largeur il
     passe sur deux lignes plutôt que de forcer un défilement horizontal. */
  .seg { width: 100%; }
  .seg__btn { white-space: normal; text-align: left; flex: 1 1 100%; justify-content: space-between; }
  /* Le bouton de la barre de saisie passe sous le champ : à cette largeur,
     les deux côte à côte ne laissent pas trois caractères au champ. */
  .searchbar { flex-wrap: wrap; border-radius: var(--radius-lg); padding: 12px 14px; }
  .searchbar input { flex: 1 1 100%; }
  .btn--inset { flex: 1 1 100%; }
}

/* ============================= IMPRESSION ================================ */

/* Le piège documenté sur l'autre application vaut ici : un thème sombre
   imprimé sort en encre pâle sur papier blanc. L'application n'a pas de
   rapport à imprimer — c'est un outil interactif — mais si quelqu'un imprime
   tout de même la page, elle doit rester lisible. La palette est donc
   redéclarée en clair, et les fonds décoratifs retirés. */
@media print {
  :root {
    --bg: #fff;
    --surface: #fff;
    --surface-2: #f2f5f8;
    --ink: #10202e;
    --ink-soft: #33485c;
    --muted: #5c7085;
    --border: #c8d4de;
    --border-strong: #a9b9c7;
    --accent: #1c5f8f;
    --accent-deep: #1c5f8f;
    --accent-ink: #fff;
    --accent-soft: #eaf3fa;
    --ok: #1f6b3a;
    --ok-soft: #ebf6ef;
    --warn: #8a5a12;
    --warn-soft: #fbf3e5;
    --alert: #a32017;
    --alert-soft: #fbeceb;
    --shadow: none;
    color-scheme: light;
  }
  body { background-image: none; padding: 0; }
  html.is-embedded, html.is-embedded body { overflow: visible; }
  .tabbar, .actions, .searchbar, .searchbar__hint, .hint-line, .appfoot { display: none; }
  .panel, .result, .gradecard { break-inside: avoid; box-shadow: none; }
  /* Les deux panneaux d'onglets s'impriment, y compris celui qui est masqué à
     l'écran : sur papier, il n'y a plus d'onglet pour aller le chercher. */
  .panel-tab[hidden] { display: grid !important; }
}
