/* ═══════════════════════════════════════════════
   NX-UI Design System — layout.css v2.7.0
   Sidebar (Claude.ai slide), grid, responsive, mobile
   topbar-none + content-centered modes (data-topbar="none", data-content="centered")
   sidebar-none mode (data-sidebar="none" → 1-col grid)
   nx-page, nx-focus, nx-sb-*, mobile always-visible default,
           D1 mobile sidebar modes (data-mobile-side), D2 mobile search
   ═══════════════════════════════════════════════ */

/* ── App shell ── */
/* Block browser swipe-nav + pull-to-refresh/bounce at root.
   `overscroll-behavior: none` disables horizontal swipe-back nav AND vertical
   pull-to-refresh/bounce. Chat/dashboard app context — pull-to-refresh не е
   desired UX (causes container drift when dragging top areas).
   Real scroll contexts are .nx-content (inside-app) + per-app content. */
html, body {
  height: 100%;
  overscroll-behavior: none;
  overflow: hidden;
  touch-action: pan-y;
}

/* Kill the mobile tap-highlight flash — the blue overlay that briefly covers a
   link/button on tap (Chrome/Android, some WebKit). Cosmetic only; focus-visible
   outlines for keyboard a11y are unaffected. Global base reset (all apps). */
* { -webkit-tap-highlight-color: transparent; }

.nx-app {
  display: grid;
  grid-template-columns: var(--side-collapsed) 1fr;
  grid-template-rows: calc(var(--topbar-h) + env(safe-area-inset-top, 0px)) 1fr;
  grid-template-areas:
    "side topbar"
    "side content";
  height: 100dvh;       /* was min-height — viewport-lock so .nx-content is the sole scroller (PWA address-bar fix, 2026-05-29) */
  position: relative;
  overflow: hidden;
  touch-action: manipulation; /* native: kill 300ms double-tap-zoom delay (children override with pan-x/pan-y/none where needed) */
}

/* Sidebar position = "right" — flip grid order */
.nx-app[data-sidebar-pos="right"] {
  grid-template-columns: 1fr var(--side-collapsed);
  grid-template-areas:
    "topbar side"
    "content side";
}
.nx-app[data-sidebar-pos="right"].is-expanded,
.nx-app[data-sidebar-pos="right"]:has(.nx-side.is-expanded) {
  grid-template-columns: 1fr var(--side-expanded);
}
/* .nx-side has position:fixed; left:0 which override-ва grid
   placement. Override here за right-mode — pin to right edge, border на ляво. */
.nx-app[data-sidebar-pos="right"] .nx-side {
  left: auto;
  right: 0;
  border-right: 0;
  border-left: 1px solid var(--border);
}

/* ── App shell with focus sub-bar (opt-in via .nx-focus child) ── */
.nx-app:has(.nx-focus) {
  grid-template-rows: calc(var(--topbar-h) + env(safe-area-inset-top, 0px)) auto 1fr;
  grid-template-areas:
    "side topbar"
    "side focus"
    "side content";
}

/* ── nx-page — SPA routed page (uses nxFadeIn from components.css) ── */
.nx-page { display: none; }
.nx-page.is-on {
  display: block;
  animation: nxFadeIn .3s cubic-bezier(.16,1,.3,1);
}

/* ── nx-focus — Sub-bar under topbar ── */
.nx-focus {
  grid-area: focus;
  border-bottom: 1px solid var(--border);
  background: var(--bg-sidebar);
  padding: 8px var(--sp);
  display: flex;
  align-items: center;
  gap: calc(var(--sp) * 0.667);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  /* removed margin-left — grid column 1 reserves space */
}

/* ── Sidebar ── */
.nx-side {
  grid-area: side;
  background: var(--bg-sidebar);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: calc(var(--sp) * 0.5 + env(safe-area-inset-top, 0px)) 0 calc(var(--sp) * 0.5 + env(safe-area-inset-bottom, 0px)); /* native: clear notch top + home-indicator bottom (env=0 elsewhere) */
  gap: 4px;
  z-index: var(--z-raised);
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: var(--side-collapsed);
  /* slide reverted per @deyan — handled in a separate session */
  overflow: hidden;
}

/* Desktop default — sidebar fully expanded (labels visible) via
   the canonical .is-expanded class added by shell.js boot. CSS-only grid
   reservation stays here so layout doesn't shift on JS load.
   + suppress overlay/shadow/click-collapse on desktop so sidebar
   behaves as part of layout, not floating modal. */
@media (min-width: 1024px) and (pointer: fine) {
  .nx-app {
    grid-template-columns: var(--side-expanded) 1fr;
  }
  /* No backdrop overlay on desktop — sidebar is layout, not modal */
  .nx-side-overlay,
  .nx-side-overlay.is-open { display: none !important; }
  /* Neutralize floating-modal effects of .is-expanded — solid + flush with content */
  .nx-side.is-expanded {
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background: var(--bg-sidebar);
  }
}

/* sidebar-none mode — when manifest.sidebar === false,
   shell.js adds data-sidebar="none" → grid collapses to single column. */
.nx-app[data-sidebar="none"] {
  grid-template-columns: 1fr;
  grid-template-areas:
    "topbar"
    "content";
}

/* topbar-none mode — when manifest.topbar === false,
   shell.js adds data-topbar="none" → topbar row collapses. Floating cog
   (.nx-floating-cog in components.css) renders independently when
   settings:true. */
.nx-app[data-topbar="none"] {
  grid-template-rows: 1fr;
  grid-template-areas:
    "side content";
}
.nx-app[data-topbar="none"][data-sidebar="none"] {
  grid-template-columns: 1fr;
  grid-template-areas: "content";
}

/* content-centered mode — when manifest.content === "centered",
   shell.js adds data-content="centered" → .nx-content gets max-width
   wrapper for premium centered look. */
/* content-centered mode — when manifest.content === "centered",
   shell.js adds data-content="centered" → .nx-content gets max-width
   wrapper for premium centered look. */
.nx-app[data-content="centered"] .nx-content {
  max-width: 1100px; /* fixed: layout content cap */
  margin: 0 auto;
  padding: 0 var(--sp);
  width: 100%;
  box-sizing: border-box;
}

/* content-full-height mode — manifest.content === "full-height".
   For chat / dashboard / map apps where inner content must occupy full viewport
   and own its scroll context. .nx-content drops own scroll + padding so flex
   children (cm-room, etc.) control height/scroll directly. */
.nx-app[data-content="full-height"] .nx-content {
  overflow: hidden;
  padding: 0;
}

/* Expanded state (canonical, v2.3.0) */
.nx-side.is-expanded {
  width: var(--side-expanded);
  background: rgba(10, 10, 14, 0.85);
  backdrop-filter: blur(40px) saturate(180%);
  -webkit-backdrop-filter: blur(40px) saturate(180%);
  border-right-color: rgba(255, 255, 255, 0.08);
  box-shadow: var(--shadow-3); /* functional floating-overlay separation (the glass above is fx-flattened in effects.css under [data-fx=none]) */
}

/* Hamburger / pin button */
.nx-side-toggle {
  width: 100%;
  min-height: var(--tap-min); /* native: 44 tap target */
  display: flex;
  align-items: center;
  /* gap + padding scale + center хамбургер when collapsed */
  gap: calc(12px * var(--app-scale));
  padding: calc(8px * var(--app-scale)) calc(12px * var(--app-scale));
  margin-bottom: 8px;
  background: none;
  border: none;
  color: var(--text-3);
  cursor: pointer;
  transition: color .15s;
  min-height: calc(40px * var(--app-scale));
  justify-content: center;
}
/* When sidebar expanded — natural left-align (label visible) */
.nx-side.is-expanded .nx-side-toggle {
  justify-content: flex-start;
}
.nx-side-toggle:hover { color: var(--text); }
.nx-side-toggle svg {
  flex-shrink: 0;
  /* scale с --app-scale (was hardcoded 20px) — sidebar hamburger icon */
  width: calc(24px * var(--app-scale));
  height: calc(24px * var(--app-scale));
  /* removed margin-left (was 6px static — caused off-center когато collapsed) */
}
.nx-side-toggle span {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 2px;
  text-transform: uppercase;
  white-space: nowrap;
  opacity: 0;
  /* display:none removes from flex flow → ☰ stays centered when collapsed */
  display: none;
  transition: opacity .15s;
}
.nx-side.is-expanded .nx-side-toggle span { opacity: 1; display: inline; }

/* Nav items */
.nx-side-nav {
  display: flex;
  flex-direction: column;
  width: 100%;
  gap: 2px;
  padding: 0 8px;
  flex: 1 1 0;          /* take remaining height → footer (settings/avatar) pins to bottom */
  min-height: 0;
  overflow-y: auto;     /* scroll when items exceed available height (any align, fixes clipped footer) */
  overflow-x: hidden;
  scrollbar-width: none;      /* Firefox/Edge hide scrollbar */
  -ms-overflow-style: none;
}
.nx-side-nav::-webkit-scrollbar { width: 0; height: 0; display: none; } /* WebKit hide */

/* Sidebar vertical alignment — [data-sidebar-align="center"] centers the nav
   items (Instagram-style). Scroll/flex now live on the base .nx-side-nav above. */
.nx-app[data-sidebar-align="center"] .nx-side-nav {
  justify-content: center;
}
/* Disable text selection + long-press callout on touch devices (coarse pointer)
   so message long-press / button taps don't select text or pop the OS menu.
   Desktop (fine pointer) keeps normal selection; text inputs stay selectable. */
@media (pointer: coarse) {
  body { -webkit-user-select: none; -ms-user-select: none; user-select: none; -webkit-touch-callout: none; }
  input, textarea, [contenteditable], [contenteditable] * { -webkit-user-select: text; user-select: text; }
}
.nx-side-item {
  display: flex;
  align-items: center;
  min-height: var(--tap-min); /* native: 44 tap target */
  /* gap scales с --app-scale */
  gap: calc(12px * var(--app-scale));
  /* padding scales с --app-scale */
  padding: calc(8px * var(--app-scale)) calc(10px * var(--app-scale));
  border-radius: calc(var(--radius) * 0.667);
  cursor: pointer;
  color: var(--text-3);
  text-decoration: none;
  /* scoped transition to color only — was `all .15s` which animated
     background + box-shadow + colors simultaneously, creating "expanding ring"
     feel on click. Now only label color transitions smoothly. */
  transition: color .15s ease;
  white-space: nowrap;
  /* min-height scales с --app-scale (was 36px hardcoded) */
  min-height: calc(40px * var(--app-scale));
  /* anchor for [data-has-count] dot indicator (::after) */
  position: relative;
  /* center slot horizontally when sidebar collapsed (overridden when expanded) */
  justify-content: center;
}
/* When sidebar expanded → revert to left-align so
   slot + label flow normally. */
.nx-side.is-expanded .nx-side-item {
  justify-content: flex-start;
}
.nx-side-item:hover {
  color: var(--text);
  background: var(--bg-card-hover);
}
.nx-side-item.is-active {
  color: var(--accent);
  background: var(--bg-card);
}
.nx-side-item svg {
  flex-shrink: 0;
  /* SVG icons scale с --app-scale (replaces hardcoded 18px) */
  width: calc(24px * var(--app-scale));
  height: calc(24px * var(--app-scale));
}
.nx-side-item-label {
  font-family: var(--font-body);
  font-size: var(--text-base);
  opacity: 0;
  /* width 0 when collapsed so labels don't push slots off-center.
     display:none completely removes from flex flow (was max-width:0,
     но flex gap still applied между slot и invisible label → slot off-center). */
  display: none;
  transition: opacity .15s;
}
.nx-side.is-expanded .nx-side-item-label { opacity: 1; display: inline; }

/* ── Sidebar item dot indicator (visible when sidebar collapsed) ── */
/* App writes data-has-count="true" via NxSettings-agnostic JS (e.g. setSidebarCount). */
/* When sidebar is expanded/hovered → numerical pill takes over; dot hides. */
.nx-side-item[data-has-count="true"]::after {
  content: '';
  position: absolute;
  top: calc(5px * var(--app-scale));
  right: calc(5px * var(--app-scale));
  width: calc(6px * var(--app-scale));
  height: calc(6px * var(--app-scale));
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 4px var(--accent);
  pointer-events: none;
  opacity: 1;
  transition: opacity .15s;
}
.nx-side.is-expanded .nx-side-item[data-has-count="true"]::after { opacity: 0; }

/* Sidebar section labels (above nav groups) */
.nx-side .nx-meta {
  padding: calc(var(--sp) * 0.5) calc(var(--sp) * 0.5) 4px;
  letter-spacing: 2px;
  text-transform: uppercase;
  opacity: 0;
  max-height: 32px; /* fixed: sidebar meta chrome */
  overflow: hidden;
  transition: opacity .15s, padding .15s, max-height .15s;
}
.nx-side.is-expanded .nx-meta { opacity: 1; }

/* Collapsed-default state — meta shrinks до thin gap (no opacity-only;
   преди това px16+12+4 vertical space се запазваше invisible → x2 gap visual).
   bump shrunk size 8→14px max-height + 6+2 pad — items имаха слят look.
   bump otnovo 14→24px + 10+4 pad per user "digni oshte". */
.nx-side:not(.is-expanded) .nx-meta {
  padding: 10px 0 4px; /* fixed: collapsed-sidebar chrome */
  max-height: 24px; /* fixed: sidebar meta chrome */
}

/* ── nx-sb-count — Sidebar badge count ── */
.nx-side-count {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--accent);
  background: var(--accent-bg);
  padding: 1px 6px;
  border: 1px solid var(--accent-border);
  border-radius: var(--radius);
  opacity: 0;
  /* display:none когато collapsed (was opacity-only) — count badge
     was taking flex width, push-ваше slot indicators off-center. */
  display: none;
  transition: opacity .15s;
}
.nx-side.is-expanded .nx-side-count { opacity: 1; display: inline-block; }

/* ── nx-sb-bottom — Sidebar bottom info ── */
.nx-side-footer {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;                       /* #13 — match .nx-side-nav item spacing */
  padding: 6px 8px;               /* symmetric vertical rhythm to match the nav items */
  width: 100%;
  border-top: 1px solid var(--border);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
}
.nx-side-footer > * {
  opacity: 0;
  transition: opacity .15s;
  white-space: nowrap;
}
.nx-side.is-expanded .nx-side-footer > * { opacity: 1; }

/* ══════════════════════════════════════════════════════════════════════════
   Sidebar SLOT components — canonical `.nx-*` surface за consumer apps.
   Promoted 2026-05-26 от nx-chat-mesh's cm-side-avatar / cm-side-hash /
   cm-online-dot per nx-ui-everywhere doctrine (workspace rule 13).

   Used inside `.nx-side-item` icon slot (replaces or accompanies SVG icon).
   Apps build sidebar entries via nxSidebarItem / sideItem helper passing one
   of these as `icon` / `slotHtml`.

   ══════════════════════════════════════════════════════════════════════════
   v2.X.0 — Feature-bundle migration Step 1 (2026-05-26):
   .nx-side-avatar / .nx-side-chip / .nx-side-pulse + theming + opt-out moved
   to features/sidebar-slots/feature.css. Loaded separately by index.html.
   App opt-in declarative via manifest.features: ["sidebar-slots"].
   ══════════════════════════════════════════════════════════════════════════ */

/* Mobile sidebar overlay backdrop */
.nx-side-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,.5);
  z-index: var(--z-sticky);
}
.nx-side-overlay.is-open { display: block; }

/* Mobile sidebar toggle button (hidden on desktop, visible on mobile)
   raise specificity to (0,2,0) so .nx-topbar-btn { display:flex; }
   (which has equal (0,1,0) specificity and appears later in cascade) cannot
   override the hide. Without this, the topbar mobile hamburger is visible
   on desktop too — duplicate of the .nx-side-toggle inside the sidebar. */
.nx-topbar-btn.nx-side-mobile-toggle { display: none; }

/* ── Topbar ── */
.nx-topbar {
  grid-area: topbar;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: env(safe-area-inset-top, 0px) var(--sp) 0; /* native: clear status-bar/notch (env=0 on non-notched) */
  border-bottom: 1px solid var(--border);
  background: var(--bg-glass);
  backdrop-filter: blur(14px) saturate(1.5);
  -webkit-backdrop-filter: blur(14px) saturate(1.5);
  position: sticky;
  top: 0;
  z-index: 5;
  /* removed margin-left — grid column 1 already reserves sidebar space.
     Previous margin-left created double-gap (56px column + 56px margin). */
}
.nx-topbar-title {
  font-family: var(--font-body);
  font-size: var(--text-lg);
  font-weight: 500;
  color: var(--text);
}
/* .nx-topbar-meta — folded into .nx-meta primitive in components.css (G1 dedup) */
.nx-topbar-actions {
  display: flex;
  align-items: center;
  /* Density-driven gap.
     bumped multiplier 0.34→0.5 за по-видим Density ефект.
     Compact 10 → 5px, Normal 16 → 8px, Relax 24 → 12px, Wide 36 → 18px */
  gap: calc(var(--sp) * 0.5);
}
.nx-topbar-btn {
  /* scale с --app-scale; min 44px tap target (native) */
  width: var(--tap-min);
  height: var(--tap-min);
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: calc(var(--radius) * 0.667);
  border: 1px solid var(--border);
  background: var(--bg-card);
  color: var(--text-3);
  cursor: pointer;
  transition: all .15s;
  flex-shrink: 0;
}
.nx-topbar-btn:hover {
  color: var(--text);
  border-color: var(--accent-border);
  background: var(--bg-card-hover);
}
/* SVG sizing inside topbar buttons — was hardcoded 18px on the SVG attrs,
   now driven by var(--app-scale) here. */
.nx-topbar-btn svg {
  width: calc(18px * var(--app-scale));
  height: calc(18px * var(--app-scale));
  flex-shrink: 0;
}

/* ── nx-topbar-search — visual search trigger (base; moved here from
   components.css in modular-realignment Phase 2.3 — topbar-family ownership.
   Mobile icon-only override lives in the @media(max-width:767px) block below). ── */
.nx-topbar-search {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px; /* fixed: topbar chrome control */
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-card);
  cursor: pointer;
  /* min-width scales с --app-scale */
  min-width: calc(200px * var(--app-scale));
  transition: border-color .15s, background .15s;
  color: var(--text-3);
}
.nx-topbar-search:hover {
  border-color: var(--accent-border);
  color: var(--text-2);
}
/* SVG inside scales с --app-scale (was hardcoded 18px attr — now SVG attr removed) */
.nx-topbar-search svg {
  width: calc(18px * var(--app-scale));
  height: calc(18px * var(--app-scale));
  flex-shrink: 0;
}
.nx-topbar-search input {
  flex: 1;
  background: none;
  border: none;
  outline: none;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--text-2);
  cursor: pointer;
  pointer-events: none;
}
.nx-topbar-search input::placeholder { color: var(--text-3); }
.nx-topbar-search kbd {
  font-family: var(--font-mono);
  font-size: calc(9px * var(--app-scale)); /* typography dimension → scales with App Size */
  padding: 1px 5px;
  border: 1px solid var(--border);
  border-radius: calc(var(--radius) * 0.25);
  color: var(--text-3);
  white-space: nowrap;
}

/* ── Content ── */
.nx-content {
  grid-area: content;
  padding: var(--sp) calc(var(--sp) * 1.333);
  overflow-y: auto;
  position: relative;
  z-index: var(--z-base);
  /* removed margin-left — see nx-topbar comment */
  /* disable browser back/forward swipe-nav + stop swipe-down moving the shell (@deyan) */
  overscroll-behavior: none;
  touch-action: pan-y;
}

/* ═══════════════════════════════════════════════
   Responsive
   ═══════════════════════════════════════════════ */

/* Tablet: 768–1023 */
@media (max-width: 1023px) {
  .nx-content { padding: 20px 20px; /* fixed: @media tablet */ }
  .nx-bento { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
}

/* Mobile: <768 — DEFAULT: Always-visible 56px sidebar (D1) */
@media (max-width: 767px) {
  /* matched higher specificity from default rule above; no !important needed */
  .nx-topbar-btn.nx-side-mobile-toggle { display: flex; }

  /* Sidebar visible at 56px; .is-expanded expands to 240px as overlay */
  .nx-side {
    width: var(--side-collapsed);
    transform: none;
    z-index: var(--z-nav);
  }
  .nx-side.is-expanded {
    width: var(--side-expanded);
    background: rgba(10, 10, 14, 0.95);
    backdrop-filter: blur(40px) saturate(180%);
    -webkit-backdrop-filter: blur(40px) saturate(180%);
    box-shadow: 0 0 40px rgba(0, 0, 0, 0.5);
  }
  .nx-side.is-expanded .nx-side-item-label,
  .nx-side.is-expanded .nx-side-toggle span,
  .nx-side.is-expanded .nx-meta,
  .nx-side.is-expanded .nx-side-count,
  .nx-side.is-expanded .nx-side-footer > * { opacity: 1; }
  /* ensure labels + ☰ text reappear in flex flow when expanded */
  .nx-side.is-expanded .nx-side-item-label,
  .nx-side.is-expanded .nx-side-toggle span { display: inline; }
  /* Layout stays 56px + content (no full collapse)
     removed margin-left — grid reserves column 1 */
  .nx-content { padding: 16px; /* fixed: @media mobile */ }

  .nx-bento { grid-template-columns: 1fr; }

  /* D2: Mobile search becomes icon-only button */
  .nx-topbar-search {
    min-width: 0;
    width: calc(40px * var(--app-scale));
    height: calc(40px * var(--app-scale));
    padding: 0;
    justify-content: center;
  }
  .nx-topbar-search input,
  .nx-topbar-search kbd { display: none; }
}

/* ═══ Mobile sidebar mode overrides (UI Setting opt-in) ═══ */

/* Mode: Hidden — legacy behavior (sidebar fully offscreen until opened) */
@media (max-width: 767px) {
  .nx-app[data-mobile-side="hidden"] {
    grid-template-columns: 1fr;
    grid-template-areas:
      "topbar"
      "content";
  }
  .nx-app[data-mobile-side="hidden"]:has(.nx-focus) {
    grid-template-areas:
      "topbar"
      "focus"
      "content";
  }
  [data-mobile-side="hidden"] .nx-side {
    width: var(--side-expanded);
    transform: translateX(-100%);
    transition: transform .3s cubic-bezier(.2,0,.1,1);
  }
  [data-mobile-side="hidden"] .nx-side.is-expanded {
    transform: translateX(0);
  }
  [data-mobile-side="hidden"] .nx-topbar,
  [data-mobile-side="hidden"] .nx-content,
  [data-mobile-side="hidden"] .nx-focus { margin-left: 0; }
}

/* Mode: Bottom-tabs — skeleton (sidebar hidden, bottom nav visible) */
@media (max-width: 767px) {
  .nx-app[data-mobile-side="bottom-tabs"] {
    grid-template-columns: 1fr;
    grid-template-areas:
      "topbar"
      "content";
  }
  [data-mobile-side="bottom-tabs"] .nx-side { display: none; }
  /* nx-bottom-nav rule removed v2.X.30 — feature was opt-in but never wired. */
  [data-mobile-side="bottom-tabs"] .nx-topbar,
  [data-mobile-side="bottom-tabs"] .nx-content { margin-left: 0; }
  /* padding-bottom block for nx-bottom-nav removed — feature unwired */
}

/* ══════════════════════════════════════════════════════════════════════════
   v2.X.0 — Motion preference (D8A2-b)
   Per Settings → Motion: full / reduced / none.
   Respects prefers-reduced-motion as system fallback when no user choice.
   ══════════════════════════════════════════════════════════════════════════ */
.nx-app[data-motion="reduced"] *,
.nx-app[data-motion="reduced"] *::before,
.nx-app[data-motion="reduced"] *::after {
  animation-duration: 0.001ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.001ms !important;
  scroll-behavior: auto !important;
}
.nx-app[data-motion="none"] *,
.nx-app[data-motion="none"] *::before,
.nx-app[data-motion="none"] *::after {
  animation: none !important;
  transition: none !important;
}

/* ══════════════════════════════════════════════════════════════════════════
   v2.X.0 — nx-info-banner: MOVED to features/info-banner/feature.css
   (feature-bundle migration Step 2, 2026-05-26).
   App opt-in declarative via manifest.features: ["info-banner"].
   ══════════════════════════════════════════════════════════════════════════ */

/* ══════════════════════════════════════════════════════════════════════════
   v2.X.0 — Topbar user avatar + User menu dropdown:
   MOVED to features/user-menu/feature.css (feature-bundle migration Step 3,
   2026-05-26). Includes BOTH .nx-topbar-user (trigger) AND .nx-user-menu
   (panel) as one cohesive feature. App opt-in: manifest.features: ["user-menu"].
   ══════════════════════════════════════════════════════════════════════════ */
