/* Flow Debugger swimlane — mirrors the design mockup's values through the kit's CSS variables (theme.css light defaults; site.css dark overrides) so dark mode follows automatically. Border #d2d2cb = --rule-strong, selected 2px #2f5fd0 on #eef2fc = --accent on --active-bg. */ .fg-wrap { overflow: auto; border: 1px solid var(--rule); border-radius: 4px; background: var(--bs-body-bg, #fff); } .fg-svg { display: block; font-family: var(--bs-body-font-family, system-ui, sans-serif); } /* Lanes. The gutter labels are emitted as raw markup (SvgText — Razor reserves the tag), so they carry no CSS-isolation scope attribute and must be reached through ::deep. */ .fg-lane-bg { fill: transparent; } .fg-lane-bg.alt { fill: var(--rule); fill-opacity: 0.25; } .fg-lane-rule { stroke: var(--rule); stroke-width: 1; } .fg-wrap ::deep .fg-lane-label { fill: var(--ink-soft); font-size: 0.7rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; } .fg-wrap ::deep .fg-lane-sublabel{ fill: var(--ink-faint); font-size: 0.65rem; font-family: var(--mono); } .fg-wrap ::deep .fg-lane-prefix { fill: var(--ink-faint); font-size: 0.65rem; font-family: var(--mono); } .fg-wrap ::deep .fg-empty { fill: var(--ink-faint); font-size: 0.85rem; font-style: italic; } /* Nodes */ .fg-node { cursor: pointer; outline: none; } .fg-node .fg-node-box { fill: var(--bs-body-bg, #fff); stroke: var(--rule-strong); stroke-width: 1; } .fg-node:hover .fg-node-box, .fg-node:focus-visible .fg-node-box { stroke: var(--accent); } .fg-node.selected .fg-node-box { stroke: var(--accent); stroke-width: 2; fill: var(--active-bg); } .fg-node.predicted .fg-node-box { stroke-dasharray: 4 3; } .fg-node.input-dependent .fg-node-box { stroke-dasharray: 2 3; } .fg-node.ghost { opacity: 0.45; } .fg-node.ghost .fg-node-box { fill: var(--rule); fill-opacity: 0.35; } .fg-node.failed .fg-node-box, .fg-node.timed-out .fg-node-box { stroke: var(--bs-danger, #c0392b); } .fg-node.not-traced .fg-node-box { stroke-dasharray: 1 3; } .fg-node.parked .fg-node-box { stroke: var(--warn, var(--bs-warning, #e8920c)); } /* Node body: HTML inside a foreignObject so text is measured by the browser. */ .fg-body { box-sizing: border-box; width: 100%; height: 100%; padding: 6px 8px 5px; display: flex; flex-direction: column; gap: 1px; overflow: hidden; line-height: 1.2; color: var(--ink); font-family: var(--bs-body-font-family, system-ui, sans-serif); } .fg-head { display: flex; justify-content: space-between; align-items: baseline; gap: 6px; } .fg-eyebrow { color: var(--ink-faint); font-size: 0.6rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .fg-duration { color: var(--ink-soft); font-size: 0.6rem; font-family: var(--mono); white-space: nowrap; flex-shrink: 0; } .fg-title { font-size: 0.78rem; font-weight: 600; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .fg-subtitle { color: var(--ink-soft); font-size: 0.66rem; margin-top: auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .fg-node.failed .fg-eyebrow, .fg-node.timed-out .fg-eyebrow { color: var(--bs-danger, #c0392b); } /* Outbound nodes (API call / database / notify): the folded outcome as a status pill, then the call mode. Kit status tokens first (dark mode follows via site.css), Bootstrap's as fallback. */ .fg-status { margin-top: auto; display: flex; align-items: center; gap: 4px; min-width: 0; } .fg-pill { display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0; font-size: 0.6rem; font-weight: 500; line-height: 1.3; padding: 0 6px; border-radius: 999px; white-space: nowrap; color: var(--ink-soft); background: var(--rule); } .fg-pill::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; } .fg-pill.ok { color: var(--ok, var(--bs-success, #2f9e44)); background: var(--ok-bg, rgba(var(--bs-success-rgb, 25, 135, 84), 0.12)); } .fg-pill.pending { color: var(--warn, var(--bs-warning, #e8920c)); background: var(--warn-bg, rgba(var(--bs-warning-rgb, 255, 193, 7), 0.15)); } .fg-pill.parked { color: var(--warn, var(--bs-warning, #e8920c)); background: var(--warn-bg, rgba(var(--bs-warning-rgb, 255, 193, 7), 0.15)); } .fg-pill.failed { color: var(--bad, var(--bs-danger, #e03131)); background: var(--bad-bg, rgba(var(--bs-danger-rgb, 220, 53, 69), 0.12)); } .fg-mode { color: var(--ink-soft); font-size: 0.66rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* A pending pill pulses while the page keeps refreshing outcomes after a Live run. */ .fg-node.pending .fg-pill { animation: fg-pulse 1.6s ease-in-out infinite; } @keyframes fg-pulse { 0%, 100% { opacity: 1; box-shadow: 0 0 0 0 var(--warn-bg, rgba(var(--bs-warning-rgb, 255, 193, 7), 0.15)); } 50% { opacity: 0.6; box-shadow: 0 0 0 4px var(--warn-bg, rgba(var(--bs-warning-rgb, 255, 193, 7), 0.15)); } } @media (prefers-reduced-motion: reduce) { .fg-node.pending .fg-pill { animation: none; } } /* Connectors */ .fg-edge { fill: none; stroke: var(--ink-faint); stroke-width: 1.5; } .fg-edge.boundary { stroke: var(--accent); } .fg-edge.external { stroke-dasharray: 5 4; } /* Async branch (cached call / notification): same dash as the echo edge, its own class — the run carried on without waiting. Spawn: solid line with an ordinary arrowhead plus a bolt badge sitting on the riser (a trigger cascade) — the bolt is NOT the arrowhead, that read as a broken arrow. */ .fg-edge.async { stroke-dasharray: 5 4; } .fg-edge.spawn { stroke: var(--ink-soft); } .fg-edge.predicted { stroke-opacity: 0.5; stroke-dasharray: 3 3; } .fg-arrowhead { fill: var(--ink-faint); } .fg-arrowhead.accent { fill: var(--accent); } .fg-spawn-badge-bg { fill: var(--bs-body-bg, #fff); stroke: var(--ink-soft); stroke-width: 1; } .fg-spawn-bolt { fill: var(--ink-soft); } .fg-wrap ::deep .fg-edge-tag { fill: var(--ink-faint); font-size: 0.6rem; font-family: var(--mono); }