/* Voice input — FAB + confirmation sheet.
   Mirrors paywall.css conventions: themed via CSS variables (--panel-solid,
   --ink, --accent, --line) so dark/light toggles cleanly. Hex fallbacks bias
   dark since that's the default theme. */

/* ── FAB ──────────────────────────────────────────────────────────── */
/* `[hidden]` short-hand: `.voice-fab` sets `display: inline-flex` (same
   specificity as the UA-default `[hidden]` rule, loaded later → wins). So
   without this override the JS `fab.hidden = true` would be ignored and
   the FAB would render for every user — including desktop without the
   voiceInput entitlement. */
.voice-fab[hidden] { display: none; }
.voice-fab {
  position: fixed;
  right: calc(16px + env(safe-area-inset-right, 0px));
  bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  z-index: 8000;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 75px;
  height: 75px;
  border: none;
  border-radius: 999px;
  background: var(--accent, #2f80ed);
  color: #fff;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.35);
  cursor: grab;
  /* `none` so pointermove fires for drag — `manipulation` lets the page
     scroll-handle take precedence on vertical drags, which would silently
     break drag-to-reposition. Double-tap zoom is still suppressed by
     user-scalable=no in the viewport meta tag. */
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  transition: transform 120ms ease, box-shadow 120ms ease, opacity 120ms ease;
}
.voice-fab:active {
  transform: scale(1.05);
  box-shadow: 0 14px 30px rgba(47, 128, 237, 0.6);
}

/* Recording state — distinct color, larger size, brighter glow, pulsing
   scale animation. Multi-layer box-shadow creates a "halo" that draws
   the eye from peripheral vision. */
.voice-fab.is-recording {
  background: #e63946;
  transform: scale(1.08);
  box-shadow:
    0 0 0 4px rgba(230, 57, 70, 0.20),
    0 0 0 12px rgba(230, 57, 70, 0.10),
    0 16px 32px rgba(230, 57, 70, 0.50);
  animation: voiceFabPulseRecording 1.4s ease-in-out infinite;
}

.voice-fab.is-dragging {
  /* Slightly bigger + heavier shadow during drag so the user gets clear
     "I'm carrying this" feedback. Suppress the pulse animation in case
     drag was triggered mid-recording. */
  transform: scale(1.12);
  box-shadow: 0 20px 36px rgba(0, 0, 0, 0.5);
  animation: none;
  cursor: grabbing;
}
.voice-fab:disabled {
  opacity: 0.55;
  cursor: default;
}
@keyframes voiceFabPulseRecording {
  0%, 100% {
    transform: scale(1.06);
    box-shadow:
      0 0 0 4px rgba(230, 57, 70, 0.20),
      0 0 0 12px rgba(230, 57, 70, 0.10),
      0 16px 32px rgba(230, 57, 70, 0.50);
  }
  50% {
    transform: scale(1.12);
    box-shadow:
      0 0 0 6px rgba(230, 57, 70, 0.28),
      0 0 0 18px rgba(230, 57, 70, 0.14),
      0 20px 38px rgba(230, 57, 70, 0.65);
  }
}


.voice-fab-icon {
  display: inline-flex;
  position: relative;
  z-index: 1; /* keep icon above sonar rings */
}

/* Icon swap: mic when idle, stop-square when recording. Both glyphs sit
   inside the same SVG; CSS toggles visibility per state. */
.voice-fab-icon-stop {
  display: none;
}
.voice-fab.is-recording .voice-fab-icon-mic {
  display: none;
}
.voice-fab.is-recording .voice-fab-icon-stop {
  display: inline;
}

/* Sonar rings — two concentric rings that scale outward + fade. Only
   painted while .is-recording is on the parent. Staggered so one is
   always at peak visibility. */
.voice-fab-ring {
  position: absolute;
  inset: 0;
  border-radius: 999px;
  border: 2px solid rgba(230, 57, 70, 0.55);
  pointer-events: none;
  opacity: 0;
}
.voice-fab.is-recording .voice-fab-ring-1 {
  animation: voiceFabSonar 1.6s ease-out infinite;
}
.voice-fab.is-recording .voice-fab-ring-2 {
  animation: voiceFabSonar 1.6s ease-out infinite;
  animation-delay: 0.8s;
}
@keyframes voiceFabSonar {
  0%   { transform: scale(1.0); opacity: 0.65; }
  100% { transform: scale(2.2); opacity: 0;    }
}

/* Status ribbon above the FAB.
   Three visible modes driven by data-status (none = live transcript,
   "processing" = spinner + status text, "error" = red background).
   Theme-aware via --panel-solid + --ink — looks correct in both light
   and dark mode. Lives outside the FAB so it remains tappable
   separately if we ever want a cancel hit-target on it. */
.voice-fab-ribbon {
  position: fixed;
  right: calc(16px + env(safe-area-inset-right, 0px));
  bottom: calc(103px + env(safe-area-inset-bottom, 0px));
  z-index: 8000;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: min(80vw, 360px);
  padding: 8px 12px;
  border-radius: 12px;
  background: var(--panel-solid, #0c1926);
  color: var(--ink, #eff8ff);
  border: 1px solid var(--line, rgba(143, 170, 192, 0.22));
  /* Bumped one step over --text-md so medium mode reads comfortably from
     a phone held at arm's length; --text-lg also scales with the user's
     small/medium/large font-mode pref. */
  font-size: var(--text-lg);
  line-height: 1.4;
  box-shadow: 0 10px 24px var(--shadow, rgba(0, 0, 0, 0.45));
  pointer-events: none;
  transition: background-color 150ms ease;
}
.voice-fab-ribbon[hidden] { display: none; }

/* Spinner shown before the status text while processing. Uses
   currentColor so it inherits the ribbon's theme-aware text color. */
.voice-fab-ribbon[data-status="processing"]::before {
  content: '';
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
  border-radius: 50%;
  border: 2px solid var(--line, rgba(143, 170, 192, 0.3));
  border-top-color: currentColor;
  animation: voiceFabSpinner 0.8s linear infinite;
}
@keyframes voiceFabSpinner {
  to { transform: rotate(360deg); }
}

/* Error state — fixed dark red so white text passes WCAG AA (6.5:1) in both
   themes. --danger varies: #ff6b6b (salmon) in dark mode gives only 2.7:1
   contrast with #fff, and #e74c3c in light mode gives 3.75:1 — both fail.
   #b91c1c is dark enough in any theme. */
.voice-fab-ribbon[data-status="error"] {
  background: #b91c1c;
  border-color: #b91c1c;
  color: #fff;
}

/* ── Sheet ────────────────────────────────────────────────────────── */
.voice-sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 8900;
  background: rgba(15, 20, 26, 0.6);
  -webkit-backdrop-filter: blur(2px);
          backdrop-filter: blur(2px);
}
.voice-sheet-backdrop[hidden] { display: none; }

.voice-sheet {
  position: fixed;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  z-index: 9000;
  width: min(640px, 100vw);
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  background: var(--panel-solid, #0c1926);
  color: var(--ink, #eff8ff);
  border: 1px solid var(--line, rgba(143, 170, 192, 0.22));
  border-bottom: none;
  border-top-left-radius: 18px;
  border-top-right-radius: 18px;
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 8px);
  box-shadow: 0 -16px 40px rgba(0, 0, 0, 0.5);
}
.voice-sheet[hidden] { display: none; }

.voice-sheet-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px 6px;
}
.voice-sheet-header h2 {
  margin: 0;
  font-size: var(--text-2xl);
  font-weight: 600;
  letter-spacing: 0.01em;
}

/* Title group in the sheet header. Wraps gracefully if the title gets long. */
.voice-sheet-title-group {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  min-width: 0;
}
.voice-sheet-close {
  /* Inherits .btn base styling for a normal pill button. Override the
     uppercase/letter-spacing of the wider .btn since "Close" is short
     and reads better in title-case at this size. The previous "×"
     glyph was a low-affordance close affordance that didn't read as a
     tap target on touch devices — switched to an explicit "Close"
     button label. */
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--text-sm);
  padding: 6px 12px;
}

.voice-sheet-transcript {
  padding: 0 18px;
  font-size: var(--text-md);
  color: var(--muted, #8faac0);
  font-style: italic;
  max-height: 4em;
  overflow: hidden;
  text-overflow: ellipsis;
}

.voice-sheet-body {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 12px 18px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.voice-sheet-section h3 {
  margin: 0 0 8px;
  font-size: var(--text-base);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted, #8faac0);
}
.voice-sheet-section.is-warn h3 {
  color: #e0a040;
}

.voice-op-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--line, rgba(143, 170, 192, 0.22));
  border-radius: 10px;
  /* Subtle theme-aware tint — uses --panel-alt-solid (light: #f4f9ff,
     dark: undefined → falls back to a near-transparent overlay that
     reads correctly on the dark card). */
  background: var(--panel-alt-solid, rgba(255, 255, 255, 0.02));
  font-size: var(--text-xl);
}
.voice-op-row .voice-op-text {
  flex: 1 1 auto;
}

.voice-amb-row {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 12px;
  border: 1px solid #e0a04055;
  border-radius: 10px;
  background: rgba(224, 160, 64, 0.06);
  font-size: var(--text-md);
}
.voice-amb-phrase {
  font-style: italic;
  color: var(--muted, #8faac0);
}
.voice-amb-candidates {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.voice-amb-chip {
  border: 1px solid var(--line, rgba(143, 170, 192, 0.22));
  background: transparent;
  color: var(--ink, #eff8ff);
  border-radius: 999px;
  padding: 6px 12px;
  font-size: var(--text-base);
  cursor: pointer;
}
.voice-amb-chip.is-selected {
  background: var(--accent, #2f80ed);
  border-color: var(--accent, #2f80ed);
  color: #fff;
}

.voice-unparsed-row {
  font-size: var(--text-base);
  color: var(--muted, #8faac0);
  padding: 6px 0;
}
.voice-unparsed-row .voice-unparsed-reason {
  color: #e0a040;
  margin-left: 8px;
}

.voice-sheet-footer {
  display: flex;
  gap: 12px;
  padding: 12px 18px 16px;
  border-top: 1px solid var(--line, rgba(143, 170, 192, 0.22));
}
.voice-sheet-footer .btn {
  flex: 1 1 50%;
  padding: 12px 16px;
  font-size: var(--text-xl);
  border-radius: 10px;
  cursor: pointer;
}
.voice-sheet-cancel {
  background: transparent;
  border: 1px solid var(--line, rgba(143, 170, 192, 0.22));
  color: var(--ink, #eff8ff);
}
.voice-sheet-apply {
  background: var(--accent, #2f80ed);
  border: 1px solid var(--accent, #2f80ed);
  color: #fff;
}
.voice-sheet-apply:disabled {
  opacity: 0.5;
  cursor: default;
}
