@elasticias/ui 0.0.16 → 1.0.0

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.
@@ -0,0 +1,4090 @@
1
+ /* ────────────────────────────────────────────────────────────────────
2
+ Comptoir patterns — screen-level utility classes lifted from the
3
+ prototype `screens/_screens.css` so every consumer gets the same
4
+ smart-bar / filter-pill / drawer / table / pager / detail-toolbar
5
+ / card / form-grid surface without re-implementing it.
6
+
7
+ Depends on the token CSS vars defined in `_comptoir.scss`. Apps
8
+ include both partials via the `comptoir.scss` barrel:
9
+
10
+ @use '@elasticias/ui/styles/comptoir' as *;
11
+ @use '@elasticias/ui/styles/patterns' as *;
12
+ ──────────────────────────────────────────────────────────────────── */
13
+
14
+ /* ── action-btn / action-btn-text ───────────────────────────────────
15
+ Inline-flex wrappers used by ef-toolbar, ef-datatable-actionbar,
16
+ and any consumer that wants compact action buttons. */
17
+
18
+ .action-btn {
19
+ display: inline-flex;
20
+ margin: 0.1rem;
21
+ }
22
+
23
+ .action-btn-text {
24
+ display: inline-flex;
25
+ width: inherit;
26
+ height: var(--hit);
27
+ margin: 0.1rem;
28
+ }
29
+
30
+ /* ──────────── PAGE HEAD ROW (label + title + meta + actions) ───── */
31
+
32
+ .page-head-row {
33
+ display: grid;
34
+ grid-template-columns: 1fr auto;
35
+ gap: var(--s-3);
36
+ align-items: center;
37
+ margin-bottom: var(--s-3);
38
+ }
39
+
40
+ .page-head-row h1 {
41
+ margin: 0;
42
+ font-family: 'Bricolage Grotesque', system-ui, sans-serif;
43
+ font-variation-settings:
44
+ 'opsz' 96,
45
+ 'wght' 700,
46
+ 'wdth' 80;
47
+ font-size: clamp(18px, 2vw, 22px);
48
+ // letter-spacing: -0.025em;
49
+ line-height: 1.1;
50
+ }
51
+
52
+ .page-head-row .actions {
53
+ display: flex;
54
+ gap: 8px;
55
+ align-items: center;
56
+ flex-wrap: wrap;
57
+ }
58
+
59
+ .title-meta {
60
+ display: flex;
61
+ flex-wrap: wrap;
62
+ align-items: baseline;
63
+ column-gap: 14px;
64
+ row-gap: 2px;
65
+ }
66
+
67
+ .title-meta .meta {
68
+ font-size: 12px;
69
+ color: var(--text-mute);
70
+ margin: 0;
71
+ }
72
+
73
+ /* ──────────── BUTTON (lifted from _comptoir.css) ──────────────── */
74
+
75
+ .btn {
76
+ appearance: none;
77
+ font: inherit;
78
+ font-size: 13.5px;
79
+ font-weight: 600;
80
+ line-height: 1;
81
+ padding: 10px 18px;
82
+ border-radius: var(--r-pill);
83
+ border: 1px solid transparent;
84
+ cursor: pointer;
85
+ display: inline-flex;
86
+ align-items: center;
87
+ gap: 8px;
88
+ transition: all var(--t-base) var(--ease-spring);
89
+ letter-spacing: -0.005em;
90
+ min-height: var(--hit-base);
91
+ }
92
+
93
+ .btn:active {
94
+ transform: scale(0.97);
95
+ }
96
+
97
+ /* Disabled state — the Comptoir `.btn` had none, so a disabled button
98
+ (ef-button comptoir variant or an ef-dialog action with `disabled: true`)
99
+ stayed at full colour with no affordance. Dim it and block the pointer.
100
+ `.is-loading` is excluded — that's a separate busy state with its own look. */
101
+ .btn:disabled:not(.is-loading),
102
+ .btn[disabled]:not(.is-loading) {
103
+ opacity: 0.5;
104
+ cursor: not-allowed;
105
+ box-shadow: none;
106
+ }
107
+ .btn:disabled:active,
108
+ .btn[disabled]:active {
109
+ transform: none;
110
+ }
111
+
112
+ .btn-primary {
113
+ background: var(--ink-active);
114
+ color: var(--paper);
115
+ border-color: var(--ink-active);
116
+ }
117
+
118
+ .btn-primary:hover {
119
+ transform: translateY(-1px);
120
+ box-shadow: var(--shadow-2, 0 4px 14px -4px rgba(15, 17, 21, 0.1));
121
+ }
122
+
123
+ .btn-tenant {
124
+ background: var(--tenant-500);
125
+ color: #fff;
126
+ border-color: var(--tenant-500);
127
+ }
128
+
129
+ .btn-tenant:hover {
130
+ background: var(--tenant-600);
131
+ transform: translateY(-1px);
132
+ box-shadow: 0 12px 30px -10px var(--tenant-200);
133
+ }
134
+
135
+ .btn-ghost {
136
+ background: var(--paper-alt);
137
+ color: var(--text);
138
+ border-color: var(--rule);
139
+ }
140
+
141
+ .btn-ghost:hover {
142
+ border-color: var(--text);
143
+ }
144
+
145
+ /* Brand-tinted action buttons (e.g. ef-pdf-preview footer). */
146
+ .btn-whatsapp {
147
+ background: #25d366;
148
+ color: #fff;
149
+ border-color: #25d366;
150
+ }
151
+ .btn-whatsapp:hover {
152
+ background: #1ebe5b;
153
+ border-color: #1ebe5b;
154
+ }
155
+ .btn-download {
156
+ background: #2563eb;
157
+ color: #fff;
158
+ border-color: #2563eb;
159
+ }
160
+ .btn-download:hover {
161
+ background: #1d4ed8;
162
+ border-color: #1d4ed8;
163
+ }
164
+
165
+ /* Footer layout helper — pushes this button (and the gap) to the leading
166
+ edge so following actions stay right-aligned in a flex-end footer. */
167
+ .btn-dialog-leading {
168
+ margin-inline-end: auto;
169
+ }
170
+
171
+ .btn-ico {
172
+ padding: 10px 12px;
173
+ }
174
+
175
+ .btn-sm {
176
+ font-size: 12px;
177
+ padding: 7px 14px;
178
+ min-height: 32px;
179
+ }
180
+
181
+ /* ──────────── PILL GROUP (status filters) ───────────────────────── */
182
+
183
+ .pill-group {
184
+ display: inline-flex;
185
+ background: var(--paper-alt);
186
+ border: 1px solid var(--rule);
187
+ border-radius: var(--r-pill);
188
+ padding: 4px;
189
+ gap: 2px;
190
+ }
191
+
192
+ .pill-group button {
193
+ appearance: none;
194
+ background: transparent;
195
+ border: 0;
196
+ font: inherit;
197
+ font-size: 12px;
198
+ font-weight: 600;
199
+ color: var(--text-mute);
200
+ padding: 6px 12px;
201
+ border-radius: var(--r-pill);
202
+ cursor: pointer;
203
+ transition: all var(--t-base) var(--ease-spring);
204
+ display: inline-flex;
205
+ align-items: center;
206
+ gap: 6px;
207
+ }
208
+
209
+ .pill-group button.active {
210
+ background: var(--ink-active);
211
+ color: var(--paper);
212
+ }
213
+
214
+ .pill-group button:not(.active):hover {
215
+ color: var(--text);
216
+ }
217
+
218
+ .pill-group .count {
219
+ font-family: 'JetBrains Mono', ui-monospace, monospace;
220
+ font-size: 10.5px;
221
+ font-weight: 700;
222
+ color: inherit;
223
+ opacity: 0.65;
224
+ margin-inline-start: 4px;
225
+ }
226
+
227
+ .pill-group button.active .count {
228
+ opacity: 0.75;
229
+ }
230
+
231
+ /* ──────────── CHIP ──────────────────────────────────────────────── */
232
+
233
+ .chip {
234
+ display: inline-flex;
235
+ align-items: center;
236
+ gap: 6px;
237
+ padding: 4px 10px;
238
+ border-radius: var(--r-pill);
239
+ font-size: 11px;
240
+ font-weight: 700;
241
+ letter-spacing: -0.005em;
242
+ white-space: nowrap;
243
+ }
244
+
245
+ .chip-pending {
246
+ background: var(--st-pending-bg);
247
+ color: var(--st-pending-fg);
248
+ }
249
+ .chip-processing {
250
+ background: var(--st-processing-bg);
251
+ color: var(--st-processing-fg);
252
+ }
253
+ .chip-shipped {
254
+ background: var(--st-shipped-bg);
255
+ color: var(--st-shipped-fg);
256
+ }
257
+ .chip-delivered {
258
+ background: var(--st-delivered-bg);
259
+ color: var(--st-delivered-fg);
260
+ }
261
+ .chip-cancelled {
262
+ background: var(--st-cancelled-bg);
263
+ color: var(--st-cancelled-fg);
264
+ }
265
+ .chip-refunded {
266
+ background: var(--st-refunded-bg);
267
+ color: var(--st-refunded-fg);
268
+ }
269
+ .chip-onhold {
270
+ background: var(--st-onhold-bg);
271
+ color: var(--st-onhold-fg);
272
+ }
273
+ .chip-tenant {
274
+ background: var(--tenant-100);
275
+ color: var(--tenant-700);
276
+ }
277
+ .chip-module {
278
+ background: color-mix(in srgb, var(--module) 15%, var(--paper-alt));
279
+ color: var(--module);
280
+ }
281
+
282
+ /* ──────────── AVATAR ────────────────────────────────────────────── */
283
+
284
+ .avatar {
285
+ width: 32px;
286
+ height: 32px;
287
+ border-radius: var(--r-pill);
288
+ background: var(--ink-active);
289
+ color: var(--paper);
290
+ display: grid;
291
+ place-items: center;
292
+ font-family: 'Bricolage Grotesque', system-ui, sans-serif;
293
+ font-variation-settings:
294
+ 'opsz' 12,
295
+ 'wght' 700,
296
+ 'wdth' 80;
297
+ font-size: 12px;
298
+ flex-shrink: 0;
299
+ }
300
+
301
+ .avatar.tenant {
302
+ background: var(--tenant-500);
303
+ }
304
+
305
+ .avatar.sm {
306
+ width: 28px;
307
+ height: 28px;
308
+ font-size: 11px;
309
+ }
310
+
311
+ /* ──────────── TYPOGRAPHY UTILITIES ─────────────────────────────── */
312
+
313
+ .label {
314
+ font-family: 'Manrope', system-ui, sans-serif;
315
+ font-size: 11px;
316
+ font-weight: 700;
317
+ text-transform: uppercase;
318
+ letter-spacing: 0.08em;
319
+ color: var(--text-mute);
320
+ }
321
+
322
+ .label.tenant {
323
+ color: var(--tenant-500);
324
+ }
325
+ .label.module {
326
+ color: var(--module);
327
+ }
328
+
329
+ .text-mute {
330
+ color: var(--text-mute);
331
+ }
332
+ .text-soft {
333
+ color: var(--text-soft);
334
+ }
335
+ .text-tenant {
336
+ color: var(--tenant-500);
337
+ }
338
+ .text-module {
339
+ color: var(--module);
340
+ }
341
+
342
+ .small {
343
+ font-size: 12.5px;
344
+ line-height: 1.5;
345
+ }
346
+ .micro {
347
+ font-size: 10.5px;
348
+ line-height: 1.4;
349
+ }
350
+
351
+ .mono,
352
+ .num {
353
+ font-family: 'JetBrains Mono', ui-monospace, monospace;
354
+ font-feature-settings: 'tnum', 'zero', 'ss01';
355
+ font-variant-numeric: tabular-nums slashed-zero;
356
+ }
357
+
358
+ /* ──────────── SMART BAR + FILTER ROW ─────────────────────────── */
359
+
360
+ .screen-search {
361
+ background: var(--paper-alt);
362
+ border: 1px solid var(--rule);
363
+ border-radius: var(--r-2xl);
364
+ box-shadow: var(--shadow-1, 0 1px 2px rgba(15, 17, 21, 0.04));
365
+ overflow: hidden;
366
+ margin-bottom: var(--s-3);
367
+ }
368
+
369
+ .smart-bar {
370
+ display: grid;
371
+ grid-template-columns: 1fr auto;
372
+ gap: var(--s-3);
373
+ align-items: stretch;
374
+ background: transparent;
375
+ padding: 6px 8px 6px 12px;
376
+ }
377
+
378
+ .smart-bar .search {
379
+ position: relative;
380
+ display: flex;
381
+ align-items: center;
382
+ }
383
+
384
+ .smart-bar .search::before {
385
+ content: '';
386
+ position: absolute;
387
+ inset-inline-start: 14px;
388
+ top: 50%;
389
+ transform: translateY(-50%);
390
+ width: 14px;
391
+ height: 14px;
392
+ background: currentColor;
393
+ opacity: 0.5;
394
+ mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'><circle cx='11' cy='11' r='7'/><path d='m20 20-3-3'/></svg>")
395
+ center/contain no-repeat;
396
+ }
397
+
398
+ .smart-bar .search input {
399
+ width: 100%;
400
+ font: inherit;
401
+ font-size: 14px;
402
+ background: transparent;
403
+ border: 0;
404
+ border-radius: var(--r-xl);
405
+ padding: 8px 14px 8px 38px;
406
+ color: var(--text);
407
+ outline: 0;
408
+ min-height: 36px;
409
+ letter-spacing: -0.005em;
410
+ }
411
+
412
+ .smart-bar .search input::placeholder {
413
+ color: var(--text-soft);
414
+ }
415
+
416
+ .smart-bar .search .kbd {
417
+ position: absolute;
418
+ inset-inline-end: 14px;
419
+ top: 50%;
420
+ transform: translateY(-50%);
421
+ font-family: 'JetBrains Mono', ui-monospace, monospace;
422
+ font-size: 10.5px;
423
+ font-weight: 600;
424
+ color: var(--text-mute);
425
+ background: var(--paper);
426
+ border: 1px solid var(--rule);
427
+ border-radius: 6px;
428
+ padding: 2px 7px;
429
+ line-height: 1;
430
+ }
431
+
432
+ .smart-bar .actions {
433
+ display: flex;
434
+ align-items: center;
435
+ gap: 8px;
436
+ padding-inline-end: 6px;
437
+ }
438
+
439
+ .filter-row {
440
+ display: flex;
441
+ flex-wrap: wrap;
442
+ gap: var(--s-2);
443
+ align-items: center;
444
+ padding: 8px 12px;
445
+ border-top: 1px solid var(--rule-soft, rgba(15, 17, 21, 0.05));
446
+ margin: 0;
447
+ }
448
+
449
+ .filter-row .sep {
450
+ width: 1px;
451
+ align-self: stretch;
452
+ background: var(--rule);
453
+ margin: 0 var(--s-1);
454
+ }
455
+
456
+ /* Filter pill — value-bearing button with optional caret. */
457
+
458
+ .f-pill {
459
+ appearance: none;
460
+ font: inherit;
461
+ font-size: 12.5px;
462
+ font-weight: 600;
463
+ color: var(--text);
464
+ background: var(--paper-alt);
465
+ border: 1px solid var(--rule);
466
+ border-radius: var(--r-pill);
467
+ padding: 7px 12px;
468
+ display: inline-flex;
469
+ align-items: center;
470
+ gap: 8px;
471
+ cursor: pointer;
472
+ transition: all var(--t-fast) var(--ease-out);
473
+ min-height: 32px;
474
+ letter-spacing: -0.005em;
475
+ position: relative;
476
+ }
477
+
478
+ .f-pill:hover {
479
+ border-color: var(--text);
480
+ }
481
+ .f-pill .ico {
482
+ width: 14px;
483
+ height: 14px;
484
+ opacity: 0.7;
485
+ }
486
+ .f-pill .lbl {
487
+ color: var(--text-mute);
488
+ font-weight: 600;
489
+ }
490
+ .f-pill .val {
491
+ color: var(--text);
492
+ font-weight: 700;
493
+ }
494
+ .f-pill .caret {
495
+ width: 10px;
496
+ height: 10px;
497
+ opacity: 0.5;
498
+ }
499
+
500
+ .f-pill.active {
501
+ background: var(--ink-active);
502
+ color: var(--paper);
503
+ border-color: var(--ink-active);
504
+ }
505
+
506
+ .f-pill.active .lbl,
507
+ .f-pill.active .val,
508
+ .f-pill.active .ico,
509
+ .f-pill.active .caret {
510
+ color: var(--paper);
511
+ opacity: 0.9;
512
+ }
513
+
514
+ .f-pill.tenant {
515
+ background: var(--tenant-50);
516
+ border-color: var(--tenant-200);
517
+ color: var(--tenant-700);
518
+ }
519
+
520
+ .f-pill.tenant .lbl {
521
+ color: var(--tenant-600);
522
+ }
523
+ .f-pill.tenant .val {
524
+ color: var(--tenant-700);
525
+ }
526
+
527
+ .f-pill .clear {
528
+ width: 14px;
529
+ height: 14px;
530
+ border-radius: 50%;
531
+ background: rgba(15, 17, 21, 0.08);
532
+ display: inline-grid;
533
+ place-items: center;
534
+ color: var(--text);
535
+ margin-inline-start: 2px;
536
+ flex-shrink: 0;
537
+ }
538
+
539
+ .f-pill .clear:hover {
540
+ background: rgba(15, 17, 21, 0.18);
541
+ }
542
+ .f-pill.active .clear {
543
+ background: rgba(255, 255, 255, 0.16);
544
+ color: var(--paper);
545
+ }
546
+
547
+ .f-more {
548
+ appearance: none;
549
+ font: inherit;
550
+ font-size: 12.5px;
551
+ font-weight: 600;
552
+ color: var(--text-mute);
553
+ background: transparent;
554
+ border: 1px dashed var(--rule);
555
+ border-radius: var(--r-pill);
556
+ padding: 7px 14px;
557
+ display: inline-flex;
558
+ align-items: center;
559
+ gap: 6px;
560
+ cursor: pointer;
561
+ min-height: 32px;
562
+ transition: all var(--t-fast) var(--ease-out);
563
+ }
564
+
565
+ .f-more:hover {
566
+ color: var(--text);
567
+ border-color: var(--text);
568
+ }
569
+
570
+ .f-more.open {
571
+ background: var(--paper-alt);
572
+ border-style: solid;
573
+ color: var(--text);
574
+ }
575
+
576
+ .f-clear {
577
+ appearance: none;
578
+ font: inherit;
579
+ font-size: 12px;
580
+ font-weight: 600;
581
+ color: var(--st-cancelled-fg);
582
+ background: transparent;
583
+ border: 0;
584
+ cursor: pointer;
585
+ padding: 6px 10px;
586
+ border-radius: var(--r-pill);
587
+ text-decoration: underline;
588
+ text-underline-offset: 3px;
589
+ }
590
+
591
+ .f-clear:hover {
592
+ background: var(--st-cancelled-bg);
593
+ }
594
+
595
+ /* Advanced-filter drawer */
596
+
597
+ .f-drawer {
598
+ background: var(--paper);
599
+ border-top: 1px solid var(--rule);
600
+ padding: var(--s-4) var(--s-5);
601
+ display: none;
602
+ animation: ef-drawer-in var(--t-base) var(--ease-out);
603
+ }
604
+
605
+ .f-drawer.open {
606
+ display: grid;
607
+ }
608
+
609
+ @keyframes ef-drawer-in {
610
+ from {
611
+ opacity: 0;
612
+ transform: translateY(-6px);
613
+ }
614
+ to {
615
+ opacity: 1;
616
+ transform: none;
617
+ }
618
+ }
619
+
620
+ .f-drawer .grid {
621
+ display: grid;
622
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
623
+ gap: var(--s-4);
624
+ }
625
+
626
+ .f-drawer .foot {
627
+ display: flex;
628
+ justify-content: space-between;
629
+ align-items: center;
630
+ margin-top: var(--s-5);
631
+ padding-top: var(--s-4);
632
+ border-top: 1px solid var(--rule);
633
+ gap: var(--s-3);
634
+ }
635
+
636
+ .f-drawer .foot .left {
637
+ font-size: 12px;
638
+ color: var(--text-mute);
639
+ }
640
+
641
+ /* Date-range / preset popover */
642
+
643
+ .popover {
644
+ position: absolute;
645
+ top: calc(100% + 8px);
646
+ inset-inline-start: 0;
647
+ background: var(--paper-alt);
648
+ border: 1px solid var(--rule);
649
+ border-radius: var(--r-xl);
650
+ box-shadow: var(--shadow-3, 0 16px 40px -16px rgba(15, 17, 21, 0.2));
651
+ padding: var(--s-3);
652
+ display: none;
653
+ z-index: 20;
654
+ min-width: 240px;
655
+ }
656
+
657
+ .popover.open {
658
+ display: grid;
659
+ gap: 4px;
660
+ }
661
+
662
+ .popover .preset {
663
+ display: flex;
664
+ justify-content: space-between;
665
+ align-items: center;
666
+ padding: 8px 12px;
667
+ font-size: 13px;
668
+ font-weight: 600;
669
+ color: var(--text);
670
+ border-radius: var(--r-md);
671
+ cursor: pointer;
672
+ }
673
+
674
+ .popover .preset:hover {
675
+ background: var(--paper);
676
+ }
677
+ .popover .preset.active {
678
+ background: var(--ink-active);
679
+ color: var(--paper);
680
+ }
681
+
682
+ .popover .preset .hint {
683
+ font-family: 'JetBrains Mono', ui-monospace, monospace;
684
+ font-size: 10.5px;
685
+ color: var(--text-mute);
686
+ font-weight: 500;
687
+ }
688
+
689
+ .popover .preset.active .hint {
690
+ color: rgba(255, 255, 255, 0.7);
691
+ }
692
+
693
+ .f-pill.has-pop {
694
+ position: relative;
695
+ }
696
+
697
+ /* ──────────── RESULTS BAR + TABLE WRAPPER ──────────────────────── */
698
+
699
+ .tbl-wrap {
700
+ background: var(--paper-alt);
701
+ border: 1px solid var(--rule);
702
+ border-radius: var(--r-2xl);
703
+ overflow: hidden;
704
+ }
705
+
706
+ .tbl-head {
707
+ display: flex;
708
+ justify-content: space-between;
709
+ align-items: center;
710
+ padding: var(--s-4) var(--s-5);
711
+ border-bottom: 1px solid var(--rule);
712
+ gap: var(--s-3);
713
+ flex-wrap: wrap;
714
+ }
715
+
716
+ .tbl {
717
+ width: 100%;
718
+ border-collapse: collapse;
719
+ font-size: 13.5px;
720
+ }
721
+
722
+ .tbl thead th {
723
+ text-align: start;
724
+ padding: 12px 18px;
725
+ font-size: 11px;
726
+ text-transform: uppercase;
727
+ letter-spacing: 0.06em;
728
+ font-weight: 700;
729
+ color: var(--text-mute);
730
+ border-bottom: 1px solid var(--rule);
731
+ background: var(--paper);
732
+ white-space: nowrap;
733
+ }
734
+
735
+ .tbl tbody td {
736
+ padding: 14px 18px;
737
+ border-bottom: 1px solid var(--rule-soft, rgba(15, 17, 21, 0.05));
738
+ vertical-align: middle;
739
+ }
740
+
741
+ .tbl tbody tr:last-child td {
742
+ border-bottom: 0;
743
+ }
744
+
745
+ .tbl tbody tr {
746
+ transition: background-color var(--t-fast) var(--ease-out);
747
+ cursor: pointer;
748
+ }
749
+
750
+ .tbl tbody tr:hover {
751
+ background: var(--paper);
752
+ }
753
+
754
+ .tbl .who {
755
+ display: flex;
756
+ align-items: center;
757
+ gap: 10px;
758
+ }
759
+
760
+ .tbl .ref {
761
+ font-family: 'JetBrains Mono', ui-monospace, monospace;
762
+ font-size: 12px;
763
+ font-weight: 700;
764
+ }
765
+
766
+ .tbl .num {
767
+ text-align: end;
768
+ font-feature-settings: 'tnum';
769
+ font-variant-numeric: tabular-nums;
770
+ }
771
+
772
+ .tbl .total {
773
+ font-family: 'Bricolage Grotesque', system-ui, sans-serif;
774
+ font-variation-settings:
775
+ 'opsz' 12,
776
+ 'wght' 700,
777
+ 'wdth' 85;
778
+ font-size: 14.5px;
779
+ font-feature-settings: 'tnum';
780
+ }
781
+
782
+ .tbl .stack {
783
+ display: grid;
784
+ gap: 2px;
785
+ line-height: 1.3;
786
+ }
787
+
788
+ .tbl .meta-line {
789
+ display: flex;
790
+ align-items: center;
791
+ gap: 6px;
792
+ color: var(--text-mute);
793
+ font-size: 12px;
794
+ margin-top: 2px;
795
+ }
796
+
797
+ .tbl .strong {
798
+ font-weight: 700;
799
+ color: var(--text);
800
+ letter-spacing: -0.005em;
801
+ }
802
+
803
+ /* ──────────── PAGER ─────────────────────────────────────────────── */
804
+
805
+ .pager {
806
+ display: flex;
807
+ justify-content: space-between;
808
+ align-items: center;
809
+ gap: var(--s-3);
810
+ padding: var(--s-3) var(--s-5);
811
+ border-top: 1px solid var(--rule);
812
+ background: var(--paper-alt);
813
+ }
814
+
815
+ .pager .pages {
816
+ display: flex;
817
+ align-items: center;
818
+ gap: 4px;
819
+ }
820
+
821
+ .pager .pg {
822
+ appearance: none;
823
+ font: inherit;
824
+ font-size: 12.5px;
825
+ font-weight: 600;
826
+ color: var(--text-mute);
827
+ background: transparent;
828
+ border: 0;
829
+ border-radius: var(--r-md);
830
+ min-width: 32px;
831
+ height: 32px;
832
+ padding: 0 10px;
833
+ cursor: pointer;
834
+ }
835
+
836
+ .pager .pg:hover {
837
+ background: var(--paper);
838
+ color: var(--text);
839
+ }
840
+ .pager .pg.active {
841
+ background: var(--ink-active);
842
+ color: var(--paper);
843
+ }
844
+
845
+ .pager .size {
846
+ display: flex;
847
+ align-items: center;
848
+ gap: 8px;
849
+ font-size: 12.5px;
850
+ color: var(--text-mute);
851
+ }
852
+
853
+ .pager .size select {
854
+ font: inherit;
855
+ font-size: 12.5px;
856
+ font-weight: 600;
857
+ background: var(--paper);
858
+ border: 1px solid var(--rule);
859
+ border-radius: var(--r-md);
860
+ padding: 5px 8px;
861
+ color: var(--text);
862
+ min-height: 30px;
863
+ }
864
+
865
+ /* ──────────── BULK ACTIONS BAR ─────────────────────────────────── */
866
+
867
+ .bulk {
868
+ background: var(--ink-active);
869
+ color: var(--paper);
870
+ border-radius: var(--r-pill);
871
+ padding: 6px 8px 6px 18px;
872
+ display: inline-flex;
873
+ align-items: center;
874
+ gap: var(--s-3);
875
+ font-size: 12.5px;
876
+ font-weight: 600;
877
+ box-shadow: var(--shadow-2, 0 4px 14px -4px rgba(15, 17, 21, 0.1));
878
+ }
879
+
880
+ .bulk .count {
881
+ color: rgba(255, 255, 255, 0.7);
882
+ }
883
+
884
+ .bulk .btn {
885
+ background: rgba(255, 255, 255, 0.1);
886
+ color: var(--paper);
887
+ border: 0;
888
+ min-height: 28px;
889
+ padding: 6px 12px;
890
+ font-size: 12px;
891
+ }
892
+
893
+ .bulk .btn:hover {
894
+ background: rgba(255, 255, 255, 0.2);
895
+ }
896
+
897
+ /* ──────────── EMPTY STATE ──────────────────────────────────────── */
898
+
899
+ .empty {
900
+ padding: var(--s-9) var(--s-5);
901
+ text-align: center;
902
+ color: var(--text-mute);
903
+ display: grid;
904
+ gap: var(--s-3);
905
+ justify-items: center;
906
+ }
907
+
908
+ .empty .ic {
909
+ width: 48px;
910
+ height: 48px;
911
+ border-radius: var(--r-pill);
912
+ background: var(--paper);
913
+ border: 1px dashed var(--rule);
914
+ display: grid;
915
+ place-items: center;
916
+ color: var(--text-soft);
917
+ }
918
+
919
+ .empty .title {
920
+ font-weight: 700;
921
+ color: var(--text);
922
+ font-size: 15px;
923
+ }
924
+
925
+ /* ──────────── DETAIL TOOLBAR ───────────────────────────────────── */
926
+
927
+ /* The authoritative `.detail-toolbar` base rule (flush, full-bleed square band)
928
+ lives in the "DETAIL SCREEN PRIMITIVES (Phase 7)" section below. Only the
929
+ sub-element rules (`.left` / `.ref` / `.name` / `.right`) live here. */
930
+
931
+ .detail-toolbar .left {
932
+ display: flex;
933
+ align-items: center;
934
+ gap: 10px;
935
+ flex: 1;
936
+ min-width: 0;
937
+ flex-wrap: wrap;
938
+ }
939
+
940
+ .detail-toolbar .ref {
941
+ font-family: 'JetBrains Mono', ui-monospace, monospace;
942
+ font-size: 14px;
943
+ font-weight: 700;
944
+ letter-spacing: -0.01em;
945
+ }
946
+
947
+ .detail-toolbar .name {
948
+ font-family: 'Bricolage Grotesque', system-ui, sans-serif;
949
+ font-variation-settings:
950
+ 'opsz' 12,
951
+ 'wght' 700,
952
+ 'wdth' 85;
953
+ font-size: 17px;
954
+ letter-spacing: -0.015em;
955
+ }
956
+
957
+ .detail-toolbar .right {
958
+ display: flex;
959
+ align-items: center;
960
+ gap: 6px;
961
+ flex-wrap: wrap;
962
+ margin-inline-start: auto;
963
+ }
964
+
965
+ /* ──────────── DETAIL GRID + CARD ───────────────────────────────── */
966
+
967
+ .detail-grid {
968
+ display: grid;
969
+ grid-template-columns: minmax(0, 1fr);
970
+ gap: var(--s-3);
971
+ }
972
+
973
+ @media (min-width: 1100px) {
974
+ .detail-grid {
975
+ grid-template-columns: minmax(0, 2fr) minmax(300px, 1fr);
976
+ }
977
+ }
978
+
979
+ .detail-stack {
980
+ display: grid;
981
+ gap: var(--s-3);
982
+ align-content: start;
983
+ min-width: 0;
984
+ }
985
+
986
+ .card {
987
+ background: var(--paper-alt);
988
+ border: 1px solid var(--rule);
989
+ border-radius: var(--r-2xl);
990
+ overflow: hidden;
991
+ }
992
+
993
+ .card-head {
994
+ display: flex;
995
+ justify-content: space-between;
996
+ align-items: center;
997
+ padding: 12px 16px;
998
+ border-bottom: 1px solid var(--rule-soft, rgba(15, 17, 21, 0.05));
999
+ gap: var(--s-3);
1000
+ }
1001
+
1002
+ .card-head .title {
1003
+ font-family: 'Bricolage Grotesque', system-ui, sans-serif;
1004
+ font-variation-settings:
1005
+ 'opsz' 12,
1006
+ 'wght' 700,
1007
+ 'wdth' 88;
1008
+ font-size: 14.5px;
1009
+ letter-spacing: -0.01em;
1010
+ }
1011
+
1012
+ .card-head .meta {
1013
+ font-size: 11.5px;
1014
+ color: var(--text-mute);
1015
+ font-weight: 600;
1016
+ }
1017
+
1018
+ .card-body {
1019
+ padding: 16px;
1020
+ display: grid;
1021
+ gap: var(--s-3);
1022
+ }
1023
+
1024
+ .card-foot {
1025
+ padding: 10px 16px;
1026
+ border-top: 1px solid var(--rule-soft, rgba(15, 17, 21, 0.05));
1027
+ display: flex;
1028
+ justify-content: space-between;
1029
+ align-items: center;
1030
+ gap: var(--s-3);
1031
+ background: var(--paper);
1032
+ }
1033
+
1034
+ /* ──────────── FORM GRID + FIELD ────────────────────────────────── */
1035
+
1036
+ .form-grid {
1037
+ display: grid;
1038
+ gap: var(--s-3);
1039
+ grid-template-columns: repeat(12, minmax(0, 1fr));
1040
+ }
1041
+
1042
+ .form-grid > .col-1 {
1043
+ grid-column: span 1;
1044
+ }
1045
+ .form-grid > .col-2 {
1046
+ grid-column: span 2;
1047
+ }
1048
+ .form-grid > .col-3 {
1049
+ grid-column: span 3;
1050
+ }
1051
+ .form-grid > .col-4 {
1052
+ grid-column: span 4;
1053
+ }
1054
+ .form-grid > .col-6 {
1055
+ grid-column: span 6;
1056
+ }
1057
+ .form-grid > .col-12 {
1058
+ grid-column: span 12;
1059
+ }
1060
+
1061
+ @media (max-width: 720px) {
1062
+ .form-grid > [class^='col-'] {
1063
+ grid-column: span 12;
1064
+ }
1065
+ }
1066
+
1067
+ .field {
1068
+ display: grid;
1069
+ gap: 4px;
1070
+ }
1071
+
1072
+ .field label {
1073
+ font-size: 11px;
1074
+ font-weight: 700;
1075
+ color: var(--text-mute);
1076
+ text-transform: uppercase;
1077
+ letter-spacing: 0.06em;
1078
+ }
1079
+
1080
+ .field label .req {
1081
+ color: var(--st-cancelled-fg);
1082
+ margin-inline-start: 2px;
1083
+ }
1084
+
1085
+ .field .hint {
1086
+ font-size: 11px;
1087
+ color: var(--text-soft);
1088
+ margin-top: 2px;
1089
+ }
1090
+
1091
+ /* ──────────── BANNER ───────────────────────────────────────────── */
1092
+
1093
+ .banner {
1094
+ display: flex;
1095
+ align-items: center;
1096
+ gap: 10px;
1097
+ padding: 10px 14px;
1098
+ background: var(--st-pending-bg);
1099
+ color: var(--st-pending-fg);
1100
+ border-radius: var(--r-xl);
1101
+ font-size: 12.5px;
1102
+ font-weight: 600;
1103
+ }
1104
+
1105
+ .banner.ok {
1106
+ background: var(--st-delivered-bg);
1107
+ color: var(--st-delivered-fg);
1108
+ }
1109
+
1110
+ /* ──────────── PULSE ────────────────────────────────────────────── */
1111
+
1112
+ .pulse {
1113
+ display: inline-flex;
1114
+ align-items: center;
1115
+ gap: 6px;
1116
+ font-size: 11.5px;
1117
+ font-weight: 600;
1118
+ color: var(--text-mute);
1119
+ }
1120
+
1121
+ .pulse .dot {
1122
+ width: 8px;
1123
+ height: 8px;
1124
+ background: var(--st-delivered-fg);
1125
+ border-radius: 50%;
1126
+ position: relative;
1127
+ }
1128
+
1129
+ .pulse .dot::after {
1130
+ content: '';
1131
+ position: absolute;
1132
+ inset: 0;
1133
+ border-radius: 50%;
1134
+ background: var(--st-delivered-fg);
1135
+ opacity: 0.5;
1136
+ animation: ef-pulse 1.6s ease-out infinite;
1137
+ }
1138
+
1139
+ @keyframes ef-pulse {
1140
+ from {
1141
+ transform: scale(1);
1142
+ opacity: 0.5;
1143
+ }
1144
+ to {
1145
+ transform: scale(2.6);
1146
+ opacity: 0;
1147
+ }
1148
+ }
1149
+
1150
+ /* ──────────── BOOLEAN INDICATOR ────────────────────────────────── */
1151
+
1152
+ .bool {
1153
+ display: inline-flex;
1154
+ align-items: center;
1155
+ gap: 6px;
1156
+ font-size: 12px;
1157
+ font-weight: 600;
1158
+ color: var(--text-mute);
1159
+ }
1160
+
1161
+ .bool .dot {
1162
+ width: 8px;
1163
+ height: 8px;
1164
+ border-radius: 50%;
1165
+ background: var(--ink-300);
1166
+ }
1167
+
1168
+ .bool.yes {
1169
+ color: var(--st-delivered-fg);
1170
+ }
1171
+ .bool.yes .dot {
1172
+ background: var(--st-delivered-fg);
1173
+ }
1174
+ .bool.no {
1175
+ color: var(--text-mute);
1176
+ }
1177
+ .bool.no .dot {
1178
+ background: var(--ink-300);
1179
+ }
1180
+
1181
+ /* ──────────── CHECKBOX (table) ─────────────────────────────────── */
1182
+
1183
+ .cb {
1184
+ appearance: none;
1185
+ width: 16px;
1186
+ height: 16px;
1187
+ border: 1.5px solid var(--ink-300);
1188
+ border-radius: 5px;
1189
+ background: var(--paper-alt);
1190
+ cursor: pointer;
1191
+ display: inline-grid;
1192
+ place-items: center;
1193
+ transition: all var(--t-fast) var(--ease-out);
1194
+ flex-shrink: 0;
1195
+ }
1196
+
1197
+ .cb:checked {
1198
+ background: var(--ink-active);
1199
+ border-color: var(--ink-active);
1200
+ }
1201
+
1202
+ .cb:checked::after {
1203
+ content: '';
1204
+ width: 9px;
1205
+ height: 9px;
1206
+ background: var(--paper);
1207
+ mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='3.5'><polyline points='5 12 10 17 19 7'/></svg>")
1208
+ center/contain no-repeat;
1209
+ }
1210
+
1211
+ /* ──────────── DASHBOARD KPI CARDS ───────────────────────────────── */
1212
+
1213
+ .kpi-grid {
1214
+ display: grid;
1215
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
1216
+ gap: var(--s-3);
1217
+ }
1218
+
1219
+ .kpi-card {
1220
+ background: var(--paper-alt);
1221
+ border: 1px solid var(--rule);
1222
+ border-radius: var(--r-2xl);
1223
+ padding: 16px;
1224
+ display: grid;
1225
+ gap: 8px;
1226
+ align-content: start;
1227
+ }
1228
+
1229
+ .kpi-head {
1230
+ display: flex;
1231
+ justify-content: space-between;
1232
+ align-items: center;
1233
+ gap: 8px;
1234
+ }
1235
+
1236
+ .kpi-value {
1237
+ font-family: 'Bricolage Grotesque', system-ui, sans-serif;
1238
+ font-variation-settings:
1239
+ 'opsz' 96,
1240
+ 'wght' 700,
1241
+ 'wdth' 80;
1242
+ font-size: clamp(28px, 4vw, 40px);
1243
+ letter-spacing: -0.03em;
1244
+ line-height: 1;
1245
+ font-feature-settings: 'tnum';
1246
+ color: var(--text);
1247
+ }
1248
+
1249
+ .kpi-value .unit {
1250
+ font-family: 'JetBrains Mono', ui-monospace, monospace;
1251
+ font-size: 0.4em;
1252
+ font-weight: 500;
1253
+ color: var(--text-mute);
1254
+ margin-inline-start: 6px;
1255
+ letter-spacing: 0;
1256
+ }
1257
+
1258
+ .spark {
1259
+ width: 100%;
1260
+ height: 28px;
1261
+ margin-top: 4px;
1262
+ }
1263
+
1264
+ .delta {
1265
+ font-family: 'JetBrains Mono', ui-monospace, monospace;
1266
+ font-size: 11px;
1267
+ font-weight: 700;
1268
+ padding: 2px 8px;
1269
+ border-radius: var(--r-pill);
1270
+ letter-spacing: 0;
1271
+ }
1272
+
1273
+ .delta.up {
1274
+ background: var(--st-delivered-bg);
1275
+ color: var(--st-delivered-fg);
1276
+ }
1277
+
1278
+ .delta.down {
1279
+ background: var(--st-cancelled-bg);
1280
+ color: var(--st-cancelled-fg);
1281
+ }
1282
+
1283
+ /* Lower-half dashboard grid (recent activity / pipeline / top lists). */
1284
+
1285
+ .dash-grid {
1286
+ display: grid;
1287
+ grid-template-columns: minmax(0, 1fr);
1288
+ gap: var(--s-3);
1289
+ }
1290
+
1291
+ @media (min-width: 1100px) {
1292
+ .dash-grid {
1293
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1294
+ }
1295
+ }
1296
+
1297
+ /* Pipeline-by-status row: chip · bar · count. */
1298
+
1299
+ .pipeline-row {
1300
+ display: grid;
1301
+ grid-template-columns: 110px 1fr 40px;
1302
+ align-items: center;
1303
+ gap: 12px;
1304
+ }
1305
+
1306
+ .pipeline-row .bar {
1307
+ position: relative;
1308
+ width: 100%;
1309
+ height: 8px;
1310
+ border-radius: var(--r-pill);
1311
+ background: var(--paper);
1312
+ overflow: hidden;
1313
+ }
1314
+
1315
+ .pipeline-row .bar-fill {
1316
+ position: absolute;
1317
+ inset: 0;
1318
+ width: var(--w, 0%);
1319
+ border-radius: var(--r-pill);
1320
+ transition: width var(--t-base) var(--ease-out);
1321
+ }
1322
+
1323
+ /* ──────────── PRODUCT THUMB ────────────────────────────────────── */
1324
+
1325
+ .thumb {
1326
+ width: 40px;
1327
+ height: 40px;
1328
+ border-radius: var(--r-md);
1329
+ background: linear-gradient(135deg, var(--tenant-100), var(--tenant-50));
1330
+ display: grid;
1331
+ place-items: center;
1332
+ color: var(--tenant-500);
1333
+ font-family: 'Bricolage Grotesque', system-ui, sans-serif;
1334
+ font-variation-settings:
1335
+ 'opsz' 12,
1336
+ 'wght' 700,
1337
+ 'wdth' 75;
1338
+ font-size: 14px;
1339
+ flex-shrink: 0;
1340
+ border: 1px solid var(--rule);
1341
+ }
1342
+
1343
+ /* ──────────── ROW ACTIONS (per-row dropdown menu) ───────────────
1344
+ Discreet trailing-cell ⋯ trigger that opens a small Comptoir-styled
1345
+ menu. Source: docs/ux-ui/erp/screens/components.html (prototype canon).
1346
+ Used by `ef-row-actions` from `@elasticias/ui`. */
1347
+
1348
+ .row-actions {
1349
+ position: relative;
1350
+ display: inline-block;
1351
+ }
1352
+
1353
+ .row-actions__trigger {
1354
+ appearance: none;
1355
+ background: var(--paper-alt);
1356
+ border: 1px solid var(--rule);
1357
+ width: 34px;
1358
+ height: 28px;
1359
+ border-radius: var(--r-pill);
1360
+ display: inline-grid;
1361
+ place-items: center;
1362
+ color: var(--text-mute);
1363
+ cursor: pointer;
1364
+ transition: all var(--t-fast) var(--ease-out);
1365
+ }
1366
+
1367
+ .row-actions__trigger:hover {
1368
+ color: var(--text);
1369
+ border-color: var(--ink-400);
1370
+ background: var(--paper);
1371
+ }
1372
+
1373
+ .row-actions__trigger:focus-visible {
1374
+ outline: 0;
1375
+ border-color: var(--tenant-400);
1376
+ box-shadow: 0 0 0 4px var(--tenant-100);
1377
+ color: var(--text);
1378
+ }
1379
+
1380
+ .row-actions__trigger.open {
1381
+ background: var(--ink-active);
1382
+ border-color: var(--ink-active);
1383
+ color: var(--paper);
1384
+ }
1385
+
1386
+ .row-actions__trigger[disabled] {
1387
+ opacity: 0.4;
1388
+ cursor: not-allowed;
1389
+ }
1390
+
1391
+ .row-actions__trigger .ico,
1392
+ .row-actions__trigger i {
1393
+ font-size: 14px;
1394
+ line-height: 1;
1395
+ }
1396
+
1397
+ .row-actions__menu {
1398
+ position: absolute;
1399
+ top: calc(100% + 8px);
1400
+ inset-inline-end: 0;
1401
+ min-width: 212px;
1402
+ background: var(--paper-alt);
1403
+ border: 1px solid var(--rule);
1404
+ border-radius: var(--r-xl);
1405
+ box-shadow: var(--shadow-3);
1406
+ padding: 6px;
1407
+ z-index: 40;
1408
+ display: none;
1409
+ animation: row-actions-menu-in var(--t-base) var(--ease-spring);
1410
+ }
1411
+
1412
+ .row-actions__menu.open {
1413
+ display: grid;
1414
+ gap: 1px;
1415
+ }
1416
+
1417
+ .row-actions__menu.left {
1418
+ inset-inline-end: auto;
1419
+ inset-inline-start: 0;
1420
+ }
1421
+
1422
+ @keyframes row-actions-menu-in {
1423
+ from {
1424
+ opacity: 0;
1425
+ transform: translateY(-4px) scale(0.98);
1426
+ }
1427
+ to {
1428
+ opacity: 1;
1429
+ transform: none;
1430
+ }
1431
+ }
1432
+
1433
+ .row-actions__item {
1434
+ appearance: none;
1435
+ background: transparent;
1436
+ border: 0;
1437
+ width: 100%;
1438
+ text-align: start;
1439
+ font: inherit;
1440
+ font-size: 13.5px;
1441
+ font-weight: 600;
1442
+ color: var(--text);
1443
+ display: flex;
1444
+ align-items: center;
1445
+ gap: 12px;
1446
+ padding: 9px 12px;
1447
+ border-radius: var(--r-md);
1448
+ cursor: pointer;
1449
+ transition: background-color var(--t-fast) var(--ease-out);
1450
+ }
1451
+
1452
+ .row-actions__item:hover,
1453
+ .row-actions__item:focus-visible {
1454
+ background: var(--paper);
1455
+ outline: 0;
1456
+ }
1457
+
1458
+ .row-actions__item[disabled] {
1459
+ opacity: 0.45;
1460
+ cursor: not-allowed;
1461
+ background: transparent;
1462
+ }
1463
+
1464
+ .row-actions__item .ico,
1465
+ .row-actions__item i {
1466
+ width: 16px;
1467
+ height: 16px;
1468
+ font-size: 16px;
1469
+ line-height: 1;
1470
+ color: var(--text-mute);
1471
+ flex-shrink: 0;
1472
+ transition: color var(--t-fast) var(--ease-out);
1473
+ display: inline-grid;
1474
+ place-items: center;
1475
+ }
1476
+
1477
+ .row-actions__item:hover .ico,
1478
+ .row-actions__item:hover i,
1479
+ .row-actions__item:focus-visible .ico,
1480
+ .row-actions__item:focus-visible i {
1481
+ color: var(--text);
1482
+ }
1483
+
1484
+ .row-actions__item .kbd {
1485
+ margin-inline-start: auto;
1486
+ font-family: 'JetBrains Mono', monospace;
1487
+ font-size: 10px;
1488
+ font-weight: 600;
1489
+ color: var(--text-mute);
1490
+ background: var(--paper);
1491
+ border: 1px solid var(--rule);
1492
+ border-radius: 6px;
1493
+ padding: 2px 6px;
1494
+ line-height: 1;
1495
+ }
1496
+
1497
+ .row-actions__item.danger {
1498
+ color: var(--st-cancelled-fg);
1499
+ }
1500
+
1501
+ .row-actions__item.danger .ico,
1502
+ .row-actions__item.danger i {
1503
+ color: var(--st-cancelled-fg);
1504
+ }
1505
+
1506
+ .row-actions__item.danger:hover,
1507
+ .row-actions__item.danger:focus-visible {
1508
+ background: var(--st-cancelled-bg);
1509
+ }
1510
+
1511
+ .row-actions__divider {
1512
+ height: 1px;
1513
+ background: var(--rule);
1514
+ margin: 4px;
1515
+ }
1516
+
1517
+ /* When a row-actions menu is open inside a data card, the table-wrap
1518
+ should let the popover escape its overflow. The data-card binds
1519
+ .has-open-menu when any descendant trigger is in `.open` state. */
1520
+ .tbl-wrap.has-open-menu {
1521
+ overflow: visible;
1522
+ }
1523
+
1524
+ /* ──────────── DATEPICKER ADVANCED ───────────────────────────────
1525
+ Smart-bar period pill: short label + bold value + popover with
1526
+ preset list + custom-range calendar. Source:
1527
+ docs/ux-ui/erp/screens/components.html.
1528
+ Used by `ef-datepicker-advanced` from `@elasticias/ui`. */
1529
+
1530
+ .dp {
1531
+ position: relative;
1532
+ display: inline-block;
1533
+ }
1534
+
1535
+ .dp__trigger {
1536
+ appearance: none;
1537
+ font: inherit;
1538
+ font-size: 13.5px;
1539
+ font-weight: 600;
1540
+ color: var(--text);
1541
+ background: var(--paper-alt);
1542
+ border: 1px solid var(--rule);
1543
+ border-radius: var(--r-pill);
1544
+ padding: 8px 14px;
1545
+ display: inline-flex;
1546
+ align-items: center;
1547
+ gap: 10px;
1548
+ cursor: pointer;
1549
+ transition: all var(--t-fast) var(--ease-out);
1550
+ min-height: 38px;
1551
+ letter-spacing: -0.005em;
1552
+ }
1553
+
1554
+ .dp__trigger:hover {
1555
+ border-color: var(--ink-400);
1556
+ }
1557
+
1558
+ .dp__trigger:focus-visible {
1559
+ outline: 0;
1560
+ border-color: var(--tenant-400);
1561
+ box-shadow: 0 0 0 4px var(--tenant-100);
1562
+ }
1563
+
1564
+ .dp__trigger.open {
1565
+ border-color: var(--ink-active);
1566
+ }
1567
+
1568
+ .dp__trigger .ico,
1569
+ .dp__trigger > i {
1570
+ font-size: 15px;
1571
+ line-height: 1;
1572
+ color: var(--text-mute);
1573
+ }
1574
+
1575
+ .dp__trigger .lbl {
1576
+ color: var(--text-mute);
1577
+ font-weight: 600;
1578
+ }
1579
+ .dp__trigger .val {
1580
+ color: var(--text);
1581
+ font-weight: 700;
1582
+ }
1583
+
1584
+ .dp__trigger .caret,
1585
+ .dp__trigger > i.caret {
1586
+ font-size: 11px;
1587
+ line-height: 1;
1588
+ color: var(--text-mute);
1589
+ transition: transform var(--t-base) var(--ease-spring);
1590
+ }
1591
+
1592
+ .dp__trigger.open .caret {
1593
+ transform: rotate(180deg);
1594
+ }
1595
+
1596
+ /* ── Field mode (form control) ──────────────────────────────────────
1597
+ `mode="field"` turns the picker into a Comptoir form control. The
1598
+ `.dp--field` wrapper fills its column; `.dp__field-trigger` is the
1599
+ bordered field box (matching ef-select / ef-input height + radius)
1600
+ that replaces the rounded pill. */
1601
+
1602
+ .dp--field {
1603
+ display: block;
1604
+ width: 100%;
1605
+ }
1606
+
1607
+ .dp__field-trigger {
1608
+ appearance: none;
1609
+ font: inherit;
1610
+ font-size: 13.5px;
1611
+ font-weight: 600;
1612
+ color: var(--text);
1613
+ background: var(--paper-alt);
1614
+ border: 1px solid var(--rule);
1615
+ border-radius: var(--r-md);
1616
+ padding: 0 14px;
1617
+ width: 100%;
1618
+ min-height: var(--hit-base);
1619
+ display: inline-flex;
1620
+ align-items: center;
1621
+ gap: 10px;
1622
+ cursor: pointer;
1623
+ text-align: start;
1624
+ transition: all var(--t-fast) var(--ease-out);
1625
+ letter-spacing: -0.005em;
1626
+ }
1627
+
1628
+ .dp__field-trigger:hover:not(:disabled) {
1629
+ border-color: var(--ink-400);
1630
+ }
1631
+
1632
+ .dp__field-trigger:focus-visible,
1633
+ .dp__field-trigger.open {
1634
+ outline: 0;
1635
+ border-color: var(--tenant-400);
1636
+ box-shadow: 0 0 0 3px var(--tenant-100);
1637
+ }
1638
+
1639
+ .dp__field-trigger:disabled {
1640
+ opacity: 0.6;
1641
+ cursor: not-allowed;
1642
+ }
1643
+
1644
+ .dp__field-trigger .ico {
1645
+ font-size: 15px;
1646
+ line-height: 1;
1647
+ color: var(--text-mute);
1648
+ }
1649
+
1650
+ .dp__field-trigger .val {
1651
+ flex: 1;
1652
+ min-width: 0;
1653
+ overflow: hidden;
1654
+ text-overflow: ellipsis;
1655
+ white-space: nowrap;
1656
+ color: var(--text);
1657
+ font-weight: 600;
1658
+ }
1659
+
1660
+ /* Placeholder state — muted, lighter weight, like an empty input. */
1661
+ .dp__field-trigger.is-placeholder .val {
1662
+ color: var(--text-soft);
1663
+ font-weight: 500;
1664
+ }
1665
+
1666
+ .dp__field-trigger .caret {
1667
+ font-size: 11px;
1668
+ line-height: 1;
1669
+ color: var(--text-mute);
1670
+ transition: transform var(--t-base) var(--ease-spring);
1671
+ }
1672
+
1673
+ .dp__field-trigger.open .caret {
1674
+ transform: rotate(180deg);
1675
+ }
1676
+
1677
+ /* Inline clear ✕ for the field-mode trigger (single mode only). Overlaid as a
1678
+ sibling — not nested in the trigger <button> — just left of the caret,
1679
+ vertically centred on the --hit-base trigger box. */
1680
+ .dp .ef-field-control {
1681
+ position: relative;
1682
+ }
1683
+
1684
+ .dp__field-clear {
1685
+ position: absolute;
1686
+ top: calc(var(--hit-base) / 2);
1687
+ inset-inline-end: 30px;
1688
+ transform: translateY(-50%);
1689
+ }
1690
+
1691
+ /* Invalid (touched + failing validation) — red border + halo. */
1692
+ .dp__field-trigger.is-invalid {
1693
+ border-color: var(--st-cancelled-fg);
1694
+ }
1695
+ .dp__field-trigger.is-invalid:focus-visible,
1696
+ .dp__field-trigger.is-invalid.open {
1697
+ box-shadow: 0 0 0 3px var(--st-cancelled-bg);
1698
+ }
1699
+
1700
+ .dp__panel {
1701
+ position: absolute;
1702
+ top: calc(100% + 8px);
1703
+ inset-inline-start: 0;
1704
+ min-width: 320px;
1705
+ background: var(--paper-alt);
1706
+ border: 1px solid var(--rule);
1707
+ border-radius: var(--r-xl);
1708
+ box-shadow: var(--shadow-3);
1709
+ padding: 6px;
1710
+ z-index: 40;
1711
+ display: none;
1712
+ animation: row-actions-menu-in var(--t-base) var(--ease-spring);
1713
+ }
1714
+
1715
+ .dp__panel.open {
1716
+ display: grid;
1717
+ gap: 2px;
1718
+ }
1719
+ .dp__panel.wide {
1720
+ min-width: 340px;
1721
+ }
1722
+
1723
+ .dp__view {
1724
+ display: none;
1725
+ }
1726
+ .dp__view.active {
1727
+ display: grid;
1728
+ gap: 0;
1729
+ }
1730
+
1731
+ .dp__preset {
1732
+ appearance: none;
1733
+ background: transparent;
1734
+ border: 0;
1735
+ width: 100%;
1736
+ text-align: start;
1737
+ font: inherit;
1738
+ display: flex;
1739
+ justify-content: space-between;
1740
+ align-items: center;
1741
+ gap: var(--s-4);
1742
+ padding: 11px 14px;
1743
+ border-radius: var(--r-md);
1744
+ cursor: pointer;
1745
+ font-size: 14px;
1746
+ font-weight: 600;
1747
+ color: var(--text);
1748
+ transition: background-color var(--t-fast) var(--ease-out);
1749
+ }
1750
+
1751
+ .dp__preset:hover,
1752
+ .dp__preset:focus-visible {
1753
+ background: var(--paper);
1754
+ outline: 0;
1755
+ }
1756
+
1757
+ .dp__preset .hint {
1758
+ font-family: 'JetBrains Mono', monospace;
1759
+ font-size: 11.5px;
1760
+ font-weight: 500;
1761
+ color: var(--text-mute);
1762
+ letter-spacing: -0.005em;
1763
+ }
1764
+
1765
+ .dp__preset.active {
1766
+ background: var(--ink-active);
1767
+ color: var(--paper);
1768
+ border-radius: var(--r-pill);
1769
+ }
1770
+
1771
+ .dp__preset.active .hint {
1772
+ color: rgba(255, 255, 255, 0.7);
1773
+ }
1774
+
1775
+ .dp__preset.muted {
1776
+ color: var(--text-mute);
1777
+ font-weight: 600;
1778
+ }
1779
+ .dp__preset.muted .ico-lead,
1780
+ .dp__preset.muted i.ico-lead {
1781
+ font-size: 14px;
1782
+ line-height: 1;
1783
+ opacity: 0.6;
1784
+ margin-inline-end: 8px;
1785
+ }
1786
+
1787
+ .dp__divider {
1788
+ height: 1px;
1789
+ background: var(--rule);
1790
+ margin: 4px;
1791
+ }
1792
+
1793
+ .dp__custom-head {
1794
+ display: grid;
1795
+ grid-template-columns: 32px 1fr 32px;
1796
+ align-items: center;
1797
+ gap: 8px;
1798
+ padding: 6px 6px 4px;
1799
+ }
1800
+
1801
+ .dp__back,
1802
+ .dp__close {
1803
+ appearance: none;
1804
+ background: transparent;
1805
+ border: 0;
1806
+ width: 32px;
1807
+ height: 32px;
1808
+ border-radius: var(--r-md);
1809
+ display: grid;
1810
+ place-items: center;
1811
+ color: var(--text-mute);
1812
+ cursor: pointer;
1813
+ transition: all var(--t-fast) var(--ease-out);
1814
+ }
1815
+
1816
+ .dp__back:hover,
1817
+ .dp__close:hover {
1818
+ background: var(--paper);
1819
+ color: var(--text);
1820
+ }
1821
+ .dp__back i,
1822
+ .dp__close i {
1823
+ font-size: 14px;
1824
+ line-height: 1;
1825
+ }
1826
+
1827
+ .dp__custom-title {
1828
+ font-size: 12.5px;
1829
+ font-weight: 700;
1830
+ color: var(--text);
1831
+ text-align: center;
1832
+ letter-spacing: -0.005em;
1833
+ display: flex;
1834
+ justify-content: center;
1835
+ align-items: center;
1836
+ gap: 8px;
1837
+ }
1838
+
1839
+ .dp__custom-title .pulse-dot {
1840
+ width: 6px;
1841
+ height: 6px;
1842
+ border-radius: 50%;
1843
+ background: var(--tenant-500);
1844
+ }
1845
+
1846
+ .dp__cal {
1847
+ padding: 0 6px 4px;
1848
+ --band: var(--ink-100);
1849
+ }
1850
+
1851
+ .p-dark .dp__cal,
1852
+ [data-theme='dark'] .dp__cal {
1853
+ --band: rgba(255, 255, 255, 0.06);
1854
+ }
1855
+
1856
+ .dp__cal-head {
1857
+ display: flex;
1858
+ justify-content: space-between;
1859
+ align-items: center;
1860
+ padding: 6px 4px;
1861
+ gap: 8px;
1862
+ }
1863
+
1864
+ .dp__cal-nav {
1865
+ appearance: none;
1866
+ background: transparent;
1867
+ border: 0;
1868
+ width: 30px;
1869
+ height: 30px;
1870
+ border-radius: var(--r-md);
1871
+ display: grid;
1872
+ place-items: center;
1873
+ color: var(--text-mute);
1874
+ cursor: pointer;
1875
+ transition: all var(--t-fast) var(--ease-out);
1876
+ }
1877
+
1878
+ .dp__cal-nav:hover {
1879
+ background: var(--paper);
1880
+ color: var(--text);
1881
+ }
1882
+ .dp__cal-nav i {
1883
+ font-size: 14px;
1884
+ line-height: 1;
1885
+ }
1886
+
1887
+ .dp__cal-title {
1888
+ display: inline-flex;
1889
+ align-items: center;
1890
+ gap: 6px;
1891
+ }
1892
+
1893
+ .dp__cal-pick {
1894
+ appearance: none;
1895
+ background: transparent;
1896
+ border: 0;
1897
+ font: inherit;
1898
+ font-size: 14px;
1899
+ font-weight: 700;
1900
+ color: var(--text);
1901
+ letter-spacing: -0.01em;
1902
+ cursor: pointer;
1903
+ padding: 6px 10px;
1904
+ border-radius: var(--r-md);
1905
+ transition: background var(--t-fast) var(--ease-out);
1906
+ }
1907
+
1908
+ .dp__cal-pick:hover {
1909
+ background: var(--paper);
1910
+ }
1911
+
1912
+ /* Year-window range label — looks like the pick buttons but is inert. */
1913
+ .dp__cal-pick.is-static {
1914
+ cursor: default;
1915
+ }
1916
+ .dp__cal-pick.is-static:hover {
1917
+ background: transparent;
1918
+ }
1919
+
1920
+ /* Month / year picker grid — 3 columns of selectable cells, sized to
1921
+ roughly match the height of the 6-row day grid above. */
1922
+ .dp__cal-cells {
1923
+ display: grid;
1924
+ grid-template-columns: repeat(3, 1fr);
1925
+ gap: 6px;
1926
+ padding: 8px 2px;
1927
+ }
1928
+
1929
+ .dp__cal-cell {
1930
+ appearance: none;
1931
+ background: transparent;
1932
+ border: 0;
1933
+ height: 44px;
1934
+ border-radius: var(--r-md);
1935
+ font: inherit;
1936
+ font-size: 13px;
1937
+ font-weight: 600;
1938
+ color: var(--text);
1939
+ cursor: pointer;
1940
+ transition: background var(--t-fast) var(--ease-out);
1941
+ }
1942
+
1943
+ .dp__cal-cell:hover {
1944
+ background: var(--paper);
1945
+ }
1946
+
1947
+ .dp__cal-cell.today {
1948
+ box-shadow: inset 0 0 0 1.5px var(--ink-300);
1949
+ }
1950
+
1951
+ .p-dark .dp__cal-cell.today,
1952
+ [data-theme='dark'] .dp__cal-cell.today {
1953
+ box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, 0.18);
1954
+ }
1955
+
1956
+ .dp__cal-cell.active {
1957
+ background: var(--ink-active);
1958
+ color: var(--paper);
1959
+ }
1960
+ .dp__cal-cell.active:hover {
1961
+ background: var(--ink-active);
1962
+ }
1963
+
1964
+ .dp__cal-dow-row {
1965
+ display: grid;
1966
+ grid-template-columns: repeat(7, 1fr);
1967
+ padding: 0 2px 4px;
1968
+ }
1969
+
1970
+ .dp__cal-dow {
1971
+ font-family: 'JetBrains Mono', monospace;
1972
+ font-size: 10px;
1973
+ font-weight: 700;
1974
+ color: var(--text-mute);
1975
+ text-align: center;
1976
+ text-transform: uppercase;
1977
+ letter-spacing: 0.05em;
1978
+ padding: 6px 0;
1979
+ }
1980
+
1981
+ .dp__cal-grid {
1982
+ display: grid;
1983
+ grid-template-columns: repeat(7, 1fr);
1984
+ gap: 2px 0;
1985
+ padding: 0 2px;
1986
+ }
1987
+
1988
+ .dp__cal-day {
1989
+ position: relative;
1990
+ display: grid;
1991
+ place-items: center;
1992
+ height: 36px;
1993
+ background: transparent;
1994
+ border: 0;
1995
+ cursor: pointer;
1996
+ font: inherit;
1997
+ padding: 0;
1998
+ }
1999
+
2000
+ .dp__cal-day .day-num {
2001
+ display: grid;
2002
+ place-items: center;
2003
+ width: 32px;
2004
+ height: 32px;
2005
+ border-radius: var(--r-pill);
2006
+ font-size: 13px;
2007
+ font-weight: 600;
2008
+ color: var(--text);
2009
+ position: relative;
2010
+ z-index: 1;
2011
+ transition: background var(--t-fast) var(--ease-out);
2012
+ }
2013
+
2014
+ .dp__cal-day:hover:not(.in-range):not(.range-start):not(.range-end) .day-num {
2015
+ background: var(--paper);
2016
+ }
2017
+
2018
+ .dp__cal-day.out .day-num {
2019
+ color: var(--text-soft);
2020
+ font-weight: 500;
2021
+ opacity: 0.5;
2022
+ }
2023
+
2024
+ .dp__cal-day.today .day-num {
2025
+ box-shadow: inset 0 0 0 1.5px var(--ink-300);
2026
+ }
2027
+
2028
+ .p-dark .dp__cal-day.today .day-num,
2029
+ [data-theme='dark'] .dp__cal-day.today .day-num {
2030
+ box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, 0.18);
2031
+ }
2032
+
2033
+ .dp__cal-day.in-range {
2034
+ background: var(--band);
2035
+ }
2036
+ .dp__cal-day.range-start {
2037
+ background: linear-gradient(to right, transparent 50%, var(--band) 50%);
2038
+ }
2039
+ .dp__cal-day.range-end {
2040
+ background: linear-gradient(to right, var(--band) 50%, transparent 50%);
2041
+ }
2042
+ .dp__cal-day.range-start.range-end {
2043
+ background: transparent;
2044
+ }
2045
+
2046
+ .dp__cal-day.range-start .day-num,
2047
+ .dp__cal-day.range-end .day-num {
2048
+ background: var(--ink-active);
2049
+ color: var(--paper);
2050
+ box-shadow: none;
2051
+ }
2052
+
2053
+ .dp__cal-day.in-range:nth-child(7n + 1) {
2054
+ border-top-left-radius: var(--r-pill);
2055
+ border-bottom-left-radius: var(--r-pill);
2056
+ }
2057
+
2058
+ .dp__cal-day.in-range:nth-child(7n) {
2059
+ border-top-right-radius: var(--r-pill);
2060
+ border-bottom-right-radius: var(--r-pill);
2061
+ }
2062
+
2063
+ .dp__cal-day.range-start:nth-child(7n) {
2064
+ background: transparent;
2065
+ }
2066
+ .dp__cal-day.range-end:nth-child(7n + 1) {
2067
+ background: transparent;
2068
+ }
2069
+
2070
+ .dp__inputs {
2071
+ display: grid;
2072
+ grid-template-columns: 1fr 1fr;
2073
+ gap: var(--s-2);
2074
+ padding: var(--s-2) var(--s-3) 0;
2075
+ }
2076
+
2077
+ /* Single-date field mode — one full-width input instead of Du / Au. */
2078
+ .dp__inputs--single {
2079
+ grid-template-columns: 1fr;
2080
+ padding-bottom: var(--s-3);
2081
+ }
2082
+
2083
+ .dp__field {
2084
+ display: grid;
2085
+ gap: 4px;
2086
+ }
2087
+
2088
+ .dp__field label {
2089
+ font-size: 10.5px;
2090
+ font-weight: 700;
2091
+ color: var(--text-mute);
2092
+ letter-spacing: 0.06em;
2093
+ text-transform: uppercase;
2094
+ padding-inline-start: 2px;
2095
+ }
2096
+
2097
+ .dp__field input {
2098
+ font: inherit;
2099
+ font-family: 'JetBrains Mono', monospace;
2100
+ font-size: 12.5px;
2101
+ background: var(--paper);
2102
+ border: 1px solid var(--rule);
2103
+ border-radius: var(--r-md);
2104
+ padding: 7px 10px;
2105
+ color: var(--text);
2106
+ outline: 0;
2107
+ width: 100%;
2108
+ min-height: 32px;
2109
+ transition: all var(--t-fast) var(--ease-out);
2110
+ }
2111
+
2112
+ .dp__field input:focus {
2113
+ border-color: var(--tenant-400);
2114
+ box-shadow: 0 0 0 3px var(--tenant-100);
2115
+ background: var(--paper-alt);
2116
+ }
2117
+
2118
+ .dp__field input.invalid {
2119
+ border-color: var(--st-cancelled-fg);
2120
+ box-shadow: 0 0 0 3px var(--st-cancelled-bg);
2121
+ }
2122
+
2123
+ .dp__actions {
2124
+ display: flex;
2125
+ justify-content: space-between;
2126
+ align-items: center;
2127
+ gap: var(--s-2);
2128
+ padding: var(--s-3) var(--s-3) var(--s-2);
2129
+ }
2130
+
2131
+ .dp__actions .duration {
2132
+ font-family: 'JetBrains Mono', monospace;
2133
+ font-size: 11.5px;
2134
+ font-weight: 600;
2135
+ color: var(--text-mute);
2136
+ }
2137
+
2138
+ .dp__actions .duration .num {
2139
+ color: var(--text);
2140
+ font-weight: 700;
2141
+ }
2142
+ .dp__actions .right {
2143
+ display: flex;
2144
+ gap: 6px;
2145
+ }
2146
+
2147
+ /* ──────────── TOAST (ef-toast-region) ─────────────────────────────
2148
+ Region anchored top-right (or whichever edge the consumer picks)
2149
+ that stacks Comptoir-styled toasts. Source:
2150
+ docs/ux-ui/erp/screens/components.html — four severities mapped
2151
+ to the status spectrum. */
2152
+
2153
+ .toast-region {
2154
+ position: fixed;
2155
+ top: var(--s-4);
2156
+ inset-inline-end: var(--s-4);
2157
+ z-index: 1000;
2158
+ display: grid;
2159
+ gap: var(--s-2);
2160
+ align-content: start;
2161
+ pointer-events: none; /* the region itself is transparent */
2162
+ }
2163
+
2164
+ .toast-region.bottom-right {
2165
+ top: auto;
2166
+ bottom: var(--s-4);
2167
+ }
2168
+ .toast-region.bottom-left {
2169
+ top: auto;
2170
+ bottom: var(--s-4);
2171
+ inset-inline-end: auto;
2172
+ inset-inline-start: var(--s-4);
2173
+ }
2174
+ .toast-region.top-left {
2175
+ inset-inline-end: auto;
2176
+ inset-inline-start: var(--s-4);
2177
+ }
2178
+ .toast-region.top-center,
2179
+ .toast-region.bottom-center {
2180
+ inset-inline: 0;
2181
+ justify-content: center;
2182
+ }
2183
+ .toast-region.bottom-center {
2184
+ top: auto;
2185
+ bottom: var(--s-4);
2186
+ }
2187
+
2188
+ .toast {
2189
+ --toast-fg: var(--ink-active);
2190
+ --toast-bord: var(--rule);
2191
+ position: relative;
2192
+ background: var(--paper-alt);
2193
+ /* Soft 1 px frame on three sides + thick coloured bar on the leading
2194
+ edge. border-radius applies to the whole element so the bar's outer
2195
+ corners round flush with the toast outline (matches PrimeNG p-toast).
2196
+ Severities drive `--toast-fg` (bar) and `--toast-bord` (surround) via
2197
+ custom props — see `.toast.info|success|warn|error` below. */
2198
+ border: 1px solid var(--toast-bord);
2199
+ border-inline-start: 6px solid var(--toast-fg);
2200
+ border-radius: var(--r-xl);
2201
+ padding: var(--s-4);
2202
+ box-shadow: var(--shadow-2);
2203
+ display: grid;
2204
+ grid-template-columns: auto 1fr auto;
2205
+ gap: var(--s-3);
2206
+ align-items: start;
2207
+ min-width: 280px;
2208
+ max-width: 480px;
2209
+ animation: toast-in var(--t-base) var(--ease-spring);
2210
+ overflow: hidden;
2211
+ pointer-events: auto;
2212
+ }
2213
+
2214
+ @keyframes toast-in {
2215
+ from {
2216
+ opacity: 0;
2217
+ transform: translateX(20px) scale(0.98);
2218
+ }
2219
+ to {
2220
+ opacity: 1;
2221
+ transform: none;
2222
+ }
2223
+ }
2224
+
2225
+ .toast.out {
2226
+ animation: toast-out var(--t-base) var(--ease-out) forwards;
2227
+ pointer-events: none;
2228
+ }
2229
+
2230
+ @keyframes toast-out {
2231
+ from {
2232
+ opacity: 1;
2233
+ transform: none;
2234
+ max-height: 240px;
2235
+ }
2236
+ to {
2237
+ opacity: 0;
2238
+ transform: translateX(20px);
2239
+ max-height: 0;
2240
+ margin-top: calc(var(--s-2) * -1);
2241
+ padding-top: 0;
2242
+ padding-bottom: 0;
2243
+ border-top-width: 0;
2244
+ border-bottom-width: 0;
2245
+ }
2246
+ }
2247
+
2248
+ .toast__ico {
2249
+ width: 30px;
2250
+ height: 30px;
2251
+ border-radius: var(--r-pill);
2252
+ border: 1.75px solid var(--toast-fg);
2253
+ color: var(--toast-fg);
2254
+ display: grid;
2255
+ place-items: center;
2256
+ flex-shrink: 0;
2257
+ background: transparent;
2258
+ }
2259
+
2260
+ .toast__ico i,
2261
+ .toast__ico .ico {
2262
+ font-size: 14px;
2263
+ line-height: 1;
2264
+ }
2265
+
2266
+ .toast__body {
2267
+ display: grid;
2268
+ gap: 3px;
2269
+ min-width: 0;
2270
+ padding-top: 2px;
2271
+ }
2272
+
2273
+ .toast__title {
2274
+ font-size: 14px;
2275
+ font-weight: 700;
2276
+ color: var(--toast-fg);
2277
+ letter-spacing: -0.005em;
2278
+ line-height: 1.25;
2279
+ }
2280
+
2281
+ .toast__text {
2282
+ font-size: 13.5px;
2283
+ color: var(--text);
2284
+ line-height: 1.5;
2285
+ }
2286
+
2287
+ .toast__actions {
2288
+ display: flex;
2289
+ gap: 6px;
2290
+ margin-top: 8px;
2291
+ }
2292
+
2293
+ .toast__actions .btn {
2294
+ min-height: 30px;
2295
+ padding: 6px 12px;
2296
+ font-size: 12px;
2297
+ }
2298
+
2299
+ .toast__close {
2300
+ appearance: none;
2301
+ background: transparent;
2302
+ border: 0;
2303
+ width: 28px;
2304
+ height: 28px;
2305
+ border-radius: var(--r-md);
2306
+ color: var(--toast-fg);
2307
+ cursor: pointer;
2308
+ display: grid;
2309
+ place-items: center;
2310
+ flex-shrink: 0;
2311
+ opacity: 0.7;
2312
+ transition: all var(--t-fast) var(--ease-out);
2313
+ }
2314
+
2315
+ .toast__close:hover {
2316
+ background: color-mix(in srgb, var(--toast-fg) 14%, transparent);
2317
+ opacity: 1;
2318
+ }
2319
+
2320
+ .toast__close i {
2321
+ font-size: 13px;
2322
+ line-height: 1;
2323
+ }
2324
+
2325
+ .toast__progress {
2326
+ position: absolute;
2327
+ left: 0;
2328
+ right: 0;
2329
+ bottom: 0;
2330
+ height: 2px;
2331
+ background: color-mix(in srgb, var(--toast-fg) 14%, transparent);
2332
+ overflow: hidden;
2333
+ }
2334
+
2335
+ .toast__progress::after {
2336
+ content: '';
2337
+ display: block;
2338
+ height: 100%;
2339
+ width: 100%;
2340
+ background: var(--toast-fg);
2341
+ transform-origin: left center;
2342
+ animation: toast-progress var(--toast-life, 5s) linear forwards;
2343
+ }
2344
+
2345
+ @keyframes toast-progress {
2346
+ from {
2347
+ transform: scaleX(1);
2348
+ }
2349
+ to {
2350
+ transform: scaleX(0);
2351
+ }
2352
+ }
2353
+
2354
+ /* Severity variants — mapped 1:1 to the status spectrum.
2355
+ Each severity drives BOTH `--toast-fg` (the leading bar + icon + title)
2356
+ and `--toast-bord` (the soft surround) via custom properties — so the
2357
+ `border-color` shorthand isn't fighting the `border-inline-start`. */
2358
+ .toast.info {
2359
+ background: var(--st-processing-bg);
2360
+ --toast-fg: var(--st-processing-fg);
2361
+ --toast-bord: color-mix(in srgb, var(--st-processing-fg) 22%, transparent);
2362
+ }
2363
+
2364
+ .toast.success {
2365
+ background: var(--st-delivered-bg);
2366
+ --toast-fg: var(--st-delivered-fg);
2367
+ --toast-bord: color-mix(in srgb, var(--st-delivered-fg) 22%, transparent);
2368
+ }
2369
+
2370
+ .toast.warn {
2371
+ background: var(--st-pending-bg);
2372
+ --toast-fg: var(--st-pending-fg);
2373
+ --toast-bord: color-mix(in srgb, var(--st-pending-fg) 22%, transparent);
2374
+ }
2375
+
2376
+ .toast.error {
2377
+ background: var(--st-cancelled-bg);
2378
+ --toast-fg: var(--st-cancelled-fg);
2379
+ --toast-bord: color-mix(in srgb, var(--st-cancelled-fg) 22%, transparent);
2380
+ }
2381
+
2382
+ /* ──────────── DETAIL SCREEN PRIMITIVES (Phase 7) ─────────────────
2383
+ Source: docs/ux-ui/erp/screens/{order,product,clients,user}-details.html
2384
+ Used by ef-detail-toolbar, ef-card, ef-form-grid, ef-totals,
2385
+ ef-stats, ef-timeline, ef-kpi-card. */
2386
+
2387
+ /* Detail toolbar: a flush, full-bleed header band welded under the breadcrumb
2388
+ bar (`ef-app-top`, which sits outside the scroll area) at the top of every
2389
+ detail screen — opaque + square so scrolled content tucks cleanly beneath it
2390
+ (no peeking around floating-card corners).
2391
+ It sticks inside the `.ef-app-main__content` scroller (padding
2392
+ var(--s-5) top / var(--s-6) sides). The negative `margin` cancels that padding
2393
+ for an edge-to-edge band; `top: calc(-1 * var(--s-5))` cancels the scroller's
2394
+ padding-top so the bar pins FLUSH at the scrollport top when stuck (a plain
2395
+ `top: 0` leaves a padding-top-sized strip where content bleeds above the bar —
2396
+ verified). If a consuming app pads its detail-screen scroller differently,
2397
+ override `margin` + `top` to match its padding. */
2398
+ .detail-toolbar {
2399
+ position: sticky;
2400
+ top: calc(-1 * var(--s-5));
2401
+ z-index: 8;
2402
+ margin: calc(-1 * var(--s-5)) calc(-1 * var(--s-6)) 0;
2403
+ background: var(--paper-alt);
2404
+ border: 0;
2405
+ border-bottom: 1px solid var(--rule);
2406
+ border-radius: 0;
2407
+ box-shadow: 0 6px 16px -14px rgba(15, 17, 21, 0.3);
2408
+ padding: 10px var(--s-6);
2409
+ display: flex;
2410
+ align-items: center;
2411
+ gap: var(--s-3);
2412
+ flex-wrap: wrap;
2413
+ }
2414
+
2415
+ .detail-toolbar .left {
2416
+ display: flex;
2417
+ align-items: center;
2418
+ gap: 10px;
2419
+ flex: 1;
2420
+ min-width: 0;
2421
+ flex-wrap: wrap;
2422
+ }
2423
+
2424
+ .detail-toolbar .ref {
2425
+ font-family: 'JetBrains Mono', monospace;
2426
+ font-size: 14px;
2427
+ font-weight: 700;
2428
+ letter-spacing: -0.01em;
2429
+ }
2430
+
2431
+ .detail-toolbar .sep {
2432
+ color: var(--text-soft);
2433
+ }
2434
+
2435
+ .detail-toolbar .right {
2436
+ display: flex;
2437
+ align-items: center;
2438
+ gap: 6px;
2439
+ flex-wrap: wrap;
2440
+ margin-inline-start: auto;
2441
+ }
2442
+
2443
+ /* Search-screen counterpart of `.detail-toolbar`. Same left | right
2444
+ structure, but lives at the top of a list page so it doesn't
2445
+ stick — the `.title-meta` block in the left slot stays in normal
2446
+ flow above the smart-bar / data-card stack. */
2447
+ .search-toolbar {
2448
+ display: flex;
2449
+ align-items: center;
2450
+ gap: var(--s-3);
2451
+ flex-wrap: wrap;
2452
+ }
2453
+
2454
+ .search-toolbar h1 {
2455
+ margin: 0;
2456
+ font-family: 'Bricolage Grotesque', system-ui, sans-serif;
2457
+ font-variation-settings:
2458
+ 'opsz' 96,
2459
+ 'wght' 700,
2460
+ 'wdth' 80;
2461
+ font-size: clamp(18px, 2vw, 22px);
2462
+ // letter-spacing: -0.025em;
2463
+ line-height: 1.1;
2464
+ }
2465
+
2466
+ .search-toolbar .left {
2467
+ display: flex;
2468
+ align-items: center;
2469
+ gap: 10px;
2470
+ flex: 1;
2471
+ min-width: 0;
2472
+ flex-wrap: wrap;
2473
+ }
2474
+
2475
+ .search-toolbar .sep {
2476
+ color: var(--text-soft);
2477
+ }
2478
+
2479
+ .search-toolbar .right {
2480
+ display: flex;
2481
+ align-items: center;
2482
+ gap: 8px;
2483
+ flex-wrap: wrap;
2484
+ margin-inline-start: auto;
2485
+ }
2486
+
2487
+ /* Icon-only collapse below the `EfViewportService` mobile breakpoint
2488
+ (767px). Targets the Comptoir `.btn` rendered by `ef-button`, which
2489
+ wraps its label in a plain `<span>` after the `<i>` icon. The
2490
+ label's accessible name still reaches the user via pTooltip — the
2491
+ button keeps `labelKey` and `ef-button` falls back to it when no
2492
+ explicit `tooltipKey` is provided. */
2493
+ @media (max-width: 767px) {
2494
+ .detail-toolbar .right .btn > span,
2495
+ .search-toolbar .right .btn > span {
2496
+ display: none;
2497
+ }
2498
+
2499
+ /* Square up the icon-only button so the padding doesn't read as
2500
+ "missing text". Mirrors the .btn-sm vertical padding on both
2501
+ axes. */
2502
+ .detail-toolbar .right .btn,
2503
+ .search-toolbar .right .btn {
2504
+ padding: 7px;
2505
+ min-width: var(--hit-base, 32px);
2506
+ justify-content: center;
2507
+ }
2508
+ }
2509
+
2510
+ /* 2/3 + 1/3 column grid that collapses below 1100px. */
2511
+ .detail-grid {
2512
+ display: grid;
2513
+ grid-template-columns: minmax(0, 1fr);
2514
+ gap: var(--s-3);
2515
+ }
2516
+
2517
+ @media (min-width: 1100px) {
2518
+ .detail-grid {
2519
+ grid-template-columns: minmax(0, 2fr) minmax(300px, 1fr);
2520
+ }
2521
+ }
2522
+
2523
+ .detail-stack {
2524
+ display: grid;
2525
+ gap: var(--s-3);
2526
+ align-content: start;
2527
+ min-width: 0;
2528
+ }
2529
+
2530
+ /* Card structural primitive. */
2531
+ .card {
2532
+ background: var(--paper-alt);
2533
+ border: 1px solid var(--rule);
2534
+ border-radius: var(--r-2xl);
2535
+ overflow: hidden;
2536
+ }
2537
+
2538
+ .card-head {
2539
+ display: flex;
2540
+ justify-content: space-between;
2541
+ align-items: center;
2542
+ padding: 12px 16px;
2543
+ border-bottom: 1px solid var(--rule-soft);
2544
+ gap: var(--s-3);
2545
+ }
2546
+
2547
+ /* Module-tinted head (used on the order's Status card). */
2548
+ .card-head.tone-module {
2549
+ background: color-mix(in srgb, var(--module) 8%, var(--paper-alt));
2550
+ border-bottom-color: color-mix(in srgb, var(--module) 16%, var(--rule));
2551
+ }
2552
+
2553
+ .card-head .title {
2554
+ font-family: 'Bricolage Grotesque', sans-serif;
2555
+ font-variation-settings:
2556
+ 'opsz' 12,
2557
+ 'wght' 700,
2558
+ 'wdth' 88;
2559
+ font-size: 14.5px;
2560
+ letter-spacing: -0.01em;
2561
+ color: var(--text);
2562
+ }
2563
+
2564
+ .card-head .meta {
2565
+ font-size: 11.5px;
2566
+ color: var(--text-mute);
2567
+ font-weight: 600;
2568
+ }
2569
+
2570
+ .card-body {
2571
+ padding: 16px;
2572
+ display: grid;
2573
+ gap: var(--s-3);
2574
+ }
2575
+
2576
+ .card-foot {
2577
+ padding: 10px 16px;
2578
+ border-top: 1px solid var(--rule-soft);
2579
+ display: flex;
2580
+ justify-content: space-between;
2581
+ align-items: center;
2582
+ gap: var(--s-3);
2583
+ background: var(--paper);
2584
+ }
2585
+
2586
+ /* Form grid — 12 cols, collapses to single col under 720px. */
2587
+ .ef-form-grid {
2588
+ display: grid;
2589
+ gap: var(--s-3);
2590
+ grid-template-columns: repeat(12, minmax(0, 1fr));
2591
+ }
2592
+
2593
+ .ef-form-grid > .col-2 {
2594
+ grid-column: span 2;
2595
+ }
2596
+ .ef-form-grid > .col-3 {
2597
+ grid-column: span 3;
2598
+ }
2599
+ .ef-form-grid > .col-4 {
2600
+ grid-column: span 4;
2601
+ }
2602
+ .ef-form-grid > .col-6 {
2603
+ grid-column: span 6;
2604
+ }
2605
+ .ef-form-grid > .col-8 {
2606
+ grid-column: span 8;
2607
+ }
2608
+ .ef-form-grid > .col-9 {
2609
+ grid-column: span 9;
2610
+ }
2611
+ .ef-form-grid > .col-12 {
2612
+ grid-column: span 12;
2613
+ }
2614
+
2615
+ @media (max-width: 720px) {
2616
+ .ef-form-grid > [class^='col-'] {
2617
+ grid-column: span 12;
2618
+ }
2619
+ }
2620
+
2621
+ /* Field row inside a form grid. */
2622
+ .ef-field {
2623
+ display: grid;
2624
+ gap: 4px;
2625
+ }
2626
+
2627
+ .ef-field > label {
2628
+ font-size: 11px;
2629
+ font-weight: 700;
2630
+ color: var(--text-mute);
2631
+ text-transform: uppercase;
2632
+ letter-spacing: 0.06em;
2633
+ }
2634
+
2635
+ .ef-field .req {
2636
+ color: var(--st-cancelled-fg);
2637
+ margin-inline-start: 2px;
2638
+ }
2639
+
2640
+ .ef-field .read {
2641
+ font-size: 14px;
2642
+ color: var(--text);
2643
+ padding: 9px 0;
2644
+ line-height: 1.2;
2645
+ }
2646
+
2647
+ .ef-field .read.strong {
2648
+ font-weight: 700;
2649
+ }
2650
+
2651
+ /* Totals card — sidebar key-value rows with display-size grand row. */
2652
+ .totals {
2653
+ display: grid;
2654
+ gap: 6px;
2655
+ padding: 16px;
2656
+ }
2657
+
2658
+ .totals .row {
2659
+ display: flex;
2660
+ justify-content: space-between;
2661
+ align-items: baseline;
2662
+ gap: 16px;
2663
+ }
2664
+
2665
+ .totals .row .lbl {
2666
+ font-size: 12.5px;
2667
+ font-weight: 600;
2668
+ color: var(--text-mute);
2669
+ }
2670
+
2671
+ .totals .row .v {
2672
+ font-family: 'JetBrains Mono', monospace;
2673
+ font-feature-settings: 'tnum';
2674
+ font-variant-numeric: tabular-nums;
2675
+ font-weight: 700;
2676
+ font-size: 13px;
2677
+ }
2678
+
2679
+ .totals .row.discount .v {
2680
+ color: var(--st-pending-fg);
2681
+ }
2682
+
2683
+ .totals hr {
2684
+ border: 0;
2685
+ border-top: 1px solid var(--rule);
2686
+ margin: 6px 0;
2687
+ }
2688
+
2689
+ .totals .grand-row {
2690
+ padding-top: 4px;
2691
+ }
2692
+
2693
+ .totals .grand-row .lbl {
2694
+ font-family: 'Bricolage Grotesque', sans-serif;
2695
+ font-variation-settings:
2696
+ 'opsz' 12,
2697
+ 'wght' 700,
2698
+ 'wdth' 85;
2699
+ font-size: 14px;
2700
+ color: var(--text);
2701
+ text-transform: none;
2702
+ letter-spacing: 0;
2703
+ }
2704
+
2705
+ .totals .grand-row .v {
2706
+ font-family: 'Bricolage Grotesque', sans-serif;
2707
+ font-variation-settings:
2708
+ 'opsz' 96,
2709
+ 'wght' 700,
2710
+ 'wdth' 80;
2711
+ font-size: 28px;
2712
+ letter-spacing: -0.03em;
2713
+ color: var(--text);
2714
+ }
2715
+
2716
+ .totals .currency {
2717
+ font-family: 'JetBrains Mono', monospace;
2718
+ font-size: 0.35em;
2719
+ color: var(--text-mute);
2720
+ margin-inline-start: 4px;
2721
+ }
2722
+
2723
+ /* Stats card — mini-KPIs in a vertical list. */
2724
+ .stats {
2725
+ padding: 4px 16px 16px;
2726
+ display: grid;
2727
+ gap: 0;
2728
+ }
2729
+
2730
+ .stats .row {
2731
+ display: flex;
2732
+ justify-content: space-between;
2733
+ align-items: baseline;
2734
+ padding: 12px 0;
2735
+ border-bottom: 1px solid var(--rule-soft);
2736
+ gap: 16px;
2737
+ }
2738
+
2739
+ .stats .row:last-child {
2740
+ border-bottom: 0;
2741
+ }
2742
+
2743
+ .stats .row .l {
2744
+ font-size: 12.5px;
2745
+ font-weight: 600;
2746
+ color: var(--text-mute);
2747
+ }
2748
+
2749
+ .stats .row .v {
2750
+ font-family: 'JetBrains Mono', monospace;
2751
+ font-size: 13.5px;
2752
+ font-weight: 700;
2753
+ font-feature-settings: 'tnum';
2754
+ font-variant-numeric: tabular-nums;
2755
+ }
2756
+
2757
+ .stats .row .v .unit {
2758
+ color: var(--text-mute);
2759
+ font-weight: 500;
2760
+ margin-inline-start: 4px;
2761
+ font-size: 11.5px;
2762
+ }
2763
+
2764
+ .stats .row.big .v {
2765
+ font-family: 'Bricolage Grotesque', sans-serif;
2766
+ font-variation-settings:
2767
+ 'opsz' 12,
2768
+ 'wght' 700,
2769
+ 'wdth' 80;
2770
+ font-size: 22px;
2771
+ letter-spacing: -0.02em;
2772
+ }
2773
+
2774
+ /* Timeline — vertical history with done/now/upcoming dots. */
2775
+ .timeline {
2776
+ display: grid;
2777
+ gap: 10px;
2778
+ padding: 16px;
2779
+ }
2780
+
2781
+ .timeline .item {
2782
+ display: grid;
2783
+ grid-template-columns: 14px 1fr;
2784
+ gap: 10px;
2785
+ align-items: start;
2786
+ position: relative;
2787
+ }
2788
+
2789
+ .timeline .item:not(:last-child)::before {
2790
+ content: '';
2791
+ position: absolute;
2792
+ left: 6px;
2793
+ top: 16px;
2794
+ bottom: -10px;
2795
+ width: 2px;
2796
+ background: var(--rule);
2797
+ }
2798
+
2799
+ .timeline .dot {
2800
+ width: 14px;
2801
+ height: 14px;
2802
+ border-radius: 50%;
2803
+ background: var(--paper);
2804
+ border: 2px solid var(--ink-300);
2805
+ margin-top: 2px;
2806
+ }
2807
+
2808
+ .timeline .item.done .dot {
2809
+ background: var(--st-delivered-fg);
2810
+ border-color: var(--st-delivered-fg);
2811
+ }
2812
+
2813
+ .timeline .item.now .dot {
2814
+ background: var(--module);
2815
+ border-color: var(--module);
2816
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--module) 18%, transparent);
2817
+ }
2818
+
2819
+ .timeline .body .label {
2820
+ font-size: 12.5px;
2821
+ font-weight: 700;
2822
+ color: var(--text);
2823
+ }
2824
+
2825
+ .timeline .body .meta {
2826
+ font-size: 11.5px;
2827
+ color: var(--text-mute);
2828
+ margin-top: 1px;
2829
+ }
2830
+
2831
+ /* KPI tile — dashboard headline with sparkline + delta. */
2832
+ .kpi {
2833
+ background: var(--paper-alt);
2834
+ border: 1px solid var(--rule);
2835
+ border-radius: var(--r-2xl);
2836
+ padding: var(--s-5);
2837
+ display: grid;
2838
+ gap: var(--s-3);
2839
+ position: relative;
2840
+ overflow: hidden;
2841
+ }
2842
+
2843
+ .kpi-head {
2844
+ display: flex;
2845
+ justify-content: space-between;
2846
+ align-items: center;
2847
+ }
2848
+
2849
+ .kpi-value {
2850
+ font-family: 'Bricolage Grotesque', sans-serif;
2851
+ font-variation-settings:
2852
+ 'opsz' 96,
2853
+ 'wght' 700,
2854
+ 'wdth' 80;
2855
+ font-size: clamp(32px, 4.5vw, 48px);
2856
+ line-height: 1;
2857
+ letter-spacing: -0.04em;
2858
+ font-feature-settings: 'tnum';
2859
+ font-variant-numeric: tabular-nums;
2860
+ }
2861
+
2862
+ .kpi-value .unit {
2863
+ font-family: 'JetBrains Mono', monospace;
2864
+ font-size: 0.32em;
2865
+ font-weight: 500;
2866
+ color: var(--text-mute);
2867
+ letter-spacing: 0;
2868
+ margin-inline-start: 6px;
2869
+ }
2870
+
2871
+ .delta {
2872
+ font-family: 'JetBrains Mono', monospace;
2873
+ font-size: 11px;
2874
+ font-weight: 600;
2875
+ }
2876
+
2877
+ .delta.up {
2878
+ color: var(--st-delivered-fg);
2879
+ }
2880
+ .delta.down {
2881
+ color: var(--st-cancelled-fg);
2882
+ }
2883
+ .delta.flat {
2884
+ color: var(--text-mute);
2885
+ }
2886
+
2887
+ .spark {
2888
+ height: 28px;
2889
+ opacity: 0.9;
2890
+ display: block;
2891
+ width: 100%;
2892
+ }
2893
+
2894
+ .kpi-grid {
2895
+ display: grid;
2896
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
2897
+ gap: var(--s-3);
2898
+ }
2899
+
2900
+ /* ──────────── FORM LABEL (Comptoir typography) ─────────────────
2901
+ Applied by `<ef-label>` (always) and to the field-label
2902
+ convention used by raw `<label>` elements inside `.ef-field`.
2903
+ The `.is-required` modifier appends a red asterisk via `::after`. */
2904
+
2905
+ .ef-label {
2906
+ display: inline-block;
2907
+ font-family: 'Manrope', system-ui, sans-serif;
2908
+ font-size: 11px;
2909
+ font-weight: 700;
2910
+ color: var(--text-mute);
2911
+ text-transform: uppercase;
2912
+ letter-spacing: 0.06em;
2913
+ line-height: 1.4;
2914
+ margin: 0;
2915
+ }
2916
+
2917
+ .ef-label.is-required::after {
2918
+ content: '*';
2919
+ color: var(--st-cancelled-fg);
2920
+ margin-inline-start: 4px;
2921
+ font-weight: 700;
2922
+ }
2923
+
2924
+ /* ──────────── NATIVE FORM CONTROLS (Comptoir variant) ──────────────
2925
+ Shared styling for V2's native ef-input-text / ef-input-number /
2926
+ ef-textarea / ef-select / ef-checkbox components and any raw
2927
+ `<input>` / `<textarea>` / `<select>` placed inside an `.ef-field`. */
2928
+
2929
+ .ef-input,
2930
+ .ef-select-native,
2931
+ .ef-textarea,
2932
+ .ef-field input:not([type='checkbox']):not([type='radio']),
2933
+ .ef-field select,
2934
+ .ef-field textarea {
2935
+ font: inherit;
2936
+ font-size: 14px;
2937
+ /* Tight line-height + fixed height pins single-line controls to the
2938
+ canonical hit target. Without this they inherit the body's 1.5
2939
+ line-height and overflow `--hit-base` by 3-7px (and each native
2940
+ control overflows by a different amount), breaking row alignment
2941
+ with `.btn` (which sets line-height:1). See ADR-009. */
2942
+ line-height: 1.2;
2943
+ /* Own the box model so `height` includes the border (40px exactly),
2944
+ instead of depending on the consuming app's global reset. Native
2945
+ <input>/<textarea> default to content-box, so without this they
2946
+ render 2px taller than <select>/<button>. */
2947
+ box-sizing: border-box;
2948
+ padding: 0 12px;
2949
+ background: var(--paper-alt);
2950
+ border: 1px solid var(--rule);
2951
+ border-radius: var(--r-md);
2952
+ color: var(--text);
2953
+ outline: 0;
2954
+ height: var(--hit-base);
2955
+ width: 100%;
2956
+ transition:
2957
+ border-color var(--t-fast) var(--ease-out),
2958
+ box-shadow var(--t-fast) var(--ease-out);
2959
+ }
2960
+
2961
+ /* Textarea is the one multi-line exception: it must grow with its rows,
2962
+ so it overrides the fixed height back to auto and restores readable
2963
+ padding + line-height. */
2964
+ .ef-textarea,
2965
+ .ef-field textarea {
2966
+ height: auto;
2967
+ min-height: 88px;
2968
+ padding: 10px 12px;
2969
+ line-height: 1.5;
2970
+ resize: vertical;
2971
+ }
2972
+
2973
+ .ef-input:focus,
2974
+ .ef-select-native:focus,
2975
+ .ef-textarea:focus,
2976
+ .ef-field input:not([type='checkbox']):not([type='radio']):focus,
2977
+ .ef-field select:focus,
2978
+ .ef-field textarea:focus {
2979
+ border-color: var(--tenant-400);
2980
+ box-shadow: 0 0 0 4px var(--tenant-100);
2981
+ }
2982
+
2983
+ .ef-input[disabled],
2984
+ .ef-select-native[disabled],
2985
+ .ef-textarea[disabled],
2986
+ .ef-field input[disabled]:not([type='checkbox']):not([type='radio']),
2987
+ .ef-field select[disabled],
2988
+ .ef-field textarea[disabled] {
2989
+ background: var(--paper);
2990
+ color: var(--text-mute);
2991
+ cursor: not-allowed;
2992
+ }
2993
+
2994
+ .ef-input[readonly],
2995
+ .ef-textarea[readonly],
2996
+ .ef-field input[readonly],
2997
+ .ef-field textarea[readonly] {
2998
+ background: var(--paper);
2999
+ color: var(--text);
3000
+ }
3001
+
3002
+ .ef-input.is-invalid,
3003
+ .ef-select-native.is-invalid,
3004
+ .ef-textarea.is-invalid,
3005
+ .ef-field input.is-invalid,
3006
+ .ef-field select.is-invalid,
3007
+ .ef-field textarea.is-invalid {
3008
+ border-color: var(--st-cancelled-fg);
3009
+ box-shadow: 0 0 0 3px var(--st-cancelled-bg);
3010
+ }
3011
+
3012
+ /* Field-level validation message rendered under an ef-* control
3013
+ (required hint + server/validation errors). Small + danger-toned,
3014
+ matching the .is-invalid red ring (--st-cancelled-fg). */
3015
+ .text-danger {
3016
+ display: block;
3017
+ margin-top: 4px;
3018
+ font-size: 12px;
3019
+ font-weight: 500;
3020
+ line-height: 1.35;
3021
+ color: var(--st-cancelled-fg);
3022
+ }
3023
+
3024
+ /* Native select chevron — replaces the browser default. */
3025
+ .ef-select-native,
3026
+ .ef-field select {
3027
+ appearance: none;
3028
+ -webkit-appearance: none;
3029
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236c7280' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
3030
+ background-repeat: no-repeat;
3031
+ background-position: right 12px center;
3032
+ padding-inline-end: 36px;
3033
+ cursor: pointer;
3034
+ }
3035
+
3036
+ /* Native checkbox — keeps the underlying <input> for native keyboard
3037
+ handling, hidden visually; the .ef-checkbox-box gets the visuals. */
3038
+ .ef-checkbox-native {
3039
+ display: inline-flex;
3040
+ align-items: center;
3041
+ gap: 8px;
3042
+ font-size: 13.5px;
3043
+ font-weight: 600;
3044
+ color: var(--text);
3045
+ cursor: pointer;
3046
+ user-select: none;
3047
+ position: relative;
3048
+ }
3049
+
3050
+ .ef-checkbox-native input[type='checkbox'] {
3051
+ position: absolute;
3052
+ opacity: 0;
3053
+ width: 18px;
3054
+ height: 18px;
3055
+ margin: 0;
3056
+ cursor: pointer;
3057
+ }
3058
+
3059
+ .ef-checkbox-native .ef-checkbox-box {
3060
+ width: 18px;
3061
+ height: 18px;
3062
+ border: 1.5px solid var(--rule);
3063
+ border-radius: var(--r-sm);
3064
+ background: var(--paper-alt);
3065
+ display: inline-grid;
3066
+ place-items: center;
3067
+ flex-shrink: 0;
3068
+ transition: all var(--t-fast) var(--ease-out);
3069
+ }
3070
+
3071
+ .ef-checkbox-native input[type='checkbox']:checked ~ .ef-checkbox-box {
3072
+ background: var(--tenant-500);
3073
+ border-color: var(--tenant-500);
3074
+ }
3075
+
3076
+ .ef-checkbox-native input[type='checkbox']:checked ~ .ef-checkbox-box::after {
3077
+ content: '';
3078
+ width: 10px;
3079
+ height: 10px;
3080
+ background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
3081
+ background-repeat: no-repeat;
3082
+ background-position: center;
3083
+ background-size: contain;
3084
+ }
3085
+
3086
+ .ef-checkbox-native input[type='checkbox']:focus-visible ~ .ef-checkbox-box {
3087
+ border-color: var(--tenant-400);
3088
+ box-shadow: 0 0 0 4px var(--tenant-100);
3089
+ }
3090
+
3091
+ .ef-checkbox-native input[type='checkbox'][disabled] ~ .ef-checkbox-box {
3092
+ background: var(--paper);
3093
+ border-color: var(--rule);
3094
+ cursor: not-allowed;
3095
+ opacity: 0.55;
3096
+ }
3097
+
3098
+ .ef-checkbox-native input[type='checkbox'][disabled] ~ .ef-checkbox-label {
3099
+ color: var(--text-mute);
3100
+ cursor: not-allowed;
3101
+ }
3102
+
3103
+ .ef-checkbox-native input[type='checkbox'].is-invalid ~ .ef-checkbox-box {
3104
+ border-color: var(--st-cancelled-fg);
3105
+ box-shadow: 0 0 0 3px var(--st-cancelled-bg);
3106
+ }
3107
+
3108
+ .ef-checkbox-native .ef-checkbox-required {
3109
+ margin-inline-start: 4px;
3110
+ color: var(--st-cancelled-fg);
3111
+ font-weight: 700;
3112
+ }
3113
+
3114
+ /* Input group — flex wrapper around an `<input>` plus optional
3115
+ prefix / suffix addons (URL prefix, unit suffix, currency, …).
3116
+ Used by `ef-input-text` when `[prefix]` or `[suffix]` is set. */
3117
+
3118
+ .ef-input-group {
3119
+ display: flex;
3120
+ align-items: stretch;
3121
+ background: var(--paper-alt);
3122
+ border: 1px solid var(--rule);
3123
+ border-radius: var(--r-md);
3124
+ overflow: hidden;
3125
+ min-height: 40px;
3126
+ width: 100%;
3127
+ transition:
3128
+ border-color var(--t-fast) var(--ease-out),
3129
+ box-shadow var(--t-fast) var(--ease-out);
3130
+ }
3131
+
3132
+ .ef-input-group:focus-within {
3133
+ border-color: var(--tenant-400);
3134
+ box-shadow: 0 0 0 4px var(--tenant-100);
3135
+ }
3136
+
3137
+ .ef-input-group.is-invalid {
3138
+ border-color: var(--st-cancelled-fg);
3139
+ box-shadow: 0 0 0 3px var(--st-cancelled-bg);
3140
+ }
3141
+
3142
+ .ef-input-group input {
3143
+ flex: 1;
3144
+ border: 0;
3145
+ background: transparent;
3146
+ outline: 0;
3147
+ padding: 10px 12px;
3148
+ font: inherit;
3149
+ font-size: 14px;
3150
+ color: var(--text);
3151
+ min-width: 0;
3152
+ }
3153
+
3154
+ .ef-input-group .ef-input-addon {
3155
+ background: var(--paper);
3156
+ padding: 9px 12px;
3157
+ font-family: 'JetBrains Mono', monospace;
3158
+ font-size: 12.5px;
3159
+ color: var(--text-mute);
3160
+ display: flex;
3161
+ align-items: center;
3162
+ flex-shrink: 0;
3163
+ }
3164
+
3165
+ .ef-input-group .ef-input-addon.is-prefix {
3166
+ border-inline-end: 1px solid var(--rule);
3167
+ }
3168
+
3169
+ .ef-input-group .ef-input-addon.is-suffix {
3170
+ border-inline-start: 1px solid var(--rule);
3171
+ }
3172
+
3173
+ /* Stepper buttons (±) sit at either end of the group. They reuse the
3174
+ addon base so border weights stay consistent; the button override
3175
+ gives them affordance (cursor, hover/active feedback). */
3176
+ .ef-input-group button.ef-input-addon.is-stepper {
3177
+ appearance: none;
3178
+ background: var(--paper);
3179
+ border: 0;
3180
+ border-inline-end: 1px solid var(--rule);
3181
+ color: var(--text-mute);
3182
+ cursor: pointer;
3183
+ font: inherit;
3184
+ padding: 0 12px;
3185
+ display: inline-flex;
3186
+ align-items: center;
3187
+ justify-content: center;
3188
+ flex-shrink: 0;
3189
+ min-width: 36px;
3190
+ transition:
3191
+ background var(--t-fast) var(--ease-out),
3192
+ color var(--t-fast) var(--ease-out);
3193
+ }
3194
+
3195
+ .ef-input-group button.ef-input-addon.is-stepper:last-child {
3196
+ border-inline-end: 0;
3197
+ border-inline-start: 1px solid var(--rule);
3198
+ }
3199
+
3200
+ .ef-input-group button.ef-input-addon.is-stepper:hover:not([disabled]) {
3201
+ background: var(--tenant-50);
3202
+ color: var(--tenant-500);
3203
+ }
3204
+
3205
+ .ef-input-group button.ef-input-addon.is-stepper:active:not([disabled]) {
3206
+ background: var(--tenant-100);
3207
+ }
3208
+
3209
+ .ef-input-group button.ef-input-addon.is-stepper[disabled] {
3210
+ cursor: not-allowed;
3211
+ opacity: 0.4;
3212
+ }
3213
+
3214
+ .ef-input-group button.ef-input-addon.is-stepper i {
3215
+ font-size: 11px;
3216
+ }
3217
+
3218
+ /* Hide the native number spinners — we expose ours via .is-stepper.
3219
+ Note: ef-inputnumber's comptoir input is now `type="text"
3220
+ inputmode="decimal"` (comma-decimal entry), which renders no native
3221
+ spinners, so these rules are inert for it; they're kept for any
3222
+ stray `type="number"` inputs styled with .ef-input / .ef-input-group
3223
+ so the comptoir visual stays identical either way. */
3224
+ .ef-input-group input[type='number']::-webkit-outer-spin-button,
3225
+ .ef-input-group input[type='number']::-webkit-inner-spin-button,
3226
+ .ef-input input[type='number']::-webkit-outer-spin-button,
3227
+ .ef-input input[type='number']::-webkit-inner-spin-button {
3228
+ -webkit-appearance: none;
3229
+ margin: 0;
3230
+ }
3231
+
3232
+ input.ef-input[type='number'] {
3233
+ -moz-appearance: textfield;
3234
+ }
3235
+
3236
+ /* ──────────── EF-SELECT — COMPTOIR SKIN OVER P-SELECT ─────────────
3237
+ Source: docs/ux-ui/erp/screens/components.html § Section 04.
3238
+
3239
+ The wrapper applies `[styleClass]="es"` to the PrimeNG root and
3240
+ `[panelStyleClass]="es__panel"` to the overlay; these rules then
3241
+ target both our class names AND PrimeNG's stable internal classes
3242
+ (.p-select-*, .p-multiselect-*) so the wrapped <p-select> /
3243
+ <p-multiselect> look exactly like the static prototype while
3244
+ keeping all of PrimeNG's filter / virtualScroll / keyboard nav /
3245
+ ARIA / RTL behaviour. */
3246
+
3247
+ /* ── Trigger (root) ─────────────────────────────────────────── */
3248
+
3249
+ .es.p-select,
3250
+ .es.p-multiselect {
3251
+ width: 100%;
3252
+ min-width: 200px;
3253
+ background: var(--paper-alt);
3254
+ border: 1px solid var(--rule);
3255
+ border-radius: var(--r-md);
3256
+ min-height: var(--hit-base);
3257
+ padding: 0 10px 0 14px;
3258
+ display: inline-flex;
3259
+ align-items: center;
3260
+ gap: 10px;
3261
+ color: var(--text);
3262
+ font-size: 14px;
3263
+ cursor: pointer;
3264
+ transition:
3265
+ border-color var(--t-fast) var(--ease-out),
3266
+ box-shadow var(--t-fast) var(--ease-out);
3267
+ }
3268
+
3269
+ .es.p-select:not(.p-disabled):hover,
3270
+ .es.p-multiselect:not(.p-disabled):hover {
3271
+ border-color: var(--ink-400);
3272
+ }
3273
+
3274
+ .es.p-select.p-focus,
3275
+ .es.p-select.p-select-open,
3276
+ .es.p-multiselect.p-focus,
3277
+ .es.p-multiselect.p-multiselect-open {
3278
+ border-color: var(--tenant-400);
3279
+ box-shadow: 0 0 0 4px var(--tenant-100);
3280
+ outline: 0;
3281
+ }
3282
+
3283
+ .es.p-disabled {
3284
+ opacity: 0.55;
3285
+ cursor: not-allowed;
3286
+ background: var(--paper);
3287
+ }
3288
+
3289
+ /* Small variant — match `.btn-sm` height */
3290
+ .es--sm.p-select,
3291
+ .es--sm.p-multiselect {
3292
+ font-size: 12.5px;
3293
+ min-height: 32px;
3294
+ padding: 0 8px 0 12px;
3295
+ border-radius: var(--r-sm);
3296
+ }
3297
+
3298
+ /* Invalid state — drives the cancelled-status colour */
3299
+ .es.ng-invalid.ng-touched.p-select,
3300
+ .es.ng-invalid.ng-touched.p-multiselect {
3301
+ border-color: var(--st-cancelled-fg);
3302
+ box-shadow: 0 0 0 3px var(--st-cancelled-bg);
3303
+ }
3304
+
3305
+ /* Trigger value text */
3306
+ .es .p-select-label,
3307
+ .es .p-multiselect-label {
3308
+ flex: 1;
3309
+ min-width: 0;
3310
+ padding: 0;
3311
+ color: var(--text);
3312
+ font: inherit;
3313
+ font-size: inherit;
3314
+ background: transparent;
3315
+ border: 0;
3316
+ white-space: nowrap;
3317
+ overflow: hidden;
3318
+ text-overflow: ellipsis;
3319
+ }
3320
+
3321
+ .es .p-select-label.p-placeholder,
3322
+ .es .p-multiselect-label.p-placeholder {
3323
+ color: var(--text-soft);
3324
+ font-weight: 500;
3325
+ }
3326
+
3327
+ /* Inline clear ✕ — rendered via the p-select/p-multiselect `#clearicon`
3328
+ template as a shared stroke-X (`.es-clear`), identical to
3329
+ ef-product-typeahead's clear so every clearable control uses one glyph.
3330
+ The wrapping container PrimeNG provides is reset to a neutral inline box. */
3331
+ .es .p-select-clear-icon,
3332
+ .es .p-multiselect-clear-icon {
3333
+ width: auto;
3334
+ height: auto;
3335
+ display: inline-flex;
3336
+ flex-shrink: 0;
3337
+ }
3338
+
3339
+ /* Shared clear ✕ — `ef-clear-button` (`.ef-clear`) and the p-select /
3340
+ p-multiselect `#clearicon` template (`.es-clear`) share one look so every
3341
+ clearable control (input, number, select, typeahead) is identical. */
3342
+ .ef-clear,
3343
+ .es .es-clear {
3344
+ display: inline-grid;
3345
+ place-items: center;
3346
+ box-sizing: border-box;
3347
+ width: 22px;
3348
+ height: 22px;
3349
+ border: 0;
3350
+ padding: 0;
3351
+ background: transparent;
3352
+ border-radius: 50%;
3353
+ color: var(--text-soft);
3354
+ flex-shrink: 0;
3355
+ cursor: pointer;
3356
+ transition: all var(--t-fast) var(--ease-out);
3357
+ }
3358
+
3359
+ .ef-clear svg,
3360
+ .es .es-clear svg {
3361
+ width: 13px;
3362
+ height: 13px;
3363
+ }
3364
+
3365
+ .ef-clear:hover,
3366
+ .es .es-clear:hover {
3367
+ background: var(--ink-100);
3368
+ color: var(--text);
3369
+ }
3370
+
3371
+ .ef-clear:disabled {
3372
+ opacity: 0.5;
3373
+ cursor: not-allowed;
3374
+ }
3375
+
3376
+ /* Clear ✕ inside an .ef-input-group (prefix / suffix / stepper variants of
3377
+ ef-input-text + ef-inputnumber). Here the ✕ is a real flex sibling sitting
3378
+ between the input and the trailing addon — no absolute positioning or
3379
+ trailing-padding hack needed (unlike the bare .ef-input-wrap variant). */
3380
+ .ef-input-group .ef-group-clear {
3381
+ align-self: center;
3382
+ margin-inline: 2px;
3383
+ flex-shrink: 0;
3384
+ }
3385
+
3386
+ /* Inline clear ✕ overlaid at the trailing edge of a single-line input
3387
+ (ef-input-text / ef-inputnumber comptoir variants). */
3388
+ .ef-input-wrap {
3389
+ position: relative;
3390
+ }
3391
+
3392
+ .ef-input-wrap .ef-input.has-clear {
3393
+ /* room for the clear button so text doesn't run under it */
3394
+ padding-inline-end: 36px;
3395
+ }
3396
+
3397
+ .ef-input-clear {
3398
+ position: absolute;
3399
+ inset-inline-end: 8px;
3400
+ top: 50%;
3401
+ transform: translateY(-50%);
3402
+ }
3403
+
3404
+ /* Clearable textarea — clear ✕ pinned to the top-trailing corner (clear of
3405
+ the bottom-right resize handle); text gets trailing padding to dodge it. */
3406
+ .ef-textarea-wrap {
3407
+ position: relative;
3408
+ }
3409
+
3410
+ .ef-textarea-wrap .ef-textarea.has-clear {
3411
+ padding-inline-end: 36px;
3412
+ }
3413
+
3414
+ .ef-textarea-clear {
3415
+ position: absolute;
3416
+ inset-inline-end: 8px;
3417
+ top: 8px;
3418
+ }
3419
+
3420
+ /* Shared chevron — one glyph (the canonical stroke polyline) across every
3421
+ control that drops down: ef-select / ef-multiselect (via the `#dropdownicon`
3422
+ template), ef-datepicker-advanced, and the native <select> background image.
3423
+ Sized + toned here; rotation is owned by each control's open state. The SVG
3424
+ markup also carries width/height attributes so it can never stretch even if
3425
+ this rule is missing. */
3426
+ .ef-chevron {
3427
+ width: 12px;
3428
+ height: 12px;
3429
+ flex-shrink: 0;
3430
+ color: var(--text-mute);
3431
+ }
3432
+
3433
+ /* Caret */
3434
+ .es .p-select-dropdown,
3435
+ .es .p-multiselect-dropdown {
3436
+ width: 12px;
3437
+ height: 12px;
3438
+ color: var(--text-mute);
3439
+ flex-shrink: 0;
3440
+ background: transparent;
3441
+ border: 0;
3442
+ padding: 0;
3443
+ transition: transform var(--t-base) var(--ease-spring);
3444
+ }
3445
+
3446
+ /* Constrain whatever icon PrimeNG / our #dropdownicon template renders to the
3447
+ canonical 12px (PrimeNG's default chevron otherwise renders at 16px). */
3448
+ .es .p-select-dropdown svg,
3449
+ .es .p-multiselect-dropdown svg {
3450
+ width: 12px;
3451
+ height: 12px;
3452
+ }
3453
+
3454
+ .es.p-select-open .p-select-dropdown,
3455
+ .es.p-multiselect-open .p-multiselect-dropdown {
3456
+ transform: rotate(180deg);
3457
+ }
3458
+
3459
+ /* Multi-select chips inside the trigger */
3460
+ .es .p-multiselect-token {
3461
+ display: inline-flex;
3462
+ align-items: center;
3463
+ gap: 4px;
3464
+ padding: 2px 4px 2px 9px;
3465
+ border-radius: var(--r-pill);
3466
+ background: var(--ink-100);
3467
+ font-size: 11.5px;
3468
+ font-weight: 700;
3469
+ color: var(--text);
3470
+ max-width: 160px;
3471
+ margin: 2px;
3472
+ }
3473
+
3474
+ .es .p-multiselect-token-label {
3475
+ white-space: nowrap;
3476
+ overflow: hidden;
3477
+ text-overflow: ellipsis;
3478
+ }
3479
+
3480
+ .es .p-multiselect-token-icon {
3481
+ box-sizing: content-box;
3482
+ width: 9px;
3483
+ height: 9px;
3484
+ padding: 3px;
3485
+ border-radius: 50%;
3486
+ color: var(--text-mute);
3487
+ flex-shrink: 0;
3488
+ cursor: pointer;
3489
+ transition: all var(--t-fast) var(--ease-out);
3490
+ }
3491
+
3492
+ .es .p-multiselect-token-icon:hover {
3493
+ background: rgba(15, 17, 21, 0.12);
3494
+ color: var(--text);
3495
+ }
3496
+
3497
+ /* ── Overlay panel ─────────────────────────────────────────── */
3498
+
3499
+ .es__panel.p-select-overlay,
3500
+ .es__panel.p-multiselect-overlay {
3501
+ background: var(--paper-alt);
3502
+ border: 1px solid var(--rule);
3503
+ border-radius: var(--r-xl);
3504
+ box-shadow: var(--shadow-3);
3505
+ overflow: hidden;
3506
+ margin-top: 6px;
3507
+ animation: es-in var(--t-base) var(--ease-spring);
3508
+ }
3509
+
3510
+ @keyframes es-in {
3511
+ from {
3512
+ opacity: 0;
3513
+ transform: translateY(-4px) scale(0.99);
3514
+ }
3515
+ to {
3516
+ opacity: 1;
3517
+ transform: none;
3518
+ }
3519
+ }
3520
+
3521
+ /* Filter container (overlay header) */
3522
+ .es__panel .p-select-header,
3523
+ .es__panel .p-multiselect-header {
3524
+ padding: 8px;
3525
+ border-bottom: 1px solid var(--rule);
3526
+ background: transparent;
3527
+ display: block;
3528
+ position: relative;
3529
+ }
3530
+
3531
+ /* Multiselect header carries the "select all" checkbox alongside the
3532
+ filter — lay them out inline (single-select has no checkbox, so it
3533
+ keeps the block layout above). The filter container already grows
3534
+ (flex: 1 1 auto from PrimeNG); just make the header a flex row. */
3535
+ .es__panel .p-multiselect-header {
3536
+ display: flex;
3537
+ align-items: center;
3538
+ gap: 8px;
3539
+ }
3540
+
3541
+ .es__panel .p-multiselect-header .p-multiselect-filter-container {
3542
+ flex: 1 1 auto;
3543
+ min-width: 0;
3544
+ }
3545
+
3546
+ .es__panel .p-select-filter,
3547
+ .es__panel .p-multiselect-filter {
3548
+ width: 100%;
3549
+ font: inherit;
3550
+ font-size: 13px;
3551
+ background: var(--paper);
3552
+ border: 1px solid var(--rule);
3553
+ border-radius: var(--r-md);
3554
+ padding: 7px 30px 7px 32px;
3555
+ color: var(--text);
3556
+ outline: 0;
3557
+ min-height: 32px;
3558
+ transition: all var(--t-fast) var(--ease-out);
3559
+ }
3560
+
3561
+ .es__panel .p-select-filter:focus,
3562
+ .es__panel .p-multiselect-filter:focus {
3563
+ border-color: var(--tenant-400);
3564
+ box-shadow: 0 0 0 3px var(--tenant-100);
3565
+ background: var(--paper-alt);
3566
+ }
3567
+
3568
+ .es__panel .p-select-filter-icon,
3569
+ .es__panel .p-multiselect-filter-icon {
3570
+ position: absolute;
3571
+ inset-inline-start: 18px;
3572
+ top: 50%;
3573
+ transform: translateY(-50%);
3574
+ width: 13px;
3575
+ height: 13px;
3576
+ color: var(--text-mute);
3577
+ opacity: 0.7;
3578
+ pointer-events: none;
3579
+ }
3580
+
3581
+ /* Scrollable list */
3582
+ .es__panel .p-select-list-container,
3583
+ .es__panel .p-multiselect-list-container {
3584
+ padding: 6px;
3585
+ max-height: 280px;
3586
+ overflow-y: auto;
3587
+ }
3588
+
3589
+ .es__panel .p-select-list-container::-webkit-scrollbar,
3590
+ .es__panel .p-multiselect-list-container::-webkit-scrollbar {
3591
+ width: 6px;
3592
+ }
3593
+ .es__panel .p-select-list-container::-webkit-scrollbar-thumb,
3594
+ .es__panel .p-multiselect-list-container::-webkit-scrollbar-thumb {
3595
+ background: var(--ink-300);
3596
+ border-radius: 999px;
3597
+ }
3598
+
3599
+ .es__panel .p-select-list,
3600
+ .es__panel .p-multiselect-list {
3601
+ display: grid;
3602
+ gap: 1px;
3603
+ list-style: none;
3604
+ margin: 0;
3605
+ padding: 0;
3606
+ }
3607
+
3608
+ /* Group headers */
3609
+ .es__panel .p-select-option-group,
3610
+ .es__panel .p-multiselect-option-group {
3611
+ font-family: 'JetBrains Mono', ui-monospace, monospace;
3612
+ font-size: 10px;
3613
+ font-weight: 800;
3614
+ letter-spacing: 0.06em;
3615
+ text-transform: uppercase;
3616
+ color: var(--text-mute);
3617
+ padding: 10px 10px 4px;
3618
+ margin-top: 2px;
3619
+ background: transparent;
3620
+ }
3621
+
3622
+ .es__panel .p-select-option-group:first-child,
3623
+ .es__panel .p-multiselect-option-group:first-child {
3624
+ margin-top: 0;
3625
+ padding-top: 6px;
3626
+ }
3627
+
3628
+ /* Option row */
3629
+ .es__panel .p-select-option,
3630
+ .es__panel .p-multiselect-option {
3631
+ display: flex;
3632
+ align-items: center;
3633
+ gap: 10px;
3634
+ width: 100%;
3635
+ padding: 9px 10px;
3636
+ border-radius: var(--r-md);
3637
+ cursor: pointer;
3638
+ font-size: 13.5px;
3639
+ color: var(--text);
3640
+ background: transparent;
3641
+ border: 0;
3642
+ transition: background-color var(--t-fast) var(--ease-out);
3643
+ }
3644
+
3645
+ .es__panel .p-select-option:hover,
3646
+ .es__panel .p-multiselect-option:hover,
3647
+ .es__panel .p-select-option.p-focus,
3648
+ .es__panel .p-multiselect-option.p-focus {
3649
+ background: var(--paper);
3650
+ }
3651
+
3652
+ .es__panel .p-select-option.p-select-option-selected {
3653
+ background: var(--ink-active);
3654
+ color: var(--paper);
3655
+ }
3656
+
3657
+ /* Multi: lighter tenant-tinted bg + filled checkbox marks the state */
3658
+ .es__panel .p-multiselect-option.p-multiselect-option-selected {
3659
+ background: color-mix(in srgb, var(--tenant-100) 60%, var(--paper));
3660
+ color: var(--text);
3661
+ }
3662
+
3663
+ /* Checkbox — PrimeNG 21 renders a <p-checkbox> (.p-checkbox / .p-checkbox-box)
3664
+ inside both the multiselect option rows and the header "select all".
3665
+ The old .p-multiselect-checkbox markup is gone, so size + skin the real
3666
+ element to the Comptoir tokens; otherwise it renders unstyled / oversized. */
3667
+ .es__panel .p-checkbox {
3668
+ position: relative;
3669
+ width: 16px;
3670
+ height: 16px;
3671
+ flex-shrink: 0;
3672
+ }
3673
+
3674
+ .es__panel .p-checkbox .p-checkbox-input {
3675
+ position: absolute;
3676
+ inset: 0;
3677
+ width: 100%;
3678
+ height: 100%;
3679
+ margin: 0;
3680
+ opacity: 0;
3681
+ cursor: pointer;
3682
+ }
3683
+
3684
+ .es__panel .p-checkbox .p-checkbox-box {
3685
+ width: 16px;
3686
+ height: 16px;
3687
+ border: 1.5px solid var(--ink-300);
3688
+ border-radius: 4px;
3689
+ background: var(--paper-alt);
3690
+ display: grid;
3691
+ place-items: center;
3692
+ flex-shrink: 0;
3693
+ transition: all var(--t-fast) var(--ease-out);
3694
+ }
3695
+
3696
+ .es__panel .p-checkbox.p-checkbox-checked .p-checkbox-box {
3697
+ background: var(--tenant-500);
3698
+ border-color: var(--tenant-500);
3699
+ }
3700
+
3701
+ .es__panel .p-checkbox .p-checkbox-icon {
3702
+ width: 10px;
3703
+ height: 10px;
3704
+ font-size: 10px;
3705
+ color: var(--paper);
3706
+ }
3707
+
3708
+ /* Selected check-mark for single-select (the trailing ✓) */
3709
+ .es__panel .p-select-option-check-icon {
3710
+ width: 13px;
3711
+ height: 13px;
3712
+ color: var(--paper);
3713
+ margin-inline-start: auto;
3714
+ flex-shrink: 0;
3715
+ }
3716
+
3717
+ .es__panel .p-select-option[disabled],
3718
+ .es__panel .p-multiselect-option[disabled] {
3719
+ opacity: 0.45;
3720
+ cursor: not-allowed;
3721
+ }
3722
+
3723
+ /* Empty / loading states */
3724
+ .es__panel .p-select-empty-message,
3725
+ .es__panel .p-multiselect-empty-message {
3726
+ padding: 36px 20px;
3727
+ text-align: center;
3728
+ color: var(--text-mute);
3729
+ font-size: 12.5px;
3730
+ line-height: 1.5;
3731
+ }
3732
+
3733
+ /* ── RTL safety — PrimeNG handles direction; our Comptoir tokens
3734
+ already use logical properties (inset-inline-start/end). No
3735
+ extra rules needed beyond the ones above. */
3736
+
3737
+ /* ──────────── EF-DIALOG — COMPTOIR SKIN OVER P-DIALOG ─────────────
3738
+ Lifts the PrimeNG modal to Comptoir tokens: --paper-alt surface,
3739
+ --rule borders, --r-xl radius, Bricolage Grotesque title, soft
3740
+ elevation. The wrapper adds `[styleClass]="es-dialog"` to
3741
+ <p-dialog>; rules target both our class and PrimeNG's stable
3742
+ internal classes (.p-dialog-*) so the wrapped <p-dialog> looks
3743
+ exactly like the Comptoir prototype while keeping all of
3744
+ PrimeNG's focus trap, ESC handling, ARIA, and animation. */
3745
+
3746
+ /* Backdrop — match the page-wide modal scrim used elsewhere. */
3747
+ .p-dialog-mask.p-overlay-mask {
3748
+ background: color-mix(in srgb, var(--ink-900) 35%, transparent);
3749
+ backdrop-filter: blur(2px);
3750
+ }
3751
+
3752
+ /* Root panel */
3753
+ .es-dialog.p-dialog {
3754
+ background: var(--paper-alt);
3755
+ border: 1px solid var(--rule);
3756
+ border-radius: var(--r-xl);
3757
+ box-shadow:
3758
+ 0 1px 2px rgba(15, 17, 21, 0.04),
3759
+ 0 24px 64px rgba(15, 17, 21, 0.18);
3760
+ overflow: hidden;
3761
+ /* PrimeNG sometimes injects an inline white background — neutralize. */
3762
+ color: var(--text);
3763
+ }
3764
+
3765
+ /* Header strip — flex layout so the custom header template
3766
+ (icon + title-block + close) sits naturally. The title-block
3767
+ takes `flex:1` so the close stays right-aligned regardless of
3768
+ whether the icon is present. */
3769
+ .es-dialog .p-dialog-header {
3770
+ background: var(--paper-alt);
3771
+ border-bottom: 1px solid var(--rule);
3772
+ padding: 18px 20px;
3773
+ display: flex;
3774
+ align-items: center;
3775
+ gap: var(--s-3);
3776
+ }
3777
+
3778
+ .es-dialog .p-dialog-title,
3779
+ .es-dialog .p-dialog-header-title {
3780
+ font-family: 'Bricolage Grotesque', system-ui, sans-serif;
3781
+ font-variation-settings:
3782
+ 'opsz' 14,
3783
+ 'wght' 700,
3784
+ 'wdth' 85;
3785
+ font-size: 18px;
3786
+ line-height: 1.2;
3787
+ color: var(--text);
3788
+ margin: 0;
3789
+ }
3790
+
3791
+ /* Close button — ghost icon, matches .btn-ghost severity. */
3792
+ .es-dialog .p-dialog-header-icons {
3793
+ display: flex;
3794
+ align-items: center;
3795
+ gap: 6px;
3796
+ }
3797
+
3798
+ .es-dialog .p-dialog-header-icon,
3799
+ .es-dialog .p-dialog-close-button {
3800
+ appearance: none;
3801
+ background: transparent;
3802
+ border: 0;
3803
+ color: var(--text-mute);
3804
+ width: 32px;
3805
+ height: 32px;
3806
+ border-radius: var(--r-pill);
3807
+ display: inline-grid;
3808
+ place-items: center;
3809
+ cursor: pointer;
3810
+ transition:
3811
+ background var(--t-fast) var(--ease-out),
3812
+ color var(--t-fast) var(--ease-out);
3813
+ }
3814
+
3815
+ .es-dialog .p-dialog-header-icon:hover,
3816
+ .es-dialog .p-dialog-close-button:hover {
3817
+ background: var(--paper);
3818
+ color: var(--text);
3819
+ }
3820
+
3821
+ .es-dialog .p-dialog-header-icon:focus-visible,
3822
+ .es-dialog .p-dialog-close-button:focus-visible {
3823
+ outline: 2px solid var(--tenant-400);
3824
+ outline-offset: 2px;
3825
+ }
3826
+
3827
+ .es-dialog .p-dialog-header-icon i,
3828
+ .es-dialog .p-dialog-close-button i,
3829
+ .es-dialog .p-dialog-header-icon .p-icon,
3830
+ .es-dialog .p-dialog-close-button .p-icon {
3831
+ font-size: 14px;
3832
+ }
3833
+
3834
+ /* Body — scrolls within the dialog if content overflows. */
3835
+ .es-dialog .p-dialog-content {
3836
+ background: var(--paper-alt);
3837
+ padding: 20px;
3838
+ max-height: 70vh;
3839
+ overflow-y: auto;
3840
+ color: var(--text);
3841
+ }
3842
+
3843
+ /* Footer strip — rule-top separator, actions right-aligned. */
3844
+ .es-dialog .p-dialog-footer {
3845
+ background: var(--paper);
3846
+ border-top: 1px solid var(--rule);
3847
+ padding: 14px 20px;
3848
+ display: flex;
3849
+ justify-content: flex-end;
3850
+ gap: var(--s-2);
3851
+ margin: 0;
3852
+ }
3853
+
3854
+ /* Position variants — match p-dialog's offset for `position="top"`. */
3855
+ .p-dialog-mask.p-dialog-top .es-dialog.p-dialog {
3856
+ margin-top: 60px;
3857
+ }
3858
+
3859
+ /* ── Size modifiers ──────────────────────────────────────────
3860
+ Width presets driven by `[size]`. `--full` takes the whole
3861
+ viewport minus a comfortable gutter for the backdrop. */
3862
+ .es-dialog--sm.p-dialog {
3863
+ width: 380px;
3864
+ max-width: 92vw;
3865
+ }
3866
+ .es-dialog--md.p-dialog {
3867
+ width: 480px;
3868
+ max-width: 92vw;
3869
+ }
3870
+ .es-dialog--lg.p-dialog {
3871
+ width: 640px;
3872
+ max-width: 92vw;
3873
+ }
3874
+ .es-dialog--xl.p-dialog {
3875
+ width: 880px;
3876
+ max-width: 92vw;
3877
+ }
3878
+ .es-dialog--full.p-dialog {
3879
+ width: calc(100vw - 48px);
3880
+ height: calc(100vh - 48px);
3881
+ max-width: none;
3882
+ }
3883
+ .es-dialog--full .p-dialog-content {
3884
+ max-height: none;
3885
+ flex: 1;
3886
+ }
3887
+
3888
+ /* ── Header inner structure (rendered via #header content template).
3889
+ `flex:1` on the title-block keeps the close right-aligned with or
3890
+ without the leading icon. */
3891
+ .es-dialog .es-dialog__icon {
3892
+ width: 36px;
3893
+ height: 36px;
3894
+ border-radius: var(--r-pill);
3895
+ display: inline-grid;
3896
+ place-items: center;
3897
+ background: var(--ink-100);
3898
+ color: var(--text);
3899
+ flex-shrink: 0;
3900
+ }
3901
+
3902
+ .es-dialog .es-dialog__icon i,
3903
+ .es-dialog .es-dialog__icon .p-icon {
3904
+ font-size: 14px;
3905
+ }
3906
+
3907
+ /* Severity tints — colour the icon only; the panel stays paper-alt
3908
+ so the dialog never feels "shouty" (status spectrum cue is enough). */
3909
+ .es-dialog--danger .es-dialog__icon {
3910
+ background: var(--st-cancelled-bg);
3911
+ color: var(--st-cancelled-fg);
3912
+ }
3913
+ .es-dialog--warn .es-dialog__icon {
3914
+ background: var(--st-pending-bg);
3915
+ color: var(--st-pending-fg);
3916
+ }
3917
+ .es-dialog--info .es-dialog__icon {
3918
+ background: var(--st-processing-bg);
3919
+ color: var(--st-processing-fg);
3920
+ }
3921
+ .es-dialog--success .es-dialog__icon {
3922
+ background: var(--st-delivered-bg);
3923
+ color: var(--st-delivered-fg);
3924
+ }
3925
+ .es-dialog--tenant .es-dialog__icon {
3926
+ background: var(--tenant-100);
3927
+ color: var(--tenant-700);
3928
+ }
3929
+
3930
+ .es-dialog .es-dialog__title-block {
3931
+ display: grid;
3932
+ gap: 2px;
3933
+ min-width: 0;
3934
+ flex: 1;
3935
+ }
3936
+
3937
+ .es-dialog .es-dialog__title {
3938
+ font-family: 'Bricolage Grotesque', system-ui, sans-serif;
3939
+ font-variation-settings:
3940
+ 'opsz' 14,
3941
+ 'wght' 700,
3942
+ 'wdth' 85;
3943
+ font-size: 17px;
3944
+ letter-spacing: -0.005em;
3945
+ color: var(--text);
3946
+ line-height: 1.25;
3947
+ }
3948
+
3949
+ .es-dialog .es-dialog__sub {
3950
+ font-size: 12.5px;
3951
+ color: var(--text-mute);
3952
+ font-weight: 600;
3953
+ line-height: 1.4;
3954
+ }
3955
+
3956
+ /* Custom close button — used by the comptoir variant. Same look as
3957
+ PrimeNG's built-in close button but driven by our markup so the
3958
+ header layout stays predictable. */
3959
+ .es-dialog .es-dialog__close {
3960
+ appearance: none;
3961
+ background: transparent;
3962
+ border: 0;
3963
+ width: 32px;
3964
+ height: 32px;
3965
+ border-radius: var(--r-md);
3966
+ color: var(--text-mute);
3967
+ cursor: pointer;
3968
+ display: inline-grid;
3969
+ place-items: center;
3970
+ flex-shrink: 0;
3971
+ transition:
3972
+ background var(--t-fast) var(--ease-out),
3973
+ color var(--t-fast) var(--ease-out);
3974
+ }
3975
+
3976
+ .es-dialog .es-dialog__close:hover {
3977
+ background: var(--paper);
3978
+ color: var(--text);
3979
+ }
3980
+
3981
+ .es-dialog .es-dialog__close:focus-visible {
3982
+ outline: 2px solid var(--tenant-400);
3983
+ outline-offset: 2px;
3984
+ }
3985
+
3986
+ .es-dialog .es-dialog__close i {
3987
+ font-size: 13px;
3988
+ }
3989
+
3990
+ /* ── Entity highlight callout — opt-in markup for confirm dialogs.
3991
+ Use inside the projected body to anchor "what's being modified". */
3992
+ .es-dialog .es-dialog__highlight {
3993
+ padding: 12px 16px;
3994
+ background: var(--paper);
3995
+ border: 1px solid var(--rule);
3996
+ border-radius: var(--r-md);
3997
+ display: flex;
3998
+ align-items: center;
3999
+ gap: var(--s-3);
4000
+ margin-bottom: var(--s-3);
4001
+ font-size: 13px;
4002
+ }
4003
+
4004
+ .es-dialog .es-dialog__highlight .av {
4005
+ width: 32px;
4006
+ height: 32px;
4007
+ border-radius: 50%;
4008
+ background: var(--ink-active);
4009
+ color: var(--paper);
4010
+ display: grid;
4011
+ place-items: center;
4012
+ font-family: 'Bricolage Grotesque', system-ui, sans-serif;
4013
+ font-variation-settings:
4014
+ 'opsz' 12,
4015
+ 'wght' 700,
4016
+ 'wdth' 80;
4017
+ font-size: 12px;
4018
+ flex-shrink: 0;
4019
+ }
4020
+
4021
+ .es-dialog .es-dialog__highlight .av.tenant {
4022
+ background: var(--tenant-500);
4023
+ }
4024
+
4025
+ .es-dialog .es-dialog__highlight .body {
4026
+ display: grid;
4027
+ gap: 2px;
4028
+ min-width: 0;
4029
+ }
4030
+
4031
+ .es-dialog .es-dialog__highlight .nm {
4032
+ font-weight: 700;
4033
+ color: var(--text);
4034
+ }
4035
+
4036
+ .es-dialog .es-dialog__highlight .meta {
4037
+ font-size: 11.5px;
4038
+ color: var(--text-mute);
4039
+ font-weight: 600;
4040
+ }
4041
+
4042
+ /* ── Destructive primary CTA. Lifted from `components.html` so any
4043
+ `.btn.btn-danger` in the framework picks it up (notably the dialog
4044
+ action footer when an action declares `severity: 'danger'`). */
4045
+ .btn.btn-danger {
4046
+ background: var(--st-cancelled-fg);
4047
+ color: #fff;
4048
+ border-color: var(--st-cancelled-fg);
4049
+ }
4050
+
4051
+ .btn.btn-danger:hover {
4052
+ background: color-mix(in srgb, var(--st-cancelled-fg) 88%, #000);
4053
+ transform: translateY(-1px);
4054
+ box-shadow: 0 12px 30px -10px var(--st-cancelled-bg);
4055
+ }
4056
+
4057
+ /* ── Per-action loading spinner overlay. The button auto-toggles
4058
+ `.is-loading` while an async `command()` is in flight; the label
4059
+ fades out and a centred SVG spinner takes its place. */
4060
+ .btn.is-loading {
4061
+ position: relative;
4062
+ color: transparent !important;
4063
+ pointer-events: none;
4064
+ }
4065
+
4066
+ .btn.is-loading::after {
4067
+ content: '';
4068
+ position: absolute;
4069
+ inset: 0;
4070
+ display: grid;
4071
+ place-items: center;
4072
+ background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2.5' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9' opacity='.3'/%3E%3Cpath d='M21 12a9 9 0 0 1-9 9'/%3E%3C/svg%3E")
4073
+ center/16px no-repeat;
4074
+ animation: es-dialog-spin 1s linear infinite;
4075
+ }
4076
+
4077
+ /* Spinner colour switches to ink on light-bg buttons (ghost). */
4078
+ .btn.btn-ghost.is-loading::after {
4079
+ background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%231d1f24' stroke-width='2.5' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9' opacity='.3'/%3E%3Cpath d='M21 12a9 9 0 0 1-9 9'/%3E%3C/svg%3E")
4080
+ center/16px no-repeat;
4081
+ }
4082
+
4083
+ @keyframes es-dialog-spin {
4084
+ from {
4085
+ transform: rotate(0);
4086
+ }
4087
+ to {
4088
+ transform: rotate(360deg);
4089
+ }
4090
+ }