/* ============================================================
   Swiftpoint Hub — Profile Builder v3

   Full-viewport app shell: fixed header + 3-column workspace, no
   page scroll (each panel scrolls internally).

   Tokens and the reset come from the shared layers (tokens.css,
   base.css). The header shell comes from css/hdr.css, overridden
   below where the app needs a wider bar and an opaque backdrop.
   ============================================================ */

/* ---- Shell geometry ----
   The header itself is css/hdr.css and nothing here touches it: same
   height, same width, same colours as on every other page. The builder
   earns its width from `.ed-workspace` below, not from a wider bar.

   The flex column and the non-scrolling body come from the shared
   `body[data-app-shell]` rule in css/base.css. Only the app's own
   overflow behaviour is set here. */
html,
body {
  height: 100%;
  overflow: hidden;
}

/* ── Buttons ──
   `.ed-btn` is the builder's own vocabulary, used inside the panels
   (group list, binding editor). The header strip does not use it — that
   is `.tbar-btn` from the shared toolbar, so a strip button looks the
   same here as it does in the OLED editor. */
.ed-btn {
  padding: 5px 12px; font-size: var(--fs-xs); font-weight: 500;
  background: var(--surface-2);
  border: 1px solid transparent; border-radius: var(--r-xs);
  color: var(--text-2); white-space: nowrap;
  transition: background 0.12s, color 0.12s;
}
.ed-btn:hover:not(:disabled) { background: var(--surface-3); color: var(--text); }
.ed-btn:active:not(:disabled) { transform: translateY(1px); }
.ed-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.ed-btn-tiny { padding: 2px 8px; font-size: 12.5px; font-weight: 600; }
.ed-btn-primary {
  background: var(--accent);
  border-color: transparent;
  color: var(--on-accent);
  font-weight: 600;
}
.ed-btn-primary:hover:not(:disabled) { background: var(--accent-hi); color: var(--on-accent); }
.ed-btn-danger { color: var(--red); }
.ed-btn-danger:hover { background: var(--red-dim); }

/* ── Workspace: fills whatever the header leaves ──
   Four columns, two of them conditional and both resizable:
     rail | centre | binding | (nothing when the binding panel is closed)
   `--ed-rail-w` and `--ed-side-w` are written by editor.js while an edge
   is dragged and are persisted, so a wide panel or a hidden rail is a
   choice made once rather than every session.

   The centre is `minmax(0, 1fr)`, never a bare `1fr`: a bare fr has an
   automatic minimum of its content, so a wide diagram can push the
   binding panel out of the grid entirely. */
.ed-workspace {
  --ed-rail-w: 288px;
  flex: 1 1 auto;
  min-height: 0;
  /* No max-width. The builder is a working tool, not a page of prose: on a
     wide monitor it should use the monitor. Reading-width constraints belong
     on the guides, not here. */
  width: 100%;
  padding: 12px 16px;
  /* Two columns. The binding editor is a view, not a third column: it used
     to be a permanent panel that the window gave up a third of its width
     to, empty until you picked something. */
  display: grid;
  grid-template-columns: var(--ed-rail-w) minmax(0, 1fr);
  grid-template-rows: 100%;
  gap: 12px;
  overflow: hidden;
}
/* Collapsed rail: the layers panel swaps for a narrow strip, so the grid
   keeps its shape and only the first column shrinks. */
.ed-workspace:has(> .ed-rail-collapsed:not([hidden])) {
  grid-template-columns: 44px minmax(0, 1fr);
}
.ed-panel-left { position: relative; min-width: 0; }
/* A hidden panel is a grid child, so `hidden` has to win over its display. */
.ed-workspace > .ed-panel[hidden] {
  display: none;
}

/* Drag handle on the rail's right edge — the mirror of the binding
   panel's, since the two share a wall. */
.ed-resize--rail {
  left: auto;
  right: -7px;
}

/* The collapsed strip is a plain row of the same buttons, so nothing you
   could do from the rail becomes impossible by collapsing it. */
.ed-rail-collapsed {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-1);
  padding: 8px 0;
  background: var(--bg-raise);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
}
.ed-rail-collapsed[hidden] {
  display: none;
}

.ed-resize:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  border-radius: var(--r-sm);
}
/* ── Status bar ──
   Thin, quiet, and always in the same place: where you are, what the
   document is called, whether it is unsaved, and how many problems the
   editor can see. */
.ed-status {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  height: 28px;
  padding: 0 var(--sp-4);
  border-top: 1px solid var(--line);
  background: var(--bg-raise);
  color: var(--text-3);
  font-family: var(--font-mono);
  font-size: 11px;
  white-space: nowrap;
  overflow: hidden;
}
.ed-status-item { flex: 0 0 auto; }
#ed-status-layer {
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 40ch;
  color: var(--text-2);
}
#ed-status-doc { overflow: hidden; text-overflow: ellipsis; max-width: 34ch; }
.ed-status-grow { flex: 1 1 auto; min-width: var(--sp-2); }
.ed-status-sep {
  flex: 0 0 auto;
  width: 1px;
  height: 12px;
  background: var(--line);
}
.ed-status-btn {
  border: 0;
  background: none;
  padding: 2px 6px;
  border-radius: var(--r-xs);
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.ed-status-btn:hover { background: var(--surface-2); color: var(--text); }
.ed-status-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.ed-status-btn.has-issues { color: var(--orange); }
.ed-status-btn.is-clean { color: var(--text-3); }
#ed-status-dirty.is-dirty { color: var(--orange); }
#ed-status-dirty.is-dirty::before {
  content: '';
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-right: 6px;
  border-radius: 50%;
  background: var(--orange);
  vertical-align: middle;
}

/* ── Binding editor (a view, full width) ──
   It used to be a side panel. As a view it gets the whole centre, so it
   carries its own header — otherwise there is nothing telling you what
   you are editing or how to get back. */
#view-binding { display: none; }
#view-binding.active { display: flex; flex-direction: column; }
.ed-be-bar {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex: 0 0 auto;
  padding: 0 var(--sp-3) var(--sp-2);
  border-bottom: 1px solid var(--line);
}
.ed-be-bar-title {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ed-be-bar-hint {
  flex: 0 0 auto;
  color: var(--text-3);
  font-size: var(--fs-2xs);
  white-space: nowrap;
}
/* ── The editor's layout ──
   The editor used to be a 900px single column in the middle of the window,
   which left most of a wide monitor empty. The sections now say what they
   are, and the grid puts them where they belong:

     trigger | on activation | on deactivation
     -----------------------------------------  (sequence & label, full width)

   Activation and deactivation sit side by side because they are read
   against each other — "what happens when I press this" next to "what
   happens when I let go" is the comparison you actually make. */
.ed-be-grid {
  display: grid;
  /* Two columns, and everything tall gets one of them.
   *
   * Reading order is chosen rather than inherited: the two action lists are
   * the tall parts and the only pair worth comparing side by side, so they
   * get a column each. The trigger is a short row of fields that reads
   * fine across the full width. Sequence and label is two fields and rarely
   * touched, so it goes to the top and out of the way of the work.
   *
   * The press card and the editor's own head each get their own row. They
   * used to share one named area, which in CSS grid means the same cells —
   * so "Mapping #1" and "Press stages" were drawn on top of each other. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  grid-template-areas:
    "summary    summary"
    "header     header"
    "rest       rest"
    "trigger    trigger"
    "activation deactivation";
  gap: var(--sp-4) var(--sp-4);
  align-items: start;
}
.ed-be-summary { grid-area: summary; }
.ed-be-head--wide { grid-area: header; }
.ed-section--rest { grid-area: rest; }
.ed-section--trigger { grid-area: trigger; }
.ed-section--activation { grid-area: activation; }
.ed-section--deactivation { grid-area: deactivation; }

/* The trigger is a row of fields, not a column of them, so lay them out
   side by side and let them wrap rather than stacking. */
.ed-section--trigger .ed-fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--sp-2) var(--sp-3);
}
.ed-section--trigger .ed-f { flex-direction: column; align-items: stretch; gap: 3px; }
.ed-section--trigger .ed-f > .ed-f-label { flex: none; }
/* And the two short rows are not really sections — they read as one
   control group each, so they lose the section chrome. */
.ed-section--rest { margin-bottom: 0; }
.ed-section--rest .ed-section-head { border-bottom: 0; padding-bottom: 0; margin-bottom: var(--sp-1); }

/* Two columns hold as long as the pane can give each of them something
   usable. Keyed off the pane, not the window, because the divider decides
   how wide it is. */
@container editor (max-width: 700px) {
  .ed-be-grid {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "summary"
      "header"
      "rest"
      "trigger"
      "activation"
      "deactivation";
  }
}

/* The editor's own two-column layout, set below. */
.ed-be-scroll {
  padding: var(--sp-4) var(--sp-5) var(--sp-6);
  width: 100%;
}

/* Panels are surfaces, not boxes. The old editor drew a 1px border around
   every region, so the whole window read as a grid of frames; removing the
   border and letting the surface tint do the separating is most of why the
   result looks like a different program. */
.ed-panel {
  background: var(--bg-raise);
  border-radius: var(--r-md);
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.ed-panel-head {
  font-family: var(--font-display);
  font-size: 11px; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-3);
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 12px 8px;
  flex: 0 0 auto;
}
.ed-panel-tools { display: flex; align-items: center; gap: 4px; }
.ed-mt { margin-top: 16px; }
.ed-scroll {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto;
  padding: 0 12px 12px;
}
.ed-scroll::-webkit-scrollbar { width: 8px; }
.ed-scroll::-webkit-scrollbar-thumb { background: var(--line-med); border-radius: 4px; }
.ed-scroll::-webkit-scrollbar-track { background: transparent; }

/* ── Groups ── */
.ed-groups { display: flex; flex-direction: column; gap: 2px; margin-bottom: 10px; }
.ed-group {
  display: flex; align-items: center; gap: 6px;
  width: 100%; text-align: left;
  background: transparent; border: 1px solid transparent;
  border-radius: var(--r-xs);
  color: var(--text-3); font-size: 12.5px;
  padding: 5px 7px;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.ed-group:hover { background: var(--surface); color: var(--text); }
.ed-group.active { background: var(--lime-dim); border-color: var(--lime-border); color: var(--text); }
.ed-group-name {
  flex: 1 1 auto;
  min-width: 0;
  /* Wrap rather than truncate. A layer name cut to "Productivity Set…" is
     ambiguous, and two lines of name is a much smaller cost than not being
     able to tell two layers apart. */
  overflow-wrap: anywhere;
  line-height: 1.3;
}
.ed-group-count {
  font-family: var(--font-mono); font-size: 10px; color: var(--text-4);
  background: var(--surface); border-radius: var(--r-pill); padding: 1px 6px;
}
.ed-group.active .ed-group-count { color: var(--lime); }

/* ── Profile meta ── */
.ed-meta-name { font-family: var(--font-display); font-size: 13px; font-weight: 600; word-break: break-word; }
.ed-meta-id { color: var(--text-4); font-size: 11px; margin-top: 2px; }
.ed-meta-stats { color: var(--text-3); font-size: 11px; margin-top: 4px; }

/* ── Diagnostics ── */
.ed-diag { font-size: 11.5px; }
.ed-diag.ok { color: var(--teal); }
.ed-diag.warn { color: var(--orange); }
.ed-diag-list { margin-top: 4px; display: flex; flex-direction: column; gap: 2px; }
.ed-diag-item {
  color: var(--text-2); font-size: 11px;
  border-left: 2px solid var(--orange);
  background: var(--surface);
  padding: 3px 7px; border-radius: 3px;
  word-break: break-word;
}
.ed-diag-readout { color: var(--text-4); font-size: 10.5px; margin-top: 6px; }

/* ── Diagnostics tab ── */
.ed-diag-okbig { color: var(--teal); font-size: 13px; padding: 14px 2px; }
.ed-diag-count { color: var(--orange); font-size: 12.5px; margin-bottom: 10px; }
.ed-diissue {
  display: flex; align-items: center; gap: 10px; justify-content: space-between;
  flex-wrap: wrap; background: var(--surface);
  border: 1px solid var(--line); border-left: 3px solid var(--orange);
  border-radius: 6px; padding: 7px 10px; margin-bottom: 7px;
}
.ed-diissue-text { flex: 1 1 auto; min-width: 220px; font-size: 12px; color: var(--text-2); word-break: break-word; }
.ed-diissue-actions { flex: 0 0 auto; display: flex; gap: 6px; flex-wrap: wrap; }
.ed-diissue-actions .ed-btn-danger { color: var(--danger); border-color: var(--danger); }
.ed-diag-item[data-diag-jump] { cursor: pointer; }
.ed-diag-item[data-diag-jump]:hover { border-color: var(--orange); background: var(--surface-2); }

/* Highlight of a mapping the user jumped to from Diagnostics. */
.ed-flash { animation: ed-flash 1.7s ease; }
@keyframes ed-flash {
  0%, 100% { box-shadow: none; }
  25%, 55% { box-shadow: 0 0 0 2px var(--lime); background: var(--surface-2); }
}

/* ── Center Panel ── */
.ed-center-header {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 10px;
  padding: 6px 12px;
  border-bottom: 1px solid var(--line);
}
.ed-group-name-input {
  background: transparent; border: 1px solid transparent;
  border-radius: var(--r-xs);
  color: var(--text); font-family: var(--font-display);
  font-size: 15px; font-weight: 600;
  padding: 3px 8px; flex: 1 1 auto; min-width: 100px;
}
.ed-group-name-input:hover { border-color: var(--line); }
.ed-group-name-input:focus { border-color: var(--lime); background: var(--surface); outline: none; }

.ed-center-tabs { display: flex; gap: 2px; flex: 0 0 auto; }
.ed-ctab {
  padding: 4px 12px; font-size: 12px; font-weight: 500;
  color: var(--text-3); border-radius: var(--r-xs);
  transition: background 0.12s, color 0.12s;
}
.ed-ctab:hover { color: var(--text); background: var(--surface); }
.ed-ctab.active { color: var(--lime); background: var(--lime-dim); }

/* The centre is a split: the views, and beside them the binding editor for
   whatever is selected. The editor used to replace the whole centre, which
   meant you could not see the button you were editing while you edited it —
   and that is the comparison the mouse grid exists to make.

   `--ed-editor-w` is written while the divider is dragged and remembered,
   so the split can be set once rather than every session. */
.ed-center-split {
  --ed-editor-w: 900px;
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  /* Two columns, always. The binding pane is part of the layout rather than
     something that appears and disappears, so a recording shows a screen
     that does not change shape between steps. */
  grid-template-columns: minmax(0, 1fr) var(--ed-editor-w);
  align-items: stretch;
}

.ed-pane-editor {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  /* Its own container, so the editor's internal columns respond to how
     wide this pane is — not to the window, which says nothing once the
     divider has been dragged. */
  container-type: inline-size;
  container-name: editor;
  border-left: 1px solid var(--line);
}
/* The pane is always part of the split now, so the column is always
   reserved. It used to collapse when nothing was selected, which made the
   layout change shape between steps and a recording harder to follow. */

.ed-center-scroll {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto;
  padding: 10px 12px 12px;
}
.ed-center-scroll::-webkit-scrollbar { width: 8px; }
.ed-center-scroll::-webkit-scrollbar-thumb { background: var(--line-med); border-radius: 4px; }
.ed-center-scroll::-webkit-scrollbar-track { background: transparent; }

.ed-view { display: none; }
.ed-view.active { display: block; animation: fadeUp 0.2s var(--ease); height: 100%; }
@keyframes fadeUp {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: none; }
}

/* ── Mouse Diagram ── */
/* A vertical stack of sections; each section sizes its own tile grid. */
.ed-mouse-diagram {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  align-content: start;
  min-height: 100%;
}
.ed-mouse-count {
  padding: 1px 6px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  font-size: 9px;
  letter-spacing: 0.06em;
  color: var(--text-4);
}
.ed-mouse-section {
  display: flex; flex-direction: column; gap: 5px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 8px 10px;
}
.ed-mouse-section-title {
  font-family: var(--font-display);
  font-size: 10px; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-4);
}

/* ── Mouse grid ──
   The centrepiece: every button on the device, grouped by where it sits on
   your hand. Each tile answers three questions at a glance — what is it,
   is anything bound to it, and what does it do.

   The tiles are real buttons so the grid is keyboard-reachable, which the
   old divs were not. Everything is a grid row rather than a free-flowing
   line, so the name, the count and the summary stack instead of fighting
   for horizontal space — the previous layout put all four on one line and
   the labels stopped lining up. */
.ed-mouse-grid {
  display: grid;
  /* Narrower tiles: the labels are short ("Left Button", "Wheel Pull") and
     the summary below them is one clipped line, so the old 148px floor was
     spending width on empty space. */
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: var(--sp-2);
}
.ed-mouse-section { margin-bottom: var(--sp-4); }
.ed-mouse-section.wide { grid-column: 1 / -1; }
.ed-mouse-section-title {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-2);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-3);
}
.ed-mouse-section-title::after {
  content: '';
  flex: 1 1 auto;
  height: 1px;
  background: var(--line);
}

.ed-mbtn {
  display: grid;
  grid-template-rows: auto auto auto;
  align-items: start;
  gap: 2px;
  width: 100%;
  padding: 7px 9px;
  border: 0;
  border-left: 2px solid transparent;
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--text-2);
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  text-align: left;
  cursor: pointer;
  transition: border-color var(--t-fast), background var(--t-fast), box-shadow var(--t-fast);
}
.ed-mbtn:hover { background: var(--surface-3); color: var(--text); }
.ed-mbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.ed-mbtn:active { transform: translateY(1px); }

/* Bound buttons carry the accent on their leading edge, so a scan down
   the column finds them without reading a word. */
.ed-mbtn.has-bindings { border-left: 2px solid var(--accent); }
.ed-mbtn.active {
  border-left-color: var(--accent);
  background: var(--accent-dim);
  color: var(--text);
}

.ed-mbtn-name {
  font-size: var(--fs-xs);
  font-weight: 600;
  line-height: 1.2;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ed-mbtn-holder {
  font-size: var(--fs-2xs);
  color: var(--text-3);
  line-height: 1.2;
}
.ed-mbtn-meta {
  display: flex;
  align-items: center;
  gap: 4px;
  min-height: 15px;
}
.ed-mbtn-badge {
  min-width: 16px;
  padding: 0 4px;
  border-radius: var(--r-xs);
  background: var(--accent-dim);
  color: var(--accent);
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  line-height: 15px;
  text-align: center;
}
.ed-mbtn-force {
  padding: 0 4px;
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  color: var(--text-3);
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.06em;
  line-height: 13px;
  text-transform: uppercase;
}
.ed-mbtn-binds {
  display: block;
  min-height: 15px;
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  line-height: 1.5;
  color: var(--text-3);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.ed-mbtn-none { color: var(--text-4); font-style: italic; }

.ed-mbtn-name {
  font-family: var(--font-display);
  font-size: 12px; font-weight: 600; color: var(--text);
  flex: 0 0 auto;
}
.ed-mbtn-holder { font-family: var(--font-mono); font-size: 9.5px; color: var(--text-4); }
.ed-mbtn-badge {
  font-family: var(--font-mono); font-size: 9.5px;
  color: var(--teal); margin-left: auto; flex: 0 0 auto;
}

/* ── Mapping List ── */
.ed-maplist { display: flex; flex-direction: column; gap: 6px; }
.ed-mapitem {
  display: flex; align-items: flex-start; gap: 10px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: 8px 10px;
  cursor: pointer; transition: border-color 0.12s, background 0.12s;
  position: relative;
}
.ed-mapitem:hover { border-color: var(--line-med); }
.ed-mapitem.active { border-color: var(--lime); background: var(--lime-dim); }
.ed-mapitem.sys { border-left: 3px solid var(--violet); }
.ed-mapitem-num {
  font-family: var(--font-mono); font-size: 10px;
  color: var(--text-4); padding-top: 2px; flex: 0 0 18px;
}
.ed-mapitem-body { flex: 1; min-width: 0; }
.ed-mapitem-trig { font-size: 13px; font-weight: 500; color: var(--text); }
.ed-mapitem-name {
  font-size: 11px; font-weight: 600; font-style: italic; color: var(--text);
  margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ed-mapitem-acts {
  font-family: var(--font-mono); font-size: 11px;
  color: var(--text-3); margin-top: 2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ed-mapitem-badge {
  display: inline-block; font-size: 9px; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--violet);
  border: 1px solid rgba(182,155,255,0.35); border-radius: var(--r-pill);
  padding: 1px 6px; margin-right: 5px; vertical-align: 1px;
}
.ed-mapitem-rm {
  color: var(--text-4); font-size: 14px; padding: 2px 5px;
  border-radius: 4px; flex: 0 0 auto; align-self: center;
  transition: color 0.12s, background 0.12s;
}
.ed-mapitem-rm:hover { color: var(--danger); background: var(--danger-dim); }

/* ── Binding Editor (right panel) ── */
.ed-be-head {
  display: flex; align-items: center; justify-content: space-between;
  flex: 0 0 auto;
  padding: 12px 14px 4px;
}
.ed-be-title {
  font-family: var(--font-display); font-size: 13px; font-weight: 600;
  color: var(--text);
}
.ed-be-sub { font-size: 11px; color: var(--text-3); }
.ed-be-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 8px 14px 12px; }
.ed-be-body::-webkit-scrollbar { width: 8px; }
.ed-be-body::-webkit-scrollbar-thumb { background: var(--line-med); border-radius: 4px; }

.ed-section {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 10px 12px;
  margin-bottom: 8px;
}
/* ═══════════════ Type & rhythm ═══════════════
   The editor was laid out for a narrow column and every size was tuned
   down to fit it. With the width back, the sizes are set here once rather
   than sprinkled through the rules above, so the whole app shares one
   scale and reads as a single program. */
.ed-panel,
.ed-workspace,
.ed-status {
  font-size: var(--fs-sm);
}

.ed-section { margin-bottom: var(--sp-5); }
.ed-section:last-child { margin-bottom: 0; }

/* Section heads become a quiet label and a rule, rather than a small
   uppercase slab. Nothing in the editor shouts. */
.ed-section-head {
  font-size: var(--fs-2xs);
  letter-spacing: 0.1em;
  color: var(--text-3);
  margin-bottom: var(--sp-3);
  padding-bottom: var(--sp-1);
  border-bottom: 1px solid var(--line);
}

/* Fields: a label column that can breathe, and controls that take the
   rest. The old 88px gutter was sized to keep the whole row narrow. */
.ed-f { gap: var(--sp-3); font-size: var(--fs-sm); }
.ed-f > .ed-f-label { flex: 0 0 108px; }
.ed-input,
.ed-f select,
.ed-f input[type="text"],
.ed-f input[type="number"] {
  font-size: var(--fs-sm);
}

/* The layer tree: rows rather than boxes, and the count carries meaning
   instead of just being a number. */
.ed-rail-filter {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex: 0 0 auto;
  padding: 0 var(--sp-3) var(--sp-2);
}
.ed-rail-filter .ed-input { flex: 1 1 auto; min-width: 0; }
.ed-rail-progress {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  color: var(--text-4);
  white-space: nowrap;
}
.ed-rail-progress.is-complete { color: var(--accent); }
.ed-rail-empty {
  padding: var(--sp-3);
  color: var(--text-4);
  font-size: var(--fs-xs);
  font-style: italic;
}
/* An unbound layer is what you are hunting for when filling one in, so a
   zero is dimmed rather than styled like a real count. */
.ed-group-count.is-empty { color: var(--text-4); }

.ed-group {
  border: 0;
  border-radius: var(--r-xs);
  background: transparent;
}
.ed-group:hover { background: var(--surface-2); }
.ed-group.active { background: var(--accent-dim); }

.ed-section-head {
  font-family: var(--font-display);
  font-size: 10.5px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-3);
  margin-bottom: 8px;
  display: flex; align-items: center; justify-content: space-between;
}
.ed-section-head .ed-section-count {
  font-family: var(--font-mono); font-size: 10px;
  color: var(--text-4); text-transform: none; letter-spacing: 0;
}

/* ── Form Fields ── */
.ed-fields { display: flex; flex-direction: column; gap: 7px; }
.ed-f {
  display: flex; align-items: center; gap: 8px;
  font-size: 12.5px; color: var(--text-2);
}
.ed-f > .ed-f-label {
  flex: 0 0 88px; font-size: 11.5px; color: var(--text-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ed-f select, .ed-f input[type="text"], .ed-f input[type="number"] {
  flex: 1; min-width: 0; width: 100%;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-xs); color: var(--text);
  font-size: 12.5px; padding: 4px 8px;
  transition: border-color 0.12s;
}
.ed-f select:focus, .ed-f input:focus { border-color: var(--lime); }
.ed-f input::placeholder { color: var(--text-4); }
.ed-f select { cursor: pointer; }
.ed-f select option { background: var(--surface); color: var(--text); }
.ed-f-check { display: flex; align-items: center; gap: 6px; cursor: pointer; }
.ed-f-check input { accent-color: var(--lime); width: 14px; height: 14px; }
.ed-f-check span { color: var(--text); font-size: 12.5px; }
.ed-f-hint {
  display: block;
  font-size: 10.5px; color: var(--text-4); margin-top: 1px; font-style: italic;
}

.ed-f-text-wrap {
  flex: 1 1 auto; min-width: 0;
  display: flex; flex-direction: column;
}
.ed-f-text-wrap input { width: 100%; }

.ed-f-input-wrap {
  flex: 1 1 auto; min-width: 0;
  display: flex; flex-direction: column;
}
.ed-f-input-wrap input { width: 100%; }

/* ── Action Rows ── */
.ed-actions { display: flex; flex-direction: column; gap: 4px; }
.ed-action {
  display: flex; align-items: center; gap: 8px;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-xs); padding: 5px 8px;
  font-size: 12px;
}
.ed-action-num {
  font-family: var(--font-mono); font-size: 10px;
  color: var(--lime); flex: 0 0 16px;
}
.ed-action-desc { flex: 1; color: var(--text); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ed-action-tools { display: flex; align-items: center; gap: 2px; flex: 0 0 auto; }
.ed-action-item {
  color: var(--text-4); font-size: 12px; line-height: 1;
  padding: 2px 3px; border-radius: 3px;
  flex: 0 0 auto; cursor: pointer;
  transition: color 0.12s, background 0.12s;
}
.ed-action-item:hover { color: var(--lime); background: var(--surface); }
.ed-action-item.rm:hover { color: var(--danger); background: var(--danger-dim); }
.ed-action-item.disabled { opacity: 0.25; pointer-events: none; }

/* ── Composer ── */
.ed-composer {
  background: var(--lime-dim);
  border: 1px solid var(--lime-border);
  border-radius: var(--r-sm);
  padding: 10px 12px; margin-top: 8px;
}
.ed-composer-head {
  font-family: var(--font-display);
  font-size: 11px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--lime); margin-bottom: 8px;
}
.ed-composer-actions {
  display: flex; gap: 8px; margin-top: 8px; justify-content: flex-end;
}

/* ── OLED Canvas ── */
.ed-oled-block { flex: 1 1 100%; width: 100%; display: flex; flex-direction: column; }
.ed-oled-block .ed-f-hint { margin-top: 2px; }
.ed-oled-wrap { flex: 1 1 100%; width: 100%; margin-top: 4px; }
.ed-oled {
  width: 100%; height: auto;
  image-rendering: pixelated;
  background: var(--oled-canvas);
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  cursor: crosshair; touch-action: none;
}
.ed-oled-btns { display: flex; gap: 6px; margin-top: 4px; }

/* ── OLED Animation paste ── */
.ed-anim-block { flex: 1 1 100%; width: 100%; display: flex; flex-direction: column; gap: 6px; }
.ed-anim-info {
  font-family: var(--font-mono); font-size: 10px; color: var(--teal);
}
.ed-anim-strip { display: flex; flex-wrap: wrap; gap: 4px; }
.ed-anim-frame {
  width: 64px; height: 32px;
  image-rendering: pixelated;
  background: var(--oled-canvas);
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
}

/* ── Levels (DPI) ── */
.ed-levels { display: flex; flex-direction: column; gap: 4px; margin: 4px 0; }
.ed-level-row { display: flex; align-items: center; gap: 6px; }
.ed-level-row input[type="number"] {
  width: 85px; padding: 4px 6px;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-xs); font-family: var(--font-mono);
  font-size: 12px; color: var(--text);
}
.ed-level-row .ed-unit { color: var(--text-4); font-size: 11px; }
.ed-rm {
  background: none; border: none;
  color: var(--text-4); font-size: 14px; padding: 1px 5px;
  border-radius: 4px; cursor: pointer;
}
.ed-rm:hover { color: var(--danger); background: var(--danger-dim); }

/* ── Raw JSON ── */
.ed-raw {
  margin: 0; padding: 10px 12px;
  font-family: var(--font-mono); font-size: 11px; line-height: 1.5;
  color: var(--text-3); white-space: pre-wrap; word-break: break-word;
  user-select: text; cursor: text;
  background: var(--surface); border-radius: var(--r-sm);
}

/* ── Toasts ── */
.ed-overlay {
  position: fixed; inset: 0; z-index: 100;
  pointer-events: none;
  display: flex; flex-direction: column;
  align-items: center; gap: 8px; padding: 16px;
}
.ed-toast {
  pointer-events: auto;
  background: var(--surface-2); border: 1px solid var(--line-strong);
  border-left: 3px solid var(--lime); color: var(--text);
  border-radius: var(--r-xs); padding: 8px 14px;
  font-size: 12.5px; max-width: 400px;
  box-shadow: 0 10px 25px rgba(0,0,0,0.4);
  animation: toastIn 0.18s var(--ease);
  transition: opacity 0.18s, transform 0.18s;
}
.ed-toast.error { border-left-color: var(--danger); }
.ed-toast.ok { border-left-color: var(--teal); }
.ed-toast.out { opacity: 0; transform: translateY(-4px); }
@keyframes toastIn {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: none; }
}

/* ── Empty / Note / Muted ── */
.ed-empty {
  color: var(--text-4); font-size: 12.5px;
  padding: 20px 8px; text-align: center;
}
.ed-empty b { color: var(--text-3); }
.ed-note {
  font-size: 11.5px; color: var(--text-3);
  background: var(--surface); border: 1px solid var(--line);
  border-left: 3px solid var(--lime);
  border-radius: var(--r-xs); padding: 8px 10px;
  margin-bottom: 10px;
}
.ed-note b { color: var(--text); }
.ed-muted { color: var(--text-4); font-size: 11.5px; }

/* ── Publish Modal ── */
.ed-modal-backdrop {
  position: fixed; inset: 0; z-index: 90;
  background: rgba(4,4,6,0.72); backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center;
  padding: 20px; animation: fadeUp 0.18s var(--ease);
}
.ed-modal {
  background: var(--bg-raise); border: 1px solid var(--line-strong);
  border-radius: var(--r-lg); padding: 20px;
  width: 100%; max-width: 400px;
  box-shadow: 0 25px 50px rgba(0,0,0,0.5);
}
.ed-modal h3 {
  font-family: var(--font-display); font-size: 16px;
  margin: 0 0 14px;
}
/* The sign-in step says what happens to the work, which is the one thing
   someone leaving for an OAuth round trip needs to be told. */
.ed-modal-note {
  margin: -2px 0 16px;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--text-2);
}
.ed-modal label {
  display: block; font-size: 12px; color: var(--text-3);
  margin: 0 0 10px;
}
.ed-modal input, .ed-modal textarea {
  display: block; width: 100%; margin-top: 4px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-xs); color: var(--text);
  font-size: 12.5px; padding: 7px 10px;
}
.ed-modal textarea { resize: vertical; }
.ed-modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px; }

/* ── Shortcut help overlay ── */
/* ------------------------------------------------------------
   First run

   The Builder opens on a blank profile, which is the right default and
   a poor first impression: an empty button map says nothing about what
   the four ways in are. This is that panel.

   A dialog over the workspace rather than a page: the editor is already
   there behind it, and replacing it would be a bigger change than the
   problem needs. It is shown once, and "once" lives in localStorage —
   never a cookie, and never for a deep link.
   ------------------------------------------------------------ */
.ed-start-backdrop {
  position: fixed; inset: 0; z-index: 92;
  background: rgba(4,4,6,0.66); backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center;
  padding: 20px; animation: fadeUp 0.18s var(--ease);
}
.ed-start {
  background: var(--bg-raise); border: 1px solid var(--line-strong);
  border-radius: var(--r-lg); padding: 22px;
  width: 100%; max-width: 640px;
  box-shadow: 0 25px 50px rgba(0,0,0,0.5);
}
.ed-start-ttl {
  font-family: var(--font-display); font-size: 17px; font-weight: 600;
  margin: 0 0 6px;
}
.ed-start-sub {
  margin: 0 0 18px; font-size: 12.5px; color: var(--text-3);
  max-width: 52ch;
}
.ed-start-grid {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.ed-start-card {
  display: flex; flex-direction: column; gap: 4px; align-items: flex-start;
  padding: 13px 15px; text-align: left;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-sm); font-family: inherit; color: var(--text);
  text-decoration: none; cursor: pointer;
  transition: border-color var(--t-fast), background var(--t-fast), transform var(--t-fast);
}
.ed-start-card:hover {
  border-color: var(--accent); background: var(--surface-2);
  transform: translateY(-1px); text-decoration: none;
}
.ed-start-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ed-start-card-ttl { font-size: 13.5px; font-weight: 600; }
.ed-start-card-sub { font-size: 12px; color: var(--text-3); }
.ed-start-card--primary {
  background: var(--accent-dim); border-color: color-mix(in srgb, var(--accent) 35%, transparent);
}
.ed-start-skip {
  margin-top: 16px; padding: 6px 0;
  background: none; border: 0; cursor: pointer;
  font-family: inherit; font-size: 12px; color: var(--text-3);
}
.ed-start-skip:hover { color: var(--text); }

.ed-shortcut-backdrop {
  position: fixed; inset: 0; z-index: 95;
  background: rgba(4,4,6,0.72); backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center;
  padding: 20px; animation: fadeUp 0.18s var(--ease);
}
.ed-shortcut {
  background: var(--bg-raise); border: 1px solid var(--line-strong);
  border-radius: var(--r-lg); padding: 18px 20px;
  width: 100%; max-width: 460px;
  box-shadow: 0 25px 50px rgba(0,0,0,0.5);
}
.ed-shortcut-head {
  font-family: var(--font-display); font-size: 15px; font-weight: 600;
  margin-bottom: 14px;
}
.ed-shortcut-body { display: flex; flex-direction: column; gap: 7px; }
.ed-shortcut-row {
  display: flex; align-items: baseline; gap: 12px;
  font-size: 12.5px; color: var(--text-2);
}
.ed-shortcut-row span { color: var(--text); }
.ed-shortcut-row kbd {
  font-family: var(--font-mono); font-size: 11px;
  color: var(--lime);
  background: var(--surface-2); border: 1px solid var(--line-med);
  border-bottom-width: 2px; border-radius: 4px;
  padding: 1px 6px; white-space: nowrap;
}
.ed-shortcut-hint {
  margin-top: 8px; padding-top: 10px;
  border-top: 1px solid var(--line);
  font-size: 11px; color: var(--text-4); font-style: italic;
}
.ed-shortcut-hint code {
  font-family: var(--font-mono); font-size: 10.5px;
  color: var(--text-3); background: var(--surface); padding: 0 4px;
  border-radius: 3px;
}

/* ── Press stages card (deep-click stack) ── */
.ed-presscard {
  background: linear-gradient(180deg, rgba(255,159,69,0.05), rgba(79,216,196,0.04) 45%, transparent);
  border: 1px solid var(--line);
  border-left: 3px solid var(--orange);
  border-radius: var(--r-sm);
  padding: 10px 12px;
  margin-bottom: 10px;
}
.ed-presscard-head {
  display: flex; align-items: center; justify-content: space-between;
  font-family: var(--font-display);
  font-size: 11px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-2);
  margin-bottom: 8px;
}
.ed-presscard-title { color: var(--orange); }
.ed-presscard-count { color: var(--text-4); letter-spacing: 0; text-transform: none; font-size: 10.5px; }

.ed-press-track {
  position: relative; height: 12px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  overflow: hidden;
}
.ed-press-band { position: absolute; top: 0; height: 100%; }
.ed-press-band.light { background: var(--teal); opacity: 0.55; }
.ed-press-band.deep { background: var(--orange); box-shadow: 0 0 8px rgba(255,159,69,0.5); }
.ed-press-scale {
  display: flex; justify-content: space-between;
  font-family: var(--font-mono); font-size: 9px; color: var(--text-4);
  margin: 2px 0 8px;
}
.ed-press-empty { font-style: italic; }

.ed-press-list { display: flex; flex-direction: column; gap: 4px; }
.ed-rung {
  display: flex; align-items: center; gap: 8px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-left: 2px solid transparent;
  border-radius: var(--r-xs);
  padding: 5px 8px;
  font-size: 11.5px;
  cursor: pointer;
  transition: border-color 0.12s, background 0.12s;
}
.ed-rung:hover { border-color: var(--line-med); background: var(--surface-3); }
.ed-rung.active { border-color: var(--lime); background: var(--lime-dim); }
.ed-rung-tag {
  font-family: var(--font-display); font-size: 10px; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase;
  flex: 0 0 auto;
}
.ed-rung-tag.light { color: var(--teal); }
.ed-rung-tag.deep { color: var(--orange); }
.ed-rung-range {
  font-size: 10px; color: var(--text-3);
  border: 1px dashed rgba(255,159,69,0.4); border-radius: var(--r-pill);
  padding: 0 6px; flex: 0 0 auto;
}
.ed-rung-out {
  flex: 1; min-width: 0;
  color: var(--text-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ed-press-actions { display: flex; gap: 6px; margin-top: 8px; justify-content: flex-end; }

/* ── Range plot (live deep-zone position) ── */
.ed-range-plot {
  position: relative;
  width: 100%; height: 22px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  margin-top: 2px;
  touch-action: none;
}
.ed-range-exit {
  position: absolute; top: 2px; bottom: 2px;
  background: repeating-linear-gradient(45deg, var(--violet), var(--violet) 3px, transparent 3px, transparent 6px);
  opacity: 0.5;
  border-radius: 2px;
  left: 0; width: 0;
}
.ed-range-fill {
  position: absolute; top: 2px; bottom: 2px;
  background: var(--orange);
  border-radius: 2px;
  box-shadow: 0 0 8px rgba(255,159,69,0.4);
  transition: left 0.1s, width 0.1s;
}
.ed-range-handle {
  position: absolute; top: 1px; bottom: 1px;
  width: 7px;
  background: var(--text);
  border-radius: 3px;
  cursor: ew-resize;
  box-shadow: 0 0 0 1px rgba(11,11,13,0.8), 0 0 6px rgba(198,245,66,0.6);
  transform: translateX(-50%);
  transition: background 0.12s;
}
.ed-range-handle:hover { background: var(--lime); }
.ed-range-ticks {
  position: absolute; inset: 0;
  pointer-events: none;
  font-family: var(--font-mono); font-size: 8px; color: var(--text-4);
}
.ed-range-ticks span { position: absolute; top: 5px; transform: translateX(-50%); }

/* ── Deep mapping list accent ── */
.ed-mapitem.deep { border-left: 3px solid var(--orange); }
.ed-mapitem-badge.deep {
  color: var(--orange);
  border-color: rgba(255,159,69,0.4);
}

/* ── Force-capable button marker ── */
.ed-mbtn-force {
  margin-left: auto;
  font-size: 9px; line-height: 12px;
  color: var(--orange);
  border: 1px solid rgba(255,159,69,0.4);
  border-radius: var(--r-pill);
  padding: 0 5px;
  flex: 0 0 auto;
}

.ed-mbtn.playing {
  border-color: var(--lime);
  background: var(--lime-dim);
  box-shadow: 0 0 0 1px var(--lime), 0 0 14px rgba(198,245,66,0.35);
}

/* ── At-a-glance overview (Mouse tab) ── */
.ed-overview-stats {
  grid-column: 1 / -1;
  display: flex; flex-wrap: wrap; gap: 6px;
  margin-bottom: 4px;
}
.ed-stat {
  font-family: var(--font-mono); font-size: 11px;
  color: var(--text-3);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 2px 10px;
}
.ed-stat b {
  color: var(--lime); font-weight: 600;
}
.ed-mbtn-binds {
  display: flex; flex-direction: column; gap: 3px;
  min-width: 0;
}
.ed-mbind {
  display: flex; align-items: center; gap: 5px;
  font-size: 10.5px; color: var(--text-2);
  background: var(--surface);
  border-radius: 4px;
  padding: 1px 6px;
  min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ed-mbind.light { border-left: 2px solid var(--teal); }
.ed-mbind.deep { border-left: 2px solid var(--orange); }
.ed-mbind-tag {
  font-family: var(--font-mono); font-size: 8.5px; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase;
  flex: 0 0 auto;
}
.ed-mbind.light .ed-mbind-tag { color: var(--teal); }
.ed-mbind.deep .ed-mbind-tag { color: var(--orange); }
.ed-mbind-name {
  font-style: italic; font-weight: 600; color: var(--text);
  min-width: 0; overflow: hidden; text-overflow: ellipsis;
}
.ed-mbind-sum {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--text-3);
}
.ed-mbind-range {
  font-family: var(--font-mono); font-size: 9px;
  color: var(--text-3); flex: 0 0 auto;
}

/* ── Grouped mapping list (Bindings tab) ── */
.ed-maplist-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px;
  font-family: var(--font-display);
  font-size: 11px; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-2);
  margin-bottom: 8px;
}
.ed-maplist-tools { display: flex; align-items: center; gap: 6px; }
.ed-select-tiny {
  max-width: 180px;
  padding: 3px 6px;
  font-size: 12.5px;
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--line-med);
  border-radius: 6px;
}
.ed-mapfilter {
  display: flex; gap: 8px;
  margin: 0 0 10px;
}
.ed-mapfilter input[type="text"] {
  flex: 1; min-width: 0;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-xs); color: var(--text);
  font-size: 12px; padding: 5px 9px;
}
.ed-mapfilter input[type="text"]::placeholder { color: var(--text-4); }
.ed-mapfilter input[type="text"]:focus { border-color: var(--lime); outline: none; }
.ed-mapfilter select {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-xs); color: var(--text-2);
  font-size: 12px; padding: 5px 8px; cursor: pointer;
}
.ed-mapfilter select option { background: var(--surface); color: var(--text); }
.ed-mgroups { display: flex; flex-direction: column; gap: 12px; }
.ed-mgroup-head {
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 5px;
}
.ed-mgroup-name {
  font-family: var(--font-display); font-size: 12px; font-weight: 600;
  color: var(--text);
}
.ed-mgroup-count {
  font-family: var(--font-mono); font-size: 10px;
  color: var(--text-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 0 7px;
}

/* ── Responsive ── */
@media (max-width: 1200px) {
  /* The rail gives ground first: the centre is where the work happens,
     and the rail is only a tree. */
  .ed-workspace { --ed-rail-w: 232px; }
}
@media (max-width: 760px) {
  .ed-workspace { grid-template-columns: 1fr; grid-template-rows: auto; position: relative; }
  .ed-panel-left { height: 26vh; }
  .ed-panel-center { height: 50vh; }
  /* The header chrome is owned by css/hdr.css; only the editor's own
     controls need to shrink here. */
  .ed-ctab { padding: 4px 8px; }
  .ed-f { flex-direction: column; align-items: stretch; gap: 4px; }
  .ed-f > .ed-f-label { flex: none; }
}

/* ═══════════════ Recorder (admin-only Scribe-style step capture) ═══════ */
/* The record toggle is a strip button, so it only contributes the two
   states the shared toolbar does not already have. */
#btn-record {
  color: var(--lime);
  border-color: var(--lime-glow);
}
#btn-record:hover {
  color: var(--lime);
  background: var(--lime-dim);
  border-color: var(--lime);
}
#btn-record.recording {
  color: var(--on-solid);
  background: var(--red);
  border-color: var(--red);
}
#btn-record.recording:hover {
  background: var(--red);
}

.ed-rec { position: fixed; left: 14px; bottom: 14px; z-index: 80; width: 320px; max-width: calc(100vw - 28px); }
.ed-rec.ed-rec--min { width: auto; }
.ed-rec--min .ed-rec-list { display: none; }
.ed-rec--min .ed-rec-hd { border-bottom: none; padding: 7px 10px; }
.ed-rec--min .ed-rec-count { display: none; }
.ed-rec--min .ed-rec-empty, .ed-rec--min .ed-rec-export-note { display: none; }
.ed-rec-panel {
  background: var(--bg-raise); border: 1px solid var(--lime-border);
  border-radius: var(--r-lg); box-shadow: 0 18px 44px rgba(0,0,0,0.55);
  overflow: hidden; display: flex; flex-direction: column;
}
.ed-rec-toggle {
  /* A single glyph in a square box has to be centred by the box, not by
     the text baseline: without this the arrow sits high and left. */
  flex: none; width: 24px; height: 24px; border-radius: var(--r-xs);
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface); border: 1px solid var(--line-strong);
  color: var(--text-2); font-size: 12px; line-height: 1; cursor: pointer;
  padding: 0;
}
.ed-rec-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.ed-rec-toggle:hover { border-color: var(--lime); color: var(--lime); }
.ed-rec-hd {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 12px; font-size: 12.5px; font-weight: 600;
  font-family: var(--font-display); color: var(--lime);
  border-bottom: 1px solid var(--line-med);
}
.ed-rec-hd > *:last-child { margin-left: auto; }
.ed-rec-dot {
  width: 9px; height: 9px; border-radius: 50%; background: var(--danger);
  box-shadow: 0 0 0 3px var(--danger-dim); animation: recPulse 1.4s infinite;
}
@keyframes recPulse { 0%,100% { opacity: 1; } 50% { opacity: 0.35; } }
.ed-rec-count { font-size: 11.5px; color: var(--text-3); font-weight: 500; }
.ed-rec-list {
  max-height: 52vh; overflow-y: auto; display: flex; flex-direction: column; gap: 10px;
  padding: 12px;
}
.ed-rec-empty { font-size: 12px; color: var(--text-3); line-height: 1.5; padding: 4px 2px; }
.ed-rec-step { position: relative; display: flex; gap: 10px; font-size: 12.5px; }
.ed-rec-num {
  flex: none; width: 22px; height: 22px; border-radius: var(--r-pill);
  background: var(--lime-dim); border: 1px solid var(--lime-border);
  color: var(--lime); font-weight: 700; font-size: 11px;
  display: flex; align-items: center; justify-content: center;
}
.ed-rec-body { min-width: 0; flex: 1; }
.ed-rec-title { font-weight: 600; color: var(--text); line-height: 1.4; padding-right: 22px; }
.ed-rec-chips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
.ed-rec-chip {
  font-family: var(--font-mono); font-size: 10.5px; color: var(--lime);
  background: var(--surface); border: 1px solid var(--line-med);
  border-radius: var(--r-pill); padding: 2px 8px;
}
.ed-rec-where {
  display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap;
  margin-top: 3px; font-size: 11px; line-height: 1.5;
}
.ed-rec-where-k { color: var(--orange); }
.ed-rec-where-v {
  color: var(--text-2); font-family: var(--font-mono); font-size: 10.5px;
  padding: 0 5px; border: 1px solid var(--line); border-radius: var(--r-xs);
  max-width: 24ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ed-rec-detail {
  margin-top: 2px; font-size: 10.5px; line-height: 1.5; color: var(--text-4);
  overflow-wrap: anywhere;
}
/* Frames that could not be captured. Loud on purpose: a recording whose
   screenshots are silently missing is worse than one that says so. */
.ed-rec-warn {
  margin: 6px 8px;
  padding: 7px 9px;
  border: 1px solid var(--orange-dim);
  border-radius: var(--r-sm);
  background: var(--orange-dim);
  color: var(--orange);
  font-size: 11px;
  line-height: 1.45;
}

.ed-rec-del {
  position: absolute; top: 4px; right: 4px; width: 22px; height: 22px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0;
  background: none; border: 1px solid var(--line); border-radius: var(--r-xs);
  color: var(--text-3); font-size: 13px; line-height: 1; cursor: pointer;
}
.ed-rec-del:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.ed-rec-del:hover { color: var(--danger); border-color: var(--danger); }
.ed-rec-export-note { font-size: 11.5px; color: var(--text-3); margin: 0 0 6px; }
.ed-rec-actions { flex-wrap: wrap; }

/* ── Replay highlight ──
   A step that only restores the document tells you what changed but not
   where. The outline goes on the control the step was about: the button
   picked on the mouse, the button that added a binding, the side of the
   binding editor the step filled in.

   Two parts, because they do different jobs: the box is a fixed ring that
   says *here*, and the class on the element itself gives the control a
   lift, so the target is findable once the ring has scrolled or the panel
   has reflowed. */
.ed-replay-box {
  position: fixed;
  z-index: 90;
  pointer-events: none;
  /* Warm, not the accent. The accent means "this is selected"; this means
     "this is the control the step is about" — a different thing, and using
     the same colour made the ring read as a selection rather than a cue. */
  border: 2px solid var(--orange);
  border-radius: 10px;
  box-shadow: 0 0 0 3px var(--orange-dim), 0 0 24px var(--orange-dim);
  animation: edReplayIn 0.22s var(--ease);
}
@keyframes edReplayIn {
  from { opacity: 0; transform: scale(1.04); }
  to { opacity: 1; transform: none; }
}
.ed-replay-target {
  position: relative;
  z-index: 1;
  animation: edReplayLift 1.1s var(--ease);
}
@keyframes edReplayLift {
  0%, 100% { box-shadow: none; }
  35% { box-shadow: 0 0 0 2px var(--orange-dim), 0 0 18px var(--orange-dim); }
}
/* The ring is fixed to the viewport, so it has to follow the page. */
@media (prefers-reduced-motion: reduce) {
  .ed-replay-box, .ed-replay-target { animation: none; }
}

/* Replay bar */
.ed-replay {
  position: fixed; right: 14px; bottom: 14px; z-index: 80;
  width: 340px; max-width: calc(100vw - 28px);
  background: var(--bg-raise); border: 1px solid var(--teal);
  border-radius: var(--r-lg); box-shadow: 0 18px 44px rgba(0,0,0,0.55);
  padding: 12px; font-size: 12.5px;
}
.ed-replay-hd { display: flex; align-items: center; gap: 8px; font-weight: 600; font-family: var(--font-display); color: var(--teal); }
.ed-replay-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--teal); }
.ed-replay-pos { margin-top: 8px; color: var(--text-2); line-height: 1.45; }
.ed-replay-actions { display: flex; gap: 6px; margin-top: 10px; }
.ed-replay-actions button {
  background: var(--surface); border: 1px solid var(--line-strong); color: var(--text);
  border-radius: var(--r-xs); padding: 5px 10px; font-size: 11.5px; cursor: pointer;
}
.ed-replay-actions button:disabled { opacity: 0.4; cursor: default; }
.ed-replay-actions button:hover:not(:disabled) { border-color: var(--teal); color: var(--teal); }
.ed-replay-actions button:last-child { margin-left: auto; color: var(--text-3); }
.ed-replay-steps {
  margin-top: 10px; max-height: 26vh; overflow-y: auto;
  display: flex; flex-direction: column; gap: 2px;
  border-top: 1px solid var(--line); padding-top: 8px;
}
.ed-replay-step {
  display: flex; gap: 8px; align-items: baseline; padding: 4px 6px; border-radius: var(--r-xs);
  color: var(--text-2); cursor: pointer;
}
.ed-replay-step:hover { background: var(--surface); }
.ed-replay-step.is-cur { background: var(--lime-dim); color: var(--text); }
.ed-replay-n {
  flex: none; width: 18px; height: 18px; border-radius: var(--r-pill);
  border: 1px solid var(--line-strong); font-size: 10px;
  display: flex; align-items: center; justify-content: center; color: var(--text-3);
}
.ed-replay-step.is-cur .ed-replay-n { border-color: var(--lime); color: var(--lime); }

/* ═══════════════ Input nesting ═══════════════
   The Bindings tab: one card per control, each card holding the trees
   rooted on it. This replaces .ed-chain-* / .ed-seqhead, which drew a
   binding's followers as a row of step boxes and so made a fork look
   like two bindings in a row with no relationship between them.

   The tree is read from two things the JS already writes on every row:
   --ed-nest-d (the depth, in edges) and data-depth. Depth is never
   restated here — the step below is a property of the viewport, not of
   the document, and every rail and elbow is derived from the row's own
   depth number. 20 levels is the format's cap, so the step is a clamp()
   rather than a fixed 18px: at the cap that is ~320px on a wide window
   and ~200px on a narrow one, and the trigger label keeps a readable
   share of the row at either. */
/* The Mouse tab's own hint line. Same box as the nesting hint it points at —
   it explains the same grammar, one tab earlier, and a second set of six
   properties for it would be two things to keep in step. */
.ed-nest-hint, .ed-mouse-hint {
  font-size: var(--fs-xs);
  color: var(--text-2);
  line-height: 1.5;
  margin-bottom: var(--sp-2);
  padding: 7px 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--lime);
  border-radius: var(--r-xs);
}
.ed-nest-hint strong, .ed-mouse-hint strong { color: var(--text); font-weight: 600; }

.ed-nest-card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 10px 12px 8px;
}
.ed-nest-card:hover { border-color: var(--line-med); }
/* An open Add menu is out of flow and deliberately hangs over the card's
   own edge — clipping it would hide half the trigger types. The card it
   belongs to is lifted instead, so it also paints over the cards below. */
.ed-nest-card:has(.ed-nest-menu) { z-index: 4; }

/* Where a click on the Mouse grid landed: the card holding that control's
   bindings. The diagnostics jump's ring, sized to the card, and it does not
   outlast it — a target that stays marked is a second selection, and two of
   them mean something. The border colour is the marker that survives: under
   reduced motion the animation is dropped and the lime edge is the whole
   signal. */
.ed-nest-card-hit {
  border-color: var(--lime);
  animation: edNestCardHit 1.7s ease;
}
@keyframes edNestCardHit {
  0%, 100% { box-shadow: none; background: var(--surface); }
  25%, 55% { box-shadow: 0 0 0 2px var(--lime), 0 0 22px var(--lime-dim); background: var(--surface-2); }
}
@media (prefers-reduced-motion: reduce) {
  .ed-nest-card-hit { animation: none; }
}

.ed-nest {
  /* Containing block for the Add menu, which is a sibling of the row it
     belongs to. Deliberately not a flex container: an absolutely
     positioned child of a flex container takes its static position as if
     it were the container's only flex item, which would drop the menu at
     the top of the tree instead of under its own row. Rows are blocks
     and stack on their own. */
  position: relative;
}

/* ── A row ──
   Rows are flush, not stacked cards: the guide lines have to run from one
   row's bottom edge into the next row's top edge with no gap in between,
   because that continuity is what tells a reader the branch is still
   open. The card around them supplies the outer surface and the radius. */
.ed-nest-row {
  --ed-nest-pad: 6px;
  --ed-nest-step: clamp(10px, 1.4vw, 16px);
  /* Where the elbow sits below the top of a row: half of the trigger
     line. The elbow and the fork tee share it so they meet exactly. */
  --ed-nest-elbow: 9px;
  position: relative;
  display: flex;
  align-items: flex-start;
  /* Never wraps. A row that may break puts a long gate sentence on a line of
     its own, where it starts at the left margin — which is what made the
     pills look scattered. One line per row instead: the gate shrinks and
     wraps its own text, and the trigger name wraps inside .ed-nest-body. */
  flex-wrap: nowrap;
  column-gap: 8px;
  row-gap: 4px;
  padding: 6px 8px 6px 0;
  /* editor.js still emits `padding-left:6px + depth*18px` inline so the
     tree reads before this stylesheet exists. That inline value has no
     clamp and a hard step, so it is superseded here — and `!important`
     is the only thing in this file that can beat an inline declaration. */
  padding-left: calc(var(--ed-nest-pad) + var(--ed-nest-d, 0) * var(--ed-nest-step)) !important;
  cursor: pointer;
  transition: background 0.12s, box-shadow 0.12s;
}
/* Every row but the first is separated from the one above it. `:first-of-type`
   and `.top` cannot do this: a card's tree can hold more than one root, so
   "first" is a sibling question and not a depth one. */
.ed-nest-row ~ .ed-nest-row { border-top: 1px solid var(--line); }
.ed-nest-row:hover { background: var(--surface-2); }
.ed-nest-row.active { background: var(--lime-dim); }
.ed-nest-row:focus-within { background: var(--surface-2); }
/* The left accent bar sits outboard of the guide lines so the two never
   collide, the way .ed-mapitem draws its violet and orange. Selection is
   last so it wins over the trigger kind it happens to be. */
.ed-nest-row.deep { box-shadow: inset 3px 0 0 var(--orange); }
.ed-nest-row.sys { box-shadow: inset 3px 0 0 var(--violet); }
.ed-nest-row.active { box-shadow: inset 3px 0 0 var(--lime); }

/* One vertical line per ancestor level, at the left edge of that level's
   indent cell. A row at depth d therefore carries exactly d rails, so the
   rail count is the depth and the rail's x is the ancestor it belongs to:
   two rows whose rails line up are siblings, and a rail that continues
   into the rows below is a branch that is still open. Drawn as one
   repeating gradient rather than d separate lines, because d is a number
   in a custom property and not something to enumerate. */
.ed-nest-row::before {
  content: '';
  position: absolute;
  top: 0; bottom: 0;
  left: var(--ed-nest-pad);
  width: calc(var(--ed-nest-d, 0) * var(--ed-nest-step));
  background: repeating-linear-gradient(
    to right,
    var(--line-med) 0 1px,
    transparent 1px var(--ed-nest-step));
  pointer-events: none;
}
/* The fork itself: a row whose next row is one level deeper has children,
   so its innermost rail carries on past its own text into them. One rule
   per depth, which is the whole of MAX_NEST_DEPTH — CSS cannot do the
   +1, and guessing would put the tee on the wrong row. */
.ed-nest-row::after {
  content: '';
  position: absolute;
  top: var(--ed-nest-elbow);
  bottom: 0;
  left: max(var(--ed-nest-pad),
    calc(var(--ed-nest-pad) + (var(--ed-nest-d, 0) - 1) * var(--ed-nest-step)));
  width: 1px;
  background: var(--line-med);
  pointer-events: none;
}
.ed-nest-row[data-depth='0']:has(+ .ed-nest-row[data-depth='1'])::after,
.ed-nest-row[data-depth='1']:has(+ .ed-nest-row[data-depth='2'])::after,
.ed-nest-row[data-depth='2']:has(+ .ed-nest-row[data-depth='3'])::after,
.ed-nest-row[data-depth='3']:has(+ .ed-nest-row[data-depth='4'])::after,
.ed-nest-row[data-depth='4']:has(+ .ed-nest-row[data-depth='5'])::after,
.ed-nest-row[data-depth='5']:has(+ .ed-nest-row[data-depth='6'])::after,
.ed-nest-row[data-depth='6']:has(+ .ed-nest-row[data-depth='7'])::after,
.ed-nest-row[data-depth='7']:has(+ .ed-nest-row[data-depth='8'])::after,
.ed-nest-row[data-depth='8']:has(+ .ed-nest-row[data-depth='9'])::after,
.ed-nest-row[data-depth='9']:has(+ .ed-nest-row[data-depth='10'])::after,
.ed-nest-row[data-depth='10']:has(+ .ed-nest-row[data-depth='11'])::after,
.ed-nest-row[data-depth='11']:has(+ .ed-nest-row[data-depth='12'])::after,
.ed-nest-row[data-depth='12']:has(+ .ed-nest-row[data-depth='13'])::after,
.ed-nest-row[data-depth='13']:has(+ .ed-nest-row[data-depth='14'])::after,
.ed-nest-row[data-depth='14']:has(+ .ed-nest-row[data-depth='15'])::after,
.ed-nest-row[data-depth='15']:has(+ .ed-nest-row[data-depth='16'])::after,
.ed-nest-row[data-depth='16']:has(+ .ed-nest-row[data-depth='17'])::after,
.ed-nest-row[data-depth='17']:has(+ .ed-nest-row[data-depth='18'])::after,
.ed-nest-row[data-depth='18']:has(+ .ed-nest-row[data-depth='19'])::after,
.ed-nest-row[data-depth='19']:has(+ .ed-nest-row[data-depth='20'])::after {
  content: '';
}

/* ── Where one tree ends and the next begins ──
   A card is a group key, not a tree. mappingGroupKey files the control-less
   triggers — tilt, wheel, surface, sys — under one trigger-type card, so a
   single card can hold several independent trees: profile 20 holds #1 → #2
   and #3 → #4 under `tilt`, and 48 and 50 hold a tree plus singletons.
   editor.js marks where the second and each later tree starts, and both parts
   of that marker are answered here.

   The rows above the boundary belong to a tree that has ended, so their rails
   simply stop, because the branch really did close. Nothing is suppressed to
   make room for that: a row which opens a new tree is a root, so its own rails
   are already `calc(0 * step)` wide and there is nothing drawn to reset. The
   gradient underneath is left alone deliberately — its 1px-on / step-off period
   is anchored to each row's own left edge, and that shared phase is the reason
   one row's rails land exactly on the next row's, so re-anchoring it to
   anything would shift every rail in the card at once. */
.ed-nest-row.new-tree {
  /* The space is padding, not margin, and that is the whole constraint. A
     margin would open a gap in the block flow: the rows above end their rails
     at their own bottom edge and the new root's would start lower down, with
     nothing between, so a rail would appear to stop in mid-air. Rows stay
     flush, one bottom edge still meeting the next top edge, and the air is
     taken inside the row instead — above the rule it draws and below it. */
  padding-top: calc(6px + var(--sp-3));
  /* Stronger than the `1px solid var(--line)` that every row after the first
     already carries. An ordinary separator says "the next row", which is what
     it is for; this one has to say "a different tree", and at 1px of --line
     it would be indistinguishable from the rows it is separating. */
  border-top: 1px solid var(--line-strong);
}
/* The tee, corrected for the boundary. A tee is a claim that the next row is a
   child: it runs from the elbow height down to this row's bottom edge, and
   the row below carries on the same rail only if it is one level deeper. That
   held while a card was one tree. It is a false claim on the row a new tree
   starts under — the row below it is depth 0 and descends from nothing above
   — so it is dropped there rather than left pointing across the separator.
   `:not(.new-tree)` keeps the rule off the new root itself, whose tee is the
   opposite case and is correct whenever its own tree has followers. */
.ed-nest-row:not(.new-tree):has(+ .ed-nest-row.new-tree)::after { content: none; }
/* On the new root the tee is drawn only when the row below really is its
   child. Both halves are stated rather than left to the cascade: the first
   clears the base rule that puts a tee on every row, the second puts it back
   for the tree that has followers (profile 20's #3 → #4), at the same
   specificity as the per-depth rules above and after them, so it wins
   outright instead of by accident. */
.ed-nest-row.new-tree::after { content: none; }
.ed-nest-row.new-tree:has(+ .ed-nest-row[data-depth='1'])::after { content: ''; }

/* The elbow: a horizontal run from the row's own rail out to its text, in
   the indent cell between them. The glyph the JS puts in the span is the
   arrow head sitting on that run. */
.ed-nest-arm {
  position: relative;
  flex: 0 0 var(--ed-nest-step);
  width: var(--ed-nest-step);
  align-self: stretch;
  font-size: 11px;
  line-height: 18px;
  color: var(--text-4);
}
.ed-nest-row:not(.top) .ed-nest-arm::before {
  content: '';
  position: absolute;
  top: var(--ed-nest-elbow);
  left: calc(-1 * var(--ed-nest-step));
  width: var(--ed-nest-step);
  height: 1px;
  background: var(--line-med);
}
/* The root is the one row that is live on its own, so it is marked as
   such by the same lime the .active selection uses. */
.ed-nest-row.top .ed-nest-arm { color: var(--lime); font-size: 12px; }

.ed-nest-num {
  flex: 0 0 22px;
  font-family: var(--font-mono); font-size: 10px;
  color: var(--text-4);
  padding-top: 2px;
}
.ed-nest-body {
  flex: 1 1 14ch;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 8px;
}
/* min-width: 0 so a long trigger name can break at a space when the row runs
   out of room, instead of pushing the gate pill off the card. */
.ed-nest-trig { font-size: 13px; font-weight: 500; color: var(--text); min-width: 0; }
.ed-nest-label {
  font-size: 11px; font-style: italic; color: var(--text);
  min-width: 0;
  overflow-wrap: anywhere;
}
.ed-nest-acts {
  flex: 1 1 100%;
  min-width: 0;
  font-family: var(--font-mono); font-size: 11px;
  color: var(--text-3);
  overflow-wrap: anywhere;
}

/* ── The gate, which is the whole semantics ──
   A follower is live only while the row above it is held, so the pill
   names the row it waits for. The root says so instead, and is the only
   lime in the card: one tree, one thing that is live by itself. */
.ed-nest-gate {
  flex: 0 1 auto;
  min-width: 0;
  align-self: center;
  font-family: var(--font-mono); font-size: 9.5px;
  color: var(--text-3);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  padding: 1px 7px;
  /* Wraps rather than clipping. The sentence is the whole reason the pill
     exists — "active while #4 (Right Trigger Push) is held" is already longer
     than any fixed width, and a trigger name has no length limit. An
     ellipsis here would silently drop the trigger, which is the one word the
     reader needs. The full text is also on the row's title. */
  white-space: normal;
  overflow-wrap: anywhere;
}
/* The same sentence in the binding editor is a block of prose, not a badge,
   so it does not inherit the pill's shape. */
.ed-nest-gate-full {
  align-self: stretch;
  font-family: var(--font-ui);
  font-size: 11.5px;
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
}
.ed-nest-gate.is-root {
  color: var(--lime);
  background: var(--lime-dim);
  border-color: var(--lime-border);
}

/* ── The gate, restated in the binding editor ──
   The same sentence the row's pill carries, in long form, under the gate
   section of the editor for the selected binding. Only a broken gate has
   anything to add, and that is a warning. */
.ed-nest-gate-line {
  color: var(--text-2);
  line-height: 1.5;
  padding: 1px 0;
}
.ed-nest-gate-line strong { color: var(--text); font-weight: 600; }
.ed-nest-gate-issue {
  margin-top: 5px;
  padding: 5px 8px;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--text);
  background: var(--orange-dim);
  border-left: 3px solid var(--orange);
  border-radius: var(--r-xs);
}

/* ── Row tools ──
   Kept out of the way until the row is pointed at or something inside it
   takes focus. opacity rather than visibility, because a visibility:hidden
   button leaves no tab stop at all and the Add menu would be unreachable
   from the keyboard. */
/* The right-hand cluster: the gate pill and the row's tools, pinned to the
   right margin as one unit. This is what makes every pill in the card line up,
   rather than each landing wherever the trigger's own width left it. */
.ed-nest-right {
  margin-left: auto;
  flex: 0 0 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
}
.ed-nest-tools {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 4px;
  opacity: 0;
  transition: opacity 0.12s;
}
.ed-nest-row:hover .ed-nest-tools,
.ed-nest-row:focus-within .ed-nest-tools,
.ed-nest-row:has(.ed-nest-add[aria-expanded='true']) .ed-nest-tools {
  opacity: 1;
}
.ed-nest-add {
  padding: 3px 8px;
  font-size: var(--fs-2xs); font-weight: 600;
  color: var(--text-3);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-xs);
  cursor: pointer;
  transition: color 0.12s, background 0.12s, border-color 0.12s;
}
.ed-nest-add:hover:not(:disabled) { color: var(--lime); border-color: var(--lime-border); background: var(--surface-3); }
.ed-nest-add:disabled { opacity: 0.4; cursor: not-allowed; }
.ed-nest-add[aria-expanded='true'] {
  color: var(--on-accent);
  background: var(--accent);
  border-color: transparent;
}
.ed-nest-del {
  width: 22px; height: 22px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 15px; line-height: 1;
  color: var(--text-4);
  background: none; border: 0;
  border-radius: 4px;
  cursor: pointer;
  transition: color 0.12s, background 0.12s;
}
.ed-nest-del:hover { color: var(--danger); background: var(--danger-dim); }
.ed-nest-add:focus-visible, .ed-nest-del:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px;
}
/* Nothing hovers on a touch screen, so the tools are always out. */
@media (hover: none) {
  .ed-nest-tools { opacity: 1; }
}

/* ── Add menu ──
   Out of flow at its row's own static position, so it opens past the
   bottom edge of the card without the card having to clip it and without
   shoving the rest of the tree down a screen. `.ed-nest` is the containing
   block for left only; top stays auto, which is what puts the menu under
   the row it belongs to. */
.ed-nest-menu {
  position: absolute;
  top: auto;
  left: 0;
  z-index: 3;
  margin-top: 4px;
  min-width: 210px;
  max-width: min(320px, calc(100% - 8px));
  max-height: min(60vh, 360px);
  overflow-y: auto;
  background: var(--bg-raise);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-lg);
  padding: 5px;
}
.ed-nest-menu::-webkit-scrollbar { width: 8px; }
.ed-nest-menu::-webkit-scrollbar-thumb { background: var(--line-med); border-radius: 4px; }
.ed-nest-menu-head {
  font-family: var(--font-display);
  font-size: 10px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-3);
  padding: 4px 8px 6px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 4px;
}
.ed-nest-menu-kinds,
.ed-nest-menu-ctls { display: flex; flex-direction: column; gap: 1px; }
.ed-nest-menu-item,
.ed-nest-menu-ctl {
  display: block;
  width: 100%;
  text-align: left;
  padding: 6px 8px;
  font-size: 12px;
  color: var(--text-2);
  border-radius: var(--r-xs);
  transition: background 0.12s, color 0.12s;
}
.ed-nest-menu-item:hover,
.ed-nest-menu-ctl:hover { background: var(--surface-2); color: var(--text); }
.ed-nest-menu-item:focus-visible,
.ed-nest-menu-ctl:focus-visible {
  outline: 2px solid var(--accent); outline-offset: -2px;
  background: var(--surface-2); color: var(--text);
}
.ed-nest-menu-back {
  display: block;
  width: 100%;
  text-align: left;
  margin-top: 4px;
  padding: 5px 8px;
  font-size: 11px;
  color: var(--text-3);
  border-top: 1px solid var(--line);
  transition: color 0.12s;
}
.ed-nest-menu-back:hover { color: var(--text); }
.ed-nest-menu-back:focus-visible {
  outline: 2px solid var(--accent); outline-offset: -2px;
  color: var(--text);
}

/* ── "This row holds others" ──
   In flow, not floating: the question names bindings by index and has to
   be read against the tree it belongs to. */
.ed-nest-confirm {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 6px 0;
  padding: 8px 10px;
  background: var(--orange-dim);
  border: 1px solid var(--orange);
  border-left-width: 3px;
  border-radius: var(--r-xs);
}
.ed-nest-confirm-q { font-size: 12px; line-height: 1.5; color: var(--text); }
.ed-nest-confirm-acts { display: flex; gap: 6px; flex-wrap: wrap; }
.ed-nest-confirm-ok,
.ed-nest-confirm-no {
  padding: 5px 10px;
  font-size: var(--fs-xs); font-weight: 500;
  background: var(--surface-2);
  border: 1px solid transparent;
  border-radius: var(--r-xs);
  color: var(--text-2);
  cursor: pointer;
  transition: background 0.12s, color 0.12s;
}
.ed-nest-confirm-ok { color: var(--danger); border-color: var(--danger); }
.ed-nest-confirm-ok:hover { background: var(--danger-dim); }
.ed-nest-confirm-no:hover { background: var(--surface-3); color: var(--text); }
.ed-nest-confirm-ok:focus-visible,
.ed-nest-confirm-no:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px;
}

/* ── A control's own press stages ──
   One button's force axis, not nesting: the same teal/orange bands and
   the same deep-range pill the press card uses, so a binding that exists
   is on screen without pretending to be gated by anything. */
.ed-nest-stages {
  margin-bottom: 8px;
  padding: 8px 10px;
  background: linear-gradient(180deg, rgba(255, 159, 69, 0.05), transparent 60%);
  border: 1px solid var(--line);
  border-left: 3px solid var(--orange);
  border-radius: var(--r-xs);
}
.ed-nest-stages-head {
  font-family: var(--font-display);
  font-size: 11px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-2);
  margin-bottom: 8px;
}
.ed-nest-stage {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 0;
  cursor: pointer;
}
/* Every stage but the first is separated from the one above it. `:first-of-type`
   would not do here: the track and the scale are divs in the same block, so
   no stage is the first div. "Has a stage before it" is what is meant. */
.ed-nest-stage ~ .ed-nest-stage { border-top: 1px solid var(--line); }
.ed-nest-stage:hover { background: var(--surface-2); }
.ed-nest-stage .ed-nest-num { flex: 0 0 22px; padding-top: 0; }
.ed-nest-stage .ed-nest-trig { font-size: 11.5px; font-weight: 600; flex: 0 0 auto; }
.ed-nest-stage.light .ed-nest-trig { color: var(--teal); }
.ed-nest-stage.deep .ed-nest-trig { color: var(--orange); }
.ed-nest-stage-range {
  flex: 0 0 auto;
  font-family: var(--font-mono); font-size: 9.5px;
  color: var(--text-3);
  border: 1px dashed rgba(255, 159, 69, 0.4);
  border-radius: var(--r-pill);
  padding: 0 6px;
}
.ed-nest-stage .ed-nest-acts { flex: 1 1 auto; font-size: 10.5px; }

/* ── A card with nothing of its own ──
   Deliberately empty, and meant to look like a decision: dashed, muted,
   no warning colour anywhere. Its bindings are on screen, in the tree of
   the trigger that holds them. */
.ed-nest-empty {
  font-size: var(--fs-xs);
  font-style: italic;
  line-height: 1.5;
  color: var(--text-4);
  background: var(--surface-2);
  border: 1px dashed var(--line-med);
  border-radius: var(--r-xs);
  padding: 10px 12px;
}

/* ── "This binding lives in another tree" (Mouse tab and press card) ──
   The tile's badge counts what a button fires; these say the same thing
   about nesting, so they take the badge's shape in the hue editor.html's
   legend already assigns to nesting rather than reading as more badge
   digits. Violet, because a binding is never gated by the control it is
   measured on. */
.ed-mbtn-held {
  font-size: 10.5px;
  color: var(--violet);
  background: var(--violet-dim);
  border-radius: 4px;
  padding: 1px 6px;
  min-width: 0;
  overflow-wrap: anywhere;
}
.ed-mbtn-nested {
  padding: 0 4px;
  border: 1px solid var(--violet-dim);
  border-radius: var(--r-xs);
  color: var(--violet);
  background: var(--violet-dim);
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  line-height: 15px;
  white-space: nowrap;
}
/* The press card's version of the same sentence, and the one place a
   binding can be invisible from the button it is measured on. It says
   where the binding went, so it gets a pointer's treatment — a rule and a
   tint rather than another stage row in the list above it. */
.ed-press-held {
  margin-top: 6px;
  padding: 5px 8px;
  font-size: 10.5px;
  line-height: 1.45;
  color: var(--text-2);
  background: var(--violet-dim);
  border-left: 2px solid var(--violet);
  border-radius: var(--r-xs);
}
/* A force axis with no stage of its own. editor.js emits no band at all
   rather than a full-width light one, so the track is left genuinely
   empty — dashed and hollow, so an unclaimed axis does not read as a bar
   that failed to paint. The scale under it is dimmed for the same reason:
   with no band, 0/50/100% describes nothing. */
.ed-press-track.is-empty {
  background: transparent;
  border-style: dashed;
}
.ed-presscard:has(.ed-press-track.is-empty) .ed-press-scale { opacity: 0.45; }

@media (prefers-reduced-motion: reduce) {
  .ed-nest-row,
  .ed-nest-tools,
  .ed-nest-add,
  .ed-nest-del,
  .ed-nest-menu-item,
  .ed-nest-menu-ctl,
  .ed-nest-menu-back,
  .ed-nest-confirm-ok,
  .ed-nest-confirm-no { transition: none; }
}