/* assets/css/workspace-ui.css — Workspace 共享 UI 层（Phase 10A2）
 *
 * ── 它是什么 ────────────────────────────────────────────────────────
 * Image Workspace 与 PDF Workspace 共用的**组件视觉契约**：
 *   custom select / popover menu / advanced accordion /
 *   primary·secondary·ghost 三级按钮 / Workspace 外壳 token。
 *
 * ── 它不是什么（硬边界，10A2 §7）────────────────────────────────────
 * 它**不拥有**任何 Image / PDF 的 state、Engine 行为、Intent 分支或埋点。
 * 那些仍然只属于各自的 Workbench 与 Workspace 模型。这一层只回答
 * "一个 select / 一个菜单 / 一个 accordion 长什么样、怎么排"。
 *
 * 色值一律引用 tokens.css，只在这里额外定义 Workspace 才有的尺寸档。
 */

.yqw {
  /* ── 排版五档：每一档必须一眼可分，不是"差不多大"（10A1-C 冻结）── */
  --yqw-t1-size: 21px;  --yqw-t1-weight: 680;  --yqw-t1-lh: 1.25;   /* 任务标题 */
  --yqw-t2-size: 15px;  --yqw-t2-weight: 600;  --yqw-t2-lh: 1.35;   /* 主对象 / 主状态 */
  --yqw-t3-size: 13px;  --yqw-t3-weight: 500;  --yqw-t3-lh: 1.4;    /* 控件标签 */
  --yqw-t4-size: 12px;  --yqw-t4-weight: 450;  --yqw-t4-lh: 1.45;   /* 元数据 */
  --yqw-t5-size: 11px;  --yqw-t5-weight: 400;  --yqw-t5-lh: 1.45;   /* 诊断 */

  /* ── Task Header 节奏：全站一处，任何状态都不许各排各的（10A1-D §4）── */
  --yqw-title-subtitle-gap: 6px;
  --yqw-header-content-gap: 22px;
  --yqw-subtitle-size: 14px;

  /* ── 控件高度分档：类型不同，视觉权重就该不同 ── */
  --yqw-h-primary: 46px;
  --yqw-h-select: 42px;
  --yqw-h-secondary: 36px;
  --yqw-h-ghost: 30px;
  --yqw-h-mini: 28px;   /* 浮层 / 对象区里的行内动作 */

  /* ── 标准字段宽度：Workspace 的**第二层几何 authority**（Phase 19A1.2）──
     19A1.1 统一了同 tier 控件的**高度**，但宽度当时还各自为政，实测（1440×900，
     Image 输出 JPG）：
         Export 104px（= trigger 的 min-width 兜底）
         Quality 96px（.yqw-field input[type=number] 的本地 width）
         Background 156 / 246.91 / 244.58 / 180px（zh / en / vi / ja）
     Background 那一列随 locale 摆动 90.91px，根因是 `.yqw-field` 是 inline-flex
     **无显式宽度**（内容撑开），而 helper 说明文案就在它里面 ——
     于是「文案越长 → 字段越宽 → 上面的控件跟着变宽」。

     ⚠️ 168px 不是拍的，三条证据：
       ① 它是共享层里**唯一一个刻意为标准文本字段定的宽度**
          （原 `.yqw-field input[type="text"]`，即 Select range 的输入框）——
          复用既有 authority 好过发明一个新数字；
       ② 实测 9 个 locale 里最长的选项文字 = 51px（ru「Чёрный」），
          trigger 左右 padding 合计 56px ⇒ 不出省略号所需最小宽 = **107px**；
          最长 label = 70px（en「Background」），远小于 168，不会换行；
       ③ Founder 明确「Quality 96 太窄、Background 视觉更合理」，
          而他截图那门语言（zh）的 Background 实测是 156px —— 168 与它同一量级。 */
  --yqw-field-w: 168px;

  /* ── 可滚容器的滚动条（组件规范，见下面 .yqw-scroll）── */
  --yqw-scroll-w: 12px;      /* 占位宽：thumb 可见宽 = 这个减去两侧 pad */
  --yqw-scroll-pad: 2px;     /* thumb 两侧透明边 = 它与容器边缘的固定间距 */
  --yqw-scroll-end: 10px;    /* 轨道两端的空档：不许顶到容器圆角上 */

  /* ── Advanced 四态：REST / HOVER / EXPANDED / DETAIL 必须肉眼可分 ── */
  --yqw-adv-outer-bg: #F8FAFC;
  --yqw-adv-rest-bg: #FFFFFF;
  --yqw-adv-hover-bg: #F3F6FA;
  --yqw-adv-expanded-bg: #EAF1FE;
  --yqw-adv-child-bg: #FFFFFF;

  /* ── Primary 三档蓝：hover / active 只压深，不换语义（10A1-F §3）── */
  --yqw-primary: var(--yq-primary);
  --yqw-primary-hover: var(--yq-primary-hover);
  --yqw-primary-active: #1E40AF;
  --yqw-primary-disabled-bg: #DBE7FB;
  --yqw-primary-disabled-text: #4B69A8;

  --yqw-text-faint: #9CA3AF;
  --yqw-sunken: #F8FAFC;

  /* ── Composition（10A2-B）：Image 与 PDF 的**同一份** authority ──
     Founder 在生产上看到的是"太宽、太扁、两条线像两个产品"，
     根因是外层 composition 从来没有统一过 —— 交互层级统一了，
     但外壳、内边距、空态体量各走各的。这一组 token 就是那份统一。 */
  --yqw-max-width: 1200px;                        /* 1280 实测仍偏宽（§9） */
  --yqw-page-pad-block: clamp(36px, 5vh, 56px);   /* 页面级外边距（§10） */
  --yqw-page-pad-inline: clamp(68px, calc(4vw + 40px), 96px);
  --yqw-surface-pad: clamp(28px, 3vw, 40px);      /* Surface 内边距（§11） */
  --yqw-section-gap: 24px;                        /* 大区块节奏（§12） */
  --yqw-dropzone-min: clamp(340px, 42vh, 460px);
  /* 空态外壳的高度下限（V0.0.1230）：Image 与 PDF **两条线一样高**。
     ⚠️ 不写死像素 —— 跟视口走（面板与视口的比例才是"扁不扁"的判据）。
     多出来的高度由 dropzone 吸收，绝不堆成尾部死白。 */
  --yqw-empty-min: clamp(560px, 72vh, 660px);  /* 空态体量（§15） */
}

/* 作者样式普遍是 display:flex/grid，会盖过 UA 的 [hidden]。删了这一条，
   `el.hidden = true` 会**静默失效**（Quiet UI V2 与 export bar 各交过一次学费）。 */
.yqw [hidden] { display: none !important; }
.yqw *, .yqw *::before, .yqw *::after { box-sizing: border-box; }
.yqw :focus-visible { outline: 2px solid var(--yqw-primary); outline-offset: 2px; }

/* ═══════════════════════ Workspace Surface ═══════════════════════
   **Image 与 PDF 唯一的外壳权威**（10A2-B §4/§5/§6）。
   在这之前 Image 的外壳来自 Quiet UI V2 的 .yq2-panel，PDF 干脆没有外壳，
   于是两条线共用了组件却仍然像两个产品。

   width:100% + max-width，绝不写死宽度：容器比它窄时自然满宽（§9）。
   高度**永远不写死**：空态高度由 header + dropzone + padding + trust
   自然形成，ready 态有多少内容就多高 —— 固定高度只会制造死白（§17）。 */
.yqw-surface {
  width: 100%; max-width: var(--yqw-max-width); margin-inline: auto;
  background: var(--yq-bg-page);
  border: 1px solid var(--yq-border);
  border-radius: var(--yq-r-surface);
  padding: var(--yqw-surface-pad);
  display: flex; flex-direction: column; gap: var(--yqw-section-gap);
}
.yqw-frame { width: 100%; max-width: var(--yqw-max-width); margin-inline: auto; }

/* ═══════════════════════ Dropzone ═══════════════════════
   空态是两条线**共享**的组件（§13/§14）：背景 / 虚线 / 圆角 / 图标 /
   标题排版 / 提示排版 / CTA 间距 / hover 全部同一份。
   允许不同的只有：标题文案、支持格式串、accept。 */
.yqw-dropzone {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; text-align: center; cursor: pointer;
  min-height: var(--yqw-dropzone-min);
  padding: clamp(24px, 4vh, 40px) 24px;
  background: var(--yq-bg-sidebar);
  border: 1.5px dashed var(--yq-border-strong);
  border-radius: var(--yq-r-card);
  transition: border-color var(--yq-dur) var(--yq-ease), background var(--yq-dur) var(--yq-ease);
}
.yqw-dropzone:hover,
.yqw-dropzone.is-dragover { border-color: var(--yqw-primary); background: var(--yq-primary-weak); }
.yqw-dropzone.is-disabled { opacity: .5; pointer-events: none; }
.yqw-dz-icon { color: var(--yq-text-3); display: block; }
.yqw-dz-icon svg { width: 32px; height: 32px; }
.yqw-dz-title { font-size: var(--yqw-t2-size); font-weight: var(--yqw-t2-weight);
                line-height: var(--yqw-t2-lh); color: var(--yq-text-1); }
.yqw-dz-hint { font-size: var(--yqw-t4-size); color: var(--yq-text-3); margin-bottom: 6px; }

/* ═══════════════════════ Task Header ═══════════════════════
   Title row → Job promise → count。间距由 token 决定，
   有没有 "Add files" 按钮都不改变节奏（10A1-D 实测踩过）。 */
/* Task Header 不再自己管横向 inset —— 那属于 Surface（§8）。
   它只负责 title → promise → count 这一段的纵向节奏。
   ⚠️ 曾经的缺陷：Image 的 .yqw-head 直接挂在 .yq2-panel 下，而那个 panel
   只有 background/border/radius **没有 padding**，于是标题贴着卡片边缘，
   与下面有 padding 的 body 左边缘对不齐 —— 看起来就是"标题跑位"。
   修法是让所有区块共用同一个 Surface inset，不是给 h1 单独加 margin。 */
.yqw-head { padding-bottom: 0; }
.yqw-head-row { display: flex; align-items: center; gap: 12px;
                /* ⚠️ 行高按**这一行可能出现的最高动作**定档（secondary 36px），
                   不按有没有按钮定 —— 否则 Image（右上角有 Add files/Clear）与
                   PDF（只有 ⋮）的标题基线会差 3px，两条线看起来就不是一套版式。
                   10A1-D 已经用同一个修法处理过"有/无按钮"的节奏差。 */
                min-height: var(--yqw-h-secondary); }
.yqw-head-id { min-width: 0; }
.yqw-head-row .yqw-grow { flex: 1 1 auto; }
.yqw-title { font-size: var(--yqw-t1-size); font-weight: var(--yqw-t1-weight);
             line-height: var(--yqw-t1-lh); color: var(--yq-text-1); margin: 0; }
.yqw-promise { font-size: var(--yqw-subtitle-size); line-height: 1.5; color: var(--yq-text-2);
               margin: var(--yqw-title-subtitle-gap) 0 0; max-width: 64ch; }
.yqw-count { margin-top: 8px; }
.yqw-count-badge { display: inline-block; font-size: var(--yqw-t4-size); font-weight: 450;
                   color: var(--yq-text-3); background: var(--yq-bg-quiet);
                   border-radius: 999px; padding: 2px 9px; }

/* ═══════════════════════ 按钮三级 ═══════════════════════ */
.yqw-btn { font: inherit; font-size: var(--yqw-t3-size); font-weight: 500;
           height: var(--yqw-h-secondary); padding: 0 14px;
           border: 1px solid var(--yq-border); border-radius: var(--yq-r-ctl);
           background: #fff; color: var(--yq-text-2); cursor: pointer;
           display: inline-flex; align-items: center; justify-content: center; gap: 8px;
           white-space: nowrap; }
/* ⚠️ 这条基础 hover 曾经把 primary 的白字改成近黑色（10A1-F 实测）。
   secondary 的样式不许污染 primary：这里**结构性地**把 primary 排除掉，
   而不是靠下面某条规则的 source order 碰巧盖回来（§60）。 */
.yqw-btn:hover:not(:disabled):not(.yqw-btn--primary) { border-color: var(--yq-border-strong); color: var(--yq-text-1); }
.yqw-btn:disabled { opacity: .45; cursor: not-allowed; }

.yqw-btn--primary { height: var(--yqw-h-primary); padding: 0 22px;
                    font-size: var(--yqw-t2-size); font-weight: var(--yqw-t2-weight);
                    background: var(--yqw-primary); border-color: var(--yqw-primary);
                    border-radius: var(--yq-r-ctl); }
.yqw-btn--primary,
.yqw-btn--primary:hover,
.yqw-btn--primary:active,
.yqw-btn--primary:focus-visible { color: #fff; }
.yqw-btn--primary:hover:not(:disabled) { background: var(--yqw-primary-hover); border-color: var(--yqw-primary-hover); }
.yqw-btn--primary:active:not(:disabled) { background: var(--yqw-primary-active); border-color: var(--yqw-primary-active); }
/* disabled 用淡蓝 + 高对比文字：既不像 enabled，也不是一坨看不清的灰。 */
.yqw-btn--primary:disabled { background: var(--yqw-primary-disabled-bg);
                             border-color: var(--yqw-primary-disabled-bg);
                             color: var(--yqw-primary-disabled-text); opacity: 1; }

/* Header 右上角那对动作（10A3 §2.2/§2.3）：
   Add files 是高频动作，要比普通 secondary 更显眼 —— 浅蓝底 + 蓝描边 + 蓝字；
   Clear workspace 安静一档。
   ⚠️ 都**不与主 CTA 抢**：主 CTA 是实底深蓝，这两个都不是实底。
   层级：Export（实底蓝） > Add files（浅蓝 tint） > Clear（安静） */
.yqw-btn--accent { background: var(--yq-primary-weak); border-color: #BFDBFE;
                   color: var(--yq-primary-hover); font-weight: 550; }
.yqw-btn--accent:hover:not(:disabled) { background: #DCE9FE; border-color: var(--yqw-primary);
                                        color: var(--yq-primary-hover); }
.yqw-btn--quiet { background: transparent; border-color: transparent; color: var(--yq-text-3);
                  font-weight: 450; }
/* neutral：**看得见边框**的次级动作（10A3-B §17）。与 accent 同一高度档，
   区别只在权重 —— accent 有底色和更重的字，neutral 只有一圈中性描边。
   ⚠️ 这是新增的一档，既有 quiet / accent 一个字没改，所以 PDF 那条线
   （它不用这三个 modifier）计算样式 0 变化。 */
.yqw-btn--neutral { background: #fff; border-color: var(--yq-border-strong);
                    color: var(--yq-text-2); font-weight: 500; }
.yqw-btn--neutral:hover:not(:disabled) { background: var(--yq-bg-quiet);
                                         border-color: var(--yq-border-strong);
                                         color: var(--yq-text-1); }
.yqw-btn--quiet:hover:not(:disabled) { background: var(--yq-bg-quiet); color: var(--yq-text-2);
                                       border-color: transparent; }

/* mini：**对象区内部**那条 contextual toolbar 用的一档（10B1 §24/§32）。
   它必须比 Header 的 secondary 更轻 —— 那条 toolbar 是工具，不是主流程；
   Export 的权重不许被它抢走。 */
.yqw-btn--mini { height: var(--yqw-h-mini); padding: 0 10px; font-size: var(--yqw-t4-size);
                 font-weight: 450; border-radius: var(--yq-r-ctl); }

.yqw-btn--ghost { height: var(--yqw-h-ghost); padding: 0 10px; border-color: transparent;
                  background: transparent; color: var(--yq-text-3); font-weight: 450; }
.yqw-btn--ghost:hover:not(:disabled) { background: var(--yq-bg-quiet); color: var(--yq-text-2);
                                       border-color: transparent; }

/* ═══════════════════════ Custom Select ═══════════════════════
   为什么不用 native <select>：它的弹层由**系统**绘制，macOS 会把菜单直接
   盖在控件上，"菜单必须在下拉框下面"这条规范用原生控件保证不了（§49）。
   真正的取值权威仍然是隐藏的 <select>（§52），这里只是它的可见外观。 */
.yqw-sel { position: relative; display: inline-block; }
.yqw-sel > select { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
                    overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
                    white-space: nowrap; border: 0; }
.yqw-sel-trigger { display: flex; align-items: center; gap: 8px; font: inherit;
                   font-size: var(--yqw-t3-size); font-weight: 500;
                   height: var(--yqw-h-select); padding: 0 42px 0 14px;
                   border: 1px solid var(--yq-border-strong); border-radius: var(--yq-r-ctl);
                   background: #fff; color: var(--yq-text-1); cursor: pointer;
                   /* ⚠️ 这是"没有被字段宽度管到"时的**兜底下限**，不是设计宽度。
                      原值 104px 实测对俄语偏窄 3px：最长选项「Чёрный」51px +
                      左右 padding 56px = 107px，104 会让它出省略号。
                      标准 tier 现在由 --yqw-field-w 统一管宽，这条只在兜底路径上生效。 */
                   min-width: 112px; width: 100%; line-height: 1; text-align: left;
                   position: relative; }
.yqw-sel-trigger:hover:not(:disabled) { border-color: var(--yq-text-3); }
.yqw-sel-trigger:disabled { opacity: .45; cursor: not-allowed; }
.yqw-sel-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.yqw-sel-chev { position: absolute; right: 14px; top: 50%; transform: translateY(-50%);
                width: 13px; height: 13px; color: var(--yq-text-3); pointer-events: none;
                transition: transform var(--yq-dur) var(--yq-ease); }
.yqw-sel-trigger[aria-expanded="true"] .yqw-sel-chev { transform: translateY(-50%) rotate(180deg); }
/* 菜单上沿永远在 trigger 下沿之下 —— 这是这个组件存在的理由（§50）。 */
.yqw-sel-pop { position: absolute; top: calc(100% + 6px); left: 0; min-width: 100%; z-index: 50;
               background: #fff; border: 1px solid var(--yq-border); border-radius: 9px;
               box-shadow: var(--yq-shadow-pop); padding: 6px; max-height: 288px;
               overflow-y: auto; }
.yqw-sel-pop.is-right { left: auto; right: 0; }
/* 下方装不下就翻到 trigger **上方** 6px（V0.0.1246）：仍然不盖住 trigger，
   但也绝不把文档顶高 —— 那会让 iframe 长出滚动条、外壳宽度跟着变。
   两边都装不下时由 JS 夹住高度，这里负责让它自己滚。 */
.yqw-sel-pop.is-up { top: auto; bottom: calc(100% + 6px); }
.yqw-sel-pop { overflow-y: auto; }
.yqw-sel-opt { display: flex; align-items: center; gap: 8px; width: 100%; font: inherit;
               font-size: var(--yqw-t3-size); font-weight: 450; color: var(--yq-text-1);
               height: 38px; padding: 0 13px; border: 0; background: transparent;
               border-radius: 6px; cursor: pointer; text-align: left; white-space: nowrap; }
.yqw-sel-opt:hover:not(:disabled), .yqw-sel-opt.is-active { background: var(--yq-bg-quiet); }
.yqw-sel-opt[aria-selected="true"] { background: var(--yq-primary-weak);
                                     color: var(--yq-primary-hover); font-weight: 550; }
.yqw-sel-opt:disabled { color: var(--yq-text-3); cursor: not-allowed; }
.yqw-sel-opt .yqw-tick { width: 14px; flex: 0 0 auto; color: var(--yqw-primary);
                         visibility: hidden; display: flex; }
.yqw-sel-opt[aria-selected="true"] .yqw-tick { visibility: visible; }

/* ═══════════════════════ Field（label + 控件是一个 Gestalt 组）═══ */
/* ── 两层契约（19A1.2 §6）────────────────────────────────────────────
     STANDARD_FIELD    拥有宽度
     STANDARD_CONTROL  width: 100%（宽度属于 field，不属于控件自己的内容）
     label / helper    只在 field 内换行，**永远不能反向决定控件宽度**
   ⚠️ `max-width: 100%` 是响应式的那一半：窄到装不下时字段收成容器宽，
      但那是**视口**决定的，不是文案长度决定的。 */
.yqw-field { display: inline-flex; flex-direction: column; gap: 4px;
             width: var(--yqw-field-w); max-width: 100%; min-width: 0; }
.yqw-field > .yqw-label { font-size: var(--yqw-t4-size); font-weight: 500; color: var(--yq-text-3); }
.yqw-field input[type="number"], .yqw-field input[type="text"] {
  font: inherit; font-size: var(--yqw-t3-size); height: var(--yqw-h-select);
  padding: 0 12px; border: 1px solid var(--yq-border-strong); border-radius: var(--yq-r-ctl);
  background: #fff; color: var(--yq-text-1); width: 100%; }
/* 字段内的自定义 select 与它的 trigger 同样填满字段 */
.yqw-field > .yqw-sel { display: block; width: 100%; }
/* ⚠️ helper / 说明段：显式 100% + 正常换行。它在 field 内**只能换行**，
      不许再参与字段的固有宽度计算（那正是 19A1.2 之前的缺陷）。
      绝不为了稳定宽度去截断说明文案（§7）。 */
.yqw-field > p { width: 100%; max-width: 100%; white-space: normal; overflow-wrap: anywhere; }

/* Export 虽然自己单独一行，也吃**同一个** field 宽度（§8）——
   不因为"它不在 .yqw-field 里"就拥有另一套随机宽度。 */
.yqw-complete-line > .yqw-sel { width: var(--yqw-field-w); max-width: 100%; }
/* 只给读屏用的 label（10B1 §28）：视觉上"Export as"只讲一次，
   但 `<select>` 仍然需要一个被程序化关联的名字。
   ⚠️ 不能用 `display:none` / `hidden` —— 那样读屏也读不到了。 */
.yqw-sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
          overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* 状态就是文字，不是胶囊（§31）。 */
.yqw-state { font-size: var(--yqw-t4-size); color: var(--yq-text-3); margin: 0; }

/* ═══════════════════════ Popover menu ═══════════════════════ */
.yqw-menu { position: absolute; z-index: 60; min-width: 188px; background: #fff;
            border: 1px solid var(--yq-border); border-radius: var(--yq-r-ctl);
            box-shadow: var(--yq-shadow-pop); padding: 4px; }
.yqw-menu button { display: flex; align-items: center; gap: 12px; width: 100%; font: inherit;
                   font-size: var(--yqw-t3-size); font-weight: 450; color: var(--yq-text-1);
                   height: 34px; padding: 0 8px; border: 0; background: transparent;
                   border-radius: 6px; cursor: pointer; text-align: left; }
.yqw-menu button:hover:not(:disabled) { background: var(--yq-bg-quiet); }
.yqw-menu button:disabled { opacity: .45; cursor: not-allowed; }
.yqw-menu button .yqw-ico { width: 16px; display: flex; justify-content: center;
                            color: var(--yq-text-3); flex: 0 0 auto; }
.yqw-menu button.is-danger { color: var(--yq-danger); }
.yqw-menu button.is-danger .yqw-ico { color: var(--yq-danger); }
.yqw-menu hr { border: 0; border-top: 1px solid var(--yq-border); margin: 4px; }

/* ── Popover：装任意内容的浮层（与 .yqw-menu 同一条定位规范）──────────
   ⚠️ 与 select 弹层同一条纪律：**永远在 trigger 下方**，放不下只在视口内夹取，
   绝不向上翻转盖住 trigger（10A2 §51）。 */
.yqw-popover { position: absolute; z-index: 60; background: #fff;
               /* 内边距做成令牌：浮层里的可滚容器要按它反算滚动条的位置。 */
               --yqw-pop-pad-x: 16px;
               border: 1px solid var(--yq-border); border-radius: var(--yq-r-card);
               box-shadow: var(--yq-shadow-pop);
               padding: 14px var(--yqw-pop-pad-x); max-width: 340px; }
/* 装一张表（来源清单）的那一档：给文件名留出真正够用的宽度 ——
   它是靠内容撑的，所以下限也要给，否则一堆短文件名会把表挤成一条。 */
.yqw-popover--wide { min-width: min(480px, calc(100vw - 32px));
                     max-width: min(600px, calc(100vw - 32px)); }
/* 装一个小表单（range）的那一档：比默认宽一点，字段不至于挤成一条缝。 */
.yqw-popover--form { max-width: 360px; min-width: 300px; }

/* 卡片上的 ⋮：桌面 hover/focus 才显形，触屏常驻可 tap（§20/§30/§65）。
   ⚠️ 隐藏用 opacity 是**故意**的：这是"可发现性"而不是"progressive disclosure"，
   按钮始终在 tab 顺序里；真正该隐藏的控件一律 display:none（§63）。 */
.yqw-kebab { position: absolute; top: 6px; right: 6px; width: 28px; height: 28px;
             display: inline-flex; align-items: center; justify-content: center;
             border: 1px solid var(--yq-border); border-radius: 8px; background: #fff;
             color: var(--yq-text-3); cursor: pointer; padding: 0;
             opacity: 0; transition: opacity var(--yq-dur) var(--yq-ease); }
.yqw-kebab:hover { color: var(--yq-text-1); }
:where(.yqw) *:hover > .yqw-kebab,
:where(.yqw) *:focus-within > .yqw-kebab,
.yqw-kebab:focus-visible,
.yqw-kebab[aria-expanded="true"] { opacity: 1; }
/* 触屏与窄屏都不能依赖 hover 去发现动作（§65）。
   ⚠️ 只写 `hover: none` 是不够的：窄视口 + 鼠标（桌面缩窗、测试环境）
   仍然会拿到 hover:hover，于是"手机上能不能发现"这件事根本没被覆盖。 */
@media (hover: none), (max-width: 560px) { .yqw-kebab { opacity: .7; } }

/* ═══════════════════════ Completion ═══════════════════════
   左设置 / 右动作属于**同一个完成步骤**，所以它们在同一个带底色的区域里。
   CTA 相对整块面板垂直居中，不跟某一行文字对齐（§54）。 */
.yqw-complete { background: var(--yqw-sunken); border: 1px solid var(--yq-border);
                border-radius: var(--yq-r-ctl); padding: 16px;
                display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: stretch; }
.yqw-complete-main { min-width: 0; display: flex; flex-direction: column; gap: 12px;
                     align-items: flex-start; }
.yqw-complete-label { font-size: var(--yqw-t5-size); font-weight: 600; letter-spacing: .08em;
                      text-transform: uppercase; color: var(--yq-text-3); margin: 0; }
.yqw-complete-result { font-size: var(--yqw-t4-size); color: var(--yqw-text-faint); margin: 0; }
.yqw-complete-action { display: flex; align-items: center; justify-content: flex-end; gap: 12px;
                       min-width: 196px; padding-left: 28px; }

/* ── Completion 的单行变体（10A3 §3/§4）─────────────────────────
   一个区块标题（不重复），下面**一整行**：左边格式选择、右边导出按钮。
   硬要求：两者同高、垂直中心严格对齐 —— 不许一个高按钮配一个矮 select。
   所以这里把 primary 的高度对齐到 select 那一档。 */
.yqw-complete--row { display: flex; flex-direction: column; gap: 12px; align-items: stretch; }
.yqw-complete-line { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
/* 标题行（10A3-B §28）：左 eyebrow、右 N/M Ready。两边**顶边对齐**，
   所以两个节点的 line-height 必须同一档 —— 靠 baseline 对齐会差一两个像素，
   而这一条是要被几何断言逐像素量的。 */
.yqw-complete-line--head { align-items: flex-start; }
.yqw-complete-line--head .yqw-complete-label,
.yqw-complete-line--head .yqw-complete-result { line-height: 16px; }
.yqw-complete-line--head .yqw-complete-result { text-align: right; }
.yqw-complete-line .yqw-grow { flex: 1 1 auto; }
/* 高度对齐到 select 那一档（§4 的硬要求），但**字号不降档** ——
   primary 的标签仍然比 secondary/quiet 大，层级不能因为对齐高度而丢掉。 */
.yqw-complete--row .yqw-btn--primary { height: var(--yqw-h-select); padding: 0 20px; }
.yqw-complete--row .yqw-sel-trigger { height: var(--yqw-h-select); }

/* 次级动作与 Advanced 各占一条弱行，和 completion 明显隔开 */
.yqw-secondary { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 12px 0 0; }
.yqw-secondary .yqw-grow { flex: 1 1 auto; }

/* ═══════════════════════ Selection context ═══════════════════════ */
.yqw-selbar { background: var(--yq-primary-weak); border: 1px solid #BFDBFE;
              border-radius: var(--yq-r-ctl); padding: 12px 16px; }
.yqw-selbar-top { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.yqw-selbar .yqw-mode { font-size: var(--yqw-t5-size); font-weight: 600; letter-spacing: .08em;
                        text-transform: uppercase; color: var(--yq-primary-hover); margin: 0; }
.yqw-selbar .yqw-selcount { font-size: var(--yqw-t2-size); font-weight: var(--yqw-t2-weight);
                            color: var(--yq-text-1); margin: 0; }
.yqw-selbar .yqw-grow { flex: 1 1 auto; }
.yqw-selbar-actions { display: flex; gap: 8px; align-items: center; margin-top: 8px; flex-wrap: wrap; }
.yqw-selbar .yqw-btn:not(.yqw-btn--primary) { background: rgba(255, 255, 255, .75); border-color: #BFDBFE; }

/* ═══════════════════════ Advanced ═══════════════════════
   trigger 与 panel 是**两个独立的盒子**，中间留一级空间（§41）。 */
.yqw-adv { margin-top: 8px; }
.yqw-adv-toggle { display: flex; align-items: center; gap: 8px; width: 100%; font: inherit;
                  font-size: var(--yqw-t3-size); font-weight: 450; color: var(--yq-text-3);
                  background: #fff; border: 1px solid var(--yq-border);
                  border-radius: var(--yq-r-ctl); height: 50px; padding: 0 17px;
                  cursor: pointer; text-align: left; }
.yqw-adv-toggle:hover { color: var(--yq-text-2); background: var(--yqw-adv-hover-bg); }
.yqw-adv-toggle[aria-expanded="true"] { color: var(--yq-text-2); }
.yqw-adv-toggle .yqw-chev { margin-left: auto; color: var(--yqw-text-faint);
                            transition: transform var(--yq-dur) var(--yq-ease); }
.yqw-adv-toggle[aria-expanded="true"] .yqw-chev { transform: rotate(90deg); }
.yqw-adv-panel { margin-top: 11px; border: 1px solid var(--yq-border); border-radius: 11px;
                 overflow: hidden; background: var(--yqw-adv-outer-bg); }
.yqw-adv-list { list-style: none; margin: 0; padding: 0; background: transparent; }
.yqw-adv-list li + li { border-top: 1px solid var(--yq-border); }
/* REST */
.yqw-adv-cat { display: flex; align-items: flex-start; gap: 12px; width: 100%; font: inherit;
               background: var(--yqw-adv-rest-bg); border: 0; padding: 15px 17px;
               cursor: pointer; text-align: left; }
/* HOVER —— 只表达"可点"，不做位移与阴影 */
.yqw-adv-cat:hover { background: var(--yqw-adv-hover-bg); }
/* EXPANDED —— 与 hover 必须不同；且**不靠常驻蓝框**表达（§47） */
.yqw-adv-cat[aria-expanded="true"] { background: var(--yqw-adv-expanded-bg); }
.yqw-adv-cat .yqw-cat-name { font-size: var(--yqw-t3-size); font-weight: 500;
                             color: var(--yq-text-2); display: block; }
.yqw-adv-cat .yqw-cat-sum { font-size: var(--yqw-t4-size); color: var(--yqw-text-faint);
                            display: block; margin-top: 2px; }
.yqw-adv-cat .yqw-chev { margin-left: auto; color: var(--yqw-text-faint); flex: 0 0 auto;
                         margin-top: 2px; transition: transform var(--yq-dur) var(--yq-ease); }
.yqw-adv-cat[aria-expanded="true"] .yqw-chev { transform: rotate(90deg); color: var(--yq-text-2); }
.yqw-adv-cat[aria-expanded="true"] .yqw-cat-name { color: var(--yq-text-1); font-weight: 550; }
/* DETAIL —— 白卡内嵌在 tinted parent 之下，四周都有 inset（§45，top 绝不能是 0） */
.yqw-adv-body { background: var(--yqw-adv-child-bg); margin: 12px 12px 14px;
                border: 1px solid var(--yq-border); border-radius: 11px; padding: 17px;
                display: flex; flex-wrap: wrap; gap: 16px 24px; align-items: flex-end; }
.yqw-adv-body .yqw-full { width: 100%; }
/* Diagnostics 视觉权重最低：rest 更弱，hover 回到 secondary，展开才 normal（§48） */
.yqw-adv--diag .yqw-adv-cat .yqw-cat-name { color: var(--yqw-text-faint); }
.yqw-adv--diag .yqw-adv-cat:hover .yqw-cat-name { color: var(--yq-text-2); }
.yqw-adv--diag .yqw-adv-cat[aria-expanded="true"] .yqw-cat-name { color: var(--yq-text-1); }
.yqw-log { width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
           font-size: var(--yqw-t5-size); color: var(--yq-text-3); background: var(--yq-bg-quiet);
           border-radius: 6px; padding: 8px 12px; margin: 0; white-space: pre-wrap;
           max-height: 180px; overflow-y: auto; }

/* ═══════════════════════ Notice 三档（§61）═══════════════════════
   不是每一种提示都值一整块。 */
.yqw-notice-action { display: flex; gap: 12px; background: var(--yq-primary-weak);
                     border: 1px solid #BFDBFE; border-radius: var(--yq-r-ctl);
                     padding: 12px 16px; }
.yqw-notice-action .yqw-ico { color: var(--yqw-primary); flex: 0 0 auto; margin-top: 1px; }
.yqw-notice-action .yqw-hd { font-size: var(--yqw-t3-size); font-weight: 600;
                             color: var(--yq-text-1); margin: 0 0 2px; }
.yqw-notice-action .yqw-bd { font-size: var(--yqw-t4-size); color: var(--yq-text-2); margin: 0; }
.yqw-notice-warn { background: #FFFBEB; border: 1px solid #FDE68A; border-radius: 8px;
                   padding: 8px 12px; font-size: var(--yqw-t4-size); color: #92400E; margin: 0; }
.yqw-notice-info { font-size: var(--yqw-t4-size); color: var(--yqw-text-faint); margin: 0; }

/* ═══════════════ 可滚容器（组件规范 · V0.0.1228 冻结）═══════════════
   任何"内部滚动 + 有边框"的容器都走这一条，别各写各的：

     · 滚动条**必须看得见**。macOS 默认是 overlay 滚动条（平时隐形、不占宽度），
       用户不知道下面还有内容。给 ::-webkit-scrollbar 一个明确宽度会让 Chrome
       切回占位式滚动条；`scrollbar-gutter: stable` 再把那条槽稳定留出来 ——
       两条缺一不可（只给宽度实测仍是 overlay，占宽 0）。
     · ⚠️ **不要**同时写标准的 `scrollbar-width` / `scrollbar-color`：新版 Chrome
       里只要设了它们，下面这几条 ::-webkit-scrollbar 规则就整组失效，
       于是又退回隐形 overlay。
     · thumb 两侧留 --yqw-scroll-pad 的透明边（background-clip: content-box），
       所以它与容器边缘之间始终是同一条固定间距；改粗细只动 --yqw-scroll-w，
       间距不会跟着变。
     · **两端各留 --yqw-scroll-end 的空档**：轨道顶到容器圆角上很难看，
       滚到头时 thumb 必须停在圆角之内（Founder 实测提出）。 */
.yqw-scroll { scrollbar-gutter: stable; }
.yqw-scroll::-webkit-scrollbar { width: var(--yqw-scroll-w); }
.yqw-scroll::-webkit-scrollbar-track { background: transparent;
                                       margin: var(--yqw-scroll-end) 0; }
.yqw-scroll::-webkit-scrollbar-thumb { background: var(--yq-border-strong);
                                       border-radius: 8px;
                                       border: var(--yqw-scroll-pad) solid transparent;
                                       background-clip: content-box; }
.yqw-scroll::-webkit-scrollbar-thumb:hover { background: var(--yq-text-3);
                                             background-clip: content-box; }

/* ═══════════════ 空态：两条线等高（V0.0.1230）═══════════════
   Founder 要求 Image 的容器高度与 PDF Workspace 一致。
   ⚠️ 实现方式不是给外壳加 min-height（那会在"内容本来就更高"的视口上再次分叉，
   而且多出来的空间只会堆成死白），而是**把两条线的非拖放区 chrome 做成等高**：
   两边都是 head + dropzone + trust + 同样的 section gap，于是共享的
   dropzone 档位一决定，两条线的外壳高度就自动一致。
   trust 的等高做法见 image-workbench.css（Image 没有那条分割线，
   但用同样的上内边距把盒高补齐）。 */

/* ═══════════════════════ Trust ═══════════════════════
   页面最弱的一行：必须弱于 Advanced 入口、field label 与对象标签（§28）。 */
.yqw-trust { display: flex; align-items: center; gap: 8px; padding-top: 12px;
             border-top: 1px solid var(--yq-border); margin-top: 12px; }
.yqw-trust .yqw-dot { width: 5px; height: 5px; border-radius: 50%; background: #34D399;
                      flex: 0 0 auto; }
.yqw-trust span:last-child { font-size: var(--yqw-t4-size); color: var(--yqw-text-faint); }

/* ═══════════════════════ 390px ═══════════════════════ */
@media (max-width: 560px) {
  /* 手机上不能继承 desktop 的 340 下限，否则空态变成超长空屏（§33）。 */
  .yqw { --yqw-dropzone-min: clamp(220px, 30vh, 300px); }
  /* ⚠️ 390px 上标题与右上角两个动作放不进同一行：标题框被压成 38px，
     "Workspace" 这种长单词直接溢出压到按钮上（10B1 实测，Image 与 PDF 同病，
     两条线共用这一行所以一起修）。让这一行换行 —— 标题独占一行，动作退到下一行。 */
  .yqw-head-row { flex-wrap: wrap; }
  .yqw-head-id { flex: 1 1 100%; }
  .yqw-dz-icon svg { width: 28px; height: 28px; }
  .yqw-complete { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .yqw-complete--row .yqw-complete-line { flex-direction: column; align-items: stretch; }
  .yqw-complete--row .yqw-sel, .yqw-complete--row .yqw-btn--primary { width: 100%; }
  /* ⚠️ Phase 19A1.2：标准字段在这一档也满宽，与上面 Export 那条保持同一形态
     （否则窄屏上会出现"Export 满宽、画质/底色却还是 168"的不一致）。
     ⚠️ 触发它的是 **viewport**，不是文案长度 —— 同一个视口下
     zh / en / vi / ja 的列数与宽度必须完全一样（§12 的硬要求）。 */
  .yqw-field { width: 100%; }
  .yqw-complete-action { min-width: 0; padding-left: 0; }
  .yqw-complete-main, .yqw-complete-main .yqw-sel, .yqw-complete-main .yqw-field { width: 100%; }
  .yqw-complete-action .yqw-btn--primary { width: 100%; }
  .yqw-adv-body { margin: 10px 10px 12px; padding: 12px; gap: 12px; }
  .yqw-adv-cat, .yqw-adv-toggle { padding-left: 12px; padding-right: 12px; }
  .yqw-kebab { width: 34px; height: 34px; }
}

/* ══ 图片查看器（V0.0.1247 从 image-workbench.css 搬来）═══════════════════
   Image 与 PDF 用的是**同一个** viewer（YuqueWorkspaceUI.openViewer），
   所以样式住在共享组件表里 —— 两条线各留一份才是真的会漂移。
   class 名一个字没改，Image 那边的外观逐值不变。
   ⚠️ 这段注释的结束符曾经漏掉过（V0.0.1247 第一版），整段 viewer 样式因此被
   吞进注释里：两条线的查看器都没了遮罩、没了居中，图片直接落进文档流。
   而当时的断言只查了"class 在不在 / 节点有没有"，全绿。
   教训是这个仓库早就记过的：**视觉契约要量 computed style**，不看 class presence。
   ⚠️ 同理，CSS 注释体里**不许出现注释结束符的字面量** —— 它会就地把注释截断，
   紧跟着的第一条规则会被 CSS 的错误恢复整块吃掉（第二次踩的就是这个）。 */
.viewer-overlay {
  position: fixed; inset: 0; z-index: 9999;
  /* 放大之后图会超出画面：溢出的部分由平移来看，不许溅到遮罩外面。 */
  overflow: hidden;
  background: rgba(0, 0, 0, 0.85);
  display: flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity .2s ease;
}
.viewer-overlay.open { opacity: 1; }
.viewer-overlay img {
  object-fit: contain; border-radius: 4px;
  box-shadow: 0 4px 40px rgba(0, 0, 0, 0.4);
  transition: transform .2s ease;
  max-width: 90vw; max-height: 90vh;
}
.viewer-toolbar {
  position: absolute; top: 16px; right: 20px;
  display: flex; flex-direction: column; gap: 8px;
}
.viewer-btn {
  width: 36px; height: 36px; border: none;
  background: rgba(255, 255, 255, 0.15); color: #fff;
  border-radius: 50%; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background .15s;
}
.viewer-btn:hover { background: rgba(255, 255, 255, 0.3); }
.viewer-btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.viewer-btn svg { width: 18px; height: 18px; }
.viewer-counter {
  position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%);
  color: rgba(255, 255, 255, 0.7); font-size: 14px;
}

@media (prefers-reduced-motion: reduce) {
  .viewer-overlay, .viewer-overlay img { transition-duration: 1ms; }
}


