/* workflow-canvas.css
 *
 * n8n-style node + edge styling layered over the reused GridCanvas. The canvas
 * surface, drag/snap and FLIP animation come from grid-canvas.css unchanged;
 * this file only styles the workflow node visual (icon/label/ports) and the
 * SVG connection overlay.
 */

/* The viewport: a window that CLIPS the pannable/zoomable world. By default it
   fills the screen height left under the DetailView chrome (top nav + tab strip
   + toolbar + Name field, ~210px) so a node near the canvas edge isn't cut off;
   a host can still pin an explicit size via --wf-viewport-h. The world inside can
   be far larger and is navigated by pan/zoom. */
/* Workflow header — title + subtitle (Name/Description), same design as the
   Dashboard header in grid-canvas.css. Shown above the shell; in view mode it
   replaces the hidden Name/Description/Active editors. */
.workflow-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    padding: 12px 8px 8px;
}

.workflow-header__title {
    font-size: 1.25rem;
    font-weight: 600;
    color: #212529;
    line-height: 1.3;
}

.workflow-header__subtitle {
    font-size: 0.875rem;
    color: #868e96;
    margin-top: 2px;
}

/* Flex-row shell: the palette is a vertical side panel on the LEFT and the
   canvas fills the remaining width. The palette is kept a sibling OUTSIDE the
   canvas (see WorkflowCanvas.razor) so its click-to-add @onclick stays off the
   DevExpress event surface. */
.workflow-shell {
    display: flex;
    flex-direction: row;
    align-items: stretch;
    gap: 12px;
}

.workflow-canvas {
    position: relative;
    flex: 1 1 auto;
    min-width: 0; /* allow the canvas to shrink beside the palette */
    height: var(--wf-viewport-h, calc(100vh - 210px));
    min-height: 480px;
    overflow: hidden;
    touch-action: none; /* let the pan/zoom controller own touch gestures */
}

/* The transformed world layer. transform-origin:0 0 keeps the pan/zoom math
   linear (workflow-viewport-interop solves pan against this origin). The
   transform itself is written inline by JS; data-wf-scale is published here. */
.workflow-canvas__world {
    position: relative;
    /* A generous surface so the edge SVG (sized to the world) covers the node
       area; nodes are absolutely positioned within and panning is free, so this
       is just the visible "paper", not a hard bound — a node can be dragged
       beyond it and reached again by panning. */
    width: 100%;
    min-width: 4000px;
    min-height: 3000px;
    transform-origin: 0 0;
    cursor: grab;
}

.workflow-canvas--panning .workflow-canvas__world {
    cursor: grabbing;
}

/* Floating zoom/pan control cluster, pinned to the viewport's top-right and
   kept OUTSIDE the world so it never scales or pans. */
.workflow-canvas__controls {
    position: absolute;
    right: 12px;
    top: 12px;
    z-index: 20;
    display: flex;
    align-items: center;
    gap: 2px;
    padding: 3px;
    background: rgba(255, 255, 255, 0.95);
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
    user-select: none;
}

.workflow-canvas__ctrl {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: #444;
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
    transition: background-color 80ms ease;
}

.workflow-canvas__ctrl:hover {
    background: rgba(0, 0, 0, 0.07);
}

.workflow-canvas__zoom {
    min-width: 44px;
    text-align: center;
    font-size: 12px;
    color: #555;
    font-variant-numeric: tabular-nums;
}

/* SVG overlay sits over the grid, but BEHIND the node cells (cells use z-index:1
   in GridCanvas) so edges render behind nodes n8n-style. pointer-events:none
   lets port pointerdowns reach the ports beneath; edge paths opt back in. */
.workflow-canvas__edges {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: 0;
    pointer-events: none;
    overflow: visible;
}

.workflow-edge {
    fill: none;
    stroke: #9aa3b2;
    stroke-width: 2;
    pointer-events: none;
    transition: stroke 0.12s ease, stroke-width 0.12s ease;
}

/* Wide transparent hit path layered behind each visible edge — the real click
   target. A 16px stroke makes the edge trivially easy to click without
   affecting the visual. */
.workflow-edge__hit {
    fill: none;
    stroke: transparent;
    stroke-width: 16;
    pointer-events: stroke;
    cursor: pointer;
}

/* While a node is being dragged, hide Blazor's declarative edge paths (direct
   children of the SVG) and let the JS-owned live layer (<g data-wf-live>) show
   the moving edges. Swapped back to the Blazor paths on drop. */
.workflow-canvas__edges.wf-dragging > path {
    display: none;
}

/* Hover: subtly brighten the edge when the cursor is over its hit area.
   Because the hit path precedes the visible path in DOM order, we use the
   adjacent-sibling combinator to style the visible path on hit-path hover. */
.workflow-edge__hit:hover + .workflow-edge {
    stroke: #c8cdd8;
    stroke-width: 2.5;
}

/* Selected state — snaps to accent colour with no transition. */
.workflow-edge--selected {
    stroke: #ff6d5a;
    stroke-width: 3;
    transition: none;
}

.workflow-edge--draft {
    stroke: #4c8bf5;
    stroke-dasharray: 5 4;
    pointer-events: none;
}

/* Draft wire turns red while hovering an input that would be rejected. */
.workflow-edge--draft.workflow-edge--invalid {
    stroke: #e03a28;
}

.workflow-edge__arrow {
    fill: #9aa3b2;
}

/* Floating × delete button rendered at the bezier midpoint of the selected
   edge. Lives inside .workflow-canvas (position:relative) so left/top map
   directly to SVG coordinates (both share the same origin). */
.workflow-edge__delete-btn {
    position: absolute;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    border: none;
    background: #ff6d5a;
    color: #fff;
    font-size: 14px;
    font-weight: 700;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transform: translate(-50%, -50%);
    z-index: 10;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
    pointer-events: auto;
    transition: background 0.1s ease;
}

.workflow-edge__delete-btn:hover {
    background: #e03a28;
}

/* While a node is being dragged the Blazor edge paths are hidden (the JS live
   layer draws the moving edges), so hide the selected edge's delete button too
   rather than leave it floating with no visible edge. The button follows the
   SVG in DOM order, so the general-sibling combinator reaches it. */
.workflow-canvas__edges.wf-dragging ~ .workflow-edge__delete-btn {
    display: none;
}

/* ---- Free-positioned node wrapper ---- */

/* Each node is absolutely positioned by pixel (left/top) inside the pan/zoom
   world — no grid. The wrapper carries the card chrome; .workflow-node inside
   draws the icon/label and anchors the ports. */
.workflow-canvas__node {
    position: absolute;
    box-sizing: border-box;
    background: #fff;
    border: 1px solid #e0e0e0;
    border-radius: 6px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
    user-select: none;
    /* Overflow visible so the ports / ＋ that straddle the edge aren't clipped. */
    overflow: visible;
}

/* Only design mode is draggable; show the grab affordance there. */
.workflow-canvas__node--draggable {
    cursor: grab;
}

/* Per-type accent on the card border (rectangular types). The icon tint is
   handled by the .workflow-node--type .workflow-node__icon rules below (the
   wrapper is the ancestor, so those still match). */
.workflow-canvas__node.workflow-node--action { border-color: #4c8bf5; }
.workflow-canvas__node.workflow-node--subworkflow { border-color: #8b5cf6; }

/* Trigger and Condition draw their own shapes (circle / diamond) as layers
   inside .workflow-node below; the wrapper stays the untouched 200×110 hit-box
   (drag surface + port anchors + JS geometry), just with its card chrome off. */
.workflow-canvas__node.workflow-node--trigger,
.workflow-canvas__node.workflow-node--condition {
    background: transparent;
    border: none;
    box-shadow: none;
}

/* Edit / delete controls, top-right of the node card (design mode). */
.workflow-node__controls {
    position: absolute;
    top: 4px;
    right: 4px;
    display: flex;
    align-items: center;
    gap: 2px;
    padding: 1px 3px;
    background: rgba(255, 255, 255, 0.9);
    border-radius: 6px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
    z-index: 4;
}

.workflow-node__ctrl {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border: none;
    background: transparent;
    cursor: pointer;
    border-radius: 4px;
}

.workflow-node__ctrl:hover {
    background: rgba(0, 0, 0, 0.08);
}

.workflow-node__ctrl img {
    width: 16px;
    height: 16px;
    display: block;
}

/* ---- Node visual ---- */

.workflow-node {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 8px 14px;
    box-sizing: border-box;
    text-align: center;
}

/* Keep the icon/label above the per-type shape layers (::before/::after). */
.workflow-node__icon,
.workflow-node__label {
    position: relative;
    z-index: 1;
}

/* ---- Per-type node shapes ----
   Trigger = circle, Condition = diamond, Action/Sub-workflow = rectangle.
   The shapes are painted layers INSIDE the fixed 200×110 wrapper so node
   dragging, DOM-measured port anchors and the edge geometry in the workflow
   JS interop files need no changes. */

/* Trigger — a circle the full height of the node box, centred horizontally. */
.workflow-node--trigger .workflow-node::before {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    height: 100%;
    aspect-ratio: 1 / 1;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: #fff;
    border: 2px solid #2ea36b;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
    box-sizing: border-box;
}

.workflow-node--trigger .workflow-node__label {
    max-width: 96px; /* keep the name inside the 110px circle */
}

/* Pin the trigger's output port (and the ＋ append button under it) to the
   circle's right edge instead of the wrapper's. 55px = half the node height
   (the circle's radius — mirrors WorkflowCanvasActions.NODE_HEIGHT_PX). */
.workflow-node--trigger .workflow-node__port--out {
    right: calc(50% - 55px - 7px);
}

.workflow-node--trigger .workflow-node__add {
    right: calc(50% - 55px - 9px);
}

/* Condition — a diamond whose left/right vertices sit exactly on the wrapper's
   mid-left/mid-right edges, i.e. on the ports, so wires attach to the tips.
   CSS borders can't follow clip-path, so the border is a two-layer trick:
   ::before paints the accent-coloured diamond, ::after a slightly inset white
   diamond on top of it. */
.workflow-node--condition .workflow-node::before {
    content: "";
    position: absolute;
    inset: 0;
    background: #e8a33d;
    clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
    filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.08));
}

.workflow-node--condition .workflow-node::after {
    content: "";
    position: absolute;
    inset: 2px;
    background: #fff;
    clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
}

.workflow-node--condition .workflow-node__label {
    max-width: 55%; /* the diamond narrows away from the centre */
    font-size: 12px;
}

.workflow-node__icon {
    color: #4c8bf5;
}

.workflow-node__icon svg {
    width: 28px;
    height: 28px;
    display: block;
}

/* Tint the icon to match each node type's accent. */
.workflow-node--trigger .workflow-node__icon {
    color: #2ea36b;
}

.workflow-node--action .workflow-node__icon {
    color: #4c8bf5;
}

.workflow-node--condition .workflow-node__icon {
    color: #e8a33d;
}

.workflow-node--subworkflow .workflow-node__icon {
    color: #8b5cf6;
}

.workflow-node__label {
    font-size: 13px;
    font-weight: 600;
    color: #333;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
}

/* Let clicks on empty canvas fall THROUGH the grid surface to the edge hit-paths
   in the SVG behind it. The grid root is a full-canvas div painted on top of the
   SVG; with its default pointer-events:auto it swallows every click over empty
   space (transparent background still hit-tests), so edge clicks never reach the
   wires and the delete affordance can't be selected. n8n avoids this by placing
   only node divs above the connection SVG — no intercepting surface. We replicate
   that: the workflow grid root is pointer-events:none, and its cells / overlay
   controls opt back in, so node drag/edit/delete and port grabs still work while
   empty-space clicks reach the wires beneath. Scoped via :has so the dashboard
   canvas (which has no SVG underlay) is untouched. */
.grid-canvas:has(.workflow-node) {
    pointer-events: none;
}

/* The design-mode slot guides (grid lines painted as a background gradient in
   grid-canvas.css) are noise on the workflow canvas, where placement is wire-
   driven rather than grid-snapped. Drop the gradient for workflow canvases only;
   the dashboard canvas keeps its guides. */
.grid-canvas--design:has(.workflow-node) {
    background-image: none;
}

.grid-canvas:has(.workflow-node) .grid-canvas-cell,
.grid-canvas:has(.workflow-node) .grid-canvas-cell-controls {
    pointer-events: auto;
}

/* Phase 1 — port anchoring. GridCanvas pins every cell to overflow:hidden inline
   (so widget content never bleeds), which forced the ports just INSIDE the cell
   and made wires look like they left the corner. For workflow cells we opt back
   into overflow:visible (scoped via :has so other canvases are untouched) and
   centre each port ON the node edge, n8n-style. Inline styles win over external
   CSS for the same property, so !important is required to beat the inline rule. */
.grid-canvas-cell:has(.workflow-node) {
    overflow: visible !important;
}

/* Resizing a workflow node has no meaning (nodes are a fixed size), and the
   GridCanvas resize handle (a dark corner triangle) reads as a connection
   point. Hide it for workflow cells so the only connection affordances are the
   directional ports. */
.grid-canvas-cell:has(.workflow-node) .grid-canvas__resize-handle {
    display: none !important;
}

/* Ports re-enable pointer events even though GridCanvas sets the cell body to
   pointer-events:none in design mode (a descendant may opt back in), so the
   user can grab a port to draw an edge while the body still drives node drag. */
.workflow-node__port {
    position: absolute;
    top: 50%;
    width: 14px;
    height: 14px;
    margin-top: -7px;
    border-radius: 50%;
    background: #fff;
    border: 2px solid #9aa3b2;
    pointer-events: auto;
    z-index: 3;
    box-sizing: border-box;
    transition: border-color 0.12s ease, background 0.12s ease, box-shadow 0.12s ease;
}

/* Phase 2 — direction is readable at a glance: the INPUT is a hollow socket on
   the left edge, the OUTPUT is a filled nub on the right edge. Both straddle the
   node edge (half in, half out) so wires visibly attach to them. */
.workflow-node__port--in {
    left: -7px;
}

.workflow-node__port--out {
    right: -7px;
    cursor: crosshair;
    background: #9aa3b2;
}

.workflow-node__port--out:hover,
.workflow-node__port--in:hover {
    border-color: #4c8bf5;
    background: #eaf1fe;
}

.workflow-node__port--out:hover {
    background: #4c8bf5;
    box-shadow: 0 0 0 4px rgba(76, 139, 245, 0.18);
}

/* Phase 3 — live drag feedback. While a wire is being drawn, the input port
   under the cursor lights up green when the drop is valid (passes the graph
   guard) or red when it would be rejected (self / duplicate / cycle). */
.workflow-node__port--drop-ok {
    border-color: #2ea36b !important;
    background: #2ea36b !important;
    box-shadow: 0 0 0 5px rgba(46, 163, 107, 0.25);
}

.workflow-node__port--drop-invalid {
    border-color: #e03a28 !important;
    background: #ffe3df !important;
    box-shadow: 0 0 0 5px rgba(224, 58, 40, 0.18);
    cursor: not-allowed;
}

/* Inline "+" appends and auto-connects the next node. Sits just below the
   output port, inside the cell (GridCanvas cells clip overflow). */
.workflow-node__add {
    position: absolute;
    right: -9px;
    top: calc(50% + 18px);
    width: 18px;
    height: 18px;
    border-radius: 50%;
    border: none;
    background: #4c8bf5;
    color: #fff;
    font-size: 14px;
    font-weight: 700;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    pointer-events: auto;
    z-index: 4;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}

.workflow-node__add:hover {
    background: #3b78e0;
}

/* ---- Palette ---- */

/* Vertical side panel on the left of the canvas — not an overlay, so it never
   covers nodes (which start at the top-left of the grid). Buttons are stacked so
   the panel absorbs additional node types as they are added. */
.workflow-palette {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
    flex: 0 0 auto;
    width: 168px;
    padding: 10px;
    background: #fafbfc;
    border: 1px solid #e3e6ec;
    border-radius: 8px;
    height: var(--wf-viewport-h, calc(100vh - 210px));
    min-height: 480px;
    overflow-y: auto;
}

.workflow-palette__title {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #888;
    margin-bottom: 4px;
}

.workflow-palette__item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    background: #fff;
    border: 1px solid #e3e6ec;
    border-radius: 6px;
    font-size: 13px;
    color: #333;
    /* grab hint — the item is draggable onto the canvas as well as click-to-add. */
    cursor: grab;
    text-align: left;
}

.workflow-palette__item:active {
    cursor: grabbing;
}

.workflow-palette__item:hover {
    border-color: #4c8bf5;
    background: #eaf1fe;
}

/* Mini shape preview around each palette icon so the palette teaches the
   canvas vocabulary: circle = Trigger, diamond = Condition, rounded square =
   Action / Sub-workflow. */
.workflow-palette__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 26px;
    height: 26px;
    border: 1.5px solid currentColor;
    border-radius: 6px;
    box-sizing: border-box;
    color: #4c8bf5;
}

.workflow-palette__icon svg {
    width: 16px;
    height: 16px;
}

.workflow-palette__item--trigger .workflow-palette__icon {
    color: #2ea36b;
    border-radius: 50%;
}

.workflow-palette__item--action .workflow-palette__icon {
    color: #4c8bf5;
}

.workflow-palette__item--condition .workflow-palette__icon {
    color: #e8a33d;
    border-radius: 3px;
    transform: rotate(45deg) scale(0.85);
}

.workflow-palette__item--condition .workflow-palette__icon svg {
    transform: rotate(-45deg);
}

.workflow-palette__item--subworkflow .workflow-palette__icon {
    color: #8b5cf6;
}

/* The node graph uses free positioning (FreePosition="true"), so the shared
   GridCanvas's design-mode slot grid is just visual noise here — suppress it for
   the workflow canvas only, leaving the Dashboard dashboard's grid intact. */
.workflow-canvas .grid-canvas--design {
    background-image: none;
}

/* (Node-type accent now lives on .workflow-canvas__node above; the old GridCanvas
   cell-chrome accent rule was removed with the grid.) */

/* Empty-state CTA, centred over the canvas while the workflow has no nodes.
   The container is click-through (so it never traps drags) but the button
   re-enables pointer events for itself. */
.workflow-canvas__empty {
    position: absolute;
    inset: 0;
    z-index: 15;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    pointer-events: none;
    user-select: none;
}

.workflow-canvas__empty-icon {
    display: inline-flex;
    color: #2ea36b;
}

.workflow-canvas__empty-icon svg {
    width: 40px;
    height: 40px;
    opacity: 0.85;
}

.workflow-canvas__empty-text {
    font-size: 13px;
    color: #888;
}

.workflow-canvas__empty-add {
    pointer-events: auto;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 16px;
    background: #2ea36b;
    border: none;
    border-radius: 6px;
    color: #fff;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
    transition: background-color 80ms ease;
}

.workflow-canvas__empty-add:hover {
    background: #268a5b;
}

.workflow-node--action {
    box-shadow: inset 0 0 0 2px #4c8bf5;
    border-radius: 4px;
}

.workflow-node--subworkflow {
    box-shadow: inset 0 0 0 2px #8b5cf6;
    border-radius: 4px;
}
