/* =====================================================================
   shell-fit.css —— 应用外壳「视口高锁定」与遗留 vh 反算（V2.3.11 新增）
   ─────────────────────────────────────────────────────────────────────
   为什么独立成文件：本轮是**骨架级**改动（动全局外壳高度），风险面比单页大。
   独立成文件后，回退 = 删掉 index.html 里本文件那一行，其余文件零残留。
   本文件**排在最后加载**（见 index.html），同特异性下靠「后出现」压制
   styles.css / theme-v3~v5 / ai-page.css 的旧写法。

   ── 起因（涛哥 2026-09-22 报障，原话）─────────────────────────────────
   「还是发现了一个全局左栏实际高度大于网页窗口视图高度，且网页底部仍然留有空白…
     虽然乍一看确实左栏高度目前与网页视图高度一致，但是只要在左栏之外滚动滚轮向下，
     就会发现全局左栏实际上要更高，这导致了所有右侧功能页面实际上都有超出当前网页
     视图高度的隐患…请参考主流APP类似问题操作和修改逻辑修复这个问题，
     使全局左栏高度与功能页面高度一致不至于溢出网页视图」

   ── 实测取证（_01_e2e/_probe_shell_overflow_20260922.cjs，改动前）───────
     1440×900：视口 900，文档却 1689px ⇒ 溢出 789px，可滚到底 maxScrollY=789
       .sidebar   rect.h = 900  ✓（所以涛哥说"乍一看一致"）
       .app-shell rect.h = 1573 ✗（比视口高 673px）
       #view      bottom  = 1573 ✗（底端落在屏幕外 673px 处）
       body       padding-bottom:86px ⇒ 底部那截空白
     1280×720 ⇒ 溢出 998px；1920×1080 ⇒ 溢出 583px（三种视口全中）

   ── 真根因 ─────────────────────────────────────────────────────────
     ① `.app-shell{min-height:100vh}` 与 `.app-main{min-height:100vh}`
        在 `body{zoom:1.35}` 下 **`vh` 不参与 zoom 换算** ⇒ 视觉 135vh。
        （同源先例：theme-v5.css 里 V2.2.71/76、V2.3.3 已为此反算过弹窗与左栏，
          但**外壳这一层漏了** —— 这正是"弹窗溢出屏幕外"反复复发的深层原因：
          祖先比视口高，后代再怎么限高都只是被顶出屏幕。）
     ② `body{padding-bottom:86px}` 是给手机端 tabbar 留的，PC 端纯浪费 ⇒ 底部空白。
     ③ 各功能页的"撑满可用高度"普遍写死 `calc(100vh - Npx)`（styles.css 里 6 处），
        N 是按"文档会滚动"估算的魔法数，外壳一虚高就全部错位。

   ── 修法（主流 SaaS 做法：Linear / Vercel / Notion 都是这一套）────────
     把应用做成**固定高度外壳**：文档永不滚动，滚动权全部下放给 `main#view`。
       · html / body  = 恰好一个视口高，overflow:hidden  ⇒ 结构上不可能再溢出
       · 左栏         = 100%（内部 .side-nav 自己滚）
       · .app-main    = 100%，纵向 flex
       · main#view    = flex:1 + min-height:0 + overflow-y:auto + overscroll-behavior:contain
                       ⇒ 每个功能页在自己的滚动区里独立滚动、不穿透，
                         且**底端永远落在视口内**（V2.3.5「栏底出屏」类问题从根上消失）
     配套：`scrollbar-gutter:stable` 常驻预留滚动条槽位 ⇒ 长短页切换时内容宽度不抖
     （否则滚动条忽有忽无，每切一页就横移十几像素 —— 又一类"页面弹动"）。

   ⚠ 断点与 theme-v5 的 zoom 定义保持一致（min-width:821px = 电脑端）。
     手机端（≤820px）**一行都不动**：那边是竖向堆叠 + 文档滚动，本来就正常。

   ⚠ `var(--hz-page-zoom, 1)` 的兜底值 1 不能省 —— 该变量只定义在
     `@media (min-width:821px)` 里，无兜底会让整条声明失效。
   ===================================================================== */

@media (min-width:821px){

  /* ═══════════ ① 外壳锁进视口 ═══════════ */

  html{
    height:100%;
    overflow:hidden;                 /* 结构上封死文档滚动 */
  }
  body{
    /* ★ 必须除以 zoom：`100vh` 在 zoom 下不换算，直接写会渲染成视觉 135vh */
    height: calc(100vh / var(--hz-page-zoom, 1));
    overflow:hidden;
    padding-bottom: 0;               /* 那 86px 是手机端 tabbar 的预留，PC 端只造成底部空白 */
  }
  .app-shell{
    height: 100%;
    min-height: 0;                   /* 覆盖 styles.css 的 min-height:100vh */
  }
  .sidebar{
    height: 100%;
    max-height: 100%;
    position: relative;              /* 外层已不滚动 ⇒ sticky 无意义，改静态消除歧义 */
    top: auto;
    overflow: hidden;                /* 左栏自身不滚，滚动交给内部 .side-nav */
  }
  .app-main{
    height: 100%;
    min-height: 0;                   /* 覆盖 styles.css 的 min-height:100vh */
  }
  header{
    flex: 0 0 auto;                  /* 顶栏不被压缩 */
    position: relative;              /* #view 才是滚动容器，顶栏不需要 sticky */
    top: auto;
  }

  /* ★ 唯一的滚动容器：功能页在这里独立滚动，底端对齐视口底边 */
  main#view{
    flex: 1 1 auto;
    min-height: 0;                   /* flex 子项能收缩的必要条件 */
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    scrollbar-gutter: stable;        /* 常驻预留槽位，长短页切换宽度不抖 */
    /* 底部留一点呼吸：滚到最后一行的下方不贴死屏幕边 */
    padding-bottom: 18px;
  }
  main#view.law-wide,
  main#view.ai-wide{ padding-bottom: 0; }   /* 这两类页面自己撑满，不留底边距 */

  /* ═══════════ ② 残留 vh 反算 ═══════════
     以下每一条都是"想表达视觉 N vh"却写了裸 `Nvh` 的旧规则。
     逐个换成 calc(N / zoom) 后，它们才真正等于"屏幕的 N%"。
     （纯数值替换，不改任何布局意图。） */

  /* 2.1 通用弹窗盒：88vh 在 zoom 下渲染成 118.8vh ⇒ 比视口还高（"弹窗溢出"的同源根因） */
  .mbox{ max-height: calc(88vh / var(--hz-page-zoom, 1)); }
  .modal .sheet{ max-height: calc(84vh / var(--hz-page-zoom, 1)); }

  /* 2.2 阅读类容器 */
  .lib-doc{ max-height: calc(56vh / var(--hz-page-zoom, 1)); }
  .lib-doc-full,
  .gen-clause{ max-height: calc(46vh / var(--hz-page-zoom, 1)); }
  .toc-tree{ max-height: calc(55vh / var(--hz-page-zoom, 1)); }
  #j59-basis-list{ max-height: calc(44vh / var(--hz-page-zoom, 1)); }
  .law-body-html[data-chunking]{ min-height: calc(60vh / var(--hz-page-zoom, 1)); }

  /* 2.3 指南依据抽屉：fixed 的 top/bottom 用 vh 同样不换算 ⇒ 视觉高 121.5vh 被上下切 */
  .j59-drawer{
    top: calc(5vh / var(--hz-page-zoom, 1));
    bottom: calc(5vh / var(--hz-page-zoom, 1));
  }

  /* ═══════════ ③ 规范全文阅读页：把高度全让给正文 ═══════════
     涛哥原话：「既保证规范正文阅读窗口大小不至于因限制页面高度而只有一点点的
               阅读空间（你可以使规范信息折叠？不成熟的看法，希望你有更好的办法）」

     比"折叠"更好的办法：**信息不藏、只压扁**，并把阅读区改成"吃掉所有剩余高度"。
       · 旧写法 `.law-body-wrap{height:calc(100vh - 150px)}` —— 150 是"文档会滚动"
         年代估出来的魔法数，现在壳一锁死就完全错位（实测要 1012px 视觉，
         实际只有约 600px 可用）⇒ 改成 flex:1 自适应。
       · 顶部「规范信息」（标题/发布机构/日期/标签）从三行压成一行紧凑条，
         省下的高度直接给正文 —— 不需要用户点一下才能展开，反而更省事。 */

  .law-detail{
    position: relative;              /* 给下面「按钮行叠到面包屑行」做定位参照 */
    height: 100%;                    /* 撑满 #view 的可用高（#view 高度是确定的） */
    display: flex; flex-direction: column;
    padding: 10px 24px 10px;         /* 旧 18px 24px 44px：底部 44px 是给文档滚动留的，已无必要 */
    margin: 0;
  }
  .law-detail > .law-crumb,
  .law-detail > .law-head{ flex: 0 0 auto; }

  .law-crumb{ margin-bottom: 6px; padding-right: 168px; }   /* 右侧留给叠上去的按钮行 */

  /* ★ 高度再榨一层：把「📑目录 / 🖨打印」按钮行**叠到面包屑那一行**的右侧，
     不占纵向空间（省下 46px + 8px 间距 ≈ 视口的 6%）。
     用绝对定位而不是负 margin —— 负 margin 在 flex 纵向容器里会连带影响后续兄弟的位置，
     这里只要把按钮行移出文档流即可，面包屑行用 padding-right 让位，永不重叠。
     窄屏（<1100px）不叠：面包屑里的规范名较长会折行，叠上去容易互相压住。 */
  .law-topbar{
    position: absolute; right: 24px; top: 10px;
    margin: 0; gap: 8px;
  }
  .law-head{
    display: flex; align-items: center; gap: 5px 12px; flex-wrap: wrap;
    padding: 8px 12px;
    margin-bottom: 8px;
  }
  .law-head .lh-title{ font-size: 16px; line-height: 1.35; flex: 1 1 auto; min-width: 0; }
  .law-head .lh-org{ margin-top: 0; font-size: 12px; gap: 12px; }
  .law-head .law-tags{ margin-top: 0; }

  /* 阅读区：吃掉「页面总高 − 上方信息」的全部剩余 */
  .law-full{
    flex: 1 1 auto; min-height: 0;
    display: flex; flex-direction: column;
    padding: 10px 12px;
  }
  .law-full-h{ flex: 0 0 auto; margin-bottom: 8px; }
  .law-body-wrap{
    flex: 1 1 auto; min-height: 0;
    height: auto;                    /* 覆盖 styles.css 的 calc(100vh - 150px) */
    margin-top: 0;
  }
  .law-body{ height: 100%; }
  .law-toc{ height: 100%; }

  /* 窄一点的电脑窗口（821~1099px）不叠按钮行：面包屑里的规范名往往很长会折行，
     叠上去容易互相压住 ⇒ 退回原来的上下两行（嵌套 @media 即 (min-width:821px) and (max-width:1099px)）。 */
  @media (max-width:1099px){
    .law-topbar{ position: static; right: auto; top: auto; margin-bottom: 6px; }
    .law-crumb{ padding-right: 0; }
  }

  /* ═══════════ ④ AI 智能问答页：同样撑满 ═══════════
     ai.js 的 _fitAiPage() 用 innerHeight 手算，在锁定后的壳里会差几像素
     （#view 的 padding 没算进去）⇒ PC 端交给 CSS 的 100% 自适应，JS 不再插手。 */
  main#view > .ai-page{ height: 100%; min-height: 0; }

}
