/* doandplay 前端样式。2026-08-12 从 index.html 里原样搬出来的（一个字节没改）。
 * 搬出来的理由：单文件涨到 5,976 行之后，改一条 CSS 要在六千行里翻。
 * 这一份是「产品前端」的全部自定义样式；Tailwind 的工具类仍由 CDN 提供。
 * ⚠️ 通过 /app.css 伺服（不是 /static/app.css）—— 那条路由带 no-cache，
 *    保证 HTML 换了新版之后不会配上浏览器缓存里的旧样式。见 api/main.py。
 */
  [x-cloak]{display:none!important}
  body{-webkit-tap-highlight-color:transparent}
  .chat-scroll{scrollbar-width:thin}
  .spin{ width:14px;height:14px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;display:inline-block;animation:spin .7s linear infinite;vertical-align:-2px }
  /* 小燃桌面停靠（split view，≥1024px）：右侧常驻面板，左侧照常可操作、看板改动实时可见 */
  @media (min-width:1024px){
    .app-shell{ transition:padding-right .22s ease; }
    .app-shell.ai-dockpush{ padding-right:400px; }
  }
  @keyframes spin{ to{ transform:rotate(360deg) } }
  /* 小燃 FAB：带文字的 pill，比纯图标可发现性高得多（非技术家长也一眼懂）。
     首次出现给一次轻微的“呼吸+光晕”提示“点我”，之后停下不打扰；尊重减弱动效偏好。 */
  .xr-fab{ display:inline-flex; align-items:center; gap:8px; padding:0 18px 0 14px; height:56px;
    border-radius:999px; color:#fff; font-weight:700; font-size:15px; letter-spacing:.01em;
    background:linear-gradient(135deg,#7c3aed 0%,#6d28d9 55%,#ec4899 130%);
    box-shadow:0 6px 22px -6px rgba(124,58,237,.55), 0 2px 6px rgba(30,26,60,.18);
    border:none; cursor:pointer; white-space:nowrap; }
  .xr-fab:hover{ transform:translateY(-2px); box-shadow:0 10px 28px -6px rgba(124,58,237,.65); }
  .xr-fab:active{ transform:translateY(0); }
  /* 「装到主屏」横幅在底部时，把 FAB 抬到它上面。
     真事（Playwright 冒烟测试抓到的）：横幅是 `fixed bottom-3 inset-x-0 z-[65]`，FAB 是 `z-40` ——
     横幅整条压在 FAB 上，手机上家长**根本点不到「问小燃」**，而它是产品的核心入口。
     ⚠️ 故意用自定义 CSS 而不是在 `:class` 里写 `bottom-24`：Tailwind 走的是 Play CDN（运行时扫 DOM），
     只出现在 Alpine 表达式里的类**可能不会被生成**（本轮已被这个坑咬过一次，见 git log）。
     自定义类写在这里，永远存在。 */
  .xr-fab-lifted{ bottom:6.25rem !important; }

  /* 小燃这一层的进场（#135）。两套动画，按断点分：
       手机 —— 从底部升起（bottom sheet）。家长点的是吸底那个输入框，历史就该从他手指那边上来。
       sm 起 —— 还是从右边滑进来的抽屉，桌面一个像素没动。
     ⚠️ 为什么不用 x-transition：它只能配一套 enter-start/enter-end，没法按屏幕宽度分。
     ⚠️ 尊重「减弱动效」：系统里关了动效的就直接出现，不要滑。 */
  /* 装到主屏之后没有地址栏挡着，整屏的那一层会顶进刘海里。只在手机上吃这层内边距 ——
     桌面那块抽屉是页面内的一栏，不该跟着缩。 */
  .pt-safe{ padding-top:env(safe-area-inset-top, 0px); }
  @media (min-width:640px){ .pt-safe{ padding-top:0; } }

  @keyframes ai-sheet-up{ from{ transform:translateY(100%); } to{ transform:none; } }
  @keyframes ai-drawer-in{ from{ transform:translateX(100%); } to{ transform:none; } }
  .ai-panel{ animation:ai-sheet-up .22s cubic-bezier(.32,.72,0,1); }
  @media (min-width:640px){ .ai-panel{ animation:ai-drawer-in .2s ease-out; } }
  @media (prefers-reduced-motion:reduce){ .ai-panel{ animation:none; } }

  /* ===== 家长端主按钮 = 墨黑 =====
     这条规则本来就写在下面的 `.theme-parent .bg-indigo-600{ background:#2f333c }`
     ——「主按钮走墨黑，把彩色让给孩子」。但页面到处用**内联渐变**和 `:style="background:var(--c)"`
     绕过它，结果家长端被孩子的强调色淹没，跟自己定的规矩正好相反。
     现在收敛成两个类。`var(--c)` 只保留给**身份**：孩子头像、色脊、目标圆点 —— 那才是「谁的」标记。
     ⚠️ 写成自定义 CSS 而不是 Tailwind 类：这些要用在 Alpine `:class` 附近，
     而 Play CDN 对只出现在表达式里的类可能不生成（本轮已被咬过，见 git log）。 */
  .btn-ink{ background:#2f333c !important; color:#fff !important; border:none !important; }
  .btn-ink:hover{ background:#1f232b !important; }
  .btn-ink-o{ background:transparent !important; color:#3c4149 !important; border:1px solid #d9d5cc !important; }
  .btn-ink-o:hover{ background:rgba(28,28,38,.04) !important; }
  .xr-fab .xr-ic{ width:30px; height:30px; flex:none; }
  .xr-fab.attn{ animation:xrBreathe 2.4s ease-in-out 3; }   /* 只呼吸 3 个来回 */
  @keyframes xrBreathe{
    0%,100%{ box-shadow:0 6px 22px -6px rgba(124,58,237,.55), 0 2px 6px rgba(30,26,60,.18); }
    50%{ box-shadow:0 6px 26px -4px rgba(124,58,237,.85), 0 0 0 8px rgba(124,58,237,.12); transform:translateY(-1px); }
  }
  @media (prefers-reduced-motion:reduce){
    .xr-fab.attn{ animation:none; } .xr-fab:hover{ transform:none; }
  }
  .md h1,.md h2,.md h3,.md h4{ font-size:14px; font-weight:600; margin:8px 0 3px; }
  .md p{ margin:5px 0; }
  .md ul,.md ol{ margin:5px 0; padding-left:18px; }
  .md ul{ list-style:disc; } .md ol{ list-style:decimal; }
  .md li{ margin:2px 0; }
  .md strong{ font-weight:600; }
  .md code{ background:#f1f5f9; padding:0 3px; border-radius:3px; font-size:12px; }
  .md table{ border-collapse:collapse; margin:6px 0; font-size:12px; }
  .md th,.md td{ border:1px solid #e2e8f0; padding:2px 6px; }
  .md hr{ margin:8px 0; border:0; border-top:1px solid #e2e8f0; }
  .md blockquote{ border-left:3px solid #e2e8f0; padding-left:8px; color:#64748b; margin:5px 0; }

  /* ===== 设计令牌 ===== */
  :root{
    --ink:#23272f; --muted:#79808c; --paper:#f4f4f1; --card:#ffffff;
    --line:#ebe9e3; --line-soft:#f1efea;
    --shadow-sm:0 1px 2px rgba(28,28,38,.05);
    --shadow-md:0 1px 2px rgba(28,28,38,.04), 0 10px 26px -16px rgba(28,28,38,.28);
  }
  .font-display{ font-family:ui-rounded,'SF Pro Rounded','Hiragino Maru Gothic Pro','Yuanti SC',system-ui,sans-serif; letter-spacing:.01em; }
  /* 区块标题：卡片/分区的主标题（目标 / 本科目行动 / 下一步行动 / 家庭日历…）。
     要明显压过正文——15px 深色粗体、不再用 11px 大写灰当标题。 */
  .eyebrow{ font-size:15px; font-weight:700; letter-spacing:.01em; color:var(--ink); }

  /* 每个孩子的一整块「墙」：左侧色脊 + 抬升 */
  .child-wall{ background:var(--card); border:1px solid var(--line); border-radius:1.25rem;
    box-shadow:var(--shadow-md); overflow:hidden; position:relative; }
  .child-wall::before{ content:''; position:absolute; left:0; top:0; bottom:0; width:5px; background:var(--c,#5b54e6); }

  /* 本周进度环（签名元素）：一眼看这周完成多少 */
  .ring{ width:54px; height:54px; border-radius:50%; display:grid; place-items:center; flex:none;
    background:conic-gradient(var(--c) calc(var(--pct)*1%), #e0ddd3 0); position:relative; }
  .ring::before{ content:''; position:absolute; inset:5px; border-radius:50%; background:var(--card); }
  .ring b{ position:relative; font-size:13px; font-weight:700; color:var(--ink); font-variant-numeric:tabular-nums; }

  /* 计划勾选框：统一成一个实心小方块，替代 ☐/☑ 字形。
     🐛 2026-08-11 修：勾上之后一直是绿色，而原意是「跟着科目/孩子的颜色走」——
        调用处写的是 `:style="done ? '--c:var(--c)' : ''"`，**自引用的 CSS 变量在计算时会失效**，
        于是每次都落到 var() 的回落值 #16a34a。四处调用的内联样式已全部删掉，
        现在真的继承页面的 --c 了。
     用 --c-deep 而不是 --c：白色的 ✓ 压在中明度原色上只有 ~3:1，看不清。 */
  .chk{ width:18px; height:18px; border-radius:6px; border:1.5px solid #cfd3da; background:#fff;
    display:grid; place-items:center; color:#fff; font-size:11px; line-height:1; flex:none; transition:.12s; }
  .chk.on{ border-color:var(--c-deep,#16a34a); background:var(--c-deep,#16a34a); }

  /* 科目小进度条 */
  .mini-bar{ height:4px; border-radius:99px; background:#edeae3; overflow:hidden; }
  .mini-bar>i{ display:block; height:100%; border-radius:99px; background:var(--c,#5b54e6); }

  /* 用当前孩子的强调色标注「选中/激活」——只用实色，避免 color-mix 的兼容风险 */
  /* 🎨 —— 承载文字的实色面：用「深而饱和」的科目色，不用原色 ——
     调色板是 10 个**中明度低饱和**色。它们直接大面积平涂 + 深墨字，读起来像没修饰过的
     默认色块（外部设计评审的原话是「廉价、像抠图没抠干净」）—— 中明度是最尴尬的区间：
     不够亮到显得轻盈，不够深到显得厚重。
     ⚠️ **不能用 color-mix 往黑里兑**：那会同时把彩度一起兑掉，十个色全塌成褐灰
     （上一版就是这么闷的）。用 oklch 相对色 —— **只压明度、反而把彩度提上去**，
     于是颜色更深也更纯，白字压上去有 ~7:1。
     不支持 oklch 相对色的老浏览器回落到 color-mix 版（偏闷但可用，不影响功能）。 */
  .subj-head, .on-c, .here, .tab-on, .btn-c, .chk{
    --c-deep:  color-mix(in srgb, var(--c,#5b54e6) 62%, #241f1c);
    --c-deep2: color-mix(in srgb, var(--c,#5b54e6) 50%, #241f1c); }
  @supports (color: oklch(from red l c h)){
    .subj-head, .on-c, .here, .tab-on, .btn-c, .chk{
      /* ⚠️ 彩度只微调（×1.08），别往上推：这套调色板的性格就是**低饱和**，
         试过 ×1.55，十个色全变成宝石色，深是深了，但那已经不是这套色板了。
         压明度就够让它厚重；彩度补一点只是抵掉压暗时人眼感觉到的发灰。 */
      --c-deep:  oklch(from var(--c,#5b54e6) 0.49 calc(c * 1.08) h);
      --c-deep2: oklch(from var(--c,#5b54e6) 0.42 calc(c * 1.12) h); }
  }
  .on-c{ background:var(--c-deep) !important; color:#fff !important; }
  /* 科目色的实心按钮。里程碑那几个「加」原来只是一个彩色字配 10% 的浅底 ——
     没有边界、没有阴影，扫过去看不出是能点的（外部评审列为最严重的一条）。
     用科目色而不是品牌靛蓝：这些按钮长在科目页里，页面已经是这一科的颜色了，
     再插一个靛蓝进来等于多一个和内容无关的主角。 */
  .btn-c{ background:var(--c-deep); color:#fff; border:none; cursor:pointer;
    box-shadow:0 1px 2px rgba(28,28,38,.14), 0 4px 12px -6px var(--c-deep2);
    transition:filter .15s ease, transform .15s ease; }
  .btn-c:hover{ filter:brightness(1.12); }
  .btn-c:active{ transform:translateY(1px); }

  /* 「＋ 加一条待办」这类**往列表里加一条**的入口。
     原来是一行灰色小字（`text-slate-400` 配一个线框加号）—— 看着像说明文字不像按钮，
     用户根本不知道这儿能加（用户 2026-08-11 指出）。
     三件事让它变成按钮：① 撑满整行、有实际高度（列表末尾的「加一行」是很成熟的模式）；
     ② 虚线边 —— 「这里可以插一条」的通用语汇，同时和实心 CTA 区分开，不去抢主行动的位置；
     ③ 加号做成**实心深色圆点** + 文字用墨色不用灰 —— 醒目靠这两处，不靠整块涂色。 */
  .btn-add{ display:flex; align-items:center; justify-content:center; gap:9px; width:100%;
    padding:11px 16px; border-radius:.85rem; font-size:14px; font-weight:650;
    color:#3b4150; background:#fff; border:1.5px dashed #c7ccd6; cursor:pointer;
    transition:border-color .15s ease, background .15s ease, color .15s ease; }
  .btn-add:hover{ border-color:#8d95a4; background:#fcfcfe; color:#1f2430; }
  .btn-add:active{ transform:translateY(1px); }
  .btn-add > i{ width:21px; height:21px; border-radius:999px; display:grid; place-items:center;
    background:#23272f; color:#fff; flex:none; }
  .btn-add > i .ico{ width:12px; height:12px; vertical-align:0; }
  /* 小号：一屏里会重复出现好几个（每个里程碑一个）。
     除了缩小，还必须**不撑满整行** —— 三个整行虚线框叠在一起，分量比内容还重，
     一眼看过去先看到三个空框子而不是「练到哪儿了」。 */
  .btn-add.sm{ display:inline-flex; width:auto; padding:6px 11px; font-size:12.5px;
    font-weight:600; border-radius:.7rem; color:var(--muted); }
  .btn-add.sm:hover{ color:var(--ink-2); }
  .btn-add.sm > i{ width:16px; height:16px; background:transparent; color:var(--muted);
    border:1px solid #c7ccd6; }
  .btn-add.sm:hover > i{ background:#23272f; color:#fff; border-color:#23272f; }
  .btn-add.sm > i .ico{ width:10px; height:10px; }
  /* 科目切换入口：从「白底彩字彩边」改成深色药丸 —— 它是整页里「你在哪一科」的答案，
     该是第一眼看到的东西，不是一个描边按钮。 */
  .tab-on{ border-color:transparent !important; color:#fff !important;
    background:linear-gradient(180deg,var(--c-deep),var(--c-deep2)) !important; font-weight:700;
    box-shadow:0 6px 18px -8px var(--c-deep2), 0 1px 2px rgba(28,28,38,.12); }
  .cat-on{ color:var(--c) !important; font-weight:600; box-shadow:inset 3px 0 var(--c); }
  .bar-on{ background:var(--c) !important; }
  /* 各页面统一卡片：温暖描边 + 轻抬升，替代到处重复的 shadow-sm */
  .card{ background:var(--card); border:1px solid var(--line); border-radius:1rem; box-shadow:var(--shadow-sm); }
  .theme-parent .card{ box-shadow:var(--shadow-md); }
  /* 身份小头：科目/主页顶部复用 dashboard 的孩子色 */
  .id-badge{ width:40px; height:40px; border-radius:.85rem; display:grid; place-items:center; color:#fff; font-weight:700; }
  /* iOS Safari：输入框字号 <16px 时聚焦会自动放大整页——移动端统一 ≥16px 消除放大（桌面不受影响） */
  @media (max-width:640px){ input,textarea,select{ font-size:16px !important; } }

  /* ===================== 首页 V2.5 视觉系统 =====================
     沿用既有法则：var(--c) 只给「身份」（谁的），家长端的操作走墨黑/中性。
     ⚠️ 全部写成自定义类而非 Tailwind 工具类 —— 这些要配合 Alpine `:class` 用，
     而 Play CDN 只扫 DOM 里的静态类名，光出现在表达式里的可能不生成（本仓库已被咬过两次）。 */
  :root{
    --wash:#edeae2;        /* 内嵌字段/进度轨底色（比 --line 深一档，能压住白卡） */
    --ink-2:#565c66;       /* 次级文字：比 --muted 深，用于要读的内容 */
    --amber:#d98324;       /* 「该动手了」的唯一暖色，只给主按钮，不铺底 */
  }

  /* —— 签名元素：分段进度轨 ——
     一段 = 一件真实的事。这个产品里今天的事永远是个位数，
     分段能让家长**数出来**，而不是去解读一个抽象百分比。 */
  .seg{ display:flex; gap:3px; align-items:center; }
  .seg > i{ flex:1 1 0; height:7px; border-radius:99px; background:var(--wash);
    transition:background .3s ease, transform .3s ease; transform-origin:center; }
  .seg > i.on{ background:var(--c,#5b54e6); }
  .seg.done > i{ background:var(--c,#5b54e6); }
  /* 勾掉最后一件时整条轻轻一弹——唯一一处庆祝动效，别再加第二个 */
  .seg.pop > i{ animation:segPop .42s cubic-bezier(.34,1.56,.64,1) backwards; }
  @keyframes segPop{ 0%{transform:scaleY(1)} 45%{transform:scaleY(2.1)} 100%{transform:scaleY(1)} }

  /* —— 复利·连续坚持点阵：孩子页 hero 的 signature。格子一天天点亮＝坚持在累积。 —— */
  .cstrip{ display:flex; gap:4px; }
  .cstrip > i{ width:14px; height:14px; border-radius:4px; background:var(--wash); display:block;
    transition:background .3s ease; }
  .cstrip > i.on{ background:var(--c,#5b54e6); }
  /* 孩子页 hero：用孩子身份色做极浅晕染 + 同色的柔和落影，把身份与动能收进一块，建立焦点。 */
  .child-hero{ position:relative; border-radius:1.25rem; padding:20px 22px;
    background:linear-gradient(180deg, color-mix(in srgb,var(--c) 9%,#fff), #fff);
    border:1px solid color-mix(in srgb,var(--c) 15%,var(--line));
    box-shadow:0 1px 2px rgba(28,28,38,.04), 0 22px 46px -30px color-mix(in srgb,var(--c) 55%,transparent); }
  /* 成长目标·科目卡：**整张卡就是科目色**（用户 2026-08-11 定）。
     以前科目色只出现在图标 chip 和几个数字上 —— 面积太小，四张卡扫过去还是四张白卡，
     家长仍然得读字才知道哪张是钢琴。颜色要替代读字就必须占面积：
       · 卡身   科目色 20%→7% 的纵向渐变（顶部重、底部轻，字压在浅的一头）
       · 描边   同色 34%，把卡从米白页底上拎起来
       · 顶条   3px 实色，一排卡并列时这条是最快的辨认线索
       · 图标块 **实色**（原来 13% 的浅底在有色卡上会糊掉，反而更看不清）
     悬停时同色微抬升，取代最早那道厚重的左边框。 */
  /* 卡身**留白**：卡头已经把颜色说完了，卡身再铺一层同色渐变就把整张卡糊满、不透气
     （外部评审点名的第三条）。只留一层几乎看不见的底 + 同色描边。 */
  .subj-card{ border-radius:1.25rem; position:relative; overflow:hidden;
    background:color-mix(in srgb,var(--c) 3%,#fff);
    border:1px solid color-mix(in srgb,var(--c) 26%,var(--line));
    transition:box-shadow .2s ease, transform .2s ease, border-color .2s ease; }
  /* 卡头 —— 这是「哪一科」最强的信号：一排卡并列时，五条不同颜色的头比五片浅色底
     快得多（浅底之间的差别在余光里会互相拉平）。
     深色富饱和底 + 白字（见上面那段 --c-deep 的理由），**图标块反过来用原色**：
     亮色点在深色上是这张卡唯一的高光，也是原色唯一还出场的地方。
     ⚠️ 图标块别用半透明白 —— 白色叠在中低饱和色上会发浑，看着像没抠干净的边。 */
  .subj-head{ background:linear-gradient(180deg,var(--c-deep),var(--c-deep2)); color:#fff; }
  .subj-head .subj-ic{ background:var(--c);
    box-shadow:0 1px 2px rgba(0,0,0,.18), inset 0 1px 0 rgba(255,255,255,.28); }
  .subj-head .subj-arrow{ color:rgba(255,255,255,.62); }
  /* 深色卡头上的操作按钮。有底、有边、30px（.ed-btn 的 26px 在深色底上按不准），
     所以单独一个类。「常驻可见」这一条现在是全局规矩了，见 ADR-008。 */
  .head-btn{ display:inline-grid; place-items:center; width:30px; height:30px; border-radius:9px;
    flex:none; cursor:pointer; font-size:14px; line-height:1;
    background:rgba(255,255,255,.16); border:1px solid rgba(255,255,255,.22);
    transition:background .15s ease, border-color .15s ease; }
  /* 深色卡头上：图标继承白色描边（换成线性图标后靠 currentColor 上色） */
  .head-btn{ color:#fff; }
  .head-btn svg{ width:15px; height:15px; vertical-align:0; }
  .head-btn:hover{ background:rgba(255,255,255,.3); border-color:rgba(255,255,255,.4); }
  .head-btn:focus-visible{ outline:2px solid #fff; outline-offset:1px; }
  /* ── 科目页 Hero ──────────────────────────────────────────────────
     用户 2026-08-11：「Math 这个目标的 title 做的太小气了」。
     原来的「页头」其实只是一个承载**切换科目**功能的小药丸，而紧跟着的愿景卡又大又带色，
     视觉上把主角位抢走了 —— 整页没有一个真正统领的头。顶栏面包屑里还重复了一次科目名。

     合并成一张卡：科目名是全页最大的字，同时**它自己就是切换入口**（点标题行展开下拉），
     编辑降级成右上角一个安静的图标。愿景、总进度、连续天数、点阵作为这一科的状态跟在下面。
     于是「这是哪一科 + 这一科要去哪儿 + 现在走到哪儿」在同一张卡里说完。

     ⚠️ 全程只用 color-mix，不碰 --c-deep：两者出现在同一个元素上会卡死渲染（known-issues #19）。 */
  /* ⚠️ **没有 overflow:hidden**。以前有，为的是让顶上那条 4px 色脊被圆角裁齐；
     代价是「换一科」的下拉菜单**也被裁掉**，科目一多就只露出前几个
     （用户 2026-08-12：五科只看得到四科）。现在色脊自己带圆角，卡片不再裁剪内容。 */
  .subj-hero{ position:relative; border-radius:1.25rem; padding:18px 18px 16px;
    margin-bottom:18px;
    background:linear-gradient(158deg, color-mix(in srgb,var(--c) 24%,#fff), color-mix(in srgb,var(--c) 7%,#fff));
    border:1px solid color-mix(in srgb,var(--c) 34%,var(--line));
    box-shadow:0 1px 2px rgba(28,28,38,.04), 0 22px 46px -34px color-mix(in srgb,var(--c) 75%,transparent); }
  .subj-hero::before{ content:''; position:absolute; left:0; right:0; top:0; height:4px; background:var(--c);
    border-radius:1.25rem 1.25rem 0 0; }
  /* 标题行：图标 + 科目名 + 右侧动作组，一个 flex 行。
     ⚠️ 动作组**不要绝对定位** —— 那样在窄屏上会直接压住科目名，而想避开就得给标题
     猜一个 padding-right，可按钮宽度会随语言和字号变，猜不准。 */
  .subj-hero-top{ display:flex; align-items:flex-start; gap:12px; min-width:0; }
  .subj-hero-acts{ display:flex; align-items:center; gap:6px; flex:none; position:relative; z-index:3; }
  /* 「换一科」。⚠️ 以前这是 `width:100%` 的整行热区，从图标一直延伸到卡片右边缘，
     而且完全看不见 —— 所以才不得不在标题下面加一行小字「点这里换一科」。
     那行字的存在本身就是证据：**要用文字告诉用户这里能点，说明它长得不像能点的**（ADR-008）。
     现在是个有底、有边、有内边距的小按钮，那行小字也就不需要了。 */
  .subj-switch{ display:inline-flex; align-items:center; gap:4px; flex:none;
    height:32px; padding:0 9px 0 11px; border-radius:10px; cursor:pointer;
    font-size:12.5px; font-weight:650; white-space:nowrap;
    color:color-mix(in srgb,var(--c) 58%,#241f1c);
    background:rgba(255,255,255,.7);
    border:1px solid color-mix(in srgb,var(--c) 22%,transparent);
    transition:background .15s, border-color .15s; }
  .subj-switch:hover{ background:#fff; border-color:color-mix(in srgb,var(--c) 42%,transparent); }
  .subj-switch:focus-visible{ outline:2px solid #a5b4fc; outline-offset:1px; }
  .subj-switch .ico{ width:14px; height:14px; display:block; transition:transform .15s; }
  /* 窄屏把这两个按钮撑到 40px 高 —— ADR-008 定的触控下限。桌面用鼠标，32 够了；
     手机上 32px 高的按钮挨着另一个 32px 的按钮，拇指按不准。同 .day-goto 的处理。 */
  @media (max-width:479px){
    .subj-switch{ height:40px; padding:0 11px 0 13px; }
    .subj-hero-edit{ width:40px; height:40px; }
  }
  .subj-hero-ic{ width:46px; height:46px; border-radius:14px; display:grid; place-items:center;
    font-size:24px; flex:none; background:#fff;
    box-shadow:0 1px 3px color-mix(in srgb,var(--c) 45%,transparent),
               inset 0 0 0 1px color-mix(in srgb,var(--c) 18%,transparent); }
  .subj-hero-name{ font-size:22px; font-weight:800; line-height:1.22; letter-spacing:-.01em; overflow-wrap:anywhere; align-self:center;
    color:color-mix(in srgb,var(--c) 60%,#241f1c); overflow-wrap:anywhere; }
  /* .subj-hero-cue（那行「点这里换一科」小字）已删：切换现在是个看得出来的按钮，
     不再需要用一行字去解释另一个元素能点。 */
  /* .subj-hero-chev 已删：箭头现在长在「换一科」按钮里，不再单独绝对定位。 */
  /* 编辑：安静地待在右上角。它是低频的管理动作，不该和「换一科」抢同一档分量 */
  .subj-hero-edit{ display:grid; place-items:center; width:32px; height:32px; border-radius:10px; flex:none;
    background:rgba(255,255,255,.7); border:1px solid color-mix(in srgb,var(--c) 22%,transparent);
    cursor:pointer; font-size:14px; line-height:1; transition:.15s; }
  .subj-hero-edit{ color:color-mix(in srgb,var(--c) 58%,#241f1c); }
  .subj-hero-edit svg{ width:16px; height:16px; vertical-align:0; }
  .subj-hero-edit:hover{ background:#fff; border-color:color-mix(in srgb,var(--c) 42%,transparent); }
  /* 愿景正文比标题大一档：标题是 15px 粗体近黑（.eyebrow），正文 17px 常规。
     标题负责说「这是什么」，正文才是这张卡的主角，两者不能同一号字互相抢。 */
  .subj-hero-vision{ font-size:17px; line-height:1.55; color:var(--ink); font-weight:500; }
  /* Hero 里的小号动作按钮（现在只有「编辑终极愿景」用）。跟「换一科」同一套语言，
     只是更小 —— 它编辑的是一句话，不是整一科，权重本来就该低一档。 */
  .hero-mini-btn{ display:grid; place-items:center; width:24px; height:24px; border-radius:7px;
    flex:none; cursor:pointer; color:color-mix(in srgb,var(--c) 58%,#241f1c);
    background:rgba(255,255,255,.62);
    border:1px solid color-mix(in srgb,var(--c) 20%,transparent);
    transition:background .15s, border-color .15s; }
  .hero-mini-btn:hover{ background:#fff; border-color:color-mix(in srgb,var(--c) 42%,transparent); }
  .hero-mini-btn:focus-visible{ outline:2px solid #a5b4fc; outline-offset:1px; }
  .hero-mini-btn svg{ width:13px; height:13px; display:block; }
  @media (max-width:479px){ .hero-mini-btn{ width:32px; height:32px; } .hero-mini-btn svg{ width:15px; height:15px; } }

  /* 连续天数 / 本周完成：**一个 flex、一条基线**。
     以前是两个独立的 flex，而且分隔线塞在第二组的基线组里，把那一组整体顶高 10px。 */
  .hero-stats{ display:flex; align-items:baseline; flex-wrap:wrap; gap:0 14px; margin-top:16px; }
  .hero-stat{ display:inline-flex; align-items:baseline; gap:5px; }
  .hero-stat b{ font-family:var(--font-display,inherit); font-size:30px; font-weight:800; line-height:1; }
  .hero-stat b.wk{ font-size:20px; font-weight:700; color:var(--ink-2); }
  .hero-stat em{ font-style:normal; font-size:16px; color:#c9cdd6; margin:0 1px; }
  .hero-stat i{ font-style:normal; font-size:13px; font-weight:500; color:var(--muted); }
  /* 分隔线是**兄弟元素**，不参与基线计算；用 translateY 微调到视觉中线上 */
  .hero-stat-sep{ display:inline-block; width:1px; height:22px; margin-right:9px;
    background:color-mix(in srgb,var(--c) 26%,transparent); transform:translateY(3px); }

  /* 30 天格子。空格子必须在**带色卡面**上看得见 —— 原来用 #eceef0，跟卡片底色几乎同一档，
     30 格里 22 格是隐形的（用户 2026-08-12 圈出来的那半）。半透明白在任何科目色下都成立。 */
  .hero-dots-wrap{ margin-top:14px; }
  .hero-dots{ display:flex; flex-wrap:wrap; gap:3px; }
  .hero-dot{ width:9px; height:9px; border-radius:2px; background:rgba(255,255,255,.72);
    box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--c) 12%,transparent); }
  .hero-dot.on{ background:var(--c); box-shadow:none; }
  .hero-dots-hint{ font-size:10.5px; color:var(--muted); margin-top:7px; }

  /* .hero-eyebrow 已删（2026-08-12）：10.5px 的淡色小字读不出是标题，
     改用应用里统一的 .eyebrow（15px/700/近黑 + 科目色图标）。 */

  /* 科目页上「编辑这一科」：跟在深色药丸旁边，用同色的浅底描边 —— 看得见是个按钮，
     但明显比药丸轻一档（切换科目是高频动作，改科目本身不是）。带文字不只给图标：
     一个孤零零的铅笔要靠猜「改的是什么」。

     🔴 **这条规则刻意不进上面 --c-deep 那一组**（known-issues #19）。
     在**同一个元素**上既用 `oklch(from var(--c) …)` 定义自定义属性、又用
     `color-mix(in srgb, var(--c) …)` 上色，会把 Chromium 的样式计算**卡死** ——
     不是报错，是整条主线程停住：fetch 发出去到不了服务端、promise 既不 resolve
     也不 reject、控制台一片干净。两者单独用都没事，凑一块儿才炸。
     所以这里的深色文字自己用 color-mix 算，不借 --c-deep。 */
  .subj-edit-btn{ display:inline-flex; align-items:center; gap:5px; padding:6px 11px 6px 9px;
    border-radius:.7rem; font-size:13px; font-weight:600; cursor:pointer;
    color:color-mix(in srgb,var(--c) 58%,#241f1c); background:color-mix(in srgb,var(--c) 12%,#fff);
    border:1px solid color-mix(in srgb,var(--c) 30%,var(--line));
    transition:background .15s ease, border-color .15s ease; }
  .subj-edit-btn:hover{ background:color-mix(in srgb,var(--c) 20%,#fff);
    border-color:color-mix(in srgb,var(--c) 45%,var(--line)); }
  @media (max-width:380px){ .subj-edit-btn .txt{ display:none; } }
  .subj-card:hover{ transform:translateY(-1px);
    border-color:color-mix(in srgb,var(--c) 48%,var(--line));
    box-shadow:0 12px 30px -18px color-mix(in srgb,var(--c) 75%,rgba(28,28,38,.35)); }
  .subj-card:hover .subj-arrow{ transform:translateX(2px); }
  .subj-ic{ width:38px; height:38px; border-radius:11px; display:grid; place-items:center;
    font-size:19px; flex:none; background:var(--c,#5b54e6);
    box-shadow:0 1px 3px color-mix(in srgb,var(--c) 45%,transparent); }
  .subj-arrow{ transition:transform .2s ease; color:color-mix(in srgb,var(--c) 60%,#9aa1ac); }

  /* —— 成长路线图（目标页 Tab2）：纵向技能树。一条竖脊串起阶段，done/current/locked 三态。
     这是目标页的签名元素：把「今日行动 → 十年愿景」的路径画成看得见的阶梯，全程用孩子身份色 --c
     承载「进度＝你自己的颜色在往上长」，locked 才褪成灰。 */
  /* ── 当前阶段卡里的一个里程碑 ─────────────────────────────────────
     重做的原因（用户 2026-08-11：「多个打勾的层级方式看起来很乱，看不出主次」）：
     里程碑和它下面的日常行动用的是**同一个 .chk 勾选框**，同样大小、同样颜色、
     只靠缩进区分。可这两个勾的意思完全不同 —— 里程碑是「这件事达成了」，
     一辈子勾一次；行动是「今天做了」，每天都要勾。长得一样就是在说它们是一类东西。

     现在：里程碑左边**不再有勾选框**，改成右侧一个药丸「标记达成 / 已达成」；
     左边那条竖直的勾选轴专属于日常行动。标题也放大一档，靠字号而不是缩进拉开层级。 */
  .ms-block{ margin-top:14px; padding-top:13px; border-top:1px solid var(--line-soft); }
  .ms-head{ display:flex; align-items:flex-start; gap:10px; }
  .ms-title{ font-size:15px; font-weight:700; color:var(--ink); line-height:1.35;
    overflow-wrap:anywhere; }
  .ms-block.is-done .ms-title{ color:var(--muted); text-decoration:line-through; }
  /* 「这学期」原来是个灰底圆角徽章，看着像系统发的标签，其实是家长自己打的字。
     去掉底色，降成一句说明 —— 图形边界留给能点的东西。 */
  .ms-sub{ display:flex; flex-wrap:wrap; gap:4px 12px; margin-top:3px;
    font-size:11.5px; color:var(--muted); }
  .ms-sub b{ color:var(--ink-2); font-variant-numeric:tabular-nums; font-weight:600; }
  .ms-flag{ display:inline-flex; align-items:center; gap:4px; flex:none;
    padding:5px 11px; border-radius:99px; font-size:11.5px; font-weight:600; cursor:pointer;
    color:var(--muted); background:transparent; border:1px solid var(--line); transition:.15s; }
  .ms-flag .ico{ width:12px; height:12px; vertical-align:0; }
  .ms-flag:hover{ background:var(--wash); color:var(--ink-2); }
  /* ⚠️ 这里只用 color-mix，不碰 --c-deep —— 两者同时出现在一个元素上会卡死渲染，
     见 docs/known-issues.md #19。 */
  .ms-flag.on{ background:color-mix(in srgb,var(--c) 18%,#fff);
    border-color:color-mix(in srgb,var(--c) 34%,transparent);
    color:color-mix(in srgb,var(--c) 62%,#241f1c); }
  /* 这里以前有一条 `.ms-head .ed-btn{opacity:1 !important}` —— 那时 .ed-btn 在桌面端
     默认透明，只有这一处特批常驻。ADR-008 之后所有 .ed-btn 都常驻，这条特批就多余了。 */
  .ms-acts{ margin-top:9px; }
  .ms-empty{ font-size:11.5px; color:#c5c9d2; padding:2px 0 4px; }
  /* 连续天数：原来只有「🔥16」，一个孤零零的数字，家长不知道是什么、也不知道能不能改
     （用户 2026-08-11 问）。它是后端从打卡记录算出来的**只读**派生值，
     所以把话说全，别让人猜。 */
  .streak-tag{ display:inline-flex; align-items:baseline; gap:2px; margin-left:6px;
    font-size:11px; color:#b45309; white-space:nowrap; vertical-align:middle; }
  .streak-tag b{ font-weight:700; font-variant-numeric:tabular-nums; }

  /* 「你在这里」：深色富饱和底 + 白字（同 .on-c 的理由） */
  .here{ font-size:10px; font-weight:800; letter-spacing:.03em; padding:2px 8px; border-radius:99px;
    color:#fff; background:var(--c-deep,#5b54e6); flex:none; }
  .rmap{ position:relative; }
  .rnode{ position:relative; padding-left:42px; padding-bottom:14px; }
  /* ⚠️ 竖脊和未走到的节点原来用 --line(#ebe9e3) —— 白卡上几乎看不见，
     强光下会让人以为路线到当前阶段就没了（外部评审提的）。加深到能看清但仍属「未激活」。 */
  .rnode::before{ content:''; position:absolute; left:15px; top:0; bottom:0; width:2px; background:#d8d3c8; }
  .rnode:first-child::before{ top:14px; }
  .rnode:last-child::before{ bottom:auto; height:14px; }
  .rdot{ position:absolute; left:6px; top:6px; width:20px; height:20px; border-radius:50%;
    display:grid; place-items:center; background:var(--card); border:2px solid #d8d3c8; z-index:1; }
  .rdot.done{ background:var(--c,#5b54e6); border-color:var(--c,#5b54e6); }
  .rdot.cur{ background:var(--c,#5b54e6); border-color:var(--c,#5b54e6);
    box-shadow:0 0 0 4px color-mix(in srgb,var(--c) 18%,transparent); animation:rpulse 2.4s ease-in-out infinite; }
  .rdot.lock{ background:#e4dfd4; border-color:#cdc7ba; }
  @keyframes rpulse{ 0%,100%{ box-shadow:0 0 0 3px color-mix(in srgb,var(--c) 22%,transparent); }
    50%{ box-shadow:0 0 0 7px color-mix(in srgb,var(--c) 6%,transparent); } }
  /* 旗舰 CTA：让小燃拟路线——目标页唯一被允许的「页面内 AI 启动键」，用炫彩描边呼应底部 composer。 */
  .composer-cta{ position:relative; border:none; border-radius:12px; padding:10px 20px; cursor:pointer;
    font-size:14px; font-weight:700; color:#fff; background:linear-gradient(120deg,var(--c,#5b54e6),color-mix(in srgb,var(--c) 55%,#8b5cf6));
    box-shadow:0 2px 10px color-mix(in srgb,var(--c) 24%,transparent), 0 14px 30px -18px color-mix(in srgb,var(--c) 70%,transparent);
    transition:transform .15s ease, box-shadow .15s ease; }
  .composer-cta:hover{ transform:translateY(-1px);
    box-shadow:0 4px 14px color-mix(in srgb,var(--c) 30%,transparent), 0 18px 38px -16px color-mix(in srgb,var(--c) 75%,transparent); }
  @media (prefers-reduced-motion:reduce){ .rdot.cur{ animation:none; } .composer-cta:hover{ transform:none; } }

  /* —— 成长轨（孩子页科目卡）——
     家长的原话：「每个技能下面很多文字，很多家人特别讨厌看文字」。所以这里**不放文字**，
     只放一条能一眼看懂「走到哪了」的轨。
     视觉直接借营销站（www）那条线的语汇，保持两端一致：
       · 走过的 = 实心深色节点 + 实线
       · 当前   = 金色 #d97706 + 5px 柔光环（营销站的 --now / --now-soft）
       · 没走的 = 虚线 + 空心点
     轨固定 10 格按比例映射（里程碑再多也不会挤成一团），真实数字在右边用等宽字标出来。 */
  .rail{ display:flex; align-items:center; gap:7px; }
  .rail-num{ font-size:11px; color:var(--muted); font-variant-numeric:tabular-nums; flex:none; }
  .rail-track{ flex:1; min-width:0; position:relative; display:flex; align-items:center;
    justify-content:space-between; height:14px; }
  /* 整条路：没走的部分是虚线（营销站 lock 阶段就是虚线）。
     卡身现在带科目色底，中性灰的虚线压在有色底上会发脏 → 让灰也带一点同色。 */
  .rail-track::before{ content:''; position:absolute; left:3px; right:3px; top:50%;
    border-top:2px dashed color-mix(in srgb,var(--c) 30%,#ded9d0); }
  /* 走过的部分：实线，用孩子的身份色。宽度由 --walk 给（railPct 算） */
  .rail-track::after{ content:''; position:absolute; left:3px; top:50%; width:var(--walk,0%);
    border-top:2px solid var(--c,#5b54e6); }
  .rail-dot{ position:relative; z-index:1; width:7px; height:7px; border-radius:50%;
    background:#fff; border:1.5px solid color-mix(in srgb,var(--c) 30%,#d9d5cc); flex:none; }
  .rail-dot.done{ background:var(--c,#5b54e6); border-color:var(--c,#5b54e6); }
  .rail-dot.now{ width:11px; height:11px; background:#d97706; border-color:#d97706;
    box-shadow:0 0 0 4px #fdf1e0; }          /* 营销站的「你在这里」金点 */
  .rail-more{ font-size:11px; color:#c9c4ba; letter-spacing:1px; flex:none; }

  /* —— 语音输入「正在听」的指示：一排随音量起伏的圆点 ——
     刻意**不用条形波形**（那是别家的招牌），改用本 App 自己的点阵语汇 —— 30 天点阵、
     阶段圆点、段进度都是这套圆点/分段的语言，听写时它们随音量放大缩小，一眼能看出"在听"。
     每颗点的 transform/opacity 由 JS 直接写（绕过 Alpine 响应式，60fps 不掉帧）。 */
  .vdots{ display:flex; align-items:center; gap:5px; height:20px; }
  .vdots i{ display:block; width:6px; height:6px; border-radius:50%; background:var(--c,#5b54e6);
    opacity:.25; transform:scale(.7); transform-origin:center;
    transition:transform .09s ease-out, opacity .09s ease-out; }
  /* 静音/降低动效偏好：不跳动，改成一颗恒亮的呼吸点 + 计时数字照常走 */
  @media (prefers-reduced-motion:reduce){ .vdots i{ transition:none; transform:scale(.85); opacity:.5; } }
  .vspin{ width:14px; height:14px; border-radius:50%; border:2px solid rgba(91,84,230,.25);
    border-top-color:#5b54e6; animation:vspin .8s linear infinite; flex:none; }
  @keyframes vspin{ to{ transform:rotate(360deg); } }
  @media (prefers-reduced-motion:reduce){ .vspin{ animation:none; } }
  .vclock{ font-size:12.5px; font-weight:600; color:var(--muted); font-variant-numeric:tabular-nums; flex:none; }

  /* —— 孩子今日行 —— */
  .kid-row{ position:relative; display:block; width:100%; text-align:left; cursor:pointer;
    background:linear-gradient(90deg, color-mix(in srgb,var(--c) 6%,var(--card)), var(--card) 34%);
    border:1px solid var(--line); border-radius:1.1rem;
    padding:14px 16px; box-shadow:var(--shadow-sm);
    transition:box-shadow .18s ease, transform .18s ease, border-color .18s ease; }
  /* 悬停时在孩子自己的身份色里轻轻抬升发光——和孩子页 Hero / 科目卡同一套语言，点进去前就有"这是谁"的暗示。
     （左侧竖脊已去掉：身份靠左缘淡色晕染 + 头像 + 悬停发光承载，不再需要那根条。） */
  .kid-row:hover{ transform:translateY(-1px);
    border-color:color-mix(in srgb,var(--c) 24%,var(--line));
    box-shadow:0 12px 30px -18px color-mix(in srgb,var(--c) 60%,rgba(28,28,38,.35)); }
  .kid-row:focus-visible{ outline:2px solid var(--c,#5b54e6); outline-offset:2px; }
  .kid-name{ font-size:17px; font-weight:700; color:var(--ink); letter-spacing:.01em;
    font-family:ui-rounded,'SF Pro Rounded','Hiragino Maru Gothic Pro','Yuanti SC',system-ui,sans-serif; }
  .kid-left{ font-size:13px; color:var(--ink-2); font-variant-numeric:tabular-nums; }
  .kid-next{ font-size:13.5px; color:var(--ink-2); }

  /* ── 编辑弹窗 ───────────────────────────────────────────── */
  .ie-lb{ display:block; font-size:11.5px; font-weight:600; color:var(--muted); margin:10px 0 4px; }
  .ie-in{ width:100%; padding:8px 10px; font-size:14px; border-radius:.6rem;
    border:1px solid var(--line); background:var(--card); color:var(--ink); }
  .ie-in:focus{ outline:2px solid color-mix(in srgb,var(--c,#5b54e6) 40%,transparent); outline-offset:1px; }
  .ie-swatch{ width:14px; height:14px; border-radius:99px; flex:none; }
  .ie-colors{ display:flex; flex-wrap:wrap; gap:7px; margin-bottom:2px; }
  .ie-dot{ width:26px; height:26px; border-radius:99px; border:2px solid transparent;
    cursor:pointer; padding:0; position:relative; transition:.14s; }
  .ie-dot:hover{ transform:scale(1.08); }
  .ie-dot.on{ border-color:var(--ink); }
  /* 「跟科目」是个带字的胶囊，不是一个塞了字的圆点 —— 圆点里挤一个汉字既看不清也说不明白 */
  .ie-auto{ display:inline-flex; align-items:center; gap:5px; height:26px; padding:0 10px 0 6px;
    border-radius:99px; border:2px solid transparent; background:var(--wash);
    font-size:12px; color:var(--ink-2); cursor:pointer; transition:.14s; }
  .ie-auto:hover{ background:color-mix(in srgb, var(--ink) 7%, var(--wash)); }
  .ie-auto.on{ border-color:var(--ink); color:var(--ink); font-weight:600; }
  .ie-auto-dot{ width:14px; height:14px; border-radius:99px; flex:none; }
  .ie-scope{ display:flex; gap:0; border:1px solid var(--line); border-radius:.6rem; overflow:hidden; }
  .ie-scope > button{ flex:1; padding:7px 6px; font-size:13px; border:none; background:transparent;
    color:var(--ink-2); cursor:pointer; transition:.14s; }
  .ie-scope > button.on{ background:var(--ink); color:#fff; font-weight:600; }
  /* 「某天请假」也是个动作，不是一句说明 —— 原来是加下划线的裸文字（＝链接的语汇），
     现在给它按钮该有的边框和内边距（同 .btn-danger 的理由）。 */
  /* margin-top 交给外层的 flex gap —— 这两个键现在是并排一组（请假 / 恢复），
     各自再挂一个上边距会让它们跟上一行的间距不一致。
     min-height 40：触控目标 ≥40×40 是硬规矩（CLAUDE.md 第 7 条），实测原来只有 31px。 */
  .ie-skip{ display:inline-flex; align-items:center; font-size:12.5px; font-weight:500;
    min-height:40px; color:var(--ink-2); background:transparent; border:1px solid var(--line);
    border-radius:.55rem; padding:5px 12px; cursor:pointer; transition:.15s; }
  .ie-skip:hover{ color:#e5484d; border-color:color-mix(in srgb,#e5484d 42%,transparent);
    background:color-mix(in srgb,#e5484d 7%,transparent); }

  .ie-emoji-btn{ min-width:44px; padding:5px 10px; border-radius:.6rem; border:1px solid var(--line);
    background:transparent; font-size:17px; cursor:pointer; line-height:1.3; }
  .ie-emoji-panel{ border:1px solid var(--line); border-radius:.7rem; padding:8px; }
  .ie-etab{ font-size:11.5px; padding:3px 8px; border-radius:99px; border:1px solid var(--line);
    background:transparent; color:var(--muted); cursor:pointer; }
  .ie-etab.on{ background:var(--ink); border-color:var(--ink); color:#fff; }
  .ie-egrid{ display:grid; grid-template-columns:repeat(8,1fr); gap:3px; max-height:150px; overflow-y:auto; }
  .ie-em{ font-size:19px; line-height:1; padding:5px 0; border:none; background:transparent;
    border-radius:.4rem; cursor:pointer; }
  .ie-em:hover{ background:var(--wash); }
  /* ── 今日行程（ADR-007）──────────────────────────────────────
     一行 = 一个时间锚点。时间在最左边固定宽度，扫一列就能读出「几点」。 */
  /* 整行带**底色**，不是只有左边一条边。
     3px 的边太轻 —— 家长扫一屏时看到的是一列白卡片，颜色要凑近才看得出来，
     那就没起到「一眼认出这是哪门课」的作用（用户 2026-08-11 指出）。
     20% 的底色 + 同色描边（第一版给的 10% 用户说太淡，翻倍）。
     敢上到 20% 是因为调色板本来就是低饱和中明度的（S≈25–40%、L≈45–58%）：
     20% 叠在白底上明度还在 90% 左右，深色正文照样清清楚楚
     （api/palette.py：层级靠明度差不靠饱和度 —— 这条在这儿反过来帮了忙）。 */
  .trip{ display:flex; align-items:center; gap:9px; padding:9px 10px; border-radius:12px;
    background:color-mix(in srgb, var(--c,#5b54e6) 20%, var(--card));
    border:1px solid color-mix(in srgb, var(--c,#5b54e6) 40%, var(--line));
    border-left:4px solid var(--c,#5b54e6); margin-bottom:6px; }
  .trip-time{ flex:none; width:44px; font-size:13.5px; font-weight:600; color:var(--ink);
    font-variant-numeric:tabular-nums; }
  .trip-icon{ flex:none; font-size:15px; }
  /* align-self:stretch —— 这是个按钮（点开详情），撑满整行高度才够 40px 的触控目标；
     只有文字那点高度（实测 23px）在手机上按不准（CLAUDE.md 第 7 条）。
     align-items 从 baseline 改成 center：撑高之后 baseline 会把文字顶到最上面。 */
  .trip-body{ flex:1; min-width:0; display:flex; align-items:center; gap:6px; align-self:stretch;
    min-height:40px; text-align:left; background:none; border:none; padding:0; cursor:pointer; }
  .trip-label{ font-size:14.5px; color:var(--ink); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  /* 「还有更多，点开看」的常驻提示（用户 2026-08-13 要的详情浮层）。
     ⚠️ 常驻，不做「悬停才现形」：手机根本没有悬停，而这一栏在手机上恰恰最容易被截断（ADR-008）。
     压得比正文淡：它是入口不是内容，跟旁边的时间/地点抢注意力就本末倒置了。 */
  .trip-more{ flex:none; width:13px; height:13px; color:var(--muted); opacity:.75;
    transition:transform .12s, opacity .12s; }
  .trip-body:hover .trip-more{ transform:translateX(2px); opacity:1; }
  .trip-body:focus-visible{ outline:2px solid #a5b4fc; outline-offset:2px; border-radius:6px; }
  .trip-when{ flex:none; font-size:11.5px; color:var(--muted); font-variant-numeric:tabular-nums; }
  /* 窄屏把结束时刻收起来：开始时刻在最左边已经有了，而「哪门课」被挤成「送 Leo 去…」
     等于这一行白写。次要信息让位给主要信息，不是把两个都挤扁。 */
  @media (max-width:520px){
    .trip-when{ display:none; }
    .trip-time{ width:40px; font-size:13px; }
    .trip{ gap:7px; padding:9px 8px; }
    .chore-act{ width:26px; height:26px; }
  }
  .trip-meta{ flex:none; font-size:11.5px; color:var(--muted); }
  .trip-tick{ flex:none; width:24px; height:24px; border-radius:99px; border:1px solid var(--line);
    background:transparent; color:transparent; cursor:pointer; line-height:1;
    transition:color .15s ease, border-color .15s ease, background .15s ease; }
  .trip:hover .trip-tick{ color:var(--muted); }
  .trip-tick.on{ background:var(--c,#5b54e6); border-color:var(--c,#5b54e6); color:#fff; }

  /* 三态之二：时间过了 → 整行退到背景里。**只对行程用**，功课过点不置灰
     （17:30 的练琴 18:30 弹完很正常，置灰会被读成「来不及了」）。 */
  .trip.is-past{ opacity:.5; }
  /* 三态之三：手动确认过 → 划掉。给需要闭环感的人，不强制。 */
  .trip.is-done .trip-label{ text-decoration:line-through; }
  .trip.is-done{ opacity:.45; }

  /* 家长日程的分组标题：和日历标题同一档字号 —— 它是这一段的锚，不是脚注。
     原来是 12px 的灰色小字，往下滚两屏就找不到自己在哪一天了。 */
  .ag-head{ font-family:ui-rounded,'SF Pro Rounded','Hiragino Maru Gothic Pro','Yuanti SC',system-ui,sans-serif;
    font-size:16px; font-weight:700;
    color:var(--ink); letter-spacing:.01em; margin:14px 0 8px 2px; }
  .ag-head:first-of-type{ margin-top:2px; }
  /* 「今天」那组空着时的一句话。压得比行本身轻 —— 它是「这里本来就没有东西」的说明，
     不是一条内容；做得跟行一样重会让人以为今天有一件叫「今天没有排给你的事」的事。 */
  .ag-clear{ font-size:13px; color:var(--muted); margin:0 0 2px 2px; line-height:1.6; }
  /* ===== 弹窗层要让开键盘 =====
     事故（用户实测 2026-08-13，iPhone / Safari）：点编辑弹窗里的输入框，键盘一弹出来，
     **输入框被顶到可视区之外**，屏幕上只剩地点/颜色/图标和保存键 —— 想输入什么都看不见。

     根因是 `max-h-[88vh]`：**iOS 上 `vh` 在键盘弹起时根本不变**，它算的是大视口高度。
     iPhone 13 上 88vh ≈ 743px，而键盘占掉约 330px 之后实际可见只剩 ~514px。
     面板比可用空间高出一半，底边又贴在被键盘盖住的位置，Safari 只能把整层往上顶，
     顶出去的正好是最上面那一栏 —— 也就是家长要打字的那一栏。

     修法不用新造：`--kb`（键盘占了多少像素）**早就有了**，由 `_initKeyboardInset()` 从
     visualViewport 算出来，小燃抽屉一直在用（搜 `bottom:var(--kb`）。只是 12 个弹窗
     一个都没接上。这里把它接上：外层收到键盘上方，面板 `max-h-full` 跟着收。
     ⚠️ 别再改回 `vh`/`dvh` —— `dvh` 让的是浏览器地址栏，不是键盘，这个坑它接不住。 */
  .sheet-wrap{ bottom:var(--kb,0px); }

  /* ===== 「发给家人」那层的高度：必须用 dvh，不是 vh =====
     这跟上面那段是**同一个坑的另一半**。上面那次挡住弹窗的是键盘（`dvh` 接不住，
     所以用 `--kb`）；这一次挡住它的是**浏览器地址栏**，而 `dvh` 正是为这个场景设计的。

     事故（用户实测 2026-08-15，iPhone / Safari）：切到「大字版」之后图很长，
     面板顶满 `max-height:90vh`。可 iOS 上 `vh` 算的是**地址栏收起时**的大视口高度 ——
     地址栏一展开，实际可见区就矮了一截；而这层在手机上是**贴着底边**的（items-end），
     多出来的那部分只能从**顶上**溢出去，顶掉的正好是版式切换器和 ✕ ——
     于是家长切到大字版之后**再也切不回一周表格**，也关不掉。

     `dvh` 跟着地址栏实时变，面板永远装得进当前可见区。前一行 `vh` 是老浏览器的回落
     （不支持 dvh 的会忽略第二行）。
     ⚠️ 治标的那一半也做了：版式切换器已经挪到底部动作区，跟按钮在一起 ——
        家长是**看完图**才决定要不要换版式的，那个控件本来就该在拇指所在的位置。
        两处都改是有意的：dvh 治根因，挪位置保证就算哪天 dvh 又失效也点得到。 */
  .share-sheet{ max-height:90vh; max-height:90dvh; }
  /* 登录卡那层同理（同一个地址栏的坑）。⚠️ 故意**不跟上面并成一条选择器**：
     两条各自被一条 e2e 盯着（用例是按 `.x{...}` 的形状去 app.css 里找 dvh 的），
     并起来两条都会失配。而且 .card-sheet 也**不许再去蹭 .share-sheet 这个类** ——
     登录卡在 DOM 里排在出图弹窗前面，蹭了之后 querySelector('.share-sheet')
     会选中登录卡，出图那条用例当场找不到版式切换器（2026-08-16 真撞过一次）。 */
  .card-sheet{ max-height:90vh; max-height:90dvh; }

  /* 「要发谁的这一周？」那几个选项。做成**看着就能点的大块**（ADR-008）：
     有描边、有内边距、整块 ≥56px 高 —— 这一屏对不擅长用软件的家长是个岔路口，
     不该长得像一行说明文字。选中态只加深描边和底色，不换成实心 ——
     实心块在这个位置会读成「已经选好了，可以走了」，而它其实是「按这里就出图」。 */
  .sharepick{ border:1px solid var(--line); background:var(--card); border-radius:.85rem;
    padding:12px 14px; min-height:56px; transition:.14s; cursor:pointer; }
  .sharepick:hover{ border-color:var(--ink-2); background:var(--wash); }
  .sharepick.on{ border-color:var(--ink); box-shadow:0 0 0 1px var(--ink) inset; }

  /* 编辑弹窗里那句「这句话只有你看得到 / 下面的时间是孩子那节课的」（ADR-010）。
     给底色不给描边：它是一段说明，不是一个能点的东西（同 .day-chip 那条理由）。
     行高放到 1.7 —— 这是整个弹窗里唯一一段需要**读完**的话，别挤成一坨。 */
  .ie-owner{ font-size:12px; line-height:1.7; color:#5b6371; background:#f5f7fa;
    border-radius:.6rem; padding:8px 10px; margin:6px 0 2px; }
  .ag-head.is-late{ color:#e5484d; }
  /* 「落下的」行上那块日期（「昨天 · 8月12日」）。
     跟标题同一个红系但压得更淡：标题负责喊「这一组是落下的」，行上这块只负责回答
     「落下多久了」。两块一样红的话，一屏全是警报，反而分不出轻重。
     对比度实测 #b4232a on #fdeced = 5.72:1。 */
  .late-flag{ display:inline-flex; align-items:center; flex:none; white-space:nowrap;
    font-size:11px; font-weight:600; line-height:1.5; padding:1.5px 6px; border-radius:5px;
    color:#b4232a; background:#fdeced; }

  /* 当前时刻的红线 */
  .now-line{ display:flex; align-items:center; gap:6px; margin:2px 0 8px; }
  .now-dot{ flex:none; width:7px; height:7px; border-radius:99px; background:#e5484d; }
  .now-time{ flex:none; font-size:11px; font-weight:700; color:#e5484d;
    font-variant-numeric:tabular-nums; }
  .now-rule{ flex:1; height:1px; background:#e5484d; opacity:.55; }

  /* 「还剩」的科目片：低对比、不抢内容。它是盘点，不是号召。 */
  /* 「主页」按钮：安静但有标签。它不该抢内容，但必须一眼认得出是能点的。 */
  .kid-home{ display:inline-flex; align-items:center; gap:2px; padding:3px 7px 3px 9px;
    border-radius:99px; border:1px solid var(--line); background:transparent;
    font-size:12px; color:var(--muted); cursor:pointer; transition:.14s; line-height:1.4; }
  .kid-home:hover{ color:var(--ink); border-color:color-mix(in srgb,var(--c) 40%,var(--line));
    background:var(--wash); }
  /* 展开按钮：和「进入孩子页」同一套药丸，只是更安静一点 */
  .kid-hint{ display:inline-flex; align-items:center; gap:4px; margin-top:9px;
    padding:4px 11px 4px 8px; border-radius:99px; border:1px solid var(--line);
    background:transparent; font-size:12.5px; color:var(--ink-2); cursor:pointer;
    transition:.14s; line-height:1.4; }
  .kid-hint:hover{ color:var(--ink); background:var(--wash);
    border-color:color-mix(in srgb,var(--c) 40%,var(--line)); }
  .kid-hint-caret{ transform:rotate(90deg); transition:transform .18s ease; }
  .kid-hint-caret.up{ transform:rotate(-90deg); }

  .kid-chip{ font-size:12.5px; color:var(--ink-2); background:var(--wash);
    border-radius:99px; padding:1.5px 8px; line-height:1.5; }
  /* 展开层：一行一件今天的事 */
  /* 展开层每行也带一点底色：家长陪读时是逐条扫的，颜色帮他把「这条是哪科」认出来。
     比 .trip 更淡（7%）—— 这里一屏里行数多，同样的浓度会显得脏。 */
  .kid-todo{ display:flex; align-items:center; gap:8px; padding:6px 8px; margin-bottom:2px;
    border-radius:8px; font-size:13.5px; color:var(--ink);
    background:color-mix(in srgb, var(--c,#5b54e6) 14%, transparent); }
  .kid-todo.is-done{ opacity:.45; text-decoration:line-through; }
  /* 课不划掉、也不打勾 —— 它不是「完成」出来的，只是发生了 */
  .kid-todo.is-class{ color:var(--ink-2); }
  .kid-todo-time{ flex:none; width:38px; font-size:12px; color:var(--muted);
    font-variant-numeric:tabular-nums; }
  .kid-todo-meta{ flex:none; font-size:11.5px; color:var(--muted); }
  .kid-pct{ font-size:12px; color:var(--muted); font-variant-numeric:tabular-nums; }

  /* 入场：孩子行依次浮上来。只在首屏跑一次，不做滚动触发（那会变成到处都在动） */
  .rise{ animation:rise .42s cubic-bezier(.22,.8,.3,1) backwards; }
  @keyframes rise{ from{ opacity:0; transform:translateY(7px) } to{ opacity:1; transform:none } }

  /* —— 待办条目：压扁成一行一件，去掉每张卡重复的徽章 —— */
  .chore{ display:flex; align-items:flex-start; gap:12px; padding:12px 12px 12px 14px;
    background:var(--card); border:1px solid var(--line); border-radius:.85rem;
    box-shadow:var(--shadow-sm); transition:box-shadow .16s ease, border-color .16s ease; }
  .chore:hover{ box-shadow:var(--shadow-md); }
  .chore-dot{ width:7px; height:7px; border-radius:50%; margin-top:7px; flex:none; background:var(--c,#5b54e6); }
  .chore-text{ font-size:15px; color:var(--ink); line-height:1.45; }
  .chore-meta{ font-size:12px; color:var(--muted); margin-top:2px; }
  .chore-meta .late{ color:#c2410c; font-weight:600; }
  /* 操作按钮：静息时低调，hover/聚焦才显形——7 行卡片上 21 个高对比按钮是视觉噪音 */
  .chore-act{ width:30px; height:30px; border-radius:.6rem; display:grid; place-items:center;
    color:#a9adb5; background:transparent; border:none; cursor:pointer; transition:.14s; flex:none; }
  .chore:hover .chore-act{ color:var(--ink-2); }
  .chore-act:hover{ background:var(--wash); color:var(--ink) !important; }
  .chore-act.ok:hover{ background:#e7f5ee; color:#0f7b4d !important; }
  .chore-act:focus-visible{ outline:2px solid var(--ink); outline-offset:1px; }

  /* —— Tab：下划线指示器，比黑底药丸克制 —— */
  .htab{ position:relative; padding:9px 2px 11px; margin-right:22px; font-size:14.5px; font-weight:600;
    color:var(--muted); background:none; border:none; cursor:pointer; transition:color .16s; }
  .htab:hover{ color:var(--ink-2); }
  .htab.on{ color:var(--ink); }
  .htab.on::after{ content:''; position:absolute; left:0; right:0; bottom:0; height:2.5px;
    border-radius:99px; background:var(--ink); }
  /* 待办计数徽章：比裸数字好扫，选中时填墨色 */
  .htab-n{ display:inline-grid; place-items:center; min-width:19px; height:19px; padding:0 5px; margin-left:7px;
    border-radius:99px; font-size:11px; font-weight:700; font-variant-numeric:tabular-nums;
    background:var(--wash); color:var(--ink-2); vertical-align:2px; line-height:1; }
  .htab.on .htab-n{ background:var(--ink); color:#fff; }

  /* —— 「落下的事」——
     原来是整块黄底。黄=警告=评判，和这条的文案纪律（中性、不带评判、别让家长
     觉得在批评孩子）正好打架。改成中性卡片，暖色只留给那一个主按钮。 */
  .catchup{ background:var(--card); border:1px solid var(--line); border-radius:1.1rem;
    box-shadow:var(--shadow-md); padding:15px 16px; }
  .catchup-title{ font-size:15px; font-weight:650; color:var(--ink); }
  .btn-amber{ background:var(--amber); color:#fff; border:none; font-weight:650;
    border-radius:.65rem; padding:7px 13px; font-size:13px; cursor:pointer; transition:.15s; }
  .btn-amber:hover{ background:#c0721c; }
  .btn-quiet{ background:transparent; color:var(--ink-2); border:1px solid var(--line);
    border-radius:.65rem; padding:7px 13px; font-size:13px; font-weight:600; cursor:pointer; transition:.15s; }
  .btn-quiet:hover{ background:var(--wash); }
  .btn-quiet.sm{ padding:5px 10px; font-size:12px; font-weight:500; }
  /* 孩子页右上角那个「管理」（2026-08-16）。原来是个**光秃秃的「⋯」** ——
     Quincy 的原话：「我知道是啥，但第一次用的人不知道这里还有入口」。
     这跟首页那个「进入孩子主页」是**同一条教训的第二次**：那儿原来只剩一个箭头，
     没标签的图标看着像装饰不像按钮，等于把入口堵死了（用户 2026-08-10 实测踩到，
     搜 .kid-home 上面那段注释）。孩子页这个「⋯」是最后一个没补上的。
     所以：有描边、有字、够大。40px 高是 ADR-008 的触控下限 —— 原来那个是 32px，本来就不达标。
     ⚠️ 别为了「干净」把字去掉再退回一个纯图标。 */
  .hero-manage{ display:inline-flex; align-items:center; gap:6px; height:40px; padding:0 12px;
    border-radius:.7rem; border:1px solid var(--line); background:var(--card);
    font-size:13px; font-weight:600; color:var(--ink-2); cursor:pointer; transition:.14s; }
  .hero-manage:hover{ color:var(--ink); background:var(--wash);
    border-color:color-mix(in srgb,var(--c) 40%,var(--line)); }
  /* 「发给家人」（用户 2026-08-14）。第一版用的是 .btn-quiet.sm，太轻 ——
     这是家长**每周真会用一次**的动作（把安排发给负责接送的家人），
     不该长得像旁边 tab 的一个附属链接。所以给足描边、字重和触控面积：
     墨色实描边 + 悬停反白，一眼就是个按钮（ADR-008），高 40px 满足触控下限。 */
  /* 吸底的小燃输入条是 position:fixed 的，不占文档流 —— 页面必须自己让出这段高度，
     否则最后一屏的内容会被它压住（用户 2026-08-15：科目页底部的按钮点不着）。
     76px 是那条 bar 的高度（小燃对话层里也是按这个数留的空），再加一点余量。 */
  .has-dock{ padding-bottom:88px; }
  .btn-share{ display:inline-flex; align-items:center; gap:6px; height:40px; padding:0 14px;
    border-radius:.7rem; font-size:13.5px; font-weight:700; letter-spacing:.01em;
    color:var(--ink); background:var(--card); border:1.5px solid var(--ink);
    cursor:pointer; transition:.15s; white-space:nowrap; }
  .btn-share:hover{ background:var(--ink); color:#fff; }
  /* 会走的省略号：一秒一个点，三秒一轮（用户 2026-08-14 指定的节奏）。
     纯 CSS，不开 setInterval —— 等待时最不该做的事就是每秒去动一次 Alpine 的响应式状态，
     那会让整页跟着重算。steps(1,end) 保证是「跳」不是「渐变」。 */
  @keyframes dots3{ 0%,32.9%{content:'.'} 33%,65.9%{content:'..'} 66%,100%{content:'...'} }
  .dots::after{ content:'.'; animation:dots3 3s steps(1,end) infinite; }
  @media (prefers-reduced-motion:reduce){ .dots::after{ content:'…'; animation:none; } }
  /* 危险动作：描边而不是实心 —— 实心红会比「保存」还抢眼，而删除不该是这一屏的主角。
     但它**必须长得像按钮**（用户 2026-08-11：「如果是按钮的功效，那就做成按钮该有的样子」）：
     原来它是一行红色裸文字，和它下面那句灰色说明看着是同一种东西。 */
  .btn-danger{ background:transparent; color:#e5484d;
    border:1px solid color-mix(in srgb,#e5484d 34%,transparent);
    border-radius:.65rem; padding:7px 13px; font-size:13px; font-weight:600;
    cursor:pointer; transition:.15s; }
  .btn-danger:hover{ background:color-mix(in srgb,#e5484d 8%,transparent); border-color:#e5484d; }
  .btn-danger:disabled{ opacity:.4; cursor:default; }
  .btn-danger.sm{ padding:5px 10px; font-size:12px; font-weight:500; }

  /* —— 图标：统一描线 SVG，跟随 currentColor。
     emoji 在不同系统上字形、基线、配色全不一样，是「没设计过」最明显的信号。 —— */
  .ico{ width:1.05em; height:1.05em; display:inline-block; vertical-align:-.15em;
    fill:none; stroke:currentColor; stroke-width:1.9; stroke-linecap:round; stroke-linejoin:round; }
  .ico-lg{ width:19px; height:19px; }

  /* —— 吸底输入 —— */
  /* align-items:flex-end —— textarea 长高时按钮沉在底部（聊天软件都这样），单行时同样贴底看不出差别。
     border-radius 用 22px 而非 99px：药丸在多行时两端会鼓成怪形，22px 单行仍够圆、多行也体面。 */
  /* 小燃是全站唯一的 AI 入口（管家），所以把「醒目」的预算全花在这条输入框上：
     渐变描边环（靛→紫→粉→琥珀）+ 缓慢流动 + 聚焦时柔和光晕。用双 background（padding-box 填卡片底、
     border-box 铺渐变）做真渐变边框；边框渐变层放大到 300% 宽、动 background-position 让颜色沿边缘流动。 */
  .composer{ display:flex; align-items:flex-end; gap:6px;
    border:2px solid transparent; border-radius:22px; padding:5px 6px;
    background:linear-gradient(var(--card),var(--card)) padding-box,
      linear-gradient(120deg,#5b54e6,#8b5cf6,#ec6a9c,#f0932b,#5b54e6) border-box;
    background-size:100% 100%, 300% 100%; background-position:0 0, 0 0;
    box-shadow:0 2px 10px rgba(91,84,230,.10), 0 14px 34px -20px rgba(91,84,230,.34);
    animation:compflow 8s linear infinite; }
  @keyframes compflow{ to{ background-position:0 0, 300% 0; } }
  .composer:focus-within{ box-shadow:0 0 0 4px rgba(91,84,230,.10), 0 6px 18px rgba(139,92,246,.22), 0 18px 40px -18px rgba(91,84,230,.5); }
  @media (prefers-reduced-motion:reduce){ .composer{ animation:none; } }
  .composer input, .composer textarea{ flex:1; min-width:0; border:none; background:transparent; outline:none;
    font-size:15px; color:var(--ink); padding:8px 4px; font-family:inherit; }
  .composer textarea{ resize:none; line-height:1.4; max-height:112px; overflow-y:auto; }
  /* 占位文字**永远只占一行**，装不下就省略号（Quincy 2026-08-16 报的）。
     病因：这是个 `rows="1"` 的 textarea，高度由 JS 按 scrollHeight 撑 ——
     而**打字才触发 input，占位文字不触发**。于是长一点的占位文字换到第二行，
     框还是一行高，第二行就被切掉了（实测 375px：clientHeight 38 / scrollHeight 61）。
     ⚠️ 别改成「把文案写短一点」—— 那治不住：这几句里嵌着**孩子名和科目名**
        （「和小燃聊聊 Alexandra 的 English & Writing…」），换个长名字照样破。
        也别把框调高：这条 bar 是常驻吸底的，每多一行都是从内容那儿抠来的。 */
  .composer textarea::placeholder,
  .ai-panel textarea::placeholder{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  /* ⚠️ 光有上面那条**不够**，占位文案本身也得留余量。
     实测（Quincy 2026-08-17 截图）：桌面右侧抽屉宽固定 400px，输入框可用 299px，
     而原来那句「问小燃，或拍/截图现成的安排让我录…」是 277px —— **只剩 5px**。
     他那台机器的滚动条是**常驻显示**的（截图里那个 ▲▼），自己又吃掉十几像素，
     于是同一句话在我这儿放得下、在他那儿换行，第二行「我录」被切掉半截。
     所以文案按「可用宽 − 17px 滚动条」来卡，e2e 里有一条直接量这个余量。 */
  .composer input::placeholder, .composer textarea::placeholder{ color:#a6aab2; }
  .comp-btn{ width:36px; height:36px; border-radius:50%; display:grid; place-items:center;
    border:none; background:transparent; color:var(--muted); cursor:pointer; transition:.15s; flex:none; }
  .comp-btn:hover{ background:var(--wash); color:var(--ink); }
  .comp-send{ background:#2f333c; color:#fff; }
  .comp-send:hover{ background:#1f232b; color:#fff; }
  /* 「结束录音」键。⚠️ 必须写在 .comp-btn **之后** —— 同特异度后来者胜，写前面会被
     .comp-btn 的 background:transparent / color:var(--muted) 吃掉（第一版就这么灰掉了）。 */
  .comp-stop{ background:#fee2e2; color:#e11d48; }
  .comp-stop:hover{ background:#fecdd3; color:#be123c; }

  /* —— 日历视图切换：分段控件（iOS 风，比黑底药丸克制，贴合暖纸底）—— */
  .segctl{ display:inline-flex; background:var(--wash); border-radius:.7rem; padding:3px; gap:2px; }
  .segctl button{ padding:5px 13px; border-radius:.55rem; font-size:13px; font-weight:600;
    color:var(--muted); background:transparent; border:none; cursor:pointer; transition:.15s; }
  .segctl button:hover{ color:var(--ink-2); }
  .segctl button.on{ background:var(--card); color:var(--ink); box-shadow:0 1px 2px rgba(28,28,38,.09); }
  /* 日历前后导航 + 今天 */
  /* flex-shrink:0 不是可有可无的：这一行右边那个「看谁的日历」下拉，2026-08-15 合并
     两块日历之后选项从「Leo」变成了「Leo 的全部安排」，390px 上它一撑，
     「今天」「明天」就被压成竖排的两个字。按钮该保持原样，装不下的是下拉，
     所以让下拉换行（容器 flex-wrap），别让按钮变形。 */
  .cal-nav{ width:32px; height:32px; border-radius:.55rem; display:grid; place-items:center; flex-shrink:0;
    color:var(--ink-2); background:transparent; border:none; cursor:pointer; transition:.14s; }
  .cal-nav:hover{ background:var(--wash); color:var(--ink); }
  .cal-today{ padding:6px 12px; border-radius:.55rem; font-size:13px; font-weight:600; flex-shrink:0; white-space:nowrap;
    color:var(--ink-2); background:transparent; border:1px solid var(--line); cursor:pointer; transition:.14s; }
  .cal-today:hover{ background:var(--wash); }
  .cal-today.on{ background:var(--ink); border-color:var(--ink); color:#fff; }
  /* 孩子页日视图行右侧的排期胶囊（「每天 08:00」「每周一二三四五 16:00」）。
     用科目色的淡底，跟科目页那个胶囊是同一个语言，但底色更淡 ——
     这一行的主角是任务本身，排期是附注，不该跟任务文字抢。
     ⚠️ 别给这个类加 oklch(from …) 的自定义属性：跟 color-mix() 同处一条规则会让
     Chromium 的样式计算卡死，见 docs/known-issues.md #19。 */
  /* 孩子页日视图的一行。三块横排：打卡框 / 主体（点进科目页）/ 行内改删。
     ⚠️ 三块是**兄弟**，不是嵌套 —— 主体是个 <button>，HTML 不允许按钮套按钮，
     把改删塞进主体里会让读屏软件错乱、手机上点「改」直接触发跳转。 */
  .day-row{ display:flex; align-items:stretch; border:1px solid #f1f5f9; border-radius:.75rem;
    background:#fff; overflow:hidden; }
  /* 刻意没有 :hover 阴影。整行浮起来等于在说「整行可点」，而现在整行不可点 ——
     能按的只有打卡框、科目按钮、改和删这四个，反馈就只给它们自己。 */
  .day-chk{ display:grid; place-items:center; width:34px; flex:none; }
  .day-main{ flex:1; min-width:0; display:flex; align-items:flex-start; gap:10px;
    text-align:left; padding:10px 4px 10px 0; }
  .day-txt{ display:flex; align-items:center; gap:8px; flex:1; min-width:0; }
  .day-content{ flex:1; min-width:0; font-size:14px; overflow-wrap:anywhere; }
  /* 唯一的跳转按钮。给底色和内边距，是为了让它在按之前就看起来像个能按的东西 ——
     这一行别的地方都不可点，所以它必须自己把「可按」说清楚，不能靠悬停才现形。
     底色用中性灰而不是科目色：科目色已经被排期胶囊占了，两块彩色贴在一起会打架。 */
  .day-goto{ display:inline-flex; align-items:center; gap:1px; flex:none; align-self:center;
    font-size:11px; color:var(--ink-2); white-space:nowrap; cursor:pointer;
    padding:5px 7px 5px 8px; border-radius:8px; border:1px solid #eef1f5; background:#f8fafc;
    transition:background .12s, border-color .12s, color .12s; }
  .day-goto .ico{ width:13px; height:13px; display:block; transition:transform .12s; }
  .day-goto:hover{ background:#eef2f7; border-color:#e2e8f0; color:var(--ink); }
  .day-goto:hover .ico{ transform:translateX(2px); }
  .day-goto:focus-visible{ outline:2px solid #a5b4fc; outline-offset:1px; }
  /* 行内改/删。跟周视图行、家长日程是同一组动作，只是按这一行的高度放大到 26px。
     **常驻可见**，不做「悬停才出现」：这一行原来根本没有编辑入口，藏起来等于问题没解决，
     而且家长主要在手机上用，手机没有 hover。 */
  /* 一条竖线把「这一行本身」和「对这一行做点什么」分开。没有它，改删两个图标
     会跟前面的科目名和箭头连成一排等重的灰图标，看不出哪些是同一组。 */
  .day-acts{ display:flex; align-items:center; gap:2px; flex:none;
    margin-left:6px; padding:0 8px 0 7px; border-left:1px solid #f1f5f9; }
  .day-act{ display:grid; place-items:center; width:26px; height:26px; border-radius:8px;
    border:none; background:transparent; color:#9aa0aa; cursor:pointer; padding:0;
    transition:background .12s, color .12s; -webkit-tap-highlight-color:transparent; }
  .day-act .ico{ width:15px; height:15px; display:block; }
  .day-act:hover{ background:rgba(91,84,230,.11); color:var(--ink); }
  .day-act.del:hover{ background:rgba(229,72,77,.11); color:#e5484d; }
  .day-act:focus-visible{ outline:2px solid #a5b4fc; outline-offset:1px; }
  /* 窄屏先让科目名走：它是这一行里最可省的一块 —— 左边的色条、图标和排期胶囊
     已经在说这条属于哪一科，而改删按钮是省不掉的功能。 */
  @media (max-width:479px){
    /* 窄屏放不下科目名，但按钮本身要撑到 40px 见方 —— 手指点得准比省那点宽度重要。 */
    .day-goto-name{ display:none; }
    .day-goto{ min-width:40px; min-height:40px; justify-content:center; padding:0; }
    /* 正文和排期胶囊改成上下两行。胶囊 nowrap 且不收缩，跟正文挤一行的话
       正文只剩一百来像素，一句话要断成四行。 */
    .day-txt{ flex-direction:column; align-items:flex-start; gap:3px; }
    .day-content{ flex:0 0 auto; width:100%; }
  }
  /* ⚠️ 这里**故意不写 `align-self`**（2026-08-12 删掉了原来的 `align-self:center`）。
     `align-self` 管的是交叉轴：父容器 .day-txt 横排时交叉轴是垂直的，`center` = 垂直居中，
     跟父容器自己的 `align-items:center` 完全同义 —— 也就是说它在宽屏上一天都没起过作用。
     但窄屏那一档把 .day-txt 翻成了 column，交叉轴随之变成**水平**方向，同一句 `center`
     就变成了水平居中，把容器上的 `align-items:flex-start` 盖掉：四条胶囊各自居中，
     左边缘 145/126/120/109 全不一样，整列没有一条对齐线（用户 2026-08-12：
     「要么该在左边，要么该在上面，但它在下一行的中间」）。
     交给父容器决定就两边都对：横排继承 center（垂直居中），竖排继承 flex-start（贴左）。 */
  .day-chip{ display:inline-flex; align-items:center; flex-shrink:0;
    font-size:11.5px; font-weight:600; line-height:1.5; padding:2px 7px; border-radius:5px;
    white-space:nowrap; font-variant-numeric:tabular-nums;
    color:color-mix(in srgb, var(--c,#5b54e6) 68%, #1f2430);
    background:color-mix(in srgb, var(--c,#5b54e6) 15%, #fff); }
    /* 没有描边、圆角只有 5px：带边框的大圆角看着像个能按的药丸，会引诱家长去点它改时间。
       它是纯信息，不可点，所以要长得像一块底色高亮，不像一个控件。 */
  /* 「这次改过」。
     🔴 **凡是渲染日历行的地方都要带上它**，一处不落 —— 这不是装饰。
     ADR-009 允许「单次覆盖在整条被改之后仍然有效」，代价就是会出现
     「我改了整条，某一天却没跟着变」。这个标记是那一天唯一的解释。
     只在孩子页日视图挂了它、别处不挂，等于在别处把状态又藏了回去（用户实测 2026-08-13：
     在家长日程里改完，界面上什么都没变，看着像没保存）。
     现在挂了它的位置：家长日程行 · 两份周视图行 · 孩子页日视图行 · 行详情浮层。
     用字不用图标：图标只说「有点特别」，说不出特别在哪（同 dayChipText 那次的理由）。

     ⚠️ **不要用中性灰。** 第一版是灰底灰字，结果它紧挨着同样是灰字的地点（「学习中心」），
     两者读起来是同一类东西，扫一眼分不出哪个是状态哪个是地点 —— 等于没做。
     琥珀色是这个界面里除科目色之外唯一已经在用的强调色（假期背景），
     不会跟「科目身份」那套颜色打架，也不会跟品牌靛蓝的「可点」语义混。
     对比度实测 #92400e on #fef3c7 = 6.37:1，超过 4.5 的下限。 */
  .occ-flag{ display:inline-flex; align-items:center; flex:none; white-space:nowrap;
    font-size:11px; font-weight:700; line-height:1.5; padding:1.5px 6px; border-radius:5px;
    color:#92400e; background:#fef3c7; }

  @media (prefers-reduced-motion:reduce){
    .rise, .seg.pop > i{ animation:none; }
    .kid-row:hover{ transform:none; }
  }

  /* ===== 品牌 / 顶栏 ===== */
  .brand-word{ font-family:ui-rounded,'SF Pro Rounded','Hiragino Maru Gothic Pro','Yuanti SC',system-ui,sans-serif;
    font-weight:800; font-size:16px; letter-spacing:-.01em; color:var(--ink); line-height:1; }
  .brand-word .a{ color:#a2a8b2; font-weight:700; }
  .topbar-div{ width:1px; height:20px; background:var(--line); flex:none; }
  .fam-chip{ display:inline-flex; align-items:center; gap:5px; font-size:12px; font-weight:600; color:#7c3aed;
    background:#f3ecff; padding:3px 9px; border-radius:999px; white-space:nowrap; }
  .crumb-back{ display:inline-flex; align-items:center; gap:3px; font-size:13px; color:var(--muted); padding:4px 8px; border-radius:8px; }
  .crumb-back:hover{ background:rgba(0,0,0,.05); color:var(--ink); }
  /* 面包屑：能点的一定看得出能点（有色 + hover 底色 + 手型），当前页灰色、无交互 */
  .crumbs{ display:flex; align-items:center; gap:2px; min-width:0; overflow:hidden; }
  .crumb-link{ font-size:13px; font-weight:600; color:#4f46e5; padding:4px 8px; border-radius:8px;
    cursor:pointer; max-width:34vw; }
  .crumb-link:hover{ background:#eef2ff; }
  .crumb-link:focus-visible{ outline:2px solid #6366f1; outline-offset:1px; }
  .crumb-now{ font-size:13px; color:var(--muted); padding:4px 4px; max-width:40vw; cursor:default; }
  .crumb-sep{ color:#cbd5e1; font-size:13px; flex:none; }
  .theme-kid .crumb-link{ color:#be185d; } .theme-kid .crumb-link:hover{ background:#fce7f3; }
  .user-chip{ display:inline-flex; align-items:center; gap:7px; font-size:13px; color:var(--ink); padding:4px 8px; border-radius:10px; }
  .user-chip:hover{ background:rgba(0,0,0,.05); }
  .user-av{ width:26px; height:26px; border-radius:8px; background:var(--ink); color:#fff; display:grid; place-items:center; font-size:12px; font-weight:700; flex:none; }
  .user-pop{ position:absolute; right:0; top:calc(100% + 8px); background:#fff; border:1px solid var(--line); border-radius:14px;
    box-shadow:var(--shadow-md); padding:6px; min-width:190px; z-index:50; }
  .pop-head{ padding:8px 10px 10px; border-bottom:1px solid var(--line-soft); margin-bottom:4px; }
  .pop-row{ display:flex; align-items:center; gap:8px; width:100%; text-align:left; padding:8px 10px; font-size:13px; color:var(--ink); border-radius:8px; }
  .pop-row:hover{ background:rgba(0,0,0,.05); }
  .pop-row.danger{ color:#e5484d; }
  /* 孩子主题下品牌字与家庭 chip 的适配 */
  .theme-kid .brand-word{ color:#5b21b6; } .theme-kid .user-av{ background:#ec4899; }

  /* 行内「改 / 删」图标按钮：静止态就看得见的中灰、够大的点按区、悬停出底色 */
  .ed-btn,.del-btn{ display:inline-grid; place-items:center; width:26px; height:26px; border-radius:8px;
    font-size:14px; line-height:1; color:#9aa0aa; flex:none; transition:background .12s; cursor:pointer;
    filter:saturate(1.05); -webkit-tap-highlight-color:transparent; }
  .ed-btn svg,.del-btn svg{ display:block; width:15px; height:15px; vertical-align:0; }
  .ed-btn:hover{ background:rgba(91,84,230,.11); }
  .del-btn:hover{ background:rgba(229,72,77,.11); }
  .ed-btn:focus-visible,.del-btn:focus-visible{ outline:2px solid #a5b4fc; outline-offset:1px; }
  /* 🔴 这两个图标**一律常驻可见**，不做「悬停才出现」（ADR-008，用户 2026-08-12）。
     以前的做法是桌面默认 opacity:0、指过去才现形，触屏压到 .4。看着确实干净，
     但家长在悬停之前根本不知道这里能不能改 —— 用户原话：「不 hover 的时候他怎么知道
     这里能编辑？」隐藏功能等于没有功能。整页图标多一点，也比让人猜强。 */
  .theme-kid .ed-btn:hover{ color:#db2777; background:rgba(236,72,153,.12); }

  /* ===== 周视图（家庭页 / 孩子页共用同一套样式与取值器）=====
     2026-08-06 用户反馈重做。原来七列硬压：390px 屏上卡片内宽 ≈326px，减 6 个 gap 后每列 41px，
     再减列内 p-1.5 与 chip 的 px-1.5 只剩 ~17px 给「emoji + 文字」，而 emoji 本身占 12–14px
     → 家长看到的就是「没有字」。孩子页则相反，把完整 content 塞进同样 41px 里 → 逐字竖排。
     解法不是压缩文字，是换布局：手机 grid-cols-1（按天纵向排，整行宽度给文字），
     ≥640px 才切回七列网格。完整内容一律点开详情浮层看。 */
  /* 行 = 「可点的正文」+「右侧动作」。原来整行就是一个 <button>，于是**只能有一个动作**
     （点开详情浮层，再在浮层里找「标记完成」）—— 家长要打个勾得点两次、还要读一屏字。
     家长日程那边早就是「✓ 改 删」直接摆在行上（.chore-act），这里对齐它（用户 2026-08-11）。
     ⚠️ 按钮不能套在按钮里，所以行必须从 <button> 变成 <div>，正文另起一个 .wk-main。 */
  /* 悬停看全文。**原生 `title` 的延迟由浏览器/系统定死，CSS 和 JS 都改不了**，
     用户要 0.5 秒就只能自己画一个（2026-08-12）。
     纯 CSS：延迟写在 `:hover` 那条的 transition-delay 上，移开时不带延迟（立刻收）。
     右边最后两列会冲出屏幕 —— 用 nth-child 静态翻转对齐方向，不用 JS 算位置。
     只在**真有悬停能力**的设备上出现（`hover:hover`），手机不受影响。 */
  .wk-tip{ display:none; }
  @media (hover:hover) and (min-width:640px){
    .wk-tip{ display:block; position:absolute; left:0; top:calc(100% + 5px); z-index:40;
      width:max-content; max-width:230px; padding:6px 9px; border-radius:8px;
      background:#23272f; color:#fff; font-size:11.5px; line-height:1.45; font-weight:500;
      white-space:normal; text-align:left; text-decoration:none; pointer-events:none;
      box-shadow:0 10px 26px -10px rgba(15,23,42,.6);
      opacity:0; visibility:hidden;
      transition:opacity .12s ease, visibility 0s linear .12s; }
    .wk-row:hover .wk-tip{ opacity:1; visibility:visible;
      transition:opacity .13s ease .5s, visibility 0s linear .5s; }
    /* 最后两列（周六周日）贴右对齐，否则浮层会冲出日历卡。
       ⚠️ 用 nth-last-child 从**末尾**数：Alpine 的 `<template x-for>` 元素留在 DOM 里
       占着 nth-child(1)，从头数会整体错一位（实测 7 个日期格是 nth-child 2..8）。 */
    .wk-week > div:nth-last-child(-n+2) .wk-tip{ left:auto; right:0; }
  }

  .wk-row{ display:flex; align-items:center; gap:2px; width:100%; position:relative;
    border-left:2px solid var(--wk,#5b54e6); border-radius:6px; padding:3px 6px;
    font-size:11.5px; line-height:1.35;
    /* 一格里同时挤着几门课，只靠 2px 的边分不出哪条是哪门 —— 也给底色 */
    background:color-mix(in srgb, var(--wk,#5b54e6) 20%, #fff);
    -webkit-tap-highlight-color:transparent; transition:background .12s; }
  .wk-row:hover{ background:color-mix(in srgb, var(--wk,#5b54e6) 32%, #fff); }
  /* 划掉和变淡只作用在正文上：整行变淡的话，「撤销完成」那个勾也跟着看不见了 */
  .wk-row.is-done .wk-main{ opacity:.45; text-decoration:line-through; }
  .wk-main{ display:flex; align-items:center; gap:4px; flex:1; min-width:0; text-align:left;
    background:transparent; border:none; padding:0; margin:0; font:inherit; color:inherit;
    cursor:pointer; }
  .wk-acts{ display:flex; align-items:center; gap:0; flex:none; }
  .wk-act{ display:grid; place-items:center; width:24px; height:24px; border-radius:6px;
    border:none; background:transparent; color:color-mix(in srgb,var(--ink) 42%,transparent);
    cursor:pointer; padding:0; transition:background .12s, color .12s; }
  .wk-act:hover{ background:rgba(255,255,255,.75); color:var(--ink); }
  .wk-act.ok:hover{ color:#16a34a; }
  .wk-act.del:hover{ color:#e5484d; }
  .wk-act .ico{ width:14px; height:14px; vertical-align:0; }
  .wk-time{ flex:none; width:36px; font-variant-numeric:tabular-nums; color:var(--muted); }
  .wk-body{ display:flex; align-items:center; gap:4px; flex:1; min-width:0; }
  .wk-label{ flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .wk-who{ flex:none; font-size:10.5px; color:var(--muted); }
  .wk-empty{ font-size:11px; color:#c9cdd6; padding:2px 6px; }
  /* 七列里一行放不下「时间 + 图标 + 文字」（每列约 108px 可用）→ 时间单独占一行，
     文字才拿得到整列宽度。手机整宽时反而一行更紧凑，所以只在 ≥640px 拆行。 */
  @media (min-width:640px){
    .wk-row{ display:block; }
    /* 🔴 `width:100%` 不能省。`.wk-main` 是个 <button>，而按钮的 `width:auto` 是
       **收缩到内容**（shrink-to-fit），不是填满父级 —— 手机上父级是 flex、有 flex:1
       兜着所以看不出来；桌面上父级变成 block，按钮就按文字长度撑开，冲出七列网格的格子，
       整周的文字互相压在一起（用户 2026-08-12 在 iPad 宽度下发现）。
       量出来的现场：.wk-row 宽 120px，而里面的 .wk-main 宽 200px —— 子比父还宽。 */
    .wk-main{ display:block; width:100%; }
    .wk-time{ display:block; width:auto; }
    .wk-time:empty{ display:none; }
    .wk-who{ display:none; }   /* 桌面列太窄写不下名字，「谁的」靠左侧色条区分 */
    /* 七列网格每列约 108px，塞不下三个按钮 —— 桌面仍然点开详情浮层操作。
       手机是主战场（家长晚上 9 点单手看这一屏），那里才是这三个按钮该在的地方。 */
    .wk-acts{ display:none; }
  }

  /* ===== 家长主题：温暖·沉稳 ===== */
  .theme-parent{ background:radial-gradient(1100px 440px at 50% -160px,#ffffff, var(--paper) 62%) fixed; color:var(--ink); }
  .theme-parent .app-topbar{ background:rgba(250,249,246,.85); box-shadow: inset 0 -1px 0 var(--line); }
  .theme-parent .app-title{ font-family:ui-rounded,'SF Pro Rounded','Hiragino Maru Gothic Pro','Yuanti SC',system-ui,sans-serif; letter-spacing:.01em; font-weight:700; }
  .theme-parent .border-slate-100{ border-color:var(--line) !important; }
  .theme-parent .bg-indigo-600{ background-color:#2f333c !important; }        /* 主按钮走墨黑，把彩色让给孩子 */
  .theme-parent .hover\:bg-indigo-700:hover{ background-color:#1f232b !important; }

  /* ===== 孩子主题：可爱 ===== */
  .theme-kid{ background:linear-gradient(160deg,#e8f3ff 0%,#f1e9ff 45%,#ffe9f5 100%);
    font-family:'Comic Sans MS','Yuanti SC','Hiragino Maru Gothic Pro',ui-rounded,system-ui,sans-serif; }
  .theme-kid .app-topbar{ background:linear-gradient(90deg,#a5d8ff,#c4adf6,#ffc2e2) !important; border-bottom:none !important; }
  .theme-kid .app-title{ color:#6d28d9; }
  .theme-kid .rounded-2xl{ border-radius:1.75rem !important; }
  .theme-kid .rounded-xl{ border-radius:1.15rem !important; }
  .theme-kid .rounded-lg{ border-radius:0.9rem !important; }
  .theme-kid .border-slate-100{ border-color:#efe3fb !important; }
  /* 主色：靛蓝 → 粉紫 */
  .theme-kid .bg-indigo-600{ background-color:#ec4899 !important; }
  .theme-kid .hover\:bg-indigo-700:hover{ background-color:#db2777 !important; }
  .theme-kid .bg-indigo-50{ background-color:#fdf2f8 !important; }
  .theme-kid .bg-indigo-100{ background-color:#fce7f3 !important; }
  .theme-kid .bg-indigo-500{ background-color:#ec4899 !important; }
  .theme-kid .border-indigo-100{ border-color:#fbcfe8 !important; }
  .theme-kid .border-indigo-200{ border-color:#f9a8d4 !important; }
  .theme-kid .text-indigo-900{ color:#9d174d !important; }
  .theme-kid .text-indigo-700{ color:#be185d !important; }
  .theme-kid .text-indigo-600{ color:#db2777 !important; }
  .theme-kid .text-indigo-500{ color:#ec4899 !important; }
  .theme-kid .text-indigo-400{ color:#f472b6 !important; }
  .theme-kid .focus\:ring-indigo-300:focus,.theme-kid .focus\:ring-indigo-200:focus{ --tw-ring-color:#f9a8d4 !important; }
