@echelon-foundry/design-system 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/all.css CHANGED
@@ -632,6 +632,7 @@
632
632
  }
633
633
 
634
634
  :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) {
635
+ box-sizing: border-box;
635
636
  min-block-size: 2.75rem;
636
637
  inline-size: 100%;
637
638
  padding: 0.7rem 0.8rem;
@@ -639,6 +640,50 @@
639
640
  border-radius: var(--ef-primitive-radius-none, 0);
640
641
  background: var(--ef-color-surface-primary, #f2efe7);
641
642
  color: var(--ef-color-text-primary, #171a18);
643
+ font: inherit;
644
+ line-height: var(--ef-field-line-height, 1.6);
645
+ }
646
+
647
+ /*
648
+ * Equivalent single-line controls share one intrinsic block size
649
+ * (UI-LAYOUT-INTERACTION ULI-L02, L13): one line of field text plus the
650
+ * field padding and border. Native date/time and select boxes otherwise
651
+ * differ by several pixels from text inputs (Chromium: 51.97 vs 49.97 vs
652
+ * 46.38 px), which staggers peers in a row. Em-based, so it grows with
653
+ * the user's text size.
654
+ */
655
+ :where(
656
+ input:not([type]),
657
+ input:is([type="text"], [type="email"], [type="password"], [type="search"], [type="tel"], [type="url"], [type="number"],
658
+ [type="date"], [type="time"], [type="datetime-local"], [type="month"], [type="week"]),
659
+ select:not([multiple], [size])
660
+ ) {
661
+ block-size: var(--ef-field-control-block-size, calc(var(--ef-field-line-height, 1.6) * 1em + 1.4rem + 2px));
662
+ }
663
+
664
+ :where(input[type="date"], input[type="time"], input[type="datetime-local"], input[type="month"], input[type="week"])::-webkit-datetime-edit {
665
+ padding-block: 0;
666
+ }
667
+
668
+ /* A fieldset's default min-inline-size is min-content, which lets a long
669
+ legend or option widen a form past a narrow viewport (ULI-L05). */
670
+ :where(fieldset) {
671
+ min-inline-size: 0;
672
+ }
673
+
674
+ /* iOS temporal controls otherwise retain a native intrinsic width despite
675
+ the author's width limit. Keep the native picker, not its native box. */
676
+ :where(input[type="date"], input[type="time"], input[type="datetime-local"]) {
677
+ -webkit-appearance: none;
678
+ appearance: none;
679
+ box-sizing: border-box;
680
+ min-inline-size: 0;
681
+ max-inline-size: 100%;
682
+ }
683
+
684
+ :where(input[type="date"], input[type="time"], input[type="datetime-local"])::-webkit-date-and-time-value {
685
+ min-inline-size: 0;
686
+ text-align: start;
642
687
  }
643
688
 
644
689
  :where(:focus-visible) {
@@ -650,16 +695,29 @@
650
695
  .ef-stack { display: flex; flex-direction: column; gap: var(--ef-stack-gap, var(--ef-primitive-spacing-4, 1rem)); }
651
696
  .ef-cluster { display: flex; flex-wrap: wrap; gap: var(--ef-cluster-gap, var(--ef-primitive-spacing-3, 0.75rem)); align-items: center; }
652
697
  /* .ef-actions (catalog: button) is an application-layer row of related actions; it was styled only in marketing.css (forma#93 gap). */
653
- .ef-actions { display: flex; flex-wrap: wrap; gap: var(--ef-primitive-spacing-3, 0.75rem); align-items: center; }
698
+ /* min-inline-size: 0 lets an action row shrink inside a flex or grid parent
699
+ instead of being held at its widest button's word (ULI-L05). */
700
+ .ef-actions { display: flex; flex-wrap: wrap; gap: var(--ef-primitive-spacing-3, 0.75rem); align-items: center; min-inline-size: 0; }
654
701
  .ef-grid { display: grid; gap: var(--ef-grid-gap, var(--ef-primitive-spacing-4, 1rem)); grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--ef-grid-min, 16rem)), 1fr)); }
655
702
  .ef-section { padding-block: var(--ef-section-space, var(--ef-primitive-spacing-7, 3rem)); }
656
703
  .ef-surface { --ef-color-text-secondary: var(--ef-color-text-on-secondary-surface, #3a403c); --ef-color-text-muted: var(--ef-color-text-on-secondary-surface, #3a403c); --ef-surface-accent-text-color: var(--ef-color-text-primary, #171a18); min-inline-size: 0; padding: var(--ef-surface-space, var(--ef-primitive-spacing-5, 1.5rem)); background: var(--ef-color-surface-secondary, #e3e0d7); color: var(--ef-color-text-on-secondary-surface, #3a403c); border: 1px solid var(--ef-color-border-subtle, #e3e0d7); }
657
704
  .ef-surface :where(h1, h2, h3, h4, h5, h6, p) { color: inherit; }
658
705
 
659
- :where(h1, h2, h3, h4, h5, h6, p, a, label, legend, td, th) {
706
+ :where(h1, h2, h3, h4, h5, h6, p, a, label, legend, td, th, dt, dd, caption, figcaption) {
660
707
  overflow-wrap: anywhere;
661
708
  }
662
709
 
710
+ /* Action labels never split mid-word because their container allows it
711
+ (a link button inherits the rule above; a button inherits it from a
712
+ table cell). A single word wider than its container may still break
713
+ rather than overflow. Must follow the rule above (ULI-L06, L07). The
714
+ inline-size cap also clamps a flex item's automatic minimum, so a
715
+ button never pushes its row past its container (ULI-L05). */
716
+ :where(button, .ef-button) {
717
+ overflow-wrap: break-word;
718
+ max-inline-size: 100%;
719
+ }
720
+
663
721
  @media (prefers-reduced-motion: reduce) {
664
722
  *, *::before, *::after {
665
723
  scroll-behavior: auto !important;
@@ -774,9 +832,44 @@
774
832
  min-inline-size: 0;
775
833
  max-inline-size: 100%;
776
834
  grid-template-columns: minmax(0, 1fr);
835
+ /* A stretched field (a grid row taller than it) keeps its label and
836
+ control at the top instead of distributing the extra space between
837
+ rows, which would push the control below its peers (ULI-L01). */
838
+ align-content: start;
777
839
  gap: var(--ef-primitive-spacing-2, 0.5rem);
778
840
  }
779
841
 
842
+ /*
843
+ * Form row (ULI-L01, L03, L13). Peer fields share one set of rows: label,
844
+ * description, control and validation message. Each field spans the four
845
+ * rows through subgrid and places its parts explicitly, so every control
846
+ * in a visual row starts at the same top edge however long the labels,
847
+ * descriptions or messages of its neighbours are. Fields wrap to new rows
848
+ * by available width. Without subgrid support each field keeps its own
849
+ * grid (the previous behavior).
850
+ */
851
+ .ef-form-row {
852
+ --ef-form-row-min: 14rem;
853
+ display: grid;
854
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--ef-form-row-min)), 1fr));
855
+ column-gap: var(--ef-form-row-gap, var(--ef-primitive-spacing-4, 1rem));
856
+ row-gap: 0;
857
+ align-items: start;
858
+ min-inline-size: 0;
859
+ }
860
+
861
+ .ef-form-row > .ef-field {
862
+ grid-row: span 4;
863
+ grid-template-rows: subgrid;
864
+ row-gap: 0;
865
+ margin-block-end: var(--ef-form-row-gap, var(--ef-primitive-spacing-4, 1rem));
866
+ }
867
+
868
+ .ef-form-row > .ef-field > .ef-field__label { grid-row: 1; }
869
+ .ef-form-row > .ef-field > .ef-field__description { grid-row: 2; margin-block-start: var(--ef-primitive-spacing-2, 0.5rem); }
870
+ .ef-form-row > .ef-field > :not(.ef-field__label, .ef-field__description, .ef-validation-message) { grid-row: 3; margin-block-start: var(--ef-primitive-spacing-2, 0.5rem); }
871
+ .ef-form-row > .ef-field > .ef-validation-message { grid-row: 4; margin-block-start: var(--ef-primitive-spacing-2, 0.5rem); }
872
+
780
873
  .ef-field > :is(input[type="date"], input[type="time"], input[type="datetime-local"]) {
781
874
  min-inline-size: 0;
782
875
  inline-size: 100%;
@@ -815,6 +908,10 @@
815
908
  --ef-motion-perceptual-emphasis-duration: var(--ef-primitive-motion-duration-standard, 180ms);
816
909
  --ef-motion-perceptual-easing: var(--ef-primitive-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1));
817
910
 
911
+ /* Brief surface fade: gentle ignition, fast middle, gentle settling.
912
+ Monotonic and symmetric: no opacity overshoot or delayed activation. */
913
+ --ef-motion-fade-easing: cubic-bezier(0.65, 0, 0.35, 1);
914
+
818
915
  /* Constant-velocity cadence: repeated activity is parameterized by a
819
916
  named period and constant velocity, never by spring settling (MOT-016). */
820
917
  --ef-motion-cadence-period: 1600ms;
@@ -844,7 +941,7 @@
844
941
  --ef-motion-gravity-duration: 255ms;
845
942
  --ef-motion-state-duration: 154ms;
846
943
  --ef-motion-press-duration: 107ms;
847
- --ef-motion-exit-duration: 146ms;
944
+ --ef-motion-exit-duration: 214ms;
848
945
  --ef-motion-spring-easing: linear(0, 0.42 24%, 0.82 48%, 1.055 70%, 0.988 88%, 1);
849
946
  --ef-motion-damped-easing: cubic-bezier(0.2, 0.8, 0.2, 1);
850
947
  }
@@ -918,11 +1015,8 @@
918
1015
  calc(var(--ef-motion-inertia-duration) * 0.5),
919
1016
  160ms
920
1017
  );
921
- --ef-motion-exit-duration: clamp(
922
- 80ms,
923
- calc(var(--ef-motion-inertia-duration) * 0.68),
924
- 220ms
925
- );
1018
+ /* Compatibility token: reciprocal motion has one response time. */
1019
+ --ef-motion-exit-duration: var(--ef-motion-inertia-duration);
926
1020
  }
927
1021
  }
928
1022
 
@@ -1106,7 +1200,9 @@
1106
1200
 
1107
1201
  /* Select -------------------------------------------------------------- */
1108
1202
  .ef-select-field {
1203
+ min-inline-size: 0;
1109
1204
  display: grid;
1205
+ grid-template-columns: minmax(0, 1fr);
1110
1206
  gap: var(--ef-primitive-spacing-2, 0.5rem);
1111
1207
  max-inline-size: 100%;
1112
1208
  }
@@ -1114,12 +1210,19 @@
1114
1210
  .ef-select {
1115
1211
  --ef-motion-distance: 0.35;
1116
1212
  position: relative;
1213
+ min-inline-size: 0;
1117
1214
  display: grid;
1215
+ grid-template-columns: minmax(0, 1fr);
1118
1216
  align-items: center;
1119
1217
  max-inline-size: 100%;
1120
1218
  }
1121
1219
 
1122
1220
  .ef-select__input {
1221
+ overflow: hidden;
1222
+ text-overflow: ellipsis;
1223
+ white-space: nowrap;
1224
+ min-inline-size: 0;
1225
+ max-inline-size: 100%;
1123
1226
  inline-size: 100%;
1124
1227
  min-block-size: 2.75rem;
1125
1228
  padding-inline-end: 2.75rem;
@@ -1338,6 +1441,10 @@
1338
1441
 
1339
1442
  .ef-disclosure > summary {
1340
1443
  min-inline-size: 0;
1444
+ /* The open marker rotates 45deg; its rotated (empty) box corners would
1445
+ otherwise add a few pixels of page overflow at narrow widths (ULI-L05).
1446
+ Clipping does not affect the summary's own focus ring. */
1447
+ overflow-x: clip;
1341
1448
  display: flex;
1342
1449
  align-items: center;
1343
1450
  justify-content: space-between;
@@ -1391,35 +1498,19 @@
1391
1498
  overflow: auto;
1392
1499
  overscroll-behavior: contain;
1393
1500
  opacity: 0;
1394
- translate: 0 -0.35rem;
1395
- scale: 0.985;
1396
- transition:
1397
- opacity var(--ef-motion-perceptual-duration) var(--ef-motion-perceptual-easing),
1398
- translate var(--ef-motion-exit-duration) var(--ef-motion-damped-easing),
1399
- scale var(--ef-motion-exit-duration) var(--ef-motion-damped-easing),
1400
- display var(--ef-motion-exit-duration) allow-discrete,
1401
- overlay var(--ef-motion-exit-duration) allow-discrete;
1501
+ /* Fade at the resting geometry. Never translate or scale a visible exit. */
1502
+ transition: opacity var(--ef-motion-perceptual-duration) var(--ef-motion-fade-easing);
1402
1503
  }
1403
1504
 
1404
1505
  .ef-popover:popover-open,
1405
1506
  .ef-menu:popover-open {
1406
1507
  opacity: 1;
1407
- translate: 0 0;
1408
- scale: 1;
1409
- transition-duration:
1410
- var(--ef-motion-perceptual-duration),
1411
- var(--ef-motion-inertia-duration),
1412
- var(--ef-motion-inertia-duration),
1413
- var(--ef-motion-inertia-duration),
1414
- var(--ef-motion-inertia-duration);
1415
1508
  }
1416
1509
 
1417
1510
  @starting-style {
1418
1511
  .ef-popover:popover-open,
1419
1512
  .ef-menu:popover-open {
1420
1513
  opacity: 0;
1421
- translate: 0 -0.35rem;
1422
- scale: 0.985;
1423
1514
  }
1424
1515
  }
1425
1516
 
@@ -1471,9 +1562,9 @@
1471
1562
  * the hint stays inside the viewport; otherwise the browser's centered
1472
1563
  * top-layer popover placement remains readable and in view.
1473
1564
  *
1474
- * Motion: light perceived weight. Opacity is perceptual interpolation and
1475
- * a tiny origin-related displacement is light inertial; exit uses the
1476
- * shorter derived exit duration. Reduced motion removes the displacement.
1565
+ * Motion: reciprocal perceptual fades at unchanged resting geometry. The
1566
+ * top layer is removed only after opacity reaches zero where supported.
1567
+ * Reduced motion reaches the endpoint immediately.
1477
1568
  */
1478
1569
  .ef-tooltip {
1479
1570
  --ef-motion-distance: 0.2;
@@ -1492,7 +1583,6 @@
1492
1583
  }
1493
1584
 
1494
1585
  .ef-tooltip__surface {
1495
- --ef-tooltip-offset: 0.25rem;
1496
1586
  inline-size: max-content;
1497
1587
  max-inline-size: min(20rem, calc(100vw - 2rem));
1498
1588
  margin: 0;
@@ -1504,30 +1594,30 @@
1504
1594
  line-height: 1.4;
1505
1595
  overflow-wrap: anywhere;
1506
1596
  opacity: 0;
1507
- translate: 0 var(--ef-tooltip-offset);
1508
- transition:
1509
- opacity var(--ef-motion-perceptual-duration) var(--ef-motion-perceptual-easing),
1510
- translate var(--ef-motion-exit-duration) var(--ef-motion-damped-easing),
1511
- display var(--ef-motion-exit-duration) allow-discrete,
1512
- overlay var(--ef-motion-exit-duration) allow-discrete;
1597
+ transition: opacity var(--ef-motion-perceptual-duration) var(--ef-motion-fade-easing);
1513
1598
  }
1514
1599
 
1515
1600
  .ef-tooltip__surface:popover-open {
1516
1601
  opacity: 1;
1517
- translate: 0 0;
1518
- /* Entry settles over the light inertial duration; exit (above) uses the
1519
- shorter derived exit duration. Opacity stays perceptual both ways. */
1520
- transition-duration:
1521
- var(--ef-motion-perceptual-duration),
1522
- var(--ef-motion-inertia-duration),
1523
- var(--ef-motion-inertia-duration),
1524
- var(--ef-motion-inertia-duration);
1525
1602
  }
1526
1603
 
1527
1604
  @starting-style {
1528
1605
  .ef-tooltip__surface:popover-open {
1529
1606
  opacity: 0;
1530
- translate: 0 var(--ef-tooltip-offset);
1607
+ }
1608
+ }
1609
+
1610
+ /* Keep the top-layer placement and implicit anchor through the entire fade.
1611
+ Display retention alone can expose a relocated, translucent surface in
1612
+ engines without overlay retention. Those engines dismiss immediately. */
1613
+ @supports (overlay: auto) {
1614
+ .ef-popover,
1615
+ .ef-menu,
1616
+ .ef-tooltip__surface {
1617
+ transition:
1618
+ opacity var(--ef-motion-perceptual-duration) var(--ef-motion-fade-easing),
1619
+ display var(--ef-motion-perceptual-duration) allow-discrete,
1620
+ overlay var(--ef-motion-perceptual-duration) allow-discrete;
1531
1621
  }
1532
1622
  }
1533
1623
 
@@ -1550,9 +1640,8 @@
1550
1640
  }
1551
1641
 
1552
1642
  @media (prefers-reduced-motion: reduce) {
1553
- .ef-tooltip__surface,
1554
- .ef-tooltip__surface:popover-open {
1555
- translate: 0 0;
1643
+ .ef-tooltip__surface {
1644
+ transition-duration: 0.01ms;
1556
1645
  }
1557
1646
  }
1558
1647
 
@@ -1654,8 +1743,8 @@
1654
1743
 
1655
1744
  /* Dialog and flyout ---------------------------------------------------- */
1656
1745
  :where(.ef-dialog, .ef-flyout) {
1657
- --ef-overlay-motion-duration: var(--ef-motion-exit-duration);
1658
- --ef-overlay-spatial-easing: var(--ef-motion-damped-easing);
1746
+ --ef-overlay-motion-duration: var(--ef-motion-inertia-duration);
1747
+ --ef-overlay-spatial-easing: var(--ef-motion-spring-easing);
1659
1748
  padding: 0;
1660
1749
  border: 1px solid var(--ef-color-border-functional, #3a403c);
1661
1750
  background: var(--ef-color-surface-primary, #f2efe7);
@@ -1674,9 +1763,7 @@
1674
1763
  /* Dimming is non-spatial: perceptual interpolation, without mass. */
1675
1764
  transition:
1676
1765
  background var(--ef-motion-perceptual-duration) var(--ef-motion-perceptual-easing),
1677
- backdrop-filter var(--ef-motion-perceptual-duration) var(--ef-motion-perceptual-easing),
1678
- display var(--ef-overlay-motion-duration) allow-discrete,
1679
- overlay var(--ef-overlay-motion-duration) allow-discrete;
1766
+ backdrop-filter var(--ef-motion-perceptual-duration) var(--ef-motion-perceptual-easing);
1680
1767
  }
1681
1768
 
1682
1769
  :where(.ef-dialog, .ef-flyout)[open]::backdrop {
@@ -1693,9 +1780,7 @@
1693
1780
  scale: 0.985;
1694
1781
  transition:
1695
1782
  translate var(--ef-overlay-motion-duration) var(--ef-overlay-spatial-easing),
1696
- scale var(--ef-overlay-motion-duration) var(--ef-overlay-spatial-easing),
1697
- display var(--ef-overlay-motion-duration) allow-discrete,
1698
- overlay var(--ef-overlay-motion-duration) allow-discrete;
1783
+ scale var(--ef-overlay-motion-duration) var(--ef-overlay-spatial-easing);
1699
1784
  }
1700
1785
 
1701
1786
  .ef-dialog[open] {
@@ -1728,9 +1813,34 @@
1728
1813
  box-shadow: 0 0 3rem rgb(0 0 0 / 0.24);
1729
1814
  translate: var(--ef-flyout-closed-x) 0;
1730
1815
  transition:
1731
- translate var(--ef-overlay-motion-duration) var(--ef-overlay-spatial-easing),
1732
- display var(--ef-overlay-motion-duration) allow-discrete,
1733
- overlay var(--ef-overlay-motion-duration) allow-discrete;
1816
+ translate var(--ef-overlay-motion-duration) var(--ef-overlay-spatial-easing);
1817
+ }
1818
+
1819
+ /* Retain display only when overlay can retain viewport/top-layer geometry.
1820
+ Otherwise a closed dialog can animate inside a containing ancestor. */
1821
+ @supports (overlay: auto) {
1822
+ :where(.ef-dialog, .ef-flyout)::backdrop {
1823
+ transition:
1824
+ background var(--ef-motion-perceptual-duration) var(--ef-motion-perceptual-easing),
1825
+ backdrop-filter var(--ef-motion-perceptual-duration) var(--ef-motion-perceptual-easing),
1826
+ display var(--ef-overlay-motion-duration) allow-discrete,
1827
+ overlay var(--ef-overlay-motion-duration) allow-discrete;
1828
+ }
1829
+
1830
+ .ef-dialog {
1831
+ transition:
1832
+ translate var(--ef-overlay-motion-duration) var(--ef-overlay-spatial-easing),
1833
+ scale var(--ef-overlay-motion-duration) var(--ef-overlay-spatial-easing),
1834
+ display var(--ef-overlay-motion-duration) allow-discrete,
1835
+ overlay var(--ef-overlay-motion-duration) allow-discrete;
1836
+ }
1837
+
1838
+ .ef-flyout {
1839
+ transition:
1840
+ translate var(--ef-overlay-motion-duration) var(--ef-overlay-spatial-easing),
1841
+ display var(--ef-overlay-motion-duration) allow-discrete,
1842
+ overlay var(--ef-overlay-motion-duration) allow-discrete;
1843
+ }
1734
1844
  }
1735
1845
 
1736
1846
  .ef-flyout[data-ef-side="left"] {
@@ -1752,9 +1862,11 @@
1752
1862
  .ef-flyout[open] { translate: 0 0; }
1753
1863
 
1754
1864
  .ef-flyout__surface {
1865
+ min-inline-size: 0;
1755
1866
  min-block-size: 0;
1756
1867
  block-size: 100%;
1757
1868
  display: grid;
1869
+ grid-template-columns: minmax(0, 1fr);
1758
1870
  grid-template-rows: auto minmax(0, 1fr) auto;
1759
1871
  }
1760
1872
 
@@ -1784,6 +1896,7 @@
1784
1896
  }
1785
1897
 
1786
1898
  .ef-flyout__body {
1899
+ min-inline-size: 0;
1787
1900
  min-block-size: 0;
1788
1901
  overflow: auto;
1789
1902
  overscroll-behavior: contain;
@@ -1877,7 +1990,9 @@
1877
1990
  font-family: var(--ef-primitive-font-family-mono, ui-monospace, monospace);
1878
1991
  font-size: 0.6875rem;
1879
1992
  line-height: 1.2;
1880
- white-space: nowrap;
1993
+ /* Wraps inside its container rather than widening it (ULI-L05). */
1994
+ max-inline-size: 100%;
1995
+ overflow-wrap: anywhere;
1881
1996
  }
1882
1997
 
1883
1998
  .ef-status-lozenge::before {
@@ -1935,7 +2050,7 @@
1935
2050
 
1936
2051
  .ef-collection-toolbar__controls label,
1937
2052
  .ef-collection-toolbar__filters {
1938
- min-inline-size: 9rem;
2053
+ min-inline-size: min(9rem, 100%);
1939
2054
  font-size: 0.75rem;
1940
2055
  font-weight: 650;
1941
2056
  }
@@ -2229,8 +2344,10 @@
2229
2344
  /* Metrics and dashboards --------------------------------------------- */
2230
2345
  .ef-metric-card {
2231
2346
  display: grid;
2347
+ grid-template-columns: minmax(0, 1fr);
2232
2348
  gap: 0.5rem;
2233
2349
  min-inline-size: 0;
2350
+ overflow-wrap: anywhere;
2234
2351
  padding: 1.25rem;
2235
2352
  border: 1px solid var(--ef-color-border-functional, #3a403c);
2236
2353
  background: var(--ef-color-surface-primary, #f2efe7);
@@ -2573,6 +2690,8 @@
2573
2690
  }
2574
2691
 
2575
2692
  .ef-wizard__steps li {
2693
+ min-inline-size: 0;
2694
+ overflow-wrap: anywhere;
2576
2695
  display: flex;
2577
2696
  gap: 0.5rem;
2578
2697
  align-items: center;
@@ -2599,6 +2718,7 @@
2599
2718
  .ef-readiness-checklist > header h3 { margin: 0.5rem 0 0; }
2600
2719
  .ef-readiness-checklist__items { margin: 0; padding: 0; list-style: none; }
2601
2720
  .ef-readiness-checklist__items li {
2721
+ overflow-wrap: anywhere;
2602
2722
  display: grid;
2603
2723
  grid-template-columns: 2rem minmax(0, 1fr) auto;
2604
2724
  gap: 0.75rem;
@@ -2666,6 +2786,7 @@
2666
2786
  .ef-command-palette__group button {
2667
2787
  inline-size: 100%;
2668
2788
  display: flex;
2789
+ flex-wrap: wrap;
2669
2790
  justify-content: space-between;
2670
2791
  gap: 1rem;
2671
2792
  text-align: start;
@@ -2782,13 +2903,38 @@
2782
2903
  }
2783
2904
 
2784
2905
  .ef-record-header__main nav { font-size: 0.75rem; }
2906
+ /*
2907
+ * The status sits beside the record title and is centred on the title's
2908
+ * row, not on the type label and title together (ULI-L01, L07). The
2909
+ * identity block spans two subgrid rows (type, title); the status occupies
2910
+ * the title row. Narrow containers stack the status below.
2911
+ */
2912
+ .ef-record-header__main { container-type: inline-size; min-inline-size: 0; }
2785
2913
  .ef-record-header__title {
2786
- display: flex;
2787
- flex-wrap: wrap;
2788
- gap: 1rem;
2914
+ display: grid;
2915
+ grid-template-columns: minmax(0, auto) auto;
2916
+ grid-template-rows: auto auto;
2917
+ justify-content: start;
2918
+ column-gap: 1rem;
2919
+ row-gap: 0.5rem;
2789
2920
  align-items: center;
2790
2921
  margin-block: 0.75rem 0;
2791
2922
  }
2923
+ .ef-record-header__title > :first-child {
2924
+ display: grid;
2925
+ grid-row: 1 / span 2;
2926
+ grid-template-rows: subgrid;
2927
+ row-gap: 0;
2928
+ min-inline-size: 0;
2929
+ }
2930
+ .ef-record-header__title > :first-child > .ef-record-header__type { grid-row: 1; align-self: end; }
2931
+ .ef-record-header__title > :first-child > :is(h1, h2, h3, h4) { grid-row: 2; }
2932
+ .ef-record-header__title > :not(:first-child) { grid-row: 2; }
2933
+ @container (max-width: 24em) {
2934
+ .ef-record-header__title { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; }
2935
+ .ef-record-header__title > :first-child { grid-row: auto; }
2936
+ .ef-record-header__title > :not(:first-child) { grid-row: auto; justify-self: start; }
2937
+ }
2792
2938
  .ef-record-header__title h2 { margin: 0.15rem 0 0; }
2793
2939
  .ef-record-header__type {
2794
2940
  color: var(--ef-surface-accent-text-color, var(--ef-color-accent-secondary, #47756b));
@@ -2872,7 +3018,9 @@
2872
3018
  .ef-provenance-trail li {
2873
3019
  position: relative;
2874
3020
  display: grid;
3021
+ grid-template-columns: minmax(0, 1fr);
2875
3022
  gap: 0.2rem;
3023
+ overflow-wrap: anywhere;
2876
3024
  padding: 0.75rem 0 0.75rem 2rem;
2877
3025
  }
2878
3026
  .ef-provenance-trail li::before {
@@ -2932,6 +3080,7 @@
2932
3080
  /* Mobile action bar --------------------------------------------------- */
2933
3081
  .ef-mobile-action-bar {
2934
3082
  display: flex;
3083
+ flex-wrap: wrap;
2935
3084
  gap: 0.5rem;
2936
3085
  padding: 0.75rem;
2937
3086
  padding-block-end: max(0.75rem, env(safe-area-inset-bottom));
@@ -2940,7 +3089,7 @@
2940
3089
  backdrop-filter: blur(10px);
2941
3090
  }
2942
3091
 
2943
- .ef-mobile-action-bar > * { flex: 1 1 0; }
3092
+ .ef-mobile-action-bar > * { flex: 1 1 min(100%, 8rem); }
2944
3093
  .ef-mobile-action-bar__primary {
2945
3094
  background: var(--ef-color-surface-inverse, #202421);
2946
3095
  color: var(--ef-color-text-inverse, #f2efe7);
@@ -2973,7 +3122,9 @@
2973
3122
  .ef-operation-status,
2974
3123
  .ef-record-header,
2975
3124
  .ef-file-upload__queue li {
2976
- grid-template-columns: 1fr;
3125
+ /* minmax(0, …): a long word or wide font never holds the track wider
3126
+ than the screen (ULI-L05). */
3127
+ grid-template-columns: minmax(0, 1fr);
2977
3128
  }
2978
3129
 
2979
3130
  .ef-work-queue__item button,
@@ -3052,6 +3203,7 @@
3052
3203
  margin-block-end: 0.75rem;
3053
3204
  border: 1px solid var(--ef-color-border-functional, #3a403c);
3054
3205
  }
3206
+ .ef-data-grid tr { container-type: inline-size; }
3055
3207
  .ef-data-grid td {
3056
3208
  display: grid;
3057
3209
  grid-template-columns: minmax(6.5rem, 0.4fr) minmax(0, 1fr);
@@ -3065,13 +3217,19 @@
3065
3217
  font-weight: 650;
3066
3218
  }
3067
3219
  .ef-data-grid td[data-label="Actions"] > * { inline-size: 100%; }
3220
+ /* When enlarged text leaves no room for a label beside its value, the
3221
+ label stacks above the value instead of squeezing it to a few
3222
+ characters per line (ULI-L05). 16em is two readable columns. */
3223
+ @container (max-width: 16em) {
3224
+ .ef-data-grid td { grid-template-columns: minmax(0, 1fr); gap: 0.25rem; }
3225
+ }
3068
3226
 
3069
3227
  .ef-search__control { grid-template-columns: 1fr; }
3070
3228
  .ef-search__control input { border-inline-end: 1px solid var(--ef-color-border-functional, #3a403c); border-block-end: 0; }
3071
3229
 
3072
3230
  .ef-work-queue__header { display: grid; }
3073
3231
  .ef-diff-row,
3074
- .ef-key-value-list > div { grid-template-columns: 1fr; gap: 0.25rem; }
3232
+ .ef-key-value-list > div { grid-template-columns: minmax(0, 1fr); gap: 0.25rem; }
3075
3233
 
3076
3234
  .ef-status-lozenge-set { align-items: stretch; }
3077
3235
  .ef-status-lozenge-set .ef-status-lozenge { flex: 1 1 100%; justify-content: center; }
@@ -3313,7 +3471,7 @@
3313
3471
  .ef-composer__actions button { min-block-size: 2.75rem; }
3314
3472
  .ef-composer__submit { inline-size: 100%; }
3315
3473
 
3316
- .ef-turn { grid-template-columns: 1fr; }
3474
+ .ef-turn { grid-template-columns: minmax(0, 1fr); }
3317
3475
  .ef-turn__actions { grid-column: 1; }
3318
3476
  .ef-turn__actions button { inline-size: 100%; }
3319
3477
 
@@ -3596,6 +3754,7 @@
3596
3754
  --ef-color-text-muted: var(--ef-color-text-on-secondary-surface, #3a403c);
3597
3755
  --ef-surface-accent-text-color: var(--ef-color-text-primary, #171a18);
3598
3756
  display: inline-flex;
3757
+ max-inline-size: 100%;
3599
3758
  align-items: center;
3600
3759
  gap: var(--ef-primitive-spacing-2, 0.5rem);
3601
3760
  padding: 0.55rem 0.75rem;
@@ -3614,6 +3773,8 @@
3614
3773
  .ef-diagnostic-status__body {
3615
3774
  display: grid;
3616
3775
  gap: 0.1rem;
3776
+ min-inline-size: 0;
3777
+ overflow-wrap: anywhere;
3617
3778
  }
3618
3779
 
3619
3780
  .ef-diagnostic-status__body > span {
@@ -3752,7 +3913,7 @@
3752
3913
  .ef-security-state-matrix__grid dt { font-size:.75rem; font-weight:700; }
3753
3914
  .ef-security-state-matrix__grid dd { margin:0; font-size:clamp(1.4rem,4vw,2rem); font-weight:750; }
3754
3915
  .ef-security-blockers ul { display:grid; gap:.75rem; padding-inline-start:1.25rem; }
3755
- .ef-security-blockers li { padding-inline-start:.25rem; }
3916
+ .ef-security-blockers li { padding-inline-start:.25rem; min-inline-size:0; overflow-wrap:anywhere; }
3756
3917
  .ef-security-blockers li span { display:block; }
3757
3918
  .ef-security-evidence-chain ol { display:grid; grid-template-columns:repeat(auto-fit,minmax(9rem,1fr)); gap:.5rem; padding:0; list-style:none; }
3758
3919
  .ef-security-evidence-chain li { padding:.75rem; border:1px solid var(--ef-color-border-functional,#3a403c); overflow-wrap:anywhere; }
@@ -3766,7 +3927,7 @@
3766
3927
  .ef-invariant-result dl { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(10rem,100%),1fr)); gap:.75rem; }
3767
3928
  .ef-invariant-result dt { color:var(--ef-color-text-secondary,#686d68); font-size:.75rem; font-weight:700; } .ef-invariant-result dd { margin:0; overflow-wrap:anywhere; }
3768
3929
  :where(.ef-evidence-age,.ef-security-trend) ul { display:grid; gap:.5rem; margin:0; padding:0; list-style:none; }
3769
- :where(.ef-evidence-age,.ef-security-trend) li { --ef-color-text-secondary: var(--ef-color-text-on-secondary-surface, #3a403c); --ef-color-text-muted: var(--ef-color-text-on-secondary-surface, #3a403c); --ef-surface-accent-text-color: var(--ef-color-text-primary, #171a18); display:grid; gap:.2rem; border-inline-start:.3rem solid currentColor; padding:.65rem .75rem; background:var(--ef-color-surface-secondary,#e3e0d7); }
3930
+ :where(.ef-evidence-age,.ef-security-trend) li { grid-template-columns:minmax(0,1fr); min-inline-size:0; overflow-wrap:anywhere; --ef-color-text-secondary: var(--ef-color-text-on-secondary-surface, #3a403c); --ef-color-text-muted: var(--ef-color-text-on-secondary-surface, #3a403c); --ef-surface-accent-text-color: var(--ef-color-text-primary, #171a18); display:grid; gap:.2rem; border-inline-start:.3rem solid currentColor; padding:.65rem .75rem; background:var(--ef-color-surface-secondary,#e3e0d7); }
3770
3931
  :where(.ef-evidence-age,.ef-security-trend) li span { overflow-wrap:anywhere; }
3771
3932
 
3772
3933
  /* Composition primitives ---------------------------------------------- */
@@ -3976,7 +4137,10 @@
3976
4137
  }
3977
4138
 
3978
4139
  @media (max-width: 48rem) {
3979
- .ef-split-pane[data-ef-resizable] { grid-template-columns: minmax(0, 1fr); }
4140
+ /* Includes the collapsed state, whose track list would otherwise keep
4141
+ three columns on a phone and squeeze the secondary pane (ULI-L05). */
4142
+ .ef-split-pane[data-ef-resizable],
4143
+ .ef-split-pane[data-ef-resizable][data-ef-collapsed] { grid-template-columns: minmax(0, 1fr); }
3980
4144
  .ef-split-pane__separator { display: none; }
3981
4145
  }
3982
4146
 
@@ -6282,7 +6446,10 @@
6282
6446
 
6283
6447
  /* Choice cards -------------------------------------------------------- */
6284
6448
  .ef-choice-group {
6285
- display: grid;
6449
+ /* A column flex layout lets wrapped cards determine their own height.
6450
+ WebKit can retain stale grid row heights on fieldset viewport reflow. */
6451
+ display: flex;
6452
+ flex-direction: column;
6286
6453
  gap: var(--ef-primitive-spacing-2, 0.5rem);
6287
6454
  }
6288
6455
 
@@ -6782,6 +6949,7 @@
6782
6949
 
6783
6950
  .ef-semantic-differential__endpoints {
6784
6951
  display: flex;
6952
+ flex-wrap: wrap;
6785
6953
  justify-content: space-between;
6786
6954
  gap: 1rem;
6787
6955
  margin-block-end: 0.4rem;
@@ -6997,6 +7165,9 @@
6997
7165
  }
6998
7166
 
6999
7167
  .ef-best-worst__row + .ef-best-worst__row { border-block-start: 0; }
7168
+ /* A long unbroken option (a product code, a compound word) wraps inside
7169
+ its track instead of scrolling the page sideways (ULI-L05). */
7170
+ .ef-best-worst__row > :first-child { min-inline-size: 0; overflow-wrap: anywhere; }
7000
7171
  .ef-best-worst__row label { display: grid; place-items: center; min-block-size: 2.75rem; }
7001
7172
  .ef-best-worst__row input { inline-size: 1.25rem; block-size: 1.25rem; accent-color: var(--ef-color-accent-primary, #905831); }
7002
7173
 
@@ -7095,6 +7266,10 @@
7095
7266
  padding-inline: 0.75rem;
7096
7267
  }
7097
7268
 
7269
+ /* Desktop labels align at the top of their own row. In the horizontal
7270
+ mobile layout that override must not defeat marker/text centering. */
7271
+ .ef-ordinal-option__label { align-self: center; }
7272
+
7098
7273
  .ef-ordinal-option + .ef-ordinal-option {
7099
7274
  border-inline-start: 0;
7100
7275
  border-block-start: 1px solid var(--ef-color-border-subtle, #e3e0d7);
@@ -7107,7 +7282,7 @@
7107
7282
  }
7108
7283
 
7109
7284
  .ef-rule-clause { grid-template-columns: 1fr; }
7110
- .ef-obligation { grid-template-columns: 1fr; }
7285
+ .ef-obligation { grid-template-columns: minmax(0, 1fr); }
7111
7286
  .ef-obligation__status { justify-self: start; }
7112
7287
 
7113
7288
  .ef-ranking__item {
@@ -7756,6 +7931,9 @@
7756
7931
  flex-wrap: wrap;
7757
7932
  align-items: center;
7758
7933
  gap: var(--ef-space-3);
7934
+ /* Shrinks inside a flex or grid parent instead of being held at its
7935
+ widest button's word (ULI-L05). */
7936
+ min-inline-size: 0;
7759
7937
  }
7760
7938
 
7761
7939
  .ef-site .ef-button {
@@ -7775,6 +7953,10 @@
7775
7953
  line-height: var(--ef-type-leading-snug);
7776
7954
  text-align: center;
7777
7955
  text-decoration: none;
7956
+ /* Never wider than its row and never split mid-word unless one word is
7957
+ wider than the row (ULI-L05, L06). */
7958
+ max-inline-size: 100%;
7959
+ overflow-wrap: break-word;
7778
7960
  cursor: pointer;
7779
7961
  /* Hover and press are perceptual emphasis on the surface only: the hit
7780
7962
  target never moves under the pointer (MOT-014). */
@@ -8021,7 +8203,10 @@
8021
8203
  min-block-size: 1.5rem;
8022
8204
  font-size: var(--ef-type-size-small);
8023
8205
  font-weight: var(--ef-type-weight-strong);
8024
- white-space: nowrap;
8206
+ /* One line whenever it fits (inline-flex shrinks to its content); it
8207
+ wraps at word boundaries instead of overflowing a narrow column at
8208
+ large text sizes (UI-LAYOUT-INTERACTION ULI-L05). */
8209
+ max-inline-size: 100%;
8025
8210
  }
8026
8211
 
8027
8212
  /* Four disparate columns cannot degrade by a minimum item size, so the
@@ -8053,6 +8238,9 @@
8053
8238
  padding-block: var(--ef-space-4);
8054
8239
  border-block-end: var(--ef-shape-border-width) solid var(--ef-tone-rule);
8055
8240
  color: var(--ef-tone-text);
8241
+ /* The step text is an anonymous flex item; let a long word wrap inside
8242
+ it instead of overflowing a narrow column (ULI-L05). */
8243
+ overflow-wrap: anywhere;
8056
8244
  }
8057
8245
 
8058
8246
  .ef-steps > li::before {
@@ -8182,6 +8370,7 @@
8182
8370
 
8183
8371
  .ef-code-figure > figcaption {
8184
8372
  margin-block-end: var(--ef-space-2);
8373
+ overflow-wrap: anywhere;
8185
8374
  color: var(--ef-tone-muted);
8186
8375
  font-family: var(--ef-type-family-mono);
8187
8376
  font-size: var(--ef-type-size-label);
@@ -8322,6 +8511,11 @@
8322
8511
  .ef-site .ef-section[data-ef-tone]:not([data-ef-tone="page"]) {
8323
8512
  margin-block: calc(var(--ef-layout-section-space) / 2);
8324
8513
  padding: calc(var(--ef-layout-block-space) * 2);
8514
+ /* The block-space token is rem-based; at enlarged text on a phone it
8515
+ would leave a few characters of line length (96px content at 320px
8516
+ and 200% text). Cap only the inline padding by viewport width; this
8517
+ changes nothing at the default text size (ULI-L05). */
8518
+ padding-inline: min(calc(var(--ef-layout-block-space) * 2), 12.5vw);
8325
8519
  }
8326
8520
 
8327
8521
  .ef-section > * + * { margin-block-start: var(--ef-layout-stack-space); }