<script lang="ts" setup>
import { ref, computed, nextTick, onUnmounted } from 'vue'
import { onLoad, onShow, onHide, onUnload, onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
import dayjs from 'dayjs'
import { getAppointmentDetail, checkIn, checkOut, getVisitRecords } from '@/api/appointment'
import { workflowAPI } from '@/api/workflow'
import type { IAppointmentDetail, IVisitRecord } from '@/api/types/appointment'
import type { ITask } from '@/api/types/workflow'
import { APPOINTMENT_STATUS_MAP } from '@/utils/constants'
import { useUserStore } from '@/store'
import QRCode from 'qrcode';
import { md5 } from 'js-md5';

// 微信小程序环境缺少 TextEncoder polyfill（qrcode 包内部依赖）
if (typeof TextEncoder === 'undefined') {
  const _TextEncoder = function () {} as any
  _TextEncoder.prototype.encode = function (str: string): Uint8Array {
    const buf = new Uint8Array(str.length * 4)
    let len = 0
    for (let i = 0; i < str.length; i++) {
      let c = str.charCodeAt(i)
      if (c < 0x80) {
        buf[len++] = c
      } else if (c < 0x800) {
        buf[len++] = 0xc0 | (c >> 6)
        buf[len++] = 0x80 | (c & 0x3f)
      } else if (c < 0xd800 || c >= 0xe000) {
        buf[len++] = 0xe0 | (c >> 12)
        buf[len++] = 0x80 | ((c >> 6) & 0x3f)
        buf[len++] = 0x80 | (c & 0x3f)
      } else {
        i++
        c = 0x10000 + (((c & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff))
        buf[len++] = 0xf0 | (c >> 18)
        buf[len++] = 0x80 | ((c >> 12) & 0x3f)
        buf[len++] = 0x80 | ((c >> 6) & 0x3f)
        buf[len++] = 0x80 | (c & 0x3f)
      }
    }
    return buf.slice(0, len)
  }
  // 兼容小程序环境，globalThis / global / self / window 依次降级
  const g = (typeof globalThis !== 'undefined' ? globalThis :
             typeof global !== 'undefined' ? global :
             typeof self !== 'undefined' ? self :
             typeof window !== 'undefined' ? window : ({} as any)) as any
  g.TextEncoder = _TextEncoder as any
}

const userStore = useUserStore()
const isGuard = computed(() => userStore.isGuard)
const isVisitor = computed(() => userStore.isVisitor)

const visitRecords = ref<IVisitRecord[]>([])
const recordsLoading = ref(false)

definePage({
  style: { navigationBarTitleText: '预约详情' },
})

const detail = ref<IAppointmentDetail | null>(null)
const loading = ref(false)
const id = ref(0)

// 工作流实例和任务
const workflowInstance = ref<any>(null)
const workflowTasks = ref<ITask[]>([])

// 审核操作
const showActionPopup = ref(false)
const actionType = ref<'approve' | 'reject'>('approve')
const comment = ref('')
const submitting = ref(false)

// 当前用户待处理的任务（可能有多个审批人，取第一个）
const currentTask = computed(() => {
  const uid = userStore.userInfo.id
  if (!uid) return null
  return workflowTasks.value.find(t => t.assignee_id === uid && t.status === 0) || null
})

// 当前用户是否可审核
const canAudit = computed(() => {
  return !!currentTask.value
})

// 访客端状态描述
const statusDescription = computed(() => {
  const status = detail.value?.status
  if (status === 3) return '已取消'
  if (status === 1) return '审核通过'
  if (status === 2) return '拒绝入园'
  if (status === 0) return '审核中'
  return ''
})

// 固定审批步骤时间线
const timelineSteps = computed(() => {
  const steps = [
    { id: 'start',     label: '审核开始' },
    { id: 'Employee',     label: '公司接待员工审批' },
    { id: 'DeptApprover', label: '部门指定人审批' },
    { id: 'VPApproval',   label: '分管领导审批' },
    { id: 'end',          label: '审核结束' },
  ]

  let rejectedAfter = false  // 是否已遇到拒绝，后续步骤加删除线

  return steps.map((step) => {
    // 查找对应 task（end/start 无 task）
    const task = (step.id === 'start' || step.id === 'end')
      ? null
      : workflowTasks.value.find(t => t.node_id === step.id)

    let status = 0 // 0=未开始 1=通过 2=拒绝 3=进行中
    let name = ''
    let time = ''
    let comment = ''
    let hasTask = false

    if (task) {
      hasTask = true
      status = task.status
      if (status === 1) name = task.assignee?.real_name || task.assignee?.nickname || ''
      time = task.handled_at ? dayjs(task.handled_at).format('MM-DD HH:mm') : ''
      comment = task.comment || ''

      // 访客只能看员工姓名
      if (isVisitor.value && step.id !== 'Employee') {
        name = ''
      }
    }

    // 如果该节点被拒绝，后续步骤标记 strikethrough
    if (status === 2) rejectedAfter = true

    const isRejected = status === 2

    // 样式
    let dotClass = 'bg-gray-200 border-gray-300'
    let lineClass = 'bg-gray-200'
    let textClass = 'text-[#999]'
    let tag = ''
    let tagType = 'info'

    if (hasTask) {
      if (status === 1) {
        dotClass = 'bg-green-500 border-green-500'
        lineClass = 'bg-green-300'
        textClass = ''
        tag = '已通过'
        tagType = 'success'
      } else if (status === 2) {
        dotClass = 'bg-red-500 border-red-500'
        lineClass = 'bg-red-300'
        textClass = 'text-red-500'
        tag = '已拒绝'
        tagType = 'danger'
      } else if (status === 0) {
        dotClass = 'bg-orange-400 border-orange-400'
        lineClass = 'bg-gray-200'
        textClass = ''
        tag = '待审批'
        tagType = 'warning'
      }
    } else if (step.id === 'start') {
      dotClass = 'bg-blue-400 border-blue-400'
      textClass = 'text-[#333]'
    } else if (step.id === 'end') {
      // 根据整体状态
      const s = detail.value?.status
      if (s === 1) {
        dotClass = 'bg-green-500 border-green-500'
        lineClass = 'bg-green-300'
        textClass = 'text-green-600'
      } else if (s === 2) {
        dotClass = 'bg-red-500 border-red-500'
        textClass = 'text-red-500'
      } else {
        dotClass = 'bg-gray-200 border-gray-300'
        textClass = 'text-[#999]'
      }
    }

    // 遇到拒绝后，后续步骤加删除线
    const strikethrough = rejectedAfter && step.id !== 'start' && status !== 2

    return {
      ...step,
      status,
      dotClass,
      lineClass,
      textClass: textClass + (strikethrough ? ' line-through' : '') + (isRejected ? ' font-medium' : ''),
      tag,
      tagType,
      name,
      time,
      comment,
    }
  })
})

onLoad((options) => {
  id.value = Number(options?.id || 0)
})

let qrTimer: ReturnType<typeof setInterval> | null = null

onShow(() => {
  // 禁止截屏（进入页面时开启）
  try { uni.setVisualEffectOnCapture({ visualEffect: 'hidden' }) } catch {}
  if (id.value) {
    loadDetail()
    loadWorkflow()
  }
})

// 离开页面时关闭禁止截屏，避免污染其他页面
onHide(() => {
  try { uni.setVisualEffectOnCapture({ visualEffect: 'none' }) } catch {}
})
onUnload(() => {
  try { uni.setVisualEffectOnCapture({ visualEffect: 'none' }) } catch {}
})
onUnmounted(() => {
  try { uni.setVisualEffectOnCapture({ visualEffect: 'none' }) } catch {}
})

// 分享到聊天
onShareAppMessage(() => {
  return {
    title: `访客预约`,
    path: `/subpackages/appointment/appointment-detail?id=${id.value}`,
  }
})

// 分享到朋友圈
onShareTimeline(() => {
  return {
    title: `访客预约`,
  }
})

async function loadDetail() {
  loading.value = true
  try {
    detail.value = await getAppointmentDetail(id.value)
    // 通过状态且为创建者 → 启动二维码定时刷新
    if (detail.value.status === 1 && userStore.userInfo.id === detail.value.creator_user_id) {
      // 清除旧定时器，防止 onShow 重复启动
      if (qrTimer) {
        clearInterval(qrTimer)
        qrTimer = null
      }
      nextTick(() => {
        drawQR()
        qrTimer = setInterval(drawQR, 5000)
      })
    } else if (qrTimer) {
      // 状态变更或非创建者，停止定时器
      clearInterval(qrTimer)
      qrTimer = null
    }
    // 加载进出场记录（保安查看时）
    loadVisitRecords()
  } catch { /* ignore */ } finally { loading.value = false }
}

async function loadWorkflow() {
  try {
    // 按业务类型+业务ID查找工作流实例
    const res = await workflowAPI.getProcessInstances({
      business_type: 'appointment',
      business_id: id.value,
    })
    const list = (res as any)?.list || (res as any) || []
    if (Array.isArray(list) && list.length > 0) {
      const inst = list[0]
      workflowInstance.value = inst
      // 获取实例详情（含任务列表）
      const detail = await workflowAPI.getInstanceDetail(inst.id)
      workflowTasks.value = (detail as any)?.tasks || []
    }
  } catch { /* ignore */ }
}

// === 审核操作 ===
function openApprove() {
  actionType.value = 'approve'
  comment.value = ''
  showActionPopup.value = true
}

function openReject() {
  actionType.value = 'reject'
  comment.value = ''
  showActionPopup.value = true
}

async function submitAction() {
  if (!currentTask.value) {
    uni.showToast({ title: '没有待处理的任务', icon: 'none' })
    return
  }
  submitting.value = true
  try {
    if (actionType.value === 'approve') {
      await workflowAPI.approveTask(currentTask.value.id!, comment.value)
    } else {
      await workflowAPI.rejectTask(currentTask.value.id!, comment.value)
    }
    showActionPopup.value = false
    uni.showToast({ title: actionType.value === 'approve' ? '已通过' : '已拒绝', icon: 'success' })
    // 刷新数据
    await loadDetail()
    await loadWorkflow()
  } catch {
    // http.ts 已自动弹出错误提示
  } finally {
    submitting.value = false
  }
}

function callPhone(phone: string) {
  uni.makePhoneCall({ phoneNumber: phone })
}

// ==================== 保安出入场登记 ====================
async function loadVisitRecords() {
  if (!detail.value || !isGuard.value) return
  recordsLoading.value = true
  try {
    visitRecords.value = await getVisitRecords(detail.value.id)
  } catch { /* ignore */ } finally { recordsLoading.value = false }
}

const checkingIn = ref(false)
const checkingOut = ref(false)

async function handleCheckIn() {
  if (!detail.value || checkingIn.value) return
  const confirmed = await new Promise<boolean>((resolve) => {
    uni.showModal({
      title: '确认进场',
      content: '请确认访客已到达并完成身份核验，是否登记进场？',
      success: (r) => resolve(r.confirm),
    })
  })
  if (!confirmed) return
  checkingIn.value = true
  try {
    await checkIn({ appointment_id: detail.value.id })
    uni.showToast({ title: '登记进场成功', icon: 'success' })
    await loadVisitRecords()
  } catch (err: any) {
    uni.showToast({ title: err.message || '登记进场失败', icon: 'none' })
  } finally { checkingIn.value = false }
}

async function handleCheckOut() {
  if (!detail.value || checkingOut.value) return
  const confirmed = await new Promise<boolean>((resolve) => {
    uni.showModal({
      title: '确认出场',
      content: '请确认访客已离开园区，是否登记出场？',
      success: (r) => resolve(r.confirm),
    })
  })
  if (!confirmed) return
  checkingOut.value = true
  try {
    await checkOut({ appointment_id: detail.value.id })
    uni.showToast({ title: '登记出场成功', icon: 'success' })
    await loadVisitRecords()
  } catch (err: any) {
    uni.showToast({ title: err.message || '登记出场失败', icon: 'none' })
  } finally { checkingOut.value = false }
}

function stepStatusText(status: number) {
  const map: Record<number, string> = { 0: '待审核', 1: '已通过', 2: '已拒绝' }
  return map[status] || '未知'
}

function stepStatusType(status: number) {
  const map: Record<number, string> = { 0: 'warning', 1: 'success', 2: 'danger' }
  return map[status] || 'info'
}

// ==================== 入场二维码 ====================
// ==================== 入场二维码 ====================
const qrFailed = ref(false)

/** 用于签名二维码的密钥（前后端约定） */
const QR_SECRET_KEY = 'LKTX_QR_SECRET_2024'

function generateQRData(id: number) {
  const timestamp = Math.floor(Date.now() / 1000)
  const sign = md5(`${id}|${timestamp}|${QR_SECRET_KEY}`)
  return { id, timestamp, sign }
}

function drawQR() {
  if (!detail.value) return
  qrFailed.value = false
  const data = generateQRData(detail.value.id)
  const content = JSON.stringify(data)
  try {
    // QRCode.create 返回 { modules: BitMatrix, version, ... }
    // BitMatrix: { size, data: Uint8Array, get(row,col) }
    const qrData = QRCode.create(content, { errorCorrectionLevel: 'L' })
    const bits = qrData.modules
    const size = bits.size
    const canvasSize = 200
    const cellSize = Math.floor(canvasSize / size)
    const offset = Math.floor((canvasSize - cellSize * size) / 2)

    const ctx = uni.createCanvasContext('qrCanvas')
    ctx.setFillStyle('#ffffff')
    ctx.fillRect(0, 0, canvasSize, canvasSize)
    ctx.setFillStyle('#000000')
    for (let r = 0; r < size; r++) {
      for (let c = 0; c < size; c++) {
        if (bits.get(r, c)) {
          ctx.fillRect(offset + c * cellSize, offset + r * cellSize, cellSize, cellSize)
        }
      }
    }
    ctx.draw()
  } catch (e) {
    console.warn('QR码生成失败', e)
    qrFailed.value = true
  }
}

onUnmounted(() => {
  if (qrTimer) {
    clearInterval(qrTimer)
    qrTimer = null
  }
})
</script>

<template>
  <view class="min-h-screen bg-[#f5f7fa] px-4 pt-4">
    <wd-loading v-if="loading" class="py-20" />

    <template v-else-if="detail">
      <!-- 状态 -->
      <view class="card mb-3 p-4">
        <view class="flex items-center justify-between">
          <text class="text-lg font-bold text-[#1a1a1a]">预约详情</text>
          <view class="flex items-center gap-2">
            <wd-tag :type="APPOINTMENT_STATUS_MAP[detail.status]?.color === '#18bc37' ? 'success' : APPOINTMENT_STATUS_MAP[detail.status]?.color === '#dd524d' ? 'danger' : 'warning'" size="large">
              {{ APPOINTMENT_STATUS_MAP[detail.status]?.text || '未知' }}
            </wd-tag>
          </view>
        </view>
        <view class="mt-1 text-xs text-[#999]">
          预约编号：{{ detail.id }} | {{ dayjs(detail.created_at).format('YYYY-MM-DD HH:mm') }}
        </view>
      </view>

      <!-- 入场二维码（通过状态且为创建者） -->
      <view v-if="detail.status === 1 && userStore.userInfo.id === detail.creator_user_id" class="card mb-3 p-4">
        <view class="card-qrcode-title">入场二维码</view>
        <view class="flex justify-center py-3">
          <canvas canvas-id="qrCanvas" style="width: 200px; height: 200px;" />
        </view>
        <view v-if="qrFailed" class="text-center text-xs text-[#dd524d]">二维码生成失败，请稍后重试</view>
        <view v-else class="text-center text-xs text-[#999]">二维码每5秒自动刷新</view>
      </view>

      <!-- 保安出入场登记（审核通过 + 保安身份） -->
      <view v-if="detail.status === 1 && isGuard" class="card mb-3 p-4">
        <view class="card-title">出入场登记</view>
        <view class="flex justify-center gap-4 py-3">
          <wd-button type="success" :loading="checkingIn" :disabled="checkingIn || checkingOut" @click="handleCheckIn">
            登记进场
          </wd-button>
          <wd-button type="warning" :loading="checkingOut" :disabled="checkingIn || checkingOut" @click="handleCheckOut">
            登记出场
          </wd-button>
        </view>

        <!-- 进出场记录 -->
        <view v-if="recordsLoading" class="text-center text-xs text-[#999] py-2">加载中...</view>
        <view v-else-if="visitRecords.length === 0" class="text-center text-xs text-[#999] py-2">暂无进出场记录</view>
        <view v-else class="border-t border-[#f0f0f0] pt-3">
          <view
            v-for="(rec, idx) in visitRecords"
            :key="rec.id"
            class="flex items-center justify-between py-2 text-sm"
          >
            <view class="flex items-center gap-2">
              <view
                class="w-2 h-2 rounded-full"
                :class="rec.check_type === 1 ? 'bg-green-500' : 'bg-orange-400'"
              />
              <text class="text-[#333]">{{ rec.check_type === 1 ? '进场' : '出场' }}</text>
            </view>
            <text class="text-[#999]">{{ dayjs(rec.check_time).format('YYYY-MM-DD HH:mm:ss') }}</text>
          </view>
        </view>
      </view>

      <!-- 外部来访人员 -->
      <view class="card mb-3">
        <view class="card-title">外部来访人员</view>
        <view class="card-body">
          <view v-for="(v, idx) in detail.visitors" :key="idx" class="visitor-block">
            <view class="flex items-center gap-2 mb-2">
              <wd-icon name="user" size="16" :color="idx === 0 ? '#2979ff' : '#18bc37'" />
              <text class="text-sm font-medium text-[#1a1a1a]">{{ v.name }}</text>
              <wd-tag :type="idx === 0 ? 'primary' : 'success'">访客{{ idx + 1 }}</wd-tag>
            </view>
            <view class="text-xs text-[#666] leading-relaxed">
              单位：{{ v.company || '-' }}
            </view>
            <view class="text-xs text-[#666] leading-relaxed">
              <text>手机号：</text>
              <text class="text-[#2979ff]" @click="callPhone(v.phone)">{{ v.phone }}</text>
            </view>
            <view v-if="v.id_type" class="text-xs text-[#666] leading-relaxed">
              证件：{{ v.id_type }} {{ v.id_number || '' }}
            </view>
          </view>
        </view>
      </view>

      <!-- 预约信息 -->
      <view class="card mb-3">
        <view class="card-title">预约信息</view>
        <view class="card-body">
          <view v-if="detail.visit_purpose" class="info-row">
            <text class="info-label">来访目的</text>
            <text class="info-value">{{ detail.visit_purpose }}</text>
          </view>
          <view class="info-row">
            <text class="info-label">开始时间</text>
            <text class="info-value">{{ dayjs(detail.visit_start_time).format('YYYY-MM-DD HH:mm') }}</text>
          </view>
          <view class="info-row">
            <text class="info-label">结束时间</text>
            <text class="info-value">{{ dayjs(detail.visit_end_time).format('YYYY-MM-DD HH:mm') }}</text>
          </view>
          <view class="info-row">
            <text class="info-label">到访区域</text>
            <view class="info-value text-right">
              <view v-for="(area, idx) in detail.areas" :key="idx" class="text-[#666] text-sm">{{ area }}</view>
              <text v-if="!detail.areas?.length" class="text-[#999]">无</text>
            </view>
          </view>
          <view v-if="detail.goods?.length" class="info-row">
            <text class="info-label">携带物品</text>
            <view class="info-value text-right">
              <view v-for="(g, idx) in detail.goods" :key="idx" class="text-[#666] text-sm">{{ g.name }} x{{ g.quantity }}</view>
            </view>
          </view>
          <view v-if="detail.comment" class="info-row">
            <text class="info-label">审核意见</text>
            <text class="info-value">{{ detail.comment }}</text>
          </view>
        </view>
      </view>

      <!-- 车辆信息 -->
      <view v-if="detail.vehicles?.length" class="card mb-3">
        <view class="card-title">车辆信息</view>
        <view class="card-body">
          <view v-for="(v, idx) in detail.vehicles" :key="idx" class="info-row">
            <text class="info-label">车辆{{ idx + 1 }}</text>
            <text class="info-value">{{ [v.brand, v.plate, v.color].filter(Boolean).join(' ') }}</text>
          </view>
        </view>
      </view>

      <!-- 备注 -->
      <view v-if="detail.remark" class="card mb-3">
        <view class="card-title">备注</view>
        <view class="px-4 py-3 text-sm text-[#666] leading-relaxed">
          {{ detail.remark }}
        </view>
      </view>

      <!-- 申请人信息 -->
      <view class="card mb-3" v-if="false">
        <view class="card-title">申请人</view>
        <view class="card-body">
          <view class="info-row">
            <text class="info-label">姓名</text>
            <text class="info-value">{{ detail.creator_info?.name || '-' }}</text>
          </view>
          <view class="info-row">
            <text class="info-label">手机号</text>
            <text class="info-value text-[#2979ff]" @click="callPhone(detail.creator_info?.phone || '')">{{ detail.creator_info?.phone || '-' }}</text>
          </view>
        </view>
      </view>

      <!-- 公司接待人员 -->
      <view class="card mb-3">
        <view class="card-title">公司接待人员</view>
        <view class="card-body">
          <view class="info-row">
            <text class="info-label">姓名</text>
            <text class="info-value">{{ detail.employee_info?.name || '-' }}</text>
          </view>
          <view class="info-row">
            <text class="info-label">手机号</text>
            <text class="info-value text-[#2979ff]" @click="callPhone(detail.employee_info?.phone || '')">{{ detail.employee_info?.phone || '-' }}</text>
          </view>
        </view>
      </view>

      <!-- 审核进度（固定五步） -->
      <view class="card mb-3">
        <view class="card-title">审核进度</view>
        <view class="card-body">
          <view class="px-4 py-3">
            <view class="space-y-0">
              <view
                v-for="(step, idx) in timelineSteps"
                :key="step.id"
                class="flex gap-3"
                :class="{ 'pb-4': idx < timelineSteps.length - 1 }"
              >
                <!-- 左侧圆点 + 连线 -->
                <view class="flex flex-col items-center">
                  <view
                    class="w-3 h-3 rounded-full border-2 mt-1"
                    :class="step.dotClass"
                  />
                  <view
                    v-if="idx < timelineSteps.length - 1"
                    class="w-0.5 flex-1 mt-1"
                    :class="step.lineClass"
                  />
                </view>
                <!-- 右侧内容 -->
                <view class="flex-1 min-w-0" :class="step.textClass">
                  <view class="flex items-center gap-2">
                    <text class="text-sm font-medium">{{ step.label }}</text>
                    <wd-tag v-if="step.tag" :type="step.tagType" size="small">{{ step.tag }}</wd-tag>
                  </view>
                  <view class="text-xs mt-0.5">
                    <text v-if="step.name" class="text-[#999]">{{ step.name }}</text>
                    <text v-if="step.time" class="text-[#999]"> · {{ step.time }}</text>
                  </view>
                  <view v-if="step.comment" class="text-xs text-[#666] mt-0.5 bg-gray-50 rounded px-2 py-1">
                    {{ step.comment }}
                  </view>
                </view>
              </view>
            </view>
          </view>
        </view>
      </view>

      <!-- 底部审核按钮 -->
      <view v-if="canAudit" class="pb-4">
        <view class="mt-3 flex gap-3">
          <wd-button type="danger" size="large" block @click="openReject">拒绝</wd-button>
          <wd-button type="primary" size="large" block @click="openApprove">通过</wd-button>
        </view>
      </view>
    </template>

    <!-- 审核操作弹窗 -->
    <wd-popup
      v-model="showActionPopup"
      position="bottom"
      custom-style="border-radius: 16px 16px 0 0; padding: 20px;"
    >
      <view class="text-center">
        <text class="text-lg font-bold">
          {{ actionType === 'approve' ? '确认通过' : '确认拒绝' }}
        </text>
      </view>
      <view class="mt-4">
        <text class="text-sm text-gray-500">审批意见（选填）</text>
        <wd-textarea
          v-model="comment"
          placeholder="请输入审批意见"
          :maxlength="200"
          custom-style="margin-top: 8px;"
        />
      </view>
      <view class="mt-4 flex gap-3">
        <wd-button size="large" block @click="showActionPopup = false">取消</wd-button>
        <wd-button
          :type="actionType === 'approve' ? 'primary' : 'danger'"
          size="large"
          block
          :loading="submitting"
          @click="submitAction"
        >
          {{ actionType === 'approve' ? '确认通过' : '确认拒绝' }}
        </wd-button>
      </view>
      <view class="h-4" />
    </wd-popup>

    <view class="h-6" />
  </view>
</template>

<style lang="scss" scoped>
.card { background: #fff; border-radius: 12px; overflow: hidden; }
.card-qrcode-title { font-size: 13px; font-weight: 600; color: #999; padding: 12px 0 4px; letter-spacing: 0.5px; }
.card-title { font-size: 13px; font-weight: 600; color: #999; padding: 12px 16px 4px; letter-spacing: 0.5px; }
.card-body { padding: 4px 0; }
.info-row {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 16px; min-height: 40px;
}
.info-label { font-size: 14px; color: #999; flex-shrink: 0; }
.info-value { font-size: 14px; text-align: right; }
.visitor-block {
  margin: 8px 12px;
  padding: 12px;
  background: #f8f9ff;
  border-radius: 10px;
}
</style>
