/* ============================================================
   components.css — 卡片 / 进度环 / 勾选 / 日历热力图 / 弹窗
   ============================================================ */

/* ============ 卡片 ============ */
/* ★ 入场动画用 backwards 而不是 both。
   both 会让终态（transform: none）在动画结束后一直锁住，
   而动画声明的优先级高于一切普通样式，于是下面 :hover 的 translateY
   永远赢不了 —— 悬浮抬升形同虚设。backwards 只在延迟期间保持起始帧，
   播完就把控制权还给普通样式；终态本来就和自然样式一致，视觉上没有差别。 */
.card {
  background: var(--surface);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-2);
  padding: var(--s-4) var(--s-4) var(--s-3);
  animation: card-in var(--t-card) var(--ease) backwards;
}

@keyframes card-in {
  from { opacity: 0; transform: translateY(10px) scale(.985); }
  to   { opacity: 1; transform: none; }
}

/* 列表内卡片依次进场。用 --i 变量做阶梯延迟，
   超过 8 个后不再累加，避免长列表末尾等太久。 */
.card { animation-delay: calc(min(var(--i, 0), 7) * 45ms); }

.card--goal { border: 1px solid var(--line); position: relative; }

/* 整行做完后，卡片上扫过一道柔光。
   不用 overflow:hidden 裁剪（会误伤里面的焦点环与阴影），
   而是让伪元素自己带圆角，渐变在它自己的盒子里平移。 */
.card--goal::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(105deg, transparent 38%, var(--sheen) 50%, transparent 62%) no-repeat;
  background-size: 250% 100%;
  background-position: 100% 0;
}

.card--goal[data-sweep="1"]::after {
  animation: card-sweep 900ms var(--ease) 1;
}

@keyframes card-sweep {
  0%   { opacity: 1; background-position: 100% 0; }
  100% { opacity: 1; background-position: 0% 0; }
}

/* 悬浮微抬升。只改 transform 与 shadow，触发合成层，不引起重排。
   触屏设备上 @media (hover: hover) 会把它排除在外，
   避免「点一下卡片却像浮起来」的怪异感。 */
@media (hover: hover) {
  .card--goal {
    transition: transform var(--t-base) var(--ease),
                box-shadow var(--t-base) var(--ease),
                border-color var(--t-base) var(--ease);
  }
  .card--goal:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-3);
  }
  .card--goal:active { transform: translateY(0) scale(.995); }
}

.card--behind {
  border-color: color-mix(in srgb, var(--warn) 35%, var(--line));
  background: linear-gradient(180deg, var(--warn-soft), var(--surface) 64%);
}

.card--caught {
  border-color: color-mix(in srgb, var(--done) 30%, var(--line));
}

/* ============ 目标卡头部 ============ */
.goal-head {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
}

.goal-head__main { flex: 1; min-width: 0; }

.goal-title {
  font-size: var(--f-lg);
  font-weight: 800;
  letter-spacing: -.01em;
  line-height: 1.3;
  overflow-wrap: anywhere;
}

.goal-title--done {
  color: var(--ink-3);
  text-decoration: line-through;
  text-decoration-color: var(--ink-4);
  text-decoration-thickness: 2px;
}

.goal-meta {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin-top: 5px;
  flex-wrap: wrap;
  font-size: var(--f-xs);
  color: var(--ink-3);
  font-weight: 600;
}

.goal-meta .dot { color: var(--ink-4); }

/* 学科色片 */
.subject-chip {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 8px;
  border-radius: var(--r-pill);
  font-size: var(--f-2xs);
  font-weight: 800;
  letter-spacing: .02em;
  color: var(--chip-ink, var(--ink-2));
  background: var(--chip-bg, var(--surface-sunk));
}

/* ============ 进度环 ============ */
.ring { position: relative; width: 54px; height: 54px; flex: 0 0 auto; }
.ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }

.ring__track { stroke: var(--line); }
.ring__fill {
  stroke: var(--accent);
  stroke-linecap: round;
  transition: stroke-dashoffset 720ms var(--ease), stroke 300ms linear;
}
.ring__fill--done  { stroke: var(--done); }
.ring__fill--carry { stroke: var(--carry); }

/* 进度环光晕：画在 .ring 容器的伪元素上，只动 opacity / transform。

   ★ 不能把 filter: drop-shadow 加在 .ring__fill 上 —— 它是 SVG 内部的 <circle>，
     Safari/WebKit 不支持对 SVG 子元素用 CSS filter，iPhone 上光晕根本不会出现。
     伪元素 + box-shadow 在所有浏览器里行为一致，而且 opacity 动画走合成层，很省。 */
.ring::before {
  content: "";
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  box-shadow: 0 0 10px 1px var(--accent-ring);
}

/* 进行中：缓慢呼吸，完成或落后时停下 */
.ring--live::before { animation: ring-halo 2.6s ease-in-out infinite; }

@keyframes ring-halo {
  0%, 100% { opacity: 0; }
  50%      { opacity: 1; }
}

/* 刚完成：绿色光晕向外扩散一次 */
.ring--done::before {
  box-shadow: 0 0 12px 2px color-mix(in srgb, var(--done) 55%, transparent);
  animation: ring-done-halo 900ms var(--ease) 1 both;
}

@keyframes ring-done-halo {
  0%   { opacity: 0; transform: scale(.9); }
  40%  { opacity: 1; }
  100% { opacity: 0; transform: scale(1.14); }
}

/* 数值跳变时短暂放大高亮 */
.ring__label--bump {
  animation: num-bump 420ms var(--ease-back);
}

@keyframes num-bump {
  0%   { transform: scale(1); }
  38%  { transform: scale(1.18); color: var(--accent-ink); }
  100% { transform: scale(1); }
}

.ring__label {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--f-xs);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}

/* ============ 进度条 ============ */
.bar {
  position: relative;
  height: 8px;
  border-radius: var(--r-pill);
  background: var(--accent-softer);
  overflow: hidden;
}

.bar__fill {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: var(--r-pill);
  background: linear-gradient(90deg, var(--accent), color-mix(in srgb, var(--accent) 72%, #FFD9A8));
  transition: width 720ms var(--ease), background 300ms linear;
  width: 0;
}

.bar__fill--done { background: linear-gradient(90deg, var(--done), color-mix(in srgb, var(--done) 74%, #BDEBD9)); }

/* 进度条流动光泽，弱动画，只在未完成时出现 */
.bar__fill--live::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, transparent 20%, rgba(255,255,255,.42) 50%, transparent 80%);
  animation: sheen 2.4s linear infinite;
}

@keyframes sheen {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%); }
}

/* ============ 待办条目 ============ */
.todo {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-height: 44px;
  padding: var(--s-2) 0;
}

.todo + .todo { border-top: 1px solid var(--line); }

/* --- 勾选圆点 --- */
.check {
  position: relative;
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  margin-left: -10px;
}

.check__box {
  width: 25px;
  height: 25px;
  border-radius: 50%;
  border: 2px solid var(--line-strong);
  background: var(--surface);
  display: grid;
  place-items: center;
  transition: border-color var(--t-micro) var(--ease),
              background var(--t-micro) var(--ease),
              transform var(--t-micro) var(--ease-back);
}

.check__box svg {
  width: 15px;
  height: 15px;
  stroke: #fff;
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
  /* 描边绘制动画 */
  stroke-dasharray: 26;
  stroke-dashoffset: 26;
  transition: stroke-dashoffset 260ms var(--ease) 60ms;
}

.check:active .check__box { transform: scale(.88); }

.todo[data-done="1"] .check__box {
  border-color: var(--done);
  background: var(--done);
}
.todo[data-done="1"] .check__box svg { stroke-dashoffset: 0; }

.todo[data-carry="1"] .check__box { border-color: var(--carry); }

/* 部分完成：用扇形进度提示，不用文字，保持安静 */
.check__box--partial {
  border-color: var(--accent) !important;
  background: conic-gradient(var(--accent) calc(var(--p, 0) * 1%), var(--surface) 0) !important;
}

/* --- 内容 --- */
.todo__body { flex: 1; min-width: 0; }

.todo__label {
  position: relative;
  display: inline-block;
  font-size: var(--f-md);
  font-weight: 700;
  line-height: 1.35;
  transition: color var(--t-base) var(--ease);
}

/* 划线从左到右扫过 */
.todo__label::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 52%;
  height: 1.5px;
  background: currentColor;
  border-radius: 2px;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 320ms var(--ease) 90ms;
}

.todo[data-done="1"] .todo__label { color: var(--ink-3); }
.todo[data-done="1"] .todo__label::after { transform: scaleX(1); }

.todo__note {
  margin-top: 2px;
  font-size: var(--f-xs);
  color: var(--ink-3);
  font-weight: 600;
}

/* --- 数量步进器 --- */
.step {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  border-radius: var(--r-pill);
  background: var(--surface-sunk);
  border: 1px solid var(--line);
}

.step__btn {
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--ink-2);
  transition: background var(--t-micro) var(--ease), color var(--t-micro) var(--ease),
              transform var(--t-micro) var(--ease-back);
}

.step__btn:active { transform: scale(.9); background: var(--accent-soft); }
.step__btn[disabled] { color: var(--ink-4); opacity: .45; }

.step__val {
  min-width: 46px;
  text-align: center;
  font-size: var(--f-sm);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
}

/* ============ 单位清单：待办行内的具体篇目 ============ */
.todo--units {
  align-items: stretch;
  padding: var(--s-3) 0;
}

.unit-list {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.unit-item {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  /* 保证可点区域 >= 44px，iPhone 上单手也不易点错 */
  min-height: 44px;
  padding: 2px 0;
  transition: opacity var(--t-base) var(--ease);
}

/* 完成项：整体下沉淡出，但保留可点（便于回退） */
.unit-item[data-done="1"] { opacity: .55; }
.unit-item[data-skipped="1"] { opacity: .4; }

.unit-check {
  flex: 0 0 auto;
  width: 40px;
  height: 44px;
  margin-left: -8px;
  display: grid;
  place-items: center;
}

.unit-box {
  width: 22px;
  height: 22px;
  border-radius: 7px;
  border: 2px solid var(--line-strong);
  background: var(--surface);
  display: grid;
  place-items: center;
  transition: border-color var(--t-micro) var(--ease),
              background var(--t-micro) var(--ease),
              transform var(--t-micro) var(--ease-back),
              border-radius var(--t-base) var(--ease);
}

.unit-box svg {
  width: 13px;
  height: 13px;
  stroke: #fff;
  stroke-width: 3.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
  /* 描边绘制动画 */
  stroke-dasharray: 24;
  stroke-dashoffset: 24;
  transition: stroke-dashoffset 240ms var(--ease) 40ms;
}

.unit-check:active .unit-box { transform: scale(.86); }

/* 打勾回弹 + 涟漪。
   用 data-pop 这个由 JS 临时挂上、播完即摘的属性触发，而不是挂在 data-done 上：
   data-done 在每次渲染时都带着，挂上去会让页面一刷新所有已完成项集体回弹一遍。 */
.unit-box { position: relative; }

.unit-box::after {
  content: "";
  position: absolute;
  inset: -3px;
  border-radius: 50%;
  border: 2px solid var(--done);
  opacity: 0;
  pointer-events: none;
}

.unit-item[data-pop="1"] .unit-box { animation: unit-pop 420ms var(--ease-back); }
.unit-item[data-pop="1"] .unit-box::after { animation: unit-ripple 560ms var(--ease) 1 both; }

@keyframes unit-pop {
  0%   { transform: scale(.78); }
  55%  { transform: scale(1.16); }
  100% { transform: scale(1); }
}

@keyframes unit-ripple {
  0%   { opacity: .6; transform: scale(.75); }
  100% { opacity: 0;  transform: scale(2.2); }
}

.unit-item[data-done="1"] .unit-box {
  border-color: var(--done);
  background: var(--done);
  border-radius: 50%;
}
.unit-item[data-done="1"] .unit-box svg { stroke-dashoffset: 0; }

/* 顺延来的单位用橙色描边区分 */
.unit-item[data-carry="1"] .unit-box { border-color: var(--carry); }
.unit-item[data-carry="1"][data-done="1"] .unit-box {
  border-color: var(--done);
  background: var(--done);
}

.unit-name {
  position: relative;
  flex: 1;
  min-width: 0;
  font-size: var(--f-md);
  font-weight: 700;
  letter-spacing: .01em;
  overflow-wrap: anywhere;
  transition: color var(--t-base) var(--ease);
}

/* 划线从左到右扫过 */
.unit-name::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 54%;
  height: 1.5px;
  background: currentColor;
  border-radius: 2px;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 300ms var(--ease) 70ms;
}

.unit-name--done,
.unit-item[data-done="1"] .unit-name { color: var(--ink-3); }

.unit-name--done::after,
.unit-item[data-done="1"] .unit-name::after { transform: scaleX(1); }

/* 展开更多 */
.todo__more {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  margin-top: 2px;
  padding: 0 var(--s-3);
  border-radius: var(--r-pill);
  background: var(--surface-sunk);
  color: var(--accent-ink);
  font-size: var(--f-xs);
  font-weight: 800;
  transition: background var(--t-micro) var(--ease);
}
.todo__more:active { background: var(--accent-soft); }

/* ============ 单位清单编辑器（弹窗内） ============ */
.unit-editor {
  margin-top: 2px;
  padding: var(--s-3);
  border-radius: var(--r-md);
  border: 1px solid var(--line);
  background: var(--surface-sunk);
}

.unit-editor__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
}

.unit-gen {
  margin-top: var(--s-3);
  padding-top: var(--s-3);
  border-top: 1px dashed var(--line-strong);
  animation: gen-in var(--t-card) var(--ease) both;
}

@keyframes gen-in {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

.unit-check-row {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 40px;
}

.unit-check-row input[type="checkbox"] {
  width: 20px;
  height: 20px;
  accent-color: var(--accent);
  flex: 0 0 auto;
}

.unit-list-edit {
  margin-top: var(--s-3);
  max-height: 260px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.unit-list-edit:empty { margin-top: 0; }

.unit-edit-row {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 48px;
  padding: 2px 0;
  animation: row-in 220ms var(--ease) both;
}

@keyframes row-in {
  from { opacity: 0; transform: translateX(-6px); }
  to   { opacity: 1; transform: none; }
}

.unit-edit-row + .unit-edit-row { border-top: 1px solid var(--line); }

.unit-edit-row__num {
  flex: 0 0 auto;
  width: 26px;
  text-align: right;
  font-size: var(--f-2xs);
  font-weight: 800;
  color: var(--ink-4);
  font-variant-numeric: tabular-nums;
}

.unit-edit-row__input {
  flex: 1;
  min-width: 0;
  height: 38px;
  padding: 0 10px;
  border-radius: var(--r-sm);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  font-size: var(--f-sm);
  font-weight: 700;
  outline: none;
  transition: border-color var(--t-micro) var(--ease), box-shadow var(--t-micro) var(--ease);
}

.unit-edit-row__input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
}

.unit-edit-row__btn {
  flex: 0 0 auto;
  width: 36px;
  height: 38px;
  display: grid;
  place-items: center;
  border-radius: var(--r-sm);
  color: var(--ink-3);
  transition: background var(--t-micro) var(--ease), color var(--t-micro) var(--ease);
}

.unit-edit-row__btn:active { background: var(--accent-soft); }
.unit-edit-row__btn[disabled] { opacity: .3; pointer-events: none; }
.unit-edit-row__btn--del { color: var(--danger); }
.unit-edit-row__btn--del:active { background: var(--danger-soft); }

/* ============ 顺延区 ============ */
.carried-zone {
  border: 1px solid color-mix(in srgb, var(--carry) 30%, var(--line));
  border-left: 3px solid var(--carry);
  border-radius: var(--r-md);
  background: linear-gradient(180deg, var(--carry-soft), var(--surface) 70%);
  padding: var(--s-3) var(--s-4);
  margin-bottom: var(--s-4);
  animation: carry-in 420ms var(--ease) both;
}

@keyframes carry-in {
  from { opacity: 0; transform: translateY(-10px); }
  to   { opacity: 1; transform: none; }
}

/* 进场后脉冲一次，明示「这些是昨天的」 */
.carried-zone::after { content: none; }
.carried-zone[data-pulse="1"] { animation: carry-in 420ms var(--ease) both, carry-pulse 900ms var(--ease) 320ms 1; }

@keyframes carry-pulse {
  0%   { box-shadow: 0 0 0 0 var(--accent-ring); }
  70%  { box-shadow: 0 0 0 10px rgba(242,118,46,0); }
  100% { box-shadow: 0 0 0 0 rgba(242,118,46,0); }
}

.carried-zone__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
}

.carried-zone__title {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--f-sm);
  font-weight: 800;
  color: var(--carry-ink);
}

.carried-zone__count {
  display: inline-grid;
  place-items: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--r-pill);
  background: var(--carry);
  color: #fff;
  font-size: var(--f-2xs);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

/* 「来自 10-04」徽标 */
.carry-badge {
  display: inline-flex;
  align-items: center;
  height: 18px;
  padding: 0 7px;
  border-radius: var(--r-pill);
  background: var(--carry-soft);
  border: 1px solid color-mix(in srgb, var(--carry) 34%, transparent);
  font-size: var(--f-2xs);
  font-weight: 800;
  color: var(--carry-ink);
  font-variant-numeric: tabular-nums;
}

/* ============ 顶部统计条 ============ */
.stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s-2);
  padding: var(--s-3);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-2);
}

.stat { text-align: center; }
.stat + .stat { border-left: 1px solid var(--line); }

.stat__v {
  font-size: var(--f-xl);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.02em;
}

.stat__k {
  margin-top: 1px;
  font-size: var(--f-2xs);
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--ink-3);
  text-transform: uppercase;
}

/* ============ 按钮 ============ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 44px;
  padding: 0 var(--s-4);
  border-radius: var(--r-pill);
  font-size: var(--f-sm);
  font-weight: 800;
  letter-spacing: .01em;
  transition: transform var(--t-micro) var(--ease-back),
              background var(--t-micro) var(--ease),
              box-shadow var(--t-micro) var(--ease),
              opacity var(--t-micro) linear;
}

.btn:active { transform: scale(.955); }
.btn[disabled] { opacity: .45; pointer-events: none; }

.btn--primary {
  background: var(--accent);
  color: #fff;
  box-shadow: 0 2px 8px rgba(200, 100, 40, .28);
}
.btn--primary:active { box-shadow: 0 1px 3px rgba(200, 100, 40, .3); }

.btn--soft { background: var(--accent-softer); color: var(--accent-ink); }
.btn--ghost { background: transparent; color: var(--ink-2); border: 1px solid var(--line-strong); }
.btn--danger { background: var(--danger-soft); color: var(--danger); }
.btn--sm { min-height: 34px; padding: 0 var(--s-3); font-size: var(--f-xs); }
.btn--block { width: 100%; }
.btn--icon { width: 44px; padding: 0; }

.btn--fab {
  position: fixed;
  right: var(--s-5);
  bottom: calc(var(--tabbar-h) + var(--sa-bottom) + var(--s-4));
  width: 56px;
  height: 56px;
  min-height: 56px;
  padding: 0;
  border-radius: 50%;
  z-index: 35;
  box-shadow: var(--shadow-pop);
}

/* ============ 胶囊标签 ============ */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 26px;
  padding: 0 10px;
  border-radius: var(--r-pill);
  font-size: var(--f-xs);
  font-weight: 800;
  background: var(--accent-softer);
  color: var(--accent-ink);
}

.pill--done  { background: var(--done-soft);  color: var(--done-ink); }
.pill--carry { background: var(--carry-soft); color: var(--carry-ink); }
.pill--warn  { background: var(--warn-soft);  color: var(--warn); }
.pill--mute  { background: var(--surface-sunk); color: var(--ink-3); }

/* ============ 空状态 ============ */
.empty {
  text-align: center;
  padding: var(--s-8) var(--s-5);
  animation: card-in var(--t-card) var(--ease) both;
}

.empty__art {
  width: 96px;
  height: 96px;
  margin: 0 auto var(--s-5);
  border-radius: 32px;
  display: grid;
  place-items: center;
  background: linear-gradient(145deg, var(--accent-soft), var(--accent-softer));
  color: var(--accent);
  animation: float 4s var(--ease) infinite;
}

@keyframes float {
  0%, 100% { transform: translateY(0) rotate(-2deg); }
  50%      { transform: translateY(-8px) rotate(2deg); }
}

.empty__title { font-size: var(--f-lg); font-weight: 800; }
.empty__text  { margin-top: 6px; font-size: var(--f-sm); color: var(--ink-3); }

/* ============ 表单 ============ */
.field { display: block; margin-bottom: var(--s-4); }

.field__label {
  display: block;
  margin-bottom: 6px;
  font-size: var(--f-xs);
  font-weight: 800;
  letter-spacing: .04em;
  color: var(--ink-3);
  text-transform: uppercase;
}

.input, .select {
  width: 100%;
  min-height: 48px;
  padding: 0 var(--s-4);
  border-radius: var(--r-md);
  border: 1px solid var(--line-strong);
  background: var(--surface-sunk);
  font-size: var(--f-md);
  font-weight: 600;
  outline: none;
  transition: border-color var(--t-micro) var(--ease),
              box-shadow var(--t-micro) var(--ease),
              background var(--t-micro) var(--ease);
}

.input:focus, .select:focus {
  border-color: var(--accent);
  background: var(--surface);
  box-shadow: 0 0 0 4px var(--accent-ring);
}

/* iOS 上 date/number 输入框默认样式很丑，这里统一压平 */
.input[type="date"], .input[type="number"] {
  -webkit-appearance: none;
  appearance: none;
}

.select {
  -webkit-appearance: none;
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-3) 50%),
                    linear-gradient(135deg, var(--ink-3) 50%, transparent 50%);
  background-position: calc(100% - 20px) 21px, calc(100% - 15px) 21px;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 40px;
}

.field__hint { margin-top: 5px; font-size: var(--f-xs); color: var(--ink-3); }
.field__error { margin-top: 5px; font-size: var(--f-xs); font-weight: 700; color: var(--danger); }

/* 策略选择：分段控件 */
.segmented {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 3px;
  padding: 3px;
  border-radius: var(--r-md);
  background: var(--surface-sunk);
  border: 1px solid var(--line);
}

.segmented__opt {
  min-height: 40px;
  padding: 0 6px;
  border-radius: var(--r-sm);
  font-size: var(--f-xs);
  font-weight: 800;
  color: var(--ink-3);
  transition: background var(--t-base) var(--ease),
              color var(--t-base) var(--ease),
              box-shadow var(--t-base) var(--ease);
}

.segmented__opt[aria-pressed="true"] {
  background: var(--surface);
  color: var(--accent-ink);
  box-shadow: var(--shadow-1);
}

/* ============ 底部弹窗 ============ */
.sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(60, 40, 25, .34);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  opacity: 0;
  transition: opacity var(--t-base) var(--ease);
}

.sheet-backdrop[data-open="true"] { opacity: 1; }

.sheet {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 61;
  max-height: 92dvh;
  display: flex;
  flex-direction: column;
  padding: 0 var(--s-5) calc(var(--sa-bottom) + var(--s-5));
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  background: var(--surface);
  box-shadow: 0 -8px 40px rgba(120, 70, 30, .18);
  transform: translateY(102%);
  transition: transform var(--t-card) var(--ease);
}

.sheet[data-open="true"] { transform: none; }

.sheet__grab {
  width: 40px;
  height: 5px;
  margin: var(--s-3) auto var(--s-4);
  border-radius: var(--r-pill);
  background: var(--line-strong);
  flex: 0 0 auto;
}

.sheet__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  margin-bottom: var(--s-4);
  flex: 0 0 auto;
}

.sheet__title { font-size: var(--f-xl); font-weight: 800; letter-spacing: -.01em; }

.sheet__body {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding-bottom: var(--s-4);
}

.sheet__foot {
  flex: 0 0 auto;
  padding-top: var(--s-3);
  border-top: 1px solid var(--line);
}

/* ============ 预览条（新建目标时实时显示拆解结果） ============ */
.preview {
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md);
  background: var(--accent-softer);
  border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
  font-size: var(--f-xs);
  line-height: 1.65;
  color: var(--ink-2);
  font-weight: 600;
}

.preview b { color: var(--accent-ink); font-weight: 800; }

/* ============ 微缩日历 / 热力图 ============ */
.heat {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
}

.heat__cell {
  aspect-ratio: 1;
  border-radius: 7px;
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  display: grid;
  place-items: center;
  font-size: var(--f-2xs);
  font-weight: 700;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
  transition: transform var(--t-micro) var(--ease-back),
              background var(--t-base) var(--ease);
}

.heat__cell--head { background: none; border: 0; color: var(--ink-4); }
.heat__cell--past-empty { opacity: .5; }
.heat__cell--l1 { background: color-mix(in srgb, var(--done) 22%, var(--surface)); border-color: transparent; color: var(--done-ink); }
.heat__cell--l2 { background: color-mix(in srgb, var(--done) 42%, var(--surface)); border-color: transparent; color: var(--done-ink); }
.heat__cell--l3 { background: color-mix(in srgb, var(--done) 66%, var(--surface)); border-color: transparent; color: #fff; }
.heat__cell--l4 { background: var(--done); border-color: transparent; color: #fff; }
.heat__cell--l4 { box-shadow: 0 2px 8px color-mix(in srgb, var(--done) 45%, transparent); }

.heat__cell--miss { background: var(--danger-soft); border-color: transparent; color: var(--danger); }

.heat__cell--today {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  transform: scale(1.08);
}

/* 热力图图例 */
.legend {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: var(--s-4);
  font-size: var(--f-2xs);
  color: var(--ink-3);
  font-weight: 700;
}

.legend__sw { width: 13px; height: 13px; border-radius: 4px; border: 1px solid var(--line); }

/* ============ 目标列表项（管理页） ============ */
.goal-row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) 0;
}

.goal-row + .goal-row { border-top: 1px solid var(--line); }

.goal-row__main { flex: 1; min-width: 0; }
.goal-row__title { font-size: var(--f-md); font-weight: 800; overflow-wrap: anywhere; }
.goal-row__meta { margin-top: 2px; font-size: var(--f-xs); color: var(--ink-3); font-weight: 600; }

/* ============ 完成庆祝粒子 ============ */
.confetti {
  position: fixed;
  inset: 0;
  z-index: 70;
  pointer-events: none;
  overflow: hidden;
}

.confetti__p {
  position: absolute;
  width: 9px;
  height: 9px;
  border-radius: 2px;
  animation: confetti-fall 1100ms var(--ease-in) forwards;
}

@keyframes confetti-fall {
  0%   { opacity: 1; transform: translate3d(0, 0, 0) rotate(0deg) scale(1); }
  100% { opacity: 0; transform: translate3d(var(--dx, 0), var(--dy, 220px), 0)
                                     rotate(var(--rot, 300deg)) scale(.6); }
}

/* 全部完成时的大勾 */
.all-done {
  position: fixed;
  inset: 0;
  z-index: 71;
  display: grid;
  place-items: center;
  pointer-events: none;
}

.all-done__badge {
  width: 132px;
  height: 132px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--done);
  color: #fff;
  box-shadow: 0 18px 50px color-mix(in srgb, var(--done) 42%, transparent);
  animation: badge-pop 1500ms var(--ease) forwards;
}

@keyframes badge-pop {
  0%   { opacity: 0; transform: scale(.4) rotate(-12deg); }
  22%  { opacity: 1; transform: scale(1.08) rotate(3deg); }
  34%  { transform: scale(1) rotate(0deg); }
  78%  { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.12); }
}

/* 勾号线条加粗。svg() 工具函数里已经带了 stroke-width，
   再往 extra 里塞一个会变成重复属性，HTML 解析时后者被丢弃，所以改用 CSS 覆盖。 */
.all-done__badge svg { stroke-width: 2.6; }

/* 大任务完成：更大、停留更久、外圈多一层光环 */
.all-done--goal .all-done__badge {
  width: 168px;
  height: 168px;
  animation-duration: 2100ms;
  box-shadow: 0 22px 60px color-mix(in srgb, var(--done) 48%, transparent),
              0 0 0 14px color-mix(in srgb, var(--done) 18%, transparent);
}

.all-done--goal .all-done__badge svg { width: 84px; height: 84px; }

/* ============ 导入：检查结果面板 ============ */
.import-box {
  margin-top: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md);
  border: 1px solid var(--line);
  background: var(--surface-sunk);
  font-size: var(--f-sm);
  line-height: 1.55;
}

.import-box__title {
  font-size: var(--f-sm);
  font-weight: 800;
  color: var(--ink);
}

/* 错误用实色边框 + 淡红底，而不只是改字色：
   这是用户必须逐条处理的内容，要一眼和「提醒」区分开 */
.import-box--bad {
  border-color: color-mix(in srgb, var(--danger) 38%, var(--line));
  background: var(--danger-soft);
}
.import-box--bad .import-box__title { color: var(--danger); }

.import-box--warn {
  border-color: color-mix(in srgb, var(--warn) 38%, var(--line));
  background: var(--warn-soft);
}

.import-list {
  margin: var(--s-2) 0 0;
  padding: 0;
  list-style: none;
}

.import-list li {
  position: relative;
  padding: 5px 0 5px 14px;
  overflow-wrap: anywhere;
  font-weight: 600;
  color: var(--ink-2);
}

.import-list li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 14px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--ink-4);
}

.import-box--bad .import-list li::before { background: var(--danger); }
.import-box--warn .import-list li::before { background: var(--warn); }

.import-list__sub {
  display: block;
  margin-top: 1px;
  font-size: var(--f-xs);
  font-weight: 600;
  color: var(--ink-3);
}

/* ============ 设置页小项 ============ */
.setting {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  min-height: 56px;
  padding: var(--s-2) 0;
}

.setting + .setting { border-top: 1px solid var(--line); }
.setting__main { flex: 1; min-width: 0; }
.setting__name { font-size: var(--f-md); font-weight: 700; }
.setting__desc { margin-top: 2px; font-size: var(--f-xs); color: var(--ink-3); line-height: 1.45; }

/* 开关 */
.switch {
  position: relative;
  flex: 0 0 auto;
  width: 50px;
  height: 30px;
  border-radius: var(--r-pill);
  background: var(--line-strong);
  transition: background var(--t-base) var(--ease);
}

.switch::after {
  content: "";
  position: absolute;
  top: 3px; left: 3px;
  width: 24px; height: 24px;
  border-radius: 50%;
  background: #fff;
  box-shadow: var(--shadow-1);
  transition: transform var(--t-base) var(--ease-back);
}

.switch[aria-checked="true"] { background: var(--accent); }
.switch[aria-checked="true"]::after { transform: translateX(20px); }

/* ============ Toast ============ */
.toast-host {
  position: fixed;
  left: 0; right: 0;
  bottom: calc(var(--tabbar-h) + var(--sa-bottom) + var(--s-4));
  z-index: 80;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-2);
  pointer-events: none;
  padding: 0 var(--s-5);
}

.toast {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  max-width: 100%;
  padding: 10px var(--s-4);
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--ink) 90%, transparent);
  color: var(--bg);
  font-size: var(--f-sm);
  font-weight: 700;
  box-shadow: var(--shadow-pop);
  animation: toast-in 320ms var(--ease-back) both;
}

.toast[data-leaving="1"] { animation: toast-out 240ms var(--ease-in) both; }

@keyframes toast-in {
  from { opacity: 0; transform: translateY(14px) scale(.94); }
  to   { opacity: 1; transform: none; }
}

@keyframes toast-out {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(-6px) scale(.96); }
}

/* ============ 自测面板 ============ */
.testline {
  display: flex;
  align-items: flex-start;
  gap: var(--s-2);
  padding: 6px 0;
  font-size: var(--f-xs);
  font-weight: 600;
  line-height: 1.5;
  font-family: ui-monospace, "SF Mono", "Cascadia Code", Consolas, monospace;
}

.testline + .testline { border-top: 1px solid var(--line); }
.testline__mark { flex: 0 0 auto; font-weight: 800; }
.testline--ok  .testline__mark { color: var(--done); }
.testline--bad .testline__mark { color: var(--danger); }
.testline--bad { color: var(--danger); }

/* ============ 备份提醒 ============ */
/* 用警示色而非危险色：数据此刻还在，是「提醒」不是「出事」。
   同 .storage-warn 的取舍。 */
.backup-nudge {
  border-left: 3px solid var(--warn);
  background: var(--warn-soft);
}

.backup-nudge__head {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font-weight: 800;
  font-size: var(--f-sm);
  color: var(--ink);
}

.backup-nudge__head svg {
  flex: 0 0 auto;
  color: var(--warn);
}

/* ============ 邀请码入口 ============ */
/* 低调的文字样式：拿到码的人找得到，其他人不会误点 */
.invite-link {
  color: var(--ink-3);
  font-weight: 600;
  font-size: var(--f-xs);
}
.invite-link:active { color: var(--accent-ink); }

/* ============ 持久化失败横幅 ============ */
/* 页面顶部常驻。用警示色而非危险色：数据没法保存是硬伤，
   但当下没有丢东西，用红会让人以为已经出事了。 */
.storage-warn {
  position: relative;
  z-index: 40;
  display: flex;
  align-items: flex-start;
  gap: var(--s-2);
  padding: calc(var(--s-2) + env(safe-area-inset-top, 0px) * 0.4) var(--s-4) var(--s-2);
  background: var(--warn-soft);
  color: var(--ink);
  font-size: var(--f-xs);
  font-weight: 600;
  line-height: 1.5;
  border-bottom: 1px solid var(--warn);
}

.storage-warn svg {
  flex: 0 0 auto;
  margin-top: 1px;
  color: var(--warn);
}

.storage-warn b { font-weight: 800; }

/* 横幅出现时顶栏留出位置，避免被压住 */
.storage-warn + .app .topbar {
  padding-top: calc(var(--s-3) + env(safe-area-inset-top, 0px) * 0.2);
}
