/*
 * 庫存進出管理系統｜全站樣式
 * 設計調性：SmoothUI（smoothui.dev）
 *
 * 三個視覺主軸：
 *   1. 近黑主色 + zinc 中性灰階。互動色不是彩色，而是墨黑；彩色一律留給狀態。
 *   2. 淺色骨架。側欄與前台頂列都是白底細邊，靠陰影與留白分層，不靠深色塊。
 *   3. 大圓角 + 兩層柔和陰影 + spring 緩動。
 *
 * 保留的硬約束（現場戴手套、日光燈下用平板）：
 *   控制項 44px、正文 16px、文字對比 ≥ 4.5:1、顏色只表達狀態。
 *
 * 改色只需動下面的 :root。全站 PHP 裡的 inline style 一律寫 var(--*)
 * 而非色碼，所以換皮不必去改 28 個頁面。新增顏色請一併加進這裡。
 */
:root {
  /* ---- 結構：zinc 中性階。畫面九成由這幾個色構成 ---- */
  --bg:        #FAFAFA;   /* 頁底，幾乎是白，讓卡片靠陰影浮出來 */
  --panel:     #FFFFFF;
  --panel-2:   #F4F4F5;   /* 表頭、合計列、次級底 */
  --ink:       #09090B;   /* 正文，近黑 */
  --ink-2:     #3F3F46;   /* 次要正文 */
  --muted:     #64646E;   /* 標籤、說明。在 --panel-2 上仍有 5.3:1 */
  --line:      #E4E4E7;   /* 一般邊界，很淡，只負責收邊 */
  --line-2:    #F1F1F3;   /* 表格內斛馬線 */
  --line-3:    #D4D4D8;   /* 表單控制項的邊界，比 --line 深一階才框得出來 */

  /* ---- 骨架：側欄與前台頂列。SmoothUI 是淺色介面，不用深色側欄 ---- */
  --shell:     #FFFFFF;
  --shell-2:   #F4F4F5;   /* 骨架上的 hover 底 */
  --shell-ink: #3F3F46;   /* 骨架上的正文 */
  --shell-dim: #64646E;   /* 骨架上的次要字 */

  /* ---- 互動主色：墨黑 ----
   * SmoothUI 的主要按鈕是近黑而非品牌彩色。這一步同時解掉了舊版
   * 「主色藍」與「調撥藍」撞色的問題——黑色不會跟任何語意色混淆。 */
  --accent:    #18181B;
  --accent-d:  #000000;   /* hover 更黑 */
  --accent-bg: #F4F4F5;   /* 淺底（選中列、hover 底） */

  /* ---- 語意色：只用在標籤、數字、狀態點 ----
   * 前景一律取深階，因為多半疊在淺底上當文字，深階才過得了 4.5:1 對比。 */
  --in:    #166534;  --in-bg:   #F0FDF4;   /* 入庫 */
  --out:   #B42318;  --out-bg:  #FEF2F2;   /* 出庫 */
  --move:  #0E7490;  --move-bg: #ECFEFF;   /* 調撥 */
  --warn:  #B54708;  --warn-bg: #FFFBEB;   /* 盤點、警示 */

  /* ---- 字體 ----
   * Inter 是 SmoothUI 的字。系統沒裝就依序退回 system-ui，中文一律走 Noto Sans TC。 */
  --font:     "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI",
              "Noto Sans TC", "Microsoft JhengHei", system-ui, sans-serif;
  --font-num: ui-monospace, "SF Mono", "JetBrains Mono", Consolas, monospace;

  --fs-xs:  12px;  --fs-sm:  13px;  --fs-md: 15px;  --fs-lg:  16px;
  --fs-xl:  18px;  --fs-2xl: 22px;  --fs-3xl: 32px;

  /* ---- 間距：4px 基準階梯 ---- */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 20px; --sp-6: 24px; --sp-8: 32px;

  /* ---- 圓角：SmoothUI 的 rounded-xl / 2xl 調性 ---- */
  --r-sm: 8px; --r-md: 10px; --r-lg: 14px; --r-xl: 20px; --r-2xl: 24px;
  --r-pill: 999px;

  /* ---- 陰影：兩到三層疊加（貼地細邊 + 散開柔光），「軟」的來源 ---- */
  --sh-1:     0 1px 2px rgba(9, 9, 11, .05);
  --sh-2:     0 1px 2px rgba(9, 9, 11, .04), 0 4px 10px -2px rgba(9, 9, 11, .06);
  --sh-3:     0 2px 4px rgba(9, 9, 11, .04), 0 12px 28px -8px rgba(9, 9, 11, .12);
  --sh-pop:   var(--sh-3);
  --sh-modal: 0 8px 16px -4px rgba(9, 9, 11, .10), 0 32px 64px -12px rgba(9, 9, 11, .30);
  /* 實心黑按鈕自帶的內光，讓它看起來有厚度而不是一塊平貼的色塊 */
  --sh-solid: inset 0 1px 0 rgba(255, 255, 255, .12), 0 1px 2px rgba(9, 9, 11, .30);

  /* focus 光環 */
  --ring:     0 0 0 3px rgba(9, 9, 11, .12);
  --ring-out: 0 0 0 3px rgba(180, 35, 24, .16);

  /* ---- 圖表系列色 ----
   * 與 UI 主色（墨黑）分開：主色管互動，這兩個色管「這條是哪個系列」。
   * 取自 dataviz 參考色盤的前兩個槽位，已驗算色盲可辨性與對白底的對比。
   * 定義在這裡而不是 .chart 上，因為 SVG 外面的圖例色塊也要用同一個值。
   * 藍取的是比槽位標準值深一階的步階：柱體上要壓白色數字，
   * 標準值的白字對比只有 4.42:1（差一點沒過 4.5），深一階後是 5.39:1。 */
  --c-bar:  #256abf;   /* 出貨件數：柱 */
  --c-line: #eb6834;   /* 出貨單數：折線 */

  /* ---- 動態：spring 緩動 ----
   * --ease-spring 末段會些微過衝再回彈，是 SmoothUI 那種「彈一下」的手感來源。
   * 位移類（浮起、彈入）用它；顏色與透明度用 --ease-out——過衝在色彩上看起來像閃爍。 */
  --ease-spring: cubic-bezier(.34, 1.56, .64, 1);
  --ease-out:    cubic-bezier(.22, 1, .36, 1);
  --dur-1: .12s;  --dur-2: .2s;  --dur-3: .32s;

  /* ---- 觸控尺寸：現場戴手套操作，點擊區不能小（不因換風格而縮） ---- */
  --h-ctl: 44px;   /* 按鈕、輸入框 */
  --h-sm:  32px;   /* 表格列內小按鈕 */
}

/* 使用者在系統層關掉動畫時，全站動效一律停掉，只留狀態變化。
 * 寫在這裡是全域的，日後新加的動效自動被涵蓋，不必逐條補。 */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

* { box-sizing: border-box; }

/* 手機橫置再轉回直式時，iOS 會自作主張放大字級。固定住。 */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

/* 圖片一律不許比容器寬。少了這行，一張大圖就能把整頁撐開變成可左右拖。 */
img, video, svg { max-width: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: var(--fs-lg)/1.6 var(--font);
  letter-spacing: -.011em;             /* Inter 在中文字級下略收字距才不鬆散 */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* 標題收字距、加字重。這是 SmoothUI 排版最明顯的特徵之一。 */
h1, h2, h3, .modal__name, .login__title {
  letter-spacing: -.02em;
  font-weight: 650;
}

a { color: var(--accent); }

/* 數量、金額、SKU、單號、時間戳一律等寬對齊，掃視時位數才對得上 */
.num, td.num, th.num, .mono {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
}
td.num, th.num { text-align: right; }

/* ---------- 後台：淺色側欄 ---------- */
.layout { display: grid; grid-template-columns: 248px 1fr; min-height: 100vh; }

.sidebar {
  background: var(--shell);
  border-right: 1px solid var(--line);
  color: var(--shell-ink);
  display: flex;
  flex-direction: column;
}
.sidebar__brand { padding: var(--sp-5) var(--sp-4); border-bottom: 1px solid var(--line); }
.sidebar__brand strong { display: block; color: var(--ink); font-size: var(--fs-lg); letter-spacing: -.02em; }
.sidebar__brand span { font-size: var(--fs-xs); color: var(--shell-dim); }
.sidebar__group {
  margin-top: var(--sp-3); padding: var(--sp-3) var(--sp-4) var(--sp-1);
  border-top: 1px solid var(--line-2);
  font-size: var(--fs-xs); font-weight: 600; letter-spacing: .06em; color: var(--shell-dim);
}
.sidebar__group:first-of-type { margin-top: 0; border-top: 0; }

/* 選單項是內縮的圓角膠囊：active 是一塊墨黑實心塊，滑過只浮出很淡的灰底。
 * 滑過時整項往右挪 2px，用 spring 收住，是很輕的回饋。 */
/* 用 flex + gap 控制圖示與文字的距離。
 * 原本靠 HTML 裡的一個空白字元，換成 Font Awesome 之後那個空白貼著 <i> 邊界，
 * 看起來就像圖示黏在字上。gap 是唯一穩定的作法，也不受字型影響。 */
.sidebar a {
  display: flex; align-items: center; gap: var(--sp-3);
  margin: 2px var(--sp-2); padding: 9px var(--sp-3);
  border-radius: var(--r-md);
  color: var(--shell-ink); text-decoration: none; font-size: var(--fs-md); font-weight: 500;
  transition: background var(--dur-2) var(--ease-out),
              color var(--dur-2) var(--ease-out),
              transform var(--dur-2) var(--ease-spring);
}
.sidebar a:hover { background: var(--shell-2); color: var(--ink); transform: translateX(2px); }
.sidebar a.active {
  background: var(--accent); color: #fff; font-weight: 600;
  box-shadow: var(--sh-solid);
}
.sidebar a.active:hover { background: var(--accent); transform: none; }
.sidebar__foot {
  margin-top: auto; padding: var(--sp-4);
  border-top: 1px solid var(--line); font-size: var(--fs-sm);
}
.sidebar__foot .dim { font-size: var(--fs-xs); color: var(--shell-dim); }

.main { display: flex; flex-direction: column; min-width: 0; }
.topbar {
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  padding: var(--sp-3) var(--sp-6);
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
}
.topbar h1 { margin: 0; font-size: var(--fs-2xl); }
.topbar__user { display: flex; gap: var(--sp-2); align-items: center; font-size: var(--fs-md); color: var(--muted); }

/* ---- 頂列右側：倒數膠囊、登入者、動作連結 ----
 * 狀態做成膠囊、可點的做成低調按鈕，滑過才浮出底色。 */
.idle {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: 4px var(--sp-3);
  border: 1px solid var(--line); border-radius: var(--r-pill);
  background: var(--panel-2); color: var(--muted);
  font-size: var(--fs-sm); white-space: nowrap;
}
.idle__ico { font-size: var(--fs-md); line-height: 1; }
/* 等寬數字，秒數跳動時寬度才不會抖 */
.idle b { font-variant-numeric: tabular-nums; font-weight: 700; color: var(--ink-2); }
.idle--warn { background: var(--warn-bg); border-color: var(--warn); color: var(--warn); }
.idle--warn b { color: var(--warn); font-weight: 800; }

.topbar__me { display: inline-flex; align-items: center; gap: var(--sp-2); white-space: nowrap; }
.topbar__avatar {
  display: grid; place-items: center;
  width: 28px; height: 28px; border-radius: 50%;
  background: var(--accent); color: #fff;
  font-size: var(--fs-sm); font-weight: 700;
  box-shadow: var(--sh-solid);
}
.topbar__name { color: var(--ink-2); font-size: var(--fs-sm); }

.topbar__link {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  min-height: var(--h-sm); padding: 0 var(--sp-3);
  border: 1px solid transparent; border-radius: var(--r-md);
  color: var(--ink-2); text-decoration: none; font-size: var(--fs-sm); font-weight: 500;
  white-space: nowrap;
  transition: background var(--dur-2) var(--ease-out),
              border-color var(--dur-2) var(--ease-out),
              color var(--dur-2) var(--ease-out);
}
/* 頂列的 Font Awesome 圖示：固定寬度，文字被收掉時每顆鈕仍等寬、不會跳動 */
.topbar .fa-solid, .topbar__logo .fa-solid { width: 1.1em; text-align: center; font-size: var(--fs-md); }
.topbar__link:hover { background: var(--panel-2); border-color: var(--line); color: var(--ink); }
.topbar__link--out { color: var(--out); }
.topbar__link--out:hover { background: var(--out-bg); border-color: var(--out); color: var(--out); }

/* 前台頂列與後台同為淺色，所以不再需要一整組深底覆寫，
 * 只保留「大圖大字開啟中」這個狀態——按鈕本身要標示成作用中。 */
.topbar--front .topbar__link.is-on {
  background: var(--accent); border-color: var(--accent); color: #fff;
  box-shadow: var(--sh-solid);
}
.topbar--front .topbar__link.is-on:hover { background: var(--accent-d); border-color: var(--accent-d); }

/* 窄螢幕：先收掉「閒置登出」四個字，只留圖示與數字 */
@media (max-width: 900px) {
  .idle__lb, .topbar__name { display: none; }
}
.content { padding: var(--sp-8); }

/* ---------- 前台：頂列橫向大按鈕（倉庫端／平板） ---------- */
.topbar--front {
  background: var(--shell);
  border-bottom: 1px solid var(--line);
  color: var(--ink);
  flex-wrap: wrap;
}
.topbar--front a { color: var(--shell-ink); text-decoration: none; }
.topbar__logo {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  font-weight: 700; font-size: var(--fs-xl); color: var(--ink) !important; letter-spacing: -.02em;
  white-space: nowrap; flex: 0 0 auto;
}
/*
 * 選單一律不換行，塞不下就自己橫向捲。
 * 原本是 flex-wrap: wrap，六個功能在平板上會折成兩三行，把畫面上半部吃掉，
 * 前台最重要的掃碼欄就被推到摺線以下。換行的高度是不可預期的，橫捲不是。
 */
.topbar__nav {
  display: flex; gap: var(--sp-1); flex: 1;
  flex-wrap: nowrap; overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;                  /* 捲軸佔掉的高度比它幫上的忙多 */
}
.topbar__nav::-webkit-scrollbar { display: none; }
.topbar__nav a { flex: 0 0 auto; white-space: nowrap; }
.topbar__nav a {
  display: flex; align-items: center; gap: var(--sp-2);
  min-height: var(--h-ctl);
  padding: 0 var(--sp-4); border-radius: var(--r-pill);
  font-size: var(--fs-lg); font-weight: 500;
  transition: background var(--dur-2) var(--ease-out), color var(--dur-2) var(--ease-out);
}
.topbar__nav a:hover { background: var(--shell-2); color: var(--ink); }
.topbar__nav a.active {
  background: var(--accent); color: #fff; font-weight: 600;
  box-shadow: var(--sh-solid);
}
.topbar--front .topbar__user { color: var(--muted); }

.wrap { padding: var(--sp-8); max-width: 1200px; margin: 0 auto; }
.wrap--narrow { max-width: 640px; }

/* ---------- 元件 ---------- */
/* 卡片靠陰影分層，邊線退成很淡的一道（只負責收邊）。
 * 這是整份樣式最主要的一處風格來源。 */
.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  box-shadow: var(--sh-2);
  padding: var(--sp-6);
  margin-bottom: var(--sp-5);
}
/* 需要拉住注意力的卡片：擋下來的錯誤、等待確認的動作。
 * 除了換邊線，再加一圈同色柔光，遠看就知道這張卡不一樣。 */
.card--out    { border-color: var(--out);    box-shadow: 0 0 0 4px var(--out-bg),   var(--sh-2); }
.card--accent { border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-bg), var(--sh-2); }
.card--warn   { border-color: var(--warn);   box-shadow: 0 0 0 4px var(--warn-bg),  var(--sh-2); }

.card h2 { margin: 0 0 var(--sp-4); font-size: var(--fs-xl); }
.card h3 { margin: var(--sp-6) 0 var(--sp-2); font-size: var(--fs-lg); }

/* 按鈕：實心墨黑、內光讓它有厚度，滑過往上浮 1px、按下 spring 壓回。
 * 位移只有 1px 是刻意的——現場快速連點時，大幅位移會讓人以為按錯格。 */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  min-height: var(--h-ctl);
  background: var(--accent);
  color: #fff;
  border: 1px solid var(--accent);
  border-radius: var(--r-md);
  padding: 0 var(--sp-5);
  font: inherit; font-size: var(--fs-md); font-weight: 600;
  letter-spacing: -.01em;
  cursor: pointer;
  text-decoration: none;
  box-shadow: var(--sh-solid);
  transition: background var(--dur-2) var(--ease-out),
              border-color var(--dur-2) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out),
              transform var(--dur-2) var(--ease-spring);
}
.btn:hover { background: var(--accent-d); border-color: var(--accent-d); box-shadow: var(--sh-solid), var(--sh-2); transform: translateY(-1px); }
.btn:active { transform: translateY(0) scale(.985); box-shadow: var(--sh-solid); transition-duration: var(--dur-1); }
.btn:disabled { opacity: .45; cursor: not-allowed; transform: none; box-shadow: none; }
.btn--block { width: 100%; min-height: 52px; font-size: var(--fs-lg); }
.btn--ghost {
  background: var(--panel); color: var(--ink-2); border-color: var(--line-3);
  box-shadow: var(--sh-1);
}
.btn--ghost:hover { background: var(--panel-2); color: var(--ink); border-color: var(--line-3); box-shadow: var(--sh-2); }
.btn--danger { background: var(--out); border-color: var(--out); }
.btn--danger:hover { background: #96200F; border-color: #96200F; }
.btn--sm { min-height: var(--h-sm); padding: 0 var(--sp-3); font-size: var(--fs-sm); border-radius: var(--r-sm); }

.field { display: block; margin-bottom: var(--sp-4); }
.field > span {
  display: block; font-size: var(--fs-sm); font-weight: 500;
  color: var(--ink-2); margin-bottom: 6px;
}

/* 必填。原本各頁在文案裡手寫「 *」，跟一般說明文字同色，掃視時看不出來。 */
.field--req > span::after {
  content: " *";
  color: var(--out);
  font-weight: bold;
}

/* 欄位層級的錯誤：邊框轉紅 + 下方一行說明。
 * 只靠頁首的 .alert，使用者得自己找是哪一格。 */
.field--err > input, .field--err > select, .field--err > textarea { border-color: var(--out); }
.field--err > input:focus, .field--err > select:focus { outline-color: var(--out); box-shadow: var(--ring-out); }
.field__err {
  display: block; margin-top: var(--sp-1);
  font-size: var(--fs-sm); color: var(--out);
}
.field input, .field select, .field textarea {
  width: 100%;
  min-height: var(--h-ctl);
  padding: 0 var(--sp-3);
  border: 1px solid var(--line-3);
  border-radius: var(--r-md);
  font: inherit; font-size: var(--fs-md);
  color: var(--ink);
  background: #fff;
  box-shadow: var(--sh-1);
  transition: border-color var(--dur-2) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out);
}
/*
 * 核取方塊與單選鈕要整組退出上面那套「輸入框」的樣式。
 *
 * 上面給的是 width:100% + min-height:var(--h-ctl) + 邊框 + 白底 + 內距，
 * 套在核取方塊上會變成一個 52px 高、帶邊框和白底的大方框，中間一個小勾勾。
 * 特別注意 min-height 壓過 height，所以只設 width/height 是壓不下去的，
 * 必須連 min-height、border、background、padding 一起還原。
 *
 * 選擇器特異度 (0,2,1) 高於 .field input 的 (0,1,1)，不必在意寫在前面或後面。
 */
.field input[type="checkbox"], .field input[type="radio"] {
  width: auto; height: auto; min-height: 0;
  padding: 0; border: 0; border-radius: 0; background: none;
  box-shadow: none;
  accent-color: var(--accent);
}
.field textarea { padding: var(--sp-3); line-height: 1.6; }
.field input:hover, .field select:hover { border-color: var(--muted); }
.field input::placeholder, .field textarea::placeholder { color: #A1A1AA; }

/* focus：外描邊仍保留（無障礙要求的可見焦點），外面再加一圈柔光。
 * 只有柔光沒有描邊，在強光下的平板上會看不出焦點在哪，兩者都要。 */
.field input:focus, .field select:focus, .field textarea:focus,
.btn:focus-visible, .sidebar a:focus-visible, .topbar__nav a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  box-shadow: var(--ring);
}

.row { display: flex; gap: var(--sp-4); flex-wrap: wrap; }
.row > * { flex: 1; min-width: 180px; }

.alert {
  border-radius: var(--r-lg); padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-4);
  border: 1px solid transparent; font-size: var(--fs-md);
}
.alert--ok   { background: var(--in-bg);   color: var(--in);   border-color: var(--in); }
.alert--err  { background: var(--out-bg);  color: var(--out);  border-color: var(--out); }
.alert--info { background: var(--move-bg); color: var(--move); border-color: var(--move); }
.alert--warn { background: var(--warn-bg); color: var(--warn); border-color: var(--warn); }

/* ---------- 前台提示浮層 ----------
 * 倉庫端站著操作、視線在手上的貨與掃碼槍之間移動，訊息貼在頁面上方常常沒被看到，
 * 所以前台一律改成畫面正中央的浮層。容器本身不吃點擊，才不會擋住底下的作業。 */
.toasts {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center;
  padding: var(--sp-5); pointer-events: none;
}
.toasts:empty { display: none; }
.toast {
  pointer-events: auto;
  width: 100%; max-width: 620px; margin: 0;
  padding: var(--sp-6) var(--sp-8) var(--sp-6) var(--sp-6);
  font-size: var(--fs-xl); line-height: 1.6;
  border-width: 1px; border-radius: var(--r-2xl);
  box-shadow: var(--sh-modal);
  position: relative;
  /* 彈入：縮小＋往下一點點，用 spring 收住 */
  animation: toastIn var(--dur-3) var(--ease-spring);
}
@keyframes toastIn { from { transform: scale(.94) translateY(8px); opacity: 0; } }
.toast__x {
  position: absolute; top: var(--sp-2); right: var(--sp-3);
  background: none; border: 0; cursor: pointer;
  font-size: var(--fs-2xl); line-height: 1; color: inherit; opacity: .6;
}
.toast__x:hover { opacity: 1; }
@media print { .toasts { display: none !important; } }

table { width: 100%; border-collapse: collapse; background: var(--panel); }
th, td {
  padding: var(--sp-3) var(--sp-3);
  border-bottom: 1px solid var(--line-2);
  text-align: left; vertical-align: middle;
}
/* 表頭改成白底 + 一道較深的下緣線，不再是灰色色塊。
 * 灰底表頭是傳統後台的做法，白底加線才是 SmoothUI 的表格樣子。 */
th {
  background: var(--panel);
  font-size: var(--fs-xs); color: var(--muted); font-weight: 600;
  letter-spacing: .04em; white-space: nowrap;
  border-bottom: 1px solid var(--line-3);

  /*
   * 長清單捲到一半時，表頭跟著黏在畫面上緣，否則看到第 40 列已經不知道欄位是什麼。
   * 注意：sticky 會被任何祖先的 overflow: hidden/auto/scroll 破壞，
   * 所以包表格的 .card 不能加 overflow。要橫向捲動請用 .table-x 包一層。
   */
  position: sticky;
  top: 0;
  z-index: 2;
}
/* 展開的明細用內層表格，它的表頭不該也黏住，會和外層打架 */
.tr--sub th { position: static; }

tbody tr { transition: background var(--dur-2) var(--ease-out); }
tbody tr:hover { background: var(--panel-2); }

/* 表格的操作欄（明細／沖正／編輯／停用…）。
 * 給按鈕一個共同的寬度下限：不然「沖正」兩個字的鈕會比「明細 / 履歷」小一號，
 * 同一欄裡大小不一，掃視和點按都吃力。窄螢幕的卡片模式另外用 grid 等分。 */
.td--act { white-space: nowrap; }
.td--act .btn { min-width: 92px; }

/* 欄位很多的表格橫向捲動用。會關閉表頭 sticky（overflow 的必然代價）。 */
.table-x { overflow-x: auto; }
.table-x th { position: static; }

/* ---------- 空狀態 ---------- */
.empty {
  padding: var(--sp-8) var(--sp-5);
  text-align: center;
  color: var(--muted);
  font-size: var(--fs-md);
}
.empty em {
  display: block; font-style: normal; font-size: 34px;
  margin-bottom: var(--sp-2); opacity: .45;
}
.empty .btn { margin-top: var(--sp-4); }

.muted { color: var(--muted); font-size: var(--fs-sm); }

/* 標籤：淺底深字再加一圈同色邊，比純色塊更輕，也更好在白底上定位 */
.tag {
  display: inline-block; padding: 3px var(--sp-2); border-radius: var(--r-pill);
  font-size: var(--fs-xs); font-weight: 600; white-space: nowrap;
  border: 1px solid transparent;
}
.tag--ok   { background: var(--in-bg);    color: var(--in);    border-color: rgba(22, 101, 52, .18); }
.tag--off  { background: var(--panel-2);  color: var(--muted); border-color: var(--line); }
.tag--warn { background: var(--warn-bg);  color: var(--warn);  border-color: rgba(181, 71, 8, .18); }
.tag--out  { background: var(--out-bg);   color: var(--out);   border-color: rgba(180, 35, 24, .18); }
.tag--in   { background: var(--in-bg);    color: var(--in);    border-color: rgba(22, 101, 52, .18); }
.tag--move { background: var(--move-bg);  color: var(--move);  border-color: rgba(14, 116, 144, .18); }

/* 數字卡 */
/* minmax 的下限一律包 min(…, 100%)：容器比下限還窄時，硬下限會把版面撐出橫向捲動 */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(160px, 100%), 1fr)); gap: var(--sp-4); margin-bottom: var(--sp-5); }
.stat {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-xl); padding: var(--sp-5);
  box-shadow: var(--sh-2);
  transition: box-shadow var(--dur-2) var(--ease-out),
              transform var(--dur-2) var(--ease-spring);
}
.stat:hover { box-shadow: var(--sh-3); transform: translateY(-2px); }
/* 數字卡的值刻意**不用**等寬數字：等寬會把每個字都撐成 0 的寬度，
 * 在 32px 這種字級下「121」看起來會鬆散破碎。等寬只留給需要上下對齊的地方
 * （表格欄位、座標軸刻度）。滾動計數期間 app.js 會暫時切回等寬，避免位數變化時抖動。 */
.stat b {
  display: block; font-size: var(--fs-3xl); line-height: 1.15;
  font-weight: 650; letter-spacing: -.03em;
}
.stat span { font-size: var(--fs-sm); color: var(--muted); }

/* 前台大按鈕 */
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr)); gap: var(--sp-4); }
/* 前台六顆大磁磚是整套系統最常被看到的畫面，動效放在這裡最划算：
 * 滑過整塊浮起、圖示同時放大一階，兩者都走 spring。 */
.tile {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-2xl);
  box-shadow: var(--sh-2);
  padding: var(--sp-8) var(--sp-5);
  text-align: center;
  text-decoration: none;
  color: var(--ink);
  font-size: var(--fs-2xl); font-weight: 650; letter-spacing: -.02em;
  transition: border-color var(--dur-2) var(--ease-out),
              background var(--dur-2) var(--ease-out),
              box-shadow var(--dur-3) var(--ease-out),
              transform var(--dur-3) var(--ease-spring);
}
.tile:hover {
  border-color: var(--line-3); background: var(--panel);
  box-shadow: var(--sh-3); transform: translateY(-4px);
}
.tile:active { transform: translateY(-1px) scale(.99); transition-duration: var(--dur-1); }
.tile em {
  display: block; font-size: 44px; font-style: normal; margin-bottom: var(--sp-2);
  transition: transform var(--dur-3) var(--ease-spring);
}
.tile:hover em { transform: scale(1.12) rotate(-3deg); }

/* ---- 進場：格狀元件依序淡入上移 ----
 * 只給首頁的數字卡與大磁磚。整頁元素都動會很吵，也會延後現場看到內容的時間。
 * backwards 讓元素在延遲期間就先套用起始狀態，否則會先閃現一次才開始動。 */
@keyframes riseIn { from { opacity: 0; transform: translateY(12px); } }
.stats > *, .tiles > * { animation: riseIn var(--dur-3) var(--ease-out) backwards; }
.stats > :nth-child(2), .tiles > :nth-child(2) { animation-delay: .05s; }
.stats > :nth-child(3), .tiles > :nth-child(3) { animation-delay: .10s; }
.stats > :nth-child(4), .tiles > :nth-child(4) { animation-delay: .15s; }
.stats > :nth-child(5), .tiles > :nth-child(5) { animation-delay: .20s; }
.stats > :nth-child(n+6), .tiles > :nth-child(n+6) { animation-delay: .25s; }

/* ---- 骨架屏 ----
 * 給查詢中、尚未有資料的區塊用：<div class="skel" style="height:44px"></div>
 * 比空白畫面更能讓人知道「在載入」而不是「壞了」。 */
.skel {
  border-radius: var(--r-sm);
  background: linear-gradient(90deg, var(--line-2) 25%, var(--panel-2) 37%, var(--line-2) 63%);
  background-size: 400% 100%;
  animation: skelSlide 1.4s ease-in-out infinite;
}
@keyframes skelSlide { from { background-position: 100% 0; } to { background-position: -100% 0; } }

/* ---------- 大圖大字模式（前台，於首頁切換）----------
 * 全站字級與控制項高度都走 :root 的變數，所以這裡只要覆寫變數，
 * 表格、表單、按鈕、掃碼欄就會一起放大，不必逐個元件改。
 * 圖示與磁磚是固定 px，另外調。 */
body.big {
  --fs-xs: 14px;  --fs-sm: 16px;  --fs-md: 18px;  --fs-lg:  20px;
  --fs-xl: 24px;  --fs-2xl: 28px; --fs-3xl: 42px;
  --h-ctl: 56px;  --h-sm: 44px;   /* 戴手套也點得到 */
}
body.big .stats { grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr)); }
body.big .tiles { grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); }
body.big .tile { padding: var(--sp-8) var(--sp-6); }
body.big .tile em { font-size: 72px; }
body.big .topbar__nav a { font-size: var(--fs-xl); padding: 0 var(--sp-5); }

/* 大圖大字下，凡是寫死 px 的寬度限制都要跟著放大。
 *
 * 字級與控制項高度是走變數、會自己長大，但這幾處的**寬度**是固定值：
 * 欄位一放大、字卻只能塞進原本的框，結果就是內容被擠出格子、按鈕掉行、
 * 出庫頁的錄影浮層整個爆出去——這就是「版型跑掉」的來源。
 * 這裡只補寬度，高度與字級仍交給上面的變數統一控制。 */
body.big .f-xs { max-width: 170px; }
body.big .f-sm { max-width: 215px; }
body.big .f-md { max-width: 285px; }
body.big .f-lg { max-width: 370px; }
body.big .f-xl { max-width: 455px; }
body.big .row > * { min-width: 240px; }          /* 原本 180px 只夠放標準字級 */
body.big .col-nar { flex-basis: 260px; min-width: 240px; }

/* 掃碼欄是前台主角，字級另外寫死過（!important），變數放大吃不到它 */
body.big .scan { font-size: 28px !important; }

/* 數量浮層：原本 440px，放大後標題與 ±10 按鈕會擠成兩行 */
body.big .modal__box { width: min(560px, 100%); }

/* 出貨錄影浮層：固定 300px 寬，放大後三顆控制鈕與狀態文字塞不下 */
body.big .rec-float { width: 380px; }
body.big .rec-float .rec__status { font-size: var(--fs-sm); }

/* ---------- 圖表（儀表板）----------
 * 圖表用色與 UI 主色分開：主色（墨黑）管互動，這兩個色管「這條是哪個系列」。
 * 取自 dataviz 參考色盤的前兩個槽位，已驗算過色盲可辨性與對白底的對比。
 *
 * 文字一律穿文字色（--ink-2 / --muted），**不穿系列色**。
 * 淺色的系列色當文字讀不動；識別是靠旁邊那個有顏色的記號，不是靠把字染色。 */
.chart { width: 100%; height: auto; }
.chart__grid  { stroke: var(--line-2); stroke-width: 1; }
.chart__base  { stroke: var(--line-3); }        /* 零線比一般格線深一階 */
.chart__axis  { fill: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; }
.chart__cap   { fill: var(--ink-2); font-size: 11px; font-weight: 600; }
.chart__value { fill: var(--ink-2); font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums; }
/* 件數寫在柱體內時改白字。白字對柱藍的對比 5.39:1，11px 小字也讀得動。 */
.chart__value--in { fill: #fff; }
.chart__bar   { fill: var(--c-bar); }
.chart__line  { stroke: var(--c-line); stroke-width: 2; fill: none; stroke-linejoin: round; stroke-linecap: round; }
/* 點外面留一圈底色，和折線交疊時才分得出來。這圈不是描邊裝飾，是留白。 */
.chart__dot   { fill: var(--c-line); stroke: var(--panel); stroke-width: 2; }
.chart__hole  { fill: var(--ink); }             /* 甜甜圈中央的總數字 */

/* 整日的感應區。其餘元素都不吃滑鼠事件，所以滑到柱子上時觸發的是這一塊，
 * 不必精準對準細細的折線點（點只有 8px，直接當熱區太小）。 */
.chart__hit { fill: transparent; cursor: default; }
.chart__hit:hover { fill: rgba(9, 9, 11, .045); }
.chart__grid, .chart__base, .chart__axis, .chart__cap,
.chart__value, .chart__bar, .chart__line, .chart__dot { pointer-events: none; }

/* 圖例。兩個系列疊在同一張圖上時圖例是必要的——不能讓人只靠顏色去猜哪條是哪條，
 * 而且要寫明各自吃哪一條軸，否則右邊那排刻度不知道是給誰看的。
 * 記號的形狀刻意不同（方塊 vs 短橫線），色盲情況下靠形狀也分得出來。 */
.chart-key {
  display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-5);
  margin: var(--sp-3) 0 0; font-size: var(--fs-sm); color: var(--muted);
}
.key { display: inline-flex; align-items: center; gap: 6px; }
.key i { display: inline-block; width: 12px; height: 12px; border-radius: 3px; background: var(--c-bar); }
.key--line i { width: 16px; height: 3px; border-radius: 2px; background: var(--c-line); }

/* 甜甜圈圖＋右側圖例。色塊顏色由 PHP 的分類色盤決定，只能寫 inline。
 * 圖例把名稱、百分比、個數都寫出來，是刻意的：色盤裡有幾個色對白底的對比低於 3:1，
 * 不能讓顏色單獨承擔資訊。 */
.pie { display: flex; gap: var(--sp-5); align-items: center; flex-wrap: wrap; }
.pie > svg { width: 160px; height: 160px; flex: 0 0 160px; }
.pie__list { flex: 1; min-width: 150px; }
.pie__row {
  display: flex; align-items: center; gap: var(--sp-2);
  margin-bottom: var(--sp-1); font-size: var(--fs-sm);
}
.pie__row i { width: 11px; height: 11px; border-radius: 3px; flex: 0 0 11px; }
.pie__row .nm { flex: 1; }
.pie__row b { font-variant-numeric: tabular-nums; }

/* ---------- 統一掃描浮層（入庫／調撥／退貨） ---------- */
.modal {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(9, 9, 11, .45);
  backdrop-filter: blur(8px);           /* 遮罩用毛玻璃，底下畫面還看得出輪廓 */
  -webkit-backdrop-filter: blur(8px);
  display: grid; place-items: center; padding: var(--sp-5);
  animation: fadeIn var(--dur-2) var(--ease-out);
}
.modal__box {
  background: var(--panel); border-radius: var(--r-2xl); padding: var(--sp-6);
  width: min(440px, 100%); max-height: 92vh; overflow: auto;
  box-shadow: var(--sh-modal);
  animation: popIn var(--dur-3) var(--ease-spring);
}
@keyframes fadeIn { from { opacity: 0; } }
@keyframes popIn  { from { transform: scale(.94) translateY(10px); opacity: 0; } }
.modal__head { display: flex; gap: var(--sp-4); margin-bottom: var(--sp-4); }
.modal__img {
  width: 88px; height: 88px; flex: 0 0 88px;
  object-fit: cover; border-radius: var(--r-lg); border: 1px solid var(--line);
}
.modal__img--none {
  display: grid; place-items: center;
  background: var(--panel-2); color: var(--muted); font-size: var(--fs-xs);
}
.modal__name { font-size: var(--fs-xl); font-weight: 700; }
.modal__stock {
  margin-top: var(--sp-2); font-size: var(--fs-sm); color: var(--move);
  background: var(--move-bg); border-radius: var(--r-pill);
  border: 1px solid rgba(14, 116, 144, .18);
  padding: 2px var(--sp-3); display: inline-block;
}
/* 確認浮層（取代瀏覽器 confirm），沿用 .modal 的遮罩與白框 */
.modal--confirm { z-index: 300; }        /* 要蓋過提示浮層 .toasts */
.confirm__msg {
  margin: 0 0 var(--sp-5);
  font-size: var(--fs-xl); line-height: 1.6; color: var(--ink);
  white-space: pre-line;                 /* 訊息想換行就換行 */
}
.modal__foot { display: flex; gap: var(--sp-2); margin-top: var(--sp-1); }
.modal__foot .btn--block { flex: 2; }
.modal__foot .btn--ghost { flex: 1; min-height: 52px; }

/* 數量加減器：現場戴手套也要按得準，所以 ±鍵做到 48px */
.stepper { display: flex; gap: var(--sp-2); align-items: stretch; }
.stepper__btn {
  border: 1px solid var(--line-3); background: var(--panel); border-radius: var(--r-md);
  min-width: 48px; padding: 0 var(--sp-3); cursor: pointer;
  font: inherit; font-size: var(--fs-2xl); color: var(--ink);
  box-shadow: var(--sh-1);
  transition: background var(--dur-2) var(--ease-out),
              border-color var(--dur-2) var(--ease-out),
              transform var(--dur-1) var(--ease-spring);
}
.stepper__btn:hover { background: var(--panel-2); border-color: var(--muted); }
.stepper__btn:active { transform: scale(.92); }
.stepper__input {
  flex: 1; text-align: center;
  font-family: var(--font-num) !important;
  font-size: 26px !important; font-weight: bold;
}

/* ---------- 列表通則：分頁與排序 ---------- */
/* 三段固定站位：筆數與頁次靠左、頁碼置中、每頁筆數靠右。
 * 只有一頁時中間那段不會輸出，剩下兩段仍是一左一右。 */
.pager {
  display: flex; gap: var(--sp-4); align-items: center; flex-wrap: wrap;
  justify-content: space-between;
  margin-top: var(--sp-5); font-size: var(--fs-sm); color: var(--muted);
}
.pager__info { margin-right: auto; }
.pager__per  { margin-left: auto; }
.pager__pages, .pager__per { display: flex; gap: var(--sp-1); align-items: center; }
.pager a, .pager .on {
  min-width: var(--h-sm); min-height: var(--h-sm);
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0 var(--sp-2); border-radius: var(--r-sm); border: 1px solid var(--line-3);
  text-decoration: none; color: var(--ink-2); background: #fff;
  font-family: var(--font-num);
  box-shadow: var(--sh-1);
  transition: background var(--dur-2) var(--ease-out),
              border-color var(--dur-2) var(--ease-out),
              color var(--dur-2) var(--ease-out);
}
.pager a:hover { border-color: var(--muted); color: var(--ink); background: var(--panel-2); }
.pager .on { background: var(--accent); border-color: var(--accent); color: #fff; box-shadow: var(--sh-solid); }
.pager__info b { color: var(--ink); }

a.th-sort { color: inherit; text-decoration: none; display: block; }
a.th-sort:hover { color: var(--accent); }

/* 分類樹側欄（商品管理） */
.tree { font-size: var(--fs-md); }
.tree a {
  display: flex; justify-content: space-between; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-2); border-radius: var(--r-sm);
  text-decoration: none; color: var(--ink);
  transition: background var(--dur-2) var(--ease-out), color var(--dur-2) var(--ease-out);
}
.tree a:hover { background: var(--panel-2); }
.tree a.on { background: var(--accent); color: #fff; box-shadow: var(--sh-solid); }
.tree a.on .muted { color: rgba(255, 255, 255, .85); }
.tree .muted { font-size: var(--fs-xs); }

/* 載入中指示（規格書 4.25：查詢超過 300ms 顯示） */
.loading {
  position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 99;
  background: linear-gradient(90deg, var(--accent), var(--move), var(--accent));
  background-size: 200% 100%; animation: loadBar .9s linear infinite;
}
@keyframes loadBar { to { background-position: -200% 0; } }

/* 物流單號格式檢核提示（規格書 4.3.3：符合綠底、不符黃底但仍可送出） */
.track-hint { margin: var(--sp-2) 0 0; font-size: var(--fs-sm); padding: 0; }
.track-hint--ok   { color: var(--in);   background: var(--in-bg);   padding: var(--sp-1) var(--sp-2); border-radius: var(--r-sm); }
.track-hint--warn { color: var(--warn); background: var(--warn-bg); padding: var(--sp-1) var(--sp-2); border-radius: var(--r-sm); }

/* ---------- 沖正浮層（單據查詢頁）----------
 * 沖正原本是換頁後把表單畫在列表下方，按下去畫面看似毫無變化，
 * 改成就地跳出浮層。沿用 .modal 的遮罩與白框，只補上開關與層級。 */

/* .modal 寫死了 display: grid，作者樣式一定蓋過瀏覽器內建的 [hidden]{display:none}，
 * 所以要自己補這一條，否則 hidden 屬性對浮層完全沒有作用。 */
.modal[hidden] { display: none; }
.modal--rev { z-index: 200; }            /* 低於 .modal--confirm(300)，高於 .toasts */
.modal--rev .modal__box { width: min(520px, 100%); }
.modal--rev h3 { margin: 0 0 var(--sp-3); font-size: var(--fs-xl); }
.modal--rev #revNo { font-family: var(--font-num); }

/* 不敢用 .alert：app.js 會把全站的 .alert 搬進中央提示浮層，一進頁面就會自己彈出來 */
.rev-warn {
  background: var(--warn-bg); color: var(--warn);
  border: 1px solid var(--warn); border-radius: var(--r-md);
  padding: var(--sp-2) var(--sp-3); margin-bottom: var(--sp-4);
  font-size: var(--fs-md);
}

/* JS 沒載到時的退路版面（<noscript> 內的換頁式沖正表單） */
.rev-jump { padding-left: var(--sp-3); border-left: 4px solid var(--out); }

/* 收合式小提醒（單據查詢頁的「什麼是沖正」）
 * 預設收合：真正需要的人點開就好，不必每天都佔掉列表上方一大塊。 */
.tip { margin: calc(var(--sp-2) * -1) 0 var(--sp-4); }
.tip > summary { font-size: var(--fs-md); padding: var(--sp-1) 0; width: fit-content; }
.tip > summary:hover { color: var(--ink); }
.tip__body {
  margin-top: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  background: var(--panel-2); border-left: 4px solid var(--accent);
  border-radius: var(--r-md);
  font-size: var(--fs-md); line-height: 1.7;
}
.tip__body p { margin: 0 0 var(--sp-2); }
.tip__body p:last-child { margin-bottom: 0; }

/* ---------- 明細／履歷浮層（單據查詢頁）----------
 * 明細與履歷兩張表可能很長，所以做得比沖正浮層寬，內容自己捲。 */
.modal--doc { z-index: 200; }
.modal--doc .modal__box { width: min(920px, 100%); }
.modal--doc table { margin-bottom: var(--sp-5); }
.modal--doc h3 { margin: var(--sp-5) 0 var(--sp-2); font-size: var(--fs-lg); }

/* 浮層標題列：標題靠左、關閉鈕靠右，捲動內容時標題不跟著跑掉 */
.modal__bar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
  position: sticky; top: calc(var(--sp-6) * -1);   /* 抵銷 .modal__box 的內距 */
  background: var(--panel); z-index: 1;
  margin: calc(var(--sp-6) * -1) calc(var(--sp-6) * -1) var(--sp-4);
  padding: var(--sp-4) var(--sp-6);
  border-bottom: 1px solid var(--line);
}
.modal__bar h3 { margin: 0; font-size: var(--fs-xl); }
.modal__x {
  background: none; border: 0; cursor: pointer; color: var(--muted);
  font-size: var(--fs-2xl); line-height: 1; padding: 0 var(--sp-2);
}
.modal__x:hover { color: var(--ink); }

/* ---------- 出貨錄影 ---------- */
.rec { display: flex; gap: var(--sp-4); flex-wrap: wrap; align-items: flex-start; }

/*
 * min-width: 0 是這裡的關鍵。
 * flex 項目預設 min-width: auto，代表「不能縮到比內容還小」。
 * <video> 沒有影像時的內建尺寸是 300×150，一旦接上 1280×720 的相機串流，
 * 內建尺寸就變成 1280px，這個 flex 項目便拒絕縮回 360px，整塊撐出卡片外。
 * 寫死 min-width: 0 之後，寬度才會真的聽 CSS 的。
 */
.rec__box {
  position: relative;
  flex: 1 1 360px;
  min-width: 0;
  max-width: 480px;
  overflow: hidden;          /* 雙保險：影像再大也不會溢出圓角邊界 */
  border-radius: var(--r-lg);
}
.rec__video {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  max-height: 55vh;          /* 筆電螢幕較矮時不會把畫面整個占滿 */
  background: #18181B;
  object-fit: contain;       /* 完整顯示鏡頭畫面，不裁切 */
}
/* 未錄影時不顯示紅點，開始錄影才亮起 */
.rec__dot {
  position: absolute; top: var(--sp-2); left: var(--sp-2); display: none;
  background: rgba(180, 35, 24, .95); color: #fff;
  padding: 3px var(--sp-2); border-radius: var(--r-pill);
  font-size: var(--fs-xs); letter-spacing: .04em;
}
.rec--on .rec__dot { display: block; animation: recBlink 1.2s infinite; }
@keyframes recBlink { 50% { opacity: .35; } }

/* 回放頁的播放器（admin/video.php） */
.video-play {
  display: block; width: 100%; max-width: 900px;
  background: #000; border-radius: var(--r-lg);
}

.rec__side { flex: 1 1 260px; min-width: 0; }
/* 鏡頭選擇：只有偵測到兩台以上鏡頭時 rec.js 才會把 [hidden] 拿掉 */
.rec-dev { display: flex; gap: var(--sp-2); align-items: center; margin-top: var(--sp-2); }
.rec-dev[hidden] { display: none; }      /* 上面寫死 display:flex，會蓋掉瀏覽器內建的 [hidden] */
.rec-dev__sel {
  flex: 1; min-width: 0;                 /* 鏡頭名稱很長（含廠商與型號），要能縮 */
  font: inherit; font-size: var(--fs-sm);
  padding: var(--sp-1) var(--sp-2);
  border: 1px solid var(--line-3); border-radius: var(--r-sm);
  background: var(--panel); color: var(--ink);
  text-overflow: ellipsis;
}

.rec__status { margin: var(--sp-3) 0 0; font-size: var(--fs-md); color: var(--muted); }
.rec__status--ok  { color: var(--in); }
.rec__status--err { color: var(--out); }
.rec__status--rec { color: var(--out); font-weight: bold; }
.rec--done .rec__video { outline: 3px solid var(--in); }

/* ---- 出貨錄影浮層（出庫頁）----
 * 固定在畫面右下，員工掃碼時視線不必離開主畫面就能確認還在錄。
 * 內容用 .rec 既有樣式，只是改成窄版直式排列。 */
.rec-float {
  position: fixed; right: var(--sp-4); bottom: var(--sp-4); z-index: 60;
  width: 300px; max-width: calc(100vw - var(--sp-6));
  /* 筆電螢幕不高，浮層不能無上限地長，否則整個右側都被它佔滿 */
  max-height: 62vh;
  display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-xl); box-shadow: var(--sh-3);
  overflow: hidden;
  animation: popIn var(--dur-3) var(--ease-spring);
}
/*
 * 浮層是 position: fixed，完全不佔版面，所以頁面最底部的內容
 *（「完成出庫」按鈕、最近出庫紀錄）會被它**永久蓋住**——捲到底也露不出來。
 * 這就是出庫頁版型看起來壞掉的主因。這裡把底部讓出浮層的高度。
 * 用 :has() 是因為只有出庫頁有這個浮層，其他前台頁不該白留一截；
 * 而且 :has() 是即時求值的，收合後留白會馬上跟著縮回去。
 */
body:has(.rec-float) .wrap { padding-bottom: 340px; }
body.big:has(.rec-float) .wrap { padding-bottom: 400px; }
body:has(.rec-float--min) .wrap { padding-bottom: var(--sp-8); }
.rec-float__hd {
  flex: 0 0 auto;                 /* 內容捲動時標題列固定不動 */
  display: flex; justify-content: space-between; align-items: center;
  padding: var(--sp-2) var(--sp-3);
  background: var(--panel-2); color: var(--ink);
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-md); font-weight: 600;
}
.rec-float__min {
  background: none; border: 0; color: inherit; cursor: pointer;
  font-size: var(--fs-lg); line-height: 1; padding: 0 var(--sp-1);
}
.rec-float .rec { display: block; padding: var(--sp-3); overflow-y: auto; }
.rec-float .rec__box { max-width: none; margin-bottom: var(--sp-2); }
.rec-float .rec__video { max-height: none; }
.rec-float .rec__status { margin-top: var(--sp-2); font-size: var(--fs-sm); }
/* 收合後只留標題列，錄影仍在進行 */
.rec-float--min .rec { display: none; }

/* 背景上傳進度條：只在真的在傳的時候佔版面 */
.rec-prog { display: none; align-items: center; gap: var(--sp-2); margin-top: var(--sp-2); }
.rec--uploading .rec-prog { display: flex; }
.rec-prog__track {
  flex: 1; height: 6px; min-width: 0;
  background: var(--line-2); border-radius: var(--r-pill); overflow: hidden;
}
.rec-prog__bar {
  display: block; height: 100%; width: 0;
  background: var(--accent); transition: width .2s linear;
}
.rec-prog__pct { font-size: var(--fs-xs); color: var(--muted); font-variant-numeric: tabular-nums; }
/* 上傳中時標題列顯示提示色，收合狀態下也看得出來還沒傳完 */
.rec-float:has(.rec--uploading) .rec-float__hd { background: var(--accent); color: #fff; }
/* 錄影中整個浮層加紅框，遠遠就看得出來 */
.rec-float:has(.rec--on) { border-color: var(--out); box-shadow: 0 0 0 3px var(--out-bg), var(--sh-3); }

/* 手機版的浮層改成貼齊底部的整條，規則寫在本檔最後的
 * 「前台：平板與手機」段落，這裡不再有第二份 700px 的定義。 */
@media print { .rec-float { display: none !important; } }

/* 掃碼欄：字大、置中、焦點明顯，現場一眼確認游標在不在這 */
.scan {
  font-family: var(--font-num) !important;
  font-size: 20px !important;
  letter-spacing: .08em;
  text-align: center;
}
.scan:focus { outline-width: 3px !important; }

/* ---- 鏡頭掃描鈕 ----
 * scan.js 會把每個 .scan 包進 .scan-wrap，再把按鈕絕對定位貼在右側。
 * 用絕對定位而不是 flex，是因為輸入框底下還會冒出輸入法警告框，
 * 按鈕不該跟著那個高度變化跑上跑下。 */
.scan-wrap { position: relative; }
.scan-wrap .scan { padding-right: 56px !important; }   /* 留出按鈕的位置，字才不會被蓋住 */
.scan-cam {
  position: absolute; right: 6px; top: 6px; bottom: 6px;
  width: 44px;                            /* 戴手套也按得到 */
  display: grid; place-items: center;
  border: 1px solid var(--line-3); border-radius: var(--r-sm);
  background: var(--panel); cursor: pointer;
  font-size: 20px; line-height: 1;
}
.scan-cam:hover { background: var(--panel-2); }

/* ---- 鏡頭掃描浮層 ---- */
.camscan {
  position: fixed; inset: 0; z-index: 400;   /* 蓋過所有既有浮層 */
  background: rgba(9, 9, 11, .92);
  display: none; flex-direction: column;
  align-items: center; justify-content: center; gap: var(--sp-4);
  padding: var(--sp-4);
}
.camscan.on { display: flex; }
.camscan__box {
  position: relative;
  width: min(560px, 100%);
  border-radius: var(--r-lg); overflow: hidden; background: #000;
}
.camscan__video { display: block; width: 100%; height: auto; }
/* 取景框：只是給人對準用的視覺參考，辨識本身是整張畫面都看 */
.camscan__frame {
  position: absolute; left: 8%; right: 8%; top: 28%; bottom: 28%;
  border: 3px solid rgba(255, 255, 255, .9); border-radius: var(--r-md);
  box-shadow: 0 0 0 100vmax rgba(0, 0, 0, .35);
}
.camscan__hint { margin: 0; color: #fff; font-size: var(--fs-lg); text-align: center; }
.camscan__bar { display: flex; gap: var(--sp-3); }
.camscan__bar .btn { min-height: 48px; }

/* 掃碼欄的輸入法警示（assets/scan.js）。
   用 --warn 而不是 --out：這不是操作失敗，是提醒現場「輸入法沒切英文」，
   碼並沒有送出去，性質接近盤點差異那類「請你看一下」。
   顯示與否走 .on，不要改 style.display——JS 蓋不掉 PHP 給的 class。 */
.ime-warn {
  display: none;
  margin-top: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--warn); border-left-width: 4px;
  border-radius: var(--r-sm);
  background: var(--warn-bg); color: var(--warn);
  font-size: var(--fs-sm); line-height: 1.7; text-align: left;
  letter-spacing: normal;          /* .scan 的字距是給碼看的，這裡是中文說明 */
}
.ime-warn.on { display: block; }
.scan--warn { border-color: var(--warn) !important; background: var(--warn-bg) !important; }

/* ---------- 登入頁 ----------
 * 整個系統只有這一頁是深色的：它是進入前的門面，深底 + 光暈最能定調。
 * 這裡的深色是寫死的，不吃 --shell（--shell 已經是白的）。 */
body.login {
  display: grid; place-items: center; min-height: 100vh; padding: var(--sp-6);
  background: #09090B;
  background-image:
    radial-gradient(60% 55% at 15% 5%,   rgba(99, 102, 241, .30), transparent 70%),
    radial-gradient(55% 50% at 90% 95%,  rgba(14, 165, 233, .22), transparent 70%),
    radial-gradient(40% 40% at 80% 10%,  rgba(217, 70, 239, .14), transparent 70%),
    linear-gradient(rgba(255,255,255,.025) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.025) 1px, transparent 1px);
  background-size: auto, auto, auto, 36px 36px, 36px 36px;
}
.login__box {
  width: min(420px, 100%); background: #fff;
  border-radius: var(--r-2xl); padding: var(--sp-8);
  box-shadow: var(--sh-modal);
  animation: popIn var(--dur-3) var(--ease-spring);
}
.login__title { margin: 0 0 var(--sp-1); font-size: var(--fs-2xl); }
.login__sub { margin: 0 0 var(--sp-5); color: var(--muted); font-size: var(--fs-sm); }

/* 分頁切換：外層是一條淺色軌道，選中的那顆是軌道上的白色滑塊
 * （SmoothUI 的 Animated Tabs 手感，選中狀態由既有的 JS 給 class）。 */
.tabs {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-1);
  margin-bottom: var(--sp-5); padding: var(--sp-1);
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.tabs button {
  min-height: 40px; padding: 0 var(--sp-2);
  min-width: 0;                 /* grid 項目預設 min-width:auto，字長就會把格子撐破 */
  border: 1px solid transparent; background: transparent;
  border-radius: var(--r-md); cursor: pointer; font: inherit;
  font-size: var(--fs-md); font-weight: 600; color: var(--muted);
  transition: background var(--dur-2) var(--ease-out),
              color var(--dur-2) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out);
}
.tabs button:hover { color: var(--ink); }
.tabs button.on { background: var(--panel); color: var(--ink); box-shadow: var(--sh-2); }
.hint { text-align: center; color: var(--muted); font-size: var(--fs-sm); margin: var(--sp-4) 0 0; }

/* ==========================================================
 * 排版工具類
 *
 * 用途很窄，只收全站重複出現的那幾種排法。
 * 真正一次性的排版仍然寫 inline style 就好，不必為它發明 class。
 * ========================================================== */

/* 主從雙欄：左邊主內容、右邊側欄。原本 8 個頁面各自手寫 flex:2/flex:1。 */
.row--top { align-items: flex-start; }
.row--bottom { align-items: flex-end; }   /* 篩選列裡按鈕要和輸入框底部切齊 */
.col-main { flex: 2 1 380px; }
.col-side { flex: 1 1 280px; }
.col-wide { flex: 2 1 500px; }   /* 欄位特別多的表格（員工、SKU） */
.col-nar  { flex: 0 1 210px; min-width: 190px; }  /* 分類樹那種固定窄欄 */

/* 水平排列：按鈕組、篩選列、圖示加文字 */
.inline { display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap; }
.inline--tight { gap: var(--sp-1); }
.inline--end   { justify-content: flex-end; flex-wrap: nowrap; }
.inline--top   { align-items: flex-start; }
.split { display: flex; justify-content: space-between; align-items: center; gap: var(--sp-2); }
/* 當 .split 當成卡片標題列用時，標題自己的下邊距交給 .mb-* 控制 */
.split > h2, .split > h3 { margin: 0; }

/* 表單送出列：主鈕＋取消＋說明文字 */
.actions {
  display: flex; gap: var(--sp-3); align-items: center; flex-wrap: wrap;
  margin-top: var(--sp-5);
}
.actions .muted { margin: 0; }

/* 只為了讓刪除／停用的小表單不換行，包在表格列裡用 */
.f-inline { display: inline; }

/* 商品圖／縮圖 */
.thumb {
  width: 88px; height: 88px; object-fit: cover;
  border-radius: var(--r-lg); border: 1px solid var(--line);
}
.thumb--sm { width: 48px; height: 48px; display: block; border-radius: var(--r-sm); }
/* 表格列裡的選檔按鈕，壓窄一點才不會把欄位撐開 */
/* ---------- 檔案選擇 ----------
 * 原生的 <input type="file"> 長相由作業系統決定：Windows 是灰色的「選擇檔案」、
 * macOS 又是另一種，字級和高度都不受樣式表控制，跟站上其他控制項完全搭不起來。
 *
 * 作法是把真正的 input 藏起來（但仍留在無障礙樹裡、仍可鍵盤聚焦），
 * 外面套一顆自己畫的按鈕。整個 .filepick 是 <label>，
 * 所以點按鈕＝點 label＝觸發它的 input，不需要任何 JS 也能開檔案視窗。
 * JS 只負責把選到的檔名顯示出來，沒 JS 也只是少顯示檔名而已。 */
.filepick {
  display: inline-flex; align-items: center; gap: var(--sp-3);
  flex-wrap: wrap; cursor: pointer;
  margin-top: var(--sp-1);
}
/* 不用 display:none：那會讓它從無障礙樹消失，也不能用鍵盤聚焦 */
.filepick input[type="file"] {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; border: 0;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}
.filepick__btn {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  min-height: var(--h-ctl); padding: 0 var(--sp-4);
  border: 1px solid var(--line-3); border-radius: var(--r-md);
  background: var(--panel); color: var(--ink-2);
  font-size: var(--fs-md); font-weight: 600; white-space: nowrap;
  box-shadow: var(--sh-1);
  transition: background var(--dur-2) var(--ease-out),
              border-color var(--dur-2) var(--ease-out),
              box-shadow var(--dur-2) var(--ease-out);
}
.filepick:hover .filepick__btn {
  background: var(--panel-2); color: var(--ink); box-shadow: var(--sh-2);
}
/* 鍵盤聚焦時要看得出來——input 藏起來了，焦點框得畫在按鈕上 */
.filepick input[type="file"]:focus-visible + .filepick__btn {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
.filepick__name {
  min-width: 0; flex: 1 1 8em;
  font-size: var(--fs-sm); color: var(--muted);
  overflow-wrap: anywhere;       /* 檔名常常很長又沒有空白 */
}
.filepick__name.is-set { color: var(--ink-2); font-weight: 500; }

/* 表格列內的縮小版（SKU 那一欄） */
.filepick--sm { gap: var(--sp-2); }
.filepick--sm .filepick__btn {
  min-height: var(--h-sm); padding: 0 var(--sp-3);
  font-size: var(--fs-sm); border-radius: var(--r-sm);
}
.filepick--sm .filepick__name { font-size: var(--fs-xs); flex-basis: 6em; }

/* ---------- 上傳前的圖片調整浮層 ---------- */
/* .modal 本身是 display: grid，不補這兩條的話浮層會一載入就掛在畫面上 */
.imgpick { display: none; z-index: 320; }  /* 蓋過確認浮層(300)，它是在確認流程之前觸發的 */
.imgpick.on { display: grid; }
.imgpick__box { width: min(680px, 100%); }
/* 舞台給一個固定高度，換張圖或旋轉時浮層不會忽高忽低跳動 */
.imgpick__stage {
  display: grid; place-items: center;
  height: min(52vh, 420px);
  background: var(--panel-2);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  overflow: hidden;
}
/* canvas 的實際像素就是輸出尺寸，這裡只用 CSS 縮到看得下——所見即所得 */
.imgpick__canvas { max-width: 100%; max-height: 100%; display: block; }
.imgpick__tools {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2);
  margin-top: var(--sp-3);
}
.imgpick__size { display: inline-flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-sm); color: var(--muted); }
.imgpick__sel {
  font: inherit; font-size: var(--fs-sm);
  padding: var(--sp-1) var(--sp-2);
  border: 1px solid var(--line-3); border-radius: var(--r-sm);
  background: var(--panel); color: var(--ink);
}
.imgpick__info {
  margin: var(--sp-2) 0 var(--sp-4);
  font-size: var(--fs-sm); color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* ---------- 規格設定（商品編輯頁）----------
 *
 * 這一區要傳達兩件事，而且都是靠版面傳達，不是靠文字說明：
 *   1. 規格組的「順序」就是 SKU 碼的串接順序 —— 用編號與組成式呈現。
 *   2. 每個選項的「值」與「縮寫」是一組 —— 用膠囊把兩者綁在一起。
 */

/* SKU 碼組成式：商品代碼 – ①容量 – ②顏色，下面再給一個實際範例 */
.spec-formula {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-3);
  margin-bottom: var(--sp-4); padding: var(--sp-3) var(--sp-4);
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.spec-formula__lb {
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: .06em;
  color: var(--muted); white-space: nowrap;
}
.spec-formula__parts { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-1); }
.spec-formula__sep { color: var(--muted); font-weight: 700; }
.spec-formula__eg { margin-left: auto; font-size: var(--fs-sm); color: var(--muted); }
.spec-formula__eg b { color: var(--ink); }

/* 膠囊：主體是值，右邊掛一個深色的縮寫小標 */
.chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-2) 0 var(--sp-3); }
.chip {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: 4px var(--sp-2) 4px var(--sp-3);
  border: 1px solid var(--line-3); border-radius: var(--r-pill);
  background: var(--panel); font-size: var(--fs-sm); white-space: nowrap;
}
.chip__abbr {
  font-family: var(--font-num); font-style: normal; font-size: var(--fs-xs);
  padding: 1px var(--sp-2); border-radius: var(--r-pill);
  background: var(--panel-2); color: var(--ink-2);
}
.chip--base { border-color: var(--accent); }
/* 膠囊上的刪除鈕：平常很淡，滑過膠囊才轉紅，避免整排紅叉搶走注意力 */
.chip__x {
  display: inline-grid; place-items: center;
  width: 20px; height: 20px; padding: 0;
  border: 0; border-radius: 50%;
  background: transparent; color: var(--line-3);
  font-size: var(--fs-xs); line-height: 1; cursor: pointer;
  transition: background var(--dur-2) var(--ease-out), color var(--dur-2) var(--ease-out);
}
.chip:hover .chip__x { color: var(--muted); }
.chip__x:hover, .chip__x:focus-visible { background: var(--out-bg); color: var(--out); }
/* 觸控裝置沒有 hover，叉叉會一直是最淡的顏色而看不見，直接給它可讀的對比 */
@media (pointer: coarse) {
  .chip__x { color: var(--muted); }
}
/* 圓形序號，跟組成式裡的編號一致，一眼對得起來是哪一組 */
.chip__no {
  display: inline-grid; place-items: center;
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--accent); color: #fff;
  font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums;
}

/* 規格組排成網格，不用 .row 的 flex：欄寬一致，卡片高度不齊時也不會互相牽動 */
.spec-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr));
  gap: var(--sp-4);
}
.spec-box {
  border: 1px solid var(--line); border-radius: var(--r-lg); padding: var(--sp-4);
  background: var(--panel); box-shadow: var(--sh-1);
}
.spec-box__hd { display: flex; align-items: center; gap: var(--sp-2); }
.spec-box__name { font-size: var(--fs-lg); }
/* 刪除鈕推到最右，中間的「N 個選項」不管字多長都不會把它擠掉 */
.spec-box__hd form { margin-left: auto; }
.spec-box__add { display: flex; gap: var(--sp-2); align-items: center; }
/* 新增規格組那張卡用虛線框，跟已建立的規格組區分開 */
.spec-box--new { border-style: dashed; background: var(--panel-2); box-shadow: none; }

/* 生成 SKU 那一列 */
.spec-gen {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-3);
  margin-top: var(--sp-5); padding-top: var(--sp-4);
  border-top: 1px solid var(--line);
}

.center { text-align: center; }
.m0 { margin: 0; }
.f-full { flex-basis: 100%; }   /* 在 .row 裡獨占一整列 */
.mt-1 { margin-top: var(--sp-2); }
.mt-2 { margin-top: var(--sp-3); }
.mt-3 { margin-top: var(--sp-4); }
.mt-4 { margin-top: var(--sp-6); }
.mb-1 { margin-bottom: var(--sp-2); }
.mb-2 { margin-bottom: var(--sp-3); }
.nowrap { white-space: nowrap; }
.preline { white-space: pre-line; }
.pointer { cursor: pointer; }
.hidden { display: none; }
.w-auto { width: auto; }   /* 蓋掉 .field input 的 width:100%，給核取方塊用 */

/* 展開後顯示的原始值。只在 JSON 結構不如預期、無法攤成表格時才會用到。 */
.code-block {
  font-family: var(--font-num); font-size: var(--fs-xs);
  white-space: pre-wrap; word-break: break-all;
}

/* ---------- 拖曳排序（商品類別）----------
 * 只有握把可以起拖：整列可拖的話，想點「編輯」會變成不小心把順序拖亂。 */
.hidden-form { display: none; }
.td--grip {
  width: 40px; text-align: center; color: var(--muted);
  cursor: grab;
  /* touch-action: none 是觸控裝置能拖的關鍵：
   * 不關掉預設手勢的話，手指一往下滑就被瀏覽器判定成捲動頁面，拖曳永遠起不來。 */
  touch-action: none;
  -webkit-user-select: none; user-select: none;
}
.td--grip:active { cursor: grabbing; }
.td--grip:hover { color: var(--ink-2); }

/* 拖曳中：整串被搬動的列（父節點與其子孫）一起變色，看得出來搬的是哪一塊。
 *
 * ⚠ 標示線只能畫在每一列的第一格。
 *   寫成 `> td { box-shadow: inset 2px 0 0 }` 的話，每個儲存格左邊都會多一條線，
 *   一列六格就變成六條短豎線，看起來像是跑出了欄位分隔線。 */
.table--sortable tr.is-dragging > td { background: var(--accent-bg); }
.table--sortable tr.is-dragging > td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
/* 拖曳過程中關掉列的 hover 底色與文字選取，畫面才不會一直閃 */
.table--sortable.is-sorting { -webkit-user-select: none; user-select: none; }
.table--sortable.is-sorting tbody tr:hover { background: inherit; }

/* 窄螢幕是卡片模式，握把改成卡片頂端的一條，仍然只有它可以拖 */
@media (max-width: 1024px) {
  .table--stack > tbody > tr > td.td--grip {
    display: block; width: auto; text-align: center;
    padding: var(--sp-1) 0 var(--sp-2);
    border-bottom: 1px dashed var(--line-2);
  }
  .table--stack > tbody > tr > td.td--grip::before { content: none; }
}

/* ---------- 操作紀錄的欄寬（桌機）----------
 * 這張表七個欄位，長短懸殊：時間與 IP 固定長度，人員／操作／對象是短詞，
 * 內容則是一長串說明、還可能展開 min-width: 340px 的變更明細。
 *
 * 交給瀏覽器自動分配的結果是：內容欄把寬度全部吃走，
 * 人員／操作／對象被壓到最小內容寬度，中文就一格一個字往下折。
 * 這裡把六個可預期的欄位寬度直接寫定，第五欄（內容）不指定，讓它吃剩下的。
 *
 * 只在桌機生效：1024px 以下那張表是卡片模式，欄寬完全不適用。 */
@media (min-width: 1025px) {
  .table--audit th:nth-child(1), .table--audit td:nth-child(1) { width: 150px; }
  .table--audit th:nth-child(2), .table--audit td:nth-child(2) { width: 120px; }
  .table--audit th:nth-child(3), .table--audit td:nth-child(3) { width: 140px; }
  /* 對象放的是單號，長且不含空白，要允許斷行才不會把欄位撐開 */
  .table--audit th:nth-child(4), .table--audit td:nth-child(4) { width: 190px; overflow-wrap: anywhere; }
  .table--audit th:nth-child(6), .table--audit td:nth-child(6) { width: 90px;  white-space: nowrap; }
  .table--audit th:nth-child(7), .table--audit td:nth-child(7) { width: 130px; overflow-wrap: anywhere; }
}

/* ---------- 操作紀錄的變更明細 ----------
 * 把 audit_logs 的 before/after JSON 攤成「欄位 / 變更前 / 變更後」。 */
.diff {
  width: auto; min-width: 340px; max-width: 100%;
  margin: var(--sp-2) 0 var(--sp-1);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  font-size: var(--fs-sm);
  overflow: hidden;              /* 讓內部的列不要壓過圓角 */
}
.diff th, .diff td {
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--line-2);
  vertical-align: middle;
}
.diff th { position: static; }          /* 展開在表格列裡，不需要跟著捲動 */
.diff tr:last-child td { border-bottom: 0; }
.diff tbody tr:hover, .diff tr:hover { background: none; }
.diff-col { color: var(--muted); white-space: nowrap; }
.diff-old { color: var(--muted); }      /* 舊值退到背景 */
.diff-new { color: var(--ink); font-weight: 600; }   /* 新值是重點 */
.diff-none { color: var(--muted); font-style: italic; }
.diff-img {
  width: 40px; height: 40px; object-fit: cover; vertical-align: middle;
  border-radius: var(--r-sm); border: 1px solid var(--line); margin-right: var(--sp-2);
}
.diff-file { font-family: var(--font-num); font-size: var(--fs-xs); color: var(--muted); }
.diff-list { display: block; line-height: 1.7; }   /* 品項清單，一行一筆 */

/* 語意文字色。動態判斷時用 <?= $x > 0 ? 't-in' : 't-out' ?> 接上去。 */
.t-in    { color: var(--in); }
.t-out   { color: var(--out); }
.t-move  { color: var(--move); }
.t-warn  { color: var(--warn); }
.t-muted { color: var(--muted); }

/* 表格合計列與警示列。原本各頁手寫 background + font-weight。 */
.tr--total > td { background: var(--panel-2); font-weight: bold; }
.tr--flag  > td { background: var(--warn-bg); }
.tr--sub   > td { background: var(--panel-2); }   /* 展開的明細列 */
.tr--off   > td { color: var(--muted); }          /* 已停用但仍要顯示的列 */

/* 篩選列裡的核取方塊。要和左右的輸入框對齊，所以往下推一個 label 的高度。 */
.chk-line { display: flex; align-items: center; gap: var(--sp-2); min-height: var(--h-ctl); }
/*
 * ⚠ 這裡必須把 .field input 的樣式整組還原，不能只設 width／height。
 *   .field input 給的是 width:100% + min-height:var(--h-ctl) + 邊框 + 白底 + 內距。
 *   只蓋 width 的話，min-height 仍然生效 —— 核取方塊會變成
 *   18px 寬、52px 高、帶著邊框和白底的一個大方框，非常醜。
 *   min-height 的優先權高於 height，光設 height 也壓不下去。
 */
/* 尺寸統一給 18px；把 .field input 那套還原的工作交給上面的
 * `.field input[type="checkbox"]` 通則，這裡不再重複一份。
 * .chk-line 也會用在 .field 之外（表格列裡），所以 min-height/border 仍要保險地歸零。 */
/*
 * 選擇器要寫到 [type=...] 才夠力：上面的 `.field input[type="checkbox"]` 是 (0,2,1)，
 * 只寫 `.chk-line input`（0,1,1）會被它的 width:auto 蓋掉，變回瀏覽器預設的 13px。
 * 補上屬性選擇器後兩者同分，再靠寫在後面取勝。
 */
.chk-line input[type="checkbox"], .chk-line input[type="radio"] {
  flex: 0 0 auto;                  /* .chk-line 是 flex，不固定住會被壓扁 */
  width: 18px; height: 18px; min-height: 0;
  padding: 0; border: 0; background: none; box-shadow: none;
  accent-color: var(--accent);
}
/* 表格列裡的版本：不吃 --h-ctl 那麼高，否則整列會被撐開 */
.chk-line--sm { min-height: 0; font-size: var(--fs-sm); }
.chk-line--sm input[type="checkbox"] { width: 15px; height: 15px; }

/* ---------- 圖片欄位（預覽＋換圖＋移除）----------
 * 原本「選擇圖片」和「目前圖片」是兩個分開的表單欄位，換圖時得在兩處之間
 * 來回對照，也沒有任何地方可以把圖片拿掉。合併成一塊之後三件事在同一個視線範圍內。 */
.field__lb {
  display: block; font-size: var(--fs-sm); font-weight: 500;
  color: var(--ink-2); margin-bottom: 6px;
}
.imgfield { display: flex; gap: var(--sp-4); align-items: flex-start; flex-wrap: wrap; }
.imgfield__preview {
  flex: 0 0 auto;
  display: grid; place-items: center;
  width: 132px; height: 132px;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--panel-2); overflow: hidden;
}
.imgfield__preview img { max-width: 100%; max-height: 100%; display: block; object-fit: contain; }
.imgfield__none { color: var(--muted); font-size: var(--fs-sm); text-align: center; line-height: 1.8; }
.imgfield__none .fa-solid { font-size: 26px; }
/* min-width: 0 讓右欄縮得回去，長檔名才不會把整塊撐出容器 */
.imgfield__side { flex: 1 1 260px; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-2); }

/* ---------- 欄寬 ----------
 * 掛在 <th> 上，整欄跟著走。原本散在各頁的 width:70px 之類都收到這裡。 */
.w-icon { width: 40px; }    /* 序號、勾選 */
.w-qty  { width: 70px; }    /* 數量 */
.w-num  { width: 90px; }    /* 金額、較大的數字 */
.w-code { width: 130px; }   /* SKU 碼、單號 */
.w-date { width: 130px; }   /* 日期 */
.w-time { width: 150px; }   /* 日期時間 */
.w-op   { width: 110px; }   /* 操作欄 */
.w-name { width: 200px; }   /* 姓名、名稱 */
.w-in   { width: 120px; }   /* 欄位裡放輸入框的那一欄 */
.w-btn  { width: 60px; }    /* 只放一顆小按鈕的那一欄 */

/* ---------- 表格列內的輸入框 ----------
 * 這些 input 不包在 .field 裡（會撐破表格列），所以另給一組較矮的樣式。 */
.in {
  min-height: var(--h-sm); padding: 0 var(--sp-2);
  border: 1px solid var(--line-3); border-radius: var(--r-sm);
  font: inherit; font-size: var(--fs-sm);
  color: var(--ink); background: #fff;
  transition: border-color var(--dur-2) var(--ease-out), box-shadow var(--dur-2) var(--ease-out);
}
.in:hover { border-color: var(--muted); }
.in:focus { outline: 2px solid var(--accent); outline-offset: 1px; box-shadow: var(--ring); }
.in:read-only { background: var(--panel-2); color: var(--ink-2); }
/* 表格／行內輸入框的錯誤態。訊息由頁首 alert 負責，這裡只標出是哪一格。 */
.in--err { border-color: var(--out); }
.in--err:focus { outline-color: var(--out); box-shadow: var(--ring-out); }
.in--xs   { width: 50px; }
.in--qty  { width: 80px; text-align: right; font-family: var(--font-num); }
.in--sm   { width: 90px; }
.in--md   { width: 130px; }
.in--lg   { width: 150px; }
.in--full { width: 100%; }
/* 在 .inline 裡撐滿剩餘寬度。min-width:0 是必要的，
 * 否則 input 的預設 size 會讓它拒絕縮小，把整列撐出容器。 */
.in--flex { flex: 1 1 auto; min-width: 0; }
.in--flex2 { flex: 2 1 auto; min-width: 0; }

/* ---------- 篩選列的欄位寬度 ----------
 * 篩選列的 .field 預設會撐滿，用這幾個收窄。 */
.f-xs { max-width: 120px; }
.f-sm { max-width: 150px; }
.f-md { max-width: 200px; }
.f-lg { max-width: 260px; }
.f-xl { max-width: 320px; }
.f-auto { width: auto; }

/* ==========================================================
 * 平板／手機
 *
 * 這一段解掉的是「整個頁面可以左右拖」的問題。成因幾乎都是
 * 某個元素比視窗寬又沒有自己的捲動容器，於是把 <body> 撐開，
 * 結果整頁跟著左右移動，選單和標題也一起跑掉。
 * 逐項堵住比在 body 加 overflow:hidden 好——後者只是把症狀藏起來，
 * 而且會讓 position: sticky 失效。
 * ========================================================== */
@media (max-width: 900px) {
  /*
   * ⚠ grid-template-rows 不能省。
   *
   * .layout 有 min-height: 100vh，橫過來之後變成「選單列 + 主內容」兩個 auto 列。
   * Grid 的 align-content 預設是 stretch，頁面內容不滿一個螢幕高時，
   * 剩下的空間會**平均分給這兩列**——選單列那一格被撐高，
   * 但選單本身只有 56px，下面就空出一大塊，看起來像頂部莫名變高。
   * 而內容夠長的頁面沒有剩餘空間可分，就不會發生，於是換頁時忽高忽低。
   *
   * 寫成 auto 1fr：選單列只佔自己的高度，多出來的空間全部給主內容。
   */
  .layout { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
  /*
   * 側邊選單在平板上改成橫向的一條。
   *
   * ⚠ 高度必須寫死，不能讓它自己長。
   *   這條是 overflow-x: auto，捲軸一出現就會多吃掉十幾像素的高度；
   *   而捲軸出不出現又取決於「該頁內容有沒有橫向溢出」——
   *   於是從單據查詢切到退貨清單、品牌管理、資料匯入時，這條就忽高忽低。
   *   固定高度 + 藏起捲軸（照樣可以用手指滑）就把這個變數整個拿掉了。
   */
  .sidebar {
    flex-direction: row; flex-wrap: nowrap;
    align-items: center;
    height: 56px; min-height: 56px;
    overflow-x: auto; overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    border-right: 0; border-bottom: 1px solid var(--line);
  }
  .sidebar::-webkit-scrollbar { display: none; }
  .sidebar__group, .sidebar__brand span, .sidebar__foot { display: none; }
  .sidebar__brand {
    flex: 0 0 auto;
    /* 直式時的上下內距是 24px，橫過來會超出 56px 的列高被裁掉，改成只留左右 */
    padding: 0 var(--sp-4);
    align-self: stretch; display: flex; align-items: center;
    border-bottom: 0; border-right: 1px solid var(--line); white-space: nowrap;
  }
  /* 橫向排列時膠囊照舊，只是不換行、外距收小。
     flex: 0 0 auto 讓它們維持原寬度往右排，而不是互相擠壓成一團。 */
  .sidebar a { flex: 0 0 auto; white-space: nowrap; margin: var(--sp-1); }
  .sidebar a:hover { transform: none; }
  .content, .wrap { padding: var(--sp-4); }
  .topbar { padding: var(--sp-3) var(--sp-4); flex-wrap: wrap; }
  /* 標題太長時讓它自己截斷，不要把右邊的登出區擠到第二行去——
     那會讓標題列在不同頁之間忽高忽低，跟選單列是同一種毛病。 */
  .topbar h1 { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  /*
   * iOS Safari 會在聚焦到字級小於 16px 的輸入框時自動放大整個頁面，
   * 而且放大後不會自己縮回去——使用者以為版面壞了。
   * 這是唯一的解法：手機上輸入類控制項一律 16px。
   */
  .field input, .field select, .field textarea, .in, .tabs button { font-size: 16px; }
}

@media (max-width: 700px) {
  /* 表格的橫向捲動、變更明細的 min-width、篩選欄寬上限，
     都已提前到上面的 1024px 區塊處理，這裡不再重複一份 */

  /* 卡片收窄留白、圓角收小，否則手機上內容區被吃掉太多 */
  .card { padding: var(--sp-4); border-radius: var(--r-lg); margin-bottom: var(--sp-4); }
  .content, .wrap { padding: var(--sp-3); }
  .stats { grid-template-columns: repeat(auto-fit, minmax(min(140px, 100%), 1fr)); gap: var(--sp-3); }
  .stat { padding: var(--sp-4); border-radius: var(--r-lg); }
  .tile { padding: var(--sp-6) var(--sp-4); border-radius: var(--r-xl); }

  /* SVG 會等比縮小，字跟著縮到讀不動。字級在 SVG 裡是使用者單位，放大就回來了。 */
  .chart__axis, .chart__cap, .chart__value { font-size: 15px; }
  .pie { justify-content: center; }

  .login__box { padding: var(--sp-5); }
  .toast { padding: var(--sp-5) var(--sp-6) var(--sp-5) var(--sp-5); font-size: var(--fs-lg); }
}

@media (max-width: 460px) {
  /* 確認浮層的兩顆鈕並排會擠成兩行字，直接改上下排 */
  .modal__foot { flex-direction: column; }
  .modal__foot .btn--ghost { min-height: var(--h-ctl); }
  /* 表單送出列：主鈕吃滿整列，才不會出現只有半顆寬的按鈕 */
  .actions > .btn { flex: 1 1 100%; }
}

/* ==========================================================
 * 前台：平板與手機
 *
 * 前台是站著操作、單手拿裝置、常常戴手套，跟後台坐著用滑鼠是兩種情境，
 * 所以這一段調的是「手指按得到、視線找得到」，不只是把版面塞進去。
 * ========================================================== */

/*
 * 觸控裝置一律加大控制項。用 pointer: coarse 而不是螢幕寬度判斷——
 * 13 吋的觸控平板螢幕不小，但手指的精度跟手機一樣，該放大的是它而不是手機而已。
 * body.big（大圖大字）會再蓋過這裡的值，兩者不衝突。
 */
@media (pointer: coarse) {
  :root { --h-ctl: 52px; --h-sm: 40px; }
  .btn { padding: 0 var(--sp-5); }
  .btn--sm { padding: 0 var(--sp-4); font-size: var(--fs-md); }
  /* 表格列裡的操作鈕擠在一起時很容易按錯隔壁列 */
  td .btn + .btn { margin-left: var(--sp-1); }
  .tile:hover { transform: none; }        /* 觸控沒有 hover，殘留的浮起效果只會閃一下 */
  .tile:hover em { transform: none; }
}

/*
 * 前台頂列：logo 與使用者區一行、功能選單自己一行並可橫向捲動。
 *
 * ⚠ 斷點是 1024px 不是 700px。iPad 直向的 CSS 寬度是 768px、橫向是 1024px，
 *   斷點壓在 700px 的話平板一個都吃不到，六個功能加上右側五個控制項全擠在同一行。
 *   平板才是前台的主力裝置，這個斷點要照著它抓。
 */
@media (max-width: 1024px) {
  .topbar--front { flex-wrap: wrap; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3); }
  .topbar--front .topbar__logo { font-size: var(--fs-lg); }
  .topbar--front .topbar__nav {
    order: 3; flex: 1 0 100%;
    padding-bottom: 2px;
  }
  .topbar--front .topbar__user { gap: var(--sp-1); font-size: var(--fs-sm); margin-left: auto; }
  .topbar__avatar { display: none; }
  /* 觸控裝置上頂列的連結也要按得到，但別大到把整列擠掉 */
  .topbar__link { min-height: 40px; padding: 0 var(--sp-2); }

  /*
   * 表格橫向捲動同樣要提前到 1024px。
   * 出庫頁的「最近出庫紀錄」有八欄，在 800px 寬的裝置上不捲動就會被壓成
   * 一格一個字（「里港A倉」直排四行），整張表沒辦法讀。
   * 代價是表頭 sticky 失效（任何 overflow 祖先都會破壞 sticky），這個取捨划算。
   */
  table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; }
  th { position: static; }

  /* ---- 共通的防破版保護 ----
   * 以下三項在平板就要生效，不能等到手機（700px）。
   * 平板寬度已經不夠放桌機版的固定尺寸，拖到手機才放開等於中間整段沒人管。 */

  /* 篩選欄的寬度上限會把欄位卡成一小格，字打不完也看不到，全部放開讓它自己排 */
  .f-xs, .f-sm, .f-md, .f-lg, .f-xl { max-width: none; }
  /* 操作紀錄的變更明細寫死 min-width: 340px，窄螢幕必定把卡片撐破 */
  .diff { min-width: 0; }
  /* 表格裡的固定寬輸入框（如強制釋放的原因欄）改成跟著欄位走 */
  .in--md, .in--lg { width: 100%; }

  /*
   * 長字串的斷行。
   * SKU 碼（S-BPH-R02BU-R05NU）、單號、物流單號這類連續英數不含空白，
   * 瀏覽器預設不會斷，一長就直接衝出格子或把整欄撐開。
   * anywhere 而不是 break-word：只有 anywhere 會一併縮小「最小內容寬度」，
   * 才擋得住 flex/grid 項目被硬撐開的情況。
   */
  td, th, .table--stack > tbody > tr > td { overflow-wrap: anywhere; }
  /* 欄名不要被拆開，值再長也不該把「物流單號」四個字折成兩行 */
  .table--stack > tbody > tr > td::before { overflow-wrap: normal; }

  /* ---- 卡片式表格 ----
   * 給欄位多的「紀錄」清單用（最近入庫／出庫／調撥／退貨、盤點紀錄、庫存查詢）。
   *
   * 這些表有六到九欄，窄螢幕上不管是硬擠還是橫捲都不好讀：
   * 硬擠會把「里港A倉」壓成直排四行，橫捲則要來回拉才拼得出一筆完整資料。
   * 改成一列一張卡，欄名由 td 的 data-label 補在左邊，一眼就看得完整筆紀錄。
   *
   * 表頭那一列直接隱藏：欄名已經跟著每一格走了，留著只是重複。
   * 這幾張表沒有寫 <thead>，第一列就是 th，所以用 :first-child 抓。 */
  .table--stack { display: block; overflow: visible; }
  /*
   * 卡片排成網格，寬度夠就並排。
   *
   * grid 要下在 tbody 上，不能下在 table 上：這幾張表沒有寫 <tbody>，
   * 但 HTML 解析器會自己補一個把所有 <tr> 包起來，
   * 於是 table 的 grid 只會看到「一個 tbody 子項」，卡片仍舊一列一張。
   *
   * auto-fill + minmax 讓欄數自己決定：約 800px 的平板兩欄、手機一欄，
   * 不必為每種尺寸各寫一個斷點。
   */
  .table--stack > tbody {
    display: grid;
    /*
     * min(300px, 100%) 不能簡寫成 300px。
     * minmax 的下限是硬下限：容器比 300px 還窄時（320px 的手機扣掉卡片與內容區留白
     * 只剩 260px 左右），軌道照樣佔 300px，整頁就被撐出橫向捲動。
     * 包一層 min() 之後，下限最多只到容器寬度，永遠不會溢出。
     */
    grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
    gap: var(--sp-3);
  }
  .table--stack > tbody > tr:first-child { display: none; }
  .table--stack > tbody > tr {
    display: block;
    padding: var(--sp-2) var(--sp-3);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: var(--panel);
    box-shadow: var(--sh-1);
  }
  /* 「查無資料」不是一筆紀錄，讓它橫跨整排、也不要卡片外框 */
  .table--stack > tbody > tr:has(td.empty) {
    grid-column: 1 / -1;
    border: 0; box-shadow: none; background: none; padding: 0;
  }
  /*
   * 展開的子表格（庫存查詢的「異動明細」）：橫跨整排，內容維持一般表格外觀。
   * 卡片規則全部用直接子代選擇器（> tbody > tr > td）就是為了這種巢狀表格——
   * 用後代選擇器的話，裡面那張表的每一格也會被變成卡片欄位，整塊變成一團。
   */
  .table--stack > tbody > tr:has(.td--sub) { grid-column: 1 / -1; }
  .table--stack > tbody > tr > td.td--sub {
    display: block; text-align: left; padding: var(--sp-2) 0;
  }
  .table--stack > tbody > tr > td.td--sub::before { content: none; }

  /* 合計列（庫存報表）同樣橫跨整排，並排到最後、用強調底色跟資料卡分開 */
  .table--stack > tbody > tr.tr--total {
    grid-column: 1 / -1;
    order: 1;
    background: var(--panel-2); font-weight: 700;
  }
  .table--stack > tbody > tr.tr--total > td:empty { display: none; }
  .table--stack > tbody > tr > td {
    display: flex; gap: var(--sp-4); flex-wrap: wrap;
    align-items: baseline; justify-content: space-between;
    border: 0; padding: 5px 0;
    text-align: right;              /* 值靠右、欄名靠左，數字才會對齊成一直行 */
  }
  /* 有些格子放的是「主值 + 補充說明」（安全庫存的『全倉 N』、低於門檻標籤）。
   * 那些補充是 <div>，在 flex 裡會被拉到同一行去，強制讓它獨佔一行。
   * <br> 在 flex 容器裡不產生換行，留著只會多一個空的匿名項目，直接隱藏。 */
  .table--stack > tbody > tr > td > div { flex: 1 0 100%; text-align: right; }
  .table--stack > tbody > tr > td > br { display: none; }
  .table--stack > tbody > tr > td + td { border-top: 1px dashed var(--line-2); }
  .table--stack > tbody > tr > td::before {
    content: attr(data-label);
    flex: 0 0 auto; min-width: 5.5em;
    color: var(--muted); font-size: var(--fs-sm); font-weight: 500;
    text-align: left;
  }
  /* 沒有 data-label 的格子（純按鈕、空白欄）不畫欄名，按鈕自己撐滿一列 */
  .table--stack > tbody > tr > td:not([data-label]) { display: block; text-align: left; }
  .table--stack > tbody > tr > td:not([data-label])::before { content: none; }
  /*
   * 操作欄：按鈕排成等寬網格。
   *
   * 用 grid 而不是 flex：flex 的 basis 跟著內容走，
   * 「明細 / 履歷」就會比「沖正」寬一倍，同一張卡裡兩顆鈕大小不一很難看，
   * 手指也抓不準。grid 的 1fr 是等分，不管標籤幾個字都一樣寬。
   * minmax 的下限讓欄數自己決定：放不下兩顆就自動換行，仍舊等寬。
   *
   * ⚠ 選擇器一定要寫成 td.td--act，不能只寫 .td--act。
   *   上面那條 `td:not([data-label])` 的優先權是 (0,2,1)，比 `.td--act` 的 (0,2,0) 高；
   *   操作欄剛好沒有 data-label，所以會被它的 display:block 蓋掉，grid 完全不會生效。
   *   補一個 td 讓兩者同分，再靠寫在後面取勝。
   */
  .table--stack > tbody > tr > td.td--act {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(130px, 100%), 1fr));
    gap: var(--sp-2);
    padding-top: var(--sp-3); text-align: left;
  }
  .table--stack > tbody > tr > td.td--act::before { content: none; }
  .table--stack > tbody > tr > td.td--act > * { min-width: 0; }   /* 標籤長的鈕才縮得回格子裡 */
  .table--stack > tbody > tr > td.td--act .btn {
    width: 100%;
    padding: 0 var(--sp-2);        /* 等寬之後左右內距用不到那麼多 */
    white-space: nowrap;           /* 寧可讓格子寬一點，也不要按鈕字被折成兩行 */
  }
  /* 查無資料那一列不該被當成卡片 */
  .table--stack > tbody > tr > td.empty { display: block; text-align: center; }
  .table--stack > tbody > tr > td.empty::before { content: none; }
  /* 卡片內不需要斑馬紋與 hover 底色，那是給列狀表格辨識行的 */
  .table--stack > tbody > tr:hover { background: var(--panel); }
}

/*
 * 出貨錄影浮層一律維持在右下角、維持原本大小（現場的習慣位置）。
 *
 * 曾經改成「貼齊底部的一整條」以避開內容，但現場反映位置與預覽大小都不對，
 * 已改回來。它與內容的重疊改用兩個手段處理，不動位置與尺寸：
 *   1. body:has(.rec-float) .wrap 的 padding-bottom —— 讓出底部空間，
 *      最後一張卡（完成出庫、最近出庫紀錄）不會被永久蓋住。
 *   2. 表格在 1024px 以下自己橫向捲動 —— 欄位不會被壓成一格一個字。
 * 中途捲動時右下角仍會被遮到一塊，那是常駐浮層的必然代價；
 * 需要看下面的內容時按標題列的「—」收合，收合狀態會被記住。
 */

@media (max-width: 700px) {
  /* 掃碼欄是前台的主角，手機上給它整行並加大 */
  .scan { font-size: 22px !important; }
}

@media (max-width: 520px) {
  /* 前台六顆磁磚單欄排會要捲三四頁；兩欄一眼看得完，仍然好按 */
  .tiles { grid-template-columns: repeat(2, 1fr); gap: var(--sp-3); }
  .tile { padding: var(--sp-5) var(--sp-2); border-radius: var(--r-xl); }
  .tile em { font-size: 34px; }

  /* .row 的 180px 下限在窄機上會逼出橫向捲動，直接讓每格獨佔一行 */
  .row > * { min-width: 100%; }
  .inline > .btn { flex: 1 1 auto; }      /* 按鈕組平分整列，不要出現半顆寬的鈕 */

  /*
   * 手機上的頂列右側：只留圖示。
   * 「登出」這種一看圖就懂的先收文字，倒數膠囊整個藏起來——
   * 它是資訊、不是操作，而畫面寬度要優先留給功能選單。
   * title 屬性還在，長按仍看得到說明。
   */
  .topbar--front .idle { display: none; }
  .topbar--front .topbar__link .topbar__lb { display: none; }
  .topbar--front .topbar__link { padding: 0 var(--sp-2); }
  .topbar__name { display: none; }
  /* logo 只留圖示，「倉庫作業」四個字讓位給功能選單 */
  .topbar--front .topbar__logo .topbar__lb { display: none; }
}

/* ---------- 列印（規格書要求隱藏選單與工具列） ---------- */
@media print {
  .sidebar, .topbar, .no-print, .btn { display: none !important; }
  body { background: #fff; }
  .content, .wrap { padding: 0; }
  /* 陰影與圓角在紙上只會印成髒灰邊，一律拿掉 */
  .card, .stat { border: 0; padding: 0; box-shadow: none; border-radius: 0; }
}
