<template>
  <div class="app-page period-workbench">
    <div class="app-page-header page-hero">
      <div class="hero-info">
        <h2 class="app-page-title">考核周期</h2>
        <p class="app-page-subtitle">管理考核节奏，推动 OKR 对齐</p>
      </div>
      <el-button v-if="auth.isAdmin" type="primary" size="large" @click="showCreate = true" round>
        创建新周期
      </el-button>
    </div>

    <div class="period-metrics metric-row" v-if="periods.length">
      <div class="metric-card">
        <span class="metric-card__label">进行中</span>
        <strong>{{ active.length }}</strong>
      </div>
      <div class="metric-card">
        <span class="metric-card__label">OKR 对齐</span>
        <strong>{{ okrAlign.length }}</strong>
      </div>
      <div class="metric-card">
        <span class="metric-card__label">考评中</span>
        <strong>{{ assessing.length }}</strong>
      </div>
      <div class="metric-card">
        <span class="metric-card__label">已归档</span>
        <strong>{{ archived.length }}</strong>
      </div>
    </div>

    <div v-if="periods.length === 0" class="empty-state">
      <el-empty description="暂无考核周期">
        <template #extra>
          <span class="empty-hint">管理员创建周期后，团队即可开始 OKR 对齐</span>
        </template>
      </el-empty>
    </div>

    <template v-else>
      <!-- Tabs (admin only) -->
      <div class="tab-bar" v-if="auth.isAdmin">
        <div v-for="t in tabs" :key="t.key" class="tab-item" :class="{ active: filterTab === t.key }" @click="filterTab = t.key">
          <span class="tab-label">{{ t.label }}</span>
          <span class="tab-count">{{ t.count }}</span>
        </div>
      </div>

      <!-- Filtered: 进行中 -->
      <div class="category" v-if="showActive">
        <div class="cat-head">
          <span class="cat-badge active">进行中</span>
          <span class="cat-count">{{ active.length }} 个周期</span>
        </div>

        <!-- Sub-group: 执行中 + 考评 -->
        <div class="sub-group" v-if="executing.length">
          <div class="sub-label">执行 & 考评</div>
          <div v-for="p in executing" :key="p.id" class="period-card" :class="'status-'+p.status">
            <div class="card-body">
              <div class="card-main">
                <div class="card-info">
                  <span class="card-name">{{ p.name }}</span>
                  <el-tag size="small" :type="statusTagType(p.status)" effect="dark" round>{{ statusLabel(p.status) }}</el-tag>
                  <el-tag size="small" effect="plain" type="info" round>{{ typeLabel(p.periodType) }}</el-tag>
                </div>
                <div class="card-date">{{ p.startDate }} ~ {{ p.endDate }}</div>
              </div>
              <div class="card-actions">
                <template v-if="auth.isAdmin">
                  <el-button v-if="p.status === 'EXECUTING'" type="warning" size="small" plain :loading="transitioning === p.id" @click="transition(p.id, 'OKR_ALIGN')" round>回退</el-button>
                  <el-button size="small" plain @click="openParticipants(p)" round>参与人</el-button>
                  <el-button v-if="p.status === 'EXECUTING'" type="primary" size="small" :loading="transitioning === p.id" @click="transition(p.id, 'ASSESSING')" round>进入考评</el-button>
                  <el-button v-if="p.status === 'EXECUTING'" type="danger" size="small" plain :loading="transitioning === p.id" @click="transition(p.id, 'ARCHIVED')" round>直接归档</el-button>
                  <el-button v-if="p.status === 'ASSESSING'" type="warning" :loading="transitioning === p.id" @click="transition(p.id, 'ARCHIVED')" round>结束并归档</el-button>
                </template>
                <el-button v-if="p.status === 'ASSESSING'" type="warning" size="small" @click="$router.push({ path: '/scores/my', query: { periodId: p.id } })" round>去自评</el-button>
              </div>
            </div>
            <div class="card-guide">{{ statusGuide(p) }}</div>
          </div>
        </div>

        <!-- Sub-group: 已发布(OKR对齐中) -->
        <div class="sub-group" v-if="okrAlign.length">
          <div class="sub-label">OKR 对齐中</div>
          <div v-for="p in okrAlign" :key="p.id" class="period-card status-OKR_ALIGN">
            <div class="card-body">
              <div class="card-main">
                <div class="card-info">
                  <span class="card-name">{{ p.name }}</span>
                  <el-tag type="warning" size="small" effect="dark" round>OKR 对齐中</el-tag>
                  <el-tag size="small" effect="plain" type="info" round>{{ typeLabel(p.periodType) }}</el-tag>
                </div>
                <div class="card-date">{{ p.startDate }} ~ {{ p.endDate }}</div>
              </div>
              <div class="card-actions">
                <template v-if="auth.isAdmin">
                  <el-button type="warning" size="small" :loading="transitioning === p.id" @click="transition(p.id, 'EXECUTING')" round>锁定并开始执行</el-button>
                  <el-button size="small" plain @click="openParticipants(p)" round>参与人</el-button>
                </template>
                <el-button type="primary" size="small" @click="$router.push({ path: '/okr/my', query: { periodId: p.id } })" round>去填写 OKR</el-button>
              </div>
            </div>
            <div class="card-guide">{{ statusGuide(p) }}</div>
          </div>
        </div>

        <!-- Sub-group: 未发布 -->
        <div class="sub-group" v-if="drafts.length">
          <div class="sub-label">未发布</div>
          <div v-for="p in drafts" :key="p.id" class="period-card status-DRAFT">
            <div class="card-body">
              <div class="card-main">
                <div class="card-info">
                  <span class="card-name">{{ p.name }}</span>
                  <el-tag type="info" size="small" effect="dark" round>待发布</el-tag>
                  <el-tag size="small" effect="plain" type="info" round>{{ typeLabel(p.periodType) }}</el-tag>
                </div>
                <div class="card-date">{{ p.startDate }} ~ {{ p.endDate }}</div>
              </div>
              <div class="card-actions" v-if="auth.isAdmin">
                <el-button type="primary" size="small" :loading="transitioning === p.id" @click="transition(p.id, 'OKR_ALIGN')" round>发布 OKR 对齐</el-button>
                <el-button size="small" plain @click="openParticipants(p)" round>参与人</el-button>
                <el-button type="danger" size="small" plain @click="handleDelete(p.id)" round>删除</el-button>
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- Category: 未开始 -->
      <div class="category" v-if="showUpcoming && upcoming.length">
        <div class="cat-head">
          <span class="cat-badge upcoming">未开始</span>
          <span class="cat-count">{{ upcoming.length }} 个周期</span>
        </div>
        <div v-for="p in upcoming" :key="p.id" class="period-card status-DRAFT">
          <div class="card-body">
            <div class="card-main">
              <div class="card-info">
                <span class="card-name">{{ p.name }}</span>
                <el-tag type="info" size="small" effect="dark" round>未开始</el-tag>
                <el-tag size="small" effect="plain" type="info" round>{{ typeLabel(p.periodType) }}</el-tag>
              </div>
              <div class="card-date">{{ p.startDate }} ~ {{ p.endDate }}</div>
            </div>
            <div class="card-actions" v-if="auth.isAdmin">
              <el-button type="danger" size="small" plain @click="handleDelete(p.id)" round>删除</el-button>
            </div>
          </div>
        </div>
      </div>

      <!-- Category: 已结束 -->
      <div class="category" v-if="showArchived && archived.length">
        <div class="cat-head">
          <span class="cat-badge archived">已结束</span>
          <span class="cat-count">{{ archived.length }} 个周期</span>
        </div>
        <div v-for="p in archived" :key="p.id" class="period-card status-ARCHIVED">
          <div class="card-body">
            <div class="card-main">
              <div class="card-info">
                <span class="card-name">{{ p.name }}</span>
                <el-tag type="info" size="small" effect="plain" round>已归档</el-tag>
                <el-tag size="small" effect="plain" type="info" round>{{ typeLabel(p.periodType) }}</el-tag>
              </div>
              <div class="card-date">{{ p.startDate }} ~ {{ p.endDate }}</div>
            </div>
          </div>
        </div>
      </div>
    </template>

    <!-- Create Dialog -->
    <el-dialog v-model="showCreate" title="创建考核周期" width="520px">
      <el-form :model="newPeriod" label-width="80px">
        <el-form-item label="名称前缀">
          <el-input v-model="newPeriod.name" placeholder="如：2026、" />
        </el-form-item>
        <el-form-item label="周期类型">
          <el-select v-model="newPeriod.periodType" placeholder="选择周期类型" @change="onTypeChange" style="width:100%">
            <el-option label="单周（7天）" value="WEEKLY" /><el-option label="双周（14天）" value="BI_WEEKLY" />
            <el-option label="单月（自然月）" value="MONTHLY" /><el-option label="双月（2个自然月）" value="BI_MONTHLY" />
            <el-option label="季度（3个自然月）" value="QUARTERLY" />
          </el-select>
        </el-form-item>
        <el-form-item label="开始日期">
          <el-date-picker v-model="newPeriod.startDate" type="date" placeholder="选择日期" @change="onTypeChange" style="width:100%" />
        </el-form-item>
        <el-form-item label="结束日期">
          <el-date-picker v-model="newPeriod.endDate" type="date" placeholder="自动计算或手动选择" style="width:100%" />
        </el-form-item>
        <el-divider />
        <el-form-item label="批量创建">
          <el-checkbox v-model="batchMode" @change="onTypeChange">
            批量创建 6 个连续周期
          </el-checkbox>
        </el-form-item>
        <div v-if="batchMode && previewPeriods.length" class="batch-preview">
          <div class="bp-title">预览（将创建以下 6 个周期）</div>
          <div v-for="(p, i) in previewPeriods" :key="i" class="bp-item">
            <span class="bp-num">{{ i + 1 }}</span>
            <span class="bp-name">{{ newPeriod.name || '周期' }}{{ i + 1 }}</span>
            <span class="bp-date">{{ p.startDate }} ~ {{ p.endDate }}</span>
          </div>
        </div>
      </el-form>
      <template #footer>
        <el-button @click="showCreate = false">取消</el-button>
        <el-button type="primary" @click="handleCreate">
          {{ batchMode ? '批量创建 6 个周期' : '创建' }}
        </el-button>
      </template>
    </el-dialog>

    <el-dialog v-model="showParticipants" :title="'周期参与人 · ' + selectedPeriod?.name" width="760px">
      <div class="participant-summary">
        <div>
          <div class="participant-summary__title">参与人范围</div>
          <div class="participant-summary__hint">
            {{ isDraftPeriod ? '发布 OKR 对齐时将默认纳入全部在职成员。' : '取消员工勾选会记录为本周期豁免，重新勾选会恢复参与。' }}
          </div>
        </div>
        <div class="participant-stat">
          <strong>{{ participantSelectedCount }}</strong>
          <span>/ {{ participantTotal }} 人</span>
        </div>
      </div>
      <el-alert
        v-if="isDraftPeriod"
        type="info"
        :closable="false"
        show-icon
        class="participant-alert"
        title="草稿期仅展示默认全员名单，发布后可对个别成员做豁免。"
      />
      <el-tree
        ref="participantTreeRef"
        v-loading="participantActionLoading"
        class="participant-tree"
        :data="participantTree"
        :props="participantTreeProps"
        node-key="id"
        show-checkbox
        default-expand-all
        :default-checked-keys="participantCheckKeys"
        empty-text="暂无可参与成员，请先在组织管理中维护部门和员工"
        @check-change="handleParticipantCheckChange"
      >
        <template #default="{ data }">
          <div class="participant-node" :class="{ 'is-user': data.type === 'user' }">
            <span class="participant-node__label">{{ data.label }}</span>
            <template v-if="data.type === 'user'">
              <el-tag v-if="data.participantStatus === 'EXEMPT'" size="small" type="warning" effect="plain" round>豁免</el-tag>
              <el-tag v-else-if="data.participantStatus === 'MISSING'" size="small" type="info" effect="plain" round>未加入</el-tag>
              <el-tag v-else size="small" type="success" effect="plain" round>参与</el-tag>
              <span v-if="data.meta" class="participant-node__meta">{{ data.meta }}</span>
            </template>
          </div>
        </template>
      </el-tree>
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, computed, nextTick, onMounted } from 'vue'
import { periodApi, orgApi } from '../../api'
import { useAuthStore } from '../../stores/auth'
import { ElMessage, ElMessageBox } from 'element-plus'

const auth = useAuthStore()
const periods = ref([])
const showCreate = ref(false)
const batchMode = ref(false)
const transitioning = ref(null)
const showParticipants = ref(false)
const selectedPeriod = ref(null)
const participants = ref([])
const allUsers = ref([])
const departmentTree = ref([])
const participantTreeRef = ref(null)
const participantActionLoading = ref(false)
const syncingParticipantTree = ref(false)
const participantCheckKeys = ref([])
const newPeriod = ref({ name: '', periodType: 'MONTHLY', startDate: '', endDate: '' })
const typeDays = { WEEKLY: 6, BI_WEEKLY: 13 }
const typeMonths = { MONTHLY: 1, BI_MONTHLY: 2, QUARTERLY: 3 }
const participantTreeProps = {
  children: 'children',
  label: 'label',
  disabled: data => data.disabled
}

function calcEndDate(startDate, periodType) {
  // Parse as UTC midnight (consistent with toISOString formatting used elsewhere)
  const sd = new Date(typeof startDate === 'string' ? startDate + 'T00:00:00Z' : startDate)
  if (typeMonths[periodType]) {
    const months = typeMonths[periodType]
    const y = sd.getUTCFullYear()
    const m = sd.getUTCMonth()
    const d = sd.getUTCDate()
    // Clamp day to last day of target month (mimics Java plusMonths)
    const targetMonth = m + months
    const lastDay = new Date(Date.UTC(y, targetMonth + 1, 0)).getUTCDate()
    const clampedDay = Math.min(d, lastDay)
    // Target = start + months with clamped day, then subtract 1 day
    const end = new Date(Date.UTC(y, targetMonth, clampedDay) - 86400000)
    return end
  }
  const days = typeDays[periodType] || 6
  return new Date(sd.getTime() + days * 86400000)
}

const today = new Date().toISOString().split('T')[0]

// For non-admin, only show active periods and no tabs
const defaultTab = computed(() => auth.isAdmin ? 'active' : 'active')

const filterTab = ref(defaultTab.value)

const tabs = computed(() => [
  { key: 'all', label: '全部', count: periods.value.length },
  { key: 'active', label: '进行中', count: active.value.length },
  { key: 'upcoming', label: '未开始', count: upcoming.value.length },
  { key: 'archived', label: '已结束', count: archived.value.length }
])

const showActive = computed(() => filterTab.value === 'all' || filterTab.value === 'active')
const showUpcoming = computed(() => filterTab.value === 'all' || filterTab.value === 'upcoming')
const showArchived = computed(() => filterTab.value === 'all' || filterTab.value === 'archived')

// Categorize by time window
const archived = computed(() => periods.value.filter(p => p.status === 'ARCHIVED'))
const active = computed(() => periods.value.filter(p => p.status !== 'ARCHIVED' && p.startDate <= today))
const upcoming = computed(() => periods.value.filter(p => p.status !== 'ARCHIVED' && p.startDate > today))

// Sub-groups within 进行中
const executing = computed(() => active.value.filter(p => p.status === 'EXECUTING' || p.status === 'ASSESSING'))
const assessing = computed(() => active.value.filter(p => p.status === 'ASSESSING'))
const okrAlign = computed(() => active.value.filter(p => p.status === 'OKR_ALIGN'))
const drafts = computed(() => active.value.filter(p => p.status === 'DRAFT'))

const previewPeriods = computed(() => {
  if (!batchMode.value) return []
  const start = newPeriod.value.startDate
  if (!start) return []
  const sd = new Date(typeof start === 'string' ? start : start)
  const result = []
  let cursor = new Date(sd)
  for (let i = 0; i < 6; i++) {
    const end = calcEndDate(cursor, newPeriod.value.periodType)
    result.push({ startDate: cursor.toISOString().split('T')[0], endDate: end.toISOString().split('T')[0] })
    cursor = new Date(end); cursor.setDate(cursor.getDate() + 1)
  }
  return result
})

function onTypeChange() {
  const start = newPeriod.value.startDate
  if (start && !batchMode.value) {
    const end = calcEndDate(start, newPeriod.value.periodType)
    newPeriod.value.endDate = end.toISOString().split('T')[0]
  }
}

function fmtDate(d) { return d ? (typeof d === 'string' ? d : d.toISOString().split('T')[0]) : '' }

onMounted(() => load())
async function load() { periods.value = await periodApi.list() }

const activeParticipants = computed(() => participants.value.filter(p => p.status === 'ACTIVE'))
const isDraftPeriod = computed(() => selectedPeriod.value?.status === 'DRAFT')
const participantByUserId = computed(() => {
  const map = new Map()
  participants.value.forEach(p => map.set(p.userId, p))
  return map
})
const eligibleUsers = computed(() => allUsers.value.filter(isEligibleParticipantUser))
const participantTotal = computed(() => eligibleUsers.value.length)
const participantSelectedCount = computed(() => isDraftPeriod.value ? participantTotal.value : activeParticipants.value.length)
const activeParticipantKeys = computed(() => {
  if (isDraftPeriod.value) return eligibleUsers.value.map(u => userNodeId(u.id))
  return activeParticipants.value.map(p => userNodeId(p.userId))
})
const participantTree = computed(() => {
  const usersByDept = new Map()
  allUsers.value
    .filter(u => isEligibleParticipantUser(u) || participantByUserId.value.has(u.id))
    .forEach(u => {
      if (!u.departmentId) return
      if (!usersByDept.has(u.departmentId)) usersByDept.set(u.departmentId, [])
      usersByDept.get(u.departmentId).push(u)
    })

  return departmentTree.value
    .map(dept => buildParticipantDeptNode(dept, usersByDept))
    .filter(Boolean)
})

async function openParticipants(period) {
  selectedPeriod.value = period
  showParticipants.value = true
  participantActionLoading.value = true
  try {
    const [participantData, userData, treeData] = await Promise.all([
      periodApi.getParticipants(period.id),
      orgApi.getUsers(),
      orgApi.getTree()
    ])
    participants.value = participantData
    allUsers.value = userData
    departmentTree.value = treeData
    await syncParticipantTree()
  } finally {
    participantActionLoading.value = false
  }
}

async function refreshParticipants() {
  if (!selectedPeriod.value) return
  participants.value = await periodApi.getParticipants(selectedPeriod.value.id)
  await syncParticipantTree()
}

function isEligibleParticipantUser(user) {
  return user.departmentId && user.status !== 'DISABLED' && user.status !== 'LEFT'
}

function userNodeId(userId) {
  return `user-${userId}`
}

function buildParticipantDeptNode(dept, usersByDept) {
  const childDeptNodes = (dept.children || [])
    .map(child => buildParticipantDeptNode(child, usersByDept))
    .filter(Boolean)
  const userNodes = (usersByDept.get(dept.id) || [])
    .slice()
    .sort((a, b) => (a.realName || a.username || '').localeCompare(b.realName || b.username || '', 'zh-Hans-CN'))
    .map(user => {
      const participant = participantByUserId.value.get(user.id)
      const status = isDraftPeriod.value ? 'ACTIVE' : (participant?.status || 'MISSING')
      return {
        id: userNodeId(user.id),
        label: user.realName || user.username || `用户 ${user.id}`,
        type: 'user',
        userId: user.id,
        participantStatus: status,
        disabled: isDraftPeriod.value || participantActionLoading.value,
        meta: user.position || user.username || ''
      }
    })
  const children = [...childDeptNodes, ...userNodes]
  if (!children.length) return null
  return {
    id: `dept-${dept.id}`,
    label: dept.name,
    type: 'dept',
    disabled: true,
    children
  }
}

async function syncParticipantTree() {
  participantCheckKeys.value = activeParticipantKeys.value
  syncingParticipantTree.value = true
  await nextTick()
  participantTreeRef.value?.setCheckedKeys(participantCheckKeys.value)
  await nextTick()
  syncingParticipantTree.value = false
}

async function handleParticipantCheckChange(node, checked) {
  if (syncingParticipantTree.value || participantActionLoading.value || node.type !== 'user') return
  if (isDraftPeriod.value) {
    ElMessage.info('草稿期默认全员纳入，发布后可豁免个别成员')
    await syncParticipantTree()
    return
  }

  participantActionLoading.value = true
  try {
    const existing = participantByUserId.value.get(node.userId)
    if (checked) {
      if (!existing) {
        await periodApi.addParticipant(selectedPeriod.value.id, node.userId)
      } else if (existing.status !== 'ACTIVE') {
        await periodApi.activateParticipant(selectedPeriod.value.id, node.userId)
      }
      ElMessage.success('已恢复参与')
    } else {
      const { value } = await ElMessageBox.prompt('请输入豁免原因', '豁免参与人', {
        inputType: 'textarea',
        inputPlaceholder: '如：本周期休假/新入职不参与考核',
        confirmButtonText: '确认豁免',
        cancelButtonText: '取消',
        inputValidator: val => !!String(val || '').trim() || '请填写豁免原因'
      })
      await periodApi.exemptParticipant(selectedPeriod.value.id, node.userId, value)
      ElMessage.success('已豁免')
    }
    await refreshParticipants()
  } catch {
    await syncParticipantTree()
  } finally {
    participantActionLoading.value = false
  }
}

async function handleCreate() {
  if (batchMode.value) {
    await periodApi.batchCreate({
      namePrefix: (newPeriod.value.name || '周期') + '',
      periodType: newPeriod.value.periodType,
      startDate: fmtDate(newPeriod.value.startDate)
    })
    ElMessage.success('已批量创建 6 个连续周期')
  } else {
    const d = newPeriod.value
    await periodApi.create({ name: d.name, periodType: d.periodType, startDate: fmtDate(d.startDate), endDate: fmtDate(d.endDate) })
    ElMessage.success('周期已创建')
  }
  showCreate.value = false; batchMode.value = false
  newPeriod.value = { name: '', periodType: 'MONTHLY', startDate: '', endDate: '' }
  load()
}

async function transition(id, status) {
  if (transitioning.value) return
  transitioning.value = id
  try {
    const readiness = await periodApi.checkReadiness(id, status)
    if (!readiness.ready) {
      await ElMessageBox.alert(
        readiness.blockers.map(item => `· ${item}`).join('<br>'),
        '暂不能切换周期状态',
        { dangerouslyUseHTMLString: true, confirmButtonText: '知道了', type: 'warning' }
      )
      return
    }
    await periodApi.transition(id, status)
    ElMessage.success('周期状态已更新')
    load()
  } catch {
    // error handled by interceptor
  } finally {
    transitioning.value = null
  }
}

async function handleDelete(id) {
  await ElMessageBox.confirm('确定删除该周期吗？', '提示', { type: 'warning' })
  await periodApi.delete(id)
  ElMessage.success('已删除')
  load()
}

function typeLabel(t) { return { WEEKLY: '单周', BI_WEEKLY: '双周', MONTHLY: '单月', BI_MONTHLY: '双月', QUARTERLY: '季度' }[t] || t }
function statusLabel(s) { return { DRAFT: '待发布', OKR_ALIGN: 'OKR 对齐中', EXECUTING: '执行中', ASSESSING: '考评中', ARCHIVED: '已归档' }[s] || s }
function statusTagType(s) { return { DRAFT: 'info', OKR_ALIGN: '', EXECUTING: 'success', ASSESSING: 'warning', ARCHIVED: 'info' }[s] || 'info' }
function statusGuide(p) {
  return {
    DRAFT: '管理员发布后团队成员即可提交 OKR。',
    OKR_ALIGN: '部门负责人发布部门 OKR &rarr; 员工提交个人 OKR 并对齐 &rarr; 上级审核。',
    EXECUTING: 'OKR 已锁定，员工每周更新 KR 进度。',
    ASSESSING: '员工自评 &rarr; 上级打分 &rarr; 面谈沟通 &rarr; 归档。',
    ARCHIVED: '已归档，数据不可修改。'
  }[p.status] || ''
}
</script>

<style scoped>
.period-workbench { max-width: 960px; }
.page-hero { margin-bottom: var(--spacing-lg); }
.period-metrics { margin-bottom: var(--spacing-lg); }
.empty-state { min-height: 300px; }
.empty-hint { font-size: var(--font-size-sm); color: var(--color-text-muted); }

/* Tabs */
.tab-bar { display: flex; gap: var(--spacing-xs); margin-bottom: var(--spacing-xl); background: var(--color-surface-subtle); border-radius: var(--radius-md); padding: var(--spacing-xs); }
.tab-item { padding: var(--spacing-sm) var(--spacing-lg); border-radius: var(--radius-sm); cursor: pointer; display: flex; align-items: center; gap: var(--spacing-sm); font-size: var(--font-size-base); color: var(--color-text-muted); transition: all var(--transition-fast); }
.tab-item:hover { color: var(--color-text); background: var(--color-surface-muted); }
.tab-item.active { background: var(--color-surface); color: var(--color-brand); font-weight: 600; box-shadow: var(--shadow-panel); }
.tab-count { font-size: var(--font-size-xs); background: var(--color-surface-muted); border-radius: var(--radius-xs); padding: 1px var(--spacing-sm); color: var(--color-text-muted); }
.tab-item.active .tab-count { background: var(--color-brand); color: var(--color-white); }

/* Category */
.category { margin-bottom: var(--spacing-2xl); }
.cat-head { display: flex; align-items: center; gap: var(--spacing-sm); margin-bottom: var(--spacing-md); }
.cat-badge { padding: 5px var(--spacing-lg); border-radius: var(--radius-sm); font-size: var(--font-size-base); font-weight: 700; color: var(--color-white); }
.cat-badge.active { background: var(--color-brand); }
.cat-badge.upcoming { background: var(--color-info); }
.cat-badge.archived { background: var(--color-text-muted); }
.cat-count { font-size: var(--font-size-sm); color: var(--color-text-muted); }

/* Sub-group */
.sub-group { margin-bottom: var(--spacing-md); }
.sub-label { font-size: var(--font-size-sm); font-weight: 600; color: var(--color-text-secondary); margin-bottom: var(--spacing-sm); padding-left: var(--spacing-xs); }

/* Card */
.period-card {
  background: var(--color-surface); border-radius: var(--radius-md); padding: 0; margin-bottom: var(--spacing-sm);
  box-shadow: var(--shadow-panel); overflow: hidden; border: 1px solid var(--color-border-soft);
  border-left: 4px solid var(--color-border);
}
.period-card.status-EXECUTING { border-left-color: var(--color-accent); }
.period-card.status-ASSESSING { border-left-color: var(--color-warning); }
.period-card.status-OKR_ALIGN { border-left-color: var(--color-info); }
.period-card.status-DRAFT { border-left-color: var(--color-border); }
.period-card.status-ARCHIVED { border-left-color: var(--color-text-muted); opacity: 0.75; }

.card-body { padding: var(--spacing-md) var(--spacing-lg); display: flex; justify-content: space-between; align-items: center; gap: var(--spacing-lg); }
.card-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--spacing-xs); }
.card-info { display: flex; align-items: center; gap: var(--spacing-sm); flex-wrap: wrap; }
.card-name { font-size: var(--font-size-lg); font-weight: 700; color: var(--color-text); }
.card-date { font-size: var(--font-size-xs); color: var(--color-text-muted); }
.card-actions { display: flex; gap: var(--spacing-xs); flex-shrink: 0; align-items: center; }
.card-actions .el-button { white-space: nowrap; }
.card-guide { font-size: var(--font-size-xs); color: var(--color-text-secondary); padding: var(--spacing-sm) var(--spacing-lg) 10px; background: var(--color-surface-subtle); border-top: 1px solid var(--color-border-soft); }

/* Dialog */
.batch-preview { background: var(--color-surface-subtle); border-radius: var(--radius-sm); padding: var(--spacing-sm) var(--spacing-md); margin-top: var(--spacing-sm); border: 1px solid var(--color-border-soft); }
.bp-title { font-size: var(--font-size-xs); font-weight: 600; color: var(--color-brand); margin-bottom: var(--spacing-sm); }
.bp-item { display: flex; align-items: center; gap: var(--spacing-sm); padding: var(--spacing-xs) 0; font-size: var(--font-size-xs); }
.bp-num { width: var(--font-size-xl); height: var(--font-size-xl); line-height: var(--font-size-xl); text-align: center; border-radius: 50%; background: var(--color-accent); color: var(--color-white); font-size: var(--font-size-xs); font-weight: 700; }
.bp-name { color: var(--color-text); font-weight: 500; min-width: 80px; }
.bp-date { color: var(--color-text-muted); }
.participant-summary {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--spacing-md);
  padding: var(--spacing-md) var(--spacing-lg);
  margin-bottom: var(--spacing-sm);
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius-md);
  background: var(--color-surface-tint-brand);
}
.participant-summary__title { font-size: var(--font-size-lg); font-weight: 700; color: var(--color-text); margin-bottom: var(--spacing-xs); }
.participant-summary__hint { font-size: var(--font-size-sm); color: var(--color-text-secondary); line-height: 1.6; }
.participant-stat { min-width: 88px; text-align: right; color: var(--color-text-secondary); }
.participant-stat strong { font-size: var(--font-size-2xl); color: var(--color-brand); line-height: 1; }
.participant-stat span { font-size: var(--font-size-sm); }
.participant-alert { margin-bottom: var(--spacing-sm); }
.participant-tree {
  max-height: 460px;
  overflow: auto;
  padding: var(--spacing-sm) var(--spacing-xs);
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius-md);
  background: var(--color-surface);
}
.participant-node {
  width: 100%;
  display: flex;
  align-items: center;
  gap: var(--spacing-sm);
  min-width: 0;
}
.participant-node.is-user { color: var(--color-text); }
.participant-node__label { font-size: var(--font-size-sm); font-weight: 600; }
.participant-node__meta {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--font-size-xs);
  color: var(--color-text-muted);
}
</style>
