<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>U 位色块视图方案预览</title>
    <style>
      :root {
        color: #1d2129;
        background: #f2f3f5;
        font-family:
          Inter, "PingFang SC", "Microsoft YaHei", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
        font-synthesis: none;
        --primary: #165dff;
        --primary-light: #e8f3ff;
        --available: #00b42a;
        --available-bg: #e8ffea;
        --occupied: #165dff;
        --occupied-bg: #e8f3ff;
        --reserved: #ff7d00;
        --reserved-bg: #fff7e8;
        --disabled: #86909c;
        --disabled-bg: #f2f3f5;
        --border: #e5e6eb;
        --muted: #86909c;
      }

      * {
        box-sizing: border-box;
      }

      body {
        margin: 0;
        min-width: 1080px;
        min-height: 100vh;
        background: #f2f3f5;
      }

      button,
      select {
        font: inherit;
      }

      button {
        cursor: pointer;
      }

      .prototype-mark {
        position: fixed;
        top: 18px;
        right: 22px;
        z-index: 10;
        padding: 5px 10px;
        border: 1px solid #ffb65d;
        border-radius: 4px;
        color: #b54708;
        background: #fff7e8;
        font-size: 12px;
        font-weight: 600;
      }

      .app-shell {
        display: grid;
        grid-template-columns: 208px minmax(0, 1fr);
        min-height: 100vh;
      }

      .sidebar {
        position: sticky;
        top: 0;
        height: 100vh;
        color: #c9cdd4;
        background: #232324;
      }

      .brand {
        display: flex;
        align-items: center;
        gap: 10px;
        height: 58px;
        padding: 0 20px;
        color: #fff;
        font-size: 16px;
        font-weight: 600;
        border-bottom: 1px solid rgba(255, 255, 255, 0.08);
      }

      .brand-logo {
        display: grid;
        place-items: center;
        width: 26px;
        height: 26px;
        border-radius: 6px;
        background: var(--primary);
        font-size: 12px;
      }

      .menu {
        padding: 14px 10px;
      }

      .menu-group {
        padding: 10px 12px 6px;
        color: #6b7785;
        font-size: 12px;
      }

      .menu-item {
        display: flex;
        align-items: center;
        gap: 10px;
        height: 40px;
        padding: 0 12px;
        border-radius: 4px;
        font-size: 14px;
      }

      .menu-item.active {
        color: #fff;
        background: var(--primary);
      }

      .menu-icon {
        width: 16px;
        text-align: center;
        opacity: 0.85;
      }

      .main {
        min-width: 0;
      }

      .topbar {
        display: flex;
        align-items: center;
        height: 58px;
        padding: 0 24px;
        background: #fff;
        border-bottom: 1px solid var(--border);
      }

      .breadcrumbs {
        color: var(--muted);
        font-size: 13px;
      }

      .breadcrumbs strong {
        color: #1d2129;
        font-weight: 500;
      }

      .page {
        padding: 20px 24px 100px;
      }

      .page-title-row {
        display: flex;
        align-items: center;
        justify-content: space-between;
        margin-bottom: 16px;
      }

      .page-title {
        margin: 0;
        font-size: 20px;
        font-weight: 600;
      }

      .page-note {
        margin-top: 5px;
        color: var(--muted);
        font-size: 13px;
      }

      .card {
        border: 1px solid var(--border);
        border-radius: 6px;
        background: #fff;
        box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02);
      }

      .selector-card {
        margin-bottom: 16px;
        padding: 16px 20px;
      }

      .card-heading {
        display: flex;
        align-items: center;
        gap: 8px;
        margin-bottom: 14px;
        font-size: 15px;
        font-weight: 600;
      }

      .selectors {
        display: grid;
        grid-template-columns: repeat(4, minmax(160px, 1fr));
        gap: 16px;
      }

      .select-box {
        display: flex;
        align-items: center;
        justify-content: space-between;
        height: 34px;
        padding: 0 12px;
        border: 1px solid #c9cdd4;
        border-radius: 4px;
        color: #4e5969;
        background: #fff;
        font-size: 13px;
      }

      .stats {
        display: flex;
        flex-wrap: wrap;
        gap: 8px;
        margin-top: 14px;
      }

      .tag {
        display: inline-flex;
        align-items: center;
        height: 24px;
        padding: 0 9px;
        border-radius: 3px;
        color: #4e5969;
        background: #f2f3f5;
        font-size: 12px;
      }

      .tag.blue {
        color: #0e42d2;
        background: var(--occupied-bg);
      }

      .tag.orange {
        color: #b54708;
        background: var(--reserved-bg);
      }

      .tag.green {
        color: #009a29;
        background: var(--available-bg);
      }

      .toolbar-card {
        display: flex;
        align-items: center;
        justify-content: space-between;
        min-height: 58px;
        margin-bottom: 16px;
        padding: 11px 20px;
      }

      .toolbar,
      .view-mode {
        display: flex;
        align-items: center;
        gap: 10px;
      }

      .button {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        gap: 6px;
        height: 34px;
        padding: 0 14px;
        border: 1px solid #c9cdd4;
        border-radius: 4px;
        color: #4e5969;
        background: #fff;
        font-size: 13px;
      }

      .button:hover {
        color: var(--primary);
        border-color: var(--primary);
      }

      .button.primary {
        color: #fff;
        border-color: var(--primary);
        background: var(--primary);
      }

      .button.danger {
        color: #f53f3f;
        border-color: #ffccc7;
        background: #fff;
      }

      .button.small {
        height: 30px;
        padding: 0 11px;
        font-size: 12px;
      }

      .divider {
        width: 1px;
        height: 22px;
        margin: 0 2px;
        background: var(--border);
      }

      .mode-button {
        color: var(--primary);
        border-color: #94bfff;
        background: #f2f7ff;
      }

      .content-card {
        min-height: 580px;
        overflow: hidden;
      }

      .content-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        min-height: 56px;
        padding: 0 20px;
        border-bottom: 1px solid var(--border);
      }

      .content-title {
        display: flex;
        align-items: center;
        gap: 8px;
        font-size: 15px;
        font-weight: 600;
      }

      .legend {
        display: flex;
        align-items: center;
        gap: 18px;
        color: #4e5969;
        font-size: 12px;
      }

      .legend-item {
        display: flex;
        align-items: center;
        gap: 6px;
      }

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

      .variant-description {
        color: var(--muted);
        font-size: 12px;
      }

      .unit-tile {
        position: relative;
        min-width: 0;
        border: 1px solid transparent;
        border-radius: 5px;
        text-align: left;
        transition:
          transform 0.12s ease,
          box-shadow 0.12s ease,
          border-color 0.12s ease;
      }

      .unit-tile:hover {
        z-index: 1;
        transform: translateY(-1px);
        box-shadow: 0 4px 12px rgba(78, 89, 105, 0.15);
      }

      .unit-tile.available {
        color: #006d22;
        border-color: #7be188;
        background: var(--available-bg);
      }

      .unit-tile.occupied {
        color: #0e42d2;
        border-color: #94bfff;
        background: var(--occupied-bg);
      }

      .unit-tile.reserved {
        color: #b54708;
        border-color: #ffb65d;
        background: var(--reserved-bg);
      }

      .unit-tile.disabled {
        color: #4e5969;
        border-color: #c9cdd4;
        background:
          repeating-linear-gradient(135deg, transparent 0, transparent 6px, rgba(134, 144, 156, 0.1) 6px, rgba(134, 144, 156, 0.1) 10px),
          var(--disabled-bg);
      }

      .unit-tile.selected {
        outline: 3px solid rgba(114, 46, 209, 0.72);
        outline-offset: 1px;
        box-shadow: 0 0 0 5px rgba(114, 46, 209, 0.11);
      }

      .unit-number {
        font-weight: 700;
      }

      .unit-state {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      .inspector {
        padding: 20px;
        background: #fafafa;
        border-left: 1px solid var(--border);
      }

      .inspector-title {
        display: flex;
        align-items: center;
        justify-content: space-between;
        margin-bottom: 18px;
      }

      .inspector-title strong {
        font-size: 16px;
      }

      .status-pill {
        display: inline-flex;
        align-items: center;
        height: 24px;
        padding: 0 9px;
        border-radius: 12px;
        font-size: 12px;
      }

      .status-pill.available {
        color: #006d22;
        background: var(--available-bg);
      }

      .status-pill.occupied {
        color: #0e42d2;
        background: var(--occupied-bg);
      }

      .status-pill.reserved {
        color: #b54708;
        background: var(--reserved-bg);
      }

      .status-pill.disabled {
        color: #4e5969;
        background: var(--disabled-bg);
      }

      .detail-list {
        margin: 0;
      }

      .detail-row {
        display: grid;
        grid-template-columns: 78px 1fr;
        gap: 10px;
        padding: 11px 0;
        border-bottom: 1px solid #eee;
        font-size: 13px;
      }

      .detail-row dt {
        color: var(--muted);
      }

      .detail-row dd {
        min-width: 0;
        margin: 0;
        color: #1d2129;
        font-weight: 500;
        word-break: break-all;
      }

      .action-title {
        margin: 22px 0 10px;
        color: #4e5969;
        font-size: 12px;
        font-weight: 600;
      }

      .actions {
        display: flex;
        flex-wrap: wrap;
        gap: 8px;
      }

      .empty-detail {
        display: grid;
        place-items: center;
        min-height: 350px;
        color: var(--muted);
        font-size: 13px;
        text-align: center;
      }

      /* 方案 A：矩阵与常驻侧栏 */
      .variant-a {
        display: grid;
        grid-template-columns: minmax(620px, 1fr) 300px;
        min-height: 524px;
      }

      .matrix-area {
        padding: 20px;
      }

      .matrix-grid {
        display: grid;
        grid-template-columns: repeat(6, minmax(84px, 1fr));
        gap: 10px;
      }

      .matrix-grid .unit-tile {
        display: flex;
        flex-direction: column;
        justify-content: space-between;
        height: 58px;
        padding: 9px 10px;
      }

      .matrix-grid .unit-number {
        font-size: 14px;
      }

      .matrix-grid .unit-state {
        font-size: 11px;
        opacity: 0.86;
      }

      /* 方案 B：状态分组看板 */
      .variant-b {
        padding: 20px;
      }

      .status-board {
        display: grid;
        grid-template-columns: repeat(4, minmax(0, 1fr));
        gap: 14px;
      }

      .status-column {
        min-height: 438px;
        padding: 14px;
        border: 1px solid var(--border);
        border-radius: 6px;
        background: #fafafa;
      }

      .column-title {
        display: flex;
        align-items: center;
        justify-content: space-between;
        margin-bottom: 12px;
        color: #4e5969;
        font-size: 13px;
        font-weight: 600;
      }

      .column-count {
        display: grid;
        place-items: center;
        min-width: 24px;
        height: 20px;
        padding: 0 6px;
        border-radius: 10px;
        color: #fff;
        background: #86909c;
        font-size: 11px;
      }

      .status-units {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: 8px;
      }

      .status-units .unit-tile {
        display: grid;
        place-items: center;
        height: 44px;
        padding: 4px;
        font-size: 12px;
      }

      .board-selection {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        align-items: center;
        gap: 16px;
        min-height: 70px;
        margin-top: 14px;
        padding: 12px 16px;
        border: 1px solid #d6e4ff;
        border-radius: 6px;
        background: #f7faff;
      }

      .selection-summary {
        display: flex;
        align-items: center;
        gap: 18px;
        min-width: 0;
      }

      .selection-main {
        min-width: 100px;
      }

      .selection-main strong {
        display: block;
        margin-bottom: 3px;
        font-size: 15px;
      }

      .selection-main span,
      .selection-meta {
        color: var(--muted);
        font-size: 12px;
      }

      .selection-meta {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      /* 方案 C：紧凑总览与底部操作条 */
      .variant-c {
        padding: 20px;
      }

      .overview-head {
        display: flex;
        align-items: flex-start;
        justify-content: space-between;
        gap: 20px;
        margin-bottom: 16px;
      }

      .overview-title strong {
        display: block;
        margin-bottom: 5px;
        font-size: 15px;
      }

      .overview-title span {
        color: var(--muted);
        font-size: 12px;
      }

      .overview-filter {
        display: flex;
        gap: 6px;
      }

      .filter-chip {
        height: 28px;
        padding: 0 10px;
        border: 1px solid var(--border);
        border-radius: 14px;
        color: #4e5969;
        background: #fff;
        font-size: 12px;
      }

      .filter-chip.active {
        color: var(--primary);
        border-color: #94bfff;
        background: var(--primary-light);
      }

      .heatmap-grid {
        display: grid;
        grid-template-columns: repeat(14, minmax(0, 1fr));
        gap: 8px;
        padding: 18px;
        border: 1px solid var(--border);
        border-radius: 6px;
        background: #fafafa;
      }

      .heatmap-grid .unit-tile {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        aspect-ratio: 1 / 1;
        min-height: 50px;
        padding: 4px;
      }

      .heatmap-grid .unit-number {
        font-size: 12px;
      }

      .heatmap-grid .unit-state {
        max-width: 100%;
        margin-top: 3px;
        font-size: 9px;
        opacity: 0.8;
      }

      .overview-note {
        display: flex;
        align-items: center;
        justify-content: space-between;
        margin-top: 12px;
        color: var(--muted);
        font-size: 12px;
      }

      .floating-action-panel {
        display: grid;
        grid-template-columns: 180px minmax(0, 1fr) auto;
        align-items: center;
        gap: 18px;
        min-height: 86px;
        margin-top: 20px;
        padding: 14px 18px;
        border: 1px solid #d6e4ff;
        border-radius: 8px;
        background: linear-gradient(135deg, #f7faff 0%, #fff 65%);
        box-shadow: 0 6px 18px rgba(22, 93, 255, 0.08);
      }

      .selected-block {
        display: flex;
        align-items: center;
        gap: 12px;
      }

      .selected-swatch {
        display: grid;
        place-items: center;
        width: 46px;
        height: 46px;
        border-radius: 6px;
        color: #fff;
        background: #722ed1;
        font-size: 12px;
        font-weight: 700;
      }

      .selected-copy strong {
        display: block;
        margin-bottom: 4px;
      }

      .selected-copy span {
        color: var(--muted);
        font-size: 12px;
      }

      .selected-description {
        min-width: 0;
        color: #4e5969;
        font-size: 13px;
      }

      .selected-description strong {
        color: #1d2129;
      }

      .prototype-switcher {
        position: fixed;
        bottom: 22px;
        left: 50%;
        z-index: 20;
        display: grid;
        grid-template-columns: 38px minmax(210px, auto) 38px;
        align-items: center;
        padding: 6px;
        border: 1px solid rgba(255, 255, 255, 0.16);
        border-radius: 24px;
        color: #fff;
        background: rgba(29, 33, 41, 0.95);
        box-shadow: 0 8px 30px rgba(0, 0, 0, 0.26);
        transform: translateX(-50%);
        backdrop-filter: blur(8px);
      }

      .switch-arrow {
        display: grid;
        place-items: center;
        width: 34px;
        height: 34px;
        padding: 0;
        border: 0;
        border-radius: 50%;
        color: #fff;
        background: rgba(255, 255, 255, 0.1);
        font-size: 18px;
      }

      .switch-arrow:hover {
        background: rgba(255, 255, 255, 0.2);
      }

      .switch-label {
        padding: 0 12px;
        text-align: center;
        font-size: 13px;
        font-weight: 600;
      }

      .toast {
        position: fixed;
        top: 74px;
        left: 50%;
        z-index: 30;
        display: none;
        padding: 10px 16px;
        border-radius: 5px;
        color: #1d2129;
        background: #fff;
        box-shadow: 0 6px 18px rgba(0, 0, 0, 0.15);
        font-size: 13px;
        transform: translateX(-50%);
      }

      .toast.show {
        display: block;
        animation: fade-in 0.16s ease;
      }

      @keyframes fade-in {
        from {
          opacity: 0;
          transform: translate(-50%, -6px);
        }
        to {
          opacity: 1;
          transform: translate(-50%, 0);
        }
      }

      @media (max-width: 1280px) {
        body {
          min-width: 980px;
        }

        .app-shell {
          grid-template-columns: 176px minmax(0, 1fr);
        }

        .matrix-grid {
          grid-template-columns: repeat(5, minmax(78px, 1fr));
        }

        .heatmap-grid {
          grid-template-columns: repeat(12, minmax(0, 1fr));
        }
      }
    </style>
  </head>
  <body>
    <!-- 三套 U 位色块视图方案，通过 ?variant=A|B|C 切换，仅用于设计评审。 -->
    <div class="prototype-mark">临时原型，不接真实接口</div>
    <div class="app-shell">
      <aside class="sidebar">
        <div class="brand"><span class="brand-logo">DC</span><span>运维管理平台</span></div>
        <nav class="menu">
          <div class="menu-group">数据中心</div>
          <div class="menu-item"><span class="menu-icon">◇</span><span>数据中心管理</span></div>
          <div class="menu-item"><span class="menu-icon">□</span><span>楼层管理</span></div>
          <div class="menu-item"><span class="menu-icon">▦</span><span>机房管理</span></div>
          <div class="menu-item"><span class="menu-icon">▥</span><span>机柜管理</span></div>
          <div class="menu-item active"><span class="menu-icon">▦</span><span>U 位管理</span></div>
        </nav>
      </aside>

      <main class="main">
        <header class="topbar">
          <div class="breadcrumbs">数据中心 / 机柜管理 / <strong>U 位管理</strong></div>
        </header>

        <div class="page">
          <div class="page-title-row">
            <div>
              <h1 class="page-title">U 位管理</h1>
              <div class="page-note">选择机柜后查看并操作每个 U 位的当前状态</div>
            </div>
          </div>

          <section class="card selector-card">
            <div class="card-heading"><span>▦</span><span>选择机柜</span></div>
            <div class="selectors">
              <div class="select-box"><span>华东数据中心</span><span>⌄</span></div>
              <div class="select-box"><span>一层</span><span>⌄</span></div>
              <div class="select-box"><span>A 区机房</span><span>⌄</span></div>
              <div class="select-box"><span>A01 核心机柜</span><span>⌄</span></div>
            </div>
            <div class="stats">
              <span class="tag">总 U 位：42</span>
              <span class="tag blue">已使用：9</span>
              <span class="tag orange">已预留：4</span>
              <span class="tag">已禁用：2</span>
              <span class="tag green">空余：27</span>
              <span class="tag orange">使用率：21%</span>
            </div>
          </section>

          <section class="card toolbar-card">
            <div class="toolbar">
              <button class="button primary" data-action="分配 U 位"><span>＋</span><span>分配 U 位</span></button>
              <button class="button" data-action="预留 U 位"><span>◇</span><span>预留 U 位</span></button>
              <button class="button" data-action="刷新"><span>↻</span><span>刷新</span></button>
              <span class="divider"></span>
              <button class="button mode-button"><span>▦</span><span>列表视图</span></button>
            </div>
            <div class="view-mode"><span class="tag blue">当前：图形视图</span></div>
          </section>

          <section class="card content-card">
            <div class="content-header">
              <div>
                <div class="content-title"><span>▦</span><span id="content-title">U 位色块矩阵</span></div>
                <div id="variant-description" class="variant-description"></div>
              </div>
              <div class="legend">
                <span class="legend-item"><i class="legend-dot" style="background: var(--available)"></i>可用</span>
                <span class="legend-item"><i class="legend-dot" style="background: var(--occupied)"></i>已占用</span>
                <span class="legend-item"><i class="legend-dot" style="background: var(--reserved)"></i>已预留</span>
                <span class="legend-item"><i class="legend-dot" style="background: var(--disabled)"></i>已禁用</span>
              </div>
            </div>
            <div id="variant-root"></div>
          </section>
        </div>
      </main>
    </div>

    <div id="prototype-switcher" class="prototype-switcher">
      <button id="previous-variant" class="switch-arrow" title="上一个方案">←</button>
      <div id="switch-label" class="switch-label"></div>
      <button id="next-variant" class="switch-arrow" title="下一个方案">→</button>
    </div>
    <div id="toast" class="toast"></div>

    <script>
      const variantDefinitions = {
        A: {
          name: '矩阵与常驻侧栏',
          title: 'U 位色块矩阵',
          description: '按 U 位顺序展示，右侧始终保留详情和操作。',
        },
        B: {
          name: '状态分组看板',
          title: '按状态分组',
          description: '直接找到可用、占用、预留或禁用 U 位，选中后在下方操作。',
        },
        C: {
          name: '紧凑总览',
          title: 'U 位紧凑总览',
          description: '42 个 U 位一屏展示，底部操作条承载选中信息和操作。',
        },
      }

      const statusMeta = {
        available: { label: '可用', shortLabel: '可用' },
        occupied: { label: '已占用', shortLabel: '占用' },
        reserved: { label: '已预留', shortLabel: '预留' },
        disabled: { label: '已禁用', shortLabel: '禁用' },
      }

      const occupiedGroups = [
        { units: [42, 41], groupId: 'asset-switch', assetName: '核心交换机 SW-01', assetCode: 'NET-SW-001', assetType: '网络设备', power: '320 W' },
        { units: [36, 35, 34, 33], groupId: 'asset-server', assetName: '虚拟化服务器 HV-03', assetCode: 'SRV-HV-003', assetType: '服务器', power: '860 W' },
        { units: [18, 17, 16], groupId: 'asset-storage', assetName: '存储阵列 ST-02', assetCode: 'STG-002', assetType: '存储设备', power: '680 W' },
      ]
      const reservedGroups = [
        { units: [30, 29, 28], groupId: 'reserve-project', reservedFor: '数据库扩容项目', reservedUntil: '2026-09-30 18:00' },
        { units: [8], groupId: 'reserve-network', reservedFor: '网络组临时预留', reservedUntil: '2026-08-31 18:00' },
      ]
      const disabledNumbers = new Set([3, 2])

      const units = Array.from({ length: 42 }, (_, index) => {
        const number = 42 - index
        const occupied = occupiedGroups.find((group) => group.units.includes(number))
        if (occupied) {
          return {
            number,
            status: 'occupied',
            groupId: occupied.groupId,
            range: `${Math.max(...occupied.units)}U～${Math.min(...occupied.units)}U`,
            assetName: occupied.assetName,
            assetCode: occupied.assetCode,
            assetType: occupied.assetType,
            power: occupied.power,
          }
        }

        const reserved = reservedGroups.find((group) => group.units.includes(number))
        if (reserved) {
          return {
            number,
            status: 'reserved',
            groupId: reserved.groupId,
            range: reserved.units.length > 1 ? `${Math.max(...reserved.units)}U～${Math.min(...reserved.units)}U` : `${number}U`,
            reservedFor: reserved.reservedFor,
            reservedUntil: reserved.reservedUntil,
          }
        }

        if (disabledNumbers.has(number)) {
          return { number, status: 'disabled', groupId: `disabled-${number}`, range: `${number}U` }
        }

        return { number, status: 'available', groupId: `available-${number}`, range: `${number}U` }
      })

      const root = document.querySelector('#variant-root')
      const switchLabel = document.querySelector('#switch-label')
      const contentTitle = document.querySelector('#content-title')
      const variantDescription = document.querySelector('#variant-description')
      const toast = document.querySelector('#toast')
      const variants = Object.keys(variantDefinitions)
      let selectedUnit = units.find((unit) => unit.number === 39)
      let activeFilter = 'all'
      let toastTimer

      function getVariant() {
        const candidate = new URLSearchParams(window.location.search).get('variant')?.toUpperCase()
        return variants.includes(candidate) ? candidate : 'A'
      }

      function isSelected(unit) {
        return selectedUnit && unit.groupId === selectedUnit.groupId
      }

      function unitTitle(unit) {
        if (unit.status === 'occupied') return `${unit.number}U · ${unit.assetName}`
        if (unit.status === 'reserved') return `${unit.number}U · ${unit.reservedFor}`
        return `${unit.number}U · ${statusMeta[unit.status].label}`
      }

      function unitSecondary(unit) {
        if (unit.status === 'occupied') return unit.assetCode
        if (unit.status === 'reserved') return unit.reservedFor
        return statusMeta[unit.status].label
      }

      function unitButton(unit, mode) {
        const secondary = unitSecondary(unit)
        if (mode === 'matrix') {
          return `
            <button class="unit-tile ${unit.status} ${isSelected(unit) ? 'selected' : ''}" data-unit="${unit.number}" title="${unitTitle(unit)}">
              <span class="unit-number">U${unit.number}</span>
              <span class="unit-state">${secondary}</span>
            </button>
          `
        }

        if (mode === 'heatmap') {
          return `
            <button class="unit-tile ${unit.status} ${isSelected(unit) ? 'selected' : ''}" data-unit="${unit.number}" title="${unitTitle(unit)}">
              <span class="unit-number">U${unit.number}</span>
              <span class="unit-state">${statusMeta[unit.status].shortLabel}</span>
            </button>
          `
        }

        return `
          <button class="unit-tile ${unit.status} ${isSelected(unit) ? 'selected' : ''}" data-unit="${unit.number}" title="${unitTitle(unit)}">
            <span class="unit-number">U${unit.number}</span>
          </button>
        `
      }

      function details(unit) {
        if (!unit) return '<div class="empty-detail">点击任意 U 位<br />查看状态和可用操作</div>'

        const commonRows = `
          <div class="detail-row"><dt>U 位</dt><dd>${unit.range}</dd></div>
          <div class="detail-row"><dt>状态</dt><dd>${statusMeta[unit.status].label}</dd></div>
        `
        let extraRows = ''
        if (unit.status === 'occupied') {
          extraRows = `
            <div class="detail-row"><dt>设备名称</dt><dd>${unit.assetName}</dd></div>
            <div class="detail-row"><dt>设备编号</dt><dd>${unit.assetCode}</dd></div>
            <div class="detail-row"><dt>设备类型</dt><dd>${unit.assetType}</dd></div>
            <div class="detail-row"><dt>功耗</dt><dd>${unit.power}</dd></div>
          `
        }
        if (unit.status === 'reserved') {
          extraRows = `
            <div class="detail-row"><dt>预留对象</dt><dd>${unit.reservedFor}</dd></div>
            <div class="detail-row"><dt>截止时间</dt><dd>${unit.reservedUntil}</dd></div>
          `
        }

        return `
          <div class="inspector-title">
            <strong>当前选中</strong>
            <span class="status-pill ${unit.status}">${statusMeta[unit.status].label}</span>
          </div>
          <dl class="detail-list">${commonRows}${extraRows}</dl>
          <div class="action-title">可用操作</div>
          <div class="actions">${actionButtons(unit)}</div>
        `
      }

      function actionButtons(unit) {
        if (!unit) return ''
        if (unit.status === 'available') {
          return `
            <button class="button primary small" data-action="分配 U${unit.number}">分配</button>
            <button class="button small" data-action="预留 U${unit.number}">预留</button>
            <button class="button small" data-action="禁用 U${unit.number}">禁用</button>
          `
        }
        if (unit.status === 'occupied') {
          return `
            <button class="button small" data-action="查看 ${unit.assetName}">查看设备</button>
            <button class="button danger small" data-action="释放 ${unit.range}">释放</button>
          `
        }
        if (unit.status === 'reserved') {
          return `<button class="button danger small" data-action="取消预留 ${unit.range}">取消预留</button>`
        }
        return `<button class="button small" data-action="启用 U${unit.number}">启用</button>`
      }

      function renderVariantA() {
        return `
          <div class="variant-a">
            <div class="matrix-area">
              <div class="matrix-grid">${units.map((unit) => unitButton(unit, 'matrix')).join('')}</div>
            </div>
            <aside class="inspector">${details(selectedUnit)}</aside>
          </div>
        `
      }

      function renderVariantB() {
        const statusOrder = ['available', 'occupied', 'reserved', 'disabled']
        const columns = statusOrder
          .map((status) => {
            const items = units.filter((unit) => unit.status === status)
            return `
              <section class="status-column">
                <div class="column-title"><span>${statusMeta[status].label}</span><span class="column-count">${items.length}</span></div>
                <div class="status-units">${items.map((unit) => unitButton(unit, 'group')).join('')}</div>
              </section>
            `
          })
          .join('')

        const selectionText = selectedUnit
          ? `${selectedUnit.status === 'occupied' ? selectedUnit.assetName : selectedUnit.status === 'reserved' ? selectedUnit.reservedFor : statusMeta[selectedUnit.status].label}`
          : '点击色块选择 U 位'
        return `
          <div class="variant-b">
            <div class="status-board">${columns}</div>
            <div class="board-selection">
              <div class="selection-summary">
                <div class="selection-main"><strong>${selectedUnit ? selectedUnit.range : '未选择'}</strong><span>${selectedUnit ? statusMeta[selectedUnit.status].label : '请选择 U 位'}</span></div>
                <div class="selection-meta">${selectionText}</div>
              </div>
              <div class="actions">${actionButtons(selectedUnit)}</div>
            </div>
          </div>
        `
      }

      function renderVariantC() {
        const filters = [
          ['all', '全部'],
          ['available', '可用'],
          ['occupied', '占用'],
          ['reserved', '预留'],
          ['disabled', '禁用'],
        ]
        const visibleUnits = activeFilter === 'all' ? units : units.filter((unit) => unit.status === activeFilter)
        const filterButtons = filters
          .map(([value, label]) => `<button class="filter-chip ${activeFilter === value ? 'active' : ''}" data-filter="${value}">${label}</button>`)
          .join('')
        const summary = selectedUnit
          ? selectedUnit.status === 'occupied'
            ? `<strong>${selectedUnit.assetName}</strong>，${selectedUnit.assetCode}，功耗 ${selectedUnit.power}`
            : selectedUnit.status === 'reserved'
              ? `<strong>${selectedUnit.reservedFor}</strong>，截止 ${selectedUnit.reservedUntil}`
              : `<strong>${statusMeta[selectedUnit.status].label}</strong>，可直接执行对应操作`
          : '点击上方 U 位色块后显示操作'

        return `
          <div class="variant-c">
            <div class="overview-head">
              <div class="overview-title"><strong>A01 核心机柜 · 42U</strong><span>色块按 U42 至 U1 排列，点击后选中关联设备或预留区间</span></div>
              <div class="overview-filter">${filterButtons}</div>
            </div>
            <div class="heatmap-grid">${visibleUnits.map((unit) => unitButton(unit, 'heatmap')).join('')}</div>
            <div class="overview-note"><span>当前显示 ${visibleUnits.length} 个 U 位</span><span>紫色描边表示当前选择</span></div>
            <div class="floating-action-panel">
              <div class="selected-block">
                <div class="selected-swatch">${selectedUnit ? selectedUnit.range.replace('U～', '–').replaceAll('U', '') + 'U' : '-'}</div>
                <div class="selected-copy"><strong>${selectedUnit ? selectedUnit.range : '未选择'}</strong><span>${selectedUnit ? statusMeta[selectedUnit.status].label : '请选择 U 位'}</span></div>
              </div>
              <div class="selected-description">${summary}</div>
              <div class="actions">${actionButtons(selectedUnit)}</div>
            </div>
          </div>
        `
      }

      function render() {
        const variant = getVariant()
        const definition = variantDefinitions[variant]
        contentTitle.textContent = definition.title
        variantDescription.textContent = definition.description
        switchLabel.textContent = `${variant} — ${definition.name}`

        if (variant === 'A') root.innerHTML = renderVariantA()
        if (variant === 'B') root.innerHTML = renderVariantB()
        if (variant === 'C') root.innerHTML = renderVariantC()

        bindInteractiveElements()
      }

      function bindInteractiveElements() {
        document.querySelectorAll('[data-unit]').forEach((button) => {
          button.addEventListener('click', () => {
            selectedUnit = units.find((unit) => unit.number === Number(button.dataset.unit))
            render()
          })
        })

        document.querySelectorAll('[data-action]').forEach((button) => {
          button.addEventListener('click', () => showToast(`原型操作：${button.dataset.action}`))
        })

        document.querySelectorAll('[data-filter]').forEach((button) => {
          button.addEventListener('click', () => {
            activeFilter = button.dataset.filter
            render()
          })
        })
      }

      function showToast(message) {
        window.clearTimeout(toastTimer)
        toast.textContent = message
        toast.classList.add('show')
        toastTimer = window.setTimeout(() => toast.classList.remove('show'), 1800)
      }

      function switchVariant(offset) {
        const currentIndex = variants.indexOf(getVariant())
        const nextIndex = (currentIndex + offset + variants.length) % variants.length
        const params = new URLSearchParams(window.location.search)
        params.set('variant', variants[nextIndex])
        window.history.replaceState({}, '', `${window.location.pathname}?${params.toString()}`)
        activeFilter = 'all'
        render()
      }

      document.querySelector('#previous-variant').addEventListener('click', () => switchVariant(-1))
      document.querySelector('#next-variant').addEventListener('click', () => switchVariant(1))
      window.addEventListener('keydown', (event) => {
        const target = event.target
        if (target.matches('input, textarea, [contenteditable="true"]')) return
        if (event.key === 'ArrowLeft') switchVariant(-1)
        if (event.key === 'ArrowRight') switchVariant(1)
      })
      window.addEventListener('popstate', render)

      render()
    </script>
  </body>
</html>
