/* 无限画布：视口由本页自行控制，不参与全站 2560×1280 锁定缩放。
   原因：整页 transform scale 会让 wheel 灵敏度随窗口大小漂移，
   且 getBoundingClientRect 返回的是缩放后的值，画布内坐标换算需要额外补偿。 */

html.canvas-page,
html.canvas-page body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background: #07080b;
}

/* 顶栏按 27 寸 93px 设计稿等比缩放：只改一个比例因子，不换样式。
   2560 = 1.000（93px）；1920 ≈ 0.852（79px）；1440 ≈ 0.741（69px）；
   1024 及以下 = 0.645（60px 下限，保证 iPad 上还能读）。
   所有尺寸都从 --canvas-header-h 乘 27 寸原比例得到，LOGO 宽高比恒为 60:93。 */
.canvas-page {
  --canvas-header-h: clamp(60px, calc(60px + (100vw - 1024px) * 0.021484), 93px);
}

.canvas-page .canvas-shell {
  position: fixed;
  inset: 0;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  padding: 0;
  background: var(--bg-dark, #07080a);
}
html[data-theme="light"] .canvas-page .canvas-shell {
  background: #ffffff;
}
html[data-theme="light"] .canvas-page .canvas-header {
  background: #ffffff !important;
  border-bottom: 1px solid rgba(180, 140, 80, 0.25) !important;
}

.canvas-page .canvas-header {
  flex: 0 0 auto;
  height: var(--canvas-header-h);
  padding: 0 calc(var(--canvas-header-h) * 0.6022) 0 calc(var(--canvas-header-h) * 0.172);
  align-items: center;
  border-bottom: 1px solid rgba(212, 190, 136, 0.16) !important;
  background: #07080b;
  box-shadow: none;
  box-sizing: border-box;
}

.canvas-page .canvas-header-inner {
  flex: 1 1 auto;
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  height: 100%;
  min-width: 0;
  box-sizing: border-box;
  padding: 0;
  margin-left: 0;
  transform: none;
  transform-origin: initial;
}

/* 27 寸原比例：品牌 34/93、导航 21/93、副标题 18/93、LOGO 宽 60/93。
   全部用同一个顶栏高度乘出来，所以任何宽度下都是同一套样式。 */
.canvas-page .shared-brand { gap: calc(var(--canvas-header-h) * 0.1505); }
.canvas-page .shared-brand-copy { min-height: calc(var(--canvas-header-h) * 0.4516); }
.canvas-page .shared-crest {
  width: calc(var(--canvas-header-h) * 0.6452);
  flex-basis: calc(var(--canvas-header-h) * 0.6452);
}
.canvas-page .shared-brand-title {
  font-size: calc(var(--canvas-header-h) * 0.3656);
  letter-spacing: 0.176em;
}
.canvas-page .shared-brand-subtitle {
  font-size: calc(var(--canvas-header-h) * 0.1935);
  letter-spacing: 0.167em;
}
.canvas-page .shared-header-right { gap: calc(var(--canvas-header-h) * 0.2581); }
.canvas-page .shared-nav { gap: calc(var(--canvas-header-h) * 0.3871); }
.canvas-page .shared-nav a,
.canvas-page .shared-theme {
  font-size: calc(var(--canvas-header-h) * 0.2258);
  letter-spacing: 0.095em;
}

/* 原版中英文架构等比微缩至 85%（51px 微标 + 29px AMBIENT + 15.3px 副标，带 13px 下限保护） */
.canvas-page .shared-brand-copy {
  display: flex !important;
  flex-direction: column !important;
  justify-content: center !important;
  gap: 7px !important;
}
.canvas-page .shared-crest {
  width: clamp(38px, calc(var(--canvas-header-h) * 0.5484), 51px) !important;
  height: clamp(38px, calc(var(--canvas-header-h) * 0.5484), 51px) !important;
  flex: 0 0 clamp(38px, calc(var(--canvas-header-h) * 0.5484), 51px) !important;
  flex-basis: clamp(38px, calc(var(--canvas-header-h) * 0.5484), 51px) !important;
  align-self: center !important;
}
.canvas-page .shared-brand-title {
  font-size: clamp(21px, calc(var(--canvas-header-h) * 0.3108), 29px) !important;
  letter-spacing: 0.176em !important;
  font-weight: 300 !important;
  color: #ffffff !important;
  text-transform: uppercase !important;
}
.canvas-page .shared-brand-subtitle {
  font-size: clamp(13px, calc(var(--canvas-header-h) * 0.1645), 15.3px) !important;
  letter-spacing: 0.167em !important;
  font-weight: 400 !important;
  color: var(--gold-primary, #d4be88) !important;
}
html[data-theme="light"] .canvas-page .shared-brand-title {
  color: #0f172a !important;
}
html[data-theme="light"] .canvas-page .shared-brand-subtitle {
  color: #9b7c38 !important;
}
.canvas-page .shared-status { display: none; }

/* 项目切换器（方案 A）：画布左上角 40px 胶囊，距顶栏 12px。
   不再骑在分界线；z-index 高于卡片，但不吃画布手势（JS 里有排除）。 */
.canvas-page .canvas-stage #currentProjectBadge.workbench-hud-plaque,
.canvas-page .canvas-stage .workbench-hud-plaque {
  top: 12px !important;
  /* 和顶栏 LOGO 方框的左边框对齐：同一个内边距 */
  left: calc(var(--canvas-header-h) * 0.172) !important;
  transform: none !important;
  height: 38px !important;
  line-height: 36px !important;
  padding: 0 14px !important;
  border-radius: 8px !important;
  gap: 8px !important;
  max-width: min(280px, 42vw) !important;
  z-index: 40 !important;
}
.canvas-page .canvas-stage .workbench-hud-plaque:hover {
  transform: translateY(-1px) !important;
}
.canvas-page .canvas-stage #currentProjectBadge .hud-plaque-prefix,
.canvas-page .canvas-stage .workbench-hud-plaque .hud-plaque-prefix {
  font-size: 15px !important;
}
.canvas-page .canvas-stage #currentProjectBadge .hud-plaque-title,
.canvas-page .canvas-stage .workbench-hud-plaque .hud-plaque-title,
.canvas-page .canvas-stage #currentProjectBadge.workbench-hud-plaque strong {
  font-size: 15px !important;
  max-width: 220px !important;
}
.canvas-page .canvas-stage .workbench-hud-plaque .hud-plaque-arrow {
  font-size: 11px !important;
}
@media (max-width: 899px) {
  .canvas-page .canvas-stage #currentProjectBadge.workbench-hud-plaque,
  .canvas-page .canvas-stage .workbench-hud-plaque {
    top: 10px !important;
    left: calc(var(--canvas-header-h) * 0.172) !important;
    height: 38px !important;
    line-height: 36px !important;
    max-width: 56vw !important;
  }
  .canvas-page .canvas-stage #currentProjectBadge .hud-plaque-title,
  .canvas-page .canvas-stage .workbench-hud-plaque .hud-plaque-title,
  .canvas-page .canvas-stage #currentProjectBadge.workbench-hud-plaque strong {
    font-size: 15px !important;
    max-width: 32vw !important;
  }
  .canvas-page .canvas-stage #currentProjectBadge .hud-plaque-prefix,
  .canvas-page .canvas-stage .workbench-hud-plaque .hud-plaque-prefix {
    font-size: 14px !important;
  }
}

/* ── 画布舞台 ───────────────────────────────────────────── */
/* 默认手势是框选（P29），所以空白处是十字光标；
   平移改由中键或 Space+左键触发，那两种情况下光标换成抓手。
   原来是 grab——那时拖空白确实是平移，现在拖空白拉框，光标必须跟着改，
   否则用户按下去才发现「怎么在画框」。 */
.canvas-stage {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  /* overflow: clip 不产生滚动容器：卡片再远也不会把舞台撑出滚动，
     HUD 的 absolute bottom 才不会被 scrollIntoView 顶走。hidden 作为旧浏览器兜底。 */
  overflow: hidden;
  overflow: clip;
  cursor: crosshair;
  background: #07080b;
  /* 触屏手势由画布自己接管：双指平移/缩放、单指框选/平移。
     不写这一条，iPad 会把双指解释成页面缩放并打断指针事件。 */
  touch-action: none;
  overscroll-behavior: none;
}

.canvas-stage.is-panning {
  cursor: grabbing;
}
/* 平移模式：拖空白是平移，空白处给抓手；拖动中收紧。
   框选模式仍保持十字光标，卡片自己的 grab/grabbing 优先级更高。 */
.canvas-stage.is-pan-mode {
  cursor: grab;
}
.canvas-stage.is-pan-mode.is-panning {
  cursor: grabbing;
}
/* 按住空格：整块画布（含卡片）都变成「可抓」，
   光标先给反馈，用户不用先试一下才知道空格生效了。 */
.canvas-stage.is-space-pan,
.canvas-stage.is-space-pan .canvas-card {
  cursor: grab;
}
.canvas-stage.is-space-pan.is-panning,
.canvas-stage.is-space-pan.is-panning .canvas-card {
  cursor: grabbing;
}

/* 缩放/捏合进行中：卡片上的毛玻璃会在每一帧重新栅格化，Safari 上尤其贵。
   手势期间先退回纯色底，结束 200ms 后自动恢复，视觉几乎无感。 */
.canvas-stage.is-zooming .canvas-card-action,
.canvas-stage.is-zooming .canvas-corner-chip,
.canvas-stage.is-zooming .canvas-card-del,
.canvas-stage.is-zooming .canvas-card-view,
.canvas-stage.is-zooming .canvas-card-dl {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

/* 参考线已永久改成实线/保留箭头与命中区，缩放期间不再切换样式，避免松手恢复卡顿。 */

/* ?perf=1 调试浮层：只在查询参数打开时创建，pointer-events 关闭不挡操作。 */
.canvas-perf {
  position: fixed;
  left: 12px;
  top: 12px;
  z-index: 99999;
  padding: 6px 10px;
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.72);
  color: #d4be88;
  font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: nowrap;
  pointer-events: none;
}

/* 框选矩形（P29）。
   画在屏幕空间的层里，不随视口缩放：绑进 viewport 的话缩到 20% 时
   边框只剩 0.3px，看不见。命中判定另按世界坐标算，两者互不影响。
   用 outline 而不是 border：outline 不占布局，少一次重排。 */
.canvas-marquee {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 8;
  pointer-events: none;
  border: 1px solid rgba(212, 190, 136, 0.9);
  background: rgba(212, 190, 136, 0.10);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.35) inset;
  border-radius: 2px;
  will-change: transform, width, height;
}
.canvas-marquee[hidden] {
  display: none !important;
}

/* 画布底板：纯净深黑背景，零点阵杂质，极致聚焦图片内容与连线网络 */
.canvas-grid {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-color: #07080b;
}

/* 视口层：只做变换，尺寸不设，内容自然撑开 */
.canvas-viewport {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 0;
  transform-origin: 0 0;
  will-change: transform;
}

.canvas-edges {
  position: absolute;
  left: 0;
  top: 0;
  width: 1px;
  height: 1px;
  overflow: visible;
  pointer-events: none;
}

.canvas-nodes {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 0;
}

/* ── 连线 ───────────────────────────────────────────────── */
/* 线宽按视口缩放做部分补偿：完全跟随缩放时缩到 20% 只剩 0.3px 几乎看不见，
   全额补偿又会让密集区域糊成一片。这里只补 85%，其余留给自然缩小。

   颜色分三层（P27）：
     默认   中性灰 —— 线是背景信息，不该和图片抢注意力；
     悬停   灰蓝   —— 唯一「可点」的提示，只有参考线有命中区；
     选中   金色   —— 当前这条关系在被操作。
   此前默认色是蓝灰，整片画布铺满带色相细线，看着像蒙了一层滤镜。 */
.canvas-edge {
  fill: none;
  stroke: rgba(188, 193, 203, 0.55);
  stroke-width: calc(1.6px * var(--edge-inv-scale, 1));
  stroke-linecap: round;
}

/* 生成边：实线，表示「这张图是从那张出来的」 */
.canvas-edge.is-generated {
  stroke: rgba(188, 193, 203, 0.55);
}

/* 转换边：虚线，表示「这张图是从那张复制出来的」。
   注意：复制机制已取消，新建项目不会再产生这种边，只有历史项目才有。 */
.canvas-edge.is-derived {
  stroke: rgba(188, 193, 203, 0.55);
  stroke-dasharray: 6 5;
}

/* 参考边：任意图 → 使用它的卡，表示「作为参考图参与出图」。
   永久实线、高对比琥珀橙，不再用虚线：Safari/iPad 每次缩放都要沿长曲线
   重算 dash 段，参考线越多越卡；实线没有这项成本，也不会在松手恢复时二次卡顿。
   与冷灰生成实线靠颜色区分；箭头保留，颜色跟随线。 */
.canvas-edge.is-reference {
  stroke: rgba(245, 166, 35, 0.95);
  stroke-width: calc(1.8px * var(--edge-inv-scale, 1));
}

/* 箭头颜色按类型分开：生成线冷灰，参考线琥珀橙，参考悬停亮蓝。
   箭头定义在 canvas.html 的 <defs> 里，CSS 覆盖显式填充。 */
#arrowGenerated path,
#arrowDerived path {
  fill: rgba(188, 193, 203, 0.8);
}
#arrowReference path {
  fill: rgba(245, 166, 35, 0.95);
}
#arrowReferenceHover path {
  fill: rgba(140, 170, 210, 1);
}

/* 参考线的透明命中区：可见线只有 1.6px，缩到 45% 时不到 1px，直接点几乎点不中。
   宽度按屏幕像素恒定，不随缩放变细；默认透明，只负责命中与光标。 */
.canvas-edge-hit {
  fill: none;
  stroke: transparent;
  stroke-width: calc(14px * var(--edge-inv-scale, 1));
  stroke-linecap: round;
  pointer-events: stroke;
  cursor: pointer;
}

/* 悬停反馈：命中区是同级元素，用 SVG 的兄弟选择器把可见参考线点亮。
   线和箭头一起变亮蓝，提示「这条线可点」。 */
.canvas-edge-hit:hover + .canvas-edge.is-reference {
  stroke: rgba(140, 170, 210, 1);
  stroke-width: calc(2.4px * var(--edge-inv-scale, 1));
  marker-end: url(#arrowReferenceHover);
}

.canvas-edge.is-active {
  stroke: rgba(212, 190, 136, 0.95);
  stroke-width: calc(2.2px * var(--edge-inv-scale, 1));
}

/* 独立参考图节点：用金色描边把「素材」与「作品」区分开，
   但它与普通卡同尺寸、同交互，拖拽与拉线行为一致。 */
.canvas-card.is-reference .canvas-card-inner {
  outline: 1px solid rgba(212, 190, 136, 0.4);
  outline-offset: -1px;
}

.canvas-card.is-reference .canvas-port.is-out {
  border-color: rgba(212, 190, 136, 0.85);
}

/* ── 卡片 ───────────────────────────────────────────────── */
/* 外壳：只管定位与状态，不裁剪——端口要落在卡片边界之外。
   裁剪交给内壳，否则端口伸出卡片的那一半会被切掉。 */
.canvas-card {
  position: absolute;
  left: 0;
  top: 0;
  display: block;
  cursor: grab;
  user-select: none;
  /* 用 layout + style 而非 paint：paint 同样会裁剪溢出内容 */
  contain: layout style;
}

/* 内壳：负责圆角、裁剪与视觉。媒体区与底部说明原本靠卡片圆角裁切，
   放开外壳裁剪后必须由内壳接手，否则方角会戳出圆角。 */
.canvas-card-inner {
  display: flex;
  flex-direction: column;
  border-radius: 14px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  background: rgba(16, 19, 26, 0.96);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55);
  overflow: hidden;
  height: 608px;
  contain: paint;
  transition: border-color 0.16s ease;
}

/* 低缩放轻量化档：卡片只保留图片。装饰层不是被隐藏，而是根本没建（见 canvas.js），
   这里额外省掉圆角、描边与投影——几百上千张卡同时画阴影是真开销，
   而缩到 30% 以下时那点阴影本来也看不出来。 */
.canvas-card.is-lite .canvas-card-inner {
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: #0b0d12;
}

.canvas-card:hover .canvas-card-inner {
  border-color: rgba(212, 190, 136, 0.45);
  box-shadow: 0 14px 38px rgba(0, 0, 0, 0.65), 0 0 0 1px rgba(212, 190, 136, 0.12);
}

.canvas-card.is-dragging {
  cursor: grabbing;
  z-index: 10;
}

.canvas-card.is-dragging .canvas-card-inner {
  border-color: rgba(212, 190, 136, 0.7);
  box-shadow: 0 20px 48px rgba(0, 0, 0, 0.8), 0 0 0 1px rgba(212, 190, 136, 0.35);
}

.canvas-card.is-selected .canvas-card-inner {
  border-color: rgba(212, 190, 136, 0.85);
  box-shadow: 0 16px 42px rgba(0, 0, 0, 0.7), 0 0 0 1px rgba(212, 190, 136, 0.45);
}

.canvas-card-media {
  position: relative;
  width: 100%;
  height: 100%;
  background: #0b0d12;
  flex: 0 0 auto;
  overflow: hidden;
  contain: paint;
}

/* 出图页左右大图同款底板：空出的区域展示当前图片的高斯模糊饱和光晕 + 暗色遮罩，绝不死黑空洞 */
.canvas-card-media.has-bg::before {
  content: "";
  position: absolute;
  inset: -24px;
  background-image: var(--media-bg, none);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  filter: blur(36px) saturate(1.2);
  transform: scale(1.08);
  z-index: 0;
  pointer-events: none;
}
.canvas-card-media.has-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(10, 12, 16, 0.45);
  z-index: 0;
  pointer-events: none;
}

.canvas-card-media img {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  /* 图片完全不吃指针事件：原生图片拖拽会派发 pointercancel 打断卡片拖动。
     图片本身没有任何交互，代价为零，但事件必须能落到卡片上。 */
  pointer-events: none;
  -webkit-user-drag: none;
  user-select: none;
}

/* ── 卡片内嵌就地对比滑杆（Before/After Slider） ────────────────────
   默认常驻停在最左侧（0%），平时 100% 完整展示效果图；
   鼠标按住金色小手柄（◀▶）向右拖动，随时随地在卡片上刮开透视原图 */
.card-compare-wrap {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  overflow: hidden;
  --card-cut: 0%;
}
.card-compare-before {
  position: absolute;
  inset: 0;
  overflow: hidden;
  clip-path: polygon(0 0, var(--card-cut, 0%) 0, var(--card-cut, 0%) 100%, 0 100%);
  pointer-events: none;
}
.card-compare-before img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
.card-compare-divider {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--card-cut, 0%);
  width: 2px;
  background: #ffffff;
  box-shadow: 0 0 6px rgba(0, 0, 0, 0.7);
  transform: translateX(-50%);
  pointer-events: none;
}
.card-compare-handle {
  position: absolute;
  top: 25%;
  left: var(--card-cut, 0%);
  transform: translate(-50%, -50%);
  width: calc(34px * var(--icon-inv, 1));
  height: calc(34px * var(--icon-inv, 1));
  border-radius: 50%;
  background: #ffffff;
  color: #1a160f;
  border: 2px solid var(--accent, #d4be88);
  box-shadow: 0 0 0 calc(2px * var(--icon-inv, 1)) rgba(212, 190, 136, 0.4), 0 4px 10px rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: calc(13px * var(--icon-inv, 1));
  font-weight: 800;
  letter-spacing: -1px;
  cursor: ew-resize;
  pointer-events: auto;
  user-select: none;
  z-index: 30;
  transition: transform 0.12s ease, background 0.12s ease;
}
.card-compare-handle:hover {
  transform: translate(-50%, -50%) scale(1.15);
  background: var(--gold-primary, #d4be88);
  color: #000;
}

/* 端口：唯一连线锚点。结构分两层——
   外层 .canvas-port 是透明热区（24×32 屏幕像素），只负责命中与 hover 保持；
   内层 ::before 是 12px 可见圆点，负责好看。
   两层都按 --port-inv 反补缩放：世界坐标尺寸乘上 1/scale 后，
   屏幕上才恒为设计尺寸。不补的话 45% 缩放下热区只剩 11×14，等于没扩大命中范围。
   圆点中心固定在卡片边缘外 --port-gap（世界坐标，与连线端点同一坐标）。 */
.canvas-port {
  position: absolute;
  width: calc(24px * var(--port-inv, 1));
  height: calc(32px * var(--port-inv, 1));
  opacity: 0;
  transition: opacity 0.14s ease;
  /* 热区可命中：指针进入即保持显示，从卡片移到圆点不会闪断 */
  pointer-events: auto;
  z-index: 6;
}

/* 减少动态效果时直接显示，不依赖过渡：过渡若因任何原因不推进（系统设置、
   低功耗模式、时间轴异常），端口会永远停在 opacity 0，等于端口消失。 */
@media (prefers-reduced-motion: reduce) {
  .canvas-port,
  .canvas-port::before {
    transition: none;
  }
}

/* 可见圆点：居中于热区 */
.canvas-port::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: calc(12px * var(--port-inv, 1));
  height: calc(12px * var(--port-inv, 1));
  margin-left: calc(-6px * var(--port-inv, 1));
  margin-top: calc(-6px * var(--port-inv, 1));
  border-radius: 50%;
  border: calc(1.5px * var(--port-inv, 1)) solid rgba(212, 190, 136, 0.75);
  background: #0d1016;
  box-shadow: 0 0 0 calc(3px * var(--port-inv, 1)) rgba(13, 16, 22, 0.9);
  transform: scale(0.7);
  transition: transform 0.14s ease, border-color 0.14s ease;
}

.canvas-card:hover .canvas-port,
.canvas-card.is-dragging .canvas-port,
.canvas-card.is-selected .canvas-port,
.canvas-card.is-open .canvas-port,
.canvas-port:hover {
  opacity: 1;
}

/* 拉线期间：正向拉线目标卡亮入口端口(is-in)；反向拉线（从原图左端起手拉向参考图）目标卡亮出口端口(is-out)。 */
.canvas-stage:not(.is-linking-reverse) .canvas-card.is-link-target .canvas-port.is-in,
.canvas-stage.is-linking-reverse .canvas-card.is-link-target .canvas-port.is-out {
  opacity: 1;
}

.canvas-stage:not(.is-linking-reverse) .canvas-card.is-link-target .canvas-port.is-in::before,
.canvas-stage.is-linking-reverse .canvas-card.is-link-target .canvas-port.is-out::before {
  transform: scale(1.15);
}

/* 最近的可落点：对应方向的端口明显放大并换成实心，让「松手就连这里」一眼可见 */
.canvas-stage:not(.is-linking-reverse) .canvas-card.is-link-nearest .canvas-port.is-in,
.canvas-stage.is-linking-reverse .canvas-card.is-link-nearest .canvas-port.is-out {
  opacity: 1;
}

.canvas-stage:not(.is-linking-reverse) .canvas-card.is-link-nearest .canvas-port.is-in::before,
.canvas-stage.is-linking-reverse .canvas-card.is-link-nearest .canvas-port.is-out::before {
  transform: scale(1.7);
  border-color: rgba(212, 190, 136, 1);
  background: rgba(212, 190, 136, 0.9);
  box-shadow: 0 0 0 calc(4px * var(--port-inv, 1)) rgba(212, 190, 136, 0.22);
}

.canvas-card.is-link-nearest .canvas-card-inner {
  outline: 1px solid rgba(212, 190, 136, 0.55);
  outline-offset: -1px;
}

/* 拉线时整卡描边 + 顶部「＋参考」标签：不再只靠一个小圆点提示可落。 */
.canvas-card.is-link-target .canvas-card-inner {
  outline: 1px dashed rgba(212, 190, 136, 0.5);
  outline-offset: -1px;
}
.canvas-card.is-link-target::after {
  content: "＋参考";
  position: absolute;
  left: 50%;
  top: 10px;
  transform: translateX(-50%);
  z-index: 8;
  padding: 4px 10px;
  border-radius: 999px;
  background: rgba(14, 16, 22, 0.92);
  border: 1px solid rgba(212, 190, 136, 0.5);
  color: var(--gold-primary, #d4be88);
  font-size: calc(13px * var(--icon-inv, 1));
  line-height: 1.2;
  white-space: nowrap;
  pointer-events: none;
}

.canvas-card:hover .canvas-port::before,
.canvas-card.is-dragging .canvas-port::before,
.canvas-port:hover::before {
  transform: scale(1);
}

.canvas-port:hover::before {
  border-color: rgba(212, 190, 136, 1);
}

/* 定位按「元素边缘」算，不是中心：
   热区宽 24*inv，其中心要落在距卡片边缘 -gap 处，
   所以边缘在 -(gap + 12*inv)。纵向同理，高 32*inv，中心对准 --port-y。 */
.canvas-port.is-in {
  left: calc(0px - var(--port-gap, 14px) - 12px * var(--port-inv, 1));
  top: calc(var(--port-y, 304px) - 16px * var(--port-inv, 1));
}

.canvas-port.is-out {
  right: calc(0px - var(--port-gap, 14px) - 12px * var(--port-inv, 1));
  top: calc(var(--port-y, 304px) - 16px * var(--port-inv, 1));
}

/* 内嵌风格参考图废除：统一采用卡片外挂悬浮胶囊样式，不再在卡片右上角内嵌方图 */
.canvas-card-badge {
  display: none !important;
}

/* 生成中的卡片 */
.canvas-card.is-pending {
  border-style: dashed;
  border-color: rgba(212, 190, 136, 0.5);
}

.canvas-card-pending {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  color: rgba(212, 190, 136, 0.9);
  background: repeating-linear-gradient(
    45deg,
    rgba(212, 190, 136, 0.04) 0 12px,
    transparent 12px 24px
  );
}

/* ── HUD ────────────────────────────────────────────────── */
/* ── HUD 底栏（三岛独立浮岛架构） ───────────────────────── */
.canvas-hud {
  position: absolute;
  bottom: 24px;
  z-index: 30;
  display: flex;
  align-items: center;
  height: 38px;
  padding: 0 14px;
  box-sizing: border-box;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  background: rgba(14, 16, 22, 0.94);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.6);
}

/* 左胶囊：纯视口控制（缩放与全屏） */
/* 中间操作台胶囊：屏幕几何绝对居中！死钉在 50% 正中线，绝不偏位 */
/* 中间胶囊最左端：极显眼的实心金色圆角上传加号 */
/* 右胶囊：纯数字资产盘点（如 43 | 8 | 35） */
.canvas-hud-right {
  right: 24px;
  padding: 0 14px;
  cursor: pointer;
  user-select: none;
}
.canvas-hud-right:hover {
  border-color: rgba(212, 190, 136, 0.35);
}

/* 框选/平移当前模式高亮：香槟金微光底 */
.canvas-hud-stat {
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
  font-variant-numeric: tabular-nums;
  color: #a0a6b5;
  letter-spacing: 0.04em;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
}
.stat-digit-sep {
  opacity: 0.25;
  margin: 0 4px;
}

/* ═══════════════════════════════════════════════════════════════
   左侧竖向工具轨
   底栏原「左视口 / 中操作」两岛合并到这里，竖向排列、垂直居中。
   操作组在上（上传/框选/平移/后退/前进），视口组在下（缩放/全屏）；
   统计仍留在右下角，不占轨道。
   ═══════════════════════════════════════════════════════════════ */
.canvas-rail {
  position: absolute;
  left: 20px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 30;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 6px 4px;
  box-sizing: border-box;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  background: rgba(14, 16, 22, 0.94);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.6);
}

.canvas-rail-group {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}

/* 组内分隔（细）与组间分隔（香槟金，略长） */
.canvas-rail-sep {
  width: 20px;
  height: 1px;
  margin: 1px 0;
  background: rgba(255, 255, 255, 0.16);
}
.canvas-rail-divider {
  width: 24px;
  height: 1px;
  margin: 3px 0;
  background: rgba(212, 190, 136, 0.34);
}

.canvas-rail-btn {
  border: 0;
  background: transparent;
  color: #9aa0ae;
  width: 38px;
  height: 38px;
  border-radius: 10px;
  font-size: 18px;
  line-height: 1;
  font-family: inherit;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: color 0.15s ease, background 0.15s ease;
  flex: none;
}
.canvas-rail-btn:hover {
  color: #ffffff;
  background: rgba(255, 255, 255, 0.08);
}
.canvas-rail-btn[aria-disabled="true"],
.canvas-rail-btn.is-disabled {
  opacity: 0.35 !important;
  cursor: not-allowed !important;
  pointer-events: auto !important;   /* 仍接收点击，好弹功能提示气泡 */
}
.canvas-rail-btn svg { display: block; }

/* 框选/平移当前模式高亮：香槟金微光底 */
.canvas-rail-btn.is-on {
  color: var(--gold-primary, #d4be88);
  background: rgba(212, 190, 136, 0.16);
}
.canvas-rail-btn.is-on:hover {
  color: #f0e0b8;
  background: rgba(212, 190, 136, 0.24);
}

/* 上传：金色实心圆，轨上唯一主操作 */
.canvas-rail-primary {
  width: 38px;
  height: 38px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: #d4be88;
  color: #17140e;
  font-size: 20px;
  font-weight: 700;
  line-height: 1;
  font-family: inherit;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 4px 16px rgba(212, 190, 136, 0.4);
  transition: background 0.15s ease, box-shadow 0.15s ease, transform 0.1s ease;
  flex: none;
}
.canvas-rail-primary:hover {
  background: #f0e0b8;
  box-shadow: 0 6px 20px rgba(212, 190, 136, 0.55);
}
.canvas-rail-primary:active { transform: scale(0.95); }

.canvas-rail-zoom {
  border: 0;
  background: transparent;
  color: #e8e9ee;
  width: 38px;
  height: 26px;
  font-size: 11.5px;
  font-weight: 700;
  font-family: inherit;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  border-radius: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
}
.canvas-rail-zoom:hover { background: rgba(255, 255, 255, 0.08); }

/* 气泡朝右：竖轨在左边缘，气泡放上方会被屏幕裁切 */
.hud-tooltip.is-side-right {
  transform: translateY(-50%);
}
.hud-tooltip.is-side-right::after {
  left: -5px;
  top: var(--arrow-top, 50%);
  bottom: auto;
  transform: translateY(-50%);
  border-left: 0;
  border-right: 5px solid rgba(14, 16, 22, 0.94);
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
}
.hud-tooltip.is-side-right.is-fading {
  opacity: 0;
  transform: translateY(-50%) translateX(-4px);
}

/* ── HUD 气泡 Tooltip ───────────────────────────────────── */
.hud-tooltip {
  position: fixed;
  z-index: 100;
  padding: 6px 12px;
  border-radius: 6px;
  border: 1px solid rgba(212, 190, 136, 0.35);
  background: rgba(14, 16, 22, 0.94);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  color: #f5eedb;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.04em;
  white-space: nowrap;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6);
  pointer-events: none;
  transform: translateX(-50%) translateY(0);
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.hud-tooltip::after {
  content: "";
  position: absolute;
  left: var(--arrow-left, 50%);
  bottom: -5px;
  transform: translateX(-50%);
  width: 0;
  height: 0;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 5px solid rgba(14, 16, 22, 0.94);
}
.hud-tooltip[hidden] {
  display: none !important;
}
.hud-tooltip.is-fading {
  opacity: 0;
  transform: translateX(-50%) translateY(4px);
}

/* 触屏首次提示：悬浮在 HUD 上方，一行可关闭。 */
.canvas-touch-hint {
  position: absolute;
  left: 50%;
  bottom: calc(76px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  z-index: 45;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: min(92vw, 560px);
  padding: 10px 12px 10px 14px;
  border-radius: 10px;
  border: 1px solid rgba(212, 190, 136, 0.35);
  background: rgba(14, 16, 22, 0.94);
  color: #d8cdba;
  font-size: 13px;
  line-height: 1.5;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.6);
  touch-action: manipulation;
}
.canvas-touch-hint[hidden] { display: none !important; }
.canvas-touch-hint button {
  flex: none;
  width: 28px;
  height: 28px;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  color: #d8cdba;
  font-size: 14px;
  cursor: pointer;
}

/* 参考图首次提示：与触屏提示同一位置，二者只显示一个。 */
.canvas-ref-hint {
  position: absolute;
  left: 50%;
  bottom: calc(76px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  z-index: 44;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: min(92vw, 640px);
  padding: 10px 12px 10px 14px;
  border-radius: 10px;
  border: 1px solid rgba(212, 190, 136, 0.35);
  background: rgba(14, 16, 22, 0.94);
  color: #d8cdba;
  font-size: 13px;
  line-height: 1.5;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.6);
  touch-action: manipulation;
}
.canvas-ref-hint[hidden] { display: none !important; }
.canvas-ref-hint button {
  flex: none;
  height: 30px;
  padding: 0 12px;
  border: 1px solid rgba(212, 190, 136, 0.35);
  border-radius: 8px;
  background: rgba(212, 190, 136, 0.12);
  color: var(--gold-primary, #d4be88);
  font-size: 13px;
  cursor: pointer;
}

/* 触屏端口热区：可见圆点仍 16px，热区放大到 44px，手指点得中。 */
@media (hover: none), (pointer: coarse) {
  .canvas-port {
    width: calc(44px * var(--port-inv, 1));
    height: calc(44px * var(--port-inv, 1));
  }
  .canvas-port.is-in {
    left: calc(0px - var(--port-gap, 14px) - 22px * var(--port-inv, 1));
    top: calc(var(--port-y, 304px) - 22px * var(--port-inv, 1));
  }
  .canvas-port.is-out {
    right: calc(0px - var(--port-gap, 14px) - 22px * var(--port-inv, 1));
    top: calc(var(--port-y, 304px) - 22px * var(--port-inv, 1));
  }
  .canvas-port::before {
    width: calc(16px * var(--port-inv, 1));
    height: calc(16px * var(--port-inv, 1));
    margin-left: calc(-8px * var(--port-inv, 1));
    margin-top: calc(-8px * var(--port-inv, 1));
  }
}

/* ── HUD 响应式 ─────────────────────────────────────────── */
/* 触屏与窄屏把按钮放大到舒适指头尺寸；左侧竖轨同理，整条等比放大。 */
@media (max-width: 899px), (hover: none) and (pointer: coarse) {
  /* 顶栏不在这里换样式：它按同一个比例因子缩到 60px 下限（见文件顶部）。
     这里只处理 HUD 与左侧竖轨的窄屏形态。 */
  .canvas-hud {
    height: 52px;
    bottom: calc(10px + env(safe-area-inset-bottom, 0px));
  }
  .canvas-rail {
    left: 12px;
    gap: 5px;
    padding: 6px 4px;
  }
  .canvas-rail-btn,
  .canvas-rail-primary {
    width: 40px;
    height: 40px;
    font-size: 19px;
  }
  .canvas-rail-zoom {
    width: 40px;
    height: 28px;
    font-size: 12px;
  }
  .canvas-rail-sep { width: 22px; }
  .canvas-rail-divider { width: 26px; }
  .canvas-hud-right {
    right: 10px;
    padding: 0 10px;
  }
  .canvas-hud-stat { font-size: 13px; }
}

/* ── 空态（纯粹诗意双行，精准居中于画布） ───────────────── */
.canvas-empty {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: min(540px, 92vw);
  text-align: center;
  z-index: 20;
  pointer-events: none;
}

.canvas-empty-title {
  margin: 0 0 10px;
  font-size: 20px;
  font-weight: 600;
  color: #ffffff;
  letter-spacing: 0.04em;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
}

.canvas-empty-sub {
  margin: 0;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.5;
  color: #8b91a0;
  letter-spacing: 0.02em;
}

@media (max-width: 899px), (hover: none) and (pointer: coarse) {
  .canvas-empty-title { font-size: 18px; }
  .canvas-empty-sub { font-size: 13px; }
}

/* 连线在卡片下层：连线穿过卡片时不会挡住卡片的交互 */
.canvas-nodes { z-index: 2; }
.canvas-edges { z-index: 1; }

/* 画布顶栏：内容与其它页面一致，但画布不套 2560×1280 的舞台缩放，
   窄窗口下品牌名会被挤断行。这里只固定品牌块不被压缩，其余照常自适应。 */
.canvas-page .shared-brand-copy { white-space: nowrap; }
.canvas-page .shared-header-left { min-width: 0; flex-wrap: nowrap; }
.canvas-page .shared-nav { flex-wrap: nowrap; }

/* 选中的卡片：屏幕恒定最小可见厚度补偿与高定金色微光（10% 远景下边界依然清晰醒目） */
.canvas-card.is-selected .canvas-card-inner {
  outline: calc(2.5px * var(--select-inv, 1)) solid var(--accent, #d4be88) !important;
  outline-offset: calc(2px * var(--select-inv, 1)) !important;
  box-shadow: 0 0 calc(24px * var(--select-inv, 1)) rgba(212, 190, 136, 0.45) !important;
}

html[data-theme="light"] .canvas-card.is-selected .canvas-card-inner {
  outline: calc(2.5px * var(--select-inv, 1)) solid #9b7c38 !important;
  outline-offset: calc(2px * var(--select-inv, 1)) !important;
  box-shadow: 0 0 calc(20px * var(--select-inv, 1)) rgba(155, 124, 56, 0.4) !important;
}


/* ── 生成节点（P10 打样）：信息栏 + 出图框 + 覆盖式参数板块 ──────────────
   节点自身的占位尺寸就是那个出图框（260×175）：信息栏悬在它上方、参数板块垂在它下方，
   两者都不参与排布。理由是展开变高若参与排布，要么盖住下方的节点，要么自动推开——
   而自动推开等于篡改用户手动摆好的位置。 */
.canvas-card.is-gen { cursor: grab; }
/* 展开时压在其他卡片之上：浮层必然与相邻卡片重叠，不抬起来就会被后画的卡盖住 */
.canvas-card.is-gen.is-open { z-index: 20; }

/* 信息栏（P25）：图框外上方两排，第一排名字、第二排参数。
   默认收起，悬停图片主体或选中才出现——
   鼠标悬停到上方命名信息区不主动触发显示，避免掠过卡片上方虚空误触。
   触屏没有 hover，所以选中态必须同样能展开（iPad 上靠的就是这条）。 */
.canvas-card-info {
  position: absolute;
  left: 50%;
  bottom: 100%;
  transform: translateX(-50%);
  margin-bottom: 8px;
  width: 100%;
  max-width: 100%;
  box-sizing: border-box;
  padding: 0 8px;
  white-space: nowrap;
  overflow: hidden;
  text-align: center;
  pointer-events: none;
  visibility: hidden;
  display: flex;
  flex-direction: column;
  gap: 3px;
  opacity: 0;
  transition: opacity 0.14s ease, visibility 0.14s ease;
}
.canvas-card:hover .canvas-card-info,
.canvas-card:has(.canvas-card-inner:hover, .canvas-card-actions:hover) .canvas-card-info,
.canvas-card.is-selected .canvas-card-info,
.canvas-card.is-open .canvas-card-info {
  opacity: 1;
  visibility: visible;
}
/* 名字那一排：严格收拢至 T3: 20px，字重 500，与 1080px 大画幅相得益彰 */
.canvas-card-name {
  font-size: 20px;
  line-height: 1.4;
  font-weight: 500;
  color: rgba(213, 220, 232, 0.95);
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: auto;
  cursor: text;
  border-radius: 4px;
  padding: 2px 8px;
}
.canvas-card-name:hover {
  background: rgba(255, 255, 255, 0.09);
}
.canvas-card-meta {
  font-size: 17px;
  line-height: 1.4;
  color: rgba(154, 163, 178, 0.9);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}
.canvas-card:hover .canvas-card-meta,
.canvas-card.is-selected .canvas-card-meta,
.canvas-card.is-open .canvas-card-meta {
  pointer-events: auto;
  cursor: help;
}
/* 就地改名的输入框：长在名字原来的位置上，字号与字色跟上排一致，
   换行高与背景，不要看起来像换了一行表单。 */
.inline-edit-input {
  width: 100%;
  min-width: 80px;
  box-sizing: border-box;
  padding: 0 3px;
  border: 1px solid rgba(212, 190, 136, 0.7);
  border-radius: 4px;
  background: rgba(16, 20, 28, 0.96);
  color: #e7eaf2;
  font: inherit;
  line-height: 1.5;
  text-align: center;
  outline: none;
}

/* 框外右侧操作列：涂抹/打标/参考微标紧贴卡片右缘竖排，顶部与右上角三剑客（看/存/删）
   同高对齐，连成一条右手操作带——对这张卡能做的一切都集中在右侧。
   卡片下方完全让给参数区，参数面板贴沿直挂不再脱节；
   单列纵队：涂抹最上，依次打标/参考，一人一行；纵队整体压不到 y=304 的连线端口。 */
.canvas-card-actions {
  position: absolute;
  left: calc(100% + 6px);
  top: 8px;
  flex-direction: column;
  opacity: 0;
  /* 出图页同款右下角微晶胶囊 */
  right: 14px;
  bottom: 14px;
  left: auto;
  top: auto;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  z-index: 15;
  pointer-events: auto;
  opacity: 1 !important;
  transition: opacity 0.14s ease;
}
/* 悬停桥：向左延伸 10px 消除 6px 横向间隙死区，从卡片右侧滑入保持悬停 */
.canvas-card-actions::before {
  content: "";
  position: absolute;
  left: -10px;
  top: 0;
  bottom: 0;
  width: 10px;
  background: transparent;
  pointer-events: auto;
}
.canvas-card:hover .canvas-card-actions,
.canvas-card:has(.canvas-card-inner:hover, .canvas-card-actions:hover) .canvas-card-actions,
.canvas-card.is-selected .canvas-card-actions,
.canvas-card.is-open .canvas-card-actions {
  opacity: 1;
}
/* P32：有激活标注（涂抹、打标）或参考连线的小片常驻可见，
   扫视画布即知哪张卡带标注，不再必须 hover 或选中才看得到 */
.canvas-card:not(.is-multi) .canvas-card-actions.has-active-chips,
.canvas-card:not(.is-multi) .canvas-card-actions:has(.has-data, .is-active) {
  opacity: 1;
}
/* 锁定物理槽位：在未 hover 且未选中时，未激活的普通按钮保持物理占位但完全透明，
   涂抹与打标严格停留在本位，绝对不向左跳移；hover 时平滑淡入，零位移抖动 */
.canvas-card:not(:hover):not(.is-selected):not(.is-open) .canvas-card-action,
.canvas-card:not(:hover):not(.is-selected):not(.is-open) .canvas-corner-chip:not(.has-data):not(.is-active) {
  opacity: 0 !important;
  pointer-events: none !important;
  border-color: transparent !important;
  background: transparent !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}
/* 多选（P29）：选中数 > 1 时收起按钮组与两排信息。
   一框十张时每张都挂着一条按钮和两行字，画布上会铺满文字的毛边，
   而这时候用户要看的恰恰是「我框到了哪些」。单选时仍照常展开——
   单选是「我要操作这一张」，那是需要按钮的时候。 */
.canvas-card.is-multi .canvas-card-actions,
.canvas-card.is-multi .canvas-card-info {
  opacity: 0 !important;
  pointer-events: none;
}
/* 右侧操作列按钮：40px 高、T4 17px 字号，iPad 触屏盲戳不误触 */
.canvas-card-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 40px;
  padding: 0 16px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: rgba(20, 24, 32, 0.85);
  color: var(--muted);
  font-size: 17px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0;
  white-space: nowrap;
  box-sizing: border-box;
  backdrop-filter: blur(8px);
  cursor: pointer;
  transition: all 0.16s ease;
}
.canvas-card-action:hover {
  border-color: var(--muted);
  color: var(--ink);
  background: rgba(30, 36, 48, 0.95);
}
.canvas-card-action[hidden] {
  display: none !important;
}
/* 参考小片：一枚一行竖排在参考胶囊正下方，有几个编号显示几个，编号即连线顺序。
   挂得特别多时在 154px 高度内自动折出第二小列，绝不下行压到 y=304 的连线端口。 */
.canvas-ref-chips {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  align-content: flex-start;
  flex-wrap: wrap;
  gap: 6px;
  max-height: 154px;
}
.canvas-ref-chips[hidden] {
  display: none !important;
}
.canvas-ref-chip {
  width: 22px;
  height: 22px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid rgba(212, 190, 136, 0.55);
  background: rgba(212, 190, 136, 0.16);
  color: var(--gold-primary);
  font-size: 10.5px;
  line-height: 20px;
  text-align: center;
  cursor: pointer;
  transition: all 0.16s ease;
}
.canvas-ref-chip:hover {
  background: rgba(212, 190, 136, 0.3);
  border-color: var(--gold-primary);
}
.canvas-ref-chip.is-active {
  font-weight: 700;
}

/* 图1完整底栏：面板宽 880px，几何中心严格对齐 1080px 卡片的垂直中线 */
.gen-panel {
  position: absolute;
  top: 100%;
  left: calc(50% - 980px);
  width: 1960px;
  /* 880px 居中微晶创作舱 */
  left: calc(50% - 440px);
  width: 880px;
  height: auto;
  /* 贴沿 10px 直挂，消灭脱节感 */
  margin-top: 10px;
  padding: 0;
  border: none !important;
  border-radius: 0 !important;
  background: transparent !important;
  box-shadow: none !important;
  backdrop-filter: none !important;
  cursor: default;
  user-select: text;
  z-index: 100;
  pointer-events: auto;
}
.gen-panel[hidden] { display: none !important; }

/* ═══════════════════════════════════════════════════════════════════
   底部参数坞（屏幕空间）
   卡片在 #canvasViewport 的 scale() 里，参数舱若跟着卡片走，缩到 60%
   时 48px 药丸只剩 29px；反向放大到 1.25 倍就会撑破 1080 宽的卡片。
   所以参数舱改挂屏幕空间，任何倍率下都是 1:1 真实尺寸。
   ═══════════════════════════════════════════════════════════════════ */
.canvas-param-dock {
  position: absolute;
  left: 100px;      /* 让开左侧竖向工具轨 */
  right: 150px;     /* 让开右下角统计 */
  bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  margin: 0 auto;   /* 在「竖轨～统计」这条带子里居中 */
  max-width: 820px;
  width: auto;
  max-height: calc(100vh - 160px);
  padding: 0 !important;
  z-index: 45;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  border: none !important;
  border-radius: 0 !important;
  background: transparent !important;
  box-shadow: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  animation: canvas-dock-in 0.2s cubic-bezier(0.16, 1, 0.3, 1);
  pointer-events: none;
}
.canvas-param-dock[hidden] { display: none !important; }
@keyframes canvas-dock-in {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

.canvas-dock-slot {
  min-width: 0;
  pointer-events: auto !important;
}

/* 坞里的参数舱回到常规文档流：去掉卡片内的绝对定位 */
.canvas-param-dock .gen-panel {
  position: static !important;
  left: auto !important;
  top: auto !important;
  width: 100% !important;
  margin: 0 !important;
  transform: none !important;
  pointer-events: auto !important;
}
.canvas-param-dock .gen-panel .dock.canvas-dock-bar {
  width: 100% !important;
  margin-left: 0 !important;
  margin-right: 0 !important;
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
}
.canvas-param-dock .gen-panel .cockpit-outer-header {
  width: 100% !important;
  margin-left: 0 !important;
  margin-right: 0 !important;
}
.canvas-param-dock .gen-panel .jimeng-cockpit {
  width: 100% !important;
  margin-left: 0 !important;
  margin-right: 0 !important;
}

html[data-theme="light"] .canvas-param-dock {
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
}

/* 卡片展开参数面板时，父级卡片同步提升至顶层 z-index: 100，绝不被任何其他卡片遮挡 */
.canvas-card.is-open,
.canvas-card:has(.gen-panel:not([hidden])) {
  z-index: 100 !important;
}

/* 下拉菜单提升至 z-index: 200，位置由 canvas.js 按触发按钮屏幕矩形动态计算 */
.canvas-page .v3-menu.canvas-v3-menu {
  z-index: 200 !important;
  bottom: auto;
}

/* 面板顶部那一行名字与卡片上方名字重复，按用户要求彻底隐藏移除，避免视觉冗余打架 */
.gen-panel-head {
  display: none !important;
}

/* 拖拽外部图片到画布舞台的视觉反馈 */
.canvas-stage.is-drag-over {
  outline: 2px dashed var(--gold-primary, #d4be88) !important;
  outline-offset: -12px;
}

.gen-panel .cockpit-action-right #runBtn,
.gen-panel .canvas-run-btn {
  width: 34px !important;
  height: 34px !important;
  min-width: 34px !important;
}
