@kontourai/survey 7.0.0 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +2 -1
  3. package/dist/src/calibration.d.ts +15 -0
  4. package/dist/src/calibration.js +11 -0
  5. package/dist/src/canonical-reviewed-trust-input.d.ts +10 -0
  6. package/dist/src/canonical-reviewed-trust-input.js +15 -0
  7. package/dist/src/console/review-console-server.d.ts +20 -0
  8. package/dist/src/console/review-console-server.js +75 -4
  9. package/dist/src/decision-carry-forward.d.ts +157 -0
  10. package/dist/src/decision-carry-forward.js +295 -0
  11. package/dist/src/extraction-envelope.d.ts +4 -3
  12. package/dist/src/extraction-envelope.js +3 -3
  13. package/dist/src/field-states.d.ts +100 -0
  14. package/dist/src/field-states.js +169 -0
  15. package/dist/src/index.d.ts +3 -1
  16. package/dist/src/index.js +2 -0
  17. package/dist/src/mcp/review-mcp.js +44 -13
  18. package/dist/src/review-resource.d.ts +29 -0
  19. package/dist/src/review-session-file.d.ts +6 -0
  20. package/dist/src/review-session-file.js +2 -0
  21. package/dist/src/review-workbench/audit-sample.d.ts +32 -0
  22. package/dist/src/review-workbench/audit-sample.js +53 -0
  23. package/dist/src/review-workbench/extraction-inspector.d.ts +1 -1
  24. package/dist/src/review-workbench/extraction-inspector.js +1 -1
  25. package/dist/src/review-workbench/field-state-view.d.ts +15 -0
  26. package/dist/src/review-workbench/field-state-view.js +84 -0
  27. package/dist/src/review-workbench/review-queue-session.d.ts +46 -1
  28. package/dist/src/review-workbench/review-queue-session.js +74 -3
  29. package/dist/src/review-workbench/review-session-replay.d.ts +1 -1
  30. package/dist/src/review-workbench/review-session-replay.js +15 -1
  31. package/dist/src/review-workbench/review-workbench-css.generated.d.ts +9 -0
  32. package/dist/src/review-workbench/review-workbench-css.generated.js +453 -43
  33. package/dist/src/review-workbench/review-workbench-element.js +68 -212
  34. package/dist/src/review-workbench/review-workbench.css +386 -42
  35. package/dist/src/review-workbench/review-workbench.d.ts +13 -1
  36. package/dist/src/review-workbench/review-workbench.js +41 -15
  37. package/dist/src/review-workbench/review-workbench.standalone.css +37 -23
  38. package/dist/src/review-workbench/vendor/kontourai-ui/tokens/themes.css +189 -10
  39. package/dist/src/review-workbench/vendor/kontourai-ui/tokens/tokens.css +156 -5
  40. package/package.json +13 -8
@@ -1,5 +1,5 @@
1
1
  // Generated by scripts/copy-review-workbench-package-assets.cjs from CSS sources.
2
- // Do not edit directly; edit examples/review-workbench/review-workbench.css or vendor tokens instead.
2
+ // Do not edit directly; edit examples/review-workbench/review-workbench.css or bump @kontourai/ui instead.
3
3
  /* eslint-disable */
4
4
  export const REVIEW_WORKBENCH_CSS = `/* Bundled, scoped Survey Review Workbench styles for downstream embeds. Font loading is left to the host app. */
5
5
  .survey-workbench-embed{
@@ -23,12 +23,23 @@ export const REVIEW_WORKBENCH_CSS = `/* Bundled, scoped Survey Review Workbench
23
23
  /* text */
24
24
  --k-text: #eef3f8;
25
25
  --k-text-muted: #aebccb;
26
- --k-text-faint: #72869b;
26
+ --k-text-faint: #75889d;
27
27
 
28
- /* brand */
28
+ /* brand — the product identity slot (eyebrows, accents, the progress sweep).
29
+ A product theme or a white-label deployment may override it; nothing
30
+ interactive reads it, so a brand that fails contrast cannot break a
31
+ button or a focus ring. */
29
32
  --k-brand: #5ce0c6;
30
33
  --k-brand-contrast: #06080b;
31
34
 
35
+ /* interaction roles — primary actions and keyboard focus. They hold their
36
+ own values instead of var(--k-brand): an alias would make every brand
37
+ override repaint buttons whether or not the new color can carry text.
38
+ Each theme and mode sets its action and focus beside its brand. */
39
+ --k-action: #5ce0c6;
40
+ --k-action-contrast: #06080b;
41
+ --k-focus: #5ce0c6;
42
+
32
43
  /* semantic status scale */
33
44
  --k-positive: #34d399;
34
45
  --k-caution: #f3b14b;
@@ -39,6 +50,51 @@ export const REVIEW_WORKBENCH_CSS = `/* Bundled, scoped Survey Review Workbench
39
50
  --k-caution-soft: color-mix(in oklab, var(--k-caution) 14%, transparent);
40
51
  --k-negative-soft: color-mix(in oklab, var(--k-negative) 14%, transparent);
41
52
  --k-active-soft: color-mix(in oklab, var(--k-active) 14%, transparent);
53
+ /* Text on a filled status tone (tone badges, status-toned buttons). Its own
54
+ role so a white-label --k-brand-contrast cannot recolor status text. */
55
+ --k-status-contrast: #06080b;
56
+
57
+ /* trust states — Surface's claim statuses (TRUST_STATUSES, in Surface's
58
+ order), shown as presentation states: separate from the five status
59
+ tones and from the product accents. Each state has its own ink (label,
60
+ glyph, border), fill (the chip's own background, so its text pair does
61
+ not depend on the container) and line style (the non-color cue borders
62
+ and chart strokes reuse). Values are literal, never aliases: an alias
63
+ declared here resolves here, so a light or themed scope would inherit
64
+ this scope's hue (ui#78). */
65
+ --k-trust-unknown: #a2acb7;
66
+ --k-trust-unknown-fill: #252d39;
67
+ --k-trust-unknown-line: dotted;
68
+ --k-trust-proposed: #87b1fd;
69
+ --k-trust-proposed-fill: #222d42;
70
+ --k-trust-proposed-line: dashed;
71
+ --k-trust-assumed: #c4a4fe;
72
+ --k-trust-assumed-fill: #2a2c43;
73
+ --k-trust-assumed-line: dashed;
74
+ --k-trust-verified: #50d492;
75
+ --k-trust-verified-fill: #1a3233;
76
+ --k-trust-verified-line: solid;
77
+ --k-trust-stale: #f6b84d;
78
+ --k-trust-stale-fill: #312e2a;
79
+ --k-trust-stale-line: dotted;
80
+ --k-trust-disputed: #ec81c0;
81
+ --k-trust-disputed-fill: #30273a;
82
+ --k-trust-disputed-line: double;
83
+ --k-trust-superseded: #6bc8d3;
84
+ --k-trust-superseded-fill: #1e313d;
85
+ --k-trust-superseded-line: dotted;
86
+ --k-trust-rejected: #f66d67;
87
+ --k-trust-rejected-fill: #31242d;
88
+ --k-trust-rejected-line: solid;
89
+ --k-trust-revoked: #fdc2a2;
90
+ --k-trust-revoked-fill: #323036;
91
+ --k-trust-revoked-line: dotted;
92
+
93
+ /* trust basis — the line after a trust-state chip. Caveat facets
94
+ (Model-derived, not evaluated, cited only, contradicts, failed) are
95
+ underlined with a dashed line in the text's own color: a line-style cue,
96
+ never a hue. Names no color, so it inherits the rated muted text. */
97
+ --k-basis-caveat-decoration: underline dashed 1px;
42
98
 
43
99
  /* spacing */
44
100
  --k-space-1: 4px;
@@ -89,8 +145,11 @@ export const REVIEW_WORKBENCH_CSS = `/* Bundled, scoped Survey Review Workbench
89
145
  --k-tracking-wide: 0.08em;
90
146
 
91
147
  /* focus — one ring contract so every interactive primitive (controls,
92
- buttons, links) shows the same keyboard-focus affordance. */
93
- --k-focus-ring: var(--k-brand);
148
+ buttons, links) shows the same keyboard-focus affordance. The ring color
149
+ is the --k-focus role; --k-focus-ring is kept as a compatibility alias for
150
+ consumer styles. Primitives read --k-focus directly, so an override of
151
+ --k-focus on any element reaches them. */
152
+ --k-focus-ring: var(--k-focus);
94
153
  --k-focus-ring-width: 2px;
95
154
  --k-focus-ring-offset: 2px;
96
155
 
@@ -106,6 +165,63 @@ export const REVIEW_WORKBENCH_CSS = `/* Bundled, scoped Survey Review Workbench
106
165
  --k-z-toast: 1400;
107
166
  }
108
167
 
168
+ /* The dark contract again, for a data-theme="dark" element below a light
169
+ element (ui#80). It sets exactly the tokens the light block sets, with the
170
+ :root values; check:contrast fails if either drifts. Mode-independent
171
+ tokens (spacing, type, radii, fonts) are not reset, so a theme's own values
172
+ for them survive a mode switch. With no light ancestor there is nothing to
173
+ reset, so it does not match and a dark element simply inherits. */
174
+ .survey-workbench-embed[data-theme="dark"]:where([data-theme="light"] *){
175
+ color-scheme: dark;
176
+ --k-bg: #0a0e13;
177
+ --k-panel: #111824;
178
+ --k-panel-raised: #16202d;
179
+ --k-line: rgba(150, 180, 210, 0.12);
180
+ --k-line-strong: rgba(150, 180, 210, 0.22);
181
+ --k-shadow: 0 26px 60px -42px rgba(0, 0, 0, 0.95);
182
+ --k-elevation-overlay:
183
+ 0 1px 2px rgba(0, 0, 0, 0.35),
184
+ 0 12px 32px rgba(0, 0, 0, 0.45),
185
+ 0 24px 56px rgba(0, 0, 0, 0.4);
186
+ --k-text: #eef3f8;
187
+ --k-text-muted: #aebccb;
188
+ --k-text-faint: #75889d;
189
+ --k-brand: #5ce0c6;
190
+ --k-brand-contrast: #06080b;
191
+ --k-action: #5ce0c6;
192
+ --k-action-contrast: #06080b;
193
+ --k-focus: #5ce0c6;
194
+ --k-positive: #34d399;
195
+ --k-caution: #f3b14b;
196
+ --k-negative: #ff6f6f;
197
+ --k-neutral: #6f8095;
198
+ --k-active: #7aa2ff;
199
+ --k-status-contrast: #06080b;
200
+ --k-trust-unknown: #a2acb7;
201
+ --k-trust-unknown-fill: #252d39;
202
+ --k-trust-proposed: #87b1fd;
203
+ --k-trust-proposed-fill: #222d42;
204
+ --k-trust-assumed: #c4a4fe;
205
+ --k-trust-assumed-fill: #2a2c43;
206
+ --k-trust-verified: #50d492;
207
+ --k-trust-verified-fill: #1a3233;
208
+ --k-trust-stale: #f6b84d;
209
+ --k-trust-stale-fill: #312e2a;
210
+ --k-trust-disputed: #ec81c0;
211
+ --k-trust-disputed-fill: #30273a;
212
+ --k-trust-superseded: #6bc8d3;
213
+ --k-trust-superseded-fill: #1e313d;
214
+ --k-trust-rejected: #f66d67;
215
+ --k-trust-rejected-fill: #31242d;
216
+ --k-trust-revoked: #fdc2a2;
217
+ --k-trust-revoked-fill: #323036;
218
+ --k-focus-ring: var(--k-focus);
219
+ --k-positive-soft: color-mix(in oklab, var(--k-positive) 14%, transparent);
220
+ --k-caution-soft: color-mix(in oklab, var(--k-caution) 14%, transparent);
221
+ --k-negative-soft: color-mix(in oklab, var(--k-negative) 14%, transparent);
222
+ --k-active-soft: color-mix(in oklab, var(--k-active) 14%, transparent);
223
+ }
224
+
109
225
  .survey-workbench-embed[data-theme="light"]{
110
226
  color-scheme: light;
111
227
  --k-bg: #f5f4ef;
@@ -120,25 +236,92 @@ export const REVIEW_WORKBENCH_CSS = `/* Bundled, scoped Survey Review Workbench
120
236
  0 18px 48px rgba(0, 0, 0, 0.08);
121
237
  --k-text: #202124;
122
238
  --k-text-muted: #5b626b;
123
- --k-text-faint: #707782;
239
+ --k-text-faint: #6a707b;
124
240
  /* The dark-mode brand (#5ce0c6) is too light to carry white text or read as
125
241
  text on the light canvas (~1.6:1). Darken it for light mode: #0e7c64 holds
126
242
  the teal hue while clearing WCAG AA both as a button fill (white text,
127
243
  5.1:1) and as text on --k-bg (4.7:1). */
128
244
  --k-brand: #0e7c64;
129
245
  --k-brand-contrast: #ffffff;
246
+ --k-action: #0e7c64;
247
+ --k-action-contrast: #ffffff;
248
+ --k-focus: #0e7c64;
130
249
  --k-positive: #168257;
131
250
  --k-caution: #8a5a00;
132
251
  --k-negative: #c83b3b;
133
252
  --k-neutral: #5f6975;
134
253
  --k-active: #3f6fd6;
254
+ --k-status-contrast: #ffffff;
255
+
256
+ /* Light trust inks are dark enough that each label clears AA on its own
257
+ tinted fill; check:contrast rates every pair. */
258
+ --k-trust-unknown: #535c66;
259
+ --k-trust-unknown-fill: #eeeff0;
260
+ --k-trust-proposed: #2855ad;
261
+ --k-trust-proposed-fill: #eaeef7;
262
+ --k-trust-assumed: #6938a7;
263
+ --k-trust-assumed-fill: #f0ebf6;
264
+ --k-trust-verified: #006d42;
265
+ --k-trust-verified-fill: #e6f0ec;
266
+ --k-trust-stale: #855a00;
267
+ --k-trust-stale-fill: #f3efe6;
268
+ --k-trust-disputed: #94296f;
269
+ --k-trust-disputed-fill: #f4eaf1;
270
+ --k-trust-superseded: #016770;
271
+ --k-trust-superseded-fill: #e6f0f1;
272
+ --k-trust-rejected: #a12628;
273
+ --k-trust-rejected-fill: #f6e9ea;
274
+ --k-trust-revoked: #713408;
275
+ --k-trust-revoked-fill: #f1ebe6;
276
+
277
+ /* Tokens derived with var() resolve on the element that declares them and
278
+ are inherited as computed values, so each scope that changes an input
279
+ redeclares them. Without this, data-theme on a descendant of <html> would
280
+ keep the dark default's ring and soft fills. */
281
+ --k-focus-ring: var(--k-focus);
282
+ --k-positive-soft: color-mix(in oklab, var(--k-positive) 14%, transparent);
283
+ --k-caution-soft: color-mix(in oklab, var(--k-caution) 14%, transparent);
284
+ --k-negative-soft: color-mix(in oklab, var(--k-negative) 14%, transparent);
285
+ --k-active-soft: color-mix(in oklab, var(--k-active) 14%, transparent);
135
286
  }
136
287
 
137
288
 
289
+ /* Product themes. Each theme sets its brand (identity) and, beside it, the
290
+ action and focus roles. The roles hold their own values rather than aliasing
291
+ the brand, so a later brand override cannot break primary actions or focus.
292
+ Every scope that sets --k-action also sets --k-action-contrast: a theme
293
+ nested under another theme's scope would otherwise inherit that scope's text
294
+ color onto its own fill.
295
+
296
+ Every scope that sets --k-focus also redeclares --k-focus-ring, and every
297
+ scope that sets a status hue redeclares its soft fill: a var()-derived token
298
+ resolves where it is declared, so without this a scope below the root would
299
+ inherit the root's value (issue 78). check:tokens enforces both.
300
+
301
+ Each light block matches three placements of the theme class and
302
+ data-theme="light": on one element, the theme class below the light
303
+ attribute, and the light attribute below the theme class. The last is
304
+ wrapped in :where() so it weighs the same as the bare [data-theme="light"]
305
+ block in tokens.css (which it follows, so it wins there) and less than a
306
+ theme class sitting on the light element itself: a nested theme keeps its
307
+ own identity.
308
+
309
+ Scoping rule (ui#80, ui#84; DESIGN.md "Theme and mode scoping"): the
310
+ nearest data-theme picks the mode and the nearest theme class picks the
311
+ product. The :where(:not(...)) tails enforce "nearest" without changing a
312
+ selector's weight: the class-below-attribute form stops at a nearer
313
+ data-theme="dark", and the attribute-below-class form stops at a nearer
314
+ (or its own) other theme class. Weights are unchanged, so an override
315
+ declared later with these selectors still wins. */
316
+
138
317
  .survey-workbench-embed.theme-survey{
139
318
  --k-bg: #06080b;
140
319
  --k-brand: #5ce0c6;
141
- --k-text-faint: #6f8095;
320
+ --k-action: #5ce0c6;
321
+ --k-action-contrast: #06080b;
322
+ --k-focus: #5ce0c6;
323
+ --k-text-faint: #78889b;
324
+ --k-focus-ring: var(--k-focus);
142
325
  }
143
326
 
144
327
  .survey-workbench-embed.theme-console{
@@ -150,6 +333,9 @@ export const REVIEW_WORKBENCH_CSS = `/* Bundled, scoped Survey Review Workbench
150
333
  --k-radius-overlay: 0;
151
334
  --k-brand: #c9ff4a;
152
335
  --k-brand-contrast: #11120f;
336
+ --k-action: #c9ff4a;
337
+ --k-action-contrast: #11120f;
338
+ --k-focus: #c9ff4a;
153
339
  --k-bg: #11120f;
154
340
  --k-panel: #191b16;
155
341
  --k-panel-raised: #20231e;
@@ -157,30 +343,81 @@ export const REVIEW_WORKBENCH_CSS = `/* Bundled, scoped Survey Review Workbench
157
343
  --k-line-strong: #8ea36e;
158
344
  --k-text: #f2f0e8;
159
345
  --k-text-muted: #a6ab9c;
160
- --k-text-faint: #747a6c;
346
+ --k-text-faint: #858b7e;
161
347
  --k-positive: #a6d37b;
162
348
  --k-caution: #e8c15f;
163
349
  --k-negative: #ee776f;
164
350
  --k-active: #84d8c8;
351
+ --k-status-contrast: #11120f;
352
+ --k-focus-ring: var(--k-focus);
353
+ --k-positive-soft: color-mix(in oklab, var(--k-positive) 14%, transparent);
354
+ --k-caution-soft: color-mix(in oklab, var(--k-caution) 14%, transparent);
355
+ --k-negative-soft: color-mix(in oklab, var(--k-negative) 14%, transparent);
356
+ --k-active-soft: color-mix(in oklab, var(--k-active) 14%, transparent);
165
357
  }
166
358
 
359
+ /* Flow dark: the brand is the accent lightened (OKLCH hue within 0.3°, same chroma)
360
+ just enough to read as text at 4.5:1 on bg, panel and raised panel; the
361
+ action and focus keep the accent, which clears their 3:1 and dark-text
362
+ pairs. */
167
363
  .survey-workbench-embed.theme-flow{
168
- --k-brand: #2f88a6;
364
+ --k-brand: #3890ae;
365
+ --k-action: #2f88a6;
366
+ --k-action-contrast: #06080b;
367
+ --k-focus: #2f88a6;
368
+ --k-focus-ring: var(--k-focus);
169
369
  }
170
370
 
171
371
  .survey-workbench-embed.theme-surface{
172
372
  --k-brand: #14a37a;
173
- }
174
-
373
+ --k-action: #14a37a;
374
+ --k-action-contrast: #06080b;
375
+ --k-focus: #14a37a;
376
+ --k-focus-ring: var(--k-focus);
377
+ }
378
+
379
+ /* Station: the draft accent, adjusted per mode only where WCAG AA needs it.
380
+ Dark: the accent carries white text as a fill but is too dark to read as
381
+ text on the dark panel, so the focus ring is the accent lightened to clear
382
+ contrast, and the brand is lightened a little further (OKLCH hue within 0.1°) to
383
+ read as text at 4.5:1 on bg, panel and raised panel; the action keeps the
384
+ draft accent. */
385
+ .survey-workbench-embed.theme-station{
386
+ --k-brand: #966aff;
387
+ --k-action: #7c3aed;
388
+ --k-action-contrast: #ffffff;
389
+ --k-focus: #9364ff;
390
+ --k-focus-ring: var(--k-focus);
391
+ }
392
+
393
+ /* Survey light: the brand is the accent darkened (same OKLCH hue and chroma) just enough
394
+ to read as text at 4.5:1 on the light canvas; the action and focus keep the
395
+ accent. */
175
396
  .survey-workbench-embed[data-theme="light"].theme-survey,
176
- .survey-workbench-embed[data-theme="light"] .theme-survey{
177
- --k-brand: #16806f;
178
- }
179
-
397
+ .survey-workbench-embed[data-theme="light"] .theme-survey:where(:not([data-theme="dark"], [data-theme="light"] [data-theme="dark"] *)),
398
+ .survey-workbench-embed :where(.theme-survey) [data-theme="light"]:where(:not(.theme-console, .theme-flow, .theme-surface, .theme-station, .theme-survey :is(.theme-console, .theme-flow, .theme-surface, .theme-station) *)){
399
+ --k-bg: #f5f4ef;
400
+ --k-text-faint: #6a707b;
401
+ --k-brand: #107e6d;
402
+ --k-action: #16806f;
403
+ --k-action-contrast: #ffffff;
404
+ --k-focus: #16806f;
405
+ --k-focus-ring: var(--k-focus);
406
+ }
407
+
408
+ /* Console light: the brand is the lime darkened (OKLCH hue within 0.1°) just enough
409
+ to read as text at 4.5:1 on bg, panel and raised panel, and it keeps white
410
+ brand-contrast text (5.1:1; dark text would be 3.7:1). The action and focus
411
+ keep the brighter lime with dark text. */
180
412
  .survey-workbench-embed[data-theme="light"].theme-console,
181
- .survey-workbench-embed[data-theme="light"] .theme-console{
182
- --k-brand: #6c9400;
413
+ .survey-workbench-embed[data-theme="light"] .theme-console:where(:not([data-theme="dark"], [data-theme="light"] [data-theme="dark"] *)),
414
+ .survey-workbench-embed :where(.theme-console) [data-theme="light"]:where(:not(.theme-survey, .theme-flow, .theme-surface, .theme-station, .theme-console :is(.theme-survey, .theme-flow, .theme-surface, .theme-station) *)){
415
+ --k-brand: #577800;
183
416
  --k-brand-contrast: #ffffff;
417
+ --k-action: #6c9400;
418
+ --k-action-contrast: #11120f;
419
+ --k-focus: #6c9400;
420
+ --k-focus-ring: var(--k-focus);
184
421
  --k-bg: #f3f5eb;
185
422
  --k-panel: #fbfcf7;
186
423
  --k-panel-raised: #eef2e6;
@@ -188,21 +425,114 @@ export const REVIEW_WORKBENCH_CSS = `/* Bundled, scoped Survey Review Workbench
188
425
  --k-line-strong: #8fa36f;
189
426
  --k-text: #1e2319;
190
427
  --k-text-muted: #596250;
191
- --k-text-faint: #77816d;
428
+ --k-text-faint: #68705f;
192
429
  --k-positive: #2f7d32;
193
430
  --k-caution: #8a6500;
194
431
  --k-negative: #b93a36;
195
432
  --k-active: #247f75;
433
+ --k-status-contrast: #ffffff;
434
+ --k-positive-soft: color-mix(in oklab, var(--k-positive) 14%, transparent);
435
+ --k-caution-soft: color-mix(in oklab, var(--k-caution) 14%, transparent);
436
+ --k-negative-soft: color-mix(in oklab, var(--k-negative) 14%, transparent);
437
+ --k-active-soft: color-mix(in oklab, var(--k-active) 14%, transparent);
196
438
  }
197
439
 
198
440
  .survey-workbench-embed[data-theme="light"].theme-flow,
199
- .survey-workbench-embed[data-theme="light"] .theme-flow{
441
+ .survey-workbench-embed[data-theme="light"] .theme-flow:where(:not([data-theme="dark"], [data-theme="light"] [data-theme="dark"] *)),
442
+ .survey-workbench-embed :where(.theme-flow) [data-theme="light"]:where(:not(.theme-survey, .theme-console, .theme-surface, .theme-station, .theme-flow :is(.theme-survey, .theme-console, .theme-surface, .theme-station) *)){
200
443
  --k-brand: #1f6f88;
444
+ --k-action: #1f6f88;
445
+ --k-action-contrast: #ffffff;
446
+ --k-focus: #1f6f88;
447
+ --k-focus-ring: var(--k-focus);
201
448
  }
202
449
 
203
450
  .survey-workbench-embed[data-theme="light"].theme-surface,
204
- .survey-workbench-embed[data-theme="light"] .theme-surface{
451
+ .survey-workbench-embed[data-theme="light"] .theme-surface:where(:not([data-theme="dark"], [data-theme="light"] [data-theme="dark"] *)),
452
+ .survey-workbench-embed :where(.theme-surface) [data-theme="light"]:where(:not(.theme-survey, .theme-console, .theme-flow, .theme-station, .theme-surface :is(.theme-survey, .theme-console, .theme-flow, .theme-station) *)){
205
453
  --k-brand: #0f6b52;
454
+ --k-action: #0f6b52;
455
+ --k-action-contrast: #ffffff;
456
+ --k-focus: #0f6b52;
457
+ --k-focus-ring: var(--k-focus);
458
+ }
459
+
460
+ .survey-workbench-embed[data-theme="light"].theme-station,
461
+ .survey-workbench-embed[data-theme="light"] .theme-station:where(:not([data-theme="dark"], [data-theme="light"] [data-theme="dark"] *)),
462
+ .survey-workbench-embed :where(.theme-station) [data-theme="light"]:where(:not(.theme-survey, .theme-console, .theme-flow, .theme-surface, .theme-station :is(.theme-survey, .theme-console, .theme-flow, .theme-surface) *)){
463
+ --k-brand: #7c3aed;
464
+ --k-action: #7c3aed;
465
+ --k-action-contrast: #ffffff;
466
+ --k-focus: #7c3aed;
467
+ --k-focus-ring: var(--k-focus);
468
+ }
469
+
470
+
471
+ /* Dark islands (ui#80): a data-theme="dark" element below a light element
472
+ takes its nearest theme's dark values. tokens.css resets it to the default
473
+ dark contract first; each block below repeats its theme's mode-dependent
474
+ base values (check:contrast fails if one drifts) and, like the light
475
+ blocks, yields to a nearer theme class. Without a light ancestor nothing
476
+ needs resetting, so these blocks do not match and the element inherits. */
477
+
478
+ .survey-workbench-embed[data-theme="dark"]:where(.theme-survey *):where([data-theme="light"] *):where(:not(.theme-console, .theme-flow, .theme-surface, .theme-station, .theme-survey :is(.theme-console, .theme-flow, .theme-surface, .theme-station) *)){
479
+ --k-bg: #06080b;
480
+ --k-brand: #5ce0c6;
481
+ --k-action: #5ce0c6;
482
+ --k-action-contrast: #06080b;
483
+ --k-focus: #5ce0c6;
484
+ --k-text-faint: #78889b;
485
+ --k-focus-ring: var(--k-focus);
486
+ }
487
+
488
+ .survey-workbench-embed[data-theme="dark"]:where(.theme-console *):where([data-theme="light"] *):where(:not(.theme-survey, .theme-flow, .theme-surface, .theme-station, .theme-console :is(.theme-survey, .theme-flow, .theme-surface, .theme-station) *)){
489
+ --k-brand: #c9ff4a;
490
+ --k-brand-contrast: #11120f;
491
+ --k-action: #c9ff4a;
492
+ --k-action-contrast: #11120f;
493
+ --k-focus: #c9ff4a;
494
+ --k-bg: #11120f;
495
+ --k-panel: #191b16;
496
+ --k-panel-raised: #20231e;
497
+ --k-line: #3a4035;
498
+ --k-line-strong: #8ea36e;
499
+ --k-text: #f2f0e8;
500
+ --k-text-muted: #a6ab9c;
501
+ --k-text-faint: #858b7e;
502
+ --k-positive: #a6d37b;
503
+ --k-caution: #e8c15f;
504
+ --k-negative: #ee776f;
505
+ --k-active: #84d8c8;
506
+ --k-status-contrast: #11120f;
507
+ --k-focus-ring: var(--k-focus);
508
+ --k-positive-soft: color-mix(in oklab, var(--k-positive) 14%, transparent);
509
+ --k-caution-soft: color-mix(in oklab, var(--k-caution) 14%, transparent);
510
+ --k-negative-soft: color-mix(in oklab, var(--k-negative) 14%, transparent);
511
+ --k-active-soft: color-mix(in oklab, var(--k-active) 14%, transparent);
512
+ }
513
+
514
+ .survey-workbench-embed[data-theme="dark"]:where(.theme-flow *):where([data-theme="light"] *):where(:not(.theme-survey, .theme-console, .theme-surface, .theme-station, .theme-flow :is(.theme-survey, .theme-console, .theme-surface, .theme-station) *)){
515
+ --k-brand: #3890ae;
516
+ --k-action: #2f88a6;
517
+ --k-action-contrast: #06080b;
518
+ --k-focus: #2f88a6;
519
+ --k-focus-ring: var(--k-focus);
520
+ }
521
+
522
+ .survey-workbench-embed[data-theme="dark"]:where(.theme-surface *):where([data-theme="light"] *):where(:not(.theme-survey, .theme-console, .theme-flow, .theme-station, .theme-surface :is(.theme-survey, .theme-console, .theme-flow, .theme-station) *)){
523
+ --k-brand: #14a37a;
524
+ --k-action: #14a37a;
525
+ --k-action-contrast: #06080b;
526
+ --k-focus: #14a37a;
527
+ --k-focus-ring: var(--k-focus);
528
+ }
529
+
530
+ .survey-workbench-embed[data-theme="dark"]:where(.theme-station *):where([data-theme="light"] *):where(:not(.theme-survey, .theme-console, .theme-flow, .theme-surface, .theme-station :is(.theme-survey, .theme-console, .theme-flow, .theme-surface) *)){
531
+ --k-brand: #966aff;
532
+ --k-action: #7c3aed;
533
+ --k-action-contrast: #ffffff;
534
+ --k-focus: #9364ff;
535
+ --k-focus-ring: var(--k-focus);
206
536
  }
207
537
 
208
538
 
@@ -299,31 +629,13 @@ export const REVIEW_WORKBENCH_CSS = `/* Bundled, scoped Survey Review Workbench
299
629
  .survey-workbench-embed .queue-result-count{ margin-left: auto; color: var(--k-muted); }
300
630
  .survey-workbench-embed .queue-empty{ padding: 2rem; color: var(--k-muted); text-align: center; }
301
631
 
302
- /* Re-assert the base structural tokens for light mode. themes.css's \`.theme-*\`
303
- preset classes hardcode a dark --k-bg/--k-panel/etc (so the preset "just
304
- works" without requiring [data-theme] to be set), and a bare class selector
305
- ties with the \`[data-theme="light"]\` selector in tokens.css on specificity —
306
- so source order decides, and themes.css loads after tokens.css. Re-declaring
307
- the light values here (loaded after themes.css) wins that tie-break. Presets
308
- still control --k-brand (and theme-console's fuller palette) via their own,
309
- more specific \`[data-theme="light"].theme-*\` rules in themes.css. */
632
+ /* Light-mode token values come from @kontourai/ui (tokens.css plus each
633
+ theme's light block, which since 1.17 re-sets every mode-dependent value
634
+ its dark preset sets). Only color-scheme is re-asserted here: the :root rule
635
+ above sets it to dark and would otherwise win the source-order tie with
636
+ tokens.css's [data-theme="light"], leaving native controls dark. */
310
637
  .survey-workbench-embed[data-theme="light"]{
311
638
  color-scheme: light;
312
- --k-bg: #f5f4ef;
313
- --k-panel: #ffffff;
314
- --k-panel-raised: #fbfaf7;
315
- --k-line: rgba(36, 40, 46, 0.12);
316
- --k-line-strong: rgba(36, 40, 46, 0.20);
317
- --k-shadow: 0 18px 50px -30px rgba(0, 0, 0, 0.25);
318
- --k-text: #202124;
319
- --k-text-muted: #5b626b;
320
- --k-text-faint: #707782;
321
- --k-brand-contrast: #ffffff;
322
- --k-positive: #168257;
323
- --k-caution: #8a5a00;
324
- --k-negative: #c83b3b;
325
- --k-neutral: #5f6975;
326
- --k-active: #3f6fd6;
327
639
  }
328
640
 
329
641
  .survey-workbench-embed *{
@@ -617,6 +929,30 @@ export const REVIEW_WORKBENCH_CSS = `/* Bundled, scoped Survey Review Workbench
617
929
  .survey-workbench-embed .chip.rejected{ color: var(--k-negative); background: var(--k-negative-wash); }
618
930
  .survey-workbench-embed .chip.could-not-confirm{ color: var(--k-caution); background: var(--k-caution-wash); }
619
931
 
932
+ /* Field-state panel: one row per field, content and lifecycle derived from
933
+ the records, never stored labels. A field that was not read is listed. */
934
+ .survey-workbench-embed .field-states{ padding: var(--k-space-3, 12px) 1rem; border-bottom: 1px solid var(--k-line); background: var(--k-panel); }
935
+ .survey-workbench-embed .field-states h2{ margin: 0 0 8px; font-size: 13px; font-weight: 700; color: var(--k-text); }
936
+ .survey-workbench-embed .field-states ul{ list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
937
+ .survey-workbench-embed .field-states-lead{ margin: 0 0 8px; font-size: 13px; line-height: 1.45; color: var(--k-caution); }
938
+ .survey-workbench-embed .field-state-row{ display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 12px; align-items: center; padding: 6px 0; border-top: 1px solid var(--k-line); }
939
+ .survey-workbench-embed .field-state-row:first-child{ border-top: 0; }
940
+ .survey-workbench-embed .field-state-name{ font-family: var(--k-font-mono, ui-monospace, monospace); font-size: 12px; color: var(--k-text); overflow-wrap: anywhere; }
941
+ .survey-workbench-embed .field-state-chips{ display: inline-flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; }
942
+ .survey-workbench-embed .field-state-note{ grid-column: 1 / -1; font-size: 12px; color: var(--k-muted); }
943
+ .survey-workbench-embed .field-state-note.signal{ color: var(--k-caution); }
944
+ .survey-workbench-embed .chip.state-content.value{ color: var(--k-positive); background: var(--k-positive-wash); }
945
+ .survey-workbench-embed .chip.state-content.conflicting,
946
+ .survey-workbench-embed .chip.state-content.excluded,
947
+ .survey-workbench-embed .chip.state-content.not_covered{ color: var(--k-caution); background: var(--k-caution-wash); }
948
+ .survey-workbench-embed .chip.state-content.unsupported{ color: var(--k-negative); background: var(--k-negative-wash); }
949
+ .survey-workbench-embed .chip.state-content.none,
950
+ .survey-workbench-embed .chip.state-lifecycle.superseded{ color: var(--k-muted); background: var(--k-sunken); }
951
+ .survey-workbench-embed .chip.state-lifecycle.pending{ color: var(--k-brand); background: var(--k-brand-wash); }
952
+ .survey-workbench-embed .chip.state-lifecycle.accepted{ color: var(--k-positive); background: var(--k-positive-wash); }
953
+ .survey-workbench-embed .chip.state-lifecycle.rejected{ color: var(--k-negative); background: var(--k-negative-wash); }
954
+ .survey-workbench-embed .chip.state-lifecycle.could_not_confirm{ color: var(--k-caution); background: var(--k-caution-wash); }
955
+
620
956
  .survey-workbench-embed .frow1 .push{
621
957
  margin-left: auto;
622
958
  }
@@ -861,6 +1197,12 @@ export const REVIEW_WORKBENCH_CSS = `/* Bundled, scoped Survey Review Workbench
861
1197
  background: var(--k-negative-wash);
862
1198
  }
863
1199
 
1200
+ /* A score-blind session: an informational note, not a warning. */
1201
+ .survey-workbench-embed .queue-attestation.score-blind{
1202
+ color: var(--k-active);
1203
+ background: var(--k-active-soft);
1204
+ }
1205
+
864
1206
  /* Conflicting proposed values: each value is listed with its own choice
865
1207
  control until the field is decided, then marked chosen or not chosen. */
866
1208
  .survey-workbench-embed .conflict-values ul{
@@ -1421,6 +1763,8 @@ export const REVIEW_WORKBENCH_CSS = `/* Bundled, scoped Survey Review Workbench
1421
1763
  /* The diff-collapse must also hold in host contexts without a query container
1422
1764
  (e.g. an embed that doesn't establish \`container-type\`): fall back to viewport. */
1423
1765
  @media (max-width: 620px) {
1766
+ .survey-workbench-embed .field-state-row{ grid-template-columns: minmax(0, 1fr); }
1767
+ .survey-workbench-embed .field-state-chips{ justify-content: flex-start; }
1424
1768
  .survey-workbench-embed .diff{
1425
1769
  grid-template-columns: 1fr;
1426
1770
  }
@@ -1441,4 +1785,70 @@ export const REVIEW_WORKBENCH_CSS = `/* Bundled, scoped Survey Review Workbench
1441
1785
  .survey-workbench-embed {
1442
1786
  overflow: hidden;
1443
1787
  }`;
1788
+ /** Literal dark token defaults for the element's :host, from @kontourai/ui tokens.css :root. */
1789
+ export const REVIEW_WORKBENCH_DARK_TOKEN_DECLARATIONS = ` --k-bg: #0a0e13;
1790
+ --k-panel: #111824;
1791
+ --k-panel-raised: #16202d;
1792
+ --k-text: #eef3f8;
1793
+ --k-text-muted: #aebccb;
1794
+ --k-text-faint: #75889d;
1795
+ --k-line: rgba(150, 180, 210, 0.12);
1796
+ --k-line-strong: rgba(150, 180, 210, 0.22);
1797
+ --k-brand: #5ce0c6;
1798
+ --k-brand-contrast: #06080b;
1799
+ --k-active: #7aa2ff;
1800
+ --k-positive: #34d399;
1801
+ --k-caution: #f3b14b;
1802
+ --k-negative: #ff6f6f;
1803
+ --k-neutral: #6f8095;
1804
+ --k-positive-soft: color-mix(in oklab, var(--k-positive) 14%, transparent);
1805
+ --k-caution-soft: color-mix(in oklab, var(--k-caution) 14%, transparent);
1806
+ --k-negative-soft: color-mix(in oklab, var(--k-negative) 14%, transparent);
1807
+ --k-active-soft: color-mix(in oklab, var(--k-active) 14%, transparent);
1808
+ --k-radius-md: 14px;
1809
+ --k-radius-sm: 9px;
1810
+ --k-shadow: 0 26px 60px -42px rgba(0, 0, 0, 0.95);
1811
+ --k-font-ui: "Hanken Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
1812
+ --k-font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
1813
+ --k-font-display: "Fraunces", Georgia, "Times New Roman", serif;`;
1814
+ /** Literal light token values for the element's color-scheme="light", from @kontourai/ui tokens.css [data-theme="light"]. */
1815
+ export const REVIEW_WORKBENCH_LIGHT_TOKEN_DECLARATIONS = ` --k-bg: #f5f4ef;
1816
+ --k-panel: #ffffff;
1817
+ --k-panel-raised: #fbfaf7;
1818
+ --k-line: rgba(36, 40, 46, 0.12);
1819
+ --k-line-strong: rgba(36, 40, 46, 0.20);
1820
+ --k-shadow: 0 18px 50px -30px rgba(0, 0, 0, 0.25);
1821
+ --k-text: #202124;
1822
+ --k-text-muted: #5b626b;
1823
+ --k-text-faint: #6a707b;
1824
+ --k-brand: #0e7c64;
1825
+ --k-brand-contrast: #ffffff;
1826
+ --k-positive: #168257;
1827
+ --k-caution: #8a5a00;
1828
+ --k-negative: #c83b3b;
1829
+ --k-neutral: #5f6975;
1830
+ --k-active: #3f6fd6;
1831
+ --k-positive-soft: color-mix(in oklab, var(--k-positive) 14%, transparent);
1832
+ --k-caution-soft: color-mix(in oklab, var(--k-caution) 14%, transparent);
1833
+ --k-negative-soft: color-mix(in oklab, var(--k-negative) 14%, transparent);
1834
+ --k-active-soft: color-mix(in oklab, var(--k-active) 14%, transparent);`;
1835
+ /** Each built-in preset's dark and light values, from @kontourai/ui themes.css. */
1836
+ export const REVIEW_WORKBENCH_THEME_TOKEN_DECLARATIONS = {
1837
+ "survey": {
1838
+ "dark": " --k-bg: #06080b;\n --k-brand: #5ce0c6;\n --k-text-faint: #78889b;",
1839
+ "light": " --k-bg: #f5f4ef;\n --k-text-faint: #6a707b;\n --k-brand: #107e6d;"
1840
+ },
1841
+ "console": {
1842
+ "dark": " --k-font-ui: \"Aptos Narrow\", \"DIN Condensed\", \"IBM Plex Sans Condensed\", \"Gill Sans\", sans-serif;\n --k-font-display: \"Aptos Narrow\", \"DIN Condensed\", \"IBM Plex Sans Condensed\", \"Gill Sans\", sans-serif;\n --k-radius-sm: 0;\n --k-radius-md: 0;\n --k-brand: #c9ff4a;\n --k-brand-contrast: #11120f;\n --k-bg: #11120f;\n --k-panel: #191b16;\n --k-panel-raised: #20231e;\n --k-line: #3a4035;\n --k-line-strong: #8ea36e;\n --k-text: #f2f0e8;\n --k-text-muted: #a6ab9c;\n --k-text-faint: #858b7e;\n --k-positive: #a6d37b;\n --k-caution: #e8c15f;\n --k-negative: #ee776f;\n --k-active: #84d8c8;\n --k-positive-soft: color-mix(in oklab, var(--k-positive) 14%, transparent);\n --k-caution-soft: color-mix(in oklab, var(--k-caution) 14%, transparent);\n --k-negative-soft: color-mix(in oklab, var(--k-negative) 14%, transparent);\n --k-active-soft: color-mix(in oklab, var(--k-active) 14%, transparent);",
1843
+ "light": " --k-brand: #577800;\n --k-brand-contrast: #ffffff;\n --k-bg: #f3f5eb;\n --k-panel: #fbfcf7;\n --k-panel-raised: #eef2e6;\n --k-line: #ccd5bf;\n --k-line-strong: #8fa36f;\n --k-text: #1e2319;\n --k-text-muted: #596250;\n --k-text-faint: #68705f;\n --k-positive: #2f7d32;\n --k-caution: #8a6500;\n --k-negative: #b93a36;\n --k-active: #247f75;\n --k-positive-soft: color-mix(in oklab, var(--k-positive) 14%, transparent);\n --k-caution-soft: color-mix(in oklab, var(--k-caution) 14%, transparent);\n --k-negative-soft: color-mix(in oklab, var(--k-negative) 14%, transparent);\n --k-active-soft: color-mix(in oklab, var(--k-active) 14%, transparent);"
1844
+ },
1845
+ "flow": {
1846
+ "dark": " --k-brand: #3890ae;",
1847
+ "light": " --k-brand: #1f6f88;"
1848
+ },
1849
+ "surface": {
1850
+ "dark": " --k-brand: #14a37a;",
1851
+ "light": " --k-brand: #0f6b52;"
1852
+ }
1853
+ };
1444
1854
  export default REVIEW_WORKBENCH_CSS;