/* Non-critical styles for new.nexius.pro SPA — extracted from inline <style>
 * in index.html. Theme variables, body baseline, and the pre-React skeleton-
 * shell stay inline so the initial HTML paint stays styled even if this file
 * is still in flight. Everything here is either UI polish, mobile utilities,
 * or layout/markdown styles that only render inside the React tree (i.e. after
 * the JS bundle has parsed and React has mounted, by which time the stylesheet
 * has long since loaded). Cache-bust via ?v=<hash> in index.html. */

::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-thumb { background: var(--scroll-thumb); }
::-webkit-scrollbar-track { background: transparent; }
html, body, #root { max-width: 100%; overflow-x: hidden; }
/* Reserve scrollbar space даже когда контента мало (workspace overflow:hidden,
   marketplace overflow:auto) — иначе при переключении табов viewport ширина
   прыгает на ~15px и логотип/центрированный nav в TopBar сдвигается. */
html { scrollbar-gutter: stable; }

/* Plain background — раньше тут была dotted-grid SVG-картинка (32x32 paddle
   из circle r=1), но юзер попросил убрать точки. Оставлен только
   background-color, !important чтобы перебить inline body shorthand из
   index.html. --canvas — отдельный токен страницы: в light это мягкий
   серый, чтобы белые поверхности (--bg) читались как карточки; в тёмных
   темах --canvas == --bg, разницы нет. */
body {
  background-color: var(--canvas) !important;
  background-image: none !important;
}
/* Strip the opaque var(--bg) backgrounds React views set on their roots
   so the body bg bleeds through (legacy от dotted-grid; теперь просто
   keep transparent чтобы цвет body был один источник истины). */
[data-screen-label] { background: transparent !important; }
input::placeholder { color: var(--placeholder); }
button:focus-visible, input:focus-visible, select:focus-visible { outline: 1px solid var(--fg); outline-offset: 1px; }
select { color-scheme: light; }
[data-theme="dark"] select,
[data-theme="dark-blue"] select,
[data-theme="dark-gray"] select { color-scheme: dark; }
.wireframe-stamp {
  position: fixed; top: 12px; right: 16px; z-index: 100;
  font-family: "JetBrains Mono", monospace; font-size: 10px; color: var(--fg-watermark);
  letter-spacing: 0.18em; text-transform: uppercase; pointer-events: none;
}

/* ── Right-side drawer animations ── */
@keyframes drawer-slide-in { from { transform: translateX(100%); } to { transform: translateX(0); } }
@keyframes drawer-fade-in  { from { opacity: 0; } to { opacity: 1; } }
@keyframes drawer-slide-in-left { from { transform: translateX(-100%); } to { transform: translateX(0); } }

/* ── Worker Setup sidebar cards (create.jsx WorkerExtrasAside) ──
   Hover lift + border accent that's hard to express purely as inline
   styles on a static React element. Pairs with `createStyles.setupCard`
   and the `.nx-setup-card` className applied to each section. The card
   body never gets the hover treatment — only the header is interactive,
   so we restrict the cursor pointer there. */
.nx-setup-card { will-change: transform; }
.nx-setup-card:hover {
  border-color: var(--border-mid, var(--border));
  box-shadow: 0 6px 24px -18px rgba(0, 0, 0, 0.45);
}
[data-theme="dark"] .nx-setup-card:hover,
[data-theme="dark-blue"] .nx-setup-card:hover,
[data-theme="dark-gray"] .nx-setup-card:hover {
  background-color: color-mix(in oklab, var(--bg) 92%, white 8%);
  box-shadow: 0 6px 24px -16px rgba(0, 0, 0, 0.7);
}

/* ── Build-pipeline indicators (BuildingPreview in /create) ── */
@keyframes pulse  { 0%, 100% { opacity: 0.4; } 50% { opacity: 1; } }
@keyframes shimmer { 0% { transform: translateX(-100%); } 100% { transform: translateX(280%); } }
/* progressShimmer — overlay sweep on BuildingProgress when the LLM
   takes longer than estimated. Pure visual cue that the request is
   still alive while pct is pinned at 99%. */
@keyframes progressShimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
/* indeterminateSlide — travelling slice for indeterminate progress
   (BuildingProgress when the backend doesn't stream % data). The slice
   is 35% wide; it enters from -35% and exits at 100%, so the bar is
   never fully empty mid-animation. */
@keyframes indeterminateSlide { 0% { left: -35%; } 100% { left: 100%; } }

/* ── Workspace Office ─────────────────────────────────────────────
   The office is a real viewport-filling product surface, not a card preview.
   Mobile keeps the actual floor visible inside a horizontal scroller so the
   pixel workers do not collapse into unreadable dots. */
.nx-office-panel {
  flex: 1;
  min-width: 0;
  height: calc(100vh - 56px);
  padding: 4px 6px 6px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
}
.nx-office-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
  gap: 16px;
  flex-wrap: wrap;
}
.nx-office-intro {
  max-width: 760px;
}
.nx-office-intro h1 {
  font-size: 20px !important;
  margin: 0 !important;
}
.nx-office-intro > div:first-child {
  margin-bottom: 2px !important;
}
.nx-office-intro p {
  display: none;
}
.nx-office-stats {
  margin-bottom: 6px !important;
  flex: 0 0 auto;
}
.nx-office-stats > div {
  padding: 6px 12px !important;
}
.nx-office-stats > div > div:first-child {
  font-size: 8.5px !important;
}
.nx-office-stats > div > div:last-child {
  font-size: 16px !important;
}
.nx-office-floor {
  border: none;
  background: var(--bg-soft);
  position: relative;
  overflow: hidden;
  flex: 1 1 auto;
  min-height: 0;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.02);
}
.nx-office-floor svg {
  width: 100%;
  height: 100%;
  display: block;
  shape-rendering: crispEdges;
}
.nx-office-mobile-list {
  display: none !important;
}

/* ── Hero (HomeHero) decorative animations ── */
@keyframes nx-glow-breathe {
  0%, 100% { opacity: 0.55; transform: translate(-50%, -50%) scale(1); }
  50%      { opacity: 0.85; transform: translate(-50%, -50%) scale(1.06); }
}

/* Gradient-shimmer green accent — replaces the flat #00FF88 fill on the
   "AI workers" headline word. Slow horizontal sweep gives the hero motion
   without distracting from copy; reduced-motion users get a static fill. */
@keyframes nx-hero-shimmer {
  0%   { background-position:   0% 50%; }
  50%  { background-position: 100% 50%; }
  100% { background-position:   0% 50%; }
}
.nx-hero-accent {
  background-image: linear-gradient(110deg,
    #00FF88 0%, #6CFFB6 28%, #C8FFE0 50%, #6CFFB6 72%, #00FF88 100%);
  background-size: 240% 100%;
  background-position: 0% 50%;
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  animation: nx-hero-shimmer 9s cubic-bezier(0.45, 0.05, 0.55, 0.95) infinite;
  filter: drop-shadow(0 0 28px rgba(0, 255, 136, 0.18));
}
@media (prefers-reduced-motion: reduce) {
  .nx-hero-accent { animation: none; background-position: 0% 50%; }
}

/* Status pulse dot for the "live now" pill — radial ring expands then fades. */
@keyframes nx-hero-pulse-dot {
  0%   { box-shadow: 0 0 0 0   rgba(0, 255, 136, 0.55); }
  70%  { box-shadow: 0 0 0 9px rgba(0, 255, 136, 0);    }
  100% { box-shadow: 0 0 0 0   rgba(0, 255, 136, 0);    }
}
.nx-hero-pulse-dot {
  display: inline-block;
  width: 6px; height: 6px; border-radius: 50%;
  background: #00FF88;
  animation: nx-hero-pulse-dot 2.4s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}

/* Live-status pill that sits between title and lede. Tight mono, faint green
   border, glassy fill — anchors the hero with a real "this is alive" signal. */
.nx-hero-live-pill {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 6px 14px 6px 12px;
  margin-bottom: 22px;
  border: 1px solid rgba(0, 255, 136, 0.22);
  border-radius: 999px;
  background: rgba(0, 255, 136, 0.04);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fg-strong);
  font-weight: 400;
}
.nx-hero-live-pill .nx-hero-live-sep {
  color: var(--fg-faintest);
  margin: 0 2px;
}

/* Faint dotted grid behind the hero — only the top-left quadrant via mask,
   so it suggests a coordinate plane without competing with the headline. */
.nx-hero-grid {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image:
    radial-gradient(circle at 1px 1px, rgba(255, 255, 255, 0.045) 1px, transparent 0);
  background-size: 28px 28px;
  background-position: -1px -1px;
  -webkit-mask-image:
    radial-gradient(ellipse 60% 60% at 22% 38%, rgba(0, 0, 0, 0.9), transparent 75%);
          mask-image:
    radial-gradient(ellipse 60% 60% at 22% 38%, rgba(0, 0, 0, 0.9), transparent 75%);
  opacity: 0.55;
}

/* Hairline rule that extends from the eyebrow text toward the viewport edge —
   adds editorial discipline without weight. Drawn inside .nx-home-eyebrow as
   a flex item; flex:1 lets it fill remaining space up to a reasonable cap. */
.nx-hero-eyebrow-rule {
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, var(--border-mid), transparent);
  max-width: 140px;
  margin-left: 4px;
  opacity: 0.7;
}
@media (max-width: 768px) {
  .nx-hero-eyebrow-rule { display: none; }
}
/* Composer input wrapper — invisible on desktop (textarea + actions row
   stack vertically inside .nx-composer as before). Flipped to flex-row on
   mobile fullscreen — see body.nx-chat-fullscreen rules below. */
.nx-composer-input { display: contents; }

.nx-hero-glow { animation: nx-glow-breathe 7s ease-in-out infinite; }

@keyframes nx-fade-up {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
.nx-hero-fade { animation: nx-fade-up 0.7s cubic-bezier(0.22, 1, 0.36, 1) both; }

/* ── Sprout (HomeHeroSprout) ── fully grown, gentle sway + glow + sparks.
   Each leaf wraps in a sway group; the rotation pivots around the leaf
   base (50% 100% of fill-box). Left and right swing in counter-phase. */
@keyframes nx-sprout-sway-l {
  0%, 100% { transform: rotate(-1.4deg); }
  50%      { transform: rotate(2.2deg); }
}
@keyframes nx-sprout-sway-r {
  0%, 100% { transform: rotate(1.4deg); }
  50%      { transform: rotate(-2.2deg); }
}
.nx-sprout-sway-l, .nx-sprout-sway-r {
  transform-box: fill-box;
  transform-origin: 50% 100%;
}
.nx-sprout-sway-l { animation: nx-sprout-sway-l 5.5s ease-in-out infinite; }
.nx-sprout-sway-r { animation: nx-sprout-sway-r 5.5s ease-in-out 0.25s infinite; }

@keyframes nx-sprout-glow-pulse {
  0%, 100% { opacity: 0.65; }
  50%      { opacity: 1; }
}
.nx-sprout-glow { animation: nx-sprout-glow-pulse 5s ease-in-out infinite; }

/* tiny rising particles around the sprout */
@keyframes nx-sprout-spark {
  0%   { opacity: 0; transform: translateY(0); }
  20%  { opacity: 0.9; }
  100% { opacity: 0; transform: translateY(-60px); }
}
.nx-sprout-spark { animation: nx-sprout-spark 4.5s ease-out infinite; }

/* Soil horizon — gentle breath: scaleX in from center + opacity pulse
   so the ground line feels alive without competing with the leaf sway. */
@keyframes nx-sprout-ground-pulse {
  0%, 100% { opacity: 0.7; transform: scaleX(0.92); }
  50%      { opacity: 1;   transform: scaleX(1); }
}
.nx-sprout-ground {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: nx-sprout-ground-pulse 5.5s ease-in-out 0.4s infinite;
}

/* ── Worker card grid — desktop default = 4 columns, fall back as
     viewport narrows so cards never get crushed below ~240 px. ────── */
@media (max-width: 1280px) { .mb-card-grid { grid-template-columns: repeat(3, minmax(0, 1fr)) !important; } }
@media (max-width: 960px)  { .mb-card-grid { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; } }
@media (max-width: 640px)  { .mb-card-grid { grid-template-columns: 1fr !important; } }

/* ── TopBar responsive — laptops & narrow windows ────────────────────
   The desktop TopBar packs logo + 5-item nav (incl. the long "Create AI
   worker") + a centered decorative tagline + theme toggle + account
   cluster onto one flex row. The full row only fits cleanly at ≥1680px
   (verified with screenshots); below that the flex:1 tagline
   (whiteSpace:nowrap, can't shrink past its text) squeezes the row —
   nav labels wrap to 2–3 lines and the account cluster clips off the
   right edge (the "overlap / unreadable" reported on 13" screens).
   Fix: drop the decorative tagline ≤1680px (purely a slogan,
   pointer-events:none, already hidden ≤768px via mb-hide);
   margin-left:auto on the theme cluster replaces it as the spacer that
   right-anchors theme + account — same trick the ≤768px block uses on
   .nx-topbar-right. Queries start at min-width:769px so they never
   collide with the existing ≤768px mobile rules. */
.nx-topbar-nav button { white-space: nowrap; }
@media (min-width: 769px) and (max-width: 1680px) {
  .nx-topbar-tagline { display: none !important; }
  .nx-topbar-row .nx-topbar-right { margin-left: auto !important; }
}
/* Narrow windows 769–1100px: tighten the row so logo + nav + theme +
   account never overflow before the burger menu takes over at ≤768px. */
@media (min-width: 769px) and (max-width: 1100px) {
  .nx-topbar-row { gap: 12px !important; }
  .nx-topbar-nav { margin-left: 4px !important; }
  .nx-topbar-nav button { padding: 6px 8px !important; letter-spacing: 0.05em !important; }
  .nx-topbar-logo-text { font-size: 23px !important; }
}

/* ── Mobile responsive (≤768px) ──────────────────────────────── */
/* Default desktop state for mobile-only utility classes.
   `mb-only` is for inline-level chips (display:inline-flex on mobile).
   `mb-flex` is for block-level panels (display:flex on mobile).
   Both default to `display:none !important` so they beat any inline
   `style={{ display: "flex" }}` left in JSX from earlier code. */
.mb-only { display: none !important; }
.mb-flex { display: none !important; }
.mb-backdrop { display: none; }
.nx-title-tail { display: inline; }
.nx-mobile-copy,
.nx-mobile-flow { display: none; }

/* ── Fleet status filter — minimal underline tabs ─────────────────────
   ACTIVE / STOPPED / ARCHIVED. No container, no elevation, no halo:
   three text buttons in a row sharing a thin baseline; the active one
   carries a 1px underline + brighter text. Dot fills from --dot-color
   inlined on the button. */
.nx-fleet-status-tabs {
  display: inline-flex;
  align-items: stretch;
  gap: 24px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--border-soft);
}
.nx-fleet-status-tab {
  position: relative;
  border: none;
  background: transparent;
  color: var(--fg-faint);
  padding: 8px 2px;
  margin-bottom: -1px;
  border-bottom: 1px solid transparent;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-weight: 500;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  white-space: nowrap;
  transition: color 140ms ease, border-color 140ms ease;
}
.nx-fleet-status-tab:hover { color: var(--fg-muted); }
.nx-fleet-status-tab:focus-visible { outline: none; color: var(--fg-strong); }
.nx-fleet-status-tab.is-active {
  color: var(--fg-strong);
  border-bottom-color: var(--fg-strong);
}
.nx-fleet-status-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--dot-color, var(--fg-faint));
  flex-shrink: 0;
}
.nx-fleet-status-count {
  font-size: 10.5px;
  color: var(--fg-faint);
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-variant-numeric: tabular-nums;
  transition: color 140ms ease;
}
.nx-fleet-status-tab.is-active .nx-fleet-status-count { color: var(--fg-muted); }

@media (max-width: 768px) {
  .mb-pad { padding-left: 16px !important; padding-right: 16px !important; }
  .mb-pad-y { padding-top: 14px !important; padding-bottom: 14px !important; }
  .mb-pad-tight { padding: 16px !important; }
  .mb-hide { display: none !important; }
  .mb-only { display: inline-flex !important; }
  /* My fleet — status filter on mobile renders as a single compact <select>
     instead of a 3-segment row. The segmented control is hidden via .mb-hide
     in JSX. We strip the OS chrome and paint our own decorations:
       ::before — 5px colored dot reflecting current status (CSS var
                  --status-color set inline on the wrap by JSX),
       ::after  — chevron on the right.
     Native picker on iOS/Android still pops on tap; only the trigger is
     restyled to match the rest of the workspace chrome. */
  .nx-fleet-status-select-wrap {
    position: relative;
    width: fit-content;
    max-width: 100%;
  }
  .nx-fleet-status-select-wrap::before {
    content: "";
    position: absolute;
    left: 10px;
    top: 50%;
    transform: translateY(-50%);
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--status-color, var(--fg-faint));
    pointer-events: none;
  }
  .nx-fleet-status-select-wrap::after {
    content: "▾";
    position: absolute;
    right: 9px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 9px;
    color: var(--fg-faint);
    pointer-events: none;
  }
  .nx-fleet-status-select {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    background: var(--bg);
    color: var(--fg-strong);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    padding: 5px 22px 5px 22px;
    font-size: 10.5px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
    font-weight: 500;
    cursor: pointer;
    outline: none;
    line-height: 1.3;
    min-width: 0;
  }
  .nx-fleet-status-select:focus-visible {
    border-color: var(--fg-strong);
  }
  /* FleetPanel (unauthed wireframe) stats strip: 4 stats in one row at
     ~95px each clips "$324.18" and the success-rate %. Collapse to 2x2
     and rewire the border recipe so the cross-seam (between row 1 and
     row 2) reads with a top-border on items 3-4 instead of stale
     right-borders on items 1 and 3. */
  .nx-fleet-stats {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }
  .nx-fleet-stats > div {
    border-right: none !important;
  }
  .nx-fleet-stats > div:nth-child(2n+1) {
    border-right: 1px solid var(--border-soft) !important;
  }
  .nx-fleet-stats > div:nth-child(n+3) {
    border-top: 1px solid var(--border-soft);
  }
  /* Create — team review stats footer (4 metrics: Orchestrator / Workers /
     Avg runtime / Tools). Same 4-col → 2x2 collapse + border rewire as
     FleetPanel above. */
  .nx-team-review-stats {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }
  .nx-team-review-stats > div {
    border-right: none !important;
  }
  .nx-team-review-stats > div:nth-child(2n+1) {
    border-right: 1px solid var(--border-soft) !important;
  }
  .nx-team-review-stats > div:nth-child(n+3) {
    border-top: 1px solid var(--border-soft);
  }
  /* Create — team review member cards. Inline grid is N × minmax(180px),
     so 3+ workers overflow a 480px viewport. On mobile drop to a single
     column and unclamp maxWidth (inline `memberCount * 220` becomes 660+
     for a 3-worker team and stretches the visible bus arrow off-screen). */
  .nx-team-review-members {
    grid-template-columns: 1fr !important;
    max-width: 100% !important;
  }
  /* Balance — transaction row. Desktop is a flat 5-col table (date | type |
     desc | amount | balance) plus an .mb-hide header row above. On mobile
     we rewire the same 5 cells into a 2-row receipt:
       row 1:  [date]  [type-pill]                       [amount]
       row 2:  [description spans 2 cols]                [balance]
     so the operator reads "what / how much" first, then the trailing
     balance + description below. No JSX duplication — cells are addressed
     by data-tx-cell attributes set in BalanceTxRow. */
  .nx-bal-tx-row {
    grid-template-columns: auto 1fr auto !important;
    grid-template-areas:
      "date   type    amount"
      "desc   desc    balance" !important;
    gap: 6px 12px !important;
    padding: 12px 16px !important;
    align-items: center !important;
  }
  .nx-bal-tx-row > [data-tx-cell="date"]    { grid-area: date; }
  .nx-bal-tx-row > [data-tx-cell="type"]    { grid-area: type; justify-self: start; }
  .nx-bal-tx-row > [data-tx-cell="desc"]    { grid-area: desc; font-size: 12px; color: var(--fg-soft) !important; }
  .nx-bal-tx-row > [data-tx-cell="amount"]  { grid-area: amount; }
  .nx-bal-tx-row > [data-tx-cell="balance"] { grid-area: balance; font-size: 10.5px; }
  /* Detail (worker page) — team orchestration SVG diagram. Desktop fixes
     minWidth: 700 + overflowX: auto on the parent, so on a 480px viewport
     the user has to swipe to see the hub. Drop the floor on mobile and
     let SVG re-scale naturally (it has explicit viewBox, so geometry stays
     proportional, just visually smaller). */
  .nx-team-svg {
    min-width: 0 !important;
  }
  /* Detail (team page) — TeamCostBreakdown row. Desktop is "label | bar
     200px | price 80px", which pushes past 360px viewport on mobile.
     Collapse to "label | price" and hide the decorative bar — numeric
     price is the actionable data. */
  .nx-cost-breakdown-row {
    grid-template-columns: 1fr auto !important;
    gap: 10px !important;
  }
  .nx-cost-breakdown-row > *:nth-child(2) {
    display: none !important;
  }
  /* Worker card actions row. Corner icons (Edit + Delete) live INLINE in
     this row now (anchored right via margin-left:auto in JSX), not pinned
     absolutely to the corner — so the row is a single naturally-wrapping
     flex strip and there's no right-padding reservation to keep in sync.
     Mobile-only refinements:
       - tighter horizontal padding + gap,
       - chip padding/tracking shrunk so the primary chips fit on phones,
       - secondary meta chips (v{N} / published / from-marketplace) hidden
         since Edit listing + Unpublish already signal published state. */
  .nx-fleet-card-actions {
    padding-left: 14px !important;
    padding-right: 14px !important;
    padding-top: 12px !important;
    gap: 5px !important;
    row-gap: 6px !important;
  }
  .nx-fleet-card-actions > button {
    padding-left: 6px !important;
    padding-right: 6px !important;
    margin-left: 0 !important;
    letter-spacing: 0.1em !important;
  }
  .nx-fleet-card-actions > span {
    margin-left: 0 !important;
  }
  .nx-fleet-card-actions .nx-fleet-meta-chip {
    display: none !important;
  }
  /* TopBar mobile layout: collapse the 32px desktop gap and right-anchor
     the theme+avatar cluster so [hamburger][logo] hug the left while
     [theme][avatar] hug the right. Also normalize the hamburger button
     to a 32×32 square so it matches the 32px avatar circle visually. */
  .nx-topbar-row { gap: 10px !important; }
  .nx-topbar-row .nx-topbar-spacer { display: none !important; }
  .nx-topbar-row .nx-topbar-right { margin-left: auto !important; }
  .nx-topbar-row .nx-topbar-burger {
    width: 32px !important; height: 32px !important;
    padding: 0 !important; display: inline-flex !important;
    align-items: center !important; justify-content: center !important;
    margin-right: 0 !important;
  }
  .mb-flex { display: flex !important; }
  .nx-office-panel {
    height: auto !important;
    min-height: calc(100svh - 75px) !important;
    padding: 14px !important;
    overflow: auto !important;
  }
  .nx-office-header {
    align-items: flex-start !important;
    margin-bottom: 12px !important;
  }
  .nx-office-intro h1 {
    font-size: 26px !important;
  }
  .nx-office-intro p {
    font-size: 12px !important;
    line-height: 1.45 !important;
  }
  .nx-office-actions {
    width: 100% !important;
    justify-content: flex-start !important;
    overflow-x: auto !important;
    padding-bottom: 2px !important;
  }
  .nx-office-stats {
    display: none !important;
  }
  .nx-office-floor {
    flex: 0 0 auto !important;
    min-height: 520px !important;
    height: 520px !important;
    overflow: auto !important;
    -webkit-overflow-scrolling: touch;
  }
  .nx-office-floor svg {
    width: 980px !important;
    min-width: 980px !important;
    height: 520px !important;
  }
  .nx-office-mobile-list {
    display: none !important;
  }
  .mb-stack { grid-template-columns: 1fr !important; gap: 16px !important; }
  .mb-col-stack { flex-direction: column !important; height: auto !important; }
  .mb-fullw { width: 100% !important; flex: 1 0 auto !important; }
  .mb-no-side-border { border-right: none !important; border-left: none !important; border-bottom: 1px solid var(--border) !important; }
  /* Two-pane mobile: only one of list/chat is visible at a time. */
  .mob-pane-list, .mob-pane-chat { width: 100% !important; flex: 1 !important; min-width: 0 !important; border-right: none !important; }
  .mob-pane-list.mob-hidden, .mob-pane-chat.mob-hidden { display: none !important; }
  .mb-table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .mb-table-scroll > * { min-width: 720px; }
  /* Cap для sidebar'ов которые на мобиле стекаются над main контентом
     (Tables / Notes list). Иначе list забирает весь viewport и main не виден. */
  .mb-aside-cap {
    max-height: 45vh !important;
    flex: 0 0 auto !important;
    width: 100% !important;
  }
  /* Auto-высота для shell'ов которые на десктопе калиброваны как
     100vh - 75px, но на мобиле после mb-col-stack дают 2x viewport. */
  .mb-shell-auto-h {
    height: auto !important;
    min-height: calc(100vh - 75px) !important;
  }
  .mb-sidebar { display: none !important; }
  .mb-sidebar.open {
    display: flex !important;
    position: fixed; top: 0; left: 0; bottom: 0;
    width: min(86vw, 320px);
    z-index: 50;
    animation: drawer-slide-in-left 200ms ease-out;
    box-shadow: 4px 0 24px rgba(0,0,0,0.3);
  }
  .mb-backdrop {
    display: block;
    position: fixed; inset: 0; z-index: 49;
    background: rgba(0,0,0,0.45);
    animation: drawer-fade-in 200ms ease-out;
  }
  /* ── Mobile UX pass: extra utilities (Phase 6 of plan) ──
     New names so we don't change the meaning of existing classes. */

  /* Generic N-col grid collapsers.
     mb-stack collapses to 1fr unconditionally; -3/-4/-6 are softer:
     N-col → 2-col on tablets and 1-col on phones. Use them on grids
     where collapsing fully to one column wastes too much horizontal
     space at 600-768px. */
  .mb-stack-3 { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; gap: 12px !important; }
  .mb-stack-4 { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; gap: 12px !important; }
  .mb-stack-6 { grid-template-columns: repeat(3, minmax(0, 1fr)) !important; gap: 10px !important; }

  /* Era / money-flow arrows turn from horizontal → vertical so the
     sequence still reads top-to-bottom on stacked layouts. Apply on
     the arrow glyph itself, not its parent grid. display: inline-block
     so rotate works on naturally-inline <span> arrows; align: center
     so the arrow sits in the middle of the stacked flow. */
  .mb-arrow-rotate {
    transform: rotate(90deg) !important;
    display: inline-block !important;
    text-align: center !important;
  }

  /* Settings-style form rows: "label  |  control" → label on top, control below. */
  .mb-form-row-stack { grid-template-columns: 1fr !important; gap: 6px !important; }

  /* ── Settings shell mobile polish ────────────────────────────────
     Targets the entire Settings surface ([data-screen-label="04 Settings"]
     wrapping <SettingsView>). Inline styles on settingsStyles.page / .h1 /
     .sectionHeader can't be tweaked per-viewport in JSX without plumbing
     className everywhere, so we override here with !important. */
  /* Lock the outer settings surface to viewport width so no inner element
     can push horizontal scroll. Inline `flex: 1; minWidth: 0` on the main
     column already allows shrinking, but some catalog rows with long URLs
     occasionally measure wider — clip them at the shell. */
  [data-screen-label="04 Settings"] {
    max-width: 100vw !important;
    overflow-x: hidden !important;
  }
  /* The main flex:1 column has inline `overflowY: auto`. Per CSS spec,
     when one axis is non-visible, the other auto-promotes from visible
     to auto — so the column ends up scrollable horizontally too. Force
     overflow-x: hidden so only vertical scroll remains. */
  [data-screen-label="04 Settings"] > div:not(.mb-sidebar) {
    overflow-x: hidden !important;
    max-width: 100% !important;
    min-width: 0 !important;
  }
  [data-screen-label="04 Settings"] header > h1 {
    font-size: 24px !important;
    letter-spacing: 0 !important;
  }
  /* Catch settingsStyles.sectionHeader (display:flex; justifyContent:
     space-between; border-bottom). On mobile allow wrap so [title] +
     [Catalog/Custom toggle / +Add key / etc] stack instead of forcing the
     right cluster off-screen. */
  [data-screen-label="04 Settings"] div[style*="justify-content: space-between"][style*="border-bottom"] {
    flex-wrap: wrap !important;
    row-gap: 8px !important;
  }
  /* External / Proxy keys catalog expanded row — desktop pads 52px on the
     left to align the inline form under the 28px icon + 10px gap. On
     mobile that wastes ~14% of a 360px viewport; align flush with the row. */
  [data-screen-label="04 Settings"] div[style*="padding: 12px 14px 14px 52px"] {
    padding: 12px 14px 14px 16px !important;
  }
  /* Outer page container — drop the 32px/48px vertical padding so section
     headers sit closer to the breadcrumb. Horizontal padding already
     handled by .mb-pad (16/16). */
  [data-screen-label="04 Settings"] .mb-pad {
    padding-top: 18px !important;
    padding-bottom: 28px !important;
  }
  /* External / Proxy / Residential / Browser keys catalog list:
     overflowY:auto + maxHeight:520 puts a vertical scrollbar over the
     right edge — chevron and url ellipsis end up clipped. Reserve the
     gutter and bump row padding so content breathes. */
  [data-screen-label="04 Settings"] div[style*="max-height: 520"][style*="overflow-y: auto"] {
    scrollbar-gutter: stable !important;
  }
  [data-screen-label="04 Settings"] div[style*="max-height: 520"][style*="overflow-y: auto"] button {
    padding-right: 18px !important;
  }

  /* Tighter button padding for cramped clusters in TopBar. */
  .mb-tight { padding: 5px 8px !important; font-size: 11px !important; }

  /* Marketplace LiveFilterBar collapse. Default mobile state hides the
     chip row entirely — the mb-only "Filters · N ▾" toggle in the JSX
     reveals it via .nx-fbar-row-expanded. Chips themselves get tighter
     padding + smaller font when expanded so 6 dropdowns fit on ~2 rows
     instead of 4. */
  .nx-fbar-row { display: none !important; }
  .nx-fbar-row-expanded { display: flex !important; margin-top: 10px; gap: 6px !important; }
  .nx-fbar-row-expanded > button { padding: 5px 9px !important; font-size: 11px !important; }
  .nx-fbar-row-expanded > button > span:last-child { font-size: 8px !important; }

  /* Touch-target floor — every interactive element gets at least 36×36
     on mobile (iOS HIG suggests 44, but many of our action pills are
     deliberately compact; 36 is a safe minimum that doesn't break
     existing UI). Add :where() so specificity stays low — inline
     styles still win. Opt out with .mb-no-min on the element. */
  button:not(.mb-no-min):not([disabled]),
  a[role="button"]:not(.mb-no-min),
  [role="button"]:not(.mb-no-min) {
    min-height: 36px;
  }

  .nx-home-title {
    font-size: clamp(28px, 7.6vw, 34px) !important;
    line-height: 1.08 !important;
    max-width: min(360px, calc(100vw - 32px)) !important;
    margin: 0 auto 14px !important;
    letter-spacing: -0.005em !important;
    text-align: center !important;
    text-wrap: balance;
  }
  /* Override the desktop inline clamp() on the two H1 lines so the title
     fits the viewport. Defeat whiteSpace: nowrap as well — at phone width
     "for personal & business tasks" exceeds even max-content, so we let
     it wrap naturally as a soft 2-line subtitle below the headline. */
  .nx-home-title > span:nth-of-type(1) {
    font-size: clamp(30px, 9vw, 44px) !important;
    white-space: normal !important;
    display: block !important;
  }
  .nx-home-title > span:nth-of-type(2) {
    font-size: clamp(17px, 5.4vw, 24px) !important;
    white-space: normal !important;
    display: block !important;
    margin-top: 6px !important;
    color: var(--fg-strong) !important;
  }
  /* Legacy hooks for the older <br>+tail markup — kept harmless. */
  .nx-home-title .nx-title-br { display: none !important; }
  .nx-title-tail { display: block !important; }
  /* Mobile: drop the desktop one-screen lock — hero content (title +
     mobile copy + flow + CTA + 8-row stack strip) totals ~700px, which
     exceeded `100svh - 77px` on iPhone SE / short-viewport phones and
     was clipped under `overflow: hidden`. On mobile let the shell scroll
     naturally; sticky TopBar keeps the chrome on top. */
  .nx-vision-shell {
    height: auto !important;
    min-height: 100svh !important;
    overflow-y: auto !important;
    -webkit-overflow-scrolling: touch;
  }
  [data-screen-label="00 Vision"] {
    height: auto !important;
    min-height: 0 !important;
    overflow: visible !important;
  }
  .nx-home-hero {
    height: auto !important;
    min-height: 0 !important;
    padding-top: 18px !important;
    padding-bottom: 28px !important;
    align-items: flex-start !important;
    overflow: visible !important;
  }
  .nx-home-copy {
    display: block !important;
    width: 100% !important;
    max-width: min(360px, calc(100vw - 32px)) !important;
    margin: 0 auto !important;
    text-align: center !important;
  }
  .nx-home-eyebrow {
    justify-content: center !important;
    margin-bottom: 12px !important;
  }
  .nx-home-lede,
  .nx-home-detail {
    display: none !important;
  }
  .nx-mobile-copy {
    display: block !important;
    max-width: min(360px, calc(100vw - 32px)) !important;
    margin: 0 auto 16px !important;
  }
  .nx-mobile-copy p {
    margin: 0 !important;
    color: var(--fg-strong) !important;
    font-size: 13.5px !important;
    line-height: 1.5 !important;
    font-weight: 300 !important;
    text-align: center !important;
  }
  .nx-mobile-flow {
    display: grid !important;
    grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
    gap: 8px !important;
    width: 100% !important;
    max-width: min(360px, calc(100vw - 32px)) !important;
    margin: 0 auto 14px !important;
  }
  .nx-mobile-flow span {
    display: grid !important;
    align-content: center !important;
    justify-items: center !important;
    min-height: 68px !important;
    border: 1px solid var(--border) !important;
    background: rgba(0, 255, 136, 0.025) !important;
    padding: 10px 6px !important;
    color: var(--fg-strong) !important;
    font-size: 12px !important;
    line-height: 1.25 !important;
    font-weight: 350 !important;
    letter-spacing: -0.005em !important;
    text-align: center !important;
  }
  .nx-mobile-flow strong {
    display: block !important;
    margin-bottom: 7px !important;
    color: #00FF88 !important;
    font-family: "JetBrains Mono", ui-monospace, Menlo, monospace !important;
    font-size: 11px !important;
    font-weight: 500 !important;
    letter-spacing: 0.08em !important;
  }
  .nx-home-cta-row {
    display: grid !important;
    grid-template-columns: 1fr !important;
    gap: 8px !important;
    max-width: min(360px, calc(100vw - 32px)) !important;
    margin: 0 auto !important;
  }
  .nx-home-button {
    width: 100% !important;
    justify-content: center !important;
    min-height: 44px !important;
    padding: 12px 14px !important;
    font-size: 12.5px !important;
  }
  .nx-home-section {
    padding-top: 58px !important;
    padding-bottom: 32px !important;
  }
  .nx-future-hero {
    padding-top: 24px !important;
    padding-bottom: 14px !important;
  }
  .nx-future-title {
    font-size: clamp(31px, 9.1vw, 38px) !important;
    line-height: 1.02 !important;
    max-width: 330px !important;
  }
  .nx-future-console {
    min-height: 0 !important;
  }
  .nx-future-section {
    padding-top: 58px !important;
    padding-bottom: 34px !important;
  }
  .nx-step-card {
    min-height: 0 !important;
    padding: 18px !important;
  }
  .nx-step-card-head {
    margin-bottom: 14px !important;
  }
  /* Two-pane layouts that hardcode "320px 1fr" — collapse to single column. */
  .mb-twopane { grid-template-columns: 1fr !important; }

  /* ── Mobile master/detail (.nx-md-master / .nx-md-detail) ──────
     Used together with `useIsMobile()` + URL state to swap between a
     full-width list (master) and a full-width detail panel on phones.
     Add .is-hidden-mobile to whichever pane should not be visible.
     Desktop ≥769px ignores these classes — the parent two-pane grid
     keeps both columns side-by-side. */
  .nx-md-master, .nx-md-detail {
    width: 100% !important;
    min-width: 0 !important;
    grid-column: 1 / -1 !important;
  }
  .nx-md-master.is-hidden-mobile,
  .nx-md-detail.is-hidden-mobile,
  .is-hidden-mobile {
    display: none !important;
  }
  /* Sticky back-bar above mobile detail (overrides .mb-only's default
     inline-flex to flex so the title can ellipsis-truncate). */
  .nx-md-back { display: flex !important; }

  /* Drawer panel becomes full-screen on phones — it's the only thing on the
     surface and the floating "✕ close" already serves as a back affordance. */
  .nx-drawer-panel {
    width: 100vw !important;
    border-left: none !important;
    box-shadow: none !important;
  }
  /* Auto-fill card grids that hardcode minmax(>=320px) — drop the minmax floor
     so cards never push horizontal scroll on narrow viewports. */
  .mb-cards-auto { grid-template-columns: 1fr !important; }
  /* Compact composer / chat-surface padding override. */
  .mb-pad-compact { padding-left: 14px !important; padding-right: 14px !important; }
  /* Composer base + responsive padding. Base lives outside @media so desktop
     keeps the original 16/28 padding via inline style; mobile shrinks. */
  .nx-composer { padding: 14px 16px !important; }

  /* Stack strip on mobile — collapse the 4-col desktop bento to a single
     column. Cells use inline `gridColumn: span N` (anchor=2, chip=1) which
     would otherwise leave anchors trying to span phantom columns; force
     every cell to full row so the list reads top-to-bottom. */
  .nx-home-stack {
    display: block !important;
    max-width: min(360px, calc(100vw - 32px)) !important;
    margin: 14px auto 0 !important;
    padding-top: 14px !important;
  }
  .nx-home-stack-grid {
    grid-template-columns: 1fr !important;
    grid-auto-rows: auto !important;
    gap: 8px !important;
  }
  .nx-home-stack-grid > div {
    grid-column: 1 / -1 !important;
    min-height: 56px !important;
    padding: 12px 14px !important;
    gap: 4px !important;
  }
  /* Anchors keep their green sheen via the inline gradient — no need to
     change background here. Inner name + role/metric row stay as-is. */

  /* ── Worker card compact mode (≤768px) ─────────────────────────────
     Desktop card carries minHeight:480 + padding "12px 18px" per row for
     grid density. On phones (1-col grid) that leaves huge empty space
     under the BRAIN line for a never-run worker. Shrink:
       - drop the 480px floor so the card stretches to actual content;
       - tighten horizontal padding 18 → 12 on every direct child row;
       - tighter footer padding so the Chat/Run row sits flush. */
  .nx-worker-card {
    min-height: 0 !important;
  }
  .nx-worker-card > div {
    padding-left: 12px !important;
    padding-right: 12px !important;
  }
  /* Last row (footer with Chat / Run) gets a tighter vertical padding too —
     desktop uses 12px which feels loose under the now-collapsed card. */
  .nx-worker-card > div:last-child {
    padding-top: 9px !important;
    padding-bottom: 9px !important;
  }

  /* ── LiveChatPanel header on mobile ────────────────────────────────
     The header packs avatar + name + model-picker + Stop + Test/Live +
     Run on one wrapping flex row. On a narrow viewport that produces a
     cramped mess (10.5px caps text squeezed between 5+ chips). Mobile
     overrides:
       - shrink horizontal padding so the row gets ~10px more breathing
         room left and right;
       - keep `flex-wrap: wrap` (already inline) but force each chip to
         a 36px touch-floor with bumped font/padding so taps land cleanly;
       - smaller avatar (40 → 32) and tighter gap so first row stays
         on a single line: [back] [CR] [name + model] [Stop] [Run];
       - Test/Live toggle gets `mb-hide` in JSX so it's gone here. */
  .nx-chat-header {
    padding: 10px 14px !important;
    gap: 8px !important;
  }
  /* Avatar — shrink so the row doesn't wrap on iPhone SE. Target by class,
     NOT `> div:nth-of-type(1)`: for custom workers the avatar is a <button>,
     so the nth-of-type(1) div was actually the title block, which got crushed
     to 32px and forced its ⚙ Settings chip to spill over the Call button. */
  .nx-chat-header .nx-chat-avatar {
    width: 32px !important;
    height: 32px !important;
    min-height: 0 !important;
    padding: 0 !important;
    font-size: 11px !important;
  }
  .nx-chat-header > button,
  .nx-chat-header > a {
    min-height: 36px !important;
    padding: 8px 12px !important;
    font-size: 11px !important;
    letter-spacing: 0.12em !important;
  }
  /* The back-arrow chip keeps its glyph-height instead of getting
     fattened by the generic button rule above. */
  .nx-chat-header > button.mb-only {
    min-height: 32px !important;
    width: 32px !important;
    padding: 4px 8px !important;
    font-size: 16px !important;
  }
  /* Action chips (+ Telegram, ■ Stop, ▶ Start, ▶ Run, Telegram-connected
     pill). The generic `> button` rule above bumps them to 36px tall to
     hit a comfortable touch target — but that fattens the whole row and
     forces it to wrap on tablet-narrow widths, leaving the LLM picker
     visually colliding with the wrapped buttons. Bring action chips back
     to compact sizing (still 30px min-height = WCAG-friendly) so the
     strip stays single-row on most mobile viewports. */
  .nx-chat-header .nx-chat-act {
    min-height: 30px !important;
    padding: 5px 10px !important;
    font-size: 10px !important;
    letter-spacing: 0.08em !important;
  }
  /* In mobile full-screen chat the chat-header keeps its full toolbar
     (avatar, name, ⚙ Settings, ▶ Run, ■ Stop) — only the global TopBar
     gets hidden (see the body.nx-chat-fullscreen .nx-topbar-root rule
     outside this media query). Compact ergonomics for the toolbar live
     in the .nx-chat-header block above. */
  /* Composer in mobile chat — extra-compact so the keyboard + the
     "Message …" input + the action row fit without scrolling on small
     screens. Reduces padding, removes the inter-element gap, and caps
     the textarea auto-grow at ~3 lines so a long draft doesn't push
     the send row off-screen. */
  body.nx-chat-fullscreen .nx-composer {
    padding: 8px 10px !important;
    gap: 0 !important;
  }
  /* Mobile chat: collapse composer into a single inline row — textarea on
     the left, mic + Send pinned right. LIVE chip, char counter and 📎 are
     hidden so the row stays uncluttered and the messages pane gets the
     freed vertical space. */
  body.nx-chat-fullscreen .nx-composer-input {
    display: flex !important;
    flex-direction: row !important;
    align-items: flex-end !important;
    gap: 8px !important;
    width: 100% !important;
  }
  body.nx-chat-fullscreen .nx-composer textarea {
    flex: 1 1 0 !important;
    min-width: 0 !important;
    max-height: 96px !important;
    min-height: 36px !important;
    /* 16px floor: iOS Safari auto-zooms the page when a focused field's
       font-size is < 16px. Anything smaller here re-triggers the zoom on tap. */
    font-size: 16px !important;
    padding: 8px 10px !important;
    background: var(--bg-soft) !important;
    border: 1px solid var(--border-soft) !important;
    border-radius: var(--radius-md) !important;
    line-height: 1.4 !important;
  }
  body.nx-chat-fullscreen .nx-composer .nx-composer-row {
    flex: 0 0 auto !important;
    gap: 6px !important;
    justify-content: flex-end !important;
    align-items: center !important;
    margin-top: 0 !important;
  }
  body.nx-chat-fullscreen .nx-composer-chip-mode,
  body.nx-chat-fullscreen .nx-composer-charcount,
  body.nx-chat-fullscreen .nx-composer-attach,
  body.nx-chat-fullscreen .nx-composer .nx-composer-row .mb-hide {
    display: none !important;
  }
  /* Tighten Send button on mobile so it doesn't push the textarea too far.
     Height matches the mic chip (38px inline) so the composer action row is a
     single even-height line on mobile, mirroring desktop. */
  body.nx-chat-fullscreen .nx-composer .nx-composer-row > button:last-child {
    padding: 0 14px !important;
    height: 38px !important;
    font-size: 11px !important;
    letter-spacing: 0.1em !important;
  }
}

/* Fullscreen worker chat (LiveChatExperience.mobView === "chat"): kill the
   global TopBar at every width so the screen is just the conversation + the
   ‹ back chip. Body class is set only while mobView === "chat" in
   workspace.jsx, so desktop layouts are unaffected in practice. */
body.nx-chat-fullscreen .nx-topbar-root { display: none !important; }
/* WorkspaceView root locks `height: calc(100vh - 75px)` to reserve space for
   the global TopBar. In mobile fullscreen-chat the TopBar is hidden — but the
   subtracted 75 px stayed, leaving a dead strip below the composer. Reclaim
   the full viewport so the chat history + composer stretch to the bottom.

   Pinned to the *visual* viewport, not the layout viewport: on iOS the soft
   keyboard and the collapsing URL-bar don't resize 100vh/svh. The fix has
   three parts, all driven from window.visualViewport in LiveChatExperience:
     1. Lock the body (position:fixed + inset:0) so the *page* can't scroll.
     2. Size the chat shell to the live visualViewport HEIGHT (--nx-chat-vh)
        so when the keyboard slides up the shell shrinks and the composer (the
        bottom flex child) rides just above it instead of being covered.
     3. Compensate for the visualViewport OFFSET (--nx-chat-top) with a GPU
        transform. When iOS pans the visual viewport up to reveal the focused
        input, the fixed shell would otherwise stay glued to the layout-top
        and the composer would "slide to the top" of the screen. Translating
        the shell down by offsetTop re-glues it to the visible region. Using
        transform (not top) keeps it on the compositor — no layout jitter.
   Falls back to 100svh / 0 offset before the first measurement. */
body.nx-chat-fullscreen {
  position: fixed !important;
  inset: 0 !important;
  overflow: hidden !important;
}
body.nx-chat-fullscreen [data-screen-label="03 Workspace"] {
  position: fixed !important;
  left: 0 !important;
  right: 0 !important;
  top: 0 !important;
  height: var(--nx-chat-vh, 100svh) !important;
  transform: translateY(var(--nx-chat-top, 0px)) !important;
}

/* ── Master/detail at the wider mobile breakpoint (≤960px) ──────────
   The main mobile utility block above gates on 768px to avoid disturbing
   desktop/tablet typography. The chat-style list→detail toggle should
   apply on a wider band so that iPhone landscape (≤932) and small
   tablets in portrait (810–960) also get a single-pane swap instead of
   stacking the list above the detail. Only the strict master/detail
   rules are duplicated here; everything else (mb-pad, mb-only, drawer,
   …) keeps the 768 cap. JS useIsMobile() also uses 960 so the
   `is-hidden-mobile` class is added at the same threshold. */
@media (max-width: 960px) {
  .mb-twopane { grid-template-columns: 1fr !important; }
  .nx-md-master, .nx-md-detail {
    width: 100% !important;
    min-width: 0 !important;
    grid-column: 1 / -1 !important;
  }
  .nx-md-master.is-hidden-mobile,
  .nx-md-detail.is-hidden-mobile,
  .is-hidden-mobile {
    display: none !important;
  }
  .nx-md-back { display: flex !important; }
}

/* On the narrowest viewports (phones in portrait), tighten the chat
   header further and drop the "+ Telegram" word — the ✈ glyph alone is
   enough to convey the affordance and saves precious horizontal space. */
@media (max-width: 480px) {
  .nx-chat-header {
    padding: 8px 10px !important;
    gap: 6px !important;
  }
  .nx-chat-header .nx-chat-act {
    padding: 4px 8px !important;
    font-size: 9.5px !important;
    letter-spacing: 0.06em !important;
  }
  .nx-chat-header .nx-chat-act .nx-chat-act-label { display: none !important; }
}

@media (min-width: 481px) and (max-width: 768px) {
  .nx-home-title {
    max-width: min(560px, calc(100vw - 56px)) !important;
    margin-bottom: 16px !important;
  }
  /* Tablet bumps the headline + subtitle a notch above the phone scale. */
  .nx-home-title > span:nth-of-type(1) {
    font-size: clamp(38px, 6.8vw, 56px) !important;
  }
  .nx-home-title > span:nth-of-type(2) {
    font-size: clamp(22px, 4.2vw, 32px) !important;
  }
  .nx-home-copy,
  .nx-mobile-copy,
  .nx-mobile-flow,
  .nx-home-cta-row,
  .nx-home-stack {
    max-width: min(560px, calc(100vw - 56px)) !important;
  }
  .nx-mobile-copy {
    margin-bottom: 16px !important;
  }
  .nx-mobile-flow span {
    min-height: 54px !important;
    font-size: 11.5px !important;
  }
  /* Tablet: stay on 1-col like phones. A 2-col tablet bento sounds nice
     but the HOME_STACK order has chips interleaved with anchors (chip,
     chip, chip, anchor, chip, anchor, anchor, …), and CSS grid auto-flow
     leaves orphan half-slots before every span-2 anchor. The 1-col list
     reads cleanly top-to-bottom; cells just get slightly more padding
     for the wider viewport. */
  .nx-home-stack-grid > div {
    min-height: 64px !important;
    padding: 13px 18px !important;
  }
}

@media (max-width: 768px) and (max-height: 620px) {
  .nx-home-hero {
    padding-top: 4px !important;
    padding-bottom: 6px !important;
  }
  .nx-home-eyebrow {
    margin-bottom: 6px !important;
    font-size: 10px !important;
    letter-spacing: 0.14em !important;
  }
  .nx-home-title {
    font-size: clamp(25px, 7.6vw, 31px) !important;
    max-width: 300px !important;
    margin-bottom: 8px !important;
  }
  .nx-mobile-copy {
    max-width: 300px !important;
    margin-bottom: 8px !important;
  }
  .nx-mobile-copy p {
    font-size: 12px !important;
    line-height: 1.38 !important;
  }
  .nx-mobile-copy p:first-child {
    font-size: 13px !important;
    margin-bottom: 5px !important;
  }
  .nx-mobile-flow {
    gap: 6px !important;
    margin-bottom: 8px !important;
  }
  .nx-mobile-flow span {
    min-height: 42px !important;
    padding: 5px !important;
    font-size: 10px !important;
    line-height: 1.16 !important;
  }
  .nx-mobile-flow strong {
    margin-bottom: 3px !important;
    font-size: 9.5px !important;
  }
  .nx-home-cta-row {
    gap: 6px !important;
  }
  .nx-home-button {
    min-height: 36px !important;
    padding: 7px 10px !important;
  }
}

/* ── ≤480px overrides — drop one more level on already-mobile layouts ── */
@media (max-width: 480px) {
  .mb-stack-3, .mb-stack-4 { grid-template-columns: 1fr !important; }
  .mb-stack-6 { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
  /* Body padding on already-padded surfaces — avoid cards bumping the edge. */
  .mb-pad { padding-left: 12px !important; padding-right: 12px !important; }
  .nx-home-title {
    font-size: clamp(27px, 7.4vw, 32px) !important;
    max-width: min(360px, calc(100vw - 32px)) !important;
  }
  .nx-home-section {
    padding-top: 50px !important;
    padding-bottom: 28px !important;
  }
  .nx-future-title {
    font-size: clamp(30px, 8.9vw, 36px) !important;
    max-width: 320px !important;
  }
  .nx-future-section {
    padding-top: 50px !important;
    padding-bottom: 28px !important;
  }
  .nx-home-section h2 {
    font-size: 28px !important;
    line-height: 1.08 !important;
  }
  .nx-future-section h2 {
    font-size: 28px !important;
    line-height: 1.08 !important;
  }
  /* Search/sort row — stack input over sort control on narrow phones. */
  .mb-row-stack { display: grid !important; grid-template-columns: 1fr !important; gap: 8px !important; align-items: stretch !important; }
}

@media (max-width: 768px) and (max-height: 620px) {
  .nx-home-title {
    font-size: clamp(25px, 7.6vw, 31px) !important;
    max-width: 300px !important;
    margin-bottom: 8px !important;
  }
}

/* ── ≤380px overrides — iPhone SE / older Android phones ────────── */
@media (max-width: 380px) {
  .mb-pad { padding-left: 10px !important; padding-right: 10px !important; }
  .mb-stack-6 { grid-template-columns: 1fr !important; }
  /* Chat header: hide secondary controls (Edit, Clear, mode label) — Run/Stop stay. */
  .mb-chat-secondary { display: none !important; }
  .nx-composer { padding: 12px 12px !important; }
}

/* Layout-specific skeleton bodies (rendered inside React tree, after mount). */
.sk-page { padding: 32px 40px; max-width: 1600px; margin: 0 auto; }
.sk-h1 { width: 280px; max-width: 60vw; height: 28px; margin: 8px 0 12px; }
.sk-eyebrow { width: 140px; height: 11px; margin-bottom: 8px; }
.sk-lede { width: 460px; max-width: 80vw; height: 14px; margin-bottom: 28px; }
.sk-grid { display: grid; gap: 16px; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
.sk-card { border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px; min-height: 168px; display: flex; flex-direction: column; gap: 12px; }
.sk-card-row { height: 14px; }
.sk-card-row.sm { width: 60%; height: 11px; }
.sk-card-row.lg { width: 90%; }

/* Dimensions match the real SettingsView/WorkspaceView shell so the swap
   from RouteSkeleton → real route doesn't shift the layout (used to flash
   ~11 px of vertical jump + sidebar padding change on every cold load). */
.sk-with-sidebar { display: flex; height: calc(100vh - 75px); overflow: hidden; }
.sk-sidebar { width: 260px; border-right: 1px solid var(--border); padding: 20px 0; display: flex; flex-direction: column; gap: 6px; flex-shrink: 0; background: var(--bg-soft); }
.sk-sidebar-row { height: 24px; margin: 0 14px; border: 1px solid var(--border-soft); border-radius: var(--radius-sm); }
.sk-sidebar-head { height: 18px; width: 60%; margin: 0 16px 16px; border: 1px solid var(--border-soft); border-radius: var(--radius-sm); }
.sk-content { flex: 1; padding: 32px 40px 48px; max-width: 1280px; }
@media (max-width: 768px) {
  .sk-sidebar { display: none; }
  .sk-content { padding: 24px 16px 40px !important; }
}
@media (max-width: 480px) {
  .sk-content { padding: 20px 12px 36px !important; }
}

.sk-split { display: grid; grid-template-columns: 380px 1fr; gap: 18px; padding: 32px 40px; }
.sk-split-pane { border: 1px solid var(--border); border-radius: var(--radius-md); min-height: 320px; padding: 18px; display: flex; flex-direction: column; gap: 10px; }

/* Loading spinner — used wherever a panel is awaiting first data */
@keyframes nx-spin { to { transform: rotate(360deg); } }
.nx-spinner {
  width: 22px;
  height: 22px;
  border: 2px solid var(--border-mid, #2a2a3a);
  border-top-color: var(--fg, #fff);
  border-radius: 50%;
  animation: nx-spin 0.7s linear infinite;
  flex-shrink: 0;
}
.nx-spinner-inline {
  width: 12px;
  height: 12px;
  border: 1.5px solid var(--border-mid, #2a2a3a);
  border-top-color: currentColor;
  border-radius: 50%;
  animation: nx-spin 0.7s linear infinite;
  display: inline-block;
  vertical-align: -2px;
}
.nx-loading-pane {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 160px;
  color: var(--fg-muted, #888);
  gap: 10px;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-family: inherit;
}
.nx-loading-pane.compact { min-height: 96px; }
@media (prefers-reduced-motion: reduce) {
  .nx-spinner, .nx-spinner-inline { animation: none; }
}
/* Chat-bubble markdown render. Inherits color/size from parent bubble. */
.nx-md p { margin: 0 0 8px; line-height: 1.55; }
.nx-md p:last-child { margin-bottom: 0; }
.nx-md h3, .nx-md h4, .nx-md h5, .nx-md h6 {
  margin: 12px 0 6px; font-weight: 600; line-height: 1.3; letter-spacing: -0.005em;
}
.nx-md h3 { font-size: 1.05em; }
.nx-md h4 { font-size: 1em; }
.nx-md h5, .nx-md h6 { font-size: 0.95em; opacity: 0.9; }
.nx-md h3:first-child, .nx-md h4:first-child, .nx-md h5:first-child, .nx-md h6:first-child { margin-top: 0; }
.nx-md ul, .nx-md ol { margin: 4px 0 8px; padding-left: 22px; }
.nx-md li { margin-bottom: 3px; line-height: 1.5; }
.nx-md li:last-child { margin-bottom: 0; }
.nx-md strong { font-weight: 600; }
.nx-md em { font-style: italic; }
.nx-md code {
  font-family: "JetBrains Mono", ui-monospace, Menlo, monospace;
  font-size: 0.88em; padding: 1px 5px; border-radius: var(--radius-sm);
  background: rgba(127,127,127,0.18);
}
.nx-md a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.nx-md a:hover { opacity: 0.85; }
.nx-md hr { margin: 12px 0; border: 0; border-top: 1px solid var(--border-soft); }
.nx-md blockquote {
  margin: 8px 0; padding: 4px 12px;
  border-left: 3px solid var(--border-mid);
  color: var(--fg-muted); font-style: italic;
}
.nx-md blockquote p { margin: 0; }
/* Tables wider than the bubble must not stretch the bubble (which would
   defeat the chat panel's flex:1 + overflow-y on the message list and
   kill vertical scrolling). The wrapper trick: render the <table> as
   block, cap it at 100% of its container, and scroll horizontally
   when needed. The .nx-md container itself is min-width: 0 so flex
   ancestors can shrink it. */
.nx-md { min-width: 0; max-width: 100%; }
.nx-md table {
  border-collapse: collapse; margin: 8px 0; font-size: 0.92em;
  display: block; max-width: 100%; width: max-content;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
}
.nx-md th, .nx-md td {
  border: 1px solid var(--border-soft);
  padding: 5px 9px; text-align: left; vertical-align: top;
  white-space: nowrap;
}
.nx-md th { background: rgba(127,127,127,0.08); font-weight: 600; }
.nx-md tr:nth-child(even) td { background: rgba(127,127,127,0.04); }
