@inflexa-ai/harness 0.19.2 → 0.21.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 (139) hide show
  1. package/dist/agents/conversation-agent.d.ts +26 -14
  2. package/dist/agents/conversation-agent.js +12 -23
  3. package/dist/agents/report-session-agent.d.ts +31 -5
  4. package/dist/agents/report-session-agent.js +31 -6
  5. package/dist/app/chat-turn.js +3 -1
  6. package/dist/app/message-assembly.d.ts +15 -1
  7. package/dist/app/message-assembly.js +22 -8
  8. package/dist/app/report-session-runtime.d.ts +21 -4
  9. package/dist/app/report-session-runtime.js +53 -6
  10. package/dist/app/spawn-report-session.d.ts +174 -17
  11. package/dist/app/spawn-report-session.js +233 -21
  12. package/dist/contracts/chat-events.d.ts +7 -2
  13. package/dist/contracts/message.d.ts +10 -0
  14. package/dist/contracts/report-blocks.d.ts +743 -19
  15. package/dist/contracts/report-blocks.js +186 -11
  16. package/dist/contracts/report-reference.d.ts +88 -0
  17. package/dist/contracts/report-reference.js +48 -0
  18. package/dist/index.d.ts +9 -0
  19. package/dist/index.js +20 -0
  20. package/dist/lib/chrome.d.ts +26 -6
  21. package/dist/lib/chrome.js +99 -32
  22. package/dist/lib/eyes.d.ts +57 -0
  23. package/dist/lib/eyes.js +45 -0
  24. package/dist/lib/page-capture.d.ts +14 -0
  25. package/dist/lib/page-capture.js +56 -2
  26. package/dist/literature/sources/pubmed.js +2 -1
  27. package/dist/loop/run-agent.js +58 -23
  28. package/dist/loop/tool-detail.d.ts +19 -2
  29. package/dist/loop/tool-detail.js +35 -2
  30. package/dist/memory/content-to-cortex.d.ts +3 -2
  31. package/dist/memory/content-to-cortex.js +21 -12
  32. package/dist/memory/conversation-display-replay.js +14 -7
  33. package/dist/memory/thread-history.d.ts +60 -3
  34. package/dist/memory/thread-history.js +55 -16
  35. package/dist/memory/thread-store.d.ts +11 -1
  36. package/dist/memory/thread-store.js +12 -2
  37. package/dist/prompts/conversation.d.ts +1 -1
  38. package/dist/prompts/conversation.js +23 -6
  39. package/dist/prompts/report-session.d.ts +1 -1
  40. package/dist/prompts/report-session.js +183 -9
  41. package/dist/report-model/authoring-grammar.d.ts +463 -0
  42. package/dist/report-model/authoring-grammar.js +77 -0
  43. package/dist/report-model/block-walk.d.ts +57 -14
  44. package/dist/report-model/block-walk.js +116 -11
  45. package/dist/report-model/draft-finish.d.ts +22 -4
  46. package/dist/report-model/draft-finish.js +39 -7
  47. package/dist/report-model/draft-operations.d.ts +3 -0
  48. package/dist/report-model/draft-operations.js +19 -5
  49. package/dist/report-model/draft-read.d.ts +8 -5
  50. package/dist/report-model/draft-read.js +21 -5
  51. package/dist/report-model/fixture-resolver.js +15 -4
  52. package/dist/report-model/pin-snapshot.d.ts +24 -6
  53. package/dist/report-model/pin-snapshot.js +204 -6
  54. package/dist/report-model/production-resolver.js +15 -4
  55. package/dist/report-model/reference-resolver.d.ts +50 -1
  56. package/dist/report-model/reference-resolver.js +94 -1
  57. package/dist/report-model/reference-stamp.d.ts +45 -0
  58. package/dist/report-model/reference-stamp.js +173 -0
  59. package/dist/report-model/structural-validation.d.ts +4 -1
  60. package/dist/report-model/structural-validation.js +48 -3
  61. package/dist/report-model/validate.d.ts +1 -1
  62. package/dist/report-model/validate.js +1 -1
  63. package/dist/report-render/asset-lookup.d.ts +19 -0
  64. package/dist/report-render/asset-lookup.js +37 -0
  65. package/dist/report-render/assets.d.ts +45 -1
  66. package/dist/report-render/assets.js +65 -6
  67. package/dist/report-render/chart-presets.d.ts +102 -0
  68. package/dist/report-render/chart-presets.js +205 -0
  69. package/dist/report-render/chart.d.ts +112 -10
  70. package/dist/report-render/chart.js +1164 -41
  71. package/dist/report-render/design.d.ts +99 -0
  72. package/dist/report-render/design.js +289 -107
  73. package/dist/report-render/number-format.d.ts +117 -0
  74. package/dist/report-render/number-format.js +479 -0
  75. package/dist/report-render/page.d.ts +121 -5
  76. package/dist/report-render/page.js +689 -7
  77. package/dist/report-render/references.d.ts +70 -16
  78. package/dist/report-render/references.js +54 -22
  79. package/dist/report-render/render.d.ts +23 -5
  80. package/dist/report-render/render.js +81 -22
  81. package/dist/report-render/table-data.d.ts +92 -0
  82. package/dist/report-render/table-data.js +129 -0
  83. package/dist/report-render/types.d.ts +15 -0
  84. package/dist/report-render/value-bridge.js +6 -2
  85. package/dist/report-render/views/chart-view.d.ts +12 -5
  86. package/dist/report-render/views/chart-view.js +14 -8
  87. package/dist/report-render/views/page-view.d.ts +36 -15
  88. package/dist/report-render/views/page-view.js +45 -23
  89. package/dist/report-render/views/prose.d.ts +12 -2
  90. package/dist/report-render/views/prose.js +29 -7
  91. package/dist/report-render/views/references-view.d.ts +22 -8
  92. package/dist/report-render/views/references-view.js +101 -13
  93. package/dist/report-render/views/values.d.ts +128 -13
  94. package/dist/report-render/views/values.js +199 -18
  95. package/dist/runtime/assemble.d.ts +39 -5
  96. package/dist/runtime/assemble.js +47 -6
  97. package/dist/sandbox/client.d.ts +10 -1
  98. package/dist/sandbox/create-sandbox.d.ts +9 -4
  99. package/dist/sandbox/create-sandbox.js +22 -16
  100. package/dist/sandbox/docker-client.js +10 -7
  101. package/dist/sandbox/k8s-client.js +11 -18
  102. package/dist/sandbox/mount-plan.d.ts +45 -7
  103. package/dist/sandbox/mount-plan.js +43 -5
  104. package/dist/sandbox/types.d.ts +10 -6
  105. package/dist/state/init.js +38 -9
  106. package/dist/state/report-session-state.d.ts +42 -1
  107. package/dist/state/report-session-state.js +70 -2
  108. package/dist/state/report-versions.d.ts +22 -17
  109. package/dist/state/report-versions.js +48 -35
  110. package/dist/state/snapshot-parse.js +16 -1
  111. package/dist/tasks/data-profile.d.ts +7 -4
  112. package/dist/tasks/data-profile.js +9 -9
  113. package/dist/tools/define-tool.d.ts +32 -0
  114. package/dist/tools/define-tool.js +4 -0
  115. package/dist/tools/detail-resolver.d.ts +7 -2
  116. package/dist/tools/detail-resolver.js +7 -2
  117. package/dist/tools/iterate-report.d.ts +1 -1
  118. package/dist/tools/report/preview-snapshot.js +1 -0
  119. package/dist/tools/report-authoring/authoring-tools.d.ts +15 -6
  120. package/dist/tools/report-authoring/authoring-tools.js +99 -18
  121. package/dist/tools/report-session/derive-table.d.ts +162 -0
  122. package/dist/tools/report-session/derive-table.js +459 -0
  123. package/dist/tools/report-session/examine-page.d.ts +48 -18
  124. package/dist/tools/report-session/examine-page.js +204 -24
  125. package/dist/tools/report-session/index.d.ts +2 -0
  126. package/dist/tools/report-session/index.js +2 -0
  127. package/dist/tools/report-session/list-artifacts.d.ts +97 -0
  128. package/dist/tools/report-session/list-artifacts.js +197 -0
  129. package/dist/tools/report-session/preview-report.d.ts +6 -0
  130. package/dist/tools/report-session/preview-report.js +180 -28
  131. package/dist/tools/report-session/record-version.d.ts +22 -10
  132. package/dist/tools/report-session/record-version.js +99 -14
  133. package/dist/tools/start-report-session.d.ts +115 -0
  134. package/dist/tools/start-report-session.js +179 -0
  135. package/dist/workflows/sandbox-step.d.ts +7 -4
  136. package/dist/workflows/sandbox-step.js +10 -12
  137. package/dist/workspace/paths.d.ts +50 -0
  138. package/dist/workspace/paths.js +63 -0
  139. package/package.json +2 -1
@@ -17,11 +17,110 @@
17
17
  */
18
18
  /** The style rules of the page. The renderer inlines them in one `<style>` block. */
19
19
  export declare const DESIGN_CSS: string;
20
+ /**
21
+ * The row height and the header height of a grid, in pixels.
22
+ *
23
+ * The theme takes both, and the page script measures the mount with the same two numbers. Thus the box of
24
+ * the mount and the rows inside it agree, and no row half shows at the bottom edge.
25
+ */
26
+ export declare const GRID_ROW_HEIGHT_PX = 36;
27
+ export declare const GRID_HEADER_HEIGHT_PX = 40;
28
+ /**
29
+ * The bottom border of the header row, in pixels.
30
+ *
31
+ * The grid draws that border under the header, thus the box of the mount holds the header, this border, and
32
+ * the rows. Without it the last row sits one pixel past the box, and the grid paints a scrollbar over a
33
+ * table that fits.
34
+ */
35
+ export declare const GRID_HEADER_BORDER_PX = 1;
36
+ /**
37
+ * The count of rows that a grid shows before it scrolls, and the smallest width of a column.
38
+ *
39
+ * A card of a fixed height would leave a short table half empty. The page script takes the smaller of this
40
+ * count and the row count, thus a table of three rows takes the height of three rows.
41
+ */
42
+ export declare const GRID_VISIBLE_ROWS = 12;
43
+ export declare const GRID_MIN_COLUMN_WIDTH_PX = 120;
44
+ /** The delay before a cell tooltip shows, in milliseconds. */
45
+ export declare const GRID_TOOLTIP_DELAY_MS = 200;
46
+ /**
47
+ * The count of rows that a print form holds.
48
+ *
49
+ * The print layout lays every row out at once, and a table with no row bound would take hundreds of pages.
50
+ * The print stops at this count, the note of the card states the truncation, and the download carries the
51
+ * whole table. A table under the count prints whole.
52
+ */
53
+ export declare const GRID_PRINT_ROW_CAP = 1000;
54
+ /**
55
+ * The theme parameters of a grid. The page script passes them to `themeQuartz.withParams`.
56
+ *
57
+ * The values read the shared tokens above. Thus the palette, the two font families, and the header
58
+ * treatment of the grid are the palette, the families, and the treatment of the page.
59
+ *
60
+ * `browserColorScheme` pins the light scheme. The page carries one palette, thus a browser in the dark
61
+ * scheme must not invert the grid under it.
62
+ */
63
+ export declare const GRID_THEME_PARAMS: {
64
+ accentColor: "#576dea";
65
+ backgroundColor: "#ffffff";
66
+ foregroundColor: "#334155";
67
+ borderColor: "#e2e8f0";
68
+ browserColorScheme: string;
69
+ fontFamily: "\"Space Grotesk Variable\", system-ui, sans-serif";
70
+ fontSize: number;
71
+ headerBackgroundColor: "#f8fafc";
72
+ headerFontFamily: "\"IBM Plex Mono\", ui-monospace, monospace";
73
+ headerFontSize: number;
74
+ headerFontWeight: number;
75
+ headerTextColor: "#64748b";
76
+ headerHeight: number;
77
+ iconColor: "#94a3b8";
78
+ oddRowBackgroundColor: "#ffffff";
79
+ rowHeight: number;
80
+ rowHoverColor: "#f8fafc";
81
+ subtleTextColor: "#94a3b8";
82
+ wrapperBorder: boolean;
83
+ wrapperBorderRadius: number;
84
+ borderRadius: number;
85
+ };
20
86
  /**
21
87
  * The registered name of the ECharts theme. The registration script writes this name, and the bootstrap
22
88
  * reads it. One source prevents a silent mismatch between the two sites.
23
89
  */
24
90
  export declare const ECHARTS_THEME_NAME = "inflexa";
91
+ /**
92
+ * The muted chart color, beside the palette of the theme.
93
+ *
94
+ * A null category states no finding, thus it must recede behind the categories that do. The value is the
95
+ * `--color-ns` token of the tokens above. A chart option rides to the page as inline JSON, thus it reads no
96
+ * custom property and the color is written again here.
97
+ */
98
+ export declare const MUTED_CHART_COLOR = "#94a3b8";
99
+ /**
100
+ * The symbol ladder of a scatter, as one row count and one symbol size for each tier.
101
+ *
102
+ * A sparse scatter keeps the symbol of the chart runtime. Past the hover count the points sit close, thus
103
+ * the series takes a larger symbol and a point stays hoverable. Past the crowd count the plot is a cloud.
104
+ * A larger symbol paints one blob there, thus the series takes a small symbol at a reduced opacity. The
105
+ * shape of the cloud then reads. Per-point hover is lost in a crowd, and shape legibility wins.
106
+ */
107
+ export declare const SCATTER_HOVER_ROWS = 2000;
108
+ /** The symbol size of a scatter past the hover count. The ECharts default is 10, thus a point grows one step. */
109
+ export declare const SCATTER_HOVER_SYMBOL_SIZE = 12;
110
+ /** The row count from which a scatter recedes into a cloud. */
111
+ export declare const SCATTER_CROWD_ROWS = 10000;
112
+ /** The symbol size of a crowded scatter. It matches the outlier dot of a box plot. */
113
+ export declare const SCATTER_CROWD_SYMBOL_SIZE = 4;
114
+ /** The opacity of a crowded scatter point. An overlap then reads darker than a lone point. */
115
+ export declare const SCATTER_CROWD_OPACITY = 0.5;
116
+ /**
117
+ * The size bound of an inline chart option, in characters of serialized JSON.
118
+ *
119
+ * A small option reads well inside the page, and it costs one element. A dense scatter of many thousands of
120
+ * points writes each pair again inside the markup, and one such chart then holds most of the bytes of the
121
+ * page. Past this bound the option carries no row, and the chart reads the columnar payload of its artifact.
122
+ */
123
+ export declare const CHART_INLINE_OPTION_BOUND = 100000;
25
124
  /**
26
125
  * The ECharts theme as a plain object. The renderer serializes this object and registers it, thus the theme
27
126
  * never rides a JSON file and no run-time read is necessary. The palette and the axis styles mirror the
@@ -57,6 +57,27 @@ const FONT_FACES = `@font-face {
57
57
  font-style: normal;
58
58
  font-display: swap;
59
59
  }`;
60
+ /**
61
+ * The token values that the page and the grid share.
62
+ *
63
+ * The grid takes its theme through a parameter object, and that API takes values. A CSS custom property
64
+ * would arrive at the grid as text that it cannot read. Thus the shared tokens are constants here, the
65
+ * `:root` block below interpolates them, and `GRID_THEME_PARAMS` reads the same names. One source then
66
+ * styles the page and the grid.
67
+ */
68
+ const TOKEN = {
69
+ primary500: "#576dea",
70
+ heading: "#0f172a",
71
+ textStrong: "#334155",
72
+ textSecondary: "#64748b",
73
+ textMuted: "#94a3b8",
74
+ card: "#ffffff",
75
+ bgAlt: "#f8fafc",
76
+ borderSubtle: "#f1f5f9",
77
+ border: "#e2e8f0",
78
+ fontSans: `"Space Grotesk Variable", system-ui, sans-serif`,
79
+ fontMono: `"IBM Plex Mono", ui-monospace, monospace`,
80
+ };
60
81
  /** The style rules of the page. The renderer inlines them in one `<style>` block. */
61
82
  export const DESIGN_CSS = `${FONT_FACES}
62
83
 
@@ -68,23 +89,23 @@ export const DESIGN_CSS = `${FONT_FACES}
68
89
  --color-primary-200: #bcc2f9;
69
90
  --color-primary-300: #9ba5f5;
70
91
  --color-primary-400: #7987f0;
71
- --color-primary-500: #576dea;
92
+ --color-primary-500: ${TOKEN.primary500};
72
93
  --color-primary-600: #4458d4;
73
94
  --color-primary-700: #3545b0;
74
95
 
75
96
  /* Text */
76
- --color-heading: #0f172a;
77
- --color-text-strong: #334155;
97
+ --color-heading: ${TOKEN.heading};
98
+ --color-text-strong: ${TOKEN.textStrong};
78
99
  --color-text: #475569;
79
- --color-text-secondary: #64748b;
80
- --color-text-muted: #94a3b8;
100
+ --color-text-secondary: ${TOKEN.textSecondary};
101
+ --color-text-muted: ${TOKEN.textMuted};
81
102
 
82
103
  /* Surface */
83
104
  --color-bg: #ffffff;
84
- --color-bg-alt: #f8fafc;
85
- --color-card: #ffffff;
86
- --color-border-subtle: #f1f5f9;
87
- --color-border: #e2e8f0;
105
+ --color-bg-alt: ${TOKEN.bgAlt};
106
+ --color-card: ${TOKEN.card};
107
+ --color-border-subtle: ${TOKEN.borderSubtle};
108
+ --color-border: ${TOKEN.border};
88
109
  --color-border-hover: #cbd5e1;
89
110
  --color-surface-dark: #0f172a;
90
111
 
@@ -109,9 +130,10 @@ export const DESIGN_CSS = `${FONT_FACES}
109
130
  --color-stat-amber: #f59e0b;
110
131
 
111
132
  /* Typography and layout */
112
- --font-sans: "Space Grotesk Variable", system-ui, sans-serif;
113
- --font-mono: "IBM Plex Mono", ui-monospace, monospace;
133
+ --font-sans: ${TOKEN.fontSans};
134
+ --font-mono: ${TOKEN.fontMono};
114
135
  --layout-max: 1600px;
136
+ --content-max: 1100px;
115
137
  --nav-width: 15rem;
116
138
  }
117
139
 
@@ -175,6 +197,13 @@ img {
175
197
  padding-left: 24px;
176
198
  padding-right: 24px;
177
199
  }
200
+ /* The one content column of the page. The container gives the full-bleed gutter, and this column carries
201
+ every block kind. Thus the prose, the metric grid, the tables, and the charts read at one measure. */
202
+ .report-content {
203
+ margin-left: auto;
204
+ margin-right: auto;
205
+ max-width: var(--content-max);
206
+ }
178
207
 
179
208
  .report-hero {
180
209
  position: relative;
@@ -261,11 +290,13 @@ img {
261
290
  padding: 20px;
262
291
  border-bottom: 1px solid var(--color-border);
263
292
  }
293
+ /* The brand is the one link of the page that leaves it. It reads as the brand, not as a link. */
264
294
  .report-nav-brand-name {
265
295
  font-family: var(--font-mono);
266
296
  font-size: 14px;
267
297
  font-weight: 600;
268
298
  letter-spacing: 0.08em;
299
+ text-decoration: none;
269
300
  color: var(--color-primary-500);
270
301
  }
271
302
  .report-nav-list {
@@ -290,6 +321,13 @@ img {
290
321
  background-color: var(--color-bg-alt);
291
322
  border-left-color: var(--color-primary-500);
292
323
  }
324
+ /* The page script adds this class to the link of the section in view. One link carries it at a time. */
325
+ .report-nav-link-active {
326
+ font-weight: 600;
327
+ color: var(--color-heading);
328
+ background-color: var(--color-bg-alt);
329
+ border-left-color: var(--color-primary-500);
330
+ }
293
331
  .report-nav-index {
294
332
  flex-shrink: 0;
295
333
  width: 16px;
@@ -307,15 +345,29 @@ img {
307
345
  }
308
346
 
309
347
  /* ── Prose and evidence markers ───────────────────────── */
348
+ /* The prose fills the content column. No inner measure caps it, thus a band carries no half-empty side. */
310
349
  .report-prose {
311
- max-width: 72ch;
312
350
  margin-bottom: 16px;
313
351
  line-height: 1.7;
314
352
  color: var(--color-text);
315
353
  }
354
+ /* A list reads as prose that carries markers, thus it takes the type scale of the paragraph. The margin
355
+ is explicit on each side, because the base rule zeroes an ordered list and a browser still spaces an
356
+ unordered one. The left padding holds the markers, thus each item aligns inside the content column. */
357
+ .report-list {
358
+ margin: 0 0 16px;
359
+ padding-left: 24px;
360
+ line-height: 1.7;
361
+ color: var(--color-text);
362
+ }
363
+ .report-list-item {
364
+ margin-bottom: 6px;
365
+ }
366
+ /* The bracket marker of the one reference ladder. It sits inline beside 16px prose, thus it reads at the
367
+ size of a small inline word and never at the size of a superscript. */
316
368
  .report-marker {
317
369
  font-family: var(--font-mono);
318
- font-size: 10px;
370
+ font-size: 12px;
319
371
  }
320
372
  .report-marker a {
321
373
  color: var(--color-primary-500);
@@ -389,6 +441,9 @@ img {
389
441
  font-weight: 700;
390
442
  line-height: 1.2;
391
443
  color: var(--color-stat-primary);
444
+ /* A value that the number format cannot shorten, for example a long identifier, breaks inside the card
445
+ instead of past its edge. */
446
+ overflow-wrap: anywhere;
392
447
  }
393
448
  .stat-card-label {
394
449
  margin-top: 8px;
@@ -404,7 +459,9 @@ img {
404
459
  .report-table {
405
460
  margin-bottom: 32px;
406
461
  }
407
- .report-table-title {
462
+ /* The title line of a data card. A table and a chart carry the same line, thus one rule serves both. */
463
+ .report-table-title,
464
+ .report-chart-title {
408
465
  margin-bottom: 12px;
409
466
  font-family: var(--font-mono);
410
467
  font-size: 12px;
@@ -413,39 +470,71 @@ img {
413
470
  text-transform: uppercase;
414
471
  color: var(--color-primary-500);
415
472
  }
416
- .data-table-scroll {
417
- overflow-x: auto;
418
- }
419
- .data-table {
473
+ /* The mount of one grid. The page script sizes it from the row count, thus the box fits a short table and
474
+ a long table scrolls inside its own viewport. A mount whose payload the page does not hold takes no size,
475
+ thus the card shows its title and its download alone. */
476
+ .report-grid {
420
477
  width: 100%;
421
- border-collapse: collapse;
422
- font-size: 14px;
423
478
  }
424
- .data-table th {
425
- padding: 12px 16px;
426
- text-align: left;
479
+ /* The footer of a table card: the status of the table on the left, the download button on the right, and
480
+ the print note under both. The three read as one surface under the grid. */
481
+ .report-table-footer {
482
+ padding: 8px 16px;
483
+ border-top: 1px solid var(--color-border);
484
+ background: var(--color-bg-alt);
485
+ }
486
+ .report-table-footer-row {
487
+ display: flex;
488
+ align-items: center;
489
+ justify-content: space-between;
490
+ gap: 16px;
491
+ }
492
+ /* The status of the table: the row count, and the row bound of the binding beside it. */
493
+ .report-table-status {
427
494
  font-family: var(--font-mono);
428
495
  font-size: 11px;
429
- font-weight: 600;
430
496
  letter-spacing: 0.06em;
431
- text-transform: uppercase;
432
497
  color: var(--color-text-secondary);
433
- background-color: var(--color-bg-alt);
434
- border-bottom: 1px solid var(--color-border);
435
498
  }
436
- .data-table td {
437
- padding: 10px 16px;
438
- color: var(--color-text-strong);
439
- border-bottom: 1px solid var(--color-border-subtle);
499
+ .report-table-bound {
500
+ color: var(--color-text-muted);
440
501
  }
441
- .report-row {
442
- transition: background-color 0.15s ease;
502
+ /* The bound reads as a second clause of the status line, thus a separator divides the two. The dot is a
503
+ literal character, because a hex escape eats the space that follows it and the two clauses would touch. */
504
+ .report-table-bound::before {
505
+ content: " · ";
443
506
  }
444
- .report-row:hover {
445
- background-color: var(--color-bg-alt);
507
+ /* The download of the raw bytes. It reads as a button, because it is the one action of a table card. */
508
+ .report-table-download {
509
+ display: inline-block;
510
+ flex-shrink: 0;
511
+ padding: 6px 12px;
512
+ font-family: var(--font-mono);
513
+ font-size: 11px;
514
+ font-weight: 600;
515
+ letter-spacing: 0.08em;
516
+ text-transform: uppercase;
517
+ color: var(--color-primary-500);
518
+ text-decoration: none;
519
+ background: var(--color-card);
520
+ border: 1px solid var(--color-primary-200);
521
+ transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
522
+ }
523
+ .report-table-download:hover {
524
+ color: var(--color-primary-700);
525
+ background: var(--color-primary-50);
526
+ border-color: var(--color-primary-500);
446
527
  }
447
- .report-row:last-child td {
448
- border-bottom: 0;
528
+ /* The print note of a table card. The page script writes the bound of a truncated print form into it at
529
+ print time, and it clears the text after. An empty note takes no space, thus the screen shows none. */
530
+ .report-grid-note {
531
+ margin-top: 6px;
532
+ font-family: var(--font-mono);
533
+ font-size: 11px;
534
+ color: var(--color-text-secondary);
535
+ }
536
+ .report-grid-note:empty {
537
+ display: none;
449
538
  }
450
539
 
451
540
  /* ── Figure and citation cards ────────────────────────── */
@@ -463,79 +552,37 @@ img {
463
552
  font-size: 14px;
464
553
  color: var(--color-text-secondary);
465
554
  }
555
+ /* The short citation is the name of the paper, thus it carries the weight of the card. */
556
+ .report-citation-source {
557
+ font-weight: 600;
558
+ color: var(--color-text);
559
+ }
560
+ a.report-citation-source {
561
+ text-decoration: none;
562
+ }
563
+ a.report-citation-source:hover {
564
+ text-decoration: underline;
565
+ }
466
566
  .report-citation-note {
467
567
  color: var(--color-text);
468
568
  }
569
+ .report-citation-key {
570
+ font-family: var(--font-mono);
571
+ font-size: 12px;
572
+ color: var(--color-text-muted);
573
+ }
469
574
  .report-caption {
470
575
  margin-top: 8px;
471
576
  font-size: 13px;
472
577
  color: var(--color-text-muted);
473
578
  }
474
579
 
475
- /* ── Window-chrome chart panel ────────────────────────── */
476
- /* The chart panel is the one component with rounded corners. */
580
+ /* ── Chart card ───────────────────────────────────────── */
581
+ /* The chart is a data card, thus it takes the square corner-accent form of the table and the figure. */
477
582
  .report-chart {
478
583
  margin-bottom: 32px;
479
584
  }
480
- .window-chrome {
481
- overflow: hidden;
482
- border: 1px solid var(--color-border);
483
- border-radius: 12px;
484
- background: var(--color-card);
485
- box-shadow: 0 8px 30px -8px rgba(15, 23, 42, 0.08), 0 4px 12px -4px rgba(15, 23, 42, 0.04);
486
- transition: transform 0.5s ease, box-shadow 0.5s ease, border-color 0.5s ease;
487
- }
488
- .window-chrome:hover {
489
- transform: translateY(-4px);
490
- border-color: var(--color-border-hover);
491
- box-shadow: 0 25px 50px -12px rgba(15, 23, 42, 0.18), 0 12px 24px -8px rgba(15, 23, 42, 0.1);
492
- }
493
- .window-chrome-bar {
494
- display: flex;
495
- align-items: center;
496
- justify-content: space-between;
497
- gap: 16px;
498
- padding: 12px 16px;
499
- background-color: var(--color-bg-alt);
500
- border-bottom: 1px solid var(--color-border-subtle);
501
- }
502
- .chrome-dots {
503
- display: flex;
504
- align-items: center;
505
- gap: 6px;
506
- }
507
- .chrome-dot {
508
- display: block;
509
- width: 10px;
510
- height: 10px;
511
- border-radius: 50%;
512
- background-color: var(--color-border-hover);
513
- transition: background-color 0.3s ease;
514
- }
515
- .window-chrome:hover .dot-1 {
516
- background-color: #fb7185;
517
- }
518
- .window-chrome:hover .dot-2 {
519
- background-color: #fbbf24;
520
- }
521
- .window-chrome:hover .dot-3 {
522
- background-color: #4ade80;
523
- }
524
- .window-chrome-title {
525
- flex: 1 1 auto;
526
- text-align: center;
527
- font-size: 14px;
528
- font-weight: 600;
529
- color: var(--color-heading);
530
- }
531
- .window-chrome-badge {
532
- font-family: var(--font-mono);
533
- font-size: 11px;
534
- font-weight: 600;
535
- letter-spacing: 0.1em;
536
- color: var(--color-primary-500);
537
- }
538
- .window-chrome-body {
585
+ .report-chart-card {
539
586
  padding: 16px;
540
587
  }
541
588
  /* The chart runtime measures the container. A container with no height shows no chart. */
@@ -544,31 +591,68 @@ img {
544
591
  height: 400px;
545
592
  }
546
593
 
547
- /* ── Reference list ───────────────────────────────────── */
594
+ /* ── References appendix ──────────────────────────────── */
595
+ /* The appendix is a record of where each value came from. A reader consults it, and a reader does not read
596
+ it through. Thus it stays smaller and quieter than the body of the report. One list holds both reference
597
+ kinds, thus one set of rules styles an artifact entry and a paper entry alike. */
598
+ .report-ref-title {
599
+ color: var(--color-text-secondary);
600
+ }
548
601
  .report-references {
549
602
  padding-left: 24px;
550
603
  list-style: decimal;
604
+ color: var(--color-text-muted);
551
605
  }
552
606
  .report-ref-item {
553
- margin-bottom: 8px;
554
- font-size: 14px;
555
- color: var(--color-text);
607
+ margin-bottom: 6px;
608
+ font-size: 13px;
609
+ line-height: 1.5;
610
+ color: var(--color-text-secondary);
556
611
  }
557
612
  .report-ref-kind {
558
613
  font-family: var(--font-mono);
559
- font-size: 11px;
614
+ font-size: 10px;
560
615
  letter-spacing: 0.06em;
561
616
  text-transform: uppercase;
562
617
  color: var(--color-text-muted);
563
618
  }
564
619
  .report-ref-path {
565
620
  font-family: var(--font-mono);
566
- font-size: 13px;
621
+ font-size: 12px;
567
622
  color: var(--color-primary-700);
568
623
  }
569
624
  .report-ref-detail {
625
+ color: var(--color-text-muted);
626
+ }
627
+ /* The chain of a derived path reads under its entry, thus the entry line keeps the form of a pinned
628
+ artifact and the sources sit on a line of their own. */
629
+ .report-ref-chain {
630
+ margin-top: 2px;
631
+ color: var(--color-text-muted);
632
+ }
633
+ /* A link of the chain: the staged script, and the derived file. The link carries the code span of its
634
+ subject, thus the underline arrives on hover alone and the line stays quiet. */
635
+ .report-ref-link {
636
+ text-decoration: none;
637
+ }
638
+ .report-ref-link:hover {
639
+ text-decoration: underline;
640
+ }
641
+ /* The head of a content hash. It reads smaller than a path, because it identifies bytes and a reader
642
+ compares it against a staged file name instead of reading it. */
643
+ .report-ref-hash {
644
+ font-family: var(--font-mono);
645
+ font-size: 11px;
646
+ color: var(--color-text-muted);
647
+ }
648
+ .report-cite-source {
570
649
  color: var(--color-text-secondary);
571
650
  }
651
+ /* The description of the paper reads under its citation, thus it takes a line of its own. */
652
+ .report-cite-description {
653
+ margin-top: 2px;
654
+ color: var(--color-text-muted);
655
+ }
572
656
 
573
657
  /* ── Section textures ─────────────────────────────────── */
574
658
  /* A texture is felt, not seen: a low opacity and a faded edge. */
@@ -740,15 +824,113 @@ img {
740
824
  .report-footer-note {
741
825
  color: var(--color-text-strong);
742
826
  }
743
- .window-chrome {
744
- box-shadow: none;
745
- }
746
827
  }`;
828
+ /**
829
+ * The row height and the header height of a grid, in pixels.
830
+ *
831
+ * The theme takes both, and the page script measures the mount with the same two numbers. Thus the box of
832
+ * the mount and the rows inside it agree, and no row half shows at the bottom edge.
833
+ */
834
+ export const GRID_ROW_HEIGHT_PX = 36;
835
+ export const GRID_HEADER_HEIGHT_PX = 40;
836
+ /**
837
+ * The bottom border of the header row, in pixels.
838
+ *
839
+ * The grid draws that border under the header, thus the box of the mount holds the header, this border, and
840
+ * the rows. Without it the last row sits one pixel past the box, and the grid paints a scrollbar over a
841
+ * table that fits.
842
+ */
843
+ export const GRID_HEADER_BORDER_PX = 1;
844
+ /**
845
+ * The count of rows that a grid shows before it scrolls, and the smallest width of a column.
846
+ *
847
+ * A card of a fixed height would leave a short table half empty. The page script takes the smaller of this
848
+ * count and the row count, thus a table of three rows takes the height of three rows.
849
+ */
850
+ export const GRID_VISIBLE_ROWS = 12;
851
+ export const GRID_MIN_COLUMN_WIDTH_PX = 120;
852
+ /** The delay before a cell tooltip shows, in milliseconds. */
853
+ export const GRID_TOOLTIP_DELAY_MS = 200;
854
+ /**
855
+ * The count of rows that a print form holds.
856
+ *
857
+ * The print layout lays every row out at once, and a table with no row bound would take hundreds of pages.
858
+ * The print stops at this count, the note of the card states the truncation, and the download carries the
859
+ * whole table. A table under the count prints whole.
860
+ */
861
+ export const GRID_PRINT_ROW_CAP = 1000;
862
+ /**
863
+ * The theme parameters of a grid. The page script passes them to `themeQuartz.withParams`.
864
+ *
865
+ * The values read the shared tokens above. Thus the palette, the two font families, and the header
866
+ * treatment of the grid are the palette, the families, and the treatment of the page.
867
+ *
868
+ * `browserColorScheme` pins the light scheme. The page carries one palette, thus a browser in the dark
869
+ * scheme must not invert the grid under it.
870
+ */
871
+ export const GRID_THEME_PARAMS = {
872
+ accentColor: TOKEN.primary500,
873
+ backgroundColor: TOKEN.card,
874
+ foregroundColor: TOKEN.textStrong,
875
+ borderColor: TOKEN.border,
876
+ browserColorScheme: "light",
877
+ fontFamily: TOKEN.fontSans,
878
+ fontSize: 14,
879
+ headerBackgroundColor: TOKEN.bgAlt,
880
+ headerFontFamily: TOKEN.fontMono,
881
+ headerFontSize: 11,
882
+ headerFontWeight: 600,
883
+ headerTextColor: TOKEN.textSecondary,
884
+ headerHeight: GRID_HEADER_HEIGHT_PX,
885
+ iconColor: TOKEN.textMuted,
886
+ oddRowBackgroundColor: TOKEN.card,
887
+ rowHeight: GRID_ROW_HEIGHT_PX,
888
+ rowHoverColor: TOKEN.bgAlt,
889
+ subtleTextColor: TOKEN.textMuted,
890
+ // The corner-accent card around the mount carries the border and the square corners of the identity.
891
+ // A second border on the wrapper would double the rule at each edge of the card.
892
+ wrapperBorder: false,
893
+ wrapperBorderRadius: 0,
894
+ borderRadius: 0,
895
+ };
747
896
  /**
748
897
  * The registered name of the ECharts theme. The registration script writes this name, and the bootstrap
749
898
  * reads it. One source prevents a silent mismatch between the two sites.
750
899
  */
751
900
  export const ECHARTS_THEME_NAME = "inflexa";
901
+ /**
902
+ * The muted chart color, beside the palette of the theme.
903
+ *
904
+ * A null category states no finding, thus it must recede behind the categories that do. The value is the
905
+ * `--color-ns` token of the tokens above. A chart option rides to the page as inline JSON, thus it reads no
906
+ * custom property and the color is written again here.
907
+ */
908
+ export const MUTED_CHART_COLOR = "#94a3b8";
909
+ /**
910
+ * The symbol ladder of a scatter, as one row count and one symbol size for each tier.
911
+ *
912
+ * A sparse scatter keeps the symbol of the chart runtime. Past the hover count the points sit close, thus
913
+ * the series takes a larger symbol and a point stays hoverable. Past the crowd count the plot is a cloud.
914
+ * A larger symbol paints one blob there, thus the series takes a small symbol at a reduced opacity. The
915
+ * shape of the cloud then reads. Per-point hover is lost in a crowd, and shape legibility wins.
916
+ */
917
+ export const SCATTER_HOVER_ROWS = 2000;
918
+ /** The symbol size of a scatter past the hover count. The ECharts default is 10, thus a point grows one step. */
919
+ export const SCATTER_HOVER_SYMBOL_SIZE = 12;
920
+ /** The row count from which a scatter recedes into a cloud. */
921
+ export const SCATTER_CROWD_ROWS = 10000;
922
+ /** The symbol size of a crowded scatter. It matches the outlier dot of a box plot. */
923
+ export const SCATTER_CROWD_SYMBOL_SIZE = 4;
924
+ /** The opacity of a crowded scatter point. An overlap then reads darker than a lone point. */
925
+ export const SCATTER_CROWD_OPACITY = 0.5;
926
+ /**
927
+ * The size bound of an inline chart option, in characters of serialized JSON.
928
+ *
929
+ * A small option reads well inside the page, and it costs one element. A dense scatter of many thousands of
930
+ * points writes each pair again inside the markup, and one such chart then holds most of the bytes of the
931
+ * page. Past this bound the option carries no row, and the chart reads the columnar payload of its artifact.
932
+ */
933
+ export const CHART_INLINE_OPTION_BOUND = 100_000;
752
934
  /**
753
935
  * The ECharts theme as a plain object. The renderer serializes this object and registers it, thus the theme
754
936
  * never rides a JSON file and no run-time read is necessary. The palette and the axis styles mirror the