/* ===========================================================================
   NexSpec Warehouse design tokens.

   Two layers, deliberately:

   LAYER 1 is the design system's own palette, copied verbatim from the DS
   project (tokens/colors.css et al). Nothing in this stylesheet may reference
   these directly — they are the vocabulary, not the usage.

   LAYER 2 is the app's ROLES (--panel, --warn-bg, --accent-solid …) expressed
   in terms of layer 1. Every rule below uses role tokens only, which is what
   makes a theme a swap of ~35 values rather than an edit of 1600 lines. Before
   this, app.css held 11 ad-hoc tokens plus 70 hex literals and 31 inline
   rgba() washes, and a theme was not expressible at all.

   Two shapes recur and are worth knowing:
     --x        text/border-safe. Readable ON the page background.
     --x-solid  the loud fill. Text on top of it uses --on-accent.
   In dark they collapse to the same value; in light they cannot, because
   #2196F3 and #FBB500 are fine as fills and fail contrast as small text.
   =========================================================================== */

@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,300;0,400;0,500;0,700;1,400&family=Roboto+Mono:wght@400;500&display=swap');

:root {
  /* ---- Layer 1: the DS palette (verbatim; do not use directly) ---------- */
  --color-primary: #F27E30;          /* NexSpec orange — the brand accent */
  --color-primary-tint: #FEF3EB;
  --color-secondary: #C64C1C;
  --color-white: #FFFFFF;            /* surfaces, cards */
  --color-surface: #F6F8FA;          /* page / field fill */
  --color-border: #DDE0E3;           /* borders, app canvas */
  --color-ink: #1B1B1B;
  --color-charcoal: #4B4D4F;
  --color-slate: #6A6E72;

  --color-info:    #2196F3;  --color-info-bg:    #D3EAFD;  --color-info-text:    #004073;
  --color-success: #4CAF50;  --color-success-bg: #DBEFDC;  --color-success-text: #004D03;
  --color-warning: #FBB500;  --color-warning-bg: #FEF0CC;  --color-warning-text: #8C5E00;
  --color-error:   #FF5252;  --color-error-bg:   #FFDCDC;  --color-error-text:   #8C0000;

  --font-sans: 'Roboto', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif;
  --font-mono: 'Roboto Mono', ui-monospace, Menlo, Monaco, Consolas, monospace;

  --radius-sm: 4px;
  --radius-md: 6px;
  --radius-lg: 8px;
  --radius-pill: 999px;

  /* The DS is flat for cards (1px border, no shadow); shadow is reserved for
     overlay layers, which is exactly what this app's five modals are. */
  --shadow-menu: 0 4px 16px rgba(0, 0, 0, 0.12);
  --shadow-dialog: 0 12px 32px rgba(0, 0, 0, 0.24);

  /* ---- Layer 2: app roles — LIGHT, the DS default ----------------------- */
  --bg: var(--color-border);         /* the DS calls #DDE0E3 the app canvas */
  --panel: var(--color-white);
  --panel-2: var(--color-surface);   /* inset fill: inputs, sunken strips */
  --border: var(--color-border);
  --border-subtle: rgba(0, 0, 0, 0.08);
  --border-hover: var(--color-slate);
  --text: rgba(0, 0, 0, 0.8);        /* the DS never uses solid black for text */
  /* DEVIATION from the DS's --text-secondary (rgba(0,0,0,.5)). At .5 this
     lands at 3.98:1 on white, and this tool sets secondary text at 10-12px
     where AA wants 4.5. .6 gives 5.4:1 and is visually indistinguishable. */
  --muted: rgba(0, 0, 0, 0.6);
  --hover: rgba(0, 0, 0, 0.05);      /* DS --action-hover */
  --inset: rgba(0, 0, 0, 0.04);
  --backdrop: rgba(27, 27, 27, 0.45);
  --on-accent: var(--color-white);   /* text on any -solid fill */

  --accent:        var(--color-info-text);
  /* DEVIATION, and worth knowing about: the DS's own primary-blue button is
     white on rgba(33,150,243,.85), which is ~2.8:1 — it fails AA, and plain
     #2196F3 only reaches 3.12:1. This steps down the same hue to Material
     Blue 700, reading as the identical blue at 4.6:1 with white text. */
  --accent-solid:  #1976D2;
  --accent-bg:     var(--color-info-bg);
  --accent-border: #A8D5FB;

  --ok:        var(--color-success-text);
  --ok-solid:  var(--color-success);
  --ok-bg:     var(--color-success-bg);
  --ok-border: #A5D6A7;

  --warn:        var(--color-warning-text);
  --warn-solid:  var(--color-warning);
  --warn-bg:     var(--color-warning-bg);
  --warn-bg-strong: #FBE3A0;
  --warn-border: #F2D48A;

  --danger:        var(--color-error-text);
  --danger-solid:  var(--color-error);
  --danger-bg:     var(--color-error-bg);
  --danger-bg-strong: #FFC9C9;
  --danger-border: #F5A9A9;

  /* Inert states ("identical", "unchanged") are deliberately NOT success —
     they mean no-op, and colouring them green reads as an achievement. */
  --neutral: var(--muted);
  --neutral-bg: var(--inset);
  --neutral-border: var(--border);

  /* Legacy alias: --accent-2 meant "low-emphasis success" (mapped QB app,
     verified-identical). Kept so older rules resolve; new rules use --ok. */
  --accent-2: var(--ok);

  --brand: var(--color-primary);
  /* Brand triplet, for "this is production": a fact about the destination,
     not an error, so it must not borrow the danger red. Text is a darker
     orange than the brand fill — #F27E30 is 2.7:1 on white and fails AA at
     9px; #A83F14 on this tint measures 5.68:1, above the danger badge's own
     ~5.1. The border is decorative and carries no contrast requirement. */
  --brand-bg:     #FEF3EC;
  --brand-text:   #A83F14;
  --brand-border: #F5C9AE;
  color-scheme: light;
}

/* Dark: the palette this tool shipped with, kept intact so switching costs an
   operator nothing. Relationships are preserved rather than hues — orange is
   still brand, blue still interaction, the semantic families still hold. */
:root[data-theme="dark"] {
  --bg: #0f1115;
  --panel: #161a22;
  --panel-2: #1c2230;
  --border: #262d3d;
  --border-subtle: #1a1f2a;
  --border-hover: #3a4a6a;
  --text: #e6e9ef;
  --muted: #8b94a7;
  --hover: rgba(255, 255, 255, 0.04);
  --inset: rgba(0, 0, 0, 0.18);
  --backdrop: rgba(8, 10, 16, 0.78);
  --on-accent: #0b1322;

  --accent:        #6aa9ff;
  --accent-solid:  #6aa9ff;
  --accent-bg:     #15233a;
  --accent-border: #2c3a4a;

  --ok:        #7ee7c7;
  --ok-solid:  #7ee7c7;
  --ok-bg:     rgba(126, 231, 199, 0.10);
  --ok-border: #2c4a40;

  --warn:        #ffb347;
  --warn-solid:  #ffb347;
  --warn-bg:     rgba(255, 179, 71, 0.10);
  --warn-bg-strong: rgba(255, 179, 71, 0.20);
  --warn-border: #4a3a2c;

  --danger:        #ff6b6b;
  --danger-solid:  #ff6b6b;
  --danger-bg:     rgba(255, 107, 107, 0.10);
  --danger-bg-strong: rgba(255, 107, 107, 0.20);
  --danger-border: #4a2c2c;

  --neutral: var(--muted);
  --neutral-bg: rgba(255, 255, 255, 0.04);
  --neutral-border: var(--border);

  --accent-2: var(--ok);
  --brand: var(--color-primary);
  --brand-bg:     #3a2216;
  --brand-text:   var(--color-primary);   /* #F27E30 on the tint: 5.49:1 */
  --brand-border: #5a3320;
  color-scheme: dark;
}
* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
  background: var(--bg);
  color: var(--text);
  font: 14px/1.5 var(--font-sans);
  display: grid;
  /* top bar · ledger · stage · action bar */
  grid-template-rows: auto auto minmax(0, 1fr) auto;
  height: 100vh;
}

/* Rows are pinned explicitly. Hiding the ledger with display:none removes it
   from the grid, and without this every later child shifts up a row — the
   stage landed in the ledger's auto row and the action bar inherited the 1fr,
   leaving a 500px void under it on the Logs screen. */
.topbar { grid-row: 1; }
.ledger { grid-row: 2; }
.stage { grid-row: 3; }
.action-bar { grid-row: 4; }

/* Both views occupy the same stage cell, so one action bar can serve every
   route instead of each view carrying its own footer. */
.stage { min-height: 0; display: grid; }
.stage > .view { grid-area: 1 / 1; }

/* ---- Top bar (dark ink, per the DS's nav surface) ---------------------- */
.topbar {
  background: var(--color-ink);
  color: #FFFFFF;
  /* Three columns so the tool switcher is centred on the HEADER, not merely
     between the brand and the right-hand cluster. Both side columns take an
     equal share of the free space, and `minmax(0, …)` lets them shrink rather
     than push the centre off when the status line runs long. */
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: stretch;
  gap: 24px;
  padding: 0 16px;
  height: 48px;
}
.topbar .tool-nav { align-self: center; }
.topbar-right {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 24px;
  min-width: 0;
}
/* The status is the one thing here that can run long, so it is the one thing
   that gives way. */
.topbar-right .topbar-status,
.topbar-right .topbar-source {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 0 0 auto;
  text-decoration: none;
  color: inherit;
}
.brand-name { font: 500 14px/1 var(--font-sans); letter-spacing: 0.2px; }
.logomark { display: block; flex: 0 0 auto; }
.topbar-source { align-self: center; font-size: 12px; color: rgba(255, 255, 255, 0.5); }
.topbar-status { align-self: center; font-size: 12px; color: rgba(255, 255, 255, 0.5); }
.topbar-user { align-self: center; font-size: 12px; color: rgba(255, 255, 255, 0.75); white-space: nowrap; }
.topbar-status.ok { color: rgba(255, 255, 255, 0.5); }
.topbar-status.err { color: #FFB3B3; }
/* Icon-only chrome buttons on the dark bar. */
.topbar .icon-only {
  width: 32px;
  height: 32px;
  align-self: center;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  border-radius: var(--radius-md);
  color: rgba(255, 255, 255, 0.7);
  cursor: pointer;
  padding: 0;
}
.topbar .icon-only:hover:not(:disabled) { background: rgba(255, 255, 255, 0.1); color: #FFFFFF; }
.topbar .icon-only .ms { font-size: 20px; }

.tool-nav { display: flex; align-items: stretch; flex: 0 0 auto; gap: 0; }
.tool-nav a {
  display: flex;
  align-items: center;
  padding: 0 16px;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.5);
  text-decoration: none;
  border-bottom: 2px solid transparent;
}
.tool-nav a:hover { color: #FFFFFF; }
/* Orange underline marks the active tool — the brand accent's one job in the
   chrome. Driven by body[data-route], which the router stamps. */
body[data-route='promote'] .tool-nav a[data-route='promote'],
body[data-route='audit']   .tool-nav a[data-route='audit'],
body[data-route='logs']    .tool-nav a[data-route='logs'] {
  color: #FFFFFF;
  font-weight: 500;
  border-bottom-color: var(--brand);
}
/* The gear is the way into Settings; it lights up like an active tab. */
body[data-route='settings'] .topbar #manageTenants { background: rgba(255, 255, 255, 0.14); color: #FFFFFF; }

/* ---- Ledger stepper --------------------------------------------------- */
/* Each step shows the choice made in it, so wizard state is readable without
   stepping back to look. Logs is not a wizard step, so it has nothing here. */
.ledger {
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}
body[data-route='logs'] .ledger,
body[data-route='settings'] .ledger { display: none; }
.ledger-step {
  padding: 11px 18px 12px;
  border: none;
  border-right: 1px solid var(--border-subtle);
  border-top: 2px solid transparent;
  background: transparent;
  color: inherit;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  text-align: left;
  font-family: inherit;
}
.ledger-step:last-child { border-right: none; }
.ledger-step:hover:not(:disabled) { background: var(--hover); }
.ledger-step:disabled { cursor: default; opacity: 0.55; }
.ledger-step.active { border-top-color: var(--accent-solid); background: var(--panel-2); }
.ledger-head { display: flex; align-items: center; gap: 7px; }
.ledger-label {
  font: 500 11px/1 var(--font-sans);
  letter-spacing: 0.7px;
  text-transform: uppercase;
  color: var(--muted);
}
.ledger-step.active .ledger-label { color: var(--accent); }
.ledger-mark { font-size: 16px; color: var(--ok-solid); flex: 0 0 auto; }
.ledger-num {
  width: 16px;
  height: 16px;
  border-radius: 999px;
  background: var(--accent-solid);
  color: var(--on-accent);
  font: 700 10px/16px var(--font-sans);
  text-align: center;
  flex: 0 0 auto;
}
.ledger-num.todo { background: var(--neutral-bg); color: var(--muted); }
.ledger-value {
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  padding-left: 23px;
}
.ledger-value .sub { color: var(--muted); font-weight: 400; }
.ledger-value .none { color: var(--muted); font-weight: 400; }

main {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  min-height: 0;
  overflow: hidden;
}
/* Every step lives in column 1; the drawer is column 2. */
main > section.step { grid-column: 1; }
main > section.step {
  display: none;
  height: 100%;
  min-height: 0;
}
main > section.step.active {
  display: flex;
  flex-direction: column;
}
main > section.step > .col {
  flex: 1 1 auto;
  min-height: 0;
  border-right: none;
}

/* Wizard stepper */
.stepper {
  display: flex;
  align-items: stretch;
  gap: 0;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  padding: 0 8px;
}
.stepper .wstep {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  font-size: 12px;
  color: var(--muted);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  user-select: none;
}
.stepper .wstep:hover { color: var(--text); }
.stepper .wstep .num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 999px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  font-size: 11px;
  font-weight: 600;
}
.stepper .wstep.done { color: var(--text); }
.stepper .wstep.done .num {
  background: var(--ok-bg);
  color: var(--ok);
  border-color: var(--ok-border);
}
.stepper .wstep.active { color: var(--accent); border-bottom-color: var(--accent); }
.stepper .wstep.active .num {
  background: var(--accent-solid);
  color: var(--on-accent);
  border-color: var(--accent);
}

/* Source chip — shows the selected source tenant once column 1 is collapsed. */
.source-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--panel-2);
  font-size: 12px;
}
.source-chip.hidden { display: none; }
.source-chip .chip-lbl {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--muted);
}
.source-chip .chip-name {
  font-weight: 600;
  color: var(--accent);
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.source-chip .chip-change {
  font-size: 10px;
  padding: 2px 8px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  text-transform: uppercase;
  letter-spacing: 0.3px;
}
.source-chip .chip-change:hover { color: var(--text); border-color: var(--accent); }

/* QuickBase tenant→app mapping badge. Lives inside the source chip (step 2)
   and inside each destination tile (step 3). */
.qb-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-family: var(--font-mono);
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--panel-2);
  color: var(--muted);
  flex-wrap: wrap;
}
.qb-badge.unmapped { color: var(--warn);     border-color: var(--warn-border); background: var(--warn-bg); }
.qb-badge.readonly { color: var(--accent-2); border-color: var(--ok-border); background: var(--ok-bg); }
.qb-badge.unmapped a { color: inherit; text-decoration: underline; }
.qb-badge.unmapped a:hover { color: var(--text); }
.qb-badge code { font-family: inherit; background: var(--inset); padding: 0 4px; border-radius: 3px; }
.qb-badge .qb-lbl {
  text-transform: uppercase;
  letter-spacing: 0.4px;
  font-size: 9px;
  opacity: 0.85;
}
.qb-badge .qb-name {
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.qb-badge.unmapped .qb-name { max-width: none; white-space: normal; overflow: visible; text-overflow: clip; }
.qb-badge-slot { margin-right: 6px; }

/* Inside destination tile: badge sits on its own line under the meta. */
.dest .qb-badge { margin-top: 6px; }

.col {
  min-height: 0;
  min-width: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--border);
  background: var(--panel);
}
.col:last-child { border-right: none; }

/* Step 4 is the only mode-dependent screen, so it holds both panels and shows
   one. Steps 1-3 are shared markup and need no such switch. */
.mode-panel { display: none; }
.mode-panel.active { display: flex; flex-direction: column; }

.col-head {
  padding: 12px 16px 8px;
  display: flex;
  align-items: center;
  gap: 8px;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}
.col-head h2 {
  flex: 1;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--muted);
  margin: 0;
}
.col-head input[type=search] {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--text);
  padding: 4px 10px;
  font-size: 12px;
  outline: none;
  width: 130px;
}
.col-head input[type=search]:focus { border-color: var(--accent); }
.col-head input[type=search]:disabled { opacity: 0.4; }
.col-head .muted {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  color: var(--muted);
}
.list { padding: 8px; overflow: auto; flex: 1; }
.empty { color: var(--muted); padding: 16px; text-align: center; font-size: 12px; }

/* Pickers whose rows are short, uniform cards (step 1 tenants, step 3
   destinations) reflow into however many columns fit, instead of one
   full-width row per item — 28 tenants at 1400px wide was mostly dead space
   and a lot of scrolling. auto-fill means this collapses to a single column
   on narrow viewports with no breakpoints of its own.
   The step-2 tree and the step-4 review body stay single-column: their rows
   are hierarchical, not uniform cards. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 10px;
  align-content: start;
  padding: 16px;
}
/* Cards space themselves with the grid gap; their own margins would double it. */
.card-grid > * { margin-bottom: 0; }
.card-grid .empty { grid-column: 1 / -1; }
/* Long monospace metadata (plan id, external id) must not stretch a column. */
.card-grid .meta { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ---- Tenant list (left) ---- */
/* Cards carry a left accent bar rather than only a border: at a glance down a
   grid of 29, a 4px coloured edge reads as "this one" far faster than a
   1px ring does. */
.tenant {
  padding: 12px 14px 12px 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--panel);
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: border-color 0.12s;
}
.tenant::before {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 4px;
  background: transparent;
}
.tenant:hover { border-color: var(--border-hover); }
.tenant.active {
  border-color: var(--accent-solid);
  box-shadow: 0 0 0 1px var(--accent-solid) inset;
}
.tenant.active::before { background: var(--accent-solid); }
.tenant .name { font-weight: 600; font-size: 13px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.tenant .meta {
  color: var(--muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  margin-top: 2px;
}
.tag {
  font-size: 10px;
  padding: 1px 6px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* ---- Step 2 layout: tree pane + manifest sidebar ---- */
.step2-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 280px;
  min-height: 0;
  height: 100%;
}
.step2-layout .tree-pane {
  display: flex;
  flex-direction: column;
  min-height: 0;
  border-right: 1px solid var(--border);
}
.step2-layout .stats-pane {
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--panel);
  overflow: hidden;
}
.step2-layout .stats-pane .col-head { padding: 12px 16px; }
.manifest-stats-list {
  flex: 1;
  overflow: auto;
  padding: 10px 14px;
  font-size: 13px;
}

/* Type filter chip row */
.type-filter {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--inset);
}
.type-filter.hidden { display: none; }

/* Project-wide promote flags (include tags / data / test cases). */
.promote-flags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--inset);
  font-size: 12px;
  color: var(--muted);
}
.promote-flags.hidden { display: none; }
.promote-flags label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  user-select: none;
}
.promote-flags input[type=checkbox] { accent-color: var(--accent); cursor: pointer; }
/* ---- Material Symbols --------------------------------------------------
   A ligature font: the element's TEXT is the icon name, which is why every
   emitter marks it translate="no" (a translation would rewrite the glyph).
   Unlike the emoji this replaced, it inherits currentColor and font-size — so
   an icon inside a warning row is warning-coloured without a single rule. */
.ms {
  font-family: 'Material Symbols Rounded';
  font-weight: normal;
  font-style: normal;
  font-size: 18px;
  line-height: 1;
  letter-spacing: normal;
  text-transform: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  direction: ltr;
  font-feature-settings: 'liga';
  font-variation-settings: 'opsz' 20, 'wght' 400, 'FILL' 0, 'GRAD' 0;
  -webkit-font-smoothing: antialiased;
  vertical-align: middle;
  flex: 0 0 auto;
}
/* Tree rows are dense and their heights are load-bearing (step 4 measures
   indentation off them), so icons there stay at the old glyph's size. */
.tree-folder .icon .ms,
.tree-asset .icon .ms,
.merged-asset-head .icon .ms { font-size: 15px; }
.twist .ms { font-size: 16px; }
.tf-btn .ms, .pf-chip .ms, .rm-icon .ms, .status-tag .ms { font-size: 14px; }
.dep-warn .ms, .mb-warn .ms, .mb-summary .ms { font-size: 13px; }
.ghost .ms, .primary .ms { font-size: 16px; }
.tool-nav a .ms { font-size: 15px; }

/* ---- Dependency warnings (step 2) ------------------------------------- */

/* Selection-wide notice above the tree. Collapses to nothing when the
   selection is self-contained, so the strip only exists when it has news.
   The audit's "incomplete" notice is the same strip above its matrix. */
.dep-notice, .audit-incomplete {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--warn-border);
  background: var(--warn-bg);
  color: var(--warn);
  font-size: 12px;
}
.dep-notice:empty, .audit-incomplete:empty { display: none; }
.dep-notice-icon, .audit-incomplete .ms { font-size: 13px; }
.dep-notice button, .audit-incomplete button { color: var(--warn); border-color: var(--warn-border); }

/* Per-row badge. Sits inline after the asset name; the row toggles selection,
   so the badge stops propagation. */
.dep-warn {
  margin-left: 6px;
  padding: 0 6px;
  border: 1px solid var(--warn-border);
  border-radius: 999px;
  background: var(--warn-bg);
  color: var(--warn);
  font-size: 10px;
  line-height: 16px;
  cursor: pointer;
  flex: 0 0 auto;
}
.dep-warn:hover { background: var(--warn-bg-strong); }

.deps-body { display: block; overflow-y: auto; }
.dep-group { margin-bottom: 14px; }
.dep-group-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 4px;
  border-bottom: 1px solid var(--border);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--muted);
}
.dep-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 4px;
  font-size: 12px;
  cursor: pointer;
}
.dep-row:hover { background: var(--panel-2); }
.dep-row input[type=checkbox] { accent-color: var(--accent); cursor: pointer; }
.dep-row .dep-name { color: var(--text); }
.dep-row .dep-why { font-size: 11px; }
/* Out-of-project dependencies can't be selected here, so they get no
   checkbox and no hover affordance — they're a statement, not an action. */
.dep-row.is-external { cursor: default; }
.dep-row.is-external:hover { background: none; }
.dep-external .dep-group-head { color: var(--warn); border-bottom-color: var(--warn-border); }
.dep-note {
  padding: 6px 4px 8px;
  font-size: 11px;
  line-height: 1.5;
  color: var(--muted);
}

.promote-flags .depth-field { gap: 8px; }
.promote-flags select {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  padding: 3px 8px;
  font-size: 12px;
  outline: none;
  cursor: pointer;
}

.type-filter .tf-lbl {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--muted);
  margin-right: 4px;
}
.type-filter .tf-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  padding: 3px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}
.type-filter .tf-btn:hover { color: var(--text); }
.type-filter .tf-btn.active {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-bg);
}
.type-filter .tf-btn .tf-icon { font-size: 13px; }
.type-filter .tf-btn .tf-count {
  font-family: var(--font-mono);
  font-size: 10px;
  opacity: 0.7;
  padding: 0 4px;
  background: var(--hover);
  border-radius: 999px;
}

/* Source tree (unified folder + asset) */
.source-tree { padding: 6px 0; font-size: 13px; }
.source-tree-inner { padding: 4px 0; }
.tree-folder, .tree-asset {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 8px;
  border-radius: 4px;
  cursor: pointer;
  user-select: none;
}
.tree-folder:hover, .tree-asset:hover { background: var(--hover); }
.tree-folder { font-weight: 500; }
.tree-folder.active { background: var(--accent-bg); color: var(--accent); }
.tree-asset.unchecked { opacity: 0.55; }
.tree-folder .twist, .tree-asset .twist, .merged-asset-head .twist {
  display: inline-block;
  width: 18px;
  text-align: center;
  font-size: 16px;
  color: var(--muted);
}
.tree-folder input[type=checkbox],
.tree-asset input[type=checkbox] {
  margin: 0;
  accent-color: var(--accent);
  cursor: pointer;
}
.tree-folder .icon, .tree-asset .icon { font-size: 14px; line-height: 1; }
.tree-folder .name {
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tree-folder .project-tag {
  font-size: 9px;
  padding: 1px 6px;
  border-radius: 4px;
  background: var(--accent-bg);
  color: var(--accent);
  border: 1px solid var(--accent-border);
  text-transform: uppercase;
  letter-spacing: 0.3px;
}
.tree-folder .count {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--muted);
}
.tree-asset .name {
  flex: 1;
  font-weight: 400;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tree-asset .prov {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--muted);
}
.tree-asset .val {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--accent-2);
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tree-asset .val.empty { color: var(--muted); opacity: 0.6; font-style: italic; }
/* How a promote treats a property (src/property-modes.js): the label on the
   row, and the menu that changes it. */
.prop-mode {
  display: inline-flex; align-items: center; gap: 3px; flex: 0 0 auto;
  font: 500 10px/1 var(--font-sans); padding: 3px 7px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--panel-2); color: var(--muted); cursor: pointer;
}
.prop-mode .ms { font-size: 12px; }
.prop-mode.copy { color: var(--accent); border-color: var(--accent-border); background: var(--accent-bg); }
.prop-mode.rule { color: var(--ok); border-color: var(--ok-border); background: var(--ok-bg); }
.pm-opts { display: grid; gap: 2px; padding: 6px; }
.pm-opt { display: grid; gap: 2px; text-align: left; padding: 7px 9px; border: 0; border-radius: 6px; background: none; cursor: pointer; color: var(--text); }
.pm-opt:hover:not([disabled]) { background: var(--panel-2); }
.pm-opt.on { background: var(--accent-bg); }
.pm-opt[disabled] { cursor: default; opacity: 0.55; }
.pm-name { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 500; }
.pm-name .ms { font-size: 14px; }
.pm-default { font-weight: 400; font-size: 10px; color: var(--muted); border: 1px solid var(--border); border-radius: 999px; padding: 0 5px; }
.pm-why { font-size: 11px; color: var(--muted); padding-left: 19px; }
.pm-rule { font-size: 11px; color: var(--muted); padding: 6px 12px; border-top: 1px solid var(--border); }
.pm-rule code { font-family: var(--font-mono); font-size: 10px; color: var(--text); }
.pm-foot { display: flex; align-items: center; gap: 5px; font-size: 11px; color: var(--muted); padding: 7px 12px; border-top: 1px solid var(--border); }
.pm-foot .ms { font-size: 13px; }
.pm-edit { font-size: 11px; padding: 1px 8px; margin-left: 6px; }
.modal-foot button.danger { color: var(--danger); border-color: var(--danger); }
/* The per-tenant formula editor (public/formula-editor.js). */
.formula-card { width: min(760px, calc(100vw - 32px)); }
.fx-label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.4px; margin: 12px 0 6px; }
.fx-label .muted { text-transform: none; letter-spacing: 0; }
.fx-input { width: 100%; font-family: var(--font-mono); font-size: 13px; padding: 8px 10px; }
.fx-error { color: var(--danger); font-size: 12px; min-height: 16px; margin-top: 4px; }
.fx-readonly { background: var(--panel-2); color: var(--text); cursor: text; }
.fx-hint { font-size: 11px; color: var(--muted); margin-top: 4px; }
.fx-match { font-size: 12px; margin-top: 4px; min-height: 16px; display: flex; align-items: center; gap: 5px; color: var(--muted); }
.fx-match .ms { font-size: 14px; }
.fx-match.ok { color: var(--ok); }
.fx-match.warn { color: var(--warn); }
.fx-match code { font-family: var(--font-mono); }
.fx-vars { display: flex; flex-wrap: wrap; gap: 6px; }
.fx-var { display: inline-flex; align-items: center; gap: 6px; padding: 3px 8px; border-radius: 999px; font-size: 11px; cursor: pointer; }
.fx-var code { font-family: var(--font-mono); font-size: 11px; }
.fx-sample { color: var(--muted); max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fx-preview { border: 1px solid var(--border); border-radius: 8px; max-height: 280px; overflow: auto; }
.fx-row { display: grid; grid-template-columns: minmax(0, 180px) minmax(0, 1fr); gap: 12px; padding: 6px 10px; border-top: 1px solid var(--border); font-size: 12px; }
.fx-row:first-child { border-top: 0; }
.fx-row.source { background: var(--panel-2); }
.fx-row.none .fx-value { color: var(--danger); }
.fx-value { font-family: var(--font-mono); overflow-wrap: anywhere; display: inline-flex; align-items: center; gap: 4px; }
.fx-value .ms { font-size: 14px; }
.fx-tag { font-size: 10px; color: var(--muted); border: 1px solid var(--border); border-radius: 999px; padding: 0 5px; }
.fx-sep { font-size: 11px; color: var(--muted); padding: 6px 10px; border-top: 1px solid var(--border); background: var(--panel-2); }
.fx-note { font-size: 12px; padding: 6px 10px; border-top: 1px solid var(--border); display: flex; gap: 6px; align-items: center; }
.fx-note.warn { color: var(--warn); }
.fx-note code { font-family: var(--font-mono); }
/* A credential's value arrives masked (src/secrets.js); the lock says why. */
.tree-asset .val.masked, .mx-prop.masked { display: inline-flex; align-items: center; gap: 3px; cursor: help; }
.tree-asset .val.masked .ms, .mx-prop.masked .ms, .dw-lv-value .ms { font-size: 12px; color: var(--muted); }
.dw-note { font-size: 11px; line-height: 1.45; color: var(--muted); padding: 2px 0 4px; }
/* A promote's drift check (public/drift.js): the verdict replaces "updated",
   and a spinner holds its place until it lands. */
.mx-drift-wait { display: inline-flex; color: var(--muted); }
.mx-drift-wait .ms { font-size: 14px; animation: spin 1.2s linear infinite; }
.dw-drift { font-size: 11px; color: var(--muted); }
.tree-loading { font-style: italic; color: var(--muted); padding: 4px 8px; font-size: 11px; }

/* Step 4: matched-folder line + status filter chips. */
.matched-line {
  padding: 8px 14px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
  border-bottom: 1px solid var(--border);
}
.matched-line .none { color: var(--warn); }
.review-manifest {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--inset);
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
}
.review-manifest .rm-head { font-size: 9px; text-transform: uppercase; letter-spacing: 0.5px; opacity: 0.7; }
.review-manifest .rm-cell { display: inline-flex; align-items: center; gap: 4px; }
.review-manifest .rm-count { color: var(--accent); font-weight: 600; }
.review-manifest .rm-label { color: var(--muted); }
.review-manifest .rm-total { margin-left: auto; color: var(--accent); font-weight: 600; }
.review-manifest.empty-manifest { color: var(--muted); font-style: italic; }
.type-filter.status-filter .tf-btn[data-status="new"].active       { color: var(--ok);       border-color: var(--ok-border); background: var(--ok-bg); }
.type-filter.status-filter .tf-btn[data-status="changed"].active   { color: var(--warn);     border-color: var(--warn-border); background: var(--warn-bg); }
.type-filter.status-filter .tf-btn[data-status="identical"].active { color: var(--text); border-color: var(--border-hover); background: var(--neutral-bg); }

/* Status tag (right side of step 4 asset rows) */
.status-tag {
  display: inline-block;
  margin-left: auto;
  padding: 1px 8px;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  border: 1px solid var(--border);
  color: var(--muted);
}
.status-tag.new       { color: var(--ok);       border-color: var(--ok-border); background: var(--ok-bg); }
.status-tag.changed   { color: var(--warn);     border-color: var(--warn-border); background: var(--warn-bg); }
.status-tag.identical { color: var(--neutral); border-color: var(--neutral-border); background: var(--neutral-bg); }

.review-tree { padding: 8px 0; overflow: auto; flex: 1; }
.review-tree .tree-asset .diff-link {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--muted);
  margin-left: 6px;
  cursor: pointer;
  padding: 1px 6px;
  border: 1px solid var(--border);
  border-radius: 4px;
}
.review-tree .tree-asset .diff-link:hover { color: var(--accent); border-color: var(--accent); }
.review-tree .tree-asset.identical { opacity: 0.65; }
.review-tree .tree-asset.identical .name { color: var(--muted); }
.review-tree .tree-asset.identical input[type=checkbox] { cursor: not-allowed; }

/* Inline property-value display: current → incoming on `changed`, single
   value otherwise. Truncates with ellipsis; full value is in the tooltip. */
.review-tree .tree-asset .prop-display {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: var(--font-mono);
  font-size: 10px;
  margin-left: auto;
  max-width: 360px;
  min-width: 0;
  overflow: hidden;
  color: var(--muted);
}
.review-tree .tree-asset .prop-display.diff {
  /* changed: dest (old) → src (new) */
  color: var(--warn);
}
.review-tree .tree-asset .prop-display.diff .prop-val:first-child {
  color: var(--danger);
  text-decoration: line-through;
  opacity: 0.85;
}
.review-tree .tree-asset .prop-display.diff .prop-val:last-child {
  color: var(--ok);
}
.review-tree .tree-asset .prop-display.new .prop-val { color: var(--ok); }
.review-tree .tree-asset .prop-display.identical .prop-val { color: var(--muted); }
.review-tree .tree-asset .prop-display .prop-val {
  max-width: 160px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.review-tree .tree-asset .prop-display .prop-arrow {
  color: var(--muted);
  font-size: 11px;
}
.review-tree .tree-asset .prop-display .prop-empty {
  color: var(--muted);
  font-style: italic;
  opacity: 0.55;
}
/* When a prop-display is present, the status tag still sits at the right;
   `margin-left:auto` on prop-display already pushes it next to status-tag. */

/* ---- Destination tiles (right) ---- */
.dest {
  padding: 12px 14px 12px 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--panel);
  position: relative;
  overflow: hidden;
  cursor: pointer;
  transition: border-color 0.12s;
}
.dest::before {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 4px;
  background: transparent;
}
.dest:hover { border-color: var(--border-hover); }
.dest.checked {
  border-color: var(--accent-solid);
  box-shadow: 0 0 0 1px var(--accent-solid) inset;
}
.dest.checked::before { background: var(--accent-solid); }
.dest .head {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  cursor: pointer;
}
.dest input[type=checkbox] {
  margin: 3px 0 0;
  accent-color: var(--accent);
  cursor: pointer;
}
.dest .body { flex: 1; min-width: 0; }
.dest .name {
  font-weight: 600;
  font-size: 13px;
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.dest .meta {
  color: var(--muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  margin-top: 2px;
}

/* ---- Buttons ---- */
button {
  background: var(--panel-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 6px 14px;
  font: inherit;
  cursor: pointer;
  transition: border-color 0.12s, background 0.12s;
}
button:hover:not(:disabled) { border-color: var(--accent); }
button:disabled { opacity: 0.4; cursor: not-allowed; }
/* .primary and .ghost keep their names because they're emitted from JS
   template strings all over the app; only their looks move to the DS's
   primary-blue and secondary-black variants. */
button.primary {
  background: var(--accent-solid);
  color: var(--on-accent);
  border-color: transparent;
  font-weight: 500;
  border-radius: var(--radius-md);
  padding: 6px 12px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
button.primary:hover:not(:disabled) { filter: brightness(1.08); }
button.ghost {
  padding: 5px 10px;
  font-size: 12px;
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
button.ghost:hover:not(:disabled) { color: var(--text); border-color: var(--border-hover); background: var(--hover); }

/* ---- Action bar ---- */
.action-bar {
  padding: 0 16px;
  height: 60px;
  background: var(--panel);
  border-top: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 14px;
}
.action-bar .spacer { flex: 1; }
.action-facts { display: flex; align-items: center; gap: 12px; min-width: 0; }
/* The live-workspace call-out sits next to the button that would write to
   them, not on a screen you already clicked past. */
.live-callout {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 12px;
  background: var(--color-ink);
  border-radius: var(--radius-md);
  color: #FFFFFF;
  flex: 0 0 auto;
  white-space: nowrap;
  font-size: 12px;
}
.live-callout .ms { color: var(--color-warning); font-size: 18px; }
.action-summary {
  font-size: 12px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.muted { color: var(--muted); font-size: 12px; }
.spacer { flex: 1; }

.error-banner {
  margin: 12px 20px;
  padding: 10px 14px;
  background: var(--danger-bg);
  border: 1px solid var(--danger-border);
  border-radius: 8px;
  color: var(--danger);
  font-size: 12px;
}
/* Shares the stage's single grid cell with the views, so it overlays their
   top edge instead of claiming a row of its own. */
.stage > .error-banner {
  grid-area: 1 / 1;
  align-self: start;
  justify-self: center;
  z-index: 20;
  max-width: min(720px, calc(100% - 40px));
  box-shadow: 0 6px 20px rgba(0, 0, 0, .12);
}

/* ---- Monaco diff modal ---- */
.modal {
  position: fixed;
  inset: 0;
  background: var(--backdrop);
  display: flex;
  align-items: stretch;
  justify-content: stretch;
  z-index: 100;
  padding: 24px;
}
.modal.hidden { display: none; }
.modal-card {
  flex: 1;
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  min-height: 0;
}
.modal-head {
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--panel-2);
  display: flex;
  align-items: center;
  gap: 12px;
}
.modal-title { display: flex; align-items: baseline; gap: 10px; font-weight: 600; flex: 1; min-width: 0; }
.diff-nav {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-right: 10px;
}
.diff-nav .nav-btn {
  padding: 3px 10px;
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
}
.diff-nav .nav-counter {
  font-family: var(--font-mono);
  font-size: 11px;
  min-width: 60px;
  text-align: center;
}
.modal-title .muted { font-weight: 400; font-family: var(--font-mono); font-size: 12px; }
.modal-body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 12px 14px 14px;
  gap: 10px;
}
.meta-row {
  display: grid;
  /* minmax(0, …): a recipe's link is one long unbreakable string, and a plain
     1fr track would grow to fit it instead of letting it ellipsise. */
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  gap: 10px;
  align-items: stretch;
}
.meta-card {
  min-width: 0;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
}
.meta-card .lbl { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
.meta-card .val { margin-top: 4px; font-family: var(--font-mono); font-size: 12px; }
.meta-card.src { border-color: var(--ok-border); }
.meta-card.dst { border-color: var(--accent-border); }
/* Each recipe's Workato link: open, read (one click selects the whole address),
   or copy it. */
.meta-link { display: flex; align-items: center; gap: 6px; margin-top: 6px; min-width: 0; }
.meta-open { display: inline-flex; color: var(--accent); }
.meta-open .ms { font-size: 15px; }
.meta-url {
  flex: 1 1 auto; min-width: 0;
  font-family: var(--font-mono); font-size: 11px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  user-select: all; cursor: text;
}
.meta-copy { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 3px; font-size: 11px; padding: 2px 8px; }
.meta-copy .ms { font-size: 13px; }
.meta-arrow { display: flex; align-items: center; justify-content: center; color: var(--muted); font-size: 20px; }
.monaco-mount {
  flex: 1;
  min-height: 300px;
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  /* Monaco paints its own canvas; this only shows in the gap before it
     mounts, so it tracks the theme Monaco is about to use. */
  background: var(--panel-2);
}
.modal-body .err { color: var(--danger); font-family: var(--font-mono); font-size: 12px; }

.mask-badge {
  font-family: var(--font-mono);
  font-size: 11px;
  padding: 6px 10px;
  border-radius: 6px;
  background: var(--ok-bg);
  border: 1px solid var(--ok-border);
  color: var(--ok);
}
.mask-badge.has-unfired {
  background: var(--warn-bg);
  border-color: var(--warn-border);
  color: var(--warn);
}
.mask-badge .mb-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.mask-badge .mb-warn {
  color: var(--warn);
  font-size: 11px;
}
.mask-badge .mb-toggle {
  margin-left: auto;
  cursor: pointer;
  user-select: none;
  font-size: 10px;
  color: var(--muted);
  padding: 1px 6px;
  border: 1px solid var(--border);
  border-radius: 4px;
}
.mask-badge .mb-toggle:hover { color: var(--text); border-color: var(--accent); }
.mask-badge .mb-body { margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--border); }
.mask-badge .mb-body.hidden { display: none; }
.mask-badge .mb-section {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--muted);
  margin: 4px 0 2px;
}
.mask-badge .rule-row {
  display: grid;
  grid-template-columns: 70px 1.4fr 1.2fr 130px;
  gap: 8px;
  font-size: 10px;
  padding: 2px 0;
  align-items: baseline;
}
.mask-badge .rule-row.unfired {
  grid-template-columns: 70px 1.4fr 1.2fr 130px;
}
.mask-badge .rule-row .rule-reason {
  grid-column: 2 / -1;
  font-size: 10px;
  color: var(--warn);
  padding-left: 4px;
  border-left: 2px solid var(--warn-border);
  margin-top: 1px;
}
.mask-badge .conn-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-top: 4px;
}
.mask-badge .conn-head {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--muted);
  margin-bottom: 3px;
}
.mask-badge .conn-row {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: 10px;
  padding: 1px 0;
}
.mask-badge .conn-row .conn-name { color: var(--text); }
.mask-badge .conn-row .conn-prov { color: var(--muted); opacity: 0.8; }
.mask-badge .conn-empty { font-size: 10px; color: var(--muted); }
.mask-badge .rule-row .rule-action {
  font-size: 9px;
  text-transform: uppercase;
  color: var(--muted);
}
.mask-badge .rule-row.unfired .rule-action { color: var(--warn); }
.mask-badge .rule-row.fired .rule-action { color: var(--ok); }
.mask-badge .rule-row .rule-path { color: var(--text); }
.mask-badge .rule-row .rule-where { color: var(--muted); }
.mask-badge .rule-row .rule-counts { color: var(--muted); text-align: right; }
.mask-badge .rule-row.unfired .rule-counts { color: var(--warn); opacity: 0.8; }


/* ============================================================
   Wizard step 4 — review tabs (one per destination)
   ============================================================ */
.review-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  padding: 0 8px;
  border-bottom: 1px solid var(--border);
  background: var(--panel-2);
}
.review-tab {
  background: transparent;
  color: var(--muted);
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  padding: 8px 14px;
  font: inherit;
  font-size: 12px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.review-tab:hover { color: var(--text); background: var(--hover); }
.review-tab.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}
.review-tab .tab-badge {
  font-family: var(--font-mono);
  font-size: 10px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--panel);
  color: var(--muted);
}
.review-tab .tab-badge.computing { color: var(--warn); }
.review-tab .tab-badge.err { color: var(--danger); }
.review-tab .tab-badge .new { color: var(--ok); }
.review-tab .tab-badge .changed { color: var(--warn); }
.review-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  padding: 12px 16px;
}
/* Footer back button */
.action-bar button:disabled { opacity: 0.4; cursor: not-allowed; }
.action-bar button.hidden { display: none; }

/* ---------------------------------------------------------------------------
   Manage-tenants modal — shared, server-side wizard visibility settings.
   Reuses .modal / .modal-card / .modal-head from the diff modal.
   --------------------------------------------------------------------------- */

/* The diff modal fills the viewport (Monaco wants the room); a tenant list
   reads better as a centered card that doesn't stretch to 100% width. */

.manage-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--panel-2);
}
.manage-toolbar .spacer { flex: 1; }
.manage-toolbar input[type='search'] { min-width: 220px; }
.manage-toolbar .muted { font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; }

/* .modal-body is a flex column with no overflow — correct for the diff modal
   (Monaco scrolls itself), wrong for a long list. Reset to a plain scrolling
   block so the tenant rows can actually be reached. */
.manage-body {
  display: block;
  padding: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.manage-head-row,
.manage-row {
  display: grid;
  grid-template-columns: 1fr 110px 110px 110px;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  border-bottom: 1px solid var(--border);
}
.manage-head-row {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--panel);
  color: var(--muted);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-align: center;
}
.manage-head-row .col-name { text-align: left; }
.manage-head-row .muted { text-transform: none; letter-spacing: 0; font-size: 10px; }

.manage-row:hover { background: var(--panel-2); }
.manage-row .col-name {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  min-width: 0;
}
.manage-row .name { font-weight: 600; font-size: 13px; }
.manage-row .meta { color: var(--muted); font-size: 11px; font-family: var(--font-mono); }
.manage-row .col-flag { text-align: center; }
.manage-row input[type='checkbox'],
.manage-row input[type='radio'] { cursor: pointer; }
.manage-row input[type='radio']:disabled { cursor: not-allowed; opacity: 0.3; }
/* Hidden from both pickers — dim it so the excluded set reads at a glance. */
.manage-row.all-hidden .col-name { opacity: 0.45; }

.manage-foot { padding: 10px 16px; font-size: 12px; }
#manageStatus.ok  { color: var(--ok); }
#manageStatus.err { color: var(--danger); }

/* ---------------------------------------------------------------------------
   Top-level views (hash-routed): home / promote / audit
   Each view owns its internal row layout; the body only lays out header + view.
   --------------------------------------------------------------------------- */
.view { display: none; min-height: 0; }
.view.active { display: grid; min-height: 0; }
#view-wizard.active  { grid-template-rows: minmax(0, 1fr); }

.audit-panel {
  display: flex;
  flex-direction: column;
  min-height: 0;
  border-right: 1px solid var(--border);
}
.audit-panel:last-child { border-right: none; }
.panel-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--panel);
}
.panel-head .spacer { flex: 1; }
.panel-head h2 { margin: 0; font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); }
.panel-sub { padding: 8px 14px; border-bottom: 1px solid var(--border); }
.panel-sub input[type='search'] {
  width: 100%;
  background: var(--panel-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 5px 8px;
  font: inherit;
  font-size: 12px;
}
.panel-list { flex: 1; min-height: 0; overflow: auto; padding: 6px; }

.pick-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 13px;
}
.pick-row:hover { background: var(--panel-2); }
.pick-row input { cursor: pointer; accent-color: var(--accent); }
.pick-row .pick-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.tally-chip {
  font-size: 11px;
  padding: 2px 9px;
  border-radius: 999px;
  margin-right: 4px;
  border: 1px solid var(--border);
  background: var(--panel-2);
}
.tally-chip.off { opacity: 0.35; }
.tally-chip.current { color: var(--ok); border-color: var(--ok-border); }
.tally-chip.behind { color: var(--warn); border-color: var(--warn-border); }
.tally-chip.forked { color: var(--danger); border-color: var(--danger-border); }

.audit-recipe { border-bottom: 1px solid var(--border); padding: 8px 6px 10px; }
.audit-recipe-head { display: flex; align-items: baseline; gap: 8px; padding: 0 6px 6px; }
.audit-recipe-head .spacer { flex: 1; }
.audit-recipe-head .name { font-weight: 600; font-size: 13px; }
.audit-recipe-head .muted { font-size: 11px; font-family: var(--font-mono); }
.deep-btn { font-size: 11px; padding: 3px 10px; }
.err-inline { color: var(--danger); font-size: 11px; }

/* Outcome of step 4's explicit "build source package" action. Its own
   full-width row rather than a header cell: Workato's export errors are long
   sentences and would otherwise reflow the whole header. Collapses when
   empty, so the row only exists once there's something to report. */
.manifest-sync-line {
  margin: 0 0 8px;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--panel-2);
  color: var(--muted);
  font-size: 12px;
  line-height: 1.45;
}
.manifest-sync-line:empty { display: none; }
.manifest-sync-line.err-inline {
  font-size: 12px;
  border-color: var(--danger);
  color: var(--danger);
}

.audit-dest {
  display: grid;
  grid-template-columns: 84px minmax(120px, 1fr) 46px minmax(180px, 2fr);
  gap: 8px;
  align-items: center;
  padding: 4px 6px;
  font-size: 12px;
  border-radius: 6px;
}
.audit-dest:hover { background: var(--panel-2); }
.audit-dest .dest-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.audit-dest .dest-ver { color: var(--muted); font-family: var(--font-mono); font-size: 11px; }
.audit-dest .dest-detail { color: var(--muted); font-size: 11px; }
.audit-dest.forked .dest-detail { color: var(--text); }

.status-pill {
  font-size: 10px;
  text-align: center;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.status-pill.current    { color: var(--ok);      border-color: var(--ok-border); background: var(--ok-bg); }
.status-pill.behind     { color: var(--warn);    border-color: var(--warn-border); background: var(--warn-bg); }
.status-pill.forked     { color: var(--danger);  border-color: var(--danger-border); background: var(--danger-bg); }
/* `missing` and `unverified` must never collapse into each other: one says
   "it isn't there", the other says "we could not tell". Both were
   `color: var(--muted)` and nothing else — literally identical rules. Neither
   is an error, so neither gets a semantic hue; they're separated by weight
   instead. Solid neutral = a fact. Dashed outline = an absence of knowledge. */
.status-pill.missing {
  color: var(--text);
  background: var(--neutral-bg);
  border-color: var(--neutral-border);
}
.status-pill.unverified {
  color: var(--muted);
  background: transparent;
  border-style: dashed;
  border-color: var(--border-hover);
}
.tally-chip.missing,
.fold-count.missing { color: var(--text); border-color: var(--neutral-border); }
.tally-chip.unverified,
.fold-count.unverified { color: var(--muted); border-style: dashed; border-color: var(--border-hover); }

/* Icon-only header buttons (expand/collapse all). */
.icon-btn {
  padding: 2px 7px;
  font-size: 13px;
  line-height: 1.2;
}
.head-sep {
  width: 1px;
  height: 14px;
  background: var(--border);
  display: inline-block;
  margin: 0 2px;
}

/* Result folder rows: no checkbox, whole row toggles expand, and each folder
   summarises the statuses beneath it so drift is visible while collapsed. */
.result-folder { cursor: pointer; }
.result-folder .name { flex: 0 1 auto; }
.fold-counts { display: inline-flex; gap: 6px; margin-left: 10px; flex-wrap: wrap; }
.fold-count {
  font-size: 10px;
  padding: 1px 7px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--muted);
  white-space: nowrap;
}
.fold-count.current    { color: var(--ok);     border-color: var(--ok-border); }
.fold-count.behind     { color: var(--warn);   border-color: var(--warn-border); }
.fold-count.forked     { color: var(--danger); border-color: var(--danger-border); }

/* Result rows that link out to Workato. The row is already a grid; making it
   an <a> must not change its geometry or inherit link colours. */
a.audit-dest {
  color: inherit;
  text-decoration: none;
}
a.audit-dest.linked:hover {
  background: var(--panel-2);
  outline: 1px solid var(--border);
}
a.audit-dest.linked:hover .dest-name { color: var(--accent); }
.audit-recipe-head a.name.linked {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px dotted var(--border);
}
.audit-recipe-head a.name.linked:hover { color: var(--accent); border-bottom-color: var(--accent); }

/* ---- Tool switcher (header) ----------------------------------------------
   Present in every view so promote ⇄ audit ⇄ home is always one click. The
   active item comes from body[data-route], stamped by the router, so there's
   no second source of truth to keep in sync with the URL. */
.tool-nav {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  flex: 0 0 auto;
}
.tool-nav a {
  padding: 4px 12px;
  border-radius: 999px;
  font-size: 12px;
  color: var(--muted);
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.12s, color 0.12s;
}
.tool-nav a:hover { color: var(--text); background: var(--hover); }
body[data-route='home']    .tool-nav a[data-route='home'],
body[data-route='promote'] .tool-nav a[data-route='promote'],
body[data-route='audit']   .tool-nav a[data-route='audit'] {
  background: var(--accent-solid);
  color: var(--on-accent);
  font-weight: 600;
}
/* The strapline is the first thing that should give up room. */
header .sub { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 900px) { header .sub { display: none; } }

/* ---------------------------------------------------------------------------
   Promote pre-flight / applying / results modal
   --------------------------------------------------------------------------- */
.promote-body { display: block; overflow-y: auto; padding: 0; }
.modal-foot {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
  border-top: 1px solid var(--border);
  background: var(--panel-2);
}
.modal-foot .spacer { flex: 1; }
.pf-wrap { padding: 14px 16px 18px; }

.pf-summary {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px 14px;
  background: var(--panel-2);
  margin-bottom: 14px;
}
.pf-headline { font-size: 14px; margin-bottom: 8px; }
.pf-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.pf-chip {
  font-size: 11px;
  padding: 2px 9px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--muted);
  white-space: nowrap;
}
.pf-facts { margin: 0; padding-left: 18px; font-size: 12px; color: var(--muted); }
.pf-facts li { margin: 3px 0; }
.pf-facts li.warn { color: var(--warn); }
.pf-facts li.err  { color: var(--danger); }
.pf-banner {
  padding: 10px 12px;
  border-radius: 8px;
  margin-bottom: 12px;
  font-size: 12px;
}
.pf-banner.err { color: var(--danger); border: 1px solid var(--danger-border); background: var(--danger-bg); }

.pf-dest {
  border: 1px solid var(--border);
  border-radius: 10px;
  margin-bottom: 10px;
  overflow: hidden;
}
.pf-dest.failed { border-color: var(--danger-border); }
.pf-dest-head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 8px 12px;
  background: var(--panel-2);
  border-bottom: 1px solid var(--border);
}
.pf-dest-head .name { font-weight: 600; font-size: 13px; }
.pf-tag {
  font-size: 10px;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--muted);
}
.pf-tag.ok   { color: var(--ok);     border-color: var(--ok-border);     background: var(--ok-bg); }
.pf-tag.warn { color: var(--warn);   border-color: var(--warn-border);   background: var(--warn-bg); }
.pf-tag.err  { color: var(--danger); border-color: var(--danger-border); background: var(--danger-bg); }
.pf-dest-body { padding: 8px 12px; }
.pf-line { font-size: 12px; padding: 3px 0; display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }
.pf-line .lbl {
  min-width: 78px;
  color: var(--muted);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.pf-line.indent { padding-left: 86px; }
.pf-line.warn { color: var(--warn); }
.pf-line .err { color: var(--danger); }
.pf-skipped { font-size: 12px; margin-top: 4px; }
.pf-skipped summary { cursor: pointer; color: var(--muted); }

.pf-applying { display: flex; align-items: center; gap: 14px; padding: 24px 8px; }
.spinner {
  width: 22px; height: 22px;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 0.9s linear infinite;
  flex: 0 0 auto;
}
@keyframes spin { to { transform: rotate(360deg); } }

.pr-recipe {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 6px;
  font-size: 12px;
  border-radius: 6px;
  flex-wrap: wrap;
}
.pr-recipe:hover { background: var(--panel-2); }
.pr-recipe.restart-failed { background: var(--danger-bg); }
.pr-name { font-weight: 500; min-width: 160px; }
.pr-note { font-size: 11px; }
.pr-note.ok { color: var(--ok); }
.pr-note.err { color: var(--danger); }
.run-pill {
  font-size: 10px;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.run-pill.running { color: var(--ok);   border-color: var(--ok-border); background: var(--ok-bg); }
.run-pill.stopped { color: var(--muted); }
.start-btn { font-size: 11px; padding: 2px 10px; }

/* ---- Logs view (historical promotion results, read-only) ---- */
body[data-route='logs'] .tool-nav a[data-route='logs'] {
  background: var(--accent-solid);
  color: var(--on-accent);
  font-weight: 600;
}
#view-logs.active { grid-template-rows: auto 1fr; }

.logs-body {
  display: grid;
  grid-template-columns: 340px minmax(0, 1fr);
  min-height: 0;
}
.log-row {
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  margin-bottom: 6px;
  background: var(--panel);
  cursor: pointer;
  transition: border-color 0.12s;
}
.log-row:hover { border-color: var(--border-hover); }
.log-row.active {
  border-color: var(--accent-solid);
  background: var(--accent-bg);
}
.log-when { font-size: 13px; font-weight: 500; }
.log-what { font-size: 12px; color: var(--muted); margin-top: 3px; text-wrap: pretty; }
.log-health { margin-top: 7px; }
.logs-body .panel-list { padding: 8px; }

/* ---- Per-recipe tenant chips on the results screens ---- */
.pf-section-title {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
  margin: 14px 0 8px;
}
.pf-wrap .pf-section-title:first-child { margin-top: 0; }
.pr-head { display: flex; align-items: center; gap: 8px; }
.pr-chips { display: flex; flex-wrap: wrap; gap: 6px; padding: 6px 0 2px 24px; width: 100%; }
.ten-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 11px;
  background: var(--panel-2);
}
.ten-chip .tc-name { font-weight: 600; }
.ten-chip .tc-result { color: var(--muted); }
.ten-chip.running { border-color: var(--ok-border); }
.ten-chip.failed  { border-color: var(--danger-border); background: var(--danger-bg); }
.ten-chip .tc-err, .ten-chip .tc-err-slot { color: var(--danger); }
.ten-chip .tc-err-slot:empty { display: none; }
.chip-act { font-size: 10px; padding: 2px 9px; }

/* ---- Environment badge --------------------------------------------------
   Which environment a tenant belongs to, from the QuickBase admin table.
   Production is called out because this tool writes to live customer
   workspaces and the pickers previously gave no clue which ones were live. */
.env-badge {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 1px 6px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--muted);
  white-space: nowrap;
}
/* Production is the brand orange, never the danger red: red means "something
   is wrong", and a live customer workspace is not wrong — it is the point. */
.env-badge.prod {
  color: var(--brand-text);
  border-color: var(--brand-border);
  background: var(--brand-bg);
  font-weight: 700;
}
.env-badge.compact { padding: 0 5px; font-size: 9px; letter-spacing: 0.04em; }
.env-badge.staging { color: var(--accent); border-color: var(--accent-border); }
.env-badge.other   { color: var(--muted); }
.env-badge.unknown { color: var(--warn); border-color: var(--warn-border); }

/* The pre-flight's production call-out. This is the last screen before writes
   land, so it reads as a warning rather than another fact in the list. */
.pf-prod-warning {
  margin: 0 0 14px;
  padding: 11px 14px;
  border: 1px solid var(--danger-border);
  border-left: 3px solid var(--danger);
  border-radius: var(--radius-lg);
  background: var(--danger-bg);
  color: var(--text);
  display: flex;
  align-items: flex-start;
  gap: 10px;
}
.pf-prod-warning .ms { color: var(--danger); font-size: 20px; flex: 0 0 auto; }
.pf-prod-warning strong { color: var(--danger); }

/* ---- Step 4: merged review (asset header + one row per destination) ------
   Replaces the per-destination tab layout. Destination rows use FIXED grid
   columns and a fixed left indent — deliberately independent of folder depth —
   so every status lands in the same column across the whole tree. Scanning
   that column is how you answer "where is this different?" without reading
   names. */
.dest-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 6px 12px;
  border-bottom: 1px solid var(--border);
  background: var(--panel);
  font-size: 12px;
}
.dest-summary .spacer { flex: 1; }
.dest-summary .ds-name { font-weight: 600; }
.dest-summary .ds-counts { color: var(--muted); font-size: 11px; }
.dest-summary .ds-counts .new { color: var(--ok); }
.dest-summary .ds-counts .changed { color: var(--warn); }
.dest-summary .ds-bulk { font-size: 10px; padding: 2px 8px; }

.merged-asset-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px 3px;
  font-size: 13px;
  font-weight: 500;
}
.merged-asset-head .icon { font-size: 14px; line-height: 1; }
/* A real checkbox, hidden — matches the width of the folder rows' real ones so
   asset headers line up one indent step right of their parent folder. */
.merged-asset-head .cb-ghost { visibility: hidden; pointer-events: none; margin: 0; }
.merged-asset-head .ma-summary { margin-left: auto; font-size: 10px; color: var(--muted); }
.merged-asset-head .mas.new { color: var(--ok); }
.merged-asset-head .mas.changed { color: var(--warn); }
.merged-asset-head .mas.identical { color: var(--muted); }
.merged-asset-head .mas-sep { color: var(--border); margin: 0 4px; }

.ma-dest-row {
  display: grid;
  grid-template-columns: 20px 180px 86px minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  /* One indent for every destination row, so the columns align across the whole
     tree; the value is measured at render time from the deepest asset icon
     (see alignDestRows) so these always sit right of their asset header. */
  padding: 2px 12px;
  padding-left: var(--dest-indent, 96px);
  font-size: 12px;
  border-radius: 4px;
}
.ma-dest-row:hover { background: var(--hover); }
.ma-dest-row .mdr-cb { display: flex; align-items: center; justify-content: center; }
.ma-dest-row input[type=checkbox] { margin: 0; accent-color: var(--accent); cursor: pointer; }
.ma-dest-row .ma-noop { color: var(--muted); }
.ma-dest-row .mdr-tenant {
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The status column is fixed-width and left-aligned so the tags stack into a
   readable column rather than floating to wherever the text ends. */
.ma-dest-row .mdr-status { justify-self: start; }
.ma-dest-row .mdr-status .status-tag { margin-left: 0; }
.ma-dest-row .mdr-value {
  color: var(--muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.ma-dest-row .mdr-value .prop-val { color: var(--muted); }
.ma-dest-row .mdr-diff .diff-link { margin-left: 0; }
/* Excluded for this destination: still legible — you need to see what you
   turned off — but visibly not shipping. */
.ma-dest-row.excluded { opacity: 0.45; }
.ma-dest-row.excluded .mdr-tenant { text-decoration: line-through; }

/* The run button doubles as cancel mid-audit. */
/* Audit's step-4 footer button doubles as cancel while a run is in flight. */
#continueBtn.is-cancel {
  background: var(--panel-2);
  color: var(--warn);
  border-color: var(--warn-border);
}


/* ===========================================================================
   THE MATRIX — step 4, both modes.

   Assets are rows and destinations are COLUMNS. The previous design gave each
   asset a stack of per-destination sub-rows, which answered "what happens to
   this asset" but made "which destination is the odd one out" a reading
   exercise. In a grid that question is a glance down a column.

   The asset column is sticky and the destination columns scroll horizontally,
   so the layout holds at 3 destinations or at 13 — the shape never changes
   under you, you just scroll further.
   =========================================================================== */

.mx-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
  background: var(--panel);
}
.mx-toolbar .spacer { flex: 1; }
.mx-toolbar input[type='search'] {
  width: 236px;
  flex: 0 0 auto;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text);
  padding: 6px 10px;
  font: inherit;
  font-size: 13px;
  outline: none;
}
.mx-toolbar input[type='search']:focus { border-color: var(--accent-solid); }
.tb-sep { width: 1px; height: 20px; background: var(--border); flex: 0 0 auto; }
.mx-pkg { font-size: 12px; color: var(--muted); }
.mx-pkg.err-inline { color: var(--danger); }
.mx-stats { font: 400 11px/1 var(--font-mono); color: var(--muted); }
.mx-tally { display: inline-flex; gap: 6px; flex-wrap: wrap; align-items: center; }

/* Segmented status filter. */
.seg { display: inline-flex; gap: 4px; }
.seg button {
  padding: 5px 10px;
  font-size: 12px;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.seg button:hover:not(:disabled) { background: var(--hover); color: var(--text); }
.seg button.active { background: var(--accent-bg); color: var(--accent); border-color: var(--accent-border); font-weight: 500; }
.seg button .n { font-weight: 600; }

.mx-scroll { flex: 1; min-height: 0; overflow: auto; background: var(--panel); }

/* The grid must be sized to its CONTENT, not to the scroller. Without this the
   fixed 176px destination tracks just overflow invisibly: the scroll container
   never grows, so at 13 destinations there was nothing to scroll and the far
   columns were simply unreachable. min-width keeps it filling the pane when
   there are only two or three. */
.mx {
  width: max-content;
  min-width: 100%;
}
.mx-row {
  display: grid;
  grid-template-columns: var(--mx-cols);
  --row-bg: var(--panel);
}
/* Zebra striping is what keeps a wide row readable across a horizontal scroll:
   with the name column stuck to the left, the stripe is the only thing tying a
   far-right cell back to its asset. */
.mx-row:nth-of-type(even) { --row-bg: var(--panel-2); }
.mx-row.mx-folder { --row-bg: var(--border); }
.mx-row.mx-asset-row:hover { --row-bg: var(--accent-bg); }
.mx-row.is-open { --row-bg: var(--accent-bg); }
.mx-row.is-open .mx-name-c { box-shadow: inset 3px 0 0 var(--accent-solid); }

.mx-c {
  background: var(--row-bg);
  border-left: 1px solid var(--border-subtle);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-width: 0;
  padding: 6px 10px;
}
.mx-c:first-child { border-left: none; }
/* The name column outranks the scrolling cells; the header outranks both, and
   the corner (sticky in both directions) outranks everything. */
.mx-name-c {
  position: sticky;
  left: 0;
  z-index: 2;
  justify-content: flex-start;
  padding: 7px 16px;
}
.mx-head { position: sticky; top: 0; z-index: 3; }
.mx-head .mx-name-c { z-index: 4; }
.mx-head .mx-c { border-bottom: 1px solid var(--border); align-items: flex-end; }

.mx-th {
  font: 500 11px/1 var(--font-sans);
  letter-spacing: 0.7px;
  text-transform: uppercase;
  color: var(--muted);
}

/* Destination column header: who it is, whether it is live, and its totals. */
.mx-dh {
  flex-direction: column;
  align-items: stretch !important;
  gap: 4px;
  padding: 8px 12px;
  /* A live customer workspace gets a heavier cap than a sandbox, so the
     column itself carries the warning rather than only a tag inside it. */
  border-top: 3px solid var(--border);
}
.mx-dh.is-live { border-top-color: var(--color-charcoal); }
.mx-dh-top { display: flex; align-items: center; gap: 6px; min-width: 0; }
.mx-dh-name { font-size: 12px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Wraps: "N second copies · N name clash · 1 to rename+move" is wider than a
   176px track, and a count that runs under the next column says nothing. */
.mx-dh-counts { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 5px; font-size: 11px; min-width: 0; }
.mx-dh-counts > .ok, .mx-dh-counts > .warn, .mx-dh-counts > .sub { white-space: nowrap; }
.mx-dh-counts .ok { color: var(--ok); font-weight: 500; }
.mx-dh-counts .warn { color: var(--warn); font-weight: 500; }
.mx-dh-counts .sub { color: var(--muted); }
.mx-dh-counts .sep { color: var(--muted); opacity: 0.5; }
.mx-dh-counts .danger { color: var(--danger); font-weight: 500; white-space: nowrap; }
/* What the import will really do to a recipe (second copy, overwriting a
   different recipe) — an icon on the cell, the sentence in its tooltip. */
.mx-flag { display: inline-flex; align-items: center; cursor: help; }
.mx-flag .ms { font-size: 15px; }
.mx-flag.warn { color: var(--warn); }
.mx-flag.danger { color: var(--danger); }
.mx-flag.accent { color: var(--accent); }
.mx-dh-counts .accent { color: var(--accent); font-weight: 500; white-space: nowrap; }
/* Mirroring: an action the promote takes, not a problem — the info triplet. */
.dw-gaps.dw-mirror { border-color: var(--accent-border); background: var(--accent-bg); }
/* Another tenant's ids, mapped to this one's own: they ship; the note is for the source. */
.dw-gaps.dw-mapped { border-color: var(--accent-border); background: var(--accent-bg); }
.dw-mapped .dw-gaps-head { color: var(--accent); }
.dw-gap-kind.mapped { color: var(--accent); }
.dw-mirror .dw-gaps-head { color: var(--accent); }
.dw-mirror .dw-gap-what strong { color: var(--text); }
.mx-dh-acts { display: flex; gap: 2px; margin-left: auto; flex: 0 0 auto; }
.mx-dh-acts button {
  width: 22px; height: 22px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  background: transparent; color: var(--muted);
}
.mx-dh-acts button:hover:not(:disabled) { background: var(--hover); color: var(--text); border-color: var(--border); }
.mx-dh-acts .ms { font-size: 15px; }

.mx-folder .mx-name-c { gap: 8px; cursor: pointer; }
.mx-folder-name { font-size: 12px; font-weight: 600; }
.mx-folder-count { font-size: 11px; color: var(--muted); }
.mx-folder .twist { width: 18px; text-align: center; color: var(--muted); flex: 0 0 auto; }
.mx-fold-icon { color: var(--brand); font-variation-settings: 'FILL' 1; flex: 0 0 auto; }
/* Per-destination new / updated / unchanged roll-up for the folder. */
.mx-tri { font-size: 11px; color: var(--muted); font-family: var(--font-mono); }
.mx-tri .ok { color: var(--ok); }
.mx-tri .warn { color: var(--warn); }

.mx-asset-row .mx-name-c { cursor: pointer; gap: 9px; }
.mx-asset-name { font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mx-asset-row .icon { flex: 0 0 auto; color: var(--muted); }
.mx-cell { gap: 8px; }
.mx-cell.excluded { opacity: 0.45; }
.mx-cell input[type=checkbox] { accent-color: var(--accent-solid); cursor: pointer; flex: 0 0 auto; }
.mx-noop { color: var(--muted); font-size: 12px; }
.mx-ver { font: 400 11px/1 var(--font-mono); color: var(--muted); }
.mx-src-ver { font: 400 11px/1 var(--font-mono); color: var(--muted); }
.mx-prop { font: 400 11px/1 var(--font-mono); color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mx-diff {
  font-size: 11px;
  color: var(--accent);
  cursor: pointer;
  text-decoration: underline;
  flex: 0 0 auto;
}
.mx-empty-cell { color: var(--muted); font-size: 11px; }
.mx-tail { height: 40px; }


/* ---- Detail drawer ------------------------------------------------------
   One asset across every destination. It sits BESIDE the matrix rather than
   over it, so the row you opened stays on screen and you can keep walking
   down the column with it open. */
.drawer {
  grid-column: 2;
  grid-row: 1;
  width: 392px;
  background: var(--panel);
  display: flex;
  flex-direction: column;
  min-height: 0;
  border-left: 1px solid var(--border);
  box-shadow: -8px 0 24px rgba(0, 0, 0, 0.06);
}
.drawer.hidden { display: none; }
.dw-head {
  padding: 14px 16px 12px;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: flex-start;
  gap: 10px;
}
.dw-head .ms { color: var(--accent-solid); font-size: 20px; margin-top: 2px; }
.dw-title { flex: 1; min-width: 0; }
.dw-name { font-size: 14px; font-weight: 500; }
.dw-path { font: 400 11px/1.4 var(--font-mono); color: var(--muted); margin-top: 3px; word-break: break-all; }
.dw-close {
  width: 28px; height: 28px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: none; border-radius: var(--radius-md);
  background: transparent; color: var(--muted); flex: 0 0 auto;
}
.dw-close:hover:not(:disabled) { background: var(--panel-2); color: var(--text); }
.dw-body { flex: 1; min-height: 0; overflow: auto; padding: 14px 16px 20px; }

.dw-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-bottom: 10px;
}
.dw-card-head {
  padding: 9px 12px;
  background: var(--panel-2);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.dw-card-name { font-size: 13px; font-weight: 500; flex: 1; min-width: 0; }
.dw-card-body { padding: 4px 12px 10px; }
.dw-lv {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 0;
  border-bottom: 1px solid var(--border-subtle);
  font-size: 12px;
}
.dw-lv:last-of-type { border-bottom: none; }
.dw-lv-label { color: var(--muted); flex: 0 0 auto; }
.dw-lv-value { text-align: right; min-width: 0; word-break: break-word; }
.dw-lv-value.warn { color: var(--warn); }
.dw-diff-btn { width: 100%; justify-content: center; margin-top: 8px; }

.dw-section {
  font: 500 11px/1 var(--font-sans);
  letter-spacing: 0.7px;
  text-transform: uppercase;
  color: var(--muted);
  margin: 16px 0 8px;
}
.dw-subs {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--panel-2);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 7px;
}
.dw-sub-row { display: flex; align-items: baseline; gap: 8px; font: 400 11px/1.5 var(--font-mono); }
.dw-sub-path { color: var(--muted); width: 128px; flex: 0 0 auto; overflow-wrap: anywhere; }
.dw-sub-val { flex: 1; min-width: 0; word-break: break-all; }
.dw-sub-val .arrow { color: var(--muted); }
.dw-sub-val.ignored { color: var(--muted); }
.dw-loading { color: var(--muted); font-size: 12px; padding: 8px 0; }


/* ---- Popovers (step 2 toolbar) ----------------------------------------- */
.popover {
  position: fixed;
  z-index: 40;
  min-width: 240px;
  max-height: 60vh;
  overflow: auto;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-menu);
}
.popover.hidden { display: none; }
.pop-head {
  padding: 9px 12px;
  border-bottom: 1px solid var(--border);
  font: 500 11px/1 var(--font-sans);
  letter-spacing: 0.7px;
  text-transform: uppercase;
  color: var(--muted);
}
.pop-body { padding: 6px 4px; display: flex; flex-direction: column; gap: 2px; }
.pop-body.hidden { display: none; }
.pop-body > label {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  font-size: 13px;
  cursor: pointer;
}
.pop-body > label:hover { background: var(--hover); }
.pop-body input[type=checkbox] { accent-color: var(--accent-solid); cursor: pointer; }
.pop-row .pop-name { flex: 1; min-width: 0; }
.pop-row .pop-count { font: 400 12px/1 var(--font-mono); color: var(--muted); }
.pop-body select {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text);
  padding: 4px 8px;
  font: inherit;
  font-size: 12px;
}
.pop-body .depth-field { flex-direction: column; align-items: stretch; gap: 6px; }
.pop-body .muted { padding: 0 10px 6px; font-size: 11px; line-height: 1.45; }
button.is-filtered { border-color: var(--accent-border); color: var(--accent); background: var(--accent-bg); }

/* ---- Manifest sidebar --------------------------------------------------- */
.mf-total {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 14px 0 12px;
  border-bottom: 1px solid var(--border-subtle);
}
.mf-total-n { font: 300 34px/1 var(--font-sans); letter-spacing: -1px; }
.mf-total-lbl { font-size: 12px; color: var(--muted); }
.mf-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border-subtle);
}
.mf-row .icon { color: var(--muted); flex: 0 0 auto; }
.mf-name { flex: 1; font-size: 13px; min-width: 0; }
.mf-n { font: 500 14px/1 var(--font-mono); }


/* ---- Pre-flight ---------------------------------------------------------
   Four numbers stated large, then the prose. The old screen led with a list
   where "11 running recipes will be stopped" carried the same weight as the
   provenance-comment note. */
.pf-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px;
  margin-bottom: 14px;
}
.pf-tile {
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--panel-2);
}
.pf-tile.warn { border-color: var(--warn-border); background: var(--warn-bg); }
.pf-tile-n { font: 300 30px/1 var(--font-sans); letter-spacing: -1px; }
.pf-tile.warn .pf-tile-n { color: var(--warn); }
.pf-tile-lbl { font-size: 12px; color: var(--muted); margin-top: 5px; }
.pf-tile-sub { font-size: 11px; color: var(--muted); margin-top: 2px; opacity: 0.85; }
.pf-section {
  font: 500 11px/1 var(--font-sans);
  letter-spacing: 0.7px;
  text-transform: uppercase;
  color: var(--muted);
  margin: 18px 0 10px;
}
.pf-facts-note { font-size: 12px; color: var(--muted); margin-bottom: 12px; }
.pf-prod-head { font-size: 13px; font-weight: 600; }
.pf-prod-sub { font-size: 12px; margin-top: 3px; }

/* ---- Diff summary (plain-English view over the raw side-by-side) --------- */
.sm-tabs {
  display: flex;
  gap: 4px;
  padding: 0 0 10px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 12px;
}
.sm-tab {
  padding: 6px 12px;
  font-size: 13px;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--muted);
}
.sm-tab:hover:not(:disabled) { background: var(--hover); color: var(--text); }
.sm-tab.active { background: var(--accent-bg); color: var(--accent); border-color: var(--accent-border); font-weight: 500; }
.sm-panel { display: flex; flex-direction: column; min-height: 0; flex: 1; }
.sm-panel.hidden { display: none; }

/* The colour meaning is inverted from the git reflex once the destination is
   on the right, so it is stated rather than left to be inferred. */
.sm-legend {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 11px;
  color: var(--muted);
  padding: 0 0 8px;
}
.sm-legend-key { width: 22px; height: 10px; border-radius: 2px; display: inline-block; }
.sm-legend-key.del { background: var(--danger-bg); border: 1px solid var(--danger-border); }
.sm-legend-key.add { background: var(--ok-bg); border: 1px solid var(--ok-border); }

.sm-wrap { overflow: auto; min-height: 0; padding-right: 4px; }
.sm-headline { font-size: 15px; font-weight: 500; margin-bottom: 14px; }
.sm-headline.ok { color: var(--ok); font-weight: 400; line-height: 1.5; }
.sm-note { color: var(--muted); font-weight: 400; font-size: 12px; margin-left: 8px; }

.sm-section { margin-bottom: 18px; }
.sm-section-head {
  font: 500 11px/1 var(--font-sans);
  letter-spacing: 0.7px;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 8px;
}
.sm-step {
  border: 1px solid var(--border);
  border-left: 3px solid var(--border-hover);
  border-radius: var(--radius-md);
  padding: 9px 12px;
  margin-bottom: 6px;
  background: var(--panel);
}
.sm-step.add { border-left-color: var(--ok-solid); }
.sm-step.del { border-left-color: var(--danger-solid); }
.sm-step-head { font-size: 13px; font-weight: 500; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sm-num { font: 400 11px/1 var(--font-mono); color: var(--muted); flex: 0 0 auto; }
.sm-prov {
  font: 400 10px/1 var(--font-mono);
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 2px 7px;
}
.sm-fields { list-style: none; margin: 7px 0 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.sm-fields li { font-size: 12px; line-height: 1.6; }
.sm-path { font: 400 11px/1 var(--font-mono); color: var(--muted); margin-right: 6px; }
.sm-val {
  font: 400 11px/1.4 var(--font-mono);
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 1px 5px;
  word-break: break-all;
}
.sm-none { font-size: 11px; color: var(--muted); font-style: italic; }
.sm-arrow { color: var(--muted); margin: 0 2px; }
.sm-tag {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 1px 6px;
  border-radius: var(--radius-pill);
  margin-left: 4px;
}
.sm-tag.new { color: var(--ok); background: var(--ok-bg); border: 1px solid var(--ok-border); }
.sm-tag.gone { color: var(--danger); background: var(--danger-bg); border: 1px solid var(--danger-border); }
/* A disabled step is a state, not a fault — neutral, like `identical`. */
.sm-tag.off { color: var(--muted); background: var(--panel-2); border: 1px solid var(--border); }

.sm-more { color: var(--muted); font-style: italic; }

/* --- Recipe flow diff (public/recipe-flow.js) ------------------------------
   Two aligned columns of the step outline. The row is the grid: both cells and
   the detail strip are its children, so a modified step's field list spans the
   full width instead of being duplicated per side. */
.fl { margin-top: 10px; }
.fl-head,
.fl-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.fl-head {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--panel);
  padding: 6px 0 7px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 6px;
}
.fl-col {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}
.fl-col span { font-weight: 600; color: var(--text); }
.fl-row { margin-bottom: 3px; }
/* A step with field changes is ONE bounded card: the pair of step cells and
   the fields that belong to them, inside a single boundary. Before this the
   field rows floated between two steps with nothing saying whose they were. */
.fl-row.has-detail {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--panel-2);
  padding: 5px 5px 2px;
  margin-bottom: 7px;
  row-gap: 5px;
}
.fl-row.modified.has-detail { border-left: 3px solid var(--warn-solid); }
/* Where the nav arrows just landed. */
.fl-row.nav-here { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-md); }
/* The detail spans the row and re-creates the row's own two columns inside
   itself, so an incoming value lands under the incoming step and a current
   value under the current one. Same template, same gap — the alignment is
   exact rather than approximate. */
.fl-row .fl-detail { grid-column: 1 / -1; padding: 0; }
/* A disclosure control, not a label: the twirl says it opens, the badges say
   what KIND of change is inside, and the count says how much. */
.fl-fbtn {
  display: flex;
  align-items: center;
  gap: 7px;
  width: 100%;
  border: 0;
  background: transparent;
  color: var(--muted);
  font-size: 11px;
  padding: 3px 4px;
  cursor: pointer;
  text-align: left;
  border-radius: var(--radius-sm);
}
.fl-fbtn:hover { background: var(--panel); color: var(--text); }
.fl-twirl { display: inline-flex; flex: 0 0 auto; transition: transform 120ms ease; }
.fl-twirl .ms { font-size: 16px; }
.fl-fbtn.open .fl-twirl { transform: rotate(90deg); }
.fl-badge {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  flex: 0 0 auto;
  padding: 1px 7px 1px 5px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--panel);
  color: var(--muted);
  cursor: help;
}
.fl-badge .ms { font-size: 14px; }
.fl-badge-n { font: 500 10.5px/1 var(--font-mono); color: var(--text); }
.fl-fbtn.open .fl-badge, .fl-fbtn:hover .fl-badge { border-color: var(--border-hover); }
.fl-btext { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fl-coderow { display: flex; align-items: center; gap: 10px; margin-left: 26px; flex-wrap: wrap; }
.fl-raw-jump {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  padding: 3px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--panel);
  color: var(--accent);
  cursor: pointer;
}
.fl-raw-jump:hover { border-color: var(--accent); }
.fl-raw-jump .ms { font-size: 14px; }
.fl-coderow .fl-more { padding-left: 0; }
.fl-fields { margin-top: 5px; display: flex; flex-direction: column; gap: 5px; }
/* A class selector outranks the UA's `[hidden] { display: none }`, so setting
   display above quietly un-hid the collapsed panel. */
.fl-fields[hidden] { display: none; }
.fl-field {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.fl-field-path {
  grid-column: 1 / -1;
  padding-left: 26px;
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}
/* The label leads and the id stays beside it — never instead of it. The
   picklist dictionaries ship inside the package and are not substituted, so a
   destination's label can be the source's, frozen at last promotion. */
.fl-fp-main { font-size: 12px; font-weight: 500; color: var(--text); }
.fl-fp-id { font: 400 10.5px/1.4 var(--font-mono); color: var(--muted); }
.fl-verdict { font-size: 11.5px; display: inline-flex; align-items: baseline; gap: 5px; }
.fl-vmark { font: 600 11px/1 var(--font-mono); width: 9px; text-align: center; }
.fl-verdict.change { color: var(--text); }
.fl-verdict.change .fl-vmark { color: var(--warn); }
.fl-verdict.muted { color: var(--muted); font-style: italic; }
.fl-verdict.add { color: var(--ok); }
.fl-verdict.del { color: var(--danger); }
.fl-field.solo { grid-template-columns: 1fr; }
.fl-field.quiet .fl-fp-main { font-weight: 400; color: var(--muted); }
.fl-ftag { font-style: italic; }
.fl-fv { min-width: 0; }
/* Nothing is ever removed from a value; the clip is visual and reversible. */
.fl-val {
  display: block;
  font: 400 11px/1.7 var(--font-mono);
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 4px 8px;
  overflow-wrap: anywhere;
  max-height: 11em;
  overflow: auto;
}
.fl-none { font-size: 11px; color: var(--muted); font-style: italic; }
.fl-more { color: var(--muted); font-style: italic; font-size: 11px; padding-left: 26px; }
.fl-inline {
  font: 400 10.5px/1.5 var(--font-mono);
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0 5px;
  overflow-wrap: anywhere;
}

/* Set deltas and property deltas: one line per thing that moved. */
.fl-list, .fl-props { margin-left: 26px; display: flex; flex-direction: column; gap: 3px; }
.fl-li {
  display: flex;
  align-items: baseline;
  gap: 7px;
  font-size: 11.5px;
  padding: 2px 8px;
  border-radius: var(--radius-sm);
  border-left: 2px solid transparent;
}
/* path + the same incoming | current pair the rest of the panel uses. */
.fl-pr {
  display: grid;
  grid-template-columns: 9px minmax(90px, 0.7fr) 1fr 1fr;
  align-items: baseline;
  gap: 8px;
  font-size: 11.5px;
  padding: 2px 8px;
  border-radius: var(--radius-sm);
  border-left: 2px solid transparent;
}
.fl-pr.head {
  padding-bottom: 0;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}
.fl-pr-v { min-width: 0; overflow-wrap: anywhere; }
.fl-li-mark { font: 600 11px/1 var(--font-mono); width: 9px; flex: 0 0 auto; text-align: center; }
.fl-li-id, .fl-pr-path { font: 400 10.5px/1.4 var(--font-mono); color: var(--muted); }
.fl-li.add, .fl-pr.add { background: var(--ok-bg); border-left-color: var(--ok-solid); }
.fl-li.add .fl-li-mark, .fl-pr.add .fl-li-mark { color: var(--ok); }
.fl-li.del, .fl-pr.del { background: var(--danger-bg); border-left-color: var(--danger-solid); }
.fl-li.del .fl-li-mark, .fl-pr.del .fl-li-mark { color: var(--danger); }
.fl-pr.mod { background: var(--panel-2); border-left-color: var(--warn-solid); }
.fl-pr.mod .fl-li-mark { color: var(--warn); }

/* Datapills. A chip, the way the recipe editor draws one — the alternative is
   90 characters of JSON per reference, which is what made two nearly-identical
   values impossible to tell apart. */
.dp {
  display: inline-block;
  font: 400 10px/1.5 var(--font-sans, inherit);
  border-radius: var(--radius-pill);
  padding: 0 7px;
  margin: 0 1px;
  vertical-align: baseline;
  white-space: nowrap;
}
/* Three tiers. With 35 chips in a formula, making the changed ones louder
   gives 35 loud things and 3 louder ones — so the UNCHANGED ones recede to
   punctuation instead, and the rare chip is the one that stands out. */
.dp-same { color: var(--muted); background: transparent; border: 1px solid var(--border); }
.dp-diff { color: var(--accent); background: var(--accent-bg); border: 1px solid var(--accent-border); font-weight: 500; }
.dp-mark { font: 600 9px/1 var(--font-mono); margin-right: 3px; }
.dp-unresolved { color: var(--warn); background: var(--warn-bg); border: 1px solid var(--warn-border); }

.fl-cell {
  /* --d is the outline depth; the indent is what carries the branch nesting
     now that there are no connector lines to follow. */
  padding: 6px 10px 6px calc(10px + var(--d, 0) * 16px);
  border: 1px solid var(--border);
  border-left: 3px solid var(--border-hover);
  border-radius: var(--radius-md);
  background: var(--panel);
  min-width: 0;
}
.fl-cell.empty { border: 1px dashed var(--border); background: transparent; }
.fl-line { display: flex; align-items: center; gap: 7px; font-size: 13px; min-width: 0; }
.fl-mark {
  flex: 0 0 auto;
  width: 9px;
  font: 600 12px/1 var(--font-mono);
  color: var(--muted);
  text-align: center;
}
.fl-num { flex: 0 0 auto; font: 400 11px/1 var(--font-mono); color: var(--muted); }
.fl-label { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fl-prov {
  /* Shrinks before the label does. A provider reads "quickbase rest connector
     528080 1697828506" — machine noise that was squeezing the one part of the
     line anybody reads. */
  flex: 0 1 auto;
  min-width: 0;
  max-width: 38%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font: 400 10px/1.5 var(--font-mono);
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 1px 7px;
}
.fl-off {
  flex: 0 0 auto;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 1px 6px;
  border-radius: var(--radius-pill);
  color: var(--muted);
  background: var(--panel-2);
  border: 1px solid var(--border);
}
.fl-note {
  font-size: 11px;
  font-style: italic;
  color: var(--muted);
  padding-left: 16px;
  margin-top: 2px;
}

/* A control-flow step is the container its children sit in, not an action. */
.fl-cell.cf { background: var(--panel-2); }
.fl-cell.cf .fl-label { font: 600 11px/1.4 var(--font-mono); letter-spacing: 0.06em; }

/* Arriving is mint, leaving is red — the same reading the summary sections
   used, and the opposite of the RAW tab, which is Monaco's orientation and
   carries its own legend. The mark in the gutter says it without colour. */
.fl-row.added .fl-cell:not(.empty) { border-left-color: var(--ok-solid); background: var(--ok-bg); }
.fl-row.added .fl-mark { color: var(--ok); }
.fl-row.removed .fl-cell:not(.empty) { border-left-color: var(--danger-solid); background: var(--danger-bg); }
.fl-row.removed .fl-mark { color: var(--danger); }
.fl-row.modified .fl-cell { border-left-color: var(--warn-solid); }
.fl-row.modified .fl-mark { color: var(--warn); }

.fl-fold-row { margin: 3px 0 5px; }
.fl-fold {
  width: 100%;
  border: 1px dashed var(--border);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--muted);
  font-size: 11px;
  padding: 4px 8px;
  cursor: pointer;
}
.fl-fold:hover { border-color: var(--border-hover); color: var(--text); }
.fl-fold.open { border-style: solid; }

@media (max-width: 900px) {
  .fl-head, .fl-row { grid-template-columns: 1fr; }
  .fl-row .fl-detail { padding-left: 10px; }
}


/* An audit cell opens the diff; Workato's own history is the secondary icon. */
.mx-cell.can-diff { cursor: pointer; }
.mx-cell.can-diff:hover { background: var(--accent-bg); }
.mx-open {
  display: inline-flex;
  align-items: center;
  color: var(--muted);
  opacity: 0;
  transition: opacity 0.12s;
}
.mx-open .ms { font-size: 14px; }
.mx-cell:hover .mx-open, .mx-open:focus-visible { opacity: 1; }
.mx-open:hover { color: var(--accent); }


/* ===========================================================================
   SETTINGS — the three shared documents that decide what a promotion does.
   =========================================================================== */
#view-settings.active { display: block; overflow: auto; }
.settings-wrap { max-width: 1240px; margin: 0 auto; padding: 18px 20px 40px; display: flex; flex-direction: column; gap: 18px; }
.settings-section { border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--panel); overflow: hidden; }
.settings-section.hidden { display: none; }

/* Tabs: the section you're working in gets the whole screen. */
.settings-tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border); }
.settings-tab {
  padding: 9px 14px;
  font-size: 13px;
  color: var(--muted);
  background: transparent;
  border: 1px solid transparent;
  border-bottom: none;
  border-radius: var(--radius-md) var(--radius-md) 0 0;
  margin-bottom: -1px;
}
.settings-tab:hover:not(:disabled) { color: var(--text); background: var(--hover); }
.settings-tab.active {
  color: var(--accent);
  font-weight: 500;
  background: var(--panel);
  border-color: var(--border);
  border-bottom: 1px solid var(--panel);
}

/* Collapsed cluster: the head becomes its one-line summary. */
.cluster-toggle { padding: 2px 4px; }
.cluster-toggle .ms { font-size: 18px; }
.cluster-card.collapsed .cluster-head { border-bottom: none; }
.cluster-head .danger { color: var(--danger); }
.settings-head {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 10px 14px; border-bottom: 1px solid var(--border);
}
.settings-head h2, .settings-head h3 {
  margin: 0; font: 500 11px/1 var(--font-sans); letter-spacing: 0.7px; text-transform: uppercase; color: var(--muted);
}
.settings-head.sub { border-top: 1px solid var(--border); background: var(--panel-2); }
.settings-head .spacer { flex: 1; }
.settings-head input[type='search'] {
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius-md);
  color: var(--text); padding: 5px 10px; font: inherit; font-size: 12px; width: 180px;
}
.settings-head .small { font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; margin-left: 6px; }
.settings-note { padding: 10px 14px; font-size: 12px; color: var(--muted); line-height: 1.5; border-bottom: 1px solid var(--border-subtle); }
.settings-body { padding: 10px 14px 14px; }
.settings-body .empty.small { padding: 8px 0; font-size: 12px; }
.settings-problems {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 9px 12px; margin-bottom: 12px;
  border: 1px solid var(--danger-border); border-radius: var(--radius-md); background: var(--danger-bg);
  color: var(--danger); font-size: 12px;
}
.settings-problems .ms { flex: 0 0 auto; }

/* Inventory */
.inv-grid { display: grid; grid-template-columns: minmax(0, 1fr) 110px 70px 200px; gap: 0 12px; font-size: 12px; }
.inv-grid > div { padding: 6px 0; border-bottom: 1px solid var(--border-subtle); display: flex; align-items: center; min-width: 0; }
.inv-head { font: 500 10px/1 var(--font-sans); letter-spacing: 0.6px; text-transform: uppercase; color: var(--muted); }
.inv-id { font: 400 11px/1 var(--font-mono); color: var(--muted); }
.inv-n { font-family: var(--font-mono); }
.inv-cluster.unplaced { color: var(--muted); font-style: italic; }

/* Clusters */
.cluster-card { border: 1px solid var(--border); border-radius: var(--radius-lg); margin-bottom: 12px; overflow: hidden; }
.cluster-head { display: flex; align-items: center; gap: 10px; padding: 9px 12px; background: var(--panel-2); border-bottom: 1px solid var(--border); }
.cluster-head .ms { color: var(--brand); }
.cluster-head .spacer { flex: 1; }
.cluster-name {
  font: 500 14px/1.2 var(--font-sans); color: var(--text);
  background: transparent; border: 1px solid transparent; border-radius: var(--radius-sm); padding: 4px 6px; min-width: 200px;
}
.cluster-name:hover, .cluster-name:focus { border-color: var(--border); background: var(--panel); outline: none; }
.cl-grid { display: grid; grid-template-columns: minmax(0, 1fr) 70px 320px 40px; gap: 0 12px; padding: 4px 12px; font-size: 12px; }
.cl-grid > div { padding: 6px 0; border-bottom: 1px solid var(--border-subtle); display: flex; align-items: center; gap: 8px; min-width: 0; }
.cl-head { font: 500 10px/1 var(--font-sans); letter-spacing: 0.6px; text-transform: uppercase; color: var(--muted); }
.cl-app { display: flex; flex-direction: column; align-items: flex-start !important; gap: 2px !important; }
.cl-n { font-family: var(--font-mono); }
.cl-sel select, .cl-add-select, .cluster-new input {
  width: 100%; background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius-md);
  color: var(--text); padding: 5px 8px; font: inherit; font-size: 12px;
}
.cl-sel.unassigned select { border-color: var(--danger-border); background: var(--danger-bg); color: var(--danger); }
.cl-remove { padding: 2px 6px; }
.cl-add { padding: 8px 12px 10px; }
.cluster-new { display: flex; gap: 8px; align-items: center; margin-top: 4px; }
.cluster-new input { max-width: 320px; }



/* ---- Settings › Recipe links ------------------------------------------- */
.lk-pick { display: inline-flex; align-items: center; gap: 6px; }
.lk-pick select {
  background: var(--panel-2); border: 1px solid var(--border); border-radius: var(--radius-md);
  color: var(--text); padding: 5px 8px; font: inherit; font-size: 12px; max-width: 220px;
}
.lk-grid { font-size: 12px; }
.lk-r { display: grid; grid-template-columns: minmax(180px, 1fr) 84px 84px 96px repeat(4, 84px); align-items: center; border-bottom: 1px solid var(--border-subtle); }
.lk-h { font: 500 10px/1 var(--font-sans); letter-spacing: 0.6px; text-transform: uppercase; color: var(--muted); }
.lk-h .lk-c { padding: 8px 6px; }
.lk-c { padding: 7px 6px; min-width: 0; }
.lk-clickable { cursor: pointer; }
.lk-clickable:hover { background: var(--panel-2); }
.lk-clickable.open { background: var(--panel-2); }
.lk-dest { display: flex; align-items: center; gap: 4px; font-weight: 500; }
.lk-twirl { font-size: 18px; color: var(--muted); width: 18px; }
.lk-n { font-family: var(--font-mono); text-align: right; padding-right: 18px; }
.lk-n.zero { color: var(--muted); opacity: 0.55; }
.lk-n.ok { color: var(--ok); }
.lk-n.accent { color: var(--accent); font-weight: 600; }
.lk-n.warn { color: var(--warn); font-weight: 600; }
.lk-n.danger { color: var(--danger); font-weight: 600; }
.lk-wait { color: var(--muted); font-style: italic; }
.lk-err { color: var(--danger); display: flex; align-items: center; gap: 6px; }
.lk-detail { padding: 8px 10px 14px 28px; border-bottom: 1px solid var(--border); background: var(--panel); }
.lk-sect-head { display: flex; align-items: center; gap: 6px; font-weight: 600; color: var(--warn); margin: 6px 0 4px; }
.lk-sect-head .ms { font-size: 16px; }
.lk-count { font: 500 10px/1 var(--font-mono); color: var(--muted); border: 1px solid var(--border); border-radius: var(--radius-pill); padding: 2px 6px; margin-left: 4px; }
.lk-item { border: 1px solid var(--warn-border); background: var(--warn-bg); border-radius: var(--radius-md); padding: 7px 10px; margin: 6px 0; }
.lk-reason { color: var(--warn); font-size: 11.5px; margin-bottom: 5px; }
.lk-pair { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 4px 16px; }
.lk-side > div + div { margin-top: 3px; }
.lk-label { display: inline-block; min-width: 0; font: 500 9.5px/1 var(--font-sans); letter-spacing: 0.5px; text-transform: uppercase; color: var(--muted); margin-right: 6px; }
.lk-name { color: var(--text); font-weight: 500; }
.lk-sub { color: var(--muted); font-size: 11px; }
.lk-run { color: var(--ok); font-weight: 600; }
.lk-flag { display: inline-block; margin-left: 8px; color: var(--warn); font-size: 11px; }
.lk-callout { display: flex; gap: 8px; align-items: flex-start; margin: 8px 0; padding: 7px 10px; border: 1px solid var(--warn-border); background: var(--warn-bg); color: var(--warn); border-radius: var(--radius-md); font-size: 11.5px; }
.lk-callout .ms { font-size: 15px; flex: 0 0 auto; }
.lk-details { margin: 6px 0; }
.lk-details summary { cursor: pointer; color: var(--text); font-weight: 500; padding: 3px 0; }
.lk-list { padding: 4px 0 4px 16px; max-height: 320px; overflow: auto; }
.lk-row { padding: 3px 0; border-bottom: 1px solid var(--border-subtle); display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline; }
.lk-confirm { margin-left: 8px; padding: 1px 8px; font-size: 11px; color: var(--accent); border-color: var(--accent-border); }
.lk-confirm .ms { font-size: 14px; }

/* ---- Coverage badge (step 1 chip, step 3 tiles) ------------------------- */
.qb-badge .ms { font-size: 12px; }
.qb-badge a { color: inherit; text-decoration: underline; }
.qb-badge + .qb-badge { margin-left: 4px; }

/* ---- Drawer: what this destination cannot take -------------------------- */
.status-tag.blocked { color: var(--danger); border-color: var(--danger-border); background: var(--danger-bg); }
.dw-gaps {
  margin: 8px 0 4px;
  border: 1px solid var(--danger-border);
  border-radius: var(--radius-md);
  background: var(--danger-bg);
  padding: 8px 10px;
  font-size: 12px;
}
.dw-gaps-head { display: flex; align-items: center; gap: 6px; color: var(--danger); font-weight: 600; margin-bottom: 6px; }
.dw-gaps-head .ms { font-size: 15px; }
.dw-gap { display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: 2px 8px; padding: 4px 0; border-top: 1px solid var(--danger-border); }
.dw-gap-kind { font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--danger); font-weight: 600; padding-top: 2px; }
.dw-gap-what { font-size: 12px; word-break: break-all; }
.dw-gap-what code { font: 400 11px/1.4 var(--font-mono); background: var(--panel); border: 1px solid var(--danger-border); border-radius: var(--radius-sm); padding: 0 4px; }
.dw-gap-why { grid-column: 2; color: var(--muted); font-size: 11px; }
.dw-gap-fix { margin-top: 8px; font-size: 11px; color: var(--text); }
.dw-gap-fix a { color: var(--accent); }
/* Reported, not refused: the warn tier keeps the same anatomy in the warning
   triplet so "this will ship stale" never reads as "this will be refused". */
.dw-gaps.dw-warns { border-color: var(--warn-border); background: var(--warn-bg); }
.dw-warns .dw-gaps-head, .dw-warns .dw-gap-kind { color: var(--warn); }
.dw-warns .dw-gap, .dw-warns .dw-gap-what code { border-color: var(--warn-border); }

/* What stands between a stopped recipe and running — Workato's own stop cause
   and the connections it uses that are not authorized. Amber: a fact about
   the destination, not a failure of the promotion. */
.ten-chip .tc-blk {
  font-size: 10.5px;
  color: var(--warn);
  background: var(--warn-bg);
  border: 1px solid var(--warn-border);
  border-radius: var(--radius-pill);
  padding: 0 7px;
  max-width: 260px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
