/* ============================================================
   ✨ nexus-motion.css — Motion Layer
   
   Loaded last, so it layers on top of style.css without editing it.
   Nothing here changes layout, colour or structure — only how things
   arrive, respond and settle.
   
   Three acts:
     1. BOOT      the first second, before anything is interactive
     2. AUTH      sign in / sign up
     3. APP       everything after the door opens
   
   Timing follows one rule: entrances are unhurried (500–700ms) so they
   read as intentional, responses are immediate (120–180ms) so the app
   never feels sluggish. Nothing loops unless it signals live state.
   ============================================================ */

/* ─── Motion vocabulary ──────────────────────────────────── */
:root {
  /* Decelerate hard — objects arrive fast then settle. The signature
     curve for anything entering the screen. */
  --m-enter:   cubic-bezier(.16, 1, .3, 1);
  /* Slight overshoot — for things that should feel physical. */
  --m-spring:  cubic-bezier(.34, 1.4, .64, 1);
  /* Symmetric — for state changes that are not arrivals. */
  --m-swap:    cubic-bezier(.65, 0, .35, 1);
  /* Fast out — for dismissals. */
  --m-exit:    cubic-bezier(.4, 0, 1, 1);

  --m-instant: 120ms;
  --m-quick:   180ms;
  --m-normal:  320ms;
  --m-slow:    560ms;
  --m-stage:   720ms;
}

/* ════════════════════════════════════════════════════════════
   ACT 1 — BOOT
   
   There was no boot sequence: the auth screen simply appeared.
   This gives the first second a shape — the field lights, the mark
   resolves, then the form arrives.
   ════════════════════════════════════════════════════════════ */

/* The backdrop lifts out of black rather than cutting to it. */
#auth-overlay {
  animation: nmBootField 900ms var(--m-enter) both;
}

@keyframes nmBootField {
  0%   { opacity: 0; }
  100% { opacity: 1; }
}

/* style.css already places two blurred orbs on #auth-overlay's
   ::before and ::after. Overwriting them removed an existing effect,
   so the boot sequence composes with them instead: the orbs are
   already animating, they simply had no entrance. */
#auth-overlay::before,
#auth-overlay::after {
  animation:
    orb 8s ease-in-out infinite alternate,
    nmOrbIn 1400ms var(--m-enter) 200ms both;
}

/* ::after carries a -4s delay in the base sheet so the two orbs drift
   out of phase. Re-declaring the shorthand above resets it, so it is
   restored here — without this both orbs would move in lockstep. */
#auth-overlay::after {
  animation-delay: -4s, 340ms;
}

@keyframes nmOrbIn {
  0%   { opacity: 0; }
  100% { opacity: 1; }
}

/* ════════════════════════════════════════════════════════════
   ACT 2 — AUTH
   
   The card's children arrive in reading order rather than all at once,
   so the eye is led from the mark down to the button.
   ════════════════════════════════════════════════════════════ */

.auth-card {
  animation: nmCardIn var(--m-stage) var(--m-enter) 200ms both;
}

@keyframes nmCardIn {
  0%   { opacity: 0; transform: translateY(16px) scale(.985); filter: blur(6px); }
  100% { opacity: 1; transform: translateY(0)    scale(1);    filter: blur(0); }
}

/* Stagger: brand → title → subtitle → tabs → email → password → button.
   80ms apart is enough to read as sequence without feeling slow. */
.auth-card > * {
  animation: nmRise 520ms var(--m-enter) both;
}
.auth-card > .auth-brand   { animation-delay: 320ms; }
.auth-card > .auth-title   { animation-delay: 400ms; }
.auth-card > .auth-sub     { animation-delay: 460ms; }
.auth-card > .auth-view    { animation-delay: 540ms; }

@keyframes nmRise {
  0%   { opacity: 0; transform: translateY(10px); }
  100% { opacity: 1; transform: translateY(0); }
}

/* The logo settles with a quarter turn as it lands. */
.auth-brand img {
  animation: nmMarkIn 900ms var(--m-spring) 320ms both;
}

@keyframes nmMarkIn {
  0%   { opacity: 0; transform: rotate(-90deg) scale(.6); }
  100% { opacity: 1; transform: rotate(0)      scale(1); }
}

/* The wordmark's gradient drifts once as it appears, then rests. */
.auth-brand-name {
  background-size: 200% auto;
  animation: nmRise 520ms var(--m-enter) 400ms both,
             nmGradientSettle 2200ms var(--m-swap) 400ms both;
}

@keyframes nmGradientSettle {
  0%   { background-position: 100% center; }
  100% { background-position: 0% center; }
}

/* ─── Tabs ───────────────────────────────────────────────── */
/* The active tab is tracked by a bar rather than a hard swap. */
.auth-tabs { position: relative; }

.auth-tab {
  position: relative;
  z-index: 1;
  transition:
    color var(--m-quick) var(--m-swap),
    transform var(--m-instant) var(--m-swap);
}
.auth-tab:active { transform: scale(.97); }

.auth-tab.active::after {
  content: "";
  position: absolute;
  left: 14%;
  right: 14%;
  bottom: -3px;
  height: 2px;
  border-radius: 2px;
  background: var(--grad);
  animation: nmTabBar 420ms var(--m-spring) both;
}

@keyframes nmTabBar {
  0%   { transform: scaleX(0); opacity: 0; }
  100% { transform: scaleX(1); opacity: 1; }
}

/* Switching tabs re-runs the field entrance so the form feels rebuilt
   rather than relabelled. Toggled by the existing switchTab(). */
#auth-email,
#auth-password {
  transition:
    border-color var(--m-quick) var(--m-swap),
    background   var(--m-quick) var(--m-swap),
    box-shadow   var(--m-normal) var(--m-enter);
}

.auth-input:focus {
  border-color: #35f1c655;
  box-shadow: 0 0 0 3px #35f1c612;
}

/* ─── Primary action ─────────────────────────────────────── */
.auth-btn {
  transition:
    transform var(--m-instant) var(--m-spring),
    box-shadow var(--m-quick) var(--m-enter),
    filter var(--m-quick) var(--m-swap);
}
.auth-btn:hover  { transform: translateY(-1px); box-shadow: 0 8px 24px #35f1c625; }
.auth-btn:active { transform: translateY(0) scale(.985); }

/* A light passes across the button once on arrival, drawing the eye to
   the thing the user is meant to press. */
.auth-btn::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg, transparent 38%, #ffffff38 50%, transparent 62%);
  transform: translateX(-120%);
  animation: nmSweep 1100ms var(--m-swap) 1100ms both;
  pointer-events: none;
}

@keyframes nmSweep {
  to { transform: translateX(120%); }
}

/* While a request is in flight the button breathes instead of freezing. */
.auth-btn:disabled {
  animation: nmPending 1300ms ease-in-out infinite;
  cursor: progress;
}

@keyframes nmPending {
  0%, 100% { filter: brightness(1)   saturate(1); }
  50%      { filter: brightness(.82) saturate(.75); }
}

/* Errors shake once. A loop would be punitive. */
.auth-error:not(:empty) {
  animation: nmShake 420ms var(--m-swap) both;
}

@keyframes nmShake {
  0%, 100%    { transform: translateX(0); }
  18%, 58%    { transform: translateX(-5px); }
  38%, 78%    { transform: translateX(5px); }
}

/* ════════════════════════════════════════════════════════════
   ACT 3 — APP
   
   After sign-in the shell arrives in layers: chrome first, then the
   content it frames.
   ════════════════════════════════════════════════════════════ */

#app {
  animation: nmAppIn var(--m-slow) var(--m-enter) both;
}

@keyframes nmAppIn {
  0%   { opacity: 0; transform: scale(1.012); filter: blur(4px); }
  100% { opacity: 1; transform: scale(1);     filter: blur(0); }
}

#sidebar  { animation: nmSlideL 620ms var(--m-enter) 80ms both; }
#topbar   { animation: nmDrop   540ms var(--m-enter) 140ms both; }

@keyframes nmSlideL {
  0%   { opacity: 0; transform: translateX(-18px); }
  100% { opacity: 1; transform: translateX(0); }
}
@keyframes nmDrop {
  0%   { opacity: 0; transform: translateY(-10px); }
  100% { opacity: 1; transform: translateY(0); }
}

/* Sidebar entries cascade rather than appearing as a block. */
.sb-nav-btn {
  animation: nmRise 380ms var(--m-enter) both;
  transition:
    background var(--m-instant) var(--m-swap),
    color      var(--m-instant) var(--m-swap),
    transform  var(--m-instant) var(--m-spring);
}
.sb-nav-btn:hover  { transform: translateX(2px); }
.sb-nav-btn:active { transform: translateX(2px) scale(.99); }

.sb-nav-btn:nth-child(1)  { animation-delay: 200ms; }
.sb-nav-btn:nth-child(2)  { animation-delay: 224ms; }
.sb-nav-btn:nth-child(3)  { animation-delay: 248ms; }
.sb-nav-btn:nth-child(4)  { animation-delay: 272ms; }
.sb-nav-btn:nth-child(5)  { animation-delay: 296ms; }
.sb-nav-btn:nth-child(6)  { animation-delay: 320ms; }
.sb-nav-btn:nth-child(7)  { animation-delay: 340ms; }
.sb-nav-btn:nth-child(8)  { animation-delay: 356ms; }
.sb-nav-btn:nth-child(9)  { animation-delay: 370ms; }
.sb-nav-btn:nth-child(n+10) { animation-delay: 380ms; }

/* ─── The new shell ──────────────────────────────────────── */
.nxs-rail {
  animation: nmSlideL 620ms var(--m-enter) both;
}

.nxs-rail-logo {
  animation: nmMarkIn 820ms var(--m-spring) 120ms both;
}

.nxs-rail-btn {
  animation: nmRise 420ms var(--m-enter) both;
}
.nxs-rail-btn:nth-of-type(1) { animation-delay: 180ms; }
.nxs-rail-btn:nth-of-type(2) { animation-delay: 230ms; }
.nxs-rail-btn:nth-of-type(3) { animation-delay: 280ms; }
.nxs-rail-btn:nth-of-type(4) { animation-delay: 330ms; }
.nxs-rail-btn:nth-of-type(5) { animation-delay: 380ms; }
.nxs-rail-btn:nth-of-type(6) { animation-delay: 430ms; }
.nxs-rail-btn:nth-of-type(n+7) { animation-delay: 470ms; }

/* The active area is marked by a bar that grows in, not a colour flip. */
.nxs-rail-btn::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 2px;
  height: 0;
  border-radius: 0 2px 2px 0;
  background: var(--grad);
  transform: translateY(-50%);
  transition: height var(--m-normal) var(--m-spring);
}
.nxs-rail-btn.active::before { height: 22px; }

/* Drawer items cascade on open. */
.nxs-drawer.open .nxs-item {
  animation: nmItemIn 360ms var(--m-enter) both;
}
.nxs-drawer.open .nxs-item:nth-child(1) { animation-delay: 40ms; }
.nxs-drawer.open .nxs-item:nth-child(2) { animation-delay: 68ms; }
.nxs-drawer.open .nxs-item:nth-child(3) { animation-delay: 92ms; }
.nxs-drawer.open .nxs-item:nth-child(4) { animation-delay: 112ms; }
.nxs-drawer.open .nxs-item:nth-child(5) { animation-delay: 128ms; }
.nxs-drawer.open .nxs-item:nth-child(n+6) { animation-delay: 140ms; }

@keyframes nmItemIn {
  0%   { opacity: 0; transform: translateX(-8px); }
  100% { opacity: 1; transform: translateX(0); }
}

/* ─── Home hero ──────────────────────────────────────────── */
.nxs-home-eyebrow { animation: nmRise 520ms var(--m-enter) 120ms both; }
.nxs-home-h       { animation: nmHeroIn 760ms var(--m-enter) 180ms both; }
.nxs-home-sub     { animation: nmRise 520ms var(--m-enter) 280ms both; }
.nxs-input-wrap   { animation: nmHeroIn 700ms var(--m-enter) 340ms both; }
.nxs-ex           { animation: nmRise 520ms var(--m-enter) 440ms both; }
.nxs-cards        { animation: nmRise 560ms var(--m-enter) 520ms both; }

@keyframes nmHeroIn {
  0%   { opacity: 0; transform: translateY(14px) scale(.99); filter: blur(4px); }
  100% { opacity: 1; transform: translateY(0)    scale(1);   filter: blur(0); }
}

.nxs-home-h b {
  background-size: 200% auto;
  animation: nmGradientSettle 2600ms var(--m-swap) 400ms both;
}

.nxs-ex-chip {
  transition:
    background var(--m-quick) var(--m-swap),
    border-color var(--m-quick) var(--m-swap),
    color var(--m-quick) var(--m-swap),
    transform var(--m-instant) var(--m-spring);
}
.nxs-ex-chip:hover  { transform: translateY(-1px); }
.nxs-ex-chip:active { transform: translateY(0) scale(.97); }

.nxs-card {
  transition:
    border-color var(--m-normal) var(--m-enter),
    transform    var(--m-normal) var(--m-enter),
    box-shadow   var(--m-normal) var(--m-enter);
}
.nxs-card:active { transform: translateY(-1px) scale(.995); }

/* ─── Orchestration run ──────────────────────────────────── */
/* Each step arrives as it is queued, so the plan builds visibly. */
.nxo-step {
  animation: nmItemIn 400ms var(--m-enter) both;
}
.nxo-step:nth-child(1)  { animation-delay: 30ms; }
.nxo-step:nth-child(2)  { animation-delay: 58ms; }
.nxo-step:nth-child(3)  { animation-delay: 82ms; }
.nxo-step:nth-child(4)  { animation-delay: 102ms; }
.nxo-step:nth-child(5)  { animation-delay: 118ms; }
.nxo-step:nth-child(6)  { animation-delay: 132ms; }
.nxo-step:nth-child(n+7){ animation-delay: 144ms; }

/* The running step carries a travelling highlight — the one loop in
   this file that is justified, because it signals live work. */
.nxo-step.running {
  position: relative;
  overflow: hidden;
}
.nxo-step.running::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg, transparent 40%, #35f1c60f 50%, transparent 60%);
  animation: nmSweepLoop 1900ms var(--m-swap) infinite;
  pointer-events: none;
}

@keyframes nmSweepLoop {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(100%); }
}

/* A completed step settles rather than blinking off. */
.nxo-step.done { transition: opacity var(--m-slow) var(--m-enter); }

/* The approval gate should read as a stop. It arrives with weight. */
.nxo-gate {
  animation: nmGateIn 620ms var(--m-spring) both;
}

@keyframes nmGateIn {
  0%   { opacity: 0; transform: translateY(12px) scale(.97); }
  60%  { opacity: 1; }
  100% { opacity: 1; transform: translateY(0)    scale(1); }
}

/* ─── Project workspace ──────────────────────────────────── */
.nxp-step {
  animation: nmItemIn 360ms var(--m-enter) both;
}
.nxp-step:nth-child(1) { animation-delay: 24ms; }
.nxp-step:nth-child(2) { animation-delay: 46ms; }
.nxp-step:nth-child(3) { animation-delay: 64ms; }
.nxp-step:nth-child(4) { animation-delay: 78ms; }
.nxp-step:nth-child(n+5) { animation-delay: 90ms; }

.nxp-appr { animation: nmGateIn 560ms var(--m-spring) both; }

/* The progress bar fills smoothly instead of jumping between polls. */
.nxp-bar span,
.nxo-bar span {
  transition: width 700ms var(--m-enter);
}

.nxp-card {
  transition:
    border-color var(--m-normal) var(--m-enter),
    transform    var(--m-normal) var(--m-enter);
}
.nxp-card:active { transform: translateY(-1px) scale(.996); }

/* ─── Panels ─────────────────────────────────────────────── */
.nxui-panel {
  animation: nmPanelIn 620ms var(--m-enter) both;
}

@keyframes nmPanelIn {
  0%   { opacity: 0; transform: translateY(12px) scale(.994); filter: blur(3px); }
  100% { opacity: 1; transform: translateY(0)    scale(1);    filter: blur(0); }
}

.nxui-tab {
  transition:
    color var(--m-quick) var(--m-swap),
    border-color var(--m-quick) var(--m-swap);
}

.nxui-btn {
  transition:
    transform var(--m-instant) var(--m-spring),
    box-shadow var(--m-quick) var(--m-enter);
}
.nxui-btn:active { transform: translateY(0) scale(.985); }

.nxui-card {
  transition:
    border-color var(--m-normal) var(--m-enter),
    transform    var(--m-normal) var(--m-enter);
}

/* ─── Notifications ──────────────────────────────────────── */
.nx-bell {
  animation: nmDrop 520ms var(--m-enter) 400ms both;
  transition:
    background var(--m-quick) var(--m-swap),
    border-color var(--m-quick) var(--m-swap),
    transform var(--m-instant) var(--m-spring);
}
.nx-bell:active { transform: scale(.94); }

/* A new notification rings the badge once. */
.nx-bell-badge:not(.hidden) {
  animation: nmBadgeIn 520ms var(--m-spring) both;
}

@keyframes nmBadgeIn {
  0%   { transform: scale(0)   rotate(-25deg); opacity: 0; }
  60%  { transform: scale(1.18) rotate(6deg); opacity: 1; }
  100% { transform: scale(1)   rotate(0);     opacity: 1; }
}

.nx-notif-item {
  animation: nmItemIn 340ms var(--m-enter) both;
}
.nx-notif-item:nth-child(1) { animation-delay: 20ms; }
.nx-notif-item:nth-child(2) { animation-delay: 40ms; }
.nx-notif-item:nth-child(3) { animation-delay: 56ms; }
.nx-notif-item:nth-child(n+4) { animation-delay: 68ms; }

/* ─── Mode bar ───────────────────────────────────────────── */
.cine-mode-bar {
  animation: nmDrop 520ms var(--m-enter) 460ms both;
  transition: background var(--m-quick) var(--m-swap);
}

/* ════════════════════════════════════════════════════════════
   ACCESSIBILITY
   
   Everything above is decoration. When motion is unwelcome the
   interface must still be complete — so animations are removed, not
   shortened, and every element is left in its final state.
   ════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  #auth-overlay,
  #auth-overlay::before,
  #auth-overlay::after,
  .auth-card,
  .auth-card > *,
  .auth-brand img,
  .auth-brand-name,
  .auth-tab.active::after,
  .auth-btn::after,
  .auth-btn:disabled,
  .auth-error:not(:empty),
  #app, #sidebar, #topbar, .sb-nav-btn,
  .nxs-rail, .nxs-rail-logo, .nxs-rail-btn,
  .nxs-drawer.open .nxs-item,
  .nxs-home-eyebrow, .nxs-home-h, .nxs-home-h b, .nxs-home-sub,
  .nxs-input-wrap, .nxs-ex, .nxs-cards,
  .nxo-step, .nxo-step.running::after, .nxo-gate,
  .nxp-step, .nxp-appr,
  .nxui-panel,
  .nx-bell, .nx-bell-badge:not(.hidden), .nx-notif-item,
  .cine-mode-bar {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
  }

  .nxp-bar span,
  .nxo-bar span {
    transition: none !important;
  }
}

/* ── Low-power and small screens ──────────────────────────
   Blur is the most expensive property here. Dropping it keeps the
   choreography intact on hardware that would otherwise drop frames. */
@media (max-width: 600px) {
  .auth-card,
  #app,
  .nxs-home-h,
  .nxs-input-wrap,
  .nxui-panel {
    animation-name: nmRise;
    animation-duration: 460ms;
  }

  #auth-overlay::after { display: none; }
}

/* ════════════════════════════════════════════════
   INTERACTION STATES
   
   The base sheet styles the resting and hover states well, but the
   interface has 111 buttons and only two of them describe what
   disabled looks like. A control that looks identical whether or not
   it will respond is the most common source of "nothing happened".
   ════════════════════════════════════════════════ */

/* ── Disabled ────────────────────────────────────
   Dimming alone reads as low contrast rather than unavailable, so the
   cursor changes too and every interaction is suppressed. */
button:disabled,
input:disabled,
textarea:disabled,
select:disabled,
[aria-disabled="true"] {
  opacity: .45;
  cursor: not-allowed;
  pointer-events: none;
  filter: saturate(.6);
}

/* A control waiting on the network is not disabled — it is busy, and
   the distinction matters to anyone deciding whether to click again. */
[aria-busy="true"] {
  cursor: progress;
  pointer-events: none;
}

/* ── Invalid input ───────────────────────────────
   Only applied once the field has content, so an empty required field
   is not flagged before it has been filled in. */
input:not(:placeholder-shown):invalid,
textarea:not(:placeholder-shown):invalid {
  border-color: #ef444455;
}
input:not(:placeholder-shown):invalid:focus,
textarea:not(:placeholder-shown):invalid:focus {
  border-color: #ef4444aa;
  box-shadow: 0 0 0 3px #ef444415;
}

/* ── Placeholders ────────────────────────────────
   A placeholder that competes with real input is easy to misread as a
   filled field. */
::placeholder {
  color: var(--t3, #3d3d5c);
  opacity: 1;          /* Firefox dims placeholders by default */
}

/* ── Text handling ───────────────────────────────
   Headings look unbalanced when the last line holds a single word. */
h1, h2, h3,
.auth-title,
.page-title,
.nxs-home-h,
.nxui-title {
  text-wrap: balance;
}

/* AI output arrives as prose; paragraphs read better without ragged
   last lines, and long unbroken strings must not force a scrollbar. */
.msg-bubble,
.nxui-result,
.nxo-out {
  text-wrap: pretty;
  overflow-wrap: anywhere;
}

/* ── Numbers ─────────────────────────────────────
   Counters, timings and progress values shift horizontally as digits
   change unless the figures are fixed width. */
.nxo-meta,
.nxp-bar-meta,
.nxui-meta,
.nx-bell-badge,
.nxp-tile-n,
.nxs-card-d {
  font-variant-numeric: tabular-nums;
}

/* ── Empty states ────────────────────────────────
   An empty container that still reserves its padding reads as a
   loading failure. */
.nxui-list:empty,
.nxo-out:empty,
.auth-error:empty {
  display: none;
}

/* Selection is deliberately left to style.css. Its first definition
   (lime on white) was already corrected there by a later rule using
   --a1-40 with --text, so overriding it here would discard that fix
   rather than build on it. */