/**
 * 心愿墙的样式表。
 *
 * 分层和 deals 那份报告一样，是固定的两段：
 *   一、共用骨架  令牌、重置、.wrap / .ico / .label / .n / .chip / .empty
 *   二、这块墙     封面（和它顶上那两颗浮着的图标）、愿望卡、发布面板、回执、页脚
 *
 * 「风格」在这里就是下面这一组令牌。**几何整套是照抄 deals 的**（直角、1px 发丝线、
 * 10px / 0.12em 的小字标签），纸和墨是这站自己的。
 *
 * **底色这一栏翻过三趟，账记在这儿免得再翻第四遍**（要动颜色，先看完这一段）：
 *   2026-10-08 上午  黑纸白字（纸 #0b0f13、字 #e9eef3、红 #ff3b45）
 *   2026-10-08 晚上  翻回白纸（用户那句「整个 UI 都向微信靠拢」把它定了 ——
 *                    朋友圈是白底，而这份皮本来就是照着那份白纸报告誊的，
 *                    翻回去之后两边连纸都是同一张）
 *   **2026-10-09       翻成深色**（用户：「整个网站默认用暗色主题，就和微信的暗色主题一样」）
 * 前两趟翻的只是「谁亮谁暗」；这一趟换的是**整套明暗关系** —— 令牌那一组重新配过、
 * 每个数都重新量过（见 :root 里每个数后面的比值）。因为深色的世界里灰阶不等距：
 * 同样的亮度差压在深底上看着比白底上小，白纸那一组照搬过来会糊成一片。
 * 纸取微信暗色那一张（#191919），不是上一趟那个更黑的 #0b0f13。
 *
 * **这份文件里凡是写着「白纸」「白底」「在白纸上 X:1」的句子，都是写它那一刻的账**
 * —— 那些比值当时确实是在白纸上量的，按「那时候的纸」读；这些判断今天的现值都在 :root 里。
 *
 * 四条规矩，改版面时别破：
 *   · **没有圆角**，只留两处例外（都是微信身上长出来的）：头像 4px 圆角方框、
 *     相机那一层那两块卡片 12px 圆角；
 *   · **没有蓝色** —— 微信的名字和链接是蓝的（#576b95），这站不跟：用户
 *     2026-10-06 明确过「我的网站不希望出现蓝色」，那一条没被后面的话撤掉。
 *     微信的味道从灰阶、留白、那个灰块和那颗绿按钮上来，不从色相上来；
 *   · 红色是**强调**，不是配色。它只出现在「已实现」和「有回执待处理」两处，
 *     别拿去做 hover、边框、装饰；
 *   · 绿（#07c160，微信那个绿）只给**主按钮** —— 一次点击里最该按的那一颗。
 *     全站就这一处用绿，别的任何地方都不许引它。
 *
 * 和报告的两处**故意不同**：
 *   · 正文基准字号 12 → 13px。报告是商品网格，一屏扫价格，越小越密越好；
 *     这里是 140 个字的散文，12px 读起来费劲。小字标签仍是 10px（那一条是签名）。
 *   · 灰那一档整体比报告**深一档**：报告的白量（#616161 / #8a8f96 / #b3bac3 /
 *     #d3d8de）是给商品元数据配的，那里的灰字只是标签；这站的灰字常常是
 *     **一整句说明**（清单注意事项、上传提示、封面那句），报告最浅那一档
 *     #b3bac3 在白纸上只有 1.96:1，手机上读不动。所以取
 *     #616161 / #737c85 / #8a8f96（6.2 / 4.2 / 3.3:1，都是量出来的）。
 *     2026-10-09 翻成深色时，**这三个比值原样搬了过去**（6.17 / 4.21 / 3.30）——
 *     梯子没重配，换的只是纸。
 */

/* ======================== 一、共用骨架 ======================== */

:root {
  /* 声明成深色（2026-10-09，跟着上面那一趟翻）。这一句管的是**原生零件**：
     滚动条、输入框那个调整角、自动填充的底色，还有 number/date 那种控件。
     翻成深色之后它比原来要紧得多 —— 不写它，那些零件会是浅色的，
     压在一张深纸上比原来白纸上扎眼得多（原来只是「不太搭」）。
     **不是跟着系统的 prefers-color-scheme 走**：这个站没有第二套皮，
     跟着系统就等于有一半人看到的东西我们自己没看过。 */
  color-scheme: dark;

  /* 底下这一组受一条规矩管：**每一档落在深纸上的比值，和它在白纸上的比值对得上**。
     梯子整个搬过来，不是另配一套灰 —— 所以每个数后面跟两个比值，
     前面是现在（深纸 #191919）量出来的，后面是白纸那一版原来的。
     两个值都写着，是为了下一次翻的时候能一眼看出「哪一档掉过、掉了多少」。 */
  --paper: #191919;        /* 纸 —— 页面底（微信暗色那张纸就是这个值）*/
  --card: #2c2c2c;         /* 浮在暗底上的**卡片**：相机那一层那两块 + 「取消」那一块。
                              白天它们是白纸卡片压在暗底上，这一版反过来 —— 比纸亮一格的卡。
                              它和 --surface 撞成同一个值是巧合也不全是：两处都是
                              「比纸亮一格」，只是一处是浮层、一处是纸上的灰块，
                              两件事就留着两个名字（哪天其中一个要动，不用先拆耦合）*/
  --block: #333333;        /* 深色块：toast、两个点弹出来的那一片。白天那两处写的是
                              半透明的黑 rgba(0,0,0,.8/.78) —— 黑压在一张深纸上等于没铺，
                              这一版换成实心的灰块（顺带把那两个 .78 和一个 .8 收成一个数）*/
  --ink: #ededed;          /* 字（深纸上 15.0:1）。白纸那一版是 #000f17（19.4:1）。
                              19.4 够不着 —— 纸一暗，天花板就压下来了：纯白也只有 17.6:1。
                              取微信那个 #ededed，15.0:1 */
  --ink-2: #999999;        /* 次级正文：引言、说明句（6.17:1 / 白纸 6.19:1）*/
  --ink-3: #7c7c7c;        /* 三级：时间、提示、要读清的灰字（4.21:1 / 4.24:1）*/
  --ink-4: #6b6b6b;        /* 小字标签那一档（3.30:1 / 3.26:1）—— 全站最小的一档字 */
  --ink-faint: #363636;    /* 极弱：只给分隔符和禁用态（1.45:1 / 1.43:1，不当字读）*/
  --rule: #2e2e2e;         /* 发丝线（对着纸 1.29:1；白纸上那一条是 1.37:1）*/
  --rule-soft: #262626;    /* 更弱的一道（卡片内部分层用，1.16:1 / 1.13:1） */
  --surface: #2c2c2c;      /* 离纸面一格。**朋友圈底下那一块就是这个灰**。
                              白天它比纸**暗**一格（#f7f7f7 对 #fff），
                              这一版比纸**亮**一格：方向反了，「离纸面一格」这件事没反
                              （离纸 1.26:1，白天是 1.07:1 —— 深底上同样的差看着更小，
                              所以这一步比白天那一步实）*/
  --surface-2: #242424;    /* 只挪一点点：清单悬停、输入框底（离纸 1.13:1，白天是 1.04:1）*/
  --field-focus: #2a2a2a;  /* 输入框聚焦时再亮一格。白纸上那一版是「回到纸色」（#fff）——
                              深纸上回不去，纸是这一组里最暗的，聚焦往**亮**里走 */
  --on-solid: #fff;        /* 压在**实心深块**上的字和图标。用它的有八处，都在
                              绿按钮、红点数、封面那张近黑的画、两个点那一片、
                              图上那把叉这些地方 —— 这些底不分白天黑夜都是深的，
                              所以这一档两趟一样白。**它是这一组里唯一不用翻的值，
                              翻的时候也别动它**（白图标落到白纸上就没了，见 .float 那段）*/
  --red: #fa5151;          /* 强调用的红（深纸上 5.33:1）。白纸那一版是 #e20c18（4.9:1），
                              上一趟黑纸用的是 #ff3b45；这一版取微信那个红 */
  --red-wash: rgba(250, 81, 81, .16); /* 闪一下的淡红（刚提交的那条回执、被点到的那条）。
                              白天那层 .1 落在白纸上是 1.18:1；同一个 .1 压在深纸上只剩
                              1.12:1 —— 比白天那一步还弱，所以抬到 .16（1.21:1）*/
  --green: #07c160;        /* 微信那个绿。全站只有主按钮用它，别处不许引。
                              白字压在它上面是 **2.4:1** —— 不高，但这是微信自己那一对，
                              这一趟翻色没让它变差，也没打算在这一点上另立一套。
                              （README 版面那一节原来把这个数记成 3.0:1，重算过，两处都改了）*/

  /* 三档内衬，和 deals 一致（40 → 24 → 8） */
  --pad: 40px;
  /* （原来这里还有一个 `--nav-gap`：报头那一行距屏幕顶的那 44px。2026-10-08
     晚上报头整条撤了、封面顶到屏幕最上，这个数就没有落在哪儿了 —— 在用的地方
     一并删掉，留着它只会让人以为哪儿还欠着一段留白） */

  --sans: "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Hiragino Sans GB",
    "Microsoft YaHei", sans-serif;
  /* 数字走价格字体栈（DIN Alternate）—— 站上的天数、条数都用它，和 deals 同一个感觉 */
  --num: "DIN Alternate", "DIN Condensed", "Roboto Condensed", "Helvetica Neue", Helvetica, Arial,
    sans-serif;
}

@media (max-width: 1180px) {
  :root { --pad: 24px; }
}
@media (max-width: 760px) {
  :root { --pad: 8px; }
}

* {
  box-sizing: border-box;
  /* 手机上点一下会有一块灰色高亮，和这张干净的纸很不搭。写在 * 上，不指望继承 */
  -webkit-tap-highlight-color: transparent;
}

html, body { margin: 0; padding: 0; }
html { overflow-anchor: none; }

/* 这面墙的人基本都在手机上，所以字号由我们说了算。iOS Safari 横过来时会把
   **块里**的字自动放大一点（它以为那是正文该有的样子）—— 一放大，卡上那些
   量好的尺寸就全不对了。这一条是关掉那个自动放大，不是设字号。
   窄屏那段里那条 16px 管的是另一件事：输入框聚焦时不许整页缩放 */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

/**
 * 顶上那一条 —— 时间和灵动岛那一栏（2026-10-09）。
 *
 * 用户报的是「最上面有一部份是白色的，就是时间和灵动岛那一栏，有没有办法把这部份变黑」。
 * 根子不在我们写的颜色上：**iOS 26 的 Safari 不再理会 `theme-color`** ——
 * caniuse 上 ios_saf 26.0–27.2 那一格是「支持，但这个颜色哪儿都没用上」
 * （桌面 Safari 更窄：只有装成 web app 才认）。那条玻璃底下露出来的是**它背后那一层**：
 * 页面没铺到屏幕最上沿时，露出来的是画布（body 的底色）—— 这就是用户看见的那一条；
 * 铺到最上沿，露出来的就是封面那张画（近黑）—— 也就是用户要的黑。
 * （写这一段的时候纸是白的，所以他要的是「变黑」；2026-10-09 纸自己就是深色的了，
 *  这一条不用再管：铺不铺满，露出来的都是深色。铺满那一条留着，理由是下面那些安全区）
 *
 * 铺满靠 `viewport-fit=cover`（写在 index.html 的 viewport 那一条上）。代价只有一个方向：
 * **内容从屏幕最上沿开始**，于是顶上不是画的页面要各自让开那条安全区。全站 fixed / sticky
 * 的一共五处，一处一处看过：
 *   .floats      本来就带 env(safe-area-inset-top)（那颗相机当初就照这一条写的；在没有
 *                cover 的时候它一直算成 0 —— 到这一笔才真的不是 0）
 *   .write__bar  补上了（顶栏不加就被灵动岛压住，取消/发表点不着）
 *   .msgs        补上了（消息那一页顶上直接是标题，没有封面替它让位）
 *   .sheet       不用补（justify-content: flex-end，两块卡片在下面，底下那条内衬本来就有）
 *   .toast       底边补了 env(safe-area-inset-bottom)（免得掉到那条浮起来的地址栏底下）
 * 墙上和我那一页不用补：那两页顶上就是封面，画铺到最上沿正是要的效果；底下的 `.wrap`
 * 本来就留 60px。
 *
 * **这一笔在本地量不出对不对**：桌面浏览器里 env(safe-area-inset-*) 恒等于 0，于是本地量出来
 * 和改之前一模一样 —— 也正因为这样它是安全的一笔（不动桌面上的任何东西）。
 * 要验就把上面那几处的 env() 临时写成 59px / 34px 再看一眼（README 里那一轮记了量法）。
 *
 * 真机上还有一处**必然会变**的量：封面高 `34vh` 现在按整屏（含安全区）算，以前按让开之后
 * 那一截算 —— 带刘海/灵动岛那几档会比本机量出来的高出一小截（多出来的正是那条安全区的 34%）。
 * 下面 .scene 那段里「手机上只有 227px 高」是**不带安全区那一档**的数。
 */

/**
 * 页面右边那条滑块不要（2026-10-08 让藏掉的）。
 *
 * 两条并写的理由：`scrollbar-width` 是标准那条（Firefox 一直认，Chrome 121 起认），
 * `::-webkit-scrollbar` 是 Safari 和更早的 Chromium 那条 —— 只写一条，
 * 换一个浏览器它就又回来了。
 *
 * 藏掉的是**画出来那一根条**，不是滚动本身：滚轮、触控板、触摸拖动、空格和方向键、
 * 惯性滑动全部照旧。这一页本来就得往下滚（实测内容 1393px 对 720px 的窗口，
 * 条宽 15px）。
 *
 * 代价说在明处：那条条是「下面还有」唯一不靠字说话的提示，这里认了 ——
 * 屏幕底下那张卡是被切开的（卡高 687px、底边落在折线以下 394px），
 * 一张切开的卡片本身就是那句话。
 *
 * 只管文档这一条，全站就这一条被藏掉。
 */
html { scrollbar-width: none; }
html::-webkit-scrollbar { display: none; }

/**
 * `hidden` 属性说了算：**一条 !important，把这条规矩钉死**。
 *
 * 为什么需要它：UA 样式表里 `[hidden] { display: none }` 的权重是 (0,1,0)，
 * 而本站有一堆类选择器也声明了 display（`.btn` 是 inline-flex、`.float`
 * 是 inline-flex、`.post` 是 grid…）。类选择器同样是 (0,1,0)，**后写的赢** ——
 * 于是 `el.hidden = true` 对这些元素完全无效。
 * 这不是假设：封面那张脸上那颗红点数就是这么在「我的 0」里露出来的（实测抓到的），
 * 它只是"藏起来了但没藏住"，肉眼还不容易发现。
 *
 * 与其在每个用 hidden 的地方补一条 `[hidden]`，不如在这里一次说清 ——
 * 以后谁给某个类加 display，都不会再把这套开关弄坏。
 */
[hidden] { display: none !important; }

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 13px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }
a:hover { color: var(--ink); }

button {
  /* `font: inherit` 这条简写不能拆成长写：简写会把父级算出来的 line-height 一起继承过来，
     拆开之后 line-height 会落到初始值 normal，按钮高度就和旁边的输入框对不齐 */
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}
button, input, textarea { line-height: normal; }
button:focus-visible, input:focus-visible, textarea:focus-visible, a:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: -2px;
}

/* 小字标签：小号 + 大字距 + 灰。整套皮的签名，卡片抬头、页脚都用它 */
.label { color: var(--ink-4); font-size: 10px; font-weight: 400; letter-spacing: 0.12em; }

/* 可是**一整个句子**的小字标签要亮一档（--ink-4 → --ink-3）。
   这一条是 2026-10-08 翻回白纸之后补的：这一档灰在白纸上只有 3.26:1，
   而它原来坐在黑纸上（同一档灰对着黑纸是 6.9:1）—— 翻过去的只是纸，
   这些句子要说的意思没变，可读性掉了一截。挑出来抬一档的是**成句**的那些：
   输入的说明（破折号后面那半句解释）、传图的提示、
   认领表单底下那句、封面那句、「还有 N 条更早的」、
   一张商品卡的来源说明、「还没有人说话…」。
   （原来这一串里还有 `.way__hint` —— 弹层里每行底下那半句，2026-10-09 撤了。）
   量过：--ink-3 是 4.24:1。真按 AA 那条线（4.5:1）该上 --ink-2（6.2:1），
   可那一档是**正文**的灰，说明句跳上去就跟正文一样重了 —— 这一档是刻意的一小步。
   （2026-10-09 翻深色时这一条**没动**，连数都没怎么变：深纸上是 3.30 → 4.21，
   同一个台阶，见 :root。上面那句「对着黑纸 6.9:1」说的是 2026-10-08 上午那一版 ——
   那版的纸更黑（#0b0f13）、灰更浅（#8a8f96），两头都拉得开；现在两头都换了）
   只管「一句话」：编号、时间、计数这些**元信息**不在此列 ——
   它们本来就是签名那一档（也是全站占多数的一档），一个都没动 */
.compose__label,
.write__note,
.pledge__note,
.acts__more,
.scene__cap,
.pcard__own,
.talk__none { color: var(--ink-3); }

/* 数字一律等宽排布，纵向才对得齐 */
.n { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

/* 图标（Lucide，24×24 的描边图标）。
   2026-10-09 换图标时这里改过一个词：原来那套像素图标是**实心方块拼的**，配
   `shape-rendering: crispEdges` 让每条边压在整格上（交给浏览器抗锯齿反而糊出一圈灰边）。
   Lucide 是**描边**的，crispEdges 会把弧线切成台阶，所以改成 geometricPrecision ——
   线头和线角是圆的（`stroke-linecap` / `stroke-linejoin: round` 写在 svg 根上，见 icons.js）。 */
.ico { display: block; flex: none; shape-rendering: geometricPrecision; }

/* 只给读屏的文字。别用 display:none —— 那样读屏也读不到了 */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* 「加一张图」那一格的真身（一个 file input，见 index.html 里 </form> 底下那一段）。
   **不出面，但必须是一个真在页面上的元素** —— 两件事都是 iOS Safari 长年的脾气：

   一、藏在一棵 `display: none` 里的 input，`.click()` 一下它**不弹相册**，
        一声不响。它从前就长在 #compose 那一页里，而「从相册选一张」点它那一下
        那一页还藏着 —— 所以它现在搬到了 body 底下（用户 2026-10-09 报的那个
        「我还没选，直接到发帖页面了」就是这个）。
   二、**不用 `.sr-only` 那种藏法**：`clip-path: inset(50%)`（以及 `clip`、
        `pointer-events: none`）在 iOS 上会让选完文件之后的 change 不回来 ——
        人挑了张照片，页面这边一点动静都没有。
        这里用的是最老的那一种：挪到屏幕外（1px + 负偏移）。元素照旧在布局里、
        在无障碍树上，键盘也走得到（Tab 到它、回车就是那个框），就是看不见。
        `left: -9999px` 在从左往右读的页面里不会多出滚动条：往左的溢出滚不到。 */
.write__file {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* 页面外壳：只有一套，宽度和内衬全由它定 */
/* 底部那 60px 原来是页脚的（`.foot { padding-bottom: 60px }`）。页脚撤了之后
   挪到这儿 —— 每一页的最后一样东西都不该贴着屏幕底 */
.wrap { max-width: 1560px; margin: 0 auto; padding: 0 var(--pad) 60px; }

/* 小标签。**2026-10-09 改的**：原来是「小方角、直角、1px 线、12px —— 和 deals 的
   .chip 同款」，用户那句是「等一个好人做圆角，小一点」—— 方角改 **4px**
   （和头像、主按钮、方格同一档；20 高的块给 4px 已经一眼看得出是圆的，不必做成药丸），
   22 → **20 高**、12 → **11px 字**、左右内衬 7 → 6，整颗因此窄了 8px 上下。
   **同一天第二笔**（用户：「等一个好人也小点」，和昵称那句一起说的）：20 → **18 高**、
   11 → **10px 字**、左右内衬 6 → 5，图标跟着 11 → 10（在 app.js 的 stateChip 里配的）。
   这一笔的由头是抬头那两行：昵称收小之后，标签得跟着让出 2px，两行加起来才装得进
   左边那张 40 的脸 —— 账记在 `.post__head` 那一段。
   `flex: none` 是跟着它搬进抬头那一块才加上的，那会儿抬头还是**并排一行**：
   标签不许被昵称挤扁（要挤的是昵称，它自带省略号）。2026-10-09 改成**上下两行**之后
   这一句仍然要留：抬头是纵向 flex，`flex: none`（`flex-shrink: 0`）让这颗 18 高的块
   在那一列里保持 18 高，不会被压。 */
.chip {
  display: inline-flex;
  align-items: center;
  flex: none;
  gap: 3px;
  height: 18px;
  padding: 0 5px;
  font-size: 10px;
  line-height: 18px;
  color: var(--ink-2);
  border: 1px solid var(--rule);
  border-radius: 4px;
  white-space: nowrap;
}
/* 有分量的一格：底和字翻过来（亮底 + 深字）。用在「已实现」以外的强调位置 */
.chip--ink { color: var(--paper); background: var(--ink); border-color: var(--ink); }
/* 已实现：全站唯一的红。这是这块墙上最该被看见的状态 */
.chip--done { color: var(--red); border-color: var(--red); }
/* `.chip[aria-pressed='true']`（选中的小方角）撤了，2026-10-08：方角当按钮用只有
   「怎么说」和分类树两处，分类树先撤、三种说法随后搬进相机那一层（那一层是
   **一行一行**的，微信的样子）—— 最后一处用法跟着走了，这一条也就没人用了。
   方角现在只当**状态标签**（.chip--ink / .chip--done），不再接手指 */

/* 一行红色的提示（表单校验、接口报错）。别用 alert —— 它会打断人 */
.err { margin: 10px 0 0; font-size: 12px; line-height: 1.7; color: var(--red); }
.err[hidden] { display: none; }

/* 屏幕下方飘一句就走的提示。微信那个 toast 是一块**深色圆角**的浮片 ——
   原来是直角（皮是从那份直角报告誊来的），2026-10-08 跟着收尾改成圆角 */
.toast {
  position: fixed;
  left: 50%;
  /* 28px 是「离屏幕底下多远」；那枚安全区（2026-10-09 跟着 cover 补的）是躲开
     底下那条浮起来的地址栏和 home 指示条 —— 没有 cover 的时候这一句恒等于 28px */
  bottom: calc(28px + env(safe-area-inset-bottom));
  z-index: 60;
  transform: translate(-50%, 8px);
  max-width: 76vw;
  padding: 10px 16px;
  font-size: 14px;
  text-align: center;
  color: var(--on-solid);
  background: var(--block);
  border-radius: 8px;
  opacity: 0;
  pointer-events: none;
  transition: opacity .18s ease, transform .18s ease;
}
.toast--on { opacity: 1; transform: translate(-50%, 0); }
@media (prefers-reduced-motion: reduce) {
  .toast { transition: none; }
}

/* 引一段别人写的话（认领留言、致谢）。左边一道竖线，像划在纸上的一栏 */
.quote {
  margin: 10px 0 0;
  padding: 2px 0 2px 10px;
  border-left: 2px solid var(--rule);
  font-size: 13px;
  line-height: 1.7;
  color: var(--ink-2);
  word-break: break-word;
}
.quote--thanks { border-left-color: var(--red); color: var(--ink); }
.quote__by { display: block; margin-top: 4px; }

/* 按钮。全站只有**两种**，两种都是微信身上量出来的（用户 2026-10-08：
   「我看微信很多都是文字按钮，没有外面的框，有的是圆角按钮，等等之类的细节」）：

   一、**光秃秃就是几个字**（`.btn`）。取消 / 算了 / 等一下 / 再想想 / 不给 /
       去墙上找它 / 去那一页 / 申请看看 / 说句话 —— 微信里这类动作没有框、
       没有底、也没有边框，白纸上就是一行字，按下去颜色深一档。
       灰一档（--ink-3）：它们是这一页上「另一条路」，不是主角。

   二、**填满的圆角**（`.btn--primary`）。微信那个绿 + **4px 圆角** ——
       微信的主按钮是圆角矩形，不是胶囊。一次点击里最该按的那一颗：
       发表 / 说这一句 / 就它了 / 我收到了 / 给他看 / 我来实现。
       全站就这一处用绿，别的任何地方都不许引（文件头上那条规矩）。
       **高度 2026-10-09 从 44 收到 32。** 用户：「我来实现的按钮太大太笨重」、
       「发表按钮太大太笨重，不像微信原版的」。原来窄屏那条 `.btn { min-height: 44px }`
       把**这两颗绿的**也一起撑到 44（`padding` 还是 5/14），量出来是一块 100×44 的绿砖；
       微信身上那颗「发表」量下来在 30 上下，是个小方块。
       所以现在：文字按钮照样 44（那一行字本来只有 32 高，撑高只是把可点的地方放大，
       看不见、不发胖），**绿底那一颗固定 32**（见窄屏那一段）—— 形状不跟着手指变。
       代价写在明处：手机上这颗绿的按下去只有 32 高，比 iOS 那条 44 的建议值矮一截，
       这是他要的「像微信原版」，认了。

   「白底 + 1px 发丝线」那一版（和 deals 的按钮同款）2026-10-08 撤了：
   一列卡上挂着七八个方框，看着像一张表格，不像朋友圈。
   **别再加第三种** —— 这一条是这一版收尾的重点，多一种形状就少一分「一眼认得」。 */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-height: 32px;
  padding: 4px;
  font-size: 15px;
  white-space: nowrap;
  color: var(--ink-3);
  background: none;
  border: 0;
  cursor: pointer;
}
.btn:hover { color: var(--ink); }
.btn:disabled { color: var(--ink-faint); cursor: default; }
.btn:disabled:hover { color: var(--ink-faint); }
.btn--primary {
  /* 32 高、左右 12 —— 微信那颗小方按钮的比例。数值一起写在这儿，
     窄屏那一段再照抄一遍（窄屏 `.btn` 那条位置更靠后，会盖过这一条） */
  min-height: 32px;
  padding: 0 12px;
  color: var(--on-solid);
  background: var(--green);
  border-radius: 4px;
}
.btn--primary:hover { color: var(--on-solid); opacity: .88; }
/* 一颗填满的按钮禁用时不能还是绿的（那看起来照样能按）：退回灰底，字取**纸色**——
   也就是「一块灰的，上面的字几乎看不见」。两个方向量出来是同一件事：
   白纸上 1.37:1（白字压 #d9dde1），深纸上 1.29:1（纸色字压 #2e2e2e）。
   这一档刻意就要它读不清（它说的是「现在按不了」），所以别顺手把它提亮 */
.btn--primary:disabled { color: var(--paper); background: var(--rule); }
.btn--primary:disabled:hover { color: var(--paper); background: var(--rule); }
/* 「小一号」只改字号，不改形状：上面那两种形状一改就不是微信了 */
.btn--sm { font-size: 14px; }

/* ---------------- 封面右上角那两颗浮着的图标 ----------------
   2026-10-08 晚上报头整条撤了（用户那句「朋友圈的背景要到顶」）：朋友圈的背景图上面
   没有一条留给站名和数字的横条，图就贴着屏幕最上沿。原来报头干的两件事这么分：
     那串数   整个撤了（用户：「1 个愿望 · 0 个已实现这些文字，纯 icon 就够了」）——
              墙上现在什么样，页脚那一行照旧说得更全
     那台相机 浮到封面右上角（就是微信那个位置）
   两颗图标各属一页：墙上那页是相机（发布），我那一页是消息（去 #/msgs）——
   谁露谁由 app.js 的 route() 点，和那两张封面一个规矩。

   **为什么是 fixed，不是贴在封面里**：贴进封面它就跟着封面一起滚走了 ——
   封面手机上只有 227px 高（见下面 .scene 那段量出来的数），滚过去之后整个站
   就没有发布入口了。所以它钉在视口右上角，只在封面还没滚出画面的时候露着
   （露多久由 onScroll 量 `.scene__img` 的底边决定，见 app.js）：封面一走它跟着消失，
   往回滚一截又回来。这样它**永远在画上**，那颗白图标也就永远有底可依 ——
   不用给它垫一层半透明的圆，也不用在滚过界时把颜色翻一遍。
   （报头那条「往下滚收起来、往上滚放下来」的账留着，只是量法换了。） */
.floats {
  position: fixed;
  top: 0;
  right: 0;
  left: 0;
  z-index: 30;
  display: flex;
  justify-content: flex-end;
  gap: 4px;
  padding: calc(4px + env(safe-area-inset-top)) var(--pad) 4px;
  /* 这一条铺满整个屏宽，但它只有两颗图标要接手指 —— 底下的封面照旧能点
     （封面右下角那张脸就在这一条底下，别让它挡住）
     上面那枚 env(safe-area-inset-top) 一直写着，但直到 2026-10-09 补上
     `viewport-fit=cover` 才真的不是 0 —— 在那之前它算成 0，这一条是靠顶到视口上沿对齐的 */
  pointer-events: none;
  transition: opacity .18s ease, visibility 0s linear .18s;
}
/* 封面滚过去了：整条淡走。`visibility` 一起收掉是因为 Tab 走得到看不见的按钮 ——
   `opacity: 0` 改的只是看不看得见，不改进出（和那几个面板一个道理） */
.floats--up { opacity: 0; visibility: hidden; }

/* 一颗图标：44×44（手指点得准的那条线），图标 22px 摆在正中。
   它压在封面那张动图上，所以是**纯白**（`--on-solid`）—— 那两张图 90% 以上是近黑的像素
   （见 .scene 那一段量出来的数），白图标在上面 19:1。
   **2026-10-09 翻成深色，这一档没动**：它的底是那张画，不是纸 —— 画两趟都是近黑的。
   连「滚过去就让开」那一条也留着，虽然当初量出来的理由不成立了：白纸那一版是
   「白图标落到白卡上什么都看不见」，深色这一版落到纸上反而看得见。让开照旧，
   因为让开的道理本来就不只是「看不见」—— 一颗浮着的相机压在别人的愿望上，
   比看不见更不像朋友圈。 */
.float {
  position: relative;
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 44px;
  height: 44px;
  padding: 0;
  color: var(--on-solid);
  background: none;
  border: 0;
  cursor: pointer;
}
/* 入口**刻意不做 hover 下划线**：手机上 :hover 会粘住，点一下就一直留着 */
.float:hover .ico { opacity: .72; }

/* 图标角上那颗红点数（有 N 件事等着你时才有）。和封面右下角那张脸上的那颗
   （`.scene__me .badge` 那一条）是同一个东西，只是长在不同的母体上 ——
   那颗钉在脸上，这颗钉在图标上。红只做点缀那条没破：它说的是「有 N 件事等你」

   形状是**圆的**（一位数就是一个圆点，两位数才抻成胶囊）—— 微信角标就是这个形状，
   原来是直角方块，2026-10-08 收尾改的 */
.badge {
  position: absolute;
  top: 0;
  right: 0;
  min-width: 18px;
  padding: 0 5px;
  font-size: 11px;
  line-height: 18px;
  text-align: center;
  color: var(--paper);
  background: var(--red);
  border-radius: 999px;
}


/* 搜索框（`.searchbox` / `.search`）撤了，2026-10-08 —— 全站唯一一处用法是
   发布面板里搜榜上那件东西，跟着「榜上挑一件」一起走的。
   那种「图标 + 输入框」的外层容器写法本身没毛病（<input> 不生成伪元素，
   想在文字前面放图标只有外层容器这一条路），需要的时候照这个思路再写一份就是 */

/* 「一排小方角按钮」（和 deals 的尺码那一排同款）2026-10-08 起全站没有它了 ——
   怎么回事、最后一处用法跟着什么走的，记在上面 `.chip[aria-pressed]` 那一条里 */

/* ---------------- 一件东西（照报告的卡片誊写） ----------------
   这一段的每个数都是**从报告中量出来的**，不是在这里调出来的：
   图正好 3:4 铺满卡片宽、名称 13px、现价 22px 红、上市价 15px 划线、
   降幅条 25 格（每格最多 3px、缝 2px、高 9px）、「降 N 元」12px 墨色。
   报告那边这些值散在 `web/src/styles.css` 的 `.card*` 里（带一大串为什么改过的注释），
   心愿墙是原生 ESM，拿不到那份 CSS，所以是誊写 —— **改口径要改两边**。
   一处刻意不同：那边卡片在网格里没有边框；这里也没有（图本身自带底色，加框反而脏）。 */

.pcard { min-width: 0; }
.pcard__img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4; /* 官图就是 3:4，画框也做成 3:4 —— contain 即满框 */
  background: var(--paper);
  overflow: hidden; /* 图抬起 0.86 透明那一下不越出画框 */
}
.pcard__pic {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  transition: opacity .15s ease;
}
/* 图只干一件事：点一下去官网。悬停淡一点，和报告一样 */
.piclink { display: block; }
.piclink:hover .pcard__pic { opacity: .86; }
.piclink:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* 没有图的那一格：说实话，不摆一个灰方块装作有东西 */
.pcard__img--empty {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px;
  text-align: center;
  background: var(--surface);
}

.pcard__name {
  display: block;
  margin-top: 6px;
  overflow: hidden;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: 0.04em;
  color: var(--ink);
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* 一句话顶掉一张图的位置：照片底下说明它是谁拍的、或者「当时挑的那件东西
   现在榜上没有了」。上边距和 .pcard__name 一样是 6px —— 两张卡摆一起时，
   图底下那行字落在同一条线上 */
.pcard__own { display: block; margin-top: 6px; }
.pcard__prices {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
  line-height: 1.15;
}.pcard__now {
  font-size: 22px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--red);
}
/* 没降价的不配红：退回次级灰 */
.pcard__now--flat { color: var(--ink-2); }
/* ¥ 缩到 13px，按报告那样压到数字左下角 */
.now__sym {
  display: inline-block;
  margin-right: 3px;
  font-size: 13px;
  vertical-align: -3px;
}
.pcard__was {
  font-size: 15px;
  font-weight: 400;
  color: var(--ink-2);
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}
/* 降幅条 + 「降 N 元」。条和字同行，条占满剩余宽度，字跟在条尾 */
.pcard__deal {
  display: flex;
  align-items: center;
  margin-top: 2px;
  font-size: 13px;
}
.pcard__bar {
  display: flex;
  flex: 1;
  align-items: flex-end;
  gap: 2px;
  min-width: 40px;
  height: 9px;
  /* 左边缩进 4px：名字和价格都是齐左边沿的，这条稍微内收一点，
     看着不像是被裁掉了（用户对报告提过的那一处，这里照做） */
  margin-left: 4px;
}
.pcard__bar > i {
  flex: 1;
  max-width: 3px;
  height: 100%;
  background: var(--rule-soft);
}
/* 亮的格子是墨色，不是红色 —— 条是「降了多少」的度量，红色留给现价一处 */
.pcard__bar > i.is-on { background: var(--ink); }
.pcard__off {
  font-size: 12px;
  font-weight: 500;
  color: var(--ink);
}
/* 价格和降幅数字走窄体数字栈，和报告同一个感觉 */
.pcard__now, .pcard__was, .pcard__off, .pcard__deal {
  font-family: var(--num);
}

/* 回执那一处的**一行版**：左边一张小图，右边名称 + 价格（小一号）。
   原来挑东西那一格也用它（挑中的那件收成一行），那一格撤了（2026-10-08） */
.pcard--row { display: flex; align-items: flex-start; gap: 10px; }
.pcard--row .pcard__thumb { flex: none; width: 56px; }
.pcard--row .pcard__side { flex: 1; min-width: 0; }
.pcard--row .pcard__name { margin-top: 0; }
.pcard--row .pcard__now { font-size: 16px; }
.pcard--row .pcard__was { font-size: 13px; }
.pcard--row .pcard__deal { font-size: 12px; }
.pcard--row .pcard__bar { height: 7px; max-width: 96px; }

/* 「挑一件东西」那一整格（`.picker*` / `.picklist` / `.pick*` / `.picked*`）
   撤了，2026-10-08 —— 一整块挑选面板：搜索框 + 一列可滚的清单，
   挑中的那一件把清单收起来换成一行。跟着「榜上挑一件」一起走的。
   墙上**已经挑过东西的那些愿望**照旧显示商品卡，那些走的是上面 `.pcard` 这一段 */

/* ---------------- 相机那一层（从底下滑上来的） ----------------
   2026-10-08 添的。微信点相机弹出来的就是这一层：底下压暗，上面**两块卡片**，
   上面那块带三行（拍摄 / 从相册选择 / 取消），下面那块只写「取消」。
   这里那三行是三种说法（挑、传、写）—— 文案和图标在 app.js 的 WAYS 那张表里，
   行是 buildWays() 造的。
   2026-10-08 又照微信收了一遍：面板原来是一整块**灰的**（--surface + 顶上一道线），
   现在拆成两块卡片 + 中间一道 8px 的缝（缝里露出来的是暗底）。
   卡片是白纸、缝是暗底 —— 分层靠这两样，不靠线了 */
.sheet {
  position: fixed;
  inset: 0;
  /* 80：**比写愿望那一页（70）还高一层**。这一层从两个地方叫得出来 ——
     墙上的相机，和写愿望那一页上那个「加一张图」的空格子；后一处如果压在
     那一页底下，它就是一整屏看不见的东西（写愿望那一页是固定的、铺满的、
     底色是白的）。README 那句「在哪一页点开，就在哪一页上关掉」说的就是这个：
     点开的那一页必须还能看见它。实测抓到的：从那一格点开时
     `#sheet-cancel` 是「covered by .write__page」，点不着。
     上面这一档之下是写愿望那一页 70（原来它是最高的一层）、toast 60、浮图标 30 */
  z-index: 80;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}
.sheet__scrim {
  position: absolute;
  inset: 0;
  /* 暗底从 .74 收到 .5：翻成白纸之后 .74 压在白纸上像一块黑板。
     微信那一层也就是 .5 上下 —— 底下那页还得看得见一眼（这一层是「换一种说法」，
     不是「挡住你」）。
     2026-10-09 翻深色，这一个数**没动**：它盖着的底换了，但这层黑照旧做同一件事
     （把底下那页压暗、让上面那两块浮起来）。深纸上 .5 压下去是 1.5:1 上下 ——
     底下那页还看得见，两块卡片（--card）在它上面照样立得住 */
  background: rgba(0, 0, 0, .5);
  opacity: 0;
  transition: opacity .2s ease;
}
.sheet__panel {
  position: relative;
  /* 左右内衬和别处那条 640 的列对齐（宽屏上是 (100% - 640px)/2，窄屏就是 --pad）：
     1280 上这一层仍然铺满屏幕，但里面那两块和墙上那一条排在同一条线上 ——
     一行 1280 宽的字读不动，一颗 1280 宽的「取消」也不是一颗按钮。
     手机上 --pad 是 8px，于是两块卡片离屏幕两边各 8px —— 微信那一层的样子 */
  padding: 0 max(var(--pad), calc((100% - 640px) / 2)) calc(8px + env(safe-area-inset-bottom));
  /* 这一层自己**不铺底色**了（原来是 --surface + 顶上一道线）：底色现在在下面
     两块卡片上，中间那道 8px 的缝里露出来的是**暗底**（深色这一版里露出来的是
     压暗了的那张深纸）—— 面板换成浮块 + 一道缝，是微信那一层的样子 */
  background: none;
  /* 出生时在屏幕外（100% 就是它自己那么高），加上 .sheet--on 才滑上来。
     两处都在 .22s：滑上来和滑下去是同一趟，眼睛才不别扭 */
  transform: translateY(100%);
  transition: transform .2s cubic-bezier(.22, .8, .26, 1);
}
.sheet--on .sheet__scrim { opacity: 1; }
.sheet--on .sheet__panel { transform: translateY(0); }
/* 上面那一块卡片（`--card`）：两行说法。四角都圆 —— 它底下不是屏幕边，
   是一道 8px 的缝，方角摆在那儿像没做完。
   白纸那一版它是**白**的（浮在压暗的白纸上）；深色这一版它是**比纸亮一格的灰**
   （#2c2c2c 浮在压暗的 #191919 上）—— 微信暗色那一层的浮块也是这个路子，
   而且在这个站里它和 --surface 是同一个值（见 :root 里那两行） */
.sheet__group {
  padding: 6px 12px 8px;
  background: var(--card);
  border-radius: 12px;
}
/* 原来这里有一条 `.sheet__h`（顶上那行「发一条愿望」），2026-10-09 连着 HTML 一起撤了 */
/* 两行：一句话，居中，前面不带图标。整行都是可点的（52px），不是两块小方角 ——
   微信那一层也不是按钮排。
   这一块动过两笔（都在 2026-10-09）：
     · 每行底下那半句解释撤了（`.way__hint`）—— 撤掉之后一行只剩一句话，
       `min-height` 跟着从 56 收到 52，字从 15 抬到 16
       （骨架空了一档就得让字补上一点，不然两行话孤零零地浮在两块大块里）；
     · **行首那颗图标也撤了，整句居中，两行不再分深浅**（用户：「从相册选一张几个字
       怎么和下面的纯文字创作颜色不一样，从相册选一张是浅灰的，这两前面不要icon，
       文字居中」）。深浅的来路是 `aria-pressed`：当前那一种说法亮一档，于是打开的一瞬间
       一行深、一行浅 —— 而这一层点一下就关，那点差别只是让人愣一下。
       现在两行都是 `--ink`（原来不选的那种是 `--ink-2` 的浅灰），
       `justify-content: center` 让字落在整行正中（`.way__label` 是 `display: block`，
       顺带把 `text-align: center` 也写上，字长了换行时第二行也是居中的）。
       `aria-pressed` 留着，但**没有任何样式认它** —— 它只是给读屏的一句话 */
.ways { display: block; }
.way {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 52px;
  padding: 6px 0;
  text-align: center;
  color: var(--ink);
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rule-soft);
  cursor: pointer;
}
.way:last-child { border-bottom: 0; }
/* 悬停那条 `.way:hover .way__label { color: var(--ink) }` 跟着撤了：两行现在本来就是
   `--ink`，它没有东西可亮了。.way__ico 那两条（图标那一格）也撤了 —— 行首没有图标了 */
.way__label { display: block; font-size: 16px; }
/* 「取消」：单独一块卡片，和上面那一块隔着 8px（微信那一层也是这么分的：
   底下那一块只写「取消」，两块之间那道缝是它的分界线，不用再画线） */
.sheet__cancel {
  width: 100%;
  min-height: 52px;
  margin-top: 8px;
  font-family: inherit;
  font-size: 15px;
  color: var(--ink-2);
  background: var(--card);
  border: 0;
  border-radius: 12px;
  cursor: pointer;
}
.sheet__cancel:hover { color: var(--ink); }

/* ---------------- 传一张图（写愿望那一页的配图） ----------------
   这一块 2026-10-08 收尾时**合并成了一排方格**，见下面 `.write__tiles`。
   原来这里是「96px 的 3:4 画框 + 两个按钮（选一张图 / 不要这张了）」，
   和旁边那一格「＋」是两套东西干同一件事 —— 用户打开这一页会先愣一下：
   点＋还是点「选一张图」？一页上不该有两扇通向同一个地方的门。
   合并的办法是照微信：**只有一排方格**，空格子上是 ＋（点它叫出相机那一层），
   有图了那一格就是那张图、右上角一个小 × 拿掉。所以这一段整个撤了。 */

/* ---------------- 信息流（墙上） ----------------
   2026-10-08 换的。原来这里是一副牌：一次一张、左滑看下一个、两枚章、
   底下那排「上一个 / 下一个」。照「主页也弄成朋友圈那种」改成了一列信息流：
   一条愿望一条，左边一张脸，右边署名 + 正文。撤掉的东西记在 README 里。

   列宽 640：和对话那一页同一条（那边量过「再宽一行就读不动了」）。
   封面是铺满屏幕的一条，信息流不是 —— 朋友圈在宽屏上也是这样一条窄列。

   分隔用发丝线，**没有底色也没有边框**：一副牌那张卡是因为「一张单独悬在
   纸上的卡必须有边」才画了框，一列里不需要 —— 这和报告那份「商品卡不画边框」
   又回到同一句话上了。 */
.feed { max-width: 640px; margin: 0 auto; padding: 8px 0 60px; }
.post {
  display: grid;
  /* 40px 那张脸 + 剩下全给正文。minmax(0, 1fr) 那一段是给 long 的商品名留的：
     少了它，卡片里那些 nowrap 的长名字会把这一格撑破（这笔老账见 ≤560 那一段） */
  grid-template-columns: 40px minmax(0, 1fr);
  gap: 12px;
  padding: 18px 0;
  border-bottom: 1px solid var(--rule);
}
.post:last-child { border-bottom: 0; }
/* 「去墙上找它」跳过来的那一条：闪一下。和刚发表那条回执闪的是同一套说法 */
.post--hit { animation: hit 1.6s ease-out; }
@keyframes hit {
  from { background: var(--red-wash); }
}
.post__face {
  display: block;
  width: 40px;
  height: 40px;
  /* 4px 圆角的方框 —— 微信的头像就是这个形状（这是全站两处圆角之一，
     另一处是相机那一层面板顶上那两个角）。overflow 是给里面那张图裁角的 */
  border-radius: 4px;
  overflow: hidden;
  /* 一道发丝线：这 42 张脸是照片，边上没有线就散在白纸上了
     （和两张封面图是同一个判断） */
  border: 1px solid var(--rule);
  background: var(--surface);
}
.post__faceimg { display: block; width: 100%; height: 100%; object-fit: cover; }
.post__main { min-width: 0; }
/* 抬头：**昵称在上、状态标签在下**，上下两行。
   2026-10-09 改的（用户：「昵称和等一个好人采用上下排列」）。
   这一天这一块动过**三次**，账记在这儿免得下次看糊涂：
     · 先是标签从下面单独一行（`.wish__top`）搬上来，和昵称**并排**一行
       （用户：「最好和昵称加上这个标签，高度恰好和左边的头像一样，这样看着比较和谐」），
       那一版的 `min-height: 40px` 就是**那张脸的高度** —— 左列 40、右列第一行也 40，两列起头齐平；
     · 然后改回上下两行，理由和那一版不冲突：并排时昵称和标签在抢同一条横向空间
       （昵称那边还得靠省略号让位），分开之后各自占一行，谁也不用挤谁。
       代价当时没量：两行加起来是 **46.4**（16px 的昵称一行 22.4 ＋ 缝 4 ＋ 20 高的标签），
       **反超了那张 40 的脸**；
     · **第二笔就在同一天**（用户：「昵称再小点，等一个好人也小点，他两的高不要超过左边头像的高」）：
       昵称 16 → **14px**（行高 1.4 → 1.3，一行 18.2）、缝 4 → **3**、
       标签 20 → **18 高 / 10px 字**（在 `.chip` 那一条上改，四处共用的是同一颗）。
       两行加起来 **39.2**，再由下面那个 `min-height: 40px` 撑到**正好 40 ＝ 左边那张脸的高**。
       `min-height` 这一版才真正说话：上一版它写着 40 却是空的（46.4 早把 40 盖过去了）。
   三处细节：
     · `align-items: flex-start` 是**必须的**：纵向 flex 默认的 `stretch` 会把标签也拉满整列宽
       —— 它是 `inline-flex`，横向一撑就成了一根横条，不再是那个 18 高的小块。
     · 昵称因此得自己带上 `max-width: 100%`：`flex-start` 下每一样都按「内容多宽就多宽」摆，
       一个长名字会直接顶出这一列。它自带 `nowrap` + 省略号，但**省略号只在宽度被限制住时
       才出现**（`max-width` 就是那道限制）。
     · 多出来的那 0.8px（40 − 39.2）落在列尾：纵向 flex 默认 `justify-content: flex-start`，
       两行贴着自己这一列的顶，和左边那张脸从同一条线上起头；`40×40` 的脸是定高、
       `.post` 那两列默认 stretch。 */
.post__head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
  min-height: 40px;
  margin-bottom: 8px;
}
.post__name {
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
  /* 14px（2026-10-09 第二笔，用户：「昵称再小点」；上一档是 16）。
     昵称比正文（15）还小一档是**故意的** —— 正文是这条愿望本身，昵称只说「谁写的」，
     现在连字号也退半步。行高 1.3（一行 **18.2**）是给下面那颗 18 高的标签留的数：
     两行加起来不许超过左边那张 40 的脸，见 `.post__head` 那一段。
     名字不用蓝是微信的做法，这站不用蓝（用户 2026-10-06 那条），
     所以它和正文一样是墨色，靠字号和位置认出「这是谁」 */
  font-size: 14px;
  line-height: 1.3;
  color: var(--ink);
  white-space: nowrap;
  text-overflow: ellipsis;
}
/* 图收一档（260px，3:4 就是 347px 高；画框里的图本来就按 contain 放在正中间，
   所以框小了图跟着小，不会裁）。报告那份卡片的几何一个字没改，
   收的只是它在信息流里的宽度 —— 588px 宽的一张商品图，一条愿望就占掉一屏 */
.post .pcard:not(.pcard--row) .pcard__img { max-width: 260px; }

/* 卡片**没有边框也没有底色**（和报告一样）：底色和页面一个样，图自己就把格子撑起来了。
   一圈灰线只会把这面墙切成一个个盒子，那是另一套版面 */
.wish {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* `.wish__top`（状态标签那一行）和 `.wish__id`（编号）都在 2026-10-09 撤了：
   标签搬进了抬头（`.post__head`），编号整个不上屏了（用户：「右边不要有 #1 之类的东西」），
   两条规则就都没人用了 —— 这一版不用的不留。 */

.wish__text {
  margin: 0;
  font-size: 15px;
  line-height: 1.7;
  /* 最长 140 字，靠这点行高读得下去。
     white-space: pre-wrap 让用户自己打的换行保留下来（textarea 里回车是允许的） */
  white-space: pre-wrap;
  word-break: break-word;
}
/* **上面挂了图的那一条**才有那道发丝线：把「这件东西」和「这个人说的话」分成两层
   （卡片上半截是榜单，下半截是这面墙）。
   只有一段字的那几条**不画**（2026-10-08 收尾改的）：那种卡上这一条的上头
   只有状态那一行，一条横线摆在那儿像一张表格里的一格，而朋友圈的正文
   就是接着名字往下写 —— 空着的那点距离交给 `.wish` 自己的 gap */
.wish__text--after {
  padding-top: 10px;
  border-top: 1px solid var(--rule-soft);
}

.wish__foot {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: auto;
  padding-top: 10px;
  border-top: 1px solid var(--rule-soft);
}
.wish__by { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wish__age { font-family: var(--num); }
/* 卡脚这一排里的小字（时间 / 「认领于…」 / 「等了 3 天」）抬到 12px。
   `.label` 那一档是 10px + 0.12em 大字距（全站签名那一档），可朋友圈底下
   那行时间就是这一档大小 —— 时间在这一排里是**要读的**东西，不是标签 */
.wish__foot .label { font-size: 12px; letter-spacing: 0.06em; }

/* 认领留言 / 致谢那一段的抬头小字 */
.wish__notehead { display: block; margin-bottom: 2px; }

/* 认领时那小块表单：**替换掉卡片底部那一行**，就地展开，不弹窗。
   它接过 .wish__foot 的位置和那条发丝线，所以卡片高度变化最小 */
.pledge {
  margin-top: auto;
  padding-top: 10px;
  border-top: 1px solid var(--rule-soft);
}
.pledge .compose__in { min-height: 52px; }
.pledge .compose__field { margin-bottom: 8px; }

/* ---------------- 写一条愿望（一整页） ----------------
   点左上角那台相机，先滑出「怎么发」那一层（.sheet），选一种说法才进这一页。
   这一页学的是微信的发表页：**固定在整屏之上**，顶上一格是「取消 / 标题 / 发表」，
   下面正文一行提示「这一刻的想法」，再下面配图，最后几行「价格 / 联系方式 / 署名」
   （微信那三行是所在位置 / 提醒谁看 / 谁可以看 —— 这面墙要人交代的是另外三样）。

   ⚠️ 这一页**不能再和 `.wrap` 摞在一起**（原来是 `class="wrap compose"`）。
   老账留在 `.mine` 那一条里：两个类权重一样、`.compose` 在文件里更靠后，
   一个 `padding: 0` 的简写就能把 `.wrap` 的左右内衬抹平 —— 手机上量出来的样子是
   输入框从 x=0 起、375 铺满整屏，贴着屏幕边打字。这一页索性不再借 `.wrap`：
   左右内衬由里面的 `.write__page` 自己管。

   为什么不照原来那样在文档里整行展开（点「我要许愿」推出来一块面板）：
   朋友圈这一套里「写这一页」和「底下那一页」是两层，取消要能原样还给你 ——
   连滚动到哪儿都还给你（见 app.js 的 goWrite / leaveWrite）。
   在文档流里的一块面板做不到这个，它撑开就把底下推走了。 */
.write {
  position: fixed;
  inset: 0;
  /* 压在所有东西上面：封面顶上那两颗浮图标 30、弹层 60，这一页 70 ——
     它是最前面那一层（原来第二档是报头 40，报头撤了换成那两颗图标） */
  z-index: 70;
  display: flex;
  flex-direction: column;
  /* 整页自己滚（它顶上是自己那一条 .write__bar，不靠别的东西粘），
     滚到头也不再带动底下那一页 */
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--paper);
  /* 这一层自己那根滑块也藏掉（和 html 上那一对同一个写法、同一个理由）：
     它一占就是 15px，`100%` 从 1280 变成 1265，里面那条 640 的列跟着左移 7.5px ——
     量出来 312.5，和墙上那一条（320）差半根手指，一眼看得出没对齐。
     它又是盖在文档上的一层：文档那根已经藏了，这一根留着就成了两层里
     只有上面那层挂着一根灰条，在白纸上格外扎眼。
     代价和文档那一条一样（少了「下面还有」那个不靠字说话的提示），
     这里也认了 —— 顶上那一格是 sticky 的，「发表」一直在，要按的东西不会滑走 */
  scrollbar-width: none;
}
.write::-webkit-scrollbar { display: none; }
/* 张开时底下那一页不许滚：它是盖上来的一层，不是插进来的一节。
   少了这一条，手机上两根手指分别划上下两层，底下的墙会跟着一起动 */
body.composing { overflow: hidden; }

/* 顶上一格：取消 / 发表。和微信一样**粘在顶上**（往下滚只有正文在动）。
   **2026-10-09 从三格改成两格**：中间原来还有一行标题（「发一条愿望」），
   用户那句「中间也不该有发一条愿望几个字」把它撤了。
   原来是 `1fr auto 1fr` 三格（为了标题真居中），两格之后 `space-between` 就够 ——
   顺便修掉一个量出来的毛病：「取消」原来撑在那半格里、字是**居中**的，
   375 上量出来那两个字起在 x≈52，离屏幕左沿半根手指，就是用户说的「不够靠左」。
   现在它 `justify-self: start`，就贴着内衬那一列（手机上 8px）起。
   左右内衬和正文那一列同一句话（见 .write__page）：宽屏上两样都落在 640 那一条上，
   窄屏就是 --pad。底色和底下那条发丝线照旧铺满整屏（一条顶栏就该是整条） */
.write__bar {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  /* 48 而不是 52：右边那颗按钮收成 32 之后，一条 52 的顶栏会显得空一截 */
  min-height: 48px;
  /* 顶上那一枚是 2026-10-09 跟着 `viewport-fit=cover` 补的：内容现在从屏幕最上沿起，
     不加就被灵动岛压住（「取消」和「发表」都点不着）。底色照旧铺到最上沿 ——
     padding 算在这一条自己的盒子里，所以让开安全区的是字和按钮，不是那块底 */
  padding: env(safe-area-inset-top) max(var(--pad), calc((100% - 640px) / 2)) 0;
  background: var(--paper);
  border-bottom: 1px solid var(--rule-soft);
}
/* 「取消」：**光秃秃的两个字贴左沿**。16px（不是原来那档 15）—— 微信顶栏那两样
   是一样大的，右边那颗按钮的字就是 15，左边这行字跟着抬一档才配得上 */
.write__cancel { min-height: 44px; text-align: left; font-size: 16px; color: var(--ink-2); }
.write__cancel:hover { color: var(--ink); }
/* 「发表」**不用自己写一条**：它要的正是 `.btn--primary` 那副形状
   （32 高、左右 12、4px 圆角、15px 字）。原来这里写着 `padding: 5px 14px`，
   窄屏那条又把它撑到 44 高 —— 一块绿砖，HTML 上还挂着一颗 12px 的 send 图标。
   2026-10-09 两样都撤了（图标连着 wire() 里那行 append 一起）。
   `.write__send` 这个类名留着（页面上要靠它认那颗按钮），身上没有声明 */

/* 正文那一列：和对话那一页、信息流同一条 640（再宽一行就读不动了）。
   左右内衬写成 `max(--pad, (100% - 640px)/2)`：窄屏是 --pad（8），
   宽屏上正好把这条 640 摆在屏幕正中 —— 1280 上是 320..960，
   和墙上那一条、对话那一条量出来是同一条线。所以这里不写 max-width，
   宽度交给内衬算（写 max-width 的话内衬会落在那条线里面 40px）。
   底下 80px 是有意的 —— 手机上最后一行是「署名」，键盘弹起来时
   它得能从键盘上面挪出来，不留这段余量就顶在键盘底下点不着 */
.write__page {
  width: 100%;
  padding: 14px max(var(--pad), calc((100% - 640px) / 2)) 80px;
}
.write__textbox { display: block; }
/* 正文：这一页上唯一「一开始就该是空的」的东西，所以**不画框、没有底色**，
   只有一行提示。16px（原来 17）—— 用户那句「这一页的字体好像偏大，有点笨重」
   就是照着一整页量出来的；17 抬上去的那一档让这一页比墙上的正文还重，
   而这里要的只是「一进来就知道该往哪儿写」。16px 也还压着 iOS 那条线
   （输入框 ≥16px 聚焦不放大），这一条不能破。
   `min-height` 96 → 88：撤掉上头那行标签之后，正文到配图那一排之间的空当也跟着收一档 */
.write__text {
  display: block;
  width: 100%;
  min-height: 88px;
  padding: 6px 0;
  font-family: inherit;
  font-size: 16px;
  line-height: 1.7;
  color: var(--ink);
  background: none;
  border: 0;
  outline: none;
  resize: none;
}
/* 这一页上那几个「没有框的」输入（正文 + 底下三行的值）要把全局那条聚焦框关掉。
   尤其是**正文**：`textarea:focus-visible` 是**永远命中**的 ——
   浏览器把能打字的控件一律当成键盘可达（因为你要在里面打字），
   所以手指点一下那行「这一刻的想法」，整块会套上一个 2px 的黑框，
   就是用户说的「点击这一刻的想法也不该有黑框」。
   框没了、位置提示还在：光标本身就是提示（微信那一页也是这样）。
   那几行值的框在 `.write__val` 那条里已经写成 `outline: none` 了，这里是补上
   `:focus-visible` 那一档 —— 它比类选择器多一个伪类，权重更高，不补挡不住 */
.write__text:focus-visible,
.write__val:focus-visible { outline: none; }
.write__text::placeholder { color: var(--ink-4); }
/* 字数那一格：正文底下一行、靠右。上头原来还有一行标签
   （「为什么想要它」 / 「想要什么 —— 卡片上就只有你写的这段话」），2026-10-09 撤了 ——
   微信那一页正文上面什么都没有，提示已经在「这一刻的想法」那句里了 */
.write__meta { display: flex; justify-content: flex-end; margin-bottom: 14px; }
.write__meta .compose__counter { margin-left: auto; }

/* 配图那一块：**一排 96px 见方的方格** —— 微信发表页上就是这一排。
   手上没有图的时候是一格空的 ＋；有图了那一格就是那张图，右上角一个小 × 拿掉。
   **有图的时候不摆 ＋**：微信的规矩是「到上限就不摆 ＋」（那边上限 9 张，
   到 9 就不给了），这里上限是一张，所以一条路上只有一格。

   为什么要合并：原来这一排和旁边那套「96px 画框 + 选一张图 + 不要这张了」是
   同一件事的两扇门（点 ＋ 还是点「选一张图」？），收尾时并成一扇。
   这一排本来还是三种说法的第二个入口（挑一件 / 传一张 / 就写字），
   挑一件撤了之后就只剩它自己了。 */
.write__media { margin-bottom: 14px; }
.write__tiles { display: flex; flex-wrap: wrap; gap: 8px; }
/* 从桌面把图拖到这一排上的时候，让那几格一起亮一圈 —— 不然「能往哪儿放」
   全靠猜。用的是正文那种墨色，不是蓝色（用户那条老规矩） */
.write__tiles--over .write__add,
.write__tiles--over .write__thumb { outline: 2px solid var(--ink); outline-offset: -2px; }
/* 加一格：一个**实心浅灰**的方格 + 一个灰 ＋ —— 微信那一格就是这个样子
   （浅灰底、没有边框），96px 是手指点得准的那条线再宽一档。
   它只管一件事：把相机那一层叫出来（「从哪儿来」，不是「加什么」） */
.write__add {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 96px;
  height: 96px;
  padding: 0;
  color: var(--ink-4);
  background: var(--surface);
  border: 0;
  border-radius: 4px;
  cursor: pointer;
}
.write__add:hover { color: var(--ink-2); background: var(--rule-soft); }
/* 有图的那一格。**1:1 裁满** —— 方格就该是方的（3:4 那个画框是卡片上的规矩，
   不是这一页的）。它里面套着两颗按钮（点图换一张 / 点角上那个 × 拿掉），
   所以容器是 div 不是 button：按钮里不能再套按钮 */
.write__thumb {
  position: relative;
  width: 96px;
  height: 96px;
  background: var(--surface);
  border-radius: 4px;
  overflow: hidden;
}
.write__thumbpic { display: block; width: 100%; height: 100%; padding: 0; border: 0; background: none; cursor: pointer; }
.write__thumbimg { display: block; width: 100%; height: 100%; object-fit: cover; }
/* 拿掉这一张：右上角那一块。它压在图上，所以是半透明黑底 + 白点；
   点得到的地方比看得见的那一块大一圈（手指点不准 20px 的东西），
   多的那一圈（左、下各 8px）用内衬让出来 —— 于是那一块黑和里头那个 ×
   都落在右上角 24×24 那块地方上，正好压在方格的那个角上 */
.write__drop {
  position: absolute;
  top: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0 0 8px 8px;
  color: var(--on-solid);
  background: none;
  border: 0;
  cursor: pointer;
}
.write__drop::before {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  width: 24px;
  height: 24px;
  background: rgba(0, 0, 0, .45);
  border-bottom-left-radius: 4px;
}
/* 那把叉原来是拿「＋」转 45° 转出来的（pixelarticons 里没有单独的叉）。
   2026-10-09 换成 Lucide 之后用它现成的 `x`，`transform` 那半条就撤了 ——
   留着反而会把一把正正经经的叉转成十字。
   **`position: relative` 那一半得留着**：右上角那块半透明的黑是 `::before`
   （绝对定位），静态定位的图标会被它盖在底下（黑压黑，等于看不见）。
   写上 relative 之后图标自己成一层，压在黑块上面 */
.write__drop .ico { position: relative; }
/* 那一排底下的说明：只在**有事要说**的时候才出现（正在传 / 传完了切掉多少 / 没传成）。
   手上没图的时候是空的 —— `<p>` 上的 margin 会让空落落的一块占 10px，
   所以空的这一条要连边距一起收掉（`display: none` 不是 `visibility`，
   后者照样占位）。2026-10-09 之前这一格进门就摆着
   「JPG / PNG / WEBP，不超过 4MB。传上来会先把照片里的定位信息切掉，再存。」
   `--err` 是这张图没传成的时候（那时候说明换成错的那一句） */
.write__note { display: block; margin: 10px 0 0; line-height: 1.7; }
.write__note:empty { display: none; }
.write__note--err { color: var(--red); }

/* 底下那几行「价格 / 联系方式 / 署名」：左边名字、右边填，行与行之间一道发丝线。
   微信那三行就是这个样子（所在位置 / 提醒谁看 / 谁可以看），
   所以这一块看上去该像一份表单，不像几个输入框。
   行高 52 → 48（2026-10-09）：三行 52 加上正文和配图，一页就长出屏幕一截，
   而每一行里其实只有一行字 —— 48 是「一行字 + 上下各 12」，
   手指仍然点得准（整行都可点，不是那两个字） */
.write__rows { border-top: 1px solid var(--rule-soft); }
.write__row {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 48px;
  padding: 4px 0;
  border-bottom: 1px solid var(--rule-soft);
}
/* `.write__rowline`（`div` 套一层 `label` 那个内壳）2026-10-09 撤了：它只为
   联系方式那一行存在 —— 那一行底下原本还挂着一句说明，句子得摆在 `label` 外面
   （不然点那句话也会跳进输入框），于是有了「外壳 + 内层 label」两层。
   说明撤了，那一行和上面两行长得一模一样，壳子就没用了 */
/* 左边那一格：一颗图标 + 两个字。图标 18px（图标集原尺寸 24 缩下来，
   和那一格 15px 的字同高）—— 三行都是「左边名字右边填」，光靠字读，
   眼睛要一行一行看过去；三颗图（钱 / 话筒 / 人）一眼就分得开 */
.write__key {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 15px;
  color: var(--ink-2);
}
.write__ico { display: inline-flex; flex: none; color: var(--ink-4); }
/* 右边那格：**没有框也没有底色**，一行就是一行 —— 微信那一行的值也是这么摆的。
   16px 和正文同档：手机上聚焦不放大，也是这面墙上唯一一处要人打字的地方 */
.write__val {
  flex: 1;
  min-width: 0;
  padding: 8px 0;
  font-family: inherit;
  font-size: 16px;
  line-height: 1.4;
  text-align: right;
  color: var(--ink);
  background: none;
  border: 0;
  outline: none;
}
.write__val::placeholder { color: var(--ink-4); }

.compose__field { display: block; margin-bottom: 14px; }
.compose__label { display: block; margin-bottom: 6px; }
.compose__in {
  display: block;
  width: 100%;
  padding: 10px 12px;
  font-family: inherit;
  font-size: 13px;
  line-height: 1.7;
  color: var(--ink);
  background: var(--surface-2);
  border: 1px solid var(--rule);
  outline: none;
  resize: vertical;
}
.compose__in:focus { border-color: var(--ink); background: var(--field-focus); }
.compose__in::placeholder { color: var(--ink-4); }
.compose__text { min-height: 88px; }
.compose__row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* 行里嵌的那格（昵称）不要再往下推一行的高度，间距由 .compose__row 的 gap 管 */
.compose__row > .compose__field { margin-bottom: 0; }
.compose__counter { margin-left: auto; }
.compose__counter--over { color: var(--red); }
.compose__note { margin: 12px 0 0; font-size: 12px; line-height: 1.7; color: var(--ink-3); }
.compose__error { margin: 12px 0 0; font-size: 12px; color: var(--red); }
.compose__error[hidden] { display: none; }

/* ---------------- 空状态 / 加载 ----------------
   .empty 照抄 deals 那份（80px 上下留白、居中、标题 15px 粗体、提示 12px 灰） */
.empty { padding: 80px 0; text-align: center; }
.empty__ico { display: flex; justify-content: center; margin-bottom: 14px; color: var(--ink-faint); }
.empty__title { font-size: 15px; font-weight: 700; }
.empty__hint { margin-top: 8px; font-size: 12px; color: var(--ink-2); }
.empty[hidden] { display: none; }

/* 骨架那一段（请求没回来时占住版面）**跟着一副牌一起撤了**（2026-10-08）：
   它量的是牌那套几何（一块 3:4 的图 + 三行字，宽度按牌上的 260px 收）。
   信息流不需要它 —— 一列里的位置本来就是「上面一条的下面」，
   没有「一张卡要正好进屏」这件事，回来时也不会跳一下 */

/* ---------------- 两张场景图（封面） ----------------
   墙上那张画「排队」，我那张画「还在等」。都是现成的像素动图，1536×1024（3:2）。
   它们现在占的是**封面**那个位置：贴着屏幕两边、压在本页内容最上面，往下滚它先走掉。
   所以这一块在 `.wrap` 外面（见 index.html —— 它们是 <body> 的孩子），宽就是屏宽。

   三条判断，前两条是量出来的：

   一、**这两张图几乎是全黑的一块，所以那道发丝线是替它们收边的**（2026-10-09 又回到
   这个活儿上了，见下面那条账）。把图 draw 进 canvas 数过：排队那张平均 rgb(14,9,20)、
   90.5% 的像素近乎纯黑；等待那张平均 rgb(6,3,2)、98.3% 近乎纯黑。
   账是这样的：**这条线要不要，取决于图和纸的亮度差**。
     · 黑纸白字那一版（2026-10-08 上午）：图和纸一样黑（对纸约 1.1:1），亮的那几个像素
       等于散在黑纸上，非得有一条线把它们收住不可；
     · 白纸那一版（2026-10-08 晚上到 2026-10-09）：一块黑图自己就是一条边（对纸 19.7:1），
       线只剩一件事 —— 把图和紧挨着它的那行说明分开；
     · **深纸这一版（2026-10-09）：线又承重了**（对纸 1.12:1，和第一版同一个量级），
       现在它两头都管：收住这张图，也把图和下面那行说明分开。量的：线（--rule #2e2e2e）
       压在图上 1.45:1、压在纸上 1.29:1 —— 图比纸还暗一点点，线正好卡在两者中间。
   当初留下这条线时写了一句「换纸的时候要么留着这条线，要么就别让说明贴着图」——
   这一趟就是那句话兑现的地方：线留着，所以什么都不用动。

   二、**宽交给屏幕，高自己定死**（`clamp(180px, 34vh, 420px)`）。
   3:2 的图铺满一行没有固定高宽比：1280 宽就是 853 高，一屏就没了 ——
   封面本来就是屏幕上的一条，不是一整屏。手机上这算得刚刚好，整张图几乎原样放下
   （375×667 是 227 高：原图 1024 行里露出 80–1010，亮像素留住 99.9%）；
   裁得狠的是桌面那一头，所以下面那条 `object-position` 只在那儿起作用。

   三、裁的只有竖直方向，`object-position: 50% 85%` 不是随手比的数：两张图的亮像素
   **逐行**数过，然后把这个位置从 70% 扫到 100%（1280×800，窗口 326 行）：

       位置   排队    等待
       50%   0.43    0.24     居中：两张都只剩小半
       80%   0.68    0.51
       85%   0.66    0.56     ← 取这个
       90%   0.58    0.56
       100%  0.33    0.41     贴底

   两张各自单独能拿到的上限是 0.69（窗口停在第 522 行）和 0.568（停在第 614 行）——
   窗口只有一个，取不到两个上限。85% 是**两张都还留着自己那一档 95% 以上**的那个位置
   （0.66/0.69 = 96%，0.557/0.568 = 98%）；再往上一档（90%）等待追平了上限，
   排队却掉到 0.58。排队那张从第 512 行起才是人（峰在 640）；等待那张是两团 ——
   224–383 一团、640–927 一团，中间 384–639 是空的：85% 落的是第二团那一段。
   手机上这个数无所谓（哪个位置都是 99.9% 以上），它只治桌面那头的大裁。

   动图停不住（CSS 关不掉 GIF）—— prefers-reduced-motion 那一段对它无能为力，
   这里没有加"藏着不给看"的补丁：宁可它在动，也不要把内容藏掉 */
/* 封面这一条（画 + 下面那一行说明 + 右下角那张脸）。
   2026-10-08 起它比原来高一点：那张半垂在画外的脸把它撑开了，
   底下留 14px，免得信息的头一条贴着那张脸 */
.scene { position: relative; margin: 0 0 14px; }
.scene[hidden] { display: none; }
.scene__img {
  display: block;
  width: 100%;
  height: clamp(180px, 34vh, 420px);
  object-fit: cover;
  object-position: 50% 85%;
  border-bottom: 1px solid var(--rule);
  /* 底色跟着纸走：图里万一有透明的地方，透出来是纸色 —— 深色这一版透出来是深纸。
     那条发丝线在深纸上是**承重的**，理由见上面「一、」那一段 */
  background: var(--paper);
}
/* 说明那行字和右下角那张脸排在**同一行**：说明在左、脸在右。
   两样都跟着内容那一列走（`class="scene__foot wrap"`），不跟着图居中 ——
   图是铺满的，字要是也铺满就会顶在屏幕边上，宽屏上更是离内容十万八千里。

   这一行**自己不带上下内衬**：它的上边界就是画的下边界，那张脸往上提多少才
   正好是一半压画、一半垂在画外（见 .scene__me —— 这笔账按「行顶＝画底」算的）。
   要给它添内衬得写成 `padding-top: 10px` 这样的长写：`.wrap` 的
   `padding: 0 var(--pad)` 是简写，在这儿再写一遍会把左右内衬一起抹平
   （老账见 `.mine` 那一条）。说明那行自己的 10px 挂在 `.scene__cap` 上。

   ⚠️ **「自己不带上下内衬」这半句 2026-10-09 才发现是错的**：底内衬它一直有 **60px**，
   而且是从 `.wrap` 来的 —— `.wrap` 那条写的是 `padding: 0 var(--pad) 60px`，
   那 60 是给**页面外壳**留的（墙上、我那一页、消息那一页：屏上最后一样东西不许
   贴着屏幕底）。这一行是**封面里的一行**，离页尾远着，跟着吃就成了封面和信息流之间
   一条 60px 的空档 —— 见下面 `.scene__foot` 那一条把它抹掉。 */
.scene__foot {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}
/* 底内衬归零（2026-10-09）。用户：「主页的头像和第一条帖子间距过大了，我看微信朋友圈
   间距没那么大」—— 量出来那张脸的下沿到第一条之间是 **82px**，拆开正好三笔：
   `.wrap` 的 60（见上）、`.scene` 的 `margin-bottom` 14、`.feed` 的 `padding-top` 8。
   那 60 不是为这儿写的，所以在自己这一条上抹掉它，剩下 22（我那一页是同一处：80 → 20）。
   这一条**得排在 `.wrap` 之后** —— 两边都是 (0,1,0) 的权重、后写的赢
   （`.wrap` 在文件前半截，这一条在封面那一段里，顺序是稳的）。 */
.scene__foot { padding-bottom: 0; }
/* 那一行说明：跟着 `.label` 那一档（10px / 0.12em），离画 10px
   （原来整行都在画下面时，这 10px 就在这一格上）。行高给足 ——
   手机上这行字要折成两行，1.7 折起来才不挤 */
.scene__cap { padding-top: 10px; line-height: 1.7; }

/* 封面右下角那张脸（2026-10-08）：朋友圈背景图右下角就是它，点它进自己那一页。
   56×56 是手指点得准的那条线（44）再宽一档 —— 它是这一页上第二处入口，
   又压在一张动图上，大一点才敢点。
   `margin-top: -28px` 把自己的一半提到画上去：行顶就是画底，所以这一下
   正好一半压在画上、一半垂在画外，正是朋友圈那颗头像的位置。
   往上提多少由**自己的一半**定，改了上面那个 56 就得跟着改这个数 */
.scene__me {
  position: relative;
  flex: none;
  display: block;
  width: 56px;
  height: 56px;
  margin-top: -28px;
}
/* 那颗红点数钉在脸的右上角、往画外挪出去一点（`.badge` 的底子在上面「浮着的图标」
   那一段）。脸上的那颗和图标上那颗是同一个东西，只是母体不同 */
.scene__me .badge { top: -7px; right: -7px; }
/* 一道发丝线 + 4px 圆角：这 42 张脸是照片，一半压在画上、一半垂在白纸上，
   两边都不是它的底色，没有边就散进去了（和信息流里的小脸是同一个判断） */
.scene__face {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 4px;
  border: 1px solid var(--rule);
  background: var(--surface);
}
/* 这里原来有一句 `.scene__me:hover .scene__face { opacity: .8 }` —— **2026-10-09 撤了**
   （用户：「头像不要点一下变透明」）。为什么手机上特别明显：`:hover` 在触屏上是**粘的**，
   点一下之后那个样式会一直挂着，直到你点别处 —— 于是头像就一直停在 80% 不透明，
   看着像坏了。桌面上鼠标压上去也会淡一下，一并撤掉。
   代价是**这一下没有按压反馈了**（它还是个 `<a>`，桌面上 `cursor: pointer` 还在，
   手机上没有任何变化）。要反馈的话别再用透明度 —— 那正是用户不要的那个效果。
   撤掉之后下面那句「没有悬停那一下」才真的成立：以前两个封面共用 `scene__me`，
   「我」那一页那个不可点的 `<span>` 照样会被这条规则压淡，和它自己的注释对不上。 */
/* 我那一页上它是**辨认**，不是入口（已经站在自己的朋友圈里了，点它没地方去），
   所以它是个 <span>：不可点，也没有悬停那一下 */
.scene__me--here { cursor: default; }

/* ---------------- 我的（回执） ----------------
   只存本机发过的愿望 id，回执去服务器现取 —— 服务器不知道你是谁，
   这台设备也不存别人能看的东西 */
/* 同上：`class="mine wrap"`，只用 padding-block，别拿简写把左右内衬抹掉 */
.mine { padding-block: 6px 60px; }
.mine[hidden] { display: none; }
.mine__h { margin: 16px 0 4px; font-size: 14px; font-weight: 700; }
.mine__hint { margin: 0 0 18px; font-size: 12px; line-height: 1.8; color: var(--ink-3); }
.mine__list { display: flex; flex-direction: column; gap: 3px; }

/* 一条回执。左边那道 2px 的竖线是状态条：暗＝还在等，亮＝有人在做，红＝已实现 */
.receipt {
  padding: 12px 0 12px 12px;
  border-left: 2px solid var(--rule-soft);
}
/* 我发过的那几条（2026-10-08 起）左上也有一张脸：这一半是「我发过的愿望」，
   朋友圈里自己的那一条也是脸 + 名字打头 —— 墙上那些条是别人的脸，
   这几条是我的。脸和名字由 app.js 的 receipt() 摆在 .receipt__main 前面，
   所以这一种变成 40px + 正文 两列（和 .post 同一份几何）。
   ⚠️ **只有 .receipt--mine 是两列**：下面「我申请过的」那几条（askedCard）没有脸，
   它们仍是一条整行的回执。这一条区别是量出来的：第一版把两列写在 .receipt 上，
   结果申请过的那几条被塞进 40px 的第一列，一句 153px 高的话挤成 40px 宽 */
.receipt--mine {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr);
  gap: 12px;
}
.receipt--mine > .receipt__main { min-width: 0; }
/* 刚提交的那一条：底色闪一下，让眼睛从上面的表单跟到下面这一行。
   闪底色而不是描边：左边那道 2px 竖线是状态条，外面再套一圈框会把这条规矩看乱 */
.receipt--hit { animation: receipt-hit 1.4s ease; }
@keyframes receipt-hit {
  0%, 100% { background: transparent; }
  10%, 60% { background: var(--red-wash); }
}
.receipt--claimed { border-left-color: var(--ink); }
.receipt--done { border-left-color: var(--red); }
.receipt__top { display: flex; align-items: center; gap: 8px; }
.receipt__text { margin: 6px 0 0; font-size: 14px; line-height: 1.7; word-break: break-word; }
/* 收据里那一行东西（小图 + 名称 + 价格）跟在正文后面 */
.receipt .pcard--row { margin-top: 10px; }
.receipt .pcard--row .pcard__name { font-size: 13px; }
.receipt__acts { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
.receipt__form { margin-top: 10px; }
.receipt__form[hidden] { display: none; }
.receipt__done { margin: 8px 0 0; font-size: 12px; color: var(--ink-3); }

/* ---------------- 联系方式、评论、谁申请了 ----------------

   2026-10-08 加到卡片上的三块。它们和卡上别的字有一条共同的分别：**都是关于人的**,
   不是关于那件东西的 —— 所以三块各自带一个抬头（小字标签 + 一个图标），
   和商品那一摞（.pcard）分开。 */

/* 自己写的价。挑了一件东西的那条不摆它（价从那件东西来，比手写的准），
   所以这一行只出现在「传了张图」「就写字」的卡上。字号取 18：比正文大一档，
   比商品卡那行 22 小一档 —— 它是自己填的一个数，不是官网上量出来的数 */
.wish__price {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin: 0;
  font-family: var(--num);
  font-size: 18px;
  font-weight: 500;
  line-height: 1.2;
}
.receipt .wish__price { font-size: 15px; }

/* 联系方式那一格。左边一道竖线，和 .quote 同一个写法 —— 这一格讲的也是一件
   「别人做过的事」，不是卡上的装饰。**不铺底**：卡里唯一一处底色是底部那一块
   （.acts__feed，朋友圈那个灰）—— 那是「别人对这条的反应」，属于另一层；
   这一格是这条愿望自己的，守在纸上 */
.contact {
  padding: 2px 0 2px 12px;
  border-left: 2px solid var(--rule);
}
/* 结果出来了、还没在「我要许愿」那一页看过：那道线转成红色。
   红色全站只有两处用处，这是第二处 —— 「有回执待处理」。灭红点的地方是那一页，
   不是这张卡：卡片是一次一张翻的，翻到哪张是碰运气（见 app.js 的 markAskSeen） */
.contact--news { border-left-color: var(--red); }
.contact__head { display: flex; align-items: center; gap: 6px; }
.contact__head .ico { color: var(--ink-4); }
.contact__note { margin: 6px 0 0; font-size: 12px; line-height: 1.8; color: var(--ink-2); }

/* 点头之后那串字：整页最该看清、最该抄走的一行。给一圈发丝线（没有底色），
   字号抬到 15 —— 它可能是一个手机号，别让人眯着眼核对
   user-select: all：手指点一下就是全选，长按直接出复制菜单。它本来就是一整段
   要拿走的东西，选一半没有意义 */
.contact__text {
  margin: 8px 0 0;
  padding: 8px 10px;
  font-size: 15px;
  line-height: 1.7;
  color: var(--ink);
  border: 1px solid var(--rule);
  white-space: pre-wrap;
  word-break: break-word;
  user-select: all;
  -webkit-user-select: all;
}

/* 赞 + 评论：朋友圈底下那一块。摆法、灰底、顶上那个小三角都是朋友圈的 ——
   正文下面先是「时间 / 那两个点」那一排（.wish__foot），再往下才是这一块，
   三角正指着上面那两个点。

   这一格**铺朋友圈那个灰**（--surface）。原来它不铺：上一版全站是黑纸，
   垫底的是「比纸亮一格」那几处，一块灰的和它们不是一档。2026-10-08 翻成白纸之后
   灰成了这一块唯一的身份 —— 它是「别人对这条的反应」，和这条愿望本身不是一层
   （朋友圈也是这么分的）。

   这一格**永远在**（浮层里那格「评论」要把输入框掀开，它就在这儿等着），
   但空着的时候一个像素都不占：收放由 app.js 的 syncActs 算 —— 有赞或有评论就一直在，
   否则要等那个输入框掀开（浮层里那两格不在这块灰的里面，所以算不到它们头上）。

   那个小三角（::before）指着上面那两个点。位置是从右边量的：两个点那颗按钮 34 宽
   （手机上 44），它的中心离卡片右沿 17px（手机上 22），三角宽 12 ——
   `right: 11px` 正好把它摆在正中间（手机上 16px，见窄屏那一段）。
   三角画在灰块的**外面**（top: -6px），所以它不会把灰块撑高 */
.acts__feed {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px 10px;
  background: var(--surface);
}
.acts__feed::before {
  content: '';
  position: absolute;
  top: -6px;
  right: 11px;
  width: 0;
  height: 0;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-bottom: 6px solid var(--surface);
}

/* 这一块头顶那两颗（「赞 | 评论」，一人一半、中间一道竖线）**搬走了**（2026-10-08）：
   它们现在长在**两个点弹出来的那一片深色浮层**里（见下面 `.dots__menu`），
   不在这块灰的里面。原因就一句：朋友圈那两颗是在浮层里，不是在互动区里。
   这块灰的（互动区）只摆**结果** —— 谁赞过、谁说了什么 —— 不摆那两个选择 */

/* 底下**不划线**：这一块里的几个格子要么是结果、要么是输入框，
   一块灰的里再横着切一刀，切出来的两半还是同一件事 */

/* 赞那一行。**这颗心不是红的** —— 红全站只给「已实现」和「有回执待处理」。
   我点过的时候它亮一档（--ink-3 → --ink），换的是明度不是色相。
   色阶比原来（--ink-3）深一档：这一块自己铺了灰，灰上的同一档灰要掉一档
   （--ink-3 在白纸上是 4.24:1，落到 --surface 上只剩 3.96:1）——
   量过 --ink-2 在这块灰上是 5.78:1（README 里原来记的 5.6 重算过），
   和底下那些评论（--ink-2）也就齐了。
   2026-10-09 翻深色：这一条**不用改** —— 同一个 --ink-2 落在 --surface 上是 4.90:1，
   还在 AA 那条线上，和白天那一档（5.78:1）也还挨得近 */
.acts__likes {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  font-size: 12px;
  line-height: 1.6;
  color: var(--ink-2);
  word-break: break-word;
}
.acts__likes .ico { flex: none; color: var(--ink-3); }
.acts__likes--mine { color: var(--ink); }
/* 自己点过的那一颗**填实**。
   2026-10-09 换图标之前，这一条只管颜色：像素那套的心本来就是一大块实心，
   灰的和黑的一眼分得出。Lucide 的心是**描边**的，只改颜色就只剩「灰的圈 / 黑的圈」，
   差别淡了 —— 所以这里补一个 `fill: currentColor` 把它填上（图标自带
   `fill="none"`，是**表现属性**，样式表里的 fill 压得过它）。
   整颗心也因此从「一个圈」变成「一块墨」，和旁边那行灰字分得开 */
.acts__likes--mine .ico { color: var(--ink); fill: currentColor; }
.acts__likes .label { color: var(--ink-3); }

/* 一格一格的间距全交给上面 `.acts__feed` 的 gap（所以这里 margin 一律是 0）：
   露出来的都有哪几行是变的（有赞没评论、掀开输入框……），
   一行一行地推边距迟早会推出两处不一样的空隙 */
.acts__list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* 一条评论：左边一道更弱的线（比 .quote 弱一档 —— 认领留言是这条愿望的主线，
   评论是旁边的声音）。线是 --rule 不是 --rule-soft：这一块自己铺了灰，
   灰上的发丝线得比纸上的实一档才看得见（--rule-soft 落在 --surface 上差 7‰，
   等于没画） */
.acts__item { padding-left: 10px; border-left: 1px solid var(--rule); }
.comment__text { margin: 0; font-size: 13px; line-height: 1.7; color: var(--ink-2); word-break: break-word; }
.comment__foot { display: flex; align-items: center; gap: 10px; margin-top: 3px; }
.acts__more { display: block; margin: 0; }

/* ---------------- 两个点（赞 / 评论） ----------------
   朋友圈每条底下右下角那两颗点。位置就是 .wish__foot 那一排的最右端
   （那一排是 flex，时间那一格 flex:1 把剩下的地方都占了，所以它自然靠右）。

   **点它弹出来的是一片深色的小浮层**，两格：「赞」和「评论」，中间一道竖线，
   右下角一个小尖指着那两个点。这是朋友圈那一层原样 —— 它是浮在**上面**的
   （从下往上弹，盖住上一条评论），点别处、按 Esc、或者选了其中一格就收起来。

   这一片中间改过两回，记在这儿免得再来回翻：
     · 2026-10-08 中午那一版做成了**铺在卡片里**的两格灰底按钮，注释里写着
       「朋友圈里那两个点掀开的是铺在卡片里的那一块」—— 那句话是记错了。
       朋友圈掀开的是一片深色浮层；**铺开的**是底下那块灰的「互动区」
       （`.acts__feed`，有赞或有评论时才在，顶上那个小三角指着这两个点）。
       两件事各是各的：浮层是**一次性的选择**，互动区是**留下来给别人看的结果**。
     · 这一版改回浮层，就是为了那句「用户打开就会用」—— 朋友圈里这一下
       是最好认的一个动作。

   浮层挂在 `.dots` 上（那一格 position: relative），`bottom: 100%` 从下往上弹。
   一列卡片里没有 `overflow: hidden`（文件里只有 `.post__face` 自己裁角、
   `.pcard__img` 自己裁图），所以它不会被切掉；
   `z-index: 5` 只管压住上面那一条愿望的字。

   色用 --ink-3 不用 --ink-4：--ink-4 那一档在白纸上只有 3.26:1（深纸上是 3.30:1），
   而这颗图标是这一块唯一的入口，得比小字清楚一点 —— 深色这一版照旧是 --ink-3（4.21:1） */
.dots { position: relative; display: inline-flex; }

.dots__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 24px;
  padding: 0;
  color: var(--ink-3);
  background: none;
  border: 0;
  cursor: pointer;
}
.dots__btn:hover,
.dots__btn[aria-expanded='true'] { color: var(--ink); }
/* 焦点圈只有键盘走到时才画（.btn 那一套同一条规矩） */
.dots__btn:focus-visible { outline: 2px solid var(--ink-3); outline-offset: 1px; }

/* 那一片浮层。收着的时候是 `hidden` —— 文件顶上那条
   `[hidden] { display: none !important }` 压得住这里的 flex，
   也顺手把它从 Tab 能走到的地方摘掉了（不用再写 inert）

   **`width: max-content` 这一条不能省。** 浮层是绝对定位的，它挂着的 `.dots`
   只有那颗点那么宽（手机上 44）；只写 `right: 0` 不写 `left` 的时候，
   浏览器把「可用宽度」算成那颗点的宽度，于是浮层被压成窄窄一条，
   「评论」两个字只能一个字一行 —— 量过：两格变成 47 宽、72 高，
   一个竖着的黑条。`max-content` 让它按内容取宽，不理会那颗点有多窄 */
.dots__menu {
  position: absolute;
  right: 0;
  bottom: calc(100% + 8px);
  z-index: 5;
  display: flex;
  align-items: stretch;
  width: max-content;
  background: var(--block);
  border-radius: 6px;
}
/* 小尖：指着下面那两个点。它画在浮层**外面**（bottom 是负的），所以不占浮层的高。
   `right: 11px` 和 `.acts__feed::before` 那一个数同一处算法 —— 两个点那颗按钮
   34 宽，中心离这一排右沿 17px，尖宽 12，17 − 6 = 11 */
.dots__menu::after {
  content: '';
  position: absolute;
  right: 11px;
  bottom: -6px;
  width: 0;
  height: 0;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-top: 6px solid var(--block);
}
/* 两格：光秃秃两个字，白字不带图标（朋友圈那两格也不带）。
   **两格一样宽**（各 64）：「赞」一个字、「评论」两个字，各按各的内容取宽
   会一窄一宽，看着像两个东西；朋友圈那两格是一样的。
   高给到 36（手机上 44，见窄屏那一段）—— 这是一颗要手指点准的东西。
   `nowrap` 是兜底：万一以后有人往这两格塞更长的词，它会撑开而不是折成两行 */
.dots__act {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  min-height: 36px;
  padding: 0;
  font: inherit;
  font-size: 15px;
  white-space: nowrap;
  color: var(--on-solid);
  background: none;
  border: 0;
  cursor: pointer;
}
.dots__act:active { opacity: .6; }
/* 两格之间一道竖线：它们是一件事的两个选择，不是一个列表里的两项。
   这一条**两趟都一样**（半透明白压在深块上）：白纸那一版压在纯黑上是 2.26:1，
   深色这一版压在 --block 上是 2.41:1 —— 同一件事，不用翻 */
.dots__act + .dots__act { border-left: 1px solid rgba(255, 255, 255, .28); }
.dots__act:focus-visible { outline: 2px solid var(--on-solid); outline-offset: -3px; }

/* 删掉：只有作者看得见。写成一个字，不写成一颗按钮 —— 一列评论里挂着一排按钮，
   那一列就变成操作面板了。手机上它会撑到 44px（见窄屏那一段） */
.comment__del {
  padding: 0;
  font: inherit;
  color: var(--ink-4);
  background: none;
  border: 0;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.comment__del:hover { color: var(--red); }

/* 说一句那一行。**在那一块灰的里面**（最底下），所以它自己不带线也不带边距 ——
   上下留白交给 `.acts__feed` 的 padding 和 gap。它上面那一样东西可能是赞那一行、
   可能是评论、也可能什么都没有（掀开它的那两格在浮层里，不在这块灰的里面），
   固定往哪一侧推边距都会推出错的那一处。
   收着的时候 display:none，一个像素不占（那一块本身收不收由 syncActs 算） */
.comment__form {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.comment__form[hidden] { display: none; }
/* 输入框占满剩下的地方，两颗按钮跟在后面。手机上整行都会摊开（见窄屏那一段）。
   底**换成纸色**：`.compose__in` 那层 --surface-2 和这一块自己那层灰（--surface）
   挨得太近，摆进去等于没边框 —— 纸色才看得出这是能打字的地方。
   白纸那一版的方向是「比这块灰更白」（#fff 对 #f7f7f7，1.07:1）；
   深色这一版反过来是「比这块灰更暗」（#191919 对 #2c2c2c，1.26:1）。
   方向反了，但都是同一个动作：**从这块灰里挖出来**。深色这一版这一步还更实一点 */
.comment__in { flex: 1 1 200px; min-width: 0; padding: 8px 10px; background: var(--paper); }

/* 收据里那一块：谁申请过看我的联系方式。一列待办，每条右边两颗按钮 */
.receipt__asks { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--rule-soft); }
.receipt__askhead { display: flex; align-items: center; gap: 6px; }
.receipt__askhead .ico { color: var(--ink-4); }
/* 这行说实话的小字和 .receipt__done 同一档（12px）：它是一句要说清的话，
   不是标签 —— 所以不带 label 类。
   （这一档原来有三个用户，「谁申请过看我的联系方式」那一块尾巴上那句
   2026-10-09 用户点名删了 —— 见 README「说明书式的文字删了一批」。
   剩下两个都在「我申请过的」那一侧，所以这条规则不走。） */
.receipt__asknote { margin: 8px 0 0; font-size: 12px; line-height: 1.8; color: var(--ink-3); }
.ask {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 8px;
  padding-left: 10px;
  border-left: 2px solid var(--rule-soft);
}
/* 等着我点头的那几条：线亮起来，和墙上那张卡上的「你留了联系方式」是同一句话 */
.ask--todo { border-left-color: var(--ink); }
.ask__acts { display: flex; gap: 8px; margin-left: auto; }

/* ---------------- 消息（#/msgs） ----------------
   别人发到我这儿的联系方式。入口在「我发过的愿望」那一页封面的右上角 ——
   那颗消息图标（见上面「封面右上角那两颗浮着的图标」）。
   版面上它就是刚才那两节合成的一列：一条一条还走回执那套几何（左边一道 2px 状态线），
   只是排序换成了**时间倒着排** —— 这一页要说的是先后：谁先给你看了、谁还在等你点头。
   所以同一件事的两头在这儿第一次挨着摆（在 #/mine 上它们隔着一屏）。 */
/* 上面那 6px 是这一页自己的呼吸；再加一枚安全区，是因为这一页顶上没有封面替它让位 ——
   `viewport-fit=cover` 之后标题就顶在屏幕最上沿，不加这一枚会被灵动岛吃掉
   （两头都写在 `padding-block` 里：底色铺满整屏，所以让开的是字，不是那条底） */
.msgs { padding-block: calc(6px + env(safe-area-inset-top)) 60px; }
.msgs[hidden] { display: none; }
.msgs__h { margin: 16px 0 4px; font-size: 14px; font-weight: 700; }
.msgs__hint { margin: 0 0 18px; font-size: 12px; line-height: 1.8; color: var(--ink-3); }
/* 这一页上没有封面，所以也没有那颗浮图标 —— 回去的路只有浏览器返回和这一颗。
   全站不用蓝（用户 2026-10-06 那条），链接和周围那行灰字的分别只在底下那根线 */
.msgs__hint a { color: var(--ink-2); text-decoration: underline; text-underline-offset: 2px; }
.msgs__hint a:hover { color: var(--ink); }
.msgs__list { display: flex; flex-direction: column; gap: 3px; }
/* 一条消息的抬头：一句说明 + 编号（+ 结果刚出来时那颗红点） */
.msg {
  padding: 12px 0 12px 12px;
  border-left: 2px solid var(--rule-soft);
}
/* 等着我点头的那几条：线亮起来 —— 和收据里那几条 `.ask--todo` 同一句话 */
.msg--todo { border-left-color: var(--ink); }
.msg__top { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.msg__top .ico { color: var(--ink-4); }
.msg__text { margin: 6px 0 0; font-size: 14px; line-height: 1.7; word-break: break-word; }
/* 照实说的那些小字，和 `.receipt__done` / `.receipt__asknote` 同一档（12px）：
   它们是一句要说清的话，不是标签 —— 所以不带 label 类 */
.msg__note { margin: 8px 0 0; font-size: 12px; line-height: 1.8; color: var(--ink-3); }
.msg__acts { display: flex; align-items: center; gap: 8px; margin-top: 10px; }

/* 认领表单（.pledge）和写愿望那一页共用这几格：一格 = 一行小字标签 + 一个输入框。
   写愿望那一页自己那三行（价格 / 联系方式 / 署名）是另一套（见 .write__row），
   所以这里只剩认领留言、评论框在用。
   原来这儿还有两条 —— 价格和署名那两格「窄一些」（max-width 200px）：
   它们现在是 .write__row 里的值，贴在行的右边，宽度由那一行管了 */
/* 联系方式那一格底下原来挂着一句说明（`.compose__hint`），2026-10-09 撤了 ——
   那一句连同「写上去之后谁都能看见…」那一段一起走的，两条规则都没人用了 */

/* ---------------- 两个人的话（#/talk/<编号>） ----------------
   一段只有两个人看得见的对话。版面上故意和卡片划清界限：卡片是一块块贴在
   发丝线上的纸，这一段是**一栏字** —— 每句话左边一道竖线，自己说的那道是墨色、
   底下一层比纸亮一格的底。没有气泡、没有圆角，还是纸和线。

   一栏而不是两栏（一边一句）：一句话能写到 300 个字，分左右两栏在手机上
   就变成巴掌宽的一条，读不动。谁说的靠左边那道线和抬头那两个字认。 */
/* 同上：`class="talk wrap"` */
.talk { padding-block: 6px 60px; }
.talk[hidden] { display: none; }
.talk__h { margin: 16px 0 4px; font-size: 14px; font-weight: 700; }
.talk__hint { margin: 0 0 18px; font-size: 12px; line-height: 1.8; color: var(--ink-3); }

/* 顶上是「说的是哪一条愿望」：隔几天再进来，得认得出它。
   一栏读到 640px 就够 —— 再宽一行字就太长了，眼睛找不着下一行 */
.talk__wish { max-width: 640px; }
.talk__card {
  padding: 12px 0 12px 14px;
  border-left: 2px solid var(--rule);
  border-top: 1px solid var(--rule-soft);
  border-bottom: 1px solid var(--rule-soft);
}
.talk__top { display: flex; align-items: center; gap: 8px; }
.talk__card .pcard--row { margin-top: 10px; }
.talk__quote { margin: 8px 0 0; font-size: 14px; line-height: 1.7; color: var(--ink-2); word-break: break-word; }

.talk__list { max-width: 640px; margin: 22px 0 0; padding: 0; list-style: none; }
.talk__none { display: block; padding: 2px 0 2px 10px; border-left: 1px solid var(--rule); line-height: 1.9; }
.talk__msg {
  margin: 0 0 16px;
  padding: 8px 0 8px 12px;
  border-left: 1px solid var(--rule);
}
/* 自己说的：线变亮、垫一层比纸亮一格的底。对话里唯一一处底色块 ——
   一栏对话里，这是最省事又最不吵的「换个方向」 */
.talk__msg--mine { border-left-color: var(--ink); background: var(--surface); }
.talk__who { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.talk__text { margin: 5px 0 0; font-size: 14px; line-height: 1.8; white-space: pre-wrap; word-break: break-word; }
/* 拆不开的那几句话（换过认领的人）：不装作看得见，就说读不出来 */
.talk__text--lost { color: var(--ink-4); font-size: 12px; line-height: 1.9; }

.talk__form { max-width: 640px; margin-top: 22px; padding-top: 16px; border-top: 1px solid var(--rule); }
.talk__form[hidden] { display: none; }
.talk__error { margin: 10px 0 0; font-size: 12px; line-height: 1.7; color: var(--red); }
.talk__error[hidden] { display: none; }

/* 底下这一段是这张纸上最要紧的一段实话：加密保住了什么、代价是什么 */
.talk__note {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  max-width: 640px;
  margin: 18px 0 0;
  padding-top: 10px;
  border-top: 1px solid var(--rule-soft);
  font-size: 12px;
  line-height: 1.9;
  color: var(--ink-3);
}
.talk__note .ico { margin-top: 4px; }
.talk__note b { font-weight: 600; color: var(--ink-2); }

/* 卡片上那个入口旁边：有话没看的小红点、和已经说了几句 */
.dot { flex: none; width: 6px; height: 6px; background: var(--red); }
.talk__count { color: var(--ink-4); }
.pledge__note { display: block; margin: 8px 0 0; line-height: 1.8; }

/* ---------------- 页脚 ---------------- */
/* 页脚整个撤了（2026-10-09，用户把那两段字一起删了：「这里没有钱。全站不收款…」和
   「商品和价格借隔壁那份捡漏榜…」）。它原来占两行：一行讲这面墙怎么对待钱和联系方式，
   一行是出处（捡漏榜、那 42 张脸、Pixelarticons）+ 实时那几个数。
   那两行连同 `.foot__line` 一整套样式和那两颗图标（`shield` / `star`）一起走了 ——
   这一版不用的不留。**那 60px 的底部留白没跟着走**：它挪到 `.wrap` 上了（见那一行），
   不然最后一条愿望会贴着屏幕底。出处那一句在 README 里留着（Pixelarticons 的 MIT 署名）。 */

/* ======================== 二、窄屏 ========================
   这个站的用户主要在手机上（2026-10-08 明确过），所以这一段按**手指**来定：
   可点的地方至少 44px（iOS 人机指南那条，也是 WCAG 2.5.5 的建议值）、
   输入框 16px（小于 16 时 iOS 一聚焦就把整页放大一档）、正文不缩。
   下面每个数都是在 375×667（iPhone SE 那一档，还找得到的最窄的屏）上量的。 */

/* 触屏上输入框字号必须 ≥16px，否则 iOS 一聚焦就自动把整页放大一点。
   原来只给 `.search` 那一格写了 —— 手机上真正要打字的是许愿面板那几格
   （正文、昵称、认领留言），量出来全是 13px，正好都在会触发缩放的那一档。
   类名得一个一个点出来：`.compose__in` 和 `.search` 都是类选择器（0,1,0），
   比裸 `input`（0,0,1）更具体 —— 只写元素名的话，这两条谁也改不动
   （第一版就是这么写的，量出来还是 13px） */
@media (max-width: 900px) {
  input, textarea, select,
  .compose__in { font-size: 16px; }
}
/* 手机上的小内衬（原来这一档里还有一条「搜索框铺满整行」，
   搜索框跟着「榜上挑一件」撤了，2026-10-08） */
@media (max-width: 760px) {
  /* 封面顶上那两颗浮图标在手机上是 44×44 的正方（本来就是），字号那一类都不沾。
     这里只把它离屏幕边那点内衬收窄 —— 它跟着 `--pad` 走，375 上就是离边 8px */
  :root { --pad: 8px; }

  /* 手指能按的按钮。形状不再跟着手指变（那两种形状是微信量出来的，见 .btn 那一段），
     变的只有**点得到的地方**：文字按钮那一行字本来只有 32 高，手机上撑到 44
     （iOS 人机指南那条，也是 WCAG 2.5.5 的建议值）；左右再给一点，
     不然一排文字按钮会粘在一起（放不下就换行）。
     **绿底那一颗不跟着长**（下一条）：它一长就是一块绿砖，一列卡上挂七八个 ——
     用户 2026-10-09 那句「我来实现的按钮太大太笨重」「发表按钮太大太笨重」说的就是它。
     这一条必须写在 `.btn` 后面：两个选择器权重一样，靠后那条赢 */
  .btn { min-height: 44px; padding: 4px 10px; }
  .btn--primary { min-height: 32px; padding: 0 12px; }

  /* 写愿望的人自己那段话是全卡最该读的东西。朋友圈里一条的正文就是 17px，
     手机上这里跟着抬到 17 —— 一屏只看得见两条，读得下去比看得见几条要紧 */
  .wish__text { font-size: 17px; }

  /* 评论那一行在手机上摊开：输入框占一整行，两颗按钮跟到下面一行。
     挤在一行里的话，输入框只剩 150px 宽，写的字还没框长 */
  .comment__in { flex: 1 1 100%; }
  .comment__form .btn { flex: 1 1 auto; }
  /* 「删掉」也要手指点得中：它是一行 10px 的小字，量出来 20×14。
     撑高不撑宽 —— 它右边就是那条评论的时间，加宽会把两样挤在一起 */
  .comment__del { display: inline-flex; align-items: center; min-height: 44px; }
  /* 右下角那两个点要手指点得中（34×24 是鼠标的尺寸）。
     撑高不影响卡脚那一排的高度：同一排里「我来实现」本来就是 .btn，手机上
     已经是 44px 了（见上面那条）。那颗按钮宽了 10px，
     指着它的那个三角跟着往左挪 —— 44 宽的中心离右沿 22px，三角宽 12，
     所以 right 从 11px 挪到 16px。**两个三角都挪**：互动区顶上那个，
     和那两个点弹出来的深色浮层底下那个 */
  .dots__btn { width: 44px; height: 44px; }
  .acts__feed::before { right: 16px; }
  .dots__menu::after { right: 16px; }
  /* 浮层里那两格在手机上也要 44 高（36 是鼠标的尺寸） */
  .dots__act { min-height: 44px; }
  /* 联系方式那串字在手机上给足行高：可能是一个手机号，别让人眯着眼核对 */
  .contact__text { font-size: 16px; }
  /* 封面上那张脸在手机上再大一点（56 → 64）：它压在动图上，
     375 的屏里它是右手边唯一一处入口，大一圈才敢点准（垂在画外的那一半跟着涨到 32） */
  .scene__me { width: 64px; height: 64px; margin-top: -32px; }

  /* 手机上没有横向可滚的东西。上一轮这条是为牌的拖动（往右最多约 130px）和
     入场动画留的，牌撤了（2026-10-08），但**这笔账还作数**：商品名是 `nowrap`
     （要省略号就得不断行），一条「童装男童女童 法兰绒格子衬衫…」量出来 379px，
     比 375 的屏还宽。信息流里那些 `minmax(0, 1fr)` 是为它留的，这一条是兜底。
     `clip` 不是 `hidden`：它不建滚动容器，封面顶上那颗浮图标（fixed）照旧钉着 */
  body { overflow-x: clip; }
}
