@typeonce/effect-machine-devtools 0.27.1 → 0.29.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.
@@ -5,16 +5,8 @@ import type {
5
5
  State as VisualizationState,
6
6
  Transition as VisualizationTransition
7
7
  } from "../../MachineDocument.js"
8
- import { type InputField, projectInputSchema } from "./input-form.js"
9
8
  import { stateLabel, triggerLabel } from "./visualizer-model.js"
10
9
 
11
- export interface ChartField {
12
- readonly key: string
13
- readonly label: string
14
- readonly type: string
15
- readonly required: boolean
16
- }
17
-
18
10
  export interface ChartActivity {
19
11
  readonly id: string
20
12
  readonly kind: VisualizationActivity["type"]
@@ -29,7 +21,6 @@ export interface ChartNode {
29
21
  readonly children: ReadonlyArray<string>
30
22
  readonly active: boolean
31
23
  readonly initial: boolean
32
- readonly fields: ReadonlyArray<ChartField>
33
24
  readonly activities: ReadonlyArray<ChartActivity>
34
25
  }
35
26
 
@@ -69,53 +60,6 @@ export interface ChartModel {
69
60
  readonly initials: ReadonlyArray<ChartInitial>
70
61
  }
71
62
 
72
- const literalType = (value: string | number | boolean | null): string =>
73
- typeof value === "string" ? JSON.stringify(value) : String(value)
74
-
75
- const fieldType = (field: InputField): string => {
76
- switch (field._tag) {
77
- case "String":
78
- return field.format ?? "string"
79
- case "Number":
80
- return field.integer ? "integer" : "number"
81
- case "Boolean":
82
- return "boolean"
83
- case "Enum":
84
- return field.values.map(literalType).join(" | ")
85
- case "Literal":
86
- return literalType(field.value)
87
- case "Object":
88
- return "object"
89
- case "Array":
90
- return `${fieldType(field.item)}[]`
91
- case "Union":
92
- return field.alternatives.map(fieldType).join(" | ")
93
- case "Unsupported":
94
- return "unknown"
95
- }
96
- }
97
-
98
- const stateFields = (state: VisualizationState): ReadonlyArray<ChartField> => {
99
- if (state.valueSchema === null) return []
100
- const projected = projectInputSchema(state.valueSchema)
101
- if (projected._tag !== "Object") {
102
- return [{
103
- key: "value",
104
- label: projected.title ?? "value",
105
- type: fieldType(projected),
106
- required: true
107
- }]
108
- }
109
- return projected.fields
110
- .filter(({ key }) => key !== "_tag")
111
- .map(({ field, key, required }) => ({
112
- key,
113
- label: field.title ?? key,
114
- type: fieldType(field),
115
- required
116
- }))
117
- }
118
-
119
63
  const activityLabel = (activity: VisualizationActivity): string => {
120
64
  switch (activity.type) {
121
65
  case "machine":
@@ -177,7 +121,6 @@ export const makeChartModel = (document: VisualizationDocument): ChartModel => {
177
121
  children: [...state.children],
178
122
  active: active.has(state.path),
179
123
  initial: initialPaths.has(state.path),
180
- fields: stateFields(state),
181
124
  activities: state.activityIds.flatMap((id): ReadonlyArray<ChartActivity> => {
182
125
  const activity = activities.get(id)
183
126
  return activity === undefined ? [] : [{ id, kind: activity.type, label: activityLabel(activity) }]
@@ -6,8 +6,7 @@ import {
6
6
  type LaidOutChart,
7
7
  type LaidOutChartNode,
8
8
  layoutChart,
9
- maxVisibleActivities,
10
- maxVisibleFields
9
+ maxVisibleActivities
11
10
  } from "./chart-layout.js"
12
11
  import { makeChartModel } from "./chart-model.js"
13
12
 
@@ -130,7 +129,7 @@ const statusLabel = (active: boolean, initial: boolean): string => {
130
129
  const status = (active: boolean, initial: boolean): HTMLSpanElement => {
131
130
  const dot = element(
132
131
  "span",
133
- `chart-state-status${active ? " is-active" : ""}${initial ? " is-initial" : ""}`
132
+ `chart-state-status${active ? " is-active" : ""}`
134
133
  )
135
134
  dot.dataset.initial = String(initial)
136
135
  dot.setAttribute("aria-label", statusLabel(active, initial))
@@ -147,26 +146,8 @@ const stateContent = (layout: LaidOutChartNode, stateStatus: HTMLElement): Docum
147
146
  heading.append(identity)
148
147
  fragment.append(heading)
149
148
 
150
- if (layout.node.fields.length > 0) {
151
- const fields = element("div", "chart-state-section")
152
- fields.append(element("div", "chart-section-label", "value"))
153
- layout.node.fields.slice(0, maxVisibleFields).forEach((field) => {
154
- const row = element("div", "chart-field-row")
155
- row.append(
156
- element("span", "chart-field-name", `${field.label}${field.required ? "" : "?"}`),
157
- element("span", "chart-field-type", field.type)
158
- )
159
- fields.append(row)
160
- })
161
- if (layout.node.fields.length > maxVisibleFields) {
162
- fields.append(more(layout.node.fields.length - maxVisibleFields))
163
- }
164
- fragment.append(fields)
165
- }
166
-
167
149
  if (layout.node.activities.length > 0) {
168
150
  const activities = element("div", "chart-state-section")
169
- activities.append(element("div", "chart-section-label", "invokes"))
170
151
  layout.node.activities.slice(0, maxVisibleActivities).forEach((activity) => {
171
152
  const row = element("div", `chart-activity-row chart-activity-${activity.kind}`)
172
153
  row.append(
@@ -314,7 +295,19 @@ const render = (
314
295
  const directionArrow = svgElement("path")
315
296
  directionArrow.setAttribute("d", "M 1 1 L 7 4 L 1 7 z")
316
297
  directionMarker.append(directionArrow)
317
- definitions.append(marker, directionMarker)
298
+ const initialMarker = svgElement("marker")
299
+ initialMarker.id = "chart-initial-arrow"
300
+ initialMarker.setAttribute("viewBox", "0 0 8 8")
301
+ initialMarker.setAttribute("refX", "7")
302
+ initialMarker.setAttribute("refY", "4")
303
+ initialMarker.setAttribute("markerWidth", "7")
304
+ initialMarker.setAttribute("markerHeight", "7")
305
+ initialMarker.setAttribute("markerUnits", "userSpaceOnUse")
306
+ initialMarker.setAttribute("orient", "auto")
307
+ const initialArrow = svgElement("path")
308
+ initialArrow.setAttribute("d", "M 1 1 L 7 4 L 1 7 z")
309
+ initialMarker.append(initialArrow)
310
+ definitions.append(marker, directionMarker, initialMarker)
318
311
  svg.append(definitions)
319
312
  const nodesLayer = element("div", "chart-nodes")
320
313
  const labelsLayer = element("div", "chart-labels")
@@ -447,7 +440,10 @@ const render = (
447
440
  const route = chartEdgePathData(laidOut.points)
448
441
  casing.setAttribute("d", route)
449
442
  visible.setAttribute("d", route)
450
- visible.setAttribute("marker-end", "url(#chart-arrow)")
443
+ visible.setAttribute(
444
+ "marker-end",
445
+ laidOut.kind === "initial" ? "url(#chart-initial-arrow)" : "url(#chart-arrow)"
446
+ )
451
447
  const hit = svgElement("path", "chart-edge-hit")
452
448
  hit.setAttribute("d", route)
453
449
  group.append(casing, visible)
@@ -3,6 +3,19 @@ import { mountVisualizer } from "./visualizer.js"
3
3
 
4
4
  let selectedKey: string | undefined
5
5
 
6
+ export const machineIndexScrollLeft = (
7
+ scrollLeft: number,
8
+ viewportWidth: number,
9
+ selectedLeft: number,
10
+ selectedWidth: number
11
+ ): number => {
12
+ if (selectedLeft < scrollLeft) return selectedLeft
13
+ const selectedRight = selectedLeft + selectedWidth
14
+ return selectedRight > scrollLeft + viewportWidth
15
+ ? selectedRight - viewportWidth
16
+ : scrollLeft
17
+ }
18
+
6
19
  const createElement = <Tag extends keyof HTMLElementTagNameMap>(
7
20
  tag: Tag,
8
21
  className?: string,
@@ -36,6 +49,7 @@ const statusLabel = (result: MachineResult): string => {
36
49
  }
37
50
 
38
51
  export const mountMachineIndex = (root: HTMLElement, snapshot: RegistrySnapshot): void => {
52
+ const previousScrollLeft = root.querySelector<HTMLElement>(".machine-index")?.scrollLeft ?? 0
39
53
  const results = [...snapshot.results].sort((left, right) => resultLabel(left).localeCompare(resultLabel(right)))
40
54
  if (selectedKey === undefined || !results.some((result) => result.key === selectedKey)) {
41
55
  selectedKey = results[0]?.key
@@ -78,4 +92,13 @@ export const mountMachineIndex = (root: HTMLElement, snapshot: RegistrySnapshot)
78
92
 
79
93
  shell.append(index, view)
80
94
  root.replaceChildren(shell)
95
+ const selected = index.querySelector<HTMLElement>(".machine-row.is-selected")
96
+ index.scrollLeft = selected === null
97
+ ? previousScrollLeft
98
+ : machineIndexScrollLeft(
99
+ previousScrollLeft,
100
+ index.clientWidth,
101
+ selected.offsetLeft,
102
+ selected.offsetWidth
103
+ )
81
104
  }
@@ -0,0 +1,141 @@
1
+ import { Machine } from "@typeonce/effect-machine"
2
+ import { Effect, Schema } from "effect"
3
+
4
+ const ReplicationStates = Machine.states({
5
+ Connecting: {},
6
+ IdentifyingSource: {},
7
+ ReadingServerInfo: {},
8
+ ReadingSlot: {},
9
+ CreatingSlot: {},
10
+ CopyingSnapshot: {},
11
+ CatchingUp: {},
12
+ ApplyingChanges: {},
13
+ Ready: {},
14
+ SessionUnavailable: {},
15
+ Stopping: {},
16
+ Stopped: {},
17
+ Failed: {}
18
+ })
19
+
20
+ const ReplicationEvents = Machine.events(
21
+ Schema.TaggedUnion({
22
+ Retry: {},
23
+ SessionUnavailable: {},
24
+ StopRequested: {}
25
+ })
26
+ )
27
+
28
+ const operation = (): Effect.Effect<void, string> => Effect.succeed(undefined)
29
+
30
+ export const sharedTerminalRoutingMachine = Machine.make({
31
+ id: "shared-terminal-routing",
32
+ states: ReplicationStates.states,
33
+ events: ReplicationEvents,
34
+ initial: (to) => to.Connecting()
35
+ }).handle({
36
+ Connecting: {
37
+ invoke: (from) =>
38
+ from.effect("connect", operation)
39
+ .onDone((to) => to.full.IdentifyingSource())
40
+ .onFailure((to) => to.full.Failed()),
41
+ on: {
42
+ SessionUnavailable: (to) => to.full.SessionUnavailable(),
43
+ StopRequested: (to) => to.full.Stopping()
44
+ }
45
+ },
46
+ IdentifyingSource: {
47
+ invoke: (from) =>
48
+ from.effect("identify-source", operation)
49
+ .onDone((to) => to.full.ReadingServerInfo())
50
+ .onFailure((to) => to.full.Failed()),
51
+ on: {
52
+ SessionUnavailable: (to) => to.full.SessionUnavailable(),
53
+ StopRequested: (to) => to.full.Stopping()
54
+ }
55
+ },
56
+ ReadingServerInfo: {
57
+ invoke: (from) =>
58
+ from.effect("read-server-info", operation)
59
+ .onDone((to) => to.full.ReadingSlot())
60
+ .onFailure((to) => to.full.Failed()),
61
+ on: {
62
+ SessionUnavailable: (to) => to.full.SessionUnavailable(),
63
+ StopRequested: (to) => to.full.Stopping()
64
+ }
65
+ },
66
+ ReadingSlot: {
67
+ invoke: (from) =>
68
+ from.effect("read-slot", operation)
69
+ .onDone((to) => to.full.CreatingSlot())
70
+ .onFailure((to) => to.full.Failed()),
71
+ on: {
72
+ SessionUnavailable: (to) => to.full.SessionUnavailable(),
73
+ StopRequested: (to) => to.full.Stopping()
74
+ }
75
+ },
76
+ CreatingSlot: {
77
+ invoke: (from) =>
78
+ from.effect("create-slot", operation)
79
+ .onDone((to) => to.full.CopyingSnapshot())
80
+ .onFailure((to) => to.full.Failed()),
81
+ on: {
82
+ SessionUnavailable: (to) => to.full.SessionUnavailable(),
83
+ StopRequested: (to) => to.full.Stopping()
84
+ }
85
+ },
86
+ CopyingSnapshot: {
87
+ invoke: (from) =>
88
+ from.effect("copy-snapshot", operation)
89
+ .onDone((to) => to.full.CatchingUp())
90
+ .onFailure((to) => to.full.Failed()),
91
+ on: {
92
+ SessionUnavailable: (to) => to.full.SessionUnavailable(),
93
+ StopRequested: (to) => to.full.Stopping()
94
+ }
95
+ },
96
+ CatchingUp: {
97
+ invoke: (from) =>
98
+ from.effect("catch-up", operation)
99
+ .onDone((to) => to.full.ApplyingChanges())
100
+ .onFailure((to) => to.full.Failed()),
101
+ on: {
102
+ SessionUnavailable: (to) => to.full.SessionUnavailable(),
103
+ StopRequested: (to) => to.full.Stopping()
104
+ }
105
+ },
106
+ ApplyingChanges: {
107
+ invoke: (from) =>
108
+ from.effect("apply-changes", operation)
109
+ .onDone((to) => to.full.Ready())
110
+ .onFailure((to) => to.full.Failed()),
111
+ on: {
112
+ SessionUnavailable: (to) => to.full.SessionUnavailable(),
113
+ StopRequested: (to) => to.full.Stopping()
114
+ }
115
+ },
116
+ Ready: {
117
+ on: {
118
+ SessionUnavailable: (to) => to.full.SessionUnavailable(),
119
+ StopRequested: (to) => to.full.Stopping()
120
+ }
121
+ },
122
+ SessionUnavailable: {
123
+ on: {
124
+ Retry: (to) => to.full.Connecting(),
125
+ StopRequested: (to) => to.full.Stopping()
126
+ }
127
+ },
128
+ Stopping: {
129
+ invoke: (from) =>
130
+ from.effect("stop-session", operation)
131
+ .onDone((to) => to.full.Stopped())
132
+ .onFailure((to) => to.full.Failed())
133
+ },
134
+ Stopped: {},
135
+ Failed: {
136
+ on: {
137
+ Retry: (to) => to.full.Connecting(),
138
+ StopRequested: (to) => to.full.Stopping()
139
+ }
140
+ }
141
+ })
@@ -455,8 +455,10 @@ button {
455
455
 
456
456
  .chart-state {
457
457
  position: absolute;
458
- display: block;
459
- padding: 16px;
458
+ display: flex;
459
+ flex-direction: column;
460
+ justify-content: center;
461
+ padding: 12px;
460
462
  border: 1px solid #30363f;
461
463
  border-radius: 4px;
462
464
  color: #d9dce1;
@@ -493,7 +495,6 @@ button {
493
495
 
494
496
  .chart-state-heading,
495
497
  .chart-state-identity,
496
- .chart-field-row,
497
498
  .chart-activity-row {
498
499
  display: flex;
499
500
  min-width: 0;
@@ -523,18 +524,7 @@ button {
523
524
  box-shadow: 0 0 0 3px rgb(117 167 255 / 10%);
524
525
  }
525
526
 
526
- .chart-state-status.is-initial {
527
- border-color: #d9a441;
528
- background: #d9a441;
529
- box-shadow: 0 0 0 3px rgb(217 164 65 / 12%);
530
- }
531
-
532
- .chart-state-status.is-active.is-initial {
533
- box-shadow: 0 0 0 2px #14181d, 0 0 0 4px var(--accent);
534
- }
535
-
536
527
  .chart-state-name,
537
- .chart-field-name,
538
528
  .chart-activity-name {
539
529
  min-width: 0;
540
530
  overflow: hidden;
@@ -546,8 +536,6 @@ button {
546
536
  font-size: 12px;
547
537
  }
548
538
 
549
- .chart-section-label,
550
- .chart-field-type,
551
539
  .chart-activity-kind,
552
540
  .chart-more {
553
541
  color: #7e8792;
@@ -555,28 +543,20 @@ button {
555
543
  line-height: 1.3;
556
544
  }
557
545
 
558
- .chart-section-label,
559
546
  .chart-activity-kind {
560
547
  text-transform: uppercase;
561
548
  }
562
549
 
563
550
  .chart-state-section {
564
551
  width: min(300px, 100%);
565
- margin-top: 11px;
566
- padding-top: 8px;
552
+ margin-top: 8px;
553
+ padding-top: 7px;
567
554
  border-top: 1px solid #242a31;
568
555
  }
569
556
 
570
- .chart-section-label {
571
- margin-bottom: 4px;
572
- letter-spacing: 0.06em;
573
- }
574
-
575
- .chart-field-row,
576
557
  .chart-activity-row {
577
558
  display: grid;
578
- min-height: 22px;
579
- grid-template-columns: minmax(0, 1fr) auto;
559
+ min-height: 20px;
580
560
  gap: 10px;
581
561
  }
582
562
 
@@ -584,13 +564,11 @@ button {
584
564
  grid-template-columns: auto minmax(0, 1fr);
585
565
  }
586
566
 
587
- .chart-field-name,
588
567
  .chart-activity-name {
589
568
  color: #c4c9d0;
590
569
  font-size: 10px;
591
570
  }
592
571
 
593
- .chart-field-type,
594
572
  .chart-activity-kind {
595
573
  flex: 0 1 auto;
596
574
  overflow: hidden;
@@ -646,15 +624,15 @@ button {
646
624
  }
647
625
 
648
626
  .chart-more {
649
- min-height: 18px;
650
- padding-top: 3px;
627
+ min-height: 16px;
628
+ padding-top: 2px;
651
629
  }
652
630
 
653
631
  .chart-initial {
654
632
  position: absolute;
655
633
  border-radius: 50%;
656
- background: #9ba3ae;
657
- box-shadow: 0 0 0 3px rgb(155 163 174 / 8%);
634
+ background: #b0965d;
635
+ box-shadow: 0 0 0 2px rgb(176 150 93 / 10%);
658
636
  pointer-events: none;
659
637
  }
660
638
 
@@ -705,7 +683,8 @@ button {
705
683
  }
706
684
 
707
685
  .chart-edge-initial .chart-edge-line {
708
- stroke: #89929e;
686
+ stroke: #9f8a5c;
687
+ stroke-width: 1.2;
709
688
  }
710
689
 
711
690
  .chart-edge-hit {
@@ -716,7 +695,8 @@ button {
716
695
  }
717
696
 
718
697
  #chart-arrow path,
719
- #chart-direction path {
698
+ #chart-direction path,
699
+ #chart-initial-arrow path {
720
700
  fill: context-stroke;
721
701
  }
722
702
 
@@ -1613,7 +1593,7 @@ button {
1613
1593
  gap: 12px;
1614
1594
  }
1615
1595
 
1616
- .input-field-identity,
1596
+ .input-field-contract,
1617
1597
  .input-constraints {
1618
1598
  display: flex;
1619
1599
  min-width: 0;
@@ -1622,6 +1602,12 @@ button {
1622
1602
  gap: 6px;
1623
1603
  }
1624
1604
 
1605
+ .input-field-contract {
1606
+ flex: 0 0 auto;
1607
+ justify-content: flex-end;
1608
+ margin-left: auto;
1609
+ }
1610
+
1625
1611
  .input-label,
1626
1612
  .input-required,
1627
1613
  .input-optional {
@@ -1681,6 +1667,18 @@ button {
1681
1667
  color: #d7a5a2;
1682
1668
  }
1683
1669
 
1670
+ .value-shape-block {
1671
+ margin: 0;
1672
+ padding: 12px;
1673
+ border: 1px solid var(--line-soft);
1674
+ color: #b8c1ce;
1675
+ background: var(--surface-raised);
1676
+ font: 10px/1.55 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
1677
+ overflow-x: auto;
1678
+ tab-size: 2;
1679
+ white-space: pre;
1680
+ }
1681
+
1684
1682
  .trace-topology {
1685
1683
  display: grid;
1686
1684
  gap: 10px;
@@ -271,13 +271,12 @@ const renderContractField = (
271
271
  if (omit.has(label)) return null
272
272
  const row = createElement("div", "input-contract-field")
273
273
  const heading = createElement("div", "input-field-heading")
274
- const identity = createElement("div", "input-field-identity")
275
- identity.append(
276
- createElement("span", "input-label", label),
274
+ const contract = createElement("div", "input-field-contract")
275
+ contract.append(
277
276
  createElement("span", "input-field-type", inputType(field)),
278
277
  createElement("span", required ? "input-required" : "input-optional", required ? "required" : "optional")
279
278
  )
280
- heading.append(identity)
279
+ heading.append(createElement("span", "input-label", label), contract)
281
280
  row.append(heading)
282
281
  const constraints = inputConstraints(field)
283
282
  if (constraints.length > 0) {
@@ -350,6 +349,55 @@ const contractSummary = (schema: InputSchema | null): string | null => {
350
349
  ) + (fields.length > 3 ? ` · +${fields.length - 3}` : "")
351
350
  }
352
351
 
352
+ type ValueShape = string | ReadonlyArray<ValueShape> | { readonly [key: string]: ValueShape }
353
+
354
+ const typePreview = (field: InputField): string => {
355
+ switch (field._tag) {
356
+ case "String":
357
+ return field.format === undefined ? "string" : `string (${field.format})`
358
+ case "Number":
359
+ return field.integer ? "integer" : "number"
360
+ case "Boolean":
361
+ return "boolean"
362
+ case "Enum":
363
+ return field.values.map(String).join(" | ")
364
+ case "Literal":
365
+ return String(field.value)
366
+ case "Object":
367
+ return "object"
368
+ case "Array":
369
+ return `${typePreview(field.item)}[]`
370
+ case "Union":
371
+ return [...new Set(field.alternatives.map(typePreview))].join(" | ")
372
+ case "Unsupported":
373
+ return "unknown"
374
+ }
375
+ }
376
+
377
+ const fieldValueShape = (field: InputField): ValueShape => {
378
+ if (field._tag === "Object") {
379
+ return Object.fromEntries(
380
+ field.fields
381
+ .filter(({ key }) => key !== "_tag")
382
+ .map(({ key, required, field }) => [required ? key : `${key}?`, fieldValueShape(field)])
383
+ )
384
+ }
385
+ if (field._tag === "Array") return [fieldValueShape(field.item)]
386
+ return typePreview(field)
387
+ }
388
+
389
+ export const valueShape = (schema: InputSchema): ValueShape => fieldValueShape(projectInputSchema(schema))
390
+
391
+ const renderValueShape = (title: string, schema: InputSchema): HTMLElement => {
392
+ const section = createElement("section", "inspector-section value-shape-section")
393
+ section.append(inspectionSection(title))
394
+ const code = createElement("code", undefined, JSON.stringify(valueShape(schema), null, 2))
395
+ const block = createElement("pre", "value-shape-block")
396
+ block.append(code)
397
+ section.append(block)
398
+ return section
399
+ }
400
+
353
401
  const triggerName = (trigger: Trigger): string => {
354
402
  switch (trigger.type) {
355
403
  case "event":
@@ -518,6 +566,13 @@ export const renderVisualizer = (
518
566
  }
519
567
  inspectorContent.append(header)
520
568
 
569
+ if (
570
+ inspection.state.valueSchema !== null &&
571
+ contractSummary(inspection.state.valueSchema) !== null
572
+ ) {
573
+ inspectorContent.append(renderValueShape("Value", inspection.state.valueSchema))
574
+ }
575
+
521
576
  if (inspection.outgoing.length > 0) {
522
577
  const transitions = createElement("section", "inspector-section")
523
578
  transitions.append(inspectionSection("Transitions", inspection.outgoing.length))
@@ -1 +0,0 @@
1
- :root{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark;color:#e8eaed;font-synthesis:none;text-rendering:optimizelegibility;--surface:#0f1114;--surface-raised:#14171b;--line:#292d34;--line-soft:#20242a;--muted:#8e949e;--accent:#75a7ff;--accent-bg:#4b7dff2e;--orange:#f0a35b;background:#0b0c0e;font-family:Inter,ui-sans-serif,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}*{box-sizing:border-box}html,body,#app{min-width:320px;height:100%;margin:0}body{background:#0b0c0e}.devtools-shell{flex-direction:column;width:100vw;height:100vh;display:flex;overflow:hidden}.machine-index{border-bottom:1px solid var(--line);scrollbar-width:thin;background:#0d0f12;flex:none;width:100%;min-width:0;min-height:48px;padding:0;display:flex;overflow:auto hidden}.machine-row{border:0;border-right:1px solid var(--line-soft);color:#c9cdd2;text-align:left;cursor:pointer;-webkit-user-select:none;user-select:none;background:0 0;flex:none;align-items:center;gap:8px;width:auto;min-width:148px;max-width:280px;min-height:48px;padding:0 16px;display:flex;position:relative}.machine-row:hover,.machine-row:focus-visible{color:#fff;background:#171a1f;outline:none}.machine-row.is-selected{color:#fff;background:#4b7dff1f}.machine-row.is-selected:after{background:var(--accent);content:"";height:2px;position:absolute;bottom:0;left:0;right:0}.machine-row-status{background:#646b75;border-radius:50%;flex:none;width:6px;height:6px}.machine-row-status.status-ready{background:#67c894}.machine-row-status.status-partial{background:var(--orange)}.machine-row-status.status-error{background:#df6964}.machine-row-label,.machine-row-file{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.machine-row-label{font:600 12px/1.3 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.machine-row-file{display:none}.machine-index-empty{color:#646b75;padding:16px;font:11px/1.4 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.machine-view{flex:1;min-width:0;min-height:0;overflow:hidden}.registry-empty,.connection-failure{color:#7f8690;align-content:start;gap:7px;min-height:100vh;padding:28px;font:12px/1.6 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;display:grid}.registry-empty strong{color:#d4d7dc;font-size:13px}.connection-failure{color:#e4b4b1}button{font:inherit}.app-shell,.workspace{width:100%;height:100%;min-height:0}.workspace{position:relative}.chart-panel{flex-direction:column;width:100%;min-width:0;height:100%;display:flex;position:relative;overflow:hidden}.diagnostics{border-bottom:1px solid var(--line);background:#121418}.diagnostic{color:#aeb4bd;flex-wrap:wrap;align-items:center;gap:8px;min-height:36px;padding:8px 16px;font-size:11px;display:flex}.diagnostic+.diagnostic{border-top:1px solid var(--line-soft)}.badge-warning{color:#e9c89f;background:#f0a35b21}.badge-error{color:#f2aaa7;background:#e0595424}.toolbar{border-bottom:1px solid var(--line);justify-content:space-between;align-items:center;gap:2px;min-height:46px;padding:7px 16px;display:flex}.toolbar-actions,.zoom-controls,.runtime-summary{align-items:center;display:flex}.toolbar-actions{gap:2px}.zoom-controls{z-index:5;border:1px solid var(--line-soft);background:#0d0f12f0;border-radius:4px;flex:none;gap:1px;padding:2px;position:absolute;bottom:14px;left:14px;box-shadow:0 8px 28px #00000052}.chart-panel.has-walkthrough .zoom-controls{bottom:88px}.zoom-button{min-width:28px;padding-inline:7px}.zoom-value{color:#aeb4bd;min-width:46px;font:10px/1 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.runtime-summary{color:#7f8690;gap:7px;font:11px/1 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.runtime-dot{background:#555c65;border-radius:50%;width:6px;height:6px}.runtime-dot.has-snapshot{background:var(--accent)}.toolbar-button{color:var(--muted);cursor:pointer;background:0 0;border:0;border-radius:4px;padding:6px 9px;font-size:12px}.toolbar-button:not(:disabled):hover,.toolbar-button:not(:disabled):focus-visible{color:#fff;background:#1b1e23;outline:none}.toolbar-button:disabled{color:#50545c;cursor:default}.toolbar-button[aria-pressed=true]{color:#dce8ff;background:#4167a547}.analysis-button{align-items:center;gap:6px;display:inline-flex}.analysis-button[hidden]{display:none}.analysis-button-count{color:#9da6b3;text-align:center;background:#20242a;border-radius:8px;min-width:17px;padding:2px 5px;font:10px/1 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.topology-chart{flex-direction:column;flex:1;min-height:0;font:13px/1.5 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;display:flex}.topology-empty{color:#7f8690;gap:7px;max-width:420px;margin:26px 8px;font:12px/1.6 Inter,ui-sans-serif,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;display:grid}.topology-empty strong{color:#d4d7dc;font:600 13px/1.4 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.chart-host,.chart-viewport{width:100%;height:100%}.chart-host{flex:1;min-height:0;position:relative}.chart-viewport{overscroll-behavior:contain;cursor:grab;touch-action:pan-x pan-y;overflow:auto}.chart-viewport.is-panning,.chart-viewport.is-panning .chart-state,.chart-viewport.is-panning .chart-edge-hit,.chart-viewport.is-panning .chart-edge-label{cursor:grabbing;-webkit-user-select:none;user-select:none}.chart-stage{min-width:100%;min-height:100%;position:relative}.chart-canvas{transform-origin:0 0;position:absolute;top:0;left:0}.chart-regions,.chart-edges,.chart-nodes,.chart-labels{position:absolute;inset:0}.chart-regions{z-index:0;pointer-events:none}.chart-edges{z-index:1;pointer-events:none;overflow:visible}.chart-nodes{z-index:2;pointer-events:none}.chart-labels{z-index:3;pointer-events:none}.chart-compound{background:#121519b8;border:1px solid #272c33;border-radius:5px;position:absolute}.chart-compound-parallel{background:#14181ebd;border-color:#303845}.chart-unconnected-region{background:#0e101394;border:1px dashed #353b44;border-radius:5px;position:absolute}.chart-unconnected-region-label{color:#727984;letter-spacing:.05em;text-transform:uppercase;font:9px/1 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;position:absolute;top:17px;left:24px}.chart-state{color:#d9dce1;text-align:left;cursor:pointer;pointer-events:auto;-webkit-user-select:none;user-select:none;background:#14181d;border:1px solid #30363f;border-radius:4px;padding:16px;display:block;position:absolute;overflow:hidden;box-shadow:0 4px 14px #0000002e}.chart-state-compound,.chart-state-parallel{background:#171b21;border-color:#353c47}.chart-state-final{border-color:#445066}.chart-state-history,.chart-state-choice{background:#18171d}.chart-state:not(:disabled):hover,.chart-state:focus-visible{color:#fff;border-color:#596475;outline:none}.chart-state-heading,.chart-state-identity,.chart-field-row,.chart-activity-row{align-items:center;min-width:0;display:flex}.chart-state-heading{justify-content:space-between;gap:12px}.chart-state-identity{gap:8px}.chart-state-status{border:1px solid #686f78;border-radius:50%;flex:none;width:7px;height:7px}.chart-state-status.is-active{border-color:var(--accent);background:var(--accent);box-shadow:0 0 0 3px #75a7ff1a}.chart-state-status.is-initial{background:#d9a441;border-color:#d9a441;box-shadow:0 0 0 3px #d9a4411f}.chart-state-status.is-active.is-initial{box-shadow:0 0 0 2px #14181d, 0 0 0 4px var(--accent)}.chart-state-name,.chart-field-name,.chart-activity-name{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.chart-state-name{font-size:12px}.chart-section-label,.chart-field-type,.chart-activity-kind,.chart-more{color:#7e8792;font-size:9px;line-height:1.3}.chart-section-label,.chart-activity-kind{text-transform:uppercase}.chart-state-section{border-top:1px solid #242a31;width:min(300px,100%);margin-top:11px;padding-top:8px}.chart-section-label{letter-spacing:.06em;margin-bottom:4px}.chart-field-row,.chart-activity-row{grid-template-columns:minmax(0,1fr) auto;gap:10px;min-height:22px;display:grid}.chart-activity-row{grid-template-columns:auto minmax(0,1fr)}.chart-field-name,.chart-activity-name{color:#c4c9d0;font-size:10px}.chart-field-type,.chart-activity-kind{text-overflow:ellipsis;white-space:nowrap;flex:0 auto;overflow:hidden}.chart-activity-kind{min-width:max-content;color:var(--chart-activity-color,#77a990);text-overflow:clip;overflow:visible}.chart-activity-process,.chart-edge-activity-process,.badge-activity-process{--chart-activity-color:#a98978;--chart-activity-border:#51433c;--chart-activity-background:#191513}.chart-activity-effect,.chart-edge-activity-effect,.badge-activity-effect{--chart-activity-color:#7f9f8c;--chart-activity-border:#42564b;--chart-activity-background:#131815}.chart-activity-timer,.chart-edge-activity-timer,.badge-activity-timer{--chart-activity-color:#aaa07c;--chart-activity-border:#57503c;--chart-activity-background:#191812}.chart-activity-stream,.chart-edge-activity-stream,.badge-activity-stream{--chart-activity-color:#7898a2;--chart-activity-border:#405259;--chart-activity-background:#121719}.chart-activity-machine,.chart-edge-activity-machine,.badge-activity-machine{--chart-activity-color:#958aa8;--chart-activity-border:#4d4658;--chart-activity-background:#17151a}.chart-more{min-height:18px;padding-top:3px}.chart-initial{pointer-events:none;background:#9ba3ae;border-radius:50%;position:absolute;box-shadow:0 0 0 3px #9ba3ae14}.chart-runtime-target{color:#858d98;letter-spacing:.04em;text-transform:uppercase;pointer-events:none;-webkit-user-select:none;user-select:none;background:#111419;border:1px dashed #555d68;border-radius:3px;place-items:center;font:9px/1 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;display:grid;position:absolute}.chart-edge-casing,.chart-edge-line,.chart-edge-direction,.chart-edge-hit{fill:none}.chart-edge-parent-origin{fill:#101318;stroke:#89929e;stroke-width:1.8px;vector-effect:non-scaling-stroke}.chart-edge-casing{stroke:#0b0c0e;stroke-width:5.5px;vector-effect:non-scaling-stroke}.chart-edge-line,.chart-edge-direction{stroke:#626b77;stroke-width:1.4px;vector-effect:non-scaling-stroke}.chart-edge-direction{pointer-events:none}.chart-edge-initial .chart-edge-line{stroke:#89929e}.chart-edge-hit{stroke:#0000;stroke-width:14px;cursor:pointer;pointer-events:stroke}#chart-arrow path,#chart-direction path{fill:context-stroke}.chart-edge-label{color:#c0c7d0;text-overflow:ellipsis;white-space:nowrap;cursor:pointer;pointer-events:auto;-webkit-user-select:none;user-select:none;background:#101318;border:1px solid #2c333c;border-radius:3px;padding:4px 7px;font:10px/1 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;position:absolute;overflow:hidden}.chart-edge-group.chart-edge-parent-child .chart-edge-line,.chart-edge-group.chart-edge-parent-child .chart-edge-direction,.chart-edge-group.chart-edge-parent-child .chart-edge-parent-origin{stroke:#9187a7}.chart-edge-group.chart-edge-parent-child .chart-edge-parent-origin{fill:#1b1820}.chart-edge-label-parent-child{color:#aea5c1;background:#17151b;border-color:#4d465a;box-shadow:inset 2px 0 #6f6681}.chart-edge-label:not(:disabled):hover,.chart-edge-label:focus-visible,.chart-edge-group.is-hovered+.chart-edge-label{color:#fff;border-color:#53657e;outline:none}.chart-edge-label-always,.chart-edge-label-choice,.chart-edge-label-done{color:#d4b4e9}.chart-edge-group.is-hovered .chart-edge-line,.chart-edge-group.is-hovered .chart-edge-direction,.chart-edge-group.is-hovered .chart-edge-parent-origin{stroke:#7c9ed2;stroke-width:1.8px}.chart-edge-group.is-walkthrough-unavailable .chart-edge-line{stroke:#6e675b;stroke-dasharray:5 5}.chart-edge-group.chart-transition-runtime .chart-edge-line{stroke-dasharray:5 5}.chart-edge-group.chart-edge-trigger-always .chart-edge-line,.chart-edge-group.chart-edge-trigger-choice .chart-edge-line,.chart-edge-group.chart-edge-trigger-done .chart-edge-line,.chart-edge-group.chart-edge-trigger-invoke .chart-edge-line{stroke-dasharray:7 5}.chart-edge-group.chart-edge-activity-process .chart-edge-line,.chart-edge-group.chart-edge-activity-effect .chart-edge-line,.chart-edge-group.chart-edge-activity-timer .chart-edge-line,.chart-edge-group.chart-edge-activity-stream .chart-edge-line,.chart-edge-group.chart-edge-activity-machine .chart-edge-line,.chart-edge-group.chart-edge-activity-process .chart-edge-direction,.chart-edge-group.chart-edge-activity-effect .chart-edge-direction,.chart-edge-group.chart-edge-activity-timer .chart-edge-direction,.chart-edge-group.chart-edge-activity-stream .chart-edge-direction,.chart-edge-group.chart-edge-activity-machine .chart-edge-direction,.chart-edge-group.chart-edge-activity-process .chart-edge-parent-origin,.chart-edge-group.chart-edge-activity-effect .chart-edge-parent-origin,.chart-edge-group.chart-edge-activity-timer .chart-edge-parent-origin,.chart-edge-group.chart-edge-activity-stream .chart-edge-parent-origin,.chart-edge-group.chart-edge-activity-machine .chart-edge-parent-origin{stroke:var(--chart-activity-color)}.chart-edge-label.chart-edge-activity-process,.chart-edge-label.chart-edge-activity-effect,.chart-edge-label.chart-edge-activity-timer,.chart-edge-label.chart-edge-activity-stream,.chart-edge-label.chart-edge-activity-machine{border-color:var(--chart-activity-border);color:var(--chart-activity-color);background:var(--chart-activity-background)}.chart-edge-group.is-incoming .chart-edge-line,.chart-edge-group.is-incoming .chart-edge-direction,.chart-edge-group.is-incoming .chart-edge-parent-origin{stroke:#ed6a70;stroke-width:2px}.chart-edge-group.is-outgoing .chart-edge-line,.chart-edge-group.is-outgoing .chart-edge-direction,.chart-edge-group.is-outgoing .chart-edge-parent-origin{stroke:#6eb6dc;stroke-width:2px}.chart-edge-group.is-selected .chart-edge-line,.chart-edge-group.is-selected .chart-edge-direction,.chart-edge-group.is-selected .chart-edge-parent-origin{stroke:#8ab5ff;stroke-width:2.5px}.chart-edge-group.is-walkthrough-available .chart-edge-line,.chart-edge-group.is-walkthrough-available .chart-edge-direction{stroke:#d5aa57;stroke-width:2px}.chart-edge-label.is-hovered{color:#dbe8ff;border-color:#526783}.chart-edge-label.is-walkthrough-unavailable{color:#8d8679;background:#151412;border-color:#4e4a43}.chart-edge-label.is-incoming{color:#ffd0d2;background:#251416;border-color:#a9484e}.chart-edge-label.is-outgoing{color:#c5e8f7;background:#111c22;border-color:#47758d}.chart-edge-label.is-selected{color:#fff;background:#18253a;border-color:#719ce0}.chart-edge-label.is-walkthrough-available{color:#f0d39a;background:#211b12;border-color:#8d713c}.chart-state.is-selected,.chart-compound.is-selected{background:#2c4b7a6b;border-color:#5f88ca}.chart-state.is-related-to,.chart-compound.is-related-to,.chart-state.is-hover-target,.chart-compound.is-hover-target{background:#30708c3d;border-color:#4f829a}.chart-state.is-related-from,.chart-compound.is-related-from,.chart-state.is-hover-source,.chart-compound.is-hover-source{background:#97303942;border-color:#a94b52}.chart-state.is-related-from.is-related-to,.chart-compound.is-related-from.is-related-to{background:linear-gradient(135deg,#97303947 0 50%,#30708c42 50% 100%);border-color:#6e708f}.chart-viewport.is-simulating .chart-state:disabled,.chart-viewport.is-simulating .chart-edge-label:disabled,.chart-viewport.is-simulating .chart-edge-hit{cursor:default}.chart-viewport.is-simulating .chart-edge-label:disabled,.chart-viewport.is-simulating .chart-edge-hit{pointer-events:none}.chart-viewport.is-simulating .chart-edge-label.is-walkthrough-available,.chart-viewport.is-simulating .chart-edge-label.is-walkthrough-unavailable,.chart-viewport.is-simulating .chart-edge-group.is-walkthrough-available .chart-edge-hit,.chart-viewport.is-simulating .chart-edge-group.is-walkthrough-unavailable .chart-edge-hit{cursor:pointer}.chart-viewport.is-simulating .chart-edge-group.is-walkthrough-available .chart-edge-hit,.chart-viewport.is-simulating .chart-edge-group.is-walkthrough-unavailable .chart-edge-hit{pointer-events:stroke}.chart-loading,.chart-layout-error{color:#7f8690;gap:7px;max-width:440px;margin:28px;font:11px/1.6 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;display:grid}.chart-layout-error{color:#d58d89}.chart-layout-error strong{color:#efb0ac}.inspector-eyebrow,.card-title,.card-flags,.branch-main{align-items:center;gap:6px;display:flex}.badge{color:#a8afb8;letter-spacing:.02em;white-space:nowrap;background:#20242a;border-radius:3px;align-items:center;min-height:18px;padding:2px 6px;font:600 10px/1 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;display:inline-flex}.badge-active{color:#b9d2ff;background:#4b7dff38}.badge-trigger{color:#b9d2ff;background:#4b7dff29}.badge-condition{color:#d9b8ef;background:#ac68e026}.badge-activity{color:#a8ddc4;background:#4eb58324}.badge-activity-process,.badge-activity-effect,.badge-activity-timer,.badge-activity-stream,.badge-activity-machine{color:var(--chart-activity-color);background:var(--chart-activity-background)}.badge-count{color:#858c96;background:0 0}.inspector{z-index:10;background:#0f1114f7;border:1px solid #333943;border-radius:6px;width:min(460px,100% - 28px);min-width:0;padding:0;position:absolute;top:60px;bottom:14px;right:14px;overflow:hidden;box-shadow:0 18px 60px #0000007a}.inspector[hidden]{display:none}.inspector-close{z-index:1;color:#8e949e;cursor:pointer;background:0 0;border:0;border-radius:3px;padding:6px 9px;font-size:11px;position:absolute;top:11px;right:12px}.inspector-content{width:100%;height:100%;padding:48px 24px 24px;overflow:auto}.inspector-close:hover,.inspector-close:focus-visible{color:#fff;background:#20242a;outline:none}.failure-shell{color:#d6d8dc;background:#0b0c0e;align-content:start;gap:12px;min-height:100vh;padding:32px;display:grid}.failure-kind{color:#f2aaa7;letter-spacing:.08em;text-transform:uppercase;font:600 10px/1.2 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.failure-shell h1{margin:0;font:600 18px/1.4 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.failure-shell pre{color:#e4b4b1;white-space:pre-wrap;background:#151112;border-left:2px solid #a54743;max-width:900px;margin:4px 0 0;padding:14px 16px;font:12px/1.6 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;overflow:auto}.failure-shell p{color:#7f8690;margin:0;font-size:12px}.inspector-empty{max-width:420px;color:var(--muted)}.inspector-empty-kind{color:var(--orange);letter-spacing:.08em;text-transform:uppercase;font:600 10px/1.2 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.inspector-empty h2,.inspector-header h2{color:#f1f2f4;overflow-wrap:anywhere;margin:9px 0 0;font:600 18px/1.4 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.inspector-title-row,.inspector-state-title{align-items:center;min-width:0;display:flex}.inspector-title-row{justify-content:space-between;gap:16px}.inspector-state-title{gap:9px}.inspector-title-row h2{min-width:0;margin:0}.inspector-empty p,.section-empty{color:var(--muted);font-size:12px;line-height:1.6}.inspector-header{border-bottom:1px solid var(--line);padding-bottom:24px}.analysis-summary{color:#7f8690;margin:12px 0 0;font-size:12px;line-height:1.6}.state-annotations{margin-top:18px}.state-annotations p{color:#aeb4bd;margin:7px 0 0;font-size:12px;line-height:1.6}.state-annotations .state-documentation{color:#7f8690;white-space:pre-wrap;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.breadcrumbs{flex-wrap:wrap;align-items:center;gap:5px;min-width:0;margin-bottom:16px;display:flex}.breadcrumb-separator{color:#555c65}.state-link{color:#9bbfff;overflow-wrap:anywhere;text-align:left;cursor:pointer;background:0 0;border:0;max-width:100%;padding:0;font:11px/1.5 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.state-link:hover,.state-link:focus-visible{color:#d5e4ff;outline:none;text-decoration:underline}.metadata{grid-template-columns:minmax(74px,max-content) minmax(0,1fr);gap:8px 16px;margin:20px 0 0;font-size:11px;display:grid}.metadata dt{color:#727983}.metadata dd{color:#d2d5da;overflow-wrap:anywhere;min-width:0;margin:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.inspector-section{margin-top:28px}.section-heading{justify-content:space-between;align-items:center;margin-bottom:11px;display:flex}.section-heading h3{color:#c9cdd2;margin:0;font-size:12px;font-weight:650}.section-count{color:#737a84;font:11px/1 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.inspection-card{border:1px solid var(--line-soft);background:var(--surface-raised);margin-top:8px}.analysis-card{padding-bottom:12px}.analysis-card .state-link{color:#e2e4e7;font-size:12px;font-weight:600}.analysis-message,.analysis-qualification{margin:0;padding:0 12px;font-size:11px;line-height:1.6}.analysis-message{color:#b9bec6}.analysis-qualification{color:#747c87;margin-top:5px}.card-header{justify-content:space-between;align-items:center;gap:12px;min-height:42px;padding:10px 12px;display:flex}.card-title{min-width:0}.card-title strong{color:#e2e4e7;overflow-wrap:anywhere;font:600 12px/1.4 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.branch-list{border-top:1px solid var(--line-soft)}.branch-row{padding:11px 12px 13px}.branch-row+.branch-row{border-top:1px solid var(--line-soft)}.branch-main{min-width:0}.branch-arrow{color:var(--accent)}.branch-target{color:#d7d9dc;overflow-wrap:anywhere;font:11px/1.4 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.branch-row .metadata,.transition-card .card-metadata,.activity-card .metadata,.incoming-card .metadata{margin-top:10px}.incoming-card{padding-bottom:12px}.incoming-card .metadata{padding:0 12px}.transition-card .card-metadata{padding:0 12px 10px}.branch-updates{grid-template-columns:minmax(74px,max-content) minmax(0,1fr);gap:8px 16px;margin-top:9px;font-size:11px;display:grid}.branch-updates-label{color:#727983}.branch-updates .state-link+.state-link{grid-column:2}.activity-card{padding-bottom:12px}.activity-card .metadata{padding:0 12px}.walkthrough-dock{border-top:1px solid var(--line);background:#0d0f12fa;flex:none;grid-template-rows:auto auto;gap:6px;min-height:0;max-height:76px;padding:8px 14px 10px;font:10px/1.4 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;display:grid;box-shadow:0 -12px 32px #0003}.walkthrough-dock[hidden]{display:none}.walkthrough-heading,.walkthrough-identity,.walkthrough-timeline{align-items:center;min-width:0;display:flex}.walkthrough-heading{color:#d4d7dc;justify-content:space-between;gap:12px}.walkthrough-identity{gap:10px}.walkthrough-status,.walkthrough-hint{color:#777f8a}.walkthrough-hint{font-size:9px}.walkthrough-timeline{scrollbar-width:thin;gap:5px;overflow-x:auto}.walkthrough-step{border:1px solid var(--line-soft);color:#aeb4bd;cursor:pointer;white-space:nowrap;background:#14171b;border-radius:3px;flex:none;padding:5px 8px}.walkthrough-step:hover,.walkthrough-step:focus-visible{color:#fff;border-color:#53657e;outline:none}.walkthrough-step.is-current{color:#d8e6ff;background:#2c4b7a61;border-color:#5f88ca}.transition-picker{z-index:20;background:#0f1114fa;border:1px solid #4e4535;border-radius:5px;grid-template-rows:auto minmax(0,1fr);width:min(380px,100vw - 20px);max-height:min(420px,100vh - 68px);display:grid;position:fixed;overflow:hidden;box-shadow:0 18px 60px #00000085}.transition-picker[hidden]{display:none}.transition-picker-header,.transition-picker-choice-main{align-items:center;min-width:0;display:flex}.transition-picker-header{border-bottom:1px solid var(--line-soft);justify-content:space-between;gap:10px;min-height:38px;padding:8px 10px 8px 12px}.transition-picker-header strong{color:#d8dadd;text-overflow:ellipsis;white-space:nowrap;font:600 11px/1.4 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;overflow:hidden}.transition-picker-close{color:#7f8690;cursor:pointer;background:0 0;border:0;border-radius:3px;padding:4px 6px;font-size:10px}.transition-picker-close:hover,.transition-picker-close:focus-visible{color:#fff;background:#20242a;outline:none}.transition-picker-content{gap:5px;padding:7px;display:grid;overflow:auto}.transition-picker-choice{border:1px solid var(--line-soft);color:#aeb4bd;text-align:left;cursor:pointer;background:#14171b;border-radius:3px;gap:4px;min-width:0;padding:8px 9px;font:10px/1.4 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;display:grid}.transition-picker-choice:hover,.transition-picker-choice:focus-visible{background:#1d1912;border-color:#8d713c;outline:none}.transition-picker-choice-main{gap:7px}.transition-picker-choice-main strong,.transition-picker-choice-title,.transition-picker-choice-route,.transition-picker-choice-contract,.transition-picker-choice-decision,.transition-picker-choice-unavailable{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.transition-picker-choice-main strong{color:#efd18f}.transition-picker-choice-title{color:#d9b8ef}.transition-picker-choice-route{color:#777f8a}.transition-picker-choice-contract{color:#9ebce9}.transition-picker-choice-decision{color:#b69ac9}.transition-picker-choice-unavailable{color:#a38c6f}.transition-picker-choice.is-unavailable{opacity:.68;cursor:default}.input-contract-fields,.input-contract-field,.input-contract-children,.input-contract-alternatives{gap:9px;display:grid}.input-contract-fields{border:1px solid var(--line-soft);background:var(--surface-raised);padding:11px 12px}.input-contract-field+.input-contract-field{border-top:1px solid var(--line-soft);padding-top:9px}.input-contract-children,.input-contract-alternatives{border-top:1px solid var(--line-soft);border-left:1px solid var(--line-soft);margin-top:2px;padding:9px 0 0 12px}.input-contract .section-empty{margin:0}.input-field-heading{justify-content:space-between;align-items:center;gap:12px;display:flex}.input-field-identity,.input-constraints{flex-wrap:wrap;align-items:center;gap:6px;min-width:0;display:flex}.input-label,.input-required,.input-optional{font-size:11px}.input-label{color:#cfd3d9;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.input-field-type,.input-constraints span{color:#8294ad;font:10px/1.3 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.input-field-type{background:#4b7dff1f;border-radius:3px;padding:2px 5px}.input-constraints{margin-top:-1px}.input-constraints span+span:before{color:#4f5762;content:"·";margin-right:6px}.input-required{color:#737b86}.input-optional{color:#9199a4;align-items:center;gap:6px;display:inline-flex}.input-description,.input-unsupported{margin:0;font-size:11px;line-height:1.55}.input-description{color:#737b86}.input-unsupported{color:#d7a5a2}.trace-topology{gap:10px;display:grid}.trace-topology .section-heading{margin-bottom:1px}.trace-path-group,.trace-values,.trace-transitions{gap:7px;display:grid}.trace-path-group{grid-template-columns:78px minmax(0,1fr);align-items:start}.trace-paths{flex-wrap:wrap;gap:4px 10px;min-width:0;display:flex}.trace-label{color:#747c87;text-transform:uppercase;font-size:10px;line-height:1.5}.trace-card{padding-bottom:12px}.trace-card>.trace-path-group,.trace-card>.trace-values,.trace-card>.trace-transitions{margin:0 12px 10px}.trace-transition{flex-wrap:wrap;align-items:center;gap:6px;min-width:0;display:flex}.trace-transition+.trace-path-group{margin:0 0 4px}.trace-results{padding-bottom:24px}.trace-results>.trace-values{margin-top:12px}@media (width<=900px){.machine-row{min-width:132px;max-width:220px}.inspector{width:min(440px,100% - 16px);top:54px;bottom:8px;right:8px}}@media (width<=600px){.toolbar{align-items:flex-start;padding-inline:10px}.toolbar-actions{flex-wrap:wrap;justify-content:flex-end}.runtime-summary{padding-top:8px}.inspector-content{padding:48px 14px 18px}.inspector{top:84px}.zoom-controls{bottom:8px;left:8px}.chart-panel.has-walkthrough .zoom-controls{bottom:88px}}@media (prefers-reduced-motion:no-preference){.chart-state,.chart-edge-label,.toolbar-button{transition:color .12s,background-color .12s,border-color .12s}}