<template>
  <div
    class="warehouse-dashboard"
    :style="{ width: `${style.width}px`, height: `${style.height}px`, transform: style.transform }"
  >
    <div class="dashboard-shell">
      <header class="dashboard-header">
        <div class="brand">
          <img class="brand-logo" :src="clientLogoUrl" alt="RENLY" />
        </div>
        <h1>仓库任务大屏</h1>
        <div class="time-block">
          <div class="current-time">{{ currentTime }}</div>
          <div class="refresh-time">数据刷新 {{ refreshTimeText }}</div>
        </div>
      </header>

      <main class="dashboard-content">
        <section class="kpi-row">
          <article v-for="item in summaryCards" :key="item.key" class="panel kpi-card" :class="item.className">
            <div class="kpi-title">{{ item.title }}</div>
            <div class="kpi-value">
              <span>{{ item.value }}</span>
              <small>{{ item.unit }}</small>
            </div>
            <div class="kpi-sub">{{ item.subText }}</div>
          </article>
        </section>

        <section class="chart-grid">
          <article class="panel chart-panel">
            <div class="panel-caption">入库任务结构</div>
            <div ref="inboundChartRef" class="chart-box"></div>
          </article>
          <article class="panel chart-panel">
            <div class="panel-caption">拣配任务结构</div>
            <div ref="pickingChartRef" class="chart-box"></div>
          </article>
          <article class="panel chart-panel">
            <div class="panel-caption">超期风险分布</div>
            <div ref="riskChartRef" class="chart-box"></div>
          </article>
          <article class="panel chart-panel">
            <div class="panel-caption">今日发货保障</div>
            <div ref="deliveryChartRef" class="chart-box"></div>
          </article>
        </section>

        <section class="detail-grid">
          <article v-for="table in detailTables" :key="table.key" class="panel table-panel">
            <div class="panel-caption">{{ table.title }}</div>
            <div class="table-wrap">
              <table>
                <thead>
                  <tr>
                    <th v-for="column in table.columns" :key="column.key" :style="{ width: column.width }">
                      {{ column.label }}
                    </th>
                  </tr>
                </thead>
                <tbody>
                  <tr v-if="table.rows.length === 0" class="empty-row">
                    <td :colspan="table.columns.length">暂无数据</td>
                  </tr>
                  <tr v-for="row in table.rows" :key="`${table.key}-${row._idx}`" :class="{ warning: row.urgentValue > 0 }">
                    <td v-for="column in table.columns" :key="column.key">
                      {{ row[column.key] }}
                    </td>
                  </tr>
                </tbody>
              </table>
            </div>
          </article>
        </section>
      </main>
    </div>
  </div>
</template>

<script setup>
import { computed, inject, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'
import * as echarts from 'echarts'

defineOptions({ name: 'WarehouseDashboard' })

const CreateData = inject('CreateData')
const ExecDatabase = inject('ExecDatabase')

const clientLogoUrl = './public/ClientLogo.png'
const now = ref(new Date())
const lastRefreshTime = ref('')
const style = reactive({
  width: 1920,
  height: 1080,
  transform: 'scaleY(1) scaleX(1) translate(-50%, -50%)'
})

let clockTimer = null
let refreshTimer = null
let resizeHandler = null
let scrollTimers = []
let inboundChart = null
let pickingChart = null
let riskChart = null
let deliveryChart = null

const inboundChartRef = ref(null)
const pickingChartRef = ref(null)
const riskChartRef = ref(null)
const deliveryChartRef = ref(null)

const summaryRows = ref([])
const inboundOverdueRows = ref([])
const outboundOverdueRows = ref([])
const todayDeliveryRows = ref([])
const inboundOffset = ref(0)
const outboundOffset = ref(0)
const deliveryOffset = ref(0)

const VISIBLE_ROWS = {
  inbound: 6,
  outbound: 6,
  delivery: 6
}

const inboundCategories = ['装配件', '机加件', '原材料', '外购件']
const pickingCategories = ['装配件', '机加件']
const riskCategories = ['超2天未入库', '超2天未上架', '超10天未出库', '汇报未清']

const currentTime = computed(() => formatDateTime(now.value))
const refreshTimeText = computed(() => lastRefreshTime.value || '--:--:--')

const summaryCards = computed(() => {
  const inboundTaskQty = sumRows('入库', 'quantity')
  const inboundUrgentQty = sumRows('入库', 'urgentQty')
  const inboundItemCount = sumRows('入库', 'taskCount')
  const pickingTaskQty = sumRows('拣配', 'quantity')
  const pickingUrgentQty = sumRows('拣配', 'urgentQty')
  const pickingItemCount = sumRows('拣配', 'taskCount')
  const overdueInbound = getSummaryRow('风险', '超2天未入库')
  const overdueInventory = getSummaryRow('风险', '超2天未上架')
  const overdueOutbound = getSummaryRow('风险', '超10天未出库')
  const reportOpen = getSummaryRow('风险', '汇报未清')
  const todayDelivery = getSummaryRow('发货', '今日应发')

  return [
    {
      key: 'inbound',
      title: '待入库任务',
      value: formatCompactNumber(inboundTaskQty),
      unit: '条',
      subText: `${formatAmount(inboundItemCount)} 项物料`,
      className: 'blue-card'
    },
    {
      key: 'inboundUrgent',
      title: '入库加急',
      value: formatCompactNumber(inboundUrgentQty),
      unit: '条',
      subText: `加急占比 ${formatPercent(inboundUrgentQty, inboundTaskQty)}`,
      className: 'red-card'
    },
    {
      key: 'overdueInbound',
      title: '超2天未入库',
      value: formatCompactNumber(overdueInbound.quantity),
      unit: '件',
      subText: `${formatAmount(overdueInbound.taskCount)} 条任务`,
      className: 'orange-card'
    },
    {
      key: 'overdueInventory',
      title: '超2天未上架',
      value: formatCompactNumber(overdueInventory.quantity),
      unit: '件',
      subText: `${formatAmount(overdueInventory.taskCount)} 条物料`,
      className: 'orange-card'
    },
    {
      key: 'picking',
      title: '未清拣配',
      value: formatCompactNumber(pickingTaskQty),
      unit: '条',
      subText: `${formatAmount(pickingItemCount)} 项物料`,
      className: 'blue-card'
    },
    {
      key: 'overdueOutbound',
      title: '超10天未出库',
      value: formatCompactNumber(overdueOutbound.quantity),
      unit: '件',
      subText: `${formatAmount(overdueOutbound.taskCount)} 条任务`,
      className: 'red-card'
    },
    {
      key: 'reportOpen',
      title: '汇报未清',
      value: formatCompactNumber(reportOpen.quantity),
      unit: '件',
      subText: `加急 ${formatCompactNumber(reportOpen.urgentQty)}`,
      className: 'red-card'
    },
    {
      key: 'todayDelivery',
      title: '今日应发',
      value: formatCompactNumber(todayDelivery.quantity),
      unit: '件',
      subText: `${formatAmount(todayDelivery.taskCount)} 单 / 加急 ${formatCompactNumber(todayDelivery.urgentQty)}`,
      className: todayDelivery.urgentQty > 0 ? 'red-card' : 'green-card'
    }
  ]
})

const inboundDisplayRows = computed(() => makeDisplayRows(inboundOverdueRows.value, inboundOffset.value, VISIBLE_ROWS.inbound))
const outboundDisplayRows = computed(() => makeDisplayRows(outboundOverdueRows.value, outboundOffset.value, VISIBLE_ROWS.outbound))
const deliveryDisplayRows = computed(() => makeDisplayRows(todayDeliveryRows.value, deliveryOffset.value, VISIBLE_ROWS.delivery))

const detailTables = computed(() => [
  {
    key: 'inbound',
    title: '超2天未入库明细',
    columns: inboundColumns,
    rows: inboundDisplayRows.value
  },
  {
    key: 'outbound',
    title: '超10天未出库明细',
    columns: outboundColumns,
    rows: outboundDisplayRows.value
  },
  {
    key: 'delivery',
    title: '今日应发明细',
    columns: deliveryColumns,
    rows: deliveryDisplayRows.value
  }
])

onMounted(() => {
  startClock()
  setScale()
  resizeHandler = () => {
    setScale()
    resizeCharts()
  }
  window.addEventListener('resize', resizeHandler)
  nextTick(() => {
    initCharts()
    searchDashboardData()
  })
  startAllScroll()
  refreshTimer = window.setInterval(searchDashboardData, 300000)
})

onUnmounted(() => {
  if (clockTimer) {
    window.clearInterval(clockTimer)
  }
  if (refreshTimer) {
    window.clearInterval(refreshTimer)
  }
  if (resizeHandler) {
    window.removeEventListener('resize', resizeHandler)
  }
  stopAllScroll()
  disposeCharts()
})

const startClock = () => {
  clockTimer = window.setInterval(() => {
    now.value = new Date()
  }, 1000)
}

const setScale = () => {
  const scale = getScale()
  style.transform = `scaleY(${scale.y}) scaleX(${scale.x}) translate(-50%, -50%)`
}

const getScale = () => ({
  x: window.innerWidth / style.width,
  y: window.innerHeight / style.height
})

const initCharts = () => {
  if (inboundChartRef.value && !inboundChart) {
    inboundChart = echarts.init(inboundChartRef.value)
  }
  if (pickingChartRef.value && !pickingChart) {
    pickingChart = echarts.init(pickingChartRef.value)
  }
  if (riskChartRef.value && !riskChart) {
    riskChart = echarts.init(riskChartRef.value)
  }
  if (deliveryChartRef.value && !deliveryChart) {
    deliveryChart = echarts.init(deliveryChartRef.value)
  }
  renderCharts()
}

const renderCharts = () => {
  if (!inboundChart || !pickingChart || !riskChart || !deliveryChart) {
    return
  }

  inboundChart.setOption(createInboundChartOption(), true)
  pickingChart.setOption(createPickingChartOption(), true)
  riskChart.setOption(createRiskChartOption(), true)
  deliveryChart.setOption(createDeliveryChartOption(), true)
  resizeCharts()
}

const resizeCharts = () => {
  ;[inboundChart, pickingChart, riskChart, deliveryChart].forEach(chart => {
    if (chart) {
      chart.resize()
    }
  })
}

const disposeCharts = () => {
  ;[inboundChart, pickingChart, riskChart, deliveryChart].forEach(chart => {
    if (chart) {
      chart.dispose()
    }
  })
  inboundChart = null
  pickingChart = null
  riskChart = null
  deliveryChart = null
}

const searchDashboardData = () => {
  const tasks = [
    queryDashboardData('仓库大屏_汇总查询', applySummaryData),
    queryDashboardData('仓库大屏_入库超期明细', applyInboundOverdueData),
    queryDashboardData('仓库大屏_出库超期明细', applyOutboundOverdueData),
    queryDashboardData('仓库大屏_今日应发明细', applyTodayDeliveryData)
  ]

  Promise.all(tasks).finally(() => {
    lastRefreshTime.value = formatTime(new Date())
    nextTick(renderCharts)
  })
}

const queryDashboardData = (procedureName, applyData) => {
  if (!CreateData || !ExecDatabase) {
    applyData([])
    return Promise.resolve([])
  }

  const data = CreateData('11', procedureName, [])
  return ExecDatabase(data)
    .then(response => {
      const rows = getResponseRows(response)
      applyData(rows)
      return rows
    })
    .catch(error => {
      console.error(`${procedureName} 查询失败:`, error)
      applyData([])
      return []
    })
}

const getResponseRows = response => {
  if (response && Array.isArray(response.data)) {
    return response.data
  }
  if (response && response.data && Array.isArray(response.data.rows)) {
    return response.data.rows
  }
  if (response && response.data && Array.isArray(response.data.result)) {
    return response.data.result
  }
  return []
}

const applySummaryData = rows => {
  summaryRows.value = rows.map(row => ({
    section: normalizeText(row.环节),
    category: normalizeText(row.分类),
    taskCount: toNumber(row.任务数),
    quantity: toNumber(row.数量),
    urgentQty: toNumber(row.加急数量),
    sort: toNumber(row.排序)
  }))
}

const applyInboundOverdueData = rows => {
  inboundOffset.value = 0
  inboundOverdueRows.value = rows.map((row, index) => ({
    _idx: index + 1,
    docDate: formatDateValue(row.单据日期),
    orderNo: normalizeText(row.订单号),
    itemCode: normalizeText(row.物料编码),
    itemName: normalizeText(row.物料名称),
    quantity: formatAmount(row.未入库数量, 2),
    urgentQty: formatAmount(row.加急数量, 2),
    urgentValue: toNumber(row.加急数量)
  }))
}

const applyOutboundOverdueData = rows => {
  outboundOffset.value = 0
  outboundOverdueRows.value = rows.map((row, index) => ({
    _idx: index + 1,
    pickNo: normalizeText(row.拣配清单号),
    pickDate: formatDateValue(row.拣配日期),
    productionOrder: normalizeText(row.生产订单号),
    itemCode: normalizeText(row.物料编码),
    itemName: normalizeText(row.物料描述),
    planQty: formatAmount(row.计划数量, 2),
    urgentQty: formatAmount(row.加急数量, 2),
    urgentValue: toNumber(row.加急数量)
  }))
}

const applyTodayDeliveryData = rows => {
  deliveryOffset.value = 0
  todayDeliveryRows.value = rows.map((row, index) => ({
    _idx: index + 1,
    deliveryDate: formatDateValue(row.发货日期),
    customer: normalizeText(row.客户),
    externalNo: normalizeText(row.外部单号 || row.订单号),
    itemCode: normalizeText(row.物料编码),
    itemName: normalizeText(row.物料名称),
    openQty: formatAmount(row.未清数量, 2),
    urgentQty: formatAmount(row.加急数量, 2),
    urgentValue: toNumber(row.加急数量)
  }))
}

const startAllScroll = () => {
  startScrollWhenOverflow(inboundOverdueRows, inboundOffset, VISIBLE_ROWS.inbound)
  startScrollWhenOverflow(outboundOverdueRows, outboundOffset, VISIBLE_ROWS.outbound)
  startScrollWhenOverflow(todayDeliveryRows, deliveryOffset, VISIBLE_ROWS.delivery)
}

const startScrollWhenOverflow = (rowsRef, offsetRef, visibleCount) => {
  const timerId = window.setInterval(() => {
    if (rowsRef.value.length <= visibleCount) {
      offsetRef.value = 0
      return
    }

    const maxOffset = rowsRef.value.length - visibleCount
    offsetRef.value = offsetRef.value >= maxOffset ? 0 : offsetRef.value + 1
  }, 2600)
  scrollTimers.push(timerId)
}

const stopAllScroll = () => {
  scrollTimers.forEach(timerId => window.clearInterval(timerId))
  scrollTimers = []
}

const makeDisplayRows = (rows, offset, visibleCount) => {
  if (!Array.isArray(rows) || rows.length === 0) {
    return []
  }

  const maxOffset = Math.max(rows.length - visibleCount, 0)
  const safeOffset = Math.min(Math.max(offset, 0), maxOffset)
  return rows.slice(safeOffset, safeOffset + visibleCount).map((row, index) => ({
    ...row,
    _idx: safeOffset + index + 1
  }))
}

const createInboundChartOption = () => createBarOption({
  categories: inboundCategories,
  totalName: '未入库任务',
  urgentName: '加急任务',
  totalData: inboundCategories.map(category => getSummaryRow('入库', category).quantity),
  urgentData: inboundCategories.map(category => getSummaryRow('入库', category).urgentQty)
})

const createPickingChartOption = () => createBarOption({
  categories: pickingCategories,
  totalName: '未清拣配',
  urgentName: '加急拣配',
  totalData: pickingCategories.map(category => getSummaryRow('拣配', category).quantity),
  urgentData: pickingCategories.map(category => getSummaryRow('拣配', category).urgentQty)
})

const createBarOption = ({ categories, totalName, urgentName, totalData, urgentData }) => ({
  color: ['#35c5ff', '#ff3345'],
  tooltip: {
    trigger: 'axis',
    axisPointer: { type: 'shadow' },
    backgroundColor: 'rgba(3, 16, 48, 0.94)',
    borderColor: 'rgba(43, 216, 255, 0.55)',
    textStyle: { color: '#f3f9ff' },
    valueFormatter: value => formatAmount(value, 2)
  },
  legend: {
    top: 0,
    right: 18,
    itemWidth: 14,
    itemHeight: 10,
    textStyle: { color: '#bde9ff', fontSize: 13 }
  },
  grid: {
    left: 58,
    right: 24,
    top: 48,
    bottom: 38
  },
  xAxis: {
    type: 'category',
    data: categories,
    axisLine: { lineStyle: { color: 'rgba(141, 211, 255, 0.6)' } },
    axisTick: { show: false },
    axisLabel: { color: '#d8f3ff', fontSize: 15 }
  },
  yAxis: {
    type: 'value',
    splitLine: { lineStyle: { color: 'rgba(95, 172, 231, 0.18)' } },
    axisLabel: {
      color: '#9bdcff',
      formatter: value => formatCompactNumber(value)
    }
  },
  series: [
    {
      name: totalName,
      type: 'bar',
      barWidth: 28,
      data: totalData,
      itemStyle: {
        borderRadius: [3, 3, 0, 0]
      }
    },
    {
      name: urgentName,
      type: 'bar',
      barWidth: 28,
      data: urgentData,
      itemStyle: {
        borderRadius: [3, 3, 0, 0]
      }
    }
  ]
})

const createRiskChartOption = () => {
  const data = riskCategories.map(category => {
    const row = getSummaryRow('风险', category)
    return {
      name: category,
      value: row.taskCount,
      quantity: row.quantity,
      urgentQty: row.urgentQty
    }
  })

  return {
    color: ['#ffb13b', '#e5602c', '#ff3040', '#8f6bff'],
    tooltip: {
      trigger: 'item',
      backgroundColor: 'rgba(3, 16, 48, 0.94)',
      borderColor: 'rgba(43, 216, 255, 0.55)',
      textStyle: { color: '#f3f9ff' },
      formatter: params => {
        const item = params.data
        return `${params.name}<br/>任务数：${formatAmount(item.value)}<br/>数量：${formatAmount(item.quantity, 2)}<br/>加急：${formatAmount(item.urgentQty, 2)}`
      }
    },
    legend: {
      orient: 'vertical',
      right: 24,
      top: 46,
      itemWidth: 14,
      itemHeight: 10,
      textStyle: { color: '#bde9ff', fontSize: 13 }
    },
    series: [
      {
        name: '风险任务',
        type: 'pie',
        radius: ['44%', '70%'],
        center: ['39%', '54%'],
        roseType: 'radius',
        avoidLabelOverlap: true,
        label: {
          color: '#f3f9ff',
          fontSize: 13,
          formatter: '{b}\n{c}条'
        },
        labelLine: {
          lineStyle: { color: 'rgba(189, 233, 255, 0.68)' }
        },
        data
      }
    ]
  }
}

const createDeliveryChartOption = () => {
  const today = getSummaryRow('发货', '今日应发')
  const open = getSummaryRow('发货', '销售交货未清')
  const categories = ['今日应发', '今日加急', '销售未清', '销售加急']
  const data = [today.quantity, today.urgentQty, open.quantity, open.urgentQty]
  const colors = ['#18d36f', '#ff3040', '#35c5ff', '#ff6a37']

  return {
    color: colors,
    tooltip: {
      trigger: 'axis',
      axisPointer: { type: 'shadow' },
      backgroundColor: 'rgba(3, 16, 48, 0.94)',
      borderColor: 'rgba(43, 216, 255, 0.55)',
      textStyle: { color: '#f3f9ff' },
      valueFormatter: value => formatAmount(value, 2)
    },
    grid: {
      left: 58,
      right: 24,
      top: 34,
      bottom: 42
    },
    xAxis: {
      type: 'category',
      data: categories,
      axisLine: { lineStyle: { color: 'rgba(141, 211, 255, 0.6)' } },
      axisTick: { show: false },
      axisLabel: { color: '#d8f3ff', fontSize: 14 }
    },
    yAxis: {
      type: 'value',
      splitLine: { lineStyle: { color: 'rgba(95, 172, 231, 0.18)' } },
      axisLabel: {
        color: '#9bdcff',
        formatter: value => formatCompactNumber(value)
      }
    },
    series: [
      {
        name: '数量',
        type: 'bar',
        barWidth: 32,
        data,
        itemStyle: {
          borderRadius: [3, 3, 0, 0],
          color: params => colors[params.dataIndex]
        },
        label: {
          show: true,
          position: 'top',
          color: '#f3f9ff',
          fontSize: 13,
          formatter: params => formatCompactNumber(params.value)
        }
      }
    ]
  }
}

const getSummaryRow = (section, category) => {
  return summaryRows.value.find(row => row.section === section && row.category === category) || {
    section,
    category,
    taskCount: 0,
    quantity: 0,
    urgentQty: 0,
    sort: 0
  }
}

const sumRows = (section, key) => {
  return summaryRows.value
    .filter(row => row.section === section)
    .reduce((total, row) => total + toNumber(row[key]), 0)
}

const pad = value => String(value).padStart(2, '0')

const formatDateTime = value => {
  const date = value instanceof Date ? value : new Date(value)
  if (Number.isNaN(date.getTime())) {
    return ''
  }
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${formatTime(date)}`
}

const formatTime = value => {
  const date = value instanceof Date ? value : new Date(value)
  if (Number.isNaN(date.getTime())) {
    return ''
  }
  return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
}

const formatDateValue = value => {
  if (!value) {
    return ''
  }
  if (value instanceof Date) {
    return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`
  }
  return String(value).split('T')[0].split(' ')[0]
}

const formatAmount = (value, maximumFractionDigits = 0) => {
  const number = toNumber(value)
  return new Intl.NumberFormat('zh-CN', {
    maximumFractionDigits
  }).format(number)
}

const formatCompactNumber = value => {
  const number = toNumber(value)
  const absolute = Math.abs(number)

  if (absolute >= 100000000) {
    return `${trimZero((number / 100000000).toFixed(2))}亿`
  }
  if (absolute >= 10000) {
    return `${trimZero((number / 10000).toFixed(2))}万`
  }
  return formatAmount(number, Number.isInteger(number) ? 0 : 2)
}

const formatPercent = (part, total) => {
  const totalNumber = toNumber(total)
  if (totalNumber <= 0) {
    return '0%'
  }
  return `${trimZero(((toNumber(part) * 100) / totalNumber).toFixed(1))}%`
}

const trimZero = value => String(value).replace(/\.0+$/, '').replace(/(\.\d*?)0+$/, '$1')

const toNumber = value => {
  if (value === null || value === undefined || value === '') {
    return 0
  }
  const number = Number(String(value).replace(/,/g, ''))
  return Number.isNaN(number) ? 0 : number
}

const normalizeText = value => {
  if (value === null || value === undefined) {
    return ''
  }
  return String(value).trim()
}

const inboundColumns = [
  { label: '单据日期', key: 'docDate', width: '78px' },
  { label: '订单号', key: 'orderNo', width: '66px' },
  { label: '物料编码', key: 'itemCode', width: '150px' },
  { label: '物料名称', key: 'itemName', width: '178px' },
  { label: '未入库', key: 'quantity', width: '70px' },
  { label: '加急', key: 'urgentQty', width: '58px' }
]

const outboundColumns = [
  { label: '拣配单', key: 'pickNo', width: '66px' },
  { label: '日期', key: 'pickDate', width: '76px' },
  { label: '生产订单', key: 'productionOrder', width: '70px' },
  { label: '物料编码', key: 'itemCode', width: '148px' },
  { label: '物料描述', key: 'itemName', width: '178px' },
  { label: '计划', key: 'planQty', width: '58px' },
  { label: '加急', key: 'urgentQty', width: '58px' }
]

const deliveryColumns = [
  { label: '发货日期', key: 'deliveryDate', width: '78px' },
  { label: '客户', key: 'customer', width: '128px' },
  { label: '订单号', key: 'externalNo', width: '82px' },
  { label: '物料编码', key: 'itemCode', width: '150px' },
  { label: '物料名称', key: 'itemName', width: '154px' },
  { label: '未清', key: 'openQty', width: '58px' },
  { label: '加急', key: 'urgentQty', width: '58px' }
]
</script>

<style scoped lang="scss">
* {
  box-sizing: border-box;
}

.warehouse-dashboard {
  position: fixed;
  top: 50%;
  left: 50%;
  width: 1920px;
  height: 1080px;
  overflow: hidden;
  color: #f3f9ff;
  font-family: 'Microsoft YaHei', Arial, sans-serif;
  background:
    radial-gradient(circle at 18% 16%, rgba(13, 87, 122, 0.34), transparent 28%),
    radial-gradient(circle at 78% 72%, rgba(86, 30, 55, 0.22), transparent 30%),
    linear-gradient(180deg, #07001f 0%, #07132d 54%, #071628 100%);
  transform-origin: 0 0;
  transition: 0.3s;
}

.dashboard-shell {
  width: 100%;
  height: 100%;
  padding: 0 15px 15px;
}

.dashboard-header {
  display: grid;
  grid-template-columns: 336px 1fr 442px;
  align-items: center;
  height: 95px;
  border-bottom: 1px solid rgba(21, 162, 240, 0.7);
}

.brand {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  width: 300px;
  height: 72px;
  overflow: visible;
}

.brand-logo {
  width: 224px;
  height: 58px;
  padding: 5px 10px;
  object-fit: contain;
  object-position: left center;
  background: #ffffff;
  border-radius: 6px;
  filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.28));
}

.dashboard-header h1 {
  margin: 0;
  color: #ffffff;
  font-size: 55px;
  font-weight: 900;
  letter-spacing: 0;
  text-align: center;
}

.time-block {
  display: grid;
  gap: 6px;
  padding-right: 40px;
  text-align: right;
}

.current-time {
  color: #eef7ff;
  font-size: 27px;
}

.refresh-time {
  color: #89d4ff;
  font-size: 17px;
}

.dashboard-content {
  display: grid;
  grid-template-rows: 128px 480px 1fr;
  gap: 15px;
  height: calc(100% - 95px);
  padding-top: 10px;
}

.kpi-row {
  display: grid;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  gap: 15px;
  min-height: 0;
}

.chart-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: repeat(2, minmax(0, 1fr));
  gap: 15px;
  min-height: 0;
}

.detail-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 15px;
  min-height: 0;
}

.panel {
  position: relative;
  min-width: 0;
  min-height: 0;
  border: 1px solid rgba(29, 153, 229, 0.78);
  background: rgba(3, 16, 48, 0.48);
  box-shadow:
    inset 0 0 18px rgba(27, 130, 214, 0.24),
    0 0 10px rgba(1, 132, 220, 0.18);
}

.panel::before {
  position: absolute;
  inset: -1px;
  content: '';
  pointer-events: none;
  background:
    linear-gradient(90deg, #2bd8ff 0 14px, transparent 14px) left top / 14px 2px no-repeat,
    linear-gradient(180deg, #2bd8ff 0 14px, transparent 14px) left top / 2px 14px no-repeat,
    linear-gradient(270deg, #2bd8ff 0 14px, transparent 14px) right top / 14px 2px no-repeat,
    linear-gradient(180deg, #2bd8ff 0 14px, transparent 14px) right top / 2px 14px no-repeat,
    linear-gradient(90deg, #2bd8ff 0 14px, transparent 14px) left bottom / 14px 2px no-repeat,
    linear-gradient(0deg, #2bd8ff 0 14px, transparent 14px) left bottom / 2px 14px no-repeat,
    linear-gradient(270deg, #2bd8ff 0 14px, transparent 14px) right bottom / 14px 2px no-repeat,
    linear-gradient(0deg, #2bd8ff 0 14px, transparent 14px) right bottom / 2px 14px no-repeat;
}

.panel::after {
  position: absolute;
  inset: 0;
  content: '';
  pointer-events: none;
  box-shadow: inset 0 0 12px rgba(43, 216, 255, 0.12);
}

.kpi-card {
  display: grid;
  grid-template-rows: 28px minmax(0, 1fr) 24px;
  align-items: center;
  min-width: 0;
  padding: 12px 14px 10px;
}

.kpi-title {
  overflow: hidden;
  color: #95ddff;
  font-size: 19px;
  font-weight: 700;
  line-height: 28px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.kpi-value {
  display: flex;
  align-items: baseline;
  justify-content: center;
  min-width: 0;
  color: #ffffff;
  font-weight: 900;
  line-height: 1;
}

.kpi-value span {
  overflow: hidden;
  max-width: 150px;
  font-size: 42px;
  text-align: center;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.kpi-value small {
  margin-left: 6px;
  color: #ffffff;
  font-size: 18px;
  font-weight: 700;
  white-space: nowrap;
}

.kpi-sub {
  overflow: hidden;
  color: #c8eaff;
  font-size: 15px;
  line-height: 22px;
  text-align: center;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.blue-card .kpi-value span {
  color: #36c8ff;
}

.green-card .kpi-value span {
  color: #18d36f;
}

.red-card .kpi-value span {
  color: #ff3040;
}

.orange-card .kpi-value span {
  color: #ffb13b;
}

.chart-panel,
.table-panel {
  display: flex;
  flex-direction: column;
}

.panel-caption {
  height: 38px;
  color: #8cd3ff;
  font-size: 26px;
  font-weight: 700;
  line-height: 38px;
  text-align: center;
}

.chart-box {
  position: relative;
  z-index: 1;
  flex: 1;
  min-height: 0;
}

.table-wrap {
  position: relative;
  z-index: 1;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  padding: 0 8px 9px;
}

table {
  width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
}

th,
td {
  border: 1px solid rgba(53, 149, 217, 0.54);
  color: #f1f8ff;
  line-height: 1.18;
  text-align: center;
  vertical-align: middle;
  word-break: break-all;
  background: rgba(4, 24, 57, 0.5);
}

th {
  height: 36px;
  color: #bde9ff;
  font-size: 14px;
  font-weight: 600;
  background: rgba(12, 68, 119, 0.34);
}

td {
  height: 38px;
  font-size: 13px;
}

.warning td {
  color: #ff4949;
  font-weight: 700;
}

.empty-row td {
  height: 228px;
  color: #89d4ff;
  font-size: 18px;
}
</style>
