/* =============================================================================
   WORKFLOW MANAGEMENT — Phase 1
   Loaded after tasks.css/designer.css (see index.html) — reuses .task-theme's
   own --task-* custom properties throughout rather than introducing a second
   palette, and follows tasks.css's own dark-mode approach: nothing here sets
   colors directly, so :root[data-theme="dark"] .task-theme's existing
   variable overrides apply automatically with zero duplicate rules needed.
   ============================================================================= */

/* ---- Designer landing: workflow definition cards -------------------------- */

.wf-def-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 12px;
}
.wf-def-card { cursor: pointer; }
.wf-def-menu-btn { padding: 4px 6px; min-height: auto; }

/* ---- Canvas editor ---------------------------------------------------------- */

.wf-editor {
  display: flex;
  flex-direction: column;
  /* § bugfix: this was `height: 100vh`/`100svh` — the FULL viewport
     height, ignoring that .wf-editor actually renders inside #view-root
     (app.css), which is already shorter than the viewport (sized via
     `flex: 1` to exclude the topbar/tabbar it sits between). Forcing the
     full viewport height here made .wf-editor taller than its real
     container, pushing everything near its bottom — most visibly the
     floating "+" add-step FAB — down past the visible area, behind the
     bottom tab bar. That's why the Step Library was unreachable on
     mobile: the one button that opens it had been pushed off-screen.
     `height: 100%` fills the actual (correctly-sized) parent instead. */
  height: 100%;
  padding: 0;
}
/* § direct request: "Move the Title, V badge, Undo, and Redo buttons to
   the bottom to the left of the other buttons. Make sure all the
   buttons now fit on one row." — collapses the previous two-row split
   (§196-201: a top row for back/title/badge/undo/redo, a second row for
   Steps/Test/Publish/Save/options) into the single row this app started
   with conceptually, just fully consolidated now: back-circle, title,
   badge, undo/redo (all left-aligned) — spacer — +Step, Publish, Test,
   Save, options (right-aligned). Fitting everything on one row is what
   drove converting Test and Save to icon-only squares this same request
   asked for (see .wf-icon-square-btn below) — those two used to be the
   widest text buttons in the row. */
.wf-editor-header {
  display: flex;
  flex-direction: column;
  padding: 10px 12px;
  border-bottom: 1px solid var(--task-border);
  background: var(--task-surface);
  flex: none;
}
/* § direct request: "Make sure all the buttons are on the same row and
   don't wrap to another row" — explicitly overrides the earlier
   flex-wrap:wrap safety net (added a few requests back after a real
   overflow report) with a hard flex-wrap:nowrap instead. This row has
   shed real width since that safety net was added — the zoom buttons
   and the header-level "+" are both gone now (one removed outright, the
   other moved back to the canvas), and Test/Save are compact icon
   squares rather than text buttons — so nowrap is a deliberate,
   informed choice here, not a reversion to the original unguarded
   default. The title (flex:1 1 auto; min-width:40px, below) is still
   the one element that shrinks to absorb whatever width the fixed-size
   buttons around it need. */
.wf-header-row {
  display: flex; align-items: center; gap: 6px; flex-wrap: nowrap;
  /* § bugfix: "make sure the 3-dots button is not overflowing the right
     side of the screen" — on a narrow phone, this row's fixed-size
     buttons (back/undo/redo/publish/test/save/options) plus the
     title-group's own 40px floor can add up to more than the viewport
     width even with flex-wrap:nowrap's one shrinkable spacer/title
     doing all the giving. Without this, that overflow was invisible,
     not just clipped-looking — body has its own `overflow: hidden`
     (app.css), so anything pushed past the row's right edge (the
     options button, being last) was completely unreachable, not merely
     off-screen-but-scrollable. A local horizontal scroller guarantees
     it's always reachable regardless of viewport width or how many
     buttons this row ever grows to. */
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.wf-header-row::-webkit-scrollbar { display: none; }
.wf-header-spacer { flex: 1 1 auto; }

.wf-back-circle {
  width: 36px; height: 36px; flex: none; border-radius: 50%;
  border: none; background: var(--task-surface-raised); color: var(--task-text);
  box-shadow: var(--task-shadow);
  display: flex; align-items: center; justify-content: center; cursor: pointer; padding: 0;
}
.wf-back-circle:active { transform: scale(0.94); }
@media (hover: hover) { .wf-back-circle:hover { background: var(--task-accent-dim); } }

/* § direct request: "in desktop only put the title 'Workflow Designer'
   in the header to the right of the Back button" — a plain static
   label, distinct from .wf-title-text (which shows THIS workflow's own
   editable name, over on the other side of .wf-header-spacer). Hidden
   by default; the same min-width:821px desktop breakpoint the rest of
   this file already uses (see .wf-canvas-fab-add below) turns it on. */
.wf-static-title { display: none; }

/* § history: this wrapper used to also stack the version badge under
   the title as subtext (align-items:flex-end tucked it into the
   corner). § direct request: "on desktop make the V badge normal size
   and directly to the left of the Publish button instead of subtext" —
   the badge itself moved out to its own sibling spot in .wf-header-row
   now (workflows.js), but this wrapper still does useful work on its
   own: it's what carries the shrink role .wf-title-text used to carry
   directly as a header-row flex item (flex:1 1 auto; min-width:40px),
   so the title still ellipsizes correctly as the row gets tight. */
.wf-title-group {
  display: flex; flex-direction: column; align-items: flex-end;
  flex: 1 1 auto; min-width: 40px; gap: 1px;
}
/* § direct request: "Remove the pencil icon and allow the user to simply
   click the title to edit it" — no wrapper/pencil needed any more, the
   text itself is the whole clickable target (cursor + hover feedback
   below say so). */
.wf-title-text {
  font-size: 20px; font-weight: 800; color: var(--task-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  width: 100%; min-width: 0;
  padding: 3px 6px; margin: -3px -6px; border-radius: var(--task-radius-sm); cursor: pointer;
}
@media (hover: hover) { .wf-title-text:hover { background: var(--task-accent-dim); } }
/* § direct request: "show the full title in the box while editing...
   close the full box when they click away" — floats as a fixed-position
   overlay (portaled to document.body — see startTitleRename()'s own
   comment for why) rather than participating in the header row's normal
   flex layout, so it escapes BOTH the cramped .wf-title-group flex-item
   width AND .wf-header-row's own overflow-x:auto scroll clipping (added
   for the 3-dots-button overflow bugfix — a plain in-flow element wide
   enough to overflow that row would just get scroll-clipped there
   rather than actually staying visible). Width is set inline per
   instance in JS, measured to the ACTUAL text so the full title is
   always shown uncropped rather than the old width:100%-of-container. */
.wf-title-text-input {
  position: fixed; z-index: 100;
  font-size: 18px; font-weight: 800; font-family: inherit; color: inherit;
  border: 1px solid var(--task-border); border-radius: var(--task-radius-sm);
  padding: 2px 8px; background: var(--task-surface-raised);
  box-shadow: var(--task-shadow);
  min-width: 60px;
}

/* Icon-only circular buttons (undo/redo/options) — a plain white circle
   with a subtle shadow, matching the back-circle's own treatment. Redo
   dims itself out (via :disabled, set in JS whenever the redo stack is
   empty) rather than hiding — the reference design shows it present but
   visibly inactive, not absent. */
.wf-icon-circle-btn {
  width: 34px; height: 34px; flex: none; border-radius: 50%;
  border: none; background: var(--task-surface-raised); color: var(--task-text);
  box-shadow: var(--task-shadow);
  display: flex; align-items: center; justify-content: center; cursor: pointer; padding: 0;
}
.wf-icon-circle-btn:disabled { opacity: 0.35; cursor: default; box-shadow: none; }
@media (hover: hover) { .wf-icon-circle-btn:not(:disabled):hover { background: var(--task-accent-dim); } }

/* § direct request: "make the Save button a square save icon. Make the
   Test button a square play button." — same sizing/shadow treatment as
   .wf-icon-circle-btn, just squared off (10px radius) rather than fully
   round, to read as visually distinct from the neutral undo/redo/
   options circles as its own small family (Test/Save — the two actions
   this request specifically called out to convert to icon-only). */
.wf-icon-square-btn {
  width: 34px; height: 34px; flex: none; border-radius: 10px;
  border: none; background: var(--task-surface-raised); color: var(--task-text);
  box-shadow: var(--task-shadow);
  display: flex; align-items: center; justify-content: center; cursor: pointer; padding: 0;
}
@media (hover: hover) { .wf-icon-square-btn:hover { background: var(--task-accent-dim); } }
/* § direct request (an earlier turn): "change the Test button to a
   button with a light blue background" — kept on the new square-icon
   shape rather than dropped when Test went icon-only. */
.wf-btn-test { background: var(--task-blue-bg); color: #2255c7; }
@media (hover: hover) { .wf-btn-test:hover { background: var(--task-blue-bg); opacity: 0.85; } }
/* § direct request: "make the Save button background the same color as
   the Test button" — identical color pair to .wf-btn-test just above,
   applied to the header's own #wf-save-btn. */
.wf-btn-save { background: var(--task-blue-bg); color: #2255c7; }
@media (hover: hover) { .wf-btn-save:hover { background: var(--task-blue-bg); opacity: 0.85; } }
/* § direct request: "in desktop only add 'Test'/'Save' to the Test/Save
   buttons next to the icon" — .wf-btn-label stays hidden (icon-only,
   the existing mobile treatment) below the desktop breakpoint; see the
   min-width:821px block further down for the widened button + visible
   label. */
.wf-btn-label { display: none; }

/* § direct request: "put the Undo and Redo buttons closer together" —
   they're the only two elements that need TIGHTER spacing than
   .wf-canvas-fabs' own 8px gap (the group they now live inside — see
   its own comment below on the "move Undo/Redo to the canvas" request),
   so they keep their own small wrapper with a smaller internal gap
   rather than changing that shared gap, which the other floating
   buttons beside them also rely on. */
.wf-undo-redo-group { display: flex; align-items: center; gap: 0px; flex: none; }
/* § direct request: "make the undo and redo buttons darker, a little
   closer together, and a little bit bigger" — overrides the plain
   white-circle .wf-icon-circle-btn treatment (shared with the Options
   button, which is deliberately left alone) with a solid, darker fill
   so the two read as a more prominent, distinct control cluster rather
   than blending into the white toolbar; sized up from the shared 34px
   base for the "bigger" half of the same request. The gap change above
   is the "closer together" half. */
.wf-undo-redo-group .wf-icon-circle-btn {
  width: 38px; height: 38px;
  background: var(--task-slate);
  color: #fff;
}
.wf-undo-redo-group .wf-icon-circle-btn:disabled { background: var(--task-gray-bg); color: var(--task-text-faint); box-shadow: none; }
@media (hover: hover) { .wf-undo-redo-group .wf-icon-circle-btn:not(:disabled):hover { background: var(--task-navy); } }

/* § direct request: "on desktop make the V badge normal size and
   directly to the left of the Publish button instead of subtext" — no
   longer a small-size override (that was the earlier "make the badge
   small" subtext treatment for sitting tucked under the title); this
   now falls through to .task-badge's own plain default sizing. Markup
   moved out of .wf-title-group entirely (workflows.js), to sit as its
   own sibling in .wf-header-row right before #wf-publish-btn — mobile
   is unaffected either way since it already hides this badge outright
   (max-width:820px block, further down). */
.wf-version-pill { flex: none; }

.wf-editor-body {
  display: flex;
  flex: 1;
  min-height: 0;
}

/* ---- Step Library sidebar ---------------------------------------------------- */

.wf-step-library {
  width: 200px;
  flex: none;
  overflow-y: auto;
  padding: 14px 10px;
  border-right: 1px solid var(--task-border);
  background: var(--task-surface);
}
.wf-lib-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.wf-lib-close-btn { display: none; } /* shown only in the mobile overlay state, below */

/* § direct request: "give the user the ability to resize the left and
   right panels" — a thin draggable strip between the Step Library and
   the canvas, and between the canvas and Properties (workflows.js's own
   setupPanelResize()). Widened on hover/drag (not just a color change)
   so it's easy to actually grab despite being only 6px at rest. */
.wf-panel-resize-handle {
  flex: none; width: 6px; cursor: col-resize; background: transparent;
  position: relative; z-index: 5;
}
.wf-panel-resize-handle::after {
  content: ''; position: absolute; top: 0; bottom: 0; left: 2px; width: 2px;
  background: transparent; border-radius: 2px; transition: background-color 0.12s ease;
}
@media (hover: hover) { .wf-panel-resize-handle:hover::after { background: var(--task-accent-dim); } }
.wf-panel-resize-handle:active::after, body.wf-canvas-dragging .wf-panel-resize-handle::after { background: var(--task-accent); }

/* ---- Step properties panel header/footer (§ direct request: match the
   reference screenshots for Decision/Complete Form/Create Task) ------- */
.wf-prop-header { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 14px; }
.wf-prop-header-text { flex: 1 1 auto; min-width: 0; }
.wf-prop-title { margin: 0; font-size: 20px; font-weight: 800; color: var(--task-text); text-transform: none; letter-spacing: normal; }
.wf-prop-subtitle { margin: 2px 0 0; font-size: 13px; color: var(--task-text-dim); }
/* Bordered-square close button, replacing the plain ghost \u2715 this panel
   used to share with the Step Library sidebar's own header (still
   .wf-lib-header/.wf-lib-close-btn there, untouched) \u2014 matches the
   reference images' own outlined-square close button exactly. */
.wf-prop-close-btn {
  flex: none; width: 34px; height: 34px; border-radius: 8px;
  border: 1px solid var(--task-border); background: var(--task-surface);
  color: var(--task-text); display: flex; align-items: center; justify-content: center;
  cursor: pointer; font-size: 14px; padding: 0;
}
@media (hover: hover) { .wf-prop-close-btn:hover { background: var(--task-accent-dim); } }
.wf-prop-divider { height: 1px; background: var(--task-border); margin: 14px 0; }
.wf-prop-help { margin: 6px 0 0; font-size: 12px; color: var(--task-text-dim); }
.wf-prop-actions-row { display: flex; gap: 10px; }
.wf-prop-actions-row > button { flex: 1 1 0; }
/* Pale-red outline, not the solid-fill .task-btn-danger used elsewhere
   in the app \u2014 matches the reference images' own lighter Delete Step
   treatment specifically for this panel. */
.wf-prop-delete-btn { background: var(--task-red-bg); color: var(--task-red); border: 1px solid var(--task-red-bg); }
@media (hover: hover) { .wf-prop-delete-btn:hover { opacity: 0.85; } }
.wf-lib-category { margin-bottom: 14px; }
.wf-lib-category-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--task-text-faint);
  margin-bottom: 6px;
  padding: 0 4px;
}
/* § direct request (screenshot): each Step Library row is a colored
   icon badge, a bold title with a gray description line under it, and a
   trailing chevron — replaces the previous plain icon+label row. */
.wf-lib-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  padding: 8px;
  margin-bottom: 4px;
  border: 1px solid transparent;
  border-radius: var(--task-radius-sm);
  background: transparent;
  color: var(--task-text);
  font-size: 13px;
  cursor: grab;
  transition: background 0.12s ease, border-color 0.12s ease;
}
.wf-lib-item:hover:not(:disabled) { background: var(--task-accent-dim); border-color: var(--task-border); }
.wf-lib-item:disabled { opacity: 0.4; cursor: default; }
.wf-lib-item:active:not(:disabled) { cursor: grabbing; }
.wf-lib-icon-badge {
  flex: none; width: 36px; height: 36px; border-radius: 10px;
  display: flex; align-items: center; justify-content: center;
}
.wf-lib-icon-green  { background: var(--task-green-bg);  color: var(--task-green); }
.wf-lib-icon-red    { background: var(--task-red-bg);    color: var(--task-red); }
.wf-lib-icon-purple { background: var(--task-purple-bg); color: var(--task-purple); }
.wf-lib-icon-blue   { background: var(--task-blue-bg);   color: var(--task-blue); }
.wf-lib-icon-orange { background: var(--task-orange-bg); color: var(--task-orange); }
.wf-lib-icon-teal   { background: var(--task-teal-bg);   color: var(--task-teal); }
.wf-lib-icon-pink   { background: #fde7ef; color: #d6336c; }
.wf-lib-item-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.wf-lib-item-title { font-weight: 700; font-size: 13.5px; color: var(--task-text); overflow-wrap: break-word; }
/* § direct request: "wrap the text if needed" — was a single-line
   ellipsis truncation; now wraps onto as many lines as the description
   needs instead of cutting it off, which matters more now that the
   panel itself is user-resizable (§214) and can be narrowed quite a
   bit. .wf-lib-item's own align-items:center still centers the row
   vertically against the icon badge even when this grows taller. */
.wf-lib-item-desc {
  font-size: 11.5px; color: var(--task-text-faint);
  white-space: normal; overflow-wrap: break-word;
}
.wf-lib-chevron { flex: none; color: var(--task-text-faint); display: flex; }

/* ---- Canvas viewport / world / nodes ---------------------------------------- */

/* Wraps the scrolling .wf-canvas-viewport AND the floating FABs as
   siblings — the FABs must NOT be inside the scrolling element itself, or
   they'd scroll away with the canvas content instead of staying pinned to
   the visible corner (position:absolute inside an overflow:auto ancestor
   still scrolls with that ancestor's content unless it's outside it). */
.wf-canvas-area {
  position: relative;
  flex: 1;
  min-width: 0;
  display: flex;
}
.wf-canvas-viewport {
  flex: 1;
  overflow: auto;
  position: relative;
  background:
    radial-gradient(circle, var(--task-border) 1px, transparent 1px) 0 0 / 22px 22px;
  background-color: var(--task-bg);
  /* § direct request: "allow the user to pan around the canvas by
     holding left click and dragging" — grab/grabbing matches the exact
     same affordance .wf-node already uses for its own drag, so empty
     canvas and step nodes read as consistently "draggable" (just
     dragging different things). setupCanvasPanning() in workflows.js
     toggles .wf-canvas-panning only once real movement has happened,
     so a plain click (no drag) never flashes the grabbing cursor. */
  cursor: grab;
}
.wf-canvas-viewport.wf-canvas-panning { cursor: grabbing; }
.wf-canvas-sizer { position: relative; }
.wf-canvas-world {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
  width: 3000px;
  height: 1800px;
}
.wf-canvas-world svg { position: absolute; top: 0; left: 0; pointer-events: none; }
.wf-canvas-world svg .wf-conn { pointer-events: auto; cursor: pointer; touch-action: none; } /* see setupCanvasPanning()'s touch handling in workflows.js — a tap here needs to bypass the browser's OWN scroll-vs-tap heuristic, not just ours */
/* § bugfix: "make it possible to select and remove connector lines" —
   .wf-conn-hit is a wide but fully transparent stroke specifically so
   there's a forgiving invisible hit target over the thin visible line
   (see workflows.js's own template comment). The default SVG
   pointer-events value (visiblePainted) is only guaranteed to count a
   stroke as "painted" — and therefore hit-testable — when it has a
   non-none, non-fully-transparent color; whether literal `transparent`
   itself still counts is an inconsistently-implemented edge case across
   browsers, which is exactly the kind of thing that "looks like it
   should work" while silently not registering clicks on some devices.
   Setting pointer-events: stroke here removes that ambiguity outright —
   the stroke area hit-tests regardless of how "painted" it's judged to
   be. */
.wf-canvas-world svg .wf-conn-hit { cursor: pointer; pointer-events: stroke; }
/* § direct request: "for Decision steps, allow the user to edit the
   'Yes' and 'No' text on the connector... let them click the text to
   edit" — text cursor on the editable label only (Decision steps'
   connectors only — see renderCanvas()'s own gating in workflows.js),
   overriding the plain .wf-conn pointer cursor above for just this
   child element so hovering the label itself reads as "click to
   rename" rather than "click to select this connector". */
.wf-conn-label-editable { cursor: text; }
/* Same fixed-position-input-overlay pattern .wf-title-text-input above
   already uses for the workflow title rename — positioned in JS
   (startConnectionLabelEdit()) directly over the SVG label's own
   getBoundingClientRect(), so it lines up regardless of current pan/
   zoom. Smaller font-size (11px) to match the SVG <text> it replaces. */
.wf-conn-label-input {
  position: fixed; z-index: 100;
  font-size: 11px; font-family: inherit; color: var(--task-text);
  text-align: center;
  border: 1px solid var(--task-accent); border-radius: 4px;
  padding: 0 4px; background: var(--task-surface-raised);
  box-shadow: var(--task-shadow);
}
#wf-canvas-nodes { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }

.wf-node {
  position: absolute;
  background: var(--task-surface);
  /* § direct request: "add a dark gray border around steps" — a fixed
     gray (rather than --task-border, which is a light steel tone meant
     for subtle dividers) so the node outline reads clearly against
     --task-surface in both light and dark theme. */
  border: 1.5px solid #4b5563;
  border-radius: 16px;
  box-shadow: var(--task-shadow);
  padding: 10px 12px;
  cursor: grab;
  user-select: none;
  /* § bugfix ("can't connect Steps in the canvas" on mobile): without
     this, a touch-drag starting on a node gets intercepted by the
     browser's own scroll/pan handling of the surrounding
     `overflow:auto` .wf-canvas-viewport before pointermove ever sees
     it — the exact same class of bug already documented and fixed this
     way for every other custom drag interaction in this app (drag-to-
     reorder handles, popup-designer blocks, etc. — see tasks.css's own
     comment on this). Applies to node-dragging (startDragStep) and,
     via .wf-port below, to connection-drawing (startDrawConnection). */
  touch-action: none;
  /* § direct request ("don't highlight anything and don't show the iOS
     focus view" while dragging on mobile) — the same paired fix this
     app already uses everywhere else for a custom touch-drag surface
     (see app.css's/designer.css's own drag-handle comments on this):
     -webkit-touch-callout suppresses iOS's long-press callout/magnifier
     (the "focus view"), -webkit-user-select backs up the standard
     user-select above on WebKit specifically. */
  -webkit-touch-callout: none;
  -webkit-user-select: none;
}
.wf-node:active { cursor: grabbing; }
.wf-node.selected { box-shadow: 0 0 0 3px var(--task-accent-dim), var(--task-shadow); }
.wf-node-terminal { border-radius: 999px; background: var(--task-navy); }
.wf-node-terminal .wf-node-type, .wf-node-terminal .wf-node-name { color: #fff; }
.wf-node-row { display: flex; align-items: center; gap: 8px; }
.wf-node-text { min-width: 0; flex: 1 1 auto; }
.wf-node-type { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; color: var(--task-text-faint); }
.wf-node-name { font-size: 13px; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Icon badges — one small colored circle per node, replacing the old
   colored-left-border accent strip (the reference design has no border
   accent at all; the color-coding lives entirely in this badge now). */
.wf-node-icon-badge {
  flex: none; width: 26px; height: 26px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
}
.wf-badge-start { background: var(--task-green); color: #fff; }
.wf-badge-end { background: var(--task-red); color: #fff; }
.wf-badge-automated { background: var(--task-accent-yellow); color: #8a5a00; }
.wf-badge-human { background: var(--task-purple-bg); color: var(--task-purple); }

/* § direct request: "remove the 3 horizontal dots at the top left of
   every step card" — .wf-node-menu-btn (the per-node quick-delete menu
   button) is gone; Delete Step lives only in the Properties panel now.

   § direct request: "the circle anchor on the preceding step should
   follow the lines instead of staying static on the right edge" — the
   base rule now carries only shape/appearance; each edge gets its own
   positioning modifier (right/top/bottom), chosen per-port in
   renderStepNode() from the exact same relationship the connector line
   itself is drawn with (see renderCanvas()'s connectionRelationship()),
   so a port's dot and its own line always agree on where it sits. */
.wf-port {
  position: absolute;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--task-surface);
  border: 2.5px solid var(--task-accent);
  box-shadow: var(--task-shadow);
  cursor: crosshair;
  touch-action: none; /* see .wf-node's own comment on this — same fix, same reason, the actual reported symptom */
  -webkit-touch-callout: none; /* see .wf-node's own comment on this */
  -webkit-user-select: none;
  user-select: none;
}
.wf-port:hover { background: var(--task-accent); }
/* § direct request: "if steps are connected and properties are filled
   out correctly the connector circle should turn green" — wf-port-
   complete is only added (see renderStepNode() in workflows.js) once a
   port has an outgoing connection AND both connected steps' own config
   is filled in; :hover above still wins for the port being interacted
   with, so this doesn't fight normal port hover feedback. */
.wf-port-complete { background: var(--task-green); border-color: var(--task-green); }
/* § direct request: "implement the parallel branches functionality" —
   Parallel Split's spare dot (renderStepNode()'s own displayPorts logic)
   has no real connection yet; dashed rather than solid so it visually
   reads as "drag from here to add a branch" rather than an already-wired
   port waiting to turn green. */
.wf-port-spare { border-style: dashed; opacity: 0.7; }
.wf-port-spare:hover { opacity: 1; }
/* Default (and unconnected-port) position — the node's right edge, at
   whichever vertical fraction renderStepNode() sets inline for this
   port's index among however many the node has. */
.wf-port-right { right: -8px; margin-top: -8px; }
.wf-port-top { top: -8px; left: 50%; transform: translateX(-50%); }
.wf-port-bottom { bottom: -8px; left: 50%; transform: translateX(-50%); }
/* § bugfix: "the arrow head disappears when the preceding step moves to
   the right of the next step" — connectionRelationship() now recognizes
   that horizontal-reversal case too (see workflows.js), routing the
   connector out the source's LEFT edge instead of its right. This is
   that edge's dot position, mirroring .wf-port-right exactly (same
   inline top:N% + this same -8px vertical centering margin). */
.wf-port-left { left: -8px; margin-top: -8px; }

/* Floating canvas controls, bottom-right: § direct request ("move the
   Undo and Redo buttons to the left of the three buttons in the
   canvas") — Undo/Redo (their own .wf-undo-redo-group, relocated here
   from the header row) now lead this group, followed by the original
   three: center, fit-to-extent, and add-step (mobile only). */
.wf-canvas-fabs {
  position: absolute; right: 14px; bottom: 14px;
  display: flex; align-items: center; gap: 8px; z-index: 10;
}
.wf-canvas-fab {
  width: 42px; height: 42px; border-radius: 12px; border: none;
  background: var(--task-surface); color: var(--task-text); box-shadow: var(--task-shadow);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
@media (hover: hover) { .wf-canvas-fab:hover { background: var(--task-accent-dim); } }
/* Accent fill so add-step still stands out among the neutral center/fit
   pair — the same treatment this button has carried in every position
   it's occupied so far (header icon circle, and now here again). */
.wf-canvas-fab-add { background: var(--task-accent); color: #fff; }
@media (hover: hover) { .wf-canvas-fab-add:hover { background: var(--task-accent); opacity: 0.9; } }
/* § direct request: "on desktop only add zoom buttons in the canvas to
   the left of the full extent and center buttons" — hidden by default
   (mobile keeps pinch-to-zoom only, same as before this existed); the
   min-width:821px block further down turns it into `display:contents`
   so its two .wf-canvas-fab buttons become direct flex items of
   .wf-canvas-fabs itself and pick up that same 8px gap, rather than
   needing their own separate spacing rule here. */
.wf-zoom-group { display: none; }

/* ---- Properties panel -------------------------------------------------------- */

.wf-properties {
  width: 280px;
  flex: none;
  overflow-y: auto;
  padding: 14px;
  border-left: 1px solid var(--task-border);
  background: var(--task-surface);
}

/* ---- Project Workflows tab: instance cards ---------------------------------- */

.wf-instance-card { cursor: pointer; }

/* ---- Workflow Instance Detail: progress + history --------------------------- */

.wf-progress-list { display: flex; flex-direction: column; gap: 2px; }
.wf-progress-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 8px 4px;
  border-bottom: 1px solid var(--task-border);
}
.wf-progress-row:last-child { border-bottom: none; }
.wf-progress-mark {
  flex: none;
  width: 20px;
  text-align: center;
  font-size: 14px;
  line-height: 1.6;
  color: var(--task-text-faint);
}
.wf-progress-row.done .wf-progress-mark { color: var(--task-green); }
.wf-progress-row.current .wf-progress-mark { color: var(--task-accent); }
.wf-progress-row.failed .wf-progress-mark { color: var(--task-red); }
.wf-progress-row.skipped .wf-progress-mark { color: var(--task-text-faint); }
.wf-progress-row.skipped .wf-progress-name { color: var(--task-text-faint); text-decoration: line-through; }
.wf-progress-name { font-size: 14px; font-weight: 600; }

.wf-history-list { display: flex; flex-direction: column; gap: 6px; }
.wf-history-row {
  display: flex;
  gap: 10px;
  font-size: 13px;
  padding: 4px 2px;
}
.wf-history-row .task-faint { flex: none; width: 140px; }

/* ---- Project Workflows tab: Board (kanban) view ------------------------------ */

.wf-board {
  display: flex;
  gap: 12px;
  overflow-x: auto;
  padding-bottom: 8px;
  -webkit-overflow-scrolling: touch;
}
.wf-board-column {
  flex: 0 0 240px;
  background: var(--task-gray-bg);
  border-radius: var(--task-radius-sm);
  padding: 10px;
  max-height: calc(100vh - 320px);
  display: flex;
  flex-direction: column;
}
.wf-board-column-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  font-size: 13px;
  font-weight: 700;
  margin-bottom: 8px;
  padding: 0 2px;
}
.wf-board-column-body {
  display: flex;
  flex-direction: column;
  gap: 8px;
  overflow-y: auto;
}
.wf-board-card { cursor: pointer; padding: 10px; }

/* ---- My Work: Workflow Steps section ----------------------------------------- */

.wf-mywork-card { margin-bottom: 14px; }
.wf-mywork-list { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }
.wf-mywork-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  padding: 10px 8px;
  border: 1px solid var(--task-border);
  border-radius: var(--task-radius-sm);
  background: var(--task-surface);
  cursor: pointer;
  min-height: 44px; /* spec "MOBILE": large touch targets */
}
.wf-mywork-row:hover { background: var(--task-accent-dim); }
.wf-mywork-name { font-size: 14px; font-weight: 600; }

/* Mobile: both the Step Library and the Properties panel are plain
   `display:none` by default below 820px (there's no room for a persistent
   side panel on a phone), but each becomes reachable as a full-screen
   overlay on demand — Step Library via the header's "+" add-step button
   (#wf-add-step-btn, shown at every width now — see openEditor()'s own
   comment on why that's safe on desktop too), Properties automatically
   the moment something is selected on the canvas (renderProperties()
   itself toggles .wf-mobile-open). Both overlays share the identical
   treatment and the same .wf-lib-close-btn close-button class. */
@media (max-width: 820px) {
  .wf-properties, .wf-step-library { display: none; }
  .wf-panel-resize-handle { display: none; }
  .wf-properties.wf-mobile-open,
  .wf-step-library.wf-mobile-open {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 60;
    width: 100%;
    height: 100%;
    padding: 14px;
    border-right: none;
    border-left: none;
    overflow-y: auto;
  }
  .wf-lib-close-btn { display: inline-flex; }
  /* § direct request: "on mobile remove the badge in the header" — the
     version pill (e.g. "V2 DRAFT") tucked under the workflow title.
     Desktop keeps it (.wf-title-group still reserves the stacked-badge
     layout there); this only hides the pill element itself on narrow
     screens, so .wf-title-text above it is unaffected. */
  .wf-version-pill { display: none; }
  /* A tighter gap here (still not the header row's full 6px) leaves a
     little more room before the row's own overflow-x:auto scroller
     (above) has to kick in at all. */
  .wf-header-row { gap: 4px; }
}
/* § "move the '+' button back to... the canvas" — back to being
   redundant with the always-visible Step Library sidebar on desktop,
   the same reasoning that gated it to mobile-only the first time this
   button lived on the canvas (before it moved into the header and back
   again). */
@media (min-width: 821px) {
  .wf-canvas-fab-add { display: none; }
  /* § direct request: "on desktop only add zoom buttons in the canvas
     to the left of the full extent and center buttons" — display:contents
     (not display:flex) so the two zoom buttons become direct flex items
     of .wf-canvas-fabs itself, picking up its existing 8px gap instead
     of needing a separate one of their own. */
  .wf-zoom-group { display: contents; }
  .wf-static-title { display: inline-block; font-size: 15px; font-weight: 700; color: var(--task-text); white-space: nowrap; margin-left: 2px; }
  /* Test/Save go from icon-only squares to icon+label pills at this
     width — .wf-icon-square-btn's own 34px fixed width has to give way
     to auto/padding for the label to have room. */
  .wf-icon-square-btn { width: auto; min-width: 34px; padding: 0 12px; gap: 6px; }
  .wf-btn-label { display: inline; font-size: 13px; font-weight: 700; }
}

/* ---- Decision condition field-path combobox --------------------------------
   § bugfix: replaces a plain <input list=datalist> (workflows.js's
   renderFieldPathCombobox()) — see that function's own comment for why a
   hand-rolled dropdown was needed instead of the browser's native one. */
.wf-combobox { position: relative; flex: 1; min-width: 140px; }
.wf-combobox .wf-cr-field { width: 100%; }
.wf-combobox-list {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 20;
  max-height: 220px; overflow-y: auto;
  background: var(--task-surface); border: 1px solid var(--task-border);
  border-radius: var(--task-radius-sm); box-shadow: var(--task-shadow);
  padding: 4px; display: flex; flex-direction: column;
}
.wf-combobox-item {
  display: block; width: 100%; text-align: left; padding: 7px 8px; border-radius: 6px;
  border: none; background: none; font-size: 12.5px; color: var(--task-text); cursor: pointer;
}
.wf-combobox-item:hover { background: var(--task-accent-dim); }
/* § bugfix: "dropdown remains stuck open" — .wf-combobox-list's own
   `display: flex` above has the same specificity as the UA stylesheet's
   `[hidden] { display: none }` and comes later in the cascade, so it was
   silently winning and the list never actually hid when JS set
   list.hidden = true (on selecting an item OR clicking away). This
   explicit [hidden] override (same pattern as .mb-tab-panel[hidden] in
   app.css) restores the attribute's effect. */
.wf-combobox-list[hidden] { display: none; }

/* ---- Decision step's Conditions section (§ direct request: match the
   reference screenshot) ------------------------------------------------ */
/* The "Match ALL of the following conditions:" + condition rows sit in
   their own light-gray box; "+ Add Condition"/"+ Add Group" stay OUTSIDE
   it as their own row below \u2014 matches the reference image's own
   grouping exactly. A nested group (workflows.js's own dashed-border
   wrapper around a recursive renderWorkflowConditionGroup() call) gets
   one of these boxes too, nested inside that dashed border \u2014 a
   reasonable "each level gets its own container" look rather than a
   special case for depth. */
.wf-cond-box { background: var(--task-gray-bg); border-radius: var(--task-radius-sm); padding: 14px; }
.wf-cond-btn-outline {
  flex: 1 1 0;
  background: var(--task-surface); color: var(--task-accent); border: 1px solid var(--task-accent);
}
@media (hover: hover) { .wf-cond-btn-outline:hover { background: var(--task-accent-dim); } }
/* Bordered-square remove button, matching .wf-prop-close-btn's own
   treatment rather than the plain ghost \u2715 this row used to use. */
.wf-cr-remove-btn {
  flex: none; width: 34px; height: 34px; border-radius: 8px;
  border: 1px solid var(--task-border); background: var(--task-surface);
  color: var(--task-text); display: flex; align-items: center; justify-content: center;
  cursor: pointer; font-size: 14px; padding: 0;
}
@media (hover: hover) { .wf-cr-remove-btn:hover { background: var(--task-accent-dim); } }

/* § bugfix: "when linking steps, it is highlighting things on the
   screen" — toggled on document.body for the exact duration of a
   step-drag or connection-draw (startDragStep/startDrawConnection in
   workflows.js), suppressing text selection document-wide rather than
   only within the canvas. .wf-node/.wf-port's own user-select:none only
   stops a selection from STARTING there; it does nothing to stop the
   cursor selecting text elsewhere on the page (the Properties panel,
   header, etc) as it sweeps across during the drag. */
body.wf-canvas-dragging { user-select: none; -webkit-user-select: none; }
