/* ============================================================================
   site-mobile.css — mobile adaptation for the BUYER-FACING site only.

   Loaded LAST in the two buyer layouts (partials/header.php, backend/layout.php)
   and deliberately NOT in admin/layout.php, so the admin console is untouched.

   Rules of engagement (please keep to them):
     1. Every rule lives inside a `max-width` media query. Desktop must be
        byte-for-byte unaffected — that is the safety guarantee of this file.
     2. Do not edit site.css / admin.css to make room for something here. This
        file wins on source order at equal specificity, which is exactly why it
        can override `.admin-sidebar` (admin.css) without touching that file.
     3. Breakpoints reuse Bootstrap's own boundaries so nothing drifts:
          sm  <= 575.98px   phones (360/390/430 all live here)
          md  <= 767.98px   matches table-expand.js's `innerWidth < 768`
          lg  <= 991.98px   account sidebar drawer tier
        Bootstrap's own md/lg utilities use the same numbers, so `d-lg-none`
        and these queries always agree.
     4. site.css sets `html,body{overflow-x:clip}`. That CLIPS horizontal
        overflow instead of scrolling — a too-wide layout loses its right edge
        silently. Never "verify" a fix here by checking that nothing scrolls.
   ========================================================================== */

/* ---------------------------------------------------------------------------
   1. Global rails
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  /* The float-bar chat button is shrunk to 36px by site.css at this width; it is
     a primary contact CTA (the ad funnel's main fallback), so keep it tappable. */
  .float-bar .chaty-btn { width: 44px; height: 44px; font-size: 1.15rem; }

  body { font-size: 15px; line-height: 1.55; }
  h1 { font-size: 1.45rem; }
  h2 { font-size: 1.25rem; }
  h3 { font-size: 1.1rem; }

  /* Content images only. A blanket `img{max-width:100%;height:auto}` would fight
     the two-layer flash preview in site.css and the aspect-ratio tiles that rely
     on a fixed height, so it is scoped to the places that actually render
     author-supplied images. */
  .blog-content img,
  .p-info img,
  .card-body > img:not([class]) { max-width: 100%; height: auto; }

  .container,
  .container-fluid { padding-left: 12px; padding-right: 12px; }
  .section { padding: 40px 0; }

  /* Bootstrap modals: give their inner tables/forms the full width. */
  .modal-dialog { margin: .5rem; }
  .modal-content { border-radius: .75rem; }
}

/* Tap targets — sized up to 991.98px, not just 767.98px: an iPad in portrait is
   exactly 768px and is as much a thumb device as a phone. Note this stops at the
   `lg` boundary, so a desktop mouse (>=992px) sees buttons exactly as before.
   ⚠️ Deliberately NO `min-width` on `.topbar-link`. The topbar's action row is
   ~10 icons wide on a phone and is `flex:none`; forcing 44px min-width there
   pushes the whole bar wider than the viewport (it was already overflowing
   before this file existed). Height and padding carry the improvement instead,
   and the dense icon row is allowed to stay dense. */
@media (max-width: 991.98px) {
  .btn-accent,
  .btn-customize,
  .blank-buy-btn,
  .ps-chat-send,
  .blank-card-actions .blank-card-btn { min-height: 44px; }
}

/* ---------------------------------------------------------------------------
   2. Topbar —— **单行 + 纯图标**（用户 2026-10-07）

   ## 为什么之前是两行，为什么现在能回到一行

   量过（390px 生产）：`.topbar-actions` 那 8–10 个图标占 ~225px（`flex:none`），
   留给 `.topbar-nav` 的窗口只有 **151px**，而**带文字**的五个导航项要 **196px** ——
   于是 Blog 被切一半、About 整个跑到屏幕外，还看不出能横滑。所以当时拆成两行：
   第一行品牌 + 动作，第二行导航（**显示文字、隐藏图标**）。

   用户现在要求：「topbar 移动端目前分成了 2 行，能不能用**简写 icon** 的方式合成一行」。

   算过了，**换成纯图标正好放得下**：五个导航项各 ~30px ≈ **150px ≤ 151px** 窗口 ✓。
   所以这一版：**撤掉强制换行**、导航改成**只留图标**（与上一版正好相反）。

   ⚠️ 与上一版的取舍不同，别再"顺手改回去"：
      · 上一版优先"让人看懂文字"，代价是两行；
      · 这一版优先"一行"，代价是导航只剩图标（靠 `title` 与形状辨认）。
   ⚠️ `display:contents` 与 `order` 那套"提升成 container 子项"的写法一并撤掉 ——
      那是专为两行设计的；单行时让 `.topbar-nav` 恢复成一个**真正的盒子**，
      它自己的 `overflow-x:auto` 才继续有效（万一某个宽度仍差几像素，能横滑）。
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  .topbar .container {
    flex-wrap: nowrap !important;
    padding: .3rem .6rem;
    gap: .2rem;
  }

  /* 品牌：只留 logo 图形，省下那串文字的位置。 */
  .topbar-brand-text { display: none !important; }

  /* 导航：**图标 + 文字**（用户 2026-10-11：「移动端 topbar 现在宽度够了，随意
     blanks blogs links about 文本能不能加上，光 icon 不方便」）。
     ⚠️ 上面那段"151px 窗口"的测算**前提已经没了** —— 右侧动作区在 §33 收进一颗
        Account 按钮、不再跟导航抢第一行，导航这一侧真的放得下文字了。
     仍然保留 `.topbar-nav` 自带的 `overflow-x:auto` 兜底：万一某个更窄的机型还差几像素，
     横滑总好过把 About 顶出屏外（旧版就是因为顶出屏外才退回纯图标）。 */
  .topbar-nav { gap: .1rem; }
  .topbar-nav .topbar-link { padding: .3rem .3rem; font-size: .74rem; }
  .topbar-nav .topbar-link i { display: inline-block !important; font-size: .85rem; }
  .topbar-nav .topbar-label { display: inline !important; }

  /*
   * ⚠️⚠️ **这里原来有几条"移动端藏掉顶栏 Blanks/Links 下拉"的规则 —— 已全部删除**
   *    （用户 2026-10-11 改主意：「topbar 的 shopbyseasons / shopbycategories
   *     **不要删除了**，我改变主意了」）。下拉在手机上要照常能用。
   *
   *    ⚠️ **但请记住这条血泪教训，别再试图用 CSS 去藏它**：
   *    `assets/js/hover-menu.v2.js` 的 `openOne()` 里写着
   *      `menu.style.setProperty('display', 'block', 'important')` ——
   *    **行内 `!important` 的优先级高于样式表里的 `!important`**。
   *    所以无论把 `.dropdown-menu` / `.dd-below` / `.topbar ~ .dropdown-menu` 写成
   *    什么，**只要 JS 走过 `openOne()`，CSS 就永远盖不住**（前面几轮反复改 CSS 没生效
   *    就是这个原因）。真要控制它，只能改 JS 那一层。
   *
   *    下面 `.topbar-nav > .dropdown` 那几条只负责"触发器怎么排"，与显示无关。
   */
  .topbar-nav > .dropdown,
  .topbar-nav > .topbar-link { display: inline-flex; align-items: center; }

  /* 动作区的间距/图标化其实 site.css 的移动端那段**已经做过了**
     （`.topbar-actions .topbar-label{display:none}`、`gap:.05rem`、`topbar-cta` 藏掉），
     这里不再重复覆盖 —— 只把"导航显示文字"那两条反过来。

     "Get a Quote" 仍然是 site.css 的 `display:none`（单行里没有它的位置），**别在这里放出来**。 */
}

/* ---------------------------------------------------------------------------
   3. Account centre — off-canvas sidebar drawer  (<= 991.98px)

   admin.css:7 pins `.admin-sidebar` at 230px with no collapsing breakpoint, so
   on a 390px phone the content column was left ~160px. Making the sidebar
   `position:fixed` takes it out of flow and `.admin-main.flex-grow-1` reclaims
   the full width — no `.admin-wrap` flex surgery required.

   ⚠️ **2026-10-10 起三端都吃这一条**（用户：「admin 的 sidebar 也要和 buyer 一样
   移动端能折叠」）—— 原来只写给 buyer/seller、并刻意把 admin 排除在外，同时
   `admin/layout.php` **不引**本文件。现在：admin 也引了本文件，scope 里补上
   `admin`，管理台那份 sidebar 也挂上了 `id="adminSidebar"` + 菜单键 + 遮罩
   （与 buyer/seller 同一套 id / 类名，同一支 `sidebar-toggle.js`）。
   桌面端仍然零影响：本段整段在 `max-width:991.98px` 里。
   --------------------------------------------------------------------------- */

@media (max-width: 991.98px) {
  .app-shell .admin-sidebar[data-sidebar-scope="buyer"],
  .app-shell .admin-sidebar[data-sidebar-scope="seller"],
  .app-shell .admin-sidebar[data-sidebar-scope="admin"] {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    height: 100dvh;
    width: min(82vw, 300px);
    /* Above the topbar (1030); at/below the modal backdrop (1040) so a quick-buy
       modal dims the drawer instead of the drawer floating over it. This mirrors
       the existing `body.modal-open .admin-sidebar{z-index:1035}` in admin.css. */
    z-index: 1035;
    transform: translateX(-100%);
    transition: transform .22s ease;
    background: #fff;
    border-right: 1px solid var(--border);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding-bottom: env(safe-area-inset-bottom);
  }

  .app-shell .admin-sidebar.open { transform: none; }

  /* `--app-w:95%` (admin.css:49) is a desktop alignment device; on a phone it
     just steals 5% of an already-narrow column. */
  .admin-wrap { width: 100%; }

  .admin-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1034;
    background: rgba(16, 22, 40, .45);
  }
  .admin-backdrop[hidden] { display: none; }

  .admin-menu-btn {
    width: 44px;
    height: 44px;
    flex: none;
    border: 1px solid var(--border);
    border-radius: .5rem;
    background: #fff;
    color: var(--navy);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.15rem;
  }

  /* Scroll lock lives on <html>, not <body>: `html,body{overflow-x:clip}` in
     site.css makes <html> the scroll container (same trap documented there for
     modals). Putting it on <body> leaves the background scrollable. */
  html.drawer-open,
  html.drawer-open body { overflow: hidden; }
}

@media (max-width: 575.98px) {
  .app-shell .admin-topbar { padding: .7rem 1rem; }
  .app-shell .admin-main > main.p-4 { padding: 1rem !important; }
}

/* `/account/chat`、`/seller/chat` 的 two-pane 布局**已删**（2026-10-11）——
   三个收件箱页现在都是「顶部筛选 + 一份内嵌聊天窗」(partials/chat/window.php)，
   没有左右分栏这回事了。`.chat-scroll` 留着：message_list.php 那个约定 id 还在。 */
.chat-scroll { height: 45vh; min-height: 220px; }



/* ---------------------------------------------------------------------------
   4. iPhone safe areas

   These are INERT until `viewport-fit=cover` is added to the viewport meta tag
   (see partials/header.php). Without cover the env() values resolve to 0.
   Only edge-pinned elements are listed, because cover lets content slide under
   the notch / home indicator — everything else keeps the default inset.
   --------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
   5. Product cards (shared by /links masonry and /blanks grid)

   The card's action row holds two buttons side by side. `.masonry` runs at 2
   columns below 576px, which leaves each card ~165-175px — the two labels wrap
   and the buttons squeeze to ~31px tall (measured on production: `.blank-card-btn`
   is 81x31 and the Buy button 43-76x31). Stacking them vertically gives each the
   full card width and a comfortable height.

   The column count itself is NOT changed: going to 1 column would turn the grid
   into full-bleed Pinterest tiles, which is a much bigger visual change.
   --------------------------------------------------------------------------- */

/* Cards are ONE PER ROW on phones now (see product_card_row.php), so the action
   row has ~366px to play with and the two buttons sit side by side exactly as on
   desktop. The vertical stacking that used to live here was a workaround for the
   old 2-column / ~165px card and is no longer needed — their height is covered by
   the shared tap-target block above. */
@media (max-width: 575.98px) {
  .blank-card-actions .blank-card-btn { font-size: .82rem; }
  .masonry .blank-card-title,
  .blank-card-title { font-size: .92rem; }
  /* The size-matrix quantity stepper is NOT here -- it belongs to buy_css.php
     (body <style>), which would win on source order. See that file. */

  /* Collection (aggregate) member blocks — the per-member size/quantity table.
     Drop the middle SKU column: it is reference data the buyer does not need to
     order, and hiding it leaves the 120px size + 110px quantity columns fitting
     a 342px row comfortably. Hiding beats horizontal scrolling here, because
     scrolling to reach a quantity input is miserable on touch.
     Deliberately NOT routed through table-expand.js: that collapses to 2 key
     columns and would bury the quantity input behind an "Expand" button. */
  .goods-ob .ob-code { display: none; }
}

/* ---------------------------------------------------------------------------
   6. Product detail — only the parts owned by site.css

   ⚠️ The buy box (`.det-pt-info-tip`, `.blank-buy-group`, `.qty-step`, the size
   matrix) is NOT here. Those rules live in `partials/buy/buy_css.php`, which is
   emitted as a `<style>` block in the BODY. A `<link>` in <head> loses to a body
   `<style>` at equal specificity no matter which file it is — so putting them
   here would look like a fix while doing nothing. They are in buy_css.php.
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  /* Sticky section tabs: `flex-wrap:wrap` turns them into 2-3 stacked rows of
     sticky chrome. A single swipeable row is what the topbar already does. */
  .p-info-cate ul {
    flex-wrap: nowrap;
    justify-content: flex-start;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding: .5rem .75rem;
    gap: .4rem;
  }
  .p-info-cate ul::-webkit-scrollbar { display: none; }
  .p-info-cate li a { padding: .45rem .8rem; font-size: .85rem; white-space: nowrap; }
}

@media (max-width: 575.98px) {
  /* Packaging / FBA tables are already wrapped and 2-column-collapsed by
     table-expand.js (its selector covers `.p-info table`), so do NOT add
     .table-responsive here -- that would nest a second scroll container. */
  .p-info table.normal th:first-child { width: auto !important; }
  .p-info table.normal td,
  .p-info table.normal th { padding: .35rem .4rem; font-size: .82rem; }
}

/* ---------------------------------------------------------------------------
   6a. Sticky buy bar (product detail pages)

   用户 2026-10-06：「Add to Cart / Buy Now / Personalize 要在滚动 page 的时候
   fix 在底部」。回滚到买框之外时，这一条接管那三个动作 —— 按钮只是**代理点击**
   页面上那颗真按钮，逻辑不重复实现（见 partials/buy/sticky_buybar.php）。

   Shown/hidden by JS off the real buy box's viewport intersection, not by CSS.
   --------------------------------------------------------------------------- */

@media (max-width: 991.98px) {
  .pod-buybar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    /* Below the floating toolbar (1050) and the chat bubble (1040), above page
       content. The bubble is pushed up instead of being covered — see below. */
    z-index: 1032;
    display: none;
    gap: .45rem;
    padding: .5rem .75rem calc(.5rem + env(safe-area-inset-bottom));
    background: #fff;
    border-top: 1px solid var(--border);
    box-shadow: 0 -4px 16px rgba(16, 22, 40, .08);
  }
  .pod-buybar.show { display: flex; }

  .pod-buybar-btn {
    flex: 1 1 0;
    min-width: 0;
    min-height: 46px;
    font-weight: 600;
    font-size: .9rem;
    line-height: 1.15;
    border: 1px solid var(--border);
    border-radius: .5rem;
    background: #fff;
    color: var(--navy);
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  .pod-buybar-primary { background: var(--accent); color: #fff; border-color: var(--accent); }

  /*
   * 回到顶部（用户 2026-10-07 从 sidebar 搬到了视口右下角）默认 `bottom:16px`
   * —— 抬起来让开那条购买栏，否则正压在 "Buy Now" 上。
   * ⚠️ 这条原本作用于蓝色 chat 气泡（它以前也 fixed 在右下角）；2026-10-07 那颗气泡
   * 搬进 sidebar 列里了，右下角这个位置换成了回到顶部，所以改指向 `.float-top`。
   *
   * ⚠️ 2026-10-12（用户第 3 条）：**别写死高度**。
   *    原来钉的是 `78px` —— 那个数是"63px 的购买栏 + 15px 间隙"，
   *    是一次性的手算结果；而购买栏的高度**不是常数**（窄屏三颗按钮换行会变高、
   *    `safe-area-inset-bottom` 也会变）。一旦对不上，回到顶部就压回 Buy Now 上面。
   *    现在购买栏自己在每次 `sync()` 里把**实测高度**写进 `--pod-buybar-h`
   *    （见 `partials/buy/sticky_buybar.php`），这里只负责加间隙。
   *    兜底值 63px 与原来那个 78 的结果一致 ⇒ 变量没写上时行为与改动前逐字相同。
   */
  html.pod-buybar-on .float-top {
    bottom: calc(var(--pod-buybar-h, 63px) + 15px + env(safe-area-inset-bottom));
  }
}

/* ---------------------------------------------------------------------------
   6b. Footer

   On a phone the footer ran very tall (用户 2026-10-06：「footer 在移动端太高太大」):
   the brand block, three link columns wrapping two-up, a contact block, the payment
   row and the legal text, all separated by desktop-scale whitespace (`pt-5` = 3rem,
   `g-4` gutters, `mb-3` headings, `gap-2` link lists).

   This is a whitespace problem, not a content problem — the links themselves are
   fine to keep. So: tighten the vertical rhythm and drop the type a notch. The
   columns keep their 2-up mobile layout.
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  /* Bootstrap utilities carry `!important`, so these need it too; `footer.pt-5`
     out-specifies `.pt-5` and therefore wins the tie. */
  footer.pt-5 { padding-top: 3rem !important; }
  footer.pb-4 { padding-bottom: 1.25rem !important; }

  /* Column rows: `g-4` is 1.5rem on both axes. */
  footer .row.g-4 { --bs-gutter-x: 1rem; --bs-gutter-y: 1.1rem; }
  footer .row.g-4.pb-4 { padding-bottom: .75rem !important; }

  footer h6 { margin-bottom: .45rem !important; font-size: .84rem; letter-spacing: 0; }

  /* The three link lists are `d-flex flex-column gap-2` (0.5rem each) with an
     inline `font-size:.92rem`. */
  footer .d-flex.flex-column.gap-2 { gap: .3rem !important; font-size: .85rem !important; }

  /* Payment row + legal block: `border-top pt-3` twice, plus their own gaps. */
  footer .border-top { padding-top: .85rem !important; }
  footer .border-top.d-flex.flex-wrap { gap: .4rem !important; }
  footer .border-top.d-flex.flex-column { font-size: .82rem; }

  /* The brand block owns a big bottom margin on desktop. */
  footer .brand-logo { margin-bottom: .6rem !important; }
  footer .container > .row .col-lg-3:first-child > .mb-3 { margin-bottom: .6rem !important; }

  /* The three link columns are `col-6` — two per row, which strands "Occasions"
     alone on a half-empty row (the Contact block is `col-lg-3`, i.e. already full
     width down here). Three per row reclaims that entire row. */
  footer .row.g-4 > .col-6 { flex: 0 0 33.3333%; max-width: 33.3333%; }

  /* Legal paragraphs: `.85rem` with default line-height runs to ~180px of prose. */
  footer .border-top.d-flex.flex-wrap.justify-content-between { line-height: 1.45; }
}

/* ---------------------------------------------------------------------------
   7. Chat modal (site-wide widget)

   Between 820px and 576px it is 96vw wide with `.ps-list` at 38%, i.e. two
   skinny columns. Below 576px, switch to the native messaging pattern: the list
   fills the panel and the conversation replaces it, with a back control.
   `.ps-modal-body.convo-open` is toggled by chat/widget.php.
   --------------------------------------------------------------------------- */

@media (max-width: 575.98px) {
  /*
   * ⚠️ **不要用 `100vw`**（2026-10-10 用户：「点 input 会水平滑动，是不是弹键盘之后
   *    width 变了？」—— 就是这个）。
   *
   * `vw` 跟的是**布局视口**：手机上点输入框弹出软键盘时，**视觉视口**变窄而布局视口
   * 不变，于是 `100vw` 的盒子比"看得见的宽度"还宽 —— 浏览器为了把聚焦的输入框带进
   * 视野，就**横向滑动**整页。
   * 改成跟父级 `.ps-modal`（`position:fixed; inset:0`）吃宽度：`width:100%` 正好
   * 是那一层的确切宽度，没有 vw 的取整/滚动条误差。
   * 再补一道 `overflow-x:hidden` 兜底：里面任何东西溢出都不会把整页顶宽。
   */
  .ps-modal-box {
    width: 100%;
    /*
     * ⚠️ **`100%` 而不是 `100dvh`**（2026-10-11，001/002 两张截图）。
     * 键盘弹出时 iOS **不会**让 `dvh` 跟着缩 —— 盒子比"看得见的那块"高，
     * 底部的输入框 / Send 就被键盘压在下面。改成跟父级（`.ps-modal`）吃高度：
     * 父级的宽高由 `widget.php` 的 `pinToViewport()` 按**视觉视口**写死，
     * 于是键盘一出来盒子跟着变矮，输入框和 Send 正好落在键盘上方。
     */
    height: 100%;
    max-width: 100%;
    border-radius: 0;
    overflow: hidden;
  }
  .ps-modal-body { position: relative; display: block; overflow-x: hidden; }

  .ps-list {
    position: absolute;
    inset: 0;
    width: 100%;
    z-index: 2;
    border-right: 0;
    background: #f8f9fb;
  }
  .ps-modal-body.convo-open .ps-list { display: none; }

  .ps-convo { height: 100%; }
  .ps-convo-msgs { padding: .7rem .75rem; }
  .ps-modal-head { padding: .6rem .8rem; }

  /* Bigger hit areas for the thread rows and the avatar button. */
  .ps-list-item { padding: .7rem .8rem; min-height: 56px; }
  .ps-avatar { width: 44px; height: 44px; }

  /* Only shown while a conversation is open (full-width list hides it otherwise). */
  .ps-back { display: none; }
  .ps-modal-body.convo-open .ps-back { display: inline-flex; }
  .ps-modal-body:not(.convo-open) .ps-back { display: none; }

  /* 只有"没有浮动工具栏槽位"时的原地气泡走这条；进了 `.float-chat-slot` 的那颗
     由 site.css 的 36px 那条管（要和绿 chat 一样大）。 */
  .ps-chat-open { width: 52px; height: 52px; font-size: 1.4rem; }
}

/* The topbar is edge-pinned at the top, so with `viewport-fit=cover` it would
   otherwise slide under the status bar / notch. */
.topbar { padding-top: env(safe-area-inset-top); }
/* Landscape notch: the strip itself bleeds to the left/right edges. */
.topbar .container { padding-left: max(.75rem, env(safe-area-inset-left)); padding-right: max(.75rem, env(safe-area-inset-right)); }
/* Keep the last line of legal text clear of the home indicator. */
footer { padding-bottom: calc(1.25rem + env(safe-area-inset-bottom)) !important; }

.float-bar-wrap { right: max(0px, env(safe-area-inset-right)); }
/* 右下角那个位置现在是**回到顶部**（蓝色 chat 已搬进 sidebar 列里、
   它是 `position:static`，右边距/下边距对它没意义了）。 */
.float-top { right: max(16px, env(safe-area-inset-right)); bottom: max(16px, env(safe-area-inset-bottom)); }
/* 没有槽位时的原地气泡（防御性分支）仍按安全区躲刘海/home 指示条。 */
.ps-chat-open { right: max(12px, env(safe-area-inset-right)); bottom: max(14px, env(safe-area-inset-bottom)); }
.admin-sidebar { padding-bottom: env(safe-area-inset-bottom); }
.admin-backdrop,
.ps-modal { padding-left: env(safe-area-inset-left); padding-right: env(safe-area-inset-right); }

/* ---------------------------------------------------------------------------
   8. 面包屑 / Size Chart+FAQ 行：**保持一行不换行**（用户 2026-10-07）

   「移动端面包屑导航不要换行保持一行超过的隐藏」「sizechart 和 faq 也不要换行」。

   ⚠️ 光写 `white-space:nowrap` 是**没用的** —— flex 子项的 `min-width` 默认是
      `auto`（= 内容宽度），它宁可把容器撑破、或者干脆换行，也不肯缩。
      `text-overflow:ellipsis` 要生效，**必须**在每一层会参与收缩的元素上把
      `min-width` 置 0。这条踩过，别再只加 nowrap 就以为好了。
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  .breadcrumb { flex-wrap: nowrap; overflow: hidden; }
  .breadcrumb-item { white-space: nowrap; }
  /* 前面的层级（Home / Links…）**不缩**，否则会先被压成 "Ho…"；它们短且可点。 */
  .breadcrumb-item:not(.active) { flex: 0 0 auto; }
  /* 当前页那一段（常常是长商品名）吃掉剩余宽度，超出的截断。 */
  .breadcrumb-item.active { min-width: 0; overflow: hidden; text-overflow: ellipsis; }

  /* `.page-head` 那条细面包屑外面还有两层 flex（wrapper + nav），
     同样要 `min-width:0` 才能让它缩下去而不是换行。 */
  .page-head .container > div:first-child,
  .page-head nav { min-width: 0; }

  /* Size Chart / FAQ 两颗按钮：窄屏也不换行（两个小按钮本来就放得下）。 */
  .size-faq-row { flex-wrap: nowrap; }
  .size-faq-row .btn { white-space: nowrap; }
}

/* ---------------------------------------------------------------------------
   9. 页脚：再压一轮（用户 2026-10-07：「移动端 footer 太高太大了，减一半」）

   §6b 已经压缩过一轮（那是 2026-10-06 的「footer 太高太大」）。这一轮再砍：
   主要是**垂直节奏**和**字阶** —— 结构（4 列 + 付款 + 版权）一个都没动。

   ⚠️ 这一段在 §6b **之后**，同特异性下后者胜；但 §6b 有几条带 `!important`
      （Bootstrap 的 utility 也带），所以这里该带的也带，否则压不过。
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  /* 上下留白是最大的一块（3rem + 1.25rem ≈ 68px）。 */
  footer.pt-5 { padding-top: 1.1rem !important; }
  footer.pb-4 { padding-bottom: .5rem !important; }

  /* 列与列、行与行之间。 */
  footer .row.g-4 { --bs-gutter-y: .75rem; }
  footer .row.g-4.pb-4 { padding-bottom: .35rem !important; }

  /* 每个小标题下面到第一条链接之间。 */
  footer h6 { margin-bottom: .3rem !important; font-size: .8rem; }

  /* 链接列表：行距是第二大块（每条 ≈ 行高 + gap）。 */
  footer .d-flex.flex-column.gap-2 { gap: .12rem !important; font-size: .8rem !important; line-height: 1.35; }

  /* 品牌块：大字 logo + 一句 tagline + 一排社交图标。 */
  footer .brand-logo { font-size: 1.05rem !important; margin-bottom: .3rem !important; }
  footer .brand-logo + p { font-size: .8rem !important; margin-bottom: .4rem !important; line-height: 1.4; }
  footer .d-flex.gap-3.fs-5 { font-size: 1rem !important; gap: .8rem !important; }
  footer .container > .row .col-lg-3:first-child > .mb-3 { margin-bottom: .3rem !important; }

  /* 联系块 4 行。 */
  footer .contact-row { gap: .12rem !important; font-size: .8rem !important; line-height: 1.4; }

  /* 付款方式那条 + 版权那条（两条 `border-top pt-3`）。 */
  footer .border-top { padding-top: .5rem !important; }
  footer .border-top.d-flex.flex-column { font-size: .76rem; gap: .3rem !important; }
  footer .border-top.d-flex.flex-wrap { gap: .3rem !important; }
  footer .border-top.d-flex.flex-wrap.justify-content-between { font-size: .74rem; line-height: 1.35; }
}

/* ---------------------------------------------------------------------------
   9b. 页脚：四列挤进一行（用户 2026-10-07：「company / blank / occasions / contact
       四列一行，其他文本都可以缩小，减少高度到 50% 左右」）

   §6b 把它们排成"每行 3 个"，于是 Contact（`col-lg-3`）自己掉到第二行 ——
   整整多出一行的高度。这里把 4 列都钉成 25%（品牌块仍是整行，它是第一列）。

   ⚠️ 品牌块**也是** `col-lg-3` —— 所以选 Contact 必须用 `:last-child`，
      否则会把品牌块也压成 1/4 宽（那一块有 logo + tagline + 一排图标，压不得）。
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  /*
   * ⚠️ 这里**不再**有 `footer .row.g-4 > .col-6 { flex: 0 0 25%; max-width: 25% }`
   *    —— 那条是"四列挤一行"时代的（把每列卡在 366×25% ≈ 92px，本仓那段
   *    "92px 真凶"的注释就是它）。2026-10-13 用户改成"展开时四列一行、各占 1/4"，
   *    改由下面 `@media (max-width:991.98px)` 里的 `#footerMore:checked ~ .row >
   *    .footer-collapsible { flex: 1 1 0 }` 一条统一负责（它同时管平板档）。
   */

  /* 1/4 宽放不下 .8rem 的长词（"Christmas Ornaments"），收一档并允许断词。 */
  footer .row.g-4 > .col-6 h6 { font-size: .72rem; margin-bottom: .2rem !important; }
  footer .row.g-4 > .col-6 .d-flex.flex-column { font-size: .72rem !important; line-height: 1.3; }
  footer .row.g-4 > .col-6 a,
  footer .row.g-4 > .col-6 .contact-row > div { overflow-wrap: anywhere; }
}

/* ---------------------------------------------------------------------------
   10. 整块折叠：Key points / 发货信息条 / 运费块（用户 2026-10-07）

   「keypoint 默认折叠成一行」「ordertoday shipby oct8 afternoon 整个模块默认折叠成一行」
   「shipping 默认折叠到 ddp ddu 的说明」。

   机制见 `assets/js/m-collapse.js` 的文件头：每个可折叠块自己用
     `data-mcoll`（容器）/ `data-mcoll-head`（收起时只剩它）/ `data-mcoll-body`（展开才出）
   标注。**这里只管"收起时长什么样"**。

   ⚠️ 整段都在 media query 里 —— 桌面上这三个属性没有任何效果，
      渲染与改动前逐字相同（这是这个文件的第一条规矩）。
   ⚠️ **`data-mcoll` 必须和 `data-mcoll-head` 成对出现**：只有 `data-mcoll` 而没有 head 的
      容器，body 会被永久隐藏、却没有任何东西点得开它。这里**没有**用 `:has()` 去兜
      （要兼容旧 Safari），改由**测试**兜：`_scratch/_mcoll_view.php` 断言渲染出来的 HTML 里
      每个 `[data-mcoll]` 内部都找得到 `[data-mcoll-head]`。
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  /* 用**后代**选择器（不是 `>`）：发货信息条那块的 head/body 在 `blank_info_tip`
     的内层 flex 行里，不是容器的直接子元素。 */
  [data-mcoll]:not(.is-open) [data-mcoll-body] { display: none !important; }

  /* 收起时那"一行"：可点、给个箭头、别的照旧。 */
  [data-mcoll] > [data-mcoll-head] { cursor: pointer; }
  [data-mcoll] > [data-mcoll-head]::after,
  [data-mcoll] [data-mcoll-head]::after {
    content: '';
    display: inline-block;
    width: .5rem; height: .5rem;
    margin-left: .4rem;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    transform: rotate(45deg) translateY(-1px);
    transition: transform .15s;
    vertical-align: middle;
    opacity: .7;
  }
  [data-mcoll].is-open [data-mcoll-head]::after { transform: rotate(-135deg) translateY(-1px); }

  /* 折叠头不要被下面的元素压住点击区域。 */
  [data-mcoll] > [data-mcoll-head] { position: relative; z-index: 1; }

  /* 发货信息条：折起来只剩 "Order today, ships by … afternoon" 那一行。 */
  .det-pt-info-tip [data-mcoll-head] { margin-bottom: 0 !important; }

  /* 运费块：折起来只剩 DDP/DDU 那句说明；那句本来是小灰字，给个可读的尺寸。 */
  #goodsShipWrap [data-mcoll-head] { font-size: .78rem !important; }
}

/* ---------------------------------------------------------------------------
   11. 又一轮收紧（用户 2026-10-07 第二批）

   · 「keypoint 至少要有一行的卖点！标题也用折叠…默认显示 2 行」
     → 折起来时**标题 + 第一条卖点**（= 两行），而不是只剩标题。
   · 「accept payment 的 icon 也减小 50%」
   · 「footer 高度继续减少 50%」
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  /* Key points 折起来也要留住**第一条**卖点（标题 + 第一条 = 两行）。
     ⚠️ 覆盖 §10 那条"整个 body 藏起来"的通用规则，所以带 `!important`。 */
  .detail-selling-points:not(.is-open) ul[data-mcoll-body] { display: block !important; }
  .detail-selling-points:not(.is-open) ul[data-mcoll-body] > li:not(:first-child) { display: none !important; }

  /* 付款图标 26px → 13px（高写在行内，必须 `!important` 才压得动）。 */
  img.pay-icon { height: 13px !important; }

  /* ---- footer 再收一轮 ----
     ⚠️ **合规文本不能删**：`Footer.legal.governing` / `Footer.legal.payments` 那两行
        是收单机构要求必须出现在 footer 的（见 partials/footer.php 里的注释），
        所以这一轮只压"留白 + 字阶 + 社交图标"。 */
  /*
   * ⚠️ 这里原来是 `display: none !important` —— **整排社交图标在手机端被藏掉**。
   *    理由当时写在行尾："装饰性、且尚无链接"：那时 footer 里是五个写死的 `<span>`，
   *    点不动，藏了对买家没有任何损失。
   *
   *    2026-10-12 用户给了全套 handle、图标接上真链接 —— 这条隐藏就自相矛盾了：
   *    **手机是社交流量的主入口，却是唯一看不到这几个入口的地方**。
   *    改成**压小不藏**：这一节要的是"footer 高度继续减少 50%"（用户 2026-10-07），
   *    而链接该留 —— 一行小图标 ≈ 20px，比藏掉它们值。
   *
   * ⚠️ 两条规则都是 `!important`，靠**源码顺序**分胜负（本节在 §3 那条之后），
   *    别把它挪到上面去。
   */
  footer .d-flex.gap-3.fs-5 { font-size: .92rem !important; gap: .6rem !important; }
  footer .brand-logo { font-size: .95rem !important; margin-bottom: .15rem !important; }
  footer .brand-logo + p { font-size: .74rem !important; line-height: 1.3; margin-bottom: .25rem !important; }
  footer .row.g-4 { --bs-gutter-y: .5rem; }
  footer .row.g-4.pb-4 { padding-bottom: .15rem !important; }
  footer h6 { font-size: .7rem; margin-bottom: .15rem !important; }
  footer .d-flex.flex-column.gap-2 { gap: .05rem !important; font-size: .7rem !important; line-height: 1.28; }
  footer .contact-row { font-size: .7rem !important; }
  footer .border-top { padding-top: .35rem !important; }
  footer .border-top.d-flex.flex-column { font-size: .68rem; gap: .2rem !important; line-height: 1.3; }
  footer .border-top.d-flex.flex-wrap.justify-content-between { font-size: .68rem; line-height: 1.3; }
  footer .d-flex.flex-wrap.align-items-center { gap: .3rem !important; }
}

/* ---------------------------------------------------------------------------
   12. 又一批（用户 2026-10-07 第三批）

   · 2 列卡片里的按钮文字显示不全
   · footer：品牌块与"付款/合规"块要**同一行各 50%**
   · footer：版权行压到 **2 行以内**
   --------------------------------------------------------------------------- */

@media (max-width: 575.98px) {
  /*
   * 2 列卡片时每颗按钮只有 **~76px** 宽（390px 视口：容器 366 → 去掉 g-4 的 24px 间距
   * 再对半是 171，减去卡内 p-2 的 16px ≈ 155，按钮各占一半 ≈ 76）。
   * 而 "Customize Similar" 在 .7rem 下约 **105px** —— 溢出后被 `.blank-card` 的
   * `overflow:hidden` 裁掉，就是用户报的「显示不全」。
   *
   * 两条一起做才装得下：**去掉图标**（省 ~16px）+ 字号压到 .56rem（≈78px）。
   * ⚠️ 按钮本身已经是 `flex:1 1 0`（各 50%），宽度不用动 —— 用户说的"50% width"
   *    本来就是这个行为，真正撑破的是**文字**。
   */
  .blank-card-actions .blank-card-btn { font-size: .56rem; padding: .34rem .08rem; letter-spacing: -.01em; }
  .blank-card-actions .blank-card-btn i { display: none; }
}

@media (max-width: 767.98px) {
  /*
   * ---- 品牌块 + 付款/合规块：**同一行、各 50%** ----
   *
   * 这两块在 DOM 里隔着中间那四列，靠 `order` 把它们提到最前面才凑得到一行
   * （品牌 1、合规 2，其余四列 3）—— 用户 2026-10-07：「Operated by… 和
   * Pod-Sourcing.com / Factory-direct… 显示成 2 列同一行」。
   */
  /* ---- 用 **grid** 摆，而不是 flex + order ----
     用户 2026-10-07 追加：「we accept + 支付方式 做到 Pod-Sourcing.com **同一列下方**」——
     那要求"付款块在品牌下方、且同属左半边"，flex 的 order 做不到（order 只能换位置，
     不能把一块塞进另一块下面）。grid 可以：
       · 左半（第 1-2 栏）：品牌（第 1 行）→ 付款方式（第 2 行）
       · 右半（第 3-4 栏）：合规文本（跨两行）
       · 再下一行：Company / Blank / Occasions / Contact 四列（各 1 栏）
     ⚠️ `.footer-legal-col` 用 `display:contents` 把两个内层包装**提升成 grid 项**，
        这样才能分别指定位置（桌面端它仍是一个普通的 2/12 列）。 */
  footer .row.g-4 {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: .75rem;
  }
  /* 子项的 Bootstrap 左右内边距是给 flex 行用的，grid 里改用 `gap`，去掉免得双份。 */
  footer .row.g-4 > * { padding-left: 0; padding-right: 0; max-width: none; }

  /* ⚠️ 这块**不生效**（下面约 973 行那块条件相同且在后面，覆盖了它）。
     留着只是因为排法一致；要改 footer 手机端位置请改下面那块。 */
  .footer-brand-col { grid-column: 1 / span 2; grid-row: 1; }
  .footer-legal-col { display: contents; }
  .footer-pay-col   { grid-column: 1 / span 2; grid-row: 2; }
  .footer-terms     { grid-column: 3 / span 2; grid-row: 1 / span 2; }
  footer .row.g-4 > .col-6 { grid-column: span 1; }

  /*
   * ---- 版权行压到 **2 行以内** ----
   *
   * 原来是 `d-flex + justify-content:space-between`：两个子块各占一整行，
   * 政策链接自己还会折成两行 → 一共 3 行。
   * 改成 `display:block` 让版权句与那串链接**当一段连续文本自然折行**：
   * 合起来约 140 字符，.58rem 下每行能放 ~76 字符 → **正好 2 行** ✓
   * （链接是行内 `<a>`，流式排版照常可点）。
   */
  footer .border-top.d-flex.flex-wrap.justify-content-between {
    display: block !important;
    font-size: .58rem !important;
    line-height: 1.35;
  }
}

/* ---------------------------------------------------------------------------
   13. 又一批（用户 2026-10-07 第四批）

   · blog / post 卡：只留封面图 + 标题（不要摘要、不要 Read more、不要分类日期）
   · 回到顶部那颗在手机上**不是圆**（高 > 宽）
   · topbar 那几个导航图标太小、太挤，不好点
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  /*
   * ---- blog 卡：只留封面图 + 标题 ----
   *
   * 用户原话：「post blog 的手机端 card 内标题文本字减小！只显示封面图 + 标题，
   * 不要摘要不要 readmore」「首页的 post blog … 只有标题 + title 不要摘要」。
   * 三块一起藏：分类/日期行、摘要、Read more（改标题就是改封面图下面那一行）。
   * 两处宿主都挂了同一套类名：`blog/index.php`（列表页）与 `home/index.php`（首页预览）。
   */
  .blog-card-meta,
  .blog-card-excerpt,
  .blog-card-more { display: none !important; }

  .blog-card .card-title { font-size: .8rem; line-height: 1.3; margin-bottom: 0; }
  /* 2 列下封面 190px 太高（一屏放不下两张），压低一点。 */
  .blog-card img.card-img-top { height: 120px !important; }

  /*
   * ---- 回到顶部：**必须是真的圆** ----
   *
   * 用户报「不是圆？高大于宽？」—— 对的。`.float-top` 在 site.css 里定的是
   * 56×56 + `border-radius:50%`，但它**同时带着 `float-bar-item` 类**，
   * 而 site.css 的移动端那段写着 `.float-bar-item{ width:42px; ... }` ——
   * 同样特异性、**排在后面**，于是宽度被压成 42px、高度还是 56px → 高 > 宽。
   * 这里把宽度抢回来（em 内联/同特异性都压不过后面的 media query，所以要用 !important）。
   */
  .float-top { width: 56px !important; height: 56px !important; border-radius: 50% !important; }

  /*
   * ---- topbar 导航图标：加大 + 拉开间距 ----
   *
   * 用户：「blanks links blog about 都变成 icon 要有间距，不然太小了不好点，
   * 加大 icon + 一点间距」。图标尺寸原来跟着 `.topbar-link` 的 font-size 走
   * （移动端 .72rem ≈ 11px）—— 确实太小。这里单独给图标定尺寸。
   *
   * ⚠️ 图标一大，5 个导航项就未必挤得进那一行（见本文件 §2 的宽度测算）——
   *    真放不下时 `.topbar-nav` 会横向滚动（它自己带 `overflow-x:auto`），
   *    这是"好点"与"完全不用滑"之间的取舍，用户选了前者。
   */
  .topbar-nav .topbar-link { padding: .42rem .4rem; }
  .topbar-nav .topbar-link i { font-size: 1.15rem !important; display: inline-block !important; }
}

/* ---------------------------------------------------------------------------
   14. 又一批（用户 2026-10-07 第五批）

   · 首页「Shop by Season」的季节标签太大 → 手机端缩 **30%**
   · topbar 手机上全是图标了 → 图标尺寸 **+20%**
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  /*
   * ---- 季节标签 chip：整体缩 30% ----
   *
   * 用户：「首页 shop by season 的 tag 在移动端太大了减小 30%」。
   * 按比例缩三处（外距 / 字号 / emoji），**不是**只缩字号 ——
   * 只缩字号的话圆角与内边距还是原来的，看着依旧是"大块"。
   *   内边距 .55/.1   → ×0.7 ≈ .385/.7
   *   字号   .95rem   → ×0.7 ≈ .665rem
   *   间隙   .45rem   → ×0.7 ≈ .315rem
   *   图标   1.15rem  → ×0.7 ≈ .805rem
   *   计数   .72rem   → ×0.7 ≈ .5rem（再小就糊了，保底 .56rem）
   */
  .season-chip {
    padding: .385rem .7rem;
    gap: .315rem;
    font-size: .665rem;
  }
  .season-ico { font-size: .805rem !important; }
  .season-n { font-size: .56rem !important; padding: .04rem .3rem; }

  /*
   * ---- topbar 图标 +20% ----
   *
   * 用户：「topbar 移动端顶部都是 icon 了，icon 尺寸 +20%」。
   * 两处都按**当前值**加两成：
   *   · 导航（Blanks/Links/Blog/About）：1.15rem → **1.38rem**
   *   · 动作区那一排：原来没定过尺寸、跟着 `.topbar-link` 的 .72rem 走
   *     → 显式写成 **.86rem**（.72 × 1.2）
   *
   * ⚠️ 导航图标再大两成之后，390px 上五个导航项**很可能挤不进一行** ——
   *    `.topbar-nav` 自带 `overflow-x:auto`，会给横向滑动兜底（见本文件 §2 的测算）。
   */
  .topbar-nav .topbar-link i { font-size: 1.38rem !important; }
  .topbar-actions .topbar-link i { font-size: .86rem !important; }
}

/* ---------------------------------------------------------------------------
   15. 又一批（用户 2026-10-07 第六批）

   · 详情页标题：只留 2 行，多的隐藏
   · footer：四列之间 margin 收到 1px（"宽度明明够，换行很丑"）
   · footer：品牌块 + 付款/合规 的图标与文本再减 ~10%
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  /*
   * ---- 详情页标题：**2 行封顶** ----
   *
   * 用户：「详情页的标题要留 2 行，多余的隐藏」。
   * `-webkit-line-clamp` 是目前唯一稳的跨浏览器多行截断写法（标准 `line-clamp`
   * 支持面还不够）。三个属性**必须一起给**，少一个就不生效
   * （`display:-webkit-box` + `-webkit-box-orient:vertical` + overflow hidden）。
   * 标题长的商品（"Custom Photo … Personalized … for …"）在手机上原来能占 5-6 行。
   */
  .detail-title {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  /*
   * ---- footer 四列：把列间距收到 ~1px ----
   *
   * 用户：「明明宽度很够，但是文本换行格式很丑！优化 margin，1px 差不多就够了」。
   * 25% 一列在 390px 上只有 ~91px，而 grid 的 `gap:.75rem`(12px) 又吃掉一些 ——
   * "Kids Girl Dress" 就被拆成三行。收掉间距，每列多出十几像素。
   */
  footer .row.g-4 { gap: 1px; }

  /*
   * ---- footer 品牌块 + 付款/合规：图标与文本再减 ~10% ----
   *
   * （用户：「icon 和文本都能减少 10% 左右」。按当前值 ×0.9）
   */
  footer .brand-logo { font-size: .85rem !important; }
  footer .brand-logo + p { font-size: .665rem !important; }
  img.pay-icon { height: 12px !important; }
  .footer-terms > div { font-size: .68rem !important; }
}

/* ---------------------------------------------------------------------------
   16. 详情页底部「关联产品」：一行 3 个 + 做小（用户 2026-10-07）

   两处模块（link 页的 "You may also like"、blank 页的 "Ready-made designs on this blank"）
   在窄屏是一行 3 张（列宽由 PHP 侧的 `col-4` 决定），卡片里只有**图 + 标题**，
   而标题是长商品名 —— 3 列下每张只有 ~110px，必须把字号压下来。
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  /*
   * ⚠️ 作用域必须是 **`.pcr-row`**（2026-10-10 移动端修复）。
   *
   * 原先这两条是裸的 `.blank-card-title`，还带 `!important`。而 `/links` 的卡片
   * （`goods/index.php` 的瀑布流）**也**用这个类名 —— 于是它对 /links 生效、反过来
   * 压掉了本文件 §5 里那条**专门给 `.masonry` 写**的 `.92rem`（后者特异性更高，但
   * 是**非** `!important`，在 `!important` 面前一律输）。
   * 结果就是「scoped 的规则被 unscoped 的 `!important` 吃掉」：/links 卡片标题
   * 在 175px 宽的卡上只有 .72rem（≈11.5px），比该有的 .92rem 小一档。
   *
   * §16 的意图（见上面那段说明）是 **3 列**的关联行 / 首页行，那些都在 `.pcr-row` 里。
   */
  .pcr-row .blank-card-title { font-size: .72rem !important; line-height: 1.3; }
  /* 标题最多 3 行，多的截断 —— 3 列下再长也没意义，只会把卡片拉得参差不齐。 */
  .pcr-row .blank-card-title {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  /* 卡片内边距也收一档，把宽度让给图。 */
  .blank-card .p-2 { padding: .35rem !important; }

  /*
   * ---- 3 列卡片里，两颗按钮**并排放不下** → 改成上下堆叠（2026-10-08）----
   *
   * 3 列时卡宽只有 ~111px，而 `Customize`(76) + `Buy`(36) 并排要 112px ——
   * 实测**第二颗每张卡都溢出 11px**（`Buy` 的右边缘跑到卡片外面去了）。
   * 这不是"有点挤"，是**按钮真的被卡片裁掉**（截图里是 `Bu…`）。
   *
   * 堆叠之后每颗独占一行、各 ~95px，两个都放得下。只影响窄屏：
   * 桌面 6 列时卡宽 ~190px、平板 3 列时 ~230px，并排都够，不用动。
   *
   * ⚠️ 作用域是 `.pcr-row`（`product_card_row` 的样式根）—— 所以
   *    **首页 `Shop blanks`、link/blank 详情页底部关联行、blog 底部**一起生效
   *    （它们 3 列时是同一个毛病）。
   */
  /* ⚠️ `gap:1px`（用户 2026-10-11：「customize / buy 在移动端卡内是换行的，
     中间加一点间隙 1px 左右那样」）—— 上下堆叠时两颗按钮原本**紧贴**在一起，
     看着像一颗被切成两半。留 1px 缝，读起来是两颗。 */
  .pcr-row .blank-card-actions { flex-direction: column; gap: 1px; }
  .pcr-row .blank-card-actions .btn { width: 100%; }

  /*
   * ---- 堆叠时把圆角**改成上下连体**（2026-10-11 用户口径）----
   *
   * 那两颗的皮肤本来是给**左右并排**画的（`site.css:711/713`：只圆左两角 / 只圆右两角）。
   * 上下堆叠之后要的是**连体**：上面那颗圆上两角、下面那颗圆下两角，中间靠 `gap:1px`
   * 留一条缝。
   *
   * ⚠️ **2026-10-12 用户改回来了一次**：
   *    「移动端 2、3 列 card 按钮的边缘**左边都不完整**，比如首页的 blanks card、
   *      首页的 links card，都是左边感觉差 1px」。
   *
   *    病根是 `site.css` 那条 `.blank-card-buy{ border-left:0 }` —— 那是给**左右并排**
   *    用的（让两颗的接缝不出现双线）。堆叠之后 buy 变成"下面那颗"，
   *    于是它的**左边一整条边框不见了**：轮廓只剩"上、右、下"，
   *    看起来像画了一半（截图放大 3 倍看得很清楚）。
   *
   *    ⚠️ 注意**这不是** 2026-10-11 被否掉的那一版。那一版是
   *       "**各自四角全圆** + 补左边框"（两颗变独立按钮）。现在只补左边框，
   *       **圆角仍然是连体**（上圆上两角 / 下圆下两角），1px 缝也还在 ——
   *       连体的观感没变，只是把那条缺掉的线画回来。
   */
  .pcr-row .blank-card-actions .blank-card-customize { border-radius: .4rem .4rem 0 0; }
  .pcr-row .blank-card-actions .blank-card-buy {
    border-radius: 0 0 .4rem .4rem;
    /* 把 site.css 那条 `border-left:0` 补回来 —— 堆叠时左边那条线不能缺。 */
    border-left: 1px solid var(--accent);
  }
}

/* ---------------------------------------------------------------------------
   17. 触屏：topbar 的下拉变成"栏下方的一条整宽面板"（用户 2026-10-07）

   「blanks / links 点了之后 select 做为 DOM 插入 topbar 下方，再点就是 toggle 隐藏，
   然后点项目跳转」。

   DOM 的搬运在 `assets/js/hover-menu.js`（`.dd-below` 是它加的类）；这里只负责长相：
   不再是浮在触发器下面的小浮层，而是**贴着 topbar 下沿的一条整宽面板**。
   ⚠️ `position:static` 是关键 —— 它已经被搬到 `.topbar` 后面了，
      再 `absolute` 定位就会以那个新位置为基准乱跑。
   ⚠️ **让菜单"显示"的那条在 `site.css` 的顶层**（`.dropdown-menu.dd-below`）——
      因为 `site.css` 里原本那条 `display:block` 用的是**直接子元素**（`>`），
      菜单一被搬走就匹配不上 → 点了完全没动静（用户 2026-10-08 报的）。
      这里只管"长什么样"。
   ⚠️ 断点用 **991.98px**（不是 767.98）：`hover-menu.js` 的判据是 `(hover: none)`，
      触屏平板（>768px）同样没有 hover、同样会搬菜单 —— 那边也得有这么一条整宽面板，
      否则面板会带着桌面那套窄浮层的位置贴在栏下。
   --------------------------------------------------------------------------- */

@media (max-width: 991.98px) {
  .dropdown-menu.dd-below {
    position: static !important;
    float: none !important;
    width: 100%;
    margin: 0;
    border-radius: 0;
    border-left: 0;
    border-right: 0;
    box-shadow: 0 6px 16px rgba(16, 22, 40, .10);
    max-height: 60vh;
    overflow-y: auto;
  }
  /* 面板里每一项都做成好点的大行（触屏）。 */
  .dropdown-menu.dd-below .dropdown-item,
  .dropdown-menu.dd-below .gtm-all {
    padding: .6rem .9rem;
    font-size: .9rem;
  }
}

/* ---------------------------------------------------------------------------
   18. 手机 footer 四列：把宽度真正让给文字（用户 2026-10-07）

   用户：「这 4 列明显文本没满 width 结果就被迫换行了！」

   算过：390px 屏容器 366px，4 列各 **90.75px**；而 `Kids Girl Dress` 在 .7rem 下
   约 **87px** —— 只差几像素，字体渲染一波动就折行。三处一起收：
     1. 列间距 1px → **0**
     2. 链接字号 .7 → **.62rem**（`Kids Girl Dress` 降到 ~77px，留出余量）
     3. **去掉 `overflow-wrap:anywhere`** —— 它允许**在词中间断开**，
        于是"前面还有空位却把最后一个词甩到下一行"的观感更明显；改回 normal，
        只在空格处折。
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  footer .row.g-4 { gap: 0; }
  footer .row.g-4 > .col-6 .d-flex.flex-column,
  footer .row.g-4 > .col-6 h6 { font-size: .62rem !important; }
  footer .row.g-4 > .col-6 a,
  footer .row.g-4 > .col-6 .contact-row > div { overflow-wrap: normal !important; }
  /* 联系块那 4 行（邮箱/电话/微信/地址）也跟着收，否则照样顶格折。 */
  footer .row.g-4 > .col-6 .contact-row i { margin-right: .25rem !important; }
}

/* ---------------------------------------------------------------------------
   19. 手机 footer：四列用满宽度 + 品牌/合规挪到四列下方（用户 2026-10-07）

   用户两连问：
     「这 4 类之间为什么还是有这么空！width 不能完全利用吗？1px 间距或者 padding 就够了」
     「[品牌块 + We accept + 合规文本] 移动到 [Company/Blank/Occasions/Contact] 模块的下方」

   ⚠️ **"还是这么空"的真凶是 Bootstrap `.row` 的负外边距**：
      `.row` 自带 `margin-left/right: calc(-.5 * var(--bs-gutter-x))`，原本是配
      子项的左右 padding 用的。而上面几条已经把子项 padding 清零了 ——
      于是那 24px 变成"**行比容器宽 24px**"：4 列被整体抻宽并左移，
      看起来就是"列没对齐、空档在右边"。这里把负外边距也归零，两头才对得上。
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  footer .row.g-4 {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 0;                 /* 用户：「1px 间距或者 padding 就够了」→ 直接给 0 */
    margin-left: 0;         /* ← 归零那条负外边距（见上面注释） */
    margin-right: 0;
  }
  footer .row.g-4 > * {
    padding-left: 0 !important;
    padding-right: 0 !important;
    margin-left: 0 !important;
    margin-right: 0 !important;
    max-width: none;
  }

  /* ---- 顺序重排：四列在上，下面**品牌 ｜ 付款+合规 左右两列** ---- */
  footer .row.g-4 > .col-6 { grid-column: span 1; grid-row: 1; }

  /*
   * ---- 品牌 ｜ We accept 并排（2026-10-07）----
   *
   * 用户：「和 We accept … 要显示成 **2 列**，而不是当前一样 100% width！**节省高度**」。
   *
   * 原先这三块各占一整行（品牌 / We accept / 运营主体+管辖法）= 3 行。
   * 改成左品牌、右付款+合规之后省掉一整行：
   *   左（第 2-3 行）：品牌 —— `span 2` 跨两行，因为右边比它高，
   *                     不跨行的话品牌下面会空出一块
   *   右（第 2 行）：We accept + 付款图标
   *   右（第 3 行）：运营主体 / 管辖法声明
   *
   * ⚠️ 上面 §"footer 手机端" 那块（约 651 行起）**也**给这三个类定过 grid 位置，
   *    两块的媒体查询条件完全相同（都是 max-width:767.98px）—— **靠后者赢**。
   *    改这里要连着看那边，别只改一处（这次的"整行"就是被这一块覆盖出来的）。
   */
  /* ⚠️ 2026-10-08 用户改口径：**付款图标要跟品牌同一列**（左半），
     这样右半那一列只剩合规文本、高度立刻降下来。
     原话：「pay icon 等等 移动到左边 Pod-Sourcing.com … 和他同一列，减少右边第二列的高度」。

       左半：品牌（第 2 行）→ 付款图标（第 3 行）
       右半：合规文本（跨第 2–3 行）

     ⚠️ 上面第 676 行那块也摆过这三个类（那版正好就是这个排法）；两块条件相同、
        **这块在后面 → 以这块为准**。改这里就等于改最终效果。 */
  .footer-brand-col { grid-column: 1 / span 2; grid-row: 2; margin-top: .6rem !important; }
  /* `.footer-legal-col` 用 display:contents 把两个内层包装提升成 grid 项，
     这样才能把它们分开摆（桌面端它仍是一个普通的 2/12 列）。 */
  .footer-legal-col { display: contents; }
  .footer-pay-col { grid-column: 1 / span 2; grid-row: 3; margin-top: .4rem !important; }
  .footer-terms   { grid-column: 3 / span 2; grid-row: 2 / span 2; margin-top: .6rem !important; }

  /*
   * ---- 2026-10-12 用户改口径：手机端 footer 收成**一列**（不再左右两列）----
   *
   * 用户原话：「footer 口号 / operate、laws 这 3 块……**改成同一列内的分行显示**，
   *   然后 icons 也是行显示，就**没有 2 列了**，只在移动端这样更好看；
   *   另外移动端是不是可以多折叠一些 —— 只露出社媒 icons 和 pay icons」。
   *
   * 上面那一整套 4 栏 grid（品牌占 1-2 栏、合规占 3-4 栏）是 2026-10-07 的口径，
   * 现在**整块作废**：手机上就是**一列、从上到下**。
   *
   * 排出来的顺序（`grid-template-columns:1fr` 之后按 DOM 顺序自动落行）：
   *   ① 店招 + 口号（`.footer-brand-col`）
   *   ② 社媒图标（在①里面，本来就是一行 `d-flex gap-3`）
   *   ③ We accept + 付款图标（`.footer-pay-col`）
   *   ④ 运营主体 / 管辖法（`.footer-terms`）
   * 中间那四列导航（`.footer-collapsible`）默认被 `#footerMore` 折起来，点 More 才出现。
   *
   * ⚠️ 四个 `.footer-collapsible` 的 `grid-column: span 1`（下面 743 行那条）在单列
   *    网格里就是"占满这一列"，不用改。
   * ⚠️ `.footer-legal-col` 的 `display:contents` **保留** —— 不保留的话
   *    pay/terms 会被当成一个整体、只能一起摆，做不到"付款在品牌下面、合规再下面"。
   * ⚠️ 合规那两块（运营主体 / 管辖法）**必须仍然可见**（收单机构要求，见 footer.php
   *    里那段注释）—— 这次是"挪成一列"，**不是**把它们折起来。
   * ⚠️ 这一块**排在这里**（本文件最后一块 footer 规则）才赢：
   *    上面两处（§"footer 手机端"与这一段的 1104-1109 行）条件完全相同。
   */
  /*
   * ⚠️ **`!important` 不能省。**
   *
   * 本文件里 `footer .row.g-4` 这个选择器**一共出现过三次**（§"footer 手机端"两处 +
   * 这里），前两处写的是 `repeat(4, 1fr)`。同特异性下"后面那条赢"本来成立，
   * 但实测在**展开 More 之后**仍然会算出 4 条轨道（`112px 62.8px 51.2px 127.6px`，
   * 内容驱动的宽度）—— 也就是用户报的「展开的时候这些**只占用一列 width 没有 100%**」。
   * 与其去追"到底是哪一层在赢"，不如把这条钉死：**手机端 footer 就是一列**。
   */
  footer .row.g-4 { grid-template-columns: 1fr !important; gap: .55rem !important; }

  /*
   * ⚠️⚠️ **`grid-column: 1 / -1 !important` 这一条是必须的 —— 它就是那个 bug 的正解。**
   *
   * 用户报的：「展开的时候 bug —— 这些会**只占用一列 width 没有 100%**」。
   *
   * 真凶不是"列定义没改过来"，而是**旧的四栏布局还留着每一块的落线**：
   * 上面那条 4 栏版里给 `.footer-terms` 写过 `grid-column: 3 / span 2`、
   * 给品牌/付款写过 `1 / span 2`。把列改成 `1fr`（只有 1 条轨道）之后，
   * 这些**指向第 3、第 4 条线的落线**会逼浏览器**生成隐式轨道** ——
   * 实测：收起时看起来是单列（`366px`），一旦展开 More、那几列真的开始参与布局，
   * 计算值就变成 4 条轨道（`112px 62.8px 51.2px 127.6px`），
   * 于是每块只剩一格宽。
   *
   * 所以要把**所有**子元素的落线一起按死到"整行"（`1 / -1`）。
   * 用 `footer .row.g-4 > *` 通配而不是逐个点名 —— 逐个点就会漏掉
   * `.footer-collapsible`（它们带着 Bootstrap 的 `.col-6`，上面还有一条
   * `footer .row.g-4 > .col-6 { grid-column: span 1 }` 特异性更高）。
   * ⚠️ 它带 `!important`，是为了压过那条 `.col-6` 规则（(0,3,0)）。
   */
  /*
   * ⚠️⚠️ **手机端 footer 干脆不用 grid 了**（2026-10-12 第二次修）。
   *
   * 上一版把列改成 `1fr` + 把所有子元素按死到 `grid-column: 1 / -1`，
   * 单看"轨道数"是对的（`getComputedStyle` 回来 `366px`），
   * **但那四列导航展开时四块还是叠在同一个位置**（实测四个 `.col-6` 的
   * `getBoundingClientRect()` **完全一样**：`t:1044 b:1127 l:12 w:92`，
   * 正是用户报的「contact / link / category 这些都**重叠**了」）。
   *
   * 追不动是哪一层在跟 grid 打架 —— 直接换成**普通块级堆叠**：
   * 一列 div 从上往下排，`> *` 各占整宽，间距靠 `margin-top`。
   * 没有轨道、没有落线，也就没有"隐式轨道"这件事可以出错。
   *
   * ⚠️ `.footer-legal-col` 的 `display:contents` 保留 —— 块级上下文里它同样
   *    把两个孩子提升成兄弟，付款与合规照样各占一行。
   */
  footer .row.g-4 { display: flex !important; flex-wrap: wrap; gap: .55rem; }

  /*
   * ⚠️⚠️ `max-width` 与 `flex-basis` **都必须带 `!important`** —— 这一条是
   * "四列导航只占一格宽"的**真凶**（2026-10-12 第三轮才挖到）。
   *
   * 本文件 581 行还留着旧四栏布局那条：
   *     `footer .row.g-4 > .col-6 { flex: 0 0 25%; max-width: 25%; }`
   * 它的特异性是 (0,3,1)，**高过** `footer .row.g-4 > *` 的 (0,2,1) ——
   * 所以我把 `width:100%` 加上 `!important` 也没用，`max-width:25%` 照样把它
   * 卡在 `366 × 25% = 91.5px`（就是那个一直解释不通的 **92px**）。
   * 展开 More 时四块并排、每块 92px，看着就是"挤在一列里 / 重叠"。
   */
  /*
   * ⚠️ 这条**排除** `.footer-collapsible`（那四组导航）—— 它们要**四列一行**，
   *    由 `@media (max-width:991.98px)` 里 `#footerMore:checked ~ .row >
   *    .footer-collapsible` 那条按 `flex:1 1 0` 均分；这里只管把
   *    品牌块 / 付款 / 合规各自**占满一整行**（`flex:0 0 100%` 在 wrap 里
   *    保证它们各起一行，不会被同行的兄弟挤）。
   *
   * ⚠️⚠️ **第二条选择器不能省**：`.footer-legal-col` 是 `display:contents`
   *    （见上），它把 `.footer-pay-col` / `.footer-terms` **提升成 `.row` 的 flex 项**，
   *    但 DOM 上它们仍是 `.footer-legal-col` 的**孙子** —— `> *` 选不到。
   *    不显式给它们 `flex:0 0 100%` 的话，它们会按内容宽跟那四列挤在**同一行**
   *    （付款图标本来就不窄），"每块一整行"就散架了。
   *
   * ⚠️⚠️ 见上面那段：`.col-6` 那条 `max-width:25%`（(0,3,1)）曾经高过
   *    `footer .row.g-4 > *`（(0,2,1)），所以 `!important` 不能省。
   */
  footer .row.g-4 > *:not(.footer-collapsible),
  footer .row.g-4 > .footer-legal-col > * {
    width: 100% !important;
    max-width: 100% !important;
    flex: 0 0 100% !important;
    margin-top: 0 !important;
  }

  /*
   * ---- 顺序：店招 → **付款图标** → 四列导航 → 合规（用户 2026-10-12）----
   *
   * 用户原话：「**pay icon 和社媒的 icon 共用一样、紧跟着**排版在手机端 footer」。
   *
   * 社媒图标在 `.footer-brand-col` 里（店招那块的末尾），付款图标在
   * `.footer-pay-col` 里 —— 而 DOM 顺序是「品牌 → 四列导航 → 付款 → 合规」，
   * 中间隔着那四列，两排图标看着毫不相干。
   *
   * 用 `order` 把付款提到导航前面 ⇒ 店招+口号+社媒 → **付款** 紧挨着，
   * 两排图标同一套排法（都是 `space-between` 通栏，见上面那段）。
   * ⚠️ 这里必须是 **flex**（不能用 grid）：`order` 只在 flex/grid 的**子项**上生效，
   *    而上一版试过 grid，展开时四列导航会叠在一起（见上面那段 ⚠️）。
   * ⚠️ `.footer-legal-col` 的 `display:contents` 在 flex 里同样成立 ——
   *    它的两个孩子照样成为 flex 项、照样吃得到 `order`。
   */
  .footer-brand-col { order: 1; }
  .footer-pay-col   { order: 2; }
  .footer-collapsible { order: 3; }
  .footer-terms     { order: 4; }
  /* 一列之后每块之间靠 gap 就够，别再叠 margin。 */
  .footer-brand-col .brand-logo { margin-bottom: .4rem !important; }
  .footer-pay-col h6 { margin-bottom: .25rem !important; }
}

/* ---------------------------------------------------------------------------
   20. 手机 footer —— **已改成单列**（2026-10-12 用户改口径）

   这一节原来是"四列按内容宽度排、不换行"（2026-10-07 的口径）：

     用户：「尤其是 contact 各种换行，可以修改一下 4 列的宽度，不要换行每行都能完整显示」，
     于是做成 `grid-template-columns: auto auto auto auto` + `space-between` + `column-gap:1px`。

   ⚠️ **2026-10-12 用户把整个手机 footer 改成了一列**：
     「footer 口号 / operate、laws 这 3 块……改成**同一列内的分行显示**，
       然后 icons 也是行显示，就**没有 2 列了**，只在移动端这样更好看」。

   所以那四条 `auto` 轨道**整块删掉了** —— 它跟单列是直接冲突的
   （同样的 `footer .row.g-4` 选择器，且这一节在后面 ⇒ **它会赢**，
     单列规则写在上面那一节里就会被它悄悄盖掉，实测 computed 出来还是
     `363px 0px 0px 0px` 四条轨道）。
   现在列的定义只剩上面那条 `grid-template-columns: 1fr`，**别再往这里加轨道**。

   保留下面那条 `white-space: nowrap`：单列之后导航列（More 展开时）仍然是
   一格一行的短链接，`nowrap` 让它们不中途断词，无害且更整齐。
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  /* 列内文字不许再中途断词（`overflow-wrap:anywhere` 早就去掉了，这里再钉一次） */
  footer .row.g-4 > .col-6 a,
  footer .row.g-4 > .col-6 h6,
  footer .row.g-4 > .col-6 .contact-row > div { overflow-wrap: normal !important; white-space: nowrap; }
}

/* ---------------------------------------------------------------------------
   21. 详情页各面板：媒体/长文本不许撑破宽度（用户 2026-10-07）

   用户：「详情页内的 product size 如果有表格或者 image，要让他适配手机端的 width；
   其他模块也是，比如 product details、packing 等，都是适配宽度，width 不能让他超出了」。

   ⚠️ **表格**这里**不用再加** —— `table-expand.js` 的选择器已经是 `.p-info table`
      （含 Product Size / Packaging / FBA 三块），会把它们折成两列 + 展开按钮。
      上面第 5 节那条注释还专门警告过：再加 `.table-responsive` 会**嵌套出第二个
      滚动容器**。所以这里只补它没管的：**图片 / 内嵌页 / 长文本**。
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  /* 面板里的图一律不许超出容器（`.p-info img` 第 1 节已有，这里把 goods 页的
     其余包装也一起兜住 —— `.p-info-content*` 是各面板的公共外层）。 */
  .p-info img,
  .p-info-content img,
  .p-info-ai img,
  .p-info-content-pack img { max-width: 100% !important; height: auto !important; }

  /* 视频 / iframe（AI 正文里偶尔会有）同样收口。 */
  .p-info iframe,
  .p-info video,
  .p-info-content iframe { max-width: 100% !important; }

  /* 长串（链接、货号）不许把容器顶宽 —— 允许断词，但**只在必要时**。 */
  .p-info-ai,
  .p-info-content-detail { overflow-wrap: break-word; }

  /* `<pre>` 默认不折行，一条长命令就能把整页顶宽。 */
  .p-info pre { white-space: pre-wrap; overflow-wrap: anywhere; max-width: 100%; }

  /* 兜底：任何面板内的直接块级子元素都不许超出（宽表/宽图的最后一层保险）。 */
  .p-info-content > * { max-width: 100%; }
}

/* ---------------------------------------------------------------------------
   22. 触屏 topbar 面板：**用真 DOM 菜单**（不是 `<select>`），每行排满、不 word-break
   （用户 2026-10-08）

   用户：「blanks / links 点击出的 Shop by category / Shop by season 要一个 DOM 而不是
   select，**要带 icon**，要和 PC 端一样，**不是每行一个是每行都排满**！但是不 word break 换行」。

   所以：撤掉 `topbar.php` 里那套原生 `<select>`（commit e36734b），
   回到 `hover-menu.js` 那套 —— 点一下把**真** `.dropdown-menu`（就是 PC 上那个
   `.gtm-*` 面板）搬到 topbar 下方，它自带 icon（`.gtm-ico`）与数量（`.gtm-n`）。

   ✅ **"每行排满"和"不 word-break"本来就是 PC 那套的行为**，不用重写：
      · `.gtm-list { display:flex; flex-wrap:wrap }` —— 条目 `flex:0 1 auto`（按内容宽度），
        一行能放几个就放几个
      · `.gtm-name { white-space:nowrap; text-overflow:ellipsis }` —— 不折行、太长才省略号
   这里只把触屏的手感调好（点得着、看得清）。
   --------------------------------------------------------------------------- */

@media (max-width: 991.98px) {
  .dropdown-menu.dd-below .gtm-head { padding: .5rem .8rem .3rem; }
  .dropdown-menu.dd-below .gtm-list { gap: .35rem .5rem; padding: .3rem .8rem .8rem; }
  /* 条目放大到好点（原来 PC 上是给鼠标用的）。 */
  .dropdown-menu.dd-below .gtm-item { padding: .45rem .6rem; font-size: .86rem; }
  .dropdown-menu.dd-below .gtm-ico { font-size: 1.1rem; }
  /* 名字**不许折行**（用户明确要求）；够宽就整条显示，不够才省略号。 */
  .dropdown-menu.dd-below .gtm-name { white-space: nowrap; max-width: 60vw; }
  .dropdown-menu.dd-below .gtm-empty { padding: 0 .8rem .8rem; }
}

/* ---------------------------------------------------------------------------
   23. 折叠容器里的 <video>：手机浏览器会把它"拎"到页面顶部（用户 2026-10-08）

   用户：「about 的 video 在移动端会显示到页面顶部而不是 dom 内」。

   `/about` 上那几个视频是店铺 FAQ 挂在 Bootstrap accordion 里的
   （`partials/shop_faq.php`：`<video controls preload="metadata" playsinline>`）。
   markup 与样式都查过了 —— **没有任何规则给它定位**，它就该老老实实待在
   `.accordion-body` 里。

   那它为什么会跑到页面顶部？这是**部分手机内核的老毛病**：祖先被 `display:none`
   （折叠起来的那几段 `accordion-collapse` 就是）时，媒体元素**没有布局盒**，
   某些内核仍会为它建合成层、并把坐标算成文档原点（左上角）——
   于是它"浮"在页面最上面，看着完全不在 DOM 里。

   这里给"折叠态里的媒体"额外加一条 `visibility:hidden`（`display:none` 之外再收一道）：
   元素连绘制都被禁掉，就不会再有"浮到顶部"的东西。
   ⚠️ 只作用于**折叠态**（`:not(.show)`），展开后照常显示。
   ⚠️ 不要改成 `display:none !important` —— Bootstrap 的折叠动画靠它自己的
      `.collapsing` 过渡，硬压会破坏展开动画。
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  .accordion-collapse.collapse:not(.show) video,
  .accordion-collapse.collapse:not(.show) iframe,
  .accordion-collapse:not(.show) audio {
    visibility: hidden !important;
  }
}

/* ---------------------------------------------------------------------------
   24. 移动端 topbar：左右图标**统一尺寸**并排满一行（用户 2026-10-08）

   用户：「topbar 目前 blanks links blogs about quote 这几个 icon 比较大，
   右边的 icon 比如 message chat 这些比较小，统一成一样的大小，排满一行！」

   原来是两套尺寸（都是我按用户前几轮的要求加的，方向相反）：
     · 导航（Blanks/Links/Blog/About/Quote）1.38rem   ← 用户当时说"+20%"
     · 动作区（消息/聊天/账号…）.86rem               ← 当时按"+20%"换算出来的
   现在统一成 **1.2rem**（取中间，两侧都不至于撑破），并让导航**撑满剩余宽度、
   图标均分**（`justify-content: space-around`），整行看着是排满的。
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  .topbar-nav .topbar-link i,
  .topbar-actions .topbar-link i { font-size: 1.2rem !important; }

  /* 导航吃掉中间所有剩余宽度，图标在它内部均分 → "排满一行"。
     ⚠️ `flex-wrap: nowrap` 不能省：`topbar.php` 给这个 nav 挂了 Bootstrap 的
        `flex-wrap` 类，图标一大就可能折成两行（那就不是"一行"了）。 */
  .topbar-nav {
    flex: 1 1 auto;
    justify-content: space-around !important;
    flex-wrap: nowrap !important;
    overflow-x: auto;
  }
  .topbar-nav > .topbar-link,
  .topbar-nav > .dropdown { flex: 0 0 auto; }

  /* 动作区保持靠右、不参与拉伸。 */
  .topbar-actions { flex: 0 0 auto; }
}

/* ---------------------------------------------------------------------------
   25. 国产内核的"视频悬浮层"：<video> 在滚动进视口前被画到页面顶部（用户 2026-10-08）

   用户把症状说得很准，这条是**决定性线索**：

     「video 还是脱离 dom 在 page 最顶部……然后页面滚动到**那个 dom 位置**为止，
       视频又会回到 dom 内！」

   → 这不是"位置算错"，是**合成层**的行为：某些国产内核（百度浏览器这类）会把
     `<video>` 放进一个**独立的"视频层"**，而这一层在视频滚动进视口、触发几何重算
     之前，一直按**文档原点**绘制 —— 所以看着就是"浮在页面最顶部"。
     一滚到它该在的位置，它就归位了。Chrome / Safari 没有这套东西。

   ⚠️ 上一条（§23）让折叠态里的媒体 `visibility:hidden` —— 那**没打中**：
      `/about` 的**第一条 FAQ 默认就是展开的**，视频本来就在可见容器里。
     那条留着无害（折叠态确实不该画媒体），但真正的病根在这条。
   ⚠️ 我没有真机，**无法复现验证** —— 这是针对该内核行为的定向缓解，不是"我复现了再修"。
      如果仍不行，下一步是把视频改成"poster + 点一下才加载"（`preload="none"`），
      让视频层在用户点击之前根本不建（那一招对这类 bug 基本是终结性的）。

   两条缓解一起上（都只作用于手机）：
     1. `translateZ(0)` + `position:relative` —— 把它拉回**常规合成路径**，
        别让它单独成层（这是这类"悬浮层"最常用的解法）。
     2. 给它一个**先于元数据存在的盒子**：`min-height` + `object-fit:contain` +
        `background:#000` —— 元数据没到之前盒子就是 0 高，没有几何可算，
        那正是悬浮层乱画的窗口期。`contain` 保证竖屏视频只加黑边、不会被压扁
        （所以**不要**用 `.ratio` 那种固定 16:9 的写法，见 shop_faq.php 的注释）。
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  .accordion-body video {
    display: block;
    width: 100%;
    min-height: 180px;          /* 元数据没到也有盒子，见上面第 2 条 */
    object-fit: contain;
    background: #000;
    position: relative;         /* 第 1 条：回常规合成路径 */
    transform: translateZ(0);
    -webkit-transform: translateZ(0);
  }
}

/* ---------------------------------------------------------------------------
   26. 收尺寸（用户 2026-10-08）

   用户两条：
     「顶部的 icon quote 被遮挡了，说明整体 size 或者 margin 太大了？减小一点」
     「footer 的 we accept 和 pay icon 之间 gap 太大了；pay icon 多了 2 个换行了，减少尺寸」

   算过（付款图标宽度 ≈ **18.98 × 高度**：9 张卡各 1.56H + Apple/Google Pay 各 2.47H）：
   它所在的第 6 列在 1560px 容器里 ≈236px，减去 10 个 3px 间距 = 206px
   → 高度必须 **≤ ~10.8px** 才排得进一行。原来 13px（桌面）/12px（手机）都超了。
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  /* ---- topbar：再收一档，别让最后一个（Quote）被切掉 ---- */
  .topbar-nav .topbar-link { padding: .28rem .22rem; }
  .topbar-nav .topbar-link i,
  .topbar-actions .topbar-link i { font-size: 1.05rem !important; }

  /* ---- footer：付款图标一行放得下 ---- */
  img.pay-icon { height: 10px !important; }
  /* "We accept" 那行到图标的间距：原来是 h6 的 `mb-3`（1rem），太空。 */
  .footer-pay-col h6 { margin-bottom: .2rem !important; }
  .footer-pay-col .d-flex.align-items-center { gap: 2px !important; }
}


/* ---------------------------------------------------------------------------
   27. 手机端商品卡的主图：**不再裁掉**，改成"运镜那样"（用户 2026-10-08）

   用户：「首页 Custom Gifts for Every Occasion 内 card 的 image 几乎全部被遮挡了，
   修改成移动端 image 用**运镜那样的方式**让图在 dom 内飘，不然看不到完整内容」。

   原来：`.pcr-media { aspect-ratio:1/1 }` + 图 `object-fit:cover` ——
   商品图多是 **3:4 竖图**，塞进 1:1 的方框里只能**上下各裁掉一大截**，
   商品被裁得只剩中间一条 = 他说的"几乎全部被遮挡"。

   现在照合成器那套"运镜/填满"的做法：**同一张图模糊铺满 + 原图完整居中**
   （`site-mobile.css` 里管这叫 blur fill；视频管线里就是 `fit=blur`）。
     · 底图用 `--pcr-img`（partial 里写在 `.pcr-media` 上的内联变量）——
       **和 `<img>` 是同一个 URL → 浏览器不会多下载一次**。
     · `inset:-6px` + 外层 `overflow:hidden` 把模糊的软边切掉，四边不留白。
     · 原图 `object-fit:contain` → 整件商品都在，只是两边由模糊底填上。
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  .pcr-media { position: relative; background: var(--light); }

  .pcr-media::before {
    content: '';
    position: absolute;
    inset: -6px;
    background-image: var(--pcr-img);
    background-size: cover;
    background-position: center;
    filter: blur(12px);
    transform: scale(1.08);
    opacity: .95;
    z-index: 0;
  }

  /* 原图（含忽闪的第二层）抬到模糊底之上，并**完整显示**。 */
  .pcr-media > .psflash,
  .pcr-media > img { position: relative; z-index: 1; }
  .pcr-media > img,
  .pcr-media .psflash > img,
  .pcr-media .psflash > .psflash-b > img { object-fit: contain !important; }

  /*
   * ⚠️ **有忽闪第二层的卡：两层统一 `cover`**（2026-10-10，用户报的 A003）。
   *
   * 主图与带印花的封面**比例往往不同**（主图不一定正方形、封面一般 1:1）。
   * `contain` 下主图留边、封面填满 → 闪起来**顶部那块"漏"出来**，那行
   * 「You can customize this」也跟着错位/被压。两张都 `cover` 就严丝合缝了。
   * 模糊补底同时关掉（图已铺满，用不上）。
   * ⚠️ 只作用于挂了 `pcr-flash` 的卡（partial 里只在"有第二层"时加）——
   *    没有第二层的卡保持上面那套 contain + 模糊底（那是"别把商品裁掉"的口径）。
   */
  .pcr-media.pcr-flash::before { display: none; }
  .pcr-media.pcr-flash > img,
  .pcr-media.pcr-flash .psflash > img,
  .pcr-media.pcr-flash .psflash > .psflash-b > img { object-fit: cover !important; }
}

/* ---------------------------------------------------------------------------
   28. 首页三块：手机端 2 列 / 3 列（用户 2026-10-08）

   用户：
     · 「Why Pod-Sourcing … 这个模块在移动端显示成 **2 列**」
     · 「Ready to Start Your Custom Order? 的 **2 个 button** 也显示成 **2 列**」
     · 「Our Sales Team / Talk to a Specialist **每行 3 个**联系人」

   勾选项与销售卡的列宽在 markup 里改了（`col-6` / `col-4`）；这里补两处
   光靠 Bootstrap 栅格做不到的：事实卡网格、以及 CTA 两颗按钮并排不换行。
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  /* ---- Why Pod-Sourcing 右栏那几张"公司事实卡"：2 列 ---- */
  .why-facts {
    display: grid !important;
    grid-template-columns: 1fr 1fr;
    gap: .6rem .7rem !important;
  }
  /* 2 列之后每格窄了，去掉那条分隔线（两列之间画分割线会很乱），只留间距。 */
  .why-facts > div { padding-bottom: 0 !important; border-bottom: 0 !important; }
  .why-facts .fact-title { font-size: .82rem; }
  .why-facts .fact-sub { font-size: .72rem; line-height: 1.35; }

  /* ---- CTA 两颗按钮：并排 50/50，不换行 ---- */
  .cta-buttons { flex-wrap: nowrap !important; gap: .5rem !important; }
  .cta-buttons .btn {
    flex: 1 1 0;
    min-width: 0;
    font-size: .85rem;      /* 两个长标签（Get a Free Quote / Chat on WhatsApp）要放得下 */
    padding-left: .5rem;
    padding-right: .5rem;
    white-space: nowrap;
  }
}

/* ---------------------------------------------------------------------------
   29. 季节卡按钮、顶栏面板（用户 2026-10-08）

   用户：
     · 「Shop by Season 模块移动端 card 内**没有购买这些**？PC 端是 hover 出现的！」
     · 「tag list 目前有**垂直滚动条**，消除；按照内容设定高度；宽度移动端 **100%**；
        尽量一行放下**至少 2 个** tag；tag 排版不够紧凑」
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  /*
   * ---- 季节卡里的按钮 = 触屏上**一直显示** ----
   *
   * `product_card_row.php` 自己那段 `<style>` 里，那一层（`.pcr-reveal`）默认只露标题，
   * 靠 `.pcr-card:hover` / `:focus-within` 拉开，并有一条
   * `@media (hover:none){ .pcr-reveal{ transform:none } }` 兜底。
   *
   * ⚠️ **那条兜底在这些国产内核上不生效** —— 它们对 `(hover: none)` 的回报不可靠
   *    （和顶栏下拉是**同一个病**，见 §2 的说明）。所以这里用**宽度**兜一层：
   *    窄屏 = 触屏 = 按钮就该一直看得见。
   *
   * ⚠️ **光 `transform:none` 还不够**（2026-10-10，用户报的：post 的 "You may also
   *    like"、首页季节卡在手机上**只有图**，没有标题 / 价格 / 按钮）。
   *    那一层本身还是 `position:absolute; bottom:0`，卡片高度只是那张 **1:1 的方图**
   *    —— 于是它把图的下半截盖住，而标题/价格/按钮比图还高，多出来的部分被卡片的
   *    `overflow:hidden` **整块裁掉**。
   *    所以窄屏要**退成常规流**（与 `$alwaysOpen` 那一档同一套退法）：
   *    `position:static` + 取消位移 + 去掉那道用来遮下缘的阴影 ⇒ 卡片随内容长高，
   *    标题/价格/按钮完整显示在图**下方**。
   */
  .pcr-row .pcr-reveal {
    position: static;
    transform: none !important;
    box-shadow: none;
  }

  /*
   * ---- 「You can customize this」标签：跟着卡片一起缩小 ----
   * 用户 2026-10-10：「首页的 shop blanks 的 label（you can customize this）缩小字体」。
   * 那张标签是给 6 列桌面卡（~190px）定的 `.74rem`，手机上这排卡只有 ~111px，
   * 同样字号就显大、还会盖住图。字号/内边距各收一档。
   * ⚠️ 作用域是 `.pcr-row`（`product_card_row` 的样式根）—— 首页 Shop blanks、
   *    季节区、详情页底部关联行、blog 底部**一起生效**（它们都是同一张卡）。
   */
  .pcr-row .psflash-tagline {
    font-size: .62rem;
    padding: .12rem .38rem;
    left: .35rem;
    bottom: .35rem;
  }

  /*
   * ---- 「You can customize this」再收一档 + **绝不许换行**（用户 2026-10-12）----
   *
   * 用户原话：「手机端的 you can customize this label **减小一些**，
   *   对齐 card **最多只能是 card 的宽度**，手机端好多**换成了 2 行**」。
   *
   * 那是一张**绝对定位盖在商品图左下角**的标签：一旦折成两行，它就从"角标"
   * 变成"盖住半张图的一块蓝底"，而商品图正是这张卡最值钱的东西。
   *
   * 所以：
   *   · 字号再收一档（.62 → .54rem）；
   *   · `white-space: nowrap` **钉死一行**；
   *   · `max-width: calc(100% - .7rem)` —— 左右各留 .35rem（与 `left` 对齐），
   *     于是它**永远不可能比卡片宽**；
   *   · 万一某个语言/字号下真的塞不下，`ellipsis` 收尾（`overflow:hidden` 会
   *     把它**竖着切一半**，比省略号难看得多）。
   * ⚠️ 这条**不限 `.pcr-row`** —— 首页那批卡 + `goods/index.php` 的卡片
   *    （`.blank-card`，不带 `.pcr-row`）同一套标签，一起管。
   */
  .blank-card .psflash-tagline {
    font-size: .54rem;
    padding: .1rem .32rem;
    left: .35rem;
    bottom: .35rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: calc(100% - .7rem);
  }
}

@media (max-width: 991.98px) {
  /* ---- 顶栏那条面板：去掉滚动条、按内容高、整宽 ---- */
  .dropdown-menu.dd-below {
    max-height: none !important;   /* 原来是 60vh + overflow-y:auto → 一条竖滚动条 */
    overflow-y: visible !important;
    width: 100%;
    max-width: 100%;
  }

  /*
   * ---- 上下留白收一档（用户 2026-10-08）----
   *
   * 用户：「topbar blanks links 下拉的 Shop by category/season **上下 margin 还是 padding
   *   太空了**，减少 height 浪费」。
   *
   * 线上实测（390px，真机视口量的是计算值）：这块面板**上下一共 55px 纯空白**，四处叠的：
   *   ① 菜单自身 `padding: .85rem 1rem 1rem`（PC 那份，手机上没被覆盖）→ 13.6 + 16
   *   ② `.gtm-head` 的 `margin-bottom:.55rem` + `padding-bottom` → 8.8 + 4.8
   *   ③ 分隔线 1
   *   ④ `.gtm-list` 的 `padding-bottom` → 11.2
   *
   * 收到 ~34px（约 21px 的净省）：顶/底各留一点气口，**但不留白到"看起来断了一截"**。
   * ⚠️ 只动 `.dd-below`（= 只影响顶栏那条整宽面板）—— 搜索框旁那个品类菜单是
   *    **就地展开**（`dd-open`，见 hover-menu.v2.js），窄浮层里这点留白是必要的。
   */
  .dropdown-menu.dd-below { padding-top: .45rem; padding-bottom: .5rem; }
  /* head 的上下也一起收 —— 它上面那 8px padding 是 PC 上的取值 */
  .dropdown-menu.dd-below .gtm-head { padding: .25rem .6rem .3rem; margin-bottom: .4rem; }

  /* ---- tag 排紧凑些：一行尽量多放 ----
     一个 tag ≈ 图标(.95rem) + 名字 + 内边距；把三处都收一档，
     390px 上能一行放下 3 个短 tag / 2 个长 tag（原来 1-2 个）。
     ⚠️ 底部的 `.7rem` 已去掉 —— 菜单自己那份 padding-bottom 已经给了气口，
        两处叠着就是上面 ④ 那 11px。 */
  .dropdown-menu.dd-below .gtm-list { gap: .25rem .35rem; padding: .2rem .6rem 0; }
  .dropdown-menu.dd-below .gtm-item { padding: .3rem .45rem; font-size: .8rem; gap: .28rem; }
  .dropdown-menu.dd-below .gtm-ico { font-size: .95rem; }
  .dropdown-menu.dd-below .gtm-n { font-size: .6rem; padding: .02rem .28rem; }
}

/* ---------------------------------------------------------------------------
   30. FAQ 里的视频占位块（用户 2026-10-08）

   加载期**不放 `<video>`**（见 `partials/shop_faq.php` 的说明：某些国产内核
   只要元素进了 DOM 就把"视频层"建到文档原点去）。这里画一个占位块，
   点了才把 `<video>` 注入进去。
   --------------------------------------------------------------------------- */

.faq-video-holder {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;      /* 竖屏片源点开后按自己的比例显示，这里只占位 */
  background: #0d0f14;
  border-radius: .5rem;
  cursor: pointer;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
.faq-video-holder.is-mounted { aspect-ratio: auto; background: transparent; cursor: default; }
.faq-video-play {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .92);
  color: #111;
  font-size: 1.9rem;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .25);
}
.faq-video-holder .bi-play-fill { margin-left: 4px; }   /* 视觉居中：三角形右边空一点 */

/* ---------------------------------------------------------------------------
   31. 首页首屏：**只做 2 屏**（用户 2026-10-08）

   用户原话：「首页第一屏在手机端不要换这个多行！只做 2 屏」——左栏那几行
   （eyebrow / h1 / 副标题 / 3 个卖点）是第一屏，右栏那 4 张能力卡是第二屏
   （`home/index.php` 里右栏**不再** `d-none d-lg-block`）。

   要的是"**不要那么多行**"+"每一屏高度减少一半"，所以这里做三件事：

     ① **砍掉一层 padding** —— 标记上原来是 `header.hero py-5` + `.container py-5`，
        两层 3rem 叠着，手机上光这一项就吃掉小半屏；
     ② 字号/间距整体收一档（h1 从 `clamp(2.1rem,…)` 落到 1.4rem 左右）；
     ③ **3 个按钮同一行**（用户明说的）：`flex:1` 均分 + 最小宽度 0 允许压缩。
        窄屏把图标藏掉 —— 图标 + 文字并排时第三个按钮必然被挤下去，
        而"同一行"是用户的原话（1024px 以上图标照留，那里宽度够）。

   ⚠️ 断点用 **991.98px**：右栏在 `lg` 以下才堆到下面（`col-lg-7` / `col-lg-5`），
      所以 768–991 的平板也是"两屏"这个形态，规则得一起覆盖 ——
      只写 `max-width:767.98px` 的话平板会掉进一个没人管过的中间态。
   --------------------------------------------------------------------------- */

@media (max-width: 991.98px) {
  .hero { padding: .6rem 0 !important; }
  .hero > .container { padding-top: .6rem !important; padding-bottom: .6rem !important; }
  .hero .row { --bs-gutter-y: 1rem; }          /* g-5 在手机上留 3rem 的空档，太空 */
  .hero h1 { font-size: 1.45rem; line-height: 1.2; margin-bottom: .7rem !important; }
  .hero .hero-sub { font-size: .86rem; margin-bottom: .8rem !important; max-width: none; }
  .hero .eyebrow { font-size: .68rem; margin-bottom: 8px; letter-spacing: .08em; }
  /* 信任条的字号/间距见下面那段（要把它挤成**一行**，规则集中在那边） */

  /* 3 颗按钮**按自己的文字定宽**（用户 2026-10-11 第三版：「按钮和文本几乎要等宽度
     不然难看」）。
     ⚠️ 历史：先是"等宽"（`flex:1 1 0`）⇒ `View Personalize Designs` 超出框；
     然后"允许折行"⇒ 框比字宽太多、空一大片。现在改成**内容宽度**（`flex:0 1 auto`
     + `nowrap`）—— 因为按钮已经是**两行制**（`.hero-btn-l1` / `.hero-btn-l2`），
     每颗的宽度 = 它较长那行的宽度，框和字就贴合了。
     `.72rem` 下三颗加起来（≈110 + 70 + 50 + 内边距）仍在 390px 内，**不会换行**；
     `flex-wrap:wrap` 只是万一某机型更窄时的兜底。 */
  /* 移动端三颗按钮 = **横向一行、等宽，每颗"大字 + 小字"两行**
     （用户 2026-10-11 第四版：「我希望是横向排列一行！可以大文本 + 小文本」）。
     ⚠️ 这个位置用户来回改了四轮（等宽→长文案被截断；允许折行→框比字宽一大片；
        按内容定宽→三颗宽窄不一嫌丑；整宽竖排→又要横排回来）。
        **唯一同时成立的组合就是现在这套**：等宽（三颗一样宽、整齐）
        + 两行（大字一个词 / 小字其余）—— 高度靠 `align-items:stretch` 由 flex 拉齐，
        长文案放进**小字那一行**，真放不下时小字自己折行，也不会溢出按钮框。
     ⚠️ 图标在这档**藏掉**（腾宽度给文字）；桌面端图标照旧。 */
  .hero .hero-cta { flex-direction: row !important; flex-wrap: nowrap !important; gap: .35rem !important; margin-bottom: .9rem !important; align-items: stretch; }
  .hero .hero-cta .btn { display: block; flex: 1 1 0; min-width: 0; width: auto; font-size: .72rem; padding: .5rem .3rem; white-space: normal; line-height: 1.15; text-align: center; }
  .hero .hero-cta .hero-btn-l1 { display: block; font-size: 1.05em; font-weight: 700; }
  .hero .hero-cta .hero-btn-l2 { display: block; font-size: .72em; font-weight: 500; line-height: 1.1; }
  .hero .hero-cta .btn i { display: none !important; }
  /*
   * ⚠️ **不再让第三颗按内容宽度**（2026-10-08 用户反馈：「3 个按钮文本太长，长短和按钮
   *    宽度有点不匹配」）—— 上一版为了塞下 `Browse Ready Designs` 让第三颗 `flex:0 0 auto`，
   *    结果是 143/143/70 三颗**宽窄不一**，看着像排错了。
   *
   *    现在回到**三颗等分**，靠**把文案改短**来腾空间（`Browse Ready Designs` →
   *    `Browse Designs`，见 `home/index.php`）。三颗 ~119px 都放得下，也不截断。
   *    ⚠️ 以后要动这三颗的文案，先按这个预算量一下：`.72rem` 下 **~20 个字符**是上限。
   */
  .hero .hero-cta .btn i { margin-right: 0 !important; }   /* 同一行时靠 gap 就够 */

  /*
   * ---- 信任条挤成**一行**（用户 2026-10-08：「No MOQ for samples / Free design
   *      consultation / Reply within 24h 在移动端能减小文本成一行吗？」）----
   *
   * 三句加起来 ~59 个字符，390px 里一行放不下 —— 实测会**折成两行**（第三句掉下去）。
   * 挤进一行只能靠两件事：**藏掉三个对勾图标**（每个 ~14px + 间距）和**收字号**。
   * 图标藏掉不丢语义：三句本身就是承诺，前面那个 ✓ 只是装饰，而这一带本来就在首屏里、
   * 视觉上已经有 `China-Based Factory Direct` 那个徽章打头了。
   */
  .hero .trust-row { font-size: .68rem; gap: .15rem .55rem !important; flex-wrap: nowrap !important; }
  .hero .trust-row i { display: none; }

  /* 第二屏：4 张能力卡，2 列一行 ×2 行 */
  .hero .hero-cards > .row { --bs-gutter-x: .5rem; --bs-gutter-y: .5rem; }
  .hero-card { padding: .55rem .5rem; border-radius: .7rem; }
  .hero-card i { font-size: 1rem; margin-bottom: .1rem !important; }
  .hero-card .hc-t { font-size: .76rem; line-height: 1.25; }
  .hero-card .hc-s { font-size: .66rem; line-height: 1.25; }
}

/* 更窄的屏（iPhone SE 一档）—— ⚠️ 这两条**已废**：上一版是"三颗挤一行"才需要
   （藏图标腾宽度、字号收到 .68rem）。现在三颗是**整宽竖排**，空间绰绰有余，
   图标留着、字号不动（见上一段的 `.hero .hero-cta` 那组规则）。
   ⚠️ 别再在这里把图标藏掉或把字号压小 —— 那会让整宽按钮里只有半行字。 */
@media (max-width: 575.98px) {

  /*
   * ---- 底部 CTA band：长文案按钮在手机上**超出按钮框**（用户 2026-10-08）----
   *
   * 症状：`Bulk or custom? Get a quote` 在 390px 下比按钮还宽，字漏到框外。
   * 原因：那几颗是 `btn-lg`（1.25rem + 左右各 2rem 内边距），25 个字符 ≈ 400px，
   *      而容器只有 ~366px；`d-flex flex-wrap` 只会让**按钮**换行，不会让**按钮变窄**
   *      （flex 项默认 `min-width:auto`，压不到内容宽度以下）。
   *
   * 修法：收字号/内边距 + `max-width:100%`（允许压到容器宽）+ `white-space:normal`
   *      （长文案在按钮内部折成两行，而不是漏出去）。**整页级**，所以首页与
   *      `/about` 两个 CTA band 一起生效 —— 它们都是 `.cta-band`。
   */
  .cta-band { padding: 1.4rem 1rem !important; }
  .cta-band .btn {
    font-size: .82rem; padding: .5rem 1rem;
    max-width: 100%; white-space: normal;
  }
}

/* 信任条：3 列一行，别再堆成三行（用户：「3 列一行」） */
@media (max-width: 991.98px) {
  /*
   * ⚠️ 这张卡是**上移压住 hero** 的（桌面 -64px），所以它下面那块内容的底部会伸进
   *    hero 的范围内。首屏底部加了「Who we print for」那排 chip 之后，`-28px` 正好
   *    **压住最后一排 chip**（B2a 手机截图实测）。
   *    两边各让一步：chip 留出下边距、卡片少上移一点。
   */
  .stats { margin-top: -12px; }
  .who-we-print-for { padding-bottom: .5rem; }
  .stats .card { padding: .7rem .55rem !important; }
  .stats .row { --bs-gutter-x: .35rem; }
  .promise-title { font-size: .82rem; }
  .stat-label { font-size: .64rem; line-height: 1.3; }
}

/* ---------------------------------------------------------------------------
   32. 首页两处固定高度的图（2026-10-10 移动端修复）

   · Popular Categories 的拼贴图 `.cat-media` 在 `site.css` 里是**写死 `height:200px`**
     的一张横条（桌面 3 列时卡宽 ~230px，200 高还算方）。手机端卡片变 `col-6`
     （2 列），卡内宽只剩 **~143px** —— 于是 143×200 变成一张**竖图**，2–3 张图各占
     1/2、1/3 宽之后就是一根根细长条（`object-fit:cover` 把它们裁得面目全非）。
     `site-mobile.css` 之前**没有任何一条**管过它。改成定宽比：宽高比 3:2（143×95），
     条带短一半，裁切也不那么极端。

   · Blog 预览卡无封面时的兜底 `.blog-thumb` 是 `height:170px`，而**有**封面的
     `img.card-img-top` 被 §13 压成 120px（`!important`）—— 同一行里带封面与不带封面的
     卡高矮不齐。把它一起收进 §13 那一档（见上面 `.blog-card img.card-img-top`）。
   --------------------------------------------------------------------------- */

@media (max-width: 575.98px) {
  .cat-media { height: auto; aspect-ratio: 3 / 2; }
  .blog-card .blog-thumb { height: 120px !important; }
}

/* ---------------------------------------------------------------------------
   35. page-head 里的下拉浮层：手机端**贴着容器整宽**（用户 2026-10-10）

   用户原话：「home-links 面包屑的 categories 的 shop by category 部分被遮挡了，
   是不是没考虑手机 width？」

   病根：这两颗（`/links` 面包屑旁的 Categories、右端搜索框那颗）也是
   `.dropdown-hover`，但 `hover-menu.js` 只把**顶栏里**的那些搬到 topbar 下方
   （`bar.contains(d)`）—— 在 `.page-head` 里的是**就地展开**：相对那颗按钮 `left:0`。
   而 `.goods-tag-menu` 的宽度是 `min(96vw, 1100px)`，按钮只要不在屏幕最左边
   （面包屑前面还有 Home / Links 两段），「左偏移 + 96vw」就超出视口，
   右边缘被 `html,body{overflow-x:clip}` **裁掉** —— 就是"被遮挡"。

   做法：窄屏把包含块从**按钮**换成**容器** ——
     · `.page-head .container` 给定位上下文；
     · `.page-head .dropdown` 退回 `static`（Bootstrap 默认是 `relative`），
       于是菜单的包含块变成那个容器；
     · 菜单 `left:0; width:100%` ⇒ 从容器左缘起、占满容器宽，永远不横出屏幕。
   `!important` 是给 `hover-menu.v2.js` 可能写在元素上的内联定位留的余量。
   ⚠️ 只作用于 `.page-head` —— 顶栏那套（`.dd-below`）、搜索/品类菜单在别处的
      用法都不受影响。
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  .page-head .container { position: relative; }
  .page-head .dropdown { position: static; }
  .page-head .dropdown-menu {
    left: 0 !important;
    right: auto !important;
    width: 100% !important;
    max-width: 100% !important;
    max-height: 60vh;
    overflow-y: auto;
  }
}

/* ---------------------------------------------------------------------------
   37. 手机端两处跟"顶部被遮挡 / 用不了"有关的收口（用户 2026-10-11）

   · 聊天窗的 **Screenshot 按钮**：它走的是 `getDisplayMedia`（屏幕捕捉），
     **手机上根本没有这个 API** → 点了只会弹一句 "Screenshot is not supported here"。
     一个只会报错的按钮不该摆在那儿，窄屏直接藏掉（桌面保留）。
   · **全局 toast 的位置**：`partials/toast.php` 钉的是 `top-0`。2026-10-11 曾把窄屏
     挪到**底部**（理由："手机顶部是浏览器地址栏，怕被压住"）。用户 2026-10-12 否掉了：
       「手機端添加成功的 toast message 爲什麽在 bottom 而不是窗口的 top 位置？」
     ——不要挪到屏底，**留在顶部**；真正要防的是刘海/状态栏把 toast 盖住，
     那个用 `safe-area-inset-top` 往下让出高度就够了，不必整块搬到底部。
     ⚠️ `z-index` 是 9999（markup 上的内联），压在购买栏/悬浮按钮之上，不会被盖。
   --------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
  #psAttachShot { display: none !important; }

  #globalToast {
    top: calc(12px + env(safe-area-inset-top)) !important;
  }
}

/* ---------------------------------------------------------------------------
   38. 首页小卡上的按钮太高（用户 2026-10-11，第 1/2/3 条）

   §1 那条 `min-height:44px` 是给**触控**定的（整个 ≤991.98 都算拇指设备），对
   `/links`、`/blanks` 那种大卡没问题。但首页这两排用的是 **3 列小卡（~111px）**
   —— 两颗按钮上下堆叠，44px × 2 + 间距 ≈ 90px，几乎比卡片本身还高，
   多出来的部分被卡片的 `overflow:hidden` **裁掉**，看着就是"按钮不完整"。

   小卡不需要 44px 的触控高度：30px 仍然好点，而且两颗加起来才 60px。
   --------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
  .pcr-row .blank-card-actions .blank-card-btn {
    min-height: 30px;
    padding: .25rem .2rem;
    line-height: 1.15;
  }

  /*
   * ---- CTA band：短按钮被长按钮"带高"（第 3 条）----
   * `Ready to print your design?` 那两颗：右边 `Bulk or custom? Get a quote` 文案长、
   * 在窄屏折成两行，而 flex 默认 `align-items:stretch` —— 左边那颗
   * `Start Designing` 明明只有一行，却被拉成两行高（用户报的"高度太高，是被
   * bulk or custom 带的"）。让每颗**按自己的内容定高**就对了。
   */
  .cta-buttons { align-items: flex-start; }
}

/* ---------------------------------------------------------------------------
   移动端**不显示面包屑导航**（用户 2026-10-11：「移动端 不要显示面包屑导航！」）

   `partials/page-head.php` 那条细页头 = `Home > 父级 > 当前页` 一行痕迹 + 当前页名。
   手机上这一行既占高度又基本没用。

   ⚠️ **只藏痕迹，不藏当前页名**：这个 partial 是 24 个页面**唯一的 `<h1>`**
      （见它文件头的说明），整块 `display:none` 会把标题一起弄没 —— 那是把
      "省一行"换成了"这一页没有标题"，得不偿失。

   实现：非 `.active` 的 crumb 全藏；`.active` 那条前面的分隔符（Bootstrap 的
   `.breadcrumb-item + .breadcrumb-item::before` 那个 `/`）也一起藏，免得剩下一个孤零零的斜杠。
   ⚠️ 桌面（≥768）**一个字节都不变**。
   --------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
  .page-head .breadcrumb > .breadcrumb-item:not(.active) { display: none !important; }
  .page-head .breadcrumb > .breadcrumb-item.active::before { display: none !important; }
}

/* ---------------------------------------------------------------------------
   移动端**不显示悬浮侧栏**（用户 2026-10-11：「悬浮的 sidebar 绿绿泡泡和蓝泡泡
   整个 sidebar 在移动端不显示」）。

   ⚠️ 那条蓝泡泡是**聊天入口**：widget 运行时把 `.ps-chat` 搬进 `#floatChatSlot`
      （在 `.float-bar-wrap` 里面）—— 所以这一藏，手机上就没有"右下角开聊天"这颗了。
      手机端**能开聊天的入口**剩：首页/关于页首屏那颗 `Chat With Us`、联系页那颗、
      商品/订单页的 Chat 按钮。
   ⚠️ "回到顶部"（`.float-top`）**在 wrap 之外**（见 floating-bar.php 文件头那段），
      不受影响，手机上仍然是常驻的。
   --------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
  .float-bar-wrap { display: none !important; }
}

/* ---------------------------------------------------------------------------
   手机端：订单列表**表格折成卡片**（P4）

   原来 `/orders`、`/seller/orders` 是 `table-responsive` **横向滚**的表格 ——
   手机上要左右拖才看得到 Total / Status，列名还得靠记。折成卡片后每单一块，
   列名由 `td::before` 从 `data-label` 印出来（**与列定义同源**，
   见 `partials/order_list_table.php` 的注释）。

   ⚠️ 只在 ≤767.98px 生效，桌面那个表格**一个字节不变**。
   ⚠️ `white-space:normal !important` 是必须的：markup 里给日期/件数那几格写了
      `style="white-space:nowrap"`（桌面表格里是对的），卡片模式下会把那一行撑出去。
   --------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
  .order-list-table thead { display: none; }
  .order-list-table,
  .order-list-table tbody,
  .order-list-table tr,
  .order-list-table td { display: block; width: 100%; }

  .order-list-table tbody tr { border: 1px solid var(--border); border-radius: var(--r-md); padding: var(--sp-2) var(--sp-3); margin-bottom: var(--sp-2); background: #fff; }
  .order-list-table td { border: 0; padding: .18rem 0; white-space: normal !important; }

  /* 列名从 `data-label` 印出来 —— 与 `$cols` 同源，改列不会再对不上。 */
  .order-list-table td[data-label]:not([data-label="Order"])::before {
    content: attr(data-label) ": ";
    color: var(--muted);
    font-size: var(--fs-xs);
  }
  /* 「Order」那格 = 卡片标题：订单号在上、下面一条虚线跟内容分栏。 */
  .order-list-table td[data-label="Order"] { padding-bottom: .3rem; border-bottom: 1px dashed var(--border); margin-bottom: .3rem; }
}

/* ---------------------------------------------------------------------------
   §「板块名 + 搜索框」那一条页头 —— 手机上**整行藏掉**（用户 2026-10-12 第 5 条）

   「移動端是不是可以整行都不顯示 每個板塊的標題+search input 這一行，
     比如 link+search、blank+search、blog+search」

   手机上这条既占首屏、又把商品列表往下推，而搜索本来也不是移动端的主路径
   （顶栏那颗搜索一直都在）。桌面那半在 `site.css`。

   ⚠️ 只藏**这一种**页头（`ph-has-search`）—— 别的页头里的标题是页面主角，不能跟着没。
   --------------------------------------------------------------------------- */
@media (max-width: 991.98px) {
  .page-head .ph-has-search { display: none !important; }
}

/* ---------------------------------------------------------------------------
   § 手机端页脚：中间四列折起来（用户 2026-10-12 第 4 条）

   「手機端 footer 是不是也能摺叠一部分？實際上 footer 不是給用戶看的，
     是為了合規設置的，看起來非常的冗餘！less is more」

   折 Company / Blank / Occasions / Contact 这四列（`.footer-collapsible`）；
   品牌列与合规列（运营主体 / 备案号 / 管辖法 / 付款方式）**始终露出** —— 那是
   收单机构的合规要求，不是"冗余"。

   开关是 checkbox hack：`#footerMore`（footer.php 里那个 `d-none` 的 input）
   `:checked ~ .row .footer-collapsible` —— 不开 JS，也不会首屏闪一下。
   --------------------------------------------------------------------------- */
@media (max-width: 991.98px) {
  /* 默认收起；勾上就展开。`~` 能跨过中间的注释/文本节点，只认元素兄弟。 */
  #footerMore:not(:checked) ~ .row .footer-collapsible { display: none; }

  /*
   * ---- 展开后：四组导航 **四列一行**（用户 2026-10-13）----
   *
   * 用户原话：「footer company blank occasions contact 展开没有排在一行内而是排成了
   *   一列导致右边大量空」。
   *
   * 用 `flex:1 1 0`（**不是** `25%`）：`.row` 上有 `gap`，四个 `25%` 的 basis 加上三条
   * gap 会溢出 100%，flex 就会换成 2+2。basis 归 0 则由 flex-grow 把这一行**精确**切成四份。
   *
   * ⚠️ 用 **id 选择器**起头，特异性高过 `.col-6`（`footer .row.g-4 > .col-6` 是 (0,3,1)，
   *    这里 `#footerMore:checked ~ .row > .footer-collapsible` 有 id ⇒ (1,2,0)）——
   *    这正是当年"92px 真凶"要压的那条，别再写回 `max-width`。
   * ⚠️ 写在 ≤991.98 里（不是 ≤767.98）：768–991.98 平板档今天这四组是 2×2，
   *    用户要的"一行"在平板也要成立 ⇒ 放在两者都吃得到的这层。
   */
  #footerMore:checked ~ .row > .footer-collapsible {
    flex: 1 1 0 !important;
    min-width: 0;
    max-width: none !important;
  }
  /*
   * ⚠️ 四列一行后每列只剩 ~85px，`@media (max-width:767.98px)` 那条
   *    `footer .row.g-4 > .col-6 a/h6 { white-space: nowrap }` 会把长词顶出列外。
   *    这一块在后面、且同带 `!important` ⇒ 由它赢。
   */
  #footerMore:checked ~ .row > .footer-collapsible a,
  #footerMore:checked ~ .row > .footer-collapsible h6,
  #footerMore:checked ~ .row > .footer-collapsible .contact-row > div {
    white-space: normal !important;
    overflow-wrap: anywhere;
  }

  /*
   * ---- 收起时**连口号与合规文本一起收**（用户 2026-10-12）----
   *
   * 用户原话：「footer **less 的时候**，口号和 operate、terms、law 这些**也收起来**；
   *   展开的时候 bug —— 这些会只占用一列 width 没有 100%」。
   *
   * 收起态只留三样：**店招、社媒图标、付款图标**（与用户 2026-10-12
   * 早些时候那句「只露出社媒 icons 和 pay icons」一致）。
   *
   * ⚠️⚠️ **合规风险，上线前请确认**：本仓 `partials/footer.php` 里一直写着
   *    「运营主体、备案号、管辖法、付款方式是**收单机构要求必须露出**的，
   *      藏起来等于给自己找合规麻烦」。现在按用户要求把它们折进 More 了 ——
   *      点一下 More 就能看到，但**默认不可见**。若收单机构（Airwallex）的
   *      审核口径要求 footer 常驻，就把下面这行删掉、回到"合规始终露出"。
   *
   * ⚠️ 只收 `.footer-terms`（operate + laws 那两块）。
   *
   * ⚠️ **品牌那句 tagline 不收**（用户 2026-10-12 当天改回来：
   *   「折叠的时候**不要折叠**这一句 No minimum order — buy custom-printed gifts…」）。
   *    上一版连它一起收了，用户看完觉得"连自己是谁都没说清楚"。
   */
  #footerMore:not(:checked) ~ .row .footer-terms { display: none; }

  /*
   * ---- 社媒图标 / 付款图标：各占**一整行铺满**（用户 2026-10-12）----
   *
   * 用户原话：「移动端的 **icon 社媒的让他一行满**，**pay 的也让他一行满**，
   *   你调整 width / icon 大小」。
   *
   * 两排原来都是"挤在左边的一小撮"（`d-flex gap-3` 贴着左边缘），
   * 右边留一大片空 —— 在一列布局里看着就是没排完。
   * 改成 `space-between` + 通栏：五颗社媒、九个付款标识各自**均分整行**。
   *
   * ⚠️ 图标尺寸**一起放大**，否则"铺满"只是把空隙拉大、图标还是小点点。
   *    用 `flex:0 0 auto` 保住图标本身不被压扁（`space-between` 只分剩余空间）。
   */
  /*
   * ---- 社媒图标：**靠左一簇**，不再拉满一行（用户 2026-10-13）----
   *
   * 用户原话：「footer 的社媒 icon **太空了**，一行靠右！」
   *
   * 上一版按"一行满"做成了 `space-between` —— 5 颗图标摊在 366px 上，
   * 每两颗之间 ~86px 的空隙，看着是**五个孤零零的点**，不是一排图标。
   * 现在改成**靠左、固定间距**（间距用 rem 而不是均分），图标本身放大一点补回来。
   */
  .footer-brand-col > .d-flex {
    justify-content: flex-start !important;
    width: 100%;
    gap: 1.35rem !important;
  }
  .footer-brand-col > .d-flex > a { flex: 0 0 auto; font-size: 1.5rem; }

  /*
   * ---- 付款图标：要铺满一行，但规则得打在**里面那一层** ----
   *
   * ⚠️ 这里有两层 `d-flex`：`footer.php` 包了一层，`pay-methods.php` 自己又包了一层
   *    （`<div class="d-flex flex-wrap align-items-center" style="gap:.45rem">` + 若干 `<img>`）。
   *    第一版把规则打在 `.footer-pay-col .d-flex` 上 —— 命中的是**外层**，而外层
   *    只有一个孩子（内层那个 div），`justify-content` 于是完全没效果。
   *    现在打在**内层**（`.d-flex .d-flex`）。
   *
   * ⚠️ `!important`：`site.css:1239` 有一条 `.footer-pay-col img.pay-icon{height:10px !important}`
   *    把它钉死在 10px（2026-10-07「icon 减小 50%」留下的）—— 手机端要放大就得同样带。
   * ⚠️ 高度从行内 `style="height:26px"` 来，压它也只能靠 `!important`。
   */
  .footer-pay-col .d-flex .d-flex {
    justify-content: space-between !important;
    width: 100%;
    gap: .3rem !important;
  }
  .footer-pay-col img.pay-icon { height: 18px !important; flex: 0 0 auto; }

  .footer-more-btn {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    margin-top: .75rem;
    padding: .4rem .9rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    font-size: .85rem;
    color: var(--accent);
    font-weight: 600;
    cursor: pointer;
    user-select: none;
    background: #fff;
  }
  /* 两段文案按勾选状态互换 —— 与 `display:none` 那半同一份状态。 */
  #footerMore:checked ~ .footer-more-btn .fm-more { display: none; }
  #footerMore:not(:checked) ~ .footer-more-btn .fm-less { display: none; }
}

/* ---------------------------------------------------------------------------
   § 购物车右侧「Order Summary」手机端（用户 2026-10-12 第 10 条）

   「移動端的 order summary list 內容太多太擁擠導致 css 錯亂」

   两处病根，都在窄屏才发作：
     ① `position:sticky; top:1rem`（写在内联样式里）—— 桌面端它跟着长清单很好用，
        可手机端这一列是**通栏**（col-12），sticky 会把它钉在顶栏底下、
        和页面内容互相盖住，看着就是"CSS 錯亂"。
     ② 每行是 `d-flex justify-content-between` + `white-space` 默认 —— 右侧那格
        偏偏有 `Choose a delivery address at checkout` 这种长句，它不换行就**把左边
        的标签挤扁/挤出容器**。

   所以：手机端取消 sticky；行内允许换行、值右对齐并允许它自己占满下一行。
   ⚠️ 只作用于 `.cart-sum-card`（购物车那个框）—— 结算页另有自己的结构。
   --------------------------------------------------------------------------- */
@media (max-width: 991.98px) {
  .cart-sum-card { position: static !important; }

  /* 行内允许折行；值那一格被挤到时**整格落到下一行**，而不是把标签压扁。 */
  .cart-sum-card > .d-flex { flex-wrap: wrap; gap: .15rem .75rem; }
  .cart-sum-card > .d-flex > span:last-child { margin-left: auto; text-align: right; min-width: 0; }
  /* 超过一屏宽的长句（地址提示那种）必须能断，否则又把容器顶破。 */
  .cart-sum-card > .d-flex > span:last-child { overflow-wrap: anywhere; }
}

/* ---------------------------------------------------------------------------
   § 购物车**条目行**（`.cart-entry`）手机端（用户 2026-10-14 第 5 条）

   原话：「ordersummary page 的 unit price / qty input 后面的**按钮超宽度了**，
          行内容要简化！」（`cart/index.php:176` 那条 `.cart-entry`）

   那一行塞了 6 样：勾选框 · Personalize 徽章 · `Unit $x` · Qty 输入框 · 小计 ·
   删除按钮。手机上 390px 放不下，而 `Unit $x` / 小计那两格都带 `text-nowrap`
   （不肯缩），于是**删除按钮被顶出容器**。它此前**一条专用 CSS 都没有**，
   全靠 Bootstrap 工具类，所以窄屏才会散。

   简化：**藏掉 `Unit $x` 那一格** —— 单价 × 件数 = 小计，小计就在它右边，
   这一格信息量最低。⚠️ 只是不显示，DOM 与金额一个都没动。
   其余格子允许收缩，删除按钮**钉在行尾**（`margin-left:auto`），不再被顶出去。
   --------------------------------------------------------------------------- */
@media (max-width: 991.98px) {
  /*
   * ⚠️ 选 `.text-secondary.text-nowrap` 而不是 `.text-nowrap`：小计那颗是
   *    `fw-semibold text-nowrap entry-sub`（**没有** `text-secondary`），
   *    这个组合只命中单价那一颗 —— 别把小计也藏了。
   */
  .cart-entry > span.text-secondary.text-nowrap { display: none; }

  /* 剩下的格子都要能缩（`min-width:auto` 会宁可撑破容器也不缩，本仓踩过多次）。 */
  .cart-entry > * { min-width: 0; }

  /* 删除按钮钉在行尾；数量输入框收窄一点，别和它抢宽度。 */
  .cart-entry .cart-del { margin-left: auto; }
  .cart-entry .cart-qty { width: 58px !important; }
}

/* ---------------------------------------------------------------------------
   § 行内**左滑删除** —— 购物车页 + buy-now 结账页**共用**（2026-10-15 / 10-16）

   用户：「手機端的刪除可以通過滑動來實現 … **ordersumary 也可以通過滑動代替刪除按鈕**」
        「购物车：左滑一行 → 露出 [Back][🗑] **要带上 quantity 行一起**！」
        「buynow ordersummary 手机端也要：左滑一行 → 露出 back delete，
          **删除原来行内的删除按钮**！」

   ## 契约（`row-swipe.js`）

     · `.swipe-row`  —— 行容器（手势落点 + 操作条的定位基准）
     · 直接子级里一颗 `.swipe-del` —— 真正干活的删除控件（表单或按钮都行）
     · 操作条 `.swipe-acts` 由 JS **露出时才建**，`[🗑]` 是 `realDel.click()` 的转发

   口径与定制面板那套一致（`personalize-editor.css` 的 `.psbuy-group.is-row-open`）：
   行内容 `visibility:hidden`（**不是** `display:none` —— 占位留着，行高不变、页面不跳）、
   操作条 `position:absolute; inset:0` 铺满整行 ⇒ **数量输入框也在覆盖范围内**
   （这正是"带上 quantity 行一起"那条要的）。

   ⚠️ 藏删除按钮这条**必须**带 `html.row-swipe-ready`：那个类由 `row-swipe.js`
      在**自己跑起来之后**加上。万一脚本没加载到（网络/缓存），删除按钮照旧在 ——
      不能因为一个增强手势让用户**彻底删不掉东西**。
   --------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
  /* 操作条要 absolute 铺满这一行 ⇒ 行得是定位上下文。 */
  .swipe-row { position: relative; }

  /*
   * ⚠️⚠️ **基础态必须 `display:none`**（2026-10-16 补，上一版漏了这条）。
   *
   * 操作条 `.swipe-acts` 是 `row-swipe.js` **注入**到行里的一个 div，只有
   * `.is-swipe-open` 时才该显示。上一版**只写了"露出时"的那条规则**，
   * 没有基础规则 ⇒ 那个 div 就是个普通 block：
   *   · 第一次滑开之后**永远留着**（Back 收不回、内容也回不来）；
   *   · 浏览器**后退返回**（bfcache 把 DOM 原样还原，包括注入的 div 和 `is-swipe-open`）
   *     还挂着一条红的 `[Back][🗑]`。
   *   用户报的「back delete 顯示一次之後就不 hide？返回之後也顯示？」就是它。
   *
   * `!important` 在下面那条"露出时"的规则里 —— 特异性也更长，所以仍然压得住。
   */
  html.row-swipe-ready .swipe-row > .swipe-acts { display: none; }

  /* 手机上不再常驻删除按钮，一律靠左滑（仅当手势脚本就位）。 */
  html.row-swipe-ready .swipe-row > .swipe-del { display: none; }

  /* 露出的那一下：行内容藏起来、操作条铺满。 */
  html.row-swipe-ready .swipe-row.is-swipe-open > * { visibility: hidden; }
  html.row-swipe-ready .swipe-row.is-swipe-open > .swipe-acts {
    display: flex !important;
    visibility: visible;
    position: absolute;
    inset: 0;
    align-items: center;
    justify-content: space-between;   /* [Back] 靠左、[🗑] 靠右 —— 别让"删"贴着"返" */
    padding: 0 .6rem;
    margin: 0;
    background: #fff5f5;              /* 淡红：这是破坏性动作 */
    border-radius: inherit;
    z-index: 3;                       /* 盖住行内的图/徽章 */
  }
  html.row-swipe-ready .swipe-row.is-swipe-open > .swipe-acts > button { flex: 0 0 auto; }
}

/* ---------------------------------------------------------------------------
   § 手机端：**所有弹窗 = 底部弹层**（贴底 / 通栏 / 上方留 20%）

   起源是分享与订阅那两个弹窗（用户 2026-10-12 §5 定的形状），当天泛化到全部：

   用户原话：「手机端的**各种 modal 都修改成** sharemodal 订阅modal 这种
   bottom width 对齐 顶部留空的」——
   并说明了动机：「主要的套的是底部对齐 width 同宽 顶部留空，**方便点顶部空白**（关掉）」。

   ## 做法

     · `.modal-dialog` → `display:flex; align-items:flex-end`：内容是**贴底**的。
     · `.modal-content` → 通栏 + `max-height:80vh`：上面那 20% 就是留出来给拇指
       "点空白关掉"的那一块（点它落到的是 Bootstrap 自己的 backdrop，本来就能关）。
     · 顶部两角圆、底边贴屏幕。

   ## 为什么用 `align-items:flex-end` 而不是 `position:fixed; bottom:0`

   `partials/modal.php` 的外壳是标准 Bootstrap 结构（`.modal` 是滚动容器、
   `.modal-dialog` 在里面）。把 dialog 改成 `position:fixed` 会把它**摘出文档流**，
   `.modal` 的高度归零 —— 滚动、`modal-dialog-scrollable` 那些行为就全废了。

   而 dialog 本来就被设成 `display:flex`（`.modal-dialog-centered` 干的）或
   普通 block。给出 `display:flex + align-items:flex-end + min-height:100%` 之后
   两种情况都能贴底，其余行为一个字不动。

   ## ⚠️ 三处 `!important` 不能省

   `#buyModal .modal-dialog{margin-top:2vh;margin-bottom:2vh}`、
   `#buyModal .modal-dialog-centered{align-items:flex-start}`（`products/index.php` 内联）、
   `#personalizeModal/#goodsBuyModal .modal-dialog{…}`（`personalize-editor.css`）
   全是 **id 选择器**，特异性高过这里任何写法 —— 不加 `!important` 就是
   "写了等于没写"，而且**不报错**。（改完必须读 `getComputedStyle` 验，
   别只看源码 —— 本仓这条规矩已经写进好几处注释了。）

   ## ⚠️ 作用域：`body:not(.app-shell)`

   公开页是裸的 `<body>`（`partials/header.php:246`），而**后台**（`admin/layout.php`）
   与**买家/卖家后台**（`backend/layout.php`）都是 `<body class="app-shell">`。
   那两个一律**不动**：它们是密集表单/数据页，弹层化只会碍事。
   （注：`site-mobile.css` 现在三个布局**都在引**，所以这里必须显式挡掉，
     不能再指望"后台不引这个文件"。）

   ## ⚠️ 唯一的例外：图片放大

   `imgZoomModal`（`partials/image_zoom.php`）加了一颗 `.modal-dialog-nosheet` ——
   它是个**看图的 lightbox**，贴到底部反而怪。这是刻意的例外，不是漏了。
   --------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
  body:not(.app-shell) .modal .modal-dialog:not(.modal-dialog-nosheet) {
    display: flex !important;
    align-items: flex-end !important;   /* ← 这一行就是"贴底" */
    margin: 0 !important;               /* 通栏：左右不留缝，宽度对齐手机屏 */
    /*
     * ⚠️ `max-width:100% !important` 必须有 —— `#buyModal` 的 dialog 上写着
     *    **内联** `max-width:min(1370px,96vw)`（`products/index.php`），
     *    390px 下算出来是 374px，于是通栏变成"左右各留 8px"。
     *    内联样式只能靠 `!important` 压。窄屏下 Bootstrap 给 `.modal-lg/xl`
     *    定的那些 px 上限本来就都不生效，所以这里压成 100% 是安全的。
     */
    max-width: 100% !important;
    min-height: 100%;
  }
  body:not(.app-shell) .modal .modal-dialog:not(.modal-dialog-nosheet) .modal-content {
    width: 100%;
    max-height: 80vh;                   /* ← 上面那 20% 靠它留空 */
    overflow-y: auto;
    /*
     * ⚠️⚠️ `overflow-x: hidden` **不是可选项**（用户 2026-10-15 第 4 条：
     *    「移動端 buymodal 這樣的窗口左右滑動的時候窗口 width 內容會飄，
     *      是不是可以鎖定」）。
     *
     * 机制是 CSS 规范里的那条：**一个轴不是 `visible` 时，另一个轴的 `visible`
     * 计算成 `auto`**。这里只写了 `overflow-y: auto`，于是 `overflow-x` 实际是
     * `auto` —— 弹窗里任何一个横向超宽的子元素（价带、`text-nowrap` 的小字、
     * 尺码行）都会让**整层弹窗**变成可横向拖动：手指左右一划内容就跟着跑。
     *
     * 锁死成 `hidden`（只裁不滚）。**需要横滚的地方不受影响** —— 尺码表那些
     * 自带 `overflow-x:auto` 的**内层**容器照旧能滚，滚的是它们自己、不是弹窗。
     */
    overflow-x: hidden;
    /* flex 子项默认 `min-width:auto`，宁可撑破容器也不缩 —— 本仓踩过多次。 */
    min-width: 0;
    border: 0;
    /* 只有上面两个角圆 —— 下面贴着屏幕底边。 */
    border-radius: 1rem 1rem 0 0 !important;
  }

  /*
   * ---- 例外 2：**顶对齐**的弹窗（`.modal-dialog-top`，目前只有"搜 link"那个）----
   *
   * 用户 2026-10-12：「移动端的 search modal 点了之后点 input 出来键盘，
   * 然后关闭键盘之后 page 垂直就不在 top 位置了发生了滚动，
   * **所以 search 应该用 top 对齐的 modal，下方留空**」。
   *
   * 病根是原生的：底部弹层里的输入框一聚焦，浏览器为了把它顶到键盘上方会
   * **滚动整页**；键盘收起后那个滚动量不还回来，页面就停在半截。
   * 顶对齐之后输入框本来就在屏幕上半部，不需要滚动。
   *
   * ⚠️ 特异性必须**高过上面那条通吃的底部弹层规则**（它带 `:not(.modal-dialog-nosheet)`
   *    且是 `!important`）：这里是 `.modal-dialog.modal-dialog-top` 两个类连写，
   *    比 `:not(…)` 那一条高，两条都 `!important` ⇒ 这条胜。
   * ⚠️ 只在窄屏生效 —— 桌面端它仍是普通居中弹窗（`centered => true`）。
   */
  body:not(.app-shell) .modal .modal-dialog.modal-dialog-top {
    align-items: flex-start !important;   /* ← 顶对齐 */
    min-height: 0;                        /* 不撑满整屏 ⇒ 下面自然空着 */
    margin: .5rem !important;
  }
  body:not(.app-shell) .modal .modal-dialog.modal-dialog-top .modal-content {
    max-height: none;                     /* 不被 80vh 卡住 —— 它不是底部弹层 */
    border-radius: 1rem !important;       /* 四角都圆回来 */
  }

  /*
   * ---- chat：「改会话名」那颗笔在手机上**藏掉**（用户 2026-10-12）----
   *
   * 用户原话：「chat 改 name 在移动端隐藏掉，不要显示修改的笔 icon」。
   * 手机上的聊天窗顶栏宽度本来就被「返回 / 标题 / 关闭」三样占满，
   * 再插一支笔会把标题挤到换行。
   *
   * ⚠️ 只藏**这颗按钮**，`Chat::rename` 那条接口一个字没动 ——
   *    桌面端照旧能改，而且服务端的权限判定（`ownedByViewer()`）也不受影响。
   *    用 `!important`：`.ps-modal.ps-inline .ps-chat-x{display:none}` 那条是 (0,3,0)，
   *    这里得确保赢过它（虽然那支是"内联态"、与这里不冲突，但别留悬念）。
   */
  #psRename { display: none !important; }

  /*
   * ---- blog 文章页：作者那一块压成 **2–3 行**（用户 2026-10-12）----
   *
   * 用户原话：「手机端 blog post 详情页 **头像作者 日期 联系方式 收藏 share
   *   排版紧密一点 2-3 行**」。
   *
   * 桌面那套是**竖排**的（名字 / 日期 / 联系方式各占一行），加上下面
   * "收藏 + 分享"那一行，手机上要 5–6 行，把正文顶下去。
   *
   * 手机端改成：
   *   ① 头像缩小（48 → 34），
   *   ② 名字与日期**并排同一行**（`flex-wrap` 让它们放不下时自己折），
   *   ③ 联系方式那一行字号收一档 —— 于是整块变成
   *      `[头像] 名字 · 日期` / `联系方式` / `[收藏] [分享]` = 3 行。
   *
   * ⚠️ 只加在移动端；桌面端这两个类**没有任何规则**，观感一个字节不变。
   */
  .blog-author-row { gap: .55rem !important; margin-bottom: 1rem !important; align-items: flex-start; }
  .blog-author-row .rounded-circle { width: 34px !important; height: 34px !important; font-size: .85rem !important; }
  .blog-author-meta { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: .5rem; row-gap: 0; line-height: 1.3; }
  .blog-author-meta > .fw-semibold { font-size: .95rem; }
  .blog-author-meta > .small { font-size: .74rem !important; }
  /* 联系方式那一行：整条占满一行（`flex-basis:100%`），字号再收一档 */
  .blog-author-meta > .small.mt-1 { flex: 0 0 100%; margin-top: .1rem !important; gap: .5rem !important; font-size: .72rem !important; }
  /* 收藏 / 分享那一行贴紧上面，别再各留一份外边距 */
  .blog-author-row + .d-flex { margin-bottom: 1rem !important; }

  /*
   * ---- blog 文章页标题收小（用户 2026-10-12）----
   *
   * 用户原话：「blogs post 详情页标题太大了是不是可以做小一点」。
   * 桌面的 1.8rem 是有意写大的（长标题在宽屏上霸气），但 390px 下
   * 1.8rem 的中文/长英文标题会占掉三行、把作者栏和正文全推到屏幕外。
   * ⚠️ 那个 1.8rem 是**内联样式**（`blog/show.php` 的 `<h1 style="…">`），
   *    压它只能靠 `!important`。
   */
  .blog-post-h1 { font-size: 1.26rem !important; line-height: 1.35 !important; }

  /* 名片头/复制行/图标排的内边距在窄屏收一档，390px 下才不显臃肿 */
  .ps-brand-row { padding: .7rem .85rem; }
  .ps-copy-row { padding: .75rem .85rem .6rem; }
  .ps-share-targets { gap: .4rem; padding: .1rem .85rem .85rem; }
  .ps-share-target { width: 3.7rem; padding: .5rem .1rem; font-size: .64rem; }
  .ps-sub-form { padding: .75rem .85rem .45rem; }
  .ps-sub-note { padding: 0 .85rem .7rem; }
  .ps-sub-invite { padding: .6rem .85rem .8rem; }
}
