/* =====================================================================
   "Still Water" display theme (LIB-SPEC render §"Display theme", locked
   2026-08-29). The full theme ships here; conf::$css stays a thin
   per-project overlay (accent hex, logo) — not a place to re-fork
   components. Phase A = tokens + palette (this block). Density, motion,
   busy/saved states, print and the small-screen floor land in later
   phases and are NOT in this block yet.
   ===================================================================== */

/* Noto Sans (+ Thai) — one family, self-hosted (SIL OFL), latin + thai
   subsets, weights 400/500/600/700. No Google Fonts <link>: offline
   deploys render fully. */
@font-face{font-family:'Noto Sans';font-style:normal;font-weight:400;font-display:swap;src:url(font/NotoSans-latin-400.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Noto Sans';font-style:normal;font-weight:500;font-display:swap;src:url(font/NotoSans-latin-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Noto Sans';font-style:normal;font-weight:600;font-display:swap;src:url(font/NotoSans-latin-600.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Noto Sans';font-style:normal;font-weight:700;font-display:swap;src:url(font/NotoSans-latin-700.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Noto Sans Thai';font-style:normal;font-weight:400;font-display:swap;src:url(font/NotoSansThai-thai-400.woff2) format('woff2');unicode-range:U+02D7,U+0303,U+0331,U+0E01-0E5B,U+200C-200D,U+25CC}
@font-face{font-family:'Noto Sans Thai';font-style:normal;font-weight:500;font-display:swap;src:url(font/NotoSansThai-thai-500.woff2) format('woff2');unicode-range:U+02D7,U+0303,U+0331,U+0E01-0E5B,U+200C-200D,U+25CC}
@font-face{font-family:'Noto Sans Thai';font-style:normal;font-weight:600;font-display:swap;src:url(font/NotoSansThai-thai-600.woff2) format('woff2');unicode-range:U+02D7,U+0303,U+0331,U+0E01-0E5B,U+200C-200D,U+25CC}
@font-face{font-family:'Noto Sans Thai';font-style:normal;font-weight:700;font-display:swap;src:url(font/NotoSansThai-thai-700.woff2) format('woff2');unicode-range:U+02D7,U+0303,U+0331,U+0E01-0E5B,U+200C-200D,U+25CC}

:root {
    /* --- neutrals & ground (faint blue bias, never pure black/white) --- */
    --bs-body-bg:            #F7F9FB;
    --bs-body-color:         #1C2733;
    --bs-secondary-color:    #566677;
    --bs-tertiary-bg:        #F1F5F9;
    --bs-border-color:       #E2E8EF;
    --bs-emphasis-color:     #1C2733;   /* one dark ink — Still Water has no near-black; BS tables/‹strong› default to this */

    /* --- one accent, two weights + an ink for 4.5:1 text --- */
    --lds-accent:        #4A93D1;   /* ambient: rings, active nav, chart fill */
    --lds-accent-strong: #2E7CC2;   /* the one primary button per view       */
    --lds-accent-ink:    #235E96;   /* accent-coloured text on light         */
    --lds-accent-weak:   #EAF2FA;   /* active-row / selected tint            */
    --bs-primary:        #2E7CC2;
    --bs-primary-rgb:    46, 124, 194;
    --bs-link-color:     #235E96;
    --bs-link-color-rgb: 35, 94, 150;
    --bs-link-hover-color: #1B4A78;

    /* --- state colours (meaning only) --- lightened 12% toward white from
       the v4 draft (#2F8F5B/#B8791C/#C0453C); white button text stays
       ≥3:1 (large/UI text). */
    --bs-success: #489C6F; --bs-success-rgb: 72, 156, 111;
    --bs-warning: #C18937; --bs-warning-rgb: 193, 137, 55;
    --bs-danger:  #C85B53; --bs-danger-rgb:  200, 91, 83;

    /* --- named hues (not state, just colour a BA can pick) --- */
    --lds-lilac:     #8F7CC4;   /* solid btn-lilac, white text ≥3:1 */
    --lds-lilac-ink: #5E4F86;   /* lilac text/ink on a light tint   */

    /* --- topbar chrome tint --- */
    --lds-topbar-bg:     #CFE1F1;
    --lds-topbar-border: #B4CCE0;
    --lds-topbar-ink:    #3C5468;

    /* --- surface tokens (used by later phases) --- */
    --lds-ground:   #F7F9FB;
    --lds-surface:  #FFFFFF;
    --lds-ink:      #1C2733;
    --lds-ink-soft: #566677;
    --lds-line:     #E2E8EF;
    --lds-shadow:   0 1px 2px rgba(27, 58, 92, .06), 0 8px 24px rgba(27, 58, 92, .07);

    /* --- one radius scale, one font family --- */
    --bs-border-radius:    .3125rem;   /* 5px  */
    --bs-border-radius-sm: .25rem;     /* 4px  */
    --bs-border-radius-lg: .4375rem;   /* 7px  */
    --bs-font-sans-serif: "Noto Sans", "Noto Sans Thai", system-ui, -apple-system,
        "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

/* Palette overrides for the handful of BS5.3 components whose colours are
   compiled, not variable-driven. */
.btn-primary {
    --bs-btn-bg: var(--lds-accent-strong);
    --bs-btn-border-color: var(--lds-accent-strong);
    --bs-btn-hover-bg: #2A70B0;
    --bs-btn-hover-border-color: #2A70B0;
    --bs-btn-active-bg: #255F96;
    --bs-btn-active-border-color: #255F96;
    --bs-btn-disabled-bg: var(--lds-accent-strong);
    --bs-btn-disabled-border-color: var(--lds-accent-strong);
}
.btn-outline-primary {   /* segmented selectSet */
    --bs-btn-color: var(--lds-accent-ink);
    --bs-btn-border-color: var(--lds-accent);
    --bs-btn-hover-bg: var(--lds-accent-strong);
    --bs-btn-hover-border-color: var(--lds-accent-strong);
    --bs-btn-active-bg: var(--lds-accent-strong);
    --bs-btn-active-border-color: var(--lds-accent-strong);
}
.table {
    --bs-table-striped-bg: color-mix(in srgb, var(--lds-accent) 10%, transparent);
    --bs-table-hover-bg:   color-mix(in srgb, var(--lds-accent) 20%, transparent);
}

/* Topbar: soft-blue chrome tint, not the old dark bg-dark bar. Markup
   swapped navbar-dark/bg-dark → .lds-topbar in frame::pageTopbar(). */
.lds-topbar {
    background: var(--lds-topbar-bg);
    border-bottom: 1px solid var(--lds-topbar-border);
    --bs-navbar-color: var(--lds-topbar-ink);
    --bs-navbar-brand-color: var(--lds-ink);
    --bs-navbar-brand-hover-color: var(--lds-ink);
}
.lds-topbar .btn-outline-secondary {   /* the icon controls read as light chips on the tint */
    --bs-btn-color: var(--lds-topbar-ink);
    --bs-btn-bg: var(--lds-surface);
    --bs-btn-border-color: var(--lds-topbar-border);
    --bs-btn-hover-bg: #fff;
    --bs-btn-hover-color: var(--lds-ink);
    --bs-btn-hover-border-color: var(--lds-accent);
}
/* topbar chips (icon buttons + the language select) read as one set — square
   28px icon buttons, all on the same white/topbar-line/5px-radius spec as the
   direction mockup's .tbtn / .u. The language <select> is a real control (not
   the mockup's static "EN" chip) but wears the same border and ground. */
.lds-topbar .btn {
    width: 1.9rem;
    height: 1.9rem;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--bs-border-radius);
}
.lds-topbar .btn[data-nav="profile"] {   /* profile carries a name — not square */
    width: auto;
    padding: 0 .6rem;
    gap: .35rem;
}
.lds-topbar .form-select {
    height: 1.9rem;
    padding-top: 0;
    padding-bottom: 0;
    border-color: var(--lds-topbar-border);
    background-color: var(--lds-surface);
    border-radius: var(--bs-border-radius);
    color: var(--lds-topbar-ink);
}
.lds-topbar .navbar-brand {
    font-weight: 600;
    letter-spacing: -.01em;
}

/* =====================================================================
   Still Water Phase C — consistency audit. BS5 compiles the semantic
   button colours from hex, so a CSV `style` column of btn-success /
   btn-warning / btn-danger / btn-info renders in BS's loud defaults,
   not the theme's muted state tokens. Route every variant (and its
   outline) through the tokens so the whole button set reads as one
   system. The BA's semantic *choice* is respected — only the shade
   changes.
   ===================================================================== */
.btn-success {
    --bs-btn-bg: var(--bs-success); --bs-btn-border-color: var(--bs-success);
    --bs-btn-hover-bg: color-mix(in srgb, var(--bs-success) 88%, #000);
    --bs-btn-hover-border-color: color-mix(in srgb, var(--bs-success) 88%, #000);
    --bs-btn-active-bg: color-mix(in srgb, var(--bs-success) 78%, #000);
    --bs-btn-active-border-color: color-mix(in srgb, var(--bs-success) 78%, #000);
    --bs-btn-disabled-bg: var(--bs-success); --bs-btn-disabled-border-color: var(--bs-success);
}
.btn-danger {
    --bs-btn-bg: var(--bs-danger); --bs-btn-border-color: var(--bs-danger);
    --bs-btn-hover-bg: color-mix(in srgb, var(--bs-danger) 88%, #000);
    --bs-btn-hover-border-color: color-mix(in srgb, var(--bs-danger) 88%, #000);
    --bs-btn-active-bg: color-mix(in srgb, var(--bs-danger) 78%, #000);
    --bs-btn-active-border-color: color-mix(in srgb, var(--bs-danger) 78%, #000);
    --bs-btn-disabled-bg: var(--bs-danger); --bs-btn-disabled-border-color: var(--bs-danger);
}
.btn-warning {
    --bs-btn-color: #fff; --bs-btn-hover-color: #fff; --bs-btn-active-color: #fff;
    --bs-btn-bg: var(--bs-warning); --bs-btn-border-color: var(--bs-warning);
    --bs-btn-hover-bg: color-mix(in srgb, var(--bs-warning) 88%, #000);
    --bs-btn-hover-border-color: color-mix(in srgb, var(--bs-warning) 88%, #000);
    --bs-btn-active-bg: color-mix(in srgb, var(--bs-warning) 78%, #000);
    --bs-btn-active-border-color: color-mix(in srgb, var(--bs-warning) 78%, #000);
    --bs-btn-disabled-bg: var(--bs-warning); --bs-btn-disabled-border-color: var(--bs-warning);
}
.btn-info {   /* cyan Export etc. → the theme accent, not a second hue */
    --bs-btn-color: #fff; --bs-btn-hover-color: #fff; --bs-btn-active-color: #fff;
    --bs-btn-bg: var(--lds-accent); --bs-btn-border-color: var(--lds-accent);
    --bs-btn-hover-bg: var(--lds-accent-strong); --bs-btn-hover-border-color: var(--lds-accent-strong);
    --bs-btn-active-bg: var(--lds-accent-ink); --bs-btn-active-border-color: var(--lds-accent-ink);
    --bs-btn-disabled-bg: var(--lds-accent); --bs-btn-disabled-border-color: var(--lds-accent);
}
.btn-outline-success { --bs-btn-color: var(--bs-success); --bs-btn-border-color: var(--bs-success);
    --bs-btn-hover-bg: var(--bs-success); --bs-btn-hover-border-color: var(--bs-success);
    --bs-btn-active-bg: var(--bs-success); --bs-btn-active-border-color: var(--bs-success); }
.btn-outline-danger { --bs-btn-color: var(--bs-danger); --bs-btn-border-color: var(--bs-danger);
    --bs-btn-hover-bg: var(--bs-danger); --bs-btn-hover-border-color: var(--bs-danger);
    --bs-btn-active-bg: var(--bs-danger); --bs-btn-active-border-color: var(--bs-danger); }
.btn-outline-warning { --bs-btn-color: var(--bs-warning); --bs-btn-border-color: var(--bs-warning);
    --bs-btn-hover-bg: var(--bs-warning); --bs-btn-hover-border-color: var(--bs-warning); --bs-btn-hover-color: #fff;
    --bs-btn-active-bg: var(--bs-warning); --bs-btn-active-border-color: var(--bs-warning); }
.btn-outline-info { --bs-btn-color: var(--lds-accent-ink); --bs-btn-border-color: var(--lds-accent);
    --bs-btn-hover-bg: var(--lds-accent-strong); --bs-btn-hover-border-color: var(--lds-accent-strong);
    --bs-btn-active-bg: var(--lds-accent-strong); --bs-btn-active-border-color: var(--lds-accent-strong); }


/* =====================================================================
   Button palette beyond BS5's six. Two additions, both usable straight
   from a Page CSV `style` cell:
     btn-lilac        — one named hue, filled, same shape as btn-success
     btn-pastel-<x>   — a tinted duplicate of every filled variant, for
                        rows of buttons where six saturated fills shout
   Pastels are one shared rule + a hue per variant: bg is the hue mixed
   into white, ink is the same hue darkened, so a new hue needs one line
   and cannot drift out of the family. `disp::button()`'s variant regex
   knows both names — a class it doesn't recognise gets the neutral
   outline fallback bolted on top of it.
   ===================================================================== */
.btn-lilac {
    --bs-btn-color: #fff; --bs-btn-hover-color: #fff; --bs-btn-active-color: #fff; --bs-btn-disabled-color: #fff;
    --bs-btn-bg: var(--lds-lilac); --bs-btn-border-color: var(--lds-lilac);
    --bs-btn-hover-bg: color-mix(in srgb, var(--lds-lilac) 88%, #000);
    --bs-btn-hover-border-color: color-mix(in srgb, var(--lds-lilac) 88%, #000);
    --bs-btn-active-bg: color-mix(in srgb, var(--lds-lilac) 78%, #000);
    --bs-btn-active-border-color: color-mix(in srgb, var(--lds-lilac) 78%, #000);
    --bs-btn-disabled-bg: var(--lds-lilac); --bs-btn-disabled-border-color: var(--lds-lilac);
}

.btn-pastel-primary, .btn-pastel-secondary, .btn-pastel-success,
.btn-pastel-danger, .btn-pastel-warning, .btn-pastel-info,
.btn-pastel-lilac {
    --lds-pastel-ink: color-mix(in srgb, var(--lds-pastel) 72%, #000);
    --bs-btn-color: var(--lds-pastel-ink);
    --bs-btn-hover-color: var(--lds-pastel-ink);
    --bs-btn-active-color: var(--lds-pastel-ink);
    --bs-btn-disabled-color: var(--lds-pastel-ink);
    --bs-btn-bg:                    color-mix(in srgb, var(--lds-pastel) 18%, #fff);
    --bs-btn-border-color:          color-mix(in srgb, var(--lds-pastel) 34%, #fff);
    --bs-btn-hover-bg:              color-mix(in srgb, var(--lds-pastel) 30%, #fff);
    --bs-btn-hover-border-color:    color-mix(in srgb, var(--lds-pastel) 46%, #fff);
    --bs-btn-active-bg:             color-mix(in srgb, var(--lds-pastel) 40%, #fff);
    --bs-btn-active-border-color:   color-mix(in srgb, var(--lds-pastel) 56%, #fff);
    --bs-btn-disabled-bg:           color-mix(in srgb, var(--lds-pastel) 18%, #fff);
    --bs-btn-disabled-border-color: color-mix(in srgb, var(--lds-pastel) 34%, #fff);
}
.btn-pastel-primary   { --lds-pastel: var(--lds-accent-strong); --lds-pastel-ink: var(--lds-accent-ink); }
.btn-pastel-info      { --lds-pastel: var(--lds-accent);        --lds-pastel-ink: var(--lds-accent-ink); }
.btn-pastel-secondary { --lds-pastel: var(--lds-ink-soft); }
.btn-pastel-success   { --lds-pastel: var(--bs-success); }
.btn-pastel-danger    { --lds-pastel: var(--bs-danger); }
.btn-pastel-warning   { --lds-pastel: var(--bs-warning); }
.btn-pastel-lilac     { --lds-pastel: var(--lds-lilac);         --lds-pastel-ink: var(--lds-lilac-ink); }
/* native colour input: a small swatch, not a full-width bar. .lds-field's
   `flex:1 1 auto` on every non-label child would otherwise grow it to the
   full row; `input.form-control-color` (specificity 0,2,1) beats that and
   pins the flex basis. */
.form-control-color {
    padding: .25rem;
}
.lds-field > input.form-control-color {
    flex: 0 0 3rem;
    max-width: 3rem;
}

/* icons: one box width so they line up down a menu / across a button row
   regardless of glyph (fa-pencil vs fa-trash vs fa-cubes). */
.btn > .fa, .nav-link > .fa, .nav-head > .fa {
    width: 1.15em;
    text-align: center;
}

/* topbar language select: don't let it collapse to a sliver on a narrow
   viewport next to the other topbar controls. */
.lds-topbar select[name="lang"] {
    min-width: 4.5rem;
}

/* sidebar/content row: stretch to at least the viewport height, not just
   the shorter of the two children's own content height — otherwise the
   sidebar's border-end (the visual divider) stops wherever the shorter
   side ends instead of running the full page. body becomes the flex
   column (topbar's natural height, then this row grows to fill the
   rest); the row's own internal stretch (sidebar vs content) is
   untouched and still handles content taller than the viewport normally. */
body {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}
.lds-shell {
    flex: 1 0 auto;
}
/* sidebar reads as a white panel against the off-white content ground
   (matches the direction mockup's .dev-side) — the .border-end divider only
   shows if the two sides differ. #content keeps --lds-ground from the body. */
#lds-sidebar {
    background: var(--lds-surface);
    font-size: 13px;   /* one notch under the 14px body — mockup's .dev-side is 12.5 on a 13.5 base */
}

/* Data density (Still Water Phase B). Text size held at 14px (one root cut,
   2026-07-20 benchmark) — readability is not the thing to trade. Spacing is
   loosened one notch from the original build and split two tiers: table
   cells stay tight for scanning; form controls get a little more room since
   accuracy matters more there than fitting one extra row. Keyboard +
   barcode-wedge tool, not touch — no 44px target minimum on desktop (the
   small-screen block below lifts targets only under 768px). */
html {
    font-size: 87.5%;
}
.table > :not(caption) > * > * {
    padding: .42rem .55rem;   /* ~32px rows */
}

.btn {
    padding: .3rem .6rem;
    margin-right: 5px;
}
.form-control {
    padding: .38rem .55rem;
    font-size: .875rem;
}
.form-select {
    /* right side stays Bootstrap's own reserved caret space (2.25rem) —
       .form-control's symmetric shorthand overwrote it, overlapping the
       ▾ icon with the selected text */
    padding-top: .38rem;
    padding-bottom: .38rem;
    padding-left: .55rem;
    font-size: .875rem;
}

/* control-set (label + control): side by side by default. Override per
   field with disp::set()'s $opt['stacked'] — see disp.php. */
.lds-field {
    display: flex;
    align-items: center;
    gap: .6rem;
}
.lds-field > .form-label {
    flex: 0 0 8.5rem;
    margin-bottom: 0;
    text-align: right;
}
.lds-field > *:not(.form-label) {
    flex: 1 1 auto;
    min-width: 0;
}
.lds-field-stacked {
    display: block;
}
.lds-field-stacked > .form-label {
    display: block;
    flex: none;
    width: auto;
    text-align: left;
    margin-bottom: .25rem;
}

/* search row: fields sit inline with the Search button — each field's own
   mb-3 would otherwise push it up off the button's baseline. Bootstrap's
   own .mb-3 utility is !important, so cancelling it needs !important too. */
.lds-search > .lds-field {
    margin-bottom: 0 !important;
}

/* footer buttons (Save/Back/...) centered by default — see interp.php's
   render() footer case for the wrapping div */

/* sidebar menu group: collapsed by default, .show expands it, each group
   toggles independently — runtime.js's [data-toggle-group] handler owns
   the toggle, frame.php marks the group containing the active page with
   .show server-side */
.lds-menu-group .nav-head {
    cursor: pointer;
    user-select: none;
    font-weight: 600;   /* mockup's .gh is 600, not fw-bold's 700 */
}
/* leading menu icons sit quiet, like the mockup's .ico */
.lds-menu .nav-head > .fa,
.lds-menu .nav-link > .fa {
    opacity: .7;
}
.lds-menu-group .lds-menu-body {
    display: none;
}
.lds-menu-group.show .lds-menu-body {
    display: flex;
}
.lds-menu-caret {
    font-size: .65em;        /* mockup's .cv is a small hint, not text-sized */
    opacity: .6;
    transition: transform .15s;
}
.lds-menu-group.show .lds-menu-caret {
    transform: rotate(180deg);
}
/* menu colour, per the direction mockup:
   - top level (group headers + flat pages) = dark ink, 600 — the spine
   - sub-menu links = quiet ink-soft, 400 — indented detail
   - active page (either level) = accent, handled below
   BS5's own .nav re-sets --bs-nav-link-color on the wrapper, so the
   sub-links need it re-set on .nav specifically. Hover tints, doesn't
   recolour. */
.lds-menu .nav {
    --bs-nav-link-color: var(--lds-ink-soft);
    --bs-nav-link-hover-color: var(--lds-accent-ink);
}
.lds-menu > a.nav-link {
    color: var(--lds-ink);
    font-weight: 600;   /* reads like a collapsed group header */
    margin-bottom: 7px;
}
.lds-menu .nav-link:hover {
    background: var(--lds-accent-weak);
    color: var(--lds-accent-ink);
}
/* sub-menu links sit a clear step right of their group header (which stays
   at px-3 = 1rem) so the two levels read as parent/child at a glance,
   not two links at the same indent. */
.lds-menu-body .nav-link {
    padding-left: 2.25rem;
}

/* searchable dropdown (disp::combobox, runtime.js combobox) — the text
   box wears .form-select for the native ▾ look */
.lds-combobox { position: relative; }
.lds-combobox .lds-combobox-list {
    position: absolute;
    z-index: 1000;
    width: 100%;
    max-height: 16rem;
    overflow-y: auto;
}
.lds-combobox-list .list-group-item {
    padding-top: .25rem;
    padding-bottom: .25rem;
}
.lds-menu .nav-link.active {
    font-weight: 600;
    color: var(--lds-accent-ink);
    background: var(--lds-accent-weak);
    border-radius: var(--bs-border-radius);
    box-shadow: inset 2px 0 0 var(--lds-accent-strong);
}

/* the datepicker popup (runtime.js datePicker) — every visual of it lives
   here; JS only sets top/left */
.lds-datepicker {
    position: absolute;
    z-index: 3000;
    background: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius);
    box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, .15);
    padding: 0.375rem;
    width: 16rem;
}
.lds-datepicker-hd {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 0.25rem;
}
.lds-datepicker-title { font-weight: 600; }
.lds-datepicker-nav {               /* « » month buttons */
    border: none;
    background: none;
    color: var(--lds-accent-ink);
    font-size: 1.25rem;
    line-height: 1;
    min-width: 2rem;
    min-height: 2rem;
    border-radius: var(--bs-border-radius);
}
.lds-datepicker-nav:hover { background: var(--bs-tertiary-bg); }
.lds-datepicker-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    text-align: center;
}
.lds-datepicker-dow {
    font-size: .75rem;
    color: var(--bs-secondary-color);
    padding: 0.25rem 0;
}
.lds-datepicker-day {
    border: none;
    background: none;
    border-radius: var(--bs-border-radius);
    padding: 0.25rem 0;
}
.lds-datepicker-day:hover {
    background: var(--bs-tertiary-bg);
}
.lds-datepicker-day.today {
    font-weight: 600;
}
.lds-datepicker-day.selected {
    background: var(--bs-primary);
    color: var(--bs-body-bg);
}

/* =====================================================================
   Still Water Phase B — focus / active-row / busy / saved cues.
   CSS-only motion, ≈130ms, "where am I / what changed" only. All of it
   goes still under prefers-reduced-motion (block at the very bottom).
   ===================================================================== */

/* Focus: unmistakable. BS5 already rings form controls off --bs-primary-rgb;
   this widens the net (segmented buttons, combobox, switches) and lifts the
   ring a touch so "which field am I in" reads instantly during Enter-to-next
   keyboard flow. */
.form-control:focus,
.form-select:focus,
.form-check-input:focus,
.lds-combobox input:focus,
.btn-check:focus-visible + .btn {
    border-color: var(--lds-accent);
    box-shadow: 0 0 0 .2rem color-mix(in srgb, var(--lds-accent) 38%, transparent);
    transition: box-shadow .13s ease, border-color .13s ease;
}
/* req segmented select left unanswered (runtime validateReq) */
.btn-check.is-invalid + .btn { border-color: var(--bs-danger); color: var(--bs-danger); }

/* Active / just-edited data row (addTable click-to-edit toggles tr.active) */
.table tbody tr.active {
    --bs-table-bg: var(--lds-accent-weak);
}
.table tbody tr.active > *:first-child {
    box-shadow: inset 2px 0 0 var(--lds-accent-strong);
}

/* request lock (runtime.js loadLock): an invisible shield over the content
   zone while a request runs — whole page before #content exists (login).
   No dimming/fade: a semi-transparent fill reads as a flash on every
   request, however brief; it only needs to block interaction. */
#lds-lock {
    position: fixed;
    inset: 0;
    z-index: 2000;
    cursor: wait;
}
.lds-lock-zone { position: relative; }
.lds-lock-zone > #lds-lock { position: absolute; }

/* Busy bar: a 2px indeterminate accent sweep along the top of the content
   zone. Rides #lds-lock's exact lifecycle (runtime.js loadLock/loadUnlock)
   with zero JS — the lock was already an invisible interaction shield;
   this just lets people see it. */
#lds-lock::after {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 2px;
    background: linear-gradient(90deg, transparent 0%, var(--lds-accent) 50%, transparent 100%);
    background-size: 40% 100%;
    background-repeat: no-repeat;
    animation: lds-busy 1s linear infinite;
}
@keyframes lds-busy {
    from { background-position: -45% 0; }
    to   { background-position: 145% 0; }
}

/* Saved / notice: a non-blocking toast, replaces msgPopup's alert().
   runtime.js appends #lds-toasts once and drops .lds-toast children in. */
#lds-toasts {
    position: fixed;
    top: 3.25rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 2100;
    display: flex;
    flex-direction: column;
    gap: .4rem;
    align-items: center;
    pointer-events: none;
}
.lds-toast {
    pointer-events: auto;
    cursor: pointer;
    white-space: pre-wrap;
    max-height: 60vh;
    overflow: auto;
    max-width: min(90vw, 30rem);
    padding: .45rem .8rem;
    border-radius: var(--bs-border-radius);
    background: var(--lds-surface);
    color: var(--lds-ink);
    border: 1px solid var(--bs-border-color);
    border-left: 3px solid var(--lds-accent-strong);
    box-shadow: var(--lds-shadow);
    font-size: .875rem;
    animation: lds-toast-in .15s ease;
}
.lds-toast--error {
    border-left-color: var(--bs-danger);
}
.lds-toast.is-leaving {
    animation: lds-toast-out .2s ease forwards;
}
@keyframes lds-toast-in {
    from { opacity: 0; transform: translateY(-.4rem); }
}
@keyframes lds-toast-out {
    to { opacity: 0; transform: translateY(-.4rem); }
}

/* data-confirm-arm's armed state (runtime.js checkArm) — visual twin of the
   toast it fires alongside, cueing "click again to proceed" without a
   blocking dialog. */
.is-armed {
    outline: 2px solid var(--bs-danger);
    outline-offset: 1px;
    animation: lds-arm-pulse 1s ease-in-out infinite;
}
@keyframes lds-arm-pulse {
    50% { outline-color: transparent; }
}

/* =====================================================================
   Still Water refresh (2026-09-27) — same tokens, structure + density
   only. Design + before/after: docs/lds-display-refresh.html.
   ===================================================================== */

/* R1 — login: centred card on the ground + one faint topbar-blue glow */
body.lds-login-page {
    background:
        radial-gradient(1100px 380px at 50% -8%, var(--lds-topbar-bg), transparent 72%),
        var(--lds-ground);
    align-items: center;
    justify-content: center;
    padding: 1rem;
}
.lds-login {
    width: min(100%, 23rem);
    margin-bottom: 8vh;   /* sits a touch above centre */
}
.lds-login-card {
    background: var(--lds-surface);
    border: 1px solid var(--lds-line);
    border-radius: var(--bs-border-radius-lg);
    box-shadow: var(--lds-shadow);
    padding: 1.75rem 1.75rem 1.5rem;
}
.lds-login-brand {
    display: flex;
    align-items: center;
    gap: .7rem;
    margin-bottom: 1.4rem;
}
.lds-login-mark {
    width: 2.25rem;
    height: 2.25rem;
    border-radius: var(--bs-border-radius-lg);
    background: var(--lds-accent-strong);
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
}
.lds-login-name { font-weight: 600; line-height: 1.2; }
.lds-login-sub  { color: var(--lds-ink-soft); font-size: .8125rem; }
.lds-login-title { font-size: 1.25rem; font-weight: 600; margin: 0 0 1rem; }
.lds-login .form-control { padding: .55rem .7rem; }   /* one-off screen: roomier */
.lds-login .btn-primary  { padding: .55rem; margin: .5rem 0 0; font-weight: 600; }
.lds-login .alert        { font-size: .875rem; padding: .5rem .75rem; }
.lds-login-foot {
    text-align: center;
    color: var(--lds-ink-soft);
    font-size: .75rem;
    margin-top: 1rem;
}

/* R2 — content sits on white panels, not the bare ground. A list and an
   addTable are one panel each; a form is one panel per section
   (interp::renderForm() closes the panel at every `section` and opens the
   next one headed by it). A hook-built .lds-form with no panels inside is
   the panel itself. */
#content { padding: 1rem 1.25rem !important; }
#content > .lds-list,
#content > .lds-addtable,
#content > .lds-search,
#content > .lds-form > .lds-panel,
#content > .lds-form:not(:has(> .lds-panel)) {
    background: var(--lds-surface);
    border: 1px solid var(--lds-line);
    border-radius: var(--bs-border-radius-lg);
    box-shadow: 0 1px 2px rgba(27, 58, 92, .05);
    padding: .75rem 1rem;
    margin-bottom: .75rem;
}
/* footer buttons (Save/Upload/...) sitting between two panels get the same
   gap below as a panel does */
#content > .lds-footer { margin-bottom: .75rem; }
/* a search feature is a BS .row: cancel its negative gutters so the panel
   lines up with the list below, and match the panel gap (mb-3 is !important) */
#content > .lds-search { margin-left: 0; margin-right: 0; margin-bottom: .75rem !important; }
.lds-panel > .lds-section:first-child { margin-top: 0 !important; }
.lds-section {
    padding-bottom: .4rem;
    margin-bottom: .75rem !important;
    border-color: var(--lds-line) !important;
}

/* R4 — the header row reads as a header (no uppercase: it breaks Thai) */
.table > thead > tr > th {
    background: var(--bs-tertiary-bg);
    color: var(--lds-ink-soft);
    font-size: 1rem;   /* same 14px as the cells; ground + weight mark it */
    font-weight: 600;
    white-space: nowrap;
    border-bottom: 1px solid var(--lds-line);
}

/* click targets runtime.js wires up: toggle-next sections/labels, addTable
   panel rows (click to edit) */
[data-toggle-next] { cursor: pointer; user-select: none; }
tr[data-addtable-row] { cursor: pointer; }

/* list quick-filter box */
.lds-list input[data-filter] { max-width: 16rem; }

/* sortable list header: click sorts (runtime.js); the arrow marks the live column */
.table > thead > tr > th[data-sort] { cursor: pointer; user-select: none; }
.table > thead > tr > th[data-sort]:hover { color: var(--lds-accent-ink); }
.table > thead > tr > th[aria-sort="ascending"]::after  { content: " \25B4"; }
.table > thead > tr > th[aria-sort="descending"]::after { content: " \25BE"; }

/* R5 — stripes at 14% accent: 4% was indistinguishable on real lists
   (user, consoleLog, 2026-09-27); digits line up down a column */
.table {
    --bs-table-striped-bg: color-mix(in srgb, var(--lds-accent) 14%, transparent);
    --bs-table-hover-bg:   color-mix(in srgb, var(--lds-accent) 24%, transparent);
    font-variant-numeric: tabular-nums;
}

/* R6 — row action buttons compact, so a full toolbar row stays ~32px;
   colours stay whatever the BA's `style` cell chose */
.table td .btn {
    padding: .12rem .45rem;
    font-size: .8125rem;
    margin-right: 3px;
}
.table td:last-child:has(.btn) { white-space: nowrap; }

/* R7 — hairline between the brand and the page title */
#lds-page-title {
    border-left: 1px solid var(--lds-topbar-border);
    padding-left: .9rem;
    margin-left: .25rem !important;
    color: var(--lds-topbar-ink) !important;
    font-weight: 500;
}

/* Small screens — desktop-first, "workable, not broken" floor.
   (The sidebar drawer is handled in its own block below.) */
@media (max-width: 767.98px) {
    /* (wide tables already scroll in their own lane — disp::table() wraps
       them in BS5's .table-responsive) */
    /* touch-sized targets at this breakpoint only */
    .btn { padding: .5rem .8rem; }
    .form-control, .form-select { padding: .5rem .6rem; }
    .lds-menu .nav-link, .lds-menu .nav-head { padding: .55rem .75rem; }
    .lds-menu-body .nav-link { padding-left: 1.75rem; }   /* keep the indent, just tighter */
    /* topbar sheds the non-essential controls */
    .lds-topbar [data-duplicate], .lds-topbar [data-fullscreen] { display: none; }
}
@media (max-width: 575.98px) {
    /* label spine unstacks — label above the input, full width */
    .lds-field {
        display: block;
    }
    .lds-field > .form-label {
        flex: none;
        width: auto;
        text-align: left;
        margin-bottom: .15rem;
    }
    .lds-search > .lds-field { margin-bottom: .5rem !important; }
}

/* sidebar toggle: one class, opposite default per breakpoint —
   narrow screens start hidden (button reveals as overlay), wide
   screens start shown (button collapses for more content room). */
@media (max-width: 767.98px) {
    #lds-sidebar { display: none; }
    #lds-sidebar.lds-menu-open {
        display: block;
        position: fixed;
        top: 56px;
        left: 0;
        bottom: 0;
        z-index: 1030;
        background: var(--bs-body-bg);
        overflow-y: auto;
    }
}
@media (min-width: 768px) {
    #lds-sidebar.lds-menu-open { display: none; }
}

/* every Still Water transition/animation is a cue, not decoration — honour
   the OS setting and drop all of it. The busy bar stays visible, just still. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
    }
    #lds-lock::after {
        background: var(--lds-accent);
        background-size: 100% 100%;
    }
}

/* =====================================================================
   Still Water Phase D — print. Ctrl+P on any live page (the callPopup
   document view already ships chrome-free from index.php). Strip the
   runtime chrome, go black-on-white, show every row. printPdf is a
   separate mPDF pipeline and not styled here.
   ===================================================================== */
@media print {
    .lds-topbar,
    #lds-sidebar,
    .lds-search,
    #lds-toasts,
    #lds-lock,
    .btn,
    .lds-list .d-flex.justify-content-between,   /* quick-filter + toolbar bar */
    .alert-warning {                             /* "showing first 500" — export/print is never row-limited */
        display: none !important;
    }
    /* the trailing action-button column in a list */
    .lds-list table > thead > tr > :last-child,
    .lds-list table > tbody > tr > :last-child {
        display: none;
    }

    body {
        display: block !important;
        background: #fff;
        color: #000;
        font-size: 11pt;
    }
    .lds-shell, #content, .flex-grow-1, .container {
        display: block !important;
        width: auto !important;
        max-width: none !important;
        padding: 0 !important;
        margin: 0 !important;
        flex: none !important;
    }

    .table-responsive { overflow: visible !important; }
    .table {
        --bs-table-bg: #fff;
        --bs-table-striped-bg: #fff;
        --bs-table-hover-bg: #fff;
    }
    .table > :not(caption) > * > * { border-color: #999; }
    thead { display: table-header-group; }       /* repeat the header on each page */
    tr, .card, .lds-dash-widget { page-break-inside: avoid; }
    .card, .lds-panel, .lds-list, .lds-addtable { border: 1px solid #999; box-shadow: none; }

    /* form spine stays legible, minus the input chrome */
    .form-control, .form-select, .form-control-plaintext {
        border: 0 !important;
        padding: 0 !important;
        background: none !important;
    }
    a { color: #000; text-decoration: none; }
    @page { margin: 14mm; }
}
