@xenosystem/elements-react 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/LICENSE +18 -18
  2. package/README.md +41 -41
  3. package/dist/containers/Chip.js +1 -1
  4. package/dist/containers/Panel.js +1 -1
  5. package/package.json +58 -58
  6. package/src/chrome-separated.css +132 -132
  7. package/src/chrome-unified.css +72 -72
  8. package/src/containers/Badges.css +116 -116
  9. package/src/containers/avatar.css +70 -70
  10. package/src/containers/card.css +75 -75
  11. package/src/containers/chip.css +103 -103
  12. package/src/containers/list-row.css +132 -132
  13. package/src/containers/message-bubble.css +112 -112
  14. package/src/containers/panel.css +125 -120
  15. package/src/containers/table.css +102 -102
  16. package/src/containers/tile.css +17 -17
  17. package/src/content/callout.css +77 -77
  18. package/src/content/caret.css +30 -30
  19. package/src/content/code-block.css +159 -159
  20. package/src/content/collapsible.css +80 -80
  21. package/src/content/inline-code.css +14 -14
  22. package/src/content/model-picker.css +219 -219
  23. package/src/content/source-card.css +99 -99
  24. package/src/content/sources-disclosure.css +118 -118
  25. package/src/controls.css +453 -453
  26. package/src/fonts.css +34 -34
  27. package/src/forms/Checkbox.css +114 -114
  28. package/src/forms/RadioGroup.css +115 -115
  29. package/src/goo.css +97 -97
  30. package/src/icon-motion.css +1909 -1909
  31. package/src/layout/ResizablePanel.css +79 -79
  32. package/src/nav/sidebar.css +296 -296
  33. package/src/overlays/date-time-picker.css +238 -238
  34. package/src/overlays/menu.css +195 -195
  35. package/src/overlays/modal.css +167 -167
  36. package/src/overlays/picker-field.css +81 -81
  37. package/src/overlays/pill-filter.css +129 -129
  38. package/src/overlays/reveal.css +56 -56
  39. package/src/overlays/segmented-control.css +185 -185
  40. package/src/overlays/tabs.css +143 -143
  41. package/src/overlays/tooltip.css +126 -126
  42. package/src/scrollbar.css +184 -184
  43. package/src/size.css +179 -179
  44. package/src/status/progress-bar.css +88 -88
  45. package/src/status/spinner.css +33 -33
  46. package/src/status/statusindicator.css +65 -65
  47. package/src/status/steptimeline.css +137 -137
  48. package/src/status/thinkingcube.css +75 -75
  49. package/src/xeno-element.css +60 -60
  50. package/src/xeno-elements.css +71 -71
  51. package/src/xeno-theme.css +112 -112
@@ -1,75 +1,75 @@
1
- /*
2
- * Card — the base surface box. Monochrome shell: background + hairline border + card radius; depth on the
3
- * `elevated` variant is the darker `--xeno-elevated` token plus one soft (hue-free) shadow. The hover-lift
4
- * is opt-in via `data-interactive` and its transform lives behind `prefers-reduced-motion` — the border
5
- * brighten still applies when motion is reduced. Only theme tokens; no literal colours (shadow excepted).
6
- */
7
- .xeno-card {
8
- display: flex;
9
- flex-direction: column;
10
- padding: var(--xeno-chrome-card-pad);
11
- background: var(--xeno-chrome-card-bg);
12
- border: var(--xeno-chrome-card-border);
13
- border-radius: var(--xeno-chrome-card-radius);
14
- color: var(--xeno-text);
15
- }
16
-
17
- .xeno-card[data-variant='elevated'] {
18
- background: var(--xeno-chrome-card-elevated-bg);
19
- box-shadow: var(--xeno-chrome-card-shadow);
20
- }
21
-
22
- /* Body — the default slot. min-width:0 keeps flex-row children from overflowing the card. */
23
- .xeno-card-body {
24
- min-width: 0;
25
- }
26
-
27
- /* Header — a titled slot split from the body by an inset hairline. */
28
- .xeno-card-header {
29
- margin-bottom: 11px;
30
- padding-bottom: 11px;
31
- border-bottom: var(--xeno-chrome-divider);
32
- color: var(--xeno-text);
33
- font-size: 14px;
34
- font-weight: 600;
35
- line-height: 1.2;
36
- }
37
-
38
- /* Footer — a trailing slot, mirror of the header, quieter text. */
39
- .xeno-card-footer {
40
- margin-top: 11px;
41
- padding-top: 11px;
42
- border-top: var(--xeno-chrome-divider);
43
- color: var(--xeno-muted);
44
- font-size: 13px;
45
- line-height: 1.3;
46
- }
47
-
48
- /* Interactive — the sole hover-lift XENO allows. Border brighten is safe without motion. */
49
- .xeno-card[data-interactive='true'] {
50
- cursor: pointer;
51
- }
52
- .xeno-card[data-interactive='true']:hover {
53
- border-color: var(--xeno-muted);
54
- }
55
-
56
- /* Strong lift — the artifact-card affordance: a bigger rise + one soft, hue-free shadow. The shadow is
57
- safe without motion, so it sits outside the reduced-motion guard; only the transform is gated. */
58
- .xeno-card[data-interactive='true'][data-lift='strong']:hover {
59
- box-shadow: var(--xeno-chrome-card-lift);
60
- }
61
-
62
- @media (prefers-reduced-motion: no-preference) {
63
- .xeno-card[data-interactive='true'] {
64
- transition:
65
- transform var(--xeno-dur) var(--xeno-ease),
66
- border-color var(--xeno-dur) ease,
67
- box-shadow var(--xeno-dur) ease;
68
- }
69
- .xeno-card[data-interactive='true']:hover {
70
- transform: translateY(-1px);
71
- }
72
- .xeno-card[data-interactive='true'][data-lift='strong']:hover {
73
- transform: translateY(-3px);
74
- }
75
- }
1
+ /*
2
+ * Card — the base surface box. Monochrome shell: background + hairline border + card radius; depth on the
3
+ * `elevated` variant is the darker `--xeno-elevated` token plus one soft (hue-free) shadow. The hover-lift
4
+ * is opt-in via `data-interactive` and its transform lives behind `prefers-reduced-motion` — the border
5
+ * brighten still applies when motion is reduced. Only theme tokens; no literal colours (shadow excepted).
6
+ */
7
+ .xeno-card {
8
+ display: flex;
9
+ flex-direction: column;
10
+ padding: var(--xeno-chrome-card-pad);
11
+ background: var(--xeno-chrome-card-bg);
12
+ border: var(--xeno-chrome-card-border);
13
+ border-radius: var(--xeno-chrome-card-radius);
14
+ color: var(--xeno-text);
15
+ }
16
+
17
+ .xeno-card[data-variant='elevated'] {
18
+ background: var(--xeno-chrome-card-elevated-bg);
19
+ box-shadow: var(--xeno-chrome-card-shadow);
20
+ }
21
+
22
+ /* Body — the default slot. min-width:0 keeps flex-row children from overflowing the card. */
23
+ .xeno-card-body {
24
+ min-width: 0;
25
+ }
26
+
27
+ /* Header — a titled slot split from the body by an inset hairline. */
28
+ .xeno-card-header {
29
+ margin-bottom: 11px;
30
+ padding-bottom: 11px;
31
+ border-bottom: var(--xeno-chrome-divider);
32
+ color: var(--xeno-text);
33
+ font-size: 14px;
34
+ font-weight: 600;
35
+ line-height: 1.2;
36
+ }
37
+
38
+ /* Footer — a trailing slot, mirror of the header, quieter text. */
39
+ .xeno-card-footer {
40
+ margin-top: 11px;
41
+ padding-top: 11px;
42
+ border-top: var(--xeno-chrome-divider);
43
+ color: var(--xeno-muted-text);
44
+ font-size: 13px;
45
+ line-height: 1.3;
46
+ }
47
+
48
+ /* Interactive — the sole hover-lift XENO allows. Border brighten is safe without motion. */
49
+ .xeno-card[data-interactive='true'] {
50
+ cursor: pointer;
51
+ }
52
+ .xeno-card[data-interactive='true']:hover {
53
+ border-color: var(--xeno-muted);
54
+ }
55
+
56
+ /* Strong lift — the artifact-card affordance: a bigger rise + one soft, hue-free shadow. The shadow is
57
+ safe without motion, so it sits outside the reduced-motion guard; only the transform is gated. */
58
+ .xeno-card[data-interactive='true'][data-lift='strong']:hover {
59
+ box-shadow: var(--xeno-chrome-card-lift);
60
+ }
61
+
62
+ @media (prefers-reduced-motion: no-preference) {
63
+ .xeno-card[data-interactive='true'] {
64
+ transition:
65
+ transform var(--xeno-dur) var(--xeno-ease),
66
+ border-color var(--xeno-dur) ease,
67
+ box-shadow var(--xeno-dur) ease;
68
+ }
69
+ .xeno-card[data-interactive='true']:hover {
70
+ transform: translateY(-1px);
71
+ }
72
+ .xeno-card[data-interactive='true'][data-lift='strong']:hover {
73
+ transform: translateY(-3px);
74
+ }
75
+ }
@@ -1,103 +1,103 @@
1
- /*
2
- * Chip — a small selectable/removable token. SQUARE (radius-control), 1px hairline border, monochrome
3
- * shell. State is driven by `data-*` on the root; size metrics arrive as inline `--xeno-h/-padx/-gap/
4
- * -font` vars set from the size tokens, so a size can never drift from the data. No literal colours.
5
- */
6
- .xeno-chip {
7
- display: inline-flex;
8
- align-items: center;
9
- height: var(--xeno-h, 32px);
10
- max-width: 100%;
11
- color: var(--xeno-text);
12
- background: transparent;
13
- border: 1px solid var(--xeno-border);
14
- border-radius: var(--xeno-radius-control);
15
- font-family: inherit;
16
- font-size: var(--xeno-font, 14px);
17
- font-weight: 500;
18
- line-height: 1;
19
- white-space: nowrap;
20
- user-select: none;
21
- overflow: hidden; /* clips inner hover fills to the rounded corners */
22
- }
23
- .xeno-chip[data-selection='on'] {
24
- background: var(--xeno-control);
25
- }
26
- .xeno-chip[data-availability='disabled'] {
27
- opacity: 0.4;
28
- }
29
-
30
- /* Body — the label region: a plain span, or a toggle <button> when selectable. */
31
- .xeno-chip-body {
32
- display: inline-flex;
33
- align-items: center;
34
- gap: var(--xeno-gap, 6px);
35
- height: 100%;
36
- min-width: 0;
37
- padding: 0 var(--xeno-padx, 12px);
38
- color: inherit;
39
- background: transparent;
40
- border: none;
41
- font: inherit;
42
- line-height: 1;
43
- }
44
- .xeno-chip[data-removable='true'] .xeno-chip-body {
45
- padding-right: var(--xeno-gap, 6px);
46
- }
47
- .xeno-chip-label {
48
- overflow: hidden;
49
- text-overflow: ellipsis;
50
- white-space: nowrap;
51
- }
52
-
53
- /* Selectable body toggles the selection axis (aria-pressed); hover/press are CSS, not attributes. */
54
- .xeno-chip-select {
55
- cursor: pointer;
56
- transition:
57
- background var(--xeno-dur) ease,
58
- color var(--xeno-dur) ease;
59
- }
60
- .xeno-chip-select:hover:not(:disabled) {
61
- background: var(--xeno-hover);
62
- }
63
- .xeno-chip-select:focus-visible {
64
- outline: none;
65
- box-shadow: inset 0 0 0 2px var(--xeno-border);
66
- }
67
- .xeno-chip-select:disabled {
68
- cursor: not-allowed;
69
- }
70
-
71
- /* Remove — a bare 'x' button; muted ink that resolves to text on hover. */
72
- .xeno-chip-remove {
73
- display: inline-flex;
74
- align-items: center;
75
- justify-content: center;
76
- height: 100%;
77
- padding: 0 6px;
78
- color: var(--xeno-muted);
79
- background: transparent;
80
- border: none;
81
- cursor: pointer;
82
- transition:
83
- background var(--xeno-dur) ease,
84
- color var(--xeno-dur) ease;
85
- }
86
- .xeno-chip-remove:hover:not(:disabled) {
87
- color: var(--xeno-text);
88
- background: var(--xeno-hover);
89
- }
90
- .xeno-chip-remove:focus-visible {
91
- outline: none;
92
- box-shadow: inset 0 0 0 2px var(--xeno-border);
93
- }
94
- .xeno-chip-remove:disabled {
95
- cursor: not-allowed;
96
- }
97
-
98
- @media (prefers-reduced-motion: reduce) {
99
- .xeno-chip-select,
100
- .xeno-chip-remove {
101
- transition: none;
102
- }
103
- }
1
+ /*
2
+ * Chip — a small selectable/removable token. SQUARE (radius-control), 1px hairline border, monochrome
3
+ * shell. State is driven by `data-*` on the root; size metrics arrive as inline `--xeno-h/-padx/-gap/
4
+ * -font` vars set from the size tokens, so a size can never drift from the data. No literal colours.
5
+ */
6
+ .xeno-chip {
7
+ display: inline-flex;
8
+ align-items: center;
9
+ height: var(--xeno-h, 32px);
10
+ max-width: 100%;
11
+ color: var(--xeno-text);
12
+ background: transparent;
13
+ border: 1px solid var(--xeno-border);
14
+ border-radius: var(--xeno-radius-control);
15
+ font-family: inherit;
16
+ font-size: var(--xeno-font, 14px);
17
+ font-weight: 500;
18
+ line-height: 1;
19
+ white-space: nowrap;
20
+ user-select: none;
21
+ overflow: hidden; /* clips inner hover fills to the rounded corners */
22
+ }
23
+ .xeno-chip[data-selection='on'] {
24
+ background: var(--xeno-control);
25
+ }
26
+ .xeno-chip[data-availability='disabled'] {
27
+ opacity: 0.4;
28
+ }
29
+
30
+ /* Body — the label region: a plain span, or a toggle <button> when selectable. */
31
+ .xeno-chip-body {
32
+ display: inline-flex;
33
+ align-items: center;
34
+ gap: var(--xeno-gap, 6px);
35
+ height: 100%;
36
+ min-width: 0;
37
+ padding: 0 var(--xeno-padx, 12px);
38
+ color: inherit;
39
+ background: transparent;
40
+ border: none;
41
+ font: inherit;
42
+ line-height: 1;
43
+ }
44
+ .xeno-chip[data-removable='true'] .xeno-chip-body {
45
+ padding-right: var(--xeno-gap, 6px);
46
+ }
47
+ .xeno-chip-label {
48
+ overflow: hidden;
49
+ text-overflow: ellipsis;
50
+ white-space: nowrap;
51
+ }
52
+
53
+ /* Selectable body toggles the selection axis (aria-pressed); hover/press are CSS, not attributes. */
54
+ .xeno-chip-select {
55
+ cursor: pointer;
56
+ transition:
57
+ background var(--xeno-dur) ease,
58
+ color var(--xeno-dur) ease;
59
+ }
60
+ .xeno-chip-select:hover:not(:disabled) {
61
+ background: var(--xeno-hover);
62
+ }
63
+ .xeno-chip-select:focus-visible {
64
+ outline: none;
65
+ box-shadow: inset 0 0 0 2px var(--xeno-border);
66
+ }
67
+ .xeno-chip-select:disabled {
68
+ cursor: not-allowed;
69
+ }
70
+
71
+ /* Remove — a bare 'x' button; muted ink that resolves to text on hover. */
72
+ .xeno-chip-remove {
73
+ display: inline-flex;
74
+ align-items: center;
75
+ justify-content: center;
76
+ height: 100%;
77
+ padding: 0 6px;
78
+ color: var(--xeno-muted);
79
+ background: transparent;
80
+ border: none;
81
+ cursor: pointer;
82
+ transition:
83
+ background var(--xeno-dur) ease,
84
+ color var(--xeno-dur) ease;
85
+ }
86
+ .xeno-chip-remove:hover:not(:disabled) {
87
+ color: var(--xeno-text);
88
+ background: var(--xeno-hover);
89
+ }
90
+ .xeno-chip-remove:focus-visible {
91
+ outline: none;
92
+ box-shadow: inset 0 0 0 2px var(--xeno-border);
93
+ }
94
+ .xeno-chip-remove:disabled {
95
+ cursor: not-allowed;
96
+ }
97
+
98
+ @media (prefers-reduced-motion: reduce) {
99
+ .xeno-chip-select,
100
+ .xeno-chip-remove {
101
+ transition: none;
102
+ }
103
+ }
@@ -1,132 +1,132 @@
1
- /*
2
- * ListRow — a selectable / actionable list row. CSS-first: the `selection` and `availability` axes
3
- * arrive as `data-*` attributes on the root; interaction (hover / press / focus) is pure CSS. Depth is
4
- * hairline borders only — monochrome shell, no coloured glow. Only the theme tokens from
5
- * `xeno-theme.css` are used; no literal colours.
6
- */
7
-
8
- .xeno-list-row {
9
- display: flex;
10
- align-items: center;
11
- gap: 12px;
12
- width: 100%;
13
- box-sizing: border-box;
14
- min-width: 0;
15
- margin: 0;
16
- padding: 8px 12px;
17
- color: var(--xeno-text);
18
- text-align: left;
19
- background: transparent;
20
- border: 1px solid transparent;
21
- border-radius: var(--xeno-radius-md);
22
- cursor: default;
23
- transition:
24
- background var(--xeno-dur) ease,
25
- border-color var(--xeno-dur) ease;
26
- }
27
-
28
- /* Reset the native <button> chrome so a button row reads exactly like a div row. */
29
- button.xeno-list-row {
30
- font: inherit;
31
- appearance: none;
32
- -webkit-appearance: none;
33
- }
34
-
35
- /* Interactive rows (a real button, or a div carrying role=button / role=option) invite the pointer. */
36
- button.xeno-list-row:not(:disabled),
37
- .xeno-list-row[role='button'],
38
- .xeno-list-row[role='option'] {
39
- cursor: pointer;
40
- }
41
-
42
- /* Hover — only interactive, enabled, unselected rows lift onto --xeno-hover. */
43
- button.xeno-list-row:not(:disabled):hover,
44
- .xeno-list-row[role='button']:not([aria-disabled='true']):hover,
45
- .xeno-list-row[role='option']:not([aria-disabled='true']):hover {
46
- background: var(--xeno-hover);
47
- }
48
-
49
- /* Selection wins over hover — a selected row stays on the solid control fill. */
50
- .xeno-list-row[data-selection='on'],
51
- button.xeno-list-row[data-selection='on']:not(:disabled):hover,
52
- .xeno-list-row[role='button'][data-selection='on']:hover,
53
- .xeno-list-row[role='option'][data-selection='on']:hover {
54
- background: var(--xeno-control);
55
- }
56
-
57
- .xeno-list-row:focus-visible {
58
- outline: none;
59
- box-shadow:
60
- 0 0 0 2px var(--xeno-canvas),
61
- 0 0 0 4px var(--xeno-border);
62
- }
63
-
64
- button.xeno-list-row:active:not(:disabled) {
65
- transform: scale(0.995);
66
- }
67
-
68
- .xeno-list-row:disabled,
69
- .xeno-list-row[data-availability='disabled'] {
70
- opacity: 0.4;
71
- cursor: not-allowed;
72
- }
73
-
74
- /* ── Slots ───────────────────────────────────────────────────────────────────────── */
75
-
76
- /* Leading — glyph / avatar. Fixed, never shrinks. Its own children keep their intrinsic colour. */
77
- .xeno-list-row-leading {
78
- display: inline-flex;
79
- align-items: center;
80
- justify-content: center;
81
- flex: none;
82
- color: var(--xeno-muted);
83
- }
84
-
85
- /* Middle — the truncation column. min-width:0 lets the title/subtitle ellipsize inside the flex row. */
86
- .xeno-list-row-body {
87
- display: flex;
88
- flex: 1 1 auto;
89
- flex-direction: column;
90
- gap: 2px;
91
- min-width: 0;
92
- }
93
-
94
- .xeno-list-row-title {
95
- overflow: hidden;
96
- font-size: 14px;
97
- font-weight: 500;
98
- line-height: 1.3;
99
- color: var(--xeno-text);
100
- white-space: nowrap;
101
- text-overflow: ellipsis;
102
- }
103
-
104
- .xeno-list-row-subtitle {
105
- overflow: hidden;
106
- font-size: 12px;
107
- font-weight: 400;
108
- line-height: 1.3;
109
- color: var(--xeno-muted);
110
- white-space: nowrap;
111
- text-overflow: ellipsis;
112
- }
113
-
114
- /* Trailing — meta / actions pinned to the end. Fixed, never shrinks. */
115
- .xeno-list-row-trailing {
116
- display: inline-flex;
117
- align-items: center;
118
- gap: 6px;
119
- flex: none;
120
- margin-left: auto;
121
- font-size: 12px;
122
- color: var(--xeno-muted);
123
- }
124
-
125
- @media (prefers-reduced-motion: reduce) {
126
- .xeno-list-row {
127
- transition: none;
128
- }
129
- button.xeno-list-row:active:not(:disabled) {
130
- transform: none;
131
- }
132
- }
1
+ /*
2
+ * ListRow — a selectable / actionable list row. CSS-first: the `selection` and `availability` axes
3
+ * arrive as `data-*` attributes on the root; interaction (hover / press / focus) is pure CSS. Depth is
4
+ * hairline borders only — monochrome shell, no coloured glow. Only the theme tokens from
5
+ * `xeno-theme.css` are used; no literal colours.
6
+ */
7
+
8
+ .xeno-list-row {
9
+ display: flex;
10
+ align-items: center;
11
+ gap: 12px;
12
+ width: 100%;
13
+ box-sizing: border-box;
14
+ min-width: 0;
15
+ margin: 0;
16
+ padding: 8px 12px;
17
+ color: var(--xeno-text);
18
+ text-align: left;
19
+ background: transparent;
20
+ border: 1px solid transparent;
21
+ border-radius: var(--xeno-radius-md);
22
+ cursor: default;
23
+ transition:
24
+ background var(--xeno-dur) ease,
25
+ border-color var(--xeno-dur) ease;
26
+ }
27
+
28
+ /* Reset the native <button> chrome so a button row reads exactly like a div row. */
29
+ button.xeno-list-row {
30
+ font: inherit;
31
+ appearance: none;
32
+ -webkit-appearance: none;
33
+ }
34
+
35
+ /* Interactive rows (a real button, or a div carrying role=button / role=option) invite the pointer. */
36
+ button.xeno-list-row:not(:disabled),
37
+ .xeno-list-row[role='button'],
38
+ .xeno-list-row[role='option'] {
39
+ cursor: pointer;
40
+ }
41
+
42
+ /* Hover — only interactive, enabled, unselected rows lift onto --xeno-hover. */
43
+ button.xeno-list-row:not(:disabled):hover,
44
+ .xeno-list-row[role='button']:not([aria-disabled='true']):hover,
45
+ .xeno-list-row[role='option']:not([aria-disabled='true']):hover {
46
+ background: var(--xeno-hover);
47
+ }
48
+
49
+ /* Selection wins over hover — a selected row stays on the solid control fill. */
50
+ .xeno-list-row[data-selection='on'],
51
+ button.xeno-list-row[data-selection='on']:not(:disabled):hover,
52
+ .xeno-list-row[role='button'][data-selection='on']:hover,
53
+ .xeno-list-row[role='option'][data-selection='on']:hover {
54
+ background: var(--xeno-control);
55
+ }
56
+
57
+ .xeno-list-row:focus-visible {
58
+ outline: none;
59
+ box-shadow:
60
+ 0 0 0 2px var(--xeno-canvas),
61
+ 0 0 0 4px var(--xeno-border);
62
+ }
63
+
64
+ button.xeno-list-row:active:not(:disabled) {
65
+ transform: scale(0.995);
66
+ }
67
+
68
+ .xeno-list-row:disabled,
69
+ .xeno-list-row[data-availability='disabled'] {
70
+ opacity: 0.4;
71
+ cursor: not-allowed;
72
+ }
73
+
74
+ /* ── Slots ───────────────────────────────────────────────────────────────────────── */
75
+
76
+ /* Leading — glyph / avatar. Fixed, never shrinks. Its own children keep their intrinsic colour. */
77
+ .xeno-list-row-leading {
78
+ display: inline-flex;
79
+ align-items: center;
80
+ justify-content: center;
81
+ flex: none;
82
+ color: var(--xeno-muted);
83
+ }
84
+
85
+ /* Middle — the truncation column. min-width:0 lets the title/subtitle ellipsize inside the flex row. */
86
+ .xeno-list-row-body {
87
+ display: flex;
88
+ flex: 1 1 auto;
89
+ flex-direction: column;
90
+ gap: 2px;
91
+ min-width: 0;
92
+ }
93
+
94
+ .xeno-list-row-title {
95
+ overflow: hidden;
96
+ font-size: 14px;
97
+ font-weight: 500;
98
+ line-height: 1.3;
99
+ color: var(--xeno-text);
100
+ white-space: nowrap;
101
+ text-overflow: ellipsis;
102
+ }
103
+
104
+ .xeno-list-row-subtitle {
105
+ overflow: hidden;
106
+ font-size: 12px;
107
+ font-weight: 400;
108
+ line-height: 1.3;
109
+ color: var(--xeno-muted);
110
+ white-space: nowrap;
111
+ text-overflow: ellipsis;
112
+ }
113
+
114
+ /* Trailing — meta / actions pinned to the end. Fixed, never shrinks. */
115
+ .xeno-list-row-trailing {
116
+ display: inline-flex;
117
+ align-items: center;
118
+ gap: 6px;
119
+ flex: none;
120
+ margin-left: auto;
121
+ font-size: 12px;
122
+ color: var(--xeno-muted);
123
+ }
124
+
125
+ @media (prefers-reduced-motion: reduce) {
126
+ .xeno-list-row {
127
+ transition: none;
128
+ }
129
+ button.xeno-list-row:active:not(:disabled) {
130
+ transform: none;
131
+ }
132
+ }