@zevra/ui 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/data.css ADDED
@@ -0,0 +1,742 @@
1
+ /* Zevra UI — données : tableaux, listes, cellules, métadonnées, citations
2
+ *
3
+ * CE QUE PORTE LE MODULE
4
+ * le cadre de tableau et sa grille (.zv-table, .zv-table__head, .zv-row),
5
+ * les trois registres de cellule, le dégroupage mobile (.zv-unwrap), la
6
+ * liste sans colonnes, la métadonnée mono, le couple clé-valeur, la
7
+ * pastille d'initiales, les deux utilitaires de troncature, l'étiquette de
8
+ * catégorie, la puce de citation et la vignette de format.
9
+ *
10
+ * DÉPENDANCES
11
+ * tokens.css — pour les seules variables qui y sont déclarées ;
12
+ * base.css — reset (box-sizing), body, et le :focus-visible global, dont
13
+ * ce module ne redéfinit ni la couleur ni l'épaisseur.
14
+ * Ni le reset, ni body, ni :root ne sont reposés ici.
15
+ *
16
+ * CE QUE LE MODULE NE PORTE PAS
17
+ * la pagination (.zv-pager--foot, navigation.css) : le pied de tableau et
18
+ * le pied de pagination sont LE MÊME nœud dans la source (un seul
19
+ * conteneur flex, compteur à gauche, flèches à droite). La version de ce
20
+ * module a été supprimée, elle faisait doublon — les emboîter donnait deux
21
+ * space-between imbriqués et 24px de padding vertical.
22
+ * Aucun contrôle de formulaire non plus : [disabled] et [aria-invalid]
23
+ * n'ont pas d'objet ici, ils vivent dans forms.css.
24
+ *
25
+ * ÉCHELLES — comment lire les tailles ci-dessous
26
+ * La source emploie des demi-pixels (9,5 / 10,5 / 11,5) que le paquet ne
27
+ * nomme pas. Deux rabattements, appliqués systématiquement :
28
+ * — registre TEXTE : plancher --app-sm (12px). Les paliers 10,5 et 11,5
29
+ * relevés y remontent ; c'est une décision, pas un relevé.
30
+ * — micro-labels et glyphes (label de colonne, initiales, vignette) :
31
+ * l'échelle mono (--mono-lg 11 / --mono 10 / --mono-sm 9), employée là
32
+ * comme MESURE, la famille restant celle que la règle déclare.
33
+ * ⚠️ Toutes les tailles et tous les interlettrages mono de la source ont
34
+ * été mesurés sur Menlo (chasse fixe). Le paquet rend la mono en Space
35
+ * Grotesk, de largeur et de hauteur d'x différentes : ces valeurs ne sont
36
+ * donc plus des relevés, ce sont des transpositions à revérifier à l'œil.
37
+ *
38
+ * ÉTATS
39
+ * Le seul état interactif de la source est le survol (.zv-row, .zv-cite).
40
+ * Ni sélection, ni ligne active, ni tri : c'est dit à chaque endroit
41
+ * concerné plutôt que comblé par une valeur inventée.
42
+ */
43
+
44
+ /* ─── Cadre de tableau ──────────────────────────────────────────────────
45
+ * Le tableau scrolle DANS son cadre : sans ce confinement, une colonne trop
46
+ * large fait scroller la page entière et le rail de navigation part avec.
47
+ * Angle vif et sans ombre : un tableau est une surface de travail, pas une
48
+ * carte éditoriale. Aucun rayon n'est posé — la valeur par défaut est déjà 0,
49
+ * il n'y a rien à annuler tant qu'on ne compose pas .zv-table avec .zv-card
50
+ * (ce que ce module déconseille : ce sont deux intentions différentes). */
51
+
52
+ .zv-table {
53
+ overflow-x: auto;
54
+ border: 1px solid var(--line);
55
+ background: var(--white);
56
+
57
+ /* --zv-cols est un PARAMÈTRE de grille, pas un token de marque : il n'a
58
+ * rien à faire dans tokens.css et n'y est pas. Il est déclaré ici, sur la
59
+ * classe qui l'expose, avec une valeur de repli ; l'appelant l'écrase en
60
+ * style inline sur ce même élément (le style inline l'emporte), et les
61
+ * enfants en héritent. Une seule déclaration pour l'en-tête ET les lignes :
62
+ * recopié aux deux endroits, on l'oublie à l'un des deux et les intitulés
63
+ * se décalent des valeurs sans que rien ne casse visiblement.
64
+ *
65
+ * Repli à une colonne souple bornée à 0 : sans gabarit, la rangée se
66
+ * dégrade en pile dont les cellules tronquent, au lieu de pousser le cadre.
67
+ *
68
+ * Gabarits relevés, à reprendre tels quels :
69
+ * bibliothèque : 26px minmax(150px, 1fr) 78px 92px 170px 128px
70
+ * répertoire : minmax(190px, 1.4fr) 108px 118px minmax(150px, 1fr) 74px 96px
71
+ * Les colonnes souples sont en minmax(0|150px, …) : sans borne basse à 0 ou
72
+ * sans min-width sur la cellule, la troncature ne se déclenche jamais. */
73
+ --zv-cols: minmax(0, 1fr);
74
+ }
75
+
76
+ .zv-table__head {
77
+ display: grid;
78
+ /* Le repli est répété dans le var() : .zv-table__head et .zv-row servent
79
+ * aussi hors de .zv-table (rangée seule dans une carte), où rien ne
80
+ * déclarerait le paramètre. */
81
+ grid-template-columns: var(--zv-cols, minmax(0, 1fr));
82
+ align-items: center;
83
+ gap: 10px;
84
+ /* Filet plein (--line) contre le cheveu (--hairline) des lignes : c'est le
85
+ * seul repère qui sépare les intitulés des données, il doit être plus
86
+ * marqué qu'une séparation de lignes. */
87
+ border-bottom: 1px solid var(--line);
88
+ background: var(--panel);
89
+ padding: 9px 14px;
90
+ font-family: var(--font-mono);
91
+ font-size: var(--mono);
92
+ font-weight: 600;
93
+ /* 0,14em et non les 0,2em de .zv-mono : c'est l'interlettrage que le paquet
94
+ * emploie déjà pour les capitales denses (.zv-tag, .zv-src). À 0,2em, six
95
+ * intitulés de colonne ne tiennent plus dans leurs pistes. */
96
+ letter-spacing: 0.14em;
97
+ text-transform: uppercase;
98
+ color: var(--label);
99
+ }
100
+
101
+ /* En-tête d'un tableau posé DANS une carte : la carte porte déjà son filet,
102
+ * un second trait au-dessus ferait doublon. */
103
+ .zv-table__head--inset {
104
+ border-bottom: 0;
105
+ padding: 7px 10px;
106
+ }
107
+
108
+ /* ⚠️ Aucun tri : les intitulés sont inertes, sans indicateur ni gestionnaire.
109
+ * Rien dans la source ne dit ce que trierait un clic ni comment se marque la
110
+ * colonne active — un en-tête triable serait une décision, pas un style. */
111
+
112
+ /* ─── Ligne de données ─────────────────────────────────────────────────
113
+ * Une seule écriture de balisage pour deux mises en page : grille au-dessus
114
+ * du seuil, carte empilée en dessous (voir .zv-unwrap et le bloc responsive).
115
+ * Aucun zébrage nulle part dans la source — le survol est le seul repère de
116
+ * ligne, ce qui laisse le fond blanc porter la lecture. */
117
+
118
+ .zv-row {
119
+ display: grid;
120
+ grid-template-columns: var(--zv-cols, minmax(0, 1fr));
121
+ align-items: center;
122
+ gap: 10px;
123
+ padding: 10px 14px;
124
+ border-bottom: 1px solid var(--hairline);
125
+ text-decoration: none;
126
+ cursor: pointer;
127
+ }
128
+
129
+ /* La dernière ligne colle au cadre : son filet doublerait le trait de bordure
130
+ * et épaissirait le bas du tableau. C'est déjà ce que fait la source côté
131
+ * bibliothèque et côté import (`last:border-b-0`) ; seul le répertoire garde
132
+ * le filet. Le module tranche pour le retrait — ce n'est donc pas une
133
+ * correction contre la production, c'est l'alignement sur sa majorité. */
134
+ .zv-row:last-child {
135
+ border-bottom: 0;
136
+ }
137
+
138
+ .zv-row:hover {
139
+ /* --panel sur --white : l'écart est faible (2 %), c'est le seul fond de
140
+ * survol dont dispose un système clair sans introduire un troisième gris.
141
+ * Ne pas y accrocher un signal qui doit être vu de loin. */
142
+ background: var(--panel);
143
+ /* La ligne est un lien : sans cette annulation, le survol souligne d'un
144
+ * coup tout le contenu de la rangée. */
145
+ text-decoration: none;
146
+ }
147
+
148
+ /* Le cadre du tableau est en overflow auto : un contour de focus posé à
149
+ * l'extérieur de la ligne y serait rogné sur les bords. On le rentre.
150
+ * (La couleur et l'épaisseur restent celles de base.css.) */
151
+ .zv-row:focus-visible {
152
+ outline-offset: -2px;
153
+ }
154
+
155
+ /* ⚠️ Ni état sélectionné, ni :focus-within, ni ligne active : la source n'en
156
+ * contient aucun, et les inventer ferait apparaître un fond de plus dans un
157
+ * système qui n'en a que deux (blanc, survol). */
158
+
159
+ /* ─── Cellules ─────────────────────────────────────────────────────────
160
+ * Trois registres seulement : le texte courant, le nombre, et l'intitulé qui
161
+ * remplace l'en-tête quand celui-ci disparaît. */
162
+
163
+ .zv-cell {
164
+ /* Sans min-width: 0, une cellule de grille ne descend pas sous la largeur
165
+ * de son contenu : la troncature ne se déclenche pas et la ligne pousse le
166
+ * tableau au lieu de couper. C'est la panne la plus fréquente ici. */
167
+ min-width: 0;
168
+ overflow: hidden;
169
+ text-overflow: ellipsis;
170
+ white-space: nowrap;
171
+ font-size: var(--app-sm);
172
+ color: var(--ink-2);
173
+ }
174
+
175
+ /* Cellule d'identité (nom de document, nom d'entité) : c'est elle qu'on lit
176
+ * en premier, elle monte d'un cran en taille, en graisse et en contraste.
177
+ * Sans cette variante, .zv-cell rendrait le titre au même poids que ses
178
+ * métadonnées et la ligne perdrait son point d'entrée. */
179
+ .zv-cell--name {
180
+ font-size: var(--app);
181
+ font-weight: 500;
182
+ color: var(--ink);
183
+ }
184
+
185
+ /* Nombres et dates, alignés à droite.
186
+ *
187
+ * ⚠️ CORRECTION DE FOND. La version précédente comptait sur --font-mono pour
188
+ * aligner les colonnes de chiffres. Dans la source, --font-mono valait
189
+ * `ui-monospace, Menlo` — une vraie chasse fixe. Dans CE paquet, --font-mono
190
+ * vaut « Space Grotesk », une grotesque PROPORTIONNELLE : le 1 y est plus
191
+ * étroit que le 8, les unités ne tombent pas les unes sous les autres et
192
+ * l'alignement annoncé n'existe pas.
193
+ *
194
+ * Ce qui aligne réellement, c'est font-variant-numeric: tabular-nums : la
195
+ * fonte sert alors ses chiffres à chasse égale, quelle que soit sa famille.
196
+ * La famille mono est conservée ici pour le REGISTRE (valeur technique), pas
197
+ * pour l'alignement — les deux ne sont plus confondus. */
198
+ .zv-cell--num {
199
+ /* Sans effet dans la grille ; utile sous 768px, où la cellule se retrouve
200
+ * enfant d'un .zv-unwrap devenu flex et ne doit pas se laisser comprimer. */
201
+ flex: none;
202
+ text-align: right;
203
+ font-family: var(--font-mono);
204
+ font-size: var(--mono-lg);
205
+ font-weight: 500;
206
+ font-variant-numeric: tabular-nums;
207
+ white-space: nowrap;
208
+ color: var(--muted);
209
+ }
210
+
211
+ /* Intitulé porté par la valeur : quand la ligne devient une carte, l'en-tête
212
+ * de colonnes n'existe plus et « 12 » ne veut plus rien dire. Il disparaît
213
+ * dès que la colonne reprend son titre (bloc responsive). */
214
+ .zv-cell__label {
215
+ display: none;
216
+ font-family: var(--font-mono);
217
+ font-size: var(--mono);
218
+ font-weight: 600;
219
+ letter-spacing: 0.14em;
220
+ text-transform: uppercase;
221
+ color: var(--label);
222
+ }
223
+
224
+ /* Même logique pour l'unité en clair — « 42 docs » en carte, « 42 » en
225
+ * tableau. Reste INLINE : elle appartient à la valeur, pas à sa ligne. */
226
+ .zv-cell__unit {
227
+ display: none;
228
+ }
229
+
230
+ /* ─── Dégroupage ───────────────────────────────────────────────────────
231
+ * La primitive structurelle du module : un groupe qui rassemble deux ou trois
232
+ * cellules pour l'empilement mobile, puis s'efface (display: contents) et
233
+ * rend ses enfants DIRECTEMENT à la grille du tableau. C'est ce qui permet
234
+ * d'écrire le balisage une seule fois pour deux mises en page — sans lui, il
235
+ * faudrait dupliquer la rangée et les deux copies divergeraient.
236
+ *
237
+ * Quatre enveloppes de ce type dans la source, sur trois écrans indépendants :
238
+ * c'est une primitive, pas une astuce locale.
239
+ *
240
+ * ⚠️ Ce que .zv-unwrap NE fait PAS : la fiche client emploie le mouvement
241
+ * inverse (`contents` sur mobile, flex au-dessus du seuil) pour défaire ses
242
+ * colonnes en pile ordonnée. Cette classe-ci pose `contents` en base : elle
243
+ * ne peut pas exprimer ce cas, et le prétendre serait faux. */
244
+
245
+ .zv-unwrap {
246
+ display: contents;
247
+ }
248
+
249
+ /* ─── Liste sans colonnes ──────────────────────────────────────────────
250
+ * Même cadre que le tableau, mais rien à aligner : pas d'en-tête, pas de
251
+ * gabarit. Sert dès que les rangées n'ont pas de colonnes comparables. */
252
+
253
+ .zv-list {
254
+ border: 1px solid var(--line);
255
+ background: var(--white);
256
+ }
257
+
258
+ .zv-list__row {
259
+ display: flex;
260
+ align-items: center;
261
+ gap: 16px;
262
+ padding: 13px 18px;
263
+ border-bottom: 1px solid var(--hairline);
264
+ }
265
+
266
+ .zv-list__row:last-child {
267
+ border-bottom: 0;
268
+ }
269
+
270
+ /* Rangée « envoi en cours ».
271
+ * ⚠️ La source porte ici un papier CHAUD (#FDFCF8) : une température, pas un
272
+ * accent. Le paquet n'a pas de fond chaud, et le plus proche (--panel) est à
273
+ * 2 % du blanc de la liste : ce fond est, en pratique, INVISIBLE. Il est
274
+ * conservé comme rappel discret, mais le signal « quelque chose est en train
275
+ * de se passer » doit être porté par la jauge (.zv-progress) ou par le
276
+ * libellé de la rangée — surtout pas par ce fond. */
277
+ .zv-list__row--upload {
278
+ background: var(--panel);
279
+ }
280
+
281
+ .zv-list__main {
282
+ /* Colonne, et non bloc : le nom surmonte sa métadonnée. Sans direction
283
+ * explicite, les deux se suivaient sur une même ligne et la ligne de
284
+ * liste se lisait comme une phrase. */
285
+ display: flex;
286
+ flex-direction: column;
287
+ gap: 2px;
288
+ min-width: 0;
289
+ flex: 1 1 0%;
290
+ }
291
+
292
+ .zv-list__name {
293
+ overflow: hidden;
294
+ text-overflow: ellipsis;
295
+ white-space: nowrap;
296
+ font-size: var(--app);
297
+ font-weight: 500;
298
+ }
299
+
300
+ /* ⚠️ Aucun état interactif dans la source : ni survol de rangée, ni sélection,
301
+ * ni focus. Une liste de fichiers n'y est pas cliquable — si elle le devient,
302
+ * c'est .zv-row qu'il faut employer, pas cette rangée-ci. */
303
+
304
+ /* ─── Métadonnée mono ──────────────────────────────────────────────────
305
+ * Second registre mono du système, en BAS DE CASSE : taille de fichier, date,
306
+ * horodatage, compteur, latence. Ni capitales ni interlettrage — c'est
307
+ * exactement ce qui le distingue de .zv-tag et .zv-mono (capitales,
308
+ * 0,14–0,2em) : une date ou un nom de fichier passés en capitales espacées
309
+ * deviennent illisibles et s'alignent mal.
310
+ *
311
+ * tabular-nums pour la même raison qu'en .zv-cell--num : Space Grotesk est
312
+ * proportionnelle, et une colonne d'horodatages y danse d'une ligne à
313
+ * l'autre sans cette propriété. */
314
+
315
+ .zv-meta {
316
+ font-family: var(--font-mono);
317
+ font-size: var(--mono);
318
+ font-weight: 500;
319
+ font-variant-numeric: tabular-nums;
320
+ color: var(--muted);
321
+ }
322
+
323
+ /* La source échelonne 10,5 / 10 / 11px. Les deux premiers se rabattent sur le
324
+ * même palier du paquet (--mono, 10px) : la variante basse est re-fondée sur
325
+ * --mono-sm (9px, plancher du paquet) pour garder un écart perceptible.
326
+ * C'est une décision assumée, pas une mesure. */
327
+ .zv-meta--sm {
328
+ font-size: var(--mono-sm);
329
+ }
330
+
331
+ .zv-meta--md {
332
+ font-size: var(--mono-lg);
333
+ }
334
+
335
+ /* Métadonnée de second plan (taille d'un fichier en cours d'envoi, compteur
336
+ * d'en-tête) : elle recule d'un cran pour ne pas concurrencer le nom. */
337
+ .zv-meta--quiet {
338
+ color: var(--label);
339
+ }
340
+
341
+ /* Plusieurs métadonnées sur une même ligne : le retour à la ligne est autorisé
342
+ * dès l'origine, une date et une taille ne tiennent pas côte à côte à 320px. */
343
+ .zv-meta-row {
344
+ display: flex;
345
+ flex-wrap: wrap;
346
+ align-items: center;
347
+ column-gap: 10px;
348
+ row-gap: 4px;
349
+ }
350
+
351
+ /* ─── Clé-valeur ───────────────────────────────────────────────────────
352
+ * ⚠️ RÔLES REMIS DANS LE SENS DE LA SOURCE. La version précédente mettait la
353
+ * mono à droite et la césure à gauche ; la fiche client fait l'inverse : la
354
+ * CLÉ est en mono, en accent, et se coupe ; la VALEUR est en texte courant,
355
+ * en gris de label, et ne se coupe jamais. Inverser les deux change ce que
356
+ * l'œil balaie en premier dans une colonne de métadonnées. */
357
+
358
+ .zv-kv {
359
+ display: flex;
360
+ flex-direction: column;
361
+ gap: 8px;
362
+ font-size: var(--app-sm);
363
+ font-weight: 500;
364
+ color: var(--ink-2);
365
+ }
366
+
367
+ .zv-kv__row {
368
+ display: flex;
369
+ justify-content: space-between;
370
+ gap: 10px;
371
+ }
372
+
373
+ .zv-kv__key {
374
+ /* Une clé longue doit se couper plutôt que pousser la valeur hors de la
375
+ * carte : ici on préfère la césure à la troncature, un libellé amputé de sa
376
+ * fin ne se devine pas. */
377
+ min-width: 0;
378
+ overflow-wrap: break-word;
379
+ font-family: var(--font-mono);
380
+ font-size: var(--mono-lg);
381
+ font-weight: 500;
382
+ /* L'accent, parce que la clé est le point d'entrée de la ligne. Les alias
383
+ * sémantiques de citation (--cite-ink & co.) n'existent pas dans ce paquet :
384
+ * c'est --accent, directement. */
385
+ color: var(--accent);
386
+ }
387
+
388
+ .zv-kv__val {
389
+ white-space: nowrap;
390
+ font-variant-numeric: tabular-nums;
391
+ color: var(--label);
392
+ }
393
+
394
+ /* Variante en pied de carte : le filet supérieur détache le récapitulatif du
395
+ * contenu qu'il résume. Valeurs de la fiche client (mt 12 / pt 10 / corps
396
+ * 11px / graisse 400). Le corps 11px n'a pas de palier texte dans le paquet,
397
+ * dont l'échelle s'arrête à --app-sm (12px) : rabattu, et dit. */
398
+ .zv-kv--foot {
399
+ margin-top: 12px;
400
+ border-top: 1px solid var(--hairline);
401
+ padding-top: 10px;
402
+ font-size: var(--app-sm);
403
+ font-weight: 400;
404
+ color: var(--muted);
405
+ }
406
+
407
+ /* Variante en grille (métadonnées d'une source citée) : deux colonnes des DEUX
408
+ * côtés du seuil — ce sont quatre valeurs courtes, les empiler ferait une
409
+ * colonne inutilement longue. Le libellé passe au-dessus de la valeur. */
410
+ .zv-kv--grid {
411
+ display: grid;
412
+ grid-template-columns: repeat(2, minmax(0, 1fr));
413
+ column-gap: 14px;
414
+ row-gap: 8px;
415
+ font-size: var(--app-sm);
416
+ }
417
+
418
+ .zv-kv--grid .zv-kv__row {
419
+ display: block;
420
+ min-width: 0;
421
+ overflow-wrap: break-word;
422
+ }
423
+
424
+ /* En grille, la clé redevient un label de colonne : capitales, plus petit,
425
+ * gris de label — et non plus l'accent de la variante en ligne. */
426
+ .zv-kv--grid .zv-kv__key {
427
+ display: block;
428
+ margin-bottom: 2px;
429
+ font-size: var(--mono-sm);
430
+ font-weight: 600;
431
+ letter-spacing: 0.14em;
432
+ text-transform: uppercase;
433
+ color: var(--label);
434
+ }
435
+
436
+ /* La valeur peut se couper ici : dans une case de 50 % de largeur, un
437
+ * nowrap déborderait de sa colonne. */
438
+ .zv-kv--grid .zv-kv__val {
439
+ white-space: normal;
440
+ }
441
+
442
+ /* ─── Pastille d'initiales ─────────────────────────────────────────────
443
+ * CARRÉE, jamais ronde : le rond est un usage de réseau social, l'encre et le
444
+ * papier posent des blocs. La teinte dit la NATURE de l'entité, elle n'est pas
445
+ * décorative — la changer, c'est changer le sens.
446
+ *
447
+ * 9,5px dans la source. Le paquet ne descend sous 12px que sur son échelle
448
+ * mono : --mono-sm (9px) est employé ici comme MESURE, la pastille restant
449
+ * rendue dans la famille de texte héritée. */
450
+
451
+ .zv-avatar {
452
+ display: flex;
453
+ width: 26px;
454
+ height: 26px;
455
+ flex: none;
456
+ align-items: center;
457
+ justify-content: center;
458
+ font-size: var(--mono-sm);
459
+ font-weight: 600;
460
+ }
461
+
462
+ /* Puce d'organisation, dans le chrome : une taille en dessous pour ne pas
463
+ * peser plus que le nom qu'elle accompagne. */
464
+ .zv-avatar--sm {
465
+ width: 22px;
466
+ height: 22px;
467
+ }
468
+
469
+ .zv-avatar--accent {
470
+ background: var(--accent);
471
+ color: var(--white);
472
+ }
473
+
474
+ .zv-avatar--ink {
475
+ background: var(--ink-2);
476
+ color: var(--white);
477
+ }
478
+
479
+ /* Défaut (société, confrère) : sans aplat, pour que les pastilles pleines
480
+ * restent l'exception et gardent leur valeur de signal. */
481
+ .zv-avatar--soft {
482
+ border: 1px solid var(--line);
483
+ background: var(--hairline);
484
+ color: var(--ink-2);
485
+ }
486
+
487
+ /* Règles d'initiales, à respecter côté appelant. Il y en a DEUX, et les
488
+ * confondre change le rendu :
489
+ * — .zv-avatar (26px, entité du répertoire) : préfixes « M. | Me | SARL |
490
+ * SCI | SA » retirés, découpe sur [espace ou &], AUCUN repli ;
491
+ * — .zv-avatar--sm (22px, organisation dans le chrome) : deux mots au plus,
492
+ * mots-outils écartés (« cabinet », « étude », « office », « selarl »,
493
+ * « scp », « et », « de », « du », « des », « la », « le », « les »),
494
+ * repli sur « · » quand rien ne sort — sans ce repli, une pastille vide
495
+ * passe pour un défaut d'affichage.
496
+ * Prescrire la seconde pour la première change les initiales du répertoire. */
497
+
498
+ /* ─── Troncature ───────────────────────────────────────────────────────
499
+ * Deux comportements, jamais interchangeables : couper, ou revenir à la ligne. */
500
+
501
+ /* Couper : réservé à ce qui reste identifiable amputé (titres, noms d'entité)
502
+ * ET à ce qui est récupérable ailleurs. ⚠️ Ne marche QUE si le parent porte
503
+ * min-width: 0 (ou si la colonne est en minmax(0, 1fr)) : sans cela, la boîte
504
+ * ne descend pas sous son contenu, rien n'est coupé et la grille est poussée.
505
+ * Livré seul, c'est un utilitaire qui ne marche pas. */
506
+ .zv-trunc {
507
+ min-width: 0;
508
+ overflow: hidden;
509
+ text-overflow: ellipsis;
510
+ white-space: nowrap;
511
+ }
512
+
513
+ /* Revenir à la ligne : tout ce qui ne se devine pas coupé — adresses e-mail,
514
+ * noms de fichiers, jetons, messages d'erreur. Perdre la fin d'un message
515
+ * d'erreur, c'est perdre l'incident. */
516
+ .zv-break {
517
+ min-width: 0;
518
+ overflow-wrap: break-word;
519
+ }
520
+
521
+ /* ─── Étiquette de catégorie ───────────────────────────────────────────
522
+ * Étiquette NON porteuse d'alerte : nature d'entité, matière, mot-clé. À ne
523
+ * pas confondre avec le badge d'état (feedback.css), qui porte une couleur
524
+ * sémantique, ni avec .zv-tag (components.css : mono capitales, sans fond) qui est
525
+ * un kicker éditorial. Le fond hairline suffit : une catégorie n'a pas à
526
+ * crier.
527
+ *
528
+ * 10,5 puis 11px dans la source : deux micro-labels, rabattus sur --mono et
529
+ * --mono-lg pris comme mesures (la famille reste celle du texte). */
530
+
531
+ .zv-chip {
532
+ display: inline-block;
533
+ background: var(--hairline);
534
+ padding: 3px 9px;
535
+ font-size: var(--mono);
536
+ font-weight: 600;
537
+ color: var(--ink-2);
538
+ }
539
+
540
+ /* Valeur technique dans une étiquette (date normalisée, référence) : la mono
541
+ * sans capitales, comme .zv-meta. */
542
+ .zv-chip--mono {
543
+ font-family: var(--font-mono);
544
+ font-variant-numeric: tabular-nums;
545
+ }
546
+
547
+ /* Variante à contour : employée quand les étiquettes sont posées sur une carte
548
+ * blanche, où le fond hairline ne se détacherait plus assez. */
549
+ .zv-chip--outline {
550
+ border: 1px solid var(--line);
551
+ background: none;
552
+ padding: 4px 10px;
553
+ font-size: var(--mono-lg);
554
+ font-weight: 500;
555
+ }
556
+
557
+ .zv-chip-row {
558
+ display: flex;
559
+ flex-wrap: nowrap;
560
+ gap: 6px;
561
+ }
562
+
563
+ /* ─── Citation ─────────────────────────────────────────────────────────
564
+ * Référence cliquable, insérée dans le fil du texte ou groupée en pied de
565
+ * réponse ; elle ouvre la feuille de source. Fond, filet et encre sont ceux
566
+ * de l'accent (--accent-soft / --accent-line / --accent) : le paquet ne
567
+ * déclare pas d'alias sémantique de citation, et en inventer un ferait un
568
+ * token de plus qu'aucun test n'accepterait. */
569
+
570
+ .zv-cite {
571
+ display: inline-flex;
572
+ align-items: center;
573
+ gap: 5px;
574
+ max-width: 100%;
575
+ border: 1px solid var(--accent-line);
576
+ background: var(--accent-soft);
577
+ color: var(--accent);
578
+ padding: 2px 8px;
579
+ /* La puce est insérée au fil d'un paragraphe : sans ce recalage elle pend
580
+ * sous la ligne de base et hache l'interligne. */
581
+ vertical-align: 1px;
582
+ font-family: var(--font-mono);
583
+ font-size: var(--mono-lg);
584
+ font-weight: 500;
585
+ font-variant-numeric: tabular-nums;
586
+ /* nowrap l'emporte tant qu'on est au-dessus du seuil ; overflow-wrap est
587
+ * posé d'avance pour le passage en white-space: normal du bloc mobile. */
588
+ overflow-wrap: break-word;
589
+ white-space: nowrap;
590
+ cursor: pointer;
591
+ }
592
+
593
+ .zv-cite:hover {
594
+ border-color: var(--accent);
595
+ }
596
+
597
+ .zv-cite-row {
598
+ display: flex;
599
+ flex-wrap: wrap;
600
+ gap: 6px;
601
+ margin-top: 10px;
602
+ }
603
+
604
+ /* ⚠️ Aucun état « source déjà ouverte » : la puce ne se marque pas quand sa
605
+ * feuille est affichée, on ne sait pas laquelle on lit dès qu'il y en a
606
+ * plusieurs. À trancher, pas à combler au hasard.
607
+ * ⚠️ En production c'est un <span onClick> : inatteignable au clavier. Le
608
+ * rendre focusable côté appelant (<button>) suffit à lui donner le contour de
609
+ * base.css — c'est la correction à faire, pas un style à ajouter ici.
610
+ * ⚠️ Recoupe .zv-src (components.css : mono capitales, fond blanc, filet
611
+ * --accent-line, rayon 4px) : même rôle éditorial, aucune valeur commune. La
612
+ * version applicative est remplie et à angle vif. Fusion ou dédoublement
613
+ * assumé : à arbitrer, ce module ne tranche pas seul. */
614
+
615
+ /* ─── Vignette de format ───────────────────────────────────────────────
616
+ * ⚠️ HORS CHARTE, ET ASSUMÉ : --fmt-pdf et --fmt-docx sont le rouge Acrobat
617
+ * et le bleu Word, pas --err ni --accent. Deux relevés indépendants les codent
618
+ * à l'identique — c'est une intention (reconnaître le format d'un coup d'œil),
619
+ * pas une dérive ; tokens.css les déclare et n'en autorise pas d'autre usage.
620
+ * ⚠️ La source pose 7,5px, sous le plancher du paquet (--mono-sm, 9px).
621
+ * Rabattu sur ce plancher : la vignette grossit d'un pixel et demi, ce qui
622
+ * n'est PAS le relevé — à revoir à l'œil sur une rangée dense. Réservé à deux
623
+ * ou quatre caractères en capitales, jamais à du texte.
624
+ * L'interlettrage reste à 0,06em et non aux 0,14em du reste des capitales :
625
+ * sur quatre caractères dans 30px de large, 0,14em déborde de la vignette.
626
+ * Règle de choix relevée : DOCX si le type MIME contient « word », PDF sinon. */
627
+
628
+ .zv-filetype {
629
+ flex: none;
630
+ padding: 2px 5px;
631
+ text-align: center;
632
+ font-family: var(--font-mono);
633
+ font-size: var(--mono-sm);
634
+ font-weight: 700;
635
+ letter-spacing: 0.06em;
636
+ color: var(--white);
637
+ }
638
+
639
+ .zv-filetype--pdf {
640
+ background: var(--fmt-pdf);
641
+ }
642
+
643
+ .zv-filetype--docx {
644
+ background: var(--fmt-docx);
645
+ }
646
+
647
+ /* ─── Sous 768px ───────────────────────────────────────────────────────
648
+ * Point de rupture unique de la charte. Les lignes cessent d'être des lignes :
649
+ * une grille de six colonnes à 375px produit six colonnes illisibles, ou un
650
+ * débordement horizontal de la page. Le balisage, lui, ne change pas — c'est
651
+ * .zv-unwrap qui rend l'inversion possible.
652
+ *
653
+ * ⚠️ La source coupe à 1024px (lg de Tailwind), pas à 768 : entre les deux,
654
+ * l'application affichera des cartes là où la source affiche encore un
655
+ * tableau. C'est la charte qui tranche, mais les gabarits de colonnes ont été
656
+ * réglés pour 1024 et méritent une vérification à 800px. */
657
+
658
+ @media (max-width: 767px) {
659
+ /* Des intitulés de colonnes sans colonnes ne veulent rien dire : ce sont les
660
+ * valeurs qui reprennent leur étiquette (.zv-cell__label). */
661
+ .zv-table__head {
662
+ display: none;
663
+ }
664
+
665
+ /* stretch, pas flex-start : c'est ce que fait la source (aucun items-* au
666
+ * répertoire, items-stretch à l'import). Avec flex-start, tout enfant
667
+ * direct qui n'est pas une enveloppe se rétracte à sa largeur de contenu. */
668
+ .zv-row {
669
+ display: flex;
670
+ flex-direction: column;
671
+ align-items: stretch;
672
+ gap: 7px;
673
+ padding-block: 12px;
674
+ }
675
+
676
+ /* Le groupe redevient une boîte : ses deux ou trois cellules tiennent sur
677
+ * une même ligne au lieu de s'empiler chacune sur la sienne.
678
+ * flex-wrap: wrap n'est pas optionnel — deux des quatre enveloppes de la
679
+ * source passent à la ligne, et sans lui la rangée taille + date + statut
680
+ * déborde de la carte à 320px, c'est-à-dire exactement le défaut que le
681
+ * passage en carte devait supprimer.
682
+ * ⚠️ La gouttière est arbitrée : la source en emploie trois (6, 10, 12px).
683
+ * 10px est la valeur médiane et la plus fréquente, pas une mesure unique. */
684
+ .zv-unwrap {
685
+ display: flex;
686
+ flex-wrap: wrap;
687
+ align-items: center;
688
+ column-gap: 10px;
689
+ row-gap: 4px;
690
+ min-width: 0;
691
+ width: 100%;
692
+ }
693
+
694
+ .zv-cell__label {
695
+ display: inline-block;
696
+ }
697
+
698
+ /* La source ne pose PAS de marge sur l'étiquette : elle rend la CELLULE
699
+ * flex, intitulé à gauche et valeur à droite
700
+ * (tableau-de-bord/page.tsx:120). Sans cela, « TAILLE » vient se coller à
701
+ * « 2,4 Mo » et les deux se lisent comme un seul mot.
702
+ * `:has()` cible les seules cellules qui portent une étiquette — les
703
+ * autres gardent leur flux. */
704
+ .zv-cell:has(.zv-cell__label) {
705
+ display: flex;
706
+ align-items: center;
707
+ justify-content: space-between;
708
+ gap: 12px;
709
+ }
710
+
711
+ /* Inline, et pas inline-block : « 42 docs » doit rester un seul mot visuel
712
+ * collé à sa valeur. */
713
+ .zv-cell__unit {
714
+ display: inline;
715
+ }
716
+
717
+ .zv-list__row {
718
+ flex-direction: column;
719
+ align-items: stretch;
720
+ gap: 8px;
721
+ padding: 12px 14px;
722
+ }
723
+
724
+ /* Clé et valeur empilées : côte à côte à 320px, elles se chevauchent dès que
725
+ * le libellé dépasse trois mots. */
726
+ .zv-kv__row {
727
+ flex-direction: column;
728
+ gap: 2px;
729
+ }
730
+
731
+ /* Un libellé long ne tient pas sur une ligne à cette largeur : les
732
+ * étiquettes passent en plusieurs rangées plutôt que de déborder. */
733
+ .zv-chip-row {
734
+ flex-wrap: wrap;
735
+ }
736
+
737
+ /* Une référence complète ne tient pas en une ligne sur un écran de 320px :
738
+ * sans césure autorisée, la puce déborde de sa colonne. */
739
+ .zv-cite {
740
+ white-space: normal;
741
+ }
742
+ }