@crewlethq/ui 0.75.4 → 0.76.1

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 (145) hide show
  1. package/README.md +21 -1
  2. package/dist/AgentHandoffPanel/index.js +3 -3
  3. package/dist/ArrangeList/index.js +3 -3
  4. package/dist/ArtifactBar/index.js +4 -4
  5. package/dist/ArtifactCard/index.js +3 -3
  6. package/dist/AttachmentChip/index.js +3 -3
  7. package/dist/AvatarPicker/index.js +5 -5
  8. package/dist/BriefCard/index.js +4 -4
  9. package/dist/ChangeList/index.js +3 -3
  10. package/dist/Checkbox/index.js +2 -2
  11. package/dist/ChoiceCards/ChoiceCards.d.ts +80 -0
  12. package/dist/ChoiceCards/index.d.ts +3 -0
  13. package/dist/ChoiceCards/index.js +3 -0
  14. package/dist/ChoiceCards-HAGJMK6Q.css +318 -0
  15. package/dist/ChoiceList/ChoiceList.d.ts +10 -1
  16. package/dist/ChoiceList/index.js +1 -1
  17. package/dist/{ChoiceList-Q772QLFX.css → ChoiceList-JN4PPNFW.css} +22 -1
  18. package/dist/ColorField/index.js +2 -2
  19. package/dist/Combobox/index.js +2 -2
  20. package/dist/CommandPalette/index.js +2 -2
  21. package/dist/CommentThread/index.js +3 -3
  22. package/dist/Composer/index.js +2 -2
  23. package/dist/ConfirmField/index.js +2 -2
  24. package/dist/ContrastBadge/index.js +3 -3
  25. package/dist/ContrastHelp/index.js +4 -4
  26. package/dist/DataGrid/index.js +3 -3
  27. package/dist/DataTable/index.js +3 -3
  28. package/dist/DataView/index.js +5 -5
  29. package/dist/DatasetSource/index.js +3 -3
  30. package/dist/DeepWorkCard/index.js +5 -5
  31. package/dist/DocTabsEditor/index.js +2 -2
  32. package/dist/EditingStatus/index.js +2 -2
  33. package/dist/{Editor-VW5CRDKY.js → Editor-BXOWAV46.js} +1 -1
  34. package/dist/EntityList/index.js +4 -4
  35. package/dist/HuePicker/index.js +4 -4
  36. package/dist/InlineEditor/index.js +3 -3
  37. package/dist/Input/index.js +1 -1
  38. package/dist/{Input-GEFFMKBS.css → Input-WSMB7JG2.css} +6 -1
  39. package/dist/ListInput/index.js +2 -2
  40. package/dist/LogoSlot/index.js +4 -4
  41. package/dist/MarkdownEditor/index.js +4 -4
  42. package/dist/MergeConflicts/index.js +4 -4
  43. package/dist/OpenInAppCard/index.js +5 -5
  44. package/dist/PricingCard/index.js +3 -3
  45. package/dist/PrincipalPicker/index.js +3 -3
  46. package/dist/ProposalCard/index.js +7 -7
  47. package/dist/RefusalCard/index.js +4 -4
  48. package/dist/ResultTableCard/index.js +4 -4
  49. package/dist/ReviewList/index.js +3 -3
  50. package/dist/RichEditor/index.js +1 -1
  51. package/dist/RunChip/index.js +3 -3
  52. package/dist/SegmentedControl/index.js +3 -3
  53. package/dist/SettingsTable/index.js +2 -2
  54. package/dist/SetupWizard/SetupWizard.d.ts +32 -7
  55. package/dist/SetupWizard/index.js +3 -3
  56. package/dist/{SetupWizard-WDOFFNIM.css → SetupWizard-Y2PFPBCZ.css} +151 -4
  57. package/dist/ShareDialog/index.js +1 -1
  58. package/dist/SheetColumnEditor/index.js +7 -7
  59. package/dist/SheetGrid/index.js +3 -3
  60. package/dist/StatusCard/index.js +3 -3
  61. package/dist/StatusDot/StatusDot.d.ts +7 -0
  62. package/dist/StatusDot/index.js +1 -1
  63. package/dist/{StatusDot-7OMCQCZF.css → StatusDot-FJND3CZY.css} +9 -2
  64. package/dist/Stepper/index.js +2 -2
  65. package/dist/{Stepper-32INSYF7.css → Stepper-YQBNYCIS.css} +31 -8
  66. package/dist/Switcher/index.js +2 -2
  67. package/dist/TableCells/index.js +3 -3
  68. package/dist/Tabs/index.js +2 -2
  69. package/dist/Tag/index.js +2 -2
  70. package/dist/TagsInput/index.js +3 -3
  71. package/dist/ThemeGallery/index.js +3 -3
  72. package/dist/ThemeSwitcher/index.js +4 -4
  73. package/dist/TimeRangePicker/index.js +5 -5
  74. package/dist/TimeWindowPicker/index.js +2 -2
  75. package/dist/ValueField/index.js +3 -3
  76. package/dist/VersionCompare/index.js +4 -4
  77. package/dist/{chunk-3N36CYTU.js → chunk-2PIH6JHJ.js} +1 -1
  78. package/dist/{chunk-RNXYNXKN.js → chunk-2STOSJT3.js} +2 -2
  79. package/dist/{chunk-P64MIA7C.js → chunk-3JXX5S3Z.js} +2 -2
  80. package/dist/{chunk-H32JW6CZ.js → chunk-45M73G5A.js} +1 -1
  81. package/dist/{chunk-6C6YK75K.js → chunk-57AKYFML.js} +1 -1
  82. package/dist/{chunk-4KSCGXLJ.js → chunk-5BOFC5OP.js} +2 -2
  83. package/dist/{chunk-NXNLAZPK.js → chunk-5CEQ3HLH.js} +1 -1
  84. package/dist/chunk-5EFLA6BW.js +205 -0
  85. package/dist/{chunk-VLGGLLKH.js → chunk-624X3RVA.js} +1 -1
  86. package/dist/{chunk-LWFOX3PB.js → chunk-63GBSOLU.js} +1 -1
  87. package/dist/{chunk-ZUBFVK6B.js → chunk-6EWNMSS4.js} +1 -1
  88. package/dist/{chunk-7IW3P6FH.js → chunk-6G6WHTC7.js} +1 -1
  89. package/dist/{chunk-JXJNX7QQ.js → chunk-6LXS3X7A.js} +1 -1
  90. package/dist/{chunk-NQABJAUY.js → chunk-6RW2MUTY.js} +15 -8
  91. package/dist/{chunk-IPUEVTRJ.js → chunk-6THG44LI.js} +1 -1
  92. package/dist/{chunk-KWLH45YC.js → chunk-7PJFUQMY.js} +1 -1
  93. package/dist/{chunk-AB6F5YXX.js → chunk-ADGNISEI.js} +1 -1
  94. package/dist/{chunk-WQRSZMN4.js → chunk-BBD7PTBG.js} +1 -1
  95. package/dist/{chunk-TYIHFDMP.js → chunk-BKXUZCZX.js} +1 -1
  96. package/dist/{chunk-NU5RUT7Y.js → chunk-CFTBESST.js} +1 -1
  97. package/dist/{chunk-WIATRXEJ.js → chunk-CYQBDZ6M.js} +1 -1
  98. package/dist/{chunk-ZKIEOWR6.js → chunk-D7G2QGET.js} +1 -1
  99. package/dist/{chunk-3EY5SEGH.js → chunk-DSMQTED5.js} +3 -3
  100. package/dist/{chunk-5OFRDX66.js → chunk-E35HYQMK.js} +1 -1
  101. package/dist/{chunk-ETSF7SU6.js → chunk-FB3TCNQP.js} +1 -1
  102. package/dist/{chunk-BWAUL5MB.js → chunk-GHQG4VZA.js} +3 -3
  103. package/dist/{chunk-PIAGTZY3.js → chunk-GPFYITOF.js} +1 -1
  104. package/dist/{chunk-R6K62EXV.js → chunk-H22SR76M.js} +1 -1
  105. package/dist/{chunk-IO374IX6.js → chunk-HQOEAUDJ.js} +1 -1
  106. package/dist/{chunk-J5WWMC35.js → chunk-HWPP22XG.js} +2 -2
  107. package/dist/{chunk-BWFJB5JD.js → chunk-HZDI2CCS.js} +1 -1
  108. package/dist/{chunk-3WN5HUAK.js → chunk-I3G5FJ54.js} +1 -1
  109. package/dist/{chunk-TKBFRGUV.js → chunk-IPBSCJIZ.js} +1 -1
  110. package/dist/{chunk-ERFXYGRF.js → chunk-IQMWMT4V.js} +2 -2
  111. package/dist/{chunk-GT2OG2XM.js → chunk-JMTBK55P.js} +2 -2
  112. package/dist/{chunk-FURJVZCT.js → chunk-LCZULGMB.js} +1 -1
  113. package/dist/{chunk-PBCNINMB.js → chunk-LMQG3VPG.js} +1 -1
  114. package/dist/chunk-MKFEVHSR.js +83 -0
  115. package/dist/{chunk-JPQSVECQ.js → chunk-MP7DGKB6.js} +1 -1
  116. package/dist/{chunk-DO4J4TBZ.js → chunk-NKBKQEKS.js} +1 -1
  117. package/dist/{chunk-4MVLED6E.js → chunk-OCGIQGYJ.js} +2 -2
  118. package/dist/{chunk-EOXI3CRG.js → chunk-OOLMTUCO.js} +1 -1
  119. package/dist/{chunk-YUHKRRWO.js → chunk-OWHETZIU.js} +1 -1
  120. package/dist/{chunk-VWMB3CJN.js → chunk-PMBJMNBX.js} +1 -1
  121. package/dist/{chunk-ADSOY7UM.js → chunk-RIAPCEB5.js} +1 -1
  122. package/dist/{chunk-HJBHP66O.js → chunk-RJWO2PC5.js} +2 -2
  123. package/dist/{chunk-NTQPJJAN.js → chunk-RTMNULTL.js} +1 -1
  124. package/dist/{chunk-AQIFKLXY.js → chunk-SK5SLGZH.js} +1 -1
  125. package/dist/{chunk-7LSSYHOT.js → chunk-T24HBEU6.js} +1 -1
  126. package/dist/{chunk-W5AXJWOW.js → chunk-TJIBTHWJ.js} +1 -1
  127. package/dist/{chunk-TWJERLT3.js → chunk-TKUAGWNR.js} +1 -1
  128. package/dist/{chunk-JVII5264.js → chunk-TPIMOGOZ.js} +1 -1
  129. package/dist/{chunk-RQHN4J5P.js → chunk-UYF2B2TH.js} +1 -1
  130. package/dist/{chunk-I5OIKDX2.js → chunk-VERJCAJ4.js} +1 -1
  131. package/dist/{chunk-PKVWCKS2.js → chunk-VG3SIJ3M.js} +1 -1
  132. package/dist/{chunk-HEIOLITO.js → chunk-WK4FQPYW.js} +1 -1
  133. package/dist/{chunk-4RKTZSEW.js → chunk-WL4TC4YZ.js} +1 -1
  134. package/dist/{chunk-4DL2BQJE.js → chunk-YL7AR6PR.js} +1 -1
  135. package/dist/{chunk-LQWORB5B.js → chunk-YQDRJZ5J.js} +1 -1
  136. package/dist/{chunk-OOPOQKZZ.js → chunk-ZKLEAK7M.js} +1 -1
  137. package/dist/{chunk-KAA3GFTP.js → chunk-ZMIVQGAB.js} +3 -3
  138. package/dist/{chunk-OXEOC64K.js → chunk-ZW5SRQ5M.js} +1 -1
  139. package/dist/generated/index.d.ts +1 -0
  140. package/dist/generated/index.js +80 -79
  141. package/dist/index.js +80 -79
  142. package/dist/styles.css +545 -21
  143. package/package.json +3 -3
  144. package/dist/chunk-SBHAKX2T.js +0 -124
  145. /package/dist/{chunk-TWM6G2I2.js → chunk-PCBPDMNU.js} +0 -0
package/README.md CHANGED
@@ -42,7 +42,7 @@ import { Button } from '@crewlethq/ui/Button'; // 8 KB of CSS, not 182 KB
42
42
  | ---- | ---------- |
43
43
  | Actions | `Button` and `ButtonLink` (variants `primary`, `secondary`, `outline`, `ghost`, `accent`, `danger`; sizes `small`, `medium`, `large`; shapes `square`, `pill`), `IconButton`, `Menu`, `Link`, `Copyable`, `CopyButton`, `DownloadButton`, `Kbd` |
44
44
  | Content and text | `Text` (the eight registers and `inherit`), `Prose`, `Statement`, `Document`, `DocumentTitle` and `DocumentTitleField`, the Markdown grammar (`parseMarkdown`, `parseMarkdownSpans`, `markdownInline`, `safeHref` and the line readers, with no React in it), `Highlight`, `InlineCode`, `List` and `ListItem`, `DescriptionList`, `Timeline`, `RelativeTime`, `Note`, `Divider` |
45
- | Forms | `Input`, `Textarea`, `Label`, `FormField`, `FormRow`, `Checkbox`, `CheckboxGroup`, `ConfirmField`, `Select`, `Tag`, `TagsInput`, `DateTimePicker`, `TimeWindowPicker`, `TimeRangePicker`, `ImageUpload`, `CharacterPicker`, `HuePicker`, `AvatarPicker`, `FactList`, `ChoiceList`, `Composer`, `MarkdownEditor`, `RichEditor` (with `preloadRichEditor`, and the round trip `roundTripMarkdown` and `canonicalMarkdown` from `@crewlethq/ui/RichEditorMarkdown`) |
45
+ | Forms | `Input`, `Textarea`, `Label`, `FormField`, `FormRow`, `Checkbox`, `CheckboxGroup`, `ConfirmField`, `Select`, `Tag`, `TagsInput`, `DateTimePicker`, `TimeWindowPicker`, `TimeRangePicker`, `ImageUpload`, `CharacterPicker`, `HuePicker`, `AvatarPicker`, `FactList`, `ChoiceList`, `ChoiceCards`, `Composer`, `MarkdownEditor`, `RichEditor` (with `preloadRichEditor`, and the round trip `roundTripMarkdown` and `canonicalMarkdown` from `@crewlethq/ui/RichEditorMarkdown`) |
46
46
  | Data display | `DataGrid` (with `useDataGridDriving`, `useAnyDataGridDriving` and `countDataGridRows`), `DataTable`, `CopyableCell`, `Table`, the read-only table cells (`TableKeyCell`, `TableNumberCell`, `TableDateCell`, `TableEntityCell`, `TableStatusCell`, `TableTagsCell`, `TableTextCell`), `StatCard`, `Meter`, `SegmentedMeter`, `Stepper`, `PricingCard`, `Avatar`, `AvatarStack`, `EntityChip`, `CodeBlock`, `QrCode`, `ObjectHeader`, `FactLine`, `SetByLine`, `Skeleton`, `Eyebrow`, `StatGroup`, `PinTile`, `IconTile`, `SettingsTable` (with `SettingsTable.Row`), `CatalogTile` |
47
47
  | List screens | `DataView`, and its parts on their own: `DataViewToolbar`, `FilterAxisBar`, `FilterAxisChip`, `FilterPill`, plus the filter model (`filterDefsFromColumns`, `applyColumnFilters`, `filterPredicate`, `blankFilterValue`) |
48
48
  | Feedback and overlays | `Callout`, `ConflictNote`, `EditingStatus`, `Toaster`, `Modal` (with `variant="sheet"`), `ConfirmModal`, `CommandPalette`, `Tooltip`, `Popover`, `Announcer` |
@@ -205,6 +205,26 @@ places:
205
205
  - A surface that pans or zooms dispatches `LAYER_REPOSITION_EVENT` on its host
206
206
  when the content beneath it moves, and every panel anchored inside follows.
207
207
 
208
+ ## Changes in 0.76.1
209
+
210
+ **`SetupWizard`'s row of steps on a phone has a name of its own.** While the row hides steps it takes the keyboard to scroll, as a group named by the new `labels.stepsRow` ("All steps, scrolls sideways"), rather than by `stepsLabel` again, which already names the navigation around it and the list inside it.
211
+
212
+ **The row's edge fade never dims a focus ring.** The row's ring is drawn by a frame around it (`crewlet-setup-wizard__steps-frame`), which the fade does not reach, and a step that takes focus is scrolled clear of the faded edges, by the fade's width (the row's `scroll-padding-inline`), in either direction.
213
+
214
+ ## Changes in 0.76.0
215
+
216
+ **`ChoiceCards`, choices a person compares.** One answer from a few, each a card of the same lines: a picture (`media`), the choice's name (`label`) and its `tag`; one line of `description`; and a few `facts`, one to a line, each a name and a value, with an optional `tone` drawn as a `StatusDot` beside words that say the state. Cards side by side share every line on a CSS subgrid, so their facts stand level; where subgrid is not supported each card is a plain column. A description row is held only when some card has one, and a card without one keeps it empty. `minCardWidth` is the narrowest a card stands (`md`, 20rem, the default, or `lg`, 24rem), which decides how many share a row. The radios are native, so the arrow keys move the choice; each is named by its choice and tag alone and described by the rest of the card. A card that cannot be chosen (`disabled`) stands on the page's ground with a quiet radio and a muted picture, and says why in `disabledReason`, on its last line or, with `disabledReasonAt`, in place of a fact's value, read with the fact's name. A chosen card lays the selected tint over its raised surface inside an edge in the accent's ink. Under forced colours the radio is the platform's own, the chosen card's edge the system's selection colour and an unavailable card the system's disabled ink. The types are `ChoiceCardsProps`, `ChoiceCardOption`, `ChoiceCardFact` and `ChoiceCardsMinWidth`.
217
+
218
+ **`ChoiceList` lets one choice be unavailable.** An option takes `disabled` and `disabledReason`: the choice is skipped by the arrows, keeps its words at full contrast, and gives its reason on its detail line in the warning ink, read once, as its radio's description. Element ids now follow each option's position rather than its `value`, so a value holding a space no longer breaks its radio's name.
219
+
220
+ **`SetupWizard` takes `primaryAction`, and fills its screen.** `primaryAction` is the step's primary action, last in the footer row: its box keeps one width on every step (at least 8rem), so "Create" on the last step moves nothing beside it, and on a phone it takes the whole width of the footer's second row. `footer` is now optional and holds the step's other actions. In a flex column as tall as the view (an `AppShell`'s content) the wizard grows into the room and the footer rests at the bottom on every step, a phone included. On a phone the row of steps is one row that scrolls sideways, with the current step brought to its middle (in either writing direction), an edge that hides steps faded out, and the row reachable by the keyboard and named while it overflows; a thin bar under the position line says how far along. What to change: on a phone the whole-width row now follows `primaryAction` by name rather than whichever action came last, so move the primary action from `footer` to `primaryAction`.
221
+
222
+ **`Stepper`'s pickable pill is pressed through the 24px target floor**: an invisible band of `--size-target-min` centred on the 20px pill, so the pill does not grow. The pill's height is declared once as `--crewlet-stepper-pill-height`, a supported read-only property for a layout that sizes around a pill.
223
+
224
+ **`StatusDot`'s size is `--crewlet-status-dot-size`**, declared once, a supported read-only property for a layout that centres the dot on a wrapped line by the same measure.
225
+
226
+ **A `FormField`'s `labelAction` keeps its own case and letter spacing**, rather than the label's capitals and wide tracking.
227
+
208
228
  ## Changes in 0.73.0
209
229
 
210
230
  **`RecordChip`, a record named inside a sentence.** A button at the text's own size on its baseline, exactly one line of the text it sits in tall (`line-height: calc(1lh - 2px)` with its hairline, standing at the line's top so a key's mono text never moves the line), drawing the record's key in mono (`code`) or its name (`label`), with an optional kind glyph. It opens a **peek**: a `Popover` dialog named after the record (its key, then its name), with one line of context (`meta`), a body for its facts or rows (`children`) and a foot for the host's own link and downloads (`actions`). Focus moves into the peek; Escape, Tab out or a control that closes it returns focus to the chip, and a press outside closes it. A `code` chip's accessible name is the key and then the record's name ("GOP-11, Android 12 crash ..."); a `label` chip is named by its label alone, and carries its whole text as its `title`, so a pointer reader can read one cut at the line's end.
@@ -1,12 +1,12 @@
1
- export { AGENT_HANDOFF_LABELS, AgentHandoffPanel } from '../chunk-6C6YK75K.js';
1
+ export { AGENT_HANDOFF_LABELS, AgentHandoffPanel } from '../chunk-57AKYFML.js';
2
2
  import '../chunk-4MJN2KN6.js';
3
3
  import '../chunk-WFCWPXJN.js';
4
- import '../chunk-5OFRDX66.js';
4
+ import '../chunk-E35HYQMK.js';
5
5
  import '../chunk-222VILSP.js';
6
6
  import '../chunk-FFGEJUAP.js';
7
7
  import '../chunk-CLJGFOTV.js';
8
8
  import '../chunk-ANTAQAR4.js';
9
- import '../chunk-ETSF7SU6.js';
9
+ import '../chunk-FB3TCNQP.js';
10
10
  import '../chunk-PGCLHYVP.js';
11
11
  import '../chunk-IVTQWOXV.js';
12
12
  import '../chunk-MLDTLZAO.js';
@@ -1,8 +1,8 @@
1
- export { ArrangeList, DEFAULT_ARRANGE_LABELS } from '../chunk-TWJERLT3.js';
1
+ export { ArrangeList, DEFAULT_ARRANGE_LABELS } from '../chunk-TKUAGWNR.js';
2
2
  import '../chunk-3XDT5ZWL.js';
3
3
  import '../chunk-MPVMB2DV.js';
4
- import '../chunk-PBCNINMB.js';
5
- import '../chunk-ETSF7SU6.js';
4
+ import '../chunk-LMQG3VPG.js';
5
+ import '../chunk-FB3TCNQP.js';
6
6
  import '../chunk-PGCLHYVP.js';
7
7
  import '../chunk-PQADV6P6.js';
8
8
  import '../chunk-2G355KDJ.js';
@@ -1,11 +1,11 @@
1
- export { ARTIFACT_BAR_LABELS, ArtifactBar, ArtifactVersionChip } from '../chunk-TKBFRGUV.js';
1
+ export { ARTIFACT_BAR_LABELS, ArtifactBar, ArtifactVersionChip } from '../chunk-IPBSCJIZ.js';
2
2
  import '../chunk-25AZQ3GZ.js';
3
3
  import '../chunk-I33UIUZD.js';
4
- import '../chunk-TYIHFDMP.js';
5
- import '../chunk-IPUEVTRJ.js';
4
+ import '../chunk-BKXUZCZX.js';
5
+ import '../chunk-6THG44LI.js';
6
6
  import '../chunk-EGMQURBR.js';
7
7
  import '../chunk-AVL52EME.js';
8
- import '../chunk-WIATRXEJ.js';
8
+ import '../chunk-CYQBDZ6M.js';
9
9
  import '../chunk-RSKW25Y5.js';
10
10
  import '../chunk-ANTAQAR4.js';
11
11
  import '../chunk-MLDTLZAO.js';
@@ -1,4 +1,4 @@
1
- export { ArtifactCard, ArtifactCardSkeleton, ArtifactPreviewGlyph } from '../chunk-OOPOQKZZ.js';
1
+ export { ArtifactCard, ArtifactCardSkeleton, ArtifactPreviewGlyph } from '../chunk-ZKLEAK7M.js';
2
2
  import '../chunk-BRLZXYPA.js';
3
3
  import '../chunk-N2BF272H.js';
4
4
  import '../chunk-EJNJMHQG.js';
@@ -8,10 +8,10 @@ import '../chunk-AV2DXAEL.js';
8
8
  import '../chunk-BT3GB7HC.js';
9
9
  import '../chunk-KTIQSSME.js';
10
10
  import '../chunk-NHLNHH4J.js';
11
- import '../chunk-PBCNINMB.js';
11
+ import '../chunk-LMQG3VPG.js';
12
12
  import '../chunk-DOY3KC6I.js';
13
13
  export { ARTIFACT_KINDS, ArtifactKindGlyph } from '../chunk-RSKW25Y5.js';
14
- import '../chunk-ETSF7SU6.js';
14
+ import '../chunk-FB3TCNQP.js';
15
15
  import '../chunk-PGCLHYVP.js';
16
16
  import '../chunk-MLDTLZAO.js';
17
17
  import '../chunk-PQADV6P6.js';
@@ -1,6 +1,6 @@
1
- export { ATTACHMENT_CHIP_LABELS, AttachmentChip } from '../chunk-3N36CYTU.js';
2
- import '../chunk-I5OIKDX2.js';
3
- import '../chunk-WIATRXEJ.js';
1
+ export { ATTACHMENT_CHIP_LABELS, AttachmentChip } from '../chunk-2PIH6JHJ.js';
2
+ import '../chunk-VERJCAJ4.js';
3
+ import '../chunk-CYQBDZ6M.js';
4
4
  import '../chunk-XSPCITPN.js';
5
5
  import '../chunk-UT6CW3C7.js';
6
6
  import '../chunk-ODEQTPFM.js';
@@ -1,12 +1,12 @@
1
- export { AvatarPicker } from '../chunk-OXEOC64K.js';
2
- import '../chunk-AQIFKLXY.js';
1
+ export { AvatarPicker } from '../chunk-ZW5SRQ5M.js';
2
+ import '../chunk-SK5SLGZH.js';
3
3
  import '../chunk-KHJIEPZV.js';
4
4
  import '../chunk-AYKXMIOR.js';
5
5
  import '../chunk-I22MUEG7.js';
6
- import '../chunk-TYIHFDMP.js';
7
- import '../chunk-IPUEVTRJ.js';
6
+ import '../chunk-BKXUZCZX.js';
7
+ import '../chunk-6THG44LI.js';
8
8
  import '../chunk-EGMQURBR.js';
9
- import '../chunk-WIATRXEJ.js';
9
+ import '../chunk-CYQBDZ6M.js';
10
10
  import '../chunk-KTIQSSME.js';
11
11
  import '../chunk-MLDTLZAO.js';
12
12
  import '../chunk-OQG4P5KU.js';
@@ -1,8 +1,8 @@
1
- export { BRIEF_CARD_LABELS, BRIEF_CARD_MAX_FOLLOW_UPS, BRIEF_CARD_MAX_NEEDS, BriefCard } from '../chunk-4KSCGXLJ.js';
1
+ export { BRIEF_CARD_LABELS, BRIEF_CARD_MAX_FOLLOW_UPS, BRIEF_CARD_MAX_NEEDS, BriefCard } from '../chunk-5BOFC5OP.js';
2
2
  import '../chunk-Y7PVLEZJ.js';
3
- import '../chunk-NTQPJJAN.js';
4
- import '../chunk-I5OIKDX2.js';
5
- import '../chunk-WIATRXEJ.js';
3
+ import '../chunk-RTMNULTL.js';
4
+ import '../chunk-VERJCAJ4.js';
5
+ import '../chunk-CYQBDZ6M.js';
6
6
  import '../chunk-4AUTALJA.js';
7
7
  import '../chunk-AV2DXAEL.js';
8
8
  import '../chunk-MLDTLZAO.js';
@@ -1,7 +1,7 @@
1
- export { CHANGE_LIST_COLLAPSE_OVER, CHANGE_LIST_LABELS, ChangeList, ChangeListItems, shortenUnchanged } from '../chunk-WQRSZMN4.js';
1
+ export { CHANGE_LIST_COLLAPSE_OVER, CHANGE_LIST_LABELS, ChangeList, ChangeListItems, shortenUnchanged } from '../chunk-BBD7PTBG.js';
2
2
  import '../chunk-LR4IWUWQ.js';
3
- import '../chunk-I5OIKDX2.js';
4
- import '../chunk-WIATRXEJ.js';
3
+ import '../chunk-VERJCAJ4.js';
4
+ import '../chunk-CYQBDZ6M.js';
5
5
  import '../chunk-222VILSP.js';
6
6
  import '../chunk-IVTQWOXV.js';
7
7
  import '../chunk-MLDTLZAO.js';
@@ -1,5 +1,5 @@
1
- export { Checkbox, CheckboxGroup } from '../chunk-PBCNINMB.js';
2
- import '../chunk-ETSF7SU6.js';
1
+ export { Checkbox, CheckboxGroup } from '../chunk-LMQG3VPG.js';
2
+ import '../chunk-FB3TCNQP.js';
3
3
  import '../chunk-PGCLHYVP.js';
4
4
  import '../chunk-PQADV6P6.js';
5
5
  import '../chunk-2G355KDJ.js';
@@ -0,0 +1,80 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { Tone } from '../utils/tone.js';
3
+ /** One short fact on a card: what it needs, how ready that is, how it behaves. */
4
+ export interface ChoiceCardFact {
5
+ /** The fact's name, a word or two: "Needs", "Isolation". */
6
+ label: ReactNode;
7
+ /** The fact: "Docker 24.0.7 found". */
8
+ value: ReactNode;
9
+ /** A state the fact reports, drawn as a dot beside it; none for a plain fact. */
10
+ tone?: Tone | undefined;
11
+ }
12
+ /** One choice in [ChoiceCards]. */
13
+ export interface ChoiceCardOption {
14
+ /** Stable identity, and what `onChange` is called with. */
15
+ value: string;
16
+ /** What the choice is, in a few words: its radio's name, with the tag. */
17
+ label: ReactNode;
18
+ /** A picture of the choice before its name: a product's mark, a glyph. Decorative; the label names it. */
19
+ media?: ReactNode;
20
+ /** A short word beside the choice's name, read as part of its name: "Company default". */
21
+ tag?: ReactNode;
22
+ /** One plain line saying what choosing it means. */
23
+ description?: ReactNode;
24
+ /** A few short facts, one to a line under the description, each its name and its value. */
25
+ facts?: readonly ChoiceCardFact[] | undefined;
26
+ /** The choice cannot be made now. */
27
+ disabled?: boolean | undefined;
28
+ /**
29
+ * Why not, and what fixes it ("Add E2B_API_KEY to use it"), in the warning
30
+ * ink and read with its radio: the one way a card gives its reason. It is
31
+ * the card's last line, or, with `disabledReasonAt`, the value of a fact.
32
+ */
33
+ disabledReason?: ReactNode;
34
+ /**
35
+ * The index of the fact whose value the reason takes the place of ("Needs:
36
+ * E2B key not set. Add E2B_API_KEY"), so a card that cannot be chosen keeps
37
+ * the same lines as the cards beside it rather than growing one.
38
+ */
39
+ disabledReasonAt?: number | undefined;
40
+ }
41
+ /** The narrowest a card stands: `md` 20rem, `lg` 24rem. */
42
+ export type ChoiceCardsMinWidth = 'md' | 'lg';
43
+ export interface ChoiceCardsProps {
44
+ /** Names the group for a screen reader. */
45
+ label: string;
46
+ options: readonly ChoiceCardOption[];
47
+ /** The selected choice's `value`, or null when none is. */
48
+ value: string | null;
49
+ onChange: (value: string) => void;
50
+ /**
51
+ * The narrowest a card stands, which decides how many share a row: `md`
52
+ * (20rem) for a short name and facts, `lg` (24rem) for choices with more to
53
+ * say. A form's column holds one to a row; a wider surface two or three.
54
+ */
55
+ minCardWidth?: ChoiceCardsMinWidth | undefined;
56
+ className?: string | undefined;
57
+ }
58
+ /**
59
+ * One answer from a few, each a card of the same lines: a picture, the
60
+ * choice's name and its tag; one line of description; and a few facts, one to
61
+ * a line. Cards side by side share every line (a subgrid), so the facts of
62
+ * every card stand level, in a grid that wraps.
63
+ *
64
+ * FOR CHOICES A PERSON COMPARES rather than reads down: a product to run, a
65
+ * place to run it, what a new thing is for. Where each answer is a line of
66
+ * words, [ChoiceList] is the lighter control.
67
+ *
68
+ * NATIVE RADIOS, so the arrow keys move the choice and Tab leaves the group as
69
+ * the platform does, and each card is its radio's label, so the whole card
70
+ * takes the pointer. The radio is NAMED by the choice's name and tag alone
71
+ * and DESCRIBED by the rest (the description, the facts, the reason): named by
72
+ * the whole card, it was read as some twenty-five words on every arrow press,
73
+ * a link's words among them. A disabled card is skipped by the arrows, keeps
74
+ * its words at full contrast, and says why it is unavailable and what fixes
75
+ * it, because a choice greyed out with no reason is a dead end.
76
+ *
77
+ * A FACT'S STATE IS NEVER ONLY A COLOUR: the dot sits beside words that say
78
+ * it ("Docker 24.0.7 found", "E2B key not set").
79
+ */
80
+ export declare function ChoiceCards({ label, options, value, onChange, minCardWidth, className }: ChoiceCardsProps): import("react").JSX.Element;
@@ -0,0 +1,3 @@
1
+ import './ChoiceCards.css';
2
+ export { ChoiceCards } from './ChoiceCards.js';
3
+ export type { ChoiceCardFact, ChoiceCardOption, ChoiceCardsMinWidth, ChoiceCardsProps } from './ChoiceCards.js';
@@ -0,0 +1,3 @@
1
+ export { ChoiceCards } from '../chunk-MKFEVHSR.js';
2
+ import '../chunk-CYQBDZ6M.js';
3
+ import '../chunk-ODEQTPFM.js';
@@ -0,0 +1,318 @@
1
+ /*
2
+ * Choices a person compares, as cards in a grid that wraps. The chosen card
3
+ * takes the selected fill and the strong boundary, as a chosen ChoiceList row
4
+ * does; the radio stays the mark, drawn in the card's corner; and no card is
5
+ * ringed in the accent except by the focus ring itself.
6
+ *
7
+ * ONE LINE A ROW, THE SAME ROWS IN EVERY CARD: the picture, the name and its
8
+ * tag on the first; the description on the second; then each fact on a line
9
+ * of its own, its name and its value; and a reason line where any card has
10
+ * one. Every card spans the same rows of the group's grid and lays its lines
11
+ * on them as a subgrid, so cards side by side share each row: a description
12
+ * that wraps in one card moves the facts of all of them, and Runs stays level
13
+ * with Runs. Facts run together as one line of meta wrapped differently in
14
+ * each card, and three cards read as three different shapes.
15
+ *
16
+ * A card is at least 20rem (24rem at `lg`), the width a fact's name and a
17
+ * short value keep to one line in: a form's column holds one to a row, and a
18
+ * wider surface two or three, aligned.
19
+ */
20
+ .crewlet-choice-cards {
21
+ --crewlet-choice-cards-min: 20rem;
22
+ --crewlet-choice-cards-radio: 16px;
23
+ /* How many rows a card spans, set from the options: the name, the description, the most facts, a reason. */
24
+ --crewlet-choice-cards-rows: 3;
25
+ display: grid;
26
+ grid-template-columns: repeat(
27
+ auto-fit,
28
+ minmax(min(100%, var(--crewlet-choice-cards-min)), 1fr)
29
+ );
30
+ grid-auto-rows: auto;
31
+ column-gap: var(--spacing-3);
32
+ row-gap: 0;
33
+ }
34
+
35
+ .crewlet-choice-cards--lg {
36
+ --crewlet-choice-cards-min: 24rem;
37
+ }
38
+
39
+ /*
40
+ * THE CARD is a column of its lines. The space under a card is its margin,
41
+ * the gap between one row of cards and the next; the lines inside a card keep
42
+ * their own, smaller gap.
43
+ *
44
+ * Its first line is as tall as the largest picture a card set draws (a
45
+ * product's mark), so the radio and every name sit on one line whatever
46
+ * picture each card has.
47
+ */
48
+ .crewlet-choice-cards__card {
49
+ --crewlet-choice-cards-title: var(--size-control-sm);
50
+
51
+ position: relative;
52
+ display: flex;
53
+ flex-direction: column;
54
+ gap: var(--spacing-1);
55
+ min-width: 0;
56
+ margin-block-end: var(--spacing-3);
57
+ padding: var(--spacing-3);
58
+ border: 1px solid var(--color-border-default);
59
+ border-radius: var(--radius-md);
60
+ background: var(--color-surface-elevated);
61
+ cursor: pointer;
62
+ transition:
63
+ background-color var(--motion-duration-fast) var(--motion-easing-default),
64
+ border-color var(--motion-duration-fast) var(--motion-easing-default);
65
+ }
66
+
67
+ .crewlet-choice-cards__card:hover {
68
+ background: var(--color-surface-hover);
69
+ }
70
+
71
+ /*
72
+ * THE CHOSEN CARD wears the soft selected tint laid OVER the raised surface,
73
+ * inside an edge in the accent's ink, as a chosen ChoiceButtons option does.
74
+ * The tint is translucent: laid on its own it let the page's ground through,
75
+ * darker than the raised cards beside it in dark, and the chosen card looked
76
+ * recessed. The edge is the accent's ink rather than the strong border,
77
+ * because the edge is what tells the chosen card from the others at 3:1.
78
+ */
79
+ .crewlet-choice-cards__card.is-checked {
80
+ border-color: var(--color-brand-accent-ink);
81
+ background-color: var(--color-surface-elevated);
82
+ background-image: linear-gradient(var(--color-surface-selected), var(--color-surface-selected));
83
+ }
84
+
85
+ /*
86
+ * A CARD THAT CANNOT BE CHOSEN stands on the page's own ground and takes no
87
+ * hover: the subtle surface it took is brighter than the raised one in light,
88
+ * so the card that could not be chosen read as the most inviting. Its radio is
89
+ * drawn quiet, as ChoiceList's disabled radio is, and its picture muted; its
90
+ * words stay at full contrast, and its reason says why.
91
+ */
92
+ .crewlet-choice-cards__card.is-disabled {
93
+ cursor: not-allowed;
94
+ background: transparent;
95
+ }
96
+
97
+ .crewlet-choice-cards__card.is-disabled:hover {
98
+ background: transparent;
99
+ }
100
+
101
+ .crewlet-choice-cards__card.is-disabled .crewlet-choice-cards__radio {
102
+ border-color: var(--color-border-default);
103
+ background: var(--color-surface-subtle);
104
+ }
105
+
106
+ .crewlet-choice-cards__card.is-disabled .crewlet-choice-cards__media {
107
+ color: var(--color-text-tertiary);
108
+ filter: grayscale(1);
109
+ }
110
+
111
+ /* The ring sits inside the card's own box, over its border, rather than a second line beside it. */
112
+ .crewlet-choice-cards__card:has(.crewlet-choice-cards__radio:focus-visible) {
113
+ outline: 2px solid var(--color-focus);
114
+ outline-offset: var(--size-focus-ring-inset-offset);
115
+ }
116
+
117
+ /* The radio, drawn in the card's top end and centred on the first line: an empty ring, or the accent ring round an accent dot. */
118
+ .crewlet-choice-cards__radio {
119
+ appearance: none;
120
+ position: absolute;
121
+ inset-block-start: calc(var(--spacing-3) + (var(--crewlet-choice-cards-title) - var(--crewlet-choice-cards-radio)) / 2);
122
+ inset-inline-end: var(--spacing-3);
123
+ display: inline-grid;
124
+ place-content: center;
125
+ width: var(--crewlet-choice-cards-radio);
126
+ height: var(--crewlet-choice-cards-radio);
127
+ margin: 0;
128
+ border: 1px solid var(--color-border-control);
129
+ border-radius: var(--radius-circle);
130
+ background: var(--color-surface-elevated);
131
+ }
132
+
133
+ .crewlet-choice-cards__radio:checked {
134
+ border-color: var(--color-brand-accent);
135
+ }
136
+
137
+ .crewlet-choice-cards__radio:checked::after {
138
+ content: "";
139
+ width: 8px;
140
+ height: 8px;
141
+ border-radius: var(--radius-circle);
142
+ background: var(--color-brand-accent);
143
+ }
144
+
145
+ /*
146
+ * THE CARD DRAWS THE RING (above), so the radio's own is transparent rather
147
+ * than off: invisible beside the card's, and still a ring where forced-colors
148
+ * mode repaints every outline in the system's colour.
149
+ */
150
+ .crewlet-choice-cards__radio:focus-visible {
151
+ outline: 2px solid transparent;
152
+ }
153
+
154
+ /* The first line: the picture, the name and its tag, stopping short of the radio. */
155
+ .crewlet-choice-cards__title {
156
+ display: flex;
157
+ align-items: center;
158
+ gap: var(--spacing-2);
159
+ grid-row: 1;
160
+ min-width: 0;
161
+ min-height: var(--crewlet-choice-cards-title);
162
+ padding-inline-end: calc(var(--spacing-2) + var(--crewlet-choice-cards-radio));
163
+ }
164
+
165
+ .crewlet-choice-cards__media {
166
+ display: inline-flex;
167
+ flex: none;
168
+ color: var(--color-text-secondary);
169
+ }
170
+
171
+ .crewlet-choice-cards__tag {
172
+ padding: 0 var(--spacing-2);
173
+ border: 1px solid var(--color-border-default);
174
+ border-radius: var(--radius-pill);
175
+ color: var(--color-text-secondary);
176
+ font-size: var(--font-size-xs);
177
+ white-space: nowrap;
178
+ }
179
+
180
+ /* On the chosen card's tint the default edge all but vanishes in light, so the tag takes the strong one. */
181
+ .crewlet-choice-cards__card.is-checked .crewlet-choice-cards__tag {
182
+ border-color: var(--color-border-strong);
183
+ }
184
+
185
+ .crewlet-choice-cards__label {
186
+ min-width: 0;
187
+ color: var(--color-text-primary);
188
+ font-weight: var(--font-weight-medium);
189
+ }
190
+
191
+ /*
192
+ * The second row is the description's in every card once any card has one: a
193
+ * card without one keeps an empty cell there, so its facts stay level with
194
+ * the facts beside them.
195
+ */
196
+ .crewlet-choice-cards__description {
197
+ grid-row: 2;
198
+ color: var(--color-text-secondary);
199
+ font-size: var(--font-size-sm);
200
+ }
201
+
202
+ /* The facts give each of their lines to the card's grid, one row a fact. */
203
+ .crewlet-choice-cards__facts {
204
+ display: contents;
205
+ }
206
+
207
+ /*
208
+ * A fact: its name in the quiet ink, then its value, on one line while there
209
+ * is room. Its two parts share one size and line height and align to the top,
210
+ * so a fact with a dot is no taller than one without: aligned on a baseline,
211
+ * the dot (a box with no text) gave the line a baseline of its own and the
212
+ * Needs line stood a pixel taller than Runs.
213
+ */
214
+ .crewlet-choice-cards__fact {
215
+ display: flex;
216
+ align-items: flex-start;
217
+ gap: var(--spacing-2);
218
+ min-width: 0;
219
+ font-size: var(--font-size-xs);
220
+ }
221
+
222
+ .crewlet-choice-cards__fact-label {
223
+ flex: none;
224
+ color: var(--color-text-tertiary);
225
+ }
226
+
227
+ .crewlet-choice-cards__fact-value {
228
+ min-width: 0;
229
+ color: var(--color-text-secondary);
230
+ }
231
+
232
+ /*
233
+ * A FACT WRAPS RATHER THAN CLIPS. A StatusDot is one line and its word gives
234
+ * way with an ellipsis, which is right in a table cell and wrong here: a
235
+ * fact is read to choose between the cards, and a narrow card cut "Docker
236
+ * 24.0.7 found" to "Docker 24.0..." with nothing to reveal the rest. The word
237
+ * wraps under itself, and the dot stays on the first line's centre, where it
238
+ * sat while the fact was one line.
239
+ */
240
+ .crewlet-choice-cards__fact-value > .crewlet-status {
241
+ display: flex;
242
+ align-items: flex-start;
243
+ }
244
+
245
+ .crewlet-choice-cards__fact-value > .crewlet-status > .crewlet-status-dot {
246
+ margin-block-start: calc((1lh - var(--crewlet-status-dot-size)) / 2);
247
+ }
248
+
249
+ .crewlet-choice-cards__fact-value > .crewlet-status > .crewlet-status__label {
250
+ overflow: visible;
251
+ white-space: normal;
252
+ overflow-wrap: anywhere;
253
+ }
254
+
255
+ /* A reason given on a fact's line (`disabledReasonAt`) takes that line's value, in the same ink. */
256
+ .crewlet-choice-cards__fact.is-reason .crewlet-choice-cards__fact-value {
257
+ color: var(--color-feedback-warning-ink);
258
+ }
259
+
260
+ /* The reason a disabled card cannot be chosen: the card's last row, in the warning ink. */
261
+ .crewlet-choice-cards__reason {
262
+ grid-row: -2;
263
+ color: var(--color-feedback-warning-ink);
264
+ font-size: var(--font-size-xs);
265
+ }
266
+
267
+
268
+ /*
269
+ * WHERE SUBGRID IS SUPPORTED, every card spans the same rows of the group and
270
+ * takes them from it, so a row is as tall as its tallest line across the cards
271
+ * beside it and their facts stand level. Elsewhere each card stays the plain
272
+ * column above: its lines still read in order, only not level with the next
273
+ * card's.
274
+ */
275
+ @supports (grid-template-rows: subgrid) {
276
+ .crewlet-choice-cards__card {
277
+ display: grid;
278
+ grid-row: span var(--crewlet-choice-cards-rows);
279
+ grid-template-rows: subgrid;
280
+ grid-template-columns: minmax(0, 1fr);
281
+ row-gap: var(--spacing-1);
282
+ align-content: start;
283
+ }
284
+ }
285
+
286
+ /*
287
+ * UNDER FORCED COLOURS the system repaints every colour above, so the drawn
288
+ * radio's dot and the tint vanish together: the radio becomes the platform's
289
+ * own, which marks itself; the chosen card's edge takes the system's
290
+ * selection colour, twice as wide; and a card that cannot be chosen takes the
291
+ * system's disabled ink, edge and words, rather than reading as one that can.
292
+ */
293
+ @media (forced-colors: active) {
294
+ .crewlet-choice-cards__radio {
295
+ appearance: auto;
296
+ }
297
+
298
+ .crewlet-choice-cards__card.is-checked {
299
+ border-width: 2px;
300
+ border-color: Highlight;
301
+ }
302
+
303
+ .crewlet-choice-cards__card.is-disabled {
304
+ border-color: GrayText;
305
+ color: GrayText;
306
+ }
307
+
308
+ .crewlet-choice-cards__card.is-disabled :is(.crewlet-choice-cards__label, .crewlet-choice-cards__description, .crewlet-choice-cards__fact-label, .crewlet-choice-cards__fact-value) {
309
+ color: GrayText;
310
+ }
311
+ }
312
+
313
+ /* The border's ease into its hover and checked inks is held for a reader who asked for no motion. */
314
+ @media (prefers-reduced-motion: reduce) {
315
+ .crewlet-choice-cards__card {
316
+ transition: none;
317
+ }
318
+ }
@@ -24,6 +24,14 @@ export interface ChoiceListOption {
24
24
  * "Something else" choice opens.
25
25
  */
26
26
  panel?: ReactNode;
27
+ /** This choice cannot be made now; the rest of the list can. */
28
+ disabled?: boolean | undefined;
29
+ /**
30
+ * Why not, and what fixes it, on the choice's detail line while it is
31
+ * disabled, and read with the radio as its description. A choice greyed out
32
+ * with no reason is a dead end.
33
+ */
34
+ disabledReason?: ReactNode;
27
35
  }
28
36
  export interface ChoiceListProps {
29
37
  options: readonly ChoiceListOption[];
@@ -44,7 +52,8 @@ export interface ChoiceListProps {
44
52
  *
45
53
  * NATIVE RADIOS, so the arrow keys move the choice and Tab leaves the group as
46
54
  * the platform does, and every row is its radio's label, so the whole row
47
- * takes the pointer. A choice's `panel` is drawn under it only while it is
55
+ * takes the pointer. A disabled choice is skipped by the arrows, keeps its
56
+ * words at full contrast, and says why on its detail line. A choice's `panel` is drawn under it only while it is
48
57
  * selected, which is how a list ends in "Something else" with a field of its
49
58
  * own without a second control beside the list.
50
59
  */
@@ -1,3 +1,3 @@
1
- export { ChoiceList } from '../chunk-NQABJAUY.js';
1
+ export { ChoiceList } from '../chunk-6RW2MUTY.js';
2
2
  import '../chunk-2YTOOH3A.js';
3
3
  import '../chunk-ODEQTPFM.js';
@@ -101,10 +101,22 @@
101
101
  background: var(--color-surface-selected);
102
102
  }
103
103
 
104
- .crewlet-choice-list.is-disabled .crewlet-choice-list__row {
104
+ .crewlet-choice-list.is-disabled .crewlet-choice-list__row,
105
+ .crewlet-choice-list__row.is-disabled {
105
106
  cursor: not-allowed;
106
107
  }
107
108
 
109
+ /* A choice that cannot be made takes no hover: the ground would offer it. */
110
+ .crewlet-choice-list__row.is-disabled:hover {
111
+ background: none;
112
+ }
113
+
114
+ /* Its ring is drawn on the quiet boundary and ground; its words stay at full contrast. */
115
+ .crewlet-choice-list__row.is-disabled .crewlet-choice-list__radio {
116
+ border-color: var(--color-border-default);
117
+ background: var(--color-surface-subtle);
118
+ }
119
+
108
120
  /*
109
121
  * THE RADIO IS DRAWN, not the platform's. In a dark palette the platform's
110
122
  * own radio fills an unchosen answer with a grey disc, which beside the one
@@ -163,6 +175,15 @@
163
175
  overflow-wrap: anywhere;
164
176
  }
165
177
 
178
+ /* Why a disabled choice cannot be made, after its detail on the same line, in the warning ink. */
179
+ .crewlet-choice-list__reason {
180
+ color: var(--color-feedback-warning-ink);
181
+ }
182
+
183
+ .crewlet-choice-list__detail > .crewlet-choice-list__reason:not(:first-child) {
184
+ margin-inline-start: var(--spacing-2);
185
+ }
186
+
166
187
  /* A tag says something ABOUT an answer ("Suggested"), so it is set in the
167
188
  tertiary ink: in the accent ink it was a second accent mark on the chosen
168
189
  row, beside the radio and the fill that already say which one is chosen. */