/* 騎乘情境視覺語言：延伸 style.css 的深色主題與變數，不覆蓋既有色彩。 */
:root {
    /* 新增：四級風險/熱指數色（--danger/--warning/--success 沿用既有，只補上第四級 --caution） */
    --caution: #f1c40f;

    /* 新增：曹操品牌識別色（呼應 logo 的深紫袍 + 金色令旗），用於騎乘功能專屬強調色 */
    --brand-gold: #d4af37;
    --brand-gold-soft: rgba(212, 175, 55, 0.14);
    --brand-ink: #1e293b;
    --brand-ink-soft: rgba(56, 59, 89, 0.18);

    --road-line: rgba(30, 41, 59, 0.18);
    --surface-light: #ffffff;
    --text-muted: #64748b;

    /* 復古/科技融合：數據讀數用等寬字體，呼應「儀表／軍情戰報」的科技感 */
    --tech-font: "SFMono-Regular", Consolas, "Courier New", monospace;

    /* 曹操之字形進場動畫「一鏡到底」用的共用數字：caocao-run 這個 keyframes 動畫的
       最後一幀，跟 #intro-caocao.docked 的靜止樣式，兩邊都讀同一組變數算出來的數字，
       animationend 換 class 那一刻兩邊視覺上完全相等（同一個位置、同一個大小），
       才能做到真正零跳動的一鏡到底，不是用過場動畫去掩蓋跳動。
       --caocao-end-scale 必須手動維持等於 --caocao-dock-size ÷ --caocao-flight-w
       （calc() 沒辦法直接做「長度 ÷ 長度」，只能手動算好填進來）——這是 caocao-run
       100% 那一幀的 scale 目標值，也是中間所有 keyframe 用「終值的百分之幾」表示
       時的基準。改了 --caocao-dock-size 記得同步改這個值。 */
    --caocao-flight-w: 180px;
    /* 300px 是原始落地尺寸，這裡再乘 1.3 倍放大，避免主角被上方對話泡泡壓得太近、
       兩者視覺區隔不夠。放大只動這個尺寸變數，泡泡尺寸不連動。 */
    --caocao-dock-size: 390px;
    --caocao-dock-gap-x: 3vw;
    --caocao-dock-gap-y: 3vh;
    --caocao-dock-bubble-gap: 16px;
    --caocao-end-scale: 2.16667;
    --caocao-dock-offset: calc((var(--caocao-dock-size) - var(--caocao-flight-w)) / 2);
}

/* ---------- 進場動畫：依序浮現，不要一次生硬出現 ---------- */
@keyframes reveal-rise {
    from { opacity: 0; transform: translateY(16px); }
    to { opacity: 1; transform: translateY(0); }
}

.reveal {
    animation: reveal-rise 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
}

@media (prefers-reduced-motion: reduce) {
    .reveal {
        animation: none;
    }
}

/* 開場動畫播完後才由 JS 依序加上 .show-now 顯示的內容（AI 聊天框／天氣輪播卡片）。
   跟 .reveal 不同：.reveal 的 animation-delay 是從頁面載入就開始算，在 .ride-container
   淡入前早就播完了；.sequenced-reveal 預設是靜止的隱藏狀態，只有真的被加上 .show-now
   才會觸發 transition，藉此做出「動畫演完才依序出現」的效果。 */
.sequenced-reveal {
    opacity: 0;
    transform: translateY(16px);
    transition: opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
}

.sequenced-reveal.show-now {
    opacity: 1;
    transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
    .sequenced-reveal {
        transition: none;
    }
}

/* 背景圖片放在 body 上，兩頁共用同一份，不需要另外一個 fixed 圖層、也不需要 JS
   在切頁時顯示/隱藏——body 本身不受 .ride-container 的 opacity 淡入邏輯影響，
   一載入就看得到，兩頁切換時也不會有任何開關要處理。疊一層半透明深色漸層在圖片
   上方，確保不管照片本身亮暗，畫面上的文字／泡泡／卡片都維持一致的可讀對比。 */
body {
    /* 上下收緊到 14px（第一頁排版整合的一部分，騰出空間給下面對話框的白底框架），
       左右維持原本 40px 不動，水平留白不受影響。 */
    padding: 14px 40px;
    position: relative;
}

body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    background-image:
        linear-gradient(180deg, rgba(8, 13, 24, 0.62), rgba(8, 13, 24, 0.7)),
        url("assets/background_1.png");
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}

/* 預設讓主畫面完全透明、不可點擊 */
.ride-container {
    opacity: 0;
    pointer-events: none; /* 隱藏時防止使用者點擊裡面的按鈕 */
    transition: opacity 1.2s ease-in-out; /* 1.2秒的平滑淡入效果 */
}

/* 當 JS 在 5 秒後幫它加上 .show 類別時，主畫面會優雅淡入現身 */
.ride-container.show {
    opacity: 1;
    pointer-events: auto; /* 恢復可點擊狀態 */
}

/* 3. Z 字型奔馳軌跡 Keyframes (終極強制 Y 軸旋轉版) */
.ride-topnav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 16px;
}

.ride-back-link {
    /* 淺色文字：現在兩頁背景都是深色照片，跟第一頁的品牌文字用同一套淺色調 */
    color: rgba(232, 237, 247, 0.85);
    text-decoration: none;
    font-size: 0.9rem;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.ride-back-link:hover {
    color: var(--brand-gold);
}

/* 第二頁導覽列右側群組：天氣型態小圖示＋返回連結，都是 .ride-topnav 這個 flex 列
   的正常子元素。圖示不管放多大，都只會把這一列的高度撐高，不會蓋到下面的警報橫幅
   或正文——不再需要用 position:absolute + padding 去手動喬位置。 */
.ride-topnav-right {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
}

.top-weather-icon {
    font-size: 6.8rem; /* 使用者要求放大成 4 倍 (1.7rem → 6.8rem) */
    width: 176px;
    height: 176px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    border-radius: 50%;
    box-shadow: 0 6px 16px rgba(15, 23, 42, 0.1);
    /* emoji 字體常見的 baseline 偏移修正，讓圖示視覺上真正置中在圓圈裡 */
    padding-left: 2px;
    padding-bottom: 6px;
}

/* ---------- 第一頁：進場頁 ---------- */
/* 整頁是 flex-column：品牌列先佔掉它自己需要的高度（正常文件流），
   .entry-main 只會拿到「剩下的」空間，兩者物理上不可能疊在一起。 */
#page-entry {
    position: relative;
    /* 跟著 body 的上下 padding（14px×2=28px）算，維持跟先前同樣的邏輯關係。 */
    min-height: calc(100vh - 28px);
    display: flex;
    flex-direction: column;
}

/* riding-script.js 的 initCaocaoCompactOnScroll() 動態插入、拿來判斷「使用者
   是否還在頁面頂端」的隱形觀察點。position:absolute 讓它完全脫離文件流，不會
   在 .entry-brand-row 上方多擠出任何空間，也不會被看到；1px 只是給
   IntersectionObserver 一個實際尺寸可以判斷交集，不是視覺元素。 */
#scroll-top-sentinel {
    position: absolute;
    top: 0;
    left: 0;
    width: 1px;
    height: 1px;
    pointer-events: none;
}

/* 品牌列：正常文件流裡的一個區塊，佔掉它自己實際需要的高度。
   第一頁排版整合：頂部 padding 收緊（28px→10px），logo/字級整體縮小為 0.85 倍
   （見下面幾條規則），騰出垂直空間讓 1366×768 這類常見筆電解析度不需捲動就能
   看到完整頁面（含下面對話框新加的白底框架）。 */
.entry-brand-row {
    position: relative;
    z-index: 2;
    flex-shrink: 0;
    padding: 10px 32px 0;
}

.entry-brand {
    display: flex;
    align-items: center;
    gap: 14px;
}

.entry-brand-logo {
    position: relative;
    width: 102px;
    height: 102px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    filter: drop-shadow(0 2px 8px rgba(212, 175, 55, 0.35));
}

.entry-brand-img {
    margin-left: 10px;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.entry-brand-fallback {
    position: absolute;
    font-size: 1.1rem;
    color: var(--brand-gold);
}

.entry-brand-text-group {
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.entry-brand-text {
    font-weight: 700;
    /* 手機版斷字修復：改用 clamp() 讓字級跟著版面寬度連續縮放，不用另外開斷點。
       453px 以下維持 1.7rem 下限（避免中文字被壓到一行只擠得下一兩個字）、
       861px 以上維持原本 3.23rem 上限（桌機/筆電尺寸完全不受影響），中間才是
       6vw 的線性過渡。這個 class 兩頁共用（見下方 .results-brand 註解），改一次
       兩頁同步生效。 */
    font-size: clamp(1.7rem, 6vw, 3.23rem);
    color: #e8edf7;
    letter-spacing: 0.02em;
    line-height: 1.1;
}

.entry-brand-slogan {
    font-size: 0.89rem;
    font-weight: 500;
    color: rgba(232, 237, 247, 0.72);
    letter-spacing: 0.02em;
}

/* 品牌列底下剩餘的空間：三欄 CSS Grid（範例泡泡欄／置中堆疊欄／留白欄）。
   欄寬是 grid 軌道保留出來的真實空間，範例泡泡欄跟置中堆疊欄物理上不會重疊，
   不管視窗多寬都一樣——不是各自猜一個座標再祈禱不撞在一起。 */
.entry-main {
    flex: 1;
    position: relative;
    z-index: 1;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 800px) minmax(0, 1fr);
    align-items: center;
    column-gap: 28px;
    width: 100%;
    /* 上下收緊（第一頁排版整合，見 .entry-brand-row / body 的說明） */
    padding: 8px 24px 12px;
}

/* 左欄：兩個範例泡泡，靠右對齊（貼近中欄邊界），尾巴朝右指向中間的主要內容 */
.entry-examples-col {
    grid-column: 1;
    justify-self: end;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 36px;
    width: 100%;
    max-width: 300px;
}

.entry-main-spacer {
    grid-column: 3;
}



/* 中欄：曹操對話框、輪播卡片，由下往上排。寬度上限 960px（原本的 2 倍），
   實際欄寬由 grid 軌道決定，這裡不用再自己設 max-width。 */
.entry-stack {
    grid-column: 2;
    display: flex;
    flex-direction: column;
    align-items: center;
    /* 輪播/對話框本身尺寸不動，縮小兩者間距挪出空間（第一頁排版整合） */
    gap: 8px;
    width: 100%;
}

.entry-stack-carousel,
.entry-stack-chat {
    width: 100%;
}

.entry-stack-carousel .ride-weather-label {
    color: #e8edf7;
}

/* 第一頁手機版：三欄 grid（範例泡泡欄／置中堆疊欄／留白欄）在窄螢幕下完全放不下
   （960px 上限的中欄擠不出空間，兩側 minmax(0,...) 欄會被壓成 0px，但欄位裡的
   內容不會跟著縮到 0，於是溢出疊到中欄上面），改成單欄直式堆疊。範例泡泡欄
   維持在原本的 DOM 順序（第一欄，也就是視覺上「最上面」），只縮小字級/間距，
   不隱藏、不搬到下面——這是使用者確認過的做法。斷點跟第二頁 .ride-main-bottom
   的 900px 保持一致，同一支檔案裡兩個斷點數字互相呼應。 */
@media (max-width: 900px) {
    .entry-main {
        grid-template-columns: minmax(0, 1fr);
        row-gap: 20px;
        padding: 8px 20px 20px;
    }

    .entry-examples-col {
        grid-column: 1;
        justify-self: center;
        width: 100%;
        max-width: 480px;
        gap: 16px;
        /* 排在天氣輪播（order:1）之後、AI 對話框（order:3）之前，見下方
           .entry-stack 的 display:contents 說明。 */
        order: 2;
    }

    .entry-examples-col .thought-bubble {
        width: 100%;
        padding: 14px 18px;
        font-size: 0.82rem;
        border-radius: 28px;
    }

    /* .entry-stack 本來是「天氣輪播卡片」「AI 對話框」共用的 flex 容器，跟
       .entry-examples-col 是不同層級（entry-examples-col 是 .entry-main 的
       grid item，entry-stack-carousel／entry-stack-chat 是 entry-stack 的
       flex item，差一層）。手機版要把「範例提問」插進輪播卡片跟對話框中間，
       等於要把三個區塊攤平成同一層才能用 order 排序，所以這裡用
       display:contents 讓 .entry-stack 自己的框「消失」（不影響它的兩個子元素
       ——它本來就沒有自己的背景/邊框，只有 flex 版面用途），改由
       .entry-stack-carousel／.entry-stack-chat 直接變成 .entry-main 的
       grid item，三者用 order 互相插隊。 */
    .entry-stack {
        display: contents;
    }

    .entry-stack-carousel {
        grid-column: 1;
        order: 1;
    }

    .entry-stack-chat {
        grid-column: 1;
        order: 3;
    }

    /* 留白欄本來就沒有內容，單欄化之後不需要它再佔一個 grid-row。 */
    .entry-main-spacer {
        display: none;
    }

    /* 「📍 目前顯示：○○市 ○○區」這行原本 font-size:2rem 又 white-space:nowrap，
       桌機上欄寬夠所以沒事，中欄被壓窄之後這兩個設定會逼出橫向捲軸，縮小字級
       並允許換行修掉。 */
    .entry-stack-carousel .ride-weather-label {
        font-size: 1.05rem;
        white-space: normal;
    }
}

/* 曹操落地後不再自己用 position:fixed + top/left 算座標，改成跟 thought bubble
   一起變成 #intro-caocao-container.docked-stage（見下面）這個 flex 直欄的正常
   子元素：position:static，尺寸只由 --caocao-dock-size 決定，右下角座標完全
   交給父層的 flex 排版算，好處是不用預先知道泡泡的渲染高度（泡泡文字改長改短
   都不用動這裡的算式）。order:2 讓他排在 flex 欄位的最後一個（視覺上在最下面，
   緊貼容器的 padding 邊界＝視窗右下角）。
   數學上，父層 flex 容器「padding-right:gap-x + padding-bottom:gap-y、
   justify-content/align-items:flex-end」跟直接手算「right:gap-x; bottom:gap-y」
   結果完全等價，所以下面 @keyframes caocao-run 100% 那一幀用同一組變數手算出來
   的落點，跟這裡 flex 排出來的最終位置仍然精準對齊、animationend 換 class 那
   一刻零跳動——換了排版機制，落點座標的數學關係並沒有變。
   .docked 階段的來源圖檔（Cao_riding_docked.png，riding-script.js 在 animationend
   時換上）本身是正方形（1024×1024，跟飛行時的 Cao_riding.gif 畫布比例一致），
   所以 width 配 height:auto 實際渲染高度必然等於 width。 */
#intro-caocao.docked {
    position: static;
    order: 2;
    width: var(--caocao-dock-size);
    height: auto;
    opacity: 1;
    transform: rotateY(180deg);
    transition: none;
    animation: none;
}

/* thought bubble 共用樣式：只負責外觀（底色／邊框／陰影／字體），完全不管
   「什麼時候顯示」跟「顯示在哪」——這兩件事是每個用法各自的事，不要在共用樣式裡
   預埋 opacity:0 或固定座標，否則不同用法的邏輯會互相打架。造型改成真正圓潤的
   泡泡（大圓角），取代原本方形卡片+單一小尖角的做法，尺寸也放大。 */
.thought-bubble {
    position: relative;
    z-index: 2;
    width: 300px;
    flex-shrink: 0;
    padding: 22px 26px;
    border-radius: 46px;
    background: rgba(255, 255, 255, 0.96);
    border: 1px solid rgba(212, 175, 55, 0.45);
    box-shadow: 0 10px 26px rgba(15, 23, 42, 0.22);
    font-size: 0.95rem;
    line-height: 1.6;
    color: #1e293b;
}

/* 指向左邊（主角所在方向）的兩顆隨行小圓泡，經典 thought bubble 造型，
   取代原本單一小尖角，所有 thought-bubble 共用同一組。 */
.thought-bubble::before,
.thought-bubble::after {
    content: "";
    position: absolute;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.96);
    border: 1px solid rgba(212, 175, 55, 0.45);
}

.thought-bubble::before {
    left: -22px;
    bottom: 18px;
    width: 22px;
    height: 22px;
}

.thought-bubble::after {
    left: -36px;
    bottom: 6px;
    width: 11px;
    height: 11px;
}

/* 主角泡泡：明確顯示在曹操「正上方」（不是側邊）。不自己算 position:fixed 座標
   （position 沿用共用的 .thought-bubble：position:relative，只是給 ::before/
   ::after 尾巴當定位基準，不是拿來對到視窗），落地後的位置完全交給
   #intro-caocao-container.docked-stage 這個 flex 直欄決定：order:1 排在曹操
   （order:2）前面，也就是視覺上的「上面」；跟曹操之間的間距是容器的
   `gap: var(--caocao-dock-bubble-gap)`，水平對齊靠容器的 align-items:flex-end
   讓兩者自動貼齊同一個右緣，不用另外量測或計算泡泡自己的高度。docking 前是
   隱藏狀態，riding-script.js 在動畫結束後才加 .show-now 讓它淡入。只用 id 選到
   這一個元素，不會影響左側用 .reveal 的兩個範例泡泡（它們仍是尾巴朝左的原始
   樣式）。 */
#caocao-thought-bubble {
    order: 1;
    opacity: 0;
    transform: scale(0.92);
    transition: opacity 0.5s ease, transform 0.5s ease;
}

#caocao-thought-bubble.show-now {
    opacity: 1;
    transform: scale(1);
}

/* 主角泡泡的尾巴改成朝下指向曹操頭頂（蓋掉共用的 .thought-bubble::before/after
   朝左設定），兩顆圓泡沿泡泡底部、貼齊曹操那一側（右緣）往下遞減，較大的圓
   靠近泡泡本體，較小的圓靠近曹操，取代原本水平方向的尾巴。 */
#caocao-thought-bubble::before,
#caocao-thought-bubble::after {
    left: auto;
    bottom: auto;
    top: 100%;
}

#caocao-thought-bubble::before {
    right: 38px;
    margin-top: -11px;
    width: 22px;
    height: 22px;
}

#caocao-thought-bubble::after {
    right: 20px;
    margin-top: 2px;
    width: 11px;
    height: 11px;
}

.example-bubble-label {
    font-weight: 700;
    font-size: 0.78rem;
    color: var(--brand-gold);
    margin-bottom: 4px;
}

.example-bubble p {
    margin: 0;
}

/* 縮小版路線示意：跟第二頁 .route-map 共用同一套視覺語言（📍標記 + 公路連接線）。
   連接線改用跟第二頁 .route-edge 一模一樣的做法——同一張 assets/road.jpg 公路
   照片、object-fit:cover 填滿一個矮條，不是純色線條，只是縮小版尺寸（8px 高，
   對應第二頁的 20px），不需要顯示縣市/行政區文字。 */
.example-route-preview {
    display: flex;
    align-items: center;
    width: 100%;
    margin-top: 6px;
}

.example-route-pin {
    font-size: 1rem;
    line-height: 1;
    flex-shrink: 0;
}

.example-route-line {
    flex: 1 1 auto;
    height: 8px;
    object-fit: cover;
    border-radius: 2px;
    margin: 0 3px;
    display: block;
}

/* 左側空白處的兩個範例泡泡：位置交給 .entry-examples-col 這個 grid 欄位決定
   （不再自己 position:absolute 猜座標），寬度改成 100% 填滿欄位寬度（欄位本身
   上限 300px，比欄位窄時會自動縮小，不會超出邊界）。尾巴翻到右側，指向中間的
   主要內容，暗示這兩個想法是從中間延伸出來的。 */
.example-bubble {
    width: 100%;
}

.example-bubble::before,
.example-bubble::after {
    left: auto;
}

.example-bubble::before {
    right: -22px;
}

.example-bubble::after {
    right: -36px;
}

/* ---------- Mode tabs ---------- */
.ride-mode-tabs {
    display: flex;
    gap: 8px;
    margin-bottom: 18px;
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    border-radius: 12px;
    padding: 6px;
}

.ride-tab {
    flex: 1;
    padding: 10px 14px;
    border: none;
    border-radius: 8px;
    background: transparent;
    color: #475569;
    font-size: 0.95rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.2s, color 0.2s;
}

.ride-tab:hover {
    color: #1e293b;
}

.ride-tab.active {
    background: linear-gradient(120deg, #f8fafc 0%, #eef2ff 220%);
    color: #111827;
    box-shadow: 0 2px 10px rgba(148, 163, 184, 0.2);
}

/* ---------- Live alert banner (顯眼但不干擾：inline slim ticker, 不擋內容) ---------- */
.live-alert-banner {
    display: flex;
    align-items: center;
    gap: 12px;
    border-radius: 10px;
    border: 1px solid var(--danger);
    background: rgba(255, 245, 244, 0.95);
    box-shadow: 0 8px 22px rgba(15, 23, 42, 0.25);
    padding: 10px 16px;
    margin-bottom: 18px;
    animation: banner-slide-in 0.35s ease;
}

@keyframes banner-slide-in {
    from { opacity: 0; transform: translateY(-8px); }
    to { opacity: 1; transform: translateY(0); }
}

.live-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--danger);
    flex-shrink: 0;
    animation: pulse-dot 1.4s ease-in-out infinite;
}

@keyframes pulse-dot {
    0%, 100% { box-shadow: 0 0 0 0 rgba(192, 57, 43, 0.55); }
    50% { box-shadow: 0 0 0 6px rgba(192, 57, 43, 0); }
}

.live-alert-banner .banner-text {
    flex-grow: 1;
    font-size: 0.9rem;
    color: #1e293b;
}

.live-alert-banner .banner-text strong {
    color: #111827;
}

.live-alert-banner .banner-close {
    background: none;
    border: none;
    color: #475569;
    cursor: pointer;
    font-size: 0.9rem;
    flex-shrink: 0;
}

/* ---------- Layout ---------- */
.ride-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 24px;
    align-items: start;
}

.ride-col-form, .ride-col-main {
    min-width: 0;
}

.ride-main-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 24px;
    align-items: start;
}

.ride-main-left,
.ride-main-right {
    min-width: 0;
}

.route-chat {
    width: 100%;
    max-width: none;
    min-width: 0;
}

.ride-main-right {
    display: grid;
    gap: 20px;
    width: 100%;
    min-width: 0;
}

.ride-main-bottom {
    display: grid;
    grid-template-columns: minmax(260px, 320px) minmax(0, 1fr);
    gap: 24px;
    align-items: start;
    width: 100%;
}

.ride-main-left {
    min-width: 0;
    max-width: 320px;
}

.ride-main-right .route-chat {
    width: 100% !important;
    box-sizing: border-box;
}

@media (max-width: 900px) {
    .ride-main-bottom {
        grid-template-columns: 1fr;
    }

    .ride-main-left {
        max-width: none;
        width: 100%;
    }
}

/* ---------- 第二頁：結果頁 ---------- */
/* 左上角品牌區塊：跟第一頁共用 .entry-brand 的視覺（logo／標題／slogan），這次
   要求兩頁完全一致，所以 .results-brand 不再覆寫尺寸——直接沿用 .entry-brand-logo
   / .entry-brand-text / .entry-brand-slogan 跟第一頁一模一樣的數字，不用另外
   加規則。如果之後要讓第二頁的品牌區塊改小或改排版，再另外針對 .results-brand
   加專屬覆寫即可。 */

@media (max-width: 640px) {
    .ride-topnav {
        flex-direction: column;
        align-items: flex-start;
        gap: 8px;
    }

    .top-weather-icon {
        width: 100px;
        height: 100px;
        font-size: 3.8rem;
    }
}

/* 左欄 5 張資訊卡片：直式清單，沿用輪播出現前的舊版排法 */
.result-weather-grid {
    display: flex;
    flex-direction: column;
    gap: 14px;
    margin-top: 10px;
    width: 100%;
}

.result-weather-grid .wx-card {
    display: grid;
    gap: 10px;
    width: 100%;
    min-height: 120px;
    padding: 20px 16px;
    border-radius: 18px;
    text-align: center;
    box-shadow: 0 10px 24px rgba(15, 23, 42, 0.05);
    align-items: center;
    justify-items: center;
}

.result-weather-grid .wx-card > :first-child {
    margin: 0;
    font-weight: 700;
    font-size: 0.92rem;
    color: #475569;
}

.result-weather-grid .wx-value {
    font-size: 1.3rem;
    font-weight: 700;
    margin: 0;
}

.result-weather-grid .wx-sub,
.result-weather-grid .wx-sub2 {
    margin: 0;
    color: #64748b;
    font-size: 0.82rem;
    line-height: 1.35;
}

/* 舒適度卡片的角色圖示（riding-script.js 的 renderComfortIndex()）：來源圖檔是
   1024×1024 正方形透明底 PNG，object-fit:contain 讓它等比縮進這個尺寸，不變形。
   第二頁清單卡片空間比較侷促，圖示比輪播版小一號。 */
.result-weather-grid .wx-ci-icon {
    display: block;
    width: 100px;
    height: 100px;
    object-fit: contain;
}

.ride-panel h2 {
    font-size: 1.1rem;
    margin: 0 0 12px;
    text-align: left;
    /* 這些標題直接坐在 body 的深色照片背景上（不像卡片內文字有自己的底色），
       改用淺色調才看得清楚 */
    color: #e8edf7;
}

.ride-panel h2 i {
    color: var(--brand-gold);
}

/* 進場動畫把每個區塊包了一層 .reveal，垂直間距改由 wrapper 的 margin-top 控制，
   避免每個 h2 都變成自己 wrapper 的 first-child 而讓 h2:first-child 規則失效 */
.ride-col-form .reveal,
.ride-col-main .reveal {
    margin-top: 22px;
}

.ride-col-form .reveal:first-child,
.ride-col-main .reveal:first-child {
    margin-top: 0;
}

/* ---------- 出發地目前天氣（輪播卡片：中間置中放大，左右縮小半透明預覽） ---------- */
.weather-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}

.weather-header h2 {
    margin: 0;
}

.ride-weather-label {
    margin: 0 0 14px;
    color: #475569;
    font-size: 2rem;
    white-space: nowrap;
    text-align: left;
}

.wx-carousel {
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
}

.wx-carousel-viewport {
    flex: 1;
    min-width: 0;
    position: relative;
    height: 252px; /* 原本 168px 的 1.5 倍 */
    overflow: hidden;
    /* 跟 .wx-card 自己的圓角一致：overflow:hidden 的裁切邊界本身也要是圓角，
       側邊卡片被裁掉的切面才會呈現弧形輪廓，不是硬直角（卡片自己的圓角落在
       裁切線外側，永遠不會剛好露出來）。 */
    border-radius: 18px;
}

.wx-carousel-track {
    position: relative;
    width: 100%;
    height: 100%;
    cursor: grab;
    touch-action: pan-y;
}

.wx-carousel-track:active {
    cursor: grabbing;
}

/* 用 --offset（跟置中卡片差幾張，JS 算好的環狀最短距離）決定位移／縮放，
   左右兩側卡片維持完全不透明，跟中間卡片視覺清晰度一致，只靠縮放/層級區分主次。 */
.wx-carousel-slide {
    position: absolute;
    top: 0;
    left: 50%;
    width: 78%;
    max-width: 440px; /* 原本 220px 的 2 倍，跟隨 .entry-stack 加寬 */
    height: 100%;
    box-sizing: border-box;
    transform: translateX(calc(-50% + var(--offset, 0) * 62%)) scale(0.82);
    opacity: 1;
    transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.4s ease;
    pointer-events: none;
    z-index: 1;
}

.wx-carousel-slide.is-center {
    transform: translateX(-50%) scale(1);
    opacity: 1;
    pointer-events: auto;
    z-index: 2;
}

.wx-carousel-slide.is-far {
    opacity: 0;
    pointer-events: none;
}

.wx-carousel-slide .wx-card {
    /* 針對輪播情境單獨覆寫成完全不透明的白色，不用共用的 --panel-bg
       （那是 92% 透明度，卡片彼此絕對定位重疊時，透光的邊緣會讓中間卡片
       看起來半透明）。只影響輪播卡片，不影響第二頁 .result-weather-grid
       等其他用到 .wx-card 的地方。 */
    background: #ffffff;
    display: grid;
    gap: 10px;
    width: 100%;
    height: 100%;
    padding: 20px 16px;
    border-radius: 18px;
    text-align: center;
    box-shadow: 0 10px 24px rgba(15, 23, 42, 0.05);
    align-items: center;
    justify-items: center;
    box-sizing: border-box;
}

/* 卡片內文字/圖示（含 emoji 圖示，跟著所在文字的 font-size 縮放）整體放大 1.5 倍：
   0.92→1.38 / 1.3→1.95 / 0.82→1.23rem。 */
.wx-carousel-slide .wx-card > :first-child {
    margin: 0;
    font-weight: 700;
    font-size: 1.38rem;
    color: #475569;
}

.wx-carousel-slide .wx-value {
    font-size: 1.95rem;
    font-weight: 700;
    margin: 0;
}

.wx-carousel-slide .wx-sub,
.wx-carousel-slide .wx-sub2 {
    margin: 0;
    color: #64748b;
    font-size: 1.23rem;
    line-height: 1.35;
}

/* 舒適度卡片的角色圖示，輪播卡片空間比第二頁清單卡片寬裕，圖示大一號。 */
.wx-carousel-slide .wx-ci-icon {
    display: block;
    width: 100px;
    height: 100px;
    object-fit: contain;
}

.wx-carousel-btn {
    flex-shrink: 0;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    border: 1px solid var(--panel-border);
    background: var(--panel-bg);
    color: #475569;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.2s, color 0.2s, transform 0.1s;
}

.wx-carousel-btn:hover {
    background: var(--brand-gold-soft);
    color: var(--brand-ink);
}

.wx-carousel-btn:active {
    transform: scale(0.92);
}

@media (max-width: 920px) {
    .wx-carousel-viewport {
        height: 222px;
    }
}

/* ---------- Route chat（LLM 驅動的行程輸入） ---------- */
/* 重新加回一層白底框架，把「AI 引導訊息」跟「輸入框」包成同一個對話功能區塊
   （不是之前拿掉的那種厚重方形卡片——圓角夠大、陰影夠柔，視覺上仍算輕量）。
   裡面的引導訊息維持原本的琥珀色調當次要層次，輸入框維持自己原本的白色膠囊
   造型，兩者疊在這層暖白底色上，形成「白色大容器 → 琥珀色提示 → 更白的輸入
   膠囊」三層次。min-height 維持 270px 不變（對話框本身尺寸不動，這次只加外框），
   新增的 padding 是唯一多出來的高度，已經算進第一頁整體排版壓縮的預算裡。 */
.route-chat {
    background: rgba(255, 253, 248, 0.95);
    border: 1px solid rgba(212, 175, 55, 0.25);
    border-radius: 22px;
    box-shadow: 0 10px 28px rgba(15, 23, 42, 0.18);
    padding: 12px 16px;
    overflow: visible;
    display: flex;
    flex-direction: column;
    min-height: 270px; /* 原本 180px 的 1.5 倍，維持不變（content-box，padding 是額外疊加上去的高度，不會擠壓這 270px） */
    height: auto;
    transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}

.route-chat:hover {
    transform: translateY(-3px);
    box-shadow: 0 18px 38px rgba(15, 23, 42, 0.08);
    border-color: rgba(212, 175, 55, 0.22);
}

.route-chat-log {
    max-height: 480px; /* 原本 320px 的 1.5 倍 */
    overflow-y: auto;
    padding: 14px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.route-chat-msg {
    max-width: 92%;
    padding: 9px 12px;
    border-radius: 10px;
    font-size: 0.85rem;
    line-height: 1.55;
    opacity: 0;
    transform: translateY(8px);
    animation: fade-in-up 0.32s ease forwards;
}

.route-chat-user {
    align-self: flex-end;
    background: var(--secondary);
    border-bottom-right-radius: 2px;
}

.route-chat-assistant {
    align-self: flex-start;
    background: rgba(245, 158, 11, 0.12);
    border: 1px solid rgba(245, 158, 11, 0.25);
    border-bottom-left-radius: 2px;
    color: #1e293b;
}

.route-chat-assistant.is-error {
    background: rgba(248, 113, 113, 0.16);
    border-color: rgba(192, 57, 43, 0.32);
    color: #991b1b;
}

.route-chat-assistant .chat-raw-toggle {
    display: inline-block;
    margin-top: 6px;
    font-size: 0.76rem;
    color: var(--primary);
    cursor: pointer;
    text-decoration: underline;
}

.route-chat-assistant .chat-raw-body {
    margin-top: 6px;
    font-size: 0.72rem;
    color: #334155;
    background: rgba(226, 232, 240, 0.9);
    border-radius: 6px;
    padding: 6px 8px;
    white-space: pre-wrap;
    word-break: break-word;
}

.route-chat-unmatched {
    display: inline-block;
    margin-top: 4px;
    font-size: 0.78rem;
    color: var(--warning);
}

/* 輸入列本身是一條柔和陰影的圓角長條（不是先前那種厚重方形卡片），
   讓使用者一眼就能認出這是個可輸入的區塊，邊界清楚但不厚重。 */
.route-chat-form {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    margin-top: auto;
    background: rgba(255, 255, 255, 0.95);
    border: 1px solid rgba(212, 175, 55, 0.3);
    border-radius: 28px;
    box-shadow: 0 8px 22px rgba(15, 23, 42, 0.25);
}

.route-chat-form input {
    flex-grow: 1;
    min-width: 0;
    padding: 9px 12px;
    border-radius: 20px;
    border: none;
    background: transparent;
    color: #111827;
    outline: none;
}

.route-chat-form:focus-within {
    border-color: #fbbf24;
}

.route-chat-form button {
    background: linear-gradient(120deg, var(--brand-gold), #b8912c);
    color: #241a05;
    border: none;
    border-radius: 50%;
    width: 38px;
    height: 38px;
    flex-shrink: 0;
    cursor: pointer;
    transition: transform 0.1s, filter 0.2s;
}

.route-chat-form button:hover {
    filter: brightness(1.1);
}

.route-chat-form button:active {
    transform: scale(0.95);
}

.route-chat-form button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

#intro-caocao-container {
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100vh;
    pointer-events: none;
    z-index: 9999;
}

/* 動畫結束後（riding-script.js 的 freezeCaocaoAtRestPosition()）加在這個容器上：
   從「飛行舞台」切成貼齊視窗右下角的 flex 直欄，把曹操（.docked，order:2）跟
   thought bubble（order:1）疊成上下兩層。padding-right/bottom 就是
   --caocao-dock-gap-x/y，align-items:flex-end 讓兩者自動貼齊同一個右緣，
   justify-content:flex-end 讓整組貼齊底部——不用另外計算泡泡的渲染高度。
   容器本身仍是 position:fixed 滿版，這裡只是切換其排版模式，不影響飛行期間
   #intro-caocao 自己的 fixed 定位（飛行時 #intro-caocao 是它自己的
   position:fixed，不受這個 flex 排版影響，直到 .docked 把它改回 static 才
   真正變成這個 flex 容器的子元素）。 */
#intro-caocao-container.docked-stage {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: flex-end;
    gap: var(--caocao-dock-bubble-gap);
    padding: 0 var(--caocao-dock-gap-x) var(--caocao-dock-gap-y) 0;
    box-sizing: border-box;
    transition: transform 0.4s ease;
}

/* 使用者開始往下滑（riding-script.js 用 IntersectionObserver 偵測頁面頂端的
   sentinel 是否還在畫面內）之後，加上 .is-compact：把角色跟泡泡整組縮小一半，
   避免手機版長期釘在螢幕角落擋住下方 AI 對話框的輸入框。縮放整個容器（而不是
   分別改角色跟泡泡各自的尺寸變數），兩者的相對間距/對齊會自動等比縮小，不用
   另外處理；transform-origin 釘在容器自己的右下角——因為容器本身
   inset:0/100vw/100vh，它的右下角座標就等於視窗右下角，縮小後角色+泡泡仍貼齊
   同一個錨點，不會偏移位置。 */
#intro-caocao-container.docked-stage.is-compact {
    transform: scale(0.5);
    transform-origin: bottom right;
}

/* 曹操本體樣式與之字形動畫設定（飛行過程，不是停駐位置）。width 讀
   --caocao-flight-w，跟下面 keyframes、跟 .docked 的落地尺寸換算都是同一個
   基準值，不會各自寫死不同數字。 */
#intro-caocao {
    position: fixed;
    width: var(--caocao-flight-w);
    height: auto;
    opacity: 0;
    /* timing-function 特別選一條「兩端慢、中段快」的對稱曲線（跟 ease-in-out 標準
       曲線同一路數），刻意避開像先前 cubic-bezier(0.25,1,0.5,1) 那種內部控制點卡在
       y=1 的極端寫法——那種寫法會讓實際速度在真實時間裡前段暴衝、幾乎瞬間衝完全部
       位移，後段長時間幾乎靜止空等，觀感上不均勻。3.6s 這個時長也是配合這條新曲線
       重新抓的，需要跟 Cao_riding.gif 的逐幀延遲總和保持一致（見該檔案的中繼資料，
       51 幀、48 幀 @70ms + 3 幀 @80ms，總和精準 3600ms）。 */
    animation: caocao-run 4.2s cubic-bezier(0.55, 0.15, 0.65, 0.85) forwards;
    transition: opacity 0.5s ease-in-out;
    z-index: 10000;
}

/* 3. Z 字型奔馳軌跡 Keyframes ── 一鏡到底版
   中間每一幀的 scale 都寫成「--caocao-end-scale 的百分之幾」（30%→50%→70%→
   80%→90%→100%），不是寫死的絕對數字：不管 --caocao-end-scale 實際是多少
   （桌機/窄螢幕會是不同值，見下面 media query），這個百分比序列本身保證全程
   單調遞增、100% 那一幀必然精準等於 --caocao-end-scale——不會有「桌機對得上、
   手機對不上」的狀況。
   100% 這一幀的 top/left，用的是跟 #intro-caocao.docked 完全同一組
   --caocao-dock-* 變數算式，只多加一個 --caocao-dock-offset：因為這裡的
   scale() 是以元素中心點為軸心放大（transform-origin 預設 50% 50%），180px
   的基準框放大到「視覺上等於 --caocao-dock-size」時，框本身會往四周多長出
   (--caocao-dock-size − 180px)/2 的量，所以 top/left 要往回退這個偏移量，
   縮放後的視覺框邊界才會剛好落在跟 .docked 完全相同的位置——這樣 animationend
   換 class 那一刻，位置、大小、透明度、鏡像方向全部相等，畫面上真的沒有任何
   東西改變，才是名副其實的一鏡到底，不是靠過場動畫掩蓋銜接處。 */
@keyframes caocao-run {
    0% {
        top: 5%;
        left: -120px;
        transform: scale(calc(var(--caocao-end-scale) * 0.25)) rotateY(180deg);
        opacity: 0;
    }
    10% {
        opacity: 1;
    }
    40% {
        top: 30%;
        left: 100vw;
        transform: scale(calc(var(--caocao-end-scale) * 0.40)) rotateY(180deg);
        opacity: 1;
    }
    42% {
        transform: scale(calc(var(--caocao-end-scale) * 0.60)) rotateY(0deg);
    }
    72% {
        top: 50%;
        left: -250px;
        transform: scale(calc(var(--caocao-end-scale) * 0.70)) rotateY(0deg);
        opacity: 1;
    }
    74% {
        transform: scale(calc(var(--caocao-end-scale) * 0.80)) rotateY(180deg);
    }
    100% {
        top: calc(100vh - var(--caocao-dock-size) - var(--caocao-dock-gap-y) + var(--caocao-dock-offset));
        left: calc(100vw - var(--caocao-dock-size) - var(--caocao-dock-gap-x) + var(--caocao-dock-offset));
        transform: scale(var(--caocao-end-scale)) rotateY(180deg);
        opacity: 1;
    }
}

/* 窄螢幕／短視窗防裁切：曹操+泡泡疊在一起的預設尺寸是為常見桌機/平板視窗設計
   的，在較窄或較矮的視窗（例如手機）上，兩者維持原尺寸垂直疊加可能會超出視窗
   高度、或泡泡寬度超出視窗寬度，導致頂部被裁切。這裡只在視窗真的容不下時才
   等比縮小，不是為了幫泡泡騰位置而縮小曹操。
   只需要覆蓋 --caocao-dock-* 這組變數（--caocao-dock-offset 是用 calc() 從
   --caocao-dock-size 自動算出來的，不用另外覆蓋），#intro-caocao.docked 跟
   #caocao-thought-bubble 的 top/left/right/bottom 都是讀這組變數算出來的，
   會自動跟著變、不用重複寫一次座標算式；--caocao-end-scale 也要跟著手動同步
   （= --caocao-dock-size ÷ --caocao-flight-w），維持 caocao-run 100% 那一幀
   在窄螢幕下同樣精準對齊、零跳動。這裡只需要額外覆蓋泡泡「純外觀」（不影響
   定位）的寬度/內距/字級，避免文字在較窄的泡泡裡被擠壓。 */
  
@media (max-width: 640px), (max-height: 700px) {
    :root {
        /* 同樣是原始 150px 落地尺寸乘 1.3 倍 */
        --caocao-dock-size: 195px;
        --caocao-dock-gap-x: 4vw;
        --caocao-dock-bubble-gap: 10px;
        --caocao-end-scale: 1.08333;
    }

    #caocao-thought-bubble {
        width: min(300px, 78vw);
        padding: 16px 20px;
        font-size: 0.85rem;
    }
}

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

/* 之前用 .panel-hidden/.visible 讓這兩塊各自淡入，現在整個 #page-results 是
   一次性切換顯示（不做轉場動畫），這兩塊只要跟著頁面一起出現就好，不用再各自演一次。 */
.route-panel {
    opacity: 1;
}

.wx-card {
    transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
}

.wx-card:hover {
    transform: translateY(-3px);
    box-shadow: 0 18px 38px rgba(15, 23, 42, 0.08);
    border-color: rgba(212, 175, 55, 0.22);
}

.wx-carousel-slide .wx-card:hover {
    border-color: var(--panel-border);
}

.live-alert-banner {
    animation: pulse-banner 2.5s ease-in-out infinite;
}

@keyframes pulse-banner {
    0%, 100% { box-shadow: 0 0 0 0 rgba(212, 57, 43, 0.12); }
    50% { box-shadow: 0 0 0 20px rgba(212, 57, 43, 0); }
}

/* ---------- Route map visualization ---------- */
.route-map {
    position: relative;
    border: 1px solid var(--panel-border);
    border-radius: 18px;
    background: var(--panel-bg);
    padding: 24px 16px 30px;
    display: flex;            
    flex-direction: column;    
    justify-content: center;   
    min-height: 220px;
    overflow: hidden;
}

.route-summary-box {
    width: 100%;
    min-height: 170px;
    margin-top: 18px;
    padding: 18px 20px;
    border-radius: 18px;
    border: 1px solid var(--panel-border);
    background: var(--panel-bg);
    box-shadow: inset 0 0 0 1px rgba(148, 163, 184, 0.06);
}

.route-summary-title {
    font-weight: 700;
    margin-bottom: 10px;
    color: #1f2937;
}

.route-summary-text {
    color: #475569;
    line-height: 1.6;
    font-size: 0.95rem;
}

.route-map:hover {
    transform: translateY(-2px);
    box-shadow: 0 18px 38px rgba(15, 23, 42, 0.08);
}

.route-map::before,
.route-map::after {
    content: "";
    position: absolute;
    width: 18px;
    height: 18px;
    border: 2px solid var(--brand-gold);
    opacity: 0.55;
    pointer-events: none;
}

.route-map::before {
    top: 8px;
    left: 8px;
    border-right: none;
    border-bottom: none;
}

.route-map::after {
    bottom: 8px;
    right: 8px;
    border-left: none;
    border-top: none;
}

.route-map::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg, rgba(255,255,255,0.08), rgba(255,255,255,0) 30%);
    opacity: 0.25;
    pointer-events: none;
    animation: map-scan 8s linear infinite;
}

@keyframes map-scan {
    0% { background-position: 0 0; }
    100% { background-position: 0 100%; }
}

.route-map-track {
    position: relative;
    display: flex;
    align-items: flex-start; 
    justify-content: center;
    width: 100%;
    top: 0;                   
    padding-top: 0;          
    padding-bottom: 0;
}

.route-bike {
    position: absolute;
    left: 4%;
    /* .route-edge 高度是 20px、貼齊 track 頂部（y:0~20px）；所有 keyframes 都用
       translate(-50%, -50%) 把圖片中心錨定在 top 這個 y 座標，原本 top:20px 等於錨定在
       路段「底邊」，導致車身大半浮在路段下方。改成 10px（路段垂直中線）就會讓車身
       主要落在路段之上，看起來像騎在路上，而不是被路段從中間橫切。 */
    top: 10px;
    display: block;
    width: 40px; /* 這裡控制曹操騎車的圖片寬度，可以依畫面大小微調 */
    height: auto;
    animation: bike-travel 14s ease-in-out infinite; 
    z-index: 2;
    filter: drop-shadow(0 0 4px rgba(212, 175, 55, 0.7)); /* 自帶帥氣的金黃色光暈 */
    pointer-events: none;
}

@keyframes bike-travel {
    /* ==================== 往右騎階段 (0% ~ 48%) ==================== */
    0% {
        left: 4%;
        transform: translate(-50%, -50%) scaleX(-1) translateY(0);
    }
    /* 騎到中間：上下震動低點 */
    12% {
        transform: translate(-50%, -50%) scaleX(-1) translateY(3px);
    }
    24% {
        transform: translate(-50%, -50%) scaleX(-1) translateY(0);
    }
    36% {
        transform: translate(-50%, -50%) scaleX(-1) translateY(3px);
    }
    /* 即將抵達右邊頂點（96%）：維持翻轉狀態 */
    48% {
        left: 96%;
        transform: translate(-50%, -50%) scaleX(-1) translateY(0);
    }

    /* ==================== 在右端點轉向 (49% ~ 51%) ==================== */
    50% {
        left: 96%;
        transform: translate(-50%, -50%) scaleX(1) translateY(0);
    }

    /* ==================== 往左騎階段 (52% ~ 98%) ==================== */
    62% {
        transform: translate(-50%, -50%) scaleX(1) translateY(3px);
    }
    74% {
        transform: translate(-50%, -50%) scaleX(1) translateY(0);
    }
    86% {
        transform: translate(-50%, -50%) scaleX(1) translateY(3px);
    }
    /* 即將抵達左邊頂點（4%）：維持正常比例（面向左邊） */
    98% {
        left: 4%;
        transform: translate(-50%, -50%) scaleX(1) translateY(0);
    }

    /* ==================== 在左端點轉向 (99% ~ 100%) ==================== */
    100% {
        left: 4%;
        transform: translate(-50%, -50%) scaleX(-1) translateY(0);
    }
}

/* 節點＝使用者輸入的地點（起點/中停/終點），純標記，不帶風險色 */
.route-node {
    position: relative;
    z-index: 1;
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 0 4px;
    min-width: 60px;
}

/* 📍 標記：.route-node 跟 .route-edge 是 .route-map-track 這個 flex row 裡的
   兄弟元素，都用 align-items:flex-start 對齊到同一個頂邊——把這個標記的高度
   設成跟 .route-edge 一樣的 20px，兩者的垂直中心就會自動對齊在同一條線上
   （不用額外算絕對座標），標記剛好落在公路線段的正中央，而不是隨意置頂。
   （該站示警中時，riding-script.js 的 isStopTriggered() 會把這裡換成 ⚠️
   並加上 .is-alert，見下方。） */
.route-node .node-marker {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 20px;
    font-size: 1.15rem;
    line-height: 1;
    margin-bottom: 6px;
    flex-shrink: 0;
}

/* ⚠️ 示警圖示：跟下方「逐站路線摘要」示警卡片共用同一組硃砂紅（--danger）語言，
   讓使用者一眼就能把地圖上的圖示跟卡片對起來。圓形底光 + box-shadow 呼吸動畫，
   寫法比照 .live-dot 的 pulse-dot（同一組 rgba(192, 57, 43, ...) 數值），
   只是把擴散半徑放大到符合這個圖示的尺寸。 */
.route-node .node-marker.is-alert {
    border-radius: 50%;
    background: rgba(192, 57, 43, 0.18);
    animation: pulse-marker-glow 1.6s ease-in-out infinite;
}

@keyframes pulse-marker-glow {
    0%, 100% { box-shadow: 0 0 0 0 rgba(192, 57, 43, 0.45); }
    50% { box-shadow: 0 0 0 12px rgba(192, 57, 43, 0); }
}

.route-node .node-name {
    font-size: 0.82rem;
    font-weight: 700;
    color: #111827;
    line-height: 1.3;
    white-space: nowrap;
}

/* 每一站底下的即時天氣（氣溫／風速／降雨），資料尚未串接前顯示「無法顯示」 */
.route-node .node-weather {
    margin-top: 4px;
    font-size: 0.7rem;
    color: var(--text-muted);
    line-height: 1.5;
    white-space: nowrap;
}

.route-edge {
    position: relative;
    flex: 1 1 64px;
    height: 20px; /* 設定高度以容納圖片 */
    overflow: visible;
}

/* 路段目前沒有逐路段的風險資料來源，固定中性色文字提示，不假裝有風險等級可以上色 */
.edge-status-label {
    position: absolute;
    left: 50%;
    top: 100%;
    transform: translate(-50%, 4px);
    white-space: nowrap;
    font-size: 0.66rem;
    color: var(--text-muted);
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    border-radius: 20px;
    padding: 2px 8px;
    z-index: 1;
    pointer-events: none;
}

.edge-line-img {
    width: 100%;
    height: 100%;
    object-fit: cover; /* 讓圖片自動縮放填滿 */
    display: block;
}

@keyframes edge-glow {
    0%, 100% { filter: drop-shadow(0 0 0 rgba(255,255,255,0)); }
    50% { filter: drop-shadow(0 0 18px rgba(212, 175, 55, 0.14)); }
}

.route-edge .edge-line {
    position: absolute;
    left: 0;
    right: 0;
    top: 9px;
    height: 4px;
    border-radius: 2px;
    /* 將背景色改為黑色，並移除原本可能覆蓋顏色的變數設定 */
    background-color: #000000 !important;
    overflow: hidden;
}

.route-edge .edge-line::after {
    content: "";
    position: absolute;
    inset: 0;
    background: repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.6) 0 8px, transparent 8px 16px);
    background-size: 32px 100%;
    background-repeat: repeat-x;
    animation: route-edge-dash-move 1.4s linear infinite;
    mix-blend-mode: overlay;
}

@keyframes route-edge-dash-move {
    from { background-position: 0 0; }
    to { background-position: 32px 0; }
}

.route-edge .edge-badge {
    display: none !important;
}

.route-edge .edge-badge i {
    font-size: 0.68rem;
    color: var(--risk-color, #888);
}

.route-edge .edge-badge .edge-terrain-icon {
    color: #64748b;
}

.route-map-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: 16px;
    font-size: 0.78rem;
    color: #475569;
    position: absolute;   
    bottom: 15px;         
    left: 20px;           
    margin-top: 0;      
    z-index: 2;
}

.route-map-legend span {
    display: inline-flex;
    align-items: center;
    gap: 5px;
}

.route-map-legend .dot-sm {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    display: inline-block;
}

/* ---------- Day timeline ---------- */
.day-timeline {
    position: relative;
    padding: 24px 24px 24px 22px;
    border: 1px solid var(--panel-border);
    border-radius: 18px;
    background: var(--panel-bg);
    box-shadow: 0 10px 24px rgba(15, 23, 42, 0.05);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.day-timeline:hover {
    transform: translateY(-2px);
    box-shadow: 0 18px 38px rgba(15, 23, 42, 0.08);
}

.day-timeline::before {
    content: "";
    position: absolute;
    left: 6px;
    top: 6px;
    bottom: 6px;
    width: 2px;
    background: linear-gradient(var(--brand-gold), var(--panel-border));
}

.day-card {
    position: relative;
    margin-bottom: 18px;
    border: 1px solid var(--panel-border);
    border-left: 4px solid var(--risk-color, var(--panel-border));
    border-radius: 12px;
    background: var(--panel-bg);
    padding: 16px 16px 14px;
}

.day-card::before {
    content: "";
    position: absolute;
    left: -25px;
    top: 20px;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: var(--risk-color, var(--brand-gold));
    box-shadow: 0 0 0 3px var(--bg-color);
}

.day-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 10px;
}

.day-card-date {
    font-family: var(--tech-font);
    font-weight: 700;
    font-size: 1rem;
}

.risk-pill {
    font-family: var(--tech-font);
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 10px;
    border-radius: 20px;
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--risk-color, #fff);
    background: color-mix(in srgb, var(--risk-color, #888) 18%, transparent);
    border: 1px solid var(--risk-color, #888);
}

.day-card-suggestion {
    font-size: 0.85rem;
    color: #334155;
    background: rgba(241, 245, 249, 0.9);
    border-radius: 8px;
    padding: 8px 10px;
    margin-bottom: 10px;
    line-height: 1.5;
}

.day-card-suggestion i {
    color: var(--brand-gold);
    margin-right: 6px;
}

.day-segments {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.segment-row {
    display: flex;
    align-items: center;
    gap: 10px;
    border: 1px solid var(--panel-border);
    border-radius: 8px;
    padding: 8px 10px;
    font-size: 0.82rem;
}

.segment-row .seg-risk-icon {
    color: var(--risk-color, #888);
    font-size: 1rem;
    flex-shrink: 0;
    width: 18px;
    text-align: center;
}

.segment-row .seg-main {
    flex-grow: 1;
    min-width: 0;
}

.segment-row .seg-route {
    color: #111827;
    font-weight: 600;
}

.segment-row .seg-meta {
    color: #64748b;
    font-size: 0.76rem;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 2px;
}

.segment-row .seg-meta span {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

.segment-row .seg-meta .warn-flag {
    color: var(--warning);
}

.segment-row .seg-meta .water-flag {
    color: var(--primary);
}

/* 淹水／土石流警戒級別標籤 */
.segment-row .seg-meta .risk-level-flag {
    color: var(--warning);
}

/* ---------- Live monitor list ---------- */
.live-alert-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.live-alert-item {
    display: flex;
    gap: 12px;
    border: 1px solid var(--panel-border);
    border-left: 4px solid var(--danger);
    background: var(--panel-bg);
    border-radius: 10px;
    padding: 12px 14px;
    animation: banner-slide-in 0.3s ease;
}

.live-alert-item .cause-icon {
    font-size: 1.3rem;
    color: var(--danger);
    flex-shrink: 0;
}

.live-alert-item .live-time {
    font-family: var(--tech-font);
    font-size: 0.75rem;
    color: #475569;
}

.live-alert-item .live-segment {
    font-weight: 700;
    margin: 2px 0;
}

.live-alert-item .live-action {
    font-size: 0.85rem;
    color: #334155;
}

.hidden {
    display: none !important;
}

/* ==========================
   Scroll-driven Animations
   ========================== */
/* Progressive enhancement：用 CSS Scroll-Driven Animations（animation-timeline:
   view()）讓第二頁的幾個區塊（.result-weather-grid／#results-chat-slot／
   #route-visual-panel／#timeline-panel）捲入視窗時淡入＋輕微放大，純粹是視覺
   加分，不影響內容本身。這個 API 目前 Safari 支援度較低——不支援的瀏覽器會
   完全忽略 animation-timeline，.scroll-animate 的 animation 沒有指定
   duration（預設 0s），等同於瞬間套用終值（opacity:1 scale(1)，跟平常沒有
   這個 class 時的樣子一樣），所以不支援的瀏覽器上內容一樣完整顯示，只是少了
   淡入效果，不會壞版、不會有任何內容不見。
   跟 .route-node .node-marker.is-alert 的 pulse-marker-glow 呼吸動畫沒有衝突：
   兩者作用的元素不同（.scroll-animate 套在外層容器，.is-alert 在 #route-map
   內部很深的子元素），動的 CSS 屬性也不同（這裡是 transform/opacity，
   pulse-marker-glow 動的是 box-shadow），互不相干。 */
.scroll-animate {
    animation: scrolling linear;
    animation-timeline: view();
    animation-range: entry 0% cover 40%;
}

@keyframes scrolling {
    from {
        opacity: 0.9;
        transform: scale(0.85);
    }

    to {
        opacity: 1;
        transform: scale(1);
    }
}

/* ---------- Responsive: 手機版完整可用，非電腦版縮小 ---------- */
@media (max-width: 640px) {
    .ride-container {
        padding: 12px;
    }

    .ride-mode-tabs {
        flex-direction: column;
    }

    .route-map-track {
    position: relative;
    display: flex;
    align-items: center;      /* 確保垂直置中 */
    justify-content: center;  /* 確保水平置中 */
    width: 100%;
}

    .route-bike {
        left: 9px !important;
        top: 4%;
        /* 同桌機版理由：改成 -50% 讓車身垂直置中在道路線上，不是從 top 往下掛。 */
        transform: translate(-50%, -50%);
        animation-name: bike-travel-vertical;
    }

    @keyframes bike-travel-vertical {
        0% { top: 4%; }
        100% { top: 96%; }
    }

    .route-node {
        position: relative;
        flex-direction: row;
        align-items: center;
        text-align: left;
        min-width: 0;
        padding: 6px 0 6px 26px;
    }

    .route-node .node-marker {
        position: absolute;
        left: 0;
        top: 50%;
        transform: translateY(-50%);
        margin: 0;
        height: auto;
        flex-shrink: 0;
    }

    .route-edge {
        width: 100%;
        height: 34px;
        align-self: stretch;
    }

    .route-edge .edge-line {
        left: 9px;
        right: auto;
        top: 0;
        bottom: 0;
        width: 4px;
        height: auto;
    }

    .route-edge .edge-line::after {
        background: repeating-linear-gradient(180deg, rgba(255, 255, 255, 0.6) 0 8px, transparent 8px 16px);
    }

    .route-edge .edge-badge {
        left: 9px;
        top: 50%;
    }

    .day-card-head {
        flex-direction: column;
        align-items: flex-start;
    }

    .segment-row {
        flex-wrap: wrap;
    }

}
