/* ==========================================================================
   移植适配样式
   上游 styles.css 保持逐字节不变，所有对上游样式的补救都集中在本文件。
   加载顺序必须是 vendor/fonts.css → styles.css → adapt.css（本文件必须最后加载，
   否则同优先级的选择器会被上游覆盖掉）。
   ========================================================================== */

/* --------------------------------------------------------------------------
   中文名字的显示效果
   上游的展示字体 Archivo Black 只有拉丁字形：节点名、站点名只要是中文，浏览器就会
   掉到系统默认无衬线的常规字重上——卡片上其余元素都是粗边框 + 超粗标签，只有中文
   标题细一截，整张卡片看着不协调。
   这里不动上游的字体声明，只补一个「中文专用的本机粗体」字面：
   - @font-face + unicode-range 只吃中日韩文字与全角标点，拉丁文仍由 Archivo Black 负责，
     所以「测试节点 · Tokyo」这种混排也不会变成两种字体的拼贴；
   - src 全部用 local()，只挑本机已有的字体（Windows / macOS / Linux 各取常用名），
     不下载 CJK 字体——思源黑体这类即使用子集也有好几 MB，中文站又必须能用；
   - size-adjust 把中文稍微收一点，和 Archivo Black 的字面大小对齐。
   本机没有命中任何一款时，会回落到上游原本的系统默认字体，即与未改前一致，不会更差。
   -------------------------------------------------------------------------- */
@font-face {
  font-family: 'Monitor Display CJK';
  src:
    local('Microsoft YaHei UI Bold'), local('Microsoft YaHei Bold'),
    local('PingFang SC Semibold'), local('PingFang SC Bold'),
    local('Hiragino Sans GB W6'), local('Hiragino Sans GB'),
    local('Source Han Sans SC Bold'), local('Source Han Sans CN Bold'),
    local('Noto Sans CJK SC Bold'), local('Noto Sans SC Bold'),
    local('WenQuanYi Micro Hei Bold');
  font-weight: 400 900;
  font-display: swap;
  size-adjust: 96%;
  unicode-range: U+2E80-2EFF, U+3000-303F, U+3400-4DBF, U+4E00-9FFF, U+F900-FAFF, U+FE30-FE4F, U+FF00-FFEF;
}

/* 与上游 styles.css 里所有用 Archivo Black 的选择器一一对应。
   scripts/check-adapt.mjs 会核对这份清单：上游升级后新增了用法而这里没跟上，
   npm run check 会直接报错，而不是让中文又悄悄变细。 */
h1, h2, h3, h4, h5, h6,
.stat-value,
.node-name,
.row-node-name,
.row-arrow,
.footer-signature-author,
.modal-content h2,
.modal-content h3,
.modal-node-status-tag {
  font-family: 'Archivo Black', 'Monitor Display CJK', sans-serif;
}

/* --------------------------------------------------------------------------
   页脚：左侧原有的那行 Powered by 已并入右下角徽章（同一句话不必出现两遍），
   徽章本身保持不变，用 margin-left: auto 继续钉在右下角。
   窄屏下页脚本来就是居中排列（上游样式），这个 auto 会把徽章顶到右边不对称，
   所以只在宽屏生效。
   -------------------------------------------------------------------------- */
@media (min-width: 681px) {
  .footer-signature {
    margin-left: auto;
  }
}

/* --------------------------------------------------------------------------
   新增：分组（Hub 节点的 group 字段）
   上游没有分组概念，这一块是新加的样式：main 内的分组筛选标签行 + 节点列表里的分段标题
   （script.js 的 renderGroupTabs / createGroupHeading）。视觉沿用上游的新粗野主义：
   3px 黑边、硬阴影、激活态用强调色 + 按下位移，与顶栏的 .btn-view / .btn-admin 同款。
   -------------------------------------------------------------------------- */
.group-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 1.25rem;
}

/* 与 .btn-admin 同理：作者样式里的 display 会压过浏览器的 [hidden]，
   没有这一条时「没有分组就把标签行收起来」会静默失效（标签行照旧占着位置）。
   scripts/check-adapt.mjs 会核对这条规则存在。 */
#group-tabs[hidden] {
  display: none;
}

.group-tab {
  font-family: 'Space Grotesk', sans-serif;
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--black);
  background: var(--card-bg);
  border: 3px solid var(--black);
  box-shadow: 3px 3px 0 var(--black);
  padding: 0.5rem 0.9rem;
  cursor: pointer;
  transition: transform 0.1s ease, box-shadow 0.1s ease;
}

.group-tab:hover {
  transform: translate(-2px, -2px);
  box-shadow: 5px 5px 0 var(--black);
}

.group-tab.active {
  transform: translate(3px, 3px);
  box-shadow: none;
  background: var(--accent);
}

/* 分段标题：网格视图里横跨整行（与 .empty-state 同款做法），列表视图在下面单独摆 */
.group-heading {
  grid-column: 1 / -1;
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  padding-bottom: 0.5rem;
  border-bottom: 3px solid var(--black);
}

.group-heading-name {
  font-family: 'Archivo Black', 'Monitor Display CJK', sans-serif;
  font-size: 1rem;
  text-transform: uppercase;
  letter-spacing: -0.02em;
  word-break: break-word;
}

.group-heading-count {
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--text-muted);
}

/* 列表视图：标题带跟着 .node-row 一起横向滚动（行本身 min-width: 820px，见上游注释），
   所以给它同样的 min-width；线只画在标题上方，下面那条留给紧随其后的行边框，
   否则会叠成 4px 的双线。 */
.nodes-container.list-view .group-heading {
  min-width: 820px;
  padding: 0.7rem 0.85rem 0.55rem;
  border-bottom: 0;
  border-top: 2px solid var(--black);
  background: var(--card-bg);
}

.nodes-container.list-view .group-heading:first-child {
  border-top: 0;
}

/* --------------------------------------------------------------------------
   移植差异：卡片上的三网延迟（站点设置 showNodePing，逻辑见 script.js 的 applyCardPings）
   位置：卡片内容末尾，NETWORK 区之后（原来的底部栏已拆掉，这一块就是卡片底部那一段）。
   排版上刻意与卡片自己的语言对齐（实测过尺寸）：
     · 延迟值用 .metric-value 的 0.85rem，右列数字才和上面的 CPU/内存/磁盘成一条竖线
       （原来照抄 0.75rem，比指标值小一号，同一列里数字突然变矮）；
     · 分割线 3px，与 .node-header 的下边线同粗（原为 2px，两条线挨着时粗细不一很显眼）；
     · 上留白 1.5rem 对齐卡片里其它分区的节奏；下留白由卡片自身的 padding 给。
   三列：线路名（可省略号）| 最新延迟 | 丢包率。这说的是 rows（竖排）那一档；后两列固定宽度 + 右对齐，
   数字在竖直方向成两列。默认的 columns（横排）档见下面 .ping-layout-columns 那段。
   丢包率与弹窗「延迟监控」同一口径（0.0% 常显，有丢包才变红，复用 .has-loss 语义）。
   卡片整体可点（打开节点详情），这里不单独挂点击——点上去还是同一个动作，
   详情弹窗里的「延迟监控」就是它的放大版。
   -------------------------------------------------------------------------- */
.node-ping {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-top: 1.5rem;
  padding-top: 0.75rem;
  border-top: 3px solid var(--black);
}

.node-ping-row {
  display: flex;
  align-items: baseline;
  /* 不用 space-between：三列各自定位，右两列靠固定宽度对齐成两条竖线 */
  justify-content: flex-start;
  gap: 0.75rem;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.05em;
}

.node-ping-name {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.node-ping-value {
  flex: none;
  min-width: 4.2rem;
  text-align: right;
  font-family: 'Space Grotesk', 'SF Mono', 'Consolas', 'Monaco', monospace;
  font-variant-numeric: tabular-nums;
  font-size: 0.85rem;   /* 与 .metric-value 同号，右列数字对齐 */
  color: var(--black);
}

.node-ping-loss {
  flex: none;
  min-width: 3.1rem;
  text-align: right;
  font-family: 'Space Grotesk', 'SF Mono', 'Consolas', 'Monaco', monospace;
  font-variant-numeric: tabular-nums;
  font-size: 0.75rem;
  color: var(--text-muted);
}

.node-ping-loss.has-loss {
  color: var(--accent-red);
}

/* 站点设置 cardPingLayout = columns（默认）：三条线路并排成三列，每条是
   「线路名 / 延迟 / 丢包率」三行。列宽用 flex:1 1 0 均分（三个数字块因此左对齐成三竖列），
   gap 比竖排时更紧（0.4rem）——卡片实测只有 326px 宽，均分后每列约 88px，
   延迟值与丢包率放在同一行会放不下（需要 ~92px），所以让它们各自占一行；
   flex-wrap 仍留着：窄卡片上万一某一列装不下，丢包率会自己换行，不会溢出被裁。
   按要求（2026-09-26）：每列内容各自居中，列与列之间用竖条隔开（表格感，与卡片其它分割线同一套粗野主义语言）。 */
.node-ping.ping-layout-columns {
  flex-direction: row;
  /* stretch 让三列等高：竖条的高度才一致（某列多折一行时也不会参差） */
  align-items: stretch;
  gap: 0.5rem;
}

.node-ping.ping-layout-columns .node-ping-row {
  flex: 1 1 0;
  min-width: 0;
  flex-direction: column;
  align-items: center;   /* 每列内容各自居中（横向由 align-items 定） */
  text-align: center;    /* 文本本身居中（线路名、数值、丢包率） */
  row-gap: 0.2rem;
  padding: 0 0.25rem;
  position: relative;    /* 列间竖条的定位基准 */
}

/* 列间竖条：3px（与卡片里其它分割线同粗），画在两列之间那 8px 空隙的正中——
   left:-0.25rem 把它的左边定在空隙中线，再 translateX(-50%) 让它以中线为轴，
   于是「上一列文字到竖线」与「竖线到下一列文字」的留白完全相等（padding 两边都是 0.25rem）；
   上下各内缩 3px：比通高轻，也不会与上面的 3px 分割线连成一张网格。 */
.node-ping.ping-layout-columns .node-ping-row + .node-ping-row::before {
  content: '';
  position: absolute;
  left: -0.25rem;
  transform: translateX(-50%);
  top: 3px;
  bottom: 3px;
  width: 3px;
  background: var(--black);
}

/* 竖排时的 flex:1 1 auto 是让线路名吃掉整行剩下的宽度；变成列之后它会想撑满列高，
   改成按内容宽（align-items: flex-start 决定横向宽度），max-width 兜住超长线路名的省略号 */
.node-ping.ping-layout-columns .node-ping-name {
  flex: 0 0 auto;
  max-width: 100%;
}

/* 竖排时延迟/丢包靠固定 min-width + 右对齐成两条竖线；列里没有那两条竖线要对，
   min-width 归零（否则 88px 的列会被 4.2rem 的下限撑出去），文本跟着整列居中 */
.node-ping.ping-layout-columns .node-ping-value,
.node-ping.ping-layout-columns .node-ping-loss {
  min-width: 0;
  text-align: center;
}

/* --------------------------------------------------------------------------
   移植差异：上行/下行速度并入 NETWORK 区（原来在卡片底部那一栏，已拆掉）。
   结构：合计(↑) · 速率 …… 合计(↓) · 速率 —— 左一组、右一组，用 margin-left:auto 分组。
   合计与速率是各自独立的元素（不是嵌套），因为轮询更新走 scrambleTextIfChanged，
   它会直接改 textContent —— 嵌在里面的 <b> 会在第一次刷新时被抹掉；
   同理「·」分隔符只能用 ::before，不能写进 HTML。
   颜色：合计沿用上游的绿/蓝语义；速率用黑色，让随轮询跳动的数字更醒目。
   -------------------------------------------------------------------------- */
.net-totals {
  align-items: baseline;
  justify-content: flex-start;
  gap: 0.45rem;
}

.net-total-item.net-total-up {
  color: var(--accent-green);
}

.net-total-item.net-total-down {
  margin-left: auto;
  color: var(--accent-blue);
}

.net-rate {
  font-family: 'Space Grotesk', 'SF Mono', 'Consolas', 'Monaco', monospace;
  font-variant-numeric: tabular-nums;
  color: var(--black);
}

.net-rate::before {
  content: '·';
  margin-right: 0.35rem;
  color: var(--text-muted);
}

/* --------------------------------------------------------------------------
   移植差异：成本汇总卡片（站点设置 showCostCard / showCostMonthCard）
   参照上游主题仓库的 custom-body/cost.html：上游把「每月花多少」与「还剩多少没用掉」
   做成两张卡，移植版合成**一张卡里的两栏**——左 COST / MONTH、中间竖线、右
   RESIDUAL VALUE，最右边一行小字（两侧各自可在后台关掉）。卡片外壳与 .node-card
   同一套语言：粗边框 + 硬阴影（含 card-style-thin / -double 变体）。
   一栏 = 标题 + 大数字 + 标签行（标签行紧贴数字下方，窄屏自动换行）。
   节点一台都没填价格时由 JS 加 hidden 收起（下面有配套的 [hidden] 规则）。
   -------------------------------------------------------------------------- */
.cost-card {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
  padding: 1.25rem;
  background: var(--card-bg);
  border: var(--border-width) solid var(--black);
  box-shadow: var(--shadow-offset) var(--shadow-offset) 0 var(--shadow);
}

/* 一栏（COST / MONTH 或 RESIDUAL VALUE）：宽度跟着内容，放不下时换行 */
.cost-block {
  flex: 0 1 auto;
  min-width: 0;
}

.cost-title {
  display: block;
  margin-bottom: 0.35rem;
  font-family: 'Archivo Black', 'Monitor Display CJK', sans-serif;
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--black);
}

.cost-amount {
  font-family: 'Archivo Black', 'Monitor Display CJK', sans-serif;
  font-size: 1.8rem;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--black);
  text-shadow: 1px 1px 0 var(--white);
}

/* 两栏之间的竖线：跟着两栏的高度拉满（align-self: stretch） */
.cost-divider {
  align-self: stretch;
  width: 4px;
  min-height: 2.5rem;
  flex-shrink: 0;
  background: var(--black);
}

.cost-chips {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.6rem;
}

.cost-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.3rem 0.75rem;
  border: 2px solid var(--black);
  box-shadow: 3px 3px 0 var(--black);
  font-family: 'Space Grotesk', 'SF Mono', 'Consolas', 'Monaco', monospace;
  font-size: 0.75rem;
  font-weight: 900;
  font-variant-numeric: tabular-nums;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--black);
}

.cost-chip-blue {
  background: var(--accent-blue);
}

.cost-chip-red {
  background: var(--accent-red);
}

/* 没有告警时的那行字：与卡片里的次要信息同一个灰度 */
.cost-clear {
  font-family: 'Space Grotesk', 'SF Mono', 'Consolas', 'Monaco', monospace;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* 右侧小字：margin-left: auto 把它顶到最右边 */
.cost-meta {
  margin-left: auto;
  text-align: right;
  font-family: 'Space Grotesk', 'SF Mono', 'Consolas', 'Monaco', monospace;
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* 卡片风格变体：与 .node-card 的三种粗细保持一致 */
.card-style-thin .cost-card {
  border-width: 2px;
  box-shadow: 4px 4px 0 var(--shadow);
}

.card-style-double .cost-card {
  border-width: 6px;
  box-shadow: 8px 8px 0 var(--shadow);
  position: relative;
}

.card-style-double .cost-card::before {
  content: '';
  position: absolute;
  inset: 6px;
  border: 2px solid var(--black);
  pointer-events: none;
}

#cost-card[hidden] {
  display: none;
}

@media (max-width: 768px) {
  .cost-card {
    gap: 1rem;
    padding: 1rem;
  }
  /* 竖线在两栏堆叠时就没有意义了 */
  .cost-divider {
    display: none;
  }
  /* 让两栏自己决定并排还是堆叠：有空间就并排，放不下才换行 */
  .cost-block {
    flex: 1 1 11rem;
  }
  .cost-amount {
    font-size: 1.5rem;
  }
  .cost-meta {
    margin-left: 0;
    text-align: left;
  }
}

/* 标题下那行「系统 · CPU 型号」在窄卡片里会折成两行，上游的行高是 normal（约 1.15），
   两行贴着像一块灰砖。给到 1.35 后它读起来才是一段说明文字，不会和标题、指标抢注意力。 */
.node-info {
  line-height: 1.35;
}

/* --------------------------------------------------------------------------
   部件状态：JS 用 hidden 属性开关的元素
   script.js 关闭「显示后台入口按钮」（showLoginButton=false）时，是给按钮加
   hidden 属性。但浏览器默认的 [hidden] { display: none } 来自 UA 样式表，
   而作者样式里的 .btn-admin { display: flex } 优先级更高——实测开关关掉后按钮
   照旧显示（hidden 属性在、computed display 仍是 flex，34×34 可见）。
   上游 styles.css 里没有全局 [hidden] 规则（只有 .auth-oauth[hidden]），所以这里
   补一条作者规则；本文件在 styles.css 之后加载且特异性更高，不依赖顺序。
   scripts/check-adapt.mjs 会核对：凡 script.js 用 hidden 属性开关的元素，
   都必须有配套的 [hidden] 规则，否则这类开关以后还会静默失效。
   -------------------------------------------------------------------------- */
.btn-admin[hidden] {
  display: none;
}
