/* ============================================================
   出行规划小助手 · 样式
   规矩（TECH_DESIGN 第五节）：改样式只动这个文件
   设计取向：干净、清爽、学生党友好；窄屏（手机）也能读完，不用左右拖

   主题（Day 8 追加）：两套 ——
     · 明亮（默认）：浅底深字，教室 / 图书馆 / 白天户外看着舒服，截图干净
     · 赛博（可选）：深空黑底 + 电子青 + 冷紫霓虹，科技感
   切换靠 <html data-theme="cyber">，颜色全部走 CSS 变量，改一处全站生效。
   ============================================================ */

:root {
  /* 告诉浏览器当前是浅色界面：滚动条、下拉框这些"原生控件"会跟着变，
     否则赛博主题下会出现一条亮白色滚动条，很跳。 */
  color-scheme: light;

  /* --- 明亮主题（默认）：暖米白 ---
     底色从原来的冷灰 #f6f7f9 换成暖米白 #faf7f2。
     原因：冷灰配蓝色主色，整体偏"办公软件"；换成带一点黄红的米白后，
     页面像纸一样有温度，白色的卡片放上去也能"浮起来"（原来卡片和白底几乎同色，
     全靠边框撑着，很平）。
     代价：底色一变，边框、补丁色都得跟着往暖调挪，否则冷灰边配暖底会发脏。
     这就是下面一串色号都动了的原因。 */
  --bg: #faf7f2;
  --card: #ffffff;
  --border: #e8e1d6;

  --ink: #221f1a;            /* 墨色也带一点暖，不用纯黑 */
  --ink-2: #6b6459;
  /* ink-3 用在副标题、字段提示、页脚 —— 都是要读的正文级文字。
     原来 #9aa1ab 对比度只有 2.43:1（WCAG 要求 ≥4.5），看着发虚，
     Day 8 算过对比度后调深；换暖底后又按新底色重算过，现在是 5.3:1。 */
  --ink-3: #6a6459;

  --brand: #2a5fe0;
  --brand-soft: #e9effc;     /* 冷色补丁底，刻意留住一点冷，用来"提神" */

  --good: #16794c;
  --good-soft: #e7f4ec;
  --bad: #b4322b;
  --bad-soft: #fbece9;
  --bad-line: #efd3cd;       /* 危险提示条的细边（比 --bad 淡，不抢眼） */
  --on-bad: #ffffff;         /* 压在 --bad 上的文字色 */

  /* 警示色（Day 14 加）：用在"这个数字是估算的，别当真"这类提醒上。
     跟 --bad（红色，代表出错）分开：估算不是错误，是"要知道它的局限"，
     所以用琥珀色，语气是提醒而不是报警。
     对比度：--warn 在 --warn-soft 底上 ≥ 4.5:1，符合 WCAG AA。 */
  --warn: #9a5b00;
  --warn-soft: #fdf3e3;

  /* 抽出这些"补丁色"，是为了让深色主题能整体换掉。
     原来它们散落在各处写死（#fff / #fafbfc / #fcfcfd），
     深色主题下会变成一块块白斑。 */
  --surface: #ffffff;        /* 顶部 / 底部条、按钮底 */
  --surface-2: #f7f2e9;      /* 表头、斑马底 */
  --surface-3: #fdfbf8;      /* 输入框底 */
  --surface-4: #fffdfa;      /* 计划卡底 */
  --hover: #f1ebe1;          /* 按钮悬浮 */
  --field-text: #8c8577;     /* 输入框占位文字 */
  --header-grad-a: #fdfbf7;  /* 标题区渐变起色 */
  --header-grad-b: var(--bg);
  --accent-line: none;       /* 标题区顶部的品牌横条（赛博主题才显示） */

  /* 吃的 / 玩的 小标签 */
  --tag-eat-bg: #fdeedd;
  --tag-eat-fg: #9c5109;
  --tag-play-bg: #e6effc;
  --tag-play-fg: #24589c;

  /* 「第 N 天」果冻胶囊（Day 9 追加）：淡蓝 → 淡紫渐变，通透得像果冻 */
  --pill-a: #eef3ff;         /* 顶部：最亮 */
  --pill-b: #e6e6fb;         /* 中段 */
  --pill-c: #e2e5f7;         /* 底部：略沉，撑出立体感 */
  --pill-ink: #24589c;       /* 字色 */
  --pill-edge: #ccd8f5;      /* 描边 */
  --pill-hi: #ffffff;        /* 顶部内高光 */
  --pill-sh: rgba(42, 95, 224, .13);  /* 悬浮投影 */

  /* 方案卡被选中时的高亮边 */
  --on-brand: #ffffff;       /* 主按钮上的文字色 */

  /* 卡片顶部那条 3px 彩色细边：让卡片之间有了"分节"的感觉，
     不再是几张一模一样的白纸。默认走主色。 */
  --edge: var(--brand);

  /* 纸飞机的颜色：比主色更亮更跳的天蓝/宝蓝。
     单独开一个变量，是为了让它不跟按钮、航线同色 ——
     整张图如果只有"一种蓝的深浅变化"，会显得很平。 */
  --plane: #1e7cf5;

  --radius: 14px;
  /* 阴影加重了一档：底色变暖变浅后，卡片需要更明确的投影才"立得起来" */
  --shadow: 0 1px 2px rgba(90, 66, 34, .05), 0 10px 28px rgba(90, 66, 34, .07);
  --shadow-lift: 0 2px 5px rgba(90, 66, 34, .07), 0 18px 40px rgba(90, 66, 34, .12);
}

/* --- 赛博主题：深空黑底 + 电子青 + 冷紫 ---
   红绿功能色特意调亮（#FF5C6E / #2EE6A8），
   否则在深底上对比度不够，「超支警告」就失去了警示力。 */
[data-theme="cyber"] {
  color-scheme: dark;

  --bg: #080b14;
  --card: #0f1420;
  --border: #1e2a3d;

  --ink: #e8eefc;
  --ink-2: #97a6c4;
  /* 同上：调亮到 4.9:1，深底上不达标会显得字"糊" */
  --ink-3: #8291b0;

  --brand: #00e5ff;
  --brand-soft: #0d2b38;

  --good: #2ee6a8;
  --good-soft: #08281f;
  --bad: #ff5c6e;
  --bad-soft: #2e0f16;
  --bad-line: #4a2029;
  --on-bad: #04121a;         /* 赛博的 --bad 是亮红，压深色字才够对比（白字只有 3.0，不达标） */

  /* 警示色（Day 14 加）：深底上必须调亮，否则琥珀色糊成一片看不清。
     这是"红绿功能色在深色主题必须调亮"的同一个道理。 */
  --warn: #ffb454;
  --warn-soft: #2a1d08;

  --surface: #0d1220;
  --surface-2: #121a2a;
  --surface-3: #0c111c;
  --surface-4: #101827;
  --hover: #1a2436;
  --field-text: #6d7c99;
  --header-grad-a: #101a2e;
  --header-grad-b: var(--bg);
  --accent-line: linear-gradient(90deg, #00e5ff 0%, #7c5cff 50%, #ff5c9e 100%);

  --tag-eat-bg: #2b1a12;
  --tag-eat-fg: #ffa463;
  --tag-play-bg: #0d2333;
  --tag-play-fg: #4fc3ff;

  /* 「第 N 天」果冻胶囊（Day 9 追加）：深底上不能沿用浅色渐变，
     换成"青 → 紫"的暗色玻璃感 —— 跟赛博主题的霓虹调子一致 */
  --pill-a: #14263a;         /* 顶部：稍亮，模拟反光 */
  --pill-b: #17203a;         /* 中段 */
  --pill-c: #1a1f3a;         /* 底部 */
  --pill-ink: #7fe8ff;       /* 字色：电子青 */
  --pill-edge: #24455f;      /* 描边 */
  --pill-hi: rgba(0, 229, 255, .22);  /* 顶部内高光：青色微光 */
  --pill-sh: rgba(0, 0, 0, .55);      /* 深底上的投影 */

  --on-brand: #04121a;

  --shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 8px 24px rgba(0, 0, 0, .45);
  --shadow-lift: 0 2px 6px rgba(0, 0, 0, .55), 0 14px 34px rgba(0, 229, 255, .10);

  /* 卡片顶部细边：赛博主题下换青→紫渐变，和标题区的霓虹线呼应 */
  --edge: linear-gradient(90deg, #00e5ff 0%, #7c5cff 100%);

  /* 赛博主题的纸飞机颜色：写明确的电子青。
     ⚠️ 试过写 "跟着当前主题色走"（继承 color）的写法，但它要隔着一层
        var() 去继承元素自己的 color，行为不够确定 —— 一旦解析不出来，
        fill 就退化成浏览器默认的黑色，米白底上会是一只黑飞机。
        写死色值最稳，行为 100% 确定。 */
  --plane: #00e5ff;
}

* { box-sizing: border-box; }

/* ============================================================
   动效基础（Day 8 追加）

   原则：
   1. 动画只做"锦上添花"，不做"必须等它演完"——所以都很短（0.3–0.7 秒）。
   2. 尊重系统设置：用户在操作系统里开了「减少动态效果」，
      下面所有动画自动关闭（这是无障碍要求，不是可选项）。
   3. 只用 transform / opacity / filter 做动画，不动 width/height/top/left
      —— 后几个会让浏览器反复重排，卡。
   ============================================================ */

/* 动画时长与缓动曲线，统一放在变量里，方便一处调整 */
:root {
  --dur-fast: .16s;
  --dur: .34s;
  --dur-slow: .62s;
  --ease: cubic-bezier(.22, .68, .32, 1);      /* 起步快、收尾缓，自然 */
  --ease-spring: cubic-bezier(.34, 1.42, .5, 1); /* 带一点回弹，活泼 */
}

/* 系统开了「减少动态效果」→ 全部动画归零 */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001s !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001s !important;
    scroll-behavior: auto !important;
  }
}

html, body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--bg);
  color: var(--ink);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei",
               "PingFang SC", "Hiragino Sans GB", sans-serif;
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  /* 内容不满一屏时，也让页脚贴着窗口底部，不留半屏空白 */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* 赛博主题：底图铺一层极淡的网格线（科技感的来源，比铺霓虹色更耐看、也不干扰读字） */
[data-theme="cyber"] body {
  background-image:
    linear-gradient(rgba(0, 229, 255, .045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0, 229, 255, .045) 1px, transparent 1px);
  background-size: 44px 44px, 44px 44px;
  background-attachment: fixed;
}

/* 明亮主题：底图铺两层很淡的东西，让米白不成为"一块死板的平色" ——
   1. 一道斜向的暖色渐层（右上偏暖、左下偏冷），像自然光斜照在纸上；
   2. 一层间隔 26px 的小圆点（透明度只有 4%），远看几乎看不见，
      近看有一点点"纸纹"的质感。
   两层都极淡，是为了不干扰读字 —— 底图的任务是氛围，不是抢戏。 */
html:not([data-theme="cyber"]) body {
  background-image:
    radial-gradient(rgba(120, 96, 62, .055) 1px, transparent 1.2px),
    linear-gradient(157deg, #fdf8ef 0%, #faf7f2 46%, #f6f3f4 100%);
  background-size: 26px 26px, 100% 100%;
  background-attachment: fixed, fixed;
}

/* 明亮主题：标题区背后一团缓慢漂移的柔光，让浅色页面不再"死白"。
   放在固定层里、不接收鼠标事件，所以不会挡住任何按钮。
   默认隐藏，只有非赛博主题（也就是明亮主题）才显示。 */
.light-glow {
  display: none;
}

html:not([data-theme="cyber"]) .light-glow {
  display: block;
  position: fixed;
  inset: 0 0 auto 0;
  height: 420px;
  pointer-events: none;
  z-index: 0;
  overflow: hidden;
}

html:not([data-theme="cyber"]) .light-glow i {
  position: absolute;
  display: block;
  border-radius: 50%;
  filter: blur(64px);
  opacity: .62;
}

html:not([data-theme="cyber"]) .light-glow i:nth-child(1) {
  width: 420px; height: 420px;
  left: -80px; top: -140px;
  background: #f3d9b8;
  animation: drift-a 19s ease-in-out infinite alternate;
}

html:not([data-theme="cyber"]) .light-glow i:nth-child(2) {
  width: 360px; height: 360px;
  right: -60px; top: -110px;
  background: #e6d3f5;
  animation: drift-b 23s ease-in-out infinite alternate;
}

html:not([data-theme="cyber"]) .light-glow i:nth-child(3) {
  width: 300px; height: 300px;
  left: 46%; top: -60px;
  background: #cfe6f7;
  animation: drift-c 17s ease-in-out infinite alternate;
}

/* 赛博主题：网格"通电"—— 底纹缓慢向上流动，像电路在跑 */
[data-theme="cyber"] body {
  animation: grid-flow 6s linear infinite;
}

.wrap {
  /* 宽度跟着窗口走：两边各留 3.5% 呼吸缝，但最多 1600px、最少 320px。
     这样大屏上会自然铺开（不用手动放大），窗口再宽也不会拉到难读的超长行。 */
  width: min(1600px, 93%);
  max-width: none;
  margin: 0 auto;
  padding: 0 18px;
}

/* 让正文区自动撑开剩余高度，把页脚推到最下面 */
.site-header, .site-footer { flex: none; }
main, .page-main { flex: 1 0 auto; }

/* ---------- ① 标题区 ---------- */

.site-header {
  position: relative;
  padding: 44px 0 30px;
  background: linear-gradient(180deg, var(--header-grad-a) 0%, var(--header-grad-b) 100%);
  border-bottom: 1px solid var(--border);
}

/* 赛博主题下，标题区顶部亮一条青 → 紫的霓虹线（明亮主题为 none，不显示） */
.site-header::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: var(--accent-line);
}

/* 主题切换按钮：固定在标题区右上角 */
.theme-toggle {
  position: absolute;
  top: 16px;
  right: 18px;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 13px;
  font-size: 13px;
  font-family: inherit;
  font-weight: 500;
  color: var(--ink-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
  transition: border-color .15s, color .15s, box-shadow .15s;
}

.theme-toggle:hover {
  color: var(--brand);
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-soft);
}

.theme-toggle .dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--brand);
  flex: none;
}

.site-header h1 {
  margin: 0 0 10px;
  font-size: 34px;
  letter-spacing: -.6px;
}

/* 赛博主题：标题区顶部的霓虹线缓慢呼吸（明亮主题是 none，不显示） */
[data-theme="cyber"] .site-header::before {
  animation: neon-breathe 3.4s ease-in-out infinite;
}

/* 赛博主题：标题带一层冷青→紫的渐变字 + 霓虹光晕 */
[data-theme="cyber"] .site-header h1 {
  background: linear-gradient(92deg, #00e5ff 0%, #7c5cff 60%, #ff5c9e 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  text-shadow: 0 0 26px rgba(0, 229, 255, .28);
  animation: text-glow 4s ease-in-out infinite;
}

/* 标题右侧的"网络状态"小点：赛博主题下缓慢呼吸，像设备指示灯 */
.header-signal {
  display: none;
}

[data-theme="cyber"] .header-signal {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 12px;
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--brand);
  animation: breathe 2.6s ease-in-out infinite alternate;
}

[data-theme="cyber"] .header-signal b {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--brand);
  box-shadow: 0 0 8px var(--brand);
}

.tagline {
  margin: 0 0 6px;
  font-size: 17px;
  color: var(--ink-2);
}

.sub-tagline {
  margin: 0;
  font-size: 14px;
  color: var(--ink-3);
}

.sub-tagline b { color: var(--ink-2); }

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

.card {
  position: relative;
  overflow: hidden;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 24px 26px 26px;
  margin: 18px 0;
  transition: box-shadow .2s, border-color .2s;
}

/* 每块卡片顶部压一条 3px 的彩色细边 ——
   几张卡片连着往下排时，这条边充当"分节线"，
   不至于变成一叠一模一样的白纸。（赛博主题下是青→紫渐变） */
.card::after {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: var(--edge);
  opacity: .9;
}

/* 结果区的每块卡换个色相，视觉上能区分"这是第几块" */
#block-plans::after     { background: var(--brand); }
#block-itinerary::after { background: #1f9d78; }
#block-transport::after { background: #d98a18; }
#block-cost::after      { background: #7c5cff; }
#block-budget::after    { background: #cf5a86; }
#block-highlights::after{ background: #3f9ad6; }

/* ---------- 空状态（Day 8 第 2 周新增） ----------
   还没点过「生成方案」时显示。和加载中一样用居中布局，
   但它是"常态"不是"过渡态"，所以留白比加载中宽一点、语气更引导性。 */
.empty-card {
  /* 空卡片不做悬浮抬升 —— 它里面没有可点的东西，浮起来会误导 */
  pointer-events: none;
}

.empty-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 46px 24px 50px;
}

.empty-art {
  display: block;
  width: 150px;
  height: auto;
  margin-bottom: 22px;
  opacity: .9;
  color: #1e7cf5;                 /* 给 SVG 一个基准色 */
}

/* 虚线轨迹 + 两个端点：和首页那张纸飞机图案是同一种语言 */
.empty-art .ea-line { opacity: .38; }
.empty-art .ea-dot  { opacity: .7; }

/* 小纸飞机轻轻飘一下 —— 空状态唯一的动效，不喧闹 */
.empty-art .ea-plane {
  animation: plane-float-a 7s ease-in-out infinite;
  transform-origin: 61px 16px;
}
/* 第二个面（背光那面）单独延迟，避免两个面完全同步显得假 */
.empty-art .ea-plane + .ea-plane {
  animation-duration: 8.4s;
}

.empty-title {
  margin: 0 0 10px;
  font-size: 17px;
  font-weight: 600;
  color: var(--ink);
}

.empty-text {
  margin: 0;
  max-width: 430px;
  font-size: 14px;
  line-height: 1.75;
  color: var(--ink-2);
}

.empty-hint {
  margin: 14px 0 0;
  font-size: 13px;
  color: var(--ink-3);
}

/* 赛博主题：线条和飞机转成电子青，加一点霓虹光晕 */
[data-theme="cyber"] .empty-art {
  color: #00e5ff;
  filter: drop-shadow(0 0 6px rgba(0, 229, 255, .4));
}

/* 系统开了「减少动态效果」→ 飞机别飘 */
@media (prefers-reduced-motion: reduce) {
  .empty-art .ea-plane { animation: none !important; }
}

/* 手机端：图小一号、留白收窄 */
@media (max-width: 560px) {
  .empty-box { padding: 34px 18px 38px; }
  .empty-art { width: 118px; margin-bottom: 18px; }
  .empty-title { font-size: 16px; }
  .empty-text { font-size: 13px; line-height: 1.7; }
  .empty-hint { font-size: 12px; }
}

/* ---------- 加载中状态（Day 8 第 2 周新增） ----------
   一个转圈 + 两行说明。为什么是转圈不是骨架屏：
   结果区有六块、每块形状都不一样，画骨架屏要画六套，不值当；
   转圈一个元素通用，而且这块地方加载完就整块消失（不留位置），
   所以"从转圈变成六块结果"的视觉跳跃可以接受。 */
.loading-card {
  /* 转圈时整块卡片不做悬浮抬升，免得看着像可点的东西 */
  pointer-events: none;
}

.loading-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 13px;
  padding: 46px 20px 50px;
  text-align: center;
}

/* 转圈本体：一个圆环，只有顶边有颜色 —— 旋转起来就是"进度在跑"的观感 */
.spinner {
  display: block;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 3px solid var(--border);
  border-top-color: var(--brand);
  animation: spin .78s linear infinite;
  /* 兜底色：万一变量取不到，至少还是个看得见的灰圈 */
  border-color: #e8e1d6;
  border-top-color: #2a5fe0;
}

.loading-title {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
}

.loading-sub {
  margin: 0;
  font-size: 13px;
  color: var(--ink-3);
  max-width: 380px;
  line-height: 1.6;
}

/* 赛博主题：转圈换成电子青，和整页的霓虹语言一致 */
[data-theme="cyber"] .spinner {
  border-color: #1e2a3d;
  border-top-color: #00e5ff;
  filter: drop-shadow(0 0 6px rgba(0, 229, 255, .45));
}

/* 关键帧：只转 transform，不动 width/height（性能） */
@keyframes spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* 系统开了「减少动态效果」→ 转圈别转，改成让圆环缓慢明暗呼吸，
   既保留"正在忙"的提示，又不会有持续旋转的动感。 */
@media (prefers-reduced-motion: reduce) {
  .spinner {
    animation: breathe 1.4s ease-in-out infinite !important;
  }
}

/* 手机端：转圈小一号，上下留白收窄，别把首屏占满 */
@media (max-width: 560px) {
  .loading-box { padding: 34px 16px 38px; gap: 11px; }
  .spinner { width: 30px; height: 30px; border-width: 3px; }
  .loading-title { font-size: 14px; }
  .loading-sub { font-size: 12px; }
}

/* 鼠标移上去轻轻浮起来 —— 让卡片有"层"，不再平贴背景 */
.card:hover {
  box-shadow: var(--shadow-lift);
  border-color: var(--brand);
}

/* 赛博主题：卡片青色光晕缓缓脉冲，像通着电 */
[data-theme="cyber"] .card {
  background: linear-gradient(160deg, rgba(0, 229, 255, .05) 0%, transparent 42%), var(--card);
  animation: glow-pulse 4.6s ease-in-out infinite;
}

/* 鼠标移上去时停掉脉冲，避免和 hover 阴影打架 */
[data-theme="cyber"] .card:hover { animation: none; }

/* ---------- 结果区逐块淡入 ----------
   app.js 生成结果时给每块加上 .reveal 并设置 animationDelay（错峰 70ms）。
   动画结束后回到普通状态（forwards 不必，因为终态就是常态）。 */
.reveal {
  animation: fade-up var(--dur-slow) var(--ease) both;
}

.card-title {
  margin: 0 0 16px;
  font-size: 17px;
  font-weight: 600;
}

/* ---------- 输入区 ---------- */

/* ① 填一下你的计划：窄屏是一条条往下排，宽屏自动变成"一排格子"，
   这样大屏不会留大片空白，也不用手动放大。
   注意用 minmax(0, 1fr) 而不是 1fr：1fr 的最小宽度是"内容宽度"，
   长占位文字会把格子顶宽、撑出横向滚动条。 */
#input-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0 22px;
}

/* 卡片标题横跨整行，四个字段各占一个格子 */
#input-card .card-title { grid-column: 1 / -1; }

.field { margin-bottom: 18px; min-width: 0; }

.field:last-of-type { margin-bottom: 8px; }

/* 窗口 ≥ 900px：前两个字段（出发城市 / 想去的城市）各占一半，
   后两个短字段（天数 / 预算）各占四分之一，最后一行正好排满；
   操作按钮横跨整行。 */
@media (min-width: 900px) {
  #input-card { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  #input-card .field:nth-of-type(1),
  #input-card .field:nth-of-type(2) { grid-column: span 2; }
  #input-card .actions { grid-column: 1 / -1; }
}

@media (min-width: 1280px) {
  /* 大屏：字段排成一行（2 + 1 + 1 + 1 = 5 格），按钮落在这个网格的下一行、横跨整行 */
  #input-card { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  #input-card .field:nth-of-type(1) { grid-column: span 2; }
  #input-card .field:nth-of-type(2) { grid-column: span 1; }
  #input-card .field:nth-of-type(3) { grid-column: span 1; }
  #input-card .field:nth-of-type(4) { grid-column: span 1; }
  #input-card .actions { grid-column: 1 / -1; margin-top: 12px; }
}

@media (min-width: 900px) and (max-width: 1279px) {
  /* 中等屏幕：6 列网格，字段按 3 / 3 / 3 / 3 排两行，按钮横跨整行 */
  #input-card { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  #input-card .field:nth-of-type(1) { grid-column: span 3; }
  #input-card .field:nth-of-type(2) { grid-column: span 3; }
  #input-card .field:nth-of-type(3) { grid-column: span 3; }
  #input-card .field:nth-of-type(4) { grid-column: span 3; }
  #input-card .actions { grid-column: 1 / -1; }
}

.field label {
  display: block;
  font-size: 14px;
  font-weight: 600;
  margin-bottom: 7px;
}

.required { color: var(--bad); font-weight: 400; }

.hint {
  font-weight: 400;
  font-size: 12px;
  color: var(--ink-3);
  margin-left: 6px;
}

.field input[type="text"],
.field input[type="number"] {
  width: 100%;
  padding: 10px 12px;
  font-size: 15px;
  font-family: inherit;
  color: var(--ink);
  background: var(--surface-3);
  border: 1px solid var(--border);
  border-radius: 10px;
  outline: none;
  transition: border-color .15s, box-shadow .15s, background .15s;
}

.field input::placeholder { color: var(--field-text); }

.field input:focus {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-soft);
  background: var(--surface);
}

.field-note {
  margin: 8px 0 0;
  font-size: 12.5px;
  color: var(--ink-3);
}

/* 城市顺序说明（Day 14）
   为什么做得比普通注释醒目：顺序填错会直接算错行程和费用（返程是从最后一个城市
   回到出发地），这句话用户不看就会踩坑 —— 所以给它底色和左侧竖线，别混在灰字里。 */
.order-note {
  margin: 8px 0 0;
  padding: 9px 12px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--ink-2);
  background: var(--brand-soft);
  border-left: 3px solid var(--brand);
  border-radius: 0 8px 8px 0;
}

.order-note b {
  color: var(--brand);
  font-weight: 700;
}

.error {
  margin: 7px 0 0;
  font-size: 13px;
  color: var(--bad);
}

/* 城市添加行 */

.city-picker {
  display: flex;
  gap: 8px;
}

.city-picker input { flex: 1 1 auto; min-width: 0; }

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}

.chips:empty { margin-top: 0; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px 5px 12px;
  font-size: 14px;
  background: var(--brand-soft);
  color: var(--brand);
  border-radius: 999px;
  /* 新加的城市标签会"弹"出来（app.js 加上 .chip-new 类，动画完自然停在常态） */
  animation: pop-in var(--dur) var(--ease-spring) both;
}

.chip button {
  border: 0;
  background: transparent;
  color: inherit;
  font-size: 15px;
  line-height: 1;
  padding: 2px 4px;
  cursor: pointer;
  border-radius: 999px;
  opacity: .55;
}

.chip button:hover { opacity: 1; background: var(--brand-soft); }

/* ---------- 按钮 ---------- */

.btn {
  position: relative;
  overflow: hidden;          /* 流光扫过时裁掉溢出部分 */
  font-family: inherit;
  font-size: 15px;
  border-radius: 10px;
  border: 1px solid transparent;
  padding: 10px 18px;
  cursor: pointer;
  transition: background .15s, border-color .15s, transform var(--dur-fast),
              box-shadow .18s, filter .18s;
}

.btn:active { transform: translateY(1px); }

.btn-primary {
  background: var(--brand);
  color: var(--on-brand);
  font-weight: 600;
  padding: 11px 26px;
}

/* 主按钮：一层斜向的光缓慢扫过，不再是一块死板的色块。
   平时每 4.6 秒自己扫一次（静静吸引注意）；鼠标悬浮时立刻扫一次。

   为什么用两套 animation：
   悬浮时把循环动画换成"扫一次"，动画名字变了浏览器才会重播它。 */
.btn-primary::after {
  content: "";
  position: absolute;
  top: -60%;
  left: 0;
  width: 34%;
  height: 220%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .38), transparent);
  transform: translateX(-120%);
  pointer-events: none;
  animation: sweep 4.6s var(--ease) infinite 1.2s;
}

.btn-primary:hover::after {
  animation: sweep .72s var(--ease);
}

/* 主按钮悬浮：用"亮度 + 光晕"而不是写死一个深蓝，
   这样两套主题下都自然（明亮下变深、赛博下发青光） */
.btn-primary:hover {
  filter: brightness(1.08);
  box-shadow: 0 0 0 3px var(--brand-soft);
}

/* 赛博主题下主按钮带一层呼吸光晕，像按下去会通电 */
[data-theme="cyber"] .btn-primary {
  box-shadow: 0 0 0 1px rgba(0, 229, 255, .30), 0 0 18px rgba(0, 229, 255, .22);
}

[data-theme="cyber"] .btn-primary:hover {
  box-shadow: 0 0 0 1px rgba(0, 229, 255, .55), 0 0 26px rgba(0, 229, 255, .38);
}

.btn-ghost {
  background: var(--surface);
  border-color: var(--border);
  color: var(--ink);
  flex: 0 0 auto;
}

.btn-ghost:hover { border-color: var(--ink-3); }

.btn-quiet {
  background: transparent;
  color: var(--ink-2);
}

.btn-quiet:hover { background: var(--hover); }

.actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 20px;
  padding-top: 18px;
  border-top: 1px solid var(--border);
  /* 窄屏（没启用网格）时按钮区自己占一整行 */
  grid-column: 1 / -1;
}

/* ---------- 清空确认条（Day 10 新增） ----------

   背景：原来的「清空重填」一点就清 —— 连用户自己加的、存在浏览器里的地点
   也一起删掉，页面上却一个字都不说。误点之后没有后悔药，只能重填一遍。

   为什么做成【页面上的一条】，而不是浏览器原生那种灰框弹窗：
   Day 9 刚把 Windows 原生灰滚动条美化掉，理由就是"露出系统原生控件会显得没做完"。
   原生弹窗是同一个毛病，而且它盖在页面正中间，比滚动条还显眼。

   安全设计：焦点默认落在「取消」上（逻辑在 app.js 的 showResetConfirm 里）。
   这是会删数据的操作，闭着眼敲回车不该把东西删掉。 */

/* 这一条不能省，也不能删：
   上面给 .confirm-bar 设了 display:flex，而【作者样式里的 display
   会盖过浏览器默认的 [hidden]{display:none}】—— 结果是 hidden 属性
   彻底失效，确认条一打开页面就杵在那儿。踩过一次，记在这儿。 */
.confirm-bar[hidden] { display: none; }

.confirm-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  /* 跟 .actions 一样：窄屏（没启用网格）时自己占一整行 */
  grid-column: 1 / -1;
  margin-top: 12px;
  padding: 12px 14px;
  border: 1px solid var(--bad-line, #efd3cd);
  border-left: 3px solid var(--bad, #b4322b);   /* 左边一条加粗的警示条 */
  border-radius: 10px;
  background: var(--bad-soft, #fbece9);
  animation: confirm-in .22s ease-out;
}

.confirm-mark {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--bad, #b4322b);
  color: var(--on-bad, #ffffff);
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
}

.confirm-text {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font-size: 14px;
  line-height: 1.6;
  color: var(--bad, #b4322b);
}

.confirm-actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* 危险按钮：只给"确认会删东西"的操作用，全站唯一一处 */
.btn-danger {
  background: var(--bad, #b4322b);
  color: var(--on-bad, #ffffff);
  font-weight: 600;
  font-size: 14px;
  padding: 8px 16px;
}

.btn-danger:hover { filter: brightness(1.08); }

@keyframes confirm-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .confirm-bar { animation: none !important; }
}

@media (max-width: 560px) {
  /* 窄屏：图标和文字留在第一行（文字自己换行），按钮组落到第二行右对齐。
     不给 .confirm-text 设 100%，否则图标会被单独挤到一行，白占一行高度。 */
  .confirm-bar { flex-wrap: wrap; gap: 10px; padding: 11px 12px; }
  .confirm-text { font-size: 13px; }
  .confirm-actions { flex: 1 1 100%; justify-content: flex-end; }
}

/* ============================================================
   纸飞机航线图案（Day 8 追加）

   位置：输入卡片最底下、按钮下面。
   做法：手写的 SVG（矢量图），不是图片文件，更不是网络图片 ——
      · 不联网：整张图就是几行代码，断网照常显示（AC14 不受影响）；
      · 不失真：矢量图放大到任何尺寸都清晰；
      · 能换皮肤：所有颜色都写 var(--xxx)，跟着主题自动变。
   它是纯装饰，所以标了 aria-hidden="true"，读屏软件会跳过。
   动效：纸飞机沿弧线轻轻浮动 + 虚线上有一小段亮光在跑，
        让人一眼看出"这是一条航线"。
   ============================================================ */

.route-art {
  grid-column: 1 / -1;        /* 和按钮一样横跨整行 */
  margin: 22px 0 2px;
  /* 尽量大：小屏铺满可用宽度，大屏最多 1180px（再宽就拉稀了） */
  width: 100%;
  max-width: 1180px;
  margin-left: auto;
  margin-right: auto;
  color: var(--brand, #2a5fe0);   /* SVG 子元素各写 fill；这里是继承兜底，变量取不到也不发黑 */
}

.route-art svg {
  display: block;
  width: 100%;
  height: auto;               /* 高度跟着宽度等比缩放 */
}

/* 三条航线：虚线 + 圆头线帽，看着像航迹图上的线段。
   兜底色 #2a5fe0 = 明亮主题的主色，和 var(--edge) 在明亮下等价；
   赛博主题有渐变，会正常覆盖它。 */
.route-art .route-line {
  fill: none;
  stroke: var(--edge, #2a5fe0);
  stroke-width: 2.4;
  stroke-linecap: round;
  opacity: .40;
}

/* 地点圆点：实心 + 一圈"光晕"。
   ⚠️ 原来只填个色，在浅底上几乎看不见 —— 加上描边才对得上
      "地图上的城市标记"的印象。 */
.route-art .route-node {
  fill: var(--edge, #2a5fe0);
  stroke: var(--card, #ffffff);
  stroke-width: 3;
  opacity: .75;
}

/* 起点 / 终点：更大、更实，一眼能分出主次 */
.route-art .route-node.main {
  opacity: 1;
  stroke-width: 4;
}

/* ---- 纸飞机 ----
   一张纸飞机 = 两片机翼（迎光，浅）+ 一块机身（背光，深）+ 一条折痕。
   机身和机翼明暗差一档，中间的脊线才立得起来；
   不这么分层的话，几个面糊成一块，看着就是个箭头。

   颜色：明亮主题下专门用 --plane，不跟按钮的深蓝共用。
   原因：航线用的是 --edge（主色，偏深的宝蓝），飞机如果也同色，
   整张图就是"一种蓝深浅不一"，太平；换成更亮更跳的蓝，飞机才跳得出来。
   赛博主题不动 —— 那边靠整图的 filter 变电子青。

   ⚠️ var() 后面那个逗号值是"兜底色"，非常重要：
      万一变量因为任何原因取不到（比如样式表版本不一致），
      fill 会退化成浏览器默认的【黑色】—— 米白底上一只黑飞机，很难看。
      写上兜底色，最坏情况也只是一只深蓝飞机。 */
.route-art .pf-wing-a { fill: var(--plane, #1e7cf5); opacity: .34; }
.route-art .pf-wing-b { fill: var(--plane, #1e7cf5); opacity: .50; }
.route-art .pf-fus-a  { fill: var(--plane, #1e7cf5); opacity: 1; }

/* 折痕：只是"纸被折过"的一点点暗示，所以细、淡、不封口。
   ⚠️ 一开始画成白色粗线还带圆头，放大后像贴了根白管子，机头还多个白点 —— 
      改成半透明的细线、方头收尾，才像折痕。 */
.route-art .pf-crease {
  fill: none;
  stroke: var(--card, #ffffff);
  stroke-width: 1.1;
  stroke-linecap: butt;
  opacity: .55;
}

/* 三张飞机各自飘，周期和幅度都不一样，显得不是同一个动画在复制 */
.route-art .plane-a { animation: plane-float-a 7s ease-in-out infinite; }
.route-art .plane-b { animation: plane-float-b 9s ease-in-out infinite; }
.route-art .plane-c { animation: plane-float-c 8s ease-in-out infinite; }

/* 赛博主题：整张图转成电子青 + 霓虹光晕，和页面其它部分同一套语言 */
[data-theme="cyber"] .route-art {
  color: #00e5ff;
  filter: drop-shadow(0 0 7px rgba(0, 229, 255, .5));
}

[data-theme="cyber"] .route-art .route-line { opacity: .58; }
[data-theme="cyber"] .route-art .route-node { opacity: .85; }

/* ---------- 表格 ---------- */

.table-scroll { overflow-x: auto; }

/* 窄屏时表格要左右滚 —— 滚动条美化（Day 9 追加）
   不美化的话，Windows 上是一根又粗又亮灰色的原生条，
   压在一张精心做的卡片里很"露怯"。
   · 用 var(--xxx, 兜底色)：变量取不到时也有正确颜色，不会变黑（沿用 Day 8 的三层防线）
   · Firefox 用 scrollbar-width / scrollbar-color，不支持伪元素
   · 触屏（hover: none）本来就看不到滚动条，不额外处理 */
.table-scroll {
  scrollbar-width: thin;
  scrollbar-color: var(--border, #e8e1d6) transparent;
}
.table-scroll::-webkit-scrollbar {
  height: 8px;
}
.table-scroll::-webkit-scrollbar-track {
  background: transparent;
}
.table-scroll::-webkit-scrollbar-thumb {
  background: var(--border, #e8e1d6);
  border-radius: 999px;
}
.table-scroll::-webkit-scrollbar-thumb:hover {
  background: var(--ink-3, #6a6459);
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 15px;
}

.data-table th,
.data-table td {
  text-align: left;
  padding: 11px 14px;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}

.data-table th {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-2);
  background: var(--surface-2);
  white-space: nowrap;
}

/* 鼠标划过整行高亮：大表格看着不容易看串行 */
.data-table tbody tr:hover td { background: var(--brand-soft); }

.data-table tbody tr:last-child td { border-bottom: 0; }

.data-table td.num,
.data-table th.num { text-align: right; white-space: nowrap; }

/* 数字列用等宽字体：金额上下对齐，读起来利落（也是"科技感"的来源之一） */
.data-table td.num {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ---------- 「第 N 天」果冻胶囊（Day 9 追加） ----------
   需求原话是"果冻悬浮包围住字"。果冻感不是一个属性做出来的，是四件事叠的：
     ① 胶囊底   —— 圆角 + 淡渐变，把字"包围"起来
     ② 内高光   —— 顶部一条更亮的细高光，像果冻表面的反光
     ③ 悬浮感   —— 极轻的投影 + 底部一条略深的边，像软糖浮着
     ④ 落停回弹 —— 载入时轻微放大回弹，像果冻颤了一下
   三条必须守住的规矩：
     · 只作用于「每日行程」表的第 1 列（.col-day），不碰交通表 —— 那张表第一列是
       「✓ 本方案选用」，套胶囊会变得莫名其妙
     · span 用 inline-block：宽度跟着字走。若直接给 td 上色，td 被拉满整列宽，
       胶囊就变成"一条横杠"了
     · 第 1 列定宽 92px：这样"第 1 天"和"第 12 天"占的宽度一样，
       天数变多时后面的列不会被挤得来回跳
   ---------------------------------------------------------- */

.data-table td.col-day {
  width: 92px;
  white-space: nowrap;
}

.day-pill {
  display: inline-block;
  padding: 5px 13px;
  border-radius: 999px;          /* 999px = 完全圆角，也就是胶囊形 */
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .2px;
  line-height: 1.35;
  /* 淡蓝 → 淡紫渐变：果冻的"通透感"来源。
     var() 全部带兜底 —— 变量取不到时也有正确颜色，不会退化成透明/黑色（Day 8 的教训） */
  background: linear-gradient(180deg,
              var(--pill-a, #eef3ff) 0%,
              var(--pill-b, #e6e6fb) 62%,
              var(--pill-c, #e2e5f7) 100%);
  color: var(--pill-ink, #24589c);
  border: 1px solid var(--pill-edge, #ccd8f5);
  box-shadow:
    inset 0 1px 0 var(--pill-hi, #ffffff),      /* ② 顶部内高光 */
    0 1px 2px var(--pill-sh, rgba(42, 95, 224, .13));  /* ③ 悬浮投影 */
  transition: transform .18s var(--ease-spring, cubic-bezier(.34, 1.42, .5, 1)),
              box-shadow .18s var(--ease, cubic-bezier(.22, .68, .32, 1));
}

/* ③ 悬浮感加强：鼠标划过整行时（行高亮由 tbody tr:hover 触发），胶囊轻轻抬一下 */
.data-table tbody tr:hover .day-pill {
  transform: translateY(-1px) scale(1.03);
  box-shadow:
    inset 0 1px 0 var(--pill-hi, #ffffff),
    0 3px 7px var(--pill-sh, rgba(42, 95, 224, .18));
}

/* ④ 落停回弹：结果淡入到这一行时，胶囊轻轻"颤"一下 */
.reveal-item .day-pill {
  animation: jelly-drop .5s var(--ease-spring, cubic-bezier(.34, 1.42, .5, 1)) both;
}

@keyframes jelly-drop {
  0%   { transform: scale(.86); opacity: 0; }
  55%  { transform: scale(1.06); opacity: 1; }
  78%  { transform: scale(.98); }
  100% { transform: scale(1); opacity: 1; }
}

/* 无预算 / 无结果时空着的第一列不套胶囊（那时日期没意义） */
.data-table td.col-day:empty { background: none; }

/* 系统开了"减少动态效果"→ 所有动画归零（无障碍要求，全站一贯做法） */
@media (prefers-reduced-motion: reduce) {
  .day-pill,
  .data-table tbody tr:hover .day-pill { transition: none !important; }
  .reveal-item .day-pill { animation: none !important; }
}

/* 窄屏：胶囊收紧一点，否则定宽 92px 会占掉太多横向空间 */
@media (max-width: 560px) {
  .data-table td.col-day { width: 76px; }
  .day-pill { padding: 4px 10px; font-size: 12px; letter-spacing: 0; }
}



.data-table tr.row-total td {
  font-weight: 700;
  background: var(--surface-2);
  border-top: 2px solid var(--border);
}

/* 合计那一行金额：放大 + 主色，最该突出的数字一眼就看到 */
.data-table tr.row-total td.num {
  font-size: 20px;
  color: var(--brand);
  letter-spacing: -.3px;
}

/* 赛博主题：合计金额带青色微光，像仪表的读数 */
[data-theme="cyber"] .data-table tr.row-total td.num {
  animation: text-glow 3s ease-in-out infinite;
}

/* 赛博主题的果冻胶囊：加一层淡淡的青色外发光，像通着电的软糖 */
[data-theme="cyber"] .day-pill {
  box-shadow:
    inset 0 1px 0 var(--pill-hi, rgba(0, 229, 255, .22)),
    0 1px 3px var(--pill-sh, rgba(0, 0, 0, .55)),
    0 0 10px rgba(0, 229, 255, .10);
}
[data-theme="cyber"] .data-table tbody tr:hover .day-pill {
  box-shadow:
    inset 0 1px 0 var(--pill-hi, rgba(0, 229, 255, .22)),
    0 3px 9px var(--pill-sh, rgba(0, 0, 0, .6)),
    0 0 14px rgba(0, 229, 255, .20);
}

/* 费用清单每一行错峰淡入（app.js 设置 animationDelay） */
.reveal-item {
  animation: fade-up var(--dur) var(--ease) both;
}

.data-table tr.row-transit td { color: var(--ink-2); }

.data-table tr.row-transit td:first-child { font-weight: 600; color: var(--ink); }

.legend-line {
  margin: 12px 0 0;
  font-size: 13px;
  color: var(--ink-2);
  padding-left: 12px;
  border-left: 3px solid var(--brand);
}

/* ---------- 整体方案对比（Day 7 追加） ---------- */

.card-note {
  margin: 0 0 14px;
  font-size: 13px;
  line-height: 1.7;
  color: var(--ink-2);
}

.plans-wrap {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 14px;
}

.plan-card {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  padding: 16px 18px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-4);
  transition: border-color var(--dur), box-shadow var(--dur), background var(--dur),
              transform var(--dur);
}

/* 鼠标划过整张卡：轻轻抬起来 */
.plan-card:hover { transform: translateY(-2px); }

/* 正在查看的那一套：加品牌色边 + 淡底，一眼能认出 */
.plan-card.active {
  border-color: var(--brand);
  background: var(--brand-soft);
  box-shadow: 0 0 0 1px var(--brand);
}

/* 选中的卡片：一道光从左侧扫过，提示"现在是这一套" */
.plan-card.active::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 38%;
  height: 100%;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .55), transparent);
  pointer-events: none;
  animation: sweep .82s var(--ease);
}

/* 赛博主题：选中的卡片带青紫渐变边 + 外发光 */
[data-theme="cyber"] .plan-card.active {
  box-shadow: 0 0 0 1px rgba(0, 229, 255, .55), 0 0 22px rgba(0, 229, 255, .22);
}

[data-theme="cyber"] .plan-card.active::after {
  background: linear-gradient(100deg, transparent, rgba(0, 229, 255, .30), transparent);
}

.plan-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 4px;
}

.plan-name {
  margin: 0;
  font-size: 17px;
  font-weight: 700;
  color: var(--ink);
}

.plan-total {
  font-size: 20px;
  font-weight: 700;
  color: var(--brand);
  white-space: nowrap;
}

.plan-tagline {
  margin: 0 0 12px;
  font-size: 13px;
  color: var(--ink-2);
}

.plan-legs {
  margin: 0 0 12px;
  padding-left: 18px;
  font-size: 13px;
  line-height: 1.9;
  color: var(--ink);
}

.plan-legs li { margin: 0; }

/* 段内交通方式下拉（2026-10-27：用户要的"某一段飞机换高铁"）。
   用原生 select：手机上弹系统选择器，电脑上键盘可达，符合窄屏和无障碍要求。 */
.leg-swap {
  max-width: 15.5em;          /* 太长会撑破窄屏卡片 */
  padding: 1px 4px;
  font: inherit;
  font-size: 12.5px;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.leg-swap:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-soft);
}

/* 「估算」小标记：这段数字是兜底估的，不是查来的 */
.plan-est-mark {
  margin-left: 6px;
  padding: 0 5px;
  font-size: 11px;
  border-radius: 4px;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--ink-2);
  cursor: help;
}

/* 「已手动调整」徽标：用户换过段之后挂在方案名旁 */
.plan-badge {
  margin-left: 8px;
  padding: 1px 8px;
  font-size: 11.5px;
  font-weight: 600;
  border-radius: 999px;
  background: var(--brand-soft);
  color: var(--brand);
}

/* 换段被拒（能玩天数不够）时的一次性提示 */
.plan-notice {
  margin: 0 0 10px;
  padding: 8px 12px;
  border-radius: 8px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--warn, #c77d00);
  font-size: 13px;
  line-height: 1.7;
  color: var(--ink);
}

/* 价格口径声明：估算 + 实价自查 */
.plan-note {
  margin: 0 0 12px;
  font-size: 11.5px;
  line-height: 1.6;
  color: var(--ink-2);
}

.plan-days {
  margin: 0 0 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}

.plan-compare {
  margin: 0 0 14px;
  padding: 8px 10px;
  border-radius: 8px;
  background: var(--surface);
  border: 1px dashed var(--border);
  font-size: 12.5px;
  line-height: 1.7;
  color: var(--ink-2);
}

/* 按钮永远贴卡片底部，两张卡片对得齐 */
.plan-card .btn {
  margin-top: auto;
  width: 100%;
}

/* 交通表：每段的分隔标题行 */
.row-seg-head td {
  padding-top: 16px;
  font-weight: 700;
  color: var(--ink);
  background: transparent;
  border-bottom: 1px solid var(--border);
}

/* 交通表：当前方案选中的那一行 */
.row-picked td {
  background: var(--brand-soft);
  font-weight: 600;
}

/* 行程表：这一天的安排里含有"你自己添加的地点"
   用左侧色条标出来，一眼能认出哪些是自己加的，不被埋在内置建议里 */
.row-has-self td:nth-child(3) {
  box-shadow: inset 3px 0 0 0 var(--brand);
  padding-left: 12px;
}

/* 表格下方的"每段一句结论" */

.seg-note {
  margin: 6px 0 0;
  font-size: 13px;
  color: var(--ink-2);
}

/* 估算声明（有城市不在数据表里时出现）
   跟普通 .seg-note 区分开：用警示色 + 左侧竖线，
   一眼能看出"这句是在提醒你这个数字不可靠"，而不是又一句行程说明。 */
.seg-note-estimate {
  margin-top: 8px;
  padding: 8px 12px;
  border-left: 3px solid var(--warn);
  background: var(--warn-soft);
  border-radius: 0 8px 8px 0;
  color: var(--ink);
}

/* 机票去哪查的提示（Day 16）
   用蓝色系（--brand 一路的色），跟上面那条橙色的"估算警示"分开 ——
   这条不是"数字不可靠"的警告，是"这条数据没有官方查询站，请去别处查"的指路。
   两件事混成一种颜色，用户就分不清哪个是警告、哪个是提示了。 */
.seg-note-air {
  margin-top: 8px;
  padding: 8px 12px;
  border-left: 3px solid var(--brand);
  background: var(--brand-soft);
  border-radius: 0 8px 8px 0;
  color: var(--ink);
}

/* ---------- 外部核对链接（Day 16） ----------
   出现在两个地方：段标题后的「查路线」、每行说明列里的「去 12306 查」。
   做成一眼能认出"这是个能点的链接"的样子（颜色 + 下划线 + 小箭头），
   但又不能太抢眼 —— 表格主体是数据，链接只是辅助入口。 */
.ext-link {
  color: var(--brand);
  text-decoration: none;
  border-bottom: 1px dashed var(--brand);
  padding-bottom: 1px;
  font-weight: 500;
  transition: color .15s, border-color .15s, opacity .15s;
  white-space: nowrap;
}

.ext-link::after {
  content: "↗";
  font-size: .85em;
  margin-left: 2px;
  opacity: .75;
}

.ext-link:hover,
.ext-link:focus-visible {
  color: var(--brand-strong, var(--brand));
  border-bottom-style: solid;
  border-bottom-color: currentColor;
}

/* 键盘用户走到链接上要看得见（不能只在鼠标悬停时有反馈） */
.ext-link:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: 3px;
}

/* 段标题里那个"查路线"：比行内的链接轻一点，
   因为它是"整段"的辅助动作，跟数据行不是同一层级。 */
.ext-link.seg-link {
  font-weight: 400;
  font-size: 12.5px;
}

/* 交通表底下的核对入口总说明（Day 16）
   用中性的底色，区别于橙色警示 —— 它讲的是"去哪里核对"，不是"数字有问题"。 */
.lookup-note {
  margin-top: 14px;
  padding: 10px 14px;
  background: var(--surface-3);
  border-radius: 10px;
  border: 1px solid var(--border);
}

/* ---------- 预算结论 ---------- */

.verdict {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  padding: 14px 16px;
  border-radius: 12px;
  font-size: 16px;
}

.verdict.good { background: var(--good-soft); color: var(--good); }
.verdict.bad  { background: var(--bad-soft);  color: var(--bad); }

.verdict .verdict-main { font-size: 20px; font-weight: 700; }

.verdict-advice {
  margin: 14px 0 0;
  padding: 0;
  list-style: none;
}

.verdict-advice li {
  display: flex;
  gap: 10px;
  padding: 10px 12px;
  margin-bottom: 8px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  font-size: 14.5px;
}

.verdict-advice li .advice-no {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  margin-top: 2px;
  border-radius: 50%;
  background: var(--brand-soft);
  color: var(--brand);
  font-size: 12px;
  font-weight: 700;
  line-height: 20px;
  text-align: center;
}

/* ---------- 美食美景清单 ---------- */

.city-block { margin-bottom: 20px; }
.city-block:last-child { margin-bottom: 0; }

.city-block h3 {
  margin: 0 0 10px;
  font-size: 15px;
  font-weight: 600;
}

.city-block h3 .city-tag {
  font-size: 12px;
  font-weight: 400;
  color: var(--ink-3);
  margin-left: 6px;
}

.tag-eat, .tag-play {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 6px;
  font-size: 12px;
  white-space: nowrap;
}

.tag-eat  { background: var(--tag-eat-bg); color: var(--tag-eat-fg); }
.tag-play { background: var(--tag-play-bg); color: var(--tag-play-fg); }

.empty-note {
  padding: 12px 14px;
  background: var(--surface-2);
  border: 1px dashed var(--border);
  border-radius: 10px;
  font-size: 14px;
  color: var(--ink-2);
}

.self-add {
  margin-top: 10px;
  padding: 12px 14px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 10px;
}

.self-add .self-add-row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.self-add input,
.self-add select {
  font-family: inherit;
  font-size: 14px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink);
  outline: none;
}

.self-add input:focus,
.self-add select:focus { border-color: var(--brand); }

.self-add input.name { flex: 1 1 140px; min-width: 0; }
.self-add input.price { flex: 0 0 92px; }
.self-add select { flex: 0 0 auto; }

.subtotal-line {
  margin: 10px 0 0;
  font-size: 13.5px;
  color: var(--ink-2);
}

/* ---------- 自填条目的删除按钮（Day 7 追加） ---------- */

/* 最后一列很窄，专门放 × 按钮 */
.data-table th.col-op,
.data-table td.col-op {
  width: 42px;
  padding-left: 4px;
  padding-right: 8px;
  text-align: right;
}

.btn-del {
  width: 24px;
  height: 24px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: var(--ink-3);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  transition: background .15s, color .15s, border-color .15s;
}

/* 悬停才变红：平时不抢眼，指上去才明确"这是删除" */
.btn-del:hover {
  background: var(--bad-soft);
  border-color: var(--bad);
  color: var(--bad);
}

.btn-del:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 1px;
}

/* ---------- 底部 ---------- */

.site-footer {
  margin-top: 32px;
  padding: 22px 0 34px;
  border-top: 1px solid var(--border);
  background: var(--surface);
  font-size: 13px;
  color: var(--ink-2);
}

.site-footer p { margin: 0 0 6px; }

.footer-note { color: var(--ink-3); }

/* ---------- 窄屏适配（AC13：不用左右拖动） ---------- */

/* ---------- 顶端进度条 ----------
   点「生成方案」时滑过一条，给"正在算"的反馈。
   算得快（几十毫秒），所以它更像一个"有动静"的确认，
   而不是真的在等——但用户会觉得更踏实。 */
.progress-bar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  z-index: 50;
  transform-origin: left center;
  transform: scaleX(0);
  opacity: 0;
  background: linear-gradient(90deg, var(--brand), #7c5cff);
  pointer-events: none;
}

/* app.js 加上 .running 才播动画；动画跑完自己淡出，不占位 */
.progress-bar.running {
  animation: bar-run 900ms var(--ease) forwards;
}

/* 任何屏都不该出现横向滚动条：给页面根加一道保险 */
html { overflow-x: hidden; }

@media (max-width: 560px) {
  body { font-size: 15px; }

  .site-header { padding: 56px 0 20px; }
  .site-header h1 { font-size: 22px; }
  .tagline { font-size: 15px; }

  /* 手机上把主题按钮提到标题上方，不再绝对定位挡住 h1 */
  .theme-toggle {
    position: static;
    margin: 0 18px 14px;
    font-size: 12px;
    padding: 6px 11px;
  }

  /* 手机上一律单列：四个字段一条条往下排 */
  #input-card { display: block; }
  .card { padding: 18px 16px 20px; border-radius: 12px; }

  /* 合计金额在手机上缩小一点，避免撑破表格 */
  .data-table tr.row-total td.num { font-size: 17px; }

  .city-picker { flex-wrap: wrap; }
  .city-picker input { flex: 1 1 100%; }
  .btn-ghost { width: 100%; }

  .actions { flex-direction: column; align-items: stretch; }
  .actions .btn { width: 100%; }

  /* 纸飞机图案在手机上收窄、贴近按钮一点，免得占掉半屏 */
  .route-art { margin-top: 16px; margin-bottom: 0; }

  .data-table { font-size: 14px; }
  .data-table th, .data-table td { padding: 9px 10px; }

  /* 方案卡片在窄屏改成单列，避免挤成两窄条 */
  .plans-wrap { grid-template-columns: 1fr; }
  .plan-head { flex-direction: column; align-items: flex-start; gap: 2px; }

  .verdict { flex-direction: column; gap: 4px; }

  /* 手机上关掉底纹：小屏上格子太密看着眼晕，也省点电 */
  [data-theme="cyber"] body { background-image: none; animation: none; }

  /* 手机上也关掉光晕：占地方、也没必要 */
  html:not([data-theme="cyber"]) .light-glow { display: none; }
}

/* ---------- 行程表「没排下的建议」展开 / 收起（Day 11 追加） ----------

   这一块解决的是"程序算出来了、但显示不出来"的内容：
   一个城市待的天数不够时，池子里总会剩下几条排不进去。
   以前只报个数（"还有 2 条建议没排下"），现在点一下能看见是哪几条。
*/

/* 行程正文单独成块：这样后面的展开按钮自己换行，
   不用在 JS 里塞 <br>，也不用给按钮写死外边距去硬凑 */
.row-detail { display: block; }

.more-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 8px;
  padding: 3px 10px;
  border: 1px dashed var(--border);
  border-radius: 999px;
  background: transparent;
  color: var(--ink-2);
  font-family: inherit;
  font-size: 12.5px;
  line-height: 1.7;
  cursor: pointer;
  transition: color .15s, border-color .15s, background .15s;
}

.more-btn:hover {
  color: var(--brand);
  border-color: var(--brand);
  background: var(--brand-soft);
}

/* 键盘 Tab 走到它的时候也得看得见（Day 11 加练：焦点状态）。
   只写 :focus-visible、不写 :focus —— 鼠标点完不该留一圈框，
   只有键盘操作才需要，这件事浏览器已经帮我们区分好了。 */
.more-btn:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

/* 展开之后箭头翻个身，指回"收起来"的方向。
   为什么不用 ▾ 这个字符：小字号下它在屏幕上只剩一个点，等于没有（Day 11 实测）。
   改成用边框自己画一个三角，大小才由我们说了算。
   font-size: 0 是为了把 span 里那个 ▾ 字符藏起来 ——
   留着它是当"样式万一没加载"时的降级；正常情况由边框画的三角出场。 */
.more-arrow {
  display: inline-block;
  width: 0;
  height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid currentColor;   /* currentColor：跟着按钮文字颜色走，
                                           悬停变蓝时箭头一起变，不用再写一条规则 */
  font-size: 0;
  line-height: 0;
  transition: transform var(--dur) var(--ease);
}

.more-btn.is-open .more-arrow { transform: rotate(180deg); }

/* 展开出来的那几条 */
.more-list {
  margin: 8px 0 0;
  padding: 8px 12px 8px 22px;
  border-radius: 8px;
  background: var(--brand-soft);
  box-shadow: inset 3px 0 0 0 var(--brand);
  font-size: 13px;
  line-height: 1.8;
  color: var(--ink);
  animation: more-open var(--dur) var(--ease) both;
}

/* 为什么排不下 —— 先看内容是哪几条，再看原因，顺序别倒 */
.more-why {
  margin: 6px 0 0;
  font-size: 12.5px;
  line-height: 1.7;
  color: var(--ink-3);
}

/* 兜底（Day 10 踩过的坑）：作者样式里的 display 会盖掉浏览器默认的
   [hidden]{display:none}，元素就"藏不住"了。这里虽然没给它们设 display，
   但先写死一条，免得以后有人加了 display:flex 才发现。 */
.more-list[hidden],
.more-why[hidden] { display: none; }

/* ============================================================
   美食清单的筛选栏（Day 12）
   ============================================================ */

/* 一整条：标签 + 三个档位 + 右侧条数 */
.filter-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 16px;
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-2);
}

.filter-label {
  font-size: 13.5px;
  color: var(--ink-3);
}

/* 三个档位按钮。
   未选中 = 空心（透明/浅底 + 边框）；选中 = 实心（主色填充）。
   ⚠️ 刻意做成"空心 → 实心"，而不是"浅一点 → 深一点"：
      只靠颜色深浅的话，色弱用户分不出来 —— 填充状态的变化才是谁都看得见的。 */
.filter-btn {
  font: inherit;
  font-size: 13.5px;
  line-height: 1.4;
  padding: 5px 16px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink-2);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              transform var(--dur-fast) var(--ease);
}

.filter-btn:hover {
  background: var(--hover);
  color: var(--ink);
  border-color: var(--ink-3);
}

/* 选中态放在 :hover 后面 —— 特异性一样时后写的赢，
   否则"选中的那一档"一悬停就被 hover 的浅底盖掉了 */
.filter-btn.is-on {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--on-brand);   /* 两套主题各有一份：明亮用白字，赛博用深字 */
  font-weight: 600;
}

.filter-btn.is-on:hover {
  background: var(--brand);
  color: var(--on-brand);
  transform: translateY(-1px);
}

/* 焦点框只给键盘（鼠标点完不该留一圈） */
.filter-btn:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

/* 右侧条数：筛选结果最直接的"文字证据"，会一直留在那儿 */
.filter-count {
  margin-left: auto;
  font-size: 13.5px;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;   /* 数字等宽，来回切档位时不会左右抖 */
}

/* 空状态：这一档下一条都没有。
   外形复用 .empty-note，只补一条左侧竖线 —— 跟"这个城市从来没数据"区分开 */
.filter-empty {
  box-shadow: inset 3px 0 0 0 var(--ink-3);
}

/* 窄屏：标签和按钮挤在一起不好点，让条数单独占一行 */
@media (max-width: 560px) {
  .filter-bar { gap: 6px; padding: 9px 11px; }
  .filter-btn { padding: 5px 13px; font-size: 13px; }
  .filter-count { flex-basis: 100%; margin-left: 0; text-align: right; }
}

/* 窄屏：手机上的表格本来就窄，缩进和字号都收一点 */
@media (max-width: 560px) {
  .more-btn { font-size: 12px; }
  .more-list { padding: 7px 10px 7px 19px; font-size: 12.5px; }
}

/* ============================================================
   「当天大致安排」可编辑（Day 15）

   来历：这一格原来是纯文字（AI 算出来就没法动）。用户的实际需求是
   "照着这个去玩，但我想自己微调" —— 比如把"人民公园"换成"锦里"。
   所以给它加一个「编辑」入口，点开变成一组可增删改的输入框。

   ⚠️ 三条设计约束（都不是随便定的）：
   1. 改安排【不改花费】。当天的钱是按时天固定标准估的（市内交通+吃+门票），
      跟写了几条安排无关。这件事必须在编辑态里写出来，否则用户改完
      看到钱没变，会以为功能坏了。 → .edit-tip
   2. 只在【编辑态】才出现输入框。"直接点文字就能改"看着更省事，
      但在手机上手指一划就误触进编辑、还弹键盘 —— 宁可多一个按钮。
   3. 改过的行要有【看得见的痕迹】（.edited-badge + .row-edited），
      否则用户下次打开会分不清"这是我改的、还是 AI 给的"。
   颜色一律走语义变量，双主题自动跟着变。
   ============================================================ */

/* 「编辑」按钮：跟同格子里的「展开看这 N 条」是一家人（虚线胶囊），
   但更轻 —— 它是次要动作，不该跟展开按钮抢注意力。 */
.edit-btn {
  display: inline-flex;
  align-items: center;
  margin-top: 8px;
  margin-left: 6px;              /* 和展开按钮并排时不贴在一起 */
  padding: 3px 10px;
  border: 1px dashed var(--border);
  border-radius: 999px;
  background: transparent;
  color: var(--ink-2);
  font-family: inherit;
  font-size: 12.5px;
  line-height: 1.7;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              background var(--dur-fast) var(--ease);
}

.edit-btn:hover {
  color: var(--brand);
  border-color: var(--brand);
  background: var(--brand-soft);
}

.edit-btn:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

/* 改过的标记。做成实心小胶囊而不是纯文字 ——
   纯文字（灰提示色）在浅底上太弱，用户扫一眼看不见。 */
.edited-badge {
  display: inline-block;
  margin-top: 8px;
  margin-left: 6px;
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--brand);
  color: var(--on-brand);
  font-size: 11.5px;
  line-height: 1.7;
  vertical-align: middle;
}

/* 整行"我改过"的底色：只在左侧留一条主色竖线。
   为什么不整行填色 —— 这一列本来就有斑马纹和悬停底色，
   再叠一层会打架，"哪一层是什么"就说不清了。 */
.data-table tr.row-edited td:first-child {
  box-shadow: inset 3px 0 0 0 var(--brand);
}

/* ---------- 编辑态 ---------- */

/* 正在编辑的整行：底色跟别的行拉开，一眼看出"改的是这一行"。
   为什么还要给 tr 上色、而不是只靠那圈框：编辑块在表格第三列里，
   用户视线容易只盯着输入框，忘了自己在改哪一天的行程。 */
.data-table tr.is-editing td {
  background: var(--brand-soft);
}

.edit-list {
  margin-top: 4px;
  padding: 12px 14px;
  border: 1px solid var(--brand);
  border-radius: 12px;
  background: var(--surface-3);
  /* ⚠️ 这里【故意不写 animation-fill-mode: both】。
     写了 both 的话，动画一旦没播（浏览器省电模式、被降级、或者
     截图/导出这类"静态渲染"场景），元素会永远停在第一帧 opacity: 0 ——
     用户点开「编辑」看到一片空白，还查不出原因。
     不写 both 就没这个问题：动画没播时元素直接就是它本来的样子（可见）。 */
  animation: fade-up var(--dur) var(--ease);
}

/* "改这里不改花费"的说明。做成提示条而不是小灰字：
   它承担的是"消除误会"的职责，用户看不见就等于没写。 */
.edit-tip {
  margin: 0 0 12px;
  padding: 8px 12px;
  border-radius: 8px;
  background: var(--brand-soft);
  box-shadow: inset 3px 0 0 0 var(--brand);
  color: var(--ink-2);
  font-size: 12.5px;
  line-height: 1.7;
}

/* 一条安排：输入框占满剩下的宽度，右边三个小按钮 */
.edit-line {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 8px;
}

.edit-input {
  flex: 1;
  /* ⚠️ min-width: 0 必须写：flex 子项的默认最小宽度是 auto（内容宽度），
     输入框里文字一长就会把整行撑出横向滚动条。这个坑在 #input-card 踩过。 */
  min-width: 0;
  font-family: inherit;
  font-size: 14px;
  line-height: 1.6;
  padding: 7px 11px;
  border: 1px solid var(--border);
  border-radius: 8px;
  /* 配色跟顶部那几个输入框保持一致（用户填字的地方都长一个样）。
     ⚠️ 正文色必须是 --ink，不能用 --field-text —— 那个变量的用途是
        "占位文字"，赛博主题下它是 #6d7c99（深蓝灰），压在深底上根本看不清。 */
  background: var(--surface-3);
  color: var(--ink);
}

.edit-input:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-soft);
}

/* 「当天花费估算」的编辑态（2026-10-27 加）。
   花费格在表格最右列，空间窄 —— 输入框做得比安排编辑的小，
   数字输入框宽度按 4 位数字预留，两个按钮收成小号。 */
.cost-edit {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
}

.cost-edit-input {
  width: 76px;                    /* 4 位数字 + 步进按钮刚好，再宽会顶出列 */
  font-family: inherit;
  font-size: 14px;
  line-height: 1.6;
  padding: 4px 8px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface-3);
  color: var(--ink);
  text-align: right;
}

.cost-edit-input:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-soft);
}

.cost-edit-btn {
  padding: 4px 10px;
  font-size: 12.5px;
}

/* 花费格的「改」按钮：跟安排格的「编辑」一个长相（虚线胶囊），
   但列窄，外边距收小一点。 */
td.col-cost .edit-btn {
  margin-top: 4px;
  margin-left: 4px;
}

td.col-cost .cost-badge {
  margin-top: 0;
  margin-left: 4px;
}

.edit-tools {
  flex: none;
  display: flex;
  gap: 4px;
}

/* 上移 / 下移 / 删除。做成小方块而不是圆点：
   圆点在 12px 这个尺寸下几乎分不出边界，方块更像"能按的东西"。 */
.edit-mini {
  width: 28px;
  height: 28px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--surface-2);
  color: var(--ink-2);
  font-family: inherit;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              background var(--dur-fast) var(--ease);
}

.edit-mini:hover:not(:disabled) {
  color: var(--brand);
  border-color: var(--brand);
  background: var(--brand-soft);
}

/* 到头了就禁用（第一条的上移、最后一条的下移）。
   为什么不禁用而是"点一下没反应"：禁用是【看得见】的，
   用户一眼就知道"已经是最上面了"，不用去猜为什么点了不动。 */
.edit-mini:disabled {
  opacity: .38;
  cursor: not-allowed;
}

.edit-mini:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 1px;
}

/* 删除用警示色，跟"上移下移"区分开 —— 它是个会造成损失的动作 */
.edit-del:hover:not(:disabled) {
  color: var(--bad);
  border-color: var(--bad);
  background: var(--bad-soft);
}

.edit-empty {
  margin: 0 0 10px;
  color: var(--ink-3);
  font-size: 12.5px;
  line-height: 1.7;
}

.edit-add {
  margin-top: 2px;
  padding: 6px 14px;
  font-size: 13.5px;
  white-space: nowrap;
}

.edit-actions {
  display: flex;
  flex-wrap: wrap;   /* 列宽不够时让按钮换行，而不是把它们压扁到"完/成"竖排 */
  gap: 8px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}

/* 编辑块里的按钮要"缩得住"：
   · flex: none —— 不许被压缩（压了就会变成「完\n成」这种竖排字，很丑）
   · 内边距比主按钮小一号 —— 这里只是单元格里的小操作，
     用页面顶部那种大按钮的尺寸会把这一格占满 */
.edit-actions .btn {
  flex: none;
  padding: 8px 20px;
  font-size: 14px;
  white-space: nowrap;
}

/* 窄屏（Day 15）：编辑块塞不进"当天大致安排"这一列。
   这一列在 420px 宽的屏幕上只剩一百多像素，输入框 + 三个小按钮
   再怎么挤也放不下 —— 实测会被顶出屏幕右侧，用户看不见按钮。

   处理办法：让一条安排【竖着排】——
     · 输入框独占一行（宽度就跟列宽走，不再硬撑）
     · ↑↓× 三个按钮挪到下一行、靠右对齐（还是"属于这一条"，不会和上一条混）
   为什么不让编辑块整体横向溢出、靠表格滚动条看：
     横向滚动的表格里再套一个要滚的东西，手机上就是"两指怎么划都不对"，
     宁可竖着排、牺牲一点紧凑。 */
@media (max-width: 560px) {
  .edit-line {
    display: block;          /* 从 flex 换成块：两行自然堆叠 */
  }
  .edit-input {
    width: 100%;
    box-sizing: border-box;  /* 不加这条，100% 宽 + 左右内边距会溢出 */
    font-size: 13.5px;
    padding: 6px 9px;
  }
  .edit-tools {
    display: flex;
    justify-content: flex-end;   /* 按钮靠右，视觉上仍贴着上面那个输入框 */
    margin-top: 5px;
  }
  .edit-list { padding: 10px 11px; }
  .edit-mini { width: 26px; height: 26px; }
  .edit-tip { font-size: 12px; padding: 7px 10px; }
}

/* 编辑态那一块是"点一下就冒出来"的，对动效敏感的用户会觉得突兀，
   这里跟着全局规矩一起关掉（无障碍要求，不是可选项）。 */
@media (prefers-reduced-motion: reduce) {
  .edit-list { animation: none; }
}

/* ============================================================
   结果区视图切换栏（Day 13）

   三个标签：整体方案 / 每日行程 / 账本。
   外形跟 Day 12 的筛选栏是一家人（同一套圆角和间距），
   但观感更"重"一点 —— 它是页面级导航，不是列表内的小筛选。
   ============================================================ */

.view-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0 0 20px;
  padding: 6px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--surface-2);
  /* 跟着页面滚时贴在顶上，切完视图不用再滚回去找标签。
     为什么用 sticky 而不是 fixed：fixed 会脱离文档流、把内容顶掉，
     sticky 保持占位，只是"滚到顶就停住"。 */
  position: sticky;
  top: 12px;
  z-index: 20;
}

/* ⚠️ 这里必须补一条 [hidden] 兜底：
   作者样式里的 display: flex 优先级高于浏览器默认的 [hidden] { display: none }，
   不写这条，没结果的时候标签栏照样会露出来。
   （Day 10 给 .confirm-bar 写 display: flex 时踩过同一个坑。） */
.view-tabs[hidden] { display: none; }

.view-tab {
  font: inherit;
  font-size: 14.5px;
  line-height: 1.4;
  padding: 8px 22px;
  border: 1px solid transparent;
  border-radius: 10px;
  background: transparent;
  color: var(--ink-2);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              transform var(--dur-fast) var(--ease);
}

.view-tab:hover {
  background: var(--hover);
  color: var(--ink);
}

/* 选中态：实心填充（跟筛选栏一个理由 —— 空心/实心的差别色弱用户也分得出来）。
   放在 :hover 后面，特异性相同时后写的赢。 */
.view-tab.is-on {
  background: var(--brand);
  color: var(--on-brand);
  font-weight: 600;
  box-shadow: var(--shadow-lift);
}

.view-tab.is-on:hover {
  background: var(--brand);
  color: var(--on-brand);
  transform: translateY(-1px);
}

/* 焦点框只给键盘（鼠标点完不留圈） */
.view-tab:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

/* 窄屏：三个标签平分一整行，字略小 */
@media (max-width: 560px) {
  .view-tabs { gap: 4px; padding: 5px; top: 8px; }
  .view-tab { flex: 1; padding: 8px 6px; font-size: 13.5px; text-align: center; }
}

/* ============================================================
   错误状态卡片（Day 13）

   以前输入不合格只有字段下面一行红字，结果区默默退回"空"——
   用户看不到"错误"这个状态本身，只看到页面又空了。
   这张卡片就是为了让错误有个看得见的样子：哪几项不对 + 怎么改。
   ============================================================ */

.error-card {
  /* 左边一条警示竖线，跟确认条（Day 10）用同一套视觉语言 */
  box-shadow: inset 4px 0 0 0 var(--bad);
}

/* 顶部那条 3px 彩边也要换成红色。
   为什么：.card::after 默认走 --edge（主色蓝/青紫渐变），
   一张"出错了"的卡片顶着蓝色细边，跟卡片里的红字、红感叹号唱反调。 */
.error-card::after {
  background: var(--bad);
}

.error-box {
  display: flex;
  gap: 14px;
  align-items: flex-start;
}

/* 左侧的感叹号圆标：形状 + 颜色双重提示，不只靠颜色 */
.error-mark {
  flex: none;
  width: 26px;
  height: 26px;
  line-height: 26px;
  text-align: center;
  border-radius: 50%;
  background: var(--bad);
  color: var(--on-bad);
  font-weight: 700;
  font-size: 15px;
}

.error-body { min-width: 0; }

.error-title {
  margin: 3px 0 10px;
  font-size: 16.5px;
  font-weight: 600;
  color: var(--ink);
}

.error-list {
  margin: 0;
  padding-left: 20px;
  font-size: 15px;
  line-height: 1.75;
  color: var(--ink-2);
}

.error-list b {
  color: var(--ink);
  font-weight: 600;
}

.error-tip {
  margin: 12px 0 0;
  font-size: 13.5px;
  color: var(--ink-3);
}

@media (max-width: 560px) {
  .error-box { gap: 11px; }
  .error-mark { width: 23px; height: 23px; line-height: 23px; font-size: 14px; }
  .error-title { font-size: 15.5px; }
  .error-list { font-size: 14.5px; }
}

/* ============================================================
   关键帧动画库（Day 8 追加）

   全部集中在这里，方便一处查看、一处调整。
   规则：只用 transform / opacity / filter，不改尺寸和位置属性。
   ============================================================ */

/* --- 共用 --- */

/* 从下方 14px 淡入上浮：结果区逐块出现用 */
/* 展开的建议：轻轻从上往下浮出来（Day 11 追加）
   位移只给 4px —— 它是"补充说明"，不是新内容，不该从远处飞进来抢注意力 */
@keyframes more-open {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes fade-up {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* 轻轻弹一下：城市标签被添加时用 */
@keyframes pop-in {
  0%   { opacity: 0; transform: scale(.72); }
  62%  { opacity: 1; transform: scale(1.08); }
  100% { opacity: 1; transform: scale(1); }
}

/* 一道光从左扫到右：按钮和选中卡片的"流光" */
@keyframes sweep {
  from { transform: translateX(-120%); }
  to   { transform: translateX(320%); }
}

/* 顶端进度条：从 0 涨到 100% 再淡出 */
@keyframes bar-run {
  0%   { transform: scaleX(0);   opacity: 1; }
  72%  { transform: scaleX(1);   opacity: 1; }
  100% { transform: scaleX(1);   opacity: 0; }
}

/* 轻微呼吸（透明度）：用于"还活着"的感觉 */
@keyframes breathe {
  from { opacity: .55; }
  to   { opacity: 1; }
}

/* --- 明亮主题专用 --- */

/* 标题区的三团柔光各自漂移，三个方向不同，显得随机、不生硬 */
@keyframes drift-a {
  from { transform: translate(0, 0) scale(1); }
  to   { transform: translate(70px, 40px) scale(1.14); }
}

@keyframes drift-b {
  from { transform: translate(0, 0) scale(1.06); }
  to   { transform: translate(-60px, 52px) scale(.94); }
}

@keyframes drift-c {
  from { transform: translate(0, 0) scale(.94); }
  to   { transform: translate(44px, 64px) scale(1.12); }
}

/* --- 纸飞机航线（Day 8 追加） --- */

/* 三张纸飞机各自沿一个小范围飘动，像被风托着。
   三组周期和方向都不同，是为了不让它们"一起齐步走"。 */
@keyframes plane-float-a {
  0%, 100% { transform: translate(0, 0) rotate(0deg); }
  50%      { transform: translate(-16px, -13px) rotate(-3.5deg); }
}

@keyframes plane-float-b {
  0%, 100% { transform: translate(0, 0) rotate(0deg); }
  50%      { transform: translate(13px, 11px) rotate(3deg); }
}

@keyframes plane-float-c {
  0%, 100% { transform: translate(0, 0) rotate(0deg); }
  50%      { transform: translate(-9px, 14px) rotate(2.5deg); }
}

/* --- 赛博主题专用 --- */

/* 网格往上流一格，接上时看不出接缝，像电路在通电 */
@keyframes grid-flow {
  from { background-position: 0 0, 0 0; }
  to   { background-position: 0 -44px, -44px 0; }
}

/* 霓虹线左右呼吸 */
@keyframes neon-breathe {
  0%, 100% { opacity: .55; filter: saturate(1); }
  50%      { opacity: 1;   filter: saturate(1.5) brightness(1.22); }
}

/* 卡片青色光晕脉冲 */
@keyframes glow-pulse {
  0%, 100% { box-shadow: var(--shadow); }
  50%      { box-shadow: var(--shadow), 0 0 0 1px rgba(0, 229, 255, .18); }
}

/* 文字带一点青色微光晃动（金额、标题用） */
@keyframes text-glow {
  0%, 100% { text-shadow: 0 0 10px rgba(0, 229, 255, .30); }
  50%      { text-shadow: 0 0 18px rgba(0, 229, 255, .62); }
}


/* ============================================================
   2026-10-27 新增：出行工具箱 / 确认锁定 / 总览图
   三段界面来自同一次用户反馈，样式也放一起，方便下一次改的时候一眼找全。
   ============================================================ */

/* ---------- ① 出行工具箱（跳抖音/小红书/12306/美团…的集中入口） ---------- */
.toolbox-kw {
  margin: 0 0 12px;
  font-size: 13px;
  line-height: 1.7;
  color: var(--ink-2);
}
.toolbox-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(196px, 1fr));
  gap: 10px;
}
.tool-btn {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  color: var(--ink);
  text-decoration: none;
  transition: border-color .16s ease, transform .16s ease;
}
.tool-btn:hover {
  border-color: var(--brand);
  transform: translateY(-1px);
}
.tool-btn:focus-visible {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-soft);
}
.tool-icon {
  font-size: 21px;
  line-height: 1;
  flex: none;
}
.tool-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.tool-body b {
  font-size: 13.5px;
  font-weight: 600;
}
.tool-body small {
  font-size: 11.5px;
  color: var(--ink-2);
}

/* ---------- ② 确认区（不可逆动作，警告写在按钮上面） ---------- */
.confirm-final {
  margin-top: 18px;
  padding: 16px;
  border: 1px solid var(--border);
  border-left: 4px solid var(--brand);
  border-radius: 10px;
  background: var(--surface);
}
.confirm-final[hidden] { display: none; }
.confirm-final-warn {
  margin: 0 0 12px;
  font-size: 13.5px;
  line-height: 1.85;
  color: var(--ink);
}
.confirm-final-warn b { color: var(--bad, #b4322b); }
.btn-lg {
  padding: 13px 28px;
  font-size: 15.5px;
}

/* 已确认后的状态条 */
.locked-bar {
  margin-top: 18px;
  padding: 14px 16px;
  border: 1px solid var(--good, #2f7d4e);
  border-left: 4px solid var(--good, #2f7d4e);
  border-radius: 10px;
  background: var(--good-soft, #eef8f1);
}
.locked-bar[hidden] { display: none; }
.locked-text {
  margin: 0 0 10px;
  font-size: 13.5px;
  line-height: 1.75;
  color: var(--ink);
}
.locked-actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

/* 确认方案的二次确认条：比"清空"那句话长，让文字占主位 */
.confirm-final-bar { align-items: flex-start; }
.confirm-final-bar .confirm-text {
  flex: 1;
  line-height: 1.75;
}

/* ---------- ③ 总览图 ---------- */
.overview-wrap {
  margin-top: 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: auto;
  max-height: 78vh;
  background: #ffffff;      /* 画布底色固定白：图片要发给别人看，不跟主题走 */
}
#overview-canvas {
  display: block;
  width: 100%;
  max-width: 760px;
  height: auto;
  margin: 0 auto;
}
.overview-actions {
  margin-top: 14px;
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.overview-hint {
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--ink-2);
}

/* ---------- ④ 锁定态：编辑入口统一灰掉 ---------- */
/* 为什么用 body 上一个 class 统一管，而不是在每个渲染函数里各写一份 if：
   要锁的地方有十来处（安排编辑、花费编辑、换段、换方案、加地点、删地点…），
   逐处写 if 只要漏一个就留一个后门，而且很难测全。 */
body.is-locked .edit-btn,
body.is-locked .cost-edit-btn,
body.is-locked .btn-del,
body.is-locked .self-add,
body.is-locked .plan-card .btn,
body.is-locked .plan-legs select.leg-swap {
  opacity: .45;
  pointer-events: none;
  cursor: not-allowed;
}
/* 灰掉之后得告诉人"为什么点不动"，否则就是让人猜 */
body.is-locked #block-itinerary .card-note::after {
  content: "（方案已确认，编辑已锁定 —— 要改请点下方「重新规划」）";
  color: var(--bad, #b4322b);
  font-weight: 600;
}

/* 「已锁定」小标记：确认后替换掉原来的「编辑」「改」按钮 */
.lock-tag {
  margin-left: 6px;
  padding: 1px 7px;
  font-size: 11px;
  border-radius: 4px;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--ink-2);
  cursor: help;
  white-space: nowrap;
}

/* ============================================================
   旅行档案 + 账单回填（2026-10-28）
   档案区挂在 main 外面（footer 上面），不跟着视图切换走：
   有档案才出现，刷新 / 清空重填之后历史还在。 */
.archive-area { margin: 26px auto 8px; }

.archive-card { margin-top: 18px; }
.archive-card h2 { margin-top: 0; }

/* 档案列表：一行一份档案（城市 · 天数 + 存档时间 + 状态） */
.archive-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 4px;
  border-top: 1px solid var(--border);
  flex-wrap: wrap;
}
.archive-item:first-child { border-top: 0; }
.archive-main { min-width: 0; flex: 1; }
.archive-title { font-weight: 700; font-size: 16px; }
.archive-meta { margin-top: 3px; font-size: 13px; color: var(--ink-3); }
.archive-ops { display: flex; gap: 8px; flex-shrink: 0; }

.btn-sm { padding: 6px 14px; font-size: 13px; }

/* 账单回填编辑器 */
.actual-list { margin: 6px 0 14px; }
.actual-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 10px 0;
  border-bottom: 1px dashed var(--border);
}
.actual-info { min-width: 0; }
.actual-label { font-weight: 600; }
.actual-detail { margin-top: 2px; font-size: 13px; color: var(--ink-3); }
.actual-input {
  width: 130px;
  padding: 9px 12px;
  font-size: 16px;
  border: 1.5px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink);
  text-align: right;
  flex-shrink: 0;
}
.actual-input:focus { outline: none; border-color: var(--brand, #2a5fe0); }

/* 感悟两栏：推荐 / 避雷 */
.sense-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin: 14px 0; }
.sense-col label { display: block; font-size: 14px; font-weight: 600; margin-bottom: 6px; }
.sense-col textarea {
  width: 100%;
  box-sizing: border-box;
  padding: 10px 12px;
  font-size: 15px;
  line-height: 1.55;
  border: 1.5px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink);
  resize: vertical;
  font-family: inherit;
}
.sense-col textarea:focus { outline: none; border-color: var(--brand, #2a5fe0); }

.archive-actions { display: flex; gap: 10px; flex-wrap: wrap; margin: 6px 0 4px; }

/* 窄屏：感悟两栏并一栏，输入框收窄 */
@media (max-width: 640px) {
  .sense-grid { grid-template-columns: 1fr; }
  .actual-input { width: 110px; }
  .archive-ops { width: 100%; }
  .archive-ops .btn { flex: 1; }
}

/* 实际行程回填（每天一行）+ 计划外花销（2026-10-28 补） */
.archive-days { margin: 8px 0 16px; }
.day-edit-row { padding: 10px 0; border-bottom: 1px dashed var(--border); }
.day-edit-label { font-weight: 600; font-size: 14px; margin-bottom: 6px; }
.day-edit-input {
  width: 100%;
  box-sizing: border-box;
  padding: 9px 12px;
  font-size: 15px;
  line-height: 1.55;
  border: 1.5px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink);
  resize: vertical;
  font-family: inherit;
}
.day-edit-input:focus { outline: none; border-color: var(--brand, #2a5fe0); }

.extra-cost-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin: 6px 0 2px;
}
.extra-cost-tip { font-size: 13px; color: var(--ink-3); }
.extra-name {
  flex: 1;
  min-width: 0;
  padding: 9px 12px;
  font-size: 15px;
  border: 1.5px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink);
  font-family: inherit;
}
.extra-name:focus { outline: none; border-color: var(--brand, #2a5fe0); }
.extra-del { flex-shrink: 0; }

/* 已确认条上的档案提示 + 跳转后闪一下（2026-10-28） */
.locked-archive-tip { font-size: 13px; color: var(--ink-3); }
.archive-area.is-flash .archive-card {
  animation: archiveFlash 1.5s ease;
}
@keyframes archiveFlash {
  0%   { box-shadow: 0 0 0 0 rgba(42, 95, 224, 0); }
  25%  { box-shadow: 0 0 0 6px rgba(42, 95, 224, 0.20); }
  100% { box-shadow: 0 0 0 0 rgba(42, 95, 224, 0); }
}

/* 真实感受一栏（2026-10-28） */
.sense-solo { margin: 0 0 14px; }
.sense-solo label { display: block; font-size: 14px; font-weight: 600; margin-bottom: 6px; }
.sense-solo textarea {
  width: 100%;
  box-sizing: border-box;
  padding: 10px 12px;
  font-size: 15px;
  line-height: 1.55;
  border: 1.5px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink);
  resize: vertical;
  font-family: inherit;
}
.sense-solo textarea:focus { outline: none; border-color: var(--brand, #2a5fe0); }
