/* ==========================================================================
   GANGLIA DESIGN SYSTEM & CLASSIC STYLING
   ========================================================================== */

/* Design Tokens (Dark Demonic Palette) */
:root {
  --color-bg-darker: #0a060c;
  --color-bg-dark: #110b14;
  --color-bg-sidebar: #0c080f;
  
  --color-accent-violet: #bb3aed;
  --color-accent-violet-glow: rgba(187, 58, 237, 0.3);
  --color-accent-green: #10b981;
  --color-accent-green-glow: rgba(16, 185, 129, 0.35);
  --color-accent-red: #ef4444;
  --color-accent-red-glow: rgba(239, 68, 68, 0.35);
  
  --color-text-main: #f3f4f6;
  --color-text-muted: #9ca3af;
  --color-text-dimmed: #6b7280;
  
  --glass-bg: rgba(18, 12, 22, 0.6);
  --glass-border: rgba(187, 58, 237, 0.12);
  --glass-border-focus: rgba(187, 58, 237, 0.4);
  
  --font-sans: 'Outfit', sans-serif;
  --font-mono: 'Outfit Mono', monospace;
  
  --transition-smooth: 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
}

/* Base Resets */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: var(--font-sans);
  color: var(--color-text-main);
  background-color: var(--color-bg-darker);
  min-height: 100vh;
  line-height: 1.5;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}

/* Custom Scrollbar Styling (Hiding default bulk) */
::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}

::-webkit-scrollbar-track {
  background: var(--color-bg-darker);
}

::-webkit-scrollbar-thumb {
  background: var(--glass-border-focus);
  border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
  background: var(--color-accent-violet);
}

/* Accessibility Focus Management */
*:focus-visible {
  outline: 2px solid var(--color-accent-violet);
  outline-offset: 4px;
  box-shadow: 0 0 12px var(--color-accent-violet-glow);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
}

/* ==========================================================================
   GRID LAYOUT
   ========================================================================== */

.app-layout {
  /* Single-column view, capped at a standard monitor width and centered. On
     wider screens the body's red underworld glow bleeds into the side margins.
     The former left sidebar's functions now live in the top-banner corners. */
  width: 100%;
  max-width: 1440px;
  margin: 0 auto;
  height: 100vh;
  position: relative;
  z-index: 1;
  /* Transparent so the body's red "underworld" glow washes the whole screen,
     not just the side margins. The translucent panels below supply contrast. */
  background-color: transparent;
}

/* Crackling red "underworld" glow that spreads into the dead space beside the
   capped layout on wide monitors. Hidden behind the (opaque) app on narrow
   screens. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 55% 85% at 0% 50%, rgba(220, 38, 38, 0.24), transparent 72%),
    radial-gradient(ellipse 55% 85% at 100% 50%, rgba(220, 38, 38, 0.24), transparent 72%),
    radial-gradient(ellipse 120% 90% at 50% 50%, rgba(220, 38, 38, 0.11), transparent 78%);
  animation: underworld-crackle 4.2s ease-in-out infinite;
}

@keyframes underworld-crackle {
  0%   { opacity: 0.5; }
  18%  { opacity: 0.82; }
  32%  { opacity: 0.44; }
  48%  { opacity: 0.92; }
  60%  { opacity: 0.52; }
  76%  { opacity: 0.78; }
  100% { opacity: 0.58; }
}

/* Setup Sidebar Styling */
.setup-sidebar {
  background-color: var(--color-bg-sidebar);
  border-right: 1px solid var(--glass-border);
  padding: 2rem 1.5rem;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  gap: 2rem;
}

.sidebar-header {
  text-align: center;
  border-bottom: 1px solid var(--glass-border);
  padding-bottom: 1.5rem;
}

.sidebar-logo {
  width: 90px;
  height: 90px;
  border-radius: 50%;
  margin-bottom: 1rem;
  box-shadow: 0 0 20px var(--color-accent-violet-glow);
  border: 2px solid var(--glass-border);
  transition: transform 0.5s ease;
}

.sidebar-logo:hover {
  transform: rotate(360deg);
}

.sidebar-title {
  font-family: 'Orbitron', sans-serif;
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: 0.3em;
  color: #fff;
  text-shadow: 0 0 8px var(--color-accent-violet-glow);
}

.sidebar-subtitle {
  font-size: 0.65rem;
  font-weight: 500;
  letter-spacing: 0.25em;
  color: var(--color-accent-violet);
  margin-top: 0.25rem;
}

/* Form & Inputs */
.setup-section {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.setup-section h3 {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--color-text-muted);
  font-weight: 700;
}

.form-group {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.form-group label {
  font-size: 0.75rem;
  color: var(--color-text-muted);
  font-weight: 500;
}

.form-group input {
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--glass-border);
  border-radius: 8px;
  padding: 0.75rem 1rem;
  color: #fff;
  font-family: var(--font-sans);
  font-size: 0.9rem;
  transition: var(--transition-smooth);
}

.form-group input:focus {
  border-color: var(--color-accent-violet);
  background: rgba(255, 255, 255, 0.05);
  outline: none;
}

/* Buttons */
.btn {
  width: 100%;
  padding: 0.9rem;
  border-radius: 8px;
  border: none;
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 700;
  cursor: pointer;
  transition: var(--transition-smooth);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  position: relative;
  overflow: hidden;
}

.btn-primary {
  background: linear-gradient(135deg in oklab, var(--color-accent-violet), #ea84fc);
  color: #fff;
  box-shadow: 0 0 15px var(--color-accent-violet-glow);
}

.btn-primary:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 0 25px var(--color-accent-violet-glow);
}

.btn-danger {
  background: transparent;
  border: 1px solid var(--color-accent-red);
  color: var(--color-accent-red);
}

.btn-danger:hover:not(:disabled) {
  background: var(--color-accent-red);
  color: #fff;
  box-shadow: 0 0 15px var(--color-accent-red-glow);
}

.btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
  transform: none !important;
  box-shadow: none !important;
}

.action-section {
  gap: 0.75rem;
}

/* Auth lives in the bottom-left corner: push it to the foot of the sidebar. */
.account-section {
  margin-top: auto;
}

.sidebar-footer {
  border-top: 1px solid var(--glass-border);
  padding-top: 1rem;
  font-size: 0.7rem;
  color: var(--color-text-dimmed);
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.status-indicator-wrapper {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

#sys-status {
  font-weight: 700;
}

.status-online {
  color: var(--color-accent-green);
  text-shadow: 0 0 4px var(--color-accent-green-glow);
}

.status-offline {
  color: var(--color-text-dimmed);
}

/* ==========================================================================
   MAIN WORKSPACE PANEL
   ========================================================================== */

.active-workspace {
  /* Transparent so the red underworld glow shows through behind the workspace
     (the header / arena / transcript / input panels keep their own backdrops). */
  background-color: transparent;
  display: grid;
  /* banner / header / GANGLIA avatar / conversation (dominant) / input */
  grid-template-rows: 128px 56px var(--avatar-height, 30vh) 1fr 80px;
  height: 100vh;
  position: relative;
}

/* Header Status Bar */
.workspace-header {
  position: relative;
  border-bottom: 1px solid var(--glass-border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 0 2rem;
  background-color: rgba(12, 8, 15, 0.4);
}

/* ==========================================================================
   SÉANCE METER — gothic countdown of time on the line + a piggy bank that
   swallows a full-moon coin each minute. Shown only while summoned, centered
   between the GANGLIA banner and the avatar (video) panel.
   ========================================================================== */
.seance-meter {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  gap: 1.4rem;
  z-index: 15;
}

.seance-meter[hidden] {
  display: none;
}

.piggy-bank {
  display: flex;
  align-items: center;
  gap: 0.3rem;
}

.grave-icon {
  width: 3.4rem;
  height: 3.4rem;
  display: block;
  filter: drop-shadow(0 0 7px rgba(187, 58, 237, 0.55));
}

.piggy-count {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 700;
  color: #eec2ff;
}

.piggy-bank.piggy-accept {
  animation: piggy-bounce 0.32s ease;
}

@keyframes piggy-bounce {
  0%, 100% { transform: scale(1) rotate(0); }
  40% { transform: scale(1.2) rotate(-6deg); }
}

.countdown {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.countdown-icon {
  font-size: 1rem;
  line-height: 1;
}

.countdown-time {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 1.1rem;
  letter-spacing: 0.08em;
  color: #ff6b6b;
  text-shadow: 0 0 10px rgba(239, 68, 68, 0.65), 0 0 22px rgba(239, 68, 68, 0.3);
}

/* A spent minute, flying as a full moon into the piggy bank. */
/* The flying coin always carries both classes; use the combined selector so
   position:fixed + size win over .moon-token's position:relative (defined later,
   same specificity) — otherwise the coin is placed in document flow off-screen
   and the flight is invisible (only the piggy "accept" bounce shows). */
.moon-coin.moon-token {
  position: fixed;
  transform: translate(-50%, -50%);
  width: 22px;
  height: 22px;
  z-index: 80;
  pointer-events: none;
}

.state-badge, .connection-status-badge {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.state-text {
  color: var(--color-text-muted);
}

.pulse-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  display: inline-block;
}

.pulse-dot.idle {
  background-color: var(--color-text-dimmed);
}

.pulse-dot.listening {
  background-color: var(--color-accent-green);
  animation: pulse-green 1.5s infinite;
}

.pulse-dot.thinking {
  background-color: var(--color-accent-violet);
  animation: pulse-violet 1.2s infinite;
}

.pulse-dot.speaking {
  background-color: #f472b6;
  animation: pulse-magenta 1s infinite;
}

.conn-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
}

.conn-dot.connected {
  background-color: var(--color-accent-green);
  box-shadow: 0 0 8px var(--color-accent-green);
}

.conn-dot.disconnected {
  background-color: var(--color-accent-red);
  box-shadow: 0 0 8px var(--color-accent-red);
}

/* ==========================================================================
   AVATAR ARENA (The Demon visual face)
   ========================================================================== */

.avatar-arena {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  border-bottom: 1px solid var(--glass-border);
  position: relative;
  background: radial-gradient(circle at center, rgba(187, 58, 237, 0.04) 0%, transparent 70%);
  gap: 0.75rem;
  padding: 1rem 0;
  overflow: hidden;
  min-height: 0;
}

.avatar-wrapper {
  position: relative;
  width: min(250px, 34vh);
  height: min(330px, 44vh);
  display: flex;
  align-items: center;
  justify-content: center;
  /* Depth so the rig's cursor-driven rotateX/rotateY read as a real head turn */
  perspective: 1000px;
}

/* ==========================================================================
   SUMMON / BANISH — fun buttons flanking the avatar (moved out of sidebar)
   ========================================================================== */
.flank-btn {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 6;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.45rem;
  width: 104px;
  padding: 1.1rem 0.75rem;
  border-radius: 18px;
  border: 1px solid var(--glass-border);
  background: rgba(26, 13, 29, 0.55);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  color: #fff;
  font-family: var(--font-sans);
  font-size: 0.8rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
  transition: var(--transition-smooth);
}

.flank-btn .flank-icon {
  font-size: 1.7rem;
  line-height: 1;
  filter: drop-shadow(0 0 6px currentColor);
}

.flank-btn:disabled {
  opacity: 0.3;
  cursor: not-allowed;
  filter: grayscale(0.6);
  animation: none;
  box-shadow: none;
}

.flank-summon {
  left: clamp(0.75rem, 7vw, 6rem);
  border-color: var(--color-accent-violet);
  color: #eec2ff;
  box-shadow: 0 0 18px var(--color-accent-violet-glow), inset 0 0 12px rgba(187, 58, 237, 0.15);
  animation: flank-summon-pulse 2.6s ease-in-out infinite;
}

.flank-summon:hover:not(:disabled) {
  transform: translateY(-50%) scale(1.06);
  color: #fff;
  background: linear-gradient(160deg in oklab, rgba(187, 58, 237, 0.45), rgba(234, 132, 252, 0.25));
  box-shadow: 0 0 34px var(--color-accent-violet-glow);
}

.flank-banish {
  right: clamp(0.75rem, 7vw, 6rem);
  border-color: var(--color-accent-red);
  color: #ff9e8a;
  box-shadow: 0 0 18px var(--color-accent-red-glow), inset 0 0 12px rgba(239, 68, 68, 0.12);
}

.flank-banish:hover:not(:disabled) {
  transform: translateY(-50%) scale(1.06);
  color: #fff;
  background: linear-gradient(160deg in oklab, rgba(239, 68, 68, 0.45), rgba(124, 16, 16, 0.3));
  box-shadow: 0 0 34px var(--color-accent-red-glow);
}

@keyframes flank-summon-pulse {
  0%, 100% { box-shadow: 0 0 14px var(--color-accent-violet-glow), inset 0 0 12px rgba(187, 58, 237, 0.12); }
  50% { box-shadow: 0 0 30px var(--color-accent-violet-glow), inset 0 0 16px rgba(187, 58, 237, 0.24); }
}

/* ==========================================================================
   TOP BANNER — single-column view.
   Summons (upper-left) · GANGLIA brand (center) · Account "S" (upper-right).
   ========================================================================== */
.top-banner {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 1rem 1rem 0.65rem;
  border-bottom: 1px solid var(--glass-border);
  background-color: rgba(12, 8, 15, 0.4);
}

.banner-brand .sidebar-logo {
  width: 56px;
  height: 56px;
  margin-bottom: 0.35rem;
}

.banner-brand .sidebar-title {
  font-size: 1.5rem;
}

/* Corner controls pinned to the banner's top corners. */
.banner-corner {
  position: absolute;
  top: 0.85rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  z-index: 60;
  max-width: 42%;
}

.banner-left {
  left: 1.1rem;
  align-items: flex-start;
}

.banner-right {
  right: 1.1rem;
  align-items: flex-end;
}

/* Keep the emporium hotlink compact in the corner. */
.banner-left .emporium-link {
  font-size: 0.68rem;
}

/* Account "S" button (opens the sign-in / user popover). */
.account-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  flex: 0 0 auto;
  height: 38px;
  padding: 0 1rem;
  border-radius: 19px;
  border: 1px solid transparent;
  background: linear-gradient(135deg in oklab, var(--color-accent-violet), #ea84fc);
  color: #fff;
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 0.82rem;
  letter-spacing: 0.04em;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: 0 0 14px var(--color-accent-violet-glow);
  transition: var(--transition-smooth);
}

.account-toggle:hover {
  color: #fff;
  transform: translateY(-1px);
  box-shadow: 0 0 22px var(--color-accent-violet-glow);
}

/* When signed in, collapse the sign-in pill to a round avatar showing the
   user's initial. */
.account-toggle.signed-in {
  width: 38px;
  padding: 0;
  border-radius: 50%;
  font-size: 1.05rem;
  font-weight: 800;
}

/* Account popover — holds the sign-in form / signed-in panel moved from the
   old sidebar. Hidden until the "S" button is clicked. */
.account-popover {
  position: absolute;
  top: 50px;
  right: 0;
  width: min(320px, 86vw);
  max-height: 72vh;
  overflow-y: auto;
  padding: 1.1rem;
  border-radius: 14px;
  border: 1px solid var(--glass-border);
  background: rgba(21, 11, 24, 0.97);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.6);
  text-align: left;
  transform-origin: top right;
  transition: opacity 0.2s ease, transform 0.2s ease;
  z-index: 40;
}

.account-popover.inert {
  opacity: 0;
  transform: translateY(-8px) scale(0.97);
  pointer-events: none;
}

/* No longer pinned to the foot of a sidebar column. */
.account-popover .account-section {
  margin-top: 0;
  gap: 0.85rem;
}

/* Orbital Rings */
.aura-ring {
  position: absolute;
  border-radius: 50%;
  border: 1px solid transparent;
  transition: var(--transition-smooth);
}

.aura-outer {
  width: 100%;
  height: 100%;
  border-color: rgba(187, 58, 237, 0.08);
  box-shadow: inset 0 0 20px rgba(187, 58, 237, 0.02);
}

.aura-middle {
  width: 80%;
  height: 80%;
  border-color: rgba(187, 58, 237, 0.12);
}

.aura-inner {
  width: 60%;
  height: 60%;
  border-color: rgba(187, 58, 237, 0.2);
  box-shadow: 0 0 25px rgba(187, 58, 237, 0.05);
}

/* ==========================================================================
   LIVE GANGLIA RIG — the ghoul in his coffin, amplitude-driven jaw
   A static coffin-scene backdrop carries the whole face + red casket. Over it,
   a dark mouth cavity and a hinged jaw flap (the lower face, cut from the same
   image so it lines up exactly) drop open on the mouth line, driven entirely by
   --jaw-open (0..1) set from the live audio analyser. Container is clipped to a
   casket silhouette so it reads unmistakably as a coffin.
   ========================================================================== */

:root {
  /* Vertical position of the mouth line in the scene image (clip + hinge line) */
  --rig-mouth: 56%;
}

.ganglia-rig {
  position: absolute;
  inset: 0;
  margin: auto;                 /* center the casket inside the wrapper */
  width: min(220px, 30vh);
  height: min(310px, 42vh);
  overflow: hidden;
  background-color: #0e0810;
  /* Casket silhouette: narrow head end, widening to the shoulders, tapering to the foot */
  clip-path: polygon(28% 0%, 72% 0%, 100% 21%, 88% 100%, 12% 100%, 0% 21%);
  z-index: 10;
  /* perspective lets the jaw rotateX read as a real hinge */
  perspective: 520px;
  filter: drop-shadow(0 0 26px var(--color-accent-violet-glow));
  transition: filter var(--transition-smooth);
  /* --jaw-open is set live from JS; default closed */
  --jaw-open: 0;
}

/* Static coffin scene: full face + red tufted casket interior */
.rig-backdrop,
.rig-jaw {
  position: absolute;
  inset: 0;
  background-image: url(assets/ganglia_coffin.png?v=1);
  background-size: cover;
  background-position: center top;
  background-repeat: no-repeat;
  backface-visibility: hidden;
}

.rig-backdrop {
  z-index: 1;
}

/* Dark mouth interior — a soft dark pocket at the mouth, hidden behind the jaw
   flap at rest and revealed in the gap when the jaw drops */
.rig-cavity {
  position: absolute;
  inset: 0;
  z-index: 2;
  background:
    radial-gradient(ellipse 16% 7% at 50% calc(var(--rig-mouth) + 3%),
      #1c0404 0%, #0c0202 60%, transparent 100%);
}

/* Jaw flap: only the lower face below the mouth line. A soft elliptical mask
   confines the moving pixels to the chin/jaw so the edges feather into the
   static backdrop instead of cutting a hard rectangle. */
.rig-jaw {
  z-index: 3;
  clip-path: inset(var(--rig-mouth) 0 0 0);
  -webkit-mask: radial-gradient(ellipse 30% 18% at 50% calc(var(--rig-mouth) + 6%),
      #000 52%, transparent 100%);
  mask: radial-gradient(ellipse 30% 18% at 50% calc(var(--rig-mouth) + 6%),
      #000 52%, transparent 100%);
  transform-origin: 50% var(--rig-mouth);
  transform:
    translateY(calc(var(--jaw-open) * 8px))
    rotateX(calc(var(--jaw-open) * -17deg));
  /* Snappy follow for low latency; gentle release */
  transition: transform 0.045s linear;
}

/* Red glowing eyes overlaid on the skull's lenses. Hidden by default; lit only
   while GANGLIA is summoned (states below). The pupils swivel via --gaze-x/y,
   set live by ganglia_eyes.js. */
.rig-eyes {
  position: absolute;
  inset: 0;
  z-index: 4;                 /* above the jaw flap so they're never occluded */
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.5s ease;
}

/* Animatable angle for the swirling-hellfire (thinking) iris. */
@property --fire-angle {
  syntax: '<angle>';
  inherits: true;
  initial-value: 0deg;
}

/* The eye: a semi-realistic glowing amber goat eye (image) sitting IN FRONT of a
   red glowing orb. The orb (this element) is the red halo behind; ::before is the
   goat-eye image, which swivels toward the pointer via --gaze-x/y so it reads as
   the eye looking around. ::after is the thinking-only hellfire swirl. */
.rig-eye {
  position: absolute;
  top: 39%;                   /* eye line of the face (round glasses) */
  width: 15%;
  height: 6.4%;               /* wider + thinner: a goat-almond oval, not a disc */
  transform: translate(-50%, -50%);
  border-radius: 50%;
  overflow: hidden;           /* clip the eye image to the almond aperture */
  animation: eye-flicker 3.2s ease-in-out infinite;
}

/* The amber goat eye itself, in front of the orb (a thin red ring shows around
   it). Swivels with the gaze. */
.rig-eye::before {
  content: "";
  position: absolute;
  inset: 6%;
  background: url(assets/goat_eye.png?v=1) center / cover no-repeat;
  transform: translate(var(--gaze-x, 0px), var(--gaze-y, 0px));
  filter: drop-shadow(0 0 3px rgba(255, 90, 30, 0.6));
}

/* THINKING: a swirling hellfire vortex layered over the amber eye. Hidden
   otherwise. The conic angle spins (not the element), blended onto the eye. */
.rig-eye::after {
  content: "";
  position: absolute;
  inset: 6%;
  border-radius: 50%;
  background: conic-gradient(from var(--fire-angle),
    #ffe46b 0deg, #ff9b1e 55deg, #ff3a00 110deg, #8c0d00 165deg,
    #ff5a00 220deg, #ffc24a 290deg, #ffe46b 360deg);
  mix-blend-mode: screen;
  opacity: 0;
  transform: translate(var(--gaze-x, 0px), var(--gaze-y, 0px));
  -webkit-mask: radial-gradient(ellipse at 50% 50%, transparent 16%, #000 34%, #000 74%, transparent 86%);
  mask: radial-gradient(ellipse at 50% 50%, transparent 16%, #000 34%, #000 74%, transparent 86%);
}

.rig-eye-left { left: 36.5%; }
.rig-eye-right { left: 58.5%; }

/* Faint living pulse to the glow. */
@keyframes eye-flicker {
  0%, 100% { filter: brightness(1); }
  45% { filter: brightness(1.2); }
  70% { filter: brightness(0.92); }
}

/* Lit only when summoned. Idle/ended keep the eyes dark (eyelids are down). */
.avatar-wrapper[data-state="listening"] .rig-eyes,
.avatar-wrapper[data-state="thinking"] .rig-eyes,
.avatar-wrapper[data-state="speaking"] .rig-eyes {
  opacity: 1;
}

/* THINKING: light the hellfire swirl over the eye + run the glow molten orange. */
.avatar-wrapper[data-state="thinking"] .rig-eye {
  box-shadow:
    0 0 10px 3px rgba(255, 150, 0, 0.95),
    0 0 22px 8px rgba(255, 70, 0, 0.6),
    0 0 40px 16px rgba(190, 40, 0, 0.4);
}

.avatar-wrapper[data-state="thinking"] .rig-eye::after {
  opacity: 0.85;
  animation: eye-spiral 0.7s linear infinite;
}

@keyframes eye-spiral {
  to { --fire-angle: 360deg; }
}

@media (prefers-reduced-motion: reduce) {
  .rig-eye { animation: none; }
  .avatar-wrapper[data-state="thinking"] .rig-eye::after { animation: none; }
}

/* Reveal smoke: a one-shot billow when GANGLIA is unveiled from the shadows.
   Idle by default (no puffs); the .revealing class on the wrapper fires the
   staggered rise. Soft radial puffs (no blur filter) so it's cheap. */
.rig-smoke {
  position: absolute;
  inset: 0;
  z-index: 20;                /* over the rig, so he's unveiled as it clears */
  pointer-events: none;
  overflow: visible;
}

.rig-smoke span {
  position: absolute;
  bottom: 2%;
  left: 50%;
  width: 95%;
  height: 95%;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
    rgba(206, 198, 224, 0.6) 0%, rgba(150, 138, 176, 0.4) 38%,
    rgba(80, 70, 100, 0.2) 60%, transparent 75%);
  transform: translate(-50%, 20%) scale(0.3);
  opacity: 0;
}

/* Spread the puffs wide across (and past) the avatar, staggered for a roiling
   billow. */
.rig-smoke span:nth-child(1) { left: 12%; }
.rig-smoke span:nth-child(2) { left: 50%; }
.rig-smoke span:nth-child(3) { left: 88%; }
.rig-smoke span:nth-child(4) { left: 28%; }
.rig-smoke span:nth-child(5) { left: 72%; }
.rig-smoke span:nth-child(6) { left: 50%; }

.avatar-wrapper.revealing .rig-smoke span {
  animation: smoke-rise 4.2s ease-out forwards;
}
.avatar-wrapper.revealing .rig-smoke span:nth-child(1) { animation-delay: 0s; }
.avatar-wrapper.revealing .rig-smoke span:nth-child(2) { animation-delay: 0.2s; }
.avatar-wrapper.revealing .rig-smoke span:nth-child(3) { animation-delay: 0.1s; }
.avatar-wrapper.revealing .rig-smoke span:nth-child(4) { animation-delay: 0.45s; }
.avatar-wrapper.revealing .rig-smoke span:nth-child(5) { animation-delay: 0.32s; }
.avatar-wrapper.revealing .rig-smoke span:nth-child(6) { animation-delay: 0.62s; }

@keyframes smoke-rise {
  0%   { opacity: 0;    transform: translate(-50%, 25%) scale(0.35); }
  28%  { opacity: 0.85; transform: translate(-50%, -2%) scale(1.0); }
  100% { opacity: 0;    transform: translate(-50%, -75%) scale(2.2); }
}

@media (prefers-reduced-motion: reduce) {
  .avatar-wrapper.revealing .rig-smoke span { animation: none; }
}

/* Dynamic visual states */

/* Idle Aura */
.avatar-wrapper[data-state="idle"] .aura-outer {
  animation: breathe 4s ease-in-out infinite;
}

.avatar-wrapper[data-state="idle"] .aura-middle {
  animation: breathe 4s ease-in-out infinite 1s;
}

/* Banished / dormant state — GANGLIA is not summoned (no active session). */
.avatar-wrapper[data-state="idle"] .aura-inner,
.avatar-wrapper[data-state="ended"] .aura-inner {
  opacity: 0.35;
  box-shadow: none;
}

.avatar-wrapper[data-state="idle"] .ganglia-rig,
.avatar-wrapper[data-state="ended"] .ganglia-rig {
  filter: drop-shadow(0 0 12px rgba(187, 58, 237, 0.12));
}

.avatar-wrapper[data-state="idle"] .rig-backdrop,
.avatar-wrapper[data-state="idle"] .rig-jaw,
.avatar-wrapper[data-state="ended"] .rig-backdrop,
.avatar-wrapper[data-state="ended"] .rig-jaw {
  filter: grayscale(0.9) brightness(0.45) contrast(0.95);
  transition: filter var(--transition-smooth);
}

/* Lowered eyelids: a dark veil over the upper face reads as closed eyes. */
.avatar-wrapper[data-state="idle"] .rig-backdrop::after,
.avatar-wrapper[data-state="ended"] .rig-backdrop::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    rgba(7, 3, 8, 0) 0%,
    rgba(7, 3, 8, 0.55) 22%,
    rgba(7, 3, 8, 0.85) 33%,
    rgba(7, 3, 8, 0) 48%
  );
}

/* Listening state: Green neon alert aura */
.avatar-wrapper[data-state="listening"] .aura-inner {
  border-color: var(--color-accent-green);
  box-shadow: 0 0 30px var(--color-accent-green-glow);
}

.avatar-wrapper[data-state="listening"] .aura-outer {
  animation: pulse-border-green 2s infinite;
  border-color: rgba(16, 185, 129, 0.2);
}

/* Thinking state: Rapid high-speed rotation */
.avatar-wrapper[data-state="thinking"] .aura-outer {
  border-style: dashed;
  border-color: var(--color-accent-violet);
  animation: spin-clockwise 2.5s linear infinite;
}

.avatar-wrapper[data-state="thinking"] .aura-inner {
  border-style: dashed;
  border-color: #ea84fc;
  animation: spin-counterclockwise 1.5s linear infinite;
}

.avatar-wrapper[data-state="thinking"] .ganglia-rig {
  filter: drop-shadow(0 0 26px var(--color-accent-violet));
}

/* Speaking state: warm glow while the jaw is live */
.avatar-wrapper[data-state="speaking"] .aura-outer {
  animation: breathe 1.5s ease-in-out infinite;
  border-color: rgba(244, 114, 182, 0.25);
  box-shadow: 0 0 40px rgba(244, 114, 182, 0.1);
}

.avatar-wrapper[data-state="speaking"] .ganglia-rig {
  filter: drop-shadow(0 0 34px rgba(244, 114, 182, 0.45));
}

/* Head motion (gaze-follow + rotoscope boil) is owned by JS in app.js —
   no CSS transform animation here, or it would override the inline transform. */

/* Legacy equalizer overlay — superseded by the live jaw, kept hidden */
.audio-waves {
  display: none;
}

/* ==========================================================================
   TRANSCRIPT LOG VIEWPORT
   ========================================================================== */

.transcript-section {
  display: grid;
  grid-template-rows: 40px 1fr;
  padding: 1.5rem 2rem;
  border-bottom: 1px solid var(--glass-border);
  overflow: hidden;
  /* Positioned so the shush cluster (and the resizer) anchor to the transcript
     top — i.e. just below the avatar arena, clear of GANGLIA. */
  position: relative;
}

/* --- Lightweight quest tracker HUD ------------------------------------- */
/* Floats over the workspace (active-workspace is position:relative), pinned
   below the banner+header so it clears them. Absolute → out of the grid flow,
   so it never disturbs the arena/transcript layout. Hidden until a quest is
   active (the [hidden] attribute is toggled from app.js). */
.quest-panel {
  position: absolute;
  top: calc(128px + 56px + 0.75rem); /* below banner (128) + header (56) */
  right: 1rem;
  z-index: 40;
  width: 260px;
  max-width: calc(100vw - 2rem);
  padding: 0.7rem 0.85rem;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border-focus);
  border-radius: 12px;
  backdrop-filter: blur(8px);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45),
    0 0 18px var(--color-accent-violet-glow);
  font-size: 0.85rem;
  animation: quest-panel-in 0.25s ease-out;
}
.quest-panel[hidden] { display: none; }

@keyframes quest-panel-in {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: translateY(0); }
}

.quest-panel-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.quest-panel-badge {
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  color: var(--color-accent-violet);
  border: 1px solid var(--glass-border-focus);
  border-radius: 4px;
  padding: 0.1rem 0.3rem;
  text-transform: uppercase;
}
.quest-panel-title {
  flex: 1;
  font-weight: 700;
  color: var(--color-text-main);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.quest-panel-timer {
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
  font-size: 0.8rem;
}
.quest-panel-milestone {
  margin-top: 0.4rem;
  color: var(--color-text-muted);
  font-size: 0.8rem;
  line-height: 1.35;
}
.quest-panel-clone { margin-top: 0.6rem; }
.quest-panel-clone[hidden] { display: none; }
.quest-clone-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}
.quest-clone-label {
  color: var(--color-text-main);
  font-size: 0.78rem;
}
.quest-clone-eta {
  font-variant-numeric: tabular-nums;
  color: var(--color-accent-violet);
  font-size: 0.78rem;
}
.quest-clone-bar {
  margin-top: 0.35rem;
  height: 4px;
  border-radius: 3px;
  background: rgba(187, 58, 237, 0.15);
  overflow: hidden;
}
.quest-clone-fill {
  height: 100%;
  width: 0%;
  border-radius: 3px;
  background: var(--color-accent-violet);
  box-shadow: 0 0 8px var(--color-accent-violet-glow);
  transition: width 0.6s linear;
}
/* Ready state: the cloned scare has returned — flip the card to green so the
   guest sees it's ready even if GANGLIA hasn't spoken yet. */
.quest-panel.ready {
  border-color: var(--color-accent-green);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45),
    0 0 18px var(--color-accent-green-glow);
}
.quest-panel.ready .quest-clone-label { color: var(--color-accent-green); }
.quest-panel.ready .quest-clone-fill {
  width: 100% !important;
  background: var(--color-accent-green);
  box-shadow: 0 0 8px var(--color-accent-green-glow);
}

/* ── Always-on quest directory HUD ─────────────────────────────────────── */
/* Lives up in the header banner, in the gap between the centred GANGLIA brand
   and the Sign In / account control on the right. Collapsed it's a compact pill;
   expanded the list drops down over the stage. */
.quest-directory {
  position: absolute;
  top: 0.7rem;
  right: 7rem;   /* clear the Sign In / account control at right: 1.1rem */
  z-index: 50;
  width: 240px;
  max-width: calc(100vw - 2rem);
  background: var(--glass-bg);
  border: 1px solid var(--glass-border-focus);
  border-radius: 12px;
  backdrop-filter: blur(8px);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45),
    0 0 18px var(--color-accent-violet-glow);
  overflow: hidden;
  animation: quest-panel-in 0.25s ease-out;
}
.quest-dir-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.6rem 0.7rem;
  background: transparent;
  border: 0;
  cursor: pointer;
  color: var(--color-text-main);
}
.quest-dir-count {
  flex: 1;
  text-align: left;
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}
.quest-dir-chevron {
  width: 0;
  height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid var(--color-accent-violet);
  transition: transform 0.2s ease;
}
.quest-directory.collapsed .quest-dir-chevron { transform: rotate(-90deg); }
.quest-directory.collapsed .quest-dir-list { display: none; }

.quest-dir-list {
  list-style: none;
  margin: 0;
  padding: 0 0.55rem 0.5rem;
}
.quest-dir-item {
  padding: 0.4rem 0.2rem;
  border-top: 1px solid var(--glass-border);
}
.quest-dir-item:first-child { border-top: 0; }
.quest-dir-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.quest-dir-dot {
  flex: 0 0 auto;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-text-dimmed);
}
.quest-dir-name {
  flex: 1;
  font-size: 0.82rem;
  color: var(--color-text-main);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.quest-dir-pill {
  flex: 0 0 auto;
  font-size: 0.56rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.12rem 0.42rem;
  border-radius: 999px;
  border: 1px solid var(--color-text-dimmed);
  color: var(--color-text-muted);
  white-space: nowrap;
}
.quest-dir-item.status-not_started .quest-dir-name { color: var(--color-text-muted); }
.quest-dir-item.status-in_progress .quest-dir-dot {
  background: var(--color-accent-violet);
  box-shadow: 0 0 6px var(--color-accent-violet-glow);
  animation: quest-dir-pulse 1.4s ease-in-out infinite;
}
.quest-dir-item.status-in_progress .quest-dir-pill {
  border-color: var(--color-accent-violet);
  color: var(--color-accent-violet);
}
.quest-dir-item.status-complete .quest-dir-dot { background: var(--color-accent-green); }
.quest-dir-item.status-complete .quest-dir-name { color: var(--color-text-muted); }
.quest-dir-item.status-complete .quest-dir-pill {
  border-color: var(--color-accent-green);
  color: var(--color-accent-green);
}
@keyframes quest-dir-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}
.quest-dir-detail { margin: 0.4rem 0 0.15rem 1rem; }
.quest-dir-detail-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}
.quest-dir-milestone {
  font-size: 0.72rem;
  color: var(--color-text-muted);
}
.quest-dir-timer {
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  color: var(--color-accent-violet);
}
.quest-dir-item.is-ready .quest-dir-pill {
  border-color: var(--color-accent-green);
  color: var(--color-accent-green);
}
.quest-dir-item.is-ready .quest-clone-label { color: var(--color-accent-green); }
.quest-dir-item.is-ready .quest-clone-fill {
  width: 100% !important;
  background: var(--color-accent-green);
  box-shadow: 0 0 8px var(--color-accent-green-glow);
}
@media (prefers-reduced-motion: reduce) {
  .quest-dir-item.status-in_progress .quest-dir-dot { animation: none; }
}

.transcript-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.5rem;
}

.transcript-header h2 {
  font-size: 0.9rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--color-text-muted);
  font-weight: 700;
}

.btn-clear {
  background: transparent;
  border: none;
  color: var(--color-text-dimmed);
  font-family: var(--font-sans);
  font-size: 0.75rem;
  font-weight: 600;
  cursor: pointer;
  transition: var(--transition-smooth);
}

.btn-clear:hover {
  color: var(--color-accent-red);
}

/* Right-side cluster in the transcript header (music toggle + Clear). */
.transcript-header-controls {
  display: flex;
  align-items: center;
  gap: 0.65rem;
}

/* Discreet ambient-music on/off toggle, sized to sit beside the Clear control. */
.btn-music-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  color: var(--color-text-dimmed);
  transition: var(--transition-smooth);
}

.btn-music-toggle:hover {
  color: var(--color-text-main);
}

.btn-music-toggle svg {
  width: 16px;
  height: 16px;
}

.btn-music-toggle .music-slash {
  display: none;
}

/* Muted (music off): show the slash and dim the note. */
.btn-music-toggle.muted {
  opacity: 0.6;
}

.btn-music-toggle.muted .music-slash {
  display: inline;
}

.transcript-messages {
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding-right: 0.5rem;
}

.welcome-message {
  text-align: center;
  margin: auto;
  max-width: 400px;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.welcome-message .timestamp {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--color-accent-violet);
  letter-spacing: 0.15em;
  font-weight: 700;
}

.welcome-message .body {
  font-size: 0.85rem;
  color: var(--color-text-muted);
}

/* Chat bubble styling */
.message-bubble {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  max-width: 80%;
  animation: slide-in-bubble var(--transition-smooth);
}

.message-bubble.user {
  align-self: flex-end;
}

.message-bubble.assistant {
  align-self: flex-start;
}

.message-meta {
  font-size: 0.65rem;
  font-family: var(--font-mono);
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.message-bubble.user .message-meta {
  color: var(--color-text-dimmed);
  text-align: right;
}

.message-bubble.assistant .message-meta {
  color: var(--color-accent-violet);
}

.message-content {
  padding: 0.75rem 1.1rem;
  border-radius: 12px;
  font-size: 0.9rem;
  line-height: 1.45;
}

.message-bubble.user .message-content {
  background: var(--color-bg-darker);
  border: 1px solid var(--glass-border);
  /* The human's voice: a real, living hand — gentle blue-purple to sit with
     the violet scheme, set apart from GANGLIA's crimson incantations. */
  color: #d9bcff;
  border-top-right-radius: 2px;
  font-family: 'Caveat', cursive;
  font-weight: 600;
  font-size: 1.65rem;
  line-height: 1.25;
  letter-spacing: 0.01em;
  text-shadow: 0 0 8px rgba(190, 140, 255, 0.3);
  /* Honor the line breaks STT inserts at each speech-event boundary (the brief
     pauses between things the seeker says), so multi-line turns read as lines. */
  white-space: pre-line;
}

.message-bubble.assistant .message-content {
  background: rgba(187, 58, 237, 0.06);
  border: 1px solid rgba(187, 58, 237, 0.25);
  color: var(--color-accent-red); /* Demonic Crimson */
  border-top-left-radius: 2px;
  box-shadow: 0 0 15px rgba(187, 58, 237, 0.03);
  font-family: 'Orbitron', sans-serif;
  font-size: 1.05rem;
  line-height: 1.6;
  letter-spacing: 0.04em;
  text-shadow: 0 0 10px rgba(239, 68, 68, 0.6), 0 0 20px rgba(239, 68, 68, 0.3);
}

/* Inline code in GANGLIA's markdown turns — break out of Orbitron so magic
   words / years read clearly. */
.message-bubble.assistant .message-content code {
  font-family: 'Outfit Mono', monospace;
  font-size: 0.92em;
  letter-spacing: 0;
  padding: 0 0.3em;
  background: rgba(187, 58, 237, 0.12);
  border-radius: 3px;
}

/* Replay control on GANGLIA's responses — revealed when hovering the bubble. */
.message-bubble .replay-btn {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  margin-top: 0.15rem;
  padding: 0;
  border: 1px solid rgba(187, 58, 237, 0.4);
  border-radius: 50%;
  background: rgba(187, 58, 237, 0.08);
  color: var(--color-accent-violet);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.2s ease, background 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
}

.message-bubble.assistant:hover .replay-btn,
.message-bubble .replay-btn:focus-visible {
  opacity: 0.85;
}

.message-bubble .replay-btn:hover {
  opacity: 1;
  background: rgba(187, 58, 237, 0.22);
  color: #fff;
  box-shadow: 0 0 10px rgba(187, 58, 237, 0.45);
}

.message-bubble.interim .message-content {
  opacity: 0.75;
  border-style: dashed;
  font-family: 'Caveat', cursive;
  font-weight: 600;
  font-size: 1.65rem;
}

/* ==========================================================================
   INPUT PANEL (Search panel)
   ========================================================================== */

.input-panel {
  display: flex;
  align-items: center;
  padding: 0 2rem;
}

.prompt-form-wrapper {
  display: flex;
  width: 100%;
  background-color: var(--color-bg-darker);
  border: 1px solid var(--glass-border);
  border-radius: 10px;
  overflow: hidden;
  transition: var(--transition-smooth);
}

.prompt-form-wrapper:has(input:focus) {
  border-color: var(--color-accent-violet);
  box-shadow: 0 0 15px var(--color-accent-violet-glow);
}

.prompt-form-wrapper input {
  flex: 1;
  background: transparent;
  border: none;
  padding: 1rem 1.5rem;
  color: #fff;
  font-family: var(--font-sans);
  font-size: 0.95rem;
}

.prompt-form-wrapper input:focus {
  outline: none;
}

.btn-send-prompt {
  background: transparent;
  border: none;
  color: var(--color-text-muted);
  padding: 0 1.5rem;
  cursor: pointer;
  transition: var(--transition-smooth);
}

.btn-send-prompt:hover:not(:disabled) {
  color: var(--color-accent-violet);
}

.btn-send-prompt:disabled {
  opacity: 0.25;
  cursor: not-allowed;
}

/* Microphone mode — an explicit three-state pill (Off / Auto / On) that
   replaces the old tap-to-talk + double-tap-to-latch gestures. */
.mic-mode-toggle {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 2px 4px 2px 9px;
  border: 1px solid rgba(168, 92, 219, 0.25);
  border-radius: 999px;
  background: rgba(26, 16, 32, 0.5);
  transition: var(--transition-smooth);
}

.mic-mode-glyph {
  display: flex;
  align-items: center;
  margin-right: 2px;
  color: var(--color-text-muted);
  transition: var(--transition-smooth);
}

.mic-mode-glyph svg {
  width: 16px;
  height: 16px;
}

/* The glyph tracks live mic activity: a soft violet glow once armed, and a
   brighter green when actually hearing speech. */
.mic-mode-toggle.armed .mic-mode-glyph {
  color: var(--color-accent-violet);
  filter: drop-shadow(0 0 5px var(--color-accent-violet));
}

.mic-mode-toggle.hearing .mic-mode-glyph {
  color: var(--color-accent-green);
  filter: drop-shadow(0 0 6px var(--color-accent-green-glow));
}

.mic-mode-opt {
  background: transparent;
  border: none;
  color: var(--color-text-muted);
  font: inherit;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 4px 10px;
  border-radius: 999px;
  cursor: pointer;
  transition: var(--transition-smooth);
}

.mic-mode-opt:hover:not(:disabled):not(.active) {
  color: var(--color-text-main);
  background: rgba(168, 92, 219, 0.12);
}

.mic-mode-opt.active {
  color: #fff;
  background: var(--color-accent-violet);
  box-shadow: 0 0 10px rgba(168, 92, 219, 0.5);
}

/* "Off" reads as a kill switch when it's the active state. */
.mic-mode-opt[data-mode="off"].active {
  background: var(--color-accent-red);
  box-shadow: 0 0 10px var(--color-accent-red-glow);
}

.mic-mode-toggle.disabled,
.mic-mode-opt:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

/* Shush cluster — the Shh! button plus a tight spectrum-analyzer EQ stacked
   beneath it, in the arena's bottom band below the avatar. Visible only while
   GANGLIA is speaking. */
.shush-cluster {
  position: absolute;
  /* Anchored to the transcript-section top (= the arena boundary), nudged down
     past the resizer so it sits just below GANGLIA, never over him. */
  top: 10px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 30;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  animation: shush-rise 0.28s ease;
}

.shush-cluster[hidden] {
  display: none;
}

.shush-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 14px;
  border-radius: 999px;
  border: 1px solid rgba(168, 92, 219, 0.5);
  background: rgba(16, 9, 20, 0.82);
  backdrop-filter: blur(8px);
  color: var(--color-text-main);
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 0 16px rgba(168, 92, 219, 0.3);
  transition: var(--transition-smooth);
}

.shush-btn:hover {
  border-color: var(--color-accent-violet);
  box-shadow: 0 0 22px rgba(168, 92, 219, 0.55);
  transform: translateY(-1px);
}

.shush-icon {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  object-fit: cover;
  display: block;
  /* The art's near-black ground blends into the button; a soft ring frames it. */
  box-shadow: 0 0 0 1px rgba(168, 92, 219, 0.35);
}

/* Tight spectrum-analyzer EQ — thin bars driven by GANGLIA's voice (playback.js
   feeds them from the same analyser as the orb). Bars rest flat between words. */
.shush-eq {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 2px;
  height: 16px;
}

.eq-bar {
  width: 2px;
  height: 2px;
  border-radius: 1px;
  background: linear-gradient(
    to top,
    var(--color-accent-violet),
    var(--color-accent-green)
  );
  box-shadow: 0 0 4px rgba(168, 92, 219, 0.45);
  transition: height 0.07s linear;
}

@keyframes shush-rise {
  from { opacity: 0; transform: translateX(-50%) translateY(6px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

/* Banish — a quick warm flash then snap-to-dark on the avatar so ending a
   session reads as a hard, instant interrupt (filter/scale only; the head-
   tracking rAF owns the transform on the inner rig, so this is safe here). */
.avatar-wrapper.banishing {
  animation: banish-snap 0.34s ease-out;
}

@keyframes banish-snap {
  0%   { filter: brightness(1.7) saturate(0.5) hue-rotate(-12deg); transform: scale(0.965); }
  45%  { filter: brightness(0.5) saturate(0.5); transform: scale(1.012); }
  100% { filter: brightness(1) saturate(1); transform: scale(1); }
}

/* Send now — at the right edge of the live (interim) transcript line. The
   interim bubble lays content + button out on one row; CSS hides the button
   the moment the bubble locks (loses .interim). */
.interim-line {
  display: flex;
  align-items: center;
  gap: 8px;
}

.send-now-btn {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 11px;
  border-radius: 999px;
  border: 1px solid rgba(168, 92, 219, 0.5);
  background: rgba(168, 92, 219, 0.16);
  color: var(--color-text-main);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  white-space: nowrap;
  cursor: pointer;
  transition: var(--transition-smooth);
  animation: send-now-pop 0.2s ease;
}

.send-now-btn:hover {
  background: var(--color-accent-violet);
  color: #fff;
  box-shadow: 0 0 12px rgba(168, 92, 219, 0.5);
}

/* Only meaningful while the utterance is still live. */
.message-bubble.user:not(.interim) .send-now-btn {
  display: none;
}

@keyframes send-now-pop {
  from { opacity: 0; transform: scale(0.9); }
  to   { opacity: 1; transform: scale(1); }
}

.send-icon {
  width: 20px;
  height: 20px;
}

/* ==========================================================================
   DYNAMIC QUEST EFFECT ALERTS OVERLAY
   ========================================================================== */

.quest-alert-overlay {
  position: fixed;
  top: 2rem;
  right: 2rem;
  z-index: 999;
  pointer-events: none;
  transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity 0.3s;
  opacity: 0;
  transform: translateY(-20px) scale(0.9);
}

.quest-alert-overlay:not(.inert) {
  opacity: 1;
  transform: translateY(0) scale(1);
}

.quest-card {
  background: rgba(12, 8, 15, 0.85);
  border: 1px solid var(--color-accent-red);
  backdrop-filter: blur(16px);
  padding: 1rem 1.5rem;
  border-radius: 10px;
  display: flex;
  align-items: center;
  gap: 1rem;
  box-shadow: 0 0 25px var(--color-accent-red-glow);
}

.quest-icon {
  font-size: 1.8rem;
  animation: shake 0.5s infinite;
}

.quest-body h4 {
  font-size: 0.85rem;
  color: var(--color-accent-red);
  letter-spacing: 0.15em;
  font-weight: 900;
  text-transform: uppercase;
}

.quest-body p {
  font-size: 0.75rem;
  color: #fff;
  margin-top: 0.15rem;
}

/* Positive variant (e.g. a successful purchase) — light blue-purple (matching
   the human-voice bubble palette), not alarm red, with a calm icon so it doesn't
   read as an error. */
.quest-alert-overlay.success .quest-card {
  border-color: #cc9cff;
  box-shadow: 0 0 25px rgba(190, 140, 255, 0.4);
}

.quest-alert-overlay.success .quest-body h4 {
  color: #d9bcff;
}

.quest-alert-overlay.success .quest-icon {
  animation: none;
}

/* ==========================================================================
   ANIMATIONS & KEYFRAMES
   ========================================================================== */

@keyframes breathe {
  0%, 100% {
    transform: scale(1);
    opacity: 0.7;
  }
  50% {
    transform: scale(1.05);
    opacity: 1;
    box-shadow: 0 0 25px rgba(187, 58, 237, 0.08);
  }
}

@keyframes spin-clockwise {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

@keyframes spin-counterclockwise {
  from { transform: rotate(360deg); }
  to { transform: rotate(0deg); }
}

@keyframes bounce-bars {
  0%, 100% {
    transform: scaleY(1);
    height: 5px;
  }
  50% {
    transform: scaleY(5.5);
    height: 6px;
    background: #f472b6;
  }
}

@keyframes pulse-green {
  0% {
    box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7);
  }
  70% {
    box-shadow: 0 0 0 6px rgba(16, 185, 129, 0);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(16, 185, 129, 0);
  }
}

@keyframes pulse-violet {
  0% {
    box-shadow: 0 0 0 0 rgba(187, 58, 237, 0.7);
  }
  70% {
    box-shadow: 0 0 0 6px rgba(187, 58, 237, 0);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(187, 58, 237, 0);
  }
}

@keyframes pulse-magenta {
  0% {
    box-shadow: 0 0 0 0 rgba(244, 114, 182, 0.7);
  }
  70% {
    box-shadow: 0 0 0 6px rgba(244, 114, 182, 0);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(244, 114, 182, 0);
  }
}

@keyframes pulse-border-green {
  0%, 100% { border-color: rgba(16, 185, 129, 0.2); }
  50% { border-color: rgba(16, 185, 129, 0.6); }
}

@keyframes slide-in-bubble {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes shake {
  0%, 100% { transform: rotate(0deg); }
  25% { transform: rotate(-10deg); }
  75% { transform: rotate(10deg); }
}

/* ==========================================================================
   RESPONSIVE DESIGN
   ========================================================================== */

@media (max-width: 900px) {
  .app-layout {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr;
  }
  
  .setup-sidebar {
    height: auto;
    border-right: none;
    border-bottom: 1px solid var(--glass-border);
    padding: 1.5rem;
    gap: 1.5rem;
  }
  
  .active-workspace {
    grid-template-rows: 128px 46px var(--avatar-height, 26vh) 1fr 76px;
    height: 100vh;
  }
  
  .avatar-wrapper {
    width: 230px;
    height: 300px;
  }

  .ganglia-rig {
    width: 200px;
    height: 280px;
  }

  /* Smaller arena + avatar on mobile: tuck the flank buttons into the bottom
     corners so they don't overlap the face. */
  .flank-btn {
    top: auto;
    bottom: 0.5rem;
    transform: none;
    width: 76px;
    padding: 0.6rem 0.5rem;
    font-size: 0.7rem;
  }
  .flank-summon { left: 0.5rem; }
  .flank-banish { right: 0.5rem; }
  .flank-summon:hover:not(:disabled),
  .flank-banish:hover:not(:disabled) { transform: scale(1.05); }
  .flank-btn .flank-icon { font-size: 1.3rem; }
}

/* ==========================================================================
   TRANSCRIPT DRAGGABLE RESIZER
   ========================================================================== */

.transcript-section {
  position: relative; /* Anchor for absolute resizer positioning */
}

.transcript-resizer {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 6px;
  cursor: ns-resize;
  background-color: transparent;
  z-index: 100;
  transition: background-color var(--transition-smooth);
}

.transcript-resizer:hover,
.transcript-resizer.dragging {
  background-color: var(--color-accent-violet);
  box-shadow: 0 0 10px var(--color-accent-violet-glow);
}

/* ==========================================================================
   SUMMONS ECONOMY & OCCULT MARKETPLACE
   ========================================================================== */

/* Summons Tracker Badge in Sidebar */
.summons-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: linear-gradient(135deg in oklab, rgba(187, 58, 237, 0.15) 0%, rgba(244, 114, 182, 0.08) 100%);
  border: 1px solid rgba(187, 58, 237, 0.25);
  border-radius: 20px;
  padding: 6px 14px;
  margin-top: 0.75rem;
  box-shadow: 0 0 10px rgba(187, 58, 237, 0.08);
  align-self: center;
}

/* Hand-styled full-moon token (a CSS disc, not an emoji): a pale cream moon
   with a soft glow and faint maria, used for the summons pile and the coins
   that fall into the piggy bank. */
.moon-token {
  display: inline-block;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: radial-gradient(
    circle at 35% 30%,
    #fffdf2 0%,
    #f1eedd 42%,
    #d7d3bd 78%,
    #b9b5a0 100%
  );
  box-shadow:
    0 0 8px rgba(245, 243, 225, 0.6),
    inset -2px -2px 4px rgba(110, 106, 84, 0.5),
    inset 2px 2px 3px rgba(255, 255, 255, 0.5);
  position: relative;
  flex: 0 0 auto;
}

/* Faint maria so it reads as a moon, not a plain ball. */
.moon-token::before,
.moon-token::after {
  content: "";
  position: absolute;
  border-radius: 50%;
  background: rgba(150, 145, 116, 0.38);
}

.moon-token::before { width: 26%; height: 26%; top: 24%; left: 46%; }
.moon-token::after { width: 18%; height: 18%; top: 56%; left: 24%; }

/* A pile of full-moon "summons" — one moon per available minute (capped),
   overlapping like stacked coins, with the top one floating. */
.moon-pile {
  display: inline-flex;
  align-items: center;
}

.moon-pile .moon-token {
  margin-left: -7px;
}

.moon-pile .moon-token:first-child {
  margin-left: 0;
}

.moon-pile .moon-token.moon-float {
  animation: float 2.5s ease-in-out infinite;
}

/* Compact twin of .summons-count for the phone chip (account_ui.js writes
   both); desktop shows the verbose label, phone-ui swaps to this one. */
.summons-count-short {
  display: none;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  color: #fff;
  letter-spacing: 0.04em;
  text-shadow: 0 0 4px var(--color-accent-violet-glow);
  white-space: nowrap;
}

.summons-count {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 700;
  color: #fff;
  letter-spacing: 0.05em;
  text-shadow: 0 0 4px var(--color-accent-violet-glow);
}

/* Small link beneath the summons display that opens the Emporium popup */
.emporium-link {
  display: inline-block;
  margin-top: 0.5rem;
  align-self: center;
  background: none;
  border: none;
  padding: 2px 4px;
  font-family: var(--font-sans);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--color-accent-violet);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
  opacity: 0.85;
  transition: var(--transition-smooth);
}

.emporium-link:hover {
  opacity: 1;
  color: #ea84fc;
  text-shadow: 0 0 6px var(--color-accent-violet-glow);
}

/* Occult Marketplace Storefront (lives inside the Emporium popup) */
.marketplace-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem;
  margin-top: 0.5rem;
}

.pack-card {
  background: rgba(18, 12, 22, 0.4);
  border: 1px solid var(--glass-border);
  border-radius: 8px;
  padding: 0.75rem 0.5rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.25rem;
  cursor: pointer;
  transition: var(--transition-smooth);
  position: relative;
  overflow: hidden;
}

.pack-card:hover:not(:disabled) {
  border-color: var(--color-accent-violet);
  background: rgba(187, 58, 237, 0.08);
  box-shadow: 0 0 12px var(--color-accent-violet-glow);
  transform: translateY(-2px);
}

.pack-card:active:not(:disabled) {
  transform: translateY(0);
}

.pack-card:disabled {
  opacity: 0.35;
  cursor: not-allowed;
  transform: none !important;
  box-shadow: none !important;
}

.pack-title {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
  color: var(--color-text-muted);
}

.pack-card:hover:not(:disabled) .pack-title {
  color: #fff;
}

.pack-count {
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 900;
  color: var(--color-accent-violet);
  text-shadow: 0 0 6px var(--color-accent-violet-glow);
}

.pack-card[data-pack="lich"] .pack-count {
  color: var(--color-accent-red);
  text-shadow: 0 0 8px var(--color-accent-red-glow);
}

.pack-price {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--color-text-dimmed);
}

/* Modals & Overlays */
.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background-color: rgba(7, 3, 8, 0.85);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 5000;
  transition: opacity 0.4s ease;
  opacity: 1;
}

.modal-overlay.inert {
  opacity: 0;
  pointer-events: none;
}

.modal-card {
  background: var(--color-bg-dark);
  border: 2px solid rgba(239, 68, 68, 0.3); /* Spooky red tint */
  box-shadow: 0 0 45px rgba(239, 68, 68, 0.15), inset 0 0 20px rgba(18, 12, 22, 0.9);
  border-radius: 16px;
  width: 380px;
  padding: 2.25rem 2rem;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  animation: slide-in-bubble 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
}

.modal-title {
  font-family: 'Orbitron', sans-serif;
  font-size: 1.25rem;
  color: var(--color-accent-red);
  text-shadow: 0 0 12px rgba(239, 68, 68, 0.6);
  text-align: center;
  letter-spacing: 0.05em;
}

.modal-desc {
  font-size: 0.8rem;
  color: var(--color-text-muted);
  line-height: 1.5;
  text-align: center;
}

.modal-form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

/* Checkout/billing failure surfaced in the emporium modal (e.g. server has no
   Stripe config) — visible to the seeker, not just buried in the system log. */
.emporium-error {
  margin-top: 1rem;
  padding: 0.6rem 0.8rem;
  border: 1px solid rgba(255, 90, 120, 0.5);
  border-radius: 8px;
  background: rgba(255, 90, 120, 0.08);
  color: #ff8aa0;
  font-size: 0.8rem;
  line-height: 1.45;
  text-align: center;
}

.btn-cancel {
  width: 100%;
  background: transparent;
  border: 1px solid var(--glass-border);
  color: var(--color-text-muted);
  padding: 0.75rem;
  border-radius: 8px;
  font-family: var(--font-sans);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  transition: var(--transition-smooth);
  text-align: center;
}

.btn-cancel:hover {
  background: rgba(255, 255, 255, 0.03);
  color: #fff;
  border-color: var(--color-text-muted);
}

/* Sacrificial Payment Gateway Loader */
.sacrificial-loader {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.5rem;
  text-align: center;
  max-width: 450px;
  padding: 2rem;
}

.pentagram-glow {
  font-size: 4rem;
  animation: spin-clockwise 4s linear infinite, glow-pulse-red 2s ease-in-out infinite;
  text-shadow: 0 0 25px rgba(239, 68, 68, 0.7);
}

.sacrificial-title {
  font-family: 'Orbitron', sans-serif;
  font-size: 1.15rem;
  color: var(--color-accent-red);
  text-shadow: 0 0 15px rgba(239, 68, 68, 0.6);
  letter-spacing: 0.08em;
  animation: pulse-light 1.5s infinite;
}

.sacrificial-desc {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--color-text-muted);
  letter-spacing: 0.05em;
  line-height: 1.6;
}

/* Keyframe animations */
@keyframes glow-pulse-red {
  0%, 100% { filter: drop-shadow(0 0 10px rgba(239, 68, 68, 0.4)); }
  50% { filter: drop-shadow(0 0 30px rgba(239, 68, 68, 0.9)); }
}

@keyframes pulse-light {
  0%, 100% { opacity: 0.8; }
  50% { opacity: 1; }
}

@keyframes float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-4px); }
}

.btn-secondary {
  flex: 1;
  padding: 0.75rem;
  border-radius: 8px;
  background: transparent;
  border: 1px solid var(--color-accent-violet);
  color: var(--color-accent-violet);
  font-family: var(--font-sans);
  font-size: 0.9rem;
  font-weight: 700;
  cursor: pointer;
  transition: var(--transition-smooth);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
}

.btn-secondary:hover:not(:disabled) {
  background: var(--color-accent-violet);
  color: #fff;
  box-shadow: 0 0 15px var(--color-accent-violet-glow);
}

.btn-secondary:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* ── Google sign-in / account panel ───────────────────────────── */
.google-signin-button {
  display: flex;
  justify-content: center;
  min-height: 44px;
}

.signin-fallback {
  font-size: 0.75rem;
  color: var(--color-text-dimmed);
  text-align: center;
}

.account-signed-out {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.85rem;
  margin-bottom: 1rem;
}

.account-prompt {
  font-size: 0.78rem;
  line-height: 1.4;
  color: var(--color-text-dimmed);
  text-align: center;
  margin: 0;
}

.account-panel {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  margin-bottom: 1rem;
  border-radius: 12px;
  background: rgba(16, 185, 129, 0.08);
  border: 1px solid rgba(16, 185, 129, 0.25);
}

.account-avatar {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--color-accent-violet);
  background-size: cover;
  background-position: center;
  flex-shrink: 0;
}

.account-meta {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
}

.account-name {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--color-text-main);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.account-email {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--color-text-dimmed);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.account-signout {
  background: transparent;
  border: none;
  color: var(--color-text-dimmed);
  font-size: 0.68rem;
  font-weight: 600;
  cursor: pointer;
  padding: 2px 4px;
  flex-shrink: 0;
  transition: var(--transition-smooth);
}

.account-signout:hover {
  color: var(--color-accent-red);
}

.auth-divider {
  display: flex;
  align-items: center;
  text-align: center;
  color: var(--color-text-dimmed);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin: 1rem 0;
}

.auth-divider::before,
.auth-divider::after {
  content: "";
  flex: 1;
  border-bottom: 1px solid var(--color-text-dimmed);
  opacity: 0.3;
}

.auth-divider span {
  padding: 0 0.75rem;
}

.local-auth-form {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  width: 100%;
}

.local-auth-submit {
  width: 100%;
  margin-top: 0.25rem;
}

/* Side-by-side Sign In / Create Account buttons. */
.auth-button-row {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.25rem;
}

.auth-button-row .btn {
  flex: 1 1 0;
  min-width: 0;
  white-space: nowrap;
}

.local-auth-note {
  font-size: 0.72rem;
  line-height: 1.35;
  color: var(--color-accent-red);
  text-align: center;
  margin: 0;
  min-height: 0.9rem;
}

.field-hint {
  font-size: 0.68rem;
  font-weight: 400;
  color: var(--color-text-muted, #a592ad);
  opacity: 0.8;
}

/* Username is display-only: it appears only while creating an account, never
   when signing in. Driven by the form's data-auth-mode attribute. */
.local-auth-form .create-only,
.modal-form .create-only {
  display: none;
}

.local-auth-form[data-auth-mode="register"] .create-only,
.modal-form[data-auth-mode="register"] .create-only {
  display: block;
}

/* "New to the coven? Create an account" / "Already initiated? Sign in" line. */
.auth-mode-switch {
  font-size: 0.74rem;
  color: var(--color-text-dimmed);
  text-align: center;
  margin: 0.1rem 0 0;
}

.auth-mode-switch .auth-mode-toggle {
  margin-left: 0.35rem;
}

/* Inline "edit username" affordance in the signed-in account panel. */
.account-edit-username {
  background: transparent;
  border: none;
  color: var(--color-text-dimmed);
  font-size: 0.68rem;
  font-weight: 600;
  cursor: pointer;
  padding: 2px 4px;
  flex-shrink: 0;
  transition: var(--transition-smooth);
}

.account-edit-username:hover {
  color: #dd88ff;
}

.edit-username-form {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  width: 100%;
  margin-top: 0.6rem;
}

.link-button {
  background: none;
  border: none;
  padding: 0;
  color: #dd88ff;
  font-size: 0.74rem;
  cursor: pointer;
  text-decoration: underline;
  align-self: center;
}

.link-button:hover {
  color: #ecbaff;
}

.forgot-password-link {
  margin-top: 0.1rem;
}

.unverified-notice {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.75rem;
  padding: 0.6rem 0.7rem;
  border: 1px solid #5a4a2a;
  background: rgba(120, 90, 30, 0.18);
  border-radius: 6px;
  font-size: 0.74rem;
  line-height: 1.4;
  color: #e6d49c;
}

.unverified-notice .unverified-icon {
  font-size: 1rem;
  flex-shrink: 0;
}

/* ==========================================================================
   VIEW MODE OVERRIDES (?site_type=mobile|desktop)
   ========================================================================== */

/* Force Mobile View on Desktop */
body.force-mobile {
  /* Constrain the body width and center it to simulate a phone screen */
  max-width: 400px;
  margin: 0 auto;
  border-left: 1px solid var(--glass-border);
  border-right: 1px solid var(--glass-border);
  box-shadow: 0 0 50px rgba(0,0,0,0.8);
  height: 100vh;
  height: 100dvh;
  position: relative;
  overflow-x: hidden;
}



/* ============================================================
   The Antechamber status (ADR 003)
   A persistent header pill: empty = chamber open (summon
   freely); occupied = a séance/line; queued = your place in
   line. Waiting is free.
   ============================================================ */
.antechamber-status {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 5px 12px;
  border-radius: 999px;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 600;
  white-space: nowrap;
  border: 1px solid rgba(175, 130, 200, 0.3);
  background: rgba(27, 14, 34, 0.6);
  color: #d5bfe0;
  transition: background 0.5s ease, border-color 0.5s ease,
    color 0.5s ease, box-shadow 0.5s ease;
}
.antechamber-status[hidden] { display: none; }
.antechamber-glyph { font-size: 0.95rem; line-height: 1; }

/* Open: the chamber awaits — inviting green glow, gentle breathing. */
.antechamber-status.open {
  border-color: rgba(120, 220, 170, 0.45);
  color: #aef0c8;
  background: rgba(16, 34, 26, 0.55);
  animation: antechamber-breathe 3.6s ease-in-out infinite;
}
@keyframes antechamber-breathe {
  0%, 100% { box-shadow: 0 0 12px rgba(70, 200, 140, 0.14); }
  50%      { box-shadow: 0 0 20px rgba(70, 200, 140, 0.30); }
}

/* Occupied: a séance is underway / souls wait — amber watching eye. */
.antechamber-status.occupied {
  border-color: rgba(220, 160, 90, 0.45);
  color: #f0cfa0;
  background: rgba(34, 24, 12, 0.55);
  box-shadow: 0 0 12px rgba(210, 140, 60, 0.16);
}

/* Queued: YOU wait in line — candle flame, slow flicker. */
.antechamber-status.queued {
  border-color: rgba(190, 120, 230, 0.55);
  color: #fbc4ff;
  background: rgba(37, 14, 40, 0.7);
  box-shadow: 0 0 16px rgba(219, 90, 230, 0.28);
  animation: antechamber-flicker 2.2s ease-in-out infinite;
}
@keyframes antechamber-flicker {
  0%, 100% { opacity: 1; }
  45%      { opacity: 0.82; }
  60%      { opacity: 0.95; }
}

/* Advance: replayed each time your position changes. */
.antechamber-status.advance { animation: antechamber-advance 0.7s ease; }
@keyframes antechamber-advance {
  0%   { transform: scale(1);    box-shadow: 0 0 16px rgba(219, 90, 230, 0.28); }
  40%  { transform: scale(1.08); box-shadow: 0 0 28px rgba(232, 120, 240, 0.55); }
  100% { transform: scale(1);    box-shadow: 0 0 16px rgba(219, 90, 230, 0.28); }
}

/* Summoned: a brief full-screen veil-parting flash as your séance begins. */
body.seance-summoned-flash::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  background: radial-gradient(circle at 50% 45%,
    rgba(232, 120, 240, 0.45), rgba(176, 40, 200, 0.18) 40%, transparent 70%);
  animation: summoned-flash 1.6s ease forwards;
}
@keyframes summoned-flash {
  0%   { opacity: 0; }
  18%  { opacity: 1; }
  100% { opacity: 0; }
}


/* ---- Summon flank button: two-step chamber ritual (ADR 003) ---- */
.flank-sub {
  display: block;
  font-size: 0.55rem;
  letter-spacing: 0.06em;
  opacity: 0.85;
  margin-top: 2px;
}
.flank-sub[hidden] { display: none; }

/* Holding the chamber — the button glows and breathes, urging you to summon. */
.flank-summon.chamber-holding {
  animation: summon-urge 1.6s ease-in-out infinite;
  border-color: rgba(232, 120, 240, 0.7);
}
@keyframes summon-urge {
  0%, 100% { box-shadow: 0 0 14px rgba(219, 90, 230, 0.30); }
  50%      { box-shadow: 0 0 26px rgba(232, 120, 240, 0.60); }
}

/* Waiting in line — dimmed and non-inviting. */
.flank-summon.chamber-waiting {
  opacity: 0.6;
  filter: grayscale(0.3);
  cursor: default;
}

/* ---- Antechamber backdrop (ADR 003) ---- */
/* A candlelit hall of waiting souls, behind the workspace, that fades in while
   you wait in the line or hold the chamber. Subtle so the UI stays readable. */
.antechamber-backdrop {
  position: fixed;
  inset: 0;
  z-index: 0;
  background: url('assets/antechamber_bg.jpg?v=1') center/cover no-repeat;
  opacity: 0;
  pointer-events: none;
  transition: opacity 1.4s ease;
  filter: brightness(0.7) saturate(0.95);
}
body.in-antechamber .antechamber-backdrop { opacity: 0.6; }
/* A slow drift so the hall feels alive while you wait. */
body.in-antechamber .antechamber-backdrop {
  animation: antechamber-drift 32s ease-in-out infinite alternate;
}
@keyframes antechamber-drift {
  0%   { transform: scale(1.04) translate(0, 0); }
  100% { transform: scale(1.10) translate(-1.5%, -1%); }
}

/* ==========================================================================
   PHONE UI — the pocket séance (chat-first phone layout)

   app.js sets body.phone-ui when the viewport is phone-sized (≤640px) OR the
   ?site_type=mobile simulator (force-mobile) is on, so every phone rule lives
   here exactly once — no media-query + force-mobile mirror blocks.

   Principles:
   · Nothing in the chrome is absolutely positioned → nothing can overlap.
   · GANGLIA is a small living medallion (jaw-sync + aura states intact)
     beside thumb-sized actions — the transcript owns the screen.
   · 100dvh + safe-area insets so mobile browser chrome never hides the input.
   ========================================================================== */

body.phone-ui { overflow-x: hidden; }

body.phone-ui .app-layout {
  height: 100vh;
  height: 100dvh;
}

body.phone-ui .active-workspace {
  min-width: 0;
  grid-template-columns: minmax(0, 1fr);
  /* brand / status / presence row / transcript (everything left) / input */
  grid-template-rows: auto auto auto minmax(0, 1fr) auto;
  height: 100vh;
  height: 100dvh;
}

body.phone-ui .top-banner,
body.phone-ui .workspace-header,
body.phone-ui .avatar-arena,
body.phone-ui .transcript-section,
body.phone-ui .input-panel {
  min-width: 0;
  max-width: 100%;
}

/* --- Brand bar: ONE row, all in normal document flow. ---------------------- */
body.phone-ui .top-banner {
  flex-direction: row;
  align-items: center;
  gap: 0.6rem;
  padding: 0.45rem 0.7rem;
  text-align: left;
}

body.phone-ui .banner-brand {
  order: -1; /* brand leads the row; corners follow in-flow */
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}

body.phone-ui .banner-brand .sidebar-logo {
  width: 30px;
  height: 30px;
  margin: 0;
}

body.phone-ui .sidebar-title {
  font-size: 1rem;
  letter-spacing: 0.14em;
  white-space: nowrap;
}

body.phone-ui .sidebar-subtitle { display: none; }

body.phone-ui .banner-corner {
  position: static;
  max-width: none;
  gap: 0.2rem;
}

body.phone-ui .banner-left {
  margin-left: auto; /* push the chip cluster + account to the right edge */
  align-items: flex-end;
}

body.phone-ui .banner-right { align-items: flex-end; }

body.phone-ui .summons-badge {
  margin: 0;
  padding: 3px 9px;
  gap: 5px;
}

/* Compact chip: cap the moon pile at three and swap to the short count. */
body.phone-ui .moon-pile .moon-token:nth-child(n + 4) { display: none; }
body.phone-ui .summons-count { display: none; }
body.phone-ui .summons-count-short { display: inline; }
body.phone-ui .emporium-link { font-size: 0.58rem; }

body.phone-ui .account-toggle {
  height: 32px;
  padding: 0 0.75rem;
  font-size: 0.74rem;
}

/* Anchored popover overflows a phone — make it a near-full-width sheet. */
body.phone-ui .account-popover {
  position: fixed;
  top: 54px;
  left: 0.6rem;
  right: 0.6rem;
  width: auto;
  max-height: 78vh;
  max-height: 78dvh;
}

/* --- Status strip: one thin row, séance meter in-flow (not absolute). ------ */
body.phone-ui .workspace-header {
  padding: 0.3rem 0.7rem;
  gap: 0.5rem;
}

body.phone-ui .antechamber-status {
  font-size: 0.58rem;
  padding: 3px 9px;
  gap: 0.3rem;
  min-width: 0;
}

body.phone-ui .antechamber-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

body.phone-ui .antechamber-glyph { font-size: 0.8rem; }

body.phone-ui .seance-meter {
  position: static;
  transform: none;
  gap: 0.55rem;
  margin-left: auto;
}

/* When the meter is hidden (no séance), the state badge takes the right edge. */
body.phone-ui .seance-meter[hidden] + .state-badge { margin-left: auto; }

body.phone-ui .grave-icon { width: 2rem; height: 2rem; }
body.phone-ui .countdown-time { font-size: 0.88rem; }
body.phone-ui .piggy-count { font-size: 0.66rem; }

body.phone-ui .state-text { font-size: 0.58rem; letter-spacing: 0.02em; }
body.phone-ui .state-badge { gap: 0.35rem; flex: 0 0 auto; }
/* The connection dot's color says it all — drop its label on a phone. */
body.phone-ui .connection-status-badge { gap: 0; flex: 0 0 auto; }
body.phone-ui .conn-text { display: none; }

/* --- Presence row: a living medallion beside thumb-size actions. -----------
   Same DOM (summon · avatar · banish), re-flowed into a grid:
       [ avatar ] [ Enter Chamber / Summon ]
       [ avatar ] [ Banish               ] */
body.phone-ui .avatar-arena {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: 1fr 1fr;
  grid-template-areas:
    "avatar summon"
    "avatar banish";
  column-gap: 0.8rem;
  row-gap: 0.5rem;
  padding: 0.6rem 0.75rem;
  align-items: stretch;
}

body.phone-ui .avatar-wrapper {
  grid-area: avatar;
  align-self: center;
  width: 96px;
  height: 128px;
}

body.phone-ui .ganglia-rig {
  width: 86px;
  height: 120px;
}

body.phone-ui .flank-btn {
  position: static;
  top: auto;
  bottom: auto;
  left: auto;
  right: auto;
  transform: none;
  width: 100%;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  gap: 0.55rem;
  padding: 0.5rem 0.75rem;
  border-radius: 12px;
  font-size: 0.76rem;
}

body.phone-ui .flank-summon { grid-area: summon; }
body.phone-ui .flank-banish { grid-area: banish; }

body.phone-ui .flank-summon:hover:not(:disabled),
body.phone-ui .flank-banish:hover:not(:disabled) { transform: scale(1.02); }

body.phone-ui .flank-btn .flank-icon { font-size: 1.15rem; }
body.phone-ui .flank-sub { margin-top: 0; }

/* --- Transcript: the dominant area. ---------------------------------------- */
body.phone-ui .transcript-section { padding: 0.65rem 0.9rem; }
/* Fixed layout on phones — no drag-resize handle. */
body.phone-ui .transcript-resizer { display: none; }
body.phone-ui .message-bubble { max-width: 90%; }
/* Orbitron is a display font — at desktop size one reply swallows a phone
   screen. ~0.78rem is its readable floor; tighter rhythm keeps the drip. */
body.phone-ui .message-bubble.assistant .message-content {
  font-size: 0.78rem;
  line-height: 1.55;
  letter-spacing: 0.03em;
}
body.phone-ui .message-bubble.user .message-content { font-size: 0.85rem; }

/* --- Input bar: thumb-friendly, safe-area aware. ---------------------------- */
body.phone-ui .input-panel {
  padding: 0.45rem 0.65rem;
  padding-bottom: calc(0.45rem + env(safe-area-inset-bottom, 0px));
}

body.phone-ui .prompt-form-wrapper input {
  min-width: 0;
  padding: 0.7rem 0.85rem;
  /* 16px floor stops iOS Safari's zoom-on-focus jump. */
  font-size: 16px;
}

body.phone-ui .btn-send-prompt { padding: 0 0.85rem; }
body.phone-ui .mic-mode-glyph { display: none; }
body.phone-ui .mic-mode-toggle { gap: 0; padding: 2px 3px; }
body.phone-ui .mic-mode-opt { padding: 6px 8px; font-size: 0.66rem; }

/* --- Overlays: fit small screens. ------------------------------------------- */
body.phone-ui .modal-card {
  width: min(380px, calc(100vw - 1.5rem));
  padding: 1.5rem 1.1rem;
  max-height: 88vh;
  max-height: 88dvh;
  overflow-y: auto;
}

/* The debug overlay's thought-bubble easter egg is a desktop nicety — in the
   compact presence row it paints over the action buttons. */
body.phone-ui .ggl-thought-bubble { display: none; }

/* Legal footer — quiet links under the séance input */
.legal-footer {
  display: flex;
  justify-content: center;
  gap: 0.6rem;
  padding: 0.8rem 0 max(0.8rem, env(safe-area-inset-bottom));
  font-size: 0.72rem;
  color: var(--color-text-dimmed);
}
.legal-footer a {
  color: var(--color-text-dimmed);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
.legal-footer a:hover {
  color: var(--color-text-muted);
  border-bottom-color: var(--color-accent-violet-glow);
}


/* --- Mic listening cue: breathing glow on the chat bar + live equalizer ---- */
.prompt-form-wrapper { position: relative; transition: box-shadow .3s ease; }
.prompt-form-wrapper.mic-live {
  animation: mic-breathe 2.6s ease-in-out infinite;
}
@keyframes mic-breathe {
  0%, 100% { box-shadow: 0 0 0 1px rgba(221, 136, 255, 0.35), 0 0 16px rgba(187, 58, 237, 0.30); }
  50%      { box-shadow: 0 0 0 1px rgba(221, 136, 255, 0.75), 0 0 36px rgba(187, 58, 237, 0.60); }
}
@media (prefers-reduced-motion: reduce) {
  .prompt-form-wrapper.mic-live {
    animation: none;
    /* steady glow so "mic is live" still reads without the breathing motion */
    box-shadow: 0 0 0 1px rgba(221, 136, 255, 0.6), 0 0 24px rgba(187, 58, 237, 0.45);
  }
}
.mic-eq {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  opacity: 0;
  transition: opacity .3s ease;
}
.prompt-form-wrapper.mic-live .mic-eq { opacity: 0.95; }
/* Hide the placeholder while listening so it doesn't sit behind the equalizer. */
.prompt-form-wrapper.mic-live #prompt-input::placeholder { color: transparent; }

/* ======================================================================
   Mobile bottom navigation (phone-ui only)
   ----------------------------------------------------------------------
   On a phone the Quests directory and the account panel used to live in
   the top chrome, where they overlapped the Summon/Banish flow. Here they
   become bottom SHEETS opened from a fixed two-tab bar (Quests · You), so
   the centre of the screen is always the séance. Desktop is untouched: the
   bar is display:none and mobile_nav.js's handlers simply never fire.
   ====================================================================== */
.mobile-tabbar,
.mobile-sheet-scrim { display: none; }

body.phone-ui { --mobile-tabbar-h: 54px; }

/* Keep the fixed bar from covering the input row / last bit of transcript. */
body.phone-ui .active-workspace {
  padding-bottom: calc(var(--mobile-tabbar-h) + env(safe-area-inset-bottom, 0px));
}

body.phone-ui .mobile-tabbar {
  display: flex;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  height: calc(var(--mobile-tabbar-h) + env(safe-area-inset-bottom, 0px));
  padding-bottom: env(safe-area-inset-bottom, 0px);
  z-index: 120;
  background: rgba(15, 8, 18, 0.96);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  border-top: 1px solid var(--glass-border, rgba(255, 255, 255, 0.08));
  box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.45);
}

.mobile-tab {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  position: relative;
  background: transparent;
  border: 0;
  color: var(--text-dim, #b9a8c8);
  font: inherit;
  font-size: 0.6rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  cursor: pointer;
}
.mobile-tab .mobile-tab-icon { font-size: 1.15rem; line-height: 1; }
.mobile-tab[aria-expanded="true"] { color: var(--color-accent-violet, #c08cff); }
.mobile-tab[aria-expanded="true"]::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 30px;
  height: 2px;
  border-radius: 0 0 3px 3px;
  background: var(--color-accent-violet, #c08cff);
}
.mobile-tab-badge {
  position: absolute;
  top: 5px;
  right: calc(50% - 26px);
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  line-height: 16px;
  font-size: 0.54rem;
  letter-spacing: 0;
  text-align: center;
  border-radius: 8px;
  background: var(--color-accent-violet, #c08cff);
  color: #150b18;
}
.mobile-tab-badge[hidden] { display: none; }

/* Scrim behind an open sheet — sits ABOVE content but BELOW the tab bar, so
   the tabs stay tappable (you can switch sheets without closing first). */
body.phone-ui .mobile-sheet-scrim:not([hidden]) {
  display: block;
  position: fixed;
  inset: 0;
  bottom: calc(var(--mobile-tabbar-h) + env(safe-area-inset-bottom, 0px));
  z-index: 100;
  background: rgba(0, 0, 0, 0.55);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}

/* Shared bottom-sheet geometry for the quest directory + the account panel.
   Both ride just above the tab bar and slide up when their body class is set. */
body.phone-ui #quest-directory,
body.phone-ui .account-popover {
  position: fixed;
  top: auto;
  left: 0;
  right: 0;
  bottom: calc(var(--mobile-tabbar-h) + env(safe-area-inset-bottom, 0px));
  width: 100%;
  max-width: 100%;
  max-height: 72dvh;
  overflow-y: auto;
  border-radius: 18px 18px 0 0;
  transform: translateY(110%);
  transition: transform 0.28s cubic-bezier(0.2, 0.7, 0.2, 1);
  opacity: 1; /* neutralize the desktop .inert opacity hide */
  pointer-events: none;
  z-index: 110;
  animation: none; /* the slide replaces the desktop pop-in */
  /* Opaque panel so the sheet's own content reads crisply over the dimmed
     scrim — the desktop glass (translucent) looked frosted as a full sheet. */
  background: rgba(18, 9, 22, 0.99);
  box-shadow: 0 -14px 40px rgba(0, 0, 0, 0.6);
}
/* The account popover hides via .inert (opacity:0) on desktop — on a phone we
   keep it laid out and slide it off-screen instead, driven by the body class. */
body.phone-ui .account-popover.inert {
  opacity: 1;
  transform: translateY(110%);
}
body.phone-ui.sheet-quests #quest-directory,
body.phone-ui.sheet-account .account-popover,
body.phone-ui.sheet-account .account-popover.inert {
  transform: translateY(0);
  pointer-events: auto;
}

/* In the sheet the whole list is always shown; the inline collapse chevron is
   redundant (the sheet opens/closes from the tab bar + scrim). */
body.phone-ui .quest-dir-chevron { display: none; }
body.phone-ui.sheet-quests #quest-directory.collapsed .quest-dir-list { display: block; }

/* The old top-right account control is replaced by the "You" tab. */
body.phone-ui .account-toggle { display: none; }

/* CRITICAL for the account sheet: .banner-right is a FLEX ITEM of .top-banner
   with z-index:60 (desktop layering). A flex item with a non-auto z-index forms
   a stacking context EVEN when position:static — so the account popover was
   trapped in .banner-right's z-index:60 context and painted BELOW the scrim
   (z-index:100) no matter its own z-index. The quest sheet isn't inside a flex
   item, which is why only the account sheet frosted. Drop the z-index on phone
   so the popover escapes to the .app-layout stacking context, above the scrim. */
body.phone-ui .banner-right { z-index: auto; }

/* --- Desktop "force-mobile" simulator (?site_type=mobile) -------------------
   Here the body is a 400px-wide centered canvas, but position:fixed is relative
   to the whole browser window — so the bar/sheets/scrim would span the full
   window (buttons off-canvas, scrim frosting everything). Re-anchor them to the
   centered canvas. On a REAL phone (force-mobile NOT set) the full-width rules
   above are correct, so these overrides are scoped to .force-mobile only.
   Width tracks body.force-mobile's max-width (400px). ----------------------- */
body.force-mobile .mobile-tabbar,
body.force-mobile .mobile-sheet-scrim:not([hidden]),
body.force-mobile #quest-directory,
body.force-mobile .account-popover {
  /* Centre on the 400px canvas via left/width — deliberately NOT transform:
     the sheets reserve `transform` for their translateY slide.
     left = (50% of viewport) − half the 400px width. */
  left: calc(50% - 200px);
  right: auto;
  width: 400px;
}
/* The transform-transition reliably STICKS at the hidden offset inside the
   desktop ?site_type=mobile simulator (a quirk of the centered max-width body);
   snap the sheets open/closed there so they actually show. Real phones don't set
   force-mobile, so they keep the smooth slide. */
body.force-mobile #quest-directory,
body.force-mobile .account-popover {
  transition: none;
}
