:root{
  --vh: 100dvh;              /* 由 JS 校正為實際可見高 */
  --scene-w: 432px;          /* 素材寬度 = 144 art px x 3 */
  --safe-w: 320px;           /* 中央安全區 */
  --ink: #2B2B3D;
  --cream: #F7E3C0;
  --sand: #F2DCA9;
  --orange: #E87A3A;

  /* 以下三個是從素材裡量出來的，不是憑印象調的 ——
     改配色時請以素材為準。
       --sky-deep  bg.png 最上緣的藍（純色 100%）
       --line      所有素材描邊用的近黑（建築、雲、吉祥物都是這個值）
       --paper     bg.png / new_home.png 的奶油白 */
  --sky-deep: #295DAA;
  --line: #0A0907;
  --paper: #F3EAD1;

  /* 氣泡的邊框厚度。**四個角的缺口就是這個值** ——
     四條 box-shadow 只覆蓋各自那一邊，角落自然空出 edge x edge 的方塊。
     想讓缺角更明顯就調大它；內距會自動補回來，氣泡的外觀尺寸不會變。 */
  --bubble-edge: 6px;

  /* 抵達氣泡：黑底 + 奶油字。
     黑用素材原本的 --line（#0A0907），不用純黑 —— 畫面上每一條描邊都是這個值，
     跟著用才是同一套美術。
     描邊色目前跟底色相同，所以看不到描邊，結構只剩四個缺角；
     想把描邊看出來就把 --bubble-line 換成 var(--paper)（奶油白鑲邊）。 */
  --bubble-bg: var(--line);
  --bubble-line: var(--line);
  --bubble-text: var(--paper);

  /* 桌機上場景左右兩側（超出素材 480 寬的部分）。
     之後要換成圖或漸層的話改這裡，或直接給 .scene-clip 一個 background-image。 */
  --letterbox: #000;


  /* 雲朵左右飄移的幅度（單程）。這是唯一的旋鈕 ——
     js/scene.js 的 orientDrift 也會讀它來決定每朵雲該往哪邊飄。 */
  --drift-amp: 28px;
}

/* 探頭吉祥物的裁切往外放的量（負值）。--pop 由 js/scene.js 逐隻算，
   這裡只負責取負號，免得每個 inset() 都寫一次 calc。 */
.peeker{
  --pop: 24px;
  --pop-neg: calc(var(--pop) * -1);
}

/* Silkscreen（Google Fonts，SIL OFL 1.1，授權全文見 assets/fonts/OFL.txt）。
   自己放檔案不用 Google 的 CDN：少一次 DNS + TLS、沒有外部相依、
   file:// 直接開也有字。只抓 latin + 700 一個檔，3.2KB。 */
@font-face{
  font-family:"Silkscreen";
  font-style:normal;
  font-weight:700;
  font-display:block;      /* 只用在 Zzz，寧可晚一點出現也不要先閃一次系統字 */
  src:url("../assets/fonts/silkscreen-700.woff2") format("woff2");
  unicode-range:U+0000-00FF;
}

*{ box-sizing:border-box; }
html{
  scroll-snap-type: y mandatory;   /* 一次滑動 = 一屏，這是「三次滑動」的關鍵 */
  background:var(--letterbox);     /* 回彈時露出的顏色，跟兩側留黑一致 */
}
body{
  margin:0;
  font-family:"Noto Sans TC","PingFang TC",system-ui,sans-serif;
  color:var(--ink);
  -webkit-text-size-adjust:100%;
}
img{ display:block; max-width:100%; }

/* ---------- 舞台與圖層 ---------- */
/* sticky 舞台：釘在畫面上不動，捲動範圍由 .viewport 的高度決定。
   不用 position:fixed — 在 scroll-snap 容器裡會有合成層繪製問題（iOS Safari 尤其明顯）。 */
.viewport{ position:relative; }
.stage{
  position:sticky; top:0;
  height:var(--vh);
  overflow:hidden;
  z-index:0;
  pointer-events:none;
  background:var(--letterbox);     /* 桌機上場景兩側的顏色 */
}
/* 場景欄位：素材只有 480 寬，所以畫面再寬也只給 480，置中並裁掉溢出的部分。
   靠邊的雲本來就設計成要溢出畫面，不裁的話在桌機上會變成懸在黑底上的半朵雲。
   絕對定位 + left/right 0 + margin auto = 置中（不用 transform，
   免得替子層建立新的 containing block）。
   窄機上 width 就是 100%，等於沒作用。 */
.scene-clip{
  position:absolute; top:0; bottom:0; left:0; right:0;
  width:min(100%, 480px);
  margin:0 auto;
  overflow:hidden;
}
/* 縮放層：場景座標系固定 480 寬，整個等比縮放去貼合視窗寬度。
   scale = min(1, 視窗寬 / 480)，只縮不放 —— 桌機維持 1:1 並留黑邊。
   transform-origin 用 top center：上緣貼齊畫面頂端（相機模型的原點），
   水平則以中線為軸，縮放後仍然置中。 */
.scene-scale{
  position:absolute; top:0;
  left:50%; width:480px; margin-left:-240px;
  height:100%;
  transform:scale(var(--scale, 1));
  transform-origin:top center;
}
.layer{
  position:absolute;
  left:0; width:100%;
}
/* bg.png 的座標系：所有圖層的 top 都是這張 2100px 高的圖裡的 y 座標。
   地平線（山腳）在 y=1825，距圖片下緣 275px。 */
.layer[data-layer="bg"]{
  top:0; height:2100px;
  left:50%; width:480px; margin-left:-240px;   /* 480 蓋得過 iPhone 16 Pro Max 的 440 */
  background-image:url("../assets/bg.png");
  background-size:480px 2100px;
  background-repeat:no-repeat;
  image-rendering:pixelated;                   /* 點陣圖用最近鄰，邊緣才不會糊 */
}

/* 桌機兩側的天空（見 index.html 的 bg-side）。.layer 本身只當捲動用的容器，
   左右兩塊各佔 480 欄位以外的一半；寬度 = (視窗寬 - 480) / 2，窄機上是 0。
   圖是 assets/sky-side.png（960 x 2100，格子是中間欄位的一半大，顏色的 y 位置跟 bg.png 對齊，由 tools/make-sky-side.py 產生），
   橫向重複；左側靠右對齊（貼著欄位邊緣）、右側從左開始。格子比中間小，兩側才不會搶主視覺。 */
.layer[data-layer="bg-side"]{ top:0; height:2100px; }
.sky-side{
  position:absolute; top:0; height:2100px;
  width:max(0px, calc((100% - 480px) / 2 + 1px));   /* +1px 蓋住接縫 */
  background:url("../assets/sky-side.png") repeat-x;
  background-size:960px 2100px;
  image-rendering:pixelated;
}
.sky-side-l{ left:0;  background-position:right top; }
.sky-side-r{ right:0; background-position:left top; }

/* 雲層：高度由 js/clouds.js 的 height 決定（JS 寫入），
   座標系寬度維持 432（--scene-w），這樣 clouds.js 裡的 x 值不用重算 */
.layer[data-layer^="clouds-"]{ top:0; }

/* 吉祥物的兩個圖層：跟 bg 同座標系（480 寬置中、2100 高），
   夾住 new_home 用來做遮擋。 */
.layer[data-layer^="mascot-"]{ top:0; height:2100px; }
.mascot-box{
  position:absolute; top:0; left:50%;
  width:480px; height:100%; margin-left:-240px;
}
.mascot{
  position:absolute;
  background-repeat:no-repeat;
  /* background-size / background-position 由 js/mascot-run.js 依尺寸算 */
}

/* 三樓的燈。跟前景同一個畫布，所以位置寫法完全一樣，不用對位。
   素材是 481px 寬（右邊多一欄是空的），background-size 就給原生 481 ——
   給 480 的話等於把圖橫向壓縮 0.2%，窗格會對不準。多出來的空白欄會被圖層切掉。 */
.layer[data-layer="lights"]{
  top:1647px; height:453px;
  left:50%; width:480px; margin-left:-240px;
  background-image:url("../assets/windows_light.png");
  background-size:481px 453px;
  background-repeat:no-repeat;
  image-rendering:pixelated;
  opacity:0;
}
/* 日光燈啟動：閃兩下才穩定。step-end 所以是直接亮／直接暗，沒有淡入。
   0.55 秒跑完 —— 燈要先亮定，氣泡才接著出現，所以這段不能拖。 */
.layer[data-layer="lights"].is-on{
  animation:win-light-on .55s step-end forwards;
}
@keyframes win-light-on{
  0%   { opacity:0; }
  7%   { opacity:1; }   /* 閃一下 */
  14%  { opacity:0; }
  30%  { opacity:1; }   /* 再閃一下 */
  37%  { opacity:0; }
  55%, 100% { opacity:1; }   /* 穩定亮著 */
}

/* 乳牛：跟 bg 同一個座標系（480 寬置中、2100 高），所以 js/cows.js 的 x, y
   就是 bg.png 上的座標，跟吉祥物路徑同一套。 */
.layer[data-layer="cows"]{ top:0; height:2100px; }
.cow-box{
  position:absolute; top:0; left:50%;
  width:480px; height:100%; margin-left:-240px;
}
.cow{ position:absolute; line-height:0; }
.cow-flip{ transform:scaleX(-1); transform-origin:50% 100%; }
.cow img{
  display:block;
  image-rendering:pixelated;
  transform-origin:50% 100%;     /* 支點在腳底：壓扁拉長時腳不會離地 */
  animation:cow-hop var(--hop-dur, 5s) linear var(--hop-delay, 0s) infinite;
}

/* 原地輕跳 + 壓扁拉長（squash & stretch）。
   動作集中在整個週期的前 15%，其餘時間站著不動 ——
   5 秒的週期就是「跳 0.75 秒、停 4.25 秒」。

   時間曲線用 linear，弧線靠 y 值自己畫：滯空那段的 y 是拋物線
   （0 → -8.25 → -11 → -8.25 → 0），上升會自然減速、下降會加速。
   用 ease-out 的話每一段都是「先快後慢」，下落看起來會像被拉住。

   x 和 y 的縮放互為倒數（1 / 0.86 ≈ 1.16），看起來才像有體積被擠壓，
   而不是單純被拉變形。 */
@keyframes cow-hop{
  0%,  1%    { transform:translateY(0)       scale(1,    1);    }  /* 站著 */
  2.5%       { transform:translateY(0)       scale(1.14, 0.86); }  /* 預備：蹲低壓扁 */
  4%         { transform:translateY(0)       scale(0.90, 1.14); }  /* 起跳：拉長、剛離地 */
  5.75%      { transform:translateY(-8.25px) scale(0.93, 1.10); }
  7.5%       { transform:translateY(-11px)   scale(0.97, 1.05); }  /* 最高點 */
  9.25%      { transform:translateY(-8.25px) scale(1,    1);    }
  11%        { transform:translateY(0)       scale(1.16, 0.84); }  /* 落地：壓扁 */
  12.5%      { transform:translateY(-2px)    scale(0.97, 1.05); }  /* 回彈 */
  15%, 100%  { transform:translateY(0)       scale(1,    1);    }  /* 回到站著 */
}

/* 抵達互動層：閃亮、氣泡、熱區。座標系跟 bg 一致（480 寬置中、2100 高） */
.layer[data-layer="arrival"]{ top:0; height:2100px; }
.arrival-box{
  position:absolute; top:0; left:50%;
  width:480px; height:100%; margin-left:-240px;
}

/* 閃亮：三張 28x28 的影格輪播。用切換 background-image 而不是縮放，
   點陣圖才不會因為非整數倍縮放糊掉。 */
.sparkle{
  position:absolute;
  width:28px; height:28px;
  background-image:url("../assets/sparkle-sheet.png");
  background-repeat:no-repeat;
  background-size:84px 28px;     /* 3 格橫排 */
  background-position:0 0;
  opacity:0;
}
.arrival-box.is-on .sparkle{
  animation:sparkle-pop 1.6s steps(1, end) infinite;
}
/* 只移動 background-position，不換圖檔網址 —— 換網址會閃 */
@keyframes sparkle-pop{
  0%,   11% { opacity:0; background-position:0 0; }
  12%,  23% { opacity:1; background-position:0 0; }
  24%,  35% { opacity:1; background-position:-28px 0; }
  36%,  53% { opacity:1; background-position:-56px 0; }
  54%,  65% { opacity:1; background-position:-28px 0; }
  66%,  77% { opacity:1; background-position:0 0; }
  78%, 100% { opacity:0; background-position:0 0; }
}

/* 氣泡指引：錨點是尖角的位置，框往上長。
   點陣風的三個要點，所有尺寸都是 3px（= 1 art px）的整數倍：
     1. 缺角 —— 邊框不用 border，改用四條 box-shadow 拼，四個角自然少一格
     2. 階梯尾巴 —— 點陣稿沒有斜線，所以尾巴是一階一階縮的（見 ::before/::after）
   左右置中不用 translate(-50%)：寬度是奇數時會落在半個像素上，
   改由 js/mascot-run.js 量完寬度後寫入整數 left（同時夾在畫面內）。 */
.bubble{
  position:absolute;
  transform:translateY(-100%);
  /* 尖角落在錨點上：尾巴 12px + 3px 間隙 + 下邊框的厚度 */
  margin-top:calc(-12px - var(--bubble-edge));
  /* 內距 + 邊框 = 固定值，所以調邊框厚度不會改變氣泡的外觀尺寸 */
  padding:calc(15px - var(--bubble-edge)) calc(18px - var(--bubble-edge));
  background:var(--bubble-bg);
  color:var(--bubble-text);
  font-size:18px; line-height:1; font-weight:700;
  letter-spacing:1px;
  white-space:nowrap;
  /* 這四條就是邊框，四個角會自然留下 edge x edge 的缺口。
     不要在這裡加投影 —— 會跟邊框搶戲。 */
  box-shadow:
      0 calc(var(--bubble-edge) * -1) 0 var(--bubble-line),   /* 上 */
      0 var(--bubble-edge)            0 var(--bubble-line),   /* 下 */
      calc(var(--bubble-edge) * -1) 0 0 var(--bubble-line),   /* 左 */
      var(--bubble-edge)            0 0 var(--bubble-line);   /* 右 */
  opacity:0; pointer-events:none;
}
/* 階梯尾巴。外層（描邊色）24x12 四階、內層（底色）18x9 三階，
   差一格就是描邊；寬度都用偶數，margin 才不會出現半個像素。 */
/* 尾巴接在下邊框的外緣，第一階跟邊框重疊 3px（邊框變厚也不會脫節） */
.bubble::before,
.bubble::after{
  content:""; position:absolute; left:50%;
  top:calc(100% + var(--bubble-edge) - 3px);
}
.bubble::before{
  width:24px; height:12px; margin-left:-12px;
  background:var(--bubble-line);
  clip-path:polygon(0 0, 24px 0, 24px 3px, 21px 3px, 21px 6px, 18px 6px,
                    18px 9px, 15px 9px, 15px 12px, 9px 12px, 9px 9px,
                    6px 9px, 6px 6px, 3px 6px, 3px 3px, 0 3px);
}
.bubble::after{
  width:18px; height:9px; margin-left:-9px;
  background:var(--bubble-bg);
  clip-path:polygon(0 0, 18px 0, 18px 3px, 15px 3px, 15px 6px, 12px 6px,
                    12px 9px, 6px 9px, 6px 6px, 3px 6px, 3px 3px, 0 3px);
}
.arrival-box.is-on .bubble{
  opacity:1;
  animation:bubble-bob 1.4s ease-in-out infinite;
}
/* 上下浮動也走整數格 */
@keyframes bubble-bob{
  0%,100%{ transform:translateY(-100%); }
  50%    { transform:translateY(calc(-100% - 6px)); }
}

/* 建築熱區：抵達前不可點 */
.hotspot{
  position:absolute;
  display:block;
  pointer-events:none;
  -webkit-tap-highlight-color:transparent;
}
.arrival-box.is-on .hotspot{ pointer-events:auto; cursor:pointer; }

@media (prefers-reduced-motion:reduce){
  .arrival-box.is-on .sparkle{ animation:none; opacity:1; background-position:-28px 0; }
  .arrival-box.is-on .bubble{ animation:none; }
}

/* 前景屋子：480 × 453，下緣對齊 bg 下緣（2100 - 453 = 1647）。
   跟 bg 同寬同速，所以永遠貼在地面上不會錯位。 */
.layer[data-layer="home"]{
  top:1647px; height:453px;
  left:50%; width:480px; margin-left:-240px;
  background-image:url("../assets/new_home.png");
  background-size:480px 453px;
  background-repeat:no-repeat;
  image-rendering:pixelated;
}

/* 場景框：把座標原點固定在 432px 寬的畫面中央，雲的 x/y 才對得上 art px */
.scene-box{
  position:absolute; top:0; left:50%;
  width:var(--scene-w); height:100%;
  margin-left:calc(var(--scene-w) / -2);
}
/* 探頭吉祥物：DOM 排在雲之前，所以會被雲蓋住，只露出上半身。
   外層 div 負責定位與平移，內層 img 負責翻轉與旋轉 ——
   兩者都用 transform，混在同一個元素上會互相覆蓋。 */
.peeker{
  position:absolute;
  user-select:none;
  /* 只裁下緣。上／左／右反而要往外放 --pop ——
     .peeker 沒設寬高，盒子剛好等於圖的排版尺寸，而 img 的 rotate 和
     彈跳衝過頭都會跑到盒子外面，裁在 0 的話邊緣就會被削掉。
     --pop 由 js/scene.js 依旋轉角度與下沉距離算出來。
     藏起來的那段要連左右一起裁，見 .has-peek 與 peek-clip。 */
  clip-path:inset(var(--pop-neg) var(--pop-neg) var(--clip-b, 0px) var(--pop-neg));
}
/* 躺在雲上面的：DOM 排在雲之後，整隻都看得見，不裁切 */
.peeker-front{ clip-path:none; }

/* 睡覺示意：三個字母從吉祥物右上角依序往右上飄走再散掉。
   錨點掛在 .peeker 底下，所以會跟著雲一起平移，不用另外同步。
   顏色直接用素材的描邊色 —— 底下是橘色天空，對比夠，不需要再加外框。 */
.zzz{
  position:absolute;
  /* 錨點 = 吉祥物的右上角，再用 margin 微調。
     往右是加 margin-left、往下是加 margin-top（都以 3px 為一格）。 */
  left:100%; top:0;
  margin-left:-5px;
  margin-top:9px;
  width:0; height:0;      /* 純粹當定位錨點 */
  pointer-events:none;
  user-select:none;
}
.zzz i{
  position:absolute; left:0; bottom:0;
  /* Silkscreen 是點陣字，字級一定要落在 8 的倍數上才不會有灰邊 ——
     它的字身就是 8px 一格（實測 8/16/24/32/40 之外都會糊）。
     不要加 italic：沒有真正的斜體，瀏覽器會用傾斜模擬，像素會被拉歪。 */
  font:700 8px/1 "Silkscreen", monospace;
  color:var(--line);
  white-space:pre;
  opacity:0;
  /* step-end：每一格的值撐到下一格才換 —— 字母是「一格一格跳上去」的，
     不是平滑飄移。點陣風的動態本來就不該補間，透明度也是直接出現／直接消失。 */
  animation:zzz-float var(--zzz-dur, 4.5s) step-end infinite;
}

/* 六個離散位置：一邊往上跳、一邊變大。
   放大是換 font-size（8 → 24）而不是 transform: scale() ——
   點陣字縮放會糊，換字級才是每一級都在自己的原生尺寸上重新排版。

   **六格要平均分佈（每格 1/6）**，字級每兩格跳一級。
   三個字母的相位差是 1/3 = 剛好兩格，所以畫面上永遠是
   「8 / 16 / 24」這個組合，間距也固定。格子若不平均（例如 8% / 22% / 36%…），
   相位差就對不上整數格，三個字的大小組合會一直變（實測會在
   8/16/24、8/16/32、8/24/32 之間跳來跳去，看起來很不穩）。

   沒有淡出：飄到頂的那個直接消失、同時最下面冒出新的一個 ——
   點陣風本來就不淡入淡出，這個「換手」剛好就是 Zzz 該有的節奏。

   位移的數字比看到的間距小，因為字級變大時本身就會往上長（錨點在 bottom）。 */
@keyframes zzz-float{
  0%       { opacity:1; font-size:8px;  transform:translate(0px, 0px);   }
  16.667%  { opacity:1; font-size:8px;  transform:translate(3px, -5px);  }
  33.333%  { opacity:1; font-size:16px; transform:translate(0px, -10px); }
  50%      { opacity:1; font-size:16px; transform:translate(6px, -16px); }
  66.667%  { opacity:1; font-size:24px; transform:translate(3px, -23px); }
  83.333%  { opacity:1; font-size:24px; transform:translate(9px, -31px); }
}
.peeker img{ display:block; width:auto; }

/* 探頭。預設沉在 --duck（完全藏進雲裡），捲到它才彈出來，之後就留在外面不縮回去。
   .peeker 的 clip-path 會把超出雲下緣的部分裁掉，所以藏著的時候腳不會露出來。 */
.peeker.has-peek .peek-move{
  transform:translate(var(--duck-x, 0px), var(--duck, 0px));   /* 還沒捲到：藏著 */
  /* 捲回頭重新上膛時，animation 被移除、transform 掉回上面這個值，
     由這條 transition 接住 —— 看起來是慢慢沉回雲裡，不是瞬間跳回去。
     彈出來的時候 animation 優先於 transition，所以不影響彈跳。 */
  transition:transform .45s ease-in;
}
.peeker.has-peek{
  transition:clip-path .45s ease-in;
}
/* 只有捲到它的時候才彈 —— 看不到的時候動也是白費，而且進場才播比較有戲。
   iteration 1 + forwards：彈出來就停在原位，不再循環。 */
.peeker.has-peek.in-view .peek-move{
  animation:peek-bob var(--peek-dur, .9s) ease-out 1 forwards;
}
/* 斜向探出：藏著的時候偏在斜下方，探出來時回到原位，
   所以路徑是「左下→右上」或「右下→左上」，方向由 --duck-x 的正負決定。
   衝過頭再回彈兩次，幅度逐次減半 —— 像有彈性地跳出來。
   overshoot 是往「更外面」跑（duck 的反向），那邊是空的天空，不會被裁到。 */
@keyframes peek-bob{
  0%   { transform:translate(var(--duck-x, 0px), var(--duck, 0px)); }   /* 藏著 */
  52%  { transform:translate(calc(var(--duck-x, 0px) * -0.18),
                             calc(var(--duck, 0px)   * -0.14)); }       /* 衝過頭 */
  70%  { transform:translate(calc(var(--duck-x, 0px) *  0.09),
                             calc(var(--duck, 0px)   *  0.07)); }       /* 回彈 */
  84%  { transform:translate(calc(var(--duck-x, 0px) * -0.045),
                             calc(var(--duck, 0px)   * -0.035)); }
  94%  { transform:translate(calc(var(--duck-x, 0px) *  0.018),
                             calc(var(--duck, 0px)   *  0.014)); }
  100% { transform:translate(0, 0); }                                   /* 停在探出來的位置 */
}

/* 雲與躲在雲後面的吉祥物共用同一組平移 —— 這樣它們會一起飄，不會脫節 */
.cloud,
.peeker{
  animation:cloud-drift var(--drift-dur, 18s) ease-in-out var(--drift-delay, 0s) infinite;
}

/* 有探頭動畫的：藏著時連左右也裁到雲的邊界（不然會從雲側邊探出來一角），
   彈出來的過程中把左右的裁切放掉，之後就只裁下緣。 */
.peeker.has-peek{
  clip-path:inset(var(--pop-neg) var(--clip-r, 0px) var(--clip-b, 0px) var(--clip-l, 0px));
}
.peeker.has-peek.in-view{
  animation:cloud-drift var(--drift-dur, 18s) ease-in-out var(--drift-delay, 0s) infinite,
            peek-clip   var(--peek-dur, .9s)  ease-out 1 forwards;
}
@keyframes peek-clip{
  0%   { clip-path:inset(var(--pop-neg) var(--clip-r, 0px) var(--clip-b, 0px) var(--clip-l, 0px)); }
  45%  { clip-path:inset(var(--pop-neg) var(--pop-neg) var(--clip-b, 0px) var(--pop-neg)); }
  100% { clip-path:inset(var(--pop-neg) var(--pop-neg) var(--clip-b, 0px) var(--pop-neg)); }
}

.cloud{
  position:absolute;
  user-select:none;
}

/* 平滑補間。原本做成 3px 跳階是怕點陣圖在非整數位置被重新取樣而變糊，
   但手機幾乎都是 2x 螢幕 —— 0.5 CSS px 就剛好是一個裝置像素，
   實測平滑移動不會有可見的糊化，而跳階反而看起來卡頓。
   幅度看 --drift-amp，週期看 js/scene.js 的 DRIFT_DUR。 */
@keyframes cloud-drift{
  0%, 100% { transform:translateX(0); }
  50%      { transform:translateX(calc(var(--drift-dir, 1) * var(--drift-amp, 28px))); }
}

/* ---------- Logo ---------- */
/* 寬度由 js/scene.js 的 LOGO_SIZE × LOGO_SCALE 算出來後寫入，
   高度自動推算。left / top 也由 JS 算成整數 px（見 placeLogo）——
   Logo 會隨捲動往上移到畫面上方 1/4，但不淡出。 */
.logo{
  position:absolute;
  /* 上限 = 場景寬的 88%。Logo 住在 .scene-scale 裡，座標系永遠是 480 寬
     （窄機是整個等比縮小，不是變窄），所以這條要用 px 不能用 vw ——
     用 vw 會在縮放之外再乘一次，窄機上 logo 會比預期小一截。 */
  max-width:422px;
  height:auto;
  z-index:3;
  pointer-events:none;
}
.logo img{ display:block; width:100%; height:auto; }

/* Aotter 品牌標，固定在右上角不隨捲動 */
.brand{
  position:fixed;
  top:max(10px, env(safe-area-inset-top));
  /* 桌機上場景只有中間 480 寬，品牌標要貼著那一欄的右上角，不是視窗的右上角 */
  right:calc(max(0px, (100vw - 480px) / 2) + max(10px, env(safe-area-inset-right)));
  z-index:6;
  width:28px;
  pointer-events:none;
}
.brand img{ display:block; width:100%; height:auto; }

/* ---------- 內容區 ---------- */
/* 內容往上疊在 sticky 舞台上；負 margin 讓總高度剛好等於 4 屏。
   pointer-events:none 讓點擊穿透到舞台裡的建築熱區（這幾屏本來就沒有可點的東西）。 */
main{ position:relative; z-index:2; margin-top:calc(var(--vh) * -1); pointer-events:none; }
.panel{
  position:relative;
  height:var(--vh);
  scroll-snap-align:start;
  scroll-snap-stop:always;      /* 禁止一次慣性滑過兩屏 */
  display:flex;
  flex-direction:column;
  justify-content:center;
  align-items:center;
  padding:0 24px;
}

.sr-only{
  position:absolute; width:1px; height:1px; margin:-1px;
  padding:0; border:0; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap;
}
.hint{
  position:absolute; bottom:calc(64px + env(safe-area-inset-bottom));
  display:flex; flex-direction:column; align-items:center; gap:10px;
  animation:bob 1.8s ease-in-out infinite;
  transition:opacity .25s ease;
}
body.is-scrolled .hint{ opacity:0; }
/* 「向下滑動」（2026-09-30 重新設計）：原本是 12px 的小字，在天空背景上太小。
   改成跟網站其他 UI 同一種語彙：奶油底的小牌子 + 近黑的粗描邊（四條 box-shadow，四個角自然留缺口）
   + 硬陰影，字級 15px 粗體；下面接一個點陣的向下箭頭。 */
.hint-label{
  padding:8px 18px 7px;
  background:var(--line); color:var(--cream);      /* 黑底（素材的近黑 --line）＋淺色字（2026-09-30 換色） */
  font-size:15px; font-weight:700; letter-spacing:.14em; line-height:1.2;
  text-indent:.14em;                    /* letter-spacing 會讓最後一字後面多一段空，用縮排補回來才會視覺置中 */
  box-shadow:
    0 -3px 0 var(--line), 0 3px 0 var(--line),
    -3px 0 0 var(--line), 3px 0 0 var(--line);
  filter:drop-shadow(3px 3px 0 rgba(10,9,7,.35));
}
/* 實心倒三角形，深色（同一個近黑）。2026-09-30 由「奶油色的 V 形箭頭」改成這個。 */
.hint-arrow{
  width:33px; height:18px; fill:var(--line);
}
@keyframes bob{ 0%,100%{ transform:translateY(0) } 50%{ transform:translateY(6px) } }
@media (prefers-reduced-motion:reduce){
  .hint{ animation:none; }
  .cloud, .peeker, .peek-move{ animation:none; }
  /* 乳牛的原地輕跳**照樣跳**（2026-09-30）：手機開了「減少動態效果」的人原本看不到牛在動。
     這只是原地 11px 的小跳，沒有橫向位移，也不是大面積閃爍。 */
  /* 不閃，直接亮著 */
  .layer[data-layer="lights"].is-on{ animation:none; opacity:1; }
  /* zzz 睡覺動態**照樣播放**（2026-09-30）：原本這個設定下停成一組靜態字母，開了「減少動態效果」的人看不到它在飄。
     它是原地小字級的跳格變化，沒有大範圍位移。 */
  /* 關掉動畫的話就直接停在「已經探出來」的樣子 ——
     不然預設的藏姿會讓它永遠留在雲裡看不到。 */
  .peeker.has-peek .peek-move{ transform:translate(0, 0); }
  .peeker.has-peek{
    clip-path:inset(var(--pop-neg) var(--pop-neg) var(--clip-b, 0px) var(--pop-neg));
  }
}

/* ---------- 版權宣告（2026-09-30）----------
   **固定在網站最底部、常駐**（2026-09-30 再改：原本捲到底才浮出，而且離底邊還有一段距離，會壓在乳牛上）。
   限制在中間 480px 那一欄（跟場景同寬）。沒有底色，深棕小字 + 奶油色柔光暈，深淺背景都讀得出來。
   貼齊視窗最底邊（不再加 safe-area 的墊高：瀏覽器工具列已經把可見區域切在它上面，再墊會讓字浮在半空）。
   不接收指標，不擋任何點擊。地圖頁（../數位地圖網頁）用同一組樣式。 */
.copyright{
  position:fixed; z-index:8;
  left:50%; bottom:0;
  width:min(100vw, 480px); transform:translateX(-50%);
  padding:2px 8px 3px;
  text-align:center;
  font:400 10px/1.4 "Noto Sans TC","PingFang TC",system-ui,sans-serif;
  letter-spacing:.2px;
  color:#574242;
  text-shadow:0 0 3px rgba(246,233,219,.95), 0 0 6px rgba(246,233,219,.8), 0 0 10px rgba(246,233,219,.6);
  background:none;
  pointer-events:none;
}


/* ---------- 桌機左側：用手機開啟的引導（QR code） ----------
   只在左側空白夠寬時出現（舞台 480 + 兩側各 ≥ 240 = 960px），手機與窄視窗完全不顯示。
   外形參考底部的縮放按鈕與「探索進度」計數器的階梯缺角（clip-path），但底色是地圖的描邊棕 #574242，
   **沒有框線、沒有陰影**。⚠️ 缺角多邊形又多出現一次，要改外形連這裡一起改。
   QR 位置目前是假的方框；正式網域確定後換成真的 QR 圖（見 docs/map-HANDOFF.md）。 */
.qr-side{ display:none; }
@media (min-width:960px){
  .qr-side{
    display:block; position:fixed; z-index:5;
    left:calc((100vw - 480px) / 2 - 10px - var(--qr-w));   /* 距離中間欄位 10px */
    top:max(10px, env(safe-area-inset-top));   /* 位置跟地圖頁一樣 */
    --qr-w:min(calc((100vw - 480px) / 2 - 48px), 200px);
    width:var(--qr-w);
    --nc:12px; --ns:calc(var(--nc) / 3);
    color:#F6E9DB;
    text-align:center;
    pointer-events:none;
    /* 2026-10-15 改版：棕底與階梯缺角移到 ::before，讓牛仔小獺能站出卡片外；
       硬陰影跟「探索進度」計數器一樣（右下 4px 4px）。 */
    filter:drop-shadow(4px 4px 0 rgba(87,66,66,.45));
  }
  .qr-side::before{
    content:''; position:absolute; inset:0; z-index:-1;
    background:#574242;
    box-shadow:inset 0 2px 0 #7A5D59, inset 0 -3px 0 #3F2E2C;   /* 點陣招牌的上亮下暗 */
    clip-path:polygon(
    0 var(--nc),
    var(--ns) var(--nc),
    var(--ns) calc(var(--nc) - var(--ns)),
    calc(var(--ns) * 2) calc(var(--nc) - var(--ns)),
    calc(var(--ns) * 2) calc(var(--nc) - var(--ns) * 2),
    var(--nc) calc(var(--nc) - var(--ns) * 2),
    var(--nc) 0,
    calc(100% - var(--nc)) 0,
    calc(100% - var(--nc)) var(--ns),
    calc(100% - var(--ns) * 2) var(--ns),
    calc(100% - var(--ns) * 2) calc(var(--ns) * 2),
    calc(100% - var(--ns)) calc(var(--ns) * 2),
    calc(100% - var(--ns)) var(--nc),
    100% var(--nc),
    100% calc(100% - var(--nc)),
    calc(100% - var(--ns)) calc(100% - var(--nc)),
    calc(100% - var(--ns)) calc(100% - var(--ns) * 2),
    calc(100% - var(--ns) * 2) calc(100% - var(--ns) * 2),
    calc(100% - var(--ns) * 2) calc(100% - var(--ns)),
    calc(100% - var(--nc)) calc(100% - var(--ns)),
    calc(100% - var(--nc)) 100%,
    var(--nc) 100%,
    var(--nc) calc(100% - var(--ns)),
    calc(var(--ns) * 2) calc(100% - var(--ns)),
    calc(var(--ns) * 2) calc(100% - var(--ns) * 2),
    var(--ns) calc(100% - var(--ns) * 2),
    var(--ns) calc(100% - var(--nc)),
    0 calc(100% - var(--nc))
  );
  }
}
.qr-side-in{ padding:10px 10px 6px; }          /* 標題的行框下方多半行留白，所以下方少 4px，字面到邊框才跟左右、上方一樣 */
.qr-side-title{ margin:10px 0 0; font-size:17px; font-weight:500; line-height:1.4; white-space:nowrap; }
.qr-side-code{
  /* 2026-10-15：改成「奶油白留白底 + 深棕 QR」。原本整格是反白 QR，現在這一格變成留白區，
     QR（assets/qr.svg，點的顏色 = 卡片底色 #574242）放在中間，四周留 4% 的白邊
     （2026-10-15 改版：QR 換成 H 級容錯＋中央星星，格子變小，所以留白從 9% 縮到 4% 把 QR 放大，
      實測掃碼容錯與原版相同；不要再加大留白）。
     留白底的四角也是階梯缺角（小一號），⚠️ 缺角多邊形又多一份，改外形要一起改。 */
  box-sizing:border-box; width:100%; aspect-ratio:1; padding:4%; background:#F6E9DB;
  image-rendering:pixelated;
  --nc:8px; --ns:calc(var(--nc) / 2);
  clip-path:polygon(
    0 var(--nc), var(--ns) var(--nc), var(--ns) var(--ns), var(--nc) var(--ns), var(--nc) 0,
    calc(100% - var(--nc)) 0, calc(100% - var(--nc)) var(--ns), calc(100% - var(--ns)) var(--ns), calc(100% - var(--ns)) var(--nc), 100% var(--nc),
    100% calc(100% - var(--nc)), calc(100% - var(--ns)) calc(100% - var(--nc)), calc(100% - var(--ns)) calc(100% - var(--ns)), calc(100% - var(--nc)) calc(100% - var(--ns)), calc(100% - var(--nc)) 100%,
    var(--nc) 100%, var(--nc) calc(100% - var(--ns)), var(--ns) calc(100% - var(--ns)), var(--ns) calc(100% - var(--nc)), 0 calc(100% - var(--nc))
  );
}
.qr-side-code img, .qr-side-code svg{ display:block; width:100%; height:100%; shape-rendering:crispEdges; }
.qr-side-code.is-empty{
  display:flex; align-items:center; justify-content:center;
  font-size:13px; line-height:1.5; color:#8a7a7a; background:#F6E9DB;
  outline:3px dashed #A98F8F; outline-offset:-8px;   /* 預留位置的虛線；換成真的 QR 後拿掉 is-empty 就消失 */
}
/* 2026-10-15 改版：標題兩側的像素金星（呼應 QR 中央的星星與小獺身邊的星）。
   960px 最窄時標題仍有約 8px 餘裕；改字或改星星大小要重量。 */
.qr-side-title::before, .qr-side-title::after{
  content:''; display:inline-block; width:8px; height:8px; margin:0 3px; vertical-align:2px;
  background:#F5B027;
  clip-path:polygon(50% 0,62% 36%,100% 38%,70% 60%,80% 100%,50% 76%,20% 100%,30% 60%,0 38%,38% 36%);
}
/* 牛仔小獺：從卡片下緣探出頭比讚（翻向右邊、帽緣藏在卡片後面）。純裝飾。 */
.qr-side-mascot{
  position:absolute; width:56%; left:-4%; top:calc(100% - 18px);
  transform:scaleX(-1); z-index:-2;
}
