<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import {
  getAllMembers,
  searchMembers,
  getMemberDetail,
  updateMember,
  getActiveCards,
  getMemberCardRecords,
  purchaseCard,
  refundCard,
  verifyAdminPassword,
  type Member,
  type MemberDetail,
  type UpdateMemberDto,
  type MemberCardType,
  type MemberCardRecord,
} from '@/api/member'
import AppLayout from '@/components/AppLayout.vue'
import { useToast } from '@/composables/useToast'
import { useAuthStore } from '@/stores/auth'

const toast = useToast()
const authStore = useAuthStore()

// --- 数据 ---
const members = ref<Member[]>([])
const loading = ref(false)
const keyword = ref('')

const currentPage = ref(0)
const pageSize = ref(10)

const sortOptions = [
  { label: '新增用户 ↓', value: 'createdAt_desc' },
  { label: '新增用户 ↑', value: 'createdAt_asc' },
  { label: '修改用户 ↓', value: 'updatedAt_desc' },
  { label: '修改用户 ↑', value: 'updatedAt_asc' },
  { label: 'ID ↓', value: 'id_desc' },
  { label: 'ID ↑', value: 'id_asc' },
]
const sortBy = ref('createdAt_desc')

// --- 详情弹窗 ---
const showDetailModal = ref(false)
const detailMember = ref<MemberDetail | null>(null)
const detailLoading = ref(false)

// --- 编辑弹窗 ---
const showEditModal = ref(false)
const editId = ref<number | null>(null)
const saveLoading = ref(false)

const editForm = ref<UpdateMemberDto>({
  nickname: '',
  gender: undefined,
  birthday: '',
  avatar: '',
  address: '',
})

// --- 会员卡管理 ---
const showCardModal = ref(false)
const cardMember = ref<Member | null>(null)
const cardMemberRecords = ref<MemberCardRecord[]>([])
const activeCards = ref<MemberCardType[]>([])
const cardLoading = ref(false)
const bindLoading = ref<number | null>(null)
const unbindLoading = ref<number | null>(null)

// --- 密码验证 ---
const showPasswordModal = ref(false)
const passwordInput = ref('')
const passwordError = ref('')
const passwordLoading = ref(false)
let passwordResolve: ((ok: boolean) => void) | null = null

// --- 确认弹窗 ---
const showConfirmModal = ref(false)
const confirmMessage = ref('')
let confirmResolve: ((ok: boolean) => void) | null = null

const genderMap: Record<number, string> = {
  0: '未知',
  1: '男',
  2: '女',
}

function getGenderLabel(gender: number): string {
  return genderMap[gender] || '未知'
}

function formatTime(t: string | null): string {
  if (!t) return '--'
  return new Date(t).toLocaleString('zh-CN')
}

function formatDate(d: string | null): string {
  if (!d) return '--'
  return d
}

// --- 分页 ---
const totalElements = computed(() => members.value.length)

const totalPages = computed(() => Math.max(1, Math.ceil(totalElements.value / pageSize.value)))

const pagedMembers = computed(() => {
  const sorted = [...members.value].sort((a, b) => {
    switch (sortBy.value) {
      case 'id_asc': return (a.id ?? 0) - (b.id ?? 0)
      case 'id_desc': return (b.id ?? 0) - (a.id ?? 0)
      case 'createdAt_asc': return new Date(a.createdAt || 0).getTime() - new Date(b.createdAt || 0).getTime()
      case 'createdAt_desc': return new Date(b.createdAt || 0).getTime() - new Date(a.createdAt || 0).getTime()
      case 'updatedAt_asc': return new Date(a.updatedAt || 0).getTime() - new Date(b.updatedAt || 0).getTime()
      case 'updatedAt_desc': return new Date(b.updatedAt || 0).getTime() - new Date(a.updatedAt || 0).getTime()
      default: return 0
    }
  })
  const start = currentPage.value * pageSize.value
  return sorted.slice(start, start + pageSize.value)
})

function changePage(page: number) {
  if (page >= 0 && page < totalPages.value) {
    currentPage.value = page
  }
}

// --- 搜索 ---
async function fetchMembers() {
  loading.value = true
  try {
    const keywordTrimmed = keyword.value.trim()
    if (keywordTrimmed) {
      const list = await searchMembers(keywordTrimmed, 1, 200)
      members.value = list
    } else {
      const list = await getAllMembers(1, 200)
      members.value = list
    }
    currentPage.value = 0
  } catch {
    members.value = []
  } finally {
    loading.value = false
  }
}

function handleSearch() {
  fetchMembers()
}

// --- 查看详情 ---
async function handleViewDetail(member: Member) {
  detailLoading.value = true
  showDetailModal.value = true
  detailMember.value = null
  try {
    const detail = await getMemberDetail(member.id)
    detailMember.value = detail
  } catch {
    toast.error('获取会员详情失败')
    showDetailModal.value = false
  } finally {
    detailLoading.value = false
  }
}

// --- 编辑 ---
function handleEdit(member: Member) {
  editId.value = member.id
  editForm.value = {
    nickname: member.nickname,
    gender: member.gender,
    birthday: member.birthday || '',
    avatar: member.avatar || '',
    address: member.address || '',
  }
  showEditModal.value = true
}

async function handleSave() {
  if (!editId.value) return
  saveLoading.value = true
  try {
    await updateMember(editId.value, editForm.value)
    toast.success('会员信息更新成功')
    showEditModal.value = false
    fetchMembers()
  } catch {
    toast.error('更新失败')
  } finally {
    saveLoading.value = false
  }
}

// --- 会员卡管理 ---
async function openCardModal(member: Member) {
  cardMember.value = member
  showCardModal.value = true
  cardLoading.value = true
  try {
    const [cards, records] = await Promise.all([
      getActiveCards(),
      getMemberCardRecords(member.id),
    ])
    activeCards.value = cards
    cardMemberRecords.value = records
  } catch {
    toast.error('获取会员卡信息失败')
  } finally {
    cardLoading.value = false
  }
}

async function requirePassword(): Promise<boolean> {
  return new Promise((resolve) => {
    passwordInput.value = ''
    passwordError.value = ''
    passwordResolve = resolve
    showPasswordModal.value = true
  })
}

async function confirmPassword() {
  if (!passwordInput.value) {
    passwordError.value = '请输入密码'
    return
  }
  passwordLoading.value = true
  passwordError.value = ''
  try {
    const ok = await verifyAdminPassword(passwordInput.value)
    if (ok) {
      showPasswordModal.value = false
      passwordResolve?.(true)
    } else {
      passwordError.value = '密码错误'
    }
  } catch {
    passwordError.value = '验证失败'
  } finally {
    passwordLoading.value = false
  }
}

function cancelPassword() {
  showPasswordModal.value = false
  passwordResolve?.(false)
}

async function requireConfirm(message: string): Promise<boolean> {
  return new Promise((resolve) => {
    confirmMessage.value = message
    confirmResolve = resolve
    showConfirmModal.value = true
  })
}

function onConfirmYes() {
  showConfirmModal.value = false
  confirmResolve?.(true)
}

function onConfirmNo() {
  showConfirmModal.value = false
  confirmResolve?.(false)
}

async function handleBindCard(cardType: MemberCardType) {
  if (!cardMember.value) return
  const ok = await requirePassword()
  if (!ok) return
  bindLoading.value = cardType.memberCardId
  try {
    await purchaseCard(cardMember.value.id, cardType.memberCardId)
    toast.success(`成功绑定: ${cardType.memberCardName}`)
    // 刷新卡记录
    const records = await getMemberCardRecords(cardMember.value.id)
    cardMemberRecords.value = records
  } catch {
    toast.error('绑定失败')
  } finally {
    bindLoading.value = null
  }
}

async function handleUnbindCard(record: MemberCardRecord) {
  const confirmed = await requireConfirm('确定要解绑该会员卡吗？')
  if (!confirmed) return
  const ok = await requirePassword()
  if (!ok) return
  unbindLoading.value = record.memberCardRecordId
  try {
    await refundCard(record.memberCardRecordId)
    toast.success('解绑成功')
    // 刷新卡记录
    if (cardMember.value) {
      const records = await getMemberCardRecords(cardMember.value.id)
      cardMemberRecords.value = records
    }
  } catch {
    toast.error('解绑失败')
  } finally {
    unbindLoading.value = null
  }
}

function getCardTypeLabel(type: string): string {
  const map: Record<string, string> = {
    TIME_CARD: '时长卡',
    COUNT_CARD: '次卡',
    STORED_VALUE_CARD: '储值卡',
  }
  return map[type] || type
}

function getCardStatusDesc(status: string): string {
  const map: Record<string, string> = {
    ACTIVE: '有效',
    USED_UP: '已用完',
    EXPIRED: '已过期',
    REFUNDED: '已退款',
  }
  return map[status] || status
}

function getCardNameByRecord(record: MemberCardRecord): string {
  const card = activeCards.value.find(c => c.memberCardId === record.memberCardId)
  return card?.memberCardName || `卡#${record.memberCardId}`
}

onMounted(() => {
  fetchMembers()
})
</script>

<template>
  <AppLayout>
    <!-- 工具栏 -->
    <div class="toolbar">
      <div class="search-row">
        <input
          v-model="keyword"
          type="text"
          placeholder="搜索会员编号、昵称或手机号..."
          class="search-input"
          @keyup.enter="handleSearch"
        />
        <button class="btn btn-outline" @click="handleSearch">
          <i class="fas fa-search"></i> 搜索
        </button>
        <select v-model="sortBy" class="sort-select" @change="currentPage = 0">
          <option v-for="opt in sortOptions" :key="opt.value" :value="opt.value">
            {{ opt.label }}
          </option>
        </select>
      </div>
    </div>

    <!-- 表格 -->
    <div class="table-wrapper">
      <div class="table-header">
        <h3>会员列表 ({{ totalElements }} 条)</h3>
      </div>

      <!-- skeleton -->
      <table v-if="loading">
        <thead>
          <tr>
            <th>ID</th><th>会员编号</th><th>昵称</th><th>手机号</th>
            <th>性别</th><th>生日</th><th>注册时间</th><th>操作</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="i in 5" :key="'sk-' + i">
            <td v-for="j in 8" :key="j">
              <span class="sk-td skeleton"></span>
            </td>
          </tr>
        </tbody>
      </table>

      <!-- real table -->
      <table v-else>
        <thead>
          <tr>
            <th>ID</th><th>会员编号</th><th>昵称</th><th>手机号</th>
            <th>性别</th><th>生日</th><th>注册时间</th><th>操作</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="m in pagedMembers" :key="m.id">
            <td>{{ m.id }}</td>
            <td>{{ m.memberNo }}</td>
            <td>{{ m.nickname }}</td>
            <td>{{ m.phone }}</td>
            <td>{{ getGenderLabel(m.gender) }}</td>
            <td>{{ formatDate(m.birthday) }}</td>
            <td>{{ formatTime(m.createdAt) }}</td>
            <td class="actions">
              <button class="btn-sm" @click="handleViewDetail(m)">详情</button>
              <button class="btn-sm" @click="handleEdit(m)" v-if="authStore.hasPermission('business:member:edit')">编辑</button>
              <button class="btn-sm btn-card" @click="openCardModal(m)">卡片</button>
            </td>
          </tr>
          <tr v-if="pagedMembers.length === 0">
            <td colspan="8" class="empty">暂无会员数据</td>
          </tr>
        </tbody>
      </table>

      <!-- pagination -->
      <div class="pagination" v-if="totalPages > 1">
        <button :disabled="currentPage === 0" @click="changePage(currentPage - 1)">上一页</button>
        <span>第 {{ currentPage + 1 }} / {{ totalPages }} 页</span>
        <button :disabled="currentPage >= totalPages - 1" @click="changePage(currentPage + 1)">下一页</button>
      </div>
    </div>

    <!-- 详情弹窗 -->
    <div v-if="showDetailModal" class="modal-overlay" @click.self="showDetailModal = false">
      <div class="modal modal-detail">
        <h3>会员详情</h3>
        <div v-if="detailLoading" class="loading-center">
          <i class="fas fa-spinner fa-spin"></i> 加载中...
        </div>
        <div v-else-if="detailMember" class="detail-grid">
          <div class="detail-item">
            <span class="detail-label">会员编号</span>
            <span class="detail-value">{{ detailMember.memberNo }}</span>
          </div>
          <div class="detail-item">
            <span class="detail-label">昵称</span>
            <span class="detail-value">{{ detailMember.nickname }}</span>
          </div>
          <div class="detail-item">
            <span class="detail-label">手机号</span>
            <span class="detail-value">{{ detailMember.phone }}</span>
          </div>
          <div class="detail-item">
            <span class="detail-label">性别</span>
            <span class="detail-value">{{ detailMember.genderDesc }}</span>
          </div>
          <div class="detail-item">
            <span class="detail-label">生日</span>
            <span class="detail-value">{{ formatDate(detailMember.birthday) }}</span>
          </div>
          <div class="detail-item">
            <span class="detail-label">地址</span>
            <span class="detail-value">{{ detailMember.address || '--' }}</span>
          </div>
          <div class="detail-item">
            <span class="detail-label">头像</span>
            <span class="detail-value">
              <img v-if="detailMember.avatar" :src="detailMember.avatar" class="avatar-preview" />
              <span v-else>--</span>
            </span>
          </div>
          <div class="detail-item">
            <span class="detail-label">注册时间</span>
            <span class="detail-value">{{ formatTime(detailMember.createdAt) }}</span>
          </div>
          <div class="detail-item">
            <span class="detail-label">最后登录</span>
            <span class="detail-value">{{ formatTime(detailMember.lastLoginAt) }}</span>
          </div>
          <div class="detail-item">
            <span class="detail-label">关注服务号</span>
            <span class="detail-value">
              <span :class="detailMember.subscribed ? 'badge-green' : 'badge-gray'">
                {{ detailMember.subscribed ? '已关注' : '未关注' }}
              </span>
            </span>
          </div>

          <!-- 会员卡信息 -->
          <div class="detail-section">
            <span class="detail-label">会员卡</span>
            <div class="detail-value">
              <span v-if="detailMember.memberCards.length === 0">暂无会员卡</span>
              <div v-else class="card-list">
                <div
                  v-for="card in detailMember.memberCards"
                  :key="card.id"
                  class="card-item"
                >
                  <span class="card-name">{{ card.memberCardName }}</span>
                  <span class="card-remain">
                    <template v-if="card.remainingTimes > 0">{{ card.remainingTimes }}/{{ card.memberCardTotalTimes }}次</template>
                    <template v-else-if="card.remainingAmount > 0">¥{{ card.remainingAmount }}/¥{{ card.memberCardAmount }}</template>
                    <template v-else-if="card.expireTime">到期 {{ formatTime(card.expireTime) }}</template>
                  </span>
                  <span class="card-status" :class="card.status === 'ACTIVE' ? 'badge-green' : 'badge-gray'">
                    {{ card.statusDesc }}
                  </span>
                </div>
              </div>
            </div>
          </div>
        </div>
        <div class="modal-actions">
          <button class="btn btn-outline" @click="showDetailModal = false">关闭</button>
        </div>
      </div>
    </div>

    <!-- 编辑弹窗 -->
    <div v-if="showEditModal" class="modal-overlay" @click.self="showEditModal = false">
      <div class="modal">
        <h3>编辑会员信息</h3>
        <div class="form-group">
          <label>昵称</label>
          <input v-model="editForm.nickname" placeholder="请输入昵称" />
        </div>
        <div class="form-group">
          <label>性别</label>
          <select v-model.number="editForm.gender" class="form-select">
            <option :value="undefined" disabled>请选择性别</option>
            <option :value="0">未知</option>
            <option :value="1">男</option>
            <option :value="2">女</option>
          </select>
        </div>
        <div class="form-group">
          <label>生日</label>
          <input v-model="editForm.birthday" type="date" />
        </div>
        <div class="form-group">
          <label>头像 URL</label>
          <input v-model="editForm.avatar" placeholder="请输入头像URL" />
        </div>
        <div class="form-group">
          <label>地址</label>
          <input v-model="editForm.address" placeholder="请输入地址" />
        </div>
        <div class="modal-actions">
          <button class="btn btn-outline" @click="showEditModal = false">取消</button>
          <button class="btn btn-primary" @click="handleSave" :disabled="saveLoading">
            <i v-if="saveLoading" class="fas fa-spinner fa-spin"></i>
            {{ saveLoading ? '保存中...' : '保存' }}
          </button>
        </div>
      </div>
    </div>

    <!-- 会员卡管理弹窗 -->
    <div v-if="showCardModal" class="modal-overlay" @click.self="showCardModal = false">
      <div class="modal modal-card">
        <h3>会员卡管理 - {{ cardMember?.nickname || cardMember?.memberNo }}</h3>

        <div v-if="cardLoading" class="loading-center">
          <i class="fas fa-spinner fa-spin"></i> 加载中...
        </div>
        <template v-else>
          <!-- 已绑定的卡 -->
          <div class="card-section">
            <h4>已绑定卡片</h4>
            <div v-if="cardMemberRecords.length === 0" class="empty-text">暂无绑定卡片</div>
            <div v-else class="card-record-list">
              <div
                v-for="record in cardMemberRecords"
                :key="record.id"
                class="card-record-item"
              >
                <div class="card-record-info">
                  <span class="card-name">{{ getCardNameByRecord(record) }}</span>
                  <span class="card-badge" :class="record.status === 'ACTIVE' ? 'badge-green' : 'badge-gray'">
                    {{ getCardStatusDesc(record.status) }}
                  </span>
                </div>
                <div class="card-record-detail">
                  <span v-if="record.remainingTimes > 0">剩余: {{ record.remainingTimes }}次</span>
                  <span v-if="record.remainingAmount > 0">余额: ¥{{ record.remainingAmount }}</span>
                  <span v-if="record.expireTime">到期: {{ formatTime(record.expireTime) }}</span>
                  <span class="card-purchase-time">购买: {{ formatTime(record.purchaseTime) }}</span>
                </div>
                <div class="card-record-actions">
                  <button
                    v-if="record.status !== 'REFUNDED' && authStore.hasPermission('business:memberCard:refund')"
                    class="btn-sm btn-danger"
                    :disabled="unbindLoading === record.memberCardRecordId"
                    @click="handleUnbindCard(record)"
                  >
                    <i v-if="unbindLoading === record.memberCardRecordId" class="fas fa-spinner fa-spin"></i>
                    解绑
                  </button>
                </div>
              </div>
            </div>
          </div>

          <!-- 可绑定的卡片类型 -->
          <div class="card-section">
            <h4>可绑定卡片</h4>
            <div v-if="activeCards.length === 0" class="empty-text">暂无可用卡片类型</div>
            <div v-else class="card-type-grid">
              <div
                v-for="card in activeCards"
                :key="card.id"
                class="card-type-item"
              >
                <div class="card-type-header">
                  <span class="card-type-name">{{ card.memberCardName }}</span>
                  <span class="card-type-tag">{{ getCardTypeLabel(card.memberCardType) }}</span>
                </div>
                <div class="card-type-detail">
                  <span v-if="card.memberCardPrice">¥{{ card.memberCardPrice }}</span>
                  <span v-if="card.memberCardValidityDays">{{ card.memberCardValidityDays }}天</span>
                  <span v-if="card.memberCardTotalTimes">{{ card.memberCardTotalTimes }}次</span>
                  <span v-if="card.memberCardAmount">面额 ¥{{ card.memberCardAmount }}</span>
                </div>
                <button
                  class="btn-sm btn-primary-sm"
                  :disabled="bindLoading === card.memberCardId || !authStore.hasPermission('business:memberCard:purchase')"
                  @click="handleBindCard(card)"
                >
                  <i v-if="bindLoading === card.memberCardId" class="fas fa-spinner fa-spin"></i>
                  绑定
                </button>
              </div>
            </div>
          </div>
        </template>

        <div class="modal-actions">
          <button class="btn btn-outline" @click="showCardModal = false">关闭</button>
        </div>
      </div>
    </div>

    <!-- 密码验证弹窗 -->
    <div v-if="showPasswordModal" class="modal-overlay" @click.self="cancelPassword">
      <div class="modal modal-password">
        <h3>管理员验证</h3>
        <p class="password-hint">绑定/解绑会员卡需要验证管理员密码</p>
        <div class="form-group">
          <label>密码</label>
          <input
            v-model="passwordInput"
            type="password"
            placeholder="请输入管理员密码"
            @keyup.enter="confirmPassword"
          />
          <span v-if="passwordError" class="form-error">{{ passwordError }}</span>
        </div>
        <div class="modal-actions">
          <button class="btn btn-outline" @click="cancelPassword" :disabled="passwordLoading">取消</button>
          <button class="btn btn-primary" @click="confirmPassword" :disabled="passwordLoading">
            <i v-if="passwordLoading" class="fas fa-spinner fa-spin"></i>
            {{ passwordLoading ? '验证中...' : '确认' }}
          </button>
        </div>
      </div>
    </div>

    <!-- 确认弹窗 -->
    <div v-if="showConfirmModal" class="modal-overlay" @click.self="onConfirmNo">
      <div class="modal modal-confirm">
        <h3>操作确认</h3>
        <p class="confirm-text">{{ confirmMessage }}</p>
        <div class="modal-actions">
          <button class="btn btn-outline" @click="onConfirmNo">取消</button>
          <button class="btn btn-danger-filled" @click="onConfirmYes">确定</button>
        </div>
      </div>
    </div>

  </AppLayout>
</template>

<style scoped>
.toolbar { margin-bottom: 20px; }

.search-row {
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
}

.search-input {
  padding: 9px 16px; border-radius: 12px; border: 1px solid #e2e8f0;
  font-size: 14px; width: 300px; outline: none; transition: 0.15s;
}

.search-input:focus {
  border-color: #4f7cff; box-shadow: 0 0 0 3px rgba(79,124,255,0.12);
}

.sort-select {
  padding: 9px 14px; border-radius: 12px; border: 1px solid #e2e8f0;
  font-size: 13px; color: #1e293b; background: white; outline: none;
  cursor: pointer; transition: 0.15s; min-width: 150px;
}

.sort-select:focus {
  border-color: #4f7cff; box-shadow: 0 0 0 3px rgba(79,124,255,0.12);
}

.btn {
  padding: 9px 20px; border-radius: 40px; border: 1px solid #e2e8f0;
  background: white; font-weight: 500; font-size: 13px; color: #1e293b;
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer; transition: 0.15s;
}

.btn-primary {
  background: #4f7cff; border-color: #4f7cff; color: white;
  box-shadow: 0 4px 10px rgba(79,124,255,0.25);
}

.btn-primary:hover:not(:disabled) { background: #3f6ae0; }
.btn-primary:disabled { opacity: 0.6; cursor: not-allowed; }
.btn-outline:hover { background: #f0f5ff; }

.table-wrapper {
  background: white; border-radius: 20px; border: 1px solid #edf2f9;
  padding: 20px 24px 8px 24px;
}

.table-header {
  display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;
}

.table-header h3 {
  font-weight: 600; font-size: 16px; color: #0b1a33;
}

table {
  width: 100%; border-collapse: collapse; font-size: 14px;
}

th {
  text-align: left; padding: 12px 8px 12px 0;
  font-weight: 600; color: #4e627c; border-bottom: 1px solid #eef2f8;
}

td {
  padding: 14px 8px 14px 0; border-bottom: 1px solid #f1f5fa; color: #1e2f44;
}

tr:last-child td { border-bottom: none; }

td.empty {
  text-align: center; padding: 40px; color: #8b9ab0;
}

.skeleton {
  background: linear-gradient(90deg, #eef2f8 25%, #e0e7f0 50%, #eef2f8 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
  border-radius: 6px;
}

.sk-td {
  display: block;
  height: 16px;
  width: 80%;
}

@keyframes shimmer {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}

tr { animation: fadeIn 0.35s ease; }

.actions { white-space: nowrap; }

.btn-sm {
  padding: 5px 12px; border-radius: 20px; border: 1px solid #e2e8f0;
  background: white; font-size: 12px; cursor: pointer; margin-right: 6px; transition: 0.15s;
}

.btn-sm:hover { background: #f0f5ff; }

.pagination {
  display: flex; justify-content: center; align-items: center;
  gap: 16px; padding: 16px 0; font-size: 13px; color: #4e627c;
}

.pagination button {
  padding: 6px 16px; border-radius: 8px; border: 1px solid #e2e8f0;
  background: white; cursor: pointer; font-size: 13px; transition: 0.15s;
}

.pagination button:hover:not(:disabled) { background: #f0f5ff; }
.pagination button:disabled { opacity: 0.4; cursor: not-allowed; }

/* ---- modal ---- */
.modal-overlay {
  position: fixed; inset: 0; background: rgba(0,0,0,0.3);
  display: flex; align-items: center; justify-content: center; z-index: 200;
  animation: fadeIn 0.15s ease;
}

.modal {
  background: white; border-radius: 20px; padding: 28px;
  width: 560px; max-width: 92vw; max-height: 85vh; overflow-y: auto;
  box-shadow: 0 12px 40px rgba(0,0,0,0.12);
  animation: fadeIn 0.2s ease;
}

.modal-detail { width: 620px; }

.modal h3 {
  font-size: 18px; font-weight: 600; color: #0b1a33; margin-bottom: 20px;
}

.loading-center {
  text-align: center; padding: 40px; color: #8b9ab0; font-size: 14px;
}

.detail-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 16px;
}

.detail-item {
  display: flex; flex-direction: column; gap: 4px;
}

.detail-label {
  font-size: 12px; font-weight: 600; color: #8b9ab0; text-transform: uppercase;
}

.detail-value {
  font-size: 15px; color: #1e293b; font-weight: 500;
}

.detail-section {
  grid-column: 1 / -1;
  display: flex; flex-direction: column; gap: 8px;
  margin-top: 8px;
  padding-top: 16px;
  border-top: 1px solid #eef2f8;
}

.avatar-preview {
  width: 48px; height: 48px; border-radius: 50%; object-fit: cover;
  border: 2px solid #e2e8f0;
}

.badge-green {
  display: inline-block;
  padding: 2px 10px; border-radius: 20px; font-size: 12px;
  background: #e0f5ec; color: #0b8b5e; font-weight: 500;
}

.badge-gray {
  display: inline-block;
  padding: 2px 10px; border-radius: 20px; font-size: 12px;
  background: #f1f5f9; color: #64748b; font-weight: 500;
}

.card-list {
  display: flex; flex-direction: column; gap: 8px;
}

.card-item {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 14px; background: #f8fafd; border-radius: 10px;
  border: 1px solid #eef2f8;
}

.card-name {
  flex: 1; font-weight: 500; color: #1e293b;
}

.card-remain {
  font-size: 13px; color: #4e627c;
}

.card-status {
  font-size: 12px;
}

.form-group { margin-bottom: 14px; }
.form-group label {
  display: block; font-size: 13px; font-weight: 600; color: #1e293b; margin-bottom: 4px;
}

.form-group input,
.form-group select {
  width: 100%; padding: 9px 12px; border-radius: 10px;
  border: 1px solid #e2e8f0; font-size: 14px; outline: none;
  transition: 0.15s; background: #f8fafd; font-family: inherit;
}
.form-group input:focus,
.form-group select:focus {
  border-color: #4f7cff; box-shadow: 0 0 0 3px rgba(79,124,255,0.12);
}

.form-select { appearance: auto; }

.modal-actions {
  display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px;
}

/* ---- card management ---- */
.modal-card {
  width: 680px;
}

.card-section {
  margin-bottom: 20px;
}

.card-section h4 {
  font-size: 14px; font-weight: 600; color: #1e293b;
  margin-bottom: 10px; padding-bottom: 6px; border-bottom: 1px solid #edf2f9;
}

.empty-text {
  color: #8b9ab0; font-size: 13px; padding: 12px 0;
}

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

.card-record-item {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 14px; border-radius: 12px; border: 1px solid #eef2f8;
  background: #f9fafc; flex-wrap: wrap; gap: 8px;
}

.card-record-info {
  display: flex; align-items: center; gap: 10px;
}

.card-record-info .card-name {
  font-weight: 600; font-size: 13px; color: #1e293b;
}

.card-record-detail {
  display: flex; gap: 12px; font-size: 12px; color: #4e627c; flex-wrap: wrap;
}

.card-purchase-time {
  color: #8b9ab0;
}

.card-record-actions {
  flex-shrink: 0;
}

.card-type-grid {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px;
}

.card-type-item {
  padding: 12px; border-radius: 12px; border: 1px solid #eef2f8;
  background: #f9fafc; display: flex; flex-direction: column; gap: 8px;
}

.card-type-header {
  display: flex; align-items: center; justify-content: space-between;
}

.card-type-name {
  font-weight: 600; font-size: 13px; color: #1e293b;
}

.card-type-tag {
  font-size: 11px; padding: 2px 8px; border-radius: 20px;
  background: #e0e7ff; color: #4f7cff; font-weight: 500;
}

.card-type-detail {
  font-size: 12px; color: #4e627c; display: flex; gap: 8px; flex-wrap: wrap;
}

.btn-primary-sm {
  padding: 5px 14px; border-radius: 20px; border: none;
  background: #4f7cff; color: white; font-size: 12px; cursor: pointer;
  transition: 0.15s; align-self: flex-end;
}

.btn-primary-sm:hover:not(:disabled) { background: #3f6ae0; }
.btn-primary-sm:disabled { opacity: 0.6; cursor: not-allowed; }

.btn-danger {
  margin-right: 0;
  border-color: #fecaca; color: #dc2626;
}

.btn-danger:hover:not(:disabled) { background: #fef2f2; }
.btn-danger:disabled { opacity: 0.6; cursor: not-allowed; }

.btn-card {
  border-color: #d1e7dd; color: #0f5132;
}

.btn-card:hover { background: #f0faf5; }

/* ---- password modal ---- */
.modal-password {
  width: 400px;
}

.password-hint {
  font-size: 13px; color: #4e627c; margin: -8px 0 8px 0;
}

.form-error {
  display: block; font-size: 12px; color: #dc2626; margin-top: 4px;
}

/* ---- confirm modal ---- */
.modal-confirm {
  width: 380px;
}

.confirm-text {
  font-size: 14px; color: #4e627c; margin: 12px 0;
}

.btn-danger-filled {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 20px; border-radius: 6px; font-size: 14px;
  border: none; cursor: pointer; transition: background 0.2s;
  background: #dc2626; color: #fff;
}
.btn-danger-filled:hover { background: #b91c1c; }
</style>
