/* public/assets/css/app.css
   KHMER HRMS — Premium UI Base Layer
   Season: S00 — Project Zero Setup
   Reference: KHMER_HRMS_MASTER_LIST_v2.md Section 3 (Premium UI Spec)

   This is the foundation every later season's views build on top of.
   Applies from S01 onward per the master list — established now so
   the very first real screen (S01 login) already looks premium.
*/

* {
    box-sizing: border-box;
}

/* ── Icon safety-net (bug-fix pass) ────────────────────────────────
   Every hand-drawn icon returned by icon_svg() in functions.php uses
   a 24×24 viewBox and no width/height attributes, so it inherits
   whatever the surrounding CSS gives it — with nothing at all, it
   renders at its unconstrained intrinsic size (the "giant icon"
   bug seen on the Locations → Villages filter). This default keeps
   every such icon at a sane inline-caption size (16px) the moment
   it's dropped into a new label/span/paragraph, with no per-view CSS
   required. Any spot that wants a different size (buttons, sidebar,
   stat cards, etc.) already has — or can add — a more specific
   ".xxx svg { width; height; }" rule further down this file, which
   wins by appearing later at equal specificity. ── */
svg[viewBox="0 0 24 24"] { width: 16px; height: 16px; flex-shrink: 0; vertical-align: -3px; }

html, body {
    margin: 0;
    padding: 0;
    /* Mobile Safari/Chrome bump body text size on rotation/zoom by
       default, which throws off the 8px spacing scale and can make
       wrapped Khmer text reflow oddly — normalize it everywhere. */
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}

html {
    /* A single stray wide element (a long unbroken URL, a table, a
       modal) on any one screen no longer creates a page-wide sideways
       scrollbar on the whole app — content that needs its own
       scrolling already opts in via .data-table / .tabs-nav
       (overflow-x: auto) instead. */
    overflow-x: hidden;
    width: 100%;
}

body {
    background: var(--bg);
    color: var(--text);
    -webkit-font-smoothing: antialiased;
    line-height: 1.5;
    overflow-x: hidden;
    width: 100%;
    min-height: 100vh;
}

/* Images/SVGs/videos dropped into any view (employee photos, company
   logos, uploaded documents) never force a layout wider than their
   container, on any screen size. */
img, svg, video {
    max-width: 100%;
    height: auto;
}

/* Long unbroken tokens (emails, device IPs, IDs) wrap instead of
   pushing cards/tables wider than the viewport on narrow phones. */
.card, .main-content, .filter-bar, .kv-table td, .doc-row-body,
.contract-row-body, .assignment-row-body {
    overflow-wrap: break-word;
    word-break: break-word;
}

/* ── 8px Spacing Scale (Premium UI §3) ───────────────────────── */
:root {
    --space-1: 8px;
    --space-2: 16px;
    --space-3: 24px;
    --space-4: 32px;
    --radius: 14px;
    --radius-sm: 8px;
}

/* ── Cards: soft double-layer shadow, tinted inner border ───── */
.card {
    background: var(--surface);
    border-radius: var(--radius);
    border: 1px solid var(--border);
    box-shadow: var(--shadow-ambient), var(--shadow-contact);
    padding: var(--space-3);
}

/* ── Buttons: subtle gradient + hover lift, never instant flip ─ */
/* ── Form control font reset ──────────────────────────────────
   Browsers do NOT let <button>/<input>/<select> inherit font-family
   from the page by default — they use the OS's UI font instead.
   Without this reset, buttons look visually mismatched from the rest
   of the page even when the correct Khmer/Moul fonts are loaded
   correctly everywhere else. */
button, input, select, textarea {
    font-family: inherit;
}

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    border: none;
    border-radius: var(--radius-sm);
    padding: 10px 20px;
    font-size: 0.95rem;
    font-weight: 600;
    cursor: pointer;
    transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
}

.btn-primary {
    background: linear-gradient(135deg, var(--primary), var(--accent));
    color: #FFFFFF;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
}

.btn-primary:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 16px rgba(37, 99, 235, 0.28);
}

.btn-outline {
    background: transparent;
    color: var(--text);
    border: 1px solid var(--border);
}

.btn-outline:hover {
    transform: translateY(-2px);
    background: var(--surface-alt);
}

/* ── Status dots / badges: soft glow ring ─────────────────────── */
.badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 12px;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 600;
}
.badge-success { color: var(--success); background: color-mix(in srgb, var(--success) 12%, transparent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--success) 8%, transparent); }
.badge-warning { color: var(--warning); background: color-mix(in srgb, var(--warning) 12%, transparent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--warning) 8%, transparent); }
.badge-danger  { color: var(--danger);  background: color-mix(in srgb, var(--danger) 12%, transparent);  box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 8%, transparent); }
.badge-info    { color: var(--info);    background: color-mix(in srgb, var(--info) 12%, transparent);  box-shadow: 0 0 0 3px color-mix(in srgb, var(--info) 8%, transparent); }

/* ── Typography: Moul/heavier weight for titles + stat numbers only ─
   Uses --font-kh-heading (Moul) + --font-en-heading (Inter) from
   themes.css. This is intentionally independent of the body[data-font]
   toggle — titles are always Moul/Inter, body text follows the user's
   Normal/Engraved preference (Premium UI §4: "never body text"). */
h1, h2, .stat-number, .font-title-kh {
    font-family: var(--font-kh-heading), var(--font-en-heading);
    font-weight: 700;
    letter-spacing: -0.01em;
}

/* ── Motion: staggered fade+slide-in for rows/cards on load ──── */
@keyframes fadeSlideIn {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}
.fade-in {
    animation: fadeSlideIn 0.35s ease;
}
/* IMPORTANT — do NOT add `forwards`/`both` back to the rule above.
   The "to" keyframe sets `transform: translateY(0)`, and per the CSS
   spec ANY computed transform value other than the literal keyword
   `none` — even a no-op translateY(0) — makes that element establish
   a new containing block for `position: fixed` descendants. With a
   `forwards`/`both` fill-mode the animation's final frame styles stay
   applied to the element forever after it finishes playing, so every
   `.card.fade-in` / `.filter-bar.card.fade-in` wrapper in the app
   (used on nearly every page — filter bars, forms, list cards) would
   permanently "trap" any `position: fixed` element nested inside it,
   including the S11 date/time picker popovers (public/assets/js/
   s11-picker.js), causing them to be positioned relative to that
   card's box instead of the real viewport — exactly the "popover
   floats far away from its input" bug. Dropping the fill-mode lets
   the animation's effect end naturally once it completes (there's no
   `animation-delay` anywhere on `.fade-in`, so `backwards` was never
   needed either) and the element's transform reverts to its
   un-animated value, which is `none` — visually identical to the
   final `translateY(0)` frame, but no longer a containing block. */

/* ── Empty states (used from S01+ once there is data to be empty) ── */
.empty-state {
    text-align: center;
    padding: var(--space-4);
    color: var(--text-muted);
}

/* ═══════════════════════════════════════════════════════════════
   S01 — Core MVC Bootstrap, Auth, Router
   Everything below supports the login/forgot/reset screens and the
   app shell (sidebar + top bar + bottom nav) added this season.
   ═══════════════════════════════════════════════════════════════ */

.text-muted { color: var(--text-muted); }

/* ── Buttons: additions ───────────────────────────────────────── */
.btn-block { width: 100%; }
.btn-xs    { padding: 4px 10px; font-size: 0.78rem; }
.btn-sm    { padding: 6px 14px; font-size: 0.85rem; }
.btn-danger {
    background: linear-gradient(135deg, var(--danger), #EF4444);
    color: #FFFFFF;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
}
.btn-danger:hover { transform: translateY(-2px); box-shadow: 0 6px 16px rgba(220, 38, 38, 0.28); }

/* S13 — Payroll Runs & Payslips: a positive/confirming action button
   (Approve run, Mark as Paid) — same shape as .btn-danger above, just
   themed off --success instead of --danger. Also fills in .text-danger
   /.text-success, small text-color utilities already referenced by
   S11's leave request views (rejection reason) but never defined. */
.btn-success {
    background: linear-gradient(135deg, var(--success), #22C55E);
    color: #FFFFFF;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
}
.btn-success:hover { transform: translateY(-2px); box-shadow: 0 6px 16px rgba(22, 163, 74, 0.28); }
.text-danger  { color: var(--danger); }
.text-success { color: var(--success); }

/* ── Forms ─────────────────────────────────────────────────────── */
.form-group { margin-bottom: var(--space-2); text-align: left; }
.form-group label {
    display: block;
    font-size: 0.85rem;
    font-weight: 600;
    margin-bottom: 6px;
    color: var(--text);
}
/* Icon + text form labels — e.g. System Branding's Website / Email /
   Telegram / Facebook / Phone fields. Replaces raw emoji glyphs
   (font/OS-dependent, rendered inconsistently — see icon_svg() doc
   comment) with the same local currentColor SVG icon set used
   everywhere else, sized to sit cleanly next to the label text. */
.form-group label.label-icon { display: inline-flex; align-items: center; gap: 6px; }
.form-group label.label-icon svg { width: 14px; height: 14px; color: var(--text-muted); }
.form-group input[type="text"],
.form-group input[type="email"],
.form-group input[type="url"],
.form-group input[type="tel"],
.form-group input[type="password"],
.form-group input[type="number"],
.form-group input[type="date"],
.form-group select,
.form-group textarea {
    width: 100%;
    padding: 11px 14px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    font-size: 0.95rem;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.form-group input[type="url"]::placeholder,
.form-group input[type="tel"]::placeholder { color: var(--text-muted); opacity: 0.7; }
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 18%, transparent);
}
.input-with-action { position: relative; display: flex; align-items: center; }
.input-with-action input { flex: 1; padding-right: 44px; }
.input-action-btn {
    position: absolute;
    right: 6px;
    background: none;
    border: none;
    cursor: pointer;
    font-size: 1rem;
    padding: 8px;
    color: var(--text-muted);
    border-radius: var(--radius-sm);
}
.input-action-btn:hover { background: var(--surface-alt); }
.pin-input {
    text-align: center;
    letter-spacing: 10px;
    font-size: 1.4rem;
    font-weight: 700;
}

/* ── Textarea resize handle: vertical only ────────────────────────
   Browsers default a <textarea>'s drag-handle to `resize: both`,
   which lets a person drag it wider than its card/column and break
   the layout. Every textarea in the app should only grow/shrink up
   and down. Scoped rules in chat.css (resize: none) and posts.css
   (resize: vertical, already correct) are more specific than this
   and are left untouched — this is just the app-wide default for
   every other textarea (Settings, Employees, Leave/Permission,
   Payroll, Performance, Attendance, Mobile, etc.). */
textarea { resize: vertical; }

/* ── Multi-column form grid ────────────────────────────────────────
   Bug fix: `.form-grid` was referenced by several S11 forms (Leave
   Request create/edit, Permission Request create/edit, Approval
   Workflow add/edit step, Settings → Leave & Permission) and by two
   narrow `align-items` overrides in s11-leave.css, but `display: grid`
   itself was never actually defined anywhere — every `.form-group`
   inside it just fell back to block layout and stacked one per row,
   full width, making these forms unnecessarily tall. This is the
   single shared definition every one of those views already expects.
   Auto-fits as many ~220px columns as the container allows (2-3 on a
   normal form card), collapsing to one column on narrow viewports. */
.form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: var(--space-1) var(--space-3);
    align-items: start;
}
/* Fields meant to span the whole row (textarea, file upload, a
   checkbox/switch row) — already used with this exact class name by
   every consuming view, just inert until `.form-grid` was a real
   grid. */
.form-grid .form-group-full {
    grid-column: 1 / -1;
}
/* When `.form-actions` (the Submit/Cancel button row) is a DIRECT
   child of a `.form-grid` <form> — the Approval Workflow add/edit step
   forms, where there's no separate wrapping <div> — it must span every
   column too, or the buttons get squeezed into a single narrow cell. */
.form-grid > .form-actions {
    grid-column: 1 / -1;
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: var(--space-1);
}
@media (max-width: 575.98px) {
    .form-grid { grid-template-columns: 1fr; }
}

/* ── Alerts (form-level notices — distinct from sa.js toasts) ─── */
.alert {
    padding: 12px 16px;
    border-radius: var(--radius-sm);
    font-size: 0.88rem;
    margin-bottom: var(--space-2);
    border: 1px solid transparent;
}
.alert-error   { background: rgba(220, 38, 38, 0.1);  color: var(--danger);  border-color: rgba(220, 38, 38, 0.25); }
.alert-success { background: rgba(22, 163, 74, 0.1);  color: var(--success); border-color: rgba(22, 163, 74, 0.25); }
.alert-info    { background: rgba(37, 99, 235, 0.1);  color: var(--primary); border-color: rgba(37, 99, 235, 0.25); }
.alert-warning { background: rgba(217, 119, 6, 0.1);  color: var(--warning); border-color: rgba(217, 119, 6, 0.25); }

/* ── Location Manager Upgrade (S05+): per-page picker, Recycle Bin ── */
.per-page-form { margin-left: auto; align-items: center; }
.per-page-form select { min-width: 80px; }

/* ── Guest shell (login / forgot / reset password) ────────────── */
.guest-shell {
    position: relative;
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-3);
    overflow: hidden;
}
.guest-shell-bg {
    position: absolute;
    inset: 0;
    background: radial-gradient(circle at 20% 20%, color-mix(in srgb, var(--primary) 16%, var(--bg)), var(--bg) 60%);
    z-index: 0;
}
.guest-shell-inner {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 400px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
}
.guest-lang-toggle {
    align-self: flex-end;
    font-size: 0.85rem;
    display: flex;
    gap: 6px;
    color: var(--text-muted);
}
.guest-lang-toggle a { color: var(--text-muted); text-decoration: none; padding: 2px 4px; }
.guest-lang-toggle a.is-active { color: var(--primary); font-weight: 700; }
.link-btn {
    background: none; border: none; padding: 2px 4px;
    color: var(--text-muted); font-size: 0.85rem; cursor: pointer;
    text-decoration: none;
}
.link-btn:hover { color: var(--danger); text-decoration: underline; }
.divider-with-label {
    display: flex; align-items: center; gap: 10px;
    color: var(--text-muted); font-size: 0.78rem;
    margin: 18px 0 14px;
}
.divider-with-label::before, .divider-with-label::after {
    content: ''; flex: 1; height: 1px; background: var(--border);
}

/* ── Data tables (admin lists: Pending Users, Invites, and every
   future module list from S02 onward) ──────────────────────────── */
.table-wrap { overflow-x: auto; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); }
.data-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
.data-table th, .data-table td { padding: 10px 14px; text-align: left; border-bottom: 1px solid var(--border); white-space: nowrap; }
.data-table thead th { background: var(--surface-alt); font-weight: 700; color: var(--text-muted); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.03em; }
.data-table tbody tr:last-child td { border-bottom: none; }
.data-table tbody tr:hover { background: var(--surface-alt); }

/* Sortable column headers (S29 patch — Employees list + reusable
   elsewhere) — plain link styling matching thead's existing muted/
   uppercase look, no layout shift when a column becomes active. */
.data-table th a.th-sort-link {
    color: inherit;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 3px;
    cursor: pointer;
}
.data-table th a.th-sort-link:hover { color: var(--primary); }
.data-table th a.th-sort-link.th-sort-active { color: var(--primary); }

/* Employees: keep Sex + Date of Birth in one compact column.
   Sex is the primary line; date of birth sits directly underneath. */
.data-table .employee-sex-dob-head {
    min-width: 118px;
    white-space: normal;
    line-height: 1.25;
}
.data-table .employee-sex-dob-head span {
    display: block;
}
.data-table .employee-sex-dob-head span + span {
    margin-top: 2px;
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0;
}
.data-table .employee-sex-dob-cell {
    white-space: normal;
    vertical-align: middle;
}
.employee-sex-dob-sex,
.employee-sex-dob-date {
    display: block;
    line-height: 1.3;
}
.employee-sex-dob-sex {
    font-weight: 600;
}
.employee-sex-dob-date {
    margin-top: 2px;
    color: var(--text-muted);
    font-size: 0.78rem;
}

.text-center { text-align: center; }
.actions { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.inline-approve-form { display: inline-flex; gap: 6px; align-items: center; }
.role-select {
    padding: 6px 8px; border-radius: 6px; border: 1px solid var(--border);
    background: var(--surface); color: var(--text); font-size: 0.8rem;
}
.badge-secondary { background: var(--surface-alt); color: var(--text-muted); box-shadow: 0 0 0 3px color-mix(in srgb, var(--text-muted) 8%, transparent); }
code { background: var(--surface-alt); padding: 2px 6px; border-radius: 4px; font-size: 0.85em; }

.auth-card {
    width: 100%;
    text-align: center;
    padding: var(--space-4) var(--space-3);
}
.auth-card-brand { display: flex; flex-direction: column; align-items: center; gap: 4px; margin-bottom: var(--space-2); }
.auth-card-brand-mark { font-size: 2rem; }
.auth-card-brand-mark img { height: 48px; width: auto; max-width: 160px; object-fit: contain; }
.auth-card-brand-name { font-weight: 700; font-size: 0.95rem; color: var(--text-muted); }
.auth-card-title { margin: 0 0 4px; font-size: 1.4rem; }
.auth-card-subtitle { color: var(--text-muted); font-size: 0.88rem; margin: 0 0 var(--space-3); }
.auth-form { text-align: left; }
.auth-form-row { display: flex; justify-content: flex-end; margin: 4px 0 var(--space-2); }
.auth-form-row-center { justify-content: center; margin: 8px 0 0; }
.auth-link { color: var(--primary); font-size: 0.85rem; text-decoration: none; font-weight: 600; }
.auth-link:hover { text-decoration: underline; }

/* ── Guest shell in-card controls (redesigned): the KH/EN + Day/Night
   toggle now sits INSIDE the auth card, directly below whichever
   footer link the current view renders (register / back-to-login),
   and the system version / developer credit / social links are
   Admin-editable from Settings → System Branding (settings.manage)
   exactly as before — only WHERE they render changed:
     - .guest-meta-desktop → bottom-left corner of the brand panel,
       ≥992px only (see the .auth-shell-brand-foot rules further down)
     - .guest-meta-mobile  → inside the form card, below
       .guest-controls, <992px only
   Both are built from the same markup in layouts/main.php, so this is
   purely a CSS visibility swap, never a DOM move. All icons (flags +
   socials) are inline local SVG (icon_svg() helper) instead of emoji,
   so nothing here depends on the OS/browser having an emoji font. ─── */
.guest-controls {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    width: 100%;
    margin-top: var(--space-3);
    padding-top: var(--space-2);
    border-top: 1px solid var(--border);
}
.lang-icon-toggle { display: flex; gap: 8px; }
.lang-icon,
.theme-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 12px;
    border: 1px solid var(--border);
    background: var(--surface);
    text-decoration: none;
    color: var(--text);
    cursor: pointer;
    box-shadow: 0 1px 2px rgba(0,0,0,0.04), 0 1px 1px rgba(0,0,0,0.03);
    transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
.lang-icon:hover,
.theme-icon:hover { transform: translateY(-2px); box-shadow: 0 4px 10px rgba(0,0,0,0.08); }
.lang-icon.is-active {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 20%, transparent);
}
.lang-icon svg { width: 22px; height: 15px; border-radius: 3px; display: block; }
.theme-icon svg { width: 18px; height: 18px; }
.guest-meta {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.78rem;
    color: var(--text-muted);
    text-align: center;
    flex-wrap: wrap;
    justify-content: center;
}
.guest-meta-sep { opacity: 0.5; }
.social-icons { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }
.social-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 10px;
    border: 1px solid var(--border);
    background: var(--surface);
    text-decoration: none;
    color: var(--text-muted);
    box-shadow: 0 1px 2px rgba(0,0,0,0.04);
    transition: transform 0.15s ease, box-shadow 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.social-icon svg { width: 16px; height: 16px; }
.social-icon:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 10px rgba(0,0,0,0.08);
    color: var(--primary);
    border-color: var(--primary);
}

/* Mobile-only duplicate of the version/developer/social block — lives
   inside the form card, right under .guest-controls. Hidden ≥992px,
   where the desktop copy in the brand panel takes over instead. */
.guest-meta-mobile {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    width: 100%;
    margin-top: var(--space-2);
}
@media (min-width: 992px) {
    .guest-meta-mobile { display: none; }
}

/* ═══════════════════════════════════════════════════════════════
   S06+ — Premium Split-Panel Auth Redesign
   (Login / Register / Forgot Password / Reset Password)
   Reference: Premium UI Spec (Master List §4) + Section 6.5 responsive
   rules. Two-panel "brand + form" card on tablet-landscape/desktop
   (≥992px), collapsing to a single-column compact-header layout on
   phones — verified at 375 / 768 / 1440px per Rule #11. The System
   Branding footer (lang, theme, version, developer, socials) from
   the S06 addendum is untouched and simply sits below this card via
   the existing .guest-footer. Every color is a CSS variable from
   themes.css (Day/Night both render correctly); every technique here
   (flexbox/grid/color-mix) is already used elsewhere in this file,
   so no additional browser support burden is introduced. ───────── */
.auth-shell-page {
    position: relative;
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-3);
    overflow: hidden;
}
.auth-shell-page-bg {
    position: absolute;
    inset: 0;
    z-index: 0;
    background:
        radial-gradient(circle at 12% 8%, color-mix(in srgb, var(--primary) 14%, var(--bg)), transparent 42%),
        radial-gradient(circle at 90% 92%, color-mix(in srgb, var(--accent) 12%, var(--bg)), transparent 42%),
        var(--bg);
}
.auth-shell-wrap {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 460px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
}
.auth-shell {
    width: 100%;
    display: grid;
    grid-template-columns: 1fr;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 20px;
    overflow: hidden;
    box-shadow: var(--shadow-ambient), var(--shadow-contact), 0 30px 60px -32px rgba(16, 24, 40, 0.35);
}

/* ── Brand panel ─────────────────────────────────────────────── */
.auth-shell-brand {
    position: relative;
    overflow: hidden;
    color: #FFFFFF;
    padding: var(--space-3);
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: var(--space-2);
    background: linear-gradient(155deg,
        var(--primary) 0%,
        color-mix(in srgb, var(--accent) 65%, var(--primary)) 55%,
        color-mix(in srgb, var(--primary) 55%, #000000) 130%);
}
.auth-shell-brand::before,
.auth-shell-brand::after {
    content: '';
    position: absolute;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.14);
    z-index: 0;
    pointer-events: none;
}
.auth-shell-brand::before { width: 200px; height: 200px; top: -70px; right: -55px; }
.auth-shell-brand::after  { width: 150px; height: 150px; bottom: -55px; left: -35px; background: rgba(255, 255, 255, 0.08); }
.auth-shell-brand > * { position: relative; z-index: 1; }

.auth-shell-brand-top { display: flex; align-items: center; gap: 12px; width: 100%; }
.auth-shell-logo-mark {
    width: 40px;
    height: 40px;
    border-radius: 12px;
    flex-shrink: 0;
    overflow: hidden;
    background: rgba(255, 255, 255, 0.16);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.2rem;
}
.auth-shell-logo-mark img { width: 100%; height: 100%; object-fit: contain; }
.auth-shell-logo-mark svg { width: 26px; height: 18px; border-radius: 3px; display: block; }
.auth-shell-brand-name { font-weight: 800; font-size: 1.15rem; letter-spacing: 0.2px; line-height: 1.25; }

/* Compact phone header: logo + name centered as a group (Section
   6.5 collapsed layout — the brand panel is just this header below
   992px, .auth-shell-brand-mid stays hidden per the rule further
   down). */
@media (max-width: 991.98px) {
    .auth-shell-brand-top { justify-content: center; text-align: center; }
}

.auth-shell-brand-mid { display: none; }
.auth-shell-brand-title {
    font-family: var(--font-kh-heading), var(--font-en-heading);
    font-size: 1.5rem;
    line-height: 1.4;
    margin: 0 0 10px;
}
.auth-shell-brand-desc { font-size: 0.87rem; line-height: 1.65; opacity: 0.92; margin: 0 0 var(--space-3); max-width: 340px; }

.auth-feature-list { display: flex; flex-direction: column; gap: 14px; margin: 0; padding: 0; list-style: none; }
.auth-feature-item { display: flex; align-items: center; gap: 12px; font-size: 0.85rem; }
.auth-feature-icon {
    width: 32px;
    height: 32px;
    border-radius: 10px;
    flex-shrink: 0;
    background: rgba(255, 255, 255, 0.16);
    display: flex;
    align-items: center;
    justify-content: center;
}
.auth-feature-icon svg { width: 16px; height: 16px; display: block; }
/* Desktop-only: version / developer credit / social links, bottom-left
   of the brand panel (replaces the old duplicated brand-name text).
   Hidden by default (mobile-first); shown ≥992px by .guest-meta-desktop
   further down, alongside its @media block. Colors are white/
   translucent overrides because this sits on the colored gradient,
   not the plain --surface background the shared .guest-meta /
   .social-icon rules assume. */
.auth-shell-brand-foot {
    display: none;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
    font-size: 0.72rem;
}
.auth-shell-brand-foot .guest-meta {
    color: rgba(255, 255, 255, 0.82);
    justify-content: flex-start;
    text-align: left;
}
.auth-shell-brand-foot .social-icons { justify-content: flex-start; }
.auth-shell-brand-foot .social-icon {
    background: rgba(255, 255, 255, 0.14);
    border-color: rgba(255, 255, 255, 0.28);
    color: rgba(255, 255, 255, 0.92);
}
.auth-shell-brand-foot .social-icon:hover {
    background: rgba(255, 255, 255, 0.24);
    border-color: rgba(255, 255, 255, 0.55);
    color: #ffffff;
}
@media (min-width: 992px) {
    .auth-shell-brand-foot { display: flex; }
    .auth-shell-brand-name { font-size: 1.35rem; }
}

/* ── Form panel ──────────────────────────────────────────────── */
.auth-shell-form {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-3);
    background: var(--surface);
}
.auth-shell-form-inner { width: 100%; max-width: 360px; }

.auth-heading { display: flex; align-items: center; gap: 12px; margin-bottom: 4px; }
.auth-heading-icon {
    width: 42px;
    height: 42px;
    border-radius: 13px;
    flex-shrink: 0;
    background: color-mix(in srgb, var(--primary) 12%, var(--surface));
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.2rem;
}
.auth-title { margin: 0; font-size: 1.32rem; }
.auth-subtitle { color: var(--text-muted); font-size: 0.87rem; line-height: 1.55; margin: 8px 0 var(--space-3); }

/* Icon-accented fields — layers on top of the existing .form-group
   input styling (border/radius/focus-ring) already defined above,
   so nothing about the base form system changes for every OTHER
   module's forms (Rule #5/#11 consistency). */
.form-group.has-icon .input-icon-wrap,
.form-group.has-icon .input-with-action { position: relative; display: flex; align-items: center; }
.form-group.has-icon .input-icon {
    position: absolute;
    left: 14px;
    z-index: 1;
    display: flex;
    align-items: center;
    color: var(--text-muted);
    font-size: 0.95rem;
    pointer-events: none;
}
.form-group.has-icon input[type="text"],
.form-group.has-icon input[type="email"],
.form-group.has-icon input[type="password"],
.form-group.has-icon input[type="tel"],
.form-group.has-icon input[type="search"],
.form-group.has-icon input[type="number"] {
    padding-left: 42px;
    width: 100%;
    box-sizing: border-box;
}

.auth-submit-btn { margin-top: 4px; }
.auth-submit-btn .btn-arrow { display: inline-block; transition: transform 0.15s ease; }
.auth-submit-btn:hover .btn-arrow { transform: translateX(3px); }

/* HR-Assisted Reset — identity verification step (AUTHENTICATION
   UPGRADE patch). "Not you? Change" banner shown once the phone/
   username has been confirmed against a real account and Step 2
   (code + new password) has been unlocked. */
.verified-identity-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    background: color-mix(in srgb, var(--success) 14%, transparent);
    border: 1px solid var(--success);
    color: var(--success);
    padding: 10px 14px;
    border-radius: var(--radius-sm);
    font-size: 0.85rem;
    font-weight: 600;
    margin-bottom: var(--space-3);
}
.verified-identity-banner .verified-identity-name { overflow-wrap: anywhere; }
.verified-identity-banner .link-btn {
    background: none;
    border: none;
    color: var(--primary);
    text-decoration: underline;
    cursor: pointer;
    font-size: 0.76rem;
    font-weight: 600;
    padding: 0;
    flex-shrink: 0;
    white-space: nowrap;
}
.verify-identity-btn-row { margin-top: var(--space-2); }

.auth-shell-form-foot {
    margin-top: var(--space-3);
    padding-top: var(--space-2);
    border-top: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    font-size: 0.85rem;
    color: var(--text-muted);
    text-align: center;
}
.auth-shell-form-foot-row { display: flex; align-items: center; justify-content: center; gap: 6px; flex-wrap: wrap; }

/* ── Responsive: two real panels from tablet-landscape/desktop
   upward (matches the system's existing lg breakpoint, Section 6.5);
   compact single-column "header + form" below it. ───────────────── */
@media (min-width: 992px) {
    .auth-shell-wrap { max-width: 980px; }
    .auth-shell { grid-template-columns: 1fr 1.05fr; min-height: 560px; }
    .auth-shell-brand { padding: var(--space-4); }
    .auth-shell-brand-mid { display: block; margin: auto 0; }
    .auth-shell-brand-title { font-size: 1.75rem; }
    .auth-shell-form { padding: var(--space-4); }
}
@media (max-width: 575px) {
    .auth-shell-page { padding: var(--space-2); }
    .auth-shell { border-radius: 16px; }
    .auth-shell-brand { padding: var(--space-2) var(--space-2) 18px; }
    .auth-shell-form { padding: var(--space-3) var(--space-2); }
    .auth-heading-icon { width: 38px; height: 38px; font-size: 1.05rem; }
    .auth-title { font-size: 1.18rem; }
}

/* ── App shell: sidebar (Section 6.5-6.7) ──────────────────────── */
.sidebar {
    background: var(--surface);
    border-right: 1px solid var(--border);
    flex-direction: column;
    justify-content: space-between;
    transition: transform 0.25s ease;
}
.sidebar-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: var(--space-2);
    border-bottom: 1px solid var(--border);
    font-weight: 700;
}
.sidebar-brand-mark { display: flex; align-items: center; }
.sidebar-brand-mark img { height: 28px; width: auto; max-width: 40px; object-fit: contain; }
.sidebar-brand-mark svg { width: 30px; height: 20px; border-radius: 3px; display: block; }
.sidebar-brand-name { font-size: 0.95rem; line-height: 1.3; }
.sidebar-nav { display: flex; flex-direction: column; gap: 2px; padding: var(--space-2); flex: 1; }
.sidebar-link {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    border-radius: var(--radius-sm);
    color: var(--text);
    text-decoration: none;
    font-size: 0.9rem;
    font-weight: 500;
    border: none;
    background: transparent;
    width: 100%;
    text-align: left;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}
.sidebar-link:hover { background: var(--surface-alt); }
.sidebar-link.active { background: color-mix(in srgb, var(--primary) 12%, transparent); color: var(--primary); font-weight: 700; }
.sidebar-link-icon { width: 20px; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
.sidebar-link-icon svg { width: 18px; height: 18px; display: block; }
.sidebar-link-icon.has-badge { position: relative; display: inline-flex; align-items: center; justify-content: center; overflow: visible; }
.badge-dot {
    position: absolute;
    top: -8px;
    right: -10px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--danger);
    color: #fff;
    font-size: 10px;
    line-height: 16px;
    font-weight: 700;
    text-align: center;
    box-shadow: 0 0 0 2px var(--surface);
}
.sidebar-footer { padding: var(--space-2); border-top: 1px solid var(--border); }
.sidebar-logout:hover { background: rgba(220, 38, 38, 0.1); color: var(--danger); }

/* ── App shell: top bar ────────────────────────────────────────── */
.main-wrap { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 100vh; }
.top-bar {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 12px var(--space-3);
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 20;
    /* khmer24.com-style clean floating header — a soft shadow instead
       of relying on the 1px border alone, so the bar reads as a
       raised layer above scrolling content rather than a flat strip. */
    box-shadow: var(--shadow-ambient);
}
.top-bar-title {
    font-weight: 700;
    font-size: 1.05rem;
    flex: 1;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* margin-left: auto keeps the action row (search / lang / theme /
   avatar) pinned to the right edge of the header on its own — it no
   longer depends on .top-bar-title still being there to act as the
   flex spacer, which matters now that the title is hidden on phones
   (see responsive.css). Without this the avatar drifted left instead
   of sitting flush against the edge once the title disappeared. */
.top-bar-actions { display: flex; align-items: center; gap: var(--space-1); margin-left: auto; }

/* Single-icon language menu — shows only the CURRENT locale's flag
   (instead of both flags side by side) and opens a small dropdown to
   switch, exactly like the user-menu avatar dropdown right next to it.
   Fix (S07+ UI pass): the old pure-CSS `:hover` reveal closed the
   instant the pointer left the 44px trigger button — the 8px gap
   between the button and the dropdown (`top: calc(100% + 8px)`) was
   "dead space" outside both boxes, so moving the mouse down to click
   a language option lost hover before the click ever registered.
   Two fixes now work together:
   1. `.lang-menu::after` is an invisible pseudo-element that fills
      exactly that 8px gap and belongs to `.lang-menu`'s own box, so
      hovering through it keeps `.lang-menu:hover` (and therefore the
      JS hover-intent handled in app.js) alive all the way down.
   2. app.js now opens on `mouseenter` and closes on `mouseleave`
      with a short grace delay (see initHoverMenus()) instead of a
      hard CSS `display` flip — a moment's overshoot no longer closes
      the menu, and click/tap still works identically on touch
      devices (app.js skips the hover behaviour there). */
.lang-menu, .user-menu { position: relative; }
.lang-menu::after, .user-menu::after {
    content: "";
    position: absolute;
    left: 0; right: 0;
    top: 100%; height: 8px;
}
.lang-menu-trigger svg { width: 20px; height: 14px; border-radius: 3px; display: block; }
.lang-dropdown {
    position: absolute;
    right: 0;
    top: calc(100% + 8px);
    min-width: 168px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-ambient), var(--shadow-contact);
    overflow: hidden;
    z-index: 30;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    pointer-events: none;
    transition: opacity 0.16s ease, transform 0.16s ease, visibility 0s linear 0.16s;
}
.lang-dropdown.open {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    pointer-events: auto;
    transition: opacity 0.16s ease, transform 0.16s ease, visibility 0s linear 0s;
}
.lang-dropdown-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    text-align: left;
    padding: 10px 14px;
    background: none;
    border: none;
    color: var(--text);
    text-decoration: none;
    font-size: 0.85rem;
    cursor: pointer;
}
.lang-dropdown-item:hover { background: var(--surface-alt); }
.lang-dropdown-item.is-active { color: var(--primary); font-weight: 700; background: color-mix(in srgb, var(--primary) 8%, transparent); }
.lang-dropdown-flag { display: flex; flex-shrink: 0; }
.lang-dropdown-flag svg { width: 22px; height: 15px; border-radius: 3px; display: block; }

.icon-btn {
    background: var(--surface-alt);
    border: 1px solid transparent;
    border-radius: 14px;
    width: 44px;
    height: 44px;
    cursor: pointer;
    font-size: 1.05rem;
    color: var(--text-muted);
    display: flex;
    align-items: center;
    justify-content: center;
    transition: transform 0.2s ease, box-shadow 0.25s ease, color 0.2s ease, background 0.25s ease;
}
.icon-btn svg { width: 19px; height: 19px; }
.icon-btn:hover {
    color: #fff;
    background: linear-gradient(135deg, var(--primary), var(--accent));
    box-shadow: 0 10px 20px color-mix(in srgb, var(--primary) 28%, transparent),
                0 4px 10px color-mix(in srgb, var(--accent) 20%, transparent);
    transform: translateY(-2px);
}
.icon-btn:active { transform: translateY(0); }

/* Compact in-table action icon button — a smaller sibling of .icon-btn
   (which is sized for the 44px header). Used by employees/index.php's
   "System Login" / "Assign Shift" columns for the "no value yet, click
   to create/assign" affordance. */
.action-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    flex-shrink: 0;
    border-radius: 9px;
    border: 1px solid var(--border);
    background: var(--surface-alt);
    color: var(--text-muted);
    cursor: pointer;
    text-decoration: none;
    transition: transform 0.2s ease, box-shadow 0.25s ease, color 0.2s ease, background 0.25s ease;
}
.action-icon-btn svg { width: 14px; height: 14px; }
.action-icon-btn:hover, .action-icon-btn:focus-visible {
    color: #fff;
    background: linear-gradient(135deg, var(--primary), var(--accent));
    box-shadow: 0 6px 14px color-mix(in srgb, var(--primary) 30%, transparent);
    transform: translateY(-1px);
    outline: none;
}

/* Lightweight CSS-only tooltip for compact controls (e.g. .action-icon-btn)
   where a native title="" attribute is too slow/inconsistent to appear.
   Usage: <a class="action-icon-btn tip" data-tip="Explain me">…</a> */
.tip { position: relative; display: inline-flex; }
.tip::after {
    content: attr(data-tip);
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%) translateY(4px);
    background: var(--text);
    color: var(--surface);
    font-size: 0.72rem;
    font-weight: 600;
    white-space: nowrap;
    padding: 5px 9px;
    border-radius: 8px;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s ease, transform 0.15s ease;
    z-index: 40;
    box-shadow: var(--shadow-contact), var(--shadow-ambient);
}
.tip::before {
    content: '';
    position: absolute;
    bottom: calc(100% + 3px);
    left: 50%;
    transform: translateX(-50%);
    border: 5px solid transparent;
    border-top-color: var(--text);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s ease;
    z-index: 40;
}
.tip:hover::after, .tip:hover::before,
.tip:focus-visible::after, .tip:focus-visible::before {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}

/* Stacks a status badge with a small muted caption under it (e.g. role
   name under "Yes", shift name under "Yes") — used by employees/index.php. */
.cell-stack { display: flex; flex-direction: column; gap: 3px; align-items: flex-start; }
.cell-stack-inline { display: flex; align-items: center; gap: 8px; }

/* Hamburger gets the same premium treatment as the other header
   buttons instead of its own flat/borderless style (responsive.css
   still owns display:none ≥768px). */
.hamburger {
    width: 44px; height: 44px;
    border-radius: 14px;
    background: var(--surface-alt);
    flex-direction: column;
    align-items: center;
    justify-content: center;
    transition: background 0.25s ease, transform 0.2s ease;
}
.hamburger:hover { background: color-mix(in srgb, var(--primary) 14%, var(--surface-alt)); }

/* ── User menu / dropdown ──────────────────────────────────────── */
.user-menu-trigger {
    display: flex;
    align-items: center;
    gap: 8px;
    background: var(--surface-alt);
    border: 1px solid transparent;
    border-radius: 999px;
    padding: 4px 14px 4px 4px;
    cursor: pointer;
    color: var(--text);
    transition: box-shadow 0.25s ease, transform 0.2s ease;
}
.user-menu-trigger:hover {
    box-shadow: 0 8px 18px color-mix(in srgb, var(--primary) 20%, transparent);
    transform: translateY(-1px);
}
.avatar-initial {
    width: 34px; height: 34px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--primary), var(--accent));
    color: #fff;
    display: flex; align-items: center; justify-content: center;
    font-size: 0.9rem; font-weight: 700;
    box-shadow: 0 0 0 2px var(--surface);
}
.avatar-photo {
    width: 34px; height: 34px;
    border-radius: 50%;
    object-fit: cover;
    border: 1px solid var(--border);
    box-shadow: 0 0 0 2px var(--surface);
}
.avatar-photo.avatar-lg { width: 84px; height: 84px; border-width: 2px; }
.avatar-initial.avatar-lg {
    width: 84px; height: 84px;
    font-size: 1.8rem;
}
.user-menu-name { font-size: 0.85rem; font-weight: 600; }
.user-dropdown {
    position: absolute;
    right: 0;
    top: calc(100% + 8px);
    min-width: 200px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-ambient), var(--shadow-contact);
    overflow: hidden;
    z-index: 30;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    pointer-events: none;
    transition: opacity 0.16s ease, transform 0.16s ease, visibility 0s linear 0.16s;
}
.user-dropdown.open {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    pointer-events: auto;
    transition: opacity 0.16s ease, transform 0.16s ease, visibility 0s linear 0s;
}
.user-dropdown-header { padding: 12px 14px; border-bottom: 1px solid var(--border); }
.user-dropdown-name { font-weight: 700; font-size: 0.9rem; }
.user-dropdown-role { font-size: 0.78rem; color: var(--text-muted); }
.user-dropdown-item {
    display: block;
    width: 100%;
    text-align: left;
    padding: 10px 14px;
    background: none;
    border: none;
    color: var(--text);
    text-decoration: none;
    font-size: 0.85rem;
    cursor: pointer;
}
.user-dropdown-item:hover { background: var(--surface-alt); }
.user-dropdown-danger:hover { background: rgba(220, 38, 38, 0.1); color: var(--danger); }

/* ── Bottom nav logout button (matches the <a> siblings) ───────── */
.bottom-nav-logout {
    background: none;
    border: none;
    cursor: pointer;
}

/* ── Stat cards (dashboard) ────────────────────────────────────── */
.stat-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-ambient), var(--shadow-contact);
    padding: var(--space-2);
}
.stat-card-icon {
    width: 38px; height: 38px;
    border-radius: 12px;
    display: flex; align-items: center; justify-content: center;
    background: color-mix(in srgb, var(--primary) 12%, transparent);
    color: var(--primary);
    margin-bottom: 8px;
}
.stat-card-icon svg { width: 19px; height: 19px; display: block; }
.stat-card-label { font-size: 0.8rem; color: var(--text-muted); margin-bottom: 4px; }
.stat-card-value { font-size: 1.6rem; font-weight: 700; }

/* ── Key/value table (dashboard account card, etc.) ────────────── */
.kv-table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
.kv-table th { text-align: left; color: var(--text-muted); font-weight: 600; padding: 8px 0; width: 40%; }
.kv-table td { padding: 8px 0; }
.kv-table tr + tr th, .kv-table tr + tr td { border-top: 1px solid var(--border); }

/* ── Page header (used by dashboard and every future list screen) ─ */
.page-header { margin-bottom: var(--space-3); }
.page-header h1 { margin: 0 0 4px; font-size: 1.5rem; }
.header-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }

/* ═══════════════════════════════════════════════════════════════
   S02 — Company & Settings
   ═══════════════════════════════════════════════════════════════ */

/* ── Settings hub tiles ────────────────────────────────────────── */
.settings-tile {
    text-decoration: none; color: var(--text); cursor: pointer;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.settings-tile:hover { transform: translateY(-3px); }
.settings-tile-icon {
    width: 48px; height: 48px;
    border-radius: 14px;
    display: flex; align-items: center; justify-content: center;
    background: linear-gradient(135deg,
        color-mix(in srgb, var(--primary) 16%, transparent),
        color-mix(in srgb, var(--accent) 10%, transparent));
    color: var(--primary);
    margin-bottom: 10px;
    transition: transform 0.2s ease;
}
.settings-tile:hover .settings-tile-icon { transform: scale(1.06); }
.settings-tile-icon svg { width: 24px; height: 24px; display: block; }
.settings-tile h3 { margin: 0 0 4px; font-size: 1.05rem; }

/* ── Two-column form row (stacks on mobile via responsive.css .form-row,
   but this is a plain CSS grid variant for settings forms) ──────── */
.form-row-2 {
    display: grid; grid-template-columns: 1fr; gap: 0 var(--space-2);
}
@media (min-width: 768px) {
    .form-row-2 { grid-template-columns: 1fr 1fr; }
}

/* ── Three-column form row — Login Screen Content feature rows
   (icon select + KH text + EN text), same stacking rule as
   .form-row-2 but a narrower first column for the icon select. ── */
.form-row-3 {
    display: grid; grid-template-columns: 1fr; gap: 0 var(--space-2);
}
@media (min-width: 768px) {
    .form-row-3 { grid-template-columns: 160px 1fr 1fr; }
}

/* ── Logo upload ───────────────────────────────────────────────── */
.logo-upload-row {
    display: flex; align-items: center; gap: var(--space-2);
    margin-bottom: var(--space-2); flex-wrap: wrap;
}
.logo-preview {
    width: 72px; height: 72px; border-radius: var(--radius-sm);
    border: 1px dashed var(--border); background: var(--surface-alt);
    display: flex; align-items: center; justify-content: center;
    overflow: hidden; flex-shrink: 0; font-size: 1.8rem;
}
.logo-preview img { width: 100%; height: 100%; object-fit: contain; }

/* ── Toggle switch (Attendance Sources — Master List v2 §2) ─────── */
.switch { position: relative; display: inline-block; width: 46px; height: 26px; flex-shrink: 0; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch-track {
    position: absolute; cursor: pointer; inset: 0;
    background: var(--border); border-radius: 999px;
    transition: background 0.2s ease;
}
.switch-track::before {
    content: ''; position: absolute; height: 20px; width: 20px;
    left: 3px; top: 3px; background: #fff; border-radius: 50%;
    transition: transform 0.2s ease; box-shadow: 0 1px 3px rgba(0,0,0,0.3);
}
.switch input:checked + .switch-track { background: var(--primary); }
.switch input:checked + .switch-track::before { transform: translateX(20px); }
.switch input:focus-visible + .switch-track { box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 25%, transparent); }

/* Compact .switch variant — Login Screen Content per-slot Active
   toggle (Settings → System Branding, patch: 3 → 6 feature slots). */
.switch-sm { width: 36px; height: 20px; }
.switch-sm .switch-track::before { height: 14px; width: 14px; }
.switch-sm input:checked + .switch-track::before { transform: translateX(16px); }

/* Login Screen Content — per-slot header row: "Feature N" label on
   the left, Clear button + Active switch on the right. Stacks on
   mobile (<576px) so the Clear button/switch never crowd the label
   on narrow screens (Global Rule: responsive at 375/768/1440px). */
.login-feature-slot-header {
    display: flex; align-items: center; justify-content: space-between;
    gap: 10px; flex-wrap: wrap; margin: 0 0 8px;
}
.login-feature-slot-actions { display: flex; align-items: center; gap: 10px; }
.login-feature-clear-btn { display: inline-flex; align-items: center; gap: 4px; }
.login-feature-clear-btn svg { width: 12px; height: 12px; }
@media (max-width: 375px) {
    .login-feature-slot-header { flex-direction: column; align-items: flex-start; }
}

.source-row {
    display: flex; align-items: flex-start; gap: 14px;
    padding: var(--space-2) 0; border-bottom: 1px solid var(--border);
}
.source-row:last-of-type { border-bottom: none; }
.source-row-icon {
    display: flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; flex-shrink: 0;
    border-radius: 10px; background: var(--surface-alt);
    color: var(--primary);
}
.source-row-icon svg { width: 22px; height: 22px; display: block; }
.source-row-body { flex: 1; min-width: 0; }
.source-row-title { font-weight: 700; font-size: 0.95rem; }
.source-row-desc { color: var(--text-muted); font-size: 0.8rem; margin-top: 2px; }
.source-row-extra {
    margin-top: 8px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    font-size: 0.82rem;
    color: var(--text-muted);
}
.source-row-extra input { padding: 6px 8px; border-radius: 6px; border: 1px solid var(--border); background: var(--surface); color: var(--text); }

/* ═══════════════════════════════════════════════════════════════
   S03 — Departments & Positions
   ═══════════════════════════════════════════════════════════════ */
.filter-bar { margin-bottom: var(--space-2); display: flex; flex-wrap: wrap; gap: var(--space-2); justify-content: space-between; align-items: flex-end; }
.filter-form { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end; }
/* ── Search box, given the same "caption label above field" shape as
   .location-cascade-field (Province/District/Commune) so every box in
   the filter row is the same height and bottom-aligned — previously
   the bare search input (no label) sat shorter than, and vertically
   misaligned with, the labeled cascade selects next to it. ── */
.filter-search-field {
    display: flex; flex-direction: column; gap: 4px;
    flex: 1 1 220px; min-width: 180px; max-width: 320px;
}
.filter-search-field label {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.03em; color: var(--text-muted);
}
.filter-search-field label svg { width: 13px; height: 13px; flex-shrink: 0; color: var(--primary); }
@media (max-width: 575.98px) {
    .filter-search-field { max-width: 100%; flex-basis: 100%; }
}
.filter-form select, .filter-form input {
    padding: 8px 12px; border-radius: var(--radius-sm); border: 1px solid var(--border);
    background: var(--surface); color: var(--text); font-size: 0.85rem;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.filter-form select:focus, .filter-form input:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 18%, transparent);
}
/* Search box, styled to match the header bar's premium search
   (rounded pill + inline magnifier icon) instead of a bare text
   field. The magnifier is an inline SVG data-URI (not an emoji, not
   an icon font) so it renders identically pixel-for-pixel on every
   browser; two colour variants keep it legible in both Day and Night
   mode since a CSS background-image can't use currentColor. */
.filter-form input[type="text"] {
    flex: 1 1 220px;
    min-width: 180px;
    max-width: 320px;
    padding: 9px 14px 9px 38px;
    border-radius: 999px;
    background-repeat: no-repeat;
    background-position: 13px center;
    background-size: 16px 16px;
}
body[data-theme="light"] .filter-form input[type="text"] {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236B7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m20 20-3.4-3.4'/%3E%3C/svg%3E");
}
body[data-theme="dark"] .filter-form input[type="text"] {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239CA3AF' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m20 20-3.4-3.4'/%3E%3C/svg%3E");
}
@media (max-width: 575.98px) {
    .filter-form input[type="text"] { max-width: 100%; flex-basis: 100%; }
}
/* ── Fix: this input now lives inside .filter-search-field, a COLUMN
   flex wrapper (label above, input below) — not a direct child of
   .filter-form's ROW flex layout the rule above was written for. A
   flex item's `flex-basis` sizes along its DIRECT parent's main axis,
   so inside a column container that same `flex: 1 1 220px` resolves
   against HEIGHT instead of width, inflating the input to ~220px
   tall. Combined with border-radius: 999px (a near-square box), that
   rendered as a giant circle. Reset back to a natural, content-sized
   box that simply fills its wrapper's width. ── */
.filter-search-field input[type="text"] {
    flex: 0 0 auto;
    width: 100%;
    min-width: 0;
    max-width: none;
}
.row-inactive { opacity: 0.55; }
.row-inactive td strong { color: var(--text-muted); }

/* ═══════════════════════════════════════════════════════════════
   S07+ — PRIMER-STYLE ACTION SYSTEM
   Icon + label buttons, connected button groups, and a boxed filter
   panel — applied first to the Location Manager and reused by every
   later screen. Built on the EXISTING .btn / .actions / .filter-bar /
   .tab-btn classes (no new competing button system) so this upgrades
   every current and future view automatically the moment it uses
   icon_svg() inside a .btn — nothing view-specific.
   ═══════════════════════════════════════════════════════════════ */

/* Every icon dropped inside any .btn (primary/outline/danger/xs/sm)
   sizes and aligns itself the same way, everywhere in the app. */
.btn svg { width: 15px; height: 15px; flex-shrink: 0; display: block; }
.btn-xs svg { width: 13px; height: 13px; }
.btn-sm svg { width: 14px; height: 14px; }

/* Icon-only buttons (no visible label — e.g. a compact pagination
   arrow) still get a comfortable square tap target. */
.btn-icon-only { padding: 8px; }
.btn-icon-only.btn-xs { padding: 5px; }

/* ── Connected button group (Primer-style) — used for Import/Export,
   and any other pair/trio of related actions that should read as one
   unit instead of two floating buttons. ── */
.btn-group {
    display: inline-flex;
    border-radius: var(--radius-sm);
    overflow: hidden;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
}
.btn-group .btn {
    border-radius: 0;
    box-shadow: none;
}
.btn-group .btn:not(:last-child) {
    border-right: 1px solid color-mix(in srgb, currentColor 12%, transparent);
}
.btn-group .btn.btn-outline:not(:last-child) { border-right-color: var(--border); }
.btn-group .btn:first-child { border-top-left-radius: var(--radius-sm); border-bottom-left-radius: var(--radius-sm); }
.btn-group .btn:last-child { border-top-right-radius: var(--radius-sm); border-bottom-right-radius: var(--radius-sm); }
.btn-group .btn:hover { transform: none; }

/* ── Table-row action buttons: a touch tighter than a standalone
   .btn-xs so a 3–4 button row (Edit / Activate / Delete / Restore)
   still reads as a clean, evenly-spaced icon+label group instead of
   a wall of text buttons. ── */
.actions { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.actions .btn-xs { padding: 5px 10px; }
.actions form { display: inline-flex; }

/* ── Boxed filter panel — a clearly delimited "Filter" card that
   wraps the existing .filter-bar/.filter-form, matching the Recycle
   Bin's clean search+dropdown+button layout, reused anywhere a list
   needs search + structured filters (not just Locations). ── */
.filter-panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: var(--space-2);
    margin-bottom: var(--space-2);
    box-shadow: var(--shadow-contact);
}
.filter-panel-title {
    display: flex; align-items: center; gap: 8px;
    font-size: 0.78rem; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.04em; color: var(--text-muted);
    margin-bottom: var(--space-1);
}
.filter-panel-title svg { width: 14px; height: 14px; }
/* ── Filter panel header row with actions anchored beside the title
   (Delivery Log) ──────────────────────────────────────────────────
   When a filter row has enough fields that they wrap across two
   lines at common desktop widths, the trailing .filter-panel-actions
   (Filter/Reset) used to wrap down along with whichever field ran
   out of room — landing on its own line, disconnected from and
   visually lower than the rest of the row, regardless of how much
   empty space was left on the line above (a flex-wrap item, once
   pushed to a new line, does not "back-fill" earlier line and
   nothing after it returns to the earlier line either). Anchoring
   Filter/Reset to the panel's title row instead of the end of the
   fields row sidesteps that entirely — the buttons always sit level
   with "FILTER" at a fixed position, independent of how many field
   rows the filter itself wraps to. The buttons stay real <button>/<a>
   elements associated with the form via the HTML `form="…"` attribute
   (button) — no JS required, works with native GET submit exactly as
   before. */
.filter-panel-title-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-bottom: var(--space-1);
}
.filter-panel-title-row .filter-panel-title { margin-bottom: 0; }
@media (max-width: 575.98px) {
    .filter-panel-title-row { flex-direction: column; align-items: stretch; }
    .filter-panel-title-row .filter-panel-actions { width: 100%; }
    .filter-panel-title-row .filter-panel-actions .btn { flex: 1; }
}
/* Fix: on Provinces / Recycle Bin the filter row is just a single-line
   search input + button (~38px tall). On Districts / Communes /
   Villages it also carries the two-line "label above select" cascade
   fields (~58px tall) — so the same .filter-panel rendered visibly
   shorter on the search-only tabs and taller on the cascade tabs.
   A shared min-height on the row (with the existing align-items:center)
   vertically centers whichever content is present, so every tab's
   filter-panel — and therefore the card below it — lines up at the
   exact same height regardless of which tab is active. */
.filter-panel .filter-bar { margin-bottom: 0; min-height: 60px; }

/* ── Cascading location selects (Province → District → Commune →
   Village), each with a small caption label above it so "which level
   am I narrowing" is always obvious at a glance — reusable anywhere
   a location drill-down filter is needed. ── */
.location-cascade {
    display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end;
}
.location-cascade-field { display: flex; flex-direction: column; gap: 4px; width: 190px; flex: 0 0 auto; }
.location-cascade-field label {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.03em; color: var(--text-muted);
}
/* Bug fix: this label was missing an svg-size rule, so the map-pin /
   building / home icon_svg() icons rendered at their unconstrained
   intrinsic size (huge) instead of a small caption icon — the "giant
   broken icon" seen above the Province/District/Commune dropdowns. */
.location-cascade-field label svg { width: 13px; height: 13px; flex-shrink: 0; color: var(--primary); }
.location-cascade-field select { width: 100%; }
/* ── Wide variant — for a field that needs more room than the fixed
   190px cascade columns (e.g. the searchable notification-type
   combo on Notification Delivery Log), used so it can grow
   RIGHTWARD to fill leftover row space, not grow taller.
   IMPORTANT: .location-cascade-field is a COLUMN flex container
   (label above field) — the base `.filter-form input[type="text"]`
   rule further down this file (flex: 1 1 220px + border-radius:999px
   pill + magnifier background icon) is written for a ROW flex
   parent. Applied unmodified inside a COLUMN parent, flex-basis
   resolves against HEIGHT instead of width, inflating the input to
   ~220px tall — combined with the 999px pill radius that's a giant
   circle. This is the exact same bug already fixed once for
   .filter-search-field below; this block is the same fix applied to
   the wide field. Reset to a plain rectangular input matching every
   other .location-cascade-field select, no background icon (the
   field's <label> already carries a search icon instead). ── */
.filter-field-wide { flex: 1 1 260px; width: auto; max-width: 420px; }
.filter-field-wide input[type="text"] {
    flex: 0 0 auto;
    width: 100%;
    min-width: 0;
    max-width: none;
    padding: 8px 12px;
    border-radius: var(--radius-sm);
    background-image: none;
}
@media (max-width: 767.98px) {
    .filter-field-wide { max-width: none; }
}
.filter-panel-actions { display: flex; gap: 8px; align-items: center; margin-left: auto; flex: 0 0 auto; }
/* Filter / Reset should read as one balanced pair, not two
   differently-sized buttons whose width just happens to follow their
   label length ("Filter" vs "Reset", or the longer Khmer equivalents).
   Both share this fixed min-width so they always match; the buttons
   still center their icon+label (.btn is already display:inline-flex;
   justify-content:center) so this never clips or off-centers either
   label. */
.filter-panel-actions .btn { min-width: 108px; }
@media (max-width: 575.98px) {
    .filter-panel-actions .btn { min-width: 0; }
}

/* ── Searchable Combo (searchable-combo.js) ──────────────────────
   Fully local/CSS-controlled replacement for a native
   <input list>/<datalist> — used wherever a bilingual dropdown needs
   readable, theme-matched typography, which a native datalist popup
   (rendered by the OS, not the page) can never provide. Built on the
   same .location-cascade-field label/spacing already used by every
   other filter field, so it reads as one consistent filter row. ── */
.combo-field { position: relative; }
.combo-input-wrap { position: relative; display: flex; align-items: center; }
.combo-search-input {
    width: 100%;
    /* !important: .filter-field-wide input[type="text"] (added for the
       earlier native-datalist input) has higher CSS specificity than
       this single class and would otherwise win, flattening this
       padding back to 8px 12px and crowding the search text under the
       clear (×) button on the right. That legacy rule's flex/width
       reset is still correct and needed here (this input also lives
       inside a wide field); only its padding conflicts. */
    padding: 8px 34px 8px 12px !important;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    font-size: 0.85rem;
    font-family: inherit;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.combo-search-input:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 18%, transparent);
}
/* Fix: .filter-form input[type="text"] (the pill-shaped search box
   further up this file) paints a magnifier icon into EVERY text input
   inside .filter-form via background-image, positioned at 13px from
   the left. This combo input already shows a search icon in its own
   <label> (searchable-combo fields always render one), so that second,
   background-image icon was overlapping the placeholder/typed text
   inside the box itself. Kill it here specifically for this input —
   both theme variants, since the rule being undone is theme-scoped —
   so only the label's icon remains, exactly like every other
   .location-cascade-field select next to it. */
.filter-form input[type="text"].combo-search-input,
body[data-theme="light"] .filter-form input[type="text"].combo-search-input,
body[data-theme="dark"] .filter-form input[type="text"].combo-search-input {
    background-image: none !important;
}
.combo-clear-btn {
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
    border-radius: 999px;
    transition: background 0.15s ease, color 0.15s ease;
}
.combo-clear-btn svg { width: 12px; height: 12px; }
.combo-clear-btn:hover { background: var(--border); color: var(--text); }
.combo-clear-btn[hidden] { display: none; }

/* Dropdown panel — positioned to the field, not the viewport, so it
   scrolls naturally with the page (this filter row never sits inside
   an overflow-clipping ancestor the way a table does). */
.combo-dropdown {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    z-index: 60;
    max-height: 280px;
    overflow-y: auto;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-ambient), var(--shadow-contact);
    padding: 6px;
}
.combo-option {
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 8px 10px;
    border-radius: 6px;
    cursor: pointer;
    transition: background 0.12s ease;
}
.combo-option[hidden] { display: none; }
.combo-option:hover,
.combo-option.is-active {
    background: color-mix(in srgb, var(--primary) 12%, transparent);
}
.combo-option-primary {
    font-size: 0.86rem;
    font-weight: 600;
    color: var(--text);
    line-height: 1.35;
}
.combo-option-secondary {
    font-size: 0.76rem;
    color: var(--text-muted);
    line-height: 1.3;
}
.combo-no-results {
    padding: 10px;
    text-align: center;
    font-size: 0.82rem;
    color: var(--text-muted);
}
@media (max-width: 767.98px) {
    .combo-dropdown { max-height: 240px; }
}
@media (max-width: 767.98px) {
    .location-cascade { flex-basis: 100%; }
    .location-cascade-field { width: 100%; flex: 1 1 100%; }
    .filter-panel-actions { margin-left: 0; width: 100%; }
    .filter-panel-actions .btn { flex: 1; }
}

/* ── Delivery Log filter row (Notification Delivery Log) ──────────
   This row has more fields than a typical .location-cascade filter
   bar (channel, notification type, status, date-from, date-to).
   Giving each field a fixed width and letting flex-wrap decide when
   to break the row was fragile — a flex item that wraps starts a
   brand-new line and nothing after it ever moves back up to fill
   left-over space on the line above, so a field could still drop to
   its own line even with plenty of visible room left. To guarantee
   every field genuinely stays on ONE row at normal desktop widths,
   this forces flex-wrap: nowrap and lets each field flex-SHRINK
   (instead of wrap) if the row is ever tighter than the fields'
   preferred widths, down to a sensible min-width floor per field.
   Only below the tablet breakpoint does the row intentionally switch
   back to wrapping/stacking, matching the rest of the app's mobile
   pattern. Scoped to .filter-form-compact only — no other filter bar
   in the app is affected. */
.filter-form-compact.filter-form {
    flex-wrap: nowrap;
    column-gap: 8px;
}
.filter-form-compact .location-cascade-field {
    width: auto;
    flex: 0 1 140px;
    min-width: 108px;
}
.filter-form-compact .field-date-wide {
    /* Widened so the dd/mm/yyyy display + trailing calendar icon
       (.s11-picker-field) always has enough room — at the old 210px
       basis the icon crowded/overlapped the last digits. */
    flex: 0 1 250px;
    min-width: 190px;
}
.filter-form-compact .filter-field-wide {
    /* Widened so the notification-type search placeholder/typed
       value is fully readable instead of clipping mid-word. */
    flex: 1 1 300px;
    max-width: 460px;
    min-width: 220px;
}
/* The Filter/Reset icon-only buttons live inside this same nowrap row
   (matching the Mobile/QR Check-In Log's filter-bar pattern) — pin
   them to their natural size so the shrinking rule above never
   squashes them along with the fields. */
.filter-form-compact.filter-form > .btn {
    flex: 0 0 auto;
}
@media (max-width: 1300px) {
    .filter-form-compact.filter-form { flex-wrap: wrap; }
    .filter-form-compact .location-cascade-field,
    .filter-form-compact .field-date-wide,
    .filter-form-compact .filter-field-wide {
        flex: 1 1 150px;
        max-width: none;
        min-width: 0;
    }
}
@media (max-width: 767.98px) {
    .filter-form-compact .location-cascade-field,
    .filter-form-compact .field-date-wide {
        width: 100%;
        flex: 1 1 100%;
    }
}

/* ── Locations page: box the tab-nav to visually match the
   .filter-panel box directly below it. Scoped to .tabs-nav-boxed
   only (added alongside .tabs-nav) so the plain underline-tab style
   used elsewhere (e.g. Employee Registration form tabs) is untouched. ── */
.tabs-nav.tabs-nav-boxed {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-contact);
    padding: 6px 10px;
}
.tabs-nav.tabs-nav-boxed .tab-btn { padding: 8px 14px; }
@media (max-width: 767.98px) {
    .tabs-nav.tabs-nav-boxed { padding: 6px 8px; }
}

/* ── Tabs: icon + label, Primer segmented-tab spacing. ── */
.tab-btn { display: inline-flex; align-items: center; gap: 6px; }
.tab-btn svg { width: 15px; height: 15px; }

/* ═══════════════════════════════════════════════════════════════
   S04 — Employee Registration (M01)
   ═══════════════════════════════════════════════════════════════ */

/* ── Form Tabs (Alpine.js x-data, no framework) ─────────────────
   .tabs-nav itself (overflow-x:auto/white-space:nowrap) already
   exists in responsive.css (Section 6.5) — this only styles the
   individual tab buttons + panels on top of it. */
.tabs-nav {
    display: flex; gap: 4px; border-bottom: 1px solid var(--border);
    margin-bottom: var(--space-2); padding-bottom: 0;
}
.tab-btn {
    background: none; border: none; cursor: pointer;
    padding: 10px 16px; font-size: 0.88rem; font-weight: 600;
    color: var(--text-muted); border-bottom: 2px solid transparent;
    white-space: nowrap; transition: color 0.15s ease, border-color 0.15s ease;
}
.tab-btn:hover { color: var(--text); }
.tab-btn.active { color: var(--primary); border-bottom-color: var(--primary); }
.tab-panel { display: none; }
.tab-panel.active { display: block; }

/* ── Employee photo upload (Personal tab) ───────────────────────
   Mirrors .logo-upload-row/.logo-preview but round, like an avatar. */
.employee-photo-row {
    display: flex; align-items: center; gap: var(--space-2);
    margin-bottom: var(--space-2); flex-wrap: wrap;
}
.employee-photo-preview {
    width: 84px; height: 84px; border-radius: 50%;
    border: 2px solid var(--border); background: var(--surface-alt);
    display: flex; align-items: center; justify-content: center;
    overflow: hidden; flex-shrink: 0; font-size: 1.8rem; color: var(--text-muted);
}
.employee-photo-preview img { width: 100%; height: 100%; object-fit: cover; }

/* ── Employee profile header (show.php) ─────────────────────────── */
.employee-header {
    display: flex; align-items: center; gap: var(--space-2);
    flex-wrap: wrap; margin-bottom: var(--space-2);
}
.employee-header .avatar-photo.avatar-xl,
.employee-header .avatar-initial.avatar-xl { width: 96px; height: 96px; font-size: 2rem; border-width: 2px; }
.employee-header-name-en { font-size: 1.3rem; font-weight: 700; }
.employee-header-name-kh { font-size: 1.05rem; color: var(--text-muted); }
.employee-header-meta { font-size: 0.85rem; color: var(--text-muted); margin-top: 2px; }

/* ── Employee headcount summary strip (index.php) ───────────────── */
.stat-strip {
    display: flex; gap: var(--space-2); flex-wrap: wrap;
    margin-bottom: var(--space-2);
}
.stat-pill {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius-sm); padding: 8px 16px;
    display: flex; align-items: center; gap: 8px; font-size: 0.85rem;
    box-shadow: var(--shadow-ambient);
}
.stat-pill .stat-pill-count { font-weight: 700; font-size: 1rem; color: var(--primary); }

/* ── Documents / Contracts lists (show.php tabs) ─────────────────── */
.doc-row, .contract-row, .assignment-row {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-2); padding: 12px 0; border-bottom: 1px solid var(--border);
    flex-wrap: wrap;
}
.doc-row:last-child, .contract-row:last-child, .assignment-row:last-child { border-bottom: none; }
.doc-row-icon { font-size: 1.4rem; }
.doc-row-body, .contract-row-body, .assignment-row-body { flex: 1; min-width: 180px; }
.doc-row-title, .contract-row-title, .assignment-row-title { font-weight: 600; font-size: 0.9rem; }
.doc-row-meta, .contract-row-meta, .assignment-row-meta { font-size: 0.78rem; color: var(--text-muted); margin-top: 2px; }

/* ── Pagination (index.php) ──────────────────────────────────────── */
.pagination {
    display: flex; align-items: center; justify-content: center;
    gap: 6px; margin-top: var(--space-2); flex-wrap: wrap;
}
.pagination a, .pagination span {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 34px; height: 34px; border-radius: var(--radius-sm);
    border: 1px solid var(--border); font-size: 0.85rem; text-decoration: none;
    color: var(--text);
}
.pagination a:hover { background: var(--surface-alt); }
.pagination .pagination-current { background: var(--primary); color: #fff; border-color: var(--primary); font-weight: 700; }
.pagination .pagination-disabled { opacity: 0.4; pointer-events: none; }
.pagination-ellipsis {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 24px; height: 34px; color: var(--text-muted); font-size: 0.85rem;
}

/* ═══════════════════════════════════════════════════════════════
   S07 — Hikvision Device Setup (M02)
   ═══════════════════════════════════════════════════════════════ */

/* Device card grid item — uses the existing .card (soft double-layer
   shadow, Premium UI §4) as its base, this only adds the internal
   layout on top of it. */
.device-card { display: flex; flex-direction: column; gap: var(--space-2); transition: transform 0.15s ease, box-shadow 0.15s ease; }
.device-card:hover { transform: translateY(-2px); }
.device-card.row-inactive { opacity: 0.6; }

.device-card-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.device-card-icon { font-size: 1.6rem; line-height: 1; }
.device-card-title { flex: 1; min-width: 0; }
.device-status-badge { margin-left: auto; white-space: nowrap; }

.device-card-body { display: flex; flex-direction: column; gap: 4px; }
.device-card-row { display: flex; justify-content: space-between; gap: 8px; font-size: 0.85rem; }
.device-card-note {
    font-size: 0.78rem; color: var(--text-muted); background: var(--surface-alt);
    border-radius: var(--radius-sm); padding: 6px 10px; margin-top: 2px;
}

.device-card-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; padding-top: var(--space-2); border-top: 1px solid var(--border); }


/* ═══════════════════════════════════════════════════════════════
   S07+ UI PASS — Premium Nav Search Modal (Ctrl/Cmd+K)
   Fed by window.APP_NAV_SEARCH (built in layouts/main.php from the
   same can() gates as the sidebar) and driven entirely by app.js —
   no network round-trip, no external command-palette library.
   ═══════════════════════════════════════════════════════════════ */
.nav-search-overlay {
    position: fixed; inset: 0; z-index: 200;
    background: rgba(13, 13, 26, 0.5);
    display: flex; align-items: flex-start; justify-content: center;
    padding: 10vh 16px 16px;
    backdrop-filter: blur(2px);
}
.nav-search-overlay[hidden] { display: none; }
.nav-search-modal {
    width: 100%; max-width: 560px;
    background: var(--surface);
    border-radius: var(--radius);
    box-shadow: var(--shadow-ambient), var(--shadow-contact), 0 24px 48px rgba(0,0,0,0.24);
    border: 1px solid var(--border);
    overflow: hidden;
    animation: navSearchIn 0.18s ease;
}
@keyframes navSearchIn { from { opacity: 0; transform: translateY(-8px) scale(0.98); } to { opacity: 1; transform: none; } }
.nav-search-input-row {
    display: flex; align-items: center; gap: 10px;
    padding: 14px 16px;
    border-bottom: 1px solid var(--border);
}
.nav-search-input-icon { color: var(--primary); display: flex; flex-shrink: 0; }
.nav-search-input-icon svg { width: 20px; height: 20px; }
.nav-search-input {
    flex: 1; border: none; outline: none; background: none;
    font-size: 1rem; color: var(--text); min-width: 0;
}
.nav-search-clear {
    background: var(--surface-alt); border: none; border-radius: 999px;
    width: 22px; height: 22px; display: flex; align-items: center; justify-content: center;
    color: var(--text-muted); cursor: pointer; flex-shrink: 0;
}
.nav-search-clear[hidden] { display: none; }
.nav-search-clear svg { width: 12px; height: 12px; }
.nav-search-kbd {
    font-size: 0.72rem; color: var(--text-muted);
    background: var(--surface-alt); border: 1px solid var(--border);
    border-radius: 6px; padding: 2px 7px; flex-shrink: 0;
}
.nav-search-results { list-style: none; margin: 0; padding: 8px; max-height: 50vh; overflow-y: auto; }
.nav-search-result {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 10px; border-radius: var(--radius-sm);
    text-decoration: none; color: var(--text); cursor: pointer;
}
.nav-search-result-icon { width: 22px; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; color: var(--text-muted); }
.nav-search-result-icon svg { width: 17px; height: 17px; display: block; }
.nav-search-result-label { flex: 1; font-size: 0.9rem; font-weight: 600; }
.nav-search-result-group { font-size: 0.72rem; color: var(--text-muted); }
.nav-search-result.is-active,
.nav-search-result:hover { background: color-mix(in srgb, var(--primary) 12%, transparent); color: var(--primary); }
.nav-search-empty { padding: 24px 16px; text-align: center; color: var(--text-muted); font-size: 0.85rem; }
.nav-search-empty[hidden] { display: none; }

/* ═══════════════════════════════════════════════════════════════
   S09 UI FIX — Global Image Lightbox
   Any element anywhere in the app (button/link/img) can add
   data-lightbox-image="/path/to/image.jpg" and clicking it opens the
   photo in this in-page overlay instead of navigating to a new tab.
   Click the dark backdrop, press Escape, or use the ✕ button to
   close. Markup lives ONCE in layouts/main.php (Section 24 — one
   shared layout); behavior is bound ONCE in app.js's
   initImageLightbox() via event delegation, so every current and
   future selfie/photo/scan preview link in the system gets this for
   free with no per-view JS.
   ═══════════════════════════════════════════════════════════════ */
.img-lightbox-overlay {
    position: fixed; inset: 0; z-index: 220;
    background: rgba(13, 13, 26, 0.86);
    display: flex; align-items: center; justify-content: center;
    padding: 24px;
    opacity: 0; visibility: hidden;
    transition: opacity 0.2s ease, visibility 0.2s ease;
}
.img-lightbox-overlay[hidden] { display: none; }
.img-lightbox-overlay.open { opacity: 1; visibility: visible; }
.img-lightbox-figure {
    position: relative; margin: 0;
    max-width: min(92vw, 900px); max-height: 90vh;
    display: flex; align-items: center; justify-content: center;
}
.img-lightbox-figure img {
    display: block;
    max-width: 100%; max-height: 90vh; width: auto; height: auto;
    object-fit: contain;
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: var(--shadow-ambient), var(--shadow-contact), 0 24px 60px rgba(0,0,0,0.45);
    animation: imgLightboxZoomIn 0.18s ease;
}
@keyframes imgLightboxZoomIn { from { opacity: 0; transform: scale(0.94); } to { opacity: 1; transform: none; } }
.img-lightbox-close {
    position: absolute; top: -14px; right: -14px;
    width: 36px; height: 36px; border-radius: 999px;
    background: var(--surface); color: var(--text);
    border: 1px solid var(--border);
    display: flex; align-items: center; justify-content: center;
    cursor: pointer; box-shadow: var(--shadow-ambient), var(--shadow-contact);
    transition: transform 0.15s ease;
}
.img-lightbox-close:hover { transform: scale(1.08); color: var(--primary); }
.img-lightbox-close svg { width: 16px; height: 16px; }
@media (max-width: 575.98px) {
    .img-lightbox-close { top: 8px; right: 8px; }
}

/* ═══════════════════════════════════════════════════════════════
   S07+ UI PASS — Premium Upload Widget (circular % progress ring)
   Generic, permission-agnostic component: markup lives per-view
   (profile photo, company logo, employee photo), behavior is bound
   once by app.js's initPremiumUpload() to every [data-premium-upload]
   block on the page. `data-shape="circle"` (avatars) or
   `data-shape="square"` (logos) only changes the preview's
   border-radius — the ring math is identical either way.
   ═══════════════════════════════════════════════════════════════ */
.premium-upload { display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center; }
.pu-preview-wrap { position: relative; width: 112px; height: 112px; flex-shrink: 0; }
.premium-upload[data-shape="square"] .pu-preview-wrap { width: 96px; height: 96px; }
.pu-ring { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); pointer-events: none; }
.pu-ring-track, .pu-ring-progress { fill: none; stroke-width: 6; }
.pu-ring-track { stroke: var(--border); }
.pu-ring-progress {
    stroke: var(--primary);
    stroke-linecap: round;
    stroke-dasharray: 289.03;
    stroke-dashoffset: 289.03;
    transition: stroke-dashoffset 0.12s linear;
}
.pu-preview {
    position: absolute; inset: 8px;
    border-radius: 50%;
    overflow: hidden;
    background: var(--surface-alt);
    border: 2px solid var(--surface);
    display: flex; align-items: center; justify-content: center;
    font-size: 2rem;
}
.premium-upload[data-shape="square"] .pu-preview { border-radius: var(--radius-sm); }
.pu-preview img { width: 100%; height: 100%; object-fit: cover; }
.premium-upload[data-shape="square"] .pu-preview img { object-fit: contain; }
.pu-percent {
    position: absolute; left: 50%; bottom: -6px; transform: translateX(-50%);
    background: var(--surface); color: var(--primary);
    font-size: 0.7rem; font-weight: 700;
    padding: 2px 10px; border-radius: 999px;
    border: 1px solid var(--border);
    box-shadow: var(--shadow-contact);
    opacity: 0; transition: opacity 0.2s ease;
    white-space: nowrap;
}
.pu-percent.is-visible { opacity: 1; }
.pu-file-chip {
    display: flex; align-items: center; gap: 8px;
    background: var(--surface-alt); border: 1px solid var(--border);
    border-radius: var(--radius-sm); padding: 6px 10px;
    max-width: 240px; text-align: left;
}
.pu-file-chip[hidden] { display: none; }
.pu-file-icon { flex-shrink: 0; color: var(--primary); display: flex; }
.pu-file-icon svg { width: 18px; height: 18px; }
.pu-file-meta { min-width: 0; }
.pu-file-name { display: block; font-size: 0.8rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pu-file-size { display: block; font-size: 0.72rem; color: var(--text-muted); }
.pu-actions { display: flex; gap: 8px; }
.pu-hint { font-size: 0.75rem; color: var(--text-muted); margin: 0; }

/* ══════════════════════════════════════════════════════════════
   S08 — Attendance Engine (M02)
   Master Reference §5 M02, §6.5-6.7 responsive shell, §20 SweetAlert2,
   Master List v2 §2 (unified source) and §4 (Premium UI)
   ══════════════════════════════════════════════════════════════ */

/* ── Small icon glyph inline with a <h3> card title ─────────────── */
.card-title-icon { display: inline-flex; width: 18px; height: 18px; vertical-align: -3px; margin-right: 6px; color: var(--primary); }
.card-title-icon svg { width: 100%; height: 100%; }

/* ── Dashboard "Attendance Today" mini-stat grid ─────────────────
   Same soft-glow-ring language as .badge-* (Premium UI §4), applied
   to a 2x2/4x1 grid of tappable stat tiles instead of table rows. */
.attendance-today-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-2);
    margin-top: var(--space-2);
}
@media (min-width: 576px) {
    .attendance-today-grid { grid-template-columns: repeat(4, 1fr); }
}
.attendance-today-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: var(--space-2) var(--space-1);
    border-radius: var(--radius-sm);
    background: var(--surface-alt);
    text-decoration: none;
    color: inherit;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.attendance-today-item:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-contact);
}
.attendance-today-dot { width: 10px; height: 10px; border-radius: 50%; box-shadow: none; padding: 0; }
.attendance-today-value { font-size: 1.3rem; font-weight: 700; }
.attendance-today-label { font-size: 0.72rem; color: var(--text-muted); text-align: center; }

/* ── Source badge (Hikvision/QR/Mobile GPS/Manual icon + label) ──
   Used next to every status dot in the Attendance list/calendar —
   this season's brief for the unified `source` column (Master List
   v2 §2). Deliberately the same visual language as .badge-secondary
   (soft ring) so it reads as metadata, not as another status. */
.source-badge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--text-muted);
    background: var(--surface-alt);
    white-space: nowrap;
}
.source-badge svg { width: 13px; height: 13px; }

/* ── Monthly attendance calendar grid ─────────────────────────── */
.calendar-nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
}
.calendar-month-label { font-size: 1.05rem; margin: 0; font-family: var(--font-en-heading, inherit); }

.attendance-calendar-grid {
    display: grid;
    /* minmax(0, 1fr) — NOT the bare "1fr" this used to be — is the fix.
       A plain 1fr track still has an automatic MINIMUM size equal to
       its content's min-content width, so a single cell with nowrap
       time text ("08:00–17:00") could refuse to shrink and force the
       whole 7-column grid wider than the phone's viewport. Because
       html/body use `overflow-x: hidden` (Rule set above), that extra
       width didn't scroll into view — it just got silently clipped,
       which is what made part of every row "disappear" on phones. */
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 6px;
    width: 100%;
    max-width: 100%;
    overflow: hidden;
}
.attendance-calendar-weekday {
    text-align: center;
    font-size: 0.7rem;
    font-weight: 700;
    color: var(--text-muted);
    padding-bottom: 4px;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    overflow: hidden;
    text-overflow: ellipsis;
}
.attendance-calendar-cell {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    gap: 2px;
    min-height: 74px;
    min-width: 0;
    width: 100%;
    padding: 6px 4px;
    border-radius: var(--radius-sm);
    background: var(--surface-alt);
    border: 1px solid transparent;
    text-decoration: none;
    color: inherit;
    transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
    /* overflow: hidden (rather than the default `visible`) is what lets
       the grid track above shrink to its fair 1/7 share — per the CSS
       Grid spec, a grid item's "automatic minimum size" only collapses
       to 0 when its own overflow is non-visible. Without this, this
       cell's content (see .attendance-calendar-times) was the actual
       cause of the whole-grid overflow, not the grid itself. */
    overflow: hidden;
    box-sizing: border-box;
}
a.attendance-calendar-cell:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-contact);
    border-color: var(--primary);
}
.attendance-calendar-cell-blank { background: transparent; }
.attendance-calendar-cell-today { border-color: var(--primary); box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.15); }
.attendance-calendar-cell-future { opacity: 0.45; }
.attendance-calendar-daynum { font-size: 0.8rem; font-weight: 700; }
.attendance-calendar-status { font-size: 0.62rem; padding: 2px 7px; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
.attendance-calendar-times {
    font-size: 0.62rem;
    color: var(--text-muted);
    white-space: nowrap;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
}
.attendance-calendar-late { font-size: 0.6rem; color: var(--warning); font-weight: 600; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
.attendance-calendar-source { display: inline-flex; width: 12px; height: 12px; color: var(--text-muted); margin-top: 1px; }
.attendance-calendar-source svg { width: 100%; height: 100%; }
.attendance-calendar-empty { font-size: 0.8rem; }

/* Soft per-status background tint — same color language as .badge-*,
   applied at low opacity to the whole cell rather than just the
   label, so the day is readable at a glance even before you notice
   the badge (and stays meaningful once the badge is hidden below
   480px). */
.attendance-calendar-cell[data-status="PRESENT"],
.attendance-calendar-cell[data-status="OVERTIME"] { background: rgba(22, 163, 74, 0.08); }
.attendance-calendar-cell[data-status="LATE"],
.attendance-calendar-cell[data-status="HALF_DAY"] { background: rgba(217, 119, 6, 0.08); }
.attendance-calendar-cell[data-status="ABSENT"] { background: rgba(220, 38, 38, 0.08); }
.attendance-calendar-cell[data-status="ON_LEAVE"],
.attendance-calendar-cell[data-status="PUBLIC_HOLIDAY"] { background: rgba(37, 99, 235, 0.08); }
.attendance-calendar-cell[data-status="WEEKEND"] { background: var(--surface-alt); opacity: 0.7; }

.attendance-calendar-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-3);
    padding-top: var(--space-2);
    border-top: 1px solid var(--border);
}

/* ── Responsive: shrink calendar cells on small phones ───────────── */
@media (max-width: 480px) {
    .attendance-calendar-grid { gap: 4px; }
    .attendance-calendar-cell { min-height: 58px; padding: 4px 2px; }
    .attendance-calendar-status { display: none; } /* the cell background/day number carries the meaning; label wraps too tight below 480px */
    .attendance-calendar-times { font-size: 0.58rem; }
    .attendance-calendar-source { width: 10px; height: 10px; }
}

/* ── Very small phones: the source icon + "N′ late" line are the
   first two things to give up their own row's space once 7 columns
   really can't fit them legibly — dropped rather than left to clip
   mid-character, since a half-visible "5' l…" reads worse than no
   label at all. Day number + status colour + check-in/out time
   remain, which is the information HR/employees actually scan for. */
@media (max-width: 360px) {
    .attendance-calendar-grid { gap: 3px; }
    .attendance-calendar-cell { min-height: 52px; padding: 3px 1px; gap: 1px; }
    .attendance-calendar-daynum { font-size: 0.72rem; }
    .attendance-calendar-times { font-size: 0.5rem; }
    .attendance-calendar-late,
    .attendance-calendar-source { display: none; }
}

/* ══════════════════════════════════════════════════════════════════
   Mobile / QR Check-In (S09 — Master Reference Section 5 M03, §8)
   Appended, not inserted — existing rules above are untouched
   (Rule: CSS appended, not overwritten).
   ══════════════════════════════════════════════════════════════════ */

.checkin-status-card { max-width: 480px; margin: 0 auto var(--space-3); }

.checkin-status-row {
    display: flex;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
}
.checkin-status-item {
    flex: 1;
    text-align: center;
    padding: var(--space-2);
    background: var(--surface-alt);
    border-radius: var(--radius-sm);
}
.checkin-status-label {
    display: block;
    font-size: 0.75rem;
    color: var(--text-muted);
    margin-bottom: 4px;
}
.checkin-status-value {
    display: block;
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--text);
}

.checkin-locate-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-1);
    font-size: 1.05rem;
    padding: var(--space-2) var(--space-3);
}
.checkin-locate-btn svg { width: 22px; height: 22px; }
.checkin-locate-status {
    text-align: center;
    margin-top: var(--space-1);
    min-height: 1.2em;
    font-size: 0.85rem;
}

.checkin-map {
    width: 100%;
    height: 220px;
    border-radius: var(--radius-sm);
    overflow: hidden;
    margin-bottom: var(--space-2);
    border: 1px solid var(--border);
}

.checkin-detail-row {
    display: flex;
    justify-content: space-between;
    padding: 6px 0;
    border-bottom: 1px solid var(--border);
    font-size: 0.9rem;
}
.checkin-detail-row:last-of-type { border-bottom: none; }

.checkin-geofence-notice {
    font-size: 0.85rem;
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-sm);
    margin: var(--space-1) 0 var(--space-2);
}
.checkin-geofence-ok   { background: rgba(22, 163, 74, 0.12); color: var(--success); }
.checkin-geofence-warn { background: rgba(217, 119, 6, 0.12); color: var(--warning); }

.checkin-recent-list { display: flex; flex-direction: column; gap: var(--space-1); }
.checkin-recent-item {
    display: grid;
    grid-template-columns: 1fr auto auto auto;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-2);
    background: var(--surface-alt);
    border-radius: var(--radius-sm);
    font-size: 0.85rem;
}
/* Method choice merged into the same card as the Check In button
   (per Bora), separated visually with a light divider instead of a
   card boundary. */
.checkin-method-inline { margin-top: var(--space-2); }
.checkin-method-divider {
    border: none;
    border-top: 1px solid var(--border);
    margin: var(--space-3) 0 var(--space-2);
}

/* Far/near mutually-exclusive method notice — appended this session,
   per Rule: CSS appended, not overwritten. Self-contained (padding +
   radius + margin) rather than relying on the bare .alert-warning
   color rule, so it renders as a proper box even without the .alert
   base class present. */
.checkin-method-notice {
    padding: 10px 14px;
    border-radius: var(--radius-sm);
    font-size: 0.85rem;
    margin: 0 0 var(--space-2);
    border: 1px solid transparent;
}

/* Two-method choice card (Scan QR / Take Photo + Reason) — patch
   2026-08-04, appended per Rule: CSS appended, not overwritten. */
.checkin-method-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-2);
    margin-top: var(--space-2);
}
.checkin-method-btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: var(--space-3) var(--space-2);
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--text);
    text-align: center;
    transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
.checkin-method-btn:hover,
.checkin-method-btn:focus-visible {
    transform: translateY(-2px);
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08);
    border-color: var(--primary);
}
.checkin-method-btn svg { width: 26px; height: 26px; color: var(--primary); }
.checkin-method-btn span { font-weight: 600; font-size: 0.95rem; }
.checkin-method-btn small { color: var(--text-muted); font-size: 0.75rem; line-height: 1.3; }
.checkin-employee-note {
    display: inline-block;
    color: var(--text-muted);
    font-style: italic;
    max-width: 220px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.checkin-coords {
    display: inline-block;
    white-space: nowrap;
    margin-top: 2px;
    font-variant-numeric: tabular-nums;
}

/* Live QR scanner popup — v4, polished (per Bora: make it nicer).
   `.checkin-qr-scanner-wrap` is not rendered at all by default; JS
   toggles `.is-active` which switches it to a fixed, full-viewport
   layer above everything else (z-index above the sidebar/bottom-nav's
   20-30 and the Ctrl+K nav search modal's 200). `body.qr-scanner-open`
   locks background scroll while the popup is open. */
.checkin-qr-scanner-wrap { display: none; }
.checkin-qr-scanner-wrap.is-active {
    display: flex;
    position: fixed;
    inset: 0;
    z-index: 250;
    background: #000;
    /* iOS Safari's address bar/toolbar can grow or shrink the visible
       area without firing a resize in time for plain `inset: 0`, which
       historically leaves a sliver of the old page visible at the
       bottom. `100dvh` (dynamic viewport height) tracks that live, and
       is simply ignored by browsers that don't support it — so the
       `inset: 0` box above still applies everywhere else. */
    height: 100vh;
    height: 100dvh;
    width: 100vw;
}
.checkin-qr-video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    background: #000;
}

.checkin-qr-header {
    position: absolute;
    top: 0; left: 0; right: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: calc(var(--space-2) + env(safe-area-inset-top)) var(--space-2) var(--space-4);
    background: linear-gradient(to bottom, rgba(0, 0, 0, 0.65), transparent);
}
.checkin-qr-header-title {
    display: flex;
    align-items: center;
    gap: 8px;
    color: #fff;
    font-weight: 600;
    font-size: 0.95rem;
}
.checkin-qr-header-title svg { width: 20px; height: 20px; }
.checkin-qr-close-btn {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border: none;
    background: rgba(255, 255, 255, 0.15);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 0.15s ease;
}
.checkin-qr-close-btn:hover,
.checkin-qr-close-btn:focus-visible { background: rgba(255, 255, 255, 0.28); }
.checkin-qr-close-btn svg { width: 18px; height: 18px; }

.checkin-qr-viewfinder {
    position: absolute;
    top: 44%; left: 50%;
    width: min(68vw, 280px);
    aspect-ratio: 1 / 1;
    transform: translate(-50%, -50%);
    border-radius: 20px;
    box-shadow: 0 0 0 999px rgba(0, 0, 0, 0.5);
    overflow: hidden;
}
.qr-corner {
    position: absolute;
    width: 30px;
    height: 30px;
    border: 3px solid var(--primary, #2563EB);
}
.qr-corner-tl { top: -1px; left: -1px; border-right: none; border-bottom: none; border-radius: 14px 0 0 0; }
.qr-corner-tr { top: -1px; right: -1px; border-left: none; border-bottom: none; border-radius: 0 14px 0 0; }
.qr-corner-bl { bottom: -1px; left: -1px; border-right: none; border-top: none; border-radius: 0 0 0 14px; }
.qr-corner-br { bottom: -1px; right: -1px; border-left: none; border-top: none; border-radius: 0 0 14px 0; }
.qr-scan-line {
    position: absolute;
    left: 6%; right: 6%;
    top: 0;
    height: 2px;
    border-radius: 2px;
    background: linear-gradient(to right, transparent, var(--primary, #2563EB), transparent);
    box-shadow: 0 0 8px 1px var(--primary, #2563EB);
    animation: qrScanSweep 2.2s ease-in-out infinite;
}
@keyframes qrScanSweep {
    0%   { top: 6%; opacity: 0; }
    12%  { opacity: 1; }
    50%  { top: 94%; opacity: 1; }
    62%  { opacity: 0; }
    100% { top: 6%; opacity: 0; }
}

.checkin-qr-status-overlay {
    position: absolute;
    left: var(--space-2);
    right: var(--space-2);
    bottom: calc(var(--space-4) + env(safe-area-inset-bottom));
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    text-align: center;
    padding-top: 48px;
    background: linear-gradient(to top, rgba(0, 0, 0, 0.65), transparent);
}
.checkin-qr-status-overlay p {
    margin: 0;
    color: #fff;
    font-size: 0.9rem;
    font-weight: 500;
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
.checkin-qr-overlay-actions {
    display: flex;
    gap: var(--space-1);
    flex-wrap: wrap;
    justify-content: center;
}
/* The scanner popup is always a black backdrop regardless of
   day/night theme, so buttons inside it need their own light styling
   rather than inheriting --text/--border (which flip with the theme
   and would be near-invisible against black in light mode). */
.checkin-qr-status-overlay .btn {
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.4);
    backdrop-filter: blur(4px);
}
.checkin-qr-status-overlay .btn:hover,
.checkin-qr-status-overlay .btn:focus-visible {
    background: rgba(255, 255, 255, 0.22);
}
body.qr-scanner-open { overflow: hidden; }

/* Location "Show/Hide Map" toggle and its distinct enable-location alert */
.checkin-map-toggle { margin-top: var(--space-2); }
#checkin-map-wrap { margin-top: var(--space-2); }
.checkin-location-notice { margin-top: var(--space-2); }
.checkin-location-notice p { margin: 6px 0 var(--space-2); }

@media (max-width: 480px) {
    .checkin-method-grid { grid-template-columns: 1fr 1fr; gap: var(--space-1); }
    .checkin-method-btn { padding: var(--space-2) var(--space-1); }
}

@media (max-width: 480px) {
    .checkin-recent-item {
        grid-template-columns: 1fr 1fr;
        row-gap: 4px;
    }
}

/* ── Settings → QR Check-In printable card ─────────────────────── */
.qr-print-sheet {
    text-align: center;
    margin: 0 auto var(--space-3);
    padding: var(--space-4);
}
.qr-print-header { margin-bottom: var(--space-3); }
.qr-print-logo { max-height: 56px; margin-bottom: var(--space-1); }
.qr-print-company-name { font-weight: 700; font-size: 1.05rem; line-height: 1.4; }
.qr-print-company-name span { display: block; font-weight: 400; font-size: 0.85rem; color: var(--text-muted); }

.qr-canvas { display: flex; justify-content: center; margin: var(--space-2) 0; }
.qr-canvas svg { max-width: 100%; height: auto; }
.qr-render-error { color: var(--danger); font-size: 0.85rem; }

.qr-print-instructions { font-size: 0.85rem; color: var(--text-muted); margin: var(--space-2) 0; }
.qr-print-link {
    font-size: 0.75rem;
    color: var(--text-muted);
    word-break: break-all;
    margin-top: var(--space-2);
    padding-top: var(--space-2);
    border-top: 1px solid var(--border);
}
.qr-print-link code {
    display: block;
    margin-top: 4px;
    background: var(--surface-alt);
    padding: 4px 8px;
    border-radius: var(--radius-sm);
}

/* ── Settings → Attendance Sources: geofence center sub-panel ────── */
.geofence-center-block {
    display: block;
    margin-top: var(--space-2);
    padding-top: var(--space-2);
    border-top: 1px dashed var(--border);
}
.geofence-center-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-1);
    margin-bottom: 4px;
}
.geofence-center-header > span:first-child { font-weight: 600; font-size: 0.85rem; }
.geofence-center-help {
    font-size: 0.78rem;
    margin: 0 0 var(--space-1);
}
.geofence-center-block .form-row { margin-bottom: var(--space-1); }
.geofence-center-block .form-group { margin-bottom: 0; }

@media print {
    .qr-print-sheet { box-shadow: none !important; border: 1px solid #ccc !important; }
}

/* ══════════════════════════════════════════════════════════════
   Patch: Cambodia MLVT Labor Compliance (Employee Information)
   All colors/spacing via existing CSS variables — day/night safe.
   ══════════════════════════════════════════════════════════════ */
.field-hint { font-size: 0.78rem; margin-top: 2px; margin-bottom: var(--space-1); }

.compliance-doc-block {
    padding: var(--space-2);
    margin-bottom: var(--space-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface-alt);
}
.compliance-doc-title {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 4px;
    font-size: 0.95rem;
}
.compliance-doc-title svg { width: 18px; height: 18px; color: var(--primary); flex-shrink: 0; }

.compliance-upload-zone {
    margin-top: var(--space-1);
    padding: var(--space-2);
    border: 1.5px dashed var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    transition: border-color 0.15s, background 0.15s;
}
.compliance-upload-zone:focus-within,
.compliance-upload-zone:hover { border-color: var(--primary); }
.compliance-upload-zone input[type="file"] { width: 100%; font-size: 0.85rem; }

.compliance-file-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    margin-bottom: 6px;
    border-radius: 999px;
    background: rgba(37, 99, 235, 0.1);
    color: var(--primary);
    font-size: 0.82rem;
    font-weight: 600;
    text-decoration: none;
    transition: transform 0.12s;
}
.compliance-file-chip:hover { transform: translateY(-1px); }
.compliance-file-chip svg { width: 14px; height: 14px; }

.compliance-view-link {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-left: 8px;
    font-size: 0.78rem;
    color: var(--primary);
    text-decoration: none;
}
.compliance-view-link svg { width: 12px; height: 12px; }
.compliance-view-link:hover { text-decoration: underline; }

/* ══════════════════════════════════════════════════════════════
   Patch: Check-In Late Reason / Check-Out Early Reason
   ══════════════════════════════════════════════════════════════ */

/* Settings → Check-In Reasons management page */
.reason-columns { grid-template-columns: 1fr; gap: var(--space-2); }
@media (min-width: 768px) {
    .reason-columns { display: grid; grid-template-columns: 1fr 1fr; }
}
.card-title { margin: 0 0 var(--space-2); font-size: 1.05rem; }
.reason-list { margin-bottom: var(--space-2); }
.reason-row {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    padding: 10px 0;
    border-bottom: 1px solid var(--border);
    flex-wrap: wrap;
}
.reason-row:last-child { border-bottom: none; }
.reason-row-body { flex: 1; min-width: 160px; }
.reason-row-title { font-weight: 600; font-size: 0.9rem; }
.reason-row-meta { font-size: 0.78rem; color: var(--text-muted); margin-top: 2px; }
.reason-row-actions { display: flex; gap: 6px; flex-shrink: 0; }
.reason-edit-form {
    padding: var(--space-2);
    margin: 6px 0;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface-alt);
}
.reason-edit-actions { display: flex; gap: 8px; margin-top: var(--space-1); }
.reason-add-form {
    padding-top: var(--space-2);
    border-top: 1px dashed var(--border);
}
.empty-hint { padding: var(--space-1) 0; }
.inline-form { display: inline-block; }

/* Mobile QR confirm screen — late/early reason picker */
.reason-picker-card {
    margin: var(--space-2) 0;
    padding: var(--space-2);
    border: 1.5px solid var(--warning);
    border-radius: var(--radius-sm);
    background: rgba(217, 119, 6, 0.08);
}
.reason-picker-title {
    display: flex;
    align-items: center;
    gap: 8px;
    font-weight: 700;
    font-size: 0.95rem;
    color: var(--warning);
    margin-bottom: 4px;
}
.reason-picker-title svg { width: 18px; height: 18px; flex-shrink: 0; }
.reason-picker-minutes { font-size: 0.82rem; margin-bottom: var(--space-1); }


/* ── S09 UX/UI v4 — ONE-ROW ICON ACTION SYSTEM ───────────────────
   Rule (applies to every action group, in a table or not):
   1. Every action control carries an icon (icon_svg()).
   2. A visible text label is optional.
   3. If there is NO visible label, the control MUST be icon-only
      AND carry class="tip" data-tip="…" (+ aria-label) — the CSS
      tooltip already defined above (.tip / data-tip). A bare
      icon-only button with no .tip/data-tip/title is a bug.
   4. The whole group (.actions, .header-actions, and friends) is
      ONE ROW, never wraps — icon-only buttons on narrow screens are
      what keeps a 3–4 action group fitting in that row; if a group
      still can't fit, it scrolls horizontally in place (same pattern
      as .tabs-nav) rather than wrapping to a second line.
   Because every screen already reuses these same shared classes,
   this section upgrades every current AND future view automatically
   — no view-specific CSS required. ─────────────────────────────── */
.btn,
.header-actions .btn,
.actions .btn,
.device-card-actions .btn,
.reason-row-actions .btn,
.reason-edit-actions .btn,
.filter-panel-actions .btn {
    min-height: 36px;
}

.btn svg,
.header-actions .btn svg,
.actions .btn svg,
.device-card-actions .btn svg,
.reason-row-actions .btn svg,
.reason-edit-actions .btn svg,
.filter-panel-actions .btn svg {
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
    stroke: currentColor;
}

.btn-xs svg {
    width: 14px;
    height: 14px;
}

.btn-sm svg {
    width: 15px;
    height: 15px;
}

.header-actions,
.filter-panel-actions,
.device-card-actions,
.reason-row-actions,
.reason-edit-actions {
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    gap: 8px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}
.header-actions::-webkit-scrollbar,
.filter-panel-actions::-webkit-scrollbar,
.device-card-actions::-webkit-scrollbar,
.reason-row-actions::-webkit-scrollbar,
.reason-edit-actions::-webkit-scrollbar,
.actions::-webkit-scrollbar {
    display: none;
}

.actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: nowrap;
    gap: 6px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}

.actions .btn {
    white-space: nowrap;
    flex-shrink: 0;
}

.table td.actions {
    min-width: 150px;
    vertical-align: middle;
}

/* Icon-only action control — no visible label. Requires class="tip"
   data-tip="…" (see the .tip component) so meaning is never lost.
   Kept small + circular so 3–4 of these still read as one tidy row
   on a 375px screen instead of forcing a wrap. */
.actions .btn-icon-only,
.header-actions .btn-icon-only {
    border-radius: 999px;
}

.btn-xs {
    border-radius: 8px;
    font-weight: 600;
}

.btn-xs:hover,
.btn-sm:hover {
    transform: translateY(-1px);
}

.btn-danger {
    box-shadow: 0 1px 2px rgba(220, 38, 38, 0.10);
}

.btn:focus-visible,
.action-icon-btn:focus-visible {
    outline: 3px solid color-mix(in srgb, var(--primary) 22%, transparent);
    outline-offset: 2px;
}

.action-icon-btn {
    width: 32px;
    height: 32px;
    border-radius: 9px;
}

.action-icon-btn svg {
    width: 15px;
    height: 15px;
}

@media (max-width: 767px) {
    .header-actions,
    .filter-panel-actions {
        width: 100%;
    }

    /* BUGFIX (Employee Profile on narrow phones): flex-shrink:1 here
       (the old "flex: 1 1 auto") let these buttons shrink past their
       text's natural width whenever a page has a big action group —
       Employee Profile alone can have 8 (Edit, Staff Card,
       Certifications, Awards, Terminate/Reactivate, Invite to
       System, Generate Activation Code, Create Login Now). Once a
       button is squeezed narrower than any single word, the
       word-break: break-word every card/.main-content inherits
       (app.css, "Long unbroken tokens…" rule near the top of this
       file) has nowhere left to break EXCEPT mid-word — down to one
       character per line, which is the vertical letter-stack bug.
       flex-shrink:0 + white-space:nowrap stops any shrinking below
       natural size; .header-actions already has overflow-x:auto (S09
       UX/UI v4 rule above: "ONE ROW, never wraps … scrolls
       horizontally in place if it still can't fit"), so an
       oversized group now scrolls sideways instead of collapsing.
       flex-grow stays 1 so a SHORT group (1–2 buttons) still fills
       the row nicely, same as before. */
    .header-actions .btn {
        flex: 1 0 auto;
        white-space: nowrap;
    }

    .actions {
        justify-content: flex-start;
    }

    .table td.actions {
        min-width: 0;
    }
}


/* ── S09 Enterprise UI polish ───────────────────────────────────── */
.sidebar.sidebar-hidden { width: 0 !important; min-width: 0 !important; overflow: hidden; border-right: 0; padding: 0; }
.layout-shell { min-height: 100vh; }
.top-bar { min-height: 60px; backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); background: color-mix(in srgb, var(--surface) 92%, transparent); }
.hamburger { flex: 0 0 auto; width: 40px; height: 40px; align-items: center; justify-content: center; border-radius: 10px; }
.hamburger:hover { background: var(--surface-alt); }
.table-footer { display:flex; align-items:center; justify-content:space-between; gap:16px; flex-wrap:wrap; margin-top:16px; }
.per-page-control { display:flex; align-items:center; gap:8px; font-size:.85rem; color:var(--text-muted); }
.per-page-control select, .inline-form select { min-width:78px; height:36px; border:1px solid var(--border); border-radius:10px; background:var(--surface); color:var(--text); padding:0 10px; }
.inline-form { display:inline-flex; margin:0; }
.actions .btn:not(.btn-icon-only) { min-width:36px; justify-content:center; }
.actions .btn:not(.btn-icon-only) { font-size:0; gap:0; }
.actions .btn:not(.btn-icon-only) svg { width:17px; height:17px; }
.actions .btn:not(.btn-icon-only)[title]::after { content:attr(title); }
@media (max-width: 767.98px) { .table-footer { align-items:stretch; } .per-page-control { width:100%; justify-content:space-between; } .pagination { width:100%; justify-content:flex-end; overflow-x:auto; } .top-bar { min-height:56px; padding:8px 12px; } }
.pagination button { display:inline-flex; align-items:center; justify-content:center; min-width:34px; height:34px; padding:0 8px; border:1px solid var(--border); border-radius:9px; background:var(--surface); color:var(--text); cursor:pointer; }
.pagination button:hover:not(:disabled) { background:var(--surface-alt); }
.pagination button:disabled { opacity:.4; cursor:not-allowed; }
.pagination .client-page-buttons { display:inline-flex; gap:6px; }
.pagination .client-page-buttons button.pagination-current { background:var(--primary); color:#fff; border-color:var(--primary); font-weight:700; }


/* ═══════════════════════════════════════════════════════════════
   S09 FINAL — Fixed Enterprise Shell / Sidebar + Reference Tooltip
   The sidebar and topbar are fixed layers. Content is offset so
   neither layer ever covers the page while scrolling.
   Desktop: full sidebar (280px) or compact icon rail (76px).
   Mobile: off-canvas drawer.
   ═══════════════════════════════════════════════════════════════ */

:root {
    --hrms-sidebar-width: 280px;
    --hrms-sidebar-collapsed: 76px;
    --hrms-topbar-height: 64px;
}

/* FIX — "sidebar/top-bar has no stability, shakes on every click"
   report. See the matching comment + <script> in
   app/Views/layouts/main.php right after <body> for the full
   explanation. body.hrms-preload is present ONLY for the brief window
   between first paint and Alpine.js confirming it has finished
   hydrating the page — during that window the three transitions below
   are disabled so Alpine's first, unavoidable class-correction (if any)
   is instantaneous instead of visibly sliding. The class is then
   removed and these transitions apply normally again for real,
   user-triggered sidebar toggles. */
body.hrms-preload .sidebar,
body.hrms-preload .top-bar,
body.hrms-preload .main-wrap,
body.hrms-preload .sidebar-overlay {
    transition: none !important;
}

/* FIX — "the table body area flickers/shakes with data" report.
   Most tables in this system (everything WITHOUT
   data-server-paginated) are rendered by PHP with EVERY row, and
   app.js's initClientTables() then hides all-but-one-page's-worth via
   row.style.display='none' once DOMContentLoaded fires. That is a
   real, visible two-step paint on any table with more rows than the
   page size: first the full, unpaginated list, then a sudden collapse
   down to one page. Hiding the table for the same brief
   'hrms-preload' window already used for the sidebar fix above solves
   it for free — initClientTables() runs on the same DOMContentLoaded
   dispatch, earlier than Alpine's own init (app.js loads before
   alpine.min.js in the document), so by the time 'hrms-preload' is
   removed the table has already been sliced to the correct page —
   only the final, correct page is ever painted. visibility (not
   display:none) is used deliberately so the row's own
   getBoundingClientRect()/offsetWidth stay measurable the whole time,
   in case any other script needs them during this same window.
   data-server-paginated tables are excluded — the server already sent
   the right number of rows, there is nothing to hide. */
body.hrms-preload table.data-table:not([data-server-paginated]) {
    visibility: hidden;
}

/* Fixed desktop sidebar */
.layout-shell { min-height: 100vh; }
.sidebar {
    position: fixed !important;
    top: 0;
    left: 0;
    bottom: 0;
    width: var(--hrms-sidebar-width) !important;
    min-width: var(--hrms-sidebar-width);
    height: 100dvh;
    max-height: 100dvh;
    z-index: 100;
    overflow: hidden;
    border-right: 1px solid var(--border);
    box-shadow: 4px 0 24px rgba(15, 23, 42, .05);
    transition: width .22s ease, min-width .22s ease, transform .22s ease,
                box-shadow .22s ease;
}
.sidebar-nav {
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-width: thin;
    scrollbar-color: color-mix(in srgb, var(--text-muted) 28%, transparent) transparent;
}
.sidebar-nav::-webkit-scrollbar { width: 5px; }
.sidebar-nav::-webkit-scrollbar-thumb {
    background: color-mix(in srgb, var(--text-muted) 28%, transparent);
    border-radius: 999px;
}
.sidebar-footer { flex: 0 0 auto; }

/* Main content is offset from the fixed sidebar */
.main-wrap {
    margin-left: var(--hrms-sidebar-width);
    min-height: 100vh;
    transition: margin-left .22s ease;
}
.main-wrap.sidebar-is-collapsed {
    margin-left: var(--hrms-sidebar-collapsed);
}
.main-wrap.sidebar-is-open { margin-left: var(--hrms-sidebar-width); }

/* Fixed topbar — never scrolls away */
.top-bar {
    position: fixed !important;
    top: 0;
    right: 0;
    left: 0;
    height: var(--hrms-topbar-height);
    min-height: var(--hrms-topbar-height);
    z-index: 90;
    padding: 8px 20px;
    box-sizing: border-box;
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    background: color-mix(in srgb, var(--surface) 92%, transparent);
    box-shadow: 0 1px 0 var(--border), 0 8px 28px rgba(15,23,42,.06);
    transition: left .22s ease;
}
.main-wrap.sidebar-is-open .top-bar { left: var(--hrms-sidebar-width); }
.main-wrap.sidebar-is-collapsed .top-bar { left: var(--hrms-sidebar-collapsed); }

/* Main content starts below the fixed header */
.main-content {
    padding-top: calc(var(--hrms-topbar-height) + 24px) !important;
    min-height: 100vh;
    scroll-margin-top: calc(var(--hrms-topbar-height) + 16px);
}

/* Desktop icon rail state */
.sidebar.sidebar-hidden {
    display: flex !important;
    width: var(--hrms-sidebar-collapsed) !important;
    min-width: var(--hrms-sidebar-collapsed) !important;
    border-right: 1px solid var(--border);
    padding: 0;
}
.sidebar.sidebar-hidden .sidebar-brand {
    justify-content: center;
    padding: 14px 8px;
}
.sidebar.sidebar-hidden .sidebar-brand-name,
.sidebar.sidebar-hidden .sidebar-link-label {
    display: none !important;
}
.sidebar.sidebar-hidden .sidebar-nav {
    padding: 12px 10px;
    gap: 6px;
}
.sidebar.sidebar-hidden .sidebar-link {
    justify-content: center;
    width: 56px;
    min-height: 48px;
    padding: 10px;
    margin: 0 auto;
    gap: 0;
}
.sidebar.sidebar-hidden .sidebar-link-icon {
    width: 24px;
    height: 24px;
}
.sidebar.sidebar-hidden .sidebar-link-icon svg {
    width: 20px;
    height: 20px;
}
.sidebar.sidebar-hidden .sidebar-footer {
    padding: 10px;
}
.sidebar.sidebar-hidden .sidebar-footer .sidebar-link {
    width: 56px;
}
.sidebar.sidebar-hidden .badge-dot {
    top: -4px;
    right: -4px;
}

/* Tooltip — deliberately matches the compact dark tooltip used by
   the System Login/key icon: rounded, small, readable, with arrow. */
.sidebar-link[data-tip] { position: relative; }
.sidebar.sidebar-hidden .sidebar-link[data-tip]::after {
    content: attr(data-tip);
    position: absolute;
    left: calc(100% + 10px);
    top: 50%;
    transform: translateY(-50%) translateX(-4px);
    z-index: 250;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    white-space: nowrap;
    background: var(--text);
    color: var(--surface);
    padding: 6px 10px;
    border-radius: 7px;
    font-size: .75rem;
    line-height: 1.2;
    font-weight: 600;
    box-shadow: 0 8px 20px rgba(15,23,42,.18);
    transition: opacity .14s ease, transform .14s ease, visibility 0s linear .14s;
}
.sidebar.sidebar-hidden .sidebar-link[data-tip]::before {
    content: "";
    position: absolute;
    left: calc(100% + 5px);
    top: 50%;
    transform: translateY(-50%);
    border: 5px solid transparent;
    border-right-color: var(--text);
    z-index: 251;
    opacity: 0;
    visibility: hidden;
    transition: opacity .14s ease, visibility 0s linear .14s;
}
.sidebar.sidebar-hidden .sidebar-link[data-tip]:hover::after,
.sidebar.sidebar-hidden .sidebar-link[data-tip]:focus-visible::after {
    opacity: 1;
    visibility: visible;
    transform: translateY(-50%) translateX(0);
    transition: opacity .14s ease, transform .14s ease, visibility 0s;
}
.sidebar.sidebar-hidden .sidebar-link[data-tip]:hover::before,
.sidebar.sidebar-hidden .sidebar-link[data-tip]:focus-visible::before {
    opacity: 1;
    visibility: visible;
    transition: opacity .14s ease, visibility 0s;
}

/* Keep the existing .tip component visually consistent everywhere */
.tip::after {
    background: var(--text);
    color: var(--surface);
    border-radius: 7px;
    padding: 6px 10px;
    font-size: .75rem;
    box-shadow: 0 8px 20px rgba(15,23,42,.18);
}
.tip::before { border-top-color: var(--text); }

/* Icon-only controls never leak their title text into the button */
.actions .btn:not(.btn-icon-only)[title]::after { content: none !important; }

/* Better enterprise spacing */
.top-bar-actions { gap: 8px; }
.icon-btn, .hamburger { width: 42px; height: 42px; border-radius: 12px; }
.top-bar-title { padding-left: 2px; }
.main-content { padding-left: clamp(16px, 2vw, 32px); padding-right: clamp(16px, 2vw, 32px); }

/* Sidebar overlay only belongs to the mobile drawer */
.sidebar-overlay { z-index: 80; }

/* ── Responsive fixed shell ─────────────────────────────────── */
@media (min-width: 768px) {
    .sidebar-overlay { display: none !important; }
    .sidebar.mobile-open {
        position: fixed !important;
        transform: none !important;
    }
}

@media (max-width: 767.98px) {
    :root { --hrms-topbar-height: 56px; }

    .sidebar {
        width: min(290px, 86vw) !important;
        min-width: min(290px, 86vw);
        transform: translateX(-105%);
        z-index: 120;
        box-shadow: 10px 0 30px rgba(15,23,42,.14);
    }
    .sidebar.mobile-open {
        display: flex !important;
        position: fixed !important;
        transform: translateX(0) !important;
    }
    .sidebar.sidebar-hidden {
        display: flex !important;
        width: min(290px, 86vw) !important;
        min-width: min(290px, 86vw);
        transform: translateX(-105%) !important;
        border-right: 1px solid var(--border);
        padding: 0;
    }
    .sidebar.sidebar-hidden .sidebar-brand-name,
    .sidebar.sidebar-hidden .sidebar-link-label {
        display: inline !important;
    }
    .sidebar.sidebar-hidden .sidebar-link,
    .sidebar.sidebar-hidden .sidebar-footer .sidebar-link {
        justify-content: flex-start;
        width: 100%;
        min-height: 44px;
        padding: 10px 14px;
        gap: 12px;
    }
    .sidebar.sidebar-hidden .sidebar-brand {
        justify-content: flex-start;
        padding: 16px;
    }
    .sidebar.sidebar-hidden .sidebar-nav { padding: 16px; }
    .sidebar.sidebar-hidden .sidebar-footer { padding: 16px; }

    .main-wrap,
    .main-wrap.sidebar-is-open,
    .main-wrap.sidebar-is-collapsed {
        margin-left: 0 !important;
    }
    .top-bar,
    .main-wrap.sidebar-is-open .top-bar,
    .main-wrap.sidebar-is-collapsed .top-bar {
        left: 0 !important;
        padding-left: 12px;
        padding-right: 12px;
    }
    .main-content {
        padding-top: calc(var(--hrms-topbar-height) + 16px) !important;
        padding-left: 12px;
        padding-right: 12px;
    }
    .sidebar.sidebar-hidden .sidebar-link[data-tip]::after,
    .sidebar.sidebar-hidden .sidebar-link[data-tip]::before {
        display: none;
    }
    .top-bar-actions { gap: 4px; }
    .icon-btn, .hamburger { width: 40px; height: 40px; }
}

@media (max-width: 420px) {
    .top-bar { gap: 6px; }
    .top-bar-actions .icon-btn { width: 38px; height: 38px; }
    .top-bar-actions { gap: 2px; }
}

/* ═══════════════════════════════════════════════════════════════
   S09.1 — Reference Tooltip + Table Controls / Action Alignment
   Uses the same compact dark tooltip treatment as the System Login
   key tooltip: rounded pill, small shadow, readable Khmer/English.
   ═══════════════════════════════════════════════════════════════ */
.tip::after,
.sidebar.sidebar-hidden .sidebar-link[data-tip]::after {
    background: var(--text) !important;
    color: var(--surface) !important;
    border-radius: 7px !important;
    padding: 6px 10px !important;
    font-size: .75rem !important;
    line-height: 1.2 !important;
    font-weight: 600 !important;
    box-shadow: 0 8px 20px rgba(15,23,42,.18) !important;
}

/* Topbar controls use the exact same tooltip position as System Login:
   directly below the control. */
.top-bar .tip::after {
    top: calc(100% + 8px);
    bottom: auto;
    left: 50%;
    transform: translateX(-50%) translateY(-3px);
    z-index: 300;
}
.top-bar .tip::before {
    top: calc(100% + 3px);
    bottom: auto;
    left: 50%;
    transform: translateX(-50%);
    border: 5px solid transparent;
    border-bottom-color: var(--text);
    border-top-color: transparent;
    z-index: 301;
}
.top-bar .tip:hover::after,
.top-bar .tip:focus-visible::after {
    transform: translateX(-50%) translateY(0);
}

/* The compact sidebar rail keeps the same tooltip visual language while
   pointing right because there is no room underneath the icon. */
.sidebar.sidebar-hidden .sidebar-link[data-tip]::after {
    padding: 6px 10px !important;
}

/* Page-size selector is intentionally above the table, not buried in the
   footer. It sits before the first data row and does not cover the sidebar. */
.table-top-controls {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 10px;
    margin: 0 0 10px;
    min-height: 40px;
}
.table-top-controls .per-page-control {
    margin: 0;
    padding: 5px 8px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    box-shadow: 0 2px 8px rgba(15,23,42,.04);
}
.table-top-controls .per-page-control label {
    color: var(--text-muted);
    font-size: .82rem;
    font-weight: 600;
}
.table-top-controls .per-page-control select {
    min-width: 78px;
    height: 32px;
    border-radius: 8px;
    padding: 0 9px;
}

/* Perfectly aligned table action icons: same size, same baseline, same gap,
   centered in the action column. Text is hidden only for compact table actions. */
.table td.actions,
.data-table td.actions {
    text-align: center;
    vertical-align: middle;
    width: 112px;
    min-width: 112px;
    padding-left: 10px;
    padding-right: 10px;
}
.table td.actions .actions,
.data-table td.actions {
    vertical-align: middle;
}
.actions {
    justify-content: center !important;
    align-items: center !important;
    gap: 6px !important;
    flex-wrap: nowrap !important;
}
.actions .btn,
.actions .btn.btn-xs,
.actions .btn.btn-sm {
    width: 34px !important;
    min-width: 34px !important;
    max-width: 34px !important;
    height: 34px !important;
    min-height: 34px !important;
    padding: 0 !important;
    margin: 0 !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    flex: 0 0 34px !important;
    border-radius: 9px !important;
    line-height: 1 !important;
    font-size: 0 !important;
    gap: 0 !important;
}
.actions .btn svg {
    width: 16px !important;
    height: 16px !important;
    margin: 0 !important;
}
.action-icon-btn {
    width: 34px !important;
    min-width: 34px !important;
    height: 34px !important;
    min-height: 34px !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    padding: 0 !important;
    margin: 0 !important;
    flex: 0 0 34px !important;
    vertical-align: middle;
}
.action-icon-btn svg {
    width: 16px !important;
    height: 16px !important;
    margin: 0 !important;
}

@media (max-width: 767.98px) {
    .table-top-controls { margin-bottom: 8px; }
    .table-top-controls .per-page-control { width: auto; }
    .table td.actions, .data-table td.actions { width: 100%; min-width: 0; }
    .actions { justify-content: flex-start !important; }
}

/* =====================================================================
   S09 FINAL V3 — ONE GLOBAL TOOLTIP + MOBILE QR LOG ACTION FIX
   All tooltips intentionally use the exact same visual language.
   ===================================================================== */
:root {
    --hrms-tip-bg: #172033;
    --hrms-tip-fg: #ffffff;
    --hrms-tip-radius: 7px;
    --hrms-tip-shadow: 0 8px 22px rgba(15, 23, 42, .22);
}

/* One tooltip style for every .tip control, regardless of where it lives. */
.tip { position: relative !important; }
.tip::after {
    content: attr(data-tip) !important;
    position: absolute !important;
    z-index: 99999 !important;
    opacity: 0 !important;
    visibility: hidden !important;
    pointer-events: none !important;
    white-space: nowrap !important;
    background: var(--hrms-tip-bg) !important;
    color: var(--hrms-tip-fg) !important;
    border: 0 !important;
    border-radius: var(--hrms-tip-radius) !important;
    padding: 7px 10px !important;
    min-height: 28px !important;
    box-sizing: border-box !important;
    font-size: 12px !important;
    line-height: 1.2 !important;
    font-weight: 600 !important;
    font-family: inherit !important;
    letter-spacing: 0 !important;
    box-shadow: var(--hrms-tip-shadow) !important;
    transition: opacity .14s ease, transform .14s ease !important;
    transform: translate(-50%, -4px) !important;
    top: auto !important;
    bottom: calc(100% + 8px) !important;
    left: 50% !important;
    right: auto !important;
}
.tip::before {
    content: "" !important;
    position: absolute !important;
    z-index: 100000 !important;
    opacity: 0 !important;
    visibility: hidden !important;
    pointer-events: none !important;
    top: auto !important;
    bottom: calc(100% + 3px) !important;
    left: 50% !important;
    right: auto !important;
    transform: translateX(-50%) !important;
    border: 5px solid transparent !important;
    border-top-color: var(--hrms-tip-bg) !important;
    border-bottom-color: transparent !important;
}
.tip:hover::after,
.tip:focus-visible::after,
.tip.is-tooltip-visible::after,
.tip:hover::before,
.tip:focus-visible::before,
.tip.is-tooltip-visible::before {
    opacity: 1 !important;
    visibility: visible !important;
}
.tip:hover::after,
.tip:focus-visible::after,
.tip.is-tooltip-visible::after {
    transform: translate(-50%, 0) !important;
}

/* Header controls: tooltip below the fixed header control, same style. */
.top-bar .tip::after {
    top: calc(100% + 8px) !important;
    bottom: auto !important;
    transform: translate(-50%, -4px) !important;
}
.top-bar .tip::before {
    top: calc(100% + 3px) !important;
    bottom: auto !important;
    transform: translateX(-50%) !important;
    border-top-color: transparent !important;
    border-bottom-color: var(--hrms-tip-bg) !important;
}
.top-bar .tip:hover::after,
.top-bar .tip:focus-visible::after,
.top-bar .tip.is-tooltip-visible::after {
    transform: translate(-50%, 0) !important;
}

/* Collapsed sidebar: same tooltip design, positioned to the right. */
.sidebar.sidebar-hidden .sidebar-link[data-tip]::after {
    top: 50% !important;
    bottom: auto !important;
    left: calc(100% + 9px) !important;
    transform: translate(0, -50%) !important;
}
.sidebar.sidebar-hidden .sidebar-link[data-tip]::before {
    top: 50% !important;
    bottom: auto !important;
    left: calc(100% + 3px) !important;
    transform: translateY(-50%) !important;
    border: 5px solid transparent !important;
    border-right-color: var(--hrms-tip-bg) !important;
    border-left-color: transparent !important;
}
.sidebar.sidebar-hidden .sidebar-link[data-tip]:hover::after,
.sidebar.sidebar-hidden .sidebar-link[data-tip]:focus-visible::after,
.sidebar.sidebar-hidden .sidebar-link[data-tip].is-tooltip-visible::after {
    transform: translate(0, -50%) !important;
}

/* Mobile / QR Check-In Log — action cell must remain visible and tidy.
   The table-card pseudo-label is not useful for the action column and can
   consume the row width, so remove it and give the actions their own row. */
@media (max-width: 767.98px) {
    .table-card td.actions,
    .data-table.table-card td.actions {
        display: flex !important;
        width: 100% !important;
        min-width: 100% !important;
        max-width: 100% !important;
        box-sizing: border-box !important;
        align-items: center !important;
        justify-content: flex-start !important;
        gap: 7px !important;
        flex-wrap: nowrap !important;
        overflow-x: auto !important;
        overflow-y: visible !important;
        padding: 10px 0 2px !important;
        margin-top: 7px !important;
        border-top: 1px solid var(--border) !important;
        scrollbar-width: none !important;
    }
    .table-card td.actions::-webkit-scrollbar { display: none !important; }
    .table-card td.actions::before,
    .data-table.table-card td.actions::before {
        display: none !important;
        content: none !important;
    }
    .table-card td.actions > form,
    .table-card td.actions > a,
    .table-card td.actions > button {
        display: inline-flex !important;
        flex: 0 0 34px !important;
        width: 34px !important;
        min-width: 34px !important;
        max-width: 34px !important;
        height: 34px !important;
        min-height: 34px !important;
        margin: 0 !important;
        padding: 0 !important;
        align-items: center !important;
        justify-content: center !important;
    }
    .table-card td.actions > form > button {
        width: 34px !important;
        min-width: 34px !important;
        height: 34px !important;
        min-height: 34px !important;
        padding: 0 !important;
    }
}

/* Keep every table action column aligned on desktop/tablet too. */
@media (min-width: 768px) {
    .data-table td.actions,
    .table td.actions {
        white-space: nowrap !important;
    }
}


/* S09 V4 — unified tooltip portal. One visual style everywhere, including
   collapsed sidebar, table actions and mobile QR Check-In actions. */
#hrms-unified-tooltip {
    position: fixed;
    z-index: 10000;
    max-width: min(380px, calc(100vw - 16px));
    padding: 7px 12px;
    border-radius: 8px;
    background: #172033;
    color: #fff;
    font-size: .75rem;
    line-height: 1.25;
    font-weight: 600;
    text-align: center;
    white-space: normal;
    overflow-wrap: anywhere;
    word-break: normal;
    box-shadow: 0 10px 24px rgba(15,23,42,.22);
    pointer-events: none;
    opacity: 0;
    transform: translateY(4px);
    transition: opacity .14s ease, transform .14s ease;
}
#hrms-unified-tooltip::after {
    content: '';
    position: absolute;
    left: var(--hrms-tooltip-arrow-left, 50%);
    bottom: -5px;
    transform: translateX(-50%);
    border: 5px solid transparent;
    border-top-color: #172033;
}
#hrms-unified-tooltip.hrms-tooltip-below::after {
    top: -5px;
    bottom: auto;
    border-top-color: transparent;
    border-bottom-color: #172033;
}
#hrms-unified-tooltip.is-visible { opacity: 1; transform: translateY(0); }
/* Disable the old per-element pseudo tooltip so there is exactly one style. */
.hrms-tooltip-target.tip::before,
.hrms-tooltip-target.tip::after,
.sidebar.sidebar-hidden .hrms-tooltip-target.sidebar-link::before,
.sidebar.sidebar-hidden .hrms-tooltip-target.sidebar-link::after { display: none !important; }

/* Mobile table-card rows: action controls get their own full-width action
   lane. Never clip them; the body-level tooltip portal remains visible. */
@media (max-width: 767.98px) {
    .table-card tbody td.actions {
        width: 100%;
        min-width: 0;
        display: flex;
        align-items: center;
        justify-content: flex-end;
        gap: 7px;
        flex-wrap: nowrap;
        overflow: visible !important;
        padding: 9px 0 2px;
        margin-top: 6px;
        border-top: 1px solid var(--border);
    }
    .table-card tbody td.actions::before {
        content: attr(data-label);
        margin-right: auto;
        font-weight: 600;
        color: var(--text-muted);
        flex: 0 0 auto;
    }
    .table-card tbody td.actions .btn,
    .table-card tbody td.actions .btn-icon-only,
    .table-card tbody td.actions a {
        flex: 0 0 36px;
        width: 36px;
        min-width: 36px;
        height: 36px;
        padding: 0;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
    .table-card tbody td.actions form { display: inline-flex; flex: 0 0 auto; }
}

/* V5: Mobile / QR Check-In actions get a dedicated flex lane. The label and
   icons are separate so the action buttons can never be squeezed behind the
   right edge of a card. The tooltip itself is body-level and remains unclipped. */
@media (max-width: 767.98px) {
    .table-card tbody td.mobile-checkin-actions-cell {
        display: flex !important;
        align-items: center !important;
        justify-content: flex-start !important;
        width: 100% !important;
        min-width: 0 !important;
        max-width: 100% !important;
        overflow: visible !important;
        gap: 8px !important;
        padding: 10px 0 2px !important;
    }
    .mobile-checkin-actions-cell::before { display: none !important; }
    .mobile-action-label {
        flex: 0 0 62px !important;
        color: var(--text-muted);
        font-size: 12px;
        font-weight: 700;
    }
    .mobile-checkin-actions {
        display: flex !important;
        align-items: center !important;
        gap: 7px !important;
        min-width: 0 !important;
        flex: 1 1 auto !important;
        overflow-x: auto !important;
        overflow-y: visible !important;
        padding: 2px 2px 5px !important;
        scrollbar-width: none !important;
    }
    .mobile-checkin-actions::-webkit-scrollbar { display: none !important; }
    .mobile-checkin-actions > form,
    .mobile-checkin-actions > a,
    .mobile-checkin-actions > button {
        flex: 0 0 36px !important;
        width: 36px !important;
        min-width: 36px !important;
        max-width: 36px !important;
        height: 36px !important;
        min-height: 36px !important;
        display: inline-flex !important;
        align-items: center !important;
        justify-content: center !important;
        margin: 0 !important;
        padding: 0 !important;
    }
    .mobile-checkin-actions > form > button {
        width: 36px !important;
        min-width: 36px !important;
        height: 36px !important;
        min-height: 36px !important;
    }
}

/* V5: every icon-only employee action uses the same compact footprint. */
.actions .btn-icon-only,
.actions .action-icon-btn {
    flex: 0 0 36px !important;
    width: 36px !important;
    min-width: 36px !important;
    max-width: 36px !important;
    height: 36px !important;
    min-height: 36px !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    padding: 0 !important;
    margin: 0 !important;
}

/* =====================================================================
   S09 FINAL V6 — Mobile / QR Check-In action column visibility
   ---------------------------------------------------------------------
   The QR action cell previously had a fixed 112px desktop width while
   carrying 3–4 icon controls plus a visible "Actions" label. That made
   the first/last controls appear clipped or disappear at the right edge.
   Keep the action lane wide enough on desktop/tablet, hide the redundant
   text label (icons use the unified tooltip), and give mobile a dedicated
   full-width horizontal action lane.
   ===================================================================== */

/* Desktop / tablet: reserve a real action lane for up to four controls. */
.mobile-checkin-actions-cell,
.data-table td.mobile-checkin-actions-cell {
    width: 190px !important;
    min-width: 190px !important;
    max-width: 190px !important;
    text-align: center !important;
    vertical-align: middle !important;
    padding-left: 12px !important;
    padding-right: 12px !important;
    overflow: visible !important;
}

/* "Actions" is redundant when the controls are icon-only. */
.mobile-checkin-actions-cell .mobile-action-label {
    display: none !important;
}

.mobile-checkin-actions-cell .mobile-checkin-actions {
    display: flex !important;
    width: 100% !important;
    min-width: 0 !important;
    align-items: center !important;
    justify-content: center !important;
    gap: 8px !important;
    flex-wrap: nowrap !important;
    overflow: visible !important;
    box-sizing: border-box !important;
}

.mobile-checkin-actions-cell .mobile-checkin-actions > form,
.mobile-checkin-actions-cell .mobile-checkin-actions > a,
.mobile-checkin-actions-cell .mobile-checkin-actions > button {
    flex: 0 0 36px !important;
    width: 36px !important;
    min-width: 36px !important;
    max-width: 36px !important;
    height: 36px !important;
    min-height: 36px !important;
    max-height: 36px !important;
    margin: 0 !important;
    padding: 0 !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
}

.mobile-checkin-actions-cell .mobile-checkin-actions > form > button {
    flex: 0 0 36px !important;
    width: 36px !important;
    min-width: 36px !important;
    max-width: 36px !important;
    height: 36px !important;
    min-height: 36px !important;
    margin: 0 !important;
    padding: 0 !important;
}

/* Keep the icon controls visually consistent with all other table actions. */
.mobile-checkin-actions-cell .btn-icon-only,
.mobile-checkin-actions-cell .action-icon-btn {
    border-radius: 9px !important;
    box-sizing: border-box !important;
}

@media (max-width: 767.98px) {
    /* Mobile card rows: action lane spans the complete card width. */
    .table-card tbody td.mobile-checkin-actions-cell,
    .data-table.table-card tbody td.mobile-checkin-actions-cell {
        display: flex !important;
        width: 100% !important;
        min-width: 100% !important;
        max-width: 100% !important;
        box-sizing: border-box !important;
        align-items: center !important;
        justify-content: flex-start !important;
        gap: 0 !important;
        overflow: visible !important;
        padding: 10px 0 3px !important;
        margin-top: 8px !important;
        border-top: 1px solid var(--border) !important;
    }

    .table-card tbody td.mobile-checkin-actions-cell .mobile-action-label {
        display: none !important;
    }

    .table-card tbody td.mobile-checkin-actions-cell .mobile-checkin-actions {
        display: flex !important;
        width: 100% !important;
        min-width: 0 !important;
        flex: 1 1 100% !important;
        justify-content: flex-start !important;
        align-items: center !important;
        gap: 8px !important;
        flex-wrap: nowrap !important;
        overflow-x: auto !important;
        overflow-y: visible !important;
        padding: 2px 2px 6px !important;
        margin: 0 !important;
        scrollbar-width: none !important;
        -webkit-overflow-scrolling: touch !important;
    }

    .table-card tbody td.mobile-checkin-actions-cell .mobile-checkin-actions::-webkit-scrollbar {
        display: none !important;
    }

    .table-card tbody td.mobile-checkin-actions-cell .mobile-checkin-actions > form,
    .table-card tbody td.mobile-checkin-actions-cell .mobile-checkin-actions > a,
    .table-card tbody td.mobile-checkin-actions-cell .mobile-checkin-actions > button {
        flex: 0 0 38px !important;
        width: 38px !important;
        min-width: 38px !important;
        max-width: 38px !important;
        height: 38px !important;
        min-height: 38px !important;
        max-height: 38px !important;
    }

    .table-card tbody td.mobile-checkin-actions-cell .mobile-checkin-actions > form > button {
        width: 38px !important;
        min-width: 38px !important;
        height: 38px !important;
        min-height: 38px !important;
    }
}

/* Narrow desktop/tablet widths still get a usable action lane instead of
   squeezing the icons into the edge of the table. */
@media (min-width: 768px) and (max-width: 1100px) {
    .mobile-checkin-actions-cell,
    .data-table td.mobile-checkin-actions-cell {
        width: 175px !important;
        min-width: 175px !important;
        max-width: 175px !important;
    }
}


/* =====================================================================
   S09 V7 — UNIVERSAL ENTERPRISE ACTION SYSTEM
   Applies consistently to User Accounts, Attendance Devices, Locations,
   Shift Assignments, Shifts, Departments, Positions and Mobile/QR Log.
   One compact icon language + one tooltip language, with generous action
   lanes so no icon is clipped or pushed behind the viewport.
   ===================================================================== */

/* Every action cell reserves space instead of letting long labels squeeze
   the controls. Four controls fit comfortably; fewer controls remain centered. */
.data-table th:last-child,
.data-table td.actions,
.table th:last-child,
.table td.actions {
    width: 176px !important;
    min-width: 176px !important;
    max-width: 220px !important;
}

.data-table td.actions,
.table td.actions {
    vertical-align: middle !important;
    text-align: center !important;
    overflow: visible !important;
    white-space: nowrap !important;
    padding: 10px 12px !important;
}

.data-table td.actions > .actions,
.table td.actions > .actions,
.data-table td.actions,
.table td.actions {
    line-height: 1 !important;
}

/* Action buttons are icon-only in list/table bodies. Text is retained in
   data-tip/aria-label for accessibility and the unified tooltip. */
.data-table td.actions .btn,
.table td.actions .btn,
.device-card-actions .btn {
    width: 38px !important;
    min-width: 38px !important;
    max-width: 38px !important;
    height: 38px !important;
    min-height: 38px !important;
    padding: 0 !important;
    margin: 0 !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    flex: 0 0 38px !important;
    border-radius: 10px !important;
    box-sizing: border-box !important;
    white-space: nowrap !important;
    font-size: 0 !important;
    gap: 0 !important;
}

.data-table td.actions .btn svg,
.table td.actions .btn svg,
.device-card-actions .btn svg {
    width: 17px !important;
    height: 17px !important;
    flex: 0 0 17px !important;
}

.data-table td.actions form,
.table td.actions form,
.device-card-actions form {
    display: inline-flex !important;
    flex: 0 0 auto !important;
    margin: 0 !important;
    vertical-align: middle !important;
}

.data-table td.actions,
.table td.actions,
.device-card-actions {
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    gap: 8px !important;
    flex-wrap: nowrap !important;
}

/* Cards such as Attendance Devices use the exact same icon footprint. */
.device-card-actions {
    min-height: 50px !important;
    padding: 10px 0 0 !important;
    overflow: visible !important;
}

.cell-stack-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-top: 1px;
}

/* Unified tooltip trigger footprint. */
.tip,
.btn-icon-only,
.action-icon-btn,
.sidebar-link {
    -webkit-tap-highlight-color: transparent;
}

/* On narrower desktop/tablet widths, allow a little more action space rather
   than compressing the last control into the edge. */
@media (min-width: 768px) and (max-width: 1180px) {
    .data-table th:last-child,
    .data-table td.actions,
    .table th:last-child,
    .table td.actions { min-width: 160px !important; width: 160px !important; }
}

@media (max-width: 767.98px) {
    /* Card-style mobile tables: action controls get their own full-width lane
       on every named management page, not only the QR page. */
    .table-card tbody td.actions,
    .data-table.table-card tbody td.actions,
    .data-table tbody td.actions {
        width: 100% !important;
        min-width: 100% !important;
        max-width: 100% !important;
        display: flex !important;
        justify-content: flex-start !important;
        align-items: center !important;
        overflow: visible !important;
        white-space: nowrap !important;
        padding: 10px 0 4px !important;
        margin-top: 8px !important;
        border-top: 1px solid var(--border) !important;
        gap: 8px !important;
        flex-wrap: nowrap !important;
    }

    .table-card tbody td.actions::before,
    .data-table.table-card tbody td.actions::before,
    .data-table tbody td.actions::before {
        display: none !important;
        content: none !important;
    }

    .table-card tbody td.actions .btn,
    .data-table.table-card tbody td.actions .btn,
    .data-table tbody td.actions .btn {
        flex: 0 0 38px !important;
    }

    /* The action lane itself can scroll horizontally without clipping the
       tooltip portal or the buttons. */
    .table-card tbody td.actions,
    .data-table tbody td.actions {
        overflow-x: auto !important;
        scrollbar-width: none !important;
        -webkit-overflow-scrolling: touch !important;
    }
    .table-card tbody td.actions::-webkit-scrollbar,
    .data-table tbody td.actions::-webkit-scrollbar { display: none !important; }

    .device-card-actions {
        justify-content: flex-start !important;
        overflow-x: auto !important;
        scrollbar-width: none !important;
        -webkit-overflow-scrolling: touch !important;
    }
    .device-card-actions::-webkit-scrollbar { display: none !important; }
}


/* =====================================================================
   S09 V8 — final responsive shell + quiet sidebar tooltips
   ---------------------------------------------------------------------
   - Phones + tablets (<992px) always use the hamburger drawer.
   - Desktop (>=992px) uses the fixed sidebar rail.
   - Sidebar labels have NO tooltip while expanded. Tooltip activates only
     after hamburger collapses the desktop rail.
   - Action columns are wide enough for every icon and never clip.
   ===================================================================== */
@media (min-width: 992px) {
    .layout-shell { display: block !important; }
    .sidebar {
        display: flex !important;
        position: fixed !important;
        width: var(--hrms-sidebar-width) !important;
        min-width: var(--hrms-sidebar-width) !important;
        transform: none !important;
    }
    .sidebar.sidebar-hidden {
        display: flex !important;
        width: var(--hrms-sidebar-collapsed) !important;
        min-width: var(--hrms-sidebar-collapsed) !important;
    }
    .hamburger { display: flex !important; }
}

@media (max-width: 991.98px) {
    .layout-shell { display: block !important; }
    .sidebar {
        display: flex !important;
        position: fixed !important;
        top: 0;
        left: 0;
        bottom: 0;
        width: min(300px, 88vw) !important;
        min-width: min(300px, 88vw) !important;
        transform: translateX(-105%) !important;
        z-index: 120 !important;
    }
    .sidebar.mobile-open {
        transform: translateX(0) !important;
    }
    .sidebar.sidebar-hidden {
        width: min(300px, 88vw) !important;
        min-width: min(300px, 88vw) !important;
        transform: translateX(-105%) !important;
    }
    .sidebar.sidebar-hidden .sidebar-brand-name,
    .sidebar.sidebar-hidden .sidebar-link-label {
        display: inline !important;
    }
    .sidebar.sidebar-hidden .sidebar-link {
        justify-content: flex-start !important;
        width: 100% !important;
        min-height: 44px !important;
        padding: 10px 14px !important;
        gap: 12px !important;
    }
    .sidebar.sidebar-hidden .sidebar-brand {
        justify-content: flex-start !important;
        padding: 16px !important;
    }
    .sidebar.sidebar-hidden .sidebar-nav,
    .sidebar.sidebar-hidden .sidebar-footer { padding: 16px !important; }
    .main-wrap,
    .main-wrap.sidebar-is-open,
    .main-wrap.sidebar-is-collapsed {
        margin-left: 0 !important;
    }
    .top-bar,
    .main-wrap.sidebar-is-open .top-bar,
    .main-wrap.sidebar-is-collapsed .top-bar {
        left: 0 !important;
    }
    .hamburger { display: flex !important; }
    .sidebar-overlay { display: block; }
    .sidebar.sidebar-hidden .sidebar-link[data-tip]::before,
    .sidebar.sidebar-hidden .sidebar-link[data-tip]::after { display: none !important; }
}

/* Expanded sidebar = no sidebar tooltips. The unified JS tooltip portal is
   also gated by the sidebar-hidden state. */
.sidebar:not(.sidebar-hidden) .sidebar-link[data-tip]::before,
.sidebar:not(.sidebar-hidden) .sidebar-link[data-tip]::after {
    display: none !important;
}

/* All table/card action cells: reserve a predictable lane for icon buttons. */
.data-table th:last-child,
.data-table td.actions,
.table th:last-child,
.table td.actions {
    width: 180px !important;
    min-width: 180px !important;
    max-width: 240px;
}
.data-table td.actions,
.table td.actions {
    white-space: nowrap !important;
    overflow: visible !important;
}
.data-table td.actions,
.table td.actions,
.device-card-actions {
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    gap: 7px !important;
}
.data-table td.actions > form,
.data-table td.actions > a,
.data-table td.actions > button,
.data-table td.actions > .btn,
.table td.actions > form,
.table td.actions > a,
.table td.actions > button,
.table td.actions > .btn,
.device-card-actions > form,
.device-card-actions > a,
.device-card-actions > button {
    flex: 0 0 36px !important;
    width: 36px !important;
    min-width: 36px !important;
    max-width: 36px !important;
}

/* Mobile/tablet tables may scroll horizontally; never clip the action lane. */
@media (max-width: 991.98px) {
    .table-wrap {
        overflow-x: auto !important;
        overflow-y: visible !important;
        -webkit-overflow-scrolling: touch;
    }
    .data-table,
    .table {
        min-width: 760px;
    }
    .data-table td.actions,
    .table td.actions {
        width: 180px !important;
        min-width: 180px !important;
        max-width: 240px !important;
        position: relative;
        z-index: 2;
    }
    .device-card-actions {
        flex-wrap: nowrap !important;
        overflow-x: auto !important;
        overflow-y: visible !important;
        justify-content: flex-start !important;
        scrollbar-width: none;
    }
    .device-card-actions::-webkit-scrollbar { display: none; }
}

@media (max-width: 575.98px) {
    .top-bar { padding-left: 10px !important; padding-right: 10px !important; }
    .hamburger { width: 42px !important; height: 42px !important; flex: 0 0 42px; }
    .top-bar-actions { margin-left: auto; }
    .data-table, .table { min-width: 720px; }
}


/* S09 UX PATCH — consistent table grid + visible row numbering.
   All data tables receive a stable L.R. column from the global table
   helper, so individual views cannot accidentally omit the sequence. */
.data-table {
    table-layout: auto;
}
.data-table th,
.data-table td {
    border-right: 1px solid var(--border);
    vertical-align: middle;
}
.data-table th:last-child,
.data-table td:last-child {
    border-right: 0;
}
.data-table .table-row-number {
    width: 52px !important;
    min-width: 52px !important;
    max-width: 52px !important;
    padding-left: 10px !important;
    padding-right: 10px !important;
    text-align: center !important;
    color: var(--text-muted);
    font-weight: 700;
    white-space: nowrap;
}
.data-table thead .table-row-number {
    color: var(--text);
}
.data-table tbody tr:nth-child(even) {
    background: color-mix(in srgb, var(--surface-alt) 35%, transparent);
}
.data-table tbody tr:hover {
    background: var(--surface-alt);
}

/* The fixed desktop shell must never clip the first table column while
   the sidebar changes between 280px and 76px. */
.main-wrap,
.main-content {
    min-width: 0;
    width: auto;
    box-sizing: border-box;
}
.table-wrap {
    min-width: 0;
    width: 100%;
    box-sizing: border-box;
    scrollbar-gutter: stable;
}

.radio-card { display:flex; flex-direction:column; gap:6px; padding:14px; border:1px solid var(--border); border-radius:var(--radius); cursor:pointer; }
.radio-card input { margin-bottom:4px; }
.checkbox-row { display:flex; align-items:flex-start; gap:10px; cursor:pointer; }
.checkbox-row input { margin-top:4px; flex:0 0 auto; }


/* ═══════════════════════════════════════════════════════════════
   S09 FINAL V9 — Table grid / responsive alignment
   - Keep real table cells as table-cells (never flex the <td>)
   - Fill the table all the way to the wrapper's right border
   - Keep the row-number column visible during horizontal scrolling
   - Let wrapped Khmer/English text determine the row height naturally
   ═══════════════════════════════════════════════════════════════ */

.table-wrap {
    scrollbar-gutter: auto !important;
}

.data-table {
    width: 100% !important;
    table-layout: auto;
    border-collapse: collapse;
}

.data-table th,
.data-table td {
    vertical-align: middle;
    box-sizing: border-box;
}

.data-table th:not(.table-row-number),
.data-table td:not(.table-row-number) {
    overflow-wrap: anywhere;
}

/* Row-number column: stable and visible while a wide table scrolls. */
.data-table th.table-row-number,
.data-table td.table-row-number {
    position: sticky;
    left: 0;
    z-index: 4;
    width: 52px !important;
    min-width: 52px !important;
    max-width: 52px !important;
    padding-left: 8px !important;
    padding-right: 8px !important;
    text-align: center !important;
    white-space: nowrap !important;
    background: var(--surface) !important;
}
.data-table thead th.table-row-number {
    z-index: 6;
    background: var(--surface-alt) !important;
}

/* The last/action column remains a true table cell. This is important:
   flexing the <td> itself makes row heights/column widths inconsistent
   whenever another cell wraps to 2–4 lines. */
.data-table td.actions,
.table td.actions {
    display: table-cell !important;
    vertical-align: middle !important;
    text-align: right !important;
    white-space: nowrap !important;
    width: 180px !important;
    min-width: 180px !important;
    max-width: 240px !important;
    padding-left: 10px !important;
    padding-right: 10px !important;
}

.data-table td.actions > form,
.data-table td.actions > a,
.data-table td.actions > button,
.data-table td.actions > .btn,
.table td.actions > form,
.table td.actions > a,
.table td.actions > button,
.table td.actions > .btn {
    display: inline-flex !important;
    vertical-align: middle !important;
    margin: 0 !important;
}

.data-table td.actions > form + form,
.data-table td.actions > a + a,
.data-table td.actions > a + form,
.data-table td.actions > form + a,
.data-table td.actions > button + button,
.data-table td.actions > button + a,
.data-table td.actions > a + button {
    margin-left: 6px !important;
}

/* Avoid accidental pseudo-labels changing the desktop table geometry. */
@media (min-width: 768px) {
    .data-table td.actions::before,
    .data-table td.actions::after {
        content: none !important;
        display: none !important;
    }
}


/* =====================================================================
   S09 V10.1 — Sidebar badge clearance, date-input presentation + compact
   action controls.
   ===================================================================== */
@media (min-width: 992px) {
    /* Expanded rail: keep notification badges safely inside the sidebar
       content area so the 5px scrollbar can never cover the last digits. */
    .sidebar:not(.sidebar-hidden) .badge-dot {
        top: -5px !important;
        right: -2px !important;
    }

    /* Collapsed rail keeps the previously approved compact badge position. */
    .sidebar.sidebar-hidden .badge-dot {
        top: -4px !important;
        right: -4px !important;
    }
}

/* Date fields use a visible DD/MM/YYYY text surface while retaining a real
   native date control underneath for the browser calendar picker. */
.hrms-date-input-wrap {
    position: relative;
    display: block;
    width: 100%;
}
.hrms-date-input-wrap .hrms-date-text {
    width: 100%;
    box-sizing: border-box;
}
.hrms-date-native {
    position: absolute !important;
    inset: 0 !important;
    width: 100% !important;
    height: 100% !important;
    opacity: 0 !important;
    pointer-events: none !important;
    z-index: -1 !important;
}
.hrms-date-input-wrap::after {
    content: '';
    position: absolute;
    right: 12px;
    top: 50%;
    width: 16px;
    height: 16px;
    transform: translateY(-50%);
    pointer-events: none;
    opacity: .55;
    background: currentColor;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='black' d='M7 2h2v3h6V2h2v3h2a2 2 0 0 1 2 2v13a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h2V2Zm12 8H5v10h14V10Z'/%3E%3C/svg%3E") center/contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='black' d='M7 2h2v3h6V2h2v3h2a2 2 0 0 1 2 2v13a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h2V2Zm12 8H5v10h14V10Z'/%3E%3C/svg%3E") center/contain no-repeat;
}

/* Header/page action labels are intentionally compact: the unified tooltip
   carries the full Khmer/English meaning and aria-label remains available. */
.hrms-icon-action {
    font-size: 0 !important;
    gap: 0 !important;
    min-width: 38px !important;
    width: 38px !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
    justify-content: center !important;
}
.hrms-icon-action svg {
    margin: 0 !important;
    flex: 0 0 auto !important;
}
@media (max-width: 575.98px) {
    .hrms-icon-action {
        width: 36px !important;
        min-width: 36px !important;
    }
}

/* Mobile attendance/table action controls stay centered in their own lane. */
@media (max-width: 767.98px) {
    .table-card tbody td.actions,
    .data-table.table-card tbody td.actions {
        justify-content: center !important;
        text-align: center !important;
    }
}

/* Final override: action icons are centered on every viewport. */
.data-table td.actions,
.table td.actions {
    text-align: center !important;
}

/* S09 V11 — quieter desktop sidebar scrollbar. */
@media (min-width: 992px) {
    .sidebar-nav {
        scrollbar-width: thin;
        scrollbar-color: color-mix(in srgb, var(--text-muted) 16%, transparent) transparent !important;
    }
    .sidebar-nav::-webkit-scrollbar { width: 4px; }
    .sidebar-nav::-webkit-scrollbar-thumb {
        background: color-mix(in srgb, var(--text-muted) 16%, transparent) !important;
        border-radius: 999px;
    }
    .sidebar-nav::-webkit-scrollbar-track { background: transparent !important; }
}


/* S09 V11.2 — compact date range controls for Attendance / Mobile Check-In */
.date-range-inline {
    display: inline-flex;
    align-items: flex-end;
    gap: 8px;
    flex: 0 0 auto;
    white-space: nowrap;
}
.date-range-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 142px;
}
.date-range-field > span {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: .72rem;
    font-weight: 700;
    color: var(--text-muted);
}
.date-range-field > span svg { width: 13px; height: 13px; color: var(--primary); }
.date-range-field .hrms-date-input-wrap { width: 142px; }
.date-range-field input.hrms-date-text { width: 142px; }
.date-range-separator { padding-bottom: 10px; color: var(--text-muted); font-weight: 700; }
.date-range-inline .hrms-date-input-wrap::after { right: 10px; }
.table-action-dock {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    align-items: center;
    gap: 8px;
    margin: 0 0 var(--space-2);
}
.table-action-dock .header-actions { margin-left: auto; }
/* PATCH — Mobile Responsiveness Audit: without flex-wrap above,
   .table-action-dock's two children — .filter-bar (itself a
   multi-row wrapping block) and .header-actions (a compact row of
   icon buttons) — sat side-by-side as two columns instead of
   stacking, since neither child had a percentage width to force a
   break. .filter-bar naturally grows tall as ITS OWN fields wrap
   internally, so on a phone this left the icon buttons squeezed into
   a narrow leftover strip next to a half-width filter panel instead
   of sitting cleanly below it. flex-wrap alone isn't enough while
   .filter-bar has no explicit width, so also force both children to
   their own full-width row here. */
@media (max-width: 767.98px) {
    .table-action-dock .filter-bar,
    .table-action-dock .header-actions {
        flex: 1 1 100%;
        margin-left: 0;
    }
}
.table-action-dock .hrms-icon-action {
    font-size: 0 !important;
    gap: 0 !important;
    min-width: 38px !important;
    width: 38px !important;
    height: 38px;
    padding: 0 !important;
    justify-content: center !important;
}
.table-action-dock .hrms-icon-action svg { margin: 0; }
@media (max-width: 767.98px) {
    .date-range-inline { width: max-content; }
    .date-range-field, .date-range-field .hrms-date-input-wrap, .date-range-field input.hrms-date-text { min-width: 132px; width: 132px; }
    .table-action-dock { justify-content: flex-end; }
}
/* Add / Import / Export controls live directly above their tables. */
.table-action-dock .header-actions a.hrms-icon-action,
.table-action-dock .header-actions a.btn {
    font-size: 0 !important;
    gap: 0 !important;
    min-width: 38px;
    width: 38px;
    height: 38px;
    padding: 0 10px !important;
    justify-content: center;
}
.table-action-dock .header-actions a.hrms-icon-action svg,
.table-action-dock .header-actions a.btn svg { margin: 0 !important; }
.table-action-dock .header-actions { display:flex; align-items:center; gap:8px; }
.table-action-dock .btn-group { display:inline-flex; }

/* =====================================================================
   S09 V13.1 — Unified table toolbar / footer layout
   Reference: approved Attendance screenshot.
   - Show selector always top-left
   - Add / Import / Export always top-right
   - Showing text bottom-left
   - Pagination centered
   - Prevent date-range controls from overlapping
   ===================================================================== */
.table-top-controls,
.hrms-table-toolbar {
    display: flex !important;
    align-items: center !important;
    justify-content: space-between !important;
    gap: 12px !important;
    width: 100% !important;
    min-height: 46px !important;
    margin: 0 0 10px !important;
    padding: 0 2px !important;
}
.table-top-controls .per-page-control,
.hrms-table-toolbar .per-page-control {
    display: inline-flex !important;
    align-items: center !important;
    gap: 8px !important;
    width: auto !important;
    margin: 0 !important;
    padding: 5px 8px !important;
    border: 1px solid var(--border) !important;
    border-radius: 10px !important;
    background: var(--surface) !important;
    box-shadow: 0 2px 8px rgba(15,23,42,.04) !important;
}
.table-top-controls .per-page-control label,
.hrms-table-toolbar .per-page-control label {
    white-space: nowrap !important;
    color: var(--text-muted) !important;
    font-size: .82rem !important;
    font-weight: 600 !important;
}
.table-top-controls .per-page-control select,
.hrms-table-toolbar .per-page-control select {
    min-width: 68px !important;
    height: 32px !important;
    padding: 0 9px !important;
    border-radius: 8px !important;
}
.table-top-controls .table-toolbar-actions,
.hrms-table-toolbar .table-toolbar-actions,
.table-top-controls > .header-actions,
.hrms-table-toolbar > .header-actions {
    display: inline-flex !important;
    align-items: center !important;
    justify-content: flex-end !important;
    gap: 8px !important;
    margin-left: auto !important;
}
.table-top-controls .table-toolbar-actions .btn,
.hrms-table-toolbar .table-toolbar-actions .btn,
.table-top-controls > .header-actions .btn,
.hrms-table-toolbar > .header-actions .btn {
    margin: 0 !important;
}
/* Fix: the Export/Import icon buttons (wrapped in .btn-group) were
   inheriting the mobile-first default .btn-group { flex-direction: column }
   from responsive.css, which only flips to row at >=768px. Inside the
   compact top toolbar these must always stay in a single row — on every
   screen size, including phones — next to the "Show" selector and the
   Add (+) button, never stacked. */
.table-top-controls .btn-group,
.hrms-table-toolbar .btn-group,
.table-toolbar-actions .btn-group,
.table-action-dock .btn-group {
    display: inline-flex !important;
    flex-direction: row !important;
    width: auto !important;
    flex-wrap: nowrap !important;
}

/* The bottom row is a true 3-zone layout: info | pagination | empty.
   This keeps the page numbers visually centered regardless of info width. */
.table-footer {
    display: grid !important;
    grid-template-columns: minmax(0,1fr) auto minmax(0,1fr) !important;
    align-items: center !important;
    gap: 16px !important;
    width: 100% !important;
    margin-top: 14px !important;
}
.table-footer .client-page-info,
.table-footer .table-showing-info,
.table-footer > .text-muted {
    grid-column: 1 !important;
    justify-self: start !important;
    white-space: nowrap !important;
    font-size: .84rem !important;
}
.table-footer .pagination {
    grid-column: 2 !important;
    justify-self: center !important;
    margin: 0 !important;
}
.table-footer .per-page-control {
    grid-column: 1 !important;
}
.table-footer > .pagination + * { grid-column: 3; }

/* PATCH — single-page fallback badge (".pagination-single") + the Staff
   Cards footer's relocated Card Templates / Bulk Issue icon buttons
   (".table-footer-actions"), both landing in the footer's already-
   reserved center/third grid columns above instead of leaving them empty
   on any list with one page of results. */
.pagination-page-label {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: .8rem; color: var(--text-muted);
    padding: 6px 12px; border: 1px solid var(--border);
    border-radius: 999px; background: var(--surface);
}
.table-footer-actions {
    display: flex !important; align-items: center; gap: 8px;
    grid-column: 3 !important; justify-self: end !important;
}
@media (max-width: 767.98px) {
    .table-footer-actions { justify-self: center !important; grid-column: 1 !important; }
}

/* V13.2 — The approved Attendance pattern: Showing + Pagination share
   exactly one bottom row; page numbers occupy the visual center. */
.table-footer:has(.table-showing-info),
.hrms-table-footer {
    display: grid !important;
    grid-template-columns: minmax(0,1fr) minmax(180px,auto) minmax(0,1fr) !important;
    align-items: center !important;
    width: 100% !important;
    min-height: 42px !important;
    margin-top: 12px !important;
    gap: 12px !important;
}
.table-footer:has(.table-showing-info) .table-showing-info,
.hrms-table-footer .table-showing-info {
    grid-column: 1 !important;
    justify-self: start !important;
    white-space: nowrap !important;
    margin: 0 !important;
}
.table-footer:has(.table-showing-info) .pagination,
.hrms-table-footer .pagination {
    grid-column: 2 !important;
    justify-self: center !important;
    margin: 0 !important;
}

/* Server pagination that is not wrapped in .table-footer gets the same
   centered treatment after the V13 table-layout helper moves it. */
.hrms-table-footer {
    display: grid !important;
    grid-template-columns: minmax(0,1fr) auto minmax(0,1fr) !important;
    align-items: center !important;
    gap: 16px !important;
    width: 100% !important;
    margin-top: 14px !important;
}
.hrms-table-footer .table-showing-info {
    grid-column: 1 !important;
    justify-self: start !important;
    white-space: nowrap !important;
    color: var(--text-muted) !important;
    font-size: .84rem !important;
}
.hrms-table-footer .pagination {
    grid-column: 2 !important;
    justify-self: center !important;
    margin: 0 !important;
}

/* Date range: each date is an independent box. Never let the calendar
   affordance or From/To boxes intrude into their neighbour. */
.date-range-inline {
    display: inline-flex !important;
    align-items: flex-end !important;
    gap: 10px !important;
    min-width: 0 !important;
    flex: 0 0 auto !important;
    white-space: nowrap !important;
}
.date-range-field {
    min-width: 150px !important;
    width: 150px !important;
    flex: 0 0 150px !important;
}
.date-range-field .hrms-date-input-wrap,
.date-range-field input.hrms-date-text {
    width: 150px !important;
    min-width: 150px !important;
    max-width: 150px !important;
    box-sizing: border-box !important;
}
.date-range-field input.hrms-date-text {
    padding-right: 34px !important;
    overflow: hidden !important;
    text-overflow: clip !important;
}
.date-range-inline .hrms-date-input-wrap::after {
    right: 10px !important;
}

/* Action icon consistency at the top of every table. */
.table-toolbar-actions .btn,
.table-toolbar-actions .btn.btn-sm,
.hrms-table-toolbar .btn,
.table-top-controls .btn {
    font-size: 0 !important;
    width: 38px !important;
    min-width: 38px !important;
    height: 38px !important;
    min-height: 38px !important;
    padding: 0 !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    gap: 0 !important;
}
.table-toolbar-actions .btn svg,
.hrms-table-toolbar .btn svg,
.table-top-controls .btn svg {
    width: 17px !important;
    height: 17px !important;
    margin: 0 !important;
}

@media (max-width: 767.98px) {
    .table-footer:has(.table-showing-info),
    .hrms-table-footer {
        grid-template-columns: 1fr !important;
        justify-items: center !important;
        gap: 8px !important;
    }
    .table-footer:has(.table-showing-info) .table-showing-info,
    .hrms-table-footer .table-showing-info,
    .table-footer:has(.table-showing-info) .pagination,
    .hrms-table-footer .pagination {
        grid-column: 1 !important;
        justify-self: center !important;
    }
    .table-top-controls,
    .hrms-table-toolbar {
        flex-wrap: wrap !important;
        align-items: center !important;
        row-gap: 8px !important;
    }
    .table-top-controls .table-toolbar-actions,
    .hrms-table-toolbar .table-toolbar-actions,
    .table-top-controls > .header-actions,
    .hrms-table-toolbar > .header-actions {
        margin-left: auto !important;
    }
    .date-range-inline {
        width: 100% !important;
        max-width: 100% !important;
        overflow-x: auto !important;
        padding-bottom: 2px !important;
        -webkit-overflow-scrolling: touch !important;
    }
    .date-range-field,
    .date-range-field .hrms-date-input-wrap,
    .date-range-field input.hrms-date-text {
        width: 145px !important;
        min-width: 145px !important;
        max-width: 145px !important;
        flex-basis: 145px !important;
    }
    .table-footer,
    .hrms-table-footer {
        grid-template-columns: 1fr !important;
        justify-items: center !important;
        gap: 10px !important;
    }
    .table-footer .client-page-info,
    .table-footer .table-showing-info,
    .table-footer > .text-muted,
    .hrms-table-footer .table-showing-info {
        grid-column: 1 !important;
        justify-self: center !important;
        text-align: center !important;
    }
    .table-footer .pagination,
    .hrms-table-footer .pagination {
        grid-column: 1 !important;
        justify-self: center !important;
        max-width: 100% !important;
        overflow-x: auto !important;
    }
}

/* =====================================================================
   S09 V13.3 — Table controls live INSIDE the same bordered table box.
   Reference: Attendance.  Show + actions at top, table in the middle,
   Showing + centered pagination at bottom, all within one card border.
   ===================================================================== */
.hrms-table-shell {
    width: 100% !important;
    margin: 0 0 var(--space-4) !important;
    padding: 12px !important;
    border: 1px solid var(--border) !important;
    border-radius: var(--radius) !important;
    background: var(--surface) !important;
    box-shadow: 0 2px 10px rgba(15,23,42,.035) !important;
    box-sizing: border-box !important;
}
.hrms-table-shell > .table-top-controls,
.hrms-table-shell > .hrms-table-toolbar {
    margin: 0 0 10px !important;
    padding: 0 2px !important;
    min-height: 42px !important;
}
.hrms-table-shell > .table-wrap {
    width: 100% !important;
    margin: 0 !important;
}
.hrms-table-shell > .hrms-table-footer,
.hrms-table-shell > .table-footer {
    margin: 10px 0 0 !important;
    padding: 8px 2px 2px !important;
    border-top: 1px solid var(--border) !important;
    min-height: 42px !important;
    box-sizing: border-box !important;
}
.hrms-table-shell > .table-top-controls .per-page-control,
.hrms-table-shell > .hrms-table-toolbar .per-page-control {
    border: 0 !important;
    box-shadow: none !important;
    background: transparent !important;
    padding: 0 !important;
}
.hrms-table-shell > .table-top-controls .table-toolbar-actions,
.hrms-table-shell > .hrms-table-toolbar .table-toolbar-actions {
    margin-left: auto !important;
}

@media (max-width: 767.98px) {
    .hrms-table-shell {
        padding: 8px !important;
        border-radius: 12px !important;
    }
    .hrms-table-shell > .table-top-controls,
    .hrms-table-shell > .hrms-table-toolbar {
        min-height: 0 !important;
        margin-bottom: 8px !important;
    }
    .hrms-table-shell > .hrms-table-footer,
    .hrms-table-shell > .table-footer {
        padding-top: 8px !important;
    }
}

/* ═══════════════════════════════════════════════════════════════
   S17 — Posts, Comments & Social Feed (M07)
   Feed cards, reaction bar, comment thread. Built entirely on
   existing CSS variables (Day/Night-safe) and the existing
   .card/.badge/.btn primitives — no new color literals introduced.
   ═══════════════════════════════════════════════════════════════ */
/* S24 fix: previously capped at 760px, which left the feed narrower
   than the composer/filter panel above it and created a large blank
   gap on wide desktop screens. Feed now matches the full content
   width like every other page (composer, filter panel, tables). */
.posts-feed { max-width: none; width: 100%; }

.post-card { padding: var(--space-3); }

.post-content { white-space: pre-wrap; word-break: break-word; margin: 0 0 var(--space-2); line-height: 1.55; }

.post-images {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 8px;
    margin: var(--space-2) 0;
}
.post-images img {
    width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    border-radius: var(--radius, 10px);
    border: 1px solid var(--border);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.post-images a:hover img {
    transform: translateY(-2px);
    box-shadow: 0 8px 18px color-mix(in srgb, var(--primary) 18%, transparent);
}

.post-reactions { display: flex; flex-wrap: wrap; gap: 6px; }
.reaction-btn { display: inline-flex; align-items: center; gap: 4px; }
.reaction-btn svg { width: 15px; height: 15px; }
.reaction-btn.active {
    background: color-mix(in srgb, var(--primary) 14%, transparent);
    border-color: var(--primary);
    color: var(--primary);
}
.reaction-count { font-size: 0.78rem; font-weight: 600; min-width: 10px; }

/* ── Comment thread (single post view) ── */
.comment-thread { margin-top: var(--space-2); }
.comment-item { display: flex; gap: 10px; margin-bottom: var(--space-2); }
.comment-item .avatar-photo,
.comment-item .avatar-initial { width: 28px; height: 28px; font-size: 0.75rem; flex-shrink: 0; }
.comment-bubble {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius, 10px);
    padding: 8px 12px;
    flex: 1;
}
.comment-bubble .comment-author { font-weight: 600; font-size: 0.85rem; }
.comment-bubble .comment-meta { font-size: 0.72rem; color: var(--text-muted); margin-top: 2px; }
.comment-replies { margin-left: 38px; margin-top: 8px; }
.comment-reply-form { display: none; margin-top: 8px; margin-left: 38px; }
.comment-reply-form.is-open { display: block; }

.post-image-remove-item { position: relative; }
.post-image-remove-item.marked-for-removal img { opacity: 0.35; filter: grayscale(1); }
.post-image-remove-item label { display: flex; align-items: center; gap: 6px; font-size: 0.75rem; margin-top: 4px; }

/* ═══════════════════════════════════════════════════════════════
   S18 — Staff Performance Voting (M08)
   Colleague grid, progress bar, score sliders, radar chart card.
   Built entirely on existing CSS variables (Day/Night-safe).
   ═══════════════════════════════════════════════════════════════ */
.performance-colleague-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-2);
}
@media (min-width: 576px) { .performance-colleague-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 992px) { .performance-colleague-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1200px) { .performance-colleague-grid { grid-template-columns: repeat(4, 1fr); } }

.progress-bar {
    width: 100%;
    height: 8px;
    border-radius: 999px;
    background: var(--surface);
    border: 1px solid var(--border);
    overflow: hidden;
}
.progress-bar-fill {
    height: 100%;
    background: var(--primary);
    border-radius: 999px;
    transition: width 0.3s ease;
}

.score-row { margin-bottom: var(--space-2); }
.score-row .score-value {
    font-weight: 700;
    color: var(--primary);
    font-size: 0.95rem;
    min-width: 20px;
    text-align: right;
}
.score-slider {
    width: 100%;
    appearance: none;
    height: 6px;
    border-radius: 999px;
    background: var(--border);
    outline: none;
}
.score-slider::-webkit-slider-thumb {
    appearance: none;
    width: 18px; height: 18px;
    border-radius: 50%;
    background: var(--primary);
    cursor: pointer;
    box-shadow: 0 2px 6px color-mix(in srgb, var(--primary) 35%, transparent);
    transition: transform 0.15s ease;
}
.score-slider::-webkit-slider-thumb:hover { transform: scale(1.12); }
.score-slider::-moz-range-thumb {
    width: 18px; height: 18px;
    border-radius: 50%;
    background: var(--primary);
    border: none;
    cursor: pointer;
}

/* ═══════════════════════════════════════════════════════════════
   S19 — Reports & Export (M09)
   Report catalog cards + print-friendly table polish. Reuses
   .performance-colleague-grid (S18) for the card grid layout.
   ═══════════════════════════════════════════════════════════════ */
.report-card {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: var(--space-2);
    text-decoration: none;
    color: inherit;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.report-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 10px 22px color-mix(in srgb, var(--primary) 14%, transparent);
}
.report-card-icon {
    flex-shrink: 0;
    width: 38px; height: 38px;
    border-radius: 10px;
    display: flex; align-items: center; justify-content: center;
    background: color-mix(in srgb, var(--primary) 14%, transparent);
    color: var(--primary);
}
.report-card-icon svg { width: 20px; height: 20px; }
.report-card-disabled {
    opacity: 0.55;
    cursor: not-allowed;
    pointer-events: none;
}

@media print {
    .report-card, .performance-colleague-grid { display: none !important; }
}

/* ═══════════════════════════════════════════════════════════════
   S20 — Audit Logs (M10)
   Old/new diff cell coloring for the log detail view.
   ═══════════════════════════════════════════════════════════════ */
.log-diff-old {
    color: var(--danger);
    text-decoration: line-through;
    text-decoration-color: color-mix(in srgb, var(--danger) 50%, transparent);
}
.log-diff-new {
    color: var(--success);
    font-weight: 600;
}


/* S29 — Global table design language.
   All operational data tables use .data-table; key/value/detail tables
   intentionally keep their existing .kv-table presentation. */
.data-table {
    --table-cell-y: 11px;
    --table-cell-x: 12px;
    width: 100%;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--surface);
    color: var(--text);
}

.data-table thead th {
    padding: var(--table-cell-y) var(--table-cell-x);
    background: var(--surface-alt);
    color: var(--text);
    font-size: .76rem;
    font-weight: 800;
    letter-spacing: .01em;
    white-space: nowrap;
}

.data-table tbody td {
    padding: var(--table-cell-y) var(--table-cell-x);
    min-height: 52px;
    border-top: 1px solid var(--border);
    font-size: .84rem;
}

.data-table tbody tr {
    transition: background-color .16s ease;
}

.data-table tbody tr:hover {
    background: var(--surface-hover, var(--surface-alt));
}

.table-primary-text {
    color: var(--text);
    font-weight: 750;
    line-height: 1.25;
}

.table-secondary-text {
    margin-top: 3px;
    color: var(--text-muted);
    font-size: .76rem;
    line-height: 1.25;
}

.data-table .badge {
    vertical-align: middle;
}

.data-table .actions {
    white-space: nowrap;
}

@media (max-width: 767px) {
    .data-table {
        border-radius: 10px;
    }

    .data-table tbody td {
        padding-top: 9px;
        padding-bottom: 9px;
    }

    .table-primary-text {
        font-size: .84rem;
    }

    .table-secondary-text {
        font-size: .74rem;
    }
}

.table-combined-header {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

/* ── Locations: keep Filter + Reset visually close to the Search box.
   The shared .filter-panel-actions uses margin-left:auto so it can sit
   at the far edge of generic filter rows. Locations is intentionally
   different: its two compact actions belong directly to the search
   control, so they stay beside it instead of floating at the opposite
   side of the panel. ── */
.locations-filter-panel #location-filter-form {
    justify-content: flex-start;
    align-items: flex-end;
    gap: 10px;
}
.locations-filter-panel #location-filter-form .filter-panel-actions {
    margin-left: 0;
    flex: 0 0 auto;
    gap: 8px;
}
.locations-filter-panel #location-filter-form .filter-panel-actions .btn {
    min-width: 36px;
    width: 36px;
    height: 36px;
    min-height: 36px;
    padding: 0;
}
.locations-filter-panel #location-filter-form .filter-panel-actions .btn svg {
    width: 16px;
    height: 16px;
}

@media (max-width: 767.98px) {
    .locations-filter-panel #location-filter-form .filter-panel-actions {
        width: auto;
        flex: 0 0 auto;
    }
    .locations-filter-panel #location-filter-form .filter-panel-actions .btn {
        flex: 0 0 36px;
    }
}

/* Positions — premium searchable department filter (S31 UI refinement) */
.positions-filter-card {
    position: relative;
    z-index: 30;
    overflow: visible;
    margin-bottom: var(--space-4, 24px);
    padding: var(--space-3, 16px) var(--space-4, 24px);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 16px;
    box-shadow: var(--shadow-ambient), 0 1px 2px rgba(0,0,0,.04);
}
.positions-filter-form { display:block; }
.positions-filter-field { display:flex; flex-direction:column; gap:8px; }
.positions-filter-label {
    display:flex; align-items:center; gap:8px;
    font-size:.82rem; font-weight:700; letter-spacing:.02em;
    color:var(--text); text-transform:uppercase;
}
.positions-filter-label-icon {
    display:flex; align-items:center; justify-content:center;
    width:26px; height:26px; border-radius:8px; flex:0 0 auto;
    background: color-mix(in srgb, var(--primary) 14%, transparent);
    color: var(--primary);
}
.positions-filter-label-icon svg { width:15px; height:15px; }
.positions-department-search { position:relative; width:min(520px, 100%); z-index:31; }
.positions-search-input-row {
    position:relative; display:flex; align-items:center; min-height:48px;
    padding:0 84px 0 14px; border:1.5px solid var(--border); border-radius:12px;
    background:var(--surface-alt, var(--surface)); box-shadow:var(--shadow-xs, 0 1px 2px rgba(0,0,0,.04));
    transition:border-color .16s ease, box-shadow .16s ease, background .16s ease;
    overflow:visible;
}
.positions-department-search:hover .positions-search-input-row { border-color:var(--primary); }
.positions-department-search.is-open .positions-search-input-row,
.positions-search-input:focus { outline:none; }
.positions-department-search.is-open .positions-search-input-row {
    border-color:var(--primary); background:var(--surface);
    box-shadow:0 0 0 4px color-mix(in srgb, var(--primary) 14%, transparent);
}
.positions-search-icon { display:flex; flex:0 0 auto; color:var(--primary); }
.positions-search-icon svg { width:19px; height:19px; }
.positions-search-input { flex:1; min-width:0; width:100%; border:0 !important; outline:0 !important; background:transparent !important; box-shadow:none !important; padding:0 !important; margin:0 !important; color:var(--text); font-size:.95rem; font-weight:600; line-height:1.4; }
.positions-search-input::placeholder { color:var(--text-muted); opacity:1; font-weight:400; }
.positions-search-clear, .positions-search-chevron {
    position:absolute; top:50%; transform:translateY(-50%);
    width:32px; height:32px; display:flex; align-items:center; justify-content:center;
    border:0; background:transparent; color:var(--text-muted); border-radius:8px;
    z-index:2;
}
.positions-search-clear { right:44px; cursor:pointer; }
.positions-search-clear:hover { background:var(--surface); color:var(--danger, #DC2626); }
.positions-search-clear svg { width:14px; height:14px; }
.positions-search-chevron { right:10px; pointer-events:none; transition:transform .18s ease, color .16s ease; color:var(--text-muted); }
.positions-search-chevron svg { width:16px; height:16px; }
.positions-department-search.is-open .positions-search-chevron { transform:rotate(180deg); color:var(--primary); }
.positions-department-options {
    position:absolute; z-index:1200; left:0; right:0; top:calc(100% + 8px); max-height:min(360px, 50vh); overflow:auto;
    padding:8px; border:1px solid var(--border); border-radius:14px; background:var(--surface);
    box-shadow:var(--shadow-ambient), 0 18px 40px rgba(0,0,0,.16);
    animation: positions-dept-pop .14s ease;
}
@keyframes positions-dept-pop {
    from { opacity:0; transform:translateY(-4px); }
    to { opacity:1; transform:translateY(0); }
}
.positions-department-divider { height:1px; margin:6px 4px; background:var(--border); }
.positions-department-option {
    position:relative; width:100%; display:flex; align-items:center; gap:11px; min-height:42px; padding:9px 34px 9px 10px;
    border:0; border-radius:10px; background:transparent; color:var(--text); text-align:left; cursor:pointer; font:inherit; font-size:.92rem;
    transition: background .12s ease, color .12s ease;
}
.positions-department-option-all { font-weight:650; }
.positions-department-option[hidden] { display:none; }
.positions-department-option:hover, .positions-department-option.is-keyboard-active { background:var(--surface-alt); }
.positions-department-option.is-selected { background:color-mix(in srgb, var(--primary) 10%, transparent); color:var(--primary); font-weight:650; }
.positions-option-icon {
    display:flex; align-items:center; justify-content:center; flex:0 0 auto;
    width:28px; height:28px; border-radius:8px;
    background: var(--surface-alt);
    color:var(--text-muted);
}
.positions-option-icon svg { width:15px; height:15px; }
.positions-department-option.is-selected .positions-option-icon { background: color-mix(in srgb, var(--primary) 16%, transparent); color:var(--primary); }
.positions-option-check {
    position:absolute; right:10px; top:50%; transform:translateY(-50%);
    display:none; color:var(--primary);
}
.positions-option-check svg { width:15px; height:15px; }
.positions-department-option.is-selected .positions-option-check { display:flex; }
.positions-department-empty { padding:22px 12px; text-align:center; color:var(--text-muted); font-size:.86rem; }
.positions-filter-hint { display:flex; align-items:center; gap:6px; margin:0; color:var(--text-muted); font-size:.78rem; }
@media (max-width: 767px) {
    .positions-filter-card { padding: var(--space-3, 16px); border-radius:14px; }
    .positions-department-search { width:100%; }
    .positions-search-input-row { min-height:46px; border-radius:11px; padding-left:12px; padding-right:82px; }
    .positions-search-clear { right:42px; }
    .positions-search-chevron { right:9px; }
    .positions-department-options { max-height:55vh; }
    .positions-filter-hint { font-size:.74rem; }
}

/* ── Locations (Province/District/Commune/Village/Recycle Bin) — row
   action icon spacing.
   V1 (gap:14px on .loc-row-actions.actions, width 150px) turned out
   to still lose in the browser: s29-standard-ui.css loads AFTER this
   file and sets its own `.actions { gap: 4px !important; }` plus a
   `.locations-table-wrap .data-table td.actions { width: 148px
   !important; }` — both landed at an EQUAL specificity tie with the
   V1 rules here, and later source order wins a tie, so s29's tighter
   4px/148px values were the ones actually rendering (screenshot
   confirmed: icons touching, group pinned to the cell's right edge).
   V2 fixes this the deterministic way — by specificity, not source
   order — so it wins no matter how the stylesheets get reordered
   later: every selector below carries the `.locations-table-wrap`
   ancestor class, which nothing else touching Locations actions
   currently combines with, so these are simply more specific than
   every rule they need to beat. Also adds right padding so the icon
   group sits with breathing room before the table's edge instead of
   flush against it.
   V3 — requested: the icon group was pinned hard to the cell's right
   edge (justify-content: flex-end), which read as a small, cramped
   box floating in the far corner of a much wider column. Switched to
   flex-start so the group sits toward the left of the actions column
   instead — giving it visibly more room to breathe (reads "bigger")
   — and widened the gap between the individual icons from 10px to
   16px so Edit/Toggle/Delete no longer look huddled together.
   V4 — requested again with a reference screenshot (Positions page,
   where Edit/Deactivate sit with a clear visible gap): the box model
   here is `* { box-sizing: border-box }`, so the fixed 176px width
   from V3 already included its own padding — 3 icons (36px × 3 =
   108px) + 2 gaps + 14px/18px padding left almost no slack, so at
   real-world sub-pixel rounding the 16px gap could still render as
   icons sitting flush against each other. Widened the cell to 190px
   and dropped the side padding slightly so the icons keep an
   unmistakable 18px gap with comfortable room left over (108px
   icons + 36px gaps + 26px padding = 170px content in a 190px box —
   14px of slack, not a hairline fit).
   V5 — the ACTUAL bug, found after V4 still showed zero visible gap
   even on a confirmed-fresh load: `gap` only has any effect on a
   flex/grid container, and responsive.css carries
   `.data-table td.actions { display: table-cell !important; }`
   (line ~363) plus a duplicate under `.data-table:not(.table-card)
   td.actions` (line ~270). Both use `!important`, and an `!important`
   declaration always beats a normal one regardless of specificity —
   so `display: table-cell` was silently overriding the plain
   (non-!important) `display: flex` that `.actions` sets, on EVERY
   earlier version of this fix. `gap: 18px` was a no-op the entire
   time: the actual spacing you were seeing was the unrelated
   `margin-left: 6px !important` rule responsive.css/app.css apply to
   adjacent `form`/`a`/`button` siblings — hence "Deactivate and
   Delete still close together" no matter how big `gap` was set.
   Fixed by re-asserting `display: flex !important` at the same
   4-class specificity as the rest of this block (so it now wins over
   responsive.css's 2-class `!important` rule), and zeroing the
   competing 6px margins on the direct children so `gap` is the only
   thing controlling spacing — no more double-counted/overridden
   values. ── */
.locations-table-wrap .data-table td.actions.loc-row-actions {
    display: flex !important;
    width: 190px !important;
    min-width: 190px !important;
    max-width: 190px !important;
    justify-content: flex-start !important;
    padding-left: 12px !important;
    padding-right: 14px !important;
    gap: 18px !important;
}
.locations-table-wrap .data-table td.actions.loc-row-actions > a,
.locations-table-wrap .data-table td.actions.loc-row-actions > form,
.locations-table-wrap .data-table td.actions.loc-row-actions > button {
    margin: 0 !important;
}
@media (max-width: 767.98px) {
    .locations-table-wrap .data-table td.actions.loc-row-actions {
        width: 100% !important;
        min-width: 0 !important;
        max-width: 100% !important;
        padding-left: 10px !important;
        padding-right: 10px !important;
        justify-content: flex-start !important;
        gap: 14px !important;
    }
}

/* ═══ Personnel Actions — Career Timeline (CSS variables only: day/night safe) ═══ */
.career-timeline { list-style: none; margin: 0; padding: 0 0 0 var(--space-3, 24px); position: relative; }
.career-timeline::before { content: ''; position: absolute; left: 14px; top: 4px; bottom: 4px; width: 2px; background: var(--border, #d0d5dd); }
.career-timeline .tl-item { position: relative; margin-bottom: var(--space-2, 16px); }
.career-timeline .tl-dot { position: absolute; left: calc(-1 * var(--space-3, 24px) - 1px); top: 0; width: 30px; height: 30px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center; background: var(--surface, #fff); border: 2px solid var(--primary, #2563eb); color: var(--primary, #2563eb); }
.career-timeline .tl-dot svg { width: 16px; height: 16px; }
.career-timeline .tl-body { background: var(--surface, #fff); border: 1px solid var(--border, #d0d5dd); border-radius: var(--radius, 12px); padding: var(--space-1, 8px) var(--space-2, 16px); box-shadow: var(--shadow-sm, 0 1px 3px rgba(0,0,0,.08)); }
.career-timeline .tl-head { display: flex; flex-wrap: wrap; gap: var(--space-1, 8px); align-items: center; }
.career-timeline .tl-detail { margin-top: 4px; font-size: .92em; }
.career-timeline .tl-foot { margin-top: 4px; font-size: .8em; }
.career-timeline .tl-rejected .tl-dot, .career-timeline .tl-cancelled .tl-dot { border-color: var(--danger, #dc2626); color: var(--danger, #dc2626); }
.career-timeline .tl-pending .tl-dot, .career-timeline .tl-scheduled .tl-dot { border-color: var(--warning, #d97706); color: var(--warning, #d97706); }
@media (max-width: 575px) { .career-timeline { padding-left: 36px; } .career-timeline .tl-head { flex-direction: column; align-items: flex-start; } }
@media print { .career-timeline .tl-body { box-shadow: none; break-inside: avoid; } }

/* ═══ Personnel Action letter (print shell) ═══ */
.pa-letter { font-size: 11pt; line-height: 1.5; color: #111; }
.pa-letter p { margin: 0 0 6px; }
.pa-letter .kh { color: #374151; }
.pa-letter-date { text-align: right; margin-bottom: 14px !important; }
.pa-letter-to { margin-bottom: 14px; }
.pa-subject { font-weight: 700; margin: 12px 0; }
.pa-para { margin: 10px 0; }
.pa-letter-table { width: 100%; border-collapse: collapse; margin: 10px 0; }
.pa-letter-table th, .pa-letter-table td { border: 1px solid #9ca3af; padding: 5px 8px; text-align: left; vertical-align: top; }
.pa-letter-table th { background: #f3f4f6; }
.pa-sign-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-top: 46px; break-inside: avoid; }
.pa-sign { text-align: center; font-size: 10pt; }
.pa-sign-line { border-top: 1px solid #111; margin: 0 8px 4px; height: 0; padding-top: 36px; }
.pa-letter-note { margin-top: 18px; font-size: 9pt; color: #4b5563; }
@media (max-width: 575px) { .pa-sign-grid { grid-template-columns: 1fr; } }

/* ═══ Org chart (reporting lines) ═══ */
.org-wrap { overflow-x: auto; }
.org-tree { list-style: none; margin: 0; padding-left: var(--space-3, 24px); border-left: 2px solid var(--border, #d0d5dd); }
.org-wrap > .org-tree { border-left: 0; padding-left: 0; }
.org-tree li { margin: var(--space-1, 8px) 0; position: relative; }
.org-node { display: inline-flex; flex-direction: column; gap: 2px; min-width: 220px; padding: var(--space-1, 8px) var(--space-2, 16px); text-decoration: none; color: var(--text, #111);
    background: var(--surface, #fff); border: 1px solid var(--border, #d0d5dd); border-radius: var(--radius, 12px); box-shadow: var(--shadow-sm, 0 1px 3px rgba(0,0,0,.08)); }
.org-node:hover { border-color: var(--primary, #2563eb); }
.org-node span, .org-node small { color: var(--text-muted, #6b7280); font-size: .85em; }
.org-node em { font-style: normal; font-size: .75em; color: var(--primary, #2563eb); }

/* ═══ Personnel Actions — form/page polish (mobile-first, variables only) ═══ */
.pa-check { display: flex; align-items: flex-start; gap: var(--space-1, 8px); margin: var(--space-1, 8px) 0; line-height: 1.5; }
.pa-check input[type="checkbox"] { flex: 0 0 auto; margin-top: 4px; }
.form-card .form-group small, .form-card .form-group .text-muted { display: block; margin-top: 4px; }
.form-card h3, .form-card h4 { margin: var(--space-2, 16px) 0 var(--space-1, 8px); }
.form-card .alert { margin: var(--space-2, 16px) 0; }
.pa-file { width: 100%; max-width: 100%; }
.form-actions { display: flex; flex-wrap: wrap; gap: var(--space-1, 8px); margin-top: var(--space-3, 24px); }
.form-actions .btn { flex: 1 1 160px; justify-content: center; }
/* approve / reject / cancel: one card each, stacked on phones, side by side from tablet up */
.pa-decision-forms { display: grid; gap: var(--space-2, 16px); grid-template-columns: 1fr; margin-top: var(--space-2, 16px); }
.pa-decision-forms .card { margin: 0; }
.pa-decision-forms h3 { display: flex; align-items: center; gap: 8px; margin-top: 0; }
.pa-decision-forms .btn { width: 100%; justify-content: center; }
@media (min-width: 768px) { .pa-decision-forms { grid-template-columns: repeat(3, 1fr); align-items: start; } }
.kv-table { width: 100%; border-collapse: collapse; }
.kv-table th { text-align: left; width: 38%; font-weight: 600; color: var(--text-muted, #6b7280); padding: 10px 8px; vertical-align: top; }
.kv-table td { padding: 10px 8px; word-break: break-word; }
.kv-table tr + tr th, .kv-table tr + tr td { border-top: 1px solid var(--border, #d0d5dd); }
@media (max-width: 575px) { .kv-table th { width: 42%; } }

/* Dashboard stat cards that are links (Personnel Actions widget) — same look as the static cards */
a.stat-card, a.stat-card:hover, a.stat-card:focus { text-decoration: none; color: inherit; }
a.stat-card .stat-card-value, a.stat-card .stat-card-label { color: inherit; text-decoration: none; }
a.stat-card { transition: transform .15s ease, box-shadow .15s ease; }
a.stat-card:hover { transform: translateY(-2px); }

.kv-table a, .career-timeline a, .org-node strong { color: var(--primary, #2563eb); }
.career-timeline .tl-foot a:visited, .kv-table a:visited { color: var(--primary, #2563eb); }

/* ── Sidebar groups (collapsible) ─────────────────────────────────
   Each HR section is a click-to-expand accordion item. The group that
   holds the current page opens automatically; other groups remember the
   state the person left them in (localStorage 'hrms_nav_groups'). A
   heading only renders when the role can see a link inside the group
   (layouts/main.php). In the collapsed icon rail every group is shown
   open (icons only) and the heading becomes a thin divider. */
.sidebar-section { display: flex; flex-direction: column; }
.sidebar-group-title {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    width: 100%;
    margin-top: 8px;
    padding: 8px 14px;
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    font: inherit;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
    cursor: pointer;
    text-align: left;
    transition: background 0.15s ease, color 0.15s ease;
}
.sidebar-group-title:hover { background: var(--surface-alt); color: var(--text); }
.sidebar-group-title:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.sidebar-group-title-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sidebar-group-chevron { flex-shrink: 0; transition: transform 0.2s ease; transform: rotate(-90deg); }
.sidebar-section.is-open > .sidebar-group-title .sidebar-group-chevron { transform: rotate(0deg); }
.sidebar-section.is-open > .sidebar-group-title { color: var(--text); }

/* Smooth open/close without measuring heights */
.sidebar-group-wrap {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows 0.22s ease;
}
.sidebar-section.is-open > .sidebar-group-wrap { grid-template-rows: 1fr; }
.sidebar-group { display: flex; flex-direction: column; gap: 2px; overflow: hidden; min-height: 0; }
.sidebar-section:not(.is-open) .sidebar-group { visibility: hidden; transition: visibility 0s 0.22s; }
.sidebar-nav > a.sidebar-link:nth-child(2) { margin-bottom: 2px; }
@media (prefers-reduced-motion: reduce) {
    .sidebar-group-wrap, .sidebar-group-chevron { transition: none; }
}

/* Collapsed icon rail: always show icons, heading = divider */
.sidebar.sidebar-hidden .sidebar-group-wrap { grid-template-rows: 1fr !important; }
.sidebar.sidebar-hidden .sidebar-section .sidebar-group { visibility: visible !important; gap: 6px; }
.sidebar.sidebar-hidden .sidebar-group-title {
    height: 1px;
    min-height: 0;
    padding: 0;
    margin: 6px 12px;
    background: var(--border);
    pointer-events: none;
}
.sidebar.sidebar-hidden .sidebar-group-title-text,
.sidebar.sidebar-hidden .sidebar-group-chevron { display: none; }

/* ── Users Account filter: Search / Role / Status / Employee Link share
   the row in 4 equal columns, with the icon-only Apply + Reset pair
   sitting on the same line at the right end. Scoped to
   .filter-form-quarters; mobile keeps the stacked layout. ── */
@media (min-width: 768px) {
    .filter-panel .filter-bar { display: block; }
    .filter-panel .filter-form.filter-form-quarters {
        display: grid !important;
        grid-template-columns: repeat(4, minmax(0, 1fr)) auto;
        gap: 14px;
        width: 100%;
        align-items: end;
    }
    .filter-panel .filter-form.filter-form-quarters .filter-search-field,
    .filter-panel .filter-form.filter-form-quarters .location-cascade-field {
        width: 100% !important;
        max-width: 100% !important;
        min-width: 0 !important;
        flex: none !important;
    }
    .filter-panel .filter-form.filter-form-quarters .filter-search-field input[type="text"],
    .filter-panel .filter-form.filter-form-quarters .location-cascade-field select {
        width: 100%;
        max-width: 100%;
    }
    .filter-panel .filter-form.filter-form-quarters > .filter-panel-actions {
        grid-column: auto;
        align-self: end;
        justify-self: end;
        margin: 0 !important;
    }
}


/* =====================================================================
   Hamburger icon - cross-browser fix
   ---------------------------------------------------------------------
   The old icon was three empty <span> bars sized/coloured through
   several stacked rules (flex-column button, var() backgrounds). Some
   browsers (Safari, Firefox, older Chromium/WebView, Samsung Internet)
   collapsed the spans or dropped the colour, leaving a blank button.
   It is now ONE inline SVG that paints with currentColor, and every
   value has a plain-colour fallback before the var() version.
   ===================================================================== */
.hamburger {
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    -webkit-box-pack: center;
    -ms-flex-pack: center;
    justify-content: center;
    -webkit-box-orient: horizontal;
    -webkit-box-direction: normal;
    -ms-flex-direction: row;
    flex-direction: row;
    padding: 0;
    margin: 0;
    border: 0;
    line-height: 1;
    cursor: pointer;
    overflow: visible;
    color: #1A1A2E;
    color: var(--text, #1A1A2E);
    background-color: #EDEEF2;
    background-color: var(--surface-alt, #EDEEF2);
    -webkit-tap-highlight-color: transparent;
}
.hamburger .hamburger-icon {
    display: block;
    width: 22px;
    height: 22px;
    min-width: 22px;
    -webkit-box-flex: 0;
    -ms-flex: 0 0 auto;
    flex: 0 0 auto;
    fill: none;
    stroke: currentColor;
    pointer-events: none;
    overflow: visible;
}
.hamburger:focus-visible {
    outline: 2px solid var(--primary, #4F46E5);
    outline-offset: 2px;
}


/* Inline field validation (form[data-validate-first]) — no popup, field-level feedback */
.form-group input.is-invalid,
.form-group select.is-invalid,
.form-group textarea.is-invalid {
    border-color: var(--danger) !important;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 18%, transparent);
}
.field-error-msg {
    display: block; margin-top: 4px; font-size: .8rem; font-weight: 600;
    color: var(--danger);
}


/* Hamburger — final cross-browser guarantees (Chrome/Edge/Firefox/Safari/iOS/Android WebView).
   Legacy empty-span markup is hidden so an old cached page can never show a blank button. */
.hamburger > span { display: none !important; }
.hamburger {
    min-width: 40px; min-height: 40px;
    -webkit-flex-shrink: 0; flex-shrink: 0;
    border-radius: 12px;
    color: #1A1A2E; color: var(--text, #1A1A2E);
    border: 1px solid #D9DCE3; border: 1px solid var(--border, #D9DCE3);
    background-color: #EDEEF2; background-color: var(--surface-alt, #EDEEF2);
    touch-action: manipulation;
}
.hamburger .hamburger-icon { width: 22px; height: 22px; stroke: currentColor; opacity: 1; visibility: visible; }
.hamburger .hamburger-icon line { stroke: currentColor; }


/* ── Reports: tidy page header, filter bar and result actions ───────────── */
.report-title { display: flex; align-items: center; gap: 10px; }
.report-title-icon { display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 10px; flex: 0 0 36px;
    color: var(--primary, #2563eb); background: color-mix(in srgb, var(--primary, #2563eb) 12%, transparent); }
.report-title-icon svg { width: 20px; height: 20px; }

.report-filter-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 14px 16px; }
.report-filter-form > div:not(.report-filter-actions) { display: flex; flex-direction: column; gap: 6px; min-width: 170px; }
.report-filter-form > div > label { margin: 0; font-size: .78rem; font-weight: 600; letter-spacing: .02em; color: var(--text-muted, #64748b); }
.report-filter-form select, .report-filter-form input:not([type="hidden"]) { width: 100%; min-height: 40px; }
.report-filter-actions { display: flex; align-items: center; gap: 10px; margin-left: auto; flex-wrap: wrap; }
.report-filter-actions .btn { min-height: 40px; display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
.report-filter-actions .btn svg { width: 16px; height: 16px; flex: 0 0 16px; }

.report-result-head { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px;
    margin-bottom: var(--space-2, 12px); padding-bottom: 12px; border-bottom: 1px solid var(--border, #e5e7eb); }
.report-result-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.report-result-actions .btn { display: inline-flex; align-items: center; gap: 6px; }
.report-result-actions .btn span { display: inline !important; }
.report-result-actions .btn svg { width: 16px; height: 16px; }

/* Back / "return to list" buttons now use a left chevron: keep icon + text on one line */
.btn > svg + span, .btn-outline > svg { vertical-align: middle; }

@media (max-width: 640px) {
    .report-filter-form > div:not(.report-filter-actions) { min-width: 100%; }
    .report-filter-actions { width: 100%; margin-left: 0; }
    .report-filter-actions .btn { flex: 1 1 0; justify-content: center; }
    .report-result-actions .btn { flex: 1 1 auto; justify-content: center; }
}
@media print { .report-filter-actions, .report-result-actions { display: none !important; } }

/* Recycle Bin detail popup: translated label + small raw column name underneath */
.rb-detail-table td:first-child { width: 42%; }
.rb-detail-table .rb-field-key { margin-top: 2px; font-family: ui-monospace, Menlo, Consolas, monospace; font-size: .7rem; color: var(--text-muted, #64748b); opacity: .75; }

/* Sidebar "show all" view: every link in one list, no group headings (toggle in the sidebar footer) */
.sidebar-nav.is-flat .sidebar-group-title { display: none; }
.sidebar-nav.is-flat .sidebar-group-wrap { grid-template-rows: 1fr !important; }
.sidebar-nav.is-flat .sidebar-section .sidebar-group { visibility: visible !important; }
.sidebar-nav.is-flat .sidebar-section { margin-top: 2px; }
