/* ============================================================
   Design system — the shadcn "neutral" preset (buFzUQq), dark mode,
   squared corners. Class names match the existing markup so this
   themes the whole app. Backward-compat aliases keep old rules sane.

   The preset is MONOCHROME: every token is oklch(L 0 0) — zero chroma —
   except its one destructive red. Values are the preset's verbatim, with
   the sRGB equivalent noted (they land exactly on Tailwind `neutral`).
   The app previously approximated this ramp by hand (#121212, #191919,
   #a6a6a6); these are the real steps.

   --radius stays 0: the preset itself ships `--radius: 0`, so the app's
   squared identity is the preset's, not an override of it.
   ============================================================ */
/* ============================================================
   CASCADE LAYERS

   The order is declared once, on the line below this comment, and that
   ONE line settles every tie in this file: a later layer wins over an
   earlier one REGARDLESS of selector specificity. That is the point.
   This stylesheet had rules quietly cancelling each other (a `background`
   shorthand that ate the select chevron; page-scoped rules wrestling with
   the base component rules they were only meant to nudge). A layer
   boundary is a decision you can read; a specificity count is one you
   have to work out, in your head, every time.

     tokens      the :root custom-property block. Nothing else.
     base        unqualified element rules: *, body, the shared
                 input/select/textarea chrome, ::placeholder, scrollbars.
                 Specificity (0,0,n) ONLY. Anything carrying a state or
                 attribute qualifier -- select:hover, input:focus,
                 input[type="range"] -- is class-weight and stays in
                 `components`, because it already outranks plain class
                 rules today and must keep doing so.
     components  every class/ID-based component rule. The bulk of the file.
     overrides   rules that deliberately re-style a component IN A CONTEXT:
                 `.app-main .tab-panel .panel`, the hub's
                 [data-page] / .sc-activity-slot / .dash-half block, and
                 the CLEAN PASS at the bottom of the file.

   *** UNLAYERED CSS BEATS EVERY LAYER, AT ANY SPECIFICITY. ***
   Three modules inject a <style> block into <head> at runtime:
     ui/static/charts.js              .bchart-*
     ui/static/shortcuts-activity.js  .act-* (model / readout / dual range)
     ui/static/chain-logos.js         .chain-logo
   Left unlayered they would silently outrank everything below. Each of
   those three blocks therefore opens with `@layer components {`. They are
   appended to <head> AFTER this file's <link>, so inside `components` they
   still come last and still win ties at equal specificity -- precisely the
   cascade they had before layers existed. Add a fourth injector, wrap it too.

   Live inventory of every component in every state: GET /styleguide
   (ui/templates/styleguide.html), which links THIS stylesheet.
   ============================================================ */
@layer tokens, base, components, overrides;

@layer tokens {
:root {
  /* Ashpool tone. The page used to sit on near-black (0.145) with the card a
     full 0.060 above it, so separation came from ELEVATION. Ashpool sits the
     page on charcoal (#111) with the card barely above it (#191919) and lets a
     crisp 1px EDGE do the separating instead — the step here is 0.043.
     The two halves are one change: an earlier attempt flattened these tones
     WITHOUT raising --border and the result read as mush, because nothing was
     left to mark a boundary. Never lower the border contrast below while these
     tones stay this close. */
  /* Read off the live reference (sol-mcp-client.netlify.app) with
     getComputedStyle, not eyeballed from a screenshot — which is how this got
     wrong twice: once too light, once too flat. Literal hex, so nothing drifts
     in an oklch round-trip. */
  --background: #111111;                 /* the page */
  --card: #191919;                       /* boxes DO sit above it — measured */
  /* --surface is the PANEL tone, not a control tone. It was briefly mapped to the
     preset's `secondary` (0.269) — but the app uses --surface for BOTH containers
     (.sc-card, .wallet-row, .seq-set) and controls, so that pulled every card up
     onto the lightest step and the page went flat: card, input and button all one
     colour. shadcn separates a card from an input by BORDER, not fill. */
  --surface: #191919;                    /* same plane as --card */
  --surface-2: #232323;                  /* hover / active, one step above a card */
  /* Surfaces that sit OVER the page — menus, modals, drawers, popovers. These
     are the ONLY things that get a fill, because they must read as detached
     from whatever they cover. Everything on the page itself is --card, which is
     now the page tone: a panel is drawn by its EDGE, not by a fill. */
  --float: #202020;                      /* ABOVE a card — menus/modals must detach from it */
  /* Controls lift off whatever is BEHIND them with a white wash rather than a
     fixed grey — the preset's own trick (`bg-input/30` over --input white/15%).
     A fixed grey can't work here: the same input renders on the page (0.145),
     on a card (0.205) and inside the drawer, and one value cannot read on all
     three. An alpha composites correctly over every one of them. */
  --control-bg: oklch(1 0 0 / 6%);
  --control-bg-hover: oklch(1 0 0 / 10%);

  /* ONE elevation system for everything that floats. Menus, popovers, pickers,
     drawers and gates were each carrying their own shadow (.45 / .55 / .6, three
     different blurs) and picking --border or --border-strong at random. Two
     tiers now: `pop` for things anchored to a control, `modal` for things that
     take over the screen. Floating surfaces always use the STRONGER border —
     they sit over arbitrary content and need the extra definition. */
  --shadow-pop: 0 10px 30px rgba(0, 0, 0, .5);
  --shadow-modal: 0 24px 70px rgba(0, 0, 0, .6);
  /* A drawer is flush to the RIGHT screen edge, so an omnidirectional modal
     shadow casts most of itself off-screen. It needs the light thrown LEFTWARD,
     onto the content it covers — which is a real third tier, not drift. It lives
     here rather than inline in .drawer, where it was silently overwriting
     --shadow-modal two declarations after reading it. */
  --shadow-drawer: -12px 0 32px rgba(0, 0, 0, .45);
  /* ONE inset scale. --pad-card is the same 16px every hub card uses, so a
     popover and a card have the same internal margin. */
  --pad-card: 20px;
  --pad-modal: 24px;

  /* ONE spacing scale. Gaps were declared at fourteen distinct values — 1, 2, 3,
     4, 5, 6, 7, 8, 9, 10, 11, 12, 14 and 16px — so no two blocks shared a
     rhythm and the eye reads the difference as sloppiness rather than as
     hierarchy. Five rungs, and every gap/inset picks one. The rungs were chosen
     so that no existing value moves by more than 2px: this regularises the
     rhythm without relaying out a single component. */
  --sp-0: 2px;    /* hairline: icon clusters, inline glyph gaps */
  --sp-1: 6px;    /* tight: label→control, chip internals */
  --sp-2: 8px;    /* default: most flex/grid gaps */
  --sp-3: 12px;   /* block: between related groups */
  --sp-4: 16px;   /* section: between top-level boxes */
  /* These carry the separation now that the tones are nearly flat (see the note
     on --background). 10%/15% was tuned against a 0.060 tonal step; at 0.043 an
     edge has to be visible on its own. */
  /* Below the reference's .13 on purpose. Their boxes carry more internal
     contrast, so a .13 edge reads as definition there and as a hard outline
     here — the boxes ended up looking drawn-on rather than delineated. */
  --border: rgba(255, 255, 255, .08);
  --border-strong: rgba(255, 255, 255, .14); /* inputs, floating surfaces, hover */
  --foreground: oklch(0.985 0 0);        /* #fafafa */
  --muted: oklch(0.708 0 0);             /* #a1a1a1 — preset `muted-foreground` */
  --muted-2: oklch(0.556 0 0);           /* #737373 — dim / placeholder (preset `ring` step) */
  --primary: oklch(0.922 0 0);           /* #e5e5e5 — inverted primary button */
  --primary-fg: oklch(0.205 0 0);        /* #171717 — dark text on it */
  --ring: oklch(0.556 0 0);              /* #737373 — focus ring */
  --radius: 0px;                         /* the preset's own value */
  /* (The preset's --chart-1…5 grayscale ramp was declared here with ZERO
     consumers in any CSS or JS — charts.js paints from --green/--sell. Deleted:
     a token nothing reads is a decision nobody can act on.) */
  /* TWO control rungs, and only two. There were four (34/30/28/26), which meant
     buttons rendered at 28/30/34 while fields rendered at 26/28/34 — the two
     families did not share a ladder, so a button beside an input lined up only
     by luck. Now every control, of either family, is one of these:
       --control-h     the DEFAULT: anything in a form/field, and primary actions
       --control-h-sm  COMPACT: header + toolbar clusters, small/tiny buttons,
                       inline step controls inside a sentence, single-line chips
     Nothing hardcodes its own height, so any button set beside any input
     matches. Two rungs is the invariant; a third is a regression. */
  --control-h: 32px;
  --control-h-sm: 28px;

  /* ONE wallet identity anatomy — ONE component, density variants.
     A wallet is drawn in six places (rail row, .dz-chip, .sc-wallet-card wide +
     narrow, .wallet-chip, .loop-startw-chip / .loop-sub). They used to disagree
     on height (28/30/31/53), name size (12/12.5/13) and inset (four recipes) —
     five renderings of one concept. Everything below is shared by ALL of them.

     A wallet's IDENTITY is its title + its address. Nothing else. Every variant
     used to lead with a colour dot as well, but that colour was hue =
     index * 360 / count — derived from the wallet's POSITION in the list and the
     list's LENGTH, so minting one relay re-hued every existing wallet and two
     wallets could swap colours because a third appeared. It looked like an
     identity and was not one, so it is gone from every wallet rendering; the row
     now leads with the thing that IS stable.

     The ONLY licensed difference between variants is HOW MANY LINES are shown:
       1 line  → height: var(--control-h-sm)   name + figures. A chip sits on the
                 compact CONTROL rung — a chip IS a control-sized object.
       2 lines → height: var(--chip-h-2)       + the address, on line 2 under the
                 name (narrow slots, where a one-liner starves both).
       3 lines → the rail row only: + the [group] tag and the last-tx stamp. */
  --chip-h-2: 44px;         /* two-line wallet card (narrow .sc-dropzone slots) */
  --chip-x: 18px;           /* square ✕ / copy hit area, every variant */
  --chip-pad-x: 9px;        /* the ONLY horizontal inset a wallet row/chip uses */
  --chip-gap: 8px;          /* name → figures gap, every variant */
  --chip-name-size: var(--body-size);  /* the wallet NAME — it is body copy */
  --chip-addr-size: var(--cap-size);   /* the truncated 0x address, every variant */
  --chip-fig-size: var(--fig-inline);  /* a balance figure, every variant */

  /* Numeric-input width ladder. A field holding 3 digits must NOT stretch to the
     width of an address field — every numeric control picks a rung. ch-based, so
     the box tracks the numeric face instead of a guessed pixel count. */
  --num-w-sm: 7ch;          /* 2–4 digits: counts, seconds, percentages */
  --num-w-md: 9ch;          /* 5–7 digits: bps, fee tiers, decimals */
  --num-w-lg: 13ch;         /* decimal amounts: 0.0001 … 1,234,567.8 */

  /* ============================ TYPE: THREE ROLES ============================
     The app had 22 distinct font-sizes across ~198 declarations. Sizes 11.5,
     12, 12.5 and 13 cannot be told apart at a glance, so six "levels" of
     hierarchy read as one level plus noise. Ashpool gets its clarity from
     using THREE treatments and letting WEIGHT and POSITION carry the rest.

       --title-size   section/card/modal headings. SENTENCE CASE, not uppercase.
       --body-size    everything you read or type: labels, table cells, inputs,
                      buttons, help text, wallet names. One size.
       --cap-size     the ONLY surviving uppercase: a micro caption sitting
                      directly above a figure ("TOTAL ETH"). Nothing else shouts.

     Figures get three rungs and no more, by the job they do. */
  --title-size: 20px;
  --title-weight: 600;
  --body-size: 14px;
  --body-line: 20px;      /* their 14/20 — a 1.5 ratio sets 21 and rides looser */
  --title-line: 28px;     /* their 20/28 */
  --cap-size: 11px;
  --fig-inline: 14px;       /* a figure inside a row of body text */
  --fig-card: 20px;         /* the headline number on a card */
  --fig-hero: 30px;         /* the balance triptych */

  /* Eyebrow tokens now both resolve to the single caption tier — the 9px rung
     is gone (it was unreadable and it made "uppercase" mean two things). */
  --eyebrow-size: var(--cap-size);
  --eyebrow-cap-size: var(--cap-size);
  --eyebrow-track: .08em;   /* letter-spacing */
  --eyebrow-weight: 600;

  /* SEMANTIC colours are deliberately NOT monochrome.
     The preset is grayscale apart from one destructive red — and a grayscale
     palette cannot say "this leg BOUGHT and that one SOLD", or separate a
     confirmed tx from a failed one. Those distinctions are the product, not
     decoration: --buy/--sell alias green/orange, and the net figures, status
     dots, toasts and the last-tx stamp all read from this set. So the preset
     supplies the neutrals (everything above) and `--red`; the rest stay.
     The preset's own destructive proves the point — even shadcn keeps one. */
  --red: oklch(0.704 0.191 22.216);   /* the preset's destructive, verbatim */
  --blue: #3b82f6; --blue-fg: #bfdbfe;
  --green: #22c55e; --amber: #f59e0b;
  --orange: #f97316; --violet: #a78bfa;
  --mono: "Geist Mono", ui-monospace, "SF Mono", SFMono-Regular, Menlo, monospace;
  --num: "Geist Mono", "IBM Plex Mono", ui-monospace, "SF Mono", monospace;

  /* app-shell elevation + geometry */
  --rail: #111111;            /* the rail is page-level; its border divides it */
  --shell-header-h: 52px;     /* full-width top header row */
  --shell-left-w: 300px;      /* left rail column */
  /* Hub Activity/Sweep/Transfer boxes share one height. Sized so their bodies
     do NOT scroll: at 540px Sweep overflowed by 164px and Activity by 107px, so
     the controls you were about to press sat below a scroll line inside a card
     that already had a fixed height. Sweep is the tallest of the three and sets
     the floor. Raise this if a card grows a row; do not re-introduce the inner
     scrollbar — a fixed-height box that also scrolls hides its own controls. */
  --dash-box-h: 712px;
  /* --shell-right-w removed with the right Activity feed rail (now a 2-col shell). */

  /* legacy aliases (existing rules keep working, now shadcn-toned) */
  --bg: var(--background);
  --panel: var(--card);
  --panel-2: var(--surface);
  --text: var(--foreground);
  --accent: var(--ring);
  --accent-2: var(--primary);
  --ok: var(--green); --bad: var(--red);
  --danger: var(--red); --danger-2: #b91c1c; --warn: var(--amber);
  --sell: var(--orange); --buy: var(--green); --approve: var(--violet);
}
}  /* end @layer tokens */

@layer base {
* { box-sizing: border-box; }

/* One body size for the whole document, so anything that does NOT set its own
   font-size inherits the body role instead of the browser's 16px default. */
:where(body) { font-size: var(--body-size); }

/* Controls inherit the page face and size. Without this, a <button>/<input>/
   <select> falls back to the UA's own font stack at 13.333px and quietly
   escapes the type scale. */
:where(button, input, select, textarea) { font: inherit; }

/* ---- Interaction states ----
   Written with :where() so every rule below has ZERO specificity: any component
   rule wins over it, which is what you want for a base layer. These are the two
   states the app had no styling for at all — keyboard focus was invisible on
   .btn, and a disabled control was pixel-identical to an enabled one, so the
   only way to discover a control was dead was to click it. */
:where(button, a, input, select, textarea, [tabindex], [role="button"]):focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}
:where(button, input, select, textarea):disabled,
:where([aria-disabled="true"]) {
  opacity: .45;
  cursor: not-allowed;
}

/* The `hidden` ATTRIBUTE must actually hide. The UA stylesheet implements it as
   `[hidden] { display: none }`, which ANY author `display` beats — so an element
   with `.foo { display: flex }` stays visible while `el.hidden = true`, and the
   JS reads as correct. This bit exactly once already (.act-derived rendering
   inside the action bar). !important because the whole point is to outrank a
   component's own display. */
[hidden] { display: none !important; }
}  /* end @layer base */

/* Self-hosted numeric face (Geist Mono, MIT) — consistent figures on any machine */
@font-face {
  font-family: "Geist Mono";
  src: url("/static/fonts/GeistMono-Regular.woff2") format("woff2");
  font-weight: 400 700;
  font-display: swap;
}

/* Geist Sans (Vercel, SIL OFL) — the reference's text face, self-hosted from
   the upstream @fontsource package.

   TWO weights, deliberately. The reference serves 400 and 500 and NOTHING else;
   its headings still declare 600, so the browser synthesises that from 500.
   Shipping a real 600 here would render our headings genuinely heavier than
   theirs — matching the declaration but not the result. font-display: swap
   matches theirs too, so first paint uses the fallback rather than blocking. */
@font-face {
  font-family: "Geist";
  src: url("/static/fonts/Geist-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist";
  src: url("/static/fonts/Geist-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@layer components {
/* Ethereum mark, painted in the current text color, prefixed to ETH amounts.
   A SOLID silhouette, deliberately: the canonical 4-face mark carries two faces
   at opacity .55, and at the ~7px this renders at in a chip or table cell those
   faces vanish and the rest reads as a smudge. One flat octahedron stays legible
   at every size it is used (chips, drop-zone cards, loop rows, session totals). */
.eth-lead::before {
  content: "";
  display: inline-block;
  width: .68em; height: 1em;
  vertical-align: -.12em; margin-right: 4px;
  background-color: currentColor;
  opacity: .85;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 417'%3E%3Cpath d='M128 0 0 212.3 128 270.4 256 212.3Z'/%3E%3Cpath d='M128 312.2 0 236.6 128 416.9 256 236.6Z'/%3E%3C/svg%3E") no-repeat center / contain;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 417'%3E%3Cpath d='M128 0 0 212.3 128 270.4 256 212.3Z'/%3E%3Cpath d='M128 312.2 0 236.6 128 416.9 256 236.6Z'/%3E%3C/svg%3E") no-repeat center / contain;
}
}  /* end @layer components */

@layer base {
body {
  margin: 0;
  background: var(--background);
  color: var(--foreground);
  /* Split out of the `font:` shorthand deliberately. As a shorthand this set the
     size to a hardcoded 13.5px, which no font-size sweep could see and which
     every em-based child inherited — an <h2> at 1.5em landed on 20.25px.

     Face + metrics are the reference's, read off it with getComputedStyle:
     Geist, 14px on a 20px line, letter-spacing NORMAL, smoothing AUTO. The app
     had -webkit-font-smoothing: antialiased and negative tracking on every
     heading; both are ours, neither is theirs, and together they are most of
     why our type read heavier. */
  font-family: "Geist", -apple-system, "SF Pro Text", system-ui, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  font-size: var(--body-size);
  line-height: var(--body-line);
  letter-spacing: normal;
  -webkit-font-smoothing: auto;
}
}  /* end @layer base */

@layer components {
/* ============================================================
   App shell — a full-height grid: a full-width top header, then a
   row of [left rail | center canvas | right rail]. The rails/header
   are elevated (--rail); the center canvas is the recessed floor.
   ============================================================ */
.app {
  display: grid;
  grid-template-columns: var(--shell-left-w) 1fr;
  grid-template-rows: var(--shell-header-h) 1fr;
  height: 100vh; overflow: hidden;
}
/* the center canvas is the only independently-scrolling region */
.app-main {
  min-width: 0; min-height: 0; overflow-y: auto;
  /* The FRAME must equal the RHYTHM. This was 26px top / 32px side / 64px
     bottom while the gaps between boxes are --sp-4 (16px), so the margin around
     the grid read as twice the space inside it: the header sat further from the
     first card than the cards sat from each other, and the wallet rail sat
     further from the first box than that box sat from its neighbour. One value
     on all four sides is what makes the page look evenly gridded.
     The bottom keeps extra room ONLY because the sticky shortcut bar overlays
     the scroll floor and would otherwise cover the last box. */
  padding: var(--sp-4) var(--sp-4) 64px;
  background: var(--background);
  box-shadow: inset 0 8px 18px -12px rgba(0, 0, 0, .85);

  /* NO token overrides here any more.
     This block used to redefine background/card/surface/foreground/muted and
     swap solid borders for white hairlines — because the app was approximating
     a reference by hand, and the centre needed different numbers from the rails
     to look right. The shadcn preset ships ONE coherent ramp (and already uses
     white-alpha borders), so a second divergent set is now just drift waiting
     to happen: two places to edit, two chances to disagree. One scope, in :root.
     (--hairline / --hairline-strong went with it — nothing ever read them.) */
}
/* The tx-notifications panel lives in the DOM under .app-main but belongs to
   the right feed family — shield it so the restyle above does not touch it. */
#toast-stack {
  /* The token re-shield is gone: it existed only to undo .app-main's divergent
     values for a panel that sits under .app-main but belongs to the feed family.
     With a single ramp there is nothing to undo. */
}

/* ---- Top header (full-width shell chrome) ----
   ONE rhythm for the whole bar:
     • every item is separated by --ctx-gap (no per-element margins);
     • a .ctx-vr divider marks a LOGICAL GROUP boundary and only that, and gets
       exactly --ctx-gap either side (brand | network | coin | vault … totals |
       actions);
     • every control — coin select, "+", Lock/Unlock, account pill, ↻ and ⚙ —
       is exactly --ctx-ctl-h tall with the same horizontal padding; icon
       buttons are square at that height. */
.shell-top {
  --ctx-gap: var(--sp-4);
  --ctx-ctl-h: var(--control-h-sm);
  --ctx-ctl-pad: 10px;
  grid-column: 1 / -1;
  display: flex; align-items: center; gap: var(--ctx-gap);
  padding: 0 16px;
  background: var(--rail); border-bottom: 1px solid var(--border);
  font-size: var(--body-size);
}
.brand {
  display: flex; align-items: center; gap: var(--sp-2); flex: 0 0 auto;
  font-weight: 600; font-size: var(--body-size);
}
.brand .brand-mark { width: 15px; height: 15px; background: var(--foreground); flex: 0 0 auto; }
/* header groups are borderless text; only the coin select + buttons keep chrome */
.shell-top .ctx-meta, .shell-top .ctx-item { display: inline-flex; align-items: center; gap: var(--sp-2); }
.shell-top .ctx-vr { width: 1px; height: 22px; background: var(--border); flex: 0 0 auto; }
.shell-top .ctx-right { margin-left: auto; display: inline-flex; align-items: center; gap: var(--ctx-gap); }
.shell-top .ctx-totals { display: inline-flex; align-items: center; gap: var(--ctx-gap); }
/* ↻ and ⚙ are ONE action group, so they sit tighter than the bar rhythm. */
.shell-top .ctx-actions { display: inline-flex; align-items: center; gap: var(--sp-1); flex: 0 0 auto; }
/* Right-cluster square icon buttons (↻ refresh, ⚙ settings): bordered square,
   icon only, hover lift — the corner-button look. .btn base supplies the
   centering + transition; this overrides its size/padding. */
.shell-top .hdr-iconbtn {
  width: var(--ctx-ctl-h); height: var(--ctx-ctl-h); padding: 0; flex: 0 0 auto;
  background: transparent; color: var(--muted);
  border: 1px solid var(--border); border-radius: var(--radius);
  font-size: var(--body-size); line-height: 1;
}
.shell-top .hdr-iconbtn:hover {
  background: var(--surface-2); color: var(--foreground); border-color: var(--border-strong);
}
/* While loading, btnLoading swaps the glyph for a text label ("Refreshing…")
   that can't fit a 34px square — hide the text so only the ::before spinner shows. */
.shell-top .hdr-iconbtn.loading { font-size: 0; }

/* ============================================================
   Wallet rail — left rail: persistent wallet list, sort/search,
   app-wide selection + drag source. Mirrors the feed's treatment
   (sticky column, collapses to a thin strip, overlays when narrow).
   ============================================================ */
.wallet-rail {
  grid-column: 1; grid-row: 2;
  min-height: 0; overflow: hidden;
  display: flex; flex-direction: column;
  background: var(--rail); border-right: 1px solid var(--border);
  z-index: 55;
}

/* ---- Left-rail nav: single-page shell, so the nav renders NOTHING. The #tabs
   element (and its hidden state-tracking buttons) stays in the DOM — sidebar.js
   requires a .tabs child and activateTab() still toggles .active on the
   buttons — but it takes zero height so the wallet list fills the full rail. */
.wallet-rail .tabs { display: none; }

/* The rail column has ONE left edge: head, sort/search, group folders, wallet
   rows and the footer all inset by --chip-pad-x. They used to run 14 / 12 / 14 /
   14 / 12, so the title, the search box and the row beneath it each started on a
   different pixel. */
.rail-head {
  display: flex; align-items: center; gap: var(--sp-2); flex: 0 0 auto;
  padding: var(--sp-3) var(--chip-pad-x) var(--sp-2);
}
.rail-head h2 { margin: 0; flex: 1; font-size: var(--body-size); font-weight: 600; }
.rail-controls {
  flex: 0 0 auto; display: flex; flex-direction: column; gap: var(--sp-1);
  padding: var(--sp-3) var(--chip-pad-x); border-bottom: 1px solid var(--border);
}
/* Sort + search share one rung and one inset. The select's chevron gutter is
   owned by --sel-gut (set in the compact-select list), so only padding-LEFT is
   restated here — a `padding` shorthand would delete the gutter. */
.rail-controls select, .rail-controls input {
  height: var(--control-h-sm); font-size: var(--body-size); padding-left: 8px;
}
.rail-controls input { padding-right: 8px; }
/* Draggable group folders above the wallet list. Drag a chip into any wallet
   input to add ALL its wallets; drop wallets onto a chip to assign them. */
.rail-groups { display: flex; flex-wrap: wrap; gap: var(--sp-1); padding: var(--sp-1) var(--chip-pad-x) var(--sp-2); }
.rail-groups:empty { display: none; }
.rail-group-chip {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  padding: 3px 5px 3px 9px; background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius, 0px); cursor: grab; user-select: none; max-width: 100%;
}
.rail-group-chip:hover { border-color: var(--border-strong); }
/* Active = the rail is currently filtered to this chip. Click it again to clear. */
.rail-group-chip.active { border-color: var(--ring); background: var(--surface-2); color: var(--foreground); }
.rail-group-chip.active .rail-group-name { color: var(--foreground); }
.rail-dust-chip.active { border-color: var(--amber); }
.rail-dust-chip.active .rail-group-name { color: var(--amber); }
.rail-group-chip.dragging { opacity: .5; }
.rail-group-chip.drop-hover { border-color: var(--ring); background: var(--surface); }
.rail-group-name {
  font-size: var(--cap-size); font-weight: 600; text-transform: uppercase; letter-spacing: .08em;
  color: var(--foreground); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rail-group-count { font-size: var(--cap-size); font-family: var(--num); color: var(--muted-2); font-variant-numeric: tabular-nums; }
.rail-group-del { background: none; border: 0; color: var(--muted-2); cursor: pointer; font-size: var(--body-size); line-height: 1; padding: 0 1px; }
.rail-group-del:hover { color: var(--sell); }
.rail-group-add {
  display: inline-flex; align-items: center; padding: 3px 9px; background: none;
  border: 1px dashed var(--border); border-radius: var(--radius, 0px); color: var(--muted);
  cursor: pointer; font-size: var(--cap-size); font-weight: var(--eyebrow-weight); text-transform: uppercase;
  letter-spacing: var(--eyebrow-track);
}
.rail-group-add:hover { color: var(--foreground); border-color: var(--border-strong); }
.rail-body { flex: 1 1 auto; overflow-y: auto; padding: 0; }
.rail-list { display: flex; flex-direction: column; gap: 0; }
.rail-empty { color: var(--muted); font-size: var(--body-size); padding: var(--sp-3) var(--chip-pad-x); line-height: 1.4; }
/* Footer note under a capped/streaming rail list (top-N of many, fetch progress) */
.rail-more {
  color: var(--muted-2); font-size: var(--cap-size); font-weight: var(--eyebrow-weight); text-transform: uppercase;
  letter-spacing: var(--eyebrow-track); padding: 9px 14px; border-top: 1px solid var(--border);
  font-variant-numeric: tabular-nums;
}

/* One wallet row: selectable + draggable, hairline-divided, no per-row card.
   This is a LIST ROW, not a stat card — it used to stack an uppercase 17px name,
   an address, then a 2-up grid of caption-over-24px-figure tiles, ~115px of
   height each, which made the rail unusable past a handful of wallets. Now it
   scans WHAT IT IS over WHAT IT HOLDS:

       NAME ......................................... [group]
       0x1234…5678 ⧉ ................. USDC 1,234.5   ETH 0.0421
       ▎LAST TX  got 0.012 Ξ ......................... 4m

   ~52px tall (~68px with the optional LAST TX line), balances right-aligned in
   a fixed-width tabular block so every row's figures share one column.

   The row IS the grid — there is no leading slot in front of the identity, so
   the name and the address start at the row's own inset instead of being pushed
   18px in by a colour dot whose hue came from a list position. .rail-wallet-id is
   `display: contents` (the .sc-wallet-card idiom), so name / address / balances /
   last-tx are direct children of this grid rather than a flex box inside it. */
.rail-wallet {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: center; column-gap: var(--chip-gap); row-gap: var(--sp-0);
  background: transparent; border: none; border-bottom: 1px solid var(--border); border-radius: 0;
  padding: var(--sp-3) var(--chip-pad-x); min-height: 56px; cursor: pointer; user-select: none;
  transition: background .12s;
}
/* [group] tag — shares the NAME line, in the right-hand column directly above
   the balance block (the column is simply empty when ungrouped). .act-cap look. */
.rail-wallet-group {
  grid-column: 2; grid-row: 1; justify-self: end; align-self: center;
  font-family: inherit; font-size: var(--eyebrow-cap-size); font-weight: var(--eyebrow-weight); line-height: 1;
  text-transform: uppercase; letter-spacing: .1em; color: var(--muted-2);
  white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis;
}
.rail-wallet:hover { background: var(--surface); }
.rail-wallet.selected { background: var(--surface-2); box-shadow: inset 2px 0 0 var(--ring); }
.rail-wallet.dragging { opacity: .45; }
/* Hierarchy by COLOUR, not by dimming. Name, address AND last-tx all used to sit
   at opacity .5 until the row was selected — which put the two things that tell
   one wallet from another (the title and the address) behind a veil, on the one
   surface whose whole job is being scanned. Now the identity is legible at rest
   and the type ramp carries the hierarchy (name → --foreground, address →
   --muted, mono); only the SECONDARY metadata recedes: the [group] tag and the
   last-tx stamp, which are context, not identity.
   (Per-element, not on .rail-wallet-id — that element is `display: contents`, and
   an opacity on it would create a stacking context the children can't escape.) */
.rail-wallet-group, .rail-lasttx { opacity: .62; transition: opacity .12s; }
.rail-wallet:hover .rail-wallet-group,
.rail-wallet:hover .rail-lasttx { opacity: .82; }
.rail-wallet.selected .rail-wallet-group,
.rail-wallet.selected .rail-lasttx { opacity: 1; }

/* Identity grid — the wrapper dissolves into .rail-wallet's grid:
     col 1 (flexible)  row 1 name        row 2 address + copy
     col 2 (fixed)     row 1 [group]     row 2 balances
     row 3 (full width)                  last-tx stamp (optional)
   The balance block is a fixed width, so its two figures hold the same column
   on every row no matter how long a name or group tag runs. */
.rail-wallet-id { display: contents; }
/* Wallet name — size/weight come from the shared chip-anatomy rule. The
   UPPERCASE is a kept difference: the rail is a scan-list, not a label. */
.rail-wallet-name {
  grid-column: 1; grid-row: 1;
  line-height: 1.3; letter-spacing: .01em; text-transform: uppercase;
  color: var(--foreground);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Truncated 0x address (mono, muted — matches .wallet-chip-addr) + a subtle,
   borderless copy icon button beside it. app.js wraps the two in .rail-wallet-addr-row. */
.rail-wallet-addr-row {
  grid-column: 1; grid-row: 2;
  display: flex; align-items: center; gap: var(--sp-1); min-width: 0;
}
.rail-wallet-addr {
  font-family: var(--mono); color: var(--muted); letter-spacing: .01em;
  font-variant-numeric: tabular-nums;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rail-wallet-copy {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--chip-x); height: var(--chip-x); padding: 0;
  background: none; border: 0; border-radius: var(--radius, 0px);
  color: var(--muted-2); cursor: pointer; line-height: 1; font-size: var(--cap-size);
  transition: color .12s, background .12s;
}
.rail-wallet-copy:hover { color: var(--foreground); background: var(--surface-2); }
.rail-wallet-copy:focus-visible { outline: 1px solid var(--ring); outline-offset: 1px; }
/* (A `.rail-wallet-copy svg` rule sized an icon that does not exist — app.js sets
   the button's textContent to the ⧉ glyph. Dead since it was written.) */

/* Balances: a right-aligned tabular PAIR on the address line — two equal cells,
   each a micro-caption pinned left and its figure pinned right. Fixed overall
   width, so captions form one column and figures another straight down the rail. */
.rail-wallet-bals {
  grid-column: 2; grid-row: 2; justify-self: end;
  display: grid; grid-template-columns: 84px 64px;  /* token needs more digits than ETH */
  column-gap: var(--sp-2); margin-top: 0;
}
.rail-wallet-balcell {
  min-width: 0; display: flex; flex-direction: row;
  align-items: baseline; justify-content: space-between; gap: var(--sp-1);
}
/* caption — EXACT .act-cap ("ETH AMOUNT") treatment. Shrinks before the figure
   does, so an eight-letter ticker never eats into the number. */
.rail-cap {
  flex: 0 3 auto; min-width: 0; overflow: hidden; white-space: nowrap;
  font-family: inherit; font-size: var(--eyebrow-cap-size); font-weight: var(--eyebrow-weight); line-height: 1;
  text-transform: uppercase; letter-spacing: .1em; color: var(--muted-2);
}
/* figure — the row's data point: numeric face, tabular, right-aligned. Size is
   the shared --chip-fig-size; the 600 WEIGHT is a kept difference (the rail is
   the surface you scan balances on, so its figures carry more ink). */
.rail-wallet-bal, .rail-wallet-eth {
  font-family: var(--num); font-weight: 600; line-height: 1.2;
  font-variant-numeric: tabular-nums;
  color: var(--foreground); min-width: 0; text-align: right;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rail-wallet-bal.dim, .rail-wallet-eth.dim { color: var(--muted-2); }

/* (ETH figure is styled by the shared .rail-wallet-bal, .rail-wallet-eth rule
   above; the WETH amber flag is handled by the .bal-weth rule further down.) */

/* Last-tx line: a left accent bar colored by buy/sell, an uppercase LAST TX
   label (the Logs .dlog- / .act-label full-caps treatment), the ETH amount in
   the same buy/sell color, and a muted relative time pushed to the right.
   NOTE: the "dlog-*" glob used to be written next to a slash here, which closed
   this comment early and dumped the rest of the sentence into the stylesheet. */
.rail-lasttx {
  grid-column: 1 / -1; grid-row: 3;
  display: flex; align-items: center; gap: var(--sp-1); min-width: 0;
  margin-top: 1px; white-space: nowrap;
}
.rail-lasttx-accent {
  flex: 0 0 2px; align-self: stretch; min-height: 11px; border-radius: 1px;
  background: var(--muted-2);
}
.rail-lasttx-label {
  flex: 0 0 auto;
  font-family: inherit; font-size: var(--cap-size); font-weight: 600; line-height: 1;
  text-transform: uppercase; letter-spacing: .1em; color: var(--muted-2);
}
.rail-lasttx-amt {
  min-width: 0; overflow: hidden; text-overflow: ellipsis;
  font-family: var(--mono); font-size: var(--cap-size); font-variant-numeric: tabular-nums; color: var(--muted);
}
.rail-lasttx-ago {
  flex: 0 0 auto; margin-left: auto;
  font-family: var(--mono); font-size: var(--cap-size); font-variant-numeric: tabular-nums; color: var(--muted-2);
}
.rail-lasttx.buy  .rail-lasttx-accent { background: var(--buy); }
.rail-lasttx.buy  .rail-lasttx-amt    { color: var(--buy); }
.rail-lasttx.sell .rail-lasttx-accent { background: var(--sell); }
.rail-lasttx.sell .rail-lasttx-amt    { color: var(--sell); }
.rail-foot {
  flex: 0 0 auto; padding: 8px var(--chip-pad-x); border-top: 1px solid var(--border);
  font-size: var(--body-size); color: var(--muted); min-height: 1px;
}
.rail-foot:empty { display: none; }

/* Loop-run groups: a collapsible header (name · count · ETH total) that folds a
   minted subsidiary family so it doesn't fill the flat rail. Rows inside are the
   same .rail-wallet grid, so click/drag/right-click behave identically. */
.rail-group { border-top: 1px solid var(--border); }
.rail-group-head {
  display: flex; align-items: center; gap: var(--sp-2); width: 100%; padding: 8px 12px;
  background: none; border: 0; cursor: pointer; color: var(--muted);
  font-family: inherit; font-size: var(--body-size); text-align: left;
}
.rail-group-head:hover { color: var(--foreground); background: var(--surface); }
.rail-group-caret { flex: 0 0 auto; width: 10px; color: var(--muted-2); }
.rail-group-label {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-weight: 600; letter-spacing: .02em;
}
.rail-group-count {
  flex: 0 0 auto; color: var(--muted-2); font-variant-numeric: tabular-nums;
  border: 1px solid var(--border-strong); padding: 0 5px; font-size: var(--cap-size); line-height: 15px;
}
.rail-group-stat {
  flex: 0 0 auto; margin-left: auto; color: var(--muted);
  font-variant-numeric: tabular-nums; font-size: var(--cap-size);
}
.rail-group.open > .rail-group-head { color: var(--foreground); }
.rail-group .rail-wallet:last-child { border-bottom: none; }

/* Dust section: archived (empty) wallets, collapsed at the bottom of the rail */
.rail-dust { border-top: 1px solid var(--border); margin-top: 4px; }
.rail-dust-head { display: flex; align-items: center; }
.rail-dust-toggle {
  display: flex; align-items: center; gap: var(--sp-2); flex: 1; min-width: 0; padding: 8px 12px;
  background: none; border: 0; cursor: pointer; color: var(--muted);
  font-family: inherit; font-size: var(--body-size); text-align: left;
}
.rail-dust-toggle:hover { color: var(--foreground); }
.rail-dust-caret { width: 10px; color: var(--muted-2); }
.rail-dust-label { text-transform: uppercase; letter-spacing: var(--eyebrow-track); font-weight: var(--eyebrow-weight); }
.rail-dust-stat { margin-left: auto; color: var(--muted); font-variant-numeric: tabular-nums; }
.rail-dust-refresh {
  flex: 0 0 auto; background: none; border: 0; color: var(--muted-2); cursor: pointer;
  font-size: var(--body-size); padding: 8px 12px 8px 4px;
}
.rail-dust-refresh:hover { color: var(--foreground); }
.rail-dust-refresh:disabled { opacity: .5; cursor: default; }
.rail-dust-row {
  display: flex; align-items: center; gap: var(--sp-2); padding: 6px 12px;
  border-top: 1px solid var(--border); opacity: .72;
}
.rail-dust-row .wallet-chip { flex: 1; min-width: 0; }
.rail-dust-un {
  flex: 0 0 auto; background: none; border: 1px solid var(--border-strong);
  color: var(--muted); font-family: inherit; font-size: var(--cap-size); padding: 2px 8px; cursor: pointer;
}
.rail-dust-un:hover { color: var(--foreground); border-color: var(--muted); }

/* Collapsed: a thin vertical strip that expands on click (mirrors the feed).
   The grid column tracks a CSS var so collapsing narrows the whole column. */
.app:has(> .wallet-rail.collapsed) { grid-template-columns: 42px 1fr; }
.wallet-rail.collapsed .tabs,
.wallet-rail.collapsed .rail-head,
.wallet-rail.collapsed .rail-controls,
.wallet-rail.collapsed .rail-body,
.wallet-rail.collapsed .rail-foot { display: none; }
.rail-strip {
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-3);
  width: 100%; min-height: 160px; padding: 12px 0;
  background: var(--rail); border: none; cursor: pointer; color: var(--muted);
}
.rail-strip:hover { background: var(--surface); color: var(--foreground); }
.rail-strip[hidden] { display: none; }
.rail-strip-label {
  writing-mode: vertical-rl; text-orientation: mixed;
  font-size: var(--cap-size); font-weight: 600; text-transform: uppercase; letter-spacing: .08em;
}

/* ---- Wallet drop zone (reusable target fed by rail drags) ---- */
.wallet-dropzone { position: relative; }
.wallet-dropzone.drop-hover {
  outline: 2px dashed var(--ring); outline-offset: 2px;
  background: color-mix(in srgb, var(--ring) 12%, transparent);
}
/* ---- THE chip anatomy — SINGLE-LINE density ----
   name (truncates) · balance (right, tabular) · ✕ (square hit area).
   Height is the COMPACT CONTROL RUNG, not a chip-only number: a chip is a
   control-sized object, so a chip and a .btn.small in the same row line up.
   One line has no room for the address, so the chip carries the full address in
   its `title` — the identity is a hover away, not absent. */
.dz-chips { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.dz-chip {
  display: inline-flex; align-items: center; gap: var(--chip-gap);
  height: var(--control-h-sm); min-width: 0; max-width: 100%;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 0 var(--chip-pad-x);
}
/* The wallet NAME, at one size, in every variant. */
.dz-chip-name, .sc-wallet-name, .wallet-chip-title, .rail-wallet-name, .loop-startw-nm, .loop-sub-nm {
  font-size: var(--chip-name-size); font-weight: 600;
}
/* The truncated 0x address, at one size, in every variant that shows one. It is
   always `truncAddr()` (0x1234…5678) — ONE helper in app.js, never re-cut per
   site — so the same wallet reads identically in the rail, a drop slot and the
   sub list. */
.wallet-chip-addr, .rail-wallet-addr, .loop-sub-addr, .sc-wallet-addr {
  font-size: var(--chip-addr-size);
}
/* A balance FIGURE, at one size, in every variant. */
.dz-chip-bal, .sc-wallet-bal, .sc-wallet-eth, .wallet-chip-bal,
.rail-wallet-bal, .rail-wallet-eth, .loop-startw-bal, .loop-sub-bal {
  font-size: var(--chip-fig-size);
}
.dz-chip-name {
  flex: 0 1 auto; min-width: 0; max-width: 168px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dz-chip-x {
  flex: 0 0 auto; width: var(--chip-x); height: var(--chip-x);
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: none; color: var(--muted-2); cursor: pointer;
  font-size: var(--cap-size); line-height: 1; padding: 0;
}
.dz-chip-x:hover { color: var(--foreground); background: var(--surface-2); }
.dz-empty { color: var(--muted-2); font-size: var(--body-size); }
.dz-pick { flex: 0 0 auto; }

/* Elements accepting a rail drop flash a subtle target state. */
.drop-target-hover {
  outline: 2px dashed var(--ring); outline-offset: 2px;
  background: color-mix(in srgb, var(--ring) 10%, transparent);
}

/* Narrow viewports: default the left rail collapsed to reclaim canvas width. */
@media (max-width: 1200px) {
  .app { grid-template-columns: 42px 1fr; }
  .app:has(> .wallet-rail:not(.collapsed)) { grid-template-columns: var(--shell-left-w) 1fr; }
}

/* ---- Canvas status strip (pinned status/error at the top of the center) ---- */
.canvas-status:empty { display: none; }
.canvas-status > .status:not([hidden]),
.canvas-status > .error-box:not([hidden]) { margin-bottom: 16px; }
.canvas-status > .error-box { max-height: 30vh; overflow-y: auto; }

/* ---- Tab panels (the nav chrome is gone — the hidden #tabs buttons only
   track the active section; activateTab() still shows/hides these panels) ---- */
.tab-panel { display: none; }
.tab-panel.active { display: block; }

.hint { color: var(--muted); font-size: var(--body-size); margin: 0 0 14px; }
.hint strong { color: var(--foreground); font-weight: 600; }
.sub-hint { margin: 8px 0 4px; }
/* A hint sitting INSIDE a flex row (status text beside a button): the row's gap
   owns the spacing, so the block margin goes. */
.hint.inline { margin: 0; }

/* ---- "?" hover-help (ONE reusable component) ----
   A title row (.h1row) pairs a heading with a small squared "?" badge; the
   descriptive paragraph that used to sit under the title now lives in .help-tip,
   revealed on hover or keyboard focus. Pure CSS, no click. */
.h1row { display: flex; align-items: center; gap: var(--sp-2); }
.h1row h2, .h1row h3 { margin: 0; }
/* ---- Field groups ----
   Flattened from bordered cards to an eyebrow label over a hairline divider, so a
   group sitting inside a panel (Sequencer quick-trade, Settings sections) no
   longer reads as a box-in-a-box. The legend becomes the section eyebrow. */
.field-group {
  border: none; border-radius: 0;
  padding: 0; margin: 0 0 18px;
}
.field-group legend {
  display: block; width: 100%; padding: 0 0 10px; margin-bottom: 14px;
  border-bottom: 1px solid var(--border);
  font-size: var(--cap-size); text-transform: uppercase;
  letter-spacing: .08em; color: var(--muted); font-weight: 600;
}
details.adv-group { padding: 0; }
details.adv-group > summary {
  cursor: pointer; font-size: var(--cap-size); text-transform: uppercase;
  letter-spacing: .08em; color: var(--muted); font-weight: 600;
  user-select: none; padding-bottom: 10px; border-bottom: 1px solid var(--border);
}
details.adv-group[open] > summary { margin-bottom: 14px; }

/* ---- Preset callouts ---- */
.preset-field { margin-bottom: 10px; }
.preset-note {
  background: color-mix(in srgb, var(--blue) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--blue) 40%, transparent);
  color: var(--blue-fg);
  border-radius: var(--radius); padding: 9px 11px; margin-bottom: 8px; font-size: var(--body-size);
}
.preset-warn {
  background: color-mix(in srgb, var(--amber) 12%, transparent);
  border: 1px solid var(--amber); color: #fcd34d;
  border-radius: var(--radius); padding: 9px 11px; margin-bottom: 8px; font-size: var(--body-size); font-weight: 600;
}

.grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--sp-4); }
@media (max-width: 960px) { .grid { grid-template-columns: 1fr; } }
/* results panels start hidden; give the form the full width until they show */
.grid:has(> .results-panel[hidden]) { grid-template-columns: 1fr; }
/* Full-width vertical stack: each panel spans the full width, stacked top to
   bottom (Sequencer — form on top, results below, never split-screen). */
.stack { display: flex; flex-direction: column; gap: var(--sp-4); }

/* ---- Panels / cards ---- */
.panel {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: var(--pad-card);
}
}  /* end @layer components */
@layer overrides {
/* Inside the center canvas the tab bodies are de-boxed: the recessed floor IS the
   surface, so panels drop their card chrome and sit flat on it (no border/bg/pad),
   separated by the section eyebrows/hairlines rather than a wrapping box. Results
   panels stay chromeless too. */
.app-main .tab-panel .panel {
  background: transparent; border: none; padding: 0;
}
}  /* end @layer overrides */
@layer components {
.panel h2 { margin: 0 0 4px; font-size: var(--title-size); font-weight: 600; line-height: var(--title-line); }
/* One-line canvas subtitle sitting directly under a center title. */
.canvas-sub { color: var(--muted-2); font-size: var(--body-size); font-weight: 400; margin: 5px 0 22px; }
/* h3 is a subheading, not a caption. It used to be uppercase at caption size,
   which made "uppercase" mean two different things — a label over a figure AND
   a section head. Uppercase now has exactly one job; a subheading gets its
   hierarchy from weight, like every other heading. */
.panel h3 { margin: 0; font-size: var(--body-size); color: var(--foreground); font-weight: 600; }

/* ---- Fields ---- */
.fields { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-3); }
.field { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; }
.field.wide { grid-column: 1 / -1; }
.field > span {
  font-size: var(--eyebrow-size); color: var(--muted); font-weight: var(--eyebrow-weight);
  text-transform: uppercase; letter-spacing: var(--eyebrow-track);
}
.field em { color: var(--muted-2); font-style: normal; text-transform: none; letter-spacing: normal; font-weight: 500; }
}  /* end @layer components */

@layer base {
/* Height comes from the --control-h ladder, so the box carries NO vertical
   padding — mixing the two is what made same-row controls sit a pixel or two
   out of line whenever one rule redeclared `padding` and another `height`. */
input, select, textarea {
  background: var(--control-bg); border: 1px solid var(--border-strong); color: var(--foreground);
  border-radius: var(--radius); padding: 0 10px; font-size: var(--body-size);
  width: 100%; min-width: 0; height: var(--control-h); font-family: inherit;
  transition: border-color .12s, box-shadow .12s;
}
/* A textarea is the one control sized by its `rows`, not by the ladder. */
textarea {
  height: auto; padding: 8px 10px; line-height: 1.45; resize: vertical;
}
/* Custom dropdown chrome for EVERY select: reset native appearance, draw our own
   chevron as a data-URI, one consistent look app-wide (squared, hover/focus border).
   The chevron gutter is a VARIABLE, not a hardcoded padding-right: compact selects
   used to restate `padding: 0 6px` and silently delete the gutter, dropping the
   chevron on top of the text. They now set --sel-gut / --sel-chev instead. */
select {
  --sel-gut: 30px; --sel-chev: 11px;
  cursor: pointer; appearance: none; -webkit-appearance: none;
  padding-right: var(--sel-gut);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23a1a1aa' stroke-width='1.6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--sel-chev) center;
}
}  /* end @layer base */
@layer components {
select:hover { border-color: var(--border-strong); }
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--ring); box-shadow: 0 0 0 1px var(--ring); }
/* Compact selects: a shorter left inset and a tighter chevron gutter. They only
   ever set padding-LEFT — the gutter stays owned by --sel-gut. */
.dash-sort, .dash-filter, .dash-group-sel, .dash-group-new, .dash-unwrap-group,
.session-select, .picker-group, .rail-controls select, #coin-select,
.loop-dest-sel, .loop-stop-dir, .loop-stop-asset {
  --sel-gut: 24px; --sel-chev: 8px; padding-left: 8px;
}
}  /* end @layer components */
@layer base {
input::placeholder { color: var(--muted-2); }
}  /* end @layer base */
@layer components {
input.mono, .mono { font-family: var(--mono); }
input[type="checkbox"], input[type="radio"] { height: auto; width: auto; padding: 0; accent-color: var(--foreground); }
/* A range input is a track, not a box: strip the field chrome the base rule gives
   every input (a 34px bordered surface drawn around a slider). */
input[type="range"] {
  height: 24px; padding: 0; background: transparent; border: none; box-shadow: none;
}
input[type="range"]:focus { border: none; box-shadow: none; }

/* ---- Numeric inputs ----
   A field holding "300" must not be as wide as one holding an 0x address. Every
   numeric control gets a rung off the --num-w ladder, right-aligned tabular
   digits, and the numeric face. Text/address inputs stay flexible (width: 100%).
   Matched three ways so it works without touching the JS builders:
     • input[type=number]      — the drawer's Swap/Advanced fields
     • input[inputmode=...]    — scNum() sets .inputMode = "decimal"
     • .input-num              — opt-in for type=text numeric fields in the markup */
input[type="number"],
input[inputmode="decimal"], input[inputmode="numeric"],
.input-num {
  width: var(--num-w-md); flex: 0 0 auto;
  text-align: right; font-family: var(--num); font-variant-numeric: tabular-nums;
}
/* Native steppers overlap right-aligned digits on hover; the field is still
   keyboard-steppable (↑/↓), it just stops drawing the mouse spinner. */
input[type="number"] { appearance: textfield; -moz-appearance: textfield; }
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
/* the two off-ladder rungs, plus the amount fields that hold real money */
.input-num.wide, input.sq-amount, input#qt-amount { width: var(--num-w-lg); }
.input-num.narrow { width: var(--num-w-sm); }

/* ---- Buttons ---- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-1);
  background: var(--control-bg); color: var(--foreground);
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 0 12px; height: var(--control-h); font-size: var(--body-size); font-weight: 500; cursor: pointer;
  white-space: nowrap; transition: background .12s, border-color .12s, color .12s;
}
.btn:hover { background: var(--control-bg-hover); border-color: var(--border-strong); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
/* .small and .tiny are the SAME rung — the compact one. They differ in inset,
   type size and ink, which is what actually separates a secondary action from a
   tertiary one; a 2px height difference only ever read as a misalignment. */
.btn.small { height: var(--control-h-sm); padding: 0 10px; font-size: var(--body-size); }
.btn.tiny { height: var(--control-h-sm); padding: 0 8px; font-size: var(--body-size); color: var(--muted); }
.btn.primary { background: var(--primary); border-color: var(--primary); color: var(--primary-fg); font-weight: 600; }
.btn.primary:hover { background: #e6e6e6; border-color: #e6e6e6; }
/* Secondary action beside a .primary (Cancel, Recover, Generate receivers…).
   It was used all over the app with no rule at all, so it rendered identical to
   the default button and the two read as equal weight. */
.btn.ghost { background: transparent; color: var(--muted); }
.btn.ghost:hover { background: var(--surface); color: var(--foreground); border-color: var(--border-strong); }
.btn.danger { background: transparent; border-color: #7f1d1d; color: #f87171; font-weight: 600; }
.btn.danger:hover { background: var(--red); border-color: var(--red); color: #fff; }
.btn.copy { color: var(--muted); }
.copy-v { display: inline-flex; align-items: center; gap: var(--sp-1); justify-content: flex-end; }

/* ---- Wallets (legacy rotation rows) ---- */
.wallet-block { margin-top: 16px; }
.wallet-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.wallet-list { display: flex; flex-direction: column; gap: var(--sp-3); }
.wallet-row { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 12px; }
.wallet-row-head { display: flex; gap: var(--sp-2); align-items: center; margin-bottom: 8px; }
.wallet-row-head .row-label { flex: 1; font-size: var(--cap-size); font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; }
.checkbox-field { flex-direction: column; }
.checkbox-field input[type="checkbox"] { width: auto; align-self: flex-start; }
.wallet-fields { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-3); }

/* ---- Run actions ---- */
.run-actions {
  display: flex; gap: var(--sp-3); align-items: center; flex-wrap: wrap;
  margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border);
}
.run-actions .btn.danger { margin-left: auto; }
/* An action row that already sits under a divider (a fieldset's own rule) drops
   the rule + the extra lead-in. Replaces per-element inline styles in the markup. */
.run-actions.flush { margin-top: 10px; padding-top: 0; border-top: none; }

/* ---- Status / error ---- */
.status {
  display: flex; align-items: center; gap: var(--sp-3);
  background: color-mix(in srgb, var(--blue) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--blue) 35%, transparent);
  color: var(--blue-fg);
  border-radius: var(--radius); padding: 10px 12px; margin-bottom: 12px; font-size: var(--body-size);
}
/* The hidden attribute must win over `display:flex` above — otherwise
   clearBusy() / showStatusDone()'s auto-hide (which set `.hidden = true`)
   never actually hide the banner, so a status note sits there permanently. */
.status[hidden], .error-box[hidden] { display: none !important; }
.status::before {
  content: ""; width: 13px; height: 13px; border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--blue-fg) 35%, transparent); border-top-color: var(--blue-fg);
  animation: spin .8s linear infinite; flex: 0 0 auto;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* Completed state: green with a static check instead of the spinner. */
.status.done {
  background: color-mix(in srgb, var(--green) 10%, transparent);
  border-color: color-mix(in srgb, var(--green) 35%, transparent);
  color: #86efac;
}
.status.done::before {
  content: "✓"; width: auto; height: auto; border: none; border-radius: 0;
  animation: none; color: var(--green); font-weight: 700; font-size: var(--body-size);
}

.error-box {
  background: color-mix(in srgb, var(--red) 12%, transparent);
  border: 1px solid var(--red); color: #fca5a5;
  border-radius: var(--radius); padding: 12px; margin-bottom: 12px;
  white-space: pre-wrap; font-family: var(--mono); font-size: var(--body-size);
}

/* The global status/error strip lives inside .sticky-nav so it stays visible
   from any scroll position; cap a long error so it can't swallow the page. */
.sticky-nav .status, .sticky-nav .error-box { margin-bottom: 10px; }
.sticky-nav .error-box { max-height: 30vh; overflow-y: auto; }

/* Button-local busy: the clicked run button carries its own inline spinner
   (+ a label swap done in JS) for the duration of ITS operation. */
.btn.loading::before {
  content: ""; width: 11px; height: 11px; border-radius: 50%;
  border: 2px solid color-mix(in srgb, currentColor 35%, transparent); border-top-color: currentColor;
  animation: spin .8s linear infinite; flex: 0 0 auto;
}
.btn.loading:disabled { opacity: .8; }

/* ---- Result ---- */
.result-header { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; margin-bottom: 12px; }
.mode-badge { padding: 3px 10px; border-radius: var(--radius); font-size: var(--cap-size); font-weight: 600; text-transform: uppercase; letter-spacing: .08em; }
.mode-badge.dry { background: color-mix(in srgb, var(--blue) 18%, transparent); color: #93c5fd; }
.mode-badge.exec { background: color-mix(in srgb, var(--red) 20%, transparent); color: #fca5a5; }
.hchip { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 3px 8px; font-size: var(--body-size); color: var(--muted); }

.addr-row { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-bottom: 14px; }
.addr-chip { display: inline-flex; flex-direction: column; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 5px 10px; }
.addr-k { font-size: var(--cap-size); font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; }
.addr-v { font-size: var(--body-size); font-family: var(--mono); }

.empty { color: var(--muted); padding: 18px; text-align: center; font-style: italic; }

.step-cards { display: flex; flex-direction: column; gap: var(--sp-3); }
.step-card {
  background: var(--surface); border: 1px solid var(--border);
  border-left: 3px solid var(--border); border-radius: var(--radius); padding: 11px 13px;
}
.step-card.action-approve { border-left-color: var(--approve); }
.step-card.action-sell { border-left-color: var(--sell); }
.step-card.action-buy { border-left-color: var(--buy); }
.step-head { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; margin-bottom: 8px; }
.step-idx { color: var(--muted); font-family: var(--mono); font-size: var(--body-size); }
.action-tag { font-size: var(--eyebrow-size); font-weight: var(--eyebrow-weight); letter-spacing: var(--eyebrow-track); padding: 2px 7px; border-radius: var(--radius); background: var(--surface-2); }
.action-approve .action-tag { background: color-mix(in srgb, var(--violet) 22%, transparent); color: var(--approve); }
.action-sell .action-tag { background: color-mix(in srgb, var(--orange) 22%, transparent); color: var(--sell); }
.action-buy .action-tag { background: color-mix(in srgb, var(--green) 22%, transparent); color: var(--buy); }
.step-wallet { font-weight: 600; font-size: var(--body-size); }
.step-addr { color: var(--muted); font-size: var(--body-size); margin-left: auto; font-family: var(--mono); }
.step-body { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: var(--sp-1) 14px; }
.kv { display: flex; justify-content: space-between; gap: var(--sp-2); padding: 3px 0; border-bottom: 1px dotted var(--border); min-width: 0; }
.kv .k { color: var(--muted); font-size: var(--body-size); }
.kv .v { font-size: var(--body-size); text-align: right; word-break: break-all; font-family: var(--mono); }

.raw { margin-top: 14px; }
.raw summary, details > summary { cursor: pointer; color: var(--muted); font-size: var(--body-size); }
.raw-json {
  background: var(--background); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 10px; overflow-x: auto; font-family: var(--mono); font-size: var(--cap-size); margin-top: 8px;
  max-height: 400px; overflow-y: auto;
}

/* ---- Vault panel ---- */
.vault-bar { display: flex; gap: var(--sp-2); margin-bottom: 12px; flex-wrap: wrap; align-items: center; }
.vault-bar-spacer { flex: 1 1 auto; }
.vault-bar .btn.active { background: var(--surface-2); border-color: var(--border-strong); }
.btn-ico { font-size: var(--body-size); line-height: 1; opacity: .85; }
.vault-form-host:empty { display: none; }
.vault-form-host { margin-bottom: 12px; }
.vault-form {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 14px; margin-top: 12px; display: flex; flex-direction: column; gap: var(--sp-3); max-width: 480px;
}
.vault-form.inline { max-width: none; }
.vault-form h3 { margin: 0 0 4px; font-size: var(--cap-size); font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; }
.vault-form .field { flex-direction: column; }
.vault-form .btn { align-self: flex-start; }

/* Vault rows sit in a single hairline-divided list (no per-row card): the chip
   (identity + right-aligned balance) takes the flexible lead, actions trail. */
.vault-list { display: flex; flex-direction: column; gap: 0; border-top: 1px solid var(--border); }
.vault-wallet {
  display: flex; align-items: center; gap: var(--sp-3);
  background: transparent; border: none; border-bottom: 1px solid var(--border); border-radius: 0;
  padding: 9px 4px; flex-wrap: wrap;
}
.vault-wallet:hover { background: var(--surface); }
.vw-title { display: flex; gap: var(--sp-2); align-items: center; flex: 0 1 320px; min-width: 200px; }
.vw-title-input { font-weight: 600; }
.btn.tiny.danger { color: #f87171; border-color: #7f1d1d; background: transparent; }
.btn.tiny.danger:hover { background: var(--red); color: #fff; border-color: var(--red); }

/* ---- Wallet balances ---- */
.bal-line { font-family: var(--mono); font-size: var(--cap-size); color: var(--muted); line-height: 1.35; }
.bal-hint { margin-top: 4px; }
.bal-hint[hidden] { display: none; }
.bal-empty { color: var(--muted-2); font-style: italic; }
.bal-err { color: #f87171; }

/* ---- Legs (disperse/collect) ---- */
.leg-card { border-left: 3px solid var(--blue); }
.leg-card.leg-error { border-left-color: var(--red); }
.leg-error .action-tag { background: color-mix(in srgb, var(--red) 22%, transparent); color: var(--red); }

/* ---- Sequencer ---- */
.seq-note {
  background: color-mix(in srgb, var(--amber) 12%, transparent);
  border: 1px solid var(--amber); color: #fcd34d;
  border-radius: var(--radius); padding: 9px 11px; margin-bottom: 12px; font-size: var(--body-size); font-weight: 600;
}
.seq-sets { display: flex; flex-direction: column; gap: var(--sp-3); }
.seq-set { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 14px; }
/* Rows inside a set are the inner level: drop their card border and separate them
   with hairline dividers + spacing instead of nesting a bordered card in a card. */
.seq-rows.wallet-list { gap: 0; }
.seq-set .seq-row.wallet-row {
  background: transparent; border: none; border-bottom: 1px solid var(--border);
  border-radius: 0; padding: 10px 0;
}
.seq-set .seq-rows.wallet-list > .seq-row:last-child { border-bottom: none; padding-bottom: 2px; }
.seq-set-head { display: flex; gap: var(--sp-3); align-items: flex-end; flex-wrap: wrap; margin-bottom: 12px; }
.seq-set-label { font-size: var(--cap-size); font-weight: 600; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); }
.seq-set-actions { display: flex; gap: var(--sp-1); margin-left: auto; }
.seq-rows-head { margin-top: 4px; margin-bottom: 8px; }
.seq-rows { gap: var(--sp-2); }
.seq-row { background: var(--card); }
.seq-row-remove-wrap { justify-content: flex-end; }
.seq-row-remove-wrap .btn { align-self: flex-start; margin-top: 2px; }
.trade-fields {
  grid-template-columns: minmax(0,.6fr) minmax(0,2fr) minmax(0,.9fr) minmax(0,1.1fr) minmax(0,.9fr) auto;
  align-items: end;
  column-gap: var(--sp-3);
}
@media (max-width: 720px) { .trade-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.seq-trade-row .sq-side { font-weight: 600; }

/* Single-line wallet chip standing in for a FIELD in a sequencer trade row. It
   is the one variant on the DEFAULT rung rather than the compact one, because
   here the chip is a column of the form (Delay · Wallet · Side · Amount · Unit)
   and must share the 34px edge of the inputs either side of it. Still a ladder
   rung and still the shared inset — not a third height. */
.seq-trade-row .row-wallet-chip {
  flex-wrap: nowrap;
  height: var(--control-h);
  padding: 0 var(--chip-pad-x);
  background: var(--surface);
  border: 1px solid var(--border);
}
.seq-trade-row .row-wallet-chip .wallet-chip-id { flex: 0 1 auto; }
.seq-trade-row .row-wallet-chip .wallet-chip-addr { display: none; }
.seq-trade-row .row-wallet-chip .wc-copy { display: none; }
.seq-trade-row .row-wallet-chip .wallet-chip-bal {
  flex: 1 1 auto; flex-basis: auto; margin-left: auto; padding-left: 10px;
  text-align: right; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Wallets are added by dragging from the rail — an empty set body reads as a
   drop zone; the whole set (or the empty sets area) highlights on drag-over. */
.seq-rows:empty {
  min-height: 46px; border: 1px dashed var(--border-strong); border-radius: var(--radius);
  display: flex; align-items: center; justify-content: center;
}
.seq-rows:empty::before {
  content: "Drag wallets from the rail to add rows"; color: var(--muted-2); font-size: var(--body-size);
}
.seq-rows-hint { margin: 0 0 0 auto; font-size: var(--cap-size); color: var(--muted-2); }
.seq-set.drop-target-hover { outline: 1px dashed var(--ring); outline-offset: -1px; }
.seq-set.drop-target-hover .seq-rows { border-color: var(--ring); }
#seq-sets.drop-target-hover { outline: 1px dashed var(--ring); outline-offset: 3px; border-radius: var(--radius); }
.seq-head-actions { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.seq-head-actions .btn { height: var(--control-h-sm); }
.seq-progress {
  background: color-mix(in srgb, var(--blue) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--blue) 35%, transparent);
  color: var(--blue-fg);
  border-radius: var(--radius); padding: 9px 12px; margin-bottom: 12px; font-size: var(--body-size); font-weight: 600;
}
.seq-set-result { border: 1px solid var(--border); border-radius: var(--radius); padding: 13px; margin-bottom: 12px; }
.seq-set-result-head { display: flex; gap: var(--sp-2); align-items: center; margin-bottom: 10px; }
.seq-set-tag { font-size: var(--eyebrow-size); font-weight: var(--eyebrow-weight); letter-spacing: var(--eyebrow-track); padding: 3px 8px; border-radius: var(--radius); background: var(--surface-2); color: var(--foreground); }
.seq-step-tag { font-size: var(--cap-size); font-weight: 600; padding: 3px 8px; border-radius: var(--radius); background: transparent; border: 1px solid var(--border); color: var(--muted); }

/* ---- Data table core (the Flow list table reuses .dash-table-wrap/.dash-table;
   the old Gotham dashboard page that also used it is gone) ---- */
.dash-table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius); }
.dash-table { width: 100%; border-collapse: collapse; font-size: var(--body-size); }
.dash-table th, .dash-table td { padding: 9px 12px; text-align: left; border-bottom: 1px solid var(--border); white-space: nowrap; }
.dash-table tbody tr:last-child td { border-bottom: none; }
.dash-table th { color: var(--muted); text-transform: uppercase; letter-spacing: .08em; font-size: var(--cap-size); font-weight: 600; background: var(--surface); }
.dash-table tbody tr:hover { background: var(--surface); }
.dash-table td { vertical-align: middle; }

/* ---- Unwrap WETH -> ETH tool ---- */
.dash-unwrap { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 12px; margin-bottom: 12px; }
.dash-unwrap-top { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2); margin-bottom: 10px; }
.dash-unwrap-label { font-size: var(--cap-size); text-transform: uppercase; letter-spacing: .08em; color: var(--muted); font-weight: 600; }
.dash-unwrap-group { width: auto; height: var(--control-h-sm); font-size: var(--body-size); }
.dash-unwrap-count { font-size: var(--cap-size); color: var(--muted); margin-left: auto; }
.dash-unwrap-actions { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-3); margin-top: 10px; }
.dash-unwrap-hint { margin: 0; flex-basis: 100%; }
.unwrap-wallets {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: var(--sp-0) 12px; max-height: 220px; overflow-y: auto;
  border: 1px solid var(--border); border-radius: var(--radius); padding: 6px 8px;
}
.unwrap-empty { color: var(--muted); font-style: italic; font-size: var(--body-size); padding: 6px 0; }
.unwrap-wallet { display: flex; align-items: center; gap: var(--sp-2); padding: 5px 4px; font-size: var(--body-size); cursor: pointer; min-width: 0; border-radius: var(--radius); }
.unwrap-wallet:hover { background: var(--surface-2); }
.unwrap-wallet input { width: auto; flex: 0 0 auto; }
.unwrap-wallet-title { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.unwrap-wallet-addr { color: var(--muted); flex: 0 0 auto; font-family: var(--mono); }
.unwrap-wallet-weth { margin-left: auto; color: var(--muted); font-family: var(--mono); flex: 0 0 auto; }
.unwrap-wallet-weth.has-weth { color: var(--amber); }
.unwrap-leg { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 9px 11px; margin-bottom: 8px; }
.unwrap-leg-head { font-weight: 600; margin-bottom: 4px; font-family: var(--mono); }
.unwrap-leg-line { font-size: var(--body-size); }
.unwrap-leg-line.muted { color: var(--muted); }
.unwrap-leg-err { color: #f87171; font-size: var(--body-size); font-family: var(--mono); }

/* ---- Header context bits (de-boxed: borderless text, thin dividers) ---- */
.ctx-label {
  color: var(--muted-2); text-transform: uppercase; letter-spacing: var(--eyebrow-track);
  font-size: var(--eyebrow-size); font-weight: var(--eyebrow-weight);
}
.ctx-value {
  color: var(--foreground); font-weight: 600; font-size: var(--body-size);
  max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Vault group = STATE (borderless pill) + ACTION (bordered button). They are two
   different things, so they never run together as one "Locked Unlock" string:
   the pill stays flat text, the button carries header-control chrome, and
   #ctx-vault sets a wider gap than the inside of any other group. */
.ctx-pill {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  height: var(--ctx-ctl-h); padding: 0;
  font-size: var(--body-size); font-weight: 600; border: none; background: transparent;
}
.ctx-pill .ctx-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--muted-2); flex: 0 0 auto; }
.ctx-pill.locked .ctx-dot { background: var(--red); }
.ctx-pill.unlocked .ctx-dot { background: var(--green); }
.shell-top #ctx-vault { gap: var(--sp-3); }
#ctx-vault-btn {
  height: var(--ctx-ctl-h); padding: 0 var(--ctx-ctl-pad);
  border: 1px solid var(--border); background: transparent;
  color: var(--muted); font-size: var(--body-size); font-weight: 600;
}
#ctx-vault-btn:hover {
  background: var(--surface-2); border-color: var(--border-strong);
  color: var(--foreground); text-decoration: none;
}
#coin-select {
  height: var(--ctx-ctl-h);
  font-size: var(--body-size); font-weight: 600; width: auto; max-width: 180px;
}
/* Compact "+" add-a-coin button next to the coin select (hosted only) — square,
   same height as every other header control. */
.ctx-addbtn {
  width: var(--ctx-ctl-h); height: var(--ctx-ctl-h); padding: 0; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--title-size); font-weight: 600; line-height: 1; color: var(--muted);
}
.ctx-addbtn:hover { color: var(--foreground); }
.ctx-addbtn[hidden] { display: none; }
/* Totals: two independent, LABELLED figures with a hairline between them and the
   same --ctx-gap either side as a .ctx-vr — never "◆ — — USDC" run together.
   The ETH diamond glyph is gone: at 12.5px its mask rendered as a smudge, so the
   unit is now a text eyebrow (ETH · <TICKER>) sitting beside its figure, exactly
   like the .act-cap / .rail-cap captions used everywhere else. Figures are
   tabular in a fixed-height box, so "—" occupies the same room as a value. */
.ctx-total {
  display: inline-flex; align-items: baseline; gap: var(--sp-1); height: 22px;
  white-space: nowrap;
}
.ctx-total-cap {
  font-family: inherit; font-size: var(--eyebrow-cap-size); font-weight: var(--eyebrow-weight);
  text-transform: uppercase; letter-spacing: .1em; color: var(--muted-2);
  max-width: 8ch; overflow: hidden; text-overflow: ellipsis;
}
.ctx-total-fig {
  font-family: var(--num); font-variant-numeric: tabular-nums;
  font-weight: 600; font-size: var(--body-size); color: var(--foreground);
}
/* ONE separator idiom in this bar: a .ctx-vr marks a GROUP boundary, and inside
   a group the gap separates. This rule used to add a THIRD kind of line (a
   hairline between the two totals) which landed ~15px from the account pill's
   own button border — reading as a double rule for no reason. Gap only. */
.ctx-totals > .ctx-total + .ctx-total { padding-left: var(--ctx-gap); }

/* ---- Network indicator: chain-colored dot + chain NAME (no "Network" word) ---- */
.ctx-net { gap: var(--sp-2); }
.ctx-net-dot {
  width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto;
  background: var(--muted-2);
}

/* ---- Account / credit pill (hosted only) ---- */
.ctx-acct { position: relative; display: inline-flex; align-items: center; flex: 0 0 auto; }
.ctx-acct[hidden] { display: none; }
.ctx-acct-pill {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  height: var(--ctx-ctl-h); padding: 0 var(--ctx-ctl-pad); cursor: pointer;
  background: transparent; color: var(--foreground);
  border: 1px solid var(--border); border-radius: var(--radius);
  font-size: var(--body-size); font-weight: 600; font-family: var(--num);
}
.ctx-acct-pill:hover { background: var(--surface-2); border-color: var(--border-strong); }
.ctx-acct-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--muted-2); flex: 0 0 auto; }
.ctx-acct-dot.on { background: var(--green); }
.ctx-acct-credit { color: var(--foreground); }

.ctx-acct-pop {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 70;
  width: 300px; padding: var(--pad-card);
  background: var(--float); border: 1px solid var(--border-strong); border-radius: var(--radius);
  box-shadow: var(--shadow-pop);
}
.ctx-acct-pop[hidden] { display: none; }
.ctx-acct-ph {
  font-size: var(--eyebrow-size); text-transform: uppercase; letter-spacing: var(--eyebrow-track);
  color: var(--muted-2); font-weight: var(--eyebrow-weight); margin-bottom: 10px;
}
.ctx-acct-row {
  display: flex; justify-content: space-between; gap: var(--sp-3); align-items: baseline;
  padding: 6px 0; border-bottom: 1px solid var(--border); font-size: var(--body-size);
}
.ctx-acct-row span { color: var(--muted); }
.ctx-acct-row b {
  color: var(--foreground); font-weight: 600; font-family: var(--num);
  word-break: break-all; text-align: right;
}
.ctx-acct-num { font-variant-numeric: tabular-nums; }
.ctx-acct-btns { display: flex; gap: var(--sp-2); margin-top: 12px; }
.ctx-acct-btns .btn { flex: 1 1 0; }
.ctx-acct-msg { font-size: var(--body-size); color: var(--green); margin-top: 8px; min-height: 15px; }
.ctx-acct-msg.err { color: var(--red); }

/* ---- Config drawer (right slide-over) ---- */
#drawer-backdrop {
  position: fixed; inset: 0; z-index: 60;
  background: color-mix(in srgb, var(--background) 70%, transparent);
  opacity: 0; transition: opacity .18s ease;
}
#drawer-backdrop.open { opacity: 1; }
#drawer-backdrop[hidden] { display: none; }
.drawer {
  position: fixed; top: 0; right: 0; z-index: 70;
  width: 420px; max-width: 92vw; height: 100vh;
  background: var(--float); border-left: 1px solid var(--border-strong); border-radius: 0;
  /* ONE shadow declaration. This rule read --shadow-modal and then overwrote it
     three lines later with a literal, so the token was dead here. The literal
     was the CORRECT value — a right-flush drawer must throw its shadow leftward
     — so it is now the named --shadow-drawer tier instead of an inline surprise. */
  box-shadow: var(--shadow-drawer);
  display: flex; flex-direction: column;
  transform: translateX(100%); transition: transform .2s ease;
}
.drawer.open { transform: translateX(0); }
.drawer[hidden] { display: none; }
.drawer-header {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: 14px 16px; border-bottom: 1px solid var(--border); flex: 0 0 auto;
}
.drawer-header h2 { margin: 0; font-size: var(--title-size); font-weight: 600; line-height: var(--title-line); }
.drawer-header-actions { display: flex; align-items: center; gap: var(--sp-2); }
/* A header action cluster is a TOOLBAR, so it takes the compact rung — including
   any plain .btn dropped into it, which would otherwise arrive at 34px. */
.drawer-header-actions .btn { height: var(--control-h-sm); }
.drawer-body { padding: 16px; overflow-y: auto; flex: 1 1 auto; }

/* ---- Reusable wallet chip: THE wallet identity component ----
   stacked title over mono address · balance right-aligned · copy on the far
   right. Same block in the vault list, dashboard table, pickers, pick-buttons,
   row chips, unwrap and flow. It is the TWO-LINE density: title + address, the
   whole of a wallet's identity and nothing else. */
.wallet-chip { display: inline-flex; align-items: center; gap: var(--chip-gap); min-width: 0; max-width: 100%; flex-wrap: nowrap; }
.wallet-chip-id { display: flex; flex-direction: column; gap: var(--sp-0); min-width: 0; }
.wallet-chip-title { line-height: 1.25; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wallet-chip-addr { color: var(--muted); line-height: 1.25; font-family: var(--mono); }
/* Same balance treatment as every other chip: right-aligned tabular figures. */
.wallet-chip-bal {
  margin-left: auto; padding-left: 12px; flex: 0 0 auto;
  color: var(--muted); white-space: nowrap;
  font-family: var(--num); font-variant-numeric: tabular-nums;
}

/* vault row + unwrap row now lead with a wallet-chip; keep the chip taking the
   flexible lead so the remaining controls sit to the right. */
.vault-wallet > .wallet-chip { flex: 1; min-width: 200px; }
.unwrap-wallet > .wallet-chip { flex: 1; min-width: 0; }
}  /* end @layer components */

@layer base {
/* ---- Scrollbars (subtle) ---- */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: var(--border); border: 2px solid var(--background); }
::-webkit-scrollbar-thumb:hover { background: var(--border-strong); }
::-webkit-scrollbar-track { background: transparent; }
}  /* end @layer base */

@layer components {
/* ---- Wallet multi-select picker ---- */
.picked-hidden { display: none !important; }
.picker-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.6); display: flex; align-items: center; justify-content: center; z-index: 200; }
.picker { background: var(--float); border: 1px solid var(--border-strong); border-radius: var(--radius); width: min(480px, 92vw); max-height: 82vh; display: flex; flex-direction: column; padding: var(--pad-modal); box-shadow: var(--shadow-modal); }
.picker-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.picker-title { font-weight: 600; font-size: var(--body-size); }
.picker-note { color: var(--muted); font-size: var(--body-size); margin-bottom: 8px; }
.picker-controls { display: flex; gap: var(--sp-2); margin-bottom: 10px; flex-wrap: wrap; }
.picker-search { flex: 1; min-width: 140px; }
.picker-group { width: auto; }
.picker-list { overflow-y: auto; border: 1px solid var(--border); border-radius: var(--radius); padding: 4px; flex: 1; }
.picker-item { display: flex; align-items: center; gap: var(--sp-2); padding: 6px; cursor: pointer; border-radius: var(--radius); }
.picker-item:hover { background: var(--surface-2); }
.picker-item input { width: auto; flex: 0 0 auto; }
.picker-item .wallet-chip { flex: 1; }
.picker-foot { display: flex; align-items: center; gap: var(--sp-2); margin-top: 10px; }
.picker-count { color: var(--muted); font-size: var(--body-size); margin-right: auto; }

.wc-copy { flex: 0 0 auto; }

/* ---- Flow (transaction graph) ---- */
.flow-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); flex-wrap: wrap; }
.flow-head h2 { margin: 0; }
.flow-head-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; }

/* Cytoscape mounts directly into #flow-graph, so it must be a fixed-size,
   padding-free box. The legend sits in a sibling row below (see JS). */
#flow-graph.flow-graph {
  height: 520px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: #060606;
  padding: 0;
  margin-bottom: 12px;
  position: relative;
  overflow: hidden;
}
.flow-empty {
  color: var(--muted); font-size: var(--body-size); text-align: center;
  padding: 40px 12px;
  /* Center the empty/error message inside the fixed-height graph box. */
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
}

.flow-node-circle { stroke: var(--background); stroke-width: 2; }
.flow-node-label {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: var(--cap-size); fill: var(--foreground); paint-order: stroke;
  stroke: var(--background); stroke-width: 3px; stroke-linejoin: round;
}
.flow-edge { opacity: 0.9; }
.flow-count-label {
  font-family: var(--mono); font-size: var(--cap-size); paint-order: stroke;
  stroke: var(--background); stroke-width: 3px; stroke-linejoin: round;
}

.flow-legend {
  display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap;
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border);
  font-size: var(--body-size); color: var(--muted);
}
.flow-legend-key { display: inline-flex; align-items: center; gap: var(--sp-1); }
.flow-legend-swatch { width: 12px; height: 12px; border-radius: 2px; display: inline-block; }
.flow-legend-count { margin-left: auto; font-family: var(--mono); color: var(--muted-2); }

/* ---- Flow list table (mirrors .dash-table) ---- */
.flow-table td { vertical-align: middle; }
/* Rows are hover-targets that highlight the matching edge in the graph. */
.flow-table tbody tr { cursor: pointer; }
.flow-table tbody tr:hover { background: var(--surface-2); }
.flow-table .wallet-chip { background: transparent; padding: 0; border: none; }
.flow-endpoint { display: inline-flex; align-items: center; }
.flow-arrow-cell { text-align: center; color: var(--muted); }
.flow-uniswap { color: var(--muted); font-weight: 600; }
.flow-tx-cell { display: flex; align-items: center; gap: var(--sp-1); }
.flow-time { color: var(--muted); font-size: var(--cap-size); }

.flow-kind {
  display: inline-block; padding: 2px 8px; border-radius: var(--radius);
  font-size: var(--cap-size); font-weight: var(--eyebrow-weight); text-transform: uppercase;
  letter-spacing: var(--eyebrow-track); border: 1px solid transparent;
}
.flow-kind-sell { color: var(--orange); border-color: var(--orange); background: color-mix(in srgb, var(--orange) 14%, transparent); }
.flow-kind-buy { color: var(--green); border-color: var(--green); background: color-mix(in srgb, var(--green) 14%, transparent); }
.flow-kind-transfer { color: var(--blue); border-color: var(--blue); background: color-mix(in srgb, var(--blue) 14%, transparent); }
.flow-kind-unwrap { color: var(--violet); border-color: var(--violet); background: color-mix(in srgb, var(--violet) 14%, transparent); }

/* ---- Quick trade panel ---- */
.quick-trade legend { color: var(--foreground); }
.quick-trade-fields { grid-template-columns: minmax(0,1.6fr) minmax(0,.9fr) minmax(0,1.2fr) minmax(0,.9fr); align-items: end; }
@media (max-width: 720px) { .quick-trade-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.quick-trade .run-actions { margin-top: 14px; padding-top: 12px; }

/* Sequencer per-set loop controls */
.seq-loop-toggle {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  font-size: var(--body-size); color: var(--muted); font-weight: 500; cursor: pointer;
  user-select: none;
}
.seq-loop-toggle input { width: auto; height: auto; }
.seq-loop-toggle:hover { color: var(--foreground); }
.seq-loop-field[hidden] { display: none; }
.seq-loop-controls { display: flex; align-items: center; gap: var(--sp-1); flex-wrap: wrap; }
.seq-loop-controls input { width: var(--num-w-sm); }
.seq-loop-controls select { width: auto; }
.seq-loop-x { color: var(--muted); font-size: var(--body-size); }

/* Per-set run bar (Dry run / Execute this one set) */
.seq-set-run { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }
.seq-set-run-label { margin: 0; }
.seq-set-run .btn { margin-left: 0; }

/* Collapsible unwrap tool */
.dash-unwrap.collapsed #unwrap-wallets,
.dash-unwrap.collapsed .dash-unwrap-actions,
.dash-unwrap.collapsed .dash-unwrap-hint { display: none; }
.dash-unwrap .dash-unwrap-label { cursor: pointer; }

.dash-unwrap.collapsed .dash-unwrap-top > *:not(#unwrap-toggle):not(.dash-unwrap-label) { display: none; }

/* ---- Transfer tab direction toggle (segmented) ---- */
.transfer-direction-bar { display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap; margin-bottom: 14px; }
/* "Clear saved form" sits out of the way on the right of the direction bar */
.transfer-direction-bar .clear-saved { margin-left: auto; }
.seg {
  display: inline-flex; gap: var(--sp-0);
  background: var(--surface); border: 1px solid var(--border);
  padding: 3px; border-radius: var(--radius);
}
.seg-btn {
  background: transparent; color: var(--muted);
  border: none; border-radius: var(--radius);
  padding: 6px 13px; font-size: var(--body-size); font-weight: 500; cursor: pointer;
  transition: color .12s, background .12s;
}
.seg-btn:hover { color: var(--foreground); }
.seg-btn.active { background: var(--card); color: var(--foreground); font-weight: 600; box-shadow: inset 0 0 0 1px var(--border); }

/* Inline toggle used in place of a 2-option dropdown (makeToggle): fills its
   field/row cell, matches input height, buttons split the width evenly. */
.seg-field { display: flex; width: 100%; height: var(--control-h); }
.seg-field .seg-btn { flex: 1 1 0; min-width: 0; padding: 0 8px; font-size: var(--body-size);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.seg-field.disabled { opacity: .5; pointer-events: none; }
/* In a wallet-row head (Disperse mode toggle) it sits next to the Remove button. */
.wallet-row-head .seg-field { width: auto; flex: 1; max-width: 240px; }

/* ---- Unified transfer desk (drag-driven From / To) ---- */
.transfer-desk-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.transfer-desk-head h2 { margin: 0; }
.transfer-desk-head .clear-saved { flex: 0 0 auto; }
.tf-boxes {
  display: grid; grid-template-columns: 1fr auto 1fr; gap: var(--sp-4);
  align-items: stretch; margin-top: 14px;
}
.tf-box {
  display: flex; flex-direction: column; gap: var(--sp-2);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 12px; min-width: 0;
}
.tf-box-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); }
.tf-box-title { font-size: var(--cap-size); color: var(--muted); text-transform: uppercase; letter-spacing: .08em; font-weight: 600; }
.tf-box-title em { font-style: normal; color: var(--muted-2); font-weight: 500; text-transform: none; letter-spacing: 0; }
.tf-box-actions { display: flex; gap: var(--sp-1); }
/* A POPULATED zone is exactly as tall as its chips — it used to reserve 64px and
   centre 1–2 chips in the middle of it, which is where the "void" came from. The
   comfortable target box is kept for the EMPTY state only. */
.tf-zone {
  flex: 0 0 auto; min-height: 0;
  display: flex; flex-direction: column; gap: var(--sp-2); justify-content: flex-start;
}
.tf-zone:has(.dz-empty) { min-height: 64px; justify-content: center; }
/* Inside a drop zone, chips stack one per row so their (right-aligned) balance
   forms a clean column instead of wrapping inline. */
.tf-zone .dz-chips { flex-direction: column; gap: var(--sp-1); align-items: stretch; }
.tf-zone .dz-chip { width: 100%; }
.tf-arrow { display: flex; align-items: center; justify-content: center; font-size: 22px; color: var(--muted); min-width: 24px; }
.dz-chip-addr { border-style: dashed; }
.dz-chip-addr .dz-chip-name { font-family: var(--mono); font-weight: 500; }
.dz-chip-bal {
  flex: 0 0 auto; margin-left: auto; padding-left: 10px; text-align: right;
  color: var(--muted); font-family: var(--num); font-variant-numeric: tabular-nums;
}

/* Controls sit on one baseline and each field is as wide as its CONTROL, not a
   third of the desk — an "Amount for all" box holding "1.5" was 300px wide. */
.tf-controls {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--sp-3) 16px;
  margin-top: 14px;
}
.tf-controls .field { flex: 0 0 auto; }
.tf-controls select { width: auto; min-width: 150px; }
.tf-overrides { margin-top: 14px; display: flex; flex-direction: column; gap: var(--sp-2); }
.tf-overrides:empty { display: none; }
.tf-overrides-head { font-size: var(--cap-size); font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; }
.tf-override-row {
  display: flex; align-items: center; gap: var(--sp-3);
  background: transparent; border: none; border-bottom: 1px solid var(--border);
  border-radius: 0; padding: 8px 2px;
}
.tf-override-row:last-child { border-bottom: none; }
.tf-override-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.tf-override-amount {
  width: var(--num-w-lg); flex: 0 0 auto; height: var(--control-h-sm);
  text-align: right; font-family: var(--num); font-variant-numeric: tabular-nums;
}
.tf-override-all { display: flex; align-items: center; gap: var(--sp-1); font-size: var(--body-size); color: var(--muted); flex: 0 0 auto; }
.tf-override-all input { width: auto; }

.tf-total {
  margin-top: 12px; font-size: var(--body-size); font-weight: 600; color: var(--foreground);
  font-variant-numeric: tabular-nums;
}
.tf-total[hidden] { display: none; }

.tf-role-banner {
  margin-top: 14px; padding: 10px 12px; border-radius: var(--radius);
  font-size: var(--body-size); border: 1px solid var(--border); background: var(--surface); color: var(--muted);
}
.tf-role-banner.ok { border-color: color-mix(in srgb, var(--ok) 45%, var(--border)); color: var(--foreground); background: color-mix(in srgb, var(--ok) 10%, transparent); }
.tf-role-banner.bad { border-color: color-mix(in srgb, var(--bad) 45%, var(--border)); color: var(--foreground); background: color-mix(in srgb, var(--bad) 10%, transparent); }

@media (max-width: 720px) {
  .tf-boxes { grid-template-columns: 1fr; }
  .tf-arrow { transform: rotate(90deg); }
}

/* ---- Single-select wallet pick button (popup chip) ---- */
.wallet-pick { display: flex; align-items: center; gap: var(--sp-1); }
.wallet-pick-btn {
  display: flex; flex: 1; align-items: center; gap: var(--sp-2); min-height: var(--control-h);
  padding: 5px 10px; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); cursor: pointer; color: var(--foreground); text-align: left;
}
.wallet-pick-btn .wallet-chip { flex: 1; }
.wallet-pick-btn:hover { border-color: var(--border-strong); }
.wallet-pick-placeholder { color: var(--muted); font-size: var(--body-size); }
.wallet-pick-clear {
  flex: 0 0 auto; min-height: var(--control-h); padding: 0 10px; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius); cursor: pointer;
  color: var(--muted); font-size: var(--body-size); line-height: 1;
}
.wallet-pick-clear:hover { border-color: var(--border-strong); color: var(--foreground); }

/* ---- tx notifications (fixed-size, self-scrolling panel, bottom-right) ---- */
/* ONE contained box: header + an internally-scrolling list. It never grows past
   max-height — N notifications scroll inside instead of climbing the screen. */
#toast-stack {
  position: fixed; right: 16px; bottom: 16px; z-index: 300;
  width: 340px; max-width: calc(100vw - 32px);
  display: flex; flex-direction: column;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: 0 8px 28px rgba(0,0,0,.45); overflow: hidden;
}
#toast-head {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: 6px 10px; border-bottom: 1px solid var(--border);
  background: var(--surface); font-size: var(--cap-size);
}
#toast-count {
  min-width: 18px; padding: 1px 5px; text-align: center;
  background: var(--green); color: #05130b; border-radius: var(--radius);
  font-family: var(--mono); font-weight: 700;
}
#toast-title { color: var(--muted); text-transform: uppercase; letter-spacing: var(--eyebrow-track); }
#toast-clear {
  margin-left: auto; cursor: pointer; background: none; border: 1px solid var(--border);
  border-radius: var(--radius); color: var(--muted); font-size: var(--cap-size); padding: 2px 8px;
}
#toast-clear:hover { border-color: var(--border-strong); color: var(--foreground); }
/* The scroll surface: capped height, newest on top. */
#toast-list { max-height: 40vh; overflow-y: auto; overflow-x: hidden; }
/* Each notification is a single tabular line built for volume. */
.toast {
  border-left: 3px solid var(--green); border-bottom: 1px solid var(--border);
  padding: 5px 10px; font-size: var(--body-size);
  display: flex; align-items: center; gap: var(--sp-2); white-space: nowrap;
}
.toast:last-child { border-bottom: none; }
.toast .toast-dot { flex: 0 0 auto; width: 6px; height: 6px; border-radius: 50%; background: var(--green); }
.toast-label { flex: 0 0 auto; color: var(--muted); overflow: hidden; text-overflow: ellipsis; max-width: 120px; }
.toast-label.toast-msg { flex: 1 1 auto; color: var(--foreground); white-space: normal; max-width: none; }
.toast-hash { flex: 0 0 auto; }
.toast a.toast-hash { color: var(--foreground); text-decoration: none; font-family: var(--mono); }
.toast a.toast-hash:hover { text-decoration: underline; }
.toast .toast-time { flex: 0 0 auto; margin-left: auto; color: var(--muted); font-size: var(--cap-size); font-variant-numeric: tabular-nums; }
.toast .toast-x { flex: 0 0 auto; cursor: pointer; color: var(--muted); background: none; border: none; padding: 0 2px; }
.toast .toast-x:hover { color: var(--foreground); }

/* Compact wallet chip inside narrow rows (Sequencer/Transfer). The balance drops
   onto its own line under the name so a long balance can never squeeze the name
   and address to zero width. */
.row-wallet-chip { padding: 6px 0; min-width: 0; max-width: 100%; overflow: hidden; flex-wrap: wrap; align-items: center; }
.row-wallet-chip .wallet-chip-id { min-width: 0; overflow: hidden; flex: 1 1 auto; }
.row-wallet-chip .wallet-chip-bal {
  /* The wrapped balance starts at the row's left edge, which is where the name
     and the address now start too. It used to be indented 30px to clear a 28px
     avatar — an element the chip never actually rendered. */
  flex-basis: 100%; margin-left: 0; padding-left: 0;
  overflow: hidden; text-overflow: ellipsis;
}
.row-wallet-chip .wc-copy { flex: 0 0 auto; }

/* ---- in-app assistant terminal (floating, bottom-left) ---- */
.assist-hl {
  outline: 2px solid var(--blue);
  box-shadow: 0 0 0 3px rgba(59,130,246,.35);
  border-radius: 2px;
  transition: outline .1s;
}
#assist-toggle {
  position: fixed; left: 16px; bottom: 16px; z-index: 250;
  width: 40px; height: 40px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: #0b0b0b; color: var(--green, #4ade80);
  border: 1px solid var(--border); border-radius: 8px;
  font-family: var(--mono); font-size: var(--title-size); font-weight: 700;
  cursor: pointer; box-shadow: 0 4px 16px rgba(0,0,0,.4);
}
#assist-toggle:hover { border-color: var(--blue); color: var(--foreground); }
#assist-term {
  position: fixed; left: 16px; bottom: 64px; z-index: 250;
  width: 380px; height: 420px; max-width: calc(100vw - 32px); max-height: calc(100vh - 96px);
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--float); color: var(--foreground);   /* was a hardcoded #0b0b0b */
  border: 1px solid var(--border-strong); border-radius: var(--radius);  /* was 10px in a squared app */
  box-shadow: var(--shadow-modal);
  font-family: var(--mono); font-size: var(--body-size); line-height: 1.5;
  resize: both;
}
#assist-term[hidden] { display: none; }
#assist-head {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: 7px 10px; border-bottom: 1px solid var(--border);
  background: color-mix(in srgb, var(--foreground) 5%, transparent);
}
#assist-head .assist-title {
  font-size: var(--cap-size); text-transform: lowercase; letter-spacing: .05em;
  color: var(--muted); font-weight: 600;
}
#assist-close {
  margin-left: auto; background: none; border: none; cursor: pointer;
  color: var(--muted); font-size: var(--body-size); line-height: 1; padding: 2px 4px;
}
#assist-close:hover { color: var(--foreground); }
#assist-out {
  flex: 1; overflow-y: auto; padding: 10px;
  white-space: pre-wrap; word-break: break-word;
}
#assist-out .a-line { margin: 0 0 2px; }
#assist-out .a-cmd { color: var(--foreground); }
#assist-out .a-sys { color: var(--muted); }
#assist-out .a-err { color: var(--red, #f87171); }
#assist-out .a-ok { color: var(--green, #4ade80); }
#assist-out .a-summary { color: var(--blue-fg, #bfdbfe); }
#assist-out .a-step { color: var(--foreground); }
#assist-out .a-prompt { color: var(--muted); }
#assist-inputline {
  display: flex; align-items: center; gap: var(--sp-1);
  padding: 8px 10px; border-top: 1px solid var(--border);
}
#assist-inputline .assist-prompt { color: var(--green, #4ade80); font-weight: 700; }
#assist-input {
  flex: 1; height: auto; min-width: 0;
  background: transparent; border: none; outline: none; box-shadow: none;
  color: var(--foreground); font-family: var(--mono); font-size: var(--body-size);
  padding: 0; caret-color: var(--green, #4ade80);
}
#assist-input::placeholder { color: var(--muted-2); }
#assist-input:disabled { opacity: .6; }
#assist-inputline.blink .assist-prompt { animation: assist-blink 1.1s step-start infinite; }
@keyframes assist-blink { 50% { opacity: .25; } }

/* Sequencer: show the wallet NAME clearly in a row (hide the cramped address) */
.row-wallet-chip .wallet-chip-addr { display: none; }
.row-wallet-chip .wallet-chip-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Sequencer row reorder controls */
.seq-row.has-move { display: flex; align-items: center; gap: var(--sp-2); }
.seq-row.has-move > .seq-row-inner { flex: 1; min-width: 0; }
.seq-row-move { display: flex; flex-direction: column; gap: var(--sp-0); flex: 0 0 auto; }
/* A ▲/▼ pair stacked beside a row. The PAIR is what has to match a control, not
   each half — so each half is half a default rung minus the 2px gap between
   them, off the ladder rather than a guessed 18px. */
.seq-row-move .btn.tiny {
  height: calc((var(--control-h) - 2px) / 2);
  padding: 0 6px; line-height: 1; font-size: var(--cap-size);
}

/* ============================================================
   Activity feed — right rail: live job cards on top, history below.
   Collapses to a thin strip (badge = running jobs); below ~1100px
   it overlays the content instead of squeezing it.
   ============================================================ */
.feed {
  grid-column: 3; grid-row: 2;
  min-height: 0; overflow: hidden;
  display: flex; flex-direction: column;
  background: var(--rail); border-left: 1px solid var(--border);
  z-index: 55;
}
.feed-head {
  display: flex; align-items: center; gap: var(--sp-2); flex: 0 0 auto;
  padding: 10px 12px; border-bottom: 1px solid var(--border);
}
.feed-head h2 { margin: 0; flex: 1; font-size: var(--body-size); font-weight: 600; }
.feed-badge {
  background: color-mix(in srgb, var(--blue) 20%, transparent);
  border: 1px solid color-mix(in srgb, var(--blue) 40%, transparent);
  color: #93c5fd; border-radius: var(--radius);
  padding: 1px 7px; font-size: var(--cap-size); font-weight: 700; font-family: var(--mono);
}
.feed-badge[hidden] { display: none; }
.feed-body { flex: 1 1 auto; overflow-y: auto; padding: 10px 12px; }

/* Collapsed: a thin vertical strip that expands on click. Narrows the grid col.
   (The right feed rail was removed — these grid overrides no longer apply, but
   the collapsed-strip styling below is kept harmless/unused for easy restore.) */
.feed.collapsed .feed-head, .feed.collapsed .feed-body { display: none; }
.feed-strip {
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-3);
  width: 100%; min-height: 160px; padding: 12px 0;
  background: var(--rail); border: none; cursor: pointer; color: var(--muted);
}
.feed-strip:hover { background: var(--surface); color: var(--foreground); }
.feed-strip[hidden] { display: none; }
.feed-strip-label {
  writing-mode: vertical-rl; text-orientation: mixed;
  font-size: var(--cap-size); font-weight: 600; text-transform: uppercase; letter-spacing: .08em;
}

/* ---- Live job cards ---- */
/* ---- Session net-position panel (top of the feed) ---- */
.session-panel {
  border: none; border-bottom: 1px solid var(--border); border-radius: 0;
  background: transparent; padding: 4px 3px 14px; margin-bottom: 12px;
}
.session-head { display: flex; align-items: center; gap: var(--sp-2); margin-bottom: 8px; }
.session-title {
  font-size: var(--eyebrow-size); font-weight: var(--eyebrow-weight); color: var(--muted);
  text-transform: uppercase; letter-spacing: var(--eyebrow-track);
}
.session-select { margin-left: auto; max-width: 150px; height: var(--control-h-sm); font-size: var(--body-size); }
.session-nets { display: flex; gap: var(--sp-2); margin-bottom: 8px; }
.session-net { flex: 1; display: flex; flex-direction: column; gap: var(--sp-0);
  padding: 8px; border-radius: var(--radius); background: var(--bg); border: 1px solid var(--border); }
.session-net-label { font-size: var(--cap-size); color: var(--muted); }
.session-net-val { font-size: var(--fig-inline); font-weight: 600; font-family: var(--num); font-variant-numeric: tabular-nums; color: var(--foreground); }
.session-net.pos .session-net-val { color: var(--ok); }
.session-net.neg .session-net-val { color: var(--bad); }
.session-rows { display: flex; flex-direction: column; gap: var(--sp-0); margin-bottom: 8px; }
.session-row { display: flex; justify-content: space-between; gap: var(--sp-2); font-size: var(--body-size); }
.session-row-k { color: var(--muted); }
.session-row-v { color: var(--foreground); text-align: right; font-family: var(--num); font-variant-numeric: tabular-nums; }
.session-foot { display: flex; align-items: center; gap: var(--sp-2); }
.session-note { font-size: var(--cap-size); color: var(--muted); line-height: 1.3; }
.session-foot .btn { margin-left: auto; flex: 0 0 auto; }

.feed-jobs { display: flex; flex-direction: column; gap: var(--sp-3); }
.feed-jobs:empty { display: none; }
.feed-jobs:empty + .feed-hist-head { margin-top: 0; }
.feed-job {
  background: transparent; border: none;
  border-left: 3px solid var(--blue); border-radius: 0; padding: 10px 11px;
  box-shadow: inset 0 -1px 0 var(--border);
}
.feed-job.job-done { border-left-color: var(--green); }
.feed-job.job-error { border-left-color: var(--red); }
.feed-job-head { display: flex; align-items: center; gap: var(--sp-2); margin-bottom: 6px; }
.feed-job-op { flex: 1; min-width: 0; font-size: var(--body-size); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.feed-job-time { flex: 0 0 auto; color: var(--muted); font-size: var(--cap-size); font-family: var(--mono); font-variant-numeric: tabular-nums; }
.feed-job-x { background: none; border: none; color: var(--muted); cursor: pointer; font-size: var(--body-size); line-height: 1; padding: 0 2px; }
.feed-job-x:hover { color: var(--foreground); }
.feed-job-x[hidden] { display: none; }
.feed-job-legs { display: flex; flex-direction: column; gap: var(--sp-0); margin-bottom: 6px; }
.feed-job-progress {
  padding-top: 6px; border-top: 1px dotted var(--border);
  font-size: var(--body-size); color: var(--muted); font-family: var(--mono);
}

/* ---- Per-leg rows (shared by live cards and expanded history) ---- */
.feed-leg { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; font-size: var(--body-size); }
.feed-leg-ico { width: 14px; text-align: center; flex: 0 0 auto; color: var(--muted-2); }
.feed-leg-ico.spin { display: inline-block; animation: spin 1s linear infinite; }
.feed-leg.st-building .feed-leg-ico { color: var(--blue); }
.feed-leg.st-submitted .feed-leg-ico { color: var(--amber); }
.feed-leg.st-confirmed .feed-leg-ico { color: var(--green); }
.feed-leg.st-failed .feed-leg-ico { color: var(--red); }
.feed-leg-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.feed-tx { flex: 0 0 auto; font-size: var(--cap-size); color: var(--foreground); text-decoration: none; }
a.feed-tx:hover { text-decoration: underline; }
.feed-leg-err {
  flex-basis: 100%; padding-left: 21px; /* align under the label, past the icon */
  color: #f87171; font-size: var(--cap-size); font-family: var(--mono); word-break: break-word;
}

/* ---- History (compact, click a row to expand its legs) ---- */
.feed-hist-head {
  margin: 12px 0 8px; font-size: var(--eyebrow-size); text-transform: uppercase;
  letter-spacing: var(--eyebrow-track); color: var(--muted); font-weight: var(--eyebrow-weight);
}
.feed-history { display: flex; flex-direction: column; gap: 0; }
.feed-hist-item { background: transparent; border: none; border-bottom: 1px solid var(--border); border-radius: 0; }
.feed-hist-row {
  display: flex; align-items: center; gap: var(--sp-2); width: 100%; padding: 7px 9px;
  background: none; border: none; color: var(--foreground);
  font-size: var(--body-size); text-align: left; cursor: pointer;
}
.feed-hist-row:hover { background: var(--surface-2); }
.feed-hist-op { flex: 1; min-width: 0; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.feed-hist-item.st-running .feed-hist-op::after { content: " · running"; color: #93c5fd; font-weight: 500; }
.feed-hist-time { flex: 0 0 auto; color: var(--muted); font-size: var(--cap-size); font-family: var(--mono); font-variant-numeric: tabular-nums; }
.feed-hist-count { flex: 0 0 auto; color: var(--muted); font-size: var(--cap-size); font-variant-numeric: tabular-nums; min-width: 42px; text-align: right; }
.feed-hist-item.st-error .feed-hist-count, .feed-hist-count.has-failed { color: #f87171; }
.feed-hist-legs { display: flex; flex-direction: column; gap: var(--sp-0); padding: 6px 9px 8px; border-top: 1px solid var(--border); }
.feed-hist-legs[hidden] { display: none; }
.feed-empty { color: var(--muted); font-size: var(--body-size); font-style: italic; padding: 4px 0; }

/* One-line note left in the old Results panels after an execute moves to the feed. */
.feed-handoff { color: var(--muted); font-size: var(--body-size); padding: 6px 0; }
.feed-handoff.ok { color: #86efac; }
.feed-handoff.err { color: #f87171; }

/* Plain-message toast (job summaries) — red accent for failures. */
.toast.toast-err { border-left-color: var(--red); }
.toast.toast-err .toast-dot { background: var(--red); }

/* PK exporter modal */
.pk-export { width: min(560px, 94vw); }
.pk-warn { background: color-mix(in srgb, var(--red) 12%, transparent); border: 1px solid var(--red); color: #fca5a5; border-radius: var(--radius); padding: 9px 11px; font-size: var(--body-size); margin-bottom: 10px; }
.pk-pw { display: flex; gap: var(--sp-2); margin-bottom: 8px; }
.pk-pw input { flex: 1; }
.modal-error { color: #f87171; font-size: var(--body-size); margin-bottom: 8px; }
.pk-list { display: flex; flex-direction: column; gap: var(--sp-2); max-height: 52vh; overflow-y: auto; }
.pk-row { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 8px 10px; }
.pk-row-top { display: flex; justify-content: space-between; gap: var(--sp-2); margin-bottom: 4px; }
.pk-title { font-weight: 600; }
.pk-addr { color: var(--muted); }
.pk-key-line { display: flex; align-items: center; gap: var(--sp-2); }
.pk-key { font-size: var(--cap-size); word-break: break-all; flex: 1; }

/* ================================================================ PRE-FLIGHT SHEET
   The single execute confirmation: role banner, per-leg table from the arming
   dry run, totals, dry-run age, inline master password, one red Confirm. */
.picker.preflight { width: min(600px, 94vw); }
.preflight-banner { background: color-mix(in srgb, var(--red) 10%, transparent); border: 1px solid var(--red); border-radius: var(--radius); color: var(--foreground); padding: 10px 12px; font-size: var(--body-size); font-weight: 600; margin-bottom: 10px; }
.preflight-body { overflow-y: auto; max-height: 42vh; border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: 10px; }
.preflight-table { width: 100%; border-collapse: collapse; font-size: var(--body-size); }
.preflight-table th { text-align: left; color: var(--muted); font-weight: 500; padding: 6px 8px; border-bottom: 1px solid var(--border); position: sticky; top: 0; background: var(--card); }
.preflight-table th:nth-child(3), .preflight-table th:nth-child(4) { text-align: right; }
.preflight-table td { padding: 6px 8px; border-bottom: 1px solid var(--border); vertical-align: top; }
.preflight-table tr:last-child td { border-bottom: none; }
.preflight-idx { color: var(--muted-2); width: 26px; }
.preflight-num { text-align: right; font-family: var(--mono); white-space: nowrap; }
.preflight-leg .preflight-note { display: block; color: var(--muted-2); font-size: var(--cap-size); }
.preflight-leg .preflight-note-err { color: #f87171; }
.preflight-err td { background: color-mix(in srgb, var(--red) 6%, transparent); }
.preflight-totals { font-size: var(--body-size); font-weight: 600; margin-bottom: 4px; }
.preflight-stale { font-size: var(--cap-size); color: var(--muted); margin-bottom: 10px; }
.preflight-stale-old { color: var(--amber); }
.preflight-pw-ok { font-size: var(--body-size); color: var(--green); margin-bottom: 8px; }
.preflight-foot { justify-content: flex-end; }
.preflight-confirm { font-weight: 600; }

/* ============================================================
   Loops — repeating step-chain builder (matches loops-page mockup).
   Uses the shared shadcn tokens; squared corners; Geist Mono numbers.
   ============================================================ */
.loops-accent { color: var(--violet); font-weight: 600; }
.loops-head .h1row { width: 100%; }
.loops-add { margin-left: auto; }
.loops-list { display: flex; flex-direction: column; }

.loops-empty {
  border: 1px dashed var(--border-strong); background: var(--card);
  padding: 34px 20px; text-align: center; display: flex; flex-direction: column;
  align-items: center; gap: var(--sp-1);
}
.loops-empty-title { font-weight: 600; font-size: var(--title-size); }
.loops-empty-sub { color: var(--muted-2); font-size: var(--body-size); max-width: 46ch; margin-bottom: 8px; }

.loop { background: var(--card); border: 1px solid var(--border); margin-bottom: 16px; }
.loop-head { display: flex; align-items: center; gap: var(--sp-2); padding: 11px 14px; border-bottom: 1px solid var(--border); }
.loop-name-input {
  background: transparent; border: 1px solid transparent; color: var(--foreground);
  font-size: var(--title-size); font-weight: 600; padding: 3px 6px; min-width: 90px; max-width: 240px;
  border-radius: var(--radius);
}
.loop-name-input:hover { border-color: var(--border); }
.loop-name-input:focus { border-color: var(--border-strong); background: var(--surface); outline: none; }
.loop-badge { font-size: var(--cap-size); font-weight: 600; padding: 2px 9px; border-radius: 999px; background: var(--surface); color: var(--muted-2); }
.loop-badge.run { background: #12241c; color: var(--green); }
.loop-badge.done { background: var(--surface); color: var(--muted); }
.loop-badge.error { background: #261417; color: var(--red); }
/* .loop-head's own gap spaces these; only the two `auto` pushes are layout. */
.loop-head .btn.small { margin-left: 0; }
.loop-head .loop-dry { margin-left: auto; }
.loop-head .loop-stop { margin-left: auto; color: var(--red); border-color: #4a2427; }
.loop-head .loop-stop:hover { background: var(--red); border-color: var(--red); color: #fff; }

.loop-body { padding: 14px; }
.loop-body .eyebrow {
  font-size: var(--eyebrow-size); letter-spacing: var(--eyebrow-track); text-transform: uppercase;
  color: var(--muted-2); font-weight: var(--eyebrow-weight); margin-bottom: 8px;
}

/* start wallet dropzone */
.loop-startw {
  display: flex; align-items: center; gap: var(--sp-2); background: var(--surface);
  border: 1px solid var(--border); padding: 8px 11px; margin-bottom: 16px; min-height: 40px;
  flex-wrap: wrap;
}
.loop-startw.drop-hover { border-color: var(--violet); background: var(--surface-2); }
.loop-startw-empty { color: var(--muted-2); font-size: var(--body-size); }
.loop-startw-bal { color: var(--muted); }
.loop-startw .dz-chip-x { background: none; border: none; color: var(--muted); cursor: pointer; font-size: var(--body-size); }
.loop-startw .dz-chip-x:hover { color: var(--foreground); }
/* SET of seed wallets: one chip per seed, wrapping; each chain is one chip. */
.loop-startw-chips { display: flex; flex-wrap: wrap; gap: var(--sp-1); flex: 1; min-width: 0; }
/* One seed = one SINGLE-LINE chip, so it takes the compact control rung and the
   shared inset/gap exactly like .dz-chip. (.loop-startw itself is the drop-zone
   CONTAINER these sit in — a slot, not a chip — so it keeps its own box.) */
.loop-startw-chip {
  display: inline-flex; align-items: center; gap: var(--chip-gap); background: var(--card);
  border: 1px solid var(--border); padding: 0 var(--chip-pad-x); min-width: 0;
  height: var(--control-h-sm);
}
.loop-startw-add { margin-left: auto; flex: none; }

/* steps list */
.loop-steps { display: flex; flex-direction: column; border: 1px solid var(--border); }
.loop-step {
  display: flex; align-items: center; gap: var(--sp-3); padding: 8px 11px;
  border-bottom: 1px solid var(--border); font-size: var(--body-size); background: var(--card);
}
.loop-step:last-of-type { border-bottom: 0; }
.loop-step-dragging { opacity: .5; }
.loop-step-over { box-shadow: inset 0 2px 0 var(--violet); }
.loop-grip { color: var(--muted-2); cursor: grab; font-size: var(--body-size); flex: none; }
.loop-stype { display: inline-flex; align-items: center; gap: var(--sp-2); font-weight: 600; width: 104px; flex: none; }
.loop-ic {
  width: 20px; height: 20px; border-radius: 3px; display: inline-flex; align-items: center;
  justify-content: center; font-size: var(--body-size); font-weight: 700; flex: none;
}
.loop-ic.buy { background: #12241c; color: var(--green); }
.loop-ic.sell { background: #261417; color: var(--red); }
.loop-ic.xfer { background: #1a1830; color: var(--violet); }
.loop-ic.unwrap { background: #1a1830; color: var(--violet); }
.loop-ic.wait { background: var(--surface-2); color: var(--muted); }
.loop-sconf { display: flex; align-items: center; gap: var(--sp-1); flex-wrap: wrap; color: var(--muted); }
.loop-sconf-tail { color: var(--muted-2); font-size: var(--body-size); }
/* Inline step numbers ("wait 30 s", "every 3 loops"): they read as part of a
   sentence, so they stay CENTRED — but on the shared ch ladder and numeric face
   so every one of them is the same box. */
.loop-num {
  width: var(--num-w-sm); height: var(--control-h-sm); background: var(--background); border: 1px solid var(--border);
  color: var(--foreground); text-align: center; font-size: var(--body-size); border-radius: var(--radius);
  font-family: var(--num); font-variant-numeric: tabular-nums;
}
.loop-num:focus { border-color: var(--border-strong); outline: none; }
.loop-dest-sel { height: var(--control-h-sm); font-size: var(--body-size); width: auto; max-width: 190px; }

/* inline mode toggles inside a step editor */
.loop-seg { display: inline-flex; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.loop-seg-btn {
  background: var(--surface); border: none; border-right: 1px solid var(--border);
  color: var(--muted); font-size: var(--body-size); padding: 0 8px; cursor: pointer; height: var(--control-h-sm);
}
.loop-seg-btn:last-child { border-right: 0; }
.loop-seg-btn.active { background: var(--surface-2); color: var(--foreground); font-weight: 600; }
.loop-seg-btn:hover { color: var(--foreground); }
.loop-newpill { background: #1a1830; color: var(--violet); font-size: var(--cap-size); font-weight: 700; padding: 2px 8px; border-radius: 999px; }
.loop-sx { margin-left: auto; color: var(--muted-2); cursor: pointer; font-size: var(--body-size); background: none; border: none; flex: none; }
.loop-sx:hover { color: var(--red); }

/* + add step */
.loop-addstep {
  padding: 8px 11px; background: var(--surface); display: flex; align-items: center;
  gap: var(--sp-1); flex-wrap: wrap; font-size: var(--body-size);
}
.loop-addstep-lead { color: var(--muted-2); }
.loop-addstep-btn {
  background: transparent; border: 1px solid var(--border); color: var(--muted);
  font-size: var(--body-size); padding: 0 9px; cursor: pointer; border-radius: var(--radius); height: var(--control-h-sm);
}
.loop-addstep-btn:hover { background: var(--surface-2); color: var(--foreground); border-color: var(--border-strong); }

/* stop-when row */
/* run-order row (chain by chain | staggered) */
.loop-order {
  display: flex; align-items: center; gap: var(--sp-4); margin-top: 14px; flex-wrap: wrap;
}
.loop-order-lead { margin: 0; }
.loop-order-sep { color: var(--muted-2); }
.loop-order-tail { color: var(--muted-2); font-size: var(--body-size); }
.loop-stagger-num { width: var(--num-w-sm); margin-left: 2px; }

.loop-stop {
  display: flex; align-items: center; gap: var(--sp-4); margin-top: 16px; padding-top: 14px;
  border-top: 1px solid var(--border); flex-wrap: wrap;
}
.loop-stop-lead { margin: 0; }
.loop-opt { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--body-size); color: var(--muted); cursor: pointer; }
.loop-opt.on { color: var(--foreground); }
.loop-radio { width: 13px; height: 13px; border-radius: 50%; border: 1px solid var(--border-strong); flex: none; }
.loop-radio.on { border-color: var(--foreground); box-shadow: inset 0 0 0 3px var(--foreground); }
.loop-stop-num {
  width: var(--num-w-sm); height: var(--control-h-sm); background: var(--background); border: 1px solid var(--border);
  color: var(--foreground); text-align: center; font-size: var(--body-size); border-radius: var(--radius);
  font-family: var(--num); font-variant-numeric: tabular-nums;
}
.loop-stop-dir, .loop-stop-asset { height: var(--control-h-sm); font-size: var(--body-size); width: auto; }

/* live status line */
.loop-live { margin-top: 12px; font-size: var(--body-size); color: var(--muted); font-family: var(--num); min-height: 16px; }
.loop-live b { color: var(--foreground); }
.loop-live-sep { color: var(--muted-2); }
.loop-live-note { color: var(--muted-2); font-style: italic; }
.loop-neg { color: var(--red); }
.loop-pos { color: var(--green); }

/* gas reserve (on the stop-when row) */
.loop-gasres { display: inline-flex; align-items: center; gap: var(--sp-2); margin-left: auto; }
.loop-gasres-lead { margin: 0; }
.loop-gasres-num { width: var(--num-w-md); text-align: center; }
.loop-gasres-tail { color: var(--muted-2); font-size: var(--body-size); }

/* subsidiary family list */
.loop-subs { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); }
.loop-subs-head { margin-bottom: 8px; display: flex; align-items: baseline; gap: var(--sp-2); }
.loop-subs-count { color: var(--muted-2); font-weight: 700; }
.loop-subs-selall {
  margin-left: auto; background: none; border: 1px solid var(--border-strong);
  color: var(--muted); font-family: inherit; font-size: var(--cap-size); padding: 1px 8px; cursor: pointer;
}
.loop-subs-selall:hover { color: var(--foreground); border-color: var(--muted); }
.loop-subs-list { display: flex; flex-direction: column; border: 1px solid var(--border); }
.loop-subs-list + .loop-subs-chain { margin-top: 10px; }
/* Per-chain label above each chain's subsidiary block (multi-chain loops). */
.loop-subs-chain {
  display: flex; align-items: baseline; gap: var(--sp-0); margin-bottom: 5px;
  font-size: var(--eyebrow-size); letter-spacing: var(--eyebrow-track); text-transform: uppercase;
  color: var(--muted-2);
}
.loop-subs-chain-nm { font-weight: var(--eyebrow-weight); color: var(--muted); }
.loop-subs-chain-ct { font-weight: var(--eyebrow-weight); }
/* A subsidiary row is the SINGLE-LINE wallet again (dot · name · address ·
   balance), so it takes the compact rung, the shared gap and the shared inset. */
.loop-sub {
  display: flex; align-items: center; gap: var(--chip-gap); padding: 0 var(--chip-pad-x);
  height: var(--control-h-sm);
  border-bottom: 1px solid var(--border); background: var(--card);
}
.loop-sub:last-child { border-bottom: 0; }
.loop-sub { cursor: pointer; user-select: none; }
.loop-sub:hover { background: var(--surface); }
.loop-sub.selected { background: var(--surface-2); box-shadow: inset 2px 0 0 var(--ring); }
.loop-sub.dusted { opacity: .5; cursor: default; }
.loop-sub.dusted:hover { background: none; }
.loop-sub-addr { color: var(--muted-2); }
.loop-sub-bal { margin-left: auto; color: var(--muted); }

/* ---- Quick actions: right-click wallet menu + buy/sell params modal ---- */
.qa-menu {
  position: fixed; z-index: 1000; min-width: 196px;
  background: var(--float); border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-pop); padding: 4px;   /* items carry their own inset */
}
.qa-menu-head {
  font-size: var(--eyebrow-size); letter-spacing: var(--eyebrow-track); text-transform: uppercase;
  color: var(--muted-2); font-weight: var(--eyebrow-weight); padding: 6px 9px 5px;
}
.qa-menu-item {
  display: flex; align-items: baseline; gap: var(--sp-3); width: 100%;
  background: none; border: 0; text-align: left; cursor: pointer;
  padding: 8px 9px; color: var(--foreground); font-family: inherit; font-size: var(--body-size);
}
.qa-menu-item:hover { background: var(--surface); }
.qa-menu-label { font-weight: 600; }
.qa-menu-sub { margin-left: auto; font-size: var(--cap-size); color: var(--muted-2); }
.qa-menu-sep { height: 1px; background: var(--border); margin: 4px 2px; }

.qa-params { min-width: 320px; display: flex; flex-direction: column; gap: var(--sp-3); }
.qa-params-amt { width: 100%; height: var(--control-h); }
.qa-params-unit { width: 100%; }
.qa-params-hint { font-size: var(--cap-size); color: var(--muted-2); line-height: 1.45; }
.qa-params-foot { display: flex; justify-content: flex-end; gap: var(--sp-2); }

/* Rail footer: selected count + Clear (deselect all) */
.rail-foot { display: flex; align-items: center; gap: var(--sp-2); }
.rail-foot-count { flex: 1; }
.rail-foot-clear {
  background: none; border: 1px solid var(--border-strong); color: var(--muted);
  font-family: inherit; font-size: var(--cap-size); padding: 2px 10px; cursor: pointer;
}
.rail-foot-clear:hover { color: var(--foreground); border-color: var(--muted); }

/* ---------------------------------------------------------------- LAUNCH */
.launch-signer {
  display: flex; align-items: flex-end; gap: var(--sp-3); flex-wrap: wrap;
  margin-bottom: 18px; padding-bottom: 16px; border-bottom: 1px solid var(--border);
}
.launch-signer .field { flex: 1 1 320px; margin: 0; }
.launch-token-badge {
  font-family: var(--mono); font-size: var(--body-size); color: var(--foreground);
  background: var(--surface); border: 1px solid var(--border-strong);
  padding: 6px 10px; border-radius: 0; align-self: center;
  display: flex; align-items: center; gap: var(--sp-2); word-break: break-all;
}
.launch-token-badge b { color: var(--muted); font-family: inherit; font-weight: 600; }
.launch-step {
  border: 1px solid var(--border); background: var(--card);
  padding: 16px 16px 14px; margin-top: 14px;
}
.launch-step-head { display: flex; align-items: center; gap: var(--sp-3); margin-bottom: 12px; }
.launch-step-head h3 { margin: 0; font-size: var(--title-size); font-weight: 600; }
.launch-step-n {
  flex: 0 0 auto; width: 22px; height: 22px; border-radius: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface); border: 1px solid var(--border-strong);
  color: var(--muted); font-size: var(--body-size); font-weight: 600; font-family: var(--num); font-variant-numeric: tabular-nums;
}
.launch-step .fields { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.launch-step .field { flex: 1 1 180px; margin: 0; }
.launch-step .field.wide { flex-basis: 100%; }
.launch-step .run-actions { margin-top: 14px; }
.launch-step.launch-locked { opacity: 0.55; }
.launch-step.launch-locked input,
.launch-step.launch-locked select,
.launch-step.launch-locked .btn { pointer-events: none; }

/* Template picker (choose the contract template to deploy) */
.launch-template {
  display: flex; align-items: flex-end; gap: var(--sp-3); flex-wrap: wrap;
  margin-bottom: 18px; padding-bottom: 16px; border-bottom: 1px solid var(--border);
}
.launch-template .field { flex: 1 1 320px; margin: 0; }
.launch-template-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; align-self: center; }
/* Constructor-input type tag next to each generated field label */
.ctor-type { color: var(--muted); font-size: var(--cap-size); font-weight: 500; }
/* Add-template modal: name + artifact/ABI/bytecode textareas */
.tpl-modal { max-width: 620px; width: 92vw; }
.tpl-help { color: var(--muted); font-size: var(--body-size); line-height: 1.5; margin-bottom: 12px; }
.tpl-modal .field { margin: 0 0 12px; }
.tpl-ta {
  width: 100%; resize: vertical; font-size: var(--body-size); line-height: 1.4;
  background: var(--surface); border: 1px solid var(--border); border-radius: 0;
  color: var(--foreground); padding: 8px 10px;
}

/* ---------------------------------------------------------------- MANAGE */
.manage-target {
  margin-bottom: 4px; padding-bottom: 16px; border-bottom: 1px solid var(--border);
}
.manage-target .fields { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.manage-target .field { flex: 1 1 320px; margin: 0; }
.manage-target .field.wide { flex-basis: 100%; }
.manage-target .run-actions { margin-top: 12px; }

/* Read-only status strip painted after Load. */
.manage-status {
  margin-top: 14px; border: 1px solid var(--border); background: var(--card);
  padding: 12px 14px; display: flex; flex-wrap: wrap; gap: var(--sp-2) 20px;
}
.manage-status .ms-item { display: flex; flex-direction: column; gap: var(--sp-0); min-width: 90px; }
.manage-status .ms-k { font-size: var(--cap-size); font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; }
.manage-status .ms-v { font-size: var(--body-size); color: var(--foreground); font-variant-numeric: tabular-nums; }
.manage-status .ms-v.mono { font-family: var(--mono); word-break: break-all; }
.manage-status .ms-warn {
  flex-basis: 100%; margin-top: 4px; padding: 8px 10px;
  border: 1px solid var(--danger, #c0392b); color: var(--danger, #c0392b);
  background: color-mix(in srgb, var(--danger, #c0392b) 8%, transparent);
  font-size: var(--body-size);
}
.manage-status .ms-ok { color: var(--good, #2e7d32); }
.manage-status .ms-off { color: var(--muted); }

.manage-group { margin-top: 22px; }
.manage-group-head { display: flex; align-items: center; gap: var(--sp-2); margin-bottom: 6px; }
.manage-group-head h3 {
  margin: 0; font-size: var(--cap-size); font-weight: 600; text-transform: uppercase;
  letter-spacing: .08em; color: var(--muted);
}
.manage-action {
  border: 1px solid var(--border); background: var(--card);
  padding: 14px 16px 12px; margin-top: 10px;
}
.manage-act-head { margin-bottom: 10px; }
.manage-act-title { font-size: var(--body-size); font-weight: 600; color: var(--foreground); }
.manage-impact { font-size: var(--body-size); color: var(--muted); margin-top: 2px; }
.manage-action .fields { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.manage-action .field { flex: 1 1 180px; margin: 0; }
.manage-action .field.wide { flex-basis: 100%; }
.manage-action .run-actions { margin-top: 12px; }
.manage-current {
  padding: 8px 10px; border: 1px solid var(--border-strong); background: var(--surface);
  color: var(--foreground); font-size: var(--body-size);
}
/* Owner banner "IS the owner" (positive) variant of .ms-warn. */
.manage-status .ms-warn.ms-ok-banner {
  border-color: var(--good, #2e7d32); color: var(--good, #2e7d32);
  background: color-mix(in srgb, var(--good, #2e7d32) 8%, transparent);
}

/* ABI-driven Read panel: one row per view function. */
.manage-read-row {
  border: 1px solid var(--border); background: var(--card);
  padding: 12px 14px; margin-top: 10px;
}
.manage-read-head { display: flex; align-items: center; gap: var(--sp-2); margin-bottom: 8px; }
.manage-read-fn { font-size: var(--body-size); font-weight: 600; color: var(--foreground); }
.manage-read-row .fields { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.manage-read-row .field { flex: 1 1 180px; margin: 0; }
.manage-read-row .field.wide { flex-basis: 100%; }
.manage-read-row .run-actions { margin-top: 10px; }
.manage-read-result {
  margin-top: 8px; padding: 8px 10px; border: 1px solid var(--border-strong);
  background: var(--surface); color: var(--foreground); font-size: var(--body-size);
  word-break: break-all; min-height: 1.4em;
}
.manage-read-val { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-1); }
.manage-read-val + .manage-read-val { margin-top: 4px; }
.manage-read-name { color: var(--muted); font-size: var(--body-size); }
.manage-read-type { color: var(--muted); font-size: var(--cap-size); }
.manage-read-out { color: var(--foreground); font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------- LOOPS: max-wallets, presets, gather+dust */
/* Max-wallets cap row reuses .loop-order layout; number field matches stop-num. */
.loop-walletcap-num { width: var(--num-w-sm); text-align: center; }

/* Card-head "Save preset" + toolbar "Load preset…" (btn base styles the rest). */
.loop-savepreset { white-space: nowrap; }

/* Preset picker modal. */
.loop-preset-picker { min-width: 340px; max-width: 92vw; display: flex; flex-direction: column; gap: var(--sp-3); }
.loop-preset-list { display: flex; flex-direction: column; border: 1px solid var(--border); max-height: 50vh; overflow-y: auto; }
.loop-preset-row { display: flex; align-items: stretch; border-bottom: 1px solid var(--border); }
.loop-preset-row:last-child { border-bottom: 0; }
.loop-preset-pick {
  flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-0); text-align: left;
  background: none; border: 0; color: var(--foreground); font-family: inherit; cursor: pointer; padding: 9px 11px;
}
.loop-preset-pick:hover { background: var(--surface); }
.loop-preset-nm { font-weight: 600; font-size: var(--body-size); }
.loop-preset-sub { color: var(--muted-2); font-size: var(--body-size); }
.loop-preset-del {
  flex: 0 0 auto; background: none; border: 0; border-left: 1px solid var(--border);
  color: var(--muted-2); cursor: pointer; font-size: var(--body-size); padding: 0 11px;
}
.loop-preset-del:hover { color: var(--red); background: var(--surface); }

/* Sub-panel "Gather + dust" (sits after "select all", accented). */
.loop-subs-gatherdust {
  background: none; border: 1px solid var(--ring);
  color: var(--foreground); font-family: inherit; font-size: var(--cap-size); padding: 1px 8px; cursor: pointer;
}
.loop-subs-gatherdust:hover { background: var(--surface-2); }

/* Left-rail group "Gather + dust" action row (below an open group's header). */
.rail-group-actions { padding: 4px 8px 6px 26px; }
.rail-group-gatherdust {
  background: none; border: 1px solid var(--border-strong); color: var(--muted);
  font-family: inherit; font-size: var(--cap-size); padding: 1px 8px; cursor: pointer;
}
.rail-group-gatherdust:hover { color: var(--foreground); border-color: var(--ring); }

/* ---------------------------------------------------------------- ACTIVITY FEED: per-task groups */
.feed-group { margin-bottom: 10px; }
.feed-group-head {
  display: flex; align-items: baseline; gap: var(--sp-2); padding: 2px 2px 5px;
  border-bottom: 1px solid var(--border); margin-bottom: 6px;
}
.feed-group-name {
  flex: 1; min-width: 0; font-size: var(--eyebrow-size); font-weight: var(--eyebrow-weight);
  letter-spacing: var(--eyebrow-track); text-transform: uppercase; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.feed-group-count {
  flex: 0 0 auto; font-size: var(--cap-size); color: var(--muted-2); font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}
.feed-group-body { display: flex; flex-direction: column; gap: var(--sp-2); }

/* ---------------------------------------------------------------- WALLET VAULT: dusted dropdown */
.vault-dust { border-top: 1px solid var(--border); margin-top: 8px; }
.vault-dust-head {
  display: flex; align-items: center; gap: var(--sp-2); width: 100%; padding: 9px 4px;
  background: none; border: 0; cursor: pointer; color: var(--muted);
  font-family: inherit; font-size: var(--body-size); text-align: left;
}
.vault-dust-head:hover { color: var(--foreground); }
.vault-dust-caret { width: 10px; color: var(--muted-2); }
.vault-dust-label { text-transform: uppercase; letter-spacing: var(--eyebrow-track); font-weight: var(--eyebrow-weight); }
.vault-dust-count {
  color: var(--muted-2); font-variant-numeric: tabular-nums;
  border: 1px solid var(--border-strong); border-radius: 999px; padding: 0 7px; font-size: var(--cap-size);
}
.vault-dust-list { display: flex; flex-direction: column; opacity: .82; }

/* ---------------------------------------------------------------- VAULT GATE (locked popup) */
/* Full-screen blocker: nothing behind it is reachable until the vault unlocks. */
.vault-gate {
  position: fixed; inset: 0; z-index: 6000;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--background) 78%, transparent);
  backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
}
.vault-gate-card {
  width: 340px; max-width: 92vw; padding: var(--pad-modal);
  background: var(--float); border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-modal);
  display: flex; flex-direction: column; gap: var(--sp-3);
}
.vault-gate-brand {
  font-size: var(--title-size); font-weight: 700; letter-spacing: .01em; color: var(--foreground);
  margin-bottom: 2px;
}
.vault-gate-title { margin: 0; font-size: var(--title-size); font-weight: 600; color: var(--foreground); }
.vault-gate-sub { margin: 0 0 6px; font-size: var(--body-size); line-height: 1.45; color: var(--muted); }
.vault-gate-input {
  width: 100%; height: var(--control-h); padding: 0 11px;
  background: var(--surface); border: 1px solid var(--border); color: var(--foreground);
  font-family: inherit; font-size: var(--body-size);
}
.vault-gate-input::placeholder { color: var(--muted-2); }
.vault-gate-input:focus { outline: none; border-color: var(--ring); }
.vault-gate-btn { width: 100%; margin-top: 5px; }
.vault-gate-err { color: var(--red); font-size: var(--body-size); min-height: 1em; }

/* ---------------------------------------------------------------- WETH-HOLDING WALLETS */
/* A wallet holding any WETH shows its balance amber/yellow everywhere it appears
   (WETH is folded into the ETH figure). Selectors are per-site so this wins over
   each place's base text colour. */
.bal-weth,
.rail-wallet-eth.bal-weth,
.wallet-chip-bal.bal-weth,
.loop-sub-bal.bal-weth,
.loop-startw-bal.bal-weth { color: var(--amber); }

/* ---------------------------------------------------------------- SHORTCUTS */
#shortcuts-body { display: flex; flex-direction: column; gap: var(--sp-4); }
/* The shared shortcut bar (#sc-roster) mounts into a page's slot. */
.sc-bar-slot { margin-bottom: 16px; }
.sc-bar-slot:empty { display: none; }

/* Rail create/import buttons. */
.rail-head-actions { display: flex; align-items: center; gap: var(--sp-1); }

/* Small create/import modal (rail). */
.sc-modal-backdrop {
  position: fixed; inset: 0; z-index: 1000;
  background: color-mix(in srgb, var(--background) 70%, transparent);
  display: flex; align-items: center; justify-content: center;
}
.sc-modal {
  width: min(92vw, 380px); background: var(--float);
  border: 1px solid var(--border-strong); border-radius: var(--radius, 0px); padding: var(--pad-modal);
  display: flex; flex-direction: column; gap: var(--sp-3); box-shadow: var(--shadow-modal);
}
.sc-modal-title { margin: 0 0 4px; font-size: var(--title-size); font-weight: 600; }

/* Top "in action" roster box — full width. */
.sc-roster {
  position: relative;
  min-height: 68px;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius, 0px);
  background: var(--surface);
  display: flex; flex-wrap: wrap; align-items: center; gap: 0;
}
.sc-roster > .dz-empty { padding: 16px 12px; width: 100%; text-align: center; }
.sc-roster.drop-hover {
  outline: 2px dashed var(--ring); outline-offset: 2px;
  background: color-mix(in srgb, var(--ring) 12%, transparent);
}

/* Shortcut cards grid. */
.sc-cards {
  display: grid; gap: var(--sp-4);
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
}
/* ONE interior inset on all four sides — the card used to be 16px top/bottom
   against 18px left/right, so nothing inside lined up with the title. */
.sc-card {
  border: 1px solid var(--border);
  border-radius: var(--radius, 0px);
  background: var(--surface);
  padding: var(--pad-card);
  display: flex; flex-direction: column; gap: var(--sp-3);
}
.sc-card-head { display: flex; align-items: center; gap: var(--sp-2); }
.sc-card-title { font-size: var(--title-size); font-weight: 600; line-height: var(--title-line); margin: 0; }
.sc-card-sub { margin: -6px 0 2px; }
.sc-card-body { display: flex; flex-direction: column; gap: var(--sp-3); }

/* Drop slots with a big +. */
.sc-two-slots { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }
.sc-dropzone {
  position: relative;
  min-height: 56px;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius, 0px);
  background: var(--background);
  padding: 0;
  overflow: hidden;
  display: flex; flex-wrap: wrap; align-items: center; gap: 0;
}
.sc-dropzone.drop-hover {
  border-color: var(--violet); border-style: solid;
  background: var(--surface-2);
}
.sc-dropzone-multi { min-height: 84px; align-content: flex-start; }
/* The min-height belongs to the EMPTY state — it is the size of the target you
   have to hit. Once a zone holds cards it sizes to them, so one or two wallets
   no longer float in a reserved void. */
.sc-roster:has(.sc-wallet-list),
.sc-dropzone:has(.sc-wallet-list) { min-height: 0; align-items: stretch; align-content: stretch; }
/* Empty state: one centred + / hint pair on a sane internal scale, so a tall
   zone reads as a deliberate target instead of content floating in a void. */
.sc-drop-empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-1); width: 100%; padding: 16px 12px; color: var(--muted-2); user-select: none;
}
.sc-plus { font-size: 22px; line-height: 1; font-weight: 300; color: var(--muted-2); }
.sc-drop-hint { font-size: var(--body-size); line-height: 1; }

/* ---- Wallet card (roster + drop slots) ----
   THE SAME chip as .dz-chip, in list form. One anatomy, two densities — and the
   ONLY difference between them is the line count:

     wide zones (the roster)     NAME  tok · ETH · ✕            28px  (1 line)
     narrow zones (.sc-dropzone) NAME        · ETH · ✕          44px  (2 lines)
                                 0x1234…5678 ·  tok

   The second line is the ADDRESS, not filler. Both densities used to open with a
   colour dot whose hue was the wallet's index in the list, so the two-line card
   spent a column on a non-identity and then had a hole where the identity should
   have gone. Line 2 now reads identity-left / holdings-right, the same way the
   rail row does one size up — so a wallet dragged from the rail into a slot
   keeps the same anatomy, just fewer lines.

   The single-line height is --control-h-sm, the same rung a .btn.small takes, so
   a roster row and a button beside it share an edge. It was 30px: two pixels off
   every compact control in the app, which is exactly the gap that reads as a
   mistake rather than a decision.

   The card is ALWAYS a grid whose parts sit in named areas — `display: contents`
   dissolves the .sc-wallet-id wrapper so name and balance are grid items in both
   modes, instead of a flex pair fighting over one column (which is what
   truncated a name to "A 1…"). ✕ and the type scale are identical in both, so a
   wallet reads the same wherever it is dropped. */
.sc-wallet-list { width: 100%; display: flex; flex-direction: column; }
.sc-wallet-card {
  display: grid; align-items: center;
  grid-template-columns: minmax(0, 1fr) auto auto var(--chip-x);
  grid-template-areas: "name bal eth x";
  column-gap: var(--chip-gap);
  height: var(--control-h-sm); padding: 0 var(--chip-pad-x);
  border-bottom: 1px solid var(--border);
  transition: background .12s;
}
.sc-wallet-card:last-child { border-bottom: none; }
.sc-wallet-card:hover { background: var(--surface-2); }
.sc-wallet-id { display: contents; }
.sc-wallet-name {
  grid-area: name; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Address — rendered by scWalletCard() in BOTH densities, shown only where there
   is a second line to put it on. `display: none` (not a missing element) so the
   one-line grid, which has no `addr` area, can't auto-place it into a stray
   column. Unhidden by the .sc-dropzone rule below. */
.sc-wallet-addr {
  grid-area: addr; display: none; min-width: 0;
  font-family: var(--mono); color: var(--muted); letter-spacing: .01em;
  font-variant-numeric: tabular-nums;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sc-wallet-bal {
  grid-area: bal; min-width: 0; text-align: right;
  color: var(--muted); font-family: var(--num);
  font-variant-numeric: tabular-nums; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sc-wallet-bal.dim { color: var(--muted-2); }
.sc-wallet-eth {
  grid-area: eth; text-align: right; white-space: nowrap;
  font-family: var(--num); font-variant-numeric: tabular-nums; color: var(--text);
}
/* Selectable/draggable roster rows. */
.sc-wallet-pick { cursor: grab; user-select: none; }
.sc-wallet-pick.selected { background: var(--surface-2); box-shadow: inset 2px 0 0 var(--ring); }
.sc-wallet-pick.dragging { opacity: .45; }
.sc-wallet-eth.dim { color: var(--muted-2); }

/* Narrow density — EVERY drop slot, single or multi, so the two slots of one
   card (Transfer's FROM and TO, Sweep's BUYERS and SELLERS) always match.
   Two lines, each reading identity-left → holdings-right:
     line 1  NAME .................. ETH · ✕
     line 2  0x1234…5678 ............ token
   Row 2 stops at the figure column, so the token figure and the ETH figure stack
   into ONE right edge instead of one ending under the ✕. */
.sc-dropzone .sc-wallet-card {
  grid-template-columns: minmax(0, 1fr) auto var(--chip-x);
  grid-template-areas:
    "name eth x"
    "addr bal .";
  height: var(--chip-h-2); row-gap: var(--sp-0); column-gap: var(--chip-gap);
  padding: 0 var(--chip-pad-x);
}
.sc-dropzone .sc-wallet-card .sc-wallet-addr { display: block; }
.sc-dropzone .sc-wallet-x { align-self: center; }

.sc-wallet-x {
  grid-area: x; justify-self: end;
  width: var(--chip-x); height: var(--chip-x); flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  border: none; background: none; cursor: pointer;
  color: var(--muted-2); font-size: var(--cap-size); line-height: 1; padding: 0;
  opacity: 0; transition: opacity .12s, color .12s;
}
.sc-wallet-card:hover .sc-wallet-x { opacity: 1; }
.sc-wallet-x:hover { color: var(--red); background: var(--surface-2); }

/* Form rows. Every control in a row lands on ONE bottom edge (align-items:end)
   and every field hugs its control, so a row of three fields does not stretch a
   two-digit input to a third of the card. Buttons share the control rung. */
.sc-row { display: flex; flex-wrap: wrap; gap: var(--sp-3) 16px; align-items: flex-end; }
.sc-row > .btn { flex: 0 0 auto; height: var(--control-h); }
.sc-field { display: flex; flex-direction: column; gap: var(--sp-1); flex: 0 1 auto; min-width: 0; }
.sc-field-label {
  font-size: var(--eyebrow-size); font-weight: var(--eyebrow-weight);
  text-transform: uppercase; letter-spacing: var(--eyebrow-track); color: var(--muted);
  white-space: nowrap;
}
/* A hint under a control must not set the field's width — it wraps inside it. */
.sc-field-hint { font-size: var(--cap-size); color: var(--muted); max-width: 34ch; }
/* Recessed field on a --surface card. `background-color` (not the `background`
   shorthand) so a <select class="sc-input"> keeps the app-wide chevron image.
   Height comes from the ladder, so only the horizontal inset is set here — and
   the select's chevron gutter (--sel-gut) survives untouched. */
.sc-input {
  background-color: var(--background); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius, 0px);
  padding: 0 10px; font: inherit; font-size: var(--body-size); height: var(--control-h);
}
/* …and the shorthand above must not eat that gutter back off a <select>. */
select.sc-input { padding-right: var(--sel-gut); }
.sc-input:focus { outline: 2px solid var(--ring); outline-offset: -1px; border-color: var(--ring); }
.sc-select { width: auto; min-width: 132px; padding-left: 10px; }
.sc-calc { margin: 0; font-size: var(--body-size); color: var(--muted); font-variant-numeric: tabular-nums; }

/* Shared IO wrappers — Sweep & Transfer re-parent their two wallet slots into
   the SAME structure so one rule styles both identically. .sc-io-row = the two
   captioned boxes side by side (equal widths); .sc-io-box = one box (caption +
   its existing drop slot); .sc-io-cap = the small uppercase caption above the
   slot; .sc-fields = the settings-field group below the slots. */
.sc-io-row { display: flex; gap: var(--sp-3); align-items: stretch; }
.sc-io-box { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-1); }
.sc-io-cap {
  font-size: var(--eyebrow-size); font-weight: var(--eyebrow-weight);
  text-transform: uppercase; letter-spacing: var(--eyebrow-track); color: var(--muted);
}
.sc-fields { display: flex; flex-direction: column; gap: var(--sp-3); }
@media (max-width: 560px) { .sc-io-row { flex-direction: column; } }

/* Live net-position readout (Activity). */
.sc-net {
  font-size: var(--body-size); font-variant-numeric: tabular-nums;
  min-height: 1em; margin-bottom: 2px;
}
.sc-net-label {
  color: var(--muted); font-weight: var(--eyebrow-weight); text-transform: uppercase;
  font-size: var(--eyebrow-size); letter-spacing: var(--eyebrow-track); margin-right: 8px;
}
.sc-net-eth, .sc-net-tok { font-weight: 600; font-family: var(--mono); }

/* Net steering slider (Activity). */
.sc-lean-row { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.sc-lean { flex: 1 1 180px; min-width: 140px; accent-color: var(--violet); cursor: pointer; }
.sc-lean-cap { font-size: var(--cap-size); color: var(--muted); }
.sc-lean-read { font-size: var(--body-size); font-variant-numeric: tabular-nums; color: var(--text); min-width: 96px; }

/* Per-wallet net projection panel (Activity). */
.sc-proj { display: flex; flex-direction: column; gap: var(--sp-0); margin: 2px 0 2px; }
.sc-proj-row {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-2);
  font-size: var(--body-size); font-variant-numeric: tabular-nums;
}
.sc-proj-name { font-weight: 600; min-width: 90px; }
.sc-proj-net { font-family: var(--mono); font-weight: 600; }
.sc-proj-final { color: var(--muted); font-family: var(--mono); }
.sc-proj-hint { color: var(--muted-2); font-size: var(--body-size); }

/* Run button + status. */
.sc-run { align-self: flex-start; }
.sc-status { font-size: var(--body-size); min-height: 1em; color: var(--muted); }
.sc-status-run { color: var(--amber); }
.sc-status-ok { color: var(--green); }
.sc-status-err { color: var(--red); }
.sc-error { color: var(--red); font-size: var(--body-size); }

@media (max-width: 560px) {
  .sc-two-slots { grid-template-columns: 1fr; }
}

/* ============================================================
   Activity page — instrument panel (Quiet Data redesign).
   Squared hairlines, mono figures, semantic color only.
   ============================================================ */
/* ONE vertical rhythm for the whole panel: the stack owns the spacing (gap),
   nothing inside carries its own margin. Fixes the run buttons sitting flush
   against the wallet drop zone's bottom edge. */
.act-panel { display: flex; flex-direction: column; gap: var(--sp-4); }
.act-sep { border-top: 1px solid var(--border); margin: 0; }

/* Config slider rows — ONE grid shared by every row, including NET:
     label │ gutter │ track │ gutter │ readout
   The two narrow gutter columns exist on EVERY row, so all tracks share one
   left and right edge. The NET row's inline BUY/SELL captions live in those
   gutters (its wrapper uses display:contents to join this same grid) instead of
   eating into its own track — which is what made it a different shape.
   The readout column is fixed-width, right-aligned and tabular, so 4 / 6 /
   ±25% / 0–2s / even all land on one right edge. */
/* ONE geometry for every slider row. Previously each row had its own: the unit
   ("eth") and the NET row's BUY/SELL captions lived in GUTTER COLUMNS between
   the label and the track, which shortened those tracks — so no two tracks
   started or ended on the same x. Three columns now, and nothing may sit
   between the label and the track:
     [ LABEL 140px ] [ TRACK — identical start/end on every row ] [ VALUE 90px ]
   Units moved INTO the value cell; BUY/SELL became axis captions UNDER the
   track ends (.act-axis), so the NET track is full width like the others. */
.act-row {
  --act-label-w: 140px;
  --act-read-w: 90px;
  display: grid;
  grid-template-columns: var(--act-label-w) minmax(0, 1fr) var(--act-read-w);
  align-items: center; column-gap: var(--sp-3); min-height: 32px;
}
.act-row > .act-label { grid-column: 1; }
.act-row > .act-read { grid-column: 3; }
/* the row's control (slider track, or a select on the Sweep ORDER row) */
.act-row .act-track,
.act-row > :not(.act-label):not(.act-read):not(.act-cap):not(.act-net-body) { grid-column: 2; }
/* Causality grouping: per-trade (size, delay) then distribution (wallets, net).
   A single larger gap between the pairs, instead of even spacing throughout. */
.act-row.act-group-start { margin-top: var(--sp-3); }
/* controls sharing a slider row match the compact rung, not the 34px default */
.act-row > select, .act-row > input:not(.act-slider) {
  height: var(--control-h-sm); font-size: var(--body-size);
}
.act-label {
  font-family: inherit; font-size: var(--eyebrow-size); font-weight: var(--eyebrow-weight);
  line-height: 1; text-transform: uppercase; letter-spacing: var(--eyebrow-track); color: var(--muted);
}
/* The value cell owns the UNIT too ("0.001 ETH", "3-60 s"), so every row ends on
   the same right edge instead of one row floating its unit inline mid-track. */
.act-read {
  font: var(--fig-inline)/1 var(--num); font-variant-numeric: tabular-nums;
  color: var(--foreground); text-align: right; white-space: nowrap;
}
.act-read .act-read-unit { color: var(--muted); margin-left: 4px; }
.act-read.dim { color: var(--muted-2); }

/* slider base — hairline track, thin playhead thumb */
.act-track { position: relative; display: flex; align-items: center; height: 20px; }
.act-slider { -webkit-appearance: none; appearance: none; width: 100%; height: 20px; background: transparent; cursor: ew-resize; margin: 0; }
.act-slider::-webkit-slider-runnable-track { height: 2px; border-radius: 0;
  background: linear-gradient(90deg, var(--foreground) 0 var(--pct, 0%), var(--border) var(--pct, 0%)); }
.act-slider::-moz-range-track { height: 2px; border-radius: 0; background: var(--border); }
.act-slider::-moz-range-progress { height: 2px; background: var(--foreground); }
/* A grabbable handle. This was a 3px tick - visually a scale mark, not a
   control, and a hard target to hit with a mouse. */
.act-slider::-webkit-slider-thumb { -webkit-appearance: none; width: 10px; height: 18px; margin-top: -8px; border-radius: 0;
  background: var(--foreground); border: none; cursor: ew-resize;
  transition: box-shadow .08s ease, background .08s ease; }
.act-slider::-moz-range-thumb { width: 10px; height: 18px; border-radius: 0; background: var(--foreground); border: none; cursor: ew-resize; }
.act-slider:hover::-webkit-slider-thumb { box-shadow: 0 0 0 4px color-mix(in srgb, var(--ring) 28%, transparent); }
.act-slider:active::-webkit-slider-thumb { box-shadow: 0 0 0 5px color-mix(in srgb, var(--ring) 40%, transparent); }
.act-slider:focus-visible { outline: none; }
.act-slider:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 45%, transparent); }


/* NET steering slider — same row grid as the others; the wrapper dissolves so
   its BUY / SELL captions land in the shared gutter columns and its track lines
   up with the four tracks above. Center-origin fill via --a/--b. */
/* BUY / SELL are AXIS captions under the track's two ends, not gutter content.
   Inside the row grid they stole width from the track; underneath it, the NET
   track spans exactly the same measure as the three above it. */
.act-net-body { grid-column: 2; display: flex; flex-direction: column; gap: 2px; }
.act-axis { display: flex; justify-content: space-between; }
.act-axis .act-cap { white-space: nowrap; }
.act-cap {
  font-family: inherit; font-size: var(--eyebrow-cap-size); font-weight: var(--eyebrow-weight);
  line-height: 1; text-transform: uppercase; letter-spacing: .1em; color: var(--muted-2);
}
/* Center detent — the "even" position has to be findable by eye and by feel. */
.act-net-track::before { content: ""; position: absolute; left: 50%; top: 50%; width: 2px; height: 14px;
  transform: translate(-50%, -50%); background: var(--muted); pointer-events: none; z-index: 1; }
.act-slider-net { cursor: ew-resize; }
.act-slider-net::-webkit-slider-runnable-track { height: 2px; border-radius: 0;
  background: linear-gradient(90deg, var(--border) 0 var(--a, 50%), var(--fill, var(--border)) var(--a, 50%) var(--b, 50%), var(--border) var(--b, 50%)); }
.act-slider-net::-moz-range-track { height: 2px; background: var(--border); }
.act-slider-net::-moz-range-progress { background: transparent; }

/* Outcome cards — four even columns split by hairlines; 2x2 when narrow.
   ONE anatomy for all four, whether or not the card happens to carry a
   right-hand pill (EST / TARGET) or a trailing unit:
     caption row (fixed height, pill lane always reserved on the right)
     figure row  (fixed height, baseline-aligned)
     sparkline   (fixed height, same margin)
   so the four columns read as a set instead of four different shapes. */
.act-cards {
  --act-card-pad-y: 14px;
  --act-pill-lane: 52px;
  display: grid; grid-template-columns: repeat(4, 1fr);
}
@media (max-width: 1240px) {
  .act-cards { grid-template-columns: 1fr 1fr; }
  .act-card:nth-child(3) { border-left: none; }
  .act-card:nth-child(n+3) { border-top: 1px solid var(--border); }
}
/* Each tile carries its own left/right inset as a variable, so the ONE place
   that zeroes an outer edge also moves anything pinned to that edge (the pill). */
.act-card {
  --act-pad-l: var(--pad-card);
  --act-pad-r: var(--pad-card);
  position: relative; min-width: 0;
  padding: var(--act-card-pad-y) var(--act-pad-r) var(--act-card-pad-y) var(--act-pad-l);
}
.act-card + .act-card { border-left: 1px solid var(--border); }
/* The tile band is flush with its host card's own inset: the leftmost caption
   starts on the same edge as the card title and the rightmost figure ends on the
   card's right inset. --pad-card stays the INTERNAL gutter between tiles. */
.act-cards > .act-card:first-child { --act-pad-l: 0px; }
/* (last child keeps its right inset: the row needs a gutter matching the left
   margin, or the final tile bleeds into the panel edge.) */
/* 2-up: the outer edge of each ROW is the card edge. (After the 4-up rules above
   so it wins at equal specificity.) */
@media (max-width: 1240px) {
  .act-cards > .act-card:nth-child(odd) { --act-pad-l: 0px; }
}
/* the pill lane is reserved on EVERY card, so all four captions start and end
   on the same measure and the pill sits on the caption's own baseline */
/* No pill lane any more: EST / TARGET sit beside the figure they qualify (see
   .act-pill), not pinned to the far right edge of the column - which put ~600px
   of dead space between a qualifier and the thing it qualified. */
.act-card > .act-cap {
  display: block; min-height: var(--eyebrow-cap-size);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.act-fig-line { display: flex; align-items: baseline; gap: var(--sp-1); margin-top: 10px; min-height: 24px; }
.act-fig { font: 600 var(--fig-card)/1 var(--num); font-variant-numeric: tabular-nums; color: var(--foreground); }
.act-fig.dim { color: var(--muted-2); }   /* same dim as .act-read.dim / .*-bal.dim */
.act-fig-net { font-size: var(--fig-card); }
.act-unit { font-size: var(--body-size); color: var(--muted); font-variant-numeric: tabular-nums; }
.act-word { font-size: var(--cap-size); color: var(--muted); }
/* Rides in the figure line, immediately after the number. */
.act-pill {
  align-self: center; white-space: nowrap;
  font-family: inherit; font-size: var(--eyebrow-cap-size); font-weight: var(--eyebrow-weight);
  line-height: 1; text-transform: uppercase; letter-spacing: .1em; color: var(--muted-2);
}
.act-pill.live { color: var(--green); }
.act-pill.live::before { content: ""; display: inline-block; width: 5px; height: 5px; border-radius: 50%;
  background: var(--green); margin-right: 5px; vertical-align: middle; animation: actPulse 1.4s ease-in-out infinite; }

/* stat-tile sparkline — 12-point trend: colored line + gradient area fade.
   Taller than wide-and-squished so peaks read; the NET tile reads against a
   dashed zero axis at mid-height, --sell above zero, --buy below. */
.act-spark { height: 54px; margin-top: 14px; }
.act-spark svg { display: block; width: 100%; height: 100%; overflow: visible; }
.act-spark polyline { fill: none; stroke-width: 1.5;
  stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
/* Normalize the four bars to ONE visual weight. The gradients are authored in
   JS at different stop-opacities (.22 on the ETH/TOKEN/VOL tiles, .28 on the
   split NET tile), so the NET area read heavier than its neighbours; scale it
   back to the same effective density. Stroke weight is already shared above. */
.act-spark polygon { stroke: none; opacity: .85; }
.act-spark:has(.spark-zero) polygon { opacity: .67; }
.act-spark .spark-zero { stroke: var(--muted-2); stroke-width: 1;
  stroke-dasharray: 3 3; vector-effect: non-scaling-stroke; }

/* action row */
/* Both action buttons take the same footprint. They already shared a height
   (32px, .btn) — what differed was WIDTH, because "Start" is five characters and
   "Recover" is seven, so the pair read as mismatched even though the component
   was identical. A shared min-width makes the row deliberate. */
.act-actions { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.act-actions .btn { min-width: 88px; }
/* Recover is a utility, not a peer of Start - a text link, pushed away from the
   primary cluster so the eye lands on Start. */
.act-link {
  background: none; border: 0; padding: 0; margin-right: auto;
  color: var(--muted); font: inherit; font-size: var(--body-size);
  text-decoration: underline; text-underline-offset: 3px; cursor: pointer;
}
.act-link:hover { color: var(--foreground); }
.act-link:disabled { text-decoration: none; }
/* the drop zone stated "empty" using half the panel */
.act-mid > .sc-dropzone, .act-mid .sc-dropzone { max-height: 180px; }
.act-actions .sc-run { margin-left: auto; }
.act-livedot { width: 5px; height: 5px; border-radius: 50%; background: var(--green); animation: actPulse 1.4s ease-in-out infinite; }
@keyframes actPulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

/* Middle band: config slider column on the left, wallet drop zone on the right.
   `align-items: stretch` makes the zone match the slider column's height on its
   own — the old `min-height: 100%` hack did the same job from the child side. */
.act-mid { display: grid; grid-template-columns: 1fr 1fr; column-gap: var(--sp-4); align-items: stretch; }
.act-controls { display: flex; flex-direction: column; gap: var(--sp-1); }
.act-review { min-width: 88px; }
}  /* end @layer components */

@layer overrides {
/* One full-width section, but ON the shared gray card box (title + everything
   inside it). The hub's Activity slot (.sc-activity-slot, now also
   [data-page="activity"]) and the .dash-half Sweep/Transfer boxes host the
   cards, so they share every rule below and stay pixel-consistent. */
.sc-cards[data-page="activity"],
.sc-activity-slot,
.dash-half .sc-cards[data-page="sweep"],
.dash-half .sc-cards[data-page="transfer"] {
  display: block;                           /* was auto-fill grid → card was capped near 340px */
  gap: 0;
}
.sc-cards[data-page="activity"] .sc-card,
.sc-activity-slot .sc-card,
.dash-half .sc-cards[data-page="sweep"] .sc-card,
.dash-half .sc-cards[data-page="transfer"] .sc-card {
  max-width: none; width: 100%; gap: var(--sp-4);  /* full width; keep the default gray card box */
}
/* Title reads as a proper section heading (big), keeping the "?" help beside it. */
.sc-cards[data-page="activity"] .sc-card-head,
.sc-activity-slot .sc-card-head,
.dash-half .sc-cards[data-page="sweep"] .sc-card-head,
.dash-half .sc-cards[data-page="transfer"] .sc-card-head { gap: var(--sp-3); }
.sc-cards[data-page="activity"] .sc-card-title,
.sc-activity-slot .sc-card-title,
.dash-half .sc-cards[data-page="sweep"] .sc-card-title,
.dash-half .sc-cards[data-page="transfer"] .sc-card-title {
  font-size: var(--title-size); font-weight: 600; line-height: var(--title-line);
}

/* Dashboard (hub): put the shared wallet roster a touch below the content when
   it lands in the hub's bottom bar slot. */
.tab-panel[data-tab="hub"] .sc-bar-slot { margin-top: 16px; }

/* Dashboard (hub): the half/half Sweep + Transfer row between the Activity box
   and the Logs box — two equal columns, stacking to full width on narrow canvas
   (mirrors the .grid utility). Each .dash-half is a chromeless panel; the visible
   gray box is the .sc-card moved into it (styled Activity-identical above). */
.dash-2col { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--sp-4); align-items: stretch; }
@media (max-width: 960px) { .dash-2col { grid-template-columns: 1fr; } }

/* A half-width box only has room for two metric columns. The 4-up → 2x2 switch
   was viewport-driven (max-width 1240px), so on a wide screen Sweep's four
   tiles were crushed into a half column; bind it to the container instead. */
.dash-half .act-cards { grid-template-columns: 1fr 1fr; }
.dash-half .act-card:nth-child(3) { border-left: none; }
.dash-half .act-card:nth-child(n+3) { border-top: 1px solid var(--border); }
/* …and the same 2-up edge rule, so a half-card's tiles line up with its title. */
.dash-half .act-cards > .act-card:nth-child(odd) { --act-pad-l: 0px; }
.dash-half .act-cards > .act-card:nth-child(even) { --act-pad-r: 0px; }

/* Hub uniform box height — the Activity, Sweep and Transfer boxes share ONE
   fixed height (--dash-box-h). Sweep==Transfer via the grid stretching each
   .dash-half column + the half card flexing to fill it; Activity is pinned to
   that same height. The card BODY becomes the scroll region (flex:1 + overflow)
   so tall content fits without clipping or pushing the box past --dash-box-h. */
.dash-half { display: flex; gap: var(--sp-4); }   /* same 16px rhythm as .stack and .bchart-tri */
.dash-half > .sc-cards { flex: 1 1 auto; }
/* Activity is NOT on the shared height. Sweep and Transfer sit side by side in
   .dash-2col, so they must match EACH OTHER or the row looks broken — that is
   what --dash-box-h is for. Activity is a full-width box on its own row with
   nothing to match, and pinning it to Sweep's floor left 156px of empty card
   under its controls (measured: it needs 556 of 712). It sizes to content now. */
.dash-half .sc-cards[data-page="sweep"] .sc-card,
.dash-half .sc-cards[data-page="transfer"] .sc-card { height: var(--dash-box-h); }
.dash-half .sc-cards[data-page="sweep"] .sc-card-body,
.dash-half .sc-cards[data-page="transfer"] .sc-card-body {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
}
}  /* end @layer overrides */

@layer components {
/* Transfer tree visualizer — a themed container matching the balance-chart
   cards: recessed --background fill (like the drop slots in this card), a 1px
   hairline border, squared corners. Sits inside the Transfer card body under the
   Disperse|Collect toggle; window.mountTransferTree draws an inline SVG that
   fills it. flex:0 0 auto keeps its height inside the scrolling card body. */
/* Tiny flow glyph tucked into the top-right of the Transfer "From" (sender) box.
   Sized to match the chart's line/dot language (see transfer-tree.js). Absolute
   + pointer-events:none so it's purely decorative and never blocks the slot. */
.sc-io-box { position: relative; }
.transfer-tree {
  position: absolute;
  top: 4px; right: 4px;
  width: 76px; height: 56px;
  pointer-events: none;
  overflow: visible;
}
.transfer-tree svg { display: block; width: 100%; height: 100%; overflow: visible; }
}  /* end @layer components */

@layer overrides {
/* ============================================================
   CLEAN PASS — scoped in-page overrides that can't be made in place.
   Kept minimal; the .app-main token block does the heavy lifting.
   ============================================================ */
/* Balances chart hero figure: its rule is injected into <head> by charts.js
   (not editable here), so scope under .app-main to win on specificity and
   bring the hero number down to the shared ~30px hero size (was 40px). */
.app-main .bchart-value { font-size: var(--fig-hero); font-weight: 600; }
/* Its segmented controls read as one control tier (13px/500) with the others. */
.app-main .bchart-seg button,
.app-main .bchart-tf button { font-weight: 500; }

/* ---- Dashboard balance triptych (TOTAL ETH / TOTAL <TOKEN> / <PAIR> VALUE) ---
   charts.js injects .bchart-tri-* into <head>, so its rhythm is corrected here
   (scoped under .app-main to win on specificity). The tile was a stack of five
   loosely-spaced blocks with a 120px empty chart in the middle, so the
   empty-state line floated in a void. One scale now:
     caption (fixed height) → figure (fixed height) → chart → timeframe → range
   and all three tiles are identical, so their timeframe pill rows line up. */
/* The three cards must be ONE height. The grid was align-items:start, so each
   card sized to its own content — and they do not render the same parts: a card
   with history draws canvas + timeframe bar + footer, while one without draws a
   short "Not enough history" line instead. Same row, three heights.
   stretch makes the row size to the tallest; the card is a column flexbox so
   its empty state can fill the space rather than leaving a hole. */
.app-main .bchart-tri { gap: var(--sp-4); align-items: stretch; }
/* The three balance boxes were transparent, so they floated on the page with
   only a border — the one thing in the hub that was NOT a card. On the preset's
   ramp that reads as a hole. Put them on the Card plane like every other box. */
.app-main .bchart-tri-card {
  padding: var(--pad-card);             /* same inset as .sc-card */
  background: var(--card);
  border: 1px solid var(--border);
  display: flex; flex-direction: column; min-height: 0;
}
/* the chart area is what absorbs the slack, so all three feet line up */
.app-main .bchart-tri-canvas-wrap { flex: 1 1 auto; }
/* an empty card centres its message in the space a chart would have taken */
.app-main .bchart-tri .bchart-empty {
  flex: 1 1 auto; display: flex; align-items: center; justify-content: center;
  text-align: center;
}
/* caption = EXACTLY the Activity .act-cap micro-eyebrow (charts.js says it
   should match, then sets 10px against .act-cap's 9px). One line, always. */
.app-main .bchart-tri-cap {
  font-size: var(--eyebrow-cap-size); letter-spacing: .1em; font-weight: var(--eyebrow-weight);
  min-height: var(--eyebrow-cap-size);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* ONE value-row pattern on all three tiles. The caption already names the unit
   ("TOTAL ETH" / "TOTAL USDC" / "ETH / USDC VALUE"), and the third tile's unit
   is suppressed in JS — which left cards 1-2 reading "— ETH" and card 3 a bare
   "—". Dropping the redundant unit chip makes all three caption + figure. */
.app-main .bchart-tri-fig-line { margin-top: 10px; min-height: 24px; gap: var(--sp-1); }
.app-main .bchart-tri-unit { display: none; }
/* Chart, then the timeframe pills, then the range footer — on the same steps as
   the Activity tiles (figure 10 / chart 14 / controls 12). */
.app-main .bchart-tri-canvas-wrap { min-height: 104px; margin-top: 14px; }
.app-main .bchart-tri-tfbar { margin-top: 12px; }
.app-main .bchart-tri-card .bchart-foot { margin-top: 10px; min-height: 14px; }
.app-main .bchart-tri .bchart-empty { font-size: var(--body-size); padding: 0 12px; }
/* timeframe pills: the compact control rung — the comment already claimed "one
   compact control rung" while hardcoding 22px, which is not a rung at all. */
.app-main .bchart-tf button { height: var(--control-h-sm); padding: 0 9px; line-height: 1; }
}  /* end @layer overrides */

