@twentyfirstmedia/halo 0.1.1 → 0.2.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.
package/dist/styles.css CHANGED
@@ -94,6 +94,10 @@
94
94
  --dur-enter: 520ms;
95
95
  --dur-slow: 900ms;
96
96
  --stagger-step: 55ms;
97
+ --dur-exit: 160ms;
98
+ --motion-time: 1;
99
+ --motion-travel: 1;
100
+ --motion-blur: 0px;
97
101
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
98
102
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
99
103
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
@@ -300,6 +304,20 @@
300
304
  --on-frame-danger: #f2a8a0;
301
305
  --scrim: rgba(10, 10, 10, 0.32);
302
306
  --scrim-strong: rgba(10, 10, 10, 0.38);
307
+ --glass: color-mix(in srgb, var(--card) 72%, transparent);
308
+ --glass-strong: color-mix(in srgb, var(--card) 86%, transparent);
309
+ --glass-control: color-mix(in srgb, var(--card) 62%, transparent);
310
+ --glass-well: color-mix(in srgb, var(--foreground) 6%, transparent);
311
+ --glass-hover: color-mix(in srgb, var(--foreground) 8%, transparent);
312
+ --glass-border: rgba(255, 255, 255, 0.6);
313
+ --glass-highlight: rgba(255, 255, 255, 0.95);
314
+ --glass-sheen: rgba(255, 255, 255, 0.5);
315
+ --ambient-base: var(--background);
316
+ --ambient-1: color-mix(in oklab, var(--sky) 72%, var(--sky-ink));
317
+ --ambient-2: color-mix(in oklab, var(--mint) 72%, var(--mint-ink));
318
+ --ambient-3: color-mix(in oklab, var(--lilac) 72%, var(--lilac-ink));
319
+ --ambient-4: color-mix(in oklab, var(--butter) 72%, var(--butter-ink));
320
+ --ambient-scrim: rgba(255, 255, 255, 0.28);
303
321
  --bg-000: var(--canvas);
304
322
  --bg-100: var(--surface);
305
323
  --bg-200: var(--surface-sunken);
@@ -357,6 +375,7 @@
357
375
  --shadow-lift: 0 2px 4px rgba(20,20,20,0.06), 0 14px 32px rgba(20,20,20,0.10);
358
376
  --shadow-float: 0 16px 40px rgba(0,0,0,0.22);
359
377
  --shadow-overlay: var(--shadow-float);
378
+ --shadow-glass: 0 1px 2px rgba(20, 20, 40, 0.06), 0 12px 40px rgba(20, 20, 40, 0.14);
360
379
  --button-radius: var(--radius-pill);
361
380
  --field-radius: var(--radius-pill);
362
381
  --badge-radius: var(--radius-pill);
@@ -366,6 +385,8 @@
366
385
  --panel-shadow: var(--shadow-card);
367
386
  --panel-pad: var(--space-6);
368
387
  --panel-gap: var(--space-4);
388
+ --glass-filter: blur(24px) saturate(180%) brightness(1.05);
389
+ --glass-tint: 62%;
369
390
  color-scheme: light;
370
391
  }
371
392
  @media (prefers-color-scheme: dark) {
@@ -431,6 +452,20 @@
431
452
  --on-frame-danger: #f2a8a0;
432
453
  --scrim: rgba(10, 10, 10, 0.32);
433
454
  --scrim-strong: rgba(10, 10, 10, 0.38);
455
+ --glass: color-mix(in srgb, var(--card) 70%, transparent);
456
+ --glass-strong: color-mix(in srgb, var(--card) 86%, transparent);
457
+ --glass-control: color-mix(in srgb, var(--foreground) 10%, transparent);
458
+ --glass-well: color-mix(in srgb, var(--foreground) 6%, transparent);
459
+ --glass-hover: color-mix(in srgb, var(--foreground) 12%, transparent);
460
+ --glass-border: rgba(255, 255, 255, 0.1);
461
+ --glass-highlight: rgba(255, 255, 255, 0.18);
462
+ --glass-sheen: rgba(255, 255, 255, 0.05);
463
+ --ambient-base: var(--background);
464
+ --ambient-1: var(--sky);
465
+ --ambient-2: var(--mint);
466
+ --ambient-3: var(--lilac);
467
+ --ambient-4: var(--butter);
468
+ --ambient-scrim: rgba(10, 10, 10, 0.45);
434
469
  --bg-000: var(--canvas);
435
470
  --bg-100: var(--surface);
436
471
  --bg-200: var(--surface-sunken);
@@ -488,6 +523,7 @@
488
523
  --shadow-lift: 0 2px 6px rgba(0,0,0,0.5), 0 14px 32px rgba(0,0,0,0.45);
489
524
  --shadow-float: 0 16px 40px rgba(0,0,0,0.6);
490
525
  --shadow-overlay: var(--shadow-float);
526
+ --shadow-glass: 0 1px 2px rgba(0, 0, 0, 0.3), 0 16px 48px rgba(0, 0, 0, 0.45);
491
527
  --button-radius: var(--radius-pill);
492
528
  --field-radius: var(--radius-pill);
493
529
  --badge-radius: var(--radius-pill);
@@ -497,6 +533,8 @@
497
533
  --panel-shadow: var(--shadow-card);
498
534
  --panel-pad: var(--space-6);
499
535
  --panel-gap: var(--space-4);
536
+ --glass-filter: blur(24px) saturate(160%) brightness(0.85);
537
+ --glass-tint: 62%;
500
538
  color-scheme: dark;
501
539
  }
502
540
  }
@@ -563,6 +601,20 @@
563
601
  --on-frame-danger: #f2a8a0;
564
602
  --scrim: rgba(10, 10, 10, 0.32);
565
603
  --scrim-strong: rgba(10, 10, 10, 0.38);
604
+ --glass: color-mix(in srgb, var(--card) 70%, transparent);
605
+ --glass-strong: color-mix(in srgb, var(--card) 86%, transparent);
606
+ --glass-control: color-mix(in srgb, var(--foreground) 10%, transparent);
607
+ --glass-well: color-mix(in srgb, var(--foreground) 6%, transparent);
608
+ --glass-hover: color-mix(in srgb, var(--foreground) 12%, transparent);
609
+ --glass-border: rgba(255, 255, 255, 0.1);
610
+ --glass-highlight: rgba(255, 255, 255, 0.18);
611
+ --glass-sheen: rgba(255, 255, 255, 0.05);
612
+ --ambient-base: var(--background);
613
+ --ambient-1: var(--sky);
614
+ --ambient-2: var(--mint);
615
+ --ambient-3: var(--lilac);
616
+ --ambient-4: var(--butter);
617
+ --ambient-scrim: rgba(10, 10, 10, 0.45);
566
618
  --bg-000: var(--canvas);
567
619
  --bg-100: var(--surface);
568
620
  --bg-200: var(--surface-sunken);
@@ -620,6 +672,7 @@
620
672
  --shadow-lift: 0 2px 6px rgba(0,0,0,0.5), 0 14px 32px rgba(0,0,0,0.45);
621
673
  --shadow-float: 0 16px 40px rgba(0,0,0,0.6);
622
674
  --shadow-overlay: var(--shadow-float);
675
+ --shadow-glass: 0 1px 2px rgba(0, 0, 0, 0.3), 0 16px 48px rgba(0, 0, 0, 0.45);
623
676
  --button-radius: var(--radius-pill);
624
677
  --field-radius: var(--radius-pill);
625
678
  --badge-radius: var(--radius-pill);
@@ -629,6 +682,8 @@
629
682
  --panel-shadow: var(--shadow-card);
630
683
  --panel-pad: var(--space-6);
631
684
  --panel-gap: var(--space-4);
685
+ --glass-filter: blur(24px) saturate(160%) brightness(0.85);
686
+ --glass-tint: 62%;
632
687
  color-scheme: dark;
633
688
  }
634
689
  }
@@ -695,6 +750,20 @@
695
750
  --on-frame-danger: #f2a8a0;
696
751
  --scrim: rgba(10, 10, 10, 0.32);
697
752
  --scrim-strong: rgba(10, 10, 10, 0.38);
753
+ --glass: color-mix(in srgb, var(--card) 72%, transparent);
754
+ --glass-strong: color-mix(in srgb, var(--card) 86%, transparent);
755
+ --glass-control: color-mix(in srgb, var(--card) 62%, transparent);
756
+ --glass-well: color-mix(in srgb, var(--foreground) 6%, transparent);
757
+ --glass-hover: color-mix(in srgb, var(--foreground) 8%, transparent);
758
+ --glass-border: rgba(255, 255, 255, 0.6);
759
+ --glass-highlight: rgba(255, 255, 255, 0.95);
760
+ --glass-sheen: rgba(255, 255, 255, 0.5);
761
+ --ambient-base: var(--background);
762
+ --ambient-1: color-mix(in oklab, var(--sky) 72%, var(--sky-ink));
763
+ --ambient-2: color-mix(in oklab, var(--mint) 72%, var(--mint-ink));
764
+ --ambient-3: color-mix(in oklab, var(--lilac) 72%, var(--lilac-ink));
765
+ --ambient-4: color-mix(in oklab, var(--butter) 72%, var(--butter-ink));
766
+ --ambient-scrim: rgba(255, 255, 255, 0.28);
698
767
  --bg-000: var(--canvas);
699
768
  --bg-100: var(--surface);
700
769
  --bg-200: var(--surface-sunken);
@@ -752,6 +821,7 @@
752
821
  --shadow-lift: 0 2px 4px rgba(20,20,20,0.06), 0 14px 32px rgba(20,20,20,0.10);
753
822
  --shadow-float: 0 16px 40px rgba(0,0,0,0.22);
754
823
  --shadow-overlay: var(--shadow-float);
824
+ --shadow-glass: 0 1px 2px rgba(20, 20, 40, 0.06), 0 12px 40px rgba(20, 20, 40, 0.14);
755
825
  --button-radius: var(--radius-pill);
756
826
  --field-radius: var(--radius-pill);
757
827
  --badge-radius: var(--radius-pill);
@@ -761,6 +831,8 @@
761
831
  --panel-shadow: var(--shadow-card);
762
832
  --panel-pad: var(--space-6);
763
833
  --panel-gap: var(--space-4);
834
+ --glass-filter: blur(24px) saturate(180%) brightness(1.05);
835
+ --glass-tint: 62%;
764
836
  color-scheme: light;
765
837
  }
766
838
  }
@@ -826,6 +898,20 @@
826
898
  --on-frame-danger: #f2a8a0;
827
899
  --scrim: rgba(10, 10, 10, 0.32);
828
900
  --scrim-strong: rgba(10, 10, 10, 0.38);
901
+ --glass: color-mix(in srgb, var(--card) 70%, transparent);
902
+ --glass-strong: color-mix(in srgb, var(--card) 86%, transparent);
903
+ --glass-control: color-mix(in srgb, var(--foreground) 10%, transparent);
904
+ --glass-well: color-mix(in srgb, var(--foreground) 6%, transparent);
905
+ --glass-hover: color-mix(in srgb, var(--foreground) 12%, transparent);
906
+ --glass-border: rgba(255, 255, 255, 0.1);
907
+ --glass-highlight: rgba(255, 255, 255, 0.18);
908
+ --glass-sheen: rgba(255, 255, 255, 0.05);
909
+ --ambient-base: var(--background);
910
+ --ambient-1: var(--sky);
911
+ --ambient-2: var(--mint);
912
+ --ambient-3: var(--lilac);
913
+ --ambient-4: var(--butter);
914
+ --ambient-scrim: rgba(10, 10, 10, 0.45);
829
915
  --bg-000: var(--canvas);
830
916
  --bg-100: var(--surface);
831
917
  --bg-200: var(--surface-sunken);
@@ -883,6 +969,7 @@
883
969
  --shadow-lift: 0 2px 6px rgba(0,0,0,0.5), 0 14px 32px rgba(0,0,0,0.45);
884
970
  --shadow-float: 0 16px 40px rgba(0,0,0,0.6);
885
971
  --shadow-overlay: var(--shadow-float);
972
+ --shadow-glass: 0 1px 2px rgba(0, 0, 0, 0.3), 0 16px 48px rgba(0, 0, 0, 0.45);
886
973
  --button-radius: var(--radius-pill);
887
974
  --field-radius: var(--radius-pill);
888
975
  --badge-radius: var(--radius-pill);
@@ -892,6 +979,8 @@
892
979
  --panel-shadow: var(--shadow-card);
893
980
  --panel-pad: var(--space-6);
894
981
  --panel-gap: var(--space-4);
982
+ --glass-filter: blur(24px) saturate(160%) brightness(0.85);
983
+ --glass-tint: 62%;
895
984
  color-scheme: dark;
896
985
  }
897
986
  :where([data-theme="light"], .light) {
@@ -956,6 +1045,20 @@
956
1045
  --on-frame-danger: #f2a8a0;
957
1046
  --scrim: rgba(10, 10, 10, 0.32);
958
1047
  --scrim-strong: rgba(10, 10, 10, 0.38);
1048
+ --glass: color-mix(in srgb, var(--card) 72%, transparent);
1049
+ --glass-strong: color-mix(in srgb, var(--card) 86%, transparent);
1050
+ --glass-control: color-mix(in srgb, var(--card) 62%, transparent);
1051
+ --glass-well: color-mix(in srgb, var(--foreground) 6%, transparent);
1052
+ --glass-hover: color-mix(in srgb, var(--foreground) 8%, transparent);
1053
+ --glass-border: rgba(255, 255, 255, 0.6);
1054
+ --glass-highlight: rgba(255, 255, 255, 0.95);
1055
+ --glass-sheen: rgba(255, 255, 255, 0.5);
1056
+ --ambient-base: var(--background);
1057
+ --ambient-1: color-mix(in oklab, var(--sky) 72%, var(--sky-ink));
1058
+ --ambient-2: color-mix(in oklab, var(--mint) 72%, var(--mint-ink));
1059
+ --ambient-3: color-mix(in oklab, var(--lilac) 72%, var(--lilac-ink));
1060
+ --ambient-4: color-mix(in oklab, var(--butter) 72%, var(--butter-ink));
1061
+ --ambient-scrim: rgba(255, 255, 255, 0.28);
959
1062
  --bg-000: var(--canvas);
960
1063
  --bg-100: var(--surface);
961
1064
  --bg-200: var(--surface-sunken);
@@ -1013,6 +1116,7 @@
1013
1116
  --shadow-lift: 0 2px 4px rgba(20,20,20,0.06), 0 14px 32px rgba(20,20,20,0.10);
1014
1117
  --shadow-float: 0 16px 40px rgba(0,0,0,0.22);
1015
1118
  --shadow-overlay: var(--shadow-float);
1119
+ --shadow-glass: 0 1px 2px rgba(20, 20, 40, 0.06), 0 12px 40px rgba(20, 20, 40, 0.14);
1016
1120
  --button-radius: var(--radius-pill);
1017
1121
  --field-radius: var(--radius-pill);
1018
1122
  --badge-radius: var(--radius-pill);
@@ -1022,6 +1126,8 @@
1022
1126
  --panel-shadow: var(--shadow-card);
1023
1127
  --panel-pad: var(--space-6);
1024
1128
  --panel-gap: var(--space-4);
1129
+ --glass-filter: blur(24px) saturate(180%) brightness(1.05);
1130
+ --glass-tint: 62%;
1025
1131
  color-scheme: light;
1026
1132
  }
1027
1133
  :where([data-density="compact"]) {
@@ -1038,6 +1144,69 @@
1038
1144
  --density-font-size: 14px;
1039
1145
  --density-line-height: 20px;
1040
1146
  }
1147
+ :where([data-motion="expressive"]) {
1148
+ --dur-enter: 760ms;
1149
+ --dur-slow: 1300ms;
1150
+ --dur-exit: 220ms;
1151
+ --stagger-step: 90ms;
1152
+ --motion-time: 1.4;
1153
+ --motion-travel: 1.7;
1154
+ --motion-blur: 8px;
1155
+ }
1156
+ :where([data-motion="off"]) {
1157
+ --duration-fast: 0ms;
1158
+ --duration-base: 0ms;
1159
+ --dur-fast: 0ms;
1160
+ --dur-base: 0ms;
1161
+ --dur-enter: 0ms;
1162
+ --dur-slow: 0ms;
1163
+ --dur-exit: 0ms;
1164
+ --stagger-step: 0ms;
1165
+ --motion-time: 0;
1166
+ --motion-travel: 0;
1167
+ --motion-blur: 0px;
1168
+ }
1169
+ @media (prefers-reduced-motion: reduce) {
1170
+ :where(:root, [data-motion], [data-theme]) {
1171
+ --duration-fast: 0ms;
1172
+ --duration-base: 0ms;
1173
+ --dur-fast: 0ms;
1174
+ --dur-base: 0ms;
1175
+ --dur-enter: 0ms;
1176
+ --dur-slow: 0ms;
1177
+ --dur-exit: 0ms;
1178
+ --stagger-step: 0ms;
1179
+ --motion-time: 0;
1180
+ --motion-travel: 0;
1181
+ --motion-blur: 0px;
1182
+ }
1183
+ }
1184
+ @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
1185
+ :where(:root, [data-theme], .dark, .light) {
1186
+ --glass: var(--glass-strong);
1187
+ }
1188
+ }
1189
+ @media (prefers-contrast: more) {
1190
+ :where(:root, [data-theme], .dark, .light) {
1191
+ --glass: var(--glass-strong);
1192
+ }
1193
+ }
1194
+ @media (prefers-reduced-transparency: reduce) {
1195
+ :where(:root, [data-theme], .dark, .light) {
1196
+ --glass: var(--card);
1197
+ --glass-strong: var(--popover);
1198
+ --glass-sheen: transparent;
1199
+ --glass-filter: none;
1200
+ }
1201
+ }
1202
+ @media (forced-colors: active) {
1203
+ :where(:root, [data-theme], .dark, .light) {
1204
+ --glass: var(--card);
1205
+ --glass-strong: var(--popover);
1206
+ --glass-sheen: transparent;
1207
+ --glass-filter: none;
1208
+ }
1209
+ }
1041
1210
 
1042
1211
  /* 01-foundation.css */
1043
1212
  /* Halo components, ported from reference/halo/source/halo.css (ha- → tf-). Every value comes from the tokens. */
@@ -1583,18 +1752,23 @@ select.tf-input {
1583
1752
  box-shadow: none;
1584
1753
  }
1585
1754
  .tf-bg-sky {
1755
+ --tint: var(--sky);
1586
1756
  background: var(--sky);
1587
1757
  }
1588
1758
  .tf-bg-mint {
1759
+ --tint: var(--mint);
1589
1760
  background: var(--mint);
1590
1761
  }
1591
1762
  .tf-bg-lilac {
1763
+ --tint: var(--lilac);
1592
1764
  background: var(--lilac);
1593
1765
  }
1594
1766
  .tf-bg-butter {
1767
+ --tint: var(--butter);
1595
1768
  background: var(--butter);
1596
1769
  }
1597
1770
  .tf-bg-blush {
1771
+ --tint: var(--blush);
1598
1772
  background: var(--blush);
1599
1773
  }
1600
1774
  .tf-bg-surface {
@@ -2016,7 +2190,7 @@ select.tf-input {
2016
2190
  @keyframes tf-rise {
2017
2191
  from {
2018
2192
  opacity: 0;
2019
- transform: translateY(14px) scale(0.985);
2193
+ transform: translateY(calc(14px * var(--motion-travel))) scale(0.985);
2020
2194
  }
2021
2195
  to {
2022
2196
  opacity: 1;
@@ -2034,7 +2208,7 @@ select.tf-input {
2034
2208
  @keyframes tf-drop {
2035
2209
  from {
2036
2210
  opacity: 0;
2037
- transform: translateY(-10px);
2211
+ transform: translateY(calc(-10px * var(--motion-travel)));
2038
2212
  }
2039
2213
  to {
2040
2214
  opacity: 1;
@@ -2044,7 +2218,7 @@ select.tf-input {
2044
2218
  @keyframes tf-slide-in {
2045
2219
  from {
2046
2220
  opacity: 0;
2047
- transform: translateX(24px);
2221
+ transform: translateX(calc(24px * var(--motion-travel)));
2048
2222
  }
2049
2223
  to {
2050
2224
  opacity: 1;
@@ -2054,7 +2228,8 @@ select.tf-input {
2054
2228
  @keyframes tf-slide-up {
2055
2229
  from {
2056
2230
  opacity: 0;
2057
- transform: translate(0, 28px) scale(0.9);
2231
+ transform: translate(calc(0px * var(--motion-travel)), calc(28px * var(--motion-travel)))
2232
+ scale(0.9);
2058
2233
  }
2059
2234
  to {
2060
2235
  opacity: 1;
@@ -2113,47 +2288,60 @@ select.tf-input {
2113
2288
  }
2114
2289
 
2115
2290
  .tf-in {
2291
+ --enter-delay: var(--d, 0ms);
2116
2292
  animation: tf-rise var(--dur-enter) var(--ease-out) both;
2117
- animation-delay: var(--d, 0ms);
2293
+ animation-delay: var(--enter-delay);
2118
2294
  }
2119
2295
  .tf-stagger > * {
2120
2296
  animation: tf-rise var(--dur-enter) var(--ease-out) both;
2121
2297
  }
2122
2298
  .tf-stagger > :nth-child(1) {
2123
- animation-delay: calc(var(--base, 0ms) + var(--stagger-step) * 0);
2299
+ --enter-delay: calc(var(--base, 0ms) + var(--stagger-step) * 0);
2300
+ animation-delay: var(--enter-delay);
2124
2301
  }
2125
2302
  .tf-stagger > :nth-child(2) {
2126
- animation-delay: calc(var(--base, 0ms) + var(--stagger-step) * 1);
2303
+ --enter-delay: calc(var(--base, 0ms) + var(--stagger-step) * 1);
2304
+ animation-delay: var(--enter-delay);
2127
2305
  }
2128
2306
  .tf-stagger > :nth-child(3) {
2129
- animation-delay: calc(var(--base, 0ms) + var(--stagger-step) * 2);
2307
+ --enter-delay: calc(var(--base, 0ms) + var(--stagger-step) * 2);
2308
+ animation-delay: var(--enter-delay);
2130
2309
  }
2131
2310
  .tf-stagger > :nth-child(4) {
2132
- animation-delay: calc(var(--base, 0ms) + var(--stagger-step) * 3);
2311
+ --enter-delay: calc(var(--base, 0ms) + var(--stagger-step) * 3);
2312
+ animation-delay: var(--enter-delay);
2133
2313
  }
2134
2314
  .tf-stagger > :nth-child(5) {
2135
- animation-delay: calc(var(--base, 0ms) + var(--stagger-step) * 4);
2315
+ --enter-delay: calc(var(--base, 0ms) + var(--stagger-step) * 4);
2316
+ animation-delay: var(--enter-delay);
2136
2317
  }
2137
2318
  .tf-stagger > :nth-child(6) {
2138
- animation-delay: calc(var(--base, 0ms) + var(--stagger-step) * 5);
2319
+ --enter-delay: calc(var(--base, 0ms) + var(--stagger-step) * 5);
2320
+ animation-delay: var(--enter-delay);
2139
2321
  }
2140
2322
  .tf-stagger > :nth-child(7) {
2141
- animation-delay: calc(var(--base, 0ms) + var(--stagger-step) * 6);
2323
+ --enter-delay: calc(var(--base, 0ms) + var(--stagger-step) * 6);
2324
+ animation-delay: var(--enter-delay);
2142
2325
  }
2143
2326
  .tf-stagger > :nth-child(8) {
2144
- animation-delay: calc(var(--base, 0ms) + var(--stagger-step) * 7);
2327
+ --enter-delay: calc(var(--base, 0ms) + var(--stagger-step) * 7);
2328
+ animation-delay: var(--enter-delay);
2145
2329
  }
2146
2330
  .tf-stagger > :nth-child(9) {
2147
- animation-delay: calc(var(--base, 0ms) + var(--stagger-step) * 8);
2331
+ --enter-delay: calc(var(--base, 0ms) + var(--stagger-step) * 8);
2332
+ animation-delay: var(--enter-delay);
2148
2333
  }
2149
2334
  .tf-stagger > :nth-child(10) {
2150
- animation-delay: calc(var(--base, 0ms) + var(--stagger-step) * 9);
2335
+ --enter-delay: calc(var(--base, 0ms) + var(--stagger-step) * 9);
2336
+ animation-delay: var(--enter-delay);
2151
2337
  }
2152
2338
  .tf-stagger > :nth-child(11) {
2153
- animation-delay: calc(var(--base, 0ms) + var(--stagger-step) * 10);
2339
+ --enter-delay: calc(var(--base, 0ms) + var(--stagger-step) * 10);
2340
+ animation-delay: var(--enter-delay);
2154
2341
  }
2155
2342
  .tf-stagger > :nth-child(n + 12) {
2156
- animation-delay: calc(var(--base, 0ms) + var(--stagger-step) * 11);
2343
+ --enter-delay: calc(var(--base, 0ms) + var(--stagger-step) * 11);
2344
+ animation-delay: var(--enter-delay);
2157
2345
  }
2158
2346
  .tf-topbar {
2159
2347
  animation: tf-drop var(--dur-enter) var(--ease-out) both;
@@ -2163,32 +2351,32 @@ select.tf-input {
2163
2351
  }
2164
2352
  .tf-panel.tf-side {
2165
2353
  animation: tf-slide-in var(--dur-enter) var(--ease-out) both;
2166
- animation-delay: 160ms;
2354
+ animation-delay: calc(160ms * var(--motion-time));
2167
2355
  }
2168
2356
  .tf-dock {
2169
2357
  animation: tf-slide-up var(--dur-enter) var(--ease-spring) both;
2170
- animation-delay: 420ms;
2358
+ animation-delay: calc(420ms * var(--motion-time));
2171
2359
  }
2172
2360
  .tf-dock .tf-dock-btn {
2173
2361
  animation: tf-pop var(--dur-base) var(--ease-spring) both;
2174
2362
  }
2175
2363
  .tf-dock .tf-dock-btn:nth-child(1) {
2176
- animation-delay: 520ms;
2364
+ animation-delay: calc(520ms * var(--motion-time));
2177
2365
  }
2178
2366
  .tf-dock .tf-dock-btn:nth-child(2) {
2179
- animation-delay: 570ms;
2367
+ animation-delay: calc(570ms * var(--motion-time));
2180
2368
  }
2181
2369
  .tf-dock .tf-dock-btn:nth-child(3) {
2182
- animation-delay: 620ms;
2370
+ animation-delay: calc(620ms * var(--motion-time));
2183
2371
  }
2184
2372
  .tf-dock .tf-dock-btn:nth-child(4) {
2185
- animation-delay: 670ms;
2373
+ animation-delay: calc(670ms * var(--motion-time));
2186
2374
  }
2187
2375
  .tf-dock .tf-dock-btn:nth-child(5) {
2188
- animation-delay: 720ms;
2376
+ animation-delay: calc(720ms * var(--motion-time));
2189
2377
  }
2190
2378
  .tf-dock .tf-dock-btn:nth-child(n + 6) {
2191
- animation-delay: 770ms;
2379
+ animation-delay: calc(770ms * var(--motion-time));
2192
2380
  }
2193
2381
  .tf-rail-btn {
2194
2382
  animation: tf-pop var(--dur-base) var(--ease-spring) both;
@@ -2225,7 +2413,7 @@ select.tf-input {
2225
2413
  .tf-progress > span {
2226
2414
  transform-origin: left;
2227
2415
  animation: tf-grow-x var(--dur-slow) var(--ease-out) both;
2228
- animation-delay: 200ms;
2416
+ animation-delay: calc(var(--enter-delay, 0ms) + calc(200ms * var(--motion-time)));
2229
2417
  }
2230
2418
 
2231
2419
  /* Sliding tab indicator */
@@ -2304,6 +2492,7 @@ select.tf-input {
2304
2492
  transform-box: fill-box;
2305
2493
  transform-origin: bottom;
2306
2494
  animation: tf-grow-y var(--dur-slow) var(--ease-out) both;
2495
+ animation-delay: var(--enter-delay, 0ms);
2307
2496
  }
2308
2497
  .tf-bar-ink {
2309
2498
  fill: var(--primary);
@@ -2331,7 +2520,7 @@ select.tf-input {
2331
2520
  stroke-dasharray: 1;
2332
2521
  stroke-dashoffset: 1;
2333
2522
  animation: tf-draw var(--dur-slow) var(--ease-out) forwards;
2334
- animation-delay: 150ms;
2523
+ animation-delay: calc(var(--enter-delay, 0ms) + calc(150ms * var(--motion-time)));
2335
2524
  }
2336
2525
  .tf-line-ink {
2337
2526
  stroke: var(--primary);
@@ -2354,7 +2543,7 @@ select.tf-input {
2354
2543
  .tf-area {
2355
2544
  opacity: 0;
2356
2545
  animation: tf-fade var(--dur-slow) var(--ease-out) forwards;
2357
- animation-delay: 500ms;
2546
+ animation-delay: calc(var(--enter-delay, 0ms) + calc(500ms * var(--motion-time)));
2358
2547
  }
2359
2548
  .tf-area-sky {
2360
2549
  fill: var(--sky);
@@ -2375,7 +2564,7 @@ select.tf-input {
2375
2564
  fill: var(--surface);
2376
2565
  stroke-width: 2.5;
2377
2566
  animation: tf-pop var(--dur-base) var(--ease-spring) both;
2378
- animation-delay: 900ms;
2567
+ animation-delay: calc(var(--enter-delay, 0ms) + calc(900ms * var(--motion-time)));
2379
2568
  transform-box: fill-box;
2380
2569
  transform-origin: center;
2381
2570
  }
@@ -2804,6 +2993,19 @@ select.tf-input {
2804
2993
  }
2805
2994
  }
2806
2995
 
2996
+ /* the same for an app that turns motion off: data-motion="off" */
2997
+ [data-motion="off"] *,
2998
+ [data-motion="off"] *::before,
2999
+ [data-motion="off"] *::after {
3000
+ animation-duration: 1ms !important;
3001
+ animation-delay: 0ms !important;
3002
+ animation-iteration-count: 1 !important;
3003
+ transition-duration: 1ms !important;
3004
+ }
3005
+ [data-motion="off"] .tf-line {
3006
+ stroke-dashoffset: 0;
3007
+ }
3008
+
2807
3009
  /* ───────── Refinements ───────── */
2808
3010
  .tf-nav-label {
2809
3011
  white-space: nowrap;
@@ -2835,7 +3037,7 @@ select.tf-input {
2835
3037
  .tf-area {
2836
3038
  opacity: 0;
2837
3039
  animation: tf-area-in var(--dur-slow) var(--ease-out) forwards;
2838
- animation-delay: 500ms;
3040
+ animation-delay: calc(var(--enter-delay, 0ms) + calc(500ms * var(--motion-time)));
2839
3041
  }
2840
3042
  @keyframes tf-area-in {
2841
3043
  to {
@@ -3397,7 +3599,7 @@ select.tf-input {
3397
3599
  }
3398
3600
  @keyframes tf-drawer-in {
3399
3601
  from {
3400
- transform: translateX(40px);
3602
+ transform: translateX(calc(40px * var(--motion-travel)));
3401
3603
  opacity: 0;
3402
3604
  }
3403
3605
  to {
@@ -5372,6 +5574,7 @@ label.tf-menu-item {
5372
5574
  transform-box: fill-box;
5373
5575
  transform-origin: bottom;
5374
5576
  animation: tf-grow-y var(--dur-slow) var(--ease-out) both;
5577
+ animation-delay: var(--enter-delay, 0ms);
5375
5578
  transition: opacity var(--dur-fast);
5376
5579
  }
5377
5580
  .tf-chart g.dim .tf-seg-bar {
@@ -5417,7 +5620,7 @@ label.tf-menu-item {
5417
5620
  .tf-area2 {
5418
5621
  opacity: 0;
5419
5622
  animation: tf-area2-in var(--dur-slow) var(--ease-out) forwards;
5420
- animation-delay: 400ms;
5623
+ animation-delay: calc(var(--enter-delay, 0ms) + calc(400ms * var(--motion-time)));
5421
5624
  }
5422
5625
  @keyframes tf-area2-in {
5423
5626
  to {
@@ -5654,6 +5857,18 @@ label.tf-menu-item {
5654
5857
  display: none;
5655
5858
  }
5656
5859
  }
5860
+ /* the same for an app that turns motion off: data-motion="off" */
5861
+ [data-motion="off"] .tf-seg-bar,
5862
+ [data-motion="off"] .tf-area2,
5863
+ [data-motion="off"] .tf-heat-cell,
5864
+ [data-motion="off"] .tf-tooltip,
5865
+ [data-motion="off"] .tf-popover {
5866
+ animation: none !important;
5867
+ opacity: 1;
5868
+ }
5869
+ [data-motion="off"] .tf-toast-timer {
5870
+ display: none;
5871
+ }
5657
5872
  @media (max-width: 640px) {
5658
5873
  .tf-dp {
5659
5874
  flex-direction: column;
@@ -7422,6 +7637,14 @@ label.tf-menu-item {
7422
7637
  animation: none !important;
7423
7638
  }
7424
7639
  }
7640
+ /* the same for an app that turns motion off: data-motion="off" */
7641
+ [data-motion="off"] .tf-msg,
7642
+ [data-motion="off"] .tf-caret,
7643
+ [data-motion="off"] .tf-typing i,
7644
+ [data-motion="off"] .tf-spin,
7645
+ [data-motion="off"] .tf-reason.live .tf-reason-head {
7646
+ animation: none !important;
7647
+ }
7425
7648
 
7426
7649
  /* 04-status-agents-operations.css */
7427
7650
  /* ════════ Kit 4: status shapes, approvals A–D, foundations, agent & operations ════════ */
@@ -9592,6 +9815,13 @@ label.tf-menu-item {
9592
9815
  animation: none !important;
9593
9816
  }
9594
9817
  }
9818
+ /* the same for an app that turns motion off: data-motion="off" */
9819
+ [data-motion="off"] .tf-shape-pulse,
9820
+ [data-motion="off"] .tf-voice-wave i,
9821
+ [data-motion="off"] .tf-stage.st-running::after,
9822
+ [data-motion="off"] .tf-log-line.fresh {
9823
+ animation: none !important;
9824
+ }
9595
9825
  .tf-approval-mail {
9596
9826
  padding: 12px 16px;
9597
9827
  border-radius: var(--radius-md);
@@ -9946,7 +10176,7 @@ label.tf-menu-item {
9946
10176
  @keyframes tf-fresh {
9947
10177
  0% {
9948
10178
  background: var(--surface-sunken);
9949
- transform: translateY(-4px);
10179
+ transform: translateY(calc(-4px * var(--motion-travel)));
9950
10180
  opacity: 0;
9951
10181
  }
9952
10182
  15% {
@@ -11850,6 +12080,13 @@ button.tf-runs-main[aria-expanded]:hover {
11850
12080
  animation: none;
11851
12081
  }
11852
12082
  }
12083
+ /* the same for an app that turns motion off: data-motion="off" */
12084
+ [data-motion="off"] .tf-live-dot,
12085
+ [data-motion="off"] .tf-map-bubble.pulse::after,
12086
+ [data-motion="off"] .tf-map-pin.pulse::after,
12087
+ [data-motion="off"] .tf-livefeed-row.fresh {
12088
+ animation: none;
12089
+ }
11853
12090
  .tf-map-world {
11854
12091
  position: absolute;
11855
12092
  inset: 0;
@@ -12099,6 +12336,8 @@ button.tf-runs-main[aria-expanded]:hover {
12099
12336
  }
12100
12337
  .tf-togglegroup {
12101
12338
  display: inline-flex;
12339
+ flex-wrap: wrap;
12340
+ width: fit-content;
12102
12341
  align-items: center;
12103
12342
  gap: 4px;
12104
12343
  padding: 4px;
@@ -12761,19 +13000,19 @@ button.tf-runs-main[aria-expanded]:hover {
12761
13000
  @keyframes tf-sheet-left {
12762
13001
  from {
12763
13002
  opacity: 0;
12764
- transform: translateX(-24px);
13003
+ transform: translateX(calc(-24px * var(--motion-travel)));
12765
13004
  }
12766
13005
  }
12767
13006
  @keyframes tf-sheet-top {
12768
13007
  from {
12769
13008
  opacity: 0;
12770
- transform: translateY(-24px);
13009
+ transform: translateY(calc(-24px * var(--motion-travel)));
12771
13010
  }
12772
13011
  }
12773
13012
  @keyframes tf-sheet-bottom {
12774
13013
  from {
12775
13014
  opacity: 0;
12776
- transform: translateY(24px);
13015
+ transform: translateY(calc(24px * var(--motion-travel)));
12777
13016
  }
12778
13017
  }
12779
13018
 
@@ -13501,3 +13740,514 @@ textarea.tf-input,
13501
13740
  .tf-tabs {
13502
13741
  box-sizing: border-box;
13503
13742
  }
13743
+
13744
+ /* 08-glass.css */
13745
+ /* Glass: translucent surfaces for UI that floats over content (maps, 3D scenes, video, photos).
13746
+ *
13747
+ * A glass surface shows a blurred, colour-lifted view of what is behind it, with a light edge on
13748
+ * top. What sits on it reads the glass tokens too (inputs, chips, tab tracks, hover highlights,
13749
+ * nested cards), so the surface stays one piece of glass instead of opaque blocks on glass.
13750
+ *
13751
+ * - `.tf-glass` (the `surface="glass"` prop) makes one surface glass.
13752
+ * - `[data-surface="glass"]` (`<UIProvider surface="glass">`) makes every top-level surface and
13753
+ * every floating layer inside it glass. Surfaces nested in a surface stay clear instead of
13754
+ * stacking glass on glass; `.tf-solid` (`surface="solid"`) opts one surface out.
13755
+ *
13756
+ * Floating layers (menus, popovers, dialogs, drawers) use the stronger `--glass-strong` fill: they
13757
+ * carry more text. The fills are opaque enough for 4.5:1 body text over any backdrop; reduced
13758
+ * transparency, high contrast and forced colours fall back to solid surfaces.
13759
+ */
13760
+
13761
+ /* the surface itself */
13762
+ .tf-glass:not(.tf-solid),
13763
+ [data-surface="glass"]
13764
+ :is(
13765
+ .tf-panel,
13766
+ .tf-card,
13767
+ .tf-stat,
13768
+ .tf-dock,
13769
+ .tf-sidebar,
13770
+ .tf-map-legend,
13771
+ .tf-map-ctl,
13772
+ .tf-map-pop
13773
+ ):not(
13774
+ :is(
13775
+ .tf-panel,
13776
+ .tf-card,
13777
+ .tf-stat,
13778
+ .tf-dock,
13779
+ .tf-sidebar,
13780
+ .tf-popover,
13781
+ .tf-menu,
13782
+ .tf-hovercard,
13783
+ .tf-selectmenu-list,
13784
+ .tf-navmenu-panel,
13785
+ .tf-palette,
13786
+ .tf-modal,
13787
+ .tf-drawer,
13788
+ .tf-glass
13789
+ )
13790
+ *
13791
+ ):not(.tf-solid),
13792
+ [data-surface="glass"]
13793
+ :is(
13794
+ .tf-popover,
13795
+ .tf-menu:not(.tf-menu-static),
13796
+ .tf-hovercard,
13797
+ .tf-selectmenu-list,
13798
+ .tf-navmenu-panel,
13799
+ .tf-palette,
13800
+ .tf-modal,
13801
+ .tf-drawer
13802
+ ):not(.tf-solid) {
13803
+ --glass-fill: var(--glass);
13804
+ background:
13805
+ linear-gradient(to bottom, var(--glass-sheen), transparent 56px) border-box,
13806
+ linear-gradient(var(--glass-tinted), var(--glass-tinted)) border-box,
13807
+ var(--glass-fill);
13808
+ --glass-tinted: color-mix(in srgb, var(--tint, transparent) var(--glass-tint), transparent);
13809
+ -webkit-backdrop-filter: var(--glass-filter);
13810
+ backdrop-filter: var(--glass-filter);
13811
+ border-color: var(--glass-border);
13812
+ box-shadow:
13813
+ inset 0 1px 0 0 var(--glass-highlight),
13814
+ inset 0 0 0 1px var(--glass-border),
13815
+ var(--shadow-glass);
13816
+ color: var(--foreground);
13817
+ /* what sits on the glass */
13818
+ --surface: var(--glass-control);
13819
+ --secondary: var(--glass-control);
13820
+ --bg-100: var(--glass-control);
13821
+ --surface-sunken: var(--glass-well);
13822
+ --muted: var(--glass-well);
13823
+ --canvas: var(--glass-well);
13824
+ --bg-200: var(--glass-well);
13825
+ --accent: var(--glass-hover);
13826
+ --bg-300: var(--glass-hover);
13827
+ /* the faintest text steps up one shade: glass gives up some contrast to what is behind */
13828
+ --ink-subtle: var(--muted-foreground);
13829
+ --fg-300: var(--muted-foreground);
13830
+ }
13831
+
13832
+ /* floating layers carry more text: a stronger fill */
13833
+ :is(
13834
+ .tf-popover,
13835
+ .tf-menu,
13836
+ .tf-hovercard,
13837
+ .tf-selectmenu-list,
13838
+ .tf-navmenu-panel,
13839
+ .tf-palette,
13840
+ .tf-modal,
13841
+ .tf-drawer
13842
+ ).tf-glass,
13843
+ [data-surface="glass"]
13844
+ :is(
13845
+ .tf-popover,
13846
+ .tf-menu:not(.tf-menu-static),
13847
+ .tf-hovercard,
13848
+ .tf-selectmenu-list,
13849
+ .tf-navmenu-panel,
13850
+ .tf-palette,
13851
+ .tf-modal,
13852
+ .tf-drawer
13853
+ ):not(.tf-solid) {
13854
+ --glass-fill: var(--glass-strong);
13855
+ }
13856
+
13857
+ /* Controls floating on their own (not on a surface) become small glass pills: a search field or
13858
+ zoom buttons over a map */
13859
+ [data-surface="glass"]
13860
+ :is(
13861
+ .tf-search,
13862
+ .tf-seg,
13863
+ .tf-btngroup,
13864
+ .tf-iconbtn-soft,
13865
+ .tf-iconbtn-outline,
13866
+ .tf-btn-secondary,
13867
+ .tf-chip:not([aria-pressed="true"])
13868
+ ):not(
13869
+ :is(
13870
+ .tf-panel,
13871
+ .tf-card,
13872
+ .tf-dock,
13873
+ .tf-popover,
13874
+ .tf-menu,
13875
+ .tf-hovercard,
13876
+ .tf-selectmenu-list,
13877
+ .tf-navmenu-panel,
13878
+ .tf-palette,
13879
+ .tf-modal,
13880
+ .tf-drawer,
13881
+ .tf-glass,
13882
+ .tf-btngroup,
13883
+ .tf-map-ctl,
13884
+ .tf-topbar
13885
+ )
13886
+ *
13887
+ ):not(.tf-solid) {
13888
+ background:
13889
+ linear-gradient(to bottom, var(--glass-sheen), transparent) border-box,
13890
+ var(--glass);
13891
+ -webkit-backdrop-filter: var(--glass-filter);
13892
+ backdrop-filter: var(--glass-filter);
13893
+ box-shadow:
13894
+ inset 0 1px 0 0 var(--glass-highlight),
13895
+ inset 0 0 0 1px var(--glass-border),
13896
+ var(--shadow-glass);
13897
+ color: var(--foreground);
13898
+ --surface: var(--glass-control);
13899
+ --secondary: var(--glass-control);
13900
+ --surface-sunken: var(--glass-well);
13901
+ --bg-200: var(--glass-well);
13902
+ --accent: var(--glass-hover);
13903
+ --bg-300: var(--glass-hover);
13904
+ }
13905
+ [data-surface="glass"]
13906
+ :is(.tf-iconbtn-soft, .tf-iconbtn-outline, .tf-btn-secondary, .tf-chip):not(
13907
+ :is(.tf-panel, .tf-card, .tf-dock, .tf-popover, .tf-menu, .tf-modal, .tf-drawer, .tf-glass) *
13908
+ ):not(.tf-solid):not([aria-pressed="true"]):hover {
13909
+ background:
13910
+ linear-gradient(var(--glass-hover), var(--glass-hover)) border-box,
13911
+ var(--glass);
13912
+ }
13913
+
13914
+ /* AppShell on glass: the same layout as the solid shell, the frame turned to glass. Top bar and
13915
+ rail sit on it without a fill of their own, the sheet clears so the backdrop shows through,
13916
+ and everything drawn on the frame (nav, user, rail) reads the glass colours */
13917
+ [data-surface="glass"] .tf-shell {
13918
+ background:
13919
+ linear-gradient(to bottom, var(--glass-sheen), transparent 56px) border-box,
13920
+ var(--glass);
13921
+ -webkit-backdrop-filter: var(--glass-filter);
13922
+ backdrop-filter: var(--glass-filter);
13923
+ box-shadow:
13924
+ inset 0 1px 0 0 var(--glass-highlight),
13925
+ inset 0 0 0 1px var(--glass-border),
13926
+ var(--shadow-glass);
13927
+ --frame: var(--glass);
13928
+ --frame-hover: var(--glass-hover);
13929
+ --frame-active: var(--glass-hover);
13930
+ --frame-strong: var(--glass-control);
13931
+ --on-frame: var(--foreground);
13932
+ --on-frame-muted: var(--muted-foreground);
13933
+ }
13934
+ [data-surface="glass"] .tf-shell .tf-sheet {
13935
+ background: transparent;
13936
+ }
13937
+ [data-surface="glass"] .tf-shell .tf-topbar {
13938
+ background: transparent;
13939
+ }
13940
+
13941
+ /* switch knobs stay solid on glass */
13942
+ .tf-glass .tf-switch::after,
13943
+ [data-surface="glass"] .tf-switch::after {
13944
+ background: var(--card);
13945
+ }
13946
+
13947
+ /* Reduced transparency, more contrast, forced colours and browsers without backdrop blur switch
13948
+ the glass tokens themselves (see tokens.css). Forced colours drop shadows, so glass keeps an
13949
+ edge there. */
13950
+ @media (forced-colors: active) {
13951
+ .tf-glass,
13952
+ [data-surface="glass"]
13953
+ :is(
13954
+ .tf-panel,
13955
+ .tf-card,
13956
+ .tf-stat,
13957
+ .tf-dock,
13958
+ .tf-sidebar,
13959
+ .tf-map-legend,
13960
+ .tf-map-ctl,
13961
+ .tf-map-pop
13962
+ ) {
13963
+ outline: 1px solid CanvasText;
13964
+ outline-offset: -1px;
13965
+ }
13966
+ }
13967
+
13968
+ /* FloatingLayout: UI along the edges of a full-bleed background */
13969
+ .tf-floating {
13970
+ position: relative;
13971
+ overflow: hidden;
13972
+ isolation: isolate;
13973
+ border-radius: var(--radius-lg);
13974
+ background: var(--surface-sunken);
13975
+ container: tf-floating / inline-size;
13976
+ }
13977
+ .tf-floating-bg {
13978
+ position: absolute;
13979
+ inset: 0;
13980
+ }
13981
+ .tf-floating-bg > * {
13982
+ width: 100%;
13983
+ height: 100%;
13984
+ }
13985
+ .tf-floating-ui {
13986
+ position: absolute;
13987
+ inset: 0;
13988
+ z-index: 1;
13989
+ display: grid;
13990
+ grid-template-columns: minmax(0, 320px) minmax(0, 1fr) minmax(0, 340px);
13991
+ grid-template-rows: auto minmax(0, 1fr) auto;
13992
+ grid-template-areas:
13993
+ "top top top"
13994
+ "start . end"
13995
+ "bottom bottom bottom";
13996
+ gap: var(--space-4);
13997
+ padding: var(--space-4);
13998
+ overflow-y: auto;
13999
+ /* the background stays usable between the slots */
14000
+ pointer-events: none;
14001
+ }
14002
+ .tf-floating-ui > * {
14003
+ pointer-events: auto;
14004
+ min-width: 0;
14005
+ }
14006
+ .tf-floating-top {
14007
+ grid-area: top;
14008
+ display: flex;
14009
+ flex-wrap: wrap;
14010
+ align-items: center;
14011
+ gap: var(--space-2);
14012
+ }
14013
+ .tf-floating-start {
14014
+ grid-area: start;
14015
+ align-self: start;
14016
+ }
14017
+ .tf-floating-end {
14018
+ grid-area: end;
14019
+ align-self: start;
14020
+ }
14021
+ .tf-floating-bottom {
14022
+ grid-area: bottom;
14023
+ justify-self: center;
14024
+ }
14025
+ .tf-floating-ui > :is(.tf-floating-top, .tf-floating-bottom) {
14026
+ pointer-events: none;
14027
+ }
14028
+ .tf-floating-ui > :is(.tf-floating-top, .tf-floating-bottom) > * {
14029
+ pointer-events: auto;
14030
+ }
14031
+ @container tf-floating (max-width: 720px) {
14032
+ /* on a phone the layout grows with its slots and the background fills behind them */
14033
+ .tf-floating-ui {
14034
+ position: relative;
14035
+ min-height: inherit;
14036
+ overflow: visible;
14037
+ grid-template-columns: minmax(0, 1fr);
14038
+ grid-template-rows: auto auto minmax(120px, 1fr) auto auto;
14039
+ grid-template-areas:
14040
+ "top"
14041
+ "start"
14042
+ "."
14043
+ "end"
14044
+ "bottom";
14045
+ gap: var(--space-3);
14046
+ padding: var(--space-3);
14047
+ }
14048
+ }
14049
+
14050
+ /* 09-motion.css */
14051
+ /* Motion: leaving layers, reveal on scroll, page transitions, the expressive level.
14052
+ *
14053
+ * Levels come from `data-motion` (tokens.css): "standard", "expressive" (longer, staggered page
14054
+ * entrances, more travel) and "off". The reader's reduced-motion setting always means off. Only
14055
+ * transform, opacity and (expressive Halo) a short blur move; everything else stays put.
14056
+ */
14057
+
14058
+ /* ── Leaving: layers animate out for --dur-exit, then unmount (usePresence) ── */
14059
+ @keyframes tf-out {
14060
+ to {
14061
+ opacity: 0;
14062
+ translate: 0 calc(6px * var(--motion-travel));
14063
+ scale: 0.98;
14064
+ }
14065
+ }
14066
+ @keyframes tf-out-fade {
14067
+ to {
14068
+ opacity: 0;
14069
+ }
14070
+ }
14071
+ @keyframes tf-out-right {
14072
+ to {
14073
+ opacity: 0;
14074
+ translate: calc(40px * var(--motion-travel)) 0;
14075
+ }
14076
+ }
14077
+ @keyframes tf-out-left {
14078
+ to {
14079
+ opacity: 0;
14080
+ translate: calc(-40px * var(--motion-travel)) 0;
14081
+ }
14082
+ }
14083
+ @keyframes tf-out-up {
14084
+ to {
14085
+ opacity: 0;
14086
+ translate: 0 calc(-40px * var(--motion-travel));
14087
+ }
14088
+ }
14089
+ @keyframes tf-out-down {
14090
+ to {
14091
+ opacity: 0;
14092
+ translate: 0 calc(16px * var(--motion-travel));
14093
+ }
14094
+ }
14095
+ [data-closing] {
14096
+ pointer-events: none;
14097
+ }
14098
+ .tf-popover[data-closing],
14099
+ .tf-hovercard[data-closing],
14100
+ .tf-modal-wrap[data-closing] .tf-modal,
14101
+ .tf-overlay[data-closing] :is(.tf-modal, .tf-confirm),
14102
+ .tf-palette-wrap[data-closing] .tf-palette {
14103
+ animation: tf-out var(--dur-exit) var(--ease-in-out) forwards;
14104
+ }
14105
+ .tf-tooltip[data-closing],
14106
+ .tf-modal-wrap[data-closing] .tf-backdrop,
14107
+ .tf-overlay[data-closing] .tf-backdrop,
14108
+ .tf-palette-wrap[data-closing],
14109
+ .tf-drawer-wrap[data-closing] {
14110
+ animation: tf-out-fade var(--dur-exit) var(--ease-in-out) forwards;
14111
+ }
14112
+ .tf-drawer-wrap[data-closing] .tf-drawer {
14113
+ animation: tf-out-right var(--dur-exit) var(--ease-in-out) forwards;
14114
+ }
14115
+ .tf-drawer-wrap[data-closing] .tf-drawer.side-left {
14116
+ animation-name: tf-out-left;
14117
+ }
14118
+ .tf-drawer-wrap[data-closing] .tf-drawer.side-top {
14119
+ animation-name: tf-out-up;
14120
+ }
14121
+ .tf-drawer-wrap[data-closing] .tf-drawer.side-bottom,
14122
+ .tf-toast[data-closing] {
14123
+ animation: tf-out-down var(--dur-exit) var(--ease-in-out) forwards;
14124
+ }
14125
+
14126
+ /* ── Reveal: a Stagger below the fold waits until it scrolls into view ── */
14127
+ .tf-stagger[data-reveal="waiting"] > *,
14128
+ .tf-stagger[data-reveal="waiting"] > * * {
14129
+ animation-play-state: paused;
14130
+ }
14131
+
14132
+ /* ── Page transitions (startViewTransition, Next.js and React view transitions) ── */
14133
+ @keyframes tf-vt-out {
14134
+ to {
14135
+ opacity: 0;
14136
+ translate: 0 calc(-6px * var(--motion-travel));
14137
+ }
14138
+ }
14139
+ @keyframes tf-vt-in {
14140
+ from {
14141
+ opacity: 0;
14142
+ translate: 0 calc(10px * var(--motion-travel));
14143
+ }
14144
+ }
14145
+ :root::view-transition-old(root) {
14146
+ animation: tf-vt-out var(--dur-base) var(--ease-in-out) both;
14147
+ }
14148
+ :root::view-transition-new(root) {
14149
+ animation: tf-vt-in var(--dur-enter) var(--ease-out) both;
14150
+ }
14151
+
14152
+ /* ── Expressive: things come into focus as they rise (Halo; Pro keeps a blur of 0) ── */
14153
+ @keyframes tf-focus-in {
14154
+ from {
14155
+ filter: blur(var(--motion-blur));
14156
+ }
14157
+ to {
14158
+ filter: none;
14159
+ }
14160
+ }
14161
+ [data-motion="expressive"] :is(.tf-stagger > *, .tf-in) {
14162
+ animation-name: tf-rise, tf-focus-in;
14163
+ }
14164
+ [data-motion="expressive"] .tf-topbar {
14165
+ animation-name: tf-drop, tf-focus-in;
14166
+ }
14167
+
14168
+ /* 10-ambient.css */
14169
+ /* Ambient: a calm, living background for glass UI. Soft colour fields from the kit (pastels in
14170
+ * Halo, a faint aurora in Pro) drift slowly; a photo or video can take their place under a scrim.
14171
+ * The drift only moves (transform), stops with motion off and pauses off screen.
14172
+ */
14173
+ :where(:has(> .tf-ambient:not(.fixed))) {
14174
+ position: relative;
14175
+ isolation: isolate;
14176
+ }
14177
+ .tf-ambient {
14178
+ position: absolute;
14179
+ inset: 0;
14180
+ z-index: -1;
14181
+ overflow: hidden;
14182
+ background: var(--ambient-base);
14183
+ pointer-events: none;
14184
+ }
14185
+ .tf-ambient.fixed {
14186
+ position: fixed;
14187
+ }
14188
+ .tf-ambient-field {
14189
+ position: absolute;
14190
+ width: 75%;
14191
+ aspect-ratio: 1;
14192
+ border-radius: 50%;
14193
+ background: radial-gradient(closest-side, var(--field), transparent);
14194
+ animation: tf-ambient-drift 34s var(--ease-in-out) infinite alternate;
14195
+ }
14196
+ .tf-ambient-field:nth-child(1) {
14197
+ top: -30%;
14198
+ left: -15%;
14199
+ }
14200
+ .tf-ambient-field:nth-child(2) {
14201
+ top: -10%;
14202
+ right: -25%;
14203
+ animation-duration: 41s;
14204
+ animation-direction: alternate-reverse;
14205
+ }
14206
+ .tf-ambient-field:nth-child(3) {
14207
+ bottom: -35%;
14208
+ left: 10%;
14209
+ animation-duration: 47s;
14210
+ }
14211
+ .tf-ambient-field:nth-child(4) {
14212
+ bottom: -25%;
14213
+ right: -10%;
14214
+ animation-duration: 38s;
14215
+ animation-direction: alternate-reverse;
14216
+ }
14217
+ .tf-ambient-field:nth-child(n + 5) {
14218
+ top: 20%;
14219
+ left: 30%;
14220
+ width: 55%;
14221
+ animation-duration: 52s;
14222
+ }
14223
+ @keyframes tf-ambient-drift {
14224
+ to {
14225
+ translate: 12% 9%;
14226
+ scale: 1.15;
14227
+ }
14228
+ }
14229
+ .tf-ambient.still .tf-ambient-field,
14230
+ .tf-ambient[data-paused] .tf-ambient-field {
14231
+ animation-play-state: paused;
14232
+ }
14233
+ .tf-ambient-media {
14234
+ position: absolute;
14235
+ inset: 0;
14236
+ width: 100%;
14237
+ height: 100%;
14238
+ object-fit: cover;
14239
+ }
14240
+ .tf-ambient.media::after {
14241
+ content: "";
14242
+ position: absolute;
14243
+ inset: 0;
14244
+ background: var(--ambient-scrim);
14245
+ }
14246
+ @media (prefers-reduced-motion: reduce) {
14247
+ .tf-ambient-field {
14248
+ animation: none;
14249
+ }
14250
+ }
14251
+ [data-motion="off"] .tf-ambient-field {
14252
+ animation: none;
14253
+ }