/* ==========================================================================
   Nexus Shell — homepage v2 (vibeisland-inspired)
   --------------------------------------------------------------------------
   Self-contained dark-first design for index.html. Layered ON TOP of
   style.css (the shared design system) — only re-skins HOMEPAGE elements
   via the `nx-` prefix and `.nx-body` class scope. Other pages are
   unaffected.

   Design choices:
     * Pure dark canvas (#0a0a0d), subtle radial glow + faint grid
     * Monospace display headline with a "scramble + settle" accent word
       (see home.js for the JS, here we just style the span)
     * Pixel-edges SVG icons in feature cards (mirrors vibeisland's
       developer-tool vocabulary)
     * Reveal-on-scroll: opacity 0 → 1 + 8px translateY, IntersectionObserver
     * Honors prefers-reduced-motion everywhere (no animation, no scramble)
   ========================================================================== */

/* ---- Tokens ---- */
.nx-body {
  --nx-bg:        #0a0a0d;
  --nx-bg-2:      #111114;
  --nx-text:      rgba(255, 255, 255, 0.92);
  --nx-text-2:    rgba(255, 255, 255, 0.62);
  --nx-text-3:    rgba(255, 255, 255, 0.55);
  --nx-border:    rgba(255, 255, 255, 0.10);
  --nx-border-2:  rgba(255, 255, 255, 0.18);
  --nx-surface:   rgba(255, 255, 255, 0.03);
  --nx-surface-2: rgba(255, 255, 255, 0.06);
  --nx-brand:     #387ae0;       /* Nexus brand blue from app */
  --nx-brand-2:   #5eaaff;
  --nx-accent:    #1ab38c;       /* App's teal accent */
  --nx-warn:      #f59e0b;
  --nx-success:   #22c55e;       /* status dot green */
  --nx-magenta:   #d97757;       /* warm orange — for cycle word variety */
  --nx-violet:    #a855f7;
  --nx-radius-sm: 8px;
  --nx-radius:    12px;
  --nx-radius-lg: 18px;
  --nx-ease:      cubic-bezier(0.22, 1, 0.36, 1);
  --nx-mono:      "JetBrains Mono", "SF Mono", "IBM Plex Mono", "Fira Code", ui-monospace, monospace;
  --nx-sans:      -apple-system, "system-ui", "SF Pro Text", "Segoe UI", "PingFang SC",
                  "Hiragino Sans", "Noto Sans CJK SC", system-ui, sans-serif;

  /* Override the legacy style.css font tokens so non-home pages don't
     pick up "Inter" (style.css's --ff-sans first choice) and end up with
     a visibly different sans face. Keep the mono stack identical too. */
  --ff-sans: var(--nx-sans);
  --ff-mono: var(--nx-mono);
}

/* Light-theme override — keeps theme-toggle working but the canvas
   stays mostly dark on this page (vibeisland is dark-only; we offer a
   restrained light theme for users who insist). */
.nx-body[data-theme="light"] {
  --nx-bg:        #f6f6f7;
  --nx-bg-2:      #ffffff;
  --nx-text:      rgba(15, 15, 20, 0.92);
  --nx-text-2:    rgba(15, 15, 20, 0.70);
  --nx-text-3:    rgba(15, 15, 20, 0.65);
  --nx-border:    rgba(15, 15, 20, 0.10);
  --nx-border-2:  rgba(15, 15, 20, 0.18);
  --nx-surface:   rgba(15, 15, 20, 0.04);
  --nx-surface-2: rgba(15, 15, 20, 0.06);
}

/* ---- Page reset (only for nx-body — doesn't leak to other pages) ---- */
.nx-body {
  margin: 0;
  background: var(--nx-bg);
  color: var(--nx-text);
  font-family: var(--nx-sans);
  font-size: 15px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* The dot-grid background is positioned by .nx-bg below */
}
.nx-body * { box-sizing: border-box; }
.nx-body img { display: block; max-width: 100%; height: auto; }
.nx-body a { color: inherit; text-decoration: none; }
.nx-body button { font-family: inherit; cursor: pointer; border: none; background: none; color: inherit; }

/* ---- Background canvas ----
   Layered (back→front, all position:fixed so they survive scroll):
     1. solid bg color (from .nx-body)
     2. .nx-bg-term — a long mock SSH session as raw mono text. Renders an
        actual-looking terminal session (commands, output, prompts, cursor)
        at very low alpha so it reads as ambient texture, not as content.
     3. top brand-blue glow (hero)
     4. ambient color orbs — teal bottom-left, violet middle-right (depth)
     5. animated scan beam — thin blue band slowly sweeps top→bottom (CRT/radar)
     6. corner ASCII-bracket glyphs with blinking cursor (terminal signature)
*/
.nx-bg {
  position: fixed; inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
}
/* Mock SSH session as a fixed full-viewport <pre>. Authentic-looking shell
   output (uptime, free, docker ps, top, ls) rendered in mono at ~7% white.
   Vignette mask fades it at edges so it never competes with foreground. */
.nx-bg-term {
  position: absolute; inset: 0;
  margin: 0;
  padding: 64px 3.5% 40px 3.5%;
  font-family: var(--nx-mono);
  font-size: 11.5px;
  line-height: 1.65;
  color: rgba(255, 255, 255, 0.07);
  white-space: pre;
  overflow: hidden;
  user-select: none;
  pointer-events: none;
  /* Vignette: text strongest in center, fades out at all edges */
  mask-image: radial-gradient(ellipse 105% 95% at 50% 40%, #000 30%, rgba(0, 0, 0, 0.5) 80%, transparent 100%);
  -webkit-mask-image: radial-gradient(ellipse 105% 95% at 50% 40%, #000 30%, rgba(0, 0, 0, 0.5) 80%, transparent 100%);
}
/* Highlight a few characters in the mock session for variety —
   shell prompts, OK markers, accents — without making the whole block bright. */
.nx-bg-term .b-prompt { color: rgba(94, 170, 255, 0.18); }
.nx-bg-term .b-ok     { color: rgba(34, 197, 94, 0.18); }
.nx-bg-term .b-warn   { color: rgba(245, 158, 11, 0.18); }
.nx-bg-term .b-cmd    { color: rgba(255, 255, 255, 0.16); }
.nx-bg-term .b-cursor {
  color: rgba(94, 170, 255, 0.55);
  animation: nx-cursor-blink 1.05s steps(2, end) infinite;
}
.nx-bg-glow {
  position: absolute; left: 50%; top: -200px; transform: translateX(-50%);
  width: 1200px; height: 800px;
  background: radial-gradient(ellipse 50% 50% at 50% 50%, rgba(56, 122, 224, 0.22) 0%, transparent 70%);
  filter: blur(20px);
}
.nx-bg-orb {
  position: absolute;
  border-radius: 50%;
  filter: blur(70px);
  pointer-events: none;
}
.nx-bg-orb-teal {
  bottom: -300px; left: -260px;
  width: 760px; height: 760px;
  background: radial-gradient(circle, rgba(26, 179, 140, 0.32) 0%, transparent 70%);
}
.nx-bg-orb-violet {
  top: 30%; right: -260px;
  width: 660px; height: 660px;
  background: radial-gradient(circle, rgba(168, 85, 247, 0.26) 0%, transparent 70%);
}
/* Animated horizontal scan beam — thin glowing band crosses the viewport
   top → bottom every 14s. Origin starts well above the viewport so the beam
   visibly emerges from above the (now-translucent) nav and travels down. */
.nx-bg-scan {
  position: absolute;
  left: -10%; right: -10%;
  height: 260px;
  background: linear-gradient(
    180deg,
    transparent 0%,
    rgba(94, 170, 255, 0.08) 30%,
    rgba(94, 170, 255, 0.30) 50%,
    rgba(94, 170, 255, 0.08) 70%,
    transparent 100%
  );
  filter: blur(2px);
  animation: nx-bg-beam-sweep 14s cubic-bezier(0.6, 0, 0.4, 1) infinite;
  will-change: top, opacity;
}
@keyframes nx-bg-beam-sweep {
  /* Start high above the viewport so the beam is already alive when its
     leading edge enters the nav area, giving a "shooting in from above" feel. */
  0%   { top: -340px; opacity: 0; }
  4%   { opacity: 1; }
  96%  { opacity: 1; }
  100% { top: 100%; opacity: 0; }
}
/* Mono brackets in viewport corners — geek signature */
.nx-bg-corner {
  position: absolute;
  font-family: var(--nx-mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  color: rgba(255, 255, 255, 0.22);
  user-select: none;
  white-space: pre;
}
/* top-left sits below the floating nav (which paints its own gradient bar
   across the very top), bottom-right tucks into the viewport corner. */
.nx-bg-corner-br { bottom: 16px; right: 18px; }
/* Blinking terminal cursor on the top-left glyph */
@keyframes nx-cursor-blink {
  50% { opacity: 0; }
}

/* Light-theme scaling */
.nx-body[data-theme="light"] .nx-bg-term {
  color: rgba(15, 15, 20, 0.10);
}
.nx-body[data-theme="light"] .nx-bg-term .b-prompt { color: rgba(56, 122, 224, 0.30); }
.nx-body[data-theme="light"] .nx-bg-term .b-ok     { color: rgba(34, 150, 80, 0.28); }
.nx-body[data-theme="light"] .nx-bg-term .b-warn   { color: rgba(220, 130, 30, 0.28); }
.nx-body[data-theme="light"] .nx-bg-term .b-cmd    { color: rgba(15, 15, 20, 0.30); }
.nx-body[data-theme="light"] .nx-bg-term .b-cursor { color: rgba(56, 122, 224, 0.55); }
.nx-body[data-theme="light"] .nx-bg-glow {
  background: radial-gradient(ellipse 50% 50% at 50% 50%, rgba(56, 122, 224, 0.12) 0%, transparent 70%);
}
.nx-body[data-theme="light"] .nx-bg-orb-teal {
  background: radial-gradient(circle, rgba(26, 179, 140, 0.10) 0%, transparent 70%);
}
.nx-body[data-theme="light"] .nx-bg-orb-violet {
  background: radial-gradient(circle, rgba(168, 85, 247, 0.08) 0%, transparent 70%);
}
.nx-body[data-theme="light"] .nx-bg-scan { display: none; }
.nx-body[data-theme="light"] .nx-bg-corner {
  color: rgba(15, 15, 20, 0.32);
}

/* Honor reduced-motion: stop the sweeping beam and the cursor blink */
@media (prefers-reduced-motion: reduce) {
  .nx-bg-scan { display: none; }
}

/* ---- Container ---- */
.nx-container {
  width: 100%;
  max-width: 1180px;
  margin: 0 auto;
  padding: 0 24px;
}

/* ---- sr-only ---- */
.nx-body .sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ============================================================
   Nav
   ============================================================ */
.nx-nav {
  position: sticky;
  top: 0;
  z-index: 50;
  border-bottom: none;
  /* IMPORTANT: bg / blur / mask all live on ::before so absolutely-
     positioned descendants (user-dropdown, mobile-drawer) extend BELOW
     the nav unclipped. Putting mask-image directly on .nx-nav clips
     ALL descendants — including the dropdown that opens beneath it. */
}
.nx-nav::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  backdrop-filter: saturate(180%) blur(18px);
  -webkit-backdrop-filter: saturate(180%) blur(18px);
  /* Translucent so the terminal text + scan beam clearly show through.
     Top is barely tinted, fades to fully transparent at the bottom. */
  background: linear-gradient(180deg, rgba(10, 10, 13, 0.32) 0%, rgba(10, 10, 13, 0.10) 60%, rgba(10, 10, 13, 0) 100%);
  mask-image: linear-gradient(180deg, #000 0%, #000 70%, transparent 100%);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 70%, transparent 100%);
}
.nx-body[data-theme="light"] .nx-nav::before {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.55) 0%, rgba(255, 255, 255, 0.20) 60%, rgba(255, 255, 255, 0) 100%);
}
.nx-nav-inner {
  max-width: 1180px;
  margin: 0 auto;
  padding: 0 24px;
  display: flex; align-items: center;
  height: 56px;
  gap: 16px;
}
.nx-brand {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--nx-mono);
  font-size: 13.5px;
  letter-spacing: 0.02em;
  font-weight: 500;
  color: var(--nx-text);
  margin-right: auto;
}
.nx-brand img {
  width: 22px; height: 22px;
  border-radius: 5px;
  background: var(--nx-surface-2);
  /* Slight outline so the white-bg logo doesn't dissolve into dark nav */
  box-shadow: 0 0 0 1px var(--nx-border) inset;
}
.nx-brand-name {
  color: var(--nx-text);
  /* Brand should never wrap to two lines, even when the nav is tight on
     mobile. Squeezing the brand wrap is the most common mobile-nav bug
     this site had — left text-link visibility could push it over. */
  white-space: nowrap;
}
.nx-nav-right {
  display: inline-flex; align-items: center; gap: 4px;
}
.nx-nav-link {
  display: inline-flex; align-items: center;
  padding: 6px 10px;
  font-size: 13px;
  color: var(--nx-text-2);
  border-radius: 6px;
  transition: color 140ms var(--nx-ease), background 140ms var(--nx-ease);
}
.nx-nav-link:hover { color: var(--nx-text); background: var(--nx-surface); }

.nx-icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  height: 30px;
  border-radius: 6px;
  color: var(--nx-text-2);
  transition: color 140ms var(--nx-ease), background 140ms var(--nx-ease);
}
.nx-icon-btn { width: 30px; }
.nx-icon-btn:hover { color: var(--nx-text); background: var(--nx-surface); }
/* Sun/moon visibility — synced with main.js's data-theme attribute */

/* Language picker dropdown — wraps the .nx-lang trigger and the popover. */
.nx-lang-menu { position: relative; display: inline-flex; }
.nx-lang-trigger {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 0 8px 0 9px;
  height: 30px;
  border-radius: 6px;
  color: var(--nx-text-2);
  font-size: 12px;
  font-family: var(--nx-mono);
  cursor: pointer;
  transition: color 140ms var(--nx-ease), background 140ms var(--nx-ease);
}
.nx-lang-trigger:hover { color: var(--nx-text); background: var(--nx-surface); }
.nx-lang-menu.open .nx-lang-trigger { color: var(--nx-text); background: var(--nx-surface); }
.nx-lang-chevron {
  color: currentColor;
  opacity: 0.7;
  transition: transform 140ms var(--nx-ease);
  flex-shrink: 0;
}
.nx-lang-menu.open .nx-lang-chevron { transform: rotate(180deg); }
.nx-lang-dropdown {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  min-width: 140px;
  /* Mostly-opaque so the panel stays legible against the terminal background.
     A faint translucency + backdrop blur keeps it consistent with the nav. */
  background: rgba(18, 18, 22, 0.96);
  backdrop-filter: saturate(180%) blur(18px);
  -webkit-backdrop-filter: saturate(180%) blur(18px);
  border: 1px solid var(--nx-border);
  border-radius: 10px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
  padding: 5px;
  z-index: 60;
  animation: nx-lang-dropdown-in 140ms var(--nx-ease);
}
.nx-body[data-theme="light"] .nx-lang-dropdown {
  background: rgba(255, 255, 255, 0.96);
}
@keyframes nx-lang-dropdown-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.nx-lang-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  padding: 7px 10px;
  border-radius: 6px;
  font-size: 13px;
  color: var(--nx-text);
  text-align: left;
  cursor: pointer;
  background: transparent;
  border: none;
  transition: background 120ms var(--nx-ease), color 120ms var(--nx-ease);
}
.nx-lang-item:hover { background: var(--nx-surface); }
.nx-lang-item[aria-current="true"] {
  color: var(--nx-accent, var(--nx-text));
  font-weight: 500;
}
.nx-lang-item[aria-current="true"]::after {
  content: '';
  width: 6px; height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}

/* ============================================================
   Mobile hamburger menu button
   ============================================================
   Hidden by default; shown by the @media (max-width: 720px) block
   below. Drives the .mobile-drawer below the nav. */
.nx-menu-btn {
  display: none;
  align-items: center; justify-content: center;
  width: 36px; height: 36px;
  margin-left: 2px;
  border-radius: 8px;
  background: transparent;
  border: 1px solid var(--nx-border);
  color: var(--nx-text);
  cursor: pointer;
  transition: background 140ms var(--nx-ease), border-color 140ms var(--nx-ease);
}
.nx-menu-btn:hover,
.nx-menu-btn[aria-expanded="true"] {
  background: var(--nx-surface);
  border-color: var(--nx-text-2);
}
/* Hamburger → close cross via CSS transforms. */
.nx-menu-btn .i-bar {
  display: block;
  width: 18px; height: 1.5px;
  background: currentColor;
  border-radius: 2px;
  transition: transform 180ms var(--nx-ease), opacity 180ms var(--nx-ease);
}
.nx-menu-btn .i-bar + .i-bar { margin-top: 4px; }
.nx-menu-btn .i-bar + .i-bar + .i-bar { margin-top: 4px; }
.nx-menu-btn[aria-expanded="true"] .i-bar:nth-child(1) { transform: translateY(5.5px) rotate(45deg); }
.nx-menu-btn[aria-expanded="true"] .i-bar:nth-child(2) { opacity: 0; }
.nx-menu-btn[aria-expanded="true"] .i-bar:nth-child(3) { transform: translateY(-5.5px) rotate(-45deg); }

/* ============================================================
   Mobile drawer (slides in below the nav)
   ============================================================
   The base .mobile-drawer rules are in style.css from the original
   design; here we adapt the surface tokens to the home.css `--nx-*`
   palette so the drawer matches the dark terminal look. */
.mobile-drawer {
  /* Inherits display:none + flex-column from style.css. Keep the
     drawer pinned below the nav so it doesn't get scroll-jacked. */
  position: relative;
  background: rgba(18, 18, 22, 0.96);
  backdrop-filter: saturate(180%) blur(18px);
  -webkit-backdrop-filter: saturate(180%) blur(18px);
  border-bottom: 1px solid var(--nx-border);
  padding: 12px 18px 18px;
  gap: 2px;
  /* Respect iOS notch padding when the drawer extends to the bottom of
     the viewport — viewport-fit=cover is already set on every page. */
  padding-left:  max(18px, env(safe-area-inset-left));
  padding-right: max(18px, env(safe-area-inset-right));
}
.mobile-drawer > a {
  color: var(--nx-text);
  border-bottom-color: var(--nx-border);
  padding: 14px 4px;
  font-size: 15px;
  border-radius: 0;
}
.mobile-drawer > a:hover,
.mobile-drawer > a:focus-visible {
  color: var(--nx-text);
  background: var(--nx-surface);
}
.mobile-drawer > a:last-of-type {
  border-bottom: 0;
}
.drawer-section-label {
  font-size: 11px;
  font-family: var(--nx-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--nx-text-3, var(--nx-text-2));
  padding: 14px 4px 8px;
}
.drawer-lang-group {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 6px 0 12px;
}
.drawer-lang-group .nx-lang-item {
  flex: 1 0 calc(50% - 4px);
  justify-content: center;
  background: var(--nx-surface);
  border: 1px solid var(--nx-border);
  padding: 10px 12px;
}
.drawer-cta {
  display: flex;
  margin-top: 14px;
}
.drawer-cta .nx-btn {
  width: 100%;
}

/* ============================================================
   Buttons
   ============================================================ */
.nx-btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 8px;
  padding: 10px 18px;
  border-radius: 10px;
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: -0.005em;
  border: 1px solid transparent;
  white-space: nowrap;
  user-select: none;
  cursor: pointer;
  transition: all 160ms var(--nx-ease);
}
.nx-btn-sm  { padding: 7px 13px;  font-size: 13px;   border-radius: 8px; }
.nx-btn-lg  { padding: 13px 22px; font-size: 14.5px; border-radius: 12px; }
.nx-btn-block { width: 100%; }

/* Primary CTA — pure white pill, dark text. Reads as the page's
   "click-this-first" action while staying tonally consistent with
   the dark-only canvas (no extra brand color to clash with the demo's
   already-blue accents). The compound `.nx-btn.nx-btn-primary`
   selector beats `.nx-body a { color: inherit }` on specificity
   (0,2,0 vs 0,1,1) so the dark text doesn't get clobbered into white. */
.nx-btn.nx-btn-primary {
  background: #fff;
  color: #0a0a0d;
  border-color: rgba(255, 255, 255, 0.92);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.6) inset,
    0 8px 22px -8px rgba(0, 0, 0, 0.4);
}
.nx-btn.nx-btn-primary:hover {
  background: #f4f4f7;
  transform: translateY(-1px);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.6) inset,
    0 12px 28px -8px rgba(0, 0, 0, 0.5);
}
.nx-btn.nx-btn-primary:active { transform: translateY(0) scale(0.98); }
.nx-btn.nx-btn-primary svg { color: #0a0a0d; }

.nx-btn-ghost {
  /* Slightly more presence than --nx-surface / --nx-border so the
     button reads clearly against the busy terminal-text background,
     while still staying secondary vs. the solid white primary CTA. */
  background: rgba(255, 255, 255, 0.06);
  color: var(--nx-text);
  border-color: rgba(255, 255, 255, 0.22);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.nx-btn-ghost:hover {
  background: rgba(255, 255, 255, 0.10);
  border-color: rgba(255, 255, 255, 0.32);
}
.nx-body[data-theme="light"] .nx-btn-ghost {
  background: rgba(15, 15, 20, 0.04);
  border-color: rgba(15, 15, 20, 0.20);
}
.nx-body[data-theme="light"] .nx-btn-ghost:hover {
  background: rgba(15, 15, 20, 0.07);
  border-color: rgba(15, 15, 20, 0.30);
}

/* Loading state — matches main.js's setCheckoutButton */
.btn-loading { position: relative; pointer-events: none; opacity: 0.92; }
.btn-loading > .btn-label { opacity: 0.85; }
.btn-loading::before {
  content: ""; width: 13px; height: 13px;
  border-radius: 50%;
  border: 2px solid currentColor; border-top-color: transparent;
  animation: nx-spin 0.7s linear infinite;
  flex: 0 0 auto;
}
@keyframes nx-spin { to { transform: rotate(360deg); } }

/* ============================================================
   Hero
   ============================================================ */
.nx-hero {
  position: relative;
  padding: 64px 0 0;
  text-align: center;
}
.nx-headline {
  font-family: var(--nx-mono);
  font-weight: 400;
  font-size: clamp(34px, 5.6vw, 60px);
  line-height: 1.18;
  letter-spacing: -0.02em;
  margin: 0 0 18px;
  color: var(--nx-text);
}
/* The cycle accent — color is set inline by JS to match the current word.
   We size + lock width so the layout doesn't dance during scrambles. */
.nx-cycle {
  display: inline-block;
  min-width: 1ch;
  color: var(--nx-brand-2);
  font-feature-settings: "tnum" 1;
  /* Each scramble char is wrapped in <span> so we can color them individually
     if we wanted. For now they inherit. */
}
.nx-cycle > span { display: inline-block; }

.nx-sub {
  margin: 0 auto 28px;
  max-width: 540px;
  font-size: 15.5px;
  color: var(--nx-text-2);
  line-height: 1.6;
}
.nx-cta {
  display: inline-flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  margin-bottom: 18px;
}
.nx-meta {
  font-family: var(--nx-mono);
  font-size: 12px;
  color: var(--nx-text-3);
  letter-spacing: 0.04em;
  margin: 0 0 56px;
}

/* ============================================================
   AI provider compatibility strip (hero, below CTAs)
   ------------------------------------------------------------
   A quiet two-line "trust bar":
     line 1  — small uppercase mono caption ("兼容你常用的 AI")
     line 2  — three brand mark + name groups in a row, hairline
               dividers between them
   No pill, no chrome, no animation, no cycling — the three
   providers are all visible simultaneously so users get the
   full picture at a glance. Hover individually brightens a
   single provider for a soft tactile feel.
   ============================================================ */
.nx-ai-strip {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin: 0 0 24px;
}
.nx-ai-strip-label {
  font-family: var(--nx-mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--nx-text-3);
}
.nx-ai-strip-items {
  list-style: none;
  margin: 0;
  padding: 0;
  display: inline-flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
  justify-content: center;
}
.nx-ai-strip-item {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--nx-text);
  opacity: 0.85;
  transition: opacity 200ms var(--nx-ease), transform 200ms var(--nx-ease);
}
.nx-ai-strip-item:hover {
  opacity: 1;
  transform: translateY(-1px);
}
.nx-ai-strip-mark {
  display: inline-block;
  width: 20px; height: 20px;
  flex: none;
  /* The mark is an empty <span>; the brand SVG is painted via CSS
     mask-image (one URL per provider). `background-color` is what
     actually paints — by setting it to a brand-specific value per
     provider, the silhouette inherits the brand color regardless of
     the source SVG's own fill. Lets us swap any provider's SVG
     without touching the HTML. */
  background-color: currentColor;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: contain;
  mask-size: contain;
}
.nx-ai-strip-item[data-provider="openai"] .nx-ai-strip-mark {
  -webkit-mask-image: url('../img/ai/openai.svg');
          mask-image: url('../img/ai/openai.svg');
  background-color: var(--nx-text);   /* OpenAI logo is conventionally
                                          rendered in monochrome — sits
                                          on dark, reads as the page text */
}
.nx-ai-strip-item[data-provider="anthropic"] .nx-ai-strip-mark {
  -webkit-mask-image: url('../img/ai/anthropic.svg');
          mask-image: url('../img/ai/anthropic.svg');
  background-color: #d97757;          /* Anthropic brand orange */
}
.nx-ai-strip-item[data-provider="deepseek"] .nx-ai-strip-mark {
  -webkit-mask-image: url('../img/ai/deepseek.svg');
          mask-image: url('../img/ai/deepseek.svg');
  background-color: #4d6bfe;          /* DeepSeek brand blue */
}
.nx-body[data-theme="light"] .nx-ai-strip-item[data-provider="openai"] .nx-ai-strip-mark {
  background-color: var(--nx-text);   /* still flips with theme */
}
.nx-ai-strip-name {
  font-family: var(--nx-sans);
  font-size: 13.5px;
  font-weight: 500;
  color: var(--nx-text);
  letter-spacing: 0;
}
/* Hairline vertical divider between each provider item. */
.nx-ai-strip-divider {
  width: 1px;
  height: 16px;
  background: var(--nx-border);
}
.nx-body[data-theme="light"] .nx-ai-strip-divider {
  background: rgba(15, 15, 20, 0.10);
}
@media (max-width: 560px) {
  .nx-ai-strip-items { gap: 12px; }
  .nx-ai-strip-name { font-size: 12.5px; }
  .nx-ai-strip-mark { width: 20px; height: 20px; }
  .nx-ai-strip-mark svg { width: 16px; height: 16px; }
  .nx-ai-strip-divider { height: 14px; }
}

/* Hero product mockup
   Frame the screenshot in a glass-like surface with inner highlight + outer
   ambient glow. Subtle hover lift to signal "this is real product UI". */

/* ============================================================
   Features grid (9 cards, 3-col on desktop)
   ============================================================ */
.nx-section-head {
  text-align: center;
  margin: 0 auto 40px;
  max-width: 640px;
}
.nx-section-head h2 {
  font-family: var(--nx-mono);
  font-weight: 400;
  font-size: clamp(22px, 3vw, 30px);
  letter-spacing: -0.01em;
  margin: 0 0 10px;
  color: var(--nx-text);
}
.nx-section-sub {
  margin: 0;
  font-size: 14.5px;
  color: var(--nx-text-2);
  line-height: 1.6;
}

/* ============================================================
   Screen tour (5 alternating rows)
   ============================================================ */

/* ============================================================
   Pricing
   ============================================================ */
.nx-pricing {
  padding: 80px 0;
}
.nx-price-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 18px;
  max-width: 880px;
  margin: 0 auto;
}
.nx-plan {
  position: relative;
  /* Solid-enough dark base so card content stays legible on the textured
     terminal-text background. The backdrop blur softens whatever shows
     through, giving the card a frosted-glass feel. */
  background: rgba(15, 15, 20, 0.72);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border: 1px solid var(--nx-border);
  border-radius: var(--nx-radius-lg);
  padding: 28px;
  display: flex; flex-direction: column;
}
.nx-plan-pro {
  /* Pro card stacks a brand-blue tint over the same dark base. */
  background:
    linear-gradient(180deg, rgba(56, 122, 224, 0.16) 0%, rgba(56, 122, 224, 0.04) 60%),
    rgba(15, 15, 20, 0.78);
  border-color: rgba(56, 122, 224, 0.30);
  box-shadow:
    0 0 0 1px rgba(56, 122, 224, 0.15) inset,
    0 18px 40px -18px rgba(56, 122, 224, 0.40),
    0 8px 20px -8px rgba(0, 0, 0, 0.40);
}
.nx-body[data-theme="light"] .nx-plan {
  background: rgba(255, 255, 255, 0.85);
}
.nx-body[data-theme="light"] .nx-plan-pro {
  background:
    linear-gradient(180deg, rgba(56, 122, 224, 0.10) 0%, rgba(56, 122, 224, 0.02) 60%),
    rgba(255, 255, 255, 0.92);
}
.nx-plan-badge {
  position: absolute; top: 14px; right: 14px;
  padding: 5px 12px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: #fff;
  background: linear-gradient(135deg, #ff9b3d, #ff5e7e);
  /* Soft glow pulls focus to the headline marketing copy without
     yelling — it’s a launch deal, not a banner ad. */
  box-shadow:
    0 4px 14px -4px rgba(255, 94, 126, 0.55),
    0 0 0 1px rgba(255, 255, 255, 0.10) inset;
  white-space: nowrap;
}
.nx-plan-head h3 {
  font-family: var(--nx-mono);
  font-weight: 500;
  font-size: 18px;
  margin: 0 0 4px;
  color: var(--nx-text);
}
.nx-plan-head p {
  margin: 0 0 18px;
  font-size: 13px;
  color: var(--nx-text-2);
}
.nx-plan-price {
  display: flex; align-items: baseline; gap: 8px;
  margin-bottom: 22px;
  padding-bottom: 18px;
  border-bottom: 1px dashed var(--nx-border);
}
.nx-plan-price .amt {
  font-family: var(--nx-mono);
  font-size: 36px;
  font-weight: 500;
  letter-spacing: -0.025em;
  color: var(--nx-text);
}
.nx-plan-price .amt-orig {
  font-size: 16px;
  color: var(--nx-text-3);
  text-decoration: line-through;
  text-decoration-thickness: 1.5px;
}
.nx-plan-price .per {
  margin-left: auto;
  font-size: 12px;
  color: var(--nx-text-2);
  font-family: var(--nx-mono);
}
.nx-plan-body {
  margin: 0 0 18px;
  font-size: 13.5px;
  color: var(--nx-text-2);
  line-height: 1.6;
}
.nx-plan-features {
  list-style: none;
  padding: 0; margin: 0 0 22px;
  display: flex; flex-direction: column;
  gap: 10px;
}
.nx-plan-features li {
  display: flex; gap: 9px; align-items: flex-start;
  font-size: 13px;
  color: var(--nx-text);
  line-height: 1.55;
}
.nx-plan-features li > svg { flex: none; margin-top: 3px; color: var(--nx-success); }
.nx-plan-features li small {
  display: block; margin-top: 3px;
  font-size: 11.5px;
  color: var(--nx-text-3);
  font-style: italic;
}
.nx-plan-note {
  margin: 12px 0 0;
  font-size: 11.5px;
  color: var(--nx-text-3);
  text-align: center;
}

/* Inline error placeholder for checkout retry — main.js renders into
   .checkout-error inside the .plan; we only need to ensure the color
   reads on dark. */
.nx-plan .checkout-error {
  margin-top: 10px;
  font-size: 12.5px;
  color: #f87171;
}
.nx-plan .checkout-error a { color: var(--nx-brand-2); text-decoration: underline; }

/* ============================================================
   FAQ
   ============================================================ */
.nx-faq { padding: 80px 0 100px; }
.nx-faq-inner { max-width: 720px; }
.nx-faq h2 {
  font-family: var(--nx-mono);
  font-weight: 400;
  font-size: clamp(22px, 3vw, 28px);
  text-align: center;
  margin: 0 0 30px;
  color: var(--nx-text);
}
.nx-faq-item {
  border-top: 1px solid var(--nx-border);
  padding: 18px 4px;
}
.nx-faq-item:last-of-type { border-bottom: 1px solid var(--nx-border); }
.nx-faq-item summary {
  list-style: none;
  cursor: pointer;
  display: flex; justify-content: space-between; align-items: center;
  gap: 16px;
  font-size: 14.5px;
  color: var(--nx-text);
  font-weight: 500;
  padding: 4px 0;
}
.nx-faq-item summary::-webkit-details-marker { display: none; }
.nx-faq-item summary::after {
  content: "+";
  font-family: var(--nx-mono);
  font-size: 18px;
  color: var(--nx-text-3);
  transition: transform 200ms var(--nx-ease), color 200ms var(--nx-ease);
}
.nx-faq-item[open] summary::after {
  content: "−";
  color: var(--nx-text);
}
.nx-faq-item p {
  margin: 12px 0 4px;
  font-size: 13.5px;
  color: var(--nx-text-2);
  line-height: 1.7;
}
.nx-faq-item p a { color: var(--nx-brand-2); text-decoration: underline; }

/* ============================================================
   Footer
   ============================================================ */
.nx-footer {
  border-top: 1px solid var(--nx-border);
  padding: 28px 0;
}
.nx-footer-inner {
  display: flex; justify-content: space-between; align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}
.nx-footer-brand {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 12.5px;
  color: var(--nx-text-3);
}
.nx-footer-brand img {
  border-radius: 4px;
  background: var(--nx-surface-2);
  box-shadow: 0 0 0 1px var(--nx-border) inset;
}
/* "This page updated YYYY-MM-DD" — small dimmed marker sitting next
   to the © copyright. Freshness signal for crawlers + a tiny human-
   visible hint that the page isn't stale. */
.nx-footer-updated {
  color: var(--nx-text-3);
  font-family: var(--nx-mono);
  font-size: 11.5px;
  white-space: nowrap;
}
.nx-footer-updated time {
  font-variant-numeric: tabular-nums;
}
.nx-footer-links {
  display: inline-flex; align-items: center; gap: 18px;
  font-size: 12.5px;
}
.nx-footer-links a {
  color: var(--nx-text-2);
  transition: color 140ms var(--nx-ease);
}
.nx-footer-links a:hover { color: var(--nx-text); }

/* ============================================================
   Reveal-on-scroll (data-reveal)
   ============================================================ */
[data-reveal] {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 600ms var(--nx-ease), transform 600ms var(--nx-ease);
}
[data-reveal].is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* ============================================================
   Responsive
   ============================================================ */

@media (max-width: 720px) {
  .nx-container { padding: 0 18px; }
  .nx-nav-inner { padding: 0 18px; height: 52px; gap: 8px; }

  /* Mobile nav: hide secondary text links AND the language picker — both
     move into the drawer below. The brand and the primary download CTA
     stay; the hamburger button (.nx-menu-btn) shows. The old rule only
     hid `[href^="#"]` so cross-page links on legal pages (href like
     `index.html#pricing`) leaked through and squeezed the brand into a
     two-line wrap. */
  .nx-nav-link        { display: none; }
  .nx-lang-menu       { display: none; }
  .nx-menu-btn        { display: inline-flex; }
  /* Slim the primary download CTA on mobile so there's room for brand
     + hamburger without the brand wrapping. */
  .nx-nav-right .nx-btn-primary.nx-btn-sm {
    padding: 7px 12px;
    font-size: 12.5px;
  }

  .nx-hero { padding-top: 48px; }
  .nx-headline { line-height: 1.2; }
  .nx-pricing { padding: 60px 0; }
  .nx-price-grid { grid-template-columns: 1fr; max-width: 420px; }
  .nx-faq { padding: 60px 0 80px; }
}

@media (max-width: 480px) {
  .nx-cta { flex-direction: column; align-items: stretch; width: 100%; max-width: 280px; margin-left: auto; margin-right: auto; }
  .nx-btn-lg { width: 100%; }
}

/* ==========================================================================
   Interactive product demo (.nx-demo-section)
   --------------------------------------------------------------------------
   A fake-macOS-window mock of the Nexus Shell app, driven by a timeline in
   home.js. All scenes share the same window chrome; the right-hand pane
   swaps via .nx-pane-active. Cursor is an absolutely-positioned SVG that
   home.js animates with requestAnimationFrame along a bezier path.

   The visual tokens below mirror the real app's dark-mode palette
   (Nexus Shell.app: nexusBackground / nexusSurface / nexusSidebar* etc.)
   so the demo reads as an authentic in-app screenshot rather than a generic
   developer mockup. See the matching SwiftUI tokens in the app at:
   `/Nexus Shell/Nexus Shell/Resources` and `Views/SharedViews.swift`.

   Naming: every visual is .nx-demo-* / .nx-app-* / .nx-pane-* / .nx-scene-*
   so it is fully isolated from the rest of the page.
   ========================================================================== */
.nx-body {
  /* App-native dark palette (matches the macOS app's dark mode tokens) */
  --nxa-bg:           #242429;  /* nexusBackground   */
  --nxa-surface:      #313136;  /* nexusSurface      */
  --nxa-sidebar:      #29292e;  /* nexusSidebarBackground */
  --nxa-select:       #364566;  /* nexusSidebarSelection — blue-tinted */
  --nxa-select-soft:  rgba(76, 140, 217, 0.12);
  --nxa-input:        #38383d;  /* nexusInputBackground */
  --nxa-border:       rgba(255, 255, 255, 0.10);
  --nxa-border-2:     rgba(255, 255, 255, 0.16);
  --nxa-text:         #e1e1e6;  /* nexusTextPrimary */
  --nxa-text-2:       #a6a6b3;  /* nexusTextSecondary */
  --nxa-text-3:       rgba(225, 225, 230, 0.45);
  --nxa-blue:         #4c8cd9;  /* nexusPrimary */
  --nxa-teal:         #1ab38c;  /* nexusAccent */
  --nxa-yellow:       #e1bf73;  /* nexusFavorite */
  --nxa-orange:       #f59e0b;  /* primary action color */
  --nxa-red:          #e85f5f;  /* destructive */
  --nxa-green:        #22c55e;  /* connected/running */
  --nxa-info-blue:    #73a6d9;
  --nxa-info-purple:  #b38cd9;
  --nxa-info-cyan:    #73bfcc;
}
.nx-demo-section {
  padding: 24px 0 96px;
  position: relative;
}

/* Stage = the "MacBook" frame. Aspect-ratio locks the screen, the wrapper
   sits on a soft shadow disc so it floats above the dot grid. */
.nx-stage {
  margin: 32px auto 0;
  max-width: 1080px;
  position: relative;
}
.nx-stage::before {
  /* drop shadow disc */
  content: "";
  position: absolute; left: 8%; right: 8%; bottom: -28px; height: 60px;
  background: radial-gradient(ellipse at center, rgba(56, 122, 224, 0.22) 0%, transparent 70%);
  filter: blur(20px);
  z-index: -1;
}
/* `.nx-frame` was creating a second nested frame around the screen
   (its own border + bg + 6px padding). That made the demo read as
   "thin outer line + faint inner panel + actual macbook" — too many
   layers. We collapse it to a transparent passthrough wrapper and
   let `.nx-screen` carry all the window chrome (border + bg + the
   floating drop shadow). */
.nx-frame {
  background: transparent;
  border: none;
  padding: 0;
  box-shadow: none;
}
.nx-screen {
  position: relative;
  background: var(--nxa-bg);
  border: none;
  border-radius: 12px;
  overflow: hidden;
  aspect-ratio: 16 / 10;
  display: flex; flex-direction: column;
  /* No border line + no inner highlight — the floating sidebar /
     main-pane cards inside provide all the visible chrome. The
     drop shadows below give the "floating macOS window" feel. */
  box-shadow:
    0 30px 80px -20px rgba(0, 0, 0, 0.7),
    0 8px 24px rgba(0, 0, 0, 0.4);
}

/* ---- Title bar ----
   macOS 26 (Tahoe) style: traffic lights + sidebar toggle live INSIDE
   the right pane card at its top-left. Positioned absolutely so the
   pane's page-head (which has padding-left to clear them) flows in
   the same row. Pointer events fall through to the page-head except
   on the buttons themselves. */
.nx-titlebar {
  position: absolute;
  top: 0; left: 0;
  height: 38px;
  display: flex; align-items: center;
  padding: 0 12px;
  gap: 10px;
  z-index: 5;
  pointer-events: none;
}
.nx-titlebar > * { pointer-events: auto; }
.nx-tl-dots { display: flex; gap: 6px; }
.nx-tl-dot {
  width: 11px; height: 11px; border-radius: 50%;
  display: inline-block;
  box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.4);
}
.nx-tl-red    { background: #ff5f57; }
.nx-tl-yellow { background: #febc2e; }
.nx-tl-green  { background: #28c840; }
.nx-tl-toggle {
  width: 22px; height: 22px;
  border-radius: 5px;
  display: flex; align-items: center; justify-content: center;
  color: var(--nxa-text-2);
  background: transparent;
  transition: background 0.15s var(--nx-ease), color 0.15s var(--nx-ease);
}
.nx-tl-toggle:hover { background: rgba(255, 255, 255, 0.06); color: var(--nxa-text); }
/* The title element is kept in the DOM (a few JS paths still reference
   it) but visually hidden — Tahoe puts no app-name in the title bar. */
.nx-tl-title { display: none; }

/* ---- App body: macOS 26 (Tahoe) floating-card layout ----
   The sidebar and the main content pane are two separate rounded
   cards floating on the dark canvas, with a small gap between them
   and breathing space around the window edges. This mirrors the
   real app on Tahoe where NavigationSplitView is rendered as
   pill-shaped surfaces sitting on the desktop's accent gradient. */
.nx-app {
  flex: 1;
  display: flex;
  gap: 10px;
  /* Equal 10px breathing space on all 4 sides between the floating
     cards (sidebar + main pane) and the rounded outer screen edge. */
  padding: 10px;
  min-height: 0;
  background: var(--nxa-bg);
  color: var(--nxa-text);
}

/* Re-assert text color on every heading + paragraph inside the demo so
   the page-level light theme can't override it through style.css.
   Without this, h2/h3/h4/h5 inside the demo render in the website's
   `--nx-text` (dark on light theme) — which makes them invisible on the
   demo's dark canvas. Scoped to .nx-app so it never leaks out. */
.nx-app h2, .nx-app h3, .nx-app h4, .nx-app h5, .nx-app p {
  color: var(--nxa-text);
}

/* Sidebar — Tahoe-style floating pill card. Has its own background,
   1px hairline border, soft shadow, and a small inset padding so the
   tabs breathe inside it. The TOP region (~32px) is reserved for the
   absolutely-positioned `.nx-titlebar` (traffic lights + sidebar
   toggle), so we add extra padding-top so the first menu item starts
   below them. Top half: 5 surface tabs. Bottom half: Membership /
   Settings / Help + a "● 1 connected" status row. */
.nx-app-rail {
  position: relative;
  width: 132px;
  flex: 0 0 132px;
  background: var(--nxa-sidebar);
  border: 1px solid var(--nxa-border);
  border-radius: 10px;
  padding: 36px 8px 8px;
  display: flex; flex-direction: column;
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.45),
    0 8px 24px -6px rgba(0, 0, 0, 0.35);
}
.nx-app-rail-top    { display: flex; flex-direction: column; gap: 2px; }
.nx-app-rail-bottom { margin-top: auto; display: flex; flex-direction: column; gap: 2px; padding-top: 8px; border-top: 1px solid var(--nxa-border); }
.nx-app-tab {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 9px;
  border-radius: 6px;
  color: var(--nxa-text-2);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.01em;
  transition: background 0.15s var(--nx-ease), color 0.15s var(--nx-ease);
  position: relative;
}
.nx-app-tab svg { flex-shrink: 0; opacity: 0.85; }
.nx-app-tab-label { flex: 1; text-align: left; }
.nx-app-tab:hover { color: var(--nxa-text); background: rgba(255, 255, 255, 0.04); }
.nx-app-tab-active {
  color: var(--nxa-text);
  background: var(--nxa-select);
}
.nx-app-tab-active svg { color: var(--nxa-blue); opacity: 1; }
.nx-app-tab-static { cursor: default; }
.nx-app-rail-status {
  display: flex; align-items: center; gap: 6px;
  padding: 8px 9px 4px;
  font-size: 10.5px;
  color: var(--nxa-text-3);
}
.nx-app-rail-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--nxa-green);
  box-shadow: 0 0 5px rgba(34, 197, 94, 0.7);
}

/* Pane container — Tahoe-style floating card. Stacks all 5 panes
   inside; only .nx-pane-active is shown. The card has its own border
   + shadow so it visually separates from both the sidebar (left) and
   the window canvas (right / bottom). */
.nx-app-pane {
  flex: 1;
  position: relative;
  overflow: hidden;
  background: var(--nxa-surface);
  border: 1px solid var(--nxa-border);
  border-radius: 10px;
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.45),
    0 8px 24px -6px rgba(0, 0, 0, 0.35);
}
.nx-pane {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  background: transparent;
  opacity: 0;
  transform: translateY(8px) scale(0.99);
  transition: opacity 0.4s var(--nx-ease), transform 0.4s var(--nx-ease);
  pointer-events: none;
  min-height: 0;
}
.nx-pane-active {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

/* Page header inside a pane. In Tahoe, the toolbar belongs to the
   right pane (not the window), so this row sits AT THE TOP of the
   floating pane card — page title left-aligned, optional toolbar
   buttons right-aligned. No border-bottom — Tahoe doesn't divide
   the toolbar from content. */
.nx-page-head {
  display: flex; align-items: center;
  height: 38px;
  flex: 0 0 38px;
  padding: 0 14px;
  background: transparent;
  font-size: 13px;
  font-weight: 600;
  color: var(--nxa-text);
  gap: 10px;
}
.nx-page-spacer { flex: 1; }
.nx-icon-btn {
  width: 24px; height: 24px;
  border-radius: 5px;
  display: flex; align-items: center; justify-content: center;
  color: var(--nxa-text-2);
  font-size: 13px;
  background: transparent;
  transition: background 0.15s var(--nx-ease), color 0.15s var(--nx-ease);
}
.nx-icon-btn:hover { background: rgba(255,255,255,0.06); color: var(--nxa-text); }
.nx-icon-btn-sm { width: 22px; height: 22px; font-size: 11px; }

/* ============================================================
   Pane 1 · Connections
   ------------------------------------------------------------
   3-column body inside the pane: hosts list (with search +
   FAVORITES + ALL CONNECTIONS groups) | detail (host header,
   action buttons, monitor cards, server specs, history rows).
   ============================================================ */
.nx-conn-body { flex: 1; display: flex; min-height: 0; }
/* Host list column — page-head spans the FULL pane width above this
   column and the detail sibling. No divider line between the columns —
   the host cards / search input + the detail surface cards visually
   separate themselves. */
.nx-conn-list {
  width: 240px; flex: 0 0 240px;
  background: transparent;
  padding: 12px 8px;
  overflow: auto;
  display: flex; flex-direction: column; gap: 14px;
}
/* Host header card wraps the avatar / name / Connected badge / 5
   quick-action buttons so the top of the detail column reads as a
   bordered card consistent with Real-time Monitor / Server Specs /
   Connection History below it. */
.nx-conn-card {
  background: var(--nxa-surface);
  border: 1px solid var(--nxa-border);
  border-radius: 10px;
  padding: 11px 13px;
  display: flex; flex-direction: column; gap: 10px;
}
.nx-conn-card .nx-detail-head { padding-bottom: 0; }
.nx-search {
  display: flex; align-items: center; gap: 7px;
  padding: 6px 10px;
  background: var(--nxa-input);
  border-radius: 6px;
  color: var(--nxa-text-3);
}
.nx-search input {
  flex: 1; min-width: 0;
  background: transparent; border: 0; outline: 0;
  font-family: inherit; font-size: 11px;
  color: var(--nxa-text);
}
.nx-search input::placeholder { color: var(--nxa-text-3); }

.nx-host-group { display: flex; flex-direction: column; gap: 4px; }
.nx-host-group-head {
  display: flex; align-items: center; gap: 6px;
  padding: 0 4px 2px;
  font-family: var(--nx-mono);
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--nxa-text-3);
}
.nx-host-group-head svg:first-child { color: var(--nxa-yellow); }
.nx-host-group ul { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 6px; }

/* Host list rows — each is a discrete card with surface bg, hairline
   border and slight inner shadow (matches the real ConnectionsView
   where every saved connection sits as its own pill). The active
   row highlights with the brand-blue selection bg. */
.nx-host {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 10px 11px;
  border-radius: 8px;
  background: var(--nxa-surface);
  border: 1px solid var(--nxa-border);
  cursor: default;
  transition: background 0.15s var(--nx-ease), border-color 0.15s var(--nx-ease);
}
.nx-host:hover { border-color: var(--nxa-border-2); }
.nx-host-active {
  background: var(--nxa-select);
  border-color: rgba(76, 140, 217, 0.4);
}

.nx-host-avatar {
  width: 28px; height: 28px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 600;
  flex-shrink: 0;
  font-family: var(--nx-sans);
  color: #fff;
}
.nx-host-avatar-blue   { background: linear-gradient(135deg, #4c8cd9, #387ae0); }
.nx-host-avatar-orange { background: linear-gradient(135deg, #f59e0b, #d97757); }
.nx-host-avatar-purple { background: linear-gradient(135deg, #b38cd9, #a855f7); }
.nx-host-avatar-mac    { background: linear-gradient(135deg, #6b6b73, #4a4a52); }
.nx-host-avatar-lg     { width: 36px; height: 36px; font-size: 14px; }
.nx-host-avatar-xs     { width: 16px; height: 16px; font-size: 8.5px; border-radius: 4px; }

.nx-host-meta { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.nx-host-name {
  display: flex; align-items: center; gap: 4px;
  font-size: 11.5px;
  font-weight: 500;
  color: var(--nxa-text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nx-host-fav { color: var(--nxa-yellow); font-size: 10px; }
.nx-host-sub {
  display: flex; align-items: center; gap: 4px;
  font-family: var(--nx-mono);
  font-size: 9.5px;
  color: var(--nxa-text-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nx-host-tags { display: flex; gap: 3px; flex-wrap: wrap; margin-top: 4px; }
.nx-host-tags i {
  font-style: normal;
  font-size: 8.5px;
  font-family: var(--nx-mono);
  padding: 1px 5px;
  background: rgba(255,255,255,0.06);
  color: var(--nxa-text-2);
  border-radius: 3px;
}

.nx-conn-detail {
  flex: 1;
  padding: 12px 14px 14px;
  overflow: auto;
  display: flex; flex-direction: column; gap: 10px;
  min-width: 0;
}
.nx-detail-head {
  display: flex; align-items: center; gap: 11px;
  padding-bottom: 4px;
}
.nx-detail-name { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.nx-detail-name h4 {
  margin: 0; font-size: 17px; font-weight: 600;
  display: flex; align-items: center; gap: 6px;
}
.nx-conn-badge {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--nx-mono);
  font-size: 10px;
  color: var(--nxa-green);
  background: rgba(34,197,94,0.14);
  padding: 4px 10px;
  border-radius: 12px;
  border: 1px solid rgba(34,197,94,0.28);
}
.nx-conn-badge-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--nxa-green); }

.nx-detail-actions { display: flex; gap: 5px; flex-wrap: wrap; }
.nx-act {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 6px 11px;
  border-radius: 6px;
  background: var(--nxa-input);
  color: var(--nxa-text);
  font-size: 11px;
  font-weight: 500;
  font-family: inherit;
  transition: background 0.15s var(--nx-ease);
}
.nx-act:hover { background: rgba(255,255,255,0.10); }
.nx-act-primary { background: var(--nxa-orange); color: #fff; }
.nx-act-primary:hover { background: #d97706; }
.nx-act-danger {
  background: rgba(232,95,95,0.14);
  color: var(--nxa-red);
  padding: 6px 9px;
}
.nx-act-danger:hover { background: rgba(232,95,95,0.22); }

.nx-monitor, .nx-specs, .nx-history {
  background: var(--nxa-surface);
  border: 1px solid var(--nxa-border);
  border-radius: 10px;
  padding: 11px 13px;
}
.nx-monitor-head {
  display: flex; align-items: center; gap: 6px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--nxa-text);
  margin-bottom: 9px;
}
.nx-monitor-head svg { color: var(--nxa-blue); }
.nx-monitor-live {
  margin-left: auto;
  font-family: var(--nx-mono);
  font-size: 9.5px;
  color: var(--nxa-green);
}
.nx-mgrid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px 24px;
}
/* Real-time Monitor stat rows — flat (no nested mini-cards). They
   match the visual style of Server Specs rows: a small colored icon
   + a uppercase mono label + the value. The OUTER monitor card
   (`.nx-monitor`) provides the only card chrome on this section. */
.nx-mcard {
  display: flex; align-items: center; gap: 9px;
  padding: 4px 0;
  background: transparent;
  border: none;
  min-width: 0;
}
.nx-mcard-icn {
  width: 22px; height: 22px;
  border-radius: 5px;
  display: flex; align-items: center; justify-content: center;
  font-size: 11px;
  flex-shrink: 0;
}
.nx-mcard-icn-bars { background: rgba(76,140,217,0.18); color: var(--nxa-blue); }
.nx-mcard-icn-cpu  { background: rgba(34,197,94,0.18); color: var(--nxa-green); }
.nx-mcard-icn-mem  { background: rgba(168,85,247,0.18); color: #c084fc; }
.nx-mcard-icn-net  { background: rgba(115,191,204,0.20); color: var(--nxa-info-cyan); }
.nx-mcard-icn-swap { background: rgba(245,158,11,0.20); color: var(--nxa-orange); }
.nx-mcard-icn-proc { background: rgba(115,166,217,0.20); color: var(--nxa-info-blue); }
.nx-mcard-meta { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.nx-mcard-label {
  font-family: var(--nx-mono);
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--nxa-text-3);
}
.nx-mcard-val {
  font-family: var(--nx-mono);
  font-size: 11.5px;
  color: var(--nxa-text);
  font-weight: 500;
  display: flex; flex-wrap: wrap; gap: 4px; align-items: baseline;
}
.nx-mcard-val small { font-size: 9px; color: var(--nxa-text-3); font-weight: 400; }

.nx-specs-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px 16px;
  font-size: 11px;
}
.nx-specs-grid > div { display: flex; align-items: center; gap: 8px; min-width: 0; }
.nx-specs-icn {
  width: 18px; height: 18px;
  border-radius: 4px;
  background: rgba(168,85,247,0.18);
  color: #c084fc;
  display: flex; align-items: center; justify-content: center;
  font-size: 10px;
  flex-shrink: 0;
}
.nx-specs-label {
  font-family: var(--nx-mono);
  font-size: 9.5px;
  color: var(--nxa-text-3);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  width: 60px;
  flex-shrink: 0;
}
.nx-specs-val {
  font-family: var(--nx-mono);
  font-size: 11px;
  color: var(--nxa-text);
}
/* Disk row stays within the LEFT column of the 2-col Server Specs
   grid so the progress bar doesn't run all the way across the card
   like it used to with grid-column: 1 / -1. */
.nx-specs-disk { grid-column: 1; }
.nx-specs-disk .nx-specs-val { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; }
.nx-specs-fill {
  display: block;
  height: 4px;
  flex: 1;
  background: rgba(255,255,255,0.06);
  border-radius: 2px;
  overflow: hidden;
}
.nx-specs-fill > i {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, var(--nxa-blue), var(--nxa-teal));
  border-radius: 2px;
}
.nx-specs-disk .nx-specs-val small { font-size: 10px; color: var(--nxa-text-3); }
.nx-specs-refresh { margin-left: auto; color: var(--nxa-text-3); cursor: default; }

.nx-history-row {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 0;
  font-family: var(--nx-mono);
  font-size: 10.5px;
  border-bottom: 1px solid var(--nxa-border);
}
.nx-history-row:last-child { border-bottom: 0; }
.nx-history-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--nxa-green); flex-shrink: 0; }
.nx-history-time { flex: 1; color: var(--nxa-text-2); }
.nx-history-status {
  font-size: 9.5px;
  color: var(--nxa-green);
  background: rgba(34,197,94,0.12);
  padding: 2px 8px;
  border-radius: 3px;
}

/* ============================================================
   Pane 2 · Terminal
   ------------------------------------------------------------
   xterm-style body + segmented AI panel (Chat / Saved / History
   tabs) with quick-action row + chat input footer. Terminal pane
   is laid out as a flex row: main column (page-head + tabs +
   body) on the left, AI panel on the right extending to the top
   of the pane card with only a 1px left border separating it.
   ============================================================ */
.nx-pane[data-pane="terminal"],
.nx-pane[data-pane="files"] { flex-direction: row; }

/* Sparkle "AI assistant" toggle button — anchored absolute to the
   top-right of the Terminal pane card so it stays in place whether
   the AI side panel is collapsed or open. Higher z-index than the
   pane content so it's never visually trapped behind the AI panel
   when it slides in from the right. */
.nx-ai-toggle {
  position: absolute;
  top: 8px; right: 14px;
  width: 22px; height: 22px;
  border-radius: 5px;
  display: flex; align-items: center; justify-content: center;
  color: var(--nxa-text-2);
  background: transparent;
  z-index: 6;
  transition: background 0.15s var(--nx-ease), color 0.15s var(--nx-ease);
}
.nx-ai-toggle:hover { background: rgba(255, 255, 255, 0.06); color: var(--nxa-blue); }

.nx-term-main {
  flex: 1;
  display: flex; flex-direction: column;
  min-width: 0;
  overflow: hidden;
}
.nx-term-tabs {
  display: flex; align-items: center;
  height: 32px; flex: 0 0 32px;
  padding: 0 10px;
  border-bottom: 1px solid var(--nxa-border);
  background: var(--nxa-bg);
  gap: 6px;
}
.nx-term-tab {
  display: flex; align-items: center; gap: 6px;
  padding: 0 10px; height: 24px;
  border-radius: 5px;
  font-size: 11px;
  background: var(--nxa-input);
  color: var(--nxa-text);
}
.nx-term-tab-active { box-shadow: inset 0 0 0 1px var(--nxa-blue); }
.nx-term-tab-dot   { width: 5px; height: 5px; border-radius: 50%; background: var(--nxa-green); }
.nx-term-tab-x     { color: var(--nxa-text-3); margin-left: 4px; cursor: default; }
.nx-term-tab-actions { margin-left: auto; display: flex; gap: 3px; }
.nx-term-tabbtn {
  padding: 4px 9px;
  font-size: 10px;
  border-radius: 4px;
  color: var(--nxa-text-3);
  background: transparent;
  font-family: var(--nx-mono);
  transition: background 0.15s var(--nx-ease), color 0.15s var(--nx-ease);
}
.nx-term-tabbtn:hover { background: rgba(255,255,255,0.05); color: var(--nxa-text); }

.nx-term {
  flex: 1; min-width: 0;
  background: linear-gradient(180deg, #1a1d24 0%, #14161c 100%);
  font-family: var(--nx-mono);
  font-size: 11.5px;
  line-height: 1.55;
  padding: 14px 16px;
  overflow: hidden;
  position: relative;
}
.nx-term-body { color: rgba(220, 230, 240, 0.92); white-space: pre-wrap; }
.nx-term-body > div {
  opacity: 0;
  transform: translateY(3px);
  transition: opacity 0.18s var(--nx-ease), transform 0.18s var(--nx-ease);
}
.nx-term-body > div.nx-line-visible { opacity: 1; transform: none; }
.nx-tline-prompt { color: var(--nxa-green); }
.nx-tline-cmd    { color: var(--nxa-text); }
.nx-tline-err    { color: #ff8888; }
.nx-tline-warn   { color: var(--nxa-orange); }
.nx-tline-out    { color: var(--nxa-text-2); }
.nx-tline-hint   { color: var(--nxa-text-3); font-style: italic; }
.nx-tline-banner { color: var(--nxa-info-cyan); white-space: pre; line-height: 1.0; }
.nx-cursor-blink::after {
  content: "▋";
  color: var(--nxa-green);
  margin-left: 1px;
  animation: nxBlink 1s steps(1) infinite;
}
@keyframes nxBlink { 50% { opacity: 0; } }

/* AI side-panel — sibling of the main terminal column, extends from
   top of pane card to bottom. Starts collapsed (width 0), slides in
   when the cursor clicks the sparkle (#nxAIToggle). Only a 1px left
   border separates it from the main area; no own background. */
.nx-ai {
  width: 0; flex: 0 0 0;
  background: transparent;
  border-left: 1px solid var(--nxa-border);
  overflow: hidden;
  display: flex; flex-direction: column;
  transition: flex-basis 0.5s var(--nx-ease), width 0.5s var(--nx-ease);
}
.nx-ai.nx-ai-open { flex: 0 0 300px; width: 300px; }
/* AI tab strip — matches the height of `.nx-page-head` (38px) so the
   active tab's underline aligns with where the page-head's bottom
   would sit on the left main column. Visually the two top toolbars
   read as one continuous row split by the 1px divider between
   `.nx-term-main` and `.nx-ai`. */
.nx-ai-tabs {
  display: flex; align-items: center;
  height: 38px; flex: 0 0 38px;
  border-bottom: 1px solid var(--nxa-border);
  padding: 0 10px;
  gap: 2px;
}
.nx-ai-tab {
  padding: 0 10px;
  height: 38px; line-height: 38px;
  font-size: 11px;
  color: var(--nxa-text-3);
  font-family: inherit;
  border-bottom: 2px solid transparent;
  transition: color 0.15s var(--nx-ease), border-color 0.15s var(--nx-ease);
}
.nx-ai-tab:hover { color: var(--nxa-text-2); }
.nx-ai-tab-active { color: var(--nxa-text); border-bottom-color: var(--nxa-blue); }

.nx-ai-body {
  flex: 1; min-height: 0;
  padding: 10px 12px;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--nxa-text);
  overflow: hidden;
  display: flex; flex-direction: column; gap: 4px;
}
.nx-ai-body > div {
  opacity: 0; transform: translateY(3px);
  transition: opacity 0.2s var(--nx-ease), transform 0.2s var(--nx-ease);
}
.nx-ai-body > div.nx-line-visible { opacity: 1; transform: none; }
.nx-ai-body code {
  background: rgba(255,255,255,0.06);
  padding: 1px 5px;
  border-radius: 3px;
  font-family: var(--nx-mono);
  font-size: 10.5px;
}
.nx-ai-codeblk {
  background: rgba(0,0,0,0.35);
  border: 1px solid var(--nxa-border);
  border-radius: 6px;
  padding: 0;
  margin-top: 4px;
  font-family: var(--nx-mono);
  font-size: 10.5px;
  overflow: hidden;
}
.nx-ai-codehead {
  display: flex; align-items: center;
  padding: 5px 10px;
  font-size: 9.5px;
  background: rgba(255,255,255,0.04);
  color: var(--nxa-text-3);
}
.nx-ai-codehead button {
  margin-left: auto;
  font-size: 9.5px;
  color: var(--nxa-text-2);
  font-family: inherit;
}
.nx-ai-codeblk pre {
  margin: 0; padding: 8px 10px;
  color: rgba(225,225,230,0.92);
  white-space: pre;
  overflow-x: auto;
}

/* Quick-action row hovers above the AI input with no divider — the
   real app puts these three actions on a single line that floats
   over the input area. We pack tight (no flex-wrap) and trim the
   per-button padding/font so all three fit at typical AI panel
   widths even when text labels are present. */
.nx-ai-quickrow {
  display: flex; gap: 4px;
  padding: 6px 8px 4px;
  flex-wrap: nowrap;
  white-space: nowrap;
}
.nx-ai-quick {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px 7px;
  font-size: 9.5px;
  border-radius: 4px;
  background: transparent;
  color: var(--nxa-text-2);
  font-family: inherit;
  flex: 0 1 auto;
  min-width: 0;
}
.nx-ai-quick:hover { background: rgba(255,255,255,0.08); color: var(--nxa-text); }

.nx-ai-input {
  display: flex; gap: 6px;
  padding: 4px 8px 8px;
}
.nx-ai-input input {
  flex: 1; min-width: 0;
  padding: 7px 11px;
  background: var(--nxa-input);
  border: 0;
  outline: 0;
  border-radius: 6px;
  font-family: inherit;
  font-size: 11px;
  color: var(--nxa-text);
}
.nx-ai-input input::placeholder { color: var(--nxa-text-3); }
.nx-ai-send {
  width: 28px; height: 28px;
  border-radius: 6px;
  background: var(--nxa-blue);
  color: #fff;
  font-size: 13px;
  display: flex; align-items: center; justify-content: center;
}

/* ============================================================
   Pane 3 · Files
   ------------------------------------------------------------
   Finder-style table (Name / Size / Permissions / Modified)
   + side transfer panel.
   ============================================================ */
.nx-page-head-files { gap: 4px; }
.nx-host-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px;
  border-radius: 5px;
  background: var(--nxa-input);
  color: var(--nxa-text);
  font-size: 11px;
  font-weight: 500;
  font-family: inherit;
}
.nx-host-pill:hover { background: rgba(255,255,255,0.10); }
.nx-host-pill-chev { color: var(--nxa-text-3); font-size: 9px; }
.nx-host-pill-ghost {
  background: transparent;
  border: 1px solid var(--nxa-border);
}
.nx-files-title { font-weight: 600; padding: 0 6px; }
/* File-table column — sibling of the transfer panel. Page-head sits at
   its top; the path / column headers / rows live inside .nx-files-body-inner. */
.nx-files-table {
  flex: 1;
  display: flex; flex-direction: column;
  overflow: hidden;
  min-width: 0;
}
.nx-files-body-inner {
  flex: 1;
  display: flex; flex-direction: column;
  overflow: hidden;
}
.nx-files-path {
  padding: 6px 16px;
  font-family: var(--nx-mono);
  font-size: 11px;
  color: var(--nxa-text-2);
  border-bottom: 1px solid var(--nxa-border);
  background: rgba(255,255,255,0.02);
}
.nx-files-cols {
  display: grid;
  grid-template-columns: 1fr 80px 130px 120px;
  gap: 12px;
  padding: 6px 16px;
  font-family: var(--nx-mono);
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--nxa-text-3);
  border-bottom: 1px solid var(--nxa-border);
}
.nx-files-cols i { font-style: normal; font-size: 9px; margin-left: 3px; }
.nx-files-rows { list-style: none; margin: 0; padding: 0; flex: 1; overflow: auto; }
.nx-frow {
  display: grid;
  grid-template-columns: 1fr 80px 130px 120px;
  gap: 12px;
  align-items: center;
  padding: 5px 16px;
  font-size: 11px;
  color: var(--nxa-text);
  transition: background 0.12s var(--nx-ease);
  cursor: default;
}
.nx-frow:hover { background: rgba(255,255,255,0.04); }
.nx-frow-selected { background: var(--nxa-select-soft); box-shadow: inset 0 0 0 1px rgba(76,140,217,0.4); }
.nx-frow > span:first-child {
  display: flex; align-items: center; gap: 8px;
  font-family: inherit;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nx-frow-icn { font-size: 13px; flex-shrink: 0; opacity: 0.85; }
.nx-frow-folder { color: var(--nxa-info-blue); }
.nx-frow-mono { font-family: var(--nx-mono); color: var(--nxa-text-2); font-size: 10.5px; }
.nx-frow-time { font-family: var(--nx-mono); color: var(--nxa-text-3); font-size: 10.5px; }

/* Transfer panel — sibling of the file-table column, extends from
   top of pane card to bottom. Only a 1px left border separates it
   from the main area; no own background. */
.nx-tx {
  width: 220px; flex: 0 0 220px;
  border-left: 1px solid var(--nxa-border);
  background: transparent;
  display: flex; flex-direction: column;
  overflow: hidden;
}
.nx-tx-list {
  padding: 10px;
  display: flex; flex-direction: column; gap: 6px;
  flex: 1;
  overflow: auto;
}
.nx-tx-empty {
  color: var(--nxa-text-3);
  font-style: italic;
  font-size: 10.5px;
  text-align: center;
  padding: 20px 10px;
}
.nx-tx-row {
  background: var(--nxa-surface);
  border: 1px solid var(--nxa-border);
  border-radius: 7px;
  padding: 8px 10px;
  display: flex; flex-direction: column; gap: 5px;
  font-size: 10.5px;
  animation: nxTaskIn 0.3s var(--nx-ease);
}
@keyframes nxTaskIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.nx-tx-row-head {
  display: flex; align-items: center; gap: 6px;
}
.nx-tx-row-icn {
  width: 18px; height: 18px;
  border-radius: 4px;
  background: var(--nxa-blue);
  color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 9.5px;
  flex-shrink: 0;
}
.nx-tx-row-icn-done { background: var(--nxa-green); }
.nx-tx-row-name {
  font-family: var(--nx-mono);
  flex: 1;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nx-tx-row-x { color: var(--nxa-text-3); font-size: 11px; cursor: default; }
.nx-tx-row-bar { height: 3px; background: rgba(255,255,255,0.06); border-radius: 2px; overflow: hidden; }
.nx-tx-row-fill {
  display: block; height: 100%; width: 0%;
  background: linear-gradient(90deg, var(--nxa-blue), #5fb6f5);
  border-radius: 2px; transition: width 0.4s linear;
}
.nx-tx-row[data-done="1"] .nx-tx-row-fill { background: var(--nxa-green); }
.nx-tx-row-foot {
  display: flex; justify-content: space-between;
  font-family: var(--nx-mono);
  font-size: 9.5px;
  color: var(--nxa-text-3);
}
.nx-tx-row-success { color: var(--nxa-green); }

/* ============================================================
   Pane 4 · Containers
   ------------------------------------------------------------
   Server-grouped list: each server has a header row (host info
   + count), then a grid of detailed container cards.
   ============================================================ */
.nx-cont-body {
  flex: 1;
  padding: 14px;
  overflow: auto;
  display: flex; flex-direction: column; gap: 12px;
}
.nx-cont-srv-head {
  display: flex; align-items: center; gap: 8px;
  padding: 0 4px 4px;
  font-size: 11.5px;
}
.nx-cont-srv-icn { color: var(--nxa-text-3); font-size: 11px; }
.nx-cont-srv-name { font-weight: 600; color: var(--nxa-text); }
.nx-cont-srv-host { font-family: var(--nx-mono); color: var(--nxa-text-3); font-size: 10.5px; }
.nx-cont-srv-spacer { flex: 1; }
.nx-cont-srv-count { font-size: 10.5px; color: var(--nxa-text-3); font-family: var(--nx-mono); }

.nx-cont-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}
.nx-cont {
  background: var(--nxa-surface);
  border: 1px solid var(--nxa-border);
  border-radius: 10px;
  padding: 11px 13px;
  display: flex; flex-direction: column; gap: 7px;
  transition: border-color 0.3s var(--nx-ease), box-shadow 0.3s var(--nx-ease);
}
.nx-cont-running { box-shadow: inset 0 0 0 1px rgba(34,197,94,0.16); }
.nx-cont-stopped { opacity: 0.85; }

.nx-cont-head { display: flex; align-items: center; gap: 8px; }
.nx-cont-icn {
  width: 26px; height: 26px;
  border-radius: 6px;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.nx-cont-icn-green  { background: rgba(34,197,94,0.18);  color: var(--nxa-green); }
.nx-cont-icn-purple { background: rgba(168,85,247,0.18); color: #c084fc; }

.nx-cont-name { font-size: 12.5px; font-weight: 600; color: var(--nxa-text); }
.nx-cont-status {
  font-size: 9.5px;
  font-family: var(--nx-mono);
  padding: 2px 7px;
  border-radius: 3px;
}
.nx-cont-status-running  { background: rgba(34,197,94,0.16);  color: var(--nxa-green); }
.nx-cont-status-stopped  { background: rgba(166,166,179,0.16); color: var(--nxa-text-2); }
.nx-cont-status-starting { background: rgba(245,158,11,0.16); color: var(--nxa-orange); }
.nx-cont-status-exited   { background: rgba(232,95,95,0.16);  color: var(--nxa-red); }
.nx-cont-spacer { flex: 1; }
.nx-cont-uptime { font-size: 10px; color: var(--nxa-text-3); font-family: var(--nx-mono); }

.nx-cont-img {
  font-family: var(--nx-mono);
  font-size: 10.5px;
  color: var(--nxa-text-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nx-cont-ports {
  font-family: var(--nx-mono);
  font-size: 10.5px;
  color: var(--nxa-text-3);
}
.nx-cont-stats-row {
  display: flex; gap: 14px; flex-wrap: wrap;
  font-family: var(--nx-mono);
  font-size: 10.5px;
  color: var(--nxa-text-2);
}
.nx-cont-stats-row span { display: inline-flex; align-items: center; gap: 5px; }
.nx-cont-stats-row i { font-style: normal; color: var(--nxa-text-3); font-size: 10px; }

.nx-cont-foot {
  display: flex; align-items: center; gap: 8px;
  padding-top: 6px;
  border-top: 1px solid var(--nxa-border);
}
.nx-cont-id {
  font-family: var(--nx-mono);
  font-size: 9.5px;
  color: var(--nxa-text-3);
  flex: 1;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nx-cont-actions { display: flex; gap: 2px; }
/* Card action buttons (Containers + Keys cards) — share the same
   visual component as the page-head toolbar (.nx-icon-btn-sm):
   transparent bg by default, hover lifts to a subtle white tint,
   uniform 22×22 size with a centered 11×11 SVG icon. The stateful
   variants (start = green tint, stop = orange tint, danger = red
   text) layer on top of the base. */
.nx-cact {
  width: 22px; height: 22px;
  border-radius: 5px;
  background: transparent;
  color: var(--nxa-text-2);
  display: flex; align-items: center; justify-content: center;
  font-family: inherit;
  transition: background 0.15s var(--nx-ease), color 0.15s var(--nx-ease);
}
.nx-cact:hover { background: rgba(255,255,255,0.08); color: var(--nxa-text); }
.nx-cact-on     { color: var(--nxa-green); }
.nx-cact-on:hover     { background: rgba(34,197,94,0.18); }
.nx-cact-stop   { color: var(--nxa-orange); }
.nx-cact-stop:hover   { background: rgba(245,158,11,0.18); }
.nx-cact-danger { color: var(--nxa-red); }
.nx-cact-danger:hover { background: rgba(232,95,95,0.18); color: var(--nxa-red); }

/* ============================================================
   Pane 5 · Keys
   ------------------------------------------------------------
   Adaptive 3-col card grid; each card has the key avatar, name,
   algo badge, created/comment lines, fingerprint with copy, and
   an action footer (edit / copy / delete).
   ============================================================ */
.nx-keys-grid {
  flex: 1;
  padding: 14px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 10px;
  align-content: start;
  overflow: auto;
  position: relative;
}
.nx-key-card {
  background: var(--nxa-surface);
  border: 1px solid var(--nxa-border);
  border-radius: 10px;
  padding: 12px 14px;
  display: flex; flex-direction: column; gap: 5px;
  min-height: 150px;
  animation: nxKeyIn 0.4s var(--nx-ease);
}
@keyframes nxKeyIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.nx-key-card header {
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 4px;
}
.nx-key-icn {
  width: 26px; height: 26px;
  border-radius: 6px;
  display: flex; align-items: center; justify-content: center;
  font-size: 12px;
  flex-shrink: 0;
}
.nx-key-icn-blue   { background: rgba(76,140,217,0.18); color: var(--nxa-blue); }
.nx-key-icn-purple { background: rgba(168,85,247,0.18); color: #c084fc; }
.nx-key-name {
  flex: 1;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--nxa-text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nx-key-algo {
  font-family: var(--nx-mono);
  font-size: 9.5px;
  padding: 2px 7px;
  border-radius: 3px;
  background: rgba(76,140,217,0.18);
  color: var(--nxa-blue);
}
.nx-key-line {
  display: flex; align-items: center; gap: 7px;
  font-size: 11px;
  color: var(--nxa-text-2);
}
.nx-key-line-icn {
  color: var(--nxa-text-3);
  font-size: 10px;
  width: 14px; text-align: center;
}
/* Fingerprint row — flat mono text, no extra background tint
   (matches the real KeysView card where the fingerprint sits
   directly on the card surface). */
.nx-key-fp {
  display: flex; align-items: center; gap: 6px;
  padding: 4px 0 0;
  margin-top: 2px;
  background: transparent;
  border-radius: 0;
  font-family: var(--nx-mono);
  font-size: 10.5px;
  color: var(--nxa-text-2);
}
.nx-key-fp > span {
  flex: 1; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nx-key-fp button {
  width: 22px; height: 22px;
  border-radius: 4px;
  color: var(--nxa-text-3);
  font-family: inherit;
  background: transparent;
}
.nx-key-fp button:hover { background: rgba(255,255,255,0.08); color: var(--nxa-text); }
.nx-key-fp-scramble { color: var(--nxa-orange); }
.nx-key-card footer {
  display: flex; align-items: center; gap: 4px;
  margin-top: auto;
  padding-top: 6px;
  border-top: 1px solid var(--nxa-border);
}
.nx-key-tag {
  font-family: var(--nx-mono);
  font-size: 9.5px;
  color: var(--nxa-text-3);
  display: flex; align-items: center; gap: 4px;
}
.nx-key-foot-spacer { flex: 1; }

/* Modal: appears centered over the keys grid when "Generate" is clicked */
.nx-key-modal {
  position: absolute; inset: 0;
  background: rgba(20, 20, 24, 0.7);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center;
  opacity: 0; pointer-events: none;
  transition: opacity 0.25s var(--nx-ease);
  z-index: 5;
}
.nx-key-modal.nx-key-modal-open { opacity: 1; pointer-events: auto; }
.nx-key-modal-card {
  background: var(--nxa-surface);
  border: 1px solid var(--nxa-border-2);
  border-radius: 10px;
  padding: 18px 20px;
  width: 320px;
  box-shadow: 0 20px 60px rgba(0,0,0,0.6);
  transform: scale(0.96);
  transition: transform 0.25s var(--nx-ease);
}
.nx-key-modal-open .nx-key-modal-card { transform: scale(1); }
.nx-key-modal-card h5 { margin: 0 0 14px; font-size: 13px; font-weight: 600; color: var(--nxa-text); }
.nx-key-opts { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
.nx-key-opt {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px;
  border: 1px solid var(--nxa-border);
  border-radius: 6px;
  font-size: 12px;
}
.nx-key-opt input { margin: 0; accent-color: var(--nxa-blue); }
.nx-key-opt small { color: var(--nxa-text-3); margin-left: auto; font-size: 10px; }
.nx-key-opt-active { border-color: var(--nxa-blue); box-shadow: inset 0 0 0 1px var(--nxa-blue); }
.nx-key-modal-actions { display: flex; gap: 8px; justify-content: flex-end; }
.nx-key-btn-cancel, .nx-key-btn-gen {
  font-size: 11px; padding: 7px 14px;
  border-radius: 6px;
  font-family: inherit;
}
.nx-key-btn-cancel { background: transparent; border: 1px solid var(--nxa-border-2); color: var(--nxa-text-2); }
.nx-key-btn-gen    { background: var(--nxa-blue); color: white; }

/* ============================================================
   Cursor — absolutely positioned in .nx-screen, JS animates left/top.
   ============================================================ */
.nx-cursor {
  position: absolute;
  left: 50%; top: 50%;
  width: 20px; height: 20px;
  pointer-events: none;
  z-index: 50;
  opacity: 0;
  transition: opacity 0.25s var(--nx-ease);
  filter: drop-shadow(0 2px 4px rgba(0,0,0,0.4));
  transform-origin: 0 0;
}
.nx-cursor svg { display: block; width: 100%; height: 100%; }
/* Click ping */
.nx-cursor::after {
  content: "";
  position: absolute;
  left: 8px; top: 8px;
  width: 8px; height: 8px;
  border-radius: 50%;
  border: 2px solid var(--nx-brand-2);
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.4);
}
.nx-cursor.nx-cursor-clicking::after {
  animation: nxCursorPing 0.45s var(--nx-ease) forwards;
}
@keyframes nxCursorPing {
  0%   { opacity: 0.9; transform: translate(-50%, -50%) scale(0.4); }
  100% { opacity: 0;   transform: translate(-50%, -50%) scale(2.2); }
}

/* ============================================================
   Scene navigator (5 pills + caption)
   ============================================================ */
/* Tabs sit ABOVE the demo stage; caption text sits below. The two
   blocks are direct siblings of .nx-stage inside .nx-container, so
   each owns its own vertical margin to space against the stage. */

/* Underline-tab strip — distinct from vibeisland's pill tabs.
   Five tabs sit on a thin baseline; the active tab's underline fills
   left-to-right over the scene duration; completed tabs keep a dim
   trail so users can see how far through the demo they are. */
.nx-scene-pills {
  display: flex; gap: 0;
  justify-content: center;
  position: relative;
  padding: 0;
  /* sit above the stage with breathing room */
  margin: 8px auto 22px;
  max-width: 100%;
  overflow-x: auto;     /* avoid horizontal page scroll on tight viewports */
  scrollbar-width: none;
}
.nx-scene-pills::-webkit-scrollbar { display: none; }
.nx-scene-pill {
  position: relative;
  display: inline-flex; align-items: baseline; gap: 8px;
  padding: 10px 22px 12px;
  border: none;
  background: transparent;
  font-family: var(--nx-mono);
  font-size: 12px;
  color: var(--nx-text-2);
  cursor: pointer;
  transition: color 0.2s var(--nx-ease);
  white-space: nowrap;
}
.nx-scene-pill:hover { color: var(--nx-text); }
.nx-scene-pill-num   {
  font-size: 10.5px;
  color: var(--nx-text-3);
  letter-spacing: 0.06em;
  transition: color 0.2s var(--nx-ease);
}
.nx-scene-pill-label { letter-spacing: 0.02em; }

/* The underline indicator — sits at the bottom edge of each tab and
   extends left→right over --nx-act-duration. Replaces the pill bg fill. */
.nx-scene-pill-fill {
  position: absolute; left: 0; bottom: -1px;
  height: 2px;
  width: 0%;
  background: linear-gradient(90deg, var(--nx-brand) 0%, var(--nx-brand-2) 100%);
  border-radius: 2px 2px 0 0;
  box-shadow: 0 0 12px rgba(94, 170, 255, 0.45);
  pointer-events: none;
}

/* Done = scene already played → keep a thin dim trail so users see
   their progress through the demo at a glance. */
.nx-scene-pill.nx-tl-done {
  color: var(--nx-text-2);
}
.nx-scene-pill.nx-tl-done .nx-scene-pill-num { color: rgba(94, 170, 255, 0.55); }
.nx-scene-pill.nx-tl-done .nx-scene-pill-fill {
  width: 100%;
  height: 1px;
  background: rgba(94, 170, 255, 0.32);
  box-shadow: none;
}

/* Active = currently playing → bright text + underline fills over time. */
.nx-scene-pill.nx-tl-active {
  color: var(--nx-text);
}
.nx-scene-pill.nx-tl-active .nx-scene-pill-num { color: var(--nx-brand-2); }
.nx-scene-pill.nx-tl-active .nx-scene-pill-fill {
  animation: nxPillFill var(--nx-act-duration, 5500ms) linear forwards;
}
.nx-scene-pill.nx-tl-paused .nx-scene-pill-fill { animation-play-state: paused; }
@keyframes nxPillFill {
  from { width: 0%; }
  to   { width: 100%; }
}

.nx-scene-text {
  text-align: center;
  max-width: 640px;
  padding: 0 16px;
  /* spacing under the stage now that this block is no longer inside
     the old .nx-scene-bar flex column */
  margin: 22px auto 0;
}
.nx-scene-title {
  font-family: var(--nx-mono);
  font-size: 18px;
  font-weight: 500;
  margin: 0 0 6px;
  color: var(--nx-text);
}
.nx-scene-desc {
  margin: 0;
  color: var(--nx-text-2);
  font-size: 13.5px;
  line-height: 1.55;
}

/* ============================================================
   Mobile — collapse rail to a horizontal strip, hide AI
   side-panel / shrink the dashboard grid.
   ============================================================ */
@media (max-width: 720px) {
  .nx-app-rail        { width: 52px; flex-basis: 52px; padding: 8px 6px; }
  .nx-app-tab-label   { display: none; }
  .nx-app-rail-status { display: none; }
  .nx-conn-list       { width: 180px; flex-basis: 180px; }
  .nx-mgrid           { grid-template-columns: 1fr 1fr; }
  .nx-specs-grid      { grid-template-columns: 1fr; }
  .nx-cont-grid       { grid-template-columns: 1fr; }
  .nx-keys-grid       { grid-template-columns: 1fr; }
  .nx-tx              { width: 160px; flex-basis: 160px; }
  .nx-files-cols, .nx-frow { grid-template-columns: 1fr 60px 100px; }
  .nx-files-cols > :nth-child(3), .nx-frow > :nth-child(4) { display: none; }
  .nx-ai.nx-ai-open   { flex-basis: 220px; width: 220px; }
  .nx-scene-pill      { padding: 8px 12px 10px; font-size: 11px; gap: 6px; }
  .nx-scene-pill-num  { font-size: 9.5px; }
}

/* ============================================================
   Reduced motion — kill every transition + the scramble effect
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .nx-body *,
  .nx-body *::before,
  .nx-body *::after {
    transition: none !important;
    animation: none !important;
  }
  [data-reveal] { opacity: 1; transform: none; }
}

/* ============================================================
   Cross-page support
   --------------------------------------------------------------
   Non-home pages (account, contact, privacy, terms, pay/success)
   keep their existing markup (which uses .nav / .card / .field
   from style.css) but get re-skinned to match the new design when
   their <body> carries class="nx-body". These overrides are
   intentionally narrow — they only swap the visual surface, so
   the page-specific layout in style.css continues to work.
   ============================================================ */

/* Translucent fade nav matching .nx-nav (terminal text + scan beam show through). */
.nx-body .nav {
  position: sticky;
  top: 0;
  z-index: 50;
  border-bottom: none;
  /* CRITICAL: reset style.css's legacy `.nav` background (--c-overlay,
     78% opaque) and its own backdrop-filter, otherwise non-home pages
     end up with a much more opaque nav than the homepage. The visual
     fade lives entirely on ::before. */
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.nx-body .nav::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  backdrop-filter: saturate(180%) blur(18px);
  -webkit-backdrop-filter: saturate(180%) blur(18px);
  background: linear-gradient(180deg, rgba(10, 10, 13, 0.32) 0%, rgba(10, 10, 13, 0.10) 60%, rgba(10, 10, 13, 0) 100%);
  mask-image: linear-gradient(180deg, #000 0%, #000 70%, transparent 100%);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 70%, transparent 100%);
}
.nx-body[data-theme="light"] .nav::before {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.55) 0%, rgba(255, 255, 255, 0.20) 60%, rgba(255, 255, 255, 0) 100%);
}

/* Frosted-glass card so legacy .card content stays legible on the textured bg. */
.nx-body .card {
  background: rgba(15, 15, 20, 0.72);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border-color: rgba(255, 255, 255, 0.10);
  color: rgba(255, 255, 255, 0.92);
}
.nx-body .card h1,
.nx-body .card h2,
.nx-body .card h3 { color: rgba(255, 255, 255, 0.92); }

/* Form inputs sit inside frosted cards — give them a near-opaque dark base
   so text is unambiguously readable. */
.nx-body .field input,
.nx-body .field select {
  background: rgba(0, 0, 0, 0.40);
  border-color: rgba(255, 255, 255, 0.14);
  color: rgba(255, 255, 255, 0.92);
}
.nx-body .field input::placeholder { color: rgba(255, 255, 255, 0.32); }
.nx-body .field input:focus {
  border-color: rgba(94, 170, 255, 0.55);
  box-shadow: 0 0 0 3px rgba(94, 170, 255, 0.18);
}

/* Footer should be transparent — bg orbs / terminal text continue under it. */
.nx-body .footer { background: transparent; border-top-color: rgba(255, 255, 255, 0.06); }

/* Legacy "page hero" used by privacy/terms/contact — keep transparent. */
.nx-body main,
.nx-body .account-page,
.nx-body .legal-page,
.nx-body .contact-page { background: transparent; }

/* Type scale alignment — bring legacy account-page typography into the
   homepage's scale: page title sits between hero (60px) and section h2
   (28-30px), card h2 maps to homepage card h3 (18px), row text + desc
   land at the homepage's 14.5px secondary copy size. */
.nx-body .account-h1 {
  font-family: var(--nx-mono);   /* matches homepage .nx-section-head h2 */
  font-size: 28px;
  font-weight: 400;
  letter-spacing: -0.012em;
}
.nx-body .account-card h2 {
  font-family: var(--nx-mono);   /* matches homepage .nx-plan-head h3 */
  font-size: 17px;
  font-weight: 500;
}
.nx-body .account-card .account-desc {
  font-size: 13.5px;
}
.nx-body .account-row {
  font-size: 14.5px;
}
.nx-body .account-card .field > span {
  font-size: 12.5px;
}
.nx-body .account-card .field input {
  font-size: 14.5px;
}

/* Brand "Nexus Shell" in the legacy nav must match homepage's mono brand
   wordmark — switching from a 16px/700 sans to mono on every page change
   was the loudest cue that fonts were different. */
.nx-body .brand {
  font-family: var(--nx-mono);
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: 0.02em;
}

/* Legal pages (privacy / terms): page title + section headings match the
   homepage's mono headline scale. Body p/li already use sans (correct). */
.nx-body .legal-page h1 {
  font-family: var(--nx-mono);
  font-weight: 400;
  letter-spacing: -0.012em;
}
.nx-body .legal-page h2 {
  font-family: var(--nx-mono);
  font-weight: 500;
}
.nx-body .legal-page h3 {
  font-family: var(--nx-mono);
  font-weight: 500;
}

/* Contact page page title — same treatment. */
.nx-body .contact-page h1 {
  font-family: var(--nx-mono);
  font-weight: 400;
  letter-spacing: -0.012em;
}

/* Structural alignment with the homepage nav. The legacy .nav-inner is
   60px tall, gap 24; the homepage .nx-nav-inner is 56px tall. Match
   homepage so switching pages doesn't shift the nav baseline. */

/* Enforce logo render size at the CSS layer so the rendered size stays
   in sync even if any future inline width/height regresses. */
.nx-body .brand img,
.nx-body .nav .brand img {
  width: 22px;
  height: 22px;
}

/* Legacy .btn-ghost (used on pay/success.html "返回首页") was reading
   --c-surface from style.css, which is #ffffff (pure white) under the
   light token block. If a user still had localStorage['nexus-theme']
   set to 'light' from the pre-dark-only era, the button rendered as a
   white box with white text — invisible. Pin it to --nx-* tokens here
   so it stays correctly dark regardless of any stale data-theme.  */

/* ============================================================
   "New version available" banner — homepage hero
   ------------------------------------------------------------
   A slim pill anchored above the headline. JS in home.js fills
   the version + show/hide based on /releases/latest.json + a
   dismissal flag keyed by version in localStorage.
   ============================================================ */
.nx-update-banner {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  max-width: 92%;
  padding: 4px 6px 4px 4px;
  margin: 0 auto 22px;
  border-radius: 999px;
  font-family: var(--nx-mono);
  font-size: 12.5px;
  color: var(--nx-text);
  background:
    linear-gradient(180deg, rgba(56, 122, 224, 0.18) 0%, rgba(56, 122, 224, 0.06) 100%),
    rgba(15, 15, 20, 0.65);
  border: 1px solid rgba(94, 170, 255, 0.30);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  box-shadow:
    0 8px 22px -12px rgba(56, 122, 224, 0.55),
    0 0 0 1px rgba(255, 255, 255, 0.04) inset;
  transition: border-color 200ms var(--nx-ease),
              box-shadow 200ms var(--nx-ease),
              transform 200ms var(--nx-ease);
  animation: nx-banner-in 360ms var(--nx-ease) both;
}
/* Lift the whole banner subtly when the user hovers the link area. This
   replaces the loud inner CTA — the affordance is now the pill itself. */
.nx-update-banner:has(.nx-update-link:hover) {
  border-color: rgba(94, 170, 255, 0.55);
  box-shadow:
    0 12px 28px -12px rgba(56, 122, 224, 0.70),
    0 0 0 1px rgba(255, 255, 255, 0.06) inset;
  transform: translateY(-1px);
}
@keyframes nx-banner-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.nx-update-banner[hidden] { display: none !important; }

/* The link wraps pill + version + arrow as ONE click target. Scoping
   under `.nx-update-banner` beats the global `.nx-body a` color-inherit
   reset (which would otherwise wash out the link color). */
.nx-update-banner .nx-update-link {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 3px 10px 3px 4px;
  border-radius: 999px;
  color: var(--nx-text);
  transition: color 180ms var(--nx-ease);
}
.nx-update-banner .nx-update-link:hover { color: #ffffff; }

.nx-update-pill {
  display: inline-flex; align-items: center;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #0a0a0d;
  background: linear-gradient(135deg, #5eaaff, #387ae0);
  flex: none;
  transition: box-shadow 200ms var(--nx-ease);
}
.nx-update-banner .nx-update-link:hover .nx-update-pill {
  box-shadow: 0 0 0 3px rgba(94, 170, 255, 0.22);
}
.nx-update-text {
  /* gap > 0 is what creates the space between "v1.4.2" and "已发布" —
     inline-flex strips whitespace between children, so HTML-source spaces
     don't render and we have to spell the gap out here. */
  display: inline-flex; align-items: baseline; gap: 6px;
  color: inherit;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.nx-update-ver {
  color: var(--nx-brand-2);
  font-weight: 500;
  transition: color 180ms var(--nx-ease);
}
.nx-update-banner .nx-update-link:hover .nx-update-ver { color: #ffffff; }

/* Quiet right-arrow as a navigation cue: low opacity at rest, full
   opacity + 2px slide on hover. Doesn't compete with the headline. */
.nx-update-arrow {
  color: var(--nx-brand-2);
  opacity: 0.45;
  flex: none;
  transition: opacity 200ms var(--nx-ease), transform 200ms var(--nx-ease);
}
.nx-update-banner .nx-update-link:hover .nx-update-arrow {
  opacity: 1;
  transform: translateX(2px);
}

.nx-update-banner .nx-update-close {
  width: 24px; height: 24px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--nx-text-2);
  border-radius: 999px;
  flex: none;
  transition: background 160ms, color 160ms;
}
.nx-update-banner .nx-update-close:hover { background: rgba(255, 255, 255, 0.08); color: var(--nx-text); }

.nx-body[data-theme="light"] .nx-update-banner {
  background:
    linear-gradient(180deg, rgba(56, 122, 224, 0.10) 0%, rgba(56, 122, 224, 0.02) 100%),
    rgba(255, 255, 255, 0.85);
  border-color: rgba(56, 122, 224, 0.35);
}
.nx-body[data-theme="light"] .nx-update-banner:has(.nx-update-link:hover) {
  border-color: var(--nx-brand);
}
.nx-body[data-theme="light"] .nx-update-banner .nx-update-link { color: var(--nx-text); }
.nx-body[data-theme="light"] .nx-update-banner .nx-update-link:hover,
.nx-body[data-theme="light"] .nx-update-banner .nx-update-link:hover .nx-update-ver { color: var(--nx-brand); }
.nx-body[data-theme="light"] .nx-update-arrow { color: var(--nx-brand); }

@media (max-width: 560px) {
  .nx-update-banner { padding: 3px 5px 3px 3px; font-size: 12px; }
  .nx-update-banner .nx-update-link { padding: 3px 8px 3px 3px; gap: 8px; }
  .nx-update-pill { font-size: 10px; padding: 2px 8px; }
}

/* ============================================================
   Changelog page (/releases.html)
   ------------------------------------------------------------
   Vertical timeline of release cards. The list is rendered by
   assets/js/changelog.js. Each card carries:
     * tag + date header
     * "Latest" / "Pre-release" badges (left of header)
     * markdown-rendered release notes (zh/en/ja/ko swap)
     * download row + "view on GitHub" link
   ============================================================ */
.changelog-page { padding: 56px 0 100px; position: relative; }
.changelog-inner {
  max-width: 760px;
  margin: 0 auto;
  padding: 0 24px;
}
.changelog-head {
  text-align: center;
  margin: 0 auto 40px;
}
.changelog-head h1 {
  font-family: var(--nx-mono);
  font-weight: 400;
  font-size: clamp(28px, 4.6vw, 44px);
  letter-spacing: -0.018em;
  margin: 0 0 12px;
  color: var(--nx-text);
}
.changelog-list {
  display: flex; flex-direction: column;
  gap: 22px;
}
.changelog-card {
  position: relative;
  padding: 26px 28px 24px;
  border-radius: var(--nx-radius-lg);
  background: rgba(15, 15, 20, 0.72);
  border: 1px solid var(--nx-border);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  /* Animate when JS reveals the list. */
  opacity: 0;
  transform: translateY(8px);
  animation: nx-card-in 360ms var(--nx-ease) forwards;
}
@keyframes nx-card-in {
  to { opacity: 1; transform: translateY(0); }
}
.nx-body[data-theme="light"] .changelog-card {
  background: rgba(255, 255, 255, 0.88);
}
/* Highlight the very first (latest) release card with a brand tint. */
.changelog-card.is-latest {
  background:
    linear-gradient(180deg, rgba(56, 122, 224, 0.14) 0%, rgba(56, 122, 224, 0.02) 60%),
    rgba(15, 15, 20, 0.78);
  border-color: rgba(56, 122, 224, 0.28);
  box-shadow:
    0 0 0 1px rgba(56, 122, 224, 0.12) inset,
    0 18px 40px -22px rgba(56, 122, 224, 0.35);
}
.nx-body[data-theme="light"] .changelog-card.is-latest {
  background:
    linear-gradient(180deg, rgba(56, 122, 224, 0.10) 0%, rgba(56, 122, 224, 0.02) 60%),
    rgba(255, 255, 255, 0.94);
}

.changelog-card-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 12px;
  margin: 0 0 18px;
}
.changelog-card-tag {
  font-family: var(--nx-mono);
  font-size: 22px;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--nx-text);
  margin: 0;
}
.changelog-card-date {
  font-family: var(--nx-mono);
  font-size: 12px;
  color: var(--nx-text-3);
  letter-spacing: 0.02em;
}
.changelog-badge {
  display: inline-flex; align-items: center;
  padding: 3px 9px;
  font-family: var(--nx-mono);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-radius: 999px;
}
.changelog-badge-latest {
  color: #0a0a0d;
  background: linear-gradient(135deg, #5eaaff, #387ae0);
}
.changelog-badge-pre {
  color: var(--nx-warn);
  background: rgba(245, 158, 11, 0.12);
  border: 1px solid rgba(245, 158, 11, 0.30);
}

/* Markdown body */
.changelog-body { color: var(--nx-text); font-size: 14.5px; line-height: 1.7; }
.changelog-body > :first-child { margin-top: 0; }
.changelog-body > :last-child  { margin-bottom: 0; }
.changelog-body h3 {
  font-family: var(--nx-mono);
  font-weight: 500;
  font-size: 14px;
  letter-spacing: 0.02em;
  color: var(--nx-text);
  margin: 22px 0 10px;
  padding-top: 18px;
  border-top: 1px dashed var(--nx-border);
}
.changelog-body h3:first-child { padding-top: 0; border-top: 0; margin-top: 0; }
.changelog-body p { margin: 10px 0; color: var(--nx-text); }
.changelog-body ul {
  margin: 6px 0 12px;
  padding-left: 0;
  list-style: none;
}
.changelog-body li {
  position: relative;
  padding-left: 18px;
  margin: 6px 0;
  color: var(--nx-text);
}
.changelog-body li::before {
  content: "›";
  position: absolute;
  left: 2px; top: 0;
  color: var(--nx-brand-2);
  font-family: var(--nx-mono);
  font-weight: 500;
}
.changelog-body strong { color: var(--nx-text); font-weight: 600; }
.changelog-body code {
  font-family: var(--nx-mono);
  font-size: 12.5px;
  padding: 1px 5px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.06);
  color: var(--nx-brand-2);
}
.nx-body[data-theme="light"] .changelog-body code {
  background: rgba(15, 15, 20, 0.06);
}
.changelog-body a { color: var(--nx-brand-2); text-decoration: underline; text-decoration-color: rgba(94, 170, 255, 0.35); }
.changelog-body a:hover { text-decoration-color: var(--nx-brand-2); }
.changelog-body hr {
  border: 0;
  height: 1px;
  margin: 18px 0;
  background: var(--nx-border);
}

/* Loading / error / empty states */
.changelog-status {
  text-align: center;
  padding: 60px 24px;
  color: var(--nx-text-2);
  font-family: var(--nx-mono);
  font-size: 13px;
}
.changelog-status .changelog-spinner {
  display: inline-block;
  width: 18px; height: 18px;
  margin-right: 10px;
  vertical-align: -4px;
  border: 2px solid rgba(255, 255, 255, 0.15);
  border-top-color: var(--nx-brand-2);
  border-radius: 50%;
  animation: nx-spin 900ms linear infinite;
}
.changelog-error {
  max-width: 480px;
  margin: 60px auto;
  padding: 26px 24px;
  text-align: center;
  border-radius: var(--nx-radius);
  border: 1px solid var(--nx-border);
  background: rgba(15, 15, 20, 0.72);
}
.nx-body[data-theme="light"] .changelog-error { background: rgba(255, 255, 255, 0.85); }
.changelog-error h2 {
  font-family: var(--nx-mono);
  font-weight: 500;
  font-size: 16px;
  margin: 0 0 10px;
  color: var(--nx-text);
}
.changelog-error p {
  margin: 0 0 18px;
  font-size: 13.5px;
  color: var(--nx-text-2);
  line-height: 1.6;
}
.changelog-error-actions {
  display: inline-flex; gap: 10px; flex-wrap: wrap; justify-content: center;
}

@media (max-width: 560px) {
  .changelog-page { padding: 36px 0 64px; }
  .changelog-card { padding: 22px 18px 20px; }
  .changelog-card-tag { font-size: 19px; }
  .changelog-body { font-size: 14px; }
}
