<template>
  <div v-if="isInitialLoading" class="admin-loading">
    <div class="spinner" />
    <p>正在加载监控数据...</p>
  </div>
  <div v-else class="admin-page">
    <header class="admin-header">
      <div>
        <h1>管理员监控面板</h1>
        <p>最近更新：{{ timeAgo(snapshot?.generated_at || overview.generated_at) }}</p>
      </div>
      <div class="header-actions">
        <label>
          <input type="checkbox" v-model="autoRefresh" /> 自动刷新
        </label>
        <button type="button" :disabled="refreshing" @click="handleManualRefresh">
          {{ refreshing ? '刷新中...' : '立即刷新' }}
        </button>
      </div>
    </header>

    <section v-if="bannerError" class="banner-error">
      <strong>刷新失败：</strong>
      <span>{{ bannerError }}</span>
    </section>

    <div class="admin-layout" v-if="snapshot">
      <aside class="admin-sidebar">
        <button
          v-for="tab in sectionTabs"
          :key="tab.id"
          type="button"
          :class="['sidebar-tab', { active: activeSection === tab.id }]"
          @click="activeSection = tab.id"
        >
          {{ tab.label }}
        </button>
      </aside>
      <main class="admin-main">
        <transition name="fade" mode="out-in">
          <section v-if="activeSection === 'overview'" key="overview" class="panel">
            <div class="metrics-grid">
              <div v-for="card in metricCards" :key="card.title" class="metric-card">
                <h3>{{ card.title }}</h3>
                <strong>{{ card.value }}</strong>
                <span>{{ card.sub }}</span>
              </div>
            </div>
          </section>
          <section v-else-if="activeSection === 'usage'" key="usage" class="panel">
            <h2>模型配额与趋势</h2>
            <div class="stats-row">
              <span>快速模式：{{ usageTotals.fast || 0 }}</span>
              <span>思考模式：{{ usageTotals.thinking || 0 }}</span>
              <span>搜索：{{ usageTotals.search || 0 }}</span>
            </div>
            <div class="token-summary">
              <div class="token-card">
                <p>累计输入 Token</p>
                <strong>{{ formatNumber(tokenTotals.input_tokens) }}</strong>
              </div>
              <div class="token-card">
                <p>累计输出 Token</p>
                <strong>{{ formatNumber(tokenTotals.output_tokens) }}</strong>
              </div>
              <div class="token-card">
                <p>总计 Token</p>
                <strong>{{ formatNumber(tokenTotals.total_tokens) }}</strong>
              </div>
            </div>
            <div class="token-breakdown">
              <div class="token-breakdown-header">
                <h3>按用户累计 Token</h3>
                <span>实时拉取 · 按总量降序</span>
              </div>
              <div class="token-table-wrapper" v-if="tokenBreakdown.length">
                <table class="token-table">
                  <thead>
                    <tr>
                      <th>用户</th>
                      <th>角色</th>
                      <th>输入 Token</th>
                      <th>输出 Token</th>
                      <th>累计 Token</th>
                    </tr>
                  </thead>
                  <tbody>
                    <tr v-for="entry in tokenBreakdown" :key="entry.username">
                      <td>{{ entry.username }}</td>
                      <td>{{ entry.role }}</td>
                      <td>{{ formatNumber(entry.input) }}</td>
                      <td>{{ formatNumber(entry.output) }}</td>
                      <td>
                        <strong>{{ formatNumber(entry.total) }}</strong>
                      </td>
                    </tr>
                  </tbody>
                </table>
              </div>
              <p class="token-empty" v-else>暂无 token 统计数据</p>
            </div>
            <div class="usage-leaders">
              <div v-for="block in leaderBlocks" :key="block.key" class="leader-card">
                <h3>{{ block.label }}</h3>
                <ul>
                  <li v-if="!block.items.length" class="muted">暂无数据</li>
                  <li v-for="item in block.items" :key="item.username">
                    <strong>{{ item.username }}</strong>
                    <span>{{ item.count }} / {{ item.limit ?? '∞' }}</span>
                  </li>
                </ul>
              </div>
            </div>
          </section>
          <section v-else-if="activeSection === 'users'" key="users" class="panel">
            <h2>用户与配额</h2>
            <div class="table-wrapper">
              <table>
                <thead>
                  <tr>
                    <th>用户</th>
                    <th>角色</th>
                    <th>状态</th>
                    <th>项目占用</th>
                    <th>Fast</th>
                    <th>Thinking</th>
                    <th>Search</th>
                    <th>项目体积</th>
                    <th>最近活跃</th>
                  </tr>
                </thead>
                <tbody>
                  <tr v-if="!users.length">
                    <td colspan="9">暂无用户</td>
                  </tr>
                  <tr v-for="user in users" :key="user.username">
                    <td>
                      <strong>{{ user.username }}</strong>
                      <div class="subtext">{{ user.email || '—' }}</div>
                    </td>
                    <td>{{ user.role || 'user' }}</td>
                    <td>
                      <span :class="['status-badge', user.status?.online ? 'online' : 'offline']">
                        {{ user.status?.online ? '在线' : '离线' }}
                      </span>
                    </td>
                    <td>
                      <span :class="['status-badge', usageBadge(user.storage?.status)]">
                        {{ formatPercent(user.storage?.usage_percent) }}
                      </span>
                      <div class="progress-bar">
                        <span :style="{ width: percentWidth(user.storage?.usage_percent) }" />
                      </div>
                    </td>
                    <td>{{ user.usage?.fast?.count || 0 }}</td>
                    <td>{{ user.usage?.thinking?.count || 0 }}</td>
                    <td>{{ user.usage?.search?.count || 0 }}</td>
                    <td>{{ formatBytes(user.storage?.project_bytes) }}</td>
                    <td>{{ timeAgo(user.status?.last_active) }}</td>
                  </tr>
                </tbody>
              </table>
            </div>
          </section>
          <section v-else-if="activeSection === 'containers'" key="containers" class="panel">
            <h2>容器运行状态</h2>
            <div class="table-wrapper">
              <table>
                <thead>
                  <tr>
                    <th>用户</th>
                    <th>模式</th>
                    <th>容器名</th>
                    <th>CPU</th>
                    <th>内存%</th>
                    <th>内存使用</th>
                    <th>内存上限</th>
                    <th>网络 RX/TX</th>
                    <th>状态</th>
                    <th>最近活跃</th>
                  </tr>
                </thead>
                <tbody>
                  <tr v-if="!containers.length">
                    <td colspan="10">暂无容器运行</td>
                  </tr>
                  <tr v-for="item in containers" :key="item.username">
                    <td>{{ item.username }}</td>
                    <td>{{ item.mode || 'host' }}</td>
                    <td>{{ item.container_name || '—' }}</td>
                    <td>{{ formatPercentNumber(item.stats?.cpu_percent) }}</td>
                    <td>{{ formatPercentNumber(item.stats?.memory?.percent) }}</td>
                    <td>{{ formatBytes(item.stats?.memory?.used_bytes) }}</td>
                    <td>{{ formatBytes(item.stats?.memory?.limit_bytes) }}</td>
                    <td>{{ formatBytes(item.stats?.net_io?.rx_bytes) }} / {{ formatBytes(item.stats?.net_io?.tx_bytes) }}</td>
                    <td>
                      <span :class="['status-badge', containerStatusClass(item)]">
                        {{ item.state?.status || (item.state?.running ? '运行中' : '未知') }}
                      </span>
                    </td>
                    <td>{{ timeAgo(item.last_active) }}</td>
                  </tr>
                </tbody>
              </table>
            </div>
          </section>
          <section v-else-if="activeSection === 'uploads'" key="uploads" class="panel">
            <h2>上传安全审计</h2>
            <div class="stats-row">
              <span>最近24小时：{{ uploadStats.last_24h || 0 }}</span>
              <span>阻断：{{ uploadStats.blocked_last_24h || 0 }}</span>
              <span>跳过扫描：{{ uploadStats.skipped_scan_last_24h || 0 }}</span>
              <span>隔离区占用：{{ formatBytes(uploadStats.quarantine_bytes) }}</span>
            </div>
            <div class="stats-row">
              <span v-if="!uploadSources.length">暂无来源统计</span>
              <span v-for="source in uploadSources" :key="source.source">
                {{ source.source }}：{{ source.count }}
              </span>
            </div>
            <ul class="upload-feed">
              <li v-if="!recentUploads.length" class="upload-item">暂无近期上传</li>
              <li v-for="upload in recentUploads" :key="upload.upload_id" class="upload-item">
                <div>
                  <strong>{{ upload.original_name || '未命名文件' }}</strong>
                  <div class="upload-meta">
                    <span>用户：{{ upload.username }}</span>
                    <span>来源：{{ upload.source || 'unknown' }}</span>
                    <span>大小：{{ formatBytes(upload.size) }}</span>
                  </div>
                </div>
                <div class="upload-meta">
                  <span>{{ timeAgo(upload.timestamp) }}</span>
                  <span :class="['status-badge', upload.accepted ? 'online' : 'danger']">
                    {{ upload.accepted ? '通过' : '阻断' }}
                  </span>
                  <span v-if="upload.error?.message" class="status-badge danger">{{ upload.error.message }}</span>
                </div>
              </li>
            </ul>
          </section>
          <section v-else-if="activeSection === 'invites'" key="invites" class="panel">
            <h2>邀请码</h2>
            <div class="stats-row">
              <span>总计：{{ inviteSummary.total || 0 }}</span>
              <span>可用：{{ inviteSummary.active || 0 }}</span>
              <span>已用：{{ inviteSummary.consumed || 0 }}</span>
              <span>无限制：{{ inviteSummary.unlimited || 0 }}</span>
            </div>
            <div class="invite-grid">
              <div v-if="!inviteCodes.length" class="invite-card">暂无邀请码数据</div>
              <div v-for="code in inviteCodes" :key="code.code" class="invite-card">
                <h4>{{ code.code }}</h4>
                <span>{{ inviteStatus(code.remaining) }}</span>
              </div>
            </div>
          </section>
        </transition>
      </main>
    </div>

    <div v-else class="admin-error">
      <strong>加载失败</strong>
      <p>{{ errorMessage || '无法加载监控数据' }}</p>
      <button type="button" @click="handleRetry">重试</button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount, watch } from 'vue';

type Snapshot = Record<string, any> | null;

type SectionId = 'overview' | 'usage' | 'users' | 'containers' | 'uploads' | 'invites';

const loading = ref(true);
const refreshing = ref(false);
const snapshot = ref<Snapshot>(null);
const errorMessage = ref<string | null>(null);
const bannerError = ref<string | null>(null);
const autoRefresh = ref(true);
const activeSection = ref<SectionId>('overview');
let timer: number | undefined;

const sectionTabs: Array<{ id: SectionId; label: string }> = [
  { id: 'overview', label: '系统总览' },
  { id: 'usage', label: '配额趋势' },
  { id: 'users', label: '用户' },
  { id: 'containers', label: '容器' },
  { id: 'uploads', label: '上传审计' },
  { id: 'invites', label: '邀请码' }
];

const isInitialLoading = computed(() => loading.value && !snapshot.value);

const fetchDashboard = async (background = false) => {
  if (background) {
    refreshing.value = true;
  } else if (!snapshot.value) {
    loading.value = true;
  }
  try {
    const resp = await fetch('/api/admin/dashboard');
    if (!resp.ok) {
      throw new Error(`请求失败：${resp.status}`);
    }
    const payload = await resp.json();
    if (!payload.success) {
      throw new Error(payload.error || '未知错误');
    }
    snapshot.value = payload.data;
    errorMessage.value = null;
    bannerError.value = null;
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    if (!snapshot.value) {
      errorMessage.value = message;
    } else {
      bannerError.value = message;
    }
  } finally {
    if (background) {
      refreshing.value = false;
    }
    if (!snapshot.value) {
      loading.value = false;
    }
  }
};

const scheduleAutoRefresh = () => {
  if (timer) {
    clearInterval(timer);
    timer = undefined;
  }
  if (autoRefresh.value) {
    timer = window.setInterval(() => fetchDashboard(true), 30000);
  }
};

const handleManualRefresh = () => fetchDashboard(true);
const handleRetry = () => fetchDashboard(false);

watch(autoRefresh, () => {
  scheduleAutoRefresh();
});

onMounted(() => {
  fetchDashboard(false);
  scheduleAutoRefresh();
});

onBeforeUnmount(() => {
  if (timer) {
    clearInterval(timer);
  }
});

const snapshotData = computed(() => snapshot.value || null);
const overview = computed(() => snapshotData.value?.overview ?? {});
const usageTotals = computed(() => overview.value.usage_totals ?? {});
const tokenTotals = computed(() => overview.value.token_totals ?? {});
const leaderBlocks = computed(() => {
  const leaders = overview.value.usage_leaders ?? {};
  return [
    { key: 'fast', label: '快速模式 TOP', items: leaders.fast || [] },
    { key: 'thinking', label: '思考模式 TOP', items: leaders.thinking || [] },
    { key: 'search', label: '搜索调用 TOP', items: leaders.search || [] }
  ];
});
const storage = computed(() => overview.value.storage ?? {});
const uploadStats = computed(() => snapshotData.value?.uploads?.stats ?? {});
const uploadSources = computed(() => snapshotData.value?.uploads?.sources ?? []);
const recentUploads = computed(() => snapshotData.value?.uploads?.recent_events ?? []);
const users = computed(() => snapshotData.value?.users ?? []);
const containers = computed(() => snapshotData.value?.containers ?? []);
const inviteSummary = computed(() => snapshotData.value?.invites?.summary ?? {});
const inviteCodes = computed(() => snapshotData.value?.invites?.codes ?? []);
const totals = computed(() => overview.value.totals ?? {});
const containerSummary = computed(() => overview.value.containers ?? {});
const invitesOverview = computed(() => overview.value.invites ?? {});
const tokenBreakdown = computed(() => {
  const list = users.value.map((user: any) => ({
    username: user.username,
    role: user.role || 'user',
    input: Number(user.tokens?.input_tokens || 0),
    output: Number(user.tokens?.output_tokens || 0),
    total: Number(user.tokens?.total_tokens || 0)
  }));
  return list.sort((a, b) => b.total - a.total);
});

const metricCards = computed(() => [
  {
    title: '注册用户',
    value: totals.value.users || 0,
    sub: `${totals.value.active_users || 0} 个活跃`
  },
  {
    title: '容器使用',
    value: `${containerSummary.value.active || 0}/${containerSummary.value.max_containers ?? '∞'}`,
    sub:
      containerSummary.value.available_slots === null || containerSummary.value.available_slots === undefined
        ? '无限制'
        : `${containerSummary.value.available_slots} 个空闲`
  },
  {
    title: '资源告警',
    value: (storage.value.warning_users || []).length,
    sub: '达到预警阈值的工作区'
  },
  {
    title: '上传审计 (24h)',
    value: uploadStats.value.last_24h || 0,
    sub: `${uploadStats.value.blocked_last_24h || 0} 条被阻断`
  },
  {
    title: '配额调用 (fast / thinking / search)',
    value: `${usageTotals.value.fast || 0} / ${usageTotals.value.thinking || 0} / ${usageTotals.value.search || 0}`,
    sub: '窗口内累计调用'
  },
  {
    title: '邀请码',
    value: invitesOverview.value.total || 0,
    sub: `${invitesOverview.value.active || 0} 个可用 · ${invitesOverview.value.consumed || 0} 已用`
  },
  {
    title: '存储占用',
    value: formatBytes(storage.value.total_bytes || 0),
    sub: storage.value.per_user_limit_bytes
      ? `单用户上限 ${formatBytes(storage.value.per_user_limit_bytes)}`
      : '未设置上限'
  }
]);

const formatBytes = (value?: number | null) => {
  if (value === null || value === undefined || Number(value) <= 0) return '0 B';
  const units = ['B', 'KB', 'MB', 'GB', 'TB'];
  let val = Number(value);
  const index = Math.min(Math.floor(Math.log(val) / Math.log(1024)), units.length - 1);
  val /= 1024 ** index;
  const digits = val >= 100 ? 0 : 1;
  return `${val.toFixed(digits)} ${units[index]}`;
};

const percentWidth = (value?: number | null) => {
  if (value === null || value === undefined) return '0%';
  return `${Math.min(100, Math.max(0, Number(value)))}%`;
};

const formatPercent = (value?: number | null) => {
  if (value === null || value === undefined || isNaN(Number(value))) return '—';
  return `${Number(value).toFixed(1)}%`;
};

const formatNumber = (value?: number | null) => {
  if (value === null || value === undefined) return '0';
  const parsed = Number(value);
  if (Number.isNaN(parsed)) return '0';
  return parsed.toLocaleString();
};

const formatPercentNumber = (value?: number | null) => {
  if (value === null || value === undefined || isNaN(Number(value))) return '—';
  return `${Number(value).toFixed(1)}%`;
};

const timeAgo = (input?: string | number | null) => {
  if (!input) return '未知时间';
  const date = new Date(input);
  if (Number.isNaN(date.getTime())) return String(input);
  const diff = Date.now() - date.getTime();
  if (diff < 60_000) return '刚刚';
  const minutes = Math.floor(diff / 60_000);
  if (minutes < 60) return `${minutes} 分钟前`;
  const hours = Math.floor(minutes / 60);
  if (hours < 24) return `${hours} 小时前`;
  const days = Math.floor(hours / 24);
  return `${days} 天前`;
};

const usageBadge = (status?: string) => {
  if (status === 'critical') return 'danger';
  if (status === 'warning') return 'warning';
  return 'online';
};

const containerStatusClass = (item: any) => {
  if (item?.error) return 'danger';
  if (item?.state && item.state.running === false) return 'warning';
  return 'online';
};

const inviteStatus = (remaining: number | null | undefined) => {
  if (remaining === null || remaining === undefined) return '无限制';
  if (remaining > 0) return `剩余 ${remaining}`;
  return '已用完';
};
</script>

<style scoped>
:global(body) {
  margin: 0;
  background: #f7f3ea;
  font-family: 'Iowan Old Style', ui-serif, Georgia, Cambria, "Times New Roman", serif;
  color: #2a2013;
  overflow: hidden;
}

:global(#admin-app) {
  min-height: 100vh;
  height: 100vh;
  padding: 24px 32px;
  background: #f7f3ea;
  color: #2a2013;
  box-sizing: border-box;
}

.admin-page {
  max-width: 1400px;
  margin: 0 auto;
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.admin-header {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}

.admin-header h1 {
  margin: 0;
  font-size: 28px;
}

.admin-header p {
  margin: 4px 0 0;
  color: #6b5b44;
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 12px;
}

.header-actions label {
  display: flex;
  align-items: center;
  gap: 6px;
  color: #5b4b35;
}

button {
  border: none;
  border-radius: 999px;
  padding: 10px 18px;
  background: var(--claude-highlight);
  color: #3b2f1d;
  font-weight: 600;
  cursor: pointer;
  box-shadow: inset 0 -2px 0 rgba(189, 93, 58, 0.25);
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.2s ease;
}

button:disabled {
  opacity: 0.6;
  cursor: not-allowed;
  box-shadow: none;
}

button:not(:disabled):hover {
  transform: translateY(-1px);
  background: var(--claude-accent);
  box-shadow: 0 8px 16px rgba(189, 93, 58, 0.25);
}

.banner-error {
  padding: 12px 16px;
  border-radius: 14px;
  border: 1px solid rgba(189, 93, 58, 0.5);
  background: rgba(189, 93, 58, 0.15);
  color: #7c3418;
}

.admin-layout {
  display: flex;
  gap: 20px;
  flex: 1;
  min-height: 0;
}

.admin-sidebar {
  width: 220px;
  flex-shrink: 0;
  background: rgba(255, 255, 255, 0.85);
  border-radius: 24px;
  border: 1px solid rgba(118, 103, 84, 0.35);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.sidebar-tab {
  width: 100%;
  padding: 10px 12px;
  border-radius: 14px;
  border: 1px solid transparent;
  background: rgba(255, 255, 255, 0.7);
  color: #3c2f1d;
  text-align: left;
}

.sidebar-tab.active {
  background: rgba(218, 119, 86, 0.2);
  color: #3c2f1d;
  border-color: rgba(189, 93, 58, 0.55);
  box-shadow: 0 10px 22px rgba(189, 93, 58, 0.18);
  font-weight: 600;
}

.admin-main {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding-right: 8px;
}

.panel {
  background: #f6ecda;
  border-radius: 28px;
  padding: 24px;
  border: 1px solid rgba(118, 103, 84, 0.35);
  box-shadow: var(--claude-shadow);
  color: #23170b;
}

.metrics-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
}

.metric-card {
  background: rgba(255, 255, 255, 0.95);
  border-radius: 22px;
  padding: 18px;
  border: 1px solid rgba(118, 103, 84, 0.4);
}

.metric-card h3 {
  margin: 0;
  font-size: 13px;
  color: var(--claude-text-secondary);
  letter-spacing: 0.08em;
}

.metric-card strong {
  font-size: 28px;
  display: block;
  margin-top: 8px;
}

.metric-card span {
  color: #574630;
  font-size: 13px;
}

.stats-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 18px;
  color: #5c4a32;
}

.stats-row span {
  background: rgba(218, 119, 86, 0.2);
  padding: 6px 12px;
  border-radius: 999px;
}

.token-summary {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px;
  margin-bottom: 18px;
}

.token-card {
  border-radius: 18px;
  padding: 16px;
  background: rgba(255, 255, 255, 0.92);
  border: 1px solid rgba(44, 32, 19, 0.1);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6);
}

.token-card p {
  margin: 0;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--claude-text-secondary);
}

.token-card strong {
  display: block;
  margin-top: 10px;
  font-size: 22px;
  color: #1f160c;
}

.token-breakdown {
  border: 1px solid rgba(118, 103, 84, 0.25);
  border-radius: 20px;
  padding: 18px;
  background: rgba(255, 255, 255, 0.86);
  margin-bottom: 18px;
}

.token-breakdown-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}

.token-breakdown-header h3 {
  margin: 0;
  font-size: 18px;
}

.token-breakdown-header span {
  font-size: 12px;
  color: var(--claude-text-secondary);
}

.token-table-wrapper {
  max-height: 320px;
  overflow: auto;
}

.token-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

.token-table th,
.token-table td {
  padding: 10px 8px;
  border-bottom: 1px solid rgba(118, 103, 84, 0.15);
  text-align: left;
}

.token-table th {
  font-size: 12px;
  color: #4c3a24;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.token-table td strong {
  font-size: 14px;
  color: #2c1f12;
}

.token-empty {
  margin: 0;
  color: var(--claude-text-secondary);
  font-size: 13px;
}

.usage-leaders {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 14px;
}

.leader-card {
  border: 1px solid rgba(118, 103, 84, 0.18);
  border-radius: 18px;
  padding: 14px;
  background: rgba(255, 255, 255, 0.75);
}

.leader-card h3 {
  margin: 0 0 10px;
  color: var(--claude-text-secondary);
  font-size: 14px;
}

.leader-card ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.leader-card li {
  display: flex;
  justify-content: space-between;
  font-size: 13px;
}

.leader-card .muted {
  color: var(--claude-muted);
}

.table-wrapper {
  overflow-x: auto;
}

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

thead {
  background: rgba(255, 248, 238, 0.85);
}

th,
td {
  padding: 12px;
  border-bottom: 1px solid rgba(118, 103, 84, 0.18);
  text-align: left;
}

th {
  color: #4f3f2a;
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.05em;
}

.status-badge {
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
}

.status-badge.online {
  background: rgba(118, 176, 134, 0.18);
  color: var(--claude-success);
}

.status-badge.offline {
  background: rgba(121, 109, 94, 0.2);
  color: #5b4d3b;
}

.status-badge.warning {
  background: rgba(217, 152, 69, 0.2);
  color: var(--claude-warning);
}

.status-badge.danger {
  background: rgba(189, 93, 58, 0.18);
  color: var(--claude-accent-strong);
}

.progress-bar {
  height: 6px;
  border-radius: 999px;
  background: rgba(121, 109, 94, 0.12);
  margin-top: 6px;
}

.progress-bar span {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, var(--claude-highlight), var(--claude-accent));
}

.upload-feed {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.upload-item {
  padding: 12px 16px;
  border-radius: 16px;
  border: 1px solid rgba(118, 103, 84, 0.3);
  background: rgba(255, 255, 255, 0.88);
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: space-between;
}

.upload-meta {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  color: var(--claude-text-secondary);
  font-size: 12px;
}

.invite-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 12px;
}

.invite-card {
  border: 1px solid rgba(118, 103, 84, 0.3);
  border-radius: 16px;
  padding: 14px;
  background: rgba(255, 255, 255, 0.9);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5);
}

.invite-card h4 {
  margin: 0 0 6px;
}

.invite-card span {
  color: var(--claude-text-secondary);
  font-size: 13px;
}

.admin-loading {
  min-height: 60vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  color: var(--claude-text-secondary);
}

.spinner {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 4px solid rgba(0, 0, 0, 0.12);
  border-top-color: #050505;
  border-right-color: #050505;
  border-bottom-color: #f4f4f4;
  border-left-color: #f4f4f4;
  background: transparent;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

.admin-error {
  background: rgba(189, 93, 58, 0.15);
  border: 1px solid rgba(189, 93, 58, 0.45);
  border-radius: 16px;
  padding: 18px;
  color: #7d341a;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: flex-start;
}

.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.15s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

@media (max-width: 900px) {
  :global(#admin-app) {
    padding: 16px;
  }

  .admin-layout {
    flex-direction: column;
  }

  .admin-sidebar {
    width: 100%;
    flex-direction: row;
    flex-wrap: wrap;
  }

  .sidebar-tab {
    flex: 1;
    text-align: center;
  }
}
</style>
