/* ============================================================
   zzz-bear-orb.css — 3D AI orb three-layer stack + chat tech feel
   ------------------------------------------------------------
   Layering inside .hp-bear-orbit-wrap:
     • .hp-bear-stage  — the persistent Three.js <canvas> host (z0, behind)
     • .hp-bear-layer  — orbit/chat DOM, swapped via innerHTML (z1, front)
   The stage is NEVER torn down on orbit↔chat swaps, so the same orb flows
   between states: forward in orbit, recessed behind the chat glass.

   When the orb actually mounts (.has-orb), the old CSS avatar puck is
   hidden so the live shader sphere shows through; without WebGL / with
   reduced-motion the puck stays as the graceful fallback.

   zzz- prefix keeps this late in the cascade, after homepage-daypage.css.
   ============================================================ */

/* ---- Three-layer stack ---------------------------------------------- */
.hp-bear-stage {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;   /* DOM layer above takes all clicks */
}

.hp-bear-layer {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* When the live orb is mounted, dissolve the fallback avatar puck so the
   shader sphere is what the eye sees. The button stays in the DOM as a
   transparent hit-area (and keyboard target) centred on the orb. */
.hp-bear-orbit-wrap.has-orb .hp-bear-avatar-core,
.hp-bear-orbit-wrap.has-orb .hp-bear-avatar-pulse {
  opacity: 0;
}
.hp-bear-orbit-wrap.has-orb .hp-bear-avatar-btn {
  background: transparent;
  /* enlarge the hit-area to roughly match the visible orb */
  width: 150px;
  height: 150px;
}
/* Keep the ONLINE status chip — it anchors the orb as "Bear, online". */
.hp-bear-orbit-wrap.has-orb .hp-bear-avatar-status {
  bottom: -2px;
}

/* ---- Chat: glassmorphism so the orb glow seeps through --------------- */
.hp-bear-orbit-wrap.has-orb .hp-bear-chat {
  background: rgba(255, 255, 255, 0.72);
  -webkit-backdrop-filter: blur(14px) saturate(1.1);
          backdrop-filter: blur(14px) saturate(1.1);
  border-color: rgba(214, 206, 192, 0.6);
}
body.dark .hp-bear-orbit-wrap.has-orb .hp-bear-chat {
  background: rgba(37, 34, 32, 0.7);
  border-color: rgba(61, 56, 48, 0.7);
}
/* The head/foot strips inherit the same translucency so the panel reads as
   one sheet of glass rather than stacked opaque bars. */
.hp-bear-orbit-wrap.has-orb .hp-bear-chat-head,
.hp-bear-orbit-wrap.has-orb .hp-bear-chat-quick,
.hp-bear-orbit-wrap.has-orb .hp-bear-chat-input-row,
.hp-bear-orbit-wrap.has-orb .hp-bear-chat-foot {
  background: transparent;
}

/* ---- Streaming message micro-motion --------------------------------- */
/* New messages rise + fade + scale in. Limit the enter animation to the
   LAST message only: renderChat() rebuilds the whole list via innerHTML, so
   animating every .hp-bear-chat-msg would replay the enter on the entire
   transcript on each rebuild — that, plus per-token re-renders, was the
   strobing. Streaming now patches the bubble in place (no rebuild), and only
   the freshest message animates in. */
.hp-bear-chat-msg:last-child {
  animation: hpMsgIn 0.32s cubic-bezier(0.22, 1, 0.36, 1) both;
}
/* The streaming bubble is patched in place, never re-created, so it must not
   carry an enter animation at all while it grows. */
.hp-bear-chat-bubble.is-streaming {
  animation: none;
}
@keyframes hpMsgIn {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  to   { opacity: 1; transform: none; }
}

/* The streaming reply gets a soft caret so it reads as "being typed" even
   between tokens. Added/removed by JS via .is-streaming on the bubble. */
.hp-bear-chat-bubble.is-streaming::after {
  content: '';
  display: inline-block;
  width: 2px;
  height: 1em;
  margin-left: 2px;
  vertical-align: -2px;
  background: var(--accent);
  animation: hpCaret 1s steps(1) infinite;
}
@keyframes hpCaret { 50% { opacity: 0; } }

/* ---- Input: flowing glow on focus ("awaiting input") ----------------- */
.hp-bear-chat-input-row {
  position: relative;
}
.hp-bear-chat-input:focus {
  /* A breathing amber halo reads as "alive, listening" without the extra
     wrapper a true conic-gradient border would need. */
  border-color: var(--accent);
  animation: hpInputGlow 2.4s ease-in-out infinite;
}
@keyframes hpInputGlow {
  0%, 100% { box-shadow: 0 0 0 0 rgba(201, 162, 75, 0.0); }
  50%      { box-shadow: 0 0 0 3px rgba(201, 162, 75, 0.18); }
}

/* ---- Defend the orbit bubbles from the global ripple effect ---------
   static/js/micro-interactions.js delegates a tap-ripple to every <button>,
   adding `.has-ripple` on pointerdown. That class sets `position: relative;
   overflow: hidden`, which on these absolutely-positioned orbit bubbles
   COLLAPSED them out of place (a bubble jumped to the bottom-right on first
   press) and moved the target out from under the cursor mid-press, so the
   pointerup missed and the click never fired — taps silently did nothing.
   Pin the bubble's own positioning so the ripple class can't override it,
   and drop the clip (a ripple bleeding past the pill edge is no loss here). */
.hp-bear-bubble.has-ripple {
  position: absolute;
  overflow: visible;
  isolation: auto;
}
/* The ripple selector also reparents children with position:relative;z-index:1
   — harmless for the bubble's text but disable the ink entirely on bubbles
   so nothing layers on top of the hit-area. */
.hp-bear-bubble .ripple-ink { display: none; }

/* ---- Reduced motion: kill orb-related motion, keep everything legible - */
@media (prefers-reduced-motion: reduce) {
  .hp-bear-chat-msg,
  .hp-bear-chat-bubble.is-streaming::after,
  .hp-bear-chat-input:focus {
    animation: none !important;
  }
}
