/* ================================================================
   GalSol — Gal Voice Assistant  |  voice-assistant.css

   Self-contained on purpose, same rule as account-menu.css: never
   reads a host page's CSS variables (pages disagree on what --text /
   --cyan mean, some don't define them at all). All colour lives in
   the --gv-* tokens below.

   The rotating gradient ring follows the same technique as the Prime
   Site composer glow (prime-site.css) — an animated @property angle
   driving a conic-gradient, with a static fallback for browsers that
   can't animate custom properties.
================================================================ */

:root {
  --gv-text:      #e8eef7;
  --gv-text-2:    #9fb0c6;
  --gv-surface:   #12161f;
  --gv-surface-2: rgba(255, 255, 255, .06);
  --gv-border:    rgba(255, 255, 255, .12);
  --gv-cyan:      #00d4ff;
  --gv-violet:    #a78bfa;
  --gv-danger:    #f87171;
  --gv-shadow:    0 18px 54px rgba(0, 0, 0, .55);
}
:root[data-theme="light"] {
  --gv-text:      #0f172a;
  --gv-text-2:    #475569;
  --gv-surface:   #ffffff;
  --gv-surface-2: rgba(15, 23, 42, .05);
  --gv-border:    rgba(15, 23, 42, .14);
  --gv-cyan:      #0284a8;
  --gv-violet:    #6d3fd4;
  --gv-danger:    #dc2626;
  --gv-shadow:    0 18px 54px rgba(15, 23, 42, .18);
}

@property --gv-angle {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}
@keyframes gvSpin { to { --gv-angle: 360deg; } }

/* ── Floating dock button ─────────────────────────────────────────
   Hidden until summoned with Ctrl+G — .visible is added by
   voice-assistant.js and removed again once the exchange ends. */
.gv-dock {
  position: fixed;
  right: 22px;
  bottom: 22px;
  z-index: 9990;
  width: 58px;
  height: 58px;
  border-radius: 50%;
  border: none;
  background: transparent;
  display: none;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
  filter: drop-shadow(0 10px 26px rgba(0, 0, 0, .45));
  transition: transform .18s ease;
}
.gv-dock.visible {
  display: flex;
  animation: gvDockIn .24s cubic-bezier(.2, .9, .3, 1.4);
}
.gv-dock:hover { transform: scale(1.06); }
.gv-dock:focus-visible { outline: 2px solid var(--gv-cyan); outline-offset: 4px; }
@keyframes gvDockIn {
  from { transform: scale(.5); opacity: 0; }
  to   { transform: scale(1);  opacity: 1; }
}

/* Crisp gradient ring, masked to just the rim so the icon underneath
   stays visible — this is the border itself, not a glow.
   AT REST it is a quiet, still, desaturated ring: an idle assistant
   should not sit there strobing at the corner of the page. The colour
   and the rotation both switch on only while it is actually working. */
.gv-dock::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: conic-gradient(from var(--gv-angle, 0deg),
    #00d4ff 0%, #7c3aed 28%, #ec4899 52%, #7c3aed 74%, #00d4ff 100%);
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 3px));
          mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 3px));
  animation: gvSpin 3.4s linear infinite;
  animation-play-state: paused;
  opacity: .38;
  filter: saturate(.5);
  transition: opacity .3s ease, filter .3s ease;
}

/* Soft coloured halo escaping from behind the rim — off unless active. */
.gv-dock::after {
  content: '';
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  background: conic-gradient(from var(--gv-angle, 0deg),
    rgba(0, 212, 255, 0) 0%, rgba(0, 212, 255, .85) 20%,
    rgba(124, 58, 237, .85) 45%, rgba(236, 72, 153, .6) 68%,
    rgba(0, 212, 255, 0) 100%);
  filter: blur(11px);
  opacity: 0;
  animation: gvSpin 3.4s linear infinite;
  animation-play-state: paused;
  transition: opacity .3s ease;
}

/* Lit only while listening / thinking / speaking. */
.gv-dock.listening::before, .gv-dock.processing::before, .gv-dock.speaking::before,
.gv-dock.listening::after,  .gv-dock.processing::after,  .gv-dock.speaking::after {
  animation-play-state: running;
}
.gv-dock.listening::before, .gv-dock.processing::before, .gv-dock.speaking::before {
  opacity: 1;
  filter: saturate(1);
}
.gv-dock.listening::after, .gv-dock.processing::after, .gv-dock.speaking::after {
  opacity: .8;
}
/* Hovering an idle dock hints that it is alive, without the full glow. */
.gv-dock:hover::before { opacity: .7; filter: saturate(.85); }

/* The solid face that the icon sits on, painted over the ring's centre. */
.gv-dock-inner {
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background: var(--gv-surface);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
}
.gv-dock-inner img { width: 27px; height: 27px; display: block; pointer-events: none; }

/* State: the ring is always turning, so states change its tempo and the
   button's own scale rather than adding a competing animation. */
.gv-dock.listening { animation: gvPulseListen 1.25s ease-in-out infinite; }
.gv-dock.listening::before,
.gv-dock.listening::after { animation-duration: 1.8s; }
.gv-dock.processing::before,
.gv-dock.processing::after { animation-duration: .85s; }
.gv-dock.speaking { animation: gvPulseSpeak .75s ease-in-out infinite; }
.gv-dock.speaking::before,
.gv-dock.speaking::after { animation-duration: 2.4s; }

@keyframes gvPulseListen {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.11); }
}
@keyframes gvPulseSpeak {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.06); }
}

/* Browsers that can't animate a custom property would show a frozen
   gradient, so give them a plain static ring instead of a dead one. */
@supports not (background: conic-gradient(from var(--gv-angle), red, blue)) {
  .gv-dock::before { background: linear-gradient(135deg, #00d4ff, #7c3aed, #ec4899); animation: none; }
  .gv-dock::after  { background: linear-gradient(135deg, #00d4ff, #7c3aed); animation: none; }
}

/* ── Caption bar — one line at a time, like a film subtitle.
   Appears only when there is something to show: it stays off screen
   until the user actually says something, shows that phrase, then is
   replaced by Gal's reply and fades out. Never a transcript log. ─── */
.gv-bar {
  position: fixed;
  right: 92px;
  bottom: 22px;
  z-index: 9991;
  max-width: min(430px, calc(100vw - 130px));
  background: var(--gv-surface);
  border: 1px solid var(--gv-border);
  border-radius: 12px;
  box-shadow: var(--gv-shadow);
  padding: 11px 16px;
  color: var(--gv-text);
  font-family: inherit;
  font-size: 13.5px;
  line-height: 1.45;
  opacity: 0;
  transform: translateX(10px);
  pointer-events: none;
  transition: opacity .18s ease, transform .18s ease;
}
.gv-bar.open {
  opacity: 1;
  transform: translateX(0);
}
.gv-cap {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
}
/* Who is speaking is carried by colour alone — no labels, no clutter. */
.gv-bar.you   .gv-cap { color: var(--gv-text-2); }
.gv-bar.reply .gv-cap { color: var(--gv-cyan); font-weight: 500; }
.gv-bar.error .gv-cap { color: var(--gv-danger); font-weight: 500; }

/* ── Confirm dialog (destructive commands only) ──────────────────── */
.gv-confirm-back {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: rgba(6, 10, 18, .55);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity .18s ease;
}
.gv-confirm-back.open { opacity: 1; }
.gv-confirm {
  width: min(360px, calc(100vw - 32px));
  background: var(--gv-surface);
  border: 1px solid var(--gv-border);
  border-radius: 16px;
  box-shadow: var(--gv-shadow);
  padding: 22px;
  color: var(--gv-text);
  transform: translateY(6px);
  transition: transform .18s ease;
}
.gv-confirm-back.open .gv-confirm { transform: translateY(0); }
.gv-confirm-title { font-size: 16px; font-weight: 700; margin-bottom: 8px; }
.gv-confirm-msg { font-size: 13px; color: var(--gv-text-2); margin-bottom: 18px; }
.gv-confirm-row { display: flex; justify-content: flex-end; gap: 10px; }
.gv-btn {
  border: 1px solid var(--gv-border);
  background: var(--gv-surface-2);
  color: var(--gv-text);
  border-radius: 9px;
  padding: 8px 16px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.gv-btn.danger { background: var(--gv-danger); border-color: var(--gv-danger); color: #2a0a0a; }

/* ── Mobile ──────────────────────────────────────────────────────── */
@media (max-width: 560px) {
  .gv-dock { right: 14px; bottom: 14px; width: 52px; height: 52px; }
  .gv-dock-inner img { width: 24px; height: 24px; }
  /* No room beside the dock on a phone — the caption moves above it. */
  .gv-bar { right: 14px; left: 14px; max-width: none; bottom: 78px; }
}

@media (prefers-reduced-motion: reduce) {
  .gv-dock, .gv-dock::before, .gv-dock::after { animation: none; }
  .gv-dock::before { background: linear-gradient(135deg, #00d4ff, #7c3aed, #ec4899); }
}
