/* ============================================================================
 * rtl.css — Sunum 秀能 · 全局 LTR/RTL 布局样式
 * ★ RTL-LAYOUT【样式】 —— 与 static/js/rtl.js 配套，二者是全站唯一的 RTL 真源。
 * ----------------------------------------------------------------------------
 * 放置位置：static/css/rtl.css
 * 引入方式：
 *   · 内容页族 —— 由 static/css/sunum.css 顶部 @import 自动带入（一次改动覆盖
 *                 index / tools / chat / result / about / buy / admin / 404 / 500 …）；
 *   · 无限画布 —— templates/canvas.html 不加载 sunum.css，已单独 <link> 引入。
 *
 * 设计原则（为什么不是「到处加 [dir=rtl] 补丁」）
 * ----------------------------------------------------------------------------
 *   ① 能用 CSS 逻辑属性（inline-start / inline-end / margin-inline-* / text-align:start）
 *      解决的一律用逻辑属性 —— 它们在 LTR/RTL 下自动互换，不需要镜像分支；
 *   ② 逻辑属性表达不了「物理方向」的（图标朝向、滑块位移、动画方向）才写
 *      html[dir="rtl"] 分支 —— 每一个分支都必须有明确理由，杜绝「为镜像而镜像」；
 *   ③ 全部规则都限定在 html[dir="rtl"] 之下，**中文 LTR 布局一行都不会变**。
 *
 * 分区索引
 * ----------------------------------------------------------------------------
 *   §A 根级方向与文本对齐
 *   §B 数字 / 技术 token 隔离（保证 16:9、2K+、3.0 不被镜像）
 *   §C 顶部创作输入栏（hero）RTL 专项
 *   §D 下拉 / 弹层：朝向与边界
 *   §E 复用钩子（无限画布节点面板等其它页面）
 *   §F 移动端与窗口缩放
 *   §G 长维语防挤压防溢出
 * ========================================================================== */

/* ============================================================================
 * §A 根级方向与文本对齐
 * ----------------------------------------------------------------------------
 * dir 属性已由 i18n.js / rtl.js 写在 <html> 上，direction 会自动继承。
 * 这里再显式声明一次的作用是「兜底」：某些页面/组件历史上给容器写过
 * direction:ltr（例如画布的空间容器），显式声明可保证语义不被静默覆盖。
 * ========================================================================== */
html[dir="rtl"] { direction: rtl; }
html[dir="ltr"] { direction: ltr; }

/* ---------------------------------------------------------------------------
 * §A-1 【RTL 下根除横向溢出：只裁不滚】
 * ---------------------------------------------------------------------------
 * 实测根因（puppeteer 真实几何测量）：
 *   首页 .hero 的装饰光晕 .hero::before 用了 left/right:-18% 左右各外扩，
 *   在 1440 视口下 1072×18% = 192.96px，其右缘落在 1448.96px
 *   → 文档 scrollWidth 变成 1449，比视口宽 9px。
 *
 * 为什么 LTR 看不出来、RTL 会「炸」：
 *   · LTR 的滚动原点在最左（scrollLeft=0），那 9px 溢出物停在视口右缘之外，
 *     既不显示也不产生可见位移 —— 属于站点历史遗留的「隐性溢出」；
 *   · RTL 的滚动原点在**最右**，浏览器会把初始滚动位置贴到内容右端
 *     （实测 scrollLeft=-45/-8），于是**整页内容被推出视口**：
 *     .topbar / .wrap / .hero 这些 width:100% 的容器在 getBoundingClientRect
 *     里凭空「越界」，看起来像 RTL 把布局搞坏了，其实布局宽度分毫未变
 *     （LTR/RTL 的 scrollWidth 实测完全相同：1440→1449、375→422、320→356）。
 *
 * 解法：
 *   仅对 RTL 根元素用 overflow-x: clip。
 *     · clip 与 hidden 的区别 —— clip **不建立滚动容器**：
 *       position:fixed/sticky 后代仍然相对视口定位，不会被意外改变参照系；
 *       而 hidden 会建立滚动容器，可能连带影响 sticky 顶栏。
 *     · 裁剪点就是视口边缘，光晕本来就被浏览器画在屏幕外，视觉零变化。
 *     · 效果：RTL 下**禁止横向滚动**，滚动原点失效（实测 scrollLeft 归 0），
 *       那个「9px 的横向可拖动量」消失，用户不会把整页拖偏。
 *     ⚠ 诚实说明：clip 只取消「可滚动性」，**不改变 scrollWidth** ——
 *       scrollWidth 仍然如实报告 1449，因为它报告的是溢出内容尺寸而非可否滚动。
 *       因此测试断言不能写 scrollWidth==clientWidth，只能写
 *       「RTL 的 scrollWidth 不得超过 LTR 基线」（实测两者完全相等）。
 *   ⚠ 只加在 RTL 上：中文 LTR 布局一行不改（该 9px 在 LTR 下不可见，
 *     修不修是产品取舍，不在本次「不破坏原有功能」的授权范围内）。
 * ------------------------------------------------------------------------- */
html[dir="rtl"] { overflow-x: clip; }

/* 可伸缩容器一律 min-width:0 —— 这是 RTL 下比 LTR 更容易跑版的根因：
   flex 子项默认 min-width:auto 不肯收缩，维语长词会把整行顶破。 */
html[dir="rtl"] .hero-card,
html[dir="rtl"] .hero-card .hc-row,
html[dir="rtl"] .hero-card .hc-bar,
html[dir="rtl"] .hero-card .hc-left,
html[dir="rtl"] .hero-card .hc-right,
html[dir="rtl"] .hero-card .hc-body { min-width: 0; }

/* ============================================================================
 * §B 数字 / 技术 token 隔离
 * ----------------------------------------------------------------------------
 * 问题：RTL 段落里 "2K+" 末尾的 + 是中性字符，会被 bidi 甩到左边变成 "+2K"；
 *       "1080P"、"3.0" 等也可能与相邻文字粘连错位。
 * 解法：rtl.js 把这些片段包进 <bdi dir="ltr">（HTML 原生双向隔离元素），
 *       这里只给一个可选的视觉钩子 + 给「纯数值且无法用 bdi 包裹」的元素
 *       （如 JS 里用 textContent 写的比例字形、积分数字）兜一层 CSS 隔离。
 * ⚠ 注意：不要对「混合文本」容器整体设置 direction:ltr —— 那会把维语
 *   模型名（阿拉伯字母）强行左对齐。只对**纯技术内容**的元素生效。
 * ========================================================================== */
.ltr-token {
  direction: ltr;
  unicode-bidi: isolate;
}
/* 纯数值/纯技术内容：比例字形（16:9）、积分数字（12）、时长（5） */
html[dir="rtl"] .hc-pref .prat i,
html[dir="rtl"] .hc-cost b {
  direction: ltr;
  unicode-bidi: isolate;
}
/* 声明式入口：任何容器加 data-rtl-num 即由 rtl.js 自动做数字隔离 */
[data-rtl-num] { unicode-bidi: isolate; }

/* ============================================================================
 * §C 顶部创作输入栏（hero）RTL 专项
 * ----------------------------------------------------------------------------
 * C1 整体顺序镜像
 *     · .hc-att（加号上传）是 .hc-row 的**首个子元素** → RTL 下行首即右侧，
 *       于是加号自动落到输入框最右；
 *     · .hc-right（含语音 / 发送）用 margin-inline-start:auto 顶到行尾 →
 *       RTL 下行尾即左侧，于是发送按钮自动落到最左。
 *     二者都是逻辑属性的自然结果，**不需要 row-reverse**（加了反而会转回去）。
 *     但为了在旧浏览器/异常继承下依然成立，这里显式重申 direction:rtl。
 * C2 输入区文本
 *     占位层与 textarea 均右对齐；光标从右侧开始（dir=rtl 的原生行为）。
 *     .hc-ph 在 LTR 下是 shrink-to-fit，RTL 下需要补 inset-inline-end 才能
 *     铺满整行，否则「右对齐」没有参照宽度。
 * C3 方向性图标
 *     插入符（caret）、发送箭头在 RTL 下水平翻转才符合阅读方向；
 *     加号、麦克风、星形为非方向性图标，保持原样不翻转。
 * ========================================================================== */
html[dir="rtl"] .hero-card,
html[dir="rtl"] .hero-card .hc-row,
html[dir="rtl"] .hero-card .hc-bar,
html[dir="rtl"] .hero-card .hc-left,
html[dir="rtl"] .hero-card .hc-right,
html[dir="rtl"] .hero-card .hc-menu { direction: rtl; }

html[dir="rtl"] .hc-body textarea { text-align: right; }
html[dir="rtl"] .hc-ph { inset-inline-end: 0; text-align: right; }
html[dir="rtl"] .hc-auto { text-align: right; }

html[dir="rtl"] .hc-btn .caret,
html[dir="rtl"] .hc-send svg { transform: scaleX(-1); }

/* 附件预览的「× 移除」按钮用 inset-inline-end 定位 → 已自动镜像；
   这里只保证缩略图本身不被翻转（人物/文字图像不应镜像） */
html[dir="rtl"] .hc-att img,
html[dir="rtl"] .hc-att .fvid { transform: none; }

/* ============================================================================
 * §D 下拉 / 弹层：朝向与边界
 * ----------------------------------------------------------------------------
 * .hc-menu 原本就用 inset-inline-start 定位 → RTL 下自动从右侧锚点向左伸展
 * （满足「RTL 弹窗向左侧弹出」）。这里补三件事：
 *   ① 宽度上限跟随视口，窄屏下永远不会比窗口还宽；
 *   ② 面板型弹层（.hc-pref，原 min-width:300px）在窄屏改为 min(300px, 视口-26px)；
 *   ③ 滚动区域内滚动链不外溢。
 * 横向/纵向的**动态**避让由 rtl.js 的 placeMenu() 计算并写 transform，
 * CSS 不参与（CSS 无法知道锚点与视口的实时关系）。
 * ========================================================================== */
.hc-menu { max-width: calc(100vw - 24px); }
.hc-menu .msearch input,
.hc-menu .mi .mt,
.hc-menu .mi .md { overflow-wrap: anywhere; }
.hc-pref { min-width: min(300px, calc(100vw - 26px)); }
.hc-menu .pmodel { max-width: 100%; }

/* 通用弹层（资产库 / 侧栏 / 命令面板等）：只对齐文本，**不强制 direction** ——
   画布等页面已对这些面板做过「LTR 空间」裁决，强行翻 direction 会把网格与
   工具条顺序掉头（属于"破坏原有功能"）。对齐用 start，随方向自动成立。 */
html[dir="rtl"] .lib-panel,
html[dir="rtl"] .sb-panel { text-align: start; }
html[dir="rtl"] .lib-search,
html[dir="rtl"] .sb-in { text-align: right; }

/* ============================================================================
 * §E 复用钩子（无限画布节点面板等其它页面）
 * ----------------------------------------------------------------------------
 * 给容器加类名即可获得 RTL 能力，无需改 JS：
 *   .rtl-scope     —— 容器内随全局方向镜像（适合左侧/右侧参数面板、属性栏）
 *   .rtl-ignore    —— 容器内**强制保持 LTR**（画布坐标轴、数值输入、代码/Tab 序）
 *   .rtl-mirror-x  —— 元素在 RTL 下水平翻转（方向性图标）
 *   .rtl-num       —— 纯数值文本，保持正常阅读方向
 *
 * 无限画布的实际取向（已核对代码，不是猜测）：
 *   画布在 templates/canvas.html 的 RV-FUSION-1 样式块里已经做了「只镜像文本、
 *   不镜像空间」的裁决：把 #canvasWrap / #world / #nodesLayer / #edgesLayer /
 *   #miniMap / .topbar / .rail / .zv-dock / .n-card 一律 direction:ltr
 *   —— 这与本文件的 .rtl-ignore 是同一套语义，二者可混用。
 *   之所以必须如此：画布节点是 position:absolute + left/top 物理像素定位、
 *   连线在 SVG 里按物理坐标绘制、端口用 left:-9px / right:-9px 定位，
 *   空间一旦被镜像，整个工程的视觉语义就翻了。
 *   因此：新加的面板若属于「空间/坐标/工具条」→ 沿用 .rtl-ignore；
 *   若属于「文本/表单/属性」→ 加 .rtl-scope 让它跟随镜像。
 * ========================================================================== */
html[dir="rtl"] .rtl-scope { direction: rtl; text-align: start; }
html[dir="ltr"] .rtl-scope { direction: ltr; }

/* .rtl-ignore 必须能压过 .rtl-scope 与继承的 rtl */
html[dir="rtl"] .rtl-ignore,
html[dir="rtl"] .rtl-ignore .hc-menu { direction: ltr; }
html[dir="rtl"] .rtl-ignore input,
html[dir="rtl"] .rtl-ignore textarea { text-align: left; }

html[dir="rtl"] .rtl-mirror-x { transform: scaleX(-1); }
html[dir="rtl"] .rtl-num { direction: ltr; unicode-bidi: isolate; }

/* ============================================================================
 * §F 移动端与窗口缩放
 * ----------------------------------------------------------------------------
 * 缩放（含浏览器缩放 / 系统缩放）本质是视口变化：布局必须在任意宽度可用。
 * 三条纪律：工具条折行 > 控件被压扁；面板不设固定大宽度；正文允许断词。
 * ========================================================================== */
@media (max-width: 720px) {
  html[dir="rtl"] .hc-bar,
  html[dir="rtl"] .hc-row { flex-wrap: wrap; row-gap: 6px; }
  /* ⚠ 不要在这里取消 .hc-right 的 margin-inline-start:auto ——
     它是「发送按钮落到最左」的机制本身。折行后该 auto 外边距会把右组推到
     新一行的行尾（RTL 即左侧），镜像语义依然成立；一旦取消，窄屏上发送键
     就会跟着左组跑，破坏「发送最左」的一致行为。 */
  .hc-menu { max-width: calc(100vw - 16px); }
  .hc-pref { min-width: min(300px, calc(100vw - 18px)); }
  html[dir="rtl"] .lib-panel,
  html[dir="rtl"] .sb-panel { max-width: calc(100vw - 16px); }
}

@media (max-width: 420px) {
  html[dir="rtl"] .hc-cost { max-width: 58vw; }
}

/* ============================================================================
 * §G 长维语防挤压防溢出
 * ----------------------------------------------------------------------------
 * 维吾尔语同一句话长度约为中文的 1.6~2.2 倍，且为阿拉伯字母连写 + 变音符号。
 * 两条底线：① 该换行就换行，不用 ellipsis 掩盖；② 行高放宽，避免上下吃字。
 * （字体栈与全局行高已由 sunum.css 的 html[lang="ug"] 段提供，此处只补
 *   新控件与 RTL 专属场景，避免重复声明。）
 * ========================================================================== */
html[lang="ug"] .hc-menu,
html[lang="ug"] .hc-menu .mi,
html[lang="ug"] .hc-menu .mi .md,
html[lang="ug"] .hc-pref .plab,
html[lang="ug"] .hc-pref .popt,
html[lang="ug"] .hc-pref .pmodel,
html[lang="ug"] .hc-pref .prat,
html[lang="ug"] .hc-cost {
  overflow-wrap: anywhere;
  word-break: break-word;
}
/* 菜单项里的说明文本（双语副标题）在维语下允许两行，不截断 */
html[lang="ug"] .hc-menu .mi .md { white-space: normal; line-height: 1.7; }
/* 维语下工具条按钮允许换行显示，避免长译文被压成竖排 */
html[lang="ug"] .hc-btn { white-space: normal; }
