:root {
  --ink: #24352d;
  --muted: #728078;
  --paper: #fffdf7;
  --cream: #f7f3e8;
  --line: rgba(44, 72, 57, 0.12);
  --accent: #ed845b;
  --accent-dark: #c95838;
  --soft: #fce8da;
  --green: #6aa66f;
  --shadow: 0 24px 70px rgba(51, 61, 48, 0.13);
  --radius: 30px;
  --mascot-sheet: url("img/virtue-mascots-v1.png");
}

* { box-sizing: border-box; }
html, body { min-height: 100%; margin: 0; }
body {
  color: var(--ink);
  background:
    radial-gradient(circle at 12% 8%, rgba(255, 214, 161, .48), transparent 24rem),
    radial-gradient(circle at 90% 22%, rgba(179, 224, 195, .38), transparent 28rem),
    var(--cream);
  font-family: "PingFang SC", "Microsoft YaHei", system-ui, sans-serif;
  -webkit-tap-highlight-color: transparent;
}
button, input, select { font: inherit; }
button { color: inherit; cursor: pointer; }

.app-shell { min-height: 100dvh; padding-bottom: 92px; }
.topbar {
  position: sticky; top: 0; z-index: 20;
  min-height: 78px; padding: 12px clamp(22px, 4vw, 64px);
  display: flex; align-items: center; justify-content: space-between;
  background: rgba(255, 253, 247, .84); border-bottom: 1px solid rgba(50, 70, 56, .08);
  backdrop-filter: blur(20px);
}
.brand { display: flex; align-items: center; gap: 12px; padding: 0; border: 0; background: none; text-align: left; }
.brand__spark { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 15px; color: white; background: linear-gradient(145deg, #ffc34f, #ef7f45); box-shadow: 0 10px 25px rgba(235, 125, 60, .25); font-size: 22px; }
.brand strong { display: block; font-size: 20px; letter-spacing: .03em; }
.brand small { display: block; margin-top: 2px; color: var(--muted); font-size: 11px; }
.topbar__stats { display: flex; align-items: center; gap: 12px; }
.energy-pill { display: flex; align-items: center; gap: 6px; padding: 10px 15px; border: 1px solid #f1cf91; border-radius: 999px; background: #fff8e6; font-size: 13px; }
.energy-pill i { color: #e7a623; font-style: normal; }
.avatar-button { width: 48px; height: 48px; border: 0; border-radius: 50%; background: #314d40; color: white; font-size: 13px; font-weight: 700; }

.screen { display: none; width: min(1280px, 100%); margin: auto; padding: 30px clamp(22px, 4vw, 64px) 48px; }
.screen.is-active { display: block; animation: rise .35s ease both; }
@keyframes rise { from { opacity: 0; transform: translateY(8px); } }
.week-strip { display: flex; align-items: flex-end; justify-content: space-between; gap: 32px; }
.eyebrow { display: block; color: var(--accent-dark); font-size: 12px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; }
.week-strip h1, .island-heading h1, .parent-heading h1 { margin: 8px 0 0; font-family: "STKaiti", "KaiTi", serif; font-size: clamp(28px, 4vw, 48px); font-weight: 700; line-height: 1.18; }
.goal-card { min-width: 245px; padding: 16px 18px; border: 1px solid rgba(70, 99, 73, .12); border-radius: 22px; background: rgba(255, 255, 255, .68); box-shadow: 0 12px 35px rgba(70, 87, 69, .07); }
.goal-card__number { display: flex; align-items: baseline; justify-content: space-between; }
.goal-card__number b { font-family: Georgia, serif; font-size: 30px; }
.goal-card__number span, .goal-card small { color: var(--muted); font-size: 12px; }
.goal-card__bar, .reward-progress div { height: 7px; margin: 8px 0; overflow: hidden; border-radius: 99px; background: #e8e7dd; }
.goal-card__bar i, .reward-progress i { display: block; height: 100%; width: 0; border-radius: inherit; background: linear-gradient(90deg, #f8b34f, var(--accent)); transition: width .45s ease; }

.day-rail { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 10px; margin: 24px 0; }
.day-node { position: relative; display: flex; align-items: center; gap: 10px; min-height: 58px; padding: 9px 12px; border: 1px solid var(--line); border-radius: 18px; background: rgba(255,255,255,.52); text-align: left; transition: .2s ease; }
.day-node:hover { transform: translateY(-2px); background: white; }
.day-node.is-current { border-color: var(--accent); background: var(--soft); box-shadow: 0 10px 25px rgba(218, 119, 75, .12); }
.day-node.is-done { background: #eaf4e8; }
.day-node.is-quiet { opacity: .58; }
.day-node span { display: grid; place-items: center; flex: 0 0 31px; width: 31px; height: 31px; border-radius: 50%; background: white; color: var(--muted); font-weight: 800; }
.day-node.is-current span { color: white; background: var(--accent); }
.day-node.is-done span { color: white; background: var(--green); }
.day-node b { display: block; font-size: 12px; }
.day-node small { display: block; margin-top: 2px; color: var(--muted); font-size: 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.hero-layout { display: grid; grid-template-columns: minmax(0, .88fr) minmax(430px, 1.12fr); gap: 22px; }
.companion-card, .mission-card { position: relative; min-height: 510px; overflow: hidden; border-radius: var(--radius); box-shadow: var(--shadow); }
.companion-card { display: flex; flex-direction: column; justify-content: flex-end; padding: 28px; background: linear-gradient(155deg, #fee8dc, #fff9ea 53%, #dff1e3); }
.ambient { position: absolute; border-radius: 50%; filter: blur(2px); opacity: .65; }
.ambient--one { width: 330px; height: 330px; top: -110px; right: -90px; background: rgba(255,255,255,.8); }
.ambient--two { width: 200px; height: 200px; left: -80px; bottom: 80px; background: rgba(254, 197, 159, .42); }
.character-stage { position: absolute; inset: 8px 0 110px; display: grid; place-items: center; }
.speech-bubble { position: absolute; top: 18px; right: 22px; max-width: 160px; padding: 11px 14px; border-radius: 18px 18px 18px 5px; background: rgba(255,255,255,.9); box-shadow: 0 8px 24px rgba(82, 70, 50, .1); font-size: 12px; font-weight: 700; }
.mascot { position: relative; width: 220px; height: 220px; border-radius: 40px; background-image: var(--mascot-sheet); background-size: 300% 200%; background-repeat: no-repeat; filter: drop-shadow(0 16px 18px rgba(91, 74, 51, .16)); }
.mascot--large { width: min(390px, 88%); height: min(390px, 72%); border-radius: 48px; }
.mascot[data-mascot="0"] { background-position: 0 0; }
.mascot[data-mascot="1"] { background-position: 50% 0; }
.mascot[data-mascot="2"] { background-position: 100% 0; }
.mascot[data-mascot="3"] { background-position: 0 100%; }
.mascot[data-mascot="4"] { background-position: 50% 100%; }
.mascot[data-mascot="5"] { background-position: 100% 100%; }
.energy-orbit span { position: absolute; color: #f6b13d; font-size: 18px; animation: float 2.8s ease-in-out infinite; }
.energy-orbit span:nth-child(1) { top: 31%; left: 13%; }
.energy-orbit span:nth-child(2) { top: 18%; right: 19%; animation-delay: -.8s; }
.energy-orbit span:nth-child(3) { top: 60%; right: 10%; animation-delay: -1.6s; }
@keyframes float { 50% { transform: translateY(-10px) rotate(15deg); } }
.character-copy { position: relative; z-index: 2; display: flex; align-items: end; justify-content: space-between; gap: 16px; }
.character-name { display: grid; grid-template-columns: auto auto; align-items: center; column-gap: 8px; }
.character-name > span { grid-row: 1 / 3; display: grid; place-items: center; width: 42px; height: 42px; border-radius: 14px; background: white; color: var(--accent); font-size: 21px; }
.character-name b { font-family: "STKaiti", "KaiTi", serif; font-size: 24px; }
.character-name em { color: var(--muted); font-size: 11px; font-style: normal; }
blockquote { max-width: 185px; margin: 0; color: #865b45; font-family: "STKaiti", "KaiTi", serif; font-size: 17px; font-weight: 700; text-align: right; }

.mission-card { padding: clamp(26px, 4vw, 46px); background: var(--paper); }
.mission-card__top, .mission-footer, .section-heading { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.mission-tag { display: inline-flex; align-items: center; gap: 8px; color: var(--accent-dark); font-size: 12px; font-weight: 800; letter-spacing: .08em; }
.mission-tag i { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 5px var(--soft); }
.sound-button { padding: 9px 13px; border: 1px solid var(--line); border-radius: 999px; background: white; color: var(--muted); font-size: 12px; }
.scene-chip { display: inline-block; margin-top: 28px; padding: 8px 12px; border-radius: 10px; background: #eff4ea; color: #547055; font-size: 12px; }
.mission-card h2 { margin: 14px 0 8px; font-family: "STKaiti", "KaiTi", serif; font-size: clamp(28px, 4vw, 42px); line-height: 1.12; }
.task-intro { margin: 0; color: var(--muted); font-size: 15px; line-height: 1.7; }
.say-card { margin: 22px 0; padding: 15px 18px; border-left: 4px solid var(--accent); border-radius: 5px 16px 16px 5px; background: #fff6ed; }
.say-card span { display: block; color: var(--accent-dark); font-size: 10px; font-weight: 800; letter-spacing: .12em; }
.say-card strong { display: block; margin-top: 5px; font-family: "STKaiti", "KaiTi", serif; font-size: 21px; }
.mission-footer { justify-content: flex-start; flex-wrap: wrap; margin-bottom: 20px; color: var(--muted); font-size: 11px; }
.mission-footer span { display: flex; align-items: center; gap: 5px; }
.mission-footer i { color: var(--green); font-style: normal; }
.primary-button { width: 100%; min-height: 76px; display: flex; align-items: center; gap: 14px; padding: 12px 18px; border: 0; border-radius: 22px; background: linear-gradient(135deg, var(--accent), #df6a45); color: white; box-shadow: 0 16px 30px rgba(212, 91, 52, .25); text-align: left; transition: .2s ease; }
.primary-button:hover { transform: translateY(-2px); box-shadow: 0 20px 36px rgba(212, 91, 52, .3); }
.primary-button__icon { display: grid; place-items: center; width: 44px; height: 44px; flex: 0 0 44px; border-radius: 50%; background: rgba(255,255,255,.2); }
.primary-button span:nth-child(2), .primary-button--wide span:first-child { flex: 1; }
.primary-button b { display: block; font-size: 17px; }
.primary-button small { display: block; margin-top: 2px; color: rgba(255,255,255,.8); font-size: 10px; }
.primary-button__arrow { font-size: 32px; font-weight: 300; }
.primary-button.is-done { background: linear-gradient(135deg, #6da576, #527e61); box-shadow: 0 16px 30px rgba(70, 119, 80, .22); }
.gentle-link { width: 100%; min-height: 44px; margin-top: 4px; padding: 13px 8px; border: 0; background: none; color: var(--muted); font-size: 11px; } /* H5：点击区 ≥44px，适配手指操作 */

.growth-preview { margin-top: 48px; }
.section-heading h2 { margin: 6px 0 0; font-family: "STKaiti", "KaiTi", serif; font-size: 29px; }
.section-heading > button { border: 0; background: none; color: var(--accent-dark); font-weight: 700; }
.garden-card { display: grid; grid-template-columns: 1.2fr 1fr; min-height: 220px; margin-top: 18px; overflow: hidden; border: 1px solid var(--line); border-radius: 28px; background: rgba(255,255,255,.7); }
.garden-visual { position: relative; overflow: hidden; min-height: 220px; background: linear-gradient(#dff2ec 0 55%, #d7e9bc 55%); }
.sun { position: absolute; width: 74px; height: 74px; top: 24px; right: 50px; border-radius: 50%; background: #ffd86d; box-shadow: 0 0 45px #ffdf86; }
.hill { position: absolute; width: 130%; height: 130px; left: -15%; bottom: -55px; border-radius: 50% 50% 0 0; }
.hill--back { background: #a8cf9a; transform: translateX(30%); }
.hill--front { background: #83b67f; transform: translateX(-20%); }
.garden-seed, .garden-tree, .garden-bench, .garden-star { position: absolute; z-index: 2; display: none; filter: drop-shadow(0 8px 8px rgba(50,75,42,.18)); }
.garden-seed { left: 25%; bottom: 38px; color: #528f55; font-size: 46px; }
.garden-tree { left: 44%; bottom: 28px; color: #34734c; font-size: 78px; }
.garden-bench { left: 64%; bottom: 28px; color: #9a6649; font-size: 52px; }
.garden-star { right: 14%; top: 28px; color: #f6b536; font-size: 32px; }
.garden-visual[data-growth="1"] [data-stage="1"],
.garden-visual[data-growth="2"] [data-stage="1"], .garden-visual[data-growth="2"] [data-stage="2"],
.garden-visual[data-growth="3"] [data-stage="1"], .garden-visual[data-growth="3"] [data-stage="2"], .garden-visual[data-growth="3"] [data-stage="3"],
.garden-visual[data-growth="4"] [data-stage] { display: block; animation: pop .4s ease both; }
@keyframes pop { from { transform: scale(.3); opacity: 0; } }
.garden-copy { display: flex; flex-direction: column; justify-content: center; padding: 30px; }
.garden-copy span { color: var(--accent-dark); font-size: 12px; font-weight: 800; }
.garden-copy strong { margin-top: 8px; font-family: "STKaiti", "KaiTi", serif; font-size: 22px; }
.garden-copy p { margin: 12px 0 0; color: var(--muted); font-size: 12px; line-height: 1.7; }

.island-heading, .parent-heading { margin-bottom: 26px; }
.island-heading p, .parent-heading p { max-width: 650px; color: var(--muted); line-height: 1.7; }
.island-total { min-width: 170px; padding: 18px 20px; border-radius: 22px; background: white; box-shadow: 0 15px 35px rgba(70,87,69,.08); text-align: center; }
.island-total span, .island-total small { display: block; color: var(--muted); font-size: 11px; }
.island-total strong { display: block; margin: 4px 0; font-size: 27px; }
.island-total i { color: #e5a324; font-style: normal; }
.virtue-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
.virtue-card { position: relative; overflow: hidden; min-height: 320px; padding: 20px; border: 1px solid var(--line); border-radius: 28px; background: white; box-shadow: 0 12px 36px rgba(66, 80, 65, .08); transition: .2s ease; }
.virtue-card:hover { transform: translateY(-4px); }
.virtue-card__top { display: flex; align-items: center; justify-content: space-between; }
.virtue-card__top span { color: var(--muted); font-size: 11px; font-weight: 700; }
.virtue-card__top em { padding: 6px 9px; border-radius: 999px; background: #f1f2ed; font-size: 10px; font-style: normal; }
.virtue-card .mascot { width: 190px; height: 190px; margin: -3px auto -10px; }
.virtue-card h2 { margin: 0; font-family: "STKaiti", "KaiTi", serif; font-size: 25px; }
.virtue-card p { margin: 5px 0 15px; color: var(--muted); font-size: 11px; }
.mini-progress { height: 6px; overflow: hidden; border-radius: 99px; background: #eee; }
.mini-progress i { display: block; height: 100%; border-radius: inherit; background: var(--card-color); }

.back-button { margin-bottom: 22px; padding: 10px 14px; border: 1px solid var(--line); border-radius: 999px; background: rgba(255,255,255,.65); }
/* min-width:0 不能省：栅格子项的自动最小尺寸是 min-content，而成长记忆卡里有
   一条横向滚动的相册（.album-strip，5 帧 ×128px = 680）。没有它，窄屏下轨道
   撑到 734px，整个家长中心出现横向滚动条 —— 375 宽实测。这是既有缺陷，
   相册满 3 帧以上就会触发，与胶囊改动无关（改动前同数据复现过）。 */
.parent-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 18px; }
.parent-grid > .settings-card { min-width: 0; }
.settings-card { padding: 26px; border: 1px solid var(--line); border-radius: 28px; background: white; box-shadow: 0 12px 35px rgba(70,87,69,.07); }
.settings-card h2 { margin: 0 0 20px; font-family: "STKaiti", "KaiTi", serif; font-size: 25px; }
.settings-card > p { color: var(--muted); font-size: 12px; line-height: 1.7; }
.settings-card label { display: grid; grid-template-columns: 100px 1fr; align-items: center; gap: 12px; margin: 12px 0; color: var(--muted); font-size: 12px; font-weight: 700; }
.settings-card input, .settings-card select { width: 100%; padding: 12px 14px; border: 1px solid var(--line); border-radius: 12px; outline: none; background: #fafaf6; color: var(--ink); }
.setting-row { display: flex; align-items: center; justify-content: space-between; gap: 18px; margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); }
.setting-row b, .setting-row small { display: block; }
.setting-row b { font-size: 13px; }
.setting-row small { margin-top: 3px; color: var(--muted); font-size: 10px; }
.setting-row em { color: var(--green); font-size: 11px; font-style: normal; font-weight: 800; }
.secondary-button { width: 100%; margin-top: 14px; padding: 13px; border: 1px solid #e4a58f; border-radius: 14px; background: #fff6ef; color: var(--accent-dark); font-weight: 700; }
.reset-hold { touch-action: none; transition: background .3s ease, border-color .3s ease; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
.demo-card details > summary { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 4px 0; cursor: pointer; font-size: 15px; font-weight: 800; list-style: none; }
.demo-card details > summary::-webkit-details-marker { display: none; }
.demo-card details > summary::after { content: "展开"; color: var(--muted); font-size: 11px; font-weight: 600; }
.demo-card details[open] > summary::after { content: "收起"; }
.demo-card details > summary em { color: var(--accent-dark); font-size: 10px; font-style: normal; font-weight: 600; }
.backup-card textarea { width: 100%; margin-top: 6px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 12px; background: #fffdf7; font-family: inherit; font-size: 12px; resize: vertical; }

/* 家庭回应（§6.1 第一层）：家长选一句 → 次日由伙伴转述 */
.family-heart { display: grid; place-items: center; width: 74px; height: 74px; margin: 0 auto 6px; border-radius: 50%; background: #fde9ec; color: #e57986; font-size: 32px; }
.reaction-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: 9px; margin: 14px 0 18px; }
.reaction-chip { min-height: 46px; padding: 12px 16px; border: 1px solid var(--line); border-radius: 999px; background: white; color: var(--ink); font-size: 13px; font-weight: 700; transition: .18s ease; }
.reaction-chip:hover { transform: translateY(-1px); }
.reaction-chip.is-chosen { border-color: var(--accent); background: var(--soft); color: var(--accent-dark); }
.recall-card { margin-top: 12px; padding: 11px 14px; border: 1px solid #f2d9c6; border-radius: 16px; background: rgba(255,252,246,.96); }
.recall-card[hidden] { display: none; }
.recall-card__tag { display: block; margin-bottom: 3px; color: var(--accent-dark); font-size: 10px; font-weight: 800; }
.recall-card p { margin: 0; color: var(--ink); font-size: 12px; line-height: 1.5; }
/* 能量球收取（§5.1 单次层）与二选一装饰（3 天层） */
.orb-stage { display: grid; place-items: center; min-height: 150px; }
.energy-orb-btn { display: grid; place-items: center; width: 118px; height: 118px; border: 0; border-radius: 50%; background: radial-gradient(circle at 34% 30%, #ffe9a8, #f0b23f 62%, #d99226); box-shadow: 0 16px 36px rgba(214,150,44,.34); color: white; font-size: 42px; animation: orb-float 2.4s ease-in-out infinite; touch-action: manipulation; }
.energy-orb-btn:active { transform: scale(.94); }
@keyframes orb-float { 0%,100% { transform: translateY(0) } 50% { transform: translateY(-9px) } }
.deco-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 16px 0 14px; }
.deco-option { display: grid; justify-items: center; gap: 8px; padding: 16px 10px; border: 1px solid var(--line); border-radius: 20px; background: white; color: var(--ink); font-size: 12px; font-weight: 700; }
.deco-option img { width: 78px; height: 78px; object-fit: contain; }
.deco-option:active { border-color: var(--accent); background: var(--soft); }

/* 两张同难度候选卡（§5.2）：竖排单列 —— 375 宽下并排会把标题挤成两行，
   而这是 3–6 岁唯一读得懂的一行。整卡可点，最小高度守住 44px 点击区。 */
.card-grid { display: grid; gap: 12px; margin: 16px 0 14px; }
.task-option { display: grid; gap: 6px; min-height: 44px; padding: 14px 16px; border: 1px solid var(--line); border-radius: 20px; background: white; color: var(--ink); text-align: left; }
.task-option__scene { font-size: 12px; font-weight: 700; color: var(--muted); }
.task-option__title { font-size: 17px; line-height: 1.35; }
.task-option__hint { font-size: 12px; line-height: 1.5; color: var(--muted); }
.task-option:active { border-color: var(--accent); background: var(--soft); }
.task-option.is-chosen { border-color: var(--accent); background: var(--soft); box-shadow: 0 6px 18px rgba(0,0,0,.06); }

/* 成长记忆三件套（demo 口径：胶囊 / 月度报告 / 相册）—— 全部零采集，媒体位为占位素材 */
/* R23 起三件套住在单列的 .memory-grid 里（「时光」屏），不再是 .parent-grid 的跨栏子项，
   原来的 grid-column: 1 / -1 已失去意义并被删除 —— 单列栅格里它只会让人误以为还有第二栏。 */
.memory-card h3 { margin: 18px 0 8px; font-size: 14px; }
.memory-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.memory-head h2 { margin: 0; }
.memory-head select { min-height: 44px; padding: 0 10px; border: 1px solid var(--line); border-radius: 12px; background: white; color: var(--ink); font: inherit; }
.memory-note { margin: 8px 0 0; font-size: 12px; line-height: 1.6; color: var(--muted); }
.memory-empty { margin: 0; padding: 14px; border: 1px dashed var(--line); border-radius: 14px; font-size: 12px; color: var(--muted); }

/* 胶囊是「核心资产」，不是列表缩略图：画面按当天内容合成，必须大到读得出
   行为标题与孩子说的那句，否则合成出来也没人看得见。72px 的槽位放不下这些字。 */
/* minmax 的下限必须写成 min(190px, 100%)：直接写 190px 时，容器比 190px 还窄的
   情况下轨道不会收缩，会把整个家长中心撑出横向滚动条（375 宽实测撑到 750）。
   min-width:0 同理 —— 栅格子项默认不肯窄过 min-content。 */
.capsule-list { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(min(190px, 100%), 1fr)); }
.capsule { margin: 0; min-width: 0; display: grid; gap: 6px; }
.capsule-card { position: relative; min-width: 0; aspect-ratio: 1 / 1; }
.capsule-card svg { display: block; width: 100%; height: 100%; }
.capsule-foot { display: grid; justify-items: center; gap: 2px; }
.capsule-note { font-size: 10px; line-height: 1.3; text-align: center; color: var(--muted); }
@media (max-width: 480px) { .capsule-list { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); } }

.report-box { padding: 14px; border: 1px solid var(--line); border-radius: 16px; background: #fffdf7; }
.report-box p { margin: 6px 0; font-size: 13px; line-height: 1.6; }
.report-nums { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 8px; }
.report-nums span { font-size: 11px; color: var(--muted); }
.report-nums b { display: block; font-size: 20px; color: var(--ink); }
.report-suggestion { padding-top: 8px; border-top: 1px dashed var(--line); }
.report-disclaimer { font-size: 11px !important; color: var(--muted); }

/* 帧本身就是合成卡（自带边框与日期/标题），所以外层不再套一层边框与内边距。
   150px 是 album 变体字号的下限：再小，行为标题就糊了。 */
/* ── 胶囊的 15 秒占位短片 ──────────────────────────────────────────────────
   正式版这里是孩子的实拍；占位版把同一张合成卡按四拍显影播一遍：
   伙伴(0–3s) → 那天做了什么(3–7s) → 孩子说的那句(7–11s) → 家人的心意(11–15s)。
   不播的时候四段全部照常显示 —— 卡首先得是一张读得懂的静态卡，
   「能播」是额外的，不能让静止状态变空。15 秒与 app.js 的
   CAPSULE_CLIP_SECONDS 必须一致，改一处要改两处。 */
.capsule-card.is-clipping .cap-mascot,
.capsule-card.is-clipping .cap-date,
.capsule-card.is-clipping .cap-title,
.capsule-card.is-clipping .cap-speech,
.capsule-card.is-clipping .cap-heart { animation: capBeat 15s ease-out both; }
.capsule-card.is-clipping .cap-mascot { animation-delay: 0s; }
.capsule-card.is-clipping .cap-date   { animation-delay: .35s; }
.capsule-card.is-clipping .cap-title  { animation-delay: 3s; }
.capsule-card.is-clipping .cap-speech { animation-delay: 7s; }
.capsule-card.is-clipping .cap-heart  { animation-delay: 11s; }
/* 只在开头淡入，随后一直留在画面上，播完是一张完整的卡而不是空卡 */
@keyframes capBeat { from { opacity: 0; } 8% { opacity: 1; } to { opacity: 1; } }
.capsule-card.is-clipping::after {
  content: ""; position: absolute; left: 10%; right: 10%; bottom: 6%;
  height: 4px; border-radius: 99px; background: rgba(0, 0, 0, .12);
  animation: capProgress 15s linear both;
}
@keyframes capProgress { from { right: 90%; } to { right: 10%; } }
@media (prefers-reduced-motion: reduce) {
  .capsule-card.is-clipping .cap-mascot, .capsule-card.is-clipping .cap-date,
  .capsule-card.is-clipping .cap-title, .capsule-card.is-clipping .cap-speech,
  .capsule-card.is-clipping .cap-heart, .capsule-card.is-clipping::after { animation: none; }
}

.album-strip { display: flex; gap: 10px; overflow-x: auto; padding-bottom: 6px; -webkit-overflow-scrolling: touch; }
.album-frame { flex: 0 0 150px; margin: 0; }
.album-frame svg { display: block; width: 100%; height: auto; }
.album-sound { margin: 8px 0 0; font-size: 11px; color: var(--muted); }

/* 家长导出验证数据（§10.2 / 决议 #11） */
.telemetry-summary { display: grid; gap: 10px; margin: 10px 0 14px; }
.telemetry-group { padding: 10px 12px; border: 1px solid var(--line); border-radius: 14px; background: #fffdf7; }
.telemetry-group b { display: block; margin-bottom: 6px; color: var(--accent-dark); font-size: 11px; }
.telemetry-group span { display: flex; justify-content: space-between; gap: 10px; padding: 2px 0; color: var(--muted); font-size: 11px; }
.telemetry-group em { color: var(--ink); font-style: normal; font-weight: 800; }

/* 首次使用流程（§3.4）：昵称 → 家庭称呼 → 提醒 → 金豆豆介绍 */
.onboarding { position: fixed; z-index: 60; inset: 0; display: none; place-items: center; padding: 20px; background: rgba(52,63,50,.55); backdrop-filter: blur(8px); }
.onboarding.is-open { display: grid; }
.onboarding__panel { width: min(460px, 100%); max-height: 92dvh; overflow-y: auto; padding: 26px 24px calc(24px + env(safe-area-inset-bottom, 0px)); border-radius: 30px; background: var(--card); box-shadow: 0 30px 70px rgba(40,52,40,.3); text-align: center; }
.onboarding__step { display: none; }
.onboarding__step.is-active { display: block; animation: rise .3s ease both; }
.onboarding__step h2 { margin: 6px 0 8px; font-size: 24px; }
.onboarding__step p { margin: 0 0 16px; color: var(--muted); font-size: 13px; line-height: 1.6; }
.mascot--onboard { width: 128px; height: 128px; margin: 0 auto 10px; }
.onboard-field { display: block; margin-bottom: 18px; text-align: left; color: var(--muted); font-size: 12px; font-weight: 700; }
.onboard-field input, .onboard-field select { width: 100%; min-height: 48px; margin-top: 6px; padding: 12px 14px; border: 1px solid var(--line); border-radius: 14px; background: #fffdf7; color: var(--ink); font-family: inherit; font-size: 15px; }
.role-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: 9px; margin-bottom: 18px; }
.role-chip { min-height: 46px; padding: 12px 18px; border: 1px solid var(--line); border-radius: 999px; background: white; color: var(--ink); font-size: 14px; font-weight: 700; }
.role-chip.is-chosen { border-color: var(--accent); background: var(--soft); color: var(--accent-dark); }

/* 行动贴纸册（§5.1 当天层）：模板生成，默认无儿童照片 */
.sticker-book { margin-top: 30px; }
.sticker-book[hidden] { display: none; }
.sticker-book__count { color: var(--muted); font-size: 12px; font-weight: 700; }
.sticker-row { display: flex; gap: 12px; margin-top: 14px; padding-bottom: 6px; overflow-x: auto; scroll-snap-type: x proximity; }
.sticker { position: relative; flex: 0 0 auto; width: 150px; margin: 0; padding: 14px 14px 12px; border: 1px solid var(--line); border-radius: 20px; background: white; box-shadow: 0 10px 24px rgba(90,74,52,.08); scroll-snap-align: start; }
.sticker__date { display: block; color: var(--accent-dark); font-size: 10px; font-weight: 800; }
.sticker__title { display: block; margin-top: 6px; color: var(--ink); font-size: 13px; line-height: 1.4; }
.sticker__by { margin-top: 8px; color: var(--muted); font-size: 10px; }
.sticker__heart { position: absolute; top: 10px; right: 12px; color: #e07a92; font-size: 15px; font-style: normal; }

/* 占位素材图层：有图即盖住底层 CSS 渲染，缺图时 JS 移除、自动回退 */
.mascot-state { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.mascot.has-art { height: auto; aspect-ratio: 1; background-image: none; filter: none; } /* 状态图为 512×512，容器同比才不被压缩 */
.mascot--large.has-art { width: min(300px, 72%); }
.garden-art { position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%; object-fit: cover; }
.garden-visual.has-art > :not(.garden-art) { display: none; }
.clue-card { margin-top: 12px; padding: 11px 14px; border: 1px dashed #d8c9a6; border-radius: 16px; background: #fdfaf0; }
.clue-card[hidden] { display: none; }
.clue-card__tag { display: block; margin-bottom: 3px; color: #9a7b3c; font-size: 10px; font-weight: 800; }
.clue-card p { margin: 0; color: var(--ink); font-size: 12px; line-height: 1.55; }
.record-reaction { display: flex; align-items: center; gap: 6px; width: 100%; margin-top: 4px; color: #c2607a; font-size: 10px; font-style: normal; font-weight: 700; }
.record-reaction button { min-height: 24px; padding: 2px 8px; border: 1px solid var(--line); border-radius: 8px; background: white; color: var(--muted); font-size: 10px; }
.reset-hold.is-holding { background: #fbe3d4; border-color: var(--accent); }
.clock-warning { width: min(1280px, 100%); margin: 18px auto -8px; padding: 12px 16px; border: 1px solid #e0b35f; border-radius: 14px; background: #fdf4e0; color: #7a5a17; font-size: 13px; font-weight: 600; line-height: 1.5; box-sizing: border-box; }
@media (min-width: 721px) { .clock-warning { max-width: calc(min(1280px, 100%) - 2 * clamp(22px, 4vw, 64px)); } }
@media (max-width: 720px) { .clock-warning { max-width: calc(100% - 32px); } }
.clock-warning[hidden] { display: none; }
.record-card { grid-column: 1 / -1; }
.record-list { display: grid; grid-template-columns: repeat(7, 1fr); gap: 9px; }
.record-item { padding: 13px 9px; border-radius: 15px; background: #f4f4ed; text-align: center; }
.record-item.is-done { background: #e6f2e3; color: #44704a; }
.record-item b, .record-item small { display: block; }
.record-item small { margin-top: 4px; color: var(--muted); font-size: 9px; }
.affirmation { margin: 18px 0 0; padding: 14px 16px; border-left: 4px solid var(--green); border-radius: 4px 14px 14px 4px; background: #eff6ed; color: #4c6d50 !important; }

.bottom-nav { position: fixed; z-index: 30; left: 50%; bottom: calc(16px + env(safe-area-inset-bottom, 0px)); transform: translateX(-50%); display: flex; gap: 6px; padding: 7px; border: 1px solid rgba(61,80,66,.14); border-radius: 24px; background: rgba(255,253,247,.9); box-shadow: 0 16px 45px rgba(44,58,47,.18); backdrop-filter: blur(18px); }
.bottom-nav button { min-width: 104px; padding: 8px 18px; border: 0; border-radius: 18px; background: transparent; color: var(--muted); }
.bottom-nav span, .bottom-nav b { display: block; }
.bottom-nav span { font-size: 18px; }
.bottom-nav b { margin-top: 2px; font-size: 11px; }
.bottom-nav button.is-active { background: #314d40; color: white; }

.task-modal { position: fixed; inset: 0; z-index: 100; display: none; place-items: center; padding: 22px; background: rgba(27, 40, 33, .56); backdrop-filter: blur(12px); }
.task-modal.is-open { display: grid; animation: fade .25s ease both; }
@keyframes fade { from { opacity: 0; } }
.modal-panel { position: relative; width: min(610px, 100%); max-height: calc(100dvh - 44px); overflow: auto; padding: clamp(28px, 5vw, 46px); border-radius: 34px; background: var(--paper); box-shadow: 0 35px 90px rgba(15,25,19,.3); text-align: center; }
.modal-close { position: absolute; z-index: 3; top: 16px; right: 16px; width: 38px; height: 38px; border: 0; border-radius: 50%; background: #f1f0e9; color: var(--muted); font-size: 23px; }
.modal-step { display: none; }
.modal-step.is-active { display: block; animation: rise .3s ease both; }
.modal-character { position: relative; width: 205px; margin: -16px auto 5px; }
.modal-character .mascot { width: 205px; height: 205px; }
.bean-spark { position: absolute; top: 16px; right: 0; display: grid; place-items: center; width: 42px; height: 42px; border-radius: 50%; background: #ffd065; color: white; box-shadow: 0 8px 22px rgba(225,168,40,.28); }
.modal-panel h2 { margin: 10px 0; font-family: "STKaiti", "KaiTi", serif; font-size: clamp(27px, 5vw, 40px); }
.modal-instruction, .modal-step > p { margin: 0 auto; max-width: 460px; color: var(--muted); font-size: 15px; line-height: 1.75; }
.say-card--modal { text-align: left; }
.listen-button { display: flex; align-items: center; justify-content: center; gap: 10px; width: 100%; min-height: 60px; margin: 16px 0 10px; border: 1px solid #efc98d; border-radius: 18px; background: #fff8e8; color: #8c6531; }
.listen-button b, .listen-button small { display: block; }
.listen-button small { color: #a28662; font-size: 9px; }
.sound-waves { letter-spacing: -2px; animation: pulse 1.2s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: .35; } }
.primary-button--wide { margin-top: 18px; justify-content: center; text-align: center; }
.away-illustration { display: flex; justify-content: center; gap: 15px; margin: 28px 0 32px; }
.away-illustration span { display: grid; place-items: center; width: 70px; height: 90px; border-radius: 55% 55% 45% 45%; background: #f2b282; color: white; font-family: "STKaiti", "KaiTi", serif; font-size: 20px; transform: rotate(-12deg); }
.away-illustration span:nth-child(2) { transform: translateY(18px) rotate(10deg); background: #84b59a; }
.away-illustration span:nth-child(3) { transform: rotate(-5deg); background: #e49b68; }
.parent-lock { display: grid; place-items: center; width: 94px; height: 94px; margin: 12px auto 24px; border-radius: 50%; background: #e9f1e6; color: #4f7755; font-size: 40px; }
.confirm-card { margin: 22px 0; padding: 18px; border-radius: 18px; background: #f5f3eb; text-align: left; }
.confirm-card span, .confirm-card strong, .confirm-card small { display: block; }
.confirm-card span { color: var(--accent-dark); font-size: 10px; font-weight: 800; letter-spacing: .1em; }
.confirm-card strong { margin: 6px 0; font-size: 14px; }
.confirm-card small { color: var(--muted); font-size: 10px; }
.hold-button { position: relative; overflow: hidden; width: 100%; min-height: 76px; border: 0; border-radius: 20px; background: #314d40; color: white; touch-action: none; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
.hold-button b, .hold-button small { position: relative; z-index: 2; display: block; }
.hold-button small { margin-top: 3px; color: rgba(255,255,255,.65); font-size: 9px; }
.hold-progress { position: absolute; inset: 0; z-index: 1; width: 0; background: #6a9c70; }
.hold-button.is-holding .hold-progress { width: 100%; transition: width 2s linear; }
.reward-burst { position: relative; width: 240px; height: 230px; margin: -15px auto 0; }
.reward-burst .mascot { position: absolute; inset: 5px 10px; }
.reward-burst > span { position: absolute; z-index: 2; color: #f1ae2f; font-size: 28px; animation: float 1.5s ease-in-out infinite; }
.reward-burst > span:nth-child(1) { left: 0; top: 80px; }
.reward-burst > span:nth-child(2) { right: 0; top: 45px; animation-delay: -.4s; }
.reward-burst > span:nth-child(3) { right: 20px; bottom: 22px; animation-delay: -.8s; }
.modal-step[data-step="reward"] h2 b { color: var(--accent); }
.reward-progress { margin-top: 22px; padding: 14px 18px; border-radius: 16px; background: #f4f2e9; text-align: left; }
.reward-progress span { font-size: 11px; font-weight: 800; }
.toast { position: fixed; z-index: 200; left: 50%; bottom: 110px; transform: translate(-50%, 20px); padding: 12px 18px; border-radius: 999px; background: #24352d; color: white; opacity: 0; pointer-events: none; transition: .25s ease; font-size: 12px; box-shadow: 0 10px 30px rgba(25,40,31,.25); }
.toast.is-visible { opacity: 1; transform: translate(-50%, 0); }

@media (max-width: 900px) {
  .hero-layout { grid-template-columns: 1fr; }
  .companion-card { min-height: 430px; }
  .mission-card { min-height: auto; }
  .virtue-grid { grid-template-columns: repeat(2, 1fr); }
  .day-node { justify-content: center; padding: 9px 5px; }
  .day-node div { display: none; }
}
@media (max-width: 640px) {
  .app-shell { padding-bottom: 86px; }
  .topbar { min-height: 66px; padding: 9px 16px; }
  .brand small, .energy-pill { display: none; }
  .screen { padding: 24px 16px 36px; }
  .week-strip { align-items: stretch; flex-direction: column; gap: 16px; }
  .goal-card { min-width: 0; }
  .day-rail { gap: 5px; }
  .day-node { min-height: 44px; border-radius: 14px; }
  .day-node span { width: 27px; height: 27px; flex-basis: 27px; font-size: 11px; }
  .companion-card { min-height: 390px; }
  .speech-bubble { display: none; }
  .character-copy { align-items: start; flex-direction: column; }
  blockquote { max-width: none; text-align: left; }
  .garden-card { grid-template-columns: 1fr; }
  .garden-visual { min-height: 180px; }
  .virtue-grid, .parent-grid { grid-template-columns: 1fr; }
  .record-card { grid-column: auto; }
  .island-heading { align-items: flex-start; flex-direction: column; }
  .island-total { width: 100%; }
  .bottom-nav { bottom: calc(10px + env(safe-area-inset-bottom, 0px)); width: calc(100% - 28px); justify-content: stretch; }
  .bottom-nav button { flex: 1; min-width: 0; padding: 8px 10px; }
  .modal-panel { padding: 32px 20px 24px; border-radius: 26px; }
}
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; transition-duration: .01ms !important; } }

/* demo 录制与 AI 建议（D10）：录制入口只在家长确认后出现 */
/* 整行都是 label，点哪都能切；行高守住 44px 点击区。
   必须写成 .settings-card label.switch-row：上面那条 `.settings-card label` 是
   `grid-template-columns: 100px 1fr`，特异性比单个 .switch-row 高，否则说明文字会被钉在 100px 里。 */
.settings-card label.switch-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 44px; }
.switch-row > span { flex: 1; min-width: 0; display: grid; gap: 3px; }
.switch-row small { color: var(--muted); font-size: 11px; line-height: 1.5; }
.switch-row input { flex: 0 0 auto; width: 44px; height: 26px; }
.capture-box { margin: 12px 0 4px; padding: 12px 14px; border: 1px dashed var(--line); border-radius: 16px; text-align: left; }
.capture-box p { margin: 0 0 10px; font-size: 12px; line-height: 1.6; color: var(--muted); }
.capture-box[hidden] { display: none; }
/* 收尾步的故事交接：刻意与录制盒同一套视觉语言 —— 两者都是这一步的「可选项」，
   不是必经步骤。主按钮仍是「回到我的家园」，可选项不许长得比它更像主路径。 */
.story-handoff { margin: 12px 0 4px; padding: 12px 14px; border: 1px dashed var(--line); border-radius: 16px; text-align: left; }
.story-handoff p { margin: 0 0 10px; font-size: 12px; line-height: 1.6; color: var(--muted); }
.story-handoff[hidden] { display: none; }
/* 回放键叠在合成卡上，而不是替掉卡 —— 录过的日子也该看得见是哪一天、做了什么 */
.capsule-play { position: absolute; inset: auto 8px 8px auto; display: grid; place-items: center; width: 44px; height: 44px; border: 0; border-radius: 50%; background: #314d40; color: white; font-size: 18px; box-shadow: 0 2px 8px rgba(0,0,0,.2); }
.capsule-drop { padding: 0; border: 0; background: none; color: var(--muted); font-size: 11px; text-decoration: underline; }
.report-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.ai-status { margin: 8px 0 0; font-size: 11px; line-height: 1.6; color: var(--muted); }

/* 家长中心导览：成果展出在最上面，内部工具（导出/演示控制）下沉到末尾 */
.parent-guide { margin: 8px 0 0; font-size: 12px; line-height: 1.7; color: var(--muted); }
.parent-guide b { color: var(--accent-dark); }

/* 童向文案的喇叭按钮：孩子要听的每一段字旁边都有一个，点一下重播。
   自动朗读会被浏览器的自动播放策略拦掉，这颗按钮是兜底，不是装饰。 */
.speak-button { min-height: 44px; margin-top: 8px; padding: 8px 15px; border: 1px solid #efc98d; border-radius: 14px; background: #fff8e8; color: #8c6531; font-size: 11px; font-weight: 700; }
.primary-button[hidden], .listen-button[hidden] { display: none; }

/* 「时光」屏（D16）：三件套从「家长」tab 里搬出来独立成屏 ——
   没人会去「家长」里找孩子的成长记忆，PM 找的是「时光魔法通道」。 */
.memory-heading { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.memory-heading h1 { margin: 6px 0 0; font-family: "STKaiti", "KaiTi", serif; font-size: clamp(28px, 4vw, 36px); }
.memory-heading select { min-height: 44px; padding: 0 12px; border: 1px solid var(--line); border-radius: 12px; background: white; color: var(--ink); font: inherit; }
.memory-grid { display: grid; gap: 18px; margin-top: 18px; }
/* 与 .parent-grid > .settings-card 是同一道防线，缺了就是同一种事故：
   栅格子项默认 min-width:auto，不肯窄过 min-content；而卡里装着 .album-strip
   （flex: 0 0 150px 的定宽帧，3 个月演示数据是 11 帧 = 1750px）。
   R23 首次部署就栽在这里 —— 三件套从 .parent-grid 搬进 .memory-grid 时，
   这行没跟着搬，375 宽下三张卡各被撑到 1804px，整页横向溢出到 1500+。 */
.memory-grid > .settings-card { min-width: 0; }
.memory-entry-card > p { margin: 0; }

/* 从孩子那一屏移过来的家长承诺：原句一字未改，只换了位置（改写容易把否定式写成陈述式） */
.setting-note { margin: 2px 0 14px; color: var(--muted); font-size: 11px; line-height: 1.7; }
.parent-promise { margin-top: 18px; padding: 14px 16px; border: 1px dashed var(--line); border-radius: 16px; background: #fbfaf5; }
.parent-promise h3 { margin: 0 0 6px; font-size: 12px; color: var(--accent-dark); }
.parent-promise h3 em { display: block; margin-top: 2px; color: var(--muted); font-size: 10px; font-style: normal; font-weight: 400; }
.parent-promise p { margin: 0 0 6px; color: var(--muted); font-size: 12px; line-height: 1.7; }
.parent-promise p:last-child { margin-bottom: 0; }

/* 每日美德故事会（§9.1 局后可选玩法）：入口始终在场，未结算时置灰不可点。
   此前是整块不渲染 —— 那看起来像「没有这个功能」，PM 与 Owner 都因此没找到。 */
.story-entry { display: grid; grid-template-columns: 128px 1fr; gap: 18px; align-items: center; margin: 22px 0; padding: 20px 24px; border: 1px solid var(--line); border-radius: var(--radius); background: linear-gradient(140deg, #fff6e6, var(--paper)); box-shadow: var(--shadow); }
.story-entry.is-locked { background: linear-gradient(140deg, #f3f2ec, #faf9f4); box-shadow: none; }
.story-entry.is-locked .mascot { filter: grayscale(.85); opacity: .5; }
.story-entry.is-locked .primary-button { background: #d7d3c8; box-shadow: none; color: #fbfaf6; cursor: not-allowed; }
.story-entry.is-locked .primary-button:hover { transform: none; box-shadow: none; }
.story-entry__art .mascot { width: 128px; height: 128px; border-radius: 28px; }
.story-entry__copy h2 { margin: 6px 0 4px; font-family: "STKaiti", "KaiTi", serif; font-size: clamp(22px, 3vw, 30px); }
.story-entry__copy p { margin: 0 0 12px; color: var(--muted); font-size: 13px; }
.story-body { display: grid; gap: 10px; margin: 14px 0; text-align: left; }
.story-body p { margin: 0; font-size: 16px; line-height: 1.85; }
/* .story-slogan 已随 G17-C §6（D89）撤除——格言只在正文末段出现一次，不再单独印一行 */
.story-note { margin: 10px 0 0; font-size: 11px; line-height: 1.6; color: var(--muted); }
/* 演示预览要在屏幕上明说，和「占位必须看得见」是同一条口径 */
.story-preview-note { margin: 8px 0 0; padding: 8px 12px; border: 1px dashed #e0a86f; border-radius: 12px; background: #fff6e8; color: #8c6531; font-size: 11px; line-height: 1.6; }
.story-preview-note[hidden] { display: none; }
@media (max-width: 640px) { .story-entry { grid-template-columns: 1fr; text-align: center; } .story-entry__art { justify-self: center; } }

/* ── 月度高光混剪 ─────────────────────────────────────────────────────────
   一帧一帧自动放完选中月份的高光。配乐留空（§8.1 古典乐录音版权待逐条核验），
   所以这里只有画面与进度条，note 里明说没有音轨。 */
.montage-panel { max-width: 520px; text-align: center; }
/* 用 100% 而不是 vw：vw 量的是视口、不是这块面板，窄屏下画面会顶出面板边界 */
.montage-stage { margin: 14px auto 10px; width: min(360px, 100%); aspect-ratio: 1 / 1; }
/* 入场只动 transform，不动 opacity。原因：后台标签页会把动画**冻在 currentTime=0**
   而不是跳过它 —— 此时动画仍处于 active 阶段，from 的值照样生效。
   也就是说去掉 both 并不够，只要 from 里写了 opacity:0，冻住就是一片空白。
   改成只缩放：动画停摆时最坏是「少了个入场」，内容始终看得见。 */
.montage-stage svg { display: block; width: 100%; height: 100%; animation: montageIn .45s ease; }
@keyframes montageIn { from { transform: scale(.96); } }
.montage-bar { height: 5px; margin: 0 auto; border-radius: 99px; background: #e8e7dd; overflow: hidden; }
.montage-bar i { display: block; height: 100%; width: 0; border-radius: inherit; background: linear-gradient(90deg, #f8b34f, var(--accent)); transition: width .4s ease; }
.montage-note { margin: 10px 0 0; font-size: 11px; line-height: 1.6; color: var(--muted); }
#montageButton[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) { .montage-stage svg { animation: none; } }

/* ── G2 · 四格首页与第①②件（V3 §3）────────────────────────────────────────── */

/* 四件事的唯一入口。两列两行：3–6 岁一屏内看得全，且到任一件的执行 ≤ 2 次点击。
   min-width: 0 写在第一行 —— D18 的教训是这道防线漏写一次就整页横向溢出，
   卡里装着可能变长的提示文案，栅格子项默认 min-width:auto 不肯窄过 min-content。 */
.four-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; margin-top: 18px; }
.four-grid > .quad-card { min-width: 0; }

.quad-card {
  display: flex; align-items: center; gap: 12px; min-height: 96px; padding: 16px;
  border: 1px solid var(--line); border-radius: 22px; background: var(--paper);
  color: var(--ink); font: inherit; text-align: left; cursor: pointer;
  box-shadow: 0 10px 26px rgba(51, 61, 48, 0.06);
}
.quad-card:hover { border-color: var(--accent); }
.quad-card__icon { flex: 0 0 auto; width: 44px; height: 44px; display: grid; place-items: center; border-radius: 14px; background: var(--soft); color: var(--accent-dark); font-size: 20px; }
.quad-card__body { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.quad-card__name { font-size: 16px; }
.quad-card__hint { font-size: 12px; color: var(--muted); }
/* 状态点只作视觉冗余：完成与否同时由文字与图标表达，隐藏文本后仍分得清（TEXT_HIDDEN 断言） */
.quad-card__state { margin-left: auto; flex: 0 0 auto; width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--line); }
.quad-card.is-done .quad-card__state { border-color: var(--green); background: var(--green); }
.quad-card.is-done { background: linear-gradient(180deg, #f2faf1, var(--paper)); }
.quad-card--main .quad-card__icon { background: var(--accent); color: white; }

/* 占位标注：D2/D11 同一条口径——占位必须看得见，不能等演示时才解释 */
.placeholder-note { margin: 0 0 10px; padding: 6px 12px; border-radius: 999px; background: var(--cream); color: var(--muted); font-size: 12px; }

/* 占位标记：**守的东西没变，形态变了**（与 D17 那次同一种改法）。
   原来是满框铺「占位画面」四个字，理由是"空灰框看起来像坏了"，并且写明"图到位后自然被盖住"——
   那时 ui_exercise_*.svg 一张都没有。现在 36 张占位 SVG 补上了，满框水印被图盖住，
   于是屏幕上看起来像正式美术已经到位了。**而它不是**：G2 增补需求单 §3 写的是
   「工程已做可见降级并必须保持到正式素材替换为止」。
   所以改成右上角常驻角标，压在图之上：图有了也照样看得见，且不挡住动作轮廓。
   正式美术同名替换时，**要连这一条一起撤**——撤占位标记是一次显式动作，
   不该由"图放上去了"自动发生（D2/D11）。 */
.exercise-art { position: relative; display: grid; place-items: center; min-height: 160px; margin: 6px 0 12px; border-radius: 20px; background: var(--cream); border: 1px dashed var(--line); }
.exercise-art::after {
  content: "占位画面"; position: absolute; top: 8px; right: 10px; z-index: 2;
  padding: 2px 8px; border-radius: 999px; background: var(--cream);
  border: 1px dashed var(--line); color: var(--muted); font-size: 11px;
}
.exercise-art img { position: relative; z-index: 1; }
.exercise-art img { max-width: 100%; max-height: 220px; }
.exercise-recap { margin: 0 0 12px; padding: 12px 14px; border: 1px dashed var(--line); border-radius: 16px; }
.exercise-recap__tag { font-size: 12px; color: var(--muted); }
.exercise-recap p { margin: 4px 0 0; font-size: 14px; }

/* 兜底信（G10）：三句逐句淡入。字是给家长看的，孩子那一侧靠 autoSpeak 一次念完三句——
   逐句渲染只是节奏，**不是**把语音拆成三次（speak() 进门先 cancel，拆三次只剩最后一句）。 */
.letter-body { margin: 4px 0 14px; }
.letter-line {
  margin: 0 0 10px; padding: 12px 16px; border-radius: 18px;
  background: var(--soft); font-size: 17px; line-height: 1.6;
  opacity: 0; animation: letter-in .5s ease forwards;
  animation-delay: calc(var(--letter-line, 0) * .45s + .15s);
}
@keyframes letter-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .letter-line { opacity: 1; animation: none; }
}

/* 两张食物图二选一。图上不放文字（3 岁不识字），食物名只进语音。 */
.feed-choice { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; margin: 6px 0 12px; }
.feed-choice > .feed-option { min-width: 0; }
.feed-option {
  display: grid; place-items: center; min-height: 150px; padding: 12px;
  border: 2px solid var(--line); border-radius: 20px; background: var(--paper); cursor: pointer;
}
.feed-option:hover, .feed-option.is-chosen { border-color: var(--accent); }
.feed-option img { position: relative; z-index: 1; max-width: 100%; max-height: 120px; }
/* 占位期两张食物图都还没有美术，空框会让「二选一」无从选起。
   用每种食物固定的色相把两张区分开——**靠颜色不靠文字**：V3 §3.2 规定图上不放字（3 岁不识字）。
   正式图到位后盖在色块之上，这段自然失效。 */
.feed-option { position: relative; }
.feed-option::after { content: ""; position: absolute; inset: 14px; border-radius: 14px; background: hsl(var(--food-hue, 30) 70% 88%); }
.feed-thanks { margin: 0 0 12px; padding: 14px; border-radius: 18px; background: var(--soft); }
.feed-thanks p { margin: 0; font-size: 16px; }

@media (max-width: 520px) {
  .four-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ── G3 · 微章节收尾仪式与拥抱印记 ────────────────────────────────────────── */
.hug-rite { margin: 14px 0 6px; padding: 16px; border-radius: 20px; background: var(--soft); }
.hug-rite__invite { margin: 0 0 12px; font-size: 15px; line-height: 1.7; }
/* 印记与家庭爱心并排在贴纸右上；满格再加一圈描边——两者互不覆盖 */
.sticker__seal { position: absolute; top: 8px; right: 30px; font-style: normal; color: var(--accent-dark); }
.sticker.is-full { border-color: var(--green); box-shadow: 0 0 0 2px rgba(106, 166, 111, 0.35) inset; }

/* ── G11 v5.1 · 首页＝总入口：四层容器 + 第 0 层角色入口 + 两条层带 ──────────────
   所有规则都挂在 .screen.is-master-diagram 或新增类上：masterDiagramHome=false 时
   归位函数直接 return、这个类根本不加、新增区块保持 hidden —— 一个开关回到今天的首页。 */
.layer { min-width: 0; }   /* 第一行就写：.memory-grid 那一课（窄屏被撑到 1500px）不许在新容器里重演 */
.layer[hidden] { display: none; }
.layer__body { min-width: 0; }
.layer__head { margin: 14px 0 6px; color: var(--accent-dark); font-size: 13px; font-weight: 800; letter-spacing: .18em; }
.layer__connector { margin: 6px 0 0; padding-left: 12px; border-left: 2px solid var(--soft); color: var(--muted); font-size: 12px; line-height: 1.6; }

/* 让位：只让位不删除。源码位置一行没动，开关关掉它们原样回来。
   .week-strip 是空壳（两个子块各自归了层），另两块是 v5.1 §2-C 的白名单。 */
.screen.is-master-diagram .week-strip,
.screen.is-master-diagram .hero-layout,
.screen.is-master-diagram #storyEntry { display: none; }
.screen.is-master-diagram #weekGoal { width: 100%; min-width: 0; }

/* ── 第 0 层 · 我是谁 ───────────────────────────────────────────────────────
   会议原件：「一个图标可点，其它什么都没有，孩子秒懂」「交互以语音为主」。
   所以这一层是**一句话 + 六个可点的头像**，不是一张写标语的卡。 */
.identity { min-width: 0; }
.identity__host { position: relative; display: flex; align-items: center; gap: 12px; padding-right: 108px; }
.host-art { flex: 0 0 auto; width: 52px; height: 52px; border-radius: 16px; background: var(--soft) center/86% no-repeat; }
.identity__host .speak-button { position: absolute; top: 50%; right: 0; margin: 0; transform: translateY(-50%); }
.bubble { flex: 1; min-width: 0; margin: 0; padding: 10px 14px; border: 1px solid var(--line); border-radius: 18px 18px 18px 4px; background: var(--paper); color: var(--ink); font-size: 13px; line-height: 1.65; }
.bubble[hidden] { display: none; }
/* .bubble--pet 随 #petBubble 一起下线（G13 §4.1）：伙伴说的那一句改为出现在**它自己那张卡上**，
   不再在宣言下方另开一条横幅。留着这条规则会变成没有任何元素用得上的死样式。 */

/* ── 第 II / III 层 · 层带 ──────────────────────────────────────────────── */
.band { min-width: 0; }
.band__button { display: flex; width: 100%; align-items: center; gap: 12px; min-height: 56px; padding: 10px 16px; border: 1px solid var(--line); border-radius: 20px; background: var(--paper); box-shadow: 0 8px 24px rgba(51, 61, 48, .06); text-align: left; }
.band__stat { flex: 0 0 auto; font-family: Georgia, serif; font-size: 17px; }
.band__rail { flex: 0 0 auto; display: flex; gap: 4px; }
.band__dot { width: 9px; height: 9px; border-radius: 50%; border: 1px solid var(--line); background: transparent; }
.band__dot[data-day-done="true"] { border-color: var(--accent); background: var(--accent); }
.band__note { flex: 1; min-width: 0; color: var(--muted); font-size: 12px; }
.band__arrow { flex: 0 0 auto; color: var(--muted); font-size: 20px; }
.band__expand { min-width: 0; padding-top: 4px; }
.band__expand[hidden] { display: none; }

/* 四格：宽屏一行四列——四件事第一次被看成「一组四件」，这是它作为日闭环入口的本来形态 */
@media (min-width: 900px) {
  .screen.is-master-diagram #fourGrid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .identity__host { padding-right: 0; gap: 10px; }
  .identity__host .speak-button { position: static; transform: none; flex: 0 0 auto; }
  .host-art { width: 44px; height: 44px; border-radius: 14px; }
  .bubble { font-size: 12px; padding: 8px 12px; }
  /* G14-I：第 III 层四段沉淀文案不能沿用 .band__stat 的 flex:0 0 auto。
     两列只限本卡：统计/连接语在可收缩主列，箭头独占第二列并跨两行。
     min-width:0 是同类事故的防御护栏；当前 grid 与分段文本本身已经可以收缩。 */
  .screen.is-master-diagram #memoryEntryCard .band__button { display: grid; grid-template-columns: 1fr auto; }
  .screen.is-master-diagram #memoryEntryCounts { min-width: 0; }
  .screen.is-master-diagram #layerIIIConnector { grid-column: 1; }
  .screen.is-master-diagram #memoryEntryCard .band__arrow { grid-column: 2; grid-row: 1 / 3; }
}

/* ── 首屏预算（375 档实测倒逼的三处纯定位 + 一处断点）────────────────────────
   实测：375 档有效首屏 726、正文起点 99，可用 627px。
   第 0 层 232 + 层头 39 + 问候块 201 + 四格一列 444 = 916 —— 超 289px，
   四件事只剩第①件完整。以下四处把它压回可用预算内，**不缩字号、不动四格内部**。 */
/* ① 「念给我听」贴到 eyebrow 那一行右端，不再独占一行（省 ~52px） */
.screen.is-master-diagram #welcomeBlock { position: relative; }
.screen.is-master-diagram #welcomeBlock .eyebrow { min-height: 40px; padding-right: 124px; line-height: 40px; }
.screen.is-master-diagram #welcomeBlock .speak-button { position: absolute; top: 0; right: 0; margin-top: 0; }
.screen.is-master-diagram #welcomeBlock h1 { margin-top: 0; }
/* ② 层头收紧（省 ~11px × 4） */
.screen.is-master-diagram .layer__head { margin: 10px 0 4px; }
@media (max-width: 640px) {
  /* ③ 窄屏四格改 2×2（省 220px）。⚠ 这一条偏离 v5.1 §2-C「窄屏维持一列」，
        原因是实测预算差 289px，只靠 ①②③ 只能省下 113px，收口指标 3 在 375 档必红。
        四格**内部一字未动**，改的只是列数；且挂在 .is-master-diagram 上，开关关掉即回一列。
        已在实施记录里作为显式偏离上报，待需求分析师追认。 */
  .screen.is-master-diagram #fourGrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* ⑤ 再补 34px：实测 ①—④ 之后四格底 751、仍超 25px。以下全是间距，不动任何字号与卡片高。 */
.screen.is-master-diagram #fourGrid { margin-top: 8px; }
.screen.is-master-diagram .layer__head { margin: 8px 0 2px; }
.screen.is-master-diagram #welcomeBlock .eyebrow { min-height: 36px; line-height: 36px; }
@media (max-width: 640px) {
  .screen.is-master-diagram .bubble { padding: 7px 12px; }
}
/* ⑥ 冗余：①—⑤ 之后 375 档只余 1px，换一版字体就会翻红——判据守的是「立住」，
      不该活在一个像素上。以下再买约 30px，全部只动间距与窄屏行高，四格内部与卡片高不变。 */
@media (max-width: 640px) {
  .screen.is-master-diagram { padding-top: 14px; }
  .screen.is-master-diagram #fourGrid { gap: 10px; }
  .screen.is-master-diagram .bubble { line-height: 1.5; }
  /* 问候语在窄屏由 clamp 下界 28px 降到 25px：仍是这一屏最大的一行字，
     两行不变；换来的是判据不再贴着边界跑。 */
  .screen.is-master-diagram #welcomeTitle { font-size: 25px; }
}
/* ⑦ 两列之后卡面变窄，「手语操」这种三字名会折行——3 岁孩子读的是图和这三个字，
      折行就伤可读性。给名字一行装下的空间：图标略收、内边距略收，**卡片高度与文案一字未动**。
      这一条与 ⑥ 的 2×2 是同一处偏离的配套，一并上报待追认。 */
@media (max-width: 640px) {
  .screen.is-master-diagram .quad-card { padding: 12px 12px; gap: 10px; }
  .screen.is-master-diagram .quad-card__icon { width: 36px; height: 36px; border-radius: 12px; font-size: 17px; }
  .screen.is-master-diagram .quad-card__name { white-space: nowrap; }
  .screen.is-master-diagram .quad-card__state { width: 18px; height: 18px; }
}
/* ⑧ 连接语长在带子上之后：带子略收，层头间距略收（1024 档要四层全进第一眼） */
.band__button { min-height: 48px; }
.band__note { line-height: 1.5; }
@media (min-width: 900px) {
  .screen.is-master-diagram .layer__head { margin: 6px 0 2px; }
  .screen.is-master-diagram .host-art { width: 46px; height: 46px; }
}
/* ⑨ 1024 档四层只余 9px，同样太贴边界，再买 ~20px（纯间距） */
@media (min-width: 900px) {
  .screen.is-master-diagram { padding-top: 20px; }
  .screen.is-master-diagram .band__button { min-height: 44px; }
  .screen.is-master-diagram .identity__host { gap: 10px; }
}

/* ── G12-a 首页：宣言区 + 六张美德卡（实现设计补充 D66）──────────────────────
   硬约束：**信息完整优先于六张同屏**。为了塞进首屏而删掉要表达的东西，
   这个错前面已经犯过三次，不再犯第四次。 */
.declaration { display: flex; align-items: center; gap: 20px; padding: 20px 26px; border: 1px solid rgba(226, 176, 106, .55); border-radius: 26px; background: linear-gradient(108deg, #fff4e2, #fdf7ec, #f2f7ef); box-shadow: 0 16px 40px rgba(120, 92, 48, .10); }
/* ── G14-A §1 宣言加码：形象 150、首行 34/900、格言 18、右上角挂一枚 ♪ ─────
   判据 C-11 写成关系式（形象宽 ≥ 首行字号 ×4；首行 > 格言 ×1.7），不比字面像素（D60）。 */
.declaration__art { position: relative; flex: 0 0 auto; width: 150px; height: 150px; border-radius: 32px; background: rgba(255,255,255,.7) center/86% no-repeat; box-shadow: 0 8px 20px rgba(120, 92, 48, .14); }
/* 「会说话的那个」一眼看得出来：实心橙，不是描边小字 */
.declaration__note { position: absolute; top: -6px; right: -6px; display: grid; place-items: center; width: 34px; height: 34px; border-radius: 999px; background: var(--accent); color: white; font-size: 17px; font-weight: 800; font-style: normal; box-shadow: 0 6px 16px rgba(120, 92, 48, .22); }
.declaration__copy { flex: 1; min-width: 0; }
.declaration__tag { display: inline-block; margin-bottom: 4px; padding: 4px 12px; border-radius: 999px; background: var(--accent); color: white; font-size: 12.5px; font-weight: 800; letter-spacing: .06em; }
.declaration__line { margin: 0; font-size: 34px; font-weight: 900; line-height: 1.24; color: var(--ink); }
.declaration__sub { margin: 6px 0 0; color: var(--muted); font-size: 18px; line-height: 1.5; }
.declaration__sub b { color: var(--accent-dark); font-weight: 900; }   /* 幸福格言那一段 */
/* 七格进度：已完成实心 ✦ / 今天粗描边放大 / 未到虚线框 + 数字（**不是灰锁**，A-9 口径不变）*/
.virtues-head-row { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 4px 18px; }
.virtues-head-row .layer__head { margin: 14px 0 8px; }
.week-track { display: flex; align-items: center; gap: 6px; }
.week-track[hidden] { display: none; }
.track-cell { display: grid; place-items: center; width: 34px; height: 34px; border-radius: 12px; border: 1.5px solid var(--line); background: white; color: var(--muted); font-size: 14px; font-style: normal; font-weight: 800; }
.track-cell--done { border-color: var(--accent); background: var(--accent); color: white; }
.track-cell--today { border-width: 2.5px; border-color: var(--accent-dark); color: var(--accent-dark); transform: scale(1.14); }
.track-cell--waiting { border-style: dashed; }
.track-count { margin-left: 6px; color: var(--muted); font-size: 13px; font-weight: 700; }
.declaration__replay { flex: 0 0 auto; padding: 11px 18px; border: 1.5px solid #efc98d; border-radius: 16px; background: white; color: var(--accent-dark); font-size: 14px; font-weight: 800; box-shadow: 0 6px 16px rgba(120, 92, 48, .10); }
.screen[data-screen="brand"] .layer__head { margin: 14px 0 8px; }

/* 六张卡：高度由内容撑开，**七个**必备子项一个都不省（B-3 守着）。
   ── G13 §3.1 为第 7 行「它的故事」腾高度 ────────────────────────────────
   加第 7 行后卡片由 328px 长到 371px，第一行三张就掉出第一眼了。
   **正确解法是收紧像素，不是删信息**（D62 → D66 那个错已经犯了三次）。
   下面八项收紧（本块六项 + ≥901px 里的宣言两项）把卡片压回 324px 上下，三档全过。 */
/* ⚠ 这几条一律带 `.virtue-card--home` 前缀，不是啰嗦：上面第 162 行有一条
   `.virtue-card p { margin: 5px 0 15px; }`，特指度 (0,1,1) 高于裸类名 (0,1,0)。
   写成 `.virtue-card__slogan { margin: … }` 会被它整条吃掉——G12-a 那版就是这样，
   屏上跑的一直是 5/15，收紧表里写的数一个都没生效。实测每张卡因此虚高 28px。 */
.virtue-card--home { min-height: 0; padding: 11px 14px 13px; }
.virtue-card--home .mascot { width: 112px; height: 100px; margin: 0 auto; }
.virtue-card--home h2 { margin: 2px 0 0; font-size: 20px; text-align: center; }
.virtue-card--home .virtue-card__slogan { margin: 2px 0 7px; color: var(--muted); font-size: 13px; text-align: center; }
.virtue-card--home .virtue-card__practice { margin: 0 0 6px; padding: 7px 10px; border-radius: 12px; background: #f7f5ef; color: var(--ink); font-size: 12.5px; line-height: 1.55; }
.virtue-card__practice b { color: var(--accent-dark); font-weight: 800; }
/* 第 7 子项：与「陪你练」同族，底色换 #fbf3e6 以示区分 */
.virtue-card--home .virtue-card__story { margin: 0 0 9px; padding: 7px 10px; border-radius: 12px; background: #fbf3e6; color: var(--ink); font-size: 12.5px; line-height: 1.55; }
.virtue-card__story b { margin-right: 6px; color: var(--accent-dark); font-weight: 800; }

/* 说话态（§4.1）：换掉的是 __practice / __story 两行，**形象、名字、口号全程不被遮挡**。
   气泡就地占同一段高度，所以说话时卡不会跳高，第一行也不会因此掉出第一眼。 */
.virtue-card--home .virtue-card__say { margin: 0 0 9px; padding: 8px 11px; border: 1px solid var(--card-color); border-radius: 4px 14px 14px 14px; background: color-mix(in srgb, var(--card-color) 8%, white); }
.virtue-card--home .virtue-card__say-line { margin: 0; color: var(--ink); font-size: 12.5px; line-height: 1.55; }
.virtue-card--home .virtue-card__say-note { display: block; margin-top: 5px; color: var(--muted); font-size: 11px; }
.virtue-card--home.is-speaking { box-shadow: 0 0 0 5px color-mix(in srgb, var(--card-color) 24%, transparent), 0 14px 34px rgba(44, 58, 47, .16); }
.virtue-card__go { display: block; width: 100%; margin-top: 10px; min-height: 40px; padding: 8px 10px; border: 1px solid var(--line); border-radius: 14px; background: var(--cream); color: var(--muted); font-size: 12px; font-weight: 700; }
.virtue-card--home[data-belt-state="current"] { border-color: var(--card-color); box-shadow: 0 14px 34px rgba(44, 58, 47, .16); }
.virtue-card--home[data-belt-state="current"] .virtue-card__go { border-color: var(--card-color); background: var(--card-color); color: white; }
.virtue-card--home[data-belt-state="waiting"] { border-style: dashed; }
.virtue-card--home[data-belt-state="done"] .virtue-card__top em { background: #f6efdc; }

/* ⚠ 宣言那两项收紧**必须包在 ≥901px 里**：<900px 有一套自己的缩放规则（下面那个块），
   不包就会把窄屏的 padding/尺寸覆盖掉，375 档当场掉出第一眼。 */
@media (min-width: 901px) {
  .declaration { padding: 13px 20px; }
  /* G13 曾把形象收到 100px 给「它的故事」腾高度；G14-A 加码回 150px。
     卡片那八项收紧一条没退——第一行仍在第一眼内，实测见实施记录。 */
}
@media (max-width: 899px) {
  .declaration { flex-wrap: wrap; gap: 10px; padding: 13px 16px; }
  /* ⚠ 窄屏这一档比 §1 给的数字再收一轮：形象 96→80、首行 24→20、格子 32→30。
     §1 那组数实测在 375×812 上 slack = −65px（宣言 287 + 层头行 81 + 卡 324 > 可用 627），
     第一张卡直接掉出第一眼。收完 C-11 的关系式在这一档仍然成立（80 ≥ 20×4；20 > 11×1.7），
     一个信息都没删——只是同样的东西按这块屏的尺寸摆。 */
  .declaration__art { width: 80px; height: 80px; border-radius: 20px; }
  .declaration__note { width: 24px; height: 24px; font-size: 12px; }
  .declaration__line { font-size: 20px; }
  .declaration__sub { font-size: 11px; }
  .virtues-head-row .layer__head { margin: 8px 0 4px; }
  .track-cell { width: 30px; height: 30px; border-radius: 10px; font-size: 12px; }
  .track-count { font-size: 11px; }
  .declaration__replay { width: 100%; }   /* 窄屏移到宣言下方独占一行 */
}
@media (max-width: 640px) {
  .screen[data-screen="brand"] .virtue-grid { grid-template-columns: 1fr; gap: 12px; }
  .virtue-card--home { border-radius: 22px; }
}

/* ── G13 §6 第二屏两块：成长路径四步 + 核心原则 ─────────────────────────────
   只在第二屏，不与宣言抢第一眼——所以这里刻意不做任何「往上挤」的负边距。 */
.growth-path { margin-top: 22px; }
.growth-path[hidden] { display: none; }
.growth-path__sub { margin: 0 0 12px; color: var(--muted); font-size: 13px; }
.growth-path__steps { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.growth-step { padding: 13px 15px; border: 1px solid var(--line); border-radius: 20px; background: var(--paper); box-shadow: 0 8px 22px rgba(51, 61, 48, .06); }
.growth-step b { display: block; margin-bottom: 5px; color: var(--accent-dark); font-family: Georgia, serif; font-size: 15px; }
.growth-step span { color: var(--muted); font-size: 12.5px; line-height: 1.6; }
.core-principle { margin: 14px 0 0; padding: 14px 20px; border: 1px solid rgba(226, 176, 106, .55); border-radius: 22px; background: linear-gradient(104deg, #fff4e2, #f2f7ef); color: var(--ink); font-size: 14px; font-weight: 700; text-align: center; }
.core-principle[hidden] { display: none; }
@media (max-width: 899px) {
  .growth-path__steps { grid-template-columns: repeat(2, 1fr); }   /* 窄屏两列 */
}

/* 金豆豆报幕（§5）：在故事正文之上，形态与正文分得开——它是「谁来讲」，不是故事本身 */
.story-host-intro { margin: 0 0 12px; padding: 11px 14px; border-radius: 4px 16px 16px 16px; background: #fff4e2; border: 1px solid rgba(226, 176, 106, .55); color: var(--ink); font-size: 13px; line-height: 1.7; }
.story-host-intro[hidden] { display: none; }

/* ── G12-b 身份条：这一屏是谁的页 ───────────────────────────────────────────
   与首页六张卡同一批字段（character·home / 陪你练：action / 状态胶囊），
   胶囊文案走 virtueBadge() 那个唯一判定处，不在这里另写一套。 */
.virtue-bar { display: flex; align-items: center; gap: 14px; margin-bottom: 6px; padding: 10px 16px; border: 1px solid var(--line); border-radius: 22px; background: var(--paper); box-shadow: 0 8px 22px rgba(51, 61, 48, .06); }
.virtue-bar[hidden] { display: none; }
.virtue-bar__back { flex: 0 0 auto; padding: 8px 12px; border: 1px solid var(--line); border-radius: 14px; background: var(--cream); color: var(--muted); font-size: 13px; font-weight: 700; }
.virtue-bar__art { flex: 0 0 auto; width: 52px; height: 52px; border-radius: 16px; }
.virtue-bar__copy { flex: 1; min-width: 0; display: grid; gap: 2px; }
.virtue-bar__who { font-family: "STKaiti", "KaiTi", serif; font-size: 19px; }
.virtue-bar__practice { color: var(--muted); font-size: 12px; line-height: 1.5; }
.virtue-bar__badge { flex: 0 0 auto; padding: 6px 10px; border-radius: 999px; background: #f1f2ed; font-size: 11px; font-style: normal; }
@media (max-width: 640px) {
  .virtue-bar { gap: 10px; padding: 8px 12px; }
  .virtue-bar__art { width: 42px; height: 42px; border-radius: 13px; }
  .virtue-bar__who { font-size: 16px; }
  .virtue-bar__practice { display: none; }   /* 窄屏收起「陪你练」：它在第 I 层的任务里还会完整出现 */
}

/* 家长切换「现在做哪一件」（G14-A §2.1 的退路，家长侧）*/
.quad-focus { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.quad-focus__tag { color: var(--muted); font-size: 12px; font-weight: 700; }
.quad-focus button { padding: 6px 12px; border: 1px solid var(--line); border-radius: 999px; background: var(--cream); color: var(--muted); font-size: 12px; font-weight: 700; }
/* 一次亮一件：亮的那件正常；其余淡出但**保留占位**——孩子知道后面还有 */
.quad-card.is-dimmed { opacity: .38; filter: saturate(.5); }
.quad-card.is-lit { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 34%, transparent), 0 14px 34px rgba(44, 58, 47, .16); }

/* ── G14-A §2.2 S3：图大字少的二选一卡 ─────────────────────────────────── */
.task-option--art { display: grid; gap: 10px; padding: 12px; text-align: center; }
.task-option__art { display: block; width: 100%; height: 210px; object-fit: cover; border-radius: 18px; background: var(--cream); }
.task-option--art .task-option__title { font-size: 22px; font-weight: 800; }
/* 缺图回退：图删掉之后卡仍成立，只是变回文字卡——不白屏 */
.task-option--art.is-artless { min-height: 150px; place-content: center; }

/* §2.3 S4：这一屏只有一个大按钮，边缘退路做成小字喇叭、不做成第二个大按钮 */
.primary-button--sop { min-width: 420px; font-size: 34px; }
.speak-button--edge { margin: 0 auto 6px; opacity: .72; font-size: 12px; }
@media (max-width: 640px) { .primary-button--sop { min-width: 0; width: 100%; font-size: 24px; } }

/* §2.1 S5 微闭环：三件事同屏 */
.reward-loop { display: grid; justify-items: center; gap: 10px; margin: 12px 0 4px; }
.reward-loop[hidden] { display: none; }
.reward-loop__garden { width: 168px; height: 112px; border-radius: 18px; background: var(--cream) center/contain no-repeat; }
.reward-loop__garden .garden-art { width: 100%; height: 100%; object-fit: contain; }

/* §2.3 S6：七天通关解锁 */
.unlock-art { width: 180px; height: 180px; margin: 6px auto; border-radius: 28px; background: var(--cream) center/contain no-repeat; }
.unlock-art .outfit-art { width: 100%; height: 100%; object-fit: contain; }

/* 解锁的东西**回到首页**：卡左上角一枚徽标 + 换装形象 */
.virtue-card__outfit { position: absolute; top: 10px; left: 10px; z-index: 2; padding: 4px 9px; border-radius: 999px; background: var(--accent); color: white; font-size: 11px; font-style: normal; font-weight: 800; }
.virtue-card--home.is-dressed { border-color: var(--accent); }
.mascot--dressed { background-size: 86%; background-position: center; background-repeat: no-repeat; }

/* C-7 家长事后补见证 */
.witness-card { margin-top: 12px; padding: 14px 16px; border: 1px solid var(--line); border-radius: 20px; background: var(--paper); }
.witness-card[hidden] { display: none; }
.witness-card p { margin: 6px 0 10px; color: var(--muted); font-size: 13px; line-height: 1.6; }

/* ══════════ G14-B · 第二页 SOP（任务线 / 大图标 / 做完得什么 / 成果区）══════════
   Owner 2026-08-16：「第二页要有清晰的任务线、清晰的任务图标，不需要认字就知道；
   清晰的每日行动指引，通过指引获得奖励；获得贴纸、获得拥抱要有动感。」

   ① 全部规则挂在 `.screen.is-master-diagram.is-p2-sop`（特指度 0,4,0）。
      P2_SOP=false → app.js 不加这个类 → 一条规则都不生效，第二页原样回 G14-A 形态。
      选 0,4,0 不是为了保险：现役表里 `.screen.is-master-diagram .quad-card__icon`（0,3,0）
      在 ≤640 档把图标压到 36px，写 `.quad-card__icon { width:112px }` 会**静默失效且零报错**
      ——AGENTS.md 第 9 条「只写数值不写选择器等于没写」，这一条就是那条纪律的落地。
   ② 每一组数字都标了**适用档位**（AGENTS.md 第 11 条）：没标档位的数字只对实测的那一档成立。
   ③ 任务线的几何由两个自定义属性推出来（--taskline-dot / --taskline-head），
      换档只改这两个数，连接线与圆点不会各算各的、对不上。 */

/* ── ① 任务线 ─────────────────────────────────────────────────────────────
   档位：≥641px（1024 / 768 两档）。圆点 56px 即 SOP §2.1 给的那一档。 */
.taskline { margin: 2px 0 0; }
.taskline[hidden] { display: none; }
/* --taskline-avatar 与 --taskline-dot **解耦**：头顶那枚头像原本跟圆点同尺寸（56），
   于是 head 必须留到 84px。头像收到 44 后 head 只要 72 —— 圆点仍是 SOP §2.1 给的 56，
   一个像素没动，省下的 12px 全部让给下面的四件事。 */
.screen.is-master-diagram.is-p2-sop .taskline { --taskline-dot: 56px; --taskline-avatar: 40px; --taskline-head: 68px; }
.taskline__rail { position: relative; display: flex; align-items: flex-start; justify-content: space-between; gap: 2px; min-width: 0; padding-top: var(--taskline-head, 84px); }
/* 连接线：左右各让出半个圆点，纵向落在圆点圆心上——两处都由同一个 --taskline-dot 推出来 */
.taskline__line { position: absolute; left: calc(var(--taskline-dot, 56px) / 2); right: calc(var(--taskline-dot, 56px) / 2); top: calc(var(--taskline-head, 84px) + var(--taskline-dot, 56px) / 2 - 3px); height: 6px; border-radius: 999px; background: var(--line); overflow: hidden; }
.taskline__line > b { display: block; height: 100%; border-radius: inherit; background: linear-gradient(90deg, #f8b34f, var(--accent)); transition: width .4s ease; }
.taskline__node { position: relative; z-index: 1; display: flex; flex-direction: column; align-items: center; gap: 4px; min-width: 0; flex: 1 1 0; }
.taskline__dot { width: var(--taskline-dot, 56px); height: var(--taskline-dot, 56px); display: grid; place-items: center; border-radius: 50%; border: 3px solid var(--line); background: var(--paper); color: var(--muted); font-size: calc(var(--taskline-dot, 56px) * .43); font-style: normal; font-weight: 900; }
.taskline__day { color: var(--muted); font-size: 12px; white-space: nowrap; }
/* 已完成：实心橙 + ✓ */
.taskline__node--done .taskline__dot { border-color: var(--accent); background: var(--accent); color: #fff; }
/* 当前：粗描边 + 放大 1.2（SOP §2.1）。未到态**不是灰锁**——虚线框 + 数字，沿 A-9 口径 */
.taskline__node--today .taskline__dot { border: 4px solid var(--accent-dark); color: var(--accent-dark); transform: scale(1.2); box-shadow: 0 8px 20px rgba(201, 88, 56, .22); }
.taskline__node--today .taskline__day { color: var(--accent-dark); font-weight: 800; }
.taskline__node--waiting .taskline__dot { border-style: dashed; }
/* 「你在这里」：绝对定位挂在当前那一格头顶，**不参与行高**——所以上面用 padding 预留了 --taskline-head */
.taskline__here { position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%); display: flex; flex-direction: column-reverse; align-items: center; gap: 4px; }
.taskline__here small { color: var(--accent-dark); font-size: 13px; font-weight: 800; white-space: nowrap; }
.taskline__avatar { display: block; width: var(--taskline-avatar, 44px); height: var(--taskline-avatar, 44px); border-radius: 50%; border: 3px solid var(--paper); background: var(--soft) center/contain no-repeat; box-shadow: 0 8px 18px rgba(51, 61, 48, .18); }
/* 线下那一句大字：M 由 medalThreshold 现算，这里只管它够大 */
.taskline__lead { margin: 4px 0 0; text-align: center; font-size: 20px; font-weight: 900; color: var(--ink); }

/* ── ② 四件事：图标 44px → 112px，标题 ≤4 字，标题下一枚「做完得什么」 ────────
   档位：≥641px。图标 112 ÷ 标题 20 = 5.6，判据 D-2 要求 ≥2（关系式，不写字面像素）。 */
.screen.is-master-diagram.is-p2-sop .quad-card { position: relative; flex-direction: column; align-items: center; gap: 8px; min-height: 0; padding: 12px 12px 14px; text-align: center; }
.screen.is-master-diagram.is-p2-sop .quad-card__icon { width: 112px; height: 112px; border-radius: 30px; font-size: 52px; overflow: hidden; }
.screen.is-master-diagram.is-p2-sop .quad-card__body { align-items: center; gap: 4px; }
/* 现役 `.screen.is-master-diagram #fourGrid { margin-top: 8px }`（G11 ⑤）在本档再收 4px */
.screen.is-master-diagram.is-p2-sop #fourGrid { margin-top: 2px; }
.screen.is-master-diagram.is-p2-sop .quad-card__name { font-size: 20px; font-weight: 900; white-space: nowrap; }
.screen.is-master-diagram.is-p2-sop .quad-card__state { position: absolute; top: 12px; right: 12px; margin: 0; }
/* 当前那一件：橙描边 + 外发光环，其余不发光 */
.screen.is-master-diagram.is-p2-sop .quad-card.is-lit { border-color: var(--accent); box-shadow: 0 0 0 4px rgba(237, 132, 91, .18), 0 14px 30px rgba(51, 61, 48, .10); }
/* 正式/占位大图盖住底层字符：加载成功才隐藏，失败时 mountArt 摘掉图层，字符原样露出（不白屏）。
   ⚠ 图层必须**脱离文档流**（absolute），不能只是 width/height 100%：
   `.quad-card__icon` 是 `display:grid`，容器里那个兜底字符是个匿名文本项，
   图会被排进**第二行**、被 overflow:hidden 裁掉半张——实测 img 的 top 比容器低 72.5px。
   匿名文本项选不中、没法叫它让位，所以让图浮上去，而不是叫字挪开。 */
.quad-card__icon { position: relative; }
.quad-card__icon .quad-art { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.quad-card__icon.has-art { color: transparent; }
/* 「做完得什么」胶囊：图形 + ≤6 字，四张卡各一枚（判据 D-3） */
.quad-card__reward { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; border-radius: 999px; background: var(--soft); color: var(--accent-dark); font-size: 13px; font-style: normal; font-weight: 800; }
.quad-card__reward[hidden] { display: none; }
.quad-card__reward-mark { font-style: normal; font-size: 15px; line-height: 1; }
.quad-card__reward.is-got { background: rgba(106, 166, 111, .16); color: #3f7a46; }

/* ── ③ 成果区：已获得有动感、未获得说得清怎么得到 ───────────────────────── */
.gains { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 14px; }
.gains[hidden] { display: none; }
.gain-card { display: flex; align-items: center; gap: 10px; min-width: 0; padding: 12px 14px; border: 1px solid var(--line); border-radius: 20px; background: var(--paper); box-shadow: 0 10px 26px rgba(51, 61, 48, .06); }
/* position: relative + 图 absolute：与四格图标同一条理由（grid 会把图排进第二行），
   现在容器里没有字，但以后往里加一个字就会复现那个坑，先按住 */
.gain-card__art { position: relative; flex: 0 0 auto; display: grid; place-items: center; width: 56px; height: 56px; border-radius: 16px; background: var(--soft); overflow: hidden; }
.gain-card__art .gain-art { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.gain-card__label { min-width: 0; font-size: 15px; font-weight: 800; }
.gain-card__ask { margin-left: auto; margin-top: 0; flex: 0 0 auto; }
/* 未获得：灰度 + 半透明。**不是 disabled**——它照样可点，点了会告诉你怎么得到（A-9 同一条口径） */
.gain-card.is-locked .gain-card__art { filter: grayscale(1); opacity: .45; }
.gain-card.is-locked .gain-card__label { color: var(--muted); }
/* 飞入 + 回弹 + 闪光：迭代次数写死 1，**没有 infinite**——每次进页只播一次（判据 D-5）。
   「只播一次」的另一半在 app.js：播过的键记在 gainAnimPlayed 里，重渲染不再带这个类。 */
@keyframes gain-fly-in {
  0%   { transform: translate(26px, -32px) scale(.4); opacity: 0; }
  55%  { transform: translate(0, 0) scale(1.18); opacity: 1; }
  72%  { transform: scale(.94); }
  86%  { transform: scale(1.06); }
  100% { transform: scale(1); opacity: 1; }
}
@keyframes gain-sparkle {
  0%   { box-shadow: 0 0 0 0 rgba(248, 179, 79, 0); }
  40%  { box-shadow: 0 0 0 10px rgba(248, 179, 79, .35); }
  100% { box-shadow: 0 0 0 20px rgba(248, 179, 79, 0); }
}
.gain-card__art.is-fly-in { animation: gain-fly-in .72s cubic-bezier(.22, 1.2, .36, 1) 1 both, gain-sparkle .9s ease-out .25s 1 both; }
@media (prefers-reduced-motion: reduce) { .gain-card__art.is-fly-in { animation: none; } }

/* ── 窄屏两档（实测订正，AGENTS.md 第 11 条）─────────────────────────────
   ≤640 档：四格已是 2×2，112px 图标仍装得下；圆点 56×7=392 > 可用宽，收到 44。
   ≤480 档（375 实测）：圆点再收到 38，图标 96 / 标题 18 → 比值 5.33，D-2 仍 ≥2。 */
@media (max-width: 640px) {
  .screen.is-master-diagram.is-p2-sop .taskline { --taskline-dot: 44px; --taskline-avatar: 36px; --taskline-head: 62px; }
  .taskline__lead { margin-top: 6px; font-size: 18px; }
  .taskline__day { font-size: 11px; }
  .screen.is-master-diagram.is-p2-sop .quad-card { padding: 10px 10px 12px; gap: 6px; }
  .screen.is-master-diagram.is-p2-sop #fourGrid { gap: 10px; }
  .gains { gap: 10px; }
  .gain-card { padding: 10px 12px; gap: 8px; }
  .gain-card__art { width: 46px; height: 46px; border-radius: 14px; }
  .gain-card__label { font-size: 14px; }
}
@media (max-width: 480px) {
  .screen.is-master-diagram.is-p2-sop .taskline { --taskline-dot: 38px; --taskline-avatar: 30px; --taskline-head: 56px; }
  .taskline__here small { font-size: 11px; }
  .screen.is-master-diagram.is-p2-sop .quad-card__icon { width: 96px; height: 96px; border-radius: 26px; font-size: 44px; }
  .screen.is-master-diagram.is-p2-sop .quad-card__name { font-size: 18px; }
  .taskline__lead { font-size: 16px; }
  /* 一行装不下两张成果卡时改上下排：宁可多一行，也不要把字压到读不出 */
  .gains { grid-template-columns: minmax(0, 1fr); }
}

/* ══════════ G14-D · 第①件补完（幸福格言上屏 + 第七天完整表演）══════════
   核对 xlsx 任务一逐行后补的两块。两块都挂在 `EXERCISE_FINALE` 之下：
   常量置 false → 两个容器保持 hidden，第①件原样回到 G2 形态。 */

/* ⚠ `.exercise-art` 是 `display:grid`，类规则会盖过 [hidden] 的 UA 默认值——
   不补这一条，完整表演态屏上会留一个空的虚线占位框（实看发现，测试看不出来） */
.exercise-art[hidden] { display: none; }

/* 幸福格言：这一屏的落点，字号要压得住——它是这件事的名字 */
.exercise-slogan { margin: 6px 0 0; text-align: center; font-size: 18px; font-weight: 900; color: var(--accent-dark); }
.exercise-slogan[hidden] { display: none; }

/* 完整表演：六格一行排开。窄屏折成两行，不横向滚动（D18 那一课） */
.exercise-finale { margin: 10px 0 2px; }
.exercise-finale[hidden] { display: none; }
.exercise-finale__tag { display: inline-block; padding: 4px 12px; border-radius: 999px; background: var(--soft); color: var(--accent-dark); font-size: 12px; font-weight: 800; }
.exercise-finale__row { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 8px; margin: 10px 0 0; padding: 0; list-style: none; min-width: 0; }
.exercise-finale__step { display: flex; flex-direction: column; align-items: center; gap: 4px; min-width: 0; }
.exercise-finale__art { width: 100%; aspect-ratio: 1; border-radius: 14px; background: var(--cream) center/contain no-repeat; }
.exercise-finale__step b { font-size: 13px; color: var(--accent-dark); }
.exercise-finale__step small { font-size: 11px; line-height: 1.35; color: var(--muted); text-align: center; }
@media (max-width: 640px) {
  .exercise-finale__row { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
  .exercise-slogan { font-size: 16px; }
}

/* ══════════ G14-E · 完成任务 → 奖励 → 家园（把断掉的三跳接上）══════════
   两块都挂在 P2_REWARD_LOOP 之下：常量置 false → 两个容器保持 hidden、
   带子里的 #growthPreview 原样回来，第二页回到 G14-B 形态。 */

/* ① 今日印章：卡上那句「做完得 🏅×1」的落点。四格一行，做完当场点亮 */
.stamps { margin-top: 12px; }
.stamps[hidden] { display: none; }
.stamps__row { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; margin: 0; padding: 0; list-style: none; min-width: 0; }
.stamp { position: relative; display: grid; place-items: center; aspect-ratio: 1; border-radius: 18px; border: 2px dashed var(--line); background: var(--paper); }
.stamp__art { width: 74%; height: 74%; background: center/contain no-repeat; filter: grayscale(1); opacity: .28; transition: filter .3s ease, opacity .3s ease; }
.stamp__mark { position: absolute; right: 6px; bottom: 4px; font-style: normal; font-size: 16px; color: var(--accent-dark); }
/* 点亮：实线描边 + 图恢复颜色 + 一枚 ✦。**不是灰锁变亮，是从"还没有"变成"有了"** */
.stamp.is-lit { border-style: solid; border-color: var(--accent); background: linear-gradient(180deg, #fff7ef, var(--paper)); box-shadow: 0 8px 20px rgba(237, 132, 91, .14); }
.stamp.is-lit .stamp__art { filter: none; opacity: 1; }
.stamps__count { margin: 8px 0 0; text-align: center; font-size: 15px; font-weight: 800; color: var(--accent-dark); }

/* ② 美德家园：奖励的落点。图占主体，字只有一句 */
.p2-garden { display: flex; align-items: center; gap: 14px; margin-top: 12px; padding: 12px 16px; border: 1px solid var(--line); border-radius: 22px; background: var(--paper); box-shadow: 0 10px 26px rgba(51, 61, 48, .06); }
.p2-garden[hidden] { display: none; }
.p2-garden__art { position: relative; flex: 0 0 auto; width: 104px; height: 76px; border-radius: 16px; background: var(--cream) center/contain no-repeat; overflow: hidden; }
.p2-garden__art .garden-art { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.p2-garden__stage { margin: 0; min-width: 0; font-size: 17px; font-weight: 800; color: var(--ink); }

@media (max-width: 640px) {
  .stamps__row { gap: 8px; }
  .stamp { border-radius: 14px; }
  .stamps__count { font-size: 14px; }
  .p2-garden { gap: 10px; padding: 10px 12px; }
  .p2-garden__art { width: 84px; height: 62px; }
  .p2-garden__stage { font-size: 15px; }
}

/* G14-E 追加：印章刚点亮那一枚亮一下。迭代次数写死 1，**没有 infinite**——
   「只播一次」的另一半在 app.js：播过的键记在 stampAnimPlayed 里，重渲染不再带这个类。 */
@keyframes stamp-pop {
  0%   { transform: scale(.55) rotate(-8deg); opacity: 0; }
  55%  { transform: scale(1.16) rotate(2deg); opacity: 1; }
  75%  { transform: scale(.96); }
  100% { transform: scale(1); opacity: 1; }
}
@keyframes stamp-glow {
  0%   { box-shadow: 0 0 0 0 rgba(237, 132, 91, 0); }
  45%  { box-shadow: 0 0 0 9px rgba(237, 132, 91, .32); }
  100% { box-shadow: 0 0 0 18px rgba(237, 132, 91, 0); }
}
.stamp.is-just-lit { animation: stamp-glow .9s ease-out 1 both; }
.stamp.is-just-lit .stamp__art { animation: stamp-pop .6s cubic-bezier(.22, 1.2, .36, 1) 1 both; }
@media (prefers-reduced-motion: reduce) {
  .stamp.is-just-lit, .stamp.is-just-lit .stamp__art { animation: none; }
}

/* ── G17-D ②：时光屏「留下了什么」总览（PROMISE_REDEEM 置 false 时整块不出场） ── */
.memory-summary { margin: 0 0 18px; padding: 16px 18px; border-radius: 18px;
  background: var(--card); box-shadow: 0 6px 18px rgba(90, 70, 40, .06); }
.memory-summary__counts { display: block; font-size: 19px; color: var(--accent-dark); }
.memory-summary__stage { margin: 4px 0 12px; font-size: 15px; color: var(--muted); }
.memory-summary__list { list-style: none; margin: 0; padding: 0;
  display: grid; gap: 6px; }
.memory-summary__row { display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline;
  min-width: 0; font-size: 14px; color: var(--ink); }
/* min-width:0 是 D18 那道防线：不写它，长文本会把栅格撑出 375 档（R23 栽过一次） */
.memory-summary__row > span { min-width: 0; color: var(--muted); word-break: break-word; }

/* ── G19：第③件仪式外壳（QUAD3_SOURCE 置回 realityTask 时整块不出场） ── */
.interaction-shell { margin: 0 0 14px; padding: 12px 14px; border-radius: 16px;
  background: var(--soft, #fdf3e7); }
.interaction-shell__title { display: block; font-size: 18px; color: var(--accent-dark); }
.interaction-shell__invite { margin: 4px 0 2px; font-size: 15px; color: var(--ink); }
.interaction-shell__together { margin: 0; font-size: 14px; color: var(--muted); }
/* min-width:0 那道防线同 D18：长文本不许把父栅格撑出 375 档 */
.interaction-shell > * { min-width: 0; word-break: break-word; }
