trackops 2.0.3 → 2.0.5

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.
Files changed (103) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +695 -402
  3. package/bin/trackops.js +116 -116
  4. package/lib/config.js +326 -326
  5. package/lib/control.js +208 -208
  6. package/lib/env.js +244 -244
  7. package/lib/init.js +325 -325
  8. package/lib/locale.js +24 -0
  9. package/lib/opera-bootstrap.js +941 -874
  10. package/lib/opera.js +494 -477
  11. package/lib/preferences.js +74 -74
  12. package/lib/registry.js +214 -196
  13. package/lib/release.js +56 -56
  14. package/lib/runtime-state.js +144 -144
  15. package/lib/server.js +312 -207
  16. package/lib/skills.js +74 -57
  17. package/lib/workspace.js +260 -260
  18. package/locales/en.json +192 -166
  19. package/locales/es.json +192 -166
  20. package/package.json +61 -58
  21. package/scripts/postinstall-locale.js +21 -21
  22. package/scripts/skills-marketplace-smoke.js +124 -124
  23. package/scripts/smoke-tests.js +558 -554
  24. package/scripts/sync-skill-version.js +21 -21
  25. package/scripts/validate-skill.js +103 -103
  26. package/skills/trackops/SKILL.md +126 -122
  27. package/skills/trackops/agents/openai.yaml +7 -7
  28. package/skills/trackops/locales/en/SKILL.md +126 -122
  29. package/skills/trackops/locales/en/references/activation.md +94 -75
  30. package/skills/trackops/locales/en/references/troubleshooting.md +73 -55
  31. package/skills/trackops/locales/en/references/workflow.md +55 -32
  32. package/skills/trackops/references/activation.md +94 -75
  33. package/skills/trackops/references/troubleshooting.md +73 -55
  34. package/skills/trackops/references/workflow.md +55 -32
  35. package/skills/trackops/skill.json +29 -29
  36. package/templates/hooks/post-checkout +2 -2
  37. package/templates/hooks/post-commit +2 -2
  38. package/templates/hooks/post-merge +2 -2
  39. package/templates/opera/agent.md +28 -27
  40. package/templates/opera/architecture/dependency-graph.md +24 -24
  41. package/templates/opera/architecture/runtime-automation.md +24 -24
  42. package/templates/opera/architecture/runtime-operations.md +34 -34
  43. package/templates/opera/en/agent.md +22 -21
  44. package/templates/opera/en/architecture/dependency-graph.md +24 -24
  45. package/templates/opera/en/architecture/runtime-automation.md +24 -24
  46. package/templates/opera/en/architecture/runtime-operations.md +34 -34
  47. package/templates/opera/en/reviews/delivery-audit.md +18 -18
  48. package/templates/opera/en/reviews/integration-audit.md +18 -18
  49. package/templates/opera/en/router.md +24 -19
  50. package/templates/opera/references/autonomy-and-recovery.md +117 -117
  51. package/templates/opera/references/opera-cycle.md +193 -193
  52. package/templates/opera/registry.md +28 -28
  53. package/templates/opera/reviews/delivery-audit.md +18 -18
  54. package/templates/opera/reviews/integration-audit.md +18 -18
  55. package/templates/opera/router.md +54 -49
  56. package/templates/skills/changelog-updater/SKILL.md +69 -69
  57. package/templates/skills/commiter/SKILL.md +99 -99
  58. package/templates/skills/opera-contract-auditor/SKILL.md +38 -38
  59. package/templates/skills/opera-contract-auditor/locales/en/SKILL.md +38 -38
  60. package/templates/skills/opera-policy-guard/SKILL.md +26 -26
  61. package/templates/skills/opera-policy-guard/locales/en/SKILL.md +26 -26
  62. package/templates/skills/opera-skill/SKILL.md +279 -0
  63. package/templates/skills/opera-skill/locales/en/SKILL.md +279 -0
  64. package/templates/skills/opera-skill/locales/en/references/phase-dod.md +138 -0
  65. package/templates/skills/opera-skill/references/phase-dod.md +138 -0
  66. package/templates/skills/project-starter-skill/SKILL.md +150 -131
  67. package/templates/skills/project-starter-skill/locales/en/SKILL.md +143 -105
  68. package/templates/skills/project-starter-skill/references/opera-cycle.md +195 -193
  69. package/ui/css/base.css +284 -266
  70. package/ui/css/charts.css +425 -327
  71. package/ui/css/components.css +1107 -570
  72. package/ui/css/onboarding.css +133 -0
  73. package/ui/css/panels.css +345 -406
  74. package/ui/css/terminal.css +125 -0
  75. package/ui/css/timeline.css +58 -0
  76. package/ui/css/tokens.css +284 -227
  77. package/ui/favicon.svg +5 -5
  78. package/ui/index.html +99 -96
  79. package/ui/js/api.js +49 -13
  80. package/ui/js/app.js +28 -32
  81. package/ui/js/charts.js +526 -0
  82. package/ui/js/console-logger.js +172 -172
  83. package/ui/js/filters.js +247 -0
  84. package/ui/js/icons.js +129 -104
  85. package/ui/js/keyboard.js +229 -0
  86. package/ui/js/onboarding.js +33 -42
  87. package/ui/js/router.js +142 -125
  88. package/ui/js/theme.js +100 -100
  89. package/ui/js/time-tracker.js +248 -248
  90. package/ui/js/views/board.js +84 -114
  91. package/ui/js/views/dashboard.js +870 -0
  92. package/ui/js/views/flash.js +47 -47
  93. package/ui/js/views/projects.js +745 -0
  94. package/ui/js/views/scrum.js +476 -0
  95. package/ui/js/views/settings.js +153 -203
  96. package/ui/js/views/sidebar.js +37 -31
  97. package/ui/js/views/tasks.js +218 -101
  98. package/ui/js/views/timeline.js +265 -0
  99. package/ui/js/views/topbar.js +94 -107
  100. package/ui/app.js +0 -950
  101. package/ui/js/views/insights.js +0 -340
  102. package/ui/js/views/overview.js +0 -369
  103. package/ui/styles.css +0 -688
@@ -1,570 +1,1107 @@
1
- /* ═══════════════════════════════════════════════════════
2
- COMPONENTS — Botones, Cards, Badges, Forms
3
- ═══════════════════════════════════════════════════════ */
4
-
5
- /* ───────────────────────────────────
6
- BOTONES
7
- ─────────────────────────────────── */
8
- .btn {
9
- display: inline-flex;
10
- align-items: center;
11
- justify-content: center;
12
- gap: var(--space-2);
13
- font-weight: 600;
14
- font-size: var(--text-sm);
15
- border-radius: var(--radius-full);
16
- padding: 0.6rem 1.25rem;
17
- border: 1px solid transparent;
18
- transition:
19
- background var(--duration-base) var(--ease-out),
20
- box-shadow var(--duration-base) var(--ease-out),
21
- transform var(--duration-fast) var(--ease-out),
22
- border-color var(--duration-base) var(--ease-out);
23
- letter-spacing: 0.01em;
24
- cursor: pointer;
25
- white-space: nowrap;
26
- min-height: 36px;
27
- line-height: 1;
28
- }
29
-
30
- .btn:active { transform: translateY(1px); }
31
-
32
- /* Primary */
33
- .btn-primary {
34
- background: linear-gradient(135deg, var(--accent) 0%, #4F46E5 100%);
35
- color: white;
36
- box-shadow: var(--shadow-accent);
37
- }
38
- .btn-primary:hover {
39
- transform: translateY(-1px);
40
- box-shadow: 0 12px 32px rgba(99,102,241,0.35);
41
- }
42
-
43
- /* Ghost */
44
- .btn-ghost {
45
- background: var(--surface-4);
46
- color: var(--text-secondary);
47
- border-color: var(--border);
48
- }
49
- .btn-ghost:hover {
50
- background: var(--surface-3);
51
- color: var(--text-primary);
52
- border-color: var(--border-strong);
53
- }
54
-
55
- /* Danger */
56
- .btn-danger {
57
- background: var(--danger-light);
58
- color: var(--danger);
59
- border-color: rgba(239,68,68,0.2);
60
- }
61
- .btn-danger:hover {
62
- background: var(--danger);
63
- color: white;
64
- }
65
-
66
- /* Success */
67
- .btn-success {
68
- background: var(--success-light);
69
- color: var(--success);
70
- border-color: rgba(16,185,129,0.2);
71
- }
72
- .btn-success:hover {
73
- background: var(--success);
74
- color: white;
75
- }
76
-
77
- /* Icon button */
78
- .btn-icon {
79
- padding: var(--space-2);
80
- min-height: 36px;
81
- min-width: 36px;
82
- border-radius: var(--radius-md);
83
- }
84
-
85
- /* Small */
86
- .btn-sm {
87
- padding: 0.4rem 0.85rem;
88
- font-size: var(--text-xs);
89
- min-height: 28px;
90
- }
91
-
92
- /* Chip / pill */
93
- .chip {
94
- display: inline-flex;
95
- align-items: center;
96
- gap: var(--space-1);
97
- padding: 0.25rem 0.75rem;
98
- font-size: var(--text-xs);
99
- font-weight: 600;
100
- border-radius: var(--radius-full);
101
- border: 1px solid var(--border);
102
- background: var(--surface-4);
103
- color: var(--text-secondary);
104
- cursor: pointer;
105
- transition:
106
- background var(--duration-fast) var(--ease-out),
107
- border-color var(--duration-fast) var(--ease-out),
108
- color var(--duration-fast) var(--ease-out);
109
- }
110
- .chip:hover {
111
- background: var(--surface-3);
112
- border-color: var(--border-strong);
113
- color: var(--text-primary);
114
- }
115
- .chip.is-active {
116
- background: var(--accent-light);
117
- border-color: var(--border-accent);
118
- color: var(--text-accent);
119
- }
120
-
121
- /* ───────────────────────────────────
122
- BADGES / TAGS
123
- ─────────────────────────────────── */
124
- .badge {
125
- display: inline-flex;
126
- align-items: center;
127
- gap: var(--space-1);
128
- padding: 0.2rem 0.6rem;
129
- font-size: var(--text-xs);
130
- font-weight: 700;
131
- border-radius: var(--radius-full);
132
- white-space: nowrap;
133
- letter-spacing: 0.04em;
134
- }
135
-
136
- .badge-accent { background: var(--accent-light); color: var(--text-accent); border: 1px solid rgba(99,102,241,0.2); }
137
- .badge-success { background: var(--success-light); color: var(--success); border: 1px solid rgba(16,185,129,0.2); }
138
- .badge-warning { background: var(--warning-light); color: var(--warning); border: 1px solid rgba(245,158,11,0.2); }
139
- .badge-danger { background: var(--danger-light); color: var(--danger); border: 1px solid rgba(239,68,68,0.2); }
140
- .badge-muted { background: var(--surface-3); color: var(--text-muted); border: 1px solid var(--border); }
141
- .badge-info { background: var(--info-light); color: var(--info); border: 1px solid rgba(59,130,246,0.2); }
142
-
143
- /* Priority badges */
144
- .badge-p0 { background: rgba(239,68,68,0.12); color: var(--p0); border: 1px solid rgba(239,68,68,0.2); }
145
- .badge-p1 { background: rgba(245,158,11,0.12); color: var(--p1); border: 1px solid rgba(245,158,11,0.2); }
146
- .badge-p2 { background: var(--accent-light); color: var(--accent); border: 1px solid rgba(99,102,241,0.2); }
147
- .badge-p3 { background: var(--surface-3); color: var(--text-muted); border: 1px solid var(--border); }
148
-
149
- /* Status badges */
150
- .status-pending { background: rgba(245,158,11,0.1); color: var(--warning); border: 1px solid rgba(245,158,11,0.15); }
151
- .status-in_progress { background: var(--info-light); color: var(--info); border: 1px solid rgba(59,130,246,0.2); }
152
- .status-in_review { background: var(--accent-light); color: var(--text-accent); border: 1px solid rgba(99,102,241,0.2); }
153
- .status-blocked { background: var(--danger-light); color: var(--danger); border: 1px solid rgba(239,68,68,0.2); }
154
- .status-completed { background: var(--success-light); color: var(--success); border: 1px solid rgba(16,185,129,0.2); }
155
- .status-cancelled { background: var(--surface-3); color: var(--text-muted); border: 1px solid var(--border); }
156
-
157
- /* ───────────────────────────────────
158
- CARDS
159
- ─────────────────────────────────── */
160
- .card {
161
- background: var(--surface-2);
162
- border: 1px solid var(--border);
163
- border-radius: var(--radius-lg);
164
- transition:
165
- border-color var(--duration-base) var(--ease-out),
166
- box-shadow var(--duration-base) var(--ease-out),
167
- background var(--duration-base) var(--ease-out);
168
- }
169
- .card:hover { border-color: var(--border-strong); }
170
-
171
- .card-body { padding: var(--space-5); }
172
- .card-sm .card-body { padding: var(--space-4); }
173
- .card-lg .card-body { padding: var(--space-6); }
174
-
175
- /* KPI metric card */
176
- .kpi-card {
177
- background: var(--surface-2);
178
- border: 1px solid var(--border);
179
- border-radius: var(--radius-lg);
180
- padding: var(--space-5);
181
- display: flex;
182
- flex-direction: column;
183
- gap: var(--space-2);
184
- position: relative;
185
- overflow: hidden;
186
- transition:
187
- border-color var(--duration-base) var(--ease-out),
188
- box-shadow var(--duration-base) var(--ease-out);
189
- }
190
- .kpi-card::before {
191
- content: '';
192
- position: absolute;
193
- top: 0; left: 0; right: 0;
194
- height: 2px;
195
- background: linear-gradient(90deg, var(--accent), transparent);
196
- opacity: 0;
197
- transition: opacity var(--duration-base) var(--ease-out);
198
- }
199
- .kpi-card:hover { border-color: var(--border-strong); box-shadow: var(--shadow-md); }
200
- .kpi-card:hover::before { opacity: 1; }
201
-
202
- .kpi-card.kpi-accent { border-top: 2px solid var(--accent); }
203
- .kpi-card.kpi-success { border-top: 2px solid var(--success); }
204
- .kpi-card.kpi-warning { border-top: 2px solid var(--warning); }
205
- .kpi-card.kpi-danger { border-top: 2px solid var(--danger); }
206
-
207
- .kpi-header {
208
- display: flex;
209
- align-items: center;
210
- justify-content: space-between;
211
- }
212
-
213
- .kpi-title {
214
- font-size: var(--text-xs);
215
- font-weight: 700;
216
- letter-spacing: 0.08em;
217
- text-transform: uppercase;
218
- color: var(--text-muted);
219
- }
220
-
221
- .kpi-icon {
222
- width: 32px;
223
- height: 32px;
224
- border-radius: var(--radius-sm);
225
- display: flex;
226
- align-items: center;
227
- justify-content: center;
228
- }
229
- .kpi-icon.accent { background: var(--accent-light); color: var(--accent); }
230
- .kpi-icon.success { background: var(--success-light); color: var(--success); }
231
- .kpi-icon.warning { background: var(--warning-light); color: var(--warning); }
232
- .kpi-icon.danger { background: var(--danger-light); color: var(--danger); }
233
-
234
- .kpi-value {
235
- font-family: var(--font-heading);
236
- font-size: var(--text-3xl);
237
- font-weight: 800;
238
- letter-spacing: -0.04em;
239
- line-height: 1;
240
- color: var(--text-primary);
241
- }
242
-
243
- .kpi-sub {
244
- font-size: var(--text-xs);
245
- color: var(--text-muted);
246
- }
247
-
248
- .kpi-trend {
249
- display: inline-flex;
250
- align-items: center;
251
- gap: var(--space-1);
252
- font-size: var(--text-xs);
253
- font-weight: 600;
254
- }
255
- .kpi-trend.up { color: var(--success); }
256
- .kpi-trend.down { color: var(--danger); }
257
-
258
- /* Task card (kanban) */
259
- .task-card {
260
- background: var(--surface-2);
261
- border: 1px solid var(--border);
262
- border-radius: var(--radius-md);
263
- padding: var(--space-4);
264
- text-align: left;
265
- width: 100%;
266
- cursor: pointer;
267
- transition:
268
- border-color var(--duration-fast) var(--ease-out),
269
- box-shadow var(--duration-fast) var(--ease-out),
270
- background var(--duration-fast) var(--ease-out),
271
- transform var(--duration-fast) var(--ease-out);
272
- position: relative;
273
- }
274
- .task-card:hover {
275
- border-color: var(--border-strong);
276
- box-shadow: var(--shadow-sm);
277
- transform: translateY(-1px);
278
- }
279
- .task-card.is-selected {
280
- border-color: var(--border-accent);
281
- background: linear-gradient(135deg, rgba(99,102,241,0.06) 0%, var(--surface-2) 100%);
282
- box-shadow: var(--shadow-accent);
283
- }
284
- .task-card[data-status="blocked"] {
285
- border-left: 3px solid var(--danger);
286
- }
287
- .task-card[data-status="completed"] {
288
- opacity: 0.65;
289
- }
290
- .task-card[data-status="completed"]:hover {
291
- opacity: 1;
292
- }
293
-
294
- .task-card-title {
295
- font-size: var(--text-sm);
296
- font-weight: 700;
297
- color: var(--text-primary);
298
- margin-bottom: var(--space-1);
299
- display: -webkit-box;
300
- -webkit-line-clamp: 2;
301
- -webkit-box-orient: vertical;
302
- overflow: hidden;
303
- }
304
- .task-card-id {
305
- font-family: var(--font-mono);
306
- font-size: 0.7rem;
307
- color: var(--text-muted);
308
- display: block;
309
- margin-bottom: var(--space-2);
310
- }
311
- .task-card-summary {
312
- font-size: var(--text-xs);
313
- color: var(--text-secondary);
314
- margin-bottom: var(--space-3);
315
- display: -webkit-box;
316
- -webkit-line-clamp: 2;
317
- -webkit-box-orient: vertical;
318
- overflow: hidden;
319
- min-height: 2.4em;
320
- }
321
- .task-card-meta {
322
- display: flex;
323
- flex-wrap: wrap;
324
- gap: var(--space-1);
325
- }
326
-
327
- /* Dragging state */
328
- .task-card[draggable]:hover { cursor: grab; }
329
- .task-card.is-dragging {
330
- opacity: 0.4;
331
- transform: rotate(2deg) scale(0.98);
332
- }
333
-
334
- /* ───────────────────────────────────
335
- FORMS
336
- ─────────────────────────────────── */
337
- .field { display: flex; flex-direction: column; gap: var(--space-2); }
338
-
339
- .field label {
340
- font-size: var(--text-xs);
341
- font-weight: 700;
342
- letter-spacing: 0.06em;
343
- text-transform: uppercase;
344
- color: var(--text-muted);
345
- }
346
-
347
- input[type="text"],
348
- input[type="search"],
349
- input[type="email"],
350
- textarea,
351
- select {
352
- width: 100%;
353
- padding: 0.65rem 0.85rem;
354
- font-size: var(--text-sm);
355
- font-family: var(--font-ui);
356
- color: var(--text-primary);
357
- background: var(--surface-1);
358
- border: 1px solid var(--border);
359
- border-radius: var(--radius-md);
360
- outline: none;
361
- transition:
362
- border-color var(--duration-fast) var(--ease-out),
363
- box-shadow var(--duration-fast) var(--ease-out),
364
- background var(--duration-fast) var(--ease-out);
365
- min-height: 40px;
366
- appearance: none;
367
- -webkit-appearance: none;
368
- }
369
-
370
- input:hover, textarea:hover, select:hover {
371
- border-color: var(--border-strong);
372
- }
373
-
374
- input:focus, textarea:focus, select:focus {
375
- border-color: var(--accent);
376
- box-shadow: 0 0 0 3px var(--accent-light);
377
- }
378
-
379
- textarea {
380
- resize: vertical;
381
- min-height: 80px;
382
- line-height: 1.5;
383
- }
384
-
385
- select {
386
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%2394A3B8' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
387
- background-repeat: no-repeat;
388
- background-position: right 12px center;
389
- padding-right: 36px;
390
- }
391
-
392
- /* Checkbox custom */
393
- input[type="checkbox"] {
394
- width: 18px;
395
- height: 18px;
396
- min-height: 18px;
397
- min-width: 18px;
398
- border-radius: var(--radius-xs);
399
- border: 2px solid var(--border-strong);
400
- background: var(--surface-1);
401
- cursor: pointer;
402
- accent-color: var(--accent);
403
- }
404
-
405
- .checkbox-field {
406
- display: flex;
407
- align-items: center;
408
- gap: var(--space-3);
409
- }
410
- .checkbox-field label {
411
- font-size: var(--text-sm);
412
- font-weight: 500;
413
- letter-spacing: 0;
414
- text-transform: none;
415
- color: var(--text-secondary);
416
- cursor: pointer;
417
- }
418
-
419
- .field-row {
420
- display: grid;
421
- grid-template-columns: repeat(2, 1fr);
422
- gap: var(--space-3);
423
- }
424
-
425
- .form-actions {
426
- display: flex;
427
- gap: var(--space-3);
428
- padding-top: var(--space-2);
429
- }
430
-
431
- /* Search bar */
432
- .search-wrapper {
433
- position: relative;
434
- display: flex;
435
- align-items: center;
436
- }
437
- .search-wrapper .search-icon {
438
- position: absolute;
439
- left: var(--space-3);
440
- color: var(--text-muted);
441
- pointer-events: none;
442
- flex-shrink: 0;
443
- }
444
- .search-wrapper input {
445
- padding-left: 2.4rem;
446
- }
447
- .search-wrapper .search-kbd {
448
- position: absolute;
449
- right: var(--space-3);
450
- font-family: var(--font-mono);
451
- font-size: 0.65rem;
452
- color: var(--text-muted);
453
- background: var(--surface-3);
454
- padding: 2px 6px;
455
- border-radius: var(--radius-xs);
456
- border: 1px solid var(--border);
457
- pointer-events: none;
458
- }
459
-
460
- /* ───────────────────────────────────
461
- EMPTY STATE
462
- ─────────────────────────────────── */
463
- .empty-state {
464
- display: flex;
465
- flex-direction: column;
466
- align-items: center;
467
- justify-content: center;
468
- gap: var(--space-3);
469
- padding: var(--space-10) var(--space-6);
470
- text-align: center;
471
- color: var(--text-muted);
472
- font-size: var(--text-sm);
473
- border: 1px dashed var(--border);
474
- border-radius: var(--radius-lg);
475
- }
476
- .empty-state svg { opacity: 0.4; }
477
-
478
- /* ───────────────────────────────────
479
- INFO ROWS
480
- ─────────────────────────────────── */
481
- .info-row {
482
- padding: var(--space-3) var(--space-4);
483
- border-radius: var(--radius-md);
484
- background: var(--surface-4);
485
- border: 1px solid var(--border);
486
- display: flex;
487
- flex-direction: column;
488
- gap: var(--space-1);
489
- }
490
- .info-row .label-sm { margin-bottom: var(--space-1); }
491
- .info-row .value {
492
- font-size: var(--text-sm);
493
- font-weight: 500;
494
- font-family: var(--font-mono);
495
- color: var(--text-primary);
496
- overflow: hidden;
497
- text-overflow: ellipsis;
498
- white-space: nowrap;
499
- }
500
-
501
- /* ───────────────────────────────────
502
- SECTION HEADER (dentro de vistas)
503
- ─────────────────────────────────── */
504
- .section-header {
505
- display: flex;
506
- align-items: flex-start;
507
- justify-content: space-between;
508
- gap: var(--space-4);
509
- margin-bottom: var(--space-5);
510
- }
511
- .section-header-left { display: flex; flex-direction: column; gap: var(--space-1); }
512
- .section-header h2, .section-header h3 { margin: 0; }
513
- .section-subtitle { font-size: var(--text-sm); color: var(--text-secondary); }
514
-
515
- /* ───────────────────────────────────
516
- STACK LIST
517
- ─────────────────────────────────── */
518
- .stack { display: flex; flex-direction: column; gap: var(--space-2); }
519
- .stack-sm { gap: var(--space-2); }
520
- .stack-md { gap: var(--space-3); }
521
- .stack-lg { gap: var(--space-4); }
522
-
523
- /* ───────────────────────────────────
524
- GRID LAYOUTS COMUNES
525
- ─────────────────────────────────── */
526
- .grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-4); }
527
- .grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4); }
528
- .grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-4); }
529
- .grid-split { display: grid; grid-template-columns: 1fr 380px; gap: var(--space-4); align-items: start; }
530
-
531
- @media (max-width: 1280px) {
532
- .grid-4 { grid-template-columns: repeat(2, 1fr); }
533
- .grid-3 { grid-template-columns: repeat(2, 1fr); }
534
- .grid-split { grid-template-columns: 1fr; }
535
- }
536
- @media (max-width: 768px) {
537
- .grid-4, .grid-3, .grid-2 { grid-template-columns: 1fr; }
538
- .field-row { grid-template-columns: 1fr; }
539
- }
540
-
541
- /* ───────────────────────────────────
542
- SPINNER DE CARGA
543
- ─────────────────────────────────── */
544
- .spinner {
545
- width: 20px; height: 20px;
546
- border: 2px solid var(--border-strong);
547
- border-top-color: var(--accent);
548
- border-radius: 50%;
549
- animation: spin 0.7s linear infinite;
550
- }
551
-
552
- /* ───────────────────────────────────
553
- THEME TOGGLE BUTTON
554
- ─────────────────────────────────── */
555
- .theme-toggle {
556
- position: relative;
557
- overflow: hidden;
558
- color: var(--text-secondary);
559
- }
560
- .theme-toggle:hover {
561
- color: var(--accent);
562
- background: var(--accent-light);
563
- border-color: var(--border-accent);
564
- }
565
- .theme-toggle svg {
566
- transition: transform var(--duration-slow) var(--ease-out), opacity var(--duration-base);
567
- }
568
- .theme-toggle:hover svg {
569
- transform: rotate(20deg) scale(1.1);
570
- }
1
+ /* ═══════════════════════════════════════════════════════
2
+ COMPONENTS — Botones, Cards, Badges, Forms
3
+ ═══════════════════════════════════════════════════════ */
4
+
5
+ /* ───────────────────────────────────
6
+ BOTONES
7
+ ─────────────────────────────────── */
8
+ .btn {
9
+ display: inline-flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+ gap: var(--space-2);
13
+ font-weight: 600;
14
+ font-size: var(--text-sm);
15
+ border-radius: var(--radius-full);
16
+ padding: 0.6rem 1.25rem;
17
+ border: 1px solid transparent;
18
+ transition:
19
+ background var(--duration-base) var(--ease-out),
20
+ box-shadow var(--duration-base) var(--ease-out),
21
+ transform var(--duration-fast) var(--ease-out),
22
+ border-color var(--duration-base) var(--ease-out);
23
+ letter-spacing: 0.01em;
24
+ cursor: pointer;
25
+ white-space: nowrap;
26
+ min-height: 36px;
27
+ line-height: 1;
28
+ }
29
+
30
+ .btn:active { transform: translateY(1px); }
31
+
32
+ /* Primary */
33
+ .btn-primary {
34
+ background: linear-gradient(135deg, #3B82F6 0%, #60A5FA 100%);
35
+ color: white;
36
+ box-shadow: 0 4px 12px rgba(96, 165, 250, 0.3);
37
+ }
38
+ .btn-primary:hover {
39
+ transform: translateY(-1px);
40
+ box-shadow: 0 12px 32px rgba(96, 165, 250, 0.35);
41
+ }
42
+
43
+ /* Ghost */
44
+ .btn-ghost {
45
+ background: var(--glass-bg-subtle);
46
+ color: var(--text-secondary);
47
+ border: 1px solid var(--glass-border);
48
+ }
49
+ .btn-ghost:hover {
50
+ background: var(--glass-bg);
51
+ color: var(--text-primary);
52
+ border-color: var(--border-strong);
53
+ }
54
+
55
+ /* Danger */
56
+ .btn-danger {
57
+ background: var(--danger-light);
58
+ color: var(--danger);
59
+ border-color: rgba(239,68,68,0.2);
60
+ }
61
+ .btn-danger:hover {
62
+ background: var(--danger);
63
+ color: white;
64
+ }
65
+
66
+ /* Success */
67
+ .btn-success {
68
+ background: var(--success-light);
69
+ color: var(--success);
70
+ border-color: rgba(16,185,129,0.2);
71
+ }
72
+ .btn-success:hover {
73
+ background: var(--success);
74
+ color: white;
75
+ }
76
+
77
+ /* Icon button */
78
+ .btn-icon {
79
+ padding: var(--space-2);
80
+ min-height: 36px;
81
+ min-width: 36px;
82
+ border-radius: var(--radius-md);
83
+ }
84
+
85
+ /* Small — min 44px touch target via padding (WCAG 2.2 AA) */
86
+ .btn-sm {
87
+ padding: 0.4rem 0.85rem;
88
+ font-size: var(--text-xs);
89
+ min-height: 32px;
90
+ position: relative;
91
+ }
92
+ /* Invisible touch target expansion for small buttons */
93
+ .btn-sm::after {
94
+ content: '';
95
+ position: absolute;
96
+ inset: -6px;
97
+ pointer-events: auto;
98
+ }
99
+
100
+ /* Chip / pill */
101
+ .chip {
102
+ display: inline-flex;
103
+ align-items: center;
104
+ gap: var(--space-1);
105
+ padding: 0.25rem 0.75rem;
106
+ font-size: var(--text-xs);
107
+ font-weight: 600;
108
+ border-radius: var(--radius-full);
109
+ border: 1px solid var(--border);
110
+ background: var(--surface-4);
111
+ color: var(--text-secondary);
112
+ cursor: pointer;
113
+ transition:
114
+ background var(--duration-fast) var(--ease-out),
115
+ border-color var(--duration-fast) var(--ease-out),
116
+ color var(--duration-fast) var(--ease-out);
117
+ }
118
+ .chip:hover {
119
+ background: var(--surface-3);
120
+ border-color: var(--border-strong);
121
+ color: var(--text-primary);
122
+ }
123
+ .chip.is-active {
124
+ background: var(--accent-light);
125
+ border-color: var(--border-accent);
126
+ color: var(--text-accent);
127
+ }
128
+
129
+ /* ───────────────────────────────────
130
+ BADGES / TAGS
131
+ ─────────────────────────────────── */
132
+ .badge {
133
+ display: inline-flex;
134
+ align-items: center;
135
+ gap: var(--space-1);
136
+ padding: 0.2rem 0.6rem;
137
+ font-size: var(--text-xs);
138
+ font-weight: 700;
139
+ border-radius: var(--radius-full);
140
+ white-space: nowrap;
141
+ letter-spacing: 0.04em;
142
+ }
143
+
144
+ .badge-accent { background: var(--accent-light); color: var(--accent); border: 1px solid rgba(96,165,250,0.2); }
145
+ .badge-success { background: var(--success-light); color: var(--success); border: 1px solid rgba(16,185,129,0.2); }
146
+ .badge-warning { background: var(--warning-light); color: var(--warning); border: 1px solid rgba(245,158,11,0.2); }
147
+ .badge-danger { background: var(--danger-light); color: var(--danger); border: 1px solid rgba(239,68,68,0.2); }
148
+ .badge-muted { background: var(--surface-3); color: var(--text-muted); border: 1px solid var(--border); }
149
+ .badge-info { background: var(--info-light); color: var(--info); border: 1px solid rgba(59,130,246,0.2); }
150
+
151
+ /* Priority badges */
152
+ .badge-p0 { background: rgba(239,68,68,0.12); color: var(--p0); border: 1px solid rgba(239,68,68,0.2); }
153
+ .badge-p1 { background: rgba(245,158,11,0.12); color: var(--p1); border: 1px solid rgba(245,158,11,0.2); }
154
+ .badge-p2 { background: var(--accent-light); color: var(--accent); border: 1px solid rgba(99,102,241,0.2); }
155
+ .badge-p3 { background: var(--surface-3); color: var(--text-muted); border: 1px solid var(--border); }
156
+
157
+ /* Status badges */
158
+ .status-pending { background: rgba(245,158,11,0.1); color: var(--warning); border: 1px solid rgba(245,158,11,0.15); }
159
+ .status-in_progress { background: var(--info-light); color: var(--info); border: 1px solid rgba(59,130,246,0.2); }
160
+ .status-in_review { background: var(--accent-light); color: var(--text-accent); border: 1px solid rgba(99,102,241,0.2); }
161
+ .status-blocked { background: var(--danger-light); color: var(--danger); border: 1px solid rgba(239,68,68,0.2); }
162
+ .status-completed { background: var(--success-light); color: var(--success); border: 1px solid rgba(16,185,129,0.2); }
163
+ .status-cancelled { background: var(--surface-3); color: var(--text-muted); border: 1px solid var(--border); }
164
+
165
+ /* ── Glass Card ── */
166
+ .glass-card {
167
+ background: var(--glass-bg);
168
+ backdrop-filter: blur(var(--glass-blur));
169
+ -webkit-backdrop-filter: blur(var(--glass-blur));
170
+ border: 1px solid var(--glass-border);
171
+ border-radius: var(--radius-xl);
172
+ box-shadow: var(--glass-shadow), var(--glass-inner);
173
+ }
174
+ .glass-card:hover {
175
+ background: var(--glass-bg-strong);
176
+ border-color: var(--glass-border-hover);
177
+ box-shadow: var(--shadow-md);
178
+ }
179
+
180
+ /* ───────────────────────────────────
181
+ CARDS
182
+ ─────────────────────────────────── */
183
+ .card {
184
+ background: var(--glass-bg);
185
+ backdrop-filter: blur(var(--glass-blur));
186
+ -webkit-backdrop-filter: blur(var(--glass-blur));
187
+ border: 1px solid var(--glass-border);
188
+ border-radius: var(--radius-lg);
189
+ box-shadow: var(--glass-shadow), var(--glass-inner);
190
+ transition:
191
+ border-color var(--duration-base) var(--ease-out),
192
+ box-shadow var(--duration-base) var(--ease-out),
193
+ background var(--duration-base) var(--ease-out);
194
+ }
195
+ .card:hover { border-color: var(--glass-border-hover); box-shadow: var(--shadow-md); }
196
+
197
+ /* Fallback for browsers without backdrop-filter */
198
+ @supports not (backdrop-filter: blur(1px)) {
199
+ .card, .glass-card, .kpi-card, .chart-card, .task-card, .project-card {
200
+ background: var(--gray-800);
201
+ }
202
+ }
203
+
204
+ .card-body { padding: var(--space-5); }
205
+ .card-sm .card-body { padding: var(--space-4); }
206
+ .card-lg .card-body { padding: var(--space-6); }
207
+
208
+ /* KPI metric card */
209
+ .kpi-card {
210
+ background: var(--glass-bg);
211
+ backdrop-filter: blur(var(--glass-blur));
212
+ -webkit-backdrop-filter: blur(var(--glass-blur));
213
+ border: 1px solid var(--glass-border);
214
+ border-radius: var(--radius-lg);
215
+ padding: var(--space-5);
216
+ display: flex;
217
+ flex-direction: column;
218
+ gap: var(--space-2);
219
+ position: relative;
220
+ overflow: hidden;
221
+ transition:
222
+ border-color var(--duration-base) var(--ease-out),
223
+ box-shadow var(--duration-base) var(--ease-out);
224
+ }
225
+ .kpi-card::before {
226
+ content: '';
227
+ position: absolute;
228
+ top: 0; left: 0; right: 0;
229
+ height: 2px;
230
+ background: linear-gradient(90deg, var(--accent), transparent);
231
+ opacity: 0;
232
+ transition: opacity var(--duration-base) var(--ease-out);
233
+ }
234
+ .kpi-card:hover { border-color: var(--border-strong); box-shadow: var(--shadow-md); }
235
+ .kpi-card:hover::before { opacity: 1; }
236
+
237
+ .kpi-card.kpi-accent { border-top: 2px solid var(--accent); }
238
+ .kpi-card.kpi-success { border-top: 2px solid var(--success); }
239
+ .kpi-card.kpi-warning { border-top: 2px solid var(--warning); }
240
+ .kpi-card.kpi-danger { border-top: 2px solid var(--danger); }
241
+
242
+ .kpi-header {
243
+ display: flex;
244
+ align-items: center;
245
+ justify-content: space-between;
246
+ }
247
+
248
+ .kpi-title {
249
+ font-size: var(--text-xs);
250
+ font-weight: 700;
251
+ letter-spacing: 0.08em;
252
+ text-transform: uppercase;
253
+ color: var(--text-muted);
254
+ }
255
+
256
+ .kpi-icon {
257
+ width: 32px;
258
+ height: 32px;
259
+ border-radius: var(--radius-sm);
260
+ display: flex;
261
+ align-items: center;
262
+ justify-content: center;
263
+ }
264
+ .kpi-icon.accent { background: var(--accent-light); color: var(--accent); }
265
+ .kpi-icon.success { background: var(--success-light); color: var(--success); }
266
+ .kpi-icon.warning { background: var(--warning-light); color: var(--warning); }
267
+ .kpi-icon.danger { background: var(--danger-light); color: var(--danger); }
268
+
269
+ .kpi-value {
270
+ font-family: var(--font-heading);
271
+ font-size: var(--text-3xl);
272
+ font-weight: 800;
273
+ letter-spacing: -0.04em;
274
+ line-height: 1;
275
+ color: var(--text-primary);
276
+ }
277
+
278
+ .kpi-sub {
279
+ font-size: var(--text-xs);
280
+ color: var(--text-muted);
281
+ }
282
+
283
+ .kpi-trend {
284
+ display: inline-flex;
285
+ align-items: center;
286
+ gap: var(--space-1);
287
+ font-size: var(--text-xs);
288
+ font-weight: 600;
289
+ }
290
+ .kpi-trend.up { color: var(--success); }
291
+ .kpi-trend.down { color: var(--danger); }
292
+
293
+ /* Task card (kanban) */
294
+ .task-card {
295
+ background: var(--glass-bg);
296
+ backdrop-filter: blur(var(--glass-blur-sm));
297
+ -webkit-backdrop-filter: blur(var(--glass-blur-sm));
298
+ border: 1px solid var(--glass-border);
299
+ border-radius: var(--radius-md);
300
+ padding: var(--space-4);
301
+ text-align: left;
302
+ width: 100%;
303
+ cursor: pointer;
304
+ transition:
305
+ border-color var(--duration-fast) var(--ease-out),
306
+ box-shadow var(--duration-fast) var(--ease-out),
307
+ background var(--duration-fast) var(--ease-out),
308
+ transform var(--duration-fast) var(--ease-out);
309
+ position: relative;
310
+ }
311
+ .task-card:hover {
312
+ border-color: var(--glass-border-hover);
313
+ box-shadow: var(--shadow-sm);
314
+ transform: translateY(-1px);
315
+ }
316
+ .task-card.is-selected {
317
+ border-color: var(--border-accent);
318
+ background: linear-gradient(135deg, rgba(99,102,241,0.06) 0%, var(--surface-2) 100%);
319
+ box-shadow: var(--shadow-accent);
320
+ }
321
+ .task-card[data-status="blocked"] {
322
+ border-left: 3px solid var(--danger);
323
+ }
324
+ .task-card[data-status="completed"] {
325
+ opacity: 0.65;
326
+ }
327
+ .task-card[data-status="completed"]:hover {
328
+ opacity: 1;
329
+ }
330
+
331
+ .task-card-title {
332
+ font-size: var(--text-sm);
333
+ font-weight: 700;
334
+ color: var(--text-primary);
335
+ margin-bottom: var(--space-1);
336
+ display: -webkit-box;
337
+ -webkit-line-clamp: 2;
338
+ -webkit-box-orient: vertical;
339
+ overflow: hidden;
340
+ }
341
+ .task-card-id {
342
+ font-family: var(--font-mono);
343
+ font-size: 0.7rem;
344
+ color: var(--text-muted);
345
+ display: block;
346
+ margin-bottom: var(--space-2);
347
+ }
348
+ .task-card-summary {
349
+ font-size: var(--text-xs);
350
+ color: var(--text-secondary);
351
+ margin-bottom: var(--space-3);
352
+ display: -webkit-box;
353
+ -webkit-line-clamp: 2;
354
+ -webkit-box-orient: vertical;
355
+ overflow: hidden;
356
+ min-height: 2.4em;
357
+ }
358
+ .task-card-meta {
359
+ display: flex;
360
+ flex-wrap: wrap;
361
+ gap: var(--space-1);
362
+ }
363
+
364
+ /* Dragging state */
365
+ .task-card[draggable]:hover { cursor: grab; }
366
+ .task-card.is-dragging {
367
+ opacity: 0.4;
368
+ transform: rotate(2deg) scale(0.98);
369
+ }
370
+
371
+ /* ───────────────────────────────────
372
+ FORMS
373
+ ─────────────────────────────────── */
374
+ .field { display: flex; flex-direction: column; gap: var(--space-2); }
375
+
376
+ .field label {
377
+ font-size: var(--text-xs);
378
+ font-weight: 700;
379
+ letter-spacing: 0.06em;
380
+ text-transform: uppercase;
381
+ color: var(--text-muted);
382
+ }
383
+
384
+ input[type="text"],
385
+ input[type="search"],
386
+ input[type="email"],
387
+ textarea,
388
+ select {
389
+ width: 100%;
390
+ padding: 0.65rem 0.85rem;
391
+ font-size: var(--text-sm);
392
+ font-family: var(--font-ui);
393
+ color: var(--text-primary);
394
+ background: var(--surface-1);
395
+ border: 1px solid var(--border);
396
+ border-radius: var(--radius-md);
397
+ outline: none;
398
+ transition:
399
+ border-color var(--duration-fast) var(--ease-out),
400
+ box-shadow var(--duration-fast) var(--ease-out),
401
+ background var(--duration-fast) var(--ease-out);
402
+ min-height: 40px;
403
+ appearance: none;
404
+ -webkit-appearance: none;
405
+ }
406
+
407
+ input:hover, textarea:hover, select:hover {
408
+ border-color: var(--border-strong);
409
+ }
410
+
411
+ input:focus, textarea:focus, select:focus {
412
+ border-color: var(--accent);
413
+ box-shadow: 0 0 0 3px var(--accent-light);
414
+ }
415
+
416
+ textarea {
417
+ resize: vertical;
418
+ min-height: 80px;
419
+ line-height: 1.5;
420
+ }
421
+
422
+ select {
423
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%2394A3B8' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
424
+ background-repeat: no-repeat;
425
+ background-position: right 12px center;
426
+ padding-right: 36px;
427
+ }
428
+
429
+ /* Checkbox custom */
430
+ input[type="checkbox"] {
431
+ width: 18px;
432
+ height: 18px;
433
+ min-height: 18px;
434
+ min-width: 18px;
435
+ border-radius: var(--radius-xs);
436
+ border: 2px solid var(--border-strong);
437
+ background: var(--surface-1);
438
+ cursor: pointer;
439
+ accent-color: var(--accent);
440
+ }
441
+
442
+ .checkbox-field {
443
+ display: flex;
444
+ align-items: center;
445
+ gap: var(--space-3);
446
+ }
447
+ .checkbox-field label {
448
+ font-size: var(--text-sm);
449
+ font-weight: 500;
450
+ letter-spacing: 0;
451
+ text-transform: none;
452
+ color: var(--text-secondary);
453
+ cursor: pointer;
454
+ }
455
+
456
+ .field-row {
457
+ display: grid;
458
+ grid-template-columns: repeat(2, 1fr);
459
+ gap: var(--space-3);
460
+ }
461
+
462
+ .form-actions {
463
+ display: flex;
464
+ gap: var(--space-3);
465
+ padding-top: var(--space-2);
466
+ }
467
+
468
+ /* Search bar */
469
+ .search-wrapper {
470
+ position: relative;
471
+ display: flex;
472
+ align-items: center;
473
+ }
474
+ .search-wrapper .search-icon {
475
+ position: absolute;
476
+ left: var(--space-3);
477
+ color: var(--text-muted);
478
+ pointer-events: none;
479
+ flex-shrink: 0;
480
+ }
481
+ .search-wrapper input {
482
+ padding-left: 2.4rem;
483
+ }
484
+ .search-wrapper .search-kbd {
485
+ position: absolute;
486
+ right: var(--space-3);
487
+ font-family: var(--font-mono);
488
+ font-size: 0.65rem;
489
+ color: var(--text-muted);
490
+ background: var(--surface-3);
491
+ padding: 2px 6px;
492
+ border-radius: var(--radius-xs);
493
+ border: 1px solid var(--border);
494
+ pointer-events: none;
495
+ }
496
+
497
+ /* ───────────────────────────────────
498
+ EMPTY STATE
499
+ ─────────────────────────────────── */
500
+ .empty-state {
501
+ display: flex;
502
+ flex-direction: column;
503
+ align-items: center;
504
+ justify-content: center;
505
+ gap: var(--space-3);
506
+ padding: var(--space-10) var(--space-6);
507
+ text-align: center;
508
+ color: var(--text-muted);
509
+ font-size: var(--text-sm);
510
+ border: 1px dashed var(--border);
511
+ border-radius: var(--radius-lg);
512
+ }
513
+ .empty-state svg { opacity: 0.4; }
514
+
515
+ /* ───────────────────────────────────
516
+ INFO ROWS
517
+ ─────────────────────────────────── */
518
+ .info-row {
519
+ padding: var(--space-3) var(--space-4);
520
+ border-radius: var(--radius-md);
521
+ background: var(--surface-4);
522
+ border: 1px solid var(--border);
523
+ display: flex;
524
+ flex-direction: column;
525
+ gap: var(--space-1);
526
+ }
527
+ .info-row .label-sm { margin-bottom: var(--space-1); }
528
+ .info-row .value {
529
+ font-size: var(--text-sm);
530
+ font-weight: 500;
531
+ font-family: var(--font-mono);
532
+ color: var(--text-primary);
533
+ overflow: hidden;
534
+ text-overflow: ellipsis;
535
+ white-space: nowrap;
536
+ }
537
+
538
+ /* ───────────────────────────────────
539
+ SECTION HEADER (dentro de vistas)
540
+ ─────────────────────────────────── */
541
+ .section-header {
542
+ display: flex;
543
+ align-items: flex-start;
544
+ justify-content: space-between;
545
+ gap: var(--space-4);
546
+ margin-bottom: var(--space-5);
547
+ }
548
+ .section-header-left { display: flex; flex-direction: column; gap: var(--space-1); }
549
+ .section-header h2, .section-header h3 { margin: 0; }
550
+ .section-subtitle { font-size: var(--text-sm); color: var(--text-secondary); }
551
+
552
+ /* ───────────────────────────────────
553
+ STACK LIST
554
+ ─────────────────────────────────── */
555
+ .stack { display: flex; flex-direction: column; gap: var(--space-2); }
556
+ .stack-sm { gap: var(--space-2); }
557
+ .stack-md { gap: var(--space-3); }
558
+ .stack-lg { gap: var(--space-4); }
559
+
560
+ /* ───────────────────────────────────
561
+ GRID LAYOUTS COMUNES
562
+ ─────────────────────────────────── */
563
+ .grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-4); }
564
+ .grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4); }
565
+ .grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-4); }
566
+ .grid-split { display: grid; grid-template-columns: 1fr 380px; gap: var(--space-4); align-items: start; }
567
+
568
+ @media (max-width: 1280px) {
569
+ .grid-4 { grid-template-columns: repeat(2, 1fr); }
570
+ .grid-3 { grid-template-columns: repeat(2, 1fr); }
571
+ .grid-split { grid-template-columns: 1fr; }
572
+ }
573
+ @media (max-width: 768px) {
574
+ .grid-4, .grid-3, .grid-2 { grid-template-columns: 1fr; }
575
+ .field-row { grid-template-columns: 1fr; }
576
+ }
577
+
578
+ /* ───────────────────────────────────
579
+ SPINNER DE CARGA
580
+ ─────────────────────────────────── */
581
+ .spinner {
582
+ width: 20px; height: 20px;
583
+ border: 2px solid var(--border-strong);
584
+ border-top-color: var(--accent);
585
+ border-radius: 50%;
586
+ animation: spin 0.7s linear infinite;
587
+ }
588
+
589
+ /* ───────────────────────────────────
590
+ THEME TOGGLE BUTTON
591
+ ─────────────────────────────────── */
592
+ .theme-toggle {
593
+ position: relative;
594
+ overflow: hidden;
595
+ color: var(--text-secondary);
596
+ }
597
+ .theme-toggle:hover {
598
+ color: var(--accent);
599
+ background: var(--accent-light);
600
+ border-color: var(--border-accent);
601
+ }
602
+ .theme-toggle svg {
603
+ transition: transform var(--duration-slow) var(--ease-out), opacity var(--duration-base);
604
+ }
605
+ .theme-toggle:hover svg {
606
+ transform: rotate(20deg) scale(1.1);
607
+ }
608
+
609
+ /* ───────────────────────────────────
610
+ FILTER BAR
611
+ ─────────────────────────────────── */
612
+ .filter-bar {
613
+ display: flex;
614
+ flex-direction: column;
615
+ gap: var(--space-3);
616
+ margin-bottom: var(--space-4);
617
+ }
618
+ .filter-controls {
619
+ display: flex;
620
+ gap: var(--space-2);
621
+ flex-wrap: wrap;
622
+ align-items: center;
623
+ }
624
+ .filter-select {
625
+ min-width: 130px;
626
+ height: 36px;
627
+ font-size: var(--text-sm);
628
+ padding: 0 2rem 0 0.75rem;
629
+ }
630
+ .filter-search {
631
+ flex: 1;
632
+ min-width: 180px;
633
+ }
634
+ .filter-search-input {
635
+ height: 36px;
636
+ font-size: var(--text-sm);
637
+ }
638
+ .filter-active-bar {
639
+ display: flex;
640
+ align-items: center;
641
+ gap: var(--space-2);
642
+ flex-wrap: wrap;
643
+ }
644
+ .filter-clear-all {
645
+ margin-left: auto;
646
+ }
647
+
648
+ /* ───────────────────────────────────
649
+ KEYBOARD HELP PANEL
650
+ ─────────────────────────────────── */
651
+ .kb-help-group {
652
+ margin-bottom: var(--space-4);
653
+ }
654
+ .kb-help-group:last-child {
655
+ margin-bottom: 0;
656
+ }
657
+ .kb-help-group-title {
658
+ font-size: var(--text-xs);
659
+ font-weight: 700;
660
+ letter-spacing: 0.1em;
661
+ text-transform: uppercase;
662
+ color: var(--text-muted);
663
+ margin-bottom: var(--space-3);
664
+ padding-bottom: var(--space-2);
665
+ border-bottom: 1px solid var(--border);
666
+ }
667
+ .kb-help-items {
668
+ display: flex;
669
+ flex-direction: column;
670
+ gap: var(--space-2);
671
+ }
672
+ .kb-help-item {
673
+ display: flex;
674
+ align-items: center;
675
+ justify-content: space-between;
676
+ gap: var(--space-4);
677
+ padding: var(--space-1) 0;
678
+ }
679
+ .kb-help-key {
680
+ display: inline-flex;
681
+ align-items: center;
682
+ gap: var(--space-1);
683
+ padding: 0.2rem 0.5rem;
684
+ background: var(--surface-3);
685
+ border: 1px solid var(--border-strong);
686
+ border-radius: var(--radius-xs);
687
+ font-family: var(--font-mono);
688
+ font-size: var(--text-xs);
689
+ font-weight: 600;
690
+ color: var(--text-primary);
691
+ min-width: 24px;
692
+ text-align: center;
693
+ justify-content: center;
694
+ white-space: nowrap;
695
+ }
696
+ .kb-help-label {
697
+ font-size: var(--text-sm);
698
+ color: var(--text-secondary);
699
+ }
700
+
701
+ /* ───────────────────────────────────
702
+ SKELETON LOADER
703
+ ─────────────────────────────────── */
704
+ .skeleton {
705
+ background: linear-gradient(90deg, var(--surface-3) 25%, var(--surface-4) 50%, var(--surface-3) 75%);
706
+ background-size: 200% 100%;
707
+ animation: skeleton-pulse 1.5s ease-in-out infinite;
708
+ border-radius: var(--radius-sm);
709
+ }
710
+ .skeleton-text {
711
+ height: 14px;
712
+ margin-bottom: var(--space-2);
713
+ }
714
+ .skeleton-text:last-child {
715
+ width: 60%;
716
+ }
717
+ .skeleton-card {
718
+ height: 120px;
719
+ border-radius: var(--radius-lg);
720
+ }
721
+ .skeleton-circle {
722
+ border-radius: 50%;
723
+ }
724
+
725
+ @keyframes skeleton-pulse {
726
+ 0% { background-position: 200% 0; }
727
+ 100% { background-position: -200% 0; }
728
+ }
729
+
730
+ /* ───────────────────────────────────
731
+ PROJECT CARDS (vista Projects)
732
+ ─────────────────────────────────── */
733
+ .projects-grid {
734
+ display: grid;
735
+ grid-template-columns: repeat(auto-fill, minmax(420px, 1fr));
736
+ gap: var(--space-4);
737
+ }
738
+ @media (max-width: 960px) {
739
+ .projects-grid { grid-template-columns: 1fr; }
740
+ }
741
+
742
+ .project-card {
743
+ background: var(--glass-bg);
744
+ backdrop-filter: blur(var(--glass-blur));
745
+ -webkit-backdrop-filter: blur(var(--glass-blur));
746
+ border: 1px solid var(--glass-border);
747
+ border-radius: var(--radius-xl);
748
+ padding: var(--space-5);
749
+ display: flex;
750
+ flex-direction: column;
751
+ gap: var(--space-4);
752
+ box-shadow: var(--glass-shadow), var(--glass-inner);
753
+ transition:
754
+ border-color var(--duration-fast) var(--ease-out),
755
+ box-shadow var(--duration-base) var(--ease-out),
756
+ transform var(--duration-base) var(--ease-out);
757
+ }
758
+ .project-card:hover {
759
+ border-color: var(--glass-border-hover);
760
+ box-shadow: var(--shadow-md);
761
+ transform: translateY(-2px);
762
+ }
763
+ .project-card.is-current {
764
+ border-color: var(--border-accent);
765
+ background: linear-gradient(135deg, var(--surface-2), rgba(99,102,241,0.04));
766
+ }
767
+ .project-card.is-unavailable {
768
+ opacity: 0.6;
769
+ }
770
+ .project-card.is-unavailable:hover {
771
+ transform: none;
772
+ box-shadow: none;
773
+ }
774
+
775
+ .project-card-header {
776
+ display: flex;
777
+ align-items: flex-start;
778
+ justify-content: space-between;
779
+ gap: var(--space-3);
780
+ }
781
+ .project-card-info { min-width: 0; flex: 1; }
782
+ .project-card-name {
783
+ font-size: var(--text-md);
784
+ font-weight: 800;
785
+ font-family: var(--font-heading);
786
+ color: var(--text-primary);
787
+ margin: 0;
788
+ }
789
+ .project-card-path {
790
+ font-size: var(--text-xs);
791
+ font-family: var(--font-mono);
792
+ color: var(--text-muted);
793
+ overflow: hidden;
794
+ text-overflow: ellipsis;
795
+ white-space: nowrap;
796
+ margin-top: var(--space-1);
797
+ }
798
+ .project-card-badges {
799
+ display: flex;
800
+ gap: var(--space-2);
801
+ flex-wrap: wrap;
802
+ flex-shrink: 0;
803
+ }
804
+
805
+ .project-metrics-grid {
806
+ display: grid;
807
+ grid-template-columns: repeat(5, 1fr);
808
+ gap: var(--space-2);
809
+ }
810
+ .project-metric {
811
+ text-align: center;
812
+ padding: var(--space-2);
813
+ background: var(--surface-3);
814
+ border-radius: var(--radius-md);
815
+ border: 1px solid var(--border);
816
+ }
817
+ .project-metric-value {
818
+ display: block;
819
+ font-family: var(--font-heading);
820
+ font-size: var(--text-lg);
821
+ font-weight: 800;
822
+ color: var(--text-primary);
823
+ line-height: 1.2;
824
+ }
825
+ .project-metric-label {
826
+ display: block;
827
+ font-size: 0.65rem;
828
+ font-weight: 600;
829
+ color: var(--text-muted);
830
+ text-transform: uppercase;
831
+ letter-spacing: 0.08em;
832
+ margin-top: var(--space-1);
833
+ }
834
+
835
+ .project-progress-track {
836
+ width: 100%;
837
+ height: 6px;
838
+ background: var(--surface-3);
839
+ border-radius: var(--radius-full);
840
+ overflow: hidden;
841
+ }
842
+ .project-progress-fill {
843
+ height: 100%;
844
+ background: linear-gradient(90deg, var(--accent), var(--success));
845
+ border-radius: var(--radius-full);
846
+ transition: width var(--duration-slow) var(--ease-out);
847
+ }
848
+
849
+ .project-card-meta {
850
+ display: flex;
851
+ gap: var(--space-4);
852
+ flex-wrap: wrap;
853
+ }
854
+
855
+ .project-card-actions {
856
+ display: flex;
857
+ gap: var(--space-2);
858
+ flex-wrap: wrap;
859
+ padding-top: var(--space-3);
860
+ border-top: 1px solid var(--border);
861
+ }
862
+
863
+ .project-card-metrics.is-disabled {
864
+ opacity: 0.4;
865
+ pointer-events: none;
866
+ }
867
+
868
+ /* ── Project card name row + info tooltip ── */
869
+ .project-card-name-row {
870
+ display: flex;
871
+ align-items: center;
872
+ gap: var(--space-2);
873
+ position: relative;
874
+ }
875
+ .project-info-btn {
876
+ display: flex;
877
+ align-items: center;
878
+ justify-content: center;
879
+ color: var(--text-muted);
880
+ opacity: 0;
881
+ transition: opacity var(--duration-fast), color var(--duration-fast);
882
+ }
883
+ .project-card:hover .project-info-btn { opacity: 1; }
884
+ .project-info-btn:hover { color: var(--accent); }
885
+ .project-info-tooltip {
886
+ position: absolute;
887
+ top: 100%;
888
+ left: 0;
889
+ z-index: var(--z-panel);
890
+ min-width: 280px;
891
+ background: var(--glass-bg-strong);
892
+ backdrop-filter: blur(var(--glass-blur-lg));
893
+ -webkit-backdrop-filter: blur(var(--glass-blur-lg));
894
+ border: 1px solid var(--glass-border);
895
+ border-radius: var(--radius-md);
896
+ padding: var(--space-3);
897
+ box-shadow: var(--shadow-lg);
898
+ margin-top: var(--space-2);
899
+ display: flex;
900
+ flex-direction: column;
901
+ gap: var(--space-1);
902
+ }
903
+ .project-info-tooltip.is-hidden { display: none; }
904
+ .tooltip-row {
905
+ display: flex;
906
+ justify-content: space-between;
907
+ gap: var(--space-3);
908
+ font-size: var(--text-xs);
909
+ }
910
+ .tooltip-label {
911
+ color: var(--text-muted);
912
+ white-space: nowrap;
913
+ }
914
+ .tooltip-value {
915
+ color: var(--text-secondary);
916
+ font-family: var(--font-mono);
917
+ text-align: right;
918
+ overflow: hidden;
919
+ text-overflow: ellipsis;
920
+ white-space: nowrap;
921
+ max-width: 200px;
922
+ }
923
+
924
+ /* ── Card indicators (health, deadline, priority) ── */
925
+ .project-card-indicators {
926
+ display: flex;
927
+ align-items: center;
928
+ gap: var(--space-3);
929
+ flex-wrap: wrap;
930
+ }
931
+ .project-indicator {
932
+ display: flex;
933
+ align-items: center;
934
+ gap: var(--space-1);
935
+ font-size: var(--text-xs);
936
+ color: var(--text-secondary);
937
+ }
938
+ .indicator-dot {
939
+ width: 8px;
940
+ height: 8px;
941
+ border-radius: 50%;
942
+ flex-shrink: 0;
943
+ }
944
+ .indicator-label {
945
+ font-weight: 600;
946
+ }
947
+
948
+ /* ── Portfolio KPIs ── */
949
+ .portfolio-kpi-grid {
950
+ display: grid;
951
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
952
+ gap: var(--space-3);
953
+ margin-bottom: var(--space-4);
954
+ }
955
+ .portfolio-kpi {
956
+ display: flex;
957
+ align-items: center;
958
+ gap: var(--space-3);
959
+ padding: var(--space-4);
960
+ }
961
+ .portfolio-kpi-icon {
962
+ display: flex;
963
+ align-items: center;
964
+ justify-content: center;
965
+ color: var(--text-muted);
966
+ flex-shrink: 0;
967
+ }
968
+ .portfolio-kpi-value {
969
+ display: block;
970
+ font-family: var(--font-heading);
971
+ font-size: var(--text-2xl);
972
+ font-weight: 800;
973
+ line-height: 1;
974
+ color: var(--text-primary);
975
+ }
976
+ .portfolio-kpi-label {
977
+ display: block;
978
+ font-size: var(--text-xs);
979
+ color: var(--text-muted);
980
+ margin-top: 2px;
981
+ }
982
+ .portfolio-kpi.kpi-danger { border-color: rgba(248,113,113,0.3); }
983
+ .portfolio-kpi.kpi-warning { border-color: rgba(250,204,21,0.3); }
984
+
985
+ /* ── Portfolio analytics ── */
986
+ .portfolio-analytics {
987
+ display: grid;
988
+ grid-template-columns: 1fr 1fr 1fr;
989
+ gap: var(--space-4);
990
+ margin-bottom: var(--space-5);
991
+ }
992
+ @media (max-width: 1100px) {
993
+ .portfolio-analytics { grid-template-columns: 1fr; }
994
+ }
995
+ .portfolio-chart, .portfolio-attention {
996
+ padding: var(--space-4);
997
+ }
998
+ .portfolio-attention {
999
+ max-height: 260px;
1000
+ overflow-y: auto;
1001
+ }
1002
+ .attention-item {
1003
+ display: flex;
1004
+ align-items: center;
1005
+ gap: var(--space-2);
1006
+ padding: var(--space-2) 0;
1007
+ border-bottom: 1px solid var(--border);
1008
+ }
1009
+ .attention-item:last-child { border-bottom: none; }
1010
+ .attention-dot {
1011
+ width: 6px;
1012
+ height: 6px;
1013
+ border-radius: 50%;
1014
+ flex-shrink: 0;
1015
+ }
1016
+
1017
+ /* ── Donut legend ── */
1018
+ .donut-legend {
1019
+ display: flex;
1020
+ flex-direction: column;
1021
+ gap: var(--space-1);
1022
+ }
1023
+ .donut-legend-item {
1024
+ display: flex;
1025
+ align-items: center;
1026
+ gap: var(--space-2);
1027
+ font-size: var(--text-xs);
1028
+ color: var(--text-secondary);
1029
+ }
1030
+ .donut-legend-dot {
1031
+ width: 8px;
1032
+ height: 8px;
1033
+ border-radius: 50%;
1034
+ flex-shrink: 0;
1035
+ }
1036
+
1037
+ /* Section header (reusable en views) */
1038
+ .section-header {
1039
+ display: flex;
1040
+ align-items: flex-start;
1041
+ justify-content: space-between;
1042
+ gap: var(--space-4);
1043
+ margin-bottom: var(--space-5);
1044
+ flex-wrap: wrap;
1045
+ }
1046
+ .section-header-left { min-width: 0; }
1047
+ .section-header-right {
1048
+ display: flex;
1049
+ align-items: center;
1050
+ gap: var(--space-2);
1051
+ flex-shrink: 0;
1052
+ }
1053
+ .eyebrow {
1054
+ font-size: var(--text-xs);
1055
+ font-weight: 700;
1056
+ letter-spacing: 0.12em;
1057
+ text-transform: uppercase;
1058
+ color: var(--accent);
1059
+ margin-bottom: var(--space-1);
1060
+ }
1061
+
1062
+ /* ───────────────────────────────────
1063
+ TAB PILLS
1064
+ ─────────────────────────────────── */
1065
+ .tab-pills {
1066
+ display: inline-flex;
1067
+ gap: 2px;
1068
+ padding: 3px;
1069
+ background: var(--glass-bg-subtle);
1070
+ backdrop-filter: blur(var(--glass-blur-sm));
1071
+ -webkit-backdrop-filter: blur(var(--glass-blur-sm));
1072
+ border: 1px solid var(--glass-border);
1073
+ border-radius: var(--radius-full);
1074
+ }
1075
+ .tab-pill {
1076
+ padding: 0.4rem 1rem;
1077
+ border: none;
1078
+ background: transparent;
1079
+ color: var(--text-secondary);
1080
+ font-family: var(--font-ui);
1081
+ font-size: var(--text-sm);
1082
+ font-weight: 600;
1083
+ border-radius: var(--radius-full);
1084
+ cursor: pointer;
1085
+ transition: all var(--duration-fast) var(--ease-out);
1086
+ white-space: nowrap;
1087
+ }
1088
+ .tab-pill:hover {
1089
+ color: var(--text-primary);
1090
+ background: var(--surface-4);
1091
+ }
1092
+ .tab-pill.is-active {
1093
+ background: var(--accent);
1094
+ color: #fff;
1095
+ box-shadow: 0 2px 8px rgba(96, 165, 250, 0.25);
1096
+ }
1097
+
1098
+ /* ───────────────────────────────────
1099
+ CHART CARD (glass)
1100
+ ─────────────────────────────────── */
1101
+ .chart-card {
1102
+ background: var(--glass-bg);
1103
+ backdrop-filter: blur(var(--glass-blur));
1104
+ -webkit-backdrop-filter: blur(var(--glass-blur));
1105
+ border: 1px solid var(--glass-border);
1106
+ box-shadow: var(--glass-shadow), var(--glass-inner);
1107
+ }