/* =====================================================================
   planflow-blocks.css — PlanFlow block-card design, extracted verbatim
   =====================================================================
   SOURCE: planflow_handover/workflow-template.html (prototype host shell)
     · inline <style> #1  — tokens.css / blocks.css / run.css sections
     · inline <style> #2  — plan-groups v2 module additions that restyle
       the block card (silhouette selection halo, quiet ports, repeat
       button active state, running-title yield)
   Companion fidelity checklist: docs/planflow/BLOCK_CARD_EXTRACT.md
   (DOM template, state-class list, animation/transition inventory).

   TIMINGS/EASINGS ARE SACRED. Every transition and animation duration
   in this file is copied EXACTLY from the source — do not "round",
   unify, or re-tune them. The ~540ms corridor shift used when frames
   fold/unfold (.wf-block--shifting et al.) belongs to CANVAS logic and
   is deliberately NOT in this file.

   FEATURE FLAG: every top-level selector is mechanically prefixed with
   .aw-newblocks (a body-level flag class), so this stylesheet is inert
   until <body class="aw-newblocks"> is set. Selectors that started
   with `body.` in the source become `body.aw-newblocks.…` (the flag
   lives on body, so a descendant prefix could never match).
   @keyframes keep their plain source names.

   FLAG-ON SCOPING (adversarial-verify fix): the prototype's container
   class `.wf-block` COLLIDES with the sidebar's workflow-tree rows
   (sidebar-nav.js emits "wf-item wf-block"), and the prototype's
   generic `[data-tip]` tooltip generator collides with the topbar's
   JS-tooltip buttons (topbar.js sets data-tip). Because the flag class
   lives on <body>, plain `.aw-newblocks .wf-block` / `.aw-newblocks
   [data-tip]` would restyle both while the flag is on. So every
   standalone `.wf-block` here is written `.block.wf-block` (canvas
   nodes carry BOTH classes; sidebar rows never have `.block`), and the
   tooltip GENERATOR rules are scoped to `.block.wf-block [data-tip]`.
   Rule BODIES stay byte-identical to the source; only selector
   prefixes changed. BEM child classes (.wf-block__*) are unique and
   keep their plain form.

   NOTE — the block art references document-level SVG defs that are
   MARKUP, not CSS: url(#wf-pulse-grad) and url(#wf-pulse-glow) must be
   present in the host page (see BLOCK_CARD_EXTRACT.md §4).

   NOTE — design tokens are scoped to the block-design roots (.wf-block
   and .wf-output-overlay) instead of the body flag element, so PlanFlow
   values for generic names (--text, --border, --accent, …) cannot
   clobber AirWanderer's own :root tokens while the flag is on.
   ===================================================================== */

/* ===== tokens (source: tokens.css :root) ============================= */
/* round 27e: the folded group-frame card (.wf-block.wf-frame-card, deliberately not a
   .block) wears the same tokens - measured 2026-10-19: without them its toolbar buttons
   rendered transparent, dark and shadowless beside a normal card's */
.aw-newblocks .block.wf-block,
.aw-newblocks .wf-block.wf-frame-card,
.aw-newblocks .canvas-item.is-simple-frame,
.aw-newblocks .wf-output-overlay{
  --surface:rgba(255,255,255,.72);
  --surface-solid:#FBFCFA;
  --border:rgba(255,255,255,.70);
  --divider:rgba(70,92,80,.16);
  --text:#1F2D26;
  --label:#41584C;
  --label-hover:#27543C;
  --icon:#3C5247;
  --muted:#6E8077;
  --accent:#5C9A7E;
  --accent-line:rgba(92,154,126,.45);
  --row-hover:rgba(60,74,67,.06);
  --header-hover:rgba(56,120,90,.09);
  --active-text:#235446;
  --focus:rgba(60,120,92,.55);
  --cta:#1F6E45;
  --dot-green:#4FA37C;
  --dot-black:#2B332E;
  --dot-gray:#A7B0AA;
  --port-bg:#FBFCFA;
  --port-stroke:rgba(70,92,80,.32);
  --conn:rgba(92,154,126,.65);
  --conn-hover:#1B9264;
  --loop:#7A5AF8;
  --loop-hover:#5B3DCC;
  --flash:rgba(92,154,126,.85);
  --shadow-rest:0 6px 18px rgba(55,72,64,.10);
  --shadow-hover:0 14px 34px rgba(55,72,64,.18);
  --shadow-drag:0 24px 56px rgba(55,72,64,.28);
  --shadow-pop:inset 0 1px 0 rgba(255,255,255,.6),0 10px 30px rgba(55,72,64,.18);
  --blur:blur(20px) saturate(180%) brightness(1.03);
  --ease:cubic-bezier(.32,.72,0,1);
  --dur:.24s;
}

/* ===== block card (source: blocks.css) =============================== */
.aw-newblocks .block.wf-block{
  /* Counter-transform fidelity fix (screenshot-verified): the card
     subtree renders with the prototype's OWN pixel recipe (--scale
     0.72, fixed inner px), and --pf-zoom-comp (set by canvas.init to
     1/BASE_SCALE) cancels the canvas's internal base zoom so a card
     at display-100% is pixel-identical to the prototype at 100%.
     Layout box stays 298px; visual box = layout × comp × zoom. */
  transform:scale(var(--pf-zoom-comp, 1));
  transform-origin:0 0;
  --scale:0.72;
  position:absolute;
  width:calc(414px * var(--scale));
  color:var(--text);font-family:inherit;z-index:2;
  transition:filter var(--dur) var(--ease);
  filter:drop-shadow(var(--shadow-rest));
  pointer-events:none;
}
.aw-newblocks .wf-block__port,.aw-newblocks .wf-block__drag,.aw-newblocks .wf-block__subport__dot{pointer-events:auto}
.aw-newblocks .block.wf-block:hover{filter:drop-shadow(var(--shadow-hover))}
.aw-newblocks .block.wf-block:has(.wf-block__port.is-open){z-index:20}
.aw-newblocks .block.wf-block.is-dragging{
  filter:drop-shadow(var(--shadow-drag));
  z-index:50;user-select:none;
  transition:none;
}
/* Card is pointer-events:auto so hovering anywhere on the block keeps
   the block in :hover. min-height matches the original 414×201 design
   ratio — the output panel uses normal flow to sit beneath the outer
   layer, so the card grows naturally when output is present. */
.aw-newblocks .wf-block__card{
  position:relative;display:block;
  pointer-events:auto;
}
.aw-newblocks .wf-block__card>svg{width:100%;height:auto;display:block;pointer-events:none}
/* While the connector panel grows/shrinks, clip the card to its animating
   height. Released once settled so tooltips and hover controls (which
   only appear at rest) are never clipped. */
.aw-newblocks .block.wf-block.is-cb-animating .wf-block__card{ overflow:hidden; }

/* =============================================================
   LAYER CONTAINERS — each visual piece of the block (outer card,
   inner card, panel, title bar, badge, GPT logo) is a real HTML
   node holding one inline SVG fragment, sized to that piece's
   ACTUAL bounding box in the 414×201 SVG coordinate space.

   Bounding boxes (in the 414×201 SVG coord space):
     outer:     (0, 0)   414×201   — full block, the only layer in flow
     inner:     (60, 18) 336×158   — secondary card
     panel:     (74, 76) 306×85    — innermost panel + dividers
     title-bar: (105, 18) 292×47   — green tab
     badge:     (9, 14)  78×82     — AI badge + S1 corner tab
     gpt:       (137, 92) 17×17    — chat-bubble logo
   ============================================================= */
.aw-newblocks .wf-block__layer{
  position:absolute;
  pointer-events:none;
  overflow:visible;
}
.aw-newblocks .wf-block__layer > svg{
  width:100%;height:100%;display:block;overflow:visible;
}

.aw-newblocks .wf-block__layer--outer{
  position:relative;
  width:calc(414px * var(--scale));
  height:calc((201 + var(--card-oh, var(--output-h, 0)) + var(--cb-h, 0)) * 1px * var(--scale));
  z-index:1;
}
.aw-newblocks .wf-block__layer--inner{
  top:calc(18px * var(--scale));
  left:calc(60px * var(--scale));
  width:calc(336px * var(--scale));
  height:calc((158 + var(--card-oh, var(--output-h, 0)) + var(--cb-h, 0)) * 1px * var(--scale));
  z-index:2;
}
.aw-newblocks .wf-block__layer--panel{
  top:calc(76px * var(--scale));
  left:calc(74px * var(--scale));
  width:calc(306px * var(--scale));
  height:calc((85 + var(--cb-h, 0)) * 1px * var(--scale));
  z-index:3;
}
.aw-newblocks .wf-block__layer--title-bar{
  top:calc(18px * var(--scale));
  left:calc(105px * var(--scale));
  width:calc(292px * var(--scale));
  height:calc(47px * var(--scale));
  z-index:4;
}
.aw-newblocks .wf-block__layer--gpt{
  top:calc(92px * var(--scale));
  left:calc(137px * var(--scale));
  width:calc(17px * var(--scale));
  height:calc(17px * var(--scale));
  z-index:5;
}
.aw-newblocks .wf-block__layer--badge{
  top:calc(14px * var(--scale));
  left:calc(9px * var(--scale));
  width:calc(78px * var(--scale));
  height:calc(82px * var(--scale));
  z-index:6;
}

/* HTML title text — child of the title-bar layer. */
.aw-newblocks .wf-block__title{
  position:absolute;
  top:calc(15px * var(--scale));
  left:calc(20px * var(--scale));
  font-family:inherit;
  font-size:calc(16px * var(--scale));
  font-weight:600;
  color:#1F2D26;
  letter-spacing:.005em;
  line-height:1.3;
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
  max-width:calc(260px * var(--scale));
  z-index:7;
  pointer-events:none;
  user-select:none;
}

/* HTML provider name — child of the panel layer. */
.aw-newblocks .wf-block__provider-name{
  position:absolute;
  top:calc(15.7px * var(--scale));
  left:calc(97px * var(--scale));
  font-family:inherit;
  font-size:calc(13.5px * var(--scale));
  font-weight:500;
  color:#1F2D26;
  line-height:1.3;
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
  max-width:calc(105px * var(--scale));
  z-index:7;
  pointer-events:none;
  user-select:none;
}

/* HTML S1 step text — centered over the badge's dark corner tab. */
.aw-newblocks .wf-block__badge-step{
  position:absolute;
  top:calc(2px * var(--scale));
  left:calc(7px * var(--scale));
  width:calc(20px * var(--scale));
  height:calc(15px * var(--scale));
  display:flex;align-items:center;justify-content:center;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:calc(11px * var(--scale));
  font-weight:600;
  color:#fff;
  line-height:1;
  letter-spacing:.02em;
  z-index:1;
  pointer-events:none;
  user-select:none;
}

.aw-newblocks .wf-block__drag{
  position:absolute;
  left:calc(106px * var(--scale)); top:calc(19px * var(--scale));
  width:calc(290px * var(--scale)); height:calc(46px * var(--scale));
  cursor:grab;background:transparent;touch-action:none;
  z-index:8;
}
.aw-newblocks .wf-block__drag:active,
.aw-newblocks .block.wf-block.is-dragging .wf-block__drag{cursor:grabbing}

.aw-newblocks .wf-block__statusbox{
  position:absolute;
  left:calc(208px * var(--scale));
  top:calc(10.5px * var(--scale));
  width:calc(94px * var(--scale));
  height:calc(28px * var(--scale));
  background:transparent;
  display:flex;align-items:center;
  padding:0 calc(2px * var(--scale)) 0 calc(10px * var(--scale));
  pointer-events:none;
  user-select:none;
  overflow:hidden;
  min-width:0;
  z-index:8;
}
/* SVG already draws the vertical divider line — hide the CSS one. */
.aw-newblocks .wf-block__statusbox__sep{display:none}
.aw-newblocks .wf-block__statuspill{
  display:inline-flex;align-items:center;
  gap:calc(6px * var(--scale));
  font-size:calc(13.5px * var(--scale));
  color:var(--text);line-height:1;letter-spacing:.005em;
  min-width:0;flex:1;
}
.aw-newblocks .wf-block__statuspill__text{
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  flex:1;
}
.aw-newblocks .wf-block__statuspill__dot{
  width:calc(7px * var(--scale));
  height:calc(7px * var(--scale));
  border-radius:50%;
  background:var(--dot-green);
  flex:none;
  box-shadow:0 0 0 calc(1.5px * var(--scale)) rgba(79,163,124,.15);
}
/* NB: the running dot is styled by the spinner rule in the run-state
   section — do NOT set a solid background / halo here for is-running. */
.aw-newblocks .block.wf-block.is-failed .wf-block__statuspill__dot{
  background:var(--dot-black);
  box-shadow:0 0 0 calc(1.5px * var(--scale)) rgba(43,51,46,.18);
}
.aw-newblocks .block.wf-block.is-idle .wf-block__statuspill__dot{
  background:var(--dot-gray);
  box-shadow:0 0 0 calc(1.5px * var(--scale)) rgba(167,176,170,.18);
}

.aw-newblocks .wf-block__metrics{
  position:absolute;
  left:calc(9px * var(--scale));
  top:calc(50px * var(--scale));
  width:calc(291px * var(--scale));
  height:calc(28px * var(--scale));
  background:transparent;
  /* space-between, not the template's space-around: the owner's annotated
     screenshot puts the time metric further left, with cost centred and
     tokens right. Same three slots, pushed out to the row's edges. */
  display:flex;align-items:center;justify-content:space-between;
  padding:0 calc(6px * var(--scale));
  pointer-events:auto;font-variant-numeric:tabular-nums;
  overflow:hidden;min-width:0;
  z-index:8;
}
.aw-newblocks .wf-block__metric{display:flex;align-items:center;
  gap:calc(7px * var(--scale));
  font-size:calc(13.5px * var(--scale));color:var(--text);
  line-height:1;white-space:nowrap;
  min-width:0;overflow:hidden;flex:0 1 auto;
}
.aw-newblocks .wf-block__metric>span:last-child{
  min-width:0;overflow:hidden;text-overflow:ellipsis;
}
.aw-newblocks .wf-block__metric svg{width:calc(13px * var(--scale));height:calc(13px * var(--scale));
  flex:none;color:var(--icon);display:block}
.aw-newblocks .wf-block__msep{width:1px;height:calc(14px * var(--scale));
  background:var(--divider);flex:none}

/* Legacy connection handles become INVISIBLE HIT-ZONES on the new cards.
   components.css paints them as a small bordered dot (.handle, 350-374)
   revealed on hover/selection — beside a PlanFlow card that reads as a
   stray dash floating next to the block (owner-reported). The card draws
   its own port chevrons, so the legacy dot must not paint at all; but the
   element itself is load-bearing — the chevron gesture machine hit-tests
   .handle-in / .handle-out (planflow-blocks.js _hgHandleFrom) and canvas
   wires drags through it. So: strip every painted property, keep geometry,
   opacity and pointer-events exactly as components.css set them, and
   neutralise the hover feedback that would re-reveal it. Scoped to
   .block.wf-block, so legacy blocks and the repeat handles are untouched. */
.aw-newblocks .block.wf-block .handle,
.aw-newblocks .block.wf-block .handle:hover{
  background:transparent;
  border-color:transparent;
  box-shadow:none;
}
.aw-newblocks .block.wf-block .handle:hover{ transform:translateY(-50%); }
.aw-newblocks .block.wf-block .handle.handle-out.connecting{
  background:transparent;
  transform:translateY(-50%);
}

/* ===== ports (chips + popup rows) ==================================== */
.aw-newblocks .wf-block__port{
  position:absolute;top:50%;
  display:flex;align-items:center;
  pointer-events:auto;cursor:pointer;
  background:none;border:0;padding:0;margin:0;
  font-family:inherit;z-index:4;
}
.aw-newblocks .wf-block__port:focus-visible{outline:0}
.aw-newblocks .wf-block__port:focus-visible .wf-block__port__chip{
  box-shadow:0 2px 6px rgba(55,72,64,.10),0 0 0 2px var(--focus);
}
.aw-newblocks .wf-block__port__chip{
  display:flex;align-items:center;gap:5px;
  height:24px;padding:0 10px;
  background:var(--port-bg);
  border:1px solid var(--port-stroke);
  border-radius:999px;
  color:var(--label);
  font-size:11px;font-weight:600;line-height:1;
  box-shadow:0 2px 6px rgba(55,72,64,.10);
  transition:background .15s var(--ease),border-color .15s var(--ease),color .15s var(--ease);
}
.aw-newblocks .wf-block__port__chip svg{width:11px;height:11px;color:var(--muted);
  transition:color .15s var(--ease)}
.aw-newblocks .wf-block__port:hover .wf-block__port__chip,
.aw-newblocks .wf-block__port.is-open .wf-block__port__chip{
  border-color:var(--accent-line);color:var(--active-text);
}
.aw-newblocks .wf-block__port:hover .wf-block__port__chip svg,
.aw-newblocks .wf-block__port.is-open .wf-block__port__chip svg{color:var(--accent)}

.aw-newblocks .wf-block__port--in{ left:0;transform:translate(-100%,-50%);padding-right:6px}
.aw-newblocks .wf-block__port--out{right:0;transform:translate(100%,-50%);padding-left:6px}
.aw-newblocks .wf-block__port::before{
  content:"";position:absolute;top:50%;
  width:6px;height:1px;background:var(--port-stroke);
}
.aw-newblocks .wf-block__port--in::before{right:0}
.aw-newblocks .wf-block__port--out::before{left:0}

.aw-newblocks .wf-block__subport{
  display:flex;align-items:center;gap:8px;
  padding:6px 10px;border-radius:7px;
  background:transparent;border:0;font:inherit;
  font-size:12px;color:var(--label);white-space:nowrap;width:100%;text-align:left;
  transition:background .12s var(--ease),color .12s var(--ease);
  cursor:default;position:relative;
}
.aw-newblocks .wf-block__port--in .wf-block__subport{flex-direction:row}
.aw-newblocks .wf-block__port--out .wf-block__subport{flex-direction:row-reverse}
.aw-newblocks .wf-block__subport__name{flex:1;overflow:hidden;text-overflow:ellipsis}
.aw-newblocks .wf-block__subport:hover{background:var(--row-hover);color:var(--label-hover)}

.aw-newblocks .wf-block__subport__dot{
  position:relative;z-index:5;
  width:10px;height:10px;border-radius:50%;
  background:var(--cta);                         /* same color on both sides */
  box-shadow:0 0 0 2px var(--port-bg);
  flex:none;cursor:crosshair;
  transition:transform .15s var(--ease),box-shadow .15s var(--ease);
  touch-action:none;
}
.aw-newblocks .wf-block__subport__dot:hover{
  transform:scale(1.35);
  box-shadow:0 0 0 2px var(--port-bg),0 0 0 4px rgba(31,110,69,.35);
}
.aw-newblocks .wf-block__subport__dot.is-connected{
  box-shadow:0 0 0 2px var(--port-bg),0 0 0 3px var(--accent-line);
}
.aw-newblocks .wf-block__subport__dot.is-connected:hover{
  box-shadow:0 0 0 2px var(--port-bg),0 0 0 4px rgba(31,110,69,.40);
}

/* ============================================================
   INTEGRATED CONNECTOR PANEL (CB) — port chip clicked → the
   model/metrics info fades out and a panel folds into the card
   body showing that side's ports as leader-stub rows. Opening
   both sides splits the body into two columns. The card grows
   downward (via --cb-h) to fit the rows.
   ============================================================ */
.aw-newblocks .wf-block__cb{
  position:absolute;
  top:calc(126px * var(--scale));      /* just below the panel's status/divider row */
  left:calc(74px * var(--scale));      /* panel left edge */
  width:calc(306px * var(--scale));    /* panel width */
  display:none;
  z-index:7;
  pointer-events:none;
}
.aw-newblocks .block.wf-block:has(.wf-block__port.is-open) .wf-block__cb{ display:block; }
.aw-newblocks .block.wf-block:has(.wf-block__port.is-open) .wf-block__metrics,
.aw-newblocks .block.wf-block:has(.wf-block__port.is-open) .wf-block__output{
  opacity:0;pointer-events:none;
  transition:opacity .2s var(--ease);
}

.aw-newblocks .wf-block__cb-boxes{ display:flex;align-items:stretch;pointer-events:none; }
.aw-newblocks .wf-block__cb-box{
  display:none;flex-direction:column;min-width:0;flex:1 1 0;
  pointer-events:auto;
}
.aw-newblocks .block.wf-block:has(.wf-block__port--in.is-open)  .wf-block__cb-box--in { display:flex; }
.aw-newblocks .block.wf-block:has(.wf-block__port--out.is-open) .wf-block__cb-box--out{ display:flex; }
/* Both open → a divider between the two equal halves. */
.aw-newblocks .block.wf-block:has(.wf-block__port--in.is-open):has(.wf-block__port--out.is-open) .wf-block__cb-box--in{
  border-right:1px solid var(--panel-stroke, #D9DED9);
}
.aw-newblocks .wf-block__cb-col{ min-width:0;display:flex;flex-direction:column;gap:calc(1px * var(--scale));min-height:calc(23px * var(--scale)); }

/* Hide a side's chevron once its column is open. */
.aw-newblocks .wf-block__port--in.is-open  .wf-block__port__chip,
.aw-newblocks .wf-block__port--out.is-open .wf-block__port__chip{ display:none; }

/* === ENGINEERING VIEW — header + model + status + KPIs PLUS both
   connector panels PLUS the output, all stacked. ================ */
.aw-newblocks .block.wf-block.is-eng .wf-block__metrics{ opacity:1 !important; pointer-events:auto !important; }
.aw-newblocks .block.wf-block.is-eng.has-output .wf-block__output{ opacity:1 !important; pointer-events:auto !important; }
.aw-newblocks .block.wf-block.is-eng .wf-block__cb{ top:calc(158px * var(--scale)); }
.aw-newblocks .block.wf-block.is-eng .wf-block__output{ top:calc((151 + var(--cb-h, 0)) * 1px * var(--scale)); }
.aw-newblocks .block.wf-block.is-eng .wf-block__cb-close{ display:none; }
.aw-newblocks .block.wf-block.is-eng .wf-block__cb-boxes{
  border-top:1px solid var(--panel-stroke, #D9DED9);
  padding-top:calc(7px * var(--scale));
}

/* Side header (INPUTS / OUTPUTS + close). box-sizing RESTORES the template's box model rather than deviating from it: the prototype page has no global reset and sets box-sizing:border-box on exactly four selectors (template 545, 554, 616, 631) — not this one — so its `min-height:20px*scale` is a CONTENT minimum and the head measures 14.4 + 3.6 padding + 1 border = 18.98px. AirWanderer's `* { box-sizing: border-box }` (main.css:67) folded padding and border INSIDE that minimum and made the head 14.39px; 4.59px per head is 6.4 block units of panel, so cbNeededH() answered 161 where the prototype answers 168 and EVERY connector-panel card, engineering and plain, came out 5.04px short (vm2-diag-dsf1.json: protoEng cbH 168 / cardH 265.67 vs appEng cbH 161 / cardH 260.63; the out column measured 99.59 vs 95.00). Deliberately ONE line, and the declaration rides the existing one: this file's numbering must not move, because planflow-blocks.js, planflow-groups.js and planflow-groups.css all cite line numbers into it. */
.aw-newblocks .wf-block__cb-head{
  box-sizing:content-box;display:flex;align-items:center;gap:calc(6px * var(--scale));
  font-size:calc(9.5px * var(--scale));letter-spacing:.12em;text-transform:uppercase;font-weight:600;
  color:var(--muted);min-height:calc(20px * var(--scale));margin:0 calc(11px * var(--scale)) calc(7px * var(--scale));
  line-height:1.25;padding-bottom:calc(5px * var(--scale));
  border-bottom:1px solid var(--panel-stroke, #D9DED9);
}
.aw-newblocks .wf-block__cb-col--in  .wf-block__cb-head,
.aw-newblocks .wf-block__cb-col--out .wf-block__cb-head{ flex-direction:row; }
.aw-newblocks .wf-block__cb-head__label{ flex:1;white-space:nowrap;text-align:left; }
/* IOCB only: output header mirrors so the close sits by the center divider. */
.aw-newblocks .block.wf-block:has(.wf-block__port--in.is-open):has(.wf-block__port--out.is-open) .wf-block__cb-col--out .wf-block__cb-head{ flex-direction:row-reverse; }
.aw-newblocks .block.wf-block:has(.wf-block__port--in.is-open):has(.wf-block__port--out.is-open) .wf-block__cb-col--out .wf-block__cb-head__label{ text-align:right; }
.aw-newblocks .wf-block__cb-close{
  flex:none;width:calc(18px * var(--scale));height:calc(18px * var(--scale));
  border:0;background:transparent;border-radius:calc(5px * var(--scale));color:var(--muted);
  font-size:calc(15px * var(--scale));line-height:1;cursor:pointer;
  display:flex;align-items:center;justify-content:center;padding:0;pointer-events:auto;
  transition:background .12s var(--ease),color .12s var(--ease);
}
.aw-newblocks .wf-block__cb-close:hover{ background:rgba(70,92,80,.14);color:var(--text); }

/* Full-width connector row — dot absolutely positioned at the
   inner-card edge. */
.aw-newblocks .wf-block__cb .wf-block__subport{
  position:relative;display:flex;align-items:center;
  height:calc(26px * var(--scale));width:100%;padding:0;gap:0;box-sizing:border-box;
  background:transparent;border:0;border-radius:calc(7px * var(--scale));
  font:inherit;color:var(--label);white-space:nowrap;cursor:default;
  transition:background .12s var(--ease);
}
.aw-newblocks .wf-block__cb .wf-block__subport:hover{ background:var(--row-hover); }
.aw-newblocks .wf-block__cb .wf-block__subport__dot{
  position:absolute;top:50%;margin-top:calc(-5.5px * var(--scale));z-index:6;
  width:calc(11px * var(--scale));height:calc(11px * var(--scale));border-radius:50%;
  box-sizing:border-box;background:#fff;border:1.5px solid var(--cta);
  box-shadow:0 0 0 2px #F4F7F4;cursor:crosshair;
  transition:transform .15s var(--ease),box-shadow .15s var(--ease),background .15s var(--ease);
}
.aw-newblocks .wf-block__cb .wf-block__subport__dot:hover{
  transform:scale(1.3);box-shadow:0 0 0 2px #F4F7F4,0 0 0 4px rgba(31,110,69,.32);
}
.aw-newblocks .wf-block__cb .wf-block__subport__dot.is-connected{
  background:var(--cta);border-color:var(--cta);
  box-shadow:0 0 0 2px #F4F7F4;
}
.aw-newblocks .wf-block__cb .wf-block__subport__name{
  flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:calc(12px * var(--scale));color:var(--label);
}
/* Subtle dotted leader tying each label to its port across the gap. */
.aw-newblocks .wf-block__cb .wf-block__subport__lead{
  flex:1 1 auto;min-width:0;align-self:center;height:calc(6px * var(--scale));
  background-image:radial-gradient(circle at center, var(--muted) 22%, transparent 26%);
  background-size:calc(5px * var(--scale)) calc(6px * var(--scale));
  background-repeat:repeat-x;background-position:center;
  opacity:.32;
}
.aw-newblocks .wf-block__cb-col--in  .wf-block__subport{ flex-direction:row-reverse; }
.aw-newblocks .wf-block__cb-col--in  .wf-block__subport__dot{ left:calc(11px * var(--scale)); }
.aw-newblocks .wf-block__cb-col--in  .wf-block__subport__name{ padding:0 calc(13px * var(--scale)) 0 0; }
.aw-newblocks .wf-block__cb-col--in  .wf-block__subport__lead{ margin:0 calc(9px * var(--scale)) 0 calc(24px * var(--scale)); }
.aw-newblocks .wf-block__cb-col--out .wf-block__subport{ flex-direction:row; }
.aw-newblocks .wf-block__cb-col--out .wf-block__subport__dot{ right:calc(11px * var(--scale)); }
.aw-newblocks .wf-block__cb-col--out .wf-block__subport__name{ padding:0 0 0 calc(13px * var(--scale)); }
.aw-newblocks .wf-block__cb-col--out .wf-block__subport__lead{ margin:0 calc(24px * var(--scale)) 0 calc(9px * var(--scale)); }

/* Rename + delete pill — revealed on row hover, set clear of the dot. */
.aw-newblocks .wf-block__cb-rowbtns{
  position:absolute;top:50%;transform:translateY(-50%);
  display:flex;gap:calc(2px * var(--scale));opacity:0;
  background:var(--panel-fill, #F4F7F4);border-radius:calc(7px * var(--scale));
  padding:calc(2px * var(--scale));
  transition:opacity .12s var(--ease);pointer-events:auto;
  box-shadow:0 0 0 1px var(--panel-stroke, #D9DED9);
}
.aw-newblocks .wf-block__cb-col--in  .wf-block__cb-rowbtns{ left:calc(34px * var(--scale)); right:auto; }
.aw-newblocks .wf-block__cb-col--out .wf-block__cb-rowbtns{ right:calc(34px * var(--scale)); left:auto; }
.aw-newblocks .wf-block__cb .wf-block__subport:hover .wf-block__cb-rowbtns{ opacity:1; }
.aw-newblocks .wf-block__cb-rowbtn{
  width:calc(21px * var(--scale));height:calc(21px * var(--scale));
  border:0;background:transparent;border-radius:calc(5px * var(--scale));color:var(--muted);
  font-size:calc(13px * var(--scale));line-height:1;cursor:pointer;
  display:flex;align-items:center;justify-content:center;padding:0;
  transition:background .12s var(--ease),color .12s var(--ease);
}
.aw-newblocks .wf-block__cb-rowbtn:hover{ background:rgba(70,92,80,.16);color:var(--text); }
.aw-newblocks .wf-block__cb-name-input{
  font:inherit;font-size:calc(12px * var(--scale));color:var(--cta);
  border:1px solid var(--accent-line);border-radius:calc(5px * var(--scale));
  padding:1px calc(5px * var(--scale));width:calc(86px * var(--scale));outline:none;
}
.aw-newblocks .wf-block__cb-add{
  position:relative;display:flex;align-items:center;padding:0;box-sizing:border-box;
  height:calc(23px * var(--scale));border:0;background:transparent;font:inherit;
  font-size:calc(11.5px * var(--scale));color:var(--muted);cursor:pointer;width:100%;
  transition:color .12s var(--ease);pointer-events:auto;
}
.aw-newblocks .wf-block__cb-add__txt{ flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }
.aw-newblocks .wf-block__cb-add__lead{
  flex:1 1 auto;min-width:0;align-self:center;height:calc(6px * var(--scale));
  background-image:radial-gradient(circle at center, var(--muted) 22%, transparent 26%);
  background-size:calc(5px * var(--scale)) calc(6px * var(--scale));
  background-repeat:repeat-x;background-position:center;opacity:.22;
}
.aw-newblocks .wf-block__cb-add::before{
  content:"";position:absolute;top:50%;margin-top:calc(-5.5px * var(--scale));
  width:calc(11px * var(--scale));height:calc(11px * var(--scale));
  border-radius:50%;border:1.5px dashed var(--port-stroke);box-sizing:border-box;
}
.aw-newblocks .wf-block__cb-col--in  .wf-block__cb-add{ flex-direction:row-reverse; }
.aw-newblocks .wf-block__cb-col--in  .wf-block__cb-add__txt{ padding:0 calc(13px * var(--scale)) 0 0; }
.aw-newblocks .wf-block__cb-col--in  .wf-block__cb-add__lead{ margin:0 calc(9px * var(--scale)) 0 calc(24px * var(--scale)); }
.aw-newblocks .wf-block__cb-col--in  .wf-block__cb-add::before{ left:calc(11px * var(--scale)); }
.aw-newblocks .wf-block__cb-col--out .wf-block__cb-add{ flex-direction:row; }
.aw-newblocks .wf-block__cb-col--out .wf-block__cb-add__txt{ padding:0 0 0 calc(13px * var(--scale)); }
.aw-newblocks .wf-block__cb-col--out .wf-block__cb-add__lead{ margin:0 calc(24px * var(--scale)) 0 calc(9px * var(--scale)); }
.aw-newblocks .wf-block__cb-col--out .wf-block__cb-add::before{ right:calc(11px * var(--scale)); }
.aw-newblocks .wf-block__cb-add:hover{ color:var(--cta); }
.aw-newblocks .wf-block__cb-add:hover::before{ border-style:solid;border-color:var(--cta);background:var(--cta); }

/* Body-level wiring mode (flag + is-connecting both live on <body>). */
body.aw-newblocks.is-connecting{cursor:crosshair;user-select:none;-webkit-user-select:none}
body.aw-newblocks.is-connecting *{cursor:crosshair!important}
body.aw-newblocks.is-connecting .wf-block__subport__dot{ transform:scale(1.15) }
body.aw-newblocks.is-connecting .wf-block__port.is-target .wf-block__port__chip{
  border-color:var(--accent);background:var(--header-hover);
  color:var(--active-text);
}

/* =============================================================
   Per-block top toolbar — sits ABOVE the card, appears on block
   hover. The toolbar's bounding box ABUTS the card top so moving
   the cursor up from the card into a button is one continuous
   motion. Buttons switch their own pointer-events between auto
   (visible) and none (hidden).
   ============================================================= */
.aw-newblocks .wf-block__topbar{
  position:absolute;
  top:-32px;
  right:0;
  height:32px;
  display:flex;align-items:flex-start;
  gap:6px;
  pointer-events:auto;
  opacity:0;transform:translateY(4px);
  z-index:7;
  transition:opacity .18s var(--ease),transform .18s var(--ease);
}
.aw-newblocks .block.wf-block:hover .wf-block__topbar,
.aw-newblocks .wf-block__topbar:hover{
  opacity:1;transform:translateY(0);
}
.aw-newblocks .wf-block__top-btn{
  width:28px;height:28px;
  display:flex;align-items:center;justify-content:center;
  background:var(--surface);
  -webkit-backdrop-filter:var(--blur);backdrop-filter:var(--blur);
  border:1px solid var(--border);
  border-radius:8px;
  color:var(--muted);
  cursor:pointer;padding:0;
  box-shadow:var(--shadow-pop);
  pointer-events:none;       /* not clickable while toolbar is invisible */
  transition:background .15s var(--ease),color .15s var(--ease),
             border-color .15s var(--ease),transform .12s var(--ease);
}
.aw-newblocks .block.wf-block:hover .wf-block__top-btn,
.aw-newblocks .wf-block__topbar:hover .wf-block__top-btn{
  pointer-events:auto;        /* clickable as soon as the toolbar shows */
}
.aw-newblocks .wf-block__top-btn svg{width:14px;height:14px;display:block;stroke-width:1.7}
.aw-newblocks .wf-block__top-btn:hover{
  background:var(--cta);
  color:#fff;
  border-color:var(--cta);
  transform:translateY(-2px);
}
.aw-newblocks .wf-block__top-btn--danger:hover{
  background:#b85454;border-color:#b85454;
}
.aw-newblocks .wf-block__top-btn:active{transform:translateY(0) scale(.96)}
/* Engineering button reads as "active / current view" while it's on. */
.aw-newblocks .block.wf-block.is-eng .wf-block__top-btn[data-action="engineering"]{
  background:var(--cta); color:#fff; border-color:var(--cta);
}
.aw-newblocks .block.wf-block.is-dragging .wf-block__topbar,
body.aw-newblocks.is-connecting .wf-block__topbar{opacity:0;pointer-events:none}

/* Drag-handle grip icon — six dots in the header area, visible on
   block hover; lives INSIDE .wf-block__drag. */
.aw-newblocks .wf-block__drag{
  display:flex;align-items:center;justify-content:flex-end;
  padding-right:calc(28px * var(--scale));
}
.aw-newblocks .wf-block__drag-handle{
  width:12px;height:12px;margin:0;position:absolute;right:calc(14px * var(--scale));top:50%;transform:translateY(-50%);   /* owner 2026-10-19 (round 27h): the grip sits 14 units inside the title strip's right edge, on every card (7 was 'a bit too much') */
  opacity:0;
  color:var(--muted);
  pointer-events:none;
  transition:opacity .15s var(--ease),color .15s var(--ease);
}
.aw-newblocks .block.wf-block:hover .wf-block__drag-handle{opacity:.55}
.aw-newblocks .wf-block__drag:hover .wf-block__drag-handle{opacity:.9;color:var(--cta)}
.aw-newblocks .block.wf-block.is-dragging .wf-block__drag-handle{opacity:.9;color:var(--cta)}

/* Click-flash highlight layer — the ::after pseudo creates a separate
   layer for the outline pulse so it doesn't interfere with the
   filter:drop-shadow stack on the card. (Retired as the flash VISUAL
   by the v2 silhouette halo below, but the layer is kept verbatim.) */
.aw-newblocks .wf-block__card::after{
  content:'';
  position:absolute;
  inset:0;
  border-radius:12px;
  pointer-events:none;
  opacity:0;
  box-shadow:0 0 0 0 var(--flash);
}

/* ===== collapsed view — compact 78×82 mini tile ====================== */
.aw-newblocks .wf-block__mini{
  display:block;
  position:absolute; top:0; left:0;
  width:78px;height:82px;
  opacity:0; pointer-events:none;
}
.aw-newblocks .wf-block__mini svg{width:100%;height:100%;display:block;pointer-events:none}
.aw-newblocks .block.wf-block.is-collapsed{width:78px}
.aw-newblocks .block.wf-block.is-collapsed .wf-block__mini{opacity:1}
/* Fade out all the expanded-view pieces — the mini SVG cross-fades in. */
.aw-newblocks .block.wf-block.is-collapsed .wf-block__layer,
.aw-newblocks .block.wf-block.is-collapsed .wf-block__title,
.aw-newblocks .block.wf-block.is-collapsed .wf-block__provider-name,
.aw-newblocks .block.wf-block.is-collapsed .wf-block__statusbox,
.aw-newblocks .block.wf-block.is-collapsed .wf-block__metrics{ opacity:0; pointer-events:none; }
.aw-newblocks .block.wf-block.is-collapsed .wf-block__layer--outer{ height:82px; }
.aw-newblocks .block.wf-block.is-collapsed .wf-block__card{height:82px}
/* Collapse / uncollapse transition — only active via the temporary
   is-collapse-anim class JS adds for the duration, so the outer layer's
   height transition can't fight the rAF-driven output/connector resizes. */
.aw-newblocks .block.wf-block.is-collapse-anim{
  transition:filter var(--dur) var(--ease), width var(--dur) var(--ease);
}
.aw-newblocks .block.wf-block.is-collapse-anim .wf-block__layer,
.aw-newblocks .block.wf-block.is-collapse-anim .wf-block__title,
.aw-newblocks .block.wf-block.is-collapse-anim .wf-block__provider-name,
.aw-newblocks .block.wf-block.is-collapse-anim .wf-block__statusbox,
.aw-newblocks .block.wf-block.is-collapse-anim .wf-block__metrics,
.aw-newblocks .block.wf-block.is-collapse-anim .wf-block__mini{
  transition:opacity var(--dur) var(--ease);
}
.aw-newblocks .block.wf-block.is-collapse-anim .wf-block__layer--outer{
  transition:opacity var(--dur) var(--ease), height var(--dur) var(--ease);
}
/* Collapsed top-bar: centre the icon cluster over the 78px mini card. */
.aw-newblocks .block.wf-block.is-collapsed .wf-block__topbar{
  right:auto; left:50%;
  transform:translateX(-50%) translateY(4px);
}
.aw-newblocks .block.wf-block.is-collapsed:hover .wf-block__topbar,
.aw-newblocks .block.wf-block.is-collapsed .wf-block__topbar:hover{
  transform:translateX(-50%) translateY(0);
}
.aw-newblocks .block.wf-block.is-collapsed .wf-block__cb{display:none !important}
/* In collapsed mode the entire mini card is the drag handle. */
.aw-newblocks .block.wf-block.is-collapsed .wf-block__drag{
  left:0;top:0;width:100%;height:100%;
  z-index:3;padding:0;
}
.aw-newblocks .block.wf-block.is-collapsed .wf-block__drag-handle{display:none}
.aw-newblocks .block.wf-block.is-collapsed .wf-block__drag::after,
.aw-newblocks .block.wf-block.is-collapsed .wf-block__drag::before{display:none!important}
.aw-newblocks .block.wf-block.is-collapsed .wf-block__loop{bottom:-32px}

/* =============================================================
   Output preview panel — below the metrics row inside the card.
   Closed state: minimal, just the toggle text "OUTPUT ⌄".
   Open state: materialises into a tinted panel matching the
   status/metrics panel (#F4F7F4 fill, #D9DED9 stroke).
   Transitions all unified at 0.28s, same easing, no delays.
   ============================================================= */
.aw-newblocks .wf-block__output{
  position:absolute;
  top:calc(151px * var(--scale));
  left:calc(14px * var(--scale));
  width:calc(306px * var(--scale));
  background:transparent;
  border:1px solid transparent;
  border-radius:calc(8px * var(--scale));
  overflow:hidden;
  display:flex;flex-direction:column;
  z-index:8;
  opacity:0;
  pointer-events:none;
  transition:background .28s var(--ease),
             border-color .28s var(--ease),
             box-shadow .28s var(--ease),
             opacity .25s var(--ease);
}
.aw-newblocks .block.wf-block.has-output .wf-block__output{
  opacity:1;
  pointer-events:auto;
}
.aw-newblocks .block.wf-block.is-output-open .wf-block__output{
  background:#F4F7F4;
  border-color:#D9DED9;
  box-shadow:inset 0 1px 0 rgba(0,0,0,.03);
}

.aw-newblocks .wf-block__output__toggle{
  display:flex;align-items:center;justify-content:center;
  width:100%;
  padding:calc(7px * var(--scale)) calc(12px * var(--scale));
  gap:calc(7px * var(--scale));
  background:transparent;
  border:0;
  font-family:inherit;
  font-size:calc(10.5px * var(--scale));
  font-weight:600;
  letter-spacing:.13em;
  text-transform:uppercase;
  color:#3C5247;
  line-height:1;
  cursor:pointer;
  user-select:none;
  pointer-events:auto;
  border-radius:calc(7px * var(--scale));
  transition:background .2s var(--ease),color .2s var(--ease);
}
.aw-newblocks .wf-block__output__toggle:hover{
  background:rgba(31,110,69,.06);
  color:#1F6E45;
}
.aw-newblocks .wf-block__output__chevron{
  width:calc(9px * var(--scale));
  height:calc(5.5px * var(--scale));
  display:block;
  transition:transform .28s var(--ease);
}
.aw-newblocks .block.wf-block.is-output-open .wf-block__output__chevron{
  transform:rotate(180deg);
}

/* Body — max-height, opacity, border-top-color, and padding all move
   together so the body materialises as a single coordinated reveal. */
.aw-newblocks .wf-block__output__body{
  max-height:0;
  opacity:0;
  overflow:hidden;
  padding:0 calc(13px * var(--scale));
  border-top:1px solid transparent;
  display:flex;flex-direction:row;
  align-items:flex-start;
  gap:calc(10px * var(--scale));
  pointer-events:none;
  transition:max-height .28s var(--ease),
             opacity .28s var(--ease),
             border-top-color .28s var(--ease),
             padding .28s var(--ease);
}
.aw-newblocks .block.wf-block.is-output-open .wf-block__output__body{
  max-height:calc(60px * var(--scale));
  opacity:1;
  border-top-color:#D9DED9;
  padding:calc(10px * var(--scale)) calc(13px * var(--scale));
  pointer-events:auto;
}
.aw-newblocks .wf-block__output__text{
  flex:1;min-width:0;
  font-size:calc(12px * var(--scale));
  line-height:1.5;
  color:#1F2D26;
  display:-webkit-box;
  -webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden;
  text-overflow:ellipsis;
}
/* owner round 16 (2026-10-16): the image artifact on the card, before the text */
.aw-newblocks .wf-block__output__thumb{
  flex:0 0 auto;
  width:calc(36px * var(--scale));height:calc(36px * var(--scale));   /* the open body's inner height: 56 units less its 10 + 10 padding */
  border-radius:calc(6px * var(--scale));
  box-sizing:border-box;
  border:1px solid #D9DED9;
  background:#fff;
  object-fit:cover;
  display:block;
  pointer-events:none;
}
.aw-newblocks .wf-block__output__expand{
  flex:0 0 auto;
  width:calc(22px * var(--scale));height:calc(22px * var(--scale));
  display:flex;align-items:center;justify-content:center;
  background:transparent;border:0;padding:0;cursor:pointer;
  color:var(--muted);
  border-radius:calc(5px * var(--scale));
  transition:background .15s var(--ease),color .15s var(--ease);
}
.aw-newblocks .wf-block__output__expand svg{
  width:calc(12px * var(--scale));height:calc(12px * var(--scale));
  display:block;
}
.aw-newblocks .wf-block__output__expand:hover{
  background:rgba(31,110,69,.10);
  color:var(--cta);
}

/* Two-stage growth. Closed: 32 SVG units. Open: 88 SVG units. */
.aw-newblocks .block.wf-block.has-output{ --output-h:32; }
.aw-newblocks .block.wf-block.has-output.is-output-open{ --output-h:88; }

/* =============================================================
   Full-output modal — the 2-line in-block preview expanded into a
   readable overlay. ESC, backdrop click, or × all close it.
   (Appended to <body>, so the .aw-newblocks descendant prefix
   still matches.)
   ============================================================= */
.aw-newblocks .wf-output-overlay{
  position:fixed;
  inset:0;
  background:rgba(20,30,25,.42);
  display:flex;align-items:center;justify-content:center;
  z-index:1000;
  opacity:0;
  pointer-events:none;
  transition:opacity .22s var(--ease);
  backdrop-filter:blur(3px);
  -webkit-backdrop-filter:blur(3px);
}
.aw-newblocks .wf-output-overlay.is-open{
  opacity:1;
  pointer-events:auto;
}
.aw-newblocks .wf-output-modal{
  background:linear-gradient(180deg,#FDFEFD,#F7FAF7);
  border:1px solid rgba(70,92,80,.16);
  border-radius:16px;
  padding:22px 24px 24px;
  max-width:560px;
  width:90%;
  max-height:80vh;
  overflow-y:auto;
  position:relative;
  box-shadow:0 1px 0 rgba(255,255,255,.7) inset,0 24px 60px rgba(28,44,36,.24);
  transform:scale(.96);
  transition:transform .22s var(--ease);
}
.aw-newblocks .wf-output-overlay.is-open .wf-output-modal{
  transform:scale(1);
}
.aw-newblocks .wf-output-modal__close{
  position:absolute;
  top:12px;right:12px;
  width:28px;height:28px;
  border-radius:6px;
  background:transparent;border:0;
  cursor:pointer;
  display:flex;align-items:center;justify-content:center;
  color:#6E8077;
  font-size:18px;
  font-family:inherit;
  line-height:1;
  transition:background .15s var(--ease),color .15s var(--ease);
}
.aw-newblocks .wf-output-modal__close:hover{
  background:rgba(31,110,69,.08);
  color:#1F6E45;
}
.aw-newblocks .wf-output-modal__label{
  display:flex;align-items:center;gap:7px;
  font-size:10.5px;
  font-weight:700;
  text-transform:uppercase;
  letter-spacing:.12em;
  color:#3C7A5B;
  margin:0 0 12px 0;
  padding:0 0 11px 0;
  border-bottom:1px solid rgba(70,92,80,.12);
}
.aw-newblocks .wf-output-modal__label::before{
  content:"";
  width:7px;height:7px;border-radius:50%;
  background:#5C9A7E;box-shadow:0 0 0 3px rgba(92,154,126,.18);
  flex:none;
}
.aw-newblocks .wf-output-modal__text{
  font-size:13.5px;
  line-height:1.65;
  color:#2A3B33;
  white-space:pre-wrap;
  overflow-wrap:anywhere;
  max-width:52ch;
  padding-right:2px;
}
/* first line of each step block reads as a lead line */
.aw-newblocks .wf-output-modal__text::first-line{
  font-weight:600;color:#1F2D26;
}
/* Pin chip positions to the original (pre-output) card centre — 100.5px
   is half of the SVG's native 201px height. */
.aw-newblocks .block.wf-block.has-output .wf-block__port{
  top:calc(100.5px * var(--scale));
}
/* Collapsed overrides has-output — must come AFTER it in source order. */
.aw-newblocks .block.wf-block.is-collapsed .wf-block__port{ top:50%; }

/* =============================================================
   Tooltip system — data-tip="text" + optional data-tip-pos.
   The :where() wrappers keep the position:relative helper at the
   source's ZERO specificity — elements that already declare
   position keep their own positioning.
   ============================================================= */
:where(.aw-newblocks) :where(.block.wf-block [data-tip]){position:relative}
.aw-newblocks .block.wf-block [data-tip]::after{
  content:attr(data-tip);
  position:absolute;
  /* ROUND 31: the one hint face (sidebar-nav.css .wf-tip): 12 px, regular, radius 7, the dark
     green, the same shadow - and the same quarter-second before it shows */
  background:rgba(24,40,32,.94);color:#FFFFFF;
  font-size:12px;font-weight:400;line-height:1.35;letter-spacing:0;
  padding:7px 10px;border-radius:7px;
  white-space:nowrap;pointer-events:none;
  box-shadow:0 6px 22px rgba(0,0,0,.2);
  opacity:0;transform:translateY(2px);
  transition:opacity .14s var(--ease) .25s,transform .14s var(--ease) .25s;
  z-index:300;
}
.aw-newblocks .block.wf-block [data-tip]:not([data-tip-pos])::after,
.aw-newblocks .block.wf-block [data-tip][data-tip-pos="top"]::after{
  bottom:calc(100% + 8px);left:50%;transform:translate(-50%,2px);
}
.aw-newblocks .block.wf-block [data-tip]:not([data-tip-pos]):hover::after,
.aw-newblocks .block.wf-block [data-tip][data-tip-pos="top"]:hover::after{
  opacity:.94;transform:translate(-50%,0);
}
.aw-newblocks .block.wf-block [data-tip][data-tip-pos="bottom"]::after{
  top:calc(100% + 8px);left:50%;transform:translate(-50%,-2px);
}
.aw-newblocks .block.wf-block [data-tip][data-tip-pos="bottom"]:hover::after{
  opacity:.94;transform:translate(-50%,0);
}
.aw-newblocks .block.wf-block [data-tip][data-tip-pos="left"]::after{
  right:calc(100% + 8px);top:50%;transform:translate(2px,-50%);
}
.aw-newblocks .block.wf-block [data-tip][data-tip-pos="left"]:hover::after{
  opacity:.94;transform:translate(0,-50%);
}
.aw-newblocks .block.wf-block [data-tip][data-tip-pos="right"]::after{
  left:calc(100% + 8px);top:50%;transform:translate(-2px,-50%);
}
.aw-newblocks .block.wf-block [data-tip][data-tip-pos="right"]:hover::after{
  opacity:.94;transform:translate(0,-50%);
}
/* Suppress tooltips during interactions so they don't fight with drags. */
body.aw-newblocks.is-connecting [data-tip]::after,
.aw-newblocks .block.wf-block.is-dragging [data-tip]::after,
.aw-newblocks .block.wf-block.is-dragging[data-tip]::after{opacity:0!important;transition:none}

/* Port chip — feels draggable and shows a press response. */
.aw-newblocks .wf-block__port__chip{cursor:pointer;touch-action:none}
.aw-newblocks .wf-block__port__chip:active{cursor:grabbing}
.aw-newblocks .wf-block__port.is-drag-source .wf-block__port__chip{
  background:var(--header-hover);border-color:var(--accent);
  color:var(--active-text);
}

/* ---- Repeat / Loop chevron below the card -------------------------- */
.aw-newblocks .wf-block__loop{
  position:absolute;
  left:50%;bottom:-32px;
  transform:translateX(-50%);
  transform-origin:50% 0%;
  width:28px;height:28px;
  display:flex;align-items:center;justify-content:center;
  background:#fff;
  border:1px solid rgba(122,90,248,.45);
  border-radius:50%;
  cursor:grab;
  opacity:0;
  z-index:6;
  padding:0;
  color:var(--loop);
  box-shadow:0 1px 3px rgba(55,72,64,.10);
  touch-action:none;
  pointer-events:auto;
  transition:opacity .18s var(--ease),transform .15s var(--ease),
             background .15s var(--ease),color .15s var(--ease),
             border-color .15s var(--ease),box-shadow .15s var(--ease);
}
.aw-newblocks .wf-block__loop svg{width:14px;height:14px;display:block;pointer-events:none}
.aw-newblocks .block.wf-block:hover .wf-block__loop,
.aw-newblocks .block.wf-block.is-looping .wf-block__loop,
.aw-newblocks .wf-block__loop.is-drop-target,
.aw-newblocks .wf-block__loop.is-drag-source{opacity:1}
.aw-newblocks .wf-block__loop:hover,
.aw-newblocks .wf-block__loop.is-drop-target,
.aw-newblocks .wf-block__loop.is-drag-source{
  background:var(--loop);
  color:#fff;
  border-color:var(--loop);
  transform:translateX(-50%) scale(1.08);
  box-shadow:0 3px 10px rgba(122,90,248,.35);
}
.aw-newblocks .wf-block__loop:active{cursor:grabbing}
.aw-newblocks .block.wf-block.is-looping .wf-block__loop{
  opacity:1;
  background:var(--loop);
  color:#fff;
  border-color:var(--loop);
}

/* ===== run-state visuals (source: run.css) =========================== */
.aw-newblocks .block.wf-block.is-running .wf-block__statuspill__dot{
  background:transparent;
  /* Comet-tail ring: conic sweep from transparent tail up to solid
     amber head, masked into a thin ring. */
  background:conic-gradient(from 0deg,
    rgba(224,163,65,0)     0%,
    rgba(224,163,65,.22)  30%,
    #E0A341               92%,
    #E0A341              100%);
  -webkit-mask:radial-gradient(closest-side, #0000 54%, #000 55%);
          mask:radial-gradient(closest-side, #0000 54%, #000 55%);
  border:0;
  box-shadow:none;
  filter:drop-shadow(0 0 2px rgba(224,163,65,.55));
  /* Calmer 0.7s spin — reads as a smooth, organic comet. */
  animation:wf-block-spin .7s linear infinite;
}
@keyframes wf-block-spin{
  to{ transform:rotate(360deg); }
}

/* Impulse trail — a short stroke segment running around the outer
   card's silhouette (path shares `d` with the card stroke; pathLength
   100 lets dash/offset be percentages). Stroke = url(#wf-pulse-grad),
   filter = url(#wf-pulse-glow): document-level SVG defs, see doc §4.
   1.6s tuned so travel speed matches the 1.0s wire-flow animation
   (same units-per-second). Animation always runs; the class only
   toggles opacity, so the dash never "teleports" during the fade.
   --pulse-start lets a fed block start its dash at its input port. */
.aw-newblocks .wf-block__pulse-path{
  opacity:0;
  pointer-events:none;
  animation:wf-pulse-travel 1.6s linear infinite;
  transition:opacity .2s var(--ease);
}
.aw-newblocks .block.wf-block.is-running .wf-block__pulse-path{
  opacity:1;
}
/* DEVIATION (D1 fix, owner to ratify) — template ~1632-1635 writes the
   keyframe values UNITLESS: from{stroke-dashoffset:var(--pulse-start,0)}
   to{stroke-dashoffset:calc(var(--pulse-start,0) - 100)}. In Chromium
   151 (rig + target browser) that calc types as <number> and the
   animation falls back to DISCRETE interpolation — the offset flips
   0 → -100 at 50%, which with pathLength=100 is the SAME visual
   position, i.e. the owner-reported "impulse frozen" (the Build-113
   reference file freezes identically in this Chromium — evidence
   p1-ref-a/b.png + raw serialization flip in p1-ref.js output).
   px-typing the SAME values restores smooth numeric interpolation;
   1 CSS px == 1 SVG user unit on this untransformed path geometry, so
   -100px == -100 units == exactly one pathLength cycle — numerically
   identical to the template's intent. Any future setter of
   --pulse-start (template 2591 cfg.pulseStart) must write px values. */
@keyframes wf-pulse-travel{
  from{ stroke-dashoffset:var(--pulse-start, 0px); }
  to  { stroke-dashoffset:calc(var(--pulse-start, 0px) - 100px); }
}

/* Amber title spinner — secondary running affordance near the title
   bar's right edge. Same palette as the impulse trail. */
.aw-newblocks .wf-block__title-spinner{
  position:absolute;
  top:calc(24px * var(--scale));
  right:calc(38px * var(--scale));
  width:calc(28px * var(--scale));
  height:calc(28px * var(--scale));
  border-radius:50%;
  background:conic-gradient(from 0deg,
    rgba(244,184,62,0)     0%,
    rgba(244,184,62,.20)  28%,
    #F4B83E               92%,
    #F4B83E              100%);
  -webkit-mask:radial-gradient(closest-side, #0000 67%, #000 68%);
          mask:radial-gradient(closest-side, #0000 67%, #000 68%);
  opacity:0;
  pointer-events:none;
  z-index:9;
  filter:drop-shadow(0 0 6px rgba(244,184,62,0.65))
         drop-shadow(0 0 12px rgba(244,184,62,0.35));
  transition:opacity .2s var(--ease);
}
.aw-newblocks .block.wf-block.is-running .wf-block__title-spinner{
  opacity:1;
  /* Same calm 0.7s pace as the status dot. */
  animation:wf-block-spin .7s linear infinite;
}

@keyframes wf-chip-receive{
  0%   { transform:scale(1);     box-shadow:0 0 0 5px rgba(91,109,255,0),   0 0 0 0 rgba(60,220,255,0); }
  22%  { transform:scale(.97);   box-shadow:0 0 0 4px rgba(124,91,255,.18), 0 0 4px 1px rgba(60,220,255,.34); }
  46%  { transform:scale(.98);   box-shadow:0 0 0 1px rgba(124,91,255,.55), 0 0 7px 2px rgba(60,220,255,.58); }
  64%  { transform:scale(1.05);  box-shadow:0 0 0 1px rgba(91,109,255,.32), 0 0 9px 2px rgba(60,220,255,.36); }
  100% { transform:scale(1);     box-shadow:0 0 0 8px rgba(124,91,255,0),   0 0 13px 4px rgba(60,220,255,0); }
}
.aw-newblocks .wf-block__port__chip.is-receiving{
  animation:wf-chip-receive .5s var(--ease) 1;
}
/* While the combined journey dash is the active indicator, hide every
   block's per-block pulse-path by default; the is-running rule below
   re-shows it on the block that's currently processing. */
body.aw-newblocks.wf-is-journey .wf-block__pulse-path{
  opacity:0 !important;
}
.aw-newblocks .block.wf-block.is-running{
  /* Quiet depth-lift; the animated pulsation lives on the perimeter
     outline, not the shadow. z-index lift floats the block above
     neighbours during fan-out runs. */
  filter:drop-shadow(0 12px 26px rgba(55,72,64,0.22));
  z-index:5;
}
/* During a journey, gently dim truly-idle blocks (not running, not yet
   completed, not pending their output expand). */
body.aw-newblocks.wf-is-journey .block.wf-block:not(.is-running):not(.has-output):not([data-pending-expand="1"]):not(.is-review-hold){
  opacity:0.55;
  transition:opacity .35s var(--ease);
}
/* PULSING BORDER — the running block's perimeter outlined in the
   impulse gradient, breathing in opacity, with the bright dash
   traveling on top. !important needed to beat the journey hide-rule. */
body.aw-newblocks.wf-is-journey .block.wf-block.is-running .wf-block__pulse-path{
  opacity:1 !important;
  stroke:url(#wf-pulse-grad);
  /* Solid stroke — dash longer than any real perimeter so the gap
     never appears (Safari-safe alternative to dasharray:none). */
  stroke-dasharray:99999;
  stroke-width:2.8;
  animation:wf-block-border-pulse 1.6s ease-in-out infinite;
}
@keyframes wf-block-border-pulse{
  0%, 100%{ stroke-opacity:0.25; }
  50%    { stroke-opacity:0.70; }
}
.aw-newblocks .block.wf-block.is-blocked{
  filter:drop-shadow(0 6px 18px rgba(190,80,80,.30));
}
.aw-newblocks .block.wf-block.is-flash .wf-block__card::after{
  animation:wf-block-flash .55s var(--ease);
}
@keyframes wf-block-flash{
  0%   {opacity:1;box-shadow:0 0 0 0 var(--flash);}
  60%  {opacity:.9;box-shadow:0 0 0 4px rgba(92,154,126,.55);}
  100% {opacity:0;box-shadow:0 0 0 10px rgba(92,154,126,0);}
}
.aw-newblocks .block.wf-block.is-collapsed .wf-block__title-spinner{display:none}

/* ===== reduced motion (source: app.css; block rules only) ============ */
@media (prefers-reduced-motion:reduce){
  .aw-newblocks .block.wf-block,.aw-newblocks .block.wf-block *{transition-duration:.001ms!important}
}

/* =====================================================================
   v2 MODULE ADDITIONS (source: second <style> block in the prototype)
   — the parts that restyle the BLOCK CARD itself. Frame/plan-group
   rendering (.wf-planframe, .wf-pgsec, .wf-pgout, .wf-block--grp) and
   the .wf-block--shifting corridor choreography are canvas/group
   logic and intentionally NOT extracted here.
   ===================================================================== */
/* selection halo v2 — traced from the block's PAINTED silhouette. The
   old rectangular card-box pulse is retired. */
.aw-newblocks .block.wf-block.is-flash .wf-block__card::after{ animation:none; opacity:0; }
.aw-newblocks .block.wf-block.is-collapsed.is-flash .wf-block__mini{ animation:wf-halo .62s var(--ease); }
/* the expanded halo is a stroked TWIN of the card's own outline path —
   injected on selection by the host, so it is the silhouette, notch
   for notch, in whatever state the card is currently drawn */
/* S30 (scale sweep §4) — INERT SEAM, DO NOT DELETE. Nothing in
   static/js/ injects .wf-halo-stroke today, so this rule paints
   nothing. It is kept deliberately (standing "protect custom UI"
   rule): the seam is where the prototype's selection halo lands.

   WHICH PARENT MAKES IT CORRECT — measured, not reasoned:
   the prototype injects the twin in its flashBlock wrapper
   (planflow_handover/module.js 1709-1715) into
   `el.querySelector('.wf-block__layer--outer svg')` — the CARD's own
   outer-layer svg. That svg is inside .block.wf-block, so our stage's
   0.7 is cancelled by the card's transform:scale(var(--pf-zoom-comp))
   (planflow-blocks.css:95) and what is left is the card svg's own
   --scale 0.72. Painted stroke there:
       ours 2.1599 screen px   prototype 2.1600 screen px   ratio 1.000
   (sweep/wire-s30.json + wire-s30-ref.json, dsf 1 / 1.25 / 1.5.)
   So under THAT parent these literals are already right and must NOT
   be multiplied by --pf-wire-u.

   Injected onto the stage instead — .connections-svg or
   .wf-journey-svg, both direct children of #canvas-stage with no
   counter-transform — the SAME rule paints
       ours 2.1000 screen px   prototype 3.0000 screen px   ratio 0.700
   (same evidence files). Whoever wires this up therefore appends into
   the card's own outer-layer svg; if it ever has to live on the stage
   layers instead, stroke-width and the drop-shadow radius must become
   calc(3px * var(--pf-wire-u,1)) / calc(5px * var(--pf-wire-u,1)),
   exactly like the wire rules further down this file. */
.aw-newblocks .wf-halo-stroke{ fill:none; stroke:#3C7A5B; stroke-width:3; stroke-linejoin:round;
  pointer-events:none; stroke-opacity:0;
  filter:drop-shadow(0 0 5px rgba(60,122,91,.42));
  animation:wf-halo-stroke .62s var(--ease) forwards; }
@keyframes wf-halo-stroke{
  0%   { stroke-opacity:0; }
  26%  { stroke-opacity:.8; }
  100% { stroke-opacity:0; }
}
@keyframes wf-halo{
  0%   { filter:drop-shadow(0 0 0 rgba(60,122,91,0)) drop-shadow(0 0 0 rgba(60,122,91,0)); }
  28%  { filter:drop-shadow(0 0 1.5px rgba(60,122,91,.60)) drop-shadow(0 0 9px rgba(60,122,91,.28)); }
  100% { filter:drop-shadow(0 0 1px rgba(60,122,91,0)) drop-shadow(0 0 16px rgba(60,122,91,0)); }
}
/* quiet ports — a chevron earns its place: connected, open, hovered,
   focused, or mid-wiring */
.aw-newblocks .wf-block__port{opacity:0;pointer-events:none;transition:opacity .16s ease}
.aw-newblocks .block.wf-block:hover .wf-block__port,
.aw-newblocks .wf-block__port:focus-visible,
.aw-newblocks .wf-block__port.is-open,
.aw-newblocks .wf-block__port.is-connected,
body.aw-newblocks.is-connecting .wf-block__port{opacity:1;pointer-events:auto}
/* the self-repeat control sits in the topbar — calm when idle,
   sage when the block repeats */
.aw-newblocks .wf-block__top-btn--repeat.is-active{color:#3C7A5B}
.aw-newblocks .wf-block__top-btn--repeat.is-active svg{color:#3C7A5B}
/* a block that is actively running is not a thing you drag */
body.aw-newblocks.wf-is-journey .block.wf-block.is-running .wf-block__drag{display:none !important}
/* while running, the title yields to the amber spinner instead of
   sliding under it (spinner: right 38px, 28px wide) */
.aw-newblocks .block.wf-block.is-running .wf-block__title{max-width:calc(214px * var(--scale));transition:max-width .2s ease}
/* native-style topbar buttons added by the module */
.aw-newblocks .wf-block__top-btn--pg{color:#2C6A4D}
.aw-newblocks .wf-block__top-btn--pg span{font:800 9.5px ui-monospace,Menlo,monospace;margin-left:2px}

/* =====================================================================
   FAMILY COLOR TOKENS — RATIFIED COLOR/BADGE TABLE
   (docs/planflow/BLOCK_PARITY_MATRIX.md §3, owner-approved 2026-07-29)

   Family colors show on: badge letters, title-bar tint, port dots.
   Application rules are follow-up work (the extracted rules above keep
   the prototype's sage-green art); tokens are ratified NOW so the
   parity work targets fixed values.

   Badge text per family:
     input      → "IN"
     ai_prompt  → "AI"   (prototype's letterform)
     generate_* → FORMAT LETTERS derived from the GENERATE_KINDS
                  registry (static/js/inspector.js):
                    generate_pptx→PPTX  generate_docx→DOCX
                    generate_pdf→PDF    generate_xlsx→XLSX
                    generate_image→IMG  generate_html→HTML
                    generate_md→MD      generate_csv→CSV
                    generate_json→JSON  generate_file→FILE
                    generate_custom→FILE (registry alias of generate_file)
                  ("DOC" from the ratified list has NO registry entry —
                  see BLOCK_CARD_EXTRACT.md §6.)
     output     → "OUT"
     repeat_*   → role pill instead of badge letters; dashed outline kept
     plan_group → "PG" (P3 — token only, no PG rendering yet)
   ===================================================================== */
.aw-newblocks{
  --pf-family-input:#2d799e;      /* input — teal-blue,  badge "IN"  */
  --pf-family-ai-prompt:#3E8E6E;  /* ai_prompt — sage green (prototype's), badge "AI" */
  /* generate family — owner mandate: NOT green, and not terracotta (too
     close to the plan-group gold). SLATE is the owner's pick, 2026-08-02:
     crimson "looks a bit like an error", which is fair — it collides with
     the failed-state language. Both candidates stay behind --pf-generate
     so the other can be auditioned by repointing this one line. */
  --pf-generate-crimson:#A93B4F;
  --pf-generate-slate:#64748B;
  --pf-generate:var(--pf-generate-slate);
  --pf-family-generate:var(--pf-generate); /* badge = format letters */
  --pf-family-output:#5e49a8;     /* output — indigo, badge "OUT" */
  --pf-family-repeat:#8b5cf6;     /* repeat helpers — purple, role pill, dashed outline kept */
  --pf-family-plan-group:#8F6B33; /* plan_group — gold, badge "PG" (P3: token only, no rendering yet) */
  --pf-family-loop:#2F8F8F;       /* loop — teal (owner 2026-11-02, LOOPS_DESIGN §8.1), badge "LP" */
}
/* Per-block family hook: the host sets data-family on .wf-block; the
   transplanted SVG builders read these tokens via inline style vars.
   D5 — family identity rides the badge CARD (fill + stroke + letters:
   the GR_BADGE pattern, planflow_handover/module.js:8-12, geometry-
   identical to the template's AI badge ~2452), the TITLE-BAR strip
   tint (SVG_TITLE_BAR fill, template ~2451) and the port-dot accents
   (parity matrix §3: "badge letters, title-bar tint, port dots") —
   never text colour alone.

   Tint derivation: the prototype ships exactly TWO hand-tuned family
   sets — AI (#1F6E45 deep → #DCE8DF fill / #C7D8CB stroke / #5C9A7E
   tab) and PG gold (#8F6B33 → #E7DBC0 / #D8C69E / #B99B62). The other
   families reproduce the AI set's lightness ratios over white
   (fill ≈ 15% deep, stroke ≈ 26%, tab ≈ 70%) from their ratified §3
   colour; AI and PG override with their exact prototype literals. */
.aw-newblocks .block.wf-block[data-family]{
  /* Strength calibration (owner: derived strips read washed-out /
     "greenish"): the prototype's two hand-tuned sets differ — AI is
     ~15% deep-over-white but PG gold is ~26% (E7DBC0 vs 8F6B33
     measured per channel). Derived families use the PG strength so
     every family strip reads unmistakably, like the gold does. */
  --wf-family-tint:color-mix(in srgb, var(--wf-family, #1F6E45) 24%, white);
  --wf-family-line:color-mix(in srgb, var(--wf-family, #1F6E45) 36%, white);
  --wf-family-tab:color-mix(in srgb, var(--wf-family, #1F6E45) 70%, white);
}
.aw-newblocks .block.wf-block[data-family="input"]     { --wf-family:var(--pf-family-input); }
.aw-newblocks .block.wf-block[data-family="ai_prompt"] {
  --wf-family:var(--pf-family-ai-prompt);
  /* exact prototype AI literals (template SVG_BADGE ~2452) */
  --wf-family-tint:#DCE8DF;
  --wf-family-line:#C7D8CB;
  --wf-family-tab:#5C9A7E;
}
.aw-newblocks .block.wf-block[data-family="generate"]  { --wf-family:var(--pf-family-generate); }
.aw-newblocks .block.wf-block[data-family="output"]    { --wf-family:var(--pf-family-output); }
.aw-newblocks .block.wf-block[data-family="repeat"]    { --wf-family:var(--pf-family-repeat); }
.aw-newblocks .block.wf-block[data-family="plan_group"]{
  --wf-family:var(--pf-family-plan-group);
  /* exact GR_BADGE gold literals (module.js:8-12) */
  --wf-family-tint:#E7DBC0;
  --wf-family-line:#D8C69E;
  --wf-family-tab:#B99B62;
}
/* Port-dot accents take the family colour (parity matrix §3). Rule
   bodies mirror the prototype dot rules above (--cta swapped for the
   family token); rings/shadows keep the prototype values. */
.aw-newblocks .block.wf-block[data-family] .wf-block__subport__dot{
  background:var(--wf-family, var(--cta));
}
.aw-newblocks .block.wf-block[data-family] .wf-block__cb .wf-block__subport__dot{
  background:#fff;
  border-color:var(--wf-family, var(--cta));
}
.aw-newblocks .block.wf-block[data-family] .wf-block__cb .wf-block__subport__dot.is-connected{
  background:var(--wf-family, var(--cta));
  border-color:var(--wf-family, var(--cta));
}
/* PLAN GROUP — the TITLE STRIP is its own literal, not the badge tint.
   The prototype states this in one rule (workflow-preview-build113.html
   12376-12377, comment: "identity lives ONLY in the badge (icon) and
   the title bar (header)"):
       .wf-block--grp .wf-block__layer--title-bar path[fill="#DCE8DF"]
         { fill:#E6DCC2 }
   #E6DCC2 is NOT the badge card's #E7DBC0 — the owner tuned the strip
   a shade warmer/lighter than the badge. Two things stop that rule
   from working verbatim in our port, which is exactly why the badge
   went gold and the strip stayed green: our SVG_TITLE_BAR path carries
   no fill ATTRIBUTE for `path[fill=...]` to match (it is parameterized
   as style="fill:var(--wf-family-tint,#DCE8DF)", planflow-blocks.js
   :155), and an inline style declaration beats any stylesheet rule.
   Overriding the VARIABLE on the title-bar layer works through both:
   the inline var() resolves against the cascade on its own element, so
   the strip takes #E6DCC2 while the badge keeps #E7DBC0.
   Keyed on BOTH identity carriers, and BOTH are emitted by our own
   renderer (planflow-blocks.js buildInner: data-family from familyOf,
   and .wf-block--grp stamped alongside it exactly as module.js
   applySkin:665 stamps it) — so the prototype's selector is live here,
   not a placeholder waiting for a module we have not ported. */
.aw-newblocks .block.wf-block[data-family="plan_group"] .wf-block__layer--title-bar,
.aw-newblocks .block.wf-block.wf-block--grp .wf-block__layer--title-bar{
  --wf-family-tint:#E6DCC2;
}
/* The prototype's companion rule is `.wf-block--grp
   .wf-block__layer--gpt { display:none }` for the provider logo
   (workflow-preview-build113.html:12380, "the provider logo means
   nothing on a group"). Our port needs no rule: hasProviderGlyph()
   returns false for the plan_group family, so buildInner never emits
   the --gpt layer at all and the tile falls into the glyph-less
   column rule above. Recorded so nobody adds a dead selector for it. */

/* ===== selection silhouette — .wf-block__sel-path =================== */
/* D1 (owner mandate): selection must NOT hijack the pulse-path. The
   pulse-path keeps the template invariant untouched (~1607-1635: the
   dash animation ALWAYS runs on the base rule; state classes toggle
   opacity ONLY — no stroke/animation re-declarations anywhere).
   Selection gets its OWN silhouette twin (.wf-block__sel-path, same
   `d` + pathLength=100, injected by svgBgOuter) styled with the
   steady green solid-outline technique from the prototype's focus
   outline (template ~1743-1755): stroke-dasharray 99999 — a dash
   longer than any real perimeter, the Safari-safe way to neutralise
   a dash pattern. Steady: no animation, no glow, no travel. The
   legacy rectangular ring painted the untransformed layout box and
   stays suppressed. */
.aw-newblocks .block.wf-block.selected,
.aw-newblocks .block.wf-block.primary-selection{ box-shadow:none; border-color:transparent; }
.aw-newblocks .wf-block__sel-path{
  fill:none;
  opacity:0;
  pointer-events:none;
}
.aw-newblocks .block.wf-block.selected .wf-block__sel-path{
  opacity:1;
  stroke:#43916D;
  stroke-dasharray:99999;
  stroke-width:2.4;
}
.aw-newblocks .block.wf-block.primary-selection .wf-block__sel-path{
  opacity:1;
  stroke:#43916D;
  stroke-dasharray:99999;
  stroke-width:3;
}

/* ===================================================================
   STAGE 2 — WIRES (transplant of the template's wires.css section,
   workflow-template.html ~1288-1556). Selectors carry the .aw-newblocks
   scope (doctrine: everything flag-scoped); rule bodies are verbatim
   unless marked ADAPTATION.
   =================================================================== */

/* Wire layer — the template's .wf-connections (1292-1296).
   ADAPTATION (selector + box only): the host svg is .connections-svg,
   which already carries position:absolute/inset/100% size +
   pointer-events:none + overflow:visible in panels.css (103-112) —
   identical to the template's declarations — so only the z-order is
   (re)stated here. z 21 is the load-bearing value: "in FRONT of blocks
   (open block is 20) so wires sit over the card frame" — the open
   block's z 20 is the same on our side (planflow-blocks.css .block
   .wf-block:has(.wf-block__port.is-open){z-index:20}). This is the D3
   kill: wires can no longer dive behind cards at open port columns.
   The wire tokens the transplanted rules read are re-scoped onto the
   layer (values verbatim from the tokens block above / template
   tokens.css) because the block tokens are deliberately scoped to
   .wf-block, which the svg is not inside. */
.aw-newblocks .connections-svg{
  --conn:rgba(92,154,126,.65);
  --conn-hover:#1B9264;
  --loop:#7A5AF8;
  --loop-hover:#5B3DCC;
  --cta:#1F6E45;
  --port-bg:#FBFCFA;
  --ease:cubic-bezier(.32,.72,0,1);
  /* CONSTRAINT: this svg is a child of #canvas-stage, so its user
     units are STAGE units (BASE_SCALE 0.7 screen px each), while every
     stroke/radius/dash/font literal transplanted from the template is
     a PROTOTYPE SCREEN px (its stage sat at zoom 1). The card next to
     the wire counter-transforms by --pf-zoom-comp and stays 1:1, so an
     unscaled wire painted at exactly 0.700x beside it. --pf-zoom-comp
     (canvas.js:9406 = 1/BASE_SCALE) is the stage units per prototype
     px, and is set on <body> ONLY under the flag — flag off it is
     unset and the fallback 1 keeps every value byte-identical.
     Mirrors _wireUnit() in planflow-blocks.js. */
  --pf-wire-u:var(--pf-zoom-comp, 1);
  z-index:21;
  overflow:visible;
}
/* The draft wire drawn during a connect drag lives on the same stage
   layer (canvas.js _previewClass). Its base stroke-width:2 is declared
   in panels.css:120-139, which is SHARED WITH FLAG-OFF and must not
   change; this flag-scoped override gives it the same unit conversion
   as the committed wire so the two match while the drag is live. */
.aw-newblocks .connection-path{ stroke-width:calc(2px * var(--pf-wire-u, 1)); }
.aw-newblocks .connection-path.preview{ stroke-dasharray:calc(4px * var(--pf-wire-u, 1)) calc(3px * var(--pf-wire-u, 1)); }
/* Caps that re-draw a connected sub-port dot on top of the wires
   (see drawConnections PASS 3). Mirror the live dot: cta fill with a
   light separating ring so the wire reads as terminating at the dot.
   (verbatim, template 1297-1301) */
.aw-newblocks .wf-conn-cap-ring{ fill:#F4F7F4; pointer-events:none; }
.aw-newblocks .wf-conn-cap-dot { fill:var(--cta);  pointer-events:none; }
/* The wire (verbatim, template 1393-1405 — incl. the SLOW 0.7s
   settle-out transition that the is-flowing rule's fast .12s
   overrides: snap on, fade off). */
.aw-newblocks .wf-conn{
  fill:none;stroke:var(--conn);stroke-width:calc(2px * var(--pf-wire-u, 1));stroke-linecap:round;stroke-linejoin:round;
  pointer-events:stroke;cursor:pointer;
  transition:stroke .7s var(--ease),stroke-width .7s var(--ease);
}
.aw-newblocks .wf-conn:hover{stroke:var(--conn-hover);stroke-width:calc(3px * var(--pf-wire-u, 1))}
/* Fat invisible hit-path (verbatim, template 1406-1432): 20px
   transparent stroke inserted BEFORE the visible path; hover
   forwarded to the visible sibling via ~. */
.aw-newblocks .wf-conn-hit{
  fill:none;
  stroke:transparent;
  stroke-width:calc(20px * var(--pf-wire-u, 1));
  stroke-linecap:round;
  pointer-events:stroke;
  cursor:pointer;
}
.aw-newblocks .wf-conn-hit:hover ~ .wf-conn:not(.wf-conn--repeat){
  stroke:var(--conn-hover);
  stroke-width:calc(3px * var(--pf-wire-u, 1));
}
.aw-newblocks .wf-conn-hit:hover ~ .wf-conn--repeat{
  stroke:var(--loop-hover);
  stroke-width:calc(3px * var(--pf-wire-u, 1));
  opacity:1;
}
/* Energized wire while the impulse is on it (verbatim, template
   1524-1541): brighter stroke, +0.5px, quiet glow — the fast .12s
   transition here vs the slow .7s base = snap on, afterglow off. */
.aw-newblocks .wf-conn-group.is-flowing .wf-conn:not(.wf-conn--repeat){
  stroke:var(--conn-hover);
  stroke-width:calc(3px * var(--pf-wire-u, 1));
  filter:drop-shadow(0 0 calc(4px * var(--pf-wire-u, 1)) rgba(27,146,100,0.45))
         drop-shadow(0 0 calc(8px * var(--pf-wire-u, 1)) rgba(27,146,100,0.22));
  transition:stroke .12s var(--ease),
             stroke-width .12s var(--ease),
             filter .15s var(--ease);
}
/* Repeat connection (verbatim, template 1546-1555): dashed,
   purple-tinted, routed underneath both blocks via the bottom
   anchors. */
.aw-newblocks .wf-conn--repeat{
  stroke:var(--loop);
  stroke-dasharray:calc(6px * var(--pf-wire-u, 1)) calc(5px * var(--pf-wire-u, 1));
  opacity:.95;
}
.aw-newblocks .wf-conn--repeat:hover{stroke:var(--loop-hover);opacity:1;stroke-width:calc(3px * var(--pf-wire-u, 1))}
/* While a port column is open, wires give up their pointer target so
   the rows/dots underneath stay clickable (verbatim rule body,
   template 1556). */
body.aw-newblocks:has(.wf-block__port.is-open) .wf-conn{pointer-events:none}
/* The DRAFT wire never hit-tests — it ends AT the cursor, so on the
   z-21 wire layer it would swallow the drop's elementFromPoint
   (legacy kept .connections-svg at z1 UNDER the handles, which made
   .connection-path's pointer-events:stroke harmless during a drag).
   Declarations verbatim from template 1542-1545
   (.wf-conn--draft{pointer-events:none;cursor:none}); ADAPTATION
   (selector only): the host's draft class is canvas.js's
   .connection-path.preview (_previewClass). */
.aw-newblocks .connection-path.preview{ pointer-events:none; cursor:none; }
/* (F2) The interim .wf-conn-flows running-based flow layer is
   RETIRED — the journey engine's #wfJourneySvg impulses own the wire
   dash now (owner spec: departure only on upstream completion). The
   pointer-inert rules stay so any layer left by a pre-F2 render is
   harmless until renderWires prunes it. */
.aw-newblocks .wf-conn-flows{ pointer-events:none; }
.aw-newblocks .wf-conn-flow{ pointer-events:none; }
/* ADAPTATION — AirWanderer's ratified wire colour language on top of
   the template base (parity matrix §3: "loop-generated wires stay
   dashed green … data/trigger edges keep their coloured tags").
   Values verbatim from the legacy rules (components.css 469-471,
   999-1007, 1285-1291); only the class base changed from
   .connection-path to .wf-conn. Placed after the hover rules so a
   kind keeps its identity colour under hover (width still lifts). */
.aw-newblocks .wf-conn.kind-trigger{
  stroke:var(--c-trigger);
  stroke-dasharray:calc(6px * var(--pf-wire-u, 1)) calc(4px * var(--pf-wire-u, 1));
}
.aw-newblocks .wf-conn-hit:hover ~ .wf-conn.kind-trigger,
.aw-newblocks .wf-conn.kind-trigger:hover{ stroke:var(--c-trigger); stroke-width:calc(3px * var(--pf-wire-u, 1)); }
.aw-newblocks .wf-conn.is-generated{
  stroke:#43916d;
  stroke-dasharray:calc(4px * var(--pf-wire-u, 1)) calc(3px * var(--pf-wire-u, 1));
  opacity:0.7;
}
.aw-newblocks .wf-conn.is-disabled{
  stroke:#b8b8b8;
  stroke-dasharray:calc(2px * var(--pf-wire-u, 1)) calc(4px * var(--pf-wire-u, 1));
  opacity:0.45;
}
.aw-newblocks .wf-conn.rg-external{
  pointer-events:none;
}

/* =====================================================================
   STAGE F2 — JOURNEY SYSTEM + WIRE CONTROL SURFACE (flag-scoped).
   Journey layer/path rules verbatim from the template (1336-1367,
   1690), finish trace + join dot verbatim (7559-7569), selected-wire
   + handle rules verbatim (1433-1488). Only the .aw-newblocks scope
   and the token re-scope onto the layer (same pattern as
   .connections-svg above) are AirWanderer's.
   ===================================================================== */
/* The canvas-level journey svg — always above the wires and the block
   cards so the traveling dash is never hidden by the block's own SVG
   layers (verbatim, template 1336-1347). Tokens re-scoped here because
   the layer is not inside .wf-block. */
.aw-newblocks .wf-journey-svg{
  --ease:cubic-bezier(.32,.72,0,1);
  position:absolute;inset:0;width:100%;height:100%;
  pointer-events:none;z-index:50;
  overflow:visible;
}
.aw-newblocks .wf-journey-path{
  opacity:0;
  transition:opacity .25s var(--ease, ease);
}
/* Loop journey path — purple variant fired along repeat wires
   (verbatim, template 1352-1367; wf-loop-grad defs land with the
   repeat-loop choreography stage). */
.aw-newblocks .wf-journey-path--loop{
  stroke:url(#wf-loop-grad);
  stroke-width:5;
  stroke-linecap:round;
  fill:none;
  filter:url(#wf-pulse-glow);
}
/* Journey mode reveals the paths (verbatim, template 1690). */
body.aw-newblocks.wf-is-journey .wf-journey-path{ opacity:1; }
/* a leaf with nothing to reveal closes its run by lighting up its OWN
   outline in the journey gradient — crisp, chevron-pulse language
   (verbatim, template 7559-7561) */
.aw-newblocks .wf-finish-trace{fill:none;stroke:url(#wf-pulse-grad);stroke-width:2.6;vector-effect:non-scaling-stroke;stroke-linecap:round;pointer-events:none}
/* an impulse HOLDING at an AND-join lives as a glowing dot on the
   chevron, breathing gently until the last input arrives
   (verbatim, template 7563-7569) */
@keyframes wf-join-breathe{
  0%,100%{ opacity:.82; transform:scale(1); }
  50%    { opacity:1;   transform:scale(1.22); }
}
.aw-newblocks .wf-join-dot{ animation:wf-join-breathe 1.15s ease-in-out infinite; transform-box:fill-box; transform-origin:center; pointer-events:none; }

/* ROUND 13 (owner round 11, item 4) — THE REVIEW CTA. "add sort of a button
   or something above that would be blinking. The same should be for other
   states — such as normal block view and collapsed block view." Painted by
   planflow-blocks.js refresh() (_reviewCtaSync) as a direct child of a
   paused card's node while its review modal is away: ABOVE the card in every
   view, in the hover toolbar's own band (-32px) but at the card's LEFT end
   — the toolbar's buttons are right-aligned (above) and show whenever the
   card is hovered, which it is the moment "Later" is clicked, so a centred
   CTA sat over them (measured, x13/M2 first run: CTA 139..264 vs the
   buttons from 153 on a 283 px card). Always visible, hit-testable over the
   toolbar strip (z 9 > 7) and over an open frame's head (the card sits above
   its frame; 8 px in, so the frame's fold chevron at the head's left end
   stays clear), pulsing on opacity in the waiting dot's cadence
   (wf-join-breathe 1.15s). Hover does not stop the blink — the cursor often
   rests on the card after "Later". */
.aw-newblocks .wf-block__review-cta{
  position:absolute;
  top:-36px;left:8px;
  height:26px;padding:0 12px;
  /* ROUND 14 (owner round 12): the app's own language — the serif the
     cards and the modal set in (components.css), the frame opener's solid
     green (planflow-groups.css .wf-planframe__rbtn.is-ok), the app's
     radius and a soft ink shadow. It was an amber system-font pill. */
  border:1px solid #2C6A4D;border-radius:8px;
  background:#2C6A4D;color:#fff;
  font:600 12px/1 'EB Garamond',Georgia,serif;
  letter-spacing:.01em;white-space:nowrap;
  box-shadow:0 6px 16px rgba(28,40,32,.22);
  cursor:pointer;pointer-events:auto;
  z-index:9;
  animation:wf-review-pulse 1.2s ease-in-out infinite;
}
.aw-newblocks .wf-block__review-cta:hover{filter:brightness(1.05)}
.aw-newblocks .wf-block__review-cta:active{transform:translateY(1px)}
/* a collapsed card's toolbar is CENTRED over the 78 px mini card and spans
   it on both sides: the CTA goes one band higher there (bottom 36 px above
   the card), clear of the buttons */
.aw-newblocks .block.wf-block.is-collapsed .wf-block__review-cta{top:-62px;left:0}
.aw-newblocks .block.wf-block.is-dragging .wf-block__review-cta{opacity:0;pointer-events:none;animation:none}
@keyframes wf-review-pulse{
  0%,100%{opacity:1}
  50%{opacity:.55}
}

/* ---- v39 wire control surface (owner spec: wire click opens the
   wire's control surface, not a delete confirmation) ---- */
/* Selected wire — visible-path styling while its handles are showing
   (verbatim, template 1433-1439). */
.aw-newblocks .wf-conn-group.is-selected .wf-conn{
  stroke:var(--cta, var(--conn-hover));
  stroke-width:calc(3px * var(--pf-wire-u, 1));
}
/* Control-point handles (verbatim, template 1440-1488; --dot-green
   value from the template's tokens.css line 27). The handles group
   itself stays interactive inside the pointer-inert layers. */
.aw-newblocks .wf-conn-handles{ --dot-green:#4FA37C; }
.aw-newblocks .wf-conn-handle{
  pointer-events:all;
  cursor:grab;
  transition:r .14s var(--ease), opacity .14s var(--ease),
             fill .14s var(--ease), stroke .14s var(--ease),
             filter .14s var(--ease);
}
.aw-newblocks .wf-conn-handle.is-dragging{cursor:grabbing}
/* Corner dots — the wire's bends (verbatim, template 1452-1468). */
.aw-newblocks .wf-conn-handle.is-waypoint{
  r:calc(5px * var(--pf-wire-u, 1));
  fill:var(--cta);
  stroke:var(--port-bg);
  stroke-width:calc(2.5px * var(--pf-wire-u, 1));
}
.aw-newblocks .wf-conn-handle.is-waypoint:hover,
.aw-newblocks .wf-conn-handle.is-waypoint.is-dragging{
  r:calc(5.5px * var(--pf-wire-u, 1));
  filter:drop-shadow(0 0 calc(3.5px * var(--pf-wire-u, 1)) rgba(31,110,69,.5));
}
/* Segment grips — latent "grab to slide this run" handles
   (verbatim, template 1469-1488). */
.aw-newblocks .wf-conn-handle.is-grip{
  r:calc(3.5px * var(--pf-wire-u, 1));
  fill:var(--port-bg);
  stroke:var(--dot-green);
  stroke-width:calc(1.5px * var(--pf-wire-u, 1));
}
.aw-newblocks .wf-conn-handle.is-grip:hover,
.aw-newblocks .wf-conn-handle.is-grip.is-dragging{
  r:calc(5px * var(--pf-wire-u, 1));
  fill:var(--cta);
  stroke:var(--port-bg);
  stroke-width:calc(2px * var(--pf-wire-u, 1));
  filter:drop-shadow(0 0 calc(3.5px * var(--pf-wire-u, 1)) rgba(31,110,69,.5));
}
/* Hover ghost — translucent preview dot shown on the selected
   wire under the cursor, advertising that a double-click drops a
   bend point there. Non-interactive so it never eats the click.
   (verbatim, template 1489-1498) */
.aw-newblocks .wf-conn-ghost{
  fill:var(--cta, #1B9264);
  stroke:#fff;
  stroke-width:calc(1.5px * var(--pf-wire-u, 1));
  opacity:0.4;
  pointer-events:none;
}

/* Wire labels (drawConnections PASS 4) — name the data each wire
   carries (its source port). ROUND 23 (owner, by the report mode:
   "`text.wf-conn-label__text` … 'Step 3 · Story Writing' — the text on
   the wires is too messy looking"): the prototype's bare, rotated,
   halo'd text (template 1302-1318) is now ONE short line in the app's
   serif on a pill in the canvas well colour with a hairline, never
   rotated, lifted off the stroke, hidden under 40 % zoom
   (planflow-blocks.js WLABEL_* / placeWireLabel). The group carries the
   translate; pill and text are centred on its origin, so the label's
   own drag moves both. Still DRAGGABLE along the wire by its text, still
   late children of the connection svg so they read on top of the wires.
   The tokens sit on the layer so a theme stylesheet can retune them. */
.aw-newblocks .wf-conn-labels{
  --ease:cubic-bezier(.32,.72,0,1);
  --label-ink:#22312A;                         /* the cards' ink */
  --label-pill:#EEF1EF;                        /* the canvas (#f5f7f6) under the inspector's well tint */
  --label-line:rgba(70,92,80,.32);             /* hairline */
}
.aw-newblocks .wf-conn-labels.is-zoomed-out{ display:none; }
.aw-newblocks .wf-conn-label{ cursor:grab; }
.aw-newblocks .wf-conn-label__pill{
  fill:var(--label-pill); stroke:var(--label-line); stroke-width:calc(1px * var(--pf-wire-u, 1));
  pointer-events:none;
  transition:stroke .12s var(--ease);
  visibility:hidden;   /* ROUND 39 (owner: "it should be just text"): the pill keeps its geometry (seating, overlap checks) and is not painted */
}
.aw-newblocks .wf-conn-label__text{
  fill:var(--label-ink);
  /* ROUND 39: a paper halo under the letters keeps them legible where the wire runs beneath */
  paint-order:stroke; stroke:var(--label-pill); stroke-width:calc(4px * var(--pf-wire-u, 1)); stroke-linejoin:round;
  font-family:'EB Garamond',Georgia,serif; font-weight:600; font-size:calc(12px * var(--pf-wire-u, 1));
  letter-spacing:.01em;
  text-anchor:middle; dominant-baseline:central;
  cursor:grab; pointer-events:auto; user-select:none;
  transition:fill .12s var(--ease);
}
.aw-newblocks .wf-conn-label:hover .wf-conn-label__pill,
.aw-newblocks .wf-conn-label.is-dragging .wf-conn-label__pill,
.aw-newblocks .wf-conn-label.is-selected .wf-conn-label__pill{ stroke:var(--conn-hover, #1B9264); }
.aw-newblocks .wf-conn-label__text:hover{ fill:#000; }
.aw-newblocks .wf-conn-label.is-dragging .wf-conn-label__text{ cursor:grabbing; fill:#000; }
.aw-newblocks .wf-conn-label.is-selected .wf-conn-label__text{ fill:#000; }
/* Inline editor shown when a wire label is double-clicked to rename.
   (verbatim rule body, template 1320-1327; --cta value inlined as the
   fallback because the input mounts on <body>, outside the token
   scopes — same re-scope pattern as .wf-conn-handles above.) */
body.aw-newblocks .wf-conn-label-edit{
  position:fixed; transform:translate(-50%,-50%); z-index:200;
  min-width:54px; width:auto; padding:2px 7px;
  font-family:inherit; font-size:11px; font-weight:600; color:#1d1d1d;
  text-align:center; background:#fff;
  border:1.5px solid var(--cta, #1F6E45); border-radius:7px;
  box-shadow:0 4px 14px rgba(31,46,38,.18); outline:none;
}

/* =====================================================================
   V4 — A VIEW TOGGLE PRESSED DURING A RUN IS QUEUED, NOT LOST.
   The ONLY new rule in the card-view-modes slice, and an ADAPTATION with
   no prototype counterpart: the prototype's Run button stays disabled for
   the whole journey (workflow-template.html 6491), so a prototype card can
   never be toggled mid-run. AirWanderer's journey is driven by a real event
   stream and can be on screen for as long as the backend takes, so the
   click is remembered and replayed when the run settles
   (planflow-blocks.js _deferWhileJourney) — and the button has to SAY so,
   or a view click made mid-run is indistinguishable from a dead button.
   Appended at the end of the file on purpose: this file's line numbering is
   cited from planflow-blocks.js, planflow-groups.js and planflow-groups.css.
   ===================================================================== */
.aw-newblocks .wf-block__top-btn.is-pending{ animation:wf-btn-pending 1.1s var(--ease) infinite; }
@keyframes wf-btn-pending{
  0%,100% { opacity:1; }
  50%     { opacity:.42; }
}

/* =====================================================================
   ROUND 13 (track V) — THE RUN LOCK ON A CARD'S TOOLBAR.
   Owner, 2026-10-01: a block the live run is using (processing / already
   processed) keeps its view until the run ends. planflow-blocks.js
   _paintRunLock puts `is-run-locked` on the card and aria-disabled + the
   title "Locked while this run uses it" on its two view toggles
   (planflow-groups.js does the same to a folded card's expand button
   through the same recipe). Here: the muted look, the no-hover, and a
   subtle lock glyph in the button's corner; a refused press nudges once.
   An ADAPTATION with no Build-113 counterpart (its Run button is disabled
   for the whole journey, template 6491). Appended at the end on purpose —
   this file's line numbers are cited elsewhere.
   ===================================================================== */
.aw-newblocks .block.wf-block .wf-block__top-btn[aria-disabled="true"]{
  position:relative; cursor:not-allowed; color:var(--muted); opacity:.74;
}
.aw-newblocks .block.wf-block .wf-block__top-btn[aria-disabled="true"]:hover{
  background:var(--surface); color:var(--muted); border-color:var(--border); transform:none;
}
.aw-newblocks .block.wf-block .wf-block__top-btn[aria-disabled="true"]:active{ transform:none; }
.aw-newblocks .block.wf-block .wf-block__top-btn[aria-disabled="true"]::after{
  content:""; position:absolute; right:-4px; bottom:-4px; width:11px; height:11px; border-radius:4px;
  background:#6E8077 url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2.8' stroke-linecap='round' stroke-linejoin='round'><rect x='5' y='11' width='14' height='9' rx='2'/><path d='M8 11V8a4 4 0 0 1 8 0v3'/></svg>") center/7px 7px no-repeat;
  box-shadow:0 0 0 1.5px #fff;
}
.aw-newblocks .block.wf-block .wf-block__top-btn.is-lock-nudge{ animation:wf-btn-locknudge .42s var(--ease); }
@keyframes wf-btn-locknudge{
  0%,100% { transform:translateX(0); }
  30%     { transform:translateX(-2px); }
  70%     { transform:translateX(2px); }
}

/* round 27p (owner): a wire wears the colour of the block it comes FROM - the family
   colour at the ordinary wire's weight (--conn is green at .65); hover = the family colour.
   AI stays the green; a wire leaving a group stays plum (planflow-frames.css, loaded later). */
.aw-newblocks .wf-conn.wf-conn--from-input{stroke:rgba(45,121,158,.65)}
.aw-newblocks .wf-conn.wf-conn--from-output{stroke:rgba(94,73,168,.65)}
.aw-newblocks .wf-conn.wf-conn--from-generate{stroke:color-mix(in srgb, var(--pf-generate) 65%, transparent)}
.aw-newblocks .wf-conn.wf-conn--from-plan_group{stroke:rgba(143,107,51,.65)}
.aw-newblocks .wf-conn.wf-conn--from-input:hover{stroke:#2d799e}
.aw-newblocks .wf-conn.wf-conn--from-output:hover{stroke:#5e49a8}
.aw-newblocks .wf-conn.wf-conn--from-generate:hover{stroke:var(--pf-generate)}
.aw-newblocks .wf-conn.wf-conn--from-plan_group:hover{stroke:#8F6B33}

/* round 27s (owner): hover, selection, the energized state and the label pill follow the
   wire's family colour as well (round 27p coloured the resting stroke only) */
.aw-newblocks .wf-conn-hit:hover ~ .wf-conn.wf-conn--from-input:not(.wf-conn--frame){stroke:#2d799e}
.aw-newblocks .wf-conn-group.is-selected .wf-conn.wf-conn--from-input:not(.wf-conn--frame){stroke:#2d799e}
.aw-newblocks .wf-conn-group.is-flowing .wf-conn.wf-conn--from-input:not(.wf-conn--frame){stroke:#2d799e;filter:drop-shadow(0 0 calc(4px * var(--pf-wire-u, 1)) rgba(45,121,158,0.45))}
.aw-newblocks .wf-conn-label.is-from-input:not(.is-frame) .wf-conn-label__pill{stroke:color-mix(in srgb, #2d799e 45%, white)}
.aw-newblocks .wf-conn-label.is-from-input:not(.is-frame):hover .wf-conn-label__pill,
.aw-newblocks .wf-conn-label.is-from-input:not(.is-frame).is-dragging .wf-conn-label__pill,
.aw-newblocks .wf-conn-label.is-from-input:not(.is-frame).is-selected .wf-conn-label__pill{stroke:#2d799e}
.aw-newblocks .wf-conn-hit:hover ~ .wf-conn.wf-conn--from-output:not(.wf-conn--frame){stroke:#5e49a8}
.aw-newblocks .wf-conn-group.is-selected .wf-conn.wf-conn--from-output:not(.wf-conn--frame){stroke:#5e49a8}
.aw-newblocks .wf-conn-group.is-flowing .wf-conn.wf-conn--from-output:not(.wf-conn--frame){stroke:#5e49a8;filter:drop-shadow(0 0 calc(4px * var(--pf-wire-u, 1)) rgba(94,73,168,0.45))}
.aw-newblocks .wf-conn-label.is-from-output:not(.is-frame) .wf-conn-label__pill{stroke:color-mix(in srgb, #5e49a8 45%, white)}
.aw-newblocks .wf-conn-label.is-from-output:not(.is-frame):hover .wf-conn-label__pill,
.aw-newblocks .wf-conn-label.is-from-output:not(.is-frame).is-dragging .wf-conn-label__pill,
.aw-newblocks .wf-conn-label.is-from-output:not(.is-frame).is-selected .wf-conn-label__pill{stroke:#5e49a8}
.aw-newblocks .wf-conn-hit:hover ~ .wf-conn.wf-conn--from-generate:not(.wf-conn--frame){stroke:var(--pf-generate)}
.aw-newblocks .wf-conn-group.is-selected .wf-conn.wf-conn--from-generate:not(.wf-conn--frame){stroke:var(--pf-generate)}
.aw-newblocks .wf-conn-group.is-flowing .wf-conn.wf-conn--from-generate:not(.wf-conn--frame){stroke:var(--pf-generate);filter:drop-shadow(0 0 calc(4px * var(--pf-wire-u, 1)) rgba(90,100,110,0.40))}
.aw-newblocks .wf-conn-label.is-from-generate:not(.is-frame) .wf-conn-label__pill{stroke:color-mix(in srgb, var(--pf-generate) 45%, white)}
.aw-newblocks .wf-conn-label.is-from-generate:not(.is-frame):hover .wf-conn-label__pill,
.aw-newblocks .wf-conn-label.is-from-generate:not(.is-frame).is-dragging .wf-conn-label__pill,
.aw-newblocks .wf-conn-label.is-from-generate:not(.is-frame).is-selected .wf-conn-label__pill{stroke:var(--pf-generate)}
.aw-newblocks .wf-conn-hit:hover ~ .wf-conn.wf-conn--from-plan_group:not(.wf-conn--frame){stroke:#8F6B33}
.aw-newblocks .wf-conn-group.is-selected .wf-conn.wf-conn--from-plan_group:not(.wf-conn--frame){stroke:#8F6B33}
.aw-newblocks .wf-conn-group.is-flowing .wf-conn.wf-conn--from-plan_group:not(.wf-conn--frame){stroke:#8F6B33;filter:drop-shadow(0 0 calc(4px * var(--pf-wire-u, 1)) rgba(143,107,51,0.45))}
.aw-newblocks .wf-conn-label.is-from-plan_group:not(.is-frame) .wf-conn-label__pill{stroke:color-mix(in srgb, #8F6B33 45%, white)}
.aw-newblocks .wf-conn-label.is-from-plan_group:not(.is-frame):hover .wf-conn-label__pill,
.aw-newblocks .wf-conn-label.is-from-plan_group:not(.is-frame).is-dragging .wf-conn-label__pill,
.aw-newblocks .wf-conn-label.is-from-plan_group:not(.is-frame).is-selected .wf-conn-label__pill{stroke:#8F6B33}

/* ROUND 55 - "Run this block" off: the card stays in place, dimmed, its pill reads Off; a run that
   skipped it dims it the same way with the pill Skipped (the dot stays the idle grey) */
.aw-newblocks .block.wf-block.is-off .wf-block__card,
.aw-newblocks .block.wf-block.is-skipped .wf-block__card{opacity:.62;transition:opacity .24s cubic-bezier(.32,.72,0,1)}
.aw-newblocks .block.wf-block.is-off.is-selected .wf-block__card,
.aw-newblocks .block.wf-block.is-off:hover .wf-block__card{opacity:.82}
.aw-newblocks .block.wf-block.is-off .wf-block__statuspill__dot,
.aw-newblocks .block.wf-block.is-skipped .wf-block__statuspill__dot{background:var(--dot-gray);box-shadow:0 0 0 calc(1.5px * var(--scale)) rgba(167,176,170,.18)}

/* ---- round 65 (2.3.6): the card reads "unpriced" for a model without a price row ---- */
.aw-newblocks .wf-block__metric.pfb-metric-cost--unpriced > span:last-child{font-style:italic;letter-spacing:.01em;opacity:.85}
