/*
  Canopy AI — Apple Intelligence Aesthetic
  Glass orb, edge glow, fullscreen overlay, chat interface.
*/

/* ── AI Orb v2 (Liquid Mesh + Breathing Halo) ───────────────────────────────
 * The wrapper matches the header control height; the canvas inside renders the liquid mesh.
 * A pseudo-element halo orbits OUTSIDE the sphere bounds (overflow: visible)
 * to give the orb that "Apple Intelligence presence" — a soft glow that
 * spills onto the header. The halo breathes at idle, glows brighter when the
 * orb is active or alerting.
 */
.ai-orb {
  position: relative;
  width: var(--header-control-h, 34px);
  height: var(--header-control-h, 34px);
  border-radius: 50%;
  cursor: pointer;
  z-index: 1000;
  -webkit-tap-highlight-color: transparent;
  outline: none;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-right: 0;
  overflow: visible;
  flex-shrink: 0;
  box-shadow:
    0 5px 16px rgba(0, 0, 0, 0.32),
    0 0 18px rgba(141, 159, 255, 0.24),
    0 0 34px rgba(188, 130, 243, 0.13),
    inset 0 1px 0 rgba(255, 255, 255, 0.28);
  transition: transform 0.42s var(--ease),
              opacity 0.42s var(--ease),
              box-shadow 0.42s var(--ease);
  pointer-events: auto !important;
  animation: aiOrbBreathe 6.4s ease-in-out infinite;
}
/* Subtle scale breathing — barely perceptible, but signals "alive" */
@keyframes aiOrbBreathe {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.04); }
}

/* Halo aura — radial soft glow rendered as a wider pseudo-element BEHIND
 * the orb. Pointer-events: none so taps still hit the orb. */
.ai-orb::before {
  content: '';
  position: absolute;
  inset: -15px;
  border-radius: 50%;
  background:
    conic-gradient(from 20deg,
      rgba(141, 159, 255, 0.00),
      rgba(141, 159, 255, 0.28),
      rgba(245, 185, 234, 0.20),
      rgba(120, 210, 255, 0.22),
      rgba(188, 130, 243, 0.24),
      rgba(141, 159, 255, 0.00)),
    radial-gradient(circle at 50% 50%, rgba(190, 204, 255, 0.16) 0%, rgba(188, 130, 243, 0.08) 44%, transparent 72%);
  filter: blur(9px) saturate(1.2);
  opacity: 0.78;
  pointer-events: none;
  z-index: 0;
  animation: aiOrbHalo 13s ease-in-out infinite;
  will-change: transform, opacity;
}
@keyframes aiOrbHalo {
  0%   { transform: scale(0.92) rotate(0deg); opacity: 0.70; }
  50%  { transform: scale(1.10) rotate(180deg); opacity: 0.96; }
  100% { transform: scale(0.92) rotate(360deg); opacity: 0.70; }
}

.ai-orb::after {
  content: '';
  position: absolute;
  inset: -1px;
  border-radius: 50%;
  backdrop-filter: blur(6px) saturate(1.3);
  -webkit-backdrop-filter: blur(6px) saturate(1.3);
  background:
    radial-gradient(circle at 32% 24%, rgba(255,255,255,0.24) 0%, rgba(255,255,255,0.22) 7%, rgba(255,255,255,0.09) 18%, transparent 42%),
    linear-gradient(145deg, rgba(255,255,255,0.13), rgba(255,255,255,0.03) 42%, rgba(0,0,0,0.07));
  pointer-events: none;
  z-index: 2;
  border: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow:
    inset 0 1px 1px rgba(255, 255, 255, 0.42),
    inset 0 -7px 12px rgba(0, 0, 0, 0.10),
    inset -6px -4px 12px rgba(0, 0, 0, 0.05);
}

.ai-orb:active {
  transform: scale(0.82);
  opacity: 0.55;
  transition: transform var(--motion-fast) var(--ease),
              opacity var(--motion-fast) var(--ease),
              box-shadow var(--motion-fast) var(--ease);
}
.ai-orb.active {
  transform: scale(1.08);
  box-shadow: 0 0 22px rgba(141, 159, 255, 0.55), 0 0 48px rgba(188, 130, 243, 0.22);
  transition: transform var(--motion-slow) var(--ease-spring),
              box-shadow var(--motion-slow) var(--ease);
}

.ai-orb-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  pointer-events: none;
  filter: saturate(1.18) contrast(1.06);
}

.ai-orb-glass {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background:
    radial-gradient(ellipse 54% 44% at 32% 25%, rgba(255, 255, 255, 0.28) 0%, rgba(255, 255, 255, 0.08) 35%, transparent 66%),
    radial-gradient(ellipse 75% 70% at 72% 82%, rgba(0, 0, 0, 0.18) 0%, transparent 58%);
  box-shadow:
    inset 0 1px 2px rgba(255, 255, 255, 0.34),
    inset 0 -1px 5px rgba(0, 0, 0, 0.20),
    inset 0 0 9px rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.14);
  pointer-events: none;
}

/* ── Native AI Toast ──
 * The always-on header context strip now occupies the center slot, so this
 * legacy toast lives as an absolutely-positioned overlay that only appears
 * when showNativeAIInsight() fires it. Keeps the transition logic intact
 * without stealing flex space from the context strip.
 */
.ai-native-toast {
  position: absolute;
  top: calc(var(--safe-top) + 5px);
  left: 24px; right: 24px;
  margin: 0;
  padding: 0 12px;
  height: 32px;
  border-radius: var(--radius-16);
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.08);
  backdrop-filter: blur(20px) saturate(1.4);
  -webkit-backdrop-filter: blur(20px) saturate(1.4);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3), inset 0 0.5px 0 rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.1);
  color: rgba(255, 255, 255, 0.9);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.02em;
  opacity: 0;
  transform: scale(0.95);
  transition: opacity var(--motion-slow, 0.42s) var(--ease), transform var(--motion-slow, 0.42s) var(--ease), visibility 0s var(--motion-slow, 0.42s);
  pointer-events: none;
  cursor: pointer;
  z-index: 110;
  visibility: hidden;
}
.ai-native-toast.show {
  transform: scale(1);
  opacity: 1;
  pointer-events: auto;
  visibility: visible;
}
.ai-native-toast strong { color: var(--gain); font-weight: 700; }

/* ── Screen Edge Glow (only visible when AI overlay is active) ── */
.ai-screen-glow {
  position: fixed;
  inset: 0;
  z-index: 1000;
  pointer-events: none;
  border-radius: 48px;
  opacity: 0;
  transition: opacity 0.5s var(--ease);
  box-shadow:
    inset 0 0 15px 1px var(--ai-blue),
    inset 0 0 30px 6px var(--ai-purple),
    inset 0 0 50px 14px rgba(141, 159, 255, 0.15);
}

.ai-screen-glow.active {
  opacity: 0.45;
  animation: aiGlowBreathe 5s infinite alternate ease-in-out;
}

@keyframes aiGlowBreathe {
  0%   { opacity: 0.35; }
  100% { opacity: 0.55; }
}

/* ── AI Overlay ──
 * Fade curves are asymmetric on purpose: the opening is slower with an
 * ease-out-expo so the orb's glow has time to bloom and the interior
 * content staggers in; the closing is quicker with ease-in so it gets
 * out of the way without hanging on the user.
 * origin 50% 75% puts the zoom pivot over the orb itself (bottom-center)
 * so open reads as "grow up out of the orb" rather than "scale from the
 * middle of the page".
 */
.ai-overlay {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
  transform: scale(0.94);
  transform-origin: 50% 72%;
  transition: opacity 0.46s var(--ease),
              transform 0.48s var(--ease),
              visibility 0s 0.5s;
  padding: 20px;
  padding-top: calc(20px + env(safe-area-inset-top));
  padding-bottom: calc(12px + env(safe-area-inset-bottom));
  /* No `will-change` here on purpose. It promotes the overlay to a permanent
     composited layer, and on iOS a composited/transformed ancestor desyncs
     where WKWebView paints the textarea caret from where the text actually
     sits — the caret rendered far outside the input. The open transition is
     cheap enough without the hint. */
}

.ai-overlay-bg {
  position: absolute;
  top: -50vh;
  bottom: -100vh;
  left: -50vw;
  right: -50vw;
  z-index: -1;
  background: #000000;
}

.ai-overlay-bg::before {
  content: '';
  position: absolute;
  top: -30%;
  left: -30%;
  width: 160%;
  height: 160%;
  background:
    radial-gradient(circle at 30% 20%, rgba(141, 159, 255, 0.03) 0%, transparent 50%),
    radial-gradient(circle at 70% 80%, rgba(188, 130, 243, 0.02) 0%, transparent 50%);
  filter: blur(80px);
  animation: bg-drift 20s infinite alternate ease-in-out;
  pointer-events: none;
}

@keyframes bg-drift {
  0%   { transform: translate(0, 0) scale(1); }
  100% { transform: translate(3%, -3%) scale(1.05); }
}

.ai-overlay.active {
  opacity: 1 !important;
  pointer-events: auto !important;
  visibility: visible !important;
  z-index: 2000 !important;
  transform: none !important; /* identity — animates the same, no stuck layer */
  transition: opacity 0.46s var(--ease),
              transform 0.48s var(--ease),
              visibility 0s !important;
}

/* ── Overlay Content ── */
.ai-overlay-content {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  padding-top: 32px;
  transform: translateY(14px);
  opacity: 0.85;
  transition: transform var(--motion-slow, 0.42s) var(--ease),
              opacity var(--motion-normal, 0.26s) var(--ease);
  height: 100%;
  justify-content: flex-start;
  will-change: transform, opacity;
}

.ai-chat-header {
  width: 100%;
  display: flex;
  /* Without wrap, .ai-chat-disclaimer's flex-basis:100% is inert and all
     three spans fight for one row at 390px. */
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 34px;
  padding: 2px 2px 4px;
  flex-shrink: 0;
}
.ai-chat-subtitle {
  min-width: 0;
  font-size: 11px;
  font-weight: 500;
  line-height: 1.2;
  color: var(--text-tertiary);
  text-align: right;
  letter-spacing: 0.01em;
}
.ai-chat-disclaimer {
  flex-basis: 100%;
  font-size: 10px;
  font-weight: 500;
  color: var(--text-tertiary);
  opacity: 0.6;
  text-align: center;
  letter-spacing: 0.04em;
  margin-top: 2px;
  text-transform: uppercase;
}

.ai-overlay.active .ai-overlay-content {
  transform: translateY(0);
  opacity: 1;
}

/* ── Empty State (redesigned) ─────────────────────────────────────────────
 * Big "AI itself" presence: hero orb + headline + live context + smart chips.
 * Reads as a meeting with an analyst, not a search box.
 */
.ai-empty-state {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  min-height: 0;
  width: 100%;
  padding: 0 8px;
}

/* ── Hero Orb (big) ── */
.ai-hero-orb {
  width: 132px;
  height: 132px;
  border-radius: 50%;
  position: relative;
  margin-bottom: 4px;
  isolation: isolate;
  animation: heroOrbBreathe 6s ease-in-out infinite;
}
.ai-hero-orb::before {
  content: '';
  position: absolute;
  inset: -64px;
  border-radius: 50%;
  background:
    radial-gradient(circle at 50% 50%, rgba(141, 159, 255, 0.22) 0%, rgba(188, 130, 243, 0.13) 38%, rgba(118, 213, 255, 0.06) 56%, transparent 74%);
  pointer-events: none;
  z-index: -1;
  animation: heroOrbHalo 5s ease-in-out infinite alternate;
}
.ai-hero-orb-canvas {
  width: 100%; height: 100%;
  border-radius: 50%;
  display: block;
  position: relative;
  z-index: 1;
}
@keyframes heroOrbBreathe {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.04); }
}
@keyframes heroOrbHalo {
  0%   { transform: scale(0.95); opacity: 0.7; }
  100% { transform: scale(1.10); opacity: 1; }
}

.ai-hero-text {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}
.ai-hero-title {
  font-family: var(--heading);
  font-size: 18px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  background: linear-gradient(120deg, var(--ai-blue), var(--ai-purple), var(--ai-pink), var(--ai-blue));
  background-size: 220% auto;
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
          color: transparent;
  animation: heroTitleShimmer 6s linear infinite;
}
@keyframes heroTitleShimmer {
  to { background-position: 220% center; }
}
.ai-prompt {
  font-family: var(--heading);
  font-size: 14px;
  font-weight: 500;
  color: var(--text-secondary);
  letter-spacing: 0;
  text-align: center;
  max-width: 280px;
  line-height: 1.32;
}

/* ── Live Context Strip ── */
.ai-context-strip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 14px;
  background: rgba(255, 255, 255, 0.04);
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  border: 0.5px solid rgba(255, 255, 255, 0.10);
  border-radius: var(--radius-pill);
  box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.10);
}
.ai-context-dot {
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--gain);
  box-shadow: 0 0 6px var(--gain);
  animation: marketPulse 2.4s ease-in-out infinite;
}
.ai-context-text {
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: -0.05px;
  color: rgba(255, 255, 255, 0.65);
}

/* ── Breathing Dots ── */
.ai-waveform {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 32px;
}
.ai-waveform span {
  display: block;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--ai-blue);
  opacity: 0;
  transform: scale(0.6);
  animation: ai-dot-breathe 2.5s infinite ease-in-out;
}
.ai-waveform span:nth-child(1) { animation-delay: 0s; }
.ai-waveform span:nth-child(2) { animation-delay: 0.15s; }
.ai-waveform span:nth-child(3) { animation-delay: 0.3s; }
.ai-waveform span:nth-child(4) { animation-delay: 0.45s; }
.ai-waveform span:nth-child(5) { animation-delay: 0.6s; }

@keyframes ai-dot-breathe {
  0%, 100% { opacity: 0.15; transform: scale(0.6); }
  50%      { opacity: 0.8; transform: scale(1); }
}

/* ── Suggestion Chips ── */
.ai-suggestions {
  display: flex;
  gap: 9px;
  overflow-x: auto;
  width: 100%;
  padding: 4px 16px;
  scrollbar-width: none;
  -webkit-mask-image: linear-gradient(90deg, transparent 0px, black 24px, black calc(100% - 24px), transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0px, black 24px, black calc(100% - 24px), transparent 100%);
}
.ai-suggestions::-webkit-scrollbar { display: none; }

.ai-suggest-btn {
  flex-shrink: 0;
  background: color-mix(in srgb, var(--ai-purple) 9%, var(--surface));
  backdrop-filter: blur(18px) saturate(180%);
  -webkit-backdrop-filter: blur(18px) saturate(180%);
  border: 1px solid color-mix(in srgb, var(--ai-blue) 20%, var(--glass-border));
  border-radius: var(--radius-pill);
  padding: 10px 14px;
  color: var(--text-secondary);
  font-family: var(--font);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0;
  box-shadow: inset 0 0.5px 0 var(--glass-border);
  transition: transform var(--motion-fast) var(--ease), background var(--motion-fast) var(--ease), color var(--motion-fast) var(--ease), border-color var(--motion-fast) var(--ease);
  white-space: nowrap;
  cursor: pointer;
}
.ai-suggest-btn:active {
  transform: scale(0.95);
  background: rgba(255, 255, 255, 0.12);
  color: rgba(255, 255, 255, 0.95);
  border-color: rgba(255, 255, 255, 0.22);
}

/* ── Thinking-state card ──────────────────────────────────────────────────
 * Shown between the user's last message and the (yet-to-stream) AI reply.
 * Replaces the old "Processing intelligence..." text. The mini-orb is three
 * mesh-blob spans that orbit each other in CSS — no canvas needed at this
 * scale, and it stays in sync with the system aesthetic.
 */
.ai-thinking-card {
  align-self: flex-start;
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 18px;
  background: var(--surface);
  backdrop-filter: blur(22px) saturate(180%);
  -webkit-backdrop-filter: blur(22px) saturate(180%);
  border: 1px solid transparent;
  border-radius: 22px 22px 22px 6px;
  box-shadow: 0 10px 30px color-mix(in srgb, var(--ai-purple) 10%, transparent), inset 0 0.5px 0 var(--glass-border);
  animation: msg-enter 0.32s var(--ease);
  overflow: hidden;
}
.ai-thinking-card::before,
.ai-response-card::before {
  opacity: 0.55;
}
.ai-thinking-orb {
  position: relative;
  width: 22px;
  height: 22px;
  flex-shrink: 0;
}
.ai-thinking-blob {
  position: absolute;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  filter: blur(3px);
  mix-blend-mode: screen;
  top: 4px; left: 4px;
}
.ai-thinking-blob:nth-child(1) {
  background: var(--ai-purple);
  animation: thinkOrbit 1.9s linear infinite;
}
.ai-thinking-blob:nth-child(2) {
  background: var(--ai-blue);
  animation: thinkOrbit 2.4s linear infinite reverse;
  animation-delay: -0.5s;
}
.ai-thinking-blob:nth-child(3) {
  background: var(--ai-pink);
  animation: thinkOrbit 2.1s linear infinite;
  animation-delay: -1s;
}
@keyframes thinkOrbit {
  0%   { transform: translate(0, 0); }
  25%  { transform: translate(5px, -3px); }
  50%  { transform: translate(0, 5px); }
  75%  { transform: translate(-5px, -3px); }
  100% { transform: translate(0, 0); }
}
.ai-thinking-text {
  position: relative;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
  letter-spacing: 0;
}
.ai-thinking-text::before {
  content: '✦ CANOPY AI';
  display: block;
  margin-bottom: 4px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--ai-purple);
}
.ai-thinking-dots span {
  display: inline-block;
  opacity: 0;
  animation: thinkDots 1.5s ease-in-out infinite;
}
.ai-thinking-dots span:nth-child(2) { animation-delay: 0.2s; }
.ai-thinking-dots span:nth-child(3) { animation-delay: 0.4s; }
@keyframes thinkDots {
  0%, 100% { opacity: 0; }
  50%      { opacity: 1; }
}

/* ── Streaming text caret ───────────────────────────────────────────────── */
.ai-response-text.ai-streaming::after {
  content: '';
  display: inline-block;
  width: 7px;
  height: 15px;
  background: linear-gradient(180deg, var(--ai-pink), var(--ai-blue), var(--ai-purple));
  background-size: 100% 220%;
  vertical-align: -2px;
  margin-left: 3px;
  border-radius: 5px;
  box-shadow: 0 0 9px color-mix(in srgb, var(--ai-blue) 55%, transparent);
  animation: streamCaret 1.05s var(--ease) infinite;
}
.ai-response-text.ai-streaming {
  background: linear-gradient(100deg, var(--text-secondary), var(--ai-pink), var(--text), var(--ai-blue), var(--text-secondary));
  background-size: 260% 100%;
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
          color: transparent;
  animation: aiTextSheen 1.8s var(--ease) infinite;
}
@keyframes aiTextSheen {
  from { background-position: 180% 50%; }
  to { background-position: -40% 50%; }
}
@keyframes streamCaret {
  0%, 100% { opacity: 0.7; background-position: 50% 0%; }
  50%      { opacity: 1; background-position: 50% 100%; }
}

/* ── Chat History ── */
.ai-chat-history {
  width: 100%;
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-height: 0;
  overflow-y: auto;
  padding: 0 2px 16px;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: auto;
  touch-action: pan-y !important;
  -webkit-mask-image: linear-gradient(180deg, transparent 0px, black 32px, black calc(100% - 32px), transparent 100%);
  mask-image: linear-gradient(180deg, transparent 0px, black 32px, black calc(100% - 32px), transparent 100%);
}
/* Same reason as .ai-response-card above — nothing in the scroll column
   may be shrunk to fit; the column scrolls instead. */
.ai-chat-history > * { flex-shrink: 0; }

.ai-chat-history::before {
  content: '';
  display: block;
  flex-shrink: 0;
  height: 32px;
}
.ai-chat-history::-webkit-scrollbar { display: none; }

/* User message */
.ai-msg-user {
  align-self: flex-end;
  max-width: 80%;
  background: var(--surface-elevated);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid var(--glass-border);
  border-radius: 18px 18px 6px 18px;
  padding: 10px 12px;
  color: var(--text);
  font-family: var(--font);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.42;
  animation: msg-enter 0.35s var(--ease);
}

/* AI response card */
.ai-response-card {
  width: 100%;
  max-width: 360px;
  position: relative;
  background: color-mix(in srgb, var(--surface) 78%, var(--ai-purple) 7%);
  backdrop-filter: blur(24px) saturate(1.4);
  -webkit-backdrop-filter: blur(24px) saturate(1.4);
  border-radius: 20px 20px 20px 7px;
  padding: 16px 18px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  animation: msg-enter 0.4s var(--ease);
  overflow: hidden;
  box-shadow: 0 14px 38px color-mix(in srgb, var(--ai-blue) 9%, transparent), inset 0 0.5px 0 var(--glass-border);
}
.ai-chat-history .ai-response-card {
  align-self: flex-start;
  max-width: 88%;
  /* A flex child in a column defaults to flex-shrink:1, and this card also
     sets overflow:hidden — so a long answer got vertically compressed and
     the text clipped rather than growing and letting the history scroll. */
  flex-shrink: 0;
}
.ai-overlay-content > .ai-response-card {
  align-self: center;
}
.ai-response-details::before {
  content: '✦ CANOPY AI';
  display: inline-flex;
  width: fit-content;
  margin-bottom: 10px;
  padding: 4px 9px;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--ai-purple) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--ai-purple) 24%, transparent);
  color: var(--text-secondary);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
}

.ai-response-text {
  font-size: 14.5px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--text);
  font-family: var(--font);
}

.ai-ticker-capsule {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px;
  background: rgba(255, 255, 255, 0.06);
  border-radius: var(--radius-12);
  cursor: pointer;
  border: 1px solid rgba(255, 255, 255, 0.05);
  margin-top: 8px;
  transition: background 0.2s var(--ease);
}
.ai-ticker-capsule:active { background: rgba(255, 255, 255, 0.12); }
.ai-capsule-sym { font-weight: 700; font-size: 15px; color: #fff; }
.ai-capsule-label { font-size: 12px; font-weight: 600; color: var(--gain); margin-left: auto; margin-right: 6px; }

.ai-ticker-link {
  color: var(--ai-blue);
  cursor: pointer;
  font-weight: 600;
}

@keyframes msg-enter {
  0%   { opacity: 0; transform: translateY(12px); }
  100% { opacity: 1; transform: translateY(0); }
}

/* ── Input Area ── */
.ai-input-wrapper {
  width: 100%;
  position: relative;
  display: flex;
  align-items: flex-end;
  margin-bottom: 12px;
  z-index: 10;
}

.ai-input {
  width: 100%;
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--radius-24);
  /* Caret alignment rules on iOS WKWebView:
   *   1. Use integer px line-height, not a unitless multiplier. 1.3 × 16 =
   *      20.8 which rounds unpredictably and leaves the caret ~0.4px off
   *      from the text baseline per line.
   *   2. Vertical padding = (min-height - line-height - borders) / 2.
   *      min-height 48, line-height 20, borders 2 → padding 13. (The old
   *      "borders are free under border-box" version of this math left
   *      the content box 18px, so the 20px line overflowed it by 2px and
   *      the caret rode high — measured slack was -3px live.)
   *   3. `overflow-y: hidden` until content exceeds max-height. A visible
   *      scroll gutter (even an invisible webkit-scrollbar one) shifts the
   *      text column and the caret ends up ~3px left of where taps land.
   *   4. `vertical-align: top` forces the baseline to the line-box top,
   *      matching where iOS draws the caret. The default (baseline) puts
   *      text at the line's descender line but the caret at the ascender
   *      line — visually offset by the font's descent.
   */
  padding: 13px 48px 13px 20px;
  color: white;
  font-family: var(--font);
  font-size: 16px;
  line-height: var(--input-line-height); /* = 20px; doctrine math above */
  outline: none;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  resize: none;
  overflow-y: hidden;
  min-height: 48px;
  max-height: 120px;
  box-sizing: border-box;
  white-space: pre-wrap;
  word-wrap: break-word;
  vertical-align: top;
  scrollbar-width: none;
  /* iOS applies its own internal insets to a textarea that has not opted out
     of native appearance, which shifts the text column and the caret off the
     center the padding math above computes. Desktop does not, which is why
     this only ever reproduced on device. */
  -webkit-appearance: none;
  appearance: none;
  display: block;
  margin: 0;
  caret-color: var(--ai-blue, #8d9fff);
  transition: border-color 0.3s var(--ease), background 0.3s var(--ease), box-shadow 0.3s var(--ease);
}
/* Enable scroll only once autosize hits its cap (max-height = 120). This
   way there's no scrollbar gutter on a single-line input. */
.ai-input.is-scrollable { overflow-y: auto; }
.ai-input::placeholder { color: rgba(255, 255, 255, 0.25); }
.ai-input:focus {
  border-color: rgba(141, 159, 255, 0.55);
  background: rgba(255, 255, 255, 0.1);
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}
.ai-input::-webkit-scrollbar { display: none; }

.ai-send-btn {
  position: absolute;
  right: 7px;
  bottom: 7px;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-18);
  background: rgba(255, 255, 255, 0.9);
  color: #000;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: transform 0.15s var(--ease), background 0.15s var(--ease);
}
.ai-send-btn:active {
  transform: scale(0.88);
  background: rgba(255, 255, 255, 0.7);
}

/* ── Controls Row ── */
.ai-controls-row {
  display: flex;
  justify-content: center;
  gap: 12px;
  margin-bottom: 8px;
}

.ai-control-btn {
  all: unset;
  -webkit-tap-highlight-color: transparent;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.4);
  width: 40px;
  height: 40px;
  border-radius: var(--radius-20);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.2s var(--ease);
}
.ai-control-btn:active {
  transform: scale(0.9);
  background: rgba(255, 255, 255, 0.12);
  color: rgba(255, 255, 255, 0.8);
}
.ai-control-btn svg { transition: transform 0.5s var(--ease); }
.ai-control-btn.spinning svg { transform: rotate(-360deg); }

/* ── Utility ── */
body.no-scroll {
  overflow: hidden !important;
  touch-action: none;
}

/* ── Light mode overrides ── */
[data-theme="light"] .ai-orb {
  box-shadow:
    0 4px 14px rgba(85, 102, 160, 0.16),
    0 10px 22px rgba(141, 159, 255, 0.10),
    inset 0 1px 0 rgba(255, 255, 255, 0.75);
}

[data-theme="light"] .ai-orb::before {
  background:
    conic-gradient(from 20deg,
      rgba(88, 112, 210, 0.00),
      rgba(88, 112, 210, 0.18),
      rgba(188, 106, 208, 0.12),
      rgba(68, 165, 210, 0.14),
      rgba(88, 112, 210, 0.00));
  filter: blur(10px) saturate(1.05);
  opacity: 0.42;
}

[data-theme="light"] .ai-orb::after {
  background: rgba(255, 255, 255, 0.20);
  border-color: rgba(255, 255, 255, 0.42);
}

[data-theme="light"] .ai-orb-glass {
  background: radial-gradient(ellipse 60% 50% at 35% 30%, rgba(255, 255, 255, 0.76) 0%, rgba(255, 255, 255, 0.10) 44%, transparent 72%);
  border-color: rgba(255, 255, 255, 0.44);
  box-shadow: inset 0 1px 2px rgba(255, 255, 255, 0.9), inset 0 -1px 3px rgba(85, 102, 160, 0.10), inset 0 0 6px rgba(141, 159, 255, 0.08);
}

[data-theme="light"] .ai-native-toast {
  background: var(--surface-elevated);
  color: var(--text);
  border-color: rgba(0, 0, 0, 0.08);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
}

[data-theme="light"] .ai-screen-glow {
  box-shadow:
    inset 0 0 15px 1px rgba(141, 159, 255, 0.38),
    inset 0 0 30px 6px rgba(188, 130, 243, 0.24),
    inset 0 0 50px 14px rgba(141, 159, 255, 0.10);
}

[data-theme="light"] .ai-overlay {
  color: var(--text);
}

[data-theme="light"] .ai-overlay-bg {
  background: var(--surface-elevated);
}

[data-theme="light"] .ai-overlay-bg::before {
  background:
    radial-gradient(circle at 30% 20%, rgba(141, 159, 255, 0.10) 0%, transparent 50%),
    radial-gradient(circle at 70% 80%, rgba(188, 130, 243, 0.08) 0%, transparent 50%);
}

[data-theme="light"] .ai-empty-state {
  color: var(--text);
}

[data-theme="light"] .ai-hero-orb {
  box-shadow:
    0 0 44px rgba(141, 159, 255, 0.22),
    0 0 88px rgba(188, 130, 243, 0.14);
}

[data-theme="light"] .ai-hero-orb::before {
  background:
    radial-gradient(circle at 50% 50%, rgba(141, 159, 255, 0.22) 0%, rgba(188, 130, 243, 0.10) 45%, transparent 75%);
}

[data-theme="light"] .ai-prompt {
  color: var(--text-secondary);
}

[data-theme="light"] .ai-context-strip {
  background: rgba(0, 0, 0, 0.03);
  border-color: rgba(0, 0, 0, 0.08);
  box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.70);
}

[data-theme="light"] .ai-context-text {
  color: var(--text-secondary);
}

[data-theme="light"] .ai-suggest-btn {
  background: rgba(0, 0, 0, 0.03);
  border-color: rgba(0, 0, 0, 0.08);
  color: var(--text-secondary);
  box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.70);
}

[data-theme="light"] .ai-suggest-btn:active {
  background: #ffffff;
  color: var(--text);
  border-color: rgba(0, 0, 0, 0.08);
}

[data-theme="light"] .ai-thinking-card {
  background: rgba(0, 0, 0, 0.03);
  border-color: rgba(0, 0, 0, 0.08);
  box-shadow: inset 0 0.5px 0 rgba(255, 255, 255, 0.70);
}

[data-theme="light"] .ai-thinking-blob {
  mix-blend-mode: multiply;
}

[data-theme="light"] .ai-thinking-text {
  color: var(--text-secondary);
}

[data-theme="light"] .ai-chat-history {
  color: var(--text);
}

[data-theme="light"] .ai-msg-user {
  background: #ffffff;
  color: var(--text);
  border-color: rgba(0, 0, 0, 0.08);
}

[data-theme="light"] .ai-response-card {
  background: #ffffff;
  border-color: rgba(0, 0, 0, 0.08);
}

[data-theme="light"] .ai-response-text {
  color: var(--text);
}

[data-theme="light"] .ai-response-text.ai-streaming::after {
  box-shadow: 0 0 8px rgba(141, 159, 255, 0.45);
}

[data-theme="light"] .ai-ticker-capsule {
  background: #ffffff;
  border-color: rgba(0, 0, 0, 0.08);
}

[data-theme="light"] .ai-ticker-capsule:active {
  background: rgba(0, 0, 0, 0.03);
}

[data-theme="light"] .ai-capsule-sym {
  color: var(--text);
}

[data-theme="light"] .ai-capsule-label {
  color: var(--gain);
}

[data-theme="light"] .ai-input-wrapper {
  color: var(--text);
}

[data-theme="light"] .ai-input {
  background: #ffffff;
  border-color: rgba(0, 0, 0, 0.08);
  color: var(--text);
}

[data-theme="light"] .ai-input::placeholder {
  color: var(--text-tertiary);
}

[data-theme="light"] .ai-input:focus {
  background: #ffffff;
  border-color: rgba(141, 159, 255, 0.55);
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
}

[data-theme="light"] .ai-send-btn {
  background: var(--text);
  color: var(--surface-elevated);
}

[data-theme="light"] .ai-send-btn:active {
  background: var(--text-secondary);
}

[data-theme="light"] .ai-control-btn {
  background: rgba(0, 0, 0, 0.03);
  border-color: rgba(0, 0, 0, 0.08);
  color: var(--text-tertiary);
}

[data-theme="light"] .ai-control-btn:active {
  background: #ffffff;
  color: var(--text);
}

/* ── Keyboard-open state (set by _syncAIOverlayToKeyboard) ──
   Hide the hero and drop the empty state to top-alignment so nothing can
   overflow above the viewport. Suggestions stay — they are useful mid-type. */
.ai-overlay.ai-kb-open .ai-hero-orb,
.ai-overlay.ai-kb-open .ai-prompt { display: none; }
.ai-overlay.ai-kb-open .ai-empty-state {
  justify-content: flex-start;
  gap: 12px;
}
/* The keyboard covers the home indicator, so the safe-area inset is just a
   ~34px dead strip between the controls and the keyboard. */
.ai-overlay.ai-kb-open { padding-bottom: 12px; }
/* Safety net: even without the class, the empty state must never clip
   content off the top edge. */
.ai-empty-state { overflow-y: auto; scrollbar-width: none; }
.ai-empty-state::-webkit-scrollbar { display: none; }
