@tangle-network/charts 1.0.0 → 1.0.2

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.
@@ -1,59 +1,43 @@
1
- /* Standalone surface: no Tailwind build required. Every color, radius and shadow is a Brand role from
2
- * @tangle-network/brand tokens.css, which the host loads with its theme (data-theme or the light/dark class, which also
3
- * set color-scheme for the light-dark() mixes below); nothing here is a color of its own, so a named theme carries in.
4
- * Geometry reads three variables the clock writes every frame: --fw-t (clock ms), --fw-v0 (window start ms)
5
- * and --fw-k (px per ms). A mark carries --s and --e (its start and end ms), so growth, playback and zoom
6
- * move marks without a React render.
7
- * Type uses the 12/14 px scale; spacing sits on a 4 px grid. */
8
1
  .fw-root {
9
2
  --fw-text: var(--text-primary);
10
3
  --fw-muted: var(--text-muted);
11
4
  --fw-surface: var(--bg-card);
12
5
  --fw-raised: var(--bg-elevated);
13
- /* One hairline for the card edge and every rule inside it; the plot grid is the chart grid. */
14
- --fw-border: hsl(var(--hsl-border));
6
+ --fw-border: var(--border-color, hsl(var(--hsl-border)));
15
7
  --fw-grid: var(--chart-grid);
16
8
  --fw-guide: var(--chart-rule);
17
- /* The trace is a card, so its wells are the in-card well. */
18
9
  --fw-well: var(--well-card);
19
10
  --fw-hover: var(--bg-hover);
20
11
  --fw-accent: var(--accent-text);
21
12
  --fw-failed: var(--surface-danger-text);
22
13
  --fw-ok: var(--surface-success-text);
23
14
  --fw-amber: var(--surface-warning-text);
24
- /* The running hue: a live agent's dot, outcome and pill. The host sets it to the one running ink its other pages use. */
25
15
  --fw-live: var(--fw-accent);
26
- /* Step kinds inside an agent bar: the chart accent and a categorical hue, far from the outcome greens and reds. */
27
- --fw-kind-model: var(--chart-accent);
28
- --fw-kind-tool: var(--tone-cyan-icon);
16
+ --fw-kind-model: light-dark(oklch(from var(--chart-accent) .50 min(c, .10) h), oklch(from var(--chart-accent) .72 min(c, .10) h));
17
+ --fw-kind-tool: light-dark(oklch(from var(--tone-cyan-icon) .80 min(c, .10) h), oklch(from var(--tone-cyan-icon) .46 min(c, .10) h));
29
18
  --fw-neutral-bar: color-mix(in oklab, var(--fw-text) 15%, var(--fw-surface));
30
19
  --fw-hatch: color-mix(in oklab, var(--fw-text) 22%, transparent);
31
- /* Bar identity, not status: the root neutral, then an identity ring at one lightness and a moderate chroma, so no lane
32
- outshouts another (critique P0 #3, round 29: Brand's icon-strength teal, pink and violet "compete", lime and pink read
33
- olive and maroon). Blue, violet, pink, cyan, slate: no green, teal, lime, amber or red, so no lane is mistaken for a status. */
34
20
  --fw-h0: var(--chart-muted);
35
- --fw-h1: light-dark(oklch(0.55 0.11 255), oklch(0.70 0.11 255));
36
- --fw-h2: light-dark(oklch(0.55 0.11 300), oklch(0.70 0.11 300));
37
- --fw-h3: light-dark(oklch(0.55 0.11 335), oklch(0.70 0.11 335));
38
- --fw-h4: light-dark(oklch(0.55 0.11 215), oklch(0.70 0.11 215));
39
- --fw-h5: light-dark(oklch(0.55 0.03 255), oklch(0.70 0.03 255));
40
- --fw-ease: cubic-bezier(.2, .8, .2, 1);
41
- /* Bar height = row height - 16, so a centered bar sits 8 px from the row edge: 20 px in a 36 px step row. */
21
+ --fw-h1: light-dark(oklch(from var(--tone-blue-icon) .58 .11 calc(h + 39)), oklch(from var(--tone-blue-icon) .67 .11 calc(h + 39)));
22
+ --fw-h2: light-dark(oklch(from var(--tone-violet-icon) .55 .11 h), oklch(from var(--tone-violet-icon) .70 .11 h));
23
+ --fw-h3: light-dark(oklch(from var(--tone-pink-icon) .55 .11 h), oklch(from var(--tone-pink-icon) .67 .11 calc(h - 16)));
24
+ --fw-h4: light-dark(oklch(from var(--tone-cyan-icon) .60 .11 calc(h - 16.256)), oklch(from var(--tone-cyan-icon) .67 .11 calc(h - 26.148)));
25
+ --fw-h5: light-dark(oklch(from var(--tone-blue-icon) .58 .03 calc(h + 37.6)), oklch(from var(--tone-blue-icon) .67 .03 calc(h + 29.5)));
26
+ --fw-ease: var(--ease-standard, cubic-bezier(.2,.8,.2,1));
42
27
  --fw-bar-h: 20px;
43
- /* One label column for rows, axis and every lane layer; the agent view caps it lower and drops it on phones. */
44
28
  --fw-label-col: min(var(--fw-name-w), 42%);
45
29
  --fw-bar-shift: 0px;
46
30
  position: relative;
47
31
  container-type: inline-size;
32
+ container-name: flow-waterfall;
48
33
  min-width: 0;
49
34
  color: var(--fw-text);
50
35
  background: var(--fw-surface);
51
36
  border: 1px solid var(--fw-border);
52
- border-radius: var(--radius-xl);
37
+ border-radius: var(--radius-container);
53
38
  font-family: var(--font-sans);
54
39
  font-size: 14px;
55
40
  line-height: 20px;
56
- /* Tabular figures only on numeric cells: on names they widen hyphens ("v6 - battery", "glm - 5.3"). */
57
41
  font-feature-settings: "cv11" 1;
58
42
  -webkit-font-smoothing: antialiased;
59
43
  }
@@ -69,17 +53,15 @@
69
53
  .fw-root [data-tone="3"] { --tone: var(--fw-h3); }
70
54
  .fw-root [data-tone="4"] { --tone: var(--fw-h4); }
71
55
  .fw-root [data-tone="5"] { --tone: var(--fw-h5); }
72
- /* The outline is the full hue, which passes 3:1 against the card, so every mark keeps its edge; on a dark card the fill sits a
73
- step toward the canvas, so a bar reads apart from a handoff pill in the full hue. The dim shade (tool calls) mixes the hue
74
- toward the card, more of it on a dark card. */
75
- .fw-root [data-tone] {
76
- --fill: light-dark(var(--tone), color-mix(in oklab, var(--tone) 94%, hsl(var(--hsl-background))));
56
+ .fw-root [data-tone], .fw-root .fw-kinds {
57
+ --fill: light-dark(var(--tone), color-mix(in oklab, var(--tone) 94%, var(--bg-root)));
77
58
  --line: var(--tone);
59
+ --model-fill: light-dark(oklch(from var(--tone, var(--fw-h0)) .50 min(c, .10) h), oklch(from var(--tone, var(--fw-h0)) .72 min(c, .10) h));
60
+ --tool-fill: light-dark(oklch(from var(--tone, var(--fw-h0)) .80 min(c, .10) h), oklch(from var(--tone, var(--fw-h0)) .46 min(c, .10) h));
78
61
  --dim: light-dark(color-mix(in oklab, var(--tone) 62%, var(--fw-surface)), color-mix(in oklab, var(--tone) 40%, var(--fw-surface)));
79
62
  --dim-line: light-dark(color-mix(in oklab, var(--tone) 70%, var(--fw-surface)), color-mix(in oklab, var(--tone) 55%, var(--fw-surface)));
80
63
  }
81
64
 
82
- /* Header */
83
65
  .fw-root .fw-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 16px; border-bottom: 1px solid var(--fw-border); flex-wrap: wrap; }
84
66
  .fw-root .fw-title-block { min-width: 0; display: grid; gap: 4px; }
85
67
  .fw-root .fw-title-line { display: flex; align-items: center; gap: 8px; min-width: 0; }
@@ -88,21 +70,19 @@
88
70
  .fw-root .fw-state { display: inline-flex; align-items: center; gap: 8px; height: 20px; padding: 0 8px; border-radius: var(--radius-pill); font-size: 14px; font-weight: 600; color: var(--fw-muted); background: var(--fw-well); flex-shrink: 0; }
89
71
  .fw-root .fw-state::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
90
72
  .fw-root .fw-state-live { color: var(--fw-live); background: color-mix(in oklab, var(--fw-live) 12%, transparent); }
91
- .fw-root .fw-state-live::before { animation: fw-pulse 1.6s var(--fw-ease) infinite; }
73
+ .fw-root .fw-state-live::before { animation: tangle-breathe var(--motion-ambient) infinite; }
92
74
  .fw-root .fw-headline { color: var(--fw-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
93
75
  .fw-root .fw-headline::before { content: "·"; margin-right: 8px; }
94
76
  .fw-root .fw-tools { display: flex; align-items: center; gap: 8px; }
95
- .fw-root .fw-views { display: inline-flex; padding: 4px; border-radius: var(--radius-md); background: var(--fw-well); border: 1px solid var(--fw-border); }
96
- .fw-root .fw-view-button { appearance: none; border: 0; background: transparent; height: 24px; padding: 0 12px; border-radius: var(--radius-sm); font-size: 14px; color: var(--fw-muted); cursor: pointer; transition: background-color 120ms var(--fw-ease), color 120ms var(--fw-ease); }
97
- /* The shown view is a state, not a control: no click target, no pointer. */
77
+ .fw-root .fw-views { display: inline-flex; padding: 4px; border-radius: var(--radius-control); background: var(--fw-well); border: 1px solid var(--fw-border); }
78
+ .fw-root .fw-view-button { appearance: none; border: 0; background: transparent; height: 24px; padding: 0 12px; border-radius: var(--radius-tag); font-size: 14px; color: var(--fw-muted); cursor: pointer; transition: background-color var(--motion-control), color var(--motion-control); }
98
79
  .fw-root .fw-view-button[aria-checked="true"] { opacity: 1; cursor: default; background: var(--fw-surface); color: var(--fw-text); box-shadow: var(--shadow-card), 0 0 0 1px var(--fw-border); }
99
80
  .fw-root .fw-search { position: relative; display: flex; align-items: center; }
100
81
  .fw-root .fw-search-glyph { position: absolute; left: 8px; display: inline-flex; color: var(--fw-muted); pointer-events: none; }
101
- .fw-root .fw-search-input { appearance: none; width: 200px; height: 32px; padding: 0 12px 0 28px; border: 1px solid var(--fw-border); border-radius: var(--radius-md); background: var(--fw-well); font-size: 14px; }
82
+ .fw-root .fw-search-input { appearance: none; width: 200px; height: 32px; padding: 0 12px 0 28px; border: 1px solid var(--fw-border); border-radius: var(--radius-control); background: var(--fw-well); font-size: 14px; }
102
83
  .fw-root .fw-search-input::placeholder { color: var(--fw-muted); }
103
84
  .fw-root .fw-search-input::-webkit-search-cancel-button { display: none; }
104
85
 
105
- /* KPI row */
106
86
  .fw-root .fw-summary { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); border-bottom: 1px solid var(--fw-border); }
107
87
  .fw-root .fw-summary > div { padding: 12px 16px; min-width: 0; }
108
88
  .fw-root .fw-summary > div + div { border-left: 1px solid var(--fw-border); }
@@ -110,12 +90,11 @@
110
90
  .fw-root .fw-value { margin-top: 4px; font-size: 18px; line-height: 24px; font-weight: 600; letter-spacing: -.01em; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
111
91
  .fw-root .fw-has-errors .fw-value { color: var(--fw-failed); }
112
92
 
113
- /* Axis */
114
93
  .fw-root .fw-axis { display: grid; grid-template-columns: var(--fw-label-col) minmax(0, 1fr); height: 32px; color: var(--fw-muted); font-size: 14px; }
115
94
  .fw-root .fw-axis-top { border-bottom: 1px solid var(--fw-border); }
116
95
  .fw-root .fw-axis-label { position: relative; display: flex; align-items: center; gap: 8px; padding: 0 12px 0 16px; border-right: 1px solid var(--fw-border); min-width: 0; }
117
96
  .fw-root .fw-branch-controls { margin-left: auto; display: flex; gap: 4px; }
118
- .fw-root .fw-text-button { appearance: none; border: 0; background: transparent; height: 24px; padding: 0 8px; border-radius: var(--radius-sm); font-size: 14px; color: var(--fw-muted); cursor: pointer; white-space: nowrap; }
97
+ .fw-root .fw-text-button { appearance: none; border: 0; background: transparent; height: 24px; padding: 0 8px; border-radius: var(--radius-tag); font-size: 14px; color: var(--fw-muted); cursor: pointer; white-space: nowrap; }
119
98
  .fw-root .fw-text-button:hover { background: var(--fw-hover); color: var(--fw-text); }
120
99
  .fw-root .fw-resize { position: absolute; top: 0; right: -4px; width: 8px; height: 100%; cursor: col-resize; z-index: 2; }
121
100
  .fw-root .fw-resize:hover, .fw-root .fw-resize:focus-visible { background: linear-gradient(90deg, transparent 3px, var(--fw-accent) 3px 5px, transparent 5px); outline: 0; }
@@ -123,7 +102,6 @@
123
102
  .fw-root .fw-tick { position: absolute; top: 50%; transform: translate(4px, -50%); left: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px); font-variant-numeric: tabular-nums; white-space: nowrap; line-height: 16px; }
124
103
  .fw-root .fw-cursor-handle { position: absolute; width: 9px; height: 9px; border-radius: 50%; background: var(--fw-text); box-shadow: 0 0 0 2px var(--fw-surface); left: calc(var(--fw-pad) + (var(--fw-t) - var(--fw-v0)) * var(--fw-k) * 1px - 4.5px); bottom: -5px; z-index: 3; }
125
104
 
126
- /* Body */
127
105
  .fw-root .fw-viewport { position: relative; }
128
106
  .fw-root .fw-scroller { position: relative; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; scrollbar-width: thin; outline: 0; }
129
107
  .fw-root .fw-canvas { position: relative; }
@@ -131,15 +109,12 @@
131
109
  .fw-root .fw-gridline { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--fw-grid); left: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px); }
132
110
  .fw-root .fw-cursor-layer { z-index: 3; }
133
111
  .fw-root .fw-marker-layer { position: absolute; top: 0; bottom: 0; left: var(--fw-label-col); right: 0; overflow: hidden; pointer-events: none; z-index: 2; }
134
- /* An event's line is drawn behind the rows, with the gridlines, so bars and labels cover it; its label stays on top. */
135
112
  .fw-root .fw-marker { --fw-marker: var(--fw-muted); position: absolute; top: 0; bottom: 0; width: 0; left: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px); }
136
113
  .fw-root .fw-marker-line { --fw-marker: var(--fw-muted); opacity: .4; position: absolute; top: 0; bottom: 0; border-left: 1px dashed var(--fw-marker); left: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px); }
137
114
  .fw-root .fw-marker-line[data-tone="ok"] { --fw-marker: var(--fw-ok); }
138
115
  .fw-root .fw-marker-line[data-tone="failed"] { --fw-marker: var(--fw-failed); }
139
116
  .fw-root .fw-marker[data-tone="ok"] { --fw-marker: var(--fw-ok); }
140
117
  .fw-root .fw-marker[data-tone="failed"] { --fw-marker: var(--fw-failed); }
141
- /* A pause: the run stood still. A faint band behind the rows (they stay readable over it), and on the axis a hatched strip under
142
- the ticks that names its length and reason on hover and focus; a chip over the axis hid the hour ticks. */
143
118
  .fw-root :is(.fw-pause, .fw-pause-mark) { position: absolute; left: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px); width: calc(max(0, var(--e) - var(--s)) * var(--fw-k) * 1px); }
144
119
  .fw-root .fw-pause { top: 0; bottom: 0; background: color-mix(in oklab, var(--fw-text) 4%, transparent); border-inline: 1px dashed color-mix(in oklab, var(--fw-text) 18%, transparent); }
145
120
  .fw-root .fw-pause-mark { bottom: 3px; height: 6px; border-radius: 2px; cursor: help;
@@ -147,7 +122,6 @@
147
122
  .fw-root .fw-marker-label { position: absolute; top: 2px; left: 3px; max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 0 4px; border-radius: var(--radius-tag); font-size: 14px; line-height: 18px; color: var(--fw-marker); background: var(--fw-surface); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--fw-marker) 40%, transparent); }
148
123
  .fw-root .fw-marker-label-left { left: auto; right: 3px; }
149
124
  .fw-root .fw-marker[data-tone="warn"], .fw-root .fw-event[data-tone="warn"] { --fw-marker: var(--fw-amber); }
150
- /* The event track: one dot per marker on the axis; click or N/P moves the cursor there. */
151
125
  .fw-root .fw-event { --fw-marker: var(--fw-muted); position: absolute; bottom: 2px; width: 8px; height: 8px; margin-left: -4px; padding: 0; border: 0; border-radius: 50%; background: var(--fw-marker); box-shadow: 0 0 0 2px var(--fw-surface); cursor: pointer; z-index: 4; left: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px); }
152
126
  .fw-root .fw-event[data-tone="ok"] { --fw-marker: var(--fw-ok); }
153
127
  .fw-root .fw-event[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--fw-surface), 0 0 0 4px var(--fw-marker); }
@@ -158,15 +132,11 @@
158
132
  .fw-root .fw-empty { padding: 40px 16px; text-align: center; color: var(--fw-muted); }
159
133
  .fw-root .fw-sticky { position: absolute; top: 0; left: 0; right: 0; z-index: 2; background: var(--fw-surface); box-shadow: 0 1px 0 var(--fw-border), var(--shadow-card); }
160
134
 
161
- /* Rows */
162
135
  .fw-root .fw-row { position: absolute; top: 0; left: 0; right: 0; transform: translateY(var(--row-y)); height: var(--fw-row-h); display: grid; grid-template-columns: var(--fw-label-col) minmax(0, 1fr); outline: 0; contain: layout style; }
163
- /* A row's tint is one variable, so the chips on it (an end label, a stacked name) paint the same plane as the row. */
164
136
  .fw-root .fw-row { background: var(--fw-tint, transparent); }
165
137
  .fw-root .fw-row:hover { --fw-tint: var(--fw-hover); }
166
138
  .fw-root .fw-row.fw-active { --fw-tint: color-mix(in oklab, var(--fw-accent) 7%, transparent); }
167
139
  .fw-root .fw-row[aria-selected="true"] { --fw-tint: var(--ix-sel-bg); }
168
- /* A selected agent lifts: an accent edge on its row, and its peers' marks recede; their names and labels keep the muted ink, which
169
- reads at 4.5:1, where fading whole cells dropped them to 2.2:1 and let connectors and pause bands show through the text. */
170
140
  .fw-root-agents[data-selection] .fw-row[aria-selected="true"] { box-shadow: inset 3px 0 0 var(--fw-accent); }
171
141
  .fw-root-agents[data-selection] .fw-row:not([aria-selected="true"]) { --fw-recede: .4; }
172
142
  .fw-root-agents[data-selection] .fw-row:not([aria-selected="true"]):hover { --fw-recede: .8; }
@@ -176,44 +146,37 @@
176
146
  .fw-root .fw-label { position: relative; display: flex; align-items: center; gap: 8px; min-width: 0; padding: 0 12px 0 calc(12px + var(--depth) * 16px); border-right: 1px solid var(--fw-border); }
177
147
  .fw-root .fw-guides { position: absolute; top: 0; bottom: 0; left: 20px; width: calc(var(--depth) * 16px); background: repeating-linear-gradient(90deg, var(--fw-guide) 0 1px, transparent 1px 16px); }
178
148
  .fw-root .fw-toggle, .fw-root .fw-toggle-space { flex: 0 0 16px; width: 16px; height: 16px; }
179
- .fw-root .fw-toggle { appearance: none; display: grid; place-items: center; padding: 0; border: 0; border-radius: var(--radius-tag); background: transparent; color: var(--fw-muted); cursor: pointer; }
180
- .fw-root .fw-toggle svg { width: 12px; height: 12px; transition: transform 160ms var(--fw-ease); }
149
+ .fw-root .fw-toggle { appearance: none; pointer-events: auto; display: grid; place-items: center; padding: 0; border: 0; border-radius: var(--radius-tag); background: transparent; color: var(--fw-muted); cursor: pointer; }
150
+ .fw-root .fw-toggle svg { width: 12px; height: 12px; transition: transform var(--motion-control); }
181
151
  .fw-root .fw-toggle.fw-open svg { transform: rotate(90deg); }
182
152
  .fw-root .fw-toggle:hover { background: var(--fw-hover); color: var(--fw-text); }
183
- .fw-root .fw-icon-tile { flex: 0 0 20px; display: grid; place-items: center; width: 20px; height: 20px; border-radius: var(--radius-sm); border: 1px solid var(--fw-border); background: var(--fw-surface); color: var(--line, var(--fw-muted)); }
153
+ .fw-root .fw-icon-tile { flex: 0 0 20px; display: grid; place-items: center; width: 20px; height: 20px; border-radius: var(--radius-tag); border: 1px solid var(--fw-border); background: var(--fw-surface); color: var(--line, var(--fw-muted)); }
184
154
  .fw-root .fw-icon-tile :is(svg, img) { width: 13px; height: 13px; }
185
155
  .fw-root .fw-row .fw-names { font-size: 14px; }
186
156
  .fw-root .fw-names { display: flex; align-items: baseline; gap: 8px; min-width: 0; overflow: hidden; white-space: nowrap; }
187
157
  .fw-root .fw-span-name { display: inline-flex; align-items: center; gap: 8px; overflow: hidden; flex-shrink: 0; max-width: 100%; min-width: 0; }
188
158
  .fw-root .fw-name-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
189
159
  .fw-root .fw-strong { font-weight: 600; }
190
- .fw-root .fw-detail { color: var(--fw-muted); overflow: hidden; min-width: 0; display: inline-flex; align-items: center; gap: 4px; max-width: 100%; }
191
160
  .fw-root .fw-detail-icon { display: inline-flex; }
192
161
  .fw-root .fw-detail-icon :is(svg, img) { width: 12px; height: 12px; }
193
162
  .fw-root .fw-count { flex-shrink: 0; height: 18px; padding: 0 8px; border-radius: var(--radius-pill); background: var(--fw-well); border: 1px solid var(--fw-border); color: var(--fw-muted); font-size: 14px; line-height: 18px; font-variant-numeric: tabular-nums; }
194
163
  .fw-root .fw-alert { display: inline-flex; color: var(--fw-failed); flex-shrink: 0; }
195
164
  .fw-root .fw-alert svg { width: 13px; height: 13px; }
196
- .fw-root .fw-spinner { width: 10px; height: 10px; border-radius: 50%; border: 1.5px solid color-mix(in oklab, var(--fw-accent) 30%, transparent); border-top-color: var(--fw-accent); animation: fw-spin .8s linear infinite; flex-shrink: 0; }
165
+ .fw-root .fw-spinner { width: 6px; height: 6px; border-radius: 50%; background: var(--fw-live); animation: tangle-breathe var(--motion-ambient) infinite; flex-shrink: 0; }
197
166
 
198
- /* Agent rows: a dot, the agent and its model on a second line. */
199
167
  .fw-root .fw-view-agents .fw-label { padding-left: 16px; gap: 12px; border-right: 0; }
200
168
  .fw-root .fw-view-agents .fw-names { flex-direction: column; align-items: flex-start; gap: 0; }
201
169
  .fw-root .fw-view-agents .fw-span-name { font-weight: 600; line-height: 18px; }
202
- /* One 16 px line that wraps its last parts out of sight: when the line is full a count drops whole, never "48 …". */
203
170
  .fw-root .fw-view-agents .fw-subtitle { display: flex; align-items: center; column-gap: 4px; height: 18px; overflow: hidden; max-width: 100%; min-width: 0; color: var(--fw-muted); font-size: 14px; line-height: 18px; }
204
171
  .fw-root .fw-view-agents .fw-subtitle-extra { flex: none; white-space: nowrap; }
205
- /* Only the tail wraps, so the first part is always on its line and shrinks with an ellipsis; later parts wrap out of sight. */
206
172
  .fw-root .fw-view-agents .fw-subtitle-tail { display: flex; flex-wrap: wrap; column-gap: 4px; height: 18px; overflow: hidden; min-width: 0; flex: 0 1 auto; }
207
173
  .fw-root .fw-view-agents .fw-subtitle-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
208
174
  .fw-root .fw-view-agents .fw-detail-icon { flex: none; width: 16px; height: 16px; align-items: center; justify-content: center; overflow: hidden; }
209
- .fw-root .fw-view-agents .fw-detail { font-size: 14px; line-height: 18px; }
210
- .fw-root .fw-agent-dot { flex: 0 0 8px; width: 8px; height: 8px; border-radius: 50%; background: var(--line, var(--fw-muted)); }
211
- /* The dot is outcome only (critique P0 #3): an agent with no recorded outcome gets a hollow ring, never its lane hue. */
212
- .fw-root .fw-row:not([data-outcome]) .fw-agent-dot:not([data-outcome]) { background: transparent; box-shadow: inset 0 0 0 1.5px var(--fw-muted); }
213
- .fw-root :is(.fw-agent-dot, .fw-legend .fw-swatch)[data-ring] { background: transparent; box-shadow: inset 0 0 0 2px var(--line, var(--fw-muted)); }
214
- .fw-root :is(.fw-agent-dot, .fw-legend .fw-swatch)[data-outcome="live"]:not([data-ring]) { box-shadow: 0 0 0 3px color-mix(in oklab, var(--fw-live) 22%, transparent); }
175
+ .fw-root .fw-agent-dot { flex: 0 0 8px; width: 8px; height: 8px; border-radius: 50%; background: var(--tone, var(--fw-muted)); }
176
+ .fw-root .fw-agent-dot:not([data-outcome]) { background: transparent; box-shadow: inset 0 0 0 1.5px var(--fw-muted); }
177
+ .fw-root :is(.fw-agent-dot, .fw-legend .fw-swatch)[data-ring] { background: transparent; box-shadow: inset 0 0 0 1.5px var(--tone, var(--fw-muted)); }
178
+ .fw-root :is(.fw-agent-dot, .fw-legend .fw-swatch)[data-outcome="live"]:not([data-ring]) { animation: tangle-breathe var(--motion-ambient) infinite; }
215
179
 
216
- /* Lanes and marks */
217
180
  .fw-root .fw-lane { position: relative; overflow: hidden; min-width: 0; }
218
181
  .fw-root .fw-invalid { position: absolute; left: var(--fw-pad); top: 50%; transform: translateY(-50%); color: var(--fw-muted); }
219
182
  .fw-root :is(.fw-bar, .fw-seg, .fw-end, .fw-mark) {
@@ -225,68 +188,44 @@
225
188
  left: var(--x0);
226
189
  opacity: var(--on);
227
190
  }
228
- .fw-root :is(.fw-bar, .fw-seg, .fw-mark) { opacity: calc(var(--on) * var(--fw-recede, 1)); transition: opacity 160ms var(--fw-ease); }
229
- .fw-root .fw-enter > [role="gridcell"] { animation: fw-unfold 220ms var(--fw-ease) var(--fw-enter-delay) both; }
230
- .fw-root .fw-enter :is(.fw-bar, .fw-seg) { animation: fw-grow 420ms var(--fw-ease) var(--fw-enter-delay) both; }
231
- .fw-root .fw-enter .fw-end { animation: fw-land 220ms var(--fw-ease) calc(var(--fw-enter-delay) + 300ms) both; }
232
- .fw-root .fw-settle :is(.fw-bar, .fw-seg) { animation: fw-settle 600ms var(--fw-ease); }
233
- .fw-root .fw-settle .fw-end { animation: fw-land 260ms var(--fw-ease); }
234
- /* A live bar grows with the clock instead of stepping each tick. */
235
- .fw-root .fw-row[data-running] .fw-bar { transition: width 120ms linear; }
236
- /* The chain that gates the trace's end: an accent edge under its bars, so cutting latency there is visibly what moves the end. */
191
+ .fw-root :is(.fw-bar, .fw-seg, .fw-mark) { opacity: calc(var(--on) * var(--fw-recede, 1)); transition: opacity var(--motion-control); }
192
+ .fw-root .fw-row { transition: transform var(--motion-surface); }
193
+ .fw-root .fw-enter > [role="gridcell"] { transition: opacity var(--motion-arrive); transition-delay: var(--fw-enter-delay); }
194
+ .fw-root .fw-enter :is(.fw-bar, .fw-seg) { transition: clip-path var(--motion-grow, var(--motion-stream)); transition-delay: var(--fw-enter-delay); clip-path: inset(0); }
195
+ @starting-style { .fw-root .fw-enter > [role="gridcell"] { opacity: 0; } .fw-root .fw-enter :is(.fw-bar, .fw-seg) { clip-path: inset(0 100% 0 0); } }
237
196
  .fw-root .fw-row[data-critical] .fw-bar { box-shadow: inset 0 -2px 0 var(--fw-accent); }
238
- /* While searching, ancestors shown only for context recede and the matching names stand out. */
239
- .fw-root .fw-row[data-search="context"] .fw-label { opacity: 0.55; }
197
+ .fw-root .fw-row[data-search="context"] .fw-label { color: var(--fw-muted); }
240
198
  .fw-root .fw-row[data-search="match"] .fw-name-text { font-weight: 600; }
241
- /* A restart: a notch in the failure ink through the bar at that moment (the run overview's ✕); the bar keeps its own state. */
242
199
  .fw-root .fw-mark { position: absolute; z-index: 2; left: calc(var(--x0) - 1px); width: 2px; top: calc(50% + var(--fw-bar-shift) - var(--fw-bar-h) / 2 - 4px); height: calc(var(--fw-bar-h) + 8px);
243
200
  border-radius: 2px; background: var(--fw-failed); box-shadow: 0 0 0 1px var(--fw-surface); pointer-events: auto; }
244
- /* Centered by top, not a negative margin, so measured spacing stays on the grid. */
245
201
  .fw-root :is(.fw-bar, .fw-seg) { top: calc(50% + var(--fw-bar-shift) - var(--fw-bar-h) / 2); height: var(--fw-bar-h); width: max(var(--w), calc(var(--on) * 3px)); border-radius: var(--radius-tag); cursor: pointer; display: flex; align-items: center; justify-content: center; overflow: hidden; }
246
202
  .fw-root .fw-bar { background: var(--fill); box-shadow: inset 0 0 0 1px var(--line); }
247
- /* The mark under the pointer lifts: a brighter fill and a ring in its own tone. */
248
203
  .fw-root :is(.fw-bar, .fw-seg):hover { filter: brightness(1.18); box-shadow: inset 0 0 0 1px var(--line), 0 0 0 2px color-mix(in oklab, var(--line) 55%, transparent); z-index: 1; }
249
204
  .fw-root .fw-bar.fw-k-tool { background: var(--dim); box-shadow: inset 0 0 0 1px var(--dim-line); }
250
- /* A handoff is a moment: a slim pill in the receiving tone, not a block of work. */
251
205
  .fw-root .fw-bar.fw-k-handoff { background: var(--line); height: 8px; top: calc(50% + var(--fw-bar-shift) - 4px); box-shadow: none; }
252
206
  .fw-root .fw-bar.fw-k-waiting { background: repeating-linear-gradient(-45deg, var(--fw-hatch) 0 1px, transparent 1px 5px); box-shadow: inset 0 0 0 1px var(--fw-hatch); }
253
207
  .fw-root .fw-bar.fw-k-failed { background: color-mix(in oklab, var(--fw-failed) 22%, var(--fw-surface)); box-shadow: inset 0 0 0 1px var(--fw-failed); color: var(--fw-failed); }
254
208
  .fw-root .fw-bar.fw-k-pipeline { background: linear-gradient(90deg, var(--fw-neutral-bar), color-mix(in oklab, var(--fw-neutral-bar) 55%, var(--fw-surface))); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--fw-text) 12%, transparent); }
255
- .fw-root .fw-bar.fw-k-pipeline.fw-has-marks { background: color-mix(in oklab, var(--tone) 9%, transparent); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--tone) 18%, transparent); }
209
+ .fw-root .fw-bar.fw-k-pipeline.fw-has-marks { background: repeating-linear-gradient(-45deg, var(--fw-hatch) 0 1px, transparent 1px 5px); box-shadow: inset 0 0 0 1px var(--fw-hatch); }
256
210
  .fw-root .fw-bar.fw-k-pipeline.fw-has-agents { background: repeating-linear-gradient(-45deg, var(--fw-hatch) 0 1px, transparent 1px 5px), color-mix(in oklab, var(--fw-text) 4%, transparent); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--fw-text) 12%, transparent); }
257
- /* An agent bar with no child marks says who it is by its lane color, never a neutral grey. */
258
- /* In the agent view the bar's own extent between steps is waiting: a flat faint fill under the step segments (long hatching read muddy). */
259
- .fw-root .fw-view-agents .fw-bar.fw-k-pipeline.fw-has-marks { background: color-mix(in oklab, var(--tone) 12%, transparent); }
260
- /* An agent lane is a tint of its color; full saturation is kept for its model and tool segments and its status dot, so a run never
261
- reads as a wall of colored slabs. */
262
211
  .fw-root .fw-view-agents .fw-bar.fw-k-pipeline:not(.fw-has-marks, .fw-has-agents) { background: color-mix(in oklab, var(--tone) 30%, var(--fw-surface)); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--tone) 55%, transparent); }
263
- /* A family row without one shared outcome is neutral when bars mean outcomes, not a hue of its own. */
264
212
  .fw-root.fw-fill-outcome .fw-view-agents .fw-row[data-span-id^="family:"]:not([data-outcome]), .fw-root.fw-fill-outcome .fw-view-agents .fw-row[data-span-id^="family:"]:not([data-outcome]) [data-tone] { --tone: var(--fw-h0); }
265
- /* A host outcome re-tones the whole lane: dot, bar and marks. */
266
- /* Muted agents (the default): one quiet tone for every agent bar, its steps and handoffs; the slowest agent alone takes amber, and
267
- failure keeps its red glyph, so color only ever means state. */
268
213
  .fw-root.fw-fill-muted .fw-view-agents [data-tone] { --tone: var(--fw-h0); }
269
- /* With per-agent hues the slowest agent keeps its own hue and says so with the amber pill (round 27: an amber-brown bar read "muddy"). */
270
- .fw-root [data-outcome="ok"], .fw-root [data-outcome="ok"] [data-tone] { --tone: var(--fw-ok); }
271
- .fw-root [data-outcome="failed"], .fw-root [data-outcome="failed"] [data-tone] { --tone: var(--fw-failed); }
272
- .fw-root [data-outcome="warn"], .fw-root [data-outcome="warn"] [data-tone] { --tone: var(--fw-amber); }
273
- .fw-root [data-outcome="live"], .fw-root [data-outcome="live"] [data-tone] { --tone: var(--fw-live); }
274
- .fw-root [data-outcome="neutral"], .fw-root [data-outcome="neutral"] [data-tone] { --tone: var(--fw-muted); }
275
- /* An agent's dot states how it ended in every fill: the muted fill quiets the lanes, never the dot (0.21.23 drew Shipped grey). */
276
- .fw-root.fw-root .fw-agent-dot[data-outcome="ok"] { --tone: var(--fw-ok); }
277
- .fw-root.fw-root .fw-agent-dot[data-outcome="failed"] { --tone: var(--fw-failed); }
278
- .fw-root.fw-root .fw-agent-dot[data-outcome="warn"] { --tone: var(--fw-amber); }
279
- .fw-root.fw-root .fw-agent-dot[data-outcome="live"] { --tone: var(--fw-live); }
280
- .fw-root.fw-root .fw-agent-dot[data-outcome="neutral"] { --tone: var(--fw-muted); }
214
+ .fw-root [data-outcome="ok"] { --outcome-tone: var(--fw-ok); }
215
+ .fw-root [data-outcome="failed"] { --outcome-tone: var(--fw-failed); }
216
+ .fw-root [data-outcome="warn"] { --outcome-tone: var(--fw-amber); }
217
+ .fw-root [data-outcome="live"] { --outcome-tone: var(--fw-live); }
218
+ .fw-root [data-outcome="neutral"] { --outcome-tone: var(--fw-muted); }
219
+ .fw-root [data-outcome], .fw-root [data-outcome] [data-tone] { --tone: var(--outcome-tone); }
220
+ .fw-root.fw-root .fw-agent-dot[data-outcome] { --tone: var(--outcome-tone); }
281
221
  .fw-root .fw-legend { display: flex; flex-wrap: wrap; gap: 4px 16px; margin: 8px 0 0; padding: 0; list-style: none; color: var(--fw-muted); font-size: 14px; line-height: 18px; }
282
222
  .fw-root .fw-legend li { display: inline-flex; align-items: center; gap: 8px; }
283
223
  .fw-root .fw-legends { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 24px; margin-top: 8px; }
284
224
  .fw-root .fw-legends .fw-legend { margin: 0; }
285
225
  .fw-root .fw-kinds { margin: 0; padding: 0; justify-content: flex-start; color: var(--fw-muted); }
286
226
  .fw-root .fw-kinds .fw-swatch-model { width: 12px; height: 8px; border-radius: 2px; background: var(--fw-kind-model); }
287
- /* With per-agent hues the key shows kind as shade, as the bars do: darker for a model call, lighter for a tool call. */
288
- .fw-root.fw-fill-agent .fw-kinds .fw-swatch-model { background: light-dark(oklch(0.50 0 0), oklch(0.82 0 0)); }
289
- .fw-root.fw-fill-agent .fw-kinds .fw-swatch-tool { background: light-dark(oklch(0.80 0 0), oklch(0.64 0 0)); }
227
+ .fw-root.fw-fill-agent .fw-kinds .fw-swatch-model { background: var(--model-fill); }
228
+ .fw-root.fw-fill-agent .fw-kinds .fw-swatch-tool { background: var(--tool-fill); }
290
229
  .fw-root .fw-kinds .fw-swatch-tool { width: 12px; height: 8px; border-radius: 2px; background: var(--fw-kind-tool); }
291
230
  .fw-root .fw-kinds .fw-swatch-wait { width: 12px; height: 8px; border-radius: 2px; background: color-mix(in oklab, var(--fw-text) 12%, transparent); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--fw-text) 18%, transparent); }
292
231
  .fw-root .fw-kinds .fw-swatch-event { width: 8px; height: 8px; border-radius: 50%; background: var(--fw-muted); box-shadow: 0 0 0 2px var(--fw-surface); }
@@ -301,55 +240,38 @@
301
240
  .fw-root .fw-view-agents .fw-seg { pointer-events: auto; }
302
241
  .fw-root .fw-seg-model { background: var(--fill); box-shadow: inset 0 0 0 1px var(--line); }
303
242
  .fw-root .fw-seg-tool { background: var(--dim); box-shadow: inset 0 0 0 1px var(--dim-line); }
304
- /* Without per-agent hues (muted or outcome fill), a step's kind owns its color; with them, kind is a fixed lightness on the agent's own
305
- hue (the rules below), so each agent stays one color. */
306
243
  .fw-root:is(.fw-fill-muted, .fw-fill-outcome) .fw-view-agents .fw-seg-model { background: var(--fw-kind-model); box-shadow: none; }
307
244
  .fw-root:is(.fw-fill-muted, .fw-fill-outcome) .fw-view-agents .fw-seg-tool { background: var(--fw-kind-tool); box-shadow: none; }
308
- /* Per-agent hues: a step's kind is a fixed lightness on its agent's hue, the same for every hue, so kind reads by lightness
309
- against the surface and never contradicts the key (critique P0 #4: dark tool steps were lighter than model steps, and the
310
- step varied with hue). On dark both steps are light pastels, model 0.82 and tool 0.64: round 27's pastel dark fills scored
311
- 39/252, and the critique's darker 0.72/0.46 read muddy to round 29's judges. Model is the stronger step on either surface; chroma is capped at 0.10, never raised, so a neutral (the root's grey) stays
312
- neutral instead of taking a red cast from its undefined hue. */
313
- .fw-root.fw-fill-agent .fw-view-agents .fw-seg-model { background: light-dark(oklch(from var(--tone) 0.50 min(c, 0.10) h), oklch(from var(--tone) 0.82 min(c, 0.10) h)); box-shadow: none; }
314
- .fw-root.fw-fill-agent .fw-view-agents :is(.fw-seg-tool, .fw-seg-failed) { background: light-dark(oklch(from var(--tone) 0.80 min(c, 0.10) h), oklch(from var(--tone) 0.64 min(c, 0.10) h)); box-shadow: none; }
315
- /* A failed turn is still the agent's work: it keeps the agent's hue, and the red cross on the ending turn says it failed. */
245
+ .fw-root.fw-fill-agent .fw-view-agents .fw-seg-model { background: var(--model-fill); box-shadow: none; }
246
+ .fw-root.fw-fill-agent .fw-view-agents :is(.fw-seg-tool, .fw-seg-failed) { background: var(--tool-fill); box-shadow: none; }
316
247
  .fw-root.fw-fill-agent .fw-view-agents .fw-seg-failed { box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--fw-failed) 55%, transparent); }
317
248
  .fw-root .fw-view-agents .fw-seg { container-type: inline-size; justify-content: flex-start; font-size: 14px; line-height: 18px; white-space: nowrap; color: var(--fw-seg-ink); }
318
- /* Label ink holds 4.5:1 on its fill: page-light on the dark light-theme model step (L 0.50), page-dark on every other step
319
- (light tool 0.80, dark pastels 0.82 and 0.64). */
320
- .fw-root .fw-view-agents .fw-seg-model { --fw-seg-ink: light-dark(var(--fw-surface), hsl(var(--hsl-background))); }
321
- .fw-root .fw-view-agents :is(.fw-seg-tool, .fw-seg-failed) { --fw-seg-ink: light-dark(var(--fw-text), hsl(var(--hsl-background))); }
249
+ .fw-root .fw-view-agents .fw-seg-model { --fw-seg-ink: light-dark(var(--fw-surface), var(--bg-root)); }
250
+ .fw-root .fw-view-agents :is(.fw-seg-tool, .fw-seg-failed) { --fw-seg-ink: var(--fw-text); }
322
251
  .fw-root .fw-view-agents .fw-seg > * { pointer-events: none; }
323
- /* One line, 6 px in from each end; a part that does not fit whole wraps to the hidden second line, so trailing parts drop first. */
324
252
  .fw-root .fw-seg-label { flex: 1; min-width: 0; height: 16px; margin: 0 6px; display: flex; flex-wrap: wrap; align-items: center; column-gap: 4px; overflow: hidden; }
325
253
  .fw-root .fw-seg-label > * { flex: none; height: 16px; display: inline-flex; align-items: center; }
326
254
  .fw-root .fw-seg-icon svg { width: 12px; height: 12px; stroke-width: 1.75; }
327
255
  .fw-root .fw-seg-name { font-weight: 600; }
328
256
  .fw-root .fw-seg-chip { padding: 0 4px; border-radius: var(--radius-pill); background: color-mix(in oklab, currentColor 18%, transparent); font-weight: 600; font-variant-numeric: tabular-nums; }
329
257
  .fw-root .fw-seg-meta { font-variant-numeric: tabular-nums; }
330
- @container (max-width: 24px) { .fw-root :is(.fw-seg-label, .fw-view-agents .fw-seg .fw-glyph) { display: none; } }
331
- /* Fail and retry read as filled chips inside the bar: red, and a quiet grey for a retry that recovered. */
332
- .fw-root .fw-view-agents .fw-seg .fw-glyph { flex: none; margin-left: 6px; display: grid; place-items: center; width: 14px; height: 14px; border-radius: 50%; background: var(--fw-failed); color: light-dark(var(--fw-surface), hsl(var(--hsl-background))); }
258
+ @container (max-width: 24px) { .fw-root .fw-seg-label { display: none; } }
259
+ @container (max-width: 5px) { .fw-root .fw-view-agents .fw-seg .fw-glyph { display: none; } }
260
+ .fw-root .fw-view-agents .fw-seg .fw-glyph { flex: none; margin-left: 0; display: grid; place-items: center; width: min(14px, 100%); height: 14px; border-radius: 50%; background: var(--fw-failed); color: light-dark(var(--fw-surface), var(--bg-root)); }
333
261
  .fw-root .fw-view-agents .fw-seg .fw-glyph[data-glyph="retry"] { background: var(--fw-muted); }
334
- .fw-root .fw-view-agents .fw-seg .fw-glyph svg { width: 9px; height: 9px; stroke-width: 2.5; }
335
- .fw-root:is(.fw-fill-muted, .fw-fill-outcome) .fw-view-agents :is(.fw-seg-alt.fw-seg-model, .fw-seg-alt.fw-seg-tool) { filter: brightness(.88); }
262
+ .fw-root .fw-view-agents .fw-seg .fw-glyph svg { width: min(9px, 100%); height: 9px; stroke-width: 2.5; }
336
263
  .fw-root .fw-seg-failed { background: color-mix(in oklab, var(--fw-failed) 18%, var(--fw-surface)); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--fw-failed) 70%, transparent); color: var(--fw-failed); }
337
- /* In the agent view a failed turn draws like any turn; the agent's own failure shows once, by its name and its end label. */
338
- .fw-root .fw-view-agents .fw-seg-failed { background: var(--dim); box-shadow: inset 0 0 0 1px var(--dim-line); }
264
+ .fw-root .fw-view-agents .fw-seg-failed { background: var(--tool-fill); box-shadow: inset 0 0 0 1px var(--dim-line); }
339
265
  .fw-root .fw-glyph { display: inline-flex; color: currentColor; }
340
266
  .fw-root .fw-glyph svg { width: 11px; height: 11px; stroke-width: 2; }
341
267
  .fw-root .fw-seg-model .fw-glyph, .fw-root .fw-k-model .fw-glyph { color: light-dark(var(--tone), var(--fw-text)); }
342
- /* End labels sit on the surface, so connectors and event lines stop at them, and never run past the lane: a label that would
343
- be cut at the right edge slides back over its bar's tail instead. */
344
268
  .fw-root .fw-end { z-index: 2; display: inline-flex; align-items: center; gap: 8px; top: calc(50% + var(--fw-bar-shift) - var(--fw-bar-h) / 2); height: var(--fw-bar-h);
345
- left: min(calc(var(--x0) + max(var(--w), 3px) + 4px), calc(100% - var(--fw-end-w, 0px) - 4px)); padding: 0 4px; border-radius: var(--radius-tag);
269
+ left: min(calc(var(--x0) + max(var(--w), 3px) + 4px), calc(100% - var(--fw-end-w, 0px) - var(--fw-badge-w, 0px) - 4px)); padding: 0 4px; border-radius: var(--radius-tag);
346
270
  background: linear-gradient(var(--fw-tint, transparent) 0 0), var(--fw-surface); white-space: nowrap; pointer-events: none; }
347
271
  .fw-root .fw-duration { font-size: 14px; color: var(--fw-muted); font-variant-numeric: tabular-nums; }
348
272
  .fw-root .fw-k-failed ~ .fw-end .fw-duration { color: var(--fw-failed); }
349
273
  .fw-root .fw-badge { height: 20px; padding: 0 8px; border-radius: var(--radius-pill); font-size: 14px; line-height: 20px; font-weight: 500; color: var(--fw-muted); background: transparent; box-shadow: inset 0 0 0 1px var(--fw-border); }
350
274
 
351
- /* Handoff elbows: 1.5 px stroke, 6 px radius, a 5 px dot where the handoff leaves its sender. */
352
- /* Under the rows (which follow it), so end labels and badges always sit above the connectors. */
353
275
  .fw-root .fw-links { position: absolute; top: 0; bottom: 0; left: var(--fw-label-col); right: 0; overflow: hidden; pointer-events: none; z-index: 0; }
354
276
  .fw-root .fw-link {
355
277
  --x: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px);
@@ -357,48 +279,37 @@
357
279
  opacity: clamp(0, (var(--fw-t) - var(--s)) * 1000, 1);
358
280
  }
359
281
  .fw-root .fw-view-spans .fw-link { opacity: calc(clamp(0, (var(--fw-t) - var(--s)) * 1000, 1) * .7); }
360
- /* Agent-view connectors rest faint, one curved elbow per spawn in the agent's hue (they show who started whom), and come to
361
- full strength for the agent under the pointer, pinned or focused. */
362
282
  .fw-root .fw-view-agents .fw-link:not(.fw-link-hot) { opacity: calc(clamp(0, (var(--fw-t) - var(--s)) * 1000, 1) * .8); }
363
- .fw-root .fw-link { transition: opacity 160ms var(--fw-ease); }
283
+ .fw-root .fw-link { transition: opacity var(--motion-control); }
364
284
  .fw-root .fw-link-down { left: calc(var(--x) - 12px); border-left-width: 1.5px; border-bottom-left-radius: 12px; }
365
285
  .fw-root .fw-link-up { left: var(--x); border-right-width: 1.5px; border-bottom-right-radius: 12px; }
366
286
  .fw-root .fw-link::before { content: ""; position: absolute; top: -3.5px; width: 5px; height: 5px; border-radius: 50%; background: var(--line); box-shadow: 0 0 0 1.5px var(--fw-surface); }
367
287
  .fw-root .fw-link-down::before { left: -3.25px; }
368
288
  .fw-root .fw-link-up::before { right: -3.25px; }
369
289
 
370
- /* Span card */
371
290
  .fw-root .fw-card-anchor { display: contents; }
372
291
  .fw-root .fw-seg-alt { filter: brightness(.78); }
373
- /* Agent view: the scrubber track shows how far the run has played, so at rest it reads full, not empty. */
374
292
  .fw-root.fw-root-agents .fw-slider { background: linear-gradient(90deg, color-mix(in oklab, var(--fw-text) 32%, transparent) var(--fw-progress), var(--fw-well) var(--fw-progress)) 0 50% / 100% 4px no-repeat; }
375
- /* The playhead: a round knob on a thin neutral track, visible at rest without outweighing the chart. */
376
293
  .fw-root.fw-root-agents .fw-slider::-webkit-slider-thumb { width: 14px; height: 14px; border-radius: 50%; background: var(--fw-text); box-shadow: 0 0 0 3px var(--fw-surface); }
377
294
  .fw-root.fw-root-agents .fw-slider::-moz-range-thumb { width: 14px; height: 14px; border-radius: 50%; background: var(--fw-text); box-shadow: 0 0 0 3px var(--fw-surface); }
378
- .fw-root .fw-card { position: absolute; z-index: 20; width: min(320px, calc(100% - 16px)); max-height: min(60vh, 560px); overflow-y: auto; overscroll-behavior: contain; padding: 12px; border-radius: var(--radius-xl); background: var(--fw-raised); border: 1px solid var(--fw-border);
379
- box-shadow: var(--shadow-dropdown); animation: fw-arrive 140ms var(--fw-ease); }
380
- /* A hover card never covers the next mark: only a pinned card (a dialog) takes the pointer. */
295
+ .fw-root .fw-card { position: absolute; z-index: 20; width: min(320px, calc(100% - 16px)); max-height: min(60vh, 560px); overflow-y: auto; overscroll-behavior: contain; padding: 12px; border-radius: var(--radius-card); background: var(--fw-raised); border: 1px solid var(--fw-border);
296
+ box-shadow: var(--shadow-dropdown); transition: left var(--motion-surface), top var(--motion-surface), opacity var(--motion-control); }
381
297
  .fw-root .fw-card[role="tooltip"] { pointer-events: none; }
382
- .fw-root .fw-card-above .fw-card { transform: translateY(-100%); animation-name: fw-arrive-above; }
298
+ .fw-root .fw-card-above .fw-card { transform: translateY(-100%); }
383
299
  .fw-root .fw-card-agent { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; font-size: 14px; font-weight: 600; }
384
300
  .fw-root .fw-card-agent .fw-agent-dot { align-self: center; }
385
301
  .fw-root .fw-card-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
386
- .fw-root .fw-card-head .fw-icon-tile { flex-basis: 28px; width: 28px; height: 28px; border-radius: var(--radius-md); }
302
+ .fw-root .fw-card-head .fw-icon-tile { flex-basis: 28px; width: 28px; height: 28px; border-radius: var(--radius-control); }
387
303
  .fw-root .fw-card-head .fw-icon-tile :is(svg, img) { width: 16px; height: 16px; }
388
304
  .fw-root .fw-card-name { flex: 1; min-width: 0; font-size: 14px; line-height: 20px; font-weight: 600; overflow-wrap: anywhere; }
389
- .fw-root .fw-pill { flex-shrink: 0; height: 20px; padding: 0 8px; border-radius: var(--radius-pill); font-size: 14px; line-height: 20px; font-weight: 600; }
390
- .fw-root .fw-pill-done { color: var(--fw-ok); background: color-mix(in oklab, var(--fw-ok) 13%, transparent); }
391
- .fw-root .fw-pill-running { color: var(--fw-live); background: color-mix(in oklab, var(--fw-live) 13%, transparent); }
392
- .fw-root .fw-pill-failed { color: var(--fw-failed); background: color-mix(in oklab, var(--fw-failed) 13%, transparent); }
393
- .fw-root .fw-icon-button { appearance: none; display: inline-grid; place-items: center; width: 28px; height: 28px; padding: 0; border: 0; border-radius: var(--radius-md); background: transparent; color: var(--fw-muted); cursor: pointer; flex-shrink: 0; }
305
+ .fw-root .fw-pill { flex-shrink: 0; height: 20px; padding: 0 8px; border-radius: var(--radius-pill); font-size: 14px; line-height: 20px; font-weight: 600; color: var(--fw-status); background: color-mix(in oklab, var(--fw-status) 13%, transparent); }
306
+ .fw-root .fw-icon-button { appearance: none; display: inline-grid; place-items: center; width: 28px; height: 28px; padding: 0; border: 0; border-radius: var(--radius-control); background: transparent; color: var(--fw-muted); cursor: pointer; flex-shrink: 0; }
394
307
  .fw-root .fw-icon-button:hover:not(:disabled) { background: var(--fw-hover); color: var(--fw-text); }
395
308
  .fw-root .fw-card-close { width: 20px; height: 20px; margin-right: -4px; }
396
- /* The card's table: label and value on one line each, every value with its unit; a tool breakdown sits under its total. */
397
309
  .fw-root .fw-card-table { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 6px 16px; padding-top: 8px; border-top: 1px solid var(--fw-border); font-size: 14px; line-height: 18px; }
398
310
  .fw-root .fw-card-table > div { display: contents; }
399
311
  .fw-root .fw-card-table dt, .fw-root .fw-card-label { font-size: 14px; line-height: 18px; color: var(--fw-muted); }
400
312
  .fw-root .fw-card-table dd { min-width: 0; font-weight: 500; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
401
- /* A value of several parts wraps between parts, never inside one ("4.0M" / "cached"). */
402
313
  .fw-root .fw-card-parts { display: flex; flex-wrap: wrap; column-gap: 4px; }
403
314
  .fw-root .fw-card-parts > * { white-space: nowrap; }
404
315
  .fw-root .fw-card-parts:not(.fw-card-stack) > * + *::before { content: "· "; color: var(--fw-muted); }
@@ -415,14 +326,13 @@
415
326
  .fw-root .fw-card-result { margin-top: 8px; font-size: 14px; line-height: 20px; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
416
327
  .fw-root .fw-card-text p { margin-top: 4px; color: var(--fw-text); overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
417
328
 
418
- /* Playback */
419
329
  .fw-root .fw-playback { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--fw-border); }
420
- .fw-root .fw-round-button { appearance: none; display: grid; place-items: center; width: 32px; height: 32px; padding: 0; border-radius: 50%; border: 1px solid var(--fw-border); background: var(--fw-well); color: var(--fw-text); cursor: pointer; transition: background-color 120ms var(--fw-ease), transform 120ms var(--fw-ease); }
330
+ .fw-root .fw-round-button { appearance: none; display: grid; place-items: center; width: 32px; height: 32px; padding: 0; border-radius: 50%; border: 1px solid var(--fw-border); background: var(--fw-well); color: var(--fw-text); cursor: pointer; transition: background-color var(--motion-control), transform var(--motion-control); }
421
331
  .fw-root .fw-round-button:hover:not(:disabled) { background: var(--fw-hover); }
422
332
  .fw-root .fw-round-button:active:not(:disabled) { transform: scale(.94); }
423
333
  .fw-root .fw-speed { position: relative; display: inline-flex; }
424
334
  .fw-root .fw-speed::after { content: ""; position: absolute; top: 50%; right: 11px; width: 5px; height: 5px; border: solid var(--fw-muted); border-width: 0 1.5px 1.5px 0; transform: translateY(-70%) rotate(45deg); pointer-events: none; }
425
- .fw-root .fw-speed select { appearance: none; height: 28px; padding: 0 28px 0 12px; border-radius: var(--radius-md); border: 1px solid var(--fw-border); background: var(--fw-well); font-size: 14px; font-variant-numeric: tabular-nums; cursor: pointer; }
335
+ .fw-root .fw-speed select { appearance: none; height: 28px; padding: 0 28px 0 12px; border-radius: var(--radius-control); border: 1px solid var(--fw-border); background: var(--fw-well); font-size: 14px; font-variant-numeric: tabular-nums; cursor: pointer; }
426
336
  .fw-root .fw-overview { position: relative; flex: 1 1 160px; min-width: 120px; height: 28px; }
427
337
  .fw-root .fw-overview-marks { position: absolute; left: 0; right: 0; top: 8px; height: 12px; border-radius: var(--radius-tag); background: var(--fw-well); overflow: hidden; box-shadow: inset 0 0 0 1px var(--fw-border); }
428
338
  .fw-root .fw-ov { position: absolute; top: 2px; bottom: 2px; border-radius: 1px; }
@@ -437,41 +347,12 @@
437
347
  .fw-root .fw-zoom { display: inline-flex; gap: 0; }
438
348
  .fw-root .fw-sr-only { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
439
349
 
440
- @keyframes fw-spin { to { transform: rotate(360deg); } }
441
- /* Motion verbs. Grow: an arriving bar extends from its start. Unfold: an arriving row's cells slide in, staggered by
442
- --fw-enter-delay. Settle: a bar that just finished eases its edge and lands its end label. */
443
- @keyframes fw-grow { from { clip-path: inset(0 100% 0 0 round 4px); } }
444
- @keyframes fw-unfold { from { opacity: 0; translate: 0 -4px; } }
445
- @keyframes fw-land { from { opacity: 0; translate: -6px 0; } }
446
- @keyframes fw-settle { 40% { filter: brightness(1.3); } }
447
- @keyframes fw-pulse { 50% { opacity: .35; } }
448
- @keyframes fw-arrive { from { opacity: 0; transform: translateY(4px); } }
449
- @keyframes fw-arrive-above { from { opacity: 0; transform: translateY(calc(-100% - 4px)); } }
450
350
 
451
351
  @media (prefers-reduced-motion: reduce) {
452
- .fw-root *, .fw-root *::before { animation: none !important; transition: none !important; }
453
- .fw-root .fw-spinner { border-top-color: color-mix(in oklab, var(--fw-accent) 30%, transparent); border-right-color: var(--fw-accent); }
352
+ .fw-root *, .fw-root *::before { animation: none !important; transition: opacity var(--duration-fast) !important; }
353
+ .fw-root [data-motion="essential"], .fw-root [data-motion="essential"]::before { animation: tangle-breathe var(--motion-ambient) infinite !important; }
454
354
  }
455
355
 
456
- @container (max-width: 820px) {
457
- .fw-root .fw-zoom, .fw-root .fw-time { display: none; }
458
- }
459
- @container (max-width: 560px) {
460
- .fw-root .fw-row .fw-icon-tile { display: none; }
461
- .fw-root .fw-label { padding-left: calc(8px + var(--depth) * 12px); gap: 4px; }
462
- .fw-root .fw-guides { left: 16px; width: calc(var(--depth) * 12px); background-size: 12px 100%; }
463
- .fw-root .fw-row .fw-detail { display: none; }
464
- }
465
- @media (max-width: 640px) {
466
- .fw-root .fw-summary { grid-template-columns: repeat(3, minmax(0, 1fr)); }
467
- .fw-root .fw-summary > div:nth-child(4) { border-left: 0; }
468
- .fw-root .fw-summary > div:nth-child(n + 4) { border-top: 1px solid var(--fw-border); }
469
- .fw-root .fw-search-input { width: 140px; }
470
- .fw-root .fw-branch-controls, .fw-root .fw-zoom { display: none; }
471
- .fw-root .fw-heading, .fw-root .fw-playback { padding-left: 12px; padding-right: 12px; }
472
- }
473
-
474
- /* The agent timeline is one quiet surface: no inner rules; controls and axis sit above the lanes. */
475
356
  .fw-root-agents .fw-heading { border-bottom: 0; padding: 20px 24px 12px; }
476
357
  .fw-root-agents .fw-axis-top { border-bottom: 0; }
477
358
  .fw-root-agents .fw-axis-top .fw-axis-label { border-right-color: transparent; }
@@ -479,33 +360,57 @@
479
360
  .fw-root-agents .fw-body { padding-bottom: 12px; }
480
361
  .fw-root-agents .fw-view-agents .fw-label { padding-left: 24px; }
481
362
  .fw-root-agents .fw-zoom, .fw-root-agents .fw-time { display: none; }
482
- /* The agent view keeps scrubbing on a hairline track; the lanes above already show the activity. */
483
363
  .fw-root-agents .fw-overview-marks { top: 13px; height: 2px; box-shadow: none; }
484
364
  .fw-root-agents .fw-overview-marks > * { display: none; }
485
- .fw-root .fw-segmented { display: inline-flex; padding: 4px; border-radius: var(--radius-md); background: var(--fw-well); border: 1px solid var(--fw-border); }
486
- .fw-root .fw-segmented button { appearance: none; border: 0; background: transparent; height: 26px; min-width: 36px; padding: 0 12px; border-radius: var(--radius-sm); font-size: 14px; color: var(--fw-muted); cursor: pointer; font-variant-numeric: tabular-nums; transition: background-color 120ms var(--fw-ease), color 120ms var(--fw-ease); }
365
+ .fw-root .fw-segmented { display: inline-flex; padding: 4px; border-radius: var(--radius-control); background: var(--fw-well); border: 1px solid var(--fw-border); }
366
+ .fw-root .fw-segmented button { appearance: none; border: 0; background: transparent; height: 26px; min-width: 36px; padding: 0 12px; border-radius: var(--radius-tag); font-size: 14px; color: var(--fw-muted); cursor: pointer; font-variant-numeric: tabular-nums; transition: background-color var(--motion-control), color var(--motion-control); }
487
367
  .fw-root .fw-segmented button[aria-checked="true"] { background: var(--fw-surface); color: var(--fw-text); box-shadow: var(--shadow-card), 0 0 0 1px var(--fw-border); }
488
368
 
489
- /* Below 900 px the agent's name takes its own line above a full-width lane: nothing truncates and the bar keeps the width.
490
- (At 768 px a 28% column cut 15 of 17 names on a real 17-agent run.) */
491
- @container (max-width: 899px) {
492
- /* The root is the queried container, so the variables go on its children. */
493
- /* In a 40 px row: the name on 0-20 px, a 16 px bar on 20-36 px. */
494
- .fw-root-agents > * { --fw-label-col: 0px; --fw-bar-shift: 8px; --fw-bar-h: 16px; }
495
- .fw-root-agents .fw-view-agents .fw-names { flex-direction: row; align-items: baseline; gap: 8px; min-width: 0; }
496
- /* The label leaves the grid flow, so the lane must claim the track column itself. */
497
- .fw-root-agents .fw-view-agents .fw-lane { grid-column: 2; }
498
- /* Elbows pass behind the name, not through its letters. A transform or layout containment would make each row
499
- its own stacking context and trap the name under the connector layer, so stacked rows place by top instead. */
500
- .fw-root-agents .fw-view-agents .fw-row { transform: none; top: var(--row-y); contain: style; }
501
- /* The name row is a strip across the lane on the row's own plane, so gridlines, pauses and connectors show only in the bar row. */
502
- .fw-root-agents .fw-view-agents .fw-label { position: absolute; z-index: 3; top: 0; left: 0; right: 0; height: 20px; padding: 0 calc(var(--fw-end-w, 0px) + 12px) 0 var(--fw-pad); gap: 8px; align-items: center; pointer-events: none; overflow: hidden;
503
- background: linear-gradient(var(--fw-tint, transparent) 0 0), var(--fw-surface); }
504
- /* The duration and its badges take the strip's right end, past the name, so they never sit on a bar or across a connector. */
505
- .fw-root-agents .fw-view-agents .fw-end { z-index: 4; top: 0; height: 20px; left: auto; right: 4px; }
506
- .fw-root-agents .fw-view-agents .fw-mark { top: calc(50% + var(--fw-bar-shift) - var(--fw-bar-h) / 2 - 2px); height: calc(var(--fw-bar-h) + 4px); }
507
- }
508
- /* On a phone the name row holds the name and the duration; the outcome is the dot's color and the counts are the card's. */
509
- @container (max-width: 479px) {
510
- .fw-root-agents .fw-view-agents .fw-subtitle { display: none; }
369
+ .fw-root .fw-legend button { display: inline-flex; align-items: center; gap: 8px; border: 0; padding: 0; background: transparent; color: inherit; cursor: pointer; }
370
+ .fw-root .fw-legend button[aria-pressed="true"] { color: var(--fw-text); text-decoration: underline; text-underline-offset: 4px; }
371
+ .fw-root .fw-summary button { appearance: none; background: transparent; border: 0; padding: 0; cursor: pointer; color: inherit; }
372
+ .fw-root .fw-key > summary { display: none; }
373
+ .fw-root .fw-key > summary svg { width: 12px; height: 12px; margin-left: 6px; }
374
+ .fw-root .fw-key[open] > summary svg { transform: rotate(90deg); }
375
+ .fw-root .fw-row-state { display: none; font-size: 12px; font-weight: 600; }
376
+ .fw-root .fw-error-rug { position: relative; height: 10px; margin-left: var(--fw-label-col); overflow: hidden; }
377
+ .fw-root .fw-error-tick { position: absolute; width: 2px; height: 8px; background: var(--fw-failed); left: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px); }
378
+ .fw-root .fw-error-tick[data-unlocated] { left: auto; right: calc(var(--error-index) * 3px); }
379
+ .fw-root .fw-quiet { color: var(--fw-amber); font-size: 12px; font-weight: 500; white-space: nowrap; }
380
+ .fw-root [data-activity="quiet"] :is(.fw-agent-dot, .fw-spinner) { animation: none !important; }
381
+ .fw-root .fw-quiet-tail { position: absolute; top: calc(50% + var(--fw-bar-shift) - var(--fw-bar-h) / 2); height: var(--fw-bar-h); left: calc(var(--fw-pad) + (var(--s) - var(--fw-v0)) * var(--fw-k) * 1px); width: calc(max(0, var(--fw-t) - var(--s)) * var(--fw-k) * 1px); background: repeating-linear-gradient(-45deg, var(--fw-hatch) 0 1px, transparent 1px 5px); pointer-events: none; }
382
+ .fw-root .fw-now { background: color-mix(in oklab, var(--fw-text) 28%, transparent); }
383
+ .fw-root .fw-card-error { margin-top: 8px; color: var(--fw-failed); font-size: 13px; line-height: 18px; }
384
+ .fw-root .fw-card .fw-agent-dot:not([data-outcome]) { background: transparent; box-shadow: inset 0 0 0 1.5px var(--fw-muted); }
385
+ @container flow-waterfall (max-width: 599px) {
386
+ .fw-root > * { --fw-label-col: 0px; --fw-bar-shift: 8px; --fw-bar-h: 8px; --fw-row-h: 44px; }
387
+ .fw-root .fw-summary { grid-template-columns: repeat(3, minmax(0, 1fr)); }
388
+ .fw-root .fw-summary > div { padding: 8px; }
389
+ .fw-root .fw-summary > div:nth-child(4) { border-left: 0; }
390
+ .fw-root .fw-summary > div:nth-child(n+4) { border-top: 1px solid var(--fw-border); }
391
+ .fw-root :is(.fw-zoom, .fw-branch-controls, .fw-resize, .fw-links, .fw-gridlayer, .fw-marker-layer, .fw-subtitle, .fw-badge, .fw-icon-tile) { display: none; }
392
+ .fw-root .fw-row-state { display: inline; }
393
+ .fw-root .fw-key > summary { display: flex; align-items: center; min-height: 32px; cursor: pointer; }
394
+ .fw-root .fw-key:not([open]) .fw-legends { display: none; }
395
+ .fw-root .fw-heading { padding: 12px 8px; }
396
+ .fw-root .fw-title-line { flex-wrap: wrap; }
397
+ .fw-root .fw-search-input { width: 120px; }
398
+ .fw-root .fw-axis-label { border: 0; padding: 0; }
399
+ .fw-root .fw-row { transform: none; top: var(--row-y); contain: style; }
400
+ .fw-root .fw-label { position: absolute; z-index: 3; height: 20px; top: 0; left: 0; right: 0; padding: 0 calc(var(--fw-end-w, 0px) + 8px) 0 calc(8px + var(--depth) * 12px); border: 0; gap: 4px; background: transparent; pointer-events: none; }
401
+ .fw-root .fw-view-agents .fw-names { flex-direction: row; font-size: 14px; line-height: 20px; }
402
+ .fw-root .fw-view-agents :is(.fw-subtitle, .fw-seg-label) { display: none; }
403
+ .fw-root .fw-view-agents .fw-label { padding-left: calc(8px + var(--depth) * 12px); }
404
+ .fw-root .fw-lane { grid-column: 2; }
405
+ .fw-root .fw-end { top: 0; height: 20px; left: auto; right: 4px; }
406
+ .fw-root .fw-duration { font-size: 12px; }
407
+ .fw-root .fw-time { display: block; position: absolute; top: -12px; left: clamp(0px, var(--fw-progress), calc(100% - 48px)); background: var(--fw-surface); }
408
+ .fw-root .fw-playback { position: relative; padding: 12px 8px 8px; }
409
+ .fw-root .fw-overview { margin-top: 12px; }
511
410
  }
411
+
412
+ @container flow-waterfall (max-width: 599px) { .fw-root .fw-tick:nth-child(4) { transform: translate(calc(-100% - 4px), -50%); } }
413
+
414
+ .fw-root.fw-fill-agent .fw-view-agents .fw-seg-failed[data-model] { background: var(--model-fill); --fw-seg-ink: light-dark(var(--fw-surface), var(--bg-root)); }
415
+
416
+ .fw-root.fw-root.fw-root-agents[data-selection] .fw-row:not([aria-selected="true"]) .fw-seg { opacity: var(--on); background: color-mix(in oklab, var(--fw-surface) 80%, var(--tone)); --fw-seg-ink: var(--fw-text); }