/* Graph editor CSS styles — Graphden brand palette (docs/brand).
 * Five brand values: Graphden Blue #0066CC (--accent), Blue Deep #0047A3
 * (--blue-deep), Ink #0D1117 (--fg / borders / dark ground), Soft Tint
 * #EEF4FF (--soft-tint / tinted grounds), White. #4D94FF is Blue's
 * light-on-dark tint (the dark-theme --accent). New brand values are
 * added as tokens (stylelint strict-value forbids bare hex in rule bodies).
 * Theme variables live on `:root`; `body.theme-dark` overrides. The
 * full sidebar respects `--sidebar-width` so the resize handle and
 * the collapse button can change one number to retheme + relayout. */
:root {
  --bg: #fff;
  --fg: #0d1117;
  --muted-fg: #57606a;
  --light-fg: #8b949e;
  --border: #0d1117;
  --light-border: #d0d7de;
  --input-border: #c9d1d9;
  --hover-bg: #eef4ff;
  --selected-bg: #dbe7ff;
  --sidebar-bg: #f5f8ff;
  --header-bg: #eef4ff;
  --header-fg: #0d1117;
  --accent: #0066cc;
  /* Two brand values the old monochrome scheme lacked. */
  --blue-deep: #0047a3;
  --soft-tint: #eef4ff;
  /* Graph node ("card") palette — keeps the same visual hierarchy in both
     themes (slightly-raised body + distinct header band + visible outline)
     instead of inverting to bright gray. The focused/root card wears a
     Graphden-Blue title band so the active node reads in brand colour. */
  --card-bg: #fff;
  --card-fg: #0d1117;
  --card-border: #0d1117;
  --card-header-bg: #0066cc;
  --card-header-fg: #fff;
  --card-row-highlight: #eef4ff;
  /* HOF-captured arg strip — subtle blue tint that survives both themes. */
  --hof-fg: #557;
  --hof-bg: #f4f6fc;
  --hof-border: #b8c0e0;
  /* Description tooltip — high-contrast bubble that sits over both
     overlay rows and the cy canvas, so it must read against any bg. */
  --tooltip-bg: rgba(0,0,0,0.88);
  --tooltip-fg: #fff;
  /* Shadow + scrim tokens — same alpha curves as Material Design's
     elevation set; centralised here so any new floating element picks
     consistent depth instead of inventing a new opacity. */
  --shadow-sm: 0 2px 6px rgba(0,0,0,0.18);
  --shadow-md: 0 2px 6px rgba(0,0,0,0.25);
  --shadow-lg: 0 4px 12px rgba(0,0,0,0.25);
  --shadow-xl: 0 4px 14px rgba(0,0,0,0.30);
  --overlay-hover: rgba(0,0,0,0.08);
  --overlay-pressed: rgba(127,127,127,0.20);
  --modal-backdrop-bg: rgba(0,0,0,0.4);
  --sidebar-width: 280px;
  /* Touch-target sizing — Apple HIG / WCAG 2.5.5. The visible glyph
     stays small on desktop (15px etc.); these tokens are the
     min hit-area enforced under `@media (hover:none)`. Two values
     so non-critical affordances (e.g. an inline `+` near big
     siblings) can use `--touch-target-min` without bloating the
     UI on tightly-packed rows. */
  --touch-target: 44px;
  --touch-target-min: 28px;
  /* Below this viewport width the sidebar auto-collapses on first
     visit (iPad portrait = 768; we leave headroom for split-view
     1/2 and 1/3 multitasking on iPad). */
  --sidebar-narrow-breakpoint: 900px;
  /* Right-edge action icons are sized + pinned via these vars so
     `@media (hover:none)` can grow them for touch without
     touching the JS that builds the icons. `applyActionIconBox`
     reads --icon-size; the pinning helpers use --icon-pin-r-1
     (closest to edge) and --icon-pin-r-2 (one slot in). */
  --icon-size: 15px;
  --icon-font-size: 10px;
  --icon-pin-r-1: 6px;
  --icon-pin-r-2: 24px;
  --icon-pin-r-3: 42px;
  /* Monospace font stack — shared between the layout-affecting JS
     pinning helpers (none yet) and editor's :code-shaped surfaces. */
  --mono: ui-monospace, monospace;
  /* Status semantic colours — used by edit-form validation, picker
     compatibility hints, error tooltips. Same hue across themes so
     "green = ok / red = problem" is unambiguous. */
  --success-fg: #4ade80;
  --error-fg: #ff6b6b;
  --warning-fg: #c47b00;
  /* Saturated fills: `--on-accent` is legible text over Graphden Blue / any
     status fill; `--danger-bg` backs the fatal-load banner. */
  --on-accent: #fff;
  --danger-bg: #c0392b;
  /* Effect-chip categorical palette — encodes effect KIND (db / network
     / time / …) so the chip background must be the same colour in both
     themes. Chip text is fixed dark since every bg here is bright. */
  --effect-chip-fg: #0d1117;
  --effect-chip-db: #5eaaff;
  --effect-chip-env: #c084fc;
  --effect-chip-io: #4ade80;
  --effect-chip-network: #ff9a3c;
  --effect-chip-time: #facc15;
  --effect-chip-misc: #ccc;
  --effect-chip-random: #f0abfc;
  --effect-chip-process: #94a3b8;
  --effect-chip-raw-sql: #f87171;
}
body.theme-dark {
  /* Ink #0D1117 is the brand's dark ground. */
  --bg: #0d1117;
  --fg: #e6edf3;
  --muted-fg: #9aa5b1;
  --light-fg: #7d8695;
  --border: #30363d;
  --light-border: #21262d;
  --input-border: #30363d;
  --hover-bg: #182130;
  --selected-bg: #22304a;
  --sidebar-bg: #10151c;
  --header-bg: #161b22;
  --header-fg: #e6edf3;
  --accent: #4d94ff;
  --blue-deep: #0047a3;
  --soft-tint: #182130;
  /* Card body slightly raised vs Ink body; the focused/root card keeps the
     Graphden-Blue title band (brand colour reads on dark too); outline
     subtle so the node frame isn't a glaring box. */
  --card-bg: #161b22;
  --card-fg: #e6edf3;
  --card-border: #30363d;
  --card-header-bg: #0066cc;
  --card-header-fg: #fff;
  --card-row-highlight: #182130;
  --hof-fg: #aab8e8;
  --hof-bg: #1e2030;
  --hof-border: #3a4070;
  --tooltip-bg: rgba(232,232,232,0.92);
  --tooltip-fg: #1a1a1a;
  /* Dark theme — slightly stronger shadows since they're on a darker
     base, scrim flipped to white on dark. */
  --shadow-sm: 0 2px 6px rgba(0,0,0,0.40);
  --shadow-md: 0 2px 6px rgba(0,0,0,0.50);
  --shadow-lg: 0 4px 12px rgba(0,0,0,0.50);
  --shadow-xl: 0 4px 14px rgba(0,0,0,0.55);
  --overlay-hover: rgba(255,255,255,0.08);
  --overlay-pressed: rgba(255,255,255,0.16);
  --modal-backdrop-bg: rgba(0,0,0,0.65);
  /* Status colours — slight desaturation against the dark background
     so they don't vibrate. Hue still maps green=ok / red=error. */
  --success-fg: #5be08a;
  --error-fg: #ff7777;
  --warning-fg: #e0a94a;
  --on-accent: #fff;
  --danger-bg: #e5484d;
  /* Effect-chip palette stays identical in dark theme — colour encodes
     category, not surface. Chip fg flips to white since dark-theme chips
     could otherwise look washed out against page bg in screenshots. */
}
* { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; }

/* Screen-reader-only utility — text is invisible but readable by
   assistive tech. Standard recipe (clip + 1px box) survives
   high-contrast mode and defeats accidental tab-focus. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%);
  white-space: nowrap; border: 0;
}
body { font-family: 'SF Mono', 'Monaco', 'Inconsolata', monospace;
  background: var(--bg); color: var(--fg); }

/* ID-based layout (matches editor-body hiccup structure).
 *
 * The sidebar is absolute-positioned ON TOP of a full-viewport
 * graph-container. Collapse / expand is a `transform: translateX(...)`
 * slide — graph-container never reflows, cytoscape never resizes,
 * edges never get recomputed. That keeps the animation perfectly
 * smooth (the only thing moving is the sidebar layer, GPU-composited
 * via transform) and frees us from juggling a pan compensation in
 * lockstep with the CSS transition.
 *
 * Trade-off: when the sidebar is open it covers the leftmost slice of
 * the graph. We compensate at fit time (fitInVisibleArea in
 * editor-render.js) by panning the freshly-fitted graph half the
 * sidebar width to the right so the visible portion stays centered. */
#app { height: 100vh; }
#main-container { position: relative; height: 100%; }
#side-menu {
  position: absolute; top: 0; left: 0; height: 100%;
  width: var(--sidebar-width); background: var(--sidebar-bg);
  border-right: 2px solid var(--border); overflow-y: auto;
  z-index: 200;
  transform: translateX(0);
  transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
  will-change: transform;
}
body.resizing-sidebar #side-menu { transition: none; user-select: none; }
body.resizing-sidebar { user-select: none; cursor: col-resize; }
/* Collapsed: slide the sidebar one full width to the left so it sits
   off-screen. Width stays the same in the underlying layout, but
   nothing reflows since the sidebar is absolute-positioned anyway.
   The expand affordance is a separate floating button — see
   `.sidebar-expand-floating`. */
body.sidebar-collapsed #side-menu {
  transform: translateX(calc(-1 * var(--sidebar-width) - 2px));
}
body.sidebar-collapsed #sidebar-resizer { display: none; }
#graph-container {
  position: absolute; top: 0; left: 0; width: 100%; height: 100%;
  display: flex; flex-direction: column; overflow: hidden;
}
#graph-surface { width: 100%; height: 100%; }

/* The overlay layer. Carries the viewport transform for every node / edge-label
   overlay at once, so panning and zooming is a single style write rather than a
   loop over ~200 overlays. Overlays inside it are positioned in graph
   coordinates. Zero-sized and pointer-transparent: its children are absolutely
   positioned, overflow it, and opt back into pointer events themselves. */
#graph-layer {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  z-index: 10;
  transform-origin: 0 0;
  pointer-events: none;
}

/* Edge layer — inside #graph-layer, so it rides the same viewport transform and
   its paths are emitted in graph coordinates. `overflow: visible` because graph
   coordinates run negative in both axes and the paths spill far outside this
   box. It carries no z-index, so it paints under the overlays (which do).

   The 1px size is load-bearing: per SVG, a zero width or height on the root
   `<svg>` disables rendering of the element entirely — the paths keep their
   geometry (getBoundingClientRect still reports the right box) but nothing is
   ever painted. It cannot be `0` like its parent. */
#edge-layer {
  position: absolute;
  top: 0;
  left: 0;
  width: 1px;
  height: 1px;
  overflow: visible;
}

/* Stroke widths are set on #edge-lines / #edge-hits per zoom, in graph units,
   and inherited by the paths. */
.edge-line {
  fill: none;
  stroke: var(--fg);
}

/* Hovering an edge lights the whole visual bundle it belongs to. Markers use
   `fill: context-stroke`, so the arrowheads follow without a rule of their own. */
.edge-line.edge-hovered {
  stroke: var(--accent);
}

/* The grab target: same path, fat transparent stroke. `pointer-events: stroke`
   hit-tests the stroke area, which is what lets `elementsFromPoint` return
   every edge whose vertical run passes under the cursor. */
.edge-hit {
  fill: none;
  stroke: transparent;
  pointer-events: stroke;
}

/* Floating expand button — visible only when sidebar is collapsed. Sits
   in viewport top-left, fades in once the shrink animation has cleared
   space for it so the icon doesn't appear over a still-shrinking
   sidebar. */
.sidebar-expand-floating {
  position: fixed; top: 8px; left: 8px; z-index: 250;
  width: 26px; height: 26px; padding: 3px;
  background: var(--bg); color: var(--fg);
  border: 1px solid var(--fg); border-radius: 3px;
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  opacity: 0; pointer-events: none;
  transition: opacity 0.18s ease;
}
body.sidebar-collapsed .sidebar-expand-floating {
  opacity: 1; pointer-events: auto;
  transition: opacity 0.18s ease 0.2s; /* wait for the shrink to clear */
}
.sidebar-expand-floating:hover { background: var(--fg); color: var(--bg); }

/* Right-edge resize handle. 4 px wide, centred on the sidebar's
   trailing border so the cursor lands on it before the user has to
   nail the 2 px border itself. */
#sidebar-resizer {
  position: absolute; top: 0; right: -2px; width: 6px; height: 100%;
  cursor: col-resize; z-index: 250; user-select: none;
}
#sidebar-resizer:hover { background: var(--overlay-hover); }

/* Search bar — input + clear on row 1, "Only services" toggle wraps
 * to row 2 below. Wrap-flex avoids competing with the input for
 * horizontal real estate on narrow sidebars. */
#search-bar { display: flex; padding: 6px 8px;
  border-bottom: 1px solid var(--light-border); gap: 4px;
  flex-wrap: wrap; }
/* Entity-kind visibility toggles (fn / types / secrets / services) — each
   an eye that dims + gets a slash when its kind is hidden. Button state is
   synced from localStorage by syncKindFilterBar (aria-pressed). */
#kind-filters {
  flex: 1 1 100%;
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 2px 8px; padding: 2px 0;
}
.kind-toggle {
  display: inline-flex; align-items: center; gap: 4px;
  background: transparent; border: 0; border-radius: 3px;
  padding: 1px 3px; font: inherit; font-size: 11px;
  color: var(--fg); cursor: pointer;
}
.kind-toggle:hover { background: var(--hover-bg); }
.kind-eye {
  position: relative; display: inline-flex;
  width: 14px; height: 14px; color: var(--fg);
}
.kind-eye svg { width: 14px; height: 14px; display: block; }
.kind-label { user-select: none; }
/* Hidden kind: dim the whole toggle + strike the eye with a slash. */
.kind-toggle[aria-pressed="false"] { color: var(--muted-fg); }
.kind-toggle[aria-pressed="false"] .kind-eye { color: var(--muted-fg); }
.kind-toggle[aria-pressed="false"] .kind-eye::after {
  content: ""; position: absolute; left: -1px; top: 6px;
  width: 16px; height: 1.5px; background: currentColor;
  transform: rotate(-45deg); transform-origin: center;
}
/* "+ New secret" — trailing button in the filter bar. Auth-gated: the
   `hidden` attr (set by syncKindFilterBar) must beat the flex display. */
.kind-add {
  margin-left: auto; flex-shrink: 0;
  width: 20px; height: 18px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: var(--muted-fg);
  border: 1px solid var(--input-border); border-radius: 3px;
  font-size: 14px; line-height: 1; cursor: pointer;
}
.kind-add:hover { background: var(--hover-bg); color: var(--fg); }
.kind-add[hidden] { display: none; }
#search-input {
  flex: 1; min-width: 0; padding: 4px 8px;
  border: 1px solid var(--input-border); border-radius: 3px;
  font-family: inherit; font-size: 13px; outline: none;
  background: var(--bg); color: var(--fg);
}
#search-input:focus { border-color: var(--light-fg); }
#search-clear {
  width: 24px; align-self: stretch;
  border: 1px solid var(--input-border); border-radius: 3px;
  background: var(--bg); color: var(--muted-fg);
  font-size: 16px; cursor: pointer;
  display: flex; align-items: center; justify-content: center; padding: 0;
  flex-shrink: 0; box-sizing: border-box;
}
#search-clear:hover { background: var(--hover-bg); color: var(--fg); }

/* Namespace tree in sidebar */
#entity-list { padding: 4px 0; margin: 0; }
.ns-header { display: flex; align-items: center; padding: 3px 8px; cursor: pointer; user-select: none; }
.ns-header:hover { background: var(--hover-bg); }
/* Workspace highlight (PLATFORM_PLAN §4.4): namespaces the user works in get
   a left accent. No-op without the addon — the class is only added when the
   X-Graphden-Workspace header lists the namespace. */
.ns-header.ns-in-workspace { border-left: 2px solid var(--accent); padding-left: 6px; }
.ns-arrow { width: 16px; font-size: 10px; color: var(--muted-fg); flex-shrink: 0; text-align: center; }
.ns-label { font-size: 13px; font-weight: 600; color: var(--fg); }
/* Item-count chip on the pseudo "(root)" group header. Italic +
   muted so it reads as metadata not a name. */
.ns-count {
  margin-left: 6px;
  font-size: 10px;
  color: var(--muted-fg);
  font-style: italic;
}
.ns-header-pseudo .ns-label { font-style: italic; color: var(--muted-fg); }
.ns-children { padding-left: 16px; }
#entity-list .entity-item { padding: 2px 8px 2px 24px; cursor: pointer;
  display: flex; align-items: center; }
#entity-list .entity-item:hover { background: var(--hover-bg); }
#entity-list .entity-item.selected { background: var(--selected-bg); }
#entity-list .entity-item .name { font-weight: 400; font-size: 13px; }

/* Proactive per-org plan-usage badge (editor-quota.js, task #8-frontend). */
.quota-badge {
  margin: 4px 8px;
  padding: 2px 8px;
  font-size: 11px;
  color: var(--muted-fg);
  border: 1px solid var(--border);
  border-radius: 4px;
  text-align: center;
  cursor: default;
}
.quota-badge-warn { color: var(--warning-fg); border-color: var(--warning-fg); }
.quota-badge-full { color: var(--error-fg); border-color: var(--error-fg); font-weight: 700; }

/* Menu header — asymmetric padding pulls the action buttons closer to
   the right edge while keeping the title comfortably indented on the
   left. */
/* Two stacked rows: a brand row (λ mark + wordmark) and a dedicated
   toolbar row for the JS-mounted actions (prefs / branch / auth). They
   used to share one space-between row, so a wide branch chip + the
   staggered JS mounts made the buttons jump at different heights and
   worse on sidebar resize. Separate rows keep each stable. */
.menu-header { padding: 12px 8px 10px 16px; border-bottom: 2px solid var(--border);
  background: var(--header-bg); color: var(--header-fg);
  display: flex; flex-direction: column; gap: 8px; position: relative; }
.menu-brand { display: flex; align-items: center; gap: 8px; }
.menu-logo { flex: 0 0 auto; display: block; color: var(--accent); }
.menu-header h2 { font-size: 15px; font-weight: 600; margin: 0; letter-spacing: 0.2px; }
.menu-header-actions { display: flex; flex-wrap: wrap; align-items: center;
  gap: 6px; min-height: 26px; }

/* Global utility — many JS/partial surfaces toggle `hidden` (auth-popover
   fields, the ⎋ logout-all button, applyAuthMode's tenant/single-tenant
   switch). Only SCOPED rules existed before, so partial-rendered elements
   carrying the class still displayed — single-tenant popovers showed the
   tenant username/org fields. !important so it beats the display:inline-flex
   component rules at equal specificity. */
.hidden { display: none !important; }

/* Prefs (theme + collapse) buttons — same shape/size as auth lock. */
#prefs-mount { display: inline-flex; align-items: center; gap: 6px; }
.prefs-btn {
  background: transparent; color: var(--header-fg);
  border: 1px solid var(--header-fg); border-radius: 3px;
  width: 26px; height: 26px; padding: 3px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.prefs-btn:hover { background: var(--header-fg); color: var(--header-bg); }

/* Auth lock — sits on the right of the menu header, opens a popover. */
#auth-mount { position: relative; display: inline-flex; align-items: center; }
.auth-lock-btn {
  background: transparent; color: var(--header-fg); border: 1px solid var(--header-fg); border-radius: 3px;
  width: 26px; height: 26px; padding: 3px; cursor: pointer; display: inline-flex;
  align-items: center; justify-content: center;
}
.auth-lock-btn:hover { background: var(--header-fg); color: var(--header-bg); }
.auth-lock-btn.auth-lock-open { background: var(--header-fg); color: var(--header-bg); }
/* "Sign out everywhere" — same chrome as the lock, sits beside it. */
.auth-logout-all-btn {
  background: transparent; color: var(--header-fg); border: 1px solid var(--header-fg); border-radius: 3px;
  width: 26px; height: 26px; padding: 3px; margin-left: 4px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; font-size: 14px;
}
.auth-logout-all-btn:hover { background: var(--header-fg); color: var(--header-bg); }
/* Popover positioned with `position: fixed` (relative to viewport,
   set via JS in `positionAuthPopover`) so it can escape #side-menu's
   scroll context — focusing the input would otherwise cause the
   browser to horizontally scroll the sidebar to reveal it. */
.auth-popover {
  position: fixed; z-index: 300;
  background: var(--bg); color: var(--fg); border: 1px solid var(--fg); border-radius: 3px;
  padding: 8px; min-width: 200px;
  box-shadow: var(--shadow-md);
  display: flex; flex-direction: column; gap: 6px;
}
.auth-popover.hidden { display: none; }
.auth-popover input {
  font-size: 12px; padding: 4px 6px; border: 1px solid var(--fg); border-radius: 3px;
  outline: none; width: 100%; box-sizing: border-box;
  background: var(--bg); color: var(--fg);
}
.auth-popover input:focus { box-shadow: 0 0 0 1px var(--fg) inset; }
/* Wrapper that lets the eye-toggle button float over the right edge
   of the password input. The input gets extra right-padding so the
   typed text doesn't run under the icon. */
/* Username field (multi-tenant login only) sits above the password wrap. */
.auth-username-input:not(.hidden) { margin-bottom: 6px; }
/* Login⇄signup toggle (multi-tenant). */
.auth-mode-toggle { display: block; margin-top: 6px; font-size: 11px; color: var(--accent); cursor: pointer; text-decoration: underline; }
/* Fatal graph-load banner (editor-main.js initGraph catch) — tokenised so
   it themes with everything else instead of the old inline hardcoded red. */
.editor-fatal-banner { position: fixed; top: 0; left: 0; right: 0; z-index: 99999;
  padding: 8px 16px; background: var(--danger-bg); color: var(--on-accent);
  font: 14px sans-serif; }
.auth-input-wrap { position: relative; }
.auth-input-wrap input { padding-right: 28px; }
.auth-toggle-visibility {
  position: absolute; top: 50%; right: 4px; transform: translateY(-50%);
  width: 20px; height: 20px; padding: 0;
  background: transparent; border: 0; border-radius: 2px;
  cursor: pointer; color: var(--muted-fg);
  display: inline-flex; align-items: center; justify-content: center;
}
.auth-toggle-visibility:hover { color: var(--fg); }
.auth-popover-row { display: flex; gap: 6px; }
.auth-popover-btn {
  flex: 1; font-size: 12px; padding: 4px 8px; cursor: pointer;
  background: var(--fg); color: var(--bg); border: 1px solid var(--fg); border-radius: 3px;
}
.auth-popover-btn:hover { background: var(--bg); color: var(--fg); }
.auth-popover-btn-secondary { background: var(--bg); color: var(--fg); }
.auth-popover-btn-secondary:hover { background: var(--fg); color: var(--bg); }
.auth-error {
  font-size: 11px; color: var(--fg); background: var(--hover-bg); border: 1px solid var(--fg);
  padding: 4px 6px; border-radius: 3px;
}

/* Branch selector chip + popover — sits between #prefs-mount and
   #auth-mount in the menu-header-actions row. Chip is text+icon so
   the current branch is always one glance away; non-default branches
   get an accent border so editors never forget they're on a feature
   branch. */
#branch-mount { position: relative; display: inline-flex; align-items: center; }
.branch-chip-btn {
  display: inline-flex; align-items: center; gap: 4px;
  background: transparent; color: var(--header-fg);
  border: 1px solid var(--header-fg); border-radius: 3px;
  padding: 3px 8px; height: 26px; cursor: pointer;
  font: inherit; font-size: 11px; line-height: 1;
  max-width: 140px;
}
.branch-chip-btn span {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.branch-chip-btn:hover {
  background: var(--header-fg); color: var(--header-bg);
}
.branch-chip-btn.branch-chip-non-default {
  /* Stronger affordance when off main — admin needs to see at a glance. */
  background: var(--header-fg); color: var(--header-bg);
}

.branch-popover {
  /* `position: fixed` + JS-positioned via `anchorBelowClamped` so the
     popover can escape the sidebar's scroll context (sidebar is
     280px wide; popover wants ≥ 260px and would clip otherwise). */
  position: fixed; z-index: 300;
  background: var(--bg); color: var(--fg);
  border: 1px solid var(--fg); border-radius: 3px;
  padding: 6px; min-width: 260px; max-width: 360px;
  box-shadow: var(--shadow-md);
  display: flex; flex-direction: column; gap: 6px;
  line-height: 1.3;
}
.branch-popover.hidden { display: none; }
.branch-popover-loading,
.branch-popover-error {
  font-size: 11px; color: var(--muted-fg); padding: 4px 6px;
}
.branch-popover-error {
  color: var(--fg); background: var(--hover-bg); border: 1px solid var(--fg);
  border-radius: 3px;
}
.branch-popover-error.hidden { display: none; }

/* Org-switcher chip (Track B — multi-org). Mirrors the branch chip; shown
   only when the user belongs to >1 org. */
.org-chip-btn {
  display: inline-flex; align-items: center; gap: 4px;
  background: transparent; color: var(--header-fg);
  border: 1px solid var(--header-fg); border-radius: 3px;
  padding: 3px 8px; height: 26px; cursor: pointer;
  font: inherit; font-size: 11px; line-height: 1;
  max-width: 140px;
}
.org-chip-btn span {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.org-chip-btn:hover { background: var(--header-fg); color: var(--header-bg); }
.org-popover {
  position: absolute; z-index: 300;
  background: var(--bg); color: var(--fg);
  border: 1px solid var(--fg); border-radius: 3px;
  padding: 6px; min-width: 160px; max-width: 260px;
  box-shadow: var(--shadow-md);
  display: flex; flex-direction: column; gap: 2px;
}
.org-popover.hidden { display: none; }
.org-popover-item {
  text-align: left; background: transparent; color: var(--fg);
  border: none; border-radius: 3px; padding: 4px 8px; cursor: pointer;
  font: inherit; font-size: 12px;
}
.org-popover-item:hover:not(:disabled) { background: var(--hover-bg); }
.org-popover-item-current { color: var(--muted-fg); cursor: default; }

.branch-popover-list {
  display: flex; flex-direction: column;
  max-height: 240px; overflow-y: auto;
}
.branch-row {
  display: grid;
  grid-template-columns: auto auto 1fr auto;
  align-items: center;
  gap: 6px;
  padding: 4px 6px;
  border-radius: 3px;
  cursor: pointer;
  font-size: 12px;
}
.branch-row:hover { background: var(--hover-bg); }
.branch-row-current {
  background: var(--hover-bg); cursor: default;
}
.branch-row-name { font-weight: 500; }
.branch-row-tag {
  font-size: 10px; padding: 1px 5px; border-radius: 2px;
  background: var(--fg); color: var(--bg);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.branch-row-meta {
  font-size: 10px; color: var(--muted-fg);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  min-width: 0;
}
.branch-row-actions {
  display: inline-flex; gap: 2px; align-items: center;
}
.branch-row-delete,
.branch-row-merge,
.branch-row-diff {
  background: transparent; color: var(--muted-fg);
  border: 0; border-radius: 2px;
  width: 18px; height: 18px; padding: 0; line-height: 1;
  font-size: 13px; cursor: pointer;
}
.branch-row-delete { font-size: 14px; }
.branch-row-delete:hover,
.branch-row-merge:hover,
.branch-row-diff:hover { color: var(--fg); background: var(--bg); }

.branch-popover-create {
  display: flex; gap: 4px;
  border-top: 1px solid var(--border);
  padding-top: 6px;
}
.branch-popover-create input {
  flex: 1; font-size: 11px; padding: 4px 6px;
  border: 1px solid var(--fg); border-radius: 3px;
  outline: none; background: var(--bg); color: var(--fg);
  min-width: 0;
}
.branch-popover-create input:focus { box-shadow: 0 0 0 1px var(--fg) inset; }
.branch-popover-btn {
  font-size: 11px; padding: 4px 10px; cursor: pointer;
  background: var(--fg); color: var(--bg);
  border: 1px solid var(--fg); border-radius: 3px;
}
.branch-popover-btn:hover { background: var(--bg); color: var(--fg); }

/* fn-version history popover — anchored below the ⌛ button on a
   fn-card's row-actions popover. Same visual family as branch-popover
   (shared design tokens) but wider since version rows carry both
   branch name + timestamp + a summary line. */
.fn-versions-popover {
  position: fixed; z-index: 320;
  background: var(--bg); color: var(--fg);
  border: 1px solid var(--fg); border-radius: 3px;
  padding: 6px 8px; min-width: 280px; max-width: 420px;
  box-shadow: var(--shadow-md);
  display: flex; flex-direction: column; gap: 6px;
  font-size: 12px; line-height: 1.3;
}
.fn-versions-popover.hidden { display: none; }
.fn-versions-loading,
.fn-versions-error {
  color: var(--muted-fg); font-size: 11px;
}
.fn-versions-error { color: var(--fg); }
.fn-versions-header {
  font-weight: 600; font-size: 12px;
  border-bottom: 1px solid var(--border); padding-bottom: 4px;
}
.fn-versions-empty {
  color: var(--muted-fg); font-size: 11px; padding: 4px 0;
}
.fn-versions-list {
  display: flex; flex-direction: column;
  max-height: 280px; overflow-y: auto;
  gap: 4px;
}
.fn-versions-row {
  display: flex; flex-direction: column; gap: 2px;
  padding: 4px 6px; border-radius: 3px;
  background: var(--hover-bg);
  cursor: pointer;
}
.fn-versions-row-top {
  display: flex; align-items: baseline; gap: 8px;
}
.fn-versions-branch {
  font-weight: 600;
}
.fn-versions-branch-current {
  background: var(--fg); color: var(--bg);
  padding: 1px 5px; border-radius: 2px;
  font-size: 11px;
}
.fn-versions-ts {
  font-family: var(--mono);
  font-size: 10px; color: var(--muted-fg);
  margin-left: auto;
}
.fn-versions-switch,
.fn-versions-restore {
  font-size: 10px; padding: 1px 6px; cursor: pointer;
  background: transparent; color: var(--fg);
  border: 1px solid var(--border); border-radius: 2px;
}
.fn-versions-switch:hover,
.fn-versions-restore:hover { background: var(--hover-bg); }
.fn-versions-row-meta {
  font-size: 10px; color: var(--muted-fg);
  font-family: var(--mono);
  overflow-wrap: anywhere;
}




/* Inline per-version executions, lazy-loaded on row click. */
.fn-versions-execs {
  margin-top: 4px; padding-top: 4px;
  border-top: 1px dashed var(--border);
}
.fn-versions-execs-empty {
  font-size: 10px; color: var(--muted-fg);
  padding: 2px 0;
}

.fn-versions-execs-list {
  display: flex; flex-direction: column; gap: 2px;
  max-height: 160px; overflow-y: auto;
}
.fn-versions-execs-row {
  display: flex; align-items: baseline; gap: 6px;
  font-size: 10px;
  font-family: var(--mono);
  padding: 2px 0;
}
.fn-versions-execs-status {
  padding: 0 4px; border-radius: 2px;
  background: var(--hover-bg);
  text-transform: uppercase; font-size: 9px; letter-spacing: 0.04em;
}
.fn-versions-execs-status-succeeded { background: var(--fg); color: var(--bg); }
.fn-versions-execs-status-failed,
.fn-versions-execs-status-cancelled { background: var(--fg); color: var(--bg); }
.fn-versions-execs-ts { color: var(--muted-fg); }


/* Merge conflicts modal — full-viewport overlay + centred card.
   Shown when POST /api/branches/:target/merge returns
   `{ok: false, reason: "merge-conflict", conflicts: [...]}`. User
   picks source/target per row and the modal re-submits with
   `:conflict-resolutions`. */
.merge-conflicts-modal {
  position: fixed; inset: 0; z-index: 500;
  display: flex; align-items: center; justify-content: center;
}
.merge-conflicts-modal.hidden { display: none; }
.merge-conflicts-overlay {
  position: absolute; inset: 0;
  background: var(--modal-backdrop-bg);
}
.merge-conflicts-card {
  position: relative;
  background: var(--bg); color: var(--fg);
  border: 1px solid var(--fg); border-radius: 3px;
  padding: 12px 16px;
  min-width: 520px; max-width: 720px;
  max-height: 80vh; overflow-y: auto;
  box-shadow: var(--shadow-md);
  display: flex; flex-direction: column; gap: 10px;
  font-size: 12px; line-height: 1.4;
}
.merge-conflicts-header {
  font-size: 14px; font-weight: 600;
  border-bottom: 1px solid var(--border); padding-bottom: 6px;
}
.merge-conflicts-help {
  font-size: 11px; color: var(--muted-fg);
}
.merge-conflicts-batch {
  display: flex; gap: 6px; align-items: center;
  font-size: 11px; color: var(--muted-fg);
}
.merge-conflicts-batch-label { margin-right: 2px; }
.merge-conflicts-batch-btn {
  font-size: 10px; padding: 2px 8px;
}
.merge-conflicts-rows {
  display: flex; flex-direction: column; gap: 8px;
}
.merge-conflict-row {
  border: 1px solid var(--border); border-radius: 3px;
  padding: 6px 8px;
}
.merge-conflict-row-head {
  display: flex; gap: 8px; align-items: baseline;
  margin-bottom: 4px;
}
.merge-conflict-entity {
  font-weight: 600; font-size: 12px;
  background: var(--hover-bg); padding: 1px 5px; border-radius: 2px;
}
.merge-conflict-id {
  font-family: var(--mono);
  font-size: 10px; color: var(--muted-fg);
}
.merge-conflict-choice {
  display: flex; flex-direction: column; gap: 4px;
  font-size: 11px;
}
.merge-conflict-choice label {
  cursor: pointer; display: flex; gap: 6px; align-items: baseline;
}
.merge-conflict-choice code {
  font-family: var(--mono);
  background: var(--hover-bg); padding: 0 3px; border-radius: 2px;
  color: var(--muted-fg); font-size: 10px;
}
.merge-conflicts-error {
  font-size: 11px; color: var(--fg); background: var(--hover-bg);
  border: 1px solid var(--fg); padding: 4px 6px; border-radius: 3px;
}
.merge-conflicts-error.hidden { display: none; }
.merge-conflicts-actions {
  display: flex; gap: 6px; justify-content: flex-end;
  padding-top: 8px; border-top: 1px solid var(--border);
}
.merge-conflicts-cancel {
  background: var(--bg); color: var(--fg);
}
.merge-conflicts-cancel:hover {
  background: var(--fg); color: var(--bg);
}

/* Branch diff modal — opened from the Δ button in the branch popover.
   Same overlay anatomy as the merge-conflicts modal so the user
   gets a familiar "full-viewport card on dim backdrop" pattern; the
   inner content is grouped by `:change` tag and shows a brief
   per-entity preview. */
.branch-diff-modal {
  position: fixed; inset: 0; z-index: 500;
  display: flex; align-items: center; justify-content: center;
}
.branch-diff-modal.hidden { display: none; }
.branch-diff-overlay {
  position: absolute; inset: 0;
  background: var(--modal-backdrop-bg);
}
.branch-diff-card {
  position: relative;
  background: var(--bg); color: var(--fg);
  border: 1px solid var(--fg); border-radius: 3px;
  padding: 0;
  min-width: 560px; max-width: 800px; width: 70vw;
  max-height: 80vh;
  display: flex; flex-direction: column;
  box-shadow: var(--shadow-md);
  font-size: 12px; line-height: 1.4;
}
.branch-diff-header {
  font-size: 14px; font-weight: 600;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: 8px;
}
.branch-diff-close {
  margin-left: auto;
  background: transparent; color: var(--muted-fg);
  border: 0; border-radius: 2px;
  width: 22px; height: 22px; padding: 0; line-height: 1;
  font-size: 18px; cursor: pointer;
}
.branch-diff-close:hover { background: var(--hover-bg); color: var(--fg); }
.branch-diff-body {
  padding: 8px 16px 12px;
  overflow-y: auto;
  flex: 1;
}
.branch-diff-loading,
.branch-diff-error,
.branch-diff-empty {
  font-size: 12px; color: var(--muted-fg);
  padding: 8px 0;
}
.branch-diff-error {
  color: var(--fg); background: var(--hover-bg);
  border: 1px solid var(--fg); border-radius: 3px;
  padding: 6px 8px;
}
.branch-diff-summary {
  font-size: 11px; color: var(--muted-fg);
  padding-bottom: 6px;
}
.branch-diff-section {
  margin-bottom: 12px;
}
.branch-diff-section-head {
  font-weight: 600; font-size: 12px;
  padding: 4px 0;
  border-bottom: 1px solid var(--border);
  margin-bottom: 6px;
  display: flex; align-items: baseline; gap: 6px;
}
.branch-diff-count {
  background: var(--fg); color: var(--bg);
  padding: 1px 6px; border-radius: 2px;
  font-size: 10px;
}

.branch-diff-rows {
  display: flex; flex-direction: column; gap: 4px;
}
.branch-diff-row {
  padding: 6px 8px; border-radius: 3px;
  background: var(--hover-bg);
  border: 1px solid transparent;
}
.branch-diff-row-clickable { cursor: pointer; }
.branch-diff-row-clickable:hover { border-color: var(--fg); }
.branch-diff-row-head {
  display: flex; align-items: baseline; gap: 8px;
  margin-bottom: 2px;
}
.branch-diff-entity {
  font-weight: 600; font-size: 11px;
  background: var(--bg); padding: 1px 5px; border-radius: 2px;
}
.branch-diff-id {
  font-family: var(--mono);
  font-size: 10px; color: var(--muted-fg);
}
.branch-diff-row-summary {
  font-size: 11px;
  font-family: var(--mono);
  overflow-wrap: anywhere;
}
.branch-diff-row-summary em {
  font-style: normal;
  background: var(--bg); padding: 0 3px; border-radius: 2px;
}
/* Branch-local annotation — appears on diff rows for fns whose
   effective branch-local is true. The diff lists them (the version
   DOES differ), but the resolver filters them out at merge time, so
   the badge tells the user "this won't actually propagate". */
.branch-diff-row-local-badge {
  margin-left: auto;
  font-size: 10px;
  font-style: italic;
  color: var(--muted-fg);
  padding: 1px 5px;
  border: 1px dashed var(--muted-fg);
  border-radius: 2px;
}
.branch-diff-row-local {
  opacity: 0.7;
}
.auth-error.hidden { display: none; }

/* Per-namespace row buttons — ✎ rename + + create-child. Hidden until
   the row is hovered. Both monochrome to match the sidebar palette. */
.ns-row-actions {
  margin-left: auto; display: inline-flex; gap: 4px; align-items: center;
}
/* `createDescriptionBadge` / `createOpenInNewTabButton` set an inline
   `margin-left: 4px` for non-pinned use (fn-overlay rows). Inside the
   sidebar's icon group the container already supplies a 4px `gap`,
   so the extra margin doubles up and the description-badge / ↗ drift
   right of the create-btn-inline siblings — the row's icons read as
   "dancing" at different distances. `!important` is required because
   inline styles otherwise win over external CSS; the alternative
   (changing the JS factories) would either need a per-callsite flag
   or break their default fn-overlay placement. */
.ns-row-actions > * {
  margin-left: 0 !important;
}
.create-btn {
  background: transparent; color: inherit; border: 1px solid currentColor;
  border-radius: 3px; cursor: pointer; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
}
.create-btn:hover { background: currentColor; }
.create-btn:hover svg { color: var(--bg); }
/* Match `applyActionIconBox` (description-badge `i`) so the row's
   icons line up: same 15×15 frame, same 1px border. The action
   buttons stay hover-only — `i` is informational and harmless to
   show always; ✎/+ are actions and should not clutter the row when
   the user is just scanning the tree. */
.create-btn-inline {
  width: var(--icon-size); height: var(--icon-size);
  opacity: 0; transition: opacity 0.1s;
  color: var(--fg);
}
.ns-header:hover .ns-row-actions .create-btn-inline,
.entity-item:hover .ns-row-actions .create-btn-inline { opacity: 1; }
.create-btn-inline:hover { background: var(--fg); color: var(--bg); }

/* Disabled (greyed-out) state for the delete button when the entity
   can't be removed safely. The button stays in the row layout so its
   absence doesn't shift the icon group; tooltip carries the reason.
   Hover doesn't change anything — no background flip, no color change,
   no SVG colour swap — the row's hover rule still raises opacity to
   make the button visible, but everything else stays at the disabled
   palette so it reads as "not clickable". */
.create-btn-inline.create-btn-disabled,
.create-btn-inline.create-btn-disabled:hover,
.create-btn-inline:disabled,
.create-btn-inline:disabled:hover {
  color: var(--light-fg);
  background: transparent;
  cursor: not-allowed;
}
.create-btn-inline.create-btn-disabled:hover svg,
.create-btn-inline:disabled:hover svg {
  color: var(--light-fg);
}

/* Create-child popover menu (ns / fn). Floated above the sidebar. */
.create-menu {
  background: var(--bg); color: var(--fg); border: 1px solid var(--fg); border-radius: 3px;
  min-width: 180px; padding: 4px; display: flex; flex-direction: column; gap: 2px;
  box-shadow: var(--shadow-md);
}
.create-menu-item {
  background: transparent; border: 0; border-radius: 2px;
  padding: 6px 10px; text-align: left; font-size: 12px; cursor: pointer;
  color: inherit;
}
.create-menu-item:hover { background: var(--fg); color: var(--bg); }

/* Inline input row — used both for create (indented under a parent ns)
   and for rename (replaces the ns-header label in place). */
.inline-input-row {
  display: flex; align-items: center; gap: 4px;
  padding: 4px 6px;
}
.inline-input {
  flex: 1; font-size: 12px; padding: 3px 6px;
  border: 1px solid var(--fg); border-radius: 3px; outline: none;
  background: var(--bg); color: var(--fg);
}
.inline-input:focus { box-shadow: 0 0 0 1px var(--fg) inset; }
.inline-input:disabled { opacity: 0.6; }
.inline-btn {
  width: 22px; height: 22px; padding: 0;
  background: var(--bg); color: var(--fg); border: 1px solid var(--fg);
  border-radius: 3px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.inline-btn:hover { background: var(--fg); color: var(--bg); }
.inline-btn-save:hover { background: var(--fg); color: var(--bg); }
.inline-btn:disabled { opacity: 0.4; cursor: default; }
.inline-error {
  font-size: 10px; color: var(--fg);
  background: var(--hover-bg); border: 1px solid var(--fg); border-radius: 3px;
  padding: 1px 5px; display: none;
}

/* "+ New namespace" button at the bottom of the sidebar. Side margins
   keep the dashed border off the sidebar edges. */
.create-root-ns-btn {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  width: calc(100% - 24px); box-sizing: border-box;
  padding: 10px 12px; margin: 8px 12px 12px;
  background: var(--bg); color: var(--fg);
  border: 1px dashed var(--fg); border-radius: 3px;
  font-size: 12px; cursor: pointer;
}
.create-root-ns-btn:hover { background: var(--fg); color: var(--bg); }
.create-root-ns-plus { display: inline-flex; align-items: center; }
.create-root-ns-text { font-weight: 500; }

/* Node overlay for interactive ancestor list */
.node-overlay { pointer-events: auto; z-index: 10; }
.node-overlay .ancestor-line {
  /* Each row must occupy one full text-line slot even when the content is
     empty — e.g. the top row of an anonymous fn keeps its black "no own name"
     band instead of collapsing. With global box-sizing: border-box the
     min-height must include vertical padding (4+4) + line-height (13) = 21. */
  min-height: 21px;
  line-height: 13px;
  transition: color 0.1s, font-weight 0.1s;
}

/* Return-type strip on root fn cards — sits at the bottom of the
   card, right above the optional/HOF strips and drag handle. Visual
   reuses the same compact treatment (1-px dashed top border + small
   italic font) so it feels like a peer of those informational strips
   rather than a primary control. Editable on the root card only;
   adds a hover-bg + cursor when so. */
.return-type-strip {
  padding: 2px 8px;
  color: var(--muted-fg);
  font-size: 10px;
  font-style: italic;
  border-top: 1px dashed var(--input-border);
  background: var(--card-bg);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Flex variant used when the strip wraps the display text in a span so
   a trailing provenance button stays hit-testable past long types. The
   text span takes the remaining width with its own ellipsis; the
   button is a non-shrinking sibling at the end. */
.return-type-strip-flex {
  display: flex;
  align-items: center;
  gap: 4px;
}
.return-type-strip-flex .return-type-strip-text {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.return-type-strip-editable {
  cursor: pointer;
}
.return-type-strip-editable:hover {
  background: var(--hover-bg);
  color: var(--fg);
}
/* Refinement variant: stack `(constraint)` under the base in the same
 * spirit as the arg-overlay's stacked refinement chip. Arrow + base sit
 * on the first text run; the constraint span wraps to a second line at
 * smaller weight + reduced opacity. */
.return-type-strip-refine .return-type-strip-arrow,
.return-type-strip-refine .return-type-strip-base {
  display: inline;
}
.return-type-strip-refine .return-type-strip-constraint {
  display: block;
  padding-left: 1em;
  font-size: 9px;
  opacity: 0.7;
}

/* Edit-this-type-row strip — peer of `.return-type-strip-editable`,
   surfaces "open the type-edit form" for type-rows on their nav-root
   card. Italic muted text matches the other footer strips; hover
   raises contrast so the affordance is unambiguous. */
.edit-type-strip {
  padding: 2px 8px;
  color: var(--muted-fg);
  font-size: 10px;
  font-style: italic;
  border-top: 1px dashed var(--input-border);
  background: var(--card-bg);
  cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.edit-type-strip:hover,
.edit-type-strip:focus-visible {
  background: var(--hover-bg);
  color: var(--fg);
  outline: none;
}

/* Effect badges — small per-category chips on a fn's footer. Pure
   fns omit the strip entirely; effectful fns show colour-coded
   pills (one per category) so the type of effect is visible at a
   glance without opening the full /api/types entry. */
.effects-strip {
  padding: 2px 6px;
  border-top: 1px dashed var(--input-border);
  background: var(--card-bg);
  display: flex; flex-wrap: wrap; gap: 3px;
  align-items: center;
}
.effects-chip {
  display: inline-block;
  padding: 0 5px;
  font-family: SF Mono, Monaco, monospace;
  font-size: 8.5px;
  line-height: 12px;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  border: 0;
  border-radius: 2px;
  color: var(--effect-chip-fg);
  background: var(--muted-fg);
  cursor: pointer;
}
.effects-chip:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.effects-chip:hover {
  filter: brightness(1.08);
}
.effects-chip-db      { background: var(--effect-chip-db); }       /* storage CRUD */
.effects-chip-env     { background: var(--effect-chip-env); }      /* env-var read */
.effects-chip-io      { background: var(--effect-chip-io); }       /* file/classpath */
.effects-chip-network { background: var(--effect-chip-network); }  /* HTTP socket */
.effects-chip-time    { background: var(--effect-chip-time); }     /* wall-clock */
.effects-chip-random  { background: var(--effect-chip-random); }   /* non-determinism */
.effects-chip-process { background: var(--effect-chip-process); }  /* spawns supervised background work */
.effects-chip-raw-sql { background: var(--effect-chip-raw-sql); }  /* raw SQL — bypasses org-scoped storage */

/* Drift = computed effect that the author DIDN'T declare in
   `:expects-effects`. Ring it red so it stands out without changing
   the category colour. */
.effects-chip-drift {
  box-shadow: 0 0 0 1.5px var(--error-fg);
}
/* Ghost = declared but not computed yet. Outlined-only — keeps the
   colour signal but signals "expected, not seen" via the dashed ring. */
.effects-chip-ghost {
  background: transparent !important;
  color: var(--muted-fg);
  outline: 1px dashed currentColor;
  outline-offset: -1px;
}

/* Pencil at the right of the effects-strip — opens the inline-edit
   popover that toggles `:expects-effects`. Text mode ("declare
   effects…") used when no contract is set yet, glyph (`✎`) when one
   exists. Same muted-fg → fg hover affordance as the other footer
   strip controls. */
.effects-strip-edit {
  margin-left: auto;
  padding: 0 6px;
  font-size: 10px;
  background: transparent;
  border: none;
  color: var(--muted-fg);
  cursor: pointer;
  font-style: italic;
}
.effects-strip-edit:hover,
.effects-strip-edit:focus-visible {
  color: var(--fg);
  outline: none;
}

/* Effect-contract picker — rendered inside the inline-edit popover.
   Two-row layout: mode radio + 2-column checkbox grid. Disabled
   state (no-contract mode) dims the checkboxes so the user sees
   they're not currently consulted. */
.expects-effects-edit {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 220px;
}
.expects-effects-mode-row {
  display: flex;
  gap: 12px;
  font-size: 11px;
}
.expects-effects-mode-row label {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  cursor: pointer;
}
.expects-effects-grid {
  display: grid;
  grid-template-columns: repeat(2, auto);
  gap: 4px 12px;
  font-size: 11px;
}
.expects-effects-checkbox {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  cursor: pointer;
}
.expects-effects-checkbox input:disabled + * { color: var(--muted-fg); }

/* Namespace badge `ns` — left slot in the row-actions popover, hover
   tinges to match the action-icon framework. The matching action-
   icon-disabled / action-icon-danger variants live where the rest
   of `.action-icon` styling is declared. */
.namespace-badge {
  opacity: 0.85;
  transition: opacity 0.1s, background-color 0.1s, color 0.1s;
}
.namespace-badge:hover {
  opacity: 1;
  background-color: color-mix(in srgb, currentColor 28%, transparent);
}


/* Sidebar `i` description badge + ↗ open-in-new-tab — both hidden
   until the row is hovered, matching the ✎ / + buttons. The
   card-level `i`/↗ (inside fn-overlays) keep their always-visible
   defaults; the rules below only narrow the icons that live in the
   namespace tree. */
.ns-header .description-badge,
.entity-item .description-badge,
.entity-item .open-in-new-tab.sidebar-action {
  opacity: 0;
  transition: opacity 0.1s, background-color 0.1s;
}
.ns-header:hover .description-badge,
.entity-item:hover .description-badge,
.entity-item:hover .open-in-new-tab.sidebar-action {
  opacity: 0.85;
}
.ns-header .description-badge:hover,
.entity-item .description-badge:hover,
.entity-item .open-in-new-tab.sidebar-action:hover {
  opacity: 1;
}

/* More-actions trigger ("⋯") — always visible, sits at slot r-1 of
   every fn-row. Hovering / clicking opens the row-actions popover
   (rendered OUTSIDE the card silhouette, see editor-row-actions.js)
   that hosts the ns / i / ↗ / ✎ / × / + / ✕ glyphs. The popover
   itself uses the .row-actions-popover class. */
.more-actions-trigger {
  opacity: 0.55;
  transition: opacity 0.1s, background-color 0.1s;
}
.more-actions-trigger:hover {
  opacity: 1;
  background: var(--hover-bg);
}

/* Capability gating (PLATFORM_PLAN §4.3) — per-action. The addon reports
   the caller's capabilities via X-Graphden-Capabilities; the fetch wrap sets
   gd-no-write / gd-no-execute body classes. No-op without the addon (header
   absent → classes never set → editor unchanged). Server-side enforcement
   still applies; this is the matching UX so users don't see what they can't
   do.

   A fully read-only caller (no write AND no execute) loses the whole
   entry; otherwise the trigger stays for the read actions (i / ↗ / ⌛) and
   only the disallowed per-action items are hidden. */
body.gd-no-write.gd-no-execute .more-actions-trigger {
  display: none !important;
}
body.gd-no-write [data-action="rename-fn"],
body.gd-no-write [data-action="extend-fn"],
body.gd-no-write [data-action="delete-fn"],
body.gd-no-write [data-action="add-mi-parent"],
body.gd-no-write [data-action="remove-mi-parent"],
body.gd-no-write [data-action="namespace-move"],
body.gd-no-write [data-action="change-use-site-value"],
body.gd-no-write [data-action="remove-use-site-binding"],
body.gd-no-write [data-action="service-settings"] {
  display: none !important;
}
body.gd-no-execute [data-action="run-fn"] {
  display: none !important;
}

/* Row-actions popover — floats outside the card to the right (or
   left if there's no room). Brief fade-in masks the position-shift
   when zoom changes apply scale/transform mid-frame. */
.row-actions-popover {
  transition: opacity 0.08s ease;
}
.row-actions-popover .description-badge,
.row-actions-popover .open-in-new-tab,
.row-actions-popover .action-icon,
.row-actions-popover .namespace-badge {
  opacity: 0.95;
  transition: opacity 0.1s, background-color 0.1s;
}
.row-actions-popover .description-badge:hover,
.row-actions-popover .open-in-new-tab:hover,
.row-actions-popover .action-icon:hover,
.row-actions-popover .namespace-badge:hover {
  opacity: 1;
}

/* Arg-value edit popover — small floating panel anchored below the
   clicked value box. Theme-aware via the same vars as the rest of
   the editor; uses `--card-*` so the popover visually matches a
   graph node rather than a tooltip. */
/* Shared chrome for every floating editor surface — arg-value /
   arg-rename / arg-type / fn-rename / fn-return-type /
   free-arg-bind / namespace-move popovers, the parent-set editor,
   the fn-picker, and the namespace-picker. Container differences
   (size, special accent stripe, mono vs system font) live in the
   per-popover blocks below; this rule centralises position,
   surface colours, border, shadow, padding, and the column flex
   so a future popover only needs to set its own min-width and
   accent. */
.arg-value-edit-popover,
.fn-picker-popover {
  position: fixed; z-index: 10001;
  background: var(--card-bg); color: var(--card-fg);
  border: 1px solid var(--card-border); border-radius: 4px;
  box-shadow: var(--shadow-xl);
  padding: 8px;
  display: flex; flex-direction: column; gap: 6px;
  font-family: 'SF Mono', 'Monaco', 'Inconsolata', monospace;
}
.arg-value-edit-popover {
  min-width: 220px;
  max-width: min(420px, 90vw);
}
.arg-value-edit-input {
  font-family: inherit; font-size: 12px;
  padding: 4px 6px; border-radius: 3px;
  border: 1px solid var(--card-border);
  background: var(--bg); color: var(--fg);
  outline: none; box-sizing: border-box; width: 100%;
}
.arg-value-edit-input:focus { box-shadow: 0 0 0 1px var(--fg) inset; }
.arg-value-edit-buttons { display: flex; gap: 6px; justify-content: flex-end; }
.arg-value-edit-btn {
  font-family: inherit; font-size: 11px;
  padding: 3px 10px; border-radius: 3px; cursor: pointer;
  background: var(--fg); color: var(--bg);
  border: 1px solid var(--fg);
}
.arg-value-edit-btn:hover { opacity: 0.85; }
.arg-value-edit-btn:disabled { opacity: 0.5; cursor: default; }
.arg-value-edit-btn-secondary {
  background: transparent; color: var(--fg);
}
/* Danger variant for the popover button row — used by the Delete
   button on the value-edit popover (drops the binding so the slot
   reverts to a free-arg). Floated to the LEFT of the row so it's
   visually separated from the commit/dismiss pair on the right. */
.arg-value-edit-btn-danger {
  background: transparent; color: var(--error-fg);
  border-color: color-mix(in srgb, var(--error-fg) 50%, var(--card-border));
  margin-right: auto;
}
.arg-value-edit-btn-danger:hover {
  background: color-mix(in srgb, var(--error-fg) 12%, transparent);
}
/* Persistent error panel inside edit popovers — used by reparent /
   value-edit / type-edit flows. Bumped from "tiny red text" to a
   structured panel: tinted background, left accent, "!" icon. The
   message stays until the user fixes the cause and saves, or
   closes the popover. Screen readers should announce it via
   role="alert" set in JS at the call site. */
.arg-value-edit-error {
  display: flex; align-items: flex-start; gap: 6px;
  padding: 6px 8px;
  margin-top: 4px;
  background: color-mix(in srgb, var(--error-fg) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--error-fg) 35%, transparent);
  border-left: 3px solid var(--error-fg);
  border-radius: 3px;
  font-size: 11.5px; line-height: 1.4;
  color: var(--card-fg);
  overflow-wrap: anywhere;
}
.arg-value-edit-error::before {
  content: '!';
  flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: var(--error-fg); color: var(--card-bg);
  font-weight: 700; font-size: 11px;
  line-height: 1;
}

/* Type-hint line above the value input. Subtle italic so it reads
   as metadata, not as a label that competes with the input. */
.arg-value-edit-hint {
  font-size: 11px; color: var(--muted-fg);
  font-style: italic;
  padding: 2px 0; opacity: 0.85;
}

/* Live-validation status line below the input. ✓ / ✗ + short
   message; mirrors what the backend would say on save. */
.arg-value-edit-status {
  font-size: 11px; padding: 2px 0; min-height: 14px;
  font-family: var(--mono);
}
.arg-value-edit-status.ok  { color: var(--success-fg); }
.arg-value-edit-status.err { color: var(--error-fg); }

/* Value-form — the type-aware editor form fetched from
   /api/value-form and rendered into the value-edit popover. The host
   is a scroll container so a tall textarea / record form can't push
   the Save/Cancel row off-screen. */
.value-form-host {
  display: flex; flex-direction: column; gap: 6px;
  max-height: 60vh; overflow-y: auto;
  outline: none;
}
.value-form-loading {
  font-size: 11px; color: var(--muted-fg);
  font-style: italic; padding: 4px 0;
}
.value-form-error {
  font-size: 11.5px; color: var(--error-fg);
  padding: 4px 0;
}
/* Textarea control (long text / JSON) — user-resizable, monospace. */
textarea.arg-value-edit-input {
  resize: vertical; min-height: 64px;
  font-family: var(--mono);
  line-height: 1.4;
}
/* Record / composite field grouping inside a value-form — a bordered
   fieldset with one stacked label+control per field. */
.value-form-group {
  display: flex; flex-direction: column; gap: 6px;
  border: 1px solid var(--card-border);
  border-radius: 3px; padding: 6px;
}
.value-form-field {
  display: flex; flex-direction: column; gap: 2px;
}
.value-form-label {
  font-size: 11px; color: var(--muted-fg);
}
/* Union — a branch <select> stacked over one sub-form per branch.
   Inactive branches carry the `hidden` attribute; no `display` is set
   on the branch so the UA `[hidden]` rule keeps working. */
.value-form-union {
  display: flex; flex-direction: column; gap: 6px;
}

/* Tier-2 custom-widget mount point — a registered JS widget (e.g. the
   `rating` slider) builds its control inside `.value-form-widget`. */
.value-form-widget {
  display: flex; align-items: center; gap: 8px;
}
.value-form-widget-range { flex: 1; }
.value-form-widget-readout {
  font-size: 11px; color: var(--muted-fg);
  font-family: var(--mono);
  white-space: nowrap;
}

/* Read-only value viewer — a singleton info-popover showing the
   type-aware form (controls disabled) for a structurally read-only
   arg row. Surface mirrors the mismatch-explainer info-popover. */
.value-viewer {
  position: fixed; z-index: 10001;
  display: none;
  background: var(--card-bg); color: var(--card-fg);
  border: 1px solid var(--card-border);
  border-radius: 4px;
  box-shadow: var(--shadow-lg);
  padding: 8px 10px;
  min-width: 240px; max-width: min(420px, 90vw);
  font-family: inherit; font-size: 12px; line-height: 1.4;
}
.value-viewer.visible { display: block; }
.value-viewer-header {
  display: flex; align-items: center; justify-content: space-between;
  border-bottom: 1px solid var(--light-border);
  margin-bottom: 6px; padding-bottom: 4px;
}
.value-viewer-title { font-weight: 600; font-size: 12px; }
.value-viewer-close {
  width: 20px; height: 20px; padding: 0;
  background: transparent; color: var(--card-fg);
  border: 0; border-radius: 3px;
  font-size: 18px; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  opacity: 0.65;
}
.value-viewer-close:hover { opacity: 1; background: var(--overlay-pressed); }

/* Type-mismatch indicator on arg-overlays. Surfaces broken bindings
   directly on the graph view without needing to open the value-edit
   popover. Box-shadow rather than border so the ring sits OUTSIDE
   the overlay's existing layout box (no width jitter). */
.arg-overlay-mismatch {
  box-shadow: 0 0 0 1.5px var(--error-fg) !important;
}

/* Arg type-chip — small text label "[int]" / "[fn]" docked to the
   right edge of an arg-overlay or trailing an edge-label. Click flips
   the arg's type, optionally chaining into the fn-picker. */
.arg-type-chip {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0 4px; margin-left: 4px;
  font-family: inherit; font-size: 9px; line-height: 14px;
  border: 1px solid currentColor; border-radius: 3px;
  cursor: pointer; opacity: 0.7;
  pointer-events: auto;
}
.arg-type-chip:hover { opacity: 1; }
/* Read-only variant — no hover-up because it's not actionable. The
   parent's `--muted-fg` × 0.7 opacity lands at ~#888 on white, which
   the a11y MCP flags as 3.54-contrast against the canvas. Pin
   opacity at full and the colour at a token darker than its
   neighbour `currentColor` (which is itself muted) so the chip
   stays subordinate to actionable chips but clears WCAG 2 AA. */
.arg-type-chip-readonly {
  cursor: default;
  opacity: 1;
  color: var(--fg);
}
.arg-type-chip-readonly:hover { opacity: 1; }
/* When a read-only chip carries a `title` (always set when the
   compact rich-type label differs from the full type spelled-out
   via `formatTypeHumanReadable`), surface that via cursor: help so
   the user knows there's more info on hover. The displayed text
   often gets truncated to `(request)→ring-res…` — the full type
   only lives in the title attribute. */
.arg-type-chip-readonly[title]:hover { cursor: help; }

/* Effect-constraint summary row inside a fn-type inline-expand panel.
 * Shows the slot's allowed effect-set ("eff: pure" or "eff: <chips>")
 * in read-only mode — separate from the editable tightening widgets
 * that live below it when the binding is mutable. */
.type-inline-effects-readonly {
  display: flex; align-items: center; gap: 6px;
  padding: 4px 8px;
  font-size: 10px;
  color: var(--muted-fg);
}
.type-inline-effects-label {
  font-style: italic;
  opacity: 0.8;
}
.type-inline-effects-pure {
  font-size: 9px;
  padding: 0 6px;
  border: 1px solid currentColor;
  border-radius: 8px;
  opacity: 0.85;
  color: var(--fg);
  background: var(--sidebar-bg);
}

/* Refinement chip — stacked two-line variant. The base type sits on
 * top at the chip's normal weight; the constraint sits below at a
 * smaller weight + reduced opacity so the visual reads "this is a
 * subtype of <base>, narrowed by <constraint>" without forcing the
 * eye to parse `int (> 0)` as a single phrase. */
.arg-type-chip-refine {
  flex-direction: column;
  line-height: 11px;
  padding: 1px 4px;
}
.arg-type-chip-refine .arg-type-chip-refine-base {
  font-size: 9px;
}
.arg-type-chip-refine .arg-type-chip-refine-constraint {
  font-size: 8px;
  opacity: 0.7;
  margin-top: -1px;
}

/* Narrowed chip — when a binding-level override / inherited override /
 * ref-return narrowing produced a type whose declared slot-base is
 * different from the displayed text, append a `< :base` line below the
 * chip. Same column layout as the refinement variant so the visual
 * grammar stays consistent ("this is a subtype of X" reads top→bottom). */
.arg-type-chip-narrowed {
  flex-direction: column;
  line-height: 11px;
  padding: 1px 4px;
}
.arg-type-chip-narrowed .arg-type-chip-narrowed-base {
  font-size: 8px;
  opacity: 0.7;
  margin-top: -1px;
  font-style: italic;
}

/* Sequence-item bracket chrome — wraps an `.arg-type-chip` so it reads
   as `[elem-type]` (graphden's `[:list T]` structural notation) when
   the edge represents one item of a sequence-bound slot. The brackets
   are purely visual; the chip inside keeps its click target / inline-
   expand / type-edit behaviour. Lower opacity than the chip itself so
   they read as container chrome, not a sibling type. */
.arg-type-chip-list-bracket {
  display: inline-flex;
  align-items: center;
  gap: 1px;
  margin-left: 4px;
  font-family: inherit;
  font-size: 9px;
  line-height: 14px;
}
.arg-type-chip-list-bracket > .arg-type-chip { margin-left: 0; }
.arg-type-chip-list-bracket::before {
  content: '[';
  opacity: 0.65;
}
.arg-type-chip-list-bracket::after {
  content: ']';
  opacity: 0.65;
}

/* Type-create popover — opens from sidebar's "+ New type…" menu.
   Renders a kind picker first (refinement/record/union/variant/list)
   then swaps to the per-kind form on selection. Same visual idiom
   as the type-explainer popover: card-style frame, header strip,
   form body, action-button row. */
.type-create-popover {
  display: none;
  position: fixed;
  z-index: 100;
  width: 320px;
  background: var(--card-bg);
  color: var(--card-fg);
  border: 1px solid var(--card-border);
  border-radius: 6px;
  box-shadow: var(--shadow-lg);
  padding: 0;
  font-size: 11px;
}
.type-create-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 6px 8px;
  border-bottom: 1px solid var(--light-border);
  background: var(--header-bg);
  color: var(--header-fg);
  border-radius: 6px 6px 0 0;
}
.type-create-title { font-weight: 600; font-size: 11px; }
.type-create-close {
  background: transparent; color: inherit;
  border: none; cursor: pointer; padding: 0 4px;
  font-size: 16px; line-height: 1;
}
.type-create-close:hover { opacity: 0.7; }
.type-create-form {
  display: flex; flex-direction: column; gap: 8px;
  padding: 8px;
}
.type-create-field {
  display: flex; flex-direction: column; gap: 2px;
}
.type-create-field-label {
  font-size: 10px; color: var(--muted-fg);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.type-create-input {
  font-family: inherit; font-size: 11px;
  padding: 4px 6px;
  border: 1px solid var(--input-border);
  border-radius: 3px;
  background: var(--bg); color: var(--fg);
}
.type-create-input-mono {
  font-family: 'SF Mono', Monaco, monospace;
}
.type-create-textarea {
  font-family: 'SF Mono', Monaco, monospace;
  resize: vertical;
  min-height: 60px;
}
.type-create-input:focus-visible { outline: 2px solid var(--accent); }

/* Variant / record pair-rows — two-column "(tag, type)" / "(name,
   type)" widget with per-row × remove and a "+ add row" button at
   the bottom. Replaces the freeform `tag: type\n` textarea so the
   author doesn't need to know any line syntax. */
.type-create-pair-list {
  display: flex; flex-direction: column; gap: 4px;
}
.type-create-pair-rows {
  display: flex; flex-direction: column; gap: 3px;
}
.type-create-pair-row {
  display: flex; gap: 4px; align-items: center;
  background: var(--bg);
}
.type-create-pair-row-dragging {
  opacity: 0.4;
}
.type-create-pair-drag {
  width: 16px; flex-shrink: 0;
  font-size: 12px; line-height: 1;
  color: var(--muted-fg);
  cursor: grab;
  user-select: none;
  text-align: center;
}
.type-create-pair-drag:active { cursor: grabbing; }
.type-create-pair-drag:hover { color: var(--fg); }
.type-create-pair-key,
.type-create-pair-val { flex: 1; min-width: 0; }
.type-create-pair-rm {
  width: 22px; height: 22px;
  flex-shrink: 0;
  border: 1px solid var(--input-border);
  border-radius: 3px;
  background: var(--bg); color: var(--muted-fg);
  font-size: 13px; line-height: 1;
  cursor: pointer;
}
.type-create-pair-rm:hover {
  color: var(--error-fg);
  border-color: var(--error-fg);
}
.type-create-pair-add {
  align-self: flex-start;
  font-family: inherit; font-size: 10px;
  padding: 2px 8px;
  border: 1px dashed var(--input-border);
  border-radius: 3px;
  background: transparent; color: var(--muted-fg);
  cursor: pointer;
}
.type-create-pair-add:hover {
  color: var(--fg); border-color: var(--fg);
}

/* Union branches input — single-line full-width without a "branches"
   label above it (the placeholder + form header carry the meaning). */
.type-create-input-fullrow { width: 100%; }

/* Type-create kind tabs — segmented control at the top of the
   unified create form. Saves a click vs the prior 2-step picker
   (kind menu → form) and keeps name/description carried across
   tab switches so the user doesn't retype while exploring kinds. */
.type-create-tabs {
  display: flex;
  border-top: 1px solid var(--card-border);
  border-bottom: 1px solid var(--card-border);
  background: var(--card-bg);
}
.type-create-tab {
  flex: 1;
  font-family: inherit; font-size: 10px;
  padding: 5px 4px;
  background: transparent; border: none;
  color: var(--muted-fg);
  cursor: pointer;
  border-bottom: 2px solid transparent;
}
.type-create-tab:hover { color: var(--fg); }
.type-create-tab-active {
  color: var(--fg);
  border-bottom-color: var(--accent);
  font-weight: 600;
}

/* Refinement constraint builder — op-dropdown + value-input rows,
   stacked with optional `:and` / `:or` combinator visible only when
   >1 row. Mirrors the pair-row idiom (× per row, + add at the
   bottom) so the form has one consistent multi-row UX. */
.refinement-builder {
  display: flex; flex-direction: column; gap: 4px;
}
.refinement-rows {
  display: flex; flex-direction: column; gap: 3px;
}
.refinement-row {
  display: flex; gap: 4px; align-items: center;
}
.refinement-op {
  width: 70px; flex-shrink: 0;
  font-family: inherit; font-size: 11px;
  padding: 3px 4px;
  border: 1px solid var(--input-border);
  border-radius: 3px;
  background: var(--bg); color: var(--fg);
}
.refinement-val { flex: 1; min-width: 0; }
.refinement-combinator {
  display: flex; align-items: center; gap: 4px;
  font-size: 10px; color: var(--muted-fg);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.refinement-combinator-select {
  font-family: inherit; font-size: 11px;
  padding: 2px 4px;
  text-transform: lowercase;
  border: 1px solid var(--input-border);
  border-radius: 3px;
  background: var(--bg); color: var(--fg);
}
.refinement-advanced-wrap {
  display: flex; justify-content: flex-end;
}
.refinement-advanced-toggle {
  font-family: inherit; font-size: 10px;
  padding: 2px 6px;
  background: transparent;
  border: none;
  color: var(--muted-fg);
  cursor: pointer;
}
.refinement-advanced-toggle:hover {
  color: var(--fg);
}

.type-create-error {
  font-size: 10px;
  color: var(--error-fg);
  white-space: pre-wrap;
}
.type-create-actions {
  display: flex; justify-content: space-between;
  gap: 6px; margin-top: 4px;
}
.type-create-back,
.type-create-submit {
  font-family: inherit; font-size: 11px;
  padding: 4px 10px;
  border-radius: 3px; cursor: pointer;
}
.type-create-back {
  background: var(--card-bg); color: var(--muted-fg);
  border: 1px solid var(--light-border);
}
.type-create-back:hover { background: var(--hover-bg); }
.type-create-submit {
  background: var(--header-bg); color: var(--header-fg);
  border: 1px solid var(--header-bg);
  font-weight: 600;
}
.type-create-submit:hover { opacity: 0.85; }
.type-create-submit:disabled { opacity: 0.5; cursor: not-allowed; }

/* Inline type-chip expansion — appears as a floating card below
   the chip when the user clicks an expandable chip. Reveals the
   type's constituent parts (refine→base+constraint, list→element,
   union→branches, record→fields) as a small indented tree. Same
   visual vocabulary as the chip itself; no special colours/borders. */
.type-chip-expandable {
  /* Chip itself IS the click target for expansion — give it a
     subtle dotted underline so users see the chip is interactive
     beyond just opening a popover. */
  text-decoration-line: underline;
  text-decoration-style: dotted;
  text-decoration-color: var(--muted-fg);
  text-underline-offset: 2px;
}
/* Expandable chips carry `role="button"` + `tabindex` — give keyboard
   focus a visible ring (WCAG 2.4.7). */
.type-chip-expandable:focus-visible,
.type-inline-chip-expandable:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.type-inline-header {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px;
  padding-bottom: 2px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--light-border);
}
.type-inline-header-name {
  font-weight: 600;
  font-size: 10px;
  color: var(--card-fg);
}
/* Cycle indicator — a chip rendered in place of an expandable one
   when the type would recurse back to an ancestor in the open chain
   (e.g. `:tree = [:list :tree]`). Read-only; clicking does nothing,
   the `↻` glyph + name says "you've seen this; the loop closes here". */
.type-inline-chip-cycle {
  font-style: italic;
  color: var(--muted-fg);
  border-color: var(--light-border);
  background: var(--card-row-highlight);
  cursor: default;
}

/* Structural kind tag — Map / Record / Tuple / Refinement / Union /
   Function. Sits next to the name in the inline-expand header so an
   anonymous structural type isn't mistaken for the other kinds with
   similar-looking expanded bodies. */
.type-inline-header-kind {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted-fg);
  padding: 0 4px;
  border: 1px solid var(--light-border);
  border-radius: 2px;
}
.type-inline-header-edit {
  padding: 1px 6px;
  font-size: 9px;
  font-family: inherit;
  line-height: 1.2;
  border: 1px solid var(--card-border);
  border-radius: 3px;
  background: var(--card-bg);
  color: var(--card-fg);
  cursor: pointer;
}
.type-inline-header-edit:hover,
.type-inline-header-edit:focus-visible {
  background: var(--hover-bg);
  outline: none;
}
.type-inline-header-edit:focus-visible {
  box-shadow: 0 0 0 2px var(--accent);
}
/* "↳ provenance" link in the inline-panel header — opens the
   dedicated provenance popover. Same baseline as the Change-type
   button but reads as a link (text-coloured, no border) since it
   navigates rather than mutates. */
.type-inline-header-provenance {
  padding: 1px 6px;
  font-size: 9px;
  font-family: inherit;
  line-height: 1.2;
  border: none;
  background: transparent;
  color: var(--accent);
  cursor: pointer;
}
.type-inline-header-provenance:hover,
.type-inline-header-provenance:focus-visible {
  background: var(--hover-bg);
  text-decoration: underline;
  outline: none;
}
.type-inline-header-provenance:focus-visible {
  box-shadow: 0 0 0 2px var(--accent);
}
.type-inline-description {
  margin: 0 0 4px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--light-border);
  font-size: 10px;
  color: var(--muted-fg);
  font-style: italic;
}
/* Promote-anonymous button — appears in the inline-expand panel
   when the type is anonymous (no name) and the user has edit
   rights. Naming the type creates no copy — the existing fn-row
   just gets a `:name`, so every callsite still points at it. */
.type-inline-promote { margin-top: 6px; }
.type-inline-promote-btn {
  font-family: inherit; font-size: 10px;
  padding: 3px 8px;
  border: 1px dashed var(--muted-fg);
  border-radius: 3px;
  background: transparent;
  color: var(--muted-fg);
  cursor: pointer;
}
.type-inline-promote-btn:hover,
.type-inline-promote-btn:focus-visible {
  background: var(--hover-bg);
  color: var(--fg);
  border-color: var(--fg);
  outline: none;
}

/* "Used by N" back-link footer in the type-expansion panel. Lists
   every fn that references this type-row — clickable to navigate. */
/* "Resolved via" — the type-resolution priority chain in the
 * inline-expand panel. Winning tier highlighted; the rest dimmed. */
.type-inline-resolution {
  margin-top: 6px;
  padding-top: 4px;
  border-top: 1px solid var(--light-border);
}
.type-inline-resolution-head {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--light-fg);
  margin-bottom: 2px;
}
.type-inline-resolution-row {
  display: flex; align-items: baseline; gap: 6px;
  font-size: 10px;
  white-space: nowrap;
  color: var(--muted-fg);
}

/* Refinement-chain breadcrumb inside an inline-expand panel — one
   single-line view of `:user-port ⊂ :int ⊂ :numeric` joined by ⊂
   glyphs. Each name is clickable when a named type-row backs it;
   constraints surface on hover so the row stays compact. */
.type-inline-refinement-chain {
  margin-top: 4px;
  margin-bottom: 6px;
  padding: 2px 0;
}
.type-inline-refinement-chain-row {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px;
  font-size: 10px;
  color: var(--muted-fg);
}
.type-inline-refinement-chain-sep {
  color: var(--light-fg);
  font-size: 10px;
}
.type-inline-refinement-chain-anon {
  font-style: italic;
  color: var(--light-fg);
}
.type-inline-resolution-active {
  color: var(--fg);
  font-weight: 600;
}
.type-inline-resolution-mark {
  flex: 0 0 auto;
  width: 9px;
  text-align: center;
  color: var(--light-fg);
}
.type-inline-resolution-active .type-inline-resolution-mark { color: var(--accent); }
.type-inline-resolution-label { flex: 0 1 auto; }
.type-inline-resolution-sep { flex: 0 0 auto; color: var(--light-fg); }
/* Clickable source-fn / ancestor link inside a resolution row. Reset
   default anchor underline + colour; keep it muted so it doesn't
   compete with the winning-tier highlight. */
.type-inline-resolution-link {
  flex: 0 1 auto;
  color: var(--accent);
  text-decoration: none;
  cursor: pointer;
}
.type-inline-resolution-link:hover { text-decoration: underline; }
.type-inline-resolution-active .type-inline-resolution-link { color: var(--accent); }
.type-inline-resolution-active .type-inline-resolution-type { color: var(--accent); }

/* Multi-override inheritance — when ≥ 2 ancestors carry a type-
   override for the same slot, the FIRST (closer-fn-wins) entry
   gets emphasised and tagged "(chosen)"; the rest dim out and
   read "(also by)" so the shadow-override decision is visible. */
.type-inline-resolution-chain-winner { color: var(--fg); font-weight: 600; }
.type-inline-resolution-chain-winner .type-inline-resolution-mark { color: var(--accent); }
.type-inline-resolution-chain-also   { color: var(--light-fg); }
.type-inline-resolution-chain-tag {
  flex: 0 0 auto;
  font-size: 9px;
  font-style: italic;
  color: var(--light-fg);
}
.type-inline-resolution-chain-winner .type-inline-resolution-chain-tag {
  color: var(--accent);
}

.type-inline-usages {
  margin-top: 6px;
  padding-top: 4px;
  border-top: 1px solid var(--light-border);
}
.type-inline-usages-head {
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--light-fg);
  margin-bottom: 2px;
}
.type-inline-usages-list {
  display: flex; flex-direction: column;
  gap: 1px;
  max-height: 160px;
  overflow-y: auto;
}
.type-inline-usage-row {
  display: flex; align-items: baseline; gap: 6px;
  font-size: 10px;
  white-space: nowrap;
}
.type-inline-usage-link {
  color: var(--accent);
  text-decoration: none;
  cursor: pointer;
}
.type-inline-usage-link:hover,
.type-inline-usage-link:focus-visible {
  text-decoration: underline;
  outline: none;
}
.type-inline-usage-kind {
  color: var(--muted-fg);
  font-size: 9px;
}
.type-inline-host {
  /* Body-level floating panel — `position: fixed` so it doesn't
     enlarge the edge-label overlay's bounding box (which is
     right-anchored to the target node; growing it would push under
     the source fn card). Positioned in JS to follow the chip. */
  display: none;
  position: fixed;
  z-index: 100;
  padding: 4px 6px;
  background: var(--card-bg);
  color: var(--card-fg);
  border: 1px solid var(--card-border);
  border-radius: 4px;
  box-shadow: var(--shadow-md);
  font-family: 'SF Mono', Monaco, monospace;
  font-size: 10px;
  line-height: 1.2;
  max-width: 260px;
  overflow-wrap: anywhere;
  pointer-events: auto;
}

/* Edge-label overlay sits above fn-overlays (z-index 10). Edges
   often pack the arg-name + type-chip into the gap between source
   and target; when that gap is too small the overlay inevitably
   extends into one of the cards. Without this, fn-cards stacked at
   z-index 10 cover the chip's right tail and the user loses the
   tail of the type signature. We trade: a few pixels of card chrome
   sit BEHIND the overlay rather than the type-chip disappearing. */
/* Edge-label overlay — arg-name + type-chip + description badge on an
   inheritance edge. JS owns only the dynamic bits (left/top placement
   after the taxi bend, scale transform). */
.edge-label-overlay {
  z-index: 11;
  position: absolute;
  pointer-events: auto;
  background: var(--bg);
  color: var(--muted-fg);
  font-family: 'SF Mono', Monaco, monospace;
  font-size: 10px;
  line-height: 1.2;
  padding: 2px 4px;
  white-space: pre;
  text-align: left;
  transform-origin: top left;
  user-select: none;
}
.type-inline-row {
  display: block;
  white-space: normal;
  margin: 1px 0;
}
.type-inline-label {
  display: inline-block;
  margin-right: 4px;
  font-size: 9px;
  color: var(--muted-fg);
}
.type-inline-label-renameable {
  cursor: pointer;
  text-decoration: underline dotted var(--muted-fg);
  text-underline-offset: 2px;
}
.type-inline-label-renameable:hover { color: var(--fg); }
.type-inline-chip {
  font-size: 9px;
  line-height: 12px;
  margin-left: 0;
}
.type-inline-chip-expandable {
  text-decoration: underline dotted var(--muted-fg);
  text-underline-offset: 2px;
}
.type-inline-child {
  display: block;
  margin-left: 8px;
  padding-left: 6px;
  border-left: 1px solid var(--light-border);
}
.type-inline-constraint-text {
  font-size: 9px;
  color: var(--muted-fg);
  font-family: inherit;
}

/* Stacked type-narrowing block — sits below the main edge-label row
   when ≥ 2 type groups are visible along the source-chain. One row
   per inherited level: `↑ [type] (source-fn)`. Same vocabulary as the
   stacked rename in the label text — no new visual idiom. */
.edge-type-chain {
  display: flex; flex-direction: column;
  margin-top: 2px;
  gap: 1px;
}
.edge-type-chain-row {
  display: inline-flex; align-items: center;
  white-space: nowrap;
}
.edge-type-chain-arrow {
  font-size: 9px;
  color: var(--muted-fg);
  margin-right: 2px;
  line-height: 14px;
}
.edge-type-chain-source {
  font-size: 9px;
  color: var(--muted-fg);
  margin-left: 4px;
  line-height: 1.2;
}
/* Narrowing attribution — why a chain entry has its type
 * (binding-override vs the slot's own declaration). */
.edge-type-chain-kind {
  font-size: 8px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--light-fg);
  margin-left: 4px;
  padding: 0 3px;
  border: 1px solid var(--light-border);
  border-radius: 2px;
  line-height: 1.4;
}

/* Free-arg binding chooser — two-button row inside the inline-edit
   popover; user picks "Bind literal" or "Bind fn-ref". */
.free-arg-bind-chooser {
  display: flex; gap: 6px; flex-wrap: wrap;
}

/* Sequence add/remove buttons on edge-labels (Phase 5). Shape matches
   the type-chip / is-fn chip family — same border-radius, same line-
   height — so a row of edge-label affordances reads as one strip. */
.arg-seq-btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; margin-left: 4px;
  width: 14px; height: 14px;
  font-family: inherit; font-size: 11px; line-height: 1;
  border: 1px solid currentColor; border-radius: 3px;
  background: transparent; color: currentColor;
  cursor: pointer; opacity: 0.7;
  pointer-events: auto;
}
.arg-seq-btn:hover { opacity: 1; }
.arg-seq-btn-remove { color: var(--muted-fg); }
.arg-seq-btn-add { font-weight: bold; }

/* Placeholder binder — the small `+` button that sits at the endpoint
   of an unset edge. Replaces the old dashed type-name card; the
   type-chip on the inbound edge already shows what the slot expects,
   so this is purely a click affordance ("bind this slot" or, on a
   sequence anchor, "add the first item"). Sized as a non-critical
   touch-target — `--touch-target-min` kicks in on hover-less devices. */
.placeholder-overlay {
  display: flex; align-items: center; justify-content: center;
}
.placeholder-binder {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0;
  width: 18px; height: 18px;
  font-family: inherit; font-size: 14px; line-height: 1;
  font-weight: 600;
  border: 1px dashed var(--muted-fg);
  border-radius: 50%;
  background: var(--bg);
  color: var(--muted-fg);
  cursor: pointer;
  opacity: 0.7;
  transition: opacity 120ms, color 120ms, border-color 120ms,
              background-color 120ms;
}
.placeholder-binder:hover,
.placeholder-binder:focus-visible {
  opacity: 1;
  color: var(--fg);
  border-color: var(--fg);
  border-style: solid;
  outline: none;
}
.placeholder-binder:focus-visible {
  box-shadow: 0 0 0 2px var(--accent);
}

/* Fn-picker popover — search input + scrollable list of candidate
   fns, used by Phase 2 (arg-type → fn) and reused in later phases. */
.fn-picker-popover {
  /* Picker sits ABOVE other popovers (e.g. opens from inside the
     reparent or arg-type popover) so its z-index nudges above the
     shared 10001. Padding tighter than other popovers because the
     list itself fills the body. */
  z-index: 10002;
  padding: 6px;
  min-width: 280px; max-width: 360px;
}
.fn-picker-search {
  font-family: inherit; font-size: 12px;
  padding: 5px 7px; border-radius: 3px;
  border: 1px solid var(--card-border);
  background: var(--bg); color: var(--fg);
  outline: none; box-sizing: border-box; width: 100%;
}
.fn-picker-expected {
  font-size: 10px; padding: 2px 4px;
  opacity: 0.75; line-height: 1.3;
  color: var(--muted-fg);
  border-bottom: 1px dashed var(--card-border);
}
.fn-picker-search:focus { box-shadow: 0 0 0 1px var(--fg) inset; }
.fn-picker-list {
  max-height: 200px; overflow-y: auto;
  background: var(--bg);
}
.fn-picker-list-other {
  /* slightly dimmer background so the disclosed incompat section
     reads as "secondary" without relying on row-level opacity */
  background: color-mix(in srgb, var(--bg) 96%, var(--fg) 4%);
}
.fn-picker-row {
  display: flex; align-items: center; gap: 6px;
  padding: 5px 8px; cursor: pointer;
  font-size: 11px;
  min-height: 28px;
}
.fn-picker-row-active {
  background: color-mix(in srgb, var(--fg) 12%, transparent);
}
.fn-picker-row-main { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.fn-picker-row-rt {
  font-style: italic; opacity: 0.7;
  flex-shrink: 0;
  font-size: 10px;
  color: var(--muted-fg);
}
/* Kind annotation — small tag pill on the fn-picker row for type-row
   fns (refinement / record / union / variant / list / fn-type /
   base-fn / primitive). Composed fns get nothing — the return-type
   chip already carries the "what does this fn return" signal. */
.fn-picker-row-kind {
  display: inline-block;
  flex-shrink: 0;
  padding: 0 4px;
  font-size: 9px;
  line-height: 14px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: 1px solid var(--input-border);
  border-radius: 3px;
  color: var(--muted-fg);
  background: var(--card-bg);
}
.fn-picker-row-kind-primitive {
  border-style: dashed;
}

/* Effect badges — same `.effects-chip-*` palette used on fn-overlays
   so users see the same colours throughout. Stacked horizontally
   inside a row; pre-existing chip styling handles padding/font. */
.fn-picker-row-effects {
  display: inline-flex; gap: 2px; flex-shrink: 0;
}
/* Type-compat sections (only when `expectedType` is in play). The ✓
   glyph keeps the row easy to scan; the section header makes the
   compat/incompat boundary explicit instead of having the user infer
   it from a dimmed row. */
.fn-picker-row-ok {
  color: var(--success-fg); font-weight: 600;
  flex-shrink: 0;
}
.fn-picker-row-compat {
  background: color-mix(in srgb, var(--success-fg) 6%, transparent);
}
.fn-picker-row-incompat {
  opacity: 0.7;
}

/* Section grouping — sticky-ish headers above each list, plus a
   disclosure for the "Other" (incompatible) section. */
.fn-picker-sections {
  display: flex; flex-direction: column;
  border: 1px solid var(--card-border); border-radius: 3px;
  overflow: hidden;
  background: var(--bg);
}
.fn-picker-section-header {
  font-size: 10px; font-weight: 600;
  letter-spacing: 0.5px; text-transform: uppercase;
  padding: 4px 8px;
  color: var(--muted-fg);
  background: var(--sidebar-bg);
  border-bottom: 1px solid var(--card-border);
  display: flex; align-items: center; gap: 4px;
}
.fn-picker-section-disclosure {
  cursor: pointer;
  font-family: inherit;
  text-align: left;
  border: 0; border-top: 1px solid var(--card-border);
  width: 100%;
}
.fn-picker-section-disclosure:hover {
  background: var(--hover-bg);
}
.fn-picker-disclosure-arrow {
  font-size: 8px; opacity: 0.6;
  display: inline-block; min-width: 8px;
}
.fn-picker-empty {
  padding: 8px; font-size: 11px; opacity: 0.6; text-align: center;
}
.fn-picker-cancel-row {
  display: flex; justify-content: flex-end;
}

/* Phase 4 — Action toolbar pinned to the bottom of the selected
   (nav-root) fn's overlay. Surfaces every mutating action in one
   place so the user doesn't have to discover hover-revealed icons
   scattered across the card. Always visible (no hover gate),
   ≥44px touch targets on pointer-coarse devices. */
.fn-action-toolbar {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 4px;
  padding: 6px 8px;
  border-top: 1px solid var(--card-border);
  background: var(--card-bg);
  /* Slight inset so the toolbar reads as a discrete stripe and
     not a row of arbitrary buttons floating against the card. */
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--card-border) 40%, transparent);
}
.fn-action-toolbar-hint {
  flex: 1; padding: 0 4px;
  font-size: 11px; font-style: italic; color: var(--muted-fg);
}
.fn-action-btn {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 3px 6px;
  flex: 1 1 auto;
  justify-content: center;
  font-family: inherit; font-size: 11px;
  background: transparent; color: var(--card-fg);
  border: 1px solid var(--card-border);
  border-radius: 3px;
  cursor: pointer;
  white-space: nowrap;
}
.fn-action-btn:hover {
  background: var(--hover-bg);
}
.fn-action-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.fn-action-btn-glyph {
  font-size: 13px; line-height: 1;
}
.fn-action-btn-label {
  /* Label is informational on desktop; on very narrow cards / phone
     viewports we may want icon-only. Keep it visible by default and
     hide it via the touch-narrow override below if needed. */
}
/* Edit-parents strip — same layout as return-type strip but distinct
   text colour so the two affordances on the root card don't blur. */
.reparent-strip {
  padding: 2px 8px;
  font-size: 10px;
  color: var(--muted-fg);
  border-top: 1px solid var(--card-border);
  cursor: pointer;
  background: var(--card-bg);
}
.reparent-strip:hover {
  background: color-mix(in srgb, var(--fg) 6%, var(--card-bg));
}

/* Fn-card ancestor rows — statics for the overlay renderers
   (editor-overlay-fn.js / editor-overlay-fn-rows.js). JS keeps only
   the dynamic bits: border-bottom variant, cursor, paint-machine
   colours. */
.fn-ancestor-line {
  touch-action: none;
  user-select: none;
}
.fn-ancestor-more {
  padding: 2px 8px;
  color: var(--light-fg);
  font-size: 10px;
}

/* Use-site header row of a named non-root card — an empty
   root-coloured marker row; bg comes from the paint machine. */
.fn-use-site-header {
  position: relative;
  color: var(--card-header-fg);
  border-bottom: none;
  touch-action: none;
  user-select: none;
}

/* Column-below-MI text overlay — floats over the per-parent column
   divs; the 8px/24px insets match the `colLeftInset`/`colRightInset`
   constants in editor-overlay-fn-rows.js (right inset clears the
   pinned ⋯ trigger slot). */
.mi-col-text {
  position: absolute;
  left: 8px;
  right: 24px;
  top: 4px;
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
  z-index: 1;
}

/* Branch-local strip — informational marker that this fn (or an
   ancestor) is sticky-local: version rows do not propagate across
   branches on merge. No edit affordance — once an ancestor sets the
   flag, descendants inherit it monotonically and cannot widen back. */
.branch-local-strip {
  padding: 2px 8px;
  font-size: 10px;
  color: var(--muted-fg);
  border-top: 1px solid var(--card-border);
  background: var(--card-bg);
  display: flex;
  align-items: center;
  gap: 4px;
  cursor: help;
}
.branch-local-strip-glyph {
  font-size: 11px;
  line-height: 1;
}
.branch-local-strip-label {
  font-style: italic;
  letter-spacing: 0.02em;
}

/* Optional-args strip — `?name` list of optional-but-unbound args
   (defaults in effect). Rendered by appendOptionalArgsStrip. */
.optional-args-strip {
  padding: 2px 8px;
  color: var(--light-fg);
  font-size: 10px;
  font-style: italic;
  border-top: 1px dashed var(--input-border);
  background: var(--sidebar-bg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* HOF-context strips — `λname` (args the enclosing HOF supplies at
   runtime) and `⇣name` (free args threaded into the sub-tree). Same
   look for both; rendered by appendHofCapturedArgsStrip /
   appendDeepFreeArgsStrip. */
.hof-args-strip {
  padding: 2px 8px;
  color: var(--hof-fg);
  font-size: 10px;
  font-style: italic;
  border-top: 1px dashed var(--hof-border);
  background: var(--hof-bg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Description tooltip — singleton bubble managed by
   editor-tooltips.js. JS owns only the dynamic bits (display,
   left/top placement); everything static lives here. `pointer-events`
   is toggled to `auto` by JS in sticky/edit mode. */
.description-tooltip {
  position: fixed;
  z-index: 10000;
  background: var(--tooltip-bg);
  color: var(--tooltip-fg);
  font-family: system-ui, sans-serif;
  font-size: 12px;
  line-height: 1.4;
  padding: 6px 10px;
  border-radius: 4px;
  max-width: 360px;
  pointer-events: none;
  box-shadow: var(--shadow-md);
  display: none;
  white-space: pre-wrap;
}

/* In-tooltip dismiss — the × button is the obvious affordance for
   touch users; the document-level pointerdown handler is a fallback
   for desktop "tap-outside". Sized 20×20 to be a comfortable touch
   target without dominating the tooltip header. */
.description-tooltip-close {
  position: absolute; top: 4px; right: 4px;
  width: 20px; height: 20px; padding: 0;
  background: transparent; color: var(--tooltip-fg);
  border: 0; border-radius: 3px;
  font-size: 18px; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  opacity: 0.65;
}
.description-tooltip-close:hover { opacity: 1; background: var(--overlay-pressed); }

/* In-tooltip edit affordances — Edit pencil (read mode) and the
   Save/Cancel + textarea triplet (edit mode). The tooltip itself
   inherits `var(--tooltip-bg/--tooltip-fg)` so the buttons float on
   the same dark/light bubble that already adapts to theme. */
.description-tooltip-btn {
  font-family: inherit; font-size: 11px;
  padding: 3px 10px; border-radius: 3px; cursor: pointer;
  background: var(--tooltip-fg); color: var(--tooltip-bg);
  border: 1px solid var(--tooltip-fg);
}
.description-tooltip-btn:hover { opacity: 0.85; }
.description-tooltip-btn:disabled { opacity: 0.5; cursor: default; }
.description-tooltip-btn-secondary {
  background: transparent; color: var(--tooltip-fg);
}
.description-tooltip-textarea {
  width: 320px; max-width: 100%; min-height: 60px;
  font-family: inherit; font-size: 12px; line-height: 1.4;
  padding: 4px 6px; border-radius: 3px;
  border: 1px solid var(--tooltip-fg);
  background: var(--tooltip-bg); color: var(--tooltip-fg);
  resize: vertical; box-sizing: border-box; outline: none;
}
.description-tooltip-textarea:focus {
  box-shadow: 0 0 0 1px var(--tooltip-fg) inset;
}
.description-tooltip-error {
  margin-top: 4px; font-size: 11px;
  color: var(--error-fg);
}

/* Full-name popover — bubble shown over a truncated ancestor-row name.
   Singleton managed by editor-tooltips.js; JS toggles display,
   opacity + translateY for the fade, and left/top placement. */
.full-name-tooltip {
  position: fixed;
  z-index: 9999;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--input-border);
  box-shadow: var(--shadow-sm);
  padding: 4px 10px;
  border-radius: 4px;
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 110ms ease-out, transform 110ms ease-out;
  display: none;
}

/* Disabled-icon reason popover — click-to-reveal "why is this action
   disabled" bubble. Same singleton lifecycle as .full-name-tooltip
   but multi-line and interactive (outside-click dismissed). */
.icon-reason-popover {
  position: fixed;
  z-index: 9999;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--input-border);
  box-shadow: var(--shadow-md);
  padding: 6px 10px;
  border-radius: 4px;
  font-family: inherit;
  font-size: 12px;
  line-height: 1.35;
  max-width: 260px;
  pointer-events: auto;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 110ms ease-out, transform 110ms ease-out;
  display: none;
}

/* Right-edge action icons — `i` (description) and `↗` (open in new tab).
   Highlight on hover so the click-target is obvious; the tint uses
   `currentColor` so it picks up whichever fg the row has (white on a
   header row, dark on a body row) without needing to know the theme. */
.description-badge, .open-in-new-tab {
  opacity: 0.85;
  transition: opacity 0.1s, background-color 0.1s;
}
.description-badge:hover, .open-in-new-tab:hover {
  opacity: 1;
  background-color: color-mix(in srgb, currentColor 28%, transparent);
}

/* Navigation controls — single-row pill at the bottom-right:
   `[+] [−] [⌂] [⟲] [⊙]`. The vertical slider was here historically
   (alongside +/−); it duplicated keyboard +/− and scroll-zoom while
   eating 120px of vertical real estate, so it now hides on default
   layouts and only re-surfaces in narrow-touch contexts (no
   scroll-wheel) — see the media-query block at the file's bottom. */
#nav-controls {
  position: absolute; bottom: 16px; right: 12px; z-index: 100;
  display: flex; flex-direction: row-reverse; align-items: center; gap: 4px;
  pointer-events: auto;
}
.nav-btn {
  width: 28px; height: 28px;
  border: 1px solid var(--input-border); border-radius: 4px;
  background: var(--bg); color: var(--fg);
  font-size: 24px; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  padding: 0; user-select: none; flex-shrink: 0;
}
.nav-btn:hover { background: var(--hover-bg); }
.nav-btn:active { background: var(--selected-bg); }

/* Zoom column flattens into the parent row; +/− buttons sit beside
   the bottom-row buttons. Slider hidden by default — see media
   override below for the cases it survives. */
.nav-zoom-col {
  display: contents;
}
.nav-zoom-col input[type="range"] {
  display: none;
  writing-mode: vertical-lr;
  direction: rtl;
  height: 120px; width: 20px;
  cursor: pointer; accent-color: var(--fg);
  margin: 0;
}

/* Bottom row is now a sibling segment of the same flex row. */
.nav-bottom-row {
  display: flex; gap: 4px; align-items: center;
}

/* Re-enable the slider in coarse-pointer / no-hover contexts (touch
   devices, where scroll-wheel zoom isn't available). Switches the
   container back to a vertical stack so the slider's 120px height
   fits. */
@media (pointer: coarse) and (hover: none) {
  #nav-controls { flex-direction: column; align-items: flex-end; }
  .nav-zoom-col { display: flex; flex-direction: column; align-items: center; gap: 4px; }
  .nav-zoom-col input[type="range"] { display: block; }
}

/* =========================================================================
 * EDITOR BUSY — bottom-centre banner shown while a multi-step user
 * action (reparent cascade, extend-fn, delete-fn) is in flight.
 * Single-PUT inline edits don't get a banner; the Save button's own
 * disabled state covers them. Spinner is a CSS ::before so the
 * banner's textContent stays usable for screen readers.
 * ========================================================================= */
.editor-busy-banner {
  position: fixed;
  bottom: 16px; left: 50%;
  transform: translateX(-50%);
  z-index: 10003;
  background: var(--card-bg); color: var(--card-fg);
  border: 1px solid var(--card-border);
  border-radius: 4px;
  padding: 8px 14px;
  font-family: inherit; font-size: 12px;
  box-shadow: var(--shadow-lg);
  display: none;
  align-items: center;
  gap: 8px;
  pointer-events: none;
}
.editor-busy-banner.visible { display: inline-flex; }

/* One-shot warning variant (showTransientWarning) — same banner
   element, amber border + text, no spinner. */
.editor-busy-banner.warning {
  border-color: var(--warning-fg);
  color: var(--warning-fg);
}

.editor-busy-banner.warning::before { display: none; }
.editor-busy-banner::before {
  content: '';
  width: 12px; height: 12px;
  border: 2px solid var(--accent);
  border-right-color: transparent;
  border-radius: 50%;
  animation: editor-busy-spin 0.8s linear infinite;
  flex-shrink: 0;
}
@keyframes editor-busy-spin {
  to { transform: rotate(360deg); }
}
/* `cursor: progress` on the whole page reads better than `wait`
   here — it keeps interactive affordances feeling allowed even
   though they're temporarily disabled by the buttons themselves. */
body.editor-busy { cursor: progress; }
body.editor-busy .arg-value-edit-popover,
body.editor-busy .fn-picker-popover,
body.editor-busy .fn-action-toolbar { cursor: progress; }

/* =========================================================================
 * TYPE EXPLAINER — popover that opens on click of any type chip
 * (arg-overlay's `[int]` chip, edge-label's chip). Surfaces the
 * human-readable description plus the structural form below for
 * users who already know the DSL. Editable chips additionally
 * get a "Change type" button — clicking the chip is now a strict
 * superset of the legacy "click to edit" behaviour.
 * ========================================================================= */
.type-explainer {
  position: fixed; z-index: 10001;
  display: none;
  background: var(--card-bg); color: var(--card-fg);
  border: 1px solid var(--card-border);
  border-left: 3px solid var(--accent);
  border-radius: 4px;
  box-shadow: var(--shadow-lg);
  padding: 8px 10px;
  min-width: 220px; max-width: 360px;
  font-family: inherit; font-size: 12px; line-height: 1.4;
}
.type-explainer.visible { display: block; }

.type-explainer-header {
  display: flex; align-items: center; justify-content: space-between;
  border-bottom: 1px solid var(--light-border);
  margin-bottom: 6px; padding-bottom: 4px;
}
.type-explainer-title {
  font-weight: 600; font-size: 12px;
  color: var(--accent);
  letter-spacing: 0.4px; text-transform: uppercase;
}
.type-explainer-close {
  width: 20px; height: 20px; padding: 0;
  background: transparent; color: var(--card-fg);
  border: 0; border-radius: 3px;
  font-size: 18px; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  opacity: 0.65;
}
.type-explainer-close:hover { opacity: 1; background: var(--overlay-pressed); }

.type-explainer-human {
  font-size: 13px;
  margin: 4px 0;
  color: var(--card-fg);
  line-height: 1.5;
}
.type-explainer-structural {
  font-family: ui-monospace, monospace;
  font-size: 11px;
  margin-top: 4px;
  padding: 4px 6px;
  background: color-mix(in srgb, var(--card-fg) 5%, transparent);
  border-radius: 3px;
  color: var(--muted-fg);
  overflow-wrap: anywhere;
}

.type-explainer-btn {
  font-family: inherit; font-size: 11px;
  padding: 4px 10px; border-radius: 3px; cursor: pointer;
  background: var(--card-fg); color: var(--card-bg);
  border: 1px solid var(--card-fg);
}
.type-explainer-btn:hover { opacity: 0.85; }

/* Tighten-effects controls — used by the fn-type inline expansion
   panel (editor-overlay-type-expand.js) to narrow [:fn args ret
   #{eff-set}]. Each row contains a `<select>` for narrower types;
   the effect set sits in a row of inline checkboxes; the Tighten
   button submits the delta. */
.type-explainer-tighten {
  margin-top: 8px;
  padding-top: 6px;
  border-top: 1px solid var(--light-border);
}
.type-explainer-tighten-row {
  display: flex; flex-wrap: wrap; gap: 4px 10px;
  margin-bottom: 6px;
}
.type-explainer-tighten-select {
  font-family: inherit; font-size: 11px;
  padding: 2px 4px; border-radius: 3px;
  border: 1px solid var(--light-border);
  background: var(--card-bg); color: var(--card-fg);
  flex: 1;
  min-width: 0;
}
.type-explainer-tighten-chk {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; cursor: pointer;
}
.type-explainer-tighten-chk input { margin: 0; }
.type-explainer-tighten-err {
  font-size: 10px;
  color: var(--error-fg);
  margin: 4px 0;
  white-space: pre-wrap;
}

/* =========================================================================
 * MISMATCH EXPLAINER — popover that opens on click of an
 * `arg-overlay-mismatch` indicator. Distinct from the description
 * tooltip: error-themed left accent stripe, structured rows, always
 * persistent (clicked open, closed via × / outside-click / Esc).
 * Built fresh per arg in editor-mismatch-explainer.js.
 * ========================================================================= */
.mismatch-explainer {
  position: fixed; z-index: 10001;
  display: none;
  background: var(--card-bg); color: var(--card-fg);
  border: 1px solid var(--card-border);
  border-left: 3px solid var(--error-fg);
  border-radius: 4px;
  box-shadow: var(--shadow-lg);
  padding: 8px 10px;
  min-width: 240px; max-width: 360px;
  font-family: inherit; font-size: 12px; line-height: 1.4;
}
.mismatch-explainer.visible { display: block; }

.mismatch-explainer-header {
  display: flex; align-items: center; justify-content: space-between;
  border-bottom: 1px solid var(--light-border);
  margin-bottom: 6px; padding-bottom: 4px;
}
.mismatch-explainer-title {
  font-weight: 600; font-size: 12px;
  color: var(--error-fg);
}
.mismatch-explainer-close {
  width: 20px; height: 20px; padding: 0;
  background: transparent; color: var(--card-fg);
  border: 0; border-radius: 3px;
  font-size: 18px; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  opacity: 0.65;
}
.mismatch-explainer-close:hover {
  opacity: 1; background: var(--overlay-pressed);
}

.mismatch-explainer-row {
  display: flex; gap: 6px;
  margin: 3px 0;
}
.mismatch-explainer-label {
  flex: 0 0 64px;
  color: var(--muted-fg);
  font-size: 11px;
}
.mismatch-explainer-value {
  flex: 1;
  font-family: ui-monospace, monospace;
  font-size: 11.5px;
  overflow-wrap: anywhere;
}
/* Reason text wraps onto multiple lines and reads as prose, not code,
   so flip to system font + slightly looser line-height. */
.mismatch-row-reason .mismatch-explainer-value {
  font-family: inherit;
  line-height: 1.5;
  color: var(--card-fg);
}

/* Leaf-level disagreement section — points at the EXACT failing
   field / element when expected and actual are structurally
   compatible at the top level but diverge inside. */
.mismatch-explainer-leaves {
  margin: 6px 0 4px;
  padding: 6px 0;
  border-top: 1px dashed var(--light-border);
}
.mismatch-explainer-leaves-head {
  font-size: 11px;
  color: var(--muted-fg);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 4px;
}
.mismatch-explainer-leaf {
  font-family: ui-monospace, monospace;
  font-size: 11px;
  line-height: 1.6;
  overflow-wrap: anywhere;
}
.mismatch-explainer-leaf-path {
  color: var(--card-fg);
  font-weight: 600;
}
.mismatch-explainer-leaf-sep {
  color: var(--muted-fg);
}
.mismatch-explainer-leaf-expected {
  color: var(--success-fg, currentcolor);
}
.mismatch-explainer-leaf-actual {
  color: var(--error-fg);
}
.mismatch-explainer-leaf-more {
  font-size: 11px;
  color: var(--muted-fg);
  font-style: italic;
  margin-top: 3px;
}

.mismatch-explainer-actions {
  display: flex; gap: 8px; align-items: center;
  margin-top: 8px; padding-top: 6px;
  border-top: 1px solid var(--light-border);
}
.mismatch-explainer-btn {
  font-family: inherit; font-size: 11px;
  padding: 4px 10px; border-radius: 3px; cursor: pointer;
  background: var(--card-fg); color: var(--card-bg);
  border: 1px solid var(--card-fg);
}
.mismatch-explainer-btn:hover { opacity: 0.85; }


/* Right-edge warning chip on a mismatched arg-overlay. Sits to the
   right of the type-chip when present; otherwise pinned alone. The
   visible glyph is a single ⚠-style "!" but lives inside a 1px ring
   to match `applyActionIconBox` siblings, so the whole row reads as
   "i ↗ ! type-chip" not "i ↗ × type-chip". */
.arg-mismatch-badge {
  display: inline-flex; align-items: center; justify-content: center;
  margin-left: 4px;
  width: var(--icon-size); height: var(--icon-size);
  border: 1px solid var(--error-fg);
  border-radius: 3px;
  background: transparent; color: var(--error-fg);
  font-family: inherit; font-size: var(--icon-font-size);
  font-weight: 700;
  line-height: 1; cursor: help;
  padding: 0;
}
.arg-mismatch-badge:hover {
  background: color-mix(in srgb, var(--error-fg) 18%, transparent);
}

/* Inheritance-provenance affordance — a compact ↖ on an arg row whose
 * slot was inherited from a parent fn. Hover reveals the ancestor name
 * (native title); the link opens that fn in a new tab. */
.arg-source-link {
  display: inline-flex; align-items: center; justify-content: center;
  margin-left: 4px;
  width: var(--icon-size); height: var(--icon-size);
  color: var(--muted-fg);
  font-size: var(--icon-font-size);
  line-height: 1; cursor: pointer;
  text-decoration: none;
  flex: 0 0 auto;
}
.arg-source-link:hover { color: var(--accent); }

/* Type-narrowing provenance — a ↳ pinned right after a type-chip when
 * the binding carries a :type-override-fn-id that's tighter than the
 * inherited slot type. Read-only informational badge; tooltip names the
 * fn that introduced the narrowing. Kept subdued so the chip itself
 * stays the primary focus. */
.arg-type-provenance {
  display: inline-flex; align-items: center; justify-content: center;
  margin-left: 2px;
  padding: 0 2px;
  background: transparent;
  border: 0;
  font: inherit;
  font-size: 9px; line-height: 14px;
  color: var(--muted-fg);
  cursor: pointer;
  user-select: none;
}
.arg-type-provenance:hover { color: var(--accent); }
.arg-type-provenance:focus-visible {
  outline: 1px solid var(--accent);
  border-radius: 2px;
}

/* =========================================================================
 * PROVENANCE POPOVER — anchored to the `↳` badge on a type-chip. Shows
 * the FULL narrowing chain (4-tier resolution + inheritance steps) with
 * clickable source-fn names. Built fresh per arg in
 * editor-provenance-popover.js.
 * ========================================================================= */
.provenance-popover {
  position: fixed; z-index: 10001;
  display: none;
  background: var(--card-bg); color: var(--card-fg);
  border: 1px solid var(--card-border);
  border-left: 3px solid var(--accent);
  border-radius: 4px;
  box-shadow: var(--shadow-lg);
  padding: 8px 10px;
  min-width: 240px; max-width: 380px;
  font-family: inherit; font-size: 12px; line-height: 1.4;
}
.provenance-popover.visible { display: block; }

.provenance-popover-header {
  display: flex; align-items: center; justify-content: space-between;
  border-bottom: 1px solid var(--light-border);
  margin-bottom: 6px; padding-bottom: 4px;
}
.provenance-popover-title {
  font-weight: 600; font-size: 12px;
  color: var(--accent);
}
.provenance-popover-close {
  width: 20px; height: 20px; padding: 0;
  background: transparent; color: var(--card-fg);
  border: 0; border-radius: 3px;
  font-size: 18px; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  opacity: 0.65;
}
.provenance-popover-close:hover {
  opacity: 1; background: var(--overlay-pressed);
}
/* Inside the popover the resolution rows are the primary content (not
   a secondary footer), so drop the top border + spacer that the inline
   panel adds for visual separation from the type structure above. */
.provenance-popover .type-inline-resolution {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

/* Prose intro line in the type-rule variant of the popover —
   "Return type computed by :<parent>'s :return-type-rule" — reads as
   a sentence, not a key-value row. */
.provenance-popover-intro {
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--card-fg);
  margin-bottom: 6px;
  overflow-wrap: anywhere;
}

/* Per-rule one-line interpretation, e.g. for :assoc — "Literal key
   \"name\", value typed :int — added field \"name\": :int to map's
   record shape". Distinguishes the INTERPRETATION (what the rule
   did) from the raw inputs table below. */
.provenance-popover-narrative {
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--muted-fg);
  margin-bottom: 8px;
  padding: 4px 6px;
  background: var(--card-row-highlight);
  border-left: 2px solid var(--accent);
  border-radius: 2px;
  overflow-wrap: anywhere;
}

/* Closed-enum reveal — when the resolved slot type is a refinement
   pinning the value to a finite literal set, list every member as a
   read-only chip in the provenance popover. The value-edit popover
   is where the user PICKS one; here we just surface the choices. */
.provenance-popover-enum {
  margin-top: 6px;
  padding-top: 4px;
  border-top: 1px solid var(--light-border);
}

/* Slot-effect-bound reveal — when the resolved fn-typed slot has a
   concrete effect constraint, surface it in the provenance popover
   so the reader sees the bound directly (without opening `▸/▾`). */
.provenance-popover-effects {
  margin-top: 6px;
  padding-top: 4px;
  border-top: 1px solid var(--light-border);
}
.provenance-popover-enum-list {
  display: flex; flex-wrap: wrap; gap: 4px;
  margin-top: 2px;
}
.provenance-popover-enum-chip {
  display: inline-block;
  padding: 1px 5px;
  font-size: 10px;
  font-family: monospace;
  background: var(--card-row-highlight);
  border: 1px solid var(--light-border);
  border-radius: 2px;
  color: var(--card-fg);
}

/* Type-rule provenance trigger on a fn-card's return-type strip.
   Mirrors the arg-overlay's `.arg-type-provenance` glyph so the two
   surfaces look like siblings — same `↳` character, same hit area. */
.return-type-strip-provenance {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto;
  margin-left: 4px;
  padding: 0 2px;
  background: transparent;
  border: 0;
  font: inherit;
  font-size: 9px; line-height: 14px;
  color: var(--muted-fg);
  cursor: pointer;
  user-select: none;
}
.return-type-strip-provenance:hover { color: var(--accent); }
.return-type-strip-provenance:focus-visible {
  outline: 1px solid var(--accent);
  border-radius: 2px;
}

/* =========================================================================
 * TOUCH-DEVICE OVERRIDES — fires on iPad / phones / any pointer-coarse
 * device. No-hover devices are exposed to the same hover-only affordances
 * that desktop users discover by mousing over them; here we bring those
 * up unconditionally and expand the hit area to ≥28px (WCAG 2.5.5
 * minimum) so taps don't miss.
 *
 * Strategy: bump the existing CSS vars instead of adding a parallel
 * sizing path. Component CSS that reads var(--icon-size) etc. picks up
 * the touch values automatically. Hover-gated reveal rules below
 * unconditionally surface the icons regardless of :hover state.
 *
 * Keep this block at the END of the file — last rule wins where
 * specificity ties.
 * ========================================================================= */
@media (hover: none) and (pointer: coarse) {
  :root {
    /* Action-icon visual + pinning vars: bigger glyph, more breathing room
       between stacked icons. Pin offsets recomputed so a 3-icon stack
       fits at the right edge without overlap (28 + 4 gap × 2 = 92, last
       slot = right:6+28+4+28+4 = 70 ≈ --icon-pin-r-3 below). */
    --icon-size: 28px;
    --icon-font-size: 14px;
    --icon-pin-r-1: 4px;
    --icon-pin-r-2: 38px;
    --icon-pin-r-3: 72px;
  }

  /* Reveal hover-gated sidebar affordances unconditionally on touch.
     Desktop's `opacity:0 → :hover .85` pattern is invisible on a
     no-hover device — the user would never know these icons exist. */
  .ns-header .description-badge,
  .entity-item .description-badge,
  .entity-item .open-in-new-tab.sidebar-action,
  .ns-header:not(:hover) .ns-row-actions .create-btn-inline,
  .entity-item:not(:hover) .ns-row-actions .create-btn-inline {
    opacity: 0.85;
  }

  /* Sidebar rows: minimum tappable height. Without this a row is
     ~21px tall — finger taps overshoot to neighbours. */
  .ns-header,
  .entity-item {
    min-height: var(--touch-target);
    padding-top: 4px;
    padding-bottom: 4px;
  }

  /* ns-arrow doubles as the expand-collapse target. 16px is too small
     for a finger; bump to ≥ 28px and re-centre the glyph. */
  .ns-arrow {
    min-width: var(--touch-target-min);
    min-height: var(--touch-target-min);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
  }

  /* Floating + sidebar-header buttons should hit Apple's 44pt baseline
     since they're top-level navigation, not row-level glyphs. */
  .nav-btn,
  .prefs-btn,
  .auth-lock-btn,
  .sidebar-expand-floating {
    min-width: var(--touch-target);
    min-height: var(--touch-target);
  }
  .nav-btn { font-size: 28px; }

  /* Sequence ±, type chip, popover close — small glyphs but real
     actions. Bring them up to --touch-target-min. */
  .arg-seq-btn,
  .arg-type-chip,
  .arg-type-chip-readonly,
  .description-tooltip-close,
  .arg-value-edit-btn,
  .description-tooltip-btn,
  .placeholder-binder {
    min-width: var(--touch-target-min);
    min-height: var(--touch-target-min);
  }
  /* Buttons that hold visible text (Save / Cancel) — don't constrain
     min-width, just min-height so click area is tappable without
     making a "Save" button absurdly wide. */
  .arg-value-edit-btn,
  .description-tooltip-btn {
    min-width: 0;
    min-height: var(--touch-target);
    padding: 6px 14px;
    font-size: 13px;
  }

  /* Sidebar overlays the graph on narrow viewports rather than
     pushing content. Easier than reflowing cy on every toggle. The
     graph keeps its pan/zoom; the sidebar floats on top. */
  body:not(.sidebar-collapsed) #side-menu {
    box-shadow: var(--shadow-lg);
  }

  /* SVG glyphs inside chrome buttons — every SVG in this editor was
     authored with hardcoded width="11"/height="13". On touch the
     parent button grows to ≥28px but the SVG stays small,
     producing a tiny dot inside a giant target. CSS wins over the
     inline width/height attributes for sizing — bump SVGs to 18px. */
  .create-btn-inline svg,
  .prefs-btn svg,
  .auth-lock-btn svg,
  .sidebar-expand-floating svg,
  .nav-btn svg {
    width: 18px;
    height: 18px;
  }

  /* Fn-picker rows — taps need ≥44px on touch (Apple HIG). The 11px
     row text gets a hair bigger too so the iPad keyboard's
     auto-zoom-on-focus doesn't kick in (Safari triggers when input
     font-size < 16px on focus, but the surrounding rows stay legible
     either way). */
  .fn-picker-row { min-height: var(--touch-target); padding: 8px 10px; }
  .fn-picker-search { font-size: 16px; padding: 8px 10px; }
  .fn-picker-section-header { padding: 6px 10px; }

  /* Phase 4 fn-action toolbar — buttons grow to Apple-HIG min-target
     so the user can hit Rename / Describe / Re-parent / Delete with
     a finger. Glyph reads larger; label still inline. */
  .fn-action-btn {
    min-height: var(--touch-target);
    padding: 6px 12px;
    font-size: 12px;
  }
  .fn-action-btn-glyph { font-size: 16px; }

  /* Polish round — every text/select input across the editor's
     popovers needs to reach 16px so iPad Safari doesn't auto-zoom
     on focus (which immediately clips the popover off-screen and
     leaves the user staring at the keyboard). */
  .arg-value-edit-input,
  .description-tooltip-textarea {
    font-size: 16px;
    min-height: var(--touch-target);
    padding: 8px 10px;
  }
  /* Save / Cancel buttons in popovers need the Apple-HIG target
     too — applies to every button created by openInlineEditPopover
     plus the description tooltip's Save/Cancel pair. */
}

/* =========================================================================
 * RESPONSIVE — narrow viewports (phones, iPad portrait, iPad split view).
 * Independent of pointer kind: a narrow desktop window (rare) gets the
 * same layout treatment.
 * ========================================================================= */
@media (max-width: 900px) {
  /* Sidebar overlays the graph instead of taking 30 %+ of the screen. */
  #side-menu {
    box-shadow: var(--shadow-lg);
    z-index: 50;
  }
  /* Cap sidebar width so it doesn't blanket the whole screen on a
     phone — leaves enough graph visible to remind the user where
     they came from. */
  :root {
    --sidebar-width: min(280px, 80vw);
  }
}


/* =========================================================================
 * EXECUTE POPOVER — anchored to the ▶ button on a fn-card root row. Hosts
 * the type-aware free-arg form (via /api/value-form) + effect-warning chips
 * + Run/Cancel buttons + result pane. Singleton, dismissed on outside-click
 * or Esc. See editor-execute.js.
 * ========================================================================= */
.execute-popover {
  position: fixed; z-index: 10002;
  display: none;
  background: var(--card-bg); color: var(--card-fg);
  border: 1px solid var(--card-border);
  border-left: 3px solid var(--accent);
  border-radius: 4px;
  box-shadow: var(--shadow-lg);
  padding: 10px 12px;
  min-width: 360px; max-width: 520px;
  max-height: 80vh;
  overflow-y: auto;
  font-family: inherit; font-size: 12px; line-height: 1.4;
}
.execute-popover.visible { display: block; }

.execute-popover-header {
  display: flex; align-items: center; justify-content: space-between;
  border-bottom: 1px solid var(--light-border);
  margin-bottom: 8px; padding-bottom: 4px;
}
.execute-popover-title {
  font-weight: 600; font-size: 13px;
  color: var(--accent);
}
/* History toggle in the header — sits between title and ×.
 * Plain text-link styling so it doesn't compete with title weight. */
.execute-history-toggle {
  font-family: inherit; font-size: 11px;
  background: transparent; border: 0;
  color: var(--accent);
  padding: 2px 6px; margin-right: auto; margin-left: 8px;
  cursor: pointer; border-radius: 3px;
}
.execute-history-toggle:hover { background: var(--hover-bg); }
.execute-history-toggle[aria-expanded="true"] {
  background: var(--card-row-highlight);
  font-weight: 600;
}

/* Branch indicator — only shown when running off main; mirrors the
 * top-bar chip's inverted-pill style so the visual cue is the same
 * across the editor. */
.execute-popover-branch {
  font-size: 10px; letter-spacing: 0.02em;
  background: var(--fg); color: var(--bg);
  padding: 2px 6px; border-radius: 2px;
  margin-left: 8px;
  white-space: nowrap;
}

.execute-popover-close {
  width: 22px; height: 22px; padding: 0;
  background: transparent; color: var(--card-fg);
  border: 0; border-radius: 3px;
  font-size: 20px; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  opacity: 0.65;
}
.execute-popover-close:hover {
  opacity: 1; background: var(--overlay-pressed);
}

/* Effect-warning banner — only rendered when the fn declares effects.
 * Reuses --error-fg as the warning color (red); a dedicated warning-
 * fg token doesn't exist yet in the editor theme, and red on the
 * "side effects" label is the right severity signal anyway. */
.execute-effects-warning {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px;
  padding: 6px 8px;
  margin-bottom: 8px;
  background: var(--card-row-highlight);
  border-left: 2px solid var(--error-fg);
  border-radius: 2px;
  font-size: 11px;
}
.execute-effects-warning-label {
  font-weight: 600;
  color: var(--error-fg);
  margin-right: 2px;
}

/* Runtime-effects strip — shown below the result pane when the
 * fn-execution row carries an observed `:runtime-effects` set.
 * Subtler than the declared-effects banner (it's an audit/info
 * artifact, not a gate); muted background + chips. Drift chips
 * carry a red dashed outline so widening impls are visible. */
.execute-runtime-effects-strip {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px;
  margin-top: 8px;
  padding: 4px 6px;
  background: var(--card-row-highlight);
  border-radius: 2px;
  font-size: 10px;
  color: var(--muted-fg);
}
.execute-runtime-effects-label {
  font-weight: 600;
  margin-right: 2px;
}
.execute-effects-drift {
  outline: 1px dashed var(--error-fg);
  outline-offset: 1px;
}
/* "Declared but not observed at runtime" — fn-def promised this
 * effect but the actual run didn't fire it. Could be a conditional
 * branch that wasn't taken (legitimate) OR an over-declaration in
 * the rich-type (cleanup signal). Greyed-out + dashed outline
 * distinguishes from the red-outline runtime-drift case above. */
.execute-effects-unobserved {
  outline: 1px dashed var(--muted-fg);
  outline-offset: 1px;
  opacity: 0.45;
}

/* Service settings popover — anchored from the ⚙ button in the
 * row-actions popover. Compact form for managing a :service row
 * (declare / edit / delete / reconcile). Visual language matches
 * the execute popover so the two read as siblings. */
.service-popover {
  position: fixed; z-index: 10002;
  display: none;
  background: var(--card-bg); color: var(--card-fg);
  border: 1px solid var(--card-border);
  border-left: 3px solid var(--accent);
  border-radius: 4px;
  box-shadow: var(--shadow-lg);
  padding: 10px 12px;
  min-width: 280px; max-width: 360px;
  font-family: inherit; font-size: 12px; line-height: 1.4;
}
.service-popover.visible { display: block; }

.service-popover-header {
  display: flex; align-items: center; justify-content: space-between;
  border-bottom: 1px solid var(--light-border);
  margin-bottom: 8px; padding-bottom: 4px;
}
/* Tenant-mode (client-rendered) body — the platform partial uses its own
   layout; these style only the minimal dedicated-tenant form. */
.service-popover-body {
  display: flex; flex-direction: column; gap: 10px;
  padding: 2px 2px 6px;
}
.service-popover-enabled-row {
  display: flex; align-items: center; gap: 6px;
  font-size: 12px;
}
.service-popover-note {
  margin: 0; font-size: 11px; color: var(--muted-fg);
}
.service-popover-title {
  font-weight: 600; font-size: 13px;
  color: var(--accent);
}
.service-popover-close {
  width: 22px; height: 22px; padding: 0;
  background: transparent; color: var(--card-fg);
  border: 0; border-radius: 3px;
  font-size: 20px; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  opacity: 0.65;
}
.service-popover-close:hover {
  opacity: 1; background: var(--overlay-pressed);
}

.service-popover-status {
  font-size: 11px;
  color: var(--muted-fg);
  margin-bottom: 8px;
  padding: 4px 6px;
  background: var(--card-row-highlight);
  border-radius: 2px;
}

.service-popover-option {
  display: flex; align-items: center;
  cursor: pointer;
  padding: 2px 0;
}

.service-popover-policy {
  margin: 8px 0;
}
.service-popover-policy-label {
  font-weight: 600;
  font-size: 11px;
  margin-bottom: 4px;
}
.service-popover-policy-option {
  display: inline-flex; align-items: center;
  margin-right: 10px;
  cursor: pointer;
  font-size: 11px;
}

/* Cross-branch sibling-services warning — appears when another
   :service row targets the same fn-id on a different branch.
   Dashed border + warn-tone background so it visually separates
   from regular status text without screaming. */
.service-popover-sibling-warn {
  margin: 6px 0;
  padding: 4px 8px;
  font-size: 11px;
  color: var(--fg);
  background: color-mix(in srgb, var(--warning-fg) 12%, var(--card-bg));
  border: 1px dashed var(--warning-fg);
  border-radius: 3px;
  cursor: help;
}

/* Branch picker — per-branch service scoping. The select is full-
   width so longer branch names don't truncate; size matches the
   restart-policy block visually. */
.service-popover-branch {
  margin: 8px 0;
}
.service-popover-branch-label {
  font-weight: 600;
  font-size: 11px;
  margin-bottom: 4px;
}
.service-popover-branch-select {
  width: 100%;
  font-family: inherit;
  font-size: 11px;
  padding: 2px 4px;
  background: var(--card-bg);
  color: var(--fg);
  border: 1px solid var(--card-border);
  border-radius: 3px;
}

.service-popover-actions {
  display: flex; gap: 6px;
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid var(--light-border);
}
.service-popover-save-btn,
.service-popover-delete-btn {
  font-family: inherit; font-size: 12px;
  padding: 4px 10px;
  border-radius: 3px;
  cursor: pointer;
}
.service-popover-save-btn {
  background: var(--accent); color: var(--card-bg);
  border: 1px solid var(--accent);
}
.service-popover-save-btn:hover:not(:disabled) {
  filter: brightness(1.1);
}
.service-popover-save-btn:disabled {
  opacity: 0.6; cursor: not-allowed;
}
.service-popover-delete-btn {
  background: transparent; color: var(--error-fg);
  border: 1px solid var(--error-fg);
}
.service-popover-delete-btn:hover:not(:disabled) {
  background: color-mix(in srgb, var(--error-fg) 12%, transparent);
}
.service-popover-delete-btn:disabled {
  opacity: 0.5; cursor: not-allowed;
}

/* Service badge — small status dot on the root row of any fn-card
 * whose fn is declared as a :service. Sits between the fn name and
 * the row-actions trigger; click opens the service settings popover.
 * Colors reuse design tokens used elsewhere for analogous status
 * (success / error / muted) so themes stay coherent. */
/* Floated over the row's left edge (next to the fn name) instead of
 * inserted into the flex flow — avoids ellipsizing long fn names
 * just to make room for a 9-px dot. Z-indexed above the row bg
 * stripe; pointer events kept on so clicks still open the popover. */
.service-badge {
  position: absolute;
  left: 2px; top: 50%;
  transform: translateY(-50%);
  width: 8px; height: 8px;
  font-size: 9px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  user-select: none;
  z-index: 1;
}
.service-badge:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.service-badge-running  { color: var(--success-fg); }
.service-badge-failed   { color: var(--error-fg); }
.service-badge-disabled { color: var(--muted-fg); opacity: 0.65; }
.service-badge-pending  {
  color: var(--muted-fg);
  animation: service-badge-pulse 1.5s ease-in-out infinite;
}
@keyframes service-badge-pulse {
  0%, 100% { opacity: 0.45; }
  50%      { opacity: 1; }
}

.execute-popover-body {
  margin-bottom: 10px;
}
.execute-no-args-note {
  font-style: italic;
  color: var(--muted-fg);
  padding: 4px 0;
}

.execute-arg-row {
  margin-bottom: 8px;
}
.execute-arg-label {
  font-weight: 600;
  font-size: 11px;
  color: var(--muted-fg);
  margin-bottom: 2px;
}
.execute-arg-form {
  padding: 4px 6px;
  background: var(--card-row-highlight);
  border: 1px solid var(--light-border);
  border-radius: 2px;
}
.execute-arg-form-error {
  color: var(--error-fg);
  font-style: italic;
}

.execute-options-row {
  display: flex; flex-direction: column; gap: 4px;
  margin-bottom: 8px;
}
.execute-option-label {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px;
  color: var(--muted-fg);
  cursor: pointer;
  user-select: none;
}
.execute-option-label input[type="checkbox"] { margin: 0; }
/* Confirm-effects checkbox sits in red so the gate is hard to miss
 * when the run will produce side effects. */
.execute-confirm-label { color: var(--error-fg); font-weight: 600; }
.execute-option-label-locked {
  cursor: help;
  opacity: 0.7;
}

.execute-action-bar {
  display: flex; gap: 8px;
  margin-bottom: 8px;
}
.execute-run-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.execute-run-btn, .execute-cancel-btn {
  padding: 4px 12px;
  font-family: inherit; font-size: 12px;
  border: 1px solid var(--card-border);
  border-radius: 3px;
  background: var(--card-bg); color: var(--card-fg);
  cursor: pointer;
}
.execute-run-btn {
  background: var(--accent);
  color: var(--card-bg);
  border-color: var(--accent);
  font-weight: 600;
}
.execute-run-btn:hover { opacity: 0.9; }
.execute-cancel-btn:hover { background: var(--hover-bg); }

.execute-result-host {
  border-top: 1px solid var(--light-border);
  padding-top: 8px;
}
.execute-submit-spinner, .execute-pending-pane {
  display: flex; align-items: center; gap: 6px;
  font-size: 11px;
  color: var(--muted-fg);
}
.execute-pending-spinner {
  font-size: 14px;
  color: var(--accent);
}
.execute-pending-text { font-family: inherit; }

.execute-result-pane {
  font-family: monospace; font-size: 11.5px;
  overflow-x: auto;
}
.execute-result-scalar {
  padding: 6px 8px;
  background: var(--card-row-highlight);
  border-radius: 2px;
  font-weight: 600;
}
.execute-result-list {
  list-style: disc inside;
  padding-left: 4px;
  margin: 0;
}
.execute-result-list li { padding: 2px 0; }
.execute-result-list-more {
  list-style: none;
  font-style: italic;
  color: var(--muted-fg);
}
.execute-result-record {
  border-collapse: collapse;
  width: 100%;
}
.execute-result-record th, .execute-result-record td {
  text-align: left; padding: 3px 6px;
  border-bottom: 1px solid var(--light-border);
  vertical-align: top;
}
.execute-result-record th {
  font-weight: 600;
  color: var(--muted-fg);
  width: 30%;
}
.execute-result-record pre {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.execute-result-json {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  margin: 0;
  padding: 6px 8px;
  background: var(--card-row-highlight);
  border-radius: 2px;
}
.execute-result-truncated {
  padding: 4px 6px;
  margin-bottom: 6px;
  background: var(--card-row-highlight);
  border-left: 2px solid var(--error-fg);
  border-radius: 2px;
  font-style: italic;
  font-size: 11px;
  color: var(--muted-fg);
}
.execute-cancelled {
  padding: 4px 6px;
  font-style: italic;
  color: var(--muted-fg);
}

.execute-error-pane {
  padding: 6px 8px;
  background: var(--card-row-highlight);
  border-left: 2px solid var(--error-fg);
  border-radius: 2px;
}
.execute-error-head {
  font-weight: 600;
  color: var(--error-fg);
  margin-bottom: 4px;
}
.execute-error-data {
  margin: 0;
  font-family: monospace; font-size: 11px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}


/* History panel — collapsible list of persisted runs for the
 * current fn. Each row shows status / timestamp / duration /
 * result preview. Click a row to expand its full result into the
 * main result host; Repeat re-fills the form with that run's args. */
.execute-history-host {
  margin-bottom: 8px;
  padding: 6px 8px;
  background: var(--card-row-highlight);
  border-left: 2px solid var(--accent);
  border-radius: 2px;
  max-height: 200px;
  overflow-y: auto;
}
/* Phase C2 error-log sidebar section. */
.error-log-row { padding: 6px 8px; border-bottom: 1px dashed var(--light-border);
  font-size: 11px; }
.error-log-fn { color: var(--accent); display: block; }
.error-log-time { color: var(--light-fg); }
.error-log-msg { color: var(--error-fg); overflow-wrap: anywhere; }
.error-log-data { font-size: 10px; white-space: pre-wrap; word-break: break-all;
  color: var(--muted-fg); margin: 2px 0 0; }
.error-log-details summary { cursor: pointer; color: var(--muted-fg); font-size: 10px; }
.error-log-empty { padding: 6px 8px; color: var(--muted-fg); font-size: 11px; }

/* Phase C stats sidebar section — org-scoped run rollups. */
.stats-panel { padding: 4px 8px 8px; font-size: 11px;
  max-height: 480px; overflow-y: auto; }
.stats-headline { color: var(--light-fg); font-weight: 600; padding: 4px 0 6px; }
.stats-subhead { color: var(--muted-fg); text-transform: uppercase;
  letter-spacing: 0.5px; font-size: 10px; margin: 8px 0 2px; }
.stats-table { width: 100%; border-collapse: collapse; }
.stats-table th { text-align: right; color: var(--muted-fg); font-weight: 500;
  font-size: 10px; padding: 2px 4px; border-bottom: 1px solid var(--light-border); }
.stats-table th:first-child { text-align: left; }
.stats-row { border-bottom: 1px dashed var(--light-border); }
.stats-day { color: var(--light-fg); font-variant-numeric: tabular-nums; }
.stats-fn-cell { overflow-wrap: anywhere; }
.stats-fn { color: var(--accent); }
.stats-num { text-align: right; font-variant-numeric: tabular-nums;
  color: var(--light-fg); padding: 2px 4px; }
.stats-failed { color: var(--error-fg); }
.stats-empty { padding: 4px 0; color: var(--muted-fg); }
.stats-period-bar { display: flex; gap: 4px; margin: 2px 0 6px; }
.stats-period { font-size: 10px; padding: 2px 8px; border-radius: 3px;
  border: 1px solid var(--light-border); background: none;
  color: var(--muted-fg); cursor: pointer; }
.stats-period:hover { background: var(--hover-bg); color: var(--fg); }
.stats-period-active { border-color: var(--accent); color: var(--accent); }
.stats-bar-cell { width: 56px; padding: 2px 4px; }
.stats-bar { height: 6px; border-radius: 3px; background: var(--hover-bg);
  overflow: hidden; }
.stats-bar-fill { height: 100%; border-radius: 3px; background: var(--accent);
  transition: opacity 0.15s; }
.stats-org-block { margin-top: 2px; }

/* Phase C1 usage-rollup strip above the history rows. */
.execute-history-stats { font-size: 11px; color: var(--muted-fg);
  padding: 4px 6px; border-bottom: 1px dashed var(--light-border); }
.execute-history-empty {
  font-size: 11px;
  font-style: italic;
  color: var(--muted-fg);
}
.execute-history-panel {
  display: flex; flex-direction: column; gap: 4px;
}
.execute-history-row {
  padding: 4px 6px;
  border-bottom: 1px solid var(--light-border);
  cursor: pointer;
}
.execute-history-row:last-child { border-bottom: 0; }
.execute-history-row:hover { background: var(--hover-bg); }
.execute-history-row-head {
  display: flex; align-items: center; gap: 8px;
  font-size: 10.5px;
  line-height: 1.4;
}
.execute-history-status {
  font-weight: 600;
  text-transform: uppercase;
  font-size: 9px;
  padding: 1px 4px;
  border-radius: 2px;
}
.execute-history-status-succeeded {
  color: var(--success-fg);
  background: color-mix(in srgb, var(--success-fg) 15%, transparent);
}
.execute-history-status-failed {
  color: var(--error-fg);
  background: color-mix(in srgb, var(--error-fg) 15%, transparent);
}
.execute-history-status-cancelled, .execute-history-status-pending {
  color: var(--muted-fg);
  background: var(--light-border);
}
.execute-history-ts {
  color: var(--muted-fg);
  font-family: monospace;
}

.execute-history-repeat-btn {
  margin-left: auto;
  padding: 1px 6px;
  font-family: inherit; font-size: 10px;
  background: transparent;
  color: var(--accent);
  border: 1px solid var(--card-border);
  border-radius: 3px;
  cursor: pointer;
}
.execute-history-repeat-btn:hover { background: var(--hover-bg); }
.execute-history-preview {
  font-family: monospace; font-size: 10.5px;
  color: var(--card-fg);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* =========================================================================
   SECRETS — sidebar section + 🔒 indicator + create / rotate popover.
   ========================================================================= */

#entity-list .entity-secret { display: flex; align-items: center; gap: 4px; }
#entity-list .entity-secret .name { flex: 0 0 auto; }
#entity-list .entity-secret .secret-path {
  flex: 1 1 auto;
  font-family: monospace;
  font-size: 11px;
  color: var(--card-fg);
  opacity: 0.7;
  margin-left: 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.secret-lock-icon {
  font-size: 11px;
  margin-right: 2px;
  flex: 0 0 auto;
}

.secrets-popover {
  position: absolute;
  z-index: 1000;
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: 6px;
  padding: 12px;
  box-shadow: var(--shadow-lg);
  min-width: 280px;
}

.secrets-popover .popover-title {
  font-weight: 600;
  margin-bottom: 8px;
  color: var(--card-fg);
}

.secrets-popover .popover-label {
  display: block;
  margin-bottom: 8px;
  font-size: 12px;
  color: var(--card-fg);
}

.secrets-popover .popover-label .muted {
  color: var(--muted-fg);
  font-weight: normal;
}

.secrets-popover input {
  display: block;
  width: 100%;
  margin-top: 2px;
  padding: 4px 6px;
  background: var(--card-bg);
  color: var(--card-fg);
  border: 1px solid var(--input-border);
  border-radius: 3px;
  font-family: inherit;
  font-size: 12px;
}

.secrets-popover .muted {
  color: var(--muted-fg);
  font-size: 11px;
  margin-bottom: 8px;
}

.secrets-popover .popover-error {
  color: var(--error-fg);
  font-size: 11px;
  margin: 4px 0 8px;
}

.secrets-popover .popover-buttons {
  display: flex;
  justify-content: flex-end;
  gap: 6px;
}

.secrets-popover .popover-buttons button {
  padding: 4px 10px;
  background: var(--card-bg);
  color: var(--card-fg);
  border: 1px solid var(--card-border);
  border-radius: 3px;
  cursor: pointer;
  font-size: 12px;
}

.secrets-popover .popover-buttons button.primary {
  background: var(--accent);
  color: var(--header-fg);
  border-color: var(--accent);
}

.secrets-ns-chip {
  display: inline-block;
  margin-top: 2px;
  padding: 3px 8px;
  background: var(--card-bg);
  color: var(--card-fg);
  border: 1px solid var(--input-border);
  border-radius: 12px;
  font-family: inherit;
  font-size: 12px;
  cursor: pointer;
  text-align: left;
  min-width: 80px;
}

.secrets-ns-chip:hover {
  background: var(--hover-bg);
}

/* =========================================================================
   TAINTED EXECUTE — Result pane + History badge for :secret-typed runs.
   Shown when the fn's effective return-type carries the :secret marker;
   the backend redacts the value and the editor refuses to surface
   anything (even an attempt at rendering the null result body — the
   tainted pane stands in for the result entirely).
   ========================================================================= */

.execute-result-tainted {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 8px 10px;
  border: 1px solid var(--card-border);
  background: var(--card-bg);
  border-radius: 4px;
}

.execute-tainted-icon {
  font-size: 18px;
  line-height: 1;
  margin-top: 1px;
  flex: 0 0 auto;
}

.execute-tainted-text {
  flex: 1 1 auto;
  font-size: 12px;
  color: var(--card-fg);
}

.execute-tainted-head {
  font-weight: 600;
  margin-bottom: 2px;
}

.execute-tainted-body {
  color: var(--muted-fg);
}



/* Audit trail badge + filter. Distinct from the
   tainted-result badge above — fires when the fn touched a secret
   AND observed a side-effect, even if its return-type is plain. */







/* Row-actions popover content (server-rendered partial; HTMX
   migration Phase A1). Flex container hosts ns / i / ↗ buttons.
   The wrapping `.row-actions-popover` (built JS-side) handles
   background / border / shadow; this rule covers only the inner
   layout + the loading / error placeholders during the swap-in. */
.row-actions-content {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.row-actions-loading,
.row-actions-error {
  font-size: 11px;
  padding: 0 4px;
}
.action-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: var(--icon-size);
  height: var(--icon-size);
  line-height: 1;
  border: 1px solid currentColor;
  border-radius: 3px;
  font-size: var(--icon-font-size);
  background: transparent;
  color: currentColor;
  padding: 0;
  cursor: pointer;
  text-decoration: none;
}
.action-icon:hover {
  background: var(--hover-bg);
}
.action-icon-danger {
  color: var(--error-fg);
}
.action-icon-disabled {
  opacity: 0.45;
  cursor: help;
}
.action-icon-disabled:hover {
  background: transparent;
}
.row-actions-content .namespace-badge {
  /* The legacy `applyActionIconBox` set font-size 1px smaller for
     the 2-char "ns" glyph in the same 15×15 frame — preserve here. */
  font-size: calc(var(--icon-font-size) - 1px);
  font-style: italic;
  font-weight: normal;
}




/* Org-admin Grants (§6) + Users (§4.1) panels — sidebar sections rendered from
   /partials/grants-admin and /partials/users-admin. Both share these styles;
   only shown to authed users when the tenancy addon is active (see
   editor-grants-admin.js / editor-users-admin.js). */
.sidebar-grants-admin .grants-admin-table,
.sidebar-users-admin .grants-admin-table { width: 100%; font-size: 11px; border-collapse: collapse; }
.sidebar-grants-admin .grants-admin-table th,
.sidebar-grants-admin .grants-admin-table td,
.sidebar-users-admin .grants-admin-table th,
.sidebar-users-admin .grants-admin-table td { text-align: left; padding: 2px 4px; border-bottom: 1px solid var(--border); }
.sidebar-grants-admin .grants-admin-disabled,
.sidebar-users-admin .grants-admin-disabled { padding: 6px 8px; color: var(--muted-fg); font-style: italic; }

/* Apps sidebar section (Track C4b) — the tenant org's named apps
   (:app-route rows) rendered from /partials/apps-panel: a table + a create
   form + per-row delete. Auth-required, org-scoped (editor-apps.js). */
.sidebar-apps .apps-admin-table { width: 100%; font-size: 11px; border-collapse: collapse; }
.sidebar-apps .apps-admin-table th,
.sidebar-apps .apps-admin-table td { text-align: left; padding: 2px 4px; border-bottom: 1px solid var(--border); }
.sidebar-apps .app-row-label { font-weight: 600; color: var(--fg); }
.sidebar-apps .app-host-link { color: var(--fg); text-decoration: none; }
.sidebar-apps .app-host-link:hover { color: var(--accent); text-decoration: underline; }
.sidebar-apps .app-serves-link { color: var(--accent); text-decoration: none; }
.sidebar-apps .app-serves-link:hover { text-decoration: underline; }
.sidebar-apps .apps-admin-disabled { padding: 6px 8px; color: var(--muted-fg); font-style: italic; }
.sidebar-apps .app-delete-form { display: inline; }
.sidebar-apps .app-delete { background: none; border: none; color: var(--muted-fg); cursor: pointer; padding: 0 4px; line-height: 1; }
.sidebar-apps .app-delete:hover { color: var(--error-fg); }
/* Create form — hint + stacked inputs + add button. */
.sidebar-apps .app-create-form { display: flex; flex-direction: column; gap: 3px; padding: 4px; }
.sidebar-apps .apps-create-hint { font-size: 10px; color: var(--muted-fg); }
.sidebar-apps .app-input { font-size: 10px; padding: 1px 3px; border: 1px solid var(--input-border); border-radius: 3px; background: var(--card-bg); color: var(--fg); }
.sidebar-apps .app-create-btn { align-self: flex-start; background: none; border: 1px solid var(--input-border); border-radius: 3px; color: var(--accent); cursor: pointer; font-size: 10px; padding: 1px 6px; }
.sidebar-apps .app-create-btn:hover { background: var(--hover-bg); }

/* Packages sidebar section (3e) — installed pins + browse/install registry. */
.sidebar-packages .packages-panel-table { width: 100%; font-size: 11px; border-collapse: collapse; }
.sidebar-packages .packages-panel-table th,
.sidebar-packages .packages-panel-table td { text-align: left; padding: 2px 4px; border-bottom: 1px solid var(--border); }
.sidebar-packages .packages-panel-empty { padding: 6px 8px; color: var(--muted-fg); font-style: italic; }
.sidebar-packages .packages-uninstall { background: none; border: none; color: var(--muted-fg); cursor: pointer; padding: 0 4px; line-height: 1; }
.sidebar-packages .packages-uninstall:hover { color: var(--error-fg); }
/* Browse/install registry — native <details> disclosure below the pins. */
.sidebar-packages .packages-available { margin-top: 6px; border-top: 1px solid var(--border); }
.sidebar-packages .packages-available > summary { padding: 4px; font-size: 11px; color: var(--muted-fg); cursor: pointer; }
.sidebar-packages .packages-available > summary:hover { color: var(--fg); }
.sidebar-packages .packages-install-btn { background: none; border: 1px solid var(--input-border); border-radius: 3px; color: var(--accent); cursor: pointer; font-size: 10px; padding: 1px 6px; }
.sidebar-packages .packages-install-btn:hover { background: var(--hover-bg); }
/* per-row update/rollback form — version input + ↑ submit, inline with ×. */
.sidebar-packages .packages-update-form { display: inline-flex; gap: 2px; align-items: center; }
.sidebar-packages .packages-version-input { width: 44px; font-size: 10px; padding: 0 2px; border: 1px solid var(--input-border); border-radius: 3px; background: var(--card-bg); color: var(--fg); }
.sidebar-packages .packages-update-btn { background: none; border: none; color: var(--muted-fg); cursor: pointer; padding: 0 2px; line-height: 1; }
.sidebar-packages .packages-update-btn:hover { color: var(--accent); }
/* Fork button (browse row) + transient fork notice. */
.sidebar-packages .packages-fork-btn { background: none; border: 1px solid var(--input-border); border-radius: 3px; color: var(--muted-fg); cursor: pointer; font-size: 10px; margin-left: 3px; padding: 1px 6px; }
.sidebar-packages .packages-fork-btn:hover { background: var(--hover-bg); color: var(--fg); }
.sidebar-packages .packages-fork-note { font-size: 10px; padding: 3px 6px; margin-bottom: 4px; border-radius: 3px; }
.sidebar-packages .packages-fork-ok { color: var(--success-fg); }
.sidebar-packages .packages-fork-err { color: var(--error-fg); }
/* Publish-a-namespace form — stacked inputs + Publish submit. */
.sidebar-packages .packages-publish-form { display: flex; flex-direction: column; gap: 3px; padding: 4px; }
.sidebar-packages .packages-publish-input { font-size: 10px; padding: 1px 3px; border: 1px solid var(--input-border); border-radius: 3px; background: var(--card-bg); color: var(--fg); }

.sidebar-grants-admin .grant-delete,
.sidebar-users-admin .grant-delete { background: none; border: none; color: var(--muted-fg); cursor: pointer; padding: 0 4px; }
.sidebar-grants-admin .grant-delete:hover,
.sidebar-users-admin .grant-delete:hover { color: var(--error-fg); }
.sidebar-grants-admin .grant-create-form,
.sidebar-users-admin .grant-create-form { display: flex; flex-direction: column; gap: 4px; padding: 6px 8px; }
.sidebar-grants-admin .grant-input,
.sidebar-users-admin .grant-input { font-size: 11px; padding: 2px 4px; }
.sidebar-grants-admin .grant-create-btn { font-size: 11px; padding: 2px 6px; cursor: pointer; align-self: flex-start; }

/* Set-as-app-handler (⌂) row-action — FaaS self-serve deploy (§3.4 4b).
   Hidden unless the tenancy addon is active (gd-tenancy set once a capability
   header arrives), so single-tenant editors never show it. */
body:not(.gd-tenancy) .action-deploy { display: none; }
