* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }

svg.ic { flex: none; display: inline-block; vertical-align: middle; }
body {
  display: flex; flex-direction: column; overflow: hidden;
  font: 13px/1.5 "Microsoft YaHei", "PingFang SC", sans-serif;
  color: #2c3e50;
}

/* ── 顶部工具栏（与 idc-2d-modeler 同风格：深青灰底 + 青绿强调）── */
.tb {
  display: flex; align-items: center; gap: 4px;
  padding: 8px 10px; background: #2c3e50; border-bottom: 1px solid #1a252f;
  overflow-x: auto; overflow-y: hidden;
  scrollbar-width: thin; scrollbar-color: #56708c #2c3e50;
}
.tb::-webkit-scrollbar { height: 4px; }
.tb::-webkit-scrollbar-thumb { background: #56708c; border-radius: 2px; }
.brand {
  flex: none; color: #fff; margin-right: 8px;
  white-space: nowrap; font-size: 12.5px;
  font-family: "Segoe UI", "Microsoft YaHei", "PingFang SC", sans-serif; font-weight: 600;
  display: flex; align-items: center; gap: 7px;
  text-decoration: none; cursor: pointer;
}
.brand:hover, .brand:focus-visible { color: #7fd8c6; }
.brand .logo { height: 23.4px; width: auto; display: block; }
.brand span { position: relative; top: -0.7px; }
.tb-menu { display: flex; align-items: center; gap: 4px; flex: 1; }
.group {
  display: flex; align-items: center; gap: 3px; flex: none;
  padding: 0 6px; border-left: 1px solid #46607a;
}
.group:first-of-type { border-left: none; }
.tb button {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  min-width: 34px; height: 32px; padding: 0 8px;
  border: 1px solid #56708c; border-radius: 5px;
  background: #3b5872; color: #eaf2f8; cursor: pointer;
  font-size: 13px; line-height: 1; white-space: nowrap; user-select: none;
}
.tb button i { display: flex; align-items: center; justify-content: center; line-height: 0; }
.tb svg.ic { width: 18px; height: 18px; flex: none; }
.tb button:hover { background: #4a6b8a; }
.tb button.active { background: #1abc9c; border-color: #16a085; color: #fff; }
/* 导出合并入口：按钮右侧下拉小三角（与 DCDesigner 一致，提示可展开导出格式菜单） */
#btn-export.has-pop::after {
  content: ""; flex: none; width: 0; height: 0; margin-left: 1px;
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 5px solid currentColor; opacity: .8;
}
.tb button.open, .tb button.open:hover { background: #1abc9c; border-color: #16a085; color: #fff; }
.tb-right { flex: none; display: flex; align-items: center; gap: 6px; }
/* 右上角低频入口（反馈 / 多语言）：幽灵图标样式——默认无底色无边框仅小图标，hover 才显现按钮底 */
.tb a.btn { text-decoration: none; }
.tb .tb-feedback,
.tb .btn-lang {
  min-width: 26px; width: 26px; height: 26px; padding: 0;
  background: transparent; border-color: transparent;
  color: #b8c8d8;
}
.tb .tb-feedback:hover,
.tb .btn-lang:hover { background: #4a6b8a; border-color: #56708c; color: #fff; }
.tb .tb-feedback svg.ic { width: 15px; height: 15px; }
/* 多语言图标原图 1024 视框内留白较多，略放大到 17px 与相邻 15px 反馈图标视觉等大 */
.tb .btn-lang svg.ic { width: 17px; height: 17px; }

/* 多语言下拉菜单（i18n.js 动态创建，挂在语言按钮下方右对齐） */
.lang-menu {
  position: fixed; z-index: 1200; min-width: 168px; padding: 5px;
  background: #fff; border: 1px solid #d8dee5; border-radius: 8px;
  box-shadow: 0 8px 24px rgba(30, 50, 70, .18);
}
.lang-menu .lang-item {
  display: flex; align-items: center; gap: 8px; width: 100%; padding: 7px 10px;
  border: 0; border-radius: 6px; background: none; cursor: pointer;
  font-size: 13px; color: #2c3e50; text-align: left;
}
.lang-menu .lang-item:hover { background: #f0faf8; }
.lang-menu .lang-item .lang-tick { width: 12px; color: #1abc9c; font-weight: 700; visibility: hidden; }
.lang-menu .lang-item.on { background: #e6f7f2; color: #16a085; font-weight: 600; }
.lang-menu .lang-item.on .lang-tick { visibility: visible; }
.lang-menu .lang-item .lang-sub { margin-left: auto; font-size: 11px; color: #9aa5b1; font-weight: 400; }
.lang-menu .lang-item.on .lang-sub { color: #1abc9c; }

/* ── 主区域 ── */
main { flex: 1; display: flex; min-height: 0; position: relative; --props-w: 300px; }
#view-wrap { flex: 1; min-width: 0; position: relative; display: flex; }
#cv { flex: 1; display: block; min-width: 0; background: #e8eef2; cursor: default; }
#cv.draw { cursor: crosshair; }

/* 视图区右上角视图切换：三段式分段控件（TAB）光缆图 / 纤芯图 / 管井图
   —— 三段连成一条整体轨道（无间隙、等宽、分隔线），当前段如「选中卡舌」整段填满，
   点击任一段即切换视图，营造明确的 TAB 切换感；段序即设计流程（光缆图→纤芯图→管井图） */
#view-steps {
  position: absolute; top: 10px; right: 10px; z-index: 5;
  display: flex; align-items: stretch;
  padding: 0; overflow: hidden;
  background: rgba(238, 242, 246, .45); border: 1px solid rgba(120, 140, 160, .5); border-radius: 16px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, .1);
  font-size: 12px; line-height: 1; white-space: nowrap; user-select: none;
}
#view-steps .vs-step {
  display: flex; align-items: center; justify-content: center;
  min-width: 62px; height: 26px; padding: 0 10px;
  background: transparent; border: none; border-radius: 0;
  color: #46607a; cursor: pointer; font-size: 12px; font-family: inherit; line-height: 1;
  transition: background .15s, color .15s;
}
/* 段间分隔线（选中段两侧隐藏，保证卡舌边缘完整） */
#view-steps .vs-step + .vs-step { border-left: 1px solid rgba(120, 140, 160, .4); }
#view-steps .vs-step.on + .vs-step, #view-steps .vs-step:has(+ .vs-step.on) { border-left-color: transparent; }
#view-steps .vs-step:hover { background: rgba(120, 140, 160, .22); color: #2c3e50; }
/* 当前卡舌：主题绿整段填满（与顶栏 active 按钮同色系） */
#view-steps .vs-step.on { background: #1abc9c; color: #fff; font-weight: bold; }
/* 已完成步骤（当前步之前）：深绿标识“这一步已走过”（浅底上用深色系保证对比度） */
#view-steps .vs-step.done { color: #148f77; }

/* ── 提示浮层（Toast）：必须完成的先决动作类提醒（如未指定目标对象就先触发批量动作）
   视图区顶部居中弹出，深色卡片 + 警示图标 + 标题/说明 + 「知道了」按钮；
   点击卡片/按钮/Esc 立即关闭，默认 3.4s 自动消失（toast.js）── */
#toast-wrap {
  position: absolute; top: 44px; left: 50%; transform: translateX(-50%);
  z-index: 60; display: flex; justify-content: center;
  max-width: min(560px, calc(100% - 24px));
}
#toast-wrap[hidden] { display: none; }
#toast-wrap .toast-card {
  display: flex; align-items: flex-start; gap: 8px;
  padding: 10px 12px; border-radius: 8px; cursor: pointer;
  background: rgba(44, 62, 80, .96); color: #eaf2f8;
  border-left: 4px solid #f1c40f; box-shadow: 0 8px 26px rgba(10, 25, 40, .38);
  font-size: 12px; line-height: 1.55;
}
#toast-wrap .toast-card .ic { width: 18px; height: 18px; flex: none; color: #f1c40f; margin-top: 1px; }
#toast-wrap .toast-msg { flex: 1; min-width: 0; }
#toast-wrap .toast-msg b { display: block; font-size: 13px; color: #ffe9a8; margin-bottom: 2px; }
#toast-wrap .toast-msg span { color: #dbe6ef; }
#toast-wrap .toast-x {
  flex: none; align-self: center; padding: 4px 9px; white-space: nowrap;
  border: 1px solid #56708c; border-radius: 5px; background: #3b5872;
  color: #eaf2f8; cursor: pointer; font-size: 12px; font-family: inherit;
}
#toast-wrap .toast-x:hover { background: #4a6b8a; border-color: #9dc3e8; }
#toast-wrap .toast-card.in { animation: toast-in .22s ease-out; }
@keyframes toast-in { from { opacity: 0; transform: translateY(-10px) scale(.98); } to { opacity: 1; transform: none; } }

/* ── 纤芯图视图（无 GIS 底图）：左侧接续工具面板 + 隐藏地图专用 UI ── */
body.fiber-view #toolbox { display: none; }                                  /* 地图录入工具条在本视图无意义 */
/* ── 路由图视图：取消左侧工具栏（选中光缆段即自动进入敷设状态，无需工具按钮）── */
body.layroute-view #toolbox { display: none; }
/* 视图工具分工：光缆图只显示接头包/光缆段工具，管井图只显示井/管段工具 */
body.route-view #toolbox .v-cable { display: none; }
body:not(.route-view) #toolbox .v-route { display: none; }
body.fiber-view .tb button[data-tool] { opacity: .45; pointer-events: none; } /* 地图工具快捷键按钮置灰 */
body.fiber-view #btn-tiles { opacity: .45; pointer-events: none; }           /* 本视图不使用底图 */
/* 纤芯图左侧接续工具面板：停靠视图区左侧（与地图视图的左侧录入工具条同位），
   竖向分组排布、窄屏可滚动；停靠而非浮层，图面内容不被遮挡，自动取景按剩余画布尺寸计算 */
#fiber-bar {
  flex: none; order: -3; width: 178px; align-self: stretch; z-index: 5; /* 最左停靠：与光缆/管井图左侧工具条位置统一（向导/合规自检面板排在其右） */
  display: flex; flex-direction: column; align-items: stretch; gap: 8px;
  padding: 8px 9px; overflow-y: auto; overflow-x: hidden;
  background: #2c3e50; border-right: 1px solid #1a252f;
  color: #eaf2f8; font-size: 12px; line-height: 1.35;
  scrollbar-width: thin; scrollbar-color: #56708c #2c3e50;
}
#fiber-bar::-webkit-scrollbar { width: 4px; }
#fiber-bar::-webkit-scrollbar-thumb { background: #56708c; border-radius: 2px; }
#fiber-bar[hidden] { display: none; }
#fiber-bar .fb-hd { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0 6px; } /* 标题/统计整块排版：容不下时统计整行下移，不拆字 */
#fiber-bar .fb-t { font-weight: bold; color: #1abc9c; white-space: nowrap; }
#fiber-bar .fb-stats { color: #9fd6c8; font-size: 11px; text-align: right; white-space: nowrap; margin-left: auto; }
#fiber-bar .fb-card {
  padding: 5px 7px; border: 1px solid #46607a; border-radius: 5px;
  background: rgba(0, 0, 0, .18); color: #dfe9f2; word-break: break-all;
}
#fiber-bar .fb-sec {
  display: flex; flex-direction: column; gap: 5px;
  padding-top: 8px; border-top: 1px solid rgba(86, 112, 140, .45);
}
#fiber-bar .fb-sec[hidden] { display: none; } /* 类选择器优先级高于 UA 的 [hidden] 规则，需显式回补 */
#fiber-bar .fb-side { display: flex; flex-direction: column; gap: 3px; } /* A/B 侧一组：侧色标 + 缆名下拉 + 该侧芯号范围输入 */
/* A/B 侧色标：底色 = 画布上该侧所选光缆的整条高亮色（--fiber-ab-a/b 由 fiber.js 依据画布常量发布，
   单一来源保证面板与画布线严格同色）；未选中该侧时保持中性灰蓝 */
#fiber-bar .fb-side .fb-tag {
  align-self: flex-start; padding: 0 5px; border: 1px solid #56708c; border-radius: 3px;
  background: #3b5872; color: #dfe9f2; line-height: 16px;
}
#fiber-bar .fb-side.ab-a .fb-tag { background: var(--fiber-ab-a, #e53935); border-color: var(--fiber-ab-a, #e53935); color: #fff; }
#fiber-bar .fb-side.ab-b .fb-tag { background: var(--fiber-ab-b, #8e24aa); border-color: var(--fiber-ab-b, #8e24aa); color: #fff; }
#fiber-bar .fb-side.ab-a select, #fiber-bar .fb-side.ab-a .fb-range, #fiber-bar .fb-side.ab-a .fb-pick-btn { border-color: var(--fiber-ab-a, #e53935); }
#fiber-bar .fb-side.ab-b select, #fiber-bar .fb-side.ab-b .fb-range, #fiber-bar .fb-side.ab-b .fb-pick-btn { border-color: var(--fiber-ab-b, #8e24aa); }
/* 可换行缆名下拉（wrapsel.js 增强）：原生 select 仅作数据源隐藏，界面 = 按钮（多行完整缆名）+ 可换行列表 */
#fiber-bar .fb-pick { position: relative; }
#fiber-bar .fb-pick select { display: none; }
#fiber-bar .fb-pick-btn {
  position: relative; display: block; width: 100%; min-height: 24px; padding: 3px 18px 3px 5px;
  border: 1px solid #56708c; border-radius: 4px; background: #3b5872; color: #eaf2f8;
  font-size: 12px; font-family: inherit; line-height: 1.4; text-align: left; cursor: pointer;
  white-space: normal; word-break: break-all; /* 长缆名自动换行、完整显示（不截断） */
}
#fiber-bar .fb-pick-btn:disabled { opacity: .5; cursor: not-allowed; }
#fiber-bar .fb-pick-btn:hover:not(:disabled) { background: #4a6b8a; }
#fiber-bar .fb-pick-btn:focus { outline: none; border-color: #1abc9c; }
#fiber-bar .fb-pick.empty .fb-pick-btn { color: #8fa6bb; }
#fiber-bar .fb-pick-car {
  position: absolute; right: 5px; top: 50%; width: 0; height: 0; margin-top: -2px;
  border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 5px solid #9db6cd;
}
#fiber-bar .fb-pick.open .fb-pick-car { transform: rotate(180deg); margin-top: -3px; }
#fiber-bar .fb-pick-list {
  position: absolute; z-index: 30; left: 0; right: 0; top: calc(100% + 2px);
  overflow-y: auto; border: 1px solid #56708c; border-radius: 5px;
  background: #24384a; box-shadow: 0 6px 18px rgba(0, 0, 0, .45);
  scrollbar-width: thin; scrollbar-color: #56708c #24384a;
}
#fiber-bar .fb-pick-list[hidden] { display: none; } /* 类选择器带定位样式，需显式回补 [hidden] */
#fiber-bar .fb-pick.up .fb-pick-list { top: auto; bottom: calc(100% + 2px); }
#fiber-bar .fb-pick-item {
  padding: 4px 6px; color: #dfe9f2; font-size: 12px; line-height: 1.4;
  white-space: normal; word-break: break-all; cursor: pointer; /* 列表逐项完整换行显示 */
}
#fiber-bar .fb-pick-item + .fb-pick-item { border-top: 1px solid rgba(86, 112, 140, .35); }
#fiber-bar .fb-pick-item.hi { background: #3b5872; }
#fiber-bar .fb-pick-item.on { box-shadow: inset 2px 0 0 #1abc9c; color: #9fe8d8; }
#fiber-bar .fb-pick-empty { padding: 4px 6px; color: #8fa6bb; font-size: 12px; }
#fiber-bar select {
  width: 100%; height: 24px; padding: 0 4px; border: 1px solid #56708c; border-radius: 4px;
  background: #3b5872; color: #eaf2f8; font-size: 12px; font-family: inherit;
}
#fiber-bar select:disabled { opacity: .5; }
#fiber-bar .fb-range {
  width: 100%; height: 24px; padding: 0 6px; border: 1px solid #56708c; border-radius: 4px;
  background: #3b5872; color: #eaf2f8; font-size: 12px; font-family: inherit; box-sizing: border-box;
}
#fiber-bar .fb-range::placeholder { color: #8fa6bb; }
#fiber-bar .fb-range:focus { outline: none; border-color: #1abc9c; }
#fiber-bar .fb-range:disabled { opacity: .5; }
#fiber-bar .fb-btn {
  width: 100%; height: 26px; padding: 0 6px; border: 1px solid #56708c; border-radius: 4px;
  background: #3b5872; color: #eaf2f8; cursor: pointer; font-size: 12px; white-space: nowrap;
}
#fiber-bar .fb-btn:hover:not(:disabled) { background: #4a6b8a; }
#fiber-bar .fb-btn:disabled { opacity: .45; cursor: not-allowed; }
#fiber-bar .fb-btn.warn { border-color: #e07a6f; color: #ffd9d4; }
#fiber-bar .fb-bun { /* 框内选中的接续段信息（点击合并线选中，删除在本面板完成） */
  padding: 4px 6px; border: 1px solid #7a6a4a; border-radius: 4px;
  background: rgba(230, 126, 34, .16); color: #ffd9a8; font-size: 11px; word-break: break-all;
}
#fiber-bar .fb-chk { display: flex; align-items: center; gap: 5px; color: #b8c8d8; font-size: 11px; cursor: pointer; line-height: 1.5; }
#fiber-bar .fb-chk input { accent-color: #1abc9c; cursor: pointer; }
#fiber-bar .fb-tip { margin-top: auto; padding-top: 6px; color: #8fa6bb; font-size: 11px; line-height: 1.6; }
#fiber-bar select:focus, #fiber-bar .fb-btn:focus { outline: none; border-color: #1abc9c; }

/* ── 使用向导（wizard.js）：停靠视图区左侧的按流程分步面板 ──
   按典型使用顺序引导：① 光缆图（导入或设计）→ ② 纤芯图（接续）→ ③ 管井图（井/管段/断面）→ ④ 路由图（定义接头包位置 + 敷设）→ 核对导出；
   order:-2 排在纤芯图接续面板（order:-3，最左）之后、合规自检面板（order:-1）之前，与画布同为 flex 子项（停靠不遮挡图面，画布尺寸随开关自动重设）；
   可经顶栏「向导」按钮或面板 × 随时开关，开关状态记忆在 localStorage（fiber-recorder-guide）；与合规自检面板互斥（二选一） */
#wizard {
  order: -2; flex: none; width: 252px; align-self: stretch; z-index: 5;
  display: flex; flex-direction: column;
  border-right: 1px solid #d5e0e8; background: #fbfcfe; color: #2c3e50;
  font-size: 12px; line-height: 1.6;
}
#wizard[hidden] { display: none; } /* 类选择器带 display:flex，需显式回补 [hidden] */
.wz-head { display: flex; align-items: center; gap: 6px; padding: 8px 10px; background: #eaf4fb; border-bottom: 1px solid #d5e5f0; }
.wz-head b { flex: 1; display: flex; align-items: center; color: #1c4e80; font-size: 13px; }
.wz-head svg.ic { width: 15px; height: 15px; margin-right: 5px; flex: none; }
.wz-dots { display: flex; gap: 3px; }
.wz-dots i {
  display: inline-flex; width: 18px; height: 18px; border-radius: 50%;
  background: #d5dbe3; color: #fff; font-style: normal; font-size: 11px;
  align-items: center; justify-content: center; cursor: pointer;
}
.wz-dots i:hover { background: #9dc3e8; }
.wz-dots i.done { background: #148f77; }
.wz-dots i.on { background: #1abc9c; box-shadow: 0 0 0 2px rgba(26, 188, 156, .28); }
.wz-x { border: none; background: none; padding: 0 2px; font-size: 16px; line-height: 1; cursor: pointer; color: #999; }
.wz-x:hover { color: #333; }
.wz-body { flex: 1; overflow-y: auto; padding: 10px 12px; }
.wz-stepno { display: block; font-size: 11px; color: #8a94a6; margin-bottom: 2px; }
.wz-title { margin: 0 0 7px; font-size: 13px; font-weight: bold; color: #2c3e50; }
.wz-tip { margin: 0 0 10px; color: #46607a; }
.wz-tip b { color: #16a085; }
.wz-stats {
  padding: 7px 9px; margin-bottom: 9px; border: 1px solid #e0e8ee; border-radius: 6px;
  background: #f2f6f9; line-height: 1.9;
}
.wz-stats b { color: #1c4e80; font-variant-numeric: tabular-nums; }
.wz-check {
  margin: 0 0 10px; padding: 5px 9px; border: 1px solid #f0e2c0; border-radius: 6px;
  background: #fdf8ec; color: #9a7b1f; font-size: 11px;
}
.wz-check.ok { border-color: #cde8e0; background: #eefaf6; color: #148f77; }
.wz-body > button {
  display: block; width: 100%; margin: 0 0 7px; padding: 8px 10px;
  border: 1px solid #1abc9c; border-radius: 6px; background: #fff; color: #16a085;
  font-size: 12px; font-family: inherit; cursor: pointer; text-align: left;
}
.wz-body > button:hover { background: #eafaf6; }
.wz-body > button.primary { background: #1abc9c; color: #fff; }
.wz-body > button.primary:hover { background: #16a085; }
.wz-body button svg.ic { width: 14px; height: 14px; margin-right: 6px; vertical-align: -2px; }
.wz-btns { display: flex; gap: 6px; margin-bottom: 7px; }
.wz-btns button {
  flex: 1; min-width: 0; padding: 7px 2px; border: 1px solid #1abc9c; border-radius: 6px;
  background: #fff; color: #16a085; font-size: 12px; font-family: inherit; cursor: pointer; text-align: center;
}
.wz-btns button:hover { background: #eafaf6; }
.wz-note { margin: 8px 0 0; color: #8a94a6; font-size: 11px; }
.wz-nav { display: flex; gap: 8px; padding: 9px 10px; border-top: 1px solid #dbe4ea; background: #f2f6f9; }
.wz-nav button {
  flex: 1; padding: 7px 0; border: 1px solid #c8d4de; border-radius: 6px;
  background: #fff; color: #46607a; font-size: 12px; font-family: inherit; cursor: pointer;
}
.wz-nav button:hover:not(:disabled) { background: #eafaf6; border-color: #1abc9c; color: #16a085; }
.wz-nav button[data-act="next"] { background: #1abc9c; border-color: #16a085; color: #fff; }
.wz-nav button[data-act="next"]:hover:not(:disabled) { background: #16a085; }
.wz-nav button:disabled { opacity: .4; cursor: default; }

/* 左侧录入工具条（单列网格，纯图标紧凑条：尽量少占视图区，名称/快捷键见悬停 title 提示） */
#toolbox {
  width: 48px; flex: none; display: grid; grid-template-columns: minmax(0, 1fr);
  gap: 3px; padding: 4px 3px; background: #2c3e50; border-right: 1px solid #1a252f;
  overflow-y: auto; align-content: start;
}
#toolbox button {
  display: flex; align-items: center; justify-content: center; min-width: 0;
  padding: 8px 0; border: 1px solid #56708c; border-radius: 6px;
  background: #3b5872; color: #eaf2f8; cursor: pointer;
  transition: background .12s ease, border-color .12s ease, transform .12s ease, box-shadow .12s ease;
}
#toolbox button i { display: flex; align-items: center; justify-content: center; line-height: 0; }
#toolbox svg.ic { width: 18px; height: 18px; }
#toolbox button span { display: none; } /* 纯图标：名称与快捷键在悬停提示中给出 */
#toolbox button:hover {
  background: #52799d; border-color: #9dc3e8; color: #fff;
  transform: translateY(-1px); box-shadow: 0 2px 6px rgba(0, 0, 0, .35);
}
#toolbox button:active { transform: none; box-shadow: none; }
#toolbox button.active { background: #1abc9c; border-color: #16a085; color: #fff; }
/* 工具栏分隔线：区分「全局动作（导入清单）」与「录入工具」两组 */
#toolbox .tb-sep { margin: 2px 3px; border-top: 1px solid rgba(154, 178, 202, .5); }
/* 井：图标为黑色实心样式（参考 BIIC-ico_300 (2).svg）；非激活时以白底圆片提高暗色工具条上的可读性 */
#toolbox button[data-tool="well"] i { color: #000; background: #fff; border-radius: 50%; }
#toolbox button[data-tool="well"].active i { background: none; }

/* ── 右侧属性面板 ── */
#props {
  width: var(--props-w); flex: none; overflow-y: auto;
  border-left: 1px solid #ddd; background: #fff; padding: 10px 12px;
}
/* 面板左缘拖拽条：拖动改宽，双击恢复默认；收起面板时隐藏 */
#props-resize {
  position: absolute; top: 0; bottom: 0; right: var(--props-w);
  width: 6px; z-index: 7; cursor: col-resize; touch-action: none;
}
#props-resize::after {
  content: ''; position: absolute; left: 2px; top: 0; bottom: 0; width: 2px;
  background: transparent; transition: background .12s ease;
}
#props-resize:hover::after, main.p-resizing #props-resize::after { background: #1abc9c; }
main.p-resizing { cursor: col-resize; user-select: none; }
main.phide #props-resize { display: none; }
#props-x {
  position: absolute; top: 50%; transform: translateY(-50%);
  right: calc(var(--props-w) + 6px); z-index: 6; padding: 8px 2px;
  border: none; background: none; color: #bbb; font-size: 14px; line-height: 1; cursor: pointer;
}
#props-x:hover { color: #333; }
main.phide #props { display: none; }
main.phide #props-x { right: 0; }
#props h3 {
  font-size: 14px; margin: 10px 0 6px; padding-bottom: 4px;
  border-bottom: 1px solid #eee; color: #2c3e50;
  display: flex; align-items: center; justify-content: space-between; gap: 6px;
}
#props h3:first-child { margin-top: 0; }
#props h3 .sub { font-size: 11px; font-weight: 400; color: #8a94a6; }
.pr { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }
.pr > span { flex: 0 0 88px; text-align: right; color: #666; font-size: 12px; white-space: nowrap; } /* 88px 容纳最长标签「起点盘留(米)」7 全角字符，不换行 */
.pr input, .pr select, .pr textarea {
  flex: 1; min-width: 0; padding: 4px 6px;
  border: 1px solid #ccc; border-radius: 4px; font-size: 12px; font-family: inherit;
}
.pr input:focus, .pr select:focus, .pr textarea:focus { outline: none; border-color: #1abc9c; }
.pr input[readonly] { background: #f5f5f5; color: #888; }
.pr .half { flex: 1; min-width: 0; display: flex; align-items: center; gap: 4px; }
.pr .half input { width: 100%; }
/* 分光器端口标签（属性面板：光缆段起点/终点、分光器关联光缆列表） */
.ptag { font-size: 11px; font-weight: 600; color: #6a1b9a; background: #f3e8fb; border-radius: 3px; padding: 1px 4px; white-space: nowrap; }
.ptag.over { color: #b03a2e; background: #fdecea; }
.tip { font-size: 12px; color: #999; }
.tip p { margin: 4px 0; }
.stat p { margin: 4px 0; color: #555; }
.stat b { color: #16a085; }

/* 属性面板按钮 */
.pbtns { display: flex; gap: 6px; margin: 8px 0; flex-wrap: wrap; }
.pbtns button {
  flex: 1; min-width: 80px; padding: 6px; border: 1px solid #1abc9c; background: #fff;
  color: #16a085; border-radius: 4px; cursor: pointer; font-size: 12px; white-space: nowrap;
}
.pbtns button:hover { background: #eafaf6; }
.pbtns button.warn { border-color: #e74c3c; color: #c0392b; }
.pbtns button.warn:hover { background: #fbeae8; }
.pbtns svg.ic { width: 13px; height: 13px; margin-right: 4px; vertical-align: -2px; }

/* 关联列表（接头包→光缆段、敷设行） */
.rel-list { margin: 4px 0 8px; border: 1px solid #e0e6ec; border-radius: 6px; overflow: hidden; }
.rel-item {
  display: flex; align-items: center; gap: 6px; padding: 5px 8px; font-size: 12px;
  border-bottom: 1px solid #f0f3f6; cursor: pointer; background: #fff;
}
.rel-item:last-child { border-bottom: none; }
.rel-item:hover { background: #f0faf8; }
.rel-item.pri { background: #fdf3e0; box-shadow: inset 3px 0 0 #f39c12; }  /* 多选列表中的主选中行 */
.rel-item.pri:hover { background: #fbeed3; }
.rel-item .nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rel-item .tag { flex: none; font-size: 11px; color: #8a94a6; }
.rel-item button {
  flex: none; width: 20px; height: 20px; padding: 0; border: none; background: none;
  color: #b03a2e; cursor: pointer; border-radius: 4px; font-size: 13px; line-height: 1;
}
.rel-item button:hover { background: #fbeae8; }
.rel-empty { padding: 6px 8px; font-size: 12px; color: #a6b0ba; background: #fbfcfd; }

/* 管孔截面网格 */
.hole-grid { display: grid; gap: 5px; margin: 6px 0; padding: 8px; border: 1px solid #e0e6ec; border-radius: 6px; background: #fbfcfd; width: max-content; max-width: 100%; }
.hole {
  width: 26px; height: 26px; border-radius: 50%; border: 2px solid #b8c2ce; background: #fff;
  font-size: 10px; color: #666; cursor: pointer; line-height: 1;
  display: flex; align-items: center; justify-content: center;
}
.hole:hover:not(.used):not(.self) { border-color: #1abc9c; color: #16a085; background: #eafaf6; }
.hole.used { border-color: #e74c3c; background: #fbeae8; color: #c0392b; cursor: not-allowed; }
.hole.self { border-color: #2980b9; background: #eaf3fb; color: #2980b9; cursor: default; }
.hole-cap { font-size: 11px; color: #8a94a6; margin: 2px 0 6px; }

/* 管段断面结构：栅格管/硅芯管阵列 */
.sect-grid { display: grid; gap: 6px; margin: 6px 0; padding: 8px; border: 1px solid #e0e6ec; border-radius: 6px; background: #e9edf1; width: max-content; max-width: 100%; overflow-x: auto; align-items: center; justify-items: center; }
/* 包外框统一尺寸（栅格 2×2 / 3×3 仅内部分格不同，硅芯管外接圆同径） */
.pack { display: grid; gap: 2px; padding: 3px; background: #fff; border: 1.5px solid #7f8a99; border-radius: 4px; width: 79px; height: 79px; box-sizing: border-box; place-content: stretch; justify-items: stretch; }
.pack.round { border-radius: 50%; padding: 3px; }
.pack.empty { border: 1.5px dashed #c4ccd6; background: transparent; width: 79px; height: 79px; box-sizing: border-box; }
.pack .hole { width: 100%; height: 100%; border-width: 1.5px; border-radius: 3px; font-size: 9px; }
.pack.round .hole { border-radius: 50%; }
.pack.g4 .hole, .pack.si .hole { font-size: 13px; }
.hole.view { cursor: default; }
.hole.view:hover { border-color: #b8c2ce; color: #666; background: #fff; }
.hole.na { border-color: transparent; background: transparent; cursor: default; }

/* 「管孔占用」图整体缩至原尺寸的 2/3（包框 79→52px），避免大断面撑满右侧面板；敷设点选/断面编辑器预览不受影响 */
#duct-holes .sect-grid { gap: 4px; margin: 4px 0; padding: 6px; border-radius: 5px; }
#duct-holes .pack, #duct-holes .pack.empty { width: 52px; height: 52px; border-width: 1.5px; border-radius: 4px; }
#duct-holes .pack { gap: 1.5px; padding: 2px; }
#duct-holes .pack.round { border-radius: 50%; }
#duct-holes .pack .hole { border-width: 1px; border-radius: 2px; font-size: 8px; }
#duct-holes .pack.round .hole { border-radius: 50%; }
#duct-holes .pack.g4 .hole, #duct-holes .pack.si .hole { font-size: 13px; }

/* 断面编辑器：阵列控制行 + 管材点选格 */
.sect-ctl { display: flex; flex-wrap: wrap; gap: 4px 12px; align-items: center; margin: 6px 0; font-size: 12px; color: #55627a; }
.sect-ctl input[type=number] { width: 46px; text-align: center; }
.sect-ctl select { max-width: 130px; }
.sect-edit { display: grid; gap: 6px; margin: 6px 0; width: max-content; padding: 8px; border: 1px solid #e0e6ec; border-radius: 6px; background: #fbfcfd; }
.pck { display: grid; gap: 2px; place-content: center; width: 34px; height: 34px; border: 1.5px solid #b8c2ce; border-radius: 5px; background: #fff; cursor: pointer; }
.pck:hover { border-color: #1abc9c; background: #eafaf6; }
.pck i { width: 8px; height: 8px; border-radius: 1.5px; background: #8a94a6; display: block; }
.pck.si { border-radius: 50%; }
.pck.si i { border-radius: 50%; }
.pck.empty { border-style: dashed; background: #f4f6f8; }
.pck.empty i { background: #d5dbe2; }

/* ── 底部状态栏 ── */
.sb {
  flex: none; display: flex; align-items: center; gap: 12px;
  padding: 4px 10px; background: #f4f6f8; border-top: 1px solid #dde3e8;
  font-size: 12px; color: #667; white-space: nowrap;
}
#st-hint { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.zg { display: flex; align-items: center; gap: 4px; }
.zg button {
  width: 24px; height: 22px; border: 1px solid #ccd4db; border-radius: 4px;
  background: #fff; color: #456; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.zg button:hover { border-color: #1abc9c; color: #16a085; }
.zg svg.ic { width: 13px; height: 13px; }
.zg-sep { width: 1px; height: 14px; background: #dde3e8; margin: 0 2px; } /* 缩放控件与「缩放到全部对象」之间的分隔线 */
#st-zoom { min-width: 40px; text-align: center; }
#st-coords { min-width: 210px; text-align: right; font-variant-numeric: tabular-nums; }
#st-map { color: #9fd6c8; white-space: nowrap; }   /* 当前底图·底图坐标系（WGS-84 / GCJ-02） */

/* ── 通用模态对话框 ── */
.dlg-overlay {
  position: fixed; inset: 0; z-index: 100; background: rgba(20, 30, 40, .45);
  display: flex; align-items: center; justify-content: center;
}
.dlg {
  width: 420px; max-width: 92vw; max-height: 84vh; overflow: auto;
  background: #fff; border-radius: 10px; box-shadow: 0 12px 40px rgba(0,0,0,.3);
  padding: 16px 18px;
}
.dlg h2 { font-size: 15px; margin-bottom: 10px; color: #2c3e50; }
.dlg .row { display: flex; gap: 8px; margin-top: 12px; justify-content: flex-end; }
.dlg input[type="text"] {
  width: 100%; padding: 6px 8px; border: 1px solid #ccc; border-radius: 5px; font-size: 13px;
}
.dlg input[type="text"]:focus { outline: none; border-color: #1abc9c; }
.btn {
  padding: 6px 14px; border: 1px solid #1abc9c; border-radius: 5px; background: #1abc9c;
  color: #fff; cursor: pointer; font-size: 13px;
}
.btn:hover { background: #17a589; }
.btn.ghost { background: #fff; color: #16a085; }
.btn.ghost:hover { background: #eafaf6; }
.btn.warn { border-color: #e74c3c; background: #fff; color: #c0392b; }
.btn.warn:hover { background: #fbeae8; }
/* 项目列表 */
.prj-item {
  display: flex; align-items: center; gap: 8px; padding: 8px 10px; margin-bottom: 4px;
  border: 1px solid #e0e6ec; border-radius: 6px; cursor: pointer;
}
.prj-item:hover { border-color: #1abc9c; background: #f0faf8; }
.prj-item .nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.prj-item .tm { flex: none; font-size: 11px; color: #9aa5b1; }
.prj-item button {
  flex: none; width: 24px; height: 24px; border: none; background: none; color: #b03a2e;
  cursor: pointer; border-radius: 4px; font-size: 14px;
}
.prj-item button:hover { background: #fbeae8; }
.dlg .imp { margin-top: 10px; padding-top: 10px; border-top: 1px dashed #e0e6ec; text-align: center; }
.dlg .imp label { color: #2f6fb2; cursor: pointer; font-size: 13px; }
.dlg .imp label:hover { text-decoration: underline; }

/* ── 概览：保存位置提示 ── */
.save-path { margin: 6px 0 0; font-size: 11px; color: #8a94a6; word-break: break-all; }

/* ── 底图源下拉菜单 ── */
.map-menu {
  position: fixed; z-index: 120; min-width: 210px; max-height: 72vh; overflow: auto;
  background: #fff; border-radius: 8px; box-shadow: 0 8px 30px rgba(0,0,0,.28);
  padding: 6px; font-size: 12px;
}
.map-menu .mm-cap { padding: 5px 8px 3px; color: #8a94a6; font-size: 11px; }
.map-menu .mm-item {
  display: flex; align-items: center; gap: 6px; padding: 6px 8px;
  border-radius: 5px; cursor: pointer; color: #2c3e50;
}
.map-menu .mm-item:hover { background: #f0faf8; }
.map-menu .mm-item.on { background: #e6f7f2; color: #16a085; font-weight: 600; }
.map-menu .mm-item.on .nm::before { content: '✓ '; }
.map-menu .mm-item .nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.map-menu .mm-tag { flex: none; font-size: 10px; color: #9aa5b1; border: 1px solid #e0e6ec; border-radius: 3px; padding: 0 3px; }
.map-menu .mm-item.on .mm-tag { color: #16a085; border-color: #b5e8db; }
.map-menu .mm-key { flex: none; border: 0; background: none; padding: 0 2px; font-size: 12px; line-height: 1.4; cursor: pointer; border-radius: 3px; }
.map-menu .mm-key:hover { background: #d5efe8; }
.map-menu .mm-sep { height: 1px; background: #eef1f4; margin: 4px 2px; }
.map-menu .mm-cfg .nm { color: #2f6fb2; }
.export-menu .mm-item svg.ic { width: 14px; height: 14px; flex: none; color: #16a085; }

/* ── 连接点拖到空白：新建设备类型菜单（方案 A，见 dropmenu.js）── */
#dropmenu-wrap { position: absolute; inset: 0; z-index: 40; }
.dropmenu {
  position: absolute; min-width: 176px; padding: 6px;
  background: #fff; border-radius: 8px; box-shadow: 0 8px 30px rgba(0, 0, 0, .28);
  font-size: 12px; color: #2c3e50;
}
.dropmenu .dm-hd { padding: 4px 8px 6px; color: #8a94a6; font-size: 11px; }
.dropmenu .dm-item {
  display: flex; align-items: center; gap: 8px; padding: 7px 8px;
  border-radius: 5px; cursor: pointer;
}
.dropmenu .dm-item:hover, .dropmenu .dm-item.on { background: #e6f7f2; color: #16a085; font-weight: 600; }
.dropmenu .dm-item svg.ic { width: 15px; height: 15px; flex: none; }
.dropmenu .dm-nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dropmenu .dm-key {
  flex: none; font-size: 11px; color: #9aa5b1; border: 1px solid #e0e6ec;
  border-radius: 3px; padding: 0 4px;
}
.dropmenu .dm-item:hover .dm-key, .dropmenu .dm-item.on .dm-key { color: #16a085; border-color: #b5e8db; }
.dropmenu .dm-ft {
  padding: 5px 8px 2px; margin-top: 4px; color: #9aa5b1; font-size: 11px;
  border-top: 1px dashed #eef1f4;
}

/* ── 自定义地图源对话框 ── */
.ms-sec { margin: 10px 0 6px; font-size: 12px; color: #667; font-weight: 600; }
.ms-sec a { color: #2f6fb2; text-decoration: none; }
.ms-sec a:hover { text-decoration: underline; }
.ms-row { margin-bottom: 6px; }
.ms-src {
  display: flex; align-items: center; gap: 6px; padding: 5px 8px; margin-bottom: 4px;
  border: 1px solid #e0e6ec; border-radius: 6px; font-size: 12px;
}
.ms-src .nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ms-src .tg { flex: none; font-size: 10px; color: #9aa5b1; }
.ms-src button {
  flex: none; border: none; background: none; cursor: pointer; border-radius: 4px;
  color: #2f6fb2; font-size: 12px; padding: 2px 5px;
}
.ms-src button:hover { background: #eaf3fb; }
.ms-src button.del { color: #b03a2e; }
.ms-src button.del:hover { background: #fbeae8; }
.ms-empty { font-size: 12px; color: #a6b0ba; padding: 4px 2px; }
.ms-form { border-top: 1px dashed #e0e6ec; margin-top: 8px; padding-top: 8px; display: grid; gap: 6px; }
.ms-form .ms-grid { display: flex; gap: 6px; }
.ms-form .ms-grid > * { flex: 1; min-width: 0; }
.ms-form select {
  padding: 6px 4px; border: 1px solid #ccc; border-radius: 5px; font-size: 12px; background: #fff;
}
.ms-form .ms-note { font-size: 11px; color: #9aa5b1; }
.ms-form .btn { justify-self: end; }

/* ── nVisual Studio 获取对话框 ── */
.nv-form { display: grid; gap: 8px; margin-top: 4px; }
.nv-form label { font-size: 12px; color: #2c3e50; }
.nv-form label span { display: block; margin-bottom: 3px; color: #667; }
.nv-scope { display: flex; align-items: center; gap: 14px; font-size: 12px; color: #2c3e50; }
.nv-scope > span { color: #667; }
.nv-scope .chk { display: flex; align-items: center; gap: 5px; cursor: pointer; }
.nv-scope .chk input { accent-color: #1abc9c; }
.nv-max { display: flex; align-items: center; gap: 4px; }
.nv-max select { padding: 3px 6px; border: 1px solid #ccc; border-radius: 4px; font-size: 12px; }
.nv-note { margin-top: 10px; font-size: 11px; color: #9aa5b1; line-height: 1.6; }
.nv-err { margin-top: 8px; font-size: 12px; color: #c0392b; }
.dlg.nv-fetch { width: 760px; }
.nv-msg { font-size: 13px; color: #2c3e50; line-height: 1.7; word-break: break-all; }
/* 逐条获取进度：进度条 + 每条光缆状态列表（自动滚动到当前条） */
.nv-list { max-height: 260px; overflow: auto; margin-top: 10px; border: 1px solid #e0e6ec; border-radius: 6px; }
.nv-list table { width: 100%; border-collapse: collapse; font-size: 12px; }
.nv-list th { position: sticky; top: 0; z-index: 1; background: #f5f8fa; color: #5a6b7b; font-weight: 600; text-align: left; padding: 5px 8px; border-bottom: 1px solid #e0e6ec; }
.nv-list td { padding: 4px 8px; border-bottom: 1px solid #f0f4f7; color: #2c3e50; vertical-align: middle; }
.nv-list td.nv-pair { word-break: break-all; }
.nv-list td.nv-idx { color: #9aa5b1; white-space: nowrap; }
.nv-list tr:nth-child(even) td { background: #fafcfd; }
.nv-st.wait { color: #9aa5b1; }
.nv-st.run { color: #2f6fb2; font-weight: 600; }
.nv-st.ok { color: #16a085; }
.nv-st.warn { color: #b9770e; }
.nv-st.bad { color: #c0392b; }
.nv-done { margin-top: 10px; padding: 8px 10px; background: #f2f8f7; border: 1px solid #d7ece8; border-radius: 6px; font-size: 12px; color: #2c3e50; line-height: 1.7; }
.nv-done b { color: #16a085; }
.nv-done.none { background: #fdf6e3; border-color: #f0e0b0; }
.nv-end { margin-top: 6px; font-size: 12px; color: #b9770e; }
.nv-fetch #nvf-sum { margin-top: 0; }

/* ── DXF 导出对话框 ── */
.dxf-form { display: grid; gap: 8px; margin-top: 4px; }
.dxf-form .dxf-row { display: flex; align-items: center; gap: 10px; }
.dxf-form .dxf-row > span { flex: 1; display: block; margin-bottom: 0; color: #667; }
.dxf-form .dxf-row input { flex: none; width: 120px; }
.dxf-form select { flex: none; width: 230px; padding: 5px 6px; border: 1px solid #ccc; border-radius: 5px; font-size: 12px; background: #fff; }
.dxf-form select:focus { outline: none; border-color: #1abc9c; }
.dxf-form label { flex: 1; font-size: 12px; color: #2c3e50; }
.dxf-form label span { display: block; margin-bottom: 3px; color: #667; }
.dxf-form .chk { display: flex; align-items: center; gap: 6px; cursor: pointer; }
.dxf-form .chk input { accent-color: #1abc9c; }
.dxf-note { margin-top: 10px; font-size: 11px; color: #9aa5b1; line-height: 1.6; }

/* ── PNG 完整导出对话框与进度 ── */
.png-form { display: grid; gap: 8px; margin-top: 4px; }
.png-form label { font-size: 12px; color: #2c3e50; }
.png-form label span { display: block; margin-bottom: 3px; color: #667; }
.png-form select {
  width: 100%; padding: 6px 8px; border: 1px solid #ccc; border-radius: 5px;
  font-size: 13px; background: #fff;
}
.png-form select:focus { outline: none; border-color: #1abc9c; }
.png-form .chk { display: flex; align-items: center; gap: 6px; cursor: pointer; }
.png-form .chk input { accent-color: #1abc9c; }
.png-note { margin-top: 10px; font-size: 11px; color: #667; line-height: 1.7; word-break: break-all; }
.png-note .warn-t { color: #c0392b; }
.dlg .btn:disabled { opacity: .5; cursor: not-allowed; }
.png-prog { margin-top: 6px; }
.png-bar { height: 8px; border-radius: 4px; background: #eef1f4; overflow: hidden; }
.png-bar i { display: block; height: 100%; width: 0; background: #1abc9c; transition: width .15s; }
.png-prog-t { margin-top: 8px; font-size: 12px; color: #667; }

/* ── 纤芯接续模态 ── */
.sp-modal {
  width: 980px; max-width: 96vw; height: 88vh; display: flex; flex-direction: column;
  background: #fff; border-radius: 10px; box-shadow: 0 12px 40px rgba(0,0,0,.35);
  overflow: hidden;
}
.sp-head {
  flex: none; display: flex; align-items: center; gap: 10px;
  padding: 10px 16px; background: #2c3e50; color: #fff;
}
.sp-head h2 { font-size: 15px; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sp-head .x {
  width: 28px; height: 28px; border: none; background: none; color: #b8c8d8; font-size: 18px;
  cursor: pointer; border-radius: 5px;
}
.sp-head .x:hover { background: #4a6b8a; color: #fff; }
.sp-bar {
  flex: none; display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 8px 16px; border-bottom: 1px solid #e5eaef; background: #f7fafc; font-size: 12px;
}
.sp-bar .sp-grp { display: flex; align-items: center; gap: 8px; }
.sp-bar select {
  padding: 4px 6px; border: 1px solid #ccc; border-radius: 4px; font-size: 12px; max-width: 220px;
}
.sp-bar select:focus { outline: none; border-color: #1abc9c; }
.sp-bar input.sp-range {
  width: 72px; padding: 4px 6px; border: 1px solid #ccc; border-radius: 4px; font-size: 12px;
}
.sp-bar input.sp-range:focus { outline: none; border-color: #1abc9c; }
.sp-bar .swap {
  width: 26px; height: 26px; border: 1px solid #ccd4db; border-radius: 4px; background: #fff;
  cursor: pointer; color: #456; font-size: 14px; line-height: 1;
}
.sp-bar .swap:hover { border-color: #1abc9c; color: #16a085; }
.sp-bar button.act {
  padding: 5px 10px; border: 1px solid #1abc9c; border-radius: 4px; background: #fff;
  color: #16a085; cursor: pointer; font-size: 12px;
}
.sp-bar button.act:hover { background: #eafaf6; }
.sp-bar button.act:disabled { opacity: .45; cursor: not-allowed; }
.sp-bar button.act:disabled:hover { background: #fff; }
.sp-bar .sp-stats { margin-left: auto; color: #8a94a6; }
.sp-body { flex: 1; min-height: 120px; overflow: auto; position: relative; background: #fdfefe; }
.sp-cols { position: relative; margin: 0 auto; }
.sp-side { position: absolute; top: 0; bottom: 0; width: 170px; z-index: 2; }
.sp-side.l { left: 0; }
.sp-side.r { right: 0; }
.sp-cap {
  position: sticky; top: 0; z-index: 3; height: 30px; display: flex; align-items: center;
  background: #eef3f7; color: #2c3e50; font-weight: 600; font-size: 12px; padding: 0 10px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sp-side.r .sp-cap { justify-content: flex-end; }
.sp-links { position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%; }
.sp-links path { cursor: pointer; pointer-events: stroke; }
.sp-fiber {
  position: absolute; height: 26px; display: flex; align-items: center; gap: 6px;
  padding: 0 8px; cursor: pointer; user-select: none; font-size: 12px; color: #34495e;
}
.sp-side.l .sp-fiber { right: 0; flex-direction: row-reverse; }
.sp-side.r .sp-fiber { left: 0; }
.sp-fiber i {
  flex: none; width: 14px; height: 14px; border-radius: 50%; border: 1px solid rgba(0,0,0,.25);
  display: inline-block;
}
.sp-fiber b { font-weight: 600; min-width: 22px; text-align: center; font-variant-numeric: tabular-nums; }
.sp-fiber:hover { color: #16a085; }
.sp-fiber:hover i { transform: scale(1.25); box-shadow: 0 0 0 3px rgba(26,188,156,.25); }
.sp-fiber.used { opacity: .38; }
/* 分光器进缆芯「分光中」：不置灰（可继续分光到其他出缆），紫色角标 = 已分光路数 */
.sp-fiber.fan { opacity: 1; }
.sp-fiber.fan i { box-shadow: 0 0 0 2px rgba(142,36,170,.45); }
.sp-fiber.fan u {
  flex: none; min-width: 13px; height: 13px; line-height: 13px; border-radius: 7px;
  background: #8e24aa; color: #fff; font-size: 9px; font-style: normal; text-decoration: none;
  text-align: center; padding: 0 3px;
}
.sp-side.l .sp-fiber.fan u { order: -1; }
.sp-fiber.sel { color: #16a085; }
.sp-fiber.sel i { transform: scale(1.3); box-shadow: 0 0 0 4px rgba(26,188,156,.35); animation: sp-pulse 1s infinite alternate; }
/* 已接续纤芯被选中：橙色警示（与对端同选即断开） */
.sp-fiber.sel.used { color: #d35400; opacity: .85; }
.sp-fiber.sel.used i { box-shadow: 0 0 0 4px rgba(230,126,34,.42); animation: sp-pulse-warn 1s infinite alternate; }
@keyframes sp-pulse-warn { from { box-shadow: 0 0 0 3px rgba(230,126,34,.32); } to { box-shadow: 0 0 0 6px rgba(230,126,34,.58); } }
@keyframes sp-pulse { from { box-shadow: 0 0 0 3px rgba(26,188,156,.25); } to { box-shadow: 0 0 0 6px rgba(26,188,156,.45); } }
/* 接续明细表 */
.sp-table { flex: none; max-height: 30%; overflow: auto; border-top: 1px solid #e5eaef; }
.sp-table table { width: 100%; border-collapse: collapse; font-size: 12px; }
.sp-table th, .sp-table td { padding: 4px 8px; border-bottom: 1px solid #f0f3f6; text-align: left; white-space: nowrap; }
.sp-table th { position: sticky; top: 0; background: #f4f7fa; color: #667; font-weight: 600; z-index: 1; }
.sp-table td input {
  width: 70px; padding: 2px 5px; border: 1px solid #ddd; border-radius: 3px; font-size: 12px;
}
.sp-table td input:focus { outline: none; border-color: #1abc9c; }
.sp-table td.remark input { width: 140px; }
.sp-table .cur { background: #f0faf8; }
.sp-table .del {
  width: 22px; height: 22px; border: none; background: none; color: #b03a2e; cursor: pointer;
  border-radius: 4px; font-size: 13px;
}
.sp-table .del:hover { background: #fbeae8; }
.sp-foot {
  flex: none; display: flex; align-items: center; gap: 8px; justify-content: flex-end;
  padding: 8px 16px; border-top: 1px solid #e5eaef; background: #f7fafc;
}
/* 跟随光标的操作提示浮层（移入纤芯区显示约 20 秒后自动隐藏） */
.sp-hint {
  position: fixed; z-index: 40; display: none; pointer-events: none;
  max-width: min(560px, 90vw); padding: 8px 12px; border-radius: 6px;
  background: rgba(44, 62, 80, .95); color: #eaf2f8; font-size: 12px; line-height: 1.85;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .28);
}
.sp-hint b { color: #7fe0c8; font-weight: 600; }
.sp-foot .sp-hintchk {
  margin-right: auto; display: flex; align-items: center; gap: 6px;
  color: #667; font-size: 12px; cursor: pointer; user-select: none;
}
.sp-foot .sp-hintchk input { margin: 0; accent-color: #1abc9c; }

/* ── 纤芯分光模态（分光器 1 进 N 出：IN / OUT 双侧；OUT 可多缆按缆分组）── */
.sp-modal.sp-spl { width: 1240px; }
.sp-bar .sp-tag {
  flex: none; min-width: 30px; padding: 1px 6px; border-radius: 4px; text-align: center;
  font-size: 11px; font-weight: 700; color: #fff; letter-spacing: .5px;
}
.sp-bar .sp-tag.in { background: #e53935; }
.sp-bar .sp-tag.out { background: #8e24aa; }
.sp-bar .sp-arrow { color: #8a94a6; font-weight: 700; }
.sp-bar button.act.danger { border-color: #d9534f; color: #c0392b; }
.sp-bar button.act.danger:hover { background: #fbeae8; }
.sp-cols.spl { display: flex; align-items: stretch; margin: 0; min-height: 100%; }
.spl-in { flex: none; width: 210px; display: flex; flex-direction: column; border-right: 1px solid #e5eaef; background: #fbfcfd; position: relative; z-index: 2; }
/* IN 纤芯列表在剩余空间内上下居中（IN 缆芯数少时不再贴顶；比可视区高时仍从顶部正常滚动） */
.spl-in-list { flex: 1 0 auto; display: flex; flex-direction: column; justify-content: center; }
.spl-in-list .sp-fiber { flex: none; }
.spl-in-list .spl-empty { flex: 1; display: flex; align-items: center; justify-content: center; text-align: center; }
.spl-in-cap {
  flex: none;
  position: sticky; top: 0; z-index: 3; height: 30px; display: flex; align-items: center; gap: 6px;
  padding: 0 10px; background: #fdeceb; color: #c0392b; font-weight: 600; font-size: 12px;
  border-bottom: 1px solid #f3d6d4; overflow: hidden; white-space: nowrap;
}
.spl-in-cap .gname { overflow: hidden; text-overflow: ellipsis; }
.spl-in-cap .gmeta { color: #d98a86; font-weight: 400; font-size: 11px; }
.spl-in-cap .sp-tag { margin-left: auto; }
/* IN 进芯列表（普通流式排列，覆盖 .sp-fiber 的绝对定位） */
.sp-fiber.spl-ifib { position: relative; top: auto; left: auto; right: auto; flex-direction: row; }
/* IN 与 OUT 之间留出「走线通道」：分光线从 IN 行右缘横跨空档进入 OUT 缆卡，避免连线被两侧紧贴挤得看不见 */
.spl-out { flex: 1; min-width: 0; padding: 8px 10px 24px 246px; position: relative; z-index: 2; }
.spl-grp { border: 1px solid #e3e9ef; border-radius: 7px; background: #fff; margin-bottom: 8px; }
.spl-gcap {
  position: sticky; top: 0; z-index: 3; display: flex; align-items: center; gap: 8px;
  padding: 5px 10px; background: #f3eef8; border-bottom: 1px solid #e6daf0;
  border-radius: 6px 6px 0 0; font-size: 12px; color: #6a1b9a;
}
.spl-gcap .gname { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.spl-gcap .gtag {
  flex: none; padding: 0 6px; border-radius: 3px; border: 1px solid #b58fd0; color: #7b1fa2;
  font-size: 10px; font-weight: 700; line-height: 15px;
}
.spl-gcap .gmeta { flex: none; color: #9a7fb0; font-size: 11px; }
.spl-gcap .gfree { margin-left: auto; color: #7f8c99; font-size: 11px; white-space: nowrap; }
.spl-ofibs { display: flex; flex-wrap: wrap; gap: 2px 4px; padding: 6px 8px 8px; }
.sp-ofib {
  position: relative; display: flex; align-items: center; gap: 5px; width: 78px; height: 26px;
  padding: 0 6px; border-radius: 5px; cursor: pointer; user-select: none; font-size: 12px; color: #34495e;
  box-sizing: border-box;
}
.sp-ofib i { flex: none; width: 14px; height: 14px; border-radius: 50%; border: 1px solid rgba(0,0,0,.25); }
.sp-ofib b { font-weight: 600; font-variant-numeric: tabular-nums; }
.sp-ofib em { margin-left: auto; font-style: normal; font-size: 10px; color: #8e24aa; }
.sp-ofib:hover { background: #f2f7fb; color: #16a085; }
.sp-ofib:hover i { transform: scale(1.25); box-shadow: 0 0 0 3px rgba(26,188,156,.25); }
.sp-ofib.used i { box-shadow: 0 0 0 2px rgba(142,36,170,.42); }
.sp-ofib.sel { background: #e8f8f4; color: #16a085; box-shadow: inset 0 0 0 1px rgba(26,188,156,.45); }
.sp-ofib.sel i { transform: scale(1.2); box-shadow: 0 0 0 4px rgba(26,188,156,.35); }
.spl-empty { padding: 14px 12px; color: #8a94a6; font-size: 12px; line-height: 1.8; }
.sp-links path.hot { stroke-width: 1.6; opacity: 1; }

/* ── ODF 成端图模态 ── */
.odf-modal { width: 1100px; }
.od-body { flex: 1; min-height: 140px; display: flex; overflow: hidden; background: #fdfefe; }
.od-fibers { flex: none; width: 240px; overflow: auto; border-right: 1px solid #e5eaef; }
.od-fcap {
  position: sticky; top: 0; z-index: 2; padding: 6px 10px; background: #eef3f7; color: #2c3e50;
  font-weight: 600; font-size: 12px; border-bottom: 1px solid #e0e6ec;
}
.od-fib {
  display: flex; align-items: center; gap: 7px; height: 24px; padding: 0 10px;
  cursor: pointer; user-select: none; font-size: 12px; color: #34495e;
}
.od-fib i { flex: none; width: 13px; height: 13px; border-radius: 50%; border: 1px solid rgba(0,0,0,.25); }
.od-fib b { font-weight: 600; min-width: 24px; text-align: center; font-variant-numeric: tabular-nums; }
.od-fib span { margin-left: auto; color: #8a94a6; font-size: 11px; }
.od-fib:hover { background: #f2f7fb; }
.od-fib.free:hover { color: #16a085; }
.od-fib.term { color: #5b21b6; }
.od-fib.term span { color: #7c3aed; }
.od-fib.jump { color: #11694f; }
.od-fib.jump span { color: #0e8a6d; }
.od-fib.sel { background: #eafaf6; color: #16a085; }
.od-fib.sel i { box-shadow: 0 0 0 3px rgba(26,188,156,.3); }
.od-fib.pick { background: #fdf3e0; color: #d35400; box-shadow: inset 3px 0 0 #e67e22; }
.od-fib.peer { background: #fdf6ec; box-shadow: inset 3px 0 0 #f0a03c; }
.od-grid { flex: 1; overflow: auto; padding: 8px 10px; }
.od-tray { display: flex; align-items: stretch; gap: 6px; margin-bottom: 6px; }
.od-tray-lb {
  flex: none; width: 52px; display: flex; align-items: center; justify-content: center;
  background: #eef3f7; border: 1px solid #dde5ec; border-radius: 5px;
  font-size: 11px; color: #456; font-weight: 600;
}
.od-tray-ports { flex: 1; display: flex; flex-wrap: wrap; gap: 4px 5px; }
/* 端口 = 托盘上的圆形孔（适配器）：圆孔 + 孔下端口号；占用按纤芯色谱填色，状态环分色 */
.od-port {
  flex: none; width: 32px; display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 1px 0 0; border: none; background: none; cursor: pointer; user-select: none; border-radius: 5px;
}
.od-port i {
  width: 22px; height: 22px; border-radius: 50%; box-sizing: border-box;
  border: 2px solid #aab4c0; background: #fff; transition: transform .08s;
}
.od-port b { font-size: 9px; font-weight: 600; color: #98a4b3; font-variant-numeric: tabular-nums; line-height: 1.2; }
.od-port.free i { border-style: dashed; }
.od-port.free:hover i { border-color: #1abc9c; border-style: solid; box-shadow: 0 0 0 3px rgba(26,188,156,.22); transform: scale(1.12); }
.od-port.free:hover b { color: #16a085; }
.od-port.used.term i { border-color: #7c3aed; box-shadow: 0 0 0 2px rgba(124,58,237,.18); }
.od-port.used.jump i { border-color: #16a085; box-shadow: 0 0 0 2px rgba(22,160,133,.18); }
.od-port.used.linked i { border-color: #16a085; box-shadow: 0 0 0 2px rgba(22,160,133,.18); } /* 他缆纤芯但与当前缆跳接（去向口）：同跳接青环 */
.od-port.used.other i { background: #b9c4d0; border-color: #94a3b3; box-shadow: none; } /* 他缆占用（与当前缆无关）：中性灰，只区分占用/空闲 */
.od-port.used.other b { color: #b6bfc9; }
.od-port.used:hover i { transform: scale(1.12); }
.od-port.pick i { border-color: #e67e22; box-shadow: 0 0 0 3px rgba(230,126,34,.45); }
.od-port.pick b { color: #d35400; }
.od-port.peer i { border-color: #f0a03c; box-shadow: 0 0 0 3px rgba(240,160,60,.4); }
.od-port.over { opacity: .3; cursor: not-allowed; }
.od-port.over i { border-style: dotted; }
.od-legend { margin-right: auto; font-size: 11px; color: #667; display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.od-legend .lg { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-left: 8px; }
.od-legend .lg.free { border: 1px dashed #aab4c0; }
.od-legend .lg.term { background: #f7f3fe; border: 1px solid #7c3aed; }
.od-legend .lg.jump { background: #f0faf8; border: 1px solid #16a085; }
.od-legend .lg.other { background: #b9c4d0; border: 1px solid #94a3b3; }
.od-legend .lg.pick { background: #fdf3e0; border: 1px solid #e67e22; }
/* 纤芯图左侧面板 ODF 成端区：与 ODF 节点同色系（紫） */
#fiber-bar #fb-odf-side .fb-tag { background: #7c3aed; border-color: #7c3aed; color: #fff; }
#fiber-bar #fb-odf-side select, #fiber-bar #fb-odf-side .fb-range { border-color: #7c3aed; }

/* ── 工程量清单导入预检对话框 ── */
.dlg.dlg-imp { width: 720px; }
.imp-file { font-size: 12px; color: #5a6b7b; margin-bottom: 8px; word-break: break-all; }
.imp-stats { display: flex; flex-wrap: wrap; gap: 6px 14px; padding: 8px 10px; background: #f2f8f7; border: 1px solid #d7ece8; border-radius: 6px; font-size: 12px; color: #2c3e50; }
.imp-stats b { color: #16a085; font-size: 14px; }
.imp-totals { margin: 8px 0; font-size: 12px; color: #5a6b7b; }
.lv { font-weight: 600; white-space: nowrap; }
.lv-error { color: #c0392b; }
.lv-warn { color: #b9770e; }
.lv-info { color: #7f8c99; }
.imp-issues { max-height: 250px; overflow: auto; border: 1px solid #e0e6ec; border-radius: 6px; }
.imp-issues table { width: 100%; border-collapse: collapse; font-size: 12px; }
.imp-issues th { position: sticky; top: 0; background: #f5f8fa; color: #5a6b7b; font-weight: 600; text-align: left; padding: 5px 8px; border-bottom: 1px solid #e0e6ec; }
.imp-issues td { padding: 5px 8px; border-bottom: 1px solid #f0f4f7; vertical-align: top; color: #2c3e50; }
.imp-issues td.lv { white-space: nowrap; }
.imp-issues td.pos { color: #7f8c99; white-space: nowrap; }
.imp-issues .val { display: block; color: #9aa5b1; font-size: 11px; }
.imp-clean { padding: 10px; background: #f0faf8; border: 1px solid #d7ece8; border-radius: 6px; color: #16a085; font-size: 13px; }
.imp-rejected { margin-top: 6px; font-size: 12px; color: #c0392b; }
.imp-opts { display: flex; flex-wrap: wrap; gap: 10px 22px; margin-top: 10px; font-size: 12px; color: #5a6b7b; }
.imp-opts select { margin-left: 4px; padding: 3px 6px; border: 1px solid #ccc; border-radius: 4px; font-size: 12px; }
.imp-opts .chk { display: flex; align-items: center; gap: 6px; cursor: pointer; }
.imp-opts .chk input { accent-color: #1abc9c; }
.imp-note { margin-top: 8px; font-size: 12px; color: #7f8c99; line-height: 1.6; }
.imp-ack { display: flex; align-items: center; gap: 6px; margin-top: 10px; font-size: 12px; color: #b9770e; cursor: pointer; }
.imp-msg { font-size: 13px; color: #2c3e50; line-height: 1.7; word-break: break-all; }

/* ── 合规自检左侧面板（checkpanel.js）：停靠视图区左侧的分级报告（不遮挡图面，画布随之变窄）──
   order:-1 排在纤芯图接续面板（order:-3，最左）与向导（order:-2）之后、画布之前；
   顶栏盾牌按钮或面板 × 开关，开关状态记忆 localStorage（fiber-recorder-check）；与使用向导面板互斥（二选一）；分级色复用 lv-* */
#check-panel {
  order: -1; flex: none; width: 286px; align-self: stretch; z-index: 5;
  display: flex; flex-direction: column;
  border-right: 1px solid #d5e0e8; background: #fbfcfe; color: #2c3e50;
  font-size: 12px; line-height: 1.6;
}
#check-panel[hidden] { display: none; } /* 类选择器带 display:flex，需显式回补 [hidden] */
.ckp-head { display: flex; align-items: center; gap: 6px; padding: 8px 10px; background: #eaf4fb; border-bottom: 1px solid #d5e5f0; }
.ckp-head b { flex: 1; color: #1c4e80; font-size: 13px; }
.ckp-x { border: none; background: none; padding: 0 2px; font-size: 16px; line-height: 1; cursor: pointer; color: #999; }
.ckp-x:hover { color: #333; }
.ckp-body { flex: 1; overflow-y: auto; padding: 8px 10px; }
.ckp-scope { color: #8a94a6; font-size: 11px; margin-bottom: 6px; }
.ckp-counts { display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline; margin-bottom: 8px; }
.ckp-counts .ckp-total { color: #8a94a6; font-size: 11px; }
.ckp-list { list-style: none; margin: 0; padding: 0; border-top: 1px solid #eef2f6; }
.ckp-item { display: flex; gap: 6px; padding: 6px 2px; border-bottom: 1px solid #f0f4f7; align-items: baseline; }
.ckp-item .lv {
  flex: none; font-size: 10px; border: 1px solid currentColor; border-radius: 3px;
  padding: 0 3px; line-height: 1.5;
}
.ckp-msg { min-width: 0; color: #46607a; }
.ckp-kind { color: #8a94a6; margin-right: 3px; }
.ckp-name { color: #2c3e50; }
.ckp-loc {
  border: none; background: none; padding: 0; cursor: pointer;
  color: #2f6fb2; font-size: 12px; font-weight: 600; text-align: left;
}
.ckp-loc:hover { text-decoration: underline; color: #16a085; }
.ckp-clean { padding: 10px; background: #f0faf8; border: 1px solid #d7ece8; border-radius: 6px; color: #16a085; }
.ckp-note { margin-top: 6px; color: #8a94a6; font-size: 11px; }
.ckp-tools { display: flex; gap: 6px; padding: 8px 10px; border-bottom: 1px solid #e0e6ec; } /* 重新检查/导出问题清单固定在面板顶部 */
.ckp-btn {
  flex: 1; padding: 5px 0; border: 1px solid #1abc9c; border-radius: 5px; background: #fff;
  color: #16a085; cursor: pointer; font-size: 12px;
}
.ckp-btn:hover { background: #eafaf6; }
.ckp-btn:disabled { opacity: .5; cursor: not-allowed; }
/* 自检运行中：按钮转圈（transform 动画走合成器线程）+ 顶部扫描进度条 + 旧结果变淡，运行状态可感知 */
.ckp-btn.busy { opacity: .8; cursor: wait; color: #0f7f6b; }
.ckp-btn.busy svg.ic { width: 13px; height: 13px; margin-right: 5px; vertical-align: -2px; animation: ckp-spin .8s linear infinite; transform-origin: 50% 50%; }
@keyframes ckp-spin { to { transform: rotate(360deg); } }
.ckp-prog { flex: none; height: 3px; margin: 0 10px; background: #e6eef2; border-radius: 2px; overflow: hidden; }
.ckp-prog i { display: block; height: 100%; width: 42%; border-radius: 2px; background: linear-gradient(90deg, rgba(26,188,156,0) 0%, #1abc9c 45%, #16a085 55%, rgba(26,188,156,0) 100%); animation: ckp-scan 1.05s ease-in-out infinite; }
@keyframes ckp-scan { from { transform: translateX(-110%); } to { transform: translateX(340%); } }
.ckp-body.busy { opacity: .45; filter: saturate(.7); } /* 运行中：旧结果变淡作对照，新结果出即恢复 */
/* 刚运行完：结果区闪动入场（可感知「结果已刷新」） */
.ckp-body.ckp-anim .ckp-counts { animation: ckp-flash .7s ease-out; }
.ckp-body.ckp-anim .ckp-list { animation: ckp-fade .35s ease-out; }
@keyframes ckp-flash { from { background: #fdf1d7; box-shadow: 0 0 0 4px #fdf1d7; } to { background: transparent; box-shadow: none; } }
@keyframes ckp-fade { from { opacity: .25; } to { opacity: 1; } }
.ckp-hint { padding: 12px 4px; color: #8a94a6; text-align: center; }
.ckp-hint-sub { font-size: 11px; color: #a9b3bf; }
.ckp-at { color: #9aa5b1; font-size: 11px; margin-left: auto; }
@media (prefers-reduced-motion: reduce) { /* 动画偏好降级：转圈/扫描/闪动静止（运行文案与按钮禁用态仍可感知） */
  .ckp-btn.busy svg.ic, .ckp-prog i, .ckp-body.ckp-anim .ckp-counts, .ckp-body.ckp-anim .ckp-list { animation: none; }
  .ckp-prog i { width: 100%; }
}

/* ══════════ 三维查看（只读覆盖层）══════════ */
#scene3d {
  position: fixed; inset: 0; z-index: 90;
  display: flex; flex-direction: column; background: #dfe8ee;
}
#scene3d[hidden] { display: none; }
/* 右上角：底图源名胶囊（卡片外上方）+ 控制组（挂入 MapLibre 缩放/罗盘同一白色卡片，29px 方形图标行） */
#scene3d .s3d-src {
  position: absolute; top: 10px; right: 10px; z-index: 5;
  padding: 4px 10px; border-radius: 5px;
  background: rgba(44, 62, 80, .62); color: #dbe6ef;
  font-size: 11px; line-height: 1.5; white-space: nowrap;
  max-width: 42vw; overflow: hidden; text-overflow: ellipsis;
  pointer-events: none;
}
/* 控件卡下移给源名胶囊让位（胶囊单行固定高）；未挂入时隐藏，不参与布局（否则会挤小地图容器高） */
#scene3d .maplibregl-ctrl-top-right { margin-top: 36px; }
#scene3d .s3d-ctl { display: flex; flex-direction: column; align-items: stretch; }
#scene3d > .s3d-ctl { display: none; }
#scene3d .s3d-ctl .s3d-btn {
  width: 29px; height: 29px; padding: 0; justify-content: center; gap: 0;
  border: 0; border-radius: 0; background: transparent; color: #2c3e50; box-shadow: none;
}
#scene3d .s3d-ctl .s3d-btn:hover { background: rgba(0, 0, 0, .05); }
#scene3d .s3d-ctl .s3d-btn + .s3d-btn { border-top: 1px solid #ddd; }
#scene3d .s3d-ctl .s3d-btn.on { background: #e6f7f2; color: #16a085; }
#scene3d .s3d-ctl .s3d-btn svg.ic { width: 16px; height: 16px; }
#scene3d .s3d-2d { font: 700 12px/1 "Microsoft YaHei", "PingFang SC", sans-serif; letter-spacing: .5px; }
#scene3d .s3d-btn {
  display: inline-flex; align-items: center; gap: 5px;
  height: 26px; padding: 0 10px; white-space: nowrap;
  border: 1px solid #56708c; border-radius: 5px; background: #3b5872; color: #eaf2f8;
  cursor: pointer; font-size: 12px; font-family: inherit; line-height: 1;
}
#scene3d .s3d-btn:hover { background: #4a6b8a; }
#scene3d .s3d-btn.active, #scene3d .s3d-btn.on { background: #1abc9c; border-color: #16a085; color: #fff; }
#scene3d .s3d-btn.s3d-primary { background: #1abc9c; border-color: #16a085; color: #fff; }
#scene3d .s3d-btn.s3d-primary:hover { background: #17a589; }
#s3d-map { flex: 1; min-height: 0; background: #dfe8ee; }
#s3d-map canvas:focus { outline: none; }
#scene3d .s3d-tip {
  position: absolute; left: 50%; bottom: 10px; transform: translateX(-50%);
  padding: 4px 12px; border-radius: 12px;
  background: rgba(44, 62, 80, .72); color: #eaf2f8;
  font-size: 11px; white-space: nowrap; pointer-events: none;
}
/* DOM 标注（不依赖 glyph PBF，离线可用） */
#scene3d .s3d-label {
  padding: 1px 5px; border-radius: 4px;
  background: rgba(255, 255, 255, .85); color: #0041c2;
  border: 1px solid rgba(0, 87, 255, .25);
  font: 11px/1.4 "Microsoft YaHei", "PingFang SC", sans-serif;
  white-space: nowrap; text-shadow: 0 1px 0 #fff; user-select: none; pointer-events: none;
}
#scene3d .s3d-lb-closure { color: #c0392b; border-color: rgba(192, 57, 43, .3); }
#scene3d .s3d-lb-jumper { color: #b26a00; border-color: rgba(255, 160, 0, .3); }
#scene3d .s3d-lb-splitter { color: #6a1b9a; border-color: rgba(123, 31, 162, .3); }
#scene3d .s3d-lb-odf { color: #004d40; border-color: rgba(0, 105, 92, .3); }
#scene3d .s3d-lb-olt { color: #0d47a1; border-color: rgba(21, 101, 192, .3); }
#scene3d .s3d-lb-onu { color: #880e4f; border-color: rgba(173, 20, 87, .3); }
#scene3d .s3d-lb-well { color: #b45309; border-color: rgba(180, 83, 9, .3); }
#scene3d .s3d-lb-slack { color: #0041c2; border-color: rgba(0, 87, 255, .3); }
/* 点选信息弹层 */
#scene3d .s3d-pop { min-width: 150px; font-size: 12px; color: #2c3e50; }
#scene3d .s3d-pop-t { font-weight: 600; margin-bottom: 3px; }
#scene3d .s3d-pop-r { color: #667; margin-bottom: 7px; }
#scene3d .s3d-pop .s3d-btn { height: 24px; padding: 0 9px; }
/* 地形设置面板 */
#scene3d .s3d-dlg {
  position: absolute; top: 202px; right: 10px; z-index: 5;
  width: 380px; max-width: calc(100vw - 20px); max-height: calc(100% - 212px); overflow: auto;
  background: #fff; border-radius: 8px; box-shadow: 0 8px 30px rgba(0, 0, 0, .3);
  padding: 12px 14px; font-size: 12px; color: #2c3e50;
}
#scene3d .s3d-dlg[hidden] { display: none; }
#scene3d .s3d-dlg-hd { font-weight: 600; font-size: 13px; margin-bottom: 8px; }
#scene3d .s3d-chk { display: flex; align-items: flex-start; gap: 6px; margin: 6px 0; cursor: pointer; line-height: 1.5; }
#scene3d .s3d-chk input { margin-top: 2px; accent-color: #1abc9c; }
#scene3d .s3d-row { display: flex; align-items: center; gap: 6px; margin: 6px 0; }
#scene3d .s3d-row > span { flex: none; color: #667; }
#scene3d .s3d-row input[type="text"] { flex: 1; min-width: 0; padding: 5px 7px; border: 1px solid #ccc; border-radius: 5px; font-size: 12px; }
#scene3d .s3d-row input[type="text"]:focus { outline: none; border-color: #1abc9c; }
#scene3d .s3d-row2 { display: flex; gap: 12px; margin: 6px 0; }
#scene3d .s3d-row2 label { display: flex; align-items: center; gap: 5px; color: #667; }
#scene3d .s3d-row2 select, #scene3d .s3d-row2 input { padding: 4px 6px; border: 1px solid #ccc; border-radius: 4px; font-size: 12px; background: #fff; }
#scene3d .s3d-row2 input { width: 54px; }
#scene3d .s3d-note { margin: 8px 0; padding-top: 8px; border-top: 1px dashed #e0e6ec; font-size: 11px; color: #8a94a6; line-height: 1.7; word-break: break-all; }
#scene3d .s3d-note b { color: #2f6fb2; }
#scene3d .s3d-test-r { color: #8a94a6; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#scene3d .s3d-dlg-btns { justify-content: flex-end; margin-top: 10px; }
@media (max-width: 640px) { /* 窄屏：三维源名胶囊与操作提示收起，控件卡回到顶部 */
  #scene3d .s3d-src { display: none; }
  #scene3d .s3d-tip { display: none; }
  #scene3d .maplibregl-ctrl-top-right { margin-top: 0; }
}


/* ══════════════ AI 助手面板（左侧停靠抽屉；UI 与 DCDesigner 项目 AI 建模助手掌柜一致） ══════════════ */
#ai-panel {
  order: -1; flex: none; width: 304px; max-width: 100vw; align-self: stretch; z-index: 5; min-height: 0;
  display: flex; flex-direction: column; overflow: hidden;
  background: #fff; border-right: 1px solid #e4e9ef; color: #2c3e50;
  font-size: 12px; line-height: 1.6;
}
#ai-panel[hidden] { display: none; }

/* 页头：底色与向导/自检页头一致（#eaf4fb 浅蓝底 + #1c4e80 深蓝标题） */
.aip-head { display: flex; align-items: center; gap: 6px; padding: 9px 12px; background: #eaf4fb; border-bottom: 1px solid #d5e5f0; }
.aip-head b { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; color: #1c4e80; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.aip-head b svg.ic { width: 15px; height: 15px; margin-right: 5px; flex: none; }
/* 工作中指示圈（页头标题与图标之间）：长对话滚上去也能看到「在忙」 */
.aip-ring {
  flex: none; display: none; width: 12px; height: 12px; box-sizing: border-box;
  border-radius: 50%; border: 2px solid #cfe0ee; border-top-color: #1abc9c;
  animation: aip-spin .8s linear infinite;
}
.aip-head.busy .aip-ring { display: block; }
.aip-icon {
  flex: none; width: 26px; height: 26px; border: none; border-radius: 6px; font-size: 13px;
  background: transparent; color: #98a2ae; cursor: pointer; line-height: 1;
}
.aip-icon:hover { background: #dbe9f4; color: #2c3e50; }
.aip-set-btn { font-size: 16px; }
.aip-icon.active { background: #1abc9c; color: #fff; }
.aip-icon.active:hover { background: #16a085; color: #fff; }
.aip-icon:focus-visible { outline: 2px solid #1abc9c; outline-offset: 1px; }

/* 直连降级提示：轻量黄色条 */
.aip-warn { background: #fffaf0; color: #9a6b00; border-bottom: 1px solid #f3e8c8; padding: 6px 12px; font-size: 12px; line-height: 1.55; }
.aip-warn[hidden] { display: none; }

/* 消息流：极浅灰底；欢迎语纯文本；AI 回复无气泡，仅用户消息保留绿色气泡 */
.aip-msgs { flex: 1; overflow-y: auto; padding: 12px; display: flex; flex-direction: column; gap: 10px; background: #fafbfc; }
.aip-msgs::-webkit-scrollbar { width: 6px; }
.aip-msgs::-webkit-scrollbar-track { background: transparent; }
.aip-msgs::-webkit-scrollbar-thumb { background: #dde3ea; border-radius: 3px; }
.aip-welcome { color: #98a2ae; font-size: 12px; line-height: 1.8; padding: 2px 4px; }
.aip-welcome[hidden] { display: none; }
.aip-msg { font-size: 13px; line-height: 1.7; white-space: pre-wrap; word-break: break-word; }
.aip-user { align-self: flex-end; max-width: 88%; background: #1abc9c; color: #fff; border-radius: 12px 12px 4px 12px; padding: 7px 11px; }
.aip-user-atts { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 5px; }
.aip-user-atts img { display: block; max-width: 108px; max-height: 108px; border-radius: 6px; border: 1px solid rgba(255, 255, 255, .45); }
.aip-user-file { font-size: 11px; background: rgba(255, 255, 255, .18); border-radius: 6px; padding: 2px 6px; }
.aip-ai { align-self: flex-start; max-width: 100%; color: #2c3e50; padding: 0 4px; }
.aip-note { font-size: 12px; color: #98a2ae; padding: 0 4px; }
.aip-err { align-self: stretch; background: #fdf2f1; border: 1px solid #f6dcd8; color: #b3261e; border-radius: 8px; padding: 7px 10px; font-size: 12px; }

/* 「正在处理」指示器：三点跳动 / 旋转圈 + 阶段文案 + 等待秒数 + 停止（全部事件驱动） */
.aip-thinking { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; color: #98a2ae; font-size: 12px; padding: 0 4px; }
.aip-mark { display: inline-flex; align-items: center; }
.aip-dots { display: inline-flex; align-items: center; gap: 3px; }
.aip-dots i { display: block; width: 4px; height: 4px; border-radius: 50%; background: #1abc9c; animation: aip-dot 1.2s ease-in-out infinite; }
.aip-dots i:nth-child(2) { animation-delay: .15s; }
.aip-dots i:nth-child(3) { animation-delay: .3s; }
@keyframes aip-dot {
  0%, 80%, 100% { opacity: .25; transform: translateY(0); }
  40% { opacity: 1; transform: translateY(-3px); }
}
/* 执行指令期改用旋转圈 */
.aip-spin {
  display: none; width: 11px; height: 11px; box-sizing: border-box;
  border-radius: 50%; border: 2px solid #dde3ea; border-top-color: #1abc9c;
  animation: aip-spin .8s linear infinite;
}
.aip-thinking.phase-exec .aip-dots { display: none; }
.aip-thinking.phase-exec .aip-spin { display: inline-block; }
.aip-think-text { font-style: italic; }
.aip-think-wait { font-variant-numeric: tabular-nums; color: #b6c0ca; }
.aip-think-stop {
  padding: 1px 8px; border: 1px solid #dde3ea; border-radius: 999px;
  background: #fff; color: #55606c; cursor: pointer; font-size: 11px; line-height: 1.6; font-family: inherit;
}
.aip-think-stop:hover { border-color: #1abc9c; color: #16a085; }

/* 设置：去卡片化，直接融入消息流 */
.aip-settings { padding: 2px 4px; display: flex; flex-direction: column; gap: 8px; }
.aip-settings[hidden] { display: none; }
.aip-field { display: flex; flex-direction: column; gap: 3px; font-size: 12px; color: #7b8794; }
.aip-field input { padding: 7px 9px; border: 1px solid #dde3ea; border-radius: 6px; font-size: 12px; font-family: inherit; background: #fff; }
.aip-field input:focus { outline: none; border-color: #1abc9c; }
.aip-chk { display: flex; align-items: center; gap: 5px; font-size: 12px; color: #7b8794; }
.aip-chk input { accent-color: #1abc9c; }
.aip-cfg-actions { display: flex; gap: 6px; }
.aip-cfg-actions button { flex: 1; padding: 6px 8px; border: 1px solid #dde3ea; border-radius: 6px; background: #fff; cursor: pointer; font-size: 12px; color: #55606c; font-family: inherit; }
.aip-cfg-actions button:hover { border-color: #1abc9c; color: #16a085; }
.aip-cfg-status { font-size: 12px; color: #7b8794; word-break: break-all; }
.aip-privacy { font-size: 11px; color: #a7b0ba; line-height: 1.6; }

/* 输入区：无分隔线，与消息流同底；通体圆角容器聚焦时亮边 */
.aip-inputrow { display: flex; padding: 4px 12px 12px; background: #fafbfc; }
.aip-composer {
  flex: 1; min-width: 0; display: flex; flex-direction: column;
  padding: 5px 5px 5px 12px; border: 1px solid #dde3ea; border-radius: 14px;
  background: #fff; transition: border-color .15s, box-shadow .15s;
}
.aip-editor { display: flex; align-items: center; gap: 6px; min-width: 0; }
.aip-composer:focus-within { border-color: #1abc9c; box-shadow: 0 0 0 3px rgba(26, 188, 156, .12); }
.aip-composer.aip-drop { border-color: #1abc9c; background: #f4fdfa; }
/* 附件条：缩略图/文件名/体积/移除；图片附件另有一行视觉能力提示 */
.aip-atts { display: flex; flex-direction: column; gap: 4px; padding: 1px 0 3px; }
.aip-atts[hidden] { display: none; }
.aip-att-list { list-style: none; display: flex; flex-wrap: wrap; gap: 5px; padding: 0; margin: 0; }
.aip-att {
  display: inline-flex; align-items: center; gap: 5px; max-width: 100%; padding: 2px 4px 2px 2px;
  border: 1px solid #e4e9ef; border-radius: 8px; background: #f7f9fb; font-size: 11px; color: #55606c;
}
.aip-att-thumb { flex: none; width: 26px; height: 26px; object-fit: cover; border-radius: 5px; border: 1px solid #e4e9ef; background: #fff; }
.aip-att-ic { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 5px; border: 1px solid #e4e9ef; background: #fff; color: #7b8794; }
.aip-att-ic svg.ic { width: 14px; height: 14px; }
.aip-att-name { max-width: 116px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.aip-att-size { flex: none; color: #a7b0ba; }
.aip-att-x { flex: none; width: 16px; height: 16px; padding: 0; border: none; border-radius: 50%; background: transparent; color: #a7b0ba; cursor: pointer; font-size: 10px; line-height: 1; }
.aip-att-x:hover { background: #f6dcd8; color: #b3261e; }
.aip-att-hint, .aip-att-warn { font-size: 11px; line-height: 1.5; }
.aip-att-hint { color: #a7b0ba; }
.aip-att-hint[hidden], .aip-att-warn[hidden] { display: none; }
.aip-att-warn { color: #b3261e; }
/* 高度完全由 ai-panel.js autoGrow() 写入：2 行最小高度起、约 6 行封顶 */
.aip-input {
  flex: 1; min-width: 0; resize: none; border: none; background: transparent;
  padding: 5px 0; font: inherit; font-size: 13px; line-height: 1.5;
  max-height: 132px; overflow-y: hidden;
}
.aip-input:focus { outline: none; }
.aip-input::placeholder { font-size: 12px; line-height: 1.5; opacity: 1; color: #b6bfc9; }
.aip-input:disabled { background: transparent; }
.aip-input::-webkit-scrollbar { width: 6px; }
.aip-input::-webkit-scrollbar-track { background: transparent; }
.aip-input::-webkit-scrollbar-thumb { background: #dde3ea; border-radius: 3px; }
.aip-btns { display: flex; align-items: center; gap: 4px; flex: none; }
.aip-btns button {
  display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px;
  padding: 0; border: none; border-radius: 50%; background: #1abc9c; color: #fff; cursor: pointer;
}
.aip-btns button svg.ic { width: 17px; height: 17px; }
.aip-btns button.aip-send svg.ic { margin: 0 2px 2px 0; }
.aip-btns button:hover:not(:disabled) { background: #16a085; }
.aip-btns button.aip-stop { background: #e67e22; }
.aip-btns button.aip-stop svg.ic { width: 15px; height: 15px; }
.aip-btns button.aip-stop:hover:not(:disabled) { background: #d35400; }
/* 📎 附件按钮：未挂附件时是幽灵图标，挂了附件后点亮为强调色 */
.aip-btns button.aip-att-btn { background: transparent; color: #98a2ae; }
.aip-btns button.aip-att-btn:hover:not(:disabled) { background: #eef3f7; color: #16a085; }
.aip-btns button.aip-att-btn.active { background: #1abc9c; color: #fff; }
.aip-btns button.aip-att-btn.active:hover:not(:disabled) { background: #16a085; color: #fff; }
.aip-btns button:disabled { opacity: .4; cursor: default; }
.aip-btns button[hidden] { display: none; }

/* 撤销本轮：输入区上方一行胶囊小按钮 */
.aip-foot { display: flex; align-items: center; gap: 8px; padding: 0 12px 10px; background: #fafbfc; font-size: 12px; color: #98a2ae; }
.aip-foot[hidden] { display: none; }
.aip-undo { padding: 4px 12px; border: 1px solid #dde3ea; border-radius: 999px; background: #fff; cursor: pointer; font-size: 12px; color: #55606c; font-family: inherit; }
.aip-undo:hover { border-color: #1abc9c; color: #16a085; }
.aip-undo[hidden] { display: none; }

/* 执行计划折叠 + 外部 Agent 接入：低频功能，弱化 */
.aip-plan { margin-top: 5px; font-size: 12px; color: #7b8794; }
.aip-plan summary { cursor: pointer; color: #16a085; }
.aip-plan ol { margin: 4px 0 0; padding-left: 18px; }
.aip-guide { border-top: 1px solid #eef1f5; padding: 7px 12px; background: #fff; font-size: 12px; color: #7b8794; }
.aip-guide summary { cursor: pointer; color: #98a2ae; font-size: 11px; user-select: none; }
.aip-guide summary:hover { color: #16a085; }
.aip-guide-body { padding-top: 8px; }
.aip-guide-text { margin: 0 0 6px; line-height: 1.6; }
.aip-guide-body .ai-urlrow { display: flex; gap: 6px; margin: 4px 0; }
.aip-guide-body .ai-url { flex: 1; min-width: 0; padding: 5px 7px; border: 1px solid #dde3ea; border-radius: 6px; font-size: 11px; color: #7b8794; }
.aip-guide-body .ai-copy { padding: 4px 10px; border: 1px solid #dde3ea; border-radius: 6px; background: #fff; color: #16a085; cursor: pointer; font-size: 12px; white-space: nowrap; font-family: inherit; }
.aip-guide-body .ai-copy:hover { border-color: #1abc9c; }
.aip-guide-body .ai-status { font-size: 11px; color: #a7b0ba; line-height: 1.6; margin: 0; }

/* 破坏性操作确认条 / 步骤日志：统一细边框圆角卡 */
.aip-confirm { background: #fff; border: 1px solid #f6dcd8; border-radius: 10px; padding: 10px 12px; font-size: 13px; }
.aip-confirm-head { font-weight: bold; color: #b3261e; margin-bottom: 4px; font-size: 12px; }
.aip-confirm-list { list-style: none; display: flex; flex-direction: column; gap: 5px; margin: 0; padding: 0; }
.aip-confirm-list li { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
.aip-confirm-list li.aip-danger { color: #b3261e; }
.aip-tool { font-size: 11px; color: #6b7f96; background: #f1f4f8; border-radius: 4px; padding: 1px 5px; }
.aip-badge { font-size: 11px; color: #b3261e; background: #fdf2f1; border-radius: 4px; padding: 1px 5px; }
.aip-confirm-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.aip-confirm-actions button { padding: 5px 12px; border: 1px solid #dde3ea; border-radius: 999px; background: #fff; cursor: pointer; font-size: 12px; color: #55606c; font-family: inherit; }
.aip-confirm-actions button:hover:not(:disabled) { border-color: #1abc9c; color: #16a085; }
.aip-confirm-actions button.aip-confirm-run { background: #d9534f; border-color: #d9534f; color: #fff; }
.aip-confirm-actions button.aip-confirm-run:hover:not(:disabled) { background: #c9302c; border-color: #c9302c; color: #fff; }
.aip-confirm-actions button:disabled { opacity: .45; cursor: default; }
.aip-batch { background: #fff; border: 1px solid #e4e9ef; border-radius: 10px; padding: 7px 10px; font-size: 12px; }
.aip-batch summary { cursor: pointer; color: #7b8794; user-select: none; }
.aip-step-row { display: flex; align-items: baseline; gap: 6px; padding: 3px 0; border-top: 1px dashed #eef1f5; flex-wrap: wrap; }
.aip-step-row .aip-step-ic { font-weight: bold; }
.aip-step-row.ok .aip-step-ic { color: #1abc9c; }
.aip-step-row.fail .aip-step-ic { color: #d9534f; }
.aip-step-row.fail .aip-step-text { color: #b3261e; }
.aip-step-text { flex: 1; min-width: 120px; }

/* 顶栏 AI 按钮：忙碌时持续呼吸（面板收起也可见）；打开态沿用通用 .tb button.active 强调色 */
@keyframes aip-spin { to { transform: rotate(360deg); } }
@keyframes aip-breathe {
  0%, 100% { box-shadow: 0 0 0 0 rgba(26, 188, 156, .55); }
  50% { box-shadow: 0 0 0 5px rgba(26, 188, 156, 0); }
}
#btn-ai.busy { animation: aip-breathe 1.4s ease-in-out infinite; background: #1abc9c; border-color: #16a085; color: #fff; }
/* 减动效偏好：关掉跳动/旋转/呼吸，只留静态文案与状态 */
@media (prefers-reduced-motion: reduce) {
  .aip-dots i, .aip-spin, .aip-ring, #btn-ai.busy { animation: none; }
}
@media (max-width: 1200px) { #ai-panel { width: 264px; } }
@media (max-width: 560px) { #ai-panel { width: 240px; } }
