/* ═══════════════════════════════════════════════════════════════
   VERSIONARY — "BE VERSIONARY" LOADER (v2)

   The Lottie is GONE. The mark is the morphing blob from the chat console,
   scaled to 76px and rendered in pure CSS — so the 22 KB JSON *and* the
   Lottie library both leave the critical path. The loader paints on the
   first frame instead of waiting on a fetch.

   Backdrop is a glassmorphic overlay ON TOP of the .auth-video-background
   element (already in DOM at z-index -3). The recipe matches the glass
   modal system — transparent tint + backdrop-filter blur — so the loader
   reads as the same surface language as the modals.

   Required IDs are preserved: #authLoadingScreen, #authLottieLoader,
   #authGreeting. See LOADER-HANDOFF.md.

   THEMING: dark is scoped to body.dark-mode, matching the rest of the app.
   The early inline paint script sets body.dark-mode based on the app's own
   time/preference check BEFORE loader.css is applied, so no theme flash.
   ═══════════════════════════════════════════════════════════════ */

/* ─────────── SHELL — glass over the .auth-video-background ─────────── */

#authLoadingScreen {
  position: fixed;
  inset: 0;
  z-index: 99999;                                /* above content, above modals */
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  opacity: 1;
  transition: opacity .45s ease;

  /* LIGHT (default) — warm-white frost over the light video poster. */
  background: rgba(255, 255, 255, 0.22);
  backdrop-filter: blur(20px) saturate(1.2);
  -webkit-backdrop-filter: blur(20px) saturate(1.2);
}
#authLoadingScreen.leaving { opacity: 0; pointer-events: none; }

/* DARK — slight dark tint over the dark video poster. Same modal recipe
   as .v-modal-overlay (rgba(20,16,12,.42) + blur 8), tuned darker to sit
   on top of a full-viewport video without washing it out. */
body.dark-mode #authLoadingScreen {
  background: rgba(20, 16, 12, 0.42);
  backdrop-filter: blur(20px) saturate(1.3);
  -webkit-backdrop-filter: blur(20px) saturate(1.3);
}

.auth-stack {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* Aurora removed — the video backdrop provides the atmospheric colour now,
   and the modal-glass shell + blob supply the loader's identity. The
   .auth-aurora div can stay in HTML (0×0 unstyled) or be removed. */
.auth-aurora { display: none; }

/* ─────────── THE BLOB (replaces the Lottie) ─────────── */
/* #authLottieLoader keeps its ID so nothing downstream breaks, but it is
   now a plain div with four children — no JSON, no library. */

#authLottieLoader {
  position: relative;
  width: 76px;
  height: 76px;
}

/* Outer halo — a soft colour bleed, morphing on its own slower clock */
#authLottieLoader .halo {
  position: absolute;
  inset: -13px;
  overflow: hidden;
  filter: blur(15px);
  opacity: .5;
  background: conic-gradient(from 0deg, #6c5ce7, #059669, #b45309, #2563eb, #6c5ce7);
  animation: morphB 6.5s ease-in-out infinite;
}
body.dark-mode #authLottieLoader .halo {
  opacity: .42;
  background: conic-gradient(from 0deg, #a78bfa, #34d399, #fbbf24, #60a5fa, #a78bfa);
}

/* The body */
#authLottieLoader .frost {
  position: absolute;
  inset: 0;
  overflow: hidden;
  animation: morphA 3.8s ease-in-out infinite;
}
/* Colour, blurred to a haze and rotating — reads as light trapped in glass
   rather than a painted gradient. NOTE: this rotates, it does not keyframe
   `background`. background-image cannot interpolate, so animating it makes
   the colour SNAP — that was the harshness in the first version. */
#authLottieLoader .conic {
  position: absolute;
  inset: -32%;
  filter: blur(13px);
  animation: hueSpin 12s linear infinite;
  background: conic-gradient(from 0deg, #6c5ce7, #059669, #b45309, #2563eb, #6c5ce7);
}
body.dark-mode #authLottieLoader .conic {
  background: conic-gradient(from 0deg, #a78bfa, #34d399, #fbbf24, #60a5fa, #a78bfa);
}

/* Frosted skin — 30% white on cream stays legible as glass; 9% on dark. */
#authLottieLoader .skin {
  position: absolute;
  inset: 0;
  z-index: 5;
  background: rgba(255, 255, 255, .30);
  backdrop-filter: blur(6px) saturate(1.35);
  -webkit-backdrop-filter: blur(6px) saturate(1.35);
}
body.dark-mode #authLottieLoader .skin {
  background: rgba(255, 255, 255, .09);
  backdrop-filter: blur(5px) saturate(1.6);
  -webkit-backdrop-filter: blur(5px) saturate(1.6);
}

/* Drifting highlight */
#authLottieLoader .spec {
  position: absolute;
  width: 62px; height: 42px;
  border-radius: 50%;
  z-index: 8;
  filter: blur(11px);
  background: radial-gradient(50% 50% at 50% 50%, rgba(255, 255, 255, .85), transparent 70%);
  animation: specular 7s ease-in-out infinite;
}
body.dark-mode #authLottieLoader .spec {
  background: radial-gradient(50% 50% at 50% 50%, rgba(255, 255, 255, .62), transparent 70%);
}

/* Rim light — dark is lit above and shadowed below; light needs the shade
   on TOP and the lift underneath (flipping alphas alone reads concave). */
#authLottieLoader .rim {
  position: absolute;
  inset: 0;
  z-index: 10;
  pointer-events: none;
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, .9),
              inset 0 -1px 1px rgba(0, 0, 0, .06),
              inset 0 -16px 26px rgba(28, 27, 31, .14);
}
body.dark-mode #authLottieLoader .rim {
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, .5),
              inset 0 -1px 1px rgba(255, 255, 255, .18),
              inset 0 -18px 30px rgba(0, 0, 0, .3);
}

/* Cast shadow — light only. On black the blob is a light source and needs
   nothing beneath it; on cream, without this it looks pasted on. */
#authLottieLoader::after {
  content: '';
  position: absolute;
  left: 12%; right: 12%; bottom: -14px; height: 16px;
  border-radius: 50%;
  filter: blur(11px);
  z-index: 0;
  background: radial-gradient(50% 50% at 50% 50%, rgba(28, 27, 31, .20), transparent 72%);
}
body.dark-mode #authLottieLoader::after { display: none; }

@keyframes morphA {
  0%   { border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%; }
  25%  { border-radius: 30% 60% 70% 40% / 50% 60% 30% 60%; }
  50%  { border-radius: 50% 60% 30% 60% / 30% 50% 70% 50%; }
  75%  { border-radius: 60% 30% 60% 40% / 70% 40% 50% 60%; }
  100% { border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%; }
}
@keyframes morphB {
  0%   { border-radius: 40% 60% 55% 45% / 45% 55% 45% 55%; }
  33%  { border-radius: 65% 35% 40% 60% / 60% 40% 60% 40%; }
  66%  { border-radius: 35% 65% 60% 40% / 55% 60% 40% 45%; }
  100% { border-radius: 40% 60% 55% 45% / 45% 55% 45% 55%; }
}
@keyframes hueSpin { to { transform: rotate(360deg); } }
@keyframes specular {
  0%, 100% { transform: translate(-24%, -30%); opacity: .5; }
  50%      { transform: translate(22%, 18%);   opacity: .85; }
}

/* ─────────── GREETING ─────────── */

#authGreeting {
  margin-top: 24px;
  white-space: nowrap;                  /* single line, always */
  font: 500 19px 'JetBrains Mono', ui-monospace, monospace;
  letter-spacing: -.02em;
  animation: wipeIn .8s .3s cubic-bezier(.5, 0, .2, 1) both;
}
@keyframes wipeIn {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}

/* Prompt glyphs — light values (darker so they read on cream) */
#authGreeting .g-prompt { color: rgba(108, 92, 231, .85); }
#authGreeting .g-path   { color: rgba(37, 99, 235, .85); }
#authGreeting .g-flag   { color: rgba(28, 27, 31, .34); }
#authGreeting .g-body   { color: rgba(28, 27, 31, .55); }
#authGreeting .g-brack  { color: rgba(28, 27, 31, .30); }
#authGreeting .g-ok     { color: rgba(5, 150, 105, .9); }
#authGreeting .g-caret  { color: #6c5ce7; animation: caret 1s step-end infinite; }

/* Dark overrides */
body.dark-mode #authGreeting .g-prompt { color: rgba(167, 139, 250, .7); }
body.dark-mode #authGreeting .g-path   { color: rgba(96, 165, 250, .7); }
body.dark-mode #authGreeting .g-flag   { color: rgba(255, 255, 255, .32); }
body.dark-mode #authGreeting .g-body   { color: rgba(255, 255, 255, .55); }
body.dark-mode #authGreeting .g-brack  { color: rgba(255, 255, 255, .28); }
body.dark-mode #authGreeting .g-ok     { color: rgba(52, 211, 153, .8); }
body.dark-mode #authGreeting .g-caret  { color: #a78bfa; }

/* The name — palette sweeps through it. Light palette darker for cream;
   dark palette lighter for the video-backed dark ground. */
#authGreeting .g-name {
  font-weight: 700;
  background: linear-gradient(90deg, #6c5ce7, #059669, #b45309, #2563eb, #6c5ce7);
  background-size: 200% auto;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  animation: gradSweep 7s linear infinite;
}
body.dark-mode #authGreeting .g-name {
  background: linear-gradient(90deg, #a78bfa, #34d399, #fbbf24, #60a5fa, #a78bfa);
  background-size: 200% auto;
  /* MUST re-declare background-clip here — the `background:` shorthand
     above RESETS all background-* longhands including background-clip back
     to `border-box`. Without this, the gradient fills the .g-name box as
     a solid rectangle instead of being clipped to the text shape. */
  -webkit-background-clip: text;
  background-clip: text;
}
@keyframes gradSweep {
  0%   { background-position: 0% 50%; }
  100% { background-position: 200% 50%; }
}

@keyframes caret { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

/* ─────────── MESSAGE PILL ─────────── */

/* Glass pill — lighter than its cream ground in light, subtle in dark. */
.auth-pill {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  height: 28px;
  padding: 0 14px;
  margin-top: 18px;
  border-radius: 14px;
  background: rgba(255, 255, 255, .5);
  border: 1px solid rgba(0, 0, 0, .07);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
body.dark-mode .auth-pill {
  background: rgba(255, 255, 255, .05);
  border-color: rgba(255, 255, 255, .07);
}

.auth-pill .dot {
  width: 5px; height: 5px;
  border-radius: 50%;
  flex-shrink: 0;
  background: #6c5ce7;
  box-shadow: 0 0 8px rgba(108, 92, 231, .5);
  animation: pillPulse 2s ease-in-out infinite;
}
body.dark-mode .auth-pill .dot {
  background: #a78bfa;
  box-shadow: 0 0 8px rgba(167, 139, 250, .9);
}
@keyframes pillPulse { 0%, 100% { opacity: .45; } 50% { opacity: 1; } }

/* FIXED WIDTH is load-bearing: the pill is centred, so a content-sized box
   would re-centre on every message swap and make the dot drift sideways.
   212px fits the longest line in every voice at this size. */
.auth-pill .msg {
  font: 400 11px 'JetBrains Mono', ui-monospace, monospace;
  letter-spacing: .05em;
  color: rgba(28, 27, 31, .5);
  white-space: nowrap;
  width: 212px;
  display: inline-block;
  transition: opacity .3s;
}
body.dark-mode .auth-pill .msg { color: rgba(255, 255, 255, .45); }

@media (prefers-reduced-motion: reduce) {
  #authLottieLoader .halo,
  #authLottieLoader .frost,
  #authLottieLoader .conic,
  #authLottieLoader .spec,
  .ve-blob .halo,
  .ve-blob .frost,
  .ve-blob .conic,
  .ve-blob .spec,
  .auth-pill .dot,
  #authGreeting .g-name { animation: none; }
  #authGreeting { animation: none; clip-path: none; }
  #authGreeting .g-name {
    -webkit-text-fill-color: #1c1b1f;
    color: #1c1b1f;
    background: none;
  }
  body.dark-mode #authGreeting .g-name {
    -webkit-text-fill-color: #fff;
    color: #fff;
  }
}


/* ═══════════════════════════════════════════════════════════════
   .ve-blob — reusable blob loader (batch processing, etc.)

   Same visual language as the auth loader's #authLottieLoader — six
   layers (halo + frost > conic/skin/spec/rim), same keyframes, same
   theme scoping. Container is sized by inline style or a wrapper's
   width/height (default 50px if unstyled).

   Use:
     <div class="ve-blob" style="width:50px;height:50px;">
       <div class="halo"></div>
       <div class="frost">
         <div class="conic"></div>
         <div class="skin"></div>
         <div class="spec"></div>
         <div class="rim"></div>
       </div>
     </div>

   The keyframes (morphA, morphB, hueSpin, specular) are defined
   above in the auth loader block — .ve-blob reuses them.
   ═══════════════════════════════════════════════════════════════ */

.ve-blob {
  position: relative;
  width: 50px;
  height: 50px;
}
.ve-blob .halo {
  position: absolute;
  inset: -13%;
  overflow: hidden;
  filter: blur(10px);
  opacity: .5;
  background: conic-gradient(from 0deg, #6c5ce7, #059669, #b45309, #2563eb, #6c5ce7);
  /* Default organic shape — morphB animation overrides when running. Without
     this default, the halo renders as a hard rectangle at the first frame
     AND for users with prefers-reduced-motion (where animation is disabled). */
  border-radius: 40% 60% 55% 45% / 45% 55% 45% 55%;
  animation: morphB 6.5s ease-in-out infinite;
}
body.dark-mode .ve-blob .halo {
  opacity: .42;
  background: conic-gradient(from 0deg, #a78bfa, #34d399, #fbbf24, #60a5fa, #a78bfa);
}
.ve-blob .frost {
  position: absolute;
  inset: 0;
  overflow: hidden;
  /* Default organic shape — morphA animation overrides when running. Critical:
     the .rim child inside frost has inset box-shadows that reveal the frost's
     border-radius shape via overflow:hidden clipping. If frost is a hard
     rectangle (no morphA running), the rim's shadows look boxy — the "square
     blob" bug the loader had on prod. */
  border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%;
  animation: morphA 3.8s ease-in-out infinite;
}
.ve-blob .conic {
  position: absolute;
  inset: -32%;
  filter: blur(9px);
  animation: hueSpin 12s linear infinite;
  background: conic-gradient(from 0deg, #6c5ce7, #059669, #b45309, #2563eb, #6c5ce7);
}
body.dark-mode .ve-blob .conic {
  background: conic-gradient(from 0deg, #a78bfa, #34d399, #fbbf24, #60a5fa, #a78bfa);
}
.ve-blob .skin {
  position: absolute;
  inset: 0;
  z-index: 5;
  /* A parent's overflow:hidden + border-radius does NOT clip a descendant's
     backdrop-filter in Chrome — the filtered region paints as a hard
     rectangle regardless. That is the glass square that sat over the blob.
     `inherit` takes .frost's COMPUTED radius, so it tracks morphA frame by
     frame instead of needing its own animation. */
  border-radius: inherit;
  background: rgba(255, 255, 255, .30);
  backdrop-filter: blur(4px) saturate(1.35);
  -webkit-backdrop-filter: blur(4px) saturate(1.35);
}
body.dark-mode .ve-blob .skin {
  background: rgba(255, 255, 255, .09);
  backdrop-filter: blur(3px) saturate(1.6);
  -webkit-backdrop-filter: blur(3px) saturate(1.6);
}
.ve-blob .spec {
  position: absolute;
  width: 62%; height: 42%;
  border-radius: 50%;
  z-index: 8;
  filter: blur(7px);
  background: radial-gradient(50% 50% at 50% 50%, rgba(255, 255, 255, .85), transparent 70%);
  animation: specular 7s ease-in-out infinite;
}
body.dark-mode .ve-blob .spec {
  background: radial-gradient(50% 50% at 50% 50%, rgba(255, 255, 255, .62), transparent 70%);
}
.ve-blob .rim {
  position: absolute;
  inset: 0;
  z-index: 10;
  pointer-events: none;
  /* Same reason: inset shadows are drawn to this element's own box, so
     without a radius they read as a square edge inside the round blob. */
  border-radius: inherit;
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, .9),
              inset 0 -1px 1px rgba(0, 0, 0, .06),
              inset 0 -12px 20px rgba(28, 27, 31, .14);
}
body.dark-mode .ve-blob .rim {
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, .5),
              inset 0 -1px 1px rgba(255, 255, 255, .18),
              inset 0 -14px 22px rgba(0, 0, 0, .3);
}
