:root {
    --cream: #FAF7F0;
    --cream-dark: #F0EBE0;
    --green: #4D8033;
    --green-dark: #3A6026;
    --gold: #F2B233;
    --pink: #E85A8A;
    /* Darker pink. `--pink` sits on white at ~3.3:1, which is fine for a
       large badge but fails 4.5:1 for small bold text — this is the shade
       used where the text itself has to be readable (see .attention-gm). */
    --pink-dark: #8C1D40;
    --ink: #331A20;
    --border: #E5DDD0;
    --card: #ffffff;
    /* Secondary text: timestamps, hints, counts. Was referenced all over
       the sheet as `var(--muted, #6b7280)` but never declared, so every
       use silently fell back and there was no single place to change it. */
    --muted: #6b7280;
    /* Form errors. Dark enough to read on cream and to pass 4.5:1 — an
       error message nobody can read is not an error message. */
    --error: #B3261E;
}

body {
    background: var(--cream);
    color: var(--ink);
    font-family: "Noto Sans TC", "Noto Sans CJK TC", "Microsoft JhengHei", system-ui, sans-serif;
    min-height: 100vh;
}

a {
    color: var(--green-dark);
}

a:hover {
    color: var(--green);
}

.navbar-yada {
    background: var(--green-dark) !important;
    box-shadow: 0 2px 8px rgba(51, 26, 32, 0.12);
}

.navbar-yada .navbar-brand {
    font-weight: 700;
    letter-spacing: 0.04em;
    color: #fff !important;
}

.navbar-logo {
    height: 75px;
    width: auto;
    background: #fff;
    border-radius: 6px;
    padding: 2px 8px;
    box-shadow: 0 1px 3px rgba(51, 26, 32, 0.25);
}

.navbar-yada .nav-link {
    color: rgba(255, 255, 255, 0.85) !important;
    font-weight: 500;
}

.navbar-yada .nav-link:hover,
.navbar-yada .nav-link.active {
    color: var(--gold) !important;
}


/* Legacy selector — the inline nav used Bootstrap `.nav-link`. The desktop
   nav is now `.header-pill` and the sidebar uses `.offcanvas-link`; both
   styles have their own blocks above and below. Kept here only as a guard
   for any page template still rendering a stray `.nav-link` inside the
   navbar. */


/* ------------------------------------------------------------------ navbar */


/* Sidebar-toggle hamburger icon.
   Default: white (Bootstrap's default stroke) on the dark green bar.
   On hover / focus-visible / active the surrounding `.btn-outline-light`
   fills in near-white — a white icon would vanish against that fill,
   so we flip to black for those states. Tying the flip to `:hover`,
   `:focus-visible`, and `:active` (not `:hover` alone) means a keyboard
   tab focus gets the same dark icon a mouse hover does, and a stuck
   `:focus` after a touch tap does not leave a black icon stranded on
   a still-white background. */

.navbar-yada .navbar-toggler-icon {
    /* default: let Bootstrap's white stroke show through */
}

.navbar-yada .btn:hover .navbar-toggler-icon,
.navbar-yada .btn:focus-visible .navbar-toggler-icon,
.navbar-yada .btn:active .navbar-toggler-icon {
    filter: invert(1);
}


/*
 * The bar collapses at 1023px (custom — Bootstrap's xl=1200 is too wide
 * for our needs), not Bootstrap's default lg (992px). Laid out
 * horizontally the inline row needs roughly 1,000px: four CJK group pills
 * (~520px with the "總覽" pill) + the horizontal logo at its full 75px
 * height (~300px) + the action controls (~280px). Between 992px and
 * 1,024px the pills would wrap or overflow, which reads as a broken bar
 * rather than a responsive one.
 */


/* Custom breakpoint utilities. Bootstrap doesn't ship one between `lg`
   (992) and `xl` (1200), so we define our own. `d-tablet-up` is
   `display: none` by default and `display: flex` from the breakpoint
   upward; `d-tablet-down` is the inverse. Used by the inline pill row and
   the sidebar toggle button respectively.

   ⚠️ 個 breakpoint 係 **1200px**，唔係 1024。呢個數係量度出嚟嘅，唔係
   揀出嚟嘅：

       英文版 navbar 需要 1009px（logo 164 + 6 粒 pill 605 + 右邊控制
       215 + padding 24）。而 Bootstrap 嘅 container 喺 992–1199px 之間
       只有 **936px** 可用（960 − 24）—— 差 73px。

   之前用 1024px 嘅話，1024–1199px 嗰段就必然爆。而 1200px 開始，container
   跳到 1140px（1116 可用），真正有位。`xl` 本身都係 1200，所以喺呢度
   用 `min-width: 1200px` 同 Bootstrap 自己嘅 tier 對齊 —— 唔會出現
   「我哋顯示咗 inline nav，但 container 仲係窄嗰個 tier」嘅矛盾。

   ⚠️ 兩者都要有**預設**嗰半邊。`d-tablet-down` 之前淨係喺 max-width 媒體
   查詢入面加 `display: block`，冇 base 嘅 `display: none` —— 所以 ≥1200px
   嗰邊佢乜規則都唔中，攞返 `.btn` 嘅 `inline-block`，**hamburger 一直
   出現喺 desktop**，食咗 39px + 8px gap（量過：actions 由 460px 縮到
   413px 就係靠隱藏佢）。`d-tablet-up` 之前靠 Bootstrap 嘅 `.d-none`
   頂住，順手一齊寫明，唔好再靠 cascade 運氣。

   ⚠️ 低過 1200px 嗰陣唔係「少咗功能」—— `#yadaSide` offcanvas 一直喺
   DOM 度（只係靠 `data-bs-toggle="offcanvas"` 開合），所以 hamburger
   一出現，導航就完整。代價係「總覽 / 五組 dropdown」要撳兩下先到。 */

.d-tablet-down {
    display: none;
}

@media (min-width: 1200px) {
    .d-tablet-up {
        display: flex !important;
    }
}

@media (max-width: 1199.98px) {
    .d-tablet-down {
        display: block !important;
    }
    /* Stacked layout, so the logo no longer competes with the menu for
       vertical space — 75px of branding above a phone menu is most of
       the viewport. */
    .navbar-yada .navbar-logo {
        height: 44px;
    }
}


/* ⚠️ 1200–1399px 呢段闊度：**hamburger 一定要出現**。
 *
 * 呢段闊度嘅「＋新增文件」掣係收埋嘅（下面 `.header-new-doc` 嗰條
 * media query）—— 因為全掣要 1209px，而 container 喺呢個 tier 只有
 * 1116px 可用。收埋咗之后如果仲唔俾 hamburger 出現，呢段就**完全冇
 * 開新單嘅入口**：掣收埋咗、hamburger 又喺 ≥1200px 隱藏 —— 1280×800
 * 嘅 laptop（每一部都喺呢段）就係咁。
 *
 * ⚠️ 之前呢度**冇**呢條規則，而下面嗰段註解仲寫住「hamburger 喺呢段
 * 闊度係顯示嘅」—— 嗰句係**錯**嘅（hamburger 只喺 <1200px 出現），所以
 * 個缺陷可以躲咗咁耐。
 *
 * 闊度夠唔夠？收埋 CTA 之后內容要 1063px（量度見上面「header 闊度預算」
 * 嗰段），加返 hamburger（39px + 8px gap = 47px）就係 1110px ——
 * 1200px 嗰邊有 1116px 可用，仲有 6px 餘裕。≥1400px container 跳到
 * 1320px，CTA 又出現，所以 hamburger 喺嗰段繼續收埋（呢條 media query
 * 唔覆蓋嗰段）。 */

@media (min-width: 1200px) and (max-width: 1399.98px) {
    .d-tablet-down {
        display: block !important;
    }
}


/* ── 單據列表嘅動作欄（2026-10-22）──────────────────────────────────
 *
 * 「下一步」欄最多四粒掣、「查看／PDF／編輯／複製」欄又係四粒，全部
 * `btn-sm` 但**加埋**就得好闊。喺 auto-layout 嘅 table 入面，欄闊係由
 * **最闊嗰行嘅內容**決定 —— 即係最闊嗰行唔摺，個欄就一路撐開，
 * 1280px 嘅 laptop 就要橫向 scroll，金額／狀態嗰幾欄跌出畫面。
 *
 * → 限制闊度，令掣摺埋（`flex-wrap` 已經有 ✓）。
 * 260px ≈ 兩粒「上傳簽回」咁闊 —— 大部分行只有一兩粒掣，唔會有分別。
 */
.doc-actions {
    max-width: 260px;
}

/* 掣嘅標籤**唔准摺行**。
 *
 * 點解：`.doc-actions` 係 `flex-wrap`，所以粒掣會**自己**換行（正確
 * 做法）。但如果粒掣嘅**文字**仲要摺 —— 「上傳收貨回條」變成「上傳收貨」
 * ＋「回條」兩行 —— 粒掣就變成一格高一倍嘅怪嘢，睇落似壞咗。
 *
 * ⚠️ 2026-10-25 撞到：送貨單列表加咗「送貨日期」「相關發票」兩欄之後，
 * 個表變闊，「下一步」欄喺 390px 得 113px —— 兩粒掣嘅文字各自摺成
 * 兩行，格仔由 2 行變 4 行（真瀏覽器量到，唔係估）。
 *
 * `nowrap` 唔會令粒掣「爆」出格仔：`.doc-actions` 嘅 `flex-wrap` 令
 * 佢哋上下排，表格喺細屏本來就喺 `.table-responsive` 入面橫向捲動 ✓。
 */
.doc-actions .btn {
    white-space: nowrap;
}


/* ----------------------------------------------------- header inline link */


/* The navbar's `.container` **keeps Bootstrap's width** — the header is
   aligned to the same grid as the page content, not full-bleed.

   ⚠️ 曾經改成 `max-width: none`（滿版）為咗塞落成條 navbar，代價係 header
   嘅左右邊緣同內文嘅 1140px 網格對唔齊。呢個係設計決定，唔係 bug ——
   所以回復原狀。

   回復之後要分清楚**兩個**唔同嘅約束，之前一直撈埋一齊：

     · **container 闊度**：992–1199px → 960px，1200–1399px → 1140px，
       ≥1400px → 1320px。（Bootstrap 自己嘅 tier，唔係我哋揀嘅。）
     · **navbar 內容需要**：1063px（logo 164 + 6 粒 pill 605 + 右邊控制
       214 + padding 24）。

   所以真正嘅問題唔係「內容幾闊」，而係「container 有幾闊」：
   1199px viewport 得 936px 可用，內容要 1009px（差 73px）；1200px 開始
   container 跳到 1140px（1116 可用），內容要 1063px（有 53px 餘裕）。

   ⚠️ 呢個分別好重要，因為 1185px 嗰個數字係**滿版時代**嘅量度值（當時
   logo 仲係 219px、`+ New document` 仲顯示緊）。如果照搬落嚟，會得出
   「要 1216px 先夠」嘅錯誤結論，然後將 breakpoint 訂錯。改 label 同
   縮 role badge 之後，真實需求已經跌到 1063px。

   ⚠️ `flex-wrap: nowrap` + 兩邊 `flex-shrink: 0` 仍然必需：冇咗佢哋，
   `.ms-auto` 嘅 `margin-left: auto` 喺冇剩餘空間時 collapse 成 0，
   兩個 box 一齊被 shrink 到細過內容，而 pill 有 `nowrap` 縮唔到，
   就會**溢出自己個 box 疊喺 actions 上面**（實測重疊過 460px）。 */

.navbar-yada > .container {
    flex-wrap: nowrap;
}


/* ⚠️ `flex-shrink: 0` **只可以**喺 ≥1200px 生效 —— 即係同 `.d-tablet-up`
   一齊出現嘅闊度。

   原因：<1200px 嗰邊 `.header-groups` 係 `display: none`，但 `.ms-auto`
   （語言切換 + 新增文件 + 用戶 dropdown）**照樣顯示**。如果喺所有闊度都
   加 `flex-shrink: 0`，375px 嗰陣 actions 拒絕收窄，內容闊過屏幕 →
   橫向 overflow → Chrome mobile emulation 將 visual viewport 撐闊，
   `window.innerWidth` 由 375 變 406（`tests/browser/test_harness.py`
   `test_the_viewport_can_actually_be_resized` 就係捉到呢個）。

   簡單講：`flex-shrink: 0` 係為咗「desktop 唔好重疊」而加，但喺 mobile
   反而會製造 overflow。兩個 breakpoint 綁埋，就唔會互相打架。 */

@media (min-width: 1200px) {
    .navbar-yada .header-groups,
    .navbar-yada > .container > .ms-auto {
        flex-shrink: 0;
    }
}


/* ⚠️ 呢度**唔可以**加 `overflow-x: auto`（曾經加過，已移除）。
   每一個 group dropdown 嘅 menu 都係 `position: absolute`，而**任何有
   `overflow` 嘅祖先都會 clip 絕對定位嘅後代**。加咗之後 menu 照樣被
   Bootstrap 開（button 會有 `.show`、`aria-expanded="true"`），但 menu
   本身喺 pill row 個 box 之外，被完全剪走 —— 症狀係「撳落去乜都冇」，
   而且**冇任何 console error**，所以極難察覺。

   順帶一提：想靠 `overflow-x: auto` + `overflow-y: visible` 呢個常見組合
   都係唔得 —— CSS 規格規定其中一個 axis 唔係 `visible` 嗰陣，另一個
   `visible` 會被計成 `auto`，所以冇辦法做到「淨係横向捲動、唔 clip 垂直
   方向」。

   真正嘅兜底唔係捲動，而係 media query：闊度真係唔夠（<1200px）嗰陣，
   `.d-tablet-up` 已經令成排 pill 收埋，offcanvas sidebar 接管導航。 */


/* Pills and group buttons that live on the desktop navbar (≥1200px). Sit
   between the brand and the actions group; share the gold active treatment
   so the visual language matches what the sidebar uses.

   英文版 label 比中文長，所以 padding 同 font-size 都收緊少少。
   `nowrap` 防止長 label 包到第二行 —— 包到第二行嘅話 navbar 高度會
   突然撐高，而且嗰粒 pill 嘅 hover 底色會蓋到隔籬。 */

.navbar-yada .header-pill {
    color: rgba(255, 255, 255, 0.85);
    font-weight: 500;
    font-size: 0.9rem;
    text-decoration: none;
    padding: 0.4rem 0.65rem;
    border-radius: 999px;
    background: transparent;
    border: 0;
    line-height: 1.2;
    white-space: nowrap;
}

.navbar-yada .header-pill:hover,
.navbar-yada .header-pill.active {
    color: var(--gold);
    background: rgba(242, 178, 51, 0.12);
}

.navbar-yada .header-pill:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
}


/* Group dropdown caret: smaller than Bootstrap default so it sits flush
   with the CJK label rather than jutting out. */

.navbar-yada .header-pill.dropdown-toggle::after {
    margin-left: 0.4em;
    vertical-align: middle;
}


/* The group row sits in the brand's flex line; give the group buttons a
   small left margin so they read as a cluster rather than a single bar.

   0.15rem 係英文版嘅需要：5 粒 pill × 兩個 margin 之間嘅 0.25em = 約
   10px。喺 1280px 嘅 laptop 上面，呢 10px 已經決定「成條 header 啱啱好」
   定係「admin 跌咗落第二行」。 */

.navbar-yada .header-groups {
    gap: 0.15rem;
}


/* Dropdown menu that opens from a group pill. Dark so it sits on the same
   green bar without a hard edge; items match the pill colour scheme. */

.navbar-yada .header-group-menu {
    margin-top: 0.4rem;
    background: var(--green-dark);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 12px;
    padding: 0.5rem;
    min-width: 12rem;
}

.navbar-yada .header-group-menu .offcanvas-group {
    display: contents;
}

.navbar-yada .header-group-menu .offcanvas-item {
    padding: 0;
}


/* One item = a rounded slot, separated from its neighbours by 6px of empty
   space. No hairline dividers: with a 3px gap and a hover tint already
   carrying the grouping, a rule just adds a second, redundant seam and
   makes the menu read like a table.

   Row height ≈ 40px (0.95rem at 1.4 line-height ≈ 21px of text + 9px of
   padding top and bottom). Desktop, not a phone, so it stops well short of
   the 73px the sidebar uses — that number is a WCAG touch-target figure for
   thumbs and looks absurd under a mouse cursor.

   `display: block` is load-bearing, not decoration. Without it these <a>s are
   inline boxes: vertical padding paints but does not grow the line box, and
   vertical margin is ignored entirely, so every item overlaps its neighbour
   by ~35px and the real row pitch collapses to the 21px text line. The
   sidebar's equivalent rule (`.offcanvas.yada-side .offcanvas-link`) has had
   `display: block` all along; this one was simply missing it.

   Vertical *margin* rather than extra padding because the margin sits outside
   the painted slot: it is the gap you actually see, and it cannot be eaten by
   a Bootstrap rule that re-declares `padding` on dropdown items.

   Specificity note: the selector carries three class names (`.navbar-yada
   .header-group-menu .offcanvas-link`) on purpose. Bootstrap ships a
   single-class rule on its dropdown items that would otherwise out-cascade
   us if our class name were generic. The extra two ancestors guarantee
   we win without needing `!important`, which we then leave clear for
   overrides (e.g. the active state below). */

.navbar-yada .header-group-menu .offcanvas-link {
    display: block;
    color: rgba(255, 255, 255, 0.85);
    text-decoration: none;
    padding: 0.5625rem 0.75rem;
    border-radius: 6px;
    font-size: 0.95rem;
    line-height: 1.4;
    margin: 3px 0;
    /* Reserved on every item, not just the active one, so switching pages
       does not shift the labels sideways. */
    border-left: 3px solid transparent;
}

.navbar-yada .header-group-menu .offcanvas-link:hover {
    color: var(--gold);
    background: rgba(255, 255, 255, 0.06);
}

.navbar-yada .header-group-menu .offcanvas-link:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: -2px;
}

.navbar-yada .header-group-menu .offcanvas-link.active {
    color: var(--gold);
    background: rgba(242, 178, 51, 0.12);
    border-left-color: var(--gold);
}


/* User dropdown toggle (right side, e.g. "Administrator") should never
   wrap a long display_name to a second line: that breaks the single-row
   navbar rule and pushes the role-badge off the visible row. Tighten the
   padding here instead of relying on Bootstrap's `btn-sm` default, which
   is sized for narrow phones, not a 32px-tall header. */

.navbar-yada .dropdown>.btn.dropdown-toggle {
    padding: 0.25rem 0.65rem;
    white-space: nowrap;
    max-width: 14rem;
    overflow: hidden;
    text-overflow: ellipsis;
}


/* "+ New document" button: trim a bit so it doesn't push the user dropdown
   past the right edge on a 1280px screen. */

.navbar-yada .header-new-doc {
    padding-left: 0.65rem;
    padding-right: 0.65rem;
}


/* Hide the "+ New document" duplicate when the header is tight.

   ⚠️ 呢條規則嘅範圍經量度之後大幅收窄咗。容器回復 Bootstrap 原生闊度
   之后，`+ New document` 只喺**一個**區間會爆：1200–1399px（container
   = 1140px，可用 1116px，而全掣需要 1209px）。

     · <1200px —— pill row 已經收埋，`.ms-auto` 得三粒細掣（~215px），
                   1024px 嗰陣都仲有位，冇理由收起捷徑
     · 1200–1399px —— 收埋。呢度正正就係「有 pill row 但唔夠位」嗰個
                   空隙：收埋之後省返 146px，內容由 1209 跌到 1063，
                   喺 1200px 都仲有 53px 餘裕。
     · ≥1400px —— container 跳到 1320px，顯示。

   收埋咗之後功能**冇失**：offcanvas（`#yadaSide`）嗰邊有一份一模一樣嘅
   CTA，而喺呢段闊度 hamburger（`.d-tablet-down`）**係顯示嘅** —— 呢個唔係
   「本來就顯示」，而係上面嗰條 `@media (min-width:1200px) and
   (max-width:1399.98px)` 特意補返嘅。

   ⚠️ 之前呢段註解寫住「喺呢段闊度 hamburger 係顯示嘅」，但嗰時
   hamburger 只喺 <1200px 出現 —— 兩樣嘢一齊收埋，於是 1280px 嘅 laptop
   **完全冇開新單嘅入口**（開單表單要撳 sidebar 入面嗰份 CTA，而
   sidebar 本身又開唔到）。 */

@media (max-width: 575.98px), (min-width: 1200px) and (max-width: 1399.98px) {
    .navbar-yada .header-new-doc {
        display: none;
    }
}


/* --------------------------------------------------------- offcanvas side */

.offcanvas.yada-side {
    background: var(--cream);
    color: var(--ink);
    width: min(320px, 86vw);
}

.offcanvas.yada-side .offcanvas-header {
    border-bottom: 1px solid var(--border);
    background: var(--green-dark);
    color: #fff;
}

.offcanvas.yada-side .offcanvas-title {
    color: #fff;
    font-weight: 700;
}

.offcanvas.yada-side .offcanvas-body {
    padding: 0;
}

.offcanvas.yada-side .offcanvas-cta {
    padding: 1rem;
    border-bottom: 1px solid var(--border);
    background: rgba(77, 128, 51, 0.06);
}


/* Top-level "總覽" link in the sidebar. Full-width and padded so it does
   not look like a group label by mistake. */

.offcanvas.yada-side .offcanvas-link {
    display: block;
    padding: 0.75rem 1rem;
    color: var(--ink);
    text-decoration: none;
    border-left: 3px solid transparent;
}

.offcanvas.yada-side .offcanvas-link:hover {
    background: rgba(77, 128, 51, 0.06);
    color: var(--green-dark);
}

.offcanvas.yada-side .offcanvas-link.active {
    border-left-color: var(--gold);
    background: rgba(242, 178, 51, 0.12);
    color: var(--green-dark);
}


/* Accordion inside the sidebar. The `.accordion-flush` removes the default
   rounded borders, which matches the panel style; we re-add a hairline
   between groups so each group still reads as its own region. */

.offcanvas.yada-side .yada-side-accordion .accordion-item {
    border-bottom: 1px solid var(--border);
}

.offcanvas.yada-side .accordion-button {
    font-weight: 700;
    color: var(--green-dark);
    background: transparent;
    padding: 0.875rem 1rem;
}

.offcanvas.yada-side .accordion-button:not(.collapsed) {
    background: rgba(77, 128, 51, 0.06);
    color: var(--green-dark);
    box-shadow: none;
}

.offcanvas.yada-side .accordion-button:focus {
    box-shadow: 0 0 0 0.2rem rgba(77, 128, 51, 0.2);
}

.offcanvas.yada-side .accordion-body {
    padding: 0.25rem 0 0.75rem;
}

.offcanvas.yada-side .offcanvas-item {
    padding: 0;
}

.offcanvas.yada-side .offcanvas-item .offcanvas-link {
    /* Same ~74px hit target as the desktop dropdown (20px padding + 6px
       margin on each side + text). The margin sits outside the link and
       guarantees the gap survives any cascade order that might zero the
       padding. On a phone, the accordion collapses to the relevant group
       on tap, so a user rarely scrolls past 1.5 panels. */
    padding: 1.25rem 1rem 1.25rem 1.5rem;
    font-size: 0.95rem;
    border-top: 1px solid rgba(51, 26, 32, 0.06);
    margin: 6px 0;
}

.offcanvas.yada-side .offcanvas-item:first-child .offcanvas-link {
    border-top: 0;
}

@media (max-width: 575.98px) {
    .navbar-yada .navbar-logo {
        height: 38px;
    }
}

.page-title {
    font-weight: 800;
    color: var(--green-dark);
    letter-spacing: 0.04em;
}

.page-title::after {
    content: "";
    display: block;
    width: 40px;
    height: 3px;
    margin-top: 6px;
    background: linear-gradient(90deg, var(--gold), var(--pink));
    border-radius: 2px;
}

.card-yada {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: 0 1px 3px rgba(51, 26, 32, 0.04);
}

.card-yada .card-header {
    background: rgba(77, 128, 51, 0.06);
    border-bottom: 1px solid var(--border);
    font-weight: 700;
    color: var(--green-dark);
    border-radius: 12px 12px 0 0 !important;
}

.stat-card {
    border-left: 4px solid var(--green);
    transition: transform 0.15s ease;
}

.stat-card:hover {
    transform: translateY(-2px);
}

.stat-card .stat-num {
    font-size: 1.75rem;
    font-weight: 800;
    color: var(--green-dark);
}

.stat-card.pink {
    border-left-color: var(--pink);
}

.stat-card.gold {
    border-left-color: var(--gold);
}

.btn-yada {
    background: var(--green);
    border-color: var(--green);
    color: #fff;
    font-weight: 600;
}

.btn-yada:hover {
    background: var(--green-dark);
    border-color: var(--green-dark);
    color: #fff;
}

.btn-outline-yada {
    border-color: var(--green);
    color: var(--green-dark);
    font-weight: 600;
}

.btn-outline-yada:hover {
    background: var(--green);
    border-color: var(--green);
    color: #fff;
}

.btn-pink {
    background: var(--pink);
    border-color: var(--pink);
    color: #fff;
    font-weight: 600;
}

.btn-pink:hover {
    background: #d14a78;
    border-color: #d14a78;
    color: #fff;
}

.badge-draft {
    background: #6c757d;
}

.badge-issued {
    background: var(--green);
}

.badge-paid {
    background: #2c7a7b;
}

.badge-signed {
    background: #b7791f;
}

.badge-converted {
    background: var(--gold);
    color: var(--ink);
}

.badge-received {
    background: #8e44ad;
}

.badge-other {
    background: #7f8c8d;
}

.badge-void {
    background: var(--pink);
}

/* ── 佣金狀態（2026-10-29）────────────────────────────────────────────────
   佣金單嘅三個狀態：草稿／已確認／已作廢。

   ⚠️ `badge-confirmed` 之前**冇**呢條規則 —— `badge-{{ status }}` 砌出嚟
   嘅 class 冇底色，個 badge 會白底黑字，喺列表入面睇落似普通文字
   （同 `badge-rejected` 當年嘅情況一模一樣）。

   顏色刻意同 `badge-paid` 分開：`badge-paid` 係「錢收到」，佣金呢邊
   「已確認」只係「呢個數定咗」，未派錢 —— 兩者唔係同一件事，
   用同一個顏色會令人以為佣金已經派咗。 */
.badge-confirmed {
    background: var(--green);
}


/* ── 拒絕類狀態 ────────────────────────────────────────────────────────────
   呢兩個係 2026-10-12 流程補完時加嘅狀態，之前連 CSS 都未有，所以
   `badge-{{ status }}` 砌出嚟嘅 class 完全冇底色 —— 一張「已拒絕報價」
   嘅單喺列表入面睇落同普通文字冇分別。

   顏色刻意**唔等於** `badge-void`（粉紅）：作廢係「呢件事唔算」，
   拒絕係「客戶明確講咗唔」。要分得開係因為喺 `services.py` 嘅狀態機
   入面兩者嘅下一步完全唔同 —— `rejected` 可以返 `issued`（傾返），
   `void` 係終點。用戶睇 badge 就應該睇得出邊個仲有得救。

   `--pink-dark` 而唔係 `--pink`：呢兩個係「需要人做決定」嘅狀態，
   文字要讀得到 —— `--pink` 喺白底只有約 3.3:1，細字唔夠。 */

.badge-rejected,
.badge-payment_rejected {
    background: var(--pink-dark);
    color: #fff;
}

.badge-type {
    background: rgba(77, 128, 51, 0.12);
    color: var(--green-dark);
    font-weight: 600;
}

/* 「已過有效期」badge —— 報價單專用。
   之前 `valid_until` 全 repo 只有 `todos.py` 讀過，所以一份過咗期嘅報價
   喺「報價」清單入面同仲有兩個月嘅**完全一樣** —— 分唔出邊份要跟。
   呢個 badge 就係嗰個分別。

   用琥珀色而唔係紅色：紅色喺呢個 app 入面已經代表「要即刻處理嘅錯」
   （`badge-rejected`）。過期唔係錯，係「要問客 要唔要延期」—— 權重低
   一級，但唔可以完全冇標記。 */
.badge-expired {
    background: #fff3cd;
    color: #7a5c00;
    border: 1px solid #ffe08a;
    font-weight: 600;
}


/* ── 催款狀態 ──────────────────────────────────────────────────────────────
   顏色唔係裝飾，係今張單嘅處理優先次序：紅色（應承咗冇畀）＝錢已經有人
   應承過而甩咗底，最需要即刻追；琥珀（催咗冇回覆）＝正常跟進；綠（等緊
   應承）＝唔好煩住客；灰＝未開始。
   settled 故意用另一隻綠，係想同「等緊客戶應承」一眼分得開 —— 一個係
   「咗」，一個係「仲未有」。 */

.badge-chase-no_chase {
    background: #6c757d;
}

.badge-chase-promised {
    background: rgba(77, 128, 51, 0.12);
    color: var(--green-dark);
    font-weight: 600;
}

.badge-chase-promise_broken {
    background: var(--pink);
}

.badge-chase-chased {
    background: #b7791f;
}

.badge-chase-settled {
    background: var(--green);
}


/* ── 關注等級 ──────────────────────────────────────────────────────────────
   呢個係「呢張單應該由邊個親自把關」，唔係「呢張單有幾舊」—— 所以刻意
   唔沿用催款狀態嗰套紅／琥珀色，一來兩者唔同意思（催款狀態講錢，關注
   講權責），二嚟同一行同時出現兩個紅 badge，使用者分唔到邊個嚴重。

   主管用琥珀、總經理用深紅加 ▲：▲ 唔係裝飾 —— 色盲嘅人睇唔到深紅同
   琥珀嘅分別，但一定睇到有冇三角形。badge 嘅文字亦都各自寫住等級名，
   所以就算完全睇唔到顏色都讀得明白。 */

.attention-supervisor {
    background: #b7791f;
    color: #fff;
}

.attention-gm {
    background: var(--pink-dark);
    color: #fff;
    font-weight: 700;
}

.step-badge {
    display: inline-block;
    padding: 4px 12px;
    border-radius: 999px;
    background: rgba(77, 128, 51, 0.12);
    color: var(--green-dark);
    font-size: 0.85rem;
    font-weight: 600;
}

.step-badge.done {
    background: var(--green);
    color: #fff;
}

/* ⚠️ **表格一定要有 `min-width`**（2026-10-24 修 —— 手機版面崩潰）。

   之前呢度冇 `min-width`，於是喺窄屏（390px）嗰陣：

   * 個表嘅 `width: 100%` 撞 `.table-responsive` 嘅 390px 闊度；
   * 但內容嘅 **min-content** 總和（≈490px）大過 390px，所以個表**溢出**
     咗個容器（呢個係 `.table-responsive` 預期嘅行為，佢會橫向捲動）；
   * 瀏覽器攞唔到多餘空間分欄，就**每欄都俾佢 min-content 嘅闊度**。

   中文嘅 min-content 係「**一個字**」（因為中文字可以逐個斷行）。
   於是「天主教聖若瑟小學（粉嶺）」嗰欄闊 **44px** —— 即係一個欄得
   擺一個字，十二個字要行十二行；一行單據嘅高度由 48px 爆到 **216px**。
   同一個原因，動作掣（「標記已發出」）得 42px 闊，文字**逐個字豎住**
   疊（42 × 115px）。

   實測（Chrome，390px viewport，`/receipts`）：

       客戶欄      x=71  w=44    ← 一個欄得擺一個中文字
       標記已發出   x=424 w=42 h=115  ← 五個字豎住疊
       睇「查看」掣 x=482 w=45 h=52   ← 兩個字斷開兩行

   `min-width` 一加上，瀏覽器就有多餘空間分欄，中文欄闊返正常，
   `.table-responsive` 橫向捲動就變成「正常嘅表格喺細屏睇」——
   橫向捲動係預期嘅，但**唔應該**要逐個字睇。

   900px 嘅由來：桌面版（1280px）個表實際內容約 1100px；900px 已經
   令每欄都有返正常嘅闊度（客戶欄 ≈ 180px，夠擺一個完整校名），
   又唔會迫住手機用家捲太遠。 */
.table-yada {
    min-width: 900px;
}

/* 收據列表多咗兩欄（「付款方式」「相關發票」，2026-10-24 加），所以要
   多啲闊度先唔會再出現上面講嘅「一個欄得擺一個中文字」。

   ⚠️ **點解唔係一個通用嘅 `.table-yada-wide` 值**？因為呢個表嘅實際
   內容寬度**淨係喺收據頁**係咁。其他類型頁（報價／發票／送貨）嘅欄數
   唔同，共用一個數值就會令佢哋**唔使捲動嘅時候都要捲**。

   ⚠️ **1080 嘅由來**（2026-10-24 實測）：`/receipts` 喺 1280px viewport
   入面，`.table-responsive` 嘅 `clientW` = **1114px**（Bootstrap `.container`
   1140 − padding − card border）。欄位自然寬度加埋 = **1181px**，所以
   `min-width` 如果係 1180，個表就會**喺桌面版都溢出 66px** → 橫向捲動
   條喺 1280px 嘅螢幕上面**唔應該出現**（有位就唔應該迫住人捲）。

   1080 < 1114，所以喺 1280 之下 `.table` 嘅 `width: 100%` 生效（1114px，
   啱啱好塞得落，冇捲動條）；喺 390px 之下就係 1080px → 橫向捲動，
   但每欄仲係有正常嘅闊度（客戶欄 ≈ 154px，唔會再變成 44px 嗰種
   「一個欄得擺一個中文字」）。 */
.table-yada-wide {
    min-width: 1080px;
}

.table-yada thead th {
    background: var(--green-dark);
    color: #fff;
    font-weight: 600;
    font-size: 0.875rem;
    border: none;
    white-space: nowrap;
}

.table-yada tbody td {
    vertical-align: middle;
    border-color: var(--border);
}

.table-yada tbody tr:hover {
    background: rgba(77, 128, 51, 0.04);
}


/* ---------------------------------------------------- filter / sort / paging */

/* 可搜尋嘅客戶選單（`_client_picker.html`）。

   ⚠️ **設計規矩：個欄位只可以有一個見到嘅控件。**
   輸入框用 Bootstrap 嘅 `.form-select` 外觀（連右邊個 ▼ 箭嘴都係），
   所以**睇落就係一個普通下拉**，只不过可以打字。原生 `<select>` 收埋
   喺背後（`hidden`），但仍然提交個值。

   ⚠️ `position: relative` 係因為下面嗰張建議面板要絕對定位喺個框
   下面 —— 篩選 bar 個格仔得咁闊，插入去（普通 flow）會將成個 bar
   推高好多，就變成嗰個「好架怪」嘅排版。 */
.client-picker {
    margin-bottom: 4px;
    position: relative;
}

/* 個框 + 清除掣嘅定位層。清除掣要疊喺 ▼ 箭嘴上面。 */
.client-picker__control {
    position: relative;
}

.client-picker__search {
    /* 機構名可能好長（學校全名），唔好界到半個字都見唔到。 */
    min-width: 0;
    /* 未有揀中嘢之前，右邊個 ▼ 箭嘴係「撳落去會開個下拉」嘅暗示，
       所以用 pointer；一 focused 就變返文字游標。 */
    cursor: pointer;
}

.client-picker__search:focus {
    cursor: text;
}

/* ⚠️ 已經揀咗客 → 個 ▼ 箭嘴由「×」清除掣取代。
   兩個都喺右邊，唔移走箭嘴嘅話會疊埋一齊（似壞咗）。 */
.client-picker.is-filled .client-picker__search {
    background-image: none;
    padding-right: 1.75rem;
}

.client-picker__clear {
    position: absolute;
    right: .4rem;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    width: 1.15rem;
    height: 1.15rem;
    line-height: 1;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: #6c757d;
    font-size: 1rem;
    cursor: pointer;
}

.client-picker__clear:hover {
    background: #e9ecef;
    color: #212529;
}

/* ── 建議面板 ───────────────────────────────────────────────────────────
   舊版係「由 DOM 移走唔啱嘅 `<option>`」，過濾之後畫面冇任何回饋。
   而家有一張真嘅、可見嘅建議列表 —— 學校名冊有近兩千間同名嘅學校，
   見到結果先有信心揀得啱。 */
.client-picker__panel {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    /* 同 Bootstrap dropdown 一致，唔好被卡片嘅 stacking context 蓋住。 */
    z-index: 1050;
    margin-top: 2px;
    background: #fff;
    border: 1px solid rgba(0, 0, 0, .15);
    border-radius: .25rem;
    box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15);
    overflow: hidden;
}

.client-picker__results {
    max-height: 18rem;
    overflow-y: auto;
    margin: 0;
    padding: 0;
    list-style: none;
}

.client-picker__result {
    display: flex;
    align-items: baseline;
    gap: .35rem;
    padding: .3rem .5rem;
    cursor: pointer;
    font-size: .9rem;
    line-height: 1.35;
}

/* 方向鍵游標。`is-active` 同 `:hover` 同一個樣 —— 鍵盤同滑鼠嘅
   「而家揀緊呢個」應該睇落一樣，唔好用戶以為 keyboard 壞咗。 */
.client-picker__result.is-active,
.client-picker__result:hover {
    background: #eef2f7;
}

.client-picker__result-main {
    font-weight: 600;
}

/* 部門 · 分區 · 電話。分區係分同名學校嘅關鍵（每區都有「聖公會聖三
   一堂中學」），所以一定要同一行見到，唔係要撳先見到。 */
.client-picker__result-meta {
    color: #6c757d;
    font-size: .82rem;
}

/* 「已封存」badge。封存 ≠ 刪除 —— 搵到得到，但要一眼睇出唔接新單。 */
.client-picker__result-archived {
    margin-left: auto;
    padding: 0 .35rem;
    border-radius: .25rem;
    background: #e2e3e5;
    color: #41464b;
    font-size: .72rem;
    white-space: nowrap;
}

/* 過濾後「一個都冇剩」嘅提示。唔顯示嘅話，用戶會以為客戶唔見咗。 */
.client-picker__none {
    padding: .35rem .5rem;
}

/* 面板底部：數量 + 「顯示全部客戶」逃生門。 */
.client-picker__panel-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    padding: .25rem .5rem;
    border-top: 1px solid rgba(0, 0, 0, .08);
}

/* 「顯示全部客戶」逃生門。 */
.client-picker__show-all {
    font-size: .8rem;
    text-decoration: none;
    white-space: nowrap;
}

/* 錯誤訊息。空嘅時候唔佔位（唔好撐高成個欄位）。 */
.client-picker__error:empty {
    display: none;
}

.client-picker__error {
    margin-top: 2px;
}

/* ⚠️ **`hidden` 一定要贏**。

   原生 `<select>` 收埋咗，但佢有 `.form-select` class —— 而 Bootstrap
   嘅 `.form-select` 有 `display: block`。Author style 大過 UA style，
   所以淨係加 `hidden` 屬性係**唔夠**嘅：個 select 會照樣出現喺畫面
   （`hidden` 形同冇寫），於是又變返「兩個可見控件」。

   同理，面板、清除掣、「冇符合」提示都靠 `hidden` 收埋。 */
.client-picker__native[hidden],
.client-picker [hidden] {
    display: none !important;
}

.filter-bar .form-label {
    font-size: 0.8rem;
    margin-bottom: 2px;
}

.filter-statuses {
    display: flex;
    flex-wrap: wrap;
    gap: 2px 12px;
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 6px 10px;
    background: #fff;
}

.filter-check {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 0.85rem;
    white-space: nowrap;
    cursor: pointer;
}

.filter-check input {
    margin: 0;
}

.filter-summary {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 6px;
    align-items: center;
}

.filter-chip {
    background: rgba(77, 128, 51, 0.12);
    color: var(--green-dark);
    border-radius: 999px;
    padding: 2px 10px;
    font-size: 0.8rem;
    font-weight: 600;
}


/* Sortable headers sit on the dark green thead, so the link colour has to
   invert there and stay green elsewhere (empty-state, other tables). */

.table-yada thead .sort-link {
    color: #fff;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 3px;
}

.table-yada thead .sort-link:hover {
    color: var(--gold);
}

.table-yada thead .sort-link.active {
    color: var(--gold);
    font-weight: 700;
}

.sort-arrow {
    font-size: 0.9em;
}

.pagination-yada-wrap {
    border-top: 1px solid var(--border);
    padding: 10px 14px;
}

.pagination-yada {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    align-items: center;
    justify-content: space-between;
}

.pagination-yada .page-link {
    color: var(--green-dark);
}

.pagination-yada .page-item.active .page-link {
    background: var(--green);
    border-color: var(--green);
    color: #fff;
}

.pagination-yada .page-item.disabled .page-link {
    color: #9aa;
}

.form-label {
    font-weight: 600;
    color: var(--green-dark);
    font-size: 0.9rem;
}

.form-control:focus,
.form-select:focus {
    border-color: var(--green);
    box-shadow: 0 0 0 0.2rem rgba(77, 128, 51, 0.2);
}


/* Invalid fields.

   Two things force these rules to exist here rather than relying on
   Bootstrap alone:

   1. The `.form-control:focus` rule above is declared in this sheet, so
      it and Bootstrap's `.form-control.is-invalid` end up at the same
      specificity and the winner is decided by source order — not by which
      state the field is actually in. An invalid field the user is editing
      would be outlined in the "everything is fine" green.
   2. `:user-invalid` is the honest way to mark a field the user has
      interacted with and left wrong. `:invalid` alone paints every empty
      `required` box red the moment the form loads, including the ones the
      user has not reached yet — a form that starts by accusing you of
      twenty mistakes is a form people stop trusting.

   Nothing in the app sets `is-invalid` yet; validation errors currently
   arrive only in the flash banner. These rules are here so that when a
   field-level message is rendered it looks right from the first time,
   rather than needing a stylesheet written after the templates. */

.form-control.is-invalid,
.form-select.is-invalid,
.form-control:user-invalid,
.form-select:user-invalid {
    border-color: var(--error);
    background-image: none;
}

.form-control.is-invalid:focus,
.form-select.is-invalid:focus,
.form-control:user-invalid:focus,
.form-select:user-invalid:focus {
    border-color: var(--error);
    box-shadow: 0 0 0 0.2rem rgba(179, 38, 30, 0.2);
}

.invalid-feedback {
    color: var(--error);
}

.line-row {
    background: var(--cream);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 12px;
    margin-bottom: 8px;
}

/* Line rows wrap controls inside `<label class="form-label">` so cloned
   rows (no unique ids) stay accessible. Labels are inline by default, so
   a nested `.form-control { width: 100% }` only ever sized to the browser's
   intrinsic textarea width (~300px) instead of the Bootstrap column. */
.line-row .form-label {
    display: block;
    width: 100%;
}


/* Price warning under a line's unit-price box. Amber for "the catalogue
   moved since this template was saved"; grey for "this product is retired" —
   retired is not an error, just information, so it should not shout. */

.line-notice {
    font-size: 0.75rem;
    margin-top: 4px;
    color: #92400e;
    font-weight: 600;
}

.line-notice-retired {
    color: var(--muted);
    font-weight: 500;
}


/* ── 黐住底部嘅儲存列 ──────────────────────────────────────────────────────
   `documents/form.html` 由第 29 行開到第 261 行，儲存掣喺最底。用戶填完單號、
   搵客戶、掛附件、加備註，然後由頂撳到尾先見到「有得儲存」—— 如果佢以為
   下面仲有嘢要填，佢就會喺一個已經填好嘅單上面撳離開，幾分鐘功夫冇。

   做唔做重複嘅儲存掣？唔做。同一個 form 上面有兩個儲存入口 = 兩個真相：
   將來有人改一個唔改另一個，用戶撳錯嗰個就以為儲咗。嗰條 bar 係**黐住**
   視窗底部，內容喺佢後面捲過去，所以「搵唔到」呢個問題係真解決咗，
   唔係搬走咗。

   背景點解唔係實色？因為呢個 class 兩邊都用得：users/templates/documents
   個 form 直接坐喺米色頁面（`--cream`）上面，但 settings/clients/products
   個 form 坐喺白色卡片（`--card` = #fff）嘅 `.card-body` 入面。實色只能
   揀其中一種 —— 另一邊就會出現一條色差，而且係喺**注意力最後停留嘅
   地方**（頁底）。用半透明 + blur：米色底上 ≈ `--cream`，白卡上 ≈ 白，
   兩邊都睇唔出接縫，背後捲過去嘅文字又唔會淨係黐住條 bar。 */

.save-bar {
    position: sticky;
    bottom: 0;
    z-index: 10;
    margin-top: 1.5rem;
    padding: .75rem 0;
    background: rgba(250, 247, 240, .97);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    border-top: 1px solid var(--border);
    /* 陰影向上掃，令條 bar 同內容之間有一條界線 —— 純靠 border 嘅話，
       白卡上面嗰個位好淡，內容會好似「漏咗入咗 bar 下面」。 */
    box-shadow: 0 -2px 10px rgba(51, 26, 32, .08);
}


/* 唔好用 `:focus-within` 去加 padding 遷就 focus ring：bar 一被 focus
   就會長高一截，掣跟住跳，聚焦嘅時候個目標移動咗 —— 對靠 keyboard
   撳嘢嘅人嚟講，呢個比 ring 被切走更傷。`.75rem` 已經夠 15px 嘅 focus
   ring 企喺掣下面。 */


/* ---------------------------------------------- chrome-free pages (login) */


/*
 * A centred flex column, not a Bootstrap .row. Making <main> itself
 * `d-flex` (the first attempt) breaks .row inside it: a flex parent turns
 * .row into a flex item, so its negative --bs-gutter-x margins stop
 * cancelling and `align-items: center` shrinks the column to the card's
 * content width instead of letting col-md-5 / col-lg-4 apply. Hence one
 * plain wrapper doing the centring, and the grid untouched below it.
 */

.auth-main {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2rem 1rem;
}

.auth-wrap {
    width: 100%;
    max-width: 26rem;
}


/* The logo ships as dark ink on transparent, so it needs no chip here the
   way .navbar-logo does on the green navbar. Sized off the artwork's real
   content box (~3.95:1) rather than the file's padded 2.86:1, or it renders
   smaller than it looks specified. */

.auth-brand {
    text-align: center;
    margin-bottom: 1.5rem;
}

.auth-logo {
    height: 56px;
    width: auto;
    max-width: 100%;
}

.auth-tagline {
    margin: 0.5rem 0 0;
    font-size: 0.8rem;
    letter-spacing: 0.18em;
    color: rgba(51, 26, 32, 0.5);
}


/* The card is the page's only real content, so it does not need to be
   wider than the form inside it; a hairline of breathing room keeps the
   card from butting straight into the brand block on short viewports. */

.auth-card {
    width: 100%;
}

@media (max-width: 576px) {
    .auth-logo {
        height: 44px;
    }
    .auth-main {
        padding: 1.5rem 1rem;
    }
}

.footer-yada {
    margin-top: auto;
    padding: 1.25rem 0;
    font-size: 0.8rem;
    color: rgba(51, 26, 32, 0.55);
    border-top: 1px solid var(--border);
}


/* The blank layout has a single centred footer line now (the DB/PDF engine
   label was internal plumbing, not something a signed-out visitor needs),
   so centre it rather than leaving it stranded on the left. The main
   layout keeps its two-column footer and is unaffected. */

.auth-blank .footer-yada .container {
    text-align: center;
}

.footer-yada strong {
    color: var(--green);
}

.flash-ok {
    background: rgba(77, 128, 51, 0.12);
    border: 1px solid var(--green);
    color: var(--green-dark);
    border-radius: 8px;
}

.flash-err {
    background: rgba(232, 90, 138, 0.12);
    border: 1px solid var(--pink);
    color: #8b1e45;
    border-radius: 8px;
}

@media (max-width: 768px) {
    .stat-card .stat-num {
        font-size: 1.4rem;
    }
    .table-responsive {
        font-size: 0.875rem;
    }
}


/* ---------------------------------------------------------------- reports */


/*
 * The trend chart is CSS bars, not Chart.js. Phase 0.5 moved Bootstrap off
 * the CDN because the LAN has no internet; a charting library from a CDN
 * would reintroduce exactly that failure. Twelve bars need no tooltips —
 * the numbers are printed under each column anyway.
 */

.trend {
    display: flex;
    align-items: flex-end;
    gap: 4px;
    height: 180px;
    overflow-x: auto;
}

.trend-col {
    flex: 1 1 0;
    min-width: 42px;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    height: 100%;
}

.trend-bars {
    display: flex;
    align-items: flex-end;
    justify-content: center;
    gap: 2px;
    flex: 1 1 auto;
    min-height: 0;
}


/* min-height rather than height:0 — a month with no billing still needs a
   visible (if empty) column, or the chart silently closes the gap and the
   zero reads as "no data" rather than "nothing billed". */

.trend-bar {
    width: 45%;
    min-height: 1px;
    border-radius: 3px 3px 0 0;
    transition: height 0.2s ease;
}

.trend-bar.billed {
    background: var(--green);
}

.trend-bar.collected {
    background: var(--gold);
}

.trend-label {
    font-size: 0.7rem;
    color: var(--ink);
    opacity: 0.65;
    text-align: center;
    margin-top: 4px;
}

.trend-nums {
    display: flex;
    justify-content: center;
    gap: 4px;
    font-size: 0.65rem;
    color: var(--ink);
    opacity: 0.65;
}

.trend-legend {
    display: flex;
    gap: 16px;
    align-items: center;
    margin-top: 12px;
    font-size: 0.8rem;
    color: var(--ink);
    opacity: 0.65;
}

.trend-legend .dot {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 2px;
    margin-right: 4px;
}

.trend-legend .dot.billed {
    background: var(--green);
}

.trend-legend .dot.collected {
    background: var(--gold);
}


/* Individual invoices listed under their ageing column. Indented so it
   reads as belonging to the row above rather than as its own entry. */

.ageing-detail td {
    font-size: 0.82rem;
    padding-top: 0;
    padding-bottom: 0.5rem;
    border-top: 0;
}


/* ── 今日要清單 ────────────────────────────────────────────────────────────
   六個 bucket 並排，每個自己一個框。框線刻意**唔係**紅色 —— 一個要追嘅
   單同一個「冇填到期日」都係同一個紅框嘅話，使用者會將兩者當成同級 urgency，
   而實際上後者只係自己人嘅手民工作。

   每個 bucket 最多 `BUCKET_LIMIT`（6）條，所以三欄 × 兩行剛好一屏，
   唔使捲。 */

.todo-bucket {
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 10px 12px;
    background: var(--card);
}

.todo-bucket-head {
    font-weight: 600;
    font-size: 0.875rem;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--border);
    margin-bottom: 6px;
}

.todo-list {
    list-style: none;
    padding: 0;
    margin: 0;
    font-size: 0.875rem;
}

.todo-list li {
    padding: 4px 0;
    /* 相鄰兩條之間畫一條線，但最後一條唔畫 —— 否則個框底部會有一條
       多餘嘅線，同個 header 嘅分隔線連埋一齊，睇落好似一個表。 */
    border-bottom: 1px solid var(--cream-dark);
}

.todo-list li:last-child {
    border-bottom: 0;
}

.todo-empty {
    color: var(--muted);
    padding: 2px 0;
}

.badge-count {
    background: var(--cream-dark);
    color: var(--ink);
}


/* 每個附件下面嗰條收起嘅「標籤／描述／連結」控制列。 */

.attachment-meta>summary {
    cursor: pointer;
    font-size: 0.8125rem;
    color: var(--muted);
    list-style-position: inside;
    padding: 0.25rem 0.5rem;
}

.attachment-meta>summary:hover {
    color: var(--ink);
}


/* `summary` 預設有個 disclosure triangle，Bootstrap 嘅 focus ring 又係
   outline:none —— 兩樣都令鍵盤用戶見唔到自己喺邊。所以自己畫返個
   focus 樣式。 */

.attachment-meta>summary:focus-visible {
    outline: 2px solid var(--green);
    outline-offset: 2px;
}


/* ── Navbar 角色標記 ──────────────────────────────────────────────────────
   擺喺 navbar 個用戶掣隔籬，畀用戶唔開 dropdown 都即時知道自己嘅角色。
   三色刻意用晒已存在嘅 var，三種組合嘅對比度都過 WCAG AA：
   admin     ── 白底＋`--green-dark` 字 ≈ 9.4:1
   finance   ── `--gold`（#F2B233）底＋`--ink`（#331A20）字 ≈ 7.5:1
   frontdesk ── `--pink-dark`（#8C1D40）底＋白字 ≈ 8.7:1
   唔用 Bootstrap `.badge` 因為 padding / line-height 同 `btn-sm` 唔夾。 */

.role-badge {
    display: inline-flex;
    align-items: center;
    font-size: 0.75rem;
    font-weight: 600;
    line-height: 1;
    /* 英文版個 badge 已經縮成 "Admin"/"Fin"/"FD"（見 i18n_ui.py 嘅
       `nav.role_*`），所以 padding 收窄到 0.4rem —— 三隻字嘅 badge 用
       0.6rem 會睇落鬆散，而且喺 navbar 咁闊嘅預算度每一像素都緊。 */
    padding: 0.3rem 0.4rem;
    border-radius: 999px;
    letter-spacing: 0.02em;
    white-space: nowrap;
    border: 1px solid transparent;
}

.role-badge-admin {
    background: #fff;
    color: var(--green-dark);
}

.role-badge-finance {
    background: var(--gold);
    color: var(--ink);
}

.role-badge-frontdesk {
    background: var(--pink-dark);
    color: #fff;
}


/* ── AI 助手 chat ───────────────────────────────────────────────────────────
   跟 `.card-yada`／`.navbar-yada` 嘅 palette：`--cream` 背景、`--card` 對話
   框、`--green-dark` 用戶泡泡、`--gold` 邊框線（讀屏測試對比已 4.5:1）、
   `--pink` 錯誤泡泡（借返 `.badge-void`）。`.chat-msg-error` 同 alert
   區域用同一個 `--pink`，咁用戶見到粉紅就知「壞咗」。

   高度、字型同字體大細跟 `documents/form.html` 嘅 text input
   （`rows="3"`，字體同 body 一致）—— chat 嘅輸入框唔可以突然比
   文件表單嘅細，會令人以為呢個係次要入口。 */

.chat-log {
    min-height: 240px;
    max-height: 60vh;
    overflow-y: auto;
    padding: 1rem;
    background: var(--cream);
    border-radius: 0 0 12px 12px;
    /* 視覺上同 `card-yada` 嘅白 card 分開：對話區唔係「白紙」，係「布幕」，
       訊息泡泡就喺布幕上面 —— 兩層色令訊息彈出嚟。 */
}

.chat-log .chat-empty {
    color: var(--muted);
    text-align: center;
    padding: 2rem 0;
}

.chat-msg {
    display: flex;
    margin-bottom: 0.75rem;
}

.chat-msg-body {
    max-width: 80%;
    padding: 0.6rem 0.85rem;
    border-radius: 14px;
    line-height: 1.45;
    /* 內容可能係 LLM 輸出嘅純文字，但 inline 換行有意義（保留 `\n`）。 */
    white-space: pre-wrap;
    word-wrap: break-word;
    /* 對話入面嘅連結（雖然 JS 用真 <a>，但 bot 寫入 URL 都會 clickable）。
       顏色用 `--green-dark` 跟 navbar active。 */
    overflow-wrap: anywhere;
}

.chat-msg-user {
    justify-content: flex-end;
}

.chat-msg-user .chat-msg-body {
    background: var(--green-dark);
    color: #fff;
    border-bottom-right-radius: 4px;
}

.chat-msg-bot {
    justify-content: flex-start;
}

.chat-msg-bot .chat-msg-body {
    background: var(--card);
    color: var(--ink);
    border: 1px solid var(--border);
    border-bottom-left-radius: 4px;
}

.chat-msg-error .chat-msg-body {
    background: rgba(232, 90, 138, 0.12);
    color: #8b1e45;
    border: 1px solid var(--pink);
    border-bottom-left-radius: 4px;
}

/* ── Markdown 渲染出嚟嘅元素（`chat/index.html` 嘅 `renderMarkdown`）──────
 *
 * ⚠️ 呢個 renderer **只**支援一個安全子集（粗體／斜體／等寬／清單／
 * 標題），而且**完全冇**用 innerHTML —— 所以呢度只需要排版，唔需要
 * 任何 sanitiser。
 */
.chat-msg-body p {
    margin: 0 0 0.4rem 0;
}

.chat-msg-body p:last-child {
    margin-bottom: 0;
}

.chat-msg-body ul,
.chat-msg-body ol {
    margin: 0.2rem 0 0.45rem 0;
    padding-left: 1.15rem;
}

.chat-msg-body li {
    margin-bottom: 0.15rem;
}

.chat-msg-body code {
    background: rgba(0, 0, 0, 0.06);
    border-radius: 4px;
    padding: 0.05rem 0.3rem;
    font-size: 0.92em;
}

/* 「助手正在度⋯⋯」嗰個泡泡：淡啲、斜體，令用戶知道呢個**唔係**答案。 */
.chat-msg-typing .chat-msg-body {
    opacity: 0.72;
    font-style: italic;
}

.chat-links {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0 0 0.75rem 0;
    padding-left: 0.5rem;
}

/* ── Tool 結果卡（`chat/index.html` 嘅 `renderTable`）─────────────────────
 *
 * 摺疊（`<details>`）係因為一次 `document_list` 可以有 20 行：永遠展開
 * 會將助手真正嘅回覆推出視窗。用戶想要詳情就撳一下。
 */
.chat-table {
    margin: 0 0 0.75rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--card);
}

.chat-table-summary {
    cursor: pointer;
    padding: 0.4rem 0.6rem;
    font-size: 0.8125rem;
    color: var(--muted);
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.chat-table-title {
    font-weight: 600;
    color: var(--ink);
}

.chat-table-more {
    font-size: 0.75rem;
    color: var(--muted);
}

/* `max-height` + `overflow` 係因為 tool 結果可以有幾十行。唔設嘅話一張
   20 行嘅表會令成個對話區變到要捲好耐先見到下一句回覆。 */
.chat-table-grid {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.8125rem;
    display: block;
    max-height: 18rem;
    overflow: auto;
}

.chat-table-grid th,
.chat-table-grid td {
    padding: 0.3rem 0.6rem;
    text-align: left;
    border-bottom: 1px solid var(--border);
    /* 單號／金額／備註全部可能好長；唔限闊就會撐爆整張表，
       令其他欄被迫收窄到睇唔到。 */
    white-space: nowrap;
}

.chat-table-grid th {
    position: sticky;
    top: 0;
    background: var(--cream);
    font-weight: 600;
    color: var(--ink);
}

.chat-table-grid td {
    color: var(--ink);
}

@media (max-width: 575.98px) {
    .chat-table {
        margin-left: 0;
    }
}

.chat-compose {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 1rem;
    box-shadow: 0 1px 3px rgba(51, 26, 32, 0.04);
}

.chat-compose .form-control:focus {
    /* 跟 `.form-control:focus` 嘅綠 ring 一致，唔好突然轉色。 */
    border-color: var(--green);
    box-shadow: 0 0 0 0.2rem rgba(77, 128, 51, 0.2);
}

.chat-compose .form-control:disabled {
    /* 停用嘅輸入框用 cream 而非預設灰：同 `.card-yada` 嘅白底比較，
       用戶仲可以睇到呢個輸入框存在（純灰底會被當成「走咗」）。
       對比仍然 4.5:1+（`--ink` 對 `--cream-dark`）。 */
    background: var(--cream-dark);
    color: var(--muted);
}

.chat-compose kbd {
    display: inline-block;
    padding: 1px 6px;
    font-size: 0.75rem;
    background: var(--cream-dark);
    border: 1px solid var(--border);
    border-radius: 4px;
    color: var(--ink);
}

@media (max-width: 575.98px) {
    /* 手機：對話區縮短、輸入區更貼近底部 thumb —— 鍵盤彈出時輸入框
       仍然睇得到。60vh 喺 360×640 嘅機太經 700px 高度（接近 65%）。
       收緊到 45vh 留返半屏俾輸入。 */
    .chat-log {
        max-height: 45vh;
    }
    .chat-msg-body {
        max-width: 92%;
    }
}


/* ------------------------------------------------------------ lang switch */


/*
   語言切換器：navbar 右邊嗰對「中 / EN」。
   分段（segmented）造型而唔係兩個獨立 pill —— 語言係一個**互斥選擇**，
   同一個時間只有一個成立，而 segmented control 正正就係表達「揀一個」嘅
   慣例。用兩個普通 pill 會令用戶以為佢哋係兩個獨立功能。

   對比度：非選中態白字 `rgba(255,255,255,0.85)` 喺深綠 navbar 上面係
   ~8:1；選中態金色 `--gold` 配 `rgba(242,178,51,0.14)` 底 ~7:1。
   兩個都過 WCAG AA（4.5:1）嘅門檻。
*/

.lang-switch {
    display: inline-flex;
    border: 1px solid rgba(255, 255, 255, 0.35);
    border-radius: 999px;
    overflow: hidden;
}

.lang-switch-btn {
    color: rgba(255, 255, 255, 0.85);
    font-size: 0.8rem;
    font-weight: 500;
    line-height: 1;
    text-decoration: none;
    padding: 0.35rem 0.6rem;
    min-width: 2.25rem;
    text-align: center;
}

.lang-switch-btn:hover,
.lang-switch-btn:focus {
    color: var(--gold);
    background: rgba(242, 178, 51, 0.12);
}

.lang-switch-btn.active {
    color: var(--gold);
    background: rgba(242, 178, 51, 0.18);
}


/* ---------------------------------------- 單據詳情頁：主內容下面嗰堆卡 */
/*
   2026-10-26：單據詳情頁（`documents/detail.html`）嘅版面改咗。
   主內容（電郵／流程／QR／附件／附錄／**項目明細**／備註／記事／單號）
   由 `col-lg-8` 改成**全闊** `col-12`；原本喺右邊嘅四張卡（摘要／分期
   進度／收款記錄／催款記錄）跌落主內容下面，由 `.doc-lower` 呢個 grid 排。

   點解唔用 Bootstrap 嘅 `col-lg-4` / `col-lg-8`？
   ---------------------------------------------
   Bootstrap 嘅 breakpoint 係**睇螢幕闊度**，唔係睇**容器**闊度。一旦
   將「收款記錄」卡收窄成 1/3，裡面原本喺全闊下設計嘅表單（6 個
   `col-md-2` = 螢幕嘅 1/6）仲係會攤成 1/6，但嗰張 1/3 闊嘅卡每欄只剩
   73px —— 日期／金額輸入框即刻爆。CSS grid 嘅 `1fr 2fr` 係按**呢個 grid
   自己**嘅闊度分配，唔受 viewport 影響。

   分法（lg = 992px 以上）：
     * 摘要      → 第 1 欄（1fr，約 1/3）。佢個 `dl` 係 `col-5`/`col-7`，
                    label 佔 41% —— 窄啱；闊咗反而「客戶」兩個字霸住
                    550px，右邊一大截空白。
     * 分期進度  → 第 2 欄（2fr，約 2/3）。
     * 收款記錄  → 兩欄都佔（付款表 + 6 欄表單要闊）。
     * 催款記錄  → 兩欄都佔（追款表 + 5 欄表單要闊）。

   ⚠️「分期進度」卡**淨係**喺 `quote_billing` / `related_quote_billing`
   有值時先出現（理由見 `detail.html` 嗰段註釋）⇒ 好多單（發票／收據／
   送貨單）根本冇嗰張卡，嗰陣摘要得 1/3 闊、右邊 2/3 係空白 ——
   呢個係**刻意**嘅：用戶 2026-10-26 揀咗「照留空白」。重點係上面嗰條
   長描述（項目明細）要闊，唔係下面要填滿。

   ⚠️ `align-items: start`：grid row 嘅高度由**內容**決定，唔係 stretch
   —— stretch 會將「分期進度」拉成同「摘要」一樣高，卡下面走一大截空白。
*/

.doc-lower {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
    align-items: start;
}

/* 預設每張卡都佔晒成行。`margin-bottom: 0` 係因為卡本身有 `mb-3` ——
   留住嘅話同一個 gap 之下會變兩倍空白（卡自己嘅 margin + grid gap）。 */
.doc-lower > * {
    grid-column: 1 / -1;
    margin-bottom: 0;
}

@media (min-width: 992px) {
    .doc-lower {
        grid-template-columns: 1fr 2fr;
    }

    /* TODO(你嚟調)：摘要同分期之間嘅分界。現時 `1fr 2fr`（摘要 1/3）。
       另外兩個方向同代價：
         * `1fr 1fr`   —— 摘要同分期一樣闊；但分期嗰條關聯單清單會窄咗。
         * `320px 1fr` —— 摘要固定 320px，餘下全部畀分期；極闊螢幕下
                         「分期」嘅條目唔會散開，但 992–1000px 嗰段會逼。
       改呢一行就得 —— 記得同上面嘅說明一齊改，唔好淨係改數字。 */
    .doc-lower > .doc-summary {
        grid-column: 1;
    }

    .doc-lower > .doc-instalment {
        grid-column: 2;
    }
}
