/* ============================================================
   食刻 · 样式
   设计原则：大字、大按钮、一屏看完、单手可点。
   宽屏（电脑预览）时自动套一个手机外框，所见即手机所得。
   ============================================================ */

/* ---------- 设计变量 ----------
   浅色是默认值；深色由 <html data-theme="dark"> 触发。
   「跟随系统」由 JS 解析成具体值后再写到 data-theme，
   这样深色配色在 CSS 里只需要写一份，不会出现两处要同步的问题。 */
:root {
  --bg:        #F7F5F2;
  --bg-deep:   #EDE9E3;
  --card:      #FFFFFF;
  --card-2:    #FBF9F7;
  --text:      #1C1B1A;
  --text-2:    #57534E;
  --muted:     #918A82;
  --line:      #EAE5DF;
  --line-2:    #F2EEE9;

  --primary:      #FF6B3D;
  --primary-ink:  #FFFFFF;
  --primary-soft: #FFF0EA;
  --primary-deep: #E85526;

  --good:      #2FA36B;
  --good-soft: #E8F6EF;
  --warn:      #D98A18;
  --warn-soft: #FDF3E2;
  --bad:       #DC4A4A;
  --bad-soft:  #FCEDED;
  --info:      #3B7DD8;
  --info-soft: #EAF2FC;

  --r-sm: 10px;
  --r:    16px;
  --r-lg: 22px;
  --r-full: 999px;

  --shadow-1: 0 1px 2px rgba(28,27,26,.05), 0 2px 8px rgba(28,27,26,.04);
  --shadow-2: 0 4px 14px rgba(28,27,26,.09), 0 1px 3px rgba(28,27,26,.05);
  --shadow-3: 0 18px 50px rgba(28,27,26,.20);

  --tap: 48px;
  --tabbar-h: 62px;

  /* ---------- 半透明的卡片面 ----------
     让背景的纸纹和漂移暖光从卡片底下透上来，整屏才是一张纸。
     注意**不是用 opacity** —— 那会把卡片里的文字一起变淡；
     这里只给背景色一个 alpha（color-mix 是预乘的，和 transparent
     混合只降 alpha、不改颜色）。
     86% 是算出来的：暖光峰值 30%，透过它剩 0.30 × 14% ≈ 4.2%，
     刚好能看出"有暖色在动"；卡片再透明一点文字就开始发灰了。 */
  --card-a:   color-mix(in oklab, var(--card) 86%, transparent);
  --card-2-a: color-mix(in oklab, var(--card-2) 86%, transparent);

  --font: -apple-system, BlinkMacSystemFont, "PingFang SC", "HarmonyOS Sans SC",
          "Noto Sans CJK SC", "Microsoft YaHei", "Segoe UI", Roboto, system-ui, sans-serif;
  --font-num: ui-rounded, "SF Pro Rounded", -apple-system, "PingFang SC", system-ui, sans-serif;

  /* ---------- 背景纹理（两样东西，各管一头）----------
     --tex-grain：细颗粒。铺在 #app 自己的背景上、**不遮罩** → 整张纸都有，
       所以顶栏、底栏铺同一份就能和内容连成一片（噪声是随机的，不需要对齐相位）。
     --tex-dots：错位点阵。留在遮罩层里，是"焦点纹理"：顶部强、往下慢慢化开。
       顶栏在屏幕最上方，正好接着这股强度，所以顶栏也铺它；
       底栏那里它已经化没了，底栏就只铺颗粒 —— 免得出现"栏上有、旁边没有"的断层。 */
  /* 品牌方块的渐变两端 —— 和 tools/gen-icons.mjs 里的 TOP / BOTTOM 是同一组值。
     改一处就得改两处，否则 App 里的 Logo 又和桌面图标对不上了。 */
  --brand-a: #FF9366;
  --brand-b: #F1512A;

  --tex-grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 .5 0 0 0 0 .5 0 0 0 0 .5 0 0 0 .07 0'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)'/%3E%3C/svg%3E");
  --tex-dots: radial-gradient(circle, color-mix(in oklab, var(--text) 20%, transparent) 1px, transparent 1.3px);
}

:root[data-theme="dark"] {
    --bg:        #131211;
    --bg-deep:   #0C0B0B;
    --card:      #1E1D1B;
    --card-2:    #262422;
    --text:      #F4F1ED;
    --text-2:    #C4BEB6;
    --muted:     #8E8880;
    --line:      #302D2A;
    --line-2:    #272522;

    --primary:      #FF7C52;
    --primary-ink:  #1A0F09;
    --primary-soft: #33211A;
    --primary-deep: #FF8F6B;

    --good:      #4CBE87;
    --good-soft: #16281F;
    --warn:      #E0A43F;
    --warn-soft: #2B2317;
    --bad:       #EE6B6B;
    --bad-soft:  #2E1A1A;
    --info:      #6BA4E8;
    --info-soft: #17222E;

    --shadow-1: 0 1px 2px rgba(0,0,0,.4);
    --shadow-2: 0 4px 14px rgba(0,0,0,.45);
    --shadow-3: 0 18px 50px rgba(0,0,0,.6);
}

/* ---------- 基础重置 ---------- */
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0; padding: 0;
  height: 100%;
  /* 永远不许横向滚动。
     必须写在 html 上：根元素的 overflow 会传播给视口，body 上的那条不会。
     为什么需要它：背景那团暖光（#app::after）是个 380px 的层，会贴着
     屏幕边缘走，超出 #app 的那部分原本会把整个文档的 scrollWidth 撑大
     （实测 320 宽时能横拖 50~92px，411 宽时 10~66px，还随弹跳在变）。
     页面一旦能横向拖动，安卓 WebView 在软键盘弹出、输入框获焦时就会
     横向平移/取中视口 —— 表现就是整个界面被切掉左右两边、宽度超出屏幕。
     在这里掐掉之后，无论暖光走到哪儿，视口都不可能被横向拖走。 */
  overflow-x: hidden;
  /* 这里必须和 #app 用同一个颜色。
     iOS 26 起 theme-color 元标签已经失效，状态栏颜色改成取 body 的背景色 ——
     以前这里用的是更深的 --bg-deep，#app 用的是 --bg，于是状态栏和页面
     之间会有一条明显的色带。（#app 是满宽的，body 这个底色在手机上
     本来也只有状态栏那一条和回弹区能看到，改掉没有代价。） */
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
}

body { display: flex; justify-content: center; }

button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; border: none; background: none; }
h1,h2,h3,h4,p { margin: 0; }
a { color: var(--primary); text-decoration: none; }

/* ---------- 外壳 ---------- */
#app {
  width: 100%;
  max-width: 100%;
  min-height: 100dvh;
  /* 颗粒铺在自己的背景上（不遮罩）：整张纸都有它 */
  background-color: var(--bg);
  background-image: var(--tex-grain);
  background-size: 120px 120px;
  display: flex;
  flex-direction: column;
  position: relative;
}

/* ---------- 背景：一整张纸 + 一团漂移暖光 ----------
   分工（这是"整屏是一张纸"的关键）：
     · 颗粒  → 铺在 #app 自己的背景上，不遮罩 → **整张纸都有**，
               顶栏/底栏铺同一份就能和内容连成一片
     · 点阵  → 在 ::before 上，带遮罩 → 焦点纹理：顶部强、往下慢慢化开
               顶栏在屏幕最上面，正好接住这股强度；底栏那里已化没，就不铺
     · 暖光  → 在 ::after 上，在屏幕里弹（位置由 app.js 每帧算，见那边的注释）

   ::before / ::after 都挂在 #app（**不在滚动区里**）——
   相当于"桌面不动、卡片在上面滑"，永远不会出现纹理用到一半断掉的硬边。
   而且 replaceChildren() 只换子元素、不动伪元素，所以一行 JS 都不用写。

   过渡怎么做到自然：遮罩是**一个比屏幕大得多的椭圆**（150% × 125%、
   圆心在屏幕上方外面一点），四段渐变一路衰减 —— 上下左右同时变淡，
   而不是一条水平硬边。实测强度：顶部 100% → 25% 处 88% → 中间 68%
   → 75% 处 28% → 底部 1%。
   （第一版椭圆只有 135% × 78%，屏幕中部只剩 15%，点直接看不见了。） */
#app::before {
  content: '';
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  /* 只有点阵；颗粒已经铺在 #app 自己的背景上了 */
  background-image: var(--tex-dots), var(--tex-dots);
  background-size: 16px 16px, 16px 16px;
  background-position: 0 0, 8px 8px;   /* 第二层错开半格 → 像织物而不是电子表格 */
  /* 遮罩：圆心放在屏幕上方外面一点，椭圆比屏幕大得多 ——
     这样第一屏大部分地方纹理都还看得见，到下面才慢慢化掉。
     （第一版椭圆太小，算下来屏幕中部只剩 15% 强度，点直接看不见了。） */
  -webkit-mask-image: radial-gradient(150% 125% at 50% -22%,
      #000 0%, #000 40%, rgba(0,0,0,.66) 68%, rgba(0,0,0,.24) 86%, transparent 100%);
          mask-image: radial-gradient(150% 125% at 50% -22%,
      #000 0%, #000 40%, rgba(0,0,0,.66) 68%, rgba(0,0,0,.24) 86%, transparent 100%);
}

/* 漂移层：暖光 + **跟着它走的一片点阵**
   这是"底纹跟着暖光波动"的实现方式 —— 把点阵放进这团会动的层里，
   层整体只做 transform（GPU 合成、每帧不重绘），点阵就跟着一起走；
   而且这层会因为 scale 变化和下面那张静态点阵的栅距错开，
   两层互相干涉出缓慢的摩尔纹 —— 看起来就是"波动"。
   （第一版只有一团 340px / 15% 的光，90% 被卡片挡住、15 秒才漂 100px，
     基本看不出在动；现在放大到 460px、26%、10 秒、位移也拉大一倍。）
   位置仍限制在中间那条带里，不钻到顶栏/底栏背后的 blur 区域。 */
#app::after {
  content: '';
  position: absolute; left: 0; top: 0;
  width: 380px; height: 380px;
  z-index: 0; pointer-events: none;
  will-change: transform;
  background-image:
    /* 收得更紧：实心核 + 快速衰减（之前是"一路散到 64%"，看着太散） */
    radial-gradient(circle,
      color-mix(in oklab, var(--primary) 30%, transparent) 0%,
      color-mix(in oklab, var(--primary) 13%, transparent) 36%,
      transparent 62%),
    var(--tex-dots), var(--tex-dots);
  background-size: 100% 100%, 16px 16px, 16px 16px;
  background-position: 0 0, 0 0, 8px 8px;
  /* 点阵只在这团光的范围里显形，边缘柔化 → 像"一片点阵被光照亮" */
  -webkit-mask-image: radial-gradient(circle at 50% 50%, #000 4%, rgba(0,0,0,.45) 40%, transparent 66%);
          mask-image: radial-gradient(circle at 50% 50%, #000 4%, rgba(0,0,0,.45) 40%, transparent 66%);
  /* 位置/缩放/旋转由 app.js 每帧写进 --gt（在屏幕里弹、碰到边缘反弹）。
     下面这串是**没有 JS 时的兜底**：安静地待在偏上一点的位置。 */
  transform: var(--gt, translate3d(20px, 22vh, 0));
}
@media (prefers-reduced-motion: reduce) {
  #app::after { animation: none; }
}

/* ---------- 宽屏：内容收成一列 ----------
   以前只有「又宽又高」（≥720 且 ≥620）才收窄，于是两种宽屏都漏了：
     · 560~719px 宽 —— 小平板、展开后的折叠屏：铺满整宽，没有上限
     · 高度不足 620px 的宽屏 —— 横屏手机、横屏平板、分屏：
       一样铺满，1024px 宽的弹层就是这么来的
   现在只按宽度判断，高度不再参与：屏幕够宽就收成一列。
   （这条写在前面，下面那条「手机外框」同优先级、写在后面，会盖掉它的 max-width。） */
@media (min-width: 560px) {
  body { align-items: center; }
  #app {
    max-width: 460px;
    min-height: 0;
    height: 100dvh;
    /* 顺手把暖光也裁在这一列里，不让它飘到旁边的空白上。
       弹层在这里已经改成 absolute（见下面），所以不会被裁坏。 */
    overflow: hidden;
  }

  /* 内容收窄之后，弹层和 toast 必须一起收进 #app。
     它们默认是 position: fixed —— 贴着视口铺满整宽，不改的话
     在宽屏上会比 #app 这条列宽出一大截，正好是这次要修的那种毛病。
     弹层本身在下面那条规则里改成 absolute。 */
  .sheet-host, .toast-host { position: absolute; }
  #sheet-host { position: absolute; inset: 0; pointer-events: none; }
  #sheet-host > * { pointer-events: auto; }

  /* 宽屏上把滚动条还回来（手机上永远看不到）。
     选择器带上 #app 是为了压过上面那条通用的「隐藏滚动条」规则 ——
     媒体查询写在前面，同优先级的规则会被后面的盖掉。 */
  #app .app-main { scrollbar-width: thin; }
  #app .app-main::-webkit-scrollbar { display: block; width: 7px; }
  #app .app-main::-webkit-scrollbar-thumb { background: var(--line); border-radius: 99px; }
}

/* 电脑预览：再套一圈手机外框，所见即手机所得。
   限定「真有鼠标」的设备 —— 否则平板上也会长出一圈假装是手机的边框。 */
@media (min-width: 720px) and (min-height: 620px) and (hover: hover) and (pointer: fine) {
  body {
    padding: 24px 0;
    background:
      radial-gradient(1200px 600px at 50% -10%, color-mix(in oklab, var(--primary) 12%, var(--bg-deep)), var(--bg-deep));
  }
  #app {
    max-width: 412px;
    height: min(880px, calc(100dvh - 48px));
    border-radius: 34px;
    box-shadow: var(--shadow-3), 0 0 0 10px color-mix(in oklab, var(--text) 88%, transparent), 0 0 0 11px color-mix(in oklab, var(--text) 60%, transparent);
    border: 1px solid var(--line);
  }
}

/* ---------- 顶栏 ---------- */
.app-header {
  position: sticky; top: 0; z-index: 20;
  padding-top: max(env(safe-area-inset-top), 10px);
  padding-bottom: 10px;
  padding-left: max(env(safe-area-inset-left), 18px);
  padding-right: max(env(safe-area-inset-right), 18px);
  /* 顶栏也铺同一份纹理（点阵 + 颗粒），和它上方的屏幕边缘连成一张纸。
     点阵铺在顶栏**自己**的背景上，所以它是清晰的 ——
     如果只靠背后透出来，会被这一层的 blur(14px) 糊掉。 */
  background-color: color-mix(in oklab, var(--bg) 88%, transparent);
  background-image: var(--tex-dots), var(--tex-dots), var(--tex-grain);
  background-size: 16px 16px, 16px 16px, 120px 120px;
  background-position: 0 0, 8px 8px, 0 0;
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color .2s;
}
.app-header.scrolled { border-bottom-color: var(--line); }

.app-header .row { display: flex; align-items: center; gap: 10px; min-height: 40px; }
.app-header .head-text { flex: 1 1 auto; min-width: 0; }
.app-title { font-size: 22px; font-weight: 760; letter-spacing: -.02em; }
.app-sub { font-size: 13px; color: var(--muted); margin-top: 1px; }
/* 换页时标题轻轻动一下。
   同样**不带透明度** —— 标题闪到 35% 也是闪（整页只剩它是半透明的就更显眼）。 */
.app-header .head-text.swap { animation: headSwap .3s cubic-bezier(.22,1,.36,1); }
@keyframes headSwap { from { transform: translateY(-5px); } }

.icon-btn {
  width: 40px; height: 40px; flex: 0 0 40px;
  display: grid; place-items: center;
  border-radius: var(--r-full);
  color: var(--text-2);
  background: var(--card);
  box-shadow: var(--shadow-1);
  transition: transform .12s, background .15s;
}
.icon-btn:active { transform: scale(.93); }

/* ---------- 主区域 ---------- */
.app-main {
  flex: 1 1 auto;
  overflow-y: auto;
  overflow-x: hidden;              /* 换页那 14px 横移不许变出横向滚动 */
  -webkit-overflow-scrolling: touch;
  position: relative;
  /* 压在两张背景伪元素（z-index: 0）之上：
     伪元素在 #app 上，内容在它们上面，卡片才不会被暖光染色 */
  z-index: 1;
  padding-top: 4px;
  padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 92px);
  padding-left: max(env(safe-area-inset-left), 18px);
  padding-right: max(env(safe-area-inset-right), 18px);
}

/* ---------- 滚动条 ----------
   手机上原生 App 是不显示滚动条的，网页默认那条灰条子很出戏，
   所以所有滚动容器统一藏起来。桌面预览时另外把 .app-main 放开，
   否则在电脑上完全看不出这一页还能不能往下滚。 */
.app-main, .sheet-body, .onb, .shots, .no-scrollbar {
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.app-main::-webkit-scrollbar,
.sheet-body::-webkit-scrollbar,
.onb::-webkit-scrollbar,
.shots::-webkit-scrollbar,
.no-scrollbar::-webkit-scrollbar { width: 0; height: 0; display: none; }

.section { margin-top: 22px; }
.section-head {
  display: flex; align-items: baseline; justify-content: space-between;
  margin: 0 2px 10px;
}
.section-title { font-size: 15px; font-weight: 680; color: var(--text-2); letter-spacing: .01em; }
.section-action { font-size: 13px; color: var(--primary); font-weight: 600; }

/* 没有悬浮按钮的页面（比如设置），底部不用留那么多空 */
#app.no-fab .app-main { padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 28px); }

/* ---------- 卡片 ---------- */
.card {
  background: var(--card-a);
  border-radius: var(--r-lg);
  padding: 18px;
  box-shadow: var(--shadow-1);
  border: 1px solid var(--line-2);
}
.card + .card { margin-top: 12px; }
.card.tight { padding: 14px; }
/* 整张卡可点（比如「我的」页的进度卡）：看不出是按钮，但按得下去 */
.card-tap { display: block; width: 100%; text-align: left; color: inherit; transition: transform .14s; }
.card-tap:active { transform: scale(.988); }

/* ---------- 今日大卡 ---------- */
.hero {
  background: linear-gradient(155deg, var(--card-a) 0%, var(--card-2-a) 100%);
  border-radius: var(--r-lg);
  padding: 20px;
  box-shadow: var(--shadow-2);
  border: 1px solid var(--line-2);
  position: relative;
  overflow: hidden;
}
/* 这里原本钉着一团 210px 的橙色光斑（最早那版就有的装饰）。
   现在背景已经有一团会走、会弹的暖光了，再钉一盏固定的只会互相打架，
   所以删掉 —— hero 直接借背景那团光（卡片 86% 半透明，透得上来的）。 */
.hero-top { display: flex; align-items: center; gap: 20px; }

.ring { position: relative; flex: 0 0 140px; width: 140px; height: 140px; }
.ring svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
.ring .track { stroke: var(--line); }
/* 圆环本身不带 animation / transition：
   「从满圈往下收」和中间那个数字是同一条曲线、同一个时长，
   由 today.js 里的 animateValue 一起驱动。CSS 里再放一个动画会插队
   （动画优先级高于行内 style），反而把两者的拍子拆散。 */
.ring .bar { stroke: var(--primary); stroke-linecap: round; transition: stroke .3s; }
.ring .bar.over { stroke: var(--bad); }
.ring-center {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px;
}
.ring-num { font-family: var(--font-num); font-size: 30px; font-weight: 780; letter-spacing: -.03em; line-height: 1.05; }
.ring-label { font-size: 11.5px; color: var(--muted); letter-spacing: .02em; }
.ring-sub { font-size: 11px; color: var(--muted); }

.hero-stats { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 10px; }
.hero-stat { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.hero-stat .k { font-size: 13px; color: var(--muted); }
.hero-stat .v { font-family: var(--font-num); font-size: 17px; font-weight: 700; letter-spacing: -.01em; }
/* 滚动的数字要用等宽字形，否则每换一位整行宽度都会跳一下 */
.ring-num, .hero-stat .v, .mono-num { font-variant-numeric: tabular-nums; }
.hero-stat .v small { font-size: 11.5px; font-weight: 600; color: var(--muted); margin-left: 2px; }

.macro-bar { display: flex; gap: 4px; height: 6px; border-radius: 99px; overflow: hidden; background: var(--line); margin-top: 12px; }
/* 条子不做动画：和数字一起跳来跳去看着累，直接显示当前值就好。
   （以前这里有个从 0 展开的 barGrow，撤掉了。） */
.macro-bar i { display: block; height: 100%; border-radius: 99px; }
.macro-legend { display: flex; gap: 14px; margin-top: 9px; flex-wrap: wrap; }
.macro-legend span { font-size: 12px; color: var(--muted); display: inline-flex; align-items: center; gap: 5px; }
.macro-legend i { width: 8px; height: 8px; border-radius: 3px; display: inline-block; }

/* ---------- 大按钮 ---------- */
.btn {
  min-height: var(--tap);
  padding: 0 20px;
  border-radius: var(--r-full);
  font-size: 16px; font-weight: 650;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  background: var(--card-2);
  color: var(--text);
  transition: transform .12s, filter .15s, background .15s;
  border: 1px solid var(--line);
  white-space: nowrap;
}
.btn:active { transform: scale(.965); }
.btn:disabled { opacity: .45; pointer-events: none; }
/* 穿透免疫窗口里暂时不能点的按钮。
   只变灰、**不**去掉指针事件 —— 让它继续"占住"这一下点击，
   免得穿透点击溜到后面的元素上去（比如把备注框的焦点顶掉）。 */
.btn-locked { opacity: .45; }
.btn-primary { background: var(--primary); color: var(--primary-ink); border-color: transparent; box-shadow: 0 4px 14px color-mix(in oklab, var(--primary) 32%, transparent); }
.btn-soft { background: var(--primary-soft); color: var(--primary-deep); border-color: transparent; }
.btn-ghost { background: transparent; border-color: var(--line); color: var(--text-2); }
.btn-danger { background: var(--bad-soft); color: var(--bad); border-color: transparent; }
.btn-block { width: 100%; }
.btn-lg { min-height: 56px; font-size: 17px; }
.btn-sm { min-height: 36px; padding: 0 14px; font-size: 13.5px; border-radius: var(--r-full); }

.btn-row { display: flex; gap: 10px; }
.btn-row > * { flex: 1 1 0; }

/* 趋势页的「到目标还有多久 + 这段时间的回顾」合并卡 */
.insight-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 14px;
}
.summary-badge {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; font-weight: 700; letter-spacing: .02em;
  padding: 3px 9px; border-radius: 999px;
  background: var(--bg-deep); color: var(--muted);
  white-space: nowrap;
}
.summary-badge.ai { background: var(--primary-soft); color: var(--primary-deep); }

.ig-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.ig-k { font-size: 12px; color: var(--muted); font-weight: 600; }
.ig-v {
  font-family: var(--font-num); font-size: 22px; font-weight: 770;
  letter-spacing: -.025em; margin-top: 3px;
}
.ig-note { font-size: 12px; color: var(--muted); margin-top: 10px; }
.ig-empty {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  font-size: 13.5px; color: var(--muted); line-height: 1.6;
}
.ig-link { font-size: 13px; font-weight: 650; color: var(--primary); }

.insight-divider { height: 1px; background: var(--line-2); margin: 15px 0; }

.summary-text { display: flex; flex-direction: column; gap: 8px; }
.summary-text p { font-size: 14.5px; line-height: 1.68; color: var(--text-2); }

/* 体重卡：有记录时整张卡可点，右侧给一个箭头 */
.weight-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; text-align: left;
  padding: 0; background: none; border: none; color: inherit;
  transition: transform .12s;
}
.weight-row:active { transform: scale(.985); }

/* ---------- 底部导航 ---------- */
.tabbar {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 30;
  height: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  display: grid; grid-template-columns: repeat(3, 1fr);
  align-items: center;
  /* 底栏只铺颗粒：点阵在屏幕底部已经化没了，
     这里再铺点阵会出现"栏上有、紧挨着的上面没有"的断层 */
  background-color: color-mix(in oklab, var(--bg) 86%, transparent);
  background-image: var(--tex-grain);
  background-size: 120px 120px;
  backdrop-filter: saturate(180%) blur(18px);
  -webkit-backdrop-filter: saturate(180%) blur(18px);
  border-top: 1px solid var(--line);
}
.tab {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  height: 100%; color: var(--muted); font-size: 11px; font-weight: 600;
  transition: color .15s;
}
.tab svg { width: 23px; height: 23px; }
.tab[aria-current="page"] { color: var(--primary); }
.tab:active { transform: scale(.94); }

/* 加号从导航栏里独立出来，浮在右下角。
   这样导航栏三项各自拥有均分的空间，两者不再互相挤压。 */
.fab {
  position: absolute;
  right: 18px;
  bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 18px);
  z-index: 40;
  width: 58px; height: 58px;
  border-radius: var(--r-full);
  background: var(--primary); color: var(--primary-ink);
  display: grid; place-items: center;
  box-shadow: 0 8px 22px color-mix(in oklab, var(--primary) 45%, transparent);
  transition: transform .14s;
}
.fab:active { transform: scale(.92); }
.fab svg { width: 27px; height: 27px; }

/* ---------- 软键盘弹起时 ----------
   安卓 WebView 是 adjustResize：键盘一开视口就变矮，而这一条和悬浮按钮是
   absolute bottom:0 —— 它们会被顶到键盘正上方，既挡住正在输入的地方，
   也不像原生 App（原生在键盘弹起时会让底部这条让位）。
   这个类由 app.js 在「有输入框聚焦 **且视口真的变矮了**」时加上：
   电脑预览没有软键盘、视口不会变矮，所以那边一切照旧。 */
#app.kb-open .tabbar,
#app.kb-open .fab { display: none; }
/* 导航收起来了就不用再给它留那么大的下边距，输入框才滚得到屏幕中间 */
#app.kb-open .app-main { padding-bottom: calc(24px + env(safe-area-inset-bottom)); }

/* ---------- 输入 ---------- */
.field { display: block; margin-top: 14px; }
.field:first-child { margin-top: 0; }
.label { display: block; font-size: 13.5px; font-weight: 620; color: var(--text-2); margin-bottom: 7px; }
.label .hint { font-weight: 500; color: var(--muted); font-size: 12px; margin-left: 6px; }

.input, .select, .textarea {
  width: 100%;
  min-height: var(--tap);
  padding: 12px 14px;
  border-radius: var(--r);
  background: var(--card-2);
  border: 1.5px solid var(--line);
  outline: none;
  transition: border-color .15s, background .15s;
  -webkit-appearance: none; appearance: none;
}
.textarea { min-height: 84px; resize: none; line-height: 1.55; }
.input:focus, .select:focus, .textarea:focus { border-color: var(--primary); background: var(--card); }
.input::placeholder, .textarea::placeholder { color: var(--muted); }
.input-num { font-family: var(--font-num); font-size: 20px; font-weight: 700; letter-spacing: -.01em; text-align: center; }
/* 右侧带按钮的输入框（比如 API Key 的「显示」），别让文字压到按钮上 */
.input.with-action { padding-right: 76px; }
.input.with-action.reveal { padding-right: 84px; }
.select {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23918A82' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
  background-repeat: no-repeat; background-position: right 14px center; background-size: 18px;
  padding-right: 40px;
}
.field-row { display: flex; gap: 10px; }
.field-row > * { flex: 1 1 0; min-width: 0; }

/* 分段选择器：比下拉框更适合手机，一眼看全 */
.segmented { display: flex; gap: 6px; padding: 4px; background: var(--bg-deep); border-radius: var(--r); }
.segmented button {
  flex: 1 1 0; min-height: 40px; border-radius: 11px;
  font-size: 14px; font-weight: 620; color: var(--text-2);
  transition: background .15s, color .15s, box-shadow .15s;
  padding: 0 6px;
}
.segmented button[aria-pressed="true"] { background: var(--card); color: var(--text); box-shadow: var(--shadow-1); }
.segmented.stack { flex-direction: column; }
.segmented.stack button { justify-content: flex-start; text-align: left; padding: 0 14px; }
.segmented.stack button small { display: block; font-weight: 500; color: var(--muted); font-size: 12px; }
.segmented.stack button[aria-pressed="true"] small { color: var(--text-2); }

/* ---------- 餐卡 ---------- */
.meal {
  display: flex; gap: 13px; align-items: center;
  padding: 12px; border-radius: var(--r);
  background: var(--card-a); border: 1px solid var(--line-2);
  box-shadow: var(--shadow-1);
  transition: transform .12s;
  width: 100%; text-align: left;
}
.meal:active { transform: scale(.985); }
.meal + .meal { margin-top: 10px; }
.meal-thumb {
  position: relative;          /* 「N 张」角标要贴着它定位 */
  width: 62px; height: 62px; flex: 0 0 62px;
  border-radius: 13px; overflow: hidden; background: var(--bg-deep);
  display: grid; place-items: center; color: var(--muted);
  font-size: 22px;
}
.meal-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.meal-body { flex: 1 1 auto; min-width: 0; }
.meal-slot { font-size: 12px; color: var(--muted); font-weight: 600; letter-spacing: .02em; }
.meal-title {
  font-size: 15px; font-weight: 620; margin-top: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.meal-sub { font-size: 12.5px; color: var(--muted); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.meal-kcal { flex: 0 0 auto; text-align: right; font-family: var(--font-num); font-weight: 740; font-size: 18px; letter-spacing: -.02em; }
.meal-kcal small { display: block; font-size: 10.5px; font-weight: 600; color: var(--muted); letter-spacing: .02em; }

/* ---------- 提示条 ---------- */
.banner {
  display: flex; align-items: center; gap: 11px;
  padding: 13px 15px; border-radius: var(--r);
  font-size: 14px; font-weight: 560;
  margin-top: 12px;
}
.banner .btn { flex: 0 0 auto; }
.banner-info { background: var(--info-soft); color: var(--info); }
.banner-warn { background: var(--warn-soft); color: var(--warn); }
.banner-bad  { background: var(--bad-soft);  color: var(--bad); }
.banner-good { background: var(--good-soft); color: var(--good); }
.banner .txt { flex: 1 1 auto; line-height: 1.45; white-space: pre-wrap; }
/* 横幅右侧那个小小的「稍后再说」—— 比常规 icon-btn 小一圈，免得挤 */
.banner-x {
  flex: 0 0 auto; width: 30px; height: 30px; border-radius: var(--r-full);
  display: grid; place-items: center; opacity: .65;
  transition: opacity .15s, background .15s;
}
.banner-x:active { opacity: 1; background: rgba(0,0,0,.08); }
.banner .txt b { font-weight: 700; }

/* ---------- 标签 ---------- */
.chips { display: flex; gap: 8px; flex-wrap: wrap; }
.chip {
  padding: 7px 13px; border-radius: var(--r-full);
  background: var(--bg-deep); color: var(--text-2);
  font-size: 13px; font-weight: 600;
  border: 1.5px solid transparent;
  transition: background .15s, color .15s, border-color .15s;
  min-height: 36px; display: inline-flex; align-items: center; gap: 6px;
}
.chip[aria-pressed="true"] { background: var(--primary-soft); color: var(--primary-deep); border-color: color-mix(in oklab, var(--primary) 40%, transparent); }
.chip:active { transform: scale(.96); }

/* ---------- 列表行 ---------- */
.list { background: var(--card-a); border-radius: var(--r-lg); overflow: hidden; border: 1px solid var(--line-2); box-shadow: var(--shadow-1); }
.list-row {
  display: flex; align-items: center; gap: 13px;
  padding: 14px 16px; min-height: 58px; width: 100%; text-align: left;
  border-bottom: 1px solid var(--line-2);
  transition: background .12s;
}
.list-row:last-child { border-bottom: none; }
.list-row:active { background: var(--card-2); }
.list-row .lr-icon { width: 34px; height: 34px; flex: 0 0 34px; border-radius: 10px; display: grid; place-items: center; background: var(--bg-deep); color: var(--text-2); }
.list-row .lr-body { flex: 1 1 auto; min-width: 0; }
.list-row .lr-title { font-size: 15px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.list-row .lr-sub { font-size: 12.5px; color: var(--muted); margin-top: 1px; }
.list-row .lr-value { font-size: 14px; color: var(--muted); font-weight: 600; flex: 0 0 auto; max-width: 45%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.list-row .lr-arrow { color: var(--muted); flex: 0 0 auto; display: grid; place-items: center; }

/* ---------- 统计格 ---------- */
/* minmax(0, 1fr) 而不是 1fr：1fr 的最小值是 auto（内容宽度），
   于是「1,493」这种宽数字 + kcal 单位会把整条轨道撑开，三栏一起顶出屏幕 ——
   实测 320 宽时第三栏被切掉 16px（趋势页 / 记一餐结果页 / 引导结果页都会）。
   minmax(0, …) 让轨道可以缩到比内容更窄，再由下面的换行兜住。 */
.stat-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.stat-grid.g3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.stat {
  background: var(--card-a); border-radius: var(--r); padding: 14px;
  border: 1px solid var(--line-2); box-shadow: var(--shadow-1);
}
.stat .k { font-size: 12px; color: var(--muted); font-weight: 600; }
.stat .v { font-family: var(--font-num); font-size: 22px; font-weight: 760; letter-spacing: -.025em; margin-top: 3px; }
.stat .v small { font-size: 12px; font-weight: 600; color: var(--muted); margin-left: 2px; white-space: nowrap; }
.stat .d { font-size: 11.5px; color: var(--muted); margin-top: 2px; }
/* 极窄的屏上允许在必要处断行 —— 「1,493 kcal」宁可折成两行，也不要顶出格子。
   overflow-wrap 只在真的放不下时才断，正常手机上完全看不出来。 */
.stat .k, .stat .v, .stat .d { overflow-wrap: anywhere; }

/* 320px 这一档：三栏并排本来就没有余量，把内边距和字号各收一点，
   让「1,493」还是能完整显示在一行里（不收的话它会折成「1,4 / 93」）。 */
@media (max-width: 340px) {
  .stat-grid, .stat-grid.g3 { gap: 8px; }
  .stat { padding: 11px; }
  .stat .k, .stat .d { font-size: 11px; }
  .stat .v { font-size: 18px; }
}

/* ---------- 空状态 ---------- */
.empty { text-align: center; padding: 34px 20px; color: var(--muted); }
.empty .em-ico { font-size: 34px; margin-bottom: 10px; opacity: .8; }
.empty .em-t { font-size: 15px; font-weight: 620; color: var(--text-2); }
.empty .em-s { font-size: 13px; margin-top: 5px; line-height: 1.55; }

/* ---------- 骨架屏 ---------- */
.skeleton {
  background: linear-gradient(90deg, var(--line-2) 25%, var(--bg-deep) 50%, var(--line-2) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.4s infinite linear;
  border-radius: 8px;
  color: transparent !important;
}
@keyframes shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }

/* ---------- 底部弹层 ---------- */
#sheet-host { position: fixed; inset: 0; z-index: 60; pointer-events: none; }
#sheet-host > * { pointer-events: auto; }

.sheet-backdrop {
  position: fixed; inset: 0; background: rgba(18,15,13,.42);
  opacity: 0;
  transition: opacity .2s ease-in;   /* 关：跟着面板一起快速退场 */
  /* 这里刻意不用 backdrop-filter。
     实测在安卓 WebView 上，带 backdrop-filter 的元素移除之后，那一层
     模糊偶尔不会跟着重绘 —— 弹出层关掉了，页面却一直是花的，要再点
     一下屏幕才恢复。42% 的暗色遮罩已经足够把内容压下去，不值当为这点
     装饰效果冒这个险；顺带也省了一层 GPU 合成。 */
}
.sheet-backdrop.show { opacity: 1; transition: opacity .3s ease-out; }

.sheet {
  position: fixed; left: 0; right: 0; bottom: 0;
  /* 用 % 而不是 dvh：手机上 fixed 的包含块就是视口，两者相等；
     但宽屏时弹层改成 absolute、包含块是 #app（那条 460px 的列），
     这时 92dvh 会按整个视口算 —— 比 #app 还高的弹层顶部会被裁掉，
     连标题都看不见。% 两种情况都跟着包含块走。 */
  max-height: 92%;
  background: var(--bg);
  border-radius: 26px 26px 0 0;
  box-shadow: var(--shadow-3);
  display: flex; flex-direction: column;
  transform: translateY(102%);
  /* 关闭用的曲线：先慢后快，像被"甩"下去。
     以前打开关闭共用一个「先快后慢」的曲线 —— 那个适合弹出（冲上来再稳稳停住），
     但用在收起上就是「瞬间全速下落再慢慢到位」，弹层越高越别扭。 */
  transition: transform .26s cubic-bezier(.4, 0, 1, 1);
  padding-bottom: env(safe-area-inset-bottom);
}
.sheet.show {
  transform: translateY(0);
  transition: transform .34s cubic-bezier(.22, 1, .36, 1);
}
/* 宽屏：弹层跟着 #app 那条列走（条件必须和上面「宽屏收成一列」一致，
   否则就会出现「#app 是 460px 的列、弹层却是整屏宽」的错位） */
@media (min-width: 560px) {
  #sheet-host { position: absolute; }
  .sheet-backdrop { position: absolute; }
  .sheet { position: absolute; }
}
@media (min-width: 720px) and (min-height: 620px) and (hover: hover) and (pointer: fine) {
  .sheet { border-radius: 22px; }
}

.sheet-grip { padding: 9px 0 3px; display: grid; place-items: center; flex: 0 0 auto; }
.sheet-grip i { width: 38px; height: 4.5px; border-radius: 99px; background: var(--line); display: block; }
.sheet-head { padding: 4px 20px 10px; flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.sheet-title { font-size: 19px; font-weight: 720; letter-spacing: -.02em; }
.sheet-body { padding: 4px 20px 20px; overflow-y: auto; -webkit-overflow-scrolling: touch; flex: 1 1 auto; }
.sheet-foot {
  padding: 12px 20px calc(16px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line);
  background: var(--bg);
  flex: 0 0 auto;
}

/* ---------- 加载 ---------- */
.spinner {
  width: 18px; height: 18px; border-radius: 50%;
  border: 2.4px solid color-mix(in oklab, currentColor 26%, transparent);
  border-top-color: currentColor;
  animation: spin .7s linear infinite;
  display: inline-block;
}
@keyframes spin { to { transform: rotate(360deg); } }
.loading-block { display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 38px 20px; color: var(--muted); font-size: 14px; }
.loading-block .spinner { width: 26px; height: 26px; border-width: 3px; color: var(--primary); }
.loading-block .lb-t { font-weight: 620; color: var(--text-2); font-size: 15px; }
.loading-block .lb-s { font-size: 12.5px; }

/* ---------- toast ---------- */
.toast-host {
  position: fixed; left: 0; right: 0; bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 96px);
  z-index: 90; display: flex; flex-direction: column; align-items: center; gap: 8px;
  pointer-events: none; padding: 0 20px;
}
.toast {
  max-width: 340px;
  padding: 12px 18px; border-radius: var(--r-full);
  background: color-mix(in oklab, var(--text) 92%, transparent);
  color: var(--bg); font-size: 14px; font-weight: 600;
  box-shadow: var(--shadow-2);
  animation: toastIn .28s cubic-bezier(.22,1,.36,1);
  text-align: center;
}
.toast.out { animation: toastOut .22s forwards; }
@keyframes toastIn { from { opacity: 0; transform: translateY(14px) scale(.94); } }
@keyframes toastOut { to { opacity: 0; transform: translateY(8px) scale(.96); } }

/* ---------- 图片预览 / 识别结果 ---------- */
.photo-wrap {
  border-radius: var(--r-lg); overflow: hidden; background: var(--bg-deep);
  aspect-ratio: 4 / 3; display: grid; place-items: center; position: relative;
}
.photo-wrap img { width: 100%; height: 100%; object-fit: cover; display: block; }
.photo-wrap .ph {
  display: flex; flex-direction: column; align-items: center; gap: 9px;
  color: var(--muted); font-size: 13.5px;
}
.photo-wrap .ph .em-ico { font-size: 30px; }

.dish {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 12px 0; border-bottom: 1px solid var(--line-2);
}
.dish:last-child { border-bottom: none; }
/* 一餐多张照片的缩略图条 */
.shots { display: flex; gap: 9px; overflow-x: auto; padding: 2px 0 4px; }
.shots-view { flex-wrap: wrap; }
.shot {
  position: relative; flex: 0 0 auto;
  width: 88px; height: 88px; border-radius: 13px; overflow: hidden;
  background: var(--card-2);
}
.shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.shot-x {
  position: absolute; top: 4px; right: 4px;
  width: 22px; height: 22px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(18,15,13,.72); color: #fff;
}
.shot-x:active { transform: scale(.9); }
.shot-add {
  display: grid; place-items: center;
  border: 1.5px dashed var(--line); color: var(--muted);
  background: none;
}
.shot-add:active { background: var(--card-2); }
/* 还没压好的缩略图：盖一层半透明 + 转圈，让人知道它在忙、不是卡住了 */
.shot-busy {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  background: rgba(18,15,13,.42); color: #fff;
}
.shot-busy .spinner { width: 20px; height: 20px; }
/* 今日列表里的「N 张」角标 */
.meal-thumb-n {
  position: absolute; right: 3px; bottom: 3px;
  min-width: 17px; height: 17px; padding: 0 4px; border-radius: 9px;
  display: grid; place-items: center;
  background: rgba(18,15,13,.78); color: #fff;
  font-size: 10.5px; font-weight: 700; line-height: 1;
}

/* 时长步进器：[−] 30 分钟 [+] */
.stepper {
  display: flex; align-items: center; gap: 10px;
  background: var(--card-2); border: 1.5px solid var(--line);
  border-radius: var(--r); padding: 6px;
}
.step-btn {
  width: 46px; height: 42px; flex: 0 0 46px;
  display: grid; place-items: center;
  border-radius: calc(var(--r) - 4px);
  background: var(--card); color: var(--text);
  font-size: 24px; font-weight: 700; line-height: 1;
  transition: transform .12s, background .15s;
}
.step-btn:active { transform: scale(.92); background: var(--primary-soft); color: var(--primary-deep); }
.step-val {
  flex: 1 1 auto; text-align: center;
  font-family: var(--font-num); font-size: 22px; font-weight: 760; letter-spacing: -.02em;
}
.step-val small { font-size: 13px; font-weight: 600; color: var(--muted); margin-left: 3px; }

/* 可点的菜行：整行是个按钮，右边给个箭头 */
.dish-tap {
  width: 100%; text-align: left; background: none; border: none;
  color: inherit; transition: opacity .12s;
}
.dish-tap:active { opacity: .6; }
.dish-tap .dish-body { padding-right: 2px; }
.dish-body { flex: 1 1 auto; min-width: 0; }
.dish-name { font-size: 15px; font-weight: 620; display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.dish-meta { font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.dish-kcal { font-family: var(--font-num); font-weight: 720; font-size: 16px; flex: 0 0 auto; }
.conf { font-size: 10.5px; font-weight: 700; padding: 2px 6px; border-radius: 5px; letter-spacing: .02em; }
.conf-low { background: var(--warn-soft); color: var(--warn); }
.conf-mid { background: var(--info-soft); color: var(--info); }
.conf-high { background: var(--good-soft); color: var(--good); }

/* 建议卡 */
.advice {
  border-radius: var(--r-lg); padding: 17px;
  background: linear-gradient(160deg, var(--primary-soft), color-mix(in oklab, var(--primary-soft) 45%, var(--card)));
  border: 1px solid color-mix(in oklab, var(--primary) 22%, transparent);
}
/* 程序算出来的「超了/还有余量」结论，放在建议卡最上面 */
.verdict {
  margin-top: 11px; padding: 11px 14px; border-radius: 13px;
  font-size: 14px; line-height: 1.55;
}
.verdict b { font-family: var(--font-num); font-weight: 780; font-size: 15px; }
.verdict-ok   { background: color-mix(in oklab, var(--good) 16%, var(--card)); color: var(--good); }
.verdict-over { background: color-mix(in oklab, var(--bad) 16%, var(--card));  color: var(--bad); }

.advice-head { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 720; color: var(--primary-deep); letter-spacing: .02em; }
.advice-headline { font-size: 17px; font-weight: 700; margin-top: 9px; line-height: 1.45; letter-spacing: -.01em; }
.advice-list { margin: 11px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 8px; }
.advice-list li { display: flex; gap: 9px; font-size: 14px; line-height: 1.5; color: var(--text-2); }
.advice-list li .n {
  flex: 0 0 20px; width: 20px; height: 20px; border-radius: 50%;
  background: color-mix(in oklab, var(--primary) 20%, transparent); color: var(--primary-deep);
  font-size: 11.5px; font-weight: 700; display: grid; place-items: center; margin-top: 1px;
}
.advice-kcal {
  margin-top: 13px; padding-top: 12px;
  border-top: 1px dashed color-mix(in oklab, var(--primary) 30%, transparent);
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
}
.advice-kcal .k { font-size: 13px; color: var(--text-2); font-weight: 600; }
.advice-kcal .v { font-family: var(--font-num); font-size: 21px; font-weight: 780; letter-spacing: -.02em; color: var(--primary-deep); }

/* 对比模式表格 */
.cmp { display: flex; flex-direction: column; gap: 10px; }
.cmp-row { background: var(--card-a); border-radius: var(--r); padding: 13px 15px; border: 1px solid var(--line-2); box-shadow: var(--shadow-1); }
.cmp-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.cmp-name { font-size: 14px; font-weight: 680; }
.cmp-kcal { font-family: var(--font-num); font-size: 19px; font-weight: 760; letter-spacing: -.02em; }
.cmp-dishes { font-size: 12.5px; color: var(--muted); margin-top: 5px; line-height: 1.5; }
.cmp-best { border-color: color-mix(in oklab, var(--good) 45%, transparent); background: color-mix(in oklab, var(--good-soft) 60%, var(--card)); }

/* ---------- 图表 ---------- */
.chart { width: 100%; display: block; overflow: visible; }
/* 图表会横向拉伸填满屏宽，让线宽保持不变，不然会被拉粗 */
.chart .line-main, .chart .line-avg, .chart .grid-line,
.chart .axis-line, .chart .target-line, .chart .area-main { vector-effect: non-scaling-stroke; }
.chart text { font-family: var(--font); fill: var(--muted); font-size: 10px; font-weight: 600; }
.chart .grid-line { stroke: var(--line-2); stroke-width: 1; }
.chart .axis-line { stroke: var(--line); stroke-width: 1; }
.chart .line-main { fill: none; stroke: var(--primary); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.chart .line-avg { fill: none; stroke: var(--info); stroke-width: 2; stroke-dasharray: 5 4; stroke-linecap: round; }
.chart .area-main { fill: color-mix(in oklab, var(--primary) 13%, transparent); }
.chart .dot-main { fill: var(--primary); }
.chart .dot-last { fill: var(--primary); stroke: var(--card); stroke-width: 2.5; }
.chart .bar-pos { fill: var(--good); }
.chart .bar-neg { fill: var(--bad); }
.chart .bar-zero { fill: var(--line); }
.chart .target-line { stroke: var(--muted); stroke-width: 1.4; stroke-dasharray: 4 4; }
.chart .lbl { fill: var(--muted); font-size: 9.5px; }

/* 折线图的入场：主线由 charts.js 用 dashoffset 画出来（0.9 秒），
   面积、均线、点随后淡入 —— 一层一层来，但总时长压在一秒出头，不拖沓。 */
.chart .area-main { animation: chartFade .5s ease-out .25s backwards; }
.chart .line-avg { animation: chartFade .5s ease-out .5s backwards; }
.chart .dot-main, .chart .dot-last { animation: chartFade .32s ease-out .68s backwards; }
@keyframes chartFade { from { opacity: 0; } }

.legend { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 10px; }
.legend span { font-size: 12px; color: var(--muted); display: inline-flex; align-items: center; gap: 6px; }
.legend i { width: 14px; height: 3px; border-radius: 2px; display: inline-block; }

/* ---------- 引导页 ---------- */
/* 高度跟着外壳走，不要用 100dvh —— 在电脑预览的手机外框里会被撑出界 */
.onb {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 26px 22px calc(26px + env(safe-area-inset-bottom));
}
/* 内层要能撑开，底部的按钮才会被 margin-top:auto 推到最下面 */
.onb-inner { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }

.onb-top { padding-top: env(safe-area-inset-top); }
.onb-logo { display: flex; align-items: center; gap: 11px; margin-bottom: 26px; }
/* 这一块要和桌面图标长得一模一样：
   圆角 22.5%（图标那边 radius = size × 0.225）、竖向渐变用图标的两个颜色。
   以前是 14px 圆角（30%）+ 斜向主题色，所以两边看着"不太像同一个 Logo"。 */
.onb-logo .mark {
  width: 46px; height: 46px; border-radius: 22.5%; flex: 0 0 46px;
  background: linear-gradient(180deg, var(--brand-a), var(--brand-b));
  display: grid; place-items: center; box-shadow: 0 6px 16px color-mix(in oklab, var(--primary) 35%, transparent);
}
/* 图案大小必须和图标一致：SVG 里图案占 77%，方块 46px
   → 40 / 46 × 77% = 67.5%，正好等于图标里环的外径占比。
   （以前是 27px，只有 45%，所以引导页的 Logo 看着比图标小一圈。） */
.onb-logo .mark svg { width: 40px; height: 40px; color: #fff; }
.onb-logo .nm { font-size: 19px; font-weight: 760; letter-spacing: -.02em; }
.onb-logo .sb { font-size: 12.5px; color: var(--muted); }
.onb-body { flex: 1 1 auto; display: flex; flex-direction: column; }
.onb-step { font-size: 12px; font-weight: 700; color: var(--primary); letter-spacing: .06em; }
.onb-q { font-size: 25px; font-weight: 760; letter-spacing: -.03em; margin-top: 8px; line-height: 1.28; }
.onb-desc { font-size: 14px; color: var(--muted); margin-top: 8px; line-height: 1.6; }
.onb-fields { margin-top: 26px; }
.onb-foot { margin-top: auto; padding-top: 22px; display: flex; gap: 10px; align-items: center; }
.onb-dots { display: flex; gap: 6px; margin-top: 18px; justify-content: center; }
.onb-dots i { width: 6px; height: 6px; border-radius: 50%; background: var(--line); transition: background .2s, width .2s; }
.onb-dots i.on { background: var(--primary); width: 18px; border-radius: 99px; }

/* 结果页数字 */
.big-result { text-align: center; padding: 8px 0 4px; }
.big-result .br-k { font-size: 13px; color: var(--muted); font-weight: 620; }
.big-result .br-v { font-family: var(--font-num); font-size: 46px; font-weight: 800; letter-spacing: -.04em; line-height: 1.05; margin-top: 2px; }
.big-result .br-v small { font-size: 15px; font-weight: 650; color: var(--muted); margin-left: 4px; }
.big-result .br-s { font-size: 13px; color: var(--muted); margin-top: 6px; }

/* ---------- 杂项 ---------- */
.muted { color: var(--muted); }
.small { font-size: 12.5px; }
.tiny { font-size: 11.5px; }
.center { text-align: center; }
.mt0 { margin-top: 0; } .mt6 { margin-top: 6px; } .mt10 { margin-top: 10px; }
.mt14 { margin-top: 14px; } .mt18 { margin-top: 18px; } .mt24 { margin-top: 24px; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.divider { height: 1px; background: var(--line); margin: 16px 0; }
.mono-num { font-family: var(--font-num); font-weight: 700; letter-spacing: -.01em; }
.nowrap { white-space: nowrap; }
.hidden { display: none !important; }

/* ---------- 页面切换 ----------
   新页**直接出现（不淡入）**，只做一点点横移；旧页立刻摘掉、不留层。
   走过的两个坑：
     · 只让新页从 opacity 0 淡入 → 旧页已经没了，开头几十毫秒整屏是空的（"闪"）
     · 让旧页绝对定位叠在上面淡出 → 两层内容半透明混在一起（"叠化"）
   所以现在既不淡入、也不留旧页：任何一帧都只有一页、且是实心的。
   只在**真的换页**时播；同一页刷新（刚记完一餐、AI 小结回来）完全不动。 */
.view.from-right { animation: viewSlideRight .3s cubic-bezier(.22,1,.36,1) backwards; }
.view.from-left  { animation: viewSlideLeft  .3s cubic-bezier(.22,1,.36,1) backwards; }
@keyframes viewSlideRight { from { transform: translateX(14px); } }
@keyframes viewSlideLeft  { from { transform: translateX(-14px); } }

/* 卡片级联：只有**位置**在动，没有透明度。
   为什么：`from { opacity: 0 }` 意味着开头几十毫秒整页内容都是透明的，
   而换页时旧页已经被摘掉 —— 那几十毫秒就是"闪一下"（实测 t=21ms 时六张卡片全 0）。
   现在卡片从头到尾都是实心的，只是依次向上落位。
   只有前 5 个逐个加延迟，后面的统一在 .26s 出场 —— 长页面不至于要等一秒多。 */
.view.stagger > * { animation: riseIn .36s cubic-bezier(.22,1,.36,1) backwards; }
.view.stagger > *:nth-child(1) { animation-delay: .01s; }
.view.stagger > *:nth-child(2) { animation-delay: .06s; }
.view.stagger > *:nth-child(3) { animation-delay: .11s; }
.view.stagger > *:nth-child(4) { animation-delay: .16s; }
.view.stagger > *:nth-child(5) { animation-delay: .21s; }
.view.stagger > *:nth-child(n+6) { animation-delay: .26s; }
@keyframes riseIn { from { transform: translateY(10px); } }

/* 引导页、微信拦截页是直接挂到 #app 上的，不走路由，用这个 */
.view-enter { animation: viewIn .26s cubic-bezier(.22,1,.36,1); }
@keyframes viewIn { from { opacity: 0; transform: translateY(7px); } }

@media (prefers-reduced-motion: reduce) {
  /* 延迟也要一起清零：图表那几层淡入是靠 delay 排队的，
     只清时长的话它们会先隐形半秒再"啪"地出现。 */
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-delay: 0s !important;
    transition-duration: .01ms !important;
    transition-delay: 0s !important;
  }
}
