/* ============================================================
   StarHue — 前端样式
   视觉完全对齐 linux-do/cdk（shadcn-ui new-york / zinc，浅色）
   下面每个组件的写法都是从 cdk 的组件文件里逐条抄下来的：

   button   : rounded-md text-sm font-medium shadow-xs h-9 px-4
   input    : rounded-xl border-transparent bg-muted/55 h-9 px-3 text-base
   skeleton : bg-accent animate-pulse rounded-md
   card     : rounded-[22px] bg-muted p-3.5 sm:p-4  hover:bg-muted/80
   stat     : rounded-[20px] bg-muted px-3.5 py-3 sm:min-h-[96px] sm:px-4
   layout   : px-4 pt-12 py-8 sm:px-6 md:px-8 lg:px-12 / max-w-7xl
   ============================================================ */

:root {
  --radius: 0.625rem;

  --background: oklch(1 0 0);
  --foreground: oklch(0.141 0.005 285.823);
  --card: oklch(1 0 0);
  --card-foreground: oklch(0.141 0.005 285.823);
  --popover: oklch(1 0 0);
  --popover-foreground: oklch(0.141 0.005 285.823);
  --primary: oklch(0.21 0.006 285.885);
  --primary-foreground: oklch(0.985 0 0);
  --secondary: oklch(0.967 0.001 286.375);
  --secondary-foreground: oklch(0.21 0.006 285.885);
  --muted: oklch(0.967 0.001 286.375);
  --muted-foreground: oklch(0.552 0.016 285.938);
  --accent: oklch(0.967 0.001 286.375);
  --accent-foreground: oklch(0.21 0.006 285.885);
  --destructive: oklch(0.577 0.245 27.325);
  --border: oklch(0.92 0.004 286.32);
  --input: oklch(0.92 0.004 286.32);
  --ring: oklch(0.705 0.015 286.067);

  --chart-1: oklch(0.646 0.222 41.116);
  --chart-2: oklch(0.6 0.118 184.704);
  --chart-3: oklch(0.398 0.07 227.392);
  --chart-4: oklch(0.828 0.189 84.429);
  --chart-5: oklch(0.769 0.188 70.08);

  --font-sans: "Inter", "Noto Sans SC", -apple-system, BlinkMacSystemFont,
    "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei",
    system-ui, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", "Cascadia Mono",
    Menlo, Consolas, monospace;

  --shadow-xs: 0 1px 2px 0 oklch(0 0 0 / 0.05);
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

/* 不显示任何滚动条 */
* { scrollbar-width: none; -ms-overflow-style: none; }
*::-webkit-scrollbar { width: 0; height: 0; display: none; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--background);
  color: var(--foreground);
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: inherit; text-decoration: none; }
button, input, select { font: inherit; color: inherit; }
h1, h2, h3, p { margin: 0; }
button { cursor: pointer; }

.icon {
  width: 1em; height: 1em; display: block; flex: none;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}

/* ---------------------------------------------------- page shell (cdk) */

.shell {
  width: 100%;
  max-width: 80rem;
  margin: 0 auto;
  padding-inline: 1rem;
}
@media (min-width: 640px) { .shell { padding-inline: 1.5rem; } }
@media (min-width: 768px) { .shell { padding-inline: 2rem; } }
@media (min-width: 1024px) { .shell { padding-inline: 3rem; } }

.page {
  padding-top: 3rem;
  padding-bottom: 2.5rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
@media (min-width: 768px) { .page { gap: 1.5rem; } }

/* ---------------------------------------------------------- topbar (cdk) */

.topbar {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--background);
  border-bottom: 1px solid var(--border);
}
.topbar-inner {
  height: 3.5rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.brand { display: flex; align-items: center; gap: 0.625rem; min-width: 0; }
.brand-mark {
  width: 2rem;
  height: 2rem;
  display: grid;
  place-items: center;
  flex: none;
}
.brand-logo {
  width: 1.875rem;
  height: 1.875rem;
  display: block;
  object-fit: contain;
}
.brand-text { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
.brand-text strong { font-size: 0.9375rem; font-weight: 600; letter-spacing: -0.015em; }
.brand-text em {
  font-style: normal; font-size: 0.75rem;
  color: var(--muted-foreground); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}

.topbar-actions { display: flex; align-items: center; gap: 0.5rem; }

.status-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  height: 2.25rem;
  padding: 0 0.875rem;
  border-radius: 9999px;
  background: var(--muted);
  color: var(--muted-foreground);
  font-size: 0.75rem;
  font-weight: 500;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.status-pill .dot {
  width: 0.375rem; height: 0.375rem;
  border-radius: 9999px;
  background: var(--muted-foreground);
  flex: none;
}
.status-pill.ok .dot {
  background: var(--chart-2);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--chart-2) 22%, transparent);
}
.status-pill.busy .dot {
  background: var(--chart-4);
  animation: pulse 1.1s ease-in-out infinite;
}
.status-pill.err { color: var(--destructive); }
.status-pill.err .dot { background: var(--destructive); }

/* ---------------------------------------------------------- button (cdk) */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  height: 2.25rem;
  padding: 0 1rem;
  border: 1px solid transparent;
  border-radius: 0.375rem;
  font-size: 0.875rem;
  font-weight: 500;
  white-space: nowrap;
  box-shadow: var(--shadow-xs);
  transition: background-color 0.15s, color 0.15s, border-color 0.15s;
  outline: none;
}
.btn:focus-visible {
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--ring) 50%, transparent);
}
.btn:disabled { opacity: 0.5; pointer-events: none; }
.btn .icon { width: 1rem; height: 1rem; }

.btn-default { background: var(--primary); color: var(--primary-foreground); }
.btn-default:hover { background: color-mix(in oklab, var(--primary) 90%, var(--background)); }

.btn-ghost { background: transparent; box-shadow: none; }
.btn-ghost:hover { background: var(--accent); color: var(--accent-foreground); }

/* ---------------------------------------------------------- input (cdk) */

.input-wrap {
  position: relative;
  display: flex;
  align-items: center;
  flex: 1 1 22rem;
  min-width: 0;
}
.input-wrap .search-icon {
  position: absolute;
  left: 0.75rem;
  width: 1rem; height: 1rem;
  color: var(--muted-foreground);
  pointer-events: none;
}
.input {
  display: flex;
  width: 100%;
  min-width: 0;
  height: 2.25rem;
  padding: 0.25rem 0.75rem;
  border: 1px solid transparent;
  border-radius: 0.75rem;
  background: color-mix(in oklab, var(--muted) 55%, transparent);
  color: var(--foreground);
  font-size: 1rem;
  box-shadow: none;
  outline: none;
  transition: background-color 0.15s, box-shadow 0.15s;
}
@media (min-width: 768px) { .input { font-size: 0.875rem; } }
.input::placeholder { color: var(--muted-foreground); font-size: 0.75rem; }
.input:hover { background: color-mix(in oklab, var(--muted) 70%, transparent); }
.input:focus {
  background: color-mix(in oklab, var(--muted) 70%, transparent);
  box-shadow: 0 0 0 2px color-mix(in oklab, var(--ring) 15%, transparent);
}
.input::-webkit-search-cancel-button { display: none; }
.input-wrap .input { padding-left: 2.25rem; }

.input-clear {
  position: absolute; right: 0.375rem;
  width: 1.5rem; height: 1.5rem;
  display: grid; place-items: center;
  border: 0; border-radius: 0.5rem;
  background: transparent;
  color: var(--muted-foreground);
  font-size: 1.125rem; line-height: 1;
}
.input-clear:hover { background: var(--accent); color: var(--accent-foreground); }

/* 自建下拉：原生 <select> 的弹层由系统绘制，CSS 改不动，只能自己画 */
.dropdown { position: relative; display: inline-flex; }

.dropdown-trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  height: 2.25rem;
  padding: 0 0.625rem;
  border: 1px solid transparent;
  border-radius: 0.75rem;
  background: color-mix(in oklab, var(--muted) 55%, transparent);
  color: var(--foreground);
  font-size: 0.875rem;
  font-weight: 400;
  white-space: nowrap;
  max-width: 15rem;
  transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s;
}
.dropdown-trigger:hover { background: color-mix(in oklab, var(--muted) 70%, transparent); }
.dropdown.open > .dropdown-trigger {
  background: var(--background);
  border-color: var(--border);
  box-shadow: var(--shadow-xs);
}
.dropdown-trigger > .icon {
  width: 0.875rem;
  height: 0.875rem;
  color: var(--muted-foreground);
  flex: none;
}
.dd-label { overflow: hidden; text-overflow: ellipsis; }
.dd-chevron { margin-left: 0.125rem; transition: transform 0.18s ease; }
.dropdown.open .dd-chevron { transform: rotate(180deg); }

.dropdown-menu {
  position: absolute;
  top: calc(100% + 0.375rem);
  right: 0;
  z-index: 50;
  max-height: 17rem;
  overflow-y: auto;
  padding: 0.3125rem;
  border: 1px solid var(--border);
  border-radius: 0.75rem;
  background: var(--popover, var(--card));
  box-shadow: 0 12px 32px oklch(0 0 0 / 0.14);
  animation: ddIn 0.14s cubic-bezier(0.22, 0.9, 0.3, 1);
}
@keyframes ddIn {
  from { opacity: 0; transform: translateY(-4px) scale(0.98); }
  to { opacity: 1; transform: none; }
}

.dropdown-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  width: 100%;
  height: 1.875rem;
  padding: 0 0.5rem;
  border: 0;
  border-radius: 0.5rem;
  background: transparent;
  color: var(--foreground);
  font-size: 0.8125rem;
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.12s;
}
.dropdown-item:hover { background: var(--accent); }
.dropdown-item.is-on { font-weight: 600; }
.dropdown-item .n {
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  color: var(--muted-foreground);
}

.toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  height: 2.25rem;
  padding: 0 0.875rem;
  border: 1px solid transparent;
  border-radius: 0.75rem;
  background: color-mix(in oklab, var(--muted) 55%, transparent);
  color: var(--muted-foreground);
  font-size: 0.875rem;
  font-weight: 500;
  transition: background-color 0.15s, color 0.15s;
}
.toggle .icon { width: 0.875rem; height: 0.875rem; }
.toggle:hover {
  background: color-mix(in oklab, var(--muted) 70%, transparent);
  color: var(--foreground);
}
.toggle[aria-pressed="true"] {
  background: var(--primary);
  color: var(--primary-foreground);
}
.toggle[aria-pressed="true"]:hover {
  background: color-mix(in oklab, var(--primary) 90%, var(--background));
}

/* ----------------------------------------------------------- badge (cdk) */

.badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.25rem;
  height: 1.375rem;
  padding: 0.125rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: 9999px;
  background: transparent;
  color: var(--foreground);
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
}
.badge .icon { width: 0.75rem; height: 0.75rem; opacity: 0.6; }
.badge-sub {
  background: var(--secondary);
  border-color: transparent;
  color: var(--secondary-foreground);
}
.badge-sep { opacity: 0.4; margin: 0 0.0625rem; }

/* 徽章内的图标不要被压缩 */
.badge {
  vertical-align: middle;
}
.badge > .icon {
  flex: none;
}

/* 可点复制的地址胶囊 */
.addr {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  height: 1.375rem;
  padding: 0 0.5rem;
  border: 1px dashed var(--border);
  border-radius: 9999px;
  background: transparent;
  color: var(--muted-foreground);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  line-height: 1;
  cursor: pointer;
  transition: background-color 0.15s, color 0.15s, border-color 0.15s;
}
.addr:hover {
  background: var(--accent);
  color: var(--accent-foreground);
  border-color: transparent;
}
.addr .icon {
  width: 0.75rem;
  height: 0.75rem;
  opacity: 0.55;
}
.addr:hover .icon { opacity: 1; }

/* ------------------------------------------------------------ stats (cdk) */

.stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.75rem;
}
@media (min-width: 900px) {
  .stats { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.stat {
  min-height: 5.5rem;
  border-radius: 20px;
  background: var(--muted);
  padding: 0.75rem 0.875rem;
  display: flex;
  flex-direction: column;
}
@media (min-width: 640px) {
  .stat { min-height: 6rem; padding: 0.75rem 1rem; }
}
.stat-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
}
.stat-label {
  font-size: 0.6875rem;
  font-weight: 500;
  color: var(--muted-foreground);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding-right: 0.5rem;
}
.stat-icon {
  width: 1.5rem; height: 1.5rem;
  border-radius: 9999px;
  display: grid; place-items: center;
  background: color-mix(in oklab, white 70%, transparent);
  color: var(--muted-foreground);
  flex: none;
}
.stat-icon .icon { width: 0.875rem; height: 0.875rem; }

.stat-value {
  margin-top: 0.75rem;
  font-size: 1.25rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--foreground);
  font-variant-numeric: tabular-nums;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
@media (min-width: 640px) { .stat-value { font-size: 1.5rem; } }
.stat-value.stat-text { font-size: 1.125rem; letter-spacing: -0.015em; }
@media (min-width: 640px) { .stat-value.stat-text { font-size: 1.375rem; } }
.stat-foot {
  margin-top: 0.5rem;
  font-size: 0.6875rem;
  color: var(--muted-foreground);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ------------------------------------------------------- toolbar (cdk) */

.toolbar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.toolbar-controls {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
/* --------------------------------------------------------- card grid (cdk) */

.grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.75rem;
}
@media (min-width: 720px) { .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* ProjectCard: rounded-[22px] bg-muted p-3.5 sm:p-4 hover:bg-muted/80 */
.card:hover .card-body { background: color-mix(in oklab, var(--muted) 80%, transparent); }
.card.is-open .card-body {
  box-shadow: inset 0 0 0 2px color-mix(in oklab, var(--primary) 25%, transparent);
}

.card-head {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
}
.card-icon {
  width: 2.75rem; height: 2.75rem;
  border-radius: 0.875rem;
  flex: none;
  object-fit: cover;
  background: var(--background);
}
.card-icon.fallback {
  display: grid;
  place-items: center;
  font-family: var(--font-mono);
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--muted-foreground);
}
.card-title {
  flex: 1;
  min-width: 0;
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.5;
  letter-spacing: -0.01em;
  color: var(--foreground);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  word-break: break-word;
}
@media (min-width: 640px) { .card-title { font-size: 1rem; } }

.card-count {
  flex: none;
  text-align: right;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.card-count b {
  display: block;
  font-size: 1.125rem;
  font-weight: 600;
  letter-spacing: -0.03em;
  color: var(--foreground);
}
.card-count b.zero { color: var(--muted-foreground); }
.card-count span {
  display: block;
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--muted-foreground);
  margin-top: 0.125rem;
}

.card-bar {
  position: relative;
  height: 0.25rem;
  border-radius: 9999px;
  background: color-mix(in oklab, var(--foreground) 6%, transparent);
  overflow: hidden;
  margin-top: 0.875rem;
}
.card-bar i {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: 9999px;
  background: var(--chart-3);
  transition: width 0.4s cubic-bezier(0.22, 0.9, 0.3, 1);
}
.card-bar i.warm { background: var(--chart-4); }
.card-bar i.hot { background: var(--chart-1); }
.card-bar i.empty { background: color-mix(in oklab, var(--foreground) 14%, transparent); }

/* ProjectCard 的描述位：line-clamp-2 + min-h */
.card-desc {
  margin-top: 0.625rem;
  min-height: 2.25rem;
  font-size: 0.75rem;
  line-height: 1.25rem;
  color: var(--muted-foreground);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
@media (min-width: 640px) { .card-desc { min-height: 2.5rem; } }

/* ProjectCard 的底部：grid-cols-[1fr_auto] items-end */
.card-foot {
  margin-top: 1rem;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: 0.75rem;
}
.card-tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 0.5rem;
  overflow: hidden;
  max-height: 2.75rem;
}
.card-side {
  min-height: 2.75rem;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: space-between;
  gap: 0.25rem;
  flex: none;
}
.card-mode {
  font-size: 0.6875rem;
  font-weight: 500;
  color: color-mix(in oklab, var(--foreground) 85%, transparent);
  white-space: nowrap;
}
@media (min-width: 640px) { .card-mode { font-size: 0.75rem; } }

.card-status {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  font-size: 0.6875rem;
  font-weight: 500;
  color: var(--muted-foreground);
  white-space: nowrap;
}
@media (min-width: 640px) { .card-status { font-size: 0.75rem; } }
.card-status .icon { width: 0.875rem; height: 0.875rem; }
.card-status.live { color: var(--chart-2); }

/* ProjectCard 里的 icon + 数字指标 */
.metric {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  font-size: 0.6875rem;
  line-height: 1.25rem;
  color: var(--muted-foreground);
  white-space: nowrap;
}
@media (min-width: 640px) { .metric { font-size: 0.75rem; } }
.metric .icon { width: 0.875rem; height: 0.875rem; opacity: 0.5; }

/* skeleton: bg-accent animate-pulse rounded-md */
.skel {
  border-radius: 22px;
  background: var(--muted);
  padding: 1rem;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}
.skel-line {
  height: 1rem;
  border-radius: 0.375rem;
  background: var(--accent);
  animation: pulse 1.6s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}

/* ----------------------------------------------------------- list head */

.listhead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  font-size: 0.8125rem;
  color: var(--muted-foreground);
}

/* --------------------------------------------------------------- empty */

.empty {
  border-radius: 22px;
  background: var(--muted);
  padding: 3.5rem 1.5rem;
}
.empty-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  text-align: center;
}
.empty-inner .icon { width: 1.75rem; height: 1.75rem; color: var(--muted-foreground); }
.empty-inner p { font-size: 0.9375rem; font-weight: 500; color: var(--foreground); }
.empty-inner span { font-size: 0.8125rem; color: var(--muted-foreground); }

/* ----------------------------------------------------------- pagination */

.pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}
.page-info { font-size: 0.8125rem; color: var(--muted-foreground); font-variant-numeric: tabular-nums; }
.page-ctl { display: flex; align-items: center; gap: 0.25rem; }
.page-btn {
  min-width: 2.25rem; height: 2.25rem;
  padding: 0 0.5rem;
  border: 0;
  border-radius: 0.375rem;
  background: transparent;
  color: var(--muted-foreground);
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
  transition: background-color 0.15s, color 0.15s;
}
.page-btn:hover:not(:disabled) {
  background: var(--accent);
  color: var(--accent-foreground);
}
.page-btn.is-on { background: var(--primary); color: var(--primary-foreground); }
.page-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.page-ellipsis { padding: 0 0.25rem; color: var(--muted-foreground); font-size: 0.8125rem; }

/* --------------------------------------------------------------- dialog */

.scrim {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: oklch(0 0 0 / 0.4);
  animation: fade 0.15s ease;
}
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }

.dialog {
  position: fixed;
  z-index: 61;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: min(46rem, calc(100vw - 2rem));
  /* 固定高度：切 tab / 内容多少都不跳 */
  height: min(88vh, 54rem);
  max-height: min(88vh, 54rem);
  display: flex;
  flex-direction: column;
  border-radius: 22px;
  background: var(--card);
  border: 1px solid var(--border);
  box-shadow: 0 24px 70px oklch(0 0 0 / 0.18);
  animation: pop 0.18s cubic-bezier(0.22, 0.9, 0.3, 1);
  overflow: hidden;
}
@keyframes pop {
  from { opacity: 0; transform: translate(-50%, -48%) scale(0.985); }
  to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

.dialog-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.875rem;
  padding: 1.25rem 1.5rem 1rem;
  border-bottom: 1px solid var(--border);
}
.dialog-title { display: flex; gap: 0.875rem; min-width: 0; }
.dialog-title-text { min-width: 0; }
.dialog-title h2 {
  font-size: 1.0625rem;
  font-weight: 600;
  line-height: 1.35;
  letter-spacing: -0.015em;
  word-break: break-word;
}
.dialog-sub {
  margin-top: 0.5rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.8125rem;
  color: var(--muted-foreground);
}
.dialog-icon {
  width: 3rem; height: 3rem;
  border-radius: 0.875rem;
  flex: none;
  object-fit: cover;
  background: var(--muted);
}
.dialog-icon.fallback {
  display: grid;
  place-items: center;
  font-family: var(--font-mono);
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--muted-foreground);
}

.dialog-body {
  flex: 1;
  min-height: 0;
  /* 整体不滚：数字卡 / 曲线 / tab 栏钉住，只有下面的面板滚 */
  overflow: hidden;
  padding: 1.25rem 1.5rem 0;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

/* 固定不动的三块 */
.dialog-body > .dstats,
.dialog-body > .block,
.dialog-body > .tabs {
  flex: none;
}

.dstats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.75rem;
}
@media (min-width: 640px) {
  .dstats { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.dstat {
  border-radius: 14px;
  background: var(--muted);
  padding: 0.75rem 0.875rem;
}
.dstat span { display: block; font-size: 0.75rem; font-weight: 500; color: var(--muted-foreground); }
.dstat strong {
  display: block;
  margin-top: 0.25rem;
  font-size: 1.125rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.dstat strong small { font-size: 0.75rem; font-weight: 400; color: var(--muted-foreground); }

.block {
  border-radius: 14px;
  background: var(--muted);
  padding: 0.875rem 1rem;
}
.block-head { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; }
.block-head h3 { font-size: 0.8125rem; font-weight: 600; color: var(--muted-foreground); }
.block-legend {
  display: inline-flex; align-items: center; gap: 0.375rem;
  font-size: 0.75rem; color: var(--muted-foreground);
}
.block-legend .ln { width: 0.75rem; height: 2px; border-radius: 1px; display: inline-block; }
.block-legend .ln.avg { background: var(--chart-1); }
.block-legend .ln.peak {
  background: color-mix(in oklab, var(--foreground) 35%, transparent);
  margin-left: 0.375rem;
}

/* 图表坐标系：刻度字号/颜色集中在这里，任何图表都复用同一套 */
:root {
  --chart-tick-size: 0.6875rem;
  --chart-tick-color: var(--muted-foreground);
  --chart-yw: 2.25rem;
  --chart-xh: 1.25rem;
}

.chart-wrap {
  position: relative;
  margin-top: 0.5rem;
  padding: 0 0 var(--chart-xh) var(--chart-yw);
}
.chart { width: 100%; height: 8rem; display: block; }

/* Y 轴刻度：右侧对齐，贴着绘图区左沿 */
.chart-y {
  position: absolute;
  left: 0;
  top: 0;
  bottom: var(--chart-xh);
  width: calc(var(--chart-yw) - 0.5rem);
  pointer-events: none;
}
.chart-y span {
  position: absolute;
  right: 0;
  transform: translateY(-50%);
  font-size: var(--chart-tick-size);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  color: var(--chart-tick-color);
  white-space: nowrap;
}

/* X 轴刻度：等分，居中在刻度点下方 */
.chart-x {
  position: absolute;
  left: var(--chart-yw);
  right: 0;
  bottom: 0;
  height: var(--chart-xh);
  pointer-events: none;
}
.chart-x span {
  position: absolute;
  top: 0.3125rem;
  transform: translateX(-50%);
  font-size: var(--chart-tick-size);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  color: var(--chart-tick-color);
  white-space: nowrap;
}

.chart-grid { stroke: color-mix(in oklab, var(--border) 70%, transparent); stroke-width: 1; }

/* ---- 曲线悬浮提示 ---- */
.chart-hover {
  position: absolute;
  left: var(--chart-yw);
  right: 0;
  top: 0;
  bottom: var(--chart-xh);
  pointer-events: none;
  z-index: 2;
}
.chart-guide {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: color-mix(in oklab, var(--foreground) 22%, transparent);
  transform: translateX(-0.5px);
}
.chart-dot {
  position: absolute;
  width: 0.4375rem;
  height: 0.4375rem;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  box-shadow: 0 0 0 1.5px var(--card);
}
.chart-dot.avg { background: oklch(0.646 0.222 41.116); }
.chart-dot.peak { background: oklch(0.45 0 0); }

.chart-tip {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  padding: 0.4375rem 0.5625rem;
  border-radius: 0.5rem;
  border: 1px solid var(--border);
  background: var(--popover, var(--card));
  box-shadow: 0 8px 24px oklch(0 0 0 / 0.12);
  font-size: 0.6875rem;
  line-height: 1.5;
  white-space: nowrap;
  color: var(--foreground);
  transition: left 0.06s linear;
}
.chart-tip .t {
  color: var(--muted-foreground);
  font-variant-numeric: tabular-nums;
  margin-bottom: 0.125rem;
}
.chart-tip .row {
  display: flex;
  align-items: center;
  gap: 0.3125rem;
  font-variant-numeric: tabular-nums;
}
.chart-tip .row i {
  width: 0.4375rem;
  height: 0.4375rem;
  border-radius: 50%;
  flex: none;
}
.chart-tip .row i.avg { background: oklch(0.646 0.222 41.116); }
.chart-tip .row i.peak { background: oklch(0.45 0 0); }
.chart-tip .row b {
  margin-left: auto;
  font-weight: 600;
}

/* 小屏收一点轴宽，别把绘图区挤太窄 */
@media (max-width: 520px) {
  :root { --chart-yw: 1.875rem; }
}
.block-foot {
  font-size: 0.75rem;
  color: var(--muted-foreground);
  margin-top: 0.5rem;
  font-variant-numeric: tabular-nums;
}

.tabs {
  display: flex;
  gap: 0.125rem;
  padding: 0.1875rem;
  border-radius: 0.375rem;
  background: var(--muted);
}
.tab {
  flex: 1;
  height: 2.25rem;
  border: 0;
  border-radius: 0.375rem;
  background: transparent;
  color: var(--muted-foreground);
  font-size: 0.875rem;
  font-weight: 500;
  transition: background-color 0.15s, color 0.15s;
}
.tab:hover { color: var(--foreground); }
.tab.is-on {
  background: var(--background);
  color: var(--foreground);
  box-shadow: var(--shadow-xs);
}

.tabpanel { display: none; }
.tabpanel.is-on {
  display: block;
  /* 只有这一块滚动 */
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-bottom: 1.5rem;
}

.kv {
  display: grid;
  grid-template-columns: 9.5rem minmax(0, 1fr);
  gap: 1px;
  margin: 0;
  border-radius: 14px;
  overflow: hidden;
  background: var(--border);
}
.kv dt, .kv dd {
  margin: 0;
  padding: 0.625rem 0.875rem;
  background: var(--card);
  font-size: 0.8125rem;
  overflow-wrap: anywhere;
}
.kv dt { color: var(--muted-foreground); font-size: 0.75rem; }
.kv dd { color: var(--foreground); }
.kv dd.dim { color: var(--muted-foreground); }
.kv.mono { grid-template-columns: 12rem minmax(0, 1fr); }
.kv.mono dt, .kv.mono dd { font-family: var(--font-mono); font-size: 0.75rem; }

.probe-bar {
  display: flex; align-items: center; gap: 0.875rem;
  flex-wrap: wrap; margin-bottom: 0.875rem;
}
.probe-note { font-size: 0.75rem; color: var(--muted-foreground); }
.probe-note code {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  background: var(--muted);
  padding: 0.125rem 0.375rem;
  border-radius: 0.25rem;
  color: var(--foreground);
}

.plist { border-radius: 14px; overflow: hidden; background: var(--border); display: grid; gap: 1px; }
.plist-row {
  display: grid;
  grid-template-columns: 2.25rem minmax(0, 1fr) auto;
  gap: 0.75rem;
  align-items: center;
  padding: 0.5625rem 0.875rem;
  background: var(--card);
  font-size: 0.8125rem;
}
.plist-row .n { font-family: var(--font-mono); font-size: 0.75rem; color: var(--muted-foreground); }
.plist-row .ping { font-family: var(--font-mono); font-size: 0.75rem; color: var(--muted-foreground); }
.plist-row .ping.low { color: var(--chart-2); }
.plist-row .ping.mid { color: var(--chart-4); }
.plist-row .ping.high { color: var(--chart-1); }

.notice {
  border-radius: 14px;
  background: var(--muted);
  padding: 1rem;
  font-size: 0.8125rem;
  color: var(--muted-foreground);
}
.notice.err {
  color: var(--destructive);
  background: color-mix(in oklab, var(--destructive) 8%, var(--card));
}

/* --------------------------------------------------------------- toast */

.toast {
  position: fixed;
  left: 50%;
  bottom: 1.75rem;
  transform: translateX(-50%);
  z-index: 80;
  padding: 0.625rem 1.125rem;
  border-radius: 0.375rem;
  background: var(--primary);
  color: var(--primary-foreground);
  font-size: 0.8125rem;
  box-shadow: 0 10px 34px oklch(0 0 0 / 0.18);
  animation: fade 0.15s ease;
  max-width: 90vw;
}
.toast.err { background: var(--destructive); color: white; }

/* ------------------------------------------------- motion (cdk MotionEffect) */

/* 卡片进场：slide-down + fade + zoom，按 --i 错峰 */
@keyframes cardIn {
  from {
    opacity: 0;
    transform: translateY(14px) scale(0.965);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* 统计卡 */
.stat {
  animation: cardIn 0.42s cubic-bezier(0.22, 0.9, 0.3, 1) backwards;
}
.stat:nth-child(1) { animation-delay: 0ms; }
.stat:nth-child(2) { animation-delay: 45ms; }
.stat:nth-child(3) { animation-delay: 90ms; }
.stat:nth-child(4) { animation-delay: 135ms; }

/* 图表：折线从左往右画出来，面积随后淡入 */
.chart-line {
  stroke-dasharray: 1600;
  stroke-dashoffset: 1600;
  animation: drawLine 0.85s cubic-bezier(0.22, 0.9, 0.3, 1) 0.05s forwards;
}
.chart-line.delay { animation-delay: 0.18s; }
@keyframes drawLine {
  to { stroke-dashoffset: 0; }
}
.chart-area {
  opacity: 0;
  animation: areaIn 0.55s ease 0.42s forwards;
}
@keyframes areaIn {
  to { opacity: 1; }
}

/* 刷新按钮：加载中转圈 */

/* toast 从底部滑入 */
.toast {
  animation: toastIn 0.24s cubic-bezier(0.22, 0.9, 0.3, 1);
}
@keyframes toastIn {
  from { opacity: 0; transform: translate(-50%, 14px) scale(0.97); }
  to { opacity: 1; transform: translate(-50%, 0) scale(1); }
}

/* 分页翻页时列表淡一下 */
.grid.switching {
  opacity: 0.45;
  transform: translateY(4px);
  transition: opacity 0.16s ease, transform 0.16s ease;
}
.grid {
  transition: opacity 0.22s ease, transform 0.22s ease;
}

/* ------------------------------------------------- 运营商 logo / 核准标记 */

/* --------------------------------------------------------- responsive */

@media (max-width: 640px) {
  .brand-text em { display: none; }
  .status-pill { display: none; }
  .grid { gap: 0.625rem; }
  .card-icon { width: 2.5rem; height: 2.5rem; border-radius: 0.75rem; }
  .kv { grid-template-columns: 7.5rem minmax(0, 1fr); }
  .kv.mono { grid-template-columns: 9rem minmax(0, 1fr); }
  .dialog { width: calc(100vw - 1rem); height: 92vh; max-height: 92vh; }
}

/* ============================================================ 3D / 动效层 */
/* 前面负责骨架，这一层负责手感。
   末尾的 prefers-reduced-motion 会整体兜底关掉。 */

/* ---- 1. 卡片：跟随鼠标的 3D 倾斜 + 高光 ---- */
.grid { perspective: none; }

/* .card 只负责「命中」和排版占位，自己永远不移动。
   视觉全在 .card-body 上 —— 否则 hover 抬起时命中区跟着跑，
   鼠标停在边缘会让 :hover 每帧得失，画面抽搐。 */
.card {
  position: relative;
  display: flex;
  min-width: 0;
  cursor: pointer;
  /* 透视必须挂在 .card 上：.card-body 是它的直接子元素才吃得到 */
  perspective: 1000px;
  animation: cardIn3d 0.42s cubic-bezier(0.22, 0.9, 0.3, 1) backwards;
  animation-delay: calc(var(--i, 0) * 35ms);
}

.card-body {
  position: relative;
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  padding: 0.875rem;
  border-radius: 22px;
  background: var(--muted);
  /* 常驻的一层淡光晕：浅色底上让卡片看起来是「浮着」的 */
  box-shadow:
    0 0 0 1px color-mix(in oklab, var(--primary) 5%, transparent),
    0 2px 6px -4px oklch(0 0 0 / 0.08),
    0 10px 26px -22px oklch(0 0 0 / 0.22);
  /* 静止不挂 3D 变换：有 perspective + translateZ 会让文字重新取样发虚 */
  transition:
    background-color 0.2s,
    transform 0.42s cubic-bezier(0.22, 0.9, 0.3, 1),
    box-shadow 0.42s cubic-bezier(0.22, 0.9, 0.3, 1);
}

.card.tilting .card-body {
  transition:
    background-color 0.2s,
    transform 0.08s linear,
    box-shadow 0.2s;
}

.card:hover .card-body,
.card.tilting .card-body {
  transform:
    rotateX(var(--rx, 0deg))
    rotateY(var(--ry, 0deg))
    translateY(-4px);
  /* 边缘光晕：贴着轮廓往外扩，不进卡片内部 */
  box-shadow:
    0 0 0 1px color-mix(in oklab, var(--primary) 12%, transparent),
    0 0 0 3px oklch(0.68 0.18 45 / 0.1),
    0 0 34px -2px oklch(0.68 0.18 45 / 0.28),
    0 18px 40px -22px oklch(0 0 0 / 0.28);
}

/* 卡内图标轻微放大即可，不用 translateZ（会放大像素导致文字发虚） */
.card-icon { transition: transform 0.35s cubic-bezier(0.22, 0.9, 0.3, 1); }
.card:hover .card-icon { transform: scale(1.05); }

/* ---- 2. 数字卡：抬起 + 后仰 ---- */
.stats { perspective: 900px; }
.stat {
  transition: box-shadow 0.4s;
}
.stat {
  transform: none;
}
.stat:hover {
  box-shadow: 0 10px 26px -20px oklch(0 0 0 / 0.3);
}

/* ---- 3. 控件：按下有厚度，悬浮有浮起 ---- */
.btn, .toggle, .tab, .page-ctl button, .dropdown-trigger {
  transition:
    background-color 0.15s, color 0.15s, border-color 0.15s,
    transform 0.16s cubic-bezier(0.22, 0.9, 0.3, 1),
    box-shadow 0.16s;
}
.btn:active, .toggle:active, .page-ctl button:active, .dropdown-trigger:active {
  transform: translateY(1px) scale(0.985);
}
.btn:hover { box-shadow: 0 8px 20px -12px oklch(0 0 0 / 0.35); }
.tab:hover { transform: translateY(-1px); }
.tab.is-on { box-shadow: 0 2px 8px -3px oklch(0 0 0 / 0.18); }
.page-ctl button:hover { transform: translateY(-2px); }

/* ---- 4. 品牌 logo：轻微摆动 ---- */
.brand-mark { perspective: 300px; }
.brand-logo { transition: transform 0.5s cubic-bezier(0.22, 0.9, 0.3, 1); }
.brand:hover .brand-logo { transform: rotate(-10deg) scale(1.1); }

/* ---- 5. 卡片入场：从后往前翻出来 ---- */
@keyframes cardIn3d {
  from { opacity: 0; transform: rotateX(-12deg) translateY(18px) scale(0.94); }
  to { opacity: 1; transform: rotateX(0) translateY(0) scale(1); }
}

/* ---- 6. 弹窗：从纵深推入 ---- */
@keyframes pop {
  from {
    opacity: 0;
    transform: translate(-50%, -50%) perspective(1200px) rotateX(10deg) translateY(14px) scale(0.94);
  }
  to {
    opacity: 1;
    transform: translate(-50%, -50%) perspective(1200px) rotateX(0) translateY(0) scale(1);
  }
}

/* 遮罩淡入 */
@keyframes scrimIn {
  from { opacity: 0; }
  to { opacity: 1; }
}
#scrim { animation: scrimIn 0.22s ease; }

/* ---- 7. 表格行逐行滑入 ---- */
@keyframes rowIn {
  from { opacity: 0; transform: translateX(-6px); }
  to { opacity: 1; transform: none; }
}
.kv > dt, .kv > dd {
  animation: rowIn 0.28s cubic-bezier(0.22, 0.9, 0.3, 1) backwards;
}

/* ---- 8. 数字卡数值跳动 ---- */

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}
