stackhelx 1.0.0__py3-none-any.whl

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.
stackhelx/web/app.css ADDED
@@ -0,0 +1,1729 @@
1
+ /* Hallmark · macrostructure: Index-First · tone: utilitario con contencion editorial
2
+ * genre: editorial · theme: custom "Ledger"
3
+ * vibe: "papel de cuaderno, tinta calida, una sola arcilla"
4
+ * paper oklch(96.5% 0.008 85) · accent oklch(56% 0.13 42) warm
5
+ * display: serif del sistema (Georgia) · body: sans nativa · data: mono
6
+ * axes: light paper / roman-serif display / warm accent
7
+ * pre-emit critique: P5 H5 E4 S5 R5 V4
8
+ *
9
+ * Sin webfonts a proposito: la CSP del servidor es default-src 'self' y una
10
+ * herramienta local que le pide fuentes a un CDN en cada carga esta peor
11
+ * diseñada que una que usa lo que ya tenes instalado. Georgia en grande, con
12
+ * tracking cerrado, tiene mas caracter que cualquier sans descargada.
13
+ */
14
+
15
+ @import url("tokens.css");
16
+
17
+ *,
18
+ *::before,
19
+ *::after {
20
+ box-sizing: border-box;
21
+ }
22
+
23
+ html,
24
+ body {
25
+ overflow-x: clip;
26
+ margin: 0;
27
+ }
28
+
29
+ html {
30
+ -webkit-text-size-adjust: 100%;
31
+ }
32
+
33
+ body {
34
+ background: var(--color-paper);
35
+ color: var(--color-ink);
36
+ font-family: var(--font-body);
37
+ font-size: var(--text-base);
38
+ line-height: 1.55;
39
+ min-height: 100vh;
40
+ display: flex;
41
+ flex-direction: column;
42
+ }
43
+
44
+ ::selection {
45
+ background: var(--color-accent-soft);
46
+ }
47
+
48
+ :focus-visible {
49
+ outline: 2px solid var(--color-accent);
50
+ outline-offset: 2px;
51
+ /* Sin transicion: el anillo tiene que aparecer en el instante del foco. */
52
+ }
53
+
54
+ /* masthead ---------------------------------------------------------------- */
55
+
56
+ .masthead {
57
+ display: flex;
58
+ flex-wrap: wrap;
59
+ align-items: baseline;
60
+ justify-content: space-between;
61
+ gap: var(--space-sm) var(--space-lg);
62
+ padding: var(--space-lg) var(--space-gutter) var(--space-md);
63
+ border-bottom: var(--rule) solid var(--color-rule-strong);
64
+ }
65
+
66
+ .wordmark {
67
+ font-family: var(--font-display);
68
+ font-size: var(--text-xl);
69
+ font-weight: 400;
70
+ letter-spacing: -0.022em;
71
+ margin: 0;
72
+ }
73
+
74
+ .masthead__status {
75
+ font-family: var(--font-mono);
76
+ font-size: var(--text-xs);
77
+ color: var(--color-ink-3);
78
+ margin: 0;
79
+ }
80
+
81
+ .masthead__status[data-down="true"] {
82
+ color: var(--color-bad);
83
+ }
84
+
85
+ /* Lo unico global de la pagina que habla de salud y no de inventario. Aparece
86
+ solo cuando hay algo caido: el resto del tiempo no ocupa lugar. */
87
+ .masthead__health {
88
+ font-family: var(--font-mono);
89
+ font-size: var(--text-xs);
90
+ color: var(--color-bad);
91
+ margin: 0;
92
+ }
93
+
94
+ /* `display` propio le gana a la regla del navegador para [hidden]. */
95
+ .masthead__health[hidden],
96
+ .masthead__notify[hidden] {
97
+ display: none;
98
+ }
99
+
100
+ /* sheet ------------------------------------------------------------------- */
101
+
102
+ .sheet {
103
+ flex: 1;
104
+ width: 100%;
105
+ max-width: 60rem;
106
+ margin: 0 auto;
107
+ padding: var(--space-xl) var(--space-gutter) var(--space-2xl);
108
+ }
109
+
110
+ .lede {
111
+ max-width: 42ch;
112
+ margin: 0 0 var(--space-xl);
113
+ color: var(--color-ink-2);
114
+ }
115
+
116
+ .empty {
117
+ max-width: 52ch;
118
+ color: var(--color-ink-2);
119
+ border-top: var(--rule) solid var(--color-rule);
120
+ padding-top: var(--space-lg);
121
+ }
122
+
123
+ .empty code {
124
+ font-family: var(--font-mono);
125
+ font-size: 0.9em;
126
+ color: var(--color-ink);
127
+ }
128
+
129
+ /* buscador y paginado ----------------------------------------------------- */
130
+
131
+ /* `display: flex` le gana a la regla del navegador para [hidden]: sin esto, el
132
+ buscador y el paginador se ven siempre. */
133
+ .find[hidden],
134
+ .pager[hidden] {
135
+ display: none;
136
+ }
137
+
138
+ /* buscador y filtros estilo Raycast/Vercel -------------------------------- */
139
+
140
+ .find {
141
+ display: flex;
142
+ flex-direction: column;
143
+ gap: var(--space-sm);
144
+ margin-bottom: var(--space-lg);
145
+ padding: var(--space-sm);
146
+ background: var(--color-paper-2);
147
+ border: var(--rule) solid var(--color-rule-strong);
148
+ border-radius: var(--radius);
149
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
150
+ }
151
+
152
+ .find__bar {
153
+ display: flex;
154
+ align-items: center;
155
+ gap: var(--space-sm);
156
+ padding: 0.4rem 0.75rem;
157
+ background: var(--color-paper);
158
+ border: var(--rule) solid var(--color-rule-strong);
159
+ border-radius: var(--radius);
160
+ transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
161
+ }
162
+
163
+ .find__bar:focus-within {
164
+ border-color: var(--color-ink-2);
165
+ box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.08);
166
+ }
167
+
168
+ .find__icon {
169
+ inline-size: 1.1rem;
170
+ block-size: 1.1rem;
171
+ flex: none;
172
+ color: var(--color-ink-3);
173
+ }
174
+
175
+ .find__input {
176
+ flex: 1 1 auto;
177
+ min-width: 0;
178
+ font-family: var(--font-body);
179
+ font-size: var(--text-sm);
180
+ color: var(--color-ink);
181
+ background: transparent;
182
+ border: none;
183
+ outline: none;
184
+ padding: 0.2rem 0;
185
+ }
186
+
187
+ .find__input::placeholder {
188
+ color: var(--color-ink-3);
189
+ }
190
+
191
+ .kbd {
192
+ font-family: var(--font-mono);
193
+ font-size: 0.7rem;
194
+ color: var(--color-ink-3);
195
+ background: var(--color-paper-3);
196
+ border: var(--rule) solid var(--color-rule);
197
+ border-radius: 4px;
198
+ padding: 0.15rem 0.45rem;
199
+ line-height: 1;
200
+ }
201
+
202
+ .find__count {
203
+ font-family: var(--font-mono);
204
+ font-size: var(--text-xs);
205
+ color: var(--color-ink-3);
206
+ white-space: nowrap;
207
+ }
208
+
209
+ /* filter chips */
210
+
211
+ .filter-chips {
212
+ display: flex;
213
+ flex-wrap: wrap;
214
+ align-items: center;
215
+ gap: var(--space-xs);
216
+ }
217
+
218
+ .chip {
219
+ display: inline-flex;
220
+ align-items: center;
221
+ gap: 0.45rem;
222
+ font-family: var(--font-body);
223
+ font-size: var(--text-xs);
224
+ font-weight: 500;
225
+ color: var(--color-ink-2);
226
+ background: var(--color-paper);
227
+ border: var(--rule) solid var(--color-rule-strong);
228
+ border-radius: 9999px;
229
+ padding: 0.3rem 0.8rem;
230
+ cursor: pointer;
231
+ white-space: nowrap;
232
+ user-select: none;
233
+ transition:
234
+ background-color 160ms cubic-bezier(0.23, 1, 0.32, 1),
235
+ color 160ms cubic-bezier(0.23, 1, 0.32, 1),
236
+ border-color 160ms cubic-bezier(0.23, 1, 0.32, 1),
237
+ transform 160ms cubic-bezier(0.23, 1, 0.32, 1);
238
+ }
239
+
240
+ .chip:hover:not(:disabled) {
241
+ background: var(--color-paper-3);
242
+ color: var(--color-ink);
243
+ border-color: var(--color-ink-3);
244
+ }
245
+
246
+ .chip:active:not(:disabled) {
247
+ transform: scale(0.95);
248
+ }
249
+
250
+ .chip--active {
251
+ background: var(--color-ink);
252
+ color: var(--color-paper);
253
+ border-color: var(--color-ink);
254
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
255
+ }
256
+
257
+ .chip--active:hover:not(:disabled) {
258
+ background: var(--color-ink);
259
+ color: var(--color-paper);
260
+ border-color: var(--color-ink);
261
+ }
262
+
263
+ .chip .dot {
264
+ inline-size: 0.45rem;
265
+ block-size: 0.45rem;
266
+ }
267
+
268
+ .pager {
269
+ display: flex;
270
+ align-items: center;
271
+ gap: var(--space-md);
272
+ margin-top: var(--space-md);
273
+ }
274
+
275
+ .pager__at {
276
+ font-family: var(--font-mono);
277
+ font-size: var(--text-xs);
278
+ color: var(--color-ink-3);
279
+ }
280
+
281
+ /* index de proyectos y acordeon ------------------------------------------- */
282
+
283
+ .index {
284
+ display: grid;
285
+ gap: var(--space-md);
286
+ }
287
+
288
+ .project {
289
+ border: var(--rule) solid var(--color-rule-strong);
290
+ border-radius: var(--radius);
291
+ padding: var(--space-md) var(--space-lg);
292
+ background: var(--color-paper-2);
293
+ transition: border-color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
294
+ }
295
+
296
+ .project:hover {
297
+ border-color: var(--color-ink-3);
298
+ }
299
+
300
+ .project__head {
301
+ display: flex;
302
+ flex-wrap: wrap;
303
+ align-items: center;
304
+ justify-content: space-between;
305
+ gap: var(--space-sm) var(--space-md);
306
+ }
307
+
308
+ .project__title {
309
+ display: flex;
310
+ align-items: center;
311
+ gap: var(--space-md);
312
+ }
313
+
314
+ .project__name {
315
+ font-family: var(--font-display);
316
+ font-size: var(--text-lg);
317
+ font-weight: 400;
318
+ letter-spacing: -0.018em;
319
+ margin: 0;
320
+ overflow-wrap: anywhere;
321
+ }
322
+
323
+ .project__head-actions {
324
+ display: flex;
325
+ flex-wrap: wrap;
326
+ align-items: center;
327
+ gap: var(--space-sm);
328
+ margin-left: auto;
329
+ }
330
+
331
+ .project__toggle {
332
+ display: inline-flex;
333
+ align-items: center;
334
+ gap: 0.35rem;
335
+ }
336
+
337
+ .chevron {
338
+ display: inline-block;
339
+ font-size: 0.65rem;
340
+ transition: transform 220ms cubic-bezier(0.23, 1, 0.32, 1);
341
+ }
342
+
343
+ .project[data-expanded="true"] .chevron {
344
+ transform: rotate(180deg);
345
+ }
346
+
347
+ .project__details {
348
+ display: grid;
349
+ grid-template-rows: 0fr;
350
+ transition: grid-template-rows 250ms cubic-bezier(0.23, 1, 0.32, 1), opacity 200ms ease,
351
+ visibility 250ms;
352
+ opacity: 0;
353
+ /* `opacity: 0` no saca nada del orden de tabulacion ni del arbol de
354
+ accesibilidad: con la ficha cerrada se tabulaba a ciegas hasta "Quitar
355
+ proyecto". `visibility` si, y transiciona en discreto, asi que al abrir
356
+ aparece en el instante 0 y al cerrar aguanta hasta el final de la
357
+ animacion. */
358
+ visibility: hidden;
359
+ }
360
+
361
+ .project[data-expanded="true"] .project__details {
362
+ grid-template-rows: 1fr;
363
+ opacity: 1;
364
+ visibility: visible;
365
+ margin-top: var(--space-md);
366
+ padding-top: var(--space-md);
367
+ border-top: var(--rule) solid var(--color-rule);
368
+ }
369
+
370
+ .project__body {
371
+ overflow: hidden;
372
+ }
373
+
374
+ .project__path-wrapper {
375
+ display: flex;
376
+ align-items: center;
377
+ justify-content: space-between;
378
+ flex-wrap: wrap;
379
+ gap: var(--space-xs) var(--space-sm);
380
+ margin: 0.15rem 0 0;
381
+ }
382
+
383
+ .project__path {
384
+ font-family: var(--font-mono);
385
+ font-size: var(--text-xs);
386
+ color: var(--color-ink-3);
387
+ margin: 0;
388
+ overflow-wrap: anywhere;
389
+ flex: 1 1 auto;
390
+ }
391
+
392
+ .project__path-actions {
393
+ display: inline-flex;
394
+ align-items: center;
395
+ gap: var(--space-xs);
396
+ flex-shrink: 0;
397
+ }
398
+
399
+ .project__path-copy {
400
+ font-size: var(--text-xs);
401
+ padding: 0.2rem 0.5rem;
402
+ color: var(--color-ink-3);
403
+ border-color: transparent;
404
+ transition: color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
405
+ }
406
+
407
+ .project__path-copy:hover {
408
+ color: var(--color-ink);
409
+ background: var(--color-paper-3);
410
+ }
411
+
412
+ .editor-select-wrap {
413
+ display: inline-flex;
414
+ align-items: center;
415
+ }
416
+
417
+ .editor__select {
418
+ font-family: var(--font-body);
419
+ font-size: var(--text-xs);
420
+ color: var(--color-ink-2);
421
+ background: var(--color-paper-2);
422
+ border: var(--rule) solid var(--color-rule-strong);
423
+ border-radius: var(--radius);
424
+ padding: 0.2rem 0.5rem;
425
+ cursor: pointer;
426
+ transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
427
+ }
428
+
429
+ .editor__select:hover {
430
+ color: var(--color-ink);
431
+ border-color: var(--color-ink-3);
432
+ }
433
+
434
+ .editor__select:focus-visible {
435
+ outline: none;
436
+ border-color: var(--color-accent);
437
+ }
438
+
439
+
440
+ .project__error {
441
+ font-size: var(--text-sm);
442
+ color: var(--color-bad);
443
+ margin: var(--space-sm) 0 0;
444
+ padding-left: var(--space-sm);
445
+ border-left: 2px solid var(--color-bad);
446
+ }
447
+
448
+ /* estado ------------------------------------------------------------------ */
449
+
450
+ .state {
451
+ display: inline-flex;
452
+ align-items: center;
453
+ gap: 0.45em;
454
+ font-size: var(--text-xs);
455
+ font-family: var(--font-mono);
456
+ color: var(--color-ink-2);
457
+ margin: 0;
458
+ white-space: nowrap;
459
+ }
460
+
461
+ .dot {
462
+ inline-size: 0.5rem;
463
+ block-size: 0.5rem;
464
+ border-radius: 50%;
465
+ background: var(--color-ink-3);
466
+ flex: none;
467
+ }
468
+
469
+ /* El color nunca lleva el significado solo: siempre va con su etiqueta. */
470
+ [data-tone="ready"] .dot,
471
+ .dot[data-tone="ready"] {
472
+ background: var(--color-ok);
473
+ }
474
+ [data-tone="starting"] .dot,
475
+ .dot[data-tone="starting"] {
476
+ background: var(--color-warn);
477
+ animation: dot-pulse 1.4s ease-in-out infinite;
478
+ }
479
+
480
+ @keyframes dot-pulse {
481
+ 0%,
482
+ 100% {
483
+ opacity: 1;
484
+ transform: scale(1);
485
+ }
486
+ 50% {
487
+ opacity: 0.4;
488
+ transform: scale(0.85);
489
+ }
490
+ }
491
+ [data-tone="warn"] .dot,
492
+ .dot[data-tone="warn"] {
493
+ background: var(--color-warn);
494
+ }
495
+ [data-tone="bad"] .dot,
496
+ .dot[data-tone="bad"] {
497
+ background: var(--color-bad);
498
+ }
499
+
500
+ /* servicios --------------------------------------------------------------- */
501
+
502
+ .services {
503
+ list-style: none;
504
+ margin: var(--space-md) 0 0;
505
+ padding: 0;
506
+ }
507
+
508
+ /* Tabla, no fila estirada: las columnas van juntas para que el ojo lea
509
+ "api 8080 listo" como una unidad y no como tres datos sueltos. */
510
+ .service {
511
+ display: grid;
512
+ grid-template-columns: minmax(0, 8rem) 4rem minmax(0, 1fr) auto auto;
513
+ align-items: center;
514
+ gap: var(--space-sm) var(--space-md);
515
+ padding: var(--space-xs) var(--space-sm);
516
+ margin-inline: calc(var(--space-sm) * -1);
517
+ border-radius: var(--radius);
518
+ transition: background-color var(--dur-fast) var(--ease-out);
519
+ }
520
+
521
+ .service:hover {
522
+ background: var(--color-paper-3);
523
+ }
524
+
525
+ .service__name {
526
+ display: flex;
527
+ align-items: center;
528
+ gap: 0.5em;
529
+ min-width: 0;
530
+ font-size: var(--text-sm);
531
+ }
532
+
533
+ .service__label {
534
+ overflow-wrap: anywhere;
535
+ }
536
+
537
+ /* El icono dice contenedor o proceso local, que es lo unico que se sabe del rol.
538
+ Nunca va solo: el nombre del servicio esta al lado. */
539
+ .service__icon {
540
+ inline-size: 0.95rem;
541
+ block-size: 0.95rem;
542
+ flex: none;
543
+ fill: none;
544
+ stroke: var(--color-ink-3);
545
+ stroke-width: 1.25;
546
+ stroke-linecap: round;
547
+ stroke-linejoin: round;
548
+ }
549
+
550
+ .service__port,
551
+ .service__state {
552
+ font-family: var(--font-mono);
553
+ font-size: var(--text-xs);
554
+ font-variant-numeric: tabular-nums;
555
+ color: var(--color-ink-2);
556
+ }
557
+
558
+ .service__port {
559
+ text-align: right;
560
+ }
561
+
562
+ /* Un puerto que otro proyecto registrado tambien declara. No es rojo: todavia
563
+ no rompio nada, y conviven mientras no corran a la vez. */
564
+ .service__shared,
565
+ .service__taken {
566
+ color: var(--color-warn);
567
+ cursor: help;
568
+ }
569
+
570
+ /* Unica accion en acento del index: abrir lo que esta corriendo es lo que mas se
571
+ hace desde aca, y es lo unico que se lleva un color. */
572
+ .btn--open {
573
+ font-size: var(--text-xs);
574
+ padding: 0.3rem 0.6rem;
575
+ color: var(--color-accent);
576
+ border-color: var(--color-accent);
577
+ text-decoration: none;
578
+ }
579
+
580
+ .btn--open:hover {
581
+ background: var(--color-accent-soft);
582
+ border-color: var(--color-accent);
583
+ }
584
+
585
+ /* Badge: el estado se lee como una unidad y no como texto suelto. El color va
586
+ siempre con la etiqueta y con el punto, nunca solo. */
587
+ .service__state {
588
+ display: inline-flex;
589
+ align-items: center;
590
+ gap: 0.45em;
591
+ justify-self: start;
592
+ max-inline-size: 100%;
593
+ min-width: 0;
594
+ padding: 0.15rem 0.5rem;
595
+ border-radius: 999px;
596
+ /* No paper-3: es el mismo fondo que toma la fila al pasar el mouse y el badge
597
+ se disolveria justo cuando lo estas mirando. */
598
+ background: var(--color-rule);
599
+ }
600
+
601
+ .service__state[data-tone="ready"] {
602
+ color: var(--color-ok-ink);
603
+ background: color-mix(in oklch, var(--color-ok) 14%, transparent);
604
+ }
605
+
606
+ .service__state[data-tone="starting"] {
607
+ color: var(--color-warn-ink);
608
+ background: color-mix(in oklch, var(--color-warn) 16%, transparent);
609
+ }
610
+
611
+ /* Ni verde ni alarma: el puerto esta tomado, y por algo que no es un problema.
612
+ No reusa "starting" porque ese pulsa, y aca no hay nada en transicion. */
613
+ .service__state[data-tone="warn"] {
614
+ color: var(--color-warn-ink);
615
+ background: color-mix(in oklch, var(--color-warn) 14%, transparent);
616
+ }
617
+
618
+ .service__state[data-tone="bad"] {
619
+ color: var(--color-bad-ink);
620
+ background: var(--color-bad-soft);
621
+ }
622
+
623
+ .service__state span:last-child {
624
+ overflow: hidden;
625
+ text-overflow: ellipsis;
626
+ white-space: nowrap;
627
+ }
628
+
629
+ .service__metrics {
630
+ font-family: var(--font-mono);
631
+ font-size: var(--text-xs);
632
+ color: var(--color-ink-2);
633
+ padding: 0.15rem 0.4rem;
634
+ background: var(--color-paper-3);
635
+ border-radius: var(--radius-sm);
636
+ white-space: nowrap;
637
+ align-self: center;
638
+ }
639
+
640
+ .service__act {
641
+ justify-self: start;
642
+ }
643
+
644
+ /* acciones ---------------------------------------------------------------- */
645
+
646
+ .project__actions {
647
+ display: flex;
648
+ flex-wrap: wrap;
649
+ align-items: center;
650
+ gap: var(--space-sm);
651
+ margin-top: var(--space-md);
652
+ }
653
+
654
+ .project__drop {
655
+ margin-left: auto;
656
+ color: var(--color-ink-3);
657
+ }
658
+
659
+ /* El segundo paso de congelar: el boton dice que archivo va a escribir y se
660
+ desarma solo. Sin color de peligro, porque no destruye nada. */
661
+ .project__freeze[data-armed] {
662
+ color: var(--color-ink);
663
+ border-color: var(--color-ink-3);
664
+ }
665
+
666
+ .btn {
667
+ font-family: var(--font-body);
668
+ font-size: var(--text-sm);
669
+ line-height: 1;
670
+ color: var(--color-ink);
671
+ background: var(--color-paper-2);
672
+ border: var(--rule) solid var(--color-rule-strong);
673
+ border-radius: var(--radius);
674
+ padding: 0.55rem 0.85rem;
675
+ cursor: pointer;
676
+ white-space: nowrap;
677
+ transition:
678
+ background-color var(--dur-fast) var(--ease-out),
679
+ border-color var(--dur-fast) var(--ease-out);
680
+ }
681
+
682
+ .btn:hover:not(:disabled):not([aria-disabled="true"]) {
683
+ background: var(--color-paper-3);
684
+ border-color: var(--color-ink-3);
685
+ }
686
+
687
+ .btn:active:not(:disabled):not([aria-disabled="true"]) {
688
+ background: var(--color-rule);
689
+ }
690
+
691
+ .btn:disabled {
692
+ color: var(--color-ink-3);
693
+ background: transparent;
694
+ border-color: var(--color-rule);
695
+ cursor: not-allowed;
696
+ }
697
+
698
+ .btn[data-busy="true"] {
699
+ color: transparent;
700
+ cursor: progress;
701
+ pointer-events: none;
702
+ position: relative;
703
+ }
704
+
705
+ .btn[data-busy="true"]::after {
706
+ content: "";
707
+ position: absolute;
708
+ inset: 0;
709
+ margin: auto;
710
+ width: 1em;
711
+ height: 1em;
712
+ border: 2px solid var(--color-rule-strong);
713
+ border-top-color: var(--color-ink);
714
+ border-radius: 50%;
715
+ animation: spin 0.6s linear infinite;
716
+ }
717
+
718
+ @keyframes spin {
719
+ to { transform: rotate(360deg); }
720
+ }
721
+
722
+ .btn[aria-disabled="true"] {
723
+ color: var(--color-ink-3);
724
+ background: transparent;
725
+ border-color: var(--color-rule);
726
+ cursor: not-allowed;
727
+ }
728
+
729
+ .btn--primary {
730
+ background: var(--color-ink);
731
+ color: var(--color-paper);
732
+ border-color: var(--color-ink);
733
+ }
734
+
735
+ .btn--primary:hover:not(:disabled):not([aria-disabled="true"]) {
736
+ background: var(--color-accent);
737
+ border-color: var(--color-accent);
738
+ }
739
+
740
+ .btn--quiet {
741
+ background: transparent;
742
+ border-color: transparent;
743
+ padding-inline: 0.5rem;
744
+ }
745
+
746
+ .btn--sm {
747
+ font-size: var(--text-xs);
748
+ padding: 0.35rem 0.55rem;
749
+ }
750
+
751
+ .btn--quiet:hover:not(:disabled):not([aria-disabled="true"]) {
752
+ background: var(--color-paper-3);
753
+ border-color: transparent;
754
+ }
755
+
756
+ .btn--kill {
757
+ font-family: var(--font-mono);
758
+ font-size: var(--text-xs);
759
+ padding: 0.3rem 0.55rem;
760
+ color: var(--color-bad);
761
+ border-color: var(--color-bad-rule);
762
+ }
763
+
764
+ .btn--kill:hover:not(:disabled):not([aria-disabled="true"]) {
765
+ background: var(--color-bad-soft);
766
+ border-color: var(--color-bad);
767
+ }
768
+
769
+ .btn--alt-port {
770
+ font-family: var(--font-mono);
771
+ font-size: var(--text-xs);
772
+ padding: 0.3rem 0.55rem;
773
+ color: var(--color-ink-2);
774
+ border-color: var(--color-rule-strong);
775
+ }
776
+
777
+ .btn--alt-port:hover:not(:disabled):not([aria-disabled="true"]) {
778
+ background: var(--color-paper-2);
779
+ color: var(--color-ink);
780
+ border-color: var(--color-ink-2);
781
+ }
782
+
783
+ .profile {
784
+ display: inline-flex;
785
+ align-items: center;
786
+ gap: 0.5rem;
787
+ }
788
+
789
+ .profile[hidden] {
790
+ display: none;
791
+ }
792
+
793
+ .profile__label {
794
+ font-size: var(--text-xs);
795
+ color: var(--color-ink-3);
796
+ }
797
+
798
+ .profile__select {
799
+ font-family: var(--font-body);
800
+ font-size: var(--text-sm);
801
+ color: var(--color-ink);
802
+ background: var(--color-paper-2);
803
+ border: var(--rule) solid var(--color-rule-strong);
804
+ border-radius: var(--radius);
805
+ padding: 0.5rem 0.5rem;
806
+ }
807
+
808
+ /* logs -------------------------------------------------------------------- */
809
+
810
+ .logs__box {
811
+ margin-top: var(--space-md);
812
+ }
813
+
814
+ .logs__toolbar {
815
+ display: flex;
816
+ gap: var(--space-xs);
817
+ align-items: center;
818
+ margin-bottom: var(--space-xs);
819
+ }
820
+
821
+ .logs__filter {
822
+ flex: 1;
823
+ box-sizing: border-box;
824
+ font-family: var(--font-mono);
825
+ font-size: var(--text-xs);
826
+ color: var(--color-ink);
827
+ background: var(--color-paper-2);
828
+ border: var(--rule) solid var(--color-rule);
829
+ border-radius: var(--radius);
830
+ padding: 0.4rem 0.6rem;
831
+ }
832
+
833
+ .logs__live {
834
+ font-family: var(--font-mono);
835
+ font-size: var(--text-xs);
836
+ color: var(--color-good);
837
+ background: color-mix(in oklch, var(--color-good) 12%, transparent);
838
+ border: var(--rule) solid var(--color-good);
839
+ padding: 0.25rem 0.5rem;
840
+ border-radius: var(--radius-sm);
841
+ display: inline-flex;
842
+ align-items: center;
843
+ gap: 0.35rem;
844
+ white-space: nowrap;
845
+ }
846
+
847
+ .logs__live::before {
848
+ content: "";
849
+ display: inline-block;
850
+ width: 6px;
851
+ height: 6px;
852
+ border-radius: 50%;
853
+ background: var(--color-good);
854
+ box-shadow: 0 0 4px var(--color-good);
855
+ }
856
+
857
+ .logs {
858
+ font-family: var(--font-mono);
859
+ font-size: var(--text-xs);
860
+ line-height: 1.6;
861
+ color: var(--color-ink-2);
862
+ background: var(--color-paper-2);
863
+ border: var(--rule) solid var(--color-rule);
864
+ border-radius: var(--radius);
865
+ margin: 0;
866
+ padding: var(--space-md);
867
+ max-block-size: 22rem;
868
+ overflow: auto;
869
+ overscroll-behavior: contain;
870
+ overflow-wrap: anywhere;
871
+ }
872
+
873
+ .history__table {
874
+ width: 100%;
875
+ border-collapse: collapse;
876
+ font-size: var(--text-xs);
877
+ text-align: left;
878
+ }
879
+
880
+ .history__table th,
881
+ .history__table td {
882
+ padding: var(--space-xs) var(--space-sm);
883
+ border-bottom: var(--rule) solid var(--color-rule-strong);
884
+ }
885
+
886
+ .history__table th {
887
+ font-weight: 500;
888
+ color: var(--color-ink-2);
889
+ }
890
+
891
+ .history__table td {
892
+ color: var(--color-ink);
893
+ white-space: pre-line;
894
+ }
895
+
896
+ .history__table tr:last-child td {
897
+ border-bottom: 0;
898
+ }
899
+
900
+ .chip--ports {
901
+ margin-left: auto;
902
+ }
903
+
904
+ /* grafo de dependencias --------------------------------------------------- */
905
+
906
+ .graph__box {
907
+ margin-top: var(--space-sm);
908
+ background: var(--color-paper-2);
909
+ border: var(--rule) solid var(--color-rule);
910
+ border-radius: var(--radius);
911
+ padding: var(--space-sm);
912
+ overflow: hidden;
913
+ }
914
+
915
+ .graph__header {
916
+ display: flex;
917
+ align-items: center;
918
+ justify-content: space-between;
919
+ margin-bottom: var(--space-xs);
920
+ }
921
+
922
+ .graph__title {
923
+ font-size: var(--text-xs);
924
+ font-family: var(--font-mono);
925
+ color: var(--color-ink-3);
926
+ text-transform: uppercase;
927
+ letter-spacing: 0.05em;
928
+ }
929
+
930
+ .graph__canvas-wrapper {
931
+ overflow-x: auto;
932
+ padding: var(--space-xs) 0;
933
+ }
934
+
935
+ .graph__svg {
936
+ display: block;
937
+ margin: 0 auto;
938
+ overflow: visible;
939
+ }
940
+
941
+ /* auditor seguro de entorno (.env) --------------------------------------- */
942
+
943
+ .env__box {
944
+ margin-top: var(--space-sm);
945
+ background: var(--color-paper-2);
946
+ border: var(--rule) solid var(--color-rule);
947
+ border-radius: var(--radius);
948
+ padding: var(--space-sm);
949
+ font-size: var(--text-sm);
950
+ }
951
+
952
+ .env__header {
953
+ display: flex;
954
+ align-items: center;
955
+ gap: var(--space-xs);
956
+ margin-bottom: var(--space-xs);
957
+ }
958
+
959
+ .env__status-badge {
960
+ font-size: var(--text-xs);
961
+ font-family: var(--font-mono);
962
+ padding: 2px 6px;
963
+ border-radius: var(--radius-sm);
964
+ font-weight: 600;
965
+ text-transform: uppercase;
966
+ letter-spacing: 0.04em;
967
+ }
968
+
969
+ .env__status-badge--ok {
970
+ background: color-mix(in oklch, var(--color-ok) 15%, transparent);
971
+ color: var(--color-ok-ink);
972
+ border: var(--rule) solid color-mix(in oklch, var(--color-ok) 30%, transparent);
973
+ }
974
+
975
+ .env__status-badge--warn {
976
+ background: color-mix(in oklch, var(--color-warn) 18%, transparent);
977
+ color: var(--color-warn-ink);
978
+ border: var(--rule) solid color-mix(in oklch, var(--color-warn) 35%, transparent);
979
+ }
980
+
981
+ .env__summary {
982
+ color: var(--color-ink-2);
983
+ font-size: var(--text-xs);
984
+ }
985
+
986
+ .env__details {
987
+ display: flex;
988
+ flex-direction: column;
989
+ gap: var(--space-xs);
990
+ margin-top: var(--space-xs);
991
+ }
992
+
993
+ .env__section-title {
994
+ display: block;
995
+ font-size: var(--text-xs);
996
+ color: var(--color-ink-2);
997
+ font-weight: 500;
998
+ margin-bottom: 2px;
999
+ }
1000
+
1001
+ .env__list {
1002
+ list-style: none;
1003
+ padding: 0;
1004
+ margin: 0;
1005
+ display: flex;
1006
+ flex-wrap: wrap;
1007
+ gap: 4px;
1008
+ }
1009
+
1010
+ .env__key-pill {
1011
+ display: inline-block;
1012
+ font-family: var(--font-mono);
1013
+ font-size: var(--text-xs);
1014
+ padding: 1px 6px;
1015
+ border-radius: var(--radius-sm);
1016
+ background: var(--color-paper-3);
1017
+ border: var(--rule) solid var(--color-rule);
1018
+ color: var(--color-ink);
1019
+ }
1020
+
1021
+ .env__key-pill--missing {
1022
+ border-color: color-mix(in oklch, var(--color-warn) 40%, transparent);
1023
+ color: var(--color-warn-ink);
1024
+ }
1025
+
1026
+ .env__key-pill--placeholder {
1027
+ border-color: var(--color-bad-rule);
1028
+ color: var(--color-bad-ink);
1029
+ }
1030
+
1031
+ /* alta de proyecto -------------------------------------------------------- */
1032
+
1033
+ .enroll {
1034
+ margin-top: var(--space-xl);
1035
+ padding-top: var(--space-lg);
1036
+ border-top: var(--rule) solid var(--color-rule-strong);
1037
+ }
1038
+
1039
+ .enroll__label {
1040
+ display: block;
1041
+ font-size: var(--text-xs);
1042
+ color: var(--color-ink-3);
1043
+ margin-bottom: var(--space-sm);
1044
+ }
1045
+
1046
+ .enroll__row {
1047
+ display: flex;
1048
+ flex-wrap: wrap;
1049
+ gap: var(--space-sm);
1050
+ }
1051
+
1052
+ .enroll__tools {
1053
+ display: flex;
1054
+ flex-wrap: wrap;
1055
+ align-items: center;
1056
+ gap: var(--space-sm);
1057
+ margin-top: var(--space-sm);
1058
+ }
1059
+
1060
+ .tools__docker {
1061
+ font-family: var(--font-mono);
1062
+ font-size: var(--text-xs);
1063
+ color: var(--color-ink-3);
1064
+ white-space: nowrap;
1065
+ }
1066
+
1067
+ .tools__docker[data-tone="bad"] {
1068
+ color: var(--color-bad);
1069
+ }
1070
+
1071
+ /* `display` propio le gana a la regla del navegador para [hidden]. */
1072
+ .tools__docker[hidden] {
1073
+ display: none;
1074
+ }
1075
+
1076
+ .orphan--empty {
1077
+ color: var(--color-ink-3);
1078
+ font-size: var(--text-sm);
1079
+ }
1080
+
1081
+ .project__docker-warning {
1082
+ font-size: var(--text-xs);
1083
+ color: oklch(50% 0.12 27);
1084
+ background: oklch(58% 0.19 27 / 0.1);
1085
+ border: 1px solid oklch(55% 0.12 27 / 0.3);
1086
+ padding: var(--space-xs) var(--space-sm);
1087
+ border-radius: var(--radius);
1088
+ margin-top: var(--space-xs);
1089
+ }
1090
+
1091
+ .enroll__input {
1092
+ flex: 1 1 20rem;
1093
+ min-width: 0;
1094
+ font-family: var(--font-mono);
1095
+ font-size: var(--text-sm);
1096
+ color: var(--color-ink);
1097
+ background: var(--color-paper-2);
1098
+ border: var(--rule) solid var(--color-rule-strong);
1099
+ border-radius: var(--radius);
1100
+ padding: 0.55rem 0.7rem;
1101
+ }
1102
+
1103
+ .enroll__input::placeholder {
1104
+ color: var(--color-ink-3);
1105
+ }
1106
+
1107
+ /* explorador de carpetas -------------------------------------------------- */
1108
+
1109
+ .picker {
1110
+ inline-size: min(34rem, calc(100vw - 2 * var(--space-md)));
1111
+ max-block-size: min(30rem, 80vh);
1112
+ padding: 0;
1113
+ color: var(--color-ink);
1114
+ background: var(--color-paper-2);
1115
+ border: var(--rule) solid var(--color-rule-strong);
1116
+ border-radius: var(--radius);
1117
+ }
1118
+
1119
+ .picker[open] {
1120
+ display: flex;
1121
+ flex-direction: column;
1122
+ }
1123
+
1124
+ .picker::backdrop {
1125
+ background: oklch(20% 0.01 70 / 0.45);
1126
+ }
1127
+
1128
+ .picker__head {
1129
+ display: flex;
1130
+ flex: none;
1131
+ align-items: baseline;
1132
+ gap: var(--space-sm);
1133
+ padding: var(--space-md);
1134
+ border-bottom: var(--rule) solid var(--color-rule);
1135
+ }
1136
+
1137
+ .picker__path {
1138
+ flex: 1;
1139
+ min-width: 0;
1140
+ font-family: var(--font-mono);
1141
+ font-size: var(--text-xs);
1142
+ color: var(--color-ink-2);
1143
+ overflow-wrap: anywhere;
1144
+ }
1145
+
1146
+ .picker__frequent {
1147
+ display: flex;
1148
+ align-items: center;
1149
+ gap: var(--space-xs);
1150
+ padding: 0.35rem var(--space-md);
1151
+ background: var(--color-paper-2);
1152
+ border-bottom: var(--rule) solid var(--color-rule);
1153
+ overflow-x: auto;
1154
+ }
1155
+
1156
+ .picker__frequent-label {
1157
+ font-size: var(--text-xs);
1158
+ color: var(--color-ink-3);
1159
+ white-space: nowrap;
1160
+ }
1161
+
1162
+ .picker__frequent-chips {
1163
+ display: flex;
1164
+ gap: var(--space-xs);
1165
+ flex-wrap: nowrap;
1166
+ }
1167
+
1168
+ .picker__chip {
1169
+ font-family: var(--font-mono);
1170
+ font-size: var(--text-xs);
1171
+ color: var(--color-ink-2);
1172
+ background: var(--color-paper);
1173
+ border: var(--rule) solid var(--color-rule-strong);
1174
+ border-radius: var(--radius-sm);
1175
+ padding: 0.15rem 0.5rem;
1176
+ cursor: pointer;
1177
+ white-space: nowrap;
1178
+ transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
1179
+ }
1180
+
1181
+ .picker__chip:hover {
1182
+ background: var(--color-paper-3);
1183
+ color: var(--color-ink);
1184
+ border-color: var(--color-ink-3);
1185
+ }
1186
+
1187
+ .enroll--dragover {
1188
+ border-color: var(--color-accent) !important;
1189
+ background: color-mix(in oklch, var(--color-accent) 4%, transparent);
1190
+ }
1191
+
1192
+ .picker__list {
1193
+ flex: 1;
1194
+ /* Sin esto la lista no se encoge (min-height: auto por defecto en flex), crece
1195
+ mas alla del alto del dialogo y empuja el pie fuera de la pantalla: los
1196
+ botones Subir y Registrar quedan inalcanzables. */
1197
+ min-block-size: 0;
1198
+ overflow-y: auto;
1199
+ list-style: none;
1200
+ padding: var(--space-xs) 0;
1201
+ margin: 0;
1202
+ }
1203
+
1204
+ .picker__row {
1205
+ display: flex;
1206
+ align-items: baseline;
1207
+ gap: var(--space-md);
1208
+ inline-size: 100%;
1209
+ text-align: start;
1210
+ font-family: var(--font-body);
1211
+ font-size: var(--text-sm);
1212
+ color: var(--color-ink);
1213
+ background: none;
1214
+ border: 0;
1215
+ padding: 0.45rem var(--space-md);
1216
+ cursor: pointer;
1217
+ }
1218
+
1219
+ .picker__row:hover,
1220
+ .picker__row:focus-visible {
1221
+ background: var(--color-paper-3);
1222
+ }
1223
+
1224
+ .picker__name {
1225
+ flex: 1;
1226
+ min-width: 0;
1227
+ overflow-wrap: anywhere;
1228
+ }
1229
+
1230
+ .picker__markers {
1231
+ font-family: var(--font-mono);
1232
+ font-size: var(--text-xs);
1233
+ color: var(--color-accent);
1234
+ white-space: nowrap;
1235
+ }
1236
+
1237
+ .picker__empty,
1238
+ .picker__note {
1239
+ font-size: var(--text-xs);
1240
+ color: var(--color-ink-3);
1241
+ padding: var(--space-sm) var(--space-md);
1242
+ }
1243
+
1244
+ .picker__foot {
1245
+ display: flex;
1246
+ flex: none;
1247
+ justify-content: space-between;
1248
+ gap: var(--space-sm);
1249
+ padding: var(--space-md);
1250
+ border-top: var(--rule) solid var(--color-rule);
1251
+ }
1252
+
1253
+ .picker__nav {
1254
+ display: flex;
1255
+ gap: var(--space-sm);
1256
+ }
1257
+
1258
+ .auth {
1259
+ padding: var(--space-md);
1260
+ }
1261
+
1262
+ .auth__hint {
1263
+ font-size: var(--text-xs);
1264
+ color: var(--color-ink-2);
1265
+ margin-bottom: var(--space-sm);
1266
+ }
1267
+
1268
+ .auth__input {
1269
+ width: 100%;
1270
+ box-sizing: border-box;
1271
+ }
1272
+
1273
+ /* colofon y avisos -------------------------------------------------------- */
1274
+
1275
+ .colophon {
1276
+ display: flex;
1277
+ flex-wrap: wrap;
1278
+ justify-content: space-between;
1279
+ gap: var(--space-sm);
1280
+ border-top: var(--rule) solid var(--color-rule);
1281
+ padding: var(--space-md) var(--space-gutter);
1282
+ font-size: var(--text-xs);
1283
+ color: var(--color-ink-3);
1284
+ }
1285
+
1286
+ .colophon__build {
1287
+ font-family: var(--font-mono);
1288
+ }
1289
+
1290
+ .flash {
1291
+ position: fixed;
1292
+ z-index: 2;
1293
+ inset-block-end: var(--space-md);
1294
+ inset-inline: var(--space-gutter);
1295
+ max-inline-size: 32rem;
1296
+ margin-inline: auto;
1297
+ font-size: var(--text-sm);
1298
+ color: var(--color-paper);
1299
+ background: var(--color-ink);
1300
+ border-radius: var(--radius);
1301
+ padding: var(--space-sm) var(--space-md);
1302
+ }
1303
+
1304
+ .flash[data-tone="bad"] {
1305
+ background: var(--color-bad);
1306
+ }
1307
+
1308
+ /* orphans ------------------------------------------------------------------ */
1309
+
1310
+ /* En reposo la seccion es neutra, y el rojo con el punto que late aparece solo
1311
+ * cuando hay un intruso. La seccion se muestra siempre para informar que el
1312
+ * chequeo corrio; vestida de alarma para decir "no hay nada" comunicaba lo
1313
+ * contrario, y un rojo encendido todo el tiempo deja de significar rojo: el dia
1314
+ * que aparezca uno de verdad no se distingue del dia anterior.
1315
+ *
1316
+ * Lo pone `refreshOrphans` con data-tone, del mismo modo que los estados de
1317
+ * servicio: el color nunca lleva el significado solo, siempre va con su texto. */
1318
+ .orphans {
1319
+ margin-top: var(--space-xl);
1320
+ background: var(--color-paper);
1321
+ border: var(--rule) solid var(--color-rule-strong);
1322
+ border-radius: var(--radius);
1323
+ overflow: hidden;
1324
+ box-shadow: var(--shadow-sm);
1325
+ }
1326
+
1327
+ .orphans[data-tone="bad"] {
1328
+ border-color: oklch(55% 0.18 27 / 0.4);
1329
+ }
1330
+
1331
+ .orphans__heading {
1332
+ font-family: var(--font-body);
1333
+ font-size: var(--text-sm);
1334
+ font-weight: 600;
1335
+ color: var(--color-ink-2);
1336
+ background: var(--color-paper-3);
1337
+ padding: var(--space-sm) var(--space-md);
1338
+ border-bottom: var(--rule) solid var(--color-rule);
1339
+ margin: 0;
1340
+ display: flex;
1341
+ align-items: center;
1342
+ gap: var(--space-xs);
1343
+ }
1344
+
1345
+ .orphans[data-tone="bad"] .orphans__heading {
1346
+ color: oklch(65% 0.2 27);
1347
+ background: oklch(55% 0.18 27 / 0.1);
1348
+ border-bottom-color: oklch(55% 0.18 27 / 0.25);
1349
+ }
1350
+
1351
+ .orphans__heading::before {
1352
+ content: "";
1353
+ display: block;
1354
+ width: 7px;
1355
+ height: 7px;
1356
+ border-radius: 50%;
1357
+ background: var(--color-ink-3);
1358
+ }
1359
+
1360
+ .orphans[data-tone="bad"] .orphans__heading::before {
1361
+ background: oklch(65% 0.2 27);
1362
+ box-shadow: 0 0 0 2px oklch(65% 0.2 27 / 0.25);
1363
+ animation: orphan-pulse 1.8s ease-in-out infinite;
1364
+ }
1365
+
1366
+ @keyframes orphan-pulse {
1367
+ 0%, 100% { opacity: 1; }
1368
+ 50% { opacity: 0.35; }
1369
+ }
1370
+
1371
+ .orphans__header {
1372
+ display: flex;
1373
+ align-items: center;
1374
+ justify-content: space-between;
1375
+ gap: var(--space-md);
1376
+ padding: var(--space-sm) var(--space-md) 0;
1377
+ }
1378
+
1379
+ .orphans__desc {
1380
+ font-size: var(--text-xs);
1381
+ color: var(--color-ink-3);
1382
+ margin: 0;
1383
+ }
1384
+
1385
+ /* Los tokens y no un oklch a mano: --color-bad y sus derivados ya se redefinen
1386
+ para el tema oscuro, y un color crudo se quedaria con el claro en los dos. */
1387
+ .btn--kill-all {
1388
+ font-family: var(--font-body);
1389
+ font-size: var(--text-xs);
1390
+ padding: 0.35rem 0.75rem;
1391
+ background: var(--color-bad-soft);
1392
+ border: var(--rule) solid var(--color-bad-rule);
1393
+ color: var(--color-bad);
1394
+ border-radius: var(--radius);
1395
+ cursor: pointer;
1396
+ white-space: nowrap;
1397
+ transition: background 0.15s ease, border-color 0.15s ease;
1398
+ }
1399
+
1400
+ .btn--kill-all:hover {
1401
+ border-color: var(--color-bad);
1402
+ }
1403
+
1404
+ .orphans__list {
1405
+ list-style: none;
1406
+ margin: 0;
1407
+ padding: var(--space-xs) var(--space-md) var(--space-sm);
1408
+ display: flex;
1409
+ flex-direction: column;
1410
+ gap: var(--space-xs);
1411
+ }
1412
+
1413
+ .orphan {
1414
+ display: flex;
1415
+ align-items: center;
1416
+ justify-content: space-between;
1417
+ gap: var(--space-md);
1418
+ padding: var(--space-xs) var(--space-sm);
1419
+ background: var(--color-paper-2);
1420
+ border: var(--rule) solid var(--color-rule);
1421
+ border-radius: var(--radius);
1422
+ }
1423
+
1424
+ .orphan:hover {
1425
+ background: var(--color-paper-3);
1426
+ }
1427
+
1428
+ .orphans[data-tone="bad"] .orphan:hover {
1429
+ background: oklch(55% 0.12 27 / 0.07);
1430
+ }
1431
+
1432
+ /* Elegir cuales cerrar. Sale solo con dos o mas filas, porque con una sola el
1433
+ * boton Cerrar de esa fila ya es la accion. Tamaño de dedo, y con el acento del
1434
+ * proyecto para que no parezca un control del navegador pegado encima. */
1435
+ .orphan__pick {
1436
+ flex: none;
1437
+ inline-size: 1rem;
1438
+ block-size: 1rem;
1439
+ /* El cuadro sigue midiendo 1rem; el area sensible llega a 24px, que es lo que
1440
+ pide WCAG 2.5.8. La casilla se agrega directo a la fila desde el JS, sin
1441
+ `<label>` alrededor, asi que el cuadro ES todo el blanco del puntero. Las de
1442
+ `.clean__row` no tienen el problema porque su label extiende el area. */
1443
+ padding: 0.25rem;
1444
+ box-sizing: content-box;
1445
+ margin: 0;
1446
+ accent-color: var(--color-accent);
1447
+ cursor: pointer;
1448
+ }
1449
+
1450
+ .orphan__port {
1451
+ font-family: var(--font-mono);
1452
+ font-size: var(--text-xs);
1453
+ font-weight: 600;
1454
+ color: oklch(65% 0.2 27);
1455
+ background: oklch(55% 0.18 27 / 0.15);
1456
+ padding: 0.15rem 0.4rem;
1457
+ border-radius: var(--radius);
1458
+ white-space: nowrap;
1459
+ }
1460
+
1461
+ .orphan__info {
1462
+ flex: 1;
1463
+ min-width: 0;
1464
+ }
1465
+
1466
+ .orphan__name {
1467
+ font-size: var(--text-sm);
1468
+ font-weight: 500;
1469
+ color: var(--color-ink);
1470
+ white-space: nowrap;
1471
+ overflow: hidden;
1472
+ text-overflow: ellipsis;
1473
+ }
1474
+
1475
+ /* Por que esta fila esta en la lista. Va entre el proceso y su linea de
1476
+ * comando, con el color de aviso: el dato no es del proceso, es del puerto. */
1477
+ .orphan__claim {
1478
+ font-size: var(--text-xs);
1479
+ color: var(--color-warn);
1480
+ white-space: nowrap;
1481
+ overflow: hidden;
1482
+ text-overflow: ellipsis;
1483
+ }
1484
+
1485
+ .orphan__meta {
1486
+ font-family: var(--font-mono);
1487
+ font-size: var(--text-xs);
1488
+ color: var(--color-ink-3);
1489
+ white-space: nowrap;
1490
+ overflow: hidden;
1491
+ text-overflow: ellipsis;
1492
+ }
1493
+
1494
+ .orphan__kill {
1495
+ font-family: var(--font-body);
1496
+ font-size: var(--text-xs);
1497
+ padding: 0.3rem 0.6rem;
1498
+ background: oklch(55% 0.18 27 / 0.15);
1499
+ border: var(--rule) solid oklch(55% 0.18 27 / 0.4);
1500
+ color: oklch(70% 0.2 27);
1501
+ border-radius: var(--radius);
1502
+ cursor: pointer;
1503
+ transition: background 0.15s ease, border-color 0.15s ease;
1504
+ white-space: nowrap;
1505
+ }
1506
+
1507
+ .orphan__kill:hover {
1508
+ background: oklch(55% 0.18 27 / 0.3);
1509
+ border-color: oklch(55% 0.18 27 / 0.7);
1510
+ }
1511
+
1512
+ /* mobile ------------------------------------------------------------------ */
1513
+
1514
+ @media (max-width: 34rem) {
1515
+ .service {
1516
+ grid-template-columns: minmax(0, 1fr) auto;
1517
+ }
1518
+
1519
+ .service__state {
1520
+ grid-column: 1 / -1;
1521
+ }
1522
+
1523
+ .service__metrics {
1524
+ grid-column: 1 / -1;
1525
+ justify-self: start;
1526
+ }
1527
+
1528
+ .service__act {
1529
+ grid-column: 1 / -1;
1530
+ justify-self: start;
1531
+ display: flex;
1532
+ flex-wrap: wrap;
1533
+ gap: var(--space-sm);
1534
+ margin-top: var(--space-xs);
1535
+ }
1536
+ }
1537
+
1538
+ @media (prefers-reduced-motion: reduce) {
1539
+ * {
1540
+ transition-duration: 0.01ms !important;
1541
+ }
1542
+ }
1543
+
1544
+ /* Tuneles abiertos. Hereda la caja de `.orphans` y solo cambia el acento: un
1545
+ * intruso es un estorbo local, un tunel es un puerto tuyo abierto a internet.
1546
+ * El ambar lo separa del rojo de intrusos sin gritar como un error, porque
1547
+ * abrirlo fue una decision deliberada y no una falla. */
1548
+ .tunnels {
1549
+ border-color: oklch(70% 0.15 75 / 0.55);
1550
+ }
1551
+
1552
+ /* Con la caja de intrusos ya neutra en reposo, el encabezado heredado quedaba
1553
+ * gris. Esta seccion solo existe cuando hay puertos abiertos a internet, asi
1554
+ * que siempre lleva su acento: no tiene estado de reposo. */
1555
+ .tunnels .orphans__heading {
1556
+ color: oklch(58% 0.14 75);
1557
+ background: oklch(70% 0.15 75 / 0.12);
1558
+ border-bottom-color: oklch(70% 0.15 75 / 0.3);
1559
+ }
1560
+
1561
+ .tunnels .orphans__heading::before {
1562
+ background: oklch(65% 0.16 75);
1563
+ box-shadow: 0 0 0 2px oklch(65% 0.16 75 / 0.25);
1564
+ }
1565
+
1566
+ .tunnels .orphan__port {
1567
+ /* El fondo tambien: `.orphan__port` trae texto Y fondo rojos, asi que cambiar
1568
+ solo el color dejaba un puerto ambar sobre un chip rojo dentro de una
1569
+ seccion ambar. Un tunel abierto y un intruso no significan lo mismo. */
1570
+ color: oklch(52% 0.13 75);
1571
+ background: oklch(70% 0.15 75 / 0.15);
1572
+ }
1573
+
1574
+ /* `.orphan__name` viene con `white-space: nowrap` y elipsis, que para un nombre
1575
+ * de proceso esta bien y para una URL no: la cortaba a la mitad. El
1576
+ * `word-break` solo no alcanzaba, porque contra un `nowrap` no hay donde
1577
+ * quebrar. La URL entera es el dato de esta fila. */
1578
+ .tunnels .orphan__name {
1579
+ white-space: normal;
1580
+ overflow: visible;
1581
+ overflow-wrap: anywhere;
1582
+ word-break: break-all;
1583
+ }
1584
+
1585
+ /* dialogo de limpieza ------------------------------------------------------ */
1586
+
1587
+ .clean {
1588
+ flex: 1;
1589
+ min-block-size: 0;
1590
+ overflow-y: auto;
1591
+ padding: var(--space-md);
1592
+ }
1593
+
1594
+ /* La tabla de `docker system df` tal cual la formatea Docker: alineada por
1595
+ espacios, asi que necesita mono y no puede envolver. */
1596
+ .clean__usage {
1597
+ font-family: var(--font-mono);
1598
+ font-size: var(--text-xs);
1599
+ color: var(--color-ink-2);
1600
+ background: var(--color-paper);
1601
+ border: var(--rule) solid var(--color-rule);
1602
+ border-radius: var(--radius);
1603
+ margin: 0 0 var(--space-md);
1604
+ padding: var(--space-sm);
1605
+ overflow-x: auto;
1606
+ white-space: pre;
1607
+ }
1608
+
1609
+ .clean__list {
1610
+ list-style: none;
1611
+ margin: 0;
1612
+ padding: 0;
1613
+ display: flex;
1614
+ flex-direction: column;
1615
+ gap: var(--space-xs);
1616
+ }
1617
+
1618
+ .clean__row {
1619
+ display: flex;
1620
+ align-items: baseline;
1621
+ gap: var(--space-sm);
1622
+ cursor: pointer;
1623
+ }
1624
+
1625
+ .clean__row input {
1626
+ flex: none;
1627
+ accent-color: var(--color-accent);
1628
+ cursor: pointer;
1629
+ }
1630
+
1631
+ /* Los volumenes tienen datos adentro y no se regeneran: el unico de la lista
1632
+ que no se puede deshacer, y por eso el unico que lleva color. */
1633
+ .clean__row[data-riesgo="true"] {
1634
+ color: var(--color-bad);
1635
+ }
1636
+
1637
+ .clean__warn {
1638
+ font-size: var(--text-xs);
1639
+ color: var(--color-ink-3);
1640
+ }
1641
+
1642
+ /* telemetria mcp modal ------------------------------------------------------ */
1643
+
1644
+ .mcp-modal {
1645
+ max-width: 680px;
1646
+ width: 90vw;
1647
+ }
1648
+
1649
+ .mcp__content {
1650
+ display: flex;
1651
+ flex-direction: column;
1652
+ gap: var(--space-md);
1653
+ margin-top: var(--space-sm);
1654
+ }
1655
+
1656
+ .mcp__stats {
1657
+ display: flex;
1658
+ flex-wrap: wrap;
1659
+ gap: var(--space-md);
1660
+ font-size: var(--text-sm);
1661
+ color: var(--color-ink-2);
1662
+ background: var(--color-paper);
1663
+ padding: var(--space-sm) var(--space-md);
1664
+ border: var(--rule) solid var(--color-rule);
1665
+ border-radius: var(--radius);
1666
+ }
1667
+
1668
+ .mcp__stat strong {
1669
+ color: var(--color-ink);
1670
+ font-family: var(--font-mono);
1671
+ }
1672
+
1673
+ .mcp__breakdown {
1674
+ display: flex;
1675
+ flex-wrap: wrap;
1676
+ gap: var(--space-xs);
1677
+ }
1678
+
1679
+ .mcp__pill {
1680
+ font-family: var(--font-mono);
1681
+ font-size: var(--text-xs);
1682
+ padding: 0.2rem 0.5rem;
1683
+ background: var(--color-paper-2);
1684
+ border: var(--rule) solid var(--color-rule-strong);
1685
+ border-radius: 9999px;
1686
+ color: var(--color-ink-2);
1687
+ }
1688
+
1689
+ .mcp__table-scroll {
1690
+ max-height: 320px;
1691
+ overflow-y: auto;
1692
+ border: var(--rule) solid var(--color-rule);
1693
+ border-radius: var(--radius);
1694
+ background: var(--color-paper);
1695
+ }
1696
+
1697
+ .mcp__table {
1698
+ width: 100%;
1699
+ margin: 0;
1700
+ }
1701
+
1702
+ .mcp__tool-name {
1703
+ font-family: var(--font-mono);
1704
+ font-size: var(--text-xs);
1705
+ color: var(--color-ink);
1706
+ }
1707
+
1708
+ .mcp__status-badge {
1709
+ font-family: var(--font-mono);
1710
+ font-size: var(--text-xs);
1711
+ padding: 0.15rem 0.4rem;
1712
+ border-radius: 3px;
1713
+ }
1714
+
1715
+ .mcp__status-badge--ok {
1716
+ background: color-mix(in oklch, var(--color-ok) 14%, transparent);
1717
+ color: var(--color-ok-ink);
1718
+ }
1719
+
1720
+ .mcp__status-badge--rate_limited {
1721
+ background: color-mix(in oklch, var(--color-warn) 16%, transparent);
1722
+ color: var(--color-warn-ink);
1723
+ }
1724
+
1725
+ .mcp__status-badge--error {
1726
+ background: var(--color-bad-soft);
1727
+ color: var(--color-bad-ink);
1728
+ }
1729
+