/* DALE-157: DALE-native workflow designer canvas. Global (not Blazor-scoped) on purpose — every node/
   pin/edge element is created dynamically by wwwroot/js/dale-designer.js, and Blazor's CSS isolation
   only rewrites elements it sees statically in a .razor template, so isolated CSS would silently not
   match anything JS creates. Reuses DALE's real --color-dale-* tokens, already defined globally by
   tailwind.output.css. */

/* Fills whatever height the page gives it (the designer page hands this the full viewport, next to
   the nav, same as the Studio-embedding designer route) rather than a fixed canvas height. */
.dd-designer-root { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.dd-body { display: flex; flex: 1; min-height: 0; }

/* Width is set in JS (sizePaletteToContent) to fit the widest category label by default, then the
   splitter can override it — flex-shrink:0 keeps that explicit width from being squeezed. */
.dd-palette { flex-shrink: 0; overflow-y: auto; padding: .5rem; }
.pal-search-wrap { padding: 0 .125rem .5rem; }
.pal-search-input {
  width: 100%; font-size: .78rem; padding: .35rem .5rem; border: 1px solid var(--dale-border-strong); border-radius: .375rem;
  font-family: inherit; box-sizing: border-box; background: var(--dale-surface); color: var(--dale-text);
}
.pal-search-input:focus { outline: none; border-color: var(--color-dale-600); }
.pal-group.hidden, .pal-item.hidden { display: none; }
.pal-group { margin-bottom: .125rem; }
/* Hairline between categories (not before the first one) — a plain top border on the group, so it
   separates one category's whole block (header + its body, open or closed) from the next. */
.pal-group + .pal-group { border-top: 1px solid var(--dale-border); margin-top: .25rem; padding-top: .25rem; }
.pal-cat-header {
  display: flex; align-items: center; justify-content: space-between; width: 100%; text-align: left;
  padding: .5rem .5rem; background: transparent; border: 0; font-size: .72rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .04em; color: var(--dale-text-muted); cursor: pointer; border-radius: .375rem;
  font-family: inherit; white-space: nowrap;
}
.pal-cat-header:hover { background: var(--dale-surface-hover); }
.pal-cat-header .chev { font-size: 18px; display: inline-block; transition: transform .15s; }
.pal-cat-header.open .chev { transform: rotate(90deg); }
.pal-cat-body { display: grid; grid-template-rows: 0fr; overflow: hidden; transition: grid-template-rows .22s ease; }
.pal-cat-body.open { grid-template-rows: 1fr; }
/* DALE-157: the open category's activities sit inside their own bordered box (Elsa Studio's own palette
   does the same) — reads more clearly as "this is a drag source", not just an indented list. Scoped to
   .open only (not a bare .pal-cat-inner rule): a border/padding on the always-present element survives
   the 0fr grid-collapse animation as a visible empty strip even while "closed", so the box itself must
   only exist in the DOM's styling while open, not just be clipped by the collapsed row's height. */
.pal-cat-body.open .pal-cat-inner {
  padding: .3rem; margin: 0 0 .25rem;
  border: 1px solid var(--dale-border); border-radius: .5rem; background: var(--dale-surface-2);
}
.pal-cat-inner { min-height: 0; overflow: hidden; }
.pal-item { display: flex; align-items: center; gap: .5rem; padding: .4rem .5rem .4rem 1.25rem; border-radius: .375rem; font-size: .8rem; cursor: grab; color: var(--dale-text); }
.pal-item:hover { background: var(--dale-surface-hover); }
.pal-dot { width: 8px; height: 8px; border-radius: 2px; background: var(--color-dale-600); display: inline-block; flex-shrink: 0; }

.splitter { width: 6px; flex-shrink: 0; cursor: col-resize; background: transparent; position: relative; }
.splitter::after { content: ''; position: absolute; top: 0; bottom: 0; left: 2px; width: 2px; background: transparent; }
.splitter:hover::after, .splitter.active::after { background: var(--color-dale-200); }

/* Panning the blank canvas is the default drag behavior (no modifier key) — grab cursor everywhere
   on this layer; nodes/pins/edges/the resize handle all declare their own more specific cursor
   (grab-to-move, crosshair, pointer, resize) which wins over this via the normal CSS cascade. */
.dd-canvas-outer {
  flex: 1; min-width: 0; position: relative; overflow: hidden; cursor: grab;
  background-image: linear-gradient(var(--dale-border) 1px, transparent 1px), linear-gradient(90deg, var(--dale-border) 1px, transparent 1px);
  background-size: 22px 22px; background-color: var(--dale-bg);
}
.dd-canvas-outer.panning { cursor: grabbing; }
/* Sits in canvasOuter (the viewport), not canvasContent (the pannable/zoomable layer) — stays
   centered on screen regardless of pan/zoom. DOM order (before dd-canvas-content) keeps it behind
   nodes/edges without needing a z-index. */
.dd-canvas-watermark {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 360px; height: auto; color: var(--dale-text-muted); opacity: .25; pointer-events: none;
}
.dd-canvas-content { position: absolute; top: 0; left: 0; width: 2000px; height: 1200px; transform-origin: 0 0; }
/* Minimap — sits in canvasOuter like the watermark, but needs an explicit z-index (unlike the
   watermark) since it must always paint ABOVE nodes/edges, not behind them. Width/height here must
   match the viewBox dimensions dale-designer.js renders into 1:1 (see MINIMAP_W/H), so a click's
   offsetX/Y need no extra scaling to land in the same coordinate space as the drawn content. */
.dd-minimap {
  position: absolute; top: 10px; right: 10px; width: 176px; height: 120px; z-index: 10;
  background: var(--dale-surface); border: 1px solid var(--dale-border-strong); border-radius: .5rem;
  box-shadow: var(--dale-shadow); cursor: pointer; opacity: .95;
}
.dd-minimap.hidden { display: none; }
.dd-minimap-node { fill: var(--dale-border-strong); }
.dd-minimap-node.sel { fill: var(--color-dale-600); }
.dd-minimap-viewport { fill: var(--color-dale-600); fill-opacity: .15; stroke: var(--color-dale-600); stroke-width: 1.5; pointer-events: none; }
.dd-edge-svg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; }
.dd-edge-del-icon {
  position: absolute; z-index: 20; width: 18px; height: 18px; border-radius: 50%; background: #dc2626; color: #fff;
  font-size: 14px; line-height: 18px; text-align: center; cursor: pointer; box-shadow: 0 1px 3px rgba(0,0,0,.3);
}

/* DALE-157 Phase 9: #dd-props-col is the resizable sidebar (the splitter now targets this, not #dd-props
   directly) — it stacks two SEPARATE DOM subtrees: #dd-props (this file, via innerHTML) and
   #dd-props-custom (Blazor's own component tree, for DALE's custom UIHint editors). Keeping them as
   siblings rather than nesting Blazor content inside #dd-props is deliberate: this file rebuilds
   #dd-props wholesale on every render, which would silently tear down any live Blazor component living
   inside it. */
/* Right panel is a flex column split into two vertical sections:
     • .dd-props        — grows to fill remaining height, scrolls INTERNALLY when its content overflows
     • .dd-props-custom — pinned to the bottom, intrinsic height so custom-input editors
                          (Employee mapping, Import/Export builder, Edit JavaScript, …) stay visible
                          regardless of how many regular inputs are stacked above.
   The parent's own overflow is hidden — scrolling happens inside .dd-props only, so the pinned
   custom section never scrolls off the panel. */
.dd-props-col { width: 260px; flex-shrink: 0; display: flex; flex-direction: column; overflow: hidden; }
.dd-props { flex: 1 1 auto; min-height: 0; overflow-y: auto; }

/* Palette/props columns' own scrollbars: unstyled, they render via the OS/browser default (always
   light chrome) regardless of DALE's dark-mode toggle, since nothing here sets `color-scheme` to
   tell the browser which native chrome to draw. Themed thumb/track (via the same vars everything
   else here already uses) fixes both engines instead. */
.dd-palette, .dd-props-col, .dd-props {
  scrollbar-width: thin;
  scrollbar-color: var(--dale-border-strong) transparent;
}
.dd-palette::-webkit-scrollbar, .dd-props-col::-webkit-scrollbar, .dd-props::-webkit-scrollbar { width: 8px; }
.dd-palette::-webkit-scrollbar-track, .dd-props-col::-webkit-scrollbar-track, .dd-props::-webkit-scrollbar-track { background: transparent; }
.dd-palette::-webkit-scrollbar-thumb, .dd-props-col::-webkit-scrollbar-thumb, .dd-props::-webkit-scrollbar-thumb {
  background-color: var(--dale-border-strong); border-radius: 9999px; border: 2px solid transparent; background-clip: padding-box;
}
.dd-palette::-webkit-scrollbar-thumb:hover, .dd-props-col::-webkit-scrollbar-thumb:hover, .dd-props::-webkit-scrollbar-thumb:hover {
  background-color: var(--dale-text-muted);
}
/* Bottom-pinned custom-input slot: intrinsic height inside the flex column, but capped at ~40% of
   the panel via max-height so a very tall custom editor (Employee mapping with many rows) can't
   starve .dd-props above it. Content taller than that scrolls inside this section. */
.dd-props-custom { flex: 0 0 auto; max-height: 40%; overflow-y: auto; border-top: 1px solid var(--dale-border); background: var(--dale-surface); }
.dd-props-custom:empty { border-top: none; }
.dd-kv-table { width: 100%; font-size: .78rem; border-collapse: collapse; }
.dd-kv-table td { padding: .3rem .25rem; border-bottom: 1px solid var(--dale-border); vertical-align: top; }
.dd-kv-key { font-family: ui-monospace, monospace; color: var(--dale-text-muted); width: 40%; word-break: break-word; }
.dd-kv-val { color: var(--dale-text-heading); word-break: break-word; }

/* Section headers ("Common", "Inputs", "Outputs", "Configured inputs", "Embedded activities") — a
   hairline above each (border-top, not border-bottom, so it reads as "closing off" the section
   that came before) plus a bigger/uppercase treatment so they stand out from the .dd-prop-label
   field labels they group. The first section (Common) still gets the same hairline, separating it
   from the name/type header above it, for a consistent look across every section boundary. */
.dd-prop-section {
  font-size: .74rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em;
  color: var(--dale-text-muted); margin: 1rem 0 .4rem; padding-top: .65rem;
  border-top: 1px solid var(--dale-border);
}
.dd-prop-label { display: block; font-size: .7rem; font-weight: 600; color: var(--dale-text-muted); margin: .5rem 0 .2rem; }
.dd-prop-input {
  width: 100%; font-size: .78rem; padding: .3rem .4rem; border: 1px solid var(--dale-border-strong); border-radius: .3rem;
  font-family: inherit; box-sizing: border-box; resize: vertical; background: var(--dale-surface); color: var(--dale-text);
}
.dd-prop-input:focus { outline: none; border-color: var(--color-dale-600); }
/* JS / Liquid code textareas: monospace + tighter line-height for a code feel; the wrap holds the
   textarea and a floating expand-to-modal button in the top-right corner. */
.dd-prop-code { font-family: var(--font-mono, ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace); font-size: .72rem; line-height: 1.35; }
.dd-prop-code-wrap { position: relative; }
.dd-prop-code-expand {
  position: absolute; top: .25rem; right: .25rem;
  width: 1.3rem; height: 1.3rem; padding: 0; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: .78rem; color: var(--dale-text-muted); background: var(--dale-surface); cursor: pointer;
  border: 1px solid var(--dale-border-strong); border-radius: .25rem;
}
.dd-prop-code-expand:hover { color: var(--dale-text); border-color: var(--color-dale-600); }
/* Full-width "Edit JavaScript" / "Edit Liquid" button for code-editor UIHint inputs — replaces the
   inline textarea entirely. First-line snippet on the right previews what's in there without opening. */
.dd-prop-code-open {
  display: flex; align-items: center; gap: .45rem; width: 100%;
  font: inherit; font-size: .78rem; padding: .38rem .5rem;
  border: 1px dashed var(--dale-border-strong); border-radius: .3rem;
  background: var(--dale-surface); color: var(--dale-text); cursor: pointer;
  text-align: left;
}
.dd-prop-code-open:hover { border-style: solid; border-color: var(--color-dale-600); }
.dd-prop-code-open-icon { font-size: .95rem; line-height: 1; color: var(--color-dale-600); }
.dd-prop-code-open-hint { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: var(--font-mono, ui-monospace, Menlo, monospace); color: var(--dale-text-muted); font-size: .7rem; }
/* Inbound HttpEndpoint Path input: base-URL prefix on its own line above the editable path field.
   A subtle background + rounded padding sets the prefix apart from the free-text row below without
   competing with it visually. */
.dd-inbound-path-prefix-line {
  padding: .3rem .5rem; border-radius: .3rem;
  background: var(--dale-surface-2, var(--dale-surface));
  border: 1px solid var(--dale-border);
}
/* <select class="dd-prop-input"> otherwise keeps the OS's native dropdown chrome (macOS in particular
   ignores the border/radius/background above entirely) — same fix as the app's own <select class="input-field">
   in Styles/tailwind.css: strip native appearance, draw a matching chevron. */
select.dd-prop-input {
  -webkit-appearance: none; -moz-appearance: none; appearance: none; cursor: pointer; padding-right: 1.6rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%236b7280' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19 9l-7 7-7-7'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right .45rem center; background-size: .8rem .8rem;
}
select.dd-prop-input::-ms-expand { display: none; }
/* The chevron is a data-URI SVG (can't reference a CSS variable for its stroke) — same dark repaint the
   app's own select.input-field needs, for the same low-contrast-otherwise reason. */
[data-theme="dark"] select.dd-prop-input {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2393a39a' stroke-width='2'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19 9l-7 7-7-7'/%3E%3C/svg%3E");
}
.dd-prop-check { display: flex; align-items: center; gap: .35rem; font-size: .78rem; color: var(--dale-text); margin: .45rem 0; cursor: pointer; }
/* accent-color only recolors the native CHECKED tick — the box itself (unchecked state, size,
   border) stays 100% OS-default, which is why this still looked unstyled. appearance:none + a
   hand-drawn box/checkmark makes both states match the app's own inputs. */
.dd-prop-check input[type="checkbox"] {
  -webkit-appearance: none; -moz-appearance: none; appearance: none;
  width: 14px; height: 14px; margin: 0; flex: none; cursor: pointer;
  border: 1px solid var(--dale-border-strong); border-radius: 3px;
  background: var(--dale-surface); background-repeat: no-repeat; background-position: center; background-size: 10px 10px;
  box-sizing: border-box;
}
.dd-prop-check input[type="checkbox"]:checked {
  background-color: var(--color-dale-600); border-color: var(--color-dale-600);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%23ffffff' stroke-width='3'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M5 13l4 4L19 7'/%3E%3C/svg%3E");
}
.dd-prop-check input[type="checkbox"]:focus-visible { outline: 2px solid var(--color-dale-600); outline-offset: 1px; }

/* DALE-157 round 2: Studio-style expression-syntax switcher (elsa-studio's ExpressionInput.razor —
   a "⋮" button that sits quietly next to the label when on the plain editor, becomes a labeled button
   once switched to JavaScript/Liquid/Variable, and opens a menu with a checkmark next to the active
   syntax) — a from-scratch equivalent in plain HTML/CSS since this panel isn't MudBlazor. */
.dd-syntax-label-row { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin: .5rem 0 .2rem; }
.dd-syntax-label-row .dd-prop-label { flex: 1; min-width: 0; }
.dd-syntax-menu-wrap { position: relative; flex-shrink: 0; }
.dd-syntax-btn {
  font-size: .7rem; line-height: 1; color: var(--dale-text-muted); background: transparent; border: 1px solid transparent;
  border-radius: .3rem; padding: .2rem .35rem; cursor: pointer; font-family: inherit;
}
.dd-syntax-btn:hover { background: var(--dale-surface-hover); border-color: var(--dale-border); }
.dd-syntax-btn-active { color: #fff; background: var(--color-dale-600); border-color: var(--color-dale-600); }
.dd-syntax-btn-active:hover { background: var(--color-dale-700); border-color: var(--color-dale-700); }
.dd-syntax-menu {
  position: absolute; top: calc(100% + 2px); right: 0; z-index: 30; min-width: 120px; background: var(--dale-surface);
  border: 1px solid var(--dale-border); border-radius: .4rem; box-shadow: 0 4px 12px rgba(0,0,0,.12); padding: .25rem 0;
}
.dd-syntax-menu.hidden { display: none; }
.dd-syntax-menu-item {
  display: flex; align-items: center; gap: .4rem; font-size: .78rem; color: var(--dale-text); padding: .3rem .6rem;
  cursor: pointer; white-space: nowrap;
}
.dd-syntax-menu-item:hover { background: var(--dale-surface-hover); }
.dd-syntax-check { width: 12px; flex-shrink: 0; color: var(--color-dale-600); font-size: .75rem; }

/* Node/pin/edge chrome — same conventions proven in the docs/sandbox prototype: input on the left
   (edge-hugging dot), outcomes stacked down the right edge with the label reading before its pin,
   asymmetric padding reserving space on both sides so the activity's own text never runs under either. */
.node.dnode {
  position: absolute; width: 230px; padding: .5rem 4.4rem .5rem 1.1rem; background: var(--dale-surface);
  border: 1.5px solid var(--dale-border-strong); border-radius: .5rem; user-select: none; cursor: grab;
  overflow-wrap: break-word; word-break: break-word; font-family: Inter, system-ui, sans-serif;
}
.node.dnode:active { cursor: grabbing; }
.node.dnode.sel { border-color: var(--color-dale-600); box-shadow: 0 0 0 3px var(--color-dale-100); }
/* --color-dale-100 is a fixed brand tint (same value in both themes, like badges/buttons elsewhere) —
   fine as a pale glow on the light canvas, but reads as a bright halo on the dark one. Swap it for a
   translucent wash of the same accent, the same treatment used for bg-dale-50/.dnode-embedded-slot.drillable. */
[data-theme="dark"] .node.dnode.sel { box-shadow: 0 0 0 3px rgba(74, 222, 128, 0.25); }
.node.dnode .ty { display: block; font-family: ui-monospace, monospace; font-size: .65rem; color: var(--dale-text-muted); margin-bottom: .125rem; }
.node.dnode .nm { display: block; margin-top: 1px; font-size: .875rem; color: var(--dale-text-heading); }

/* DALE-157 round 2: nested-activity slots (e.g. SendHttpRequest's FailedToConnect/Timeout) rendered
   INSIDE the parent node's own box, mirroring Studio's EmbeddedActivityWrapper (an X6 child cell) —
   dashed border marks it as a contained property, not a flow-connected node. Assignment stays
   props-panel-only; clicking here just selects the parent (see nodeEmbeddedHtml's remarks). */
.dnode-embedded { display: flex; flex-direction: column; gap: .3rem; margin-top: .45rem; }
.dnode-embedded-slot {
  display: flex; align-items: center; justify-content: space-between; gap: .4rem;
  font-size: .68rem; padding: .3rem .5rem; border: 1px dashed var(--dale-border-strong); border-radius: .35rem; background: var(--dale-surface-2);
}
.dnode-embedded-slot.empty { color: var(--dale-text-muted); }
.dnode-embedded-port { font-family: ui-monospace, monospace; color: var(--dale-text-muted); flex-shrink: 0; }
.dnode-embedded-activity {
  color: var(--dale-text-heading); text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-style: normal;
}
.dnode-embedded-slot.empty .dnode-embedded-activity { color: var(--dale-text-muted); font-style: italic; }
/* DALE-157 round 3: a Flowchart-typed slot is clickable straight from the canvas (see wireInteraction's
   central mousedown handler) — same drill-in action as the props panel's button, just like clicking an
   embedded container in Studio opens it. Solid (not dashed) border + pointer cursor mark it as such. */
.dnode-embedded-slot.drillable { cursor: pointer; border-style: solid; }
.dnode-embedded-slot.drillable:hover { border-color: var(--color-dale-600); background: rgba(74, 222, 128, 0.12); }
.dnode-embedded-slot.drillable .dnode-embedded-activity { color: var(--color-dale-600); font-weight: 600; }
.dnode-del {
  position: absolute; top: -8px; right: -8px; width: 18px; height: 18px; border-radius: 50%; background: #dc2626;
  color: #fff; font-size: 12px; line-height: 18px; text-align: center; cursor: pointer;
}

/* DALE-157 round 3: prep for the instance viewer's future per-activity execution-stats overlay
   (daleDesigner.applyActivityStats — no caller yet, see its own remarks). Opposite corner from
   .dnode-del so the two never collide. */
.dnode-stats {
  position: absolute; top: -8px; left: -8px; min-width: 18px; height: 18px; padding: 0 4px; border-radius: 9px;
  color: #fff; font-size: 10px; font-weight: 600; line-height: 18px; text-align: center; font-family: ui-monospace, monospace;
}
.dnode-stats-completed { background: #16a34a; }
.dnode-stats-running   { background: #2563eb; }
.dnode-stats-blocked   { background: #d97706; }
.dnode-stats-faulted   { background: #dc2626; }

/* ============================================================
   Instance graph viewer (dale-instance-graph.js) — read-only reuse of the designer's own node/port
   visual vocabulary (.node.dnode, .out-row/.out-pin/.out-label) so a run's graph renders exactly like
   the real designer canvas — same solid boxes, nothing faded or re-styled — not a separate, "diffed"
   reimplementation. .dig-canvas scopes the few overrides that only make sense for a non-editable view
   (no drag, no resize handle, no del button — those elements are simply never created here, so no
   override is needed for them).
   ============================================================ */
.dig-canvas .node.dnode { cursor: pointer; }
.dig-canvas .node.dnode:active { cursor: pointer; }
/* Status dot — top-right corner of a node that completed (green) or faulted (red) at least once during
   this run. Deliberately just a plain filled circle (no count, no border glow) and only for those two
   states; a node that never ran, or is still running, gets no dot and looks like any other designer node. */
.dig-status-dot {
  position: absolute; top: -10px; right: -10px; width: 24px; height: 24px; border-radius: 50%;
  border: 3px solid var(--dale-surface);
}
.dig-status-dot-completed { background: #16a34a; }
.dig-status-dot-faulted   { background: #dc2626; }

/* Small read-only detail card shown when a node is clicked — name/type/status/timing/failure only
   (the existing timeline view already covers full state/output dumps; this stays intentionally light). */
.dig-info-panel {
  position: absolute; top: 10px; left: 10px; z-index: 15; width: 260px; max-height: calc(100% - 20px);
  overflow-y: auto; background: var(--dale-surface); border: 1px solid var(--dale-border-strong);
  border-radius: .5rem; box-shadow: var(--dale-shadow); padding: .75rem; font-family: Inter, system-ui, sans-serif;
}
.dig-info-panel .dig-info-close {
  position: absolute; top: .4rem; right: .5rem; cursor: pointer; color: var(--dale-text-muted); font-size: 1rem; line-height: 1;
}
.dig-info-panel .dig-info-title { font-size: .8rem; font-weight: 600; color: var(--dale-text-heading); padding-right: 1rem; }
.dig-info-panel .dig-info-type { font-family: ui-monospace, monospace; font-size: .68rem; color: var(--dale-text-muted); margin-top: .1rem; }
.dig-info-panel .dig-info-row { font-size: .74rem; color: var(--dale-text); margin-top: .5rem; }
.dig-info-panel .dig-info-label { font-size: .65rem; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--dale-text-muted); display: block; margin-bottom: .1rem; }
.dig-info-panel .dig-info-fail { color: #dc2626; white-space: pre-line; }

/* Legend strip (Completed/Faulted) in the page header. */
.dig-legend { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.dig-legend-item { display: flex; align-items: center; gap: .35rem; font-size: .78rem; color: var(--dale-text-muted); }
.dig-legend-dot { width: .6rem; height: .6rem; border-radius: 50%; flex-shrink: 0; }
.node-resize-handle {
  position: absolute; right: -4px; bottom: -4px; width: 11px; height: 11px; border-radius: 2px;
  background: var(--dale-surface); border: 1.5px solid var(--color-dale-600); cursor: nwse-resize;
}
.dnode-start {
  position: absolute; left: -8px; top: -8px; width: 16px; height: 16px; border-radius: 50%;
  background: var(--color-dale-600); color: #fff; font-size: 8px; line-height: 16px; text-align: center;
}
.in-anchor {
  position: absolute; top: 50%; left: -6px; transform: translateY(-50%); width: 10px; height: 10px;
  border-radius: 50%; background: var(--dale-surface); border: 2px solid var(--dale-text-muted);
}
.out-row {
  position: absolute; right: -7px; top: 10px; bottom: 10px; left: auto; display: flex;
  flex-direction: column; justify-content: space-around; align-items: flex-end;
}
.out-item { display: flex; flex-direction: row-reverse; align-items: center; gap: 5px; }
.out-pin { width: 13px; height: 13px; border-radius: 50%; border: 2px solid var(--dale-surface); cursor: crosshair; }
.out-label { font-size: 9px; font-family: ui-monospace, monospace; color: var(--dale-text); }

.edge-group { cursor: pointer; }
.edge-handle { opacity: 0; pointer-events: none; cursor: col-resize; fill: var(--dale-surface); stroke: var(--dale-text-muted); stroke-width: 1.5px; }
.edge-group:hover .edge-handle { opacity: 1; pointer-events: all; }

.dd-canvas-ghost {
  position: fixed; padding: 4px 10px; background: var(--dale-surface); border: 1.5px solid var(--color-dale-600);
  border-radius: 6px; font-size: 12px; pointer-events: none; z-index: 9999; box-shadow: 0 2px 6px rgba(0,0,0,.15); color: var(--dale-text);
}

/* DALE-157 round 3: Shift+drag rubber-band multi-select — positioned in canvasOuter's own (unscaled,
   unpanned) screen space, since it's just a transient selection-drag overlay, not graph content. */
.dd-rubberband {
  position: absolute; z-index: 15; border: 1.5px dashed var(--color-dale-600);
  background: var(--color-dale-600); opacity: .18; pointer-events: none;
}
