/* Accueil — feuille du widget « Valideur JSON » (js/widgets/json.js).

   UN WIDGET = UN FICHIER, ici comme ailleurs : rien de tout ceci n'a sa place
   dans components.css. Trois autres endroits doivent suivre, et leur oubli est
   MUET : la balise <link> d'index.html, l'entrée dans SOCLE de sw.js (sans
   quoi la feuille manque hors ligne, et le déploiement refuse de partir), et
   la balise <link> de test-grille.html — sans quoi les assertions de rendu
   mesureraient un style absent.

   ORDRE DU <link>, ET IL COMPTE : cette feuille vient APRÈS atelier.css. Elle
   est la seule d'un widget à s'appuyer sur les classes `.atl__*` et à devoir
   les corriger.

   DEUX BLOCS SANS PARENTÉ : `.jsw` pour la carte, `.jsfs` pour l'atelier. Pas
   une règle mutualisée — le préfixe de projection est un bloc distinct, pas un
   modificateur du premier. */

/* ═══════════════ LA CARTE ═══════════════ */

.jsw {
  gap: 7px;
  /* Le DERNIER RECOURS, repris de qr-link : quand la carte est écrasée à
     90 px, rogner ferait disparaître les boutons SANS RIEN DIRE. Un
     défilement les laisse atteignables. Une ligne, et le cas est réglé. */
  overflow-y: auto; overflow-x: hidden;
  min-height: 96px;
}
.jsw--depot { outline: 2px dashed rgba(var(--accent-rgb), .55); outline-offset: -4px; }

/* CORRECTIF OBLIGATOIRE : atelier.css ne pose `white-space` nulle part, et un
   verdict un peu long passerait à deux lignes en volant 16 px — c'est-à-dire
   une ligne de document sur trois. La phrase entière reste en infobulle. */
.jsw .atl__etat-txt { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* LE BANDEAU EST LA PORTE de l'atelier. En faire le geste principal libère la
   barre pour l'action du moment — réparer, réindenter, copier. */
.jsw__bandeau {
  position: relative; width: 100%; text-align: left; cursor: pointer; flex: none;
  font: inherit;
}
.jsw__bandeau:hover { border-color: rgba(255, 255, 255, .22); }
.jsw__verdict { display: block; font: 500 13px/1.25 var(--sans); color: var(--text-strong); }
.jsw__fiche { display: block; font: 400 11px/1.35 var(--mono); color: var(--text-5); }

/* LA FRISE — un rail collé au bord bas du bandeau, un tic par constat, posé à
   sa position RELATIVE dans le document. Elle dit d'un coup d'œil ce qu'aucune
   phrase ne dit : douze fautes groupées en tête, c'est un collage tronqué ;
   éparpillées, c'est un document tapé à la main. Au-delà d'une quarantaine de
   tics ils se chevauchent — la densité EST le message, on ne dégrade rien. */
.jsw__frise { position: absolute; inset: auto 1px 1px 1px; height: 3px; overflow: hidden;
  border-radius: 0 0 var(--r-md) var(--r-md); }
.jsw__tic { position: absolute; top: 0; bottom: 0; width: 2px;
  left: calc(var(--p) * 100%); background: var(--danger); }
.jsw__tic--avis { background: var(--warning); }

/* LA SEULE zone élastique. Tous les autres enfants directs sont en flex:none :
   c'est ce qui garantit que le texte rétrécit, jamais la barre. */
.jsw__zone { flex: 1 1 auto; min-height: 0; display: flex; }
.jsw__zone > * { flex: 1 1 auto; min-height: 0; }

/* L'EXTRAIT — trois lignes, celles de la FAUTE, pas les trois premières du
   fichier qui ne disent rien. AUCUN accent circonflexe sous la ligne, nulle
   part : une tabulation compte pour un caractère et s'affiche pour quatre, un
   emoji occupe deux unités UTF-16, et un curseur aligné par comptage tombe
   systématiquement à côté. Le <mark> sur les caractères RÉELS ne peut pas se
   désaligner. C'est le seul texte colorié de la carte, et il est figé : aucune
   saisie, donc aucun miroir à tenir synchronisé. */
.jsw__extrait {
  display: grid; grid-template-columns: 28px minmax(0, 1fr); gap: 0 8px;
  align-content: start;
  font: 400 11.5px/1.5 var(--mono); overflow: auto;
  padding: 7px 9px; border-radius: var(--r-md);
  background: var(--glass-inner); border: 1px solid var(--glass-inner-border);
  max-height: calc(var(--jsw-lignes, 3) * 17.25px + 16px);
}
.jsw__num { text-align: right; color: var(--text-6); user-select: none;
  font-variant-numeric: tabular-nums; }
.jsw__txt { white-space: pre; overflow: hidden; text-overflow: ellipsis; color: var(--text-3); }
.jsw__ligne--geste { color: var(--text-2); }
.jsw__coupe { color: var(--text-6); }
.jsw__mark { background: rgba(248, 113, 113, .34); color: var(--text-strong);
  border-radius: 2px; padding: 0 1px; }

.jsw__memoire { font: 400 11.5px/1.45 var(--sans); color: var(--text-5); overflow: auto; }
.jsw__memoire p { margin: 0 0 6px; }

/* Le compteur-navigateur. Il fait défiler les constats SANS RIEN ÉCRIRE —
   ni réglages, ni rangement local — donc la signature ne bouge pas, donc aucun
   rendu de l'application ne peut effacer la lecture en cours. */
.jsw__nav { display: inline-flex; align-items: center; gap: 2px; flex: none; }
.jsw__compte { font: 400 11px var(--mono); color: var(--text-5); padding: 0 3px;
  font-variant-numeric: tabular-nums; }

/* Carte RASE : la fiche et l'extrait s'effacent, le verdict et la barre
   restent. Le verdict survit TOUJOURS — c'est le contrat de la carte. */
.jsw--rase .jsw__fiche,
.jsw--rase .jsw__extrait,
.jsw--rase .jsw__memoire { display: none; }

/* LE RÉFLEXE QUI SUPPRIME DU CODE : `.card` porte déjà
   `container-type: inline-size`. Le seuil de LARGEUR n'a donc besoin d'AUCUN
   JavaScript — ni observateur, ni hystérésis, ni risque d'oscillation — et la
   bascule est juste PENDANT le redimensionnement au lieu d'être juste une
   image plus tard. Seule la hauteur restante justifie encore une mesure.

   On ne pose jamais `atl--serre` ici : la règle de la brique ne s'applique
   donc pas, il n'y a aucun duel de spécificité à perdre, et l'ordre des
   feuilles cesse d'être une bombe à retardement. */
@container card (max-width: 380px) {
  .jsw .atl__b { padding: 0 9px; }
  .jsw .atl__b span { display: none; }
  /* Un bouton de réparation réduit à son icône serait un bouton MUET qui
     RÉÉCRIT le document de l'utilisateur — exactement ce que la règle « aucune
     réparation silencieuse » interdit. Son libellé, qui porte le nombre,
     survit ; ce sont les autres qui s'effacent. */
  .jsw .atl__b--principal span { display: inline; }
}

/* ═══════════════ L'ATELIER ═══════════════ */

/* `.fs` est un `display:grid; place-items:center` SANS ascenseur, et la couche
   dépose le contenu dans un <div> nu : un bloc plus haut que la fenêtre
   déborderait des deux côtés, ses premières rangées devenant inatteignables.
   D'où une hauteur bornée et une grille à trois rangées dont la seule
   élastique porte le document. */
.jsfs {
  width: min(1480px, 95vw); height: min(900px, 90vh);
  display: grid; grid-template-columns: minmax(0, 1.6fr) 360px;
  grid-template-rows: auto minmax(0, 1fr) auto; gap: 14px;
  --jsfs-lh: 17px;
}
.jsfs::before { content: ""; position: fixed; inset: 0; z-index: -1;
  background: rgba(8, 9, 13, .74); }

.jsfs__tete { grid-column: 1 / -1; align-items: flex-start; }
.jsfs__verdict { font: 500 clamp(17px, 2.6vh, 25px)/1.25 var(--sans); color: var(--text-strong); }
/* ICI la phrase a le droit de respirer sur deux lignes : c'est exactement ce
   que la carte ne peut pas porter, et la raison d'être de l'atelier. */
.jsfs__phrase { font: 400 12.5px/1.5 var(--sans); color: var(--text-3); margin: 4px 0 0;
  max-width: 92ch; user-select: text; }
.jsfs__stats { font: 400 11.5px/1.4 var(--mono); color: var(--text-5); flex: none;
  text-align: right; user-select: text; }

/* LA SOURCE. Deux décisions tiennent ensemble et se garantissent l'une
   l'autre : `wrap="off"` (posé en JS) fait qu'une ligne logique occupe
   exactement une rangée visuelle — l'alignement de la gouttière est vrai PAR
   CONSTRUCTION et non par calcul —, et `--jsfs-lh` est LA hauteur de ligne,
   déclarée une seule fois et consommée par les deux. Deux valeurs séparées
   finiraient par diverger, et un faux alignement ne casse rien : il MENT sur
   la position. */
.jsfs__source {
  display: grid; grid-template-columns: 54px minmax(0, 1fr); min-height: 0;
  border-radius: var(--r-md); overflow: hidden;
  background: var(--glass-inner); border: 1px solid var(--glass-inner-border);
}
.jsfs__gouttiere {
  overflow: hidden; text-align: right; user-select: none; padding: 10px 8px 10px 0;
  font: 400 11.5px/var(--jsfs-lh) var(--mono); color: var(--text-6);
  font-variant-numeric: tabular-nums; cursor: pointer;
  border-right: 1px solid var(--glass-inner-border);
}
.jsfs__num--geste { color: var(--danger); background: rgba(248, 113, 113, .16); font-weight: 500; }
.jsfs__num--faute { color: var(--danger); }
.jsfs__num--avis { color: var(--warning); }
.jsfs__zone {
  border: none; outline: none; resize: none; padding: 10px 12px;
  background: transparent; color: var(--text-2);
  font: 400 12.5px/var(--jsfs-lh) var(--mono);
  white-space: pre; overflow: auto;
  scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, .22) transparent;
}
.jsfs__zone::selection { background: rgba(248, 113, 113, .42); color: #fff; }

.jsfs__liste { overflow-y: auto; min-height: 0; display: flex; flex-direction: column; gap: 7px;
  scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, .22) transparent; }
.jsfs__rien { font: 400 12px/1.5 var(--sans); color: var(--text-5); margin: 0; }

/* Le bandeau de provenance REMPLACE l'énumération : un diagnostic et un
   bouton, à la place de douze constats qui disent tous la même chose. */
.jsfs__provenance {
  padding: 10px 12px; border-radius: var(--r-md);
  background: rgba(var(--accent-rgb), .16); border: 1px solid rgba(var(--accent-rgb), .34);
  display: flex; flex-direction: column; gap: 8px; align-items: flex-start;
  user-select: text;
}
.jsfs__provenance p { margin: 0; font: 400 12px/1.5 var(--sans); color: var(--text-2); }

.jsfs__constat {
  padding: 8px 10px; border-radius: var(--r-sm); cursor: pointer;
  background: var(--glass-inner); border: 1px solid var(--glass-inner-border);
  display: flex; flex-direction: column; gap: 4px; align-items: flex-start;
  border-left: 2px solid var(--danger);
}
.jsfs__constat--avis { border-left-color: var(--warning); }
.jsfs__constat--courant { background: rgba(255, 255, 255, .07); border-color: rgba(255, 255, 255, .18); }
.jsfs__ou { font: 400 10.5px var(--mono); color: var(--text-6); letter-spacing: .03em; }
.jsfs__titre { font: 500 12.5px/1.3 var(--sans); color: var(--text-strong); }

/* La couche pose `user-select: none` sur TOUT sauf `input` et `textarea` : un
   chemin affiché dans un <code> y serait du texte MORT. D'où un <input> en
   lecture seule, et son bouton Copier. Un valideur dont on ne peut pas copier
   le chemin qu'il affiche n'a pas rendu son service. */
.jsfs__cheminBoite { display: flex; gap: 6px; width: 100%; align-items: center; }
.jsfs__chemin {
  flex: 1 1 auto; min-width: 0; user-select: text;
  padding: 5px 8px; border-radius: var(--r-sm);
  background: var(--glass-field); border: 1px solid var(--glass-field-border);
  color: var(--text-2); font: 400 11.5px var(--mono);
}

.jsfs__ruban { grid-column: 1 / -1; display: flex; flex-direction: column; gap: 7px; }
/* LA RECETTE, la piste d'audit : à tout instant on lit CE QUI A ÉTÉ FAIT à son
   texte. C'est elle qui rend le reste digne de confiance — sans elle, « aucune
   réparation silencieuse » n'est qu'une intention. */
.jsfs__recette { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font: 400 11px/1.4 var(--mono); color: var(--text-5); user-select: text; }

/* Sous 980 px, les deux colonnes s'empilent : la source garde la moitié haute,
   la liste des constats prend le reste. */
@media (max-width: 980px) {
  .jsfs { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1.2fr) minmax(0, 1fr) auto; }
  .jsfs__tete, .jsfs__ruban { grid-column: 1; }
}
