/* 唱给时光听 · 全部样式
 *
 * 零依赖：不引字体文件、不引图标库、不引 CSS 框架（`界面与信息架构.md` 1.1）。
 * 图标一律内联 SVG，颜色一律 currentColor——**她的手机要是哪次没连上，
 * 一张外部字体就足以让整屏变成方块**，而这个 App 里没有一个字是装饰。
 *
 * 结构：
 *   1. 设计 token
 *   2. 基础与排版
 *   3. 壳：底栏、悬浮球
 *   4. 零件
 *   5. 各屏
 *   6. 小哆的浮层
 *   7. 减弱动态
 *   8. 设置页
 */

/* ── 1. 设计 token ─────────────────────────────
 *
 * ⚠️ **所有尺寸和颜色只在这儿定义一次。** 页面里任何一个地方写死
 * `18px` 或 `#FBC26A`，就是把"以后改一次全站生效"这件事作废一次。
 * 改界面的成本几乎全在这一节。
 */
:root{
  /* —— 字体 ——
   *
   * ⚠️ **不引 web 字体。** 中文字体子集动辄 1–3 MB，而首屏预算是
   * 200 KB、她那边是 4G；更要命的是字体文件挂了整屏变方块。
   *
   * 所以走**点名系统字体**这条路。要点在**顺序**：
   * 浏览器是**逐字符**往下找的，`system-ui`（安卓 Roboto / 苹果 SF）
   * 里没有汉字，轮到中文时才会继续往下落到我们点名的中文字体。
   * 于是：
   *   - 苹果 → PingFang SC
   *   - 华为 → HarmonyOS Sans SC ｜ 小米 → MiSans
   *   - OPPO → OPPO Sans ｜ vivo → vivo Sans
   *   - 原生安卓 → 思源黑体（Noto Sans CJK）
   *   - Windows 预览 → 微软雅黑
   * **这几家自带的中文字体都比"系统默认"精致一档**，而且零下载。
   *
   * ⚠️ **预览要拿手机看。** 你在 Windows 上看到的是微软雅黑，
   * 和妈妈手机上（多半是 MiSans 或 HarmonyOS Sans）不是一个东西。
   */
  --font-sans: -apple-system, BlinkMacSystemFont, system-ui, "Segoe UI", Roboto,
               "HarmonyOS Sans SC", "MiSans", "OPPO Sans", "vivo Sans",
               "PingFang SC", "Source Han Sans SC", "Noto Sans CJK SC",
               "Microsoft YaHei UI", "Microsoft YaHei", sans-serif;

  /* 字号。
   *
   * ⚠️ **2026-09-25 整体下调一档，默认不再走"长辈档"。**
   * 原稿是「正文 17 / 标题 22 / 大数字 32」，那是按**可读性下限**定的
   * （iOS 正文基准 17pt）。但真摆出来**像老年机**——每个字都在喊，
   * 而"像老年机"这件事本身就是一种伤害：她一眼就看出这是
   * 别人替"老人"设计的东西。
   *
   * 现在默认走**普通 App 的字号**（微信 / 支付宝那一档），
   * 原来那组数字**一个字没丢，原样保留成「大」档**，
   * 在「我 → 设置 → 字号」里切换。见下面 html[data-fs="big"]。
   *
   * ⇒ 好处是**两个都要到了**：默认不显老，她真看不清时一键回到老花档。
   * 而这比"一律用大字"更尊重她——她可以自己选，不是被当成病人。 */
  --fs-body: 15px;       /* 正文 */
  --fs-small: 13px;      /* 辅助信息 */
  --fs-section: 15px;    /* 卡片内的区段小标题，靠字重和留白分层 */
  --fs-title: 18px;      /* 页面级标题 */
  --fs-hero: 26px;       /* 封面里的占位字（`.work-ph`），别处不许用 */
  /* 「我」页那四格数字。
   *
   * ⚠️ **2026-09-25 单独拆出来，而且从 32px 一路降到 20px。**
   * 用户原话：「我中头像下方名称自己作品/粉丝/朋友/时光花数这里
   * 字号太大了」。原来那 32px 是 `--fs-hero` 给的，和封面占位字
   * 共用一档——但这两个根本不是一回事：占位字是**填满一个方块**
   * 的装饰（该大），这四个数是**一行一格的读数**（该小）。
   * 一行并排 2×2 四个 32px 粗黑数字，就是「老年机」味道的来源。
   *
   * 20px 配下面 13px 的标签（1.54 倍）是微信、支付宝那一档的比例。
   * ⚠️ **原来的 32px 一个字没丢**，在「大」档里原样保留。 */
  --fs-stat: 20px;
  --fs-tiny: 11px;       /* ⚠️ **只有底栏标签用它**，别处一律不许 */

  /* 触控与间距。
     ⚠️ **触控目标不跟着字号一起缩。** 字号是观感问题，触控是手感问题——
     手指的尺寸不会因为字小了就变小。44px 是 iOS HIG 的下限，
     普通 App 也都在这个数上下，再大就是"老年机"那种胖按钮了。 */
  --tap: 44px;           /* 触控目标下限 */
  --btn-h: 50px;         /* 主按钮 */
  --row-h: 52px;         /* 列表行高 */
  --gap: 16px;           /* 页面左右留白 + 卡片之间的距离 */
  --pad: 16px;           /* 卡片内边距 */

  /* 圆角。**分三档是精致感的一半**——一个尺寸走天下会显得粗糙：
     大块用大圆角、小控件用小圆角，视觉重量才对得上。 */
  --r-card: 16px;
  --r-btn: 13px;
  --r-item: 11px;
  --r-pill: 999px;

  --tab-h: 56px;         /* 底栏高度，别的地方按它算避让 */
  --ball: 50px;          /* 悬浮球直径 */

  /* 颜色。**全部是从 `plot/` 那几张图里量出来的**，不是配出来的——
     这样界面和她的头像、图标是同一套颜色。
     色值本身记在 `界面与信息架构.md` 1.3。 */
  --gold-100:#FDF4CB;
  --gold-200:#FCE5B3;
  --gold-300:#FBC26A;    /* 主金：主按钮的底 */
  --gold-400:#F19257;
  --gold-600:#CF7455;
  --pink-300:#F79AA0;
  --violet-300:#CDB1D7;

  --bg:#FFFBF3;          /* 页面底。暖白，跟着金色走 */
  --card:#FFFFFF;
  --line:#F1E6D6;        /* 分隔线 */
  --line-2:#F7EEE1;      /* 更淡的线，给卡片内的行用 */

  /* 文字。**对比度全部核过 WCAG 2.2 AA（正文 4.5:1）**：
     --ink 在 --bg 上约 11.9:1，--ink-2 约 6.1:1，
     --brand-ink 在白底上约 5.8:1。
     ⚠️ **不要把 --gold-600 直接当文字色**——它配白底只有 3.6:1，
     看着"很品牌"，但她那双眼在手机小字上读不动。 */
  --ink:#3A2F2A;
  --ink-2:#6B5B52;
  --ink-3:#9A8878;       /* 只给「还没做」这类极次要的说明，不当正文 */
  --brand-ink:#A34B2A;

  /* 阴影。**不用黑色，用暖褐色**——黑阴影压在暖白底上会发灰发脏，
     这是"看着糙"最常见的一个来源。分两档：贴地的一档给卡片，
     抬起的一档给按钮和浮层。 */
  --sh-1: 0 1px 2px rgba(122,86,58,.05), 0 3px 10px rgba(122,86,58,.045);
  --sh-2: 0 2px 6px rgba(122,86,58,.08), 0 10px 26px rgba(122,86,58,.07);

  /* 主按钮的渐变。**很浅**——深了就成了"网页按钮"，
     而它要看着像一块糖。 */
  --grad-gold: linear-gradient(180deg, #FDD07E 0%, #FBC26A 100%);
  --grad-hero: linear-gradient(168deg, #FFF7E4 0%, #FDF0DC 62%, #FCEADA 100%);

  /* 悬浮球的位置。**避让距离是算出来的，不是试出来的。** */
  --below: calc(var(--tab-h) + env(safe-area-inset-bottom, 0px));
}

/* ── 1b. 「大」档字号 ─────────────────────────
 *
 * ⚠️ **这一节就是把 2026-09-25 之前那组数字原样还回来**，
 * 一个值都没改：正文 17 / 标题 22 / 大数字 32。
 * 它们不是"废掉的旧值"，是**按长辈可读性下限算出来的值**——
 * 只是不该当默认。
 *
 * 开关在「我 → 设置 → 字号」，由 prefs.js 写到 `<html data-fs>` 上。
 *
 * ⚠️ **连触控和按钮高度一起放大。** 只放大字、按钮还是小的话，
 * 字会从按钮里挤出来；而且她既然选了"大"，本来就是嫌小。
 */
html[data-fs="big"]{
  --fs-body: 17px;
  --fs-small: 15px;
  --fs-section: 17px;
  --fs-title: 22px;
  --fs-hero: 32px;
  --fs-stat: 32px;       /* = 下调之前那四格的原样 */
  --fs-tiny: 12px;
  --tap: 48px;
  --btn-h: 56px;
  --row-h: 60px;
  --tab-h: 64px;
  --ball: 56px;
}

/* ── 2. 基础与排版 ───────────────────────────── */

*{ box-sizing:border-box; }

html,body{ margin:0; padding:0; }

body{
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  /* 中文行高要比英文松（汉字是满格方块），但也不能松到发飘。
     ⚠️ 2026-09-25 从 1.7 收到 1.6：1.7 配上原先那档大字，
     整屏像被撑开的表格，是"显老"的一部分。15px + 1.6 是
     微信那一档正文的比例，读起来正常。 */
  line-height: 1.6;
  /* 一丝字距，中文才不会黏在一起。**不要给多**——超过 .02em 就松散了。 */
  letter-spacing: .01em;
  color: var(--ink);
  background: var(--bg);
  -webkit-text-size-adjust: 100%;   /* 横屏时别自作主张放大 */
  -webkit-tap-highlight-color: transparent;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* 中文没有意大利体，斜体全是机器歪的——**两年里一个都不许用。** */
  font-synthesis-style: none;
}

a{ color: var(--brand-ink); }
a.plain{ color: inherit; text-decoration: none; }

/* 点了有反馈。#7 那个按下态是给触屏的——没有它，
   她会怀疑自己点没点上，然后连点好几下。 */
button, a.tab, .btn, .seg-btn, .row{
  transition: transform .09s ease, filter .09s ease, background-color .12s ease;
}
button:active, .btn:active, .seg-btn:active{ transform: scale(.975); filter: brightness(.97); }

/* ⚠️ **这里原来有一条 `.hero`（大数字）。2026-09-25 删了。**
   它只服务作品页顶上那块「日期 + 小哆一句话」，而那块被整个拿掉
   （原话「我感觉没必要」）。等宽数字那两条已经并进 `.stat .num`，
   别把这条当"删漏了"加回来——现在没有元素叫 `.hero`。 */

.h1{ font-size: var(--fs-title); font-weight: 700; letter-spacing: -.01em; margin: 0 0 4px; }
.sub{ color: var(--ink-2); font-size: var(--fs-small); margin: 0; line-height: 1.6; }

/* 能换行的地方就别硬撑。中文没有词边界，`break-word` 在长串数字/英文
   很长的作品名上会救一屏。 */
.h1, .sub, .row-label, .sheet-line{ overflow-wrap: anywhere; }

/* ── 3. 壳：底栏、悬浮球 ─────────────────────── */

.view{
  /* 顶部和左右留白。底部的避让在下面单独算。 */
  padding: 18px var(--gap) 0;
  /* ⚠️ **这个值是"底栏 + 悬浮球 + 一点余量"，不是随手写的。**
     悬浮球压在底栏之上，所以最后一屏内容的右下角会被它盖住——
     预留够了，任何东西都不会跑到球底下。 */
  padding-bottom: calc(var(--below) + var(--ball) + 20px);
  min-height: 100vh;
  max-width: 560px;      /* 平板上别把卡片拉成一条横幅 */
  margin: 0 auto;
}

.tabbar{
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 20;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  height: var(--below);
  padding-bottom: env(safe-area-inset-bottom, 0px);
  background: rgba(255,252,246,.92);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  border-top: 1px solid var(--line);
}

.tab{
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-height: var(--tap);          /* 触控目标下限 */
  text-decoration: none;
  color: var(--ink-2);
  font-size: var(--fs-tiny);       /* ⚠️ 底栏标签是**唯一**允许用 --fs-tiny 的字 */
}
.tab-icon{ width: 23px; height: 23px; }

/* 当前那一格。**同时改颜色和字重**——只改颜色的话，
   在亮处、在老花眼里，那个差别不够。 */
.tab[aria-current="page"]{ color: var(--brand-ink); font-weight: 700; }
/* 非当前的稍微退后一点，层次才出来 */
.tab:not([aria-current="page"]){ opacity: .82; }

.tab-plus .plus{
  display: flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; margin-top: -10px;   /* 往上顶出来一点，像主按钮 */
  border-radius: 50%;
  background: var(--grad-gold);
  color: var(--ink);
  box-shadow: 0 3px 10px rgba(207,116,85,.38), 0 1px 2px rgba(207,116,85,.30);
}
.tab-plus .plus svg{ width: 23px; height: 23px; }

/* 小哆的悬浮球。
 *
 * ⚠️ **2026-09-25 起可以拖动了**（原话："悬浮球位置可以移动"）。
 * 原设计是锁死的（`界面与信息架构.md` 2.4：不可拖动），理由正当——
 * 长辈会把能拖的东西拖丢。所以这次不是简单解开锁定，
 * 而是**换成一套"拖不丢"的规则**（见下面的注释）：
 *   ① 松手一定贴到最近的**左边或右边**——它永远回不到屏幕中间去挡内容；
 *   ② 纵向夹在顶边和底栏之间——拖不到底栏下面，也拖不出屏幕；
 *   ③ 位置存在本机，下次打开还在她放的地方。
 *
 * 位置由 `ball.js` 写到这两个自定义属性上，CSS 只管摆。
 * 没拖过时是 null，就落到下面的默认值（右下角）。 */
.duo-ball{
  position: fixed;
  right: var(--gap);
  /* ⚠️ **压在底栏之上，不是压在底栏里。** 压进去会把中间那格挤歪。 */
  bottom: calc(var(--below) + 12px);
  z-index: 30;
  width: var(--ball); height: var(--ball);
  padding: 0; border: 0; border-radius: 50%;
  background: var(--gold-100);
  /* 一圈白边：球飘在内容上，**没有这圈边会看着像内容的一部分**，
     而不是"浮在上面"。 */
  box-shadow: 0 0 0 3px rgba(255,255,255,.9), 0 4px 14px rgba(207,116,85,.30);
  cursor: pointer;
  /* ⚠️ **`touch-action:none` 是拖动能不能用的关键。**
     不写的话，手指一按下去浏览器就判定为"滚动页面"，
     球只会跟着滚走，pointermove 根本不派发。 */
  touch-action: none;
  -webkit-user-select: none; user-select: none;
  -webkit-touch-callout: none;
}
.duo-ball img{
  width: 100%; height: 100%; display: block; border-radius: 50%;
  /* 图片自己别吃掉拖动手势 */
  pointer-events: none;
}
/* 拖过之后就有明确的 left/top 了（由 ball.js 写内联样式），
   这三条把默认的 right/bottom 让开。 */
.duo-ball[data-placed]{
  right: auto; bottom: auto;
  left: var(--ball-x); top: var(--ball-y);
}
/* 正在拖的时候**没有过渡、稍微放大**——手指和球之间要"黏住"，
   有过渡反而会滞后。 */
.duo-ball[data-dragging]{
  transition: none;
  transform: scale(1.08);
  box-shadow: 0 0 0 3px rgba(255,255,255,.95), 0 8px 22px rgba(207,116,85,.42);
}

/* ── 4. 零件 ───────────────────────────────── */

/* 卡片。**靠阴影和圆角分层，不靠描边**——
   一圈灰线是最容易让界面显糙的东西。 */
.card{
  background: var(--card);
  border-radius: var(--r-card);
  padding: var(--pad);
  margin-bottom: var(--gap);
  box-shadow: var(--sh-1);
}
.card-tight{ padding: 0; overflow: hidden; }   /* 装整行列表的卡片 */

/* 暖渐变的一张卡。**现在只有"还没做"那一屏在用**——
   作品页顶上那块「日期 + 小哆一句话」2026-09-25 拿掉了
   （原话：「我感觉没必要」）。日期她天天知道，小哆那句话
   点悬浮球照样会说，摆在首屏只是白占第一眼的位置。 */
.card-hero{
  background: var(--grad-hero);
  box-shadow: 0 1px 2px rgba(122,86,58,.04), 0 4px 14px rgba(207,116,85,.10);
}

/* 卡片内的区段标题。见 token 里「标题分两档」那段。 */
.sec{
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px;
  margin: 0 0 12px;
}
.sec-title{ margin: 0; font-size: var(--fs-section); font-weight: 700; letter-spacing: .01em; }
.sec-note{ font-size: var(--fs-small); color: var(--ink-2); font-variant-numeric: tabular-nums; }

/* 主按钮。**金底深字，不是白字**——白字压在 --gold-600 上只有 3.6:1，
   达不到 AA，而"看起来很品牌"抵不过她读不清上面那两个字。 */
.btn{
  display: inline-flex;
  align-items: center; justify-content: center;
  gap: 8px;
  height: var(--btn-h);
  padding: 0 24px;
  border: 0;
  border-radius: var(--r-btn);
  background: var(--grad-gold);
  color: var(--ink);
  /* ⚠️ **按钮上的字跟正文同档，不单独放大。**
     原来写死 19px（比正文还大一档），配上 56px 的按钮高度，
     一个页面上三块金色胖条 —— 这是"显老"最集中的地方。
     字重 700 已经足够让它像个按钮了，不需要再靠字号喊。 */
  font-size: var(--fs-body);
  font-weight: 700;
  font-family: inherit;
  letter-spacing: .02em;
  box-shadow: 0 2px 8px rgba(207,116,85,.28);
  cursor: pointer;
}
.btn-block{ display: flex; width: 100%; }

.btn-ghost{
  background: transparent;
  box-shadow: none;
  border: 1.5px solid var(--gold-600);
  color: var(--brand-ink);
}

/* 还没接上的按钮。**看着像样，但一眼能看出是灰的**——
   不能让它看着能点（她会一直点），也不能让它看着像坏了（她会来问你）。 */
.btn[disabled]{
  background: #F6EDE0;
  color: var(--ink-3);
  box-shadow: none;
  cursor: default;
}
.btn[disabled]:active{ transform: none; filter: none; }

/* 按钮里那个小圆片，标明"这是哪个工作包"。**开发期专用，落地就删。** */
.chip{
  display: inline-flex; align-items: center;
  height: 22px; padding: 0 9px;
  border-radius: var(--r-pill);
  background: rgba(58,47,42,.07);
  color: var(--ink-2);
  font-size: var(--fs-tiny);
  font-weight: 600;
  letter-spacing: .04em;
}

/* 分段切换（作品页的「我的 / 朋友的」）。**不是两个按钮，
   是一个槽里的两个位置**——这样选中的那个是"滑过去"的，不是"亮起来"的。 */
.seg{
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 4px;
  padding: 4px;
  background: #F6EDE0;
  border-radius: var(--r-btn);
  margin-bottom: 14px;
}
.seg-btn{
  display: flex; align-items: center; justify-content: center; gap: 6px;
  height: var(--tap);          /* 触控目标下限。别为了紧凑压到 44 */
  border: 0; border-radius: 11px;
  background: transparent;
  color: var(--ink-2);
  font-family: inherit;
  font-size: var(--fs-body);
  font-weight: 600;
  cursor: pointer;
}
.seg-btn[aria-selected="true"]{
  background: var(--card);
  color: var(--ink);
  box-shadow: 0 1px 3px rgba(122,86,58,.10);
}
.seg-num{ font-size: var(--fs-small); color: var(--ink-3); font-variant-numeric: tabular-nums; }
.seg-btn[aria-selected="true"] .seg-num{ color: var(--ink-2); }

/* 列表行。触控目标按 --tap 给足。 */
.row{
  position: relative;
  display: flex; align-items: center; gap: 14px;
  width: 100%;
  min-height: var(--row-h);         /* 行里有两行字，所以比 --tap 还高一档 */
  padding: 11px var(--pad);
  border: 0;
  background: transparent;
  font: inherit; text-align: left;
  color: var(--ink);
  cursor: pointer;
}
/* ⚠️ **分隔线是内缩的，不是通栏的。** 从图标右边开始画，
   视觉上把这些行"串"成一列；通栏线会把卡片切成一条一条。 */
.row + .row::before{
  content: '';
  position: absolute; left: calc(var(--pad) + 38px); right: 0; top: 0;
  height: 1px; background: var(--line-2);
}
.row-icon{
  flex: none;
  display: flex; align-items: center; justify-content: center;
  width: 24px; height: 24px;
  color: var(--brand-ink);
}
.row-icon svg{ width: 24px; height: 24px; display: block; }
.row-main{ flex: 1; display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.row-label{ font-size: var(--fs-body); }
.row-arrow{ flex: none; color: #C9B7A4; display: flex; align-items: center; }
.row-arrow svg{ width: 18px; height: 18px; display: block; }
.row-btn{ font-family: inherit; }

/* 任务行。左边一个状态点、中间一行字、右边一个「领」。 */
.task{ display: flex; align-items: center; gap: 12px; padding: 10px 0; }
.task + .task{ border-top: 1px solid var(--line-2); }
.task-dot{
  flex: none; width: 10px; height: 10px; border-radius: 50%;
  background: var(--line); box-shadow: inset 0 0 0 1.5px #E6D8C6;
}
.task-dot[data-done="1"]{ background: var(--gold-300); box-shadow: none; }
.task-main{ flex: 1; min-width: 0; }
.task-label{ display: block; font-size: var(--fs-body); }
.task-goal{ display: block; font-size: var(--fs-small); color: var(--ink-2); }
.task-get{
  flex: none;
  height: var(--tap);          /* 同上：它是这行里真正的按钮 */
  padding: 0 18px;
  border: 0; border-radius: var(--r-pill);
  background: var(--grad-gold); color: var(--ink);
  font-family: inherit; font-size: var(--fs-small); font-weight: 700;
  cursor: pointer;
}
/* 还没做完的任务，右边给一个灰片而不是按钮。
   **它不能长得像能点**——点不动的东西比没有东西更让她上火。 */
.task-wait{
  flex: none;
  padding: 5px 12px;
  border-radius: var(--r-pill);
  background: #F6EDE0;
  color: var(--ink-3);
  font-size: var(--fs-small);
}

/* 「还有 N 个」。**点开是原地展开，不是跳页**——
   跳了页她就回不来了（长辈最常见的迷路方式）。 */
.more-btn{
  display: flex; align-items: center; justify-content: center; gap: 6px;
  width: 100%; min-height: var(--tap);
  margin-top: 4px;
  border: 0; border-radius: var(--r-item);
  background: #FAF4EA;
  color: var(--ink-2);
  font-family: inherit; font-size: var(--fs-small); font-weight: 600;
  cursor: pointer;
}
.more-btn svg{ width: 16px; height: 16px; transition: transform .16s ease; }
.more-btn[aria-expanded="true"] svg{ transform: rotate(180deg); }

/* 作品格。**两列，不是三列**——她的手指和眼睛都要更大的目标。 */
.grid{ display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.work{
  display: block;
  border: 0; padding: 0;
  background: transparent;
  text-align: left;
  font: inherit; color: inherit;
  text-decoration: none;
  cursor: pointer;
}
.work-cover{
  /* ⚠️ `display:block` 不能省。它是 `<a>` 里的一个 `<span>`，
     不写的话还是行内元素，`aspect-ratio` 根本不生效，
     封面会塌成一条线。 */
  display: block;
  position: relative;
  aspect-ratio: 1 / 1;
  border-radius: var(--r-item);
  overflow: hidden;
  background: #F7EEE1;
  box-shadow: var(--sh-1);
}
.work-cover img{ width: 100%; height: 100%; object-fit: cover; display: block; }
.work-name{
  margin-top: 9px;
  font-size: var(--fs-body);
  line-height: 1.45;
  overflow: hidden;
  display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical;
}
/* 时长和赞数都在这一行。**时长不做成封面上的小角标**——
   角标要压到 11px 才不抢戏，那就只能沿用小字辅助文字那一档，
   而"小字辅助文字"和"压在封面上的角标"根本不是一回事：
   后者永远读不清，尤其是在一张花的封面上。
   挪到这儿封面也干净。 */
.work-meta{
  display: flex; align-items: center; gap: 5px;
  margin-top: 2px;
  font-size: var(--fs-small); color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.work-meta svg{ width: 15px; height: 15px; color: var(--pink-300); flex: none; }
.work-who{ color: var(--ink-2); }
.work-dot{ color: #C9B7A4; }

/* 空态。**它出现的次数比想象中多**（刚装上、还没传过东西），
   所以要说人话，而不是"暂无数据"。 */
.empty{ text-align: center; color: var(--ink-2); padding: 30px 20px 26px; }
.empty img{ width: 92px; height: 92px; opacity: .92; margin-bottom: 10px; }
.empty p{ margin: 0; font-size: var(--fs-small); line-height: 1.7; }

/* 轻量空态：不配图、不占地方。给"这块本来就还没做"用——
   配一张小哆的图反而显得像是有内容没加载出来。 */
.blank{
  margin: 0; padding: 6px 0 4px;
  color: var(--ink-2); font-size: var(--fs-small); line-height: 1.7;
}

/* 封面的占位：一个暖色块加一个字。
   **比灰方块强得多**——灰方块永远像是图没加载出来。 */
.work-ph{
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%;
  background: linear-gradient(150deg, #FDF4CB, #FCE5B3);
  color: #B07B3E;
  /* 封面里那个占位字。**跟 --fs-hero 同档**，别单独写死数字。 */
  font-size: var(--fs-hero); font-weight: 700; letter-spacing: .02em;
}

/* 「还没做」的说明。**明说还没做，不假装是个正常页面**——
   假装的话，你会在测试的时候把它当成一个 bug 去查。
   ⚠️ 只是开发期的路标，不参与最终视觉，所以给得比辅助文字还轻。 */
.todo{
  margin: 0 0 var(--gap);
  padding: 0 4px;
  color: var(--ink-3);
  font-size: var(--fs-small);
  line-height: 1.65;
}
.todo code{
  font-family: inherit;
  color: var(--ink-2);
  background: #F6EDE0;
  border-radius: 5px;
  padding: 1px 5px;
  font-size: var(--fs-tiny);
}

.t-center{ text-align: center; }

/* 「还没做」那屏上小哆的脸。**给它一圈白边**，
   不然暖色的脸压在暖色的卡上会糊成一片。 */
.soon-face{
  display: block; margin: 0 auto 6px;
  border-radius: 50%;
  background: #fff;
  padding: 4px;
  box-shadow: 0 2px 10px rgba(207,116,85,.16);
}

/* 头像占位。1.1 才换成真图——**先用一个圆底加一个字**，
   而不是用一张灰色的人形剪影：剪影看着像"加载失败"。 */
.avatar-lg{
  /* ⚠️ 2026-09-25 从 84px/34px 收到 72px/28px。
     原来那个尺寸配上下面 32px 的数字，头部像一块牌子；
     72px 是微信、支付宝头像那一档的正常大小。 */
  width: 72px; height: 72px; margin: 0 auto;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--grad-gold);
  color: #6B3A1E;
  font-size: 28px; font-weight: 700;
  box-shadow: 0 0 0 4px rgba(255,255,255,.85), 0 4px 12px rgba(207,116,85,.22);
}

/* ── 5. 各屏 ───────────────────────────────── */

/* 作品页的「今天打卡」。
 *
 * ⚠️ **2026-09-25 从一张独立卡片挪进「今日任务」卡里**（原话：
 * "今天打卡按钮合并到今日任务里？"）。合并的理由不只是省地方：
 * 打卡本来就是**今天要做的第一件事**，把它单拎出来摆在任务上面，
 * 像是在说"任务之外还有一件事"；放进任务里，它才真的是"第一个任务"。
 *
 * ⚠️ **它永远第一行、永远不被「还有 N 个」折叠掉。**
 * 它是这一屏的主动作（她每天打开 App 就是为了点它），
 * 折起来等于把主要功能藏进了"更多"里。
 *
 * 视觉上比下面的任务行**重一档**：它用金色实心按钮，
 * 别的任务用灰片或小圆按钮。 */
.task-primary{
  display: flex; align-items: center; gap: 12px;
  padding: 10px 0 12px;
}
.task-primary .task-main{ flex: 1; min-width: 0; }
.task-primary .task-label{ font-weight: 700; }
.task-primary .btn{
  flex: none;
  height: var(--tap);              /* 它不是主按钮，是行内按钮，按 --tap 给 */
  padding: 0 18px;
  border-radius: var(--r-pill);
  font-size: var(--fs-small);
}
/* ⚠️ **打卡和任务之间那道线要留着。** 它是"第一行任务"
   和"其余任务"的分界——没有它，合并之后看着像打卡
   是那张卡的标题。 */
.task-primary + .task{ border-top: 1px solid var(--line-2); padding-top: 12px; }

/* 「我」上面那四组数字。
 *
 * ⚠️ **两行两列，不是一行四个。** 一行四个的话，数字四位就到边
 * （"1280" 在 26px 下约 62px，而 390px 屏上一格只有 74px）——
 * 她攒到一千朵的那天这一行就开始互相挤。
 * 改成 2×2 之后每格有一倍余量。
 *
 * ⚠️ **这个理由在字号下调之后依然成立，而且更该成立**：
 * 字号小了一档，"排得开"更不该成为问题。**不要因为字小了
 * 就换回一行四个**——那是一行四个真正的失效点（见 me.js
 * 里 DEMO_VALUES 那段：预览特意取 1280 就是为了验这个）。
 *
 * 四格等大是有意的：它们本来就是同一种东西（她的家底），
 * 分主次反而要多想一秒。 */
.stats{
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 16px 8px;
  margin: 18px 0 2px;
}
.stat{ display: flex; flex-direction: column; align-items: center; gap: 2px; }
/* 数字那行。**四格都套一层**（包括没有币的那三格）——
   高度和基线就天然一致，不用去凑。 */
.stat-top{ display: flex; align-items: center; justify-content: center; gap: 5px; }
.stat .num{
  font-size: var(--fs-stat);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -.03em;
  /* 等宽数字。**不然两个数字一多，位数一变整行会左右晃。** */
  font-variant-numeric: tabular-nums lining-nums;
}
.stat .lab{ font-size: var(--fs-small); color: var(--ink-2); }

/* 时光花那格多一枚币。**当货币符号用，缩到 20px**——
   和数字同高会跟数字抢，比数字还大就成了图标画。
   币在数字左边、整组居中，所以数字会比别的格偏右一点点；
   这点偏移读起来像"¥1240"，是对的。
   余额另外给个品牌色：**这一屏她最想一眼看到的就是它**。 */
.stat-coin .num{ color: var(--brand-ink); }
.stat-coin img{ width: 18px; height: 18px; }

/* 头像块的标题区 */
.me-head{ text-align: center; }
.me-name{ margin: 12px 0 2px; font-size: var(--fs-title); font-weight: 700; }

/* ── 6. 小哆的浮层 ─────────────────────────── */

.sheet{ position: fixed; inset: 0; z-index: 40; }
.sheet-mask{ position: absolute; inset: 0; background: rgba(58,47,42,.34); }

.sheet-body{
  position: absolute;
  left: 0; right: 0; bottom: 0;
  background: var(--card);
  border-radius: 26px 26px 0 0;
  padding: 12px var(--pad) calc(20px + env(safe-area-inset-bottom, 0px));
  text-align: center;
  box-shadow: 0 -6px 30px rgba(122,86,58,.16);
  animation: sheet-up .2s cubic-bezier(.2,.7,.3,1);
}
/* 顶上一道小手柄。**它是"这块能收走"的唯一暗示**——
   没有它，浮层看着像一整页。 */
.sheet-body::before{
  content: '';
  display: block;
  width: 38px; height: 4px; margin: 0 auto 14px;
  border-radius: var(--r-pill);
  background: var(--line);
}
@keyframes sheet-up{ from{ transform: translateY(18px); opacity: .5; } }

.sheet-avatar{
  width: 64px; height: 64px;
  border-radius: 50%;
  background: var(--gold-100);
  padding: 3px;
}
.sheet-line{
  /* 小哆说的话。**比正文大一档**——这是浮层的正文，
     而且它只有一句话，字大一点读着才像"有人在跟你说话"。
     但不再写死 19px：--fs-body 在「大」档是 17px，
     写死的话两档就没区别了。 */
  font-size: calc(var(--fs-body) + 2px);
  margin: 12px 0 20px;
  line-height: 1.65;
}
.sheet-actions{ display: flex; gap: 12px; }
.sheet-actions .btn{ flex: 1; }

/* ── 7. 减弱动态 ───────────────────────────────
   系统里开了"减弱动态效果"的她，看到的应该是静止的画面，
   而不是一段晃来晃去。

   ⚠️ **两条路都要留**：
     ① `@media` —— 跟随系统。这是默认，也是绝大多数人该走的那条；
     ② `html[data-motion="reduce"]` —— 她在设置里手动选的那条。
        **只有在系统没开、但她在 App 里开了的情况下才起作用。**
   ⚠️ **反过来的方向不给**（系统开了、App 里"强制开启动效"）：
   系统里开了这个的人多半是有原因的（前庭敏感），
   再给一个覆盖它的开关是没有道理的。prefs.js 里也没有这个选项。 */
@media (prefers-reduced-motion: reduce){
  *{ animation: none !important; transition: none !important; }
  button:active, .btn:active, .seg-btn:active{ transform: none; }
}
html[data-motion="reduce"] *{
  animation: none !important;
  transition: none !important;
}
html[data-motion="reduce"] button:active,
html[data-motion="reduce"] .btn:active,
html[data-motion="reduce"] .seg-btn:active{ transform: none; }

/* ── 8. 设置页 ────────────────────────────────
 *
 * ⚠️ **设置页是这一版唯一"她可以改东西"的地方**，
 * 所以零件要一眼看懂是"能改的"还是"只是说明"：
 *   - 能改的：分段控件（`.seg`）、开关（`.switch`）、按钮；
 *   - 只是说明的：`.set-val` 那种灰色右对齐文字，**不长成按钮**。
 *
 * ⚠️ **这里一个"账号"类的东西都不许有**（见 prefs.js 开头那段）。
 */
.set-row{
  display: flex; align-items: center; gap: 12px;
  min-height: var(--row-h);
  padding: 8px var(--pad);
}
.set-row + .set-row{ border-top: 1px solid var(--line-2); }
.set-main{ flex: 1; min-width: 0; }
.set-label{ display: block; font-size: var(--fs-body); }
/* 说明文字压在下面一行，**比 .sub 还轻**：它解释的是
   "为什么"，不是"是什么"，读不到也不影响操作。 */
.set-note{
  display: block; margin-top: 2px;
  font-size: var(--fs-small); color: var(--ink-3); line-height: 1.5;
}
/* 右侧的当前值。**灰的、不是按钮的样子**——
   它旁边那个开关才是能点的东西。 */
.set-val{
  flex: none;
  font-size: var(--fs-small); color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}

/* 开关。**用 checkbox 画的，所以键盘和读屏天然能用。**
   滑块的位置靠 `:checked + ...` 的兄弟选择器，不用 JS 改样式。 */
.switch{ flex: none; position: relative; display: inline-block; width: 50px; height: 30px; }
.switch input{
  position: absolute; inset: 0; width: 100%; height: 100%;
  margin: 0; opacity: 0; cursor: pointer;
}
.switch .track{
  position: absolute; inset: 0;
  border-radius: var(--r-pill);
  background: #E7DACA;
  transition: background-color .16s ease;
  pointer-events: none;
}
.switch .track::after{
  content: '';
  position: absolute; top: 3px; left: 3px;
  width: 24px; height: 24px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(122,86,58,.28);
  transition: transform .16s ease;
}
/* ⚠️ **开着的那一档要用深一点的金，不能用 --gold-300。**
   试过 #FBC26A：它压在暖白的卡片上几乎融进去，"开着还是关着"
   一眼分不出来——而一个看不出状态的开关比没有开关更糟。 */
.switch input:checked + .track{ background: var(--gold-400); }
.switch input:checked + .track::after{ transform: translateX(20px); }
/* 系统里开着减弱动效时不让她改（见 settings.js）。**灰下去，别只是无效。** */
.switch input:disabled{ cursor: default; }
.switch input:disabled + .track{ opacity: .55; }
/* ⚠️ **`--tap` 是 44，开关只有 30 高。**
   差的那 14px 用一圈看不见的外扩补上，别去动它的视觉大小——
   一个 44px 高的开关会占掉半行，看着很蠢。 */
.switch::before{
  content: '';
  position: absolute; left: 0; right: 0;
  top: 50%; height: var(--tap);
  transform: translateY(-50%);
}

/* 设置页里那种"这一段能改什么"的小标题。**比 .sec-title 轻**，
   它不是屏幕标题，是分组标签。 */
.set-group{
  margin: 4px var(--pad) 0;
  font-size: var(--fs-small); font-weight: 700;
  color: var(--ink-3); letter-spacing: .04em;
}

/* 子页面的「返回」。
   ⚠️ **一定要有这个东西。**
   设置页是从「我」点进来的，安卓的返回键能回去，但**网页上看不见
   返回键在哪儿**——她要是用的是浏览器/微信打开（0.2 就是这种），
   屏幕上唯一能点的就是底栏那三格，而它们回不到"上一层"。
   一行字换她不用去猜，值得。 */
.page-back{
  display: inline-flex; align-items: center; gap: 2px;
  min-height: var(--tap);
  margin: -4px 0 2px -4px;
  padding: 0 10px 0 4px;
  border-radius: var(--r-item);
  color: var(--ink-2);
  font-size: var(--fs-body);
  text-decoration: none;
}
.page-back svg{ width: 20px; height: 20px; }

/* 设置行里的分段控件：**不要它默认那条 margin-bottom**，
   它是行内的一块，不是卡片的头。 */
.set-row .seg{ margin: 0; width: 148px; }
.set-row .seg-btn{ height: calc(var(--tap) - 6px); }

/* 设置页里那种"解释一句话 + 一个按钮"的块。 */
.set-block{ padding: 10px var(--pad) 14px; }
.set-block .btn{ width: 100%; }
.set-block .set-note{ margin-top: 8px; }
