/* =============================================
   TYPE / SPACING / RADIUS SCALES -- theme-independent (unlike the
   color tokens below, these don't change with the theme), so
   they live in a plain :root rather than per [data-theme]. Consolidated
   from ~60 raw one-off rem values that had organically drifted across
   different authoring passes (e.g. six different "small secondary
   text" sizes within 0.1rem of each other, used interchangeably across
   unrelated components) -- each tier below is anchored on whichever
   raw value was already most common in that range, so the vast
   majority of call sites render at the exact same size as before;
   only the minority sitting a fraction of a rem off actually move,
   snapping onto the nearest real tier instead of their own arbitrary
   number. Two categories of value are deliberately left as plain
   literals, not part of either scale: --brand-sub's own two sizes
   (0.55rem in the nav header, 0.6rem in the larger auth-screen lockup)
   are a proportional pair scaled to their own differently-sized
   brand-mark, not general-purpose text; and .toast-message's 0.05rem
   top padding and the xray-link-input's 2.6rem right padding are each
   a one-off optical/layout nudge, not a reusable rhythm value either.
   ============================================= */
:root {
  --font-sans:
    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
    Arial, sans-serif;
  --font-mono:
    ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;
  --text-xs: 0.72rem;
  --text-sm: 0.78rem;
  --text-base: 0.85rem;
  --text-md: 0.95rem;
  --text-lg: 1.1rem;
  --text-xl: 1.3rem;
  --text-2xl: 1.5rem;
  --text-display: clamp(3rem, 7vw, 5.8rem);
  --content-width: 75rem;

  --space-1: 0.2rem;
  --space-2: 0.3rem;
  --space-3: 0.4rem;
  --space-4: 0.5rem;
  --space-5: 0.65rem;
  --space-6: 0.85rem;
  --space-7: 1.1rem;
  --space-8: 1.75rem;
  --space-9: 2rem;
  --space-10: 3rem;

  --radius-xs: 0.35rem;
  --radius-sm: 0.4rem;
  --radius-md: 0.5rem;
  --radius-lg: 0.65rem;
  --radius-xl: 0.75rem;
  --radius-2xl: 1rem;

  /* Same consolidation as the type/space/radius scales above, for
     motion instead of size -- ~30 call sites had organically settled
     on 0.15s for nearly every hover/interactive transition already
     (good instinct, just never named), while two continuous spinners
     had quietly drifted to different rotation speeds (0.7s vs 0.8s)
     for no real reason. Four tiers, each tied to what kind of change
     it's covering rather than to any one component:
       fast  -- hover/interactive state changes (color, background,
                border, small transforms on buttons/icons/carets).
       base  -- bigger UI movement settling into place (modal/toast
                enter-exit) and the default "fade content in" tier.
       slow  -- a value visibly changing (chart lines, the gauge arc).
       spin  -- one full rotation for a continuous spinner.
     pulse/loading-pulse are their own named durations rather than
     folded into the scale above -- each is tied to one specific,
     already-distinctively-named animation (status-dot-pulse,
     status-dot-loading-pulse), not a general-purpose tier other
     components would ever reach for. */
  --duration-fast: 0.15s;
  --duration-base: 0.2s;
  --duration-slow: 0.3s;
  --duration-spin: 0.7s;
  --duration-pulse: 1.4s;
  --duration-loading-pulse: 1.2s;

  --ease: ease;
  --ease-standard: ease;
  --ease-out: ease-out;
  --ease-in-out: ease-in-out;
  --ease-linear: linear;
}

/* =============================================
   DESIGN TOKENS -- three lightness modes, one
   brand hue (green) throughout.
   ============================================= */
[data-theme="dark"] {
  --bg-primary: #0f1417;
  --bg-secondary: #1a2126;
  --text-primary: #f5f2ee;
  --text-secondary: #9aa5ab;
  --accent: #22c55e;
  --accent-hover: #16a34a;
  --accent-rgb: 34, 197, 94;
  --accent-subtle: rgba(34, 197, 94, 0.14);
  --accent-ink: #07140b;
  --border: rgba(255, 255, 255, 0.08);
  /* Raised-chip fill for icon-btn/badge -- lightens against a dark
     page background. light inverts this to a
     dark tint instead; see its own comment below. */
  --surface-overlay: rgba(255, 255, 255, 0.06);
  --surface-overlay-strong: rgba(255, 255, 255, 0.12);
  --glass-bg: rgba(26, 33, 38, 0.7);
  --danger: #ef4444;
  --danger-subtle: rgba(239, 68, 68, 0.14);
  --success: #22c55e;
  --success-subtle: rgba(34, 197, 94, 0.14);
  --warn: #eab308;
  --warn-subtle: rgba(234, 179, 8, 0.14);
}

[data-theme="black"] {
  --bg-primary: #000000;
  --bg-secondary: #121212;
  --text-primary: #ffffff;
  --text-secondary: #8a8a8a;
  --accent: #4ade80;
  --accent-hover: #22c55e;
  --accent-rgb: 74, 222, 128;
  --accent-subtle: rgba(74, 222, 128, 0.16);
  --accent-ink: #07140b;
  --border: rgba(255, 255, 255, 0.14);
  /* Same white overlay as dark, but a notch stronger -- true black
     needs more delta to read as a raised chip than dark's own
     near-black bg does (border above gets the same bump). */
  --surface-overlay: rgba(255, 255, 255, 0.09);
  --surface-overlay-strong: rgba(255, 255, 255, 0.16);
  --glass-bg: rgba(10, 10, 10, 0.75);
  --danger: #f87171;
  --danger-subtle: rgba(248, 113, 113, 0.14);
  --success: #4ade80;
  --success-subtle: rgba(74, 222, 128, 0.14);
  --warn: #facc15;
  --warn-subtle: rgba(250, 204, 21, 0.14);
}

[data-theme="light"] {
  --bg-primary: #f7faf8;
  --bg-secondary: #ffffff;
  --text-primary: #132218;
  --text-secondary: #5d7064;
  --accent: #15803d;
  --accent-hover: #166534;
  --accent-rgb: 21, 128, 61;
  --accent-subtle: rgba(21, 128, 61, 0.1);
  --accent-ink: #ffffff;
  --border: rgba(35, 70, 46, 0.15);
  --surface-overlay: rgba(19, 34, 24, 0.04);
  --surface-overlay-strong: rgba(19, 34, 24, 0.1);
  --glass-bg: rgba(255, 255, 255, 0.9);
  --danger: #dc2626;
  --danger-subtle: rgba(220, 38, 38, 0.12);
  --success: #15803d;
  --success-subtle: rgba(21, 128, 61, 0.12);
  --warn: #a16207;
  --warn-subtle: rgba(161, 98, 7, 0.12);
}

[data-theme="light"] { color-scheme: light; }
[data-theme="dark"], [data-theme="black"] { color-scheme: dark; }

[data-theme] {
  --bg: var(--bg-primary);
  --bg-raised: var(--bg-secondary);
  --surface: var(--bg-secondary);
  --surface-soft: var(--surface-overlay);
  --glass: var(--glass-bg);
  --text: var(--text-primary);
  --text-soft: var(--text-secondary);
  --muted: var(--text-secondary);
  --border-strong: var(--surface-overlay-strong);
  --accent-soft: var(--accent-subtle);
  --success-soft: var(--success-subtle);
  --warning: var(--warn);
  --warning-soft: var(--warn-subtle);
  --danger-soft: var(--danger-subtle);
  --shadow-float: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.3);
}

.theme-btn:hover, .theme-btn.active,
.lang-btn:hover, .lang-btn.active {
  border-color: var(--accent) !important;
  box-shadow: 0 0 0 0.0625rem var(--accent);
}

[data-theme-select],
[data-locale-select],
select[data-enhanced="true"] {
  display: none !important;
}

.visually-hidden {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  white-space: nowrap !important;
  border: 0 !important;
}

/* Persian needs its own font stack -- the Latin system-ui stack has
   no real Farsi glyphs to fall back on. :lang(fa) (not [dir="rtl"])
   ties this to the language it's actually for, since that's also
   what i18n.js's loadPersianFont checks before ever fetching the
   font -- see its own comment for why the font itself is registered
   from JS (FontFace + document.fonts.add) instead of a plain
   @font-face here, which would let a browser decide to prefetch it
   for every visitor regardless of language. Tahoma/IRANSans/system-ui
   are the fallback while Vazirmatn is still loading (or if it fails
   to), not the primary choice -- previously they came first, back
   when nothing here actually bundled Vazirmatn to load. */
:lang(fa) {
  font-family: "Vazirmatn", Tahoma, "IRANSans", system-ui, sans-serif;
}

/* =============================================
   RESET & BASE
   ============================================= */
*, *::before, *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* Every scrollbar in the app (.content, .modal, .dropdown-menu-wide,
   .table-wrap, .checkbox-list, ...) stays hidden -- scrolling itself
   is untouched, only the visible track/thumb chrome a browser draws
   for it. scrollbar-width covers Firefox, the pseudo-element covers
   Chrome/Safari/newer Edge, -ms-overflow-style covers old Edge/IE. */
* {
  scrollbar-width: none;
  -ms-overflow-style: none;
}
*::-webkit-scrollbar {
  display: none;
}

/* One consistent keyboard-focus ring, in the brand's own accent color,
   for every custom-styled interactive element (buttons, tabs, custom
   selects, checkboxes, nav links, ...) -- previously the only state in
   this whole hand-styled system that fell back to the browser's plain
   native outline instead. :focus-visible (not :focus) so a mouse click
   doesn't trigger it, only actual keyboard navigation. .form-input is
   the one exception: it already gets its own clear accent-colored
   border-color change on focus (see FORMS below), so this would just
   double up on the same signal there. */
:focus-visible {
  outline: 0.125rem solid var(--accent);
  outline-offset: 0.125rem;
}
.form-input:focus-visible {
  outline: none;
}

html {
  color-scheme: light dark;
  background: var(--bg-primary);
  scroll-behavior: smooth;
}

body {
  font-family: var(--font-sans);
  background-color: var(--bg-primary);
  color: var(--text-primary);
  min-height: 100vh;
  min-height: 100dvh; /* see .page's own comment on why dvh, not vh */
  font-size: var(--text-base);
  line-height: 1.55;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}
/* Setting overflow-x alone (with overflow-y left unset) makes the
   *other* axis compute to auto, not visible -- so this was quietly
   making body its own scroll container instead of the real viewport,
   which is what made position: sticky/fixed children (.nav,
   .bottom-nav) glitch and lag during scroll instead of staying put.
   The horizontal-overflow clipping this provided is no longer needed
   now that the actual sources of horizontal overflow (long node/probe
   ids -- see .mono/.id-row above) wrap instead of overflowing. */
body.modal-open { overflow: hidden; }
/* .content, not body, is the app shell's actual scrolling element
   (see .page's own comment) -- locking body alone while a modal is
   open would lock an element that was never scrolling in the first
   place and leave the real scrollable region (.content) still able to
   move behind the modal's backdrop. */
body.modal-open .content { overflow: hidden; }

button, input, select, textarea {
  font-family: inherit;
}

a { color: inherit; }

code, .mono {
  font-family: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;
  font-variant-numeric: tabular-nums;
  /* A node/probe id is one long unbroken token -- without this, it
     never wraps and just pushes its flex row wider than the viewport
     on mobile instead of breaking onto a second line. */
  overflow-wrap: anywhere;
}

/* =============================================
   PAGE WRAPPER
   ============================================= */
/* An app shell, not a scrolling document: .page is locked to exactly
   the viewport's height (100vh as an older-browser fallback, 100dvh --
   the real, currently-visible viewport -- as the one that actually
   tracks the mobile browser's address bar animating in/out) and clips
   anything that would make it taller. .nav and .bottom-nav are then
   just plain flex children that never move, and only .content (below)
   scrolls internally -- no position: sticky/fixed juggling needed, and
   since the document itself never scrolls, a mobile browser's own
   "hide the address bar on scroll" heuristic (which watches the
   document/body scroll, not an inner div's) never triggers either. */
.page { display: flex; flex-direction: column; height: 100vh; height: 100dvh; overflow: hidden; }

/* =============================================
   UNFUNDED BANNER (persistent, account-wide notice)
   ============================================= */
.unfunded-banner {
  flex-shrink: 0;
  background: linear-gradient(90deg, var(--danger), var(--accent));
  color: #fff;
  text-shadow: 0 0.0625rem 0.15rem rgba(0, 0, 0, 0.55);
  padding: var(--space-5) var(--space-7);
  font-size: var(--text-base);
  text-align: center;
  line-height: 1.4;
}
.unfunded-banner strong { font-weight: 700; }
.unfunded-banner.banner-warn { background: linear-gradient(90deg, var(--warn), var(--accent)); }
.unfunded-banner.banner-danger { background: var(--danger); }

/* =============================================
   NAV
   ============================================= */
.nav {
  flex-shrink: 0;
  z-index: 1000;
  background: var(--bg-primary);
  border-bottom: 0.0625rem solid var(--border);
}

.nav-container {
  max-width: 75rem;
  margin: 0 auto;
  padding: var(--space-6) var(--space-7);
  display: grid;
  /* Center column is truly centered on the header regardless of how wide
     the brand/right columns are -- justify-content:space-between only
     centers .nav-center when both siblings happen to match width, which
     the brand lockup and the account/settings menu never do. */
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: var(--space-7);
}

.nav-brand {
  display: inline-flex;
  align-items: center;
  justify-self: start;
  gap: var(--space-3);
  font-weight: 700;
  font-size: var(--text-lg);
  letter-spacing: -0.02em;
  white-space: nowrap;
  text-decoration: none;
  color: var(--text-primary);
}

.nav-brand:hover,
.nav-brand:focus,
.nav-brand:active {
  text-decoration: none;
  color: var(--text-primary);
}

.nav-brand .brand-cards,
.nav-brand svg.brand-dot {
  width: 2.2rem;
  height: 1.85rem;
  flex-shrink: 0;
  overflow: visible;
  display: block;
}

.brand-text {
  display: flex;
  flex-direction: column;
  line-height: 1.15;
  text-align: start;
  text-decoration: none;
}

.brand-mark {
  font-weight: 700;
  letter-spacing: -0.02em;
  text-decoration: none;
  color: var(--text-primary);
}

.brand-mark .brand-accent {
  color: var(--accent);
}

.brand-sub {
  font-size: 0.55rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-secondary);
  text-decoration: none;
}

.nav-center {
  display: flex;
  align-items: center;
  justify-self: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.nav-center a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-6);
  border-radius: var(--radius-lg);
  text-decoration: none;
  color: var(--text-secondary);
  font-size: var(--text-base);
  font-weight: 500;
  transition: background var(--duration-fast), color var(--duration-fast);
  white-space: nowrap;
}

.nav-center a .icon { width: 1.05rem; height: 1.05rem; flex-shrink: 0; }
.nav-center a:hover { background: var(--accent-subtle); color: var(--text-primary); }
.nav-center a.active { background: var(--accent-subtle); color: var(--accent); font-weight: 600; }

/* grid-column: 3 is explicit, not just relying on source order --
   pages without a center nav (auth, public invoice/product) only have
   two .nav-container children, and grid auto-placement would otherwise
   drop .nav-right into the middle column instead of the right edge. */
.nav-right { display: flex; align-items: center; grid-column: 3; justify-self: end; gap: var(--space-5); }

/* =============================================
   MAIN CONTENT
   ============================================= */
.content {
  flex: 1;
  /* The one scrolling region in the app shell (see .page's own
     comment) -- everything that used to make the *document* tall
     (an overfilled Overview page for a root account with many nodes/
     probes especially) now only grows this box's internal scroll,
     never the page itself, so .nav/.bottom-nav (siblings, not
     ancestors of this) never have anything to react to. */
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  min-height: 0; /* a flex item's default min-height:auto would otherwise refuse to shrink below its content's height, defeating overflow-y here */
  width: 100%;
  max-width: 75rem;
  margin: 0 auto;
  padding: var(--space-7) var(--space-7) var(--space-10);
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.content-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-5);
  flex-wrap: wrap;
}

#page-actions { display: flex; align-items: center; gap: var(--space-4); }

#page-subheader {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

#page-subheader:empty {
  display: none;
}

#main-view { display: flex; flex-direction: column; gap: var(--space-6); }

/* =============================================
   SPACING UTILITIES (small, one-off overrides only --
   prefer a real component class when a pattern repeats)
   ============================================= */
.mt-sm { margin-top: var(--space-6); }
.mt-md { margin-top: var(--space-8); }
.mb-xs { margin-bottom: var(--space-4); }
.mb-sm { margin-bottom: var(--space-6); }
.mb-md { margin-bottom: var(--space-7); }
.text-center { text-align: center; }

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
  gap: var(--space-8);
  align-items: start;
}
/* Opt-in modifier: lets cards in the same row match each other's
   height (e.g. Account's Credit/Credentials pair) -- still just one
   column on a narrow viewport since auto-fit already collapses the
   grid there, so this never forces two cards side by side on mobile;
   it only changes how same-row cards size once there IS a row. */
.card-grid-stretch { align-items: stretch; }
.btn-block { width: 100%; justify-content: center; }
.btn-row { display: flex; gap: var(--space-4); }
.loading-card { display: flex; justify-content: center; padding: var(--space-10); }

h1 { font-size: var(--text-2xl); font-weight: 700; letter-spacing: -0.02em; }
/* A badge dropped inline into an h1 (e.g. Account's status pill next
   to its own heading) sits on the *text* baseline by default, which
   reads as floating too low against a heading's much taller line box
   than it would next to body text -- middle-align it to the heading's
   own line instead. */
h1 .badge { vertical-align: middle; }
h2 { font-size: var(--text-lg); font-weight: 700; }
p.subtitle { color: var(--text-secondary); font-size: var(--text-md); }

/* =============================================
   GLASS CARD / SURFACES
   ============================================= */
.glass-card {
  background: var(--bg-secondary);
  border: 0.0625rem solid var(--border);
  border-radius: var(--radius-2xl);
  padding: var(--space-8);
  box-shadow: 0 0.5rem 2rem rgba(0, 0, 0, 0.2);
}

.section-label {
  display: block;
  color: var(--muted);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  line-height: 1.3;
  text-transform: uppercase;
}

/* Every icon is now <svg class="icon"><use href="/icons.svg#name">
   (see icons.svg) instead of its own inline path data -- a use
   element's shadow content inherits CSS from the tree it's used in,
   so this rule still styles every icon exactly as it did when the
   path data lived inline here. */
.icon {
  width: 1em; height: 1em;
  display: inline-block;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: -0.125em;
}

/* =============================================
   MONITOR CARDS (Overview)
   ============================================= */
.monitor-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  gap: var(--space-7);
}
.clickable { cursor: pointer; }
.monitor-title { font-size: var(--text-md); font-weight: 600; display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }

/* Cards roomy enough for a real per-node chart. */
.monitor-grid-rich { grid-template-columns: repeat(auto-fill, minmax(22rem, 1fr)); }
.monitor-card-rich { padding: var(--space-7) var(--space-7); cursor: pointer; transition: border-color var(--duration-fast), box-shadow var(--duration-fast); }
/* box-shadow, not transform -- a `transform` on this card creates a
   new containing block for its own chart tooltip (position: fixed,
   see .chart-tooltip), which then positions itself relative to this
   card instead of the real viewport the moment it's hovered -- the
   exact "tooltip shows up in the wrong place in grid view" bug this
   replaced. box-shadow gets the same "lifted" affordance with none of
   that. */
.monitor-card-rich:hover { border-color: var(--accent); box-shadow: 0 0.4rem 1.2rem rgba(0, 0, 0, 0.25); }

/* Overview list view -- one dense row per probe: header line, an
   optional per-node color legend, and a small multi-line latency
   chart (see .overview-chart / renderProbeSparkChart) instead of one
   big per-row card, so scanning dozens of targets at once doesn't
   need scrolling past oversized cards. */
.overview-row { padding: var(--space-5) var(--space-7); }
/* display: inline-block is load-bearing, not decorative -- a plain
   <span> (this element's actual tag everywhere it's used) defaults to
   display: inline, where width/height are simply ignored. This used
   to work anyway wherever .status-dot sat as a *direct* flex child
   (flex auto-blockifies its immediate children), but nesting it one
   level deeper inside .status-dot-updating-ring's own wrapper span
   (see that class's own comment) silently collapsed it to zero size
   there, since that wrapper is a plain inline span, not a flex
   container. Explicit here means it renders correctly regardless of
   whatever happens to contain it next. */
/* background is transitioned here (not just set inline via
   statusDotStyle) so swapping from status-dot-loading's gray to a
   probe/node's real health color -- or later, one real color to
   another, e.g. healthy to degraded on a live-refresh tick -- eases
   in instead of snapping the instant the inline style changes. */
.status-dot { display: inline-block; width: 0.6rem; height: 0.6rem; border-radius: 50%; flex-shrink: 0; transition: background var(--duration-base) var(--ease-standard); }
/* The Nodes/Probes list rows' own dedicated square for the status dot
   -- a fixed-size slot (matching .icon-btn's own footprint, for visual
   balance against the actions column) that centers the dot both ways,
   independent of however many lines the title/subtitle stack next to
   it grows to. Also just deliberately empty space around the dot on
   every side for a future "just polled the API, here's a fresh
   result" glow/pulse (box-shadow around .status-dot) to bloom into
   without disturbing the row's layout. */
.status-dot-slot {
  width: 2rem;
  height: 2rem;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* A spinning ring around the dot itself, in place of a separate
   "updating…" text line next to the version -- see nodes.js's own
   isUpdating. Sized against the dot's own real rendered size (not a
   fixed guess) via padding, so it always reads as "around the dot"
   regardless of the dot's current size rules. */
.status-dot-updating-ring { position: relative; display: inline-flex; padding: var(--space-2); }
.status-dot-updating-ring::before {
  content: ""; position: absolute; inset: 0;
  border-radius: 50%; border: 0.1rem solid transparent; border-top-color: var(--warn);
  animation: spin var(--duration-spin) var(--ease-linear) infinite;
}

/* One-shot glow the instant a node/probe's status dot reflects a real,
   just-happened change (see nodePulseClass/probePulseClass and
   statusDotStyle in app.js) -- ok (green) for a fresh heartbeat/check
   landing, miss (whatever "danger"/"warn" the dot's own color just
   became, via the --pulse-glow custom property statusDotStyle sets
   inline -- not a fixed color here, since "just went stale" should
   glow the same color the dot itself just turned, and that can be
   either depending on which surface it's on) for an active node/probe
   that just went quiet. Plays once on insertion and doesn't loop:
   every Nodes/Probes/Overview render fully replaces these rows, so a
   class present on a freshly-inserted element only ever plays this
   once, never needs manual cleanup, and simply isn't there at all on
   the next render unless another real change just happened again. */
@keyframes status-dot-pulse {
  0% { box-shadow: 0 0 0 0 var(--pulse-glow, transparent); }
  60% { box-shadow: 0 0 0.6rem 0.4rem var(--pulse-glow, transparent); }
  100% { box-shadow: 0 0 0 0 transparent; }
}
.status-dot.pulse-ok, .status-dot.pulse-miss { animation: status-dot-pulse var(--duration-pulse) var(--ease-out); }
/* Shown between buildProbeRowShell's own immediate render (probe
   name/target/buttons/node legend, from the fast GET /v1/probes and
   /v1/nodes responses alone) and hydrateProbeRow filling in the real
   health color once the slower checks fetch actually resolves --
   looping rather than one-shot like pulse-ok/pulse-miss above, since
   "still finding out" is an ongoing state, not a momentary event.
   Replaced outright (className reset) the instant real data lands, so
   this never lingers past that. */
@keyframes status-dot-loading-pulse {
  0%, 100% { opacity: 0.35; }
  50% { opacity: 1; }
}
.status-dot-loading { background: var(--text-secondary); animation: status-dot-loading-pulse var(--duration-loading-pulse) var(--ease-in-out) infinite; }

.node-legend-row { display: flex; flex-wrap: wrap; gap: var(--space-4); margin-top: var(--space-3); }
.node-legend-item { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-xs); color: var(--text-secondary); white-space: nowrap; }
/* Filled = node currently reachable (recent heartbeat); outline-only
   (transparent background, colored border) = it's gone quiet. Fixed
   border width in both states via box-sizing so the circle doesn't
   change size when it toggles between the two. */
.node-legend-dot { width: 0.5rem; height: 0.5rem; border-radius: 50%; flex-shrink: 0; box-sizing: border-box; border: 0.09375rem solid transparent; }

.overview-chart { position: relative; width: 100%; height: 3.75rem; }
.overview-chart canvas { width: 100%; height: 100%; display: block; }
.overview-chart-empty { font-size: var(--text-xs); color: var(--text-secondary); margin: 0; }
/* buildProbeRowShell's placeholder, same height as the real chart it's
   replaced by (see hydrateProbeRow) so nothing jumps when it lands. */
.overview-chart-loading { display: flex; align-items: center; justify-content: center; }

/* A probe with one summary metric (most probers) keeps the original
   single full-width chart; one with several (e.g. system's cpu/mem/
   network) lays them out side by side instead, each its own small
   labeled block -- see probeMetricChartsHtml. */
.probe-metric-charts { margin-top: var(--space-4); }
/* .probe-charts-enter (not baked into .probe-metric-charts itself) is
   what keeps this to a true first-appearance animation -- the loading
   placeholder appearing, or hydrateProbeRow's first swap from that
   placeholder to a real chart -- instead of replaying on every single
   live-refresh tick's routine data update, which reused the same
   "animate on insert" element-replace approach and would otherwise
   look like the whole card were loading again every few seconds. See
   hydrateProbeRow's own isFirstHydrate check for where this gets
   added (or not). */
@keyframes probeChartsIn { from { opacity: 0; transform: translateY(0.25rem); } to { opacity: 1; transform: translateY(0); } }
.probe-charts-enter { animation: probeChartsIn var(--duration-base) var(--ease-out); }
.probe-metric-charts-multi {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: var(--space-6);
}
.probe-metric-charts-multi .probe-metric-chart-block {
  background: var(--bg-secondary); border: 0.0625rem solid var(--border); border-radius: var(--radius-lg); padding: var(--space-4) var(--space-5);
}
.probe-metric-charts-multi .overview-chart { height: 3rem; }

.view-toggle {
  display: inline-flex; gap: var(--space-1);
  background: var(--bg-primary); padding: var(--space-1); border-radius: var(--radius-lg);
  border: 0.0625rem solid var(--border);
}
.view-toggle-btn {
  width: 2rem; height: 2rem; padding: 0; border: none; background: transparent;
  color: var(--text-secondary); border-radius: var(--radius-md); cursor: pointer;
  display: flex; align-items: center; justify-content: center; transition: var(--duration-fast);
}
.view-toggle-btn:hover:not(.active) { background: var(--accent-subtle); color: var(--text-primary); }
.view-toggle-btn.active { background: var(--accent); color: #fff; }
/* Text-labeled variant (1h/6h/12h/24h/Custom) -- same toggle-group
   chrome as the icon buttons above, just auto-width with horizontal
   padding instead of a fixed square, since a label like "Custom" or
   "30d" doesn't fit a 2rem square the way a single icon does. */
.view-toggle-btn-text { width: auto; height: 1.9rem; padding: 0 var(--space-5); font-size: var(--text-sm); font-weight: 600; white-space: nowrap; }

/* The view-window picker (see overviewWindowToggleHtml) -- one
   compact trigger replacing what used to be a row of 1h/6h/12h/24h/
   Custom buttons, which ran wide enough to crowd the rest of the
   toolbar on a phone-width screen. Sized/colored like the view-toggle
   buttons above it, but built on the same .select-custom/.select-panel
   chrome as renderCustomSelect (see its own comment) so it gets the
   same open/close and modal-scroll-lock handling for free. */
.window-picker-trigger {
  display: inline-flex; align-items: center; gap: var(--space-2);
  height: 2rem; padding: 0 var(--space-5); border-radius: var(--radius-lg);
  border: 0.0625rem solid var(--border); background: var(--bg-primary);
  color: var(--text-primary); cursor: pointer;
  font-size: var(--text-sm); font-weight: 600; white-space: nowrap;
  transition: var(--duration-fast);
}
.window-picker-trigger:hover { background: var(--accent-subtle); }
.window-picker.active .window-picker-trigger { border-color: var(--accent); color: var(--accent); }
.window-picker-trigger .icon:first-child { width: 0.9rem; height: 0.9rem; }
.window-picker-trigger .select-caret { width: 0.85rem; height: 0.85rem; }
.window-picker .select-panel { min-width: 8rem; }

/* #page-actions is the one shared toolbar element every page (Probes,
   Nodes, Account, Checks) fills in -- a primary-btn has no explicit
   height of its own (just padding + line-height), which puts it a
   hair taller than the 2rem icon-btn/window-picker-trigger next to it
   once they sit directly adjacent in here, a real and visible
   mismatch (same issue on both the Probes and Nodes toolbars). Scoped
   to this one container rather than the global .primary-btn rule --
   its natural height is fine everywhere else a primary-btn appears
   with no icon-btn beside it to mismatch against. */
#page-actions .primary-btn { height: 2rem; }

/* Full time-proportional timeline + latency chart, used in the probe
   detail modal (see openJobDetailModal). */
.timeline-bar { display: flex; height: 1.25rem; border-radius: var(--radius-sm); overflow: hidden; background: var(--border); }
.timeline-bar .seg { flex: 0 0 auto; }
.chart-wrap { position: relative; width: 100%; height: 14rem; }
.chart-wrap canvas { width: 100%; height: 100%; display: block; }
/* position: fixed, not absolute -- wireChartTooltip positions this in
   real viewport coordinates (see its own comment), not relative to
   its own chart container. That container can be tiny (the overview's
   own ~3.75rem-tall sparkline), and a tooltip listing several nodes
   is routinely taller than that -- position: absolute confined its
   own placement math to that same small local box, which for a probe
   with more than a handful of nodes (or one near the bottom of the
   page) could push it partly off-screen with no way to fully see it.
   The one ancestor in this app with its own transform (.modal-covered
   .modal, which would otherwise trap a fixed descendant inside its
   own bounds instead of the real viewport) also sets pointer-events:
   none, so hover can never reach a tooltip through it regardless. */
.chart-tooltip {
  display: none; position: fixed; background: var(--bg-secondary); border: 0.0625rem solid var(--border);
  border-radius: var(--radius-md); padding: var(--space-3) var(--space-5); font-size: var(--text-xs); color: var(--text-primary);
  pointer-events: none; z-index: 20; white-space: nowrap; box-shadow: 0 0.3rem 1rem rgba(0, 0, 0, 0.3);
}
.chart-tooltip-time { color: var(--text-secondary); margin-bottom: var(--space-1); }
.chart-tooltip-row { display: flex; align-items: center; gap: var(--space-2); line-height: 1.4; }
.chart-tooltip-dot { width: 0.5rem; height: 0.5rem; border-radius: 50%; flex: 0 0 auto; }

/* Every other declared numeric field a prober's module reports, below
   the one headline chart -- smaller and grid-arranged, since there
   can be several (see openJobDetailModal). */
.metric-chart-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); gap: var(--space-7); }
.metric-chart-block { background: var(--bg-secondary); border: 0.0625rem solid var(--border); border-radius: var(--radius-xl); padding: var(--space-6) var(--space-6); }
.metric-chart-label { font-size: var(--text-xs); color: var(--text-secondary); text-transform: capitalize; margin-bottom: var(--space-3); }
.chart-wrap-small { height: 6rem; }
/* A per-node value table (renderTableCard) inside one of the small
   metric-chart-grid cards above -- the global `table` rule's 40rem
   min-width (sized for the node-metrics table) would force an
   oversized horizontal scroll inside a ~16rem card, so this overrides
   it back down to just fitting the card. */
.metric-table-wrap table { width: 100%; min-width: 0; border-collapse: collapse; font-size: var(--text-base); }
.metric-table-wrap td { padding: var(--space-2) 0; }
/* Same override as .metric-table-wrap above, for runResultsTableHtml's
   own short (4-column, node-count-many-rows) table -- the generic
   `table` rule's 40rem min-width is tuned for the much wider node-
   metrics table and just forces an unnecessary horizontal scroll here. */
.run-results-table-wrap table { min-width: 0; }
.metric-table-wrap td:first-child { color: var(--text-secondary); }
.metric-table-wrap tr + tr td { border-top: 0.0625rem solid var(--border); }

/* Probe detail modal tabs -- only rendered at all once a module
   declares more than one field `group` (see openJobDetailModal); a
   simple prober with one or zero groups just gets the flat
   .metric-chart-grid above instead, no tab chrome. */
.detail-tabs { display: flex; gap: var(--space-3); margin-bottom: var(--space-6); flex-wrap: wrap; }
.detail-tab {
  padding: var(--space-3) var(--space-6); border: 0.0625rem solid var(--border); background: transparent;
  color: var(--text-secondary); border-radius: var(--radius-md); font-size: var(--text-base); font-weight: 600;
  text-transform: capitalize; cursor: pointer; transition: var(--duration-fast);
}
.detail-tab:hover:not(.active) { background: var(--accent-subtle); color: var(--text-primary); }
.detail-tab.active { background: var(--accent); color: #fff; border-color: var(--accent); }
.metric-tab-panel.hidden { display: none; }

/* Per-probe field customization (see openFieldPickerModal) -- every
   candidate field, selected ones first in their saved order, with a
   checkbox to toggle and a pair of up/down buttons to reorder. */
.field-picker-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.field-picker-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-6);
  padding: var(--space-4) var(--space-6); background: var(--bg-secondary); border: 0.0625rem solid var(--border); border-radius: var(--radius-lg);
}
.field-picker-label { display: flex; align-items: center; gap: var(--space-4); font-size: var(--text-base); text-transform: capitalize; cursor: pointer; }
.field-picker-move { display: flex; gap: var(--space-2); flex-shrink: 0; }
.field-picker-move .icon-btn { width: 1.6rem; height: 1.6rem; font-size: var(--text-base); }
/* Same row shape as .field-picker-label (see openOverviewCustomizeModal,
   which reuses .field-picker-list/-row/-move as-is), minus the
   capitalize -- fine for fixed English field names, wrong for
   arbitrary user-set probe names/targets. */
.probe-picker-label { display: flex; align-items: center; gap: var(--space-4); font-size: var(--text-base); cursor: pointer; }
/* A probe list can run to dozens of rows, unlike a probe's own (always
   small) field list -- caps it rather than growing the modal
   unbounded. */
.probe-picker-list { max-height: 20rem; overflow-y: auto; }

/* A module field declared display: gauge -- a plain SVG radial
   progress ring (no chart library), showing the latest sample as a
   fraction of the field's bound (or its own observed max if
   unbounded). */
.gauge-wrap { position: relative; width: 5.5rem; height: 5.5rem; margin: 0 auto; }
.gauge-svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.gauge-track { fill: none; stroke: var(--border); stroke-width: 8; }
.gauge-arc { fill: none; stroke: var(--accent); stroke-width: 8; stroke-linecap: round; transition: stroke-dashoffset var(--duration-slow); }
.gauge-value { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: var(--text-base); }

/* A module field declared display: stat -- just the latest value, no
   graph (e.g. uptime, where a wiggly line isn't meaningful). */
.stat-card-value { font-size: var(--text-xl); font-weight: 700; text-align: center; padding: var(--space-6) 0; }

/* =============================================
   STAT TILES
   ============================================= */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 1px;
  background: var(--border);
  border: 0.0625rem solid var(--border);
  border-radius: var(--radius-2xl);
  overflow: hidden;
}

.stat-tile { background: var(--bg-secondary); padding: var(--space-7) var(--space-7); }
.stat-tile .label { font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-secondary); font-weight: 600; }
.stat-tile .value { font-size: var(--text-2xl); font-weight: 700; margin-top: var(--space-2); font-variant-numeric: tabular-nums; }
.stat-tile .value small { font-size: 0.65em; color: var(--text-secondary); font-weight: 500; }

/* A row-style stat -- label left, value right on the same line -- for
   a stack of related figures (e.g. Credit's balance/hourly/monthly)
   that reads better as rows than as tiles in a repeating grid. */
.lead-stat { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-7); }
.lead-stat .label { font-size: var(--text-base); color: var(--text-secondary); font-weight: 600; }
.lead-stat .value { font-size: var(--text-lg); font-weight: 700; font-variant-numeric: tabular-nums; }
.stat-tile .value-badge { font-size: var(--text-lg); }

/* =============================================
   BUTTONS
   ============================================= */
.primary-btn,
.secondary-btn,
.ghost-btn,
.danger-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: 0.625rem 1.25rem;
  border-radius: var(--radius-lg);
  font-size: var(--text-sm);
  font-weight: 650;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  transition: background var(--duration-fast), border-color var(--duration-fast), transform var(--duration-fast), box-shadow var(--duration-fast), color var(--duration-fast);
}

.primary-btn {
  background-color: var(--accent);
  color: var(--accent-ink, #ffffff);
  border: 0.0625rem solid var(--accent);
  box-shadow: 0 0.15rem 0.5rem rgba(var(--accent-rgb), 0.25);
}
.primary-btn:hover:not(:disabled) {
  background-color: var(--accent-hover);
  border-color: var(--accent-hover);
  transform: translateY(-0.0625rem);
  box-shadow: 0 0.25rem 0.75rem rgba(var(--accent-rgb), 0.35);
}
.primary-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; box-shadow: none; }

.secondary-btn {
  background: var(--surface-overlay);
  color: var(--text-primary);
  border: 0.0625rem solid var(--border);
  box-shadow: 0 0.0625rem 0.125rem rgba(0, 0, 0, 0.05);
}
.secondary-btn:hover:not(:disabled) {
  background: var(--surface-overlay-strong);
  border-color: var(--border-strong);
  color: var(--text-primary);
  transform: translateY(-0.0625rem);
}
.secondary-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; box-shadow: none; }

.ghost-btn {
  background: transparent;
  color: var(--text-primary);
  border: 0.0625rem solid var(--border);
}
.ghost-btn:hover:not(:disabled) {
  background: var(--surface-overlay);
  border-color: var(--border-strong);
  transform: translateY(-0.0625rem);
}
.ghost-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; box-shadow: none; }

.danger-btn {
  background: var(--danger-subtle);
  color: var(--danger);
  border: 0.0625rem solid var(--danger);
}
.danger-btn:hover:not(:disabled) {
  background: var(--danger);
  color: #ffffff;
  transform: translateY(-0.0625rem);
}
.danger-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; box-shadow: none; }

.primary-btn.danger-btn {
  background: var(--danger);
  color: #ffffff;
  border-color: var(--danger);
  box-shadow: 0 0.15rem 0.5rem rgba(239, 68, 68, 0.25);
}
.primary-btn.danger-btn:hover:not(:disabled) {
  background: #dc2626;
  border-color: #dc2626;
  box-shadow: 0 0.25rem 0.75rem rgba(239, 68, 68, 0.35);
  transform: translateY(-0.0625rem);
}

.icon-btn {
  width: 2rem; height: 2rem; flex-shrink: 0;
  background: var(--surface-overlay);
  border: 0.0625rem solid var(--border);
  border-radius: 50%;
  color: var(--text-secondary);
  font-size: var(--text-md);
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background var(--duration-fast), color var(--duration-fast);
}
.icon-btn:hover { color: var(--text-primary); background: var(--surface-overlay-strong); }
.icon-btn:disabled { opacity: 0.4; cursor: not-allowed; }

/* Update actions specifically -- a node with a real update available
   (or the "update everything outdated" button above the list) gets a
   yellow-ish tint instead of the neutral icon-btn background, so it
   reads as "there's something to act on here" at a glance. */
.icon-btn-warn { background: var(--warn-subtle); color: var(--warn); border-color: var(--warn-subtle); }
.icon-btn-warn:hover { background: var(--warn-subtle); filter: brightness(1.15); color: var(--warn); }

/* The account menu's own trigger -- unlike every other .icon-btn (a
   fixed circular icon-only affordance), this one also carries the
   current account's own display name, so it needs to grow into a
   pill instead of staying a fixed-size circle. */
.dropdown-trigger {
  width: auto;
  border-radius: 999px;
  padding-block: var(--space-2);
  padding-inline: var(--space-2) var(--space-6);
  gap: var(--space-3);
}
.dropdown-trigger-name {
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--text-primary);
  max-width: 9rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Empty until renderAccountChrome() fills it in (see app.js) --
   collapses back to the plain icon-only circle rather than leaving an
   awkward gap before boot() resolves or if it's ever left blank. */
.dropdown-trigger-name:empty { display: none; }
.dropdown-trigger:has(.dropdown-trigger-name:empty) {
  width: 2rem;
  padding: 0;
}

/* An icon-only affordance with no border/background of its own --
   for a copy button sitting directly beside plain text (e.g. Account
   ID) rather than one more chrome-heavy control in a row of real
   buttons. Only picks up a background on hover, as the hint that it's
   interactive. */
.icon-btn-plain {
  width: 1.75rem; height: 1.75rem; flex-shrink: 0;
  background: none; border: none; border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-size: var(--text-md);
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background var(--duration-fast), color var(--duration-fast);
}
.icon-btn-plain:hover { color: var(--text-primary); background: var(--surface-overlay-strong); }

.id-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap; }
/* Flex items default to a min-width equal to their own content's
   intrinsic width, which overrides any wrapping/overflow-wrap on a
   long id/token value -- without this, that content still forces the
   row (and the page, on mobile) wider instead of actually wrapping. */
.id-row > span:last-child, .id-row-value { min-width: 0; }
.id-row > span:last-child { text-align: end; }
/* The value and its copy button are one unit on the far right of the
   row -- without this, a 3-child space-between (label, value, button)
   would spread the value out into the middle of the row instead of
   sitting flush against the button that acts on it. */
.id-row-value { display: flex; align-items: center; gap: var(--space-2); }

.divider { border: none; border-top: 0.0625rem solid var(--border); margin: var(--space-6) 0; }

.link-btn {
  background: none; border: none; color: var(--accent);
  font-weight: 600; font-size: var(--text-base); cursor: pointer; padding: 0;
}
.link-btn:hover { text-decoration: underline; }

/* =============================================
   BADGES (status pills)
   ============================================= */
.badge {
  display: inline-flex; align-items: center; gap: var(--space-2);
  background: var(--surface-overlay);
  padding: var(--space-1) var(--space-4);
  border-radius: var(--radius-xs);
  font-size: var(--text-xs);
  font-weight: 600;
  white-space: nowrap;
}
.badge::before {
  content: ""; width: 0.4rem; height: 0.4rem; border-radius: 50%;
  background: currentColor; flex-shrink: 0;
}
.badge-active, .badge-ok { background: var(--success-subtle); color: var(--success); }
.badge-fail, .badge-suspended { background: var(--danger-subtle); color: var(--danger); }
.badge-paused, .badge-pending { background: var(--warn-subtle); color: var(--warn); }
/* No background override -- .badge's own default (--surface-overlay) is already the neutral look. */
.badge-neutral { color: var(--text-secondary); }
.badge-accent { background: var(--accent-subtle); color: var(--accent); }
/* A node update actually in flight -- the spinner replaces the usual
   static status dot so this reads as "happening right now", not just
   another static state like the other badges. */
.badge-updating::before { display: none; }
.badge-updating .spinner-xs { border-color: currentColor; border-top-color: transparent; }

/* =============================================
   LIST / TABLE
   ============================================= */
.list-container {
  display: grid;
  gap: var(--space-5);
}

.list-item {
  padding: var(--space-7);
  border: 0.0625rem solid var(--border);
  border-radius: var(--radius-xl);
  background: var(--bg-secondary);
  transition: border-color var(--duration-fast), background var(--duration-fast), transform var(--duration-fast);
}
.list-item:last-child { border-bottom: 0.0625rem solid var(--border); }
.list-item:hover { border-color: var(--border-strong); background: var(--glass-bg); }
.list-item-warn { background: var(--warn-subtle); }
.list-item-warn:hover { background: var(--warn-subtle); filter: brightness(1.1); }

.list-item-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  gap: var(--space-6);
  width: 100%;
}
.list-item-lead {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  min-width: 3.5rem;
  padding-top: var(--space-1);
}
.list-item-info { flex: 1; min-width: 0; }
.list-item-heading {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-6);
  min-width: 0;
}
.list-item-heading-copy { min-width: 0; }
.list-item-info h3 {
  margin: 0;
  overflow: hidden;
  color: var(--text-primary);
  font-size: var(--text-lg);
  font-weight: 650;
  line-height: 1.25;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.list-item-info small {
  display: block;
  margin-top: var(--space-2);
  color: var(--text-secondary);
  font-size: var(--text-sm);
}
.list-item-meta {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: var(--space-2);
  flex-shrink: 0;
}
.list-item-body {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-6);
  margin-top: var(--space-6);
}
.list-item-body .id-row {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2);
  min-width: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
}
.list-item-body .id-row > span {
  display: inline;
  min-width: 0;
}
.list-item-body .id-row > span:first-child {
  color: var(--text-secondary);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
}
.list-item-body .id-row > span:first-child::after { content: ":"; }
.list-item-body .id-row > span:last-child {
  overflow-wrap: anywhere;
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-weight: 600;
  line-height: 1.35;
  text-align: start;
}
.list-item-body .id-row:has(.asset-chip-list) { flex-basis: 100%; align-items: flex-start; }
.list-item-body .id-row:has(.asset-chip-list) > span:last-child { flex: 1 1 auto; }
/* Plain text, not a badge -- sits right after the version string, only
   for a node's own owner with a real update pending/available (see
   renderNodes's updateHint). */
/* Installed bundled-module versions, tacked on after the node's own
   version -- plain reference text like the version itself, not a
   call-to-action, so it gets no special color of its own. */
.node-module-versions::before { content: "\00b7"; margin-inline-end: var(--space-3); opacity: 0.6; }
.update-hint { color: var(--warn); font-weight: 600; }
.list-item-actions {
  display: flex;
  align-items: flex-start;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: var(--space-2);
  flex-shrink: 0;
  padding-top: var(--space-1);
}
.list-item-actions .ghost-btn { white-space: nowrap; }

.asset-icon {
  position: relative;
  display: inline-flex;
  width: 3.5rem;
  height: 3.5rem;
  flex: 0 0 3.5rem;
  align-items: center;
  justify-content: center;
}
.asset-icon-image img,
.asset-icon-coin {
  display: block;
  width: 3.25rem;
  height: 3.25rem;
  border-radius: 50%;
  object-fit: cover;
}
.asset-icon-image img { border: 0.0625rem solid var(--border); }
.asset-icon-coin { display: grid; place-items: center; }
.asset-icon-coin .asset-glyph { width: 2.1rem; height: 2.1rem; }
.asset-icon-badge {
  display: none !important;
}

.network-icon {
  display: inline-flex;
  width: 3.5rem;
  height: 3.5rem;
  flex: 0 0 3.5rem;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border: 0.0625rem solid var(--border);
  border-radius: 50%;
  background: var(--bg-primary);
}
.network-icon img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.asset-chip-list { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.asset-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  max-width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 0.0625rem solid var(--border);
  border-radius: 999px;
  background: var(--bg-primary);
  color: var(--text-primary);
  font-size: var(--text-xs);
  font-weight: 650;
}
.asset-chip img { width: 1.15rem; height: 1.15rem; border-radius: 50%; object-fit: cover; }

/* ==========================================================================
   Checkout pages (public invoice + public product) -- unified with design system
   ========================================================================== */
.checkout-page {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg-primary);
  color: var(--text-primary);
}

.checkout-content {
  flex: 1;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: var(--space-6) var(--space-4) var(--space-10);
}

.checkout-shell {
  width: 100%;
  max-width: 32rem;
  margin: var(--space-2) auto 0;
}

.checkout-product-page .checkout-shell,
.checkout-invoice-page .checkout-shell {
  max-width: 32rem;
}

.checkout-card {
  position: relative;
  border-radius: var(--radius-2xl);
  background: var(--bg-secondary);
  border: 0.0625rem solid var(--border);
  box-shadow: 0 0.5rem 2.5rem rgba(0, 0, 0, 0.25);
  padding: clamp(1.25rem, 4vw, 2rem);
  animation: fadeIn var(--duration-base) var(--ease-standard);
}

.checkout-card.state-paid {
  border-color: var(--success);
  box-shadow: 0 0.5rem 2.5rem rgba(0, 0, 0, 0.25), 0 0 2rem var(--success-subtle);
}

.checkout-card.state-expired {
  border-color: var(--border);
  opacity: 0.95;
}

/* Card Topbar: branding on left, preferences on right */
.checkout-card-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding-bottom: var(--space-4);
  margin-bottom: var(--space-5);
  border-bottom: 0.0625rem solid var(--border);
}

.checkout-brand-interactive {
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  border-radius: var(--radius-md);
  transition: opacity var(--duration-fast), transform var(--duration-fast);
}

.checkout-brand-interactive:hover {
  opacity: 0.82;
}

.checkout-brand-interactive:focus-visible {
  outline: 0.125rem solid var(--accent);
  outline-offset: 0.25rem;
}

.checkout-topbar-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

/* Prominent Countdown Timer Banner (above amount due) */
.checkout-countdown-banner {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: 0.4rem 0.9rem;
  background: var(--surface-overlay);
  border: 0.0625rem solid var(--border);
  border-radius: 999px;
  margin-bottom: var(--space-3);
  animation: fadeIn var(--duration-fast) var(--ease-standard);
}

.checkout-countdown-ring-wrap {
  position: relative;
  width: 1.85rem;
  height: 1.85rem;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.checkout-countdown-ring-wrap .countdown-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  transform: rotate(-90deg);
}

.checkout-countdown-ring-wrap circle {
  fill: none;
  stroke-width: 3.5;
}

.checkout-countdown-ring-wrap .track {
  stroke: var(--border);
}

.checkout-countdown-ring-wrap .prog {
  stroke: var(--accent);
  stroke-linecap: round;
  stroke-dasharray: 107;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset 1s linear, stroke var(--duration-fast) ease;
}

.checkout-countdown-ring-wrap .prog.level-mid {
  stroke: var(--warn);
}

.checkout-countdown-ring-wrap .prog.level-low {
  stroke: var(--danger);
}

.checkout-countdown-ring-wrap .countdown-clock-icon {
  width: 0.9rem;
  height: 0.9rem;
  color: var(--text-secondary);
}

.checkout-countdown-info {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}

.checkout-countdown-text {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.checkout-countdown-digits {
  font-size: var(--text-base);
  font-weight: 800;
  font-family: var(--font-mono);
  color: var(--text-primary);
  letter-spacing: 0.02em;
}

/* Product Details (Centered) */
.checkout-product-details {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.checkout-badge-row {
  display: flex;
  justify-content: center;
  margin-bottom: var(--space-1);
}

.checkout-pname {
  font-size: clamp(1.35rem, 4vw, 1.75rem);
  font-weight: 800;
  color: var(--text-primary);
  line-height: 1.25;
  letter-spacing: -0.03em;
  margin: 0;
  text-align: center;
}

.checkout-pdesc {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.6;
  margin: 0 auto;
  text-align: center;
  max-width: 36ch;
}

/* Centralized Price section */
.checkout-price-section {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-1);
  margin-top: var(--space-3);
  padding: var(--space-4) var(--space-5);
  background: var(--surface-overlay);
  border: 0.0625rem solid var(--border);
  border-radius: var(--radius-xl);
}

.checkout-price-row {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: var(--space-2);
}

.checkout-price {
  font-size: clamp(2.2rem, 6vw, 3rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
  text-align: center;
}

.checkout-price .cur,
.checkout-price .dec {
  font-size: 0.55em;
  font-weight: 700;
  color: var(--text-secondary);
}

.checkout-price-note {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  text-align: center;
}

/* Invoice Hero Wrapper (Centered) */
.checkout-hero-wrapper {
  margin: var(--space-2) 0 var(--space-4);
}

.checkout-hero {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
}

.checkout-hero-eyebrow {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.checkout-hero-amount {
  font-size: clamp(2.2rem, 6vw, 3rem);
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.checkout-hero-amount.muted {
  color: var(--text-secondary);
}

.checkout-hero-amount .unit {
  font-size: 0.55em;
  font-weight: 700;
  color: var(--accent);
  letter-spacing: normal;
}

.checkout-hero-fiat {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  font-weight: 500;
}

.checkout-hero-fiat b {
  color: var(--text-primary);
  font-weight: 600;
}

.checkout-status-icon {
  width: 3.5rem;
  height: 3.5rem;
  margin: 0 auto var(--space-3);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.checkout-status-icon .icon {
  width: 1.75rem;
  height: 1.75rem;
}

.checkout-status-icon.paid {
  background: var(--success-subtle);
  border: 0.0625rem solid var(--success);
  color: var(--success);
}

.checkout-status-icon.expired {
  background: var(--danger-subtle);
  border: 0.0625rem solid var(--danger);
  color: var(--danger);
}

.checkout-divider {
  height: 0.0625rem;
  background: var(--border);
  margin: var(--space-5) 0;
}

.checkout-state-view {
  display: none;
}

.checkout-state-view.active {
  display: block;
}

.checkout-card.payment-unlocked .checkout-pay-panel,
.checkout-card.payment-unlocked .checkout-wait-status,
.checkout-card.payment-locked .checkout-choices {
  display: none;
}

.checkout-choices {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  margin-bottom: var(--space-6);
}

.checkout-choice-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.checkout-section-label {
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.checkout-section-label small {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--accent);
  text-transform: none;
  letter-spacing: normal;
}

.checkout-price-pills {
  display: flex;
  gap: var(--space-2);
  width: 100%;
}

.checkout-price-pill {
  all: unset;
  box-sizing: border-box;
  flex: 1;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-xl);
  border: 0.0625rem solid var(--border);
  background: var(--surface-overlay);
  color: var(--text-primary);
  font-weight: 700;
  font-size: var(--text-sm);
  cursor: pointer;
  text-align: center;
  transition: all var(--duration-fast);
}

.checkout-price-pill:hover {
  background: var(--surface-overlay-strong);
  border-color: var(--border-strong);
}

.checkout-price-pill.selected {
  background: var(--accent-subtle);
  border-color: var(--accent);
  color: var(--accent);
  box-shadow: 0 0 0 0.0625rem var(--accent) inset;
}

.checkout-methods {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.checkout-method {
  all: unset;
  box-sizing: border-box;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: var(--space-4);
  width: 100%;
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-xl);
  border: 0.0625rem solid var(--border);
  background: var(--surface-overlay);
  transition: background var(--duration-fast), border-color var(--duration-fast), transform var(--duration-fast);
}

.checkout-method:hover {
  background: var(--surface-overlay-strong);
  border-color: var(--accent-subtle);
}

.checkout-method.selected {
  background: var(--accent-subtle);
  border-color: var(--accent);
  box-shadow: 0 0 0 0.0625rem var(--accent) inset;
}

.checkout-method-icon {
  width: 2.5rem;
  height: 2.5rem;
  flex-shrink: 0;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
}

.checkout-method-icon .asset-icon {
  width: 2.5rem;
  height: 2.5rem;
  flex: 0 0 2.5rem;
}

.checkout-method-icon .asset-icon-image img,
.checkout-method-icon .asset-icon-coin {
  width: 2.5rem;
  height: 2.5rem;
  display: block;
}

.checkout-method-icon .asset-icon-coin .asset-glyph {
  width: 1.6rem;
  height: 1.6rem;
}

/* Hide duplicate/overlapping badges in checkout methods */
.checkout-method .asset-icon-badge,
.checkout-method-icon .asset-icon-badge,
.checkout-locked-summary .asset-icon-badge,
.checkout-pay-panel .asset-icon-badge {
  display: none !important;
}

.checkout-method-icon .network-icon,
.checkout-method-icon .network-icon img {
  width: 2.5rem;
  height: 2.5rem;
  display: block;
  border-radius: 50%;
}

.checkout-method-info {
  flex: 1;
  min-width: 0;
  text-align: start;
}

.checkout-method-asset-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.checkout-method-asset {
  font-size: var(--text-base);
  font-weight: 700;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.checkout-method-net {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: 0.15rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.checkout-method-net .netdot {
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
}

.checkout-method-right {
  text-align: end;
  flex-shrink: 0;
}

.checkout-method-amt {
  font-size: var(--text-base);
  font-weight: 700;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

.checkout-method-radio {
  width: 1.35rem;
  height: 1.35rem;
  margin-inline-start: var(--space-2);
  flex-shrink: 0;
  border-radius: 50%;
  border: 0.09375rem solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: border-color var(--duration-fast), background var(--duration-fast);
}

.checkout-method.selected .checkout-method-radio {
  border-color: var(--accent);
  background: var(--accent);
}

.checkout-method-radio .icon {
  width: 0.8rem;
  height: 0.8rem;
  opacity: 0;
  color: var(--accent-ink);
  transition: opacity var(--duration-fast);
}

.checkout-method.selected .checkout-method-radio .icon {
  opacity: 1;
}

.checkout-locked-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-5);
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-lg);
  border: 0.0625rem solid var(--border);
  background: var(--surface-overlay);
  font-size: var(--text-sm);
  color: var(--text-primary);
}

.checkout-locked-text-wrap {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
}

.checkout-locked-text-wrap .icon {
  color: var(--accent);
  flex-shrink: 0;
}

.checkout-change-btn {
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--accent);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  border: 0.0625rem solid var(--border);
  background: var(--surface-overlay);
  cursor: pointer;
  white-space: nowrap;
}

.checkout-change-btn:hover {
  background: var(--surface-overlay-strong);
}

/* Modal Content for change payment method */
.checkout-modal-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-3);
  padding: 0;
}

.checkout-modal-icon-hero {
  width: 3.25rem;
  height: 3.25rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: var(--space-2);
}

.checkout-modal-icon-hero.warning {
  background: var(--warn-subtle);
  border: 0.0625rem solid var(--warn);
  color: var(--warn);
}

.checkout-modal-icon-hero .icon {
  width: 1.5rem;
  height: 1.5rem;
}

.checkout-modal-title {
  font-size: var(--text-lg);
  font-weight: 750;
  color: var(--text-primary);
  margin: 0;
}

.checkout-modal-lead {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.55;
  margin: 0;
  max-width: 34ch;
}

.checkout-modal-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  margin-top: var(--space-5);
}

.checkout-modal-actions > * {
  flex: 1;
  min-width: 0;
  padding: 0.75rem 1rem;
  font-size: var(--text-sm);
  font-weight: 650;
}

.checkout-pay-panel {
  background: var(--surface-overlay);
  border: 0.0625rem solid var(--border);
  border-radius: var(--radius-xl);
  padding: var(--space-5);
  margin-bottom: var(--space-5);
}

.checkout-pay-instruction {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  font-weight: 500;
  margin-bottom: var(--space-4);
  line-height: 1.5;
}

.checkout-pay-instruction strong {
  color: var(--text-primary);
}

.checkout-pay-target {
  display: flex;
  gap: var(--space-4);
  align-items: stretch;
}

.checkout-qr-tile {
  width: 6.5rem;
  height: 6.5rem;
  flex-shrink: 0;
  background: #ffffff;
  border-radius: var(--radius-lg);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-2);
  border: 0.0625rem solid var(--border);
  box-shadow: 0 0.25rem 1rem rgba(0, 0, 0, 0.15);
}

.checkout-qr-tile svg {
  width: 100%;
  height: 100%;
  display: block;
}

.checkout-qr-tile:empty {
  display: none;
}

.checkout-qr-toggle {
  all: unset;
  display: none;
  cursor: pointer;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--accent);
  margin-bottom: var(--space-3);
}

.checkout-pay-header {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  margin-bottom: var(--space-4);
}

.checkout-live-status-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.28rem 0.75rem;
  background: var(--warn-subtle);
  border: 0.0625rem solid var(--warn);
  color: var(--warn);
  border-radius: 999px;
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.02em;
}

.checkout-live-status-pill .pulse-dot {
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  background: var(--warn);
  box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.7);
  animation: pulseDot 2s infinite;
}

@keyframes pulseDot {
  0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.7); }
  70% { transform: scale(1); box-shadow: 0 0 0 0.35rem rgba(245, 158, 11, 0); }
  100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(245, 158, 11, 0); }
}

.ellipsis-anim::after {
  content: "";
  display: inline-block;
  width: 1.2em;
  text-align: left;
  animation: ellipsisDots 1.5s infinite steps(4, jump-none);
}

@keyframes ellipsisDots {
  0% { content: ""; }
  25% { content: "."; }
  50% { content: ".."; }
  75% { content: "..."; }
  100% { content: "..."; }
}

.checkout-pay-fields {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  flex: 1;
  min-width: 0;
}

.checkout-field-box {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  background: var(--bg-primary);
  border: 0.0625rem solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-4);
}

.checkout-field-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

.checkout-field-label {
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.checkout-field-box code {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.45;
  word-break: break-all;
  white-space: normal;
  background: none;
  padding: 0;
}

.checkout-copy-btn {
  font-size: var(--text-xs);
  font-weight: 700;
  padding: 0.28rem 0.65rem;
  gap: var(--space-1);
  border-radius: var(--radius-md);
}

.checkout-copy-btn.copied {
  background: var(--success);
  color: var(--text-inverse);
  border-color: var(--success);
}

.checkout-copy-btn .icon {
  width: 0.85rem;
  height: 0.85rem;
}

.checkout-warn-note {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 0.0625rem solid var(--border);
  font-size: var(--text-xs);
  line-height: 1.5;
  color: var(--text-secondary);
}

.checkout-warn-note .icon {
  flex-shrink: 0;
  color: var(--warn);
  width: 1.1rem;
  height: 1.1rem;
  margin-top: 0.1rem;
}

.checkout-receipt-box {
  background: var(--surface-overlay);
  border: 0.0625rem solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-5);
  margin-top: var(--space-3);
}

.checkout-kv {
  display: flex;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) 0;
  font-size: var(--text-sm);
  border-top: 0.0625rem solid var(--border);
}

.checkout-kv:first-child {
  border-top: none;
}

.checkout-kv .k {
  color: var(--text-secondary);
}

.checkout-kv .v {
  color: var(--text-primary);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: end;
}

.checkout-kv .v.mono {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
}

.receipt-tx-wrap {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  justify-content: flex-end;
  max-width: 100%;
}

.receipt-tx-link {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  color: var(--accent);
  text-decoration: none;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  transition: opacity var(--duration-fast), color var(--duration-fast);
}

.receipt-tx-link:hover {
  text-decoration: underline;
  opacity: 0.85;
}

.receipt-tx-link .ext-link-icon {
  width: 0.85rem;
  height: 0.85rem;
  stroke-width: 2.2;
}

.receipt-copy-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: var(--radius-sm);
  background: var(--surface-overlay);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  cursor: pointer;
  padding: 0;
  transition: all var(--duration-fast);
}

.receipt-copy-btn:hover {
  color: var(--text-primary);
  border-color: var(--border-hover);
  background: var(--surface-card);
}

.receipt-copy-btn .icon {
  width: 0.85rem;
  height: 0.85rem;
}

.checkout-order-details-card {
  margin-top: var(--space-4);
}

/* Invoice Details Modal */
.invoice-details-modal-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  padding: var(--space-5) var(--space-6);
}

.invoice-detail-header-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  background: var(--surface-overlay);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.invoice-detail-label {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-secondary);
  font-weight: 600;
}

.invoice-detail-amount {
  font-size: var(--text-2xl);
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--text-primary);
  line-height: 1.2;
}

.invoice-detail-subamount {
  font-size: var(--text-xs);
  color: var(--accent);
  font-weight: 600;
  font-family: var(--font-mono);
  margin-top: 0.15rem;
}

.invoice-detail-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.invoice-detail-section-title {
  font-size: var(--text-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-secondary);
}

.invoice-lifecycle-tracker {
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: relative;
  padding: var(--space-4) var(--space-3);
  background: var(--surface-overlay);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.invoice-lifecycle-step {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
  position: relative;
  z-index: 2;
  flex: 1;
  text-align: center;
}

.invoice-lifecycle-dot {
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  background: var(--surface-card);
  border: 2px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
  font-size: 0.72rem;
  font-weight: 700;
  transition: all var(--duration-fast);
}

.invoice-lifecycle-dot .icon {
  width: 0.85rem;
  height: 0.85rem;
}

.invoice-lifecycle-step.completed .invoice-lifecycle-dot {
  background: var(--success);
  border-color: var(--success);
  color: #fff;
}

.invoice-lifecycle-step.active .invoice-lifecycle-dot {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb, 16, 185, 129), 0.2);
}

.invoice-lifecycle-step.expired .invoice-lifecycle-dot {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}

.invoice-lifecycle-step-label {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--text-secondary);
}

.invoice-lifecycle-step.active .invoice-lifecycle-step-label {
  color: var(--text-primary);
  font-weight: 700;
}

.invoice-detail-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-2);
}

.invoice-detail-actions .primary-btn {
  flex: 1 1 12rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  text-decoration: none;
}

.invoice-detail-actions .ghost-btn,
.invoice-detail-actions .danger-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
}

.invoice-detail-actions .ghost-btn.success-tint {
  color: var(--accent, #10b981);
  border-color: rgba(16, 185, 129, 0.3);
}

.invoice-detail-actions .ghost-btn.success-tint:hover {
  background: rgba(16, 185, 129, 0.1);
  border-color: rgba(16, 185, 129, 0.5);
}

.password-input-wrap {
  position: relative;
  display: flex;
  align-items: center;
  width: 100%;
}

.password-input-wrap input {
  width: 100%;
  padding-inline-end: 2.5rem;
}

.password-input-wrap .eye-toggle-btn {
  position: absolute;
  inset-inline-end: 0.35rem;
  top: 50%;
  transform: translateY(-50%);
  background: transparent;
  border: none;
  color: var(--text-secondary);
  padding: 0.35rem;
  cursor: pointer;
  border-radius: var(--radius-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 0.15s ease;
}

.password-input-wrap .eye-toggle-btn:hover {
  color: var(--text-primary);
}

.order-field-val-wrap {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  max-width: 100%;
}

.order-field-val {
  word-break: break-all;
}

.order-field-val.masked {
  letter-spacing: 0.12em;
  font-family: var(--font-mono);
}

.field-inline-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.2rem;
  background: rgba(255, 255, 255, 0.04);
  border: 0.0625rem solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  cursor: pointer;
  flex-shrink: 0;
  transition: all 0.15s ease;
}

.field-inline-btn:hover {
  color: var(--text-primary);
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--border-hover);
}

.field-inline-btn .icon {
  width: 0.85rem;
  height: 0.85rem;
}

.checkout-expired-copy {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.6;
  margin-top: var(--space-2);
}

.checkout-footer-meta {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 0.0625rem solid var(--border);
}

.checkout-id-ref {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 500;
  color: var(--text-secondary);
  opacity: 0.35;
  letter-spacing: 0.08em;
  user-select: all;
  background: none;
  border: none;
  padding: 0;
  transition: opacity var(--duration-fast);
}

.checkout-id-ref:hover {
  opacity: 0.8;
}

/* Form inputs for product */
.checkout-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.checkout-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  font-weight: 600;
}

.checkout-field span:first-child {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.req-star {
  color: var(--danger);
  font-weight: 700;
  margin-inline-start: 0.15rem;
  user-select: none;
}

.checkout-field .opt {
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--text-secondary);
  opacity: 0.7;
}

label.form-group:has(> input[required]) > span::after,
label.form-group:has(> select[required]) > span::after,
label.form-group:has(> textarea[required]) > span::after {
  content: " *";
  color: var(--danger);
  font-weight: 700;
}

.checkout-field input,
.checkout-field select {
  width: 100%;
  font: inherit;
  font-size: var(--text-base);
  color: var(--text-primary);
  background: var(--surface-overlay);
  border: 0.0625rem solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-4) var(--space-5);
  transition: border-color var(--duration-fast), background var(--duration-fast), box-shadow var(--duration-fast);
}

.checkout-field input::placeholder {
  color: var(--text-secondary);
  opacity: 0.7;
}

.checkout-field input:hover,
.checkout-field select:hover {
  border-color: var(--border-strong);
}

.checkout-field input:focus,
.checkout-field select:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--surface-overlay-strong);
  box-shadow: 0 0 0 0.1875rem var(--accent-subtle);
}

.checkout-field.checkbox-row {
  flex-direction: row;
  align-items: center;
  gap: var(--space-3);
}

.checkout-field.checkbox-row input {
  width: auto;
}

.checkout-cta {
  width: 100%;
  padding: var(--space-4) var(--space-5);
  font-size: var(--text-md);
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  border-radius: var(--radius-xl);
  margin-top: var(--space-3);
}

.checkout-security-note {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  color: var(--text-secondary);
  margin-top: var(--space-4);
  text-align: center;
}

.checkout-security-note .icon {
  width: 0.9rem;
  height: 0.9rem;
  flex-shrink: 0;
}

/* Security Challenge Progress Bar (PoW) */
.checkout-challenge-bar {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-lg);
  background: var(--surface-overlay);
  border: 0.0625rem solid var(--accent);
  box-shadow: 0 0 1rem var(--accent-subtle);
  animation: fadeIn var(--duration-fast) var(--ease-standard);
  text-align: start;
}

.checkout-challenge-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.checkout-challenge-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--text-primary);
}

.checkout-challenge-title .icon {
  width: 1.15rem;
  height: 1.15rem;
  color: var(--accent);
  flex-shrink: 0;
}

.checkout-challenge-progress {
  position: relative;
  width: 100%;
  height: 0.375rem;
  background: var(--surface-overlay-strong);
  border-radius: var(--radius-pill);
  overflow: hidden;
}

.checkout-challenge-fill {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 45%;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  border-radius: var(--radius-pill);
  animation: pow-indeterminate 1.4s ease-in-out infinite;
}

@keyframes pow-indeterminate {
  0% { transform: translateX(-100%); }
  50% { transform: translateX(160%); }
  100% { transform: translateX(320%); }
}

.checkout-output {
  margin-top: var(--space-3);
}

.checkout-output:empty {
  display: none;
}

.checkout-output:not(:empty):not(.busy-status) {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-lg);
  font-size: var(--text-xs);
  font-weight: 600;
  text-align: center;
  animation: fadeIn var(--duration-fast) var(--ease-standard);
  background: var(--surface-overlay);
  border: 0.0625rem solid var(--border);
  color: var(--text-secondary);
}

.checkout-output.error:not(:empty):not(.busy-status) {
  background: var(--danger-subtle);
  border-color: var(--danger);
  color: var(--danger);
}

.checkout-output.busy-status {
  padding: 0;
  background: none;
  border: none;
}

/* Trust modal */
.checkout-trust-modal-content {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.checkout-trust-hero {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
}

.checkout-trust-icon-wrap {
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 50%;
  background: var(--accent-subtle);
  border: 0.0625rem solid var(--accent);
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: var(--space-2);
}

.checkout-trust-icon-wrap .icon {
  width: 1.75rem;
  height: 1.75rem;
}

.checkout-trust-hero h3 {
  font-size: var(--text-lg);
  font-weight: 800;
  color: var(--text-primary);
  margin: 0;
}

.checkout-trust-lead {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.6;
  margin: 0;
  max-width: 42ch;
}

.checkout-trust-features {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.checkout-trust-feature {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-4);
  background: var(--surface-overlay);
  border: 0.0625rem solid var(--border);
  border-radius: var(--radius-lg);
}

.checkout-trust-feature .feature-icon {
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-md);
  background: var(--accent-subtle);
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.checkout-trust-feature .feature-icon .icon {
  width: 1rem;
  height: 1rem;
}

.checkout-trust-feature strong {
  display: block;
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--text-primary);
  margin-bottom: 0.15rem;
}

.checkout-trust-feature p {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  line-height: 1.5;
  margin: 0;
}

.checkout-error-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-3);
  padding: var(--space-8);
}

.checkout-error-mark {
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 50%;
  background: var(--danger-subtle);
  border: 0.0625rem solid var(--danger);
  color: var(--danger);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: var(--space-2);
}

.checkout-error-mark .icon {
  width: 1.75rem;
  height: 1.75rem;
}

@media (max-width: 40rem) {
  .checkout-content {
    padding: var(--space-4) var(--space-3) var(--space-8);
  }
  .checkout-card {
    padding: var(--space-5) var(--space-4);
  }
  .checkout-pay-target {
    flex-direction: column;
    align-items: stretch;
  }
  .checkout-qr-tile {
    display: none;
  }
  .checkout-qr-tile.open {
    display: flex;
    width: 10rem;
    height: 10rem;
    margin: 0 auto var(--space-4);
  }
  .checkout-qr-toggle {
    display: inline-flex;
  }
}

.table-wrap { overflow-x: auto; border-radius: var(--radius-xl); border: 0.0625rem solid var(--border); }
table { width: 100%; border-collapse: collapse; font-size: var(--text-base); min-width: 40rem; }
thead th {
  text-align: start; font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--text-secondary); font-weight: 600;
  padding: var(--space-5) var(--space-6); border-bottom: 0.0625rem solid var(--border);
  background: var(--bg-secondary); white-space: nowrap;
}
thead th.num, td.num { text-align: end; }
tbody td { padding: var(--space-5) var(--space-6); border-bottom: 0.0625rem solid var(--border); vertical-align: top; }
tbody td.num { font-variant-numeric: tabular-nums; white-space: nowrap; }
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--glass-bg); }

/* Modern Fintech Data Tables */
.data-table-container {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  width: 100%;
}

.data-table-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  padding: var(--space-1) 0;
}

.data-table-search-box {
  position: relative;
  display: flex;
  align-items: center;
  flex: 1 1 16rem;
  max-width: 24rem;
}

.data-table-search-box .search-icon {
  position: absolute;
  inset-inline-start: var(--space-4);
  width: 1rem;
  height: 1rem;
  color: var(--text-secondary);
  pointer-events: none;
  opacity: 0.7;
}

.data-table-search-input {
  width: 100%;
  font: inherit;
  font-size: var(--text-sm);
  color: var(--text-primary);
  background: var(--surface-overlay);
  border: 0.0625rem solid var(--border);
  border-radius: var(--radius-lg);
  padding: 0.55rem 0.85rem;
  padding-inline-start: 2.35rem;
  transition: border-color var(--duration-fast), background var(--duration-fast), box-shadow var(--duration-fast);
}

.data-table-search-input:hover {
  border-color: var(--border-strong);
}

.data-table-search-input:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--surface-overlay-strong);
  box-shadow: 0 0 0 0.1875rem var(--accent-subtle);
}

.data-table-search-input::placeholder {
  color: var(--text-secondary);
  opacity: 0.65;
}

.data-table-filters {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.data-filter-chip {
  all: unset;
  box-sizing: border-box;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  padding: 0.35rem 0.75rem;
  border-radius: 999px;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-secondary);
  background: var(--surface-overlay);
  border: 0.0625rem solid var(--border);
  transition: background var(--duration-fast), color var(--duration-fast), border-color var(--duration-fast);
}

.data-filter-chip:hover {
  background: var(--surface-overlay-strong);
  color: var(--text-primary);
  border-color: var(--border-strong);
}

.data-filter-chip.active {
  background: var(--text-primary);
  color: var(--bg-primary);
  border-color: var(--text-primary);
}

.data-table-card {
  background: var(--bg-secondary);
  border: 0.0625rem solid var(--border);
  border-radius: var(--radius-xl);
  overflow: hidden;
}

.data-table-scroll {
  width: 100%;
  overflow-x: auto;
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  text-align: start;
  font-size: var(--text-sm);
}

.data-table thead th {
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-secondary);
  background: var(--surface-overlay);
  border-bottom: 0.0625rem solid var(--border);
  white-space: nowrap;
}

.data-table thead th:first-child {
  padding-inline-start: var(--space-5);
}

.data-table thead th:last-child {
  padding-inline-end: var(--space-5);
  text-align: end;
}

.data-table tbody tr {
  border-bottom: 0.0625rem solid var(--border);
  transition: background var(--duration-fast);
}

.data-table tbody tr:last-child {
  border-bottom: none;
}

.data-table tbody tr:hover {
  background: var(--surface-overlay);
}

.data-table tbody td {
  padding: var(--space-3) var(--space-4);
  vertical-align: middle;
  color: var(--text-primary);
}

.data-table tbody td:first-child {
  padding-inline-start: var(--space-5);
}

.data-table tbody td:last-child {
  padding-inline-end: var(--space-5);
  text-align: end;
  width: 1%;
  white-space: nowrap;
}

.data-amount-cell {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.data-amount-primary {
  font-size: var(--text-base);
  font-weight: 700;
  color: var(--text-primary);
  letter-spacing: -0.01em;
}

.data-amount-sub {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  color: var(--text-secondary);
}

.data-title-cell {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  max-width: 14rem;
}

.data-fields-tag {
  display: block;
  max-width: 13rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--text-xs);
  color: var(--text-secondary);
}

.data-title {
  font-size: var(--text-base);
  font-weight: 650;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.data-subtext {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.data-date-cell {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  white-space: nowrap;
}

.copyable-cell {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--surface-overlay);
  border: 0.0625rem solid var(--border);
  padding: 0.25rem 0.5rem;
  border-radius: var(--radius-md);
  max-width: fit-content;
}

.mono-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 0.8125rem;
  color: var(--text-primary);
  letter-spacing: -0.01em;
}

.copy-icon-btn {
  all: unset;
  box-sizing: border-box;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.35rem;
  height: 1.35rem;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  transition: color var(--duration-fast), background var(--duration-fast);
}

.copy-icon-btn:hover {
  color: var(--text-primary);
  background: var(--surface-overlay-strong);
}

.copy-icon-btn.copied {
  color: var(--accent);
}

.copy-icon-btn svg {
  width: 0.85rem;
  height: 0.85rem;
}

.status-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.25rem 0.65rem;
  border-radius: 999px;
  font-size: var(--text-xs);
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
}

.status-pill.status-success {
  background: var(--success-subtle);
  color: var(--success);
  border: 0.0625rem solid var(--success-line);
}

.status-pill.status-warning {
  background: var(--warn-subtle);
  color: var(--warn);
  border: 0.0625rem solid var(--warn-line);
}

.status-pill.status-danger {
  background: var(--danger-subtle);
  color: var(--danger);
  border: 0.0625rem solid var(--danger-line);
}

.status-pill.status-neutral {
  background: var(--surface-overlay);
  color: var(--text-secondary);
  border: 0.0625rem solid var(--border);
}

.data-actions-cell {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-2);
  flex-wrap: nowrap;
}

.table-action-btn {
  all: unset;
  box-sizing: border-box;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.35rem 0.65rem;
  border-radius: var(--radius-md);
  font-size: var(--text-xs);
  font-weight: 600;
  background: var(--surface-overlay);
  border: 0.0625rem solid var(--border);
  color: var(--text-primary);
  white-space: nowrap;
  transition: background var(--duration-fast), border-color var(--duration-fast);
}

.table-action-btn:hover {
  background: var(--surface-overlay-strong);
  border-color: var(--border-strong);
}

.table-action-btn.primary {
  background: var(--accent);
  color: var(--accent-ink);
  border-color: transparent;
}

.table-action-btn.primary:hover {
  filter: brightness(1.08);
}

.table-action-btn.danger {
  color: var(--danger);
  border-color: var(--danger-line);
}

.table-action-btn.danger:hover {
  background: var(--danger-subtle);
}

.table-action-btn svg {
  width: 0.85rem;
  height: 0.85rem;
}

.data-mobile-card-list {
  display: none;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-3);
}

.data-mobile-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4);
  background: var(--surface-overlay);
  border: 0.0625rem solid var(--border);
  border-radius: var(--radius-lg);
}

.data-mobile-card-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  text-align: start;
  font-size: var(--text-sm);
  min-width: 56rem;
}

.data-table-filters {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  max-width: 100%;
}

.data-mobile-card-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  font-size: var(--text-xs);
}

@media (max-width: 56rem) {
  .data-table-scroll {
    display: none;
  }
  .data-mobile-card-list {
    display: flex;
  }
}

.node-metrics-name-cell { display: flex; align-items: center; gap: var(--space-3); white-space: nowrap; }

/* Checks page -- the summary/expand table (renderChecks) and the
   per-node breakdown it expands to (runResultsTableHtml, shared with
   openRunResultsModal's own modal). Target used to be given no width
   hint at all and just soaked up whatever space Nodes/Time didn't
   need, leaving both cramped together with a large gap before them;
   capping it here is what actually fixes that, not just adding gap
   rules that would've fought the browser's own auto layout. */
.checks-table .checks-target-cell { max-width: 22rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.checks-table .checks-nodes-cell { white-space: nowrap; }
.checks-table .checks-caret-cell { width: 2rem; }
.checks-node-dot { display: inline-block; width: 0.5rem; height: 0.5rem; border-radius: 50%; margin-inline-end: var(--space-1); }
.checks-row-caret { transition: transform var(--duration-fast); }
.checks-row-caret.active { transform: rotate(180deg); }
/* The expanded per-node table needs to read as *belonging to* the
   summary row above it, not as a second, equally-weighted table --
   its own border/radius (from .table-wrap, shared with the standalone
   modal use) is dropped in favor of a plain, slightly recessed panel
   instead. */
.checks-detail-cell { padding: var(--space-5) !important; background: var(--bg-secondary); }
.checks-detail-cell .run-results-table-wrap { border: none; border-radius: 0; }

.log-message { font-size: var(--text-sm); max-width: 20rem; overflow-wrap: anywhere; }

.empty-state {
  padding: var(--space-10) var(--space-8); text-align: center; color: var(--text-secondary);
  display: flex; flex-direction: column; align-items: center; gap: var(--space-6);
}
.empty-state .icon { width: 2.25rem; height: 2.25rem; opacity: 0.5; }

.pagination { display: flex; align-items: center; justify-content: center; gap: var(--space-3); padding-top: var(--space-8); flex-wrap: wrap; }
.pagination span { font-size: var(--text-base); color: var(--text-secondary); }
/* Numbered page buttons + the prev/next arrows share the same round
   2rem footprint (see paginationControls in app.js) so the whole row
   reads as one consistent set of controls -- current page is a
   .primary-btn (filled, disabled -- it's not a link to itself), every
   other page is a plain .ghost-btn, both just overridden down from
   their normal text-button padding to fit a single number/glyph. */
.pagination-page, .pagination-arrow {
  width: 2rem;
  height: 2rem;
  min-width: 2rem;
  padding: 0;
  justify-content: center;
  border-radius: 50%;
  font-size: var(--text-base);
}
.pagination-arrow .icon { width: 1rem; height: 1rem; }
/* chevron-left/chevron-right (see tpl-pagination-prev-btn/-next-btn)
   point toward the reading-start/-end direction, not a fixed screen
   side -- prev/next's own functional binding (page - 1 / page + 1)
   never changes, only which way each arrow visually points, so a
   mirror is all either icon needs, not a different glyph. */
[dir="rtl"] .pagination-arrow .icon { transform: scaleX(-1); }
.pagination-ellipsis {
  width: 2rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
  font-size: var(--text-base);
}

/* =============================================
   FORMS
   ============================================= */
.form-group { margin-bottom: var(--space-7); }
.form-group:last-child { margin-bottom: 0; }
.form-group label, .form-group .form-group-label {
  display: block; font-size: var(--text-base); font-weight: 600;
  color: var(--text-secondary); margin-bottom: var(--space-3);
}
.form-input, select.form-input {
  width: 100%;
  background: var(--bg-primary);
  border: 0.0625rem solid var(--border);
  color: var(--text-primary);
  padding: var(--space-5) var(--space-6);
  border-radius: var(--radius-lg);
  font-size: var(--text-base);
  transition: border-color var(--duration-fast);
}
.form-input:focus { outline: none; border-color: var(--accent); }
.form-hint { font-size: var(--text-sm); color: var(--text-secondary); margin-top: var(--space-7); }
.form-hint.busy-status { display: flex; align-items: center; gap: var(--space-3); }
.form-hint.busy-status .spinner { flex-shrink: 0; }
.form-row { display: flex; gap: var(--space-6); }
.form-row .form-group { flex: 1; }
.checkbox-row { display: flex; align-items: center; gap: var(--space-4); font-size: var(--text-base); }

/* Custom-styled <select> replacement (see app.js's renderCustomSelect)
   -- a real button + a floating option list, reusing the same open/
   close visual language as .custom-dropdown/.dropdown-menu above, so
   an option can show more than a plain <option> text node allows --
   first use: which node(s) a prober variant is actually available on,
   in the probe creation form's own prober picker. */
.select-custom { position: relative; }
.select-trigger {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  width: 100%; min-height: 2.65rem; padding: var(--space-4) var(--space-5);
  border: 0.0625rem solid var(--border); border-radius: var(--radius-lg);
  background: var(--bg-primary); color: var(--text-primary);
  text-align: start; cursor: pointer;
  transition: background var(--duration-fast), border-color var(--duration-fast), color var(--duration-fast);
}
.select-trigger:hover,
.select-custom.active .select-trigger { border-color: var(--accent); background: var(--accent-subtle); }
.select-trigger:disabled { cursor: not-allowed; opacity: 0.6; }
.select-trigger-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#wallet-assets-summary { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.select-trigger-placeholder { color: var(--text-secondary); }
.select-caret { width: 1.1rem; height: 1.1rem; flex-shrink: 0; color: var(--text-secondary); transition: transform var(--duration-fast); }
.select-custom.active .select-caret { transform: rotate(180deg); }
.select-panel {
  /* position: fixed, not absolute -- same reasoning as .info-popover's
     own comment: .modal is overflow-y: auto with a capped max-height,
     which clips (not just fails to scroll into view, actually cuts
     off) any absolutely-positioned descendant that would extend past
     its edge. top/left/width are computed in JS at open time (see
     positionFloatingPanel), not expressed statically here, since a
     trigger's real on-screen position can't be known in CSS. */
  position: fixed;
  background: var(--bg-secondary); border: 0.0625rem solid var(--border); border-radius: var(--radius-lg);
  box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.3);
  opacity: 0; visibility: hidden; transform: translateY(-0.5rem);
  transition: opacity var(--duration-fast), transform var(--duration-fast); z-index: 2100;
  max-height: 16rem; overflow-y: auto; padding: var(--space-2);
  /* Without this, scrolling past this panel's own top/bottom edge
     (e.g. the node multi-select, which can hold more entries than fit)
     "chains" into whatever's underneath -- here, always .modal's own
     scrolling body -- so the whole form visibly scrolls out from under
     an open dropdown the instant your scroll gesture runs out of room
     inside the panel itself. */
  overscroll-behavior: contain;
  display: grid;
  gap: var(--space-1);
  min-width: 12rem;
}
.select-custom.active .select-panel { opacity: 1; visibility: visible; transform: translateY(0); }
.select-option {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: 2.65rem;
  padding: 0.48rem 0.65rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  font-size: var(--text-base);
  text-align: start;
}
.select-option:hover,
.select-option.focused {
  background: var(--surface-overlay);
  color: var(--text-primary);
}
.select-option.selected {
  background: var(--accent-subtle);
  color: var(--accent);
}
.select-option-checkbox { flex-direction: row; align-items: center; gap: var(--space-4); }
.select-option-checkbox input[type="checkbox"] { margin: 0; flex-shrink: 0; }
/* The synthetic "all nodes" row in renderNodeMultiSelect -- visually
   set apart (bolder, its own bottom border) from the real per-node
   rows below it, since it isn't one of them, it's a control over all
   of them. */
.select-option-all { font-weight: 600; border-bottom: 0.0625rem solid var(--border); border-radius: 0; margin-bottom: var(--space-2); padding-bottom: var(--space-5); }
/* A disabled row in renderCheckboxMultiSelect (the module picker,
   while a previous Save's module-actions batch is still pending) --
   dimmed and inert, same visual language .icon-btn:disabled already
   uses elsewhere, not just relying on the checkbox's own native
   disabled look to carry it. */
.select-option-disabled { opacity: 0.5; cursor: not-allowed; }
.select-option-disabled:hover { background: none; }
.select-option-icon {
  display: grid;
  place-items: center;
  width: 1.2rem;
  height: 1.2rem;
  margin: 0;
  color: var(--text-secondary);
}
.select-option-icon .icon { width: 0.9rem; height: 0.9rem; }
.select-option-image {
  display: block;
  width: 1.45rem;
  height: 1.45rem;
  border-radius: 50%;
  object-fit: cover;
}
.select-option.selected .select-option-icon { color: var(--accent); }
.select-option-label {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: var(--space-1);
  overflow: hidden;
}
.select-option-label strong,
.select-option-label small {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.select-option-label strong { font-size: var(--text-sm); font-weight: 600; line-height: 1.25; }
.select-option-label small { font-size: var(--text-xs); color: var(--text-secondary); line-height: 1.2; }
.select-option-hint { font-size: var(--text-xs); color: var(--text-secondary); font-weight: 500; }

.multi-select-option {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 2.7rem;
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  cursor: pointer;
  text-align: start;
}

.multi-select-option:hover,
.multi-select-option:focus-visible {
  background: var(--surface-overlay);
  color: var(--text-primary);
  outline: none;
}

.multi-select-option[aria-selected="true"] {
  background: var(--accent-subtle);
  color: var(--accent);
}

.multi-select-option-copy {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.multi-select-option-image {
  width: 1.55rem;
  height: 1.55rem;
  flex: 0 0 1.55rem;
  border-radius: 50%;
  object-fit: cover;
}
.select-option.selected .select-option-hint { color: var(--accent); opacity: 0.85; }

.info-icon-wrap { position: relative; display: inline-flex; vertical-align: middle; margin-inline-start: var(--space-2); }
.info-icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 1.1rem; height: 1.1rem; padding: 0; border: none; background: none; color: var(--text-secondary); cursor: pointer; }
.info-icon-btn:hover { color: var(--accent); }
.info-icon-btn .icon { width: 1.1rem; height: 1.1rem; }
.info-popover {
  /* position: fixed, not absolute -- .modal itself is overflow-y: auto
     with a capped max-height (see its own rule), which would otherwise
     clip this against the modal's own edge for any field below the
     fold. Fixed positioning escapes that clipping since it's relative
     to the viewport, not the scrolling ancestor; top/left are computed
     in JS at open time (see the global click handler) rather than in
     CSS, since where the button actually sits on screen can't be known
     statically. */
  position: fixed; z-index: 2100;
  width: max(14rem, min(20rem, 60vw)); padding: var(--space-5) var(--space-6); border-radius: var(--radius-lg);
  background: var(--bg-secondary); border: 0.0625rem solid var(--border);
  box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.3);
  font-size: var(--text-sm); line-height: 1.4; color: var(--text-secondary); font-weight: 400;
}

.advanced-toggle-btn { display: flex; align-items: center; gap: var(--space-3); width: 100%; justify-content: center; margin: var(--space-6) 0; padding: var(--space-4); background: none; border: 0.0625rem dashed var(--border); border-radius: var(--radius-md); color: var(--text-secondary); cursor: pointer; font-size: var(--text-base); }
.advanced-toggle-btn:hover { color: var(--text-primary); border-color: var(--text-secondary); }
.advanced-toggle-caret { width: 1rem; height: 1rem; transition: transform var(--duration-fast); }
.advanced-toggle-btn.active .advanced-toggle-caret { transform: rotate(180deg); }
.advanced-options-group { display: flex; flex-direction: column; gap: 0; }

.switch-row { display: flex; align-items: center; gap: var(--space-5); font-size: var(--text-base); cursor: pointer; }
/* Label text leads, the switch itself sits at the far end of the row
   -- for a row whose label is the more important thing to read first
   (see advancedProbeFieldsHtml's expiration toggle), instead of the
   switch always being the first thing shown regardless of content. */
.switch-row-reverse { justify-content: space-between; }
.switch { position: relative; width: 2.5rem; height: 1.4rem; flex-shrink: 0; }
.switch input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
/* pointer-events: none is load-bearing, not decorative -- .switch-track
   sits on top of the real (invisible, opacity: 0) checkbox in the same
   position:absolute/inset:0 box, later in DOM order, so it paints above
   it. Without this, a direct click anywhere on the visible track/knob
   never actually reaches the real input underneath at all -- every
   switch in the app only ever worked because it happened to sit inside
   a <label> that forwarded the click to the labeled control instead.
   That's an accident this depended on, not something to keep relying
   on: a switch that isn't (or can no longer safely be, see
   switch-row-reverse's own info-icon-button-in-a-label bug) wrapped in
   a <label> needs to work standalone. */
.switch-track { position: absolute; inset: 0; background: var(--border); border-radius: 999px; transition: background var(--duration-fast); pointer-events: none; }
.switch-track::before {
  content: ""; position: absolute; width: 1.1rem; height: 1.1rem;
  inset-inline-start: 0.15rem; top: 0.15rem;
  background: #fff; border-radius: 50%; transition: transform var(--duration-fast);
}
.switch input:checked + .switch-track { background: var(--accent); }
.switch input:checked + .switch-track::before { transform: translateX(1.1rem); }
[dir="rtl"] .switch input:checked + .switch-track::before { transform: translateX(-1.1rem); }

.secret-field { display: flex; gap: var(--space-4); align-items: center; }
.secret-field .secret-input { flex: 1; min-width: 0; }
.secret-field .icon-btn { border-radius: var(--radius-lg); width: auto; height: auto; padding: var(--space-4) var(--space-5); }

/* copyableCodeBlockHtml -- a long install command (node_id + secret +
   proxy + module flags easily runs past what a single-line .secret-
   field can show without horizontal scrolling) wraps across as many
   lines as it needs instead, so the whole thing is visible at once. */
.code-block { position: relative; }
.code-block-pre {
  background: var(--bg-primary); border: 0.0625rem solid var(--border); border-radius: var(--radius-lg);
  padding-block: var(--space-5); padding-inline: var(--space-6) 2.6rem; font-size: var(--text-base); line-height: 1.5;
  white-space: pre-wrap; overflow-wrap: anywhere; margin: 0;
}
.code-block-copy { position: absolute; top: 0.4rem; inset-inline-end: 0.4rem; }

fieldset { border: none; padding: 0; margin: 0; }

/* =============================================
   MODAL
   ============================================= */
.modal-overlay {
  position: fixed; inset: 0; background: rgba(0, 0, 0, 0.55);
  z-index: 2000; display: flex; justify-content: center; align-items: center;
  padding: var(--space-7);
  opacity: 0; visibility: hidden; transition: opacity var(--duration-base);
}
.modal-overlay.active { opacity: 1; visibility: visible; }
/* A layer with something stacked on top of it (see openModal's own
   stack) -- still fully live underneath, just receded and unable to
   receive clicks. Its own backdrop goes transparent since the layer
   above it already darkens the page; only the box itself needs to
   visibly recede for the "there's more behind this" depth cue. */
.modal-overlay.modal-covered { background: transparent; pointer-events: none; }
/* Both a scale-down and an upward shift -- most nested dialogs open at
   roughly the same centered size/position as whatever's underneath
   them, so scale alone (which shrinks evenly toward the center) barely
   peeks out from behind an equally-sized layer on top; shifting up too
   is what actually makes the receded edge visible along the top. */
.modal-overlay.modal-covered .modal { transform: scale(0.92) translateY(-0.9rem); opacity: 0.5; }
.modal { width: 100%; max-width: 28rem; max-height: 85vh; overflow-y: auto; transition: transform var(--duration-base), opacity var(--duration-base); }
/* While any .select-custom dropdown is open (see syncModalScrollLock)
   -- its panel is position: fixed and computed against the trigger's
   current on-screen position, so scrolling the modal underneath it
   would drag the still-open panel out of alignment with its own
   field instead of moving together with it. */
.modal.scroll-locked { overflow: hidden; }
/* Used by the probe detail modal once it has real per-metric charts to
   stack (see openJobDetailModal) -- the default width is fine for a
   single chart but cramped for several side by side. */
.modal-wide { max-width: 48rem; }
.modal-header {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: var(--space-7);
}
.modal-header:has(#modal-title:empty) {
  justify-content: flex-end;
  margin-bottom: var(--space-2);
}
.modal-header h2 { font-size: var(--text-lg); font-weight: 700; }
.modal-header h2:empty { display: none; }
/* modal-header-extra is populated per-modal (see openJobDetailModal's
   fieldPickerButtonHtml) -- empty and invisible for every other
   modal, which never touches it. */
.modal-header-actions { display: flex; align-items: center; gap: var(--space-4); flex-shrink: 0; }
/* The probe detail modal's window-toggle row (see openProbeDetailModal) --
   its own full-width row, right-aligned, instead of squeezed into the
   same row as the stat-grid tiles (the previous layout, which crowded
   both once the tiles needed to wrap). */
.modal-toolbar { display: flex; justify-content: flex-end; }
.modal-actions { display: flex; justify-content: flex-end; gap: var(--space-5); margin-top: var(--space-7); }
.modal-actions-split { justify-content: space-between; align-items: center; width: 100%; }
.modal-actions-end { display: flex; align-items: center; gap: var(--space-3); margin-inline-start: auto; }

/* =============================================
   TOP LOADING BAR
   ============================================= */
/* Indeterminate progress indicator shown while a fetch is in flight
   (background live-refresh polls, tab-refocus refetches, any api()
   call) -- see startTopLoadingBar/finishTopLoadingBar in api.js. A
   single steady-pace segment loops for as long as the request is
   actually in flight; reaching the right edge means nothing, it's
   mid-loop regardless of how close the fetch is to done, so it never
   implies false progress. Speed itself is the only completion signal:
   when the request resolves, exactly one pass plays faster than every
   loop before it -- but instead of exiting off-screen like a normal
   lap, it settles filling the whole bar, fully visible, holds there
   briefly, then dissolves. A fast pass that just zips off-screen has
   nothing left on screen to fade -- settling filled first is what
   gives the fade something real to dissolve *from*.
   left/width (not transform) throughout, deliberately -- the loop and
   the finish pass interpolate through the same positioning system, so
   a new fetch starting while the previous finish/fade is still
   playing never has to reconcile two different ways of expressing
   "where is the segment right now". */
#top-loading-bar {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 0.15625rem;
  z-index: 9999;
  overflow: hidden;
  background: transparent;
  opacity: 0;
  pointer-events: none;
  /* Long enough that dissolving reads as its own deliberate moment,
     not just a quick blink-and-it's-gone. Reversible mid-transition
     for free: if .running gets re-added while this is still animating
     toward 0 (a new fetch starting mid-fade), the browser just
     retargets from whatever opacity it's currently at back up to 1
     along this same curve -- no jump, no restart, no separate code
     path needed for "cancel the fade". */
  transition: opacity 0.5s ease-out;
}
/* Two independent concerns, two independent classes -- "is the
   segment pinned at full width right now" (.filled) is not the same
   question as "should the bar be opaque right now" (.running /
   .finishing / .holding). Conflating them made a fade fight a position
   change on the same frame. .filled stays applied continuously from
   the instant the finish-pass completes all the way through the hold
   *and* the fade -- the segment's position never changes again until
   the fade has fully finished and the bar resets for next time. */
#top-loading-bar.running, #top-loading-bar.finishing, #top-loading-bar.holding { opacity: 1; }
#top-loading-bar .seg {
  position: absolute;
  top: 0; bottom: 0;
  left: -40%;
  width: 40%;
  background: linear-gradient(90deg, var(--accent), var(--accent-hover));
  box-shadow: 0 0 0.5rem 0 rgba(249, 115, 22, 0.6);
}
#top-loading-bar.running .seg {
  animation: top-loading-bar-loop 1.7s linear infinite;
}
/* Sweeps in and grows to fill the entire bar, rather than the loop's
   own "same-size segment exits off the right" shape -- ending full
   and visible is the whole point here, see the block comment above.
   forwards holds that filled state for the one frame between the
   animation completing and JS handing off to .filled below. */
#top-loading-bar.finishing .seg {
  animation: top-loading-bar-finish 0.35s cubic-bezier(0.3, 0, 0.2, 1) forwards;
}
/* The static (no animation) hold of that same full-width position --
   takes over the instant .finishing's own animation ends, painting
   the identical box, so there's no discontinuity at the handoff.
   Stays applied through the hold and the entire fade; only cleared
   once the fade is fully done. */
#top-loading-bar.filled .seg {
  left: 0;
  width: 100%;
}
@keyframes top-loading-bar-loop {
  0%   { left: -40%; width: 40%; }
  100% { left: 100%; width: 40%; }
}
@keyframes top-loading-bar-finish {
  0%   { left: -40%; width: 40%; }
  100% { left: 0%; width: 100%; }
}

/* =============================================
   TOAST
   ============================================= */
#toast-container {
  position: fixed; top: 1rem; left: 50%; transform: translateX(-50%); z-index: 3000;
  display: flex; flex-direction: column; align-items: center; gap: var(--space-4);
  max-width: 22rem; width: calc(100% - 2rem);
}
.toast {
  position: relative;
  overflow: hidden;
  display: flex; align-items: flex-start; gap: var(--space-4);
  background: var(--bg-secondary);
  border: 0.0625rem solid var(--border);
  border-inline-start: 0.2rem solid var(--accent);
  border-radius: var(--radius-lg);
  padding-block: var(--space-6);
  padding-inline: var(--space-7) var(--space-4);
  box-shadow: 0 0.5rem 1.5rem rgba(0,0,0,0.3);
  font-size: var(--text-base);
  animation: toastIn var(--duration-base) var(--ease-out);
  width: 100%;
}
.toast.error { border-inline-start-color: var(--danger); }
.toast.success { border-inline-start-color: var(--success); }
.toast-message { flex: 1; padding-top: 0.05rem; }
.toast-close {
  flex-shrink: 0; background: none; border: none; cursor: pointer;
  padding: var(--space-1); color: var(--text-secondary); display: flex;
}
.toast-close:hover { color: var(--text-primary); }
.toast-close .icon { width: 0.8rem; height: 0.8rem; }
/* The countdown itself, not just its visual bar -- see toast() in
   app.js. animation-play-state is what actually freezes this in
   place on hover/hold, in lockstep with the real setTimeout that
   dismisses the toast, so the bar never drifts out of sync with when
   it'll actually disappear. */
.toast-progress {
  position: absolute; bottom: 0; inset-inline-start: 0; height: 0.15rem;
  background: var(--accent);
  animation: toastProgress var(--ease-linear) forwards;
}
.toast.error .toast-progress { background: var(--danger); }
.toast.success .toast-progress { background: var(--success); }
@keyframes toastIn { from { opacity: 0; transform: translateY(-0.6rem); } to { opacity: 1; transform: translateY(0); } }
@keyframes toastProgress { from { width: 100%; } to { width: 0%; } }

/* =============================================
   SPINNER / SKELETON
   ============================================= */
.spinner {
  width: 2rem; height: 2rem; border-radius: 50%;
  border: 0.2rem solid var(--border); border-top-color: var(--accent);
  animation: spin var(--duration-spin) var(--ease-linear) infinite;
}
.spinner-sm { width: 1rem; height: 1rem; border-width: 0.15rem; }
.spinner-xs { width: 0.6rem; height: 0.6rem; border-width: 0.1rem; }
@keyframes spin { to { transform: rotate(360deg); } }

/* =============================================
   LOGIN / SIGNUP
   ============================================= */
#boot-spinner {
  position: fixed; inset: 0; display: flex; align-items: center; justify-content: center;
  background: var(--bg-primary); z-index: 9998; transition: opacity var(--duration-base);
}
#boot-spinner.hidden { display: none !important; opacity: 0; pointer-events: none; }

#auth-screen {
  min-height: 100vh; min-height: 100dvh; display: flex; align-items: center; justify-content: center; padding: var(--space-8);
}
.auth-card { width: 100%; max-width: 25rem; text-align: center; }
.auth-card h1 {
  display: flex; align-items: center; justify-content: center; gap: var(--space-4);
  margin-bottom: var(--space-3);
}
.auth-card h1 .brand-text { align-items: center; }
.auth-card h1 .brand-mark { font-size: var(--text-2xl); }
.auth-card h1 .brand-sub { font-size: 0.6rem; }
.auth-card .subtitle { margin-bottom: var(--space-8); }
/* Generic segmented tab switcher -- originally just the login/signup
   auth-screen switcher (.auth-tabs/.auth-tab), generalized once a
   second, unrelated use (the reinstall section's "This machine"/"New
   machine" choice) came along. No margin of its own -- unlike the
   auth-only version this replaced, a shared component can't assume
   how much breathing room every future caller wants, so each call
   site brings its own margin utility instead. */
.tabs { display: flex; gap: var(--space-3); background: var(--bg-primary); padding: var(--space-1); border-radius: var(--radius-lg); }
.tab {
  flex: 1; padding: var(--space-4); border: none; background: transparent; color: var(--text-secondary);
  border-radius: var(--radius-md); font-weight: 600; font-size: var(--text-base); cursor: pointer; transition: var(--duration-fast);
}
.tab.active { background: var(--accent); color: #fff; }

/* Probes page's group switcher -- a pill row, not .tabs above: that
   component is a fixed 2/3-way equal-width segmented control (login/
   signup, reinstall situation), while this one holds a variable,
   possibly-long list of an account's own groups plus trailing add/
   manage buttons, so items need to size to their own label and the
   row itself needs to scroll rather than squeeze. */
.group-tabs { display: flex; align-items: center; gap: var(--space-4); }
.group-tabs-list { display: flex; gap: var(--space-3); overflow-x: auto; flex: 1; min-width: 0; }
.group-tab {
  flex: none; padding: var(--space-3) var(--space-6); border: 0.0625rem solid var(--border); background: transparent;
  color: var(--text-secondary); border-radius: 999px; font-weight: 600; font-size: var(--text-sm);
  cursor: pointer; white-space: nowrap; transition: var(--duration-fast);
}
.group-tab.active { background: var(--accent); color: #fff; border-color: var(--accent); }
.group-tab-manage, .group-tab-add { flex: none; }
#auth-error, .form-error {
  color: var(--danger); font-size: var(--text-base); margin-bottom: var(--space-7); min-height: 1rem;
}
.pow-progress {
  margin-top: var(--space-7); font-size: var(--text-base); color: var(--text-secondary);
  display: flex; align-items: center; justify-content: center; gap: var(--space-4);
}
.picker-row { display: flex; gap: var(--space-8); margin-top: var(--space-8); }
.picker-col { flex: 1; text-align: start; }
.picker-label { font-size: var(--text-base); color: var(--text-secondary); margin-bottom: var(--space-4); display: block; }

.theme-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4); }
.theme-btn {
  padding: var(--space-4); border-radius: var(--radius-md); border: 0.0625rem solid var(--border);
  cursor: pointer; display: flex; align-items: center; justify-content: center; transition: var(--duration-fast);
  background: var(--bg-primary); color: var(--text-primary);
}
.lang-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-4); }
.lang-btn {
  padding: var(--space-4) var(--space-5); border-radius: var(--radius-md); border: 0.0625rem solid var(--border);
  cursor: pointer; display: flex; align-items: center; justify-content: center; gap: var(--space-3);
  background: var(--bg-primary); color: var(--text-primary); font-size: var(--text-base); font-weight: 600; transition: var(--duration-fast);
}
.lang-btn .pref-flag {
  width: 1.35rem;
  height: 1.05rem;
}

/* Preferences Trigger (Theme Icon | Country Flag) */
.pref-compact-trigger,
.dropdown-trigger.compact {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.35rem 0.65rem;
  border-radius: 999px;
  background: var(--surface-overlay);
  border: 0.0625rem solid var(--border);
  color: var(--text-primary);
  cursor: pointer;
  transition: background var(--duration-fast), border-color var(--duration-fast);
}

.pref-compact-trigger:hover,
.dropdown-trigger.compact:hover {
  background: var(--surface-overlay-strong);
  border-color: var(--border-strong);
}

.pref-trigger-combo {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}

.pref-theme-icon {
  width: 1.05rem;
  height: 1.05rem;
  flex-shrink: 0;
  color: var(--text-primary);
}

.pref-pipe {
  color: var(--border-strong);
  font-size: 0.85rem;
  font-weight: 300;
  line-height: 1;
  opacity: 0.7;
  user-select: none;
}

.pref-flag,
.pref-lang-icon,
svg.pref-flag {
  width: 1.25rem;
  height: 0.95rem;
  flex-shrink: 0;
  display: inline-block;
  vertical-align: middle;
  border-radius: 0.125rem;
  overflow: hidden;
}

/* Settings dropdown (main app nav) -- theme + language pickers,
   reused from the auth screen's inline layout instead of duplicating
   markup differently. inset-inline-end (not right) so it stays
   anchored to the trailing edge in both LTR and RTL. */
.custom-dropdown { position: relative; display: inline-flex; }
.dropdown-menu {
  /* app.js positions floating menus in viewport coordinates; keeping this
     fixed prevents the trigger's own offset from being added a second time. */
  position: fixed; top: auto; inset-inline-end: auto;
  background: var(--bg-secondary); border: 0.0625rem solid var(--border); border-radius: var(--radius-lg);
  padding: var(--space-6); min-width: 14rem; box-shadow: 0 0.5rem 1.5rem rgba(0,0,0,0.3);
  opacity: 0; visibility: hidden; transform: translateY(-0.5rem);
  transition: opacity var(--duration-fast), transform var(--duration-fast); z-index: 1500;
}
.custom-dropdown.active .dropdown-menu { opacity: 1; visibility: visible; transform: translateY(0); }
.dropdown-menu-wide { min-width: 18rem; max-width: 22rem; max-height: 80vh; overflow-y: auto; }
.dropdown-divider { border: none; border-top: 0.0625rem solid var(--border); margin: var(--space-6) 0; }
.full-width { width: 100%; justify-content: center; }

.account-row {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-4) var(--space-5); border-radius: var(--radius-md);
  border: 0.0625rem solid var(--border); margin-bottom: var(--space-3);
}
.account-row:last-of-type { margin-bottom: 0; }
.account-row.clickable:hover { background: var(--glass-bg); }
.account-row-current { border-color: var(--accent); background: var(--accent-subtle); }
.account-row-icon { width: 1.1rem; height: 1.1rem; flex-shrink: 0; color: var(--text-secondary); }
.account-row-current .account-row-icon { color: var(--accent); }
.account-row-info { flex: 1; display: flex; flex-direction: column; min-width: 0; overflow: hidden; }
.account-row-name { font-size: var(--text-base); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-row-info .account-row-id { font-size: var(--text-xs); color: var(--text-secondary); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.account-row-balance { font-size: var(--text-base); font-variant-numeric: tabular-nums; flex-shrink: 0; }
.account-row-edit { flex-shrink: 0; }

/* =============================================
   MISC
   ============================================= */
.hidden { display: none !important; }
.text-secondary { color: var(--text-secondary); }
.flex-between { display: flex; align-items: center; justify-content: space-between; gap: var(--space-6); flex-wrap: wrap; }

/* Select and multi-select panels are moved here while open so they can
   escape modal/content overflow. The layer itself stays inert; only an
   open panel receives pointer events. */
#floating-layer {
  position: fixed;
  inset: 0;
  /* Select panels can be opened from inside a modal. Keep this layer
     above the modal overlay, not in the same stacking context as it. */
  z-index: 3100;
  pointer-events: none;
}

#floating-layer > .select-panel {
  pointer-events: auto;
}

#floating-layer > .select-panel.active {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

/* =============================================
   BOTTOM NAV (mobile only -- see RESPONSIVE below)
   ============================================= */
.bottom-nav {
  display: none;
  flex-shrink: 0;
  z-index: 1000;
  background: var(--bg-primary);
  border-top: 0.0625rem solid var(--border);
  padding: var(--space-2) var(--space-1) calc(var(--space-2) + env(safe-area-inset-bottom, 0px));
}
.bottom-nav a {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-2) var(--space-1);
  border-radius: var(--radius-lg);
  text-decoration: none;
  color: var(--text-secondary);
  font-size: var(--text-xs);
  font-weight: 500;
  transition: background var(--duration-fast), color var(--duration-fast);
}
.bottom-nav a .icon { width: 1.25rem; height: 1.25rem; }
.bottom-nav a:hover, .bottom-nav a.active { color: var(--accent); }
.bottom-nav a.active { background: var(--accent-subtle); }

/* Overview workspace */
.overview-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(20rem, 0.85fr);
  gap: var(--space-7);
  align-items: stretch;
}

.overview-hero,
.overview-recent {
  min-width: 0;
}

.overview-hero {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 18rem;
}

.overview-hero h2 {
  max-width: 26rem;
  margin-top: var(--space-3);
  font-size: clamp(1.7rem, 3vw, 2.5rem);
  letter-spacing: -0.03em;
  line-height: 1.05;
}

.overview-hero p {
  max-width: 32rem;
  margin-top: var(--space-4);
  color: var(--text-soft);
  font-size: var(--text-md);
}

.overview-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-top: var(--space-7);
}

.overview-recent .content-header {
  min-height: auto;
  margin-bottom: var(--space-4);
}

.overview-recent .content-header h2 {
  font-size: var(--text-lg);
}

.overview-recent .list-container {
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.overview-recent .empty-state {
  min-height: 11rem;
}

/* Root operations share the dashboard shell, but their summary needs
   explicit stat tiles rather than the generic card-grid's inline flow. */
#view-admin > .glass-card.flex-between {
  align-items: flex-end;
}

#admin-hero-copy {
  max-width: 42rem;
  color: var(--text-secondary);
  line-height: 1.5;
}

#admin-panel-providers {
  grid-template-columns: 1fr;
}

#admin-panel-providers > header,
#admin-panel-providers > .provider-capability-grid,
#admin-panel-providers > .glass-card {
  grid-column: 1;
}

#admin-panel-providers > .glass-card {
  width: 100%;
}

.provider-capability-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-5);
}

.provider-capability-card {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-5);
  min-width: 0;
  padding: var(--space-6);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
}

.provider-capability-heading,
.provider-credential-heading {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  min-width: 0;
}

.provider-capability-heading > .icon {
  flex: 0 0 auto;
  color: var(--accent);
}

.provider-capability-heading strong,
.provider-credential-heading strong {
  display: block;
  color: var(--text-primary);
  font-size: var(--text-sm);
}

.provider-capability-heading p,
.provider-credential-heading p {
  margin: var(--space-2) 0 0;
  color: var(--text-secondary);
  font-size: var(--text-xs);
  line-height: 1.5;
}

.provider-subsection-header {
  margin-top: var(--space-3);
}

.provider-subsection-header h3 {
  margin: 0;
  font-size: var(--text-lg);
}

.provider-subsection-header p {
  margin: var(--space-2) 0 0;
  color: var(--text-secondary);
  font-size: var(--text-sm);
}

.provider-modal-intro {
  margin: 0 0 var(--space-2);
  line-height: 1.55;
}

.provider-credential-section {
  display: grid;
  gap: var(--space-5);
  padding: var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface-overlay);
}

.provider-credential-section > .form-group {
  margin: 0;
}

#provider-credentials-fields {
  display: grid;
  gap: var(--space-5);
}

#provider-credentials-fields > .form-group {
  margin: 0;
}

.provider-credential-heading {
  margin-bottom: var(--space-1);
}

@media (max-width: 52rem) {
  .provider-capability-grid {
    grid-template-columns: 1fr;
  }

  .provider-capability-card {
    flex-direction: column;
  }
}

@media (max-width: 52rem) {
  #view-admin > .glass-card.flex-between {
    align-items: stretch;
  }
}

/* =============================================
   RESPONSIVE
   ============================================= */
@media (max-width: 52rem) {
  .nav-center { display: none; }
  .bottom-nav { display: flex; }
  /* No longer needs the old calc(4.5rem + safe-area) reserved at the
     bottom -- .bottom-nav is a normal flex sibling of .content now
     (see .page's own comment), not a fixed overlay floating on top of
     it, so nothing here needs to leave room to avoid being hidden
     behind it. */
  .content { padding: var(--space-7) var(--space-7); }
  .stat-grid { grid-template-columns: repeat(2, 1fr); }
  .overview-layout { grid-template-columns: 1fr; }
  .overview-hero { min-height: auto; }

  .list-item-row { grid-template-columns: auto minmax(0, 1fr); }
  .list-item-actions { grid-column: 2; justify-content: flex-start; padding-top: 0; }
}

@media (max-width: 30rem) {
  .nav-container {
    gap: var(--space-4);
    padding-inline: var(--space-5);
  }

  #home-view .nav-right {
    gap: var(--space-3);
  }

  .nav .dropdown-trigger.compact {
    width: auto;
    height: auto;
    padding: 0.3rem 0.55rem;
    border-radius: 999px;
  }

  #home-open-dashboard-desktop {
    padding-inline: var(--space-5);
    font-size: var(--text-sm);
  }

  .list-item { padding: var(--space-6); }
  .list-item-heading { display: block; }
  .list-item-meta { justify-content: flex-start; margin-top: var(--space-4); }
  .list-item-actions { grid-column: 1 / -1; }

}
:root {
  --text: var(--text-primary);
  --text-soft: var(--text-secondary);
  --muted: var(--text-secondary);
  --surface: var(--bg-secondary);
  --text-display: clamp(3rem, 7vw, 5.8rem);
}
.hero-copy {
  max-width: 36rem;
  color: var(--text-soft);
  font-size: var(--text-lg);
}

.hero-copy.compact {
  margin-top: var(--space-5);
  font-size: var(--text-md);
}

.auth-proof {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-7);
  color: var(--text-soft);
}

.auth-proof > span {
  display: flex;
  flex-direction: column;
}

.auth-proof strong {
  color: var(--text);
  font-size: var(--text-sm);
}

.auth-proof small {
  font-size: var(--text-xs);
}

.checkout-auth-page .checkout-shell {
  max-width: 32rem;
}

.checkout-auth-card .form-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  width: 100%;
}

.checkout-auth-card .input-wrap {
  width: 100%;
}

.checkout-auth-card textarea {
  display: block;
  width: 100%;
  box-sizing: border-box;
  min-height: 5.5rem;
  resize: vertical;
  background: var(--surface-overlay);
  border: 0.0625rem solid var(--border);
  border-radius: var(--radius-lg);
  color: var(--text-primary);
  padding: var(--space-3) var(--space-4);
  font: 500 var(--text-sm)/1.5 var(--font-mono);
  transition: border-color var(--duration-fast), box-shadow var(--duration-fast);
  direction: ltr;
  text-align: left;
}

.checkout-auth-card textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 0.15rem var(--accent-subtle);
}

.checkout-auth-card .form-label {
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.auth-tabs {
  margin: var(--space-4) 0 var(--space-4);
  display: flex;
  background: var(--surface-overlay);
  padding: 0.25rem;
  border-radius: var(--radius-xl);
  border: 0.0625rem solid var(--border);
  gap: 0.25rem;
}

.auth-tabs .tab {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: 0.65rem 1rem;
  font-size: var(--text-sm);
  font-weight: 700;
  border-radius: var(--radius-lg);
  color: var(--text-secondary);
  background: transparent;
  border: none;
  cursor: pointer;
  transition: all var(--duration-fast) var(--ease-standard);
}

.auth-tabs .tab:hover {
  color: var(--text-primary);
}

.auth-tabs .tab.active {
  color: var(--text-primary);
  background: var(--bg-secondary);
  box-shadow: 0 0.125rem 0.5rem rgba(0, 0, 0, 0.2);
}

.auth-tabs .tab .icon {
  width: 1rem;
  height: 1rem;
}

.auth-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-2);
}

.auth-discover-info {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  margin-bottom: var(--space-2);
}

.auth-discover-info .checkout-pdesc {
  margin-inline: auto;
  max-width: 38ch;
  text-wrap: balance;
}

.access-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  background: var(--surface);
  padding: var(--space-5);
}

.access-tabs {
  margin-bottom: var(--space-6);
}

.access-tab {
  flex: 1 1 50%;
}

.form-heading {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}

.form-heading h2 {
  font-size: var(--text-lg);
}

/* Public */
.public-content {
  scroll-behavior: smooth;
}

.public-container {
  padding: 0 var(--space-7) var(--space-10);
}

.hero {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(24rem, 0.85fr);
  align-items: center;
  gap: clamp(2.5rem, 6vw, 5.5rem);
  min-height: min(46rem, calc(100vh - 5rem));
  padding: var(--space-8) 0;
}

.hero-copy-block h1 {
  margin-top: var(--space-4);
  font-size: var(--text-display);
  font-weight: 750;
  letter-spacing: -0.035em;
  line-height: 1.02;
}

.home-hero-lead {
  max-width: 38rem;
  margin-top: var(--space-5);
  color: var(--text-secondary);
  font-size: var(--text-lg);
  line-height: 1.55;
}

.hero-actions {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-top: var(--space-6);
}

.hero-trust-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-5);
  margin-top: var(--space-6);
  color: var(--text-secondary);
  font-size: var(--text-xs);
}

.hero-trust-row .trust-item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--text-secondary);
}

.hero-trust-row .icon {
  width: 0.95rem;
  height: 0.95rem;
  color: var(--accent);
}

.invoice-demo {
  overflow: hidden;
  padding: 0;
}

.invoice-demo-head,
.invoice-demo-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border);
}

.invoice-demo-title-group {
  display: flex;
  flex-direction: column;
}

.invoice-demo-title-group small,
.invoice-demo-foot {
  color: var(--text-secondary);
  font-size: var(--text-xs);
}

.invoice-demo-title-group strong {
  font-size: var(--text-sm);
  color: var(--text-primary);
}

.status-pulse-dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px var(--accent);
  display: inline-block;
}

.invoice-amount {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-6) var(--space-5);
  border-bottom: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.015);
}

.invoice-amount-primary {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}

.invoice-amount .amount-val {
  font-size: clamp(2rem, 3.5vw, 2.75rem);
  font-weight: 750;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.04em;
  line-height: 1;
  color: var(--text-primary);
}

.invoice-amount .amount-cur {
  font-size: var(--text-md);
  font-weight: 700;
  color: var(--accent);
}

.invoice-amount-sub {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--text-secondary);
  font-size: var(--text-xs);
  margin-top: var(--space-1);
}

.payment-state-list {
  list-style: none;
  padding: var(--space-4) var(--space-5);
}

.payment-state {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-height: 3.4rem;
  color: var(--text-secondary);
}

.payment-state + .payment-state::before {
  position: absolute;
  top: -0.65rem;
  inset-inline-start: 0.95rem;
  width: 1px;
  height: 1.3rem;
  background: var(--border);
  content: "";
}

.state-marker {
  z-index: 1;
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 1.9rem;
  height: 1.9rem;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--surface);
  font-size: 0.65rem;
  font-weight: 700;
}

.state-marker .icon {
  width: 0.9rem;
  height: 0.9rem;
}

.payment-state.active .state-marker {
  border-color: var(--success);
  background: var(--success-subtle);
  color: var(--success);
}

.payment-state.current {
  color: var(--text-primary);
}

.payment-state.current .state-marker {
  border-color: var(--accent);
  background: var(--accent-subtle);
  color: var(--accent);
}

.payment-state div {
  display: flex;
  flex-direction: column;
}

.payment-state strong {
  font-size: var(--text-sm);
  color: var(--text-primary);
}

.payment-state span:last-child {
  font-size: var(--text-xs);
  color: var(--text-secondary);
}

.invoice-demo-foot {
  justify-content: space-between;
  border-top: 1px solid var(--border);
  border-bottom: 0;
}

.invoice-demo-wallet-info {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--text-secondary);
  font-size: var(--text-xs);
}

.invoice-demo-foot code {
  color: var(--text-secondary);
  font-size: var(--text-xs);
  font-family: var(--font-mono);
}

.public-section {
  padding: var(--space-9) 0;
  scroll-margin-top: 4.5rem;
}

.public-section + .public-section {
  border-top: 1px solid var(--border);
}

.section-intro {
  max-width: 44rem;
  margin-bottom: var(--space-7);
}

.section-intro h2,
.start-section h2 {
  margin-top: var(--space-3);
  font-size: clamp(1.8rem, 3.5vw, 2.75rem);
  font-weight: 750;
  letter-spacing: -0.03em;
  line-height: 1.1;
  color: var(--text-primary);
}

.section-intro p,
.start-section > div > p {
  margin-top: var(--space-3);
  color: var(--text-secondary);
  font-size: var(--text-md);
  line-height: 1.55;
}

.process-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-5);
}

.process-card {
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.process-card-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-2);
}

.process-index {
  color: var(--text-secondary);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: 700;
}

.process-icon-wrap {
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: var(--radius-md);
  background: var(--accent-subtle);
  color: var(--accent);
}

.process-icon-wrap .icon {
  width: 1.2rem;
  height: 1.2rem;
}

.process-card h3 {
  font-size: var(--text-lg);
  letter-spacing: -0.02em;
  color: var(--text-primary);
}

.process-card p {
  color: var(--text-secondary);
  font-size: var(--text-sm);
  line-height: 1.5;
}

.pricing-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-5);
}

.price-card {
  position: relative;
  min-width: 0;
  display: flex;
  flex-direction: column;
  padding: var(--space-6);
}

.price-card-featured {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent-subtle), 0 8px 24px rgba(34, 197, 94, 0.06);
}

.price-card-badge {
  position: absolute;
  top: -0.65rem;
  right: var(--space-5);
  background: var(--accent);
  color: #000000;
  font-size: 0.65rem;
  font-weight: 750;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
}

.price-card-header {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
  padding-bottom: var(--space-5);
  border-bottom: 1px solid var(--border);
}

.price-card-name {
  color: var(--text-primary);
  font-size: var(--text-md);
  font-weight: 700;
}

.price-card-price {
  color: var(--accent);
  font-size: clamp(1.4rem, 2.2vw, 1.85rem);
  line-height: 1;
  letter-spacing: -0.035em;
  font-weight: 750;
}

.price-card-price small {
  margin-inline-start: var(--space-2);
  color: var(--text-secondary);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: 0;
}

.price-card-features {
  display: grid;
  gap: 0;
  list-style: none;
  margin-top: var(--space-4);
}

.plan-feature {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 2.25rem;
  border-bottom: 1px solid var(--border);
  color: var(--text-primary);
  font-size: var(--text-xs);
}

.plan-feature:last-child {
  border-bottom: 0;
}

.plan-feature-state {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 1rem;
  height: 1rem;
  color: var(--accent);
}

.plan-feature-state .icon {
  width: 0.85rem;
  height: 0.85rem;
  stroke-width: 2.4;
}

.plan-feature.is-disabled {
  color: var(--text-secondary);
  opacity: 0.65;
}

.plan-feature.is-disabled .plan-feature-state {
  color: var(--text-secondary);
  opacity: 0.6;
}

.start-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-5);
  list-style: none;
}

.start-card {
  padding: var(--space-6);
  display: flex;
  gap: var(--space-4);
  align-items: flex-start;
}

.start-index {
  display: grid;
  place-items: center;
  width: 1.85rem;
  height: 1.85rem;
  flex: 0 0 1.85rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  font-size: 0.75rem;
  font-weight: 750;
  color: var(--accent);
}

.start-card h3 {
  font-size: var(--text-md);
  color: var(--text-primary);
  margin-bottom: var(--space-1);
}

.start-card p {
  color: var(--text-secondary);
  font-size: var(--text-xs);
  line-height: 1.45;
}

.start-actions {
  margin-top: var(--space-6);
}

@media (max-width: 64rem) {
  .pricing-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .process-grid,
  .start-grid {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 38rem) {
  .pricing-grid {
    grid-template-columns: 1fr;
  }
}

/* Gateway aliases for remaining html classes — reuse radar tokens */
.view { display: flex; flex-direction: column; gap: var(--space-8); }
.view.hidden { display: none !important; }
.hidden-root { display: none !important; }
.status-badge { display: inline-flex; align-items: center; gap: var(--space-2); padding: 0 var(--space-3); height: 1.5rem; border-radius: 999px; font-size: var(--text-xs); font-weight: 600; background: var(--surface-overlay); border: 0.0625rem solid var(--border); color: var(--text-secondary); }
.status-badge.neutral { background: var(--surface-overlay); color: var(--text-secondary); }
.status-badge.warning { background: var(--warn-subtle); color: var(--warn); border-color: transparent; }
.status-badge.success { background: var(--success-subtle); color: var(--success); }
.account-trigger { display: inline-flex; align-items: center; gap: var(--space-2); }
.toast-marker { display: none; }
.toast-region { position: fixed; top: var(--space-7); right: var(--space-7); z-index: 9999; display: flex; flex-direction: column; gap: var(--space-4); }
.wordmark, .wordmark-copy, .wordmark-icon, .wordmark-subtitle, .wordmark-title { display: inline-flex; align-items: center; gap: var(--space-2); }
.hero { min-height: min(36rem, calc(100vh - 4.1rem)); }




/* Hero single column on mobile – radar's 2-col min 25rem overflows at 375 */
@media (max-width: 52rem) {
  .hero { grid-template-columns: 1fr; min-height: auto; }
}

/* Modal form fields — radar's form-input styling was not applied to bare inputs inside cloned templates */
.modal-body .form-group input,
.modal-body .form-group select,
.modal-body .form-group textarea {
  width: 100%;
  background: var(--surface-overlay);
  border: 0.0625rem solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-4);
  color: var(--text-primary);
  font: inherit;
}
.modal-body .form-group input::placeholder,
.modal-body .form-group textarea::placeholder { color: var(--text-secondary); }
.modal-body .form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
}
.invoice-mode-row { align-items: end; }
.invoice-mode-copy { align-self: center; max-width: 30rem; margin: 0; }
.invoice-flexible-fields { display: grid; gap: var(--space-5); margin-bottom: var(--space-5); }
.invoice-section-heading { display: flex; align-items: center; justify-content: space-between; gap: var(--space-5); }
.invoice-section-heading > div { min-width: 0; }
.invoice-section-heading .form-hint { margin: 0; }
.invoice-section-heading p { margin-top: var(--space-2); }
.invoice-pricing-options, .invoice-route-amounts { display: grid; gap: var(--space-4); }
.invoice-pricing-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto; align-items: end; gap: var(--space-4); padding: var(--space-4); border: 0.0625rem solid var(--border); border-radius: var(--radius-lg); background: var(--surface-overlay); }
.invoice-pricing-row .form-group { min-width: 0; }
.invoice-pricing-row .icon-btn { margin-bottom: var(--space-2); }
.invoice-routes-field { margin-top: var(--space-2); }
.invoice-route-option .asset-icon { width: 1.65rem; height: 1.65rem; }
.invoice-route-option .asset-icon-badge { width: 0.72rem; height: 0.72rem; }
.invoice-route-amount-row { display: grid; gap: var(--space-4); padding: var(--space-5); border: 0.0625rem solid var(--border); border-radius: var(--radius-lg); background: var(--surface-overlay); }
.invoice-route-amount-heading { display: flex; align-items: center; gap: var(--space-3); }
.invoice-route-amount-heading > span:last-child { display: grid; gap: var(--space-1); min-width: 0; }
.invoice-route-amount-heading strong { font-size: var(--text-sm); }
.invoice-route-amount-heading small { color: var(--text-secondary); font-size: var(--text-xs); }
.invoice-route-amount-icon { display: grid; place-items: center; width: 2rem; height: 2rem; flex: 0 0 2rem; }
.invoice-route-amount-icon .asset-icon { width: 2rem; height: 2rem; }
.invoice-route-price-inputs { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: var(--space-4); }
@media (max-width: 30rem) {
  .modal-body .form-row { grid-template-columns: 1fr; }
  .invoice-pricing-row { grid-template-columns: 1fr 1fr; }
  .invoice-pricing-row .icon-btn { justify-self: end; }
}

/* Invoice creation is a workflow, so it gets a wider scroll surface and
   its own rhythm instead of inheriting the compact generic modal form. */
.modal.modal-invoice {
  display: flex;
  flex-direction: column;
  width: min(48rem, calc(100vw - 1.5rem));
  max-width: 48rem;
  max-height: min(92dvh, 58rem);
  padding: 0;
  overflow: hidden;
}

.modal.modal-invoice .modal-header {
  flex: 0 0 auto;
  margin: 0;
  padding: var(--space-7) var(--space-8) var(--space-6);
  border-bottom: 0.0625rem solid var(--border);
}

.modal.modal-invoice .modal-header h2 {
  font-size: var(--text-xl);
  letter-spacing: -0.025em;
}

.modal.modal-invoice #modal-body {
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.modal.modal-invoice .invoice-form {
  display: grid;
  gap: var(--space-6);
  margin: 0;
  padding: var(--space-7) var(--space-8) 0;
}

.invoice-form .form-group { margin-bottom: 0; }

.invoice-form-section,
.invoice-flexible-fields {
  display: grid;
  gap: var(--space-6);
  min-width: 0;
  padding-bottom: var(--space-6);
  border-bottom: 0.0625rem solid var(--border);
}

.invoice-form-section:last-child,
.invoice-flexible-fields:last-child {
  padding-bottom: 0;
  border-bottom: 0;
}

.invoice-mode-section {
  grid-template-columns: minmax(0, 1fr) minmax(14rem, 0.9fr);
  align-items: center;
  gap: var(--space-8);
}

.invoice-mode-copy,
.invoice-section-heading,
.invoice-subsection-heading {
  min-width: 0;
}

.invoice-mode-copy,
.invoice-section-heading > div,
.invoice-subsection-heading > div {
  display: grid;
  gap: var(--space-2);
}

.invoice-section-label,
.invoice-subsection-label {
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-weight: 750;
  letter-spacing: 0.01em;
}

.invoice-subsection-label { font-size: var(--text-base); }

.invoice-mode-control { min-width: 0; }
.invoice-mode-control select { min-height: 3rem; }

.invoice-section-heading,
.invoice-subsection-heading {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-5);
}

.invoice-section-action {
  flex: 0 0 auto;
  min-height: 2.75rem;
  padding-inline: var(--space-5);
}

.invoice-flexible-fields {
  gap: var(--space-7);
  margin: 0;
  background: transparent;
}

.invoice-pricing-options,
.invoice-route-amounts {
  display: grid;
  gap: var(--space-4);
}

.invoice-pricing-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
  align-items: end;
  gap: var(--space-4);
  padding: var(--space-5);
  border: 0.0625rem solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-secondary);
}

.invoice-pricing-row .form-group { min-width: 0; }
.invoice-pricing-row .icon-btn { margin-bottom: 0.1rem; }

.product-fields-list { display: grid; gap: var(--space-4); }

.product-field-row {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: var(--space-4);
  padding: var(--space-5);
  border: 0.0625rem solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-secondary);
}

.product-field-row .form-group { flex: 1 1 9rem; min-width: 9rem; }
.product-field-row .product-field-options,
.product-field-row .product-field-pattern { flex-basis: 100%; }
.product-field-row .product-field-required { flex: 0 0 auto; padding-bottom: var(--space-3); }
.product-field-row .icon-btn { flex: 0 0 auto; margin-bottom: var(--space-2); }

.invoice-subsection {
  display: grid;
  gap: var(--space-4);
  padding-top: var(--space-6);
  border-top: 0.0625rem solid var(--border);
}

.invoice-subsection:first-of-type { padding-top: 0; border-top: 0; }
.invoice-amounts-subsection:has(#invoice-route-amounts:empty) { display: none; }
.invoice-routes-field { margin: 0; }
.invoice-routes-field .select-trigger { min-height: 3rem; }

.invoice-route-option .asset-icon { width: 1.75rem; height: 1.75rem; }
.invoice-route-option .asset-icon-badge { width: 0.78rem; height: 0.78rem; }

.invoice-route-amount-row {
  display: grid;
  gap: var(--space-5);
  padding: var(--space-5);
  border: 0.0625rem solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-secondary);
}

.invoice-route-amount-heading {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-width: 0;
}

.invoice-route-amount-heading > span:last-child {
  display: grid;
  gap: var(--space-1);
  min-width: 0;
}

.invoice-route-amount-heading strong {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--text-sm);
}

.invoice-route-amount-heading small {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text-secondary);
  font-size: var(--text-xs);
}

.invoice-route-amount-icon {
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  flex: 0 0 2.25rem;
}

.invoice-route-amount-icon .asset-icon { width: 2.25rem; height: 2.25rem; }
.invoice-route-price-inputs { display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: var(--space-4); }

.invoice-advanced {
  overflow: hidden;
  border: 0.0625rem solid var(--border);
  border-radius: var(--radius-xl);
  background: var(--surface-overlay);
}

.invoice-advanced summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-6);
  color: var(--text-primary);
  cursor: pointer;
  list-style: none;
}

.invoice-advanced summary::-webkit-details-marker { display: none; }
.invoice-advanced summary > span { display: grid; gap: var(--space-1); }
.invoice-advanced summary strong { font-size: var(--text-sm); }
.invoice-advanced summary small { color: var(--text-secondary); font-size: var(--text-xs); }
.invoice-advanced summary > .icon { color: var(--text-secondary); transition: transform var(--duration-fast); }
.invoice-advanced[open] summary > .icon { transform: rotate(180deg); }

.invoice-advanced-body {
  display: grid;
  gap: var(--space-5);
  padding: 0 var(--space-6) var(--space-6);
}

.invoice-form-status {
  display: none;
  margin: 0;
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-lg);
  background: var(--surface-overlay);
  color: var(--text-secondary);
  overflow-wrap: anywhere;
}

.invoice-form-status[data-console-dirty="true"] { display: block; }

.invoice-modal-actions {
  position: sticky;
  bottom: 0;
  z-index: 2;
  margin: 0 calc(var(--space-8) * -1);
  padding: var(--space-5) var(--space-8);
  border-top: 0.0625rem solid var(--border);
  background: var(--bg-secondary);
}

.invoice-modal-actions .primary-btn { min-width: 10rem; }

@media (max-width: 42rem) {
  .modal-overlay:has(.modal-invoice) { padding: var(--space-3); align-items: flex-end; }
  .modal.modal-invoice { width: 100%; max-height: calc(100dvh - var(--space-6)); border-radius: var(--radius-xl); }
  .modal.modal-invoice .modal-header { padding: var(--space-6) var(--space-6) var(--space-5); }
  .modal.modal-invoice .invoice-form { padding: var(--space-6) var(--space-6) 0; gap: var(--space-5); }
  .invoice-mode-section { grid-template-columns: 1fr; gap: var(--space-5); }
  .invoice-form-section, .invoice-flexible-fields { padding-bottom: var(--space-5); }
  .invoice-section-heading { gap: var(--space-4); }
  .invoice-section-action { min-height: 2.5rem; }
  .invoice-modal-actions { margin-inline: calc(var(--space-6) * -1); padding-inline: var(--space-6); }
}

@media (max-width: 34rem) {
  .modal-body .form-row { grid-template-columns: 1fr; }
  .invoice-pricing-row {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-areas: "amount currency" "remove remove";
  }
  .invoice-pricing-row .form-group:nth-child(1) { grid-area: amount; }
  .invoice-pricing-row .form-group:nth-child(2) { grid-area: currency; }
  .invoice-pricing-row .icon-btn { grid-area: remove; justify-self: end; align-self: end; }
  .invoice-route-price-inputs { grid-template-columns: 1fr; }
  .invoice-section-heading { align-items: stretch; flex-direction: column; }
  .invoice-section-action { width: 100%; justify-content: center; }
}

/* Invoice creation stays intentionally narrow on desktop too. The form is
   a sequence of decisions, not a spreadsheet: one control per line keeps
   labels readable and gives the mobile layout the same visual hierarchy. */
.modal.modal-invoice {
  width: min(100%, 36rem);
  max-width: 36rem;
}

.modal.modal-invoice .invoice-form {
  padding-inline: var(--space-6);
}

.modal.modal-invoice .form-row,
.modal.modal-invoice .invoice-mode-section,
.modal.modal-invoice .invoice-pricing-row,
.modal.modal-invoice .invoice-route-price-inputs {
  grid-template-columns: 1fr;
}

.modal.modal-invoice .invoice-mode-section {
  align-items: stretch;
  gap: var(--space-5);
}

.modal.modal-invoice .invoice-pricing-row {
  gap: var(--space-5);
  grid-template-areas: none;
}

.modal.modal-invoice .invoice-pricing-row > * {
  grid-area: auto;
}

.modal.modal-invoice .invoice-pricing-row .icon-btn {
  justify-self: end;
  margin: 0;
}

.invoice-section-label,
.invoice-subsection-label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.invoice-section-label > [data-invoice-i18n],
.invoice-subsection-label > [data-invoice-i18n] {
  min-width: 0;
}

.invoice-section-label .info-icon-wrap,
.invoice-subsection-label .info-icon-wrap,
.invoice-advanced summary strong .info-icon-wrap {
  margin-inline-start: 0;
}

.info-popover {
  width: min(20rem, calc(100vw - 1rem));
}


.products-empty-card {
  display: flex;
  align-items: flex-start;
  gap: var(--space-5);
  padding: clamp(var(--space-6), 5vw, var(--space-9));
}

.products-empty-card h3 {
  margin: 0;
  font-size: var(--text-lg);
}

.products-empty-card p {
  max-width: 38rem;
  margin: var(--space-2) 0 0;
  color: var(--text-secondary);
}

.products-empty-icon {
  display: grid;
  flex: 0 0 3rem;
  width: 3rem;
  height: 3rem;
  place-items: center;
  color: var(--accent);
  border: 0.0625rem solid rgba(var(--accent-rgb), 0.35);
  border-radius: var(--radius-lg);
  background: var(--accent-soft);
}

.products-empty-icon .icon {
  width: 1.35rem;
  height: 1.35rem;
}

@media (max-width: 34rem) {
  .products-empty-card {
    flex-direction: column;
  }
}

@media (max-width: 42rem) {
  .modal.modal-invoice .invoice-form {
    padding-inline: var(--space-5);
  }

  .invoice-modal-actions {
    margin-inline: calc(var(--space-5) * -1);
    padding-inline: var(--space-5);
  }
}

/* Account and admin workspaces -----------------------------------------
   Radar's strongest pattern is a clear reading order: one hero, one
   decision at a time, then compact supporting cards. These overrides keep
   the shared tokens but remove the old tabbed/grid-heavy composition. */
.account-workspace,
.admin-workspace {
  gap: var(--space-7);
}

.shell-header-hidden {
  display: none;
}

.account-hero,
.admin-hero {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-7);
}

.account-hero-copy,
.admin-hero-copy {
  min-width: 0;
}

.account-hero h2,
.admin-hero h2 {
  margin-top: var(--space-2);
  color: var(--text-primary);
  font-size: clamp(1.8rem, 4vw, 2.65rem);
  letter-spacing: -0.04em;
  line-height: 1.05;
}

.account-hero p,
.admin-hero p {
  max-width: 42rem;
  margin-top: var(--space-4);
  color: var(--text-secondary);
  font-size: var(--text-md);
  line-height: 1.55;
}

.account-hero-meta,
.admin-hero-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: var(--space-4);
  flex: 0 0 auto;
}

.account-hero-id {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2);
  color: var(--text-secondary);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.07em;
}

.account-hero-id code {
  color: var(--text-primary);
  font-size: var(--text-sm);
  letter-spacing: 0;
}

.account-token-card {
  display: grid;
  gap: var(--space-5);
  padding: var(--space-7);
}

.account-card-heading {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  min-width: 0;
}

.account-card-heading > div {
  min-width: 0;
  flex: 1;
}

.account-card-heading h2,
.account-card-heading h3 {
  margin-top: var(--space-1);
  color: var(--text-primary);
  font-size: var(--text-lg);
  letter-spacing: -0.02em;
}

.account-card-icon {
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  flex: 0 0 2.25rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--accent);
  background: var(--accent-subtle);
}

.account-token-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  min-width: 0;
  padding: var(--space-4) var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-primary);
}

.account-token-row code {
  min-width: 0;
  overflow-wrap: anywhere;
  color: var(--text-primary);
  font-size: var(--text-sm);
  letter-spacing: 0.02em;
}

.account-token-hint {
  margin: 0;
  color: var(--text-secondary);
  font-size: var(--text-xs);
}

.account-primary-grid,
.account-secondary-grid {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: var(--space-7);
  align-items: stretch;
}

.account-secondary-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.account-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  min-width: 0;
  padding: var(--space-7);
}

.account-definition-list {
  display: grid;
  gap: 0;
  min-width: 0;
}

.account-definition-list .id-row {
  display: grid;
  grid-template-columns: minmax(7rem, 0.7fr) minmax(0, 1.3fr);
  align-items: baseline;
  gap: var(--space-5);
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--border);
}

.account-definition-list .id-row:first-child { padding-top: 0; }
.account-definition-list .id-row:last-child { border-bottom: 0; padding-bottom: 0; }

.account-definition-list .id-row > span:first-child {
  color: var(--text-secondary);
  font-size: var(--text-sm);
}

.account-definition-list .id-row > span:last-child {
  min-width: 0;
  overflow-wrap: anywhere;
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-weight: 600;
  text-align: end;
}

.account-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: auto;
  padding-top: var(--space-5);
  border-top: 1px solid var(--border);
}

.account-stat-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-3);
}

.account-stat {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 6.5rem;
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-primary);
}

.account-stat-label {
  overflow: hidden;
  color: var(--text-secondary);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-overflow: ellipsis;
  text-transform: uppercase;
  white-space: nowrap;
}

.account-stat strong {
  margin-top: var(--space-3);
  color: var(--text-primary);
  font-size: var(--text-2xl);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.account-stat-note {
  margin-top: auto;
  padding-top: var(--space-3);
  color: var(--text-secondary);
  font-size: var(--text-xs);
  line-height: 1.35;
}

.account-network-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
}

.account-network-list .account-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
  margin: 0;
  padding: var(--space-4);
  border-radius: var(--radius-lg);
  background: var(--bg-primary);
}

.account-network-list .account-row > span:nth-child(2) {
  display: grid;
  min-width: 0;
  gap: var(--space-1);
}

.account-network-list .account-row strong {
  overflow: hidden;
  color: var(--text-primary);
  font-size: var(--text-sm);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.account-network-list .account-row small {
  color: var(--text-secondary);
  font-size: var(--text-xs);
}

.account-network-list .account-row .badge {
  font-size: var(--text-xs);
}

.account-rates-card {
  margin-top: var(--space-7);
}

.account-rates-status {
  display: grid;
  justify-items: end;
  gap: var(--space-2);
  flex: 0 0 auto;
  text-align: end;
}

.account-rates-status small {
  color: var(--text-secondary);
  font-size: var(--text-xs);
}

.rate-list {
  display: grid;
  min-width: 0;
  border-top: 1px solid var(--border);
}

.rate-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-5);
  min-width: 0;
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--border);
}

.rate-row:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}

.rate-row:first-child {
  padding-top: var(--space-5);
}

.rate-row .asset-icon {
  width: 2.75rem;
  height: 2.75rem;
  flex-basis: 2.75rem;
}

.rate-row .asset-icon-image img,
.rate-row .asset-icon-coin {
  width: 2.5rem;
  height: 2.5rem;
}

.rate-row .asset-icon-coin .asset-glyph {
  width: 1.6rem;
  height: 1.6rem;
}

.rate-row .asset-icon-badge {
  width: 1.05rem;
  height: 1.05rem;
  border-width: 0.1rem;
}

.rate-row .asset-icon-badge img,
.rate-row .asset-icon-badge svg {
  width: 0.72rem;
  height: 0.72rem;
}

.rate-row-name,
.rate-row-value {
  display: grid;
  min-width: 0;
  gap: var(--space-1);
}

.rate-row-name strong,
.rate-row-value strong {
  overflow: hidden;
  color: var(--text-primary);
  font-size: var(--text-sm);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rate-row-name small,
.rate-row-value small {
  overflow: hidden;
  color: var(--text-secondary);
  font-size: var(--text-xs);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rate-row-value {
  justify-items: end;
  text-align: end;
}

.rate-row-value strong {
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

.admin-workspace .admin-hero {
  padding: var(--space-7);
}

.admin-workspace .admin-stats {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-4);
}

.admin-workspace .admin-stats > article {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 6.5rem;
  padding: var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-secondary);
}

.admin-workspace .admin-stats > article > span {
  color: var(--text-secondary);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.admin-workspace .admin-stats > article > strong {
  margin-top: var(--space-3);
  color: var(--text-primary);
  font-size: var(--text-2xl);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.admin-workspace .admin-stats > article > small {
  margin-top: auto;
  padding-top: var(--space-3);
  color: var(--text-secondary);
  font-size: var(--text-xs);
}

.admin-section {
  display: grid;
  gap: var(--space-5);
  min-width: 0;
  padding-top: var(--space-7);
  border-top: 1px solid var(--border);
}

.admin-section .content-header {
  align-items: flex-end;
}

.admin-section .content-header > div:first-child {
  min-width: 0;
}

.admin-section .content-header h2 {
  margin-top: var(--space-2);
  font-size: var(--text-xl);
  letter-spacing: -0.025em;
}

.admin-section .content-header p {
  max-width: 42rem;
  margin-top: var(--space-2);
  color: var(--text-secondary);
  font-size: var(--text-sm);
  line-height: 1.5;
}

.admin-workspace .admin-panel,
.admin-workspace #admin-panel-providers {
  grid-template-columns: 1fr;
}

.admin-workspace .admin-panel > header {
  grid-column: 1;
  align-self: start;
  padding-top: var(--space-2);
  min-width: 0;
}

.admin-workspace .admin-panel > .glass-card,
.admin-workspace .admin-panel > #admin-tokens-list,
.admin-workspace .admin-panel > #admin-system-summary,
.admin-workspace .admin-panel > #maintenance-output {
  grid-column: 1;
  min-width: 0;
}

.admin-workspace .admin-system-summary {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0 var(--space-6);
}

.admin-workspace .admin-system-summary .id-row {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--border);
}

.admin-workspace .admin-system-summary .id-row > span:last-child {
  text-align: start;
}

@media (max-width: 58rem) {
  .account-primary-grid,
  .account-secondary-grid {
    grid-template-columns: 1fr;
  }

  .admin-workspace .admin-stats,
  .admin-workspace .admin-system-summary {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 38rem) {
  .account-hero,
  .admin-hero {
    align-items: stretch;
    flex-direction: column;
  }

  .account-hero-meta,
  .admin-hero-actions {
    justify-content: flex-start;
  }

  .account-token-row {
    align-items: stretch;
    flex-direction: column;
  }

  .account-token-row .btn-row,
  .account-token-row .btn-row > * {
    flex: 1;
  }

  .account-definition-list .id-row {
    grid-template-columns: 1fr;
    gap: var(--space-2);
  }

  .account-definition-list .id-row > span:last-child {
    text-align: start;
  }

  .account-stat-grid,
  .account-network-list,
  .admin-workspace .admin-stats,
  .admin-workspace .admin-system-summary {
    grid-template-columns: 1fr 1fr;
  }
}

.checkout-receipt-actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-4);
  width: 100%;
}

.checkout-receipt-return-btn,
.checkout-receipt-download-btn {
  width: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding-block: var(--space-3);
  font-size: var(--text-sm);
  text-decoration: none;
}

@media print {
  @page {
    margin: 1.5cm;
    size: auto;
  }

  html,
  body {
    background: #ffffff !important;
    background-color: #ffffff !important;
    color: #111827 !important;
    font-size: 10pt !important;
    padding: 0 !important;
    margin: 0 !important;
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;
  }

  .public-header-actions,
  .checkout-receipt-actions,
  .field-inline-btn,
  .ext-link-icon,
  .checkout-qr-toggle,
  .checkout-change-btn,
  .modal-overlay,
  #toast,
  button,
  .checkout-divider {
    display: none !important;
  }

  .public-page,
  .public-frame,
  .checkout-page,
  .checkout-content,
  .checkout-shell {
    background: #ffffff !important;
    background-color: #ffffff !important;
    border: none !important;
    box-shadow: none !important;
    padding: 0 !important;
    margin: 0 auto !important;
    max-width: 480px !important;
    width: 100% !important;
  }

  .checkout-card {
    background: #ffffff !important;
    background-color: #ffffff !important;
    border: 1px solid #e5e7eb !important;
    border-radius: 12px !important;
    box-shadow: none !important;
    backdrop-filter: none !important;
    padding: 24px !important;
    margin: 0 auto !important;
  }

  .checkout-hero-amount {
    color: #059669 !important;
    font-size: 24pt !important;
  }

  .checkout-hero-amount .unit {
    color: #059669 !important;
  }

  .checkout-hero-eyebrow {
    color: #6b7280 !important;
    font-weight: 600 !important;
    letter-spacing: 0.05em !important;
  }

  .checkout-hero-fiat {
    color: #4b5563 !important;
  }

  .checkout-section-label {
    color: #374151 !important;
    font-weight: 600 !important;
  }

  .checkout-receipt-box {
    background: #f9fafb !important;
    background-color: #f9fafb !important;
    border: 1px solid #e5e7eb !important;
    border-radius: 8px !important;
    padding: 10px 14px !important;
    margin-bottom: 14px !important;
  }

  .checkout-kv {
    border-bottom: 1px solid #e5e7eb !important;
    padding: 8px 0 !important;
  }

  .checkout-kv .k {
    color: #4b5563 !important;
    font-weight: 500 !important;
  }

  .checkout-kv .v {
    color: #111827 !important;
    font-weight: 600 !important;
  }

  .receipt-tx-link {
    color: #2563eb !important;
    text-decoration: underline !important;
  }

  .checkout-brand-interactive {
    filter: invert(1) brightness(0.2);
  }
}

