/* * openwrt-isp-split — LuCI 页面样式(原生 CSS,无框架、无外链) * * 配色原则: * · 背景/边框/文字全部走 LuCI 主题变量(--background-color-*、--border-color-*、 * --text-color-*、--primary-color-*),深浅色主题自动跟随; * · 只有"运营商身份色"(电信蓝/联通红/移动绿/教育网紫)是固定色相, * 统一收敛成 --isp-accent-* 变量,主题作者可以在 theme 里覆盖; * · 阴影只用中性黑(透明度很低),两种主题下都不会突兀。 */ .isp-app { --isp-card-bg: var(--background-color-high, rgba(128, 128, 128, 0.07)); --isp-card-bg-2: var(--background-color-medium, rgba(128, 128, 128, 0.12)); --isp-border: var(--border-color-low, rgba(128, 128, 128, 0.28)); --isp-border-strong: var(--border-color-medium, rgba(128, 128, 128, 0.45)); /* 次要文字用 --text-color-medium:原来的 --text-color-low 是主题里的边框/禁用色 (浅色 #bfbfbf、深色 #404040),深色主题下正文对比度只有 1.5:1,几乎看不见。 */ --isp-muted: var(--text-color-medium, rgba(128, 128, 128, 0.95)); --isp-text: var(--text-color-high, inherit); --isp-primary: var(--primary-color-high, currentColor); --isp-ok: var(--success-color-high, currentColor); --isp-warn: var(--warn-color-high, currentColor); --isp-bad: var(--error-color-high, currentColor); --isp-accent-tel: #2f6fd0; --isp-accent-uni: #d0362f; --isp-accent-cmcc: #2e9e5b; --isp-accent-cernet: #8a5cd6; --isp-accent-other: #8a8a8a; --isp-radius: 14px; --isp-shadow: 0 1px 2px rgba(0, 0, 0, 0.10), 0 4px 12px rgba(0, 0, 0, 0.06); } .isp-app * { box-sizing: border-box; } /* ---------------------------------------------------------------- 首装提示条(BUG-25) */ .isp-banner-wrap { display: block; } .isp-banner { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 12px 18px; margin: 0 0 16px 0; padding: 14px 16px; border: 1px solid var(--isp-border-strong); border-left: 5px solid var(--isp-warn); border-radius: var(--isp-radius); background: var(--background-color-high, rgba(128, 128, 128, 0.08)); box-shadow: var(--isp-shadow); } .isp-banner.is-warn { border-left-color: var(--warn-color-high, #d9822b); background: var(--background-color-high, rgba(128, 128, 128, 0.08)); } .isp-banner-msg { flex: 1 1 360px; min-width: 240px; color: var(--isp-text); font-size: 0.92rem; line-height: 1.5; } .isp-banner-msg strong { display: block; margin-bottom: 4px; font-size: 1rem; color: var(--warn-color-high, #b35c00); } .isp-banner-hint { margin-top: 6px; color: var(--isp-muted); font-size: 0.84rem; } .isp-banner-hint code { padding: 1px 5px; border-radius: 4px; background: var(--isp-card-bg-2); font-size: 0.84rem; } .isp-banner-btn { flex: 0 0 auto; align-self: center; white-space: nowrap; } .isp-banner-err { margin-top: 6px; color: var(--isp-bad); font-size: 0.84rem; line-height: 1.45; } .isp-banner-err:empty { display: none; } @media (max-width: 600px) { .isp-banner-btn { width: 100%; } } /* ---------------------------------------------------------------- 头部 */ .isp-hero { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 0 0 14px 0; } .isp-hero h2 { margin: 0; font-size: 1.35rem; font-weight: 600; color: var(--isp-text); } .isp-hero .isp-sub { flex: 1 1 260px; min-width: 200px; color: var(--isp-muted); font-size: 0.86rem; line-height: 1.45; } .isp-toolbar { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; } .isp-pill { display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px; border-radius: 999px; background: var(--isp-card-bg-2); border: 1px solid var(--isp-border); font-size: 0.78rem; color: var(--isp-muted); white-space: nowrap; } .isp-pill .isp-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--isp-ok); } .isp-pill.is-paused .isp-dot { background: var(--isp-warn); } /* 刷新中:徽标转中文「刷新中…」并让图标转圈,替代主题自带的英文 Refreshing 提示 */ .isp-pill.is-busy { color: var(--isp-text); border-color: var(--isp-border-strong); } .isp-pill .isp-ico { display: inline-flex; align-items: center; } .isp-spin { display: inline-flex; animation: isp-rotate 0.9s linear infinite; transform-origin: 50% 50%; } @keyframes isp-rotate { to { transform: rotate(360deg); } } @media (prefers-reduced-motion: reduce) { .isp-spin { animation: none; } } /* ---------------------------------------------------------------- 卡片网格 */ .isp-cards { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-bottom: 12px; } .isp-grid-2 { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 12px; margin-bottom: 12px; } .isp-grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin-bottom: 12px; } .isp-card { position: relative; background: var(--isp-card-bg); border: 1px solid var(--isp-border); border-radius: var(--isp-radius); box-shadow: var(--isp-shadow); padding: 14px 16px; min-width: 0; overflow: hidden; } .isp-card > h3 { margin: 0 0 10px 0; font-size: 0.92rem; font-weight: 600; color: var(--isp-muted); display: flex; align-items: center; gap: 8px; } /* 运营商卡片:顶部身份色条 */ .isp-card-op { padding-top: 18px; } .isp-card-op::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 4px; background: var(--isp-accent, var(--isp-accent-other)); } /* 卡头(方案 B,0.3.1):44px 图标左置 + 名称右排。 0.3.0 是「名称左、20px 图标右」,运营商多起来时右角的小图标很难认, 且各家 SVG 的视觉重心不一致;改成左置大图后,卡片读起来像一张名片: 先认标志,再看名字,最后看数字。 */ .isp-card-head { display: flex; align-items: center; justify-content: flex-start; gap: 10px; margin-bottom: 10px; } .isp-op-name { font-size: 1rem; font-weight: 600; color: var(--isp-text); letter-spacing: 0.02em; /* 四列布局下内容宽 ~218px:44px 图标 + 10px 间距后, 长运营商名(ISP_TABLE 自定义标签)不要顶破卡片 */ min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* 图标位:logo 与「无 logo 的 glyph 占位」共用 44×44 的盒子, 两者视觉同构——卡片并排时卡头高度、名称基线完全对齐。 */ .isp-op-ico { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 44px; width: 44px; height: 44px; color: var(--isp-accent, var(--isp-accent-other)); opacity: 0.95; } /* 品牌 PNG(192×192 透明底):按盒子缩放,不拉伸、不裁切 */ .isp-brand-logo { display: block; width: 100%; height: 100%; object-fit: contain; } /* 深色主题的 logo 提亮(实测,不是"看着差不多"): 电信官方蓝 #004080 的 luminance 太低,直接贴到深色卡面 (#222) 上对比度只有 1.55:1——44px 下像蒙了一层灰。只提亮不改色相(brightness/saturate): 电信 1.55:1 → 3.31:1 联通 2.47:1 → 3.84:1 移动 2.46:1 → 5.31:1 (前一列按 PNG 原色对 #222 算,后一列从深色截图里采 logo 像素实测。) 浅色主题不动:白底上三家分别是 10.27 / 6.44 / 6.48:1,提亮反而失真。 LuCI 的 bootstrap 主题用 :root[data-darkmode="true"] 切整套变量,这里跟随 同一个属性——用户点顶栏开关或跟系统深色,都会命中。 */ [data-darkmode="true"] .isp-brand-logo { filter: brightness(1.55) saturate(1.05); } /* 兜底卡:与运营商卡同构(顶部色条 + 44px 卡头 + 大数字)。 它是「未归属任何运营商」的那一格,所以身份色用中性灰而不是某家的品牌色。 */ .isp-card-fb { --isp-accent: var(--isp-accent-other); } /* 网格里只有一条「没有启用的运营商」提示时,让它横跨整行, 否则会被 repeat(4) 挤成 1/4 宽的一条细缝 */ .isp-cards > .isp-empty { grid-column: 1 / -1; } .isp-big { display: inline-block; font-size: 1.9rem; line-height: 1.15; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--isp-text); } .isp-unit { margin-left: 6px; font-size: 0.76rem; color: var(--isp-muted); } .isp-op-sub { margin-top: 4px; font-size: 0.8rem; color: var(--isp-muted); font-variant-numeric: tabular-nums; } .isp-route { display: flex; align-items: center; gap: 6px; margin-top: 10px; font-size: 0.8rem; color: var(--isp-muted); } .isp-route .isp-arrows { display: inline-flex; align-items: center; gap: 2px; font-size: 1rem; line-height: 1; color: var(--isp-accent, var(--isp-accent-other)); } .isp-meter { margin-top: 10px; height: 6px; border-radius: 999px; background: var(--isp-card-bg-2); overflow: hidden; } .isp-meter > i { display: block; height: 100%; border-radius: 999px; background: var(--isp-accent, var(--isp-accent-other)); opacity: 0.85; transition: width 0.4s ease; } /* 零流量卡片:不画进度条(否则 0% 只剩一个孤点),改成一行占位「—」 */ .isp-meter-empty { margin-top: 10px; height: 6px; display: flex; align-items: center; font-size: 0.8rem; line-height: 1; color: var(--isp-muted); font-variant-numeric: tabular-nums; } /* ---------------------------------------------------------------- 列表 / 表格 */ .isp-list { display: flex; flex-direction: column; gap: 8px; margin: 0; padding: 0; list-style: none; } .isp-line { display: grid; grid-template-columns: 10px minmax(64px, auto) 1fr auto; gap: 10px; align-items: center; padding: 8px 10px; border-radius: 10px; background: var(--isp-card-bg-2); border: 1px solid transparent; min-width: 0; } .isp-line .isp-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--isp-muted); } .isp-line.is-online .isp-dot { background: var(--isp-ok); box-shadow: 0 0 0 3px rgba(46, 158, 91, 0.18); } .isp-line.is-offline .isp-dot { background: var(--isp-bad); } .isp-line .isp-line-name { font-weight: 600; color: var(--isp-text); font-size: 0.88rem; white-space: nowrap; } .isp-line .isp-line-track { font-size: 0.78rem; color: var(--isp-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .isp-line .isp-line-pct { font-size: 0.78rem; color: var(--isp-muted); font-variant-numeric: tabular-nums; white-space: nowrap; } .isp-table { width: 100%; border-collapse: collapse; font-size: 0.82rem; } .isp-table th { text-align: left; font-weight: 500; color: var(--isp-muted); padding: 4px 8px 6px 0; border-bottom: 1px solid var(--isp-border); white-space: nowrap; } .isp-table td { padding: 6px 8px 6px 0; border-bottom: 1px solid var(--isp-border); color: var(--isp-text); vertical-align: middle; } .isp-table tr:last-child td { border-bottom: none; } .isp-table .num { text-align: right; font-variant-numeric: tabular-nums; } .isp-tag { display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: 0.72rem; background: var(--isp-card-bg-2); color: var(--isp-muted); white-space: nowrap; } /* 长的说明性标签(如出线方式解释)允许换行,避免窄屏把页面撑宽。 圆角从 999px 收敛成 8px:胶囊形的半圆端在换行后会把首尾字符压在弧线上, 看起来像被截断;直角一点的说明条才容得下两行完整文字。 */ .isp-btn-row .isp-tag { white-space: normal; word-break: break-word; max-width: 100%; padding: 4px 10px; border-radius: 8px; line-height: 1.5; } .isp-tag.is-ok { color: var(--isp-ok); } .isp-tag.is-warn { color: var(--isp-warn); } .isp-tag.is-bad { color: var(--isp-bad); } .isp-empty { color: var(--isp-muted); font-size: 0.84rem; padding: 8px 0; } .isp-time { display: flex; flex-wrap: wrap; gap: 6px 18px; font-size: 0.82rem; color: var(--isp-muted); } .isp-time .isp-tag { margin-left: 6px; } .isp-time b { color: var(--isp-text); font-weight: 500; font-variant-numeric: tabular-nums; } /* ---------------------------------------------------------------- 操作区 */ .isp-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 10px 0; } .isp-actions .cbi-button { display: inline-flex; align-items: center; gap: 6px; } .isp-ico { display: inline-flex; align-items: center; justify-content: center; } .isp-out { max-height: 22em; overflow: auto; white-space: pre-wrap; word-break: break-all; font-family: var(--font-mono, monospace); font-size: 12px; line-height: 1.5; padding: 10px 12px; border-radius: 10px; border: 1px solid var(--isp-border); background: var(--isp-card-bg); color: var(--isp-text); margin: 0; } /* 没有输出时不留空盒子:hidden 属性必须真的生效(主题里有 pre{display:block}, 单靠 hidden 会被作者样式盖掉,只剩一个 22px 高的空边框)。 */ .isp-out[hidden], .isp-out.is-empty { display: none !important; } /* ---------------------------------------------------------------- 表单(配置页) */ .isp-section { background: var(--isp-card-bg); border: 1px solid var(--isp-border); border-radius: var(--isp-radius); box-shadow: var(--isp-shadow); padding: 14px 16px 16px; margin-bottom: 12px; } .isp-section > h3 { display: flex; align-items: center; gap: 8px; margin: 0 0 4px 0; font-size: 1rem; font-weight: 600; color: var(--isp-text); } .isp-section > .isp-desc { margin: 0 0 12px 0; font-size: 0.82rem; line-height: 1.5; color: var(--isp-muted); } .isp-form-table { width: 100%; border-collapse: collapse; font-size: 0.84rem; } .isp-form-table th { text-align: left; font-weight: 500; color: var(--isp-muted); padding: 0 8px 6px 0; border-bottom: 1px solid var(--isp-border); white-space: nowrap; } .isp-form-table td { padding: 6px 8px 6px 0; border-bottom: 1px solid var(--isp-border); vertical-align: top; } .isp-form-table tr:last-child td { border-bottom: none; } .isp-form-table input[type="text"], .isp-form-table select { width: 100%; min-width: 90px; } .isp-err { display: none; margin-top: 3px; font-size: 0.76rem; line-height: 1.35; color: var(--isp-bad); } .isp-err.is-on { display: block; } .isp-row-actions { display: inline-flex; gap: 4px; align-items: center; white-space: nowrap; } .isp-icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; padding: 0; border-radius: 8px; border: 1px solid var(--isp-border); background: var(--isp-card-bg-2); color: var(--isp-text); cursor: pointer; } .isp-icon-btn:hover:not(:disabled) { border-color: var(--isp-border-strong); } .isp-icon-btn:disabled { opacity: 0.4; cursor: default; } .isp-icon-btn.is-danger { color: var(--isp-bad); } .isp-switch { position: relative; display: inline-block; width: 40px; height: 22px; vertical-align: middle; } .isp-switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; } .isp-switch span { position: absolute; inset: 0; border-radius: 999px; background: var(--isp-card-bg-2); border: 1px solid var(--isp-border); transition: background 0.2s ease; pointer-events: none; } .isp-switch span::after { content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: var(--isp-muted); transition: transform 0.2s ease, background 0.2s ease; } .isp-switch input:checked + span { background: var(--isp-primary); border-color: transparent; } .isp-switch input:checked + span::after { transform: translateX(18px); background: var(--background-color-high, #fff); } .isp-btn-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 10px; } /* 表单下方的整段说明(如「出线方式」的解释):允许换行,不加胶囊边框 */ .isp-note { margin-top: 8px; font-size: 0.78rem; line-height: 1.55; color: var(--isp-muted); overflow-wrap: anywhere; } .isp-test-result { font-size: 0.78rem; line-height: 1.35; color: var(--isp-muted); min-height: 1em; } .isp-test-result.is-ok { color: var(--isp-ok); } .isp-test-result.is-bad { color: var(--isp-bad); } .isp-footer-actions { /* 曾经是 position:sticky; bottom:0 —— 会浮在视口底部盖住 DNS 表最后两行。 保存按钮放在正常文档流里即可,页脚用一条上边框跟内容分开。 */ display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 8px 0; padding: 12px 0 0 0; border-top: 1px solid var(--isp-border); background: transparent; } /* ---------------------------------------------------------------- 响应式 */ @media (max-width: 1100px) { .isp-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } .isp-grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } } @media (max-width: 820px) { .isp-grid-2, .isp-grid-3 { grid-template-columns: minmax(0, 1fr); } } @media (max-width: 620px) { .isp-cards { grid-template-columns: minmax(0, 1fr); } .isp-big { font-size: 1.7rem; } .isp-line { grid-template-columns: 10px 1fr; row-gap: 2px; } .isp-line .isp-line-pct { grid-column: 2; } /* 表单表格在窄屏改成两块:上半行是标签,下半行是输入 */ .isp-form-table thead { display: none; } .isp-form-table tr { display: block; padding: 8px 0; border-bottom: 1px solid var(--isp-border); } .isp-form-table td { display: block; border: none; padding: 2px 0; } .isp-form-table td::before { content: attr(data-th); display: block; font-size: 0.72rem; color: var(--isp-muted); margin-bottom: 2px; } } /* lc:begin */ /* ---------------------------------------------------------------- 线路体检(0.3.0) 本段到文件末尾全部是 .lc-* 样式(体检页 / 配置页开关区), FEATURE_LINECHECK=0 时整段物理删除。 教训(F7):标记必须包住样式体;只包一行标题注释,规则会裸奔进 0.2.3 包。 */ .lc-cards { grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); } .lc-card { display: flex; flex-direction: column; gap: 10px; padding: 14px 16px; border-top: 3px solid var(--lc-accent, var(--isp-border-strong)); position: relative; } .lc-head { display: flex; align-items: center; justify-content: flex-start; gap: 10px; } /* 与状态页同一个卡头语言(0.3.1):44px logo/glyph 左置。 映射不到运营商的 wan(isp 为空、自定义源)用线稿 glyph 占位,盒子同尺寸。 */ .lc-head-ico { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 44px; width: 44px; height: 44px; } .lc-title { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; min-width: 0; /* 加了 44px 图标后,标题占中间的剩余宽度,评分仍然贴右 */ flex: 1 1 auto; } .lc-name { font-size: 1.08rem; font-weight: 650; color: var(--isp-text); letter-spacing: 0.01em; } .lc-dev { font-size: 0.78rem; color: var(--isp-muted); background: var(--isp-card-bg-2); border-radius: 999px; padding: 1px 8px; } .lc-state { font-size: 0.76rem; color: var(--isp-muted); } .lc-state.is-on::before, .lc-state.is-off::before { content: ""; display: inline-block; width: 7px; height: 7px; margin-right: 4px; border-radius: 50%; background: var(--isp-ok); vertical-align: 1px; } .lc-state.is-off::before { background: var(--isp-bad); } .lc-score { display: flex; align-items: baseline; gap: 3px; white-space: nowrap; } .lc-total { font-size: 1.5rem; font-weight: 700; color: var(--isp-text); line-height: 1; } .lc-max { font-size: 0.75rem; color: var(--isp-muted); } /* 评分徽标:A/B/C/D 四档,颜色只在徽标上,不铺满整张卡 */ .lc-grade { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; margin-right: 6px; border-radius: 9px; font-size: 1rem; font-weight: 700; color: #fff; background: var(--isp-muted); } .lc-grade.is-a { background: var(--isp-ok); } .lc-grade.is-b { background: var(--isp-primary); } .lc-grade.is-c { background: var(--isp-warn); } .lc-grade.is-d { background: var(--isp-bad); } .lc-kv { display: flex; flex-direction: column; gap: 5px; padding: 9px 10px; border-radius: 10px; background: var(--isp-card-bg-2); } .lc-row { display: flex; gap: 10px; align-items: baseline; font-size: 0.86rem; line-height: 1.45; } .lc-key { flex: 0 0 4.9em; color: var(--isp-muted); } .lc-val { flex: 1 1 auto; min-width: 0; color: var(--isp-text); word-break: break-word; display: flex; flex-wrap: wrap; gap: 6px; align-items: baseline; } .lc-sub { font-size: 0.76rem; color: var(--isp-muted); } .lc-tag { display: inline-block; padding: 0 7px; border-radius: 999px; border: 1px solid var(--isp-border); font-size: 0.72rem; line-height: 1.6; color: var(--isp-muted); background: var(--isp-card-bg); } .lc-tag.is-ok { color: var(--isp-ok); border-color: currentColor; } .lc-tag.is-warn { color: var(--isp-warn); border-color: currentColor; } .lc-tag.is-bad { color: var(--isp-bad); border-color: currentColor; } .lc-tag.is-muted { color: var(--isp-muted); } /* 四段延迟:等宽四格,窄屏两列 */ .lc-segs { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; } .lc-seg { display: flex; flex-direction: column; gap: 1px; padding: 7px 8px; border: 1px solid var(--isp-border); border-radius: 9px; background: var(--isp-card-bg); min-width: 0; } .lc-seg-label { font-size: 0.7rem; color: var(--isp-muted); letter-spacing: 0.02em; } .lc-seg-value { font-size: 0.95rem; font-weight: 600; color: var(--isp-text); font-variant-numeric: tabular-nums; } .lc-line2 { display: flex; flex-wrap: wrap; gap: 4px 12px; font-size: 0.78rem; color: var(--isp-muted); } .lc-advice { display: flex; gap: 7px; align-items: flex-start; padding: 8px 10px; border-radius: 9px; border-left: 3px solid var(--lc-accent, var(--isp-border-strong)); background: var(--isp-card-bg-2); font-size: 0.84rem; line-height: 1.5; color: var(--isp-text); } .lc-advice .isp-ico { flex: 0 0 auto; margin-top: 2px; color: var(--lc-accent, var(--isp-muted)); } .lc-advice.is-bad { border-left-color: var(--isp-bad); } .lc-toggle { align-self: flex-start; display: inline-flex; align-items: center; gap: 4px; padding: 2px 0; border: 0; background: none; color: var(--isp-primary); font-size: 0.8rem; cursor: pointer; } .lc-toggle .isp-ico { transition: transform 0.15s ease; } .lc-toggle.is-open .isp-ico { transform: rotate(180deg); } .lc-detail { display: flex; flex-direction: column; gap: 8px; padding-top: 8px; border-top: 1px dashed var(--isp-border); } .lc-detail[hidden] { display: none !important; } .lc-detail h4 { margin: 2px 0 0 0; font-size: 0.8rem; font-weight: 600; color: var(--isp-muted); } .lc-table { width: 100%; border-collapse: collapse; font-size: 0.78rem; } .lc-table th, .lc-table td { text-align: left; padding: 4px 6px; border-bottom: 1px solid var(--isp-border); vertical-align: top; word-break: break-word; } .lc-table th { color: var(--isp-muted); font-weight: 500; white-space: nowrap; } .lc-table td:nth-child(3) { white-space: nowrap; font-variant-numeric: tabular-nums; } .lc-caps { margin: 0; padding-left: 1.2em; font-size: 0.78rem; color: var(--isp-warn); } .lc-raw { font-size: 0.74rem; color: var(--isp-muted); line-height: 1.5; word-break: break-word; } .lc-meta { font-size: 0.78rem; color: var(--isp-muted); margin: 0 0 10px 0; line-height: 1.5; } .lc-privacy { display: flex; gap: 7px; align-items: flex-start; margin: 0 0 12px 0; padding: 8px 10px; border: 1px solid var(--isp-border); border-radius: 10px; background: var(--isp-card-bg); font-size: 0.78rem; color: var(--isp-muted); line-height: 1.5; } .lc-privacy .isp-ico { flex: 0 0 auto; margin-top: 1px; } .lc-msg { font-size: 0.82rem; margin: 0 0 10px 0; color: var(--isp-muted); } .lc-msg.is-info { color: var(--isp-primary); } .lc-msg.is-ok { color: var(--isp-ok); } .lc-msg.is-bad { color: var(--isp-bad); } .lc-run:disabled { opacity: 0.6; cursor: default; } /* 尚未体检时的占位卡(BUG-23:没缓存也必须有一个能看的页面,不能白屏) */ .lc-placeholder { grid-column: 1 / -1; display: block; padding: 18px 20px; } .lc-ph-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; font-size: 0.95rem; color: var(--isp-text); } .lc-ph-head .isp-ico { color: var(--isp-primary); } .lc-ph-text { margin: 0 0 8px 0; font-size: 0.84rem; line-height: 1.65; color: var(--isp-muted); } .lc-ph-note { margin: 0; font-size: 0.78rem; line-height: 1.6; color: var(--isp-muted); opacity: 0.85; } @media (max-width: 520px) { .lc-cards { grid-template-columns: 1fr; } .lc-segs { grid-template-columns: repeat(2, minmax(0, 1fr)); } .lc-table thead { display: none; } .lc-table tr { display: block; padding: 4px 0; border-bottom: 1px solid var(--isp-border); } .lc-table td { display: flex; gap: 8px; border: 0; padding: 1px 0; } .lc-table td::before { content: attr(data-th); flex: 0 0 4.5em; color: var(--isp-muted); } } /* 配置页:线路体检开关区的小字/行内控件 */ .lc-hint { font-size: 0.74rem; color: var(--isp-muted); line-height: 1.4; margin-top: 2px; } .lc-inline { display: flex; align-items: center; gap: 6px; } .lc-inline input[type="number"] { max-width: 7em; } .lc-privacy-note { border-left: 3px solid var(--isp-primary); padding-left: 10px; } /* lc:end */ /* ---------------------------------------------------------------- 环境横幅(doctor,0.3.3) */ .isp-doctor { margin: 0; } /* fail:红条。与 .is-warn 同一套骨架,只换左边框与标题色——不新增视觉语言。 这里没有关闭按钮,也没有折叠:有 fail 项时横幅必须一直在。 */ .isp-banner.is-bad { border-left-color: var(--isp-bad); } .isp-banner.is-bad .isp-banner-msg strong { color: var(--isp-bad); } .isp-banner-actions { display: flex; flex: 0 0 auto; flex-direction: column; gap: 8px; align-items: stretch; } .isp-env-list { margin-top: 8px; display: flex; flex-direction: column; gap: 8px; } .isp-env-list[hidden] { display: none; } .isp-env-item { border-left: 3px solid var(--isp-border-strong); padding-left: 8px; } .isp-env-title { display: block; font-weight: 600; color: var(--isp-text); } .isp-env-adv { display: block; margin-top: 2px; color: var(--isp-muted); font-size: 0.86rem; line-height: 1.45; } /* 全绿:一行,尽量不占版面 */ .isp-env-ok { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; margin: 0 0 16px 0; padding: 8px 14px; border: 1px solid var(--isp-border); border-left: 4px solid var(--isp-ok); border-radius: var(--isp-radius); background: var(--isp-card-bg); color: var(--isp-text); font-size: 0.9rem; } .isp-env-ok .isp-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--isp-ok); } .isp-env-sub { color: var(--isp-muted); font-size: 0.84rem; } .isp-env-wait { margin: 0 0 16px 0; color: var(--isp-muted); font-size: 0.86rem; } /* ---------------------------------------------------------------- 首启向导(0.3.3) 沿用 isp-* 那套变量与卡片骨架:向导只新增"步骤条 / 自检行 / 完成态"三块, 不复用体检页那套样式类——它可被构建期剥离,向导不能跟着消失。 */ .wz-steps { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; margin: 0 0 16px 0; } .wz-step { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; padding: 10px 12px; text-align: left; background: var(--isp-card-bg); border: 1px solid var(--isp-border); border-radius: var(--isp-radius); color: var(--isp-muted); cursor: pointer; } .wz-step.is-current { border-color: var(--isp-primary); box-shadow: inset 0 0 0 1px var(--isp-primary); color: var(--isp-text); } .wz-step.is-done { border-left: 4px solid var(--isp-ok); color: var(--isp-text); } .wz-step[disabled] { opacity: 0.55; cursor: not-allowed; } .wz-step-no { font-size: 0.72rem; letter-spacing: 0.08em; } .wz-step-title { font-size: 0.95rem; font-weight: 600; } .wz-step-hint { font-size: 0.76rem; } .wz-body { display: flex; flex-direction: column; gap: 14px; margin-bottom: 14px; } .wz-card { padding: 14px 16px; } .wz-card h3 { margin: 0 0 10px 0; } .wz-summary { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 14px 0 10px 0; } .wz-summary-head { color: var(--isp-muted); font-size: 0.88rem; } .wz-doctor { display: flex; flex-direction: column; gap: 12px; } .wz-checks { display: flex; flex-direction: column; gap: 10px; } .wz-check { padding: 12px 14px; border: 1px solid var(--isp-border); border-left: 4px solid var(--isp-border-strong); border-radius: var(--isp-radius); background: var(--isp-card-bg); } .wz-check.is-ok { border-left-color: var(--isp-ok); } .wz-check.is-warn { border-left-color: var(--isp-warn); } .wz-check.is-bad { border-left-color: var(--isp-bad); } .wz-check-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; } .wz-check-title { font-weight: 600; color: var(--isp-text); } .wz-check-conf { margin-left: auto; color: var(--isp-muted); font-size: 0.8rem; } .wz-find { margin-top: 6px; } .wz-find-line { color: var(--isp-muted); font-size: 0.86rem; line-height: 1.5; } .wz-advice { margin-top: 6px; color: var(--isp-text); font-size: 0.88rem; line-height: 1.5; } .wz-kv { display: flex; gap: 10px; padding: 3px 0; font-size: 0.88rem; } .wz-kv-key { flex: 0 0 96px; color: var(--isp-muted); } .wz-kv-val { flex: 1 1 auto; color: var(--isp-text); word-break: break-all; } .wz-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 12px; } .wz-hint { margin: 8px 0 0 0; color: var(--isp-muted); font-size: 0.84rem; line-height: 1.5; } .wz-meta { margin-top: 8px; color: var(--isp-muted); font-size: 0.84rem; } .wz-note { min-height: 1.2em; margin: 10px 0 6px 0; color: var(--isp-primary); font-size: 0.88rem; } .wz-note.is-bad { color: var(--isp-bad); } .wz-note.is-ok { color: var(--isp-ok); } .wz-loading { color: var(--isp-muted); font-size: 0.88rem; } .wz-block { margin-top: 10px; padding: 10px 12px; border-radius: var(--isp-radius); background: var(--isp-card-bg-2); color: var(--isp-text); font-size: 0.88rem; line-height: 1.5; } .wz-preview { margin-top: 12px; } .wz-preview-head { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-bottom: 8px; color: var(--isp-muted); font-size: 0.88rem; } .wz-dry { max-height: 320px; } .isp-out.is-bad { border-color: var(--isp-bad); } .wz-cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; margin-top: 12px; } .wz-ack { display: flex; align-items: center; gap: 8px; color: var(--isp-text); font-size: 0.88rem; } .wz-list { margin: 8px 0 0 0; padding-left: 20px; color: var(--isp-text); font-size: 0.9rem; line-height: 1.55; } .wz-footer { display: flex; align-items: center; gap: 10px; margin: 14px 0 6px 0; } .wz-spacer { flex: 1 1 auto; } .wz-done { display: flex; gap: 14px; align-items: flex-start; } .wz-done-mark { flex: 0 0 auto; width: 44px; height: 44px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: var(--isp-ok); color: #fff; font-size: 1.4rem; font-weight: 700; } .wz-done-text strong { display: block; font-size: 1.05rem; color: var(--isp-text); } .wz-done-banner strong { color: var(--isp-ok); } @media (max-width: 820px) { .wz-steps { grid-template-columns: repeat(2, minmax(0, 1fr)); } .wz-cards { grid-template-columns: 1fr; } }