@cahyo-dimas/freeday 3.5.0 → 3.7.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.
Files changed (59) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/COMPONENTS.md +251 -8
  3. package/README.id.md +19 -6
  4. package/README.md +17 -5
  5. package/USAGE.md +42 -0
  6. package/adapters/blazor/FdyDateRange.razor.cs +10 -0
  7. package/adapters/blazor/FdyTable.razor +6 -2
  8. package/adapters/blazor/FdyTable.razor.cs +21 -0
  9. package/adapters/blazor/TableTypes.cs +4 -0
  10. package/adapters/react/components/FdyDateRange.tsx +8 -2
  11. package/adapters/react/components/FdyTable.tsx +24 -3
  12. package/adapters/react/index.d.ts +1 -1
  13. package/adapters/vue/components/FdyDateRange.vue +11 -5
  14. package/adapters/vue/components/FdyTable.vue +18 -2
  15. package/dist/freeday-confirm.js +101 -0
  16. package/dist/freeday-copy.js +58 -0
  17. package/dist/freeday-datepicker.js +17 -2
  18. package/dist/freeday-form.js +30 -1
  19. package/dist/freeday-search.js +77 -0
  20. package/dist/freeday.bundle.css +544 -122
  21. package/dist/freeday.css +241 -118
  22. package/dist/freeday.d.ts +17 -2
  23. package/dist/freeday.js +286 -3
  24. package/dist/freeday.tokens.css +303 -4
  25. package/docs/agent-onboarding.md +8 -0
  26. package/docs/getting-started.md +1 -1
  27. package/package.json +2 -2
  28. package/src/base.css +1 -1
  29. package/src/components/accordion.css +1 -1
  30. package/src/components/alert.css +5 -5
  31. package/src/components/app-shell.css +2 -2
  32. package/src/components/appbar.css +3 -3
  33. package/src/components/avatar.css +17 -9
  34. package/src/components/badge.css +18 -14
  35. package/src/components/breadcrumb.css +1 -1
  36. package/src/components/button.css +21 -13
  37. package/src/components/card.css +7 -0
  38. package/src/components/carousel.css +3 -3
  39. package/src/components/cascade.css +4 -4
  40. package/src/components/chip.css +33 -15
  41. package/src/components/combo.css +5 -5
  42. package/src/components/composition.css +36 -0
  43. package/src/components/datepicker.css +8 -8
  44. package/src/components/description-list.css +11 -1
  45. package/src/components/file-upload.css +9 -4
  46. package/src/components/input-group.css +2 -2
  47. package/src/components/input.css +2 -2
  48. package/src/components/menu.css +3 -3
  49. package/src/components/pagination.css +1 -1
  50. package/src/components/selection.css +8 -2
  51. package/src/components/skeleton.css +1 -1
  52. package/src/components/states.css +7 -0
  53. package/src/components/stepper.css +1 -1
  54. package/src/components/table.css +25 -11
  55. package/src/components/tabs.css +1 -1
  56. package/src/components/timepicker.css +3 -3
  57. package/src/components/toast.css +1 -1
  58. package/src/components/tree.css +1 -1
  59. package/tokens/tokens.json +57 -4
@@ -245,6 +245,7 @@
245
245
  --color-surface: var(--slate-0);
246
246
  --color-surface-2: var(--slate-50);
247
247
  --color-surface-3: var(--slate-100);
248
+ --color-surface-stripe: #fbfbfc;
248
249
  --color-surface-raised: var(--color-surface);
249
250
  --color-scrim: rgba(10,12,25,.5);
250
251
  --color-text: var(--slate-900);
@@ -267,11 +268,21 @@
267
268
  --color-danger-soft: var(--red-100);
268
269
  --color-info: var(--blue-600);
269
270
  --color-info-soft: var(--blue-100);
271
+ --color-danger-border: #de979c;
270
272
  --color-primary-strong: var(--primary-700);
271
273
  --color-success-strong: var(--green-800);
272
274
  --color-warning-strong: var(--amber-800);
273
275
  --color-danger-strong: var(--red-700);
274
276
  --color-info-strong: var(--blue-700);
277
+ --color-primary-rgb: var(--primary-600-rgb);
278
+ --color-on-primary-rgb: var(--slate-0-rgb);
279
+ --color-danger-rgb: var(--red-600-rgb);
280
+ --color-danger-btn-rgb: var(--red-600-rgb);
281
+ --color-success-rgb: var(--green-600-rgb);
282
+ --color-warning-rgb: 194,116,10;
283
+ --color-info-rgb: var(--blue-600-rgb);
284
+ --color-surface-rgb: var(--slate-0-rgb);
285
+ --color-inverse-text-rgb: var(--slate-0-rgb);
275
286
  --chart-1: #2a78d6;
276
287
  --chart-2: #eb6834;
277
288
  --chart-3: #1baf7a;
@@ -290,10 +301,55 @@
290
301
  --tone-6: var(--chart-6);
291
302
  --tone-7: var(--chart-7);
292
303
  --tone-8: var(--chart-8);
304
+ --tone-1-bg: #d9e7f8;
305
+ --tone-2-bg: #fbe4da;
306
+ --tone-3-bg: #d6f1e7;
307
+ --tone-4-bg: #fceed1;
308
+ --tone-5-bg: #fbe7ef;
309
+ --tone-6-bg: #d1e9d1;
310
+ --tone-7-bg: #dedcef;
311
+ --tone-8-bg: #fadede;
312
+ --tone-1-ink: #214a7e;
313
+ --tone-2-ink: #81422d;
314
+ --tone-3-ink: #196550;
315
+ --tone-4-ink: #825e13;
316
+ --tone-5-ink: #804b65;
317
+ --tone-6-ink: #0c4f13;
318
+ --tone-7-ink: #312b67;
319
+ --tone-8-ink: #7d3237;
320
+ --tone-1-border: #a9c3e4;
321
+ --tone-2-border: #e3beb3;
322
+ --tone-3-border: #a5d3c8;
323
+ --tone-4-border: #e4cfa4;
324
+ --tone-5-border: #e2c4d5;
325
+ --tone-6-border: #9dc6a4;
326
+ --tone-7-border: #b3b0d6;
327
+ --tone-8-border: #e1b5b9;
328
+ --tone-1-border-soft: rgba(42,120,214,.26);
329
+ --tone-2-border-soft: rgba(235,104,52,.26);
330
+ --tone-3-border-soft: rgba(27,175,122,.26);
331
+ --tone-4-border-soft: rgba(237,161,0,.26);
332
+ --tone-5-border-soft: rgba(232,123,164,.26);
333
+ --tone-6-border-soft: rgba(0,131,0,.26);
334
+ --tone-7-border-soft: rgba(74,58,167,.26);
335
+ --tone-8-border-soft: rgba(227,73,72,.26);
293
336
  --surface-filter: none;
294
337
  --surface-inset: inset 0 0 0 0 transparent;
295
338
  --focus-ring: var(--primary-600);
296
339
  --focus-ring-width: 2px;
340
+ --amber-500-rgb: 245,158,11;
341
+ --blue-400-rgb: 96,165,250;
342
+ --blue-600-rgb: 37,99,235;
343
+ --green-500-rgb: 34,197,94;
344
+ --green-600-rgb: 22,163,74;
345
+ --primary-400-rgb: 92,139,246;
346
+ --primary-500-rgb: 52,103,236;
347
+ --primary-600-rgb: 32,80,216;
348
+ --red-400-rgb: 248,113,113;
349
+ --red-600-rgb: 220,38,38;
350
+ --slate-0-rgb: 255,255,255;
351
+ --slate-900-rgb: 23,27,38;
352
+ --slate-950-rgb: 14,17,26;
297
353
  }
298
354
  /* The SYSTEM default stays root-scoped: it is a statement about the document, and dropping :root
299
355
  * here would match every element that is not itself [data-theme="light"], which would re-darken
@@ -310,8 +366,8 @@
310
366
  --color-primary-hover: var(--primary-400);
311
367
  --color-primary-active: var(--primary-300);
312
368
  --color-on-primary: var(--slate-0);
313
- --color-primary-soft: color-mix(in srgb, var(--primary-600) 20%, transparent);
314
- --color-primary-border: color-mix(in srgb, var(--primary-400) 28%, transparent);
369
+ --color-primary-soft: rgba(var(--primary-600-rgb), .2);
370
+ --color-primary-border: rgba(var(--primary-400-rgb), .28);
315
371
  --color-accent: var(--sky-400);
316
372
  --color-accent-hover: var(--sky-300);
317
373
  --color-on-accent: #06233f;
@@ -320,6 +376,7 @@
320
376
  --color-surface: var(--slate-950);
321
377
  --color-surface-2: var(--slate-900);
322
378
  --color-surface-3: var(--slate-800);
379
+ --color-surface-stripe: #131721;
323
380
  --color-surface-raised: var(--color-surface);
324
381
  --color-text: var(--slate-100);
325
382
  --color-text-muted: var(--slate-400);
@@ -339,11 +396,19 @@
339
396
  --color-danger-soft: rgba(248,113,113,.18);
340
397
  --color-info: var(--blue-400);
341
398
  --color-info-soft: rgba(96,165,250,.18);
399
+ --color-danger-border: #7a474f;
342
400
  --color-primary-strong: var(--primary-300);
343
401
  --color-success-strong: var(--green-400);
344
402
  --color-warning-strong: var(--amber-400);
345
403
  --color-danger-strong: var(--red-300);
346
404
  --color-info-strong: var(--blue-300);
405
+ --color-primary-rgb: var(--primary-500-rgb);
406
+ --color-danger-rgb: var(--red-400-rgb);
407
+ --color-success-rgb: var(--green-500-rgb);
408
+ --color-warning-rgb: var(--amber-500-rgb);
409
+ --color-info-rgb: var(--blue-400-rgb);
410
+ --color-surface-rgb: var(--slate-950-rgb);
411
+ --color-inverse-text-rgb: var(--slate-900-rgb);
347
412
  --chart-1: #3987e5;
348
413
  --chart-2: #d95926;
349
414
  --chart-3: #199e70;
@@ -354,6 +419,37 @@
354
419
  --chart-8: #e66767;
355
420
  --chart-grid: var(--slate-800);
356
421
  --chart-tick: var(--slate-600);
422
+ --tone-1-bg: #16263f;
423
+ --tone-2-bg: #331e1c;
424
+ --tone-3-bg: #102a29;
425
+ --tone-4-bg: #302615;
426
+ --tone-5-bg: #321d2d;
427
+ --tone-6-bg: #0b2615;
428
+ --tone-7-bg: #25263f;
429
+ --tone-8-bg: #352028;
430
+ --tone-1-ink: #94bced;
431
+ --tone-2-ink: #e4a58d;
432
+ --tone-3-ink: #84c7b2;
433
+ --tone-4-ink: #dcbb7a;
434
+ --tone-5-ink: #e2a1bb;
435
+ --tone-6-ink: #77ba7a;
436
+ --tone-7-ink: #bfbbef;
437
+ --tone-8-ink: #eaacae;
438
+ --tone-1-border: #2c476c;
439
+ --tone-2-border: #5c3933;
440
+ --tone-3-border: #224e49;
441
+ --tone-4-border: #574627;
442
+ --tone-5-border: #5b364e;
443
+ --tone-6-border: #1b4527;
444
+ --tone-7-border: #46466d;
445
+ --tone-8-border: #603d46;
446
+ --tone-1-border-soft: rgba(57,135,229,.26);
447
+ --tone-2-border-soft: rgba(217,89,38,.26);
448
+ --tone-3-border-soft: rgba(25,158,112,.26);
449
+ --tone-4-border-soft: rgba(201,133,0,.26);
450
+ --tone-5-border-soft: rgba(213,81,129,.26);
451
+ --tone-7-border-soft: rgba(144,133,233,.26);
452
+ --tone-8-border-soft: rgba(230,103,103,.26);
357
453
  --focus-ring: var(--primary-400);
358
454
  }
359
455
  }
@@ -374,8 +470,8 @@
374
470
  --color-primary-hover: var(--primary-400);
375
471
  --color-primary-active: var(--primary-300);
376
472
  --color-on-primary: var(--slate-0);
377
- --color-primary-soft: color-mix(in srgb, var(--primary-600) 20%, transparent);
378
- --color-primary-border: color-mix(in srgb, var(--primary-400) 28%, transparent);
473
+ --color-primary-soft: rgba(var(--primary-600-rgb), .2);
474
+ --color-primary-border: rgba(var(--primary-400-rgb), .28);
379
475
  --color-accent: var(--sky-400);
380
476
  --color-accent-hover: var(--sky-300);
381
477
  --color-on-accent: #06233f;
@@ -384,6 +480,7 @@
384
480
  --color-surface: var(--slate-950);
385
481
  --color-surface-2: var(--slate-900);
386
482
  --color-surface-3: var(--slate-800);
483
+ --color-surface-stripe: #131721;
387
484
  --color-surface-raised: var(--color-surface);
388
485
  --color-text: var(--slate-100);
389
486
  --color-text-muted: var(--slate-400);
@@ -403,11 +500,19 @@
403
500
  --color-danger-soft: rgba(248,113,113,.18);
404
501
  --color-info: var(--blue-400);
405
502
  --color-info-soft: rgba(96,165,250,.18);
503
+ --color-danger-border: #7a474f;
406
504
  --color-primary-strong: var(--primary-300);
407
505
  --color-success-strong: var(--green-400);
408
506
  --color-warning-strong: var(--amber-400);
409
507
  --color-danger-strong: var(--red-300);
410
508
  --color-info-strong: var(--blue-300);
509
+ --color-primary-rgb: var(--primary-500-rgb);
510
+ --color-danger-rgb: var(--red-400-rgb);
511
+ --color-success-rgb: var(--green-500-rgb);
512
+ --color-warning-rgb: var(--amber-500-rgb);
513
+ --color-info-rgb: var(--blue-400-rgb);
514
+ --color-surface-rgb: var(--slate-950-rgb);
515
+ --color-inverse-text-rgb: var(--slate-900-rgb);
411
516
  --chart-1: #3987e5;
412
517
  --chart-2: #d95926;
413
518
  --chart-3: #199e70;
@@ -418,6 +523,37 @@
418
523
  --chart-8: #e66767;
419
524
  --chart-grid: var(--slate-800);
420
525
  --chart-tick: var(--slate-600);
526
+ --tone-1-bg: #16263f;
527
+ --tone-2-bg: #331e1c;
528
+ --tone-3-bg: #102a29;
529
+ --tone-4-bg: #302615;
530
+ --tone-5-bg: #321d2d;
531
+ --tone-6-bg: #0b2615;
532
+ --tone-7-bg: #25263f;
533
+ --tone-8-bg: #352028;
534
+ --tone-1-ink: #94bced;
535
+ --tone-2-ink: #e4a58d;
536
+ --tone-3-ink: #84c7b2;
537
+ --tone-4-ink: #dcbb7a;
538
+ --tone-5-ink: #e2a1bb;
539
+ --tone-6-ink: #77ba7a;
540
+ --tone-7-ink: #bfbbef;
541
+ --tone-8-ink: #eaacae;
542
+ --tone-1-border: #2c476c;
543
+ --tone-2-border: #5c3933;
544
+ --tone-3-border: #224e49;
545
+ --tone-4-border: #574627;
546
+ --tone-5-border: #5b364e;
547
+ --tone-6-border: #1b4527;
548
+ --tone-7-border: #46466d;
549
+ --tone-8-border: #603d46;
550
+ --tone-1-border-soft: rgba(57,135,229,.26);
551
+ --tone-2-border-soft: rgba(217,89,38,.26);
552
+ --tone-3-border-soft: rgba(25,158,112,.26);
553
+ --tone-4-border-soft: rgba(201,133,0,.26);
554
+ --tone-5-border-soft: rgba(213,81,129,.26);
555
+ --tone-7-border-soft: rgba(144,133,233,.26);
556
+ --tone-8-border-soft: rgba(230,103,103,.26);
421
557
  --focus-ring: var(--primary-400);
422
558
  }
423
559
  [data-theme="light"] {
@@ -441,6 +577,7 @@
441
577
  --color-surface: var(--slate-0);
442
578
  --color-surface-2: var(--slate-50);
443
579
  --color-surface-3: var(--slate-100);
580
+ --color-surface-stripe: #fbfbfc;
444
581
  --color-surface-raised: var(--color-surface);
445
582
  --color-text: var(--slate-900);
446
583
  --color-text-muted: var(--slate-600);
@@ -460,11 +597,19 @@
460
597
  --color-danger-soft: var(--red-100);
461
598
  --color-info: var(--blue-600);
462
599
  --color-info-soft: var(--blue-100);
600
+ --color-danger-border: #de979c;
463
601
  --color-primary-strong: var(--primary-700);
464
602
  --color-success-strong: var(--green-800);
465
603
  --color-warning-strong: var(--amber-800);
466
604
  --color-danger-strong: var(--red-700);
467
605
  --color-info-strong: var(--blue-700);
606
+ --color-primary-rgb: var(--primary-600-rgb);
607
+ --color-danger-rgb: var(--red-600-rgb);
608
+ --color-success-rgb: var(--green-600-rgb);
609
+ --color-warning-rgb: 194,116,10;
610
+ --color-info-rgb: var(--blue-600-rgb);
611
+ --color-surface-rgb: var(--slate-0-rgb);
612
+ --color-inverse-text-rgb: var(--slate-0-rgb);
468
613
  --chart-1: #2a78d6;
469
614
  --chart-2: #eb6834;
470
615
  --chart-3: #1baf7a;
@@ -475,6 +620,37 @@
475
620
  --chart-8: #e34948;
476
621
  --chart-grid: var(--slate-200);
477
622
  --chart-tick: var(--slate-400);
623
+ --tone-1-bg: #d9e7f8;
624
+ --tone-2-bg: #fbe4da;
625
+ --tone-3-bg: #d6f1e7;
626
+ --tone-4-bg: #fceed1;
627
+ --tone-5-bg: #fbe7ef;
628
+ --tone-6-bg: #d1e9d1;
629
+ --tone-7-bg: #dedcef;
630
+ --tone-8-bg: #fadede;
631
+ --tone-1-ink: #214a7e;
632
+ --tone-2-ink: #81422d;
633
+ --tone-3-ink: #196550;
634
+ --tone-4-ink: #825e13;
635
+ --tone-5-ink: #804b65;
636
+ --tone-6-ink: #0c4f13;
637
+ --tone-7-ink: #312b67;
638
+ --tone-8-ink: #7d3237;
639
+ --tone-1-border: #a9c3e4;
640
+ --tone-2-border: #e3beb3;
641
+ --tone-3-border: #a5d3c8;
642
+ --tone-4-border: #e4cfa4;
643
+ --tone-5-border: #e2c4d5;
644
+ --tone-6-border: #9dc6a4;
645
+ --tone-7-border: #b3b0d6;
646
+ --tone-8-border: #e1b5b9;
647
+ --tone-1-border-soft: rgba(42,120,214,.26);
648
+ --tone-2-border-soft: rgba(235,104,52,.26);
649
+ --tone-3-border-soft: rgba(27,175,122,.26);
650
+ --tone-4-border-soft: rgba(237,161,0,.26);
651
+ --tone-5-border-soft: rgba(232,123,164,.26);
652
+ --tone-7-border-soft: rgba(74,58,167,.26);
653
+ --tone-8-border-soft: rgba(227,73,72,.26);
478
654
  --focus-ring: var(--primary-600);
479
655
  }
480
656
  /* Density is deliberately NOT scoped to :root. These are custom properties, so they inherit,
@@ -534,7 +710,11 @@
534
710
  --primary-600: var(--azure-600);
535
711
  --primary-700: var(--azure-700);
536
712
  --primary-800: var(--azure-800);
713
+ --primary-400-rgb: 92,139,246;
714
+ --primary-500-rgb: 52,103,236;
715
+ --primary-600-rgb: 32,80,216;
537
716
  --color-on-primary: var(--slate-0);
717
+ --color-on-primary-rgb: var(--slate-0-rgb);
538
718
  }
539
719
  [data-primary="sky"] {
540
720
  --primary-50: var(--sky-50);
@@ -545,18 +725,25 @@
545
725
  --primary-600: var(--sky-600);
546
726
  --primary-700: var(--sky-700);
547
727
  --primary-800: var(--sky-800);
728
+ --primary-400-rgb: 71,161,230;
729
+ --primary-500-rgb: 26,134,220;
730
+ --primary-600-rgb: 0,120,212;
548
731
  --color-on-primary: var(--slate-0);
732
+ --color-on-primary-rgb: var(--slate-0-rgb);
549
733
  }
550
734
  @media (prefers-color-scheme: dark) {
551
735
  :root:not([data-theme="light"])[data-primary="sky"] {
552
736
  --color-on-primary: var(--slate-950);
737
+ --color-on-primary-rgb: var(--slate-950-rgb);
553
738
  }
554
739
  }
555
740
  [data-theme="dark"][data-primary="sky"] {
556
741
  --color-on-primary: var(--slate-950);
742
+ --color-on-primary-rgb: var(--slate-950-rgb);
557
743
  }
558
744
  [data-theme="light"][data-primary="sky"] {
559
745
  --color-on-primary: var(--slate-0);
746
+ --color-on-primary-rgb: var(--slate-0-rgb);
560
747
  }
561
748
  [data-primary="blue"] {
562
749
  --primary-50: var(--blue-50);
@@ -567,18 +754,25 @@
567
754
  --primary-600: var(--blue-600);
568
755
  --primary-700: var(--blue-700);
569
756
  --primary-800: var(--blue-800);
757
+ --primary-400-rgb: 96,165,250;
758
+ --primary-500-rgb: 59,130,246;
759
+ --primary-600-rgb: 37,99,235;
570
760
  --color-on-primary: var(--slate-0);
761
+ --color-on-primary-rgb: var(--slate-0-rgb);
571
762
  }
572
763
  @media (prefers-color-scheme: dark) {
573
764
  :root:not([data-theme="light"])[data-primary="blue"] {
574
765
  --color-on-primary: var(--slate-950);
766
+ --color-on-primary-rgb: var(--slate-950-rgb);
575
767
  }
576
768
  }
577
769
  [data-theme="dark"][data-primary="blue"] {
578
770
  --color-on-primary: var(--slate-950);
771
+ --color-on-primary-rgb: var(--slate-950-rgb);
579
772
  }
580
773
  [data-theme="light"][data-primary="blue"] {
581
774
  --color-on-primary: var(--slate-0);
775
+ --color-on-primary-rgb: var(--slate-0-rgb);
582
776
  }
583
777
  [data-primary="indigo"] {
584
778
  --primary-50: var(--indigo-50);
@@ -589,27 +783,40 @@
589
783
  --primary-600: var(--indigo-600);
590
784
  --primary-700: var(--indigo-700);
591
785
  --primary-800: var(--indigo-800);
786
+ --primary-400-rgb: 129,140,248;
787
+ --primary-500-rgb: 99,102,241;
788
+ --primary-600-rgb: 79,70,229;
592
789
  --color-on-primary: var(--slate-0);
790
+ --color-on-primary-rgb: var(--slate-0-rgb);
593
791
  }
594
792
  @media (prefers-color-scheme: dark) {
595
793
  :root:not([data-theme="light"])[data-primary="indigo"] {
596
794
  --primary-300: var(--indigo-200);
597
795
  --primary-400: var(--indigo-300);
598
796
  --primary-500: var(--indigo-400);
797
+ --primary-400-rgb: 165,180,252;
798
+ --primary-500-rgb: 129,140,248;
599
799
  --color-on-primary: var(--slate-950);
800
+ --color-on-primary-rgb: var(--slate-950-rgb);
600
801
  }
601
802
  }
602
803
  [data-theme="dark"][data-primary="indigo"] {
603
804
  --primary-300: var(--indigo-200);
604
805
  --primary-400: var(--indigo-300);
605
806
  --primary-500: var(--indigo-400);
807
+ --primary-400-rgb: 165,180,252;
808
+ --primary-500-rgb: 129,140,248;
606
809
  --color-on-primary: var(--slate-950);
810
+ --color-on-primary-rgb: var(--slate-950-rgb);
607
811
  }
608
812
  [data-theme="light"][data-primary="indigo"] {
609
813
  --primary-300: var(--indigo-300);
610
814
  --primary-400: var(--indigo-400);
611
815
  --primary-500: var(--indigo-500);
816
+ --primary-400-rgb: 129,140,248;
817
+ --primary-500-rgb: 99,102,241;
612
818
  --color-on-primary: var(--slate-0);
819
+ --color-on-primary-rgb: var(--slate-0-rgb);
613
820
  }
614
821
  [data-primary="violet"] {
615
822
  --primary-50: var(--violet-50);
@@ -620,27 +827,40 @@
620
827
  --primary-600: var(--violet-600);
621
828
  --primary-700: var(--violet-700);
622
829
  --primary-800: var(--violet-800);
830
+ --primary-400-rgb: 167,139,250;
831
+ --primary-500-rgb: 139,92,246;
832
+ --primary-600-rgb: 124,58,237;
623
833
  --color-on-primary: var(--slate-0);
834
+ --color-on-primary-rgb: var(--slate-0-rgb);
624
835
  }
625
836
  @media (prefers-color-scheme: dark) {
626
837
  :root:not([data-theme="light"])[data-primary="violet"] {
627
838
  --primary-300: var(--violet-200);
628
839
  --primary-400: var(--violet-300);
629
840
  --primary-500: var(--violet-400);
841
+ --primary-400-rgb: 196,181,253;
842
+ --primary-500-rgb: 167,139,250;
630
843
  --color-on-primary: var(--slate-950);
844
+ --color-on-primary-rgb: var(--slate-950-rgb);
631
845
  }
632
846
  }
633
847
  [data-theme="dark"][data-primary="violet"] {
634
848
  --primary-300: var(--violet-200);
635
849
  --primary-400: var(--violet-300);
636
850
  --primary-500: var(--violet-400);
851
+ --primary-400-rgb: 196,181,253;
852
+ --primary-500-rgb: 167,139,250;
637
853
  --color-on-primary: var(--slate-950);
854
+ --color-on-primary-rgb: var(--slate-950-rgb);
638
855
  }
639
856
  [data-theme="light"][data-primary="violet"] {
640
857
  --primary-300: var(--violet-300);
641
858
  --primary-400: var(--violet-400);
642
859
  --primary-500: var(--violet-500);
860
+ --primary-400-rgb: 167,139,250;
861
+ --primary-500-rgb: 139,92,246;
643
862
  --color-on-primary: var(--slate-0);
863
+ --color-on-primary-rgb: var(--slate-0-rgb);
644
864
  }
645
865
  [data-primary="purple"] {
646
866
  --primary-50: var(--purple-50);
@@ -651,18 +871,25 @@
651
871
  --primary-600: var(--purple-600);
652
872
  --primary-700: var(--purple-700);
653
873
  --primary-800: var(--purple-800);
874
+ --primary-400-rgb: 192,132,252;
875
+ --primary-500-rgb: 168,85,247;
876
+ --primary-600-rgb: 147,51,234;
654
877
  --color-on-primary: var(--slate-0);
878
+ --color-on-primary-rgb: var(--slate-0-rgb);
655
879
  }
656
880
  @media (prefers-color-scheme: dark) {
657
881
  :root:not([data-theme="light"])[data-primary="purple"] {
658
882
  --color-on-primary: var(--slate-950);
883
+ --color-on-primary-rgb: var(--slate-950-rgb);
659
884
  }
660
885
  }
661
886
  [data-theme="dark"][data-primary="purple"] {
662
887
  --color-on-primary: var(--slate-950);
888
+ --color-on-primary-rgb: var(--slate-950-rgb);
663
889
  }
664
890
  [data-theme="light"][data-primary="purple"] {
665
891
  --color-on-primary: var(--slate-0);
892
+ --color-on-primary-rgb: var(--slate-0-rgb);
666
893
  }
667
894
  [data-primary="fuchsia"] {
668
895
  --primary-50: var(--fuchsia-50);
@@ -673,18 +900,25 @@
673
900
  --primary-600: var(--fuchsia-600);
674
901
  --primary-700: var(--fuchsia-700);
675
902
  --primary-800: var(--fuchsia-800);
903
+ --primary-400-rgb: 232,121,249;
904
+ --primary-500-rgb: 217,70,239;
905
+ --primary-600-rgb: 192,38,211;
676
906
  --color-on-primary: var(--slate-0);
907
+ --color-on-primary-rgb: var(--slate-0-rgb);
677
908
  }
678
909
  @media (prefers-color-scheme: dark) {
679
910
  :root:not([data-theme="light"])[data-primary="fuchsia"] {
680
911
  --color-on-primary: var(--slate-950);
912
+ --color-on-primary-rgb: var(--slate-950-rgb);
681
913
  }
682
914
  }
683
915
  [data-theme="dark"][data-primary="fuchsia"] {
684
916
  --color-on-primary: var(--slate-950);
917
+ --color-on-primary-rgb: var(--slate-950-rgb);
685
918
  }
686
919
  [data-theme="light"][data-primary="fuchsia"] {
687
920
  --color-on-primary: var(--slate-0);
921
+ --color-on-primary-rgb: var(--slate-0-rgb);
688
922
  }
689
923
  [data-primary="pink"] {
690
924
  --primary-50: var(--pink-50);
@@ -695,18 +929,25 @@
695
929
  --primary-600: var(--pink-600);
696
930
  --primary-700: var(--pink-700);
697
931
  --primary-800: var(--pink-800);
932
+ --primary-400-rgb: 244,114,182;
933
+ --primary-500-rgb: 236,72,153;
934
+ --primary-600-rgb: 219,39,119;
698
935
  --color-on-primary: var(--slate-0);
936
+ --color-on-primary-rgb: var(--slate-0-rgb);
699
937
  }
700
938
  @media (prefers-color-scheme: dark) {
701
939
  :root:not([data-theme="light"])[data-primary="pink"] {
702
940
  --color-on-primary: var(--slate-950);
941
+ --color-on-primary-rgb: var(--slate-950-rgb);
703
942
  }
704
943
  }
705
944
  [data-theme="dark"][data-primary="pink"] {
706
945
  --color-on-primary: var(--slate-950);
946
+ --color-on-primary-rgb: var(--slate-950-rgb);
707
947
  }
708
948
  [data-theme="light"][data-primary="pink"] {
709
949
  --color-on-primary: var(--slate-0);
950
+ --color-on-primary-rgb: var(--slate-0-rgb);
710
951
  }
711
952
  [data-primary="rose"] {
712
953
  --primary-50: var(--rose-50);
@@ -717,18 +958,25 @@
717
958
  --primary-600: var(--rose-600);
718
959
  --primary-700: var(--rose-700);
719
960
  --primary-800: var(--rose-800);
961
+ --primary-400-rgb: 251,113,133;
962
+ --primary-500-rgb: 244,63,94;
963
+ --primary-600-rgb: 225,29,72;
720
964
  --color-on-primary: var(--slate-0);
965
+ --color-on-primary-rgb: var(--slate-0-rgb);
721
966
  }
722
967
  @media (prefers-color-scheme: dark) {
723
968
  :root:not([data-theme="light"])[data-primary="rose"] {
724
969
  --color-on-primary: var(--slate-950);
970
+ --color-on-primary-rgb: var(--slate-950-rgb);
725
971
  }
726
972
  }
727
973
  [data-theme="dark"][data-primary="rose"] {
728
974
  --color-on-primary: var(--slate-950);
975
+ --color-on-primary-rgb: var(--slate-950-rgb);
729
976
  }
730
977
  [data-theme="light"][data-primary="rose"] {
731
978
  --color-on-primary: var(--slate-0);
979
+ --color-on-primary-rgb: var(--slate-0-rgb);
732
980
  }
733
981
  [data-primary="orange"] {
734
982
  --primary-50: var(--orange-50);
@@ -739,7 +987,11 @@
739
987
  --primary-600: var(--orange-600);
740
988
  --primary-700: var(--orange-700);
741
989
  --primary-800: var(--orange-800);
990
+ --primary-400-rgb: 251,146,60;
991
+ --primary-500-rgb: 249,115,22;
992
+ --primary-600-rgb: 234,88,12;
742
993
  --color-on-primary: var(--slate-950);
994
+ --color-on-primary-rgb: var(--slate-950-rgb);
743
995
  }
744
996
  [data-primary="amber"] {
745
997
  --primary-50: var(--amber-50);
@@ -750,7 +1002,11 @@
750
1002
  --primary-600: var(--amber-600);
751
1003
  --primary-700: var(--amber-700);
752
1004
  --primary-800: var(--amber-800);
1005
+ --primary-400-rgb: 251,191,36;
1006
+ --primary-500-rgb: 245,158,11;
1007
+ --primary-600-rgb: 217,119,6;
753
1008
  --color-on-primary: var(--slate-950);
1009
+ --color-on-primary-rgb: var(--slate-950-rgb);
754
1010
  }
755
1011
  [data-primary="yellow"] {
756
1012
  --primary-50: var(--yellow-50);
@@ -761,18 +1017,25 @@
761
1017
  --primary-600: var(--yellow-700);
762
1018
  --primary-700: var(--yellow-800);
763
1019
  --primary-800: var(--yellow-900);
1020
+ --primary-400-rgb: 250,204,21;
1021
+ --primary-500-rgb: 234,179,8;
1022
+ --primary-600-rgb: 161,98,7;
764
1023
  --color-on-primary: var(--slate-0);
1024
+ --color-on-primary-rgb: var(--slate-0-rgb);
765
1025
  }
766
1026
  @media (prefers-color-scheme: dark) {
767
1027
  :root:not([data-theme="light"])[data-primary="yellow"] {
768
1028
  --color-on-primary: var(--slate-950);
1029
+ --color-on-primary-rgb: var(--slate-950-rgb);
769
1030
  }
770
1031
  }
771
1032
  [data-theme="dark"][data-primary="yellow"] {
772
1033
  --color-on-primary: var(--slate-950);
1034
+ --color-on-primary-rgb: var(--slate-950-rgb);
773
1035
  }
774
1036
  [data-theme="light"][data-primary="yellow"] {
775
1037
  --color-on-primary: var(--slate-0);
1038
+ --color-on-primary-rgb: var(--slate-0-rgb);
776
1039
  }
777
1040
  [data-primary="lime"] {
778
1041
  --primary-50: var(--lime-50);
@@ -783,7 +1046,11 @@
783
1046
  --primary-600: var(--lime-600);
784
1047
  --primary-700: var(--lime-700);
785
1048
  --primary-800: var(--lime-800);
1049
+ --primary-400-rgb: 163,230,53;
1050
+ --primary-500-rgb: 132,204,22;
1051
+ --primary-600-rgb: 101,163,13;
786
1052
  --color-on-primary: var(--slate-950);
1053
+ --color-on-primary-rgb: var(--slate-950-rgb);
787
1054
  }
788
1055
  [data-primary="green"] {
789
1056
  --primary-50: var(--green-50);
@@ -794,7 +1061,11 @@
794
1061
  --primary-600: var(--green-600);
795
1062
  --primary-700: var(--green-700);
796
1063
  --primary-800: var(--green-800);
1064
+ --primary-400-rgb: 74,222,128;
1065
+ --primary-500-rgb: 34,197,94;
1066
+ --primary-600-rgb: 22,163,74;
797
1067
  --color-on-primary: var(--slate-950);
1068
+ --color-on-primary-rgb: var(--slate-950-rgb);
798
1069
  }
799
1070
  [data-primary="emerald"] {
800
1071
  --primary-50: var(--emerald-50);
@@ -805,7 +1076,11 @@
805
1076
  --primary-600: var(--emerald-600);
806
1077
  --primary-700: var(--emerald-700);
807
1078
  --primary-800: var(--emerald-800);
1079
+ --primary-400-rgb: 52,211,153;
1080
+ --primary-500-rgb: 16,185,129;
1081
+ --primary-600-rgb: 5,150,105;
808
1082
  --color-on-primary: var(--slate-950);
1083
+ --color-on-primary-rgb: var(--slate-950-rgb);
809
1084
  }
810
1085
  [data-primary="teal"] {
811
1086
  --primary-50: var(--teal-50);
@@ -816,7 +1091,11 @@
816
1091
  --primary-600: var(--teal-600);
817
1092
  --primary-700: var(--teal-700);
818
1093
  --primary-800: var(--teal-800);
1094
+ --primary-400-rgb: 45,212,191;
1095
+ --primary-500-rgb: 20,184,166;
1096
+ --primary-600-rgb: 13,148,136;
819
1097
  --color-on-primary: var(--slate-950);
1098
+ --color-on-primary-rgb: var(--slate-950-rgb);
820
1099
  }
821
1100
  [data-primary="cyan"] {
822
1101
  --primary-50: var(--cyan-50);
@@ -827,7 +1106,11 @@
827
1106
  --primary-600: var(--cyan-600);
828
1107
  --primary-700: var(--cyan-700);
829
1108
  --primary-800: var(--cyan-800);
1109
+ --primary-400-rgb: 34,211,238;
1110
+ --primary-500-rgb: 6,182,212;
1111
+ --primary-600-rgb: 8,145,178;
830
1112
  --color-on-primary: var(--slate-950);
1113
+ --color-on-primary-rgb: var(--slate-950-rgb);
831
1114
  }
832
1115
  [data-primary="noir"] {
833
1116
  --primary-50: var(--slate-100);
@@ -838,7 +1121,11 @@
838
1121
  --primary-600: var(--slate-900);
839
1122
  --primary-700: var(--slate-800);
840
1123
  --primary-800: var(--slate-950);
1124
+ --primary-400-rgb: 58,65,82;
1125
+ --primary-500-rgb: 38,43,56;
1126
+ --primary-600-rgb: 23,27,38;
841
1127
  --color-on-primary: var(--slate-0);
1128
+ --color-on-primary-rgb: var(--slate-0-rgb);
842
1129
  }
843
1130
  @media (prefers-color-scheme: dark) {
844
1131
  :root:not([data-theme="light"])[data-primary="noir"] {
@@ -850,7 +1137,11 @@
850
1137
  --primary-600: var(--slate-50);
851
1138
  --primary-700: var(--slate-200);
852
1139
  --primary-800: var(--slate-300);
1140
+ --primary-400-rgb: 224,227,234;
1141
+ --primary-500-rgb: 238,240,244;
1142
+ --primary-600-rgb: 247,248,250;
853
1143
  --color-on-primary: var(--slate-950);
1144
+ --color-on-primary-rgb: var(--slate-950-rgb);
854
1145
  }
855
1146
  }
856
1147
  [data-theme="dark"][data-primary="noir"] {
@@ -862,7 +1153,11 @@
862
1153
  --primary-600: var(--slate-50);
863
1154
  --primary-700: var(--slate-200);
864
1155
  --primary-800: var(--slate-300);
1156
+ --primary-400-rgb: 224,227,234;
1157
+ --primary-500-rgb: 238,240,244;
1158
+ --primary-600-rgb: 247,248,250;
865
1159
  --color-on-primary: var(--slate-950);
1160
+ --color-on-primary-rgb: var(--slate-950-rgb);
866
1161
  }
867
1162
  [data-theme="light"][data-primary="noir"] {
868
1163
  --primary-50: var(--slate-100);
@@ -873,7 +1168,11 @@
873
1168
  --primary-600: var(--slate-900);
874
1169
  --primary-700: var(--slate-800);
875
1170
  --primary-800: var(--slate-950);
1171
+ --primary-400-rgb: 58,65,82;
1172
+ --primary-500-rgb: 38,43,56;
1173
+ --primary-600-rgb: 23,27,38;
876
1174
  --color-on-primary: var(--slate-0);
1175
+ --color-on-primary-rgb: var(--slate-0-rgb);
877
1176
  }
878
1177
 
879
1178
  /* Freeday components. GENERATED by tokens/build.mjs. Do not edit by hand. */
@@ -890,7 +1189,7 @@ body {
890
1189
  -webkit-font-smoothing: antialiased;
891
1190
  }
892
1191
  h1, h2, h3, h4 { font-family: var(--font-display); font-weight: var(--weight-bold); letter-spacing: var(--tracking-tighter); line-height: var(--leading-snug); margin: 0; }
893
- a { color: var(--color-primary); }
1192
+ a { color: var(--color-primary-strong); }
894
1193
  :focus-visible { outline: var(--focus-ring-width) solid var(--focus-ring); outline-offset: var(--focus-ring-width); border-radius: var(--radius-xs); }
895
1194
  :where(button, input, select, textarea) { font: inherit; }
896
1195
  /* Hidden from sight, kept for assistive tech. `clip` hides PAINTING, not LAYOUT: the box still
@@ -930,7 +1229,7 @@ a { color: var(--color-primary); }
930
1229
  .fdy-accordion__item > summary::after{content:"";margin-left:auto;margin-top:-.15rem;flex:none;width:.5rem;height:.5rem;border-right:2px solid var(--color-text-subtle);border-bottom:2px solid var(--color-text-subtle);transform:rotate(45deg);transition:transform var(--dur-fast) var(--ease-standard);}
931
1230
  .fdy-accordion__item[open] > summary::after{transform:rotate(225deg);margin-top:.1rem;}
932
1231
  .fdy-accordion__item > summary:hover{background:var(--color-surface-2);}
933
- .fdy-accordion__item > summary:focus-visible{outline:none;box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
1232
+ .fdy-accordion__item > summary:focus-visible{outline:none;box-shadow:inset 0 0 0 2px rgba(var(--color-primary-rgb),.45);}
934
1233
  .fdy-accordion__panel{padding:0 var(--space-4) var(--space-4);color:var(--color-text-muted);font-size:var(--text-sm);line-height:var(--leading-normal);}
935
1234
  /* Content reveal on open (fade + rise). Native <details> can't animate its own height cross-
936
1235
  * browser without JS, so we animate the revealed panel instead, cheap, safe, reduced-motion gated. */
@@ -945,14 +1244,14 @@ a { color: var(--color-primary); }
945
1244
  .fdy-alert__text{color:var(--color-text);line-height:var(--leading-normal);}
946
1245
  .fdy-alert__close{flex:none;align-self:flex-start;border:0;background:transparent;color:var(--color-text-muted);cursor:pointer;font-size:var(--text-lg);line-height:1;padding:0 var(--space-1);border-radius:var(--radius-sm);}
947
1246
  .fdy-alert__close:hover{color:var(--color-text);}
948
- .fdy-alert__close:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
949
- .fdy-alert--info{background:var(--color-info-soft);border-color:color-mix(in srgb,var(--color-info) 26%,transparent);}
1247
+ .fdy-alert__close:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
1248
+ .fdy-alert--info{background:var(--color-info-soft);border-color:rgba(var(--color-info-rgb),.26);}
950
1249
  .fdy-alert--info .fdy-alert__title,.fdy-alert--info .fdy-alert__icon{color:var(--color-info-strong);}
951
- .fdy-alert--success{background:var(--color-success-soft);border-color:color-mix(in srgb,var(--color-success) 26%,transparent);}
1250
+ .fdy-alert--success{background:var(--color-success-soft);border-color:rgba(var(--color-success-rgb),.26);}
952
1251
  .fdy-alert--success .fdy-alert__title,.fdy-alert--success .fdy-alert__icon{color:var(--color-success-strong);}
953
- .fdy-alert--warning{background:var(--color-warning-soft);border-color:color-mix(in srgb,var(--color-warning) 26%,transparent);}
1252
+ .fdy-alert--warning{background:var(--color-warning-soft);border-color:rgba(var(--color-warning-rgb),.26);}
954
1253
  .fdy-alert--warning .fdy-alert__title,.fdy-alert--warning .fdy-alert__icon{color:var(--color-warning-strong);}
955
- .fdy-alert--danger{background:var(--color-danger-soft);border-color:color-mix(in srgb,var(--color-danger) 26%,transparent);}
1254
+ .fdy-alert--danger{background:var(--color-danger-soft);border-color:rgba(var(--color-danger-rgb),.26);}
956
1255
  .fdy-alert--danger .fdy-alert__title,.fdy-alert--danger .fdy-alert__icon{color:var(--color-danger-strong);}
957
1256
 
958
1257
  /* Freeday. App Shell.
@@ -1001,7 +1300,7 @@ a { color: var(--color-primary); }
1001
1300
  .fdy-nav__item:hover{background:var(--color-surface-2);color:var(--color-text);}
1002
1301
  /* Active indicator stays in the primary ramp so it never clashes with a re-themed accent. */
1003
1302
  .fdy-nav__item[aria-current="page"]{background:var(--color-primary-soft);color:var(--color-primary-strong);font-weight:var(--weight-semibold);}
1004
- .fdy-nav__item:focus-visible{outline:none;box-shadow:0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
1303
+ .fdy-nav__item:focus-visible{outline:none;box-shadow:0 0 0 2px rgba(var(--color-primary-rgb),.45);}
1005
1304
  /* Sidebar-menu extras: per-node icon (leading), count badge (trailing), and a
1006
1305
  * text label that truncates. Any of these are optional, a bare text item works. */
1007
1306
  .fdy-nav__icon{flex:none;display:inline-flex;width:1.15rem;height:1.15rem;}
@@ -1019,7 +1318,7 @@ a { color: var(--color-primary); }
1019
1318
  .fdy-nav__grouplabel{list-style:none;cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);padding:var(--space-3) var(--space-3) var(--space-1);font-size:var(--text-xs);text-transform:uppercase;letter-spacing:var(--tracking-wide);font-weight:var(--weight-semibold);color:var(--color-text-muted);}
1020
1319
  .fdy-nav__grouplabel::-webkit-details-marker{display:none;}
1021
1320
  .fdy-nav__grouplabel:hover{color:var(--color-text-muted);}
1022
- .fdy-nav__grouplabel:focus-visible{outline:none;border-radius:var(--radius-sm);box-shadow:0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
1321
+ .fdy-nav__grouplabel:focus-visible{outline:none;border-radius:var(--radius-sm);box-shadow:0 0 0 2px rgba(var(--color-primary-rgb),.45);}
1023
1322
  .fdy-nav__grouplabel::after{content:"";flex:none;width:.45rem;height:.45rem;margin-right:var(--space-1);border-right:2px solid currentColor;border-bottom:2px solid currentColor;transform:rotate(-45deg);transition:transform var(--dur-fast) var(--ease-standard);opacity:.7;}
1024
1323
  .fdy-nav__group[open] > .fdy-nav__grouplabel::after{transform:rotate(45deg);}
1025
1324
  /* --flat: calmer, non-collapsible grouped nav, hide the caret, drop the
@@ -1120,13 +1419,13 @@ a { color: var(--color-primary); }
1120
1419
  is the same component whether the nav is a sidebar column or a top row. */
1121
1420
  /* Controls on a coloured app bar go on-colour (icons stay legible, hover is a light wash) */
1122
1421
  .fdy-appbar--primary .fdy-btn--ghost{background:transparent;border-color:transparent;color:var(--color-on-primary);box-shadow:none;}
1123
- .fdy-appbar--primary .fdy-btn--ghost:hover{background:color-mix(in srgb,var(--color-on-primary) 16%,transparent);color:var(--color-on-primary);transform:none;}
1422
+ .fdy-appbar--primary .fdy-btn--ghost:hover{background:rgba(var(--color-on-primary-rgb),.16);color:var(--color-on-primary);transform:none;}
1124
1423
  /* Nav links on a coloured bar: full on-colour ink in every state (a dimmed variant would trade away
1125
1424
  the contrast the bar's own token pair guarantees). The current page is carried by the background
1126
1425
  wash plus the semibold weight the base rule already applies, and by `aria-current` itself. */
1127
1426
  .fdy-appbar--primary .fdy-nav__item{color:var(--color-on-primary);}
1128
- .fdy-appbar--primary .fdy-nav__item:hover{background:color-mix(in srgb,var(--color-on-primary) 16%,transparent);color:var(--color-on-primary);}
1129
- .fdy-appbar--primary .fdy-nav__item[aria-current="page"]{background:color-mix(in srgb,var(--color-on-primary) 24%,transparent);color:var(--color-on-primary);}
1427
+ .fdy-appbar--primary .fdy-nav__item:hover{background:rgba(var(--color-on-primary-rgb),.16);color:var(--color-on-primary);}
1428
+ .fdy-appbar--primary .fdy-nav__item[aria-current="page"]{background:rgba(var(--color-on-primary-rgb),.24);color:var(--color-on-primary);}
1130
1429
 
1131
1430
  /* Freeday. Autocomplete (text input + filtered suggestion listbox, APG editable combobox).
1132
1431
  * Enhanced by freeday-autocomplete.js. */
@@ -1151,6 +1450,8 @@ a { color: var(--color-primary); }
1151
1450
  .fdy-avatar--xs{width:1.5rem;height:1.5rem;font-size:var(--text-xs);}
1152
1451
  .fdy-avatar--sm{width:2rem;height:2rem;font-size:var(--text-xs);}
1153
1452
  .fdy-avatar--lg{width:3.5rem;height:3.5rem;font-size:var(--text-lg);}
1453
+ /* Square: a mark that is not a person (a company logo, a card's icon chip, #070). */
1454
+ .fdy-avatar--square{border-radius:var(--radius-md);}
1154
1455
  .fdy-avatar-group{display:inline-flex;}
1155
1456
  .fdy-avatar-group .fdy-avatar{border:2px solid var(--color-surface);margin-left:calc(var(--space-2)*-1);}
1156
1457
  .fdy-avatar-group .fdy-avatar:first-child{margin-left:0;}
@@ -1158,22 +1459,28 @@ a { color: var(--color-primary); }
1158
1459
  * text-leaning foreground so initials keep WCAG AA (verified >=4.5:1 in light & dark by
1159
1460
  * test/contrast.test.mjs). Use to disambiguate same-initial avatars (e.g. many "PT …" names):
1160
1461
  * hash a stable 1..8 index off the FULL name in your app, then apply .fdy-avatar--tone-<n>. */
1161
- .fdy-avatar--tone-1,.fdy-avatar--tone-2,.fdy-avatar--tone-3,.fdy-avatar--tone-4,.fdy-avatar--tone-5,.fdy-avatar--tone-6,.fdy-avatar--tone-7,.fdy-avatar--tone-8{background:color-mix(in srgb,var(--_fdy-avatar-tone) 18%,var(--color-surface));color:color-mix(in srgb,var(--_fdy-avatar-tone) 50%,var(--color-text));}
1162
- .fdy-avatar--tone-1{--_fdy-avatar-tone:var(--tone-1);}
1163
- .fdy-avatar--tone-2{--_fdy-avatar-tone:var(--tone-2);}
1164
- .fdy-avatar--tone-3{--_fdy-avatar-tone:var(--tone-3);}
1165
- .fdy-avatar--tone-4{--_fdy-avatar-tone:var(--tone-4);}
1166
- .fdy-avatar--tone-5{--_fdy-avatar-tone:var(--tone-5);}
1167
- .fdy-avatar--tone-6{--_fdy-avatar-tone:var(--tone-6);}
1168
- .fdy-avatar--tone-7{--_fdy-avatar-tone:var(--tone-7);}
1169
- .fdy-avatar--tone-8{--_fdy-avatar-tone:var(--tone-8);}
1462
+ /* One flat token per half, computed in advance by tokens/build.mjs: --tone-N-bg is 18% of the
1463
+ * tone over --color-surface, --tone-N-ink is 50% of it toward --color-text, both per theme.
1464
+ * They USED to be mixed here, with the hue injected per element through a private custom
1465
+ * property — which put var() inside color-mix(), the one shape that cannot be rescued: valid at
1466
+ * parse time, invalid at computed-value time, and there `background` and `color` drop to their
1467
+ * INITIAL values, not to any fallback. @supports answered it for exactly as long as somebody
1468
+ * remembered to write the counterpart; a precomputed token removes the question. */
1469
+ .fdy-avatar--tone-1{background:var(--tone-1-bg);color:var(--tone-1-ink);}
1470
+ .fdy-avatar--tone-2{background:var(--tone-2-bg);color:var(--tone-2-ink);}
1471
+ .fdy-avatar--tone-3{background:var(--tone-3-bg);color:var(--tone-3-ink);}
1472
+ .fdy-avatar--tone-4{background:var(--tone-4-bg);color:var(--tone-4-ink);}
1473
+ .fdy-avatar--tone-5{background:var(--tone-5-bg);color:var(--tone-5-ink);}
1474
+ .fdy-avatar--tone-6{background:var(--tone-6-bg);color:var(--tone-6-ink);}
1475
+ .fdy-avatar--tone-7{background:var(--tone-7-bg);color:var(--tone-7-ink);}
1476
+ .fdy-avatar--tone-8{background:var(--tone-8-bg);color:var(--tone-8-ink);}
1170
1477
 
1171
1478
  /* Freeday. Badge */
1172
- .fdy-badge{display:inline-flex;align-items:center;gap:var(--space-1);font-family:var(--font-body);font-size:var(--text-xs);font-weight:var(--weight-semibold);line-height:1;white-space:nowrap;padding:.35em .7em;border-radius:var(--radius-full);color:var(--color-primary-strong);background:var(--color-primary-soft);border:var(--bw) solid color-mix(in srgb,var(--color-primary) 22%,transparent);box-shadow:0 1px 1.5px rgba(16,14,30,.05);}
1173
- .fdy-badge--success{color:var(--color-success-strong);background:var(--color-success-soft);border-color:color-mix(in srgb,var(--color-success) 26%,transparent);}
1174
- .fdy-badge--warning{color:var(--color-warning-strong);background:var(--color-warning-soft);border-color:color-mix(in srgb,var(--color-warning) 26%,transparent);}
1175
- .fdy-badge--danger{color:var(--color-danger-strong);background:var(--color-danger-soft);border-color:color-mix(in srgb,var(--color-danger) 26%,transparent);}
1176
- .fdy-badge--info{color:var(--color-info-strong);background:var(--color-info-soft);border-color:color-mix(in srgb,var(--color-info) 26%,transparent);}
1479
+ .fdy-badge{display:inline-flex;align-items:center;gap:var(--space-1);font-family:var(--font-body);font-size:var(--text-xs);font-weight:var(--weight-semibold);line-height:1;white-space:nowrap;padding:.35em .7em;border-radius:var(--radius-full);color:var(--color-primary-strong);background:var(--color-primary-soft);border:var(--bw) solid rgba(var(--color-primary-rgb),.22);box-shadow:0 1px 1.5px rgba(16,14,30,.05);}
1480
+ .fdy-badge--success{color:var(--color-success-strong);background:var(--color-success-soft);border-color:rgba(var(--color-success-rgb),.26);}
1481
+ .fdy-badge--warning{color:var(--color-warning-strong);background:var(--color-warning-soft);border-color:rgba(var(--color-warning-rgb),.26);}
1482
+ .fdy-badge--danger{color:var(--color-danger-strong);background:var(--color-danger-soft);border-color:rgba(var(--color-danger-rgb),.26);}
1483
+ .fdy-badge--info{color:var(--color-info-strong);background:var(--color-info-soft);border-color:rgba(var(--color-info-rgb),.26);}
1177
1484
  .fdy-badge--outline{background:transparent;border-color:var(--color-border-strong);color:var(--color-text-muted);box-shadow:none;}
1178
1485
 
1179
1486
  /* Categorical tone badges, a status vocabulary larger than the semantic palette, from the
@@ -1185,15 +1492,19 @@ a { color: var(--color-primary); }
1185
1492
  * cannot. A back-office document list carrying Draft, Submitted, Approved, Completed, Settled,
1186
1493
  * Closed, Transferred, InDeclaration, Open and Rejected in ONE column collapsed to three colours,
1187
1494
  * which is the report this shipped for. */
1188
- .fdy-badge--tone-1,.fdy-badge--tone-2,.fdy-badge--tone-3,.fdy-badge--tone-4,.fdy-badge--tone-5,.fdy-badge--tone-6,.fdy-badge--tone-7,.fdy-badge--tone-8{background:color-mix(in srgb,var(--_fdy-badge-tone) 18%,var(--color-surface));color:color-mix(in srgb,var(--_fdy-badge-tone) 50%,var(--color-text));border-color:color-mix(in srgb,var(--_fdy-badge-tone) 26%,transparent);}
1189
- .fdy-badge--tone-1{--_fdy-badge-tone:var(--tone-1);}
1190
- .fdy-badge--tone-2{--_fdy-badge-tone:var(--tone-2);}
1191
- .fdy-badge--tone-3{--_fdy-badge-tone:var(--tone-3);}
1192
- .fdy-badge--tone-4{--_fdy-badge-tone:var(--tone-4);}
1193
- .fdy-badge--tone-5{--_fdy-badge-tone:var(--tone-5);}
1194
- .fdy-badge--tone-6{--_fdy-badge-tone:var(--tone-6);}
1195
- .fdy-badge--tone-7{--_fdy-badge-tone:var(--tone-7);}
1196
- .fdy-badge--tone-8{--_fdy-badge-tone:var(--tone-8);}
1495
+ /* Precomputed tone tokens, the same --tone-N-bg / --tone-N-ink the avatar and the chip read, so
1496
+ * one contrast assertion still covers all three. --tone-N-border-soft is the badge's own edge:
1497
+ * 26% of the tone over transparent, which is a translucent colour and therefore stays rgba()
1498
+ * rather than flattening to a hex. See avatar.css for why none of this is mixed in the
1499
+ * component any more, and tokens/build.mjs for where the mixing moved to. */
1500
+ .fdy-badge--tone-1{background:var(--tone-1-bg);color:var(--tone-1-ink);border-color:var(--tone-1-border-soft);}
1501
+ .fdy-badge--tone-2{background:var(--tone-2-bg);color:var(--tone-2-ink);border-color:var(--tone-2-border-soft);}
1502
+ .fdy-badge--tone-3{background:var(--tone-3-bg);color:var(--tone-3-ink);border-color:var(--tone-3-border-soft);}
1503
+ .fdy-badge--tone-4{background:var(--tone-4-bg);color:var(--tone-4-ink);border-color:var(--tone-4-border-soft);}
1504
+ .fdy-badge--tone-5{background:var(--tone-5-bg);color:var(--tone-5-ink);border-color:var(--tone-5-border-soft);}
1505
+ .fdy-badge--tone-6{background:var(--tone-6-bg);color:var(--tone-6-ink);border-color:var(--tone-6-border-soft);}
1506
+ .fdy-badge--tone-7{background:var(--tone-7-bg);color:var(--tone-7-ink);border-color:var(--tone-7-border-soft);}
1507
+ .fdy-badge--tone-8{background:var(--tone-8-bg);color:var(--tone-8-ink);border-color:var(--tone-8-border-soft);}
1197
1508
 
1198
1509
  /* Overlay badge, anchors a small count/dot/icon badge to a corner of any element
1199
1510
  * (icon, button, text). Wrap the content in .fdy-badge-wrap; add .fdy-badge-ov as a
@@ -1284,7 +1595,7 @@ a { color: var(--color-primary); }
1284
1595
  .fdy-breadcrumb__item{display:inline-flex;align-items:center;gap:var(--space-2);color:var(--color-text-muted);}
1285
1596
  .fdy-breadcrumb__link{color:var(--color-text-muted);text-decoration:none;border-radius:var(--radius-sm);}
1286
1597
  .fdy-breadcrumb__link:hover{color:var(--color-primary-strong);text-decoration:underline;}
1287
- .fdy-breadcrumb__link:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
1598
+ .fdy-breadcrumb__link:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
1288
1599
  .fdy-breadcrumb__item[aria-current="page"]{color:var(--color-text);font-weight:var(--weight-medium);}
1289
1600
  .fdy-breadcrumb__sep{color:var(--color-text-subtle);user-select:none;}
1290
1601
 
@@ -1354,16 +1665,24 @@ a { color: var(--color-primary); }
1354
1665
  }
1355
1666
 
1356
1667
  /* Freeday. Button */
1668
+ /* The fill is a FLAT colour with a black veil laid over it, not a gradient between two shades.
1669
+ * It used to be `linear-gradient(primary, color-mix(in srgb, #000 10%, primary))`, and that is the
1670
+ * same colour at every stop — veiling a solid base with black 0 -> .1 darkens it by exactly 10% —
1671
+ * but the mix form carries var(), so on Safari <= 16.1 the whole declaration failed at
1672
+ * computed-value time and the button lost its fill rather than falling back. Measured identical to
1673
+ * the mix it replaces (within the 1/255 the gradient's own 8-bit rounding costs); see
1674
+ * test/color-mix.test.mjs for the rule and tokens/build.mjs for why none of these may use
1675
+ * color-mix(). Pressed states veil from the TOP, which is the same trick read upside down. */
1357
1676
  .fdy-btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);
1358
1677
  height:var(--control-h);padding:0 var(--space-4);
1359
1678
  font-family:var(--font-body);font-size:var(--text-sm);font-weight:var(--weight-semibold);
1360
1679
  line-height:1;white-space:nowrap;cursor:pointer;text-decoration:none;color:var(--color-on-primary);
1361
- background:linear-gradient(180deg,var(--color-primary),color-mix(in srgb,#000 10%,var(--color-primary)));
1680
+ background:var(--color-primary) linear-gradient(180deg,rgba(0,0,0,0),rgba(0,0,0,.1));
1362
1681
  border:var(--bw) solid transparent;border-radius:var(--radius-md);
1363
- box-shadow:0 2px 9px -1px color-mix(in srgb,var(--color-primary) 50%,transparent),inset 0 1px 0 rgba(255,255,255,.26);
1682
+ box-shadow:0 2px 9px -1px rgba(var(--color-primary-rgb),.5),inset 0 1px 0 rgba(255,255,255,.26);
1364
1683
  transition:background var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard),transform var(--dur-fast) var(--ease-standard);}
1365
- .fdy-btn:hover{transform:translateY(-1px);box-shadow:0 6px 18px -2px color-mix(in srgb,var(--color-primary) 60%,transparent),inset 0 1px 0 rgba(255,255,255,.26);}
1366
- .fdy-btn:active{transform:translateY(1px);box-shadow:0 1px 4px -1px color-mix(in srgb,var(--color-primary) 46%,transparent);}
1684
+ .fdy-btn:hover{transform:translateY(-1px);box-shadow:0 6px 18px -2px rgba(var(--color-primary-rgb),.6),inset 0 1px 0 rgba(255,255,255,.26);}
1685
+ .fdy-btn:active{transform:translateY(1px);box-shadow:0 1px 4px -1px rgba(var(--color-primary-rgb),.46);}
1367
1686
  .fdy-btn:disabled{opacity:.5;cursor:not-allowed;transform:none;box-shadow:none;}
1368
1687
  /* Pressed / toggle. `aria-pressed` is already the right attribute for a toggle button, so this adds
1369
1688
  no class and no new markup contract, and it is what makes .fdy-btn-group a complete segmented
@@ -1371,8 +1690,8 @@ a { color: var(--color-primary); }
1371
1690
  defines its OWN pressed look below; a single shared rule cannot work, because a gradient is a
1372
1691
  background-IMAGE and would blank the ghost variant's background-color. Solid: invert the gradient
1373
1692
  and sink the shadow inward, so a held button reads as depressed, not merely primary-coloured. */
1374
- .fdy-btn[aria-pressed="true"]{background:linear-gradient(180deg,color-mix(in srgb,#000 12%,var(--color-primary)),var(--color-primary));box-shadow:inset 0 2px 6px color-mix(in srgb,#000 22%,transparent);}
1375
- .fdy-btn[aria-pressed="true"]:hover{transform:none;box-shadow:inset 0 2px 6px color-mix(in srgb,#000 22%,transparent);}
1693
+ .fdy-btn[aria-pressed="true"]{background:var(--color-primary) linear-gradient(180deg,rgba(0,0,0,.12),rgba(0,0,0,0));box-shadow:inset 0 2px 6px rgba(0,0,0,.22);}
1694
+ .fdy-btn[aria-pressed="true"]:hover{transform:none;box-shadow:inset 0 2px 6px rgba(0,0,0,.22);}
1376
1695
 
1377
1696
  .fdy-btn--ghost{background:var(--color-surface);color:var(--color-primary-strong);border-color:var(--color-border-strong);box-shadow:var(--shadow-1);}
1378
1697
  .fdy-btn--ghost:hover{transform:none;background:var(--color-primary-soft);border-color:var(--color-primary-border);box-shadow:var(--shadow-1);}
@@ -1384,15 +1703,15 @@ a { color: var(--color-primary); }
1384
1703
  screen. `.fdy-menu__item--danger` has always modelled the quiet form (red text, not a red block);
1385
1704
  the pairing rules below bring it to the button. They are more specific than this one and come
1386
1705
  after it, so this rule needs no :not() guard of its own, one mechanism, not two. */
1387
- .fdy-btn--danger{background:linear-gradient(180deg,var(--color-danger-btn),color-mix(in srgb,#000 12%,var(--color-danger-btn)));color:var(--color-on-danger);box-shadow:0 2px 9px -1px color-mix(in srgb,var(--color-danger-btn) 46%,transparent),inset 0 1px 0 rgba(255,255,255,.22);}
1388
- .fdy-btn--danger:hover{transform:translateY(-1px);box-shadow:0 6px 18px -2px color-mix(in srgb,var(--color-danger-btn) 54%,transparent),inset 0 1px 0 rgba(255,255,255,.22);}
1706
+ .fdy-btn--danger{background:var(--color-danger-btn) linear-gradient(180deg,rgba(0,0,0,0),rgba(0,0,0,.12));color:var(--color-on-danger);box-shadow:0 2px 9px -1px rgba(var(--color-danger-btn-rgb),.46),inset 0 1px 0 rgba(255,255,255,.22);}
1707
+ .fdy-btn--danger:hover{transform:translateY(-1px);box-shadow:0 6px 18px -2px rgba(var(--color-danger-btn-rgb),.54),inset 0 1px 0 rgba(255,255,255,.22);}
1389
1708
  .fdy-btn--danger:active{transform:translateY(1px);}
1390
1709
  /* Quiet destructive: the variant keeps its own ground and only the ink turns. */
1391
- .fdy-btn--ghost.fdy-btn--danger{background:transparent;color:var(--color-danger-strong);border-color:color-mix(in srgb,var(--color-danger) 40%,transparent);box-shadow:none;}
1710
+ .fdy-btn--ghost.fdy-btn--danger{background:transparent;color:var(--color-danger-strong);border-color:rgba(var(--color-danger-rgb),.4);box-shadow:none;}
1392
1711
  .fdy-btn--ghost.fdy-btn--danger:hover{background:var(--color-danger-soft);border-color:var(--color-danger);color:var(--color-danger-strong);}
1393
1712
  .fdy-btn--text.fdy-btn--danger{background:none;color:var(--color-danger-strong);border-color:transparent;box-shadow:none;}
1394
1713
  .fdy-btn--text.fdy-btn--danger:hover{background:var(--color-danger-soft);color:var(--color-danger-strong);}
1395
- .fdy-btn--danger[aria-pressed="true"]{background:linear-gradient(180deg,color-mix(in srgb,#000 14%,var(--color-danger-btn)),var(--color-danger-btn));box-shadow:inset 0 2px 6px color-mix(in srgb,#000 24%,transparent);}
1714
+ .fdy-btn--danger[aria-pressed="true"]{background:var(--color-danger-btn) linear-gradient(180deg,rgba(0,0,0,.14),rgba(0,0,0,0));box-shadow:inset 0 2px 6px rgba(0,0,0,.24);}
1396
1715
 
1397
1716
  .fdy-btn--sm{height:calc(var(--control-h) - var(--space-2));padding:0 var(--space-3);font-size:var(--text-xs);border-radius:var(--radius-sm);}
1398
1717
  .fdy-btn--lg{height:calc(var(--control-h) + var(--space-2));padding:0 var(--space-6);font-size:var(--text-base);border-radius:var(--radius-lg);}
@@ -1441,7 +1760,7 @@ a { color: var(--color-primary); }
1441
1760
  .fdy-btn-group .fdy-btn:last-child{border-top-right-radius:var(--radius-md);border-bottom-right-radius:var(--radius-md);}
1442
1761
 
1443
1762
  /* FAB, circular elevated action button (icon), optional extended pill with label */
1444
- .fdy-fab{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);width:3.5rem;height:3.5rem;padding:0;border:var(--bw) solid transparent;border-radius:var(--radius-full);cursor:pointer;color:var(--color-on-primary);background:linear-gradient(180deg,var(--color-primary),color-mix(in srgb,#000 10%,var(--color-primary)));box-shadow:var(--shadow-3);transition:transform var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
1763
+ .fdy-fab{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);width:3.5rem;height:3.5rem;padding:0;border:var(--bw) solid transparent;border-radius:var(--radius-full);cursor:pointer;color:var(--color-on-primary);background:var(--color-primary) linear-gradient(180deg,rgba(0,0,0,0),rgba(0,0,0,.1));box-shadow:var(--shadow-3);transition:transform var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
1445
1764
  .fdy-fab:hover{transform:translateY(-1px);box-shadow:var(--shadow-4);}
1446
1765
  .fdy-fab:active{transform:translateY(1px);}
1447
1766
  .fdy-fab:focus-visible{border-radius:var(--radius-full);}
@@ -1450,8 +1769,8 @@ a { color: var(--color-primary); }
1450
1769
  .fdy-fab--sm{width:2.5rem;height:2.5rem;}
1451
1770
  .fdy-fab--sm svg{width:1.25rem;height:1.25rem;}
1452
1771
  .fdy-fab--extended{width:auto;padding:0 var(--space-5);font-family:var(--font-body);font-size:var(--text-sm);font-weight:var(--weight-semibold);}
1453
- .fdy-fab--danger{background:linear-gradient(180deg,var(--color-danger-btn),color-mix(in srgb,#000 12%,var(--color-danger-btn)));color:var(--color-on-danger);}
1454
- .fdy-fab--accent{background:linear-gradient(180deg,var(--color-accent),color-mix(in srgb,#000 10%,var(--color-accent)));color:var(--color-on-accent);}
1772
+ .fdy-fab--danger{background:var(--color-danger-btn) linear-gradient(180deg,rgba(0,0,0,0),rgba(0,0,0,.12));color:var(--color-on-danger);}
1773
+ .fdy-fab--accent{background:var(--color-accent) linear-gradient(180deg,rgba(0,0,0,0),rgba(0,0,0,.1));color:var(--color-on-accent);}
1455
1774
 
1456
1775
  /* Freeday. Card */
1457
1776
  /* position:relative, containing block for out-of-flow card content (see base.css); it also gives
@@ -1487,6 +1806,13 @@ a { color: var(--color-primary); }
1487
1806
  absolutely positioned dismiss button back into the flow. Do not merge these two rules. */
1488
1807
  :where(:is(.fdy-card,.fdy-list__row):has(.fdy-btn--stretch) :is(a[href],button,input,select,textarea,[tabindex]):not(.fdy-btn--stretch)){position:relative;}
1489
1808
  .fdy-card__body{padding:var(--space-5);}
1809
+ /* Header band (#070): an identity mark, the title, and the card's way out pinned right. Beside the
1810
+ title the action sits in one place on every card; under the content it moved each time the
1811
+ content grew. Outside __body's padding, so its rule spans the card edge to edge. The mark is an
1812
+ .fdy-avatar (--sm --square --tone-N), so its tints are the ones the contrast gate already covers. */
1813
+ .fdy-card__head{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-4) var(--space-5);border-bottom:var(--bw) solid var(--color-border-muted);}
1814
+ .fdy-card__head .fdy-card__title{flex:1 1 auto;min-width:0;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
1815
+ .fdy-card__action{flex:none;display:inline-flex;align-items:center;gap:var(--space-2);}
1490
1816
  .fdy-card__title{font-family:var(--font-display);font-size:var(--text-lg);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-tight);color:var(--color-text);margin:0 0 var(--space-1);}
1491
1817
  .fdy-card__desc{font-size:var(--text-sm);color:var(--color-text-muted);margin:0;line-height:var(--leading-normal);}
1492
1818
  .fdy-card__footer{padding:var(--space-3) var(--space-5);border-top:var(--bw) solid var(--color-border-muted);background:linear-gradient(180deg,var(--color-surface),var(--color-surface-2));display:flex;gap:var(--space-2);justify-content:flex-end;}
@@ -1499,9 +1825,9 @@ a { color: var(--color-primary); }
1499
1825
  .fdy-carousel__viewport{position:relative;display:flex;overflow-x:auto;scroll-snap-type:x mandatory;scroll-behavior:smooth;border-radius:var(--radius-lg);scrollbar-width:none;-ms-overflow-style:none;}
1500
1826
  .fdy-carousel__viewport::-webkit-scrollbar{display:none;}
1501
1827
  .fdy-carousel__slide{flex:0 0 100%;min-width:0;scroll-snap-align:center;}
1502
- .fdy-carousel__arrow{position:absolute;top:50%;transform:translateY(-50%);z-index:2;display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border:0;border-radius:var(--radius-full);background:color-mix(in srgb,var(--color-surface) 86%,transparent);color:var(--color-text);box-shadow:var(--shadow-2);cursor:pointer;transition:background var(--dur-fast) var(--ease-standard);}
1828
+ .fdy-carousel__arrow{position:absolute;top:50%;transform:translateY(-50%);z-index:2;display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border:0;border-radius:var(--radius-full);background:rgba(var(--color-surface-rgb),.86);color:var(--color-text);box-shadow:var(--shadow-2);cursor:pointer;transition:background var(--dur-fast) var(--ease-standard);}
1503
1829
  .fdy-carousel__arrow:hover{background:var(--color-surface);}
1504
- .fdy-carousel__arrow:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 40%,transparent);}
1830
+ .fdy-carousel__arrow:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.4);}
1505
1831
  .fdy-carousel__arrow:disabled{opacity:.4;cursor:not-allowed;}
1506
1832
  .fdy-carousel__arrow--prev{left:var(--space-3);}
1507
1833
  .fdy-carousel__arrow--next{right:var(--space-3);}
@@ -1509,7 +1835,7 @@ a { color: var(--color-primary); }
1509
1835
  .fdy-carousel__dots{display:flex;justify-content:center;gap:var(--space-2);margin-top:var(--space-3);}
1510
1836
  .fdy-carousel__dot{width:.5rem;height:.5rem;padding:0;border:0;border-radius:var(--radius-full);background:var(--color-border-strong);cursor:pointer;transition:background var(--dur-fast) var(--ease-standard),width var(--dur-fast) var(--ease-standard);}
1511
1837
  .fdy-carousel__dot[aria-current="true"]{background:var(--color-primary);width:1.25rem;}
1512
- .fdy-carousel__dot:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 40%,transparent);}
1838
+ .fdy-carousel__dot:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.4);}
1513
1839
  @media (prefers-reduced-motion:reduce){.fdy-carousel__viewport{scroll-behavior:auto;}}
1514
1840
 
1515
1841
  /* Freeday. Cascade select (hierarchical drill-down picker).
@@ -1521,14 +1847,14 @@ a { color: var(--color-primary); }
1521
1847
  .fdy-cascade{position:relative;display:inline-block;width:100%;max-width:22rem;}
1522
1848
  .fdy-cascade__trigger{display:inline-flex;align-items:center;justify-content:space-between;gap:var(--space-2);width:100%;height:var(--control-h);padding:0 var(--space-3);text-align:left;cursor:pointer;font-family:var(--font-body);font-size:var(--text-sm);color:var(--color-text);background:var(--color-surface);border:1.5px solid var(--color-control-border);border-radius:var(--radius-md);box-shadow:inset 0 1px 2px rgba(16,14,30,.06);transition:border-color var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
1523
1849
  .fdy-cascade__trigger:hover{border-color:var(--color-text-muted);}
1524
- .fdy-cascade__trigger:focus-visible{outline:none;border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
1525
- .fdy-cascade__trigger.is-open{border-color:var(--color-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
1850
+ .fdy-cascade__trigger:focus-visible{outline:none;border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px rgba(var(--color-primary-rgb),.26);}
1851
+ .fdy-cascade__trigger.is-open{border-color:var(--color-primary);box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
1526
1852
  .fdy-cascade__trigger:disabled,.fdy-cascade__trigger[aria-disabled="true"]{opacity:.5;cursor:not-allowed;}
1527
1853
  .fdy-cascade__trigger[aria-readonly="true"]{background:var(--color-surface-2);border-color:var(--color-border);cursor:default;}
1528
1854
  .fdy-cascade__trigger[aria-readonly="true"]:hover{border-color:var(--color-border);}
1529
1855
  .fdy-cascade__trigger[aria-readonly="true"]:focus-visible{border-color:var(--color-border-strong);box-shadow:none;}
1530
1856
  .fdy-cascade--error .fdy-cascade__trigger,.fdy-cascade__trigger[aria-invalid="true"]{border-color:var(--color-danger);}
1531
- .fdy-cascade--error .fdy-cascade__trigger:focus-visible,.fdy-cascade--error .fdy-cascade__trigger.is-open,.fdy-cascade__trigger[aria-invalid="true"]:focus-visible{border-color:var(--color-danger);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-danger) 26%,transparent);}
1857
+ .fdy-cascade--error .fdy-cascade__trigger:focus-visible,.fdy-cascade--error .fdy-cascade__trigger.is-open,.fdy-cascade__trigger[aria-invalid="true"]:focus-visible{border-color:var(--color-danger);box-shadow:0 0 0 3px rgba(var(--color-danger-rgb),.26);}
1532
1858
  .fdy-cascade__value{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
1533
1859
  .fdy-cascade__value--placeholder{color:var(--color-text-subtle);}
1534
1860
  .fdy-cascade__trigger::after{content:"";flex:none;width:.5rem;height:.5rem;margin-top:-.15rem;border-right:2px solid var(--color-text-subtle);border-bottom:2px solid var(--color-text-subtle);transform:rotate(45deg);transition:transform var(--dur-fast) var(--ease-standard);}
@@ -1540,7 +1866,7 @@ a { color: var(--color-primary); }
1540
1866
  .fdy-cascade__head{display:flex;align-items:center;gap:var(--space-2);padding:var(--space-1) var(--space-1) var(--space-2);margin-bottom:var(--space-1);border-bottom:var(--bw) solid var(--color-border);}
1541
1867
  .fdy-cascade__back{display:inline-flex;align-items:center;justify-content:center;flex:none;width:1.5rem;height:1.5rem;padding:0;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-muted);cursor:pointer;}
1542
1868
  .fdy-cascade__back:hover{background:var(--color-surface-3);color:var(--color-text);}
1543
- .fdy-cascade__back:focus-visible{outline:none;box-shadow:0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
1869
+ .fdy-cascade__back:focus-visible{outline:none;box-shadow:0 0 0 2px rgba(var(--color-primary-rgb),.45);}
1544
1870
  .fdy-cascade__back[hidden]{display:none;}
1545
1871
  .fdy-cascade__back svg{display:block;width:1rem;height:1rem;}
1546
1872
  .fdy-cascade__crumb{min-width:0;flex:1;font-size:var(--text-xs);font-weight:var(--weight-semibold);color:var(--color-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
@@ -1703,30 +2029,48 @@ a { color: var(--color-primary); }
1703
2029
 
1704
2030
  /* Freeday. Chip (tag / removable filter) */
1705
2031
  .fdy-chip{display:inline-flex;align-items:center;gap:var(--space-2);height:1.75rem;padding:0 var(--space-3);border-radius:var(--radius-full);font-size:var(--text-xs);font-weight:var(--weight-medium);color:var(--color-text);background:var(--color-surface-3);border:var(--bw) solid var(--color-border);}
1706
- .fdy-chip--primary{color:var(--color-primary-strong);background:var(--color-primary-soft);border-color:color-mix(in srgb,var(--color-primary) 22%,transparent);}
2032
+ .fdy-chip--primary{color:var(--color-primary-strong);background:var(--color-primary-soft);border-color:rgba(var(--color-primary-rgb),.22);}
1707
2033
  /* Categorical tone chips, non-semantic category tags / legend swatches, from the general
1708
2034
  * --tone-1..8 palette. Same validated AA formula as .fdy-avatar--tone-* (text >=4.5:1 both themes,
1709
2035
  * gated by test/contrast.test.mjs). */
1710
- .fdy-chip--tone-1,.fdy-chip--tone-2,.fdy-chip--tone-3,.fdy-chip--tone-4,.fdy-chip--tone-5,.fdy-chip--tone-6,.fdy-chip--tone-7,.fdy-chip--tone-8{background:color-mix(in srgb,var(--_fdy-chip-tone) 18%,var(--color-surface));color:color-mix(in srgb,var(--_fdy-chip-tone) 50%,var(--color-text));border-color:color-mix(in srgb,var(--_fdy-chip-tone) 30%,var(--color-border));}
1711
- .fdy-chip--tone-1{--_fdy-chip-tone:var(--tone-1);}
1712
- .fdy-chip--tone-2{--_fdy-chip-tone:var(--tone-2);}
1713
- .fdy-chip--tone-3{--_fdy-chip-tone:var(--tone-3);}
1714
- .fdy-chip--tone-4{--_fdy-chip-tone:var(--tone-4);}
1715
- .fdy-chip--tone-5{--_fdy-chip-tone:var(--tone-5);}
1716
- .fdy-chip--tone-6{--_fdy-chip-tone:var(--tone-6);}
1717
- .fdy-chip--tone-7{--_fdy-chip-tone:var(--tone-7);}
1718
- .fdy-chip--tone-8{--_fdy-chip-tone:var(--tone-8);}
1719
- .fdy-chip__remove{display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;margin-right:calc(var(--space-1)*-1);border:0;padding:0;border-radius:var(--radius-full);background:transparent;color:currentColor;opacity:.6;cursor:pointer;font-size:var(--text-sm);line-height:1;}
1720
- .fdy-chip__remove:hover{opacity:1;background:color-mix(in srgb,currentColor 16%,transparent);}
1721
- .fdy-chip__remove:focus-visible{outline:none;opacity:1;box-shadow:0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
2036
+ /* Precomputed tone tokens, the same --tone-N-bg / --tone-N-ink the avatar and the badge read.
2037
+ * --tone-N-border is the chip's edge: 30% of the tone over --color-border, opaque, which is a
2038
+ * different edge from the badge's translucent one and so a different token. See avatar.css for
2039
+ * why the mix left the component, and tokens/build.mjs for where it went. */
2040
+ .fdy-chip--tone-1{background:var(--tone-1-bg);color:var(--tone-1-ink);border-color:var(--tone-1-border);}
2041
+ .fdy-chip--tone-2{background:var(--tone-2-bg);color:var(--tone-2-ink);border-color:var(--tone-2-border);}
2042
+ .fdy-chip--tone-3{background:var(--tone-3-bg);color:var(--tone-3-ink);border-color:var(--tone-3-border);}
2043
+ .fdy-chip--tone-4{background:var(--tone-4-bg);color:var(--tone-4-ink);border-color:var(--tone-4-border);}
2044
+ .fdy-chip--tone-5{background:var(--tone-5-bg);color:var(--tone-5-ink);border-color:var(--tone-5-border);}
2045
+ .fdy-chip--tone-6{background:var(--tone-6-bg);color:var(--tone-6-ink);border-color:var(--tone-6-border);}
2046
+ .fdy-chip--tone-7{background:var(--tone-7-bg);color:var(--tone-7-ink);border-color:var(--tone-7-border);}
2047
+ .fdy-chip--tone-8{background:var(--tone-8-bg);color:var(--tone-8-ink);border-color:var(--tone-8-border);}
2048
+ .fdy-chip__remove{position:relative;display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;margin-right:calc(var(--space-1)*-1);border:0;padding:0;border-radius:var(--radius-full);background:transparent;color:currentColor;opacity:.6;cursor:pointer;font-size:var(--text-sm);line-height:1;}
2049
+ /* The hover wash is 16% of the chip's OWN ink, and the chip's ink is whatever the variant set:
2050
+ * --color-text, --color-primary-strong, --tone-N-ink. So no token can stand in for it, which is why
2051
+ * this was the last color-mix() in the kit — and the only one whose two-line fallback actually
2052
+ * worked, the mix carrying no var() and therefore being rejected at parse time.
2053
+ *
2054
+ * It is a VEIL now, the same trick the button gradients use (button.css): currentColor at .16 over
2055
+ * the chip, which is algebraically the colour the mix produced — `F·.16 + (1-.16)·ground` either
2056
+ * way, including at the glyph's antialiased edges, where the veil's colour equals the glyph's and so
2057
+ * composites back to itself. No @supports, no fallback line, and nothing newer than Safari 4.
2058
+ *
2059
+ * Absolutely positioned, so it paints above the × as well as the background; `border-radius:inherit`
2060
+ * keeps it inside the pill. No transition: the mix had none, and a veil that fades is a different
2061
+ * component. */
2062
+ .fdy-chip__remove::before{content:"";position:absolute;inset:0;border-radius:inherit;background:currentColor;opacity:0;}
2063
+ .fdy-chip__remove:hover{opacity:1;}
2064
+ .fdy-chip__remove:hover::before{opacity:.16;}
2065
+ .fdy-chip__remove:focus-visible{outline:none;opacity:1;box-shadow:0 0 0 2px rgba(var(--color-primary-rgb),.45);}
1722
2066
  /* Group wrapper for a row of chips (choice / filter). */
1723
2067
  .fdy-chips{display:flex;flex-wrap:wrap;gap:var(--space-2);}
1724
2068
  /* Interactive chips: choice (toggle) + filter (toggle with a check when on).
1725
2069
  * Rendered as <button aria-pressed> and enhanced by freeday-chip.js. */
1726
2070
  .fdy-chip--choice,.fdy-chip--filter{cursor:pointer;font-family:inherit;background:var(--color-surface);border-color:var(--color-border);transition:background var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard),color var(--dur-fast) var(--ease-standard);}
1727
2071
  .fdy-chip--choice:hover,.fdy-chip--filter:hover{background:var(--color-surface-3);border-color:var(--color-text-subtle);}
1728
- .fdy-chip--choice:focus-visible,.fdy-chip--filter:focus-visible{outline:none;box-shadow:0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
1729
- .fdy-chip--choice[aria-pressed="true"],.fdy-chip--filter[aria-pressed="true"]{color:var(--color-primary-strong);background:var(--color-primary-soft);border-color:color-mix(in srgb,var(--color-primary) 40%,transparent);}
2072
+ .fdy-chip--choice:focus-visible,.fdy-chip--filter:focus-visible{outline:none;box-shadow:0 0 0 2px rgba(var(--color-primary-rgb),.45);}
2073
+ .fdy-chip--choice[aria-pressed="true"],.fdy-chip--filter[aria-pressed="true"]{color:var(--color-primary-strong);background:var(--color-primary-soft);border-color:rgba(var(--color-primary-rgb),.4);}
1730
2074
  .fdy-chip--choice:disabled,.fdy-chip--filter:disabled,.fdy-chip--choice[aria-disabled="true"],.fdy-chip--filter[aria-disabled="true"]{opacity:.5;cursor:not-allowed;}
1731
2075
  .fdy-chip__check{display:none;flex:none;width:.85rem;height:.85rem;margin-left:calc(var(--space-1)*-1);}
1732
2076
  .fdy-chip__check svg{display:block;width:100%;height:100%;}
@@ -1736,8 +2080,8 @@ a { color: var(--color-primary); }
1736
2080
  .fdy-combo{position:relative;display:block;max-width:22rem;}
1737
2081
  .fdy-combo__button{display:inline-flex;align-items:center;justify-content:space-between;gap:var(--space-2);width:100%;height:var(--control-h);padding:0 var(--space-3);text-align:left;cursor:pointer;font-family:var(--font-body);font-size:var(--text-sm);color:var(--color-text);background:var(--color-surface);border:1.5px solid var(--color-control-border);border-radius:var(--radius-md);box-shadow:inset 0 1px 2px rgba(16,14,30,.06);transition:border-color var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
1738
2082
  .fdy-combo__button:hover{border-color:var(--color-text-muted);}
1739
- .fdy-combo__button:focus-visible{outline:none;border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
1740
- .fdy-combo__button.is-open{border-color:var(--color-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
2083
+ .fdy-combo__button:focus-visible{outline:none;border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px rgba(var(--color-primary-rgb),.26);}
2084
+ .fdy-combo__button.is-open{border-color:var(--color-primary);box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
1741
2085
  .fdy-combo__button:disabled,.fdy-combo__button[aria-disabled="true"]{opacity:.5;cursor:not-allowed;}
1742
2086
  /* Read-only: stays focusable & shows its value at full contrast but can't open, muted surface,
1743
2087
  softened border, default cursor, no focus-ring escalation (matches input[readonly], #39). */
@@ -1745,7 +2089,7 @@ a { color: var(--color-primary); }
1745
2089
  .fdy-combo__button[aria-readonly="true"]:hover{border-color:var(--color-border);}
1746
2090
  .fdy-combo__button[aria-readonly="true"]:focus-visible{border-color:var(--color-border-strong);box-shadow:none;}
1747
2091
  .fdy-combo--error .fdy-combo__button,.fdy-combo__button[aria-invalid="true"]{border-color:var(--color-danger);}
1748
- .fdy-combo--error .fdy-combo__button:focus-visible,.fdy-combo--error .fdy-combo__button.is-open,.fdy-combo__button[aria-invalid="true"]:focus-visible{border-color:var(--color-danger);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-danger) 26%,transparent);}
2092
+ .fdy-combo--error .fdy-combo__button:focus-visible,.fdy-combo--error .fdy-combo__button.is-open,.fdy-combo__button[aria-invalid="true"]:focus-visible{border-color:var(--color-danger);box-shadow:0 0 0 3px rgba(var(--color-danger-rgb),.26);}
1749
2093
  .fdy-combo__value{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
1750
2094
  .fdy-combo__value--placeholder{color:var(--color-text-subtle);}
1751
2095
  .fdy-combo__button::after{content:"";flex:none;width:.5rem;height:.5rem;margin-top:-.15rem;border-right:2px solid var(--color-text-subtle);border-bottom:2px solid var(--color-text-subtle);transform:rotate(45deg);transition:transform var(--dur-fast) var(--ease-standard);}
@@ -1760,14 +2104,14 @@ a { color: var(--color-primary); }
1760
2104
  .fdy-combo__option{display:flex;align-items:center;gap:var(--space-2);padding:var(--space-2) var(--space-3);border-radius:var(--radius-sm);font-size:var(--text-sm);color:var(--color-text);cursor:pointer;white-space:nowrap;}
1761
2105
  .fdy-combo__option.is-highlighted{background:var(--color-surface-3);}
1762
2106
  .fdy-combo__option[aria-selected="true"]{background:var(--color-primary-soft);color:var(--color-primary-strong);font-weight:var(--weight-semibold);}
1763
- .fdy-combo__option.is-highlighted[aria-selected="true"]{box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--color-primary) 55%,transparent);}
2107
+ .fdy-combo__option.is-highlighted[aria-selected="true"]{box-shadow:inset 0 0 0 2px rgba(var(--color-primary-rgb),.55);}
1764
2108
  /* The tick is CSS, not content (#004 §2). Inside role="option" it joined the option's ACCESSIBLE
1765
2109
  NAME, so the selected option announced "✓August" while every other announced "August": the state
1766
2110
  was read twice, once as aria-selected, once as decoration, and a test asking for an option by
1767
2111
  its own label stopped matching the one that was selected. The `/ ""` is alt text: the glyph is
1768
2112
  painted, the accessibility tree gets nothing. Same reasoning as .fdy-label--required (1.29.0).
1769
2113
  The empty box still reserves its width, so labels stay aligned whether selected or not. */
1770
- .fdy-combo__check{display:inline-flex;justify-content:center;width:var(--space-4);flex:none;font-weight:var(--weight-bold);color:var(--color-primary);}
2114
+ .fdy-combo__check{display:inline-flex;justify-content:center;width:var(--space-4);flex:none;font-weight:var(--weight-bold);color:var(--color-primary-strong);}
1771
2115
  .fdy-combo__option[aria-selected="true"] .fdy-combo__check::after{content:"✓" / "";}
1772
2116
  @media (prefers-reduced-motion:no-preference){.fdy-combo__listbox:not([hidden]){animation:fdy-combo-in var(--dur-fast) var(--ease-standard);}}
1773
2117
  @keyframes fdy-combo-in{from{opacity:0;transform:translateY(-4px);}to{opacity:1;transform:none;}}
@@ -1869,6 +2213,42 @@ a { color: var(--color-primary); }
1869
2213
  .fdy-text-success{color:var(--color-success-strong);}
1870
2214
  .fdy-text-warning{color:var(--color-warning-strong);}
1871
2215
  .fdy-text-danger{color:var(--color-danger-strong);}
2216
+ /* Document columns (#065): a form or record page with an optional identity rail on the left
2217
+ * (__lead) and a summary rail on the right (__aside). The widths follow which rails are PRESENT,
2218
+ * so the layout is decided once here and not again by every screen:
2219
+ * __main alone one column
2220
+ * __main + __aside 8 / 4 (from md)
2221
+ * __lead + __main 3 / 9 (from md)
2222
+ * all three 3 / 6 / 3 (from lg; between md and lg both rails share the left)
2223
+ * Stacked, the summary rail comes FIRST: last, it sits under every section, and its button with it.
2224
+ * From md each rail is sticky and scrolls on its own, so the total and the status stay answerable
2225
+ * while the main column scrolls with the page, and a rail taller than the screen never hangs its
2226
+ * end off it. Screen only: a printed page is not held to a window. */
2227
+ .fdy-page__columns{--_fdy-rail-top:var(--fdy-page-sticky-top,var(--space-4));display:grid;grid-template-columns:minmax(0,1fr);gap:var(--space-6);align-items:start;}
2228
+ /* Under the shell the sticky topbar is in the way; --fdy-page-sticky-top overrides either default. */
2229
+ .fdy-app .fdy-page__columns{--_fdy-rail-top:var(--fdy-page-sticky-top,calc(var(--space-16) + var(--space-4)));}
2230
+ .fdy-page__columns > *{min-width:0;}
2231
+ @media (max-width:959.98px){.fdy-page__aside{order:-1;}}
2232
+ @media (min-width:960px){
2233
+ .fdy-page__columns:has(> .fdy-page__aside){grid-template-columns:minmax(0,2fr) minmax(0,1fr);}
2234
+ .fdy-page__columns:has(> .fdy-page__lead){grid-template-columns:minmax(0,1fr) minmax(0,3fr);}
2235
+ /* Both rails, two columns: they stack on the left while __main spans both rows on the right. */
2236
+ .fdy-page__columns:has(> .fdy-page__lead):has(> .fdy-page__aside) > .fdy-page__main{grid-row:span 2;}
2237
+ }
2238
+ @media (min-width:1280px){
2239
+ .fdy-page__columns:has(> .fdy-page__lead):has(> .fdy-page__aside){grid-template-columns:minmax(0,1fr) minmax(0,2fr) minmax(0,1fr);}
2240
+ .fdy-page__columns:has(> .fdy-page__lead):has(> .fdy-page__aside) > .fdy-page__main{grid-row:auto;}
2241
+ }
2242
+ /* The padding, taken back by the margin, is room for a focus ring at a card's edge: a scrolling box
2243
+ clips whatever draws outside it. */
2244
+ @media screen and (min-width:960px){
2245
+ .fdy-page__lead,.fdy-page__aside{position:sticky;top:var(--_fdy-rail-top);max-height:calc(100dvh - var(--_fdy-rail-top) - var(--space-4));overflow-y:auto;overscroll-behavior:contain;padding:var(--space-1);margin:calc(var(--space-1) * -1);}
2246
+ }
2247
+ /* Two sticky rails in ONE column would slide over each other: between md and lg, with both rails
2248
+ present, they scroll with the page instead. */
2249
+ @media screen and (min-width:960px) and (max-width:1279.98px){
2250
+ .fdy-page__columns:has(> .fdy-page__lead):has(> .fdy-page__aside) > :is(.fdy-page__lead,.fdy-page__aside){position:static;max-height:none;overflow:visible;}
2251
+ }
1872
2252
 
1873
2253
  /* Freeday. Date picker (input-styled trigger + calendar popover).
1874
2254
  * Built by freeday-datepicker.js from an empty [data-fdy-datepicker] wrapper. */
@@ -1881,10 +2261,10 @@ a { color: var(--color-primary); }
1881
2261
  .fdy-field>.fdy-datepicker,.fdy-field>.fdy-timepicker,.fdy-field>.fdy-daterange{max-width:none;}
1882
2262
  .fdy-datepicker__trigger{display:inline-flex;align-items:center;gap:var(--space-2);width:100%;height:var(--control-h);padding:0 var(--space-3);text-align:left;cursor:pointer;font-family:var(--font-body);font-size:var(--text-sm);color:var(--color-text);background:var(--color-surface);border:1.5px solid var(--color-control-border);border-radius:var(--radius-md);box-shadow:inset 0 1px 2px rgba(16,14,30,.06);transition:border-color var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
1883
2263
  .fdy-datepicker__trigger:hover{border-color:var(--color-text-muted);}
1884
- .fdy-datepicker__trigger:focus-visible{outline:none;border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
1885
- .fdy-datepicker__trigger.is-open{border-color:var(--color-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
2264
+ .fdy-datepicker__trigger:focus-visible{outline:none;border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px rgba(var(--color-primary-rgb),.26);}
2265
+ .fdy-datepicker__trigger.is-open{border-color:var(--color-primary);box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
1886
2266
  .fdy-datepicker--error .fdy-datepicker__trigger,.fdy-datepicker__trigger[aria-invalid="true"]{border-color:var(--color-danger);}
1887
- .fdy-datepicker--error .fdy-datepicker__trigger:focus-visible,.fdy-datepicker--error .fdy-datepicker__trigger.is-open,.fdy-datepicker__trigger[aria-invalid="true"]:focus-visible{border-color:var(--color-danger);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-danger) 26%,transparent);}
2267
+ .fdy-datepicker--error .fdy-datepicker__trigger:focus-visible,.fdy-datepicker--error .fdy-datepicker__trigger.is-open,.fdy-datepicker__trigger[aria-invalid="true"]:focus-visible{border-color:var(--color-danger);box-shadow:0 0 0 3px rgba(var(--color-danger-rgb),.26);}
1888
2268
  .fdy-datepicker__trigger:disabled,.fdy-datepicker__trigger[aria-disabled="true"]{opacity:.5;cursor:not-allowed;}
1889
2269
  .fdy-datepicker__trigger[aria-readonly="true"]{background:var(--color-surface-2);border-color:var(--color-border);cursor:default;}
1890
2270
  .fdy-datepicker__trigger[aria-readonly="true"]:hover{border-color:var(--color-border);}
@@ -1899,7 +2279,7 @@ a { color: var(--color-primary); }
1899
2279
  .fdy-datepicker__icon.is-hidden{visibility:hidden;}
1900
2280
  .fdy-datepicker__clear{position:absolute;top:50%;right:var(--space-3);transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:1.15rem;height:1.15rem;padding:0;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-subtle);cursor:pointer;transition:color var(--dur-fast) var(--ease-standard),background var(--dur-fast) var(--ease-standard);}
1901
2281
  .fdy-datepicker__clear:hover{color:var(--color-text);background:var(--color-surface-2);}
1902
- .fdy-datepicker__clear:focus-visible{outline:none;color:var(--color-text);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
2282
+ .fdy-datepicker__clear:focus-visible{outline:none;color:var(--color-text);box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
1903
2283
  .fdy-datepicker__clear svg{display:block;width:.9rem;height:.9rem;}
1904
2284
 
1905
2285
  .fdy-datepicker__panel{position:absolute;top:calc(100% + var(--space-1));left:0;z-index:130;padding:var(--space-3);background:var(--color-surface);color:var(--color-text);border:var(--bw) solid var(--color-border-strong);border-radius:var(--radius-md);box-shadow:var(--shadow-3);}
@@ -1911,16 +2291,16 @@ a { color: var(--color-primary); }
1911
2291
  reset and the same hover/focus treatment as the arrows beside it. */
1912
2292
  .fdy-cal__title{flex:1;appearance:none;border:0;background:transparent;margin:0;padding:var(--space-1) var(--space-2);border-radius:var(--radius-sm);text-align:center;font-family:inherit;font-weight:var(--weight-semibold);font-size:var(--text-sm);color:var(--color-text);text-transform:capitalize;cursor:pointer;}
1913
2293
  .fdy-cal__title:hover{background:var(--color-surface-2);}
1914
- .fdy-cal__title:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
2294
+ .fdy-cal__title:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
1915
2295
  .fdy-cal__month,.fdy-cal__year{display:inline-flex;align-items:center;justify-content:center;height:2.25rem;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text);font-family:inherit;font-size:var(--text-sm);text-transform:capitalize;cursor:pointer;}
1916
2296
  .fdy-cal__month:hover,.fdy-cal__year:hover{background:var(--color-surface-2);}
1917
- .fdy-cal__month:focus-visible,.fdy-cal__year:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
2297
+ .fdy-cal__month:focus-visible,.fdy-cal__year:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
1918
2298
  .fdy-cal__month:disabled,.fdy-cal__year:disabled{opacity:.4;cursor:not-allowed;background:transparent;}
1919
2299
  .fdy-cal__month.is-today,.fdy-cal__year.is-today{font-weight:var(--weight-bold);box-shadow:inset 0 0 0 1.5px var(--color-border-strong);}
1920
2300
  .fdy-cal__month.is-selected,.fdy-cal__year.is-selected{background:var(--color-primary);color:var(--color-on-primary);font-weight:var(--weight-semibold);}
1921
2301
  .fdy-cal__nav{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;flex:none;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-muted);font-size:var(--text-lg);line-height:1;cursor:pointer;}
1922
2302
  .fdy-cal__nav:hover{background:var(--color-surface-2);color:var(--color-text);}
1923
- .fdy-cal__nav:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
2303
+ .fdy-cal__nav:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
1924
2304
  .fdy-cal__grid{display:grid;grid-template-columns:repeat(7,2rem);gap:2px;}
1925
2305
  /* Month grid. 3×4 over the same track width as the 7-column day grid, so the panel does not resize
1926
2306
  when you drill and the pointer does not have to travel.
@@ -1932,7 +2312,7 @@ a { color: var(--color-primary); }
1932
2312
  .fdy-cal__dow{display:flex;align-items:center;justify-content:center;height:1.75rem;font-size:var(--text-xs);font-weight:var(--weight-semibold);color:var(--color-text-muted);text-transform:capitalize;}
1933
2313
  .fdy-cal__day{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text);font-size:var(--text-sm);font-variant-numeric:tabular-nums;cursor:pointer;}
1934
2314
  .fdy-cal__day:hover{background:var(--color-surface-2);}
1935
- .fdy-cal__day:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
2315
+ .fdy-cal__day:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
1936
2316
  .fdy-cal__day.is-outside{color:var(--color-text-subtle);opacity:.55;}
1937
2317
  .fdy-cal__day.is-today{font-weight:var(--weight-bold);box-shadow:inset 0 0 0 1.5px var(--color-border-strong);}
1938
2318
  .fdy-cal__day.in-range{background:var(--color-primary-soft);color:var(--color-primary-strong);border-radius:0;}
@@ -1964,9 +2344,19 @@ a { color: var(--color-primary); }
1964
2344
  /* Freeday. Description list (key-value detail view) */
1965
2345
  .fdy-dl{display:grid;grid-template-columns:auto 1fr;gap:var(--space-2) var(--space-5);margin:0;}
1966
2346
  .fdy-dl dt{color:var(--color-text-muted);font-size:var(--text-sm);}
1967
- .fdy-dl dd{margin:0;color:var(--color-text);font-size:var(--text-sm);font-weight:var(--weight-medium);}
2347
+ /* overflow-wrap: an email, an order code or a 13-digit phone number is one unbreakable token, and
2348
+ without it the value prints over whatever sits beside it (#066). */
2349
+ .fdy-dl dd{margin:0;min-width:0;color:var(--color-text);font-size:var(--text-sm);font-weight:var(--weight-medium);overflow-wrap:anywhere;}
1968
2350
  .fdy-dl--rows{grid-template-columns:1fr;gap:var(--space-1);}
1969
2351
  .fdy-dl--rows dt{margin-top:var(--space-2);}
2352
+ /* Facts grid (#066): each pair a <div> (allowed inside <dl>), caption above value, as many columns
2353
+ as the list's OWN width holds, never more than three. Sized by the element rather than the
2354
+ window, so a grid in a narrow side rail stays one column with no modifier: a window breakpoint
2355
+ gave a rail two columns of about 110px. min(100%, …) keeps a single column from overflowing a
2356
+ rail narrower than the column floor. */
2357
+ .fdy-dl--grid{grid-template-columns:repeat(auto-fill,minmax(min(100%,max(var(--fdy-dl-col-min,14rem),calc((100% - 2 * var(--space-6)) / 3))),1fr));gap:var(--space-5) var(--space-6);}
2358
+ .fdy-dl--grid > div{min-width:0;}
2359
+ .fdy-dl--grid dd{margin-top:var(--space-1);}
1970
2360
 
1971
2361
  /* Freeday. Divider */
1972
2362
  .fdy-divider{border:0;border-top:var(--bw) solid var(--color-border);margin:var(--space-4) 0;}
@@ -2003,11 +2393,11 @@ a { color: var(--color-primary); }
2003
2393
  * Enhanced by freeday-upload.js; states: idle · drag-over · uploading · done · error. */
2004
2394
  .fdy-dropzone{display:flex;flex-direction:column;align-items:center;gap:var(--space-2);padding:var(--space-6);text-align:center;border:2px dashed var(--color-control-border);border-radius:var(--radius-lg);background:var(--color-surface-2);color:var(--color-text-muted);cursor:pointer;transition:border-color var(--dur-fast) var(--ease-standard),background var(--dur-fast) var(--ease-standard),color var(--dur-fast) var(--ease-standard);}
2005
2395
  .fdy-dropzone:hover{border-color:var(--color-text-muted);}
2006
- .fdy-dropzone:focus-visible{outline:none;border-color:var(--color-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
2396
+ .fdy-dropzone:focus-visible{outline:none;border-color:var(--color-primary);box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
2007
2397
  .fdy-dropzone.is-dragover{border-color:var(--color-primary);background:var(--color-primary-soft);color:var(--color-primary-strong);}
2008
2398
  .fdy-dropzone[aria-disabled="true"],.fdy-dropzone.is-disabled{opacity:.55;cursor:not-allowed;pointer-events:none;}
2009
2399
  .fdy-dropzone__icon{color:var(--color-text-subtle);}
2010
- .fdy-dropzone.is-dragover .fdy-dropzone__icon{color:var(--color-primary);}
2400
+ .fdy-dropzone.is-dragover .fdy-dropzone__icon{color:var(--color-primary-strong);}
2011
2401
  .fdy-dropzone__icon svg{display:block;width:2rem;height:2rem;}
2012
2402
  .fdy-dropzone__title{font-size:inherit;font-weight:var(--weight-medium);color:var(--color-text);margin:0;}
2013
2403
  .fdy-dropzone__hint{font-size:var(--text-xs);}
@@ -2034,9 +2424,14 @@ a { color: var(--color-primary); }
2034
2424
  .fdy-file__progress{margin-top:var(--space-2);}
2035
2425
  .fdy-file__remove{flex:none;display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-muted);font-size:var(--text-lg);line-height:1;cursor:pointer;}
2036
2426
  .fdy-file__remove:hover{background:var(--color-surface-2);color:var(--color-text);}
2037
- .fdy-file__remove:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
2427
+ .fdy-file__remove:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
2038
2428
  .fdy-file--success .fdy-file__icon{color:var(--color-success);}
2039
- .fdy-file--error{border-color:color-mix(in srgb,var(--color-danger) 40%,var(--color-border));}
2429
+ /* 40% of --color-danger over --color-border, as a token rather than as a mix written here: it blends
2430
+ * two ROLES, so no channel triple can state it, and mixing it in the component carried var() into
2431
+ * color-mix() — the shape that goes invalid at computed-value time and drops border-color to its
2432
+ * initial currentColor, which is the black box an iPhone 7 drew around a failed upload. The blend is
2433
+ * now computed per theme by tokens/build.mjs. */
2434
+ .fdy-file--error{border-color:var(--color-danger-border);}
2040
2435
  .fdy-file--error .fdy-file__icon{color:var(--color-danger);}
2041
2436
  .fdy-file--error .fdy-file__sub{color:var(--color-danger-strong);}
2042
2437
 
@@ -2108,7 +2503,7 @@ a { color: var(--color-primary); }
2108
2503
  /* Freeday. Input group (prefix/suffix addons, e.g. Rp / % / icon) */
2109
2504
  .fdy-input-group{display:flex;align-items:stretch;height:var(--control-h);max-width:22rem;background:var(--color-surface);border:1.5px solid var(--color-control-border);border-radius:var(--radius-md);box-shadow:inset 0 1px 2px rgba(16,14,30,.06);overflow:hidden;transition:border-color var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
2110
2505
  .fdy-input-group:hover{border-color:var(--color-text-muted);}
2111
- .fdy-input-group:focus-within{border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
2506
+ .fdy-input-group:focus-within{border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px rgba(var(--color-primary-rgb),.26);}
2112
2507
  /* The group owns the height; the input fills whatever the border leaves. It used to subtract 3px
2113
2508
  for two 1.5px borders, which is arithmetic the engine is free to round: at dpr 1 each border
2114
2509
  resolves to 1px, so the group rendered 1px short of every control beside it (#018). Stretching
@@ -2137,7 +2532,7 @@ a { color: var(--color-primary); }
2137
2532
  .fdy-input-group__btn:disabled:hover{background:var(--color-surface-2);color:var(--color-text-muted);}
2138
2533
  /* Error state must win over the neutralizing rules above, promote to the wrapper. */
2139
2534
  .fdy-input-group:has(.fdy-input--error),.fdy-input-group:has(.fdy-combo--error),.fdy-input-group:has([aria-invalid="true"]){border-color:var(--color-danger);}
2140
- .fdy-input-group:has(.fdy-input--error):focus-within,.fdy-input-group:has(.fdy-combo--error):focus-within,.fdy-input-group:has([aria-invalid="true"]):focus-within{border-color:var(--color-danger);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px color-mix(in srgb,var(--color-danger) 26%,transparent);}
2535
+ .fdy-input-group:has(.fdy-input--error):focus-within,.fdy-input-group:has(.fdy-combo--error):focus-within,.fdy-input-group:has([aria-invalid="true"]):focus-within{border-color:var(--color-danger);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px rgba(var(--color-danger-rgb),.26);}
2141
2536
 
2142
2537
  /* Freeday. Field & Input */
2143
2538
  .fdy-field{display:flex;flex-direction:column;gap:var(--space-2);max-width:22rem;}
@@ -2173,7 +2568,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
2173
2568
  .fdy-input::placeholder,.fdy-textarea::placeholder{color:var(--color-text-subtle);}
2174
2569
  .fdy-textarea{height:auto;min-height:5.5rem;padding:var(--space-3);line-height:var(--leading-normal);resize:vertical;}
2175
2570
  .fdy-input:hover,.fdy-textarea:hover{border-color:var(--color-text-muted);}
2176
- .fdy-input:focus,.fdy-textarea:focus{outline:none;border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
2571
+ .fdy-input:focus,.fdy-textarea:focus{outline:none;border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px rgba(var(--color-primary-rgb),.26);}
2177
2572
  .fdy-input:disabled,.fdy-textarea:disabled,.fdy-input[aria-disabled="true"],.fdy-textarea[aria-disabled="true"]{opacity:.5;cursor:not-allowed;}
2178
2573
  /* Read-only (#39): the value is real and may be read/copied, only not edited. Full contrast (unlike
2179
2574
  :disabled which dims to placeholder-grey), muted surface + softened border so it still reads as
@@ -2190,7 +2585,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
2190
2585
  .fdy-input[type="number"]{appearance:textfield;-webkit-appearance:textfield;}
2191
2586
  .fdy-input[type="number"]::-webkit-outer-spin-button,.fdy-input[type="number"]::-webkit-inner-spin-button{appearance:none;-webkit-appearance:none;margin:0;}
2192
2587
  .fdy-input--error,.fdy-input[aria-invalid="true"],.fdy-textarea[aria-invalid="true"]{border-color:var(--color-danger);}
2193
- .fdy-input--error:focus,.fdy-input[aria-invalid="true"]:focus,.fdy-textarea[aria-invalid="true"]:focus{border-color:var(--color-danger);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-danger) 26%,transparent);}
2588
+ .fdy-input--error:focus,.fdy-input[aria-invalid="true"]:focus,.fdy-textarea[aria-invalid="true"]:focus{border-color:var(--color-danger);box-shadow:0 0 0 3px rgba(var(--color-danger-rgb),.26);}
2194
2589
  /* Required marker. The control already carries `required`, so the asterisk is decoration: the
2195
2590
  `/ ""` is CSS alt text, the glyph is painted but exposes NOTHING to the accessibility tree, so a
2196
2591
  screen reader does not read "star" after every label. That is stricter than a <span> an app has to
@@ -2256,7 +2651,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
2256
2651
  /* Focus is not hover. The enhancer moves real DOM focus (freeday-menu.js `.focus()`), so this is
2257
2652
  the only thing a keyboard user has to go on, with the hover fill alone, arrowing through the
2258
2653
  menu is invisible. Same ring as .fdy-nav__item, which got this right. Reported twice. */
2259
- .fdy-menu__item:focus-visible{outline:none;background:var(--color-surface-3);box-shadow:0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
2654
+ .fdy-menu__item:focus-visible{outline:none;background:var(--color-surface-3);box-shadow:0 0 0 2px rgba(var(--color-primary-rgb),.45);}
2260
2655
  .fdy-menu__item:disabled{opacity:.5;cursor:not-allowed;background:transparent;}
2261
2656
  .fdy-menu__item--danger{color:var(--color-danger-strong);}
2262
2657
  .fdy-menu__item svg{display:block;width:1rem;height:1rem;flex:none;}
@@ -2273,8 +2668,8 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
2273
2668
  .fdy-btn-split__toggle{padding:0 var(--space-2);margin-left:calc(var(--bw) * -1);border-top-left-radius:0;border-bottom-left-radius:0;}
2274
2669
  /* Hairline divider between the halves (outranks the segment rule above). */
2275
2670
  .fdy-btn-split > .fdy-btn-split__toggle{box-shadow:inset 0 1px 0 rgba(255,255,255,.26),inset 1px 0 0 rgba(0,0,0,.16);}
2276
- .fdy-btn-split:hover{transform:translateY(-1px);box-shadow:0 6px 18px -2px color-mix(in srgb,var(--color-primary) 55%,transparent);}
2277
- .fdy-btn-split:active{transform:translateY(1px);box-shadow:0 1px 4px -1px color-mix(in srgb,var(--color-primary) 46%,transparent);}
2671
+ .fdy-btn-split:hover{transform:translateY(-1px);box-shadow:0 6px 18px -2px rgba(var(--color-primary-rgb),.55);}
2672
+ .fdy-btn-split:active{transform:translateY(1px);box-shadow:0 1px 4px -1px rgba(var(--color-primary-rgb),.46);}
2278
2673
  .fdy-btn-split__toggle:hover,.fdy-btn-split__toggle:focus-visible{z-index:1;}
2279
2674
  .fdy-btn-split__toggle svg{display:block;width:1rem;height:1rem;}
2280
2675
 
@@ -2305,7 +2700,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
2305
2700
  .fdy-pagination__list{display:flex;align-items:center;gap:var(--space-1);list-style:none;margin:0;padding:0;}
2306
2701
  .fdy-pagination__link{display:inline-flex;align-items:center;justify-content:center;min-width:2.25rem;height:2.25rem;padding:0 var(--space-2);border-radius:var(--radius-md);font-size:var(--text-sm);font-weight:var(--weight-medium);color:var(--color-text);background:transparent;border:var(--bw) solid transparent;text-decoration:none;cursor:pointer;transition:background var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard);}
2307
2702
  .fdy-pagination__link:hover{background:var(--color-surface-2);border-color:var(--color-border);}
2308
- .fdy-pagination__link:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
2703
+ .fdy-pagination__link:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
2309
2704
  .fdy-pagination__link[aria-current="page"]{background:var(--color-primary);color:var(--color-on-primary);}
2310
2705
  .fdy-pagination__link[aria-disabled="true"],.fdy-pagination__link:disabled{opacity:.5;cursor:not-allowed;pointer-events:none;}
2311
2706
  .fdy-pagination__ellipsis{display:inline-flex;align-items:center;justify-content:center;min-width:2.25rem;height:2.25rem;color:var(--color-text-subtle);}
@@ -2351,13 +2746,19 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
2351
2746
  .fdy-radio input::before{content:"";width:.55rem;height:.55rem;border-radius:50%;transform:scale(0);transition:transform var(--dur-fast) var(--ease-spring);box-shadow:inset 1rem 1rem var(--color-on-primary);}
2352
2747
  .fdy-check input:checked,.fdy-radio input:checked{background:var(--color-primary);border-color:var(--color-primary);}
2353
2748
  .fdy-check input:checked::before,.fdy-radio input:checked::before{transform:scale(1);}
2354
- .fdy-check input:focus-visible,.fdy-radio input:focus-visible,.fdy-switch input:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
2749
+ .fdy-check input:focus-visible,.fdy-radio input:focus-visible,.fdy-switch input:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
2355
2750
  .fdy-check input:disabled,.fdy-radio input:disabled,.fdy-switch input:disabled{opacity:.5;cursor:not-allowed;}
2356
2751
  .fdy-check:has(input:disabled),.fdy-radio:has(input:disabled),.fdy-switch:has(input:disabled){cursor:not-allowed;}
2357
2752
  .fdy-switch input{appearance:none;-webkit-appearance:none;flex:none;position:relative;width:var(--control-switch-w);height:var(--control-switch-h);margin:0;border-radius:var(--radius-full);background:var(--color-control-border);cursor:pointer;transition:background var(--dur-base) var(--ease-standard);}
2358
2753
  .fdy-switch input::before{content:"";position:absolute;top:2px;left:2px;width:calc(var(--control-switch-h) - 4px);height:calc(var(--control-switch-h) - 4px);border-radius:50%;background:var(--color-switch-thumb);box-shadow:var(--shadow-1);transition:transform var(--dur-base) var(--ease-spring);}
2359
2754
  .fdy-switch input:checked{background:var(--color-primary);}
2360
2755
  .fdy-switch input:checked::before{transform:translateX(calc(var(--control-switch-w) - var(--control-switch-h)));}
2756
+ /* State hint (#068): one line saying what the CURRENT state means, never "on means X, off means Y",
2757
+ which leaves the reader to work out which half applies. The control's aria-describedby points at
2758
+ the .fdy-help WRAPPING both lines: a hidden child of a referenced node is skipped, so a screen
2759
+ reader hears only the line on screen. Point it at the two children and both are read. */
2760
+ .fdy-field:has(:is(.fdy-switch,.fdy-check) input:checked) [data-fdy-when="off"],
2761
+ .fdy-field:not(:has(:is(.fdy-switch,.fdy-check) input:checked)) [data-fdy-when="on"]{display:none;}
2361
2762
 
2362
2763
  /* Standalone checkbox (e.g. table row/select-all), supports :indeterminate */
2363
2764
  .fdy-checkbox{appearance:none;-webkit-appearance:none;flex:none;width:var(--control-box);height:var(--control-box);margin:0;border:1.5px solid var(--color-control-border);border-radius:var(--radius-sm);background:var(--color-surface);box-shadow:inset 0 1px 2px var(--color-shadow-inset);cursor:pointer;display:grid;place-content:center;transition:background var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard);}
@@ -2365,11 +2766,11 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
2365
2766
  .fdy-checkbox:checked,.fdy-checkbox:indeterminate{background:var(--color-primary);border-color:var(--color-primary);}
2366
2767
  .fdy-checkbox:checked::before{transform:scale(1);}
2367
2768
  .fdy-checkbox:indeterminate::before{width:.6rem;height:2px;transform:scale(1);clip-path:none;box-shadow:none;background:var(--color-on-primary);}
2368
- .fdy-checkbox:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
2769
+ .fdy-checkbox:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
2369
2770
  .fdy-checkbox:disabled{opacity:.5;cursor:not-allowed;}
2370
2771
 
2371
2772
  /* Freeday. Skeleton */
2372
- .fdy-skeleton{display:block;border-radius:var(--radius-sm);background:var(--color-surface-3);background-image:linear-gradient(90deg,transparent,color-mix(in srgb,var(--color-surface) 60%,transparent),transparent);background-size:200% 100%;background-repeat:no-repeat;animation:fdy-skeleton 1.4s ease-in-out infinite;}
2773
+ .fdy-skeleton{display:block;border-radius:var(--radius-sm);background:var(--color-surface-3);background-image:linear-gradient(90deg,transparent,rgba(var(--color-surface-rgb),.6),transparent);background-size:200% 100%;background-repeat:no-repeat;animation:fdy-skeleton 1.4s ease-in-out infinite;}
2373
2774
  .fdy-skeleton--text{height:.8rem;margin:.3rem 0;}
2374
2775
  .fdy-skeleton--title{height:1.2rem;width:40%;}
2375
2776
  .fdy-skeleton--circle{border-radius:var(--radius-full);}
@@ -2409,6 +2810,13 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
2409
2810
  .fdy-state__text{font-size:var(--text-sm);color:var(--color-text-muted);max-width:24rem;line-height:var(--leading-normal);}
2410
2811
  .fdy-state__actions{margin-top:var(--space-2);display:flex;gap:var(--space-2);flex-wrap:wrap;justify-content:center;}
2411
2812
  .fdy-state--danger .fdy-state__icon{color:var(--color-danger-strong);}
2813
+ /* Compact (#069): the same state inside a small card, one line of icon and text instead of a
2814
+ page-sized block. Danger matters most here: a widget that renders zero for a failed request
2815
+ reads as "nothing to do", the opposite of what happened. */
2816
+ .fdy-state--compact{flex-direction:row;align-items:flex-start;text-align:left;gap:var(--space-2);padding:var(--space-2) 0;}
2817
+ .fdy-state--compact .fdy-state__icon{width:1rem;height:1rem;flex:none;}
2818
+ .fdy-state--compact .fdy-state__text{font-size:var(--text-xs);max-width:none;}
2819
+ .fdy-state--compact .fdy-state__actions{margin:0 0 0 auto;}
2412
2820
 
2413
2821
  /* Freeday. Stepper / Wizard (progress header + optional panels).
2414
2822
  * Enhanced by freeday-stepper.js for prev/next navigation. */
@@ -2443,7 +2851,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
2443
2851
  .fdy-stepper[aria-busy="true"]{opacity:.72;}
2444
2852
  .fdy-step.is-active .fdy-step__label{color:var(--color-text);font-weight:var(--weight-semibold);}
2445
2853
  .fdy-step__btn:focus-visible{outline:none;}
2446
- .fdy-step__btn:focus-visible .fdy-step__marker{box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 30%,transparent);}
2854
+ .fdy-step__btn:focus-visible .fdy-step__marker{box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.3);}
2447
2855
 
2448
2856
  .fdy-step-panels{margin-top:var(--space-5);}
2449
2857
  .fdy-step-nav{display:flex;justify-content:space-between;gap:var(--space-2);margin-top:var(--space-5);}
@@ -2464,9 +2872,23 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
2464
2872
  invisible on every second row. The tint is HALF a surface step rather than --color-surface-2
2465
2873
  itself, for the same reason: hover paints the full step, so the two must not be the same colour.
2466
2874
  Exposed as a Tier-3 token through the var() FALLBACK, never as a declaration — a custom property
2467
- set here would outrank a host's own :root override. */
2468
- .fdy-table--striped tbody tr:nth-child(even){background:var(--fdy-table-stripe,color-mix(in srgb,var(--color-surface-2) 55%,var(--color-surface)));}
2469
- .fdy-table tbody tr:hover{background:var(--color-surface-2);}
2875
+ set here would outrank a host's own :root override.
2876
+ The tint arrives as --color-surface-stripe, already flattened, rather than as a color-mix() of
2877
+ two surfaces. A custom property cannot carry color-mix(): the `background` that finally reads it
2878
+ contains var(), so on Safari <= 16.1 it failed at computed-value time and the stripe vanished,
2879
+ and a preceding declaration is never consulted in that path. The token is also OPAQUE on
2880
+ purpose, which the frozen column below depends on. */
2881
+ /* The resting colour is also published as --_fdy-row-rest, so a row that must not answer the
2882
+ pointer can hover back to exactly what it was (see --hover-actionable). */
2883
+ .fdy-table--striped tbody tr:nth-child(even){--_fdy-row-rest:var(--fdy-table-stripe,var(--color-surface-stripe));background:var(--_fdy-row-rest);}
2884
+ .fdy-table tbody tr:hover{background:var(--_fdy-row-hover,var(--color-surface-2));}
2885
+ /* Opt-in (#064): only a row that DOES something answers the pointer. A tint on a row with no click
2886
+ handler promises a target that is not there: people click the row, nothing happens, and the
2887
+ button they had to aim at was elsewhere in it. Striping, not hover, is what keeps the eye on a
2888
+ line. The inert row's hover resolves to its own resting colour, so a stripe survives it; a frozen
2889
+ cell falls back to its opaque surface, because it scrolls over its neighbours. */
2890
+ .fdy-table--hover-actionable tbody tr:not(.fdy-table__row--activatable){--_fdy-row-hover:var(--_fdy-row-rest,transparent);}
2891
+ .fdy-table--hover-actionable tbody tr:not(.fdy-table__row--activatable) td.fdy-table__freeze{--_fdy-row-hover:var(--_fdy-row-rest,var(--color-surface));}
2470
2892
  /* Monospace data, ids, codes, IPs, timestamps. Alignment-neutral so it works in any cell
2471
2893
  or inline (`<span class="fdy-mono">`), not just numeric columns. */
2472
2894
  .fdy-mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums;}
@@ -2475,20 +2897,20 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
2475
2897
  .fdy-table__num{text-align:right;font-variant-numeric:tabular-nums;font-family:var(--font-mono);}
2476
2898
  /* Loading / empty state cell (FdyTable spans it across all columns). */
2477
2899
  .fdy-table__state{text-align:center;color:var(--color-text-muted);padding:var(--space-6) var(--space-4);}
2478
- /* Activatable row (FdyTable rowActivatable): pointer + keyboard focus ring. The hover tint already
2479
- comes from the base `.fdy-table tbody tr:hover` rule, so this only adds the affordance + focus. */
2900
+ /* Activatable row (FdyTable rowActivatable): pointer + keyboard focus ring. The hover tint comes from
2901
+ the base `.fdy-table tbody tr:hover` rule, which --hover-actionable keeps for these rows only. */
2480
2902
  .fdy-table__row--activatable{cursor:pointer;}
2481
2903
  .fdy-table__row--activatable:focus-visible{outline:2px solid var(--color-primary);outline-offset:-2px;}
2482
2904
  /* Expandable detail row (FdyTable row-detail slot): a muted panel under its row, no hover tint. */
2483
2905
  .fdy-table__detailrow>td,.fdy-table__detailrow:hover>td{background:var(--color-surface-2);}
2484
2906
  .fdy-table th[aria-sort]{cursor:pointer;}
2485
- .fdy-table th[aria-sort="ascending"]::after{content:" ↑";color:var(--color-primary);}
2486
- .fdy-table th[aria-sort="descending"]::after{content:" ↓";color:var(--color-primary);}
2907
+ .fdy-table th[aria-sort="ascending"]::after{content:" ↑";color:var(--color-primary-strong);}
2908
+ .fdy-table th[aria-sort="descending"]::after{content:" ↓";color:var(--color-primary-strong);}
2487
2909
 
2488
2910
  /* Sortable header button (accessible click target inside th) */
2489
2911
  .fdy-table__sortbtn{appearance:none;border:0;background:transparent;font:inherit;color:inherit;text-transform:inherit;letter-spacing:inherit;font-weight:inherit;cursor:pointer;padding:0;}
2490
2912
  .fdy-table__sortbtn:hover{color:var(--color-text);}
2491
- .fdy-table__sortbtn:focus-visible{outline:none;border-radius:var(--radius-sm);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
2913
+ .fdy-table__sortbtn:focus-visible{outline:none;border-radius:var(--radius-sm);box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
2492
2914
 
2493
2915
  /* Selection column + selected row */
2494
2916
  .fdy-table__selcol{width:1px;white-space:nowrap;}
@@ -2535,8 +2957,8 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
2535
2957
  stripe — and deliberately WITHOUT `.fdy-table--sticky` in the selector, which would push this to
2536
2958
  (0,4,3) and beat the hover rule below. At (0,3,3) it ties, and the later rule wins, so a hovered
2537
2959
  row still bands unbroken across the freeze line. */
2538
- .fdy-table--striped tbody tr:nth-child(even) td.fdy-table__freeze{background:var(--fdy-table-stripe,color-mix(in srgb,var(--color-surface-2) 55%,var(--color-surface)));}
2539
- .fdy-table--sticky tbody tr:hover td.fdy-table__freeze{background:var(--color-surface-2);}
2960
+ .fdy-table--striped tbody tr:nth-child(even) td.fdy-table__freeze{background:var(--fdy-table-stripe,var(--color-surface-stripe));}
2961
+ .fdy-table--sticky tbody tr:hover td.fdy-table__freeze{background:var(--_fdy-row-hover,var(--color-surface-2));}
2540
2962
  /* A frozen header cell is stuck on both axes wherever the head is frozen too, so
2541
2963
  it has to outrank both. */
2542
2964
  .fdy-table--sticky thead .fdy-table__freeze{z-index:3;}
@@ -2572,7 +2994,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
2572
2994
  .fdy-table__filterwrap{display:inline-flex;align-items:center;vertical-align:middle;}
2573
2995
  .fdy-table__filterbtn{appearance:none;display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;margin-left:var(--space-1);padding:0;vertical-align:middle;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-subtle);cursor:pointer;}
2574
2996
  .fdy-table__filterbtn:hover{background:var(--color-surface-3);color:var(--color-text);}
2575
- .fdy-table__filterbtn:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
2997
+ .fdy-table__filterbtn:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
2576
2998
  .fdy-table__filterbtn.is-active{color:var(--color-primary-strong);}
2577
2999
  .fdy-table__filterbtn svg{width:.85rem;height:.85rem;}
2578
3000
 
@@ -2628,7 +3050,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
2628
3050
  a panel per tab) for in-page tabs; use these classes on plain links for routed ones, and
2629
3051
  `.fdy-nav--horizontal` for an application's PRIMARY navigation. */
2630
3052
  .fdy-tabs__tab[aria-selected="true"],.fdy-tabs__tab[aria-current="page"]{color:var(--color-primary-strong);border-bottom-color:var(--color-primary);}
2631
- .fdy-tabs__tab:focus-visible{outline:none;border-radius:var(--radius-sm);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
3053
+ .fdy-tabs__tab:focus-visible{outline:none;border-radius:var(--radius-sm);box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
2632
3054
  .fdy-tabs__panel{padding:var(--space-5) 0;}
2633
3055
  .fdy-tabs__panel[hidden]{display:none;}
2634
3056
  /* Reveal the newly-shown panel on tab switch (fade + rise), reduced-motion gated. */
@@ -2653,10 +3075,10 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
2653
3075
  .fdy-timepicker{position:relative;display:inline-block;width:100%;max-width:11rem;}
2654
3076
  .fdy-timepicker__trigger{display:inline-flex;align-items:center;gap:var(--space-2);width:100%;height:var(--control-h);padding:0 var(--space-3);text-align:left;cursor:pointer;font-family:var(--font-body);font-size:var(--text-sm);color:var(--color-text);background:var(--color-surface);border:1.5px solid var(--color-control-border);border-radius:var(--radius-md);box-shadow:inset 0 1px 2px rgba(16,14,30,.06);transition:border-color var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
2655
3077
  .fdy-timepicker__trigger:hover{border-color:var(--color-text-muted);}
2656
- .fdy-timepicker__trigger:focus-visible{outline:none;border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
2657
- .fdy-timepicker__trigger.is-open{border-color:var(--color-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
3078
+ .fdy-timepicker__trigger:focus-visible{outline:none;border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px rgba(var(--color-primary-rgb),.26);}
3079
+ .fdy-timepicker__trigger.is-open{border-color:var(--color-primary);box-shadow:0 0 0 3px rgba(var(--color-primary-rgb),.26);}
2658
3080
  .fdy-timepicker--error .fdy-timepicker__trigger,.fdy-timepicker__trigger[aria-invalid="true"]{border-color:var(--color-danger);}
2659
- .fdy-timepicker--error .fdy-timepicker__trigger:focus-visible,.fdy-timepicker--error .fdy-timepicker__trigger.is-open,.fdy-timepicker__trigger[aria-invalid="true"]:focus-visible{border-color:var(--color-danger);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-danger) 26%,transparent);}
3081
+ .fdy-timepicker--error .fdy-timepicker__trigger:focus-visible,.fdy-timepicker--error .fdy-timepicker__trigger.is-open,.fdy-timepicker__trigger[aria-invalid="true"]:focus-visible{border-color:var(--color-danger);box-shadow:0 0 0 3px rgba(var(--color-danger-rgb),.26);}
2660
3082
  .fdy-timepicker__trigger:disabled,.fdy-timepicker__trigger[aria-disabled="true"]{opacity:.5;cursor:not-allowed;}
2661
3083
  .fdy-timepicker__value{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-variant-numeric:tabular-nums;}
2662
3084
  .fdy-timepicker__value--placeholder{color:var(--color-text-subtle);}
@@ -2696,7 +3118,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
2696
3118
  .fdy-toast__text{opacity:.88;line-height:var(--leading-normal);}
2697
3119
  .fdy-toast__close{flex:none;border:0;background:transparent;color:var(--color-inverse-text);opacity:.7;cursor:pointer;font-size:var(--text-lg);line-height:1;padding:0 var(--space-1);border-radius:var(--radius-sm);}
2698
3120
  .fdy-toast__close:hover{opacity:1;}
2699
- .fdy-toast__close:focus-visible{outline:none;opacity:1;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-inverse-text) 40%,transparent);}
3121
+ .fdy-toast__close:focus-visible{outline:none;opacity:1;box-shadow:0 0 0 3px rgba(var(--color-inverse-text-rgb),.4);}
2700
3122
  .fdy-toast.is-leaving{opacity:0;transform:translateX(16px);transition:opacity var(--dur-base) var(--ease-standard),transform var(--dur-base) var(--ease-standard);}
2701
3123
  @media (prefers-reduced-motion:no-preference){.fdy-toast{animation:fdy-toast-in var(--dur-base) var(--ease-spring);}}
2702
3124
  @keyframes fdy-toast-in{from{opacity:0;transform:translateY(14px);}to{opacity:1;transform:none;}}
@@ -2716,7 +3138,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
2716
3138
  .fdy-tree__branch > summary{list-style:none;cursor:pointer;}
2717
3139
  .fdy-tree__branch > summary::-webkit-details-marker{display:none;}
2718
3140
  .fdy-tree__branch > summary:hover,.fdy-tree__leaf:hover{background:var(--color-surface-2);}
2719
- .fdy-tree__branch > summary:focus-visible{outline:none;box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
3141
+ .fdy-tree__branch > summary:focus-visible{outline:none;box-shadow:inset 0 0 0 2px rgba(var(--color-primary-rgb),.45);}
2720
3142
  .fdy-tree__chevron{flex:none;width:.9rem;height:.9rem;color:var(--color-text-subtle);transition:transform var(--dur-fast) var(--ease-standard);}
2721
3143
  .fdy-tree__branch[open] > summary .fdy-tree__chevron{transform:rotate(90deg);}
2722
3144
  .fdy-tree__icon{flex:none;width:1rem;height:1rem;color:var(--color-text-muted);}