@mlola-ui/tokens 1.2.1 → 1.3.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.
@@ -1,15 +1,8 @@
1
- /* Library recipes — owned by Mlola, no utility framework. */
2
-
3
- /* engine/base */
4
- /* `hidden` must win over any display a recipe sets (display: grid on a
5
- collapsible body would otherwise keep it open). */
1
+ /* @mlola-ui/engine recipes.css, generated. The sources keep the comments. */
6
2
  :where([hidden]:not([hidden="until-found"])) {
7
3
  display: none !important;
8
4
  }
9
5
 
10
- /* Icons sit on the text's optical center and keep their size inside flex
11
- rows; a squeezed row shrinks text, not glyphs. The same rules ship in
12
- @mlola-ui/icons/glyphs.css for apps that use the icons without the engine. */
13
6
  :where(.mlola-glyph) {
14
7
  display: inline-block;
15
8
  flex-shrink: 0;
@@ -29,7 +22,6 @@
29
22
  --mlola-glyph-optical-scale: 1.03;
30
23
  }
31
24
 
32
- /* The mark after a link to another site, which opens in a new tab. */
33
25
  .ml-link-external {
34
26
  margin-inline-start: var(--ml-space-1);
35
27
  vertical-align: -.125em;
@@ -52,7 +44,6 @@
52
44
  z-index: var(--ml-layer-overlay);
53
45
  }
54
46
 
55
- /* A glyph inside a link flows with the text instead of breaking the line. */
56
47
  .ml-link > .mlola-glyph {
57
48
  display: inline;
58
49
  margin-inline-start: 0.15em;
@@ -151,7 +142,6 @@
151
142
  gap: var(--ml-space-2);
152
143
  }
153
144
 
154
- /* A list of benefits, each after a check. Pricing tiers and feature rows share it. */
155
145
  .ml-check-list {
156
146
  margin: 0;
157
147
  padding: 0;
@@ -176,12 +166,10 @@
176
166
 
177
167
  .ml-section {
178
168
  padding-block: var(--ml-space-fluid-xl);
179
- /* A section is a grid or flex item in a page shell; without this, a wide
180
- table inside it widens the whole page instead of scrolling in place. */
169
+
181
170
  min-width: 0;
182
171
  }
183
172
 
184
- /* A section a link jumps to clears the sticky navigation bar. */
185
173
  .ml-section[id] {
186
174
  scroll-margin-block-start: calc(var(--ml-control-md) + var(--ml-space-6));
187
175
  }
@@ -189,8 +177,7 @@
189
177
  .ml-section-shell {
190
178
  width: min(100% - 2rem,72rem);
191
179
  margin-inline: auto;
192
- /* A grid or flex item defaults to min-width: auto, which lets a wide
193
- table push the whole page sideways. */
180
+
194
181
  min-width: 0;
195
182
  }
196
183
 
@@ -208,6 +195,7 @@
208
195
  }
209
196
 
210
197
  .ml-section-header-centered .ml-section-description {
198
+ width: 100%;
211
199
  margin-inline: auto;
212
200
  }
213
201
 
@@ -291,7 +279,6 @@
291
279
  }
292
280
  }
293
281
 
294
- /* Tablets keep two columns; only phones stack to one. */
295
282
  @media(max-width:48rem) {
296
283
  .ml-grid[data-columns="3"],
297
284
  .ml-grid[data-columns="4"],
@@ -380,7 +367,6 @@
380
367
  font-weight: 800;
381
368
  }
382
369
 
383
- /* A brand's own logo takes the mark's place: no chip, the text color, the mark's size. */
384
370
  .ml-brand-mark[data-kind="logo"] {
385
371
  background: none;
386
372
  color: var(--ml-text);
@@ -591,7 +577,6 @@
591
577
  font-size: var(--ml-type-base);
592
578
  }
593
579
 
594
- /* A full page: header, main and footer stacked on the page background. */
595
580
  .ml-page-shell {
596
581
  display: grid;
597
582
  grid-template-rows: auto 1fr auto;
@@ -600,13 +585,10 @@
600
585
  color: var(--ml-text);
601
586
  }
602
587
 
603
- /* The navigation, main and footer are grid items; they may be narrower than
604
- their widest content (a table that scrolls), so the page never does. */
605
588
  .ml-page-shell > * {
606
589
  min-width: 0;
607
590
  }
608
591
 
609
- /* components/accordion */
610
592
  .ml-accordion {
611
593
  width: 100%;
612
594
  }
@@ -675,7 +657,6 @@
675
657
  line-height: 1.55;
676
658
  }
677
659
 
678
- /* components/activity-heatmap */
679
660
  .ml-heatmap {
680
661
  --ml-heatmap-color: var(--ml-primary);
681
662
  --ml-heatmap-gap: var(--ml-space-1);
@@ -700,7 +681,6 @@
700
681
  --ml-heatmap-color: var(--ml-warning);
701
682
  }
702
683
 
703
- /* Weeks share the width, so the heatmap fills any container without scrolling. */
704
684
  .ml-heatmap-grid {
705
685
  display: grid;
706
686
  grid-template-columns: auto repeat(var(--ml-heatmap-weeks), minmax(0, 1fr));
@@ -772,8 +752,6 @@
772
752
  box-shadow: 0 0 0 2px var(--ml-focus);
773
753
  }
774
754
 
775
- /* A layer on <body> placed above its cell by script (useFloating), so no
776
- card or panel around the heatmap can cover or cut it. */
777
755
  .ml-heatmap-tooltip {
778
756
  position: fixed;
779
757
  z-index: var(--ml-layer-tooltip);
@@ -819,10 +797,6 @@
819
797
  font-variant-numeric: tabular-nums;
820
798
  }
821
799
 
822
- /* components/alert */
823
- /* A card like Card and Toast. The tone lives in one place, a solid icon
824
- mark, so a stack of alerts stays calm and each one still reads at a glance.
825
- `soft` tints the whole alert instead, for a banner across a page. */
826
800
  .ml-alert {
827
801
  --ml-alert-tone: var(--ml-text-muted);
828
802
  --ml-alert-on-tone: var(--ml-surface);
@@ -868,7 +842,6 @@
868
842
  --ml-alert-tone-text: var(--ml-danger-text);
869
843
  }
870
844
 
871
- /* soft: one even tint, a matching edge, and the title in the tone. */
872
845
  .ml-alert[data-variant="soft"] {
873
846
  border-color: color-mix(in oklab, var(--ml-alert-tone) 24%, transparent);
874
847
  background: color-mix(in oklab, var(--ml-alert-tone) 8%, var(--ml-surface));
@@ -897,7 +870,6 @@
897
870
  transition: opacity var(--ml-duration-fast) var(--ml-ease-standard), transform var(--ml-duration-normal) var(--ml-ease-standard);
898
871
  }
899
872
 
900
- /* A solid mark in the tone, centered on the first line of text. */
901
873
  .ml-alert-icon {
902
874
  position: relative;
903
875
  display: inline-grid;
@@ -929,8 +901,6 @@
929
901
  font-size: var(--ml-type-base);
930
902
  }
931
903
 
932
- /* Warnings and errors ask for attention as they arrive: a ring swells out
933
- of the mark and fades, twice. */
934
904
  .ml-alert[data-tone="warning"] .ml-alert-icon::after,
935
905
  .ml-alert[data-tone="danger"] .ml-alert-icon::after {
936
906
  position: absolute;
@@ -1018,7 +988,6 @@
1018
988
  }
1019
989
  }
1020
990
 
1021
- /* components/app-shell */
1022
991
  .ml-app-shell {
1023
992
  display: grid;
1024
993
  height: 100%;
@@ -1041,7 +1010,6 @@
1041
1010
  min-height: 0;
1042
1011
  }
1043
1012
 
1044
- /* Phones: the sidebar's track closes and its pane hides; the drawer takes over. */
1045
1013
  .ml-app-shell[data-narrow] .ml-app-shell-frame.ml-resizable[data-direction] {
1046
1014
  grid-template-columns: 0 0 minmax(0, 1fr);
1047
1015
  }
@@ -1226,7 +1194,6 @@
1226
1194
  opacity: 1;
1227
1195
  }
1228
1196
 
1229
- /* components/avatar */
1230
1197
  .ml-avatar-root {
1231
1198
  position: relative;
1232
1199
  display: inline-flex;
@@ -1336,7 +1303,6 @@
1336
1303
  font-weight: 600;
1337
1304
  }
1338
1305
 
1339
- /* components/badge */
1340
1306
  .ml-badge {
1341
1307
  display: inline-flex;
1342
1308
  align-items: center;
@@ -1377,7 +1343,6 @@
1377
1343
  font-size: var(--ml-type-sm);
1378
1344
  }
1379
1345
 
1380
- /* Tone picks the color role; variant decides how it is worn. */
1381
1346
  .ml-badge[data-tone="primary"] {
1382
1347
  --ml-badge-tone: var(--ml-primary);
1383
1348
  --ml-badge-text: var(--ml-primary-text);
@@ -1408,11 +1373,9 @@
1408
1373
  --ml-badge-on: var(--ml-danger-foreground);
1409
1374
  }
1410
1375
 
1411
- /* Soft is the default, so plain markup with only data-tone is tinted too. */
1412
1376
  .ml-badge:where([data-tone]:not([data-tone="neutral"])) {
1413
1377
  background: color-mix(in oklab,var(--ml-badge-tone) 12%,transparent);
1414
- /* The -text roles clear 4.5:1 on a plain surface; on a tint they lean toward
1415
- the body text just enough to keep clearing it at badge size. */
1378
+
1416
1379
  color: color-mix(in oklab,var(--ml-badge-text) 78%,var(--ml-text));
1417
1380
  }
1418
1381
 
@@ -1448,7 +1411,6 @@
1448
1411
  background: color-mix(in srgb,currentColor 12%,transparent);
1449
1412
  }
1450
1413
 
1451
- /* components/breadcrumb */
1452
1414
  .ml-breadcrumb {
1453
1415
  display: block;
1454
1416
  color: var(--ml-text-muted);
@@ -1472,7 +1434,6 @@
1472
1434
  gap: var(--ml-space-1-5);
1473
1435
  }
1474
1436
 
1475
- /* Links, in-app buttons and the current page share one shape; a long title shortens instead of breaking the bar. */
1476
1437
  .ml-breadcrumb-link,
1477
1438
  .ml-breadcrumb-page {
1478
1439
  display: inline-flex;
@@ -1489,9 +1450,6 @@
1489
1450
  text-decoration: none;
1490
1451
  }
1491
1452
 
1492
- /* A fingertip's target. The link clips its own text for the ellipsis, which
1493
- would clip the invisible target data-hit="expand" draws around it, so on a
1494
- touch screen the link itself grows instead, without moving the bar. */
1495
1453
  @media (pointer: coarse) {
1496
1454
  .ml-breadcrumb-link {
1497
1455
  padding-block: var(--ml-space-2);
@@ -1544,7 +1502,6 @@ button.ml-breadcrumb-link {
1544
1502
  }
1545
1503
  }
1546
1504
 
1547
- /* components/button */
1548
1505
  .ml-button {
1549
1506
  position: relative;
1550
1507
  display: inline-flex;
@@ -1606,16 +1563,6 @@ button.ml-breadcrumb-link {
1606
1563
  background: color-mix(in oklab,var(--ml-background-subtle) 100%,var(--ml-text) 6%);
1607
1564
  }
1608
1565
 
1609
- /* A toggle button that is on reads as filled ink, so its state is visible
1610
- without relying on color alone. */
1611
- .ml-button[data-variant="secondary"][aria-pressed="true"],
1612
- .ml-button[data-variant="subtle"][aria-pressed="true"],
1613
- .ml-button[data-variant="outline"][aria-pressed="true"] {
1614
- border-color: transparent;
1615
- background: var(--ml-text);
1616
- color: var(--ml-background);
1617
- }
1618
-
1619
1566
  .ml-button[data-variant="subtle"] {
1620
1567
  background: transparent;
1621
1568
  color: var(--ml-text);
@@ -1637,6 +1584,16 @@ button.ml-breadcrumb-link {
1637
1584
  background: color-mix(in oklab,var(--ml-surface) 100%,var(--ml-text) 4%);
1638
1585
  }
1639
1586
 
1587
+ .ml-button:is([data-variant="secondary"], [data-variant="subtle"], [data-variant="outline"])[aria-pressed="true"] {
1588
+ border-color: transparent;
1589
+ background: var(--ml-text);
1590
+ color: var(--ml-background);
1591
+ }
1592
+
1593
+ .ml-button:is([data-variant="secondary"], [data-variant="subtle"], [data-variant="outline"])[aria-pressed="true"]:hover {
1594
+ background: color-mix(in oklab, var(--ml-text) 86%, var(--ml-background));
1595
+ }
1596
+
1640
1597
  .ml-button[data-variant="danger"] {
1641
1598
  background: var(--ml-danger);
1642
1599
  color: var(--ml-danger-foreground);
@@ -1665,9 +1622,6 @@ button.ml-breadcrumb-link {
1665
1622
  color: var(--ml-primary-text);
1666
1623
  }
1667
1624
 
1668
- /* A link button is drawn as text, a line tall. On touch it keeps that look
1669
- and gains an invisible target around it, as a control marked
1670
- data-hit="expand" does, so a finger reaches it (WCAG 2.5.8). */
1671
1625
  @media (pointer: coarse) {
1672
1626
  .ml-button[data-variant="link"]::after {
1673
1627
  position: absolute;
@@ -1695,9 +1649,6 @@ button.ml-breadcrumb-link {
1695
1649
  padding: 0;
1696
1650
  }
1697
1651
 
1698
- /* A label too long for the space wraps onto balanced lines instead of
1699
- spilling out. The taller line height is taken back by the margins, so a
1700
- one-line button is exactly as tall as before. */
1701
1652
  .ml-button-label {
1702
1653
  display: inline-flex;
1703
1654
  align-items: center;
@@ -1719,16 +1670,11 @@ button.ml-breadcrumb-link {
1719
1670
  color: var(--ml-success-foreground);
1720
1671
  }
1721
1672
 
1722
- /* A busy button keeps its color: the spinner says it is working, and its
1723
- label ("Saving…") stays readable. Only a disabled button fades, so the two
1724
- never look alike. */
1725
1673
  .ml-button[data-loading] {
1726
1674
  pointer-events: none;
1727
1675
  opacity: 1;
1728
1676
  }
1729
1677
 
1730
- /* A shortcut inside a filled button takes the button's own ink: an outline
1731
- of the text color, no fill of its own, so it reads on any variant. */
1732
1678
  .ml-button .ml-kbd {
1733
1679
  border-color: color-mix(in oklab, currentColor 40%, transparent);
1734
1680
  background: transparent;
@@ -1736,12 +1682,10 @@ button.ml-breadcrumb-link {
1736
1682
  color: inherit;
1737
1683
  }
1738
1684
 
1739
- /* components/calendar */
1740
1685
  .ml-calendar {
1741
1686
  --ml-calendar-day: 2.25rem;
1742
1687
  display: inline-grid;
1743
- /* One column no wider than the calendar, so two months wrap into a stack
1744
- when the space is narrower than both side by side. */
1688
+
1745
1689
  grid-template-columns: minmax(0, 1fr);
1746
1690
  width: max-content;
1747
1691
  max-width: 100%;
@@ -1755,7 +1699,6 @@ button.ml-breadcrumb-link {
1755
1699
  gap: var(--ml-space-2);
1756
1700
  }
1757
1701
 
1758
- /* Each month has its own caption; the arrows sit on the outer ones. */
1759
1702
  .ml-calendar-caption {
1760
1703
  display: grid;
1761
1704
  grid-template-columns: 2rem minmax(0, 1fr) 2rem;
@@ -1828,7 +1771,6 @@ button.ml-breadcrumb-link {
1828
1771
  place-items: center;
1829
1772
  }
1830
1773
 
1831
- /* A range reads as one band: filled between its ends, rounded at them. */
1832
1774
  .ml-calendar-cell[data-range] {
1833
1775
  background: color-mix(in oklab, var(--ml-primary) 12%, transparent);
1834
1776
  }
@@ -1875,7 +1817,6 @@ button.ml-breadcrumb-link {
1875
1817
  color: var(--ml-text-faint);
1876
1818
  }
1877
1819
 
1878
- /* Today is marked with a dot, so it never competes with the selection. */
1879
1820
  .ml-calendar-day[data-today]::after {
1880
1821
  position: absolute;
1881
1822
  bottom: var(--ml-space-1);
@@ -1902,7 +1843,6 @@ button.ml-breadcrumb-link {
1902
1843
  background: var(--ml-primary-foreground);
1903
1844
  }
1904
1845
 
1905
- /* Out of reach for the range being drawn: still clickable, to start a new one. */
1906
1846
  .ml-calendar-day[data-unreachable] {
1907
1847
  color: var(--ml-text-faint);
1908
1848
  }
@@ -1920,7 +1860,6 @@ button.ml-breadcrumb-link {
1920
1860
  }
1921
1861
  }
1922
1862
 
1923
- /* components/card */
1924
1863
  .ml-card {
1925
1864
  color: var(--ml-text);
1926
1865
  border: var(--ml-border-width) solid var(--ml-border-subtle);
@@ -2007,7 +1946,6 @@ button.ml-breadcrumb-link {
2007
1946
  margin-block-start: var(--ml-space-6);
2008
1947
  }
2009
1948
 
2010
- /* components/carousel */
2011
1949
  .ml-carousel {
2012
1950
  --ml-carousel-gap: var(--ml-space-4);
2013
1951
  display: grid;
@@ -2020,8 +1958,6 @@ button.ml-breadcrumb-link {
2020
1958
  min-width: 0;
2021
1959
  }
2022
1960
 
2023
- /* A native scroll-snap track: swipe, trackpad and wheel all work, and each
2024
- slide takes an equal share of the width left after gaps and the peek. */
2025
1961
  .ml-carousel-track {
2026
1962
  position: relative;
2027
1963
  display: grid;
@@ -2054,9 +1990,6 @@ button.ml-breadcrumb-link {
2054
1990
  height: 100%;
2055
1991
  }
2056
1992
 
2057
- /* With a pointer that hovers, the arrows sit in a gutter beside the slides,
2058
- never over them. On touch the reader swipes, the arrows stay hidden until
2059
- focused, and the slides keep the full width. */
2060
1993
  @media (hover: hover) and (pointer: fine) {
2061
1994
  .ml-carousel[data-arrows] .ml-carousel-viewport {
2062
1995
  padding-inline: calc(var(--ml-control-md) + var(--ml-space-3));
@@ -2071,7 +2004,6 @@ button.ml-breadcrumb-link {
2071
2004
  }
2072
2005
  }
2073
2006
 
2074
- /* The arrows come forward when the reader is near. */
2075
2007
  .ml-carousel-arrow {
2076
2008
  position: absolute;
2077
2009
  top: 50%;
@@ -2120,7 +2052,6 @@ button.ml-breadcrumb-link {
2120
2052
  outline-offset: 2px;
2121
2053
  }
2122
2054
 
2123
- /* Touch screens swipe; the arrows would only cover the slide. */
2124
2055
  @media (hover: none) {
2125
2056
  .ml-carousel-arrow {
2126
2057
  display: none;
@@ -2148,7 +2079,6 @@ button.ml-breadcrumb-link {
2148
2079
  transition: width var(--ml-duration-normal) var(--ml-ease-spring), background-color var(--ml-duration-fast) var(--ml-ease-standard);
2149
2080
  }
2150
2081
 
2151
- /* A comfortable target around a small mark. */
2152
2082
  .ml-carousel-dot::before {
2153
2083
  position: absolute;
2154
2084
  inset: calc(-1 * var(--ml-space-2)) calc(-1 * var(--ml-space-0-5));
@@ -2164,7 +2094,6 @@ button.ml-breadcrumb-link {
2164
2094
  background: var(--ml-primary);
2165
2095
  }
2166
2096
 
2167
- /* While autoplay runs, the active dot fills over the slide's time. */
2168
2097
  .ml-carousel[data-playing] .ml-carousel-dot[aria-current="true"] {
2169
2098
  background: var(--ml-track);
2170
2099
  }
@@ -2204,7 +2133,6 @@ button.ml-breadcrumb-link {
2204
2133
  }
2205
2134
  }
2206
2135
 
2207
- /* components/checkbox */
2208
2136
  .ml-checkbox-field {
2209
2137
  display: grid;
2210
2138
  grid-template-columns: auto 1fr;
@@ -2271,7 +2199,6 @@ button.ml-breadcrumb-link {
2271
2199
  min-width: 0;
2272
2200
  }
2273
2201
 
2274
-
2275
2202
  .ml-checkbox-copy {
2276
2203
  display: grid;
2277
2204
  gap: var(--ml-space-1);
@@ -2287,8 +2214,6 @@ button.ml-breadcrumb-link {
2287
2214
  color: var(--ml-danger-text);
2288
2215
  }
2289
2216
 
2290
- /* components/color-picker */
2291
- /* A swatch and a value in any format. */
2292
2217
  .ml-color-picker {
2293
2218
  min-inline-size: 10rem;
2294
2219
  }
@@ -2301,7 +2226,6 @@ button.ml-breadcrumb-link {
2301
2226
  cursor: pointer;
2302
2227
  }
2303
2228
 
2304
- /* Swatches sit on a checkerboard, so transparency reads as transparency. */
2305
2229
  .ml-color-picker-swatch,
2306
2230
  .ml-color-picker-preset,
2307
2231
  .ml-color-picker-sample {
@@ -2336,12 +2260,11 @@ button.ml-breadcrumb-link {
2336
2260
  gap: var(--ml-space-3);
2337
2261
  }
2338
2262
 
2339
- /* Saturation left to right, brightness bottom to top, over the current hue. */
2340
2263
  .ml-color-picker-area {
2341
2264
  position: relative;
2342
2265
  height: 9.5rem;
2343
2266
  border-radius: var(--ml-radius-md);
2344
- /* audit-allow: color.literal — this is the color model itself: value falls to black, saturation to white. */
2267
+
2345
2268
  background: linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, transparent), var(--ml-color-hue);
2346
2269
  cursor: crosshair;
2347
2270
  outline: none;
@@ -2357,9 +2280,6 @@ button.ml-breadcrumb-link {
2357
2280
  display: grid;
2358
2281
  }
2359
2282
 
2360
- /* A slider is a row a fingertip can land on (24 px, as the Slider's), with
2361
- its thin stripe drawn inside. A finger moving up and down still scrolls;
2362
- sideways it moves the value. */
2363
2283
  .ml-color-picker-track {
2364
2284
  position: relative;
2365
2285
  height: var(--ml-space-6);
@@ -2501,7 +2421,7 @@ button.ml-breadcrumb-link {
2501
2421
 
2502
2422
  .ml-color-picker-sample {
2503
2423
  display: grid;
2504
- /* White or black, whichever reads better on the picked color (set by the component). */
2424
+
2505
2425
  color: var(--ml-color-ink);
2506
2426
  width: 2.25rem;
2507
2427
  height: 2.25rem;
@@ -2522,8 +2442,6 @@ button.ml-breadcrumb-link {
2522
2442
  color: var(--ml-success-text);
2523
2443
  }
2524
2444
 
2525
- /* components/combobox */
2526
- /* Wide enough to read a choice and type a search. */
2527
2445
  .ml-combobox {
2528
2446
  min-inline-size: 12rem;
2529
2447
  }
@@ -2534,7 +2452,6 @@ button.ml-breadcrumb-link {
2534
2452
  grid-template-columns: minmax(0, 1fr);
2535
2453
  }
2536
2454
 
2537
- /* The input keeps the field's surface; room at the end for clear and chevron. */
2538
2455
  .ml-combobox-input.ml-combobox-input {
2539
2456
  padding-inline-end: var(--ml-space-9);
2540
2457
  }
@@ -2592,9 +2509,6 @@ button.ml-breadcrumb-link {
2592
2509
  color: var(--ml-text);
2593
2510
  }
2594
2511
 
2595
- /* The list: the same floating surface as Select's, a layer on <body>
2596
- placed beside the field by script (useFloating), so no card, panel or
2597
- scroll area around the field can cover or cut it. */
2598
2512
  .ml-combobox-popover {
2599
2513
  position: fixed;
2600
2514
  z-index: var(--ml-layer-popover);
@@ -2602,8 +2516,7 @@ button.ml-breadcrumb-link {
2602
2516
  left: 0;
2603
2517
  display: flex;
2604
2518
  flex-direction: column;
2605
- /* No taller than the room on the side it opened (--ml-floating-room, from
2606
- useFloating), so it fits beside its trigger, even with a phone's keyboard up. */
2519
+
2607
2520
  max-height: var(--ml-floating-room, none);
2608
2521
  padding: var(--ml-space-1);
2609
2522
  border: var(--ml-border-width) solid var(--ml-border-subtle);
@@ -2710,7 +2623,6 @@ button.ml-breadcrumb-link {
2710
2623
  font-size: var(--ml-type-sm);
2711
2624
  }
2712
2625
 
2713
- /* components/command-menu */
2714
2626
  .ml-command.ml-modal {
2715
2627
  top: clamp(var(--ml-space-8), 18vh, var(--ml-space-16));
2716
2628
  grid-template-rows: auto minmax(0, 1fr) auto;
@@ -2740,7 +2652,7 @@ button.ml-breadcrumb-link {
2740
2652
  flex: 1;
2741
2653
  min-width: 0;
2742
2654
  border: 0;
2743
- /* audit-allow: focus.removed — the search field is the open dialog's only field; its caret shows focus. */
2655
+
2744
2656
  outline: none;
2745
2657
  background: transparent;
2746
2658
  color: var(--ml-text);
@@ -2844,12 +2756,10 @@ button.ml-breadcrumb-link {
2844
2756
  gap: var(--ml-space-1);
2845
2757
  }
2846
2758
 
2847
- /* components/context-menu */
2848
2759
  .ml-context-menu-area {
2849
2760
  min-width: 0;
2850
2761
  }
2851
2762
 
2852
- /* The same floating surface as the dropdown menu, placed at the pointer. */
2853
2763
  .ml-context-menu {
2854
2764
  position: fixed;
2855
2765
  z-index: var(--ml-layer-toast);
@@ -2860,7 +2770,7 @@ button.ml-breadcrumb-link {
2860
2770
  border-radius: var(--ml-radius-lg);
2861
2771
  box-shadow: var(--ml-surface-highlight), var(--ml-shadow-lg);
2862
2772
  color: var(--ml-text);
2863
- /* audit-allow: focus.removed — a menu container, focused only by script (tabindex -1); its items carry the focus style. */
2773
+
2864
2774
  outline: none;
2865
2775
  background-color: color-mix(in oklab, var(--ml-surface-elevated) var(--ml-surface-alpha), transparent);
2866
2776
  background-image: var(--ml-surface-grain);
@@ -2934,15 +2844,10 @@ button.ml-breadcrumb-link {
2934
2844
  background: var(--ml-border-subtle);
2935
2845
  }
2936
2846
 
2937
- /* components/copy-button */
2938
- /* A copy the browser refused reads as a problem, not as a success. On a
2939
- filled button the label keeps its own ink; the spoken message carries it. */
2940
2847
  .ml-copy-button[data-status="error"]:not([data-variant="primary"], [data-variant="danger"]) {
2941
2848
  color: var(--ml-danger-text);
2942
2849
  }
2943
2850
 
2944
- /* components/date-picker */
2945
- /* Wide enough for a range in the reader's format. */
2946
2851
  .ml-date-picker {
2947
2852
  min-inline-size: 13rem;
2948
2853
  }
@@ -2952,8 +2857,6 @@ button.ml-breadcrumb-link {
2952
2857
  display: grid;
2953
2858
  }
2954
2859
 
2955
- /* The trigger wears the input's surface, so date fields line up with text fields.
2956
- The doubled class outranks the input's padding, keeping room for the clear button. */
2957
2860
  .ml-date-picker-trigger.ml-date-picker-trigger {
2958
2861
  padding-inline-end: var(--ml-space-8);
2959
2862
  }
@@ -3009,7 +2912,6 @@ button.ml-breadcrumb-link {
3009
2912
  outline-offset: -1px;
3010
2913
  }
3011
2914
 
3012
- /* The popover grows to the calendars it holds, never past the screen. */
3013
2915
  .ml-popover.ml-date-picker-popover {
3014
2916
  max-width: calc(100vw - 2 * var(--ml-space-4));
3015
2917
  }
@@ -3057,7 +2959,6 @@ button.ml-breadcrumb-link {
3057
2959
  outline-offset: -1px;
3058
2960
  }
3059
2961
 
3060
- /* Phones: presets become a row above the calendar. */
3061
2962
  @media (max-width: 40rem) {
3062
2963
  .ml-date-picker-panel[data-presets] {
3063
2964
  flex-direction: column;
@@ -3076,7 +2977,203 @@ button.ml-breadcrumb-link {
3076
2977
  }
3077
2978
  }
3078
2979
 
3079
- /* components/dropdown-menu */
2980
+ .ml-dial-field {
2981
+ --ml-dial-size: 10rem;
2982
+ --ml-dial-tone: var(--ml-primary-text);
2983
+ display: inline-grid;
2984
+ justify-items: center;
2985
+ align-content: start;
2986
+ gap: var(--ml-space-2);
2987
+ }
2988
+
2989
+ .ml-dial-field[data-size="sm"] {
2990
+ --ml-dial-size: 6rem;
2991
+ }
2992
+
2993
+ .ml-dial-field[data-size="lg"] {
2994
+ --ml-dial-size: 14rem;
2995
+ }
2996
+
2997
+ .ml-dial-field[data-tone="info"] {
2998
+ --ml-dial-tone: var(--ml-info-text);
2999
+ }
3000
+
3001
+ .ml-dial-field[data-tone="success"] {
3002
+ --ml-dial-tone: var(--ml-success-text);
3003
+ }
3004
+
3005
+ .ml-dial-field[data-tone="warning"] {
3006
+ --ml-dial-tone: var(--ml-warning-text);
3007
+ }
3008
+
3009
+ .ml-dial-field[data-tone="danger"] {
3010
+ --ml-dial-tone: var(--ml-danger-text);
3011
+ }
3012
+
3013
+ .ml-dial-label {
3014
+ font-size: var(--ml-type-sm);
3015
+ font-weight: 500;
3016
+ text-align: center;
3017
+ }
3018
+
3019
+ .ml-dial-caption {
3020
+ max-width: var(--ml-dial-size);
3021
+ color: var(--ml-text-muted);
3022
+ font-size: var(--ml-type-xs);
3023
+ line-height: var(--ml-leading-snug);
3024
+ text-align: center;
3025
+ }
3026
+
3027
+ .ml-dial {
3028
+ position: relative;
3029
+ width: var(--ml-dial-size);
3030
+ aspect-ratio: 1;
3031
+ border-radius: 50%;
3032
+ cursor: grab;
3033
+ -webkit-touch-callout: none;
3034
+ -webkit-user-select: none;
3035
+ user-select: none;
3036
+ }
3037
+
3038
+ .ml-dial[data-turning] {
3039
+ cursor: grabbing;
3040
+ }
3041
+
3042
+ .ml-dial:focus-visible {
3043
+ outline: 2px solid var(--ml-focus);
3044
+ outline-offset: 2px;
3045
+ }
3046
+
3047
+ .ml-dial-arc {
3048
+ position: absolute;
3049
+ inset: 0;
3050
+ width: 100%;
3051
+ height: 100%;
3052
+ overflow: visible;
3053
+ }
3054
+
3055
+ .ml-dial-ticks,
3056
+ .ml-dial-track,
3057
+ .ml-dial-range {
3058
+ fill: none;
3059
+ stroke-linecap: round;
3060
+ }
3061
+
3062
+ .ml-dial-ticks {
3063
+ stroke: var(--ml-border);
3064
+ stroke-width: 0.8;
3065
+ }
3066
+
3067
+ .ml-dial-field[data-size="sm"] .ml-dial-ticks {
3068
+ display: none;
3069
+ }
3070
+
3071
+ .ml-dial-track {
3072
+ stroke: var(--ml-track);
3073
+ stroke-width: 5;
3074
+ }
3075
+
3076
+ .ml-dial-range {
3077
+ stroke: var(--ml-dial-tone);
3078
+ stroke-width: 5;
3079
+ }
3080
+
3081
+ .ml-dial-knob {
3082
+ position: absolute;
3083
+ inset: 16%;
3084
+ border-radius: 50%;
3085
+ background:
3086
+ var(--ml-sheen),
3087
+ radial-gradient(circle at 50% 28%, color-mix(in oklab, var(--ml-surface-elevated) var(--ml-surface-alpha), transparent), color-mix(in oklab, var(--ml-background-subtle) var(--ml-surface-alpha), transparent) 78%);
3088
+ box-shadow:
3089
+ var(--ml-surface-highlight),
3090
+ inset 0 0 0 var(--ml-border-width) var(--ml-border-subtle),
3091
+ var(--ml-shadow-md);
3092
+ -webkit-backdrop-filter: var(--ml-surface-blur);
3093
+ backdrop-filter: var(--ml-surface-blur);
3094
+ rotate: var(--ml-dial-angle, 0deg);
3095
+ transition: rotate var(--ml-duration-normal) var(--ml-ease-spring);
3096
+ }
3097
+
3098
+ .ml-dial-knob::before {
3099
+ content: "";
3100
+ position: absolute;
3101
+ inset: 0;
3102
+ border-radius: inherit;
3103
+ background-image: var(--ml-surface-grain);
3104
+ background-size: 4px 4px;
3105
+ }
3106
+
3107
+ .ml-dial-knob::after {
3108
+ content: "";
3109
+ position: absolute;
3110
+ top: 6%;
3111
+ left: 50%;
3112
+ width: 5%;
3113
+ height: 13%;
3114
+ border-radius: var(--ml-radius-pill);
3115
+ background: var(--ml-dial-tone);
3116
+ translate: -50% 0;
3117
+ }
3118
+
3119
+ .ml-dial[data-turning] .ml-dial-knob {
3120
+ transition: none;
3121
+ }
3122
+
3123
+ .ml-dial-readout {
3124
+ position: absolute;
3125
+ inset: 0;
3126
+ display: grid;
3127
+ place-items: center;
3128
+ pointer-events: none;
3129
+ }
3130
+
3131
+ .ml-dial-value {
3132
+ color: var(--ml-text);
3133
+ font-family: var(--ml-font-display);
3134
+ font-size: var(--ml-type-2xl);
3135
+ font-variant-numeric: tabular-nums;
3136
+ font-weight: 600;
3137
+ letter-spacing: -0.02em;
3138
+ line-height: 1;
3139
+ white-space: nowrap;
3140
+ }
3141
+
3142
+ .ml-dial-field[data-size="sm"] .ml-dial-value {
3143
+ font-size: var(--ml-type-md);
3144
+ }
3145
+
3146
+ .ml-dial-field[data-size="lg"] .ml-dial-value {
3147
+ font-size: var(--ml-type-display-sm);
3148
+ }
3149
+
3150
+ .ml-dial-field[data-disabled] .ml-dial {
3151
+ cursor: not-allowed;
3152
+ opacity: 0.48;
3153
+ }
3154
+
3155
+ @media (prefers-reduced-motion: reduce) {
3156
+ .ml-dial-knob {
3157
+ transition: none;
3158
+ }
3159
+ }
3160
+
3161
+ @media (forced-colors: active) {
3162
+ .ml-dial-track {
3163
+ stroke: GrayText;
3164
+ }
3165
+
3166
+ .ml-dial-range,
3167
+ .ml-dial-knob::after {
3168
+ stroke: Highlight;
3169
+ background: Highlight;
3170
+ }
3171
+
3172
+ .ml-dial-knob {
3173
+ border: 1px solid CanvasText;
3174
+ }
3175
+ }
3176
+
3080
3177
  .ml-dropdown {
3081
3178
  position: relative;
3082
3179
  display: inline-block;
@@ -3106,7 +3203,6 @@ button.ml-breadcrumb-link {
3106
3203
  background: color-mix(in oklab,var(--ml-surface) 100%,var(--ml-text) 4%);
3107
3204
  }
3108
3205
 
3109
- /* An icon-only trigger: a small quiet button, named by its label. */
3110
3206
  .ml-dropdown-trigger[data-variant="icon"] {
3111
3207
  min-height: var(--ml-control-sm);
3112
3208
  min-width: var(--ml-control-sm);
@@ -3135,11 +3231,6 @@ button.ml-breadcrumb-link {
3135
3231
  color: var(--ml-text-muted);
3136
3232
  }
3137
3233
 
3138
- /* A layer above the page, placed beside its trigger by script (React's
3139
- useFloating, the runtime's floatingLayer), so no card, panel or modal
3140
- around the trigger can cut it off. It sits above a modal it opens from.
3141
- In the top layer (the Popover API) the browser's own popover styles are
3142
- set back: no centering margin, no stretching inset, the theme's ink. */
3143
3234
  .ml-dropdown-menu {
3144
3235
  position: fixed;
3145
3236
  z-index: var(--ml-layer-popover);
@@ -3148,8 +3239,7 @@ button.ml-breadcrumb-link {
3148
3239
  left: 0;
3149
3240
  margin: 0;
3150
3241
  min-width: 14rem;
3151
- /* No taller than the room on the side it opened (--ml-floating-room, from
3152
- useFloating), so it fits beside its trigger, even with a phone's keyboard up. */
3242
+
3153
3243
  max-height: var(--ml-floating-room, none);
3154
3244
  overflow-y: auto;
3155
3245
  overscroll-behavior: contain;
@@ -3250,7 +3340,6 @@ button.ml-breadcrumb-link {
3250
3340
  color: var(--ml-text-muted);
3251
3341
  }
3252
3342
 
3253
- /* components/dropzone */
3254
3343
  .ml-dropzone-root {
3255
3344
  display: grid;
3256
3345
  gap: var(--ml-space-2);
@@ -3398,7 +3487,6 @@ button.ml-breadcrumb-link {
3398
3487
  transition: width var(--ml-duration-normal) var(--ml-ease-standard);
3399
3488
  }
3400
3489
 
3401
- /* components/empty-state */
3402
3490
  .ml-empty {
3403
3491
  display: grid;
3404
3492
  justify-items: center;
@@ -3452,7 +3540,6 @@ button.ml-breadcrumb-link {
3452
3540
  margin-block-start: var(--ml-space-2);
3453
3541
  }
3454
3542
 
3455
- /* components/hover-card */
3456
3543
  .ml-hover-card {
3457
3544
  position: fixed;
3458
3545
  z-index: var(--ml-layer-popover);
@@ -3487,13 +3574,12 @@ button.ml-breadcrumb-link {
3487
3574
  }
3488
3575
  }
3489
3576
 
3490
- /* components/input */
3491
3577
  .ml-input-field {
3492
3578
  display: grid;
3493
3579
  grid-template-columns: minmax(0, 1fr);
3494
3580
  gap: var(--ml-space-1-5);
3495
3581
  width: 100%;
3496
- /* In a form row taller than itself, a field keeps its own height instead of stretching its label and control apart. */
3582
+
3497
3583
  align-content: start;
3498
3584
  }
3499
3585
 
@@ -3515,9 +3601,6 @@ button.ml-breadcrumb-link {
3515
3601
  transition: border-color var(--ml-duration-fast),box-shadow var(--ml-duration-fast);
3516
3602
  }
3517
3603
 
3518
- /* A single-line field is exactly its size's control height, like the button
3519
- beside it: the height comes from density, and the text sits centered in
3520
- it, instead of padding around the text setting a height of its own. */
3521
3604
  .ml-input {
3522
3605
  padding-block: 0;
3523
3606
  }
@@ -3612,7 +3695,6 @@ button.ml-breadcrumb-link {
3612
3695
  font-size: var(--ml-type-md);
3613
3696
  }
3614
3697
 
3615
- /* A field that is a button or a group says it is invalid with data-invalid: aria-invalid belongs to inputs. */
3616
3698
  .ml-input-field[data-invalid] .ml-input,
3617
3699
  .ml-input[aria-invalid="true"],
3618
3700
  .ml-input[data-invalid] {
@@ -3655,14 +3737,12 @@ button.ml-breadcrumb-link {
3655
3737
  pointer-events: none;
3656
3738
  }
3657
3739
 
3658
- /* A text affix ("https://", a domain, a unit) reads at the size of what is typed beside it. */
3659
3740
  .ml-input-leading,
3660
3741
  .ml-input-trailing {
3661
3742
  font-size: var(--ml-type-base);
3662
3743
  line-height: var(--ml-leading-snug);
3663
3744
  }
3664
3745
 
3665
- /* components/kbd */
3666
3746
  .ml-kbd {
3667
3747
  display: inline-grid;
3668
3748
  min-width: 1.5em;
@@ -3688,7 +3768,6 @@ button.ml-breadcrumb-link {
3688
3768
  font: inherit;
3689
3769
  }
3690
3770
 
3691
- /* components/modal */
3692
3771
  .ml-modal-overlay {
3693
3772
  position: fixed;
3694
3773
  inset: 0;
@@ -3791,13 +3870,10 @@ button.ml-breadcrumb-link {
3791
3870
  padding: var(--ml-space-4) var(--ml-space-6) var(--ml-space-6);
3792
3871
  }
3793
3872
 
3794
- /* components/number-input */
3795
- /* A number and its steppers, never squeezed below readable. */
3796
3873
  .ml-number-input {
3797
3874
  min-inline-size: 9rem;
3798
3875
  }
3799
3876
 
3800
- /* The input's surface, with the steppers inside its right edge. */
3801
3877
  .ml-number-input-control {
3802
3878
  display: flex;
3803
3879
  min-height: var(--ml-control-md);
@@ -3830,7 +3906,7 @@ button.ml-breadcrumb-link {
3830
3906
  .ml-number-input-field {
3831
3907
  flex: 1;
3832
3908
  min-width: 0;
3833
- /* The control's height comes from density, like a text field's. */
3909
+
3834
3910
  padding: 0 var(--ml-space-3);
3835
3911
  border: 0;
3836
3912
  background: transparent;
@@ -3851,9 +3927,6 @@ button.ml-breadcrumb-link {
3851
3927
  border-inline-start: var(--ml-border-width) solid var(--ml-border-subtle);
3852
3928
  }
3853
3929
 
3854
- /* Square at any height: where touch sizing makes the field taller, the steps
3855
- grow with it instead of stretching into tall slivers. They sit inside the
3856
- control's border, so their side is the height within it. */
3857
3930
  .ml-number-input-step {
3858
3931
  display: inline-grid;
3859
3932
  min-width: calc(var(--ml-control-md) - 2 * var(--ml-border-width));
@@ -3887,7 +3960,6 @@ button.ml-breadcrumb-link {
3887
3960
  opacity: 0.5;
3888
3961
  }
3889
3962
 
3890
- /* components/otp-input */
3891
3963
  .ml-otp {
3892
3964
  display: flex;
3893
3965
  max-width: 100%;
@@ -3895,7 +3967,6 @@ button.ml-breadcrumb-link {
3895
3967
  gap: var(--ml-space-2);
3896
3968
  }
3897
3969
 
3898
- /* Boxes give a little way on the narrowest phones instead of running past the edge. */
3899
3970
  .ml-otp-box {
3900
3971
  flex: 0 1 var(--ml-control-lg);
3901
3972
  width: var(--ml-control-lg);
@@ -3937,7 +4008,6 @@ button.ml-breadcrumb-link {
3937
4008
  background: var(--ml-border);
3938
4009
  }
3939
4010
 
3940
- /* components/pagination */
3941
4011
  .ml-pagination {
3942
4012
  display: flex;
3943
4013
  align-items: center;
@@ -4002,14 +4072,11 @@ button.ml-breadcrumb-link {
4002
4072
  color: var(--ml-text-muted);
4003
4073
  }
4004
4074
 
4005
- /* components/password-input */
4006
4075
  .ml-password-input {
4007
4076
  display: grid;
4008
4077
  gap: var(--ml-space-2);
4009
4078
  }
4010
4079
 
4011
- /* While the password shows, the toggle takes full ink: its state is seen,
4012
- not only announced. */
4013
4080
  .ml-password-input-toggle[aria-pressed="true"] {
4014
4081
  color: var(--ml-text);
4015
4082
  }
@@ -4030,8 +4097,6 @@ button.ml-breadcrumb-link {
4030
4097
  font-weight: 600;
4031
4098
  }
4032
4099
 
4033
- /* Three segments light up as the password grows. They are marks on the page,
4034
- so they take the -text roles, which clear 3:1 against it. */
4035
4100
  .ml-password-input-meter {
4036
4101
  display: grid;
4037
4102
  grid-template-columns: repeat(3, 1fr);
@@ -4078,7 +4143,6 @@ button.ml-breadcrumb-link {
4078
4143
  }
4079
4144
  }
4080
4145
 
4081
- /* components/popover */
4082
4146
  .ml-popover {
4083
4147
  position: fixed;
4084
4148
  z-index: var(--ml-layer-popover);
@@ -4086,8 +4150,7 @@ button.ml-breadcrumb-link {
4086
4150
  left: 0;
4087
4151
  min-width: 12rem;
4088
4152
  max-width: min(22rem, calc(100vw - 1rem));
4089
- /* No taller than the room on the side it opened (--ml-floating-room, from
4090
- useFloating), so it fits beside its trigger, even with a phone's keyboard up. */
4153
+
4091
4154
  max-height: var(--ml-floating-room, none);
4092
4155
  overflow-y: auto;
4093
4156
  overscroll-behavior: contain;
@@ -4109,7 +4172,6 @@ button.ml-breadcrumb-link {
4109
4172
  outline: none;
4110
4173
  }
4111
4174
 
4112
- /* It grows out of the side it opened from. */
4113
4175
  .ml-popover[data-side="bottom"] {
4114
4176
  --ml-popover-from: translateY(-4px);
4115
4177
  }
@@ -4133,7 +4195,6 @@ button.ml-breadcrumb-link {
4133
4195
  }
4134
4196
  }
4135
4197
 
4136
- /* components/priority-icon */
4137
4198
  .ml-priority-icon {
4138
4199
  --ml-priority-mark: var(--ml-danger-foreground);
4139
4200
  flex: none;
@@ -4153,7 +4214,6 @@ button.ml-breadcrumb-link {
4153
4214
  color: var(--ml-text);
4154
4215
  }
4155
4216
 
4156
- /* components/progress */
4157
4217
  .ml-progress-root {
4158
4218
  --ml-progress-tone: var(--ml-primary);
4159
4219
  --ml-progress-height: var(--ml-space-1-5);
@@ -4214,15 +4274,13 @@ button.ml-breadcrumb-link {
4214
4274
  box-shadow: inset 0 1px 1px color-mix(in oklab, var(--ml-text) 6%, transparent);
4215
4275
  }
4216
4276
 
4217
- /* The fill deepens towards its leading edge, so the bar reads as moving forward. */
4218
4277
  .ml-progress-fill {
4219
4278
  position: relative;
4220
4279
  height: 100%;
4221
4280
  overflow: hidden;
4222
4281
  border-radius: inherit;
4223
4282
  background: linear-gradient(90deg, color-mix(in oklab, var(--ml-progress-tone) 72%, var(--ml-surface)), var(--ml-progress-tone));
4224
- /* A steady chase toward each new value: frequent updates blend into one
4225
- motion instead of restarting a spring every time. */
4283
+
4226
4284
  transition: width var(--ml-duration-slow) var(--ml-ease-standard);
4227
4285
  animation: ml-progress-grow var(--ml-duration-reveal) var(--ml-ease-spring) both;
4228
4286
  }
@@ -4233,7 +4291,6 @@ button.ml-breadcrumb-link {
4233
4291
  }
4234
4292
  }
4235
4293
 
4236
- /* While work runs, a soft light sweeps along the fill. */
4237
4294
  .ml-progress-root[data-active] .ml-progress-fill::after {
4238
4295
  position: absolute;
4239
4296
  inset: 0;
@@ -4249,7 +4306,6 @@ button.ml-breadcrumb-link {
4249
4306
  }
4250
4307
  }
4251
4308
 
4252
- /* Done: one glow around the finished bar. */
4253
4309
  .ml-progress-root[data-state="complete"] .ml-progress-track {
4254
4310
  animation: ml-progress-done var(--ml-duration-reveal) var(--ml-ease-standard) var(--ml-duration-slow) both;
4255
4311
  }
@@ -4263,7 +4319,6 @@ button.ml-breadcrumb-link {
4263
4319
  }
4264
4320
  }
4265
4321
 
4266
- /* Unknown length: a segment glides across and loops. */
4267
4322
  .ml-progress-root[data-state="indeterminate"] .ml-progress-fill {
4268
4323
  position: absolute;
4269
4324
  inset-block: 0;
@@ -4416,7 +4471,6 @@ button.ml-breadcrumb-link {
4416
4471
  }
4417
4472
  }
4418
4473
 
4419
- /* components/radio-group */
4420
4474
  .ml-radio-item {
4421
4475
  display: grid;
4422
4476
  grid-template-columns: auto 1fr;
@@ -4533,7 +4587,6 @@ button.ml-breadcrumb-link {
4533
4587
  align-content: start;
4534
4588
  }
4535
4589
 
4536
- /* components/rating */
4537
4590
  .ml-rating {
4538
4591
  display: inline-flex;
4539
4592
  align-items: center;
@@ -4567,20 +4620,21 @@ button.ml-breadcrumb-link {
4567
4620
  font-variant-numeric: tabular-nums;
4568
4621
  }
4569
4622
 
4570
- /* components/resizable */
4571
- .ml-resizable {
4623
+ :where(.ml-resizable) {
4572
4624
  --ml-resizable-handle: var(--ml-space-2);
4625
+ }
4626
+
4627
+ .ml-resizable {
4573
4628
  display: grid;
4574
4629
  height: 100%;
4575
4630
  min-width: 0;
4576
4631
  min-height: 0;
4577
- /* A folded or zero-width pane keeps its content mounted; nothing of it may widen the page. */
4632
+
4578
4633
  overflow: clip;
4579
- /* The handle's layer stays inside the component. */
4634
+
4580
4635
  isolation: isolate;
4581
4636
  }
4582
4637
 
4583
- /* The anchored pane takes the size; the other takes the rest. */
4584
4638
  .ml-resizable[data-direction="horizontal"] {
4585
4639
  grid-template-columns: minmax(0, var(--ml-resizable-size)) var(--ml-resizable-handle) minmax(0, 1fr);
4586
4640
  }
@@ -4597,28 +4651,23 @@ button.ml-breadcrumb-link {
4597
4651
  grid-template-rows: minmax(0, 1fr) var(--ml-resizable-handle) minmax(0, var(--ml-resizable-size));
4598
4652
  }
4599
4653
 
4600
- /* Each pane is a containing block, so absolutely placed content (visually
4601
- hidden labels, popover anchors) is clipped with it instead of escaping. */
4602
4654
  .ml-resizable-pane {
4603
4655
  position: relative;
4604
- /* A pane's own layers (sticky headers, raised rows) stay inside it, so they
4605
- never paint over the handle's line or its grab area. */
4656
+
4606
4657
  isolation: isolate;
4607
4658
  min-width: 0;
4608
4659
  min-height: 0;
4609
4660
  overflow: auto;
4610
4661
  }
4611
4662
 
4612
- /* A folded pane keeps its content mounted but out of reach. */
4613
4663
  .ml-resizable-pane[data-folded] {
4614
4664
  visibility: hidden;
4615
4665
  overflow: clip;
4616
4666
  }
4617
4667
 
4618
- /* A hairline that thickens into a grip on hover, focus or drag. */
4619
4668
  .ml-resizable-handle {
4620
4669
  position: relative;
4621
- /* The line and its grab area are wider than a hairline handle, so they sit above both panes. */
4670
+
4622
4671
  z-index: var(--ml-layer-raised);
4623
4672
  display: grid;
4624
4673
  place-items: center;
@@ -4662,7 +4711,6 @@ button.ml-breadcrumb-link {
4662
4711
  outline: none;
4663
4712
  }
4664
4713
 
4665
- /* The grab area is wider than the line, so a 1px handle in an app shell is still easy to catch. */
4666
4714
  .ml-resizable-handle::after {
4667
4715
  position: absolute;
4668
4716
  inset: 0 calc(-1 * var(--ml-space-1));
@@ -4674,7 +4722,6 @@ button.ml-breadcrumb-link {
4674
4722
  inset: calc(-1 * var(--ml-space-1)) 0;
4675
4723
  }
4676
4724
 
4677
- /* A finger needs more: 24 px of grab area on a touch screen. */
4678
4725
  @media (pointer: coarse) {
4679
4726
  .ml-resizable-handle::after {
4680
4727
  inset: 0 calc(-1 * var(--ml-space-2));
@@ -4685,8 +4732,6 @@ button.ml-breadcrumb-link {
4685
4732
  }
4686
4733
  }
4687
4734
 
4688
- /* components/segmented-control */
4689
- /* In a narrow container the options wrap onto a second row instead of spilling out. */
4690
4735
  .ml-segmented-control {
4691
4736
  display: inline-flex;
4692
4737
  flex-wrap: wrap;
@@ -4699,8 +4744,6 @@ button.ml-breadcrumb-link {
4699
4744
  gap: var(--ml-space-0-5);
4700
4745
  }
4701
4746
 
4702
- /* Each option is a small control inside the track, so the whole control is
4703
- as tall as a medium one and sits level with the fields beside it. */
4704
4747
  .ml-segmented-control-item {
4705
4748
  min-height: var(--ml-control-sm);
4706
4749
  padding: 0 var(--ml-space-3);
@@ -4725,14 +4768,11 @@ button.ml-breadcrumb-link {
4725
4768
  box-shadow: var(--ml-shadow-sm);
4726
4769
  }
4727
4770
 
4728
- /* components/select */
4729
- /* minmax(0, 1fr) and min-width: 0 let a long value truncate instead of
4730
- widening the select past its container. */
4731
4771
  .ml-select-root {
4732
4772
  position: relative;
4733
4773
  display: grid;
4734
4774
  grid-template-columns: minmax(0, 1fr);
4735
- /* In a form row taller than itself, the trigger keeps a field's height instead of stretching. */
4775
+
4736
4776
  align-content: start;
4737
4777
  gap: var(--ml-space-1-5);
4738
4778
  width: 100%;
@@ -4752,14 +4792,14 @@ button.ml-breadcrumb-link {
4752
4792
  align-items: center;
4753
4793
  justify-content: space-between;
4754
4794
  gap: var(--ml-space-2);
4755
- /* As tall as its size's control height, level with an input or a button. */
4795
+
4756
4796
  padding: 0 var(--ml-space-3);
4757
4797
  color: var(--ml-text);
4758
4798
  background: var(--ml-surface);
4759
4799
  border: var(--ml-border-width) solid var(--ml-border);
4760
4800
  border-radius: var(--ml-radius-md);
4761
4801
  box-shadow: var(--ml-shadow-xs);
4762
- /* The same type as a text field, so a select and an input sit level in a row. */
4802
+
4763
4803
  font-family: var(--ml-font-sans);
4764
4804
  font-size: var(--ml-type-base);
4765
4805
  line-height: var(--ml-leading-snug);
@@ -4851,8 +4891,6 @@ button.ml-breadcrumb-link {
4851
4891
  color: var(--ml-text);
4852
4892
  }
4853
4893
 
4854
- /* In the top layer (the Popover API) the browser's own popover styles are
4855
- set back: no centering margin, no stretching inset, the theme's ink. */
4856
4894
  .ml-select-popover {
4857
4895
  position: fixed;
4858
4896
  z-index: var(--ml-layer-top);
@@ -4863,8 +4901,7 @@ button.ml-breadcrumb-link {
4863
4901
  color: var(--ml-text);
4864
4902
  display: flex;
4865
4903
  flex-direction: column;
4866
- /* No taller than the room on the side it opened (--ml-floating-room, from
4867
- useFloating), so it fits beside its trigger, even with a phone's keyboard up. */
4904
+
4868
4905
  max-height: var(--ml-floating-room, none);
4869
4906
  max-width: calc(100vw - var(--ml-space-4));
4870
4907
  padding: var(--ml-space-1);
@@ -4898,7 +4935,7 @@ button.ml-breadcrumb-link {
4898
4935
  border-radius: var(--ml-radius-sm);
4899
4936
  background: transparent;
4900
4937
  font-size: var(--ml-type-sm);
4901
- /* audit-allow: focus.removed — the search field inside an open listbox; its caret shows focus. */
4938
+
4902
4939
  outline: none;
4903
4940
  }
4904
4941
 
@@ -5008,7 +5045,6 @@ button.ml-breadcrumb-link {
5008
5045
  color: var(--ml-danger-text);
5009
5046
  }
5010
5047
 
5011
- /* components/sheet */
5012
5048
  .ml-sheet-overlay {
5013
5049
  position: fixed;
5014
5050
  inset: 0;
@@ -5045,8 +5081,6 @@ button.ml-breadcrumb-link {
5045
5081
  z-index: var(--ml-layer-overlay);
5046
5082
  }
5047
5083
 
5048
- /* The panel floats a hairline in from its edge, rounded like every other
5049
- surface, instead of being glued to the viewport. */
5050
5084
  .ml-sheet-panel {
5051
5085
  --ml-sheet-inset: var(--ml-space-2);
5052
5086
  --ml-sheet-radius: calc(var(--ml-radius-lg) + 2px);
@@ -5148,10 +5182,6 @@ button.ml-breadcrumb-link {
5148
5182
  padding: var(--ml-space-4) var(--ml-space-6) var(--ml-space-6);
5149
5183
  }
5150
5184
 
5151
- /* components/skeleton */
5152
- /* One light band sweeps the whole viewport, so every placeholder on the page
5153
- glints in unison instead of each shimmering at its own speed. Where fixed
5154
- backgrounds are ignored (iOS), each shape still shimmers on its own. */
5155
5185
  .ml-skeleton {
5156
5186
  --ml-skeleton-base: color-mix(in oklab, var(--ml-text) 8%, transparent);
5157
5187
  --ml-skeleton-glint: color-mix(in oklab, var(--ml-text) 3%, transparent);
@@ -5163,7 +5193,7 @@ button.ml-breadcrumb-link {
5163
5193
  var(--ml-skeleton-base) 60%
5164
5194
  )
5165
5195
  0 0 / 200vw 100% fixed;
5166
- /* Quick loads never flash a placeholder: it appears only after a beat. */
5196
+
5167
5197
  animation:
5168
5198
  ml-skeleton-appear 240ms var(--ml-ease-standard) 160ms both,
5169
5199
  ml-skeleton-sweep 1.6s linear infinite;
@@ -5193,8 +5223,6 @@ button.ml-breadcrumb-link {
5193
5223
  width: 100%;
5194
5224
  }
5195
5225
 
5196
- /* Each line is one line box of the surrounding text: a bar about the height
5197
- of lowercase letters, centered in 1lh. */
5198
5226
  .ml-skeleton-line {
5199
5227
  height: 0.62em;
5200
5228
  margin-block: calc((1lh - 0.62em) / 2);
@@ -5250,7 +5278,6 @@ button.ml-breadcrumb-link {
5250
5278
  }
5251
5279
  }
5252
5280
 
5253
- /* components/slider */
5254
5281
  .ml-slider-field {
5255
5282
  display: grid;
5256
5283
  gap: var(--ml-space-2-5);
@@ -5281,7 +5308,7 @@ button.ml-breadcrumb-link {
5281
5308
  width: 100%;
5282
5309
  align-items: center;
5283
5310
  min-height: var(--ml-space-6);
5284
- /* A finger scrolls the page up and down; sideways it moves the value. */
5311
+
5285
5312
  touch-action: pan-y;
5286
5313
  cursor: pointer;
5287
5314
  }
@@ -5330,8 +5357,7 @@ button.ml-breadcrumb-link {
5330
5357
  }
5331
5358
 
5332
5359
  .ml-slider-thumb:hover,
5333
- /* Focus lands on the slider (it carries role="slider"); the ring is drawn
5334
- on the thumb, where the eye already is. */
5360
+
5335
5361
  .ml-slider:focus-visible {
5336
5362
  outline: none;
5337
5363
  }
@@ -5341,10 +5367,6 @@ button.ml-breadcrumb-link {
5341
5367
  outline-offset: 2px;
5342
5368
  }
5343
5369
 
5344
- /* components/spinner */
5345
- /* A ring of the current text color with one bright arc. It is drawn from
5346
- currentColor alone, so it matches the label beside it in a button, a
5347
- toast or a line of text, in every theme and mode. */
5348
5370
  .ml-spinner {
5349
5371
  display: inline-block;
5350
5372
  flex: none;
@@ -5369,8 +5391,6 @@ button.ml-breadcrumb-link {
5369
5391
  border-width: 2.5px;
5370
5392
  }
5371
5393
 
5372
- /* Under forced colors the ring is the system's text color, and the arc is
5373
- a real border that stays visible. */
5374
5394
  @media (forced-colors: active) {
5375
5395
  .ml-spinner {
5376
5396
  border-color: CanvasText;
@@ -5378,8 +5398,6 @@ button.ml-breadcrumb-link {
5378
5398
  }
5379
5399
  }
5380
5400
 
5381
- /* components/status-icon */
5382
- /* Color carries the stage; the glyph carries it without color too. */
5383
5401
  .ml-status-icon {
5384
5402
  --ml-status-mark: var(--ml-surface);
5385
5403
  flex: none;
@@ -5399,9 +5417,8 @@ button.ml-breadcrumb-link {
5399
5417
  color: var(--ml-success-text);
5400
5418
  }
5401
5419
 
5402
- /* Done is a filled disc carrying its check in the foreground: a fill and its pair. */
5403
5420
  .ml-status-icon[data-status="done"] {
5404
- /* audit-allow: color.role — the disc is a fill; its mark takes --ml-primary-foreground. */
5421
+
5405
5422
  color: var(--ml-primary);
5406
5423
  --ml-status-mark: var(--ml-primary-foreground);
5407
5424
  }
@@ -5410,8 +5427,6 @@ button.ml-breadcrumb-link {
5410
5427
  color: var(--ml-text-faint);
5411
5428
  }
5412
5429
 
5413
- /* components/stepper */
5414
- /* The container, not the screen, decides when the row condenses. */
5415
5430
  .ml-stepper {
5416
5431
  --ml-stepper-marker: 1.75rem;
5417
5432
  container: ml-stepper / inline-size;
@@ -5441,8 +5456,6 @@ button.ml-breadcrumb-link {
5441
5456
  flex: none;
5442
5457
  }
5443
5458
 
5444
- /* Horizontal: the connector is a flex item after the step, level with the
5445
- marker's center, and fills once the step is done. */
5446
5459
  .ml-stepper[data-orientation="horizontal"] .ml-stepper-step:not(:last-child)::after {
5447
5460
  min-width: var(--ml-space-4);
5448
5461
  height: 2px;
@@ -5571,7 +5584,6 @@ button.ml-stepper-target:focus-visible {
5571
5584
  font-size: var(--ml-type-xs);
5572
5585
  }
5573
5586
 
5574
- /* Compact: one line with the current step and a meter. */
5575
5587
  .ml-stepper-summary {
5576
5588
  display: none;
5577
5589
  grid-template-columns: auto 1fr;
@@ -5603,10 +5615,8 @@ button.ml-stepper-target:focus-visible {
5603
5615
  }
5604
5616
  }
5605
5617
 
5606
- /* components/table */
5607
5618
  .ml-table-container {
5608
- /* The containing block for anything positioned inside, such as visually
5609
- hidden cell text, so it scrolls with the table instead of widening the page. */
5619
+
5610
5620
  position: relative;
5611
5621
  width: 100%;
5612
5622
  overflow-x: auto;
@@ -5616,7 +5626,6 @@ button.ml-stepper-target:focus-visible {
5616
5626
  box-shadow: var(--ml-shadow-xs);
5617
5627
  }
5618
5628
 
5619
- /* Focusable only while it scrolls; the ring sits inside, where it cannot be clipped. */
5620
5629
  .ml-table-container:focus-visible {
5621
5630
  outline: 2px solid var(--ml-focus);
5622
5631
  outline-offset: -2px;
@@ -5652,8 +5661,6 @@ button.ml-stepper-target:focus-visible {
5652
5661
  background: transparent;
5653
5662
  }
5654
5663
 
5655
- /* A header cell lines up with its column. Browsers center th by default, which
5656
- a table's own text-align does not reach. */
5657
5664
  .ml-table th,
5658
5665
  .ml-table-header th,
5659
5666
  .ml-table-head {
@@ -5773,7 +5780,6 @@ button.ml-stepper-target:focus-visible {
5773
5780
  color: var(--ml-text-faint);
5774
5781
  }
5775
5782
 
5776
- /* components/tabs */
5777
5783
  .ml-tabs {
5778
5784
  display: grid;
5779
5785
  grid-template-columns: minmax(0, 1fr);
@@ -5894,13 +5900,10 @@ button.ml-stepper-target:focus-visible {
5894
5900
  display: none;
5895
5901
  }
5896
5902
 
5897
- /* components/tag-input */
5898
- /* Room for a couple of tags beside the caret. */
5899
5903
  .ml-tag-input {
5900
5904
  min-inline-size: 14rem;
5901
5905
  }
5902
5906
 
5903
- /* The control wears the input's surface, with tags flowing before the caret. */
5904
5907
  .ml-tag-input-control {
5905
5908
  position: relative;
5906
5909
  display: flex;
@@ -5961,7 +5964,6 @@ button.ml-stepper-target:focus-visible {
5961
5964
  transition: background-color var(--ml-duration-fast), border-color var(--ml-duration-fast);
5962
5965
  }
5963
5966
 
5964
- /* Marked by Backspace or the arrows: the next Backspace removes it. */
5965
5967
  .ml-tag-input-tag[data-armed] {
5966
5968
  border-color: var(--ml-danger);
5967
5969
  background: color-mix(in oklab, var(--ml-danger) 12%, var(--ml-surface));
@@ -6016,9 +6018,6 @@ button.ml-stepper-target:focus-visible {
6016
6018
  font-variant-numeric: tabular-nums;
6017
6019
  }
6018
6020
 
6019
- /* components/textarea */
6020
- /* Shares its surface with Input (input.css); this file adds only growth and
6021
- the counter. */
6022
6021
  .ml-textarea {
6023
6022
  max-height: calc(var(--ml-textarea-max-rows, 12) * 1lh + 2 * var(--ml-space-2));
6024
6023
  field-sizing: content;
@@ -6037,13 +6036,10 @@ button.ml-stepper-target:focus-visible {
6037
6036
  color: var(--ml-danger-text);
6038
6037
  }
6039
6038
 
6040
- /* components/time-picker */
6041
- /* Room for hours, minutes and AM/PM without the field collapsing. */
6042
6039
  .ml-time-picker {
6043
6040
  min-inline-size: 8rem;
6044
6041
  }
6045
6042
 
6046
- /* The control wears the input's surface; its segments are the fields inside. */
6047
6043
  .ml-time-picker-control {
6048
6044
  display: flex;
6049
6045
  align-items: center;
@@ -6164,15 +6160,10 @@ button.ml-stepper-target:focus-visible {
6164
6160
  outline-offset: -1px;
6165
6161
  }
6166
6162
 
6167
- /* A typed time the rules refuse reads as an error until focus leaves and it snaps. */
6168
6163
  .ml-time-picker-control[data-unavailable] .ml-time-picker-segment {
6169
6164
  color: var(--ml-danger-text);
6170
6165
  }
6171
6166
 
6172
- /* components/timeline */
6173
- /* A rail of markers and a column of text. Each item draws the segment of
6174
- rail below its own marker, so the line always runs through the centers,
6175
- whatever each item holds, and fills in as the sequence progresses. */
6176
6167
  .ml-timeline {
6177
6168
  --ml-timeline-marker: 1.5rem;
6178
6169
  --ml-timeline-rail: 2px;
@@ -6206,7 +6197,6 @@ button.ml-stepper-target:focus-visible {
6206
6197
  content: "";
6207
6198
  }
6208
6199
 
6209
- /* Done: the rail is drawn in; from the current step it fades toward what is next. */
6210
6200
  .ml-timeline-item[data-status="complete"]:not(:last-child)::before {
6211
6201
  background: var(--ml-primary);
6212
6202
  }
@@ -6261,7 +6251,6 @@ button.ml-stepper-target:focus-visible {
6261
6251
  }
6262
6252
  }
6263
6253
 
6264
- /* Event tones for activity feeds; they win over the status fill. */
6265
6254
  .ml-timeline-marker[data-tone] {
6266
6255
  --ml-timeline-tone: var(--ml-text);
6267
6256
  --ml-timeline-tone-text: var(--ml-text);
@@ -6300,7 +6289,6 @@ button.ml-stepper-target:focus-visible {
6300
6289
  min-width: 0;
6301
6290
  }
6302
6291
 
6303
- /* The heading row is as tall as the marker, so a one-line title sits on its center. */
6304
6292
  .ml-timeline-heading {
6305
6293
  display: flex;
6306
6294
  flex-wrap: wrap;
@@ -6348,14 +6336,11 @@ button.ml-stepper-target:focus-visible {
6348
6336
  }
6349
6337
  }
6350
6338
 
6351
- /* components/toast */
6352
- /* The stack sits a set distance from the viewport edges. Apps with a sticky
6353
- header move top stacks below it with --ml-toaster-offset-top. */
6354
6339
  .ml-toaster {
6355
6340
  --ml-toaster-offset: var(--ml-space-4);
6356
6341
  position: fixed;
6357
6342
  z-index: var(--ml-layer-toast);
6358
- /* In the top layer (the Popover API, framework-free) the browser's own popover styles are set back. */
6343
+
6359
6344
  inset: auto;
6360
6345
  margin: 0;
6361
6346
  padding: 0;
@@ -6379,8 +6364,6 @@ button.ml-stepper-target:focus-visible {
6379
6364
  bottom: var(--ml-toaster-offset-bottom, var(--ml-toaster-offset));
6380
6365
  }
6381
6366
 
6382
- /* --ml-scroll-lock-offset is the padding a scroll lock adds, so a toast
6383
- stays put when an overlay opens. */
6384
6367
  .ml-toaster[data-position="top-right"],
6385
6368
  .ml-toaster[data-position="bottom-right"] {
6386
6369
  right: calc(var(--ml-toaster-offset) + var(--ml-scroll-lock-offset, 0px));
@@ -6392,7 +6375,6 @@ button.ml-stepper-target:focus-visible {
6392
6375
  translate: -50% 0;
6393
6376
  }
6394
6377
 
6395
- /* On a phone the stack spans the screen with even margins. */
6396
6378
  @media (max-width: 30rem) {
6397
6379
  .ml-toaster {
6398
6380
  left: var(--ml-toaster-offset);
@@ -6402,8 +6384,6 @@ button.ml-stepper-target:focus-visible {
6402
6384
  }
6403
6385
  }
6404
6386
 
6405
- /* Each toast lives in a slot that can fold to nothing, so when one leaves
6406
- the others glide into its place instead of jumping. */
6407
6387
  .ml-toast-slot {
6408
6388
  display: grid;
6409
6389
  grid-template-rows: 1fr;
@@ -6472,7 +6452,6 @@ button.ml-stepper-target:focus-visible {
6472
6452
  transition: none;
6473
6453
  }
6474
6454
 
6475
- /* With a description the icon and actions sit on the first line, not the middle. */
6476
6455
  .ml-toast[data-has-description] {
6477
6456
  align-items: flex-start;
6478
6457
  }
@@ -6575,7 +6554,6 @@ button.ml-stepper-target:focus-visible {
6575
6554
  }
6576
6555
  }
6577
6556
 
6578
- /* components/toggle */
6579
6557
  .ml-switch-field {
6580
6558
  display: inline-flex;
6581
6559
  align-items: center;
@@ -6685,16 +6663,11 @@ button.ml-stepper-target:focus-visible {
6685
6663
  color: var(--ml-text);
6686
6664
  }
6687
6665
 
6688
- /* components/tooltip */
6689
6666
  .ml-tooltip-root {
6690
6667
  position: relative;
6691
6668
  display: inline-flex;
6692
6669
  }
6693
6670
 
6694
- /* A layer above the page, placed beside its trigger by script (React's
6695
- useFloating, the runtime's floatingLayer), so no panel, card or scroll
6696
- area around the trigger can cover or cut it. In the top layer (the
6697
- Popover API) the browser's own popover styles are set back. */
6698
6671
  .ml-tooltip {
6699
6672
  position: fixed;
6700
6673
  z-index: var(--ml-layer-tooltip);
@@ -6712,7 +6685,7 @@ button.ml-stepper-target:focus-visible {
6712
6685
  font-size: var(--ml-type-xs);
6713
6686
  font-weight: 500;
6714
6687
  line-height: 1.4;
6715
- /* One line when it fits; a long tip wraps within the window instead of running past it. */
6688
+
6716
6689
  width: max-content;
6717
6690
  max-width: min(20rem, calc(100vw - 2 * var(--ml-space-2)));
6718
6691
  white-space: normal;
@@ -6728,7 +6701,6 @@ button.ml-stepper-target:focus-visible {
6728
6701
  transform: rotate(45deg);
6729
6702
  }
6730
6703
 
6731
- /* The arrow slides back by the tooltip's shift, so it keeps pointing at the trigger. */
6732
6704
  .ml-tooltip[data-side="top"] .ml-tooltip-arrow,
6733
6705
  .ml-tooltip:not([data-side]) .ml-tooltip-arrow {
6734
6706
  bottom: -.2rem;
@@ -6754,14 +6726,12 @@ button.ml-stepper-target:focus-visible {
6754
6726
  margin-top: calc(var(--ml-space-1) * -1 - var(--ml-floating-shift, 0px));
6755
6727
  }
6756
6728
 
6757
- /* components/tour */
6758
6729
  .ml-tour {
6759
6730
  position: fixed;
6760
6731
  inset: 0;
6761
6732
  z-index: var(--ml-layer-top);
6762
6733
  }
6763
6734
 
6764
- /* Without a target the whole page dims; with one, the spotlight casts the dimming. */
6765
6735
  .ml-tour-scrim {
6766
6736
  position: absolute;
6767
6737
  inset: 0;
@@ -6793,7 +6763,7 @@ button.ml-stepper-target:focus-visible {
6793
6763
  background: var(--ml-surface-elevated);
6794
6764
  box-shadow: var(--ml-shadow-xl);
6795
6765
  color: var(--ml-text);
6796
- /* audit-allow: focus.removed — the tour card is focused by script (tabindex -1); its buttons carry the focus style. */
6766
+
6797
6767
  outline: none;
6798
6768
  animation: ml-pop-in var(--ml-duration-fast) var(--ml-ease-spring);
6799
6769
  }
@@ -6902,7 +6872,6 @@ button.ml-stepper-target:focus-visible {
6902
6872
  }
6903
6873
  }
6904
6874
 
6905
- /* components/virtual-list */
6906
6875
  .ml-virtual-list {
6907
6876
  position: relative;
6908
6877
  overflow-y: auto;
@@ -6915,7 +6884,6 @@ button.ml-stepper-target:focus-visible {
6915
6884
  box-shadow: inset 0 0 0 2px var(--ml-focus);
6916
6885
  }
6917
6886
 
6918
- /* The full height of every row, so the scrollbar tells the truth. */
6919
6887
  .ml-virtual-list-space {
6920
6888
  position: relative;
6921
6889
  width: 100%;