<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { ArrowRight, Bell, Calendar, Clock, Location, Reading } from '@element-plus/icons-vue'
import { useRouter } from 'vue-router'
import http from '../api/http'
import { useAuthStore } from '../stores/auth'

interface TimetableSlot {
  periodNumber: number
  startsAt: string
  endsAt: string
}

interface TimetableEntry {
  id: string
  courseCode: string
  courseName: string
  teacherNames: string[]
  classroomName: string
  buildingName?: string
  campusName?: string
  dayOfWeek: number
  startPeriod: number
  periodCount: number
  startWeek: number
  endWeek: number
  weekPattern: 'All' | 'Odd' | 'Even'
}

interface TimetableData {
  term: {
    name: string
    startDate: string
    endDate: string
  }
  slots: TimetableSlot[]
  entries: TimetableEntry[]
}

interface NotificationItem {
  id: string
  title: string
  content: string
  category: string
  isRead: boolean
  linkUrl?: string
  senderName: string
  createdAt: string
}

interface ExamItem {
  id: string
  examDate: string
  startsAt: string
  endsAt: string
  courseCode: string
  courseName: string
  buildingName?: string
  classroomName?: string
}

interface GradeItem {
  id: string
  termName: string
  courseCode: string
  courseName: string
  totalScore?: number
  gradePoint?: number
  examStatus: string
  publishedAt?: string
}

interface DashboardGreeting {
  title: string
  subtitle: string
  label: string
  narrative: string
  insights: Array<{
    label: string
    value: string
    hint: string
    tone: 'calm' | 'positive' | 'attention'
  }>
}

interface WarningItem {
  id: string
  type: number
  status: number
  detail: string
  createdAt: string
}

const router = useRouter()
const auth = useAuthStore()
const loading = ref(true)
const failedSections = ref<string[]>([])
const timetable = ref<TimetableData | null>(null)
const notifications = ref<NotificationItem[]>([])
const unreadCount = ref(0)
const exams = ref<ExamItem[]>([])
const grades = ref<GradeItem[]>([])
const dashboardGreeting = ref<DashboardGreeting | null>(null)
const warnings = ref<WarningItem[]>([])
const graduation = ref<any>(null)
const now = ref(new Date())

const categoryLabels: Record<string, string> = {
  General: '一般通知',
  Approval: '审核待办',
  Schedule: '课程变动',
  Grade: '成绩通知',
  Attendance: '考勤消息',
  CourseSelection: '选课消息',
  Warning: '学业预警',
}

const examStatusLabels: Record<string, string> = {
  Normal: '正常',
  Absent: '缺考',
  Deferred: '缓考',
  Exempt: '免修',
}

const greeting = computed(() => {
  const hour = now.value.getHours()
  if (hour < 11) return '早上好'
  if (hour < 14) return '中午好'
  if (hour < 18) return '下午好'
  return '晚上好'
})

const todayLabel = computed(() =>
  new Intl.DateTimeFormat('zh-CN', {
    month: 'long',
    day: 'numeric',
    weekday: 'long',
  }).format(now.value),
)

const currentWeek = computed(() => {
  const start = parseDateOnly(timetable.value?.term.startDate)
  const end = parseDateOnly(timetable.value?.term.endDate)
  if (!start || !end) return null
  const today = startOfDay(now.value)
  if (today < start || today > end) return null
  const mondayOffset = start.getDay() === 0 ? 6 : start.getDay() - 1
  const firstMonday = addDays(start, -mondayOffset)
  return Math.floor((today.getTime() - firstMonday.getTime()) / 604800000) + 1
})

const todayCourses = computed(() => {
  const week = currentWeek.value
  if (!week) return []
  const weekday = now.value.getDay() || 7
  return (timetable.value?.entries ?? [])
    .filter((entry) =>
      entry.dayOfWeek === weekday &&
      week >= entry.startWeek &&
      week <= entry.endWeek &&
      (entry.weekPattern === 'All' ||
        (entry.weekPattern === 'Odd' && week % 2 === 1) ||
        (entry.weekPattern === 'Even' && week % 2 === 0)),
    )
    .sort((a, b) => a.startPeriod - b.startPeriod)
})

const upcomingExams = computed(() =>
  exams.value
    .filter((exam) => {
      const endsAt = new Date(exam.endsAt)
      if (!Number.isNaN(endsAt.getTime())) return endsAt >= now.value
      const examDate = parseDateOnly(exam.examDate)
      return examDate ? examDate >= startOfDay(now.value) : false
    })
    .sort((a, b) => examTime(a) - examTime(b))
    .slice(0, 3),
)

const recentGrades = computed(() =>
  [...grades.value]
    .sort((a, b) => dateTime(b.publishedAt) - dateTime(a.publishedAt))
    .slice(0, 4),
)

const activeWarnings = computed(() => warnings.value.filter((warning) => warning.status === 1))
const radarSummary = computed(() => activeWarnings.value.length
  ? `发现 ${activeWarnings.value.length} 项需要你关注的学习风险，建议优先处理下方提示。`
  : '系统暂未发现需要你处理的学业风险，继续保持当前学习节奏。')

function parseDateOnly(value?: string) {
  if (!value) return null
  const [year, month, day] = value.slice(0, 10).split('-').map(Number)
  if (!year || !month || !day) return null
  return new Date(year, month - 1, day)
}

function startOfDay(value: Date) {
  return new Date(value.getFullYear(), value.getMonth(), value.getDate())
}

function addDays(value: Date, days: number) {
  const result = new Date(value)
  result.setDate(result.getDate() + days)
  return result
}

function dateTime(value?: string) {
  if (!value) return 0
  const parsed = new Date(value).getTime()
  return Number.isNaN(parsed) ? 0 : parsed
}

function examTime(exam: ExamItem) {
  const parsed = new Date(exam.startsAt).getTime()
  return Number.isNaN(parsed) ? dateTime(exam.examDate) : parsed
}

function slotTime(periodNumber: number, field: 'startsAt' | 'endsAt') {
  const slot = timetable.value?.slots.find((item) => item.periodNumber === periodNumber)
  return slot?.[field]?.slice(0, 5) ?? ''
}

function courseTime(entry: TimetableEntry) {
  const endPeriod = entry.startPeriod + entry.periodCount - 1
  const startsAt = slotTime(entry.startPeriod, 'startsAt')
  const endsAt = slotTime(endPeriod, 'endsAt')
  return startsAt && endsAt
    ? `${startsAt}—${endsAt}`
    : `第 ${entry.startPeriod}—${endPeriod} 节`
}

function courseLocation(entry: TimetableEntry) {
  return [entry.buildingName, entry.classroomName].filter(Boolean).join(' · ') || '地点待定'
}

function formatExamDate(exam: ExamItem) {
  const date = parseDateOnly(exam.examDate)
  if (!date) return '日期待定'
  return new Intl.DateTimeFormat('zh-CN', {
    month: 'numeric',
    day: 'numeric',
    weekday: 'short',
  }).format(date)
}

function formatExamTime(exam: ExamItem) {
  const start = new Date(exam.startsAt)
  const end = new Date(exam.endsAt)
  if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) return '时间待定'
  const formatter = new Intl.DateTimeFormat('zh-CN', {
    hour: '2-digit',
    minute: '2-digit',
    hour12: false,
  })
  return `${formatter.format(start)}—${formatter.format(end)}`
}

function examDistance(exam: ExamItem) {
  const date = parseDateOnly(exam.examDate)
  if (!date) return '待安排'
  const days = Math.round((date.getTime() - startOfDay(now.value).getTime()) / 86400000)
  if (days === 0) return '今天'
  if (days === 1) return '明天'
  return `${days} 天后`
}

function messageTime(value: string) {
  const date = new Date(value)
  if (Number.isNaN(date.getTime())) return ''
  const today = startOfDay(now.value)
  const messageDay = startOfDay(date)
  const days = Math.round((today.getTime() - messageDay.getTime()) / 86400000)
  if (days === 0) {
    return new Intl.DateTimeFormat('zh-CN', {
      hour: '2-digit',
      minute: '2-digit',
      hour12: false,
    }).format(date)
  }
  if (days === 1) return '昨天'
  return `${date.getMonth() + 1}月${date.getDate()}日`
}

function gradeDisplay(grade: GradeItem) {
  if (grade.examStatus !== 'Normal') return examStatusLabels[grade.examStatus] ?? grade.examStatus
  return grade.totalScore ?? '—'
}

function scoreTone(grade: GradeItem) {
  if (grade.examStatus !== 'Normal' || grade.totalScore == null) return 'muted'
  if (grade.totalScore >= 85) return 'excellent'
  if (grade.totalScore >= 60) return 'passed'
  return 'attention'
}

async function openNotification(notification: NotificationItem) {
  if (notification.linkUrl) {
    await router.push(notification.linkUrl)
    return
  }
  await router.push('/notifications')
}

async function loadOverview() {
  loading.value = true
  failedSections.value = []
  const results = await Promise.allSettled([
    http.get('/timetables/mine'),
    http.get('/notifications', { params: { page: 1, pageSize: 5 } }),
    http.get('/exams/my-schedule'),
    http.get('/grades/student/transcript'),
    http.get<DashboardGreeting>('/dashboard/greeting'),
    http.get<WarningItem[]>('/warnings/my-warnings'),
    http.get('/student/academic-planning'),
  ])

  if (results[0].status === 'fulfilled') {
    timetable.value = results[0].value.data
  } else {
    failedSections.value.push('当日课表')
  }
  if (results[1].status === 'fulfilled') {
    notifications.value = results[1].value.data.items
    unreadCount.value = results[1].value.data.unreadCount
  } else {
    failedSections.value.push('消息中心')
  }
  if (results[2].status === 'fulfilled') {
    exams.value = results[2].value.data
  } else {
    failedSections.value.push('考试安排')
  }
  if (results[3].status === 'fulfilled') {
    grades.value = results[3].value.data.records
  } else {
    failedSections.value.push('考试成绩')
  }
  if (results[4].status === 'fulfilled') {
    dashboardGreeting.value = results[4].value.data
  }
  if (results[5].status === 'fulfilled') {
    warnings.value = results[5].value.data
  } else {
    failedSections.value.push('学业风险雷达')
  }
  if (results[6].status === 'fulfilled') graduation.value = results[6].value.data
  loading.value = false
}

onMounted(loadOverview)
</script>

<template>
  <div v-loading="loading" class="student-overview">
    <section class="student-overview-hero">
      <div class="student-hero-copy">
        <span class="section-kicker">我的学习安排</span>
        <h2>{{ dashboardGreeting?.title ?? `${greeting}，${auth.user?.displayName ?? '同学'}` }}</h2>
        <p>{{ dashboardGreeting?.subtitle ?? todayLabel }}<template v-if="!dashboardGreeting && timetable?.term"> · {{ timetable.term.name }}</template></p>
      </div>
      <div class="today-status">
        <span>今日课程</span>
        <strong>{{ todayCourses.length }}</strong>
        <small>{{ currentWeek ? `第 ${currentWeek} 教学周` : '当前不在教学周内' }}</small>
      </div>
    </section>

    <section v-if="dashboardGreeting?.insights.length" class="student-greeting-insights" :aria-label="dashboardGreeting.label">
      <span>{{ dashboardGreeting.label }}</span>
      <article v-for="insight in dashboardGreeting.insights" :key="insight.label" :class="insight.tone">
        <small>{{ insight.label }}</small>
        <strong>{{ insight.value }}</strong>
        <em>{{ insight.hint }}</em>
      </article>
    </section>
    <p v-if="dashboardGreeting?.narrative" class="student-greeting-narrative">{{ dashboardGreeting.narrative }}</p>

    <section class="student-risk-radar" :class="{ attention: activeWarnings.length }">
      <header>
        <div>
          <span class="panel-kicker">学业提醒</span>
          <h3>学业风险雷达</h3>
        </div>
        <button type="button" @click="router.push('/warnings')">查看全部 <el-icon><ArrowRight /></el-icon></button>
      </header>
      <p>{{ radarSummary }}</p>
      <div v-if="activeWarnings.length" class="risk-list">
        <button v-for="warning in activeWarnings.slice(0, 3)" :key="warning.id" type="button" @click="router.push('/warnings')">
          <span>{{ categoryLabels.Warning }}</span>
          <strong>{{ warning.detail }}</strong>
          <i>去处理 →</i>
        </button>
      </div>
    </section>

    <section v-if="graduation?.baseline" class="graduation-nav">
      <div><span class="panel-kicker">毕业进度</span><h3>毕业导航</h3><p>已完成 {{ graduation.baseline.planCompletedCredits ?? 0 }} 学分，距离培养方案要求还差 {{ graduation.baseline.creditGap ?? 0 }} 学分。</p></div>
      <button type="button" @click="router.push('/academic-planning')">查看毕业航线 <el-icon><ArrowRight /></el-icon></button>
    </section>

    <el-alert
      v-if="failedSections.length"
      type="warning"
      :closable="false"
      show-icon
      :title="`${failedSections.join('、')}暂时无法加载，其余内容不受影响。`"
    />

    <section class="student-overview-grid">
      <article class="overview-panel schedule-panel">
        <header class="panel-heading">
          <div>
            <span class="panel-kicker"><el-icon><Calendar /></el-icon> TODAY</span>
            <h3>当日课表</h3>
          </div>
          <button type="button" @click="router.push('/my-timetable')">
            完整课表 <el-icon><ArrowRight /></el-icon>
          </button>
        </header>

        <div v-if="todayCourses.length" class="today-course-list">
          <button
            v-for="entry in todayCourses"
            :key="entry.id"
            type="button"
            class="today-course"
            @click="router.push('/my-timetable')"
          >
            <time>
              <b>{{ slotTime(entry.startPeriod, 'startsAt') || `第 ${entry.startPeriod} 节` }}</b>
              <span>{{ courseTime(entry) }}</span>
            </time>
            <span class="course-line" aria-hidden="true" />
            <div class="course-copy">
              <span>{{ entry.courseCode }}</span>
              <h4>{{ entry.courseName }}</h4>
              <p>
                <el-icon><Location /></el-icon>
                {{ courseLocation(entry) }}
                <template v-if="entry.teacherNames?.length"> · {{ entry.teacherNames.join('、') }}</template>
              </p>
            </div>
          </button>
        </div>
        <div v-else-if="!failedSections.includes('当日课表')" class="panel-empty schedule-empty">
          <el-icon><Reading /></el-icon>
          <strong>今天没有课程</strong>
          <span>{{ currentWeek ? '可以安排自习或处理待办。' : '当前不在教学周内。' }}</span>
        </div>
      </article>

      <article class="overview-panel message-panel">
        <header class="panel-heading">
          <div>
            <span class="panel-kicker"><el-icon><Bell /></el-icon> INBOX</span>
            <h3>消息中心</h3>
          </div>
          <button type="button" @click="router.push('/notifications')">
            <span v-if="unreadCount" class="unread-count">{{ unreadCount }} 未读</span>
            全部消息 <el-icon><ArrowRight /></el-icon>
          </button>
        </header>

        <div v-if="notifications.length" class="overview-message-list">
          <button
            v-for="notification in notifications"
            :key="notification.id"
            type="button"
            :class="{ unread: !notification.isRead }"
            @click="openNotification(notification)"
          >
            <span class="message-dot" />
            <div>
              <span>{{ categoryLabels[notification.category] ?? '系统消息' }} · {{ notification.senderName }}</span>
              <h4>{{ notification.title }}</h4>
              <p>{{ notification.content }}</p>
            </div>
            <time>{{ messageTime(notification.createdAt) }}</time>
          </button>
        </div>
        <div v-else-if="!failedSections.includes('消息中心')" class="panel-empty">
          <el-icon><Bell /></el-icon>
          <strong>暂无消息</strong>
          <span>新的教务通知会显示在这里。</span>
        </div>
      </article>

      <article class="overview-panel exam-panel">
        <header class="panel-heading">
          <div>
            <span class="panel-kicker"><el-icon><Clock /></el-icon> UPCOMING</span>
            <h3>考试安排</h3>
          </div>
          <button type="button" @click="router.push('/exams')">
            全部考试 <el-icon><ArrowRight /></el-icon>
          </button>
        </header>

        <div v-if="upcomingExams.length" class="upcoming-exams">
          <button
            v-for="exam in upcomingExams"
            :key="exam.id"
            type="button"
            @click="router.push('/exams')"
          >
            <div class="exam-date">
              <b>{{ formatExamDate(exam) }}</b>
              <span>{{ examDistance(exam) }}</span>
            </div>
            <div>
              <span>{{ exam.courseCode }} · {{ formatExamTime(exam) }}</span>
              <h4>{{ exam.courseName }}</h4>
              <p>{{ [exam.buildingName, exam.classroomName].filter(Boolean).join(' · ') || '考场待定' }}</p>
            </div>
          </button>
        </div>
        <div v-else-if="!failedSections.includes('考试安排')" class="panel-empty">
          <el-icon><Calendar /></el-icon>
          <strong>近期没有考试</strong>
          <span>已发布的考试安排会显示在这里。</span>
        </div>
      </article>

      <article class="overview-panel grade-panel">
        <header class="panel-heading">
          <div>
            <span class="panel-kicker"><el-icon><Reading /></el-icon> RESULTS</span>
            <h3>考试成绩</h3>
          </div>
          <button type="button" @click="router.push('/grades')">
            全部成绩 <el-icon><ArrowRight /></el-icon>
          </button>
        </header>

        <div v-if="recentGrades.length" class="recent-grades">
          <button
            v-for="grade in recentGrades"
            :key="grade.id"
            type="button"
            @click="router.push('/grades')"
          >
            <div>
              <span>{{ grade.termName }} · {{ grade.courseCode }}</span>
              <h4>{{ grade.courseName }}</h4>
              <p>绩点 {{ grade.gradePoint ?? '—' }} · {{ examStatusLabels[grade.examStatus] ?? grade.examStatus }}</p>
            </div>
            <strong :class="scoreTone(grade)">{{ gradeDisplay(grade) }}</strong>
          </button>
        </div>
        <div v-else-if="!failedSections.includes('考试成绩')" class="panel-empty">
          <el-icon><Reading /></el-icon>
          <strong>暂无已发布成绩</strong>
          <span>最新发布的考试成绩会显示在这里。</span>
        </div>
      </article>
    </section>
  </div>
</template>

<style scoped>
.student-overview {
  display: grid;
  gap: 18px;
}

.student-overview-hero {
  min-height: 168px;
  padding: 32px 36px;
  display: flex;
  align-items: flex-end;
  gap: 30px;
  color: white;
  overflow: hidden;
  position: relative;
  background:
    linear-gradient(90deg, rgba(255, 255, 255, .045) 1px, transparent 1px),
    linear-gradient(180deg, rgba(255, 255, 255, .045) 1px, transparent 1px),
    linear-gradient(118deg, #1a2d66, #233876 62%, #0d716c 135%);
  background-size: 28px 28px, 28px 28px, auto;
}

.student-overview-hero::after {
  content: "";
  width: 280px;
  height: 280px;
  position: absolute;
  right: -52px;
  top: -118px;
  border: 48px solid rgba(255, 255, 255, .055);
  border-radius: 50%;
}

.student-hero-copy {
  position: relative;
  z-index: 1;
}

.student-overview-hero .section-kicker {
  color: #70d9c9;
}

.student-overview-hero h2 {
  margin: 12px 0 8px;
  font-family: "STZhongsong", "Songti SC", serif;
  font-size: clamp(26px, 3vw, 38px);
  letter-spacing: .04em;
}

.student-overview-hero p {
  margin: 0;
  color: #c9d3ef;
  font-size: 13px;
}

.student-greeting-insights {
  padding: 15px 21px;
  display: grid;
  grid-template-columns: 105px repeat(3, minmax(0, 1fr));
  gap: 12px;
  border: 1px solid #dce6eb;
  background: #f7faf9;
}

.student-greeting-insights > span {
  align-self: center;
  color: var(--teal);
  font: 700 10px/1.5 Consolas, monospace;
  letter-spacing: .11em;
}

.student-greeting-insights article {
  min-width: 0;
  padding-left: 12px;
  display: grid;
  gap: 3px;
  border-left: 2px solid #a9bcc6;
}

.student-greeting-insights article.positive { border-color: var(--teal); }
.student-greeting-insights article.attention { border-color: #c4812a; }
.student-greeting-insights small { color: #667488; font-size: 10px; }
.student-greeting-insights strong { color: var(--ink); font-size: 19px; }
.student-greeting-insights em { overflow: hidden; color: var(--muted); font-size: 10px; font-style: normal; text-overflow: ellipsis; white-space: nowrap; }

.student-greeting-narrative {
  margin: -5px 0 0;
  padding: 0 3px;
  color: #526078;
  font-size: 12px;
  line-height: 1.7;
}

.student-risk-radar {
  padding: 20px 22px;
  border: 1px solid #dce6eb;
  background: #fbfdfd;
}
.graduation-nav{padding:19px 22px;display:flex;justify-content:space-between;align-items:center;gap:18px;border:1px solid #dce6eb;background:#f7faf9}.graduation-nav h3{margin:7px 0;color:var(--ink);font-size:17px}.graduation-nav p{margin:0;color:#59677a;font-size:12px;line-height:1.6}.graduation-nav button{display:inline-flex;align-items:center;gap:5px;border:0;background:transparent;color:var(--indigo);font-size:12px;white-space:nowrap}

.student-risk-radar.attention { border-left: 3px solid #c4812a; background: #fffcf6; }
.student-risk-radar header { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.student-risk-radar h3 { margin: 6px 0 0; color: var(--ink); font-size: 17px; }
.student-risk-radar header button { padding: 4px 0; display: inline-flex; align-items: center; gap: 4px; border: 0; color: #526078; background: transparent; font-size: 12px; white-space: nowrap; }
.student-risk-radar header button:hover { color: var(--indigo); }
.student-risk-radar > p { margin: 13px 0 0; color: #59677a; font-size: 12px; line-height: 1.7; }
.risk-list { margin-top: 13px; display: grid; }
.risk-list button { padding: 12px 0; display: grid; grid-template-columns: 76px minmax(0, 1fr) auto; gap: 10px; text-align: left; border: 0; border-top: 1px solid #eee6d8; background: transparent; }
.risk-list span { align-self: center; color: #a16b1c; font: 700 9px/1.4 Consolas, monospace; letter-spacing: .06em; }
.risk-list strong { color: #38475a; font-size: 12px; font-weight: 600; line-height: 1.55; }
.risk-list i { align-self: center; color: #9a743d; font-size: 11px; font-style: normal; white-space: nowrap; }

.today-status {
  min-width: 180px;
  margin-left: auto;
  padding-left: 26px;
  display: grid;
  grid-template-columns: auto auto;
  align-items: end;
  position: relative;
  z-index: 1;
  border-left: 1px solid rgba(255, 255, 255, .24);
}

.today-status span {
  color: #bdc8e8;
  font-size: 12px;
}

.today-status strong {
  grid-row: 1 / span 2;
  grid-column: 2;
  padding-left: 22px;
  font: 700 52px/.9 Consolas, monospace;
}

.today-status small {
  margin-top: 6px;
  color: #70d9c9;
  font-size: 11px;
}

.student-overview-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, .8fr);
  gap: 18px;
  align-items: stretch;
}

.overview-panel {
  min-height: 320px;
  padding: 25px 27px;
  border: 1px solid var(--line);
  background: white;
}

.panel-heading {
  min-height: 48px;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 18px;
  border-bottom: 1px solid #e8ebf0;
}

.panel-heading h3 {
  margin: 5px 0 16px;
  font-size: 18px;
}

.panel-kicker {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--teal);
  font: 700 9px/1 Consolas, monospace;
  letter-spacing: .14em;
}

.panel-heading button {
  min-height: 30px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border: 0;
  color: #526078;
  background: transparent;
  font-size: 12px;
}

.panel-heading button:hover {
  color: var(--indigo);
}

.unread-count {
  padding: 3px 6px;
  color: #116f68;
  background: #e4f4f1;
  font-size: 10px;
}

.today-course-list,
.overview-message-list,
.upcoming-exams,
.recent-grades {
  display: grid;
}

.today-course {
  width: 100%;
  min-height: 92px;
  padding: 16px 0;
  display: grid;
  grid-template-columns: 82px 12px minmax(0, 1fr);
  gap: 11px;
  text-align: left;
  border: 0;
  border-bottom: 1px solid #edf0f3;
  background: transparent;
}

.today-course:last-child {
  border-bottom: 0;
}

.today-course:hover .course-copy h4 {
  color: var(--indigo);
}

.today-course time {
  display: grid;
  align-content: center;
}

.today-course time b {
  font: 700 17px/1.2 Consolas, monospace;
}

.today-course time span {
  margin-top: 6px;
  color: var(--muted);
  font-size: 10px;
  white-space: nowrap;
}

.course-line {
  width: 2px;
  min-height: 52px;
  justify-self: center;
  background: var(--teal);
  box-shadow: 0 0 0 4px #e9f5f3;
}

.course-copy {
  min-width: 0;
  align-self: center;
}

.course-copy > span,
.overview-message-list button div > span,
.upcoming-exams button > div:last-child > span,
.recent-grades button div > span {
  color: #788295;
  font-size: 10px;
}

.course-copy h4,
.overview-message-list h4,
.upcoming-exams h4,
.recent-grades h4 {
  margin: 5px 0;
  color: var(--ink);
  font-size: 14px;
  font-weight: 650;
}

.course-copy p,
.overview-message-list p,
.upcoming-exams p,
.recent-grades p {
  margin: 0;
  color: var(--muted);
  font-size: 11px;
}

.course-copy p {
  display: flex;
  align-items: center;
  gap: 4px;
}

.overview-message-list button {
  width: 100%;
  min-height: 64px;
  padding: 12px 0;
  display: grid;
  grid-template-columns: 8px minmax(0, 1fr) auto;
  gap: 10px;
  text-align: left;
  border: 0;
  border-bottom: 1px solid #edf0f3;
  background: transparent;
}

.overview-message-list button:last-child {
  border-bottom: 0;
}

.overview-message-list button:hover h4 {
  color: var(--indigo);
}

.message-dot {
  width: 6px;
  height: 6px;
  margin-top: 21px;
  border: 1px solid #aeb5c1;
  border-radius: 50%;
}

.overview-message-list button.unread .message-dot {
  border-color: var(--teal);
  background: var(--teal);
  box-shadow: 0 0 0 3px #e5f3f1;
}

.overview-message-list button.unread h4 {
  font-weight: 750;
}

.overview-message-list p {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.overview-message-list time {
  padding-top: 17px;
  color: #9098a6;
  font-size: 10px;
}

.upcoming-exams button {
  width: 100%;
  min-height: 78px;
  padding: 13px 0;
  display: grid;
  grid-template-columns: 112px minmax(0, 1fr);
  gap: 17px;
  text-align: left;
  border: 0;
  border-bottom: 1px solid #edf0f3;
  background: transparent;
}

.upcoming-exams button:last-child,
.recent-grades button:last-child {
  border-bottom: 0;
}

.upcoming-exams button:hover h4,
.recent-grades button:hover h4 {
  color: var(--indigo);
}

.exam-date {
  padding: 5px 13px;
  display: grid;
  align-content: center;
  border-left: 3px solid var(--amber);
  background: #fbf8f0;
}

.exam-date b {
  font-size: 12px;
}

.exam-date span {
  margin-top: 4px;
  color: #a56c16;
  font-size: 10px;
}

.recent-grades button {
  width: 100%;
  min-height: 67px;
  padding: 11px 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 18px;
  text-align: left;
  border: 0;
  border-bottom: 1px solid #edf0f3;
  background: transparent;
}

.recent-grades strong {
  min-width: 52px;
  text-align: right;
  color: var(--ink);
  font: 700 27px/1 Consolas, monospace;
}

.recent-grades strong.excellent {
  color: var(--teal);
}

.recent-grades strong.attention {
  color: #b64e45;
}

.recent-grades strong.muted {
  color: var(--muted);
  font: 600 13px/1 sans-serif;
}

.panel-empty {
  min-height: 220px;
  display: grid;
  place-content: center;
  justify-items: center;
  color: #9ba4b3;
  text-align: center;
}

.panel-empty .el-icon {
  margin-bottom: 13px;
  font-size: 28px;
  color: #b3bac5;
}

.panel-empty strong {
  color: #596377;
  font-size: 14px;
}

.panel-empty span {
  margin-top: 6px;
  font-size: 11px;
}

@media (max-width: 980px) {
  .student-greeting-insights { grid-template-columns: 1fr repeat(3, minmax(0, 1fr)); }
  .student-overview-grid {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 600px) {
  .student-greeting-insights { padding: 15px 18px; grid-template-columns: 1fr; }
  .student-risk-radar { padding: 18px; }
  .graduation-nav{padding:18px;align-items:flex-start;flex-direction:column}
  .risk-list button { grid-template-columns: 1fr auto; }
  .risk-list span { grid-column: 1 / -1; }
  .student-overview {
    gap: 12px;
  }

  .student-overview-hero {
    min-height: 190px;
    padding: 24px 21px;
    align-items: flex-end;
  }

  .student-overview-hero h2 {
    font-size: 25px;
  }

  .today-status {
    min-width: 0;
    padding-left: 16px;
  }

  .today-status span,
  .today-status small {
    display: none;
  }

  .today-status strong {
    padding-left: 0;
    font-size: 42px;
  }

  .overview-panel {
    min-height: 0;
    padding: 21px 18px;
  }

  .panel-heading {
    gap: 10px;
  }

  .panel-heading button {
    white-space: nowrap;
  }

  .unread-count {
    display: none;
  }

  .today-course {
    grid-template-columns: 68px 8px minmax(0, 1fr);
    gap: 8px;
  }

  .today-course time b {
    font-size: 14px;
  }

  .upcoming-exams button {
    grid-template-columns: 95px minmax(0, 1fr);
    gap: 11px;
  }

  .overview-message-list time {
    display: none;
  }

  .overview-message-list button {
    grid-template-columns: 8px minmax(0, 1fr);
  }
}
</style>
