/* 由 demos/commons_min_final_v4_2026-08-20.html 生成，勿手改。
   生成器：scripts/build_shell.py */
  * { margin: 0; padding: 0; box-sizing: border-box; }
  html { background: #e9e9eb; }
  /* 字阶只有四档,全站从这里取,不再各处各写一个数
     （2026-08-28 用户：字号不要有些特别大有些特别小）。
       22  图形里的主读数（环心那个规模数）与公地卡标题
       17  标题
       15  正文与列表行
       13  次级、说明、控件标签
     收编前是九档：11 / 12 / 12.5 / 13 / 14 / 14.5 / 15 / 17 / 22。 */
  :root { --fs-read: 22px; --fs-title: 17px; --fs-body: 15px; --fs-note: 13px; }
  body {
    /* Noto Sans SC 排在最前（2026-08-28 用户：就要这一个）。原先它排第五,
       前四个在 Windows 上一个都不存在,是**碰巧**落到它身上的——换台机器
       就换字体。想要哪一个就把哪一个写在最前,后面几个只作兜底。 */
    font-family: "Noto Sans SC", -apple-system, "SF Pro Text", "PingFang SC",
                 "Hiragino Sans GB", sans-serif;
    background: #e9e9eb; min-height: 100vh; padding: 28px 0 40px;
  }
  .device {
    width: 393px; height: 852px; flex: none;
    background: #000; border-radius: 55px; padding: 10px;
    box-shadow: 0 30px 80px rgba(0,0,0,.30);
  }
  .screen {
    width: 100%; height: 100%; background: var(--surface);
    border-radius: 46px; overflow: hidden; position: relative;
  }
  .island { position: absolute; top: 11px; left: 50%; transform: translateX(-50%);
    width: 122px; height: 35px; border-radius: 18px; background: #000; z-index: 45; }
  /* 浮在内容之上,不占位——占位的话内容还是从 58px 起,等于没改 */
  .statusbar { height: 58px; display: flex; align-items: flex-end;
    justify-content: space-between; padding: 0 34px 6px;
    position: absolute; left: 0; right: 0; top: 0; z-index: 30;
    pointer-events: none; }
  .statusbar .time { font-size: var(--fs-body); font-weight: 600; color: var(--ink); }
  /* 内容从屏顶起,不从状态栏下沿起——灵动岛两侧那一圈本来就该看得见东西。
     状态栏改成浮在上面（不占位）,内容滚到顶时从它底下穿过去。 */
  .chat { position: absolute; left: 0; right: 0; top: 0; bottom: 0;
    overflow-y: auto; padding: 116px 24px 170px;
    font-size: var(--fs-body); line-height: 2.0; color: var(--ink2); }
  /* 顶部磨砂渐隐:把穿过去的字化掉,而不是硬切一刀。
     mask 让磨砂本身也是渐隐的,否则会看见一条明显的下边缘。 */
  .topfade { position: absolute; left: 0; right: 0; top: 0; height: 96px;
    z-index: 26; pointer-events: none;
    -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
    background: linear-gradient(to bottom,
      var(--surface) 0%, color-mix(in srgb, var(--surface) 62%, transparent) 46%,
      transparent 100%);
    -webkit-mask-image: linear-gradient(to bottom, #000 52%, transparent 100%);
    mask-image: linear-gradient(to bottom, #000 52%, transparent 100%); }
  .chat::-webkit-scrollbar { display: none; }
  .chat p + p { margin-top: 12px; }
  /* 自己说的话套一只浅框:答话里的粗体上屏之后,单靠字重分不开两者。
     仍与响应同一列、同一左缘;框只到内容宽,长句照旧折行。 */
  .ub { margin: 30px 0 18px 0; width: fit-content; max-width: 100%;
    color: var(--ink); font-weight: 400;
    font-size: var(--fs-body); line-height: 1.7;
    background: var(--fill); border-radius: 18px; padding: 10px 14px; }
  .ub:first-child { margin-top: 4px; }
  /* ── 六类记号 ───────────────────────────────────────────────────────
     每一类说这一句的一件事,彼此不重复:
       数字   这句挂着你库里的哪条引文        点开=逐字原文
       °      这句没有出处                    点开=它从哪来 + 深化
       ↗      网上刚查到,还没审计            点开=原文 + 标记
       放大镜 这句背后查过哪几处              点开=门与真实查询词
       弧     这片地面有人走过                点开=那次走的关系与书
       方片   这一份是你自己给的              点开=文件与用到的那一处
     两档呈现同一份数据:符号档只出形,标签档补上符号说不清的那点
     (°/↗ 补词、放大镜补数、你提供的补名;足迹带着数已说完,不补)。 */
  sup.mk { padding: 0 1px; margin: 0 1px; font-size: .72em; line-height: 1;
    color: var(--ink6); cursor: pointer; white-space: nowrap;
    font-style: normal; }
  /* 两半装在 <i> 里,<i> 自带斜体——中文斜体读着像强调,而记号不是强调 */
  sup.mk .sym, sup.mk .lbl { font-style: normal; }
  sup.mk.on { color: var(--ink); }
  sup.mk svg { vertical-align: -0.5px; }
  sup.mk svg * { stroke: currentColor; }
  sup.mk-mine svg * { stroke: none; fill: currentColor; }
  /* 标签档:换掉符号那一半。用两个真实节点而不是 ::before,
     探针要能逐字读到切换前后的文本 */
  sup.mk .lbl { display: none; }
  .screen.mklabel sup.mk .sym { display: none; }
  .screen.mklabel sup.mk .lbl { display: inline; }
  /* 数字与足迹两档一样:一个是数,一个是图标带着数,都已经说完了 */
  .screen.mklabel sup.mk-fp .sym,
  .screen.mklabel sup.mk-src .sym { display: inline; }
  /* 你提供的:标签档带上文件名前五字,后面渐隐。
     省略号是一个字符,会占位并读作「还有内容」;渐隐读作「名字在这里断了」 */
  sup.mk-mine .lbl .nm { display: inline-block; max-width: 46px; overflow: hidden;
    vertical-align: bottom;
    -webkit-mask-image: linear-gradient(to right, #000 62%, transparent 100%);
    mask-image: linear-gradient(to right, #000 62%, transparent 100%); }
  .unsrc { color: var(--ink5); cursor: pointer; }
  .webtxt { background: var(--fill); border-radius: 4px; padding: 0 2px; transition: background .8s; }
  .webtxt.audited { background: transparent; }

  /* ── 展开块:六个变体 ────────────────────────────────────────────── */
  .exp { display: none; margin: 10px 0 16px 14px; padding: 1px 0 1px 14px;
    border-left: 2px solid var(--fill); font-size: var(--fs-note); color: var(--ink5); line-height: 1.85; }
  .exp.open { display: block; }
  .exp .q { color: var(--ink4); }
  .exp .att { font-size: var(--fs-note); color: var(--ink6); margin-top: 1px; }
  .exp .act2 { color: var(--ink0); border-bottom: 1px solid var(--ink5); cursor: pointer; }
  /* 可点的书名/文件名:跳到库里那一段,或那本书的信息 */
  /* 可点的书名/文件名。下划线要细过字:四个书名连成一排时,
     线比字还抢眼就把块读成了一张表 */
  .exp .wk { color: var(--ink2); cursor: pointer;
    border-bottom: 1px solid color-mix(in srgb, var(--hair) 55%, transparent); }
  .exp .wk:active { border-bottom-color: var(--ink4); }
  /* ② 只说来源,不猜来源 */
  .exp .prov { color: var(--ink4); }
  /* ④ 逐门分行:门 · 真实查询词 · 回来几条 */
  .exp .door { display: flex; gap: 10px; align-items: baseline; padding: 2px 0;
    font-size: var(--fs-note); color: var(--ink4); }
  .exp .door .dn { flex: none; width: 62px; color: var(--ink5); }
  .exp .door .dq { flex: 1; min-width: 0; word-break: break-word; }
  .exp .door .dc { flex: none; color: var(--ink5); font-variant-numeric: tabular-nums; }
  /* ⑤ 迹:以关系为行,不以书为行。这几行连起来才是「迹」 */
  .exp .head { color: var(--ink3); padding-bottom: 4px; }
  .exp .rel { padding: 5px 0 0; }
  .exp .rel .rl { color: var(--ink2); }
  .exp .rel .rt { color: var(--ink5); padding: 0 5px; }
  .exp .rel .rw { display: block; color: var(--ink5); padding-left: 12px; }
  .exp .solo { display: flex; gap: 8px; align-items: baseline; padding: 4px 0 0;
    color: var(--ink4); }
  .exp .solo .sw { flex: 1; min-width: 0; color: var(--ink5); }
  .exp .solo .sn { flex: none; color: var(--ink6); }
  /* 折叠:控件是一个数,不是词 */
  .exp .more2 { display: inline-block; margin-top: 6px; padding: 0 5px;
    border: 1px solid var(--hair); border-radius: 7px; color: var(--ink6);
    font-size: var(--fs-note); line-height: 1.5;
    font-variant-numeric: tabular-nums; cursor: pointer; }
  .exp .hid { display: none; }
  /* 够不到的那几本:一行一本,书名 · 够不到的原因 · 一个 ⊕。无邀请句 */
  .ncopy { display: none; margin: 14px 0 4px; }
  .ncopy.on { display: block; }
  .ncrow { display: flex; gap: 10px; align-items: center; padding: 7px 1px;
    font-size: var(--fs-body); color: var(--ink2);
    border-top: 0.5px solid var(--hair); }
  .ncrow:first-child { border-top: none; }
  .ncrow .ncr { flex: 1; min-width: 0; font-size: var(--fs-note); color: var(--ink6); }
  .ncrow .ncp { flex: none; color: var(--ink5); cursor: pointer;
    display: inline-flex; padding: 2px; }
  .ncrow.got .ncp { display: none; }
  .ncrow.got .ncr { color: var(--ink5); }
  /* 批注框里的附件:与 ⊕ 同一个文件选择器,但这条是用户自己起的 */
  .notewin .nclip { flex: none; color: var(--ink5); cursor: pointer;
    display: inline-flex; padding: 2px; }
  .exp.spread .hid { display: block; }
  .exp.spread .more2 { display: none; }
  /* 标记之后不写一句话:一条细进度条走完,换成一个勾。
     进度条与那个勾本身就是陈述,不需要「今天标记并读完」来复述 */
  .mtrack { display: inline-block; vertical-align: middle; width: 78px; height: 3px;
    border-radius: 2px; background: var(--fill); overflow: hidden; }
  .mtrack i { display: block; height: 100%; width: 0; border-radius: 2px;
    background: var(--ink4); transition: width 1.5s linear; }
  .mdone { display: inline-flex; vertical-align: middle; color: var(--ink4); }
  .chat a { color: var(--ink2); }
  .caretline { color: var(--hair); margin-top: 26px;
    display: flex; align-items: center; }
  .caretline .cur { display: inline-block; width: 2px; height: 18px; background: #000;
    margin-left: 2px; animation: blink 1.05s infinite; flex: none; }
  .caretline .typed { color: var(--ink); }
  .csend { display: none; margin-left: auto; width: 27px; height: 27px; flex: none;
    border-radius: 50%; background: var(--ink); cursor: pointer;
    align-items: center; justify-content: center; }
  .caretline.hastxt .csend { display: inline-flex; }
  @keyframes blink { 50% { opacity: 0; } }
  .ghost { position: fixed; left: 0; top: 0; width: 24px; height: 24px;
    opacity: 0; pointer-events: none; border: 0; padding: 0; margin: 0;
    background: none; outline: none; font-size: var(--fs-title); }
  .gcap2 { padding-top: 4px; }
  .pihead { font-size: var(--fs-body); font-weight: 600; color: var(--ink); padding-top: 2px;
    display: flex; align-items: center; gap: 8px; }

  .psvg { position: absolute; inset: 0; width: 100%; height: 100%; }
  .pdeck { position: absolute; left: 10px; right: 10px; top: 64px; bottom: 10px;
    overflow: hidden; border-radius: 20px; }
  .pface { position: absolute; left: 0; right: 0; top: 0; border-radius: 20px;
    background: color-mix(in srgb, var(--pc, #8e8e93) 11%, var(--raised));
    overflow: hidden;
    border: 0.5px solid rgba(0,0,0,.07);
    box-shadow: 0 12px 26px rgba(0,0,0,.12);
    transition: transform .5s cubic-bezier(.32,1.04,.35,1),
      height .5s cubic-bezier(.32,1.04,.35,1); }
  /* 高度由 JS 按卡内可用区设(卡一变高环就跟着变大);
     写死 284 会盖掉它,环还会溢出卡外。 */
  .pface:not(.pon) { cursor: pointer; }
  .pface.pon .pfhead { cursor: pointer; }
  .pface.drg { transition: none; }
  @keyframes psnap {
    0% { transform: translateY(0) scale(.965); box-shadow: 0 12px 26px rgba(0,0,0,.12); }
    45% { transform: translateY(0) scale(1.014); box-shadow: 0 18px 40px rgba(0,0,0,.22); }
    72% { transform: translateY(0) scale(.996); }
    100% { transform: translateY(0) scale(1); box-shadow: 0 12px 26px rgba(0,0,0,.12); } }
  .pface.snap { animation: psnap .34s cubic-bezier(.3,.9,.4,1); }
  .pface.pon .pfhead::after { content: ""; position: absolute;
    top: 13px; right: 18px; width: 10px; height: 10px;
    border: 2px solid var(--pc, var(--ink)); border-radius: 50%; }
  /* 卡头在卡底:上压下的堆里露出的是每张卡的下缘,名字与规模行落在那一截 */
  .pfhead { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
    padding: 9px 18px 9px; }
  .pfname { font-size: var(--fs-body); font-weight: 600; color: var(--ink); }
  .pfscale { font-size: var(--fs-note); color: var(--ink5); margin-top: 2px; }
  .piscap { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
    padding: 26px 20px 12px; font-size: var(--fs-note); color: var(--ink3); line-height: 1.6;
    pointer-events: none;
    background: linear-gradient(to top, var(--fog), rgba(252,252,253,0));
    -webkit-backdrop-filter: blur(12px) saturate(1.3);
    backdrop-filter: blur(12px) saturate(1.3);
    -webkit-mask-image: linear-gradient(to top, #000 45%, transparent);
    mask-image: linear-gradient(to top, #000 45%, transparent); }
  .piscap:empty { opacity: 0; }
  .pcard .piscap { bottom: 172px; }
  /* 入库入口:次级页内的一行。原先是一条整宽的圆角矩形（浅底＋发丝边），
     2026-08-28 用户下令按钮不要圆角矩形——只留那枚 ＋ 本身,底与边全去掉。
     它挂 .gbtn（图标按钮父类）,尺寸与按压反馈都由父类给。 */
  .pingest { margin: 8px 0 4px; cursor: pointer;
    display: flex; align-items: center; justify-content: flex-start; }
  .pfog { position: absolute; top: 0; left: 0; right: 0; z-index: 2;
    padding: 16px 20px 30px; pointer-events: none;
    background: linear-gradient(to bottom, var(--fog), rgba(252,252,253,0));
    -webkit-backdrop-filter: blur(12px) saturate(1.3);
    backdrop-filter: blur(12px) saturate(1.3);
    -webkit-mask-image: linear-gradient(to bottom, #000 45%, transparent);
    mask-image: linear-gradient(to bottom, #000 45%, transparent); }
  .fog { margin: 0 -20px; padding: 0 20px 26px; position: relative; z-index: 2;
    background: linear-gradient(to bottom, var(--fog) 40%, rgba(252,252,253,0));
    -webkit-backdrop-filter: blur(12px) saturate(1.3);
    backdrop-filter: blur(12px) saturate(1.3);
    -webkit-mask-image: linear-gradient(to bottom, #000 55%, transparent);
    mask-image: linear-gradient(to bottom, #000 55%, transparent); }
  .hlayer { position: absolute; inset: 0; opacity: 0; pointer-events: none;
    transition: opacity .45s .1s; }
  .isl.h:not(.f) .hlayer { opacity: 1; pointer-events: auto; }
  .hlayer svg { position: absolute; inset: 0; width: 100%; height: 100%; cursor: pointer; }
  .hlayer .cm { pointer-events: none; }
  .isl.h:not(.f) .hlayer .cm { pointer-events: bounding-box; }
  .isl.h:not(.f) .stage { pointer-events: none; }
  .pcard { position: absolute; left: 14px; right: 14px; bottom: 120px; z-index: 41;
    height: 0; border-radius: 32px; padding: 0; overflow: hidden;
    background: var(--fog2);
    border: 0.5px solid rgba(0,0,0,.06);
    box-shadow: 0 8px 24px rgba(0,0,0,.08);
    -webkit-backdrop-filter: blur(22px) saturate(1.1);
    backdrop-filter: blur(22px) saturate(1.1);
    opacity: 0; pointer-events: none;
 }
  .screen.pfront .isl.h:not(.f) ~ .pcard { bottom: 30px; z-index: 43;
    box-shadow: 0 18px 50px rgba(0,0,0,.16); }
  .screen.pfront .isl.h:not(.f) { box-shadow: 0 8px 24px rgba(0,0,0,.08); }
  .screen.pfront .isl.h:not(.f) { bottom: 120px; z-index: 41; }

  /* 背卡淡出=罩纱层(不动元素 opacity,免得杀掉 backdrop-filter) */
  .pcard::after { content: ""; position: absolute; inset: 0; z-index: 6;
    border-radius: inherit; pointer-events: none;
    background: var(--fog3); opacity: 1; transition: opacity .4s; }
  .screen.pfront .pcard::after { opacity: 0; }
  .isl::after { content: ""; position: absolute; inset: 0; z-index: 6;
    border-radius: inherit; pointer-events: none;
    background: var(--fog3); opacity: 0; transition: opacity .4s; }
  .screen.pfront .isl.h:not(.f)::after { opacity: 1; }

  /* 我的底图卡=与公地岛同一副白玻璃;前后由纱与投影区分 */
  .pingest .ptrack { display: none; flex: 1; height: 4px; border-radius: 2px;
    background: rgba(0,0,0,.08); margin: 0 16px; }
  .pingest .pfill { height: 100%; width: 0; border-radius: 2px; background: var(--ink);
    transition: width 2.2s linear; }
  .pingest.ing .ptrack { display: block; }
  .pingest.ing > svg { display: none; }

  .screen.pfront .pcard:not(.pf) .pfog { pointer-events: auto; cursor: pointer; }
  .isl.h:not(.f) ~ .pcard.pf { bottom: 30px; height: 740px; z-index: 44; }
  .pcard.pf .pfog, .pcard.pf .piscap,
  .pcard.pf .pdeck { opacity: 0; pointer-events: none; }
  .pdeck { transition: opacity .3s; }
  .pcard.pf .psvg { opacity: .18; }
  .pcard .psvg { transition: opacity .35s; }
  .pcard.pf { background-color: var(--fog4); background-image: none;
    border-color: rgba(0,0,0,.06);
    box-shadow: 0 18px 50px rgba(0,0,0,.16); }
  .pcard.pf .psvg { opacity: 0; }
  .aud { position: absolute; inset: 0; z-index: 7; padding: 0 20px;
    opacity: 0; pointer-events: none; transition: opacity .35s .1s;
    display: flex; flex-direction: column; }
  .ascroll { flex: 1; min-height: 0; overflow-y: auto;
    margin: 0 -20px; padding: 0 20px 70px; }
  .ascroll::-webkit-scrollbar { display: none; }
  .aud::-webkit-scrollbar { display: none; }
  .pcard.pf .aud { opacity: 1; pointer-events: auto; }
  .aud .grp { margin: 12px 0 6px; }
  .arow { display: flex; align-items: center; justify-content: space-between;
    padding: 12px 1px; font-size: var(--fs-body); color: var(--ink);
    border-top: 0.5px solid rgba(0,0,0,.08); }
  .aud .grp > .arow:first-child { border-top: none; }
  /* 折起来的那几条:默认不出场。展开那一行本身没有发丝线——它不是一条
     材料,是一个动作（2026-08-28 用户:默认显示 3 个剩下的折叠起来）。 */
  .grp:not(.unfold) > .arow[data-fold] { display: none; }
  .arow.amore { border-top: none; padding-top: 8px; }
  /* 子图谱那一行整行可点:它是进那张图的入口 */
  .arow[data-subf] { cursor: pointer; }
  .arow[data-subf]:active { opacity: .55; }
  .arow b { font-weight: 600; font-variant-numeric: tabular-nums; color: var(--ink); }
  .asect { font-size: var(--fs-note); color: var(--ink5); padding: 14px 2px 6px; }
  .asrc { font-size: var(--fs-note); color: var(--ink5); }
  .achev { color: var(--hair); font-size: var(--fs-title); line-height: 1; }
  [data-goaudit] { cursor: pointer; }
  .apg3 { display: none; }
  .aud.a3 .apg2 { display: none; }
  .aud.a3 .apg3 { display: block; }
  .isl.h:not(.f) ~ .pcard { opacity: 1; pointer-events: auto;
    height: var(--deckh, 410px); }
  .pcard .cm { pointer-events: none; }
  .isl.h:not(.f) ~ .pcard .pface.pon .cm { pointer-events: bounding-box; }
  .hiddenB { display: none; }
  .status { color: var(--ink6); font-size: var(--fs-note); margin-top: 12px; }
  .status::after { content: ""; display: inline-block; width: 3px; height: 3px;
    background: var(--ink6); border-radius: 50%; margin-left: 6px;
    vertical-align: middle; animation: blink2 1.1s infinite; }
  @keyframes blink2 { 50% { opacity: .15; } }
  .noted { border-left: 2px solid var(--hair); padding-left: 10px; }
  .anno { background: var(--fill); border-radius: 8px; padding: 2px 6px; margin: 0 -6px; }
  .notewin { background: var(--fill); border-radius: 12px;
    padding: 9px 14px; margin: 8px 0 14px; display: none; }
  .notewin.show { display: block; }
  .notewin .nrow { display: flex; align-items: center; gap: 10px; }
  .notewin input { flex: 1; border: none; outline: none; font: inherit;
    font-size: var(--fs-body); color: var(--ink); background: transparent; }
  .notewin input::placeholder { color: var(--hair); }
  .notebox { display: none; background: var(--fill); border-radius: 12px;
    padding: 10px 14px 8px; margin: 18px 0 6px; }
  .notebox.on { display: block; }
  .nbrow { display: flex; gap: 9px; font-size: var(--fs-body); line-height: 1.7;
    color: var(--ink2); padding: 3px 0; cursor: pointer; }
  .nbrow b { color: var(--ink5); font-weight: 600; font-size: var(--fs-note); padding-top: 2px; flex: none; }
  .argdraft { display: none; position: relative; margin: 14px 0 8px;
    border: 0.5px solid rgba(0,0,0,.1); border-radius: 14px; padding: 12px 14px;
    background: var(--raised); box-shadow: 0 6px 20px rgba(0,0,0,.06); }
  .argdraft.on { display: block; }
  .adprems { margin-top: 8px; }
  .apr { display: flex; align-items: center; justify-content: space-between;
    padding: 10px 1px; font-size: var(--fs-body); color: var(--ink); cursor: pointer;
    border-top: 0.5px solid rgba(0,0,0,.08); }
  .aprev { display: none; margin: 2px 0 8px 12px; padding-left: 12px;
    border-left: 2px solid var(--fill); font-size: var(--fs-note); color: var(--ink5); line-height: 1.8; }
  .aprev.open { display: block; }

  .screen.home .democonv, .screen.fresh .democonv { display: none; }
  .screen.home .notebox, .screen.home .argdraft { display: none; }
  .screen.home .chat { display: flex; flex-direction: column; justify-content: center; }
  .homehs { display: none; }
  .screen.home .homehs { display: block; margin-bottom: 40px; }
  .hsr { display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
    padding: 12px 2px; font-size: var(--fs-body); color: var(--ink3); cursor: pointer;
    border-top: 0.5px solid rgba(0,0,0,.07); }
  .hsr:first-child { border-top: none; }
  .hsr .hsn { font-size: var(--fs-note); color: var(--ink5); white-space: nowrap; }
  .screen.home .chatin { width: 100%; }

  .adt { font-size: var(--fs-body); line-height: 1.6; font-weight: 600; color: var(--ink);
    padding-right: 36px; }
  .adm { font-size: var(--fs-note); color: var(--ink5); margin-top: 4px; }
  .adsend { position: absolute; top: 12px; right: 12px; width: 27px; height: 27px;
    border-radius: 50%; background: var(--ink); cursor: pointer;
    display: flex; align-items: center; justify-content: center; }
  .adchip { display: none; font-size: var(--fs-note); color: var(--ink3);
    border: 1px solid var(--hair); border-radius: 7px; padding: 1px 7px; margin-top: 8px; }
  .argdraft.done .adchip { display: inline-block; }
  .argdraft.done .adsend { display: none; }
  .nbact { display: flex; justify-content: flex-end; padding-top: 6px; }
  .nbsend { width: 25px; height: 25px; border-radius: 50%; background: var(--ink);
    display: inline-flex; align-items: center; justify-content: center; cursor: pointer; }
  .notewin .ndone { cursor: pointer; flex: none; display: inline-flex;
    align-items: center; padding: 4px 2px; }
  .cplus { display: none; width: 26px; height: 26px; border-radius: 50%;
    border: 1.5px solid var(--hair); align-items: center; justify-content: center;
    margin-right: 9px; cursor: pointer; background: var(--raised); flex: none; }
  .caretline.live .cplus, .caretline.hastxt .cplus { display: inline-flex; }
  .isl.h .lyrH { pointer-events: auto; }
  .lyrH svg { cursor: grab; }
  /* 显式 pointer-events 会穿透父层 none:按可见层门控 */
  .lyrF .cm, .lyrF .iv { pointer-events: none; }
  .isl.f .lyrF .cm, .isl.f .lyrF .iv { pointer-events: bounding-box; }
  .lyrH .cm { pointer-events: none; }
  .isl.h .lyrH .cm { pointer-events: bounding-box; }
  .isl.pulse { animation: bpulse 1.2s 2; }
  @keyframes bpulse { 50% { background: rgba(0,0,0,.14); } }
  .homebar { position: absolute; bottom: 8px; left: 50%; transform: translateX(-50%);
    width: 134px; height: 5px; border-radius: 3px; background: #000; z-index: 46; }

  .isl {
    position: absolute; left: 14px; width: calc(100% - 28px); bottom: 30px; z-index: 42;
    height: 410px; border-radius: 32px;
    background: var(--fog2);
    border: 0.5px solid rgba(0,0,0,.06);
    box-shadow: 0 18px 50px rgba(0,0,0,.16);
    -webkit-backdrop-filter: blur(22px) saturate(1.1);
    backdrop-filter: blur(22px) saturate(1.1);
    overflow: hidden;
    display: flex; flex-direction: column;
    padding: 0 20px 16px;
  }
  .isl.h { height: 410px; }
  .isl.f { height: 740px; }
  .isl:not(.h):not(.f).lblon { width: 190px;
    background-color: var(--isltint, transparent); }
  .isl:not(.h):not(.f) { height: 64px; width: 96px;
    background: linear-gradient(155deg, rgba(255,255,255,.50) 0%,
      rgba(255,255,255,.12) 55%, rgba(255,255,255,.30) 100%);
    -webkit-backdrop-filter: blur(28px) saturate(1.9);
    backdrop-filter: blur(28px) saturate(1.9);
    border: 1px solid rgba(255,255,255,.55);
    box-shadow: 0 18px 44px rgba(0,0,0,.17), 0 2px 6px var(--press),
      inset 0 1px 1px rgba(255,255,255,.95),
      inset 0 -8px 16px rgba(255,255,255,.30); }
  .isl:not(.h):not(.f)::before { content: ""; position: absolute; inset: 0;
    border-radius: inherit; pointer-events: none;
    background: linear-gradient(120deg, rgba(255,255,255,.6) 0%, rgba(255,255,255,0) 42%),
      radial-gradient(130% 90% at 85% 115%, rgba(255,255,255,.42), rgba(255,255,255,0) 55%); }
  .sheet { opacity: 0; pointer-events: none; transition: opacity .25s;
    display: flex; flex-direction: column; flex: 1; min-height: 0; }
  .isl.h .sheet, .isl.f .sheet { opacity: 1; pointer-events: auto; }
  .tools { position: absolute; left: 0; right: 0; bottom: 0; height: 64px;
    display: flex; align-items: center; justify-content: center;
    opacity: 1; transition: opacity .22s; }
  .isl.h .tools, .isl.f .tools { opacity: 0; pointer-events: none; }
  .tool { position: relative; cursor: pointer; display: flex; align-items: center;
    padding: 12px 10px; }
  .tGraph { gap: 7px; }
  .tglbl { display: none; font-size: var(--fs-note); color: var(--ink2);
    max-width: 118px; overflow: hidden; white-space: nowrap; }
  .tglbl.on { display: block; }
  .tglbl.fade { -webkit-mask-image: linear-gradient(to right, #000 58%, transparent 96%);
    mask-image: linear-gradient(to right, #000 58%, transparent 96%); }
  .micglyph svg { margin-top: 5px; }
  .tool .dotb { position: absolute; top: 5px; right: 1px; width: 7px; height: 7px;
    border-radius: 50%; background: var(--ink0); opacity: 0; transition: opacity .4s; }
  .isl.evt .dotb { opacity: 1; }
  .micbtn { position: absolute; right: 14px; bottom: 30px; z-index: 42;
    width: 64px; height: 64px; border-radius: 32px;
    display: flex; align-items: center; justify-content: center;
    cursor: pointer;
    overflow: hidden;
    background: linear-gradient(155deg, rgba(255,255,255,.50) 0%,
      rgba(255,255,255,.12) 55%, rgba(255,255,255,.30) 100%);
    -webkit-backdrop-filter: blur(28px) saturate(1.9);
    backdrop-filter: blur(28px) saturate(1.9);
    border: 1px solid rgba(255,255,255,.55);
    box-shadow: 0 18px 44px rgba(0,0,0,.17), 0 2px 6px var(--press),
      inset 0 1px 1px rgba(255,255,255,.95),
      inset 0 -8px 16px rgba(255,255,255,.30);
    transition: width .45s cubic-bezier(.34,1.15,.35,1), opacity .25s; }
  .micbtn::before { content: ""; position: absolute; inset: 0;
    border-radius: inherit; pointer-events: none;
    background: linear-gradient(120deg, rgba(255,255,255,.6) 0%, rgba(255,255,255,0) 42%),
      radial-gradient(130% 90% at 85% 115%, rgba(255,255,255,.42), rgba(255,255,255,0) 55%); }
  .isl.h ~ .micbtn, .isl.f ~ .micbtn { opacity: 0; pointer-events: none; }
  .micbtn.rec { width: calc(100% - 28px); justify-content: flex-start;
    padding: 0 20px 0 24px; }
  .micbtn .recbar { display: none; flex: 1; align-items: center; gap: 14px; }
  .micbtn.rec .recbar { display: flex; }
  .micbtn.rec .micglyph { display: none; }
  .wave { flex: 1; display: flex; align-items: center; gap: 3px; height: 26px; }
  .wave i { width: 3px; border-radius: 2px; background: var(--ink3); height: 8px;
    animation: wv 1s infinite ease-in-out; }
  .wave i:nth-child(3n) { animation-delay: .2s; }
  .wave i:nth-child(3n+1) { animation-delay: .45s; }
  .wave i:nth-child(4n) { animation-delay: .3s; }
  @keyframes wv { 0%,100% { height: 6px; } 50% { height: 22px; } }
  .rtime { font-size: var(--fs-note); color: var(--ink3); font-variant-numeric: tabular-nums; flex: none; }
  .rx { width: 30px; height: 30px; border-radius: 50%; flex: none; position: relative;
    border: 1.6px solid var(--ink5); transition: background .2s, border-color .2s; }
  .rx::before, .rx::after { content: ""; position: absolute; left: 50%; top: 50%;
    width: 12px; height: 1.8px; background: var(--ink5); border-radius: 1px; }
  .rx::before { transform: translate(-50%,-50%) rotate(45deg); }
  .rx::after { transform: translate(-50%,-50%) rotate(-45deg); }
  .micbtn.arm .rx { background: var(--ink); border-color: var(--ink); }
  .micbtn.arm .rx::before, .micbtn.arm .rx::after { background: #fff; }
  .micbtn.arm .wave i { opacity: .3; }
  .fsheet { position: absolute; left: 0; right: 0; top: 96px; bottom: 0; z-index: 44;
    border-radius: 20px 20px 46px 46px; background: var(--sheet);
    box-shadow: 0 -12px 40px rgba(0,0,0,.18);
    padding: 8px 14px 0; display: none; flex-direction: column; }
  .fsheet.on { display: flex; }
  .fsheet .grab2 { width: 40px; height: 5px; border-radius: 3px; flex: none;
    background: rgba(0,0,0,.18); margin: 2px auto 8px; }
  .srch { display: flex; align-items: center; gap: 7px; flex: none;
    background: var(--press); border-radius: 10px; height: 36px;
    padding: 0 10px; margin: 4px 2px 8px; font-size: var(--fs-body); color: var(--ink5); }
  .flist { flex: 1; overflow-y: auto; min-height: 0; }
  .flist::-webkit-scrollbar { display: none; }
  .fpage { display: none; }
  .fpage.on { display: block; }
  .fm { display: block; font-size: var(--fs-note); color: var(--ink5); margin-top: 2px; }
  .locrow { display: flex; align-items: center; gap: 12px; padding: 14px 4px;
    cursor: pointer; font-size: var(--fs-body); color: var(--ink);
    border-top: 0.5px solid rgba(0,0,0,.07); }
  .locrow:first-child { border-top: none; }
  .locrow .chev { margin-left: auto; font-size: var(--fs-note); color: var(--ink6); }
  .ftabs { flex: none; display: flex; justify-content: space-around;
    border-top: 0.5px solid rgba(0,0,0,.12); padding: 8px 0 26px; }
  .ftab { display: flex; flex-direction: column; align-items: center; gap: 3px;
    font-size: var(--fs-note); color: var(--ink5); cursor: pointer; padding: 0 26px; }
  .ftab.on { color: var(--ink); }
  .frow { display: flex; align-items: center; gap: 12px; padding: 14px 16px; cursor: pointer;
    font-size: var(--fs-body); color: var(--ink); border-top: 0.5px solid rgba(0,0,0,.07); }
  .frow:first-child { border-top: none; }
  .ub.file { display: flex; align-items: center; gap: 9px; }
  .gz { flex: none; padding: 10px 0 12px; cursor: pointer; }
  .grab { width: 40px; height: 5px; border-radius: 3px;
    background: rgba(0,0,0,.22); margin: 0 auto; }
  .shead { flex: none; font-weight: 600; color: var(--ink); font-size: var(--fs-title);
    letter-spacing: .2px; }
  /* 规模注（「文献库收录 N 处 · 材料 N 份」）撤掉:与环心读数同属读数,
     2026-08-18 用户一并裁掉。留着 class 是因为 JS 还在往里写文本。 */
  .scale { display: none; }
  .piscale { display: none; }
  .stage { flex: 1; position: relative; }
  .lyr { position: absolute; inset: 0;
    transition: opacity .45s .1s; pointer-events: none; }
  .lyrH { display: flex; align-items: center; justify-content: center; }
  .lyrF { opacity: 0; overflow-y: auto; }
  .lyrF::-webkit-scrollbar { display: none; }
  .isl.f .lyrH { opacity: 0; }
  .isl.f .lyrF { opacity: 1; pointer-events: auto; }

  svg text { user-select: none; }
  .lyr svg { max-width: 100%; height: auto; }
  .gsvg { display: block; margin: 0 auto; }
  .cm { transition: opacity .3s; cursor: pointer; pointer-events: bounding-box; }
  .cm.dim { opacity: .15; }
  .tie { transition: opacity .3s; }
  .tie.dim { opacity: .08; }
  .iv { transition: opacity .3s; cursor: pointer; pointer-events: bounding-box; }
  .iv.dim { opacity: .15; }

  .sect { font-size: var(--fs-note); color: var(--ink5); padding: 10px 2px 2px; }
  .arg {
    padding: 10px 3px 11px; cursor: pointer;
    transition: opacity .3s; border-top: 0.5px solid rgba(0,0,0,.09);
  }
  .arg.dim { opacity: .24; }
  .arg .txt { font-size: var(--fs-body); line-height: 1.7; color: var(--ink); }
  .arg .prov { font-size: var(--fs-note); color: var(--ink5); margin-top: 6px; }
  .seal {
    display: inline-block; color: var(--ink3);
    border: 1px solid var(--hair); border-radius: 7px;
    padding: 1px 7px; margin-right: 7px; font-size: var(--fs-note);
  }
  .prov a { color: var(--ink3); }

  .gap {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px; padding: 13px 3px; cursor: pointer;
    border-top: 0.5px solid rgba(0,0,0,.09);
    font-size: var(--fs-body); color: var(--ink2); min-height: 26px;
  }
  .gap .chev { flex: none; font-size: var(--fs-note); color: var(--ink6); }
  .selcap { min-height: 26px; font-size: var(--fs-note); color: var(--ink3);
    line-height: 1.6; padding: 7px 3px 2px; }

  /* 列表无框：发丝线分隔行，永不圆角卡片盒。原先它是一只浅灰圆角盒，
     把每一组都装成一张卡——屏上最显眼的圆角矩形就是它
     （2026-08-28 用户「不要圆角矩形」；定稿语汇里这一条本来就写着）。
     行与行之间的那道发丝线在 .arow 上，去掉盒子之后分隔关系还在。 */
  .grp { padding: 1px 0; margin: 10px 0 6px; }
  .grp.dotted { background: none; border: 1.5px dotted var(--hair); }
  .grp .arg { border-top: 0.5px solid rgba(0,0,0,.08); padding: 11px 1px 12px; }
  .grp > .arg:first-child, .grp > .gap:first-child { border-top: none; }
  .grp .gap { border-top: 0.5px solid rgba(0,0,0,.08); padding: 12px 1px; }
  .more { font-size: var(--fs-note); color: var(--ink5); }
  .xarg { display: none; }
  .xarg.on { display: block; }
  .ideabox { display: none; padding: 0 1px 8px; }
  .ideabox.on { display: block; }
  .ln {
    width: 100%; border: none; outline: none; background: none;
    font-size: var(--fs-body); color: var(--ink); padding: 10px 0 6px;
    font-family: inherit;
  }
  .ln::placeholder { color: var(--ink6); }
  .match { display: none; padding: 8px 0 2px; }
  .match.on { display: block; }
  .match .txt { font-size: var(--fs-body); line-height: 1.7; color: var(--ink); }
  .match .prov { font-size: var(--fs-note); color: var(--ink5); margin-top: 4px; padding: 0; }
  .cmt { border-top: 0.5px solid rgba(0,0,0,.09); padding: 2px 3px 6px; }
  .cmtnote { display: none; font-size: var(--fs-note); color: var(--ink3); line-height: 1.7; padding: 6px 0 2px; }
  .cmtnote.on { display: block; }

  /* 分页 */
  .pg { display: none; }
  .pg.on { display: block; }
  .backrow { font-size: var(--fs-read); line-height: 1; color: var(--ink); padding: 2px 12px 10px 2px;
    width: fit-content; cursor: pointer; }
  .argfull { font-size: var(--fs-body); line-height: 1.7; color: var(--ink); font-weight: 600; padding: 0 2px 2px; }
  .prov { font-size: var(--fs-note); color: var(--ink5); padding: 4px 2px 8px; }
  .seg { display: flex; background: var(--press); border-radius: 10px; padding: 2px; margin: 8px 0 0; }
  .sg { flex: 1; text-align: center; font-size: var(--fs-note); padding: 6px 0; border-radius: 8px;
    color: var(--ink3); cursor: pointer; }
  .sg.on { background: var(--raised); color: var(--ink); font-weight: 600; box-shadow: 0 1px 3px rgba(0,0,0,.10); }
  .lenspara { font-size: var(--fs-body); line-height: 1.7; color: var(--ink2); padding: 10px 3px 4px; min-height: 96px; }

  .scopeln { font-size: var(--fs-note); line-height: 1.7; color: var(--ink5); padding: 6px 3px 8px; }
  .gapblk { border: 1.5px dashed var(--hair); border-radius: 14px; padding: 14px 15px; margin: 6px 0; }
  .gapblk > p { font-size: var(--fs-body); line-height: 1.7; color: var(--ink); }
  .chiprow { display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0; }
  .schip { font-size: var(--fs-note); border: 1px solid var(--hair); border-radius: 7px;
    padding: 2px 8px; color: var(--ink4); background: var(--raised); }
  .gapblk .note { font-size: var(--fs-note); line-height: 1.7; color: var(--ink3); margin-top: 6px; }
  .act { display: inline-block; margin-top: 12px; background: var(--ink); color: #fff;
    border-radius: 11px; padding: 9px 15px; font-size: var(--fs-body); cursor: pointer; }
  .actnote { display: none; font-size: var(--fs-note); color: var(--ink3); margin-top: 10px; line-height: 1.7; }
  .actnote.on { display: block; }

  @keyframes soft { 0%,100% { opacity: .55; } 50% { opacity: .15; } }
  .breath { animation: soft 3s infinite; }

  /* 公地地图页:底层=世界的城市点,信号层=足迹(实心)与材料来源(空心圈)。
     两层相减就是「有人在此处理、而材料不覆盖此处」。不画国界,只有点。 */
  .cgpage { position: absolute; inset: 0; z-index: 28; background: var(--surface);
    opacity: 0; pointer-events: none; transition: opacity .3s;
    display: flex; flex-direction: column; padding: 58px 20px 0; }
  .screen.cg .cgpage { opacity: 1; pointer-events: auto; }
  .screen.cg .isl, .screen.cg .pcard, .screen.cg .micbtn {
    opacity: 0 !important; pointer-events: none !important; }
  .mapsvg { display: block; flex: none; touch-action: none; cursor: grab;
    margin: 0 -20px; width: calc(100% + 40px); height: 580px;
    background: var(--surface); }
  .mapsvg:active { cursor: grabbing; }
  .mland { fill: var(--fill); }

  .mlist { flex: 1; min-height: 0; overflow-y: auto; margin: 6px -20px 0; padding: 0 20px 40px; }
  .mlist::-webkit-scrollbar { display: none; }
  /* 世界层:几何只投影一次,缩放平移只改 transform;描边不随缩放变粗 */
  .mworld path { fill: var(--fill); stroke: var(--hair); stroke-width: .7;
    vector-effect: non-scaling-stroke; }
  /* 议题清单复用 My Builds 那套卡片 */
  .mlist .bcards { gap: 12px; padding-bottom: 8px; }
  .mcard .bthumb { width: 64px; height: 64px; border-radius: 14px;
    background: var(--fill); }
  .mcard.on { border-color: var(--ink); }
  .mcard .bname { font-size: var(--fs-title); line-height: 1.35; }
  .bplist { position: absolute; inset: 0; z-index: 28; background: var(--surface);
    opacity: 0; pointer-events: none; transition: opacity .3s;
    padding: 76px 16px 0; overflow-y: auto; }
  .bplist::-webkit-scrollbar { display: none; }
  .screen.builds:not(.bproj) .bplist { opacity: 1; pointer-events: auto; }
  .bcards { display: flex; flex-direction: column; gap: 14px; }
  .bcard { display: flex; align-items: center; gap: 16px; padding: 13px;
    background: var(--raised); border: 0.5px solid rgba(0,0,0,.07); border-radius: 24px;
    box-shadow: 0 6px 20px var(--press); cursor: pointer; }
  .bcard:active { background: var(--fill); }
  .bthumb { width: 84px; height: 84px; flex: none; border-radius: 18px;
    background: color-mix(in srgb, var(--pc, #8e8e93) 11%, var(--raised)); overflow: hidden; }
  .bname { font-size: var(--fs-title); font-weight: 600; color: var(--ink); letter-spacing: .2px; }
  .bscale { font-size: var(--fs-note); color: var(--ink5); margin-top: 4px;
    font-variant-numeric: tabular-nums; }
  .bpage { position: absolute; inset: 0; z-index: 28; background: var(--surface);
    opacity: 0; pointer-events: none; transition: opacity .3s;
    display: flex; flex-direction: column; padding: 64px 20px 0; }
  .screen.builds.bproj .bpage { opacity: 1; pointer-events: auto; }

  .screen.builds .isl, .screen.builds .pcard, .screen.builds .micbtn {
    opacity: 0 !important; pointer-events: none !important; }
  .bscroll { flex: 1; min-height: 0; overflow-y: auto;
    margin: 0 -20px; padding: 0 20px 40px; }
  .bscroll::-webkit-scrollbar { display: none; }
  .bpage .grp { margin: 2px 0 6px; }
  .bpage .grp > .arow:first-child { border-top: none; }
  .bpage .arow { gap: 14px; }
  .bpage .arow > span:first-child { flex: 1; }
  .bpage .asrc { flex: none; white-space: nowrap; }
  .bsvg { display: block; margin: 6px 0 4px; }
  .bpage .shead { padding-top: 2px; }
  .bpg3 { display: none; }
  .bpage.b3 .bpg2 { display: none; }
  .bpage.b3 .bpg3 { display: block; }
  .bplist .asect { padding-left: 6px; }
  .bplist .grp { margin: 2px 4px 6px; }
  .bplist .grp > .arow:first-child { border-top: none; }
  .bplist .docrow { padding-left: 4px; }
  .docrow { display: flex; gap: 12px; overflow-x: auto; padding: 4px 2px 8px; }
  .docrow::-webkit-scrollbar { display: none; }
  .doccard { width: 104px; height: 128px; flex: none;
    background: var(--raised); border: 0.5px solid rgba(0,0,0,.08); border-radius: 14px;
    padding: 12px 12px 10px; display: flex; flex-direction: column;
    justify-content: space-between;
    box-shadow: 0 4px 12px rgba(0,0,0,.04); }
  .doct { font-size: var(--fs-note); color: var(--ink); line-height: 1.45; }
  .doce { font-size: var(--fs-note); color: var(--ink5); }
  /* 手机现实:滑动不产生选区(鼠标横扫拉出的选区会把下一次按下变成原生拖拽) */
  .screen { -webkit-user-select: none; user-select: none; }
  .screen input { -webkit-user-select: auto; user-select: auto; }
  .chatin { -webkit-user-select: text; user-select: text; }
  .scrim { position: absolute; inset: 0; z-index: 59;
    background: rgba(0,0,0,.18); opacity: 0; pointer-events: none; transition: opacity .35s; }
  .screen.nav .scrim { opacity: 1; pointer-events: auto; }
  .drawer { position: absolute; top: 0; bottom: 0; left: 0; width: 85%; z-index: 60;
    transform: translateX(-103%); transition: transform .42s cubic-bezier(.32,1.04,.35,1);
    padding: 14px 12px 30px 14px; display: flex; flex-direction: column; }
  /* （原先这里是 .dwin ——设置那几层的玻璃浮窗,连同 .dsub/.d3/.d4 的
     「贴底」。它是 08-26 之前那条侧栏的形态,新侧栏里它压在环和栏底那行
     上面（2026-08-27 用户点名：位置是错的）。设置现在与 Projects 同族,
     就是侧栏里的一页,不再有窗这一层。） */
  .dnew { height: 52px; flex: none; border-radius: 16px;
    display: flex; align-items: center; justify-content: center; cursor: pointer; }
  .dnew:active { background: var(--press); }
  /* 开发者选项默认不出现:在「关于」里连点五次「版本」才开,再点五次关。
     这是安卓那套。开关记在本地,换会话仍在。 */
  .drow[data-d3="dev"] { display: none; }
  .screen.devon .drow[data-d3="dev"] { display: flex; }
  /* 第五次那一下给个无字的回执:行自己闪一下。
     不写「还差 N 步」——那是解释,不是回执。 */
  @keyframes devpulse { 0%,100% { background: transparent; }
    40% { background: var(--press); } }
  .drow.pulse { animation: devpulse .42s ease; }
  /* 右侧的值:与行同一基线,13px 灰。它是数据陈述,不是说明 */
  .dval { margin-left: auto; font-size: var(--fs-note); color: var(--ink5); }
  /* 令牌、地址、路径这类要等宽,不然一串点和数字会挤成一团 */
  /* 用量条:槽用发丝线色,填充用正文色。左右边距与 .drow 的内边距对齐,
     所以它读起来是那一行的延伸,不是另起一件。 */
  .dbar { height: 4px; border-radius: 2px; background: var(--hair);
    margin: 2px 10px 12px; overflow: hidden; }
  .dbar i { display: block; height: 100%; border-radius: 2px;
    background: var(--ink2); }
  /* （原先这里有一条 .dval.mono 的等宽字体。markup 与 JS 里都没有 mono
     这个类,它从来没生效过——全站第二套字体族就这么一条死规则。） */
  /* 开关:iOS 尺寸按比例收到 46×28,钮 24;点击区是整行(47px) */
  .dsw { margin-left: auto; width: 46px; height: 28px; border-radius: 14px;
    background: var(--hair); position: relative; flex: none;
    transition: background .22s ease; }
  .dsw::after { content: ""; position: absolute; top: 2px; left: 2px;
    width: 24px; height: 24px; border-radius: 12px; background: #fff;
    box-shadow: 0 1px 3px rgba(0,0,0,.18);
    transition: transform .22s cubic-bezier(.32,1.04,.35,1); }
  .dsw.on { background: var(--ink2); }
  .dsw.on::after { transform: translateX(18px); }
  /* 勾:选中项的唯一标记,不用第二种形态(色块/圆点) */
  .dchk { margin-left: auto; opacity: 0; flex: none; }
  .drow.on .dchk { opacity: 1; }
  .screen.nav .drawer { transform: none; }
  /* 设置里的一行与侧栏里的一行是同一件东西:同一条列、同样是可点的一行。
     原先设置行 17、分类行 15,同一列换个页字就变一档
     （2026-08-28 用户点名「设置里面的」）。行一律走正文档。 */
  .drow { display: flex; align-items: center; gap: 12px;
    padding: 13px var(--rail-col); font-size: var(--fs-body); color: var(--ink);
    border-radius: 12px; cursor: pointer; }
  .drow:active { background: var(--press); }
  .dback2 { width: fit-content; padding: 13px var(--rail-col); }
  .dpage { display: none; flex: 1; min-height: 0; flex-direction: column; }
  .dpage.on { display: flex; }
  .dsect { font-size: var(--fs-note); color: var(--ink5); padding: 16px 10px 5px; }
  .hrow { font-size: var(--fs-body); color: var(--ink2); padding: 9px var(--rail-col); border-radius: 10px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer; }
  .hrow:active { background: var(--press); }

  /* 环 = 粗颗粒社区层。弧长 = 这一片里有多少处,是唯一的量化通道;
     选中的那片外凸并变黑,其余退成浅灰,中心换成读数,连出去的那几片写上名字 */
  .ringsvg { display: block; touch-action: none; cursor: pointer; }
  /* 填色走 CSS 过渡(改的是呈现属性,Chrome 会过渡);几何走 rAF,d 不能过渡。
     转盘滑动时挂 .fast,把时长压到 70ms,免得手指还在转、上一段颜色还在化 */
  .ringsvg path { transition: fill .22s ease, stroke .22s ease, opacity .22s ease; }
  .ringsvg.fast path { transition-duration: .07s; }
  /* 岛头那层雾是视觉渐隐,不该收指针:它的盒子一直盖到环的十二点方向,
     实测把最上面两块变成点不着的 */
  .fog { pointer-events: none; }
  /* 抓手要按可见层门控。写成无条件 .fog .gz{auto} 的话,显式 auto 会穿透父层的
     none,把收着的审计页那只抓手激活,它正盖在卡头上,一下就把卡头的点击吃了 */
  .isl.h .fog .gz, .isl.f .fog .gz { pointer-events: auto; }
  .pcard.pf .aud .fog .gz { pointer-events: auto; }
  /* 当前那张面的规模行撤掉:同一个数字环心已经在说,而这 18px 正是环差的那点地方。
     背卡保留——那是它们唯一的信息 */
  /* 规模行前卡背卡都不出:前卡的数字环心已经在说,背卡只留一条名字。
     （原来背卡保留过,2026-08-18 用户裁掉。） */
  .pfscale { display: none; }
  /* 背卡只露一条头,它自己的环会从头底下漏出半圈弧 */
  .pface:not(.pon) .psvg { display: none; }
  /* （原先这里还给它加了 12px 外边距。这一列的顶部留白只有 --rail-top
     一个来源,分类流与设置页都从它起——多这一截,两级页面就差了一截。） */
  .rlist .arow { color: var(--ink3); }
  .rlist .arow.on { color: var(--ink); }
  .rlist .arow.on span { font-weight: 600; }

  /* ── 配色 token。浅色是原样,深色只改这一组 ────────────────────
     token 定在 .screen 上而不是 :root:七屏并排时可以一屏深一屏浅,
     外观那一页选「深色」只影响它自己那一屏。 */
  .screen {
    --surface: #fff;            /* 屏底 */
    --page: #e9e9eb;            /* 页底 */
    --glass: rgba(250,250,251,.92);  /* 磨砂窗 */
    --press: rgba(0,0,0,.05);   /* 按下 */
    --ink0: #1d1d1d; --ink2: #3a3a3c; --ink3: #48484a;
    --ink4: #6e6e73; --ink5: #8e8e93; --ink6: #aeaeb2;
    --ink: #111;                /* 正文 */
    --raised: #fff;             /* 浮起的面:卡片/页/胶囊 */
    --sheet: rgba(249,249,250,.98);
    --fog: rgba(252,252,253,.72);   /* 岛/卡的雾罩 */
    --fog2: rgba(252,252,253,.68);  /* 岛与卡组的面 */
    --fog3: rgba(250,250,252,.74);  /* 收起态的罩纱 */
    --fog4: rgba(252,252,253,.94);  /* 卡组展开态 */
    --hair: #d2d2d7;            /* 发丝线 */
    --fill: #f5f5f7;            /* 浅填充(胶囊/输入) */
  }
  .screen.dark {
    --surface: #1c1c1e;
    --page: #000;
    --glass: rgba(44,44,46,.92);
    --press: rgba(255,255,255,.08);
    --ink0: #f5f5f7; --ink2: #e5e5ea; --ink3: #d1d1d6;
    --ink4: #aeaeb2; --ink5: #8e8e93; --ink6: #636366;
    --ink: #f5f5f7;
    /* 深色下浮起的面比屏底**亮**——这是暗色里的「抬升」,
       不是把它压得更暗。上一版把卡片留成纯白,配上翻白的文字
       就成了白底白字。 */
    --raised: #2c2c2e;
    --sheet: rgba(38,38,40,.98);
    /* 岛与卡的面同样要比屏底亮——它们是浮在最上层的东西 */
    --fog: rgba(46,46,48,.72);
    --fog2: rgba(44,44,46,.70);
    --fog3: rgba(40,40,42,.74);
    --fog4: rgba(46,46,48,.94);
    --hair: #3a3a3c;
    --fill: #2c2c2e;
  }
  /* 深色下需要单独说的几处:
     ① 正文与标题在浅色里写的是 var(--ink),不在灰阶 token 里;
     ② SVG 的 stroke/fill 是呈现属性,CSS 规则优先级更高,所以
        HTML 里那 85 处与 JS 里那 103 处写死的颜色都能在这里盖掉;
     ③ 状态栏与灵动岛在深色下要反过来。 */
  .screen.dark, .screen.dark .drow, .screen.dark .shead { color: var(--ink0); }
  .screen.dark svg[stroke="#3a3a3c"], .screen.dark svg[stroke="var(--ink)"] { stroke: var(--ink2); }
  .screen.dark .dchk { stroke: var(--ink0); }
  .screen.dark .dsw { background: var(--hair); }
  .screen.dark .dsw.on { background: var(--ink2); }
  .screen.dark .dsw::after { background: #fff; }
  /* 录音的停止方块在明暗下都是亮的前景件(深色里它压在暗玻璃上)。
     显式写出来,让静态审计有依据放行,而不是靠白名单。 */
  .screen.dark .micbtn.arm .rx::before,
  .screen.dark .micbtn.arm .rx::after { background: #fff; }
  .screen.dark .island { background: #000; }
  .screen.dark .statusbar { color: var(--ink0); }
  /* ── 深色玻璃 ────────────────────────────────────────────────
     第一版把玻璃调得比屏底还暗（rgba(28,28,30,.62) 压在 #1c1c1e 上）,
     等于让它消失成一块平板。查过 Liquid Glass 的做法之后改了三处:

     ① **暗处的材质是把背后提亮,不是盖一层暗色。** iOS 的暗色材质
        = 背后内容模糊 + 一层很淡的亮色叠加,所以玻璃比屏底亮。
        这里用 brightness() 把背后抬起来,再叠一层 .16 的中性亮色。
     ② **边缘是镜片,不是描边。** 真的 Liquid Glass 在边缘折射,亮度
        沿周长变化（受光那两侧亮、背光两侧暗）,不是一圈均匀的 1px。
        用 conic-gradient 做环、mask 掏空中间,得到会转的边光。
     ③ **镜面高光单独一层、screen 混合。** 原典的做法是 feBlend
        mode="screen" 叠一张高光图;CSS 里等价物是 mix-blend-mode。

     真正的折射要 SVG 的 feDisplacementMap 当 backdrop-filter,只有
     Chromium 支持,而且背后是纯色时它什么也折射不出来——这两个钮多数
     时候浮在空白上,所以没上,留作以后接前端时再评估。 */
  .screen.dark .isl:not(.h):not(.f),
  .screen.dark .micbtn {
    /* 上一版像塑料气泡,三个原因:brightness 抬到 1.5 让它自己发光、
       高光是一大片、边光一圈都亮。玻璃在暗处主要是**看得见背后**,
       自身几乎不发光——所以底几乎全透,只留一点点抬升。 */
    background: rgba(255,255,255,.055);
    -webkit-backdrop-filter: blur(20px) saturate(1.7) brightness(1.12);
    backdrop-filter: blur(20px) saturate(1.7) brightness(1.12);
    border: none;
    box-shadow: 0 14px 34px rgba(0,0,0,.5),
      inset 0 -12px 20px rgba(0,0,0,.16);
  }
  /* ① 镜面:不是一大片光泽,是**贴着上缘的一道细弧**。
        大面积径向渐变正是塑料感的来源。 */
  .screen.dark .isl:not(.h):not(.f)::before,
  .screen.dark .micbtn::before {
    background: radial-gradient(120% 42% at 50% -14%,
      rgba(255,255,255,.20), rgba(255,255,255,0) 70%);
    mix-blend-mode: screen;
  }
  /* ② 镜片环:亮度沿周长走一圈,而且**大部分是暗的**——
        受光那一侧才亮起来。一圈都亮就成了描边。 */
  .screen.dark .isl:not(.h):not(.f)::after,
  .screen.dark .micbtn::after {
    content: ""; position: absolute; inset: 0; border-radius: inherit;
    padding: 1px; pointer-events: none;
    background: conic-gradient(from 205deg,
      rgba(255,255,255,.30) 0deg, rgba(255,255,255,.04) 62deg,
      rgba(255,255,255,.02) 150deg, rgba(255,255,255,.22) 232deg,
      rgba(255,255,255,.03) 300deg, rgba(255,255,255,.30) 360deg);
    -webkit-mask: linear-gradient(#000 0 0) content-box,
      linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite: exclude;
  }
  /* 玻璃件里的图标在暗底上要提亮。**stroke 与 fill 都要盖**——
     话筒头是 fill="#3a3a3c" 的实心块,只改 stroke 的话它会在暗玻璃上
     整个消失（真机截图里只剩支架,没有话筒头）。 */
  .screen.dark .micbtn svg, .screen.dark .tool svg { stroke: var(--ink0); }
  .screen.dark .micbtn svg [fill]:not([fill="none"]),
  .screen.dark .tool svg [fill]:not([fill="none"]) { fill: var(--ink0); }
  /* 子元素自带 stroke 属性时,presentation attribute 压过 svg 上的继承值——
     只翻 fill 会漏掉这一类(浮钮那个环就是,深色下与底几乎同色)。 */
  /* 坐在 --ink 圆上的箭头:写死 #fff 只在浅色成立,深色下 --ink 是浅的,
     白箭头落在浅底上就没了。改成跟着 --surface 走,两色都成立。
     (CSS 声明优先于 SVG 的 presentation attribute,不必改标签) */
  .csend svg, .nbsend svg, .adsend svg,
  .csend svg *, .nbsend svg *, .adsend svg * { stroke: var(--surface); }
  .act { color: var(--surface); }
  .screen.dark .micbtn svg [stroke]:not([stroke="none"]),
  .screen.dark .tool svg [stroke]:not([stroke="none"]) { stroke: var(--ink0); }
  /* 环:弧与弦的颜色由 JS 按属性设,这里整体盖过去 */
  .screen.dark .ringsvg path[fill="#3a3a3c"] { fill: var(--ink4); }
  .screen.dark .ringsvg path[fill="#1d1d1d"] { fill: var(--ink0); }
  .screen.dark .ringsvg path[fill="#d2d2d7"] { fill: var(--hair); }
  .screen.dark .ringsvg path[fill="#8e8e93"] { fill: var(--ink5); }
  .screen.dark .ringsvg text { fill: var(--ink3); }
  /* 环心读数用白色描边做光晕托底(paint-order:stroke)。深色下那圈白描边
     会把字变成「空心描边字」——截图里那种衬线感就是这么来的。
     光晕要跟着面走,不是固定白。 */
  .screen.dark .ringsvg text[stroke] { stroke: var(--fog2); }
  .screen.dark .ringsvg text[font-weight="700"] { fill: var(--ink0); }
  .screen.dark .ringsvg rect { fill: var(--fill); }

  /* ── 桌面形态 ────────────────────────────────────────────────
     同一 DOM、同一 JS。容器（.device）宽过 900px 时命中下面这块,
     否则手机形态一像素不动。JS 只写状态 class,不读宽度——
     nav / h / f / pfront 照常被切换,这里决定同一状态在宽容器下
     长什么样。产品页的壳 = 一个占满窗口的 .device。 */
  .device { container-type: inline-size; container-name: app; }
  @container app (min-width: 900px) {
    /* 桌面不是手机:机身那一圈黑边与 55 的圆角都不该在。
       屏用负外边距盖满机身的边框盒,视口就落在窗框底下而不是缩在框里面。
       只改 .device 的圆角(不改它的尺寸),容器查询不会自激。 */
    .device { border-radius: 18px; }
    .screen { margin: -10px; width: calc(100% + 20px); height: calc(100% + 20px);
      border-radius: 18px; }
    /* 手机壳件不进桌面 */
    .island, .statusbar, .homebar { display: none; }
    /* 左栏:抽屉常驻。nav 态照旧被 JS 切,这里让开合没有视觉差别;
       scrim 收掉,否则点对话区会触发 navOff 的复位 */
    .scrim { display: none; }
    .drawer { transform: none; width: 300px;
      padding: 14px 10px 14px 12px;
      border-right: 0.5px solid var(--hair); }
    /* 左栏:两个窗贴在一起、一起落在栏底,与手机端同一套关系。
       空白在顶上,不在两窗之间,也不在窗里面。 */
    .dpage.dmain { justify-content: flex-end; }
    /* 齿轮是底栏那一行的左端,输入条接到它那一端去。
       齿轮留在历史窗底不动——.dwin 带 backdrop-filter,它自己就是绝对定位的
       包含块,把齿轮抽出去只会被它接住。栏底内边距 14 时齿轮中心正好
       落在 48,与条、浮钮同一条线。 */

    /* 公地页、My Builds 列表页、项目页都是 inset:0 的整屏覆盖层,
       层级(28)在常驻左栏(60)之下——不让开左栏就会被压掉左边一截,
       字从左栏底下穿出来。三个页一起让开,内容按阅读列限宽居中。 */
    .cgpage, .bplist, .bpage { left: 300px; }
    /* 三个页的内容整体按阅读列限宽居中——只限卡片会让「文档」「思考」
       这些同级段落各走各的左缘。居中走容器的 align-items,不靠改子元素的
       margin:.bplist .grp 那类规则优先级更高,逐个去压是压不完的。
       地图是通栏的主图,单独放行。 */
    .cgpage, .bpage { align-items: center; }
    .bplist { display: flex; flex-direction: column; align-items: center; }
    .cgpage > *, .bplist > *, .bpage > * { width: 100%; max-width: 640px; }
    .cgpage > .mapsvg { max-width: none; width: calc(100% + 40px); }

    /* 中栏:对话流限宽居中,占满抽屉右侧的全部宽度。
       顶部内边距不动——磨砂是 96 高,改小了第一条就落在磨砂里被化掉。 */
    .topfade { left: 300px; }
    .chat { left: 300px; }
    .chatin { max-width: 640px; margin: 0 auto; }
    /* 输入行从对话末尾提出来,做成底栏的一条输入条。
       fixed 要认准这块屏而不是浏览器窗:给屏加 layout 包含,它就成了
       fixed 的包含块。(.device 上的 container-type 实测接不住 fixed。) */
    .screen { contain: layout; }
    /* 条一直伸到右缘,从浮钮底下穿过去:两者同高 64、同圆角 32,
       合成一件——浮钮是这条的右端,不是旁边另摆的一个钮。 */
    .caretline { position: fixed;
      left: calc(var(--side-w) + var(--content-pad));
      right: var(--content-pad); bottom: 16px;
      height: 64px; margin: 0; z-index: 44;
      padding: 0 14px 0 20px; gap: 10px;
      background: var(--fog2); border: 0.5px solid rgba(0,0,0,.06);
      border-radius: 32px; box-shadow: 0 8px 24px rgba(0,0,0,.08);
      -webkit-backdrop-filter: blur(22px) saturate(1.1);
      backdrop-filter: blur(22px) saturate(1.1); }
    .caretline .cplus { display: inline-flex; }
    .caretline .csend { position: absolute; right: 10px; top: 50%;
      transform: translateY(-50%); margin-left: 0; }
    /* 话筒进条内右端,与发送并排:条里不再套第二个玻璃钮 */
    /* 话筒贴条的右端(与条右缘齐),不留悬空的一截。
       z-index 要压过条(44):否则条的那层模糊会把话筒糊成一团淡影。 */
    .micbtn { right: 24px; bottom: 16px; z-index: 45;
      background: none; box-shadow: none; border: 0;
      -webkit-backdrop-filter: none; backdrop-filter: none; }
    .micbtn::before, .micbtn::after { display: none; }
    /* 深色那套 .screen.dark .micbtn 优先级更高,会把玻璃钮的底和高光重新加回来;
       条里只能有图标,深浅两色都一样(一物一形) */
    .screen.dark .micbtn { background: none; box-shadow: none; border: 0;
      -webkit-backdrop-filter: none; backdrop-filter: none; }
    .screen.dark .micbtn::before, .screen.dark .micbtn::after { display: none; }
    .micbtn.rec { width: min(640px, 100% - 542px); }
    /* 一有字,发送顶掉话筒:同一个位置只留一件(一物一形) */
    .screen:has(.caretline.hastxt) .micbtn { opacity: 0; pointer-events: none; }
    /* 岛一开,条与话筒一起让开(话筒本来就有这条,条跟着它走) */
    .screen:has(.isl.h) .caretline, .screen:has(.isl.f) .caretline {
      opacity: 0; pointer-events: none; }
    .screen.cg .caretline, .screen.builds .caretline { display: none; }

    /* 岛与底图卡组:与手机同一套开合(浮钮 → 半屏 + 卡组),
       这里只把横向落点从整屏挪到右缘一条 360。
       bottom / height / 前后纱这些状态规则一条不碰,所以折叠、
       前后互换、拖拽换卡在两种形态下走的是同一份行为。 */
    /* 浮钮与手机同一件:同款玻璃。与输入条同一行、坐在它左边——
       两件同高 64、同底 16,读作一排里的两件东西。
       展开的岛与底图卡组跟着它,整柱都在左缘。 */
    .isl { left: 314px; right: auto; width: 360px; bottom: 16px; z-index: 46; }
    .pcard { left: 314px; right: auto; width: 360px; }
    /* 全屏态在桌面也要是全屏:手机上 .pf / .isl.f 覆盖整块屏,桌面照搬 360
       就成了压在正文上的一条,同一个状态在两种形态下意思都不一样了。
       让它铺满抽屉右侧那一整块。选择器要压过 .isl.h:not(.f) ~ .pcard.pf。 */
    .screen .isl.h:not(.f) ~ .pcard.pf, .screen .pcard.pf,
    .screen .isl.f {
      left: 314px; right: 14px; width: auto;
      top: 82px; bottom: 16px; height: auto; }
    /* 顶上这 82px 不是留白,是收回全屏的落点——手机上卡高 740、屏高 852,
       露出的正是这一条。铺满到 top:14 会把这个动作挤没。 */
  }
  /* 可拖屏:DOM 在 strip 末尾(旧探针的屏索引不动),显示排最前 */
  .dragcol { order: -1; }
  .dragdev { position: relative; }
  .grip { position: absolute; top: 0; bottom: 0; right: -30px; width: 24px;
    cursor: ew-resize; border-radius: 12px; background: rgba(0,0,0,.06); }
  .grip:active { background: rgba(0,0,0,.12); }

  /* ══ 产品入口的壳 ══════════════════════════════════════════════════════
     mockup 里那台 393×852 的机身是比对用的展示架。产品页的壳是同一件东西
     铺满窗口——容器查询照旧按 .device 的宽度换形态（≥900 走三栏桌面），
     所以屏内一像素都不用改。 */
  html, body { height: 100%; }
  body { margin: 0; padding: 0; overflow: hidden; }
  .device { position: fixed; inset: 0; width: auto; height: auto;
    padding: 0; border-radius: 0; background: none; box-shadow: none; }
  .screen { border-radius: 0; }
  /* 桌面那块用负外边距抵机身的 10px 内边距；产品页没有那圈内边距，
     不抵消的话屏会比窗口宽出 20px。 */
  @container app (min-width: 900px) {
    .screen { margin: 0; width: 100%; height: 100%; border-radius: 0; }
  }
  /* 手机壳三件不进产品：真机上再画一个刘海、一个 9:41、一条 home 条，
     是把壳当成了内容。它们腾出的位置由 .chat 的 top:58px 自己占着。 */
  .island, .statusbar, .homebar { display: none; }

  /* ══ 登录 ══════════════════════════════════════════════════════════
     与屏内同一副玻璃、同一套墨色、同一个黑圆钮。没有欢迎句也没有说明句——
     两个占位词就是标签，一个圆钮就是动作。 */
  .screen.login { display: flex; align-items: center; justify-content: center;
    background: var(--page); }
  .lgwrap { width: 100%; max-width: 340px; padding: 0 20px; }
  .lgname { font-size: var(--fs-body); color: var(--ink5); text-align: center;
    padding-bottom: 18px; letter-spacing: .3px; }
  .lgwin { background: var(--glass); border: 0.5px solid rgba(0,0,0,.06);
    border-radius: 24px; box-shadow: 0 8px 24px var(--press);
    -webkit-backdrop-filter: blur(22px) saturate(1.1);
    backdrop-filter: blur(22px) saturate(1.1);
    padding: 2px 18px; }
  .lgrow { display: flex; align-items: center; gap: 10px;
    padding: 13px 0; border-top: 0.5px solid var(--hair); }
  .lgrow:first-child { border-top: none; }
  .lgrow input { flex: 1; min-width: 0; border: none; outline: none;
    background: none; font: inherit; font-size: var(--fs-body); color: var(--ink); }
  .lgrow input::placeholder { color: var(--hair); }
  .lggo { width: 27px; height: 27px; border-radius: 50%; background: var(--ink);
    border: none; cursor: pointer; flex: none; padding: 0;
    display: inline-flex; align-items: center; justify-content: center; }
  .lggo svg * { stroke: var(--surface); }
  .lgerr { font-size: var(--fs-note); color: var(--ink4); text-align: center; padding: 13px 0 0; }
  /* Google 登录：与账号窗同一副玻璃、同一个 24 圆角，一行一个动作 */
  .lggl { display: flex; align-items: center; justify-content: center; gap: 9px;
    margin-top: 12px; padding: 13px 0; border-radius: 24px;
    background: var(--glass); border: 0.5px solid rgba(0,0,0,.06);
    box-shadow: 0 8px 24px var(--press);
    -webkit-backdrop-filter: blur(22px) saturate(1.1);
    backdrop-filter: blur(22px) saturate(1.1);
    font-size: var(--fs-body); color: var(--ink); text-decoration: none; cursor: pointer; }
  .lggl:active { background: rgba(0,0,0,.05); }
  .lgalt { text-align: center; padding: 16px 0 0; }
  .lgalt a { font-size: var(--fs-note); color: var(--ink5); text-decoration: none;
    border-bottom: 0.5px solid var(--hair); }

  /* ══ 专栏卡（v2 原样搬回，默认关着）══════════════════════════════════
     v3 起被摘掉，这里带回来但不点亮：markup 与样式都在，
     .screen.cgon 打开它。 */
  .cgcard { display: none; }
  .screen.cgon .cgcard { display: block; }
  .dpage.dmain > .cgcard { margin: 0 0 14px; flex: none; }
  .screen.cgon .cgcard { position: relative; height: 380px; margin: 8px 8px 12px;
    border-radius: 20px; overflow: hidden; cursor: pointer;
    box-shadow: 0 12px 32px rgba(0,0,0,.16); }
  .cgimg { position: absolute; inset: 0;
    background:
      radial-gradient(70% 46% at 22% 24%, rgba(255,255,255,.30) 0%, rgba(255,255,255,0) 62%),
      radial-gradient(60% 40% at 78% 8%, rgba(255,255,255,.16) 0%, rgba(255,255,255,0) 58%),
      radial-gradient(90% 55% at 62% 96%, rgba(0,0,0,.55) 0%, rgba(0,0,0,0) 60%),
      linear-gradient(168deg, #8e8e93 0%, #6e6e73 26%, #48484a 58%, #1d1d1d 100%); }
  .cgscrim { position: absolute; inset: 0;
    background: linear-gradient(to bottom, rgba(0,0,0,.30) 0%, rgba(0,0,0,0) 26%,
      rgba(0,0,0,0) 52%, rgba(0,0,0,.62) 100%); }
  .cgmast { position: absolute; top: 14px; left: 16px; right: 16px;
    font-size: var(--fs-note); color: rgba(255,255,255,.82); }
  .cgfoot { position: absolute; left: 16px; right: 16px; bottom: 16px; }
  .cgkicker { font-size: var(--fs-note); color: rgba(255,255,255,.80); padding-bottom: 5px; }
  .cgtitle { font-size: var(--fs-read); font-weight: 700; color: #fff; line-height: 1.34; }

  /* ══ 转写中 ════════════════════════════════════════════════════════════
     mockup 里话筒只有两态（录音 / 取消预备），因为它没有后端可等。接上真
     的转写就多出第三态：松手之后到文字落进输入框之间的那一段。

     这一段做成「波形停住」，不做成假的滚动文字。国际轨那一档是松手传整
     段、一次拿全文，边说边出字这件事它根本没发生——放假文字是替系统撒
     谎，用户以为已经在出字了，其实转写还没开始。停住的波形不撒谎：条还
     在，不动了，说明在等。
     取消靶这时候收起来：任务已经发出去了，撤不回来，留一个按了没用的靶
     子是骗人。opacity 归零必配 pointer-events:none，否则透明层照吃点击。 */
  .micbtn.busy .wave i { animation-play-state: paused; opacity: .28; }
  .micbtn.busy .rx { opacity: 0; pointer-events: none; }


  /* ══ pubfront：公共领域选中时的牌堆——公地牌在顶，底图卡组退后只露一条 */
  .screen.pfront.pubfront .isl.h:not(.f) { z-index: 45;
    box-shadow: 0 18px 50px rgba(0,0,0,.16); }
  .screen.pfront.pubfront .isl.h:not(.f) ~ .pcard { z-index: 41;
    box-shadow: 0 8px 24px rgba(0,0,0,.08); }
  .screen.pfront.pubfront .isl.h:not(.f)::after { opacity: 0; }
  .screen.pfront.pubfront .pcard::after { opacity: 1; }

  /* 删除键只在这一段已经有批注时出现:没有批注时它没有可删的东西 */
  .notewin .ndel { display: none; cursor: pointer; flex: none;
    align-items: center; padding: 4px 2px; margin-right: 6px; }
  .notewin.has .ndel { display: inline-flex; }
  .hrow { display: flex; align-items: center; gap: 10px; }
  .hrow > .htxt { flex: 1; min-width: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .hact { display: inline-flex; align-items: center; justify-content: center;
    width: 30px; height: 30px; border-radius: 8px; cursor: pointer; }
  .hact:active { background: var(--press); }
  /* 设置的每一层都是侧栏里的一页,与 Projects 同一族（.dpage 的 flex:1）:
     占满这一列,分类流与环让位,栏底那行留着。原先它是贴底的绝对定位浮窗
     （08-26 之前那条侧栏的形态）,在新侧栏里正压着环和栏底那行。
     哪一页在场由 .on 说了算,这里只从它派生,不另存一份状态。 */
  .drawer:has(.dpage.on:not(.dmain)) .dpage.dmain,
  .drawer:has(.dpage.on:not(.dmain)) .dpage.dproj,
  .drawer:has(.dpage.on:not(.dmain)) .dring { display: none; }
  /* 这一列的第一行只有一条顶线:分类流与设置页共用它。
     少了这一条,设置页的第一行比分类流高 42px——同一条侧栏的两级页面
     成了两套（2026-08-27 自查抓到,是这次改成页之后新出的不一致）。 */
  .dpage:not(.dmain) { padding-top: var(--rail-top); }
  /* 栏底那行跟着走:进设置时 All/Projects 退灰,Settings 点亮。
     与 .dtab.on 同一副声明——同一族控件只有一种长相。 */
  .drawer:has(.dpage.on:not(.dmain)) .dtab[data-tab] { color: var(--ink5);
    font-weight: 400; }
  .drawer:has(.dpage.on:not(.dmain)) .dset2 { color: var(--ink);
    font-weight: 600; }

  .ans strong { font-weight: 600; color: var(--ink); }
  .chat .ans .mdh { font-weight: 600; color: var(--ink); margin-top: 22px; }
  .ans .mdh:first-child { margin-top: 0; }
  .ans .mdli { position: relative; padding-left: 20px; }
  .ans .mdli::before { content: "\2022"; position: absolute; left: 4px;
    color: var(--ink5); }
  .ans .mdli.mdol::before { content: attr(data-n); left: 0; font-size: var(--fs-note);
    font-variant-numeric: tabular-nums; }
  .chat .ans .mdli + .mdli { margin-top: 0; }
  .ans .mdq { padding-left: 12px; border-left: 2px solid var(--hair);
    color: var(--ink3); }
  .ans .mdhr { border-top: 0.5px solid var(--hair); margin: 20px 0; }

  /* 画出来的光标与镜像文字退场，位置交给真输入框 */
  .caretline .typed, .caretline .cur { display: none; }
  .caretline .ghost { position: static; flex: 1 1 auto; min-width: 0;
    width: auto; height: 28px; opacity: 1; pointer-events: auto;
    font: inherit; font-size: var(--fs-title); line-height: 1.4;
    color: var(--ink); caret-color: var(--ink);
    background: none; border: 0; padding: 0; margin: 0; outline: none; }
  .caretline .ghost::placeholder { color: var(--hair); }

  /* 条内右端两格：话筒在左、发送在右。两件都不再自带玻璃底——它们在条里，
     条本身就是那块玻璃（一物一形）。 */
  .caretline .micbtn { position: static; flex: none;
    margin: 0 8px 0 auto; width: 27px; height: 27px;
    right: auto; bottom: auto; z-index: auto;
    background: none; border: 0; box-shadow: none;
    -webkit-backdrop-filter: none; backdrop-filter: none;
    opacity: 1; pointer-events: auto; }
  .caretline .micbtn::before, .caretline .micbtn::after { display: none; }
  .screen.dark .caretline .micbtn { background: none; border: 0; box-shadow: none;
    -webkit-backdrop-filter: none; backdrop-filter: none; }
  .screen.dark .caretline .micbtn::before,
  .screen.dark .caretline .micbtn::after { display: none; }
  /* 话筒：盒子要装得下它的字形,否则顶上被自己裁掉（2026-08-25 用户截图）。
     字形居中由 flex 定,不再靠 margin 顶。 */
  .caretline .micbtn { width: 24px; height: 24px; overflow: visible;
    display: inline-flex; align-items: center; justify-content: center; }
  .caretline .micbtn .micglyph { display: inline-flex; align-items: center;
    justify-content: center; line-height: 0; }
  .caretline .micbtn .micglyph svg { margin-top: 0; display: block;
    width: 16px; height: 20px; }
  /* 有字的时候两个键并排,不再互相顶掉 */
  .screen:has(.caretline.hastxt) .caretline .micbtn { opacity: 1; pointer-events: auto; }
  .caretline .csend { position: static; right: auto; top: auto;
    transform: none; margin-left: 0; }
  /* 录音：这一整条就是录音条,输入框与另两件让位 */
  .caretline:has(.micbtn.rec) .ghost,
  .caretline:has(.micbtn.rec) .cplus,
  .caretline:has(.micbtn.rec) .csend { display: none; }
  .caretline .micbtn.rec { flex: 1 1 auto; width: auto; margin: 0;
    justify-content: flex-start; }

  /* 一条里的东西按 GPT 的比例排：加号(裸字形) — 输入 — 档位 · 话筒 · 发送。
     四件同一条中线，右端三件等距。 */
  .caretline .cplus { border: 0; background: none; width: 22px; height: 22px;
    margin-right: 12px; }
  .caretline .cplus svg { width: 15px; height: 15px; }
  .caretline .micbtn { margin: 0 14px 0 0; width: 22px; height: 22px; }
  .caretline .csend { width: 32px; height: 32px; }
  .caretline .csend svg { width: 14px; height: 15px; }
  /* 档位：一行字加一个折角,与 GPT 那颗一样是「看得见的当前值」而不是按钮。
     字号与它点开后那份菜单的行(.trow)同一档——「Chat」这个词在按钮上和
     菜单里是同一个东西,不该有两个大小(2026-08-28 自查抓到:并档时把它
     并成了 13,菜单里还是 15)。 */
  .tierbtn { display: inline-flex; align-items: center; gap: 5px; flex: none;
    margin: 0 14px 0 auto; padding: 4px 2px; cursor: pointer;
    font-size: var(--fs-body); color: var(--ink4); white-space: nowrap; }
  .tierbtn:active { opacity: .6; }
  .tierbtn .tchev { flex: none; margin-top: 1px; }
  .caretline .micbtn { margin-left: 0; }
  .tiermenu { display: none; position: absolute; right: 12px; bottom: calc(100% + 10px);
    z-index: 60; min-width: 168px; padding: 6px; border-radius: 16px;
    background: var(--raised); border: 0.5px solid rgba(0,0,0,.08);
    box-shadow: 0 12px 32px rgba(0,0,0,.14); }
  .caretline.tieropen .tiermenu { display: block; }
  .trow { display: flex; align-items: center; justify-content: space-between;
    padding: 10px 12px; border-radius: 11px; font-size: var(--fs-body); color: var(--ink2);
    cursor: pointer; }
  .trow:active { background: var(--press); }
  .trow.on { color: var(--ink); font-weight: 600; }
  .trow.on::after { content: "\2713"; font-size: var(--fs-note); color: var(--ink); }

  /* 新建底图：与卡同宽、贴在卡组下缘的一张虚线卡。平时只有一个加号,
     点了就地变成一行名字输入——名字是这张图的第一件事,不该藏在弹窗里。 */
  .pnew { position: absolute; left: 10px; right: 10px; top: 0; height: 39px;
    z-index: 80;   /* 压过牌（牌的 z 到 59）:它钉在卡组底边上,牌从底下滚过 */
    display: flex; align-items: center; justify-content: center;
    gap: 10px; padding: 0 16px; border-radius: 18px; cursor: pointer;
    color: var(--ink5); background: var(--fill);
    border: 1px dashed var(--hair); }
  .pnew:active { background: var(--press); }
  .pnewin { display: none; flex: 1; min-width: 0; border: 0; outline: none;
    background: none; font: inherit; font-size: var(--fs-body); color: var(--ink);
    caret-color: var(--ink); }
  .pnew.on { justify-content: flex-start; cursor: default;
    border-style: solid; background: var(--raised); }
  .pnew.on .pnewin { display: block; }
  .pnew.on .pnewplus { width: 16px; height: 16px; }
  .pnew .pnewplus { width: 20px; height: 20px; }
  .pnew.busy { opacity: .5; pointer-events: none; }
  /* 牌面尺寸照 mockup 一分不动：卡片整体多留 45 的一条给「新建」那张,
     卡组同步下缩同样多,牌算高度用的那份可用高度因此不变（410−74 = 455−119）。
     牌多了不挤牌面,多出来的靠卡组自己滚。 */
  /* 「新建」那个加号后来收进了标题行,卡组不用再给它留一条——留着就是
     卡片下面空出一大截「下巴」（2026-08-25 用户点名）。 */
  .pdeck { overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; }
  .pdeck::-webkit-scrollbar { display: none; }
  .pspacer { position: absolute; left: 0; right: 0; top: 0; height: 1px;
    pointer-events: none; }
  /* 卡组不在前时这张卡跟着退场（岛收着时整张卡片区都是收起来的） */
  .screen:not(.pfront) .pnew { opacity: 0; pointer-events: none; }

  /* ── 面板：上下固定排列,不再互相遮挡 ─────────────────────────────
     选中与否看标题前那个圈,两个可以同时选中(问题就同时问这两处)。 */
  .pick { position: absolute; z-index: 6; cursor: pointer; color: var(--ink6);
    display: inline-flex; line-height: 0; }
  .pick .pkchk { opacity: 0; }
  .pick.on { color: var(--ink); }
  .pick.on circle { fill: var(--ink); stroke: var(--ink); }
  .pick.on .pkchk { opacity: 1; }
  .isl .pick { opacity: 0; pointer-events: none; }
  .screen.pfront .isl.h .pick { opacity: 1; pointer-events: auto; }
  .screen.pfront .pnew .pnewplus { width: 20px; height: 20px; }
  /* 两张卡的位置写死：上 公共领域 / 下 我的底图 / 底 设置 */
  /* 贴底排列：从下往上 设置 → 我的底图 → 公共领域。空档留在上面(那儿是
     对话),不是留在脚下（2026-08-25 用户点名：又对齐上部了）。 */
  /* 「我的底图」那张卡按 mockup 的牌高定死：标题 46 + 一张大牌 256 + 两条
     露头 78 + 内距 ≈ 392。牌矮了环就跟着小（半径按牌的短边算）,所以这张
     卡的高度不能随屏比例缩（2026-08-25 用户：环的大小不对）。
     公共领域占它上面剩下的那一块。 */
  .screen.pfront .isl.h:not(.f) { left: 14px; right: 14px; width: auto;
    top: 60px; bottom: 580px; height: auto; z-index: 41;
    box-shadow: 0 8px 24px rgba(0,0,0,.08); }
  .screen.pfront .isl.h:not(.f) ~ .pcard { left: 14px; right: 14px; width: auto;
    top: auto; bottom: 156px; height: 412px; z-index: 41;
    opacity: 1; pointer-events: auto;
    box-shadow: 0 8px 24px rgba(0,0,0,.08); }
  .screen.pfront .isl.h:not(.f)::after,
  .screen.pfront .pcard::after { opacity: 0; }
  /* （原先这里是 .setfrm——飘在对话那一列上的圆角矩形「设置」。侧栏底行
     已经有 Settings,两个入口是同一件事的两条路,2026-08-27 用户下令删掉。） */
  /* （原先这里有一段给子页标题逐处补内距的规则——那是手搓补偿,
     已废。标题列现在只有一条,由第 19 步的父类派生。） */
  /* （setonly 时让分类流退场的那一条已废：设置成了页,分类流由上面那条
     :has() 一并让位,不必再单写一份。） */
  /* 「我的底图」那一行的三件（勾选圈 · 标题 · 加号）落在同一条中线上。
     原来各按各的 top 定位,肉眼就是一个高一个低（2026-08-25 用户点名）。 */
  /* 一行里的东西排在同一条 flex 线上——对齐由布局保证,不靠数字凑
     （2026-08-25：连着两轮用绝对定位对不齐,改结构） */
  .hdln { display: flex; align-items: center; gap: var(--hdln-gap);
    min-height: 30px; }
  .hdln .pick { position: static; flex: none; }
  .hdln .pihead, .hdln .shead { padding: 0; margin: 0; line-height: 1.25;
    flex: 1; min-width: 0; }
  .hdln .pnew svg { flex: none; width: 20px; height: 20px; }
  .hdln .pnew { position: static; flex: none; margin-left: auto;
    width: 30px; height: 30px; top: auto; left: auto; right: auto;
    border: 0; background: none; }
  .hdln .pnew.on { flex: 1 1 auto; width: auto; height: 32px;
    background: var(--raised); border: 1px solid var(--hair); padding: 0 12px; }
  .screen.pfront .pcard .pfog { padding: 12px 12px 8px 16px; }
  /* 桌面：面板不铺满整窗——它是右边那一栏（与胶囊、卡组原来所在的那一栏
     同一条），底下还要给输入条让开一条（2026-08-25 实测漏检：只在手机宽
     试过,桌面下三块直接横穿整屏）。 */
  @container app (min-width: 900px) {
    /* 桌面：它是胶囊那一列上方的一条窄栏(360),不是横穿整栏的大块 */
    .screen.pfront .isl.h:not(.f) { left: 314px; right: auto; width: 360px;
      top: 28px; bottom: 580px; height: auto; }
    .screen.pfront .isl.h:not(.f) ~ .pcard { left: 314px; right: auto;
      width: 360px; top: auto; bottom: 156px; height: 412px; }
  }

  /* 面板开着时那颗胶囊也留在原位（不只是输入条）：它是开关,再点一次收起。
     它长在岛里,而岛此刻是面板顶上那张卡,所以把它单独钉回胶囊该在的位置,
     样子照收起态那件玻璃（2026-08-25 用户）。 */
  .screen.pfront > .tools { opacity: 1; pointer-events: auto;
    position: absolute; left: 14px; bottom: 30px; width: 190px; height: 64px;
    border-radius: 32px; z-index: 46;
    background: var(--fog2); border: 0.5px solid rgba(0,0,0,.06);
    box-shadow: 0 8px 24px rgba(0,0,0,.10);
    -webkit-backdrop-filter: blur(22px) saturate(1.1);
    backdrop-filter: blur(22px) saturate(1.1); }
  /* 桌面：胶囊回到它那一列的左下角。写在手机那条之后——容器查询不加
     权重,谁后写谁赢（2026-08-25 实测被后写的手机规则盖掉过一次）。 */
  @container app (min-width: 900px) {
    .screen.pfront > .tools { left: 314px; bottom: 16px; }
  }
  .screen.pfront:has(.isl.h) .caretline { opacity: 1; pointer-events: auto; }
  .screen.pfront:has(.isl.h) .caretline .micbtn { opacity: 1; pointer-events: auto; }

  /* 面板里的一张卡（父类）：外形、内距、标题行的规格都在这里定一次。
     子类只负责各自装什么,不许各自定长相。 */
  .screen.pfront .pnl { left: 14px; right: 14px; width: auto;
    border-radius: 28px; z-index: 41;
    background: var(--fog2); border: 0.5px solid rgba(0,0,0,.06);
    box-shadow: 0 8px 24px rgba(0,0,0,.08);
    -webkit-backdrop-filter: blur(22px) saturate(1.1);
    backdrop-filter: blur(22px) saturate(1.1); }
  /* 标题行：两张卡同一套（勾选圈 20 + 间隙 8 + 标题 17/600），
     左内距 16、上下各 12。 */
  /* 页头（父类）：带圈的标题行与不带圈的裸标题共一副盒子——同高、
     同上下内距、文字同一条中线。上下级切换时字不许位移
     （2026-08-25 第四次点名：子页抓手竖着占位,把标题往下顶了 12px）。 */
  .screen.pfront .pnl .hdln,
  .screen.pfront .pnl .fog > .shead { padding: 12px 12px 8px var(--pnl-pad);
    min-height: 46px; display: flex; align-items: center; }
  .screen.pfront .pnl .pihead,
  .screen.pfront .pnl .shead { font-size: var(--fs-title); font-weight: 600;
    color: var(--ink); letter-spacing: .2px; }
  /* 岛的标题外面还包着一层 fog：把它的内距归零,规格交给 .hdln */
  .screen.pfront .isl.h .fog { padding-left: 0; padding-right: 0; }
  .screen.pfront .isl.h .hdln { padding-left: var(--pnl-pad); }
  .screen.pfront .pcard .pfog { padding: 0; background: none; }

  /* 组件尺度的单一来源：图标按钮的盒子、行内间隙、卡片内距。凡是要与它们
     对齐的地方一律从这里派生,不许再各自写数字（2026-08-25 用户下令）。 */
  .screen { --gbtn-size: 28px; --hdln-gap: 8px; --pnl-pad: 16px; }
  /* 图标按钮（父类）：盒子、居中、按压反馈一处定 */
  .gbtn { display: inline-flex; align-items: center; justify-content: center;
    flex: none; width: var(--gbtn-size); height: var(--gbtn-size);
    padding: 0; border: 0;
    background: none; border-radius: 50%; cursor: pointer; }
  .gbtn:active { opacity: .55; }
  .gbtn svg { flex: none; display: block; }
  .gbtn-solid { background: var(--ink); }
  .gbtn-solid svg { stroke: #fff; }
  /* 大号：盒与图形一起翻倍。viewBox 不动,所以描边粗细跟着一起放大,
     形状与小号逐字同一个,只是大一倍。 */
  .gbtn-lg { width: var(--gbtn-size-lg); height: var(--gbtn-size-lg); }
  .gbtn-lg svg { width: var(--gbtn-glyph-lg); height: var(--gbtn-glyph-lg); }

  /* 父类定尺寸之后,各自只保留「非尺寸」的差别 */
  .caretline .cplus.gbtn, .caretline .micbtn.gbtn { width: 28px; height: 28px; }
  .caretline .csend.gbtn { width: 32px; height: 32px; }
  .hdln .pick.gbtn, .hdln .pnew.gbtn { width: 28px; height: 28px; }
  .hdln .pnew.gbtn.on { width: auto; border-radius: 16px; }

  /* 面板态整张卡都是入口（与点牌面进底图同一套动作）;
     环这时只是装饰,不收点击——它盖在标题上,收了就把入口吃掉 */
  .screen.pfront .isl.h:not(.f) { cursor: pointer; }
  .screen.pfront .isl.h:not(.f) .hsvg { pointer-events: none; }
  /* 标题列：整套面板只有这一条竖线,由父类三个尺寸派生。
     面板态的标题被「圈+间隙」顶到这条线;没有圈的标题由父类补同一个值;
     牌面上的名字隔着牌堆的缩进,从同一组数里减回来——改任何一个尺寸,
     所有标题一起动,不存在「各自的左缘」。 */
  .screen { --ttl-col: calc(var(--pnl-pad) + var(--gbtn-size) + var(--hdln-gap));
    --deck-inset: 10px; }
  .pdeck { left: var(--deck-inset); right: var(--deck-inset); }
  .screen.pfront .pnl .fog { padding-left: 0; padding-right: 0; }
  .screen.pfront .pnl .fog > .shead { padding-left: var(--ttl-col); }
  .screen.pfront .pnl .pfhead {
    padding-left: calc(var(--ttl-col) - var(--deck-inset)); }
  /* 展开的页面里勾选圈退场;那一格由返回键接住(第 22 步),
     标题因此不用任何补偿就落在同一条线上 */
  /* 抓手=能拖。面板态两张卡都是固定的,一律无线;展开成页面才回来 */
  .screen.pfront .pnl:not(.f):not(.pf) .gz { display: none; }
  /* 抓手是覆在页头上的把手,不占行高——有它没它,标题都在同一个位置。
     它是顶部居中的一条,不是全宽的毯子:铺满整行会把住在圈那一格的
     返回键盖死(2026-08-25 实测:elementFromPoint 全落在 gz 上)。 */
  .screen.pfront .pnl .fog .gz { position: absolute; left: 50%;
    transform: translateX(-50%); width: 120px; top: 0; z-index: 3; }

  /* 卡的运动规格（父类）：一份清单管两张卡。曲线一个、时长一套,
     位置四个方向都在清单里——面板开合时两张卡同一种滑法,不存在
     「一个滑一个跳」。 */
  .screen { --pnl-ease: cubic-bezier(.34,1.15,.35,1); --pnl-fade: .3s; }
  .pnl { transition: height .55s var(--pnl-ease), width .55s var(--pnl-ease),
    top .5s var(--pnl-ease), bottom .5s var(--pnl-ease),
    left .5s var(--pnl-ease), right .5s var(--pnl-ease),
    background .45s, box-shadow .45s, opacity var(--pnl-fade); }
  /* 内容层淡入淡出共用一个时长 */
  .sheet { transition: opacity var(--pnl-fade); }
  .pdeck { transition: opacity var(--pnl-fade); }
  .pcard .psvg { transition: opacity var(--pnl-fade); }
  /* 上面那套运动只属于面板态。总图谱态里这张卡不是浮着的面板,是侧栏里的
     一页（与 Projects 同族）,换页就该一帧换完。带着面板的运动清单进来的
     话,它会从面板那一列(x=314)一路滑到侧栏,半透明的牌组横穿对话区,底下
     的侧栏又已经空掉——就是用户 2026-08-27 说的那一帧。
     进和出的另一头都是非 pfront 态,所以按 :not(.pfront) 一次说完,
     不靠 JS 在切换那一瞬挂个临时类。 */
  .screen:not(.pfront) .pcard,
  .screen:not(.pfront) .pcard .aud,
  .screen:not(.pfront) .pcard .pdeck,
  .screen:not(.pfront) .pcard .psvg { transition: none; }

  .fog, .pfog {
    background: none;
    -webkit-backdrop-filter: none; backdrop-filter: none;
    -webkit-mask-image: none; mask-image: none; }
  .fog::before, .pfog::before { content: ""; position: absolute; inset: 0;
    z-index: -1; pointer-events: none;
    background: linear-gradient(to bottom, var(--fog) 40%, rgba(252,252,253,0));
    -webkit-backdrop-filter: blur(12px) saturate(1.3);
    backdrop-filter: blur(12px) saturate(1.3);
    -webkit-mask-image: linear-gradient(to bottom, #000 55%, transparent);
    mask-image: linear-gradient(to bottom, #000 55%, transparent); }
  .pfog::before {
    background: linear-gradient(to bottom, var(--fog), rgba(252,252,253,0));
    -webkit-mask-image: linear-gradient(to bottom, #000 45%, transparent);
    mask-image: linear-gradient(to bottom, #000 45%, transparent); }
  /* 面板态的卡头下面没有内容钻过,雾整个不要 */
  .screen.pfront .pcard .pfog::before { display: none; }
  .screen.pfront .isl.h:not(.f) .fog::before { display: none; }

  /* 返回键:与勾选圈同族(.gbtn)同槽位。面板态显圈,展开态显返回。
     fog 整层是 pointer-events:none(mockup 只放行了 gz),返回键要
     自己显式放行,否则点击穿透到卡身上 */
  .aback { display: none; color: var(--ink); }
  .pcard .aud .aback { display: inline-flex; }
  .screen.pfront .pnl.f .hdln .pick { display: none; }
  .screen.pfront .pnl.f .hdln .aback { display: inline-flex; }
  /* 放行按可见态门控——无条件 auto 会穿透父层的 none,把收着的 aud 里
     那只隐形返回键激活,正盖在勾选圈上(mockup 对 gz 的注释早警告过) */
  .pcard.pf .aud .aback { pointer-events: auto; }
  .screen.pfront .isl.f .hdln .aback { pointer-events: auto; }
  /* 桌面:全屏态回到侧栏右侧那一块。mockup 本来写的就是 left:314,
     是 .pnl 的 left:14 以同特异性后来居上压掉了它——把被压掉的补回来,
     否则公地页头钻进常驻侧栏底下,返回键被侧栏盖死 */
  @container app (min-width: 900px) {
    .screen.pfront .pnl.f { left: 314px; right: 14px; width: auto; }
  }

  /* 底图次级页右侧的图谱面板。渲染归 PPGraph(8300 原物),这里只给容器。 */
  .gpane { position: absolute; left: 690px; right: 16px; top: 28px;
    bottom: 96px; z-index: 40; display: none; border-radius: 28px;
    overflow: hidden; background: #fbfbfb;
    border: 0.5px solid rgba(0,0,0,.06); }
  .gpane .cy { position: absolute; inset: 0; touch-action: none;
    overscroll-behavior: contain; }
  /* 计数角标照 8300 的样子,但在壳里它不是控件(热度弹窗没搬),不收点击 */
  .gpane .gbadge { position: absolute; right: 14px; bottom: 12px; z-index: 5;
    font-size: var(--fs-note); color: var(--ink5); background: rgba(255,255,255,.86);
    border: 1px solid rgba(0,0,0,.1); border-radius: 999px; padding: 3px 10px;
    pointer-events: none; white-space: nowrap; }
  .gpane .gbadge:empty { display: none; }
  @container app (min-width: 900px) {
    .screen.gon .gpane { display: block; }
    /* 总图谱铺满侧栏右边那一整块 */
    .screen.gall .gpane {
      left: calc(var(--side-w) + var(--content-pad)); right: var(--content-pad);
      top: var(--content-pad); bottom: 96px; }

    /* ── 图谱页 / project 页：图谱占满，对话由输入条唤起 ──────────────
       （2026-08-30 用户拍板）。此前对话是铺满整屏的一层（实测 300–1440
       ×0–900），图谱只是浮在它上面的一张卡（316–1424×16–804），所以卡
       下缘那条 16px 的缝、输入条以下那 16px、以及左右两侧全在漏日常对话
       的正文——用户截图里看到的就是这个。
       对话不是被拿掉了：openProject 已经把这条会话切到 project:<名字>，
       升起来的就是这个 project 自己的对话。
       输入条长在 .chat 里面（.chat > .chatin > .caretline）,所以这里不能
       用 display:none —— 那会把输入条一起端掉（实测输入条矩形变成 0,0,
       整条唤起就无从谈起）。visibility 可以被子元素覆盖回来。 */
    .screen.gall .chat, .screen.pj .chat { visibility: hidden; }
    .screen.gall .chat .caretline,
    .screen.pj .chat .caretline { visibility: visible; }
    .screen.gall.cvon .chat, .screen.pj.cvon .chat {
      visibility: visible;
      left: calc(var(--side-w) + var(--content-pad));
      right: var(--content-pad);
      top: auto; bottom: 96px; height: 52%;
      /* 父类那份 116/170 的留白是给整屏一层用的,升起来这一层自己说 */
      padding: 22px 24px 18px;
      background: var(--surface);
      border-radius: 28px;
      border: 0.5px solid rgba(0,0,0,.06);
      box-shadow: 0 18px 50px rgba(0,0,0,.16);
      /* 图谱 40 < 这一层 43 < 输入条 44：盖住图谱,但不盖输入条 */
      z-index: 43; }
    /* 那道顶部磨砂是给整屏一层做的,升起来这一层有自己的上缘 */
    .screen.gall .topfade, .screen.pj .topfade { display: none; }
  }

  .screen.pfront:has(.pcard.pf) .isl { display: none; }
  @container app (min-width: 900px) {
    .screen.pfront .isl.h:not(.f) ~ .pcard.pf {
      top: 28px; bottom: 96px; height: auto; }
  }

  .screen.pfront.mhome .isl { display: none; }
  .screen.pfront.mhome .isl.h:not(.f) ~ .pcard { top: 86px; bottom: 76px;
    height: auto; }
  @container app (min-width: 900px) {
    .screen.pfront.mhome .isl.h:not(.f) ~ .pcard { top: 28px; bottom: 96px;
      height: auto; }
  }

  /* 入场:面板里的两张卡同一支动画——几何先就位,一起淡入升起。
     没有「一张从胶囊长出来、一张从零冒出来」这回事,那是两种入场 */
  @keyframes pnlin { from { opacity: 0; transform: translateY(12px); } }
  .screen.pin .pnl { transition: none;
    animation: pnlin .5s var(--pnl-ease) both; }

  /* ── 侧栏底部:环 + All|Projects（2026-08-27 用户批注）───────────────── */
  /* 侧栏宽度只有一个来源:抽屉与「总图谱态」的子页共用它 */
  .screen { --side-w: 85%; }
  /* 右边那一栏的边距也只有一个来源。原先图谱面板写 left:316/right:16、
     输入条写 left:518/right:14、子页标题上内距是 0——三条线各走各的,
     输入条因此看着偏右（518 是当年按「公共领域胶囊宽 190」算的,那颗胶囊
     08-27 已经取消,数留着参照物没了）。现在同一个 token 管三处:
     图谱面板的左右、输入条的左右、子页标题的上边线（2026-08-29 用户:
     居中 / 页边距要和所有页面统一）。 */
  .screen { --content-pad: 16px; }
  /* 侧栏里只有一条左缘:分类行、环那一行、栏底档位行都从它派生,
     不许再各写各的数字。 */
  /* 图标按钮的盒比它画的那个图形大一圈（.gbtn 28 的盒装 18 的图形）。
     要让图形与文字落在同一条列线上,得把这一圈让回去——让多少由父类的
     两个尺寸算出来,不写补偿数字。 */
  .screen { --rail-col: 10px; --rail-top: 34px; --gbtn-glyph: 18px;
    --gbtn-inset: calc((var(--gbtn-size) - var(--gbtn-glyph)) / 2);
    /* 大号图标按钮＝父类的一个尺寸变体（与 .gbtn-solid 平级,不另起一族）。
       两倍就是两倍,由 --gbtn-size / --gbtn-glyph 算出来,不写第二个数
       （2026-08-28 用户:那个圆加大 100%）。 */
    --gbtn-size-lg: calc(var(--gbtn-size) * 2);
    --gbtn-glyph-lg: calc(var(--gbtn-glyph) * 2);
    --gbtn-inset-lg: calc((var(--gbtn-size-lg) - var(--gbtn-glyph-lg)) / 2); }
  /* 栏底那一组（环那一行 + 档位行）里,由**第一个出场的**那件把空白吃掉,
     整组就贴在栏底。auto 只能有一个:两个 auto 会平分空白,中间裂开一条。
     哪些态谁出场——主页 dmain 是 flex:1 顶着;设置/Projects 是那一页
     flex:1 顶着;总图谱态一个页都没有,就靠环这一条 auto 撑住。 */
  .dring { flex: none; margin-top: auto; padding: 4px 0 2px; }
  .dhr { height: 1px; background: var(--hair); margin: 2px var(--rail-col); }
  /* 左端是那枚大圆,紧挨着是 ＋,箭头推到右端。两侧的内距各自从「谁站在
     这一头」算:左边站的是大号(inset-lg)、右边是小号(inset),两个图形的
     边这才都落在同一条列线上——盒比图形大一圈,不让回去就对不齐。 */
  .dringhead { display: flex; align-items: center; gap: 4px;
    min-height: var(--gbtn-size-lg);
    padding-left: calc(var(--rail-col) - var(--gbtn-inset-lg));
    padding-right: calc(var(--rail-col) - var(--gbtn-inset)); }
  .dgo { color: var(--ink3); margin-left: auto; }
  .rtog { color: var(--ink3); }
  /* 圆右边那枚 ＋：停在这一块上才出现,点它选文件,直接进总图谱。
     opacity:0 必配 pointer-events:none（三·命中：透明层吃点击）。 */
  .rplus { color: var(--ink3); opacity: 0; pointer-events: none;
    transition: opacity .18s; }
  .dring:hover .rplus, .rplus:focus-visible { opacity: 1; pointer-events: auto; }
  /* 送件途中:留在场上、不收点击,自己脉动。做完就停——脉动停下来本身
     就是回执,不必再写一句话。 */
  .rplus.ing { opacity: 1; pointer-events: none;
    animation: rplusing 1.1s ease-in-out infinite; }
  @keyframes rplusing { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }
  .dsvg { display: block; width: 100%; height: auto; }
  /* 环默认收成那枚小圆,坐在栏底 All 的正上方、与它同一条左缘;
     点小圆就地展开（2026-08-27 用户）。箭头是环上的动作,环收着时
     它不在场——收起态整块只有一枚圆。 */
  .dring:not(.on) .dsvg, .dring:not(.on) .dgo { display: none; }
  /* 这一行的字号在行上定一次,分隔符跟着继承。原先只有 .dtab 写了字号,
     中间那根 | 没写、继承到浏览器默认的 16px——同一行三件字不一样大,
     就是用户 2026-08-28 发来的那张截图。 */
  .dtabs { flex: none; display: flex; align-items: center; gap: 9px;
    padding: 10px var(--rail-col) 2px; font-size: var(--fs-note); }
  /* 字号归这一族自己,不靠父容器继承——这一族在别处(Settings 那一行)
     也要用,继承来的字号跟不过去(2026-08-27 实测 16 对 13) */
  /* 文字按钮的父类：一件按钮就是一行字,没有底、没有边、没有圆角矩形
     （2026-08-28 用户）。选中＝变黑加粗,此外什么都不变。
     ★加粗会把字撑宽,后面的东西跟着挪一两个像素——那就是用户说的「闪动」
     （实测 Projects 49.0→52.1、中间那根 | 左右挪 1.2px）。所以宽度按
     **最粗那一份**预留:同样的字先用 600 撑出格子（不显形、不占额外高度、
     不吃点击）,真正显示的那一份叠在它上面。切换时只有颜色与字重在变,
     几何一动不动。 */
  .dtab { display: inline-grid; cursor: pointer; color: var(--ink5);
    font-size: var(--fs-note); background: none; border: 0; padding: 0; }
  .dtab .dtl, .dtab::before { grid-area: 1 / 1; }
  .dtab::before { content: attr(data-lbl); font-weight: 600;
    visibility: hidden; pointer-events: none; }
  .dtab.on { color: var(--ink); font-weight: 600; }
  .dtab:active { opacity: .55; }
  .dtabsep { color: var(--hair); }
  .dempty { color: var(--ink5); font-size: var(--fs-note); padding: 18px 10px; }
  /* 两档互斥:All=对话流+环,Projects=空的那一页 */
  .screen.proj .dpage.dmain, .screen.proj .dring { display: none; }
  .dpage.dproj { display: none; }
  .screen.proj .dpage.dproj { display: flex; }
  /* 公共领域入口取消（2026-08-27 用户）:代码全留,只是不出场。
     胶囊与它收起态的那颗岛一起退场——留着就是屏幕右下一个空壳。 */
  .tools { display: none; }
  .isl { display: none; }

  /* 总图谱态:左栏整个变成「全部」的子页,底下那行档位仍留着 */
  .screen.gall .dpage.dmain { display: none; }
  /* 环那一行留在场上:进来点的是那枚箭头,出去点的也得是它。原先整块环
     在这一态 display:none,箭头自己就没了,人只能跑到左栏顶上点那个 ‹
     ——从这儿进、从那儿出（2026-08-28 用户圈图）。
     环本身收起来（子页顶上已经有一张同样的环,不必画两遍）,只留 ⭕ 与
     ➡ 这一行,并且盖在子页之上,否则点不着。 */
  .screen.gall .dsvg { display: none; }
  .screen.gall .dring { position: relative; z-index: 62; }
  /* 子页让出栏底那一块的高度。这个数只有一个来源,而且闸门逐宽度断言
     「子页的下缘正好接上环那一行的上缘」——不许靠肉眼凑。 */
  .screen { --foot-h: 112px; }
  .screen.gall .pcard {
    left: 0; right: auto; top: 0; bottom: var(--foot-h); width: var(--side-w);
    height: auto; border-radius: 0; z-index: 61;
    opacity: 1; pointer-events: auto;
    background: var(--fog2); border: 0; box-shadow: none; }
  .screen.gall .pcard::after { opacity: 0; }
  /* 子页标题与右边那块面板共一条上边线：原先 fog 的上内距是 0,标题贴着
     窗口顶边（用户 2026-08-29：标题太偏上,页边距要和所有页面统一）。
     标题两行的名字原先还会顶出卡外（实测字顶 -2）。
     ★标题行改成上对齐：原先是垂直居中,一行的名字与两行的名字第一行落点
     差 4px（18 / 14）——标题法要求标题相对卡框的偏移一字不差。圈与小字
     用「一行字高与图标盒高之差的一半」提上去,与第一行共中线,这个数从
     字阶与 --gbtn-size 算出来,不是凑的。 */
  .screen.gall .pcard .aud .hdln,
  .screen.pj .pcard .aud .hdln { align-items: flex-start; }
  .screen.gall .pcard .aud .hdln > .gbtn,
  .screen.pj .pcard .aud .hdln > .gbtn,
  .screen.pj .pcard .aud .hdln > .pjarch {
    margin-top: calc((var(--fs-title) * 1.25 - var(--gbtn-size)) / 2); }
  .screen.gall .pcard .aud { padding: var(--content-pad) 12px 0; }
  /* 桌面的侧栏宽度写在最后:容器查询不加特异性,后来者胜——
     写在前面会被上面那句 85% 压掉(2026-08-25 已栽过一次) */
  @container app (min-width: 900px) { .screen { --side-w: 300px; } }
/* RAIL-PROTECT start：聊天类那一栏的样式，构建只搬运 */
  /* 聊天类那一栏的样式（另一个会话的代码，构建只搬运，不改写）*/
  .rlist { flex: 1; min-height: 0; overflow-y: auto; padding-top: var(--rail-top); }
  .rlist::-webkit-scrollbar { display: none; }
  .rgap { height: 22px; }
  .dnew { display: flex; align-items: center; padding: 9px 10px;
    border-radius: 10px; cursor: pointer;
    opacity: 0; pointer-events: none; transition: opacity .18s; }
  .drawer:hover .dnew, .dnew:focus-visible { opacity: 1; pointer-events: auto; }
  .dpage.dmain { justify-content: flex-start; }
  .hacts { flex: none; display: flex; opacity: 0; pointer-events: none;
    transition: opacity .15s; }
  .hacts:focus-within { opacity: 1; pointer-events: auto; }
  .hrow.fixed .hacts { display: none; }
  .dmain.naming .hrow:not(.naming) { opacity: .45; }
  .hrow .htxt[contenteditable] { cursor: text; outline: 0;
    min-height: 22px; overflow-x: auto; text-overflow: clip;
    scrollbar-width: none; }
  .hrow .htxt[contenteditable]::-webkit-scrollbar { display: none; }
  .rmenu { display: none; position: absolute; z-index: 60; min-width: 148px;
    padding: 6px; border-radius: 16px; background: var(--raised);
    border: 0.5px solid rgba(0,0,0,.08); box-shadow: 0 12px 32px rgba(0,0,0,.14); }
  .rmenu.on { display: block; }
/* RAIL-PROTECT end */

  /* （.dproj 原来要单写一条 position:relative,是为了顶掉那条把所有非 dmain
     页压成贴底浮窗的规则。那条规则没了,这条也就不用了。） */

  /* 设置:与档位同一行、靠右端(2026-08-27 用户批注:箭头落在那一行右边)。
     同一族(.dtab),不另造一套文字控件 */
  .dset2 { margin-left: auto; }
  /* 贴栏底这件事由上面那一组的第一件（.dring）负责,这里不能再来一个
     auto——两个 auto 平分空白,环与档位行之间会裂开一条。 */
  .dtabs { position: relative; z-index: 5; }
  .screen.gall .dtabs { display: flex; }


  /* ══ Projects ══════════════════════════════════════════════════════
     来源：demos/projects_2026-08-28.html 的「NEW」一段 +
     design/projects_ui_handoff_2026-08-28.md。逻辑与接口照它的，
     样式一律走我们现在这一套（字阶 token、无框列表、文字按钮、父类）。

     两处有意偏离交接文档，都是「父类已有就必须用」：
       ① 他新造的 .lst（无框列表）＝我们的 .grp——08-28 已经把 .grp 的
          浅灰圆角盒去掉了，两者逐条等价，不再多一族。
       ② 他的 .arow 内距改写成 var(--sect-col)；我们保留这个「内容只有
          一条左缘」的做法，但取值对齐现有几何，不整体挪一次位。 */

  /* 内容那一列只有一条左缘：节标题、行首图标、行里的字、文件格、版本行、
     环上那条线全从这里取。原先 .asect 是 2px、.arow 是 1px，差 1px。 */
  .screen { --sect-col: 2px; --ico-col: 26px; }
  .aud .asect, .bpage .asect { padding: 14px var(--sect-col) 6px; }
  .aud .arow, .bpage .arow { padding: 12px var(--sect-col); gap: 10px; }
  .aud .chiprow { padding: 0 var(--sect-col); }

  /* 文档＝文件图标铺开的一格格，图谱＝一列行。两节形态必须一眼分得开：
     上面是手上的稿子，下面是这个项目调的图，不是一类东西。
     纸片 58×74（folder v5 规格），格子左对齐、按栏宽自然换行。 */
  .fgrid { display: flex; flex-wrap: wrap; align-items: flex-start;
    gap: 16px 18px; padding: 8px var(--sect-col) 14px; }
  .fcell { width: 58px; position: relative; cursor: pointer; }
  .fcell .fpaper { display: block; }
  .fcell:active .fpaper { opacity: .6; }
  .fname { margin-top: 7px; font-size: var(--fs-note); line-height: 1.35;
    color: var(--ink); word-break: break-all; }
  .fver { font-size: var(--fs-note); color: var(--ink5);
    font-variant-numeric: tabular-nums; }
  /* 一份"上传成功"却检索不到的文件是这个柜子最要命的失败,所以段落层为空
     且有报错时纸片自己说出来（数据陈述,不是解释） */
  .fcell.fbad .fver { color: #d70015; }
  /* ··· 贴在纸片右上角,和行尾那颗是同一族、同一个字形 */
  .fcell .fmore { position: absolute; top: -2px; right: -6px;
    width: 22px; height: 22px; }
  .fadd .fname, .fadd .fver { display: none; }

  /* 行首那一格图标。图谱的圈＝主界面标记选中底图的那个
     （.pface.pon .pfhead::after）：10×10、2px 描边、吃同一条 --pc 色板。 */
  .arow .ico { flex: none; width: var(--ico-col); display: inline-flex;
    align-items: center; color: var(--ink5); }
  .arow .iring::before { content: ""; width: 10px; height: 10px;
    border: 2px solid var(--pc, var(--ink5)); border-radius: 50%; }
  /* 主图＝填实的那个。哪张图是这个 project 写进去的,靠形说,不靠字。 */
  .arow .iring.iprim::before { background: var(--pc, var(--ink5)); }
  /* 行内：图标 · 名字（撑开）· 版本号/› · ···。名字不撑开的话右端那几件
     会跟着名字长短左右浮动,同一列就对不上一条线（交接文档坑 5）。 */
  .aud .grp .arow > span:not(.ico):not(.aver):not(.achev):not(.hact) {
    flex: 1; min-width: 0; overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap; }
  .arow .aver { font-size: var(--fs-note); color: var(--ink5);
    font-variant-numeric: tabular-nums; }
  /* 可点的那几种行要有指针:点得中而看不出能点,等于没有入口 */
  .arow[data-map], .arow[data-arch] { cursor: pointer; }
  .arow[data-map]:active { opacity: .55; }
  /* 加一行：加文档、加图各在自己那一节,永不共用一个加号。形制沿用 .arow,
     只把实线换成虚线;加号字形与侧栏新建（.dnew）是同一个。 */
  .aud .grp > .arow.aadd { color: var(--ink5); cursor: pointer;
    border-top: 0.5px dashed rgba(0,0,0,.16); }
  .arow.aadd:active { opacity: .55; }
  /* 名单里有、盘上没了的图 —— 不许静默丢掉：作用域悄悄变小的话,回答会
     变薄而没人说得出为什么（交接文档 2.3）。 */
  .arow.amiss > span { color: var(--ink6); text-decoration: line-through; }

  /* 版本行。模型改的那一版带一个 °,人写的是默认态、不加东西——记号系统里
     ° 一直是「模型自己出的」。 */
  .vrow { display: flex; align-items: baseline; gap: 10px;
    padding: 12px var(--sect-col); font-size: var(--fs-body); color: var(--ink);
    border-top: 0.5px solid rgba(0,0,0,.09); cursor: pointer; }
  .aud .grp > .vrow:first-child { border-top: none; }
  .vrow .vnum { flex: 1; font-variant-numeric: tabular-nums; font-weight: 600; }
  .vrow .vwhen { font-size: var(--fs-note); color: var(--ink5);
    font-variant-numeric: tabular-nums; }
  .vrow .mk { margin-left: 3px; }
  .vrow.von .vnum { color: var(--ink); }
  .vrow:not(.von) .vnum { font-weight: 400; }

  /* 行尾永远是 ···,不是删除键——摆一颗删除在手边太容易误触。产品的分类行
     就是这么做的（shell_wire 的 topicRow）,全站只有这一种行尾动作件。
     显形跟着整张卡走,不逐行——逐行亮灭会让整列跟着鼠标跳。 */
  .arow .hact, .fcell .hact { opacity: 0; pointer-events: none;
    transition: opacity .15s; }
  .arow .hact { width: 22px; height: 22px; color: var(--ink5); }
  .screen.pj .pcard:hover .aud .arow .hact,
  .screen.pj .pcard:hover .aud .fcell .hact { opacity: 1; pointer-events: auto; }
  /* 未归类那一栏在总图谱态,不在 project 态——上面那条只管 .screen.pj,
     所以它的 ··· 一直是 pointer-events:none,点了没反应（2026-08-31 实测）。
     同一个手法、同一个条件（卡上悬停），只是换一个态。 */
  .screen.gall .pcard:hover .aud .gunf .arow .hact {
    opacity: 1; pointer-events: auto; }
  /* 取回是归档页每一行唯一的动作,所以常在——藏进悬停等于把这一页的用途
     藏起来。三个类是为了压得住上面那条 .arow .hact。 */
  .apg6 .arow .hback { opacity: 1; pointer-events: auto;
    width: auto; flex: none; font-size: var(--fs-note); color: var(--ink3); }

  /* project 页：左栏换成这个 project 的子页,右边仍是对话——人是在这儿说话、
     在这儿写,图谱是需要时才调出来的东西（用户 08-28）。 */
  .screen.pj .dpage.dmain, .screen.pj .dpage.dproj,
  .screen.pj .drawer .dring { display: none; }
  .screen.pj .pcard {
    left: 0; right: auto; top: 0; bottom: 42px; width: var(--side-w);
    height: auto; border-radius: 0; z-index: 61;
    opacity: 1; pointer-events: auto;
    background: var(--fog2); border: 0; box-shadow: none; }
  .screen.pj .pcard::after { opacity: 0; }
  .screen.pj .pcard .aud { padding: var(--content-pad) 12px 0;
    opacity: 1; pointer-events: auto; }
  /* .fog 整层 pointer-events:none（它是背景材质）,子元素跟着继承。坐在
     .hdln 里的可点件都要显式放行——交接文档说这个坑他一轮踩了三次。 */
  .screen.pj .pcard .aud .aback { pointer-events: auto; }
  /* 抓手法：抓手线只在能拖的页面出现。总图谱页与 project 页的左栏是固定
     的一列,拖不动,那条线不该在——而且它带 data-abk,点下去会把屏塞进
     pfront 那个旧面板态（2026-08-28 走查抓到:探针点「返回」时命中的是它,
     顺手把状态弄脏了）。 */
  .screen.pj .pcard .aud .gz,
  .screen.gall .pcard .aud .gz { display: none; }
  /* 栏底那一行要真的贴在栏底,而且要按得到。
     「贴栏底」本来是靠环顶的（见上面 .dtabs 那条注释:「贴栏底这件事由
     上面那一组的第一件（.dring）负责」）,可 project 态把环藏了,没人顶,
     整行就浮到侧栏顶上；.pcard 的 z-index 61 又盖过它的 5——于是 All /
     Projects / Settings 看得见、按不到,进了 project 就再也回不去
     （2026-08-31 用户;实测这一行从 y=1035 窜到 y=14,命中 False,
     挡着它的是 svg 和 .aud.a4）。
     .screen.pj .pcard 本来就写着 bottom:42px,那 42px 就是留给这一行的。 */
  .screen.pj .dtabs { display: flex; margin-top: auto; z-index: 62; }

  /* 图谱：进 project 就铺开它的图（2026-08-28 用户：这一页也要接回原来那个
     图谱 render）。位置与总图谱态逐字一样——同一块地方,同一个渲染。
     画哪一张由 PP.pjGraphName 定：点开了哪一张就哪一张,没点过就是主图。 */
  @container app (min-width: 900px) {
    .screen.pj .gpane { display: block;
      left: calc(var(--side-w) + var(--content-pad)); right: var(--content-pad);
      top: var(--content-pad); bottom: 96px; }
  }

  /* 卡里的分页：project 页 / 一份文档的版本 / 一张图谱 / 归档。
     与审计页第二层（.aud.a3）同一个手法：同一张卡里换页,不新开层。
     加一页就要加进这张表,漏一个那一页永远打不开（交接文档坑 9）。 */
  .apg4, .apg5, .apg6 { display: none; }
  .aud.a4 .apg2, .aud.a4 .apg3, .aud.a4 .apg5, .aud.a4 .apg6 { display: none; }
  .aud.a4 .apg4 { display: block; }
  .aud.a5 .apg2, .aud.a5 .apg3, .aud.a5 .apg4, .aud.a5 .apg6 { display: none; }
  .aud.a5 .apg5 { display: block; }
  .aud.a6 .apg2, .aud.a6 .apg3, .aud.a6 .apg4, .aud.a6 .apg5 { display: none; }
  .aud.a6 .apg6 { display: block; }

  /* 这个 project 的环。位置关系照 All 那一层：一条分隔线、右上一个箭头、
     底下是环本体。它在子页里,产品那一个在 drawer 里,互不干涉。
     两条隐藏规则都要压住（交接文档坑 2、3）：`.screen.proj .dring` 管的是
     侧栏那个环,同样两个类也会命中这一个;环本身只在 .dring.on 时才画。
     选择器写到三个类——两个类时实测压不住 shell.css 里那条。 */
  .screen.pj .pcard .dring { display: block; margin-top: 0;
    padding: 6px 0 10px; }
  /* 没数据就整块退场。只把环藏掉的话会剩一条孤零零的分隔线和一枚指向
     空处的箭头——那是没有内容的墨（2026-08-28 截图自审抓到）。 */
  .screen.pj .pcard .dring:not(.on) { display: none; }
  .pcard .dring .dhr { margin: 6px var(--sect-col) 2px; }
  .pcard .dringhead { padding-left: calc(var(--sect-col) - var(--gbtn-inset));
    padding-right: calc(var(--sect-col) - var(--gbtn-inset));
    min-height: var(--gbtn-size); }
  .pcard .dring .dsvg { pointer-events: none; }

  /* 归档是这一页的次要入口,所以是一枚小字,不是一整行卡片。与设置同一族
     （.dtab）,落在标题行右端那个槽位（.hdln .pnew 的先例）,悬停才现。
     它也坐在 .fog 里,同样要显式放行。 */
  .hdln .pjarch { margin-left: auto; flex: none; cursor: pointer;
    opacity: 0; pointer-events: none; transition: opacity .18s; }
  .screen.pj .pcard:hover .aud .hdln .pjarch {
    opacity: 1; pointer-events: auto; }
  .aud.a6 .hdln .pjarch { display: none; }   /* 已经在归档页里了 */

  /* project 那一份 ··· 菜单挂在屏这一层：搁在 drawer 里会被它的 z-index
     封顶,盖不到 z-index 更高的子页上——画得出来但点不着（交接文档坑 4）。
     样式与侧栏那份同族（.rmenu）,不另造。 */
  .screen > .rmenu { z-index: 70; }
  /* Projects 那一档的「新建」常在。`.dnew` 的默认是 `opacity:0 +
     pointer-events:none`（只有悬停侧栏才现）——那是聊天类那一栏的做法，
     那里已经有一条流，加号是次要的；Projects 这一档**建 project 是唯一
     的入口**，藏起来等于没有。这一条是 mockup 的 NEW 段里就写着的
     （`.dpage.dproj .dnew`），我接线时漏搬了，用户实测「新建 project 的
     ＋ 不 work」有一半是它（2026-08-30）。 */
  .dpage.dproj .dnew { opacity: 1; pointer-events: auto; }
  /* 起名时其余行变淡——与分类那一栏同一个反馈。原规则写死在 `.dmain` 上
     （在 RAIL-PROTECT 区里，那是另一会话的代码，只搬运不改写），所以这里
     按「页」写一条通用的：一个行为一处定义，两个列表共用。 */
  .dpage.naming .hrow:not(.naming) { opacity: .45; }

  /* ── 图谱页：书目/文件的编辑态（2026-08-30 用户）──────────────────
     段标题右端可以挂一枚动作。只有带 .withact 的那一行才排成一行——
     .asect 全站都在用，不能整族改布局。 */
  .asect.withact { display: flex; align-items: center;
    justify-content: space-between; }
  .asect .sedit { color: var(--ink5); }
  .aud.dedit .asect .sedit { color: var(--ink); font-weight: 600; }
  /* 行尾那枚 × 只在编辑态出场。**不是悬停才现**：这一下是不可逆的，
     不能让它跟别的悬停控件一样随手就撞上。它是 .gbtn 那一族，尺寸随族，
     这里只说颜色（红＝不可逆）与出场条件。 */
  .arow .gbtn.dx { display: none; }
  .aud.dedit .grp .arow .gbtn.dx {
    display: inline-flex; color: #d70015; }
  .gbtn.dx[data-busy] { opacity: .35; pointer-events: none; }
  /* 收起来的那一节只在编辑态出场；「取回」是这一节每行唯一的动作,所以
     常在——藏进悬停等于把这一节的用处藏了（与 .apg6 归档页同一条理）。 */
  .asect.sarch, .grp.gar { display: none; }
  .aud.dedit .asect.sarch { display: block; }
  .aud.dedit .grp.gar { display: block; }
  /* 空的时候不用这里管：renderScope 那段通用的空节处理会给两者钉上行内
     display:none（行内压过这里）,非空时它写回空串,才轮到上面两条。 */
  .grp.gar .arow .hback { opacity: 1; pointer-events: auto;
    width: auto; flex: none; font-size: var(--fs-note); }
  /* 未归类文献只在总图谱那一态出场：别的态下「归没归进 project」这个问题
     没有意义（那时候左栏说的是某一张图,不是总库）。空的时候由 renderScope
     那段通用的空节处理藏掉。 */
  .asect.sunf, .grp.gunf { display: none; }
  .screen.gall .asect.sunf, .screen.gall .grp.gunf { display: block; }
  /* 这几件并进已有的族之后，只保留各自「非族」的差别（父类定尺寸,子类
     定形态）——2026-08-30 用户下令：同样的元素必须复用父类。
     .dnew 是一整行不是一颗圆钮,所以尺寸由它自己说；发送与对勾沿用族的
     尺寸,只是位置不同。 */
  .dnew.gbtn { width: auto; height: 52px; border-radius: 16px; }
  .adsend.gbtn, .nbsend.gbtn { width: 28px; height: 28px; }
  .ndone.gbtn { width: auto; height: auto; background: none; }
