<!-- eslint-disable vue/max-attributes-per-line -->
<template>
  <div
    id="screen"
    class="qa-screen"
    :style="{
      width: `${style.width}px`,
      height: `${style.height}px`,
      transform: style.transform
    }"
  >
    <div class="title-png">
      <div class="left-control-bar">
        <img class="brand-logo" src="/png/logo.png" alt="庆安" />
        <div class="mode-button-group">
          <button type="button" class="mode-button" :class="{ 'mode-button--active': currentMode === 'DT' }" @click="changeDtMode">孪生模式</button>
          <button type="button" class="mode-button" :class="{ 'mode-button--active': currentMode === 'SM' }" @click="changeSmMode">仿真模式</button>
        </div>
      </div>
      <span class="title-text">{{ titleText }}</span>
      <div class="right-control-bar">
        <div class="df-header__timebar">
          <span class="df-header__year">{{ headerDateCn }}</span>
          <span class="df-header__time">{{ headerTime }}</span>
        </div>
        <div class="view-switch">
          <button
            v-for="item in viewButtons"
            :key="item.value"
            type="button"
            class="view-switch__btn"
            :class="{ 'view-switch__btn--active': activeView === item.value }"
            @click="changeView(item.value)"
          >
            {{ item.label }}
          </button>
        </div>
      </div>
    </div>
    <div v-if="simulationRateDialogVisible" class="simulation-rate-mask" @click.self="cancelSimulationRateDialog">
      <div class="simulation-rate-dialog">
        <div class="simulation-rate-dialog__head">
          <strong>选择仿真播放倍率</strong>
          <span>默认 5 倍</span>
        </div>
        <div class="simulation-rate-options">
          <button
            v-for="rate in autoPlayRates"
            :key="rate"
            type="button"
            class="simulation-rate-option"
            :class="{ 'simulation-rate-option--active': pendingAutoPlayRate === rate }"
            @click="selectSimulationRate(rate)"
          >
            {{ rate }}x
          </button>
        </div>
        <div class="simulation-rate-dialog__actions">
          <button type="button" class="simulation-rate-action simulation-rate-action--ghost" @click="cancelSimulationRateDialog">取消</button>
          <button type="button" class="simulation-rate-action simulation-rate-action--primary" @click="confirmSimulationRate">开始仿真</button>
        </div>
      </div>
    </div>
    <div class="leftLine"></div>
    <div class="rightLine"></div>

    <div class="main-content">
      <iframe id="dmtIframe" v-if="mode === 'DT' && srcDt" allowtransparency="true" class="model-bg-frame" :src="srcDt" title="数字孪生模型" @load="handleIframeLoad"></iframe>
      <iframe id="dmtIframe" v-if="mode === 'SM' && srcSm" allowtransparency="true" class="model-bg-frame" :src="srcSm" title="仿真模型" @load="handleIframeLoad"></iframe>
      <div v-if="!mode || (!srcDt && !srcSm)" class="model-placeholder">等待配置三维模型地址</div>

      <div class="left-box">
        <section class="qa-panel qa-panel--combined">
          <div class="left-title-header">
            <span class="left-title-text">线体任务联动状态</span>
          </div>
          <div class="qa-panel-body line-monitor-body">
            <div class="line-monitor-scroll-box">
              <scroll class="line-monitor-scroll" :data="lineMonitorScrollList" :class-option="lineMonitorScrollOption">
                <div class="line-monitor-content">
                  <div v-for="(row, index) in lineMonitorScrollList" :key="row.name + index" class="monitor-card">
                    <div class="monitor-head">
                      <div class="monitor-title">
                        <strong>{{ row.name }}</strong>
                        <span>{{ row.taskNo }}</span>
                      </div>
                      <span class="line-status" :class="`line-status--${row.type}`">{{ row.status }}</span>
                    </div>

                    <div class="task-summary">
                      <div class="task-route">
                        <span>起止位置</span>
                        <strong>{{ row.startPos }} -> {{ row.endPos }}</strong>
                      </div>
                      <div v-for="item in row.taskInfo" :key="item.label" class="task-field">
                        <span>{{ item.label }}</span>
                        <strong>{{ item.value }}</strong>
                      </div>
                    </div>

                    <div class="signal-section">
                      <span class="signal-section__title">线体状态</span>
                      <div class="signal-grid signal-grid--line">
                        <div v-for="signal in row.lineSignals" :key="signal.label" class="signal-chip" :class="`signal-chip--${signal.state}`">
                          <i></i>
                          <span>{{ signal.label }}</span>
                        </div>
                      </div>
                    </div>

                    <div class="signal-section">
                      <span class="signal-section__title">任务握手</span>
                      <div class="signal-grid signal-grid--handshake">
                        <div v-for="signal in row.handshakes" :key="signal.label" class="signal-chip" :class="`signal-chip--${signal.state}`">
                          <i></i>
                          <span>{{ signal.label }}</span>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </scroll>
            </div>
          </div>
        </section>
      </div>

      <div class="right-box">
        <section class="qa-panel">
          <div class="right-title-header">
            <span class="right-title-text">加工设备状态</span>
          </div>
          <div class="qa-panel-body equipment-body">
            <div class="equipment-head">
              <span>设备</span>
              <span>产量</span>
              <span>联动信号</span>
              <span>状态</span>
            </div>
            <div class="seamless-box">
              <scroll class="seamless-scroll" :data="equipmentScrollList" :class-option="equipmentScrollOption">
                <div class="seamless-content">
                  <div
                    v-for="(item, index) in equipmentScrollList"
                    :key="item.name + index"
                    class="equipment-row"
                    :class="{ 'equipment-row--alarm': item.type === 'alarm' }"
                  >
                    <strong class="equipment-name">{{ item.name }}</strong>
                    <span class="equipment-count">{{ item.actual }}</span>
                    <div class="equipment-tags">
                      <span v-for="tag in item.tags" :key="tag.text" class="status-pill status-pill--compact" :class="`status-pill--${tag.type}`">
                        {{ tag.text }}
                      </span>
                    </div>
                    <span class="status-pill equipment-status" :class="`status-pill--${item.type}`">{{ item.status }}</span>
                  </div>
                </div>
              </scroll>
            </div>
          </div>
        </section>

        <section class="qa-panel">
          <div class="right-title-header">
            <span class="right-title-text">辅助设备状态</span>
          </div>
          <div class="qa-panel-body assist-body">
            <div class="assist-head">
              <span>设备</span>
              <span>联动信号</span>
              <span>状态</span>
            </div>
            <div class="assist-list">
              <div v-for="item in assistList" :key="item.name" class="assist-status-row">
                <strong class="assist-name">{{ item.name }}</strong>
                <div class="assist-tags">
                  <span v-for="tag in item.tags" :key="tag.text" class="status-pill status-pill--compact" :class="`status-pill--${tag.type}`">
                    {{ tag.text }}
                  </span>
                </div>
                <span class="status-pill assist-status" :class="`status-pill--${item.type}`">{{ item.status }}</span>
              </div>
            </div>
          </div>
        </section>
      </div>

      <div class="bottom-box">
        <section class="qa-panel">
          <div class="center-title-header">
            <span>物料库位矩阵</span>
          </div>
          <div class="qa-panel-body qa-panel-body--storage">
            <div class="storage-groups">
              <div v-for="group in storageGroups" :key="group.name" class="storage-card">
                <div class="storage-head">
                  <strong>{{ group.name }}</strong>
                  <span>{{ group.filled }} / {{ group.total }}</span>
                </div>
                <div class="slots">
                  <span v-for="slot in group.slots" :key="slot.name" class="slot" :class="{ 'slot--filled': slot.filled }">
                    {{ slot.name }}
                  </span>
                </div>
              </div>
            </div>
          </div>
        </section>

        <section class="qa-panel">
          <div class="center-title-header">
            <span>报警与异常事件</span>
          </div>
          <div class="qa-panel-body event-body">
            <div ref="alarmPieChart" class="alarm-bar-chart"></div>
            <div class="event-head">
              <span>时间</span>
              <span>设备</span>
              <span>事件内容</span>
            </div>
            <div class="seamless-box">
              <scroll class="seamless-scroll" :data="alarmScrollList" :class-option="alarmScrollOption">
                <div class="seamless-content">
                  <div v-for="(event, index) in alarmScrollList" :key="event.time + event.device + index" class="event-row">
                    <span>{{ event.time }}</span>
                    <strong>{{ event.device }}</strong>
                    <p>{{ event.message }}</p>
                  </div>
                </div>
              </scroll>
            </div>
          </div>
        </section>

        <section class="qa-panel">
          <div class="center-title-header">
            <span>设备运行履历</span>
          </div>
          <div class="qa-panel-body history-body">
            <div class="history-legend">
              <span v-for="item in historyLegend" :key="item.label" class="history-legend-item">
                <i :class="`history-legend-dot history-legend-dot--${item.type}`"></i>
                {{ item.label }}
              </span>
            </div>
            <div class="history-head">
              <span>设备</span>
              <div class="history-scale">
                <span v-for="tick in historyTicks" :key="tick">{{ tick }}</span>
              </div>
            </div>
            <div class="seamless-box">
              <scroll class="seamless-scroll" :data="deviceHistoryScrollList" :class-option="historyScrollOption">
                <div class="seamless-content">
                  <div v-for="(item, index) in deviceHistoryScrollList" :key="item.name + index" class="history-row">
                    <div class="history-device">
                      <strong>{{ item.name }}</strong>
                    </div>
                    <div class="history-track">
                      <span
                        v-for="segment in item.segments"
                        :key="segment.label + segment.start"
                        class="history-segment"
                        :class="`history-segment--${segment.type}`"
                        :style="getHistorySegmentStyle(segment)"
                      ></span>
                    </div>
                  </div>
                </div>
              </scroll>
            </div>
          </div>
        </section>
      </div>
    </div>
  </div>
</template>

<script>
import * as echarts from 'echarts'
import scroll from 'vue-seamless-scroll'

export default {
  name: 'QingAn',
  components: { scroll },
  data() {
    return {
      style: {
        width: 1920,
        height: 1080,
        transform: 'scaleY(1) scaleX(1) translate(-50%, -50%)'
      },
      titleText: '',
      headerDateCn: '',
      headerTime: '',
      iframeAddress: '',
      mode: 'DT',
      currentMode: 'DT',
      srcDt: 'http://localhost:10056/',
      srcSm: 'http://localhost:10059/',
      smIframeBase: 'http://localhost:10059/',
      autoPlayCraftCode: '',
      autoPlayRates: [0.5, 1, 2, 4, 5, 10],
      selectedAutoPlayRate: 5,
      pendingAutoPlayRate: 5,
      simulationRateDialogVisible: false,
      modeSwitchTimer: null,
      cameraSyncTimer: null,
      iframeReadyTimer: null,
      dmtIframeReady: false,
      lineMonitorTimer: null,
      lineMonitorRequesting: false,
      clockTimer: null,
      resizeHandler: null,
      alarmPieChart: null,
      activeView: 'overview',
      viewButtons: [
        { label: '全揽视角', value: 'overview' },
        { label: '加工中心单元视角', value: 'longRail' },
        { label: '车床加工单元视角', value: 'shortRail' },
        { label: '插齿机单元视角', value: 'joint' },
        { label: '磨床加工单元视角', value: 'grinder' }
      ],
      cameraViews: {
        longRail: {
          target: { x: -7.242748313927699, y: 1.365235845234568, z: -6.193333867630938 },
          position: { x: -20.270149575282687, y: 1.4436895348793037, z: 14.38433346048873 },
          zoom0: 1
        },
        shortRail: {
          target: { x: -7.001588883043185, y: 16.262345352657395, z: -6.097455776264499 },
          position: { x: -18.758818521416032, y: 16.33314980756182, z: 12.473888987363225 },
          zoom0: 1
        },
        joint: {
          target: { x: 1.9482734060133915, y: 15.747678960743341, z: -0.429474906955281 },
          position: { x: -5.461713633567803, y: 15.792303426652754, z: 11.275104168113232 },
          zoom0: 1
        },
        grinder: {
          target: { x: -10.691421929972085, y: -15.525894683054275, z: 1.3522782143982923 },
          position: { x: -21.99709567151696, y: -15.576998082436841, z: 11.73450668840891 },
          zoom0: 1
        },
        overview: {
          target: { x: -4.793874852627955, y: 6.052339186049304, z: -6.403726952169199 },
          position: { x: -26.23140973687205, y: 33.78061698752333, z: 24.44556028867285 },
          zoom0: 1
        }
      },
      lineMonitorRows: [
        {
          unitKey: 'longRail',
          name: '加工中心单元',
          status: '执行中',
          type: 'on',
          taskNo: 'TASK 1001',
          startPos: '01',
          endPos: '06',
          taskInfo: [
            { label: '配方号', value: 'RCP-01' },
            { label: '托盘', value: 'Pallet-A' }
          ],
          lineSignals: [
            { label: 'PCS运行', state: 'on' },
            { label: '允许接收', state: 'on' },
            { label: '自动模式', state: 'on' },
            { label: '急停', state: 'off' }
          ],
          handshakes: [
            { label: '启动', state: 'on' },
            { label: '准备好', state: 'on' },
            { label: '忙碌', state: 'on' },
            { label: '错误', state: 'off' },
            { label: '完成', state: 'off' }
          ]
        },
        {
          unitKey: 'shortRail',
          name: '车床加工单元',
          status: '准备就绪',
          type: 'ok',
          taskNo: 'TASK 1002',
          startPos: '03',
          endPos: '09',
          taskInfo: [
            { label: '配方号', value: 'RCP-09' },
            { label: '托盘', value: 'Pallet-C' }
          ],
          lineSignals: [
            { label: 'PCS运行', state: 'on' },
            { label: '允许接收', state: 'on' },
            { label: '自动模式', state: 'on' },
            { label: '急停', state: 'off' }
          ],
          handshakes: [
            { label: '启动', state: 'off' },
            { label: '准备好', state: 'on' },
            { label: '忙碌', state: 'off' },
            { label: '错误', state: 'off' },
            { label: '完成', state: 'off' }
          ]
        },
        {
          unitKey: 'joint',
          name: '插齿机单元',
          status: '异常中断',
          type: 'alarm',
          taskNo: 'TASK 1003',
          startPos: '02',
          endPos: '04',
          taskInfo: [
            { label: '配方号', value: 'RCP-11' },
            { label: '托盘', value: 'Pallet-B' }
          ],
          lineSignals: [
            { label: 'PCS运行', state: 'on' },
            { label: '允许接收', state: 'off' },
            { label: '自动模式', state: 'warn' },
            { label: '急停', state: 'off' }
          ],
          handshakes: [
            { label: '启动', state: 'on' },
            { label: '准备好', state: 'on' },
            { label: '忙碌', state: 'alarm' },
            { label: '错误', state: 'alarm' },
            { label: '完成', state: 'off' }
          ]
        },
        {
          unitKey: 'grinder',
          name: '磨床加工单元',
          status: '预制待命',
          type: 'ok',
          taskNo: 'TASK 1004',
          startPos: '05',
          endPos: '10',
          taskInfo: [
            { label: '配方号', value: 'RCP-18' },
            { label: '托盘', value: 'Pallet-D' }
          ],
          lineSignals: [
            { label: 'PCS运行', state: 'on' },
            { label: '允许接收', state: 'on' },
            { label: '自动模式', state: 'warn' },
            { label: '急停', state: 'off' }
          ],
          handshakes: [
            { label: '启动', state: 'off' },
            { label: '准备好', state: 'on' },
            { label: '忙碌', state: 'off' },
            { label: '错误', state: 'off' },
            { label: '完成', state: 'on' }
          ]
        }
      ],
      equipmentList: [
        { name: 'F500', target: '1202 件', actual: '1202 件', status: '运行中', type: 'on', tags: [{ text: '上料完成', type: 'ok' }, { text: '下料完成', type: 'ok' }] },
        { name: 'L2000LSY', target: '2206 件', actual: '2206 件', status: '待机', type: 'idle', tags: [{ text: '上料完成', type: 'ok' }, { text: '下料完成', type: 'ok' }] },
        { name: 'L2100SY', target: '3301 件', actual: '3298 件', status: '运行中', type: 'on', tags: [{ text: '上料完成', type: 'ok' }, { text: '下料完成', type: 'ok' }] },
        { name: '新日', target: '4108 件', actual: '4108 件', status: '运行中', type: 'on', tags: [{ text: '上料就绪', type: 'ok' }, { text: '下料就绪', type: 'ok' }] },
        { name: 'MAZAK', target: '-', actual: '-', status: '报警', type: 'alarm', tags: [{ text: '上料就绪', type: 'ok' }, { text: '下料就绪', type: 'ok' }] },
        { name: '磨床单元', target: '860 件', actual: '856 件', status: '待机', type: 'idle', tags: [{ text: '上料就绪', type: 'ok' }, { text: '砂轮就绪', type: 'ok' }] },
        { name: 'YKD5132H', target: '5104 件', actual: '5104 件', status: '待机', type: 'idle', tags: [{ text: '上料就绪', type: 'ok' }, { text: '下料就绪', type: 'ok' }] }
      ],
      assistList: [
        { name: '加工中心单元打标机', status: '待机', type: 'idle', tags: [{ text: '待机中', type: 'idle' }] },
        { name: '车床加工单元打标机', status: '打标中', type: 'on', tags: [{ text: '开机中', type: 'ok' }, { text: '打标中', type: 'on' }] },
        { name: '插齿机单元打标机', status: '待机', type: 'idle', tags: [{ text: '开机中', type: 'ok' }, { text: '待机中', type: 'idle' }] },
        { name: '清洗机', status: '运行中', type: 'on', tags: [{ text: '上料就绪', type: 'ok' }, { text: '运行中', type: 'on' }] },
        { name: '测量仪', status: '报警', type: 'alarm', tags: [{ text: '错误', type: 'alarm' }, { text: '下料就绪', type: 'ok' }] }
      ],
      storageGroups: [
        {
          name: '加工中心单元库区',
          total: 9,
          filled: 6,
          slots: this.createSlots(['S1', 'S2', 'S4', 'S6', 'S7', 'S9'], 9)
        },
        {
          name: '车床加工单元库区',
          total: 9,
          filled: 6,
          slots: this.createSlots(['S1', 'S3', 'S4', 'S6', 'S8', 'S9'], 9)
        },
        {
          name: '插齿机单元库区',
          total: 6,
          filled: 4,
          slots: this.createSlots(['S1', 'S3', 'S5', 'S6'], 6)
        },
        {
          name: '磨床加工单元库区',
          total: 6,
          filled: 3,
          slots: this.createSlots(['S2', 'S4', 'S6'], 6)
        }
      ],
      alarmEvents: [
        { time: '10:26', device: 'MAZAK', level: '严重', type: 'alarm', message: 'ALARM=1，建议定位机床侧运行条件。' },
        { time: '10:22', device: '测量仪', level: '严重', type: 'alarm', message: '测量流程中断，等待复测确认。' },
        { time: '10:18', device: 'L2100SY', level: '警告', type: 'warn', message: '目标程序与实际程序不一致。' },
        { time: '10:12', device: '插齿机单元机器人', level: '警告', type: 'warn', message: '自动模式待恢复，需要人工确认。' },
        { time: '10:08', device: '清洗机', level: '警告', type: 'warn', message: '下料等待时间超过设定阈值。' },
        { time: '10:03', device: '车床加工单元', level: '提示', type: 'idle', message: '任务已接收，等待上游放行。' },
        { time: '09:58', device: '磨床加工单元', level: '提示', type: 'idle', message: '预制单元待接入，当前按模拟节拍展示。' }
      ],
      alarmFrequencyTop5: [
        { name: 'MAZAK', value: 12 },
        { name: '测量仪', value: 9 },
        { name: '插齿机单元', value: 7 },
        { name: 'L2100SY', value: 5 },
        { name: '磨床加工单元', value: 4 }
      ],
      historyTicks: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00', '24:00'],
      historyLegend: [
        { label: '关机', type: 'off' },
        { label: '运行', type: 'run' },
        { label: '待机', type: 'standby' },
        { label: '报警', type: 'alarm' }
      ],
      deviceHistoryList: [
        { name: '加工中心单元机器人', segments: [{ label: '待机', type: 'standby', start: 0, end: 3.4 }, { label: '运行', type: 'run', start: 3.4, end: 10.8 }, { label: '待机', type: 'standby', start: 10.8, end: 12.1 }, { label: '运行', type: 'run', start: 12.1, end: 20.4 }, { label: '关机', type: 'off', start: 20.4, end: 24 }] },
        { name: '车床加工单元机器人', segments: [{ label: '运行', type: 'run', start: 0, end: 5.2 }, { label: '待机', type: 'standby', start: 5.2, end: 8.8 }, { label: '运行', type: 'run', start: 8.8, end: 17.6 }, { label: '待机', type: 'standby', start: 17.6, end: 18.7 }, { label: '运行', type: 'run', start: 18.7, end: 24 }] },
        { name: '插齿机单元机器人', segments: [{ label: '关机', type: 'off', start: 0, end: 2.5 }, { label: '运行', type: 'run', start: 2.5, end: 9.4 }, { label: '报警', type: 'alarm', start: 9.4, end: 10.3 }, { label: '待机', type: 'standby', start: 10.3, end: 13.1 }, { label: '运行', type: 'run', start: 13.1, end: 24 }] },
        { name: '磨床加工单元', segments: [{ label: '关机', type: 'off', start: 0, end: 3.2 }, { label: '待机', type: 'standby', start: 3.2, end: 6.4 }, { label: '运行', type: 'run', start: 6.4, end: 15.8 }, { label: '待机', type: 'standby', start: 15.8, end: 17.2 }, { label: '运行', type: 'run', start: 17.2, end: 24 }] },
        { name: 'F500', segments: [{ label: '运行', type: 'run', start: 0, end: 7.8 }, { label: '待机', type: 'standby', start: 7.8, end: 9.2 }, { label: '运行', type: 'run', start: 9.2, end: 18.6 }, { label: '待机', type: 'standby', start: 18.6, end: 19.4 }, { label: '运行', type: 'run', start: 19.4, end: 24 }] },
        { name: 'L2100SY', segments: [{ label: '关机', type: 'off', start: 0, end: 4.1 }, { label: '运行', type: 'run', start: 4.1, end: 11.6 }, { label: '报警', type: 'alarm', start: 11.6, end: 12.8 }, { label: '运行', type: 'run', start: 12.8, end: 24 }] },
        { name: 'L2000LSY', segments: [{ label: '运行', type: 'run', start: 0, end: 6.6 }, { label: '待机', type: 'standby', start: 6.6, end: 7.8 }, { label: '运行', type: 'run', start: 7.8, end: 16.2 }, { label: '待机', type: 'standby', start: 16.2, end: 17.1 }, { label: '运行', type: 'run', start: 17.1, end: 24 }] },
        { name: '新日', segments: [{ label: '关机', type: 'off', start: 0, end: 3.8 }, { label: '运行', type: 'run', start: 3.8, end: 14.6 }, { label: '待机', type: 'standby', start: 14.6, end: 15.4 }, { label: '运行', type: 'run', start: 15.4, end: 24 }] },
        { name: 'MAZAK', segments: [{ label: '运行', type: 'run', start: 0, end: 8.7 }, { label: '报警', type: 'alarm', start: 8.7, end: 10.1 }, { label: '待机', type: 'standby', start: 10.1, end: 11.4 }, { label: '运行', type: 'run', start: 11.4, end: 24 }] },
        { name: 'YKD5132H', segments: [{ label: '关机', type: 'off', start: 0, end: 2.8 }, { label: '运行', type: 'run', start: 2.8, end: 12.2 }, { label: '待机', type: 'standby', start: 12.2, end: 13.5 }, { label: '运行', type: 'run', start: 13.5, end: 24 }] },
        { name: '清洗机', segments: [{ label: '关机', type: 'off', start: 0, end: 4.4 }, { label: '运行', type: 'run', start: 4.4, end: 9.7 }, { label: '待机', type: 'standby', start: 9.7, end: 13.6 }, { label: '运行', type: 'run', start: 13.6, end: 18.8 }, { label: '关机', type: 'off', start: 18.8, end: 24 }] },
        { name: '测量仪', segments: [{ label: '待机', type: 'standby', start: 0, end: 5.1 }, { label: '运行', type: 'run', start: 5.1, end: 13.3 }, { label: '待机', type: 'standby', start: 13.3, end: 14.2 }, { label: '运行', type: 'run', start: 14.2, end: 24 }] },
        { name: '加工中心单元打标机', segments: [{ label: '待机', type: 'standby', start: 0, end: 6.5 }, { label: '运行', type: 'run', start: 6.5, end: 11.2 }, { label: '待机', type: 'standby', start: 11.2, end: 15.8 }, { label: '运行', type: 'run', start: 15.8, end: 24 }] },
        { name: '车床加工单元打标机', segments: [{ label: '待机', type: 'standby', start: 0, end: 4.9 }, { label: '运行', type: 'run', start: 4.9, end: 12.6 }, { label: '待机', type: 'standby', start: 12.6, end: 13.4 }, { label: '运行', type: 'run', start: 13.4, end: 24 }] },
        { name: '插齿机单元打标机', segments: [{ label: '待机', type: 'standby', start: 0, end: 7.1 }, { label: '运行', type: 'run', start: 7.1, end: 10.9 }, { label: '待机', type: 'standby', start: 10.9, end: 16.5 }, { label: '运行', type: 'run', start: 16.5, end: 24 }] }
      ]
    }
  },
  computed: {
    lineMonitorScrollList() {
      return [...this.lineMonitorRows, ...this.lineMonitorRows]
    },
    equipmentScrollList() {
      return [...this.equipmentList, ...this.equipmentList]
    },
    alarmScrollList() {
      return [...this.alarmEvents, ...this.alarmEvents]
    },
    deviceHistoryScrollList() {
      return [...this.deviceHistoryList, ...this.deviceHistoryList]
    },
    lineMonitorScrollOption() {
      return {
        step: 0.38,
        limitMoveNum: 3,
        hoverStop: true,
        direction: 1,
        openTouch: false,
        singleHeight: 168,
        waitTime: 1200,
        autoPlay: true
      }
    },
    equipmentScrollOption() {
      return {
        step: 0.45,
        limitMoveNum: 4,
        hoverStop: true,
        direction: 1,
        openTouch: false,
        singleHeight: 44,
        waitTime: 1200,
        autoPlay: true
      }
    },
    alarmScrollOption() {
      return {
        step: 0.4,
        limitMoveNum: 4,
        hoverStop: true,
        direction: 1,
        openTouch: false,
        singleHeight: 36,
        waitTime: 1200,
        autoPlay: true
      }
    },
    historyScrollOption() {
      return {
        step: 0.45,
        limitMoveNum: 5,
        hoverStop: true,
        direction: 1,
        openTouch: false,
        singleHeight: 32,
        waitTime: 1000,
        autoPlay: true
      }
    }
  },
  mounted() {
    const config = window.dt_Config || {}
    this.iframeAddress = config.iframeAddress ? config.iframeAddress : ''
    this.autoPlayCraftCode = config.autoPlayCraftCode || 'E8077755-9B85-46A1-9B8A-85083C00DF26'
    this.smIframeBase = config.smIframe || this.srcSm
    this.selectedAutoPlayRate = this.normalizeAutoPlayRate(config.autoPlayRate || this.getAutoPlayRateFromUrl(this.smIframeBase) || 5)
    this.pendingAutoPlayRate = this.selectedAutoPlayRate
    this.srcDt = this.buildIframeUrl(config.dtIframe || this.srcDt, this.autoPlayCraftCode)
    this.srcSm = this.buildIframeUrl(this.smIframeBase, this.autoPlayCraftCode, this.selectedAutoPlayRate)
    this.titleText = config.titleText
    this.updateTime()
    this.setScale()
    this.$nextTick(this.initAlarmPieChart)
    this.loadLineMonitorData()
    this.clockTimer = setInterval(this.updateTime, 1000)
    this.lineMonitorTimer = setInterval(() => {
      this.loadLineMonitorData()
    }, 5000)
    this.resizeHandler = () => {
      this.setScale()
      if (this.alarmPieChart) {
        this.alarmPieChart.resize()
      }
    }
    window.addEventListener('resize', this.resizeHandler)
    window.addEventListener('message', this.handleDmtMessage)
  },
  beforeDestroy() {
    if (this.clockTimer) {
      clearInterval(this.clockTimer)
      this.clockTimer = null
    }
    if (this.resizeHandler) {
      window.removeEventListener('resize', this.resizeHandler)
      this.resizeHandler = null
    }
    window.removeEventListener('message', this.handleDmtMessage)
    if (this.modeSwitchTimer) {
      clearTimeout(this.modeSwitchTimer)
      this.modeSwitchTimer = null
    }
    if (this.cameraSyncTimer) {
      clearTimeout(this.cameraSyncTimer)
      this.cameraSyncTimer = null
    }
    if (this.iframeReadyTimer) {
      clearTimeout(this.iframeReadyTimer)
      this.iframeReadyTimer = null
    }
    if (this.lineMonitorTimer) {
      clearInterval(this.lineMonitorTimer)
      this.lineMonitorTimer = null
    }
    if (this.alarmPieChart) {
      this.alarmPieChart.dispose()
      this.alarmPieChart = null
    }
  },
  methods: {
    createSlots(filledNames, total) {
      return Array.from({ length: total }, (_, index) => {
        const name = `S${index + 1}`
        return {
          name,
          filled: filledNames.includes(name)
        }
      })
    },
    loadLineMonitorData() {
      if (this.lineMonitorRequesting) return
      this.lineMonitorRequesting = true
      const data = this.CreateData('11', 'TV_线体任务联动状态_查询', [])
      this.ExecDatabase(data)
        .then((response) => {
          const list = response && response.data ? response.data : null
          if (!Array.isArray(list) || list.length === 0) return
          if (list.length === 1 && list[0] && (list[0].result === '0' || list[0].result === 0)) return
          this.applyLineMonitorData(list)
        })
        .catch((error) => {
          console.error('TV_线体任务联动状态_查询失败', error)
        })
        .then(() => {
          this.lineMonitorRequesting = false
        })
    },
    applyLineMonitorData(list) {
      const rowMap = {}
      list.forEach((item) => {
        const row = this.createLineMonitorRow(item)
        if (row.unitKey) {
          rowMap[row.unitKey] = row
        }
      })
      this.lineMonitorRows = this.lineMonitorRows.map((row) => {
        if (row.unitKey && rowMap[row.unitKey]) {
          return Object.assign({}, row, rowMap[row.unitKey])
        }
        return row
      })
    },
    createLineMonitorRow(item) {
      const getValue = (...keys) => {
        for (let i = 0; i < keys.length; i++) {
          const value = item[keys[i]]
          if (value !== undefined && value !== null) {
            return value
          }
        }
        return ''
      }
      const unitKey = this.normalizeUnitKey(getValue('UnitKey', 'unitKey'))
      const statusType = this.normalizeStatusType(getValue('StatusType', 'statusType'))
      const taskNo = this.formatTaskNo(getValue('TaskNo', 'taskNo'))
      const startPos = this.formatDisplayValue(getValue('StartPos', 'startPos'))
      const endPos = this.formatDisplayValue(getValue('EndPos', 'endPos'))
      const recipeCode = this.formatDisplayValue(getValue('RecipeCode', 'recipeCode'))
      const palletType = this.formatDisplayValue(getValue('PalletType', 'palletType'))
      return {
        unitKey,
        name: this.formatDisplayValue(getValue('UnitName', 'unitName')),
        status: this.formatDisplayValue(getValue('StatusText', 'statusText')),
        type: statusType,
        taskNo,
        startPos,
        endPos,
        taskInfo: [
          { label: '配方号', value: recipeCode },
          { label: '托盘', value: palletType }
        ],
        lineSignals: [
          { label: 'PCS运行', state: this.getSignalState(getValue('PCSRun', 'pcsRun')) },
          { label: '允许接收', state: this.getSignalState(getValue('AllowTask', 'allowTask')) },
          { label: '自动模式', state: this.getSignalState(getValue('AutoMode', 'autoMode')) },
          { label: '急停', state: this.getSignalState(getValue('EmergencyStop', 'emergencyStop'), true) }
        ],
        handshakes: [
          { label: '启动', state: this.getSignalState(getValue('StartFlag', 'startFlag')) },
          { label: '准备好', state: this.getSignalState(getValue('ReadyFlag', 'readyFlag')) },
          { label: '忙碌', state: this.getSignalState(getValue('BusyFlag', 'busyFlag')) },
          { label: '错误', state: this.getSignalState(getValue('ErrorFlag', 'errorFlag'), true) },
          { label: '完成', state: this.getSignalState(getValue('CompleteFlag', 'completeFlag')) }
        ]
      }
    },
    normalizeUnitKey(value) {
      const key = String(value || '').trim()
      if (key) return key
      return ''
    },
    normalizeStatusType(value) {
      const type = String(value || '').trim()
      return ['on', 'ok', 'alarm'].includes(type) ? type : 'ok'
    },
    formatDisplayValue(value) {
      const text = String(value == null ? '' : value).trim()
      if (!text || text.toUpperCase() === 'NULL') {
        return '--'
      }
      return text
    },
    formatTaskNo(value) {
      const text = this.formatDisplayValue(value)
      if (text === '--' || text === '0') {
        return '无任务'
      }
      return text.indexOf('TASK') === 0 ? text : `TASK ${text}`
    },
    isSignalOn(value) {
      const text = String(value == null ? '' : value).trim().toUpperCase()
      return ['1', 'TRUE', 'ON', 'YES', '是'].includes(text)
    },
    getSignalState(value, alarmWhenOn = false) {
      if (!this.isSignalOn(value)) {
        return 'off'
      }
      return alarmWhenOn ? 'alarm' : 'on'
    },
    getHistorySegmentStyle(segment) {
      const start = Math.max(0, Math.min(24, segment.start))
      const end = Math.max(start, Math.min(24, segment.end))
      return {
        left: `${(start / 24) * 100}%`,
        width: `${((end - start) / 24) * 100}%`
      }
    },
    changeView(value) {
      this.activeView = value
      if (this.dmtIframeReady) {
        this.syncActiveCamera()
      }
    },
    buildIframeUrl(baseUrl, craftCode, autoPlayRate) {
      if (!baseUrl) {
        return ''
      }
      let nextUrl = baseUrl
      if (nextUrl.includes('dmtmode=SM')) {
        nextUrl = this.withAutoPlayRateParam(nextUrl, this.normalizeAutoPlayRate(autoPlayRate))
      }
      if (craftCode && nextUrl.includes('autoPlayCraftCode=') && !nextUrl.includes(craftCode)) {
        const separator = nextUrl.endsWith('=') || nextUrl.endsWith('/') ? '' : '/'
        nextUrl = `${nextUrl}${separator}${craftCode}`
      }
      return this.withTransparentParam(nextUrl)
    },
    getAutoPlayRateFromUrl(url) {
      const match = String(url || '').match(/[?&]autoPlayRate=([^&#]*)/)
      return match ? decodeURIComponent(match[1]) : ''
    },
    normalizeAutoPlayRate(rate) {
      const numericRate = Number(rate)
      return this.autoPlayRates.includes(numericRate) ? numericRate : 5
    },
    withAutoPlayRateParam(url, rate) {
      const value = encodeURIComponent(String(rate))
      if (url.includes('autoPlayRate=')) {
        return url.replace(/([?&]autoPlayRate=)[^&#]*/, `$1${value}`)
      }
      if (url.includes('autoPlayCraftCode=')) {
        return url.replace(/([?&])autoPlayCraftCode=/, `$1autoPlayRate=${value}&autoPlayCraftCode=`)
      }
      const hashIndex = url.indexOf('#')
      if (hashIndex > -1) {
        const separator = url.slice(hashIndex).includes('?') ? '&' : '?'
        return `${url}${separator}autoPlayRate=${value}`
      }
      const separator = url.includes('?') ? '&' : '?'
      return `${url}${separator}autoPlayRate=${value}`
    },
    withTransparentParam(url) {
      if (!url || url.includes('transparent=true')) {
        return url
      }
      const hashIndex = url.indexOf('#')
      if (hashIndex > -1) {
        const separator = url.slice(hashIndex).includes('?') ? '&' : '?'
        return `${url}${separator}transparent=true`
      }
      return `${url}${url.endsWith('/') ? '' : '/'}#?transparent=true`
    },
    handleIframeLoad() {
      this.dmtIframeReady = false
      if (this.iframeReadyTimer) {
        clearTimeout(this.iframeReadyTimer)
        this.iframeReadyTimer = null
      }
    },
    handleDmtMessage(event) {
      if (!event || !event.data || event.data.type !== 'dmt_load_finish') {
        return
      }
      const iframe = document.getElementById('dmtIframe')
      if (!iframe || event.source !== iframe.contentWindow) {
        return
      }
      this.markDmtIframeReady()
    },
    markDmtIframeReady() {
      this.dmtIframeReady = true
      if (this.iframeReadyTimer) {
        clearTimeout(this.iframeReadyTimer)
        this.iframeReadyTimer = null
      }
      this.syncActiveCamera()
    },
    syncActiveCamera() {
      const camera = this.cameraViews[this.activeView]
      if (!camera) return
      if (!this.dmtIframeReady) return
      if (this.cameraSyncTimer) {
        clearTimeout(this.cameraSyncTimer)
      }
      this.cameraSyncTimer = setTimeout(() => {
        const iframe = document.getElementById('dmtIframe')
        if (!iframe || !iframe.contentWindow) return
        iframe.contentWindow.postMessage({
          type: 'set_camera',
          data: {
            target: camera.target,
            position: camera.position,
            zoom0: camera.zoom0
          }
        }, '*')
        this.cameraSyncTimer = null
      }, 300)
    },
    switchMode(mode) {
      this.currentMode = mode
      this.mode = ''
      this.dmtIframeReady = false
      if (this.iframeReadyTimer) {
        clearTimeout(this.iframeReadyTimer)
        this.iframeReadyTimer = null
      }
      if (this.modeSwitchTimer) {
        clearTimeout(this.modeSwitchTimer)
      }
      this.modeSwitchTimer = setTimeout(() => {
        this.mode = mode
        this.modeSwitchTimer = null
      }, 600)
    },
    changeDtMode() {
      this.switchMode('DT')
    },
    changeSmMode() {
      this.openSimulationRateDialog()
    },
    openSimulationRateDialog() {
      this.pendingAutoPlayRate = this.selectedAutoPlayRate
      this.simulationRateDialogVisible = true
    },
    selectSimulationRate(rate) {
      this.pendingAutoPlayRate = this.normalizeAutoPlayRate(rate)
    },
    cancelSimulationRateDialog() {
      this.simulationRateDialogVisible = false
    },
    confirmSimulationRate() {
      this.selectedAutoPlayRate = this.normalizeAutoPlayRate(this.pendingAutoPlayRate)
      this.srcSm = this.buildIframeUrl(this.smIframeBase, this.autoPlayCraftCode, this.selectedAutoPlayRate)
      this.simulationRateDialogVisible = false
      this.switchMode('SM')
    },
    initAlarmPieChart() {
      if (!this.$refs.alarmPieChart) {
        return
      }
      if (this.alarmPieChart) {
        this.alarmPieChart.dispose()
      }
      this.alarmPieChart = echarts.init(this.$refs.alarmPieChart)
      const names = this.alarmFrequencyTop5.map(item => item.name)
      const values = this.alarmFrequencyTop5.map(item => item.value)
      const maxValue = Math.max(...values, 1)
      const yAxisMax = Math.max(2, Math.ceil(maxValue / 2) * 2)
      this.alarmPieChart.setOption({
        grid: {
          left: 34,
          right: 12,
          top: 22,
          bottom: 24
        },
        tooltip: {
          trigger: 'axis',
          axisPointer: {
            type: 'shadow',
            shadowStyle: {
              color: 'rgba(114, 206, 255, 0.08)'
            }
          },
          formatter(params) {
            const item = params && params[0]
            return item ? `${item.name}<br/>报警频次：${item.value} 次` : ''
          }
        },
        xAxis: {
          type: 'category',
          data: names,
          axisTick: {
            show: false
          },
          axisLine: {
            lineStyle: {
              color: 'rgba(114, 206, 255, 0.28)'
            }
          },
          axisLabel: {
            color: '#b8d3e6',
            fontSize: 10,
            interval: 0
          }
        },
        yAxis: {
          type: 'value',
          min: 0,
          max: yAxisMax,
          interval: yAxisMax / 2,
          splitNumber: 2,
          minInterval: 1,
          axisLine: {
            show: false
          },
          axisTick: {
            show: false
          },
          axisLabel: {
            color: '#8fb8d3',
            fontSize: 10,
            formatter: value => `${Math.round(value)}`
          },
          splitLine: {
            lineStyle: {
              color: 'rgba(114, 206, 255, 0.12)',
              type: 'dashed'
            }
          }
        },
        series: [
          {
            name: '报警频次',
            type: 'bar',
            data: values,
            barWidth: 18,
            label: {
              show: true,
              position: 'top',
              color: '#e8f7ff',
              fontSize: 10,
              formatter: '{c}'
            },
            itemStyle: {
              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                { offset: 0, color: '#8df6ff' },
                { offset: 0.42, color: '#36b8ff' },
                { offset: 1, color: '#135cae' }
              ]),
              borderRadius: 0,
              shadowBlur: 10,
              shadowColor: 'rgba(80, 237, 255, 0.24)'
            }
          }
        ]
      })
    },
    setScale() {
      const scaleX = window.innerWidth / this.style.width
      const scaleY = window.innerHeight / this.style.height
      this.style.transform = `scaleY(${scaleY}) scaleX(${scaleX}) translate(-50%, -50%)`
    },
    updateTime() {
      const now = new Date()
      const pad = value => String(value).padStart(2, '0')
      this.headerDateCn = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`
      this.headerTime = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`
    }
  }
}
</script>

<style>
html,
body,
#app {
  margin: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #050b14;
}
</style>

<style scoped>
.qa-screen {
  --scada-panel: rgba(8, 20, 34, 0.84);
  --scada-panel-deep: rgba(3, 10, 20, 0.92);
  --scada-border: rgba(91, 213, 255, 0.34);
  --scada-border-strong: rgba(128, 234, 255, 0.68);
  --scada-cyan: #72e8ff;
  --scada-blue: #2c8fff;
  --scada-green: #24d88b;
  --scada-yellow: #f5c84c;
  --scada-red: #ff5266;
  --scada-text: #f1f8ff;
  --scada-muted: #91b5cd;
  position: fixed;
  left: 50%;
  top: 50%;
  transform-origin: 0 0;
  overflow: hidden;
  color: var(--scada-text);
  font-family: "Microsoft YaHei", "PingFang SC", "Segoe UI", Arial, sans-serif;
  background: #06101f url("/png/背景底图.png") no-repeat center center;
  background-size: cover;
  background-color: #050b14;
  box-shadow: inset 0 0 140px rgba(0, 0, 0, 0.72);
}

.qa-screen::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle at 25% 22%, rgba(42, 151, 255, 0.18), transparent 28%),
    radial-gradient(circle at 74% 26%, rgba(39, 217, 128, 0.08), transparent 26%),
    linear-gradient(180deg, rgba(2, 9, 22, 0.35), rgba(3, 13, 31, 0.66));
  pointer-events: none;
}

.qa-screen::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background:
    radial-gradient(circle at 50% 48%, rgba(44, 143, 255, 0.14), transparent 34%);
  opacity: 0.42;
  pointer-events: none;
}

.title-png {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 40;
  width: 100%;
  height: 100px;
  background-image: url("/png/top1.png");
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center;
  pointer-events: none;
  display: flex;
  justify-content: center;
  box-shadow: 0 10px 22px rgba(0, 0, 0, 0.32);
}

.view-switch {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 6px;
  pointer-events: auto;
}

.view-switch__btn {
  height: 30px;
  padding: 0;
  color: #b9d8ec;
  border: 1px solid rgba(111, 224, 255, 0.34);
  background:
    linear-gradient(180deg, rgba(24, 52, 82, 0.82), rgba(5, 17, 30, 0.9)),
    rgba(6, 25, 53, 0.72);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.12),
    inset 0 -10px 18px rgba(0, 0, 0, 0.18),
    0 6px 14px rgba(0, 0, 0, 0.28);
  font-family: inherit;
  font-size: 11px;
  font-weight: 700;
  line-height: 28px;
  white-space: nowrap;
  cursor: pointer;
}

.view-switch__btn--active {
  color: #ffffff;
  border-color: rgba(127, 239, 255, 0.95);
  background:
    linear-gradient(180deg, rgba(82, 207, 255, 0.74), rgba(16, 89, 142, 0.92)),
    rgba(13, 70, 118, 0.78);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.34),
    inset 0 0 14px rgba(101, 221, 255, 0.34),
    0 0 16px rgba(49, 166, 255, 0.46);
}

.view-switch__btn:focus {
  outline: none;
}

.mode-button {
  position: relative;
  flex: 1;
  height: 30px;
  padding: 0;
  color: #e9fbff;
  border: 1px solid rgba(75, 245, 209, 0.48);
  background:
    linear-gradient(90deg, rgba(8, 41, 57, 0.92), rgba(10, 67, 87, 0.78), rgba(8, 41, 57, 0.92)),
    rgba(4, 20, 35, 0.78);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.14),
    inset 0 0 14px rgba(75, 245, 209, 0.12),
    0 0 14px rgba(75, 245, 209, 0.16);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0;
  cursor: pointer;
  pointer-events: auto;
}

.mode-button::before,
.mode-button::after {
  content: "";
  position: absolute;
  top: 6px;
  width: 7px;
  height: 16px;
  border-top: 1px solid rgba(128, 255, 230, 0.74);
  border-bottom: 1px solid rgba(128, 255, 230, 0.48);
  pointer-events: none;
}

.mode-button::before {
  left: 6px;
  border-left: 1px solid rgba(128, 255, 230, 0.74);
}

.mode-button::after {
  right: 6px;
  border-right: 1px solid rgba(128, 255, 230, 0.74);
}

.mode-button:hover {
  color: #ffffff;
  border-color: rgba(128, 255, 230, 0.82);
  background:
    linear-gradient(90deg, rgba(12, 55, 73, 0.94), rgba(15, 91, 113, 0.84), rgba(12, 55, 73, 0.94)),
    rgba(4, 20, 35, 0.82);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.22),
    inset 0 0 18px rgba(75, 245, 209, 0.18),
    0 0 18px rgba(75, 245, 209, 0.22);
}

.mode-button--active {
  color: #ffffff;
  border-color: rgba(128, 255, 230, 0.95);
  background:
    linear-gradient(90deg, rgba(12, 62, 78, 0.96), rgba(26, 122, 132, 0.88), rgba(12, 62, 78, 0.96)),
    rgba(4, 20, 35, 0.86);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.26),
    inset 0 0 20px rgba(75, 245, 209, 0.24),
    0 0 20px rgba(75, 245, 209, 0.28);
}

.mode-button:focus {
  outline: none;
}

.simulation-rate-mask {
  position: absolute;
  inset: 0;
  z-index: 80;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(1, 9, 20, 0.58);
  pointer-events: auto;
}

.simulation-rate-dialog {
  width: 430px;
  padding: 20px 22px 18px;
  border: 1px solid rgba(114, 232, 255, 0.48);
  background:
    linear-gradient(180deg, rgba(19, 55, 82, 0.96), rgba(4, 16, 31, 0.96)),
    rgba(5, 20, 44, 0.96);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.12),
    inset 0 0 24px rgba(44, 143, 255, 0.18),
    0 18px 42px rgba(0, 0, 0, 0.48),
    0 0 28px rgba(114, 232, 255, 0.18);
}

.simulation-rate-dialog__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 18px;
}

.simulation-rate-dialog__head strong {
  color: #ffffff;
  font-size: 20px;
  letter-spacing: 0;
  text-shadow: 0 0 12px rgba(114, 232, 255, 0.3);
}

.simulation-rate-dialog__head span {
  color: #91dfff;
  font-size: 13px;
}

.simulation-rate-options {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}

.simulation-rate-option,
.simulation-rate-action {
  height: 36px;
  border: 1px solid rgba(111, 224, 255, 0.34);
  color: #d8f6ff;
  background:
    linear-gradient(180deg, rgba(24, 52, 82, 0.82), rgba(5, 17, 30, 0.9)),
    rgba(6, 25, 53, 0.72);
  font-family: inherit;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}

.simulation-rate-option--active {
  color: #ffffff;
  border-color: rgba(128, 255, 230, 0.95);
  background:
    linear-gradient(180deg, rgba(36, 216, 139, 0.42), rgba(14, 92, 61, 0.7)),
    rgba(14, 92, 61, 0.52);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.18),
    0 0 18px rgba(36, 216, 139, 0.24);
}

.simulation-rate-dialog__actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 18px;
}

.simulation-rate-action {
  min-width: 96px;
  padding: 0 18px;
}

.simulation-rate-action--ghost {
  color: #b9d8ec;
  border-color: rgba(160, 180, 194, 0.24);
  background: rgba(36, 48, 60, 0.48);
}

.simulation-rate-action--primary {
  color: #ffffff;
  border-color: rgba(114, 232, 255, 0.72);
  background:
    linear-gradient(90deg, rgba(12, 62, 78, 0.96), rgba(26, 122, 132, 0.88), rgba(12, 62, 78, 0.96)),
    rgba(4, 20, 35, 0.86);
  box-shadow: 0 0 18px rgba(75, 245, 209, 0.24);
}

.simulation-rate-option:focus,
.simulation-rate-action:focus {
  outline: none;
}

.brand-logo {
  display: block;
  width: 300px;
  height: 50px;
  box-sizing: border-box;
  padding: 6px 12px;
  object-fit: contain;
  object-position: left center;
  pointer-events: none;
  border: 1px solid rgba(255, 255, 255, 0.72);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.96), rgba(231, 242, 248, 0.9)),
    rgba(255, 255, 255, 0.92);
  box-shadow:
    inset 0 0 0 1px rgba(114, 206, 255, 0.14),
    0 0 16px rgba(114, 232, 255, 0.18),
    0 8px 20px rgba(0, 0, 0, 0.18);
}

.mode-button-group {
  display: flex;
  width: 300px;
  height: 30px;
  gap: 10px;
}

.title-text {
  margin-right: 37px;
  color: #fff;
  font-size: 37px;
  font-weight: 800;
  line-height: 70px;
  text-align: center;
  text-shadow:
    0 0 10px rgba(114, 232, 255, 0.56),
    0 0 26px rgba(44, 143, 255, 0.42),
    0 3px 0 rgba(0, 0, 0, 0.34);
}

.df-header__timebar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  color: #ffffff;
  height: 26px;
  padding: 0 8px;
  box-sizing: border-box;
}

.df-header__year {
  font-size: 13px;
  opacity: 0.9;
}

.df-header__time {
  font-size: 18px;
  font-weight: 700;
  color: #dffcff;
  text-shadow: 0 0 12px rgba(114, 232, 255, 0.5);
}

.leftLine,
.rightLine {
  position: absolute;
  top: 50px;
  z-index: 30;
  width: 54px;
  height: 1020px;
  background-size: 100% 100%;
  background-position: center;
  pointer-events: none;
}

.leftLine {
  left: 10px;
  background-image: url("/png/左边条.png");
}

.rightLine {
  right: 10px;
  background-image: url("/png/右边条.png");
}

.main-content {
  position: absolute;
  z-index: 2;
  left: 64px;
  right: 64px;
  top: 100px;
  bottom: 26px;
}

.left-control-bar,
.right-control-bar {
  position: absolute;
  width: 520px;
  z-index: 20;
  pointer-events: auto;
}

.left-control-bar {
  top: 18px;
  left: 80px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  height: 66px;
  gap: 7px;
}

.right-control-bar {
  top: 24px;
  right: 80px;
  height: 58px;
  display: grid;
  grid-template-rows: 24px 30px;
  justify-content: end;
  gap: 4px;
  box-sizing: border-box;
}

.right-control-bar .df-header__timebar {
  width: 236px;
  justify-self: end;
}

.right-control-bar .view-switch {
  width: 520px;
  justify-self: end;
}

.model-bg-frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  background: transparent;
  z-index: 1;
}

.model-placeholder {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #77d9ff;
  font-size: 24px;
  background: radial-gradient(circle at center, rgba(37, 111, 184, 0.24), rgba(4, 12, 28, 0.92));
}

.left-box {
  position: absolute;
  top: 0;
  left: 0;
  width: 360px;
  bottom: 260px;
  z-index: 10;
  overflow: hidden;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.left-box::-webkit-scrollbar,
.line-monitor-body::-webkit-scrollbar {
  width: 0;
  height: 0;
  display: none;
}

.right-box {
  position: absolute;
  top: 0;
  right: 0;
  width: 360px;
  bottom: 260px;
  z-index: 10;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.bottom-box {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 248px;
  z-index: 10;
  display: grid;
  grid-template-columns: 1.2fr 1fr 1fr;
  gap: 12px;
}

.left-box .qa-panel {
  height: 100%;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
}

.line-monitor-body {
  padding: 8px 12px;
  overflow: hidden;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.line-monitor-scroll-box {
  width: 100%;
  height: 100%;
  overflow: hidden;
}

.line-monitor-scroll,
.line-monitor-content {
  min-height: 100%;
  overflow: hidden;
}

.line-monitor-content {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.right-box .qa-panel:first-child {
  flex: 1;
  min-height: 0;
}

.right-box .qa-panel:last-child {
  flex-shrink: 0;
  height: 236px;
}

.right-box .qa-scroll {
  overflow: hidden;
  scrollbar-width: none;
}

.right-box .qa-scroll::-webkit-scrollbar {
  width: 0;
  height: 0;
  display: none;
}

.qa-panel {
  position: relative;
  min-height: 0;
  overflow: hidden;
  border: 1px solid var(--scada-border);
  background:
    linear-gradient(180deg, rgba(18, 44, 68, 0.72) 0%, rgba(5, 15, 29, 0.9) 100%),
    var(--scada-panel);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    inset 0 0 28px rgba(44, 143, 255, 0.18),
    0 14px 30px rgba(0, 0, 0, 0.38),
    0 0 18px rgba(44, 143, 255, 0.08);
}

.qa-panel::before,
.qa-panel::after {
  content: "";
  position: absolute;
  z-index: 1;
  width: 28px;
  height: 28px;
  pointer-events: none;
}

.qa-panel::before {
  top: 0;
  left: 0;
  border-top: 2px solid rgba(128, 234, 255, 0.82);
  border-left: 2px solid rgba(128, 234, 255, 0.72);
  box-shadow: -1px -1px 10px rgba(114, 232, 255, 0.2);
}

.qa-panel::after {
  right: 0;
  bottom: 0;
  border-right: 2px solid rgba(128, 234, 255, 0.58);
  border-bottom: 2px solid rgba(128, 234, 255, 0.5);
  box-shadow: 1px 1px 10px rgba(114, 232, 255, 0.14);
}

.qa-panel-body {
  height: calc(100% - 42px);
  padding: 10px 12px 12px;
  box-sizing: border-box;
}

.qa-panel-body--storage {
  padding: 8px 10px 10px;
  display: flex;
  min-height: 0;
}

.qa-scroll {
  overflow: auto;
}

.qa-scroll::-webkit-scrollbar {
  width: 6px;
}

.qa-scroll::-webkit-scrollbar-thumb {
  background: rgba(111, 212, 255, 0.22);
}

.left-title-header,
.right-title-header,
.center-title-header {
  height: 42px;
  display: flex;
  align-items: center;
  padding: 0 18px;
  box-sizing: border-box;
  position: relative;
  z-index: 2;
}

.left-title-header {
  justify-content: flex-start;
  padding-left: 18px;
  background:
    linear-gradient(90deg, rgba(25, 92, 145, 0.5), rgba(8, 24, 46, 0.12)),
    linear-gradient(180deg, rgba(255, 255, 255, 0.07), transparent);
}

.right-title-header {
  justify-content: flex-start;
  padding-left: 18px;
  padding-right: 18px;
  background:
    linear-gradient(90deg, rgba(25, 92, 145, 0.5), rgba(8, 24, 46, 0.12)),
    linear-gradient(180deg, rgba(255, 255, 255, 0.07), transparent);
}

.center-title-header {
  border-bottom: 1px solid rgba(114, 206, 255, 0.22);
  background:
    linear-gradient(90deg, rgba(25, 92, 145, 0.5), rgba(8, 24, 46, 0.12)),
    linear-gradient(180deg, rgba(255, 255, 255, 0.07), transparent);
}

.left-title-text,
.right-title-text,
.center-title-header span {
  color: #ffffff;
  font-size: 17px;
  font-weight: 800;
  text-shadow: 0 0 12px rgba(114, 232, 255, 0.34);
}

.center-title-header::after,
.left-title-header::after,
.right-title-header::after {
  content: "";
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(114, 232, 255, 0.46), transparent);
  pointer-events: none;
}

.right-title-text {
  text-align: left;
}

.status-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 18px;
  padding: 2px 9px;
  border-radius: 0;
  color: #b8ccdc;
  border: 1px solid rgba(255, 255, 255, 0.1);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.03)),
    rgba(40, 55, 72, 0.42);
  font-size: 12px;
  white-space: nowrap;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    0 0 10px rgba(255, 255, 255, 0.04);
  text-shadow: none;
}

.status-pill--compact {
  padding: 2px 7px;
  font-size: 11px;
}

.status-pill--ok {
  color: #d9ffea;
  border-color: rgba(36, 216, 139, 0.38);
  background:
    linear-gradient(180deg, rgba(36, 216, 139, 0.34), rgba(11, 73, 49, 0.56)),
    rgba(14, 92, 61, 0.45);
  box-shadow: 0 0 10px rgba(36, 216, 139, 0.18);
}

.status-pill--on {
  color: #e0fbff;
  border-color: rgba(114, 232, 255, 0.42);
  background:
    linear-gradient(180deg, rgba(44, 143, 255, 0.4), rgba(14, 58, 98, 0.58)),
    rgba(14, 58, 98, 0.48);
  box-shadow: 0 0 14px rgba(44, 143, 255, 0.2);
}

.status-pill--warn {
  color: #fff3bf;
  border-color: rgba(245, 200, 76, 0.42);
  background:
    linear-gradient(180deg, rgba(245, 200, 76, 0.38), rgba(101, 73, 14, 0.58)),
    rgba(101, 73, 14, 0.44);
  box-shadow: 0 0 14px rgba(245, 200, 76, 0.2);
}

.status-pill--alarm {
  color: #ffe3e6;
  border-color: rgba(255, 82, 102, 0.5);
  background:
    linear-gradient(180deg, rgba(255, 82, 102, 0.42), rgba(101, 18, 32, 0.62)),
    rgba(101, 18, 32, 0.48);
  box-shadow: 0 0 12px rgba(255, 82, 102, 0.22);
}

.status-pill--idle {
  color: #c5d4df;
  border-color: rgba(160, 180, 194, 0.24);
  background:
    linear-gradient(180deg, rgba(100, 121, 139, 0.28), rgba(36, 48, 60, 0.54)),
    rgba(36, 48, 60, 0.42);
}

.line-status {
  flex: 0 0 auto;
  min-width: 74px;
  padding: 4px 10px;
  border-radius: 0;
  color: #f3fbff;
  font-size: 13px;
  font-weight: 700;
  text-align: center;
  letter-spacing: 0;
  border: 1px solid rgba(255, 255, 255, 0.12);
  text-shadow: none;
}

.line-status--ok {
  color: #dfffee;
  border-color: rgba(36, 216, 139, 0.52);
  background:
    linear-gradient(180deg, rgba(36, 216, 139, 0.44), rgba(14, 92, 61, 0.68)),
    rgba(14, 92, 61, 0.45);
  box-shadow: 0 0 18px rgba(36, 216, 139, 0.26);
}

.line-status--on {
  color: #e7f9ff;
  border-color: rgba(114, 232, 255, 0.52);
  background:
    linear-gradient(180deg, rgba(44, 143, 255, 0.5), rgba(16, 79, 126, 0.72)),
    rgba(16, 79, 126, 0.48);
  box-shadow: 0 0 18px rgba(44, 143, 255, 0.3);
}

.line-status--alarm {
  color: #fff2f4;
  border-color: rgba(255, 82, 102, 0.58);
  background:
    linear-gradient(180deg, rgba(255, 82, 102, 0.52), rgba(118, 22, 38, 0.72)),
    rgba(118, 22, 38, 0.5);
  box-shadow: 0 0 18px rgba(255, 82, 102, 0.34);
}

.monitor-card,
.equipment-row,
.storage-card,
.event-row {
  position: relative;
  border: 1px solid rgba(114, 206, 255, 0.18);
  background:
    linear-gradient(180deg, rgba(20, 42, 62, 0.62), rgba(4, 14, 27, 0.74)),
    rgba(5, 20, 44, 0.66);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.05),
    inset 0 0 18px rgba(42, 133, 205, 0.11);
}

.monitor-card {
  flex: 0 0 168px;
  min-height: 168px;
  margin-bottom: 0;
  padding: 8px 12px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  overflow: hidden;
}

.monitor-card::before,
.equipment-row::before,
.event-row::before,
.history-row::before,
.assist-status-row::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 2px;
  background: linear-gradient(180deg, transparent, rgba(114, 232, 255, 0.42), transparent);
  opacity: 0.7;
  pointer-events: none;
}

.monitor-head,
.assist-title,
.storage-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}

.monitor-title strong {
  display: block;
  color: #fff;
  font-size: 18px;
  text-shadow: 0 0 10px rgba(114, 232, 255, 0.28);
}

.monitor-title span {
  display: block;
  margin-top: 2px;
  color: var(--scada-cyan);
  font-size: 12px;
  font-weight: 700;
}

.task-summary {
  margin-top: 5px;
  padding: 6px 8px;
  border: 1px solid rgba(114, 206, 255, 0.08);
  background:
    linear-gradient(90deg, rgba(44, 143, 255, 0.1), transparent 55%),
    rgba(0, 0, 0, 0.26);
  display: grid;
  grid-template-columns: 1.2fr 1fr 1fr;
  gap: 6px;
}

.task-route,
.task-field {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.task-route span,
.task-field span,
.equipment-program span,
.storage-head span {
  color: #9fc3dc;
  font-size: 11px;
}

.task-route strong,
.task-field strong {
  color: #fff;
  font-size: 13px;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.signal-section {
  margin-top: 6px;
  display: grid;
  grid-template-columns: 50px minmax(0, 1fr);
  align-items: center;
  gap: 6px;
}

.signal-section__title {
  color: #9fc3dc;
  font-size: 11px;
}

.signal-grid {
  min-width: 0;
  display: grid;
  gap: 5px;
}

.signal-grid--line {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

.signal-grid--handshake {
  grid-template-columns: repeat(5, minmax(0, 1fr));
}

.signal-chip {
  min-width: 0;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  color: #b8ccdc;
  border: 1px solid rgba(160, 180, 194, 0.16);
  background: rgba(36, 48, 60, 0.38);
  font-size: 10px;
  line-height: 1;
  white-space: nowrap;
  overflow: hidden;
}

.signal-chip i {
  flex: 0 0 auto;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #5f7182;
}

.signal-chip span {
  overflow: hidden;
  text-overflow: ellipsis;
}

.signal-chip--on {
  color: #d9ffea;
  border-color: rgba(36, 216, 139, 0.34);
  background: rgba(12, 80, 54, 0.52);
}

.signal-chip--on i {
  background: var(--scada-green);
  box-shadow: 0 0 8px rgba(36, 216, 139, 0.64);
}

.signal-chip--warn {
  color: #fff3bf;
  border-color: rgba(245, 200, 76, 0.36);
  background: rgba(101, 73, 14, 0.48);
}

.signal-chip--warn i {
  background: var(--scada-yellow);
  box-shadow: 0 0 8px rgba(245, 200, 76, 0.58);
}

.signal-chip--alarm {
  color: #ffe3e6;
  border-color: rgba(255, 82, 102, 0.44);
  background: rgba(101, 18, 32, 0.52);
}

.signal-chip--alarm i {
  background: var(--scada-red);
  box-shadow: 0 0 8px rgba(255, 82, 102, 0.62);
}

.signal-chip--off {
  color: #9fb0bd;
}

.signal-chip--off i {
  background: #5f7182;
}

.dot {
  flex: 0 0 auto;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.04);
}

.dot--green { background: var(--scada-green); box-shadow: 0 0 0 4px rgba(36, 216, 139, 0.08), 0 0 10px rgba(36, 216, 139, 0.5); }
.dot--blue { background: var(--scada-cyan); box-shadow: 0 0 0 4px rgba(114, 232, 255, 0.08), 0 0 10px rgba(114, 232, 255, 0.46); }
.dot--yellow { background: var(--scada-yellow); box-shadow: 0 0 0 4px rgba(245, 200, 76, 0.08), 0 0 10px rgba(245, 200, 76, 0.45); }
.dot--red { background: var(--scada-red); box-shadow: 0 0 0 4px rgba(255, 82, 102, 0.08), 0 0 10px rgba(255, 82, 102, 0.48); }
.dot--gray { background: #5f7182; }

.monitor-stage {
  border-left: 3px solid rgba(53, 178, 255, 0.8);
}

.monitor-stage--ok {
  border-left-color: #2be085;
}

.monitor-stage--alarm {
  border-left-color: #ff5d6c;
  background: rgba(64, 8, 20, 0.38);
}

.equipment-body,
.event-body,
.history-body {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.alarm-bar-chart {
  flex-shrink: 0;
  height: 98px;
  margin: -2px 0 5px;
  border: 1px solid rgba(114, 206, 255, 0.12);
  background:
    linear-gradient(180deg, rgba(24, 66, 104, 0.28), rgba(7, 20, 34, 0.12)),
    rgba(0, 0, 0, 0.18);
}

.seamless-box {
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

.seamless-scroll,
.seamless-content {
  min-height: 100%;
  overflow: hidden;
}

.equipment-head,
.equipment-row {
  display: grid;
  grid-template-columns: 68px 56px minmax(104px, 1fr) 50px;
  align-items: center;
  gap: 8px;
}

.equipment-head {
  flex-shrink: 0;
  min-height: 28px;
  margin-bottom: 6px;
  padding: 0 8px;
  color: #a9cce0;
  border: 1px solid rgba(114, 206, 255, 0.18);
  background:
    linear-gradient(180deg, rgba(38, 83, 122, 0.42), rgba(12, 34, 56, 0.42)),
    rgba(21, 58, 94, 0.34);
  font-size: 12px;
}

.equipment-row {
  min-height: 44px;
  margin-bottom: 6px;
  padding: 8px;
}

.equipment-row--alarm {
  border-color: rgba(255, 82, 102, 0.52);
  background:
    linear-gradient(90deg, rgba(255, 82, 102, 0.22), rgba(64, 8, 20, 0.44)),
    rgba(64, 8, 20, 0.48);
}

.equipment-name {
  min-width: 0;
  color: #fff;
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.equipment-program {
  display: flex;
  flex-direction: column;
}

.equipment-count {
  color: #fff;
  font-size: 13px;
  font-weight: 700;
  white-space: nowrap;
}

.equipment-program b {
  margin-top: 4px;
  color: #fff;
  font-size: 13px;
}

.equipment-tags,
.assist-flow {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  min-width: 0;
  overflow: hidden;
}

.equipment-status {
  justify-self: end;
  min-width: 40px;
}

.assist-body {
  display: flex;
  flex-direction: column;
  padding: 8px 12px 10px;
  overflow: hidden;
}

.assist-head,
.assist-status-row {
  display: grid;
  grid-template-columns: 118px minmax(108px, 1fr) 50px;
  align-items: center;
  gap: 8px;
}

.assist-head {
  flex-shrink: 0;
  min-height: 26px;
  margin-bottom: 5px;
  padding: 0 8px;
  color: #a9cce0;
  border: 1px solid rgba(114, 206, 255, 0.18);
  background:
    linear-gradient(180deg, rgba(38, 83, 122, 0.42), rgba(12, 34, 56, 0.42)),
    rgba(21, 58, 94, 0.34);
  font-size: 12px;
}

.assist-list {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-rows: repeat(5, minmax(0, 1fr));
  gap: 4px;
}

.assist-status-row {
  position: relative;
  min-height: 0;
  padding: 0 8px;
  border: 1px solid rgba(114, 206, 255, 0.18);
  background:
    linear-gradient(180deg, rgba(20, 42, 62, 0.58), rgba(4, 14, 27, 0.72)),
    rgba(5, 20, 44, 0.66);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.05),
    inset 0 0 14px rgba(42, 133, 205, 0.12);
}

.assist-name {
  min-width: 0;
  color: #fff;
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.assist-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  min-width: 0;
  overflow: hidden;
}

.assist-status {
  justify-self: end;
  min-width: 40px;
}

.storage-groups {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
  align-items: stretch;
  width: 100%;
  height: 100%;
}

.storage-card {
  min-width: 0;
  padding: 9px;
  display: flex;
  flex-direction: column;
}

.storage-head {
  margin-bottom: 8px;
  flex-shrink: 0;
}

.slots {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-rows: minmax(0, 1fr);
  gap: 6px;
}

.slot {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 30px;
  color: rgba(184, 202, 214, 0.72);
  border: 1px solid rgba(127, 154, 174, 0.24);
  background:
    linear-gradient(180deg, rgba(84, 99, 114, 0.24), rgba(28, 39, 51, 0.42)),
    rgba(56, 68, 82, 0.22);
  box-shadow:
    inset 0 0 10px rgba(120, 145, 160, 0.08),
    inset 0 0 0 1px rgba(255, 255, 255, 0.03);
  font-size: 12px;
}

.slot::before {
  content: '';
  position: absolute;
  left: 5px;
  top: 5px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: rgba(91, 109, 129, 0.75);
  box-shadow: 0 0 6px rgba(91, 109, 129, 0.2);
}

.slot--filled {
  color: #f1fcff;
  font-weight: 700;
  border-color: rgba(36, 216, 139, 0.68);
  background:
    linear-gradient(180deg, rgba(44, 143, 255, 0.34), rgba(36, 216, 139, 0.38)),
    rgba(8, 46, 50, 0.38);
  box-shadow:
    inset 0 0 0 1px rgba(126, 233, 255, 0.28),
    inset 0 0 16px rgba(36, 216, 139, 0.22),
    0 0 12px rgba(36, 216, 139, 0.18);
}

.slot--filled::before {
  background: var(--scada-green);
  box-shadow: 0 0 10px rgba(36, 216, 139, 0.72);
}

.event-head,
.event-row {
  display: grid;
  grid-template-columns: 52px 90px 1fr;
  align-items: center;
  gap: 8px;
  box-sizing: border-box;
}

.event-head {
  flex-shrink: 0;
  height: 22px;
  min-height: 22px;
  margin-bottom: 3px;
  padding: 0 10px;
  color: #a9cce0;
  border: 1px solid rgba(114, 206, 255, 0.18);
  background:
    linear-gradient(180deg, rgba(38, 83, 122, 0.42), rgba(12, 34, 56, 0.42)),
    rgba(21, 58, 94, 0.34);
  font-size: 12px;
}

.event-row {
  min-height: 30px;
  margin-bottom: 3px;
  padding: 5px 10px;
  font-size: 12px;
}

.event-row p,
.event-row strong {
  margin: 0;
  color: #d5edf8;
  line-height: 1.35;
}

.event-row p {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  line-height: 1.25;
}

.event-row > span:first-child {
  color: var(--scada-cyan);
  font-weight: 700;
  text-shadow: 0 0 8px rgba(114, 232, 255, 0.24);
}

.history-legend {
  flex-shrink: 0;
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  height: 18px;
  margin-bottom: 3px;
  padding: 0 10px;
  color: #b8d3e6;
  font-size: 12px;
}

.history-legend-item {
  display: flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
}

.history-legend-dot {
  width: 10px;
  height: 10px;
  border-radius: 2px;
}

.history-legend-dot--run {
  background: rgba(36, 216, 139, 0.9);
  box-shadow: 0 0 8px rgba(36, 216, 139, 0.42);
}

.history-legend-dot--standby {
  background: rgba(243, 198, 78, 0.9);
  box-shadow: 0 0 8px rgba(243, 198, 78, 0.36);
}

.history-legend-dot--off {
  background: rgba(92, 111, 130, 0.9);
}

.history-legend-dot--alarm {
  background: rgba(255, 82, 102, 0.9);
  box-shadow: 0 0 8px rgba(255, 82, 102, 0.38);
}

.history-head,
.history-row {
  display: grid;
  grid-template-columns: 132px 1fr;
  align-items: center;
  gap: 10px;
  box-sizing: border-box;
}

.history-head {
  flex-shrink: 0;
  height: 22px;
  margin-bottom: 3px;
  padding: 0 10px;
  color: #a9cce0;
  border: 1px solid rgba(114, 206, 255, 0.18);
  background:
    linear-gradient(180deg, rgba(38, 83, 122, 0.42), rgba(12, 34, 56, 0.42)),
    rgba(21, 58, 94, 0.34);
  font-size: 12px;
}

.history-row {
  min-height: 30px;
  margin-bottom: 3px;
  padding: 4px 10px;
  border: 1px solid rgba(114, 206, 255, 0.18);
  background:
    linear-gradient(180deg, rgba(20, 42, 62, 0.56), rgba(4, 14, 27, 0.72)),
    rgba(5, 20, 44, 0.66);
  box-shadow: inset 0 0 14px rgba(42, 133, 205, 0.12);
  font-size: 12px;
}

.history-row::before {
  left: 0;
  top: 0;
  bottom: 0;
  width: 2px;
  background: linear-gradient(180deg, transparent, rgba(114, 232, 255, 0.42), transparent);
  opacity: 0.7;
}

.history-device {
  min-width: 0;
}

.history-device strong {
  color: #fff;
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.history-scale {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: #8fb8d3;
  font-size: 11px;
}

.history-scale span {
  white-space: nowrap;
}

.history-track {
  position: relative;
  height: 16px;
  min-width: 0;
  overflow: hidden;
  border: 1px solid rgba(114, 206, 255, 0.13);
  background:
    repeating-linear-gradient(
      to right,
      rgba(114, 206, 255, 0.26) 0,
      rgba(114, 206, 255, 0.26) 1px,
      transparent 1px,
      transparent calc(100% / 6)
    ),
    rgba(0, 0, 0, 0.2);
}

.history-segment {
  position: absolute;
  top: 2px;
  bottom: 2px;
  min-width: 4px;
  overflow: hidden;
}

.history-segment--run {
  color: #eafff2;
  background: rgba(36, 216, 139, 0.72);
}

.history-segment--standby {
  color: #fff2b0;
  background: rgba(243, 198, 78, 0.58);
}

.history-segment--off {
  color: #b5c8d5;
  background: rgba(92, 111, 130, 0.5);
}

.history-segment--alarm {
  color: #fff0f1;
  background: rgba(255, 82, 102, 0.72);
}

</style>
