/* ============================================================================
 * mobile-adapt.css —— 全站手机端适配层（唯一真源）
 * ============================================================================
 *
 * 为什么单独一层，而不是改各页自己的样式：
 *   本仓库有四套并行的样式来源（clear-workflow/、clear-v2/、static/css/、
 *   以及 account.html 的内联 <style>），其中 index.html / clear.html 用的还是
 *   指纹化构建产物（static/assets/*.<hash>.css）。把移动端规则散着写，会出现
 *   「清晰版修了、经典版没修」「账户页修了、admin 没修」这类长期不一致。
 *   本文件放在 static/ 下由 Flask 直接静态服务，**不需要跑任何构建脚本**，
 *   并且在每个页面的 <head> 里排在最后 —— 同优先级下靠后者胜出。
 *
 * 加载方式（四个页面都要加，且必须在其它 stylesheet 之后）：
 *   <link rel="stylesheet" href="/static/mobile-adapt.css?v=20261021-mobile" />
 *
 * ── 断点策略（新代码只用这三个，旧断点视为遗留）─────────────────────────
 *   ≤ 360px   极窄机型（Galaxy Fold 外屏 280、iPhone SE1 320、安卓小屏 360）
 *   ≤ 767.98px 手机（竖屏为主）
 *   ≤ 991.98px 平板 / 手机横屏
 *   旧代码里散落着 720 / 600 / 640 / 575.98 / 576 / 768 / 767 等十余个值，
 *   相邻断点（如 767.98 与 768）会让同一元素被两条规则各命中一次。本层不去
 *   逐个重写它们，而是用「流体尺寸 + min()」让布局对断点不敏感，再只针对
 *   真正需要换形态的地方（导航、弹窗）用上面三个断点收口。
 *
 * ── 验收标准（与 scripts/mobile_adapt_audit.cjs 的检查项一一对应）────────
 *   A1 无横向溢出：scrollWidth ≤ clientWidth + 1（所有设备/页面/横竖屏）
 *   A2 触控目标 ≥ 44×44 CSS px（WCAG 2.5.5 / iOS HIG 44pt / Material 48dp）；
 *      表格内行内操作按 WCAG 豁免降到 ≥ 36px
 *   A3 正文字号 ≥ 12px；表单控件在触屏设备上 ≥ 16px（iOS 对 <16px 输入框
 *      会在聚焦时自动放大整页，用户必须手动缩小才能继续填）
 *   A4 安全区域：viewport-fit=cover + 固定栏用 env(safe-area-inset-*)
 *   A5 按压反馈：触屏设备上每个可点元素都有非默认的 :active 视觉变化
 *   A6 触控响应：touch-action:manipulation + 关闭默认点击高亮
 * ========================================================================= */

/* ============================================================================
 * §1 设计令牌
 * ========================================================================= */
:root {
    /* 触控目标 */
    --ma-tap: 44px;
    --ma-tap-dense: 36px;
    /* 字号下限 */
    --ma-fs-floor: 12px;
    --ma-fs-input: 16px;
    /* 安全区域（没有刘海/圆角的设备解析为 0） */
    --ma-safe-top: env(safe-area-inset-top, 0px);
    --ma-safe-right: env(safe-area-inset-right, 0px);
    --ma-safe-bottom: env(safe-area-inset-bottom, 0px);
    --ma-safe-left: env(safe-area-inset-left, 0px);
    /* 手机端页面边距 */
    --ma-gutter: 16px;
}

/* ============================================================================
 * §2 全局触控基础
 * ========================================================================= */

/* iOS 横屏时会把字号自动放大（"text inflation"），两版页面混用 px/rem
   的情况下会让布局跳动。锁成 100% 表示"不要替我放大"。 */
html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}

/* 关闭 WebKit/Blink 默认的点击高亮。
   ⚠️ 这一条必须和 §3 的 :active 反馈成对出现 —— 只关高亮不给反馈，
   用户会觉得"点了没反应"，比留着默认高亮更糟。 */
* {
    -webkit-tap-highlight-color: transparent;
}

/* 消除移动端 300ms 点击延迟与"双击缩放"歧义。
   manipulation = 允许滚动/捏合缩放，但不允许双击缩放。 */
a,
button,
summary,
label,
select,
[role="button"],
[role="tab"],
[role="link"],
input[type="checkbox"],
input[type="radio"],
input[type="submit"],
input[type="button"],
input[type="reset"],
.yan-button,
.btn {
    touch-action: manipulation;
}

/* 按钮上的长按不该弹出"复制/查找"菜单，也不该选中文字。
   输入框、正文、代码块保持可选中。 */
button,
summary,
[role="button"],
[role="tab"],
.yan-button,
.btn,
.yan-page-nav-btn,
.yan-ov-action {
    -webkit-user-select: none;
    user-select: none;
}

/* 弹层滚动：阻止"滚到底后把整页一起带着滚"（scroll chaining），
   这是移动端弹窗最常见的手感问题。 */
.yan-workspace-dialog,
.yan-confirm-dialog,
.modal,
.modal-dialog,
.v2-dialog,
.yan-mp-table-wrap,
.table-responsive,
.yan-page-sidebar {
    overscroll-behavior: contain;
}

/* 惯性滚动（老 iOS 需要显式开启；新版本默认就有，写了无害） */
.yan-workspace-dialog,
.yan-mp-table-wrap,
.table-responsive,
.yan-page-sidebar,
.yan-page-main {
    -webkit-overflow-scrolling: touch;
}

/* ============================================================================
 * §3 按压反馈（A5）
 * ----------------------------------------------------------------------------
 * 移动端 :hover 不触发，所以"只有 hover 样式"的按钮点下去毫无反馈。
 * 这里对全部可点元素补一层统一的 :active。用 filter 而不是 background，
 * 是因为按钮底色有白底/渐变/描边多种，brightness 对它们都成立。
 * 只在触屏设备生效，不影响桌面端的 hover 手感。
 * ========================================================================= */
@media (hover: none), (pointer: coarse) {
    a:active,
    button:active,
    summary:active,
    [role="button"]:active,
    [role="tab"]:active,
    [role="link"]:active,
    .btn:active,
    .yan-button:active,
    .yan-page-nav-btn:active,
    .yan-ov-action:active,
    .yan-ov-stat.is-clickable:active,
    .v2-button:active,
    .v2-sidebar a:active,
    .mobile-workspace-nav button:active {
        filter: brightness(.9);
        /* 触屏上反馈要"立刻"，不能有过渡延迟 */
        transition: filter .04s linear;
    }

    /* 禁用态不该有按压反馈 */
    a[aria-disabled="true"]:active,
    button:disabled:active,
    .yan-button:disabled:active,
    .btn:disabled:active {
        filter: none;
    }

    /* 已经是深色/主色底的行，brightness 变化不明显，改用轻微缩放 */
    .yan-primary:active,
    .yan-button.yan-primary:active,
    .btn-primary:active,
    .v2-button.primary:active {
        filter: none;
        transform: scale(.97);
    }
}

/* 键盘/开关类控件：按压时给底色，比亮度变化更符合预期 */
@media (pointer: coarse) {
    input[type="checkbox"]:active,
    input[type="radio"]:active {
        filter: brightness(.88);
    }
}

/* ============================================================================
 * §4 触控目标尺寸（A2）
 * ========================================================================= */
/* 为什么这里必须用 !important
   ----------------------------------------------------------------------------
   原样式里有一批**优先级更高**的 min-height 在跟本层抢：

     classic-overrides.css:616   .question-support-summary{min-height:40px}  (0,1,0)
     classic-overrides.css:57    min-height:42px                            (0,1,0)
     research-styles.css:1315    min-height:34px                            (0,1,0)
     research-styles.css:1618    min-height:34px                            (0,1,0)
     account-workspace.css:7     .yan-button{min-height:40px}               (0,1,0)

   本层如果只写裸 `button`(0,0,1) / `summary`(0,0,1)，实测计算值仍是
   42px / 40px / 34px —— 也就是"写了但根本没生效"。要压过 #id 级选择器
   只能靠 !important。

   风险可控：整段都在 (pointer: coarse) 内，桌面鼠标环境完全不受影响；
   并且下面两条豁免规则同样带 !important 且优先级更高，表格内的密集按钮
   仍按 36px 收口，不会把表格撑爆。 */
@media (pointer: coarse) {
    button,
    summary,
    select,
    [role="button"],
    [role="tab"],
    input[type="submit"],
    input[type="button"],
    input[type="reset"],
    .btn,
    .yan-button,
    /* 品牌链接是 flex 容器，原样式没有高度下限，实测仅 37px */
    .navbar-brand,
    /* 经典版顶栏的动作入口 */
    .nav-actions > a,
    .nav-actions > button,
    .nav-actions > .btn {
        min-height: var(--ma-tap) !important;
    }

    /* 关闭按钮（✕）必须给足 44×44 —— 它是最容易点偏的元素 */
    .btn-close,
    .yan-close,
    [aria-label*="关闭"],
    [title="关闭"] {
        min-width: var(--ma-tap) !important;
        min-height: var(--ma-tap) !important;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    /* 图标型链接（返回、汉堡菜单）同样按 44 收口 */
    .yan-page-back,
    .navbar-toggler,
    .v2-menu summary {
        min-height: var(--ma-tap) !important;
    }

    /* ── 豁免：表格内的行内操作 ──────────────────────────────────────────
       WCAG 2.5.5 对"行内/密集排布且功能在别处也可达"的目标有豁免。会员
       档位对照表里每格一个「开通」按钮，强行 44px 会把整张表撑爆。这里
       降到 36px 下限：仍明显大于原生 24px，且不破坏表格排版。 */
    table button,
    table [role="button"],
    table .btn,
    table .yan-button,
    .yan-mp-table .yan-button {
        min-height: var(--ma-tap-dense) !important;
    }

    /* 数字步进器（任务配置里 ± 按钮）：原样式 24×22px，是全局最小的可点
       元素。这里只保证高度与内边距，宽度跟着内容走。 */
    .number-stepper-btn {
        min-height: var(--ma-tap-dense) !important;
        min-width: var(--ma-tap-dense) !important;
        padding: 0 .55rem;
    }
}

/* 列表行、菜单项这类"整行可点"的，高度也要够 */
@media (pointer: coarse) {
    .yan-page-nav-btn,
    .yan-ov-action,
    .list-group-item-action,
    .dropdown-item {
        min-height: var(--ma-tap) !important;
    }
}

/* Bootstrap 开关（form-switch）与页脚备案链接：实测点击面只有 22px / 17px 高。
   开关的可见滑块本身只有 32×16，但真正被点的是包着它的 <label>，所以把
   label 撑到 44px 就能满足触控要求 —— 不去改滑块的 width/height，因为
   Bootstrap 的 .form-switch 用固定 background-position 定位滑块，改了尺寸
   会错位，得不偿失。 */
@media (pointer: coarse) {
    .form-check-label {
        min-height: var(--ma-tap) !important;
        display: inline-flex;
        align-items: center;
    }

    .site-footer a {
        min-height: var(--ma-tap) !important;
        display: inline-flex;
        align-items: center;
    }
}

/* ============================================================================
 * §5 字号下限（A3）
 * ----------------------------------------------------------------------------
 * 两个独立的坑：
 *   ① 说明文字小到 10–11px。手机上（尤其 360px 宽）几乎不可读。
 *   ② 输入框 <16px。iOS Safari 会在聚焦时把整页放大，用户填完还得手动缩回来。
 * ========================================================================= */

/* 表单控件在触屏上一律 16px。
   用 !important 是因为经典版工作台有一条 (0,3,0) 权重的
   `.clear-ready .clear-config-workbench .form-control{font-size:13px}`，
   靠同权重覆盖需要把选择器写得一样长且更晚，可读性更差。 */
@media (pointer: coarse) {
    input:not([type="checkbox"]):not([type="radio"]):not([type="range"]),
    select,
    textarea {
        font-size: var(--ma-fs-input) !important;
    }
}

/* 12px 字号下限：把已知的小字号规则抬到 12px。
   只列真正会出现在手机界面上的那些，不做全局 font-size 强制（会破坏
   图标字体、徽章排版等）。 */
/* ── 断点约定（改本文件前必读）────────────────────────────────────────────
   下面这些"手机人体工学"规则（字号下限、点击目标、内边距、单列化）如果只写在
   `max-width: 767.98px` 里，横屏会整片失效 —— 手机横屏的宽度是 844px
   （iPhone 13）到 932px（15 Pro Max），比 768 还宽，于是桌面样式照旧生效：
   字号 9–11px、按钮高 34–40px，正是竖屏已经修好的同一批问题。实测横屏
   单页仍有 130+ 处字号违规，全部来自这个盲区。

   因此本文件统一使用：

       @media (max-width: 767.98px),
              (orientation: landscape) and (max-height: 500px) and (max-width: 1024px)

   第二个分支覆盖"矮而宽"的手机横屏（高 390–500px、宽 ≤1024px）；
   再用 max-width:1024px 把"窗口被拉得很矮的桌面浏览器"排除在外，
   避免在桌面上误触发手机版式。 */
@media (max-width: 767.98px),
       (orientation: landscape) and (max-height: 500px) and (max-width: 1024px),
       /* 触屏设备不看宽度。iPad Mini 竖屏正好 768px，落在上面两个分支之外
          （767.98 取不到、竖屏也不是 landscape），实测仍有 29 处 9.8–11.5px
          的小字。平板同样是"手指操作"，所以字号下限按 pointer:coarse 收口。
          注意触控目标（§4）本来就是这么写的 —— 这正是 iPad 的 A2 能过、
          A3 过不了的原因，两边口径要一致。 */
       (pointer: coarse) {
    /* 为什么这一整块都用 !important
       ----------------------------------------------------------------------
       原样式里同一批说明文字被长链选择器压住。实测到的"钉子户"：

         body.desktop-mode .navbar-brand small       (0,2,1)  → 10.08px
         .desktop-version-badge                      (0,2,0)  →  9.14px
         body.desktop-mode .nav-actions > a          (0,2,1)  → 11.76px
         table.table-sm tbody td.text-muted.small    (0,3,2)  → 11.29px
         #taskSettingsSection .task-setting-help     (1,2,0)  → 10.72px

       本层用裸类名（(0,1,0)）赢不了，表现就是"规则明明写了、计算值却没变"。
       这批选择器全是**承载说明文字/标签的叶子元素**，不存在布局风险，所以
       统一用 !important 收口。图标字体（.bi）与 SVG 不在列表内，不会跟着放大。

       注意：这**不是**断点没生效。实测横屏下
         matchMedia('(orientation: landscape) and (max-height: 500px) and (max-width: 1024px)')
       为 true、--ma-fs-floor 解析为 12px —— 纯粹是优先级问题，改断点解决不了。 */

    /* 经典版顶栏 / 桌面外壳（body.desktop-mode 在手机上也会被打开） */
    .navbar-brand small,
    .navbar-brand .small,
    .desktop-version-badge,
    .nav-actions > a,
    .nav-actions > button,
    .nav-actions > .btn,
    .sidebar-item,
    .sidebar-item span,

    /* 主样式里的 0.62rem / 0.68rem 说明文字（约 9.9px / 10.9px） */
    .small,
    small,
    .form-text,
    .text-muted.small,

    /* Bootstrap 徽章：`.badge` 是 .75em，嵌在 `.small` 里会二次缩小，
       实测掉到 9px（「问卷星」「腾讯问卷」这类平台名）。 */
    .badge,
    .v2-tag,

    /* 表单标签与卡片标题 */
    .form-label,
    .form-label.small,
    .card-header .fw-bold,
    .task-settings-cards .card-header span,

    /* 表格内的字段名与说明（随机 / 调度设置表） */
    .table-sm thead th,
    .table-sm tbody td.fw-semibold,
    .table-sm tbody td.text-muted.small,

    /* 任务配置工作台其余小字（实测 10.4–11.6px）。
       逐个列类名而不是给整个容器强设 font-size —— 容器级强制会连图标字体
       一起放大、排版会散。新增控件若又出现小字，审计会报出来，再补一行。 */
    .ratio-segment,
    .ratio-input-group label,
    .task-setting-help,
    .task-settings-pill,
    .schedule-mode-tab,
    .input-group-sm .input-group-text,
    .filltime-price-summary span,
    .filltime-price-current span,
    #fillTimePriceToggleLabel,
    .ip-selector-hint,
    .ip-selector-hint span,
    #proxySelectorHintText,
    #selectedCount,
    .question-support-summary,
    .question-support-summary__hint,
    #studyInlineAddSurveyBtn,
    #configManagementCard .btn-sm,

    /* 个人中心 */
    .yan-nav-group-label,
    .yan-page-sidebar .yan-page-nav-btn .yan-nav-badge,
    .yan-ov-stat small,
    .yan-ov-action small,
    .yan-ov-row-main em,
    .yan-ov-row-tag,
    .yan-page-content .yan-stat span,
    .yan-page-content .yan-table th,
    .yan-page-content .yan-label,
    .yan-page-content label {
        font-size: var(--ma-fs-floor) !important;
    }

    /* 整片"小字区"按容器收口
       ----------------------------------------------------------------------
       上面是逐个列类名，但每修一轮又会浮出新的小字类（页脚备案号、
       桌面状态栏、IP 地区选择、题目-IP 映射面板…），逐个追是打地鼠。

       下面这几个容器是**纯小字区**：内部不存在需要保持大字号的标题层级，
       所以整片抬到 12px 是安全的，不会破坏排版。这与"给任意容器强设
       font-size"不同 —— 那种做法在含标题的容器上才会出问题。 */
    .site-footer,
    .site-footer *,
    #desktopStatusbar,
    #desktopStatusbar *,
    #domesticProxySelector,
    #domesticProxySelector *,
    #regionRatioHelpText,
    .province-tabs,
    .province-tabs *,
    .province-tab,
    #questionIpMappingPanel,
    #questionIpMappingPanel * {
        font-size: var(--ma-fs-floor) !important;
    }
}

/* ============================================================================
 * §6 安全区域（A4）
 * ----------------------------------------------------------------------------
 * 只在真的带刘海/圆角的设备上有值（env() 兜底 0px），所以不需要媒体查询。
 *
 * 两种写法，用途不同，别混：
 *   · 纵向（贴顶/贴底栏）→ calc(inset + 基础值)：栏体本身要"长高"，
 *     否则内容会被刘海/手势条压住。
 *   · 横向 → max(基础值, inset)：只保证不低于安全区，**不能相加**。
 *     相加会让容器总宽超过 100%（本仓库没有全局 box-sizing:border-box），
 *     直接制造横向溢出。
 * ========================================================================= */

/* 顶部固定导航避让刘海 */
.yan-page-header,
.navbar.fixed-top,
.v2-header {
    padding-top: calc(env(safe-area-inset-top, 0px) + var(--ma-header-pad-y, 14px));
}

/* 顶部栏的左右：横屏时刘海会切到左侧 */
.yan-page-header {
    padding-left: max(16px, env(safe-area-inset-left, 0px));
    padding-right: max(16px, env(safe-area-inset-right, 0px));
}

/* 底部固定栏避让 Home Indicator / 手势条 */
.mobile-workspace-nav,
.yan-settings-menu-panel,
.v2-step-footer,
.modal-footer.sticky-bottom {
    padding-bottom: calc(env(safe-area-inset-bottom, 0px) + var(--ma-footer-pad-y, 10px));
}

/* 内容区底部留出底部栏的高度，避免最后一行被挡住 */
@media (max-width: 767.98px), (orientation: landscape) and (max-height: 500px) and (max-width: 1024px) {
    .yan-page-main {
        padding-bottom: calc(24px + env(safe-area-inset-bottom, 0px));
    }
}

/* ============================================================================
 * §7 视口高度与溢出（A1）
 * ========================================================================= */

/* ① 100vh 在移动端 = "地址栏收起时的最大高度"，键盘弹出/地址栏展开时会
   比可视区高，导致底部按钮被顶到屏幕外。dvh（dynamic viewport height）
   才是真实可视高度。下面用 dvh 优先、vh 兜底的写法。 */
@supports (height: 100dvh) {

    .yan-page {
        height: 100dvh;
    }

    .modal-dialog.modal-dialog-centered {
        min-height: 100dvh;
    }

    body.clear-v2 {
        min-height: 100dvh;
    }
}

/* ② 100vw 包含纵向滚动条宽度（Windows/Android 上约 15px），
   在"有纵向滚动"的页面上必然产生横向滚动条。弹窗全屏时改成 100%。 */
.modal-dialog.modal-dialog-centered {
    width: 100%;
    max-width: 100%;
}

@media (max-width: 720px), (orientation: landscape) and (max-height: 500px) and (max-width: 1024px) {

    .yan-workspace-dialog,
    .yan-confirm-dialog {
        width: 100%;
        max-width: 100%;
    }
}

/* ③ 溢出兜底：任何页面都不该出现横向滚动。
   这里用 overflow-x: **clip** 而不是 hidden —— hidden 会让 html/body 变成
   滚动容器，从而**破坏所有 position:sticky 后代**（本仓库的页面标题栏、
   弹窗标题栏、清晰版侧栏都是 sticky）。clip 只裁剪、不建立滚动容器，
   sticky 照常工作；不支持的旧浏览器会忽略它，而真正的溢出源已经在 §7⑤
   和 §10 修掉了，不依赖这道闸。 */
html,
body {
    max-width: 100%;
    overflow-x: clip;
}

/* ④ 超长字符串（token、订单号、邮箱）不许撑破容器。
   用 break-word 而不是 anywhere：anywhere 会改变元素的 min-content 尺寸，
   让 flex/grid 轨道的收缩行为变得难以预测；break-word 只在真正放不下时
   才断行，不影响固有尺寸计算。 */
.yan-page-content,
.yan-workspace-dialog,
.v2-app {
    overflow-wrap: break-word;
}

/* 真正会出现"一长串无分隔字符"的地方才用 anywhere */
.yan-ov-token code,
.yan-kv dd,
.yan-ov-row-main b,
.order-id,
code,
kbd,
samp {
    overflow-wrap: anywhere;
}

/* ⑤ 所有 auto-fit 栅格改用 min() 下限。
   repeat(auto-fit, minmax(300px, 1fr)) 在容器 <300px 时轨道**不会**收缩，
   直接横向溢出（280px 的折叠屏、320px 的 iPhone SE1 必现）。
   min(300px, 100%) 让下限跟着容器走。 */
.yan-ov-cols {
    grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
}

.yan-ov-actions {
    grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr));
}

.yan-ov-meters,
.yan-page-content .yan-stat-grid {
    grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr));
}

.yan-page-features {
    grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr));
}

.yan-settings-shortcuts,
.yan-settings-stats {
    grid-template-columns: repeat(auto-fit, minmax(min(160px, 100%), 1fr));
}

/* ============================================================================
 * §8 弹窗 / 抽屉的移动端形态
 * ========================================================================= */
@media (max-width: 767.98px), (orientation: landscape) and (max-height: 500px) and (max-width: 1024px) {
    /* 全屏 sheet：贴满可视区，圆角去掉，底部留出安全区 */
    .yan-workspace-dialog,
    .yan-confirm-dialog {
        border-radius: 0;
        max-height: 100dvh;
    }

    /* 桌面上的居中弹窗在手机上改为"底部抽屉"形态 —— 拇指更容易够到
       底部的主操作，也更符合移动端心智模型。 */
    .v2-dialog,
    .modal-dialog.modal-dialog-centered {
        margin: auto auto 0;
        border-bottom-left-radius: 0;
        border-bottom-right-radius: 0;
        max-height: 92dvh;
        overflow-y: auto;
        overscroll-behavior: contain;
    }

    /* 弹窗底部按钮：移动端主操作靠右、撑满一行更好点 */
    .yan-workspace-dialog .yan-actions,
    .yan-confirm-dialog .yan-actions,
    .modal-footer {
        gap: 8px;
        padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
    }

    .modal-footer > .btn,
    .yan-confirm-dialog .yan-actions > .yan-button {
        flex: 1 1 auto;
    }

    /* 顶部 sticky 标题栏加一条分隔阴影，滚动时内容不会和标题糊在一起 */
    .yan-workspace-dialog > header,
    .yan-confirm-dialog > header,
    .modal-header.sticky-top {
        box-shadow: 0 2px 10px rgba(15, 23, 42, .06);
    }
}

/* ============================================================================
 * §9 个人中心（/static/account.html）
 * ========================================================================= */
@media (max-width: 767.98px), (orientation: landscape) and (max-height: 500px) and (max-width: 1024px) {
    /* 顶部栏：小屏下标题与返回按钮并排容易挤，允许换行 */
    .yan-page-header {
        flex-wrap: wrap;
        gap: 8px;
        --ma-header-pad-y: 10px;
    }

    .yan-page-title h1 {
        font-size: 17px;
    }

    /* 侧栏退化成横向滚动的 tab 条：按钮不能换行，且要能滚到当前项 */
    .yan-page-sidebar {
        scroll-snap-type: x proximity;
        padding-left: calc(12px + env(safe-area-inset-left, 0px));
        padding-right: calc(12px + env(safe-area-inset-right, 0px));
    }

    .yan-page-sidebar .yan-page-nav-btn {
        scroll-snap-align: start;
        min-height: var(--ma-tap);
    }

    /* 横向滚动条在移动端是视觉噪音，隐藏但保留滚动能力 */
    .yan-page-sidebar {
        scrollbar-width: none;
    }

    /* 2026-10-20：分组标签在窄屏原本被降级成小字，跟着横向滚动条一起滑走 ——
       用户看到的就是一排看不出归属的按钮（"这五个是一类吗？"）。
       现在把分组标签做成**吸顶分组头**：滚动时始终钉在当前组的位置，
       组里的按钮从它下面滑过，一眼能看出哪几个是一类。 */
    .yan-page-sidebar .yan-nav-group {
        display: flex;
        align-items: center;
        flex: 0 0 auto;
    }
    .yan-page-sidebar .yan-nav-group-label {
        position: sticky;
        left: 0;
        z-index: 2;
        display: inline-flex;
        align-items: center;
        align-self: stretch;
        padding: 0 10px 0 2px;
        margin-right: 2px;
        font-size: var(--ma-fs-floor);
        font-weight: 700;
        color: var(--text-muted, #64748b);
        white-space: nowrap;
        background: var(--bg-body, #fff);
        border-right: 1px solid var(--border-color, #e2e8f0);
    }
    .yan-page-sidebar .yan-nav-group + .yan-nav-group {
        margin-left: 10px;
        padding-left: 10px;
        border-left: 1px solid var(--border-color, #e2e8f0);
    }

    .yan-page-sidebar::-webkit-scrollbar {
        display: none;
    }

    /* 内容区边距收紧，把宽度让给内容 */
    .yan-page-main {
        padding: 14px var(--ma-gutter);
    }

    .yan-page-content > * {
        max-width: 100%;
    }

    /* 概览头部：头像 + 名称 + 徽章在窄屏改为纵向堆叠 */
    .yan-ov-head {
        gap: 10px;
    }

    .yan-ov-avatar {
        width: 44px;
        height: 44px;
        border-radius: 13px;
        font-size: 17px;
    }

    .yan-ov-head-actions {
        width: 100%;
        margin-left: 0;
    }

    .yan-ov-head-actions .yan-button {
        flex: 1 1 auto;
    }

    /* 账户信息表：键值对在窄屏改为上下两行（已在 account.html 内联样式
       里做过，这里再兜一次，防止内联样式被后续改动删掉） */
    .yan-kv {
        grid-template-columns: minmax(0, 1fr);
        gap: 2px 0;
    }

    .yan-kv dd {
        margin-bottom: 8px;
    }

    /* 会员对照表：横向可滚，但给出"还能往右滑"的视觉暗示 */
    .yan-mp-table-wrap {
        position: relative;
        -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 24px), transparent 100%);
        mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 24px), transparent 100%);
    }

    /* 表格横向滚动时不要让整页跟着动 */
    .yan-table,
    .yan-mp-table {
        font-size: var(--ma-fs-floor);
    }
}

/* 极窄机型（≤360px）：进一步压缩边距 */
@media (max-width: 360px) {
    :root {
        --ma-gutter: 12px;
    }

    .yan-page-content .yan-card {
        padding: 14px 12px;
    }

    .yan-ov-head-actions .yan-button {
        font-size: var(--ma-fs-floor);
    }
}

/* ============================================================================
 * §10 主应用（经典版 index.html / 清晰版 clear.html）
 * ========================================================================= */
@media (max-width: 767.98px), (orientation: landscape) and (max-height: 500px) and (max-width: 1024px) {
    /* 清晰版主容器在手机上 28px 侧边距太浪费，收到 16px */
    .v2-app {
        padding: 20px var(--ma-gutter) 48px;
    }

    .v2-header {
        height: auto;
        min-height: 56px;
        padding-left: var(--ma-gutter);
        padding-right: var(--ma-gutter);
        gap: 10px;
    }

    /* 品牌名在窄屏缩小，给右侧入口留位置 */
    .v2-brand strong {
        font-size: 19px;
    }

    /* ⚠️ 清晰版原本在 ≤400px 直接隐藏「钱包」、≤500px 隐藏「帮助」，
       等于最窄的机型上**没有入口**去充值和看帮助。改为保留入口、只压缩
       字号与内边距 —— 功能入口的可达性优先于视觉整洁。 */
    .clear-v2 #v2Wallet,
    .clear-v2 #v2Help {
        display: inline-flex !important;
    }

    /* 经典版顶部动作区：两列网格，按钮撑满避免出现 4px 内边距的窄条 */
    body:not(.desktop-mode) .navbar .nav-actions > .btn {
        padding: 10px 8px;
        min-height: var(--ma-tap);
    }

    /* 底部导航（经典版）：四个等分，图标+文字纵向排列，留安全区 */
    .mobile-workspace-nav {
        padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 6px);
    }

    .mobile-workspace-nav button {
        min-height: var(--ma-tap);
        font-size: var(--ma-fs-floor);
    }

    /* 宽表格统一进横向滚动容器，并给右缘渐隐暗示 */
    .table-responsive,
    .analysis-relation-table-wrap,
    .referral-orders-wrap {
        overscroll-behavior-x: contain;
    }
}

/* ============================================================================
 * §11 横屏 / 矮视口
 * ----------------------------------------------------------------------------
 * 手机横屏的可视高度只有 320–430px。固定高度的头部 + 底部栏会把内容挤没，
 * 必须压缩垂直占用。
 * ========================================================================= */
@media (max-width: 991.98px) and (orientation: landscape) and (max-height: 480px) {
    .yan-page-header,
    .v2-header,
    .navbar {
        --ma-header-pad-y: 6px;
        min-height: 44px;
    }

    .yan-page-title .yan-page-eyebrow,
    .v2-brand strong {
        display: none;
    }

    /* 横屏时侧栏 tab 条压扁 */
    .yan-page-sidebar {
        padding-top: 6px;
        padding-bottom: 6px;
    }

    /* 全屏弹窗在横屏矮视口里必须可滚 */
    .yan-workspace-dialog,
    .yan-confirm-dialog {
        max-height: 100dvh;
        overflow-y: auto;
    }

    /* 键盘弹出后的矮视口（visualViewport 高度）——底部固定栏让位 */
    .mobile-workspace-nav {
        position: static;
    }
}

/* ============================================================================
 * §12 动效偏好与可访问性
 * ========================================================================= */

/* 系统开启"减弱动态效果"时，停止所有无限循环动画。
   本层补上原样式遗漏的三处 loading 动画（主样式 .loading、
   clear-v2 的 .v2-loading-dot、admin 的 .admin-load-status）。 */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
        scroll-behavior: auto !important;
    }

    /* 但"正在加载"必须仍然看得出来，否则用户以为卡死了 —— 用静态指示替代 */
    .loading,
    .v2-loading-dot,
    .admin-load-status.loading::before {
        animation: none !important;
        opacity: .75;
    }
}

/* 按压反馈在减弱动效下保留（它是功能性的，不是装饰性的），
   但要确保瞬时生效、不带过渡。 */
@media (prefers-reduced-motion: reduce) and (pointer: coarse) {

    a:active,
    button:active,
    [role="button"]:active,
    .yan-button:active,
    .btn:active {
        transition: none;
    }
}

/* 焦点可见性：触屏上点击不该出现焦点圈，但键盘/外接键盘必须有 */
@media (pointer: coarse) {
    a:focus:not(:focus-visible),
    button:focus:not(:focus-visible),
    [role="button"]:focus:not(:focus-visible) {
        outline: none;
    }
}

/* ============================================================================
 * §13 长文与表单的可读性
 * ========================================================================= */
@media (max-width: 767.98px), (orientation: landscape) and (max-height: 500px) and (max-width: 1024px) {
    /* 正文行高放宽：手机上窄栏 + 1.4 行高会挤 */
    .yan-page-content p,
    .v2-app p,
    .yan-ov-row,
    .yan-page-feature p {
        line-height: 1.7;
    }

    /* 表单行间距：手指点选需要更大间距，避免误触相邻字段 */
    .yan-page-content form > * + * {
        margin-top: 14px;
    }

    /* 日期/数字选择器在 iOS 上默认右对齐会跳，统一左对齐 */
    input,
    select,
    textarea {
        text-align: left;
    }
}

/* ============================================================================
 * §14 视觉令牌 v2：圆角 / 光感 / 流式字体（2026-10-18）
 * ============================================================================
 * 用户诉求原文：「用圆角、有光感、组件和字体能自动缩放，手机上好用、不同设备
 * 显示完整清晰」。本层只做**叠加式**视觉升级：
 *   1. 统一圆角刻度（既有样式里的 4/6/8/10/12/16px 混用 → 收敛到 --ds-radius-*）；
 *   2. 光感 = 极轻的层次阴影 + 主操作按钮顶部高光渐变（不做发光描边，避免廉价感）；
 *   3. 标题与正文用 clamp() 随视口平滑缩放，替代"跳断点改字号"；
 *   4. 不动布局网格、间距刻度、DOM 结构、id/class 契约，因此不影响任何交互测试。
 * 回滚方式：删除本段即可完全恢复原视觉（不改任何其它文件）。
 * ========================================================================= */
:root {
    --ds-radius-xs: 6px;
    --ds-radius-sm: 8px;
    --ds-radius-md: 12px;
    --ds-radius-lg: 16px;
    --ds-radius-pill: 999px;

    --ds-elevation-1: 0 1px 2px rgba(15, 23, 42, 0.06), 0 1px 3px rgba(15, 23, 42, 0.08);
    --ds-elevation-2: 0 6px 16px -4px rgba(15, 23, 42, 0.12), 0 2px 6px rgba(15, 23, 42, 0.06);
    --ds-glow-primary: 0 0 0 1px rgba(37, 99, 235, 0.12), 0 8px 24px -10px rgba(37, 99, 235, 0.35);

    --ds-fluid-h1: clamp(1.25rem, 0.9rem + 1.6vw, 2rem);
    --ds-fluid-h2: clamp(1.1rem, 0.95rem + 0.9vw, 1.5rem);
    --ds-fluid-body: clamp(0.875rem, 0.82rem + 0.25vw, 0.9375rem);
}

/* 圆角收敛：卡片 / 弹窗 / 输入框 / 按钮 / 徽章 */
.card,
.yan-card,
.v2-card,
.panel,
.modal-content,
.dialog,
dialog.yan-workspace-dialog,
.alert {
    border-radius: var(--ds-radius-md);
}

.form-control,
.form-select,
.input,
.select,
textarea {
    border-radius: var(--ds-radius-sm);
}

.btn,
.yan-button,
button.btn {
    border-radius: var(--ds-radius-sm);
}

.badge,
.pill,
.chip,
.yan-tag {
    border-radius: var(--ds-radius-pill);
}

/* 光感：卡片层次阴影（仅极轻，不抢内容）+ 主操作按钮顶部高光 */
.card,
.yan-card,
.v2-card {
    box-shadow: var(--ds-elevation-1);
}

.btn-primary,
.yan-button.yan-primary,
button.btn-primary {
    /* 顶部高光必须**叠在**主渐变之上，不能替换它：
       background-image 只有一层，写成单独的光泽渐变会把 index-styles 里
       .btn-primary 的主色渐变（var(--gradient-primary)）整个替换掉 ——
       结果是 background-color 透明（shorthand 的初值）+ 近乎全透明白光泽
       + color:white + border-color:currentcolor(白)，
       整颗按钮白字白框透明底，等于隐形。
       2026-10-19 线上「查看完整报告」按钮白字看不清就是这条。
       改成两层：主渐变在下，高光在上。

       2026-10-21 补：上面那次只修好了**首页**（index.html 加载
       index-styles-classic.css，--gradient-primary / --primary-color 有定义）。
       独立账户页 /static/account.html **不加载**那套样式（它只有
       account-workspace.css + mobile-adapt.css + admin-shared.css），
       两个变量都是 undefined → var() 整条声明在「计算值阶段失效」，
       background-image 退回初值 none、background-color 退回初值 transparent，
       而 .yan-primary 的 color:#fff 仍然生效 —— 结果就是
       **白字透明底的隐形按钮**（充值弹窗、会员开通支付弹窗都是这样）。
       所以这里必须给 var() 兜底：无论页面有没有那套 token，
       这条规则都要自己站得住。 */
    background-image: var(--gradient-primary, linear-gradient(135deg, #2f5ae0 0%, #3b82f6 100%)), linear-gradient(180deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0) 55%);
    background-color: var(--primary-color, #2f5ae0);
    color: #fff;
    border-color: transparent;
    box-shadow: var(--ds-glow-primary, 0 6px 16px rgba(47, 90, 224, .28));
}

.btn-primary:hover,
.yan-button.yan-primary:hover {
    box-shadow: 0 0 0 1px rgba(37, 99, 235, 0.18), 0 10px 26px -10px rgba(37, 99, 235, 0.45);
}

/* 流式字体：只作用于正文标题层级，不碰表格内的紧凑排版 */
h1,
.h1 {
    font-size: var(--ds-fluid-h1);
    letter-spacing: -0.01em;
}

h2,
.h2 {
    font-size: var(--ds-fluid-h2);
}

.yan-page-content p,
.v2-app p {
    font-size: var(--ds-fluid-body);
}

/* 尊重用户的减弱动效偏好：光感过渡不加在 hover 上，这里只保证不引入动画 */
@media (prefers-reduced-motion: reduce) {
    .card,
    .btn-primary,
    .yan-button.yan-primary {
        transition: none;
    }
}

/* ============================================================================
 * §15 可访问性与细节打磨（2026-10-18 第二轮）
 * ============================================================================
 * 目标（不改变任何 DOM / 交互逻辑，纯 CSS 叠加）：
 *   1. 键盘/外接键盘用户始终能看见焦点（此前只做了"触屏不显示焦点圈"，未补可见圈）；
 *   2. 宽表格在手机上可发现"还能横向滑动"（滚动条 + 边缘阴影提示）；
 *   3. 长英文/URL/题号在窄屏不撑破卡片（overflow-wrap）；
 *   4. 高对比度偏好下加重边框，弱视力用户能分清卡片边界。
 * 回滚：删除本段即可。
 * ========================================================================= */

/* 1) 可见焦点圈：只对键盘焦点生效（鼠标/触屏点击不出现） */
:focus-visible {
    outline: 2px solid var(--bs-primary, #2e3192);
    outline-offset: 2px;
    border-radius: var(--ds-radius-xs, 6px);
}

/* 输入类控件聚焦时用边框+柔光，避免 outline 与表单边框重叠显得脏 */
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
    outline: none;
    border-color: var(--bs-primary, #2e3192);
    box-shadow: 0 0 0 3px rgba(46, 49, 146, 0.18);
}

/* 2) 宽表格：可发现的横向滚动（细滚动条 + 右侧渐隐提示） */
@media (max-width: 991.98px) {
    .table-responsive,
    .process-model-table-wrap,
    .analysis-relation-table-wrap {
        -webkit-overflow-scrolling: touch;
        scrollbar-width: thin;
    }

    .table-responsive::-webkit-scrollbar,
    .process-model-table-wrap::-webkit-scrollbar,
    .analysis-relation-table-wrap::-webkit-scrollbar {
        height: 6px;
    }

    .table-responsive::-webkit-scrollbar-thumb,
    .process-model-table-wrap::-webkit-scrollbar-thumb,
    .analysis-relation-table-wrap::-webkit-scrollbar-thumb {
        background: rgba(15, 23, 42, 0.28);
        border-radius: 999px;
    }

    /* 表头在长表滚动时保持可见 */
    .table-responsive > table > thead th {
        position: sticky;
        top: 0;
        z-index: 2;
        background: var(--surface, #fff);
    }
}

/* 3) 长词/长链接不撑破卡片（题号、URL、英文维度名常见） */
.card,
.yan-card,
.table-responsive td,
.modal-body,
.alert {
    overflow-wrap: anywhere;
}

/* 标题按词换行更平衡（现代浏览器生效，旧浏览器自动忽略） */
h1,
h2,
h3,
.card-title {
    text-wrap: balance;
}

/* 4) 高对比度偏好：卡片/输入边框加重，状态徽章保留描边 */
@media (prefers-contrast: more) {
    .card,
    .yan-card,
    .v2-card,
    .alert,
    .form-control,
    .form-select {
        border: 1px solid rgba(15, 23, 42, 0.55);
    }
}

/* 5) 打印/低动效：焦点与阴影在打印时无意义，去掉以免出现灰块 */
@media print {
    :focus-visible {
        outline: none;
    }

    .card,
    .yan-card {
        box-shadow: none;
    }
}

/* ============================================================================
 * §16 任务配置步骤导航（2026-10-18 UI P2）
 * ============================================================================
 * 首页任务表单很长（数据源 → 研究类型 → 问卷解析 → 任务设置），滚到底部想回看
 * 某一步要手动长滚。这里在表单顶部加一条**纯锚点**导航：不引入 JS、不改任何
 * 既有结构与 id，只做视觉与跳转；手机上横向滚动成 chips，桌面一行铺开。
 * 回滚：删除本段落（模板里的 <nav id="taskStepNav"> 可留可删，未启用样式时退化为普通链接）。
 * ========================================================================= */
.task-step-nav {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding: 8px 10px;
    margin: 0 0 16px;
    border: 1px solid var(--ds-border, #e2e8f0);
    border-radius: var(--ds-radius-pill, 999px);
    background: rgba(255, 255, 255, 0.86);
    backdrop-filter: blur(8px);
    box-shadow: var(--ds-elevation-1, 0 2px 8px rgba(15, 23, 42, 0.06));
}

.task-step-nav__label {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    color: #7a879c;
    padding-left: 4px;
}

.task-step-chip {
    display: inline-flex;
    align-items: center;
    min-height: 32px;
    padding: 4px 12px;
    border-radius: var(--ds-radius-pill, 999px);
    border: 1px solid #d6e0f0;
    background: #f8fbff;
    color: #2f4fa8;
    font-size: 12px;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    transition: background 0.16s ease, border-color 0.16s ease, transform 0.16s ease;
}

.task-step-chip:hover,
.task-step-chip:focus-visible {
    background: #eef3ff;
    border-color: #b7c9ee;
    color: #1f3c8f;
}

.task-step-chip:active {
    transform: scale(0.97);
}

@media (max-width: 767.98px), (orientation: landscape) and (max-height: 500px) and (max-width: 1024px) {
    .task-step-nav {
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
        position: sticky;
        top: 0;
        z-index: 5;
        margin-bottom: 12px;
    }

    .task-step-nav::-webkit-scrollbar {
        display: none;
    }

    .task-step-nav__label {
        display: none;
    }

    .task-step-chip {
        min-height: 36px;
        font-size: 12.5px;
    }
}

@media print {
    .task-step-nav {
        display: none;
    }
}

/* ============================================================================
 * §17 界面显示缩放（分辨率适配，2026-10-18）
 * ============================================================================
 * 用户诉求：「设计一个分辨率的设置」。高 DPI / 小屏 / 投屏 / 老人机等场景下，
 * 100% 界面要么太小要么太挤。这里提供 4 档**整体缩放**（90/100/110/125），
 * 由 account-workspace.js 写入 html[data-ui-scale] 并记忆到 localStorage。
 *
 * 为什么用 zoom 而不是改字号：本项目大量既有样式是 px 定宽 + clamp 字号，
 * 只改根字号无法等比缩放内边距与按钮；zoom 能一次性等比缩放整页（含布局），
 * 且不改变 DOM/事件坐标契约。Firefox 旧版不支持 zoom 时自动退化为 100%，
 * 属于安全降级（只是设置不生效）。
 *
 * 回滚：删除本段（JS 里 setAttribute 会变成无害空操作）。
 * ========================================================================= */
html {
    zoom: var(--ui-zoom, 1);
}

html[data-ui-scale="90"] { --ui-zoom: 0.9; }
html[data-ui-scale="100"] { --ui-zoom: 1; }
html[data-ui-scale="110"] { --ui-zoom: 1.1; }
html[data-ui-scale="125"] { --ui-zoom: 1.25; }

/* 打印时按 100% 输出，避免缩放后排版溢出 */
@media print {
    html {
        zoom: 1;
    }
}

/* 设置面板里的分段选择器（与步骤导航 chips 同一视觉语言） */
.ui-scale-options {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 10px 0 4px;
}

.ui-scale-option {
    min-height: 36px;
    padding: 6px 14px;
    border-radius: var(--ds-radius-pill, 999px);
    border: 1px solid #d6e0f0;
    background: #f8fbff;
    color: #2f4fa8;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
}

.ui-scale-option[aria-pressed="true"] {
    background: #eaf1ff;
    border-color: #2f4fa8;
    color: #1f3c8f;
    box-shadow: inset 0 0 0 1px #2f4fa8;
}

.ui-scale-hint {
    display: block;
    margin-top: 6px;
    font-size: 12px;
    color: #6b7a90;
}
