@w6w/ui 0.2.0 → 0.2.54

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 (160) hide show
  1. package/CHANGELOG.md +146 -2
  2. package/README.md +28 -10
  3. package/dist/AppPicker.d.ts +58 -2
  4. package/dist/CanvasContextMenu.d.ts +19 -0
  5. package/dist/CanvasLayer.d.ts +38 -0
  6. package/dist/{CodeBlock-Dot3Pd7P.js → CodeBlock-C-oIIYfC.js} +1436 -555
  7. package/dist/CodeBlock-C-oIIYfC.js.map +1 -0
  8. package/dist/CodeBlock-CymlfyeA.cjs +15 -0
  9. package/dist/CodeBlock-CymlfyeA.cjs.map +1 -0
  10. package/dist/CodeEditor.d.ts +4 -1
  11. package/dist/ConditionEditor.d.ts +11 -0
  12. package/dist/EditConnectionModal.d.ts +6 -1
  13. package/dist/JsonEditor.d.ts +20 -0
  14. package/dist/Markdown.d.ts +25 -0
  15. package/dist/ParamCustomization.d.ts +5 -0
  16. package/dist/ParamsForm.d.ts +18 -4
  17. package/dist/RouterEditor.d.ts +5 -0
  18. package/dist/StepBuilderModal.d.ts +103 -15
  19. package/dist/StepNameEditor.d.ts +11 -0
  20. package/dist/StopEditor.d.ts +5 -0
  21. package/dist/TriggerFillForm.d.ts +14 -1
  22. package/dist/WebhookListenPanel.d.ts +28 -0
  23. package/dist/WorkflowConnections.d.ts +29 -0
  24. package/dist/WorkflowFlowEditor.d.ts +74 -7
  25. package/dist/app-pages.d.ts +2 -0
  26. package/dist/assets/script-completion.worker-DDJKkj5M.js +8290 -0
  27. package/dist/assets/script-completion.worker-DDJKkj5M.js.map +1 -0
  28. package/dist/canvas-context-menu.d.ts +27 -0
  29. package/dist/canvas-types.d.ts +36 -0
  30. package/dist/canvas-utils.d.ts +44 -0
  31. package/dist/code.cjs +1 -1
  32. package/dist/code.mjs +3 -4
  33. package/dist/code.mjs.map +1 -1
  34. package/dist/components/AnchoredFlyout.d.ts +7 -0
  35. package/dist/components/AuthFieldsForm.d.ts +11 -0
  36. package/dist/components/BudgetMeter.d.ts +15 -0
  37. package/dist/components/CallableList.d.ts +3 -2
  38. package/dist/components/ColumnsEditor.d.ts +27 -0
  39. package/dist/components/Combobox.d.ts +19 -1
  40. package/dist/components/ConnectionList.d.ts +2 -3
  41. package/dist/components/CredentialPicker.d.ts +35 -0
  42. package/dist/components/CsvPanel.d.ts +21 -0
  43. package/dist/components/DataEntryEditor.d.ts +38 -0
  44. package/dist/components/DataEntryList.d.ts +33 -0
  45. package/dist/components/DataStoreList.d.ts +33 -0
  46. package/dist/components/DataTable.d.ts +27 -0
  47. package/dist/components/DataTableGrid.d.ts +46 -0
  48. package/dist/components/EmptyState.d.ts +9 -0
  49. package/dist/components/EventbriteTriggerPicker.d.ts +7 -0
  50. package/dist/components/ExecutionDetail.d.ts +17 -1
  51. package/dist/components/ExecutionList.d.ts +19 -1
  52. package/dist/components/ExecutionLogPanel.d.ts +61 -3
  53. package/dist/components/ExpressionEditorModal.d.ts +33 -10
  54. package/dist/components/ExpressionOptions.d.ts +101 -15
  55. package/dist/components/HtmlPreview.d.ts +7 -0
  56. package/dist/components/JsonTree.d.ts +32 -0
  57. package/dist/components/ResolvedParams.d.ts +6 -1
  58. package/dist/components/ResultViewer.d.ts +46 -0
  59. package/dist/components/ScriptBindingsNotice.d.ts +31 -0
  60. package/dist/components/ScriptCodeField.d.ts +13 -0
  61. package/dist/components/StepStatusPill.d.ts +7 -0
  62. package/dist/components/expression-authoring.d.ts +13 -0
  63. package/dist/components/expression-dom.d.ts +17 -2
  64. package/dist/components/expression-template.d.ts +25 -3
  65. package/dist/components/icons.d.ts +1 -1
  66. package/dist/components/json-compose.d.ts +53 -0
  67. package/dist/components/result-format.d.ts +13 -0
  68. package/dist/condition-editor.d.ts +44 -0
  69. package/dist/data-stores/data-store-view.d.ts +371 -0
  70. package/dist/dynamic-fields.d.ts +60 -0
  71. package/dist/editor-theme.d.ts +24 -0
  72. package/dist/eventbrite-trigger-selection.d.ts +13 -0
  73. package/dist/flow-connect.d.ts +22 -16
  74. package/dist/flow-delete.d.ts +16 -0
  75. package/dist/flow-fanout.d.ts +2 -2
  76. package/dist/flow-history.d.ts +75 -0
  77. package/dist/flow-layout.d.ts +13 -0
  78. package/dist/flow-loop-colors.d.ts +39 -0
  79. package/dist/flow-loop.d.ts +98 -0
  80. package/dist/flow-node-flags.d.ts +14 -0
  81. package/dist/flow-problems.d.ts +69 -0
  82. package/dist/flow-state.d.ts +20 -0
  83. package/dist/flow-types.d.ts +155 -10
  84. package/dist/flow-utils.d.ts +33 -12
  85. package/dist/flow.cjs +4 -2
  86. package/dist/flow.cjs.map +1 -1
  87. package/dist/flow.d.ts +15 -2
  88. package/dist/flow.mjs +3737 -1350
  89. package/dist/flow.mjs.map +1 -1
  90. package/dist/index.cjs +2 -1
  91. package/dist/index.cjs.map +1 -1
  92. package/dist/index.d.ts +43 -5
  93. package/dist/index.mjs +2855 -1333
  94. package/dist/index.mjs.map +1 -1
  95. package/dist/node-samples-ChdyMBnJ.cjs +22 -0
  96. package/dist/node-samples-ChdyMBnJ.cjs.map +1 -0
  97. package/dist/node-samples-DkJL07EX.js +12302 -0
  98. package/dist/node-samples-DkJL07EX.js.map +1 -0
  99. package/dist/node-samples.d.ts +80 -0
  100. package/dist/note-markdown.d.ts +22 -0
  101. package/dist/output-shape.d.ts +30 -14
  102. package/dist/param-customization.d.ts +7 -0
  103. package/dist/provider.d.ts +174 -13
  104. package/dist/resolve-params.d.ts +50 -11
  105. package/dist/run-visuals.d.ts +18 -22
  106. package/dist/script-bindings.d.ts +63 -0
  107. package/dist/script-completion-service.d.ts +9 -0
  108. package/dist/script-completion.worker.d.ts +1 -0
  109. package/dist/script-completions.d.ts +2 -0
  110. package/dist/state-shape.d.ts +38 -0
  111. package/dist/step-preview-state.d.ts +155 -6
  112. package/dist/step-problems.d.ts +64 -0
  113. package/dist/test-inputs.d.ts +25 -0
  114. package/dist/trigger-registration.d.ts +28 -0
  115. package/dist/types.d.ts +179 -11
  116. package/dist/use-app-action-defs.d.ts +9 -1
  117. package/dist/use-dynamic-fields.d.ts +23 -0
  118. package/dist/use-test-input.d.ts +18 -0
  119. package/dist/workflow-connections.d.ts +30 -0
  120. package/package.json +18 -7
  121. package/src/code.css +60 -6
  122. package/src/styles/_budget-meter.scss +46 -0
  123. package/src/styles/_callable-list.scss +15 -6
  124. package/src/styles/_canvas.scss +353 -0
  125. package/src/styles/_code-editor.scss +35 -0
  126. package/src/styles/_code.scss +6 -5
  127. package/src/styles/_connections.scss +2 -14
  128. package/src/styles/_copyable.scss +24 -0
  129. package/src/styles/_data-stores.scss +138 -0
  130. package/src/styles/_data-table.scss +55 -0
  131. package/src/styles/_data-vars.scss +17 -6
  132. package/src/styles/_empty-state.scss +21 -0
  133. package/src/styles/_execution-history.scss +73 -0
  134. package/src/styles/_execution-log.scss +145 -1
  135. package/src/styles/_expression-modal.scss +106 -16
  136. package/src/styles/_fields.scss +11 -0
  137. package/src/styles/_flow.scss +171 -0
  138. package/src/styles/_fx.scss +26 -1
  139. package/src/styles/_incoming-state.scss +33 -0
  140. package/src/styles/_layout.scss +10 -0
  141. package/src/styles/_markdown.scss +20 -0
  142. package/src/styles/_modal.scss +12 -0
  143. package/src/styles/_multiselect.scss +35 -0
  144. package/src/styles/_sections.scss +36 -0
  145. package/src/styles/_step-builder.scss +145 -0
  146. package/src/styles/_tester.scss +57 -14
  147. package/src/styles/_tokens.scss +27 -1
  148. package/src/styles.css +1861 -115
  149. package/src/styles.scss +7 -0
  150. package/dist/CodeBlock-CZu4TFSi.cjs +0 -15
  151. package/dist/CodeBlock-CZu4TFSi.cjs.map +0 -1
  152. package/dist/CodeBlock-Dot3Pd7P.js.map +0 -1
  153. package/dist/ConfirmModal-ChoiGy2K.cjs +0 -11
  154. package/dist/ConfirmModal-ChoiGy2K.cjs.map +0 -1
  155. package/dist/ConfirmModal-Wx3i8F1n.js +0 -6359
  156. package/dist/ConfirmModal-Wx3i8F1n.js.map +0 -1
  157. package/dist/Copyable-1niV9gUa.js +0 -771
  158. package/dist/Copyable-1niV9gUa.js.map +0 -1
  159. package/dist/Copyable-CYZU1o4X.cjs +0 -2
  160. package/dist/Copyable-CYZU1o4X.cjs.map +0 -1
package/src/styles.css CHANGED
@@ -699,7 +699,9 @@ svg.react-flow__connectionline {
699
699
  * host rule — even a plain `:root { ... }` — wins without !important. Tokens are
700
700
  * defined on :root so every `.w6w-*` component inherits them out of the box.
701
701
  */
702
- /* Light — the base defaults. */
702
+ /* Light — the base defaults.
703
+ `--w6w-loop-N` is the For each palette (flow-loop-colors.ts, LOOP_TONES): no red,
704
+ black or green, so a loop never reads as an error port or a success edge. */
703
705
  :where(:root) {
704
706
  --w6w-bg: #f7f8fa;
705
707
  --w6w-panel: #ffffff;
@@ -715,6 +717,12 @@ svg.react-flow__connectionline {
715
717
  --w6w-health-down: var(--w6w-danger);
716
718
  --w6w-health-unknown: #5f6875;
717
719
  --w6w-icon-swatch: #f0f2f6;
720
+ --w6w-loop-1: #2f6fdf;
721
+ --w6w-loop-2: #7c4ddb;
722
+ --w6w-loop-3: #b7791f;
723
+ --w6w-loop-4: #0e8fa8;
724
+ --w6w-loop-5: #b0399f;
725
+ --w6w-loop-6: #4f4fc7;
718
726
  --w6w-code-comment: #6b7280;
719
727
  --w6w-code-keyword: #8250df;
720
728
  --w6w-code-string: #0a7b34;
@@ -744,6 +752,12 @@ svg.react-flow__connectionline {
744
752
  --w6w-health-down: var(--w6w-danger);
745
753
  --w6w-health-unknown: #9aa3b2;
746
754
  --w6w-icon-swatch: #1f232c;
755
+ --w6w-loop-1: #5b9bff;
756
+ --w6w-loop-2: #a98bff;
757
+ --w6w-loop-3: #f0b13a;
758
+ --w6w-loop-4: #3cc6e0;
759
+ --w6w-loop-5: #e47ad6;
760
+ --w6w-loop-6: #8a8aff;
747
761
  --w6w-scheme: dark;
748
762
  --w6w-code-comment: #7d8797;
749
763
  --w6w-code-keyword: #d2a8ff;
@@ -772,6 +786,12 @@ svg.react-flow__connectionline {
772
786
  --w6w-health-down: var(--w6w-danger);
773
787
  --w6w-health-unknown: #5f6875;
774
788
  --w6w-icon-swatch: #f0f2f6;
789
+ --w6w-loop-1: #2f6fdf;
790
+ --w6w-loop-2: #7c4ddb;
791
+ --w6w-loop-3: #b7791f;
792
+ --w6w-loop-4: #0e8fa8;
793
+ --w6w-loop-5: #b0399f;
794
+ --w6w-loop-6: #4f4fc7;
775
795
  --w6w-code-comment: #6b7280;
776
796
  --w6w-code-keyword: #8250df;
777
797
  --w6w-code-string: #0a7b34;
@@ -797,6 +817,12 @@ svg.react-flow__connectionline {
797
817
  --w6w-health-down: var(--w6w-danger);
798
818
  --w6w-health-unknown: #9aa3b2;
799
819
  --w6w-icon-swatch: #1f232c;
820
+ --w6w-loop-1: #5b9bff;
821
+ --w6w-loop-2: #a98bff;
822
+ --w6w-loop-3: #f0b13a;
823
+ --w6w-loop-4: #3cc6e0;
824
+ --w6w-loop-5: #e47ad6;
825
+ --w6w-loop-6: #8a8aff;
800
826
  --w6w-code-comment: #7d8797;
801
827
  --w6w-code-keyword: #d2a8ff;
802
828
  --w6w-code-string: #7ee787;
@@ -914,6 +940,15 @@ svg.react-flow__connectionline {
914
940
  * uncoordinated ones exist across the four repos today).
915
941
  */
916
942
  /* Layout helpers */
943
+ /* A vertical column whose direct children are cards/panels. ONE gap, from the
944
+ shared spacing scale — never a per-screen number and never a second gap: do
945
+ not add this class to a container whose parent already gaps its children. */
946
+ .w6w-card-stack {
947
+ display: flex;
948
+ flex-direction: column;
949
+ gap: var(--w6w-sp-4);
950
+ }
951
+
917
952
  .w6w-stack {
918
953
  display: flex;
919
954
  flex-direction: column;
@@ -1100,6 +1135,18 @@ select optgroup {
1100
1135
  margin-top: var(--w6w-sp-0-5);
1101
1136
  }
1102
1137
 
1138
+ /* Combobox (`components/Combobox.tsx`): its <input> sits inside the component's
1139
+ own positioned root, so it is not a direct flex child of `.w6w-field` and would
1140
+ keep its intrinsic width. The root and the input fill the field instead. */
1141
+ .w6w-combobox {
1142
+ width: 100%;
1143
+ }
1144
+
1145
+ .w6w-combobox > input[type=text] {
1146
+ width: 100%;
1147
+ box-sizing: border-box;
1148
+ }
1149
+
1103
1150
  /* Material-style multi-select: chips live INSIDE a single input-like box,
1104
1151
  followed by the dropdown which reads as the trailing placeholder. */
1105
1152
  .w6w-multiselect {
@@ -1177,6 +1224,49 @@ select optgroup {
1177
1224
  color: var(--w6w-text);
1178
1225
  }
1179
1226
 
1227
+ .w6w-fx-wrap:has(> .w6w-multiselect-checks) {
1228
+ align-items: flex-start;
1229
+ }
1230
+
1231
+ .w6w-multiselect-checks {
1232
+ padding: var(--w6w-sp-1) 0;
1233
+ }
1234
+
1235
+ .w6w-multiselect-checks-toolbar {
1236
+ display: flex;
1237
+ align-items: center;
1238
+ gap: var(--w6w-sp-3);
1239
+ margin-bottom: var(--w6w-sp-2);
1240
+ }
1241
+
1242
+ .w6w-multiselect-checks-options {
1243
+ border: 0;
1244
+ padding: 0;
1245
+ margin: 0;
1246
+ min-width: 0;
1247
+ display: grid;
1248
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr));
1249
+ gap: var(--w6w-sp-2) var(--w6w-sp-4);
1250
+ max-height: 240px;
1251
+ overflow-y: auto;
1252
+ }
1253
+
1254
+ .w6w-field .w6w-multiselect-check {
1255
+ display: flex;
1256
+ align-items: center;
1257
+ gap: var(--w6w-sp-2);
1258
+ margin: 0;
1259
+ font-size: var(--w6w-fs-sm);
1260
+ cursor: pointer;
1261
+ }
1262
+
1263
+ .w6w-field .w6w-multiselect-check input[type=checkbox] {
1264
+ width: auto;
1265
+ margin: 0;
1266
+ flex-shrink: 0;
1267
+ accent-color: var(--w6w-accent);
1268
+ }
1269
+
1180
1270
  /* Incoming state (WorkflowFlowEditor · `IncomingStateField`, mounted by BOTH the
1181
1271
  step editor's Test tab and the canvas ▶ Run collect form). The raw JSON box is
1182
1272
  an OVERRIDE, so it sits behind a disclosure instead of being the first thing
@@ -1218,6 +1308,11 @@ select optgroup {
1218
1308
  gap: var(--w6w-sp-1-5);
1219
1309
  }
1220
1310
 
1311
+ /* A whole-value JSON field's "Use the output of <step>" row (ParamsForm). */
1312
+ .w6w-json-quickbind {
1313
+ align-items: center;
1314
+ }
1315
+
1221
1316
  .w6w-seed-chip {
1222
1317
  cursor: pointer;
1223
1318
  }
@@ -1231,6 +1326,40 @@ select optgroup {
1231
1326
  opacity: 0.6;
1232
1327
  }
1233
1328
 
1329
+ /* Run script (ParamsForm `ScriptCodeField`, WorkflowFlowEditor `ScriptBindingsNotice`): the
1330
+ insert row under the code, the collapsed "How to read data", and the quiet conversion note. */
1331
+ .w6w-script-insert {
1332
+ align-items: center;
1333
+ }
1334
+
1335
+ .w6w-script-help > summary {
1336
+ cursor: pointer;
1337
+ width: fit-content;
1338
+ }
1339
+
1340
+ .w6w-script-help-examples {
1341
+ margin: var(--w6w-sp-1-5) 0;
1342
+ padding: var(--w6w-sp-1-5) var(--w6w-sp-2);
1343
+ border-radius: 6px;
1344
+ background: var(--w6w-bg);
1345
+ font-size: 0.85em;
1346
+ overflow-x: auto;
1347
+ }
1348
+
1349
+ .w6w-script-converted {
1350
+ display: block;
1351
+ }
1352
+
1353
+ .w6w-script-converted-undo {
1354
+ border: 0;
1355
+ padding: 0;
1356
+ background: none;
1357
+ font: inherit;
1358
+ color: var(--w6w-accent);
1359
+ cursor: pointer;
1360
+ text-decoration: underline;
1361
+ }
1362
+
1234
1363
  /* Expression input: a field whose contents are inline SEGMENTS — plain text
1235
1364
  plus var/secret/expr chips, edited in place (contentEditable, like a Notion /
1236
1365
  Zapier block field). A small toolbar carries the Insert menu + the {{ }} (fx)
@@ -1321,12 +1450,99 @@ select optgroup {
1321
1450
  padding: var(--w6w-sp-2-5);
1322
1451
  display: flex;
1323
1452
  flex-direction: column;
1324
- gap: 14px;
1453
+ gap: var(--w6w-sp-1);
1454
+ }
1455
+
1456
+ /* The seed-mode "Insert as" toggle is a <fieldset> for its group semantics; drop the
1457
+ browser's fieldset chrome so it lays out like the plain row it replaced. */
1458
+ .w6w-exprmodal-insert-as {
1459
+ border: 0;
1460
+ padding: 0;
1461
+ margin: 0;
1462
+ min-inline-size: 0;
1325
1463
  }
1326
1464
 
1465
+ /* A rail section is an accordion item: its header always shows, its body animates open
1466
+ (grid-template-rows 0fr -> 1fr) and only one section is open at a time. */
1327
1467
  .w6w-exprmodal-group {
1328
1468
  display: flex;
1329
1469
  flex-direction: column;
1470
+ }
1471
+
1472
+ .w6w-exprmodal-group-head {
1473
+ display: flex;
1474
+ align-items: center;
1475
+ gap: var(--w6w-sp-1);
1476
+ }
1477
+
1478
+ .w6w-exprmodal-group-toggle {
1479
+ flex: 1;
1480
+ min-width: 0;
1481
+ display: flex;
1482
+ align-items: center;
1483
+ justify-content: space-between;
1484
+ gap: var(--w6w-sp-1);
1485
+ background: transparent;
1486
+ border: 0;
1487
+ border-radius: 4px;
1488
+ padding: var(--w6w-sp-1) var(--w6w-sp-0-5);
1489
+ font: inherit;
1490
+ text-align: left;
1491
+ cursor: pointer;
1492
+ }
1493
+
1494
+ .w6w-exprmodal-group-toggle:focus-visible {
1495
+ outline: 2px solid var(--w6w-accent);
1496
+ outline-offset: 1px;
1497
+ }
1498
+
1499
+ .w6w-exprmodal-group-toggle:hover .w6w-exprmodal-group-label,
1500
+ .w6w-exprmodal-group.is-open .w6w-exprmodal-group-label {
1501
+ color: var(--w6w-text);
1502
+ }
1503
+
1504
+ .w6w-exprmodal-group-caret {
1505
+ color: var(--w6w-muted);
1506
+ line-height: 1;
1507
+ transition: transform 200ms ease;
1508
+ }
1509
+
1510
+ .w6w-exprmodal-group.is-open .w6w-exprmodal-group-caret {
1511
+ transform: rotate(90deg);
1512
+ }
1513
+
1514
+ .w6w-exprmodal-group-body {
1515
+ display: grid;
1516
+ grid-template-rows: 0fr;
1517
+ transition: grid-template-rows 200ms ease;
1518
+ }
1519
+
1520
+ .w6w-exprmodal-group.is-open .w6w-exprmodal-group-body {
1521
+ grid-template-rows: 1fr;
1522
+ }
1523
+
1524
+ .w6w-exprmodal-group-inner {
1525
+ min-height: 0;
1526
+ overflow: hidden;
1527
+ display: flex;
1528
+ flex-direction: column;
1529
+ gap: 3px;
1530
+ }
1531
+
1532
+ .w6w-exprmodal-group.is-open .w6w-exprmodal-group-inner {
1533
+ padding-bottom: var(--w6w-sp-1-5);
1534
+ }
1535
+
1536
+ @media (prefers-reduced-motion: reduce) {
1537
+ .w6w-exprmodal-group-caret,
1538
+ .w6w-exprmodal-group-body {
1539
+ transition: none;
1540
+ }
1541
+ }
1542
+ /* A labelled block inside a section (Previous steps' Loop). */
1543
+ .w6w-exprmodal-subgroup {
1544
+ display: flex;
1545
+ flex-direction: column;
1330
1546
  gap: 3px;
1331
1547
  }
1332
1548
 
@@ -1336,7 +1552,6 @@ select optgroup {
1336
1552
  letter-spacing: 0.04em;
1337
1553
  text-transform: uppercase;
1338
1554
  color: var(--w6w-muted);
1339
- margin-bottom: var(--w6w-sp-0-5);
1340
1555
  }
1341
1556
 
1342
1557
  .w6w-exprmodal-source {
@@ -1363,6 +1578,18 @@ select optgroup {
1363
1578
  color: var(--w6w-accent);
1364
1579
  }
1365
1580
 
1581
+ /* A step's output field: a plain tree row (just the field's own name), not a
1582
+ chip, so a step with many fields reads as a list rather than a wall of boxes. */
1583
+ .w6w-exprmodal-source.w6w-exprmodal-field {
1584
+ padding: var(--w6w-sp-0-5) var(--w6w-sp-1-5);
1585
+ font-family: var(--w6w-font-mono);
1586
+ font-size: var(--w6w-fs-xs);
1587
+ }
1588
+
1589
+ .w6w-exprmodal-source.w6w-exprmodal-field:hover {
1590
+ color: var(--w6w-accent);
1591
+ }
1592
+
1366
1593
  .w6w-exprmodal-source.w6w-expr-chip-secret {
1367
1594
  color: var(--w6w-danger);
1368
1595
  }
@@ -1460,22 +1687,6 @@ select optgroup {
1460
1687
  background: var(--w6w-panel);
1461
1688
  }
1462
1689
 
1463
- /* The "N fields" toggle with the Metadata toggle stacked under it, so both
1464
- disclosures share one left edge on a step row. */
1465
- .w6w-exprmodal-toggle-stack {
1466
- display: flex;
1467
- flex: none;
1468
- flex-direction: column;
1469
- align-items: flex-start;
1470
- gap: 3px; /* lint-tokens-allow: 3px sits between no two steps of the spacing scale; tight stack of the fields and Metadata toggles */
1471
- }
1472
-
1473
- /* A step with no fields: its Metadata toggle sits at the leaf indentation of
1474
- `.w6w-exprmodal-subsources` (margin 9px + 1px border + sp-2 padding). */
1475
- .w6w-exprmodal-meta-row {
1476
- margin-left: calc(10px + var(--w6w-sp-2)); /* lint-tokens-allow: 9px margin + 1px border of .w6w-exprmodal-subsources */
1477
- }
1478
-
1479
1690
  /* Same 20px-high chrome as `.w6w-exprmodal-render-btn` above — the sizing
1480
1691
  precedent for a dense rail control — but a separate class: this toggles
1481
1692
  the `.w6w-exprmodal-subsources` block below it, the render-btn inserts a
@@ -1633,6 +1844,28 @@ select optgroup {
1633
1844
  gap: var(--w6w-sp-2);
1634
1845
  }
1635
1846
 
1847
+ /* The JSON compose pane (json-binding mode) fills the editor pane like the chips editor does,
1848
+ instead of sitting at its content height over empty space. */
1849
+ .w6w-exprmodal-json {
1850
+ display: flex;
1851
+ flex-direction: column;
1852
+ flex: 1 1 auto;
1853
+ min-height: 180px;
1854
+ }
1855
+
1856
+ .w6w-exprmodal-json > .w6w-json-editor {
1857
+ display: flex;
1858
+ flex-direction: column;
1859
+ flex: 1 1 auto;
1860
+ min-height: 0;
1861
+ }
1862
+
1863
+ /* CodeMirror's own root (the react-codemirror wrapper) — its `.cm-editor` is `height: 100%`. */
1864
+ .w6w-exprmodal-json > .w6w-json-editor > :first-child {
1865
+ flex: 1 1 auto;
1866
+ min-height: 0;
1867
+ }
1868
+
1636
1869
  /* fx toggle — an end decoration INSIDE the field, not a button beside it. The
1637
1870
  WRAPPER owns the input chrome (background/border/radius/min-height and the
1638
1871
  focus ring) and every widget it holds is flattened below, so the value and its
@@ -1662,7 +1895,9 @@ select optgroup {
1662
1895
  .w6w-fx-wrap > select,
1663
1896
  .w6w-fx-wrap > textarea,
1664
1897
  .w6w-fx-wrap > .w6w-multiselect,
1665
- .w6w-fx-wrap > .w6w-expr {
1898
+ .w6w-fx-wrap > .w6w-expr,
1899
+ .w6w-fx-wrap > .w6w-combobox,
1900
+ .w6w-fx-wrap > .w6w-fx-grow {
1666
1901
  flex: 1 1 auto;
1667
1902
  min-width: 0;
1668
1903
  }
@@ -1804,6 +2039,32 @@ select optgroup {
1804
2039
  color: var(--w6w-accent);
1805
2040
  }
1806
2041
 
2042
+ /* The json param's ƒx rides INSIDE the editor box, top-right, as the editor's
2043
+ `overlay` (same mechanism as the `copyable` button in `_step-config.scss`).
2044
+ Opaque backdrop because it sits on line 1; the text keeps right padding so
2045
+ it is never covered; with `copyable` on it steps left of the copy button. */
2046
+ .w6w-json-editor > .w6w-expr-fx {
2047
+ position: absolute;
2048
+ top: var(--w6w-sp-1);
2049
+ right: var(--w6w-sp-1);
2050
+ z-index: 1;
2051
+ border: none;
2052
+ background: var(--w6w-panel);
2053
+ border-radius: 6px;
2054
+ }
2055
+
2056
+ .w6w-json-editor:has(> .w6w-expr-fx) .cm-content {
2057
+ padding-right: var(--w6w-sp-10);
2058
+ }
2059
+
2060
+ .w6w-json-editor:has(> .w6w-copyable-btn) > .w6w-expr-fx {
2061
+ right: calc(var(--w6w-sp-1) * 2 + var(--w6w-sp-8));
2062
+ }
2063
+
2064
+ .w6w-json-editor:has(> .w6w-copyable-btn):has(> .w6w-expr-fx) .cm-content {
2065
+ padding-right: var(--w6w-sp-20);
2066
+ }
2067
+
1807
2068
  /* Copyable — an in-box copy affordance decorating a value-displaying control
1808
2069
  (an <input>, a <textarea>, or CodeBlock's <pre>). Mirrors `.w6w-fx-wrap`
1809
2070
  (`_fx.scss:1-88`): the WRAPPER owns the box chrome and every child it holds
@@ -1963,6 +2224,32 @@ select optgroup {
1963
2224
  min-width: 0;
1964
2225
  }
1965
2226
 
2227
+ /* ── Hover modifier: ResultViewer — the button overlays the top-right corner of
2228
+ ANY child and is revealed on hover/focus; always shown where there is no hover. */
2229
+ .w6w-copyable--hover {
2230
+ position: relative;
2231
+ align-items: flex-start;
2232
+ }
2233
+
2234
+ .w6w-copyable--hover > .w6w-copyable-btn {
2235
+ position: absolute;
2236
+ top: var(--w6w-sp-1);
2237
+ right: var(--w6w-sp-1);
2238
+ background: var(--w6w-panel);
2239
+ border-color: var(--w6w-border);
2240
+ opacity: 0;
2241
+ }
2242
+
2243
+ .w6w-copyable--hover:hover > .w6w-copyable-btn,
2244
+ .w6w-copyable--hover:focus-within > .w6w-copyable-btn {
2245
+ opacity: 1;
2246
+ }
2247
+
2248
+ @media (hover: none) {
2249
+ .w6w-copyable--hover > .w6w-copyable-btn {
2250
+ opacity: 1;
2251
+ }
2252
+ }
1966
2253
  /* The raw {{ }} input stands alone, so it keeps an input box of its own. */
1967
2254
  .w6w-expr-raw {
1968
2255
  width: 100%;
@@ -2273,13 +2560,13 @@ select optgroup {
2273
2560
  /* Typed key/value rows (the `vars` form element / `data` control step). */
2274
2561
  .w6w-datavar-row {
2275
2562
  display: grid;
2276
- grid-template-columns: 1fr 110px 1fr auto;
2563
+ grid-template-columns: minmax(0, 1fr) 110px minmax(0, 1fr) auto;
2277
2564
  gap: var(--w6w-sp-1-5);
2278
- /* Align to the end so that on an `expression` row — where the value cell is a
2279
- taller ExpressionInput (toolbar above its editor) — the key/type/remove
2280
- cells line up with the editor at the bottom, not the toolbar. Single-line
2281
- rows are unaffected (every cell is the same height). */
2282
- align-items: end;
2565
+ align-items: stretch;
2566
+ }
2567
+
2568
+ .w6w-datavar-row > * {
2569
+ min-width: 0;
2283
2570
  }
2284
2571
 
2285
2572
  .w6w-datavar-row input,
@@ -2314,6 +2601,20 @@ select optgroup {
2314
2601
  padding: var(--w6w-sp-1-5) var(--w6w-sp-2-5);
2315
2602
  }
2316
2603
 
2604
+ .w6w-datavar-modes {
2605
+ margin-left: auto;
2606
+ }
2607
+
2608
+ .w6w-datavar-modes .active {
2609
+ background: var(--w6w-panel-2);
2610
+ border-color: var(--w6w-accent);
2611
+ color: var(--w6w-accent);
2612
+ }
2613
+
2614
+ .w6w-datavar-error {
2615
+ color: var(--w6w-danger);
2616
+ }
2617
+
2317
2618
  /* Buttons */
2318
2619
  .w6w-btn {
2319
2620
  background: var(--w6w-accent);
@@ -2628,6 +2929,18 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
2628
2929
  flex-shrink: 0;
2629
2930
  }
2630
2931
 
2932
+ .w6w-json-customized {
2933
+ align-self: center;
2934
+ border: 1px solid var(--w6w-accent);
2935
+ border-radius: var(--w6w-radius-sm, 3px);
2936
+ padding: 3px 7px;
2937
+ background: color-mix(in srgb, var(--w6w-accent) 8%, transparent);
2938
+ color: var(--w6w-accent);
2939
+ font: inherit;
2940
+ font-size: 11px;
2941
+ cursor: pointer;
2942
+ }
2943
+
2631
2944
  .w6w-icon-btn {
2632
2945
  display: inline-flex;
2633
2946
  align-items: center;
@@ -2737,6 +3050,39 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
2737
3050
  }
2738
3051
 
2739
3052
  /* Step builder (sidebar + content) */
3053
+ .w6w-stepbuilder-picker {
3054
+ display: flex;
3055
+ flex-direction: column;
3056
+ gap: var(--w6w-sp-3);
3057
+ height: min(70vh, 560px);
3058
+ min-height: 0;
3059
+ overflow: hidden;
3060
+ }
3061
+
3062
+ .w6w-stepbuilder-picker > .w6w-stepbuilder-search {
3063
+ flex-shrink: 0;
3064
+ }
3065
+
3066
+ .w6w-stepbuilder-picker > .w6w-stepbuilder {
3067
+ flex: 1;
3068
+ min-height: 0;
3069
+ overflow: hidden;
3070
+ }
3071
+
3072
+ .w6w-stepbuilder-picker .w6w-stepbuilder-sidebar {
3073
+ overflow-y: auto;
3074
+ }
3075
+
3076
+ .w6w-stepbuilder-picker .w6w-stepbuilder-content {
3077
+ height: auto;
3078
+ }
3079
+
3080
+ .w6w-stepbuilder-picker .w6w-apppicker-host {
3081
+ height: 100%;
3082
+ min-height: 0;
3083
+ flex: 1;
3084
+ }
3085
+
2740
3086
  .w6w-stepbuilder {
2741
3087
  display: flex;
2742
3088
  gap: var(--w6w-sp-4);
@@ -3081,17 +3427,39 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
3081
3427
  color: var(--w6w-muted-text, var(--w6w-text));
3082
3428
  }
3083
3429
 
3084
- /* A Function/Workflow row, distinct from an app row at a glance: an app is
3085
- somebody else's service, a Function/Workflow is something YOU built here.
3086
- Applied by `CallableRow`, which both the home tab and the Workflows/
3087
- Functions tabs render — so the tint and the glyph travel together and a
3088
- Function looks the same wherever it is offered. */
3089
- .w6w-stepbuilder-item--callable {
3090
- background: var(--w6w-panel);
3091
- border-style: dashed;
3430
+ /* Triggers tab: the "Search all apps" row under the two columns, outside the
3431
+ grid so the columns keep the host's remaining height. */
3432
+ .w6w-readytouse-footer {
3433
+ flex: 0 0 auto;
3434
+ padding-top: var(--w6w-sp-3);
3092
3435
  }
3093
3436
 
3094
- /* Step config (app action / control node): a fixed header (connection + action)
3437
+ .w6w-readytouse-empty {
3438
+ margin: 0;
3439
+ }
3440
+
3441
+ /* A button that reads as an inline link — the empty state's "Search all apps". */
3442
+ .w6w-linkbtn {
3443
+ padding: 0;
3444
+ border: 0;
3445
+ background: none;
3446
+ font: inherit;
3447
+ color: var(--w6w-accent);
3448
+ cursor: pointer;
3449
+ text-decoration: underline;
3450
+ }
3451
+
3452
+ /* A Function/Workflow row, distinct from an app row at a glance: an app is
3453
+ somebody else's service, a Function/Workflow is something YOU built here.
3454
+ Applied by `CallableRow`, which both the home tab and the Workflows/
3455
+ Functions tabs render — so the tint and the glyph travel together and a
3456
+ Function looks the same wherever it is offered. */
3457
+ .w6w-stepbuilder-item--callable {
3458
+ background: var(--w6w-panel);
3459
+ border-style: dashed;
3460
+ }
3461
+
3462
+ /* Step config (app action / control node): a fixed header (connection + action)
3095
3463
  that never scrolls, a scrolling parameters body, and a pinned footer — mirrors
3096
3464
  the fixed-header/scroll-body split used by `.w6w-stepbuilder-apps`. */
3097
3465
  .w6w-stepconfig {
@@ -3105,6 +3473,13 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
3105
3473
  padding-inline: var(--w6w-sp-1);
3106
3474
  }
3107
3475
 
3476
+ /* The add flow's title: the node kind's label, then its editable name. */
3477
+ .w6w-stepbuilder-title {
3478
+ display: inline-flex;
3479
+ align-items: center;
3480
+ gap: var(--w6w-sp-2);
3481
+ }
3482
+
3108
3483
  /* Inline step rename (pencil) shown next to the modal title. */
3109
3484
  .w6w-step-rename {
3110
3485
  display: inline-flex;
@@ -3123,6 +3498,145 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
3123
3498
  font-family: inherit;
3124
3499
  }
3125
3500
 
3501
+ .w6w-router-editor {
3502
+ display: flex;
3503
+ flex-direction: column;
3504
+ gap: var(--w6w-sp-3);
3505
+ }
3506
+
3507
+ .w6w-router-editor > p {
3508
+ margin: 0;
3509
+ }
3510
+
3511
+ .w6w-router-routes {
3512
+ display: flex;
3513
+ flex-direction: column;
3514
+ gap: var(--w6w-sp-2);
3515
+ }
3516
+
3517
+ .w6w-router-route {
3518
+ min-width: 0;
3519
+ }
3520
+
3521
+ .w6w-router-route.is-dragging {
3522
+ opacity: 0.45;
3523
+ }
3524
+
3525
+ .w6w-router-route.is-drop-target {
3526
+ box-shadow: 0 -2px var(--w6w-accent);
3527
+ }
3528
+
3529
+ .w6w-router-route-heading {
3530
+ display: flex;
3531
+ align-items: center;
3532
+ gap: var(--w6w-sp-1);
3533
+ margin-bottom: var(--w6w-sp-1);
3534
+ }
3535
+
3536
+ .w6w-router-route-heading > :last-child {
3537
+ margin-left: auto;
3538
+ }
3539
+
3540
+ .w6w-router-grip {
3541
+ cursor: grab;
3542
+ }
3543
+
3544
+ .w6w-router-grip:active {
3545
+ cursor: grabbing;
3546
+ }
3547
+
3548
+ .w6w-router-route-name {
3549
+ display: inline-flex;
3550
+ align-items: center;
3551
+ gap: var(--w6w-sp-2);
3552
+ border: 0;
3553
+ background: transparent;
3554
+ padding: var(--w6w-sp-1) 0;
3555
+ font: inherit;
3556
+ font-size: var(--w6w-fs-xs);
3557
+ color: var(--w6w-muted);
3558
+ cursor: pointer;
3559
+ }
3560
+
3561
+ .w6w-router-route-name:hover {
3562
+ color: var(--w6w-text);
3563
+ }
3564
+
3565
+ .w6w-router-route-name:disabled {
3566
+ cursor: default;
3567
+ }
3568
+
3569
+ .w6w-router-add {
3570
+ align-self: flex-start;
3571
+ }
3572
+
3573
+ .w6w-condition-editor {
3574
+ position: relative;
3575
+ padding-right: var(--w6w-sp-8);
3576
+ }
3577
+
3578
+ .w6w-condition-row {
3579
+ display: grid;
3580
+ grid-template-columns: auto minmax(0, 1fr) minmax(100px, auto) minmax(0, 1fr);
3581
+ gap: var(--w6w-sp-2);
3582
+ align-items: stretch;
3583
+ }
3584
+
3585
+ .w6w-condition-row > * {
3586
+ min-width: 0;
3587
+ }
3588
+
3589
+ .w6w-condition-row-removable {
3590
+ grid-template-columns: auto minmax(0, 1fr) minmax(100px, auto) minmax(0, 1fr) auto;
3591
+ }
3592
+
3593
+ .w6w-condition-row-boolean {
3594
+ grid-template-columns: auto minmax(0, 1fr) minmax(100px, auto);
3595
+ }
3596
+
3597
+ .w6w-condition-row-boolean.w6w-condition-row-removable {
3598
+ grid-template-columns: auto minmax(0, 1fr) minmax(100px, auto) auto;
3599
+ }
3600
+
3601
+ .w6w-condition-negate {
3602
+ display: flex;
3603
+ align-items: center;
3604
+ gap: var(--w6w-sp-1);
3605
+ font-size: var(--w6w-fs-xs);
3606
+ }
3607
+
3608
+ .w6w-condition-rows {
3609
+ display: flex;
3610
+ flex-direction: column;
3611
+ gap: var(--w6w-sp-2);
3612
+ }
3613
+
3614
+ .w6w-condition-combinator,
3615
+ .w6w-condition-add {
3616
+ align-self: flex-start;
3617
+ }
3618
+
3619
+ .w6w-condition-remove {
3620
+ align-self: center;
3621
+ }
3622
+
3623
+ .w6w-condition-comparison {
3624
+ min-height: var(--w6w-sp-10);
3625
+ padding: var(--w6w-sp-2);
3626
+ background: var(--w6w-panel-2);
3627
+ border: 1px solid var(--w6w-border);
3628
+ border-radius: var(--w6w-radius);
3629
+ color: var(--w6w-text);
3630
+ font: inherit;
3631
+ font-size: var(--w6w-fs-xs);
3632
+ }
3633
+
3634
+ .w6w-condition-mode {
3635
+ position: absolute;
3636
+ right: 0;
3637
+ top: var(--w6w-sp-2);
3638
+ }
3639
+
3126
3640
  .w6w-stepconfig-header {
3127
3641
  display: flex;
3128
3642
  flex-direction: column;
@@ -3444,6 +3958,17 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
3444
3958
  white-space: nowrap;
3445
3959
  }
3446
3960
 
3961
+ /* A trigger node's card (ControlNodeCard, `isTrigger`) — a half-pill: fully
3962
+ rounded entry (left) edge, the ordinary 4px corner on the exit (right) edge,
3963
+ so a run's start reads apart from the steps it feeds. No radius token
3964
+ exists beyond --w6w-radius (10px), hence the literals. Wider left inset
3965
+ clears the curve. */
3966
+ .w6w-flow .w6w-node-trigger {
3967
+ border-radius: 999px 4px 4px 999px;
3968
+ /* lint-tokens-allow: 14px right inset matches the non-trigger card's inline padding */
3969
+ padding: var(--w6w-sp-1-5) 14px var(--w6w-sp-1-5) var(--w6w-sp-4);
3970
+ }
3971
+
3447
3972
  /* The ordinary (success) ports — bumped from the library's own 6px default
3448
3973
  (2026-08-30: "ports must have consistent size") to match the error port
3449
3974
  below exactly. `:not(.w6w-handle-multi)`: the multi-input handle is its
@@ -3547,6 +4072,46 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
3547
4072
  color: white;
3548
4073
  }
3549
4074
 
4075
+ /* A toggle (pause / disable) shows its on state; the divider splits the run controls
4076
+ (run, pause, disable) from the edit controls (edit, copy, delete). */
4077
+ .w6w-node-toolbar-btn[aria-pressed=true] {
4078
+ background: var(--w6w-panel-2);
4079
+ color: var(--w6w-accent);
4080
+ }
4081
+
4082
+ .w6w-node-toolbar-divider {
4083
+ align-self: stretch;
4084
+ width: 1px;
4085
+ margin: 2px var(--w6w-sp-0-5);
4086
+ background: var(--w6w-border);
4087
+ }
4088
+
4089
+ /* Persistent node flags under the card: ⏸ paused (test runs stop before it), ⃠ disabled
4090
+ (skipped in every run). The glyph and the text carry the state, not colour. */
4091
+ .w6w-flow .w6w-node-flag {
4092
+ display: inline-flex;
4093
+ align-items: center;
4094
+ gap: 3px;
4095
+ margin-top: var(--w6w-sp-0-5);
4096
+ margin-right: var(--w6w-sp-1);
4097
+ padding: 0 var(--w6w-sp-1);
4098
+ border: 1px solid var(--w6w-border);
4099
+ border-radius: var(--w6w-radius-sm, 3px);
4100
+ font-size: var(--w6w-fs-xs);
4101
+ background: var(--w6w-panel);
4102
+ color: var(--w6w-text);
4103
+ }
4104
+
4105
+ .w6w-flow .w6w-node-flag-paused {
4106
+ border-color: var(--w6w-accent);
4107
+ color: var(--w6w-accent);
4108
+ }
4109
+
4110
+ /* The card's opacity is inline (it is also the run-status visual), so only the grey is here. */
4111
+ .w6w-flow .w6w-node-disabled > [data-run-status] {
4112
+ filter: grayscale(1);
4113
+ }
4114
+
3550
4115
  .w6w-params-optional > summary {
3551
4116
  cursor: pointer;
3552
4117
  }
@@ -3565,6 +4130,136 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
3565
4130
  color: var(--w6w-muted, inherit);
3566
4131
  }
3567
4132
 
4133
+ /* For each loops (T2.3.1). The Each item edge — a loop-body link, never
4134
+ serialized — reads as a solid line in its loop's colour (`--w6w-loop-color`,
4135
+ set per edge by `paintLoopEdges`), distinct from the error dash. */
4136
+ .w6w-flow .react-flow__edge.w6w-edge-each .react-flow__edge-path {
4137
+ stroke: var(--w6w-loop-color, var(--w6w-accent));
4138
+ }
4139
+
4140
+ .w6w-flow .react-flow__edge.w6w-edge-each .react-flow__edge-text {
4141
+ fill: var(--w6w-accent);
4142
+ }
4143
+
4144
+ .w6w-flow .react-flow__edge.w6w-edge-each .react-flow__edge-textbg {
4145
+ fill: var(--w6w-panel);
4146
+ }
4147
+
4148
+ /* The Each item handle: in its loop's colour, on the right beside the Done handle. */
4149
+ .w6w-flow .react-flow__handle.w6w-handle-each {
4150
+ background: var(--w6w-loop-color, var(--w6w-accent));
4151
+ border-color: var(--w6w-loop-color, var(--w6w-accent));
4152
+ }
4153
+
4154
+ .w6w-flow .w6w-node-foreach .react-flow__handle-right {
4155
+ top: 72%;
4156
+ }
4157
+
4158
+ .w6w-flow .w6w-node-foreach .react-flow__handle-right.w6w-handle-each {
4159
+ top: 28%;
4160
+ }
4161
+
4162
+ .w6w-flow .w6w-foreach-ports {
4163
+ display: flex;
4164
+ flex-direction: column;
4165
+ justify-content: space-between;
4166
+ align-self: stretch;
4167
+ text-align: right;
4168
+ color: var(--w6w-muted);
4169
+ }
4170
+
4171
+ /* A loop-body step looks like any other step; one dot per enclosing For each, just
4172
+ above its top-right corner (named on hover), is all that marks it. */
4173
+ .w6w-flow .w6w-loop-dots {
4174
+ position: absolute;
4175
+ top: -11px; /* lint-tokens-allow: dot sits just above the card edge */
4176
+ right: var(--w6w-sp-1);
4177
+ display: flex;
4178
+ gap: var(--w6w-sp-0-5);
4179
+ }
4180
+
4181
+ .w6w-flow .w6w-loop-dot {
4182
+ width: 8px;
4183
+ height: 8px;
4184
+ border-radius: 50%;
4185
+ box-shadow: 0 0 0 1.5px var(--w6w-panel);
4186
+ cursor: help;
4187
+ }
4188
+
4189
+ .w6w-flow .w6w-loop-legacy-note {
4190
+ max-width: 220px;
4191
+ margin-top: var(--w6w-sp-1);
4192
+ color: var(--w6w-muted);
4193
+ }
4194
+
4195
+ /* The connect-time loop refusal, anchored to its source node. */
4196
+ .w6w-flow .w6w-loop-notice {
4197
+ max-width: 260px;
4198
+ padding: var(--w6w-sp-1) var(--w6w-sp-2);
4199
+ background: var(--w6w-panel);
4200
+ border: 1px solid var(--w6w-danger);
4201
+ border-radius: 8px;
4202
+ color: var(--w6w-danger);
4203
+ }
4204
+
4205
+ .w6w-flow .w6w-router-ports {
4206
+ align-self: stretch;
4207
+ display: grid;
4208
+ align-items: center;
4209
+ margin: -6px 0;
4210
+ max-width: 120px;
4211
+ text-align: right;
4212
+ color: var(--w6w-muted);
4213
+ pointer-events: none;
4214
+ }
4215
+
4216
+ .w6w-flow .w6w-router-ports > span {
4217
+ overflow: hidden;
4218
+ text-overflow: ellipsis;
4219
+ white-space: nowrap;
4220
+ line-height: 1.2;
4221
+ }
4222
+
4223
+ .w6w-flow .w6w-router-fallback-label {
4224
+ position: absolute;
4225
+ top: calc(100% + 2px);
4226
+ left: calc(50% + 8px);
4227
+ white-space: nowrap;
4228
+ pointer-events: none;
4229
+ color: var(--w6w-muted);
4230
+ }
4231
+
4232
+ /* "Write to state" (step settings): the `validateStateConfig` messages for the current config. */
4233
+ .w6w-state-write-issues {
4234
+ color: var(--w6w-danger);
4235
+ }
4236
+
4237
+ /* "Write to state" mapping rows (Pick fields, Append): the shared `.w6w-datavar-row`
4238
+ input style, laid out as output path → state key → remove. The header row uses the
4239
+ same columns so each label sits over its input. Doubled on the row so it beats
4240
+ `.w6w-datavar-row`'s own columns whatever the partial order. */
4241
+ .w6w-state-map-row,
4242
+ .w6w-datavar-row.w6w-state-map-row {
4243
+ display: grid;
4244
+ grid-template-columns: minmax(0, 1fr) 1rem minmax(0, 1fr) 2rem;
4245
+ gap: var(--w6w-sp-1-5);
4246
+ align-items: center;
4247
+ }
4248
+
4249
+ .w6w-state-map-head {
4250
+ color: var(--w6w-muted);
4251
+ font-size: 12px;
4252
+ }
4253
+
4254
+ .w6w-state-map-arrow {
4255
+ color: var(--w6w-muted);
4256
+ text-align: center;
4257
+ }
4258
+
4259
+ .w6w-state-map-add {
4260
+ align-self: flex-start;
4261
+ }
4262
+
3568
4263
  .w6w-section {
3569
4264
  border: 1px solid var(--w6w-border);
3570
4265
  border-radius: 8px;
@@ -3606,22 +4301,60 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
3606
4301
  gap: var(--w6w-sp-2-5);
3607
4302
  }
3608
4303
 
3609
- /* A labeled test-run output block (Result vs Console). */
3610
- .w6w-testout {
4304
+ /* Webhook "Listen for test event": the waiting row, its pulse, and the folded manual payload. */
4305
+ .w6w-listen-waiting {
3611
4306
  display: flex;
3612
- flex-direction: column;
3613
- gap: var(--w6w-sp-1);
4307
+ align-items: center;
4308
+ gap: var(--w6w-sp-2-5);
4309
+ min-width: 0;
3614
4310
  }
3615
-
3616
- .w6w-testout-label {
3617
- font-size: 0.72em;
3618
- font-weight: var(--w6w-fw-semibold);
3619
- letter-spacing: 0.04em;
3620
- text-transform: uppercase;
3621
- color: var(--w6w-muted);
4311
+ .w6w-listen-waiting code {
4312
+ word-break: break-all;
3622
4313
  }
3623
4314
 
3624
- /* Console output reads as a terminal — monospace + muted, distinct from Result. */
4315
+ .w6w-listen-pulse {
4316
+ flex-shrink: 0;
4317
+ width: 8px;
4318
+ height: 8px;
4319
+ border-radius: 50%;
4320
+ background: var(--w6w-accent);
4321
+ animation: w6w-listen-pulse 1.2s ease-in-out infinite;
4322
+ }
4323
+
4324
+ @keyframes w6w-listen-pulse {
4325
+ 0%, 100% {
4326
+ opacity: 1;
4327
+ }
4328
+ 50% {
4329
+ opacity: 0.25;
4330
+ }
4331
+ }
4332
+ @media (prefers-reduced-motion: reduce) {
4333
+ .w6w-listen-pulse {
4334
+ animation: none;
4335
+ }
4336
+ }
4337
+ .w6w-listen-manual > summary {
4338
+ cursor: pointer;
4339
+ margin-bottom: var(--w6w-sp-2);
4340
+ }
4341
+
4342
+ /* A labeled test-run output block (Result vs Console). */
4343
+ .w6w-testout {
4344
+ display: flex;
4345
+ flex-direction: column;
4346
+ gap: var(--w6w-sp-1);
4347
+ }
4348
+
4349
+ .w6w-testout-label {
4350
+ font-size: 0.72em;
4351
+ font-weight: var(--w6w-fw-semibold);
4352
+ letter-spacing: 0.04em;
4353
+ text-transform: uppercase;
4354
+ color: var(--w6w-muted);
4355
+ }
4356
+
4357
+ /* Console output reads as a terminal — monospace + muted, distinct from Result. */
3625
4358
  .w6w-testout-console {
3626
4359
  font-family: var(--w6w-font-mono);
3627
4360
  color: var(--w6w-muted);
@@ -3648,7 +4381,7 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
3648
4381
 
3649
4382
  /* Host-embedded ActionTestForm (studio's ConnectionTesterModal wraps it in its
3650
4383
  OWN full modal). The widget then fills that modal as a full-height flex column:
3651
- the params + saved-tests region scrolls, and the action bar is a bottom-pinned,
4384
+ the params / result / saved-tests cards scroll, and the action bar is a bottom-pinned,
3652
4385
  non-scrolling footer. These classes are only applied in `embedded` mode, so the
3653
4386
  inline / own-pop-out layout is untouched. */
3654
4387
  .w6w-tester-embedded-root {
@@ -3669,22 +4402,6 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
3669
4402
  overflow: auto;
3670
4403
  }
3671
4404
 
3672
- .w6w-tester-embedded-cols {
3673
- display: flex;
3674
- gap: var(--w6w-sp-4);
3675
- align-items: flex-start;
3676
- }
3677
-
3678
- .w6w-tester-embedded-main {
3679
- flex: 1;
3680
- min-width: 0;
3681
- }
3682
-
3683
- .w6w-tester-embedded-rail {
3684
- width: 260px;
3685
- flex-shrink: 0;
3686
- }
3687
-
3688
4405
  /* The action bar as a pinned footer: kept out of the scroll region and separated
3689
4406
  with a top border so Run / Save / Delete stay visible however long the params. */
3690
4407
  .w6w-tester-actions-footer {
@@ -3728,6 +4445,68 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
3728
4445
  baseline bar, `degraded` taller, `down` the full cell, and `unknown` a hatched
3729
4446
  outline with no solid fill (nothing was measured: not a good day, not a bad
3730
4447
  one). Each cell also carries a `title` with its day and state. */
4448
+ /* ── ResultViewer (T0.1.1) — one result value, shown by format: a collapsible
4449
+ JSON tree, xml/html/markdown source, or vendor html in a sandboxed iframe.
4450
+ The viewer owns its OWN internal spacing only; the host pane spaces it. */
4451
+ .w6w-result-viewer {
4452
+ display: flex;
4453
+ flex-direction: column;
4454
+ gap: var(--w6w-sp-1);
4455
+ min-width: 0;
4456
+ }
4457
+
4458
+ /* The toolbar — Expand all / Collapse all for a tree, Rendered / Source for
4459
+ html and markdown. End-aligned so it sits under the copy button's corner. */
4460
+ .w6w-result-viewer-bar {
4461
+ display: flex;
4462
+ justify-content: flex-end;
4463
+ gap: var(--w6w-sp-1);
4464
+ }
4465
+
4466
+ /* The viewport: the one element that scrolls, capped by the inline `maxHeight`.
4467
+ Mono + 0.85em are `.w6w-result`'s type (above), because both present the same
4468
+ thing to the reader. */
4469
+ .w6w-result-viewer-body {
4470
+ flex: 1 1 auto;
4471
+ min-width: 0;
4472
+ font-family: var(--w6w-font-mono);
4473
+ /* lint-tokens-allow: mirrors `.w6w-result`'s result type size above; the value is pinned by T0.1.1 */
4474
+ font-size: 0.85em;
4475
+ }
4476
+
4477
+ /* A plain-text result wraps instead of scrolling sideways: prose has no
4478
+ meaningful line structure to preserve (a code block does — it keeps `wrap`
4479
+ off by default for that reason). */
4480
+ .w6w-result-viewer-text {
4481
+ margin: 0;
4482
+ white-space: pre-wrap;
4483
+ word-break: break-word;
4484
+ }
4485
+
4486
+ /* One indent step per nesting level, and no list markers: the `<summary>`
4487
+ triangle already says "this node opens". */
4488
+ .w6w-json-children {
4489
+ padding-left: var(--w6w-sp-4);
4490
+ }
4491
+
4492
+ /* Keys and counts recede; values stay the reading colour. */
4493
+ .w6w-json-key,
4494
+ .w6w-json-count {
4495
+ color: var(--w6w-muted);
4496
+ }
4497
+
4498
+ /* The sandboxed frame: fills the body's width and gives the preview a fixed
4499
+ viewport (it cannot size itself to its own content from outside). `display:
4500
+ block` is load-bearing — an inline iframe sits on a text baseline, so the
4501
+ 3 px descender made the 320 px body scroll 3 px and drew a stray scrollbar. */
4502
+ .w6w-html-preview {
4503
+ display: block;
4504
+ width: 100%;
4505
+ height: 320px;
4506
+ border: none;
4507
+ background: var(--w6w-panel);
4508
+ }
4509
+
3731
4510
  .w6w-uptime-strip {
3732
4511
  display: grid;
3733
4512
  grid-auto-flow: column;
@@ -4076,15 +4855,17 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
4076
4855
  /* The expanded form of a clipped value (`resolved-clip.ts`). A block with its
4077
4856
  own scroll, so "Show all" can never restore the unbounded inline paste that
4078
4857
  buried the other params and the run's result. */
4858
+ .w6w-resolved-copy {
4859
+ margin: var(--w6w-sp-1) 0;
4860
+ }
4861
+
4079
4862
  .w6w-resolved-text-full {
4080
4863
  display: block;
4081
- margin: var(--w6w-sp-1) 0;
4864
+ margin: 0;
4082
4865
  max-height: 14em;
4083
4866
  overflow: auto;
4084
- padding: var(--w6w-sp-2);
4085
- background: var(--w6w-panel-2);
4086
- border: 1px solid var(--w6w-border);
4087
- border-radius: var(--w6w-radius);
4867
+ padding: 0;
4868
+ padding-right: var(--w6w-sp-10);
4088
4869
  font-family: var(--w6w-font-mono);
4089
4870
  font-size: 0.85em; /* lint-tokens-allow: matches the sibling code-typography rules above in this same file */
4090
4871
  line-height: var(--w6w-lh-normal);
@@ -4108,6 +4889,44 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
4108
4889
  text-decoration: underline;
4109
4890
  }
4110
4891
 
4892
+ /* Every CodeMirror code view (`JsonEditor`, `CodeEditor`, `YamlEditor`) FILLS
4893
+ its bordered box. The border is on `.cm-editor`, which react-codemirror
4894
+ sizes with the `height` / `minHeight` / `maxHeight` props and whose
4895
+ `.cm-scroller` it gives `height: 100%`. With only a min/max height (the Data
4896
+ page's Document modal) that percentage has nothing definite to resolve
4897
+ against, so the scroller — and the gutter column inside it — stopped at the
4898
+ last content line, the horizontal scrollbar sat right under it, and the rest
4899
+ of the box was empty.
4900
+
4901
+ The react-codemirror root (the wrapper's first child) is a one-cell grid.
4902
+ A grid area is definite once its track is sized, so `.cm-editor`, stretched
4903
+ into it, has a definite height whether that came from `height`, from
4904
+ `minHeight` or from the content (capped by `maxHeight`), and the scroller and
4905
+ the gutters fill it. `minmax(0, 1fr)` keeps a long line from widening the
4906
+ editor: the scroller scrolls it instead. The wrapper is a flex column so a
4907
+ wrapper given a definite height (a flex item with `min-height: 0`, as in
4908
+ `_step-config.scss`) hands that height down to the editor.
4909
+
4910
+ Everything sits under `:where()` (specificity 0 for the wrapper part), so a
4911
+ call site's own layout for the same elements — `_expression-modal.scss`'s
4912
+ JSON pane, a host's dialog — still wins. */
4913
+ :where(.w6w-json-editor, .w6w-code-editor, .w6w-yaml-editor) {
4914
+ display: flex;
4915
+ flex-direction: column;
4916
+ }
4917
+
4918
+ :where(.w6w-json-editor, .w6w-code-editor, .w6w-yaml-editor) > :first-child {
4919
+ display: grid;
4920
+ grid-template-columns: minmax(0, 1fr);
4921
+ flex: 1 1 auto;
4922
+ min-height: 0;
4923
+ }
4924
+
4925
+ :where(.w6w-json-editor, .w6w-code-editor, .w6w-yaml-editor) .cm-scroller,
4926
+ :where(.w6w-json-editor, .w6w-code-editor, .w6w-yaml-editor) .cm-gutters {
4927
+ min-height: 100%;
4928
+ }
4929
+
4111
4930
  .w6w-icon-button {
4112
4931
  display: inline-flex;
4113
4932
  align-items: center;
@@ -4411,8 +5230,40 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
4411
5230
  flex-wrap: wrap;
4412
5231
  }
4413
5232
 
4414
- .w6w-execution-log-row-label {
5233
+ /* Label over "App · Action": takes the row's free width, so the timing stays right-aligned. */
5234
+ .w6w-execution-log-row-title {
5235
+ display: flex;
5236
+ flex-direction: column;
4415
5237
  flex: 1 1 auto;
5238
+ min-width: 0;
5239
+ gap: var(--w6w-sp-0-5);
5240
+ }
5241
+
5242
+ .w6w-execution-log-row-uses {
5243
+ color: var(--w6w-muted);
5244
+ font-size: var(--w6w-fs-xs);
5245
+ overflow: hidden;
5246
+ text-overflow: ellipsis;
5247
+ white-space: nowrap;
5248
+ }
5249
+
5250
+ /* "Trigger" / "Node": a small outlined tag before the label; a trigger takes the accent. */
5251
+ .w6w-execution-log-kind {
5252
+ flex: 0 0 auto;
5253
+ padding: 0 var(--w6w-sp-1-5);
5254
+ border: 1px solid var(--w6w-border);
5255
+ border-radius: var(--w6w-radius);
5256
+ color: var(--w6w-muted);
5257
+ font-size: var(--w6w-fs-xs);
5258
+ line-height: var(--w6w-lh-tight);
5259
+ }
5260
+
5261
+ .w6w-execution-log-kind-trigger {
5262
+ border-color: var(--w6w-accent);
5263
+ color: var(--w6w-accent);
5264
+ }
5265
+
5266
+ .w6w-execution-log-row-label {
4416
5267
  min-width: 0;
4417
5268
  font-weight: var(--w6w-fw-medium);
4418
5269
  overflow: hidden;
@@ -4424,6 +5275,24 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
4424
5275
  "<ts> → <ts> (elapsed)" text inside the row's box. With `flex-shrink: 0` it
4425
5276
  kept its max-content width, wrapped onto its own flex line, and ran past the
4426
5277
  row's border. */
5278
+ .w6w-execution-log-skip-reason {
5279
+ flex: 0 0 auto;
5280
+ color: var(--w6w-muted);
5281
+ font-size: var(--w6w-fs-xs);
5282
+ }
5283
+
5284
+ /* Steps on other paths (not part of this run): one muted, collapsed line after the list. */
5285
+ .w6w-execution-log-off-path {
5286
+ margin-top: var(--w6w-sp-2);
5287
+ }
5288
+ .w6w-execution-log-off-path summary {
5289
+ cursor: pointer;
5290
+ }
5291
+ .w6w-execution-log-off-path ul {
5292
+ margin: var(--w6w-sp-1) 0 0;
5293
+ padding-left: var(--w6w-sp-4);
5294
+ }
5295
+
4427
5296
  .w6w-execution-log-timing {
4428
5297
  flex: 0 1 auto;
4429
5298
  min-width: 0;
@@ -4434,6 +5303,12 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
4434
5303
  .w6w-execution-log-json {
4435
5304
  min-width: 0;
4436
5305
  }
5306
+ .w6w-execution-log-json > summary {
5307
+ cursor: pointer;
5308
+ }
5309
+ .w6w-execution-log-json > .w6w-result-viewer {
5310
+ margin-top: var(--w6w-sp-2);
5311
+ }
4437
5312
 
4438
5313
  /* Error blocks (step `error`, run `runError`/`stepErrors`). The box itself is
4439
5314
  `.w6w-result.w6w-error` (`_tester.scss`) — TriggerFillForm's StepError
@@ -4460,6 +5335,103 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
4460
5335
  margin-bottom: var(--w6w-sp-1);
4461
5336
  }
4462
5337
 
5338
+ /* --- For each iterations (T1.5.1) ----------------------------------------
5339
+ Chips carry the status as TEXT as well as colour; failed/running also differ
5340
+ in border style, so colour is never the only signal. */
5341
+ .w6w-execution-log-foreach {
5342
+ margin-top: var(--w6w-sp-2);
5343
+ padding-left: var(--w6w-sp-3);
5344
+ border-left: 2px solid var(--w6w-border);
5345
+ }
5346
+
5347
+ .w6w-execution-log-iteration-chips {
5348
+ display: flex;
5349
+ flex-wrap: wrap;
5350
+ gap: var(--w6w-sp-1);
5351
+ }
5352
+
5353
+ .w6w-execution-log-iteration-chip {
5354
+ padding: var(--w6w-sp-0-5) var(--w6w-sp-2);
5355
+ border: 1px solid var(--w6w-border);
5356
+ border-radius: 999px;
5357
+ font-size: var(--w6w-fs-xs);
5358
+ white-space: nowrap;
5359
+ }
5360
+
5361
+ .w6w-execution-log-iteration-chip-succeeded {
5362
+ border-color: var(--w6w-success);
5363
+ }
5364
+
5365
+ .w6w-execution-log-iteration-chip-failed {
5366
+ border-color: var(--w6w-danger);
5367
+ border-width: 2px;
5368
+ }
5369
+
5370
+ .w6w-execution-log-iteration-chip-running {
5371
+ border-color: var(--w6w-accent);
5372
+ border-style: dashed;
5373
+ }
5374
+
5375
+ .w6w-execution-log-subrows {
5376
+ display: flex;
5377
+ flex-direction: column;
5378
+ gap: var(--w6w-sp-2);
5379
+ margin: 0;
5380
+ padding: 0;
5381
+ list-style: none;
5382
+ }
5383
+
5384
+ .w6w-execution-log-subrow-head {
5385
+ display: flex;
5386
+ align-items: center;
5387
+ justify-content: space-between;
5388
+ gap: var(--w6w-sp-2);
5389
+ }
5390
+
5391
+ /* An If / Stop / Router row's resolved comparisons. Operands are arbitrary
5392
+ resolved values (long URLs), so they wrap rather than widen the panel. */
5393
+ .w6w-execution-log-conditions {
5394
+ font-size: var(--w6w-fs-xs);
5395
+ table-layout: auto;
5396
+ }
5397
+
5398
+ .w6w-execution-log-conditions code {
5399
+ word-break: break-all;
5400
+ }
5401
+
5402
+ .w6w-execution-log-condition-true {
5403
+ color: var(--w6w-success);
5404
+ }
5405
+
5406
+ .w6w-execution-log-condition-false {
5407
+ color: var(--w6w-danger);
5408
+ }
5409
+
5410
+ /* A step's State block: its state writes in recorded order (`state.<path>: old → new`),
5411
+ warnings marked, then the run's state after this step. Values are arbitrary JSON,
5412
+ so rows wrap rather than widen the panel. */
5413
+ .w6w-execution-log-state {
5414
+ min-width: 0;
5415
+ }
5416
+
5417
+ .w6w-execution-log-state-writes {
5418
+ display: flex;
5419
+ flex-direction: column;
5420
+ gap: var(--w6w-sp-0-5);
5421
+ margin: 0;
5422
+ padding: 0;
5423
+ list-style: none;
5424
+ font-family: var(--w6w-font-mono);
5425
+ word-break: break-all;
5426
+ }
5427
+
5428
+ .w6w-execution-log-state-warning {
5429
+ padding-left: var(--w6w-sp-1-5);
5430
+ border-left: 2px solid var(--w6w-danger);
5431
+ color: var(--w6w-danger);
5432
+ font-family: inherit;
5433
+ }
5434
+
4463
5435
  /* --- ExecutionList ------------------------------------------------------- */
4464
5436
  /* Rows of past executions: a click-to-select list plus a prev/next pager.
4465
5437
  Closer in shape to `.w6w-execution-log` (`_execution-log.scss`) than to
@@ -4496,9 +5468,30 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
4496
5468
  }
4497
5469
 
4498
5470
  .w6w-execution-list-rows > li {
5471
+ display: flex;
5472
+ align-items: center;
5473
+ gap: var(--w6w-sp-1);
5474
+ min-width: 0;
5475
+ }
5476
+
5477
+ /* The row stays the full-width click target; a Re-run control, when the host
5478
+ offers one, sits beside it rather than inside it (a button cannot nest in
5479
+ the row's own `<button>`). */
5480
+ .w6w-execution-list-rows > li > .w6w-list-item {
5481
+ flex: 1 1 auto;
4499
5482
  min-width: 0;
4500
5483
  }
4501
5484
 
5485
+ .w6w-execution-list-rerun {
5486
+ flex-shrink: 0;
5487
+ }
5488
+
5489
+ /* The row's input snippet: inline in the subtitle, so the subtitle's own
5490
+ one-line ellipsis cuts it — no box of its own, just the code face. */
5491
+ .w6w-execution-input-preview {
5492
+ font-family: var(--w6w-font-mono);
5493
+ }
5494
+
4502
5495
  /* Both non-list branches (the loading and the empty `<p>`) hang straight off
4503
5496
  the root, so one direct-child rule covers them and neither needs a class the
4504
5497
  pinned markup doesn't have. Same dashed box `.w6w-execution-log-empty`
@@ -4651,6 +5644,12 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
4651
5644
  min-width: 0;
4652
5645
  }
4653
5646
 
5647
+ /* The Back control sits alone on its own line, flush left, so the detail
5648
+ reads as a page the list was swapped for. */
5649
+ .w6w-execution-detail-back {
5650
+ display: flex;
5651
+ }
5652
+
4654
5653
  /* Title block left, controls right; the actions keep their width, the name
4655
5654
  wraps or ellipses into whatever is left. */
4656
5655
  .w6w-execution-detail-head {
@@ -4688,6 +5687,13 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
4688
5687
  flex-shrink: 0;
4689
5688
  }
4690
5689
 
5690
+ /* Glyph + label on one baseline (`.w6w-btn` itself is not a flex box). */
5691
+ .w6w-execution-detail-rerun {
5692
+ display: inline-flex;
5693
+ align-items: center;
5694
+ gap: var(--w6w-sp-1);
5695
+ }
5696
+
4691
5697
  .w6w-execution-detail-id-line {
4692
5698
  margin: 0;
4693
5699
  }
@@ -4739,29 +5745,68 @@ dialog.w6w-modal.w6w-modal-fullscreen .w6w-modal-header {
4739
5745
  font-size: var(--w6w-fs-sm);
4740
5746
  }
4741
5747
 
4742
- /*
4743
- * Server resources — the four presentational variants from
4744
- * `src/components/ServerResources.tsx` (card, small card, rail, badge).
4745
- *
4746
- * Spacing and type come from the `--w6w-sp-*` / `--w6w-fs-*` ramps only, so a
4747
- * host that re-tunes the scale re-tunes these too; box geometry (heights,
4748
- * track widths, radii) is deliberately literal, as everywhere else in this
4749
- * package. The only colour that is not a themed token reference is the
4750
- * sparkline stroke, which is `--w6w-accent` in both modes.
4751
- *
4752
- * The rail variant is the size-sensitive one: it lands in a ~200 px sidebar
4753
- * column, so every part of it is `min-width: 0`-safe and the sparkline is
4754
- * scaled by `preserveAspectRatio="none"` rather than given an intrinsic width.
4755
- */
4756
- .w6w-server-resources {
4757
- display: flex;
4758
- flex-direction: column;
4759
- gap: var(--w6w-sp-2);
4760
- min-width: 0;
4761
- color: var(--w6w-text);
4762
- font-size: var(--w6w-fs-sm);
4763
- line-height: var(--w6w-lh-normal);
4764
- }
5748
+ /* "Re-run": marks an execution started by History's Re-run — on its list row
5749
+ (after the title) and on its detail ("Re-run of <id>"). */
5750
+ .w6w-execution-rerun-tag {
5751
+ display: inline-block;
5752
+ margin-left: var(--w6w-sp-2);
5753
+ padding: 0 var(--w6w-sp-1-5);
5754
+ border: 1px solid var(--w6w-accent);
5755
+ border-radius: var(--w6w-radius);
5756
+ color: var(--w6w-accent);
5757
+ font-size: var(--w6w-fs-xs);
5758
+ line-height: var(--w6w-lh-tight);
5759
+ vertical-align: middle;
5760
+ }
5761
+
5762
+ .w6w-execution-detail-rerun-of {
5763
+ display: flex;
5764
+ align-items: center;
5765
+ gap: var(--w6w-sp-1-5);
5766
+ margin: 0;
5767
+ }
5768
+
5769
+ .w6w-execution-detail-rerun-of .w6w-execution-rerun-tag {
5770
+ margin-left: 0;
5771
+ }
5772
+
5773
+ .w6w-execution-detail-rerun-of code {
5774
+ font-family: var(--w6w-font-mono);
5775
+ font-size: var(--w6w-fs-sm);
5776
+ }
5777
+
5778
+ .w6w-execution-detail-rerun-of-link {
5779
+ padding: 0;
5780
+ border: none;
5781
+ background: none;
5782
+ color: var(--w6w-accent);
5783
+ cursor: pointer;
5784
+ text-decoration: underline;
5785
+ }
5786
+
5787
+ /*
5788
+ * Server resources — the four presentational variants from
5789
+ * `src/components/ServerResources.tsx` (card, small card, rail, badge).
5790
+ *
5791
+ * Spacing and type come from the `--w6w-sp-*` / `--w6w-fs-*` ramps only, so a
5792
+ * host that re-tunes the scale re-tunes these too; box geometry (heights,
5793
+ * track widths, radii) is deliberately literal, as everywhere else in this
5794
+ * package. The only colour that is not a themed token reference is the
5795
+ * sparkline stroke, which is `--w6w-accent` in both modes.
5796
+ *
5797
+ * The rail variant is the size-sensitive one: it lands in a ~200 px sidebar
5798
+ * column, so every part of it is `min-width: 0`-safe and the sparkline is
5799
+ * scaled by `preserveAspectRatio="none"` rather than given an intrinsic width.
5800
+ */
5801
+ .w6w-server-resources {
5802
+ display: flex;
5803
+ flex-direction: column;
5804
+ gap: var(--w6w-sp-2);
5805
+ min-width: 0;
5806
+ color: var(--w6w-text);
5807
+ font-size: var(--w6w-fs-sm);
5808
+ line-height: var(--w6w-lh-normal);
5809
+ }
4765
5810
 
4766
5811
  .w6w-server-resources__title {
4767
5812
  margin: 0;
@@ -5341,16 +6386,23 @@ button.w6w-rel-open:focus-visible strong {
5341
6386
  .w6w-callable-list-body {
5342
6387
  border: 1px solid var(--w6w-border);
5343
6388
  border-radius: 8px;
5344
- padding: var(--w6w-sp-2);
6389
+ background: var(--w6w-panel);
6390
+ overflow: hidden;
5345
6391
  }
5346
6392
 
5347
- .w6w-callable-list-rows {
5348
- list-style: none;
5349
- margin: 0;
5350
- padding: 0;
6393
+ .w6w-callable-list-main {
5351
6394
  display: flex;
5352
6395
  flex-direction: column;
5353
- gap: var(--w6w-sp-1);
6396
+ gap: var(--w6w-sp-0-5);
6397
+ min-width: 0;
6398
+ text-align: left;
6399
+ }
6400
+
6401
+ .w6w-callable-list-open {
6402
+ all: unset;
6403
+ cursor: pointer;
6404
+ display: block;
6405
+ width: 100%;
5354
6406
  }
5355
6407
 
5356
6408
  .w6w-callable-list-trail {
@@ -5367,6 +6419,75 @@ button.w6w-rel-open:focus-visible strong {
5367
6419
  font-size: var(--w6w-fs-xs);
5368
6420
  }
5369
6421
 
6422
+ .w6w-callable-list-row-desc {
6423
+ display: block;
6424
+ }
6425
+
6426
+ /* The frame is the ONLY element of a DataTable that draws an outer border and a
6427
+ radius (T-3). `overflow: hidden` clips the cells at the rounded corners, so
6428
+ the table below never needs an outer edge of its own. */
6429
+ .w6w-data-table-frame {
6430
+ border: 1px solid var(--w6w-border);
6431
+ border-radius: var(--w6w-radius);
6432
+ background: var(--w6w-panel);
6433
+ overflow: hidden;
6434
+ }
6435
+
6436
+ /* No outer border here (T-3): the frame above already draws it. */
6437
+ .w6w-data-table {
6438
+ width: 100%;
6439
+ border-collapse: collapse;
6440
+ color: var(--w6w-text);
6441
+ }
6442
+
6443
+ /* Inner separators only (T-3): a right border on every cell but the last column
6444
+ and a bottom border on every row but the last. First column, first row, last
6445
+ column and last row therefore paint nothing on the outside — one border per
6446
+ outer edge, from the frame. */
6447
+ .w6w-data-table th,
6448
+ .w6w-data-table td {
6449
+ border-right: 1px solid var(--w6w-border);
6450
+ border-bottom: 1px solid var(--w6w-border);
6451
+ padding: var(--w6w-sp-2) var(--w6w-sp-3);
6452
+ text-align: left;
6453
+ vertical-align: middle;
6454
+ }
6455
+
6456
+ .w6w-data-table th:last-child,
6457
+ .w6w-data-table td:last-child {
6458
+ border-right: 0;
6459
+ }
6460
+
6461
+ .w6w-data-table tbody tr:last-child > td {
6462
+ border-bottom: 0;
6463
+ }
6464
+
6465
+ /* Header-only table (rows=[] + loading, e.g. a list still fetching): there is no
6466
+ `tbody tr:last-child` to strip, so the header's bottom border would sit right
6467
+ on top of the frame's own bottom edge and paint it twice. With no body row the
6468
+ header is the last row, so it paints no bottom border — the frame supplies it. */
6469
+ .w6w-data-table:not(:has(tbody tr)) th {
6470
+ border-bottom: 0;
6471
+ }
6472
+
6473
+ .w6w-data-table th {
6474
+ color: var(--w6w-muted);
6475
+ }
6476
+
6477
+ .w6w-data-table-row-clickable {
6478
+ cursor: pointer;
6479
+ }
6480
+
6481
+ .w6w-data-table-row-clickable:hover {
6482
+ background: var(--w6w-panel-2);
6483
+ }
6484
+
6485
+ .w6w-data-table-link {
6486
+ display: block;
6487
+ color: inherit;
6488
+ text-decoration: none;
6489
+ }
6490
+
5370
6491
  /* Connections (D-12u): ConnectionList, ConnectionHeader and the state tags they share. */
5371
6492
  .w6w-connection-list {
5372
6493
  display: flex;
@@ -5391,26 +6512,12 @@ button.w6w-rel-open:focus-visible strong {
5391
6512
  flex-shrink: 0;
5392
6513
  }
5393
6514
 
5394
- .w6w-connection-rows {
5395
- list-style: none;
5396
- margin: 0;
5397
- padding: 0;
5398
- display: flex;
5399
- flex-direction: column;
5400
- gap: var(--w6w-sp-1);
5401
- }
5402
-
5403
- .w6w-connection-row {
5404
- display: flex;
6515
+ .w6w-connection-cell-name {
6516
+ display: inline-flex;
5405
6517
  align-items: center;
5406
6518
  gap: var(--w6w-sp-2);
5407
6519
  }
5408
6520
 
5409
- .w6w-connection-row-main {
5410
- flex: 1;
5411
- min-width: 0;
5412
- }
5413
-
5414
6521
  .w6w-connection-row-actions {
5415
6522
  display: flex;
5416
6523
  gap: var(--w6w-sp-1-5);
@@ -5484,3 +6591,642 @@ button.w6w-rel-open:focus-visible strong {
5484
6591
  .w6w-connection-id-copy:focus-within .w6w-copyable-btn {
5485
6592
  opacity: 1;
5486
6593
  }
6594
+
6595
+ /* Data stores (F-2): DataStoreList, ColumnsEditor, CsvPanel and the data-grid/entry rules ported from Studio's Data store detail page. */
6596
+ .w6w-data-store-list {
6597
+ display: flex;
6598
+ flex-direction: column;
6599
+ gap: var(--w6w-sp-3);
6600
+ }
6601
+
6602
+ .w6w-data-store-list-head {
6603
+ display: flex;
6604
+ justify-content: space-between;
6605
+ align-items: flex-start;
6606
+ gap: var(--w6w-sp-4);
6607
+ }
6608
+
6609
+ .w6w-data-store-list-heading {
6610
+ min-width: 0;
6611
+ }
6612
+
6613
+ .w6w-data-store-list-actions {
6614
+ display: flex;
6615
+ gap: var(--w6w-sp-2);
6616
+ flex-shrink: 0;
6617
+ }
6618
+
6619
+ .w6w-data-columns-head {
6620
+ display: flex;
6621
+ justify-content: space-between;
6622
+ align-items: center;
6623
+ }
6624
+
6625
+ .w6w-data-columns-list {
6626
+ list-style: none;
6627
+ margin: 0;
6628
+ padding: 0;
6629
+ display: flex;
6630
+ flex-direction: column;
6631
+ gap: var(--w6w-sp-2);
6632
+ }
6633
+
6634
+ .w6w-data-columns-row {
6635
+ display: flex;
6636
+ flex-wrap: wrap;
6637
+ align-items: center;
6638
+ gap: var(--w6w-sp-2);
6639
+ }
6640
+
6641
+ .w6w-data-columns-required {
6642
+ display: flex;
6643
+ align-items: center;
6644
+ gap: var(--w6w-sp-1);
6645
+ }
6646
+
6647
+ .w6w-data-columns-error {
6648
+ flex-basis: 100%;
6649
+ }
6650
+
6651
+ .w6w-data-entries-toolbar {
6652
+ display: flex;
6653
+ align-items: center;
6654
+ gap: var(--w6w-sp-2);
6655
+ margin: var(--w6w-sp-3) 0;
6656
+ }
6657
+
6658
+ .w6w-data-entries-toolbar input[type=search] {
6659
+ flex: 1;
6660
+ max-width: 20rem;
6661
+ /* Same box as `.w6w-field` inputs — no host page styles a bare search input. */
6662
+ background: var(--w6w-panel-2);
6663
+ border: 1px solid var(--w6w-border);
6664
+ border-radius: 8px;
6665
+ padding: var(--w6w-sp-2) var(--w6w-sp-2-5);
6666
+ color: var(--w6w-text);
6667
+ font-size: var(--w6w-fs-sm);
6668
+ font-family: inherit;
6669
+ }
6670
+
6671
+ .w6w-data-entries-toolbar input[type=search]:focus {
6672
+ outline: none;
6673
+ border-color: var(--w6w-accent);
6674
+ }
6675
+
6676
+ /* Entries open a viewer. Keep the list quiet and bounded, even for long keys or JSON. */
6677
+ .w6w-data-entry-list {
6678
+ table-layout: fixed;
6679
+ }
6680
+
6681
+ .w6w-data-entry-key,
6682
+ .w6w-data-entry-preview {
6683
+ display: block;
6684
+ width: 100%;
6685
+ overflow: hidden;
6686
+ white-space: nowrap;
6687
+ text-overflow: ellipsis;
6688
+ }
6689
+
6690
+ .w6w-data-entry-key {
6691
+ appearance: none;
6692
+ border: 0;
6693
+ border-radius: 0;
6694
+ background: transparent;
6695
+ padding: 0;
6696
+ color: inherit;
6697
+ font: inherit;
6698
+ text-align: left;
6699
+ cursor: pointer;
6700
+ }
6701
+
6702
+ .w6w-data-entry-key:focus-visible {
6703
+ outline: 2px solid var(--w6w-accent);
6704
+ outline-offset: 2px;
6705
+ }
6706
+
6707
+ .w6w-data-entry-delete {
6708
+ margin-inline-end: auto;
6709
+ }
6710
+
6711
+ /* The wide-table pane: the sheet scrolls INSIDE this box, so a 24-column
6712
+ table never widens its container and the page itself never overflows. */
6713
+ .w6w-data-grid-scroll {
6714
+ overflow-x: auto;
6715
+ max-width: 100%;
6716
+ }
6717
+
6718
+ .w6w-data-grid-scroll input {
6719
+ min-width: var(--w6w-sp-16);
6720
+ }
6721
+
6722
+ .w6w-data-cell {
6723
+ display: flex;
6724
+ flex-direction: column;
6725
+ gap: var(--w6w-sp-1);
6726
+ }
6727
+
6728
+ .w6w-data-cell-error {
6729
+ color: var(--w6w-danger);
6730
+ font-size: var(--w6w-fs-xs);
6731
+ }
6732
+
6733
+ .w6w-data-row-actions {
6734
+ display: flex;
6735
+ align-items: center;
6736
+ gap: var(--w6w-sp-2);
6737
+ }
6738
+
6739
+ .w6w-data-entry-editor {
6740
+ margin-top: var(--w6w-sp-4);
6741
+ }
6742
+
6743
+ .w6w-data-entry-editor-actions {
6744
+ display: flex;
6745
+ gap: var(--w6w-sp-2);
6746
+ margin-top: var(--w6w-sp-3);
6747
+ }
6748
+
6749
+ .w6w-data-csv-panel {
6750
+ margin-bottom: var(--w6w-sp-4);
6751
+ }
6752
+
6753
+ .w6w-data-csv-panel-actions {
6754
+ display: flex;
6755
+ align-items: center;
6756
+ gap: var(--w6w-sp-3);
6757
+ }
6758
+
6759
+ .w6w-empty-state {
6760
+ position: relative;
6761
+ min-height: 10rem;
6762
+ background: var(--w6w-panel);
6763
+ color: var(--w6w-text);
6764
+ border: 1px solid var(--w6w-border);
6765
+ }
6766
+
6767
+ .w6w-empty-state-inner {
6768
+ position: absolute;
6769
+ top: 50%;
6770
+ left: 50%;
6771
+ transform: translate(-50%, -50%);
6772
+ display: flex;
6773
+ flex-direction: column;
6774
+ align-items: center;
6775
+ gap: var(--w6w-sp-2);
6776
+ text-align: center;
6777
+ }
6778
+
6779
+ .w6w-empty-state-body {
6780
+ color: var(--w6w-muted);
6781
+ }
6782
+
6783
+ .w6w-canvas-layer {
6784
+ position: absolute;
6785
+ top: 0;
6786
+ left: 0;
6787
+ width: 0;
6788
+ height: 0;
6789
+ overflow: visible;
6790
+ pointer-events: none;
6791
+ }
6792
+
6793
+ .w6w-canvas-section,
6794
+ .w6w-canvas-note {
6795
+ position: absolute;
6796
+ box-sizing: border-box;
6797
+ border: 1px solid var(--w6w-canvas-color, var(--w6w-border));
6798
+ border-radius: var(--w6w-radius, 6px);
6799
+ background: color-mix(in srgb, var(--w6w-canvas-color, var(--w6w-panel)) 14%, transparent);
6800
+ pointer-events: none;
6801
+ }
6802
+
6803
+ .w6w-canvas-note {
6804
+ border-color: color-mix(in srgb, var(--w6w-canvas-color, var(--w6w-border)) 55%, var(--w6w-panel));
6805
+ background: color-mix(in srgb, var(--w6w-canvas-color, var(--w6w-panel)) 32%, var(--w6w-panel));
6806
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
6807
+ }
6808
+
6809
+ .w6w-canvas-note.selected,
6810
+ .w6w-canvas-section.selected {
6811
+ outline: 2px solid var(--w6w-accent);
6812
+ outline-offset: 2px;
6813
+ }
6814
+
6815
+ .w6w-canvas-note-body {
6816
+ pointer-events: auto;
6817
+ color: var(--w6w-text);
6818
+ }
6819
+
6820
+ .w6w-canvas-section-title {
6821
+ position: absolute;
6822
+ max-width: 320px;
6823
+ overflow: hidden;
6824
+ padding: var(--w6w-sp-0-5) var(--w6w-sp-1-5);
6825
+ border-radius: 6px;
6826
+ color: color-mix(in srgb, var(--w6w-canvas-color, var(--w6w-text)) 55%, var(--w6w-text));
6827
+ font-size: var(--w6w-fs-sm);
6828
+ font-weight: var(--w6w-fw-semibold);
6829
+ text-overflow: ellipsis;
6830
+ white-space: nowrap;
6831
+ }
6832
+
6833
+ .w6w-canvas-section-title.editable {
6834
+ pointer-events: auto;
6835
+ cursor: grab;
6836
+ touch-action: none;
6837
+ }
6838
+
6839
+ .w6w-canvas-section-title.editable:hover {
6840
+ background: color-mix(in srgb, var(--w6w-canvas-color, var(--w6w-panel)) 18%, transparent);
6841
+ }
6842
+
6843
+ .w6w-canvas-note-body {
6844
+ box-sizing: border-box;
6845
+ height: 100%;
6846
+ overflow: auto;
6847
+ padding: var(--w6w-sp-3) var(--w6w-sp-3);
6848
+ font-size: var(--w6w-fs-sm);
6849
+ line-height: var(--w6w-lh-normal);
6850
+ overflow-wrap: anywhere;
6851
+ }
6852
+ .w6w-canvas-note-body h1,
6853
+ .w6w-canvas-note-body h2,
6854
+ .w6w-canvas-note-body h3,
6855
+ .w6w-canvas-note-body h4,
6856
+ .w6w-canvas-note-body h5,
6857
+ .w6w-canvas-note-body h6,
6858
+ .w6w-canvas-note-body p,
6859
+ .w6w-canvas-note-body ul,
6860
+ .w6w-canvas-note-body ol {
6861
+ margin: 0 0 var(--w6w-sp-1);
6862
+ }
6863
+ .w6w-canvas-note-body h1 {
6864
+ font-size: var(--w6w-fs-2xl);
6865
+ line-height: var(--w6w-lh-tight);
6866
+ }
6867
+ .w6w-canvas-note-body h2 {
6868
+ font-size: var(--w6w-fs-xl);
6869
+ line-height: var(--w6w-lh-tight);
6870
+ }
6871
+ .w6w-canvas-note-body h3 {
6872
+ font-size: var(--w6w-fs-lg);
6873
+ line-height: var(--w6w-lh-tight);
6874
+ }
6875
+ .w6w-canvas-note-body h4,
6876
+ .w6w-canvas-note-body h5,
6877
+ .w6w-canvas-note-body h6 {
6878
+ font-size: var(--w6w-fs-base);
6879
+ }
6880
+
6881
+ .w6w-canvas-note-editor {
6882
+ outline: none;
6883
+ cursor: text;
6884
+ }
6885
+ .w6w-canvas-note-editor > div,
6886
+ .w6w-canvas-note-editor > h1,
6887
+ .w6w-canvas-note-editor > h2,
6888
+ .w6w-canvas-note-editor > h3,
6889
+ .w6w-canvas-note-editor > h4,
6890
+ .w6w-canvas-note-editor > h5,
6891
+ .w6w-canvas-note-editor > h6 {
6892
+ margin: 0;
6893
+ }
6894
+
6895
+ .w6w-note-resize {
6896
+ position: absolute;
6897
+ right: var(--w6w-sp-0-5);
6898
+ bottom: var(--w6w-sp-0-5);
6899
+ width: 14px;
6900
+ height: 14px;
6901
+ cursor: nwse-resize;
6902
+ pointer-events: auto;
6903
+ touch-action: none;
6904
+ opacity: 0;
6905
+ background: linear-gradient(135deg, transparent 45%, var(--w6w-muted) 45% 52%, transparent 52% 70%, var(--w6w-muted) 70% 77%, transparent 77%);
6906
+ }
6907
+
6908
+ .w6w-canvas-note:hover .w6w-note-resize,
6909
+ .w6w-canvas-note.selected .w6w-note-resize,
6910
+ .w6w-canvas-section.selected .w6w-note-resize {
6911
+ opacity: 1;
6912
+ }
6913
+
6914
+ .w6w-canvas-note-empty {
6915
+ color: var(--w6w-muted);
6916
+ font-style: italic;
6917
+ }
6918
+
6919
+ .w6w-note-toolbar {
6920
+ position: absolute;
6921
+ display: flex;
6922
+ align-items: center;
6923
+ gap: var(--w6w-sp-0-5);
6924
+ padding: var(--w6w-sp-1);
6925
+ background: var(--w6w-panel);
6926
+ color: var(--w6w-text);
6927
+ border: 1px solid var(--w6w-border);
6928
+ border-radius: 10px;
6929
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
6930
+ font-size: var(--w6w-fs-sm);
6931
+ white-space: nowrap;
6932
+ pointer-events: auto;
6933
+ }
6934
+ .w6w-note-toolbar .w6w-icon-btn {
6935
+ gap: var(--w6w-sp-1);
6936
+ padding: var(--w6w-sp-1-5);
6937
+ color: var(--w6w-text);
6938
+ }
6939
+
6940
+ .w6w-note-toolbar-group {
6941
+ position: relative;
6942
+ display: inline-flex;
6943
+ }
6944
+
6945
+ .w6w-note-toolbar-sep {
6946
+ align-self: stretch;
6947
+ width: 1px;
6948
+ margin: var(--w6w-sp-0-5) var(--w6w-sp-0-5);
6949
+ background: var(--w6w-border);
6950
+ }
6951
+
6952
+ .w6w-note-grip {
6953
+ cursor: grab;
6954
+ touch-action: none;
6955
+ }
6956
+
6957
+ .w6w-note-grip:active {
6958
+ cursor: grabbing;
6959
+ }
6960
+
6961
+ .w6w-note-toolbar .w6w-section-title-input {
6962
+ flex: none;
6963
+ width: auto;
6964
+ margin: 0;
6965
+ padding: var(--w6w-sp-1) var(--w6w-sp-1-5);
6966
+ border: 1px solid transparent;
6967
+ border-radius: 6px;
6968
+ background: transparent;
6969
+ color: inherit;
6970
+ font: inherit;
6971
+ font-weight: var(--w6w-fw-semibold);
6972
+ }
6973
+
6974
+ .w6w-note-toolbar .w6w-section-title-input:hover {
6975
+ border-color: var(--w6w-border);
6976
+ }
6977
+
6978
+ .w6w-note-toolbar .w6w-section-title-input:focus {
6979
+ border-color: var(--w6w-accent);
6980
+ outline: none;
6981
+ }
6982
+
6983
+ .w6w-note-size-btn {
6984
+ font-weight: var(--w6w-fw-semibold);
6985
+ }
6986
+
6987
+ .w6w-note-delete:hover {
6988
+ color: var(--w6w-danger);
6989
+ }
6990
+
6991
+ .w6w-note-swatch {
6992
+ width: 16px;
6993
+ height: 16px;
6994
+ border-radius: 50%;
6995
+ background: var(--w6w-canvas-color);
6996
+ box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.15);
6997
+ }
6998
+
6999
+ .w6w-note-menu {
7000
+ position: absolute;
7001
+ top: calc(100% + var(--w6w-sp-2));
7002
+ left: 0;
7003
+ z-index: 1;
7004
+ display: flex;
7005
+ padding: var(--w6w-sp-1);
7006
+ background: var(--w6w-panel);
7007
+ border: 1px solid var(--w6w-border);
7008
+ border-radius: 10px;
7009
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
7010
+ }
7011
+
7012
+ .w6w-note-menu-colors {
7013
+ display: grid;
7014
+ grid-template-columns: repeat(5, 24px);
7015
+ gap: var(--w6w-sp-1-5);
7016
+ padding: var(--w6w-sp-2);
7017
+ }
7018
+
7019
+ .w6w-note-swatch-btn {
7020
+ position: relative;
7021
+ width: 24px;
7022
+ height: 24px;
7023
+ padding: 0;
7024
+ border: 2px solid transparent;
7025
+ border-radius: 50%;
7026
+ background: var(--w6w-canvas-color);
7027
+ box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.15);
7028
+ cursor: pointer;
7029
+ }
7030
+
7031
+ .w6w-note-swatch-btn.active {
7032
+ border-color: var(--w6w-panel);
7033
+ box-shadow: 0 0 0 2px var(--w6w-accent);
7034
+ }
7035
+
7036
+ .w6w-note-swatch-custom {
7037
+ overflow: hidden;
7038
+ background: conic-gradient(#f87171, #facc15, #4ade80, #60a5fa, #a78bfa, #f87171);
7039
+ }
7040
+ .w6w-note-swatch-custom input {
7041
+ position: absolute;
7042
+ inset: 0;
7043
+ width: 100%;
7044
+ height: 100%;
7045
+ opacity: 0;
7046
+ cursor: pointer;
7047
+ }
7048
+
7049
+ .w6w-note-menu-sizes {
7050
+ flex-direction: column;
7051
+ min-width: 150px;
7052
+ }
7053
+
7054
+ .w6w-note-menu-item {
7055
+ padding: var(--w6w-sp-1) var(--w6w-sp-2);
7056
+ border: 0;
7057
+ border-radius: 6px;
7058
+ background: transparent;
7059
+ color: inherit;
7060
+ text-align: left;
7061
+ font-weight: var(--w6w-fw-semibold);
7062
+ cursor: pointer;
7063
+ }
7064
+
7065
+ .w6w-note-menu-item:hover {
7066
+ background: var(--w6w-panel-2);
7067
+ }
7068
+
7069
+ .w6w-canvas-menu {
7070
+ top: auto;
7071
+ z-index: 10;
7072
+ flex-direction: column;
7073
+ min-width: 170px;
7074
+ }
7075
+ .w6w-canvas-menu .w6w-note-menu-item {
7076
+ font-weight: var(--w6w-fw-medium);
7077
+ }
7078
+ .w6w-canvas-menu .w6w-note-menu-item:focus-visible {
7079
+ outline: none;
7080
+ background: var(--w6w-panel-2);
7081
+ }
7082
+ .w6w-canvas-menu .w6w-note-menu-item.danger {
7083
+ color: var(--w6w-danger);
7084
+ }
7085
+ .w6w-canvas-menu .w6w-note-menu-item.separated {
7086
+ position: relative;
7087
+ margin-top: var(--w6w-sp-2);
7088
+ }
7089
+ .w6w-canvas-menu .w6w-note-menu-item.separated::before {
7090
+ content: "";
7091
+ position: absolute;
7092
+ top: calc(-1 * var(--w6w-sp-1) - 0.5px);
7093
+ right: 0;
7094
+ left: 0;
7095
+ border-top: 1px solid var(--w6w-border);
7096
+ }
7097
+ .w6w-canvas-menu .w6w-note-menu-item[aria-disabled=true] {
7098
+ opacity: 0.45;
7099
+ cursor: default;
7100
+ background: transparent;
7101
+ }
7102
+
7103
+ .w6w-note-size-1 {
7104
+ font-size: var(--w6w-fs-xl);
7105
+ }
7106
+
7107
+ .w6w-note-size-2 {
7108
+ font-size: var(--w6w-fs-lg);
7109
+ }
7110
+
7111
+ .w6w-note-size-3 {
7112
+ font-size: var(--w6w-fs-base);
7113
+ }
7114
+
7115
+ .w6w-note-size-0 {
7116
+ font-size: var(--w6w-fs-sm);
7117
+ font-weight: var(--w6w-fw-regular);
7118
+ }
7119
+
7120
+ .w6w-flow-tools {
7121
+ display: flex;
7122
+ align-items: center;
7123
+ gap: var(--w6w-sp-1-5);
7124
+ }
7125
+
7126
+ .w6w-flow-tool {
7127
+ padding: var(--w6w-sp-2);
7128
+ background: var(--w6w-panel);
7129
+ color: var(--w6w-text);
7130
+ border: 1px solid var(--w6w-border);
7131
+ }
7132
+
7133
+ .w6w-flow-tool.active {
7134
+ border-color: var(--w6w-accent);
7135
+ color: var(--w6w-accent);
7136
+ background: color-mix(in srgb, var(--w6w-accent) 12%, var(--w6w-panel));
7137
+ }
7138
+
7139
+ .w6w-section-draw {
7140
+ position: absolute;
7141
+ inset: 0;
7142
+ z-index: 4;
7143
+ cursor: crosshair;
7144
+ touch-action: none;
7145
+ }
7146
+
7147
+ .w6w-section-draw-rect {
7148
+ position: absolute;
7149
+ box-sizing: border-box;
7150
+ border: 2px dashed var(--w6w-accent);
7151
+ border-radius: var(--w6w-radius, 6px);
7152
+ background: color-mix(in srgb, var(--w6w-accent) 10%, transparent);
7153
+ pointer-events: none;
7154
+ }
7155
+
7156
+ /* BudgetMeter — usage-vs-limit bar with a reserved segment.
7157
+ *
7158
+ * Token-only: every colour is a --w6w-* custom property, so a host restyles
7159
+ * the meter (including the over-limit state) by overriding tokens, never by
7160
+ * targeting a hex value here. */
7161
+ .w6w-budget-meter {
7162
+ display: flex;
7163
+ flex-direction: column;
7164
+ gap: var(--w6w-sp-1);
7165
+ }
7166
+
7167
+ .w6w-budget-meter-head {
7168
+ display: flex;
7169
+ align-items: baseline;
7170
+ justify-content: space-between;
7171
+ gap: var(--w6w-sp-2);
7172
+ color: var(--w6w-muted);
7173
+ }
7174
+
7175
+ .w6w-budget-meter-label {
7176
+ color: var(--w6w-text);
7177
+ }
7178
+
7179
+ .w6w-budget-meter-value {
7180
+ font-variant-numeric: tabular-nums;
7181
+ }
7182
+
7183
+ .w6w-budget-meter-track {
7184
+ display: flex;
7185
+ width: 100%;
7186
+ height: 8px;
7187
+ overflow: hidden;
7188
+ background: var(--w6w-panel-2);
7189
+ border: 1px solid var(--w6w-border);
7190
+ border-radius: var(--w6w-radius);
7191
+ }
7192
+
7193
+ .w6w-budget-meter-used {
7194
+ height: 100%;
7195
+ background: var(--w6w-accent);
7196
+ }
7197
+
7198
+ .w6w-budget-meter-reserved {
7199
+ height: 100%;
7200
+ background: var(--w6w-health-degraded);
7201
+ }
7202
+
7203
+ .w6w-budget-meter-over .w6w-budget-meter-used {
7204
+ background: var(--w6w-danger);
7205
+ }
7206
+
7207
+ .w6w-budget-meter-over .w6w-budget-meter-value {
7208
+ color: var(--w6w-danger);
7209
+ }
7210
+
7211
+ /* Markdown: the leading YAML frontmatter, collapsed above the rendered body. The body itself
7212
+ is unstyled here — the host styles it through the `className` it passes. */
7213
+ .w6w-md-frontmatter {
7214
+ margin-bottom: var(--w6w-sp-2);
7215
+ border: 1px solid var(--w6w-border);
7216
+ border-radius: 8px;
7217
+ padding: var(--w6w-sp-1-5) var(--w6w-sp-2-5);
7218
+ font-size: var(--w6w-fs-sm);
7219
+ }
7220
+
7221
+ .w6w-md-frontmatter > summary {
7222
+ cursor: pointer;
7223
+ color: var(--w6w-muted);
7224
+ }
7225
+
7226
+ .w6w-md-frontmatter > pre {
7227
+ margin: var(--w6w-sp-2) 0 0;
7228
+ overflow-x: auto;
7229
+ font-family: var(--w6w-font-mono);
7230
+ white-space: pre-wrap;
7231
+ overflow-wrap: anywhere;
7232
+ }