/* ==========================================================================
   孩子端界面 —— 明亮、圆润、大按钮、表情丰富
   配色与间距刻度统一引用 tokens.css，与家长端保持协调一致。
   视觉刷新：英雄卡金色光环、分区标题强调条、数字等宽对齐
   ========================================================================== */

@import url('./tokens.css');

:root {
  /* 孩子端语义色 —— 映射到统一令牌，消除与家长端的色差漂移 */
  --k-ink: var(--c-ink);
  --k-muted: var(--c-muted);
  --k-card: var(--c-card);
  --k-study: var(--c-study);
  --k-study-soft: var(--c-study-soft);
  --k-life: var(--c-life);
  --k-life-soft: var(--c-life-soft);
  --k-gold: var(--c-warn);
  --k-green: var(--c-success);
  --k-pink: #ff6f91;            /* 孩子端专属点缀色，保留 */
  --k-purple: var(--c-purple);
  /* 孩子端偏向更圆润可爱的视觉语言，仍由令牌驱动 */
  --k-radius: var(--r-2xl);
  --k-shadow: 0 6px 0 rgba(0, 0, 0, .05), 0 14px 30px rgba(51, 64, 92, .12);
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0;
  padding: 0;
  min-height: 100%;
}

body.kid {
  font-family: system-ui, -apple-system, "PingFang SC", "Microsoft YaHei", "Segoe UI", sans-serif;
  color: var(--k-ink);
  /* 两色微渐变（天蓝 → 奶白），取代原来的四段式蓝→青→米→粉。
     四段渐变在长页面上会把"彩色带"暴露得很明显，越往下滚越像换了个页面；
     两色过渡在中段自然消隐，卡片浮起来更干净（报告 §4.1 --bg-page）。 */
  background: linear-gradient(180deg, #eaf6ff 0%, #f6fbff 42%, #fff9ef 100%);
  background-attachment: fixed;
  min-height: 100vh;
  min-height: 100dvh;          /* 跟随动态视口，避免地址栏收放时多出一段可滚动空白 */
  padding-bottom: 40px;
  overflow-x: hidden;
}

/* 背景装饰：云朵与星星 */
.sky-deco { position: fixed; inset: 0; pointer-events: none; z-index: 0; overflow: hidden; }
.sky-deco span {
  position: absolute; font-size: 30px; opacity: .55;
  animation: floaty 9s ease-in-out infinite;
}
.sky-deco span:nth-child(1) { left: 6%;  top: 12%; animation-delay: 0s; }
.sky-deco span:nth-child(2) { left: 82%; top: 8%;  animation-delay: 1.2s; font-size: 34px; }
.sky-deco span:nth-child(3) { left: 14%; top: 62%; animation-delay: 2.1s; }
.sky-deco span:nth-child(4) { left: 74%; top: 74%; animation-delay: .7s; font-size: 26px; }
.sky-deco span:nth-child(5) { left: 46%; top: 4%;  animation-delay: 1.8s; font-size: 24px; }
@keyframes floaty {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50%      { transform: translateY(-14px) rotate(6deg); }
}

/* 顶部内边距跟随安全区：PWA 独立模式下状态栏 / 刘海会盖住标题与孩子切换按钮 */
.kid-wrap {
  position: relative; z-index: 1; max-width: 940px; margin: 0 auto;
  padding: calc(18px + env(safe-area-inset-top)) 16px 40px;
}

/* ---------------------------------------------------------------- 顶部 */

/* 顶栏：毛玻璃浅色底 + 圆角胶囊（报告 2.2「顶部栏」）。
   不用 backdrop-filter 单独实现"半透明"是不行的：背景是渐变，纯 rgba 白底
   在浅色区域会糊成一片。加了 blur 之后文字与背后内容才有层次。
   @supports 兜底：老浏览器不支持时退回不透明白底，可读性不受影响。 */
.kid-top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 16px;
  background: rgba(255, 255, 255, .62);
  border: 1.5px solid rgba(255, 255, 255, .9);
  border-radius: var(--r-xl);
  padding: 10px 14px;
  box-shadow: 0 4px 16px rgba(51, 64, 92, .08);
}
@supports ((backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))) {
  .kid-top {
    background: rgba(255, 255, 255, .48);
    -webkit-backdrop-filter: blur(14px) saturate(1.3);
    backdrop-filter: blur(14px) saturate(1.3);
  }
}
.kid-title { display: flex; align-items: center; gap: 10px; font-size: 24px; font-weight: 800; letter-spacing: .5px; }
.kid-title .big { font-size: 30px; }

.kid-switch { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.kid-switch button {
  border: 3px solid #fff; background: rgba(255,255,255,.75);
  border-radius: 999px; padding: 5px 14px 5px 6px;
  display: flex; align-items: center; gap: 7px;
  font-size: 15px; font-weight: 700; color: var(--k-ink);
  box-shadow: 0 3px 10px rgba(51,64,92,.10);
  transition: transform .15s, box-shadow .15s;
}
.kid-switch button:hover { transform: translateY(-2px); }
.kid-switch button.active { border-color: var(--k-gold); background: #fff; box-shadow: 0 6px 16px rgba(255,183,0,.32); }
.kid-switch .av {
  width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center;
  font-size: 20px; background: #fff;
  transition: width .18s, height .18s, font-size .18s;
}
/* 当前这个孩子的头像放大一点：一眼能认出"我现在是谁"（报告 2.2） */
.kid-switch button.active .av { width: 40px; height: 40px; font-size: 23px; }

.parent-link {
  font-size: 13px; color: var(--k-muted); text-decoration: none;
  background: rgba(255,255,255,.7); padding: 6px 12px; border-radius: 999px;
}
.parent-link:hover { color: var(--k-ink); }

/* ---------------------------------------------------------------- 主卡片 */

.kid-hero {
  background: #fff; border-radius: var(--k-radius);
  box-shadow: var(--k-shadow), 0 0 0 1.5px rgba(255, 209, 106, .45);
  padding: 20px;
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  margin-bottom: 18px; border: 4px solid #fff;
  position: relative; overflow: hidden;
}
.kid-hero::after {
  content: ''; position: absolute; right: -50px; bottom: -60px;
  width: 190px; height: 190px; border-radius: 50%;
  background: radial-gradient(circle, rgba(255,183,0,.18), transparent 70%);
}

.kid-av {
  width: 92px; height: 92px; border-radius: 30px;
  display: grid; place-items: center; font-size: 50px;
  flex-shrink: 0; position: relative; z-index: 1;
  animation: wiggle 4s ease-in-out infinite;
}
@keyframes wiggle {
  0%, 100% { transform: rotate(-3deg); }
  50%      { transform: rotate(3deg); }
}

.kid-who { flex: 1; min-width: 170px; position: relative; z-index: 1; }
.kid-name { font-size: 27px; font-weight: 800; display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.kid-level {
  font-size: 14px; font-weight: 700; color: #7a4dd1;
  background: #f3edff; border-radius: 999px; padding: 4px 13px;
  display: inline-flex; align-items: center; gap: 5px;
}
.kid-sub { color: var(--k-muted); font-size: 14px; margin-top: 5px; display: flex; gap: 12px; flex-wrap: wrap; }

.kid-stars { display: flex; gap: 12px; flex-wrap: wrap; position: relative; z-index: 1; }
.star-box {
  border-radius: 20px; padding: 12px 20px; text-align: center; min-width: 118px;
  border: 3px solid transparent;
  transition: transform .18s;
}
.star-box.gold { background: linear-gradient(160deg, #fff6db, #ffefc2); border-color: #ffd76a; }
.star-box.wait { background: linear-gradient(160deg, #eef4ff, #e3ecff); border-color: #c3d7ff; }
.star-box .n { font-size: 32px; font-weight: 900; line-height: 1.1; }
.star-box.gold .n { color: #f0a000; }
.star-box.wait .n { color: #5b7cfa; }
.star-box .t { font-size: 12.5px; color: var(--k-muted); font-weight: 600; }

/* 升级进度条：让"还差多少星升级"一目了然，强化信息层级 */
.kid-levelbar { flex: 1 1 100%; margin-top: 6px; }
.klb-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  font-size: 13px; font-weight: 700; color: var(--k-muted); margin-bottom: 6px;
}
.klb-head b { color: var(--c-warn); }
.klb-track {
  height: 13px; background: rgba(91, 124, 250, .14);
  border-radius: var(--r-pill); overflow: hidden;
}
.klb-track > i {
  display: block; height: 100%; border-radius: var(--r-pill);
  background: linear-gradient(90deg, #5b7cfa, #9b6df0);
  box-shadow: 0 0 10px rgba(91, 124, 250, .45);
  transition: width .7s cubic-bezier(.2, .9, .3, 1);
}

/* ---------------------------------------------------------------- 分区标题 */

.kid-section { margin-bottom: 20px; }
.kid-section > h2 {
  font-size: 20px; font-weight: 800; margin: 0 0 12px;
  display: flex; align-items: center; gap: 9px;
}
.kid-section > h2::before {
  content: ''; width: 6px; height: 20px; border-radius: 4px;
  background: var(--k-gold); flex-shrink: 0;
}
.kid-section > h2 .cnt {
  font-size: 13px; font-weight: 700; color: var(--k-muted);
  background: rgba(255,255,255,.75); border-radius: 999px; padding: 3px 11px;
}

.kid-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(255px, 1fr)); }

/* ---------------------------------------------------------------- 任务卡
   规范（报告 §4.5）：72px 高、左图标 40px 圆形容器、中任务名+说明、右积分。
   三处刻意的收敛：
     · 左侧色条 = 唯一的"类型标签"（学习蓝 / 生活橙）；图标底色只做极浅同色，
       不再出现"色条 + 图标底色 + 金色积分块"三层颜色互相抢注意力；
     · 积分不再用金色胶囊，只把数字染成琥珀色 —— 一屏二十张卡时，
       二十个金色块会盖过任务名本身；
     · 高度统一 72px：之前每张卡按内容高度撑开，两列并排时差 1~2px，
       滚动时边缘参差（报告 2.2 的"对齐错位"）。 */

.task-btn {
  background: var(--k-card); border: 3px solid #fff;
  border-radius: 20px; padding: 12px 14px; box-shadow: var(--k-shadow);
  display: flex; align-items: center; gap: 12px; text-align: left;
  width: 100%; min-height: var(--size-task-card);
  transition: transform .16s, box-shadow .16s;
  position: relative; overflow: hidden;
}
.task-btn::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 7px;
  background: transparent; transition: background .16s;
}
.task-btn.study::before { background: var(--k-study); }
.task-btn.life::before  { background: var(--k-life); }
.task-btn:hover { transform: translateY(-2px); box-shadow: 0 10px 26px rgba(51,64,92,.16); }
/* 按压缩放 0.97 + 阴影加深：孩子按下去要"感觉得到"（报告 2.2「点击无反馈」） */
.task-btn:active { transform: scale(.97); box-shadow: 0 2px 8px rgba(51,64,92,.18); }

.task-btn .ic {
  width: var(--size-task-icon); height: var(--size-task-icon);
  border-radius: 14px; flex-shrink: 0;
  display: grid; place-items: center; font-size: 22px;
  /* 内侧底边一条淡影 = 圆润 3D 卡通的"厚度"，比描边更轻，不会抢色 */
  box-shadow: inset 0 -2px 0 rgba(45, 52, 54, .06);
}
.task-btn.study .ic { background: var(--k-study-soft); }
.task-btn.life  .ic { background: var(--k-life-soft); }

.task-btn .tx { flex: 1; min-width: 0; }
.task-btn .tt { font-size: 16.5px; font-weight: 800; line-height: 1.3; }
.task-btn .ts { font-size: 12.5px; color: var(--k-muted); margin-top: 2px; }

.task-btn .pt {
  font-size: 15px; font-weight: 900; color: #d98600;
  background: transparent; padding: 0 2px; flex-shrink: 0;
  font-variant-numeric: tabular-nums;
}

.task-btn.done { background: linear-gradient(160deg, #f0fbf6, #e4f7ee); cursor: default; }
.task-btn.done:hover { transform: none; box-shadow: var(--k-shadow); }
.task-btn.done:active { transform: none; }
.task-btn.done .tt { color: #12805c; }
.task-btn.done .pt { color: #12805c; }
/* 完成态统一变灰绿：图标底色不再保留维度色，一眼区分"做完了" */
.task-btn.done .ic { background: rgba(18, 128, 92, .10); filter: saturate(.35); }

/* 必做：卡片左上角一条很窄的红色顶边 + 角标。
   只加角标在长列表里会被忽略（"必做"两个字太小），加一条顶边则整列扫过去就看得见。 */
.task-btn.must::after {
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 3px;
  background: linear-gradient(90deg, #ff8fa6, #ff6f8d);
  z-index: 0;
}
.task-btn > * { position: relative; z-index: 1; }
/* 高光扫过：仅 hover 时一次，避免长列表里到处闪 */
.task-btn .shine {
  content: ''; position: absolute; top: 0; left: -70%; width: 50%; height: 100%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .5), transparent);
  transform: skewX(-20deg); z-index: 0; pointer-events: none;
}
.task-btn:hover .shine { animation: shine .85s ease; }

/* 提交中：立刻给出视觉反馈，避免"点了没反应"的错觉 */
.task-btn.saving { opacity: .68; pointer-events: none; }
.task-btn.saving .ts { color: var(--k-study); font-weight: 700; }
.task-btn .tick {
  position: absolute; right: 12px; top: 6px; font-size: 20px;
  animation: popIn .38s cubic-bezier(.2,.9,.3,1.5);
}
@keyframes popIn { from { transform: scale(0) rotate(-25deg); } to { transform: scale(1) rotate(0); } }

.task-btn .wait-tag {
  position: absolute; right: 10px; bottom: 8px;
  font-size: 11px; color: var(--k-muted); background: rgba(255,255,255,.85);
  border-radius: 999px; padding: 1px 8px;
}

/* ---------------------------------------------------------------- 徽章 / 奖励 */

.shelf { display: flex; gap: 11px; flex-wrap: wrap; }
.badge-chip {
  background: #fff; border: 3px solid #ffe9a8; border-radius: 20px;
  padding: 10px 14px; display: flex; align-items: center; gap: 8px;
  box-shadow: 0 4px 12px rgba(51,64,92,.08);
  /* 它现在是 <button>：可点开详情（报告 P7）。按钮默认样式必须清干净，
     否则会带上系统字体的灰色底和居中文本。 */
  font-family: inherit; text-align: left; cursor: pointer; color: inherit;
  transition: transform .15s, box-shadow .15s, border-color .15s;
}
.badge-chip:hover { transform: translateY(-2px); border-color: var(--k-gold); box-shadow: 0 8px 20px rgba(51,64,92,.14); }
.badge-chip:active { transform: scale(.97); }
.badge-chip .em { font-size: 26px; }
.badge-chip b { font-size: 13.5px; }
.badge-chip small { display: block; font-size: 11px; color: var(--k-muted); }

/* ---- 徽章详情 ---- */
.badge-detail { text-align: center; }
.bd-icon {
  font-size: 60px; line-height: 1;
  animation: popIn .4s cubic-bezier(.2,.9,.3,1.5);
}
.badge-detail h3 { margin: 8px 0 4px; }
.bd-desc { margin: 0 0 14px !important; }
.bd-rows {
  background: #fffaeb; border-radius: 16px; padding: 10px 14px;
  text-align: left; margin-bottom: 16px;
}
.bd-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  font-size: 13.5px; padding: 6px 0;
}
.bd-row + .bd-row { border-top: 1px dashed #ffe1a0; }
.bd-row span { color: var(--k-muted); flex: none; }
.bd-row b { font-weight: 800; text-align: right; color: #8a6100; }

/* 奖励卡（报告 2.2「奖励小卖部」）：
   底改成白→极浅蓝的柔和渐变，橙色**只留在金额数字上**。
   原来整张卡用暖色胶囊托着金额，一眼看过去一片橙，反而分不出哪张贵。 */
.gift-btn {
  background: linear-gradient(180deg, #ffffff 0%, #f7faff 100%);
  border: 3px solid #fff; border-radius: 20px;
  box-shadow: var(--k-shadow); padding: 14px 14px 12px; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  transition: transform .16s, box-shadow .16s, border-color .16s;
  width: 100%; position: relative; overflow: hidden;
}
.gift-btn > * { position: relative; z-index: 1; }
.gift-btn::after {
  content: ''; position: absolute; top: 0; left: -70%; width: 50%; height: 100%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .55), transparent);
  transform: skewX(-20deg); z-index: 0; pointer-events: none;
}
.gift-btn:hover::after { animation: shine .85s ease; }
@keyframes shine { from { left: -70%; } to { left: 150%; } }
.gift-btn:hover { transform: translateY(-3px); }
.gift-btn:active { transform: scale(.97); }
/* 选中态：2px 品牌色描边（报告 2.2）。用 box-shadow 而不是加 border，
   否则卡片会比邻卡大 2px、网格跟着抖。 */
.gift-btn.sel {
  border-color: var(--k-study);
  box-shadow: 0 0 0 2px var(--k-study), var(--k-shadow);
}
.gift-btn .gi { font-size: 52px; line-height: 1.05; }   /* 44 → 52，放大 20% */
.gift-btn .gn { font-size: 15px; font-weight: 800; }
.gift-btn .gc {
  font-size: 16px; font-weight: 900; color: #e09400;
  background: transparent; padding: 0;
  font-variant-numeric: tabular-nums;
}
.gift-btn.locked { opacity: .55; }
.gift-btn.locked .gc { color: var(--k-muted); }
.gift-btn .gd { font-size: 11.5px; color: var(--k-muted); min-height: 16px; }
/* 卡片右上角的"已选 N"：和下面那颗按钮重复，但这是孩子扫一眼就能看到的锚点 */
.gift-sel {
  position: absolute; top: 8px; right: 8px;
  font-size: 11.5px; font-weight: 800; color: #fff;
  background: var(--k-study); border-radius: 999px; padding: 2px 9px;
  box-shadow: 0 2px 6px rgba(91, 124, 250, .4);
}

/* 奖励小卖部改成"卡片 + 清单开关"两行：卡片仍是原来那个（点它直接兑换，行为不变），
   下面多一个清单入口。用 .gift-cell 包一层，grid 的格子由 cell 占据，
   卡片本身 width:100% 不变 —— 这样移动端量过的卡片尺寸一点都没动。 */
.gift-cell { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.gift-add {
  border: 2px dashed #c9d6f5; background: #fff; border-radius: 14px;
  padding: 6px; font-size: 12.5px; font-weight: 800; color: #5b7cfa;
  min-height: 34px; width: 100%; font-family: inherit; cursor: pointer;
  transition: background .15s, border-color .15s, color .15s;
}
.gift-add:hover { background: #f4f7ff; }
.gift-add.in { border-style: solid; border-color: var(--k-green); color: #12805c; background: #eefbf5; }
.gift-add:active { transform: translateY(2px); }

/* 兑换清单栏：只有挑了东西才出现。用占位块把最后一点内容顶出来，
   固定栏不会压住任何东西（移动端审计反复强调的那类遮挡） */
.cart-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  display: flex; align-items: center; gap: 12px;
  padding: 10px 16px calc(10px + env(safe-area-inset-bottom));
  background: rgba(255, 255, 255, .96); border-top: 3px solid #ffe1a0;
  box-shadow: 0 -6px 20px rgba(51, 64, 92, .1);
}
.cart-bar-space { height: 66px; }
.cb-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.cb-info b { font-size: 14.5px; color: #2c3a55; }
.cb-info span { font-size: 12px; color: var(--k-muted); }
.cb-go {
  border: none; border-radius: 16px; padding: 12px 20px;
  font-size: 16px; font-weight: 900; color: #fff; background: var(--k-green);
  box-shadow: 0 5px 0 #1f9e6f; min-height: 46px;
  font-family: inherit; cursor: pointer;
}
.cb-go:active { transform: translateY(3px); box-shadow: 0 2px 0 #1f9e6f; }
/* 星星不够时按钮不再是"去兑换"（点了也换不了），改成「去调整」+ 橙色。
   刻意**不禁用**：禁用了孩子就进不去清单，也就没法把多余的东西减掉。 */
.cb-go.lack {
  background: var(--k-gold); box-shadow: 0 5px 0 #d98600;
  animation: cbShake 2.4s ease-in-out infinite;
}
@keyframes cbShake {
  0%, 86%, 100% { transform: translateX(0); }
  88% { transform: translateX(-4px); }
  90% { transform: translateX(4px); }
  92% { transform: translateX(-3px); }
  94% { transform: translateX(3px); }
  96% { transform: translateX(0); }
}

/* 弹窗打开时藏起底部清单栏（报告 2.3「浮动去兑换与弹窗内去兑换并存」）。
   遮罩本来能拦点击，但看得见两个一模一样的按钮才是真的困惑。 */
body.kid-modal-open .cart-bar,
body.kid-modal-open .cart-bar-space { display: none; }

/* 清单整理弹窗 */
.cart-panel { text-align: center; }
.cart-list { text-align: left; margin-bottom: 12px; }
/* 一行放不下「图标 + 名称 + 加减器 + 小计 + 删除」：弹窗只有 360px，
   全部挤在一行时 .cn 会被压到 40px 左右，奖励名变成一字一行。
   改成两行网格 —— 名称独占第一行，加减器落到名称下面，小计与删除右侧通栏。 */
.cart-row {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) auto 32px;
  grid-template-areas:
    "ci cn   sub del"
    "ci step sub del";
  align-items: center;
  column-gap: 10px; row-gap: 6px;
  padding: 10px 0; border-bottom: 1px dashed #e3e8f2;
}
.cart-row:last-child { border-bottom: none; }
.cart-row .ci { grid-area: ci; font-size: 26px; align-self: center; line-height: 1.1; }
.cart-row .cn { grid-area: cn; min-width: 0; }
.cart-row .cn b { display: block; font-size: 13.5px; color: #2c3a55; }
.cart-row .cn small { font-size: 11px; color: var(--k-muted); }
.cart-row .stepper.sm { grid-area: step; justify-self: start; margin: 0; gap: 8px; }
.cart-row .stepper.sm button { width: 38px; height: 38px; font-size: 20px; box-shadow: 0 3px 0 #3a6fcc; }
.cart-row .stepper.sm button.minus { box-shadow: 0 3px 0 #e07c1d; }
.cart-row .stepper.sm .val { font-size: 17px; min-width: 24px; }
.cart-row .csub {
  grid-area: sub; font-size: 13px; font-weight: 800; color: #e09400;
  min-width: 58px; text-align: right; font-variant-numeric: tabular-nums;
}
.cart-row .cdel {
  grid-area: del; align-self: center;
  width: 32px; height: 32px; border: none; border-radius: 50%;
  background: #f1f3f8; color: #8b95ab; font-size: 14px; font-weight: 800;
}
.cart-total {
  background: #fff8e6; border-radius: 14px; padding: 10px 12px;
  font-size: 13.5px; color: #8a6100; margin-bottom: 14px;
}
.cart-total b { font-size: 20px; font-weight: 900; font-variant-numeric: tabular-nums; }
.cart-total span { display: block; font-size: 12px; margin-top: 2px; }

/* 预算条：一眼看出"这单花掉了我多少星星"。
   超额时条子变橙红并钉在 100% —— 数字已经写着"还差 N"，但一条满了的橙红条
   比"还差 15 ⭐"更快被看见（孩子不会逐字读小字）。 */
.cart-budget {
  height: 8px; border-radius: 999px; background: #eef1f8;
  overflow: hidden; margin: -8px 0 10px;
}
.cart-budget i {
  display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, #7ee0b0, var(--k-green));
  transition: width .18s ease, background .18s ease;
}
.cart-budget.lack i { background: linear-gradient(90deg, #ffc46b, #f08a2c); }

/* 行内"为什么加不了"——比给一个死按钮有用 */
.cart-row .cn em.cap {
  display: block; font-style: normal; font-size: 11px; font-weight: 700;
  color: #d97a00; margin-top: 2px;
}
.cart-row.over .cn em.cap { color: #d64545; }
.cart-row.over .csub { color: #d64545; }

/* 到上限的「＋」：灰掉但仍然占位（列表不会因为禁用而跳动） */
.cart-row .stepper.sm button:disabled,
.cart-row .stepper.sm button[aria-disabled="true"] {
  background: #eef1f8; color: #b6bece; box-shadow: 0 3px 0 #dfe4ee;
  cursor: not-allowed; transform: none;
}
.cart-row .stepper.sm button:disabled:active { transform: none; box-shadow: 0 3px 0 #dfe4ee; }

.cart-cap-note {
  font-size: 12px; color: #d97a00; font-weight: 700;
  margin: -6px 0 12px; text-align: left;
}

/* 「加入清单」数量选择器：实时积分反馈 + 上限说明。
   .rq-line 是数字行（随步进实时刷新），.rq-cap 是为什么最多 N 个。 */
.rq-line {
  font-size: 14px; font-weight: 700; color: #2c3a55;
  margin: -6px 0 4px; text-align: center;
}
.rq-cap {
  font-size: 12px; color: #d97a00; font-weight: 700;
  margin: 0 0 14px; text-align: center;
}

/* 批量兑换确认框（数字全部来自服务端预览） */
.cart-confirm {
  background: var(--k-study-soft); border-radius: 16px;
  padding: 12px 14px; margin: 0 0 12px; text-align: left;
}
.cr-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  font-size: 13.5px; padding: 5px 0;
}
.cr-row .k { color: var(--k-muted); flex: 1; min-width: 0; }
.cr-row b { font-weight: 800; white-space: nowrap; font-variant-numeric: tabular-nums; }
.cr-row.sum {
  margin-top: 6px; padding-top: 10px;
  border-top: 1px dashed rgba(58, 91, 208, .28);
}
.cr-row.sum .k { font-size: 14px; font-weight: 700; color: #2c3a55; }
/* 合计金额：弹一下，让孩子的视线落到"这次要花多少"上（报告 2.3） */
.cr-row.sum b {
  font-size: 20px; color: var(--k-pink);
  animation: sumBounce .55s cubic-bezier(.2,.9,.3,1.6);
}
@keyframes sumBounce {
  0%   { transform: scale(.72); opacity: .3; }
  55%  { transform: scale(1.12); }
  100% { transform: scale(1); opacity: 1; }
}
.cart-notes { margin: 0 0 16px; text-align: left; }
.cart-notes div {
  font-size: 12.5px; line-height: 1.6; color: #a8620a;
  padding: 6px 11px; border-radius: 12px; background: #fff8e6;
}
.cart-notes div + div { margin-top: 5px; }

/* ---------------------------------------------------------------- 空状态 / 提示
   统一结构：插画（ke-art）+ 一句人话（ke-title）+ 一条下一步（ke-hint）。
   只有一行灰字是最没用的空状态 —— 孩子看完不知道要做什么（报告 §4.5）。 */

.kid-empty {
  background: rgba(255,255,255,.8); border-radius: 22px;
  padding: 26px 22px; text-align: center; color: var(--k-muted); font-size: 15px;
}
.ke-art {
  position: relative; display: inline-block; line-height: 1;
  margin-bottom: 10px;
}
.ke-art > span:first-child {
  font-size: 46px; display: inline-block;
  filter: grayscale(.15);
}
/* 背后那颗虚影星：给画面一个"还有东西没拿到"的暗示 */
.ke-ghost {
  position: absolute; right: -14px; top: -6px;
  font-size: 20px; opacity: .5;
  animation: floaty 3.4s ease-in-out infinite;
}
.ke-title { margin: 0; font-size: 15px; font-weight: 800; color: var(--k-ink); }
.ke-hint { margin: 6px 0 0; font-size: 12.5px; line-height: 1.65; color: var(--k-muted); }
.ke-hint b { color: #f0a000; }

/* 徽章墙空状态：三枚"还没点亮"的虚影徽章 */
.badge-empty { padding: 22px 18px; }
.be-row { display: flex; justify-content: center; gap: 14px; margin-bottom: 12px; }
.be-ghost {
  font-size: 34px; line-height: 1;
  opacity: .28; filter: grayscale(1);
  border: 2.5px dashed #d8deea; border-radius: 18px;
  padding: 8px 10px;
  animation: floaty 4.2s ease-in-out infinite;
}
.be-ghost:nth-child(2) { animation-delay: .6s; opacity: .36; }
.be-ghost:nth-child(3) { animation-delay: 1.2s; opacity: .22; }
.kid-banner {
  background: linear-gradient(100deg, #fff7db, #ffefd0);
  border: 3px solid #ffe1a0; border-radius: 20px;
  padding: 12px 16px; margin-bottom: 14px;
  font-size: 14px; font-weight: 600; color: #8a6100;
  display: flex; align-items: center; gap: 10px;
}
.kid-banner.all-done {
  background: linear-gradient(100deg, #e6fbf1, #d5f7e6);
  border-color: #a9ebcd; color: #107952;
}

/* ---------------------------------------------------------------- 每日目标卡片 */

.goal-card {
  background: #fff; border-radius: var(--k-radius);
  box-shadow: var(--k-shadow), 0 0 0 1.5px rgba(255, 183, 0, .35);
  padding: 18px 20px; margin-bottom: 18px;
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  border: 4px solid #fff; position: relative; overflow: hidden;
}
.goal-card::after {
  content: ''; position: absolute; right: -46px; top: -54px;
  width: 170px; height: 170px; border-radius: 50%;
  background: radial-gradient(circle, rgba(91, 124, 250, .15), transparent 70%);
}
.goal-card.reached { box-shadow: var(--k-shadow), 0 0 0 1.5px rgba(43, 196, 138, .5); }

.goal-ring {
  width: 96px; height: 96px; border-radius: 50%; flex-shrink: 0;
  background: conic-gradient(var(--k-gold) calc(var(--p) * 1%), #eef1f7 0);
  display: grid; place-items: center; position: relative; z-index: 1;
  transition: background .7s cubic-bezier(.2, .9, .3, 1);
}
.goal-ring-inner {
  width: 74px; height: 74px; border-radius: 50%; background: #fff;
  display: grid; place-items: center; box-shadow: inset 0 2px 6px rgba(0,0,0,.06);
}
.goal-pct { font-size: 27px; font-weight: 900; color: #f0a000; line-height: 1; font-variant-numeric: tabular-nums; }
.goal-pct span { font-size: 14px; }
.goal-ring-label { font-size: 11px; font-weight: 700; color: var(--k-muted); margin-top: 2px; }

.goal-body { flex: 1; min-width: 200px; position: relative; z-index: 1; }
.goal-title { font-size: 19px; font-weight: 800; display: flex; align-items: center; gap: 8px; }
.goal-desc { font-size: 14px; color: var(--k-ink); margin: 5px 0 10px; }
.goal-desc b { color: #f0a000; font-variant-numeric: tabular-nums; }
.goal-bar { height: 13px; background: rgba(255, 183, 0, .16); border-radius: var(--r-pill); overflow: hidden; }
.goal-bar > i {
  display: block; height: 100%; border-radius: var(--r-pill);
  background: linear-gradient(90deg, #ffb700, #ff9b3d);
  box-shadow: 0 0 10px rgba(255, 183, 0, .45);
  transition: width .7s cubic-bezier(.2, .9, .3, 1);
}
.goal-card.reached .goal-pct { color: #1f9e6f; }
.goal-card.reached .goal-bar > i { background: linear-gradient(90deg, #2bc48a, #43d6a0); }

/* 达成时的粒子感：环上三颗小星星向外飘一下。
   纯 CSS、一次性动画 —— 不做持续的"闪烁"，否则孩子盯着看会分心。 */
.goal-ring.reached { animation: goalPop .5s cubic-bezier(.2,.9,.3,1.6); }
@keyframes goalPop { 0% { transform: scale(.9); } 60% { transform: scale(1.05); } 100% { transform: scale(1); } }
.goal-spark {
  position: absolute; font-size: 15px; pointer-events: none;
  animation: sparkFly 1.6s ease-out infinite;
}
.goal-spark.s1 { top: -6px; right: 2px; animation-delay: 0s; }
.goal-spark.s2 { bottom: -4px; left: -2px; animation-delay: .5s; }
.goal-spark.s3 { top: 40%; right: -12px; animation-delay: 1s; font-size: 12px; }
@keyframes sparkFly {
  0%   { opacity: 0; transform: translateY(4px) scale(.6); }
  30%  { opacity: 1; }
  100% { opacity: 0; transform: translateY(-16px) scale(1.05); }
}

/* 三个并排数字：已到手 / 等家长确认 / 目标。
   报告 P3 的核心 —— 把"可用"和"待确认"拆开，孩子才看得懂为什么
   进度到了 100% 却换不了礼物。 */
.goal-metrics {
  display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 10px;
}
.gm {
  flex: 1 1 0; min-width: 78px;
  background: #f7f9fe; border-radius: 14px;
  padding: 7px 6px; text-align: center;
  display: flex; flex-direction: column; gap: 1px;
}
.gm-n { font-size: 19px; font-weight: 900; color: var(--k-ink); font-variant-numeric: tabular-nums; line-height: 1.15; }
.gm-t { font-size: 11px; font-weight: 700; color: var(--k-muted); }
.gm.ok { background: #eefaf4; }
.gm.ok .gm-n { color: #12805c; }
.gm.wait { background: #eef3ff; }
.gm.wait .gm-n { color: #4266ea; }
.gm.wait.zero { opacity: .55; }
.goal-note {
  margin: 9px 0 0 !important; font-size: 12px !important; line-height: 1.6;
  color: #8a6100; background: #fff8e6; border-radius: 12px;
  padding: 7px 11px; text-align: left;
}

/* ---------------------------------------------------------------- 趣味学习分区 */

.kid-section.fun-zone > h2::before {
  background: linear-gradient(135deg, var(--k-pink), var(--k-purple));
}
.fun-tip {
  font-size: 13.5px; color: #8a4ec0; font-weight: 600; margin: -4px 0 12px;
  background: #f6edff; border-radius: 14px; padding: 8px 14px; display: inline-block;
}
.fun-zone .task-btn .ic { background: linear-gradient(160deg, #ffe1ef, #f3e3ff); }
.fun-zone .task-btn::before { background: linear-gradient(180deg, var(--k-pink), var(--k-purple)); }

/* ---------------------------------------------------------------- 成就进度 */

.achv-bar { height: 10px; background: rgba(255, 183, 0, .16); border-radius: var(--r-pill); overflow: hidden; margin-top: 12px; }
.achv-bar > i {
  display: block; height: 100%; border-radius: var(--r-pill);
  background: linear-gradient(90deg, #ffb700, #ffd76a);
  transition: width .7s cubic-bezier(.2, .9, .3, 1);
}
.achv-tip { font-size: 12.5px; color: var(--k-muted); margin: 7px 2px 0; }
.achv-tip b { color: #f0a000; }

/* ---------------------------------------------------------------- Toast */

.kid-toasts {
  position: fixed; top: calc(16px + env(safe-area-inset-top)); left: 50%; transform: translateX(-50%);
  z-index: 400; display: flex; flex-direction: column; gap: 9px; align-items: center;
  pointer-events: none; width: max-content; max-width: 92vw;
}
.kid-toast {
  background: #fff; border-radius: 999px; padding: 11px 22px;
  box-shadow: 0 10px 30px rgba(51,64,92,.22);
  font-size: 16px; font-weight: 700; display: flex; align-items: center; gap: 9px;
  animation: dropIn .3s cubic-bezier(.2,.9,.3,1.4);
}
@keyframes dropIn { from { opacity: 0; transform: translateY(-18px) scale(.9); } to { opacity: 1; transform: none; } }

/* ---------------------------------------------------------------- 撒花 */

.kid-confetti { position: fixed; inset: 0; pointer-events: none; z-index: 500; overflow: hidden; }
.kid-confetti i {
  position: absolute; width: 11px; height: 16px; border-radius: 3px;
  animation: kfall linear forwards;
}
@keyframes kfall {
  0%   { transform: translateY(-40px) rotate(0deg); opacity: 1; }
  100% { transform: translateY(105vh) rotate(760deg); opacity: .05; }
}

/* ---------------------------------------------------------------- 数量弹窗 */

.kid-mask {
  position: fixed; inset: 0; background: rgba(40,50,70,.5);
  display: grid; place-items: center; z-index: 600; padding: 20px;
}
.kid-modal {
  background: #fff; border-radius: 28px; padding: 24px;
  width: 100%; max-width: 360px; text-align: center;
  box-shadow: 0 20px 60px rgba(0,0,0,.25);
  animation: popIn .28s cubic-bezier(.2,.9,.3,1.4);
  /* 清单可能有很多行：给一个不超出遮罩的高度上限，超出时内部滚动。
     小弹窗（本来就 252px 左右）不受影响 —— 这条只是兜底，不会改变已有实测值。 */
  max-height: calc(100vh - 40px);
  max-height: calc(100dvh - 40px);
  overflow-y: auto;
}
.kid-modal h3 {
  font-size: 22px; margin: 0 0 6px; line-height: 1.35;
  /* 标题里的 emoji 跟着字号一起放大（报告 §5「弹窗图标放大」）。
     22px 是权衡结果：再大标题会换行，孩子反而更难一眼读完。 */
}
.kid-modal p { color: var(--k-muted); font-size: 13.5px; margin: 0 0 16px; }
.stepper { display: flex; align-items: center; justify-content: center; gap: 18px; margin-bottom: 18px; }
.stepper button {
  width: 54px; height: 54px; border-radius: 50%; border: none;
  font-size: 28px; font-weight: 800; color: #fff; background: var(--k-study);
  box-shadow: 0 5px 0 #3a6fcc;
}
.stepper button:active { transform: translateY(3px); box-shadow: 0 2px 0 #3a6fcc; }
.stepper button.minus { background: var(--k-life); box-shadow: 0 5px 0 #e07c1d; }
.stepper button.minus:active { box-shadow: 0 2px 0 #e07c1d; }
.stepper .val { font-size: 40px; font-weight: 900; min-width: 76px; }
.stepper .unit { font-size: 15px; color: var(--k-muted); }
.kid-modal .acts { display: flex; gap: 10px; }
.kid-modal .acts button {
  flex: 1; border: none; border-radius: 16px; padding: 13px;
  font-size: 16px; font-weight: 800; color: #fff; background: var(--k-green);
  box-shadow: 0 5px 0 #1f9e6f;
}
.kid-modal .acts button:active { transform: translateY(3px); box-shadow: 0 2px 0 #1f9e6f; }
.kid-modal .acts button.gray { background: #c3cbdb; box-shadow: 0 5px 0 #a5aec0; }
.kid-modal .acts button.gray:active { box-shadow: 0 2px 0 #a5aec0; }
.kid-modal .acts button.warn { background: var(--k-pink); box-shadow: 0 5px 0 #d84f70; }
.kid-modal .acts button.warn:active { box-shadow: 0 2px 0 #d84f70; }

/* ---------- 打卡时间点分档：打卡前先看清「时间点 ↔ 分值」 ---------- */
.kid-tiers {
  display: flex; flex-direction: column; gap: 6px;
  margin: 0 0 12px; text-align: left;
}
.kid-tier {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 13px; border-radius: 14px;
  background: var(--k-study-soft); color: #3a5bd0;
  font-size: 13.5px; font-weight: 700;
}
.kid-tier .kt-range { flex: 1; }
.kid-tier .kt-pts { font-variant-numeric: tabular-nums; font-weight: 800; }
.kid-tier.deduct { background: #ffeef1; color: #d4475f; }
.kid-tier.now { background: var(--k-gold); color: #7a4c00; box-shadow: 0 4px 0 rgba(255, 183, 0, .35); }
.kid-tier .kt-now {
  font-size: 11px; font-weight: 800; padding: 2px 8px;
  border-radius: 999px; background: #fff; color: #7a4c00;
}
.kid-tier-tip { font-size: 12.5px !important; margin: 0 0 14px !important; }

/* ---------- 打卡提交前「选择本次分值」：档位行变成可点按钮 ---------- */
.kid-tiers.pick .kid-tier {
  width: 100%; border: none; font-family: inherit; cursor: pointer;
  box-shadow: 0 3px 0 rgba(58, 91, 208, .18);
  transition: transform .12s, box-shadow .12s;
}
.kid-tiers.pick .kid-tier.deduct { box-shadow: 0 3px 0 rgba(212, 71, 95, .18); }
.kid-tiers.pick .kid-tier:active { transform: translateY(2px); box-shadow: none; }
.kid-tier .kt-sel { display: none; font-size: 15px; font-weight: 900; }
.kid-tier.sel { outline: 3px solid var(--k-green); outline-offset: 1px; }
.kid-tier.sel .kt-sel { display: inline; }
/* 选中的「自选」档位提示：只在孩子改写了自动判定时出现 */
.kid-tier-note {
  font-size: 12.5px !important; margin: -8px 0 14px !important;
  padding: 9px 11px; border-radius: 12px;
  background: #fff8e6; color: #a8620a; text-align: left;
}
.kid-tier-note:empty { display: none; }
.kid-modal .acts button.gray:active { box-shadow: 0 2px 0 #a5aec0; }

/* ---------- 打卡确认弹窗：提交前把「这次记什么」原样摆出来再问一次 ---------- */
.kc-lead { font-size: 13.5px !important; margin: 0 0 12px !important; }
.kc-box {
  background: var(--k-study-soft); border-radius: 16px;
  padding: 12px 14px; margin: 0 0 12px; text-align: left;
}
.kc-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  font-size: 13.5px; padding: 5px 0;
}
.kc-row > span { color: var(--k-muted); flex: none; }
.kc-row > b {
  font-weight: 800; text-align: right; word-break: break-word;
  font-variant-numeric: tabular-nums;
}
/* 本次分值：这一行是孩子真正关心的，单独放大并用分隔线隔开 */
.kc-pts {
  margin-top: 6px; padding-top: 11px;
  border-top: 1px dashed rgba(58, 91, 208, .28);
  align-items: center;
}
.kc-pts > span { font-size: 14px; font-weight: 700; }
.kc-pts > b { font-size: 24px; color: var(--k-green); }
.kc-pts.neg > b { color: var(--k-pink); }
.kc-tag {
  font-style: normal; font-size: 10.5px; font-weight: 800;
  margin-left: 6px; padding: 2px 7px; border-radius: 999px;
  background: #fff; color: #7a4c00; vertical-align: middle;
}
.kc-notes {
  margin: 0 0 16px; padding: 0 0 0 4px; list-style: none; text-align: left;
}
.kc-notes li {
  font-size: 12.5px; line-height: 1.6; color: #a8620a;
  padding: 7px 11px; border-radius: 12px; background: #fff8e6;
}
.kc-notes li + li { margin-top: 6px; }

/* ---------------------------------------------------------------- 数字对齐 */

.star-box .n, .klb-head b, .kid-name, .kid-level { font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------- 必打卡项提示 / 成长统计 */

/* 必打卡未完成提示条（与 .kid-banner 同族，配色改为"要加油"的粉红 / "做到啦"的绿） */
.kid-banner.req {
  background: linear-gradient(100deg, #ffeef2, #ffe2ea);
  border-color: #ffc2cf; color: #b02a44;
  align-items: flex-start;
}
.kid-banner.req.ok {
  background: linear-gradient(100deg, #e6fbf1, #d5f7e6);
  border-color: #a9ebcd; color: #107952;
}
.kid-banner.req.off {
  background: linear-gradient(100deg, #f2f5fb, #eaeff8);
  border-color: #d9e0ee; color: #5b6478;
}
.kid-banner.req .kb-body { flex: 1; min-width: 0; }
.kid-banner.req .kb-line { line-height: 1.5; }
.kid-banner.req .kb-line b { font-size: 16px; }
.kid-banner.req .kb-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.kid-banner.req .kb-chip {
  background: #fff; border: 2px solid #ffd0da; border-radius: 999px;
  padding: 2px 11px; font-size: 12.5px; font-weight: 700; color: #b02a44;
}
.kid-banner.req.ok .kb-chip { border-color: #a9ebcd; color: #107952; }
.kid-banner.req .kb-bar {
  height: 8px; border-radius: 999px; background: #ffd9e2;
  overflow: hidden; margin-top: 9px;
}
.kid-banner.req .kb-bar > i {
  display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, #ff8fa6, #ff6f8d); transition: width .3s;
}
.kid-banner.req.ok .kb-bar > i { background: linear-gradient(90deg, #2bc48a, #43d6a0); }

/* 必做标记（任务卡片标题旁的小角标 / 各弹窗标题旁）。
   报告 2.2：必打卡项要能"一眼看出来"。原来是浅粉底 + 深红字的软角标，
   混在一堆 emoji 里几乎看不见；改成实心红底白字，尺寸略缩、位置不变，
   这样既不增加视觉噪音，又能在扫视时被抓住。 */
.req-chip {
  display: inline-block; margin-left: 6px; vertical-align: middle;
  padding: 1px 8px; border-radius: 999px;
  background: linear-gradient(120deg, #ff7d95, #f2546f);
  border: none; color: #fff;
  font-size: 11px; font-weight: 800; letter-spacing: .3px;
  box-shadow: 0 2px 5px rgba(242, 84, 111, .35);
}

/* 成长统计卡片 */
.kid-growth { display: grid; gap: 11px; grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); }
.kid-gt {
  background: #fff; border-radius: 18px; padding: 12px 10px;
  box-shadow: var(--k-shadow), 0 0 0 1.5px var(--c-line);
  text-align: center;
}
.kid-gt .n { font-size: 25px; font-weight: 900; line-height: 1.15; font-variant-numeric: tabular-nums; }
.kid-gt .t { font-size: 12px; color: var(--k-muted); font-weight: 700; margin-top: 3px; }
.kid-gt .s { font-size: 11px; color: var(--k-muted); margin-top: 1px; }
.kid-gt.c1 .n { color: #5b7cfa; }
.kid-gt.c2 .n { color: #f0a000; }
.kid-gt.c3 .n { color: #9b6df0; }
.kid-gt.c4 .n { color: #ff9f43; }
.kid-gt.c5 .n { color: #ff7a45; }
.kid-gt.c6 .n { color: #2bc48a; }

.kid-chart-card {
  background: #fff; border-radius: var(--k-radius); padding: 14px 16px;
  box-shadow: var(--k-shadow), 0 0 0 1.5px var(--c-line); margin-top: 11px;
}
.kid-chart-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; flex-wrap: wrap; margin-bottom: 8px;
}
.kid-chart-head .h { font-size: 14px; font-weight: 800; color: var(--k-ink); }
.kid-chart-head .h small { font-weight: 600; color: var(--k-muted); font-size: 12px; margin-left: 4px; }
.kid-seg { display: inline-flex; gap: 3px; padding: 3px; background: #f1f4fb; border-radius: 999px; }
.kid-seg button {
  border: none; background: transparent; cursor: pointer; font: inherit;
  font-size: 12.5px; font-weight: 700; color: var(--k-muted);
  padding: 4px 12px; border-radius: 999px;
}
.kid-seg button.active { background: #fff; color: #5b7cfa; box-shadow: 0 1px 3px rgba(30, 36, 51, .12); }
.kid-chart { min-height: 110px; }
/* 柱状 ↔ 折线切换的过渡：重绘后淡入 + 轻微上浮，让"换了"这件事被看见（报告 2.2） */
.kid-chart svg { animation: chartFade .3s ease-out; }
@keyframes chartFade {
  from { opacity: .25; transform: translateY(5px); }
  to   { opacity: 1; transform: none; }
}

/* 图例 + 口径说明。只在真的存在"待确认"数据时才多一行，平时不占位 */
.kid-chart-foot { margin-top: 8px; }
.kcf-legend { display: flex; gap: 14px; flex-wrap: wrap; font-size: 11.5px; color: var(--k-muted); }
.kcf-item { display: inline-flex; align-items: center; gap: 5px; }
.kcf-dot {
  width: 11px; height: 11px; border-radius: 3px; display: inline-block;
}
.kcf-dot.solid { background: #5b7cfa; }
/* 斜纹小样：与 SVG 里的 pattern 用同一套角度，视觉上对得上 */
.kcf-dot.hatch {
  background:
    repeating-linear-gradient(45deg, rgba(143,176,255,.75) 0 2px, rgba(143,176,255,.18) 2px 5px);
  border: 1px dashed #8fb0ff;
}
.kcf-note {
  margin: 6px 0 0; font-size: 11.5px; line-height: 1.6; color: var(--k-muted);
}
.kcf-note b { color: #4266ea; }

@media (max-width: 620px) {
  .kid-growth { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 9px; }
  .kid-gt { padding: 10px 6px; border-radius: 15px; }
  .kid-gt .n { font-size: 20px; }
  .kid-gt .t { font-size: 11px; }
  .kid-gt .s { display: none; }
}

/* ---------------------------------------------------------------- 响应式 */

@media (max-width: 620px) {
  .kid-title { font-size: 20px; }
  .kid-hero { padding: 16px; gap: 14px; }
  .kid-av { width: 74px; height: 74px; font-size: 40px; border-radius: 24px; }
  .kid-name { font-size: 22px; }
  .star-box { flex: 1; min-width: 100px; padding: 10px 12px; }
  .star-box .n { font-size: 26px; }
  .kid-grid { grid-template-columns: 1fr; }
  .goal-card { padding: 14px; gap: 14px; }
  .goal-ring { width: 78px; height: 78px; }
  .goal-ring-inner { width: 60px; height: 60px; }
  .goal-pct { font-size: 22px; }
  .fun-tip { display: block; }
}

/* ---------------------------------------------------------------- 无障碍：尊重"减少动态效果"偏好 */

@media (prefers-reduced-motion: reduce) {
  .sky-deco span,
  .kid-av,
  .kid-btn-anim,
  .task-btn .shine,
  .gift-btn::after,
  .goal-spark,
  .be-ghost,
  .kl-deco span,
  .kl-star,
  .ke-ghost,
  .cb-go.lack { animation: none !important; }
  .kid-toast,
  .kid-modal,
  .task-btn .tick,
  .klb-track > i,
  .kid-chart svg,
  .cr-row.sum b,
  .cart-budget i,
  .goal-ring.reached { transition: none !important; animation: none !important; }
  .kid-confetti { display: none !important; }
}

/* ---------------------------------------------------------------- 打卡照片 */
/* 任务卡外层容器：让"照片条"能挂在按钮**外面**（button 里不能再嵌 button）。 */
.task-cell { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
.task-cell .task-btn { width: 100%; }

.photo-strip { display: flex; align-items: center; gap: 7px; padding: 0 5px; flex-wrap: wrap; }

.photo-thumb {
  width: 46px; height: 46px; object-fit: cover; border-radius: 12px;
  border: 3px solid #fff; box-shadow: 0 3px 10px rgba(51, 64, 92, .18);
  cursor: pointer; background: var(--k-study-soft); transition: transform .15s;
}
.photo-thumb:hover { transform: scale(1.06); }

.photo-add {
  width: 46px; height: 46px; border-radius: 12px; cursor: pointer;
  border: 3px dashed #c7d2fe; background: #fff; font-size: 20px; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  transition: transform .15s, border-color .15s;
}
.photo-add:hover { transform: scale(1.06); border-color: var(--k-purple); }

/* 大图查看：铺满屏幕，点任意位置关闭 */
.photo-viewer {
  position: fixed; inset: 0; z-index: 999; cursor: zoom-out;
  background: rgba(20, 24, 40, .82); backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center; padding: 24px;
}
.photo-viewer img {
  max-width: 100%; max-height: 100%; border-radius: 18px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .45); background: #fff;
}

/* ---------------------------------------------------------------- 语音开关 */
.tts-toggle {
  position: fixed; right: 14px; bottom: 14px; z-index: 60;
  width: 44px; height: 44px; border-radius: 50%; cursor: pointer;
  border: 3px solid #fff; background: var(--k-card);
  box-shadow: var(--k-shadow); font-size: 20px; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  transition: transform .15s;
}
.tts-toggle:hover { transform: scale(1.08); }
.tts-toggle[aria-pressed="false"] { opacity: .72; }

/* 清单栏（fixed 贴底，z-index 60）与语音开关（同样 fixed 贴底、同样 60）
   会叠在一起，语音按钮正好压在「去兑换」上。清单栏出现时把语音按钮抬到栏上方。
   标记由 syncCartUI() 打在 body 上 —— CSS 没法反向选到前面的兄弟节点。 */
body.has-cart-bar .tts-toggle { bottom: 78px; }

@media (max-width: 620px) {
  /* 原来写的是「避开底部标签栏」，但孩子端根本没有标签栏（.tabbar 只存在于家长端
     index.html），这个让位是多余的 —— 白白把按钮抬高 62px，反而更容易压住正文。
     改成贴底 16px，同时把内容区的下内边距加高到 72px：
     滚到页面底部时按钮浮在留白上，而不是盖在文字上。
     背景改成半透明 + 毛玻璃，即使中途滚动掠过正文也不那么挡字。 */
  .kid-wrap { padding-bottom: 72px; }
  .tts-toggle {
    right: 10px; bottom: 16px;
    background: rgba(255, 255, 255, .72);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
  }
}

/* ---------------------------------------------------------------- 触屏命中区 */
/* 孩子端最窄的两个控件：图表切换按钮高 24px、可选档位行高 37px，
   都低于 44px 的最小触控尺寸。同样只扩命中区、不改外观。 */
@media (max-width: 880px) {
  .kid-seg button, .kid-tier { position: relative; }
  .kid-seg button::after { content: ''; position: absolute; inset: -10px 0; }  /* 24 → 44 */
  .kid-tier::after { content: ''; position: absolute; inset: -4px 0; }         /* 37 → 45 */
  .kid-tiers { gap: 9px; }   /* 档位行之间留出外扩的余量，避免相邻行命中区互相重叠 */
}

/* ==========================================================================
   孩子端登录（4 位 PIN）
   --------------------------------------------------------------------------
   只在「有孩子设了 PIN」时出现，全家都没上锁的话连这一屏都不会渲染。
   所以它可以做得"有仪式感"一点：大名字、大数字键、大圆点。
   触屏命中区一律 ≥ 56px —— 4 岁到 12 岁的孩子都在用，手指精度差别很大。
   ========================================================================== */

.kid-login {
  /* 460 → 560：报告 2.1 说"内容区过小、中下部空旷"。
     卡片放大之后如果容器还窄，两列会挤成一列、页面更长更空，
     所以容器跟着放宽，让两张卡并排把首屏填满。 */
  max-width: 560px; margin: 0 auto;
  padding: 8px 4px 32px;
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
  position: relative;
}

/* 背景装饰（云朵 / 星星）：纯装饰，pointer-events:none 不挡点击。
   只放在选人页 —— 输密码时任何多余元素都会分散注意力。 */
.kl-deco { position: absolute; inset: -8px -10px auto -10px; height: 0; pointer-events: none; z-index: 0; }
.kl-deco span {
  position: absolute; opacity: .5; font-size: 26px;
  animation: floaty 8s ease-in-out infinite;
}
.kl-deco .d1 { left: 2%;  top: 30px; }
.kl-deco .d2 { right: 4%; top: 6px;  font-size: 20px; animation-delay: 1.1s; }
.kl-deco .d3 { left: 12%; top: 150px; font-size: 18px; animation-delay: 2.2s; }
.kl-deco .d4 { right: 8%; top: 190px; animation-delay: .6s; }
.kid-login > *:not(.kl-deco) { position: relative; z-index: 1; }

.kl-hero { margin: 10px 0 22px; }
.kl-hero h1 {
  margin: 12px 0 6px; font-size: 26px; font-weight: 800; letter-spacing: .5px;
  color: var(--k-ink);
}
.kl-hero p { margin: 0; font-size: 14px; color: var(--k-muted); line-height: 1.6; }
.kl-star {
  font-size: 52px; line-height: 1;
  animation: floaty 3.2s ease-in-out infinite;
  display: inline-block;
}
.kl-av {
  width: 62px; height: 62px; border-radius: 22px;
  display: grid; place-items: center; font-size: 34px;
  margin: 0 auto;
}
.kl-av.big { width: 88px; height: 88px; border-radius: 28px; font-size: 46px; }

/* ---- 选人 ----
   卡片放大到「一眼就能认出自己」的尺寸（报告 2.1：头像 96px）。
   132px 的下限是给"名字很短的小屏"留的余量：320px 屏上仍然两列并排。 */
.kl-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 14px; width: 100%; margin-bottom: 20px;
}
.kl-child {
  border: 4px solid #fff; border-radius: var(--k-radius);
  background: rgba(255,255,255,.86);
  box-shadow: 0 4px 0 rgba(0,0,0,.04), 0 10px 22px rgba(51,64,92,.10);
  padding: 22px 14px 18px;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  cursor: pointer; font-family: inherit;
  min-height: 214px; justify-content: center;
  position: relative; overflow: hidden;
  transition: transform .15s, box-shadow .15s;
}
.kl-child:hover { transform: translateY(-4px); box-shadow: 0 10px 26px rgba(51,64,92,.18); }
.kl-child:active { transform: scale(.97); }
.kl-child .kl-av {
  width: 96px; height: 96px; border-radius: 32px; font-size: 50px;
  background: #fff;
  box-shadow: inset 0 -3px 0 rgba(45, 52, 54, .05);
  transition: transform .18s;
}
.kl-child:hover .kl-av { transform: scale(1.05) rotate(-2deg); }
.kl-name { font-size: 18px; font-weight: 800; color: var(--k-ink); }
.kl-badge {
  font-size: 11.5px; font-weight: 700; padding: 3px 10px; border-radius: 999px;
}
.kl-badge.on { background: #fff2d6; color: #9a6a00; }
.kl-badge.off { background: #e7f8ef; color: #12805a; }
/* 免密账户再补一行说明（报告 2.1）：「✨ 直接进」是角标，这行是给人话解释 */
.kl-hint { font-size: 11px; color: var(--k-muted); margin-top: -4px; }

/* ---- 密码 ---- */
.kl-dots {
  display: flex; gap: 16px; justify-content: center;
  margin: 4px 0 6px; padding: 14px 0;
}
.kl-dot {
  width: 20px; height: 20px; border-radius: 50%;
  background: rgba(255,255,255,.9);
  box-shadow: inset 0 0 0 2.5px rgba(91,124,250,.30);
  transition: background .12s, transform .12s, box-shadow .12s;
}
.kl-dot.on {
  background: var(--k-gold);
  box-shadow: inset 0 0 0 2.5px #fff, 0 0 0 2px var(--k-gold);
  transform: scale(1.12);
}
.kl-dots.shake { animation: klShake .42s; }
@keyframes klShake {
  0%, 100% { transform: translateX(0); }
  15%      { transform: translateX(-9px); }
  35%      { transform: translateX(8px); }
  55%      { transform: translateX(-6px); }
  75%      { transform: translateX(4px); }
}

.kl-error {
  min-height: 20px; margin: 0 0 10px; font-size: 13.5px; font-weight: 700;
  color: #e0533f;
}

.kl-keys {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 11px; width: 100%; max-width: 330px;
  transition: opacity .15s;
}
.kl-keys.disabled, .kl-keys.busy { opacity: .45; pointer-events: none; }
.kl-key {
  height: 62px; border: none; border-radius: 20px;
  background: #fff; color: var(--k-ink);
  font-size: 26px; font-weight: 800; font-family: inherit;
  box-shadow: 0 4px 0 rgba(0,0,0,.06), 0 8px 18px rgba(51,64,92,.10);
  cursor: pointer;
  transition: transform .1s, box-shadow .1s;
}
.kl-key:active { transform: translateY(3px); box-shadow: 0 1px 0 rgba(0,0,0,.06); }
.kl-key.fn { background: rgba(255,255,255,.62); font-size: 22px; }
.kl-key.ghost { background: transparent; box-shadow: none; pointer-events: none; }

.kl-remember {
  margin-top: 20px; border: none; background: transparent; cursor: pointer;
  font-family: inherit; font-size: 13.5px; font-weight: 700; color: var(--k-muted);
  display: inline-flex; align-items: center; gap: 8px; padding: 8px 4px;
}
/* 选人页的「记住我」贴着网格，间距要小一点 */
.kl-grid + .kl-remember { margin-top: 2px; }
.kl-remember-tip {
  margin: 0 0 10px; font-size: 11.5px; color: var(--k-muted);
}
.kl-remember .kl-check {
  width: 22px; height: 22px; border-radius: 8px;
  border: 2.5px solid rgba(91,124,250,.35); background: #fff;
  display: grid; place-items: center; font-size: 14px; color: #fff;
  transition: background .15s, border-color .15s;
}
.kl-remember.on { color: var(--k-ink); }
.kl-remember.on .kl-check { background: var(--k-purple); border-color: var(--k-purple); }

.kl-back {
  margin-top: 6px; border: none; background: rgba(255,255,255,.7);
  border-radius: 999px; padding: 9px 18px; cursor: pointer;
  font-family: inherit; font-size: 13.5px; font-weight: 700; color: var(--k-ink);
}
.kl-tip { margin: 16px 0 0; font-size: 12px; color: var(--k-muted); line-height: 1.7; }
.kl-parent {
  margin-top: 6px; font-size: 13px; color: var(--k-muted); text-decoration: none;
  background: rgba(255,255,255,.7); padding: 7px 14px; border-radius: 999px;
}
.kl-empty {
  margin: 0 0 18px; font-size: 14px; color: var(--k-muted);
  background: rgba(255,255,255,.75); border-radius: 16px; padding: 16px;
}

/* 顶栏的「换一个」：与孩子切换按钮同一视觉语言，但明确不是"另一个人" */
.kid-switch-out {
  border: 3px dashed rgba(255,255,255,.9) !important;
  background: rgba(255,255,255,.55) !important;
  color: var(--k-muted) !important;
}
.kid-switch button[disabled] { cursor: default; }
.kid-switch button[disabled]:hover { transform: none; }

@media (max-width: 420px) {
  .kl-hero h1 { font-size: 22px; }
  .kl-key { height: 56px; font-size: 23px; }
  .kl-dots { gap: 13px; }
  .kl-dot { width: 17px; height: 17px; }
  /* 小屏仍然两列并排（一列会让页面变得又长又空），
     所以卡片整体缩一档：头像 96 → 84，内边距收紧。 */
  .kl-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .kl-child { min-height: 188px; padding: 16px 8px 14px; gap: 8px; }
  .kl-child .kl-av { width: 84px; height: 84px; border-radius: 28px; font-size: 44px; }
  .kl-name { font-size: 16px; }
}
