/* 樂賣市集 — 2D 逛市集
   色票沿用 market.css，與現有市集頁同一套視覺。
   2026-10-03 改版：Mobile First（390×844 為主要設計尺寸），目標是
   「使用者已經走進市集、站在攤位前面」，不是遠遠看整個市集。 */

:root {
    --cream-bg: #FBF7F0;
    --cream-light: #FFFDF9;
    --coral: #E8796B;
    --coral-soft: #F6D9D3;
    /* 加深的珊瑚紅：只給頁尾統計大數字用。--coral 在奶油底上對比只有約 2.7:1（大字標準 3:1 都不到），
       這個約 3.5:1，色相相同（2026-10-04） */
    --coral-deep: #D2604F;
    --sage: #9FB392;
    --butter: #E8C98A;
    --wood: #B08960;
    --text-primary: #3D3A36;
    --text-secondary: #6B6660;
    --text-tertiary: #9B9590;
    --border-color: #EDE6DC;

    /* 版面定案 2026-10-03。
       主卡取 90vw（需求：90~94%），390px 手機 = 351px，落在 350~366 區間。
       gap 刻意是 0：卡片這麼寬時，兩側本來就只剩 (100vw − 卡寬)/2 ≈ 19.5px，
       再扣 gap 的話下一張會完全看不到 —— 露出的那一小角正是「還有別家店」的唯一線索。
       側卡只縮 3%（.97）：縮太多等於再吃掉露出的寬度，主從關係改由 opacity 表達。
       實際露出 ≈ 14px（390）／16px（430）／14px（375）。
       ⚠️ 上限用 px 不用 dvh：需求二十七明確要「寧可往下滑，也不要為了塞進第一屏縮小內容」，
          舊版那條 `calc(100dvh - …)` 會讓矮螢幕（SE 667）的攤位縮到不到 200px。
       ⚠️ 一律 dvh 不可用 vh（LINE 內建瀏覽器的工具列會蓋住）。 */
    --card-w: min(90vw, 420px);
    --card-gap: 0px;
    --side-scale: .97;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body { margin: 0; padding: 0; }
body {
    background: var(--cream-bg);
    color: var(--text-primary);
    /* 不再從 Google Fonts 下載（見 stroll.html 的註解）—— 這裡只排優先序：
       裝置有裝 Noto Sans TC 就用它，否則交給各平台內建的中文字型 */
    font-family: 'Noto Sans TC', -apple-system, BlinkMacSystemFont,
                 'PingFang TC', 'Heiti TC', 'Microsoft JhengHei', 'Noto Sans CJK TC', sans-serif;
    min-height: 100dvh;          /* 一律 dvh：LINE 內建瀏覽器的底部工具列會蓋住 vh */
    overflow-x: hidden;
}
a { color: inherit; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }

/* 統一 icon system：線條 icon 一律 stroke、不填色，不使用 emoji */
svg { stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; fill: none; }

/* ---------- 市集場景（頁首 → 搜尋 / 分類 → 攤位，共用同一片背景）---------- */
/*
 * 背景從頁首一路延伸到攤位後方，攤位才會像「站在市集裡」而不是貼在白紙上。
 *
 * 背景有兩張，依裝置切換 —— 這塊區域在手機是直式（實測 0.488~0.526:1）、
 * 桌機是橫式（1.57:1），差距太大不可能共用一張：
 *   · 直式圖放到桌機 → 只看得到高度的 38%
 *   · 寬幅圖放到手機 → 只看得到寬度的 20%（樹冠與旗幟都在左右兩側，會被裁光，
 *     標題區變成一片純藍天）
 * 兩張都是 cover，所以手機版的比例要盡量貼近 0.5:1（實測裁切最多 2.4%）。
 */
.scene {
    position: relative;
    background-color: #F3EADC;          /* 圖還沒載入時的底色（石板地） */
    background-image: url('assets/ui/market-bg-portrait.webp');
    background-repeat: no-repeat;
    background-position: center center;
    background-size: cover;
}
/* 奶油白薄紗：壓住背景、確保標題讀得到，但不能壓過頭。
   ⚠️ 強度要跟著背景圖本身的濃淡調 —— 2026-10-03 換上直式背景（本身已經畫得很淡）
   之後，原本為高對比寬幅圖調的 .5/.72/.5/.3 就過頭了：樹冠與遠景整個糊成一片米色。
   實測比過 4 組，這組（約原本的三分之一）在「樹冠有層次」與「標題讀得清」之間最好。
   往下越淡，讓攤位所在的下半部保留街景縱深。 */
.scene::after {
    content: ''; position: absolute; inset: 0; pointer-events: none;
    background: linear-gradient(rgba(251, 247, 240, .18) 0%, rgba(251, 247, 240, .32) 22%,
                                rgba(251, 247, 240, .15) 46%, rgba(251, 247, 240, .05) 100%);
}
/* ⚠️ 場景內所有實際內容都要疊在薄紗上面：::after 是最後產生的，
   同層級時會蓋住它前面的一切（測試 W24 擋住） */
.scene > * { position: relative; z-index: 1; }

.hero {
    position: relative;
    padding: calc(10px + env(safe-area-inset-top, 0)) 16px 10px;
    min-height: 84px;
    display: flex; align-items: center; justify-content: center;
}
/* ⚠️ 標題後面刻意「沒有」任何底板或柔光（2026-10-03 商家要求：文字直上）。
   試過一次 radial-gradient 柔光 —— 即使衰減到 0，在實際畫面上仍是一塊看得出
   邊界的亮斑，把頁首變成「貼了一張卡片」。
   可讀性改由文字自己的白色光暈（text-shadow）負責，那是貼著筆畫的，不會形成色塊。 */
.hero-tx { position: relative; text-align: center; }

/* 分類列浮在背景上的白色面板。
   ⚠️ 搜尋框刻意不放進來（2026-10-03 商家要求）—— 輸入框本身就是白底，
      再包一層白卡會變成兩層白、視覺很重（實機截圖看得很明顯）。 */
.panel {
    margin: 0 14px;
    background: rgba(255, 253, 249, .9);
    border-radius: 18px;
    box-shadow: 0 4px 14px rgba(139, 119, 101, .13);
    /* 高度預算：只剩分類列 ≈ 72px。
       第一屏要塞下「頁首 + 搜尋 + 這張卡 + 攤位圖 + 資訊卡 + dots」，
       而手機瀏覽器的可視高度只有約 734（不是 844）—— 這張卡每多 10px，
       底部的切換提示就少露出 10px。 */
    padding: 2px 0;
}
.hero-tx h1 {
    margin: 0; font-size: 25px; font-weight: 700; letter-spacing: 1.5px;
    color: #A8503F;
    /* 薄紗調淡後，標題直接壓在樹冠上 —— 光暈是它唯一的對比保障 */
    text-shadow: 0 1px 0 rgba(255, 253, 249, .95), 0 0 14px rgba(255, 253, 249, .92),
                 0 0 26px rgba(255, 253, 249, .7);
}
.hero-tx p {
    margin: 3px 0 0; font-size: 12px; letter-spacing: .5px; color: #7B6B5C;
    text-shadow: 0 1px 0 rgba(255, 253, 249, .9), 0 0 12px rgba(255, 253, 249, .95),
                 0 0 22px rgba(255, 253, 249, .75);
}

/* ---------- 搜尋 ---------- */
.search { position: relative; margin: 0 14px 6px; }
.search input {
    width: 100%; height: 44px;
    border: 1px solid var(--border-color); background: var(--cream-light);
    border-radius: 22px; padding: 0 38px 0 40px; font-size: 14px;
    font-family: inherit; color: var(--text-primary); outline: none;
    box-shadow: 0 3px 12px rgba(139, 119, 101, .09);
}
.search input::placeholder { color: var(--text-tertiary); }
.search input:focus { border-color: var(--coral-soft); }
.search .ico {
    position: absolute; left: 17px; top: 50%; transform: translateY(-50%);
    width: 19px; height: 19px; color: var(--text-tertiary); pointer-events: none;
}
.search .clr {
    position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
    border: 0; background: none; color: var(--text-tertiary);
    cursor: pointer; padding: 8px; display: none; align-items: center;
}
.search .clr svg { width: 16px; height: 16px; }

/* ---------- 街區分類（圓形 icon + 名稱，不顯示數量 badge）---------- */
.streets {
    display: flex; gap: 9px; overflow-x: auto;
    padding: 7px 10px 5px; scrollbar-width: none;
}
.streets::-webkit-scrollbar { display: none; }
.street {
    flex: 0 0 auto; border: 0; background: none; padding: 0; cursor: pointer;
    font-family: inherit; display: flex; flex-direction: column; align-items: center; gap: 4px;
    width: 40px;
}
.street .ic {
    width: 40px; height: 40px; border-radius: 50%;
    background: #fff; border: 1px solid var(--border-color);
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 2px 8px rgba(139, 119, 101, .08);
    transition: background .15s, border-color .15s, box-shadow .15s, transform .15s;
}
.street .ic img { width: 23px; height: 23px; display: block; }
.street .nm { font-size: 11px; color: var(--text-secondary); white-space: nowrap; }
/* 選中狀態（2026-10-04 商家指定）：輕盈、不要按鈕感。
   ⚠️ 不可再把 icon 推成白色 —— 舊的 `filter: brightness(0) invert(1)` 是配合
      單色剪影 placeholder 的做法，換上彩色手繪 icon 後選中會變成一團白、什麼都看不到。
   ⚠️ 放大只能用 transform（不影響 layout），不可改 width/height ——
      分類列的高度與排列必須完全不變。
   ⚠️ 邊框用 2px 不用 1.5px —— Chrome 會把小數邊框捨成整數像素（實測 1.5px → 1px），
      看起來跟未選中一樣粗。box-sizing 是 border-box，所以加粗不影響尺寸。 */
.street.on .ic {
    background: #FFF0EC;
    border: 2px solid var(--coral);
    box-shadow: 0 2px 9px rgba(232, 121, 107, .22);
    transform: scale(1.04);
}
.street.on .nm { color: var(--coral); font-weight: 600; }

/* ---------- 攤位 Carousel ---------- */
.stage-wrap { position: relative; }
.stage {
    display: flex; gap: var(--card-gap); overflow-x: auto;
    scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; scrollbar-width: none;
    padding: 2px calc((100vw - var(--card-w)) / 2) 4px;
    scroll-padding-inline: calc((100vw - var(--card-w)) / 2);
    min-height: 120px;
}
.stage::-webkit-scrollbar { display: none; }

/* 縮放／透明度由 JS 每一幀依「離中心多遠」直接算，刻意不用 transition ——
   用 transition 的話要等 220ms 才到位，放手後會覺得遲鈍（實測 385ms → 150ms） */
.card {
    flex: 0 0 var(--card-w); scroll-snap-align: center;
    /* ⚠️ flex item 預設 min-width:auto —— 店名長的那張卡會被自己的內容撐大
       （實測 375px 下撐到 539px）。卡片一旦不等寬，measureStep() 用
       「第二張的 offsetLeft 減第一張的」算出來的每格寬度就全錯，
       捲動定位與置中判斷會一路飄掉。 */
    min-width: 0;
    --s: var(--side-scale); --o: .72;
    transform: scale(var(--s)); opacity: var(--o);
}
.card.center { --s: 1; --o: 1; }   /* JS 還沒跑到時的保底 */

.frame {
    container-type: inline-size;   /* 招牌字級用 cqw，跟卡片寬走而不是跟 viewport */
    /* 素材比例 = 顯示比例（都是 1:1），所以沒有裁切 —— 繪師畫到邊的東西不會被切掉。
       改這個數字就要同步 _market-stall.js 的 displayAspect 與下面 .l-sign 的 left/width。 */
    position: relative; display: block; width: 100%; aspect-ratio: 1 / 1;
    /* 刻意不給背景也不裁切：攤位圖是去背的，直接站在 .scene 的市集背景上 */
    background: none; text-decoration: none; color: inherit;
}
.frame .l-stall { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
/* 圖片還沒下載完時的佔位。
   攤位圖是去背的、卡片本身沒有底色，所以沒載入時整張是透明的 ——
   使用者看到的是「空白」，分不出是還在載入還是壞掉。
   給一塊很淡的奶油白＋呼吸感，載完（.ready）就收掉。 */
.frame:not(.ready)::before {
    content: ''; position: absolute; inset: 8% 6% 4%;
    border-radius: 18px;
    background: rgba(255, 253, 249, .5);
    animation: stallLoading 1.4s ease-in-out infinite;
}
@keyframes stallLoading { 0%, 100% { opacity: .45; } 50% { opacity: .8; } }
@media (prefers-reduced-motion: reduce) { .frame:not(.ready)::before { animation: none; } }
/* 重試過還是載不起來：留一塊靜止的淡底讓招牌有東西可以站，
   但不再播呼吸動畫 —— 那會讓人以為還在載、一直等下去 */
.frame.img-failed::before {
    content: ''; position: absolute; inset: 8% 6% 4%;
    border-radius: 18px; background: rgba(255, 253, 249, .55);
    animation: none;
}
.frame .l-sign {
    /* 必須等於 _market-stall.js 的 signBoxOnScreen()。測試 W16 會比對。 */
    position: absolute; left: 23%; top: 13.5%; width: 54%; height: 9.7%;
    display: flex; align-items: center; justify-content: center;
    padding: 0 6px; font-weight: 700; line-height: 1.14;
    text-align: center; overflow: hidden;
    /* 正式攤位圖裡「招牌板已經畫好了」，所以這裡不可以再鋪一塊底色 ——
       那會變成在木頭招牌上貼色紙。字色由 signPalette 帶（每家店略有不同），
       加一圈淺色描邊確保在深淺木紋上都看得清楚。 */
    text-shadow: 0 1px 0 rgba(255, 253, 245, .85), 0 -1px 0 rgba(255, 253, 245, .55),
                 1px 0 0 rgba(255, 253, 245, .55), -1px 0 0 rgba(255, 253, 245, .55);
}
/* ⚠️ 一律單行。改版前是 `-webkit-line-clamp: 2`，但招牌板高度只容得下一行 ——
   長店名換行之後第二行會掉到板子外面（2026-10-04 商家回報）。
   字級已經跟著板子的寬高算（見 stroll.js 的 fs），正常長度不會需要截斷；
   真的太長就 ellipsis，總比掉出板子好。 */
.frame .l-sign span {
    display: block; max-width: 100%;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* 隨機換一攤：只在置中那張顯示。
   ⚠️ 它放在 .frame（<a>）外面 —— 放進去會變成連結裡包按鈕（巢狀互動元素），
      而且點擊會順便觸發跳轉。 */
.shuffle {
    position: absolute; top: 8px; right: 8px; z-index: 3;
    width: 38px; height: 38px; border-radius: 50%;
    border: 1px solid rgba(255, 253, 249, .9);
    background: rgba(255, 253, 249, .86);
    color: var(--wood); cursor: pointer; padding: 0;
    display: none; align-items: center; justify-content: center;
    box-shadow: 0 2px 10px rgba(139, 119, 101, .22);
}
.shuffle svg { width: 19px; height: 19px; }
.card.center .shuffle { display: flex; }
.shuffle.spin svg { animation: shuffleSpin .4s ease; }
@keyframes shuffleSpin { from { transform: rotate(0); } to { transform: rotate(180deg); } }
@media (prefers-reduced-motion: reduce) { .shuffle.spin svg { animation: none; } }
/* 目標攤位的圖還在下載：畫面維持在目前這一攤，按鈕持續轉表示「正在換」。
   ⚠️ 規則要排在 .spin 之後 —— 兩個 class 同時存在時（按下 -> 進入等待）要由這條勝出。 */
.shuffle.loading { cursor: progress; }
.shuffle.loading svg { animation: shuffleWait .9s linear infinite; }
@keyframes shuffleWait { from { transform: rotate(0); } to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .shuffle.loading svg { animation: none; opacity: .45; } }

/* 收藏愛心：與隨機鈕左右對稱，同樣只在置中那張顯示。
   一樣放在 .frame（<a>）外面，點了不會順便跳進賣場。 */
.fav {
    position: absolute; top: 8px; left: 8px; z-index: 3;
    width: 38px; height: 38px; border-radius: 50%;
    border: 1px solid rgba(255, 253, 249, .9);
    background: rgba(255, 253, 249, .86);
    color: var(--wood); cursor: pointer; padding: 0;
    display: none; align-items: center; justify-content: center;
    box-shadow: 0 2px 10px rgba(139, 119, 101, .22);
}
.fav svg { width: 19px; height: 19px; transition: fill .15s, stroke .15s; }
.card.center .fav { display: flex; }
.fav.on { color: var(--coral); }
.fav.on svg { fill: var(--coral); }
.fav.pop svg { animation: favPop .32s ease; }
@keyframes favPop { 0% { transform: scale(1); } 45% { transform: scale(1.28); } 100% { transform: scale(1); } }
@media (prefers-reduced-motion: reduce) { .fav.pop svg { animation: none; } }

/* 招牌刻意沒有任何動畫：店名是「要看清楚的資訊」，不是裝飾，一直浮動會很煩
   （2026-10-03 商家回報）。要加動態只能加在攤位圖本身。 */

.stage-msg { display: none; text-align: center; color: var(--text-tertiary); font-size: 14px; padding: 36px 20px; }

/* 攤位載入中（2026-10-09）：
   - 舞台先撐到一張攤位的高度 → 攤位出現時下方入口卡片不會先被擠上來、再被推下去
   - 箭頭先藏起來 → 舞台還是空的時候箭頭會浮在下方入口卡片上
   - 收掉的時機由 stroll.js setStageLoading() 決定（第一張攤位出現 / 顯示空清單或失敗訊息） */
.stage-loading { display: none; }
/* 攤位圖是 1:1（高 = 卡寬）＋ 下方店家資訊區（2026-10-09 實測手機、桌機都是約 109px）。
   改了 .info 的版面要一起調這個數字 —— test-market-stage-loading.mjs 的 L4 會量「入口卡片有沒有跳」 */
.stage-wrap.is-loading .stage { min-height: calc(var(--card-w) + 110px); }
.stage-wrap.is-loading .arrow { display: none !important; }
.stage-wrap.is-loading .stage-loading {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px;
    position: absolute; inset: 0; pointer-events: none;
}
.stage-loading-stall {
    width: min(calc(var(--card-w) * .56), 230px); aspect-ratio: 1 / 1;
    border-radius: 22px; background: rgba(255, 253, 249, .78);
    border: 2px solid rgba(176, 137, 96, .22);
    box-shadow: 0 8px 26px rgba(139, 119, 101, .16);
    animation: stallLoading 1.4s ease-in-out infinite;
}
/* 「擺設中」要一眼看得到（商家回報：沒有提示會以為市集沒東西）→ 用奶油色膠囊＋轉圈，不只是一行淡字 */
.stage-loading-text {
    display: inline-flex; align-items: center; gap: 9px;
    padding: 9px 18px; border-radius: 999px;
    background: rgba(255, 253, 249, .95); border: 1px solid rgba(176, 137, 96, .25);
    box-shadow: 0 4px 16px rgba(139, 119, 101, .18);
    font-size: 15px; font-weight: 700; color: #8A6B4A; letter-spacing: .04em;
}
.stage-loading-text::before {
    content: ''; width: 15px; height: 15px; flex: none; border-radius: 50%;
    border: 2.5px solid var(--coral-soft); border-top-color: var(--coral);
    animation: stageSpin .8s linear infinite;
}
@keyframes stageSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .stage-loading-stall { animation: none; }
    .stage-loading-text::before { animation: none; border-color: var(--coral); }
}

.arrow {
    position: absolute; top: calc(50% - 24px); width: 42px; height: 42px; border-radius: 50%;
    border: 1px solid var(--border-color); background: rgba(255, 253, 249, .95);
    color: var(--wood); cursor: pointer; display: none; z-index: 5;
    align-items: center; justify-content: center;
    box-shadow: 0 3px 12px rgba(139, 119, 101, .18);
}
.arrow svg { width: 20px; height: 20px; }
.arrow.l { left: 10px; } .arrow.r { right: 10px; }
@media (hover: hover) and (pointer: fine) { .arrow { display: flex; } }

/* ---------- 店家資訊卡（攤位插畫延伸出來的資訊牌）----------
   2026-10-04 商家要求：原本是純白大卡（#FFFDF9 + 四周陰影），
   和上方的日系手繪市集插畫有明顯斷層，看起來像一般 App Card。
   改成「攤位自然延伸出來的木牌／紙牌」：暖奶油漸層 + 只往下的柔和陰影
   + 右上角極淡的雛菊點綴。
   ⚠️ 資訊架構、DOM、JS 綁定一律不動 —— 這一段只有 CSS。 */
.info {
    position: relative;               /* 右上角的裝飾用 ::before 絕對定位 */
    /* 不用純白：上緣接近市集背景的米色、往下才轉奶油白，
       視覺上像插畫的地面延伸下來，而不是另外蓋一張卡 */
    background: linear-gradient(180deg, #FFFCF4 0%, #FFFAF0 52%, #FDF4E7 100%);
    /* 上方大圓角（接攤位）、下方稍收 —— 像一塊靠在攤位前的立牌 */
    border-radius: 22px 22px 18px 18px;
    /* 陰影只往下（-8px 的 spread 把上緣收掉）：頂部若有陰影就會出現一條
       「圖片結束 / 卡片開始」的切割線，正是商家說的斷層
       內高光那一條讓上緣看起來是光打在紙面上，不是生硬的邊界 */
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .95),
                0 10px 22px -8px rgba(139, 119, 101, .26);
    padding: 12px 14px 13px;
    /* 資訊卡往上疊：攤位圖底部本來就是留白（去背素材的地面陰影），
       疊上去視覺更連續，也替第一屏省下幾個像素 */
    margin-top: -14px;
}
/* 右上角點綴：一朵小雛菊 ＋ 兩片葉子 ＋ 幾顆粉色小花。
   ⚠️ 只是背景氛圍，opacity 壓到 .16 —— 它必須完全不影響店名與簡介的可讀性。
      文字（.info-top / .info-bot）在 DOM 順序上排在 ::before 之後，天然蓋在它上面。 */
.info::before {
    content: ''; position: absolute; top: 5px; right: 9px;
    width: 78px; height: 54px; pointer-events: none; opacity: .16;
    background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 130 90'><path d='M6 80 C24 72 40 58 50 40' fill='none' stroke='%239FB392' stroke-width='2.6' stroke-linecap='round'/><ellipse cx='26' cy='68' rx='11' ry='5.6' fill='%239FB392' transform='rotate%28-30 26 68%29'/><ellipse cx='45' cy='49' rx='9.6' ry='5' fill='%239FB392' transform='rotate%28-42 45 49%29'/><g fill='%23FFFFFF' stroke='%23E8C98A' stroke-width='1.6'><ellipse cx='88' cy='14' rx='6.4' ry='11.6' transform='rotate%280 88 26%29'/><ellipse cx='88' cy='14' rx='6.4' ry='11.6' transform='rotate%2872 88 26%29'/><ellipse cx='88' cy='14' rx='6.4' ry='11.6' transform='rotate%28144 88 26%29'/><ellipse cx='88' cy='14' rx='6.4' ry='11.6' transform='rotate%28216 88 26%29'/><ellipse cx='88' cy='14' rx='6.4' ry='11.6' transform='rotate%28288 88 26%29'/></g><circle cx='88' cy='26' r='5.4' fill='%23E8C98A'/><g fill='%23F6D9D3'><circle cx='116' cy='58' r='4.6'/><circle cx='108' cy='64' r='3.4'/><circle cx='122' cy='67' r='3'/></g></svg>") no-repeat right top / contain;
}
.info-top { display: flex; align-items: center; gap: 11px; position: relative; }
.info .logo {
    width: 42px; height: 42px; border-radius: 50%; flex: 0 0 auto;
    object-fit: cover; background: var(--coral-soft);
    /* 像一枚小徽章：奶油白外圈 ＋ 一圈極淡的珊瑚暈 ＋ 柔和落影 */
    border: 2.5px solid rgba(255, 253, 249, .96);
    box-shadow: 0 0 0 1px rgba(232, 121, 107, .16),
                0 3px 9px rgba(139, 119, 101, .2);
    overflow: hidden;
}
.info .logo-ph {
    display: flex; align-items: center; justify-content: center;
    /* 底色是淺粉（--coral-soft），字一定要用深色 —— 白字在這個底上完全看不見 */
    background: var(--coral); color: #fff;
    font-size: 19px; font-weight: 700; line-height: 1;
    font-family: inherit; letter-spacing: 0;
}
.info .tx { min-width: 0; flex: 1; }
.info .nm {
    margin: 0; font-size: 20px; font-weight: 700; line-height: 1.25;
    /* 深咖啡灰，不用純黑 —— 純黑在奶油底上太硬，與手繪插畫的調性不合 */
    color: #4A3F36;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.info .intro {
    margin: 3px 0 0; font-size: 14px; color: #8C8178; line-height: 1.4;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    /* 沒填簡介的商家也要佔住這一行：卡片高度一跟著「有沒有簡介」變動，
       下面的 dots 就會在滑動時上下彈（卡片是等高並排的，少一行就露餡） */
    min-height: 20px;
}
.info-bot { display: flex; align-items: center; gap: 9px; margin-top: 10px; position: relative; }
.info .tag {
    /* 原本是灰膠囊（#F3EDE4 + 灰字），在暖色卡上顯得髒。
       改成淡珊瑚粉底 ＋ 珊瑚紅字，與「進入賣場」同一個色系 */
    font-size: 12.5px; color: var(--coral); background: #FCEAE5;
    border: 1px solid rgba(232, 121, 107, .14);
    border-radius: 999px; padding: 3px 11px; white-space: nowrap;
}
.info .cnt { font-size: 12.5px; color: #A2968A; white-space: nowrap; }
.info .go {
    margin-left: auto; flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    height: 40px; padding: 0 20px; border-radius: 999px;
    /* 非常輕微的漸層（上淺下深 5%）＋ 柔和珊瑚陰影；
       不做玻璃效果、不做高對比漸層 —— 它只要看起來好按就夠了 */
    background: linear-gradient(180deg, #EC8577 0%, var(--coral) 100%);
    color: #fff; text-decoration: none;
    font-size: 14.5px; font-weight: 500;
    box-shadow: 0 3px 10px rgba(232, 121, 107, .3);
}

/* ---------- Carousel dots ---------- */
.dots { display: flex; justify-content: center; align-items: center; gap: 7px; height: 18px; }
.dots i { width: 6px; height: 6px; border-radius: 50%; background: #E3DACB; display: block; }
.dots i.on { width: 18px; border-radius: 3px; background: var(--coral); }

/* ---------- 市集入口（我的最愛 / 全部店家）---------- */
/* 三顆插畫按鈕一組：上排兩顆半寬（圖 1.5:1）、下排「加入市集擺攤」整行（圖 3:1）。
   兩種比例讓上下兩排高度幾乎相同，排在一起才像同一組。
   用 grid 不用 flex —— 整行那顆靠 `grid-column: 1 / -1` 跨欄，比 flex-basis 100% 直觀。 */
.gates {
    display: grid; grid-template-columns: 1fr 1fr; gap: 11px;
    /* 底部安全區域（iPhone 底部橫條）移到 .foot：.gates 已經不是頁面最後一個元素，
       放在這裡會在按鈕與頁尾之間塞出一段 34px 的空白，而頁尾本身反而貼到底部橫條下（2026-10-04） */
    padding: 14px 16px 14px;
}
.gate-wide { grid-column: 1 / -1; }
/* 只放圖片 —— 按鈕圖本身就畫了一張完整的卡片（白底圓角＋插畫＋文字＋箭頭），
   所以這一層不再給底色／邊框／陰影：給了就會變成「卡片裡面又一張卡片」。
   ⚠️ 改版前是 `height: 66px; object-fit: cover`，裁切後只看得到插畫中央，
      才看不出圖裡本來就有卡片邊界。改成完整顯示就露出來了。 */
.gate {
    min-width: 0; border: 0; background: none; box-shadow: none;
    padding: 0; font-family: inherit; cursor: pointer;
    display: block; line-height: 0;
}
.gate img { width: 100%; height: auto; display: block; }

/* ---------- 覆蓋層（搜尋結果 / 全部店家） ---------- */
.sheet { position: fixed; inset: 0; z-index: 50; background: var(--cream-bg); display: none; flex-direction: column; }
.sheet.open { display: flex; }
.sheet-hd {
    display: flex; align-items: center; gap: 10px;
    padding: calc(12px + env(safe-area-inset-top, 0)) 14px 12px;
    border-bottom: 1px solid var(--border-color); background: var(--cream-light);
}
.sheet-hd h3 { margin: 0; font-size: 16px; flex: 1; }
.hd-btn {
    display: inline-flex; align-items: center; gap: 4px;
    border: 1px solid var(--border-color); background: var(--cream-light);
    border-radius: 999px; padding: 7px 14px 7px 10px; font-size: 13px;
    color: var(--text-secondary); font-family: inherit; cursor: pointer; white-space: nowrap;
}
.hd-btn svg { width: 15px; height: 15px; }
.sheet-body { flex: 1; overflow-y: auto; padding: 12px 14px calc(20px + env(safe-area-inset-bottom, 0)); }
.row {
    display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
    padding: 11px 12px; background: var(--cream-light); border: 1px solid var(--border-color);
    border-radius: 14px; margin-bottom: 8px; cursor: pointer; font-family: inherit;
}
.row .av {
    width: 40px; height: 40px; border-radius: 50%; flex: 0 0 auto;
    display: flex; align-items: center; justify-content: center; background: #F3EDE4;
}
.row .av img { width: 24px; height: 24px; display: block; }
.row .tx { flex: 1; min-width: 0; }
.row .tx b { display: block; font-size: 14px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text-primary); }
.row .tx span { font-size: 11.5px; color: var(--text-tertiary); }
.row .chev { color: var(--text-tertiary); display: flex; }
.row .chev svg { width: 16px; height: 16px; }
.empty { text-align: center; color: var(--text-tertiary); padding: 40px 0; font-size: 14px; }

.fav-item { position: relative; }
.fav-item .row { padding-right: 56px; }
/* 清單裡的取消鈕：永遠顯示（覆蓋攤位上「只在置中卡顯示」的規則） */
.fav-item .fav-del {
    display: flex; top: 50%; left: auto; right: 10px; transform: translateY(-50%);
    width: 36px; height: 36px; box-shadow: none; border-color: var(--border-color);
}
.fav-item.gone .row { cursor: default; opacity: .55; }

/* ---------- 頁尾人氣小統計（2026-10-04）----------
   輕量、留白多：不是卡片 → 不給底色 / 邊框 / 陰影，直接放在奶油底上。
   三欄 grid（1fr 1px 1fr）：左右兩欄等寬，分隔線永遠在正中央（數字位數不同也不會偏）。
   間距：與上方「加入市集擺攤」約 22px（.gates 下內距 14 + 這裡 8）、與下方條款約 18px（16 + .foot 2）。 */
.mkt-stats {
    display: grid; grid-template-columns: minmax(0, 1fr) 1px minmax(0, 1fr); align-items: center;
    max-width: 340px; margin: 8px auto 16px; padding: 0 16px;
}
.mkt-stats[hidden] { display: none; }
.mkt-stat { display: flex; flex-direction: column; align-items: center; text-align: center; min-width: 0; padding: 0 8px; }
.mkt-num {
    font-size: 26px; font-weight: 700; line-height: 1.1; color: var(--coral-deep);
    font-variant-numeric: tabular-nums; letter-spacing: .01em; white-space: nowrap;
}
.mkt-lbl { margin-top: 4px; font-size: 12.5px; line-height: 1.3; color: var(--text-secondary); white-space: nowrap; }
.mkt-div { display: block; width: 1px; height: 28px; background: var(--border-color); }

/* ---------- 頁尾：擺攤招募 + 條款 ---------- */
/* 「想在市集擺攤？」那張文字卡已改成 .gate-wide 插畫按鈕（2026-10-04），
   相關樣式一併移除，頁尾只剩條款連結。 */
.foot { text-align: center; padding: 2px 16px calc(24px + env(safe-area-inset-bottom, 0)); }
.foot-links { display: flex; justify-content: center; align-items: center; flex-wrap: wrap; gap: 4px 8px; font-size: 12px; }
.foot-links a { color: var(--text-secondary); text-decoration: none; padding: 4px 0; }
.foot-links span { color: var(--text-tertiary); }
.copy { margin: 4px 0 0; font-size: 11.5px; color: var(--text-tertiary); }

/* ---------- 電腦版（≥768px）：中央舞台式 Carousel ----------
   ⚠️ 手機版（<768px）的 UI、尺寸、間距與互動全部鎖定：這一段只能放在 media query 裡，
      上面的基礎規則一行都不可以為了電腦版改。
   不是把手機 Carousel 直接放大，而是另一種構圖：
     · 舞台最寬 1440px、置中，超過的部分只剩市集背景
     · 一眼看到「中央 1 個主攤位 + 左右各 1 個副攤位」（副攤位縮小、變淡）
     · 只有中央那家顯示完整資訊卡；副攤位的資訊卡保留高度但隱藏，
       否則切換時卡片高度會跳、dots 跟著上下彈
   攤位圖比例（1:1）與招牌位置（.l-sign 23/14/54/9）沿用手機版，這裡不覆寫。 */
@media (min-width: 768px) {
    /* --side-scale 一定要放在 :root：JS 的 measureStep() 從 documentElement 讀它來算每一幀的縮放 */
    :root { --side-scale: .8; --card-gap: 16px; }
    .stage-wrap {
        max-width: 1440px; margin-left: auto; margin-right: auto;
        /* 卡寬與舞台左右留白都以「舞台寬」為基準（cqw），不是 viewport ——
           超過 1440 時 100vw 會比舞台大，中央就對不準 */
        container-type: inline-size;
        /* 三張剛好：主卡約佔舞台 1/3，副卡縮成 .8 後落在兩側。
           下限 280 讓 768 的平板主攤位不會太小（副攤位此時會被舞台邊緣切到一點，是預期的）；
           上限 420 與手機版主卡上限相同，招牌字級（cqw）才會一樣 */
        --card-w: clamp(280px, 34cqw, 420px);
    }
    .stage {
        padding-left: calc((100cqw - var(--card-w)) / 2);
        padding-right: calc((100cqw - var(--card-w)) / 2);
        scroll-padding-inline: calc((100cqw - var(--card-w)) / 2);
        /* 舞台兩端淡出：第四張卡滑進來時不會在 1440 的邊界被硬切一刀 */
        -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 48px, #000 calc(100% - 48px), transparent 100%);
        mask-image: linear-gradient(90deg, transparent 0, #000 48px, #000 calc(100% - 48px), transparent 100%);
    }
    /* 縮放的原點放在攤位圖底部（攤位圖是 1:1，底部 = 卡寬）：
       三個攤位站在同一條地面上，而不是副攤位懸在半空中 */
    .card { transform-origin: 50% var(--card-w); }
    .card .info { transition: opacity .18s ease, visibility .18s; }
    .card:not(.center) .info { opacity: 0; visibility: hidden; }
    /* 箭頭放在主攤位與副攤位之間的空隙正中央，不是舞台最外緣（1440 寬時離主攤位太遠，找不到）。
       空隙寬 = gap + 卡寬 × (1 − .8) / 2 → 箭頭（42px）的外側距中線 = 卡寬 × .55 + gap / 2 + 21px。
       ⚠️ 這裡的 .55 是由上面的 --side-scale: .8 推出來的，改縮放要一起改 */
    .arrow { top: calc(var(--card-w) / 2 - 21px); }
    .arrow.l { left: calc(50% - var(--card-w) * .55 - 29px); }
    .arrow.r { right: calc(50% - var(--card-w) * .55 - 29px); left: auto; }
    /* 頁尾統計：字級略大、欄距加寬（仍是同一個 grid，分隔線在正中央） */
    .mkt-stats { max-width: 420px; margin: 14px auto 20px; }
    .mkt-stat { padding: 0 16px; }
    .mkt-num { font-size: 30px; }
    .mkt-lbl { margin-top: 5px; font-size: 13px; }
    .mkt-div { height: 32px; }
}

/* ---------- 桌機：換寬幅背景、頁首與面板置中 ---------- */
@media (min-width: 900px) {
    .hero { min-height: 190px; }
    /* 面板本身要限寬 —— 只限裡面的 .search / .streets 的話，面板仍是整個 1440 寬，
       分類 icon 會全部擠在左邊（實測） */
    /* 桌機換成寬幅背景（直式圖在這個比例下只看得到高度的 38%） */
    .scene { background-image: url('assets/ui/market-bg.webp'); }
    .panel { max-width: 720px; margin-left: auto; margin-right: auto; }
    /* 搜尋框跟分類面板同寬：否則在 1440 寬會拉成一整條橫跨畫面的白條 */
    .search { max-width: 720px; margin-left: auto; margin-right: auto; }
    .streets { justify-content: center; }
    .gates, .foot { max-width: 760px; margin-left: auto; margin-right: auto; }
}
