/* ============================================================
   NOVA BOOT — the arrival animation (nova-boot.css)

   WHAT THIS REPLACES
   ------------------
   Nova Assistant's first load used to run the shared shell's first-load
   reveal: layout.css gates `.rise` / `.stagger > *` to "first load only" (they
   are neutralised once `body.nav-ready` lands), so on arrival every element in
   the view animated in on its own staggered delay. On a dense three-pane chat —
   sidebar rows, pinned chats, project rows, greeting, chips, composer — that is
   dozens of independently-timed pop-ins firing over each other while data is
   still streaming in and re-rendering rows mid-animation. Vincent: "instead of
   this weird glitching animation — because I think it's coming from something
   else". He was right that it wasn't the chat view: it was the shell.

   So on this product that cascade is switched OFF (app.js marks the body
   nav-ready before first paint) and this ONE deliberate animation runs instead.

   THE ANIMATION
   -------------
   The Nova spark, cut into horizontal slices — its own silhouette, sliced. The
   middle rung appears first, then the pair above and below it, then the next
   pair, working outward from the centre; the slices then resolve into the whole
   mark, and the wordmark rises under it. "From the middle… like the ladders
   appear one after another… you can just do that by cutting up the SVG in
   different forms."

   Transform + opacity only, so it is GPU-cheap and cannot thrash layout. The
   whole thing is ~1.3s and it runs ONCE PER TAB (sessionStorage) — an intro you
   sit through on every navigation is a tax, not a brand moment.
   ============================================================ */

.nvb {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: grid;
  place-items: center;
  background: var(--bg, #0b0b0c);
  /* The overlay owns the screen while it plays; nothing behind it is clickable
     and nothing behind it is announced. */
  pointer-events: all;
  opacity: 1;
  transition: opacity 420ms var(--ease-out, cubic-bezier(0.22, 1, 0.36, 1));
}
.nvb.is-out {
  opacity: 0;
  pointer-events: none;
}

.nvb-stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 26px;
}

/* ---- the mark ---- */
.nvb-mark {
  width: 92px;
  height: 92px;
  display: block;
  color: var(--text-100, #fff);
  overflow: visible;
}

/* Each rung scales out from its own centre. transform-box/transform-origin so
   the origin is the RECT's box, not the SVG's — without both, every rung would
   grow from the top-left of the viewBox and the whole thing would fan sideways
   instead of opening from the middle. */
.nvb-rung {
  transform-box: fill-box;
  transform-origin: center;
  transform: scaleX(0.06);
  opacity: 0;
  animation: nvb-rung 300ms var(--ease-out, cubic-bezier(0.22, 1, 0.36, 1)) both;
  animation-delay: var(--d, 0ms);
}
@keyframes nvb-rung {
  from { transform: scaleX(0.06); opacity: 0; }
  60%  { opacity: 1; }
  to   { transform: scaleX(1); opacity: 1; }
}

/* The slices hand over to the whole mark: they fade as it arrives, so for a few
   frames you see the sliced form BECOMING the solid one rather than one thing
   swapping for another. */
.nvb-slices {
  animation: nvb-slices-out 260ms var(--ease-out, cubic-bezier(0.22, 1, 0.36, 1)) both;
  animation-delay: 640ms;
}
@keyframes nvb-slices-out {
  from { opacity: 1; transform: scale(1); }
  to   { opacity: 0; transform: scale(1.04); }
}

.nvb-solid {
  transform-box: fill-box;
  transform-origin: center;
  opacity: 0;
  animation: nvb-solid-in 460ms var(--ease-out, cubic-bezier(0.22, 1, 0.36, 1)) both;
  animation-delay: 620ms;
}
@keyframes nvb-solid-in {
  from { opacity: 0; transform: scale(0.9); }
  to   { opacity: 1; transform: scale(1); }
}

/* ---- the wordmark ---- */
.nvb-word {
  display: block;
  height: 26px;
  width: auto;
  opacity: 0;
  animation: nvb-word-in 460ms var(--ease-out, cubic-bezier(0.22, 1, 0.36, 1)) both;
  animation-delay: 780ms;
}
@keyframes nvb-word-in {
  from { opacity: 0; transform: translateY(7px); }
  to   { opacity: 1; transform: none; }
}
/* One wordmark per theme — same pair the rail lockup uses, so the intro and the
   app agree about which mark this product wears. The classes are named after the
   INK the file carries (white/black), not after the theme it belongs to: the
   first version called them -light/-dark, read as theme names, and showed black
   ink on the dark background — an invisible wordmark. */
[data-theme='light'] .nvb-word-white { display: none; }
[data-theme='dark'] .nvb-word-black,
:root:not([data-theme='light']) .nvb-word-black { display: none; }

/* .nvb-solid animates a CSS transform, so its own placement CANNOT live on the
   same node (see nova-boot.js) — this rule must never grow a `transform` that
   is meant to position the mark. */


/* ---- reduced motion ----
   Not "a faster animation" — no animation. The overlay still appears so the
   arrival is not a bare flash of an unpainted shell, but everything is in its
   final state immediately and app.js shortens the hold to match. */
@media (prefers-reduced-motion: reduce) {
  .nvb-rung,
  .nvb-slices,
  .nvb-solid,
  .nvb-word { animation: none; opacity: 1; transform: none; }
  .nvb-slices { display: none; }
  .nvb { transition: none; }
}
