/* ═══════════════════════════════════════════════════════════════════════════
   flow-workbench.css — the Orchestrator workbench (wave 9, #2267)

   Jamie: "I want a full enterprise ready experience", "that enterprise ready
   workbench style, we want it to look smooth and pretty too", "make sure it looks
   sexy." Two references: Power Automate's new designer (clean command bar, soft
   cards, a properties pane on the right) and TIBCO Business Studio (a grouped
   palette with Recently Used and Favorites pinned above, a properties panel docked
   at the bottom).

   One stylesheet, one token block. Everything below draws from the tokens, so a
   later theme is this file and nothing else. Loads AFTER orchestrator.css, which it
   deliberately overrides for the elements that moved (.orchestrator-topbar).

   The portal has no dark mode (#2259) — there is not one prefers-color-scheme rule
   in any of its stylesheets — so there is no dark variant here either. When the
   portal gets a real theme switch, this file gets one with it.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
    --flow-bg: linear-gradient(180deg, #fbfcfd 0%, #f4f6f8 100%);
    --flow-grid: #e6eaf0;
    --flow-card-bg: #fff;
    --flow-card-border: #d9dee7;
    --flow-card-radius: 12px;
    --flow-card-shadow: 0 1px 2px rgba(16, 24, 40, .06), 0 1px 3px rgba(16, 24, 40, .08);
    --flow-card-shadow-hover: 0 4px 12px rgba(16, 24, 40, .12);
    /* The portal's brand blue, read from portal-console.css (--console-accent), not
       invented. /try does not load that sheet, so the value is written out here. */
    --flow-accent: #1e4d7b;
    --flow-edge: #98a2b3;
    --flow-edge-w: 2px;
    --flow-success: #12b76a;
    --flow-error: #f04438;
    --flow-warning: #f79009;
    --flow-muted: #667085;
    --flow-text: #101828;
    --flow-ease: 160ms cubic-bezier(.2, .8, .2, 1);
}

/* Derived from the block above — never a second source of truth. rgb() triples so a
   tint can be written as rgba(var(--flow-accent-rgb), .06) without a second hex. */
:root {
    --flow-accent-rgb: 30, 77, 123;
    --flow-accent-soft: #e7effa;
    --flow-accent-ink: #123a5e;
    --flow-surface: #fff;
    --flow-surface-sunken: #f7f9fb;
    --flow-line: #e4e8ee;
    --flow-line-soft: #eef1f5;
    --flow-radius: 12px;
    --flow-radius-sm: 8px;
    --flow-shadow-pop: 0 12px 28px rgba(16, 24, 40, .14), 0 2px 6px rgba(16, 24, 40, .08);
    --flow-palette-w: 300px;
    --flow-inspector-w: 0px;
    --flow-dock-h: 240px;
    --flow-dock-strip: 40px;
}

/* ═══════════════════════════════════════════ */
/* COMMAND BAR                                 */
/* ═══════════════════════════════════════════ */

/* One clean row. The old topbar was `flex-wrap: wrap` with a 12px gap and eleven
   Bootstrap outline buttons in a .btn-group, which stacked into two rows below about
   1400px and moved the workspace down with it (#876 root cause 2). This one is a
   single row that scrolls its right half rather than wrapping, so the workbench top
   edge never moves. */
.orchestrator-topbar.flow-commandbar {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: nowrap;
    margin: 0 0 10px;
    padding: 6px 10px 6px 12px;
    min-height: 44px;
    background: var(--flow-surface);
    border: 1px solid var(--flow-line);
    border-radius: var(--flow-radius);
    box-shadow: var(--flow-card-shadow);
    /* NOT overflow:hidden — the Export dropdown is absolutely positioned inside this
       bar and any clipping ancestor cuts the menu off. The left group clips instead. */
}

.flow-cb-left {
    display: flex;
    align-items: center;
    gap: 8px;
    /* min-width:0 is what lets the name truncate instead of shoving the buttons out of
       the bar. Without it a flex item refuses to shrink below its content, and with
       `nowrap` the overflow lands ON TOP of the group next to it. */
    min-width: 0;
    flex: 1 1 auto;
    overflow: hidden;
}

.flow-cb-right {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
    flex: 0 0 auto;
}

.flow-cb-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    flex: 0 0 28px;
    border-radius: var(--flow-radius-sm);
    background: var(--flow-accent-soft);
    color: var(--flow-accent);
    font-size: 0.95rem;
}

/* The workflow name reads as a document title until you touch it, then it is a field.
   A bordered input in a command bar reads as a form; this reads as a name. */
.flow-commandbar .workflow-name-input,
.flow-commandbar input.workflow-name-input {
    flex: 0 1 15rem;
    width: 15rem;
    max-width: 15rem;
    min-width: 5rem;
    height: 30px;
    padding: 0 8px;
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--flow-text);
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--flow-radius-sm);
    box-shadow: none;
    transition: background var(--flow-ease), border-color var(--flow-ease);
}

.flow-commandbar .workflow-name-input:hover {
    background: var(--flow-surface-sunken);
    border-color: var(--flow-line);
}

.flow-commandbar .workflow-name-input:focus {
    background: var(--flow-surface);
    border-color: var(--flow-accent);
    box-shadow: 0 0 0 3px rgba(var(--flow-accent-rgb), .12);
    outline: none;
}

.flow-commandbar .workflow-name-input[readonly] {
    cursor: default;
}

/* The state chip. Color IS the message here, so each state gets its own pair and
   nothing relies on reading the word at a glance. */
.flow-state-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    height: 22px;
    padding: 0 9px;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    white-space: nowrap;
    border: 1px solid transparent;
}

.flow-state-chip::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    flex: 0 0 6px;
}

.flow-state-chip[data-state="draft"] {
    color: #5b6472;
    background: #f1f3f6;
    border-color: #e2e6ec;
}

.flow-state-chip[data-state="saved"] {
    color: var(--flow-accent);
    background: var(--flow-accent-soft);
    border-color: #cfe0f2;
}

.flow-state-chip[data-state="published"] {
    color: #087443;
    background: #e6f7ef;
    border-color: #c2ecd8;
}

.flow-state-chip[data-state="stale"] {
    color: #9a5b00;
    background: #fff5e5;
    border-color: #ffe2b8;
}

/* The estimated cost rides ON the Run button — DESIGN §8: state the price before the
   click. #535 makes interactive use free, so the number is information rather than a
   charge, and it is styled as a quiet pill inside the button rather than a warning
   beside it. Bootstrap's .badge sets color and background, so both are re-stated. */
.flow-commandbar .token-cost-badge,
.flow-commandbar #tokenCostBadge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    height: 20px;
    padding: 0 8px;
    margin-left: 2px;
    border-radius: 999px;
    font-size: 0.69rem;
    font-weight: 600;
    color: #fff;
    background: rgba(255, 255, 255, .18);
    border: 0;
    font-variant-numeric: tabular-nums;
}

.flow-commandbar #tokenCostBadge i {
    font-size: 0.72rem;
    color: rgba(255, 255, 255, .8);
}

/* #2399 — how many of the steps about to run are dry-run stubs. Same pill as the price
   beside it, in amber rather than white, because it qualifies the number rather than
   restating it: the price is for work some of which will not happen. Written by
   updateDryRunNotice() only when there IS one, so an ordinary workflow gains no chrome.
   The reason and the published-run path are in its title. */
.flow-commandbar .run-dry-badge,
.flow-commandbar #runDryRunBadge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    height: 20px;
    padding: 0 8px;
    margin-left: 4px;
    border-radius: 999px;
    font-size: 0.69rem;
    font-weight: 600;
    color: #ffe9c2;
    background: rgba(255, 178, 44, .26);
    border: 1px solid rgba(255, 199, 105, .45);
    font-variant-numeric: tabular-nums;
}

.flow-commandbar #runDryRunBadge i {
    font-size: 0.72rem;
    color: rgba(255, 233, 194, .85);
}

/* #2448 — on the portal the chip is a CONTROL: it takes you to the one thing that is next
   on the way to a real run (publish it, save it, run it). It has to look clickable, and it
   has to show a focus ring of its own, because it lives inside the Run button and the
   button's ring is not it. On /try it stays a caption and gains none of this. */
.flow-commandbar .run-dry-badge-action {
    cursor: pointer;
}

.flow-commandbar .run-dry-badge-action:hover {
    background: rgba(255, 178, 44, .4);
    border-color: rgba(255, 199, 105, .75);
}

.flow-commandbar .run-dry-badge-action:focus-visible {
    outline: 2px solid rgba(255, 233, 194, .9);
    outline-offset: 1px;
}

/* display:inline-flex above beats the UA's [hidden]{display:none}, so say it here. */
.flow-commandbar .run-dry-badge[hidden],
.flow-commandbar #runDryRunBadge[hidden] {
    display: none;
}

/* ── button sets ──────────────────────────────────────────────────────────── */

.flow-btnset {
    display: flex;
    align-items: center;
    gap: 2px;
}

/* Hairline dividers instead of borders around every control. */
.flow-cb-divider {
    width: 1px;
    height: 22px;
    background: var(--flow-line);
    flex: 0 0 1px;
    margin: 0 4px;
}

/*
 * Every button in the bar, including the ones orchestrator.js re-classes at runtime.
 * #btnPublish's className is rewritten to `btn btn-warning btn-sm` by
 * updatePublishButton(), so the neutralizing rule has to reach `.btn` and not only
 * our own class — otherwise the bar grows one solid yellow button the moment a
 * workflow is published.
 */
.flow-commandbar .flow-btn,
.flow-commandbar .btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 32px;
    padding: 0 10px;
    font-size: 0.8rem;
    font-weight: 500;
    line-height: 1;
    color: #3d4757;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--flow-radius-sm);
    box-shadow: none;
    white-space: nowrap;
    transition: background var(--flow-ease), color var(--flow-ease), border-color var(--flow-ease);
}

.flow-commandbar .flow-btn i,
.flow-commandbar .btn i {
    font-size: 0.92rem;
    line-height: 1;
    color: var(--flow-muted);
    transition: color var(--flow-ease);
}

.flow-commandbar .flow-btn:hover,
.flow-commandbar .btn:hover,
.flow-commandbar .flow-btn:focus-visible,
.flow-commandbar .btn:focus-visible {
    color: var(--flow-accent-ink);
    background: rgba(var(--flow-accent-rgb), .07);
    border-color: transparent;
    outline: none;
}

.flow-commandbar .flow-btn:hover i,
.flow-commandbar .btn:hover i {
    color: var(--flow-accent);
}

.flow-commandbar .flow-btn:focus-visible,
.flow-commandbar .btn:focus-visible {
    box-shadow: 0 0 0 3px rgba(var(--flow-accent-rgb), .18);
}

.flow-commandbar .flow-btn:disabled,
.flow-commandbar .btn:disabled {
    opacity: .5;
    background: transparent;
}

/* Publish is the one quiet button with a job of its own: it changes what the outside
   world can reach. Tinted, never filled — Run is the only filled control here. */
.flow-commandbar #btnPublish {
    color: var(--flow-accent);
    background: var(--flow-accent-soft);
}

.flow-commandbar #btnPublish i {
    color: var(--flow-accent);
}

.flow-commandbar #btnPublish:hover {
    background: #d8e6f5;
}

/* Run carries the price. DESIGN §8: the cost of a metered action is stated before the
   click, read from the catalog — so the number lives ON the button. */
.flow-commandbar #btnRun,
.flow-commandbar .btn#btnRun {
    height: 32px;
    padding: 0 14px;
    color: #fff;
    font-weight: 600;
    background: var(--flow-accent);
    border-color: var(--flow-accent);
    box-shadow: 0 1px 2px rgba(16, 24, 40, .10);
}

.flow-commandbar #btnRun i {
    color: #fff;
    font-size: 1rem;
}

.flow-commandbar #btnRun:hover {
    background: var(--flow-accent-ink);
    border-color: var(--flow-accent-ink);
    color: #fff;
}

.flow-commandbar #btnRun:hover i {
    color: #fff;
}

.flow-run-cost {
    font-weight: 500;
    opacity: .78;
    font-variant-numeric: tabular-nums;
}

.flow-run-cost::before {
    content: "·";
    margin-right: 5px;
    opacity: .7;
}

/* The Export split button keeps Bootstrap's .btn-group for dropdown positioning; the
   caret is the only part of that styling worth keeping. */
.flow-commandbar .btn-group > .btn.dropdown-toggle::after {
    margin-left: 4px;
    opacity: .55;
}

.flow-commandbar .dropdown-menu {
    border-color: var(--flow-line);
    border-radius: var(--flow-radius);
    box-shadow: var(--flow-shadow-pop);
    padding: 6px;
    min-width: 20rem;
}

.flow-commandbar .dropdown-item {
    border-radius: var(--flow-radius-sm);
    padding: 8px 10px;
    font-size: 0.83rem;
}

.flow-commandbar .dropdown-item:hover,
.flow-commandbar .dropdown-item:focus {
    background: rgba(var(--flow-accent-rgb), .07);
    color: var(--flow-accent-ink);
}

/*
 * NARROWING THE BAR — labels go before anything moves.
 *
 * The portal gives this bar the window less a 212px rail and its own padding, and the
 * widest it ever gets is with Publish showing: nine controls plus the name and the
 * state chip. Measured on the real markup, that is ~1130px, so somewhere around a
 * 1400px window the labels have to start going. They go in the order a person would
 * pick: the things you reach for occasionally (Examples, Form input, Files, Export)
 * before the four that are the file menu.
 *
 * `font-size: 0` on the button rather than a wrapper span around each label: two of
 * these buttons have their innerHTML rewritten at runtime (updatePublishButton writes
 * `<i …></i> Publish`, withButtonBusy writes a spinner and a verb), so a span would
 * survive on some of them and not others. The icon re-states its own size, and the
 * `title` on every button is what carries the name once the label is gone.
 */
/* Scoped by `.flow-cb-file ~` on purpose: only the PORTAL bar has a file menu, and
   only the portal bar is crowded. /try carries three controls and keeps its labels at
   every width — a rule keyed to the viewport rather than to what is in the bar would
   strip them for no reason. */
@media (max-width: 1399.98px) {
    .flow-cb-file ~ .flow-cb-secondary .flow-btn,
    .flow-cb-file ~ .flow-cb-secondary .btn {
        font-size: 0;
        gap: 0;
        padding: 0 9px;
    }

    .flow-cb-file ~ .flow-cb-secondary .flow-btn i,
    .flow-cb-file ~ .flow-cb-secondary .btn i {
        font-size: 0.95rem;
    }

    /* Bootstrap draws the caret with em-sized borders, so font-size:0 erases it. */
    .flow-cb-file ~ .flow-cb-secondary .btn.dropdown-toggle::after {
        border-top-width: 4px;
        border-right-width: 4px;
        border-left-width: 4px;
        margin-left: 5px;
    }
}

@media (max-width: 1199.98px) {
    .flow-cb-file .flow-btn,
    .flow-cb-file .btn {
        font-size: 0;
        gap: 0;
        padding: 0 9px;
    }

    .flow-cb-file .flow-btn i,
    .flow-cb-file .btn i {
        font-size: 0.95rem;
    }

    .flow-commandbar .workflow-name-input {
        flex: 0 1 11rem;
        max-width: 11rem;
    }
}

/* ═══════════════════════════════════════════ */
/* THE WORKBENCH GRID                          */
/* ═══════════════════════════════════════════ */

/*
 * configuration | actions | canvas, over a full-width dock.
 *
 * THE FLOW IS THE RIGHTMOST THING ON SCREEN, ALWAYS. Jeff Loucks, 2026-09-07 16:30
 * [47:14]: "everything on the left that configures the flow and the flow itself on the
 * right"; [37:16] "all of the things that go into setting up what you have focus on
 * should be left of what you have focus on, which is right." This used to be a swap
 * that only applied while a card was selected; browsing put the palette back on the
 * left and the canvas in the middle, which is the Visual Studio shape he was
 * complaining about. One layout in every state instead.
 *
 * The left column is the configuration column and it is never empty: the step
 * inspector fills it while a card is selected, and a workflow-level pane naming the
 * next thing to do fills it the rest of the time.
 *
 * HEIGHT. The `height` here is the FALLBACK for a host that cannot do better —
 * /try/orchestrator, which sits in the marketing layout's normal document flow under
 * a nav, a heading and (on dev) an environment banner, so the space left for it is
 * not knowable in CSS. 20rem is what that chrome measures on the widest of those
 * cases; the clamp is what keeps a short window usable and a tall one from turning
 * the drawer into a wall. The PORTAL does not use any of this: it makes
 * .console-content a flex column and this a flex child, which is what #876 replaced
 * the old `calc(100vh - 140px)` magic number with. Do not reintroduce a constant there.
 */
.flow-workbench {
    display: grid;
    grid-template-columns: var(--flow-inspector-w) var(--flow-palette-w) minmax(0, 1fr);
    /* #2271 — the drawer never takes more than a third of the workbench. FlowDock
       clamps a stored height against the WINDOW (innerHeight - 260), which on a
       1280x800 laptop allows 540px inside a workbench that is 480px tall: the canvas
       became a 238px letterbox and a two-step pipeline auto-fitted to 40%, which is
       unreadable. The percentage resolves against the grid container, which is the
       thing the canvas actually has to share. #2283 makes the drag handle stop there
       too, so a drag past the cap does not look ignored. */
    grid-template-rows: minmax(0, 1fr) min(var(--flow-dock-h), 38%);
    grid-template-areas:
        "inspector palette canvas"
        "dock      dock    dock";
    gap: 0;
    height: clamp(26rem, calc(100vh - 20rem), 62rem);
    min-height: 0;
    background: var(--flow-surface);
    border: 1px solid var(--flow-line);
    border-radius: var(--flow-radius);
    overflow: hidden;
    box-shadow: var(--flow-card-shadow);
    transition: grid-template-columns var(--flow-ease);
}

.flow-workbench.flow-dock-collapsed {
    grid-template-rows: minmax(0, 1fr) var(--flow-dock-strip);
}

/* The configuration column is always there, because something always fills it. */
.flow-workbench {
    --flow-inspector-w: 360px;
}

.flow-workbench.palette-collapsed {
    --flow-palette-w: 48px;
}

/* The canvas cell: the RIGHTMOST column, and it owns every spare pixel. The rule carried
   a `.blockly-panel` alternate while #2267 and #2266 landed in either order; #2272
   removed it with the workspace it was for. */
.flow-workbench > .flow-panel {
    grid-area: canvas;
    position: relative;
    min-width: 0;
    min-height: 0;
    overflow: hidden;
    background: var(--flow-bg);
}

/* ═══════════════════════════════════════════ */
/* PALETTE                                     */
/* ═══════════════════════════════════════════ */

.flow-palette {
    grid-area: palette;
    display: flex;
    flex-direction: column;
    min-height: 0;
    min-width: 0;
    background: var(--flow-surface);
    border-right: 1px solid var(--flow-line);
}

.flow-palette-head {
    flex: 0 0 auto;
    padding: 10px 10px 8px;
    border-bottom: 1px solid var(--flow-line-soft);
}

.flow-palette-title {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 8px;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--flow-muted);
}

.flow-palette-title i {
    font-size: 0.85rem;
    color: var(--flow-accent);
}

.flow-palette-collapse {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    padding: 0;
    color: var(--flow-muted);
    background: transparent;
    border: 0;
    border-radius: 6px;
    transition: background var(--flow-ease), color var(--flow-ease);
}

.flow-palette-collapse:hover {
    background: var(--flow-surface-sunken);
    color: var(--flow-accent);
}

/* The search field, inset rather than raised — it is a filter over the list below it,
   not a control that submits something. */
.flow-search {
    position: relative;
    display: flex;
    align-items: center;
}

.flow-search > i {
    position: absolute;
    left: 10px;
    font-size: 0.85rem;
    color: var(--flow-muted);
    pointer-events: none;
}

.flow-search input {
    width: 100%;
    height: 32px;
    padding: 0 40px 0 30px;
    font-size: 0.82rem;
    color: var(--flow-text);
    background: var(--flow-surface-sunken);
    border: 1px solid var(--flow-line);
    border-radius: var(--flow-radius-sm);
    box-shadow: inset 0 1px 2px rgba(16, 24, 40, .04);
    transition: border-color var(--flow-ease), box-shadow var(--flow-ease), background var(--flow-ease);
}

.flow-search input::placeholder {
    color: #98a2b3;
}

.flow-search input:focus {
    outline: none;
    background: var(--flow-surface);
    border-color: var(--flow-accent);
    box-shadow: 0 0 0 3px rgba(var(--flow-accent-rgb), .12);
}

.flow-search kbd {
    position: absolute;
    right: 8px;
    min-width: 18px;
    padding: 1px 5px;
    font-family: inherit;
    font-size: 0.68rem;
    font-weight: 600;
    color: var(--flow-muted);
    background: var(--flow-surface);
    border: 1px solid var(--flow-line);
    border-bottom-width: 2px;
    border-radius: 5px;
    pointer-events: none;
    transition: opacity var(--flow-ease);
}

.flow-search input:focus ~ kbd {
    opacity: 0;
}

/* Pick mode: a "+" on the canvas was clicked and the next palette click lands there. */
.flow-palette-pick {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 8px;
    padding: 7px 9px;
    font-size: 0.76rem;
    font-weight: 600;
    color: var(--flow-accent-ink);
    background: var(--flow-accent-soft);
    border: 1px dashed rgba(var(--flow-accent-rgb), .38);
    border-radius: var(--flow-radius-sm);
}

.flow-palette-pick[hidden] {
    display: none;
}

.flow-palette-pick button {
    margin-left: auto;
    padding: 0 6px;
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--flow-accent);
    background: transparent;
    border: 0;
    border-radius: 5px;
}

.flow-palette-pick button:hover {
    background: rgba(var(--flow-accent-rgb), .12);
}

.flow-palette-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 8px 6px 16px;
    scrollbar-width: thin;
}

.flow-palette-body::-webkit-scrollbar {
    width: 10px;
}

.flow-palette-body::-webkit-scrollbar-thumb {
    background: #d6dbe3;
    border: 3px solid var(--flow-surface);
    border-radius: 999px;
}

.flow-palette-body::-webkit-scrollbar-thumb:hover {
    background: #bcc4cf;
}

/* ── sections and bands ───────────────────────────────────────────────────── */

.flow-palette-section {
    margin-bottom: 8px;
}

.flow-palette-sechead,
.flow-palette-bandhead {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 8px 8px 5px;
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: #8a93a1;
}

.flow-palette-sechead i,
.flow-palette-bandhead i {
    font-size: 0.8rem;
    color: #a6aeba;
}

/* #2341 — with nothing expanded on a first load, the band header is what tells you how
   much is behind it. Pushed right so the four counts line up in a column. */
.flow-palette-bandcount {
    margin-left: auto;
    flex: 0 0 auto;
    min-width: 20px;
    padding: 1px 6px;
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0;
    text-align: center;
    color: var(--flow-muted);
    background: var(--flow-surface-sunken);
    border-radius: 999px;
    font-variant-numeric: tabular-nums;
}

.flow-palette-band {
    margin-bottom: 6px;
    padding-top: 2px;
    border-top: 1px solid var(--flow-line-soft);
}

.flow-palette-band:first-child {
    border-top: 0;
}

.flow-palette-bandname {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ── a category, collapsible ──────────────────────────────────────────────── */

.flow-palette-cathead {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    height: 32px;
    padding: 0 8px;
    text-align: left;
    color: #3d4757;
    font-size: 0.8rem;
    font-weight: 600;
    background: transparent;
    border: 0;
    border-radius: var(--flow-radius-sm);
    transition: background var(--flow-ease), color var(--flow-ease);
}

.flow-palette-cathead:hover {
    background: rgba(var(--flow-accent-rgb), .06);
    color: var(--flow-accent-ink);
}

.flow-palette-chev {
    font-size: 0.7rem !important;
    color: #a6aeba !important;
    transition: transform var(--flow-ease);
}

.flow-palette-cat.is-open .flow-palette-chev {
    transform: rotate(90deg);
}

.flow-palette-dot {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    flex: 0 0 20px;
    border-radius: 6px;
    color: var(--item-color, var(--flow-accent));
    background: rgba(var(--item-rgb, 30, 77, 123), .13);
}

.flow-palette-dot i {
    font-size: 0.72rem;
}

.flow-palette-catname {
    flex: 1 1 auto;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.flow-palette-count {
    flex: 0 0 auto;
    min-width: 20px;
    padding: 1px 6px;
    font-size: 0.66rem;
    font-weight: 600;
    text-align: center;
    color: var(--flow-muted);
    background: var(--flow-surface-sunken);
    border-radius: 999px;
    font-variant-numeric: tabular-nums;
}

.flow-palette-catbody {
    padding: 2px 0 6px 6px;
    margin-left: 11px;
    border-left: 1px solid var(--flow-line-soft);
}

/* ── an item ──────────────────────────────────────────────────────────────── */

.flow-palette-item {
    display: flex;
    align-items: center;
    gap: 8px;
    height: 36px;
    padding: 0 6px 0 6px;
    border-radius: var(--flow-radius-sm);
    cursor: grab;
    user-select: none;
    transition: background var(--flow-ease), box-shadow var(--flow-ease);
}

/* #2407 — the highlight has to draw the row's OWN bounds, not a faint wash that reads
   the same from one row up. A 1px inset ring says exactly what a click will hit. */
.flow-palette-item:hover,
.flow-palette-item.is-active {
    background: rgba(var(--flow-accent-rgb), .09);
    box-shadow: inset 0 0 0 1px rgba(var(--flow-accent-rgb), .22);
}

.flow-palette-item:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px rgba(var(--flow-accent-rgb), .35);
}

.flow-palette-item.is-dragging {
    opacity: .55;
}

.flow-palette-item:active {
    cursor: grabbing;
}

/* ── #2407: a structural row is not an action row ─────────────────────────────
   "Run in parallel", "For each" and "Condition" reshape the canvas; every other row
   adds a step. In RECENTLY USED they sit side by side at the same height, and on
   2026-09-07 one row of pointer slop turned a fan-out into a duplicated Dataverse
   step. Three things separate them now, and all three travel with the item so the
   treatment is the same in Recently used and in Flow control: the row is tinted in
   its own hue and ringed, the tile is a squarer, firmer chip than an action's soft
   one, and the price slot carries the word "Structure" instead of a token count. */
.flow-palette-item.is-structural {
    margin: 3px 0;
    background: rgba(var(--item-rgb, 30, 77, 123), .07);
    box-shadow: inset 0 0 0 1px rgba(var(--item-rgb, 30, 77, 123), .26);
}

.flow-palette-item.is-structural:hover,
.flow-palette-item.is-structural.is-active {
    background: rgba(var(--item-rgb, 30, 77, 123), .14);
    box-shadow: inset 0 0 0 1px rgba(var(--item-rgb, 30, 77, 123), .55);
}

.flow-palette-item.is-structural .flow-palette-tile {
    border-radius: 5px;
    background: rgba(var(--item-rgb, 30, 77, 123), .18);
    box-shadow: inset 0 0 0 1px rgba(var(--item-rgb, 30, 77, 123), .5);
}

.flow-palette-item.is-structural .flow-palette-label {
    font-weight: 600;
    color: var(--item-color, var(--flow-accent));
}

.flow-palette-kind {
    flex: 0 0 auto;
    padding: 1px 6px;
    font-size: 0.57rem;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--item-color, var(--flow-accent));
    background: rgba(var(--item-rgb, 30, 77, 123), .12);
    border: 1px solid rgba(var(--item-rgb, 30, 77, 123), .34);
    border-radius: 999px;
    white-space: nowrap;
}

.flow-palette-tile {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    flex: 0 0 28px;
    border-radius: 8px;
    color: var(--item-color, var(--flow-accent));
    background: rgba(var(--item-rgb, 30, 77, 123), .14);
    box-shadow: inset 0 0 0 1px rgba(var(--item-rgb, 30, 77, 123), .18);
    transition: transform var(--flow-ease);
}

.flow-palette-item:hover .flow-palette-tile {
    transform: scale(1.05);
}

.flow-palette-tile i {
    font-size: 0.86rem;
}

.flow-palette-label {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 0.8rem;
    font-weight: 500;
    color: var(--flow-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.flow-palette-also {
    flex: 0 0 auto;
    font-size: 0.62rem;
    font-style: italic;
    color: #a6aeba;
    white-space: nowrap;
}

.flow-palette-cost {
    flex: 0 0 auto;
    padding: 1px 5px;
    font-size: 0.63rem;
    font-weight: 600;
    color: #7a8494;
    background: var(--flow-surface-sunken);
    border: 1px solid var(--flow-line-soft);
    border-radius: 999px;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
    transition: opacity var(--flow-ease);
}

/* The star appears on hover, and stays put once it is on — a row of permanent gray
   stars is noise, and a starred row that hides its star is a lie. */
.flow-palette-star {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    padding: 0;
    color: #b9c1cc;
    background: transparent;
    border: 0;
    border-radius: 6px;
    opacity: 0;
    transition: opacity var(--flow-ease), color var(--flow-ease), background var(--flow-ease);
}

.flow-palette-item:hover .flow-palette-star,
.flow-palette-star:focus-visible,
.flow-palette-star.is-on {
    opacity: 1;
}

.flow-palette-star.is-on {
    color: #f0a52b;
}

.flow-palette-star:hover {
    color: #e08c00;
    background: rgba(240, 165, 43, .14);
}

.flow-palette-star i {
    font-size: 0.78rem;
}

.flow-palette-item:hover .flow-palette-cost {
    opacity: .55;
}

/* ── empty state ──────────────────────────────────────────────────────────── */

.flow-palette-empty {
    padding: 28px 16px;
    text-align: center;
}

.flow-palette-empty-title {
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--flow-text);
}

.flow-palette-empty-hint {
    margin-top: 4px;
    font-size: 0.75rem;
    color: var(--flow-muted);
}

/* ── the collapsed rail ───────────────────────────────────────────────────── */

.flow-palette-rail {
    display: none;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 10px 0;
}

.flow-palette.is-collapsed .flow-palette-head,
.flow-palette.is-collapsed .flow-palette-body {
    display: none;
}

.flow-palette.is-collapsed .flow-palette-rail {
    display: flex;
}

.flow-palette-railbtn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    color: var(--flow-muted);
    background: transparent;
    border: 0;
    border-radius: var(--flow-radius-sm);
    transition: background var(--flow-ease), color var(--flow-ease);
}

.flow-palette-railbtn:hover {
    color: var(--flow-accent);
    background: rgba(var(--flow-accent-rgb), .09);
}

.flow-palette-railbtn i {
    font-size: 1rem;
}

/* The way back out, always first in the rail and separated from the band glyphs. */
.flow-palette-railexpand {
    margin-bottom: 4px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--flow-line-soft);
    border-radius: var(--flow-radius-sm) var(--flow-radius-sm) 0 0;
    color: var(--flow-accent);
}

/* ═══════════════════════════════════════════ */
/* INSPECTOR — the shell (#2267), then the      */
/* properties panel itself (#2268)              */
/* ═══════════════════════════════════════════ */

.flow-inspector {
    grid-area: inspector;
    display: flex;
    flex-direction: column;
    min-height: 0;
    min-width: 0;
    overflow: hidden;
    background: var(--flow-surface);
    /* The edge that faces the canvas. The pane is the LEFT column now, so the seam is
       drawn on its right; a border on the other side reads as a line in mid-air. */
    border-right: 1px solid var(--flow-line);
}

/* `hidden` is the closed state, and it has to beat `display: flex` above. */
.flow-inspector[hidden] {
    display: none;
}

.flow-inspector-head {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 12px;
    border-bottom: 1px solid var(--flow-line-soft);
}

.flow-inspector-head .flow-inspector-title {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--flow-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.flow-inspector-head .flow-inspector-sub {
    font-size: 0.72rem;
    color: var(--flow-muted);
}

.flow-inspector-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 12px;
    font-size: 0.82rem;
    color: var(--flow-text);
}

.flow-inspector-placeholder {
    padding: 32px 12px;
    text-align: center;
    color: var(--flow-muted);
    font-size: 0.78rem;
}

.flow-inspector-placeholder i {
    display: block;
    margin-bottom: 8px;
    font-size: 1.6rem;
    color: #ccd3dc;
}

/* ─────────────────────────────────────────────────────────────────────────────
   THE PROPERTIES PANEL (#2268)

   Power Automate's right-hand pane is the model: a header that says which node you
   are in, then the parameters, then the things you can DO to it, with the canvas
   still visible beside it. Everything below is 8px-rhythm, hairline-separated and
   drawn from the token block at the top of this file — there is no color here that
   is not one of those tokens or a tint of one.
   ───────────────────────────────────────────────────────────────────────────── */

.flow-inspector-head {
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
    padding: 10px 12px 8px;
    background: linear-gradient(180deg, #fff 0%, var(--flow-surface-sunken) 100%);
}

.flow-insp-head-row {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
}

/* The icon tile, tinted by the same category tone the card carries, so the panel and
   the card you clicked are visibly the same object. */
.flow-insp-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 32px;
    width: 32px;
    height: 32px;
    border-radius: 9px;
    font-size: 0.95rem;
    background: rgba(91, 100, 112, .14);
    color: var(--flow-tone-secondary, #5b6470);
}

.flow-insp-head-row[data-tone="primary"]   .flow-insp-icon { background: rgba(13, 110, 253, .14); color: var(--flow-tone-primary); }
.flow-insp-head-row[data-tone="secondary"] .flow-insp-icon { background: rgba(91, 100, 112, .14); color: var(--flow-tone-secondary); }
.flow-insp-head-row[data-tone="success"]   .flow-insp-icon { background: rgba(25, 135, 84, .14);  color: var(--flow-tone-success); }
.flow-insp-head-row[data-tone="danger"]    .flow-insp-icon { background: rgba(220, 53, 69, .14);  color: var(--flow-tone-danger); }
.flow-insp-head-row[data-tone="warning"]   .flow-insp-icon { background: rgba(178, 106, 0, .14);  color: var(--flow-tone-warning); }
.flow-insp-head-row[data-tone="info"]      .flow-insp-icon { background: rgba(10, 124, 143, .14); color: var(--flow-tone-info); }
.flow-insp-head-row[data-tone="dark"]      .flow-insp-icon { background: rgba(33, 37, 41, .12);   color: var(--flow-tone-dark); }
.flow-insp-head-row[data-tone="light"]     .flow-insp-icon { background: rgba(108, 117, 125, .14); color: var(--flow-tone-light); }

.flow-insp-heading {
    min-width: 0;
    flex: 1 1 auto;
}

.flow-insp-title {
    display: block;
    font-size: 0.9rem;
    font-weight: 600;
    line-height: 1.25;
    color: var(--flow-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The label reads as a NAME until you touch it, then it is a field. A bordered box in
   a header reads as a form; this reads as the thing it is. */
.flow-insp-title-input {
    width: 100%;
    padding: 2px 6px;
    margin-left: -6px;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 6px;
    transition: background var(--flow-ease), border-color var(--flow-ease);
}

.flow-insp-title-input:hover {
    background: #fff;
    border-color: var(--flow-line);
}

.flow-insp-title-input:focus {
    outline: none;
    background: #fff;
    border-color: var(--flow-accent);
    box-shadow: 0 0 0 3px rgba(var(--flow-accent-rgb), .16);
}

.flow-inspector-head .flow-inspector-sub {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.flow-insp-close {
    flex: 0 0 auto;
    width: 26px;
    height: 26px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    font-size: 0.72rem;
    color: var(--flow-muted);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 7px;
    cursor: pointer;
    transition: background var(--flow-ease), color var(--flow-ease), border-color var(--flow-ease);
}

.flow-insp-close:hover {
    background: #fff;
    border-color: var(--flow-line);
    color: var(--flow-text);
}

.flow-insp-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

.flow-insp-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    max-width: 100%;
    padding: 1px 7px;
    font-size: 0.68rem;
    line-height: 1.6;
    color: var(--flow-muted);
    background: var(--flow-surface-sunken);
    border: 1px solid var(--flow-line);
    border-radius: 999px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.flow-insp-chip.is-id {
    font-family: var(--bs-font-monospace, ui-monospace, SFMono-Regular, Menlo, monospace);
    color: var(--flow-accent-ink);
    background: var(--flow-accent-soft);
    border-color: rgba(var(--flow-accent-rgb), .18);
}

.flow-insp-chip.is-cost {
    color: #7a4c00;
    background: rgba(247, 144, 9, .12);
    border-color: rgba(247, 144, 9, .3);
}

.flow-insp-chip.is-key {
    font-family: var(--bs-font-monospace, ui-monospace, SFMono-Regular, Menlo, monospace);
    font-size: 0.66rem;
}

/* #2331 — the API's own code for a failure, beside the reason. */
.flow-insp-chip.is-code {
    font-family: var(--bs-font-monospace, ui-monospace, SFMono-Regular, Menlo, monospace);
    font-size: 0.64rem;
    letter-spacing: .02em;
    color: #912018;
    background: rgba(240, 68, 56, .1);
    border-color: rgba(240, 68, 56, .28);
}

/* #2331 — a step that FAILED, in the Output section. Same red as the card's error ring,
   so the two read as one fact rather than two opinions. */
.flow-insp-failure {
    margin-bottom: 8px;
    padding: 8px 10px;
    background: rgba(240, 68, 56, .06);
    border: 1px solid rgba(240, 68, 56, .24);
    border-radius: 8px;
}

.flow-insp-failure-head {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    font-size: 0.76rem;
    font-weight: 600;
    line-height: 1.45;
    color: #912018;
}

.flow-insp-failure-head .bi {
    flex: 0 0 auto;
    margin-top: 1px;
    font-size: 0.8rem;
}

.flow-insp-failure-detail {
    margin-top: 4px;
    font-size: 0.72rem;
    line-height: 1.5;
    color: var(--flow-muted);
    overflow-wrap: anywhere;
}

.flow-insp-failure .flow-insp-chip.is-code {
    margin-top: 6px;
}

.flow-insp-busy {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.72rem;
    color: var(--flow-muted);
}

.flow-insp-busy .spinner-border {
    width: 0.75rem;
    height: 0.75rem;
    border-width: 0.12em;
}

/* ── SECTIONS ─────────────────────────────────────────────────────────────── */

.flow-inspector-body {
    padding: 0;
}

.flow-insp-section {
    padding: 10px 12px 12px;
    border-bottom: 1px solid var(--flow-line-soft);
}

.flow-insp-section:last-of-type {
    border-bottom: 0;
}

.flow-insp-section-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 8px;
}

/* #2475 — the badge and the heading are one group, so space-between still puts the hint
   on the far right instead of pushing the number away from the words it numbers. The badge
   itself is the shared component (step-groups.css); nothing about it is redeclared here. */
.flow-insp-section-name {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.flow-insp-section-title {
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--flow-muted);
}

.flow-insp-section-hint {
    font-size: 0.68rem;
    color: #98a2b3;
}

.flow-insp-help,
.flow-insp-empty {
    margin: 6px 0 0;
    font-size: 0.72rem;
    line-height: 1.45;
    color: var(--flow-muted);
}

.flow-insp-note {
    margin: 0 0 8px;
    padding: 8px 10px;
    font-size: 0.72rem;
    line-height: 1.45;
    border-radius: var(--flow-radius-sm);
    background: var(--flow-surface-sunken);
    border: 1px solid var(--flow-line);
    color: var(--flow-muted);
}

.flow-insp-note.is-error {
    color: #8a1c14;
    background: rgba(240, 68, 56, .07);
    border-color: rgba(240, 68, 56, .28);
}

/* ── ONE PARAMETER ────────────────────────────────────────────────────────── */

.flow-prop {
    position: relative;
    padding: 8px 0;
    border-top: 1px dashed var(--flow-line-soft);
}

.flow-prop:first-of-type {
    border-top: 0;
    padding-top: 0;
}

.flow-prop-label {
    display: flex;
    align-items: center;
    gap: 5px;
    margin-bottom: 4px;
    min-width: 0;
}

.flow-prop-name {
    font-size: 0.76rem;
    font-weight: 600;
    color: var(--flow-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.flow-prop-required {
    color: var(--flow-error);
    font-weight: 700;
    line-height: 1;
}

.flow-prop-type {
    padding: 0 5px;
    font-size: 0.62rem;
    line-height: 1.7;
    color: var(--flow-muted);
    background: var(--flow-surface-sunken);
    border: 1px solid var(--flow-line);
    border-radius: 999px;
}

.flow-prop-type.is-odd {
    color: #7a4c00;
    background: rgba(247, 144, 9, .12);
    border-color: rgba(247, 144, 9, .3);
}

/* The ⚡. It sits at the right edge of the label line, low-contrast at rest so a
   column of them does not read as a column of warnings, and accent on hover. */
.flow-prop-bind {
    margin-left: auto;
    flex: 0 0 auto;
    width: 24px;
    height: 22px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    font-size: 0.7rem;
    color: #98a2b3;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 6px;
    cursor: pointer;
    transition: color var(--flow-ease), background var(--flow-ease), border-color var(--flow-ease);
}

.flow-prop-bind:hover,
.flow-prop-bind:focus-visible {
    color: var(--flow-accent);
    background: var(--flow-accent-soft);
    border-color: rgba(var(--flow-accent-rgb), .25);
    outline: none;
}

.flow-prop-control {
    min-width: 0;
}

.flow-prop-input {
    width: 100%;
    font-size: 0.78rem;
    border-radius: var(--flow-radius-sm);
    border-color: var(--flow-card-border);
}

.flow-prop-input:focus {
    border-color: var(--flow-accent);
    box-shadow: 0 0 0 3px rgba(var(--flow-accent-rgb), .14);
}

textarea.flow-prop-input {
    line-height: 1.45;
    resize: vertical;
}

.flow-prop-json {
    font-family: var(--bs-font-monospace, ui-monospace, SFMono-Regular, Menlo, monospace);
    font-size: 0.72rem;
}

.flow-prop-json.is-invalid {
    border-color: var(--flow-error);
    box-shadow: 0 0 0 3px rgba(240, 68, 56, .14);
}

.flow-prop-json-error {
    margin-top: 4px;
    font-size: 0.68rem;
    line-height: 1.4;
    color: #8a1c14;
}

.flow-prop-check {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 0.76rem;
    color: var(--flow-muted);
    cursor: pointer;
}

.flow-prop-check .form-check-input {
    margin: 0;
    cursor: pointer;
}

.flow-prop-check .form-check-input:checked {
    background-color: var(--flow-accent);
    border-color: var(--flow-accent);
}

/* A BOUND value. Not a text box wearing a disabled attribute — a pill that says what
   it is wired to, with the one control that matters on it. */
.flow-prop-chip {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 5px 6px 5px 9px;
    font-size: 0.74rem;
    color: var(--flow-accent-ink);
    background: var(--flow-accent-soft);
    border: 1px solid rgba(var(--flow-accent-rgb), .28);
    border-radius: var(--flow-radius-sm);
    min-width: 0;
}

.flow-prop-chip > i {
    flex: 0 0 auto;
    font-size: 0.72rem;
    opacity: .8;
}

.flow-prop-chip-text {
    flex: 1 1 auto;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.flow-prop-chip-x {
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    font-size: 0.7rem;
    color: var(--flow-accent-ink);
    background: transparent;
    border: 0;
    border-radius: 5px;
    cursor: pointer;
    opacity: .65;
    transition: opacity var(--flow-ease), background var(--flow-ease);
}

.flow-prop-chip-x:hover {
    opacity: 1;
    background: rgba(var(--flow-accent-rgb), .12);
}

.flow-prop-help {
    margin-top: 4px;
    font-size: 0.68rem;
    line-height: 1.4;
    color: #98a2b3;
}

.flow-prop-note {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 5px;
    margin-top: 5px;
    padding: 5px 8px;
    font-size: 0.69rem;
    line-height: 1.4;
    color: var(--flow-muted);
    background: var(--flow-surface-sunken);
    border: 1px solid var(--flow-line);
    border-radius: 6px;
}

.flow-prop-note.is-good {
    color: #05603a;
    background: rgba(18, 183, 106, .09);
    border-color: rgba(18, 183, 106, .28);
}

.flow-prop-note.is-warning {
    color: #7a4c00;
    background: rgba(247, 144, 9, .1);
    border-color: rgba(247, 144, 9, .3);
}

.flow-prop-note-action {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-left: auto;
    padding: 1px 7px;
    font-size: 0.67rem;
    color: var(--flow-accent);
    background: #fff;
    border: 1px solid rgba(var(--flow-accent-rgb), .3);
    border-radius: 999px;
    cursor: pointer;
    white-space: nowrap;
    transition: background var(--flow-ease), color var(--flow-ease);
}

.flow-prop-note-action:hover {
    background: var(--flow-accent);
    color: #fff;
}

.flow-prop-issue {
    display: flex;
    align-items: flex-start;
    gap: 5px;
    margin-top: 4px;
    font-size: 0.69rem;
    line-height: 1.4;
}

.flow-prop-issue > i {
    flex: 0 0 auto;
    margin-top: 2px;
    font-size: 0.62rem;
}

.flow-prop-issue.is-error {
    color: #8a1c14;
}

.flow-prop-issue.is-warning {
    color: #7a4c00;
}

.flow-insp-issues .flow-prop-issue:first-child {
    margin-top: 0;
}

/* "Show 3 more parameters" — #2050's answer, one click rather than unreachable. */
.flow-insp-more,
.flow-insp-add {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: 8px;
    padding: 4px 10px;
    font-size: 0.72rem;
    font-weight: 500;
    color: var(--flow-accent);
    background: #fff;
    border: 1px dashed rgba(var(--flow-accent-rgb), .35);
    border-radius: 999px;
    cursor: pointer;
    transition: background var(--flow-ease), border-color var(--flow-ease);
}

.flow-insp-more:hover,
.flow-insp-add:hover {
    background: var(--flow-accent-soft);
    border-style: solid;
}

/* ── TOOLS ────────────────────────────────────────────────────────────────── */

.flow-insp-tools {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.flow-insp-tool {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 10px;
    font-size: 0.73rem;
    font-weight: 500;
    color: var(--flow-text);
    background: #fff;
    border: 1px solid var(--flow-card-border);
    border-radius: var(--flow-radius-sm);
    box-shadow: var(--flow-card-shadow);
    cursor: pointer;
    transition: box-shadow var(--flow-ease), border-color var(--flow-ease), transform var(--flow-ease);
}

.flow-insp-tool:hover {
    border-color: rgba(var(--flow-accent-rgb), .4);
    box-shadow: var(--flow-card-shadow-hover);
    transform: translateY(-1px);
}

.flow-insp-tool > i {
    color: var(--flow-accent);
}

/* The one tool that is the NEXT QUESTION rather than a side trip (#1785's
   "choose a destination"). It leads, so it is filled. */
.flow-insp-tool.is-primary {
    color: #fff;
    background: var(--flow-accent);
    border-color: var(--flow-accent);
}

.flow-insp-tool.is-primary > i {
    color: #fff;
}

.flow-insp-subrow {
    margin-top: 6px;
}

.flow-insp-toggle {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.75rem;
    color: var(--flow-text);
    cursor: pointer;
}

.flow-insp-toggle .form-check-input {
    margin: 0;
    cursor: pointer;
}

.flow-insp-toggle .form-check-input:checked {
    background-color: var(--flow-accent);
    border-color: var(--flow-accent);
}

.flow-insp-json {
    max-height: 220px;
    margin: 8px 0 0;
    padding: 8px 10px;
    overflow: auto;
    font-size: 0.68rem;
    line-height: 1.45;
    color: #344054;
    background: var(--flow-surface-sunken);
    border: 1px solid var(--flow-line);
    border-radius: var(--flow-radius-sm);
    white-space: pre-wrap;
    word-break: break-word;
}

/* ── THE LOOP'S OWN PANEL (#2254/#2284) ───────────────────────────────────────
   Deliberately built out of the branch panel's parts where they fit — the body list IS
   .flow-insp-branch — so the two containers read as siblings in the inspector as well
   as on the canvas. What is new here is a settings row (a label beside a control) and a
   two-option choice, neither of which a fan-out needs. */

.flow-insp-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 6px;
}

.flow-insp-row > .form-control,
.flow-insp-row > .form-select {
    flex: 0 0 92px;
    margin-left: auto;
}

.flow-insp-label {
    font-size: 12.5px;
    color: var(--flow-text);
    margin: 0;
}

/* #2349 — the destination chooser's own fields (Azure DevOps project, work item type).
   The parameter rows below have a label built by the inspector; the chooser is a section
   the host draws, so its labels are its own. */
.flow-insp-sublabel {
    display: block;
    margin: 0 0 3px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .01em;
    color: var(--flow-muted);
}

/* The collection, shown as the SOURCE TEXT it is: the customer sees the same
   `(output-of "query" "rows")` here, on the card and in the Source pane. */
.flow-insp-binding {
    flex: 1 1 auto;
    min-width: 0;
    padding: 4px 8px;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12px;
    color: #344054;
    background: var(--flow-surface-sunken);
    border: 1px solid var(--flow-line);
    border-radius: var(--flow-radius-sm);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.flow-insp-binding.is-missing {
    color: var(--flow-warning);
    border-color: rgba(247, 144, 9, .4);
    background: rgba(247, 144, 9, .06);
    font-style: italic;
}

.flow-insp-choice {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    width: 100%;
    padding: 8px 10px;
    margin-bottom: 6px;
    text-align: left;
    background: #fff;
    border: 1px solid var(--flow-line);
    border-radius: var(--flow-radius-sm);
    cursor: pointer;
    transition: border-color var(--flow-ease), background var(--flow-ease);
}

.flow-insp-choice:hover { border-color: #c3ccd9; }

.flow-insp-choice.is-on {
    border-color: var(--flow-accent);
    background: rgba(var(--flow-accent-rgb), .05);
}

.flow-insp-choice > i { color: var(--flow-muted); margin-top: 1px; }
.flow-insp-choice.is-on > i { color: var(--flow-accent); }

.flow-insp-choice-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.flow-insp-choice-label { font-size: 12.5px; font-weight: 600; color: var(--flow-text); }
.flow-insp-choice-note { font-size: 11.5px; color: var(--flow-muted); line-height: 1.4; }

/* ── BRANCHES (a fan-out's own panel) ─────────────────────────────────────── */

.flow-insp-branch {
    display: flex;
    align-items: center;
    gap: 4px;
    margin-bottom: 6px;
}

.flow-insp-branch-open {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    padding: 6px 8px;
    text-align: left;
    background: #fff;
    border: 1px solid var(--flow-card-border);
    border-radius: var(--flow-radius-sm);
    cursor: pointer;
    transition: border-color var(--flow-ease), box-shadow var(--flow-ease);
}

.flow-insp-branch-open:hover {
    border-color: rgba(var(--flow-accent-rgb), .4);
    box-shadow: var(--flow-card-shadow);
}

.flow-insp-branch-open > i {
    color: var(--flow-muted);
}

.flow-insp-branch-text {
    min-width: 0;
    display: block;
}

.flow-insp-branch-label {
    display: block;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--flow-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.flow-insp-branch-sub {
    display: block;
    font-size: 0.67rem;
    color: var(--flow-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.flow-insp-branch-x {
    flex: 0 0 auto;
    width: 26px;
    height: 26px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    font-size: 0.64rem;
    color: var(--flow-muted);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 6px;
    cursor: pointer;
}

.flow-insp-branch-x:hover {
    color: var(--flow-error);
    background: rgba(240, 68, 56, .08);
    border-color: rgba(240, 68, 56, .25);
}

/* ── FOOTER ───────────────────────────────────────────────────────────────── */

.flow-insp-footer {
    padding: 10px 12px 14px;
}

.flow-insp-danger {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    justify-content: center;
    padding: 6px 10px;
    font-size: 0.74rem;
    font-weight: 500;
    color: #8a1c14;
    background: #fff;
    border: 1px solid rgba(240, 68, 56, .35);
    border-radius: var(--flow-radius-sm);
    cursor: pointer;
    transition: background var(--flow-ease), color var(--flow-ease);
}

.flow-insp-danger:hover {
    color: #fff;
    background: var(--flow-error);
    border-color: var(--flow-error);
}

/* ── THE DYNAMIC-CONTENT PICKER ───────────────────────────────────────────── */

.flow-picker {
    position: absolute;
    z-index: 12;
    top: 26px;
    right: 0;
    left: 0;
    max-height: 300px;
    overflow: auto;
    background: #fff;
    border: 1px solid var(--flow-card-border);
    border-radius: var(--flow-radius-sm);
    box-shadow: var(--flow-shadow-pop);
    animation: flow-picker-in 120ms cubic-bezier(.2, .8, .2, 1);
}

@keyframes flow-picker-in {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: none; }
}

.flow-picker-head {
    position: sticky;
    top: 0;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 8px 7px 10px;
    background: linear-gradient(180deg, #fff 0%, var(--flow-surface-sunken) 100%);
    border-bottom: 1px solid var(--flow-line-soft);
}

.flow-picker-title {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--flow-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.flow-picker-close {
    flex: 0 0 auto;
    width: 22px;
    height: 22px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    font-size: 0.62rem;
    color: var(--flow-muted);
    background: transparent;
    border: 0;
    border-radius: 5px;
    cursor: pointer;
}

.flow-picker-close:hover {
    background: var(--flow-line-soft);
    color: var(--flow-text);
}

.flow-picker-body {
    padding: 4px 0 6px;
}

.flow-picker-group + .flow-picker-group {
    border-top: 1px solid var(--flow-line-soft);
    margin-top: 4px;
    padding-top: 4px;
}

.flow-picker-group-title {
    padding: 4px 10px 2px;
    font-size: 0.64rem;
    font-weight: 700;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: #98a2b3;
}

.flow-picker-group-hint {
    padding: 0 10px 4px;
    font-size: 0.67rem;
    color: #98a2b3;
}

.flow-picker-item {
    display: flex;
    width: 100%;
    align-items: baseline;
    gap: 8px;
    padding: 5px 10px;
    text-align: left;
    background: transparent;
    border: 0;
    cursor: pointer;
    transition: background var(--flow-ease);
}

.flow-picker-item:hover,
.flow-picker-item:focus-visible {
    background: var(--flow-accent-soft);
    outline: none;
}

.flow-picker-item-text {
    display: flex;
    align-items: baseline;
    gap: 8px;
    min-width: 0;
    width: 100%;
}

.flow-picker-item-label {
    flex: 0 1 auto;
    min-width: 0;
    font-size: 0.73rem;
    color: var(--flow-text);
    font-family: var(--bs-font-monospace, ui-monospace, SFMono-Regular, Menlo, monospace);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.flow-picker-item-sub {
    flex: 0 0 auto;
    margin-left: auto;
    font-size: 0.65rem;
    color: #98a2b3;
    white-space: nowrap;
}

.flow-picker-custom {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 6px 10px 2px;
}

.flow-picker-custom .form-select,
.flow-picker-custom .form-control {
    font-size: 0.72rem;
    min-width: 0;
}

.flow-picker-use {
    flex: 0 0 auto;
    padding: 3px 10px;
    font-size: 0.7rem;
    color: #fff;
    background: var(--flow-accent);
    border: 1px solid var(--flow-accent);
    border-radius: 6px;
    cursor: pointer;
}

.flow-picker-use:hover {
    filter: brightness(1.08);
}

/* The panel arrives rather than appearing. Same easing as the cards it belongs to. */
.flow-inspector:not([hidden]) {
    animation: flow-inspector-in 180ms cubic-bezier(.2, .8, .2, 1);
}

@keyframes flow-inspector-in {
    from { opacity: 0; transform: translateX(8px); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .flow-inspector:not([hidden]),
    .flow-picker {
        animation: none;
    }

    .flow-insp-tool:hover {
        transform: none;
    }
}

/* ═══════════════════════════════════════════ */
/* DOCK — Source / Run / Problems              */
/* ═══════════════════════════════════════════ */

.flow-dock {
    grid-area: dock;
    display: flex;
    flex-direction: column;
    min-height: 0;
    background: var(--flow-surface);
    border-top: 1px solid var(--flow-line);
}

/* The resize handle. 6px of hit area, one hairline of paint, a grip on hover. */
.flow-dock-handle {
    position: relative;
    flex: 0 0 6px;
    height: 6px;
    margin-top: -3px;
    cursor: row-resize;
    z-index: 3;
}

.flow-dock-handle::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: 34px;
    height: 3px;
    transform: translate(-50%, -50%);
    border-radius: 999px;
    background: transparent;
    transition: background var(--flow-ease);
}

.flow-dock-handle:hover::after,
.flow-dock.is-resizing .flow-dock-handle::after {
    background: rgba(var(--flow-accent-rgb), .45);
}

.flow-dock-tabs {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 2px;
    height: var(--flow-dock-strip);
    padding: 0 8px;
    background: var(--flow-surface);
    border-bottom: 1px solid var(--flow-line-soft);
}

.flow-dock-tabs > button[data-dock-tab] {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 28px;
    padding: 0 10px;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--flow-muted);
    background: transparent;
    border: 0;
    border-radius: 7px;
    transition: color var(--flow-ease), background var(--flow-ease);
}

.flow-dock-tabs > button[data-dock-tab] i {
    font-size: 0.85rem;
    opacity: .8;
}

.flow-dock-tabs > button[data-dock-tab]:hover {
    color: var(--flow-accent-ink);
    background: rgba(var(--flow-accent-rgb), .06);
}

.flow-dock-tabs > button[data-dock-tab].is-active {
    color: var(--flow-accent);
}

/* The sliding underline: one pseudo-element per tab, animated from the center out, so
   the active mark reads as travel rather than as a swap. */
.flow-dock-tabs > button[data-dock-tab]::after {
    content: "";
    position: absolute;
    left: 10px;
    right: 10px;
    bottom: -7px;
    height: 2px;
    border-radius: 2px;
    background: var(--flow-accent);
    transform: scaleX(0);
    transition: transform var(--flow-ease);
}

.flow-dock-tabs > button[data-dock-tab].is-active::after {
    transform: scaleX(1);
}

.flow-dock-badge {
    min-width: 18px;
    padding: 0 5px;
    font-size: 0.64rem;
    font-weight: 700;
    line-height: 16px;
    text-align: center;
    color: var(--flow-muted);
    background: var(--flow-surface-sunken);
    border: 1px solid var(--flow-line);
    border-radius: 999px;
    font-variant-numeric: tabular-nums;
}

.flow-dock-badge[hidden] {
    display: none;
}

.flow-dock-tabs > button.has-errors .flow-dock-badge {
    color: #fff;
    background: var(--flow-error);
    border-color: var(--flow-error);
}

.flow-dock-strip-right {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
}

/* #parseStatus keeps its Bootstrap color classes (orchestrator.js rewrites className
   wholesale in updateParseStatus), so only shape and size are set here — by id, which
   survives that rewrite. */
.flow-dock #parseStatus {
    height: 20px;
    padding: 0 8px;
    font-size: 0.66rem;
    font-weight: 600;
    line-height: 20px;
    border-radius: 999px;
}

/* #2347 — "Empty" is neither green nor red, and DESIGN §8 forbids a Bootstrap default
   here, so the neutral state gets a token-built chip of its own. */
.flow-dock #parseStatus.flow-chip-empty {
    color: var(--flow-muted);
    background: var(--flow-surface-sunken);
    border: 1px solid var(--flow-line);
    line-height: 18px;
}

.flow-dock-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 5px;
    height: 24px;
    padding: 0 8px;
    font-size: 0.72rem;
    font-weight: 500;
    color: var(--flow-muted);
    background: transparent;
    border: 0;
    border-radius: 6px;
    transition: background var(--flow-ease), color var(--flow-ease);
}

.flow-dock-action:hover {
    color: var(--flow-accent-ink);
    background: rgba(var(--flow-accent-rgb), .07);
}

/*
 * #2309 — Advanced. The Source tab is not rendered until the toggle is on: "The lisp is
 * gone and the new interface arrises" (Jamie, 2026-09-06). `hidden` alone is not enough
 * here — the strip's own `display: inline-flex` on every [data-dock-tab] beats the UA
 * sheet's [hidden] rule — so the class FlowDock puts on the drawer does the hiding, and
 * the attribute stays as the thing a screen reader and a test can read.
 */
.flow-dock.no-source > .flow-dock-tabs > button[data-dock-tab="source"] {
    display: none;
}

/* A code glyph, not a word: it is the one control on this strip that is for the person
   who already knows what a DSL is. Lit when it is on, so "why am I looking at Lisp" has
   a visible answer one click away. */
.flow-dock-advanced {
    width: 24px;
    padding: 0;
    opacity: .65;
}

.flow-dock-advanced:hover {
    opacity: 1;
}

.flow-dock-advanced.is-on {
    opacity: 1;
    color: var(--flow-accent-ink);
    background: rgba(var(--flow-accent-rgb), .12);
}

/* "Clear the run log" belongs to the Run pane. The general sibling combinator works
   because the strip's right-hand cluster comes AFTER the tab buttons in the markup. */
.flow-dock #btnClearLog {
    display: none;
}

.flow-dock-tabs > button[data-dock-tab="run"].is-active ~ .flow-dock-strip-right #btnClearLog {
    display: inline-flex;
}

.flow-dock-pane {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.flow-dock-pane[hidden] {
    display: none;
}

.flow-dock.is-collapsed .flow-dock-pane {
    display: none;
}

.flow-dock.is-collapsed .flow-dock-handle {
    cursor: default;
}

/* ── source pane ──────────────────────────────────────────────────────────── */

/* The editor keeps its id and its dark ground (it is code, and a dark editor beside a
   light canvas is the one place in this workbench that earns the contrast), but it
   gains the workbench's rhythm: 13px monospace, 1.5 line height, 16px gutter. */
.flow-dock #lispEditor {
    flex: 1 1 auto;
    width: 100%;
    min-height: 0;
    resize: none;
    border: 0;
    outline: none;
    padding: 12px 16px;
    font-family: 'Cascadia Code', 'Fira Code', 'JetBrains Mono', Consolas, monospace;
    font-size: 13px;
    line-height: 1.5;
    tab-size: 2;
    color: #d7dde5;
    background: #16202b;
    overflow: auto;
}

.flow-dock #lispEditor::placeholder {
    color: #6b7a8b;
}

.flow-dock .parse-error-bar,
.flow-dock .type-warning-bar {
    flex: 0 0 auto;
}

/* ── run pane ─────────────────────────────────────────────────────────────── */

.flow-dock #executionLog {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 10px 12px;
    font-size: 0.8rem;
}

/* ── problems pane ────────────────────────────────────────────────────────── */

.flow-problems {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 8px;
}

.flow-problems-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 28px 12px;
    font-size: 0.8rem;
    color: var(--flow-muted);
}

.flow-problems-empty i {
    font-size: 1rem;
    color: var(--flow-success);
}

/* ── the empty-state door (#2347) ─────────────────────────────────────────────
   A blank canvas has nothing wrong with it, so the tab offers the same two ways
   forward the canvas card does instead of a red row. Centered in the pane, and
   sized so it reads at the drawer's default height without scrolling. */

.flow-problems-door {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: 100%;
    padding: 18px 12px;
    text-align: center;
}

.flow-problems-door-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    margin-bottom: 6px;
    border-radius: 10px;
    background: var(--flow-accent-soft);
    color: var(--flow-accent);
    font-size: 1rem;
}

.flow-problems-door h6 {
    margin: 0;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--flow-text);
}

.flow-problems-door p {
    margin: 0 0 10px;
    max-width: 30rem;
    font-size: 12px;
    color: var(--flow-muted);
}

.flow-problems-door-actions {
    display: flex;
    justify-content: center;
    gap: 8px;
    flex-wrap: wrap;
}

.flow-problems-door-actions .flow-empty-btn { height: 30px; font-size: 12px; }

.flow-problem {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 7px 10px;
    text-align: left;
    font-size: 0.79rem;
    color: var(--flow-text);
    background: transparent;
    border: 0;
    border-radius: var(--flow-radius-sm);
    transition: background var(--flow-ease);
}

.flow-problem:hover {
    background: rgba(var(--flow-accent-rgb), .06);
}

.flow-problem i {
    flex: 0 0 auto;
    font-size: 0.85rem;
}

.flow-problem.is-error i { color: var(--flow-error); }
.flow-problem.is-warning i { color: var(--flow-warning); }
.flow-problem.is-info i { color: var(--flow-accent); }

.flow-problem-msg {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.flow-problem-where {
    flex: 0 0 auto;
    padding: 1px 7px;
    font-family: var(--bs-font-monospace, monospace);
    font-size: 0.68rem;
    color: var(--flow-muted);
    background: var(--flow-surface-sunken);
    border: 1px solid var(--flow-line-soft);
    border-radius: 999px;
}

/* ═══════════════════════════════════════════ */
/* NARROW WINDOWS                              */
/* ═══════════════════════════════════════════ */

/*
 * Below 1200px the inspector column would leave the canvas under 400px, so it goes
 * over the canvas as a sheet rather than beside it. Below 992px the console layout
 * itself stacks (#876 keeps that branch intact) and the workbench becomes one column
 * with the palette on top.
 */
@media (max-width: 1199.98px) {
    /* The width var itself is zeroed in the CHROME block at the end of the file — it has
       to be re-declared AFTER the rule that reads `--flow-inspector-w-set`, or a stored
       drag width beats this and the column stays 360px behind an overlay. */
    .flow-workbench.has-inspector .flow-inspector {
        position: absolute;
        top: 0;
        left: 0;
        bottom: var(--flow-dock-h);
        width: 340px;
        z-index: 6;
        box-shadow: var(--flow-shadow-pop);
    }

    .flow-workbench {
        position: relative;
    }
}

@media (max-width: 991.98px) {
    .flow-workbench {
        grid-template-columns: minmax(0, 1fr);
        grid-template-rows: auto minmax(18rem, 1fr) var(--flow-dock-h);
        grid-template-areas:
            "palette"
            "canvas"
            "dock";
        height: auto;
    }

    /* One column: there is no "left of the flow" to put the configuration in, so the
       inspector stays the overlay sheet the rule above makes it and the idle pane —
       which is only ever a signpost — gets out of the way entirely. */
    .flow-configpane {
        display: none;
    }

    .flow-palette {
        max-height: 15rem;
        border-right: 0;
        border-bottom: 1px solid var(--flow-line);
    }

    .orchestrator-topbar.flow-commandbar {
        flex-wrap: wrap;
        min-height: 0;
    }

    .flow-cb-right {
        margin-left: 0;
        overflow-x: auto;
    }
}

/* ═══════════════════════════════════════════ */
/* MOTION                                      */
/* ═══════════════════════════════════════════ */

@media (prefers-reduced-motion: reduce) {
    .flow-workbench,
    .flow-workbench * {
        transition: none !important;
        animation: none !important;
    }
}

/* ═══════════════════════════════════════════════════════════════════════════ */
/* ═══ CANVAS (CORE) ═══                                                       */
/* ═══════════════════════════════════════════════════════════════════════════ */

/* #2266 appends the flow canvas here: .flow-viewport, .flow-world, .flow-edges,
   .flow-nodes, the card, the edges, the parallel group, the empty state and the
   zoom cluster. Left empty on purpose so the two lanes do not merge into the
   middle of each other's rules. */

:root {
    /* The catalog ships Bootstrap's semantic names as `badgeColor`. Three of the eight
       are unreadable as a glyph on a pale tile at their stock values (warning #ffc107,
       info #0dcaf0, light #f8f9fa), so those are darkened to the same family rather
       than left to render as a smudge. The other five are Bootstrap 5.1's own. */
    --flow-tone-primary: #0d6efd;
    --flow-tone-secondary: #5b6470;
    --flow-tone-success: #198754;
    --flow-tone-danger: #dc3545;
    --flow-tone-warning: #b26a00;
    --flow-tone-info: #0a7c8f;
    --flow-tone-dark: #212529;
    --flow-tone-light: #6c757d;
}

/*
 * The panel is the fixed backdrop: the #2259 watermark lives on it as a ::before (in
 * orchestrator.css, where it has always been) and the drawing moves over it. The
 * viewport is the clipping window; the world is the ONE element that carries pan and
 * zoom, so a drag is a compositor transform and nothing re-lays-out.
 */
.flow-viewport {
    position: absolute;
    inset: 0;
    z-index: 1;
    overflow: hidden;
    outline: none;
    cursor: default;
    /* The grid Jamie kept ("I like the way this background is"). Dots rather than
       lines: a line grid competes with the edges, which are also thin gray lines. */
    background-image: radial-gradient(var(--flow-grid) 1px, transparent 1px);
    background-size: 20px 20px;
    background-position: center;
}

.flow-viewport.is-panning { cursor: grabbing; }

/*
 * ONE transform for the whole drawing. Pan and zoom write `transform` on this element
 * and nothing else moves, so a drag is a compositor job — no layout, no reflow, no
 * per-node style writes. Its origin is the spine's x = 0 line; FlowCanvas.fit() is what
 * puts that origin somewhere sensible, so there is no CSS offset to keep in step with it.
 */
.flow-world {
    position: absolute;
    left: 0;
    top: 0;
    transform-origin: 0 0;
    will-change: transform;
}

/* Positioned in JS from the layout bounds — the SVG's viewBox is in LAYOUT coordinates,
   so the element has to sit exactly where that box starts or every edge is offset. */
.flow-edges {
    position: absolute;
    overflow: visible;
    pointer-events: none;
}

.flow-nodes {
    position: absolute;
    left: 0;
    top: 0;
}

.flow-nodes > * { position: absolute; }

/* ── edges ───────────────────────────────────────────────────────────────────── */

.flow-edge-line {
    fill: none;
    stroke: var(--flow-edge);
    stroke-width: var(--flow-edge-w);
    stroke-linecap: round;
    stroke-linejoin: round;
    transition: stroke var(--flow-ease);
}

.flow-edge-arrow {
    fill: var(--flow-edge);
    transition: fill var(--flow-ease);
}

/* ── the "+" on an edge ──────────────────────────────────────────────────────── */

.flow-plus {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border: 1px solid var(--flow-card-border);
    border-radius: 50%;
    background: #fff;
    color: var(--flow-muted);
    font-size: 12px;
    line-height: 1;
    opacity: .6;
    box-shadow: 0 1px 2px rgba(16, 24, 40, .08);
    transition: opacity var(--flow-ease), transform var(--flow-ease),
                background var(--flow-ease), color var(--flow-ease), border-color var(--flow-ease);
    z-index: 3;
}

.flow-plus:hover,
.flow-plus:focus-visible {
    opacity: 1;
    transform: scale(1.08);
    background: var(--flow-accent);
    border-color: var(--flow-accent);
    color: #fff;
    outline: none;
}

/* Something is being dragged: stop making the customer hunt for the target. */
.flow-panel.is-dragging .flow-plus { opacity: 1; }

/* #2365 — the "+" above a form-minted workflow's destination says what it does. On a
   two-node spine a 22px glyph is not where the eye goes, and "maybe we want other
   actions before that" is someone looking straight past it. */
.flow-plus.is-labeled {
    gap: 6px;
    padding: 0 12px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .01em;
    color: var(--flow-muted);
    opacity: .9;
}

.flow-plus.is-labeled .flow-plus-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.flow-plus.is-labeled:hover,
.flow-plus.is-labeled:focus-visible,
.flow-plus.is-labeled.is-dropzone { transform: none; opacity: 1; }

.flow-plus.is-dropzone {
    opacity: 1;
    transform: scale(1.22);
    background: var(--flow-accent);
    border-color: var(--flow-accent);
    color: #fff;
    box-shadow: 0 0 0 4px rgba(var(--flow-accent-rgb), .18);
}

/* ── the card ────────────────────────────────────────────────────────────────── */

.flow-card {
    display: flex;
    align-items: center;
    gap: 12px;
    box-sizing: border-box;
    padding: 10px 12px;
    background: var(--flow-card-bg);
    border: 1px solid var(--flow-card-border);
    border-radius: var(--flow-card-radius);
    box-shadow: var(--flow-card-shadow);
    color: var(--flow-text);
    cursor: pointer;
    user-select: none;
    outline: none;
    transition: box-shadow var(--flow-ease), border-color var(--flow-ease),
                transform var(--flow-ease);
    z-index: 2;
}

/* Only a card that was NOT on the canvas a moment ago. render() runs on every keystroke
   in the Source pane, and an unconditional enter animation made the whole spine flash
   while someone typed — and photographed mid-fade in a headless screenshot, which is how
   it was caught. `forwards` rather than `both` so a card that is somehow never animated
   is still fully opaque rather than invisible. */
.flow-card.is-entering {
    animation: flow-card-in 220ms cubic-bezier(.2, .8, .2, 1) forwards;
}

@keyframes flow-card-in {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}

.flow-card:hover {
    box-shadow: var(--flow-card-shadow-hover);
    border-color: #c3ccd9;
}

.flow-card.is-selected,
.flow-card:focus-visible {
    border-color: var(--flow-accent);
    box-shadow: 0 0 0 2px var(--flow-accent), 0 0 0 6px rgba(var(--flow-accent-rgb), .18),
                var(--flow-card-shadow-hover);
}

.flow-card.is-dropzone {
    border-color: var(--flow-accent);
    box-shadow: 0 0 0 2px rgba(var(--flow-accent-rgb), .55),
                0 0 0 8px rgba(var(--flow-accent-rgb), .12);
}

/* #1785 — a destination nobody has chosen yet is drawn as an outline, because it is
   the one thing left to do rather than a step that is finished. */
.flow-card-placeholder {
    border-style: dashed;
    border-color: #b9c2cf;
    background: repeating-linear-gradient(135deg, #fff 0 8px, #fbfcfe 8px 16px);
}

.flow-card-icon {
    flex: 0 0 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 10px;
    font-size: 18px;
    background: rgba(91, 100, 112, .14);
    color: var(--flow-tone-secondary);
}

.flow-card[data-tone="primary"]   .flow-card-icon { background: rgba(13, 110, 253, .14);  color: var(--flow-tone-primary); }
.flow-card[data-tone="secondary"] .flow-card-icon { background: rgba(91, 100, 112, .14);  color: var(--flow-tone-secondary); }
.flow-card[data-tone="success"]   .flow-card-icon { background: rgba(25, 135, 84, .14);   color: var(--flow-tone-success); }
.flow-card[data-tone="danger"]    .flow-card-icon { background: rgba(220, 53, 69, .14);   color: var(--flow-tone-danger); }
.flow-card[data-tone="warning"]   .flow-card-icon { background: rgba(178, 106, 0, .14);   color: var(--flow-tone-warning); }
.flow-card[data-tone="info"]      .flow-card-icon { background: rgba(10, 124, 143, .14);  color: var(--flow-tone-info); }
.flow-card[data-tone="dark"]      .flow-card-icon { background: rgba(33, 37, 41, .12);    color: var(--flow-tone-dark); }
.flow-card[data-tone="light"]     .flow-card-icon { background: rgba(108, 117, 125, .14); color: var(--flow-tone-light); }

.flow-card-body {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.flow-card-title {
    font-size: 14px;
    font-weight: 600;
    line-height: 1.25;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* One line, ellipsized ONCE. Several separately-shrinking spans here produced three
   truncated fragments where one whole phrase would have fitted. */
.flow-card-sub {
    display: block;
    min-width: 0;
    font-size: 12.5px;
    line-height: 1.3;
    color: var(--flow-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.flow-card-status {
    font-size: 12px;
    line-height: 1.3;
    color: var(--flow-error);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The cost, in the right-hand cluster: the same shape in the same place on every card,
   so it reads as a column the eye can scan rather than a phrase it has to parse. */
.flow-chip {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border: 1px solid #e4e7ec;
    border-radius: 999px;
    background: #f9fafb;
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    color: var(--flow-muted);
    white-space: nowrap;
}

.flow-card-flags {
    position: absolute;
    left: 12px;
    bottom: -9px;
    display: flex;
    gap: 4px;
}

.flow-flag {
    padding: 0 6px;
    border: 1px solid #e4e7ec;
    border-radius: 999px;
    background: #fff;
    font-size: 10.5px;
    line-height: 16px;
    color: var(--flow-muted);
    white-space: nowrap;
    box-shadow: 0 1px 2px rgba(16, 24, 40, .06);
}

.flow-card-right {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 6px;
}

.flow-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 999px;
    background: var(--flow-warning);
    color: #fff;
    font-size: 11px;
    font-weight: 600;
    line-height: 1;
}

.flow-card.has-error .flow-badge { background: var(--flow-error); }
.flow-card.has-warning { border-color: #f0c48a; }
.flow-card.has-error { border-color: #f4b0aa; }

/* ── status ring ─────────────────────────────────────────────────────────────── */

.flow-status-ring {
    position: relative;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    border: 2px solid transparent;
    transition: border-color var(--flow-ease), background var(--flow-ease);
}

.flow-card.is-pending .flow-status-ring { border-color: #e4e7ec; }

.flow-card.is-running .flow-status-ring {
    border-color: rgba(var(--flow-accent-rgb), .2);
    border-top-color: var(--flow-accent);
    animation: flow-spin 900ms linear infinite;
}

@keyframes flow-spin { to { transform: rotate(360deg); } }

.flow-card.is-success .flow-status-ring {
    border-color: var(--flow-success);
    background: var(--flow-success);
}

.flow-card.is-success .flow-status-ring::after,
.flow-card.is-error .flow-status-ring::after {
    content: "";
    position: absolute;
    left: 4px;
    top: 1px;
    width: 4px;
    height: 8px;
    border: solid #fff;
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
}

.flow-card.is-error .flow-status-ring {
    border-color: var(--flow-error);
    background: var(--flow-error);
}

.flow-card.is-error .flow-status-ring::after {
    left: 6px;
    top: 3px;
    width: 2px;
    height: 8px;
    border-width: 0 2px 0 0;
    transform: none;
}

.flow-card.is-running { border-color: rgba(var(--flow-accent-rgb), .45); }
.flow-card.is-success { border-color: rgba(18, 183, 106, .5); }
.flow-card.is-error { border-color: rgba(240, 68, 56, .55); }
.flow-card.is-skipped { opacity: .5; }

/* ── intake / end terminators ────────────────────────────────────────────────── */

.flow-intake,
.flow-end {
    display: flex;
    align-items: center;
    gap: 10px;
    box-sizing: border-box;
    padding: 8px 16px;
    border-radius: 999px;
    font-size: 13px;
    cursor: pointer;
    user-select: none;
    outline: none;
    transition: box-shadow var(--flow-ease), transform var(--flow-ease);
    z-index: 2;
}

.flow-intake {
    background: #fff;
    border: 1px solid var(--flow-card-border);
    box-shadow: var(--flow-card-shadow);
    color: var(--flow-text);
}

.flow-intake > .bi { color: var(--flow-accent); font-size: 16px; }

.flow-end {
    background: var(--flow-text);
    border: 1px solid var(--flow-text);
    box-shadow: 0 2px 6px rgba(16, 24, 40, .18);
    color: #fff;
}

.flow-end > .bi { color: #fff; opacity: .8; font-size: 13px; }

.flow-intake-body,
.flow-end-body {
    display: flex;
    align-items: baseline;
    gap: 6px;
    min-width: 0;
}

.flow-intake-title,
.flow-end-title { font-weight: 600; }

.flow-intake-sub { color: var(--flow-muted); }
.flow-end-sub { color: rgba(255, 255, 255, .72); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.flow-intake.is-selected,
.flow-intake:focus-visible {
    box-shadow: 0 0 0 2px var(--flow-accent), 0 0 0 6px rgba(var(--flow-accent-rgb), .18);
}

.flow-end.is-selected,
.flow-end:focus-visible {
    box-shadow: 0 0 0 2px var(--flow-accent), 0 0 0 6px rgba(var(--flow-accent-rgb), .22);
}

.flow-intake.is-success { border-color: var(--flow-success); }
.flow-end.is-success { background: #0f2a1c; border-color: var(--flow-success); }

/* ── parallel: split, group, join ────────────────────────────────────────────── */

.flow-marker {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    box-sizing: border-box;
    border-radius: 999px;
    border: 1px solid var(--flow-card-border);
    background: #fff;
    box-shadow: var(--flow-card-shadow);
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: .02em;
    text-transform: uppercase;
    color: var(--flow-muted);
    cursor: pointer;
    user-select: none;
    z-index: 2;
    transition: box-shadow var(--flow-ease), border-color var(--flow-ease), color var(--flow-ease);
}

.flow-marker .bi { font-size: 12px; }
.flow-marker:hover { border-color: #c3ccd9; color: var(--flow-text); }

.flow-marker.is-selected {
    border-color: var(--flow-accent);
    color: var(--flow-accent);
    box-shadow: 0 0 0 2px var(--flow-accent), 0 0 0 6px rgba(var(--flow-accent-rgb), .18);
}

/* #2340 — the JOIN is a place, not a control.
   A pill the size of the split with the word "Continue" on it read as a button that did
   nothing ("what continue bubble doing"). What it actually marks is where the branches
   come back together, so it is drawn the way Power Automate draws that: a small merge
   node on the spine, no label, the sentence in the tooltip. It still selects — the
   fan-out, which is the object it is the bottom of. */
.flow-marker-join,
.flow-marker-loopend {
    padding: 0;
    gap: 0;
    box-shadow: 0 1px 2px rgba(16, 24, 40, .08);
}

/* The loop's tail keeps the loop tint (set further down with the rest of the loop
   family); only the fan-out's join is neutral. */
.flow-marker-join {
    border-color: #cfd6e0;
    background: #fff;
    color: #98a2b3;
}

.flow-marker-join .bi,
.flow-marker-loopend .bi {
    font-size: 13px;
    line-height: 1;
}

.flow-marker-join:hover,
.flow-marker-loopend:hover,
.flow-marker-join:focus-visible,
.flow-marker-loopend:focus-visible {
    border-color: var(--flow-accent);
    color: var(--flow-accent);
}

.flow-group {
    box-sizing: border-box;
    border: 1px dashed #ccd4df;
    border-radius: 16px;
    /* A tinted fill, not just a dashed outline: the box has to read as a CONTAINER at a
       glance, which an outline alone does not do over a dotted grid. */
    background: rgba(var(--flow-accent-rgb), .03);
    z-index: 1;
}

.flow-group-label {
    position: absolute;
    left: 14px;
    top: 5px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .03em;
    text-transform: uppercase;
    color: #8a94a6;
    white-space: nowrap;
}

/* ── for-each: the loop head, its group and its tail (#2254/#2284) ───────────────
   A loop and a fan-out are the same geometry — head marker, container box, tail marker
   — and they are drawn as SIBLINGS on purpose: the customer already knows what the box
   means, and only the semantics differ. What separates them is hue (the loop is the
   warm one, so a batch reads as a batch across a whole canvas at 40% zoom), a solid
   rather than dashed border, and a head marker that carries a SENTENCE instead of a
   one-word label. It is also the only marker on the canvas that is not uppercase: it is
   prose — "For each row in $input.rows" — and small caps make prose unreadable. */

:root {
    /* Amber, deliberately not the accent: the accent is selection, and a container that
       looked selected at rest would make the selection ring mean nothing. */
    --flow-loop: #b45309;
    --flow-loop-rgb: 180, 83, 9;
}

.flow-group-loop {
    border-style: solid;
    border-color: rgba(var(--flow-loop-rgb), .28);
    background: rgba(var(--flow-loop-rgb), .045);
}

.flow-group-loop .flow-group-label { color: var(--flow-loop); }

.flow-marker-loop,
.flow-marker-loopend {
    border-color: rgba(var(--flow-loop-rgb), .35);
    color: var(--flow-loop);
    background: #fffaf5;
}

.flow-marker-loop {
    /* Prose, not a label. Left-aligned with a little inset so a long collection
       expression truncates at the right rather than from both ends. */
    justify-content: flex-start;
    padding: 0 12px;
    text-transform: none;
    letter-spacing: 0;
    font-size: 12.5px;
    cursor: grab;
}

.flow-marker-loop:active { cursor: grabbing; }

.flow-marker-loop > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.flow-marker-loop:hover,
.flow-marker-loopend:hover {
    border-color: rgba(var(--flow-loop-rgb), .6);
    color: var(--flow-loop);
}

/* Selection still wins, and it is still the accent — a second selected color would be
   two things meaning "this one". */
.flow-marker-loop.is-selected,
.flow-marker-loopend.is-selected {
    border-color: var(--flow-accent);
    color: var(--flow-accent);
}

/* ── ghosts: "+ Branch", and the empty pipeline's first step ─────────────────── */

.flow-ghost,
.flow-first {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    box-sizing: border-box;
    border: 1px dashed #c3ccd9;
    border-radius: var(--flow-card-radius);
    background: rgba(255, 255, 255, .6);
    color: var(--flow-muted);
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    outline: none;
    z-index: 2;
    transition: border-color var(--flow-ease), color var(--flow-ease),
                background var(--flow-ease), box-shadow var(--flow-ease);
}

.flow-ghost:hover,
.flow-first:hover,
.flow-ghost:focus-visible,
.flow-first:focus-visible,
.flow-ghost.is-dropzone,
.flow-first.is-dropzone,
.flow-ghost.is-pick-target,
.flow-first.is-pick-target {
    border-color: var(--flow-accent);
    border-style: solid;
    color: var(--flow-accent);
    background: #fff;
    box-shadow: 0 0 0 4px rgba(var(--flow-accent-rgb), .12);
}

/* #2338 — an armed "+".
   Pick mode used to be a line of text in a column the eye was not on, so the gesture
   read as "nothing happened". The affordance that was clicked now holds a pulsing
   accent ring until an action is picked or Escape is pressed: the canvas says WHERE,
   the palette says WHAT. */
.flow-plus.is-pick-target {
    opacity: 1;
    background: var(--flow-accent);
    border-color: var(--flow-accent);
    color: #fff;
    box-shadow: 0 0 0 4px rgba(var(--flow-accent-rgb), .22);
    animation: flow-pick-pulse 1.6s ease-in-out infinite;
}

.flow-ghost.is-pick-target,
.flow-first.is-pick-target {
    animation: flow-pick-pulse 1.6s ease-in-out infinite;
}

@keyframes flow-pick-pulse {
    0%, 100% { box-shadow: 0 0 0 4px rgba(var(--flow-accent-rgb), .22); }
    50% { box-shadow: 0 0 0 8px rgba(var(--flow-accent-rgb), .10); }
}

@media (prefers-reduced-motion: reduce) {
    .flow-plus.is-pick-target,
    .flow-ghost.is-pick-target,
    .flow-first.is-pick-target { animation: none; }
}

/* ── zoom cluster ────────────────────────────────────────────────────────────── */

.flow-canvas-controls {
    position: absolute;
    right: 14px;
    bottom: 14px;
    z-index: 4;
}

.flow-zoom {
    display: flex;
    align-items: center;
    gap: 2px;
    padding: 3px;
    border: 1px solid var(--flow-card-border);
    border-radius: 999px;
    background: #fff;
    box-shadow: var(--flow-card-shadow);
}

.flow-zoom-btn,
.flow-zoom-value {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 26px;
    padding: 0 6px;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--flow-muted);
    font-size: 12px;
    line-height: 1;
    transition: background var(--flow-ease), color var(--flow-ease);
}

.flow-zoom-btn { width: 26px; }
.flow-zoom-value { min-width: 44px; font-variant-numeric: tabular-nums; }

.flow-zoom-btn:hover,
.flow-zoom-value:hover {
    background: #f2f4f7;
    color: var(--flow-text);
}

/* ── the empty state, over the watermark rather than instead of it (#2271) ───── */

.flow-empty {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    z-index: 2;
    width: 22rem;
    max-width: calc(100% - 32px);
    padding: 22px 24px 18px;
    border: 1px solid var(--flow-card-border);
    border-radius: 16px;
    /* OPAQUE, not a 94% wash. An empty pipeline still draws both terminators and the
       spine is centered, so this card lands on top of the result terminator every time;
       at 94% the word "Result" showed through the middle of it, which reads as a
       layering accident rather than as a card. The watermark is behind the whole panel
       and still frames it. */
    background: var(--flow-surface);
    box-shadow: var(--flow-shadow-pop);
    text-align: center;
}

.flow-empty[hidden] { display: none; }

.flow-empty-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    margin-bottom: 10px;
    border-radius: 12px;
    background: var(--flow-accent-soft);
    color: var(--flow-accent);
    font-size: 1.25rem;
}

.flow-empty h6 { font-size: 15px; font-weight: 600; color: var(--flow-text); margin-bottom: 4px; }
.flow-empty p { font-size: 13px; color: var(--flow-muted); margin-bottom: 14px; }

.flow-empty-actions {
    display: flex;
    justify-content: center;
    gap: 8px;
    flex-wrap: wrap;
}

/* Its own button rather than .btn-outline-primary: DESIGN §8 says no Bootstrap default
   color or button border shows through anywhere in the workbench, and the empty state
   is the first thing on an empty canvas. */
.flow-empty-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 32px;
    padding: 0 12px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--flow-text);
    background: var(--flow-surface);
    border: 1px solid var(--flow-line);
    border-radius: var(--flow-radius-sm);
    cursor: pointer;
    transition: background var(--flow-ease), border-color var(--flow-ease), box-shadow var(--flow-ease);
}

.flow-empty-btn:hover {
    background: var(--flow-surface-sunken);
    border-color: var(--flow-card-border);
}

.flow-empty-btn.is-primary {
    color: #fff;
    background: var(--flow-accent);
    border-color: var(--flow-accent);
    box-shadow: 0 1px 2px rgba(16, 24, 40, .12);
}

.flow-empty-btn.is-primary:hover { background: var(--flow-accent-ink); border-color: var(--flow-accent-ink); }
.flow-empty-btn[hidden] { display: none; }

.flow-empty-hint {
    margin: 12px 0 0;
    font-size: 11.5px;
    color: var(--flow-muted);
}

.flow-empty .flow-empty-hint { margin-bottom: 0; }

/* ═══════════════════════════════════════════════════════════════════════════ */
/* ═══ GESTURES (#2257 / #2255 — GESTURE lane) ═══                             */
/*                                                                             */
/* The kebab, the card menu, the drop affordances and the canvas toast. Kept in */
/* one block so the merge with the canvas section above is an append.           */
/* ═══════════════════════════════════════════════════════════════════════════ */

/* ── the card's kebab ────────────────────────────────────────────────────────── */

/*
 * #2302 — the CORNER, not the cluster.
 *
 * It used to sit in .flow-card-right, where it held a 22px slot on every card at all
 * times: .flow-card-body was 122px and "Render the document" needs 138, so the sample
 * on the anonymous playground read "Render the docu…". The two ways out of that are
 * both worse — taking the slot only while the button is painted reflows the spine under
 * the pointer, and widening the card costs horizontal room in every branch column — so
 * the button leaves the flow entirely and overlays the card's top-right corner, the way
 * the flags strip overlays its bottom-left.
 *
 * Geometry, on a 280x64 card: 2px down and 5px in clears the 12px corner radius, and its
 * bottom edge lands at 22px, one pixel above the status ring's 23px top. So it never
 * covers the ring during a run and never covers the cost chip.
 *
 * pointer-events go with the paint. An invisible button that still eats clicks is a
 * defect; it was one before, in the flow, and an expanded hit area would have made it
 * a bigger one.
 */
.flow-card-kebab {
    position: absolute;
    top: 2px;
    right: 5px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 20px;
    padding: 0;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: var(--flow-muted);
    font-size: 13px;
    line-height: 1;
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--flow-ease), background var(--flow-ease), color var(--flow-ease);
}

/* A 22x20 glyph deserves a 30x30 target. The footprint stays 22x20. */
.flow-card-kebab::after {
    content: '';
    position: absolute;
    inset: -5px -4px;
}

.flow-card:hover .flow-card-kebab,
.flow-card:focus-within .flow-card-kebab,
.flow-card.is-selected .flow-card-kebab {
    opacity: 1;
    pointer-events: auto;
}

.flow-card-kebab:hover,
.flow-card-kebab:focus-visible {
    opacity: 1;
    background: #eef1f5;
    color: var(--flow-text);
    outline: none;
}

/* ── drag affordances ───────────────────────────────────────────────────────── */

/* The card being carried. Lowered opacity plus a flattened shadow reads as "this one has
   been picked up", so the original never competes with the drop target for attention. */
.flow-card.is-lifting,
.flow-marker.is-lifting {
    opacity: .45;
    box-shadow: none;
}

/* The split marker is the handle for the whole fan-out. */
.flow-marker-split { cursor: grab; }
.flow-marker-split:active { cursor: grabbing; }

/*
 * Dropping ON a card means "insert AFTER it" (§5). The ring alone does not say that — it
 * reads as "replace this" — so the card also grows a solid accent line along its lower
 * edge, which is exactly where the new card will appear.
 */
.flow-card.is-dropzone-after::after {
    content: "";
    position: absolute;
    left: 10px;
    right: 10px;
    bottom: -7px;
    height: 3px;
    border-radius: 999px;
    background: var(--flow-accent);
    box-shadow: 0 0 0 3px rgba(var(--flow-accent-rgb), .18);
}

/* #2369 — the fan-out (or loop) box as a drop target: "we need better drag and drop
   like out of here, to out of our box." The block lights as one object, because that is
   what the card is being dropped INTO — a card or a "+" inside it still wins wherever
   one is under the pointer (resolveDropTarget prefers the smallest container). */
.flow-group.is-dropzone {
    border-color: var(--flow-accent);
    border-style: solid;
    background: rgba(var(--flow-accent-rgb), .09);
    box-shadow: 0 0 0 4px rgba(var(--flow-accent-rgb), .12);
}

.flow-group.is-dropzone > .flow-group-label { color: var(--flow-accent); }

/* A target the AST cannot hold — a fan-out being dragged into a branch. Refusing in
   silence would read as a broken drop; a struck-through zone says "not there". */
[data-drop].is-dropzone-refused {
    border-color: #f4b0aa !important;
    box-shadow: 0 0 0 3px rgba(240, 68, 56, .12);
    cursor: no-drop;
}

/* The drag image the palette hands the browser: the CARD that is about to appear, drawn
   off screen and photographed. Only the ghost needs the offset — .flow-card already
   carries the rest of the look, so this is a position and a size, not a second card. */
.flow-drag-ghost {
    position: fixed;
    left: -1000px;
    top: -1000px;
    width: 280px;
    height: 64px;
    opacity: .9;
    pointer-events: none;
}

.flow-drag-ghost .flow-card-icon {
    background: rgba(var(--item-rgb, 30, 77, 123), .14);
    color: var(--item-color, var(--flow-accent));
}

/*
 * #2342 — the card actually in flight during a POINTER drag.
 *
 * The HTML5 ghost above is a photograph: the browser snapshots it once and parks the
 * element off screen. This one is the real thing, translated to the cursor on every
 * mousemove, so it is the card the customer is carrying and it tilts a degree the way a
 * lifted card does. `transform` is the only property that moves, so it stays on the
 * compositor and never re-lays anything out.
 */
.flow-pointer-ghost {
    position: fixed;
    left: 0;
    top: 0;
    margin: 0;
    z-index: 2147483000;
    opacity: .95;
    box-shadow: 0 12px 28px rgba(16, 24, 40, .22), 0 2px 6px rgba(16, 24, 40, .12);
    rotate: -1.2deg;
    will-change: transform;
}

/* A drag in flight is not a text selection and not a resize. */
body.is-flow-dragging {
    cursor: grabbing;
    user-select: none;
    -webkit-user-select: none;
}

@media (prefers-reduced-motion: reduce) {
    .flow-pointer-ghost { rotate: none; }
}

/* ── the card menu ──────────────────────────────────────────────────────────── */

.flow-menu {
    position: absolute;
    z-index: 8;
    min-width: 246px;
    max-width: 320px;
    padding: 5px;
    border: 1px solid var(--flow-card-border);
    border-radius: 12px;
    background: #fff;
    box-shadow: 0 10px 32px rgba(16, 24, 40, .16), 0 2px 6px rgba(16, 24, 40, .08);
    outline: none;
    animation: flow-menu-in 120ms cubic-bezier(.2, .8, .2, 1);
}

.flow-menu[hidden] { display: none; }

@keyframes flow-menu-in {
    from { opacity: 0; transform: translateY(-4px) scale(.98); }
    to   { opacity: 1; transform: none; }
}

.flow-menu-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 7px 10px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--flow-text);
    font-size: 13px;
    line-height: 1.3;
    text-align: left;
    transition: background var(--flow-ease), color var(--flow-ease);
}

.flow-menu-item > .bi {
    flex: 0 0 16px;
    font-size: 14px;
    color: var(--flow-muted);
}

.flow-menu-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.flow-menu-key {
    flex: 0 0 auto;
    color: #98a2b3;
    font-size: 11px;
    font-variant-numeric: tabular-nums;
}

.flow-menu-check,
.flow-menu-more { flex: 0 0 auto; }
.flow-menu-check { color: var(--flow-accent) !important; }

.flow-menu-item:hover,
.flow-menu-item.is-active,
.flow-menu-item:focus-visible {
    background: rgba(var(--flow-accent-rgb), .09);
    outline: none;
}

.flow-menu-item:hover > .bi,
.flow-menu-item.is-active > .bi { color: var(--flow-accent); }

.flow-menu-item.is-danger:hover,
.flow-menu-item.is-danger.is-active { background: rgba(240, 68, 56, .1); color: var(--flow-error); }
.flow-menu-item.is-danger:hover > .bi,
.flow-menu-item.is-danger.is-active > .bi { color: var(--flow-error); }

/* Grayed out, but still readable and still hoverable — the title carries the reason, and a
   row you cannot point at is a row whose explanation you cannot reach. */
.flow-menu-item.is-disabled,
.flow-menu-item:disabled {
    color: #98a2b3;
    cursor: not-allowed;
}

.flow-menu-item.is-disabled > .bi { color: #c3ccd9; }
.flow-menu-item.is-disabled:hover { background: #f7f8fa; }

.flow-menu-sep {
    height: 1px;
    margin: 5px 8px;
    background: #eaecf0;
}

.flow-menu-head {
    padding: 8px 10px 4px;
    color: var(--flow-muted);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
}

/* ── the canvas toast ───────────────────────────────────────────────────────── */

/*
 * Anchored to the CANVAS, not to the window corner: it announces what the gesture just
 * did — an (output-of …) the product wrote into a socket nobody typed in — and that
 * belongs where the gesture happened.
 */
.flow-toasts {
    position: absolute;
    left: 50%;
    bottom: 18px;
    z-index: 7;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    transform: translateX(-50%);
    pointer-events: none;
}

.flow-toast {
    display: flex;
    align-items: center;
    gap: 8px;
    max-width: 30rem;
    padding: 8px 14px;
    border: 1px solid var(--flow-card-border);
    border-radius: 999px;
    background: #fff;
    color: var(--flow-text);
    font-size: 12.5px;
    line-height: 1.35;
    box-shadow: 0 6px 20px rgba(16, 24, 40, .14);
    animation: flow-toast-in 180ms cubic-bezier(.2, .8, .2, 1);
}

.flow-toast > .bi { font-size: 14px; color: var(--flow-accent); }
.flow-toast-warning > .bi { color: var(--flow-warning); }
.flow-toast-success > .bi { color: var(--flow-success); }

.flow-toast-warning { border-color: #f0c48a; }
.flow-toast-success { border-color: rgba(18, 183, 106, .45); }

.flow-toast-text { min-width: 0; }

/* The parameter name is a token in the source, so it looks like one here. */
.flow-toast-code {
    padding: 1px 6px;
    border-radius: 5px;
    background: rgba(var(--flow-accent-rgb), .1);
    color: var(--flow-accent);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 11.5px;
}

.flow-toast.is-leaving {
    opacity: 0;
    transform: translateY(6px);
    transition: opacity 200ms ease, transform 200ms ease;
}

@keyframes flow-toast-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}

/* ── motion, per §8 ──────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
    .flow-card,
    .flow-plus,
    .flow-intake,
    .flow-end,
    .flow-marker,
    .flow-ghost,
    .flow-first,
    .flow-edge-line,
    .flow-edge-arrow,
    .flow-status-ring,
    .flow-card-kebab,
    .flow-menu,
    .flow-menu-item,
    .flow-toast {
        animation: none !important;
        transition: none !important;
    }
}

/* ═══════════════════════════════════════════════════════════════════════════ */
/* ═══ EXAMPLES GALLERY (TRY, #2271) ═══                                       */
/* ═══════════════════════════════════════════════════════════════════════════ */

/*
 * flow-examples.js builds this overlay itself, so it needs no markup on either host
 * page. Above the drawer and the chat FAB, below Bootstrap's own modals (1055 +
 * backdrop) — the gallery never opens over one of those, and if it ever did, the
 * modal is the thing that must win.
 */
.flow-gallery-scrim {
    position: fixed;
    inset: 0;
    z-index: 1050;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background: rgba(16, 24, 40, .48);
    backdrop-filter: blur(2px);
    animation: flow-gallery-in 140ms cubic-bezier(.2, .8, .2, 1);
}

@keyframes flow-gallery-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

.flow-gallery {
    display: flex;
    flex-direction: column;
    width: min(920px, 100%);
    max-height: min(82vh, 720px);
    background: var(--flow-surface);
    border-radius: 16px;
    box-shadow: var(--flow-shadow-pop);
    overflow: hidden;
}

.flow-gallery-head {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 18px 20px 14px;
    border-bottom: 1px solid var(--flow-line);
}

.flow-gallery-heading { flex: 1 1 auto; min-width: 0; }
.flow-gallery-head h5 {
    margin: 0 0 2px;
    font-size: 16px;
    font-weight: 600;
    color: var(--flow-text);
}
.flow-gallery-head p {
    margin: 0;
    font-size: 12.5px;
    color: var(--flow-muted);
}

.flow-gallery-close {
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--flow-muted);
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--flow-radius-sm);
    cursor: pointer;
    transition: background var(--flow-ease), color var(--flow-ease);
}

.flow-gallery-close:hover { background: var(--flow-surface-sunken); color: var(--flow-text); }

.flow-gallery-grid {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 12px;
    padding: 16px 20px 20px;
    background: var(--flow-surface-sunken);
}

/* A tile is a <button>: it is one target, it takes Tab and Enter for free, and the
   whole card is clickable rather than a link buried in it. */
.flow-gallery-tile {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    padding: 13px 14px 12px;
    text-align: left;
    background: var(--flow-surface);
    border: 1px solid var(--flow-card-border);
    border-radius: var(--flow-card-radius);
    box-shadow: var(--flow-card-shadow);
    cursor: pointer;
    transition: transform var(--flow-ease), box-shadow var(--flow-ease), border-color var(--flow-ease);
}

.flow-gallery-tile:hover,
.flow-gallery-tile:focus-visible {
    transform: translateY(-1px);
    border-color: var(--flow-accent);
    box-shadow: var(--flow-card-shadow-hover);
    outline: none;
}

.flow-gallery-tile.is-current {
    border-color: var(--flow-accent);
    box-shadow: 0 0 0 3px rgba(var(--flow-accent-rgb), .16), var(--flow-card-shadow);
}

.flow-gallery-name {
    font-size: 13.5px;
    font-weight: 600;
    line-height: 1.3;
    color: var(--flow-text);
}

.flow-gallery-desc {
    font-size: 12px;
    line-height: 1.45;
    color: var(--flow-muted);
}

.flow-gallery-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 2px;
}

.flow-gallery-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    height: 20px;
    padding: 0 7px;
    font-size: 11px;
    font-weight: 600;
    color: var(--flow-muted);
    background: var(--flow-surface-sunken);
    border: 1px solid var(--flow-line);
    border-radius: 999px;
}

.flow-gallery-chip i { font-size: 10px; }

.flow-gallery-chip.is-fanout,
.flow-gallery-chip.is-loop,
.flow-gallery-chip.is-decision,
.flow-gallery-chip.is-current {
    color: var(--flow-accent);
    background: var(--flow-accent-soft);
    border-color: rgba(var(--flow-accent-rgb), .25);
}

.flow-gallery-chip.is-error {
    color: #b42318;
    background: #fef3f2;
    border-color: #fecdca;
}

.flow-gallery-cats {
    display: flex;
    gap: 4px;
    margin-top: 2px;
}

.flow-gallery-cat {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    font-size: 11px;
    color: var(--flow-muted);
    background: var(--flow-surface-sunken);
    border: 1px solid var(--flow-line);
    border-radius: 6px;
}

/* Tinted from the category's own hue (CatalogGroups), so a tile here and the same
   category's tile in the palette and on a card are one color. */
.flow-gallery-cat.is-tinted {
    color: rgb(var(--cat-rgb));
    background: rgba(var(--cat-rgb), .12);
    border-color: rgba(var(--cat-rgb), .22);
}

@media (max-width: 575.98px) {
    .flow-gallery-scrim { padding: 12px; }
    .flow-gallery-grid { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
    .flow-gallery-scrim { animation: none; }
    .flow-gallery-tile { transition: none; }
}


/* ═══════════════════════════════════════════ */
/* INTAKE (#2256) — the head of the spine, and its pane   */
/* ═══════════════════════════════════════════ */

/* The layout reserves a fixed 232px box for the intake and centers it by setting
   `left` to -116. A pill that NAMES what feeds the workflow is wider than that, so it
   sizes to its content and translates back by half the excess: the spine stays
   centered on x=0 and flow-layout.js keeps one number for the box it reserves. The
   width is the only thing overridden, so `place()` still owns position. */
.flow-intake {
    width: auto !important;
    min-width: 232px;
    max-width: 560px;
}

/* THE PILL IS ONE LINE, and it took Chrome to see that it was not.
   `place()` fixes the height at 48px, and .flow-intake-sub had no white-space rule of
   its own (.flow-end-sub does) — so "nothing feeds this workflow yet" wrapped to five
   lines inside a 48px box and the source chips rendered on top of the wrap. The title
   and the subtitle never wrap; the subtitle is the one that gives way, because a chip
   naming a form is more specific than the word "Webhook". */
.flow-intake-body {
    flex: 0 1 auto;
    min-width: 0;
}

.flow-intake-title { white-space: nowrap; }

.flow-intake-sub {
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.flow-nodes > .flow-intake {
    transform: translateX(calc((232px - 100%) / 2));
}

/* Source chips sit after the title/subtitle and are the answer to "and what actually
   posts to it". Long form names ellipsize rather than stretching the pill past the
   width a spine can carry. */
.flow-intake .flow-chip {
    display: inline-block;
    max-width: 160px;
    overflow: hidden;
    text-overflow: ellipsis;
    line-height: 18px;
    padding: 0 8px;
}

.flow-intake .flow-chip-form {
    border-color: rgba(var(--flow-accent-rgb), .32);
    background: rgba(var(--flow-accent-rgb), .08);
    color: var(--flow-accent);
}

.flow-intake .flow-chip-watcher {
    border-color: rgba(18, 183, 106, .35);
    background: rgba(18, 183, 106, .1);
    color: #087443;
}

/* "Not published" and "Forms unavailable" — the two things a customer must not read as
   a quiet zero. Amber, because neither is an error and both are actionable. */
.flow-intake .flow-chip-warn {
    border-color: rgba(247, 144, 9, .4);
    background: rgba(247, 144, 9, .12);
    color: #b54708;
}

/* The unpublished head is a prompt, not a status line: a dashed edge says "this is not
   finished yet" in the same language the #1785 destination placeholder card uses. */
.flow-intake.is-unbound,
.flow-intake:has(.flow-chip-warn) {
    border-style: dashed;
    border-color: rgba(247, 144, 9, .5);
}

/* ── the pane: intake and result ─────────────────────────────────────── */

.flow-intake-field { margin-bottom: 12px; }

.flow-intake-label {
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--flow-muted);
    margin-bottom: 3px;
}

.flow-intake-value { font-size: 0.82rem; color: var(--flow-text); }

.flow-intake-address {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 8px;
    border: 1px solid var(--flow-line);
    border-radius: 8px;
    background: var(--flow-surface-sunken, #f9fafb);
}

.flow-intake-address code {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 0.72rem;
    word-break: break-all;
    background: transparent;
    padding: 0;
    color: var(--flow-text);
}

.flow-intake-copy {
    flex: 0 0 auto;
    --bs-btn-padding-y: 1px;
    --bs-btn-padding-x: 6px;
    --bs-btn-font-size: 0.72rem;
}

.flow-intake-list {
    list-style: none;
    margin: 0 0 8px;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: 0.8rem;
}

.flow-intake-list li {
    display: flex;
    align-items: baseline;
    gap: 6px;
    min-width: 0;
}

/* The $input chips: the names a step parameter has to be given. Clicking one copies
   it, which is the whole reason they are here — typing them from memory is where the
   mistakes were. */
.flow-shape-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

.flow-shape-chip {
    display: inline-flex;
    align-items: baseline;
    gap: 5px;
    padding: 2px 8px;
    border: 1px solid var(--flow-line);
    border-radius: 999px;
    background: #fff;
    cursor: pointer;
    transition: border-color var(--flow-ease), background var(--flow-ease);
}

.flow-shape-chip:hover {
    border-color: rgba(var(--flow-accent-rgb), .45);
    background: rgba(var(--flow-accent-rgb), .06);
}

.flow-shape-chip.is-copied {
    border-color: var(--flow-success);
    background: rgba(18, 183, 106, .1);
}

.flow-shape-chip code {
    font-size: 0.74rem;
    background: transparent;
    padding: 0;
    color: var(--flow-text);
}

.flow-shape-chip-type { font-size: 0.68rem; color: var(--flow-muted); }

/* ── the intake picker and the result chooser ────────────────────────────── */

.flow-intake-row {
    padding: 12px 0;
    border-bottom: 1px solid var(--flow-line-soft);
}

.flow-intake-row:last-child { border-bottom: 0; }

/* "Coming" rows are readable, not invisible: the customer is being told the shape of
   what is planned, which is the opposite of a control that silently does nothing. */
.flow-intake-row.is-disabled .flow-intake-row-title { color: var(--flow-muted); }

.flow-intake-row-head {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 4px;
}

.flow-intake-row-head > .bi { color: var(--flow-accent); }
.flow-intake-row.is-disabled .flow-intake-row-head > .bi { color: var(--flow-muted); }

.flow-intake-row-title { font-size: 0.88rem; }

.flow-intake-row-head .badge { margin-left: auto; font-weight: 500; }

.flow-intake-row-body { padding-left: 24px; }
.flow-intake-row-body > .btn { margin-top: 6px; }

.flow-result-list { gap: 4px; }

.flow-result-option {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1px;
    width: 100%;
    padding: 8px 10px;
    border: 1px solid var(--flow-line);
    border-radius: 8px;
    background: #fff;
    text-align: left;
    font-size: 0.82rem;
    transition: border-color var(--flow-ease), background var(--flow-ease);
}

.flow-result-option:hover {
    border-color: rgba(var(--flow-accent-rgb), .45);
    background: rgba(var(--flow-accent-rgb), .05);
}

/* #822 — the one that is pinned right now. Selected state on a list of one choice has
   to be unmistakable, or the customer re-picks what is already true. */
.flow-result-option.is-current {
    border-color: var(--flow-accent);
    box-shadow: 0 0 0 3px rgba(var(--flow-accent-rgb), .16);
}

/* ═══════════════════════════════════════════════════════════════════════════ */
/* ═══ THE RUN (#2269) ═══                                                     */
/* ═══════════════════════════════════════════════════════════════════════════ */

/*
 * A run has to be legible from across the room: which card is working, which one
 * finished, which one failed, and which one nobody needed. Everything here is a
 * state on a card, an edge or a log row — nothing is a separate widget, because a
 * separate widget is a second place the truth can be stale.
 */

/* ── the card during a run ───────────────────────────────────────────────────── */

/* The status line REPLACES the subtitle. The card is a fixed height: an extra line
   pushes the cost chip out of the box and breaks the alignment of every card below
   it, which is exactly what a run is the worst moment to do.
   #2420 — the card carries TWO subtitle lines now, so both go. */
.flow-card.has-status .flow-card-sub,
.flow-card.has-status .flow-card-detail { display: none; }

.flow-card.is-error   .flow-card-status { color: var(--flow-error); }
.flow-card.is-warning .flow-card-status { color: var(--flow-warning); }
.flow-card.is-success .flow-card-status,
.flow-card.is-skipped .flow-card-status {
    color: var(--flow-muted);
    font-variant-numeric: tabular-nums;
}

/* #1816/#1840 — failed, and the run carried on. Amber is the whole point: red would
   say the workflow stopped when it did not, and green would say the thing the author
   asked for happened when it did not. */
.flow-card.is-warning { border-color: rgba(247, 144, 9, .55); }

/* An open amber ring with a solid center, deliberately NOT the filled disc that
   success and error wear: this step is finished but did not do what it was asked, and
   the mark should not read at a glance as either of the two that did. */
.flow-card.is-warning .flow-status-ring {
    border-color: var(--flow-warning);
    background: transparent;
}

.flow-card.is-warning .flow-status-ring::after {
    content: "";
    position: absolute;
    left: 5px;
    top: 5px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--flow-warning);
}

.flow-card.is-skipped .flow-status-ring {
    border-color: #e4e7ec;
    background: transparent;
}

/* A running card leads the eye: the accent ring spins (see .flow-status-ring), and the
   card itself carries a soft accent halo so it is findable in a twelve-step spine
   without hunting for an 18px circle. */
.flow-card.is-running {
    box-shadow: var(--flow-card-shadow-hover), 0 0 0 4px rgba(var(--flow-accent-rgb), .14);
}

.flow-card.is-error {
    box-shadow: var(--flow-card-shadow), 0 0 0 4px rgba(240, 68, 56, .12);
}

/* ── the parallel group and the terminators ──────────────────────────────────── */

.flow-group.is-success { border-color: rgba(18, 183, 106, .6); border-style: solid; }
.flow-group.is-warning {
    border-color: rgba(247, 144, 9, .7);
    border-style: solid;
    background: rgba(247, 144, 9, .05);
}
.flow-group.is-error { border-color: rgba(240, 68, 56, .6); border-style: solid; }

.flow-intake.is-running { border-color: var(--flow-accent); }
.flow-intake.is-running .flow-intake-sub { color: var(--flow-accent); }

/* The terminator is what a caller gets back, so the verdict lands on it (#822). */
.flow-end.is-warning { background: #3a2a08; border-color: var(--flow-warning); }
.flow-end.is-error   { background: #3a1512; border-color: var(--flow-error); }
.flow-end.is-pending { opacity: .72; }

/* ── the edges a run has travelled ───────────────────────────────────────────── */

/* Dashed accent INTO the card being worked on, solid accent behind it. Derived from
   the node statuses on every change rather than stored — an edge has no state of its
   own, and a second copy of "is this step running" is a second answer. */
.flow-edge.is-running .flow-edge-line {
    stroke: var(--flow-accent);
    stroke-dasharray: 7 5;
    animation: flow-edge-march 700ms linear infinite;
}

.flow-edge.is-running .flow-edge-arrow { fill: var(--flow-accent); }

.flow-edge.is-done .flow-edge-line  { stroke: var(--flow-success); }
.flow-edge.is-done .flow-edge-arrow { fill: var(--flow-success); }

.flow-edge.is-warned .flow-edge-line  { stroke: var(--flow-warning); }
.flow-edge.is-warned .flow-edge-arrow { fill: var(--flow-warning); }

.flow-edge.is-failed .flow-edge-line  { stroke: var(--flow-error); }
.flow-edge.is-failed .flow-edge-arrow { fill: var(--flow-error); }

@keyframes flow-edge-march { to { stroke-dashoffset: -24; } }

/* ── the Run button ──────────────────────────────────────────────────────────── */

/* The price stays visible while the button is off (#535): a disabled Run that has also
   lost its cost tells the customer nothing about what pressing it would have spent. */
#btnRun:disabled { opacity: .55; cursor: not-allowed; }
#btnRun.is-blocked { box-shadow: inset 0 0 0 1px rgba(240, 68, 56, .45); }
#btnRun.is-running .bi-play-fill { animation: flow-run-pulse 1s ease-in-out infinite; }

@keyframes flow-run-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }

/* One amber dot on Publish when the gate would refuse. Deliberately not a disabled
   button: the server is the gate (#916), and a client-side refusal that disagreed with
   it would be a second, wrong gate. The dot says "there is something to read"; the
   tooltip and the Problems tab say what. Written against the id because
   updatePublishButton rewrites className wholesale (the label IS the state). */
#btnPublish.has-blockers::after {
    content: "";
    display: inline-block;
    width: 6px;
    height: 6px;
    margin-left: 6px;
    border-radius: 50%;
    background: var(--flow-warning);
    vertical-align: middle;
}

/* ── the Run tab's log ───────────────────────────────────────────────────────── */

.flow-dock .exec-step {
    margin-bottom: 4px;
    padding: 0;
    border: 1px solid var(--flow-line-soft, #eceff3);
    border-radius: var(--flow-radius-sm, 8px);
    background: #fff;
    overflow: hidden;
    transition: border-color var(--flow-ease), background var(--flow-ease);
}

.flow-dock .exec-step.pending { opacity: .6; }
.flow-dock .exec-step.running { border-color: rgba(var(--flow-accent-rgb), .55); background: rgba(var(--flow-accent-rgb), .04); }
.flow-dock .exec-step.success { border-color: rgba(18, 183, 106, .35); background: #fff; }
.flow-dock .exec-step.warning { border-color: rgba(247, 144, 9, .5); background: rgba(247, 144, 9, .05); }
.flow-dock .exec-step.error   { border-color: rgba(240, 68, 56, .5); background: rgba(240, 68, 56, .04); }
.flow-dock .exec-step.skipped { opacity: .55; }

.flow-run-head {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    padding: 7px 10px;
    border: 0;
    background: transparent;
    text-align: left;
    font-size: 0.79rem;
    color: var(--flow-text);
}

button.flow-run-head:hover { background: rgba(var(--flow-accent-rgb), .05); }

.flow-run-head .status-icon {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    font-size: 0.9rem;
    color: var(--flow-muted);
}

.flow-run-label {
    flex: 0 1 auto;
    min-width: 0;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The endpoint is context, not the headline — it takes the space that is left. */
.flow-run-ep {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 0.72rem;
    color: var(--flow-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.flow-run-time {
    flex: 0 0 auto;
    font-size: 0.72rem;
    font-variant-numeric: tabular-nums;
    color: var(--flow-muted);
}

.flow-run-caret {
    flex: 0 0 auto;
    font-size: 0.7rem;
    color: var(--flow-muted);
    transition: transform var(--flow-ease);
}

.flow-run-row.is-open .flow-run-caret { transform: rotate(90deg); }

/* A branch is indented under its block, so a fan-out reads as one thing that happened
   four ways rather than five unrelated rows. */
.flow-run-row.is-branch { margin-left: 18px; }
.flow-run-row.is-handler .flow-run-label { font-style: italic; }
.flow-run-row.is-block .flow-run-label { text-transform: none; }

.flow-dock .exec-step .step-detail {
    margin: 0;
    padding: 6px 10px 8px 35px;
    border-top: 1px solid var(--flow-line-soft, #eceff3);
    max-height: 220px;
}

.flow-run-summary {
    margin-top: 8px !important;
    border-width: 1px !important;
}

.flow-dock .flow-run-summary .flow-run-head { font-size: 0.82rem; }
.flow-run-summary.is-success .status-icon { color: var(--flow-success); }
.flow-run-summary.is-warning .status-icon { color: var(--flow-warning); }
.flow-run-summary.is-error   .status-icon { color: var(--flow-error); }
.flow-run-summary.is-success { border-color: rgba(18, 183, 106, .45); }
.flow-run-summary.is-warning { border-color: rgba(247, 144, 9, .55); background: rgba(247, 144, 9, .05); }
.flow-run-summary.is-error   { border-color: rgba(240, 68, 56, .55); background: rgba(240, 68, 56, .04); }

.flow-run-note {
    padding: 0 10px 8px 35px;
    font-size: 0.74rem;
    color: var(--flow-muted);
}

/* ── motion ──────────────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
    .flow-edge.is-running .flow-edge-line {
        animation: none !important;
        stroke-dasharray: 7 5;   /* the dash still SAYS "in flight"; it just holds still */
    }
    #btnRun.is-running .bi-play-fill { animation: none !important; }
}

/* ══ #2334 — THE TOAST'S SLOT IN THE BOTTOM-RIGHT STACK ══════════════════════════
   The run's toast used to be created with an inline `bottom:20px;right:20px`, which put
   it UNDER the AI hint bubble and OVER the cookie bar's 'Got it' button — the judge's
   screenshot of 'Loaded example: …' has both. It is slot three now: above the FAB, and
   above the bubble as well while the bubble is up (ai-chat.js puts .has-ai-callout on
   <html> for exactly as long as that is true). The inset itself is measured off the zoom
   pill, the dock tab strip and the cookie notice by orchestrator.js. */
/* #2345 — the SLOT belongs to the stack, not to one toast. Every toast was a bare
   .orch-toast appended to <body>, so two fired seconds apart were drawn at the same
   coordinates and the older one showed through the newer. The container holds the fixed
   position now and the toasts flow inside it, newest nearest the corner, three at most
   (orchestrator.js ORCH_TOAST_MAX — the number FlowCanvas's own stack uses). */
.orch-toasts {
    position: fixed;
    /* Right-aligned with the FAB and the bubble: three things in one column read as a
       stack; three different right edges read as three things that landed on each other. */
    right: var(--orch-overlay-right, 20px);
    bottom: calc(var(--orch-overlay-bottom, 20px) + 60px);
    z-index: 10000;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 8px;
    /* It sits over the whole page, including the Run button and "Got it". Nothing in it
       is clickable, so nothing in it may swallow a click. */
    pointer-events: none;
    transition: bottom var(--flow-ease, 160ms cubic-bezier(.2, .8, .2, 1));
}

:root.has-ai-callout .orch-toasts {
    /* the FAB (52) + the bubble (93 at its tallest) + two 8px gaps */
    bottom: calc(var(--orch-overlay-bottom, 20px) + 161px);
}

/* Deliberately the canvas toast's shape and weight (.flow-toast), a size up: two toast
   systems on one page that look nothing like each other read as two products. The old
   look was an inline cssText carrying Bootstrap's success green, danger red and warning
   amber as literal hex — the last Bootstrap default colors on this surface (DESIGN §8). */
.orch-toast {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    max-width: 26rem;
    padding: 9px 14px;
    border: 1px solid var(--flow-card-border);
    border-radius: 12px;
    background: var(--flow-surface);
    color: var(--flow-text);
    font-size: 12.5px;
    line-height: 1.42;
    box-shadow: 0 6px 20px rgba(16, 24, 40, .14);
    animation: flow-toast-in 180ms cubic-bezier(.2, .8, .2, 1);
}

.orch-toast > .bi {
    flex: 0 0 auto;
    margin-top: 1px;
    font-size: 14px;
    color: var(--flow-accent);
}

/* `pre-line`: warnAboutStepAttributes joins its findings with newlines, and a warning
   that arrives as one run-on sentence is a warning nobody finishes reading. */
.orch-toast-text {
    min-width: 0;
    white-space: pre-line;
}

.orch-toast.is-success > .bi { color: var(--flow-success); }
.orch-toast.is-error   > .bi { color: var(--flow-error); }
.orch-toast.is-warning > .bi { color: var(--flow-warning); }
.orch-toast.is-error   { border-color: rgba(240, 68, 56, .45); }
.orch-toast.is-warning { border-color: #f0c48a; }

.orch-toast.is-leaving {
    opacity: 0;
    transform: translateY(6px);
    transition: opacity 240ms ease, transform 240ms ease;
}

@media (prefers-reduced-motion: reduce) {
    .orch-toasts { transition: none; }
    .orch-toast { animation: none; }
    .orch-toast.is-leaving { transition: none; }
}

/* ══ #2371 / #2361 — THE SCHEDULE AND THE STUCK SUBMISSIONS ══════════════════════
   Appended as one block at the end of the file on purpose: three lanes were editing
   this stylesheet in the same wave, and a trailing block merges where an insertion
   into the intake-pane section three thousand lines up would not.

   Everything here lives inside the intake picker or the intake pane, both of which
   are already 360px-or-narrower columns, so the rule for all of it is the same one
   the rest of the pane follows: stack, do not stretch, and never let a long cadence
   or a machine's error message push a button off the edge. */

/* #2371 — a schedule chip carries a whole sentence ("every day at 06:00 · next in
   4 h"), so it gets more of the pill than a form name does; the ellipsis still
   catches the ones that do not fit. Indigo rather than the accent, so a glance can
   tell a clock from a form without reading either. */
.flow-intake .flow-chip-schedule {
    max-width: 190px;
    border-color: rgba(105, 65, 198, .32);
    background: rgba(105, 65, 198, .09);
    color: #5925dc;
}

/* #2359 — a query intake. Teal, and the widest of the chips, because "accounts (12
   columns)" is the fact and truncating it to "accounts (12 c…" would lose the half
   that says how much of the table is coming. */
.flow-intake .flow-chip-query {
    max-width: 190px;
    border-color: rgba(6, 148, 162, .35);
    background: rgba(6, 148, 162, .1);
    color: #0e7090;
}

/* The "+2 more" chip and #2360's workflow-name chip: present, and deliberately quiet.
   Neither is a source; both are context. */
.flow-intake .flow-chip-muted {
    border-color: var(--flow-card-border, #d9dee7);
    background: rgba(16, 24, 40, .04);
    color: var(--flow-muted, #667085);
}

/* One schedule in the list: the cadence, the last run under it, then the buttons on
   their own line. A row that tried to keep "every 15 minutes · next in 12 min" and
   three buttons on one line wrapped mid-button at 360px. */
.flow-intake-schedules > li,
.flow-intake-submissions > li {
    display: block;
    padding: 8px;
    border: 1px solid var(--flow-card-border, #d9dee7);
    border-radius: 8px;
    background: var(--flow-card-bg, #fff);
}

.flow-intake-schedule-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin-top: 6px;
}

/* Concrete properties, not --bs-btn-* : the bundled Bootstrap is 5.1 and does not read
   those tokens (#1817's guard caught exactly this). Three buttons on a 360px pane need
   to be smaller than .btn-sm and this is the only way to say so here. */
.flow-intake-schedule-actions .btn {
    padding: 1px 6px;
    font-size: 0.72rem;
    line-height: 1.4;
}

/* The one control that creates a schedule: a box wide enough to read a cron line in,
   and a button that does not shrink under it. */
.flow-intake-schedule-new {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin-top: 8px;
}

.flow-intake-schedule-new input { flex: 1 1 180px; min-width: 0; }
.flow-intake-schedule-new > .btn { flex: 0 0 auto; }
.flow-intake-schedule-new > .text-muted { flex: 1 0 100%; }

.flow-intake-schedule-status:empty,
.flow-intake-replay-status:empty { display: none; }

.flow-intake-schedule-status,
.flow-intake-replay-status { margin-bottom: 6px; }

/* #2361 — a stuck submission. The status badge sits beside the form name; the plain
   reason wraps under it, because #1612's sentences are sentences. */
.flow-intake-submissions .fw-semibold {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
}

.flow-intake-submissions .badge {
    font-weight: 500;
    font-size: 0.66rem;
    margin-left: auto;
}

.flow-intake-submissions > li > .btn { margin-top: 6px; }


/* ═══════════════════════════════════════════════════════════════════════════════
   #2368 — THE CONDITION
   Deliberately the loop's own shape one hue along: a customer who has read one
   container should not have to learn a second picture to read the other. Teal, not
   the accent (the accent is selection) and not amber (that is the loop), so a routed
   workflow reads as routed across a whole canvas at 40% zoom. The head carries a
   SENTENCE — "If $input.status equals pproved" — so it is prose, not a label,
   and is left-aligned and not uppercase for the reason the loop's head is.
   ═══════════════════════════════════════════════════════════════════════════════ */

:root {
    --flow-decision: #0f766e;
    --flow-decision-rgb: 15, 118, 110;
}

.flow-group-if {
    border-style: solid;
    border-color: rgba(var(--flow-decision-rgb), .28);
    background: rgba(var(--flow-decision-rgb), .045);
}

.flow-group-if .flow-group-label { color: var(--flow-decision); }

.flow-marker-decision {
    border-color: rgba(var(--flow-decision-rgb), .35);
    color: var(--flow-decision);
    background: #f5fdfc;
    justify-content: flex-start;
    padding: 0 12px;
    text-transform: none;
    letter-spacing: 0;
    font-size: 12.5px;
    cursor: grab;
}

.flow-marker-decision:active { cursor: grabbing; }

.flow-marker-decision > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.flow-marker-decision:hover {
    border-color: rgba(var(--flow-decision-rgb), .6);
    color: var(--flow-decision);
}

/* The Yes / No tag on the edge into each lane. Small, in the edge's own color family,
   and with a white halo through paint-order so it reads over the 2px stroke rather
   than needing a rectangle behind it. */
.flow-edge-label {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    fill: var(--flow-decision);
    stroke: #fff;
    stroke-width: 3px;
    paint-order: stroke fill;
    pointer-events: none;
}

/* #2370 — the lane results the merge section lists, and the reference each one is
   written as. A monospace ref beside a prose name, because one is typed and the
   other is read. */
.flow-insp-lane-results { display: flex; flex-direction: column; gap: 4px; }

.flow-insp-lane-result {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    padding: 5px 8px;
    border: 1px solid var(--flow-card-border, #d9dee7);
    border-radius: 8px;
    background: #fff;
}

.flow-insp-lane-ref {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 11px;
    color: var(--flow-muted, #667085);
    white-space: nowrap;
}

/* ══════════════════════════════════════════════════════════════════════════ */
/* CHROME BLOCK — wave 10-C (#2418, #2416, #2414, #2419).                     */
/* One delimited block at the END of the file so the chrome lane and the      */
/* cards lane can both write this stylesheet without fighting over a line.    */
/* Everything here is an OVERRIDE of a rule above; do not move it up.         */
/* ══════════════════════════════════════════════════════════════════════════ */

/* ── #2418: the all-clear, in the 40px strip ─────────────────────────────────
   "So what is this big thing with no problems? ... it's taking up a hell of a lot
   of fucking room" (Jeff, 2026-09-07 [23:23]). The drawer shuts itself on an
   all-clear and says so here instead. Muted green, not a badge: it is the absence
   of news, and it must not compete with the Problems count beside it. */
.flow-dock-verdict {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    margin-left: 6px;
    padding: 0 8px;
    height: 22px;
    font-size: 0.7rem;
    font-weight: 600;
    color: #0d7a4a;
    background: rgba(18, 183, 106, .10);
    border: 1px solid rgba(18, 183, 106, .28);
    border-radius: 999px;
    white-space: nowrap;
}

.flow-dock-verdict[hidden] {
    display: none;
}

.flow-dock-verdict i {
    font-size: 0.8rem;
    color: var(--flow-success);
}


/* ── #2416: the palette and the inspector are draggable ──────────────────────
   "this is an issue where we can't drag this out, but the text is flat across. If you
   hover, it does tell you more information, but it's kind of funky that way."
   (Jamie, 2026-09-07 [03:52].)

   FlowChrome writes `--flow-palette-w-set` / `--flow-inspector-w-set` INLINE on the
   workbench. These two rules consume them as defaults, which is the whole trick: an
   inline custom property would beat `.flow-workbench.palette-collapsed` (48px) and
   `.flow-workbench.has-inspector` (360px) and freeze both states, whereas a stored
   width read through var() leaves those class rules winning. */
.flow-workbench {
    --flow-palette-w: var(--flow-palette-w-set, 300px);
}

/* Both selectors, because the configuration column is now there in every state and a
   stored drag width has to survive deselecting. The `.has-inspector` half only exists to
   out-specify the earlier `.flow-workbench.has-inspector` rules. */
.flow-workbench,
.flow-workbench.has-inspector {
    --flow-inspector-w: var(--flow-inspector-w-set, 360px);
}

/* ...and the narrow-window collapse has to be re-stated HERE, after that, or the stored
   width wins and the column stays 360px wide behind the overlay sheet. It has been that
   way since #2416 added the rule above: the `--flow-inspector-w: 0px` in the 1199.98px
   block up the file had the same specificity and lost on order. */
@media (max-width: 1199.98px) {
    .flow-workbench,
    .flow-workbench.has-inspector {
        --flow-inspector-w: 0px;
    }
}

/* The handles sit on the edge each panel shares with the canvas. Positioned children,
   so the panels become their own containing block; the 3px overhang is why they also
   take a stacking order above the canvas panel, which is `position: relative` and comes
   later in the document. */
.flow-palette,
.flow-inspector {
    position: relative;
    z-index: 3;
}

.flow-resize-handle {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 9px;
    z-index: 6;
    cursor: col-resize;
    background: transparent;
    touch-action: none;
}

/* A 1px rule that lights up rather than a permanent gutter: the edge is already drawn
   by the panel's own border, and a second visible line at rest is chrome for chrome. */
.flow-resize-handle::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 4px;
    width: 1px;
    background: transparent;
    transition: background var(--flow-ease);
}

.flow-resize-handle:hover::after,
.flow-resize-handle:focus-visible::after,
.flow-resize-handle.is-resizing::after {
    background: var(--flow-accent);
}

.flow-resize-handle:focus-visible {
    outline: none;
}

/* Both handles sit on the edge their panel shares with the thing to its right. */
.flow-resize-palette { right: -3px; }
.flow-resize-inspector { right: -3px; }

/* Nothing to drag when the column is a 48px rail, or when the inspector is shut. */
.flow-workbench.palette-collapsed .flow-resize-palette,
.flow-workbench:not(.is-adding) .flow-resize-palette,
.flow-workbench:not(.has-inspector) .flow-resize-inspector {
    display: none;
}

/* A drag must not leave the pointer selecting text down the whole page. */
.flow-workbench.is-resizing,
.flow-workbench.is-resizing * {
    user-select: none;
}

/* ── #2416: a name a person can read beats a name they must hover ─────────────
   .flow-palette-label was `white-space: nowrap` inside a 300px row that also carries a
   28px tile, the KIND badge and the price slot, so "Convert Word to PDF/A-2b" ended in
   an ellipsis and the title attribute was the only way to read it. Two lines, then
   clamp — the row grows to fit rather than the text disappearing. */
.flow-palette-label {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    white-space: normal;
    overflow: hidden;
    text-overflow: clip;
    line-height: 1.22;
}

/* ...which means the row can no longer be a fixed 36px. */
.flow-palette-item {
    height: auto;
    min-height: 36px;
    padding-top: 4px;
    padding-bottom: 4px;
}

/* ── #2533: the cross-listing note took the name's width ─────────────────────
   Jeff, 2026-09-08 [15:26], on the Destinations band: "see how it says 'also in
   email web' but it's cut off the first bit" — and [14:38] "why is there something
   in front of it". `.flow-palette-also` was `flex: 0 0 auto` with `white-space:
   nowrap`, so "also in Email & Web" claimed ~95px of a 258px row that already
   carried a 28px tile, a price pill and a star. The label is the only shrinkable
   child, so it collapsed to about 50px: "Send Email" rendered as "Send" / "Ema",
   clamped at two lines and clipped — the row read as the cross-reference with a
   fragment in front of it, which is exactly what Jeff described.

   #2416 fixed the same class of problem for long names by clamping to two lines;
   it could not help here because the width was gone, not the height. The note goes
   on its own line instead, indented under the label so it reads as belonging to the
   row above it, and the name gets the whole first line back. Only the cross-listed
   rows (Send Email and HTTP Request, #1229) grow; every other row is untouched. */
.flow-palette-item.is-crosslisted {
    flex-wrap: wrap;
    row-gap: 0;
}

.flow-palette-item.is-crosslisted .flow-palette-also {
    /* `order` puts it after the price and the star, and a 100% basis then wraps it
       onto a line of its own. 36px = the 28px tile plus the row's 8px gap. */
    order: 1;
    flex: 1 0 100%;
    margin-left: 36px;
    line-height: 1.2;
    overflow: hidden;
    text-overflow: ellipsis;
}


/* ── #2414 + #2419: configuration left, the flow right, ALWAYS ───────────────
   "if you're working on a flow like we are here, intake webhook, write the submission,
   dataverse query, result, last step, then that should be on the furthest right. And
   everything that leads up to that should be on the left of it." (Jeff, [37:37].)
   "everything on the left that configures the flow and the flow itself on the right."
   ([47:14].)
   "if I'm not adding a new action - in other words, adding a new bubble - I don't want
   to see that." (Jeff, [44:43], about the palette.)

   The grid that answers this is the BASE grid now, not a state — see THE WORKBENCH GRID
   up the file. What used to be here was a `.has-inspector.is-focused` swap that applied
   only while a card was selected, so browsing put the palette back on the left and the
   canvas back in the middle: the shape Jeff spent [36:17] describing as the thing he
   hates about Visual Studio. One layout in every state instead.

   `.is-focused` survives as a FACT — a card is selected and its pane is open — because
   the command bar condenses on it and the inspector's automatic width reads it. It no
   longer places anything.

   The palette follows the same rule with no state either: it is the 48px rail carrying
   "+ Add a step" until somebody is actually adding one. Deliberately the same 48px as
   `.palette-collapsed`, so the two cannot disagree about the width and FlowChrome has
   one number to hold the canvas floor against. */
.flow-workbench {
    --flow-palette-w: 48px;
}

.flow-workbench.is-adding {
    --flow-palette-w: var(--flow-palette-w-set, 300px);
}

/* ── #2414: the configuration column while nothing is selected ───────────────
   The left column is never empty. The step inspector fills it while a card is selected;
   this pane fills it the rest of the time and names the next thing to do, taken from the
   same stage the bar across the top has lit. Both live in `grid-area: inspector` and
   exactly one of them is on screen. */
.flow-configpane {
    grid-area: inspector;
    display: flex;
    flex-direction: column;
    min-height: 0;
    min-width: 0;
    overflow: hidden;
    background: var(--flow-surface);
    border-right: 1px solid var(--flow-line);
}

.flow-workbench.has-inspector .flow-configpane {
    display: none;
}

.flow-configpane-head {
    padding: 12px 14px 10px;
    border-bottom: 1px solid var(--flow-line-soft);
}

.flow-configpane-title {
    font-size: 0.86rem;
    font-weight: 700;
    color: var(--flow-text, #101828);
}

.flow-configpane-sub {
    margin-top: 2px;
    font-size: 0.74rem;
    color: var(--flow-muted, #667085);
}

.flow-configpane-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 12px 14px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.flow-configpane-next {
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--flow-accent, #1e4d7b);
}

.flow-configpane-stage {
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--flow-text, #101828);
}

.flow-configpane-note {
    font-size: 0.78rem;
    line-height: 1.45;
    color: var(--flow-muted, #667085);
}

.flow-configpane-hint {
    margin-top: auto;
    padding-top: 10px;
    font-size: 0.75rem;
    color: var(--flow-muted, #667085);
    border-top: 1px dashed var(--flow-line);
}

/* ── #2419: the rail's one affordance ────────────────────────────────────────
   Unless a step is being added the palette's head, body and collapsed rail all give way
   to a single vertical "+ Add a step". It is a real button in the palette, so nothing
   becomes unreachable and the tab order is unchanged. */
.flow-palette-add {
    display: none;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    gap: 10px;
    width: 100%;
    padding: 12px 0;
    color: var(--flow-accent);
    background: transparent;
    border: 0;
    cursor: pointer;
    transition: background var(--flow-ease);
}

/* Scoped to the three-column layout on purpose: below 992px the grid stacks and the
   palette is a full-width block at the TOP of the page, where a 48px vertical rail is a
   wall rather than a saving. There the full list stays. */
@media (min-width: 992px) {
    .flow-workbench:not(.is-adding) .flow-palette-add {
        display: flex;
    }

    /* ...and while the rail is showing, nothing else in the column is. */
    .flow-workbench:not(.is-adding) .flow-palette-head,
    .flow-workbench:not(.is-adding) .flow-palette-body,
    .flow-workbench:not(.is-adding) .flow-palette-rail {
        display: none;
    }
}

.flow-palette-add:hover {
    background: rgba(var(--flow-accent-rgb), .08);
}

.flow-palette-add:focus-visible {
    outline: none;
    box-shadow: inset 0 0 0 2px rgba(var(--flow-accent-rgb), .35);
}

.flow-palette-add i {
    font-size: 1.05rem;
    line-height: 1;
}

/* Vertical, because the column is 48px wide and an abbreviation nobody can read is
   worse than a label they have to tilt their head for. */
.flow-palette-add-label {
    writing-mode: vertical-rl;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
    .flow-workbench,
    .flow-palette-add,
    .flow-resize-handle::after {
        transition: none;
    }
}

/* ══════════════════ /CHROME BLOCK ══════════════════ */
/* ══════════════════════════════════════════════════════════════════════════════
   CARDS (#2420) — BEGIN. A bigger, two-line step card.

   Jeff, 2026-09-07 16:30, on a screen share: [21:34] "I need you to press control
   plus maybe three times... so that I can see it better", [46:24] "I might want to
   show a little more information in the bubble itself", [46:29] "have the bubbles
   take up more of the screen, not be just like a small part of it."

   The geometry lives in FlowLayout.METRICS (320x88, up from 280x64) because the
   layout is pure and must not read a stylesheet; everything here is the typography
   and spacing that fills that box. Keep the two numbers in step: this padding plus
   a 15px title at 1.25 plus two ~12px lines at 1.35 plus the 2px gaps is 82px, and
   NODE_H is 88.

   Deliberately a block at the END of the file, overriding the card rules above
   rather than editing them in place: the `chrome` lane is in this stylesheet in the
   same hour. Everything between these two markers is #2420's.
   ══════════════════════════════════════════════════════════════════════════════ */

.flow-card {
    padding: 12px 14px;
    gap: 14px;
}

/* 44px, up from 40. The tile is the card's anchor and it has 24 more pixels of card
   to sit in; at 40 in an 88px box it read as a small icon in a large empty tile. */
.flow-card-icon {
    flex: 0 0 44px;
    width: 44px;
    height: 44px;
    border-radius: 11px;
    font-size: 20px;
}

/* 15px, up from 14. At the 0.8 auto-fit floor (flow-canvas.js FIT_MIN_SCALE) this
   lands at 12px on screen, which is the whole reason the floor is 0.8. */
.flow-card-title {
    font-size: 15px;
}

/* LINE 2: what the step does it with — the literal it is set to ("CONFIDENTIAL .
   25%") or #2360's English sentence for a wired input ("uses the PDF from Render
   the invoice"). Painted only when there is one (flow-canvas.js renderCard), so a
   step with nothing to say keeps a one-line card inside the same box rather than
   reserving an empty row.

   Ellipsized on its OWN, which is the point of splitting it off line 1: joined,
   the two truncated together into "Watermark P... . CONFIDEN..." and neither
   fragment answered its question. */
.flow-card-detail {
    display: block;
    min-width: 0;
    font-size: 12px;
    line-height: 1.35;
    color: var(--flow-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* CARDS (#2420) — END. */


/* ═══════════════════════════════════════════════════════════════════════════
   #2406 — THE SWITCH, and the and/or condition editor.

   A switch is a DECISION, so it wears the condition's hue: a customer who has
   read one teal marker should read the other without learning a second color,
   and the glyph is what tells them apart. What it gets of its own is a slightly
   heavier group border (it holds several lanes rather than two, and the box has
   to hold together across a wider row) and a dotted lane divider, so the columns
   read as alternatives rather than as a fan-out's siblings.
   ═══════════════════════════════════════════════════════════════════════════ */

.flow-group-switch {
    border-style: dashed;
    border-color: rgba(var(--flow-decision-rgb), .38);
}

.flow-marker-switch {
    font-weight: 600;
}

/* The value on each edge into a lane. Longer than YES / NO and case-sensitive —
   `"invoice"` is the customer's own text — so it keeps its case rather than
   being shouted, and it is allowed a little more room before the ellipsis the
   layout applies. */
.flow-edge-label[data-verbatim] { text-transform: none; letter-spacing: 0; }

/* ── the and/or toggle (#2406) ───────────────────────────────────────────── */

.flow-insp-segment {
    display: flex;
    gap: 0;
    border: 1px solid var(--flow-card-border);
    border-radius: 8px;
    overflow: hidden;
    background: #fff;
}

.flow-insp-segment-btn {
    flex: 1 1 0;
    border: 0;
    border-right: 1px solid var(--flow-card-border);
    background: transparent;
    color: var(--flow-muted);
    font-size: 11.5px;
    font-weight: 600;
    padding: 6px 8px;
    cursor: pointer;
    transition: background var(--flow-ease), color var(--flow-ease);
}

.flow-insp-segment-btn:last-child { border-right: 0; }

.flow-insp-segment-btn:hover { background: rgba(var(--flow-accent-rgb), .06); }

.flow-insp-segment-btn.is-active {
    background: rgba(var(--flow-accent-rgb), .12);
    color: var(--flow-accent);
}

/* ── one clause row ──────────────────────────────────────────────────────── */

.flow-insp-clause {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 6px;
}

.flow-insp-clause .flow-insp-picker-slot { flex: 1 1 0; min-width: 0; }

.flow-insp-clause select { flex: 0 0 auto; width: auto; }

.flow-insp-clause-nested {
    flex: 1 1 auto;
    min-width: 0;
    font-family: var(--bs-font-monospace, ui-monospace, monospace);
    font-size: 11.5px;
    color: var(--flow-muted);
    background: rgba(var(--flow-decision-rgb), .06);
    border: 1px dashed rgba(var(--flow-decision-rgb), .3);
    border-radius: 6px;
    padding: 4px 6px;
    overflow-x: auto;
    white-space: nowrap;
}

.flow-insp-clause-remove {
    flex: 0 0 auto;
    border: 0;
    background: transparent;
    color: var(--flow-muted);
    border-radius: 6px;
    width: 26px;
    height: 26px;
    line-height: 1;
    cursor: pointer;
    transition: background var(--flow-ease), color var(--flow-ease);
}

.flow-insp-clause-remove:hover {
    background: rgba(var(--flow-error-rgb, 240, 68, 56), .1);
    color: var(--flow-error);
}

/* #2406 — SWITCH — END. */
/* DESTINATIONS NUDGE (#2440) — START.

   The Destinations band on /try, where the anonymous allowlist carries no destination
   endpoint at all. It has to read as a LOCKED SHELF, not as a section that failed to
   load and not as something you can drag: no row hover, no grab cursor, muted weight
   throughout, and the band's own lock glyph in the header so the state is legible before
   the sentence is read. Never rendered on the portal page. */
.flow-palette-band-locked .flow-palette-bandhead {
    opacity: .8;
}

.flow-palette-bandlock {
    margin-left: auto;
    flex: 0 0 auto;
    font-size: 0.72rem;
    color: var(--flow-muted);
}

.flow-palette-nudge {
    margin: 2px 8px 8px;
    padding: 9px 11px;
    border: 1px dashed var(--flow-line-soft);
    border-radius: 8px;
    background: var(--flow-surface-sunken);
    cursor: default;
}

.flow-palette-nudge-text {
    font-size: 0.74rem;
    line-height: 1.4;
    color: var(--flow-muted);
}

.flow-palette-nudge-link {
    display: inline-block;
    margin-top: 5px;
    font-size: 0.74rem;
    font-weight: 600;
    color: var(--flow-accent);
    text-decoration: none;
}

.flow-palette-nudge-link:hover {
    text-decoration: underline;
}

/* DESTINATIONS NUDGE (#2440) — END. */

/* COMMAND BAR OVERFLOW (#2438) — START.

   The "More" button and the panel the secondary button set moves into at narrow widths.
   Its own popover rather than a Bootstrap dropdown, for one reason: the Export button
   inside it IS a Bootstrap dropdown, and nesting one in another gives two menus fighting
   over the same outside-click. This one closes on any click, including a click on the
   button it just revealed. */
.flow-cb-more {
    position: relative;
    flex: 0 0 auto;
}

.flow-cb-more-panel {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 1050;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    min-width: 190px;
    padding: 6px;
    background: var(--flow-surface);
    border: 1px solid var(--flow-line);
    border-radius: var(--flow-radius);
    box-shadow: 0 8px 24px rgba(16, 24, 40, .14);
}

/* The moved group stacks in the panel — it is a row inside the bar and a menu here. */
.flow-cb-more-panel .flow-cb-secondary {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
}

.flow-cb-more-panel .flow-btn,
.flow-cb-more-panel .btn {
    justify-content: flex-start;
    width: 100%;
}

/* The Export dropdown keeps working inside the panel; it just opens to the left of it
   rather than under a bar button that is no longer there. */
.flow-cb-more-panel .btn-group {
    display: flex;
}

/* COMMAND BAR OVERFLOW (#2438) — END. */


/* DELIVER EACH ROW ONCE (#2449) — the watermark fields under the cadence box.

   Its own row inside the schedule form's flex line, because the column name and the
   first-run choice are one decision and reading them wrapped around a cron line makes
   them look like two unrelated settings. */
.flow-intake-watermark {
    flex: 1 0 100%;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    padding-top: 6px;
    border-top: 1px solid var(--flow-line-soft, rgba(0, 0, 0, .08));
}

.flow-intake-watermark > .text-muted { flex: 1 0 100%; }
.flow-intake-watermark input { flex: 1 1 160px; min-width: 0; }
.flow-intake-watermark select { flex: 1 1 200px; min-width: 0; }

/* The stored mark reads as a sentence, so it wraps rather than truncating: a person
   reconciling what arrived against what they expected needs the whole value. */
.flow-intake-watermark-line { word-break: break-word; }

/* DELIVER EACH ROW ONCE (#2449) — END. */
