/** Shopify CDN: Minification failed

Line 1100:66 Unexpected "358"

**/
/* hd-canvas.css — 设计台 / 设计板 全部样式
   2026-09-13 从 sections/design-canvas.liquid 的 <style> 块整体外置（38510 B）。
   动机：canvas 已达 259597 / 262144 B（余 2547），外置后回收 ~37.9KB。
   注意：:root 的 --pf-* 变量也在本文件内。 */
/* === Printful-style Design Maker === */
:root{--pf-green:#00a86b;--pf-green-hover:#008f5a;--pf-bg:rgba(255,255,255,0.92);--pf-white:#fff;--pf-border:#e0e0e0;--pf-text:#333;--pf-muted:#666;--pf-red:#e74c3c;--pf-btn-busy:#9ca3af}

/* 2026-09-10: Affinity 外观 — 灰阶描边图标按钮（强调色只留给主 CTA 与选中态） */
/* 2026-09-11: 顶栏按键统一为「主题官方按键家族」。
   .hd-back（theme.css）是主档：紫粉渐变 / 36px / 圆角 8 / 字号 0.85rem。
   撤销·重做改为同族「次级档」—— 盒模型与主档对齐（36px / 圆角 8），白底 + 发丝描边 + 主题紫字形：
   一眼看出同一套按键，但主次分明（高频次级操作不和主 CTA 抢视觉）。字号见下条（符号字形需放大）。 */
/* 2026-09-11: 箭头字形单独放大到 1rem —— ↶ ↷ 是符号字形，与 .hd-back 文字同为 0.85rem 时
   视觉上会小一圈（符号字形自带内边距），故字形比文字按钮大一档。
   2026-09-19: 图形由字符换为内联 SVG（见 design-board.liquid）⇒ 尺寸改由 font-size 驱动
   （图标 = 1em）。盒模型**回到 36×36**、font-size 1.25rem（图标 = 20px）——
   当日曾随「顶栏整行统一」放到 44px / 1.75rem，用户报「画板返回键比其它页面大」后整行回退，
   此处与全站 .hd-back 主档严格对齐。
   原「手机端 44px 覆盖」已不存在（见下文触控档注），此处即唯一规格。 */
.pfm-icon-btn{width:36px;height:36px;display:flex;align-items:center;justify-content:center;background:#fff;border:1px solid #E4E4E7;border-radius:var(--pf-r-md);cursor:pointer;font-size:1.25rem;color:#7C3AED;padding:0;transition:background var(--pf-dur-1),border-color var(--pf-dur-1),color var(--pf-dur-1)}
.pfm-icon-btn:hover{background:#F3E8FF;border-color:#D8B4FE}
.pfm-icon-btn:active{background:#EDE9FE}
.pfm-btn{height:36px;padding:0 16px;border-radius:var(--pf-r-md);border:1px solid var(--pf-border);background:var(--pf-white);cursor:pointer;font-size:0.85rem;font-weight:600;display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box}
.pfm-btn-green{background:linear-gradient(135deg,#7C3AED,#EC4899);color:#fff;border-color:transparent}
.pfm-btn-green:hover{background:linear-gradient(135deg,#6D28D9,#DB2777)}

/* Body */
.pfm-body{display:flex;flex:1}
.pfm-canvas-area{flex:1;position:relative;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;padding:16px 20px 20px;background:transparent;overflow:auto} /* 2026-08-30: 纵向布局 — 画布居中 */
/* 2026-09-10: flex:0 0 auto —— .pfm-canvas-area 是 column flex 容器，inner 作为其子项默认 flex-shrink:1
   会把设进去的正方形高度压扁（358→231 / 864→433），导致产品图 background-size:cover 上下被裁。必须禁止压缩。 */
.pfm-canvas-inner{position:relative;display:block;vertical-align:top;flex:0 0 auto;background:#fff;background-size:cover;background-position:center;border-radius:var(--pf-r-md);overflow:hidden;max-width:none;box-sizing:border-box;margin:0 auto;touch-action:pan-y} /* 2026-08-29: overflow visible→hidden（缩放裁剪框内）；2026-09-03: touch-action none→pan-y（方案A：100% 未放大时单指上下滑=页面滚动，不再被拦截成框选；放大后 JS 加 .pfm-zoom 切回 none） */
/* 2026-08-30: 内部缩放层 — 白框不动，产品图+ghost+canvas 在此层内一起缩放/平移 */
.pfm-zoom-layer{position:absolute;top:0;left:0;width:100%;height:100%;transform-origin:center center;background-repeat:no-repeat;background-position:center;background-size:100% 100%;border-radius:var(--pf-r-md)}
@media(min-width:769px){.pfm-canvas-inner{box-shadow:0 2px 10px rgba(0,0,0,0.06)}}
.pfm-product-bg{position:absolute;top:0;left:0;width:100%;height:100%;z-index:1;background-repeat:no-repeat;background-position:center;background-size:cover;pointer-events:none;border-radius:var(--pf-r-md)}
#pfmCanvas{position:absolute;top:0;left:0;z-index:10;pointer-events:auto}
/* 2026-09-13: 浮动删除钮（.pfm-del-btn / positionDelBtn）已下线 —— 与 Actions 里的 Delete
   功能重复，且在手机上会被推到画布边缘。删除统一走 Actions → Delete。 */
.pfm-canvas-inner .canvas-container{position:absolute !important;z-index:10 !important}
/* 2026-09-01: 显式覆盖 canvas-container/canvas 的 touch-action（父级 none 靠继承+hit-test 链生效，此处直接落到触摸目标，防 fabric 内部或 UA 样式覆盖导致真机捏合失效） */
/* 2026-09-03: 方案A——默认 pan-y（100% 未放大：画布上单指上下滑=页面滚动，不再拖出框选）；
   放大后 JS 给 inner 加 .pfm-zoom → touch-action:none：单指空白拖动=平移画布、双指=捏合缩放 */
.pfm-canvas-inner,.pfm-canvas-inner .canvas-container,.pfm-canvas-inner canvas{touch-action:pan-y !important}
.pfm-canvas-inner.pfm-zoom,.pfm-canvas-inner.pfm-zoom .canvas-container,.pfm-canvas-inner.pfm-zoom canvas{touch-action:none !important}
.pfm-canvas-inner .canvas-container .upper-canvas{pointer-events:auto !important}
.pfm-canvas-inner .canvas-container .lower-canvas{pointer-events:none !important}
/* Double-buffer overlay: shows a cached bitmap of the target face instantly while
   the live fabric objects are restored in the background (placement-switch speed). */
.pfm-canvas-thumb{position:absolute;top:0;left:0;width:100%;height:100%;z-index:11;display:none;background-repeat:no-repeat;background-position:center;background-size:100% 100%;pointer-events:none;border-radius:var(--pf-r-md)}

/* Loading overlay */
.pfm-loading{position:absolute;inset:0;display:none;align-items:center;justify-content:center;background:rgba(255,255,255,0.85);z-index:25;border-radius:var(--pf-r-md)}
.pfm-loading-text{font-size:0.95rem;color:var(--pf-muted);font-weight:600}
.pfm-loading.active{display:flex}

/* Print area overflow warning */
.pfm-print-warning{position:absolute;bottom:16px;left:50%;transform:translateX(-50%);padding:8px 18px;background:#e74c3c;color:#fff;border-radius:var(--pf-r-sm);font-size:0.8rem;font-weight:600;z-index:20;opacity:0;pointer-events:none;transition:opacity var(--pf-dur-2);white-space:nowrap}
.pfm-print-warning.show{opacity:1}

/* Fixed object actions toolbar (now in sidebar) */
.pfm-actions-section{margin-bottom:12px}
/* 2026-09-13: 第一行 66px → 44px（原 66 = 按钮44 + padding10×2 + border1×2）。
   做法：容器上下 padding 归零、**border 改由 box-shadow 画**（外描边不占布局高度）
   → 卡片高正好 = 按钮 44px，同时保住「白卡片 + 发丝边 + 圆角 + 柔和阴影」的观感。 */
.pfm-float-toolbar{position:static;display:flex;gap:8px;padding:0 10px;background:var(--pf-white);border:0;border-radius:var(--pf-r-lg);box-shadow:0 0 0 1px var(--pf-border),0 2px 10px rgba(0,0,0,0.08);width:100%;box-sizing:border-box}
.pfm-fbtn{flex:1;min-width:48px;min-height:44px;display:flex;flex-direction:column;align-items:center;justify-content:center;background:transparent;border:none;border-radius:var(--pf-r-md);cursor:pointer;padding:6px 4px;gap:3px}
.pfm-fbtn__icon{font-size:1.1rem;line-height:1}
.pfm-fbtn__label{font-size:0.65rem;color:var(--pf-muted);font-weight:600;white-space:nowrap}
.pfm-fbtn:hover{background:#f5f5f5}
.pfm-fbtn:disabled{opacity:0.4;cursor:not-allowed}
/* 2026-09-19: 操作条图标改 emoji 后**不再跟随按钮 color**（emoji 自带颜色）⇒
   原本挂在按钮 style="color:#e00" 上的删除红色警示，改挂到**文字标签**上。
   注：禁用态走 :disabled 的 opacity:.4，对 emoji 依然有效。 */
.pfm-fbtn[data-action="del"] .pfm-fbtn__label{color:#DC2626}
/* 2026-09-16: 吸管拾取态（hd-pick.js 给 #pfmPickBtn 加 .on） */
.pfm-fbtn.on{background:#F3E8FF}
.pfm-fbtn.on .pfm-fbtn__label{color:#7C3AED}
/* 2026-09-16: 跨面复制（#pfmCopyFaceSection） */
/* 2026-09-16: 变换数值行（#pfmTransformSection）—— 全站唯一可键入精确值的入口 */
.pfm-xf-row{display:flex;align-items:center;gap:6px;margin-bottom:8px}
.pfm-xf-row:last-of-type{margin-bottom:0}
.pfm-xf-lab{flex:0 0 auto;font-size:.72rem;color:#5F5E5A}
.pfm-xf-num{flex:1;min-width:0;width:100%;height:30px;padding:0 6px;box-sizing:border-box;background:#fff;border:1px solid #E4E4E7;border-radius:var(--pf-r-md);font-size:.78rem;color:#111;font-family:inherit;font-variant-numeric:tabular-nums}
.pfm-xf-num:focus{outline:2px solid #C4B5FD;outline-offset:1px;border-color:#C4B5FD}
.pfm-xf-hint{margin:8px 0 0;font-size:.68rem;color:#A1A1AA}
/* 2026-09-20：Size 滑杆右侧的「倍数」读数 —— 等比映射下 v 值失去直觉，必须有数字参照 */
.pfm-xf-mult{flex:0 0 auto;min-width:46px;text-align:right;font-size:.72rem;color:#5F5E5A;font-variant-numeric:tabular-nums}
/* 2026-09-20: 角度快捷胶囊（Size 区块）—— 视觉复用 .pfm-chip/.pfm-chip.sel，这里只补 button reset 与换行。
   🔴 规则一律限定 #pfmTransformSection（.pfm-chip 已被 #pfmPlacements/#pfmStageFaces/#pfmCopyFaceList 复用，别做全局改动）。 */
#pfmTransformSection .pfm-ang-row{flex-wrap:wrap}
#pfmTransformSection .pfm-ang-chip{font-family:inherit;padding:5px 11px;line-height:1.2}
.pfm-copyface-btn{width:100%}
/* 2026-09-19: Copy to face 嵌在 Size 区块（#pfmTransformSection）里 ——
   它是该区块的最后一个子元素 ⇒ `.pfm-section:last-child` 会去掉下边框/下边距，
   这里自己补一条上分隔线 + 上间距，跟 W/H/∠ 数值行分开。 */
.pfm-copyface-section{margin-bottom:12px;padding-bottom:12px;border-bottom:1px solid #EFEFF1}
.pfm-copyface-list{margin-top:8px}
.pfm-copyface-none{font-size:0.75rem;color:var(--pf-muted)}
/* 2026-09-20: Copy to face 常驻 ⇒ 不可用时靠置灰表达（取值与 .pfm-fbtn:disabled 一致） */
.pfm-copyface-btn:disabled{opacity:.4;cursor:not-allowed}
.pfm-chip-off{opacity:.4;cursor:not-allowed}

/* 2026-09-10: 三栏骨架（Affinity 式）— 左工具条 / 中画布 / 右属性面板 */
.pfm-toolbar{width:60px;min-width:60px;background:#F7F7F8;border-right:1px solid #E4E4E7;display:flex;flex-direction:column;align-items:center;gap:6px;padding:12px 0;overflow-y:auto}
.pfm-toolbar .pfm-stab{width:48px;height:48px;flex:0 0 auto;padding:4px 0;border-radius:var(--pf-r-md);gap:2px;position:relative}
.pfm-toolbar .pfm-stab .pfm-stab-ico{font-size:1.15rem}
.pfm-toolbar .pfm-stab .pfm-stab-lbl{font-size:0.58rem;line-height:1}
/* 2026-11-02: Layers 入口数字角标 —— 当前印面对象数（0 时 JS 隐藏）。 */
.pfm-toolbar .pfm-stab .pfm-stab-badge{position:absolute;top:2px;right:2px;min-width:16px;height:16px;padding:0 4px;border-radius:999px;background:#7C3AED;color:#fff;font-size:0.58rem;font-weight:800;line-height:16px;text-align:center;box-sizing:border-box;pointer-events:none}
/* 2026-09-10: Affinity 选中态 — 柔和紫底 + 左缘 3px 高亮条（替代原来的白底+投影） */
.pfm-toolbar .pfm-stab.sel{background:#EDE9FE;color:#6D28D9;box-shadow:none}
.pfm-toolbar .pfm-stab.sel::before{content:'';position:absolute;left:-6px;top:50%;transform:translateY(-50%);width:3px;height:26px;border-radius:0 3px 3px 0;background:#7C3AED}
/* 右属性面板（替代原 .pfm-sidebar 位置） */
/* 2026-09-18: 右属性面板加宽 20%（300→360，用户要求）—— COLOR/OUTLINE/No 那行原来太挤。
   本条在 @media(min-width:769px) 内 ⇒ 只影响桌面端；手机/平板竖屏走下面的 width:100%。 */
.pfm-props{width:360px;min-width:360px;background:#FAFAFB;border-left:1px solid #E4E4E7;overflow-y:auto;padding:14px}
/* 2026-09-10: 手机端属性面板 → 底部抽屉的顶部条（仅 ≤768px 显示；桌面端隐藏） */
/* 2026-09-11: 下边距 10→18px —— 抽屉内首个板块（PLACEMENT）与分割线拉开呼吸，不再贴在一起。 */
/* 2026-09-12: 灰把手 grip 移除，收起键改「独占正中」；同日「样式不协调」→ 胶囊 88×44 + 顶部条 56px。
   三轮再调：用户「行高太高、按钮太大」→ 顶部条 34px（总 35）/ 胶囊 60×26（不再与顶栏底栏等高）。
   四轮再调：用户「偏低」→ 字形 ⌄ 换内联 SVG chevron（墨迹居中）；「还要高一点」→ padding 4px→1px 14px 7px（胶囊上移 3.5px）。 */
/* 2026-09-18: sticky 条独立合成层 —— 手机端滚动时内容会从把手下「穿」过去（重绘中间帧，ArkWeb 更明显）。同 .pfm-canvas-inner.pfm-dragging #pfmCanvas 的手法。 */
.pfm-props-handle{display:none;position:sticky;top:0;z-index:10;background:#FAFAFB;min-height:34px;margin:-14px -14px 18px;padding:1px 14px 7px;border-bottom:1px solid #EFEFF1;align-items:center;justify-content:center;transform:translateZ(0)}
/* 2026-09-11: 收起键「官方按键家族·次级档」—— 白底 + 发丝描边 + 主题紫字形，与顶栏 ↶ ↷ 同族。 */
/* 2026-09-12: 收起键 = 60×26 扁胶囊（圆角 999px）。高 26 < 44pt 触控线，但横向 60px、且为低频「收起」操作。
   图标改用内联 SVG 背景（background-position:center）—— 原 `⌄`(U+2304) 是字符，字形落在基线下方（实测 asc2/desc3），
   在胶囊内低约 5.8px，且不同设备字体差异大。改 SVG 后几何绝对居中。 */
.pfm-props-collapse{width:60px;height:26px;border:1px solid #E4E4E7;background-color:#fff;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.6 4.35L6 7.65l3.4-3.3' fill='none' stroke='%237C3AED' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:center;background-size:12px 12px;line-height:1;cursor:pointer;border-radius:var(--pf-r-pill);padding:0;display:flex;align-items:center;justify-content:center;transition:background-color var(--pf-dur-1),border-color var(--pf-dur-1)}
.pfm-props-collapse:active{background:#EDE9FE;border-color:#D8B4FE}

/* 2026-09-10: 设计板全屏层（编辑态，替代原 Sketchpad 弹窗） */
/* 2026-09-10: Affinity 外观 — 画布区用浅灰底，白框画布读作"纸张"浮在灰底上 */
.pfm-board{position:fixed;inset:0;z-index:300;background:#F4F4F5;display:flex;flex-direction:column;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;color:var(--pf-text)}
html.pfm-board-open,html.pfm-board-open body{overflow:hidden}
/* 2026-09-10: Affinity 上下文栏 — 左：Close · 撤销 · 重做；中：标题；右：Apply */
.pfm-boardbar{display:flex;align-items:center;gap:8px;padding:8px 14px;border-bottom:1px solid #E4E4E7;background:#F7F7F8;z-index:10;flex:0 0 auto}
.pfm-boardbar-sep{width:1px;height:20px;background:#E4E4E7;margin:0 4px;flex:0 0 auto}
/* 2026-09-11: 设计板顶栏的退出键改用全站 .hd-back 外观（Close→Back）。
   .hd-back 自带 height（当时 36px，现 var(--pf-h-sm)=40px）+ margin:0 0 16px（它原本是页面级按钮）—— 放进 34px 的顶栏 flex 行里
   会因下边距而上偏 9px，所以这里归零并压到 34px 与 ↶↷ 齐平。 */
/* 2026-09-11: 与 .hd-back 原规格完全对齐（36px / 圆角 8 / 字号 0.85rem），
   仅归零它自带的 18px 下边距（放在 36px 顶栏 flex 行内会上偏 9px）。
   2026-09-19: 曾随「顶栏整行统一 44px」一起升到 44 ⇒ 比站内其它返回键大 8px
   （用户报「设计台的返回按钮比其他页面大」），当日回退为 36px，与全站严格一致。
   2026-11-02（用户）：全站主档 .hd-back 早已改成 var(--pf-h-sm)=40px，本行写死的 36px 没跟改
   ⇒ 反过来比站内其它返回键**小 4px**。改为 `height:var(--pf-h-sm)` 跟随主档，
   以后主档再变设计板自动同步，不再脱节。仅保留 margin:0（顶栏 flex 行内不需要下边距）。 */
.pfm-boardbar .hd-back{height:var(--pf-h-sm);padding:0 16px;margin:0;font-size:0.85rem;border-radius:var(--pf-r-md)}
/* 2026-09-19: 此处曾有一条「本作用域把 Apply 拉到 44px」的高度覆盖（顶栏整行统一时加的）——
   当日随 ← Back 一起回退删除 ⇒ Apply 回到全局主档（现为 var(--pf-h-sm)=40px），四个按钮重新齐平。
   ⚠️ 全局主档已有 height:var(--pf-h-sm)，**不要再给本作用域单独加高度覆盖**，否则又会与 .hd-back 分家。 */
/* 旧类名保留（仅作回滚兼容，已无引用） */
.pfm-boardbar-close{height:34px;padding:0 12px;border-radius:var(--pf-r-sm);border:1px solid #E4E4E7;background:#fff;cursor:pointer;font-size:0.82rem;font-weight:600;color:#3F3F46}
.pfm-boardbar-close:hover{background:#F4F4F5;border-color:#D4D4D8}
.pfm-boardbar-title{flex:1;text-align:center;font-size:0.72rem;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#A1A1AA;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pfm-board .pfm-body{flex:1;min-height:0}

/* 2026-09-10: 展示态（设计台主页 — 只看不编辑） */
.pfm-stage{flex:1;display:flex;gap:28px;padding:28px 24px;align-items:flex-start;justify-content:flex-start;flex-wrap:wrap}
/* 2026-10-29（用户裁决·甲）：新增左列容器 —— "Your product" 要跑到衣服图**上方**。
   🔴 标题原在 .pfm-stage-panel 内，与图之间隔着两个 flex 容器；CSS 的 order 只在同一容器内生效
      ⇒ 跨容器提不上去，必须加这层 wrapper。
   🔴 宽度角色从 .pfm-stage-view 搬到 .pfm-stage-main，view 只负责撑满 + aspect-ratio:1/1。
      四档（基础 / ≤768·竖屏≤1024 / 平板竖屏 769~1024 / ≥1280）必须**全部同步搬**，
      漏一档该档宽度就会塌（同 16:55 平板两栏漏 flex-direction 的教训）。 */
.pfm-stage-main{display:flex;flex-direction:column;gap:14px;min-width:0;flex:0 1 auto;width:min(520px,50vw)}
.pfm-stage-view{position:relative;flex:0 0 auto;width:100%;aspect-ratio:1/1;background:#fff;border:1px solid var(--pf-border);border-radius:var(--pf-r-lg);display:flex;align-items:center;justify-content:center;overflow:hidden;box-shadow:0 2px 12px rgba(0,0,0,0.05)}
.pfm-stage-img{width:100%;height:100%;object-fit:contain;display:block}
/* 2026-09-12: 设计层 — 与素产品图同框同比例（都 contain），叠在其上；不拦截点击 */
.pfm-stage-art{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:contain;pointer-events:none;display:none;z-index:1}
/* 2026-10-25: 帽兜前景层 — 叠在设计层之上（z-index:2），只覆盖帽兜区域；不拦截点击 */
.pfm-stage-fg{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:contain;pointer-events:none;display:none;z-index:2}
.pfm-stage-ph{color:var(--pf-muted);font-size:0.9rem}
.pfm-stage-panel{flex:0 1 320px;display:flex;flex-direction:column;gap:12px;padding-top:38px}
.pfm-stage-h{font-size:1.3rem;font-weight:600;margin:0}
.pfm-stage-sub{font-size:0.9rem;color:var(--pf-muted);margin:0 0 6px}
.pfm-topbar{padding:4px 24px 0}
.pfm-stage-faces{display:flex;align-items:center;gap:10px;margin:2px 0 6px;flex-wrap:wrap}
.pfm-stage-faces-label{font-size:0.78rem;font-weight:600;color:var(--pf-muted);white-space:nowrap}
/* 2026-10-06: View 行收窄 —— 面板最窄 310px（390 手机）/ 320px（桌面）下，4 个面 chip 要一行放得下。
   根因：4 chip 合计 291.5px + label 30px + gap 10px = 331.5px > 面板 ⇒ 标签被挤到第二行，
   行高 30.4→61.6px，下方 Design / Mockups / Add to Cart / Save design / My designs 整列瞬间下移 31px（CLS）。
   收窄后合计 293.5px（面板 310 时余量 16.5px），且「只有 1 个 Front」与「4 个面」行高同为 30.4px ⇒ 零跳动。
   ⚠️ 选择器必须限定 .pfm-stage-faces 后代 —— .pfm-chips / .pfm-chip 是共享类（设计板 #pfmPlacements 也用）。 */
.pfm-stage-faces>.pfm-chips{gap:2px;min-width:0}
.pfm-stage-faces>.pfm-chips>.pfm-chip{padding:4px 6px}
.pfm-stage-act{height:48px;font-size:0.95rem;width:100%}
/* 2026-09-13: Save design + My designs 同一行 —— 两张独立白底卡片各占一半（高度仍 48px，手机端同样并排） */
.pfm-stage-row{display:flex;gap:12px;align-items:stretch}
.pfm-stage-row>.pfm-stage-act{flex:1 1 0;width:auto;min-width:0;white-space:nowrap}
/* 2026-09-13: 操作区的 <a> 入口要跟同排 <button> 完全同排版（.pfm-btn 只管配色/圆角） */
.pfm-stage-link{display:flex;align-items:center;justify-content:center;text-decoration:none}
.pfm-stage-mock{background:#F3E8FF;color:#7C3AED;border-color:#D8B4FE}
.pfm-stage-mock:hover{background:#EDE0FF;border-color:#7C3AED}
.pfm-stage-cart{background:#111;color:#fff;border-color:transparent}
.pfm-stage-cart:hover{background:#333}

@media(max-width:767px),(orientation:portrait) and (max-width:1024px){
  .pfm-stage{flex-direction:column;gap:16px;padding:16px;align-items:flex-start}
  /* 2026-11-01（甲·单色一屏）：产品图/面板收窄到 340px（下限，避免图太小不协调）+ 压间距，
     让单色(Single)设计台在手机竖屏刚好一屏显示完、不滚动。 */
  .pfm-stage-main{width:100%;max-width:340px}
  .pfm-stage-panel{flex:1 1 auto;width:100%;max-width:340px;gap:8px}
  .pfm-stage-act{height:44px}
  .pfm-topbar{padding:4px 16px 0}
}

/* 2026-10-29（甲案·用户裁决）：平板竖屏 769~1024 改两栏并排。
   根因：上面那条 `(orientation:portrait) and (max-width:1024px)` 把平板竖屏也压成单栏 + 图满宽 420
        ⇒ 用户报「平板竖屏宽度足够，衣服显示模块却很大、不能并排」。
   做法：照详情页 .product-main__grid 的思路（1fr 1fr 等分 + 仅窄屏回落单栏、不看 orientation），
        给这一档单独开两栏；图给 460 上限，避免 1024 竖屏下图反而比改前更大。
   ⚠️ 必须 flex-wrap:nowrap —— 不改的话会走默认值 view=min(520px,50vw)+gap28+panel320，
      在 768 屏算成 384+28+320=732 > 可用 720，会被 wrap 掉、面板掉到图下方。
   ⚠️ 🔴 **必须显式 flex-direction:row** —— 本区间会**同时命中**上面那条
      `(orientation:portrait) and (max-width:1024px)`，它设了 flex-direction:column；
      只改 flex-wrap 不改方向 ⇒ 仍是竖排，且 column + align-items:flex-start + width:auto
      ⇒ .pfm-stage-view 宽度塌成 0（内容全是 absolute 定位的 img）⇒ **衣服整张看不见**
      （2026-10-29 首推即踩，实测报错后回修）。
   ⚠️ 手机（≤768）与横屏/桌面（≥1280）完全不动 —— 本条只覆盖 portrait 且 769~1024。 */
@media (orientation:portrait) and (min-width:768px) and (max-width:1024px){
  .pfm-stage{flex-direction:row;flex-wrap:nowrap;gap:24px;padding:24px 20px;align-items:flex-start}
  .pfm-stage-main{flex:1 1 0;width:auto;max-width:460px;min-width:0}
  .pfm-stage-panel{flex:1 1 0;width:auto;max-width:none;min-width:0}
  .pfm-topbar{padding:4px 20px 0}
}

/* 2026-10-09（乙案·用户裁决）：桌面宽屏放大设计台。
   现象：1440 屏下设计台只占 1104（全站内容网格上限），预览 520 + 面板 320 居中 ⇒ 左右空 336px；
         1920 屏更是空 816px（占屏宽 42%）——「空间很充足，东西很小」。
   做法：① #pfDesignMaker 跨满整个 section 网格列（等价 .full-width 的网格行为），
           宽度放到 min(1404px, 100% - 64px)：1440 屏吃满、1920 屏封顶 1404。
         ② 预览 520→680、面板 320→420、间距同步放大；
            标题 1.3→1.4rem、按钮 48→54px（轻量同步，不做大改）。
         ③ 顶栏（← Back）单独锁回原内容宽度（--page-width - 2×--page-margin = 1104）
            ⇒ 返回键的绝对位置与其它二级页保持一致，不会被拉到屏幕最左。
   ⚠️ 断点取 1280px：这是「预览 680 + 间距 40 + 面板 420 + 左右 padding 32×2」一行放得下的
      最小可用宽度（更窄会被迫换行、面板掉到预览下方）。
   ⚠️ 手机/平板（≤1024 竖屏、769~1279 中屏）完全不动 ⇒ 走上面的 max-width 查询或默认值，零回归。 */
@media(min-width:1280px){
  #pfDesignMaker{grid-column:1/-1;width:min(1404px,calc(100% - 64px));margin:0 auto}
  #pfDesignMaker>.pfm-topbar{width:100%;margin:0;padding:4px 32px 0}
  .pfm-stage{gap:40px;padding:36px 32px}
  .pfm-stage-main{width:680px}
  .pfm-stage-panel{flex:0 1 420px;gap:16px}
  .pfm-stage-h{font-size:1.4rem}
  .pfm-stage-sub{font-size:0.95rem}
  .pfm-stage-act{height:54px;font-size:1rem}
  .pfm-stage-faces-label{font-size:0.82rem}
}

/* 2026-11-01（横屏平板两栏一屏）：产品图/右栏一屏显示。
   预留 275px = 全站 header(横幅+导航 ~114) + topbar Back(52) + stage padding(56) + 标题(41)。
   min-height:500px 排除横屏手机（<500 高一屏会逼产品图 <130px、太小不协调，保持原样）；
   max-height:900px 只圈横屏平板（677/768 高），桌面横屏(>900)走上面断点、零回归。 */
@media (orientation:landscape) and (min-height:500px) and (max-height:900px){
  .pfm-stage-main{width:min(520px,50vw,calc(100dvh - 275px))}
  .pfm-stage-panel{padding-top:24px;gap:8px}
  .pfm-stage-act{height:44px}
}

/* 2026-09-12: 底栏滑块升级为 Affinity 观感 —— 6px 灰轨 + 24px 紫旋钮（白环 + 微阴影），
   可点区域同时抬到 24px 高；与顶栏 36/44px 按键家族同规格。 */
.pfm-bb-range{flex:1;min-width:56px;height:24px;background:transparent;-webkit-appearance:none;appearance:none;accent-color:#7C3AED;cursor:pointer}
.pfm-bb-range::-webkit-slider-runnable-track{height:6px;border-radius:var(--pf-r-sm);background:#E4E4E7}
.pfm-bb-range::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:24px;height:24px;margin-top:-9px;border:0;border-radius:var(--pf-r-full);background:#7C3AED;box-shadow:0 0 0 2px #fff,0 1px 3px rgba(0,0,0,.22)}
.pfm-bb-range::-moz-range-track{height:6px;border-radius:var(--pf-r-sm);background:#E4E4E7}
.pfm-bb-range::-moz-range-thumb{width:24px;height:24px;border:0;border-radius:var(--pf-r-full);background:#7C3AED;box-shadow:0 0 0 2px #fff,0 1px 3px rgba(0,0,0,.22)}
.pfm-bb-range::-ms-track{height:6px;border-color:transparent;background:transparent;color:transparent}
.pfm-bb-range::-ms-thumb{width:24px;height:24px;border:0;border-radius:var(--pf-r-full);background:#7C3AED;box-shadow:0 0 0 2px #fff,0 1px 3px rgba(0,0,0,.22)}
.pfm-bb-range:focus-visible{outline:2px solid #7C3AED;outline-offset:2px}
/* 2026-09-16: 无选中对象时置灰 —— 设计板里没有画笔，禁掉「空转去改绘板笔刷」 */
.pfm-bb-range:disabled{opacity:.4;cursor:default}
/* 2026-09-19: 对象投影面板 —— 预设胶囊折行（每行 4 个）+ 手调参数（颜色/不透明度/距离/模糊）
   参数行的输入控件全部复用既有类（.pfm-xf-row / .pfm-xf-lab / .pfm-bb-range），只新增颜色控件样式。 */
.pfm-sh-seg{flex-wrap:wrap;overflow:hidden}
.pfm-sh-seg .pfm-tx-chip{flex:0 0 25%}
.pfm-sh-seg .pfm-tx-chip:nth-child(n+5){border-top:1px solid #E4E4E7}
.pfm-sh-params{margin-top:6px}
.pfm-sh-color{flex:0 0 auto;width:38px;height:26px;padding:0;border:1px solid #E4E4E7;border-radius:var(--pf-r-sm);background:#fff;cursor:pointer;appearance:none;-webkit-appearance:none}
.pfm-sh-color::-webkit-color-swatch-wrapper{padding:2px}
.pfm-sh-color::-webkit-color-swatch{border:0;border-radius:var(--pf-r-sm)}

/* Sidebar（旧类名保留兼容） */
.pfm-sidebar{width:320px;min-width:320px;background:rgba(255,255,255,0.82);border-left:1px solid var(--pf-border);overflow-y:auto;padding:16px 14px;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}
/* 2026-09-10: Affinity 外观 — section 用发丝分割线区隔，标题全大写小字距 */
.pfm-section{margin-bottom:12px;padding-bottom:12px;border-bottom:1px solid #EFEFF1}
.pfm-section:last-child{border-bottom:none;padding-bottom:0;margin-bottom:0}
.pfm-label{display:block;font-size:0.68rem;color:#A1A1AA;margin-bottom:6px;font-weight:600;letter-spacing:0.07em;text-transform:uppercase}

.pfm-chips{display:flex;gap:4px;flex-wrap:wrap}
.pfm-chip{padding:4px 10px;background:#f5f5f5;border:1px solid var(--pf-border);border-radius:var(--pf-r-xl);font-size:0.75rem;cursor:pointer;position:relative}
/* 2026-09-13: 统一选中态 = 左侧工具条那种（淡紫底 + 深紫字），原为紫粉渐变实底。 */
.pfm-chip.sel{background:#EDE9FE;color:#6D28D9;border-color:#C4B5FD;font-weight:600}
.pfm-pdot{position:absolute;top:-4px;right:-4px;width:10px;height:10px;border-radius:var(--pf-r-full);background:#EF4444;border:2px solid #fff;display:none}
.pfm-chip.sel .pfm-pdot{border-color:#EDE9FE}

.pfm-stab{flex:1;padding:5px 0 4px;background:transparent;border:none;font-size:0.7rem;color:#6b6778;cursor:pointer;font-weight:600;border-radius:var(--pf-r-sm);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;white-space:nowrap;transition:background var(--pf-dur-1),color var(--pf-dur-1),box-shadow var(--pf-dur-1)} /* 2026-09-03: 图标上/文字下（54px 格内长单词放不下同行），B版分段控件 */
.pfm-stab-ico{font-size:1.05rem;line-height:1;display:block}
.pfm-stab:hover{color:#7C3AED;background:rgba(124,58,237,0.06)}
.pfm-stab.sel{background:#fff;color:#7C3AED;box-shadow:0 1px 3px rgba(60,45,110,0.14)}

/* 工具条内 Draw 强调（渐变底） */
.pfm-toolbar .pfm-stab[data-panel="draw"]{color:#fff;background:linear-gradient(135deg,#7C3AED,#EC4899);border-radius:var(--pf-r-md)}
.pfm-toolbar .pfm-stab[data-panel="draw"] .pfm-stab-lbl{color:#fff}

.pfm-panel{margin-bottom:12px}
/* 2026-09-16: 草稿恢复条（#panel-upload 顶部）—— 仅「画布为空 + 存在草稿」时由 JS 加 .show。
   刻意放在面板内而非画布浮层上：不遮挡画布，也与 .pfm-panel 的 12px 节奏一致。 */
/* 2026-10-09（用户裁决②乙）：草稿恢复条从「#panel-upload 流内块」改成**板外的底部独立浮条**。
   ① 定位：fixed 贴视口底部、水平居中 —— #pfmBoard 是 fixed/inset:0 满屏层，「板子下面」无文档流空间。
   ② 层级 350：必须 > #pfmBoard(300) 才看得见，且 < .pfm-modal(400) 不盖弹窗（与既有层级基线一致）。
   ③ 显隐：基础 display:none，靠 .show + `html.pfm-board-open` 门控 ⇒ **只在设计板打开时可见**，
      与搬走前（藏在 #panel-upload 里）行为一致 —— draftBarSync() 的判定一行未改。
   ④ 只在「画布为空 + 有草稿」时出现 ⇒ 浮层下面永远没有真实作品，压住画布底边无所谓。 */
/* 2026-10-14（用户确认）：草稿条**屏幕正中**（水平 + 垂直都居中：left/top 50% + translate(-50%,-50%)）；
   bottom 由 16px 抬到 20px（略少压页脚）。⚠️ 改本文件务必每次重新拉线上基线 ——
   本规则曾被「加载条」补丁用旧基线覆盖过一次（闸门拦不住"用了过期输入"这种错）。 */
.pfm-draftbar{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);width:min(392px,calc(100% - 28px));z-index:350;display:none;flex-direction:row;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px 14px;padding:10px 12px;border:1px solid #DDD6FE;border-radius:var(--pf-r-md);background:#F5F3FF;box-shadow:0 12px 34px rgba(17,24,39,.16)}
/* 2026-10-14（草稿自动恢复）：**去掉「设计板已打开」这一前提** —— 用户一进设计台就要看到
   「上次的设计还在」的提示条，否则根本无从知道草稿存在（原设计意图「只在画布为空 + 有草稿时出现」
   仍由 .show 这个类保证，见 pf-draft.js 的 barSync/barAuto）。 */
.pfm-draftbar.show{display:flex}
.pfm-draftbar-txt{display:flex;flex-direction:column;gap:1px;min-width:0}
.pfm-draftbar-txt strong{font-size:0.85rem;font-weight:600;color:#5B21B6;display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pfm-draftbar-txt #pfmDraftMeta{font-weight:600;color:#7C3AED}
/* 2026-10-14：✕ 关闭（只收起本次提示，不删草稿）—— 紧凑化后必须给一个"我不看"的出口 */
.pfm-draftbar-x{min-width:32px;padding:0 9px!important;color:#6B7280!important;font-weight:600}
.pfm-draftbar-txt span{font-size:0.75rem;color:#6B7280}
.pfm-draftbar-act{display:flex;gap:8px;justify-content:flex-end}
.pfm-draftbar .pfm-btn{height:32px;padding:0 12px;font-size:0.8rem}
/* 2026-10-14：草稿恢复的加载条（点 Pick up 后原地替换按钮行，直到所有面恢复完）。
   色系沿用草稿条/站内主色（#A78BFA → #7C3AED）；动效**只动 transform**（性能），
   并尊重系统「减少动态效果」偏好（此时退化为静态满条）。 */
/* 2026-10-14：**首屏加载遮罩**（全屏居中，position:fixed ⇒ 与 #pfmBoard 的 display 无关，
   所以首屏一定看得见）。z-index 高于草稿条(350)/#pfmBoard(300) ⇒ 加载时不会与草稿条叠在一起。 */
/* 2026-10-14（用户实测「加载条没有最优先显示」）：改为**默认就显示** ——
   原来默认 display:none、要等 loadProduct 加 .on 才出现 ⇒ 从页面开始到脚本跑完那几秒是**白屏无提示**。
   现在语义反过来：JS 只负责**隐藏**（加 .hide）；.on 保留仅为兼容可能残留的旧脚本。 */
.pfm-boot{position:fixed;inset:0;z-index:400;display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,.74)}
.pfm-boot.hide{display:none}
.pfm-boot.on{display:flex}
.pfm-boot-card{display:flex;flex-direction:column;gap:11px;min-width:210px;max-width:min(320px,84vw);padding:18px 20px;border:1px solid #DDD6FE;border-radius:var(--pf-r-md);background:#F5F3FF;box-shadow:0 12px 34px rgba(17,24,39,.16)}
.pfm-boot-txt{font-size:0.85rem;font-weight:600;color:#5B21B6}
.pfm-boot-rail{position:relative;height:6px;border-radius:999px;background:#EDE9FE;overflow:hidden}
.pfm-boot-rail i{position:absolute;top:0;left:0;width:38%;height:100%;border-radius:999px;background:linear-gradient(90deg,#A78BFA,#7C3AED);transform:translateX(-100%);animation:pfm-draft-prog 1.05s cubic-bezier(.4,0,.6,1) infinite}
@media (prefers-reduced-motion: reduce){.pfm-boot-rail i{animation:none;width:100%;transform:none}}

.pfm-draftbar-prog{display:none;width:100%;align-items:center;gap:10px}
.pfm-draftbar.is-restoring .pfm-draftbar-prog{display:flex}
.pfm-draftbar.is-restoring .pfm-draftbar-act{display:none}
.pfm-draftbar-prog-txt{font-size:0.78rem;font-weight:600;color:#5B21B6;white-space:nowrap}
.pfm-draftbar-prog-rail{position:relative;flex:1 1 auto;min-width:80px;height:6px;border-radius:999px;background:#EDE9FE;overflow:hidden}
.pfm-draftbar-prog-rail i{position:absolute;top:0;left:0;width:38%;height:100%;border-radius:999px;background:linear-gradient(90deg,#A78BFA,#7C3AED);transform:translateX(-100%);animation:pfm-draft-prog 1.05s cubic-bezier(.4,0,.6,1) infinite}
@keyframes pfm-draft-prog{0%{transform:translateX(-100%)}100%{transform:translateX(265%)}}
@media (prefers-reduced-motion: reduce){.pfm-draftbar-prog-rail i{animation:none;width:100%;transform:none}}
/* 窄档：板子底栏 = 工具条 65 + 操作条 57 = 122 ⇒ 抬到底栏之上，不压住工具行。
   122 这个数来自 .pfm-props 的 `bottom:122px`（同一套几何），改那边必须同步复核这里。
   🔴 但抽屉（#pfmProps.pfm-open）本身就占满「底栏之上」那一整块（实测 834×1194：抽屉 y 772~1072），
      草稿条若继续显示就会压住 #panel-upload / #pfmDropzone（预演实测重叠 560×88）⇒
      抽屉打开时**直接收起草稿条**：用户已经在动手传图/调参了，「欢迎回来」是噪音。
      （抽屉关了自动回来 —— 只盖 display，.show 状态原样保留。） */
@media(max-width:768px),(orientation:portrait) and (max-width:1024px){
  .pfm-draftbar{bottom:132px}
  html:has(#pfmProps.pfm-open) .pfm-draftbar.show{display:none}
}
/* 面板末尾主按钮与上方内容的间距（2026-09-15）
   原先 #pfmBtnAddText 完全没写间距 ⇒ 与上方 ALIGN 行贴合 0px（用户报「太贴近」，真机实测 gap=0px）。
   成因在 assets/hd-text.css：`#pfmTextExt .pfm-row:last-child{margin-bottom:0}` 把末行底距清零。
   10px = 与上方「ARC 行 → 分隔线 → TYPE 行」的节奏对称。
   2026-09-16: 同组的 #pfmBtnOpenDraw 规则已随「死 UI #panel-draw」删除（用户方案 A）。 */
#pfmBtnAddText{margin-top:10px}
/* 2026-10-09（丁②）：file input 转为隐藏数据源。
   刻意**不用 display:none** —— #pfmFileInput.click() 在个别浏览器上对 display:none 的
   file input 不放行，且失败时是静默的（点了没反应、控制台无报错）。故 1px 移出视口。 */
.pfm-file-hidden{position:absolute;left:-9999px;top:0;width:1px;height:1px;opacity:0;pointer-events:none}

.pfm-input{padding:8px 10px;border:1px solid var(--pf-border);border-radius:var(--pf-r-sm);font-size:0.85rem;background:var(--pf-white);width:100%;box-sizing:border-box}
textarea.pfm-input{resize:vertical}.pfm-row{display:flex;align-items:center;gap:8px;margin:8px 0;flex-wrap:wrap}
.pfm-row .pfm-label{margin:0;min-width:36px}

/* Swatches */
.pfm-swatch{width:26px;height:26px;border-radius:var(--pf-sw-r);cursor:pointer;border:var(--pf-sw-bw) solid var(--pf-sw-bd);transition:transform var(--pf-dur-1)}
.pfm-swatch:hover{transform:scale(1.15)}
.pfm-swatch.sel{border-color:#7C3AED;box-shadow:0 0 0 1px rgba(124,58,237,0.25)}

/* Cliparts */
/* 2026-09-14: 200px 是「贴纸还少」时的值 —— 现 100 个 / 4 列 = 25 行 / 内容高 1194px，
   200px 一屏只露 4.2 行（第 4 行还被切半），而 .pfm-props 是 block 布局、不会分配剩余空间。
   桌面按视口取 min(52vh,420px) → 一屏 8~9 行；手机端见下面 max-width:768px 段。 */
.pfm-clipart-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(60px,1fr));gap:6px;max-height:min(52vh,420px);overflow-y:auto;overflow-x:hidden}
.pfm-clipart-item{padding:8px;background:#f9f9f9;border:1px solid var(--pf-border);border-radius:var(--pf-r-sm);text-align:center;font-size:1.4rem;cursor:pointer}
.pfm-clipart-item:hover{background:#F3E8FF;border-color:#7C3AED}
/* 🆕 2026-10-08 素材库（Image 面板内）：My Images 私有段在 Library 官方段之上。
   两段共用同一套网格；每段各自内滚并限高 —— 素材再多也不会把拖拽区顶出面板。
   视觉语言刻意沿用 .pfm-clipart-item（同一块面板里不该出现两套卡片长相）。 */
.pfm-lib{margin:0 0 10px}
.pfm-lib-sec+.pfm-lib-sec{margin-top:12px}
.pfm-lib-head{display:flex;align-items:center;justify-content:space-between;margin:0 0 6px;font-size:.72rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:#8b8b96}
.pfm-lib-cnt{font-weight:400;letter-spacing:0;text-transform:none;color:#6f6f7a}
/* 2026-10-09（用户裁决 甲①/乙②）：桌面端密度 —— 4 列 → 5 列（每格 78.5 → 61.6 CSS px），
   可视高 min(26vh,215px) → min(34vh,300px)（900px 高屏 ⇒ 300px，一屏 2.7 行 → 4.4 行）。 */
/* 2026-10-12（甲 · 用户裁决）：电脑端一屏只露 3 行太少 ⇒ 300px → 560px。
   1440×900 实测格子 85px + gap 6 ⇒ 行高 91，6 行需 550px（含 padding）⇒ 560 正好 6 行。
   仍带 vh 上限（矮屏 64vh 先到 ⇒ 面板内滚，不撑爆）。手机档那张 media 规则**不动**。 */
.pfm-lib-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(60px,1fr));gap:6px;max-height:min(64vh,560px);overflow-y:auto;overflow-x:hidden;padding:8px 8px 2px;margin:-8px -8px -2px}
/* 2026-10-12: 懒加载哨兵（占整行、不可见不可点；滚到它才续下一批） */
.pfm-lib-sentinel{grid-column:1/-1;width:100%;height:1px;pointer-events:none}
/* 2026-10-12（第二次修）：用户报「多款设备都重叠」。Edge/Chromium 三视口实测布局正常、零重叠
   ⇒ 属**声称支持、实际渲染失效**的一类浏览器（@supports 判为「支持」⇒ 上次的兜底不触发
   ⇒ 格子高度塌陷 ⇒ 叠成一团）。故**无条件**改为老写法「padding-bottom:100% 撑高 + 绝对定位填充」，
   不依赖任何特性检测；正常设备视觉逐字不变（仍是正方形）。 */
.pfm-lib-item{display:block;position:relative;height:0;padding:0 0 100% 0;overflow:hidden;background:#f9f9f9;border:1px solid var(--pf-border);border-radius:var(--pf-r-sm);cursor:pointer}
.pfm-lib-item img{position:absolute;left:0;top:0;width:100%;height:100%;object-fit:contain;display:block}
.pfm-lib-item:hover{background:#F3E8FF;border-color:#7C3AED}
/* + 号上传格（2026-10-09 丙①）：它是上传入口**唯一可见形态**，恒在 My Images 网格首位。
   空库时能否看得见，取决于 My Images 段是否常驻（见 design-canvas.liquid 的 paint()）。 */
.pfm-lib-add{display:block;position:relative;height:0;padding:0 0 100% 0;background:#fff;border:1.5px dashed #C7C7D1;border-radius:var(--pf-r-sm);cursor:pointer;color:#8b8b96;transition:background-color var(--pf-dur-1),border-color var(--pf-dur-1),color var(--pf-dur-1)}
.pfm-lib-add:hover{background:#F3E8FF;border-color:#7C3AED;color:#7C3AED}
.pfm-lib-add svg{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:22px;height:22px;display:block}
/* 拖入高亮（丁②）：整块素材库是投放区，+ 号格同时变实线高亮，
   明确「拖到这里 = 上传到 My Images」。特异性 (0,3,0) > :hover (0,2,0)，故能盖住悬停态。 */
.pfm-lib.dragover{outline:2px dashed #7C3AED;outline-offset:3px;border-radius:var(--pf-r-sm)}
.pfm-lib.dragover .pfm-lib-add{background:#F3E8FF;border-color:#7C3AED;border-style:solid;color:#7C3AED}
.pfm-lib-msg{margin-top:6px;font-size:.72rem;color:#8b8b96}
/* 2026-10-09（甲）：长按 600ms 删除自己上传的素材 —— 卡片浮起 + 右上角 ✕。
   overflow 必须在 armed 时放开：基准规则的 overflow:hidden 会把 -7px 的角标裁掉。
   touch-callout/user-select 关掉，否则触屏长按会先弹系统图片菜单，抢走我们的长按。 */
.pfm-lib-item{-webkit-touch-callout:none;-webkit-user-select:none;user-select:none;touch-action:manipulation}
.pfm-lib-item.pfm-armed{overflow:visible;position:relative;z-index:2;transform:scale(1.06);background:#FEF2F2;border-color:#EF4444;box-shadow:0 6px 18px rgba(0,0,0,.22)}
.pfm-lib-x{position:absolute;top:-7px;right:-7px;width:22px;height:22px;padding:0;display:flex;align-items:center;justify-content:center;border:0;border-radius:50%;background:#EF4444;color:#fff;cursor:pointer;box-shadow:0 1px 4px rgba(0,0,0,.3)}
.pfm-lib-x::after{content:"";position:absolute;inset:-6px}
.pfm-lib-x svg{width:12px;height:12px;display:block}
.pfm-lib-x:hover{background:#DC2626}
/* 2026-10-10（甲）：点素材格 → 该格内转圈（图落画布即消）。.pfm-loading 由 JS 加，
   150ms 延迟显示也在 JS 控制（图快就不闪）。::after 半透明白蒙层压住缩略图，
   ::before 用 border-top-color 差异做出转圈，纯 CSS 零依赖。 */
.pfm-lib-item.pfm-loading{position:relative;cursor:progress}
.pfm-lib-item.pfm-loading::after{content:"";position:absolute;inset:0;background:rgba(255,255,255,.72);border-radius:inherit}
.pfm-lib-item.pfm-loading::before{content:"";position:absolute;left:50%;top:50%;width:18px;height:18px;margin:-9px 0 0 -9px;box-sizing:border-box;border:2px solid rgba(124,58,237,.28);border-top-color:#7C3AED;border-radius:50%;animation:pfm-lib-spin .7s linear infinite;z-index:1}
@keyframes pfm-lib-spin{to{transform:rotate(360deg)}}
/* 2026-10-12：**上传乐观占位态** —— 选完图先显本地预览，服务器确认后换真图。
   半透明 + 转圈，明确告诉用户「正在上传，别以为没反应」。复用上面的 pfm-lib-spin。 */
.pfm-lib-item.pfm-pending{opacity:.62}
.pfm-lib-item.pfm-pending::after{content:"";position:absolute;left:50%;top:50%;width:16px;height:16px;margin:-8px 0 0 -8px;box-sizing:border-box;border:2px solid rgba(124,58,237,.35);border-top-color:#7C3AED;border-radius:50%;animation:pfm-lib-spin .7s linear infinite;z-index:2}

/* ===== 2026-10-12（用户裁决）：素材库网格**填满**面板可用高度 =====
   背景：高度原来写死 vh 值（桌面 560 / 窄屏 176），面板比它高就留一大片空白
   —— 用户报「有空位，为什么不显示完全」。改为「有多少用多少」。
   🔴 **只在「Upload 面板可见」时生效**（`:has()` 精准限定）⇒ 其它面板零影响。
   🔴 不支持 `:has()` 的内核整块失效 ⇒ 退回上面的固定 max-height（安全降级，不会坏）。
   链：.pfm-props → .pfm-props-scroll → #panel-upload → .pfm-lib → .pfm-lib-sec → .pfm-lib-grid */
.pfm-props:has(> .pfm-props-scroll > #panel-upload:not([style*="none"])){display:flex;flex-direction:column;overflow:hidden}
.pfm-props:has(> .pfm-props-scroll > #panel-upload:not([style*="none"]))>.pfm-props-scroll{flex:1 1 auto;min-height:0;overflow-y:auto}
.pfm-props-scroll:has(> #panel-upload:not([style*="none"])){display:flex;flex-direction:column}
.pfm-props-scroll:has(> #panel-upload:not([style*="none"]))>#panel-upload{flex:1 1 auto;min-height:0;display:flex;flex-direction:column}
#panel-upload>.pfm-lib{flex:1 1 auto;min-height:0;display:flex;flex-direction:column}
.pfm-lib>.pfm-lib-sec{flex:1 1 auto;min-height:0;display:flex;flex-direction:column}
.pfm-lib-sec>.pfm-lib-grid{flex:1 1 auto;min-height:0;max-height:none}

/* 2026-11-01（治本）：clipart 面板仿素材库加 flex 链 —— 网格高度用 flex:1 自适应 props-scroll，
   不再靠 calc 公式写死 max-height（公式基准错位 ⇒ 网格溢出 props-scroll ⇒ 末行被裁）。
   保留 .pfm-clipart-grid 自身 overflow-y:auto 内滚（100 项贴纸不宜全展开）。 */
.pfm-props:has(> .pfm-props-scroll > #panel-clipart:not([style*="none"])){display:flex;flex-direction:column;overflow:hidden}
.pfm-props:has(> .pfm-props-scroll > #panel-clipart:not([style*="none"]))>.pfm-props-scroll{flex:1 1 auto;min-height:0;overflow-y:auto}
.pfm-props-scroll:has(> #panel-clipart:not([style*="none"])){display:flex;flex-direction:column}
.pfm-props-scroll:has(> #panel-clipart:not([style*="none"]))>#panel-clipart{flex:1 1 auto;min-height:0;display:flex;flex-direction:column}
#panel-clipart>.pfm-clipart-grid{flex:1 1 auto;min-height:0;max-height:none}

/* 2026-10-12（用户裁决）：素材库「设计师**自己上传**」的图 → **右下角人形小徽章**
   （官方素材不标 = 默认态）。格子上的 ::before/::after 已被「转圈」占用 ⇒ 徽章用**真实元素**；
   pointer-events:none ⇒ 不拦点击（点格子仍走 addImage 插图）。 */
.pfm-mine-badge{position:absolute;right:4px;bottom:4px;width:16px;height:16px;border-radius:4px;background:#7C3AED;z-index:3;pointer-events:none}
.pfm-mine-badge::before{content:"";position:absolute;left:50%;top:2.5px;width:5px;height:5px;margin-left:-2.5px;border-radius:50%;background:#fff}
.pfm-mine-badge::after{content:"";position:absolute;left:50%;top:9px;width:9px;height:5px;margin-left:-4.5px;border-radius:4px 4px 2px 2px;background:#fff}
/* 手机档：面板本身已有内滚 ⇒ 两段各自再矮一点，保证拖拽区始终在首屏可见范围内 */
@media (max-width:900px){
  .pfm-lib-grid{grid-template-columns:repeat(auto-fit,minmax(60px,1fr));max-height:min(22vh,176px)}
}

/* Layers */
.pfm-layers-list{max-height:200px;overflow-y:auto}
/* 2026-09-13: padding-left 10→7 + 透明左边框，消除 .sel 时内容右移 3px 的抖动 */
.pfm-layer-item{padding:7px 10px 7px 7px;border-left:3px solid transparent;border-bottom:1px solid #f0f0f0;cursor:pointer;font-size:0.8rem;display:flex;align-items:center;gap:8px;user-select:none;-webkit-user-select:none}
.pfm-layer-item:hover{background:#f9f9f9}
.pfm-layer-item.sel{background:#F3E8FF;border-left:3px solid #7C3AED}
.pfm-layer-item.dragging{opacity:0.45}
.pfm-layer-item.drag-over{border-color:#7C3AED;background:#F3E8FF;box-shadow:inset 0 2px 0 #7C3AED}
.pfm-layer-drag{color:#ccc;cursor:grab;font-size:0.7rem;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:18px;height:26px;touch-action:none}
/* 2026-09-13: 图层第 1 批 —— 名称 / 眼睛 / 锁 */
.pfm-layer-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pfm-layer-btn{width:26px;height:26px;flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;border-radius:var(--pf-r-sm);cursor:pointer;color:#5F5E5A;padding:0}
.pfm-layer-btn:hover{background:#EFEFEF;color:#333}
.pfm-layer-btn.dim{color:#C4C4C4}
.pfm-layer-btn.act{color:#7C3AED}
/* 2026-09-19: 图层行「删除」按钮 —— 几何与 .pfm-layer-btn 一致，hover 变红提示破坏性 */
.pfm-layer-del{width:26px;height:26px;flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;border-radius:var(--pf-r-sm);cursor:pointer;color:#5F5E5A;padding:0}
.pfm-layer-del:hover{background:#FEE2E2;color:#DC2626}
.pfm-layer-item.off .pfm-layer-name,.pfm-layer-item.off .pfm-layer-drag{color:#C4C4C4}
.pfm-layer-rename{flex:1;min-width:0;font-family:inherit;font-size:0.8rem;padding:2px 6px;border:1px solid #7C3AED;border-radius:var(--pf-r-sm);outline:none;background:#fff;color:#111}
/* 🔴 设计板层叠基线（2026-09-16 定）：画布 10 · 越界红条 20 · 内联 toast 22 ·
   加载遮罩 25 · draw-widget 30 · **属性抽屉 40（恒为画布区之上）** · 弹窗 400。
   ⚠️ .pfm-canvas-area / .pfm-body 都不是层叠上下文（z:auto+relative+transform:none）
   ⇒ 画布区浮层的 z-index 会与抽屉**同层直接比较**，改任何浮层 z 值前先看这行。 */
@media(max-width:768px),(orientation:portrait) and (max-width:1024px){
  .pfm-layer-item{min-height:44px;padding-top:0;padding-bottom:0}
  .pfm-layer-btn{width:44px;height:44px}
  .pfm-layer-del{width:44px;height:44px}
  .pfm-layer-drag{width:30px;height:44px}
}

/* Modal */
/* 2026-09-10: z-index 100 → 400 —— #pfmBoard（设计板全屏层）是 300，
   弹窗必须高于它，否则在设计板里打开的 Draw/Sketchpad 与 Mockups 弹窗被整块盖住（点不到、看不见）。 */
.pfm-modal{position:fixed;inset:0;z-index:400;display:flex;align-items:center;justify-content:center}
.pfm-modal-overlay{position:absolute;inset:0;background:rgba(0,0,0,0.5)}
.pfm-modal-box{position:relative;background:var(--pf-white);border-radius:var(--pf-r-lg);padding:28px;max-width:600px;width:90%;max-height:80vh;overflow-y:auto;box-shadow:0 4px 30px rgba(0,0,0,0.15)}
/* 2026-09-10: 加 z-index:20 —— 弹窗内的工具按钮是 position:relative（z-index auto）且在 DOM 里靠后，
   会把 ✕ 盖住导致点不到（实测 Draw 弹窗关不掉）。提升 ✕ 到工具按钮之上。 */
.pfm-modal-close{position:absolute;top:14px;right:14px;z-index:20;background:none;border:none;font-size:1.2rem;cursor:pointer;color:var(--pf-muted)}
/* 2026-10-03（甲·按钮统一）：画板右上 ✕ 与同排行键对齐 —— 行高 44、内容居中、圆角与同族一致。
   🔴 必须用 `#pfmCloseDraw` 限定：`.pfm-modal-close` 是全局类，设计台 mockup 弹窗也在用。
   改动前实测：✕ = 15.7×26、圆角 0、`top:14px` ⇒ 与 44 高的一行键不居中（下缘空 18px）。 */
#pfmCloseDraw{height:44px;display:flex;align-items:center;justify-content:center;border-radius:var(--pf-r-sm)}
/* 2026-10-06 甲甲：Mockups 生成按钮四态 —— 原 .pfm-modal-status 小字行已随 #pfmMockupStatus 一并删除。
   data-state = idle|busy|done|error|blocked。idle 直接复用 .pfm-btn-green 现值（紫粉渐变，零回归）。 */
.pfm-gen-btn{transition:background .2s ease,box-shadow .2s ease}
.pfm-gen-btn .pfm-gen-ico{position:relative;display:inline-block;width:14px;height:14px;flex:0 0 14px;margin-right:7px;box-sizing:border-box}
.pfm-gen-btn .pfm-gen-ico::before{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:13px;line-height:1;font-weight:700}
#pfmBtnGenerate[data-state="idle"] .pfm-gen-ico::before{content:"\2728"}
#pfmBtnGenerate[data-state="busy"] .pfm-gen-ico{border:2px solid rgba(255,255,255,.4);border-top-color:#fff;border-radius:50%;animation:pfmSpin .7s linear infinite}
#pfmBtnGenerate[data-state="done"] .pfm-gen-ico::before{content:"\2713"}
#pfmBtnGenerate[data-state="error"] .pfm-gen-ico::before{content:"\21BB"}
#pfmBtnGenerate[data-state="blocked"] .pfm-gen-ico::before{content:"!"}
#pfmBtnGenerate[data-state="busy"],
#pfmBtnGenerate[data-state="busy"]:hover{background:var(--pf-btn-busy);border-color:transparent;cursor:default}
#pfmBtnGenerate[data-state="done"],
#pfmBtnGenerate[data-state="done"]:hover{background:var(--pf-green);border-color:transparent}
#pfmBtnGenerate[data-state="error"],
#pfmBtnGenerate[data-state="error"]:hover{background:var(--pf-red);border-color:transparent}
#pfmBtnGenerate[data-state="blocked"],
#pfmBtnGenerate[data-state="blocked"]:hover{background:var(--pf-white);border-color:var(--pf-red);color:var(--pf-red)}
.pfm-gen-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
@keyframes pfmSpin{to{transform:rotate(360deg)}}
.pfm-mockup-gallery{display:flex;flex-direction:column;gap:10px;margin-top:10px}
.pfm-carousel-main{position:relative;border-radius:var(--pf-r-md);border:1px solid var(--pf-border);background:#f4f5f7;overflow:hidden}
.pfm-carousel-main img{width:100%;height:auto;display:block;border-radius:0;border:0}
.pfm-carousel-badge{position:absolute;top:8px;left:8px;z-index:2;background:rgba(0,0,0,.62);color:#fff;font-size:11px;padding:3px 10px;border-radius:var(--pf-r-pill);pointer-events:none;line-height:1.4}
.pfm-carousel-thumbs{display:flex;gap:8px;overflow-x:auto;padding-bottom:4px}
.pfm-carousel-thumbs img{width:56px;height:56px;object-fit:cover;border-radius:var(--pf-r-sm);border:2px solid transparent;cursor:pointer;background:#f4f5f7;flex:0 0 auto}
.pfm-carousel-thumbs img.active{border-color:#1FBDB7}
.pfm-mockup-err{display:none;position:absolute;inset:0;background:#f4f5f7;align-items:center;justify-content:center;flex-direction:column;gap:8px;font-size:13px;color:#666}

@media(max-width:768px),(orientation:portrait) and (max-width:1024px){
  .pfm-body{flex-direction:column}
  /* 2026-09-10: 手机端降级 — 工具条横排到底部，属性面板移到最下；画布占主要空间 */
  .pfm-toolbar{width:100%;min-width:100%;flex-direction:row;justify-content:space-around;align-items:center;border-right:none;border-top:1px solid #E4E4E7;order:2;padding:6px 4px;gap:2px}
  .pfm-toolbar .pfm-stab{width:auto;flex:1 1 0;height:52px;min-width:0}
  /* 2026-09-10: 工具条横排时左缘高亮条不可见 → 改为顶部高亮条（Affinity 选中态的手机版） */
  .pfm-toolbar .pfm-stab.sel::before{display:none}
  .pfm-toolbar .pfm-stab.sel{box-shadow:inset 0 2px 0 #7C3AED}
  .pfm-canvas-area{order:1;padding:12px;overflow:hidden}

  /* 2026-09-19: **画布左右去边**（用户「画布根据宽度，满屏」→「左右去掉 12px 边」）。
     原来容器左右各 12px 内边距 ⇒ 画布 366×366；去掉后可用宽 = 390 ⇒ 画布 **390×390**（= 视口宽）。
     底边 = 12 + 390 = 402 < 抽屉顶 422 ⇒ **仍不被抽屉遮挡**（配合上面的「自动收起两栏」）。
     ✅ 画布尺寸由 JS `resizeCanvasToPrintArea` 每次重读 computed padding 计算 ⇒ **本改动无需改 JS**。
     ⚠️ 只去左右；上下 12px 保留。
     ⚠️ 用 `.pfm-body>` 提升特异性，稳定胜过上面那条 `.pfm-canvas-area{padding:12px}`（同特异性时靠顺序，不可靠）。 */
  .pfm-body>.pfm-canvas-area{padding-left:0;padding-right:0}
  /* 🔴 2026-09-19: **抽屉打开时给画布区留出抽屉的位置（195px）** —— 抽屉不再挡住衣服。
     问题：只要 `视口高 < 视口宽 + 329`，抽屉那条 `max(240px,…)` 的**保底**就生效 ⇒ 抽屉顶着
     保底往上长，压住画布下缘（实测 360×640 压 64px、412×732 压 54px、768×1024 压 118px）。
     做法：抽屉打开时把画布区的**下内边距**撑到 195px ⇒ 画布按可用高缩小（JS 每次重读 padding），
     `maxH` 变小 ⇒ 画布底边上移 ⇒ 抽屉顶边恒 = 画布底 + 12px，且高度仍 ≥ 240px（内容可滚）。
     195 = 240(抽屉保底) + 12(目标间距) + 143(抽屉底边距视口底 122 + 画布区上留白 12 + 画布区顶端到视口顶 9)。
     ⚠️ 先试过 183 ⇒ 实测间距 0px（差的就是那 12px 间距），故取 195。
     实测（抽屉打开，195）：390×844 画布 390 不变、抽屉 308；412×732 画布 412→346、抽屉 240；
     768×1024 画布 768→650、抽屉 240 ⇒ **间距恒 12px、遮挡 0px**。
     ⚠️ padding 变化不触发 section 的 ResizeObserver（它看 border-box）⇒ 由 hd-edit.js 派发 resize 触发重算。
     ⚠️ 改 240 保底 / 抽屉 bottom / 画布上留白，必须同步复核这 195。 */
  .pfm-body:has(>#pfmProps.pfm-open)>.pfm-canvas-area{padding-bottom:195px}
  /* 🔴 2026-09-19: 手机端画布区**禁止滚动**。画布已按可用高度等比适配
     （见 section 的 resizeCanvasToPrintArea）—— 这里兜底：万一尺寸算偏，宁可裁也不要
     「手指滑动把整张产品图滚走」（用户报「背景图会随画面滑动」）。桌面端不受影响。 */
  /* 2026-09-10: 手机端属性面板 → 底部抽屉（默认收起）——原来常驻 420px 把画布挤到只剩 263px。
     抽屉脱离文档流后，画布独占 body 剩余空间；点工具按钮自动上滑，点画布/把手下滑收起。 */
  .pfm-body{position:relative;overflow:hidden}
  /* bottom:65px = 工具条总高(52 + 上下 padding 12 + border 1) → 抽屉展开时工具条仍可见可点，
     点别的工具直接在已开的抽屉里换面板。 */
  /* 2026-09-19: **抽屉高度 440 → 300**（用户报「修改图片属性时面板挡住了，不能实时看到变化」）。
     实测（390×844）：画布 110~476、抽屉展开 282~722 ⇒ **重叠 194px = 画布高的 53%**。
     降到 300 后抽屉顶边 422 ⇒ 只盖画布 54px（**15%**），画布可见 85%。
     抽屉内容区仍约 212px 且 `#pfmPropsScroll` 是唯一滚动体，够用。
     ⚠️ 改这里必须连带看 `.pfm-clipart-grid` 的高度公式（同口径 88px，见下方手机块内的覆盖）。 */
  /* 2026-09-19: **收起位移不足 → 残留 77px 压住底部工具栏**（用户报「布局有问题」，附截图）。
     实测（390×844 收起态）：抽屉 y=767~1067、弹窗底 844 ⇒ 残留 767~844 = 77px，
     盖住操作条（730~787）下部 20px，并与底栏（787~844）糊成一整块白。
     推导：位移必须 ≥ 自身高 + 抽屉 bottom 偏移(65) + 底栏高(57)，115% × 300 = 345 < 422。
     修法：精确位移 = 自身高 100% + 140px（含 18px 保险，底栏高度微调也不露）；
     前一行那个纯百分比是极老内核不支持 calc 混合时的兜底（vh ≥ 194 即够）。
     ⚠️ 若日后改本条 bottom / 高度 / 底栏高度，必须同步复核 140px 这个量。 */
  .pfm-props{position:absolute;left:0;right:0;bottom:122px;width:100%;min-width:0;height:min(42vh,300px);padding:14px 14px 22px;background:#FAFAFB;border-left:none;border-top:1px solid #E4E4E7;border-radius:14px 14px 0 0;box-shadow:0 -8px 28px rgba(0,0,0,.16);transform:translateY(250%);transform:translateY(calc(100% + 140px));transition:transform var(--pf-dur-3) ease;z-index:40;order:0;overflow-y:auto}
  .pfm-props.pfm-open{transform:translateY(0)}

  /* 2026-09-19: **抽屉打开时高度 = 顶边紧跟画布底边下 12px**（用户「高度刚好在画布下面，12PX间距」，方案 A）。
     适用对象：手机上**所有从底部弹出的抽屉** —— 素材/添加图案（Upload·Text·Clipart·Layers 面板）
     与 属性区块（Placement·Fill·Transform…）实测是**同一个容器 #pfmProps**（只是换内容）⇒ 改这里两者同时生效。
     推导：抽屉底边 = body 底 − 65（bottom 偏移）；目标顶边 = 画布底 + 12 = (上留白 12 + 画布宽) + 12
     ⇒ 高度 = **100%(body 高) − 89px − 100vw**(画布宽；画布已满屏且为正方形) 。
     实测 390×844：787 − 89 − 390 = **308px**（原固定 300）⇒ 画布底 402 到抽屉顶的间距由 20px 收到 **12px**。
     竖屏平板 768：967 − 89 − 768 = 仅 110px（装不下内容）⇒ **max(240px, …)** 保底，平板退化为 240px。
     ⚠️ 改「画布上留白(12px) / 底栏高(57px) / 抽屉 bottom(65px) / 画布满屏规则」都必须同步复核 **89** 这个数。
     ⚠️ 依赖上面「抽屉打开即收起两栏」生效（body 高才是 视口 − 底栏）；老内核两者一起降级，抽屉退回 ~259px，不会坏。 */
  .pfm-body>.pfm-props.pfm-open{height:max(240px,calc(100% - 146px - 60vw))}
/* 2026-09-14: 手机端贴纸网格高度 = 抽屉可用高度（抽屉 min(62vh,440px) 减 padding 14/22、
   把手 35 + margin-bottom 18 − 负 margin-top 14 + 面板 margin 2 = 88）→ 354px ≈ 7.4 行（改前 4.2 行 / 16 个）。
   用 calc 精确贴近可用高度，不改成 flex 布局 —— 避免动 .pfm-props 内其它面板（误伤）。 */
.pfm-clipart-grid{max-height:calc(min(62vh,440px) - 88px)}
/* 2026-09-18: 手机端 Text 面板改为「面板自己滚」（照搬上面贴纸网格的做法）。
   原来 .pfm-props 整体滚 + 把手 position:sticky 靠层级硬盖 ⇒ 鸿蒙 ArkWeb 上
   滚动时内容会从把手下穿透（用户报「面板顶部漏背景」）。
   让滚动发生在面板内部后，把手完全不参与滚动，物理上不可能被穿透。
   高度公式与贴纸网格一致：抽屉高 min(62vh,440px) − 88（padding 14/22 + 把手 35 + 间距）。 */
#panel-text{max-height:calc(min(62vh,440px) - 88px);overflow-y:auto}
  .pfm-props-handle{display:flex}
  /* 2026-09-10: 手机端顶栏收窄 — 隐藏标题与分隔线，Back/撤销/重做/Apply 一行放得下 */
  /* 2026-09-15: 顶栏 4 个按钮（Back / Undo / Redo / Apply）改为**与全站 .hd-back 完全一致（36px）**。
     原先触控档放大到 44px → 手机/平板竖屏比其它页面的返回键高 8px、顶栏高 4px。
     基础规格（.hd-back / .pfm-icon-btn / .pfm-btn）本就与全站对齐，故只需删掉这层放大。
     2026-09-19: 当日一度又统一到 44px（为放大 ↶↷ 图标），后因「画板内返回键比其它页面大」
     全部回退 —— **本条即最终规格：顶栏 4 个按钮 = 全站 .hd-back = 36px**。 */
  .pfm-boardbar{padding:6px 10px;gap:6px}
  .pfm-boardbar-title{display:none}
  .pfm-boardbar-sep{display:none}
  /* 2026-10-13: 此处原有 `.pfm-boardbar #pfmBtnUndo{margin-left:auto}`
     （2026-09-19 加，作用 = 把 ↶↷ 推到顶栏最右）。
     用户改口「撤销/重做改到靠近 Apply 按钮」⇒ 该条**已删除**。
     留着会把 ↶ ↷ 又推回最右、与 Apply 之间留一条大缝（小屏下 title/sep 都 display:none，
     四个键自然紧挨在左侧）。⚠️ 勿重新加回。 */
  /* 2026-09-12: Placement 常驻 —— 手机端 .pfm-props 是「默认收起的底部抽屉」，
     而 #pfmPlacementSection（切角度按钮）是它第一块 → 开板即看不见。
     JS（8e 段）把它整块搬到 .pfm-body 直下常驻，这里给它一条常驻条的皮肤。
     选择器限定 .pfm-body 直下 → 桌面端它仍在 .pfm-props 里，不受影响。 */
  .pfm-body>#pfmPlacementSection{order:0;flex:0 0 auto;margin:0;padding:8px 14px 10px;background:#FAFAFB;border-bottom:1px solid #E4E4E7}

  /* 2026-09-19: **抽屉打开时自动收起「顶栏 + 角度切换栏」**（用户要求「布局更大」，方案 B）。
     收起对象：① `#pfmBoardBar`（← Back / ✔ Apply / 标题 / ↶ ↷，49px）
              ② `#pfmPlacementSection`（Placement 角度芯片，49px）—— 共让出 **98px**。
     效果（390×844 实测基线）：画布 inner 由 110~476 上移为 12~378，抽屉顶边仍 422
     ⇒ **画布遮挡 54px → 0px**（原来抽屉正好压住画布下沿）。
     ⚠️ 代价：抽屉开着时 Back / Apply / ↶ ↷ 不可见（用户已知并接受，收起抽屉即恢复）。
     ⚠️ 触发用 CSS 的 has 伪类（Chrome 105+ / Safari 15.4+）；旧内核不支持则**静默降级为不收起**，无副作用。
     ⚠️ 只归零 height/padding/border/opacity，**不动 display** ⇒ 按钮留在 DOM，JS 绑定不受影响。
     ⚠️ 本块在手机/竖屏平板媒体查询内 ⇒ 桌面端完全不受影响。
     ⚠️ 若日后改这两栏的 padding，务必同步改下面那条（否则高度归不了零、会漏一条边）。 */
  #pfmBoard:has(.pfm-props.pfm-open)>#pfmBoardBar,
  .pfm-body:has(>#pfmProps.pfm-open)>#pfmPlacementSection{
    height:0;min-height:0;padding-top:0;padding-bottom:0;border-bottom:0;
    overflow:hidden;opacity:0;pointer-events:none;
    transition:height var(--pf-dur-2) ease,padding var(--pf-dur-2) ease,opacity var(--pf-dur-2) ease
  }
  /* 2026-09-13: Actions 常驻 —— 同 Placement（抽屉手机端默认收起 → 看不见）。
     JS（8e-2 段）搬到画布之前；未选中对象时**始终占位并置灰**（.pfm-fbtn:disabled），
     避免选中/取消时画布上下跳动。选择器限定 .pfm-body 直下 → 桌面端不受影响。
     2026-09-19: **位置由「画布上方」改到「底栏上方」**（用户：这行操作放低栏上面）——
     `order:0 → 2`（画布是 order:1 ⇒ 排到画布之后 = .pfm-body 末端，紧贴底栏）；
     分隔线随之 border-bottom → border-top。
     实测（390×844）：画布高**不变**（561px，原下方本有 65px 死区正好填空）；
     抽屉展开时底边 722 < 操作条 724 ⇒ **不重叠**，无需改 .pfm-props 的 bottom。
     ⚠️ **只改 order 即可** —— JS placeActions() 只管 DOM 插入顺序，视觉顺序由本行决定。 */
  /* 2026-09-19 ⓑ: **容器改为「工具栏 .pfm-toolbar 同款」**（用户：工具栏和操作条统一风格）——
     去掉内嵌白卡片（原由公共区 .pfm-float-toolbar 提供的 362 宽 / 圆角 12 / 描边 + 阴影），
     改成同款**通栏灰条**：同底色 #F7F7F8、同顶部分隔线、同内距 `6px 4px`。
     图标同步换 emoji（见 design-board.liquid）。
     实测：操作条 63 → **57px**（与底栏等高）、画布 561 → **566px**。 */
  .pfm-body>#pfmActionsSection{order:2;flex:0 0 auto;margin:0;padding:6px 4px;background:#F7F7F8;border-top:1px solid #E4E4E7;border-bottom:0}

  /* 2026-09-20 用户要求：抽屉打开时把「对象组格栏 + 操作条」也收起，把这块空间让给抽屉。
     与上面 BoardBar / Placement 同一套机制；旧内核不支持该伪类 ⇒ 整块降级
     （两栏不收起 + 抽屉维持旧几何），不会出现「抽屉盖住两栏」。
     数字推导（底部占用 122 = 该两栏 65 + 57 归零）：
       抽屉底边距视口底 122 → 0；高公式减数 146 → 24（= 0 + 12 间距 + 12 尾距）；
       贴纸网格减数 234 → 112（网格 = 抽屉高 − 88，抽屉 +122）；
       抽屉**顶边不变**（844−430−0 = 414 = 原 844−308−122）⇒ 画布留白 195 与收起位移 140 都无需改动。 */
  .pfm-body:has(>#pfmProps.pfm-open)>#pfmToolbar,
  .pfm-body:has(>#pfmProps.pfm-open)>#pfmActionsSection{
    height:0;min-height:0;padding-top:0;padding-bottom:0;border-top:0;border-bottom:0;
    overflow:hidden;opacity:0;pointer-events:none;
    transition:height var(--pf-dur-2) ease,padding var(--pf-dur-2) ease,opacity var(--pf-dur-2) ease
  }
  .pfm-body:has(>#pfmProps.pfm-open)>#pfmProps{bottom:0;height:max(240px,calc(100% - 24px - 60vw))}
  /* 2026-09-19: 白卡片 → 通栏（与 .pfm-toolbar 一致） */
  #pfmFloatToolbar{background:transparent;box-shadow:none;border-radius:0;padding:0}
  /* 2026-09-19: emoji 图标字号对齐工具栏（.pfm-toolbar .pfm-stab-ico 同为 1.15rem ⇒ 18.4px） */
  .pfm-fbtn__icon{font-size:1.15rem}
}

/* === Draw Modal === */
/* 2026-09-26（甲案·批次1）：弹窗宽上限 660 → min(900px,96vw)。
   动机：画布尺寸现为 `min(wrap 实测宽, 盒可用高−预算)`（见 hd-draw.js#resizeDrawCanvas），
   而宽屏（1920 类）画布长期卡在 618 = wrap 宽 = 660 − 两侧 padding ⇒ **宽度成了唯一瓶颈**，
   屏幕横向 1300px 完全闲置。放宽后 maxW 提到 ~858，画布上限交回「高度−预算」决定。
   ⚠️ 手机/平板竖屏断点（≤768）另有一条 `width:98vw;max-width:98vw`，写在文件更后面 ⇒ 不受影响。 */
.pfm-draw-box{max-width:min(900px,96vw);max-height:calc(100vh - 16px);padding:16px 20px 20px;overflow:hidden auto}
.pfm-draw-box.pfm-anim{transition:transform var(--pf-dur-3) ease,opacity var(--pf-dur-3) ease}
.pfm-draw-box.pfm-mini{transform:scale(0.2);opacity:0;transform-origin:top right}
.pfm-btn-preview{background:#fff;border:1.5px solid #7C3AED;color:#7C3AED;font-weight:600}
.pfm-btn-preview:hover{background:#F3E8FF}
/* Floating preview widget at the product canvas top-right (only buttons, stays inside canvas) */
.pfm-draw-widget{position:absolute;right:16px;top:16px;z-index:30;display:flex;gap:10px;background:rgba(255,255,255,0.95);padding:10px 12px;border-radius:var(--pf-r-lg);box-shadow:0 6px 20px rgba(0,0,0,0.18)}
.pfm-draw-widget-btn{padding:10px 16px;font-size:0.9rem;font-weight:800;border-radius:var(--pf-r-md)}
#pfmDrawWidgetRestore{background:linear-gradient(135deg,#7C3AED,#EC4899);color:#fff;border-color:transparent;box-shadow:0 4px 12px rgba(124,58,237,0.4)}
#pfmDrawWidgetRestore:hover{filter:brightness(1.08)}
.pfm-draw-container{display:flex;flex-direction:column;gap:10px;position:relative}
/* 2026-09-03: 工具条不强制单行/不横向滚动——空间够一行排完，不够自动换行（flex-wrap:wrap） */
.pfm-draw-toolbar{display:flex;justify-content:space-between;align-items:center;gap:2px;flex-wrap:wrap;margin-bottom:2px}
/* 2026-09-10: padding-right:44px —— 为右上角的 ✕ 预留位置。工具增至 21 个后第一行右端
   正好顶到 ✕（视觉重叠 + 点击被吞），预留后自动提前换行，不再压住关闭键。 */
.pfm-draw-tools{display:flex;align-items:center;gap:4px;flex-wrap:wrap;width:100%;padding-right:44px}
.pfm-dtool{width:34px;height:34px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0;background:var(--pf-white);border:1px solid var(--pf-border);border-radius:var(--pf-r-sm);cursor:pointer;font-size:0;transition:all var(--pf-dur-1);padding:2px;position:relative;flex-shrink:0;color:#7C3AED}
.pfm-dtool[data-label]{height:44px;padding:2px 2px 0}
.pfm-dtool[data-label]::after{content:attr(data-label);display:block;font-size:0.5rem;color:var(--pf-muted);line-height:1;margin-top:1px;font-weight:500}
.pfm-dtool:hover{background:#f6f0ff;border-color:#9D5BFF;color:#9D5BFF}
.pfm-dtool:hover svg{filter:drop-shadow(0 0 3px rgba(124,58,237,0.45))}
/* 🔴 2026-09-28：`.pfm-dtool:disabled` **原先没有任何样式** —— 而 snippet 的意图是「置灰，
   '不可用'必须看得见，不能静默无反应」（`showOpts` 里 `sbBtn.disabled = !stabOk`，专为喷枪那类
   不可用场景写的）。本轮「开板默认空白（没选工具）」后**稳定器一开板就是禁用态** ⇒ 必须补上，
   否则用户看到的是一个"看着能点、点了没反应"的按钮（正是项目最忌讳的静默无反馈）。 */
.pfm-dtool:disabled{opacity:.38;cursor:not-allowed}
.pfm-dtool:disabled:hover{background:var(--pf-white);border-color:var(--pf-border);color:var(--pf-muted)}
.pfm-dtool:disabled:hover svg{filter:none}
.pfm-dtool.sel{background:#F3E8FF;border-color:#7C3AED;color:#7C3AED}
/* 🔴 2026-10-04（用户裁决 甲+B）：功能键的「**功能正开着**」状态 —— 与 `.sel`（行是否展开）**解耦**。
   用户原话：「本来功能关闭的，打开后，切换后其他工具，功能按钮还是亮起。比如网格，开了和关，按钮不一样」。
   视觉 B（比整键填底更轻）：**不换背景**，改为
     ① 图标描边加粗 1.4 → 2.2（一眼能看出"亮了"）
     ② 主色光晕
     ③ 顶部一道 2px 主色指示条（远看也能分辨）
   ⚠️ 基础 `.pfm-dtool` 的 color 本来就是 #7C3AED ⇒ "图标变主色"无从区分，故用粗细/光晕/指示条。
   驱动：引擎的 `_syncFuncKeys()`（`window.__pfmSyncFuncKeys`），随状态自动跟随。 */
.pfm-dtool.on{border-color:#7C3AED}
.pfm-dtool.on svg,.pfm-dtool.on svg *{stroke-width:2.2}
.pfm-dtool.on svg{filter:drop-shadow(0 0 4px rgba(124,58,237,.55))}
.pfm-dtool.on::before{
  content:""; position:absolute; left:20%; right:20%; top:-1px; height:2px;
  background:#7C3AED; border-radius:0 0 2px 2px;
}
.pfm-dtool-sm{width:30px;height:30px;font-size:0.8rem}
/* 2026-09-25：稳定器按钮在「当前工具没有稳定器」时置灰（喷枪已被引擎排除；形状/选区/文字本来就没有）。
   取值与既有 :disabled 一致（见 .pfm-fbtn:disabled）。「不可用」必须看得见，不能静默无反应。 */
#pfmDrawStabBtn:disabled{opacity:.4;cursor:not-allowed}
/* 2026-09-25: 工具图标描边粗细统一。原 36 个图标 svg 自带的 stroke-width 有 6 种值
   （1.0 / 1.1 / 1.2 / 1.3 / 1.4 / 1.5）⇒ 撤销·重做 1.5 偏粗、网格 1.2 偏细，一排看着不齐。
   🔴 必须同时打中 svg **和它的全部后代**：stroke-width 是 presentation attribute、靠「继承」下发，
   而图标里有的写在 <svg> 上、有的写在**内层 path** 上 —— 只写 `.pfm-dtool svg{...}`
   盖不住内层自带值的那些（实测：线厚 2px→2px 纹丝不动）。
   实测双向验证（页面自报 computed stroke-width）：
     改前 svg/子元素 = 1.5/1.5  1.4/1.4  1.2/1.2  1.1/1.1
     改后            = 1.4/1.4  1.4/1.4  1.4/1.4  1.4/1.4  ✓
   取 1.4 因它是多数值（27/36），改动面最小；新增图标自动统一，无需逐处写。 */
.pfm-dtool svg,.pfm-dtool svg *{stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round}
/* 2026-09-25（甲方案）：36 个工具图标改为 **SVG sprite + `<use>`** 引用 ——
   几何只在 sprite 里写一次，绘制属性统一由 `.pfm-ico` 提供，各处不再各写一套
   （改前：stroke-width 有 5 种值、11 个图标是平端点、13 个是 miter 直角）。
   上面那条 `.pfm-dtool svg *` 保留作兜底（防 sprite 里漏改内层属性）。
   ⚠️ `<use>` 同时写 href 与 xlink:href：前者是 SVG2、后者兼容老内核（Win7 老 Chrome）。
   ⚠️ sprite 容器用 width:0;height:0;position:absolute **不要用 display:none** ——
      部分老内核不认 display:none 容器里的 symbol 引用。 */
.pfm-ico{display:block;width:16px;height:16px;pointer-events:none;fill:none;stroke:currentColor;
         stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round}
.pfm-ico-defs{position:absolute;width:0;height:0;overflow:hidden;pointer-events:none}
/* 2026-09-25（用户裁决「丙」）：第一排 7 个常驻按钮统一与分类 tab 同高 —— 桌面 44px、手机 40px（见文件尾移动端块）。
   🔴 改前是一条 2026-09-03 的「按 id 逐个点名」老规则，只罩住 撤销/重做/清除 三个
   （09-17 又补进 导入，但**漏了 网格/预设/导出**）⇒ 第一排出现「4 高 3 矮」，用户 09-25 报「高度都不一样」。
   改用类选择器后第一排按钮**自动全部跟随**，以后新增按钮不会再漏。 */
#pfmDrawCats > .pfm-dtool{height:44px}
.pfm-dtool-sep{color:#ccc;margin:0 2px;font-size:0.9rem;user-select:none}
/* 2026-09-27（同上）：**颜色区也改成浮层卡片**（选完一个色块 ⇒ 收起，见 snippet）。
   🔴 保留 `.pfm-draw-colors{display:flex}` 作为「卡片内部」的基础，定位/阴影写在 id 规则上。 */
.pfm-draw-colors{display:flex;flex-direction:column;gap:6px;padding:4px 0}
#pfmDrawColors{position:absolute;top:0;left:0;z-index:60;padding:8px 10px;background:#fff;border:1px solid var(--pf-border);border-radius:var(--pf-r-md);box-shadow:0 10px 28px rgba(17,24,39,.18);max-width:calc(100% - 16px)}
/* 2026-09-26（甲·批次2）：颜色区开合 —— **全平台共用一条规则**（原先只在手机断点里收起）。
   实测占 138px，是画布上方**最大的单块**；默认收起后预算 −138：
   台式 1920×1200 画布 724 → 858（顶到 wrap 宽上限）· 1366×900 454 → 592。
   入口：手机端点「Colour」分类 tab · 桌面端点图标行的调色盘按钮 —— 两者都只切 `.open`。
   🔴 用 id 选择器（特异性高于 class 级 `.pfm-draw-colors{display:flex}`）。
   🔴 开合**刻意不重算画布尺寸**（`_drawBudgetH()` 按视口缓存）⇒ 守住 09-25 用户裁决
      「画板用时固定大小」；与 STAB / GRID / PRESET 的折叠行为完全一致（展开时盒子内部滚动）。
     不改成「随开合重算」也不加桌面 sticky 底栏：后者会踩 09-20 记录的坑
      （sticky 在临界溢出时盖住画布下部 ⇒ 底部区域画不上东西，见 561~768 那条还原规则）。 */
#pfmDrawColors{display:none}
#pfmDrawColors.open{display:flex}
/* 2026-09-27（用户裁决 甲）：颜色入口拆成「单色 / 渐变」两个 ⇒ 面板按模式**只显示对应那一行**。
   收益：面板高度近乎减半（原来两行同时显示）⇒ 内容更矮、容器滚动更少。
   ⚠️ 选择器特异性（0,2,1）高于 `.pfm-draw-color-row{display:grid}`（0,1,0）⇒ 稳胜，无需 !important。 */
#pfmDrawColors[data-mode="solid"] .pfm-draw-color-row[data-crow="grad"]{display:none}
#pfmDrawColors[data-mode="grad"] .pfm-draw-color-row[data-crow="solid"]{display:none}
/* 2026-10-03（用户裁决 甲）：色板改「标题独占一行 + 两列等宽」。
   实测旧方案的毛病：标签占 grid-column:1 ⇒ 第 1 个色块落到第 2 列、第 2 个色块回到
   第 1 列第 2 行 ⇒ **两列色块错开整整一行（30px）**，右列比左列高一格，看着就是“不齐”。
   ⇒ 标签 grid-column:1/-1 独占第 1 行；两列固定 24px ⇒ 列距 30 = 行距 30，逐行对齐。
   两种模式（Solid / Gradient）各 18 格 = 9 行 × 2 列，正好排满、无半行。
   ⚠️ 只改桌面基础规则；手机端由 @media 自己的 grid-template-columns 与标签绝对定位覆盖。 */
.pfm-draw-color-row{display:grid;grid-template-columns:repeat(2,24px);gap:6px;row-gap:6px;align-items:center;margin-bottom:8px;padding:2px 0}
.pfm-draw-section-label{grid-column:1/-1;grid-row:1;justify-self:start;font-size:0.7rem;color:var(--pf-muted);font-weight:600;text-transform:uppercase;align-self:center}
.pfm-dcolor{width:24px;height:24px;border-radius:var(--pf-r-sm);cursor:pointer;transition:transform var(--pf-dur-1);flex-shrink:0;justify-self:center;box-sizing:border-box}
/* 2026-09-03: 手机端 9 列 + 标签绝对定位（标签不占 grid 流，色块每行从最左列开始 → 换行后行/列逐格对齐） */
@media(max-width:768px),(orientation:portrait) and (max-width:1024px){
  .pfm-draw-color-row{position:relative;grid-template-columns:repeat(9,1fr);gap:4px;row-gap:6px;padding:2px 2px 2px 48px;margin-bottom:8px}
  .pfm-draw-section-label{position:absolute;left:2px;top:50%;transform:translateY(-50%);width:44px;font-size:0.65rem;grid-column:auto;grid-row:auto}
  .pfm-dcolor{width:22px;height:22px}
}
.pfm-dcolor:hover{transform:scale(1.2)}
.pfm-dcolor.sel{box-shadow:0 0 0 2px var(--pf-green),0 0 0 4px #fff}
.pfm-dgradient{position:relative;border:1.5px solid rgba(124,58,237,0.55);box-shadow:0 0 4px rgba(124,58,237,0.25),inset 0 0 0 1px rgba(255,255,255,0.35)}
.pfm-dgradient::after{content:'✦';position:absolute;top:-3px;right:-3px;width:10px;height:10px;background:#7C3AED;color:#fff;font-size:7px;line-height:10px;text-align:center;border-radius:var(--pf-r-full);box-shadow:0 1px 2px rgba(0,0,0,0.3)} /* 2026-09-03: 徽章内缩不撑高行 */
.pfm-dgradient.sel{box-shadow:0 0 0 2px #7C3AED,0 0 0 4px #fff,0 0 8px rgba(124,58,237,0.5)}
/* 2026-09-03: 自定义渐变入口 + 搭建器 */
.pfm-gcust{display:flex;align-items:center;justify-content:center;border-radius:var(--pf-r-sm);border:1px solid rgba(124,58,237,0.35)}
.pfm-gcust-plus{font-size:14px;font-weight:600;color:#fff;line-height:1;text-shadow:0 1px 2px rgba(0,0,0,0.35);pointer-events:none}
/* 2026-09-30（用户裁决 甲）：自定义渐变搭建器 = **独立窗口**（与色轮 .pfm-wheel-modal 同款）：
   position:fixed 居中 + 半透明遮罩，挂在 #pfmDrawModal 的**直接子层**（不进 #pfmDrawBox 内容流）。
   原先它是流式块（margin:2px 0 8px 62px）⇒ 点开就"长在工具区"里。z-index 与色轮同级（460）。
   🔴 尺寸/字号/圆角全部照抄色轮那一套，保证两个取色窗口观感一致。 */
.pfm-gcust-modal{position:fixed;inset:0;z-index:460;display:flex;align-items:center;justify-content:center}
.pfm-gcust-ov{position:absolute;inset:0;background:rgba(15,23,42,0.35)}
.pfm-gcust-box{position:relative;width:min(340px,92vw);max-height:92vh;overflow:auto;background:var(--pf-white);border-radius:var(--pf-r-lg);padding:14px 16px 16px;box-shadow:0 18px 48px rgba(15,23,42,0.28)}
.pfm-gcust-head{display:flex;align-items:center;gap:8px;margin-bottom:12px}
.pfm-gcust-cap{flex:1 1 auto;min-width:0;font-size:0.72rem;font-weight:600;color:var(--pf-muted);text-transform:uppercase;letter-spacing:0.04em}
.pfm-gcust-prev{width:26px;height:26px;border-radius:var(--pf-r-sm);border:1px solid var(--pf-border);flex-shrink:0}
.pfm-gcust-x{width:28px;height:28px;flex-shrink:0;border:1px solid var(--pf-border);border-radius:var(--pf-r-sm);background:var(--pf-white);color:#5F5E5A;font-size:0.75rem;cursor:pointer}
.pfm-gcust-x:hover{border-color:#9D5BFF;color:#7C3AED}
.pfm-gcust-row{display:flex;align-items:center;gap:8px}
.pfm-gcust-row input[type="color"]{width:34px;height:34px;border:1px solid var(--pf-border);border-radius:var(--pf-r-sm);padding:0;cursor:pointer;flex-shrink:0}
.pfm-gcust-arrow{color:#7C3AED;font-weight:600;font-size:0.9rem}
.pfm-gcust-apply{margin-left:auto;flex-shrink:0;height:34px;padding:0 14px;border:none;border-radius:var(--pf-r-md);background:linear-gradient(135deg,#7C3AED,#EC4899);color:#fff;font-size:0.78rem;font-weight:600;cursor:pointer}
.pfm-gcust-apply:hover{filter:brightness(1.08)}
/* ===== 2026-09-17 取色增强：色轮按钮 + 色相环 + SV 方块 + 最近颜色 ===== */
.pfm-wheel-btn{border:none;padding:0;position:relative;background:conic-gradient(from -90deg,#f00,#ff0,#0f0,#0ff,#00f,#f0f,#f00)}
.pfm-wheel-btn::after{content:"";position:absolute;inset:4px;border-radius:var(--pf-r-full);background:var(--pf-white);box-shadow:inset 0 0 0 1px rgba(15,23,42,0.06)}
.pfm-wheel-modal{position:fixed;inset:0;z-index:460;display:flex;align-items:center;justify-content:center}
.pfm-wheel-ov{position:absolute;inset:0;background:rgba(15,23,42,0.35)}
.pfm-wheel-box{position:relative;width:min(424px,92vw);max-height:92vh;overflow:auto;background:var(--pf-white);border-radius:var(--pf-r-lg);padding:14px 16px 16px;box-shadow:0 18px 48px rgba(15,23,42,0.28)}
.pfm-wheel-head{display:flex;align-items:center;gap:8px;margin-bottom:12px}
.pfm-wheel-cap{font-size:0.72rem;font-weight:600;color:var(--pf-muted);text-transform:uppercase;letter-spacing:0.04em}
.pfm-wheel-prev{width:26px;height:26px;border-radius:var(--pf-r-sm);border:1px solid var(--pf-border);flex-shrink:0}
.pfm-wheel-hex{flex:1 1 auto;min-width:0;height:30px;padding:0 8px;border:1px solid var(--pf-border);border-radius:var(--pf-r-sm);font-size:0.8rem;font-family:ui-monospace,Menlo,Consolas,monospace;color:#26313F}
.pfm-wheel-hex:focus{outline:none;border-color:#7C3AED;box-shadow:0 0 0 3px rgba(124,58,237,0.12)}
.pfm-wheel-x{width:28px;height:28px;flex-shrink:0;border:1px solid var(--pf-border);border-radius:var(--pf-r-sm);background:var(--pf-white);color:#5F5E5A;font-size:0.75rem;cursor:pointer}
.pfm-wheel-x:hover{border-color:#9D5BFF;color:#7C3AED}
.pfm-wheel-main{display:flex;align-items:center;justify-content:center;gap:16px;flex-wrap:wrap}
.pfm-hue-wrap{position:relative;width:140px;height:140px;flex-shrink:0;cursor:crosshair;touch-action:none}
.pfm-hue{position:absolute;inset:0;border-radius:var(--pf-r-full);background:conic-gradient(from -90deg,#f00,#ff0,#0f0,#0ff,#00f,#f0f,#f00);-webkit-mask:radial-gradient(circle at 50% 50%,transparent 0 50px,#000 50px 70px,transparent 70px);mask:radial-gradient(circle at 50% 50%,transparent 0 50px,#000 50px 70px,transparent 70px)}
.pfm-hue-dot{position:absolute;width:14px;height:14px;margin:-7px 0 0 -7px;border-radius:var(--pf-r-full);border:2px solid #fff;box-shadow:0 0 0 1px rgba(15,23,42,0.3);pointer-events:none;background:transparent}
.pfm-sv{position:relative;width:222px;max-width:100%;height:140px;border-radius:var(--pf-r-md);flex:1 1 180px;cursor:crosshair;touch-action:none;background:linear-gradient(to top,#000,rgba(0,0,0,0)),linear-gradient(to right,#fff,rgba(255,255,255,0)),var(--hue,#f00)}
.pfm-sv-dot{position:absolute;width:14px;height:14px;border-radius:var(--pf-r-full);border:2px solid #fff;box-shadow:0 0 0 1px rgba(15,23,42,0.35);transform:translate(-50%,-50%);pointer-events:none}
.pfm-wheel-recent-lab{font-size:0.68rem;font-weight:600;color:var(--pf-muted);text-transform:uppercase;letter-spacing:0.04em;margin:14px 0 6px}
.pfm-wheel-recent{display:flex;flex-wrap:wrap;gap:5px;min-height:22px;align-items:center}
.pfm-wheel-recent i{width:22px;height:22px;border-radius:var(--pf-r-sm);cursor:pointer;box-shadow:inset 0 0 0 1px rgba(15,23,42,0.12)}
.pfm-wheel-recent i:hover{transform:scale(1.14)}
.pfm-wheel-recent-empty{font-size:0.7rem;color:var(--pf-muted)}
.pfm-wheel-done{margin-top:14px;width:100%;height:38px;border:none;border-radius:var(--pf-r-md);background:linear-gradient(135deg,#7C3AED,#EC4899);color:#fff;font-size:0.82rem;font-weight:600;cursor:pointer}
.pfm-wheel-done:hover{filter:brightness(1.08)}
/* 🔴 原生 <input type=color> 隐藏：色轮已经是完整取色入口（含 hex），且色板行是**固定 9 列**，
   保留它会让「17 色块 + input + 色轮」= 19 个元素排成 2 行 + 1 个孤儿（实测色轮被挤到单独一行）。
   隐藏后 17 + 1 = 18 ⇒ 正好 9+9 两行整齐。input 本身**必须保留**（section 的 input 监听 + 吸管都写它的 value），
   只是不参与 grid 布局。 */
#pfmDrawColorPicker{display:none}
/* 2026-09-25（用户选 B/乙）：Brush + Opacity 由「两个独立行」合并为「一行两个滑轨」。
   wrap + 标签 nowrap ⇒ 极窄屏优雅降级（不会把标签文字挤断）；
   滑轨 flex:1 1 64px / min-width:40px ⇒ 桌面端撑满、窄屏可继续收缩而不被裁。
   🔴 原 markup 上的行内 `flex:1;margin:0 8px` 已移除 —— 行内样式优先级高于本规则，留着会盖掉这里。 */
.pfm-draw-size-row{display:flex;align-items:center;font-size:0.8rem;color:var(--pf-muted);gap:4px;flex-wrap:wrap}
.pfm-draw-size-row label{font-weight:600;white-space:nowrap}
/* basis 取 48px（不是 64px）：wrap 容器按 flex base size 之和判断换行，
   6 个元素在 313px 容器下 basis 合计 317px 会差 4px 换行；降到 48px 后为 285px ⇒ 一行放得下。 */
.pfm-draw-size-row input[type="range"]{flex:1 1 48px;min-width:40px;margin:0 2px}
/* 2026-09-30（用户裁决 甲）：文本选项从「工具区流式块」改为**浮层卡片**
   —— 与 #pfmDrawOpts / #pfmDrawTools 同一套视觉与定位（snippet 的 `_placeCard()` 写 left/top）。
   🔴 为什么不是渐变那种"遮罩全屏的窗口"：文字是**点画布落字**的 ⇒ 本卡没有遮罩、画布全程可点。
   🔴 也正因为它此前**不是** absolute，`_drawBudgetH()` 把它算进"非画布高度"⇒ 桌面端整块下推 41px；
      改成 absolute 后这笔高度直接还给画布（与 09-27「选项区/颜色区卡片化」的收益同源）。
   ⚠️ 本规则**不写 display**：显隐仍由引擎写内联 `flex`/`none`（与选项区同款），此处只管版式。 */
#pfmDrawTextOptions{position:absolute;top:0;left:0;z-index:60;flex-direction:column;align-items:stretch;gap:6px;padding:8px 10px;background:#fff;border:1px solid var(--pf-border);border-radius:var(--pf-r-md);box-shadow:0 10px 28px rgba(17,24,39,.18);width:280px;max-width:calc(100% - 16px)}
.pfm-text-head{display:flex;align-items:center;gap:8px}
.pfm-text-cap{flex:1 1 auto;font-size:0.7rem;font-weight:600;color:var(--pf-muted);text-transform:uppercase;letter-spacing:.02em}
.pfm-text-x{width:24px;height:24px;flex-shrink:0;border:1px solid var(--pf-border);border-radius:var(--pf-r-sm);background:#fff;color:#5F5E5A;font-size:0.7rem;cursor:pointer;line-height:1}
.pfm-text-x:hover{border-color:#9D5BFF;color:#7C3AED}
.pfm-text-row{display:flex;align-items:center;gap:6px}
#pfmDrawTextOptions .pfm-dopt-grp{flex:1 1 auto}
#pfmDrawTextOptions .pfm-dopt-range{max-width:150px}
/* 2026-09-26（批次2 修正）：棋盘格 / 边框 / 圆角 从 **wrap** 移到 **canvas 本体**。
   根因（用户报「绘画区和看到的又不一致」）：画布尺寸受**高度**约束（1366×900 时仅 550），
   而 wrap 是整行宽（858）⇒ 左右各 154px 露出 wrap 的棋盘格，**看着像画布、其实不能画**。
   09-25 另一会话曾用「画布 = wrap 实测宽」消掉这个空档，但弹窗获宽后**高度重新成为瓶颈**
   ⇒ 空档必然重现（实测：1920×1200 各 19px / 1366×900 各 154px / 1366×768 各 214px）。
   ⇒ 正解：让**可见的绘图面 = 画布本体**。wrap 改为透明无边框（只保留 overflow 供缩放裁剪）。
   🔴 边框用 `outline` 而非 `border`：outline **不参与盒模型** ⇒ `getBoundingClientRect().width`
      仍严格等于 size，不会给引擎的 `canvas.width / rect.width` 换算引入 1px 漂移。
   🔴🔴 2026-09-26 **二次修正**（用户报「隐藏 Colour 层，整个绘画区都没了」）：
      棋盘格/边框**再下移到独立垫层 `#pfmDrawChecker`** —— 挂在 `#pfmDrawCanvas` 上时，
      隐藏 Colour 层（那正是主画布）会把画布区连框一起抹掉。 */
.pfm-draw-canvas-wrap{position:relative;overflow:hidden;display:flex;justify-content:center;align-items:flex-start}
/* 2026-09-26 🔴 **绘制顺序修正**：绝对定位层默认盖在普通流元素之上，而 `#pfmDrawCanvas`（= Colour 层）
   恰是唯一还在普通流里的那个 ⇒ 实测真实堆叠（上→下）= Line → Base → 参考底图 → **Colour（最底）**，
   与设计的「垫层 → 底稿 → 描线 → 上色」**正好相反**（参考底图默认不透明，会盖住你在 Colour 上画的东西）。
   修法：让它也变成定位元素（`position:relative;z-index:0`）⇒ 与其余层同为 z=0 层，改由 **DOM 序**决定
   ⇒ 恢复 垫层 < 底稿 < 描线 < 上色 ✓（覆盖层 z≥1 仍在最上）。
   ⚠️ 原注释写的"按 DOM 序（z-index 全 auto）"只在**所有元素都已定位**时成立 —— 当时并非如此。 */
#pfmDrawCanvas{position:relative;z-index:0}
/* 棋盘格 + 圆角 + 边框的**承载垫层**：独立于三张图层画布。
   它排在 wrap 的第一个子元素 ⇒ DOM 序最靠下 ⇒ 天然垫在所有图层之下。
   尺寸/居中由引擎 `_applyDrawSize()` 与图层一同同步（勿单独写死尺寸）。 */
.pfm-draw-checker{position:absolute;left:0;top:0;width:0;height:0;pointer-events:none;transform-origin:0 0;border-radius:var(--pf-r-md);outline:2px solid #3F3F46;box-shadow:0 4px 16px rgba(0,0,0,.10);background-color:#fff;background-image:linear-gradient(45deg,#eee 25%,transparent 25%),linear-gradient(-45deg,#eee 25%,transparent 25%),linear-gradient(45deg,transparent 75%,#eee 75%),linear-gradient(-45deg,transparent 75%,#eee 75%);background-size:20px 20px;background-position:0 0,0 10px,10px -10px,-10px 0}
/* 2026-09-04: 画板缩放 — transform-origin 0 0 配合 JS translate/scale（transform 不扩展滚动区，平移走 JS） */
/* 🔴 2026-09-26（用户报「拖动时图案和网格不同步、落位却对」）：本条上原有的 transform 过渡已**删除**。
   本选择器**只命中 canvas**（上色/底稿/描线三层）；而跟随同一 transform 的其它层全是 div
   （网格 #pfmDrawGridLayer、参考底图、对称轴、蚂蚁线、手柄）本来就没有过渡
   ⇒ 同一帧写入**同一串** transform，canvas 被缓动推迟 0.12s、div 层瞬间到位
   ⇒ 拖动过程中错位（真实 Ctrl+拖实测最大 **30.3px**），松手后 canvas 追上 —— 这正是「落位是对的」。
   ⇒ 删除依据：缩放/平移由滚轮·捏合·Ctrl拖连续驱动，过渡只制造滞后、不产生观感收益；
      顺带消除「过渡未走完时落笔，getDrawPos() 按 getBoundingClientRect() 换算会画偏」的隐患。
   ❌ 别只给单个层加 transform 过渡：要么**所有跟随层一起加**（整体滞后、手感发飘），
      要么**都不加**（跟手 = 现状）。同一份 transform 名单见 hd-draw.js#pfmDrawZoomApply。 */
.pfm-draw-canvas-wrap canvas{display:block;max-width:100%;height:auto;touch-action:none;transform-origin:0 0;cursor:default}
/* 2026-09-25（用户裁决 甲+乙，3 层）：画板图层 + 参考垫层。
   三张图层画布（底稿 #pfmLayerBase / 描线 #pfmLayerLine / 上色 #pfmDrawCanvas）按 DOM 顺序叠放，
   浏览器负责合成 ⇒ 显隐用 display、不透明度用 opacity，不需要 JS 每帧重绘。
   🔴 默认 `width:0;height:0` + 绝对定位 是**半部署安全网**：若 hd-draw.js 晚于本文件上线，
      两张新画布不参与 flex 布局、不占空间 ⇒ 旧逻辑逐像素照旧（零回归）。
   🔴 绘制顺序（z-index 全 auto，按 DOM 序）：垫层 → 底稿 → 描线 → 上色 → 覆盖层(z≥1)。
      所以参考底图天然在最下面、网格/蚂蚁线/对称轴天然在最上面。 */
/* 2026-09-26（用户报「拖动不丝滑」）：把会被 transform 缩放/平移的**大画布**提升为合成层。
   否则每帧 transform 变化都会触发该层**重绘** —— 主画布内部是 1200×1200，放大 1.57× 后
   一帧要栅格化约 1800×1800 像素 ⇒ 拖动发涩。提升后 transform 交给合成器，不再重绘。
   ⚠️ 只提升这几张**大**元素；小的覆盖层（div/手柄）重绘便宜，不提升（省 GPU 内存）。 */
#pfmDrawCanvas,.pfm-draw-layer,.pfm-draw-refback,#pfmDrawGridLayer{will-change:transform}
.pfm-draw-layer{position:absolute;left:0;top:0;width:0;height:0;pointer-events:none;transform-origin:0 0}
.pfm-draw-refback{position:absolute;left:0;top:0;width:0;height:0;pointer-events:none;transform-origin:0 0;background-repeat:no-repeat;background-position:center;background-size:100% 100%}
/* 图层芯片：放在**已有的底部按钮行**左侧（零新增行高）；单层退化模式下整组 display:none */
.pfm-draw-layers{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.pfm-draw-layers .pfm-dopt-chip{height:26px;padding:0 8px;font-size:.7rem}
.pfm-draw-layers #pfmLayerEye{min-width:28px;padding:0;font-size:.8rem}
.pfm-draw-bottom{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-top:4px}
.pfm-draw-bottom-actions{display:flex;gap:10px}
/* 2026-09-16: 画板工具条分类收纳 —— 收起态 1 行。原先 21 个工具平铺 4 行 ≈190px，
   把画布挤到被吸底按钮遮住 81px。分类格沿用 .pfm-dtool 的规格（图标 + 0.5rem 标签）。 */
.pfm-draw-toolbar{margin-bottom:0}
.pfm-draw-cats{display:flex;align-items:center;gap:4px;width:100%;padding-right:40px;flex-wrap:wrap}
.pfm-draw-sp{flex:1 1 auto;min-width:0}
/* 🔴 2026-10-03（用户裁决 甲）：**基础档工具栏全键等宽 34px** —— 让「分类/功能键行」与
   「第 3 行」逐列对齐（用户反馈"两行的键不一样宽、列对不上"）。
   根因：分类键与 flat 键用 `min-width:34px`（**内容撑大**），第 3 行用 `.pfm-dtool{width:34px}`
   （**固定**）；标签 0.55rem + nowrap ⇒ Export 39.4 / Image 38.5 / Shape 37.5… 把键撑过 34。
   修法：把内容压回 34 以内（本行 padding 0 5→3、flat 键 padding 0 6→3、两处标签字号
   0.55rem→0.5rem）⇒ 最长标签 Export 仅需 31.1px ⇒ **全部键恰好 34px**。
   🚫 别把 padding 调回 5/6 或字号调回 0.55rem：长标签会重新把键撑宽、两行列又对不上。
   ⚠️ 手机端：padding/字号这三条在触屏断点里本来就有覆盖 ⇒ 与本批无关；
      只有 `.pfm-dtool[data-label]::after` 的字号会一起从 8.8 变 8px（与分类键拉平）。 */
.pfm-dcat{height:44px;min-width:34px;padding:0 3px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;background:var(--pf-white);border:1px solid var(--pf-border);border-radius:var(--pf-r-sm);cursor:pointer;flex-shrink:0;color:#7C3AED;transition:all var(--pf-dur-1)}
.pfm-dcat:hover{background:#f6f0ff;border-color:#9D5BFF}
/* 2026-09-29（用户裁决 4）：顶栏「面板型」键打开 ⇒ 第二行分类键**整体禁用**（真禁用，不是只去高亮）。
   「不可用必须看得见」—— 与 `.pfm-dtool:disabled` 同一套观感：降透明 + 禁用手势 + 取消悬停反馈。
   🔴 2026-09-29：此前 `.pfm-dcat` **没有任何禁用态样式** ⇒ 补上（`disabled` 若不置灰就是"点了没反应"）。 */
.pfm-dcat:disabled{opacity:.38;cursor:not-allowed}
.pfm-dcat:disabled:hover{background:var(--pf-white);border-color:var(--pf-border)}
.pfm-dcat.sel{background:#F3E8FF;border-color:#7C3AED}
.pfm-dcat-t{font-size:0.5rem;line-height:1;color:var(--pf-muted);font-weight:500;white-space:nowrap}
.pfm-dcat.sel .pfm-dcat-t{color:#7C3AED}
/* 2026-10-24（用户裁决）：当前工具显示格此前与普通分类键完全同款（白底 + 浅灰框 + 同圆角）
   ⇒ 分不清"这是显示格"还是"这是个按钮"。它是**状态指示**（显示当前选中的工具），
   改用深色「显示屏」底：一眼区别于所有白底按钮，且不与 .sel（浅紫选中态）撞色。 */
.pfm-dcat-cur{padding:0 6px;background:#2B2B33;border-color:#2B2B33;color:#fff}
.pfm-dcat-cur .pfm-dcat-t{color:#fff}
.pfm-dcat-cur:hover{background:#3A3A44;border-color:#3A3A44}
.pfm-dcat-cur.sel{background:#2B2B33;border-color:#2B2B33}
.pfm-dcat-cur:disabled{background:#2B2B33;border-color:#2B2B33}
.pfm-dcat-cur:disabled:hover{background:#2B2B33;border-color:#2B2B33}
/* 分组已由分类体现，工具格内原有的 | 分隔符不再需要（筛掉后会变成孤立竖线） */
#pfmDrawTools .pfm-dtool-sep{display:none}
.pfm-dcat-ico{display:flex;align-items:center;justify-content:center;width:16px;height:16px}
.pfm-dcat-ico svg{width:16px;height:16px;display:block}
/* ===== 2026-10-02（甲·续4）建 / 10-03 两次修订：第 2 行「平铺常驻工具键」
   ⚠️⚠️ **本节当前没有任何元素在使用**（零引用，但**刻意保留** —— 见下）：
   10-02 用这套样式把 Erase / Fill 做成"贴在第 2 行的平铺常驻键"（用户「平铺成两个按钮，不要收起」）；
   10-03 用户先挪走 Erase（「橡皮擦，放在第三行，pick 前」）、再挪走 Fill（「把 fill 放第三行，
   在 pick 后面」）⇒ 两个键都回到第 3 行（`.pfm-currow-tools`，纯 `.pfm-dtool` 写法）。
   🔴 **别当死代码删**：这是"想让某个工具直接常驻在第 2 行、点一下即用、不弹卡"的现成做法，
      随时可能复用。⚠️ 原配套的那条手机断点排序覆盖（`order:3`）**已于 2026-10-03 删除**
      （它把 7 个功能键也一起拖进了第 2 行，见下方替换后的注释）⇒ 复用本类名时，
      排序要**自己想办法**：要么给平铺键另起专用类名、要么用 `[data-cat]` 限定，
      **不要再恢复一条无差别的 order 覆盖**（`#pfmDrawCats .pfm-dtool{order:1}` 会把
      不带该覆盖的键甩到上一行）。
   原理（当初为什么这么写）：原先是**分类键**（点开弹浮层卡、卡里才是工具），改成**直接切工具**：
   点一下即用，没有任何开合。
   🔴 类名必须是 `.pfm-dtool`（引擎与 snippet 的工具委托都按 `.pfm-dtool[data-tool]` 过滤），
      且**绝不能带 `.pfm-dcat`** —— 那个类会让 `#pfmDrawCats` 的 click 委托走 `setCat()`
      ⇒ 弹出"收起式卡片"，正是本次要去掉的东西（故另起 `.pfm-dcat-flat` 这个名字）。
   🔴 外观沿用 `.pfm-dcat` 那一套（图标 16px + 0.5rem 灰标签，由上面的通用规则给足），
      只有 `.pfm-dtool` 的默认值必须改回三点：
        · width:34px 见方 ⇒ 改 auto（否则 "Fill" 这类标签会被压出界 —— 实测 Erase 时溢出到 37px）
        · padding:2px    ⇒ 改 0 6px（`gap:0` 改 1px，与分类键一致）
        · `[data-label]::after` 会把 data-label 再画一遍 ⇒ 本键文字走真 DOM（`.pfm-dcat-t`），关掉它
   🔴 `.pfm-dcat.sel .pfm-dcat-t{color:#7C3AED}` 那条挂在 `.pfm-dcat` 上 ⇒ 本键取不到，
      须自己补一条（选中高亮时标签转主色；底框/图标色由 `.pfm-dtool.sel` 通用规则负责）。
   ⚠️ 高度不用写：`#pfmDrawCats > .pfm-dtool` 已给 44px（手机断点 40px），与本节一致。 */
.pfm-dtool.pfm-dcat-flat{width:auto;min-width:34px;padding:0 3px;gap:1px;font-size:inherit}
.pfm-dtool.pfm-dcat-flat[data-label]::after{content:none}
.pfm-dtool.pfm-dcat-flat.sel .pfm-dcat-t{color:#7C3AED}
/* 🔴🔴 2026-10-03（用户裁决「执行：只改触屏档」，**此处规则已删除**）：
   这里原本有一条手机断点规则，把带 `.pfm-dcat-flat` 的第 2 行键一律钉成 `order:3`
   —— 那是 10-02 为「第 2 行平铺常驻工具键」写的（把 Erase / Fill 钉在分类键那一行）。
   当日 Erase / Fill 都搬回第 3 行后它已**无用户**；但 10-03 另一个会话把**第 2 行那 7 个功能键**
   （图片 / 网格 / 对称 / 混合 / 导出 / 复位 / 稳定器）也标成了 `.pfm-dcat-flat`
   ⇒ 这条更具体的排序覆盖**把它们从第 1 行拖进了第 2 行**，破坏了 09-23 定下的「两行对调」。
   实测（390 线上，改前）：第 1 行 = `Pens FX Shape Select Text ●Solid ●Grad Image Grid Sym`（10 个混排）、
   第 2 行 = `Blend Export Reset Stab`（4 个）；用户原话「第一行前七个，在第二行；第三行不动，
   其他都是第一行」。
   ⇒ **删除本条**，让上面手机断点里那条 `#pfmDrawCats .pfm-dtool{order:1}` 正常生效：
      **第 1 行 = 7 个功能键 · 第 2 行 = 7 个分类键 · 第 3 行不动**。
   ⚠️ 将来若要再做「第 2 行平铺工具键」，**别再恢复这条无差别的排序覆盖** ——
      它会把所有带 `.pfm-dcat-flat` 的键（含那 7 个功能键）一起拖走；
      要么限定成 `[data-cat]`（只针对带分类的键），要么给平铺键另起一个专用类名。 */
.pfm-draw-tools.collapsed{display:none}
/* 2026-09-16: 画板工具选项行（Mirror 模式 / Erase 粗细）—— 按需出现，平时不占高度 */
/* ============================================================
   2026-09-27（用户裁决「全部卡片化 · 贴在被点键下方 · 选择即收」）：
   **选项区从「流内一行」改成「浮层卡片」** —— 绘画区上方不再有任何东西占高度。
   🔴 为什么能白赚高度：`hd-draw.js#_drawBudgetH()` 本来就**跳过 position:absolute/fixed** 的元素
      ⇒ 改成绝对定位后预算自动不含它 ⇒ 画布永久变大（桌面 590 → 约 628），
      并且**以后开任何面板都不会再影响画布**（不必再互斥、也不必让位）。
   🔴 定位由 snippet 的 `_placeCard()` 写入 left/top（贴到被点功能键的正下方，并按容器右边界夹取）。
   ⚠️ 不要再给它 `min-height` / 让它参与流布局 —— 那会把省下的高度又吃回去。
   🔴🔴 卡片样式**必须挂在 id 上，不能挂在类上** —— 同一个类名还被
      **参考底图行（#pfmDrawRefBar）** 使用：它是「导入参考图后常驻的控制行」，
      不是「点开即收的面板」。若被一起改成绝对定位，它会竖排并钉在容器左上角、
      压住工具行（线上实测已复现 position:absolute + flexDirection:column）。
      ⇒ 所以下面第一行**恢复流内行规则**给它用，卡片规则只写 id。 */
.pfm-draw-opts{display:none;align-items:center;gap:10px;flex-wrap:wrap;padding:6px 0 2px}
#pfmDrawOpts{position:absolute;top:0;left:0;z-index:60;display:none;flex-direction:column;align-items:stretch;gap:6px;padding:8px 10px;min-width:132px;width:max-content;max-width:calc(100% - 16px);background:#fff;border:1px solid var(--pf-border);border-radius:var(--pf-r-md);box-shadow:0 10px 28px rgba(17,24,39,.18)}
/* 🔴 `width:max-content` 不能省：卡片是 `flex-direction:column` + `align-items:stretch`，
   不写就会**撑满整行**（实测 848px）⇒ `_placeCard()` 的右边界夹取会把 left 拉回最左边
   （点最右侧的稳定器按钮，卡片却跑到左侧）。加上后宽度 = 内容自然宽（约 240px），
   贴到被点按钮下方时最多被夹到容器右侧，视觉上仍在按钮附近。 */
/* 卡片内每行占满卡片宽（原来靠 flex-wrap 横排，现在竖排更好点） */
#pfmDrawOpts > .pfm-dopt{width:100%}
/* 卡片内细窄控件（滑杆）保持紧凑，不要撑爆窄卡片 */
#pfmDrawOpts .pfm-dopt-range{max-width:150px}
/* ============================================================
   2026-10-02（用户裁决 甲）：**工具集回到浮层卡片**（用户原话「把工具展开选项，做成和顶栏一样风格，卡片式」）。
   ⇒ 点分类键 ⇒ 该分类工具**贴被点键下方弹出**（选完工具即收，沿用既有 collapse()），
     和顶栏的 网格/对称/混合/稳定器/颜色 卡片**完全同一套**观感与规则。
   · 视觉与选项卡片同款：白底 / 圆角 10px / 1px 边框 / 浮层阴影 / z-index:60。
   · 定位由 snippet 的 `_placeCard(tools, 被点键)` 写 left/top（宿主 = `.pfm-draw-container`）。
   🔴 `width:max-content` 不能省：`.pfm-draw-tools` 自带 `width:100%`，不覆盖就会横铺整条，
      再被 `_placeCard()` 的右边界夹取拉回最左（点中间的键、卡片却跑到最左边）。
   🔴 保留 `.pfm-draw-tools` 的 `display:flex` / `flex-wrap` / `.collapsed{display:none}`
      —— 本规则不写 display，收起语义仍由那两条管。
   ⚠️ 这是**兄弟节点**（不是 #pfmDrawCats 的子节点）⇒ snippet 的「点外部收起」必须单独排除它。
   2026-10-02（甲·同日）：**第 3 行 = 「当前工具显示格」+ 常驻工具格**（`.pfm-draw-currow`；
      现在装 Erase / Pick），而**浮层卡里的工具格不再占普通流** ⇒ 展开/收起卡片**不改变画布尺寸**
      （零代价）。 */
#pfmDrawTools{position:absolute;top:0;left:0;z-index:60;width:max-content;max-width:calc(100% - 16px);padding:8px 10px;background:#fff;border:1px solid var(--pf-border);border-radius:var(--pf-r-md);box-shadow:0 10px 28px rgba(17,24,39,.18)}
/* 2026-10-02（用户裁决 甲）：**第 3 行** = 常驻的「当前工具显示格」（+ 右侧常驻工具格）。
   显示格本身永远只这一格（工具格另在 `.pfm-currow-tools` 里）⇒ 行高恒 44，不随分类展开变化。
   与分类行同族：白底键 + 4px 间距；`width:100%` 让它在 `.pfm-draw-toolbar`（flex-wrap）里独占一行。 */
.pfm-draw-currow{display:flex;align-items:center;gap:4px;width:100%}
/* 🔴🔴 2026-10-04（用户裁决）：**Guide 选中态 = 紫色外框，与"工具键选中"做区别**。
   语义差异：Erase / Pick / Fill 是**互斥选中**（"我现在用的是这个工具"）⇒ 浅紫底 `#F3E8FF` + 1px 紫边；
   Guide 是**叠加开关**（辅助线，与任何工具并存；且它与网格/对称/混合/稳定器/颜色同属"面板位"互斥组，
   见 hd-draw.js 的 `_exclusivePanels`）⇒ 用**白底 + 紫框**表示"这个辅助开着"，不再和工具键长得一样。
   ⚠️ 框宽用 `box-shadow: inset` 补 1px（视觉 2px），**不是**真写 `border-width:2px` ——
      真加粗会把键从 34×44 撑成 36×46、整行高度与对齐全变（本项目几何铁律：成套数字不能单改一个）。
   ⚠️ 选择器带 id ⇒ (1,2,0) 高于 `.pfm-dtool.sel` (0,2,0)，能稳定压过它；
     文字色仍由 `.pfm-dtool.sel{color:#7C3AED}` 给（紫字保留）。 */
.pfm-draw-currow #pfmDrawGuideBtn.sel{background:#fff;box-shadow:inset 0 0 0 1px #7C3AED}
/* 2026-10-03：第 3 行常驻键组 = **[撤销 / 重做 / 清空] → [Erase → Pick → Fill]**（顺序 = 用户逐次指定）。
   沿革（**一天内四次改口**，别只读最后一句）：
     · Erase 从第 2 行挪回（「橡皮擦，放在第三行，pick 前」）；
     · Fill 也挪回、排在 Pick 之后（「把 fill 放第三行，在 pick 后面」）；
     · 撤销 / 重做 / 清空 从 `#pfmDrawCats` 挪来、**紧跟在「当前工具显示格」右边**
       （「返回前进清空按钮，放第三行」）。
   🔴 2026-10-03（用户裁决 甲·续2）：**两类键统一成「图标 + 文字」** —— 早前"两种长相是故意的、
      别顺手统一"那条**已被用户推翻**，动作键现在也带 `data-label`（文字由 `[data-label]::after`
      画，宽度仍 34px ⇒ 本行总宽不变）。
     · **动作键** = 只有 id（+ `data-label`）、**无** `data-tool` ⇒ 仍不是绘画工具。处理器在引擎里
       按 id 绑定，换容器零影响（含 🗑 长按 600ms 清全部层）。
     · **工具格** = 纯 `.pfm-dtool` + `data-cat` + `data-tool` + `data-label` ⇒ 同款小字。
   🔴 下面那条 `#pfmDrawCurRow .pfm-dtool{height:44px}` **保留**：本行所有格子如今都带
      `data-label`（`.pfm-dtool[data-label]{height:44px}` 已能兜住），但留着它可防将来往本行
      放"无 data-label 的格子"时掉回 34px（手机断点 30px）、比同排矮一截。
      （桌面 / 手机同值 44 —— 与本行工具格实际高度一致，别加手机断点降成 40。）
   ⚠️ 本行现装 **7 格**，真机实测总宽 390 **236**/358 · 834 **236**/793 · 1440 **264**/860
      —— **三档都不换行**；余量不多 ⇒ **再加键前先量宽度**（换行会吃掉画布高度）。
      （同批实测：三档「画布尺寸」与改前逐位相同 592×592 / 358×358 / 793×793 ⇒ 零布局代价。）
   ⚠️ 已知取舍（未改）：本行**不在**"点外部收起"的白名单里（白名单 = `#pfmDrawCats` /
      `#pfmDrawTools` / `#pfmDrawRefCard`）⇒ 点本行会顺手收起开着的小卡片；要豁免就把
      `#pfmDrawCurRow` 加进那份白名单。
   🔴 2026-10-03（用户裁决 甲）：**「当前工具显示格」与工具组之间不再留额外间距**
      —— 原写 `margin-left:6px`，叠加本行自身的 `column-gap:4px` ⇒ 首缝 = **10px**，
      而工具组内部只有 4px ⇒ **首缝是别人的 2.5 倍**（用户截图像素级实测 12 : 5~6）。
      触屏档早就修过（那边 `gap:2px` + 无左边距 ⇒ 全部 2px 均匀），**基础档一直没跟**
      ⇒ 桌面/横屏看起来就是「第 3 行间距不一致」。改后首缝 = 4px = 工具组内部，全行等距。
   沿用 .pfm-dtool 既有观感（高 44 = 本行行高 ⇒ 画布零代价）。 */
#pfmDrawCurRow .pfm-dtool{height:44px}
.pfm-currow-tools{display:flex;align-items:center;gap:4px;margin-left:0;flex:0 0 auto}
.pfm-dopt{display:none;align-items:center;gap:6px}
/* 2026-09-25: 「含两组滑杆」的行（文字 Size/Line · 选区 Tolerance/Feather）撑满整行。
   原为 flex item 按内容宽（约 600px）⇒ 右侧空一片，且滑轨比下方 Brush/Opacity 行
   （块级 flex、天然撑满 620px）明显短。撑满后四行滑轨长度一致。
   🔴 min-width:0 必须有 —— 否则 flex item 的 min-width:auto 会让本行在窄容器下
   固定不收缩（实测 327/296 容器都溢出、数值被画到盒子外）。 */
.pfm-dopt[data-opt="select"]{flex:1 1 100%;min-width:0}
/* 2026-09-30（甲）：原 `.pfm-dopt[data-opt="text"]` 的配对规则已删 —— 该行整体搬进 #pfmDrawTextOptions 浮卡，
   卡片里另给 `#pfmDrawTextOptions .pfm-dopt-range{max-width:150px}`（原来靠 `#pfmDrawOpts .pfm-dopt-range` 限宽）。 */
.pfm-dopt-lab{font-size:0.7rem;color:var(--pf-muted);font-weight:600;text-transform:uppercase;letter-spacing:.02em}
/* 🔴 2026-10-04（用户：「混合模式也增加」+ 裁四栏）：
   混合模式 6 → 16 个 chip。原来是一排 flex，16 个一行装不下（会撑爆卡片宽），
   若改两栏则高 8 行 = 282px（挡画布）。⇒ 取**四栏**：4 行 × 4 = 138px，逻辑分组仍在相邻位。
   ⚠️ 每栏仅 1/4 宽 ⇒ chip 上的文字必须短（Burn/Dodge/Diff/Sat…），全名写在 title 里。 */
.pfm-blend-grid{
  display:grid; grid-template-columns:repeat(4, minmax(0,1fr));
  gap:6px 6px; flex:1 1 auto; min-width:0;
}
.pfm-blend-grid .pfm-dopt-chip{
  width:100%; padding:0 4px; text-align:center; overflow:hidden; text-overflow:ellipsis;
}
.pfm-dopt-chip{height:30px;padding:0 10px;border:1px solid var(--pf-border);border-radius:var(--pf-r-sm);background:#fff;color:#5F5E5A;font-size:0.7rem;font-weight:600;cursor:pointer;transition:all var(--pf-dur-1);white-space:nowrap}
.pfm-dopt-chip:hover{border-color:#9D5BFF;color:#7C3AED}
.pfm-dopt-chip.sel{background:#F3E8FF;border-color:#7C3AED;color:#7C3AED}
/* 2026-10-03（甲批）：芯片禁用态 —— 项目铁律「不可用必须看得见」。（此前 `.pfm-dopt-chip` 没有禁用样式，
   `disabled` 点了没反应会像"坏了"。与 `.pfm-dtool:disabled` 同一套观感：降透明 + 禁用手势 + 取消悬停。） */
.pfm-dopt-chip:disabled{opacity:.38;cursor:not-allowed}
.pfm-dopt-chip:disabled:hover{background:var(--pf-white);border-color:var(--pf-border);color:var(--pf-muted)}
/* 2026-09-25: 参考底图行（#pfmDrawRefBar）三处修补 —— **只针对本行/本 id**，
   绝不动共用的 .pfm-dopt-chip（改它会影响对称 / 网格 / 预设 / 形状全部 chip）。
   ① ~~✕（#pfmRefRemove）命中区撑到 44×44~~ —— **2026-09-28（乙）该按钮已删**，本条作废。
   ② 滑杆 flex-basis 120px 时，换行判定按 **basis** 算（22+120+34+32+34+29+5×8=311 > 295）
      ⇒ 手机上 ✕ 被挤到第二行最左。降到 50px 后合计 ≈249 ⇒ 320 视口也能一行；
      再窄时两个 chip 作为**一组**整体换行（见 .pfm-ref-chips）。**保留 50px 这条**（与 ✕ 无关）。
   ③ .sel 的浅紫底 #F3E8FF 在 30px 小 chip 上太弱 ⇒ 本行改「实心紫 + 白字」，
      "当前选的是 Fit 还是 Fill"一眼可见（只在参考行生效，其他行维持原样）。 */
/* 2026-09-28（乙）：`#pfmRefRemove`（控制行右端 ✕）已删 ⇒ 它的 `position:relative` 与 44px 命中区一并撤。 */
#pfmRefOpacity{flex:1 1 50px;min-width:44px}
/* Fit/Fill 一组：宽屏随行、窄屏**整组**换行（原三件套里的 ✕ 已撤，见上） */
#pfmDrawRefBar .pfm-ref-chips{display:flex;align-items:center;gap:8px;flex:0 0 auto}
#pfmDrawRefBar .pfm-dopt-chip.sel{background:#7C3AED;border-color:#7C3AED;color:#fff}
/* 2026-09-28（用户裁决 B）：**控制行常驻** —— 空态（还没加图）也留在卡片里，只灰掉、不可点。
   用户原话「点开功能卡就是这样，不是加了图片才是这样」⇒ 卡片高度**恒定**、加图不再跳变。
   🔴 用 `.off` 类而不是 `display:none`（那会让卡片先矮后高）。
   🔴 id 覆盖类上的 `display:none`（`.pfm-draw-opts` 类是 display:none，原来靠内联 flex 才显示）。 */
#pfmDrawRefBar{display:flex}
#pfmDrawRefBar.off{opacity:.4;pointer-events:none}
/* 灰态里把 Fit/Fill 的高亮也中性化 —— 否则 40% 的紫块仍像「已选中」，而此时压根没有图 */
#pfmDrawRefBar.off .pfm-dopt-chip.sel{background:none;border-color:var(--pf-border);color:var(--pf-muted)}
/* ============================================================
   2026-09-28（用户裁决）：**参考图卡片**（#pfmDrawRefCard）—— 顶栏「图片」按钮点开后的面板。
   · 视觉/行为与其它顶栏卡片**同一套**：absolute + z-index:60 + 白底 + 圆角 10px + 1px 边框 +
     浮层阴影；定位由 snippet 的 `_placeCard()` 写 left/top；开合由 `.open` 类驱动。
   · 卡片内容 = 缩略图行（`.pfm-ref-head`：最多 3 张 + 虚线加号）+ 选中那张的控制行
     （`#pfmDrawRefBar` 现在住在卡片里，仍复用 `.pfm-draw-opts` / `.pfm-dopt-*` 那套）。
   · 缩略图 **40×40**（09-28 用户裁决「加大缩略图」，原 26）；选中的加紫色描边（box-shadow 外扩，不改尺寸）。
   · 每张右上角小 ✕ **在缩略图内部**（视觉 18px + 2px 白圈；命中区 32×32 由 `::after` 外扩 ——
     手机手指点得中；相邻间距 8px、外扩 7px 单侧 ⇒ 不会抢隔壁缩略图的点击）。
   · 卡片内边距 12/14、行距 8；**控制行常驻**（空态灰掉不可点，见 `#pfmDrawRefBar.off`）⇒ 卡片高度恒定。
   🔴 刻意用**独立类名**（`.pfm-ref-*`），不与 `.pfm-preset-*` 共用 —— 避免被预设相关的
      探针/选择器误统计（`.pfm-preset-add` 那条注释已踩过同类坑）。视觉值保持一致即可。
   🔴 卡片是 absolute ⇒ `_drawBudgetH` 天然不计它 ⇒ 绘画区上方**零常驻行、画布零代价**
      （09-27 那版做成常驻行时桌面画布曾为此少 40px；本版已消除）。 */
#pfmDrawRefCard{position:absolute;top:0;left:0;z-index:60;display:none;flex-direction:column;align-items:stretch;gap:8px;padding:12px 14px;min-width:132px;width:max-content;max-width:calc(100% - 16px);background:#fff;border:1px solid var(--pf-border);border-radius:var(--pf-r-md);box-shadow:0 10px 28px rgba(17,24,39,.18)}
#pfmDrawRefCard.open{display:flex}
.pfm-ref-head{display:flex;align-items:center;gap:8px;padding:0}
.pfm-ref-thumbs{display:flex;align-items:center;gap:8px;flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}
.pfm-ref-thumbs::-webkit-scrollbar{display:none}
.pfm-ref-thumb{position:relative;width:40px;height:40px;flex-shrink:0;padding:0;border:1px solid var(--pf-border);border-radius:var(--pf-r-md);background-color:#fff;background-position:center;background-size:cover;background-repeat:no-repeat;cursor:pointer;transition:border-color var(--pf-dur-1),box-shadow var(--pf-dur-1)}
.pfm-ref-thumb:hover{border-color:#9D5BFF}
.pfm-ref-thumb.sel{border-color:#7C3AED;box-shadow:0 0 0 1px #7C3AED}
/* 2026-09-28（用户裁决 乙）：缩略图上的 ✕ 角标**已撤** ⇒ 删除走「长按 600ms」（见 snippet 的
   `LONGPRESS` 段）。原 `.pfm-ref-del` / `.pfm-ref-del::after` 两条规则一并删除（已核对全项目零引用）。
   ⚠️ 触屏长按必须屏蔽长按选字/系统菜单，否则手机上一按就弹「复制」把删除手势吃掉。 */
.pfm-ref-thumb{-webkit-touch-callout:none;user-select:none;-webkit-user-select:none}
/* 「按住中」反馈：按住 600ms 期间缩略图轻微缩小 + 紫边 ⇒ 用户知道按住生效了（松手/移开立即还原） */
.pfm-ref-thumb.press{transform:scale(.9);border-color:#7C3AED;box-shadow:0 0 0 1px #7C3AED}
.pfm-ref-add{width:40px;height:40px;flex-shrink:0;padding:0;border:1px dashed var(--pf-border);border-radius:var(--pf-r-md);background:none;color:var(--pf-muted);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:border-color var(--pf-dur-1),color var(--pf-dur-1)}
.pfm-ref-add svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round}
.pfm-ref-add:hover{border-color:#9D5BFF;color:#7C3AED}
.pfm-ref-add:disabled{opacity:.35;cursor:not-allowed}
.pfm-ref-add:disabled:hover{border-color:var(--pf-border);color:var(--pf-muted)}
/* 🔴🔴 2026-10-04（用户裁决·甲·第五版）：**画布光标按工具类别分** —— 本段是光标语义的**唯一真源**。
   沿革（一天内五版，别只看最后一版）：① 画布默认 crosshair；② 只把**橡皮**改成 default（第四版）；
   ③ 甲版（本版）**查证五大软件后整体改成"按类别分"**。
   查证结论（2026-10-04，出处见 MEMORY 2026-10-04 流水）：五大软件**没有一家**把画笔光标默认设成十字 ——
     PS 绘画光标默认 = **画笔大小圆圈**（十字是「精确」选项，选框类才默认十字）；
     Krita 默认 = **无光标 + 笔刷轮廓**（"准星"只是 9 种可选形状之一）；
     CSP = **按工具类型分组**设（Cross 只是形状之一）；Procreate = 笔刷尺寸/形状预览；Painter = 增强笔刷还原。
   🔴🔴 2026-10-04（用户裁决·甲·**第六版**）：**跟笔宽走的工具改为 `cursor:none`**（光标本体交给尺寸预览圈）——
      用户澄清「我说的是**画笔**，现在画笔全是箭头」⇒ 第五版的"画笔=箭头"作废。
      现规则（判据 = snippet 的 `SIZED_TOOLS` / `isL`）：
        · **跟笔宽走的（画笔 14 支 + FX + Effect + 橡皮）= 藏系统箭头 + 尺寸预览圈当光标**；
        · 选择类（Rect / Lasso / Wand）= 十字；  · 形状 / 文字 / 填充 / 吸管 = 箭头（679 行默认）；
        · 拖参考图 = move。
      依据：五大软件的画笔光标**全是笔刷轮廓**，且**都把系统箭头藏起来**（PS 画笔大小圈 / Krita 无光标+笔刷轮廓 /
      Procreate 画笔光标 / Painter 笔刷还原）⇒ 圈本身就是光标，才不会"被箭头挡住"（用户上次实测反馈过）。
   🔴 优先顺序 = **源码顺序**（三条同为 0,3,1）：cross → none → `pfm-refmove`(move) ⇒ 拖参考图时 move 胜出。
      别把本行挪到 move 之后/之前破坏这个次序。
   🔴 状态类挂在 `.pfm-draw-container`、由 snippet 的 `showOpts()` 挂/摘（切工具必经之路）⇒ 纯 CSS 派生、天然幂等。
   🚫 原 `.pfm-draw-container.pfm-erase-mode …{cursor:default}` **已删除**：橡皮要的箭头现在就是画布默认值，
      留着等于同义的第二条规则（改一处必漏另一处）。 */
.pfm-draw-container.pfm-cursor-cross .pfm-draw-canvas-wrap canvas{cursor:crosshair}
/* 🔴🔴 2026-10-04（甲·第六版）：**藏掉系统箭头** —— 光标本体 = 尺寸预览圈（`.pfm-size-cursor`）。
   ⚠️ 取 `none` 而不是保留箭头：保留就回到"箭头压住小圈"的老问题（圈已带最小可视直径 20px）。 */
.pfm-draw-container.pfm-cursor-none .pfm-draw-canvas-wrap canvas{cursor:none}
/* 2026-09-28（用户裁决）：参考图「移动」模式 —— 开着时画布光标变 **move**（提示"现在拖的是参考图、不是笔"），
   关掉立刻回到工具自身的光标（选择类 = 十字，跟笔宽走 = 圈，其余 = 箭头）。类加在 `.pfm-draw-container` 上（snippet 的 syncMoveUI 负责挂/摘）。 */
.pfm-draw-container.pfm-refmove .pfm-draw-canvas-wrap canvas{cursor:move}

/* 2026-09-23: 选项行内的「标签 + 滑杆 + 数值」分组 —— 手机端换行时整组移动，
   不把标签孤零零留在行尾。桌面端 flex 默认为 0 1 auto（按内容宽）⇒ 与改前一致。 */
.pfm-dopt-grp{display:flex;align-items:center;gap:6px;min-width:0}
.pfm-dopt-range{flex:1 1 120px;min-width:90px;max-width:220px;margin:0}
/* 2026-09-25: 26px → 34px —— 原宽装不下 "1.20" / "100%"（会溢出/挤到下一行）。
   加宽后四个滑轨行（Size·Line·Brush·Opacity）的数值列宽完全一致。 */
.pfm-dopt-val{width:34px;text-align:center;font-size:0.75rem;color:var(--pf-muted)}
/* 2026-09-20 第 1 批：对称轴虚线覆盖层 + 画板内取色气泡（均为 DOM 层，不进 canvas 像素）
   2026-09-21 第 5 批：轴位置可拖 ⇒ 位置改由 --symx / --symy（%，引擎按画布逻辑比例写入）。
   未写变量时回退 50% ⇒ 与改前完全一致（零回归）。 */
.pfm-draw-sym{position:absolute;display:none;pointer-events:none;z-index:2}
.pfm-draw-sym i,.pfm-draw-sym b{position:absolute;display:none}
/* 🔴 2026-10-04：对称轴整层继承画布 transform ⇒ 线宽/虚线节距会被一起放大。
   用 `--hd-zi`（= 1/画布缩放，由 `_symUISync` 写）反向补偿 ⇒ 任何缩放下都是 1px 线、5/5 虚线。 */
.pfm-draw-sym i{top:0;bottom:0;left:var(--symx,50%);width:calc(1px * var(--hd-zi,1));
  background:repeating-linear-gradient(180deg,#7C3AED 0 calc(5px * var(--hd-zi,1)),
    rgba(124,58,237,0) calc(5px * var(--hd-zi,1)) calc(10px * var(--hd-zi,1)))}
.pfm-draw-sym b{left:0;right:0;top:var(--symy,50%);height:calc(1px * var(--hd-zi,1));
  background:repeating-linear-gradient(90deg,#7C3AED 0 calc(5px * var(--hd-zi,1)),
    rgba(124,58,237,0) calc(5px * var(--hd-zi,1)) calc(10px * var(--hd-zi,1)))}
.pfm-draw-sym.pfm-sym-v i,.pfm-draw-sym.pfm-sym-h b{display:block}
/* 2026-09-21 第 5 批：对称轴拖拽层 —— 与画布同 box / 同 transform，四条臂**伸到画布外**，
   末端的圆钮才是抓手（画布区域内 pointer-events:none ⇒ 绝不吞笔画）。
   轴位置由 --symx/--symy 驱动，与虚线层共用同一组变量（永远重合）。 */
.pfm-sym-drag{position:absolute;display:none;pointer-events:none;z-index:3;transform-origin:0 0}
.pfm-sym-drag b{position:absolute;display:none;pointer-events:auto}
.pfm-sym-drag b::after{content:'';position:absolute;left:50%;top:50%;width:26px;height:26px;margin:-13px 0 0 -13px;border-radius:var(--pf-r-full);background:rgba(124,58,237,0.14)}
.pfm-sym-drag.pfm-symdrag-v b[data-arm=v]{display:block;left:var(--symx,50%);top:0;bottom:0;width:1px;margin-left:-0.5px;background:rgba(124,58,237,0.35);cursor:ew-resize;touch-action:none}
.pfm-sym-drag.pfm-symdrag-h b[data-arm=h]{display:block;top:var(--symy,50%);left:0;right:0;height:1px;margin-top:-0.5px;background:rgba(124,58,237,0.35);cursor:ns-resize;touch-action:none}
/* 抓手圆钮：露在画布外的两端（-22px）。画布内那半仍然可点（pointer-events:auto），
   但它只是一个 26px 热区的**圆心在画布外**的圆 ⇒ 对画布中央的绘制影响可忽略。 */
.pfm-sym-drag.pfm-symdrag-v b[data-arm=v]::after{left:50%;top:-22px}
.pfm-sym-drag.pfm-symdrag-h b[data-arm=h]::after{left:-22px;top:50%}
.pfm-sym-drag.pfm-symdrag-h b[data-arm=h]::before{content:'';position:absolute;left:auto;right:-22px;top:50%;width:26px;height:26px;margin:-13px 0 0 -13px;border-radius:var(--pf-r-full);background:rgba(124,58,237,0.14)}
/* 形状填充行里的说明文字（Shift 提示） */
.pfm-dopt-hint{font-size:.7rem;color:var(--pf-muted);white-space:nowrap}
/* 2026-09-23: 画板内「Recent」色带的样式已删除（.pfm-draw-recent / .pfm-recent-strip /
   .pfm-recent-dot）—— 当前色改由 Colour 按钮上的圆点 #pfmDcatDot 显示。
   ⚠️ 色轮面板那份「Recent」用的是另一组类名（.pfm-wheel-recent*），**本文件保留不动**。 */
.pfm-sym-drag b.dragging::after{background:rgba(124,58,237,0.34)}
/* 2026-09-21 第 6 批：形状分色块（Stroke / Fill）。
   🔴 只针对这两个 id 加样式，**不动共用的 `.pfm-dopt-chip`**（改它会影响对称/网格/预设全部 chip）。
   底色由脚本写入的 `--c` 变量驱动（不写 inline background，遵循项目洁癖）；
   未启用（无 `.on`）时为**灰底空心示意** —— 表示「沿用主色」而不是某个具体颜色。 */
#pfmShapeStrokeColor,#pfmShapeFillColor{
  width:30px;padding:0;position:relative;
  background:repeating-conic-gradient(#e5e7eb 0% 25%, #fff 0% 50%) 50%/8px 8px;
}
#pfmShapeStrokeColor.on,#pfmShapeFillColor.on{
  background:var(--c,#000);border-color:#7C3AED;box-shadow:0 0 0 2px rgba(124,58,237,.18);
}
.pfm-sym-drag b::after{box-shadow:0 0 0 1px rgba(124,58,237,0.45) inset;transition:background var(--pf-dur-1)}
.pfm-sym-drag b:hover::after{background:rgba(124,58,237,0.28)}
/* 对称行里的第二个小标题（Fill）——与前面的 chip 拉开一点距离 */
/* 2026-09-21 第 6 批启用（第 5 批预留）：形状描边行里 "Fill" 小标签，靠左拉开与 Stroke 的距离 */
.pfm-dopt-lab-2{margin-left:8px;font-size:.7rem;color:var(--pf-muted);font-weight:600;text-transform:uppercase;letter-spacing:.02em}
/* 轴所在的那条臂高亮（拖拽中） */
.pfm-sym-drag.dragging b[data-arm]{background:rgba(124,58,237,0.7)}
/* 2026-09-20 第 2 批：选区蚂蚁线覆盖层 —— 独立 canvas，与主 canvas 同尺寸同 transform。
   position:absolute 是关键：wrap 是 flex 容器，若参与流布局会与主 canvas 并排。
   toDataURL 只取主 canvas ⇒ 选区线天然不会落到产品上。 */
.pfm-sel-layer{position:absolute;left:0;top:0;display:none;pointer-events:none;z-index:2;
  transform-origin:0 0;max-width:100%;height:auto}
.pfm-sel-layer.on{display:block}
/* 2026-09-29（用户裁决 甲）：参考图对齐辅助线（十字）—— 外观**照抄设计台 `assets/hd-align.js`**：
   洋红 #FF2D95、虚线周期 7+4px、**命中才亮**（没命中时整层 opacity:0 ⇒ 屏幕上什么都没有）。
   🔴 与设计台的唯一实现差异：设计台画在 fabric 叠加上下文，画板**必须是 DOM 层** ——
      画在 canvas 上会进 toDataURL ⇒ 污染导出与撤销栈。
   🔴 显隐一律只用 opacity（画板五层铁律，禁 display:none）；
   🔴 层覆盖整个 wrap（inset:0），两条线的 left/top 由 snippet 按「画布屏幕矩形」现场写 px ⇒ 缩放平移后仍准。
   z-index 4 = 本族最上（网格 1 < 选区 2 < 对称轴 3 < 参考线 4），仍在参考图卡片（在 wrap 外、z 60）之下；
   ⚠️ 新层是 absolute ⇒ `_drawBudgetH()` 第 727 行会跳过它，**画布尺寸零代价**。 */
.pfm-ref-guides{position:absolute;left:0;top:0;right:0;bottom:0;pointer-events:none;opacity:0;z-index:4;transition:opacity var(--pf-dur-1)}
.pfm-ref-guides.on{opacity:1}
.pfm-ref-guides b{position:absolute;display:block;opacity:0}
.pfm-ref-guides b.on{opacity:1}
/* 2026-09-29（用户裁决 乙）：**未命中的那条轴 = 淡态**（拖动中常显做参照），命中轴保持全亮。
   ⇒ 平时也看得见"十字"，且一眼看出是哪一轴对齐了。 */
.pfm-ref-guides b.on.idle{opacity:.32}
/* 🔴🔴 2026-10-04（甲/乙·用户裁决·第五版）：**尺寸预览 = 只有一个圈（ring）**，且**只有橡皮**有。
   与对称轴 / 参考图辅助线同族：**DOM 覆盖层**，绝不画进 canvas（会污染导出与撤销栈）；
   整层 `pointer-events:none` ⇒ 不吞手势。容器 left/top = 指针坐标，圈相对中心居中。
   · ring = 落笔直径（真实 < 20px 时按 20px 画，JS 挂 `.sized-up` 切点线）。
   🚫 已删：`.pfm-size-cursor-val`（三版，数值）· `.pfm-size-cursor-cross`（五版，十字）——
      最后一次删十字的理由：判据收窄为"只有橡皮显示圈"、而橡皮又明确不要十字 ⇒ 十字没有显示场景。
   z-index 5 = 本族最上（网格 1 < 选区 2 < 对称轴 3 < 参考线 4 < 预览圈 5），远低于浮动卡片（60）。 */
.pfm-size-cursor{position:absolute;left:0;top:0;z-index:5;display:none;pointer-events:none}
/* 🔴🔴 2026-10-04（甲·用户裁决）：**吸管取色浮标** —— 取色点旁弹一个**实色块**。
   与尺寸预览圈 / 对称轴 / 参考图辅助线同族：**DOM 覆盖层**，绝不画进 canvas（会污染导出）；
   `pointer-events:none` ⇒ 不吞手势。位置由引擎 `_pickSwatch()` 写 left/top（鼠标 clientX/Y 换算）。
   贴指针右侧 14px 出现（`translate(14px,-50%)`）；靠 `.on` 切换 opacity 淡出（引擎 1.2s 后摘除）。
   🔴 2026-10-04（用户裁决）：**只要色块** —— 原「色块 ＋ #RRGGBB」的 `<span>` 已删 ⇒ 容器由
      `display:flex` 改回 `display:block`、padding 收成四周等距 4px
      （否则右侧留着 9px 空档，看着像"缺了块文字"）。色值改看底栏常驻的 `.pfm-cur-color`。
   z-index 8：比尺寸预览圈（5）更高 —— 取色时两者一般不同现，但取色浮标信息性更强，给高层级。 */
.pfm-pick-swatch{position:absolute;left:0;top:0;z-index:8;display:block;
  padding:4px;border-radius:var(--pf-r-md);background:rgba(17,24,39,.9);pointer-events:none;
  opacity:0;transform:translate(14px,-50%);transition:opacity var(--pf-dur-1)}
.pfm-pick-swatch.on{opacity:1}
.pfm-pick-swatch i{display:block;width:18px;height:18px;border-radius:var(--pf-r-sm);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.4);background:#000}
/* 透明区域取色：色块改画**斜纹**（"无颜色"的通用视觉语言） */
.pfm-pick-swatch i.empty{background:
  repeating-linear-gradient(45deg,#8b8b8b 0 3px,#d9d9d9 3px 6px)}
/* 🔴🔴 2026-10-04（乙·用户裁决）：**底栏「当前色」指示块**（对标 PS 的前景色方块）。
   住在常驻行 `.pfm-draw-size-row` 里 ⇒ 任何时候都看得到当前笔色。
   背景由 snippet 的 `curColorSync()` 写行内 = `#pfmDrawColorPicker.value`；
   `flex:0 0 auto` ⇒ 不拉伸、不挤两对滑杆；尺寸走 token。 */
.pfm-cur-color{flex:0 0 auto;width:22px;height:22px;border-radius:var(--pf-r-sm);
  border:1px solid var(--pf-border);background:#000;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.18)}
.pfm-size-cursor-ring{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  border:1px dashed rgba(124,58,237,.9);border-radius:50%;box-sizing:border-box;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.55)}
/* 被放大显示（真实比最小直径细）⇒ 点线 + 半透明：一眼看出"实际比这更细" */
.pfm-size-cursor-ring.sized-up{border-style:dotted;border-color:rgba(124,58,237,.62)}
/* 🚫 2026-10-04（第五版）：`.pfm-size-cursor-cross`（圆心十字）**已删** ——
   判据收窄为"只有橡皮显示圈"，而橡皮明确不要十字 ⇒ 无任何显示场景。 */
/* 🚫 2026-10-04（用户裁决·第三版）：`.pfm-size-cursor-val`（圈下方的尺寸数值）**已删** —— 用户不要。 */
/* 🔴 2026-09-29（实拍抓到）：线的**长度/起止**必须由 JS 按「画布屏幕矩形」写（top/height、left/width），
   绝不能在这里写 `top:0;bottom:0` —— 那会让线铺满整个 wrap（绘画区比画布大）⇒ 虚线伸到画布之外。
   设计台那条铁律同理：线只画在**打印区内**。 */
.pfm-ref-guides b[data-axis=v]{width:2px;margin-left:-1px;background-image:repeating-linear-gradient(to bottom,#FF2D95 0 7px,rgba(255,45,149,0) 7px 11px)}
.pfm-ref-guides b[data-axis=h]{height:2px;margin-top:-1px;background-image:repeating-linear-gradient(to right,#FF2D95 0 7px,rgba(255,45,149,0) 7px 11px)}
/* 2026-09-20 第 3 批：网格覆盖层（DOM + CSS 渐变，不进 canvas 像素）
   z-index 1 < 选区层 2 < 取色气泡 3，保证蚂蚁线始终压在网格之上。 */
.pfm-draw-grid{position:absolute;display:none;pointer-events:none;z-index:1;transform-origin:0 0;
  /* 🔴 2026-10-04：**线宽屏幕恒定**（间距仍随缩放 —— 那才对得上内容）。
     `--hd-zi` = 1/画布缩放，由 `_gridSync` 写；不补偿时 1px 线在 2× 缩放下变 2px。 */
  background-image:linear-gradient(to right,rgba(17,24,39,.16) calc(1px * var(--hd-zi,1)),transparent calc(1px * var(--hd-zi,1))),
                   linear-gradient(to bottom,rgba(17,24,39,.16) calc(1px * var(--hd-zi,1)),transparent calc(1px * var(--hd-zi,1)));
  background-size:40px 40px}
.pfm-draw-grid.on{display:block}
.pfm-draw-grid i{display:none;position:absolute;left:0;top:0;right:0;bottom:0;
  background-image:linear-gradient(to right,rgba(236,72,153,.5) 1px,transparent 1px),
                   linear-gradient(to bottom,rgba(236,72,153,.5) 1px,transparent 1px);
  background-size:33.3333% 33.3333%}
.pfm-draw-grid.thirds i{display:block}
/* 画笔预设格 —— 背景即该笔刷的颜色/渐变，点=应用、长按=删除 */
.pfm-preset-row{display:flex;align-items:center;gap:6px;flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none;padding:2px 0;margin:-2px 0}
.pfm-preset-row::-webkit-scrollbar{display:none}
.pfm-preset{width:28px;height:28px;border:1px solid var(--pf-border);border-radius:var(--pf-r-sm);cursor:pointer;padding:0;flex-shrink:0;transition:transform var(--pf-dur-1),border-color var(--pf-dur-1)}
.pfm-preset:hover{border-color:#9D5BFF;transform:translateY(-1px)}
/* 2026-09-26（用户裁决「甲」）：预设行已从选项区**搬到画板底栏**（图层芯片 ⇢ 动作区 之间的空白）。
   `flex:1 1 0`（basis 必须是 0 而不是 auto —— 否则宽内容会按内容宽参与换行判定，把动作区挤走），
   `min-width` 给一个下限保证窄屏还能看见 1~2 个色块，再窄就整体换行。
   `justify-content:flex-end` ⇒ 色块紧贴动作区左侧，正好落在用户指的那块空白上。 */
.pfm-draw-presets{flex:1 1 0;min-width:62px;display:flex;justify-content:flex-end;min-height:0}
/* 🔴 底栏内色块压到 **26px**（= 图层芯片那排的高度）：色块原为 28px，若保持原尺寸，
   ≤430 断点下「第一行」的内容高会从 26 → 28 ⇒ 底栏 +2px、画布 −2px（实测 360 档 313→311）。
   26px 里 10px 的编号照样清楚，且 492 那档能多露出半个色块。 */
.pfm-draw-presets .pfm-preset,.pfm-draw-presets .pfm-preset-add{width:26px;height:26px}
/* 「+」= 点击即保存当前笔刷（原「+ Save」按钮）。虚线框 + 弱色，与实心色块区分。 */
.pfm-preset-add{width:28px;height:28px;flex-shrink:0;padding:0;border:1px dashed var(--pf-border);border-radius:var(--pf-r-sm);background:none;color:var(--pf-muted);font-size:1rem;line-height:1;cursor:pointer;transition:border-color var(--pf-dur-1),color var(--pf-dur-1)}
.pfm-preset-add:hover{border-color:#9D5BFF;color:#7C3AED}
.pfm-pick-tip{position:absolute;left:50%;bottom:10px;transform:translateX(-50%);padding:5px 12px;border-radius:var(--pf-r-sm);background:rgba(17,24,39,.88);color:#fff;font-size:.72rem;font-weight:600;white-space:nowrap;pointer-events:none;opacity:0;transition:opacity var(--pf-dur-2);z-index:3}
.pfm-pick-tip.on{opacity:1}
/* 2026-09-21 第 4 批：选区变换手柄 —— 纯 DOM（不是 canvas 像素）
   ⇒ 不污染 toDataURL / 撤销栈 / 落产品，与蚂蚁线/网格/对称轴同一思路。
   z-index 5 > 对称轴拖拽 3 > 取色气泡 3 > 蚂蚁线 2 > 网格 1，保证手柄永远在最上层可点
   （第 5 批：对称轴拖拽层占了 3，选区手柄从 4 提到 5，避免与抓手抢层级）。
   手柄 pointer-events:auto 会拦下画布落笔 —— 这正是要的：拖手柄时不能同时落笔。 */
.pfm-sel-hd{position:absolute;left:0;top:0;display:none;pointer-events:none;z-index:5;
  transform-origin:0 0;max-width:100%;height:auto}
.pfm-sel-hd.on{display:block}
.pfm-sel-hd-frame{position:absolute;transform-origin:center center;box-sizing:border-box;
  /* 🔴🔴 2026-10-04（用户报「画布放大时控制点也放大了」）：**框线也屏幕恒定**。
     变量 `--hd-inv-x` 由 `selHandlesSync()` 写入 = 1/(变换缩放 × 画布缩放)；
     整层继承画布的 `scale(z)` ⇒ 不补偿时 1px 虚线在 2× 缩放下会糊成 2px。
     `max(0.5px,·)` 兜底：缩得很小时不至于被四舍五入成 0 ⇒ 框线不会消失。 */
  border:var(--pf-sel-bw) solid var(--pf-sel-color);
  border-width:max(0.5px,var(--pf-sel-bw) * var(--hd-inv-x,1))}
.pfm-sel-hd-frame::before{content:'';position:absolute;left:50%;
  top:calc(-1 * var(--pf-sel-stem));width:var(--pf-sel-bw);height:var(--pf-sel-stem);
  margin-left:calc(var(--pf-sel-bw) / -2);background:var(--pf-sel-color);pointer-events:none;
  /* 🔴 2026-10-04：旋转杆（框顶那根 23px 细杆）也屏幕恒定 —— origin 钉在**底端**（= 框顶），
     反向缩放后长度与粗细都不随画布放大而变（实测改前 31→61px @ ×1.97）。 */
  transform-origin:50% 100%;transform:scale(var(--hd-inv-x,1),var(--hd-inv-y,1))}
.pfm-sel-hd-frame i{position:absolute;display:block;
  width:var(--pf-sel-corner);height:var(--pf-sel-corner);
  margin:calc(var(--pf-sel-corner) / -2) 0 0 calc(var(--pf-sel-corner) / -2);
  background:var(--pf-sel-color);border-radius:var(--pf-r-sm);pointer-events:auto;box-sizing:border-box;transform-origin:center center}
.pfm-sel-hd-frame i.ed{width:var(--pf-sel-edge);height:var(--pf-sel-edge);
  margin:calc(var(--pf-sel-edge) / -2) 0 0 calc(var(--pf-sel-edge) / -2);
  background:var(--pf-sel-edge-fill);border:var(--pf-sel-bw) solid var(--pf-sel-color)}
.pfm-sel-hd-frame i[data-xf="nw"]{left:0;top:0;cursor:nwse-resize}
.pfm-sel-hd-frame i[data-xf="ne"]{left:100%;top:0;cursor:nesw-resize}
.pfm-sel-hd-frame i[data-xf="sw"]{left:0;top:100%;cursor:nesw-resize}
.pfm-sel-hd-frame i[data-xf="se"]{left:100%;top:100%;cursor:nwse-resize}
.pfm-sel-hd-frame i[data-xf="n"]{left:50%;top:0;cursor:ns-resize}
.pfm-sel-hd-frame i[data-xf="s"]{left:50%;top:100%;cursor:ns-resize}
.pfm-sel-hd-frame i[data-xf="w"]{left:0;top:50%;cursor:ew-resize}
.pfm-sel-hd-frame i[data-xf="e"]{left:100%;top:50%;cursor:ew-resize}
.pfm-sel-hd-frame b{position:absolute;left:50%;top:0;display:block;
  width:var(--pf-sel-rot);height:var(--pf-sel-rot);
  /* 🔴 2026-10-04：旋转点离框顶的位置偏移**也必须补偿** —— JS 给它的 `scale(inv)` 只掰回**大小**，
     `margin` 是布局 px ⇒ 位置仍随画布放大（实测改前：框顶→旋转点中心 22 → 27.6px @ z=1.25）。
     ⚠️ 公式不是"整条 -32px 乘 inv"：`-32px` 里 **-23px 是位置**（要按 1/z 补偿）、
        **-9px 是"把盒子中心对到 left:50%/top:0 锚点"的居中量**（布局尺寸恒为 18 ⇒ 这一半不补偿）。
        整条乘 inv 会剩 9px 残差（实测 19.4 ≠ 22）；正确写法 = `-(23px * inv + 9px)`。 */
  margin:calc(-1 * var(--pf-sel-stem) * var(--hd-inv-y,1) - var(--pf-sel-rot) / 2) 0 0 calc(var(--pf-sel-rot) / -2);
  background:var(--pf-sel-rot-fill);border:var(--pf-sel-rot-bw) solid var(--pf-sel-color);border-radius:var(--pf-r-full);
  pointer-events:auto;cursor:grab;box-sizing:border-box;transform-origin:center center}
.pfm-sel-hd-frame b:active{cursor:grabbing}
/* 🔴 触控热区 28px：手柄视觉只有 11-14px，在平板上根本点不中（实测极难命中）。
   伪元素不是事件目标，点在它上面会冒泡到 i/b 自身 ⇒ elementFromPoint 仍返回手柄本体，
   命中测试与探针断言都不受影响。 */
.pfm-sel-hd-frame i::after{content:'';position:absolute;
  left:calc((var(--pf-sel-corner) - var(--pf-sel-touch)) / 2);top:calc((var(--pf-sel-corner) - var(--pf-sel-touch)) / 2);
  width:var(--pf-sel-touch);height:var(--pf-sel-touch);border-radius:var(--pf-r-full)}
.pfm-sel-hd-frame b::after{content:'';position:absolute;
  left:calc((var(--pf-sel-rot) - var(--pf-sel-touch)) / 2);top:calc((var(--pf-sel-rot) - var(--pf-sel-touch)) / 2);
  width:var(--pf-sel-touch);height:var(--pf-sel-touch);border-radius:var(--pf-r-full)}
.pfm-sel-hd-frame.tiny i.ed{display:none}
/* ===== 2026-10-01（甲 · 浮动文字对象）=====
   落字先浮着：虚线框可拖，点 ✓ 才 fillText 烘焙（✕ / Esc 丢弃）。
   🔴 整块是 DOM 层 —— 与 .pfm-sel-hd 同一套定位法：外层复制画布的
      left/top/宽高/transform，内层按「画布坐标 × 显示比」定位 ⇒ **不进 canvas 像素**，
      故不污染 toDataURL / 撤销栈 / 落产品。
   z-index 6 / 7 = 压在参考线(4) 与选区手柄(5) 之上 —— 它是「将要落下的内容」，不是辅助线。
   ⚠️ 两层都是 absolute ⇒ _drawBudgetH() 会跳过它们，**画布尺寸零代价**（同 .pfm-ref-guides）。
   🔴 显隐只用 display（不是 opacity）：浮动态整体开合，没有过渡语义。 */
.pfm-text-hd{position:absolute;left:0;top:0;display:none;pointer-events:none;z-index:6;
  transform-origin:0 0;max-width:100%;height:auto}
.pfm-text-hd.on{display:block}
.pfm-text-canvas{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none}
.pfm-text-frame{position:absolute;left:0;top:0;width:0;height:0;box-sizing:border-box;
  /* 2026-10-04：与选区框同一批真源（色/线宽）；**保留虚线** —— 语义是「还没烘焙的浮动文字」。
     框线同样补屏幕恒定（--hd-inv-x 由 hd-draw.js 的 _textSync 写入）。 */
  border:var(--pf-sel-bw) dashed var(--pf-sel-color);
  border-width:max(0.5px,var(--pf-sel-bw) * var(--hd-inv-x,1));pointer-events:auto;cursor:move}
/* 🔴🔴 2026-10-02（手机端拖拽不跟手 · 实测根因）：
   `touch-action` **不是继承属性** ⇒ 虽然 .pfm-draw-canvas-wrap canvas 设了 none，浮动态这一族仍是 auto。
   后果：触摸拖动被浏览器判成页面手势 ⇒ 第 4 个 pointermove 就发 pointercancel、位移只走 15%
   （真 CDP 实测：move 3/20、cancel 1、位移 61/402）。
   ⇒ 本族**必须逐个**写 touch-action，画板其它可拖元素（画布 670 / 对称轴 841 / 图层拖拽 275）都是这么做的。
   按钮也一并给 none：顺带消掉触屏点击的 300ms 延迟。 */
.pfm-text-frame,.pfm-text-frame i,.pfm-text-bar,.pfm-text-bar button{touch-action:none}
.pfm-text-bar{position:absolute;left:0;top:0;display:flex;gap:4px;z-index:7;
  transform-origin:0 0;pointer-events:auto}
.pfm-text-bar button{width:26px;height:26px;padding:0;box-sizing:border-box;cursor:pointer;
  display:flex;align-items:center;justify-content:center;font-size:13px;line-height:1;
  background:#fff;color:#534AB7;border:1px solid #534AB7;border-radius:var(--pf-r-sm)}
.pfm-text-bar button:hover{background:#EEEDFE}
.pfm-text-bar button.no{color:#A32D2D;border-color:#A32D2D}
.pfm-text-bar button.no:hover{background:#FCEBEB}
/* 2026-10-01（甲·第二批）：样式行 + 底色 + 四角缩放手柄 -- 全部对齐 Win11 画图的文字工具。 */
.pfm-text-spacer{flex:1 1 auto}
/* 底色取色：平时不占位，只有底色键选中（`.sel`）时才出现 —— 它就是底色键的相邻兄弟。 */
#pfmTextBgColor{display:none;width:30px;height:30px;padding:0;box-sizing:border-box;cursor:pointer;
  background:none;border:1px solid var(--pf-border);border-radius:var(--pf-r-sm)}
#pfmTextBg.sel + #pfmTextBgColor{display:inline-block}
/* 四角缩放手柄：12px 方块，中心落在框角上；反缩放由引擎写 transform（同 .pfm-sel-hd-frame i）。 */
.pfm-text-frame i{position:absolute;display:block;
  width:var(--pf-sel-corner);height:var(--pf-sel-corner);
  margin:calc(var(--pf-sel-corner) / -2) 0 0 calc(var(--pf-sel-corner) / -2);
  background:var(--pf-sel-color);border-radius:var(--pf-r-sm);box-sizing:border-box;pointer-events:auto;
  transform-origin:center center}
.pfm-text-frame i[data-tscale="nw"]{left:0;top:0;cursor:nwse-resize}
.pfm-text-frame i[data-tscale="ne"]{left:100%;top:0;cursor:nesw-resize}
.pfm-text-frame i[data-tscale="sw"]{left:0;top:100%;cursor:nesw-resize}
.pfm-text-frame i[data-tscale="se"]{left:100%;top:100%;cursor:nwse-resize}
/* 触控热区 28px：伪元素不是事件目标 ⇒ elementFromPoint 仍返回手柄本体（与选区手柄同理）。 */
.pfm-text-frame i::after{content:'';position:absolute;
  left:calc((var(--pf-sel-corner) - var(--pf-sel-touch)) / 2);top:calc((var(--pf-sel-corner) - var(--pf-sel-touch)) / 2);
  width:var(--pf-sel-touch);height:var(--pf-sel-touch);border-radius:var(--pf-r-full)}

/* 选区行两个滑杆同排：与 Tolerance 并肩，避免各占 flex:1 1 120px 后换行 */
.pfm-dopt[data-opt="select"]{flex-wrap:wrap}
.pfm-dopt[data-opt="select"] .pfm-dopt-range{flex:1 1 70px;min-width:64px;max-width:150px}
/* 2026-09-22: 「Colour」tab 只在**手机端**出现。
   🔴 为什么必须这样：桌面端颜色区本来就常驻，该 tab 在桌面上**没有任何功能意义**；
      而 `.pfm-draw-cats{flex-wrap:wrap}` ⇒ 多这一个 tab 会让 1366 宽的 tab 行**换行**
      ⇒ 工具栏变高 ⇒ 画布被推下去 ⇒ 底部落点被遮罩吃掉
      （实测 batch5 `V8.1 画布四条边落笔` 从 PASS 变 FAIL，命中 `pfm-modal-overlay`）。
   ⇒ 桌面端直接不渲染它，工具行布局与改前逐字一致（零回归）。 */
#pfmDrawCats .pfm-dcat[data-cat="color"]{display:none}
/* 2026-09-22: Colour tab 上的「当前色」小圆点 —— 复用 .pfm-dcat-ico 的 16×16 容器。
   颜色区在手机端默认收起后，这是画板上**唯一**的当前色指示（绿框 .sel 藏在颜色区里）。
   背景由 drawmodal 的 syncColorDot() 写入，此处只负责形状与描边。 */
/* 2026-09-27（甲）：圆点**只给单色 tab** —— 原来只写 `[data-cat="color"]`，
   现在两个 tab 都带这个属性，不限定模式的话渐变 tab 也会变成黑色圆点。 */
#pfmDrawCats .pfm-dcat[data-cat="color"][data-cmode="solid"] .pfm-dcat-ico{
  width:14px;height:14px;border-radius:var(--pf-r-full);background:#000;
  border:1.5px solid #fff;box-shadow:0 0 0 1px rgba(0,0,0,0.22);box-sizing:border-box;
}
/* 渐变 tab 的图标：一小条品牌渐变，与单色 tab 的圆点区分开。 */
#pfmDrawCats .pfm-dcat[data-cat="color"][data-cmode="grad"] .pfm-dcat-ico{
  width:14px;height:14px;border-radius:var(--pf-r-sm);background:linear-gradient(135deg,#7C3AED,#EC4899);
  border:1.5px solid #fff;box-shadow:0 0 0 1px rgba(0,0,0,0.22);box-sizing:border-box;
}
/* 2026-10-02（用户裁决 甲）：桌面端顶行的颜色开合两键，图标由「调色盘 / 渐变线条」
   换成**当前色指示** —— 单色 = 当前色实心圆点、渐变 = 当前选中那条渐变。
   🔴 为什么换：`#pi-palette`（方框+4 圆点）与 `#pi-grid`（方框+十字）在小尺寸下几乎分不出；
      `#pi-grad`（方框+3 竖线）像条形图 ⇒ 两个键都"看不出是颜色"。
   🔴 样式与手机端 Solid/Grad tab 的圆点**同一套数值**（14→18px：桌面按钮 34px 比手机 tab 更能容下）。
      · 2026-10-03（用户裁决 甲）：桌面色点 **18 → 16px** —— 与同排所有图标
        （`.pfm-dcat-ico` / `svg`，均为 16×16）**同尺寸**。原 18px 会把"色点 + 文字"
        整列撑开 2px ⇒ 实测文字 dy 9.5（其它键 8.5）、距键底 8.1（其它 9.1），
        结果是"图标不在一条线、文字不在一条线"（用户实拍反馈）。
        ⚠️ 手机端 tab（`.pfm-dcat-ico` 14px）**本次未动**；白圈 1.5px + 外阴影 1px 保留。
      ⚠️ 手机端 `#pfmDrawColorBtn,#pfmDrawGradBtn{display:none}` ⇒ 本段天然不影响手机。
      ⚠️ 渐变键**不写死颜色**：没选中渐变时由 JS 清掉内联 background，露出的就是品牌渐变（与手机端一致）。 */
#pfmDrawColorBtn .pfm-dtool-dot,
#pfmDrawGradBtn .pfm-dtool-dot{
  display:block;width:16px;height:16px;border-radius:var(--pf-r-full);background:#000;
  border:1.5px solid #fff;box-shadow:0 0 0 1px rgba(0,0,0,0.22);box-sizing:border-box;
}
#pfmDrawGradBtn .pfm-dtool-dot{border-radius:var(--pf-r-sm);background:linear-gradient(135deg,#7C3AED,#EC4899)}
@media(max-width:768px),(orientation:portrait) and (max-width:1024px){
  .pfm-dopt-chip{height:34px;padding:0 9px}
  .pfm-draw-opts{gap:8px;padding:4px 0 2px}
  /* 2026-09-23: 手机端**选项行允许行内换行**。根因：外层 #pfmDrawOpts 有 flex-wrap，
     但每个 .pfm-dopt 行内部是 display:flex **不换行** ⇒ 内容超宽直接被裁。
     实测（375 视口，可用宽 314px）：
       · Blend 行需 416px ⇒ 第 6 档 Lighten 完全消失、第 5 档 Darken 被裁成「Dar」
       · Size/Line 行需 391px ⇒ 右侧溢出 78px
     换行后：Blend 变 2 行（6 档全可见）；Size 与 Line 各独占一行，
     滑轨 129px → **219/220px**（反而更长更好拖）。
     🔴 画布尺寸不受影响 —— 375 下 294 由**宽度**决定（box.clientWidth-44），竖向余量 113px；
        仅弹窗内容多滚 19px。（已用「选项区拉高到 300px ⇒ 画布缩到 180」自检 resize 确会响应高度。）
     🔴 桌面端**不加此规则** ⇒ 与改前逐像素一致（零回归）。 */
  .pfm-dopt{flex-wrap:wrap}
  /* 2026-09-23: 「含两组滑杆」的行（文字 Size/Line · 选区 Tolerance/Feather）让每组独占一行，
     避免出现「SIZE [滑杆] 48 LINE」这种标签与其滑杆被拆开的排版。
     组内滑杆解除 max-width 上限 ⇒ 手机端滑杆吃满行宽（≈251px），比改前的 129px 更好拖。 */
  /* 🔴 2026-09-25（用户裁决「乙」）：**推翻**下方 09-23 的「每组独占一行」，改为**一行两个滑轨**。
     09-23 让每组独占一行的理由是「Size/Line 一行需 391px、容器仅 313px ⇒ 右侧溢出 78px」，
     但该 391px 的根因是滑轨 min-width:90px（两个就 180px 起）。现把 min-width 降到 40px ⇒
     两行内容最小约 240px，313px 容器放得下 ⇒ 不必再独占一行，且滑轨不被裁。
     代价（用户已知悉并接受）：手机端滑轨由 ≈219px 缩到 ≈76px，换来少占 2 行高度。 */
  /* 🔴🔴 2026-10-04（用户裁决「乙1」）：**加 `min-width:max-content`**（只加这一个属性）。
     根因：本行现在同时装了 **2 个滑杆组**（Tolerance / Feather）**和 12 个操作 chip**
     （后者是同日「选区增强」新加的），而上面那个 flex 简写让两个滑杆组**平分剩余宽度并被压缩**
     （而不是换行）⇒ 窄屏实测组宽 **390→37px · 320→3px**（组内 lab 自己就要 70px）⇒
       ① lab 溢出容器、与相邻 chip **文字重叠**（用户实拍：TOLERANCE 与 FEATHER 糊成一个词）；
       ② 组内滑杆被压到 min-width:40px ⇒ **滑块占满整条**（用户看到的那个"蓝方块"）。
     加 `min-width:max-content` ⇒ 组不小于自身内容宽 ⇒ 超宽时**正常换行**（两个滑杆各占一行），不再压扁。
     宽屏（834/1194）容器本来就够宽 ⇒ 行为不变。
     ⚠️ 别把本行改回单个 flex 简写值；这条是本行在窄屏唯一的不重叠保证。 */
  .pfm-dopt[data-opt="select"] .pfm-dopt-grp{flex:1 1 0;min-width:max-content}
  /* 🔴 flex-basis 必须是 0（不能是 auto/百分比）：wrap 容器按 flex base size 判断是否换行，
     若 basis 取内容宽（≈195px/组）则两组 390px > 313px 容器 ⇒ 仍然换行、等于没改。
     basis=0 后由 flex-grow 平分容器 ⇒ 真正并排。 */
  .pfm-dopt[data-opt="select"] .pfm-dopt-range{max-width:none;min-width:40px}
  /* 2026-09-26（甲·批次2）：颜色区的收起规则已**上移为全局**（见 `.pfm-draw-colors` 之后）。
     桌面端现在也有开合开关（图标行的调色盘按钮 `#pfmDrawColorBtn`）。
     此处只保留「桌面专用按钮不在手机出现」—— 与桌面端隐藏 Colour 分类 tab 的
     `#pfmDrawCats .pfm-dcat[data-cat="color"]{display:none}` 正好**互斥**：一种屏幕只留一个入口。 */
  #pfmDrawColorBtn,#pfmDrawGradBtn{display:none}   /* 2026-09-27（甲）：两个桌面入口都不在手机出现 */
  /* `.pfm-dcat` 基线是 display:flex ⇒ 手机端要显式恢复 */
  #pfmDrawCats .pfm-dcat[data-cat="color"]{display:flex}
}
/* 2026-09-26（乙，用户改口：双击容易出错）⇒ 缩放复位改**按钮** `#pfmDrawZoomReset`（图标行）。
   🔴 2026-09-29（用户裁决 2「长驻，不可用就灰」）：**覆盖**上面「只在缩放中出现」——
      改为**永远显示**；已回到默认视图时由 JS 置 `disabled`（观感走 `.pfm-dtool:disabled`）。
      ⚠️ 必须用 id 选择器：`.pfm-dtool{display:flex}` 是 class 级，id 才能稳定压过它；
      `.on` 规则保留只为兜历史残留（JS 已不再加它）。 */
#pfmDrawZoomReset{display:flex}
#pfmDrawZoomReset.on{display:flex}
#pfmDrawTools .pfm-dtool[data-cat]{display:none}
#pfmDrawTools[data-open="brush"] .pfm-dtool[data-cat="brush"],
#pfmDrawTools[data-open="fx"] .pfm-dtool[data-cat="fx"],
#pfmDrawTools[data-open="shape"] .pfm-dtool[data-cat="shape"],
#pfmDrawTools[data-open="fill"] .pfm-dtool[data-cat="fill"],
#pfmDrawTools[data-open="text"] .pfm-dtool[data-cat="text"],
#pfmDrawTools[data-open="erase"] .pfm-dtool[data-cat="erase"],
#pfmDrawTools[data-open="select"] .pfm-dtool[data-cat="select"],
#pfmDrawTools[data-open="effect"] .pfm-dtool[data-cat="effect"]{display:flex}
@media(max-width:768px),(orientation:portrait) and (max-width:1024px){
  .pfm-dcat{height:40px;min-width:30px;padding:0 3px}
  /* 2026-09-25 丙：第一排常驻按钮跟随分类 tab 一起降到 40px（桌面 44px 见文件上部）。 */
  #pfmDrawCats > .pfm-dtool{height:40px}
  /* 2026-10-03（甲·按钮统一）：窄屏下这三处与分类键/顶栏键不一致，一并归位。
     实测（834×1194 / 470×900，改动前）：第 1/2 行键 40 高、第 3 行 44 高（`:809` 那条没进窄屏断点）；
     第 3 行首格（当前工具显示格，走 `.pfm-dcat`）40 而右侧 6 格 44 ⇒ 同一行内不等高；
     顶栏图标键 min-width 34 vs 分类键 30 ⇒ 同一行里 Pens(30)/Grid(34) 不同宽。 */
  #pfmDrawCurRow .pfm-dtool{height:40px}
  .pfm-dtool.pfm-dcat-flat{min-width:30px;padding:0 3px}
  #pfmCloseDraw{height:40px;top:12px}

  .pfm-dcat-t{font-size:0.5rem}
  .pfm-draw-cats{padding-right:30px;gap:2px}
  /* 2026-09-23（用户裁决「甲」）：工具栏**两行对调** —— 动作图标行搬到第 1 行，
     工具/分类行落到第 2 行（原来是「上=分类、下=图标」）。
     🔴 那两行**不是两个容器**，而是 #pfmDrawCats 一个 flex-wrap 容器自动换行的产物
        ⇒ 必须用 order 显式排序；并把 .pfm-draw-sp（弹性间隔）改造成「占满整行 + 0 高度」的**断行符**
        —— flexbox 没有 break-before，否则图标右边的空位会被后面的分类格填回去（实测确实会混行）。
     🔴 只在手机断点内生效 ⇒ 桌面端逐像素不变（图标仍在最右、同一行）。
     实测：412 完美对调（2 行、总高 86→88、画布 327 不变）；
          375 因「9 个分类格」需 289px > 容器 284px，Colour 单独落到第 3 行 —— 用户已知并接受。 */
  #pfmDrawCats .pfm-dtool{order:1}
  #pfmDrawCats .pfm-draw-sp{order:2;flex:0 0 100%;height:4px}
  #pfmDrawCats .pfm-dcat{order:3}
  /* 2026-10-03（用户裁决「8px，只改触屏档」）：**三行行距 4/2 → 8/8**。
     🔴 两个数怎么来的（都实测过，别凭感觉改）：
        · 第 1 行 ↔ 第 2 行 = row-gap(2) + **断行符自身高度** + row-gap(2)。
          断行符 .pfm-draw-sp 靠 flex:0 0 100% 独占一行（原本 height:0，只负责断行、不贡献高度）
          ⇒ 把它的高度从 0 改成 **4px**，行距就是 2+4+2 = **8**。
        · 第 2 行 ↔ 第 3 行 = 父容器 #pfmDrawToolbar 的 gap:2px ⇒ 靠第 3 行的 margin-top:6px
          补到 2+6 = **8**（两个 margins 数值都写在本行下方，别顺手改成同一个数）。
     🔴 本块只在触屏断点内 ⇒ **桌面档逐像素不变**（那里只有两行，行距仍是 2px）。
     🔴 画布代价（离线实测）：390/834 **0**（画布受宽度限制）· 桌面若一起改会 −6px（故不改）。 */
  #pfmDrawCurRow{margin-top:6px}
  /* 2026-10-03（用户反馈「第三行没有对齐」·只改触屏档）：**第 3 行回到与上两行同一列网格**。
     🔴 实测根因（360×780 线上，逐格 x）：上两行是「30px 格 + 2px 缝」= 列距 **32**；
        第 3 行是「30px 格 + 4px 缝」且 `.pfm-currow-tools` 还多 `margin-left:6px`
        ⇒ 列距 **34**、首格后多 10px ⇒ 第 2 格右偏 **+8**、第 7 格右偏 **+17**（肉眼就是"没对齐"）。
     ✅ 修法：本行 `gap` 4 → 2、工具组 `margin-left` 6 → 0（两处都在触屏断点内）。
        改后第 3 行 x = 16,48,80,…,208 ⇒ 与第 1 行（16,48,80,…,209）逐格对齐（±1 取整误差）。
     ⚠️ 高度/顶部本来就是对齐的（7 格全 40 高、top 全 162）—— 别误改高度。
     ⚠️ 桌面档不动（那里只有两行，且显示格 37px ≠ 顶栏格 34px，本来就不是同一网格）。 */
  #pfmDrawCurRow{gap:2px}
  .pfm-currow-tools{gap:2px;margin-left:0}
  /* 🔴 2026-10-03（甲·触屏档）：**三行所有键统一 34×40** —— 消灭触屏档的 30/31 混排。
     背景：同一问题另一个会话在**基础档**用"压内容"解决（padding 5/6→3、标签 0.55→0.5rem），
       并在注释里写明「手机端与本批无关」⇒ 触屏档仍是 min-width 30 + 内容撑大：
       实测（440/390）第 1 行 Export=31、第 2 行 Shape=31，其余 30 ⇒ 列位漂移。
     ✅ 本档用**固定宽**收口（内容侧不再需要迁就）：`width` + `min-width` 都钉 34。
     为什么 34：最长内容 = Export 文字 23.1 + padding 6 + border 2 = 31.1 ⇒ 余 2.9px，换机型/字体也不会被撑开。
     ✅ 附带收益：三行"格宽 + 缝"都相同 ⇒ 列距恒 36 ⇒ **三行列位像素级完全对齐**。
     ⚠️ 高度不动（三行本就全 40）。画布不变（实测 360:329 / 440:407）。
     ⚠️ 必须显式写死 width：只写 min-width 会重新出现"文字一宽就把格子撑大"。 */
  #pfmDrawCats > .pfm-dcat,
  #pfmDrawCats > .pfm-dtool,
  #pfmDrawCurRow > .pfm-dcat,
  #pfmDrawCurRow .pfm-dtool{width:34px;min-width:34px}
}

@media(max-width:767px){
  /* 2026-09-13: 画板弹窗在手机端避让设计板顶栏(57px)与底栏(57px)。
     实测根因：底部按钮行 y=787 与设计板底栏 y=787 同起点完全重叠，
     且按钮行下缘(823)超出弹窗可视区(802)被 overflow 裁掉。
     改法：弹窗上下留 62px 内边距 → 居中在可用区；底部按钮行 sticky 常驻，
     内容再长也无需滚动即可点 Apply。 */
  .pfm-draw-modal{padding:62px 0}
  /* 2026-09-26（用户裁决 甲，**仅手机端**）：弹窗**固定宽高**。
     原来只有 max-*，高度是**内容撑**出来的 —— 一旦「导入参考图」，`#pfmDrawRefBar`
     由 display:none 变可见（+40px）就把盒子顶高（实测 390 下 660 → 710，**+50px**）。
     ⇒ 这里把 max-* 同值钉成 width/height。附带收益：宽度钉到 98vw 后容器变宽，
        画布 325 → 356（+31px）。⚠️ 桌面端不动（用户：「只改手机端哦」）。
     ⚠️ 配套两处（缺一不可）：hd-draw.js 的 _drawBudgetH 缓存键在手机端纳入
        「REF 行是否显示」；snippet 在 REF 行显隐后调 __hdDraw.resize()。 */
  .pfm-draw-box{padding:10px 12px 0;max-width:98vw;max-height:calc(100vh - 8px);width:98vw}
  /* 🔴 2026-09-26（用户裁决 甲）：手机端 **底栏贴底**。
     实测（412×915）：盒高 783（=100vh−132）、内容仅 627 ⇒ **不滚动** ⇒ 上面那条 sticky
     无位移可产生 ⇒ 底栏停在内容末尾，其下留 **146px 白**（用户截图那条）。
     ① 本行让容器撑满盒高（手机端父盒是固定 height ⇒ 百分比高度有确定基准）；
        🔴 刻意**不**给 #pfmDrawBox 加 display:flex + 子项 flex:1 —— 内容溢出时
        flex-shrink 会压缩子项 ⇒ **压扁画布**（本项目已踩过的坑，见 _drawBudgetH 注释）。
        用 min-height 则溢出时自然失效 ⇒ 溢出场景与改前逐像素一致（小屏仍靠容器滚动）。
     ② 下面底栏那行的纵向间距由固定值改为「自动」⇒ 吃掉剩余空间 ⇒ 底栏贴到盒底。
     ✅ 收益：留白从「按钮下方」挪到「画布下方」= 用户要的「工具区才留白」；
        画布位置/尺寸零变化（预演实测 Δ0），三按钮距盒底 12px（= 底栏自身下内边距）。
     ⚠️ 561~768 区间在后文把底栏还原为普通流（那边 sticky 会盖住画布下部）
        ⇒ 那一段底栏仍不贴底、本行只让容器撑满（视觉无变化）⇒ 零回归。 */
  .pfm-draw-container{min-height:100%}
  /* 🔴 2026-09-26（用户裁决「绘画区也落底」）：让**画布区**吃掉剩余空间 ⇒ 画布整块沉底，
     多余留白集中到「工具行下方」（用户第 1 条诉求「工具区才留白」的最终形态）。
     🔴 只动纵向间距，**不改 wrap 高度** —— wrap 高 == 画布高 是「可见面 == 可绘面」
        与 clip-path 外框语义的前提。也**不能用长高来落底**：手机端画布是被宽度卡住的
        正方形（wrap 宽 380 / 画布 378），长高即长方形 ⇒ getDrawPos 的 scaleX≠scaleY
        ⇒ 画上去的笔画会被拉变形。
     🔴 上面底栏那条的纵向间距必须还原为固定值：两个 auto 会**平分**剩余空间。
     ⚠️ 内容溢出时本行自动归 0 ⇒ 561~768（刚好装满）与更小屏逐像素照旧。 */
  .pfm-draw-canvas-wrap{margin-top:auto}

  .pfm-draw-bottom{position:sticky;bottom:0;z-index:6;background:#fff;padding:8px 0 12px;margin-top:6px;border-top:1px solid var(--pf-border)}
  .pfm-dtool{width:30px;height:30px;font-size:0.85rem}
  .pfm-dtool-sm{width:26px;height:26px}
  .pfm-dcolor{width:22px;height:22px}
}

/* 2026-09-03 Creator onboarding modal —— 2026-10-14 起弹窗样式**外置**到 assets/hd-signup.css
   （那份随 layout 全站引入；弹窗本身也改成全站渲染，不再只属于设计台）。
   本文件只保留下面的 .hdco-toast —— 设计台通用 toast，位置要跟随设计板。 */
.hdco-toast{position:fixed;top:18px;left:50%;transform:translateX(-50%);z-index:2147483999;background:#1a1a2e;color:#fff;padding:11px 18px;border-radius:var(--pf-r-md);font-size:.84rem;line-height:1.5;max-width:min(92vw,560px);box-shadow:0 8px 30px rgba(0,0,0,.3);text-align:center;pointer-events:none}
.hdco-toast b{color:#a78bfa}

/* ===== 2026-09-14：移动端拖动性能优化 =====
   背景：画布位图 3000²（= 9,000,000 像素），实际显示区仅 808²。
   移动端 GPU 每帧合成 9M 像素纹理 → 拖动掉帧（桌面独显无感，已实测确认）。
   以下为**低风险**优化，仅作用于拖动期间（由 JS 切 .pfm-dragging）：
     ① canvas 提升为独立合成层 → 移动时只做合成，不重新栅格化页面其余部分
     ② contain:paint → 把重绘影响范围限制在本容器内
   注：更彻底的解法是降低画布内部分辨率，但那会牵动 placement 坐标系统与导出链路，
       属于高风险改动，需单独评估。 */

.pfm-canvas-inner.pfm-dragging{contain:paint}
.pfm-canvas-inner.pfm-dragging #pfmCanvas{will-change:transform;transform:translateZ(0)}

/* 2026-09-20: 原「桌面端 Size/Opacity 滑块条预留」的那条 padding-bottom 规则已删（用户裁决 A）——
   该滑块条 09-19 已整条移除（Size→📐Size 区块、Opacity→Effects），预留失去对象。 */

/* 2026-09-18: 抽屉顶部把手**再次**被穿透（用户「你改完，又漏背景了」）—— 治本加固。
   上一轮的修法是「让 #panel-text 自己滚」，但只覆盖了 text（clipart-grid / layers-list 各自另有
   max-height），**#panel-upload / #panel-shape / #panel-pen 没有** ⇒ 任一内容超出则 .pfm-props 整体滚
   ⇒ sticky 把手重回滚动路径 ⇒ ArkWeb 上又穿透。
   本批把「各面板自觉」改成「容器强制」：
     .pfm-props 永不滚 ⇒ 把手与内容永远不在同一滚动路径上 ⇒ **物理上不可能穿透**；
     改由各面板自己滚（max-height 与既有 clipart-grid / panel-text 同一口径 88px）。 */
@media(max-width:768px),(orientation:portrait) and (max-width:1024px){
  /* 2026-09-19: 抽屉改「固定头 + 唯一滚动体」（标准 bottom sheet）—— 见 board 里的同段注释。
     高度分配：抽屉 min(62vh,440px) − padding 14/22 ⇒ 内容区；
     把手 flex:0 0 auto（35 + 负 margin-top 14 + margin-bottom 18 ≈ 39）⇒ 滚动层拿到 ≈365px。
     面板不再自己滚（.pfm-panel / #panel-text 解除限高）—— 否则 Path 区块与整个面板永远够不着。
     贴纸网格（.pfm-clipart-grid）与图层列表（.pfm-layers-list）保留各自内滚（100 项贴纸不宜全展开）。 */
  .pfm-props{display:flex;flex-direction:column;overflow:hidden}
  .pfm-props-handle{flex:0 0 auto;position:static}
  .pfm-props-scroll{flex:1 1 auto;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain}
  .pfm-props-scroll>.pfm-panel{max-height:none;overflow-y:visible}
  #panel-text{max-height:none;overflow-y:visible}
  /* 2026-09-19: 贴纸网格高度跟随新抽屉高（440→300）—— 公共区那条 `calc(min(62vh,440px) - 88px)`
     是**桌面端也要用**的，不能直接改（会把右栏网格从 352 压到 212）。
     这里在手机块内覆盖：抽屉矮了以后网格若仍 352px 会溢出内容区 ⇒ 出现「双层滚动」。 */
  /* 2026-09-19: 网格高度跟随新抽屉口径（max(240px, 100dvh − 146px − 100vw)，146 = 底栏 57 + 上面那 89）。
     两行是降级写法：不支持 dvh 的内核保留前一行（100vh）。 */
  /* 2026-09-19: 底栏（57px）已删 ⇒ 234 → **177**（234 = 底栏 57 + 上面那 89 + 88 口径里的其它项）。
     ⚠️ 改「底栏高 / 抽屉 bottom / 抽屉高度 89」任一项都要复核这个 177。 */
  .pfm-clipart-grid{max-height:max(152px,calc(100vh - 234px - 60vw));max-height:max(152px,calc(100dvh - 234px - 60vw))}
}

/* 2026-09-19: 底部工具栏「素材模式 / 对象模式」两套入口（方案 B，用户选定）。
   抽屉内区块的显隐**统一由 .pfm-props[data-objtab] 过滤**（!important 压过各 asset 设的 inline display）：
     · 无 data-objtab（素材模式）→ 全部属性区块隐藏 ⇒ 点 Upload 第一屏就是上传区（B 的核心收益）
     · data-objtab=<x>（对象模式）→ 只放行该 tab 对应的区块
   #pfmPlacementSection / #pfmActionsSection 是常驻条（切面 / Flip·Copy·Delete），**不参与过滤**。
   #pfmImgPop（图片效果）不是 .pfm-section，保持它自己的开关逻辑，也不参与过滤。
   ⚠️ 只作用于手机/平板竖屏；桌面端工具栏竖排 + 右栏常驻，不启用这套。 */
.pfm-toolbar .pfm-stabgroup{display:flex;align-items:center}
.pfm-toolbar .pfm-stab[data-off="1"]{opacity:.32;pointer-events:none}
@media(max-width:768px),(orientation:portrait) and (max-width:1024px){
  .pfm-toolbar .pfm-stabgroup{flex-direction:row;gap:2px;flex:1 1 auto;min-width:0}
  .pfm-toolbar .pfm-stabgroup>.pfm-stab{flex:1 1 0;min-width:0}
  /* ⚠️ 用 .pfm-keep 类排常驻条，**不要**写成 :not(#id)** —— 2026-09-19 真机踩过：
     :not(#id) 会把这条"全隐藏"规则抬到 3 个 ID，而下方"放行"规则只有 1 个 ID；
     两条同为 !important 时比特异性 ⇒ 放行永远输 ⇒ 区块全被藏、tab 点了没反应。
     改用类后：隐藏 = 0 ID / 4 类，放行 = 1 ID / 2 类 ⇒ 放行生效。 */
  .pfm-props:not([data-objtab]) .pfm-section:not(.pfm-keep),
  .pfm-props[data-objtab] .pfm-section:not(.pfm-keep){display:none!important}
  .pfm-props[data-objtab="fill"] #pfmFillSection,
  /* 2026-09-19: **去掉 `size → #pfmCopyFaceSection` 的放行**（用户审计发现）——
     它让点「尺寸」时连带冒出「Copy to face…」，而 image 另有独立 📋 格 ⇒ 同屏重复。 */
  .pfm-props[data-objtab="size"] #pfmTransformSection,
  .pfm-props[data-objtab="shadow"] #pfmShadowSection,
  .pfm-props[data-objtab="path"] #pfmPathSection,
  .pfm-props[data-objtab="bool"] #pfmBoolSection{display:block!important}
  /* 2026-09-19: 跨面复制（Copy to face）最终落在 **#pfmTransformSection（Size 区块）里**，
     带 .pfm-keep ⇒ 不属于上面这套过滤，显隐由 assets/hd-copyface.js 控制。
     原「copy 放行」那条规则已删（对象组那一格也没了）。 */
  /* 2026-09-19: Remove BG / Effects 已搬进对象组格子（image 前两格）⇒ 手机端隐藏常驻行里
     的这两个入口，消除同屏重复。🔴 桌面端没有对象组，这一行必须保留 ——
     所以本规则**只能**写在手机媒体块内，绝不能提到公共区。 */
  #pfmImgRow{display:none!important}
  /* 2026-09-19: 手机端隐藏效果面板的 ✕ —— 再点 Effects 格即关闭（同一开关），✕ 会像"关抽屉"。桌面端右侧栏保留。 */
  .pfm-imgpop-x{display:none!important}
}
/* 🔴 媒体条件必须与手机块「严格互补」—— 竖屏平板（834×1194 等）会同时命中 min-width:769px
   和 (orientation:portrait) and (max-width:1024px)；若这里只写 min-width:769px，
   因为本块在文件更后面，会把对象组压成竖排（2026-09-19 真机截图踩到）。 */
@media(min-width:769px) and (orientation:landscape),(min-width:1025px){
  .pfm-toolbar .pfm-stabgroup{flex-direction:column;gap:6px;width:100%}
}

/* ============================================================
   2026-09-19: 图片动作「无效时」的抽屉内说明（#pfmImgHint）
   由 assets/hd-image.js 控制显隐（display:none ↔ block）。
   位置 = 抽屉滚动体第一块 ⇒ 展开抽屉即见，不必滚动。
   注意：它不是 .pfm-section，**不参与** #pfmProps[data-objtab] 的区块过滤。
   ============================================================ */
.pfm-img-hint{
  margin:10px 12px 0;padding:8px 10px;border-radius:var(--pf-r-md);
  background:#FFF8E6;border:1px solid #F0DCA8;color:#7A5300;
  font-size:.78rem;line-height:1.4;
}


/* 2026-09-20 第 2 批：561~768（含平板竖屏 768）内容本来刚好装得下，不需要吸底；
   而 sticky 在临界溢出时会**盖住画布下部**（实测 768 下 elementFromPoint 命中的是底部行），
   用户在画布底部画不上东西。这里把该区间还原为普通流（底部按钮仍在缹口内，可直接点）。 */
@media (min-width:561px) and (max-width:768px){
  .pfm-draw-bottom{position:static;padding:0;margin-top:4px;border-top:0;background:none}
}
/* ============================================================
   2026-09-26（用户裁决「甲」）：手机端底栏 —— 让**动作区整体换到第二行**，
   而不是把图层芯片挤成一列竖排。
   🔴 根因（`_chips_probe.js` / `_verify_chiprow.js` 实测，线上现状）：
      底栏是 `justify-content:space-between`，右侧 `.pfm-draw-bottom-actions`
      （❌ Cancel / 👁 Preview / ✔ Apply to Product）宽度不可压（实测 ≈305~327px）
      ⇒ 左侧 `.pfm-draw-layers` 只剩几十像素，而它 `flex-wrap:wrap`
      ⇒ 4 个芯片各占一行 ⇒ **竖排 / 高 122px / 底栏涨到 143px**（用户真机截图复现）。
   🔴 断点为什么取 434（实测反推，别凭感觉设）：
      芯片行「可用宽 ≈ 视口 − 24(容器padding) − actions − 10(gap) ≈ 视口 − 345」；
      并排两个最窄芯片需 44 + 6 + 39 = 89px ——
        · 可用 < 89  ⇒ 芯片 ≥3 行 ⇒ 底栏 143px ⇒ **必须修**；
        · 可用 ≥ 89  ⇒ 芯片 ≤2 行 ⇒ 底栏 max(58,36)+padding = **79px，比拆开后的 93px 更矮**
          ⇒ 拆开是**负收益**（v1「≤560」/v2「≤768」两版都栽在这 ⇒ 已废弃）。
      ⇒ 分界≈434；取 **430** 留余量（430 是 iPhone Pro Max 宽度、仍落在「芯片 4 行」区，
        而 434 及以上已是 2 行区 ⇒ 断点绝不能写成 434，否则边界视口反而被改差）。实测确认：
          360 → 4 行/143px；375 → 4 行/143px；390 → 4 行/143px；412 → 4 行/143px（安卓主流宽度）
          492 → 2 行/79px；768 → 1 行/36px；1366 → 1 行/36px
      ⇒ 修复只覆盖 ≤430，492 / 768 / 1366 **逐项零回归**（已用注入预演逐像素核对）。
   ✅ 收益（注入预演实测）：360/375/390/412 芯片 143 → 93px（−50px），
      并且 390 的内容溢出 23px → −17px（**顺带消掉竖排造成的溢出**）。
   ✅ 写法：芯片行 `nowrap` 保证内部不裂；动作区 `flex:0 0 100%` 独占第二行；
      `margin-left:auto` 让它仍靠右（原视觉语言：动作在右）。 */
@media (max-width:430px){
  .pfm-draw-bottom{flex-wrap:wrap;row-gap:8px}
  .pfm-draw-layers{flex-wrap:nowrap}
  .pfm-draw-bottom-actions{flex:0 0 100%;margin-left:auto}
}
/* ============================================================
   2026-09-26（用户裁决 甲）：导出 PNG 前**先确认**。
   为什么：① 顶行图标小、手机上极易误触；② 本轮把画板的黑色气泡提示**全部关掉**后，
     点导出**完全无反馈**（用户无从判断到底下载没下载）。
   为什么自建而不 `window.confirm()`：原生样式不可控、与站点视觉不搭，部分移动浏览器会降级成横幅。
   🔴 挂载点 = `#pfmDrawModal`（position:fixed 全屏 flex）的直接子元素（在 #pfmDrawBox 与色轮之后）：
     ① 不受 `#pfmDrawBox{overflow:hidden auto}` 滚动影响（挂在盒内的话，滚动后确认条会被滚出视野）；
     ② **绝对定位、零布局高度** ⇒ 画板高度 / 画布尺寸**完全不变**（无需改 _drawBudgetH 的预算）。
   🔴 必须显式写 `[hidden]{display:none}`：`.pfm-draw-confirm{display:flex}` 是**作者样式**，
      会盖掉 UA 的 `[hidden]` 规则（作者样式恒胜 UA 样式，与特异性无关）。
   🚫 刻意不绑 Esc：画板自身的 Esc 会关整个弹窗（既有行为），此处不抢；退出方式 = Cancel / 点遮罩。 */
.pfm-draw-confirm{position:absolute;inset:0;z-index:5;display:flex;align-items:center;justify-content:center;background:rgba(17,24,39,.45)}
.pfm-draw-confirm[hidden]{display:none}
.pfm-draw-confirm-card{background:#fff;border-radius:var(--pf-r-lg);padding:20px 22px;box-shadow:0 12px 40px rgba(0,0,0,.28);max-width:min(360px,86vw);text-align:center}
.pfm-draw-confirm-q{margin:0 0 16px;font-size:.95rem;font-weight:600;color:#111827;line-height:1.45}
.pfm-draw-confirm-acts{display:flex;gap:10px;justify-content:center}
.pfm-draw-confirm-acts .pfm-btn{flex:1 1 0;min-width:96px}


/* ============================================================
   2026-09-27（乙）：**极窄屏（≤400）分类行回到 1 行**。
   🔴 根因（`_hdnav/_tabfit_probe.js` / `_split_cost_probe.js` 实测）：
      09-27 颜色入口拆成两个 tab（Colour → Solid + Grad）后，窄屏分类行的 tab 从 9 个变 10 个
      ⇒ 10 × 30px(min-width，见上方手机断点的 `.pfm-dcat{height:40px;min-width:30px}`)
         + 9 × 2px(gap) = 318px，而 360 宽下可用只有 299px（= 329 − padding-right 30）
      ⇒ **分类行换行成 2 行，toolbar 84 → 126px（+42px）**，画布位置被顶下 42px
        （360×780 与 375×812 实测；390 及以上本来就 1 行 ⇒ 不受影响）。
   ✅ 修法（**只动新增的那两个颜色 tab** + 分类行右内边距，不动其它 8 个 tab 的尺寸）：
      · 颜色 tab `min-width` 30 → 22、`padding` 0 3 → 0 2 ⇒ 省 16px；文字（Solid/Grad）隐藏，
        语义由圆点/渐变条图标承载（title 仍是 "Solid colour" / "Gradient"）；
      · `.pfm-draw-cats` 右内边距 30 → 22（那是给右上角 ✕ 让位的，窄屏 22px 足够）⇒ 省 8px。
      ⇒ 合计省 24px ≥ 需要的 19px，留 5px 余量。
   🔴 只在 `max-width:400px` 生效 ⇒ 390 / 412 / 492 / 768 / 1366 **逐像素不变**（已验证）。
   ⚠️ 为什么不用 `flex-wrap:nowrap + overflow-x:auto` 一刀切：那会把最右侧的颜色入口推到屏幕外
      ⇒ 用户"看不见的功能=不存在"（本项目的既有铁律）。 */
@media (max-width:400px){
  /* 🔴 2026-10-03（用户裁决）：本块原有两条"把颜色两 tab 压成 22px"的规则**已删除**。
     当初（09-27「乙」）是"分类行挤了 10 个 tab、360 放不下"才压的；现在分类行只有 7 格
     ⇒ 不需要压，留着只会让这两格比同排小 12px（用户实拍反馈"按钮大小不一致"）。 */
  .pfm-draw-cats{padding-right:22px}
}

/* ============================================================
   2026-10-09 §1010h —— 画板（设计板）属性面板挪到「图标栏右侧」。
   动机：图标栏那 5 个按钮（Image/Text/Clipart/Layers/Draw）本来就是切换属性面板内容的开关，
   现在面板在最右侧（相隔整个画布），点左边按钮、内容出现在 1000px 外，不符合操作习惯。
   对齐 Canva / 稿定式「左图标栏 + 紧邻其右的上下文面板，画布吃剩余空间」。
   做法：只换渲染顺序（order），**不搬 DOM、不改 JS**（已 grep 确认 JS 无「面板在右侧」硬编码）。
   🔴 画布不会因此变小：.pfm-canvas-inner 是正方形且受「高度」约束（min(区宽-40, 区高-36)），
      实测 1280/1366/1440/1920 四档，面板 360→420 后白框尺寸 0 变化（横向富余 ≥110px）。
   媒体条件与手机块「严格互补」，同第 1652 行既有桌面块（竖屏平板 834×1194 会同时命中两条）。
   ============================================================ */
@media(min-width:769px) and (orientation:landscape),(min-width:1025px){
  .pfm-canvas-area{order:1}                                   /* 画布排到最后 */
  .pfm-props{border-left:0;border-right:1px solid #E4E4E7}     /* 分隔线从左侧翻到右侧 */
}
/* 加宽只在宽屏生效（≥1280，与设计台断点一致）；窄窗口自动退回 360，绝不挤压画布。 */
@media(min-width:1280px){
  .pfm-props{width:420px;min-width:420px}
}

/* ============================================================
   2026-10-09 §1010j —— 画板（设计板）桌面左两栏「加宽 + 加大」（用户裁决「乙」·推荐档）。
   动机：左两栏（图标栏 / 属性面板）在宽屏下显得很小，而空间大量富余。
   档位：图标栏 60→76；属性面板 360→最多 560（只在确有富余时）；面板内元素 ×1.18。
   🔴 面板内规则一律加 #pfmProps 前缀：assets/hd-image.css（.pfm-img-btn 等）
      在本文件**之后**加载（同一 section：hd-canvas L131 / hd-image L141），
      同特异性会被它压掉，必须靠 ID 提高特异性。
   🔴 面板宽度用 clamp 只吃「横向富余」：画布白框 = min(画布区宽-40, 画布区高-36)，
      横向富余条件 = 画布区宽 ≥ 画布区高 + 4；画布区高 = 100vh - 24
      ⇒ 面板上限 = 100vw - 100vh - (图标栏 - 20) = 100vw - 100vh - 56（图标栏 76）。
      实测 1280×800 / 1366×768 / 1440×900 / 1920×1200 四档白框恒定。
   🟡 唯一代价：极端「宽 < 高」窗口（如 1300×1150）横向无富余 ⇒ clamp 收到下限 420
      （= 改前宽度），此时图标栏 +16px 会让白框最多缩 16px。
   🚫 手机 / 平板 / ≤1279 桌面 一律不动（本块只在 ≥1280 生效）。
   ============================================================ */
@media(min-width:1280px){
  /* ① 图标栏 60 → 76（格 48→56，居中余量保持 (76-56)/2=10） */
  #pfmToolbar{width:76px;min-width:76px;gap:7px;padding:13px 0}
  #pfmToolbar .pfm-stab{width:56px;height:56px;padding:5px 0;gap:2px}
  #pfmToolbar .pfm-stab .pfm-stab-ico{font-size:1.36rem}
  #pfmToolbar .pfm-stab .pfm-stab-lbl{font-size:0.68rem}
  /* 左缘高亮条随格宽重新贴边（原 left:-6 = (60-48)/2 ⇒ 现 -10 = (76-56)/2） */
  #pfmToolbar .pfm-stab.sel::before{left:-10px;height:31px}
  /* ② 属性面板 360 → clamp(420, 100vw-100vh-56, 560) */
  #pfmProps{width:clamp(420px,calc(100vw - 100vh - 56px),560px);min-width:0;flex-shrink:0;padding:17px}
  /* ③ 面板内元素 ×1.18 */
  #pfmProps .pfm-float-toolbar{gap:9px;padding:0 12px}
  #pfmProps .pfm-fbtn{min-width:56px;min-height:52px;padding:7px 5px;gap:4px}
  #pfmProps .pfm-fbtn__icon{font-size:1.3rem}
  #pfmProps .pfm-fbtn__label{font-size:0.77rem}
  #pfmProps .pfm-chips{gap:5px}
  #pfmProps .pfm-chip{padding:5px 12px;font-size:0.89rem}
  #pfmProps .pfm-label{font-size:0.8rem;margin-bottom:7px}
  #pfmProps .pfm-section{margin-bottom:14px;padding-bottom:14px}
  #pfmProps .pfm-btn{height:42px;font-size:1rem}
  #pfmProps .pfm-xf-row{gap:7px;margin-bottom:9px}
  #pfmProps .pfm-xf-lab,#pfmProps .pfm-xf-mult{font-size:0.85rem}
  #pfmProps .pfm-xf-num{height:35px;font-size:0.92rem}
  #pfmProps .pfm-xf-hint{font-size:0.8rem}
  #pfmProps .pfm-img-btn{height:42px;gap:7px;padding:0 12px}
  #pfmProps .pfm-img-btn .ico{font-size:1.12rem}
  #pfmProps .pfm-img-btn .lb{font-size:0.89rem}
  /* Effects 弹层（同一面板内，跟着放大） */
  #pfmProps .pfm-imgpop-hd{padding:12px 14px}
  #pfmProps .pfm-imgpop-title{font-size:0.97rem}
  #pfmProps .pfm-imgpop-body{padding:12px 14px 17px}
  #pfmProps .pfm-imgpop-grp{font-size:0.78rem}
  #pfmProps .pfm-imgpop-lbl{width:68px;font-size:0.85rem}
  #pfmProps .pfm-imgpop-val{width:40px;font-size:0.85rem}
  #pfmProps .pfm-imgpop-x{width:33px;height:33px;font-size:1.12rem}
  #pfmProps .pfm-imgpop-btn{height:38px;font-size:0.89rem}
  #pfmProps .pfm-imgpop-chip{height:35px;font-size:0.8rem}
  #pfmProps .pfm-imgpop-hint{font-size:0.8rem}
}

/* ============================================================
   2026-10-13（甲 · 用户报「部分手机/iPad：设计台底部五个按钮显示不全、需要左右滑动」）
   ------------------------------------------------------------
   🔴 排查结论（本地实测，非推测）：用 **Chromium + WebKit（真 Safari 内核）** 双引擎
      × 17 档视口（竖屏 320~834、横屏 667/736/812/844/1024/1180/1366、极窄 280/300）
      ⇒ 工具条 5 键**始终等分**、`documentElement.scrollWidth == clientWidth`（零横溢）⇒ **无法复现**。
      真机差异（iOS 版本 / 自动化字号 / 「请求桌面网站」）本地模拟不到 ⇒ 改做**防御性加固**：
      把一切可能造成「横向可滚 / 文字撑宽」的路径全部封死。
   ① 🔴 最大嫌疑：**iOS Safari 默认 `-webkit-text-size-adjust:auto`** —— 横屏 / 视口较宽时
      Safari 会**自动放大文字**（防小字难读）。本项目此前**从未设过**该属性（实测 count=0）
      ⇒ 文字变宽是唯一能在真机上把 5 个键撑出容器的现实路径 ⇒ 显式压回 100%。
      ⚠️ 本文件只在设计台页面加载 ⇒ 设为 100% 只影响设计台，全站其它页面不受影响。
   ② `.pfm-toolbar` 原本只写 `overflow-y:auto` ⇒ 按 CSS 规则 `overflow-x` **计算值为 auto**
      ⇒ 内容一旦超宽就会**出现横向滚动条**（正是用户描述的「要左右滑」）⇒ 显式 `overflow-x:hidden`。
   ③ 键用 `flex:1 1 0%`（带单位的百分比 —— 旧 WebKit 对无单位 `0` 有历史差异）+ `overflow:hidden`，
      文字 `text-overflow:ellipsis` ⇒ 任何尺寸下**只裁不滚**，永远横向放得下 5 个键。
   🔴 对已正常的机型**零视觉变化**（这些约束在正常布局下不会被触发）。 */
html{-webkit-text-size-adjust:100%;text-size-adjust:100%}
#pfmBoard{-webkit-text-size-adjust:100%;text-size-adjust:100%}
.pfm-toolbar{overflow-x:hidden}
.pfm-toolbar .pfm-stabgroup{flex-wrap:nowrap}
.pfm-toolbar .pfm-stab{flex:1 1 0%;min-width:0;overflow:hidden}
.pfm-toolbar .pfm-stab .pfm-stab-ico{flex:0 0 auto}
.pfm-toolbar .pfm-stab .pfm-stab-lbl{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* ============================================================
   2026-10-13 §1013d —— 画板弹窗「顶功能键」电脑端放大（用户裁决：只改电脑端 · 乙档）。
   动机：顶两行（分类键行 #pfmDrawCats / 当前工具行 #pfmDrawCurRow）自 10-03 为「两行列对齐」
     把标签压到 0.5rem(8px)、键压到 34px 后就一直沿用；而 10-09 §1010j 只放大了设计板的
     图标栏(60→76)与属性面板(×1.18) ⇒ 大屏上画板顶两行明显偏小、标签看不清（用户实拍反馈）。
   乙档（两行同度量 ⇒ 10-03 的「顶行 / 第 3 行逐列对齐」保持）：
     标签 0.5rem(8px) → 0.7rem(11px) · 图标 16 → 20px · 色点 16 → 20px
     键高 44 → 52 · 键宽 min-width 34 → 44（宽度仍 auto ⇒ 长标签 Effect/Export/Select 不裁切）
   - 作用域**仅**电脑端（媒体条件与第 1809 / 1833 行同款）⇒ 手机 / 竖屏平板档原样不动（零回归）。
   - 现只点名 #pfmDrawCats / #pfmDrawCurRow ⇒ 点分类键弹出的浮层工具卡（#pfmDrawTools）不受影响。
   - 两行的 .pfm-dcat / .pfm-dtool 一律 min-width:44px + width:auto ⇒ 实测 14 键标签均 ≤44
     ⇒ 键宽恒 44、第 3 行同宽 ⇒ 列对齐不破。
   - 折行余量：#pfmDrawCats 顶行 14 键 × 44 + 缝 13×4 + padding-right 40 = 708px，
     远小于盒内容宽 860（.pfm-draw-box max-width 900 − padding 40）⇒ 典型桌面不折行。
   ============================================================ */
@media(min-width:769px) and (orientation:landscape),(min-width:1025px){
  /* —— 顶行 1：分类键（Pens / FX / Effect / Shape / Select / Text）—— */
  #pfmDrawCats > .pfm-dcat{height:52px;min-width:44px;padding:0 5px;gap:3px}
  #pfmDrawCats .pfm-dcat-t{font-size:0.7rem}
  #pfmDrawCats .pfm-dcat-ico{width:20px;height:20px}
  #pfmDrawCats .pfm-dcat-ico svg{width:20px;height:20px}
  /* —— 顶行 2：扁平功能键（Solid / Grad / Image / Grid / Sym / Blend / Export / Reset / Stab）—— */
  #pfmDrawCats > .pfm-dtool{height:52px;min-width:44px;padding:0 5px;gap:3px}
  #pfmDrawCats > .pfm-dtool .pfm-ico{width:20px;height:20px}
  #pfmDrawColorBtn .pfm-dtool-dot,
  #pfmDrawGradBtn .pfm-dtool-dot{width:20px;height:20px}
  /* —— 第 3 行：当前工具显示格 + 常驻工具键（Erase / Guide / Pick / Fill / Undo / Redo / Clear）—— */
  #pfmDrawCurRow > .pfm-dcat{height:52px;min-width:44px;gap:3px;padding:0 9px}
  #pfmDrawCurRow .pfm-dcat-t{font-size:0.7rem}
  #pfmDrawCurRow .pfm-dcat-ico{width:20px;height:20px}
  #pfmDrawCurRow .pfm-dcat-ico svg{width:20px;height:20px}
  #pfmDrawCurRow .pfm-dtool[data-label]{width:auto;min-width:44px;height:52px;padding:3px 2px 0}
  #pfmDrawCurRow .pfm-dtool[data-label]::after{font-size:0.7rem}
  #pfmDrawCurRow .pfm-dtool .pfm-ico{width:20px;height:20px}
}

/* ══════════════════════════════════════════════════════════════════════════════
   2026-10-15：画布底部「Color」—— 换产品底色（只换底板，设计对象不动）
   位置：绝对定位贴 #pfmCanvasArea 底部（该区 flex:1、画布贴顶、下方有空档）
        ⇒ 不占文档流、不影响画布尺寸、不触发任何 resize 联动（与 .pfm-print-warning 同构）。
   色块尺寸/圆角全部走既有 token（--pf-sw-r/-gap/-bw/-bd），符合「色板全站统一」铁律。
   ────────────────────────────────────────────────────────────────────────────── */
/* 2026-10-15 修复：原来只写 max-width、没有 width（靠 shrink-to-fit），而子级 .pfm-color-strip
   又写 max-width:100% 反向依赖父级宽度 ⇒ **循环依赖**，浏览器算成极小值（实测某一拍宽 0）
   ⇒ 色带只显示 1 个色点（用户报「只搬进去一个颜色」）。改用 left:0;right:0 让宽度由包含块给死。 */
.pfm-stage-color{position:absolute;left:0;right:0;bottom:10px;z-index:25;
  padding:0 12px;box-sizing:border-box;
  display:flex;flex-direction:column;align-items:center;gap:8px;
  pointer-events:none}
.pfm-stage-color>*{pointer-events:auto}
/* 2026-10-15（用户裁决）：色带只放「上一步 combos 选中的色」⇒ 改**平铺换行**（不再横向滚动）：
   选几个就一行铺几个、全都看得见；选得多自动换行，不用左右划。 */
.pfm-color-strip{display:flex;align-items:center;justify-content:center;gap:var(--pf-sw-gap);flex-wrap:wrap;
  background:rgba(255,255,255,.96);padding:8px 10px;border-radius:var(--pf-r-lg);
  box-shadow:0 6px 20px rgba(0,0,0,.18);max-width:100%}
/* 2026-10-15：色带前的文字标签（与规格卡「Your combos」同口径：800 字重 / #1A1230） */
.pfm-color-strip .pfm-color-label{flex:0 0 auto;font-size:.78rem;font-weight:800;color:#1A1230;
  letter-spacing:.02em;margin-right:2px;white-space:nowrap}
.pfm-color-strip .pfm-cdot{flex:0 0 auto;width:26px;height:26px;border-radius:var(--pf-sw-r);
  cursor:pointer;border:var(--pf-sw-bw) solid var(--pf-sw-bd);padding:0;background-clip:padding-box}
.pfm-color-strip .pfm-cdot.sel{outline:2px solid #7C3AED;outline-offset:2px}
/* 避让：原 bottom:16px —— 抬到 56px 让出底部 Color 按钮 */
.pfm-print-warning{bottom:56px}
@media(max-width:768px){
  .pfm-stage-color{bottom:8px;padding:0 10px}
  .pfm-color-strip .pfm-cdot{width:24px;height:24px}
}

/* ══════════════════════════════════════════════════════════════════════════════
   2026-10-15 批2a：多色出图弹窗 —— 尺寸/结构常量（**逻辑在 assets/hd-canvas.js 批2b**）
   🔴 全部限定在 #pfmMockupModal 内 —— .pfm-modal-box / .pfm-carousel-* 是**多个弹窗共用**的类，
      不加作用域会连坐画板弹窗等（零回归铁律）。
   🔴 三态窗口恒定的关键是「每块都定高」：大图 min(46vh,420px) / 小图带 86px / 提示区 min-height:167px
      （167 = 确认态的天然高度）。⚠️ 以后改确认态文案行数，这个值要重新量，否则窗口又会跳。
   ────────────────────────────────────────────────────────────────────────────── */
#pfmMockupModal .pfm-modal-box{width:min(520px,94vw)}
#pfmMockupModal .pfm-mc-tip{min-height:167px;box-sizing:border-box;border-radius:10px;
  padding:14px 16px;margin-bottom:14px;font-size:13px;line-height:1.85}
#pfmMockupModal .pfm-mc-tip.confirm{background:#F5F3FF;border:1px solid #DDD6FE;color:#4C1D95}
#pfmMockupModal .pfm-mc-tip.busy{background:#F5F3FF;border:1px solid #DDD6FE;color:#5B21B6}
#pfmMockupModal .pfm-mc-tip b{font-weight:800}
#pfmMockupModal .pfm-mc-tip .sub{color:#6B7280;font-size:12px;margin-top:1px}
#pfmMockupModal .pfm-mc-eta{display:flex;gap:10px;margin-top:6px;flex-wrap:wrap}
#pfmMockupModal .pfm-mc-eta div{background:#fff;border:1px solid #DDD6FE;border-radius:8px;padding:6px 10px;font-size:12px}
#pfmMockupModal .pfm-mc-eta span{font-weight:800;font-size:14px}
#pfmMockupModal .pfm-mc-act{display:flex;gap:10px;margin:10px 0 0}
#pfmMockupModal .pfm-mc-act .pfm-btn{flex:1}
#pfmMockupModal .pfm-mc-status{display:flex;justify-content:space-between;font-size:12px;font-weight:600}
#pfmMockupModal .pfm-mc-prog{height:8px;border-radius:999px;background:rgba(255,255,255,.75);overflow:hidden;margin:10px 0 6px}
#pfmMockupModal .pfm-mc-prog>i{display:block;height:100%;width:0;background:linear-gradient(90deg,#22D3EE,#1FBDB7);
  border-radius:999px;transition:width .45s ease}
#pfmMockupModal .pfm-mc-warn{margin:0 0 8px;font-size:12px;color:#B45309;background:#FEF3C7;border-radius:6px;
  padding:6px 9px;min-height:31px;box-sizing:border-box}
#pfmMockupModal .pfm-carousel-main{height:min(46vh,420px);display:flex;align-items:center;justify-content:center;
  touch-action:pan-y;cursor:grab}
#pfmMockupModal .pfm-carousel-main img{width:100%;height:100%;object-fit:contain}
#pfmMockupModal .pfm-carousel-main.empty{background:#f4f5f7;border-style:dashed;border-color:#D1D5DB}
#pfmMockupModal .pfm-carousel-main.empty > img,
#pfmMockupModal .pfm-carousel-main.empty .pfm-carousel-badge{display:none}
#pfmMockupModal .pfm-carousel-thumbs{height:86px;box-sizing:border-box;padding:6px 0}
#pfmMockupModal .pfm-carousel-thumbs:empty{border:1px dashed #D1D5DB;border-radius:8px}

/* ══════════════════════════════════════════════════════════════════════════════
   2026-10-15（甲乙·用户裁决）：底部主按钮 #pfmBtnGenerate **隐藏**（不是删除 —— 它仍是
   整条出图流程的触发点，Start generating / Generate again 都靠 click() 它）。
   它的两个作用已由提示区接手：① 触发 ② 状态/进度显示（轮询 hdGenProgress / hdMcDoneTip）。
   ⇒ 弹窗底部不再有任何按钮，关窗走右上角 ✕。窗口总高随之变矮（三态仍恒定）。
   ────────────────────────────────────────────────────────────────────────────── */
#pfmMockupModal #pfmBtnGenerate{display:none!important}
#pfmMockupModal .pfm-mc-act{display:block}
#pfmMockupModal .pfm-mc-act .pfm-btn{width:100%}

/* ══════════════════════════════════════════════════════════════════════════════
   2026-10-15 晚：多色弹窗 v6+v7+v8（用户逐条裁决）
   · 提示区**统一淡紫**（品牌色，深浅区分态）；进度条改紫
   · 空占位放**低调品牌位**（logo+字标），出图后自动消失
   · 小图带**无滚动条**、划过去翻页、不上下动；大图可划
   · 底部**独立按钮区**：一个位、状态驱动；进行态 = 按钮内**进度填充**（对齐 Buy it now）
   ────────────────────────────────────────────────────────────────────────────── */
#pfmMockupModal .pfm-mc-tip{min-height:104px;padding:11px 14px}
#pfmMockupModal .pfm-mc-prog>i{background:linear-gradient(90deg,#A78BFA,#7C3AED)}
#pfmMockupModal .pfm-mc-prog{background:rgba(124,58,237,.12)}
#pfmMockupModal .pfm-carousel-thumbs{overflow:hidden;scrollbar-width:none;-ms-overflow-style:none;touch-action:none;scroll-behavior:smooth;height:82px;padding:8px 0 4px}
#pfmMockupModal .pfm-carousel-thumbs::-webkit-scrollbar{display:none}
#pfmMockupModal .pfm-mockup-thumb{width:72px;height:70px;border-radius:10px}
#pfmMockupModal .pfm-carousel-main{touch-action:none;border-radius:12px}
#pfmMockupModal .pfm-carousel-main .pfm-mc-ph{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;pointer-events:none}
#pfmMockupModal .pfm-carousel-main .pfm-mc-ph img{width:72px;height:auto;opacity:.38}
#pfmMockupModal .pfm-carousel-main .pfm-mc-ph span{font-size:12px;font-weight:800;letter-spacing:.14em;color:#B6BCC7;text-transform:uppercase}
#pfmMockupModal .pfm-carousel-main:not(.empty) .pfm-mc-ph{display:none}
#pfmMockupModal #pfmMcFoot{margin-top:14px;padding-top:14px;border-top:1px solid var(--pf-border);display:flex;flex-direction:column;gap:8px}
#pfmMockupModal #pfmMcFoot .pfm-btn{width:100%;min-height:44px;border-radius:10px;font-weight:700;background:#00A651;border-color:#00A651;color:#fff;position:relative;overflow:hidden}
#pfmMockupModal #pfmMcFoot .pfm-btn .pfm-fill{position:absolute;left:0;top:0;bottom:0;width:0;background:rgba(255,255,255,.30);transition:width .45s ease;pointer-events:none}
#pfmMockupModal #pfmMcFoot .pfm-btn .pfm-lbl{position:relative;z-index:1}

/* 2026-10-15（用户）：弹窗头部 = 真「一行」——标题与 ✕ 同中线 */
#pfmMockupModal .pfm-modal-box h3{display:flex;align-items:center;justify-content:space-between;gap:10px;
  min-height:30px;margin:0 0 12px;font-size:16px;font-weight:800;line-height:1.2}
#pfmMockupModal .pfm-modal-close{position:static;flex:0 0 auto;width:30px;height:30px;margin:0 -4px 0 0;
  border:0;background:transparent;font-size:17px;color:#9CA3AF;cursor:pointer;border-radius:8px;line-height:1}
/* 2026-10-16：✕ 改【绝对定位】—— 翻译把 h3 内容包 <font> 会毁掉 flex space-between
   （实测 ✕ 从 x=846 跳到 553）。绝对定位到弹窗盒右上角（与 hd-signup 的 .hdco-x 同模式），
   翻译怎么包都不动。 */
#pfmMockupModal .pfm-modal-box{position:relative}
#pfmMockupModal #pfmModalX{position:absolute;top:12px;right:12px}
#pfmMockupModal .pfm-modal-close:hover{background:#f3f4f6;color:#374151}

/* 2026-10-15（用户）：提示区**定高槽位**（永不塌陷）—— 换态时窗口零跳动 */
#pfmMockupModal #pfmMcTipWrap{position:relative;height:104px;margin-bottom:12px}
#pfmMockupModal #pfmMcTipWrap .pfm-mc-tip{position:absolute;left:0;right:0;top:0;height:104px;margin:0;overflow:hidden}

/* 2026-10-15（用户「会移位」）：弹窗内容变高/变矮 ⇒ 滚动条出现/消失 ⇒ 内容区宽度变 ~15px
   ⇒ 右侧的 ✕ 等元素**左右跳**。scrollbar-gutter:stable 让滚动条宽度**常驻预留** ⇒ 不再移位。 */
#pfmMockupModal .pfm-modal-box{scrollbar-gutter:stable}

/* 2026-10-15（用户「还是跳」·深度采样后定位）：**手机端 vh 不稳定** ——
   地址栏收起/展开会让 vh 变化 ⇒ 大图 min(44vh,400px) 与弹窗 max-height:92vh 跟着变
   ⇒ 弹窗（垂直居中）整体上下跳。改用 **svh（small viewport height，不随地址栏变化）**，
   前面保留 vh 作旧浏览器兜底（不支持的会忽略后一条）。 */
#pfmMockupModal .pfm-modal-box{max-height:92vh;max-height:92svh}
#pfmMockupModal .pfm-carousel-main{height:min(44vh,400px);height:min(44svh,400px)}

/* 2026-10-15（用户·甲）桌面端「跳」的根因：**页面滚动条出现/消失**
   #pfmMockupModal 是 position:fixed 在视口里居中 ⇒ 页面滚动条一出现/消失，视口宽度变 ~8px
   ⇒ 固定居中的弹窗跟着左右跳（上次只给弹窗内部加了预留，漏了页面本身）。
   修法：html 上预留滚动条宽度（有无滚动条内容宽度都一样）。
   🔴 本文件只在**设计台页**加载 ⇒ 影响面仅限这一页，不是全站。 */
html{scrollbar-gutter:stable}
html{overflow-y:scroll}      /* 旧浏览器兜底（不支持 scrollbar-gutter 时） */

/* 2026-10-16（用户）：提示区已不用（信息移到按钮上）⇒ **拆掉昨晚加的「定高槽位」**，
   否则 #pfmMcTipWrap 会白占 104px+12px ⇒ 弹窗顶部一大块空白、窗口过高。
   · 槽位高度归零 + 内部 tip 一律不显示（!important 压过 JS 写的行内 display）。
   ⇒ 弹窗整体**矮约 116px**。 */
#pfmMockupModal #pfmMcTipWrap{height:0;margin-bottom:0;overflow:hidden}
#pfmMockupModal #pfmMcTipWrap .pfm-mc-tip{display:none!important}
/* 标题与下方内容间距收一点（原来给提示区留的） */
#pfmMockupModal .pfm-modal-box h3{margin:0 0 10px}

/* ══════════════════════════════════════════════════════════════════════════════
   2026-10-16（用户「没有居中」·精量后定位）
   🔴 真因 = **我自己昨天为修「跳」而加的两条规则**：
        html{scrollbar-gutter:stable} + html{overflow-y:scroll}
      ⇒ 把**页面内容区永久压窄 15px**（body.clientWidth 768→753）⇒ position:fixed 的遮罩也变 753
      ⇒ 弹窗在 753 里居中、而用户看的是 768 的窗口 ⇒ **视觉上偏左 ~7.5px**（右边多 15px）。
      一句话：**我修"跳"的动作，制造了"不居中"。**
   ──────────────────────────────────────────────────────────────────────────────
   修法：① 撤掉那两条 html 规则（回到默认，内容区恢复满宽）；
        ② 遮罩改用 **100vw**（100vw 含滚动条）⇒ 居中基准**恒等于视口宽** ⇒
           既不偏位，也不会因页面滚动条出现/消失而左右跳（根治两件事）。 */
html{overflow-y:auto;scrollbar-gutter:auto}
#pfmMockupModal{width:100vw;left:0;right:auto}

/* ══════════════════════════════════════════════════════════════════════════════
   2026-10-16（用户：**弹窗里面的信息框没在弹窗中间**）—— 实测内部左 27 / 右 37 ⇒ 偏左 10px
   🔴 真因 = 我为修「弹窗内部滚动条导致跳动」而加的 `#pfmMockupModal .pfm-modal-box{scrollbar-gutter:stable}`：
      它**只在右侧预留 15px** ⇒ 内容盒右侧被吃掉 15px + 内容整体左移 ⇒ 里面所有框跟着偏左 ~7.5px。
   修法：`stable both-edges` **两侧各预留** ⇒ 内容恢复居中，且滚动条出现/消失时仍不跳。
   📌 这是「滚动条预留」这一类坑的**第三次**（html 上→弹窗上→现在是 both-edges），以后一律直接 `both-edges`。 */
#pfmMockupModal .pfm-modal-box{scrollbar-gutter:stable both-edges}

/* ============================================================================
   2026-10-16（用户选定 A+E）进行态按钮动效：
   A = 流光扫过(sheen, 1.9s) + 进度填充(起步 10%，不再从 0 起 => 第一眼就看见在跑)
   E = 三点波浪 + 数字滚动(变化时弹一下) + 完成闪光
   全部限定 #pfmMcFoot 内，不动其它按钮。含 prefers-reduced-motion 兜底。
   ============================================================================ */
#pfmMockupModal #pfmMcFoot .pfm-btn-busy{position:relative;overflow:hidden;isolation:isolate}
#pfmMockupModal #pfmMcFoot .pfm-btn-busy .pfm-lbl{z-index:3;display:inline-flex;align-items:center;
  justify-content:center;gap:9px;padding:0 14px;min-height:44px}
#pfmMockupModal #pfmMcFoot .pfm-fill{z-index:1;background:rgba(255,255,255,.26);transition:width .45s ease}
#pfmMockupModal #pfmMcFoot .pfm-sheen{position:absolute;inset:0;z-index:2;pointer-events:none;
  background:linear-gradient(100deg,transparent 32%,rgba(255,255,255,.44) 50%,transparent 68%);
  background-size:220% 100%;animation:pfmSheen 1.9s linear infinite}
@keyframes pfmSheen{from{background-position:180% 0}to{background-position:-80% 0}}
#pfmMockupModal #pfmMcFoot .pfm-dots{display:inline-flex;gap:3px;align-items:flex-end;height:12px}
#pfmMockupModal #pfmMcFoot .pfm-dots i{width:4px;height:4px;border-radius:50%;background:rgba(255,255,255,.9);
  animation:pfmWave 1.05s ease-in-out infinite}
#pfmMockupModal #pfmMcFoot .pfm-dots i:nth-child(2){animation-delay:.14s}
#pfmMockupModal #pfmMcFoot .pfm-dots i:nth-child(3){animation-delay:.28s}
@keyframes pfmWave{0%,100%{transform:translateY(0);opacity:.55}45%{transform:translateY(-5px);opacity:1}}
#pfmMockupModal #pfmMcFoot .pfm-num{display:inline-block;min-width:1.1em;text-align:center;
  transition:transform .18s cubic-bezier(.34,1.56,.64,1)}
#pfmMockupModal #pfmMcFoot .pfm-num.tick{transform:translateY(-3px) scale(1.18)}
#pfmMockupModal #pfmMcFoot .pfm-flash{animation:pfmFlash .5s ease}
@keyframes pfmFlash{0%{filter:brightness(1)}30%{filter:brightness(1.45)}100%{filter:brightness(1)}}
@media (prefers-reduced-motion: reduce){
  #pfmMockupModal #pfmMcFoot .pfm-sheen,
  #pfmMockupModal #pfmMcFoot .pfm-dots i{animation:none}
}

/* 2026-10-17：Add to Cart 按钮复用 §19 进行态。
   JS（hd-canvas.js hdAtcBusy/hdAtcDone/hdAtcReset）早已写好 .pfm-fill/.pfm-sheen/.pfm-dots/.pfm-flash 结构，
   但上面这套 CSS 全被限定在 #pfmMockupModal #pfmMcFoot 里 ⇒ 按钮上这些 class 不生效、只剩一行文字。
   这里补同一套规则，只作用 #pfmBtnAddToCart（keyframes pfmSheen/pfmWave/pfmFlash 是全局的，无需重写）。 */
#pfmBtnAddToCart.pfm-btn-busy{position:relative;overflow:hidden;isolation:isolate}
#pfmBtnAddToCart .pfm-lbl{z-index:3;display:inline-flex;align-items:center;justify-content:center;gap:9px}
#pfmBtnAddToCart .pfm-fill{position:absolute;left:0;top:0;bottom:0;width:10%;z-index:1;background:rgba(255,255,255,.26);transition:width .45s ease;pointer-events:none}
#pfmBtnAddToCart .pfm-sheen{position:absolute;inset:0;z-index:2;pointer-events:none;
  background:linear-gradient(100deg,transparent 32%,rgba(255,255,255,.44) 50%,transparent 68%);
  background-size:220% 100%;animation:pfmSheen 1.9s linear infinite}
#pfmBtnAddToCart .pfm-dots{display:inline-flex;gap:3px;align-items:flex-end;height:12px}
#pfmBtnAddToCart .pfm-dots i{width:4px;height:4px;border-radius:50%;background:rgba(255,255,255,.9);
  animation:pfmWave 1.05s ease-in-out infinite}
#pfmBtnAddToCart .pfm-dots i:nth-child(2){animation-delay:.14s}
#pfmBtnAddToCart .pfm-dots i:nth-child(3){animation-delay:.28s}
#pfmBtnAddToCart.pfm-flash{animation:pfmFlash .5s ease}
@media (prefers-reduced-motion: reduce){
  #pfmBtnAddToCart .pfm-sheen,
  #pfmBtnAddToCart .pfm-dots i{animation:none}
}

/* 2026-10-16（用户·甲）：底部按钮「守卫禁用态」—— 没设计/没产品/加载中时变灰、点不动，文案即原因 */
#pfmMockupModal #pfmMcFoot .pfm-btn[disabled]{background:#D8DCE3;border-color:#D8DCE3;color:#6B7280;
  cursor:not-allowed;filter:none!important;box-shadow:none}

/* 2026-10-16（用户：扫完一下然后回闪）：原 keyframes 位移 180%→-80%（=260%）≠ 贴片宽 220%
   ⇒ 循环处接不上、会"跳回闪一下"。改为：贴片 200% 宽、位移正好 200% ⇒ **无缝循环**。
   （同名的 @keyframes 后定义覆盖前定义。） */
#pfmMockupModal #pfmMcFoot .pfm-sheen{background-size:200% 100%}
@keyframes pfmSheen{from{background-position:200% 0}to{background-position:0 0}}

/* ══════════════════════════════════════════════════════════════════════════════
   2026-10-16（用户选甲）：大图容器**按宽度自适应** ⇒ 图片永远完整，不再被切。
   🔴 原 `height:min(44svh,400px)` **只跟视口高有关、与宽度无关** ⇒
      桌面（容器 400 封顶、图更高）⇒ 图被切；手机（svh 偏低、宽度更窄）⇒ 两侧留白。
      一个值伺候两种屏 ⇒ 必然一边切一边空。改为高度随图比例走。
   · 注：这会带来「三态窗口高度可能不同」——用户在知情后选了甲。
   · 空态保留 min-height，否则占位框会塌成 0。
   2026-10-29（用户裁决·甲·修正）：上一版用 flex+align-items:center 让真图去够 44svh 下限，
   实测**图片被裁**（flex 对 aspect-ratio+height:auto 的交叉轴计算与 block 不同，超出部分被
   overflow:hidden 裁掉）。改为反向：**空态降到和真图一致** —— 真图 img 本就是 aspect-ratio:1/1
   方形，那空态也用 aspect-ratio:1/1 方形，两态天然同高、图完整不裁。
   ────────────────────────────────────────────────────────────────────────────── */
#pfmMockupModal .pfm-carousel-main{height:auto;display:block}
#pfmMockupModal .pfm-carousel-main img{width:100%;height:auto;object-fit:contain;display:block}
#pfmMockupModal .pfm-carousel-main.empty{aspect-ratio:1/1}
/* 2026-11-02（用户）：生图弹窗「横屏矮屏」高度断点 —— 大图按视口高压缩，避免弹窗超高滚动、不垂直居中。
   768 高横屏下大图 464→215px，弹窗总高回到 80vh 内一屏放下。只影响横屏且高 ≤800。 */
@media (orientation:landscape) and (max-height:800px){
  #pfmMockupModal .pfm-carousel-main{height:min(28vh,240px);display:flex;align-items:center;justify-content:center}
  #pfmMockupModal .pfm-carousel-main img{width:auto;height:100%;max-width:100%}
  #pfmMockupModal .pfm-carousel-main.empty{aspect-ratio:auto}
}

/* ══════════════════════════════════════════════════════════════════════════════
   2026-10-16（用户选「精致卡」= 草稿条美化第 1 款）
   原样式：淡紫底 #F5F3FF + 淡紫边 + 投影，三个动作平铺 ⇒ 层级偏平、看不出该点哪个。
   改为：白底提可读性 + **左侧 3px 品牌渐变竖条**（点明"这是官方提示"）
        + 主按钮实心紫（Pick up）/ 次按钮描边 / × 保持弱。
   🔴 只改"皮"，不动结构、不动交互。
   ────────────────────────────────────────────────────────────────────────────── */
#pfmDraftBar.pfm-draftbar{background:#fff;border:1px solid #EDEBF5;
  box-shadow:0 10px 28px rgba(26,18,48,.14);padding-left:16px}
#pfmDraftBar.pfm-draftbar::before{content:'';position:absolute;left:0;top:0;bottom:0;width:3px;
  border-radius:8px 0 0 8px;background:linear-gradient(180deg,#A78BFA,#7C3AED)}
/* 主按钮：实心紫（DOM 顺序首个非关闭按钮）——单一主行动 */
#pfmDraftBar .pfm-draftbar-act .pfm-btn:first-child{background:#7C3AED;border-color:#7C3AED;color:#fff;font-weight:800}
#pfmDraftBar .pfm-draftbar-act .pfm-btn:first-child:hover{filter:brightness(1.06)}

/* 2026-10-16（用户：统一）：品牌淡紫**一套值** —— 原弹窗提示区用 #F7F5FF/#E4DEFC，
   与草稿条的 #F5F3FF/#DDD6FE 不一致 ⇒ 全文件对齐到草稿条这套（淡紫只有一份定义）。 */
/* 2026-10-16 手机端草稿条被撑成 290px 高 —— 实测 rect=[14,277,362,290]，视口 844，
   position=fixed / top=422px / bottom=132px ⇒ top:50% 与 bottom:132px 同时生效、又没给 height
   ⇒ 高度被拉成 视口高-50%-132px = 290px，而内容只占 52px（文案23+按钮32），中间 238px 全空。
   修：只在这一档里清掉 top、并去掉垂直方向的 translate —— 保留原有距底 132px 的意图，
       让高度回到内容高。必须同时改 transform，否则会整体上移半个高度。  */
@media (max-width:768px),(orientation:portrait) and (max-width:1024px){
  .pfm-draftbar{top:auto;transform:translateX(-50%)}
}
/* 2026-10-16 手机端：内容换成了两行 ⇒ 条高 87px ⇒ 左侧竖条被拉长显得突兀。
   修：**强制一行 + 内容居中** ⇒ 条高回到单行(~52px)，竖条自然变短。
   代价：文案过长时按已有规则显示省略号（.pfm-draftbar-txt strong 已带 ellipsis）。  */
@media (max-width:768px),(orientation:portrait) and (max-width:1024px){
  .pfm-draftbar{flex-wrap:nowrap;justify-content:center;gap:8px}
  .pfm-draftbar .pfm-draftbar-txt{flex:0 1 auto;min-width:0;text-align:center}
  .pfm-draftbar .pfm-draftbar-act{flex:0 0 auto}
}
/* 2026-10-16（用户）：允许两行 + 每行居中 —— 两行时每行各自居中，左右留白对称，
   不会出现「文案靠左、按钮靠右、右边一片空」。之前压成 nowrap 是搞反了。
   另：左侧竖条改为上下各留 8px + 全圆角 ⇒ 不再是通高长条。  */
@media (max-width:768px),(orientation:portrait) and (max-width:1024px){
  .pfm-draftbar{flex-wrap:wrap;justify-content:center;row-gap:8px}
  .pfm-draftbar .pfm-draftbar-txt{flex:1 1 100%;text-align:center}
  .pfm-draftbar .pfm-draftbar-act{flex:1 1 100%;justify-content:center}
}
/* 竖条：上下留边 + 圆角（不再通高） */
#pfmDraftBar.pfm-draftbar::before{top:8px;bottom:8px;border-radius:999px}
/* 2026-10-16（用户：两端不一致 —— 我只改了手机档）：
   把**竖条样式提为全局**（桌面同样是上下留边 + 圆角），两档观感统一。
   桌面布局仍保留一行（宽屏一行更省空间，文案左 / 按钮右）。  */
#pfmDraftBar.pfm-draftbar::before{top:8px;bottom:8px;border-radius:999px}
/* 2026-10-16（用户：电脑和手机一样）：**把两行居中布局提为全局** ⇒ 两端完全一致。
   文字一行（占满宽、文字居中）+ 按钮一行（占满宽、内容居中）。
   🔴 注意：**位置**仍分档（手机 top:auto/距底132px；桌面 top:50% 居中悬浮）——
      桌面没有 bottom，若也设 top:auto 会失去垂直定位 ⇒ 位置统一属于"放进布局流"另一步。  */
.pfm-draftbar{flex-wrap:wrap;justify-content:center;row-gap:8px}
.pfm-draftbar .pfm-draftbar-txt{flex:1 1 100%;text-align:center}
.pfm-draftbar .pfm-draftbar-act{flex:1 1 100%;justify-content:center}
