<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import {
  ChevronRight,
  Cloud,
  CloudDrizzle,
  CloudFog,
  CloudLightning,
  CloudRain,
  CloudSnow,
  Database,
  Sun,
  Send,
  Sparkles,
  CornerDownLeft,
  Paperclip,
  Smile,
  X,
} from 'lucide-vue-next'
import EmojiPicker from '@/components/chat/EmojiPicker.vue'
import FileMessage from '@/components/chat/FileMessage.vue'
import KnowledgeHudPanel from '@/components/chat/KnowledgeHudPanel.vue'
import KnowledgeSlidePanel from '@/components/chat/KnowledgeSlidePanel.vue'
import KnowledgeHUDPreview from '@/components/chat/KnowledgeHUDPreview.vue'
import OrchestrationPanel from '@/components/chat/OrchestrationPanel.vue'
import TelemetrySparkline from '@/components/chat/TelemetrySparkline.vue'
import NavShortcutRow from '@/components/navigation/NavShortcutRow.vue'
import DailyDigestCard from '@/components/memory/DailyDigestCard.vue'
import ReminderToast from '@/components/memory/ReminderToast.vue'
import { useChatView } from '@/pages/chat/composables/useChatView'
import { useKnowledgeView } from '@/pages/knowledge/composables/useKnowledgeView'
import { getRecentDigests, getDueReminders, snoozeReminder, dismissReminder } from '@/api/memory'
import { scheduleCenterApi, type ScheduleCenterDateResponse, type ScheduleCenterDaySummary } from '@/api/scheduleCenter'

const {
  store,
  inputMessage,
  isSending,
  chatContainer,
  inputRef,
  isTyping,
  fileInputRef,
  showEmojiPicker,
  chatModels,
  selectedModelName,
  selectedModel,
  isLoadingModels,
  orchestrationStatus,
  orchestrationInsight,
  activeAgent,
  visitedAgents,
  orchestrationEventFeed,
  systemMeta,
  systemTelemetry,
  sessionTelemetry,
  sendMessage,
  selectConversation,
  newConversation,
  formatTime,
  autoResize,
  handleFileSelect,
  insertEmoji,
  openFilePicker,
} = useChatView()


function formatNetworkRate(bytesPerSecond: number | null, online: boolean) {
  if (!online || bytesPerSecond === null) return 'OFFLINE'
  if (bytesPerSecond >= 1024 * 1024) return `${(bytesPerSecond / (1024 * 1024)).toFixed(2)} MB/s`
  if (bytesPerSecond >= 1024) return `${(bytesPerSecond / 1024).toFixed(1)} KB/s`
  return `${bytesPerSecond.toFixed(0)} B/s`
}

const clientTime = ref(new Date())
const weatherSummary = ref('Weather unavailable')
const weatherCode = ref<number | null>(null)
const orchestrationDrawerOpen = ref(false)
const knowledgeHudOpen = ref(false)
const selectedFolder = ref<any>(null)
const previewDoc = ref<any>(null)
let clientTimeTimer: ReturnType<typeof setInterval> | null = null

interface SidebarFocusItem {
  id: string
  label: string
  title: string
  meta: string
  tone: 'done' | 'doing' | 'pending'
}

interface SidebarNewsItem {
  id: string
  title: string
  meta: string
}

// Daily Digest state
const dailyDigest = ref<any>(null)
const digestLoading = ref(false)
const recentDigests = ref<any[]>([])
const todayPlanDetail = ref<ScheduleCenterDateResponse | null>(null)
const monthPlanDays = ref<ScheduleCenterDaySummary[]>([])

// Active reminder state
const activeReminder = ref<any>(null)
const reminderVisible = ref(false)
let reminderPollTimer: ReturnType<typeof setInterval> | null = null

const {
  showNewFolderDialog, newFolderName, createFolder, openNewFolderDialog,
  triggerUpload, handleUpload, uploadInput
} = useKnowledgeView()

// Load daily digest
async function loadDailyDigest() {
  digestLoading.value = true
  try {
    const today = formatDateKey(new Date())
    const response = await getRecentDigests(6)
    const items = response.data?.items ?? []
    recentDigests.value = items
    dailyDigest.value = items.find((item: any) => item.date === today) ?? null
  } catch (err) {
    console.warn('Failed to load daily digest:', err)
    recentDigests.value = []
    dailyDigest.value = null
  } finally {
    digestLoading.value = false
  }
}

// Poll for due reminders
async function pollDueReminders() {
  try {
    const response = await getDueReminders()
    if (response.data?.items?.length > 0) {
      activeReminder.value = response.data.items[0]
      reminderVisible.value = true
    } else {
      reminderVisible.value = false
    }
  } catch (err) {
    console.warn('Failed to poll due reminders:', err)
  }
}

async function handleSnooze(id: string, minutes: number) {
  try {
    await snoozeReminder(id, minutes)
    reminderVisible.value = false
    // Reschedule next poll
    setTimeout(pollDueReminders, minutes * 60 * 1000)
  } catch (err) {
    console.warn('Failed to snooze reminder:', err)
  }
}

async function handleDismiss(id: string) {
  try {
    await dismissReminder(id)
    reminderVisible.value = false
  } catch (err) {
    console.warn('Failed to dismiss reminder:', err)
  }
}

function updateClientTime() {
  clientTime.value = new Date()
}

function formatDateKey(date: Date) {
  const year = date.getFullYear()
  const month = String(date.getMonth() + 1).padStart(2, '0')
  const day = String(date.getDate()).padStart(2, '0')
  return `${year}-${month}-${day}`
}

function formatMonthKey(date: Date) {
  const year = date.getFullYear()
  const month = String(date.getMonth() + 1).padStart(2, '0')
  return `${year}-${month}`
}

async function loadSidebarPlanSnapshot(date = new Date()) {
  const dateKey = formatDateKey(date)
  const monthKey = formatMonthKey(date)

  try {
    const [todayResponse, monthResponse] = await Promise.all([
      scheduleCenterApi.date(dateKey),
      scheduleCenterApi.month(monthKey),
    ])
    todayPlanDetail.value = todayResponse.data
    monthPlanDays.value = monthResponse.data.days
  } catch (err) {
    console.warn('Failed to load sidebar plan snapshot:', err)
    todayPlanDetail.value = null
    monthPlanDays.value = []
  }
}

function openOrchestrationDrawer() {
  orchestrationDrawerOpen.value = true
}

function closeOrchestrationDrawer() {
  orchestrationDrawerOpen.value = false
}

function openKnowledgeHud() {
  selectedFolder.value = null
  previewDoc.value = null
  knowledgeHudOpen.value = true
}

function closeKnowledgeHud() {
  knowledgeHudOpen.value = false
}

function handleSelectFolder(folder: any) {
  selectedFolder.value = folder
}

function handleOpenPreview(doc: any) {
  previewDoc.value = doc
}


function formatClientDate(date: Date) {
  return date.toLocaleDateString('zh-CN', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
  })
}

function formatClientClock(date: Date) {
  return date.toLocaleTimeString('zh-CN', {
    hour12: false,
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
  })
}

function weatherCodeLabel(code: number | null | undefined) {
  if (code === 0) return 'Clear'
  if (code === 1 || code === 2) return 'Partly Cloudy'
  if (code === 3) return 'Overcast'
  if (code === 45 || code === 48) return 'Fog'
  if ([51, 53, 55, 56, 57].includes(code ?? -1)) return 'Drizzle'
  if ([61, 63, 65, 66, 67, 80, 81, 82].includes(code ?? -1)) return 'Rain'
  if ([71, 73, 75, 77, 85, 86].includes(code ?? -1)) return 'Snow'
  if ([95, 96, 99].includes(code ?? -1)) return 'Thunderstorm'
  return 'Weather'
}

const weatherIcon = computed(() => {
  const code = weatherCode.value
  if (code === 0) return Sun
  if (code === 1 || code === 2 || code === 3) return Cloud
  if (code === 45 || code === 48) return CloudFog
  if ([51, 53, 55, 56, 57].includes(code ?? -1)) return CloudDrizzle
  if ([61, 63, 65, 66, 67, 80, 81, 82].includes(code ?? -1)) return CloudRain
  if ([71, 73, 75, 77, 85, 86].includes(code ?? -1)) return CloudSnow
  if ([95, 96, 99].includes(code ?? -1)) return CloudLightning
  return Cloud
})

const todayDateKey = computed(() => formatDateKey(clientTime.value))
const monthPlanSummaryMap = computed(() => new Map(monthPlanDays.value.map((item) => [item.date, item])))

const calendarCells = computed(() => {
  const year = clientTime.value.getFullYear()
  const month = clientTime.value.getMonth()
  const daysInMonth = new Date(year, month + 1, 0).getDate()
  const firstDayOffset = (new Date(year, month, 1).getDay() + 6) % 7
  const cells: Array<{ key: string; value: number | null; active: boolean; busy: boolean }> = []

  for (let index = 0; index < firstDayOffset; index += 1) {
    cells.push({ key: `blank-start-${index}`, value: null, active: false, busy: false })
  }

  for (let day = 1; day <= daysInMonth; day += 1) {
    const monthDate = new Date(year, month, day)
    const dateKey = formatDateKey(monthDate)
    const summary = monthPlanSummaryMap.value.get(dateKey)
    const busy = Boolean(summary && (summary.todo_total + summary.task_due_total + summary.goal_total + summary.reminder_total) > 0)
    cells.push({
      key: dateKey,
      value: day,
      active: day === clientTime.value.getDate(),
      busy,
    })
  }

  while (cells.length % 7 !== 0) {
    cells.push({ key: `blank-end-${cells.length}`, value: null, active: false, busy: false })
  }

  return cells
})

const todayPlanCounters = computed(() => {
  const detail = todayPlanDetail.value
  if (!detail) {
    return {
      done: 0,
      doing: 0,
      pending: 0,
      total: 0,
      completion: 0,
    }
  }

  const todoDone = detail.todos.filter((item) => item.is_completed).length
  const todoPending = detail.todos.filter((item) => !item.is_completed).length
  const taskDone = detail.tasks.filter((item) => item.status === 'done').length
  const taskDoing = detail.tasks.filter((item) => item.status === 'in_progress').length
  const taskPending = detail.tasks.filter((item) => item.status === 'todo').length
  const goalDone = detail.goals.filter((item) => item.status === 'done').length
  const goalPending = detail.goals.filter((item) => item.status !== 'done').length
  const reminderDone = detail.reminders.filter((item) => item.status === 'done' || item.is_dismissed).length
  const reminderPending = detail.reminders.filter((item) => item.status !== 'done' && !item.is_dismissed).length

  const done = todoDone + taskDone + goalDone + reminderDone
  const doing = taskDoing
  const pending = todoPending + taskPending + goalPending + reminderPending
  const total = done + doing + pending

  return {
    done,
    doing,
    pending,
    total,
    completion: total > 0 ? Math.round((done / total) * 100) : 0,
  }
})


const monthReviewStats = computed(() => monthPlanDays.value.reduce(
  (acc, item) => {
    acc.todoTotal += item.todo_total
    acc.todoCompleted += item.todo_completed
    acc.taskTotal += item.task_due_total
    acc.reminderTotal += item.reminder_total
    acc.goalTotal += item.goal_total
    acc.highPriorityTotal += item.high_priority_total
    if (item.todo_total + item.task_due_total + item.reminder_total + item.goal_total > 0) {
      acc.activeDays += 1
    }
    return acc
  },
  {
    todoTotal: 0,
    todoCompleted: 0,
    taskTotal: 0,
    reminderTotal: 0,
    goalTotal: 0,
    highPriorityTotal: 0,
    activeDays: 0,
  },
))

const sidebarWeekLabels = ['\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u65e5']

const sidebarStatusHeadline = computed(() => (
  todayPlanCounters.value.total
    ? `\u4eca\u65e5\u5171 ${todayPlanCounters.value.total} \u9879\u8ba1\u5212\uff0c\u5df2\u5b8c\u6210 ${todayPlanCounters.value.done} \u9879`
    : '\u4eca\u65e5\u8ba1\u5212\u6b63\u5728\u540c\u6b65\uff0c\u7a0d\u540e\u4f1a\u663e\u793a\u6700\u65b0\u72b6\u6001'
))

const sidebarStatusBreakdown = computed(() => ([
  { key: 'done', label: '\u5df2\u5b8c\u6210', value: todayPlanCounters.value.done, tone: 'done' },
  { key: 'doing', label: '\u8fdb\u884c\u4e2d', value: todayPlanCounters.value.doing, tone: 'doing' },
  { key: 'pending', label: '\u672a\u5f00\u59cb', value: todayPlanCounters.value.pending, tone: 'pending' },
]))

const sidebarFocusItems = computed<SidebarFocusItem[]>(() => {
  const detail = todayPlanDetail.value
  if (!detail) return []

  const goalItems = detail.goals
    .filter((goal) => goal.status !== 'done')
    .map((goal) => ({
      id: `goal-${goal.id}`,
      label: '\u76ee\u6807',
      title: goal.title,
      meta: goal.note || '\u4eca\u65e5\u76ee\u6807\u63a8\u8fdb',
      tone: 'doing' as const,
    }))

  const taskItems = detail.tasks
    .filter((task) => task.status !== 'done' && task.status !== 'cancelled')
    .sort((a, b) => {
      const priorityRank = { urgent: 0, high: 1, medium: 2, low: 3 }
      return priorityRank[a.priority] - priorityRank[b.priority]
    })
    .map((task) => ({
      id: `task-${task.id}`,
      label: task.priority === 'urgent' || task.priority === 'high' ? '\u9ad8\u4f18\u4efb\u52a1' : '\u4efb\u52a1',
      title: task.title,
      meta: task.status === 'in_progress' ? '\u5904\u7406\u4e2d' : '\u5f85\u542f\u52a8',
      tone: task.status === 'in_progress' ? 'doing' as const : 'pending' as const,
    }))

  const reminderItems = detail.reminders
    .filter((reminder) => reminder.status !== 'done' && !reminder.is_dismissed)
    .map((reminder) => ({
      id: `reminder-${reminder.id}`,
      label: '\u63d0\u9192',
      title: reminder.title,
      meta: reminder.reminder_at.slice(11, 16),
      tone: 'pending' as const,
    }))

  const todoItems = detail.todos
    .filter((todo) => !todo.is_completed)
    .map((todo) => ({
      id: `todo-${todo.id}`,
      label: '\u5f85\u529e',
      title: todo.title,
      meta: todo.source === 'manual' ? '\u624b\u52a8\u8bb0\u5f55' : '\u7cfb\u7edf\u540c\u6b65',
      tone: 'pending' as const,
    }))

  return [...goalItems, ...taskItems, ...reminderItems, ...todoItems].slice(0, 5)
})

const sidebarReviewAchievements = computed(() => {
  const stats = monthReviewStats.value
  const items = [
    stats.todoCompleted > 0 ? `\u7d2f\u8ba1\u5b8c\u6210 ${stats.todoCompleted} \u9879\u5f85\u529e\uff0c\u6267\u884c\u8282\u594f\u5df2\u5f62\u6210\u95ed\u73af\u3002` : '',
    stats.activeDays > 0 ? `\u672c\u6708\u5df2\u6709 ${stats.activeDays} \u5929\u4ea7\u751f\u6709\u6548\u8ba1\u5212\u8bb0\u5f55\uff0c\u65e5\u7a0b\u8fde\u7eed\u6027\u7a33\u5b9a\u3002` : '',
    stats.highPriorityTotal > 0 ? `\u9ad8\u4f18\u4e8b\u9879\u5171 ${stats.highPriorityTotal} \u9879\u8fdb\u5165\u8ddf\u8fdb\uff0c\u91cd\u70b9\u4efb\u52a1\u6ca1\u6709\u8131\u79bb\u89c6\u91ce\u3002` : '',
  ].filter(Boolean)

  if (items.length > 0) return items.slice(0, 3)
  return ['\u672c\u6708\u8ba1\u5212\u6570\u636e\u8fd8\u5728\u79ef\u7d2f\u4e2d\uff0c\u53ef\u4ee5\u4ece\u4eca\u65e5\u91cd\u70b9\u5f00\u59cb\u9010\u6b65\u5efa\u7acb\u590d\u76d8\u6837\u672c\u3002']
})

const sidebarReviewReflections = computed(() => {
  const stats = monthReviewStats.value
  const pendingTodoCount = Math.max(stats.todoTotal - stats.todoCompleted, 0)
  const items = [
    pendingTodoCount > 0 ? `\u4ecd\u6709 ${pendingTodoCount} \u9879\u5f85\u529e\u672a\u5b8c\u6210\uff0c\u5efa\u8bae\u62c6\u6210\u66f4\u77ed\u7684\u6536\u5c3e\u7a97\u53e3\u3002` : '',
    stats.highPriorityTotal >= 8 ? '\u9ad8\u4f18\u4e8b\u9879\u5bc6\u5ea6\u504f\u9ad8\uff0c\u6700\u597d\u63d0\u524d\u9501\u5b9a 1 \u5230 2 \u4e2a\u7edd\u5bf9\u4f18\u5148\u7ea7\u3002' : '',
    stats.reminderTotal >= Math.max(6, stats.activeDays) ? '\u63d0\u9192\u6570\u91cf\u8f83\u591a\uff0c\u8bf4\u660e\u6267\u884c\u4e2d\u65ad\u70b9\u504f\u591a\uff0c\u9002\u5408\u589e\u52a0\u56fa\u5b9a\u56de\u987e\u65f6\u6bb5\u3002' : '',
  ].filter(Boolean)

  if (items.length > 0) return items.slice(0, 3)
  return ['\u672c\u6708\u8282\u594f\u76f8\u5bf9\u5e73\u7a33\uff0c\u4e0b\u4e00\u6b65\u53ef\u4ee5\u628a\u91cd\u70b9\u4e8b\u9879\u518d\u6536\u655b\u5230\u66f4\u6e05\u6670\u7684\u4e3b\u7ebf\u3002']
})

const sidebarFeedItems = computed<SidebarNewsItem[]>(() => {
  const digestFeed = recentDigests.value.flatMap((digest: any, digestIndex: number) => {
    const dateLabel = typeof digest.date === 'string' ? digest.date.slice(5) : '\u8fd1\u671f'
    const points = Array.isArray(digest.keyPoints) ? digest.keyPoints : []
    return points.slice(0, 2).map((point: any, pointIndex: number) => ({
      id: `digest-${digestIndex}-${pointIndex}`,
      title: typeof point?.content === 'string' ? point.content : String(point ?? ''),
      meta: point?.source || dateLabel,
    }))
  })

  if (digestFeed.length > 0) return digestFeed.slice(0, 4)

  return [
    { id: 'fallback-1', title: '\u0041\u0049 \u7814\u53d1\u8282\u594f\u7ee7\u7eed\u5347\u6e29\uff0c\u6a21\u578b\u4e0e\u5de5\u4f5c\u6d41\u4e00\u4f53\u5316\u6210\u4e3a\u4e3b\u6d41\u8bae\u9898\u3002', meta: 'Industry' },
    { id: 'fallback-2', title: '\u672c\u5730\u77e5\u8bc6\u5e93\u4e0e\u8ba1\u5212\u7cfb\u7edf\u7684\u8054\u52a8\u4f53\u9a8c\uff0c\u6b63\u5728\u6210\u4e3a\u6548\u7387\u5de5\u5177\u7684\u65b0\u7ade\u4e89\u70b9\u3002', meta: 'Product' },
    { id: 'fallback-3', title: '\u5efa\u8bae\u63a5\u5165\u771f\u5b9e RSS \u6e90\u540e\u66ff\u6362\u5f53\u524d\u5360\u4f4d\u5361\u7247\uff0c\u4ee5\u83b7\u5f97\u5373\u65f6\u8d44\u8baf\u6d41\u3002', meta: 'System' },
  ]
})

async function loadWeather(latitude: number, longitude: number) {
  try {
    const response = await fetch(
      `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current=temperature_2m,weather_code&timezone=auto`,
    )
    if (!response.ok) throw new Error('weather request failed')
    const data = await response.json()
    const current = data.current ?? {}
    weatherCode.value = typeof current.weather_code === 'number' ? current.weather_code : null
    const temp = typeof current.temperature_2m === 'number' ? `${Math.round(current.temperature_2m)}°C` : '--'
    weatherSummary.value = `${weatherCodeLabel(current.weather_code)} ${temp}`
  } catch {
    weatherCode.value = null
    weatherSummary.value = 'Weather unavailable'
  }
}

onMounted(() => {
  updateClientTime()
  clientTimeTimer = setInterval(updateClientTime, 1000)

  // Load daily digest
  void loadDailyDigest()
  void loadSidebarPlanSnapshot(new Date())

  // Start polling for due reminders (every 60 seconds)
  void pollDueReminders()
  reminderPollTimer = setInterval(() => {
    void pollDueReminders()
  }, 60000)

  if (!navigator.geolocation) {
    weatherCode.value = null
    weatherSummary.value = 'Weather unavailable'
    return
  }

  navigator.geolocation.getCurrentPosition(
    (position) => {
      void loadWeather(position.coords.latitude, position.coords.longitude)
    },
    () => {
      weatherCode.value = null
      weatherSummary.value = 'Weather unavailable'
    },
    { enableHighAccuracy: false, timeout: 8000, maximumAge: 300000 },
  )
})

onUnmounted(() => {
  if (clientTimeTimer) clearInterval(clientTimeTimer)
  if (reminderPollTimer) clearInterval(reminderPollTimer)
})

watch(todayDateKey, (next, previous) => {
  if (next === previous) return
  void loadDailyDigest()
  void loadSidebarPlanSnapshot(clientTime.value)
})

function formatUptime(seconds: number) {
  const days = Math.floor(seconds / 86400)
  const hours = Math.floor((seconds % 86400) / 3600)
  const minutes = Math.floor((seconds % 3600) / 60)
  if (days > 0) return `${days}d ${hours}h ${minutes}m`
  if (hours > 0) return `${hours}h ${minutes}m`
  return `${minutes}m`
}

function currentIp() {
  if (typeof window === 'undefined') return '--'
  return window.location.hostname || '--'
}

function currentPort() {
  if (typeof window === 'undefined') return '--'
  return window.location.port || (window.location.protocol === 'https:' ? '443' : '80')
}

function formatMemoryMb(value: number | null) {
  if (value === null) return '--'
  if (value >= 1024) return `${(value / 1024).toFixed(1)} GB`
  return `${Math.round(value)} MB`
}

function formatDiskGb(value: number) {
  if (value >= 1024) return `${(value / 1024).toFixed(1)} TB`
  return `${value.toFixed(1)} GB`
}

function extractThinkParts(content: string) {
  const thinkPattern = /<think>([\s\S]*?)<\/think>/gi
  const thinkBlocks = Array.from(content.matchAll(thinkPattern))
    .map((match) => match[1]?.trim())
    .filter((block): block is string => Boolean(block))

  const visibleContent = content.replace(thinkPattern, '').trim()

  return {
    hasThink: thinkBlocks.length > 0,
    thinkBlocks,
    visibleContent,
  }
}

function escapeHtml(content: string) {
  return content
    .replaceAll('&', '&amp;')
    .replaceAll('<', '&lt;')
    .replaceAll('>', '&gt;')
    .replaceAll('"', '&quot;')
    .replaceAll("'", '&#39;')
}

function renderInlineMarkdown(content: string) {
  return content
    .replace(/`([^`]+)`/g, '<code>$1</code>')
    .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
    .replace(/\*([^*]+)\*/g, '<em>$1</em>')
}

function isMarkdownTable(lines: string[]) {
  return lines.length >= 2
    && lines[0].includes('|')
    && lines[1].includes('|')
    && lines[1].split('|').filter(Boolean).every((cell) => /^\s*:?-{3,}:?\s*$/.test(cell))
}

function splitTableRow(line: string) {
  return line
    .trim()
    .replace(/^\|/, '')
    .replace(/\|$/, '')
    .split('|')
    .map((cell) => cell.trim())
}

function renderMarkdown(content: string) {
  const normalizedContent = content
    .replace(/\\r\\n/g, '\n')
    .replace(/\\n/g, '\n')
    .replace(/\r\n/g, '\n')

  const normalized = escapeHtml(normalizedContent)
  const blocks = normalized.split(/\n\n+/)

  return blocks.map((block) => {
    const trimmed = block.trim()
    if (!trimmed) return ''

    if (/^```[\s\S]*```$/.test(trimmed)) {
      const code = trimmed.replace(/^```\w*\n?/, '').replace(/```$/, '').trimEnd()
      return `<pre class="md-pre"><code>${code}</code></pre>`
    }

    const lines = trimmed.split('\n')

    if (isMarkdownTable(lines)) {
      const [headerLine, , ...bodyLines] = lines
      const headers = splitTableRow(headerLine)
      const thead = `<thead><tr>${headers.map((cell) => `<th>${renderInlineMarkdown(cell)}</th>`).join('')}</tr></thead>`
      const tbody = bodyLines.length
        ? `<tbody>${bodyLines.map((line) => `<tr>${splitTableRow(line).map((cell) => `<td>${renderInlineMarkdown(cell)}</td>`).join('')}</tr>`).join('')}</tbody>`
        : ''
      return `<div class="md-table-wrap"><table class="md-table">${thead}${tbody}</table></div>`
    }

    if (lines.every((line) => /^\s*[-*]\s+/.test(line))) {
      const items = lines
        .map((line) => line.replace(/^\s*[-*]\s+/, '').trim())
        .map((line) => `<li>${renderInlineMarkdown(line)}</li>`)
        .join('')
      return `<ul class="md-list">${items}</ul>`
    }

    if (lines.every((line) => /^\s*\d+\.\s+/.test(line))) {
      const items = lines
        .map((line) => line.replace(/^\s*\d+\.\s+/, '').trim())
        .map((line) => `<li>${renderInlineMarkdown(line)}</li>`)
        .join('')
      return `<ol class="md-list">${items}</ol>`
    }

    if (/^#{1,3}\s+/.test(trimmed)) {
      const level = Math.min((trimmed.match(/^#+/)?.[0].length || 1) + 1, 6)
      const text = trimmed.replace(/^#{1,3}\s+/, '')
      return `<h${level} class="md-heading">${renderInlineMarkdown(text)}</h${level}>`
    }

    return `<p>${renderInlineMarkdown(trimmed).replace(/\n/g, '<br>')}</p>`
  }).join('')
}
</script>

<template>
  <div class="chat-view">
    <!-- Conversation list sidebar -->
    <aside class="conv-sidebar jarvis-sidebar">
      <div class="jarvis-sidebar-scroll">
        <div class="jarvis-panel jarvis-date-panel">
          <div class="jarvis-date-row">
            <div class="jarvis-date-num">{{ clientTime.getDate().toString().padStart(2, '0') }}</div>
            <div class="jarvis-date-meta">
              <div class="jarvis-month">{{ clientTime.toLocaleString('zh-CN', { month: 'long' }) }} / {{ clientTime.getFullYear() }}</div>
              <div class="jarvis-time">{{ clientTime.toLocaleTimeString('zh-CN', { hour12: false }) }}</div>
            </div>
          </div>

          <div class="jarvis-calendar">
            <div class="calendar-header">
              <span v-for="label in sidebarWeekLabels" :key="label">{{ label }}</span>
            </div>
            <div class="calendar-grid">
              <span
                v-for="cell in calendarCells"
                :key="cell.key"
                class="calendar-day"
                :class="{ active: cell.active, busy: cell.busy, muted: cell.value === null }"
              >
                {{ cell.value ?? '' }}
              </span>
            </div>
          </div>

          <div class="jarvis-action-row">
            <button class="jarvis-action-chip" type="button" @click="newConversation">&#x65B0;&#x5BF9;&#x8BDD;</button>
            <button class="jarvis-action-chip schedule" type="button" @click="selectConversation('schedule-mode')">&#x65E5;&#x7A0B;&#x6A21;&#x5F0F;</button>
            <button class="jarvis-action-chip code" type="button" @click="selectConversation('code-mode')">&#x4EE3;&#x7801;&#x6A21;&#x5F0F;</button>
          </div>
        </div>

        <div class="jarvis-panel">
          <div class="jarvis-section-title">&#x4ECA;&#x65E5;&#x8BA1;&#x5212;&#x60C5;&#x51B5;</div>
          <div class="jarvis-status-shell">
            <div class="jarvis-progress-ring" :style="{ '--completion': `${todayPlanCounters.completion}%` }">
              <div class="jarvis-progress-core">
                <strong>{{ todayPlanCounters.completion }}%</strong>
                <span>&#x5B8C;&#x6210;&#x7387;</span>
              </div>
            </div>

            <div class="jarvis-status-copy">
              <div class="jarvis-status-headline">
                {{ sidebarStatusHeadline }}
              </div>
              <ul class="jarvis-status-list">
                <li v-for="item in sidebarStatusBreakdown" :key="item.key" class="jarvis-status-item">
                  <span class="status-dot" :class="item.tone"></span>
                  <span class="status-label">{{ item.label }}</span>
                  <strong class="status-value">{{ item.value }}</strong>
                </li>
              </ul>
            </div>
          </div>
        </div>

        <div class="jarvis-panel">
          <div class="jarvis-section-title">&#x4ECA;&#x65E5;&#x8BA1;&#x5212;&#x91CD;&#x70B9;</div>
          <ul v-if="sidebarFocusItems.length > 0" class="jarvis-focus-list">
            <li v-for="(item, index) in sidebarFocusItems" :key="item.id" class="jarvis-focus-item" :class="`is-${item.tone}`">
              <span class="focus-order">{{ String(index + 1).padStart(2, '0') }}</span>
              <div class="focus-copy">
                <div class="focus-label">{{ item.label }}</div>
                <div class="focus-title">{{ item.title }}</div>
                <div class="focus-meta">{{ item.meta }}</div>
              </div>
            </li>
          </ul>
          <div v-else class="jarvis-empty-state">&#x6682;&#x65E0;&#x4ECA;&#x65E5;&#x91CD;&#x70B9;&#xFF0C;&#x7B49;&#x5F85;&#x65E5;&#x7A0B;&#x4E2D;&#x5FC3;&#x8FD4;&#x56DE;&#x6570;&#x636E;&#x3002;</div>
        </div>

        <div class="jarvis-panel">
          <div class="jarvis-section-title">&#x672C;&#x6708;&#x8BA1;&#x5212;&#x590D;&#x76D8;</div>
          <div class="jarvis-review-group">
            <div class="jarvis-review-subtitle">&#x6210;&#x679C;</div>
            <ul class="jarvis-review-list">
              <li v-for="item in sidebarReviewAchievements" :key="item" class="jarvis-review-item">{{ item }}</li>
            </ul>
          </div>
          <div class="jarvis-review-group">
            <div class="jarvis-review-subtitle">&#x53CD;&#x601D;</div>
            <ul class="jarvis-review-list reflection">
              <li v-for="item in sidebarReviewReflections" :key="item" class="jarvis-review-item">{{ item }}</li>
            </ul>
          </div>
        </div>

        <div class="jarvis-panel jarvis-rss-panel">
          <div class="jarvis-section-title">RSS &#x65B0;&#x95FB;</div>
          <div class="jarvis-rss-list">
            <article v-for="item in sidebarFeedItems" :key="item.id" class="jarvis-news-card">
              <div class="jarvis-news-meta">{{ item.meta }}</div>
              <div class="jarvis-news-title">{{ item.title }}</div>
            </article>
          </div>
        </div>
      </div>
    </aside>

    <!-- Chat area -->
    <section class="chat-area">
      <div class="chat-shell">
        <div class="chat-main">
          <!-- Top bar -->
          <div class="chat-topbar">
            <div class="chat-shortcuts">
              <NavShortcutRow 
                @select-folder="handleSelectFolder" 
                @open-knowledge-hud="openKnowledgeHud" 
              />
            </div>
            <div class="chat-model-panel">
              <label class="chat-model-label" for="chat-model-select">
                <Sparkles :size="12" />
                <span>CHAT MODEL</span>
              </label>
              <select
                id="chat-model-select"
                v-model="selectedModelName"
                class="chat-model-select"
                :disabled="isSending || isLoadingModels || chatModels.length === 0"
              >
                <option v-if="chatModels.length === 0" value="">
                  {{ isLoadingModels ? 'Loading models...' : 'No chat models' }}
                </option>
                <option v-for="model in chatModels" :key="model.name" :value="model.name">
                  {{ model.name }}
                </option>
              </select>
              <div v-if="selectedModel" class="chat-model">
                <Sparkles :size="12" />
                <span>{{ selectedModel.model }}</span>
              </div>
            </div>
          </div>

          <!-- Messages -->
          <div ref="chatContainer" class="messages-area">
            <!-- Welcome screen -->
            <div v-if="store.messages.length === 0" class="welcome-screen">
              <div class="welcome-icon">
                <div class="welcome-ring r1"></div>
                <div class="welcome-ring r2"></div>
                <div class="welcome-ring r3"></div>
                <div class="welcome-core">
                  <Sparkles :size="28" />
                </div>
              </div>
              <div class="welcome-title">JARVIS</div>
              <div class="welcome-sub">Strategic Thinking Partner</div>
              <div class="welcome-hint">把目标给我，我先帮您收束重点，再往下推进。</div>
            </div>

            <!-- Daily Digest Card -->
            <DailyDigestCard
              v-if="dailyDigest"
              :digest="dailyDigest"
              :loading="digestLoading"
            />

            <!-- Message bubbles -->
            <div
              v-for="(msg, i) in store.messages"
              :key="msg.id"
              class="message-row"
              :class="msg.role"
              :style="{ animationDelay: `${i * 30}ms` }"
            >
              <div class="msg-avatar">
                <span v-if="msg.role === 'user'">{{ '>' }}</span>
                <span v-else class="ai-icon">J</span>
              </div>
              <div class="msg-content">
                <div class="msg-meta">
                  <span class="msg-role">{{ msg.role === 'user' ? 'YOU' : 'JARVIS' }}</span>
                  <span v-if="msg.role === 'assistant' && msg.model" class="msg-model">{{ msg.model }}</span>
                  <span class="msg-time">{{ formatTime(msg.created_at) }}</span>
                </div>
                <template v-if="msg.role === 'assistant' && extractThinkParts(msg.content).hasThink">
                  <details class="think-panel">
                    <summary class="think-summary">
                      <span class="think-chip">
                        <span class="think-chip-dot"></span>
                        <span>THINK</span>
                      </span>
                    </summary>
                    <div class="think-content">
                      <div
                        v-for="(block, index) in extractThinkParts(msg.content).thinkBlocks"
                        :key="index"
                        class="think-block"
                        v-html="renderMarkdown(block)"
                      ></div>
                    </div>
                  </details>
                  <div
                    v-if="extractThinkParts(msg.content).visibleContent"
                    class="msg-bubble markdown-body"
                    v-html="renderMarkdown(extractThinkParts(msg.content).visibleContent)"
                  ></div>
                </template>
                <div v-else class="msg-bubble markdown-body" v-html="renderMarkdown(msg.content)"></div>
                <div v-if="msg.role === 'user' && msg.attachments?.length" class="msg-attachments">
                  <FileMessage
                    v-for="att in msg.attachments"
                    :key="att.id"
                    :filename="att.name"
                    :file-type="att.type"
                    :file-size="att.size"
                  />
                </div>
              </div>
            </div>

            <div v-if="isTyping" class="message-row assistant thinking-row">
              <div class="msg-avatar">
                <span class="ai-icon">J</span>
              </div>
              <div class="msg-content">
                <div class="msg-meta">
                  <span class="msg-role">JARVIS</span>
                  <span class="msg-model">thinking</span>
                </div>
                <div class="msg-bubble thinking-bubble">
                  <div class="thinking-hud">
                    <div class="thinking-core">
                      <span class="thinking-ring ring-1"></span>
                      <span class="thinking-ring ring-2"></span>
                      <span class="thinking-ring ring-3"></span>
                      <span class="thinking-dot"></span>
                    </div>
                    <div class="thinking-copy">
                      <div class="thinking-title">JARVIS THINKING</div>
                      <div class="thinking-subtitle">正在分析请求并准备响应</div>
                    </div>
                    <div class="typing-inline" aria-hidden="true">
                      <span class="dot"></span>
                      <span class="dot"></span>
                      <span class="dot"></span>
                    </div>
                  </div>
                </div>
              </div>
            </div>

          </div>

          <!-- Input area -->
          <div class="input-area">
            <div class="input-frame">
              <div class="input-corners tl"></div>
              <div class="input-corners tr"></div>
              <div class="input-corners bl"></div>
              <div class="input-corners br"></div>
              <textarea
                ref="inputRef"
                v-model="inputMessage"
                placeholder="输入指令，按 Enter 发送..."
                :disabled="isSending"
                rows="1"
                @keydown.enter.exact.prevent="sendMessage"
                @input="autoResize"
              ></textarea>
              <input
                ref="fileInputRef"
                type="file"
                multiple
                accept=".jpg,.jpeg,.png,.gif,.webp,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt"
                style="display: none"
                @change="handleFileSelect"
              />
              <button class="attach-btn" @click="openFilePicker" title="上传文件">
                <Paperclip :size="15" />
              </button>
              <div class="emoji-wrapper">
                <button
                  class="emoji-btn"
                  :class="{ active: showEmojiPicker }"
                  @click="showEmojiPicker = !showEmojiPicker"
                  title="表情包"
                >
                  <Smile :size="15" />
                </button>
                <EmojiPicker
                  :visible="showEmojiPicker"
                  @select="insertEmoji"
                  @close="showEmojiPicker = false"
                />
              </div>
              <button
                class="send-btn"
                :class="{ active: inputMessage.trim() }"
                :disabled="!inputMessage.trim() || isSending"
                @click="sendMessage"
              >
                <Send :size="15" />
                <CornerDownLeft :size="12" class="enter-hint" />
              </button>
            </div>
            <div class="input-hints">
              <span class="hint-item">ENTER 发送</span>
              <span class="hint-sep">|</span>
              <span class="hint-item">SHIFT+ENTER 换行</span>
            </div>
          </div>
        </div>

        <aside class="runtime-sidebar">
          <div class="sidebar-runtime-panel" :class="`is-${orchestrationStatus}`">
            <div class="section-label">// RUNTIME STATUS</div>
            <div class="runtime-meta-panel runtime-meta-panel-merged">
              <div class="runtime-panel-title">SYSTEM</div>
              <div class="runtime-meta-item">
                <span class="runtime-meta-key">DATE</span>
                <span class="runtime-meta-value">{{ formatClientDate(clientTime) }}</span>
              </div>
              <div class="runtime-meta-item">
                <span class="runtime-meta-key">TIME</span>
                <span class="runtime-meta-value">{{ formatClientClock(clientTime) }}</span>
              </div>
              <div class="runtime-meta-item">
                <span class="runtime-meta-key">WEATHER</span>
                <span class="runtime-meta-value weather-inline">
                  <component :is="weatherIcon" :size="14" class="weather-icon" />
                  <span>{{ weatherSummary }}</span>
                </span>
              </div>
              <div class="runtime-meta-item">
                <span class="runtime-meta-key">OS</span>
                <span class="runtime-meta-value">{{ systemMeta.systemName }}</span>
              </div>
              <div class="runtime-meta-item">
                <span class="runtime-meta-key">VERSION</span>
                <span class="runtime-meta-value">{{ systemMeta.systemVersion }}</span>
              </div>
              <div class="runtime-meta-item">
                <span class="runtime-meta-key">IP</span>
                <span class="runtime-meta-value">{{ currentIp() }}</span>
              </div>
              <div class="runtime-meta-item">
                <span class="runtime-meta-key">PORT</span>
                <span class="runtime-meta-value">{{ currentPort() }}</span>
              </div>
              <div class="runtime-meta-item">
                <span class="runtime-meta-key">UPTIME</span>
                <span class="runtime-meta-value">{{ formatUptime(systemMeta.uptimeSeconds) }}</span>
              </div>
            </div>
            <div class="runtime-grid">
              <div class="runtime-card cpu-card">
                <div class="runtime-topline">
                  <span class="runtime-label">CPU</span>
                  <span class="runtime-value">{{ systemTelemetry.cpu.online && systemTelemetry.cpu.current !== null ? `${systemTelemetry.cpu.current}%` : 'OFFLINE' }}</span>
                </div>
                <TelemetrySparkline :points="systemTelemetry.cpu.series" stroke="#22d3ee" fill="rgba(34, 211, 238, 0.16)" />
              </div>
              <div class="runtime-card gpu-card">
                <div class="runtime-topline">
                  <span class="runtime-label">GPU</span>
                  <span class="runtime-value">{{ systemMeta.gpuUtilPercent !== null ? `${Math.round(systemMeta.gpuUtilPercent)}%` : 'N/A' }}</span>
                </div>
                <div class="gpu-info-stats">
                  <div class="gpu-info-row">
                    <span class="gpu-info-key">DEVICE</span>
                    <span class="gpu-info-value gpu-info-copy">{{ systemMeta.gpuName || 'GPU unavailable' }}</span>
                  </div>
                  <div class="gpu-info-row">
                    <span class="gpu-info-key">VRAM</span>
                    <span class="gpu-info-value">{{ formatMemoryMb(systemMeta.gpuMemoryUsedMb) }} / {{ formatMemoryMb(systemMeta.gpuMemoryTotalMb) }}</span>
                  </div>
                </div>
                <TelemetrySparkline
                  :points="systemTelemetry.gpu.series"
                  stroke="#f472b6"
                  fill="rgba(244, 114, 182, 0.16)"
                />
              </div>
              <div class="runtime-card mem-card">
                <div class="runtime-topline">
                  <span class="runtime-label">MEM</span>
                  <span class="runtime-value">{{ systemTelemetry.memory.online && systemTelemetry.memory.current !== null ? `${systemTelemetry.memory.current}%` : 'OFFLINE' }}</span>
                </div>
                <TelemetrySparkline :points="systemTelemetry.memory.series" stroke="#a78bfa" fill="rgba(167, 139, 250, 0.14)" />
              </div>
              <div class="runtime-card disk-card">
                <div class="runtime-topline">
                  <span class="runtime-label">DISK</span>
                  <span class="runtime-value">{{ systemTelemetry.disk.online && systemTelemetry.disk.current !== null ? `${systemTelemetry.disk.current}%` : 'OFFLINE' }}</span>
                </div>
                <div class="gpu-info-stats">
                  <div class="gpu-info-row">
                    <span class="gpu-info-key">CAPACITY</span>
                    <span class="gpu-info-value">{{ `${formatDiskGb(systemMeta.diskUsedGb)} / ${formatDiskGb(systemMeta.diskTotalGb)}` }}</span>
                  </div>
                </div>
                <TelemetrySparkline :points="systemTelemetry.disk.series" stroke="#4ade80" fill="rgba(74, 222, 128, 0.14)" />
              </div>
              <div class="runtime-card network-card">
                <div class="runtime-topline">
                  <span class="runtime-label">NETWORK</span>
                  <span class="runtime-value">{{ systemTelemetry.network.upload.online ? 'LIVE' : 'OFFLINE' }}</span>
                </div>
                <div class="network-metric-head">
                  <div class="network-legend">
                    <span class="network-direction up">↑</span>
                    <span class="network-speed up">{{ formatNetworkRate(systemTelemetry.network.upload.current, systemTelemetry.network.upload.online) }}</span>
                  </div>
                  <div class="network-legend">
                    <span class="network-direction down">↓</span>
                    <span class="network-speed down">{{ formatNetworkRate(systemTelemetry.network.download.current, systemTelemetry.network.download.online) }}</span>
                  </div>
                </div>
                <div class="network-overlay-chart">
                  <TelemetrySparkline :points="systemTelemetry.network.upload.series" stroke="#2563eb" fill="rgba(37, 99, 235, 0.10)" />
                  <TelemetrySparkline :points="systemTelemetry.network.download.series" stroke="#38bdf8" fill="rgba(56, 189, 248, 0.06)" />
                </div>
              </div>
            </div>
          </div>
          <div class="runtime-sidebar-footer">
            <button class="event-feed runtime-feed runtime-feed-launch" type="button" @click="openOrchestrationDrawer">
              <div class="feed-hero">
                <div class="feed-title">Recent Events</div>
                <div class="feed-hero-meta">
                  <div class="runtime-log-count">{{ orchestrationEventFeed.length }}</div>
                  <ChevronRight :size="16" class="feed-launch-arrow" />
                </div>
              </div>
            </button>
          </div>
        </aside>
        <div class="knowledge-hud-shell" :class="{ open: knowledgeHudOpen }">
          <button
            v-if="knowledgeHudOpen"
            class="knowledge-hud-backdrop"
            type="button"
            aria-label="Close knowledge HUD"
            @click="closeKnowledgeHud"
          ></button>
          <section class="knowledge-hud" :class="{ open: knowledgeHudOpen }">
            <div class="knowledge-hud-frame">
              <div class="knowledge-hud-chrome">
                <div class="knowledge-hud-title">
                  <Database :size="14" />
                  <span>KNOWLEDGE ARCHIVE HUD</span>
                </div>
                <button class="knowledge-hud-close" type="button" aria-label="Close knowledge HUD" @click="closeKnowledgeHud">
                  <X :size="16" />
                </button>
              </div>
              <div class="knowledge-hud-body">
                <KnowledgeHudPanel />
              </div>
            </div>
          </section>
        </div>

        <div class="agent-drawer-shell" :class="{ open: orchestrationDrawerOpen }">
          <button
            v-if="orchestrationDrawerOpen"
            class="agent-drawer-backdrop"
            type="button"
            aria-label="Close agent drawer"
            @click="closeOrchestrationDrawer"
          ></button>
          <aside class="agent-drawer" :class="{ open: orchestrationDrawerOpen }">
            <OrchestrationPanel
              :visible="true"
              :status="orchestrationStatus"
              :insight="orchestrationInsight"
              :active-agent="activeAgent"
              :visited-agents="visitedAgents"
              :events="orchestrationEventFeed"
              :system-telemetry="{
                cpu: systemTelemetry.cpu,
                memory: systemTelemetry.memory,
                disk: systemTelemetry.disk,
              }"
              :session-telemetry="sessionTelemetry"
            />
          </aside>
        </div>

        <!-- Knowledge Side Panel (Phase 02) -->
        <Transition name="slide">
          <KnowledgeSlidePanel 
            v-if="selectedFolder" 
            :folder="selectedFolder" 
            @close="selectedFolder = null"
            @open-preview="handleOpenPreview"
            @trigger-new-folder="openNewFolderDialog"
            @trigger-upload="triggerUpload"
          />
        </Transition>

        <!-- Knowledge HUD Preview (Phase 03) -->
        <Transition name="fade">
          <KnowledgeHUDPreview 
            v-if="previewDoc" 
            :doc="previewDoc" 
            @close="previewDoc = null"
          />
        </Transition>

        <!-- Global Dialogs for Knowledge -->
        <input ref="uploadInput" type="file" class="hidden-upload" accept=".pdf,.md,.txt,.doc,.docx,.csv,.xlsx" @change="handleUpload" />

        <!-- Reminder Toast -->
        <ReminderToast
          :reminder="activeReminder"
          :visible="reminderVisible"
          @snooze="handleSnooze"
          @dismiss="handleDismiss"
        />

        <div v-if="showNewFolderDialog" class="knowledge-hud-preview" @click.self="showNewFolderDialog = false">
          <div class="hud-dialog-jarvis">
            <div class="dialog-header-tech">
              <span class="sub-kicker">INIT_NODE_PROCEDURE</span>
              <button class="close-jarvis-btn small" @click="showNewFolderDialog = false"><X :size="14" /></button>
            </div>
            <div class="dialog-body-tech">
              <p class="dialog-label">ENTER_VIRTUAL_ADDRESS_NAME</p>
              <input v-model="newFolderName" class="jarvis-input" placeholder="SECTOR_NAME..." @keyup.enter="createFolder" autofocus />
            </div>
            <div class="dialog-actions-tech">
              <button class="action-btn-jarvis small" @click="showNewFolderDialog = false">ABORT</button>
              <button class="action-btn-jarvis amber small" @click="createFolder">INITIALIZE</button>
            </div>
          </div>
        </div>
        </div>
        </section>
        </div>
        </template>

        <style scoped>
        .hidden-upload { display: none; }
        .hud-dialog-jarvis { width: 400px; background: var(--jarvis-bg); border: 1px solid var(--jarvis-cyan); padding: 2px; box-shadow: 0 0 50px rgba(0,0,0,0.8); }
        .dialog-header-tech { padding: 15px; border-bottom: 1px solid rgba(0, 245, 212, 0.2); display: flex; justify-content: space-between; }
        .dialog-body-tech { padding: 25px; }
        .dialog-label { font-family: var(--font-mono); font-size: 9px; color: var(--jarvis-cyan); margin-bottom: 10px; opacity: 0.6; }
        .jarvis-input { width: 100%; background: rgba(0,0,0,0.3); border: 1px solid rgba(0, 245, 212, 0.2); padding: 12px; color: #fff; font-family: var(--font-mono); outline: none; }
        .jarvis-input:focus { border-color: var(--jarvis-cyan); box-shadow: 0 0 10px rgba(0, 245, 212, 0.2); }
        .dialog-actions-tech { padding: 15px; display: flex; justify-content: flex-end; gap: 10px; border-top: 1px solid rgba(255,255,255,0.05); }
        .action-btn-jarvis.small { padding: 4px 10px; }

.chat-view {
  display: flex;
  height: 100%;
  overflow: hidden;
}

.sidebar-runtime-panel {
  flex: 1;
  padding: 12px 14px;
  border-bottom: 1px solid var(--border-dim);
  display: flex;
  flex-direction: column;
  gap: 10px;
  background: linear-gradient(180deg, rgba(7, 12, 24, 0.92), rgba(7, 11, 20, 0.82));
  overflow-y: auto;
  min-height: 0;
}

.runtime-sidebar-footer {
  flex-shrink: 0;
  padding: 12px 14px 14px;
  border-top: 1px solid rgba(34, 211, 238, 0.08);
  background: linear-gradient(180deg, rgba(6, 10, 18, 0.96), rgba(5, 8, 16, 0.98));
}

.runtime-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 8px;
}

.runtime-env-panel {
  display: grid;
  gap: 8px;
  padding: 10px;
  border: 1px solid rgba(56, 189, 248, 0.14);
  border-radius: 12px;
  background: linear-gradient(180deg, rgba(8, 18, 36, 0.78), rgba(8, 14, 26, 0.62));
}

.runtime-env-item {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

.runtime-env-key {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  color: #7dd3fc;
  flex-shrink: 0;
}

.runtime-env-value {
  font-family: var(--font-display);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--text-primary);
  text-align: right;
  word-break: break-word;
}

.weather-inline {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.weather-icon {
  color: #7dd3fc;
  flex-shrink: 0;
}

.runtime-meta-panel {
  display: grid;
  gap: 8px;
  padding: 10px;
  border: 1px solid rgba(148, 163, 184, 0.12);
  border-radius: 12px;
  background: rgba(8, 14, 26, 0.68);
}

.runtime-meta-item {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

.runtime-meta-key {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  color: var(--text-dim);
  flex-shrink: 0;
}

.runtime-meta-value {
  font-family: var(--font-display);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--text-primary);
  text-align: right;
  word-break: break-all;
}

.runtime-card,
.runtime-activity-card {
  border: 1px solid rgba(34, 211, 238, 0.12);
  border-radius: 12px;
  background: rgba(10, 16, 30, 0.74);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.03);
  padding: 10px;
}

.runtime-topline,
.runtime-activity-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
}

.runtime-label,
.runtime-activity-title,
.runtime-stat-key {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  color: var(--text-dim);
}

.runtime-value,
.runtime-stat-value {
  font-family: var(--font-display);
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--text-primary);
}

.runtime-badge {
  padding: 3px 7px;
  border-radius: 999px;
  border: 1px solid rgba(245, 158, 11, 0.2);
  background: rgba(245, 158, 11, 0.1);
  color: var(--accent-amber);
  font-family: var(--font-mono);
  font-size: 8px;
  letter-spacing: 0.14em;
}

.cpu-card { border-color: rgba(34, 211, 238, 0.22); }
.gpu-card { border-color: rgba(244, 114, 182, 0.24); }
.mem-card { border-color: rgba(167, 139, 250, 0.22); }
.disk-card { border-color: rgba(74, 222, 128, 0.22); }
.network-card { border-color: rgba(37, 99, 235, 0.26); }

.gpu-info-copy {
  font-family: var(--font-display);
  font-size: 10px;
  line-height: 1.5;
  color: var(--text-primary);
  text-align: right;
  word-break: break-word;
}


.disk-capacity-copy {
  margin-bottom: 8px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-secondary);
}

.gpu-info-stats {
  margin-top: 8px;
  display: grid;
  gap: 4px;
}

.gpu-info-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.gpu-info-key {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  color: var(--text-dim);
}

.gpu-info-value {
  font-family: var(--font-display);
  font-size: 10px;
  color: var(--text-primary);
}

.network-metric-head,
.feed-title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.network-metric-head {
  margin-bottom: 8px;
}

.network-legend {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.network-direction {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  color: var(--text-dim);
}

.network-speed,
.runtime-log-count {
  font-family: var(--font-display);
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--accent-cyan);
}

.network-direction.up,
.network-speed.up {
  color: #2563eb;
}

.network-direction.down,
.network-speed.down {
  color: #38bdf8;
}

.network-overlay-chart {
  position: relative;
}

.network-overlay-chart :deep(svg) {
  display: block;
}

.network-overlay-chart :deep(svg + svg) {
  margin-top: -34px;
}

.runtime-feed {
  margin-top: 0;
}

.runtime-feed-launch {
  width: 100%;
  padding: 12px 14px;
  border: 1px solid rgba(34, 211, 238, 0.14);
  border-radius: 14px;
  background:
    radial-gradient(circle at top right, rgba(34, 211, 238, 0.12), transparent 36%),
    linear-gradient(180deg, rgba(8, 16, 30, 0.9), rgba(8, 14, 26, 0.72));
  text-align: left;
  color: var(--text-primary);
  cursor: pointer;
  transition: transform var(--transition-fast), border-color var(--transition-fast), box-shadow var(--transition-fast);
  appearance: none;
  -webkit-appearance: none;
}

.runtime-feed-launch:hover {
  transform: translateX(-2px);
  border-color: rgba(34, 211, 238, 0.26);
  box-shadow: 0 12px 28px rgba(4, 10, 22, 0.34), 0 0 18px rgba(34, 211, 238, 0.08);
}

.feed-hero {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.feed-hero-meta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.runtime-feed-launch .feed-title {
  color: var(--text-primary);
}

.runtime-feed-launch .runtime-log-count {
  color: #f8fafc;
}

.feed-launch-arrow {
  color: var(--accent-cyan);
}

.feed-empty {
  font-size: 12px;
  color: var(--text-muted);
}

.feed-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.feed-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 12px;
  color: var(--text-secondary);
}

.feed-marker {
  width: 7px;
  height: 7px;
  margin-top: 5px;
  border-radius: 50%;
  background: rgba(34, 211, 238, 0.5);
  box-shadow: 0 0 8px rgba(34, 211, 238, 0.2);
  flex-shrink: 0;
}

.feed-item.tool .feed-marker {
  background: var(--accent-amber);
  box-shadow: 0 0 8px rgba(249, 168, 37, 0.24);
}

.feed-item.success .feed-marker {
  background: var(--accent-green);
  box-shadow: 0 0 8px rgba(74, 222, 128, 0.24);
}

.feed-item.error .feed-marker {
  background: var(--accent-red);
  box-shadow: 0 0 8px rgba(255, 71, 87, 0.24);
}

.feed-label {
  line-height: 1.5;
}

.agent-drawer-shell {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 30;
}

.agent-drawer-shell.open {
  pointer-events: auto;
}

.agent-drawer-backdrop {
  position: absolute;
  inset: 0;
  border: none;
  background: rgba(2, 6, 14, 0.52);
  backdrop-filter: blur(4px);
}

.agent-drawer {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  transform: translateX(100%);
  transition: transform var(--transition-mid);
}

.agent-drawer.open {
  transform: translateX(0);
}

.agent-drawer :deep(.orchestration-panel) {
  height: 100%;
  padding: 12px 12px 12px 0;
}

.agent-drawer :deep(.panel-frame) {
  height: 100%;
}

.agent-drawer-close {
  position: absolute;
  top: 16px;
  right: 72px;
  z-index: 2;
  width: 34px;
  height: 34px;
  border-radius: 10px;
  border: 1px solid rgba(148, 163, 184, 0.16);
  background: rgba(8, 14, 26, 0.86);
  color: var(--text-dim);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: border-color var(--transition-fast), color var(--transition-fast), background var(--transition-fast);
}

.agent-drawer-close:hover {
  border-color: rgba(34, 211, 238, 0.26);
  color: var(--accent-cyan);
  background: rgba(8, 16, 30, 0.96);
}

.knowledge-hud-shell {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 35;
}

.knowledge-hud-shell.open {
  pointer-events: auto;
}

.knowledge-hud-backdrop {
  position: absolute;
  inset: 0;
  border: none;
  background:
    radial-gradient(circle at top center, rgba(34, 211, 238, 0.12), transparent 32%),
    rgba(2, 6, 14, 0.68);
  backdrop-filter: blur(10px);
}

.knowledge-hud {
  position: absolute;
  inset: 26px 36px 26px 20px;
  transform: scale(0.96);
  opacity: 0;
  transition: transform var(--transition-mid), opacity var(--transition-mid);
}

.knowledge-hud.open {
  transform: scale(1);
  opacity: 1;
}

.knowledge-hud-frame {
  position: relative;
  height: 100%;
  border: 1px solid rgba(34, 211, 238, 0.22);
  background:
    linear-gradient(180deg, rgba(8, 16, 30, 0.96), rgba(5, 10, 20, 0.92));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.05),
    0 0 0 1px rgba(34, 211, 238, 0.08),
    0 24px 80px rgba(2, 6, 14, 0.54);
  overflow: hidden;
}

.knowledge-hud-frame::before,
.knowledge-hud-frame::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.knowledge-hud-frame::before {
  background:
    linear-gradient(90deg, rgba(34, 211, 238, 0.08), transparent 14%, transparent 86%, rgba(34, 211, 238, 0.08)),
    linear-gradient(180deg, rgba(34, 211, 238, 0.08), transparent 16%, transparent 84%, rgba(245, 158, 11, 0.08));
}

.knowledge-hud-frame::after {
  background:
    radial-gradient(circle at top right, rgba(34, 211, 238, 0.14), transparent 24%),
    radial-gradient(circle at bottom left, rgba(245, 158, 11, 0.08), transparent 28%);
}

.knowledge-hud-chrome {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 18px;
  border-bottom: 1px solid rgba(34, 211, 238, 0.12);
  background: linear-gradient(180deg, rgba(10, 22, 38, 0.88), rgba(8, 16, 28, 0.72));
}

.knowledge-hud-title {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-size: 12px;
  letter-spacing: 0.18em;
  color: #c8f7ff;
  text-shadow: 0 0 14px rgba(34, 211, 238, 0.16);
}

.knowledge-hud-close {
  width: 34px;
  height: 34px;
  border: 1px solid rgba(148, 163, 184, 0.16);
  background: rgba(8, 14, 26, 0.78);
  color: var(--text-dim);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: border-color var(--transition-fast), color var(--transition-fast), background var(--transition-fast);
}

.knowledge-hud-close:hover {
  border-color: rgba(34, 211, 238, 0.28);
  color: var(--accent-cyan);
  background: rgba(8, 18, 32, 0.94);
}

.knowledge-hud-body {
  position: relative;
  z-index: 1;
  height: calc(100% - 63px);
  overflow: hidden;
}

.knowledge-hud-body :deep(.knowledge-view) {
  height: 100%;
  background: transparent;
}

.knowledge-hud-body :deep(.page-header) {
  padding-top: 18px;
}

@media (max-width: 960px) {
  .knowledge-hud {
    inset: 16px 14px 16px 14px;
  }
}

/* ── Conversation Sidebar ── */
.conv-sidebar {
  width: 260px;
  min-width: 260px;
  background: var(--bg-panel);
  border-right: 1px solid var(--border-dim);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.runtime-sidebar {
  width: 280px;
  min-width: 280px;
  background: var(--bg-panel);
  border-left: 1px solid var(--border-dim);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.conv-sidebar-header {
  padding: 12px 14px 10px;
  border-bottom: 1px solid var(--border-dim);
}

.section-label {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.15em;
  color: var(--text-dim);
}

.sidebar-runtime-panel .section-label {
  margin-bottom: 10px;
}

.conv-sidebar-header .section-label {
  margin-bottom: 0;
}

.conv-sidebar-footer {
  padding: 12px 14px 14px;
  border-top: 1px solid var(--border-dim);
}

.new-chat-btn.muted {
  background: rgba(0, 245, 212, 0.08);
}

.new-chat-btn.muted:hover {
  background: rgba(0, 245, 212, 0.14);
}

.new-chat-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 8px 12px;
  background: var(--accent-cyan-dim);
  border: 1px solid var(--border-mid);
  border-radius: var(--radius-md);
  color: var(--accent-cyan);
  font-size: 10px;
  letter-spacing: 0.1em;
  font-weight: 600;
  transition: all var(--transition-fast);
  position: relative;
  overflow: hidden;
}

.new-chat-btn::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, rgba(0,245,212,0.1), transparent);
  transform: translateX(-100%);
  transition: transform 0.4s;
}

.new-chat-btn:hover::before { transform: translateX(100%); }

.new-chat-btn:hover {
  background: rgba(0, 245, 212, 0.18);
  box-shadow: var(--glow-cyan);
}

.btn-line {
  width: 1px;
  height: 12px;
  background: var(--accent-cyan);
  opacity: 0.6;
}

.conv-list {
  flex: 1;
  overflow-y: auto;
  padding: 8px;
}

.conv-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: var(--radius-md);
  cursor: pointer;
  margin-bottom: 2px;
  border: 1px solid transparent;
  transition: all var(--transition-fast);
  overflow: hidden;
}

.conv-item:hover {
  background: rgba(0, 245, 212, 0.04);
  border-color: var(--border-dim);
}

.conv-item.active {
  background: var(--accent-cyan-dim);
  border-color: var(--border-mid);
}

.conv-active-line {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--accent-cyan);
  opacity: 0;
  transition: opacity var(--transition-fast);
  box-shadow: 0 0 6px var(--accent-cyan);
}

.conv-item.active .conv-active-line { opacity: 1; }

.conv-item-icon {
  color: var(--text-dim);
  flex-shrink: 0;
}

.conv-item.active .conv-item-icon { color: var(--accent-cyan); }

.conv-item-body { flex: 1; min-width: 0; }

.conv-title {
  font-size: 11px;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-bottom: 2px;
}

.conv-item.active .conv-title { color: var(--accent-cyan); }

.conv-date {
  font-size: 9px;
  color: var(--text-dim);
  font-family: var(--font-mono);
}

.conv-delete {
  background: none;
  border: none;
  color: var(--text-dim);
  cursor: pointer;
  padding: 3px;
  opacity: 0;
  transition: all var(--transition-fast);
  border-radius: 3px;
}

.conv-item:hover .conv-delete { opacity: 1; }
.conv-delete:hover { color: var(--accent-red); background: rgba(255,71,87,0.1); }

.conv-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 40px 16px;
  gap: 8px;
}

.empty-icon { color: var(--text-dim); }
.empty-text { font-size: 12px; color: var(--text-dim); }
.empty-hint { font-size: 10px; color: var(--text-muted); }

/* ── Chat Area ── */
.chat-area {
  flex: 1;
  overflow: hidden;
  position: relative;
}

.chat-area::before,
.chat-area::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.chat-area::before {
  background:
    linear-gradient(
      180deg,
      rgba(34, 211, 238, 0) 0%,
      rgba(34, 211, 238, 0.012) 44%,
      rgba(34, 211, 238, 0.055) 49%,
      rgba(125, 211, 252, 0.09) 50%,
      rgba(34, 211, 238, 0.055) 51%,
      rgba(34, 211, 238, 0.012) 56%,
      rgba(34, 211, 238, 0) 100%
    );
  transform: translateY(-100%);
  opacity: 0.62;
  filter: blur(1.5px);
  animation: chat-scan-sweep 10.5s ease-in-out infinite;
}

.chat-area::after {
  background:
    repeating-linear-gradient(
      180deg,
      rgba(125, 211, 252, 0.012) 0px,
      rgba(125, 211, 252, 0.012) 0.5px,
      transparent 0.5px,
      transparent 6px
    );
  opacity: 0.18;
  animation: chat-scan-drift 14s linear infinite;
}

.chat-shell {
  display: flex;
  height: 100%;
  overflow: hidden;
  position: relative;
  z-index: 1;
}

.chat-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

@media (max-width: 1200px) {
  .runtime-sidebar {
    width: 240px;
    min-width: 240px;
  }
}

@media (max-width: 960px) {
  .chat-view {
    flex-direction: column;
  }

  .conv-sidebar,
  .runtime-sidebar {
    width: 100%;
    min-width: 0;
  }

  .conv-sidebar {
    max-height: 220px;
    border-right: none;
    border-bottom: 1px solid var(--border-dim);
  }

  .runtime-sidebar {
    max-height: 260px;
    border-left: none;
    border-top: 1px solid var(--border-dim);
  }
}

.chat-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 24px;
  border-bottom: 1px solid var(--border-dim);
  background: rgba(5, 8, 16, 0.6);
  backdrop-filter: blur(8px);
}

.chat-shortcuts {
  display: flex;
  align-items: center;
  min-width: 0;
}


.chat-model-panel {
  display: flex;
  align-items: center;
  gap: 10px;
}

.chat-model-label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  color: var(--text-dim);
}

.chat-model-select {
  min-width: 220px;
  padding: 7px 12px;
  border: 1px solid rgba(34, 211, 238, 0.2);
  border-radius: 10px;
  background: rgba(10, 16, 28, 0.92);
  color: var(--text-main);
  font-family: var(--font-mono);
  font-size: 12px;
  outline: none;
}

.chat-model-select:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.chat-model {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-display);
  font-size: 9px;
  letter-spacing: 0.1em;
  color: var(--accent-amber);
  padding: 3px 10px;
  border: 1px solid rgba(249, 168, 37, 0.2);
  border-radius: 20px;
  background: var(--accent-amber-dim);
}

/* ── Messages ── */
.messages-area {
  flex: 1;
  overflow-y: auto;
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* Welcome screen */
.welcome-screen {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding-bottom: 80px;
}

.welcome-icon {
  position: relative;
  width: 80px;
  height: 80px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--accent-cyan);
  margin-bottom: 8px;
}

.welcome-ring {
  position: absolute;
  border-radius: 50%;
  border: 1px solid var(--accent-cyan);
  animation: spin linear infinite;
}

.r1 { width: 80px; height: 80px; opacity: 0.3; animation-duration: 8s; border-style: dashed; }
.r2 { width: 60px; height: 60px; opacity: 0.5; animation-duration: 5s; animation-direction: reverse; }
.r3 { width: 40px; height: 40px; opacity: 0.7; animation-duration: 3s; }

.welcome-core {
  position: relative;
  z-index: 1;
  filter: drop-shadow(0 0 12px var(--accent-cyan));
  animation: pulse-glow 2s ease-in-out infinite;
}

.welcome-title {
  font-family: var(--font-display);
  font-size: 32px;
  font-weight: 800;
  letter-spacing: 0.2em;
  color: var(--accent-cyan);
  text-shadow: var(--glow-cyan);
}

.welcome-sub {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.3em;
  color: var(--text-dim);
  text-transform: uppercase;
}

.welcome-hint {
  font-size: 13px;
  color: var(--text-dim);
  margin-top: 20px;
}

/* Message rows */
.message-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 8px 0;
  animation: fade-in-up 0.3s ease both;
}

.message-row.user {
  flex-direction: row-reverse;
}

.message-row.assistant {
  justify-content: flex-start;
}

.msg-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 700;
  flex-shrink: 0;
  margin-top: 4px;
}

.user .msg-avatar {
  background: rgba(0, 245, 212, 0.1);
  border: 1px solid var(--border-mid);
  color: var(--accent-cyan);
}

.assistant .msg-avatar {
  background: linear-gradient(135deg, var(--accent-cyan-dim), var(--accent-purple-dim));
  border: 1px solid var(--border-bright);
  color: var(--accent-cyan);
  box-shadow: 0 0 10px var(--accent-cyan-glow);
}

.msg-content {
  display: flex;
  flex-direction: column;
  min-width: 0;
  max-width: min(75%, 820px);
}

.user .msg-content {
  align-items: flex-end;
}

.assistant .msg-content {
  align-items: flex-start;
}

.msg-meta {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 4px;
  flex-wrap: wrap;
}

.user .msg-meta {
  justify-content: flex-end;
}

.msg-role {
  font-family: var(--font-display);
  font-size: 9px;
  letter-spacing: 0.15em;
  color: var(--text-dim);
}

.msg-model {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--accent-cyan);
  letter-spacing: 0.08em;
}

.msg-time {
  font-family: var(--font-mono);
  font-size: 9px;
  color: var(--text-muted);
}

.msg-bubble {
  display: inline-block;
  max-width: 100%;
  padding: 10px 16px;
  border-radius: var(--radius-md);
  font-size: 13px;
  line-height: 1.65;
  word-break: break-word;
  white-space: pre-wrap;
}

.user .msg-bubble {
  background: var(--accent-cyan-dim);
  border: 1px solid rgba(0, 245, 212, 0.2);
  border-radius: var(--radius-md) var(--radius-md) 4px var(--radius-md);
  color: var(--text-primary);
}

.assistant .msg-bubble {
  background: rgba(13, 21, 37, 0.8);
  border: 1px solid var(--border-dim);
  border-radius: var(--radius-md) var(--radius-md) var(--radius-md) 4px;
  color: var(--text-secondary);
  backdrop-filter: blur(4px);
}

.think-panel {
  margin-bottom: 8px;
}

.think-summary {
  cursor: pointer;
  list-style: none;
  user-select: none;
}

.think-summary::-webkit-details-marker {
  display: none;
}

.think-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border-radius: 999px;
  border: 1px solid rgba(167, 139, 250, 0.22);
  background: linear-gradient(135deg, rgba(40, 24, 68, 0.72), rgba(18, 24, 48, 0.72));
  color: #c4b5fd;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.16em;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.03), 0 0 12px rgba(167, 139, 250, 0.08);
}

.think-chip-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 8px currentColor;
}

.think-content {
  margin-top: 8px;
  padding: 10px 12px;
  border: 1px solid rgba(167, 139, 250, 0.14);
  border-radius: 12px;
  background: rgba(18, 20, 34, 0.55);
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.think-block {
  font-size: 12px;
  line-height: 1.6;
  color: var(--text-dim);
}

.markdown-body :deep(p),
.think-block :deep(p) {
  margin: 0;
}

.markdown-body :deep(p + p),
.think-block :deep(p + p) {
  margin-top: 10px;
}

.markdown-body :deep(.md-heading),
.think-block :deep(.md-heading) {
  margin: 0 0 8px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-primary);
}

.markdown-body :deep(.md-list),
.think-block :deep(.md-list) {
  margin: 0;
  padding-left: 18px;
}

.markdown-body :deep(.md-list li),
.think-block :deep(.md-list li) {
  margin: 4px 0;
}

.markdown-body :deep(code),
.think-block :deep(code) {
  padding: 1px 5px;
  border-radius: 6px;
  background: rgba(148, 163, 184, 0.14);
  font-family: var(--font-mono);
  font-size: 12px;
}

.markdown-body :deep(.md-pre),
.think-block :deep(.md-pre) {
  margin: 0;
  padding: 10px 12px;
  border-radius: 10px;
  background: rgba(5, 10, 20, 0.78);
  border: 1px solid rgba(148, 163, 184, 0.12);
  overflow-x: auto;
}

.markdown-body :deep(.md-pre code),
.think-block :deep(.md-pre code) {
  padding: 0;
  background: transparent;
}

.markdown-body :deep(.md-table-wrap),
.think-block :deep(.md-table-wrap) {
  width: 100%;
  overflow-x: auto;
  margin: 2px 0;
  border-radius: 10px;
  border: 1px solid rgba(34, 211, 238, 0.16);
  background: rgba(5, 10, 20, 0.46);
}

.markdown-body :deep(.md-table),
.think-block :deep(.md-table) {
  width: 100%;
  min-width: 320px;
  border-collapse: collapse;
  font-size: 12px;
}

.markdown-body :deep(.md-table th),
.markdown-body :deep(.md-table td),
.think-block :deep(.md-table th),
.think-block :deep(.md-table td) {
  padding: 8px 10px;
  border: 1px solid rgba(148, 163, 184, 0.16);
  text-align: left;
  vertical-align: top;
}

.markdown-body :deep(.md-table th),
.think-block :deep(.md-table th) {
  background: rgba(34, 211, 238, 0.1);
  color: var(--text-primary);
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
}

.markdown-body :deep(.md-table tbody tr:nth-child(even)),
.think-block :deep(.md-table tbody tr:nth-child(even)) {
  background: rgba(148, 163, 184, 0.04);
}

.think-panel[open] .think-chip {
  border-color: rgba(167, 139, 250, 0.3);
  color: #ddd6fe;
}

.think-panel[open] .think-chip-dot {
  animation: pulse-glow 1.4s ease-in-out infinite;
}

/* Typing indicator */
.thinking-row {
  padding-top: 2px;
}

.thinking-bubble {
  min-width: 280px;
  padding: 12px 14px;
  background: linear-gradient(135deg, rgba(12, 20, 36, 0.92), rgba(16, 18, 34, 0.84));
  border-color: rgba(34, 211, 238, 0.18);
}

.thinking-hud {
  display: flex;
  align-items: center;
  gap: 14px;
}

.thinking-core {
  position: relative;
  width: 28px;
  height: 28px;
  flex-shrink: 0;
}

.thinking-ring,
.thinking-dot {
  position: absolute;
  inset: 0;
  border-radius: 50%;
}

.thinking-ring {
  border: 1px solid rgba(34, 211, 238, 0.45);
}

.ring-1 {
  animation: spin 2.6s linear infinite;
}

.ring-2 {
  inset: 4px;
  border-color: rgba(167, 139, 250, 0.38);
  animation: spin 1.8s linear infinite reverse;
}

.ring-3 {
  inset: 8px;
  border-color: rgba(245, 158, 11, 0.36);
  animation: pulse-glow 1.4s ease-in-out infinite;
}

.thinking-dot {
  inset: 11px;
  background: var(--accent-cyan);
  box-shadow: 0 0 10px rgba(34, 211, 238, 0.7);
}

.thinking-copy {
  min-width: 0;
  flex: 1;
}

.thinking-title {
  font-family: var(--font-display);
  font-size: 10px;
  letter-spacing: 0.14em;
  color: var(--accent-cyan);
}

.thinking-subtitle {
  margin-top: 3px;
  font-size: 12px;
  color: var(--text-dim);
}

.typing .dot,
.typing-inline .dot {
  display: inline-block;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent-cyan);
  margin: 0 2px;
  animation: typing-bounce 1.2s ease-in-out infinite;
}

.typing-inline {
  flex-shrink: 0;
  white-space: nowrap;
}

.typing .dot:nth-child(2),
.typing-inline .dot:nth-child(2) { animation-delay: 0.2s; }
.typing .dot:nth-child(3),
.typing-inline .dot:nth-child(3) { animation-delay: 0.4s; }

@keyframes typing-bounce {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
  30% { transform: translateY(-4px); opacity: 1; }
}

@keyframes chat-scan-sweep {
  0% { transform: translateY(-100%); opacity: 0; }
  12% { opacity: 0.38; }
  50% { opacity: 0.62; }
  88% { opacity: 0.38; }
  100% { transform: translateY(100%); opacity: 0; }
}

@keyframes chat-scan-drift {
  0% { transform: translateY(0); }
  100% { transform: translateY(12px); }
}

/* ── Input Area ── */
.input-area {
  padding: 16px 24px 20px;
  border-top: 1px solid var(--border-dim);
  background: rgba(5, 8, 16, 0.8);
  backdrop-filter: blur(12px);
}

.input-frame {
  position: relative;
  background: var(--bg-card);
  border: 1px solid var(--border-mid);
  border-radius: var(--radius-lg);
  padding: 12px 16px;
  display: flex;
  align-items: flex-end;
  gap: 12px;
  transition: all var(--transition-mid);
}

.input-frame:focus-within {
  border-color: var(--accent-cyan);
  box-shadow: 0 0 0 1px rgba(0,245,212,0.1), var(--glow-cyan);
}

/* Corner accents */
.input-corners {
  position: absolute;
  width: 8px;
  height: 8px;
  pointer-events: none;
}
.input-corners::before,
.input-corners::after {
  content: '';
  position: absolute;
  background: var(--accent-cyan);
}
.input-corners::before { width: 100%; height: 1px; }
.input-corners::after { width: 1px; height: 100%; }
.tl { top: -1px; left: -1px; }
.tr { top: -1px; right: -1px; transform: scaleX(-1); }
.bl { bottom: -1px; left: -1px; transform: scaleY(-1); }
.br { bottom: -1px; right: -1px; transform: scale(-1); }

.input-frame textarea {
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
  color: var(--text-primary);
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.6;
  resize: none;
  max-height: 120px;
  padding: 8px 0;
  vertical-align: middle;
  overflow: hidden;
}

.input-frame textarea::placeholder { color: var(--text-dim); }

.send-btn {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-md);
  background: var(--text-muted);
  border: 1px solid transparent;
  color: var(--text-dim);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  position: relative;
  transition: all var(--transition-fast);
}

.send-btn.active {
  background: var(--accent-cyan-dim);
  border-color: var(--border-mid);
  color: var(--accent-cyan);
}

.send-btn.active:hover {
  background: rgba(0, 245, 212, 0.2);
  box-shadow: var(--glow-cyan);
  transform: scale(1.05);
}

.enter-hint { display: none; }

.input-hints {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  padding-left: 4px;
}

.hint-item {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.1em;
  color: var(--text-muted);
}

.hint-sep {
  color: var(--text-muted);
  font-size: 9px;
}

/* File attachment button */
.attach-btn {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-md);
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-dim);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all var(--transition-fast);
}
.attach-btn:hover {
  background: var(--accent-cyan-dim);
  border-color: var(--border-mid);
  color: var(--accent-cyan);
}

/* Emoji button */
.emoji-wrapper {
  position: relative;
}
.emoji-btn {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-md);
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-dim);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all var(--transition-fast);
}
.emoji-btn:hover,
.emoji-btn.active {
  background: var(--accent-cyan-dim);
  border-color: var(--border-mid);
  color: var(--accent-cyan);
}

/* Message attachments */
.msg-attachments {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 8px;
}

/* ── JARVIS SIDEBAR STYLES ── */
.jarvis-sidebar {
  background: rgba(4, 10, 20, 0.95) !important;
  padding: 10px !important;
  gap: 12px;
}

.jarvis-panel {
  background: rgba(0, 20, 40, 0.5);
  border: 1px solid rgba(0, 243, 255, 0.15);
  padding: 12px;
  margin-bottom: 12px;
  position: relative;
  clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
}

.jarvis-panel::before {
  content: '';
  position: absolute;
  top: 0; left: 0; width: 6px; height: 6px;
  border-left: 1px solid #00f3ff; border-top: 1px solid #00f3ff;
}

.jarvis-date-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
}

.jarvis-date-num {
  font-family: var(--font-display);
  font-size: 32px;
  font-weight: 800;
  color: #00f3ff;
  text-shadow: 0 0 10px rgba(0, 243, 255, 0.5);
}

.jarvis-month {
  font-family: var(--font-mono);
  font-size: 10px;
  opacity: 0.6;
  letter-spacing: 0.1em;
}

.jarvis-time {
  font-family: var(--font-mono);
  font-size: 14px;
  color: #c8f7ff;
}

.jarvis-calendar {
  border-top: 1px solid rgba(0, 243, 255, 0.1);
  padding-top: 8px;
}

.calendar-header {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  font-size: 9px;
  text-align: center;
  opacity: 0.4;
  margin-bottom: 4px;
}

.calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
  font-family: var(--font-mono);
  font-size: 9px;
  text-align: center;
}

.calendar-day {
  padding: 3px 0;
  border-radius: 2px;
  color: rgba(0, 243, 255, 0.4);
}

.calendar-day.active {
  background: #00f3ff;
  color: #000;
  font-weight: 800;
  box-shadow: 0 0 10px #00f3ff;
}

.jarvis-section-label {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.2em;
  color: rgba(0, 243, 255, 0.6);
  margin-bottom: 8px;
  padding-left: 4px;
}

.jarvis-conv-list {
  padding: 0 !important;
  margin-bottom: 10px;
}

.jarvis-session-item {
  background: rgba(0, 243, 255, 0.03) !important;
  border-left: 2px solid transparent !important;
  border-bottom: 1px solid rgba(0, 243, 255, 0.05) !important;
  margin-bottom: 4px !important;
  border-radius: 0 !important;
  transition: all 0.3s;
}

.jarvis-session-item:hover {
  background: rgba(0, 243, 255, 0.08) !important;
  padding-left: 14px !important;
}

.jarvis-session-item.active {
  background: rgba(0, 243, 255, 0.12) !important;
  border-left-color: #00f3ff !important;
  box-shadow: inset 4px 0 15px rgba(0, 243, 255, 0.05);
}

.jarvis-text-glow {
  text-shadow: 0 0 8px rgba(0, 243, 255, 0.4);
}

.jarvis-section-title {
  font-family: var(--font-display);
  font-size: 10px;
  letter-spacing: 0.15em;
  color: #00f3ff;
  margin-bottom: 10px;
  border-bottom: 1px solid rgba(0, 243, 255, 0.1);
  padding-bottom: 4px;
}

.jarvis-progress-item {
  margin-bottom: 6px;
}

.jarvis-progress-label {
  display: flex;
  justify-content: space-between;
  font-size: 9px;
  font-family: var(--font-mono);
  opacity: 0.7;
  margin-bottom: 4px;
}

.jarvis-progress-bar {
  height: 3px;
  background: rgba(0, 243, 255, 0.1);
  width: 100%;
}

.jarvis-progress-fill {
  height: 100%;
  background: #00f3ff;
  box-shadow: 0 0 10px #00f3ff;
}

.jarvis-plan-list {
  list-style: none;
  font-size: 10px;
}

.jarvis-plan-item {
  display: flex;
  gap: 8px;
  padding: 4px 0;
  opacity: 0.8;
}

.jarvis-plan-item .num {
  color: #00f3ff;
  font-weight: bold;
}

.jarvis-new-btn {
  background: rgba(0, 243, 255, 0.1) !important;
  border: 1px solid rgba(0, 243, 255, 0.3) !important;
  color: #00f3ff !important;
  font-family: var(--font-display);
  height: 36px;
  box-shadow: 0 0 15px rgba(0, 243, 255, 0.05);
}

.jarvis-new-btn:hover {
  background: rgba(0, 243, 255, 0.2) !important;
  box-shadow: 0 0 20px rgba(0, 243, 255, 0.15);
}

.conv-sidebar-footer {
  border-top: 1px solid rgba(0, 243, 255, 0.1) !important;
}

/* ── COMMANDER CARDS ── */
.jarvis-commander-grid {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 16px;
}

.commander-card {
  position: relative;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 16px;
  background: rgba(0, 243, 255, 0.02);
  border: 1px solid rgba(0, 243, 255, 0.1);
  color: #00f3ff;
  text-align: left;
  cursor: pointer;
  transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1);
  overflow: hidden;
  border-radius: 2px;
}

.commander-card:hover {
  background: rgba(0, 243, 255, 0.08);
  border-color: rgba(0, 243, 255, 0.4);
  box-shadow: 0 0 20px rgba(0, 243, 255, 0.1);
  transform: translateY(-2px);
}

/* Internal Glow */
.commander-glow {
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at center, rgba(0, 243, 255, 0.15), transparent 70%);
  opacity: 0;
  transition: opacity 0.4s;
}
.commander-card:hover .commander-glow { opacity: 1; }

/* Scanning Line */
.commander-scan {
  position: absolute;
  top: 0; left: -100%; width: 100%; height: 100%;
  background: linear-gradient(90deg, transparent, rgba(0, 243, 255, 0.1), transparent);
  transition: none;
}
.commander-card:hover .commander-scan {
  animation: commander-scan-move 1.5s infinite;
}

@keyframes commander-scan-move {
  0% { left: -100%; }
  100% { left: 100%; }
}

/* Icon Box */
.commander-icon-box {
  width: 38px; height: 38px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 243, 255, 0.05);
  border: 1px solid rgba(0, 243, 255, 0.2);
  position: relative;
  z-index: 1;
}

.commander-info { position: relative; z-index: 1; flex: 1; }
.commander-title { font-size: 14px; font-weight: 700; letter-spacing: 0.02em; margin-bottom: 2px; }
.commander-status { font-family: var(--font-mono); font-size: 8px; opacity: 0.5; letter-spacing: 0.15em; }

/* Corner Accents */
.commander-corner {
  position: absolute; width: 4px; height: 4px;
  border: 1px solid #00f3ff;
}
.commander-corner.top-r { top: 0; right: 0; border-left: none; border-bottom: none; }
.commander-corner.bottom-l { bottom: 0; left: 0; border-right: none; border-top: none; }

/* Theme Colors */
.commander-card.schedule { color: #fbbf24; border-color: rgba(245, 158, 11, 0.1); }
.commander-card.schedule:hover { border-color: rgba(245, 158, 11, 0.4); background: rgba(245, 158, 11, 0.08); }
.commander-card.schedule .commander-icon-box { background: rgba(245, 158, 11, 0.05); border-color: rgba(245, 158, 11, 0.2); }
.commander-card.schedule .commander-corner { border-color: #fbbf24; }
.commander-card.schedule .commander-glow { background: radial-gradient(circle at center, rgba(245, 158, 11, 0.15), transparent 70%); }

.commander-card.code { color: #a78bfa; border-color: rgba(167, 139, 250, 0.1); }
.commander-card.code:hover { border-color: rgba(167, 139, 250, 0.4); background: rgba(167, 139, 250, 0.08); }
.commander-card.code .commander-icon-box { background: rgba(167, 139, 250, 0.05); border-color: rgba(167, 139, 250, 0.2); }
.commander-card.code .commander-corner { border-color: #a78bfa; }
.commander-card.code .commander-glow { background: radial-gradient(circle at center, rgba(167, 139, 250, 0.15), transparent 70%); }

/* ── RSS FEED ── */
.jarvis-rss-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.rss-item {
  font-family: var(--font-mono);
  font-size: 9px;
  line-height: 1.4;
  color: rgba(200, 247, 255, 0.7);
  padding: 4px;
  border-left: 1px solid rgba(0, 243, 255, 0.2);
  transition: color 0.2s;
}

.rss-item:hover {
  color: #00f3ff;
  background: rgba(0, 243, 255, 0.05);
}

.limited-height {
  max-height: 120px;
  overflow-y: auto;
}


/* Sidebar overrides */
.jarvis-sidebar {
  background: #f3f6fb !important;
  padding: 0 !important;
  border-right-color: rgba(15, 23, 42, 0.08);
}

.jarvis-sidebar-scroll {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px 12px;
  overflow-y: auto;
  min-height: 0;
}

.jarvis-sidebar .jarvis-panel {
  background: rgba(255, 255, 255, 0.94);
  border: 1px solid #e6ebf2;
  border-radius: 16px;
  padding: 16px 14px;
  margin-bottom: 0;
  position: relative;
  overflow: hidden;
  clip-path: none;
  box-shadow: 0 10px 28px rgba(15, 23, 42, 0.06);
}

.jarvis-sidebar .jarvis-panel::before {
  display: none;
}

.jarvis-date-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
}

.jarvis-date-num {
  min-width: 62px;
  font-family: var(--font-display);
  font-size: 42px;
  line-height: 1;
  font-weight: 800;
  color: #0f172a;
  text-shadow: none;
}

.jarvis-date-meta {
  min-width: 0;
}

.jarvis-month {
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: none;
  color: #1f2937;
}

.jarvis-time {
  margin-top: 4px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  color: #64748b;
}

.jarvis-calendar {
  border-top: 1px solid #edf2f7;
  padding-top: 12px;
}

.calendar-header,
.calendar-grid {
  grid-template-columns: repeat(7, minmax(0, 1fr));
}

.calendar-header {
  display: grid;
  gap: 6px;
  margin-bottom: 6px;
  font-family: var(--font-mono);
  font-size: 10px;
  text-align: center;
  color: #94a3b8;
}

.calendar-grid {
  display: grid;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 11px;
  text-align: center;
}

.calendar-day {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 28px;
  border-radius: 10px;
  border: 1px solid transparent;
  background: #f8fafc;
  color: #64748b;
}

.calendar-day.muted {
  background: transparent;
  color: transparent;
  border-color: transparent;
}

.calendar-day.busy::after {
  content: '';
  position: absolute;
  bottom: 4px;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: #2563eb;
}

.calendar-day.active {
  color: #2563eb;
  background: #eaf4ff;
  border-color: #bfdcff;
  font-weight: 700;
}

.calendar-day.active.busy::after {
  background: #2563eb;
}

.jarvis-action-row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  margin-top: 14px;
}

.jarvis-action-chip {
  padding: 8px 10px;
  border: 1px solid #d8e2ef;
  border-radius: 12px;
  background: #f8fafc;
  color: #475569;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
}

.jarvis-action-chip:hover {
  background: #eef6ff;
  border-color: #bfdbfe;
  color: #2563eb;
}

.jarvis-action-chip.schedule {
  color: #0369a1;
}

.jarvis-action-chip.code {
  color: #7c3aed;
}

.jarvis-section-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
  padding-bottom: 0;
  border-bottom: none;
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0.01em;
  color: #0f172a;
}

.jarvis-section-title::before {
  content: '';
  width: 4px;
  height: 16px;
  border-radius: 999px;
  background: #60a5fa;
  box-shadow: none;
}

.jarvis-status-shell {
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr);
  gap: 14px;
  align-items: center;
}

.jarvis-progress-ring {
  --completion: 0%;
  position: relative;
  width: 88px;
  height: 88px;
  border-radius: 50%;
  background: conic-gradient(#60a5fa var(--completion), #e5edf8 0);
  display: flex;
  align-items: center;
  justify-content: center;
}

.jarvis-progress-ring::before {
  content: '';
  position: absolute;
  inset: 8px;
  border-radius: 50%;
  background: #ffffff;
  border: 1px solid #edf2f7;
}

.jarvis-progress-core {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}

.jarvis-progress-core strong {
  font-family: var(--font-display);
  font-size: 20px;
  color: #0f172a;
}

.jarvis-progress-core span {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  color: #94a3b8;
}

.jarvis-status-copy {
  min-width: 0;
}

.jarvis-status-headline {
  margin-bottom: 10px;
  font-size: 13px;
  line-height: 1.6;
  color: #475569;
}

.jarvis-status-list,
.jarvis-focus-list,
.jarvis-review-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.jarvis-status-list {
  display: grid;
  gap: 8px;
}

.jarvis-status-item {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 8px;
  font-size: 12px;
}

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

.status-dot.done { background: #22c55e; }
.status-dot.doing { background: #f59e0b; }
.status-dot.pending { background: #ef4444; }

.status-label {
  color: #64748b;
}

.status-value {
  font-family: var(--font-mono);
  color: #0f172a;
}

.jarvis-focus-list {
  display: grid;
  gap: 10px;
}

.jarvis-focus-item {
  display: grid;
  grid-template-columns: 30px minmax(0, 1fr);
  gap: 10px;
  padding: 11px 12px;
  border-radius: 14px;
  border: 1px solid #e8edf4;
  background: #f8fafc;
}

.jarvis-focus-item.is-doing { border-color: #fde68a; }
.jarvis-focus-item.is-pending { border-color: #fecdd3; }
.jarvis-focus-item.is-done { border-color: #bbf7d0; }

.focus-order {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 10px;
  background: #eff6ff;
  color: #2563eb;
  font-family: var(--font-mono);
  font-size: 10px;
}

.focus-copy {
  min-width: 0;
}

.focus-label {
  margin-bottom: 4px;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  color: #94a3b8;
  text-transform: uppercase;
}

.focus-title {
  font-size: 13px;
  line-height: 1.45;
  color: #0f172a;
}

.focus-meta {
  margin-top: 4px;
  font-size: 11px;
  color: #64748b;
}

.jarvis-review-group + .jarvis-review-group {
  margin-top: 14px;
}

.jarvis-review-subtitle {
  margin-bottom: 8px;
  font-size: 12px;
  font-weight: 700;
  color: #334155;
}

.jarvis-review-list {
  display: grid;
  gap: 8px;
}

.jarvis-review-item {
  position: relative;
  padding-left: 14px;
  font-size: 12px;
  line-height: 1.6;
  color: #475569;
}

.jarvis-review-item::before {
  content: '';
  position: absolute;
  left: 0;
  top: 8px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #60a5fa;
}

.jarvis-review-list.reflection .jarvis-review-item::before {
  background: #f59e0b;
}

.jarvis-rss-list {
  display: grid;
  gap: 10px;
}

.jarvis-news-card {
  padding: 10px 12px;
  border-radius: 14px;
  border: 1px solid #e8edf4;
  background: #f8fafc;
}

.jarvis-news-meta {
  margin-bottom: 6px;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  color: #94a3b8;
  text-transform: uppercase;
}

.jarvis-news-title {
  font-size: 12px;
  line-height: 1.6;
  color: #0f172a;
}

.jarvis-empty-state {
  padding: 12px;
  border-radius: 14px;
  border: 1px dashed #d8e2ef;
  background: #f8fafc;
  font-size: 12px;
  line-height: 1.6;
  color: #64748b;
}

@media (max-width: 960px) {
  .jarvis-sidebar-scroll {
    max-height: 220px;
  }
}

</style>
