@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.
@@ -1,4 +1,4 @@
1
- /*! Forma 0.6.1 | forma-all.css | bundle echelon-marketing
1
+ /*! Forma 0.7.0 | forma-all.css | bundle echelon-marketing
2
2
  Complete Forma surface (application components, assessment, and marketing layers) for sites that also present Forma application patterns such as data grids or status lozenges.
3
3
  Generated by tools/PresentationBundler. Do not edit; pin by version and sha256. */
4
4
 
@@ -637,6 +637,7 @@
637
637
  }
638
638
 
639
639
  :where(input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select) {
640
+ box-sizing: border-box;
640
641
  min-block-size: 2.75rem;
641
642
  inline-size: 100%;
642
643
  padding: 0.7rem 0.8rem;
@@ -644,6 +645,50 @@
644
645
  border-radius: var(--ef-primitive-radius-none, 0);
645
646
  background: var(--ef-color-surface-primary, #f2efe7);
646
647
  color: var(--ef-color-text-primary, #171a18);
648
+ font: inherit;
649
+ line-height: var(--ef-field-line-height, 1.6);
650
+ }
651
+
652
+ /*
653
+ * Equivalent single-line controls share one intrinsic block size
654
+ * (UI-LAYOUT-INTERACTION ULI-L02, L13): one line of field text plus the
655
+ * field padding and border. Native date/time and select boxes otherwise
656
+ * differ by several pixels from text inputs (Chromium: 51.97 vs 49.97 vs
657
+ * 46.38 px), which staggers peers in a row. Em-based, so it grows with
658
+ * the user's text size.
659
+ */
660
+ :where(
661
+ input:not([type]),
662
+ input:is([type="text"], [type="email"], [type="password"], [type="search"], [type="tel"], [type="url"], [type="number"],
663
+ [type="date"], [type="time"], [type="datetime-local"], [type="month"], [type="week"]),
664
+ select:not([multiple], [size])
665
+ ) {
666
+ block-size: var(--ef-field-control-block-size, calc(var(--ef-field-line-height, 1.6) * 1em + 1.4rem + 2px));
667
+ }
668
+
669
+ :where(input[type="date"], input[type="time"], input[type="datetime-local"], input[type="month"], input[type="week"])::-webkit-datetime-edit {
670
+ padding-block: 0;
671
+ }
672
+
673
+ /* A fieldset's default min-inline-size is min-content, which lets a long
674
+ legend or option widen a form past a narrow viewport (ULI-L05). */
675
+ :where(fieldset) {
676
+ min-inline-size: 0;
677
+ }
678
+
679
+ /* iOS temporal controls otherwise retain a native intrinsic width despite
680
+ the author's width limit. Keep the native picker, not its native box. */
681
+ :where(input[type="date"], input[type="time"], input[type="datetime-local"]) {
682
+ -webkit-appearance: none;
683
+ appearance: none;
684
+ box-sizing: border-box;
685
+ min-inline-size: 0;
686
+ max-inline-size: 100%;
687
+ }
688
+
689
+ :where(input[type="date"], input[type="time"], input[type="datetime-local"])::-webkit-date-and-time-value {
690
+ min-inline-size: 0;
691
+ text-align: start;
647
692
  }
648
693
 
649
694
  :where(:focus-visible) {
@@ -655,16 +700,29 @@
655
700
  .ef-stack { display: flex; flex-direction: column; gap: var(--ef-stack-gap, var(--ef-primitive-spacing-4, 1rem)); }
656
701
  .ef-cluster { display: flex; flex-wrap: wrap; gap: var(--ef-cluster-gap, var(--ef-primitive-spacing-3, 0.75rem)); align-items: center; }
657
702
  /* .ef-actions (catalog: button) is an application-layer row of related actions; it was styled only in marketing.css (forma#93 gap). */
658
- .ef-actions { display: flex; flex-wrap: wrap; gap: var(--ef-primitive-spacing-3, 0.75rem); align-items: center; }
703
+ /* min-inline-size: 0 lets an action row shrink inside a flex or grid parent
704
+ instead of being held at its widest button's word (ULI-L05). */
705
+ .ef-actions { display: flex; flex-wrap: wrap; gap: var(--ef-primitive-spacing-3, 0.75rem); align-items: center; min-inline-size: 0; }
659
706
  .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)); }
660
707
  .ef-section { padding-block: var(--ef-section-space, var(--ef-primitive-spacing-7, 3rem)); }
661
708
  .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); }
662
709
  .ef-surface :where(h1, h2, h3, h4, h5, h6, p) { color: inherit; }
663
710
 
664
- :where(h1, h2, h3, h4, h5, h6, p, a, label, legend, td, th) {
711
+ :where(h1, h2, h3, h4, h5, h6, p, a, label, legend, td, th, dt, dd, caption, figcaption) {
665
712
  overflow-wrap: anywhere;
666
713
  }
667
714
 
715
+ /* Action labels never split mid-word because their container allows it
716
+ (a link button inherits the rule above; a button inherits it from a
717
+ table cell). A single word wider than its container may still break
718
+ rather than overflow. Must follow the rule above (ULI-L06, L07). The
719
+ inline-size cap also clamps a flex item's automatic minimum, so a
720
+ button never pushes its row past its container (ULI-L05). */
721
+ :where(button, .ef-button) {
722
+ overflow-wrap: break-word;
723
+ max-inline-size: 100%;
724
+ }
725
+
668
726
  @media (prefers-reduced-motion: reduce) {
669
727
  *, *::before, *::after {
670
728
  scroll-behavior: auto !important;
@@ -779,9 +837,44 @@
779
837
  min-inline-size: 0;
780
838
  max-inline-size: 100%;
781
839
  grid-template-columns: minmax(0, 1fr);
840
+ /* A stretched field (a grid row taller than it) keeps its label and
841
+ control at the top instead of distributing the extra space between
842
+ rows, which would push the control below its peers (ULI-L01). */
843
+ align-content: start;
782
844
  gap: var(--ef-primitive-spacing-2, 0.5rem);
783
845
  }
784
846
 
847
+ /*
848
+ * Form row (ULI-L01, L03, L13). Peer fields share one set of rows: label,
849
+ * description, control and validation message. Each field spans the four
850
+ * rows through subgrid and places its parts explicitly, so every control
851
+ * in a visual row starts at the same top edge however long the labels,
852
+ * descriptions or messages of its neighbours are. Fields wrap to new rows
853
+ * by available width. Without subgrid support each field keeps its own
854
+ * grid (the previous behavior).
855
+ */
856
+ .ef-form-row {
857
+ --ef-form-row-min: 14rem;
858
+ display: grid;
859
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--ef-form-row-min)), 1fr));
860
+ column-gap: var(--ef-form-row-gap, var(--ef-primitive-spacing-4, 1rem));
861
+ row-gap: 0;
862
+ align-items: start;
863
+ min-inline-size: 0;
864
+ }
865
+
866
+ .ef-form-row > .ef-field {
867
+ grid-row: span 4;
868
+ grid-template-rows: subgrid;
869
+ row-gap: 0;
870
+ margin-block-end: var(--ef-form-row-gap, var(--ef-primitive-spacing-4, 1rem));
871
+ }
872
+
873
+ .ef-form-row > .ef-field > .ef-field__label { grid-row: 1; }
874
+ .ef-form-row > .ef-field > .ef-field__description { grid-row: 2; margin-block-start: var(--ef-primitive-spacing-2, 0.5rem); }
875
+ .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); }
876
+ .ef-form-row > .ef-field > .ef-validation-message { grid-row: 4; margin-block-start: var(--ef-primitive-spacing-2, 0.5rem); }
877
+
785
878
  .ef-field > :is(input[type="date"], input[type="time"], input[type="datetime-local"]) {
786
879
  min-inline-size: 0;
787
880
  inline-size: 100%;
@@ -820,6 +913,10 @@
820
913
  --ef-motion-perceptual-emphasis-duration: var(--ef-primitive-motion-duration-standard, 180ms);
821
914
  --ef-motion-perceptual-easing: var(--ef-primitive-motion-easing-standard, cubic-bezier(0.2, 0, 0, 1));
822
915
 
916
+ /* Brief surface fade: gentle ignition, fast middle, gentle settling.
917
+ Monotonic and symmetric: no opacity overshoot or delayed activation. */
918
+ --ef-motion-fade-easing: cubic-bezier(0.65, 0, 0.35, 1);
919
+
823
920
  /* Constant-velocity cadence: repeated activity is parameterized by a
824
921
  named period and constant velocity, never by spring settling (MOT-016). */
825
922
  --ef-motion-cadence-period: 1600ms;
@@ -849,7 +946,7 @@
849
946
  --ef-motion-gravity-duration: 255ms;
850
947
  --ef-motion-state-duration: 154ms;
851
948
  --ef-motion-press-duration: 107ms;
852
- --ef-motion-exit-duration: 146ms;
949
+ --ef-motion-exit-duration: 214ms;
853
950
  --ef-motion-spring-easing: linear(0, 0.42 24%, 0.82 48%, 1.055 70%, 0.988 88%, 1);
854
951
  --ef-motion-damped-easing: cubic-bezier(0.2, 0.8, 0.2, 1);
855
952
  }
@@ -923,11 +1020,8 @@
923
1020
  calc(var(--ef-motion-inertia-duration) * 0.5),
924
1021
  160ms
925
1022
  );
926
- --ef-motion-exit-duration: clamp(
927
- 80ms,
928
- calc(var(--ef-motion-inertia-duration) * 0.68),
929
- 220ms
930
- );
1023
+ /* Compatibility token: reciprocal motion has one response time. */
1024
+ --ef-motion-exit-duration: var(--ef-motion-inertia-duration);
931
1025
  }
932
1026
  }
933
1027
 
@@ -1111,7 +1205,9 @@
1111
1205
 
1112
1206
  /* Select -------------------------------------------------------------- */
1113
1207
  .ef-select-field {
1208
+ min-inline-size: 0;
1114
1209
  display: grid;
1210
+ grid-template-columns: minmax(0, 1fr);
1115
1211
  gap: var(--ef-primitive-spacing-2, 0.5rem);
1116
1212
  max-inline-size: 100%;
1117
1213
  }
@@ -1119,12 +1215,19 @@
1119
1215
  .ef-select {
1120
1216
  --ef-motion-distance: 0.35;
1121
1217
  position: relative;
1218
+ min-inline-size: 0;
1122
1219
  display: grid;
1220
+ grid-template-columns: minmax(0, 1fr);
1123
1221
  align-items: center;
1124
1222
  max-inline-size: 100%;
1125
1223
  }
1126
1224
 
1127
1225
  .ef-select__input {
1226
+ overflow: hidden;
1227
+ text-overflow: ellipsis;
1228
+ white-space: nowrap;
1229
+ min-inline-size: 0;
1230
+ max-inline-size: 100%;
1128
1231
  inline-size: 100%;
1129
1232
  min-block-size: 2.75rem;
1130
1233
  padding-inline-end: 2.75rem;
@@ -1343,6 +1446,10 @@
1343
1446
 
1344
1447
  .ef-disclosure > summary {
1345
1448
  min-inline-size: 0;
1449
+ /* The open marker rotates 45deg; its rotated (empty) box corners would
1450
+ otherwise add a few pixels of page overflow at narrow widths (ULI-L05).
1451
+ Clipping does not affect the summary's own focus ring. */
1452
+ overflow-x: clip;
1346
1453
  display: flex;
1347
1454
  align-items: center;
1348
1455
  justify-content: space-between;
@@ -1396,35 +1503,19 @@
1396
1503
  overflow: auto;
1397
1504
  overscroll-behavior: contain;
1398
1505
  opacity: 0;
1399
- translate: 0 -0.35rem;
1400
- scale: 0.985;
1401
- transition:
1402
- opacity var(--ef-motion-perceptual-duration) var(--ef-motion-perceptual-easing),
1403
- translate var(--ef-motion-exit-duration) var(--ef-motion-damped-easing),
1404
- scale var(--ef-motion-exit-duration) var(--ef-motion-damped-easing),
1405
- display var(--ef-motion-exit-duration) allow-discrete,
1406
- overlay var(--ef-motion-exit-duration) allow-discrete;
1506
+ /* Fade at the resting geometry. Never translate or scale a visible exit. */
1507
+ transition: opacity var(--ef-motion-perceptual-duration) var(--ef-motion-fade-easing);
1407
1508
  }
1408
1509
 
1409
1510
  .ef-popover:popover-open,
1410
1511
  .ef-menu:popover-open {
1411
1512
  opacity: 1;
1412
- translate: 0 0;
1413
- scale: 1;
1414
- transition-duration:
1415
- var(--ef-motion-perceptual-duration),
1416
- var(--ef-motion-inertia-duration),
1417
- var(--ef-motion-inertia-duration),
1418
- var(--ef-motion-inertia-duration),
1419
- var(--ef-motion-inertia-duration);
1420
1513
  }
1421
1514
 
1422
1515
  @starting-style {
1423
1516
  .ef-popover:popover-open,
1424
1517
  .ef-menu:popover-open {
1425
1518
  opacity: 0;
1426
- translate: 0 -0.35rem;
1427
- scale: 0.985;
1428
1519
  }
1429
1520
  }
1430
1521
 
@@ -1476,9 +1567,9 @@
1476
1567
  * the hint stays inside the viewport; otherwise the browser's centered
1477
1568
  * top-layer popover placement remains readable and in view.
1478
1569
  *
1479
- * Motion: light perceived weight. Opacity is perceptual interpolation and
1480
- * a tiny origin-related displacement is light inertial; exit uses the
1481
- * shorter derived exit duration. Reduced motion removes the displacement.
1570
+ * Motion: reciprocal perceptual fades at unchanged resting geometry. The
1571
+ * top layer is removed only after opacity reaches zero where supported.
1572
+ * Reduced motion reaches the endpoint immediately.
1482
1573
  */
1483
1574
  .ef-tooltip {
1484
1575
  --ef-motion-distance: 0.2;
@@ -1497,7 +1588,6 @@
1497
1588
  }
1498
1589
 
1499
1590
  .ef-tooltip__surface {
1500
- --ef-tooltip-offset: 0.25rem;
1501
1591
  inline-size: max-content;
1502
1592
  max-inline-size: min(20rem, calc(100vw - 2rem));
1503
1593
  margin: 0;
@@ -1509,30 +1599,30 @@
1509
1599
  line-height: 1.4;
1510
1600
  overflow-wrap: anywhere;
1511
1601
  opacity: 0;
1512
- translate: 0 var(--ef-tooltip-offset);
1513
- transition:
1514
- opacity var(--ef-motion-perceptual-duration) var(--ef-motion-perceptual-easing),
1515
- translate var(--ef-motion-exit-duration) var(--ef-motion-damped-easing),
1516
- display var(--ef-motion-exit-duration) allow-discrete,
1517
- overlay var(--ef-motion-exit-duration) allow-discrete;
1602
+ transition: opacity var(--ef-motion-perceptual-duration) var(--ef-motion-fade-easing);
1518
1603
  }
1519
1604
 
1520
1605
  .ef-tooltip__surface:popover-open {
1521
1606
  opacity: 1;
1522
- translate: 0 0;
1523
- /* Entry settles over the light inertial duration; exit (above) uses the
1524
- shorter derived exit duration. Opacity stays perceptual both ways. */
1525
- transition-duration:
1526
- var(--ef-motion-perceptual-duration),
1527
- var(--ef-motion-inertia-duration),
1528
- var(--ef-motion-inertia-duration),
1529
- var(--ef-motion-inertia-duration);
1530
1607
  }
1531
1608
 
1532
1609
  @starting-style {
1533
1610
  .ef-tooltip__surface:popover-open {
1534
1611
  opacity: 0;
1535
- translate: 0 var(--ef-tooltip-offset);
1612
+ }
1613
+ }
1614
+
1615
+ /* Keep the top-layer placement and implicit anchor through the entire fade.
1616
+ Display retention alone can expose a relocated, translucent surface in
1617
+ engines without overlay retention. Those engines dismiss immediately. */
1618
+ @supports (overlay: auto) {
1619
+ .ef-popover,
1620
+ .ef-menu,
1621
+ .ef-tooltip__surface {
1622
+ transition:
1623
+ opacity var(--ef-motion-perceptual-duration) var(--ef-motion-fade-easing),
1624
+ display var(--ef-motion-perceptual-duration) allow-discrete,
1625
+ overlay var(--ef-motion-perceptual-duration) allow-discrete;
1536
1626
  }
1537
1627
  }
1538
1628
 
@@ -1555,9 +1645,8 @@
1555
1645
  }
1556
1646
 
1557
1647
  @media (prefers-reduced-motion: reduce) {
1558
- .ef-tooltip__surface,
1559
- .ef-tooltip__surface:popover-open {
1560
- translate: 0 0;
1648
+ .ef-tooltip__surface {
1649
+ transition-duration: 0.01ms;
1561
1650
  }
1562
1651
  }
1563
1652
 
@@ -1659,8 +1748,8 @@
1659
1748
 
1660
1749
  /* Dialog and flyout ---------------------------------------------------- */
1661
1750
  :where(.ef-dialog, .ef-flyout) {
1662
- --ef-overlay-motion-duration: var(--ef-motion-exit-duration);
1663
- --ef-overlay-spatial-easing: var(--ef-motion-damped-easing);
1751
+ --ef-overlay-motion-duration: var(--ef-motion-inertia-duration);
1752
+ --ef-overlay-spatial-easing: var(--ef-motion-spring-easing);
1664
1753
  padding: 0;
1665
1754
  border: 1px solid var(--ef-color-border-functional, #3a403c);
1666
1755
  background: var(--ef-color-surface-primary, #f2efe7);
@@ -1679,9 +1768,7 @@
1679
1768
  /* Dimming is non-spatial: perceptual interpolation, without mass. */
1680
1769
  transition:
1681
1770
  background var(--ef-motion-perceptual-duration) var(--ef-motion-perceptual-easing),
1682
- backdrop-filter var(--ef-motion-perceptual-duration) var(--ef-motion-perceptual-easing),
1683
- display var(--ef-overlay-motion-duration) allow-discrete,
1684
- overlay var(--ef-overlay-motion-duration) allow-discrete;
1771
+ backdrop-filter var(--ef-motion-perceptual-duration) var(--ef-motion-perceptual-easing);
1685
1772
  }
1686
1773
 
1687
1774
  :where(.ef-dialog, .ef-flyout)[open]::backdrop {
@@ -1698,9 +1785,7 @@
1698
1785
  scale: 0.985;
1699
1786
  transition:
1700
1787
  translate var(--ef-overlay-motion-duration) var(--ef-overlay-spatial-easing),
1701
- scale var(--ef-overlay-motion-duration) var(--ef-overlay-spatial-easing),
1702
- display var(--ef-overlay-motion-duration) allow-discrete,
1703
- overlay var(--ef-overlay-motion-duration) allow-discrete;
1788
+ scale var(--ef-overlay-motion-duration) var(--ef-overlay-spatial-easing);
1704
1789
  }
1705
1790
 
1706
1791
  .ef-dialog[open] {
@@ -1733,9 +1818,34 @@
1733
1818
  box-shadow: 0 0 3rem rgb(0 0 0 / 0.24);
1734
1819
  translate: var(--ef-flyout-closed-x) 0;
1735
1820
  transition:
1736
- translate var(--ef-overlay-motion-duration) var(--ef-overlay-spatial-easing),
1737
- display var(--ef-overlay-motion-duration) allow-discrete,
1738
- overlay var(--ef-overlay-motion-duration) allow-discrete;
1821
+ translate var(--ef-overlay-motion-duration) var(--ef-overlay-spatial-easing);
1822
+ }
1823
+
1824
+ /* Retain display only when overlay can retain viewport/top-layer geometry.
1825
+ Otherwise a closed dialog can animate inside a containing ancestor. */
1826
+ @supports (overlay: auto) {
1827
+ :where(.ef-dialog, .ef-flyout)::backdrop {
1828
+ transition:
1829
+ background var(--ef-motion-perceptual-duration) var(--ef-motion-perceptual-easing),
1830
+ backdrop-filter var(--ef-motion-perceptual-duration) var(--ef-motion-perceptual-easing),
1831
+ display var(--ef-overlay-motion-duration) allow-discrete,
1832
+ overlay var(--ef-overlay-motion-duration) allow-discrete;
1833
+ }
1834
+
1835
+ .ef-dialog {
1836
+ transition:
1837
+ translate var(--ef-overlay-motion-duration) var(--ef-overlay-spatial-easing),
1838
+ scale var(--ef-overlay-motion-duration) var(--ef-overlay-spatial-easing),
1839
+ display var(--ef-overlay-motion-duration) allow-discrete,
1840
+ overlay var(--ef-overlay-motion-duration) allow-discrete;
1841
+ }
1842
+
1843
+ .ef-flyout {
1844
+ transition:
1845
+ translate var(--ef-overlay-motion-duration) var(--ef-overlay-spatial-easing),
1846
+ display var(--ef-overlay-motion-duration) allow-discrete,
1847
+ overlay var(--ef-overlay-motion-duration) allow-discrete;
1848
+ }
1739
1849
  }
1740
1850
 
1741
1851
  .ef-flyout[data-ef-side="left"] {
@@ -1757,9 +1867,11 @@
1757
1867
  .ef-flyout[open] { translate: 0 0; }
1758
1868
 
1759
1869
  .ef-flyout__surface {
1870
+ min-inline-size: 0;
1760
1871
  min-block-size: 0;
1761
1872
  block-size: 100%;
1762
1873
  display: grid;
1874
+ grid-template-columns: minmax(0, 1fr);
1763
1875
  grid-template-rows: auto minmax(0, 1fr) auto;
1764
1876
  }
1765
1877
 
@@ -1789,6 +1901,7 @@
1789
1901
  }
1790
1902
 
1791
1903
  .ef-flyout__body {
1904
+ min-inline-size: 0;
1792
1905
  min-block-size: 0;
1793
1906
  overflow: auto;
1794
1907
  overscroll-behavior: contain;
@@ -1882,7 +1995,9 @@
1882
1995
  font-family: var(--ef-primitive-font-family-mono, ui-monospace, monospace);
1883
1996
  font-size: 0.6875rem;
1884
1997
  line-height: 1.2;
1885
- white-space: nowrap;
1998
+ /* Wraps inside its container rather than widening it (ULI-L05). */
1999
+ max-inline-size: 100%;
2000
+ overflow-wrap: anywhere;
1886
2001
  }
1887
2002
 
1888
2003
  .ef-status-lozenge::before {
@@ -1940,7 +2055,7 @@
1940
2055
 
1941
2056
  .ef-collection-toolbar__controls label,
1942
2057
  .ef-collection-toolbar__filters {
1943
- min-inline-size: 9rem;
2058
+ min-inline-size: min(9rem, 100%);
1944
2059
  font-size: 0.75rem;
1945
2060
  font-weight: 650;
1946
2061
  }
@@ -2234,8 +2349,10 @@
2234
2349
  /* Metrics and dashboards --------------------------------------------- */
2235
2350
  .ef-metric-card {
2236
2351
  display: grid;
2352
+ grid-template-columns: minmax(0, 1fr);
2237
2353
  gap: 0.5rem;
2238
2354
  min-inline-size: 0;
2355
+ overflow-wrap: anywhere;
2239
2356
  padding: 1.25rem;
2240
2357
  border: 1px solid var(--ef-color-border-functional, #3a403c);
2241
2358
  background: var(--ef-color-surface-primary, #f2efe7);
@@ -2578,6 +2695,8 @@
2578
2695
  }
2579
2696
 
2580
2697
  .ef-wizard__steps li {
2698
+ min-inline-size: 0;
2699
+ overflow-wrap: anywhere;
2581
2700
  display: flex;
2582
2701
  gap: 0.5rem;
2583
2702
  align-items: center;
@@ -2604,6 +2723,7 @@
2604
2723
  .ef-readiness-checklist > header h3 { margin: 0.5rem 0 0; }
2605
2724
  .ef-readiness-checklist__items { margin: 0; padding: 0; list-style: none; }
2606
2725
  .ef-readiness-checklist__items li {
2726
+ overflow-wrap: anywhere;
2607
2727
  display: grid;
2608
2728
  grid-template-columns: 2rem minmax(0, 1fr) auto;
2609
2729
  gap: 0.75rem;
@@ -2671,6 +2791,7 @@
2671
2791
  .ef-command-palette__group button {
2672
2792
  inline-size: 100%;
2673
2793
  display: flex;
2794
+ flex-wrap: wrap;
2674
2795
  justify-content: space-between;
2675
2796
  gap: 1rem;
2676
2797
  text-align: start;
@@ -2787,13 +2908,38 @@
2787
2908
  }
2788
2909
 
2789
2910
  .ef-record-header__main nav { font-size: 0.75rem; }
2911
+ /*
2912
+ * The status sits beside the record title and is centred on the title's
2913
+ * row, not on the type label and title together (ULI-L01, L07). The
2914
+ * identity block spans two subgrid rows (type, title); the status occupies
2915
+ * the title row. Narrow containers stack the status below.
2916
+ */
2917
+ .ef-record-header__main { container-type: inline-size; min-inline-size: 0; }
2790
2918
  .ef-record-header__title {
2791
- display: flex;
2792
- flex-wrap: wrap;
2793
- gap: 1rem;
2919
+ display: grid;
2920
+ grid-template-columns: minmax(0, auto) auto;
2921
+ grid-template-rows: auto auto;
2922
+ justify-content: start;
2923
+ column-gap: 1rem;
2924
+ row-gap: 0.5rem;
2794
2925
  align-items: center;
2795
2926
  margin-block: 0.75rem 0;
2796
2927
  }
2928
+ .ef-record-header__title > :first-child {
2929
+ display: grid;
2930
+ grid-row: 1 / span 2;
2931
+ grid-template-rows: subgrid;
2932
+ row-gap: 0;
2933
+ min-inline-size: 0;
2934
+ }
2935
+ .ef-record-header__title > :first-child > .ef-record-header__type { grid-row: 1; align-self: end; }
2936
+ .ef-record-header__title > :first-child > :is(h1, h2, h3, h4) { grid-row: 2; }
2937
+ .ef-record-header__title > :not(:first-child) { grid-row: 2; }
2938
+ @container (max-width: 24em) {
2939
+ .ef-record-header__title { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; }
2940
+ .ef-record-header__title > :first-child { grid-row: auto; }
2941
+ .ef-record-header__title > :not(:first-child) { grid-row: auto; justify-self: start; }
2942
+ }
2797
2943
  .ef-record-header__title h2 { margin: 0.15rem 0 0; }
2798
2944
  .ef-record-header__type {
2799
2945
  color: var(--ef-surface-accent-text-color, var(--ef-color-accent-secondary, #47756b));
@@ -2877,7 +3023,9 @@
2877
3023
  .ef-provenance-trail li {
2878
3024
  position: relative;
2879
3025
  display: grid;
3026
+ grid-template-columns: minmax(0, 1fr);
2880
3027
  gap: 0.2rem;
3028
+ overflow-wrap: anywhere;
2881
3029
  padding: 0.75rem 0 0.75rem 2rem;
2882
3030
  }
2883
3031
  .ef-provenance-trail li::before {
@@ -2937,6 +3085,7 @@
2937
3085
  /* Mobile action bar --------------------------------------------------- */
2938
3086
  .ef-mobile-action-bar {
2939
3087
  display: flex;
3088
+ flex-wrap: wrap;
2940
3089
  gap: 0.5rem;
2941
3090
  padding: 0.75rem;
2942
3091
  padding-block-end: max(0.75rem, env(safe-area-inset-bottom));
@@ -2945,7 +3094,7 @@
2945
3094
  backdrop-filter: blur(10px);
2946
3095
  }
2947
3096
 
2948
- .ef-mobile-action-bar > * { flex: 1 1 0; }
3097
+ .ef-mobile-action-bar > * { flex: 1 1 min(100%, 8rem); }
2949
3098
  .ef-mobile-action-bar__primary {
2950
3099
  background: var(--ef-color-surface-inverse, #202421);
2951
3100
  color: var(--ef-color-text-inverse, #f2efe7);
@@ -2978,7 +3127,9 @@
2978
3127
  .ef-operation-status,
2979
3128
  .ef-record-header,
2980
3129
  .ef-file-upload__queue li {
2981
- grid-template-columns: 1fr;
3130
+ /* minmax(0, …): a long word or wide font never holds the track wider
3131
+ than the screen (ULI-L05). */
3132
+ grid-template-columns: minmax(0, 1fr);
2982
3133
  }
2983
3134
 
2984
3135
  .ef-work-queue__item button,
@@ -3057,6 +3208,7 @@
3057
3208
  margin-block-end: 0.75rem;
3058
3209
  border: 1px solid var(--ef-color-border-functional, #3a403c);
3059
3210
  }
3211
+ .ef-data-grid tr { container-type: inline-size; }
3060
3212
  .ef-data-grid td {
3061
3213
  display: grid;
3062
3214
  grid-template-columns: minmax(6.5rem, 0.4fr) minmax(0, 1fr);
@@ -3070,13 +3222,19 @@
3070
3222
  font-weight: 650;
3071
3223
  }
3072
3224
  .ef-data-grid td[data-label="Actions"] > * { inline-size: 100%; }
3225
+ /* When enlarged text leaves no room for a label beside its value, the
3226
+ label stacks above the value instead of squeezing it to a few
3227
+ characters per line (ULI-L05). 16em is two readable columns. */
3228
+ @container (max-width: 16em) {
3229
+ .ef-data-grid td { grid-template-columns: minmax(0, 1fr); gap: 0.25rem; }
3230
+ }
3073
3231
 
3074
3232
  .ef-search__control { grid-template-columns: 1fr; }
3075
3233
  .ef-search__control input { border-inline-end: 1px solid var(--ef-color-border-functional, #3a403c); border-block-end: 0; }
3076
3234
 
3077
3235
  .ef-work-queue__header { display: grid; }
3078
3236
  .ef-diff-row,
3079
- .ef-key-value-list > div { grid-template-columns: 1fr; gap: 0.25rem; }
3237
+ .ef-key-value-list > div { grid-template-columns: minmax(0, 1fr); gap: 0.25rem; }
3080
3238
 
3081
3239
  .ef-status-lozenge-set { align-items: stretch; }
3082
3240
  .ef-status-lozenge-set .ef-status-lozenge { flex: 1 1 100%; justify-content: center; }
@@ -3318,7 +3476,7 @@
3318
3476
  .ef-composer__actions button { min-block-size: 2.75rem; }
3319
3477
  .ef-composer__submit { inline-size: 100%; }
3320
3478
 
3321
- .ef-turn { grid-template-columns: 1fr; }
3479
+ .ef-turn { grid-template-columns: minmax(0, 1fr); }
3322
3480
  .ef-turn__actions { grid-column: 1; }
3323
3481
  .ef-turn__actions button { inline-size: 100%; }
3324
3482
 
@@ -3601,6 +3759,7 @@
3601
3759
  --ef-color-text-muted: var(--ef-color-text-on-secondary-surface, #3a403c);
3602
3760
  --ef-surface-accent-text-color: var(--ef-color-text-primary, #171a18);
3603
3761
  display: inline-flex;
3762
+ max-inline-size: 100%;
3604
3763
  align-items: center;
3605
3764
  gap: var(--ef-primitive-spacing-2, 0.5rem);
3606
3765
  padding: 0.55rem 0.75rem;
@@ -3619,6 +3778,8 @@
3619
3778
  .ef-diagnostic-status__body {
3620
3779
  display: grid;
3621
3780
  gap: 0.1rem;
3781
+ min-inline-size: 0;
3782
+ overflow-wrap: anywhere;
3622
3783
  }
3623
3784
 
3624
3785
  .ef-diagnostic-status__body > span {
@@ -3757,7 +3918,7 @@
3757
3918
  .ef-security-state-matrix__grid dt { font-size:.75rem; font-weight:700; }
3758
3919
  .ef-security-state-matrix__grid dd { margin:0; font-size:clamp(1.4rem,4vw,2rem); font-weight:750; }
3759
3920
  .ef-security-blockers ul { display:grid; gap:.75rem; padding-inline-start:1.25rem; }
3760
- .ef-security-blockers li { padding-inline-start:.25rem; }
3921
+ .ef-security-blockers li { padding-inline-start:.25rem; min-inline-size:0; overflow-wrap:anywhere; }
3761
3922
  .ef-security-blockers li span { display:block; }
3762
3923
  .ef-security-evidence-chain ol { display:grid; grid-template-columns:repeat(auto-fit,minmax(9rem,1fr)); gap:.5rem; padding:0; list-style:none; }
3763
3924
  .ef-security-evidence-chain li { padding:.75rem; border:1px solid var(--ef-color-border-functional,#3a403c); overflow-wrap:anywhere; }
@@ -3771,7 +3932,7 @@
3771
3932
  .ef-invariant-result dl { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(10rem,100%),1fr)); gap:.75rem; }
3772
3933
  .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; }
3773
3934
  :where(.ef-evidence-age,.ef-security-trend) ul { display:grid; gap:.5rem; margin:0; padding:0; list-style:none; }
3774
- :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); }
3935
+ :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); }
3775
3936
  :where(.ef-evidence-age,.ef-security-trend) li span { overflow-wrap:anywhere; }
3776
3937
 
3777
3938
  /* Composition primitives ---------------------------------------------- */
@@ -3981,7 +4142,10 @@
3981
4142
  }
3982
4143
 
3983
4144
  @media (max-width: 48rem) {
3984
- .ef-split-pane[data-ef-resizable] { grid-template-columns: minmax(0, 1fr); }
4145
+ /* Includes the collapsed state, whose track list would otherwise keep
4146
+ three columns on a phone and squeeze the secondary pane (ULI-L05). */
4147
+ .ef-split-pane[data-ef-resizable],
4148
+ .ef-split-pane[data-ef-resizable][data-ef-collapsed] { grid-template-columns: minmax(0, 1fr); }
3985
4149
  .ef-split-pane__separator { display: none; }
3986
4150
  }
3987
4151
 
@@ -6287,7 +6451,10 @@
6287
6451
 
6288
6452
  /* Choice cards -------------------------------------------------------- */
6289
6453
  .ef-choice-group {
6290
- display: grid;
6454
+ /* A column flex layout lets wrapped cards determine their own height.
6455
+ WebKit can retain stale grid row heights on fieldset viewport reflow. */
6456
+ display: flex;
6457
+ flex-direction: column;
6291
6458
  gap: var(--ef-primitive-spacing-2, 0.5rem);
6292
6459
  }
6293
6460
 
@@ -6787,6 +6954,7 @@
6787
6954
 
6788
6955
  .ef-semantic-differential__endpoints {
6789
6956
  display: flex;
6957
+ flex-wrap: wrap;
6790
6958
  justify-content: space-between;
6791
6959
  gap: 1rem;
6792
6960
  margin-block-end: 0.4rem;
@@ -7002,6 +7170,9 @@
7002
7170
  }
7003
7171
 
7004
7172
  .ef-best-worst__row + .ef-best-worst__row { border-block-start: 0; }
7173
+ /* A long unbroken option (a product code, a compound word) wraps inside
7174
+ its track instead of scrolling the page sideways (ULI-L05). */
7175
+ .ef-best-worst__row > :first-child { min-inline-size: 0; overflow-wrap: anywhere; }
7005
7176
  .ef-best-worst__row label { display: grid; place-items: center; min-block-size: 2.75rem; }
7006
7177
  .ef-best-worst__row input { inline-size: 1.25rem; block-size: 1.25rem; accent-color: var(--ef-color-accent-primary, #905831); }
7007
7178
 
@@ -7100,6 +7271,10 @@
7100
7271
  padding-inline: 0.75rem;
7101
7272
  }
7102
7273
 
7274
+ /* Desktop labels align at the top of their own row. In the horizontal
7275
+ mobile layout that override must not defeat marker/text centering. */
7276
+ .ef-ordinal-option__label { align-self: center; }
7277
+
7103
7278
  .ef-ordinal-option + .ef-ordinal-option {
7104
7279
  border-inline-start: 0;
7105
7280
  border-block-start: 1px solid var(--ef-color-border-subtle, #e3e0d7);
@@ -7112,7 +7287,7 @@
7112
7287
  }
7113
7288
 
7114
7289
  .ef-rule-clause { grid-template-columns: 1fr; }
7115
- .ef-obligation { grid-template-columns: 1fr; }
7290
+ .ef-obligation { grid-template-columns: minmax(0, 1fr); }
7116
7291
  .ef-obligation__status { justify-self: start; }
7117
7292
 
7118
7293
  .ef-ranking__item {
@@ -7761,6 +7936,9 @@
7761
7936
  flex-wrap: wrap;
7762
7937
  align-items: center;
7763
7938
  gap: var(--ef-space-3);
7939
+ /* Shrinks inside a flex or grid parent instead of being held at its
7940
+ widest button's word (ULI-L05). */
7941
+ min-inline-size: 0;
7764
7942
  }
7765
7943
 
7766
7944
  .ef-site .ef-button {
@@ -7780,6 +7958,10 @@
7780
7958
  line-height: var(--ef-type-leading-snug);
7781
7959
  text-align: center;
7782
7960
  text-decoration: none;
7961
+ /* Never wider than its row and never split mid-word unless one word is
7962
+ wider than the row (ULI-L05, L06). */
7963
+ max-inline-size: 100%;
7964
+ overflow-wrap: break-word;
7783
7965
  cursor: pointer;
7784
7966
  /* Hover and press are perceptual emphasis on the surface only: the hit
7785
7967
  target never moves under the pointer (MOT-014). */
@@ -8026,7 +8208,10 @@
8026
8208
  min-block-size: 1.5rem;
8027
8209
  font-size: var(--ef-type-size-small);
8028
8210
  font-weight: var(--ef-type-weight-strong);
8029
- white-space: nowrap;
8211
+ /* One line whenever it fits (inline-flex shrinks to its content); it
8212
+ wraps at word boundaries instead of overflowing a narrow column at
8213
+ large text sizes (UI-LAYOUT-INTERACTION ULI-L05). */
8214
+ max-inline-size: 100%;
8030
8215
  }
8031
8216
 
8032
8217
  /* Four disparate columns cannot degrade by a minimum item size, so the
@@ -8058,6 +8243,9 @@
8058
8243
  padding-block: var(--ef-space-4);
8059
8244
  border-block-end: var(--ef-shape-border-width) solid var(--ef-tone-rule);
8060
8245
  color: var(--ef-tone-text);
8246
+ /* The step text is an anonymous flex item; let a long word wrap inside
8247
+ it instead of overflowing a narrow column (ULI-L05). */
8248
+ overflow-wrap: anywhere;
8061
8249
  }
8062
8250
 
8063
8251
  .ef-steps > li::before {
@@ -8187,6 +8375,7 @@
8187
8375
 
8188
8376
  .ef-code-figure > figcaption {
8189
8377
  margin-block-end: var(--ef-space-2);
8378
+ overflow-wrap: anywhere;
8190
8379
  color: var(--ef-tone-muted);
8191
8380
  font-family: var(--ef-type-family-mono);
8192
8381
  font-size: var(--ef-type-size-label);
@@ -8327,6 +8516,11 @@
8327
8516
  .ef-site .ef-section[data-ef-tone]:not([data-ef-tone="page"]) {
8328
8517
  margin-block: calc(var(--ef-layout-section-space) / 2);
8329
8518
  padding: calc(var(--ef-layout-block-space) * 2);
8519
+ /* The block-space token is rem-based; at enlarged text on a phone it
8520
+ would leave a few characters of line length (96px content at 320px
8521
+ and 200% text). Cap only the inline padding by viewport width; this
8522
+ changes nothing at the default text size (ULI-L05). */
8523
+ padding-inline: min(calc(var(--ef-layout-block-space) * 2), 12.5vw);
8330
8524
  }
8331
8525
 
8332
8526
  .ef-section > * + * { margin-block-start: var(--ef-layout-stack-space); }