/* ============================================================
   园村 · AR 智慧导览   前端样式
   宣纸底 · 墨色字 · 朱砂印
   ------------------------------------------------------------
   设计稿是「浅色水墨」——米黄宣纸 + 淡墨山水，
   所以整套配色改成浅底深字，别再压暗。
   ============================================================ */

/* ------------------------------------------------------------
   华文行楷 —— 随 App 一起打包
   安卓机没有这个字体，不内嵌的话标题会退化成系统黑体，江湖体全丢。
   （个人测试版自用；日后若对外发布请换成免费商用字体）
   ------------------------------------------------------------ */
@font-face{
  font-family:"YuanCunBrush";
  src:url("../assets/fonts/STXINGKA.TTF") format("truetype");
  font-weight:400;
  font-style:normal;
  font-display:swap;
}

:root{
  --paper:      #F6EDD0;   /* 宣纸（取自设计稿四边实际取色） */
  --paper-2:    #FBF5E2;
  --ink:        #1F1B14;   /* 墨 */
  --ink-2:      #3A3226;
  --ink-soft:   rgba(31,27,20,.62);
  --ink-faint:  rgba(31,27,20,.34);
  --cinnabar:   #B0392B;   /* 朱砂 */
  --cinnabar-lt:#C9503C;

  /* 江湖体优先序：内嵌行楷 → 系统行楷 → 方正舒体 → 华文楷体 → 楷体 */
  --font-brush: "YuanCunBrush","STXingkai","华文行楷","FZShuTi","方正舒体","STKaiti","华文楷体","KaiTi","楷体","Songti SC",serif;
  --font-song:  "STSong","华文宋体","Source Han Serif SC","Noto Serif SC","SimSun","宋体",serif;

  --ease-out: cubic-bezier(.22,1,.36,1);
  --ease-io:  cubic-bezier(.4,0,.2,1);

  /* 设计稿是 637x1095；cover 铺满时纵向比例正好 1:1，方便按百分比定位 */
  --shot-ratio: 0.5817;
}

*,*::before,*::after{ box-sizing:border-box; margin:0; padding:0; }

html,body{ height:100%; }

html{
  -webkit-text-size-adjust:100%;
  background:var(--paper);
}

body{
  display:flex; align-items:center; justify-content:center;
  background:#CFC4A4;
  font-family:var(--font-song);
  color:var(--ink);
  overflow:hidden;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  /* 原生 App 手感：禁回弹、禁双指缩放、禁长按菜单 */
  overscroll-behavior:none;
  touch-action:manipulation;
  -webkit-user-select:none;
  user-select:none;
  -webkit-touch-callout:none;
}

/* ------------------------------------------------------------
   舞台：手机端满屏，桌面端套壳
   ------------------------------------------------------------ */
.app{
  position:relative;
  width:100vw;
  height:100vh;
  height:100dvh;      /* 移动端动态视口，避免状态栏导致高度跳动 */
  overflow:hidden;
  background:var(--paper);
  isolation:isolate;
}

@media (min-width:560px){
  .app{
    width:min(392px, calc(100vh * 9 / 16));
    aspect-ratio:9 / 16;
    height:auto; max-height:100vh;
    border-radius:30px;
    box-shadow:
      0 60px 140px -40px rgba(60,48,24,.55),
      0 0 0 1px rgba(31,27,20,.10);
  }
}

/* 宣纸颗粒 */
.grain{
  position:absolute; inset:0; z-index:8;
  pointer-events:none;
  opacity:.05;
  mix-blend-mode:multiply;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
  background-size:160px 160px;
}

/* ============================================================
   ① 四张设计稿：依次淡入淡出
   ============================================================ */
.stage{ position:absolute; inset:0; z-index:1; overflow:hidden; }

.shot{
  position:absolute; inset:0;
  background-color:var(--paper);
  background-size:cover;          /* 637x1095 cover 到 9:16 会裁掉左右各约 9% */
  background-position:center center;
  background-repeat:no-repeat;
  opacity:0;
  transition:opacity 1.5s var(--ease-io);
  will-change:opacity;
}
.shot[data-n="1"]{ background-image:url("../assets/stage-1.png"); }
.shot[data-n="2"]{ background-image:url("../assets/stage-2.png"); }
.shot[data-n="3"]{ background-image:url("../assets/stage-3.png"); }
.shot[data-n="4"]{ background-image:url("../assets/stage-4.png"); }

.shot.on{ opacity:1; }

/* 第一张：从模糊里缓缓浮现 */
.shot.blur-in{ filter:blur(16px) saturate(.9); transform:scale(1.05);
  transition:opacity 2.2s ease, filter 2.2s ease, transform 2.6s var(--ease-out); }
.shot.blur-in.on{ filter:blur(0) saturate(1); transform:scale(1); }

/* ------------------------------------------------------------
   纸色蒙版：盖掉设计稿上 AI 画的糊字，
   位置正好落在图上「标题带(10-22%)」和「按钮带(85-95%)」
   ------------------------------------------------------------ */
.scrim{
  position:absolute; left:0; right:0; z-index:2;
  pointer-events:none;
  background:var(--paper);
}

.scrim-top{
  top:0; height:28%;
  -webkit-mask-image:linear-gradient(to bottom, #000 0 86%, transparent 100%);
          mask-image:linear-gradient(to bottom, #000 0 86%, transparent 100%);
}

.scrim-bot{
  bottom:0; height:22%;
  -webkit-mask-image:linear-gradient(to top, #000 0 60%, transparent 100%);
          mask-image:linear-gradient(to top, #000 0 60%, transparent 100%);
}

/* 黑场：点击「进入园村」后整屏转黑，图 4 再从黑里浮出来 */
.veil{
  position:absolute; inset:0; z-index:6;
  background:#070908;
  opacity:0;
  pointer-events:none;
  transition:opacity .62s var(--ease-io);
}
.veil.on{ opacity:1; }

/* ============================================================
   ② 入场文字层
   ============================================================ */
.intro{
  position:absolute; inset:0; z-index:20;
  transition:opacity 1.05s var(--ease-io),
             transform 1.05s var(--ease-io),
             filter   1.05s var(--ease-io);
}
.intro.leaving{ opacity:0; transform:scale(1.06); filter:blur(6px); }
.intro.gone{ display:none; }

/* ------------------------------------------------------------
   每个元素按设计稿的「带」绝对定位。
   cover 铺满时纵向是 1:1，所以图上的百分比 = 屏幕上的百分比，直接照搬。
   ------------------------------------------------------------ */
.intro-core{ position:absolute; inset:0; }

/* 朱砂闲章 */
.seal{
  position:absolute;
  top:20%; right:15%;          /* 设计稿：印章带 20–25%，落款压在标题右下 */
  width:46px; height:48px;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  border:1.5px solid rgba(176,57,43,.82);
  border-radius:5px;
  background:rgba(176,57,43,.07);
  color:var(--cinnabar);
  font-family:var(--font-brush);
  font-size:19px; line-height:1.02;
  opacity:0;
  transform:translateY(16px) scale(.88);
  transition:opacity 1s ease .15s, transform 1s var(--ease-out) .15s;
}
.intro.title-in .seal{ opacity:1; transform:none; }

/* 欢迎来到园村 */
.title{
  position:absolute;
  top:11%; left:26px; right:26px;   /* 设计稿：标题带 10–22% */
  display:flex;
  justify-content:center;
  font-family:var(--font-brush);
  font-weight:400;
  font-size:clamp(38px,10.6vw,50px);
  line-height:1.14;
  letter-spacing:.055em;
  text-indent:.055em;
  color:var(--ink);
  text-shadow:0 1px 0 rgba(255,255,255,.5);
}
.title .ch{
  display:inline-block;
  opacity:0;
  transform:translateY(26px) rotate(2.5deg);
  filter:blur(10px);
  transition:opacity .9s ease, transform .9s var(--ease-out), filter .9s ease;
}
.intro.title-in .title .ch{ opacity:1; transform:none; filter:blur(0); }

.title .ch:nth-child(1){ transition-delay:0s;    }
.title .ch:nth-child(2){ transition-delay:.13s;  }
.title .ch:nth-child(3){ transition-delay:.26s;  }
.title .ch:nth-child(4){ transition-delay:.39s;  }
.title .ch:nth-child(5){ transition-delay:.52s;  }
.title .ch:nth-child(6){ transition-delay:.65s;  }

.subtitle{
  position:absolute;
  top:28.6%; left:0; right:0;       /* 印章下方的留白区 */
  margin:0;
  text-align:center;
  font-size:12.5px;
  letter-spacing:.44em; text-indent:.44em;
  color:var(--ink-soft);
  opacity:0;
  transform:translateY(11px);
  transition:opacity .95s ease .62s, transform .95s var(--ease-out) .62s;
}
.intro.title-in .subtitle{ opacity:1; transform:none; }

/* 进入园村 */
.enter-btn{
  position:absolute;
  left:50%; bottom:6.5%;            /* 设计稿：按钮带 85–95% */
  margin:0;
  padding:14px 42px 16px;
  font-family:var(--font-brush);
  font-size:21px;
  letter-spacing:.22em; text-indent:.22em;
  color:var(--ink);
  background:rgba(246,237,208,.72);
  border:1px solid rgba(31,27,20,.42);
  border-radius:2px;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  opacity:0;
  transform:translate(-50%, 22px);
  transition:opacity .95s ease, transform .95s var(--ease-out),
             letter-spacing .38s var(--ease-out),
             border-color .38s ease, background .38s ease, box-shadow .38s ease;
}
.intro.btn-in .enter-btn{ opacity:1; transform:translate(-50%, 0); }

.enter-btn:hover{
  letter-spacing:.3em; text-indent:.3em;
  border-color:var(--cinnabar);
  background:rgba(176,57,43,.10);
  box-shadow:0 0 26px rgba(176,57,43,.22);
}
.enter-btn:active{ transform:translate(-50%, 0) scale(.965); }

.enter-line{
  position:absolute; left:50%; bottom:8px;
  width:0; height:1px;
  transform:translateX(-50%);
  background:linear-gradient(90deg, transparent, var(--cinnabar), transparent);
  transition:width .9s var(--ease-out) .5s;
}
.intro.btn-in .enter-line{ width:66%; }

/* ============================================================
   ③ 主界面（图 4）
   ============================================================ */
.home{
  position:absolute; inset:0; z-index:10;
  opacity:0;
  pointer-events:none;
  transition:opacity 1.3s var(--ease-io);
}
.home.home-in{ opacity:1; pointer-events:auto; }

/* 顶部标题 */
.home-head{
  position:absolute; top:10%; left:0; right:0; z-index:6;   /* 设计稿标题带 10–22% */
  padding:0 28px;
  opacity:0;
  transform:translateY(-14px);
  transition:opacity .9s ease .1s, transform .9s var(--ease-out) .1s;
}
.home.home-in .home-head{ opacity:1; transform:none; }

.home-title{
  display:flex;
  font-family:var(--font-brush);
  font-weight:400;
  font-size:31px;
  letter-spacing:.26em;
  color:var(--ink);
}

.home-sub{
  margin-top:8px;
  font-size:11px;
  letter-spacing:.42em; text-indent:.42em;
  color:var(--ink-soft);
}

.home-rule{
  display:block;
  margin-top:14px;
  width:46px; height:1px;
  background:linear-gradient(90deg, var(--cinnabar), rgba(176,57,43,0));
}

/* ---------- 四个功能入口 ---------- */
.dock{
  position:absolute; left:0; right:0; z-index:7;
  bottom:calc(env(safe-area-inset-bottom, 0px) + 30px);
  display:flex;
  gap:7px;
  padding:0 16px;
}

.dock-item{
  flex:1 1 0;
  min-width:0;
  display:flex; flex-direction:column; align-items:center; gap:8px;
  padding:14px 3px 12px;
  font-family:var(--font-song);
  font-size:11.5px;
  letter-spacing:.05em;
  white-space:nowrap;
  color:var(--ink);
  background:rgba(246,237,208,.80);
  border:1px solid rgba(31,27,20,.20);
  border-radius:14px;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  opacity:0;
  transform:translateY(26px) scale(.93);
  transition:opacity .7s ease, transform .7s var(--ease-out),
             border-color .3s ease, background .3s ease, box-shadow .3s ease;
}
.dock-item.in{ opacity:1; transform:none; }

.dock-item svg{
  width:23px; height:23px;
  fill:none;
  stroke:var(--ink-2);
  stroke-width:1.35;
  stroke-linecap:round;
  stroke-linejoin:round;
  transition:stroke .3s ease, transform .3s var(--ease-out);
}

.dock-item:hover{
  border-color:rgba(176,57,43,.62);
  background:rgba(176,57,43,.10);
  box-shadow:0 10px 26px -10px rgba(176,57,43,.45);
}
.dock-item:hover svg{ stroke:var(--cinnabar); transform:translateY(-2px) scale(1.07); }
.dock-item:active{ transform:scale(.95); }

/* ------------------------------------------------------------
   无障碍 / 低端机降级
   ------------------------------------------------------------ */
@media (prefers-reduced-motion:reduce){
  *{ transition-duration:.35s !important; animation-duration:.35s !important; }
  .shot.blur-in{ filter:none; transform:none; }
}

/* 小屏手机（窄于 360px 的旧机型）收紧一点 */
@media (max-width:359px){
  .dock{ gap:5px; padding:0 10px; }
  .dock-item{ font-size:10.5px; padding:12px 2px 10px; border-radius:12px; }
  .dock-item svg{ width:21px; height:21px; }
  .title{ font-size:34px; }
}

/* 矮屏手机，避免四按钮和标题打架 */
@media (max-height:640px){
  .home-title{ font-size:26px; }
  .home-rule{ margin-top:10px; }
  .dock{ bottom:calc(env(safe-area-inset-bottom, 0px) + 16px); }
  .dock-item{ padding:11px 2px 9px; gap:6px; }
  .enter-btn{ padding:11px 34px 13px; }
}

/* ============================================================
   ④ 集邮图鉴
   ------------------------------------------------------------
   一张「纸」从下往上盖住主界面。顶部返回 + 标题，
   下面四个分类页签，再下面是 3 列的邮票格。
   邮票的齿孔用「宣纸色的小圆点」沿线打一排——不依赖 mask，
   老 WebView 也不会掉。
   ============================================================ */
.sheet{
  position:absolute; inset:0; z-index:30;
  display:flex; flex-direction:column;
  background:var(--paper);
  opacity:0;
  pointer-events:none;
  transform:translateY(4%);
  transition:opacity .5s var(--ease-io), transform .6s var(--ease-out);
}
.sheet.on{ opacity:1; pointer-events:auto; transform:none; }

/* ---------- 顶栏 ---------- */
.sheet-head{
  flex:0 0 auto;
  display:flex; align-items:center; gap:12px;
  padding:calc(env(safe-area-inset-top, 0px) + 18px) 18px 12px;
}

.sheet-back{
  flex:0 0 auto;
  width:34px; height:34px;
  display:flex; align-items:center; justify-content:center;
  background:transparent;
  border:1px solid rgba(31,27,20,.24);
  border-radius:50%;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:border-color .28s ease, background .28s ease;
}
.sheet-back svg{
  width:17px; height:17px;
  fill:none; stroke:var(--ink-2); stroke-width:1.6;
  stroke-linecap:round; stroke-linejoin:round;
}
.sheet-back:hover{ border-color:var(--cinnabar); background:rgba(176,57,43,.09); }
.sheet-back:hover svg{ stroke:var(--cinnabar); }
.sheet-back:active{ transform:scale(.93); }

.sheet-titles{ flex:1 1 auto; min-width:0; }

.sheet-title{
  font-family:var(--font-brush);
  font-weight:400;
  font-size:26px;
  line-height:1.1;
  letter-spacing:.14em;
  color:var(--ink);
}

.sheet-sub{
  margin-top:5px;
  font-size:11px;
  letter-spacing:.24em;
  color:var(--ink-soft);
}

.sheet-seal{
  flex:0 0 auto;
  width:32px; height:34px;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  border:1.5px solid rgba(176,57,43,.78);
  border-radius:4px;
  background:rgba(176,57,43,.07);
  color:var(--cinnabar);
  font-family:var(--font-brush);
  font-size:14px; line-height:1.02;
}

/* ---------- 四个分类页签 ---------- */
.tabs{
  position:relative;
  flex:0 0 auto;
  display:flex;
  margin:4px 18px 0;
  border-bottom:1px solid rgba(31,27,20,.13);
}

.tab{
  flex:1 1 0;
  padding:11px 2px 12px;
  background:transparent;
  border:0;
  font-family:var(--font-song);
  font-size:14px;
  letter-spacing:.2em; text-indent:.2em;
  color:var(--ink-soft);
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:color .3s ease;
}
.tab.on{ color:var(--ink); }
.tab:hover{ color:var(--cinnabar); }

/* 会滑动的朱砂下划线 */
.tab-ink{
  position:absolute; left:0; bottom:-1px;
  height:2px; width:0;
  background:linear-gradient(90deg, rgba(176,57,43,0), var(--cinnabar), rgba(176,57,43,0));
  transition:transform .42s var(--ease-out), width .42s var(--ease-out);
}

/* ---------- 邮票列表 ---------- */
.stamp-scroll{
  flex:1 1 auto;
  min-height:0;
  overflow-y:auto;
  -webkit-overflow-scrolling:touch;
  padding:16px 16px calc(env(safe-area-inset-bottom, 0px) + 26px);
}

.stamp-grid{
  display:grid;
  grid-template-columns:repeat(3, 1fr);
  gap:17px 11px;
}

/* ---------- 单张邮票 ---------- */
.stamp{
  position:relative;
  aspect-ratio:5 / 6;
  display:flex; flex-direction:column;
  padding:9px 8px 8px;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;

  /* 券面：米白 + 四边打一排齿孔
     齿孔画成「比券面深一点的小圆点」，不依赖 mask——
     老 WebView 也能出效果。 */
  background-color:#FFFDF7;
  background-image:
    radial-gradient(circle at 50% 0,    rgba(31,27,20,.17) 0 2.5px, transparent 3.1px),
    radial-gradient(circle at 50% 100%, rgba(31,27,20,.17) 0 2.5px, transparent 3.1px),
    radial-gradient(circle at 0 50%,    rgba(31,27,20,.17) 0 2.5px, transparent 3.1px),
    radial-gradient(circle at 100% 50%, rgba(31,27,20,.17) 0 2.5px, transparent 3.1px);
  background-size:10px 10px;
  background-repeat:repeat-x, repeat-x, repeat-y, repeat-y;
  background-position:0 0, 0 100%, 0 0, 100% 0;
  box-shadow:0 3px 10px -6px rgba(31,27,20,.45);

  transition:transform .3s var(--ease-out), box-shadow .3s ease;
  animation:stampIn .5s var(--ease-out) both;
}

/* 内框细线 */
.stamp::before{
  content:'';
  position:absolute; inset:4px;
  border:1px solid rgba(31,27,20,.16);
  pointer-events:none;
}

@keyframes stampIn{
  from{ opacity:0; transform:translateY(14px) scale(.94); }
  to  { opacity:1; transform:none; }
}

.stamp:active{ transform:scale(.955); }

/* 未集：整体减淡成灰 */
.stamp.locked .stamp-art svg{ stroke:rgba(31,27,20,.30); }
.stamp.locked .stamp-name{ color:var(--ink-faint); }
.stamp.locked .stamp-face{ opacity:.72; }

/* 已集：加一层浅浅的朱砂晕 + 角上落印 */
.stamp.got{
  box-shadow:0 8px 20px -12px rgba(176,57,43,.55);
}
.stamp.got::after{
  content:'集';
  position:absolute; right:5px; bottom:5px;
  width:17px; height:17px;
  display:flex; align-items:center; justify-content:center;
  border:1.2px solid rgba(176,57,43,.85);
  border-radius:3px;
  color:var(--cinnabar);
  font-family:var(--font-brush);
  font-size:11px; line-height:1;
  transform:rotate(-9deg);
  pointer-events:none;
}

.stamp-face{
  flex:1 1 auto;
  min-height:0;
  display:flex; align-items:center; justify-content:center;
  padding:3px 2px 5px;
}

.stamp-art{ width:100%; height:100%; display:block; }
.stamp-art svg{
  width:100%; height:100%;
  fill:none;
  stroke:var(--ink-2);
  stroke-width:1.35;
  stroke-linecap:round;
  stroke-linejoin:round;
  transition:stroke .3s ease;
}
/* 券面里那几笔朱砂（印章、火车烟、茶芽）单独上色 */
.stamp-art .red{ stroke:var(--cinnabar); }
.stamp-art .fill-red{ fill:rgba(176,57,43,.16); stroke:var(--cinnabar); }
.stamp-art .fill-ink{ fill:rgba(31,27,20,.10); }
.stamp-art .txt{
  fill:var(--ink);
  stroke:none;
  font-family:var(--font-brush);
}
.stamp.got .stamp-art .txt{ fill:var(--cinnabar); }

.stamp-name{
  flex:0 0 auto;
  text-align:center;
  font-size:10.5px;
  line-height:1.25;
  letter-spacing:.04em;
  color:var(--ink-2);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}

/* 门匾那类：姓一行、匾文一行，同姓的几张才分得清 */
.stamp-sub{
  flex:0 0 auto;
  margin-top:1px;
  text-align:center;
  font-size:8.5px;
  line-height:1.2;
  letter-spacing:.02em;
  color:var(--ink-faint);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.stamp.locked .stamp-sub{ color:rgba(31,27,20,.26); }

/* ---------- 底部总进度 ---------- */
.stamp-foot{
  margin-top:26px;
  padding:0 4px;
  text-align:center;
}

.foot-bar{
  position:relative;
  height:3px;
  border-radius:2px;
  background:rgba(31,27,20,.10);
  overflow:hidden;
}

.foot-bar i{
  display:block;
  height:100%;
  width:0;
  border-radius:2px;
  background:linear-gradient(90deg, rgba(176,57,43,.55), var(--cinnabar));
  transition:width .5s var(--ease-out);
}

.foot-num{
  margin-top:10px;
  font-size:11.5px;
  letter-spacing:.24em; text-indent:.24em;
  color:var(--ink-soft);
}

.stamp-hint{
  margin-top:14px;
  font-size:11px;
  line-height:1.75;
  letter-spacing:.06em;
  color:rgba(31,27,20,.46);
}

/* 窄屏收一收 */
@media (max-width:359px){
  .stamp-grid{ gap:14px 8px; }
  .stamp{ padding:7px 6px 6px; }
  .stamp-name{ font-size:9.5px; }
  .stamp-sub{ font-size:8px; }
  .sheet-title{ font-size:22px; }
  .tab{ font-size:12.5px; }
}

/* 矮屏：邮票小一点，一屏能看到一整排 */
@media (max-height:640px){
  .sheet-head{ padding-top:calc(env(safe-area-inset-top, 0px) + 12px); }
  .sheet-title{ font-size:22px; }
  .stamp-scroll{ padding-top:12px; }
  .stamp-grid{ gap:13px 9px; }
}

/* ============================================================
   主界面右上角积分
   ============================================================ */
/* 积分：压在四个入口正上方、居中 */
.home-points{
  position:absolute;
  left:0; right:0;
  bottom:calc(env(safe-area-inset-bottom, 0px) + 122px);   /* 30 底距 + 78 按钮高 + 14 间距 */
  z-index:7;
  width:max-content;
  margin:0 auto;
  display:flex; align-items:baseline; gap:5px;
  padding:6px 15px 7px;
  border:1px solid rgba(176,57,43,.42);
  border-radius:3px;
  background:rgba(246,237,208,.72);
  -webkit-backdrop-filter:blur(2px);
  backdrop-filter:blur(2px);
  color:var(--cinnabar);
  opacity:0;
  transform:translateY(10px);
  transition:opacity .8s ease .2s, transform .8s var(--ease-out) .2s;
  pointer-events:none;
}
.home.home-in .home-points{ opacity:1; transform:none; }
.home-points b{
  font-family:var(--font-brush);
  font-weight:400;
  font-size:22px;
  line-height:1;
}
.home-points i{
  font-style:normal;
  font-size:10.5px;
  letter-spacing:.18em;
  opacity:.85;
}

/* ============================================================
   四个分类分开统计
   ============================================================ */
.cat-stats{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:7px 9px;
  margin-bottom:14px;
}

.cat-stat{
  display:flex; align-items:baseline; gap:6px;
  padding:8px 10px 9px;
  border:1px solid rgba(31,27,20,.14);
  border-radius:3px;
  background:rgba(255,253,247,.6);
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:border-color .3s ease, background .3s ease;
}
.cat-stat.on{ border-color:rgba(176,57,43,.5); background:rgba(176,57,43,.06); }
.cat-stat:active{ transform:scale(.985); }

.cs-name{
  font-family:var(--font-song);
  font-size:12px;
  letter-spacing:.16em;
  color:var(--ink-2);
}
.cat-stat.on .cs-name{ color:var(--cinnabar); }

.cs-num{
  margin-left:auto;
  font-size:13px;
  color:var(--ink);
}
.cs-num i{
  font-style:normal;
  font-size:10px;
  color:var(--ink-soft);
}

.cs-pts{
  font-size:10.5px;
  letter-spacing:.04em;
  color:var(--cinnabar);
  min-width:38px; text-align:right;
}

/* ============================================================
   券详情页：上拍照、下介绍
   ============================================================ */
.detail{
  position:absolute; inset:0; z-index:40;
  display:flex; flex-direction:column;
  background:var(--paper);
  opacity:0;
  pointer-events:none;
  transform:translateX(6%);
  transition:opacity .42s var(--ease-io), transform .5s var(--ease-out);
}
.detail.on{ opacity:1; pointer-events:auto; transform:none; }

.detail-body{
  flex:1 1 auto;
  min-height:0;
  display:flex; flex-direction:column;
  gap:14px;
  padding:4px 18px calc(env(safe-area-inset-bottom, 0px) + 20px);
}

/* ---------- 上：拍照 ---------- */
.shotbox{
  position:relative;
  flex:0 0 auto;
  aspect-ratio:4 / 3;
  max-height:46%;
  display:block;
  padding:0;
  overflow:hidden;
  border:1px solid rgba(31,27,20,.22);
  border-radius:4px;
  background:rgba(255,253,247,.72);
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:border-color .3s ease;
}
.shotbox:active{ transform:scale(.99); }

/* 拍到的照片铺在底层 */
.shot-photo{
  position:absolute; inset:0;
  background-size:cover;
  background-position:center;
  opacity:0;
  transition:opacity .5s ease;
}
.shot-photo.has{ opacity:1; }

/* 相机图标 + 提示语，压在上面（有照片时缩到角落） */
.shot-ui{
  position:absolute; inset:0;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:12px;
  color:var(--ink-2);
  transition:opacity .3s ease, transform .3s ease;
}
.shot-ui svg{
  width:38px; height:38px;
  fill:none; stroke:currentColor; stroke-width:1.35;
  stroke-linecap:round; stroke-linejoin:round;
}
.shot-tip{
  font-size:11.5px;
  letter-spacing:.14em;
  text-align:center;
  padding:0 14px;
}

.shotbox.done .shot-ui{
  inset:auto 0 0 0;
  flex-direction:row; justify-content:center; gap:6px;
  padding:9px 8px;
  background:linear-gradient(0deg, rgba(7,9,8,.72), rgba(7,9,8,0));
  color:#F6EDD0;
}
.shotbox.done .shot-ui svg{ width:15px; height:15px; }
.shotbox.done .shot-tip{ font-size:10.5px; letter-spacing:.1em; padding:0; }

/* 已集：右上角落一枚朱砂印 */
.shot-seal{
  position:absolute; top:10px; right:10px;
  width:38px; height:38px;
  display:flex; align-items:center; justify-content:center;
  border:2px solid var(--cinnabar);
  border-radius:50%;
  background:rgba(255,253,247,.86);
  color:var(--cinnabar);
  font-family:var(--font-brush);
  font-size:14px;
  letter-spacing:.02em;
  opacity:0;
  transform:scale(1.7) rotate(-16deg);
  transition:opacity .3s ease, transform .45s cubic-bezier(.22,1,.36,1);
}
.shot-seal.show{ opacity:1; transform:scale(1) rotate(-9deg); }

/* ---------- 下：券面 + 介绍 ---------- */
.detail-info{
  flex:1 1 auto;
  min-height:0;
  display:flex; flex-direction:column;
  overflow-y:auto;
  -webkit-overflow-scrolling:touch;
}

.detail-art{
  flex:0 0 auto;
  width:132px; height:66px;
  align-self:center;
  margin-bottom:8px;
}
.detail-art svg{
  width:100%; height:100%;
  fill:none;
  stroke:var(--ink-2);
  stroke-width:1.35;
  stroke-linecap:round; stroke-linejoin:round;
}
.detail-art .red{ stroke:var(--cinnabar); }
.detail-art .fill-red{ fill:rgba(176,57,43,.16); stroke:var(--cinnabar); }
.detail-art .fill-ink{ fill:rgba(31,27,20,.10); }
.detail-art .txt{ fill:var(--cinnabar); stroke:none; font-family:var(--font-brush); }

.detail-name{
  flex:0 0 auto;
  text-align:center;
  font-family:var(--font-brush);
  font-weight:400;
  font-size:24px;
  letter-spacing:.14em;
  color:var(--ink);
}
.detail-name em{
  margin-left:9px;
  font-style:normal;
  font-size:14px;
  letter-spacing:.1em;
  color:var(--ink-soft);
}

.detail-desc{
  margin-top:12px;
  padding-top:12px;
  border-top:1px solid rgba(31,27,20,.13);
  font-family:var(--font-song);
  font-size:13.5px;
  line-height:1.95;
  letter-spacing:.03em;
  color:var(--ink-2);
  text-align:justify;
}

@media (max-width:359px){
  .home-points{ padding:5px 12px 6px; }
  .home-points b{ font-size:20px; }
  .cat-stats{ gap:6px 7px; }
  .cs-name{ font-size:11px; letter-spacing:.1em; }
  .cs-pts{ font-size:9.5px; min-width:32px; }
  .detail-name{ font-size:21px; }
  .detail-desc{ font-size:12.5px; line-height:1.85; }
}

/* ============================================================
   积分商城
   ============================================================ */
.shop{
  position:absolute; inset:0; z-index:32;
  display:flex; flex-direction:column;
  background:var(--paper);
  opacity:0;
  pointer-events:none;
  transform:translateY(4%);
  transition:opacity .5s var(--ease-io), transform .6s var(--ease-out);
}
.shop.on{ opacity:1; pointer-events:auto; transform:none; }

/* ---------- 钱包条 ---------- */
.wallet{
  flex:0 0 auto;
  display:flex; align-items:flex-end; gap:10px;
  margin:0 18px 14px;
  padding:14px 16px 15px;
  border:1px solid rgba(176,57,43,.34);
  border-radius:4px;
  background:
    radial-gradient(120% 160% at 0% 0%, rgba(176,57,43,.10), rgba(176,57,43,0) 62%),
    rgba(255,253,247,.66);
}

.wallet-main{
  flex:1 1 auto;
  display:flex; align-items:baseline; gap:7px;
  color:var(--cinnabar);
}
.wallet-main b{
  font-family:var(--font-brush);
  font-weight:400;
  font-size:34px;
  line-height:1;
}
.wallet-main i{
  font-style:normal;
  font-size:10.5px;
  letter-spacing:.18em;
  opacity:.85;
}

.wallet-cell{
  flex:0 0 auto;
  display:flex; flex-direction:column; align-items:flex-end; gap:3px;
  padding-left:10px;
  border-left:1px solid rgba(31,27,20,.12);
}
.wallet-cell b{
  font-size:14px;
  font-weight:400;
  color:var(--ink);
  line-height:1;
}
.wallet-cell i{
  font-style:normal;
  font-size:9.5px;
  letter-spacing:.14em;
  color:var(--ink-soft);
}

/* ---------- 商品列表 ---------- */
.shop-scroll{
  flex:1 1 auto;
  min-height:0;
  overflow-y:auto;
  -webkit-overflow-scrolling:touch;
  padding:2px 18px calc(env(safe-area-inset-bottom, 0px) + 24px);
}

.shop-grid{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:16px 11px;
}

.shop-note{
  margin-top:20px;
  text-align:center;
  font-size:10.5px;
  letter-spacing:.08em;
  color:var(--ink-faint);
}

/* ---------- 商品卡 ---------- */
.goods{
  display:flex; flex-direction:column;
  padding:0;
  border:0;
  background:transparent;
  text-align:left;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  animation:goods-in .5s var(--ease-out) both;
}
@keyframes goods-in{
  from{ opacity:0; transform:translateY(10px); }
  to  { opacity:1; transform:none; }
}
.goods:active{ transform:scale(.975); }

.goods-face{
  position:relative;
  display:block;
  aspect-ratio:1 / 1;
  border:1px solid rgba(31,27,20,.16);
  border-radius:3px;
  background:rgba(255,253,247,.72);
  overflow:hidden;
}

.goods-art{
  display:block;
  width:100%; height:100%;
  padding:12%;
  box-sizing:border-box;
}
.goods-art svg{
  width:100%; height:100%;
  fill:none;
  stroke:var(--ink-2);
  stroke-width:1.35;
  stroke-linecap:round; stroke-linejoin:round;
}

/* 积分不够：淡一点，但不藏起来（看得见才有攒分的动力） */
.goods.poor .goods-art svg{ stroke:rgba(31,27,20,.32); }
.goods.poor .goods-face{ opacity:.82; }
.goods.poor .goods-buy{ color:var(--ink-faint); border-color:rgba(31,27,20,.16); }

/* 有实拍图的商品：照片自己带米白底，铺满格子不留白边（2026-10-05 加的） */
.goods-art.has-photo{ padding:0; }
.goods-art img{
  display:block;
  width:100%; height:100%;
  object-fit:cover;
}
/* 积分不够时照片也压暗一点，跟线描那套的规矩一致 */
.goods.poor .goods-art img{ filter:grayscale(.4); opacity:.72; }

.goods-own{
  position:absolute; top:6px; right:6px;
  padding:2px 7px 3px;
  border-radius:2px;
  background:var(--cinnabar);
  color:#F6EDD0;
  font-size:9.5px;
  letter-spacing:.06em;
}

.goods-name{
  margin-top:9px;
  font-family:var(--font-song);
  font-size:13px;
  line-height:1.4;
  color:var(--ink);
}
.goods-sub{
  margin-top:3px;
  font-size:10.5px;
  letter-spacing:.05em;
  color:var(--ink-faint);
}

.goods-foot{
  margin-top:8px;
  display:flex; align-items:center; gap:6px;
}
.goods-pts{
  font-size:10.5px;
  color:var(--cinnabar);
}
.goods-pts b{
  font-family:var(--font-brush);
  font-weight:400;
  font-size:18px;
  line-height:1;
}

.goods-buy{
  margin-left:auto;
  padding:3px 10px 4px;
  border:1px solid rgba(176,57,43,.5);
  border-radius:2px;
  font-size:10.5px;
  letter-spacing:.08em;
  color:var(--cinnabar);
}

/* ---------- 兑换确认 ---------- */
.buy-mask{
  position:absolute; inset:0; z-index:6;
  display:flex; align-items:center; justify-content:center;
  padding:26px;
  background:rgba(7,9,8,.44);
  opacity:0;
  pointer-events:none;
  transition:opacity .32s ease;
}
.buy-mask.on{ opacity:1; pointer-events:auto; }

.buy-card{
  width:100%;
  max-width:300px;
  display:flex; flex-direction:column; align-items:center;
  padding:22px 22px 18px;
  border-radius:4px;
  background:var(--paper);
  box-shadow:0 18px 48px rgba(7,9,8,.3);
  transform:translateY(14px) scale(.97);
  transition:transform .42s var(--ease-out);
}
.buy-mask.on .buy-card{ transform:none; }

.buy-art{
  width:88px; height:88px;
  margin-bottom:14px;
}
.buy-art svg{
  width:100%; height:100%;
  fill:none; stroke:var(--ink-2); stroke-width:1.35;
  stroke-linecap:round; stroke-linejoin:round;
}

/* 实拍图版的兑换弹窗：照片是方图，给它留大一点 */
.buy-art.has-photo{ width:132px; height:132px; }
.buy-art img{
  display:block;
  width:100%; height:100%;
  object-fit:cover;
  border-radius:3px;
}

.buy-name{
  font-family:var(--font-brush);
  font-weight:400;
  font-size:22px;
  letter-spacing:.1em;
  color:var(--ink);
  text-align:center;
}

.buy-desc{
  margin-top:10px;
  font-family:var(--font-song);
  font-size:12.5px;
  line-height:1.9;
  letter-spacing:.02em;
  color:var(--ink-2);
  text-align:justify;
}

.buy-row{
  width:100%;
  margin-top:16px;
  padding-top:13px;
  border-top:1px solid rgba(31,27,20,.13);
  display:flex; align-items:baseline;
}
.buy-price{
  font-size:11px;
  color:var(--cinnabar);
}
.buy-price b{
  font-family:var(--font-brush);
  font-weight:400;
  font-size:26px;
  line-height:1;
}
.buy-own{
  margin-left:auto;
  font-size:10.5px;
  color:var(--ink-faint);
}

.buy-btn{
  width:100%;
  margin-top:14px;
  padding:12px 0 13px;
  border:0;
  border-radius:3px;
  background:var(--cinnabar);
  color:#F6EDD0;
  font-family:var(--font-song);
  font-size:14px;
  letter-spacing:.2em; text-indent:.2em;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:opacity .3s ease, transform .2s ease;
}
.buy-btn:active{ transform:scale(.98); }
.buy-btn.off{
  background:rgba(31,27,20,.16);
  color:rgba(255,255,255,.86);
  cursor:default;
}

.buy-cancel{
  margin-top:9px;
  padding:6px 10px;
  border:0;
  background:transparent;
  font-family:var(--font-song);
  font-size:12px;
  letter-spacing:.14em;
  color:var(--ink-faint);
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}

/* ---------- 提示条 ---------- */
.toast{
  position:absolute;
  left:50%; bottom:calc(env(safe-area-inset-bottom, 0px) + 46px);
  z-index:9;
  padding:9px 18px 10px;
  border-radius:3px;
  background:rgba(7,9,8,.86);
  color:#F6EDD0;
  font-size:12.5px;
  letter-spacing:.1em;
  white-space:nowrap;
  opacity:0;
  pointer-events:none;
  transform:translate(-50%, 12px);
  transition:opacity .32s ease, transform .38s var(--ease-out);
}
.toast.on{ opacity:1; transform:translate(-50%, 0); }

/* 集邮换到美食那一条：得比 z-index 30 的四张 sheet 高一层才看得见，
   而且是会换行的一句话，所以放开 nowrap、给个宽度上限。 */
.bag-toast{
  z-index:40;
  white-space:normal;
  max-width:82%;
  line-height:1.75;
  text-align:center;
}
.bag-toast b{ color:#FFE7A6; font-weight:600; }

@media (max-width:359px){
  .wallet{ margin:0 14px 12px; padding:12px 13px 13px; }
  .wallet-main b{ font-size:29px; }
  .wallet-cell{ padding-left:8px; }
  .shop-scroll{ padding-left:14px; padding-right:14px; }
  .shop-grid{ gap:14px 9px; }
  .goods-name{ font-size:12px; }
  .goods-pts b{ font-size:16px; }
  .goods-buy{ padding:3px 7px 4px; font-size:9.5px; }
  .buy-card{ padding:18px 16px 15px; }
}


/* ============================================================================
   ⑥ 扫一扫 · 听讲解（点底部「AR 实景」滑入）
   摄像头铺满，中间一个取景框，扫到码就从下面推上来一张讲解卡
   ============================================================================ */

.scan{
  position:absolute; inset:0; z-index:36;
  background:#0a0c0a;
  opacity:0; visibility:hidden;
  transform:scale(1.015);
  transition:opacity .36s var(--ease-out), transform .36s var(--ease-out), visibility .36s;
}
.scan.on{ opacity:1; visibility:visible; transform:none; }

.scan-video{
  position:absolute; inset:0;
  width:100%; height:100%;
  object-fit:cover;
  background:#0a0c0a;
}

/* 取景框：完全看不见。
   整屏就是相机画面 —— 不做暗幕挖洞 + 四角框那种二维码扫描器的样子。
   它只负责告诉识别内核「采画面正中央这么一块」，所以它的尺寸就是采样区尺寸。 */
.aim{
  position:absolute; left:50%; top:calc(50% - 26px);
  width:min(66vw, 248px);
  aspect-ratio:1/1;
  transform:translate(-50%,-50%);
  pointer-events:none;
}

/* 按住的时候只把画面微微压暗一点，给个「正在认」的反馈，不加任何框 */
.scan-video{ transition:filter .24s var(--ease-out); }
.scan.aiming .scan-video{ filter:brightness(.88); }

.aim-hint{
  margin:0 0 2px; max-width:80%;
  text-align:center;
  font-size:12.5px; letter-spacing:.08em;
  color:rgba(246,237,208,.72);
  text-shadow:0 1px 7px rgba(0,0,0,.8);
  transition:color .24s;
}
.scan.aiming .aim-hint{ color:var(--cinnabar-lt); }

/* —— 顶栏 —— */
.scan-head{
  position:absolute; left:0; right:0;
  top:calc(env(safe-area-inset-top,0px) + 8px);
  display:flex; align-items:center; gap:10px;
  padding:0 14px;
  z-index:2;
}
.scan-head .sheet-titles{ flex:1; min-width:0; }
.scan-head .sheet-title{ color:var(--paper); }
.scan-head .sheet-sub{ color:rgba(246,237,208,.66); }
.scan-head .sheet-back{
  border-color:rgba(246,237,208,.34);
  color:var(--paper);
  background:rgba(8,10,8,.34);
}
.scan-head .sheet-back svg{ stroke:var(--paper); }

.scan-torch{
  flex:0 0 auto; width:40px; height:40px;
  display:flex; align-items:center; justify-content:center;
  border:1px solid rgba(246,237,208,.34);
  border-radius:50%;
  background:rgba(8,10,8,.34);
  padding:0; cursor:pointer;
  transition:background .22s, border-color .22s;
}
.scan-torch svg{
  width:21px; height:21px;
  fill:none; stroke:var(--paper); stroke-width:1.5;
  stroke-linecap:round; stroke-linejoin:round;
}
.scan-torch.on{
  background:rgba(201,80,60,.72);
  border-color:rgba(246,237,208,.7);
}

/* —— 底栏：中央那颗长按圆钮 —— */
.scan-foot{
  position:absolute; left:0; right:0;
  bottom:calc(env(safe-area-inset-bottom,0px) + 20px);
  display:flex; flex-direction:column; align-items:center; gap:12px;
  z-index:2;
}

.aim-btn{
  position:relative;
  width:78px; height:78px;
  display:flex; align-items:center; justify-content:center;
  padding:0; cursor:pointer;
  border-radius:50%;
  border:2px solid rgba(246,237,208,.72);
  background:radial-gradient(circle at 50% 42%, rgba(246,237,208,.16), rgba(8,10,8,.55) 72%);
  box-shadow:0 6px 22px rgba(0,0,0,.45), inset 0 0 18px rgba(246,237,208,.08);
  -webkit-tap-highlight-color:transparent;
  touch-action:none;
  transition:transform .16s var(--ease-out), border-color .22s, box-shadow .22s, background .22s;
}
.aim-btn-ico{
  width:36px; height:36px;
  fill:none; stroke:var(--paper); stroke-width:1.5;
  stroke-linecap:round; stroke-linejoin:round;
  transition:stroke .22s;
}
/* 一圈往外扩的呼吸波纹，提示「按住」 */
.aim-btn-wave{
  position:absolute; inset:-9px;
  border-radius:50%;
  border:1.4px solid rgba(246,237,208,.26);
  animation:aimWave 2.6s var(--ease-io) infinite;
  pointer-events:none;
}
@keyframes aimWave{
  0%   { transform:scale(.86); opacity:0; }
  30%  { opacity:.85; }
  100% { transform:scale(1.16); opacity:0; }
}

.aim-btn:active,
.scan.aiming .aim-btn{
  transform:scale(.93);
  border-color:var(--cinnabar-lt);
  background:radial-gradient(circle at 50% 42%, rgba(201,80,60,.42), rgba(8,10,8,.62) 74%);
  box-shadow:0 4px 18px rgba(0,0,0,.5), 0 0 26px rgba(201,80,60,.45);
}
.scan.aiming .aim-btn-ico{ stroke:var(--cinnabar-lt); }
.scan.aiming .aim-btn-wave{ animation-duration:1.05s; }

.scan-tip{
  margin:0; font-size:11.5px; letter-spacing:.06em;
  color:rgba(246,237,208,.6);
}
.scan-tip b{ color:var(--paper); font-weight:600; }

.scan-alt{
  font-family:var(--font-brush);
  font-size:13.5px; letter-spacing:.08em;
  color:rgba(246,237,208,.82);
  background:transparent;
  border:0; border-bottom:1px dashed rgba(246,237,208,.34);
  border-radius:0;
  padding:2px 2px 3px;
  cursor:pointer;
  transition:color .22s, border-color .22s;
}
.scan-alt:active{ color:var(--cinnabar-lt); border-color:var(--cinnabar-lt); }

/* —— 讲解卡 —— */
.talk{
  position:absolute; left:0; right:0; bottom:0;
  z-index:3;
  padding:0 14px calc(env(safe-area-inset-bottom,0px) + 14px);
  transform:translateY(102%);
  transition:transform .42s var(--ease-out);
  pointer-events:none;
}
.talk.on{ transform:none; pointer-events:auto; }

.talk-inner{
  position:relative;
  background:linear-gradient(180deg, #FBF6E4 0%, var(--paper) 62%, #EFE3C2 100%);
  border:1px solid rgba(31,27,20,.2);
  border-radius:18px;
  padding:20px 20px 16px;
  box-shadow:0 -12px 34px rgba(6,8,6,.42), inset 0 1px 0 rgba(255,255,255,.6);
  max-height:70vh;
  overflow:auto;
  -webkit-overflow-scrolling:touch;
}
.talk-inner::before,
.talk-inner::after{
  content:''; position:absolute; width:22px; height:22px;
  border:1.4px solid rgba(31,27,20,.34);
}
.talk-inner::before{ top:8px; left:8px;  border-right:0; border-bottom:0; }
.talk-inner::after{ bottom:8px; right:8px; border-left:0; border-top:0; }

.talk-art{
  width:96px; height:96px;
  margin:0 auto 10px;
  display:flex; align-items:center; justify-content:center;
}
.talk-art svg{
  width:100%; height:100%;
  fill:none; stroke:var(--ink);
  stroke-width:1.5;
  stroke-linecap:round; stroke-linejoin:round;
}

/* 景点卡的配图：一张实景照片，横着铺满一条（96px 的方框装不下照片）。
   150px 高 ≈ 2.1:1，正好是那批照片本来的比例（1400×633 是 2.21:1），基本不裁。 */
.talk-art.wide{
  width:100%; height:150px;
  margin:2px 0 12px;
  border-radius:12px;
  overflow:hidden;
  background:rgba(31,27,20,.06);
}
.talk-art.wide img{
  width:100%; height:100%;
  object-fit:cover;
  display:block;
}

.talk-cat{
  margin:0; text-align:center;
  font-size:11px; letter-spacing:.22em;
  color:var(--cinnabar);
}
.talk-name{
  margin:5px 0 0; text-align:center;
  font-family:var(--font-brush);
  font-size:29px; font-weight:400; letter-spacing:.06em;
  color:var(--ink);
}
.talk-sub{
  margin:3px 0 0; text-align:center;
  font-size:12px; letter-spacing:.14em;
  color:var(--ink-faint);
}
.talk-sub:empty{ display:none; }

.talk-text{
  margin:14px 0 0;
  font-family:var(--font-song);
  font-size:14.5px; line-height:1.92;
  letter-spacing:.015em;
  text-align:justify;
  color:var(--ink);
  text-indent:2em;
}

.talk-row{
  display:flex; gap:10px;
  margin-top:18px;
}
.talk-btn{
  flex:1;
  font-family:var(--font-brush);
  font-size:16px; letter-spacing:.1em;
  padding:11px 0 12px;
  border-radius:999px;
  cursor:pointer;
  transition:opacity .2s, background .2s, color .2s;
}
.talk-btn.main{
  background:var(--cinnabar);
  color:#FBF6E4;
  border:1px solid var(--cinnabar);
}
.talk-btn.main:active{ background:var(--cinnabar-lt); }
.talk-btn.ghost{
  background:transparent;
  color:var(--ink);
  border:1px solid rgba(31,27,20,.34);
}
.talk-btn.ghost:active{ background:rgba(31,27,20,.07); }

/* —— 认不出来时的「挑一处」单 —— */
.pick{
  position:absolute; inset:0; z-index:4;
  display:flex; align-items:flex-end;
  background:rgba(6,8,6,.62);
  backdrop-filter:blur(3px);
  opacity:0; visibility:hidden;
  transition:opacity .3s var(--ease-out), visibility .3s;
}
.pick.on{ opacity:1; visibility:visible; }
.pick-inner{
  width:100%;
  max-height:76%;
  display:flex; flex-direction:column;
  padding:20px 18px calc(env(safe-area-inset-bottom,0px) + 18px);
  border-radius:22px 22px 0 0;
  background:linear-gradient(180deg, #FBF6E4 0%, var(--paper) 100%);
  box-shadow:0 -12px 34px rgba(0,0,0,.4);
  transform:translateY(24px);
  transition:transform .34s var(--ease-out);
}
.pick.on .pick-inner{ transform:none; }

.pick-head{
  margin:0 0 14px;
  font-family:var(--font-brush);
  font-size:17px; letter-spacing:.08em;
  color:var(--ink);
  text-align:center;
}

.pick-grid{
  flex:1; min-height:0;
  overflow-y:auto;
  -webkit-overflow-scrolling:touch;
  display:grid;
  grid-template-columns:repeat(3, 1fr);
  gap:10px;
  padding-bottom:6px;
}
.pick-item{
  display:flex; flex-direction:column; align-items:center; gap:5px;
  padding:8px 4px 9px;
  border:1px solid rgba(31,27,20,.18);
  border-radius:12px;
  background:rgba(255,255,255,.5);
  cursor:pointer;
  transition:background .2s, border-color .2s, transform .12s var(--ease-out);
}
.pick-item:active{
  transform:scale(.96);
  background:rgba(176,57,43,.1);
  border-color:rgba(176,57,43,.5);
}
.pick-art{
  width:52px; height:52px;
  display:flex; align-items:center; justify-content:center;
}
.pick-art svg{ width:100%; height:100%; }
/* 挑选项里带实景照片的（景点那条） */
.pick-art img{
  width:100%; height:100%;
  object-fit:cover; border-radius:8px; display:block;
}
.pick-name{
  font-size:11.5px; letter-spacing:.04em;
  color:var(--ink-soft);
  text-align:center; line-height:1.3;
}

.pick .talk-btn.ghost{ margin-top:14px; flex:0 0 auto; }

/* 识别设置：离线优先，认不准才问云端 */
.cfg{
  position:absolute; inset:0; z-index:7;
  display:flex; align-items:center; justify-content:center;
  padding:0 16px calc(env(safe-area-inset-bottom,0px) + 16px);
  background:rgba(6,8,6,.66);
  backdrop-filter:blur(3px);
  opacity:0; visibility:hidden;
  transition:opacity .28s var(--ease-out), visibility .28s;
}
.cfg.on{ opacity:1; visibility:visible; }
.cfg-inner{
  width:100%; max-width:360px;
  max-height:86%;
  overflow-y:auto;
  -webkit-overflow-scrolling:touch;
  display:flex; flex-direction:column;
  padding:20px 18px 18px;
  border-radius:18px;
  background:linear-gradient(180deg, #FBF6E4 0%, var(--paper) 100%);
  box-shadow:0 18px 44px rgba(0,0,0,.46);
  transform:translateY(16px) scale(.98);
  transition:transform .32s var(--ease-out);
}
.cfg.on .cfg-inner{ transform:none; }

.cfg-title{
  margin:0 0 16px;
  font-family:var(--font-brush);
  font-size:20px; letter-spacing:.1em;
  color:var(--ink);
  text-align:center;
}

.cfg-label{
  margin:14px 0 8px;
  font-size:11.5px; letter-spacing:.16em;
  color:var(--cinnabar);
}
.cfg-label:first-of-type{ margin-top:0; }

.cfg-note{
  margin:0 0 8px;
  font-size:11px; line-height:1.55;
  color:var(--ink-soft, #6b6252);
  opacity:.85;
}

.cfg-radios{ display:flex; flex-direction:column; gap:8px; }
.cfg-radios label{
  display:flex; align-items:flex-start; gap:10px;
  padding:10px 12px;
  border:1px solid rgba(31,27,20,.18);
  border-radius:12px;
  background:rgba(255,255,255,.5);
  cursor:pointer;
  transition:background .2s, border-color .2s;
}
.cfg-radios label:has(input:checked){
  background:rgba(176,57,43,.09);
  border-color:rgba(176,57,43,.5);
}
.cfg-radios input{
  flex:0 0 auto;
  margin:3px 0 0;
  width:15px; height:15px;
  accent-color:var(--cinnabar);
}
.cfg-radios span{
  flex:1; min-width:0;
  font-size:14px; line-height:1.35;
  color:var(--ink);
}
.cfg-radios em{
  display:block;
  margin-top:2px;
  font-style:normal;
  font-size:11px; line-height:1.4;
  color:var(--ink-soft);
  opacity:.78;
}

.cfg-input{
  width:100%;
  margin-bottom:8px;
  padding:10px 12px;
  border:1px solid rgba(31,27,20,.2);
  border-radius:10px;
  background:rgba(255,255,255,.62);
  font-family:inherit;
  font-size:13px;
  color:var(--ink);
  -webkit-appearance:none;
}
.cfg-input::placeholder{ color:rgba(58,50,38,.42); }
.cfg-input:focus{
  outline:none;
  border-color:rgba(176,57,43,.6);
  background:rgba(255,255,255,.85);
}

.cfg-row{ display:flex; gap:10px; margin-top:12px; }
.cfg-row .talk-btn{ flex:1; }

.cfg-state{
  margin:12px 0 0;
  padding:9px 11px;
  border-radius:9px;
  background:rgba(31,27,20,.05);
  font-size:11.5px; line-height:1.5;
  color:var(--ink-soft);
  word-break:break-all;
}
.cfg-state:empty{ display:none; }

@media (max-width:359px){
  .talk-inner{ padding:17px 16px 14px; }
  .talk-art{ width:80px; height:80px; }
  .talk-name{ font-size:25px; }
  .talk-text{ font-size:13.5px; line-height:1.86; }
  .talk-btn{ font-size:15px; }
  .aim{ width:min(66vw, 208px); }
  .aim-btn{ width:70px; height:70px; }
  .aim-btn-ico{ width:32px; height:32px; }
  .pick-grid{ grid-template-columns:repeat(3, 1fr); gap:8px; }
  .pick-art{ width:44px; height:44px; }
}

@media (max-height:640px){
  .talk-art{ display:none; }
  .talk-text{ font-size:13px; line-height:1.8; }
  .aim{ margin-bottom:2vh; width:min(50vh, 190px); }
  .aim-btn{ width:64px; height:64px; }
  .aim-btn-ico{ width:30px; height:30px; }
  .aim-hint{ display:none; }
}


/* ============================================================================
   桌宠物「小笋」（www/js/pet.js）
   主界面右下角常驻。z-index 8 —— 在 .dock 的 7 之上、在 .intro 的 20 之下，
   所以进场动画时它不会抢镜，四个 sheet 打开时自然被盖住。
   ============================================================================ */

.pet{
  position:absolute;
  right:14px;
  bottom:calc(env(safe-area-inset-bottom, 0px) + 118px);
  --pet-w:92px;          /* 唯一的尺寸来源：改这一个值，宽高一起跟着走 */
  width:var(--pet-w); height:98px;
  z-index:8;
  cursor:pointer;
  touch-action:none;
  -webkit-tap-highlight-color:transparent;
  -webkit-user-select:none; user-select:none;
  opacity:0;
  transform:translateY(20px) scale(.86);
  transition:opacity .6s ease, transform .55s var(--ease-out);
  filter:drop-shadow(0 6px 15px rgba(0,0,0,.42));
}
.home.home-in .pet{ opacity:1; transform:none; }
.pet.gone{ opacity:0 !important; pointer-events:none; }
/* 定时露脸的宠物藏起来的样子（pet.json 的 visit 块 → pet.js 的 setAway）。
   必须带 !important：`.home.home-in .pet{opacity:1}` 是 (0,3,0)，
   `.pet.pet-away` 只有 (0,2,0)，不加会被它盖回去，宠物藏不住。 */
.pet.pet-away{ opacity:0 !important; pointer-events:none; }
/* 拖动中关掉过渡，手才跟得住；松手时把 class 摘掉，弹回就是动画了 */
.pet.dragging{ transition:none; }
.pet.pressing{ cursor:grabbing; }

/* .pet-move：拖动位移层（由 pet.js 的 build() 建，包着 bob/heart/bubble）。
   它和 .pet 完全重合，所以 .pet-heart / .pet-bubble 的定位坐标一点不变。
   入场动画吃 .pet 的 transform，拖动吃这一层的 transform，互不打架。 */
.pet-move{
  position:absolute; left:0; top:0; width:100%; height:100%;
  transition:transform .45s var(--ease-out);
}
.pet.dragging .pet-move{ transition:none; }

/* 图集单元格的宽高比不一定是 192:208，按 manifest 里的比例留位，
   免得画被拉变形。--pet-ratio 由 pet.js 从 cell.width / cell.height 写进来。 */
.pet.pet-has-ratio{ height:calc(var(--pet-w) / var(--pet-ratio, 0.9231)); }

.pet-bob{ width:100%; height:100%; animation:petBob var(--bob, 2.9s) ease-in-out infinite; }
/* 「站桩」宠物：pet.json 里写 still:true 就挂上 .pet-still —— 关掉上下浮动
   （sprite2d 走路靠 .pet-bob）和程序化呼吸（frames2d 走路靠 .pet-frames），
   真正一动不动。逐只开关，别的宠物不受影响。 */
.pet.pet-still .pet-bob{ animation:none; }
.pet.pet-still .pet-frames{ animation:none !important; }
.pet-art{ width:100%; height:100%; transform-origin:50% 100%; }
.pet-svg{ display:block; width:100%; height:100%; }

/* ---- renderer: "sprite2d" —— 8 列 × 9 行图集，靠背景定位切帧 ----
   background-size 设成 800% 900%（8 列 9 行），于是元素自身宽高的 100%
   正好是一格，background-position 用整数倍就能对齐，跟图集分辨率无关。 */
.pet-sheet-wrap{ overflow:hidden; }
.pet-sheet{
  width:100%; height:100%;
  background-repeat:no-repeat;
  background-position:0 0;
  image-rendering:auto;
}

/* ---- renderer: "frames2d" —— 单张（或几张）平面立绘，动作靠 transform 现做 ----
   画师只给一张静态 Q 版立绘时用这个：没有逐帧图集，也没分层，
   所以「呼吸 / 摆 / 蹦 / 蔫」只能整体做。
   pet.js 把当前轨道名写到 data-motion（就是 ROW_NAMES 那九个），
   这里逐条给出对应动作。变换原点统一钉在脚底，人就不会飘。 */
.pet-frames-wrap{ overflow:visible; }
.pet-frames{
  width:100%; height:100%;
  background-repeat:no-repeat;
  background-position:50% 100%;
  background-size:contain;
  transform-origin:50% 100%;
  will-change:transform;
}

/* idle：从脚底往上鼓缩。竖向浮动交给 .pet-bob 的 petBob，这里只管呼吸，免得叠加成抽搐 */
@keyframes petF2Idle{
  0%, 100%{ transform:scale(1, 1); }
  50%     { transform:scale(1.008, 1.018); }
}
.pet-frames[data-motion="idle"]{ animation:petF2Idle 3.6s ease-in-out infinite; }

/* waiting：慢慢左右晃，像站着等人 */
@keyframes petF2Wait{
  0%, 100%{ transform:rotate(-1.2deg) scale(1,1); }
  50%     { transform:rotate(1.2deg)  scale(1.004,1.01); }
}
.pet-frames[data-motion="waiting"]{ animation:petF2Wait 5.4s ease-in-out infinite; }

/* running / running-left / running-right：小步颠，左右方向给相反的倾角 */
@keyframes petF2Run{
  0%, 100%{ transform:translateY(0)    rotate(3deg)  scale(1,1); }
  50%     { transform:translateY(-6px) rotate(1deg)  scale(1.01,.985); }
}
@keyframes petF2RunL{
  0%, 100%{ transform:translateY(0)    rotate(-3deg) scale(1,1); }
  50%     { transform:translateY(-6px) rotate(-1deg) scale(1.01,.985); }
}
.pet-frames[data-motion="running"],
.pet-frames[data-motion="running-right"]{ animation:petF2Run .64s ease-in-out infinite; }
.pet-frames[data-motion="running-left"]{ animation:petF2RunL .64s ease-in-out infinite; }

/* waving：整体来回摆，当作招手 */
@keyframes petF2Wave{
  0%, 100%{ transform:rotate(-4.5deg) translateY(0); }
  50%     { transform:rotate(4.5deg)  translateY(-2px); }
}
.pet-frames[data-motion="waving"]{ animation:petF2Wave 1.15s ease-in-out infinite; }

/* jumping：蹲一下再弹起来 */
@keyframes petF2Jump{
  0%  { transform:translateY(0)     scale(1,1); }
  22% { transform:translateY(3px)   scale(1.05,.95); }
  55% { transform:translateY(-18px) scale(.97,1.07); }
  85% { transform:translateY(0)     scale(1.03,.97); }
  100%{ transform:translateY(0)     scale(1,1); }
}
.pet-frames[data-motion="jumping"]{ animation:petF2Jump .56s ease-in-out infinite; }

/* failed：蔫掉 —— 往下坐、歪一边、颜色发灰 */
@keyframes petF2Fail{
  0%, 100%{ transform:translateY(3px) rotate(-7deg)   scale(1,.99); }
  50%     { transform:translateY(4px) rotate(-8.5deg) scale(1,.985); }
}
.pet-frames[data-motion="failed"]{
  animation:petF2Fail 1.6s ease-in-out infinite;
  filter:saturate(.5) brightness(.9);
}

/* review：歪着头打量 */
@keyframes petF2Review{
  0%, 100%{ transform:rotate(0)     translateY(0); }
  35%     { transform:rotate(5deg)  translateY(-2px); }
  70%     { transform:rotate(-3deg) translateY(0); }
}
.pet-frames[data-motion="review"]{ animation:petF2Review 2.4s ease-in-out infinite; }

/* ---- 吃东西：一口吞 + 鼓腮帮子嚼 ----
   单张立绘拆不出腮帮子图层，所以拿「整体压扁 + 横向撑开」当咀嚼的读法
   （Q 版卡通最常见的一招），开头先小抬一下表示把点心举到嘴边。
   用 both 不用 infinite —— 这是一次性动作，播完由 JS 把 data-motion 换回场景轨道。 */
@keyframes petF2Eat{
  0%   { transform:scale(1.000, 1.000) translateY(0); }
  12%  { transform:scale(0.985, 1.030) translateY(-4px); }
  24%  { transform:scale(1.020, 0.985) translateY(-2px); }
  34%  { transform:scale(1.055, 0.935) translateY(3px); }
  46%  { transform:scale(1.090, 0.900) translateY(6px); }
  56%  { transform:scale(1.120, 0.872) translateY(8px); }
  68%  { transform:scale(1.030, 0.965) translateY(1px); }
  78%  { transform:scale(1.085, 0.905) translateY(6px); }
  88%  { transform:scale(1.015, 0.985) translateY(-1px); }
  100% { transform:scale(1.000, 1.000) translateY(0); }
}
.pet-frames[data-motion="eating"]{ animation:petF2Eat 2.6s ease-in-out both; }

/* ---- 被拎起来：空中挣扎 ----
   这是 frames2d 专属的一套（JS 在你开始拖的时候给宿主挂 .grabbed）。
   有真图集的（sprite2d）则直接播第 9 行 dragged，不走这套。
   取 ROW_NAMES.length 当图集行数的图集必须在 pet.json 里显式写 atlasRows，
   否则行高会算错。

   为什么这套动作能装出「挥舞双手」：她两片宽袖子从肩膀横向张开，
   把变换原点从脚底挪到【肩部】（50% 24%），整条袖子就变成以肩为轴的摆杆 ——
   袖尖离支点约 40px，转 ±11° 就能甩出 7~8px，在 92px 的显示尺寸下非常显眼。
   再叠一层高频抖动（.pet-art.grabbed 上的 petF2Jitter）当「使劲挣」的毛刺。 */
@keyframes petF2Struggle{
  0%  { transform:translate(-3px, 0)    rotate(-9deg)  scale(1.000, 1.000); }
  13% { transform:translate( 2px, -4px) rotate( 7deg)  scale(.985, 1.020); }
  27% { transform:translate(-4px, -1px) rotate(-11deg) scale(1.010, .990); }
  41% { transform:translate( 3px, -5px) rotate( 6deg)  scale(.980, 1.030); }
  56% { transform:translate(-2px, -2px) rotate(-8deg)  scale(1.005, 1.000); }
  70% { transform:translate( 4px, -4px) rotate(10deg)  scale(.990, 1.015); }
  84% { transform:translate(-3px, -1px) rotate(-6deg)  scale(1.010, .995); }
  100%{ transform:translate(-3px, 0)    rotate(-9deg)  scale(1.000, 1.000); }
}
/* 特定度 (0,2,1) 压得过 .pet-frames[data-motion="…"] 的 (0,1,1)，所以拖动时一定赢 */
.pet-art.grabbed .pet-frames{
  animation:petF2Struggle .5s ease-in-out infinite;
  transform-origin:50% 24%;
}
/* 高频小抖：steps 让它一格一格弹，像使劲挣而不是顺滑摇晃 */
@keyframes petF2Jitter{
  0%  { transform:translateX(0); }
  50% { transform:translateX(1.6px); }
  100%{ transform:translateX(0); }
}
.pet-art.grabbed{ animation:petF2Jitter .15s steps(2, end) infinite; }

/* ---- renderer: "layered" —— 分层立绘，每层一张全画布 PNG，canvas 做变换 ----
   canvas 的位图尺寸由 pet.js 按 dpr 和宿主宽度算，CSS 只管撑满。
   整体倾斜/抬升由 JS 的矩阵做，这里不要再加 CSS 动画，否则会叠加成双重抖动。 */
.pet-layer-wrap{ overflow:visible; }
.pet-canvas{ display:block; width:100%; height:100%; }

/* ---- 配色：鲜亮的卡通色，跟水墨底拉开反差 ---- */
.p-body, .p-arm, .p-foot{ fill:#EAF7DC; stroke:#2F4A2B; stroke-width:3; }
.p-leaf{ fill:#63C463; stroke:#2F4A2B; stroke-width:3; stroke-linejoin:round; }
.p-husk{ fill:none; stroke:rgba(47,74,43,.30); stroke-width:2.6; stroke-linecap:round; }
.p-eye{ fill:#2B2B2B; }
.p-glint{ fill:#fff; }
.p-cheek{ fill:#FF9FA8; opacity:.72; }
.p-shadow{ fill:rgba(0,0,0,.22); }
.p-mouth{ fill:none; stroke:#2B2B2B; stroke-width:2.8; stroke-linecap:round; }
.p-happyeyes path, .p-squint path, .p-sleepy path{
  fill:none; stroke:#2B2B2B; stroke-width:3.4;
  stroke-linecap:round; stroke-linejoin:round;
}

/* ---- 待机小动作：上下浮 ---- */
@keyframes petBob{ 0%,100%{ transform:translateY(0); } 50%{ transform:translateY(-3px); } }

/* ---- 呼吸：整块躯干从脚底往上微微鼓 ---- */
@keyframes petBreathe{ 0%,100%{ transform:scale(1,1); } 50%{ transform:scale(.985,1.022); } }
.p-torso{
  transform-box:fill-box; transform-origin:50% 100%;
  animation:petBreathe var(--bob, 2.9s) ease-in-out infinite;
}

/* ---- 眨眼：一眼 0.2 秒，其余时间睁着 ---- */
@keyframes petBlink{
  0%, 90%, 100%{ transform:scaleY(1); }
  93.5%, 95.5%{ transform:scaleY(.08); }
}
.p-eye, .p-glint{
  transform-box:fill-box; transform-origin:center;
  animation:petBlink var(--blink, 5.2s) ease-in-out infinite;
  animation-delay:var(--blink-delay, 0s);
}

/* ---- 叶子 / 手臂：跟着呼吸一起晃 ---- */
@keyframes petLeafL{ 0%,100%{ transform:rotate(0); } 50%{ transform:rotate(-9deg); } }
@keyframes petLeafR{ 0%,100%{ transform:rotate(0); } 50%{ transform:rotate(9deg); } }
.p-leaf-l{ transform-box:fill-box; transform-origin:100% 100%; animation:petLeafL 2.3s ease-in-out infinite; }
.p-leaf-r{ transform-box:fill-box; transform-origin:0% 100%;   animation:petLeafR 2.3s ease-in-out infinite; }

@keyframes petArmL{ 0%,100%{ transform:rotate(0); } 50%{ transform:rotate(-12deg); } }
@keyframes petArmR{ 0%,100%{ transform:rotate(0); } 50%{ transform:rotate(12deg); } }
.p-arm-l{ transform-box:fill-box; transform-origin:100% 50%; animation:petArmL 2.3s ease-in-out infinite; }
.p-arm-r{ transform-box:fill-box; transform-origin:0% 50%;   animation:petArmR 2.3s ease-in-out infinite; }

/* ---- 三套备用眼睛，按心情切换 ---- */
.p-happyeyes, .p-squint, .p-sleepy{ opacity:0; transition:opacity .12s linear; }
/* 心情切换：m-* 类由渲染器挂在宿主元素（.pet-art）上 */
.pet-art.m-happy  .p-eyes{ opacity:0; }  .pet-art.m-happy  .p-happyeyes{ opacity:1; }
.pet-art.m-squint .p-eyes{ opacity:0; }  .pet-art.m-squint .p-squint{ opacity:1; }
.pet-art.m-sleepy .p-eyes{ opacity:0; }  .pet-art.m-sleepy .p-sleepy{ opacity:1; }

/* ---- 一次性动作 ---- */
@keyframes petTap{
  0%  { transform:translateY(0) scale(1,1); }
  22% { transform:translateY(-15px) scale(.93,1.09); }
  46% { transform:translateY(0) scale(1.09,.91); }
  68% { transform:translateY(-5px) scale(.98,1.03); }
  100%{ transform:translateY(0) scale(1,1); }
}
.pet-art.tap{ animation:petTap .64s var(--ease-out); }

@keyframes petHop{
  0%,100%{ transform:translateY(0); }
  30%    { transform:translateY(-9px); }
  60%    { transform:translateY(0); }
  75%    { transform:translateY(-3px); }
}
.pet-art.hop{ animation:petHop .9s ease-in-out; }

@keyframes petGlance{
  0%,100%{ transform:translateX(0); }
  18%    { transform:translateX(-4px); }
  45%    { transform:translateX(4px); }
  72%    { transform:translateX(-2px); }
}
.pet-art.glance .p-eyes{ animation:petGlance 1.6s ease-in-out; }

/* ---- 冒爱心 ---- */
.pet-heart{
  position:absolute; left:50%; top:4px;
  width:24px; height:24px; margin-left:-12px;
  font-size:21px; line-height:24px; text-align:center;
  color:#FF6B7A;
  opacity:0; pointer-events:none;
  text-shadow:0 1px 0 #fff, 0 0 9px rgba(255,107,122,.65);
}
.pet-heart::before{ content:'\2665'; }
@keyframes petHeart{
  0%  { opacity:0; transform:translateY(6px)  scale(.4); }
  25% { opacity:1; transform:translateY(-5px) scale(1.15); }
  100%{ opacity:0; transform:translateY(-32px) scale(.9); }
}
.pet-heart.on{ animation:petHeart 1s var(--ease-out); }

/* 说话气泡已经并进 .pet-think 了（见下面那节）——
   原来这里另有一个 .pet-bubble 飘在宠物左边，一次点击会同时冒出两处东西，
   所以整块删掉，不要留死代码。 */

/* ---- 头顶的云：既是「在想事情」的指示，也是【唯一的】说话气泡 ----
   形状走「有机圆角 + 一颗尾巴小点」的路子，比纯圆角方框更像云，也不用为此塞一张图。
   说话态（.saying）把三个点收起来、把文字装进去。
   定位用 bottom 而不是 top：底边钉在头顶上方 11px，说话时往【上】长 ——
   宠物常驻右下角，往上长才不会顶出画面右边（right:-4px 把右边钉住，宽了往左扩）。 */
.pet-think{
  position:absolute;
  right:-4px; bottom:calc(100% - 11px);
  min-width:40px; height:27px;
  display:flex; align-items:center; justify-content:center;
  padding:0 2px; margin:0;
  max-width:168px;
  background:#FFFDF5;
  border:2px solid #2F4A2B;
  border-radius:52% 48% 50% 50% / 58% 58% 42% 42%;
  cursor:pointer;
  box-shadow:0 4px 11px rgba(0,0,0,.35);
  -webkit-appearance:none; appearance:none;
  -webkit-tap-highlight-color:transparent;
  touch-action:manipulation;
}
/* 尾巴：一颗小圆点指着宠物的脑袋。钉在右边而不是左边 —— 说话时气泡往左长，
   钉左边的话尾巴会被甩到离宠物很远的地方去。 */
.pet-think::after{
  content:''; position:absolute;
  right:30px; bottom:-6px;
  width:7px; height:7px;
  background:#FFFDF5;
  border:2px solid #2F4A2B;
  border-radius:50%;
}
.pet-think-dots{ display:flex; align-items:center; gap:3px; }
.pet-think-dots i{
  display:block; width:5px; height:5px; border-radius:50%;
  background:#2F4A2B; opacity:.32;
  animation:petThinkDot 1.35s ease-in-out infinite;
}
.pet-think-dots i:nth-child(2){ animation-delay:.18s; }
.pet-think-dots i:nth-child(3){ animation-delay:.36s; }
@keyframes petThinkDot{
  0%, 60%, 100%{ opacity:.30; transform:translateY(0); }
  30%          { opacity:1;   transform:translateY(-2px); }
}

/* 说话态：点收起来，字装进来，云变成对话框。
   width:max-content 不能省 —— 绝对定位且只设了 right 时，
   可用宽度会被父容器（.pet-move 只有 92px 宽）卡死，
   文字会被挤成三行窄条。max-content 让它按文字实际宽度算，再由 max-width 封顶。 */
.pet-think-txt{ display:none; }
.pet-think.saying{
  width:max-content;
  height:auto; min-height:27px;
  padding:6px 10px;
  border-radius:13px;
  justify-content:flex-start;
}
.pet-think.saying .pet-think-dots{ display:none; }
.pet-think.saying .pet-think-txt{
  display:block;
  font-family:var(--font-song);
  font-size:11.5px; line-height:1.52; letter-spacing:.02em;
  color:#332F26; text-align:left;
}
.pet-think:active{ background:#F0EAD2; }

/* ---- 点云朵冒出来的小窗口：喂食 / 聊天 / 旅游日记 ----
   默认开在宠物左边（宠物常驻右下角）。宠物被拖到画面左半边时
   pet.js 会给它加 .side-right，窗口翻到右边去。 */
.pet-menu{
  position:absolute;
  right:calc(100% + 10px); bottom:4px;
  min-width:106px;
  padding:6px;
  background:#FFFDF5;
  border:2px solid #2F4A2B;
  border-radius:13px;
  box-shadow:0 9px 22px rgba(0,0,0,.42);
  opacity:0; pointer-events:none;
  transform:translateX(6px) scale(.9);
  transform-origin:100% 82%;
  transition:opacity .18s ease, transform .24s var(--ease-out);
}
.pet-menu.on{ opacity:1; pointer-events:auto; transform:none; }
.pet-menu.side-right{
  right:auto; left:calc(100% + 10px);
  transform-origin:0% 82%;
  transform:translateX(-6px) scale(.9);
}
.pet-menu.side-right.on{ transform:none; }

.pet-menu-btn{
  display:flex; align-items:center; gap:7px;
  width:100%; padding:7px 9px; margin:0;
  background:none; border:0; border-radius:9px;
  font-family:var(--font-song);
  font-size:13px; line-height:1.3; color:#332F26;
  text-align:left; cursor:pointer;
  -webkit-appearance:none; appearance:none;
  -webkit-tap-highlight-color:transparent;
  touch-action:manipulation;
}
.pet-menu-btn + .pet-menu-btn{ margin-top:2px; }
.pet-menu-btn:active{ background:rgba(47,74,43,.13); }
.pet-menu-ico{ font-size:15px; line-height:1; }

/* ---- 讨茶：「倒茶」按钮 ----
   宠物开口要茶的时候才冒出来（pet.js 的 showTea()）。right:-4px 跟气泡走
   同一条右边线；bottom 不在这儿写死 —— 由 showTea() 按气泡【实测】高度
   写内联，永远贴在气泡正上方（气泡是从底边往上长的，写死 px 会叠）。
   没 .on 的时候 opacity:0 + pointer-events:none：不挡点击，
   aria-hidden 由 js 同步，读屏也读不到。 */
.pet-tea{
  position:absolute;
  right:-4px;
  padding:6px 12px; margin:0;
  background:#FFFDF5;
  border:2px solid #2F4A2B;
  border-radius:12px;
  font-family:var(--font-song);
  font-size:13px; line-height:1.2; color:#332F26;
  white-space:nowrap;
  cursor:pointer;
  box-shadow:0 7px 18px rgba(0,0,0,.42);
  opacity:0; pointer-events:none;
  transform:translateY(5px) scale(.92);
  transition:opacity .18s ease, transform .24s var(--ease-out);
  -webkit-appearance:none; appearance:none;
  -webkit-tap-highlight-color:transparent;
  touch-action:manipulation;
}
.pet-tea.on{ opacity:1; pointer-events:auto; transform:none; }
.pet-tea:active{ background:#F0EAD2; }

/* 矮屏（横屏或小机）缩小一点，别挡住地图入口 */
@media (max-height:640px){
  .pet{ --pet-w:72px; height:77px; right:10px; bottom:calc(env(safe-area-inset-bottom, 0px) + 100px); }
  .pet-think{ max-width:144px; }
  .pet-think.saying{ padding:5px 9px; }
  .pet-think.saying .pet-think-txt{ font-size:11px; }
  .pet-menu{ min-width:98px; }
}
@media (max-width:340px){
  .pet{ --pet-w:78px; height:83px; }
  .pet-think{ max-width:132px; }
}

/* ============================================================================
   喂食屉（点桌宠头顶的云 → 小菜单「喂食」）
   和 .pick 一样是贴着底边的抽屉，区别是它装在 .home 里：
   只盖主界面，四个 sheet 一开就自然把它压住，不用另做层级。
   桌宠常驻右下角，抽屉一升上来正好把她盖住 —— 所以喂的这段时间
   把 .pet 抬到抽屉上层（.pet-over），让她站在屉子前头吃，看得见。
   ============================================================================ */

.feed{
  position:absolute; inset:0; z-index:9;   /* .dock 是 7、.pet 是 8，两个都要盖住 */
  display:flex; align-items:flex-end;
  background:rgba(6,8,6,.55);
  backdrop-filter:blur(3px);
  opacity:0; visibility:hidden; pointer-events:none;
  transition:opacity .3s var(--ease-out), visibility .3s;
}
.feed.on{ opacity:1; visibility:visible; pointer-events:auto; }

.feed-inner{
  width:100%;
  /* 8 样东西 3 列还是 3 行（3+3+2），按内容算大约 578px。抽屉矮于这个数就会出滚动条，
     而摆吃食的地方还要滚、第三行就没人看得见，所以给到 85%。
     真机（376×795）上是 676，绰绰有余；只有 667 一类的小屏才会滚一点。 */
  max-height:85%;
  display:flex; flex-direction:column;
  padding:18px 16px calc(env(safe-area-inset-bottom, 0px) + 14px);
  border-radius:22px 22px 0 0;
  background:linear-gradient(180deg, #FBF6E4 0%, var(--paper) 100%);
  box-shadow:0 -12px 34px rgba(0,0,0,.4);
  transform:translateY(26px);
  transition:transform .34s var(--ease-out);
}
.feed.on .feed-inner{ transform:none; }

.feed-top{ flex:0 0 auto; text-align:center; }
.feed-title{
  margin:0;
  font-family:var(--font-brush);
  font-size:19px; font-weight:400;
  letter-spacing:.34em; text-indent:.34em;   /* 两个字拉开成「食　盒」 */
  color:var(--ink);
}
.feed-sub{
  margin:5px 0 0;
  font-size:12px; letter-spacing:.03em;
  color:var(--ink-soft);
}
.feed-sub b{ font-size:13.5px; font-weight:600; color:var(--cinnabar); }

.feed-grid{
  /* 基数用内容高（flex:0 1 auto），抽屉高度就按内容算；顶到 max-height 才让网格缩 + 滚。
     注意别用 flex:1（基数 0），那会让抽屉高度绕开网格内容自己算。 */
  flex:0 1 auto; min-height:0;
  margin-top:14px;
  overflow-y:auto;
  -webkit-overflow-scrolling:touch;
  display:grid;
  grid-template-columns:repeat(3, 1fr);
  gap:8px;
  padding-bottom:0;
}

.feed-item{
  display:flex; flex-direction:column; align-items:center; gap:4px;
  padding:6px 5px 7px;
  border:1px solid rgba(31,27,20,.16);
  border-radius:13px;
  background:rgba(255,255,255,.55);
  font-family:inherit;
  cursor:pointer;
  -webkit-appearance:none; appearance:none;
  -webkit-tap-highlight-color:transparent;
  touch-action:manipulation;
  animation:feedPop .34s var(--ease-out) backwards;   /* backwards + 内联 delay = 逐个冒出来 */
  transition:background .2s, border-color .2s, transform .12s var(--ease-out);
}
@keyframes feedPop{
  from{ opacity:0; transform:translateY(10px); }
  to  { opacity:1; transform:none; }
}
.feed-item:active{
  transform:scale(.95);
  background:rgba(176,57,43,.1);
  border-color:rgba(176,57,43,.5);
}

.feed-face{
  position:relative;
  width:100%;
  /* 略扁一点（不是正方的）：三行正方图加起来正好比抽屉上限高一点，
     底下一行要多滚一下才看得见。0.86 之后三行全露，图看着还是方的。 */
  aspect-ratio:1 / 0.86;
  border-radius:9px; overflow:hidden;
  background:#fff;
}
/* 老 WebView 不认 aspect-ratio 时按格子宽（376 屏上约 98px）给个高度兜底 */
@supports not (aspect-ratio: 1 / 1){
  .feed-face{ height:84px; }
}
.feed-face img{
  width:100%; height:100%;
  object-fit:cover; display:block;
  pointer-events:none;
  -webkit-user-drag:none;
}
.feed-badge{
  position:absolute; left:0; bottom:0;
  padding:1px 6px 2px;
  border-radius:0 8px 0 0;
  background:rgba(31,27,20,.6);
  color:#FBF6E4;
  font-size:9.5px; line-height:1.5; letter-spacing:.02em;
}
.feed-name{
  font-size:12px; letter-spacing:.04em;
  color:var(--ink-2);
  text-align:center; line-height:1.25;
}
/* 手上没有的那几样：淡下去、褪成灰，看着就知道是「还没到手」而不是坏了。
   点了不喂，她会说一句「先去集邮盖一枚章」。 */
.feed-item.empty{ opacity:.4; }
.feed-item.empty .feed-face img{ filter:grayscale(1); }

.feed-tip{
  flex:0 0 auto;
  margin:10px 0 0;
  font-size:11.5px; letter-spacing:.02em;
  color:var(--ink-faint);
  text-align:center;
}
/* 底下两个按钮并排：去集邮换取 / 收起来。
   .talk-btn 本来是 flex:1（给横排按钮用的），横排里正好等宽两条。 */
.feed-foot{ display:flex; gap:8px; margin-top:9px; }
.feed-foot .talk-btn.ghost{ flex:1; margin-top:0; }

/* 喂食/聊天期间把桌宠抬到屉子上面。.pet 和 .feed/.chat 同在 .home 这个层叠上下文里，比大小就够了。 */
.pet.pet-over{ z-index:11; }
/* 全屏聊天开着的时候把她抬到壁纸上面（见下面 .chat-hist 那段），
   但仍在输入框之下，免得挡住打字。**跟 .pet-over 是两回事**（那是喂食屉的层级）。 */
.pet.pet-front{ z-index:10; }

/* ============================================================================
   聊天（点桌宠头顶的云 → 小菜单「聊天」）
   屏幕上只多一条输入框：左边一本「聊过的」小册子，右边写字。
   她说的话【不】在这儿 —— 走头顶那朵云（.pet-think.saying），见 js/chat.js。
   层级：全屏聊天面 .chat-hist(9) < 桌宠 .pet.pet-front(10) < 输入框 .chat-bar(11)。
   点那本书 → 整面翻开成【全屏聊天】：整面是纯米黄底（跟首页同色），
   她和她那朵云浮在聊天面之上，气泡半透明。
   （曾经把一张人物立绘铺在这面墙上当壁纸，2026-10-04 用户要求撤掉，
     见下面 .chat-hist-inner 里那段注释 —— 想装回来只要解开注释。）
   ========================================================================== */
.chat-bar{
  position:absolute; left:14px; right:14px;
  bottom:calc(env(safe-area-inset-bottom, 0px) + 16px);
  z-index:35;   /* 要压在四张 .sheet(30)/商城(32)/兑换(34)之上——地图开着也能点发送；
                   仍在 toast(40)/弹窗(60)/扫一扫(70)之下 */
  display:flex; align-items:center; gap:5px;
  padding:5px 6px 5px 5px;
  border:2px solid #2F4A2B;
  border-radius:17px;
  background:rgba(255,253,245,.9);
  -webkit-backdrop-filter:blur(7px); backdrop-filter:blur(7px);
  box-shadow:0 9px 24px rgba(0,0,0,.34);
  opacity:0; visibility:hidden; pointer-events:none;
  transform:translateY(14px);
  transition:opacity .24s var(--ease-out), transform .3s var(--ease-out), visibility .24s;
}
.chat-bar.on{ opacity:1; visibility:visible; pointer-events:auto; transform:none; }
/* sheet（地图/图鉴/日记/商城…）开着时：sheet 自己的底栏贴着底边（.map-foot 高约 62px
   + 边距），聊天条再待在原位就跟它叠成两摞 —— 抬到 sheet 底栏上方，露出全部键。
   body.sheet-on 由 chat.js 里的 MutationObserver 挂/摘（那边注释写明了缘由）。 */
body.sheet-on .chat-bar{ bottom:calc(env(safe-area-inset-bottom, 0px) + 92px); }

.chat-book{
  flex:0 0 auto;
  width:35px; height:35px;
  display:flex; align-items:center; justify-content:center;
  border:0; border-radius:11px;
  background:#EFE7CE; color:#2F4A2B;
  cursor:pointer;
  -webkit-appearance:none; appearance:none;
  -webkit-tap-highlight-color:transparent;
  transition:background .18s ease, color .18s ease;
}
.chat-book:active{ background:#E2D8B6; }
.chat-book.on{ background:var(--cinnabar); color:#FFF9EE; }   /* 小册子正翻开着 */

.chat-input{
  flex:1 1 auto; min-width:0;
  height:35px; padding:0 6px;
  border:0; background:transparent;
  font-family:var(--font-song); font-size:14px; letter-spacing:.01em;
  color:var(--ink);
  outline:none;
}
.chat-input::placeholder{ color:var(--ink-faint); }
.chat-input:disabled{ color:var(--ink-faint); }

/* ---- 聊过的：点那本书，整面翻开成全屏聊天（整面是 .chat-hist-inner 的米黄底） ---- */
.chat-hist{
  position:absolute; inset:0; z-index:9;   /* 比 .pet-front(10) 与 .chat-bar(11) 都低 */
  display:flex; align-items:stretch;
  background:transparent;
  opacity:0; visibility:hidden; pointer-events:none;
  transition:opacity .3s var(--ease-out), visibility .3s;
}
.chat-hist.on{ opacity:1; visibility:visible; pointer-events:auto; }

.chat-hist-inner{
  position:relative;
  width:100%; height:100%; max-height:none;
  display:flex; flex-direction:column;
  /* 顶上给状态栏让路、底下留出输入框那一条（35 + 16 + 余量） */
  padding:calc(env(safe-area-inset-top, 0px) + 14px) 16px calc(env(safe-area-inset-bottom, 0px) + 94px);
  border-radius:0;
  box-shadow:none;
  background-color:var(--paper);   /* 页面底色 = APP 那个米黄，跟首页一致 */
  /* 2026-10-04：用户说「把全屏聊天的那个人物立绘换下来」，于是把那张立绘壁纸撤了，
     整面只留纯米黄底（跟首页一个色）。原来的三层背景是：
       第一层压顶、第二层压底（让标题与输入框在画上也读得清），第三层才是画。
     图还在 www/assets/chat/bg.jpg，想把壁纸装回来就把下面这行的注释解开。
  background-image:
    linear-gradient(180deg, rgba(246,237,208,.92) 0%, rgba(246,237,208,0) 20%),
    linear-gradient(0deg, rgba(246,237,208,.94) 0%, rgba(246,237,208,0) 20%),
    url("../assets/chat/bg.jpg?v=40f1e9ac");
  background-repeat:no-repeat, no-repeat, no-repeat;
  background-size:auto, auto, cover;
  background-position:center top, center bottom, center center;
  */
  transform:translateY(26px);
  transition:transform .34s var(--ease-out);
}
.chat-hist.on .chat-hist-inner{ transform:none; }

.chat-hist-top{ flex:0 0 auto; text-align:center; }
/* 2026-10-06：这儿原来是一个 <h3 class="chat-hist-title">聊 过 的</h3>。
   上午换成了「聊过的 / 记录」两个页签，当天下午「那个右边的记录页面去掉」，
   页签整排也拆了 —— 现在顶上就剩这一行：她在不在线。 */
.chat-hist-sub{
  margin:2px 0 0;
  font-size:13px; letter-spacing:.06em;
  color:var(--ink-soft);
}

.chat-hist-list{
  /* 基数用内容高：话少的时候小册子就矮矮一条，多了顶到 max-height 才开始滚 */
  flex:1 1 auto; min-height:132px;
  margin-top:12px;
  overflow-y:auto;
  -webkit-overflow-scrolling:touch;
  overscroll-behavior:contain;         /* 滚到底别把整页带着一起滑 */
  display:flex; flex-direction:column; gap:9px;
  padding:2px 1px;
}
.chat-hist-empty{
  margin:auto;                        /* 上下左右都 auto：空态就杵在正中 */
  max-width:80%;
  padding:6px 13px;
  border-radius:11px;
  background:rgba(255,253,245,.60);
  border:1px solid rgba(47,74,43,.16);
  font-size:12.5px; letter-spacing:.02em;
  color:var(--ink-faint); text-align:center;
}

/* ---- 日期行：「聊过的」这条时间线里换天了就插一条 ----
   （js/chat.js 的 renderBook() + dayText()）
   ★ flex 子项默认会被压扁（flex-shrink:1）—— 行多了就全挤成一条线而不是滚动，
     所以日期行 / 旁白 / 气泡每一行都写死 flex:0 0 auto。 */
.rec-day{
  flex:0 0 auto;
  margin:10px 0 2px;
  font-size:11.5px; letter-spacing:.10em;
  color:var(--ink-faint); text-align:center;
}
.rec-day:first-child{ margin-top:0; }

/* ---- QQ 那样的气泡（2026-10-06 改）----
   她的：小头像在左、白气泡靠左；你的：朱红气泡靠右、右肩一个名字圆牌。
   互动和「账」也套一层居中气泡，只是比人说的那两种淡（.chat-narr / .rec-narr）。 */
.chat-row{ display:flex; flex:0 0 auto; align-items:flex-start; gap:7px; }
.chat-row.her{ justify-content:flex-start; }
.chat-row.me{ justify-content:flex-end; }
.chat-av{
  flex:0 0 auto;
  width:28px; height:28px; border-radius:50%;
  margin-top:2px;
  overflow:hidden;                     /* 里面那张方图自己填满这个圆 */
  background:rgba(255,253,245,.80);
  border:1px solid rgba(47,74,43,.28);
  box-shadow:0 1px 4px rgba(31,27,20,.10);
  -webkit-user-select:none; user-select:none;
}
.chat-av > img{
  display:block; width:100%; height:100%;
  object-fit:cover;
  object-position:center 45%;          /* 头像已经是裁好的方图，这里只是兜个底 */
  -webkit-user-drag:none;
}
/* 你没有头像图，就拿名字头一个字做个圆牌（见 js/chat.js 的 avatarEl()） */
.chat-av.me{
  display:flex; align-items:center; justify-content:center;
  background:rgba(176,57,43,.80);
  border-color:rgba(140,50,30,.55);
  color:#FFF9EE;
  font-size:13px; letter-spacing:0;
}
.chat-bubble{
  margin:0;
  max-width:74%;
  padding:9px 12px;
  border-radius:14px;
  font-size:13.5px; line-height:1.55; letter-spacing:.01em;
  white-space:pre-wrap; word-break:break-word;
  /* 气泡是半透明的：背后的画透出来一点，糊一层才不至于把字搅花 */
  -webkit-backdrop-filter:blur(7px); backdrop-filter:blur(7px);
}
.chat-row.her .chat-bubble{
  background:rgba(255,253,245,.72);
  border:1px solid rgba(47,74,43,.30);
  border-top-left-radius:5px;      /* 靠她那边的角收一点，像说话的气口 */
  color:var(--ink-2);
  box-shadow:0 2px 7px rgba(31,27,20,.07);
}
.chat-row.me .chat-bubble{
  background:rgba(176,57,43,.74);
  border:1px solid rgba(140,50,30,.55);
  border-top-right-radius:5px;
  color:#FFF9EE;
  box-shadow:0 2px 7px rgba(140,50,30,.22);
}
/* 中间那行系统信息：互动 = .chat-narr，集章 / 兑换 / 翻日记这些账 = .rec-narr。
   ★ 2026-10-06 用户 m10643：「系统信息也要用一个气泡包裹起来」——原来是光秃秃一行
     居中字，现在也套一层气泡：居中、宽度跟着字走（align-self + max-width，
     短句自己缩、长句最多 86%），颜色比人说的气泡淡一档，跟左右两边分得开。
   ★ .rec-narr 得写在 .chat-narr 后面才压得住它的颜色（同为单类选择器，后者赢）。 */
.chat-narr{
  flex:0 0 auto;
  align-self:center;                   /* 列向 flex 里靠这一条居中，比 auto 外边距直白 */
  max-width:86%;
  margin:3px 0;
  padding:4px 11px;
  border-radius:11px;
  background:rgba(255,253,245,.60);
  border:1px solid rgba(47,74,43,.16);
  box-shadow:0 1px 4px rgba(31,27,20,.05);
  -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px);
  font-size:11.5px; line-height:1.5; letter-spacing:.06em;
  color:var(--ink-faint); text-align:center;
  word-break:break-word;
}
.rec-narr{
  color:rgba(140,50,30,.62); letter-spacing:.04em;
  background:rgba(255,246,238,.64);
  border-color:rgba(180,90,40,.20);
}
/* 正在想 / 出错：都借她的气泡，只换个颜色，免得忽左忽右 */
.chat-row.chat-wait .chat-bubble{ color:var(--ink-faint); font-style:italic; }
.chat-row.chat-fail .chat-bubble{
  background:rgba(255,246,238,.78);
  border-color:rgba(180,90,40,.35);
  color:#8A4A2A;
}

.chat-form{ flex:0 0 auto; display:flex; gap:8px; margin-top:12px; }
.chat-input{
  flex:1 1 auto; min-width:0;
  height:40px; padding:0 13px;
  border:1px solid rgba(31,27,20,.22);
  border-radius:12px;
  background:#FFFDF5;
  font-family:var(--font-song); font-size:14px; letter-spacing:.01em;
  color:var(--ink);
  outline:none;
}
.chat-input:focus{ border-color:var(--cinnabar); box-shadow:0 0 0 3px rgba(140,50,30,.10); }
.chat-input:disabled{ background:rgba(31,27,20,.05); color:var(--ink-faint); }
.chat-go{
  flex:0 0 auto;
  height:40px; padding:0 18px;
  border:1px solid var(--cinnabar);
  border-radius:12px;
  background:var(--cinnabar);
  font-family:var(--font-song); font-size:14px; letter-spacing:.08em;
  color:#FFF9EE;
  cursor:pointer;
}
.chat-go:active{ background:var(--cinnabar-lt); }
.chat-go:disabled{ opacity:.55; }

.chat-hist-tip{
  flex:0 0 auto;
  margin:9px 0 0;
  font-size:11.5px; letter-spacing:.02em;
  color:var(--ink-faint);
  text-align:center;
}
.chat-hist-foot{ flex:0 0 auto; display:flex; gap:9px; margin-top:9px; }
.chat-hist-foot .talk-btn.ghost{ flex:1 1 0; margin-top:0; }

/* 聊天时她说得长，头顶那朵云放宽一点，也好多装两行 */
.pet-think.chat-say{ max-width:min(250px, 60vw); }
.pet-think.saying.chat-say .pet-think-txt{ font-size:12px; line-height:1.6; }

@media (max-height:640px){
  .feed-inner{ max-height:84%; }
  .feed-title{ font-size:17px; }
  .feed-grid{ gap:8px; }
  .chat-hist-inner{ padding-bottom:calc(env(safe-area-inset-bottom, 0px) + 88px); }
  .chat-hist-list{ min-height:104px; }
}

/* ============================================================
   ⑦ 导览地图（点底部「导览地图」滑入）
   底图是手绘的那张（assets/map/map.webp），舞台里能拖能缩。
   圆点两种样子：还没摆过的是虚线空心，自己摆过的是实心朱红；
   点中的那个放大并弹出讲解卡。
   ============================================================ */

/* 舞台：图 + 圆点 + 提示条 + 讲解卡都归它管，超出的裁掉。
   touch-action:none 是必须的 —— 不然手指在图上划会被浏览器当成滚页面。
   （老 WebView 不认这行也没事，那边本来就不会双指缩放。） */
.map-stage{
  position:relative;
  flex:1 1 auto;
  min-height:0;
  overflow:hidden;
  background:
    radial-gradient(120% 80% at 50% 0%, rgba(255,253,245,.6), rgba(255,253,245,0) 72%),
    var(--paper);
  touch-action:none;
  cursor:grab;
  -webkit-tap-highlight-color:transparent;
}
.map-stage.placing{ cursor:crosshair; }

/* ---------- 斜面视角（只有点了建筑才切过去） ---------- */
/* ★ 平面的刻意不留 perspective：一旦有了 3D 渲染上下文，浏览器会先把这一层
   烤成一张位图再投影，放大就糊 —— 跟下面 .map-view 里那句 will-change 同一个坑。
   转到 0 度的时候有没有 perspective 看着一模一样，所以只在 .tilt 上挂着。 */
.map-stage.tilt{ perspective:1400px; perspective-origin:50% 46%; }

/* 转的是这一层：里面装着底图 + 热区 + 圆点，一起斜过去。
   拖和缩动的是外面 .map-view，两边互不打扰。
   ★ 这一段 .85s 是「三段式」里的①，跟 map.js 的 TILT_MS 是同一个数，改一起改。 */
.map-tilt{
  position:relative; width:100%;
  transform-origin:50% 50%;
  transition:transform .85s cubic-bezier(.22,.8,.28,1);
}
.map-stage.tilt .map-tilt{ transform:rotateX(54deg); }

/* 切斜面的时候 .map-view 也得跟着挪一下（图上下被压扁了，要重新居中），
   这一段让它滑过去而不是跳过去；不切的时候不加过渡，拖动才是跟手的。 */
.map-stage.anim .map-view{ transition:transform .85s cubic-bezier(.22,.8,.28,1); }

/* 图这一层就是「图本身」的坐标系，圆点按百分比贴在它上面 */
.map-view{
  position:absolute; left:0; top:0; width:100%;
  transform-origin:0 0;
  /* 2026-10-05：这里原来挂着一句 will-change:transform，正是「放大之后还是糊」的元凶 ——
     它让浏览器先把这一层按当时的比例烤成一张位图，之后再 scale(4) 就是把那张糊图拉大，
     底图再清楚也白搭。同一取景 matrix(4,0,0,4,-588,-554) 实测边缘标准差：
     留着它 69.24，改成 auto 立刻 75.64（不套 transform 的普通 <img> 才 74.52），
     屏幕像素平均差 14.69。拖动时 Chrome 自己会给变化的 transform 升层，不需要这句提示。 */
}

.map-img{
  display:block; width:100%; height:auto;
  border-radius:3px;
  box-shadow:0 3px 22px rgba(31,27,20,.16);
  -webkit-user-drag:none;
  user-select:none;
  pointer-events:none;              /* 手指的事归舞台，图上只负责显示 */
}

/* 建筑热区那一层（31 块多边形）。★ 一条线都不画、连填色都是透明的 ——
   客人看到的就该是一张干净图，红圈和圆点都不该出现在图上；
   手指点上去才知道弹出来的是哪一栋。
   透明归透明，这 31 块都实实在在躺在 DOM 里，而且 pointer-events:all ——
   「点哪儿算哪一栋」交给浏览器自己命中（map.js 的 spotAt 走 elementsFromPoint），
   这样图斜过来、放大缩小都还是准的，不用自己反算变换矩阵。
   vector-effect 让描边宽度不跟着缩放走，放到 4 倍还是那么细。 */
.map-hot{
  position:absolute; left:0; top:0; width:100%; height:auto; display:block;
  pointer-events:none;              /* 这一层自己不接事件，只让底下的多边形接 */
}
.map-hot polygon{
  fill:transparent; stroke:transparent; stroke-width:2;
  vector-effect:non-scaling-stroke;
  pointer-events:all;
}
/* ★ 2026-10-06 用户报「鼠标变成一个手掌，点不动了」的元凶：
   面板没开的时候 #mapSheet 是 opacity:0（看不见），但 **opacity:0 不影响命中测试** ——
   而上面这句 pointer-events:all 是写在多边形自己身上的，父级 .sheet 的 pointer-events:none
   拦不住它。于是首页那张照片上盖着一层看不见的 31 块热区：光标被它们（从 .map-stage 继承）
   变成抓手，点击也被它们吃掉。开面板才允许多边形接事件。 */
#mapSheet:not(.on) .map-hot polygon{ pointer-events:none; }
/* 点中的那一栋：下头垫一块暗色「侧面」、上头盖一块亮色「顶面」，
   两块的点一模一样、侧面整体往下挪一点 —— 合起来就是一块从图上浮起来的板子。 */
.map-hot polygon.side{ fill:rgba(92,44,26,.34); stroke:rgba(92,44,26,.22); stroke-width:1; }
.map-hot polygon.top{ fill:rgba(255,236,186,.60); stroke:#b0392b; stroke-width:2.2; }
.map-hot polygon.top.on{
  fill:rgba(255,229,160,.74); stroke:#a8321f; stroke-width:2.6;
  /* 点中的这一块是「光源」，光晕给足一点，跟上面那道柱光接得上 */
  filter:drop-shadow(0 3px 5px rgba(31,27,20,.45)) drop-shadow(0 0 14px rgba(255,214,120,.78));
}
@keyframes hotflash{
  0%   { stroke:#FFE7A6; stroke-width:6; fill:rgba(255,231,166,.72); }
  100% { stroke:rgba(255,231,166,0); stroke-width:1; fill:rgba(255,236,186,.60); }
}
.map-hot polygon.flash{ animation:hotflash .85s ease-out 2; }

.map-pins{ position:absolute; left:0; top:0; right:0; bottom:0; }

/* 圆点多大，就这一个数：--pin。
   走了一遍：26px（第一版）→ 17px → 14px（「五号字那么大」）→ 现在的 7px（「缩小一倍」）。
   改这一个数，圆点的宽高、往左上挪的半个身位全跟着走。
   7px 已经塞不下圈里的序号了（那层被 CSS 藏了），所以要认出哪个是哪个，
   得点开它 —— 选中的那个会在头顶冒出名字气泡。 */
.map-stage{ --pin:7px; }

/* ---------- 圆点 ---------- */
/* 圆点都是自己加上去的，所以默认就是实的；
   .free = 还没绑到景点的自由圈点（墨色），绑上景点的用朱红 */
.map-pin{
  position:absolute;
  width:var(--pin); height:var(--pin);
  margin:calc(var(--pin) / -2) 0 0 calc(var(--pin) / -2);
  display:flex; align-items:center; justify-content:center;
  padding:0;
  border:.8px solid #FFF9EE;
  border-radius:50%;
  background:var(--cinnabar);
  color:#FFF9EE;
  box-shadow:0 1px 2px rgba(31,27,20,.26);
  font-family:var(--font-song);
  font-size:8px; line-height:1;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:transform .22s var(--ease-out), background .22s ease,
             color .22s ease, border-color .22s ease, box-shadow .22s ease;
}
/* 圈里那个「第几个加进来的」序号：14px 时还看得见，7px 就塞不下了。
   留着这一条、只把显示关掉 —— 哪天 --pin 调回 10px 以上，去掉 display:none 就回来。 */
.map-pin i{ font-style:normal; display:none; }
/* 圆点画得小，但「按得着」的范围还有 30px —— 手机上手指头没那么准 */
.map-pin::after{
  content:'';
  position:absolute; left:50%; top:50%;
  width:30px; height:30px;
  transform:translate(-50%,-50%);
  border-radius:50%;
}
.map-pin.free{
  background:var(--ink-2);
  box-shadow:0 1px 2px rgba(31,27,20,.22);
}
.map-pin.on{ transform:scale(1.7); box-shadow:0 2px 6px rgba(31,27,20,.30); z-index:2; }
.map-pin b{
  position:absolute; left:50%; bottom:150%;
  transform:translateX(-50%);
  padding:2px 7px 3px;
  border-radius:8px;
  background:rgba(31,27,20,.88);
  color:#FFF9EE;
  font-family:var(--font-song);
  font-size:10px; font-weight:400; letter-spacing:.04em;
  white-space:nowrap;
  pointer-events:none;
}
.map-stage.placing .map-pin{ transform:scale(1.1); }
.map-stage.placing .map-pin.on{ transform:scale(1.85); }

/* ---------- 一句话提示条 ---------- */
.map-tip{
  position:absolute; left:12px; right:12px; top:10px;
  margin:0;
  padding:6px 10px;
  border-radius:9px;
  background:rgba(31,27,20,.55);
  color:#FFF9EE;
  font-size:11.5px; line-height:1.5; letter-spacing:.04em;
  text-align:center;
  pointer-events:none;
  opacity:0;
  transition:opacity .3s ease, background .3s ease;
}
.map-tip.on{ opacity:1; }
.map-tip.place{ background:rgba(176,57,43,.92); }
/* 提示条上「找回 N 个」那个键：条子本身是 pointer-events:none，得单独放行 */
.map-tip .tip-find{
  pointer-events:auto;
  margin-left:6px;
  padding:2px 9px;
  border:0;
  border-radius:7px;
  background:#FFF9EE;
  color:var(--cinnabar);
  font:inherit; font-size:11px; font-weight:600; letter-spacing:.04em;
  cursor:pointer;
}
.map-tip .tip-find:active{ transform:scale(.96); }

/* ---------- 讲解卡 ---------- */
/* 卡片挂在【地图舞台正中间】，不再贴底。
   为什么不贴底：贴底那张跟下面的景点条挤在一块，气场也弱；
   挂正中、底下再拖一道光柱，才像「从投影里浮出来的一块板」。
   ★ 它必须是 .map-stage 的孩子、不能挪进 .map-tilt —— 那一层会 rotateX 转起来，
     卡片跟着躺下就没法看了。（现在就是 .map-stage 的直系孩子，别改。）
   定位走 left/top + translate(-50%,-50%)，所以 transform 被占住了，
   入场那一下的位移就写在 translate 里的那个 +10px 上。 */
.map-card{
  position:absolute; left:50%; top:50%;
  width:min(300px, calc(100% - 44px));
  z-index:3;
  padding:14px 15px 13px;
  border:1px solid rgba(31,27,20,.14);
  border-radius:14px;
  background:var(--paper);                 /* ← 跟界面（各张 sheet）一个色，不再是一块惨白 */
  box-shadow:0 10px 30px rgba(31,27,20,.24);
  opacity:0;
  pointer-events:none;
  transform:translate(-50%, calc(-50% + 10px));
  /* 收起：立刻就走，不拖泥带水（延迟只挂在 .on 那一条上，见下） */
  transition:opacity .28s var(--ease-io), transform .38s var(--ease-out);
}
.map-card.on{
  opacity:1; pointer-events:auto; transform:translate(-50%,-50%);
  /* 底下那道光会照到卡片底面，加一层暖投影，卡片才像「被光照着」而不是贴上去的 */
  box-shadow:0 10px 30px rgba(31,27,20,.24), 0 16px 40px rgba(255,203,110,.40);
  /* ★ 三段式里的③：等图斜完（.85s）、光柱亮完（.86→1.02s）才轮到她浮出来。
     1.12s 这个数跟 map.js 的 CARD_AT 是同一个，改一起改。
     transition 写在 .on 这一条上：只作用于【变成 .on】那一下，
     摘掉 .on 的时候走的是上面那条没有延迟的，收得干脆。 */
  transition:opacity .5s var(--ease-io) 1.12s,
             transform .58s var(--ease-out) 1.12s;
}
/* ★ 建筑卡片改成【贴底定位】：下沿离舞台底边留 20.9%。
   原来是「中心在 32%」，桌面量下来下沿落在 58.25%，离底边 41.75% —— 底下空一大片。
   他说的「卡片底部离屏幕底部的距离缩短一半」，41.75% 砍一半就是 20.9%。
   ★ 为什么改成 bottom 而不是继续调 top：卡片是会长的（以后填了图片和简介就更高），
     按 top 定位的话下沿会随着卡片长高往上跑；按 bottom 定位，下沿钉死在这儿。
   ★ transform 也得跟着改：.map-card 那套是 translate(-50%,-50%)（拿 top 当中心使的），
     贴底之后竖向不用再挪那半个卡片，入场那 10px 位移留在原来的位置上。 */
.map-card.spot{ top:auto; bottom:20.9%; transform:translate(-50%, 10px); }
.map-card.spot.on{ transform:translate(-50%, 0); }

/* ---- 那一道「从景点投影上来」的光柱 ----
   ★ 不是「卡片底下挂一条光」，是「点中的那一栋往上打出来的一道光」：
     根扎在那一栋上，柱身斜着升上来，顶在卡片下沿 —— 卡片是被它投出来的。
     这一层贴着卡片下沿铺开（左右跟卡片一样宽），横着不动；
     棱锥切在 ::after 上、歪多少看 --skew（楼在卡片左边就往左歪），
     落地那圈亮斑是 ::before，横着挪 --dx。
     高度、--skew、--dx 三个数都由 map.js 的 poseBeam() 每帧算好写进来。
     形状与「化」还是分两层：切在伪元素上，blur 留在 .map-beam 自己身上 ——
     同一个元素上写 filter 与 clip-path，规范是先 filter 后 clip，糊完再切一刀等于白糊。 */
.map-beam{
  position:absolute; left:0; right:0; top:100%;
  height:0;                          /* ← JS 写：卡片下沿到那一栋中心的距离 */
  pointer-events:none;
  filter:blur(4px);
  opacity:0;
  /* 收起：跟着卡片一起立刻灭 */
  transition:opacity .3s var(--ease-io);
  --dx:0px;
  --skew:0deg;
}
.map-beam::after{
  content:'';
  position:absolute; left:50%; top:0;
  width:126%; height:100%;
  transform-origin:50% 0;
  transform:translateX(-50%) skewX(var(--skew));
  clip-path:polygon(30% 0, 70% 0, 60% 100%, 40% 100%);
  background:
    radial-gradient(ellipse 22% 100% at 50% 92%,
      rgba(255,252,232,.66) 0%, rgba(255,240,170,.22) 52%, rgba(255,238,160,0) 100%),
    linear-gradient(to top,
      rgba(255,240,168,.50) 0%,
      rgba(255,246,201,.60) 22%,
      rgba(255,249,218,.40) 58%,
      rgba(255,252,236,.00) 100%);
}
.map-beam::before{
  content:'';
  position:absolute; left:50%; top:100%;
  width:172px; height:46px;
  transform:translate(-50%,-50%) translateX(var(--dx));
  border-radius:50%;
  background:radial-gradient(ellipse at center,
    rgba(255,243,182,.70) 0%, rgba(255,236,158,.26) 46%, rgba(255,236,158,0) 76%);
}
/* ★ 三段式里的②：图一停稳（.85s）光柱就【直接亮】，不再慢慢等。
   .86s 这个延迟跟 map.js 的 BEAM_AT 是同一个数，改一起改。
   以前这里挂着 .5s 的渐亮，看着像「移动完了还得等一秒才亮」—— 他自己说的：
   「移动过去了就不要等一秒钟才亮光了，直接亮光」。现在 .16s，一眨眼就满了。
   光柱仍然比卡片先亮 —— 是「那一栋先把光打上来，板子才被顶出来」。 */
.map-card.on .map-beam{
  opacity:1;
  transition:opacity .16s var(--ease-io) .86s;
}
/* 光轻轻呼吸一下，才像「在投」，不像画上去的一块色 */
.map-card.on .map-beam::after{ animation:beampulse 2.8s ease-in-out infinite; }
@keyframes beampulse{
  0%, 100%{ opacity:.74; }
  50%     { opacity:1; }
}

.map-card-x{
  position:absolute; right:8px; top:8px;
  width:26px; height:26px;
  border:0; border-radius:50%;
  background:transparent;
  color:var(--ink-soft);
  font-size:17px; line-height:1;
  cursor:pointer;
}
.map-card-x:hover{ background:rgba(31,27,20,.07); color:var(--cinnabar); }

.map-card-cat{
  margin:0 0 4px;
  font-size:10.5px; letter-spacing:.24em;
  color:var(--cinnabar);
}
.map-card-name{
  font-family:var(--font-brush);
  font-weight:400;
  font-size:19px; line-height:1.2; letter-spacing:.06em;
  color:var(--ink);
}
/* ---------- 卡片的内容区：上半实拍、下半简介 ----------
   ★ 两块都是【定高的框】，不是「有内容才撑开」——
     这样照片还没放、介绍词还没写的时候，卡片本身就是最终那个尺寸，
     以后把图文填进去，卡片不会跳高跳矮，底下那道投影光柱也不用重算长度。 */
.map-card-media,
.map-card-desc{ display:none; }            /* 只有建筑卡片用得上 */
.map-card.spot .map-card-media,
.map-card.spot .map-card-desc{ display:block; }

/* 上半：实拍。空着的时候是一块虚线空框，中间浮一个「图片」。 */
.map-card-media{
  position:relative;
  height:112px;
  margin:1px 0 9px;
  border:1px dashed rgba(31,27,20,.20);
  border-radius:10px;
  background:rgba(31,27,20,.035);
  overflow:hidden;
}
/* 空框里那行字【垫在照片底下】。照片带 [hidden] 时是 display:none，这行就露出来；
   照片一摆上（不透明 + z-index:1）刚好盖住它。省掉 :has()，也省掉一个状态类。 */
.map-card-ph{
  position:absolute; left:0; right:0; top:50%;
  transform:translateY(-50%);
  text-align:center;
  font-size:11px; letter-spacing:.2em;
  color:rgba(31,27,20,.26);
}
/* 顶上那张实拍。display:block 会盖掉浏览器对 [hidden] 的默认处理，所以得自己再写一条。 */
.map-card-photo{
  position:relative; z-index:1;
  display:block; width:100%; height:100%;
  object-fit:cover; border-radius:9px; background:#efe8da;
}
.map-card-photo[hidden]{ display:none; }

/* 下半：简介。框子会自己长高长矮 —— 短就贴着字缩起来，长了才在框里滚（长到 74px 封顶）。 */
.map-card-desc{
  position:relative;
  height:auto;
  max-height:74px;
  margin-top:6px;
  border:1px dashed rgba(31,27,20,.20);
  border-radius:10px;
  background:rgba(31,27,20,.035);
  overflow:auto;
  -webkit-overflow-scrolling:touch;
}
/* 滚的是外框自己，里面这层就别再插一手了，不然「太短自动变小」会被它顶住。 */
.map-card-desc .map-card-scroll{ height:auto; max-height:none; overflow:visible; }
/* 还没写简介：正文收掉（.notext 由 map.js 的 paintSpot 切），空框中间浮一个「简介」。 */
.map-card.spot.notext .map-card-desc::after{
  content:'简介';
  position:absolute; left:0; right:0; top:50%;
  transform:translateY(-50%);
  text-align:center;
  font-size:11px; letter-spacing:.2em;
  color:rgba(31,27,20,.26);
}

.map-card-scroll{ max-height:33vh; overflow:auto; -webkit-overflow-scrolling:touch; }
.map-card-text{
  margin:8px 10px 0;
  font-family:var(--font-song);
  font-size:13px; line-height:1.78;
  color:var(--ink-2);
}

/* 卡片底下那颗导航按钮。
   默认收着（display:none）—— 自由圈点那张卡片没有景点可导，别摆出来。
   建筑卡片的位置 / 大小 / 颜色已经由卡片布局编辑器（?cardedit=1）画好、
   烘在样式表末尾那块 `.map-card.spot .map-card-go` 里，那边写了 display:flex 把它放出来。 */
.map-card-go{
  display:none;
  width:48px; height:48px; padding:0;
  border:0; border-radius:50%;
  background:var(--cinnabar); color:#FFF8E6;
  font-family:var(--font-song,Georgia,serif); font-size:13px; letter-spacing:.06em;
  align-items:center; justify-content:center;
  cursor:pointer;
  box-shadow:0 6px 16px rgba(176,57,43,.36);
}

/* 自由圈点：卡片里就一个改名框（景点圈点才显示名字 / 讲解 / 念一遍） */
.map-card-input{
  display:none;
  width:100%;
  margin:9px 0 0;
  padding:9px 11px;
  border:1px solid rgba(31,27,20,.20);
  border-radius:9px;
  background:rgba(255,255,255,.72);
  font-family:var(--font-song);
  font-size:15px; letter-spacing:.04em;
  color:var(--ink);
}
.map-card-input::placeholder{ color:var(--ink-soft); opacity:.72; }
.map-card-input:focus{ outline:none; border-color:var(--cinnabar); background:#fff; }
.map-card-hint{
  display:none;
  margin:7px 0 0;
  font-size:10.5px; line-height:1.6; letter-spacing:.04em;
  color:var(--ink-soft);
}
.map-card.free .map-card-input,
.map-card.free .map-card-hint{ display:block; }
.map-card.free .map-card-name,
.map-card.free .map-card-scroll{ display:none; }
.map-card.free .map-card-row .talk-btn{ flex:0 0 auto; padding-left:18px; padding-right:18px; }

/* 建筑卡片：输入框、提示、「删掉」都是圈点才有的东西，这里全收起来。 */
.map-card.spot .map-card-input,
.map-card.spot .map-card-hint,
.map-card.spot #mapCardDel{ display:none; }
/* 那一排整个收掉：里面最后一颗「删掉」也藏了，空着也占着 margin-top:11px 一条白带
   （「念一遍 / 停下」拆掉之后才露出来的毛边）。 */
.map-card.spot .map-card-row{ display:none; }
/* 介绍词还没写的时候，正文那一块整个收掉 —— 卡片上就只剩一个建筑名，
   干干净净的空卡片（客人问「点开怎么是空的」，是还没写，不是坏了）。 */
.map-card.spot.notext .map-card-scroll{ display:none; }

/* ---------- 桌宠站到导览地图上 ---------- */
/* 开图的时候 map.js 会把 #pet 整个人搬进这张 sheet 里。为什么非搬不可：
   .home 自己是 `position:absolute; z-index:10`（还带 opacity），也就是一个
   层叠上下文 —— 桌宠待在里面，z-index 抬到多高都浮不出 .home；
   而地图这张 sheet 是 .home 的兄弟、z-index 30，永远压着她。
   搬进来之后「看得见」这条得补上：本来是 `.home.home-in .pet{opacity:1}`，
   人已经不在 .home 里了，不补就是一个 opacity 0 的空框（她还是能说话，
   但谁也看不见）。站哪儿（bottom / right）由 map.js 的 petStand() 按地图
   舞台的右下角现算，卡片一开就往上让一截。 */
.sheet.map .pet{
  opacity:1;
  transform:none;
  right:10px;
  z-index:9;                    /* 地图卡片是 3；扫一扫那张（12）仍压着她 */
  transition:opacity .6s ease, transform .55s var(--ease-out),
             bottom .45s var(--ease-out);
}

.map-card-row{ display:flex; gap:9px; margin-top:11px; }
.map-card-row .talk-btn{ flex:1 1 0; margin-top:0; font-size:14px; padding-left:6px; padding-right:6px; letter-spacing:.04em; }

/* ---------- 「去过」的小对勾 ----------
   2026-10-06：原来这一排是「景点条」（森林小火车、茶山梯田…19 个横着滚的胶囊），
   用户要求全删 —— 图上一条线都不画，找地方走底栏的「景点总览」名单。
   这里换成一个对勾层：游客在「标记去过的地方」模式里点一栋楼，就在它身上留个对勾。
   它跟底图同一层（挂在 #mapTilt 里），所以斜过来、放大缩小都自动跟着走；
   位置是百分比（拿地图 2560 那套坐标系算的），整层 pointer-events:none，
   一个点击都不许它抢 —— 点击还得落到热区上。 */
.map-marks{
  position:absolute; left:0; top:0; right:0; bottom:0;
  pointer-events:none;
  z-index:2;
}
.map-mark{
  position:absolute;
  width:22px; height:22px;
  margin:-11px 0 0 -11px;                 /* 以中心点定位 */
  border-radius:50%;
  background:var(--cinnabar);
  color:#FFF9EE;
  font-family:var(--font-song);
  font-size:13px; line-height:22px; text-align:center;
  box-shadow:0 1px 3px rgba(31,27,20,.38);
  animation:markIn .3s var(--ease-out) both;
}
@keyframes markIn{
  from{ transform:scale(.3); opacity:0; }
  to{ transform:scale(1); opacity:1; }
}

/* ---------- 底部三个键 ---------- */
.map-foot{
  flex:0 0 auto;
  display:flex; gap:8px;
  padding:10px 14px calc(env(safe-area-inset-bottom, 0px) + 12px);
}
.map-foot .talk-btn{ margin-top:0; }
.map-foot .talk-btn.main{ flex:1.15 1 0; }
.map-foot .talk-btn.ghost{ flex:1 1 0; font-size:13px; letter-spacing:.04em; }
.map-foot .talk-btn.arm{ background:var(--cinnabar); border-color:var(--cinnabar); color:#FFF9EE; }

/* ---------- 导出点位那张小纸 ----------
   服务器上有 /api/save-map（serve.js）时根本不会露脸；
   纯静态托管、或者服务器够不着的时候才翻出来，
   把 JSON 摆成一块能全选的字。 */
.map-out{
  position:absolute; left:12px; right:12px; top:12px; bottom:12px;
  z-index:4;
  display:none;
  flex-direction:column;
  gap:9px;
  padding:12px 13px 13px;
  border:1px solid rgba(31,27,20,.14);
  border-radius:14px;
  background:rgba(255,253,245,.98);
  box-shadow:0 6px 26px rgba(31,27,20,.20);
}
.map-out.on{ display:flex; }
.map-out-tip{
  flex:0 0 auto;
  margin:0;
  font-size:12.5px;
  line-height:1.65;
  color:var(--ink-2);
}
.map-out-box{
  flex:1 1 auto;
  width:100%;
  min-height:0;
  resize:none;
  padding:9px 10px;
  border:1px solid rgba(31,27,20,.16);
  border-radius:9px;
  background:#FFFDF5;
  color:var(--ink);
  font-family:ui-monospace, Menlo, Consolas, "Courier New", monospace;
  font-size:11px;
  line-height:1.55;
  word-break:break-all;
  -webkit-user-select:text;
  user-select:text;
}
.map-out-box:focus{ outline:none; border-color:var(--cinnabar); }
.map-out-row{ flex:0 0 auto; display:flex; gap:9px; }
.map-out-row .talk-btn{ flex:1 1 0; margin-top:0; font-size:14px; }

@media (max-height:640px){
  .map-card-scroll{ max-height:26vh; }
  .map-card-name{ font-size:17px; }
  .map-foot{ padding-top:8px; }
}

/* ---------- 游客那边：编辑那一套一律不露脸 ----------
   图上这些圈点是后台编辑用的定位点位，不是给游客看的东西。谁算编辑者由
   map.js 决定（本机 / 局域网那套开发地址），它给这张 sheet 挂 .edit 类；
   没有这个类，下面这些「只有编辑用得着」的东西全部收起来：
   加圈点 / 清空圈点 / 导出点位 / 改名框 / 删掉 / 景点后面那个「已落位」的小点，
   以及「还没落位」的虚线态。 */
.map:not(.edit) #mapPlace,
.map:not(.edit) #mapClear,
.map:not(.edit) #mapExport,
.map:not(.edit) .map-card-input,
.map:not(.edit) .map-card-hint,
.map:not(.edit) #mapCardDel{ display:none !important; }
/* 自由圈点那张卡片里只剩一颗「删掉」，让它占满整行 */
.map:not(.edit) .map-card-row .talk-btn{ flex:1 1 0; }

/* ---------- 「你在这儿」：游客那边唯一会看见的那个点 ----------
   网址里带 #p=<圈点 id>（将来二维码里写的就是它）时才会出现，比编辑用的
   圆点大一圈、戴一圈淡淡的朱红光晕，头顶常驻「你在这儿」。 */
.map-pin.beacon{
  --pin:11px;
  border-width:1.4px;
  box-shadow:0 0 0 5px rgba(176,57,43,.16), 0 2px 6px rgba(31,27,20,.32);
  animation:mapBeacon 2.4s ease-in-out infinite;
}
.map-pin.beacon b{ bottom:160%; font-size:11px; }
@keyframes mapBeacon{
  0%,100%{ box-shadow:0 0 0 5px rgba(176,57,43,.16), 0 2px 6px rgba(31,27,20,.32); }
  50%    { box-shadow:0 0 0 9px rgba(176,57,43,.05), 0 2px 6px rgba(31,27,20,.32); }
}





/* ==============================================================
   ⑧ 拍照还是相册：点「拍一张」先弹的二选一
   （券详情页里那一层，盖在 .detail 上；#shotPick / #pickCam / #pickAlbum / #pickCancel）
   ============================================================== */
.pick{
  position:absolute; inset:0; z-index:12;
  display:flex; align-items:flex-end; justify-content:center;
  padding:0 16px calc(env(safe-area-inset-bottom, 0px) + 18px);
  background:rgba(31,27,20,.36);
  opacity:0; pointer-events:none;
  transition:opacity .2s var(--ease-io);
}
.pick.on{ opacity:1; pointer-events:auto; }

.pick-inner{
  width:100%; max-width:320px;
  display:flex; flex-direction:column; gap:8px;
  padding:15px 14px 10px;
  border-radius:16px;
  background:var(--paper);
  box-shadow:0 16px 38px rgba(31,27,20,.3);
  transform:translateY(12px);
  transition:transform .26s var(--ease-out);
}
.pick.on .pick-inner{ transform:none; }

.pick-title{
  margin:0 0 3px;
  text-align:center;
  font-size:12.5px; letter-spacing:.14em;
  color:var(--ink-2);
}

.pick .talk-btn{ margin-top:0; }

.pick-cancel{
  margin-top:2px; padding:9px;
  border:0; background:none;
  color:var(--ink-2);
  font-family:inherit; font-size:12.5px; letter-spacing:.14em;
  cursor:pointer;
}
.pick-cancel:active{ color:var(--cinnabar); }

/* ---------- 底栏多了一个「显示点位」----------
   圈点在图上默认不画（后台的东西），编辑要看/要拖/要删时才按这个键露出来。
   多一个键，窄屏就让它换行，别把五个键挤成一条。 */
.map-foot{ flex-wrap:wrap; row-gap:6px; }
.map-foot .talk-btn{ min-width:64px; }
.map-foot .talk-btn.on{
  border-color:var(--cinnabar);
  color:var(--cinnabar);
  background:rgba(176,57,43,.08);
}

/* ---------- 「扫码看我在哪」----------
   到了景点扫一下码，就知道自己在这张图的哪一处。
   编辑者那边它跟别的键一样灰底（那边的主角是「加圈点」）；
   游客那边它就是图上最显眼的那个键（朱红实心），「回到全图」让一让。 */
.map:not(.edit) #mapQr{
  flex:1.15 1 0;
  background:var(--cinnabar);
  border-color:var(--cinnabar);
  color:#FFF9EE;
}
.map:not(.edit) #mapQr:active{ background:var(--cinnabar-lt); }
.map:not(.edit) #mapOv{ flex:.92 1 0; }


/* ---------- 「景点总览」那张名单 ----------
   底栏那个键翻出来的整屏面板（跟「扫一扫」那张一个写法：absolute inset:0 + .on 才显）。
   图上不画圈线，客人找地方就靠这份名单；点一条 = 那一栋在图上放大变亮挪正中。 */
.map-ov{
  position:absolute; inset:0;
  z-index:6;
  display:none;
  flex-direction:column;
  background:var(--paper);
  border-radius:14px;
  box-shadow:0 10px 30px rgba(31,27,20,.30);
  overflow:hidden;
}
.map-ov.on{ display:flex; }
.ov-head{
  flex:0 0 auto;
  padding:14px 16px 10px;
  border-bottom:1px solid rgba(31,27,20,.12);
}
.ov-title{
  margin:0;
  font-family:var(--font-brush);
  font-size:17px; letter-spacing:.14em;
  color:var(--ink);
}
.ov-sub{
  margin:5px 0 0;
  font-family:var(--font-song);
  font-size:12px; letter-spacing:.03em;
  color:var(--ink-soft);
}
/* ---------- 名单里的搜索框（2026-10-06 加）----------
   31 条翻着费劲，抬头下面挂一条：一直看得见（抬头不滚，滚的是名单）。
   一边打一边筛，「茶」→ 茶山 / 茶乡农庄 / 茶识园…，打 12 就直接跳到 12 号。 */
.ov-search{ position:relative; margin-top:9px; }
.ov-input{
  width:100%; box-sizing:border-box;
  padding:9px 34px 9px 12px;
  border:1px solid rgba(31,27,20,.16);
  border-radius:10px;
  background:rgba(255,255,255,.55);
  color:var(--ink);
  font-family:var(--font-song);
  font-size:14px; letter-spacing:.04em; line-height:1.2;
  outline:none;
  -webkit-appearance:none;
}
.ov-input::placeholder{ color:rgba(31,27,20,.32); letter-spacing:.02em; }
.ov-input:focus{ border-color:var(--cinnabar); background:#fff; }
.ov-clear{
  position:absolute; right:6px; top:50%;
  width:24px; height:24px; margin-top:-12px;
  padding:0; border:0; border-radius:50%;
  background:rgba(31,27,20,.10);
  color:var(--ink-soft);
  font-size:12px; line-height:24px; text-align:center;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.ov-clear[hidden]{ display:none; }
.ov-none{
  margin:26px 0;
  font-family:var(--font-song);
  font-size:13px; letter-spacing:.06em; text-align:center;
  color:var(--ink-soft);
}
.ov-hit{ color:var(--cinnabar); font-weight:600; }
.ov-list{
  flex:1 1 auto;
  overflow-y:auto;
  -webkit-overflow-scrolling:touch;
  padding:4px 8px 8px;
}
.ov-row{
  width:100%;
  display:flex; align-items:center; gap:10px;
  padding:11px 8px;
  border:0; border-bottom:1px solid rgba(31,27,20,.08);
  border-radius:8px;
  background:transparent;
  color:var(--ink-2);
  font-family:var(--font-song);
  font-size:14px; letter-spacing:.04em;
  text-align:left;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.ov-row:last-child{ border-bottom:0; }
.ov-no{
  flex:0 0 auto;
  width:22px; height:22px;
  border-radius:50%;
  background:rgba(31,27,20,.08);
  color:var(--ink-soft);
  font-style:normal;
  font-size:11.5px; line-height:22px; text-align:center;
}
.ov-name{ flex:1 1 auto; }
.ov-tick{
  flex:0 0 auto;
  font-style:normal;
  font-size:15px;
  color:var(--cinnabar);
}
.ov-row.got .ov-name{ color:var(--ink); }
.ov-row.on{ background:rgba(176,57,43,.10); }
.ov-row.on .ov-no{ background:var(--cinnabar); color:#FFF9EE; }
.ov-foot{
  flex:0 0 auto;
  padding:8px 14px calc(env(safe-area-inset-bottom, 0px) + 12px);
}
.ov-foot .talk-btn{ width:100%; }

/* ---------- 「扫码看我在哪」那块面板 ----------
   盖住整张导览地图（抬头、舞台、底栏全盖上），因为要看相机预览。
   它只认点位的二维码，跟「AR 实景」那套识图是两回事。 */
.map-qr{
  position:absolute; inset:0;
  z-index:6;
  display:none;
  flex-direction:column;
  align-items:center;
  gap:12px;
  padding:calc(env(safe-area-inset-top, 0px) + 30px) 20px calc(env(safe-area-inset-bottom, 0px) + 18px);
  background:#141210;
  text-align:center;
}
.map-qr.on{ display:flex; }

.qr-head{ flex:0 0 auto; }
.qr-title{
  margin:0 0 5px;
  font-size:15px; letter-spacing:.16em;
  color:#F4E9D2;
}
.qr-sub{
  margin:0;
  font-size:12.5px; line-height:1.6;
  color:rgba(244,233,210,.72);
}

.qr-box{
  position:relative;
  flex:1 1 auto;
  width:100%; max-width:330px;
  min-height:0;
  border-radius:16px;
  overflow:hidden;
  background:#0C0B0A;
}
.qr-video{
  display:block;
  width:100%; height:100%;
  object-fit:cover;
  background:#0C0B0A;
}
.qr-frame{
  position:absolute; left:12%; top:50%;
  width:76%; aspect-ratio:1 / 1;
  transform:translateY(-50%);
  border-radius:14px;
  box-shadow:0 0 0 200vmax rgba(12,11,10,.42);
}
.qr-frame::before, .qr-frame::after{
  content:'';
  position:absolute;
  width:26px; height:26px;
  border:2.5px solid #F4E9D2;
}
.qr-frame::before{ left:0; top:0; border-right:0; border-bottom:0; border-radius:8px 0 0 0; }
.qr-frame::after{ right:0; bottom:0; border-left:0; border-top:0; border-radius:0 0 8px 0; }

.qr-foot{
  flex:0 0 auto;
  display:flex; gap:8px;
  width:100%;
}
.qr-foot .talk-btn{ flex:1 1 0; margin-top:0; font-size:13px; }
.map-qr .talk-btn.ghost{ color:#F4E9D2; border-color:rgba(244,233,210,.34); }
.map-qr .talk-btn.ghost:active{ background:rgba(244,233,210,.12); }

/* ============================================================
   账户（昵称）：进站先问一句「你叫什么名字」
   —— 弹窗 z-index 60（比 toast 40、四张 sheet 30、intro 20 都高）；
      昵称小牌压在积分牌正上方，跟它同一套纸色 / 朱砂边的样子。
   ============================================================ */
.acct{
  position:absolute; inset:0;
  z-index:60;
  display:flex; align-items:center; justify-content:center;
  padding:24px;
  background:rgba(24,20,14,.52);
  -webkit-backdrop-filter:blur(6px);
  backdrop-filter:blur(6px);
  opacity:0; visibility:hidden;
  transition:opacity .26s ease, visibility .26s;
}
.acct.on{ opacity:1; visibility:visible; }

.acct-card{
  position:relative;
  width:100%; max-width:330px;
  padding:24px 22px 18px;
  border:1px solid rgba(176,57,43,.42);
  border-radius:4px;
  background:linear-gradient(180deg, rgba(249,242,222,.97), rgba(243,232,205,.97));
  box-shadow:0 18px 44px rgba(20,16,10,.42);
  text-align:center;
  transform:translateY(14px) scale(.98);
  transition:transform .3s var(--ease-out);
}
.acct.on .acct-card{ transform:none; }
.acct-card.acct-shake{ animation:acctShake .32s ease; }
@keyframes acctShake{
  0%,100%{ transform:translateX(0); }
  25%{ transform:translateX(-6px); }
  75%{ transform:translateX(6px); }
}

.acct-seal{
  display:inline-block;
  margin:0 auto 10px;
  padding:3px 9px 4px;
  border:1.5px solid var(--cinnabar);
  border-radius:2px;
  color:var(--cinnabar);
  font-family:var(--font-brush);
  font-size:15px;
  letter-spacing:.2em;
  text-indent:.2em;
}
.acct-title{
  margin:0 0 6px;
  font-family:var(--font-brush);
  font-weight:400;
  font-size:20px;
  color:#2B2620;
}
.acct-sub{
  margin:0 0 14px;
  font-size:12px;
  line-height:1.75;
  color:rgba(43,38,32,.72);
}
.acct-input{
  display:block;
  width:100%;
  padding:10px 12px;
  border:1px solid rgba(176,57,43,.36);
  border-radius:3px;
  background:rgba(255,253,245,.9);
  color:#2B2620;
  font-family:inherit;
  font-size:16px;              /* 16px 以下 iOS 会自动放大页面 */
  text-align:center;
  letter-spacing:.06em;
  outline:none;
}
.acct-input:focus{ border-color:var(--cinnabar); box-shadow:0 0 0 3px rgba(176,57,43,.12); }
.acct-input::placeholder{ color:rgba(43,38,32,.34); letter-spacing:0; }

.acct-row{ display:flex; gap:8px; margin-top:12px; }
.acct-row .talk-btn{ flex:1 1 0; margin-top:0; }

.acct-tip{
  min-height:16px;
  margin:10px 0 0;
  font-size:11.5px;
  line-height:1.7;
  color:var(--cinnabar);
}
.acct-x{
  margin-top:2px;
  padding:4px 8px;
  border:0; background:none;
  color:rgba(43,38,32,.5);
  font-family:inherit; font-size:11.5px;
  text-decoration:underline;
  cursor:pointer;
}

/* 主界面：昵称小牌（积分牌正上方，点它改名） */
.home-user{
  position:absolute;
  left:0; right:0;
  bottom:calc(env(safe-area-inset-bottom, 0px) + 158px);
  z-index:7;
  width:max-content;
  margin:0 auto;
  display:flex; align-items:center; gap:7px;
  padding:4px 12px 5px;
  border:1px solid rgba(176,57,43,.30);
  border-radius:3px;
  background:rgba(246,237,208,.58);
  -webkit-backdrop-filter:blur(2px);
  backdrop-filter:blur(2px);
  color:var(--cinnabar);
  font-family:inherit;
  cursor:pointer;
  opacity:0;
  transform:translateY(10px);
  transition:opacity .8s ease .3s, transform .8s var(--ease-out) .3s;
}
.home.home-in .home-user{ opacity:1; transform:none; }
.home-user em{ font-style:normal; font-size:12px; letter-spacing:.06em; max-width:9em; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.home-user i{ font-style:normal; font-size:10px; letter-spacing:.14em; opacity:.66; }
.home-user:active{ background:rgba(246,237,208,.86); }
.home-user-on em{ font-weight:600; }

/* 弹窗底下「这台机器上还有谁」：点一下就换成那个人（换回去不是新开一个） */
.acct-who{
  display:flex; flex-wrap:wrap; gap:6px;
  justify-content:center; align-items:center;
  margin-top:12px; padding-top:11px;
  border-top:1px dashed rgba(176,57,43,.24);
}
.acct-who-lab{
  width:100%;
  font-size:10.5px; letter-spacing:.14em;
  color:rgba(43,38,32,.5);
}
.acct-who-one{
  padding:4px 10px 5px;
  border:1px solid rgba(176,57,43,.32);
  border-radius:3px;
  background:rgba(255,253,245,.72);
  color:var(--cinnabar);
  font-family:inherit; font-size:12px;
  cursor:pointer;
}
.acct-who-one:active{ background:rgba(176,57,43,.10); }


/* ============================================================
   ⑧ 茶小仙的旅游日记 v2（桌宠气泡菜单「📔 旅游日记」滑出来的那本册子）
   ------------------------------------------------------------
   #diarySheet 还是 .sheet（第 409 行那套：整屏、宣纸底、z-index 30），
   这里只管册子里面的排版。v2 跟 v1 的「18 页讲解点卡片」不是一回事了：
     · 左边 .diary-rail = 一竖排横杠，一篇一根、从上往下按时间加；
     · 右边 .diary-page = 主页面，就是那一篇的正文（AI 用茶小仙的口吻写的）。
   .diary-wrap 是横向 flex，两个子项各自滚；不写 min-height:0 的话
   它们在手机上不肯收缩，正文一长就把表头顶出去 —— 跟别的 sheet 同一个坑。
   ============================================================ */
.diary-wrap{
  flex:1 1 auto;
  min-height:0;
  display:flex;
  align-items:stretch;
}

/* ---- 左边那一竖排横杠：一篇一根 ---- */
.diary-rail{
  flex:0 0 auto;
  width:34px;
  min-height:0;
  overflow-y:auto;
  overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;
  padding:10px 0 calc(env(safe-area-inset-bottom, 0px) + 18px);
  border-right:1px solid rgba(31,27,20,.10);
  scrollbar-width:none;
}
.diary-rail::-webkit-scrollbar{ width:0; height:0; }

.diary-bar{
  display:block;
  width:100%;
  height:14px;
  padding:0;
  border:0;
  background:none;
  cursor:pointer;
}
.diary-bar > span{
  display:block;
  width:22px;
  height:2px;
  margin:6px auto;
  border-radius:2px;
  background:rgba(31,27,20,.24);
  transition:width .18s ease, background .18s ease;
}
.diary-bar:hover > span{ width:26px; background:rgba(31,27,20,.40); }
.diary-bar.on > span{ width:28px; height:3px; margin:5.5px auto; background:var(--cinnabar); }
/* 刚新添的那一根亮一下，让人看见「又加了一篇」 */
.diary-bar.fresh > span{ animation:diaryFresh 1.1s ease 1; }
@keyframes diaryFresh{
  0%  { width:30px; height:3px; background:var(--cinnabar); }
  60% { width:30px; height:3px; background:var(--cinnabar); }
  100%{ width:22px; height:2px; background:rgba(31,27,20,.24); }
}

/* ---- 右边：这一篇的正文 ---- */
.diary-page{
  flex:1 1 auto;
  min-width:0;
  min-height:0;
  overflow-y:auto;
  -webkit-overflow-scrolling:touch;
  padding:16px 20px calc(env(safe-area-inset-bottom, 0px) + 30px);
}

.diary-no{
  font-size:9.5px;
  letter-spacing:.18em;
  color:var(--ink-faint);
}
.diary-when{
  margin-top:5px;
  font-size:11px;
  letter-spacing:.06em;
  color:var(--ink-soft);
}
.diary-text{
  margin:14px 0 0;
  font-size:13px;
  line-height:2.05;
  color:var(--ink-2);
  text-indent:2em;
}
.diary-text:first-of-type{ margin-top:18px; }
.diary-sign{
  margin-top:20px;
  text-align:right;
  font-family:var(--font-brush);
  font-size:13px;
  letter-spacing:.12em;
  color:var(--ink-soft);
}
.diary-events{
  margin-top:9px;
  text-align:right;
  font-size:9.5px;
  letter-spacing:.12em;
  color:var(--ink-faint);
}

/* ---- 空着 / 正在写 / 翻不开 ---- */
.diary-state{ padding:34px 8px; text-align:center; }
.diary-state em{
  display:block;
  font-style:normal;
  font-size:12.5px;
  line-height:1.95;
  color:var(--ink-soft);
}
.diary-state .diary-why{ display:block; margin-top:8px; font-size:10px; color:var(--ink-faint); }
.diary-empty{
  padding:30px 6px;
  text-align:center;
  font-size:12.5px;
  line-height:2;
  color:var(--ink-soft);
}
.diary-empty em{ display:block; margin-top:6px; font-style:normal; font-size:11.5px; color:var(--ink-faint); }
.diary-btn{
  margin-top:16px;
  padding:7px 16px;
  border:1px solid rgba(176,57,43,.5);
  border-radius:4px;
  background:rgba(255,253,245,.9);
  font-family:var(--font-song);
  font-size:12px;
  letter-spacing:.1em;
  color:var(--cinnabar);
  cursor:pointer;
}
.diary-dots{ display:inline-flex; gap:5px; margin-bottom:12px; }
.diary-dots b{
  width:5px; height:5px;
  border-radius:50%;
  background:rgba(176,57,43,.55);
  animation:diaryDot 1.2s ease-in-out infinite;
}
.diary-dots b:nth-child(2){ animation-delay:.18s; }
.diary-dots b:nth-child(3){ animation-delay:.36s; }
@keyframes diaryDot{
  0%,100%{ opacity:.25; transform:translateY(0); }
  50%    { opacity:1;   transform:translateY(-3px); }
}

/* ---- 表头那个「看一眼」的圆钮 ---- */
.diary-refresh{
  flex:0 0 auto;
  width:28px;
  height:28px;
  margin-right:8px;
  padding:0;
  border:1px solid rgba(31,27,20,.16);
  border-radius:50%;
  background:rgba(255,253,245,.7);
  font-size:14px;
  line-height:1;
  color:var(--ink-soft);
  cursor:pointer;
}
.diary-refresh:active{ background:rgba(176,57,43,.10); }
.diary-refresh.spin{ animation:diarySpin 1s linear infinite; }
@keyframes diarySpin{ to{ transform:rotate(360deg); } }

/* ============================================================
   ⑤″ 已兑换 · 兑换记录（商城钱包里「已兑 · 记录 ›」那一格）
   每条记录一行；点一行弹兑换二维码，到游客中心让工作人员扫。
   ============================================================ */
.redeem{ z-index:34; }                 /* 商城是 z-index:32，得压在它上面 */

/* 钱包里那一格做成按钮，外观跟旁边的格子一样 */
.wallet-cell-link{
  appearance:none; -webkit-appearance:none;
  border:0;
  border-left:1px solid rgba(31,27,20,.12);
  background:none;
  font:inherit;
  color:inherit;
  cursor:pointer;
}
.wallet-cell-link i{ color:var(--cinnabar); }
.wallet-cell-link:active{ opacity:.6; }

.redeem-scroll{
  flex:1 1 auto;
  min-height:0;
  overflow-y:auto;
  -webkit-overflow-scrolling:touch;
  padding:2px 18px calc(env(safe-area-inset-bottom, 0px) + 24px);
}

.redeem-list{
  display:flex; flex-direction:column; gap:10px;
}

.redeem-row{
  display:flex; align-items:center; gap:12px;
  width:100%;
  padding:11px 13px;
  border:1px solid rgba(31,27,20,.14);
  border-radius:4px;
  background:rgba(255,253,247,.72);
  text-align:left;
  appearance:none; -webkit-appearance:none;
  font:inherit; color:inherit;
  cursor:pointer;
}
.redeem-row:active{ transform:scale(.985); }

.redeem-thumb{
  flex:0 0 auto;
  width:46px; height:46px;
  border-radius:3px;
  overflow:hidden;
  background:rgba(31,27,20,.05);
}
.redeem-thumb img{ display:block; width:100%; height:100%; object-fit:cover; }
.redeem-thumb svg{
  width:100%; height:100%;
  fill:none; stroke:var(--ink-2); stroke-width:1.35;
  stroke-linecap:round; stroke-linejoin:round;
}

.redeem-body{ flex:1 1 auto; min-width:0; }
.redeem-name{
  display:block;
  font-family:var(--font-brush);
  font-weight:400;
  font-size:17px;
  letter-spacing:.08em;
  color:var(--ink);
}
.redeem-meta{
  display:block;
  margin-top:4px;
  font-size:10.5px;
  line-height:1.7;
  letter-spacing:.06em;
  color:var(--ink-soft);
}
.redeem-meta b{ font-weight:400; color:var(--cinnabar); letter-spacing:.1em; }
.redeem-go{
  flex:0 0 auto;
  font-size:10.5px;
  letter-spacing:.14em;
  color:var(--ink-soft);
}

.redeem-empty{
  margin-top:26px;
  font-family:var(--font-song);
  font-size:12.5px;
  letter-spacing:.04em;
  color:var(--ink-soft);
  text-align:center;
}
.redeem-empty.off{ display:none; }

/* ---------- 兑换码 ---------- */
.redeem-mask{
  position:absolute; inset:0; z-index:8;
  display:flex; align-items:center; justify-content:center;
  padding:24px;
  background:rgba(7,9,8,.44);
  opacity:0;
  pointer-events:none;
  transition:opacity .32s ease;
}
.redeem-mask.on{ opacity:1; pointer-events:auto; }

.redeem-card{
  width:100%;
  max-width:302px;
  display:flex; flex-direction:column; align-items:center;
  padding:20px 20px 16px;
  border-radius:4px;
  background:var(--paper);
  box-shadow:0 18px 48px rgba(7,9,8,.3);
  transform:translateY(14px) scale(.97);
  transition:transform .42s var(--ease-out);
}
.redeem-mask.on .redeem-card{ transform:none; }

.redeem-tip{
  font-family:var(--font-song);
  font-size:12.5px;
  letter-spacing:.04em;
  color:var(--ink-2);
  text-align:center;
}

.redeem-qr{
  display:block;
  width:200px; height:200px;
  margin:13px 0 11px;
  padding:8px;
  border:1px solid rgba(31,27,20,.16);
  border-radius:4px;
  background:#fff;
  image-rendering:pixelated;
}

.redeem-code{
  font-family:var(--font-brush);
  font-weight:400;
  font-size:21px;
  letter-spacing:.14em;
  color:var(--cinnabar);
}

.redeem-line{
  margin-top:8px;
  font-family:var(--font-song);
  font-size:12px;
  line-height:1.7;
  letter-spacing:.02em;
  color:var(--ink-2);
  text-align:center;
}

/* ======================================================================
   卡片布局（2026-10-06 用户用 ?cardedit=1 就地编辑器自己摆好、导出后烘进来的）
   ----------------------------------------------------------------------
   ★ 放在样式表【最末尾】是有意的：靠「后来的同特异性赢」压过上面那套老排版，
     不用去动老规则（那些老规则 .map-card.free 那一支还在用）。
   ★ 设计尺寸 300 × 428px，卡片下沿离舞台底 13.5%；
     位置一律写百分比 —— 卡片窄屏时按 aspect-ratio 缩，百分比两头都对得上；
     字号留 px —— 卡片宽 min(300px, calc(100% - 44px))，窄过 344px 的屏才会开始缩。
   ★ 烘进来时纠正了导出值里【他浏览器过期缓存 / 量值产物】的四处，别照抄原导出：
     ① 边框写回 1px solid rgba(31,27,20,.14)：
        导出是 0.666667px（他屏幕 DPR 取整的产物），颜色 #1f1b14 也是量出来的
        （原色的透明度被量丢了），都不是他挑的；
     ② 圆形导航键 border-radius 写 12px：导出器硬编码 50%，
        而 40×32 配 12px 才是他摆出来的那个圆角矩形；
     ③ .map-card-row 写 display:none：导出里 show.row=true 是旧缓存，
        那排现在只剩一颗（建筑卡片上本来就藏着的）「删掉」，放出来就是一条 11px 白带；
     ④ .map-card-media 只在【还没放照片】时留虚线空框（:has 那句），
        照片一摆上就是干净的一条边。
   ====================================================================== */
.map-card.spot{
  width:min(300px, calc(100% - 44px));
  aspect-ratio:300 / 428;
  padding:0;
  bottom:13.5%;
  border-radius:14px;
  background:#f6edd0;                       /* 就是 --paper，量出来写成字面量 */
  border:1px solid rgba(31,27,20,.14);
}
/* 名字和简介要各自定位，得把 .map-card-info 那层盒子拆掉 */
.map-card.spot .map-card-info{ display:contents; }

.map-card.spot .map-card-media,
.map-card.spot .map-card-cat,
.map-card.spot .map-card-name,
.map-card.spot .map-card-desc,
.map-card.spot .map-card-go,
.map-card.spot .map-card-row,
.map-card.spot .map-card-x{ position:absolute; margin:0; right:auto; bottom:auto; }

.map-card.spot .map-card-media{ left:18.19%; top:3.59%; width:63.62%; height:22.03%; border-radius:10px; border:0; overflow:hidden; }
/* 还没放照片：留着虚线空框，中间那个「图片」才有个落脚的地方 */
.map-card.spot .map-card-media:has(.map-card-photo[hidden]){ border:1px dashed rgba(31,27,20,.20); }
.map-card.spot .map-card-media .map-card-photo{ width:100%; height:100%; object-fit:cover; border-radius:9px; }
.map-card.spot .map-card-media .map-card-ph{ border-radius:10px; }

.map-card.spot .map-card-cat{ left:5.13%; top:27.03%; width:90.07%; height:3.28%; font-size:10.5px; text-align:left; color:#b0392b; letter-spacing:2.52px; }
.map-card.spot .map-card-name{ left:5.13%; top:30.31%; width:90.07%; height:5.39%; font-size:19px; text-align:left; color:#1f1b14; font-weight:400; }

/* 简介：框子自己长高长矮（短了贴着字、长了滚），虚线框留着 —— 不然看不出它有没有缩 */
.map-card.spot .map-card-desc{ left:5.13%; top:37.81%; width:90.07%; height:auto; max-height:52.50%; font-size:13px; line-height:1.78; text-align:left; color:#3a3226; overflow:auto; -webkit-overflow-scrolling:touch; }
.map-card.spot .map-card-desc .map-card-scroll{ height:auto; max-height:none; overflow:visible; }
.map-card.spot .map-card-desc .map-card-text{ margin:8px; font-size:13px; line-height:1.78; text-align:left; color:#3a3226; }

.map-card.spot .map-card-go{ left:43.30%; top:90.31%; width:13.39%; height:7.50%; border-radius:12px; display:flex; align-items:center; justify-content:center; font-size:15px; padding:0; border:0; background:#B0392B; color:#FFF8E6; }

/* ---------- 选地图导航（2026-10-06 加）----------
   点卡片上那颗「导航」弹出来的小纸：三家地图挑一家，跳出去就是那一处的搜索页
   （高德那页自带「到这去」，手机上还会问要不要进 App）。 */
.map-nav{
  position:absolute; inset:0; z-index:70;
  display:flex; align-items:center; justify-content:center;
  background:rgba(31,27,20,.34);
  opacity:0; pointer-events:none;
  transition:opacity .18s var(--ease-io);
}
.map-nav.on{ opacity:1; pointer-events:auto; }
.map-nav-box{
  width:min(280px, calc(100% - 44px));
  padding:18px 18px 14px;
  border:1px solid rgba(31,27,20,.14);
  border-radius:16px;
  background:#FFF8E6;
  box-shadow:0 18px 44px rgba(31,27,20,.24);
  text-align:center;
}
.map-nav-t{
  margin:0 0 12px;
  font-family:var(--font-song);
  font-size:14px; letter-spacing:.04em;
  color:var(--ink);
}
.map-nav-btn{
  display:block; width:100%; box-sizing:border-box;
  margin:0 0 8px; padding:11px 0;
  border:1px solid rgba(31,27,20,.14); border-radius:12px;
  background:rgba(255,255,255,.72);
  color:var(--ink);
  font-family:var(--font-song);
  font-size:15px; letter-spacing:.08em;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.map-nav-btn:hover{ border-color:var(--cinnabar); color:var(--cinnabar); }
.map-nav-x{
  display:block; width:100%; box-sizing:border-box;
  margin:4px 0 0; padding:9px 0;
  border:0; border-radius:12px;
  background:transparent;
  color:var(--ink-soft);
  font-family:var(--font-song);
  font-size:13px; letter-spacing:.08em;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}

.map-card.spot .map-card-row{ display:none; }

.map-card.spot .map-card-x{ left:88.50%; top:1.95%; width:8.71%; height:6.09%; border-radius:50px; display:flex; align-items:center; justify-content:center; font-size:17px; padding:0; border:0; background:transparent; color:#1f1b14; z-index:5; }

/* ============================================================
   实拍图：一处景点能摆好几张，横着翻页（2026-10-06）
   —— 大金山漂流给了三张，用户要「三张一起放进去，可翻页」。
   那一块还是用户摆好的 190×94（2:1），照片按 object-fit:cover 裁进去；
   手机用手指划、桌子上点下面那几个圆点，滚到哪一格圆点跟着亮。
   ============================================================ */
.map-card.spot .map-card-media .map-card-shots{
  display:flex;
  height:100%;
  overflow-x:auto;
  overflow-y:hidden;
  scroll-snap-type:x mandatory;
  -webkit-overflow-scrolling:touch;
  scrollbar-width:none;               /* 那么小一块，还拖一条滚动条出来太丑 */
  -ms-overflow-style:none;
  touch-action:pan-x;                 /* 手机上横划是翻页，别把整张卡片/地图拖着走 */
}
.map-card.spot .map-card-media .map-card-shots::-webkit-scrollbar{ display:none; }

/* 一张占满一格，手指停下来正好对齐一格 */
.map-card.spot .map-card-media .map-card-photo{
  flex:0 0 100%;
  width:100%; height:100%;
  object-fit:cover;
  border-radius:0;                    /* 圆角交给外面那个框裁，几张之间才不留缝 */
  scroll-snap-align:center;
  scroll-snap-stop:always;
}
/* 只有一张：别让它变成一块能拖的 */
.map-card.spot .map-card-media .map-card-shots:not(:has(.map-card-photo ~ .map-card-photo)){
  overflow-x:hidden;
  scroll-snap-type:none;
}

/* 底下那几个小圆点 */
.map-card.spot .map-card-media .map-card-dots{
  position:absolute;
  left:50%; bottom:5px;
  transform:translateX(-50%);
  z-index:3;
  display:flex; gap:5px;
  padding:3px 7px;
  border-radius:999px;
  background:rgba(31,27,20,.34);
}
.map-card.spot .map-card-media .map-card-dots[hidden]{ display:none; }
.map-card.spot .map-card-media .map-card-dots i{
  width:5px; height:5px;
  border-radius:50%;
  background:rgba(255,248,230,.50);
  transition:background .18s var(--ease-io), transform .18s var(--ease-io);
  cursor:pointer;
}
.map-card.spot .map-card-media .map-card-dots i.on{
  background:#FFF8E6;
  transform:scale(1.3);
}

/* ============================================================
   应用内导航：已拆（2026-10-08 删）
   —— 这里原来是一整段「在这一页里带我过去」的样式：顶上的 HUD、
      「你在这儿」蓝点、信标朝向三角、顺着街道走的虚线、亮起来的街、
      箭头、还有提醒那一行。用户拍板：
      「算了吧软件内部的导航系统删了吧，跳转高德就行了」。
      连 js/nav.js、js/map-roads.js 一起删了；卡片上那颗「导航」
      现在只弹下面那张小纸，跳高德/百度/腾讯。
      上面那段 .map-nav / .map-nav-btn / .map-nav-x 是那张小纸的，留着。
   ============================================================ */
