/* =========================================================
   ZkPush admin shell — sidebar + topbar + main layout
   Used by Views/Shared/_Layout.cshtml.

   ┌─ DESIGN SYSTEM ────────────────────────────────────────┐
   │ Notion-inspired, RTL-primary (Arabic).                 │
   │ Lime accent (#D4FF3C) used SPARINGLY — max 3 places    │
   │ per page. Pastels for category cards. Pure white/gray  │
   │ everywhere else. No heavy shadows, soft borders only.  │
   └────────────────────────────────────────────────────────┘
   ========================================================= */

/* ============================================================
   DESIGN TOKENS — single source of truth for all UI values.
   Every subsequent rule references these; do not hardcode
   colors / sizes anywhere else in the file.
   ============================================================ */
:root {

    /* ---- Accent (use sparingly) ---- */
    --lime:                #D4FF3C;   /* hero accents, active dot, calendar today pill */
    --lime-soft:           #F0FFC4;   /* hero card background, account card, primary tagged items */

    /* ---- Pastels (for category cards / brand chips / badge backgrounds) ---- */
    --peach-50:            #FFF7ED;
    --blue-50:             #EFF6FF;
    --purple-50:           #F5F3FF;
    --teal-50:             #F0FDFA;
    --amber-50:            #FFFBEB;
    --green-50:            #ECFDF5;
    --red-50:              #FEF2F2;

    /* ---- Surfaces ---- */
    --bg-page:             #F5F5F5;   /* outer canvas */
    --bg-card:             #FFFFFF;   /* cards, panels, sidebar, topbar */
    --bg-hover:            #F8FAFC;   /* slate-50: row hover, button hover */
    --bg-input:            #F1F5F9;   /* slate-100: search bg, inline inputs idle */

    /* ---- Borders ---- */
    --border-subtle:       #F1F5F9;   /* slate-100: row dividers inside cards */
    --border-default:      #E5E7EB;   /* slate-200: card outer border */

    /* ---- Text ---- */
    --text-primary:        #0F172A;
    --text-secondary:      #475569;
    --text-tertiary:       #64748B;
    --text-placeholder:    #94A3B8;

    /* ---- Status text (paired with -50 backgrounds for badges) ---- */
    --status-green-700:    #047857;   /* present, online */
    --status-red-700:      #B91C1C;   /* absent, offline, check-out */
    --status-blue-700:     #1D4ED8;   /* leave, fingerprint */
    --status-amber-700:    #B45309;   /* missing-exit, holiday */
    --status-teal-700:     #0F766E;   /* face badge */
    --status-purple-700:   #7C3AED;   /* partial-exception, vendor stroke */

    /* ---- Layout ---- */
    --sidebar-w:           240px;
    --sidebar-w-collapsed: 64px;       /* tablet: icon-only mode */
    --topbar-h:            60px;
    --container-max:       1280px;
    --content-pad:         32px;       /* main content padding (desktop) */
    --section-gap:         32px;       /* gap between major sections on a page */
    --card-pad:            20px;       /* internal padding of a panel/card */
    --card-pad-tight:      18px;       /* alt for denser cards */
    --row-h:               44px;       /* table row, list item */
    --gap-cards:           12px;       /* gap between sibling cards in a grid */

    /* ---- Radii ---- */
    --r-card:              16px;       /* main panels, KPI cards */
    --r-card-sm:           14px;       /* tables, smaller blocks */
    --r-control:           12px;       /* search bar, larger inputs */
    --r-pill:              100px;      /* badges, language switcher */
    --r-tile:              8px;        /* nav-item highlight, small chip */

    /* ---- Typography sizes (desktop) ---- */
    --fs-page-title:       28px;       /* h1 page title */
    --fs-section:          18px;       /* section heading */
    --fs-card-title:       16px;       /* card title */
    --fs-body:             14px;       /* default body */
    --fs-secondary:        13px;       /* secondary label, sub-item */
    --fs-meta:             11px;       /* badge, group label */
    --fs-count:            10px;       /* tiny counter pip */
    --fs-kpi-number:       40px;       /* big number inside KPI card */

    /* ---- Weights (Cairo + Inter both ship 400/500/600/700) ---- */
    --fw-regular:          400;
    --fw-medium:           500;
    --fw-semibold:         600;
    --fw-bold:             700;

    /* ---- Shadow alpha (overridden in dark) ----
       Used by elevation utilities so dark mode can boost depth without
       editing every box-shadow rule. Format: r g b values; the alpha
       is applied at the use site via rgba(var(...) / α). */
    --shadow-rgb:          15 23 42;   /* slate-900 in light */
    --shadow-strong-a:     0.10;
    --shadow-soft-a:       0.06;
    --shadow-hairline-a:   0.04;
}

/* ============================================================
   DARK MODE TOKEN OVERRIDES — Notion-style dark theme.

   Philosophy:
     • Not pure black — backgrounds use deep slate (#0E1014..#1E2129)
       with a slight blue undertone, the way Notion / Linear / Vercel do
       it. Pure black causes eye strain and washes out depth.
     • Surfaces are LAYERED — page < card < hover < input — each one
       notch lighter than the one below, so cards still feel "lifted"
       on a dark canvas.
     • Pastel `-50` backgrounds are inverted to translucent tints of the
       same hue (so a "blue-50 chip" stays recognisably blue on dark).
     • Status `-700` text colors brighten to `-400`-ish equivalents so
       they read against the dark surfaces instead of vanishing.
     • Shadows use a higher alpha because dark surfaces need MORE depth
       to feel separated, not less.

   Triggered by <html data-bs-theme="dark">. The cookie-driven server-
   side render in _Layout.cshtml sets this on first paint, so there's
   no flash-of-light-on-reload.
   ============================================================ */
[data-bs-theme="dark"] {
    /* ---- Surfaces (layered light → dark; each one notch lighter than
       the one above keeps the depth ladder readable) ---- */
    --bg-page:             #0E1014;   /* outermost canvas */
    --bg-card:             #16181F;   /* cards / sidebar / topbar */
    --bg-hover:            #1E2129;   /* row hover, button hover */
    --bg-input:            #1A1D24;   /* search bg, inline inputs idle */

    /* ---- Borders ----
       Subtle uses a near-bg-input shade (visible only as a hairline);
       Default is one step up so card outlines read clearly. */
    --border-subtle:       #1E2129;
    --border-default:      #2A2E37;

    /* ---- Text — off-white primary, progressively muted secondaries.
       Pure white (#FFF) feels harsh on dark; #E5E7EB lands cleaner. ---- */
    --text-primary:        #E5E7EB;
    --text-secondary:      #9CA3AF;
    --text-tertiary:       #6B7280;
    --text-placeholder:    #4B5563;

    /* ---- Pastel hue tints (used as backgrounds for chips/notes/icons).
       Converted to translucent rgba so they tint the dark surface
       beneath instead of forcing a light box on top. ---- */
    --peach-50:            rgba(251, 146, 60, 0.12);
    --blue-50:             rgba(59, 130, 246, 0.14);
    --purple-50:           rgba(167, 139, 250, 0.14);
    --teal-50:             rgba(20, 184, 166, 0.14);
    --amber-50:            rgba(251, 191, 36, 0.14);
    --green-50:            rgba(16, 185, 129, 0.14);
    --red-50:              rgba(239, 68, 68, 0.14);

    /* ---- Status text — brighter -400/-500 hues so they pop against
       the translucent tints above. Original light-mode -700 values are
       too dark to read on dark surfaces. ---- */
    --status-blue-700:     #60A5FA;   /* blue-400 */
    --status-green-700:    #34D399;   /* green-400 */
    --status-red-700:      #F87171;   /* red-400 */
    --status-amber-700:    #FBBF24;   /* amber-400 */
    --status-teal-700:     #2DD4BF;   /* teal-400 */
    --status-purple-700:   #A78BFA;   /* purple-400 */

    /* ---- Lime accent — keeps its brand vibrancy. The neon lime works
       well on dark; the soft variant becomes a translucent glow. ---- */
    --lime:                #D4FF3C;
    --lime-soft:           rgba(212, 255, 60, 0.12);

    /* ---- Shadows — boost alpha so cards read as "raised" against dark
       surfaces. The shadow-rgb stays slate-900 (deeper than pure black
       feels softer on dark) but opacities roughly double. ---- */
    --shadow-strong-a:     0.40;
    --shadow-soft-a:       0.24;
    --shadow-hairline-a:   0.50;       /* hairline rings flip to LIGHT on dark — see overrides below */

    /* ---- Native form controls + scrollbars follow dark scheme. ---- */
    color-scheme: dark;
}

/* ---- Element-level dark adjustments where a hardcoded color
   would otherwise stay light (the 5% of cases token-flipping
   doesn't auto-fix). Scoped under [data-bs-theme="dark"] only. ---- */

/* Active "thumb" of a Notion segmented control. Light mode uses a
   near-invisible slate hairline ring; on dark surfaces a slate ring
   would also vanish. Flip it to a light hairline so the active state
   stays legible. */
[data-bs-theme="dark"] .notion-seg-btn.is-active {
    box-shadow:
        0 1px 2px rgba(0, 0, 0, 0.40),
        0 0 0 0.5px rgba(255, 255, 255, 0.06);
}
[data-bs-theme="dark"] .notion-seg {
    /* The track gets a slightly LIGHTER background than bg-input so the
       active card "thumb" still reads as elevated above it. */
    background: #1E2129;
    border-color: #2A2E37;
}

/* Notion modal shadow + dropdown panel shadow — boost on dark. */
[data-bs-theme="dark"] .notion-modal .modal-content {
    box-shadow: 0 14px 32px -10px rgba(0, 0, 0, 0.60);
}
[data-bs-theme="dark"] .activity-panel {
    box-shadow: 0 8px 28px rgba(0, 0, 0, 0.55);
}

/* Toast container — same depth boost. */
[data-bs-theme="dark"] .toast {
    box-shadow: 0 10px 24px rgba(0, 0, 0, 0.55);
}

/* Bootstrap form controls — Bootstrap auto-handles them via
   data-bs-theme="dark", but our .notion-input override needs the
   explicit dark surface so it doesn't fall back to white. */
[data-bs-theme="dark"] .notion-input,
[data-bs-theme="dark"] .form-control,
[data-bs-theme="dark"] .form-select,
[data-bs-theme="dark"] .bydate-input {
    background: var(--bg-input);
    color: var(--text-primary);
    border-color: var(--border-default);
}
[data-bs-theme="dark"] .notion-input::placeholder,
[data-bs-theme="dark"] .form-control::placeholder,
[data-bs-theme="dark"] .bydate-input::placeholder {
    color: var(--text-placeholder);
}
[data-bs-theme="dark"] .notion-input:focus,
[data-bs-theme="dark"] .form-control:focus,
[data-bs-theme="dark"] .bydate-input:focus {
    background: var(--bg-card);
    border-color: var(--text-secondary);
    box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.04);
}
/* Read-only inputs stay distinguishable on dark. */
[data-bs-theme="dark"] .notion-input[readonly],
[data-bs-theme="dark"] .notion-input.is-locked {
    background: var(--bg-page);
    color: var(--text-tertiary);
}
/* Native date/time pickers' calendar icon — invert so it's visible. */
[data-bs-theme="dark"] input[type="date"]::-webkit-calendar-picker-indicator,
[data-bs-theme="dark"] input[type="time"]::-webkit-calendar-picker-indicator {
    filter: invert(1) opacity(0.7);
}

/* The topbar search has its own background hookup — re-anchor on dark. */
[data-bs-theme="dark"] .topbar-search input {
    background: var(--bg-input);
    color: var(--text-primary);
}
[data-bs-theme="dark"] .topbar-search input::placeholder { color: var(--text-placeholder); }
[data-bs-theme="dark"] .topbar-search input:focus { background: var(--bg-card); }
[data-bs-theme="dark"] .topbar-search-shortcut {
    background: var(--bg-card);
    border-color: var(--border-default);
    color: var(--text-tertiary);
}

/* The kbd-style chips in the command palette footer. */
[data-bs-theme="dark"] kbd {
    background: var(--bg-input);
    color: var(--text-secondary);
    border-color: var(--border-default);
}

/* Tables: header band needs to read against dark cards. */
[data-bs-theme="dark"] .bydate-table thead th {
    background: var(--bg-input);
    color: var(--text-tertiary);
}
[data-bs-theme="dark"] .bydate-table tbody tr:hover td { background: var(--bg-hover); }
[data-bs-theme="dark"] .bydate-table tbody tr.is-selected td { background: rgba(96, 165, 250, 0.10); }

/* Bootstrap badge variants we still emit in some places. They were tuned
   for light backgrounds; in dark they need a flat tint approach. */
[data-bs-theme="dark"] .badge.bg-success-subtle { background: rgba(16,185,129,0.16) !important; color: #6EE7B7 !important; }
[data-bs-theme="dark"] .badge.bg-danger-subtle  { background: rgba(239,68,68,0.16)  !important; color: #FCA5A5 !important; }
[data-bs-theme="dark"] .badge.bg-warning-subtle { background: rgba(251,191,36,0.16) !important; color: #FCD34D !important; }
[data-bs-theme="dark"] .badge.bg-info-subtle    { background: rgba(96,165,250,0.16) !important; color: #93C5FD !important; }
[data-bs-theme="dark"] .badge.bg-primary-subtle { background: rgba(96,165,250,0.16) !important; color: #93C5FD !important; }
[data-bs-theme="dark"] .badge.bg-secondary-subtle{background: rgba(148,163,184,0.16)!important; color: #CBD5E1 !important; }

/* Subtle emoji-grade tweaks: the row "—" placeholder color and the
   chip-counter chip use slate hex literals; tint them down on dark. */
[data-bs-theme="dark"] .bydate-empty { color: #4B5563; }
[data-bs-theme="dark"] .bydate-status[data-tone="weekly-off"] { background: rgba(148,163,184,0.16); color: #CBD5E1; }

/* Activity bell badge keeps the red but with a dark-card border so it
   doesn't optically merge with the bell wrapper. */
[data-bs-theme="dark"] .activity-bell-badge {
    border-color: var(--bg-card);
}

/* ============================================================
   /CustomReports — Notion-style row primitives.
   The page uses .bydate-table-card / .bydate-scroll-host / .bydate-table
   for the shell; the rules below decorate the row content (title +
   key stack, action icon buttons, audit modal helpers).
   ============================================================ */

/* Intro copy on the toolbar. Soft, narrow column so the right-aligned
   filter + button group has breathing room on wide screens. */
.cr-intro-text {
    font-size: var(--fs-meta);
    color: var(--text-secondary);
    margin: 0;
    max-width: 480px;
    line-height: 1.5;
    flex: 1 1 280px;
}

/* "Show disabled" toggle — replaces Bootstrap's stock checkbox row with
   a tighter Notion-y label that sits inside the .bydate-actions cluster. */
.cr-toggle-label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: var(--fs-meta);
    color: var(--text-secondary);
    cursor: pointer;
    user-select: none;
    line-height: 1;
}
.cr-toggle-label input[type="checkbox"] {
    width: 14px; height: 14px;
    accent-color: var(--text-primary);
    cursor: pointer;
    margin: 0;
}
.cr-toggle-label:hover { color: var(--text-primary); }

/* Second toolbar row — tightened spacing since both children are
   compact (a 14-px checkbox and a count text). */
.cr-toolbar-row-2 {
    border-top: 1px solid var(--border-subtle);
    padding-top: 10px;
    margin-top: 4px;
}

/* ---- Polished empty state for /CustomReports ----
   Hero pattern: big soft icon + short message + inline primary CTA so
   the first-time user has a clear next action without scanning back to
   the toolbar.

   Specificity note: `.bydate-table td` sets `text-align: end` (right in
   LTR). The double-class selector `td.cr-empty-state` matches that
   specificity AND beats it by being defined later, which lets us recover
   center-alignment for the empty cell. The contents are laid out via
   flex column for predictable vertical rhythm and bullet-proof centering
   (rather than relying on text-align inheritance, which inline-flex
   children can dodge). */
td.cr-empty-state {
    text-align: center;
    padding: 56px 24px 60px;
    background: var(--bg-card);
}
.cr-empty-state-inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0;
}
.cr-empty-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 64px;
    height: 64px;
    margin-bottom: 16px;
    border-radius: 16px;
    background: var(--blue-50);
    color: var(--status-blue-700);
    font-size: 28px;
}
.cr-empty-title {
    font-size: var(--fs-secondary);
    color: var(--text-secondary);
    line-height: 1.5;
    max-width: 420px;
    margin: 0;
    text-align: center;
}

/* Primary affirmative-action button — brand lime fill.
   Used for "Add X", "Save", "Confirm", "Download", and any other
   non-destructive primary CTA across the app. White text on lime
   has poor contrast (lime #D4FF3C is too light), so the label takes
   the dark slate `--text-primary` — same approach Notion / Linear
   take when their accent is a bright color. */
.bydate-btn.cr-btn-primary {
    background: var(--lime);
    color: var(--text-primary);
    border-color: var(--lime);
    font-weight: var(--fw-medium);
}
.bydate-btn.cr-btn-primary:hover {
    background: #C0EC2A;     /* slightly darker lime for hover */
    border-color: #C0EC2A;
    color: var(--text-primary);
}
.bydate-btn.cr-btn-primary:focus-visible {
    outline: 3px solid var(--lime-soft);
    outline-offset: 1px;
}
.bydate-btn.cr-btn-primary:disabled,
.bydate-btn.cr-btn-primary[disabled] {
    background: var(--lime-soft);
    border-color: var(--lime-soft);
    color: var(--text-tertiary);
    cursor: not-allowed;
}
/* Icons inside the lime CTA also pick up the dark text color. */
.bydate-btn.cr-btn-primary i { color: inherit; }

/* Title + key two-line cell (title bold on top, mono-script key below). */
.cr-name-cell {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}
.cr-title {
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 260px;
}
.cr-key {
    font-family: 'Inter', 'Consolas', monospace;
    font-size: 11px;
    color: var(--text-tertiary);
    line-height: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 260px;
}

/* Imported-at date, audit "actor" and "detail" — same muted slate. */
.cr-date,
.cr-actor,
.cr-detail {
    font-size: var(--fs-meta);
    color: var(--text-secondary);
}
.cr-date,
.cr-actor   { font-variant-numeric: tabular-nums; }
.cr-detail  { color: var(--text-tertiary); }

/* ---- Per-row action icon buttons (cr-act-btn) ----
   Anatomy: 32×32 transparent square that brightens to a tonal pastel
   on hover. Each tonal variant previews the action's category:
     • is-primary  blue    → Run
     • is-success  green   → Enable
     • is-warn     amber   → Disable
     • is-danger   red     → Delete
     • (default)   slate   → neutral (Export, Audit) */
.cr-actions {
    display: inline-flex;
    align-items: center;
    justify-content: flex-end;
    gap: 4px;
    white-space: nowrap;
}
.cr-act-btn {
    width: 32px; height: 32px;
    display: inline-grid;
    place-items: center;
    border: 1px solid transparent;
    border-radius: 8px;
    background: transparent;
    color: var(--text-tertiary);
    cursor: pointer;
    text-decoration: none;          /* anchor variants */
    transition: background 0.1s, color 0.1s, border-color 0.1s;
    font-size: 14px;
}
.cr-act-btn:hover {
    background: var(--bg-hover);
    border-color: var(--border-subtle);
    color: var(--text-primary);
}
.cr-act-btn:focus-visible {
    outline: 2px solid var(--status-blue-700);
    outline-offset: 2px;
}
.cr-act-btn:disabled,
.cr-act-btn[disabled] {
    opacity: 0.4;
    cursor: not-allowed;
}
.cr-act-btn:disabled:hover,
.cr-act-btn[disabled]:hover {
    background: transparent;
    border-color: transparent;
    color: var(--text-tertiary);
}
.cr-act-btn.is-primary:hover  { background: var(--blue-50);  border-color: #BFDBFE; color: var(--status-blue-700); }
.cr-act-btn.is-success:hover  { background: var(--green-50); border-color: #BBF7D0; color: var(--status-green-700); }
.cr-act-btn.is-warn:hover     { background: var(--amber-50); border-color: #FDE68A; color: var(--status-amber-700); }
.cr-act-btn.is-danger:hover   { background: var(--red-50);   border-color: #FECACA; color: var(--status-red-700); }

/* ---- Audit modal body + nested table ----
   The body is scrolled vertically so a long audit log doesn't push the
   modal past the viewport. The nested .bydate-table loses its sticky
   thead intentionally — modals already have a fixed header above. */
.cr-audit-body {
    padding: 0;
    max-height: 60vh;
    overflow-y: auto;
}
.cr-audit-table thead th {
    position: static;
    background: var(--bg-input);
    color: var(--text-tertiary);
    font-size: var(--fs-meta);
}
.cr-audit-table th,
.cr-audit-table td {
    padding: 10px 16px;
}
.cr-audit-table tbody tr:hover td { background: transparent; cursor: default; }

/* ---- Dark-mode tweaks for cr-act-btn tonal hovers (the rgba(*, 0.14)
   tints from the dark token block already work, but the static border
   colors in the rules above are light-mode hex literals — flip them). */
[data-bs-theme="dark"] .cr-act-btn.is-primary:hover  { border-color: rgba(96,165,250,0.35); }
[data-bs-theme="dark"] .cr-act-btn.is-success:hover  { border-color: rgba(52,211,153,0.35); }
[data-bs-theme="dark"] .cr-act-btn.is-warn:hover     { border-color: rgba(251,191,36,0.35); }
[data-bs-theme="dark"] .cr-act-btn.is-danger:hover   { border-color: rgba(248,113,113,0.35); }

/* Legacy .inline-edit (used by a couple of older inline-rename fields
   in admin pages — predates the .notion-input vocabulary). Patch the
   hardcoded whites/grays so it doesn't stick out as light boxes on
   the dark canvas. */
[data-bs-theme="dark"] .inline-edit {
    background: var(--bg-input);
    color: var(--text-primary);
    border-color: var(--border-default);
}
[data-bs-theme="dark"] .inline-edit:hover { background: var(--bg-hover); border-color: var(--text-tertiary); }
[data-bs-theme="dark"] .inline-edit:focus {
    background: var(--bg-card);
    border-color: var(--status-blue-700);
    box-shadow: 0 0 0 2px rgba(96,165,250,0.18);
}

/* The .dot indicators (small colored circles on Devices/Health) ship
   with a colored ring made by box-shadow that uses light-mode rgba
   values — tone them up on dark so the ring still glows. */
[data-bs-theme="dark"] .dot.ok      { box-shadow: 0 0 0 3px rgba(52,211,153,0.30); }
[data-bs-theme="dark"] .dot.bad     { box-shadow: 0 0 0 3px rgba(248,113,113,0.30); }
[data-bs-theme="dark"] .dot.warn    { box-shadow: 0 0 0 3px rgba(251,191,36,0.30); }

/* sync/wait/recv badges rely on slate hex literals — flip to lighter
   slate tints on dark so the chip stays readable. */
[data-bs-theme="dark"] .sync-badge,
[data-bs-theme="dark"] .wait-badge { background: rgba(148,163,184,0.16); color: #CBD5E1; }

/* The chips-row "all" button (ByDate filter) hardcodes slate-900 —
   flip to a slate-100 fill so it inverts cleanly. */
[data-bs-theme="dark"] .bydate-chip[data-status="all"] { background: #E5E7EB; color: #0F172A; }
[data-bs-theme="dark"] .bydate-chip[data-status="all"] .bydate-chip-count { background: #475569; color: #F1F5F9; }

/* Keep the activity tab active underline visible — it inherits
   text-primary which is now off-white, so it's already correct. */

/* ============================================================
   BASE TYPOGRAPHY
   Cairo first for Arabic glyphs, Inter as the Latin/numbers
   fallback. This pairing means digits (PINs, dates, KPI counts)
   render in Inter even on the Arabic locale — important because
   Cairo's digits are wider than Inter's and can wreck KPI cards.
   ============================================================ */
body {
    background: var(--bg-page);
    color: var(--text-primary);
    font-family: 'Cairo', 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
    font-size: var(--fs-body);
    font-weight: var(--fw-regular);
    line-height: 1.5;
    /* Tighten the letterspacing slightly on web — mobile-app spec is too tight for desktop */
    letter-spacing: -0.005em;
}

/* Headings ladder. Page templates can override via Razor @section but this
   is the default vertical rhythm for any unstyled content. */
h1, .h1 { font-size: var(--fs-page-title); font-weight: var(--fw-semibold); letter-spacing: -0.015em; }
h2, .h2 { font-size: 22px;                  font-weight: var(--fw-semibold); letter-spacing: -0.012em; }
h3, .h3 { font-size: var(--fs-section);     font-weight: var(--fw-semibold); }
h4, .h4 { font-size: var(--fs-card-title);  font-weight: var(--fw-semibold); }
h5, .h5 { font-size: var(--fs-card-title);  font-weight: var(--fw-medium); }
h6, .h6 { font-size: var(--fs-secondary);   font-weight: var(--fw-medium); }

/* Numbers always Inter. Apply via .num to any cell/span that's purely numeric. */
.num, time, .punch-time { font-family: 'Inter', system-ui, sans-serif; font-variant-numeric: tabular-nums; }

/* Helpers used across pages */
.text-secondary  { color: var(--text-secondary)  !important; }
.text-tertiary   { color: var(--text-tertiary); }
.text-placeholder{ color: var(--text-placeholder); }

/* ============================================================
   SIDEBAR (Notion-style, RTL-primary)
   240px on the right (RTL native via inset-inline-start), white
   background, soft border. Three groups (Main / Administration /
   Settings) with section labels in slate-placeholder, items in
   slate-secondary. Active item: slate100 fill + lime 4×4 dot on
   the inline-end edge. Sub-items indented with a small dot prefix.
   ============================================================ */
.sidebar {
    position: fixed;
    top: 0;
    bottom: 0;
    inset-inline-start: 0;            /* RTL: this is the right edge */
    width: var(--sidebar-w);
    background: var(--bg-card);
    border-inline-end: 1px solid var(--border-default);
    z-index: 30;
    display: flex;
    flex-direction: column;
}

/* ----- Brand block ----- */
.sidebar-brand {
    padding: 20px 16px;
    border-bottom: 1px solid var(--border-subtle);
    display: flex;
    flex-direction: column;
    /* Step 52 — hug the inline-start edge (RIGHT in RTL, LEFT in LTR) so the
       logo sits flush above the Attend pill instead of stretching/centering. */
    align-items: flex-start;
    gap: 8px;
    text-decoration: none;
    color: var(--text-primary);
}
.sidebar-brand:hover { color: var(--text-primary); }

/* Mobile-only close button — sits at the top-end corner of the
   offcanvas drawer. Bootstrap's `.d-md-none` keeps it hidden on tablet+
   where the sidebar is permanently visible. 36×36 touch target. */
.sidebar-close {
    position: absolute;
    top: 12px;
    inset-inline-end: 12px;
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border-default);
    background: var(--bg-card);
    color: var(--text-secondary);
    border-radius: 8px;
    font-size: 16px;
    cursor: pointer;
    z-index: 5;
    transition: background 0.15s, color 0.15s;
}
.sidebar-close:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}
.sidebar-brand .brand-name {
    font-size: 18px;
    font-weight: var(--fw-medium);
    line-height: 1.2;
    letter-spacing: -0.01em;
}
/* Step 52 — brand logo mark (replaces the .brand-name word-mark). The
   "B" fills with currentColor so it flips dark→light with the sidebar
   theme; the blue swoosh is baked into the SVG. */
.brand-logo {
    height: 56px;
    width: auto;
    display: block;
    color: var(--text-primary);   /* drives the letter-mark via currentColor */
}

/* ============================================================
   Step 54 — Vendor attribution (Energy Center Co. / شركة مركز الطاقة)
   ============================================================ */
.vendor-credit {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: var(--fs-meta);
    color: var(--text-tertiary);
    line-height: 1.3;
}
.vendor-credit-logo {
    height: 18px;
    width: auto;
    flex-shrink: 0;
}
.vendor-credit-text { white-space: nowrap; }
.vendor-credit-sep  { margin: 0 4px; opacity: 0.5; }
.vendor-credit-ver  { font-family: 'Inter', system-ui, sans-serif; }

/* About modal (Settings → About) */
.about-modal { border-radius: var(--r-card); }
.about-vendor {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    padding: 8px 0 18px;
    text-align: center;
}
.about-vendor-logo { height: 56px; width: auto; }
.about-vendor-name {
    font-size: var(--fs-card-title);
    font-weight: var(--fw-semibold);
    color: var(--text-primary);
}
.about-list {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 8px 16px;
    margin: 0;
    padding: 14px 0;
    border-top: 1px solid var(--border-subtle);
    border-bottom: 1px solid var(--border-subtle);
}
.about-list dt { color: var(--text-tertiary); font-size: var(--fs-secondary); }
.about-list dd { margin: 0; color: var(--text-primary); font-weight: var(--fw-medium); font-size: var(--fs-secondary); }
.about-copyright {
    margin-top: 14px;
    text-align: center;
    font-size: var(--fs-meta);
    color: var(--text-tertiary);
}
.brand-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--lime);
    color: var(--text-primary);
    padding: 4px 10px;
    border-radius: var(--r-pill);
    font-size: 14px;
    font-weight: var(--fw-medium);
}
.brand-pill i { font-size: 14px; }

/* ----- Nav body (scrollable) ----- */
.sidebar-nav {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 12px 0;
    /* Subtle scrollbar so it doesn't dominate the nav visually */
    scrollbar-width: thin;
    scrollbar-color: var(--border-default) transparent;
}
.sidebar-nav::-webkit-scrollbar { width: 6px; }
.sidebar-nav::-webkit-scrollbar-thumb { background: var(--border-default); border-radius: 3px; }

/* Group label ("القسم الرئيسي" / "الإدارة" / "الإعدادات") */
.nav-group-label {
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    color: var(--text-placeholder);
    padding: 12px 16px 6px;
    text-transform: none;
    letter-spacing: 0;
}

/* Item list */
.nav-list {
    list-style: none;
    padding: 0;
    margin: 0 8px;
}

/* Top-level item */
.nav-item {
    position: relative;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    color: var(--text-secondary);
    font-size: var(--fs-body);
    font-weight: var(--fw-regular);
    text-decoration: none;
    border-radius: var(--r-tile);
    line-height: 1.2;
    cursor: pointer;
    border: none;
    background: transparent;
    width: 100%;
    text-align: start;
}
.nav-item:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}
.nav-item.active {
    background: var(--bg-input);
    color: var(--text-primary);
    font-weight: var(--fw-medium);
}
/* Lime 4×4 dot on the inline-start edge (visual right in RTL) for the active
   item — sits inside the rounded background pill, 4px from the inline-start
   edge. Replaces the previous inline-end placement; the inline-end side is
   now reserved for badges and hint indicators. */
.nav-item.active::before {
    content: "";
    position: absolute;
    inset-inline-start: 4px;
    top: 50%;
    transform: translateY(-50%);
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background: var(--lime);
}
.nav-item > i:first-child {
    font-size: 16px;
    width: 20px;
    text-align: center;
    color: var(--text-tertiary);
    flex-shrink: 0;
}
.nav-item.active > i:first-child { color: var(--text-primary); }

/* Chevron for collapsible parents */
.nav-item .chevron {
    margin-inline-start: auto;
    font-size: 11px;
    transition: transform 0.2s ease;
    color: var(--text-tertiary);
}
.nav-item[aria-expanded="true"] .chevron { transform: rotate(180deg); }

/* Sub-items list (Bootstrap collapse content) */
.nav-sub {
    list-style: none;
    padding: 4px 0 8px;
    margin: 0;
}
.nav-sub-item {
    position: relative;
    display: flex;
    align-items: center;
    gap: 10px;
    padding-block: 8px;
    padding-inline-start: 36px;        /* indent from the parent's icon column */
    padding-inline-end: 12px;
    color: var(--text-secondary);
    font-size: var(--fs-secondary);
    text-decoration: none;
    border-radius: var(--r-tile);
    line-height: 1.2;
    margin: 0 8px;
}
.nav-sub-item:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}
.nav-sub-item.active {
    background: var(--bg-input);
    color: var(--text-primary);
    font-weight: var(--fw-medium);
}
.nav-sub-item.active::after {
    content: "";
    position: absolute;
    inset-inline-end: 8px;
    top: 50%;
    transform: translateY(-50%);
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background: var(--lime);
}
/* Tiny bullet to the inline-start of each sub-item */
.nav-sub-item::before {
    content: "";
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background: var(--text-placeholder);
    flex-shrink: 0;
    opacity: 0.5;
}
.nav-sub-item.active::before { opacity: 0; }   /* hide bullet when active dot is showing */

/* ============================================================
   SIDEBAR — simplified-nav additions (sidebar redesign)
   The post-redesign sidebar is a flat 7-item list, no group
   labels, no sub-menus. Each .nav-item can host:
   • a label badge (.nav-badge — e.g. red "2" on الأجهزة)
   • a hint indicator (.nav-hint — chevron + count on الإعدادات)
   Both push themselves to the inline-end (visual left in RTL)
   via margin-inline-start: auto, leaving icon+label grouped at
   the inline-start (visual right in RTL).
   ============================================================ */

/* Numeric/severity-driven badge inside a nav-item. Inter for the
   tabular numeral; Cairo could be used for a label badge but the
   only badges shipping today are counts, so Inter wins. */
.nav-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-inline-start: auto;       /* push to the inline-end */
    padding: 2px 8px;
    border-radius: var(--r-pill);
    background: var(--bg-input);
    color: var(--text-secondary);
    font-family: 'Inter', system-ui, sans-serif;
    font-size: 10px;
    font-weight: var(--fw-medium);
    line-height: 1.4;
    font-variant-numeric: tabular-nums;
    min-width: 18px;
    flex-shrink: 0;
}
.nav-badge--critical {
    background: #FEE2E2;             /* red-100 */
    color: var(--status-red-700);
}
.nav-badge--warning {
    background: #FEF3C7;             /* amber-100 */
    color: var(--status-amber-700);
}
.nav-badge--info {
    background: #DBEAFE;             /* blue-100 */
    color: var(--status-blue-700);
}

/* Hint indicator on the إعدادات nav-item — a small chevron that
   points to the inline-end (i.e., away from the start of reading
   in RTL: chevron-left in absolute coords) plus a numeric hint of
   how many sub-pages live behind the link. */
.nav-hint {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-inline-start: auto;
    flex-shrink: 0;
}
.nav-hint-count {
    font-family: 'Inter', system-ui, sans-serif;
    font-size: 10px;
    font-weight: var(--fw-medium);
    color: var(--text-placeholder);  /* slate-400 */
    line-height: 1;
    font-variant-numeric: tabular-nums;
}
.nav-hint-chevron {
    font-size: 14px;
    color: #CBD5E1;                  /* slate-300 — softer than the count */
    line-height: 1;
}

/* Subtle 1px separator line between primary nav and the Settings
   meta entry. Sits inside the .nav-list <ul>; gets its own list-
   item slot so spacing controls cleanly via margin. */
.nav-separator {
    display: block;
    height: 1px;
    background: var(--border-subtle);
    margin: 14px 4px 4px;            /* 14 above, 4 below per brief */
    list-style: none;
    pointer-events: none;
}

/* ----- Account card (sticky bottom) ----- */
.sidebar-account {
    border-top: 1px solid var(--border-subtle);
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    background: var(--bg-card);
}
.account-card {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    background: var(--lime-soft);
    border: none;
    border-radius: var(--r-tile);
    width: 100%;
    text-align: start;
    cursor: pointer;
    color: var(--text-primary);
    text-decoration: none;
}
.account-card:hover { filter: brightness(0.97); color: var(--text-primary); }
.account-avatar {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--bg-card);
    display: grid;
    place-items: center;
    font-size: 13px;
    font-weight: var(--fw-semibold);
    color: var(--text-primary);
    flex-shrink: 0;
}
.account-info {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    flex: 1;
}
.account-name {
    font-size: var(--fs-body);
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    line-height: 1.2;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.account-role {
    font-size: var(--fs-meta);
    color: var(--text-tertiary);
    line-height: 1.2;
}
.account-card > i:last-child { font-size: 12px; color: var(--text-tertiary); }

.logout-btn {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    background: transparent;
    border: none;
    border-radius: var(--r-tile);
    width: 100%;
    text-align: start;
    color: var(--status-red-700);
    font-size: var(--fs-secondary);
    font-weight: var(--fw-medium);
    cursor: pointer;
}
.logout-btn:hover { background: var(--red-50); }
.logout-btn i { font-size: 14px; }


/* ============================================================
   TOPBAR (60px)
   White background, sits across the top after the sidebar.
   Houses: search input, language pills, theme toggle.
   Avatar + dropdown moved to sidebar account card.
   ============================================================ */
.topbar {
    position: fixed;
    top: 0;
    inset-inline-start: var(--sidebar-w);
    inset-inline-end: 0;
    height: var(--topbar-h);
    background: var(--bg-card);
    border-bottom: 1px solid var(--border-subtle);
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 0 var(--content-pad);
    z-index: 20;
}

/* Hamburger (mobile only, hidden on desktop) */
.topbar-hamburger {
    display: none;
    width: 36px;
    height: 36px;
    border-radius: var(--r-tile);
    border: none;
    background: transparent;
    color: var(--text-primary);
    font-size: 20px;
    place-items: center;
    cursor: pointer;
    flex-shrink: 0;
}
.topbar-hamburger:hover { background: var(--bg-hover); }

/* Search */
.topbar-search {
    position: relative;
    flex: 1;
    max-width: 480px;
}
.topbar-search input {
    width: 100%;
    height: 40px;
    background: var(--bg-input);
    border: 1px solid transparent;
    border-radius: var(--r-control);
    padding-inline-start: 14px;
    padding-inline-end: 40px;        /* room for the icon on the inline-end side */
    font-size: var(--fs-body);
    font-family: inherit;
    color: var(--text-primary);
    transition: background 0.15s, border-color 0.15s;
}
.topbar-search input::placeholder { color: var(--text-placeholder); }
.topbar-search input:focus {
    outline: none;
    background: var(--bg-card);
    border-color: var(--border-default);
}
.topbar-search .search-icon {
    position: absolute;
    inset-inline-end: 14px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-placeholder);
    font-size: 16px;
    pointer-events: none;
}

/* ============================================================
   Trailing actions group — language · divider · bell · theme.
   Tight horizontal cluster anchored to the inline-end edge of
   the topbar. The thin .topbar-divider visually separates the
   "preferences" cluster (language) from the "events + theme"
   cluster so the eye reads them as two groups.
   ============================================================ */
.topbar-actions {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
}
.topbar-divider {
    display: inline-block;
    width: 1px;
    height: 22px;
    background: var(--border-subtle);
    margin: 0 4px;
    flex-shrink: 0;
}

/* ============================================================
   .notion-seg — Notion-style segmented control (reusable primitive).

   Anatomy borrowed from Notion's view-type switcher and Linear's
   filter pills:
     • Outer container: pastel slate "track" with a 1-px subtle border
       and a 3-px inner padding. The inner padding creates the breathing
       room for the active "thumb" to sit cleanly inside.
     • Inner buttons: transparent base, tertiary text. Hover lifts text
       to secondary, no background change (so the eye stays on the
       active thumb).
     • Active "thumb": a white card with a soft 1-px-deep drop-shadow
       AND a hairline 0.5-px ring (achieved with a second box-shadow).
       Together they create the "raised pill on slate" effect that's
       Notion's signature for segmented controls.

   Use cases beyond language: any binary or 3-way preference where
   both/all states should be visible at once (e.g. data density,
   week-start, units). Drop in two .notion-seg-btn children, mark one
   .is-active, and you're done.
   ============================================================ */
.notion-seg {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    height: 36px;
    padding: 3px;
    background: var(--bg-input);              /* slate-50 track */
    border: 1px solid var(--border-subtle);   /* slate-100 hairline */
    border-radius: 10px;                       /* matches notion-input */
    flex-shrink: 0;
    margin: 0;                                 /* reset for <form> */
}
.notion-seg-btn {
    position: relative;
    height: 28px;
    padding: 0 14px;
    border: 0;
    background: transparent;
    color: var(--text-tertiary);
    border-radius: 7px;                        /* outer 10 - inner 3 = 7 */
    cursor: pointer;
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
    font-size: 12px;
    font-weight: var(--fw-medium);
    line-height: 1;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    transition: color 0.15s, background 0.15s, box-shadow 0.15s, transform 0.08s;
    -webkit-font-smoothing: antialiased;       /* keep small text crisp */
}
.notion-seg-btn:hover { color: var(--text-secondary); }
.notion-seg-btn.is-active {
    background: var(--bg-card);                /* white "thumb" */
    color: var(--text-primary);
    /* Layered shadow: subtle drop + hairline ring → the trademark
       Notion "soft elevation" (no blue accent, no harsh borders). */
    box-shadow:
        0 1px 2px rgba(15, 23, 42, 0.06),
        0 0 0 0.5px rgba(15, 23, 42, 0.04);
    cursor: default;                           /* it's already selected */
}
.notion-seg-btn.is-active:hover { color: var(--text-primary); }
.notion-seg-btn:not(.is-active):active { transform: scale(0.96); }
.notion-seg-btn:focus-visible {
    outline: 2px solid var(--status-blue-700);
    outline-offset: 2px;
    border-radius: 7px;
}

/* Topbar override: ensure the segmented control sits cleanly in the
   topbar action group with the same vertical center as the round
   theme toggle next to it. (Both end up at exactly 36 px.) */
.topbar-lang-seg { line-height: 0; }

/* Reduced motion: disable the press-scale on segments. */
@media (prefers-reduced-motion: reduce) {
    .notion-seg-btn,
    .notion-seg-btn:active { transform: none; transition: color 0.15s, background 0.15s, box-shadow 0.15s; }
}

/* Theme toggle (round icon button). The icon is swapped server-side
   on first paint and client-side after the user clicks; this rule
   only owns the chrome (size, hover, focus). The transform on :active
   keeps the press-feedback consistent with .bydate-btn / .dash-stat. */
.theme-toggle {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    border: none;
    background: transparent;
    color: var(--text-secondary);
    display: grid;
    place-items: center;
    cursor: pointer;
    font-size: 16px;
    flex-shrink: 0;
    transition: background 0.15s, color 0.15s, transform 0.1s;
}
.theme-toggle:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}
.theme-toggle:active { transform: scale(0.93); }
.theme-toggle:focus-visible {
    outline: 2px solid var(--status-blue-700);
    outline-offset: 2px;
}
.theme-toggle i {
    transition: transform 0.25s ease-out;
}
@media (prefers-reduced-motion: reduce) {
    .theme-toggle, .theme-toggle:active { transform: none; transition: none; }
    .theme-toggle i { transition: none; }
}

/* ============================================================
   Step 45 — Activity bell + dropdown panel.
   Bell reuses .theme-toggle's chrome (same 36×36 round button) and
   adds a small rose-coloured badge for unseen entries. The dropdown
   panel sits absolutely-positioned beneath the bell so it can escape
   any header overflow context.
   ============================================================ */
.activity-bell-wrap { position: relative; }
.activity-bell { position: relative; }
.activity-bell-badge {
    position: absolute;
    top: -2px;
    inset-inline-end: -2px;
    min-width: 16px; height: 16px; padding: 0 4px;
    background: var(--status-red-700);
    color: #fff;
    border: 2px solid var(--bg-card);
    border-radius: 999px;
    font-size: 10px; line-height: 12px;
    font-family: 'Inter', system-ui, sans-serif;
    font-weight: var(--fw-medium);
    display: grid; place-items: center;
    pointer-events: none;
}
.activity-bell-badge[hidden] { display: none; }

.activity-panel {
    position: absolute;
    top: calc(100% + 6px);
    inset-inline-end: 0;
    width: min(380px, calc(100vw - 32px));
    max-height: 70vh;
    background: var(--bg-card);
    border: 1px solid var(--border-default);
    border-radius: 12px;
    box-shadow: 0 8px 28px rgba(15, 23, 42, 0.12);
    z-index: 1200;
    overflow: hidden;
    display: flex; flex-direction: column;
}
.activity-panel[hidden] { display: none; }
.activity-panel-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 12px 14px;
    border-bottom: 1px solid var(--border-subtle);
    color: var(--text-primary);
    font-size: var(--fs-secondary);
}
.activity-panel-clear {
    background: transparent; border: 0;
    color: var(--text-tertiary);
    font-size: var(--fs-meta);
    cursor: pointer; padding: 4px 8px;
    border-radius: 6px;
}
.activity-panel-clear:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}
.activity-list {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;        /* iOS momentum scroll */
    max-height: calc(70vh - 50px);
    padding: 4px 0;
    overscroll-behavior: contain;             /* don't chain to page scroll */
}
.activity-empty {
    padding: 32px 16px;
    text-align: center;
    color: var(--text-tertiary);
    font-size: var(--fs-meta);
}
.activity-item {
    display: flex; align-items: flex-start; gap: 10px;
    padding: 10px 14px;
    border-bottom: 1px solid var(--border-subtle);
    transition: background 0.1s;
}
.activity-item:last-child { border-bottom: none; }
.activity-item:hover     { background: var(--bg-hover); }
.activity-item.is-unseen {
    background: var(--blue-50);
}
.activity-item.is-unseen:hover { background: var(--blue-50); }
.activity-item-icon {
    flex-shrink: 0;
    width: 28px; height: 28px;
    border-radius: 8px;
    display: grid; place-items: center;
    font-size: 14px;
    background: var(--bg-input);
    color: var(--text-secondary);
}
.activity-item.is-ok   .activity-item-icon { background: var(--green-50);  color: var(--status-green-700); }
.activity-item.is-err  .activity-item-icon { background: var(--red-50);    color: var(--status-red-700); }
.activity-item.is-warn .activity-item-icon { background: var(--amber-50);  color: var(--status-amber-700); }
.activity-item.is-info .activity-item-icon { background: var(--blue-50);   color: var(--status-blue-700); }
.activity-item-body { flex: 1; min-width: 0; }
.activity-item-msg {
    font-size: var(--fs-secondary);
    color: var(--text-primary);
    line-height: 1.35;
    word-wrap: break-word;
}
.activity-item-time {
    margin-top: 2px;
    font-size: var(--fs-meta);
    color: var(--text-tertiary);
}

/* ============================================================
   Step Notifications-A — bell tabs + notification-row variant.
   The bell panel now has two tabs:
     [Notifications]   server-backed (DB + SSE)
     [Activity]        client-side (toast history in localStorage)
   The activity-* primitives above are shared; rules below add the
   tab strip, the action footer, and a couple of severity colors
   specific to server notifications.
   ============================================================ */
.activity-tabs {
    display: flex;
    border-bottom: 1px solid var(--border-subtle);
    padding: 0 8px;
    background: var(--bg-card);
}
.activity-tab {
    flex: 1;
    border: 0;
    background: transparent;
    padding: 10px 8px;
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
    font-size: var(--fs-meta);
    color: var(--text-secondary);
    cursor: pointer;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    transition: color 0.1s, border-color 0.1s;
}
.activity-tab:hover { color: var(--text-primary); }
.activity-tab.active {
    color: var(--text-primary);
    font-weight: var(--fw-medium);
    border-bottom-color: var(--text-primary);
}
.activity-tab-count {
    display: inline-grid;
    place-items: center;
    min-width: 18px;
    height: 16px;
    padding: 0 5px;
    background: var(--bg-input);
    color: var(--text-tertiary);
    font-size: 10px;
    font-variant-numeric: tabular-nums;
    border-radius: 999px;
    line-height: 1;
}
.activity-tab.active .activity-tab-count {
    background: var(--blue-50);
    color: var(--status-blue-700);
}

/* Tab content swap — only one .activity-list is visible at a time. */
.activity-list[hidden] { display: none !important; }

/* Footer with "Mark all read" + "View all" actions for the server tab. */
.activity-panel-foot {
    border-top: 1px solid var(--border-subtle);
    padding: 8px 10px;
    display: flex;
    justify-content: space-between;
    gap: 8px;
}
.activity-panel-foot[hidden] { display: none; }
.activity-panel-foot button {
    background: transparent;
    border: 0;
    padding: 6px 10px;
    border-radius: 6px;
    color: var(--text-secondary);
    font-size: var(--fs-meta);
    cursor: pointer;
    transition: background 0.1s, color 0.1s;
}
.activity-panel-foot button:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}
.activity-panel-foot button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}
/* "View all" link — anchor inside the footer pointing to /Notifications.
   Blue tone so it reads as a navigation cue rather than a mute action. */
.activity-panel-foot-link {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 6px 10px;
    border-radius: 6px;
    color: var(--status-blue-700);
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    text-decoration: none;
    transition: background 0.1s;
}
.activity-panel-foot-link:hover {
    background: var(--bg-hover);
    color: var(--status-blue-700);
    text-decoration: none;
}
[dir="ltr"] .activity-panel-foot-link i.bi-arrow-left::before {
    content: "\f135";        /* arrow-right in LTR */
}

/* Server-side notification rows (in the Notifications tab) — use a
   dedicated marker class (.activity-item.is-srv) so we can style without
   colliding with toast-derived rows in the Activity tab. */
.activity-item.is-srv {
    cursor: default;     /* row click navigates to TargetUrl, not "select" */
    align-items: center;
}
.activity-item.is-srv.has-target { cursor: pointer; }
.activity-item.is-srv .activity-item-title {
    font-size: var(--fs-secondary);
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    line-height: 1.3;
}
.activity-item.is-srv .activity-item-body-text {
    margin-top: 2px;
    font-size: var(--fs-meta);
    color: var(--text-secondary);
    line-height: 1.4;
}
/* Severity tones — re-use the existing is-ok/is-err/is-warn/is-info icon
   chips. Severity 0 = info (blue), 1 = warn (amber), 2 = error (red),
   3 = critical (red + soft pulse). Mapping happens in JS. */
.activity-item.is-srv.is-critical .activity-item-icon {
    background: var(--red-50);
    color: var(--status-red-700);
    animation: activity-pulse 1.6s ease-in-out infinite;
}
@keyframes activity-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(220, 38, 38, 0.0); }
    50%      { box-shadow: 0 0 0 4px rgba(220, 38, 38, 0.15); }
}
@media (prefers-reduced-motion: reduce) {
    .activity-item.is-srv.is-critical .activity-item-icon { animation: none; }
}

/* Per-row dismiss button (×) — only visible on hover, server rows only. */
.activity-item.is-srv .activity-item-dismiss {
    flex-shrink: 0;
    width: 24px; height: 24px;
    border: 0;
    background: transparent;
    color: var(--text-tertiary);
    font-size: 12px;
    border-radius: 6px;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.1s, background 0.1s, color 0.1s;
}
.activity-item.is-srv:hover .activity-item-dismiss { opacity: 1; }
.activity-item.is-srv .activity-item-dismiss:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}


/* ============================================================
   MAIN content area
   Container max-width 1280px so on ultrawide monitors the page
   is centered rather than stretched edge-to-edge.
   ============================================================ */
.main {
    margin-inline-start: var(--sidebar-w);
    padding-top: calc(var(--topbar-h) + var(--content-pad));
    padding-inline: var(--content-pad);
    padding-bottom: var(--content-pad);
    min-height: 100vh;
}
.main-container {
    max-width: var(--container-max);
    margin: 0 auto;
}


/* ============================================================
   RESPONSIVE
   Three breakpoints:
   • ≥1024px     desktop full sidebar (240px) + content (max 1280)
   • 768-1023    tablet — sidebar collapses to 64px icons-only
   • <768        mobile — sidebar hidden, hamburger drawer
   ============================================================ */

/* ----- Tablet (icons-only sidebar) ----- */
@media (min-width: 768px) and (max-width: 1023.98px) {
    :root { --sidebar-w: var(--sidebar-w-collapsed); }

    .sidebar { width: var(--sidebar-w-collapsed); }
    .sidebar-brand {
        padding: 16px 8px;
        align-items: center;
    }
    .sidebar-brand .brand-name { display: none; }
    /* Step 52 — shrink the logo in the collapsed rail so it fits the
       64px width without clipping. */
    .sidebar-brand .brand-logo { height: 40px; }
    .brand-pill {
        padding: 4px;
        gap: 0;
        width: 32px;
        height: 32px;
        justify-content: center;
        font-size: 0;        /* hides the "Attend" text but keeps the pill shape */
    }
    .brand-pill i { font-size: 14px; }

    .nav-group-label { display: none; }
    .nav-list { margin: 0 6px; }
    .nav-item {
        justify-content: center;
        padding: 10px 0;
        gap: 0;
    }
    .nav-item span,
    .nav-item .chevron,
    .nav-item .nav-hint { display: none; }
    /* In icon-only mode, the lime dot would crowd the icon. Use a soft tint
       instead. The new ::before lime dot is hidden the same way the old
       ::after one was. */
    .nav-item.active::before,
    .nav-item.active::after { display: none; }
    .nav-item.active {
        background: var(--lime-soft);
    }
    .nav-sub { display: none; }                    /* sub-items invisible — full sidebar mode brings them back */
    /* Badge stays visible: it carries critical info (offline devices). Reposition
       it as a small floating dot in the corner so it doesn't overflow the 64px
       icon column. */
    .nav-item .nav-badge {
        position: absolute;
        top: 2px;
        inset-inline-end: 2px;
        margin: 0;
        padding: 1px 5px;
        font-size: 9px;
        min-width: 14px;
    }
    .nav-separator { margin: 8px 8px 4px; }

    .sidebar-account { padding: 8px 6px; }
    .account-card {
        padding: 6px;
        justify-content: center;
        gap: 0;
    }
    .account-info,
    .account-card > i:last-child { display: none; }
    .logout-btn {
        justify-content: center;
        padding: 8px 0;
        gap: 0;
    }
    .logout-btn span { display: none; }
}

/* ----- Mobile (offcanvas drawer) ----- */
@media (max-width: 767.98px) {

    /* Bootstrap's offcanvas-md class kicks in here automatically, hiding the
       sidebar offscreen and showing it via the hamburger button. We just
       widen it slightly and make sure the topbar + main adjust. */
    .sidebar.offcanvas-md {
        width: 280px;
    }

    .topbar {
        inset-inline-start: 0;
        padding-inline: 16px;
        gap: 12px;
    }
    .topbar-hamburger { display: grid; }

    .topbar-search { max-width: none; }
    .topbar-search input { height: 36px; }

    .lang-switch { padding: 2px; }
    .lang-pill { padding: 5px 10px; font-size: var(--fs-meta); }

    .main {
        margin-inline-start: 0;
        padding-inline: 16px;
        padding-top: calc(var(--topbar-h) + 16px);
        padding-bottom: 16px;
    }

    /* Below 480: hide search bar entirely (admin can use it after opening
       a list page where filtering matters more). */
    @media (max-width: 479.98px) {
        .topbar-search { display: none; }
    }
}

/* ----- Below container width: stop centering, use full bleed ----- */
@media (max-width: 1279.98px) {
    .main-container { max-width: none; }
}


/* ============================================================
   DASHBOARD
   Greeting header → 30-day calendar strip → 4 stats (1 hero) →
   2×2 grid of list panels (Devices / Commands / Punches / Activity).
   All cards share the same .dash-panel base; the hero stat card
   uses .is-hero modifier for the lime soft tint.
   ============================================================ */

/* ----- Greeting header ----- */
.dash-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 24px;
}
.dash-greeting {
    display: flex;
    align-items: center;
    gap: 12px;
}
.dash-greeting .account-avatar {
    width: 40px;
    height: 40px;
    background: var(--lime-soft);
    font-size: 15px;
}
.dash-greeting-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    line-height: 1.2;
}
.dash-greeting-eyebrow {
    font-size: var(--fs-secondary);
    color: var(--text-tertiary);
}
.dash-greeting-name {
    font-size: 18px;
    font-weight: var(--fw-semibold);
    color: var(--text-primary);
    letter-spacing: -0.01em;
}
.dash-bell {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 1px solid var(--border-default);
    background: var(--bg-card);
    color: var(--text-secondary);
    display: grid;
    place-items: center;
    cursor: pointer;
    font-size: 16px;
    position: relative;
}
.dash-bell:hover { background: var(--bg-hover); color: var(--text-primary); }
.dash-bell .bell-dot {
    position: absolute;
    top: 8px;
    inset-inline-end: 8px;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--status-red-700);
    border: 2px solid var(--bg-card);
}

/* ----- 30-day calendar strip ----- */
.dash-calendar {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 24px;
}
.dash-cal-month {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
}
.dash-cal-month-name {
    font-size: var(--fs-card-title);
    font-weight: var(--fw-medium);
    color: var(--text-primary);
}
.dash-cal-nav {
    width: 28px;
    height: 28px;
    border: 1px solid var(--border-default);
    border-radius: 50%;
    background: var(--bg-card);
    color: var(--text-secondary);
    display: grid;
    place-items: center;
    cursor: pointer;
    font-size: 12px;
}
.dash-cal-nav:hover { background: var(--bg-hover); color: var(--text-primary); }
.dash-cal-strip {
    flex: 1;
    display: flex;
    gap: 4px;
    overflow-x: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--border-default) transparent;
    padding-bottom: 4px;
}
.dash-cal-strip::-webkit-scrollbar { height: 4px; }
.dash-cal-strip::-webkit-scrollbar-thumb { background: var(--border-default); border-radius: 2px; }

.dash-cal-day {
    flex-shrink: 0;
    width: 44px;
    min-height: 56px;
    border-radius: var(--r-control);
    border: none;
    background: transparent;
    color: var(--text-primary);
    font-family: 'Inter', sans-serif;
    font-size: 14px;
    font-weight: var(--fw-medium);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    cursor: pointer;
    line-height: 1.2;
    position: relative;
}
.dash-cal-day:hover { background: var(--bg-hover); }
.dash-cal-day .dow {
    font-size: var(--fs-meta);
    font-weight: var(--fw-regular);
    color: var(--text-tertiary);
    font-family: 'Cairo', 'Inter', sans-serif;     /* day-of-week is text, use Arabic font */
}
.dash-cal-day.today {
    background: var(--lime);
    color: var(--text-primary);
}
.dash-cal-day.today .dow { color: var(--text-primary); }
.dash-cal-day.future { color: var(--text-placeholder); }
.dash-cal-day.future .dow { color: var(--text-placeholder); }
/* Optional: amber dot under the number when an exception/holiday falls on that day */
.dash-cal-day.has-event::after {
    content: "";
    position: absolute;
    bottom: 6px;
    left: 50%;
    transform: translateX(-50%);
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background: var(--status-amber-700);
}

/* ----- Two-column dashboard layout -----
   Main column (1fr) holds the stats row + a 2-panel grid below.
   Right rail (320px) hosts the Live Punches column.

   The whole layout is sized to fill the visible viewport so that all
   three list columns (Device Status / Live Activity / Live Punches)
   share the same bottom edge with no outer-page scroll.
   Math:  viewport height
        − topbar (60)
        − content-area top padding (32)
        − content-area bottom padding (32)
        = 100vh − 124px available for the dashboard.

   Below the lg breakpoint the rail drops underneath the main column,
   becoming a third row instead of a side column. */
.dash-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 320px;
    gap: var(--gap-cards);
    align-items: stretch;
    /* Lock the layout to EXACTLY one viewport-height (minus the topbar
       + main padding). The previous `min-height` let the page grow
       past the viewport when a panel had many rows, so scrolling
       happened at the page level. By switching to `height`, the
       constraint cascades down to the panels: each .dash-list inside
       a panel has `flex: 1; overflow-y: auto;` which now scrolls
       INTERNALLY because its parent has a real, capped height.

       Both `height` AND `max-height` are set explicitly — some browsers
       treat `height` as a target on grid containers and let children
       push past it; the `max-height` keeps that path closed. The
       `overflow: hidden` is the belt-and-suspenders guard that ensures
       no descendant can spill out and trigger the page scrollbar. */
    height:     calc(100vh - var(--topbar-h) - var(--content-pad) * 2);
    max-height: calc(100vh - var(--topbar-h) - var(--content-pad) * 2);
    overflow: hidden;
}
.dash-main {
    display: flex;
    flex-direction: column;
    gap: var(--gap-cards);
    min-width: 0;
    min-height: 0;             /* allow column to shrink past content */
    overflow: hidden;          /* clip any rogue growth from children  */
}
.dash-rail {
    display: flex;
    flex-direction: column;
    /* Stretch to match the main column; child panel fills via flex */
    height: 100%;
    min-height: 0;
    overflow: hidden;
}
@media (max-width: 1023.98px) {
    .dash-layout {
        grid-template-columns: 1fr;
        /* Stacked layout — let the page grow naturally so the user
           can scroll between sections instead of being forced into
           three tiny scroll wells on a phone. */
        height: auto;
        min-height: 0;
    }
    .dash-rail { height: auto; }
}

/* ----- Stats grid (3 KPI cards: Attendance hero / Devices / Activity) -----
   Each card has a pastel BACKGROUND (not just an icon chip) so the row
   reads as three distinct moods at a glance. Icon chip on top of the
   pastel uses a translucent white tile so the icon stays prominent. */
.dash-stats {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--gap-cards);
}
@media (max-width: 1023.98px) { .dash-stats { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 767.98px)  { .dash-stats { grid-template-columns: 1fr; } }

/* ============================================================
   Step 47 — Dashboard Region/Branch filter bar.

   Sits above .dash-stats. Two compact selects + a passive scope
   chip on the inline-end side that reads "All locations" /
   "Baghdad · Karada Office" etc. so the operator always sees
   which slice the KPIs reflect. Visually quiet — no shadow, no
   pastel fill — because it's a control row, not a metric.
   ============================================================ */
.dash-filter {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    background: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-radius: var(--r-card-sm);
    padding: 10px 14px;
    margin-bottom: var(--gap-cards);
}
.dash-filter-group {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}
.dash-filter-label {
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    color: var(--text-tertiary);
    line-height: 1;
    white-space: nowrap;
}
.dash-filter-select {
    height: 34px;
    min-width: 160px;
    background: var(--bg-card);
    border: 1px solid var(--border-default);
    border-radius: var(--r-tile);
    padding: 0 28px 0 12px;
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
    font-size: var(--fs-secondary);
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s;
    /* Drop the native caret and draw our own so it sits on the inline-end
       side regardless of writing direction. */
    appearance: none;
    -webkit-appearance: none;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'><path fill='%2364748B' d='M5 7L1 3h8z'/></svg>");
    background-repeat: no-repeat;
    background-position: right 10px center;
    background-size: 10px 10px;
}
[dir="rtl"] .dash-filter-select {
    padding: 0 12px 0 28px;
    background-position: left 10px center;
}
.dash-filter-select:hover     { border-color: #CBD5E1; }
.dash-filter-select:focus     { outline: none; border-color: var(--status-blue-700); background: var(--bg-card); }
.dash-filter-select:disabled  { opacity: 0.6; cursor: not-allowed; }

/* Passive "what's the current scope" chip on the trailing side. Reads
   "All locations" by default, then "Region · Branch" when one or both
   filters are set. Pure read-only — operators glance at it for
   confidence, they don't click it. */
.dash-filter-scope {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-inline-start: auto;
    padding: 5px 10px;
    background: var(--bg-input);
    border-radius: var(--r-tile);
    color: var(--text-secondary);
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    line-height: 1;
    white-space: nowrap;
}
.dash-filter-scope i { font-size: 12px; opacity: 0.8; }
.dash-filter-scope.is-active {
    background: var(--blue-50);
    color: var(--status-blue-700);
}
[data-bs-theme="dark"] .dash-filter-scope.is-active {
    background: rgba(59, 130, 246, 0.16);
    color: #93C5FD;
}

@media (max-width: 575.98px) {
    .dash-filter { padding: 10px; }
    .dash-filter-group { width: 100%; }
    .dash-filter-select { flex: 1; min-width: 0; }
    .dash-filter-scope { margin-inline-start: 0; width: 100%; justify-content: center; }
}

@media print { .dash-filter { display: none; } }

.dash-stat {
    background: var(--bg-card);
    border: 1px solid var(--border-default);
    border-radius: var(--r-card);
    padding: 14px 16px;
    text-decoration: none;
    color: var(--text-primary);
    display: flex;
    align-items: flex-start;
    gap: 12px;
    min-height: 92px;
    transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
.dash-stat:hover {
    color: var(--text-primary);
    border-color: var(--text-placeholder);
    transform: translateY(-1px);
}

/* Icon chip — 36×36 pastel tile, colored per tone */
.dash-stat-icon {
    width: 36px;
    height: 36px;
    flex-shrink: 0;
    border-radius: var(--r-tile);
    display: grid;
    place-items: center;
    font-size: 16px;
    background: var(--bg-input);
    color: var(--text-secondary);
}

/* Body column (label / value / meta stacked) */
.dash-stat-body {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    flex: 1;
}
.dash-stat-label {
    font-size: var(--fs-secondary);
    color: var(--text-secondary);
    line-height: 1.2;
}
.dash-stat-value {
    font-family: 'Inter', sans-serif;
    font-size: 28px;
    font-weight: var(--fw-medium);
    line-height: 1.1;
    letter-spacing: -0.02em;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
    display: flex;
    align-items: baseline;
    gap: 2px;
    flex-wrap: wrap;
    margin: 2px 0;
}
.dash-stat-value .denom {
    color: var(--text-tertiary);
    font-size: 16px;
    font-weight: var(--fw-regular);
}
.dash-stat-meta {
    font-size: var(--fs-meta);
    color: var(--text-tertiary);
    line-height: 1.3;
}
.dash-stat-meta.up    { color: var(--status-green-700); }
.dash-stat-meta.down  { color: var(--status-red-700); }
.dash-stat-meta.warn  { color: var(--status-amber-700); }

/* Color tones — each tone has a full pastel BACKGROUND, paired with a
   border ONE STEP DARKER in the same hue (so the card edge is clearly
   visible without the heavy slate look). The icon chip is a translucent
   white tile inside, with the saturated -700 of the same tone for the
   icon glyph itself. */
.dash-stat[data-tone="blue"]   { background: var(--blue-50);   border-color: #BFDBFE; }   /* blue-200  */
.dash-stat[data-tone="purple"] { background: var(--purple-50); border-color: #DDD6FE; }   /* purple-200*/
.dash-stat[data-tone="amber"]  { background: var(--amber-50);  border-color: #FDE68A; }   /* amber-200 */
.dash-stat[data-tone="teal"]   { background: var(--teal-50);   border-color: #99F6E4; }   /* teal-200  */
.dash-stat[data-tone="green"]  { background: var(--green-50);  border-color: #BBF7D0; }   /* green-200 */
/* Step 36 — red tone added for the "over-threshold" KPI on the
   Lateness report. Same recipe as the other tones: red-50 fill,
   red-200 border, red-700 icon. */
.dash-stat[data-tone="red"]    { background: var(--red-50);    border-color: #FECACA; }   /* red-200    */

.dash-stat[data-tone] .dash-stat-icon {
    background: rgba(255, 255, 255, 0.7);
}
.dash-stat[data-tone="blue"]   .dash-stat-icon { color: var(--status-blue-700); }
.dash-stat[data-tone="purple"] .dash-stat-icon { color: var(--status-purple-700); }
.dash-stat[data-tone="amber"]  .dash-stat-icon { color: var(--status-amber-700); }
.dash-stat[data-tone="teal"]   .dash-stat-icon { color: var(--status-teal-700); }
.dash-stat[data-tone="green"]  .dash-stat-icon { color: var(--status-green-700); }
.dash-stat[data-tone="red"]    .dash-stat-icon { color: var(--status-red-700); }

/* Hover: deepen the same-family border one more step for tactile feedback */
.dash-stat[data-tone]:hover,
.dash-stat.is-hero:hover {
    border-color: rgba(15, 23, 42, 0.18);
}

/* Hero (Attendance Today) — lime soft full-card bg, with a darker lime
   border one step down from the soft fill. */
.dash-stat.is-hero {
    background: var(--lime-soft);
    border-color: #D9F99D;            /* lime-200 — deeper than the soft fill */
}
.dash-stat.is-hero .dash-stat-icon {
    background: rgba(255, 255, 255, 0.6);
    color: var(--text-primary);
}

/* ----- Panel grid (Device Status + Live Activity, side by side) -----
   Stretches to fill the remaining height in the main column so its
   bottom edge aligns with the rail's bottom edge. */
.dash-grid {
    display: grid;
    grid-template-columns: 1.4fr 1fr;
    gap: var(--gap-cards);
    flex: 1;          /* fill remaining space below the stats row */
    min-height: 0;    /* allow grid items to shrink past their content height */
    overflow: hidden; /* clip — children manage their own scroll */
}
@media (max-width: 1023.98px) {
    .dash-grid { grid-template-columns: 1fr; flex: none; overflow: visible; }
}

/* ----- Panel (notion-style card with title + count + cta + scrolling list)
   Borders strengthened to slate-300 so the white card has a clearly visible
   edge on the slate-50 page background. The panel itself is a flex column
   that fills its grid cell; only the .dash-list inside scrolls. */
.dash-panel {
    background: var(--bg-card);
    border: 1px solid #CBD5E1;        /* slate-300 — stronger edge than the default */
    border-radius: var(--r-card);
    padding: 16px 18px;
    display: flex;
    flex-direction: column;
    min-height: 0;                    /* allow flex/grid to shrink without jumping */
    overflow: hidden;                 /* belt-and-suspenders — list manages its own */
}

/* Tall variant for the right-rail Live Punches column.
   Fills the rail's full stretched height. */
.dash-panel-tall {
    height: 100%;
    min-height: 0;
}
@media (max-width: 1023.98px) {
    /* Stacked layout: give all panels a sensible minimum so they don't
       collapse to the row count when there are few items. */
    .dash-panel       { min-height: 280px; }
    .dash-panel-tall  { min-height: 320px; }
}
.dash-panel-header {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 10px;
}
.dash-panel-icon {
    width: 28px;
    height: 28px;
    border-radius: var(--r-tile);
    display: grid;
    place-items: center;
    background: var(--bg-input);
    color: var(--text-secondary);
    font-size: 14px;
}
.dash-panel-title {
    font-size: var(--fs-card-title);
    font-weight: var(--fw-medium);
    color: var(--text-primary);
}
.dash-panel-count {
    background: var(--bg-input);
    color: var(--text-secondary);
    border-radius: var(--r-pill);
    padding: 2px 10px;
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    min-width: 24px;
    text-align: center;
}
/* Tone variants — pick a soft chip color matching the panel's semantic.
   Used by the dashboard's live device panels to call out interesting
   counts (e.g. "online user count") without scattering inline styles. */
.dash-panel-count.is-info  { background: var(--blue-50);  color: var(--status-blue-700); }
.dash-panel-count.is-success { background: var(--green-50); color: var(--status-green-700); }
.dash-panel-count.is-warn  { background: var(--amber-50); color: var(--status-amber-700); }
.dash-panel-count.is-danger{ background: var(--red-50);   color: var(--status-red-700); }
.dash-panel-cta {
    margin-inline-start: auto;
    color: var(--text-tertiary);
    font-size: var(--fs-secondary);
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 8px;
    border-radius: var(--r-tile);
    /* Step 46 — gap widens on hover so the arrow appears to "lean
       forward" toward the destination. Direction-agnostic — works
       in both LTR and RTL without per-direction overrides. */
    transition: gap 0.15s ease, color 0.1s, background 0.1s;
}
.dash-panel-cta:hover { color: var(--text-primary); background: var(--bg-hover); gap: 8px; }

.dash-list {
    list-style: none;
    padding: 0;
    margin: 0 -18px;                  /* let rows extend to card edges (matches panel padding) */
    flex: 1;
    min-height: 0;                    /* allow flex item to shrink, enabling overflow */
    overflow-y: auto;
    overflow-x: hidden;
    /* Subtle scrollbar — matches .bydate-scroll-host. Keeps the chrome
       quiet so the eye stays on the row content. */
    scrollbar-width: thin;
    scrollbar-color: var(--border-default) transparent;
}
.dash-list::-webkit-scrollbar { width: 8px; }
.dash-list::-webkit-scrollbar-thumb {
    background: var(--border-default);
    border-radius: 4px;
}
.dash-list::-webkit-scrollbar-track { background: transparent; }
.dash-list-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 18px;
    border-bottom: 1px solid var(--border-subtle);
}
.dash-list-row:last-child { border-bottom: none; }
.dash-list-empty {
    padding: 24px 16px;
    text-align: center;
    color: var(--text-placeholder);
    font-size: var(--fs-secondary);
}

/* Punch row (with photo / fallback) */
.punch-photo {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    object-fit: cover;
    background: var(--bg-input);
    flex-shrink: 0;
}
.punch-fallback {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--bg-input);
    color: var(--text-placeholder);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 16px;
    flex-shrink: 0;
}

/* Activity row */
.act-icon {
    width: 32px;
    height: 32px;
    border-radius: var(--r-tile);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    flex-shrink: 0;
}

/* Status pill (used in dashboard rows for online/idle/offline) */
.dash-status-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    padding: 3px 10px;
    border-radius: var(--r-pill);
    background: var(--bg-input);
    color: var(--text-secondary);
}
.dash-status-pill.ok      { background: var(--green-50); color: var(--status-green-700); }
.dash-status-pill.warn    { background: var(--amber-50); color: var(--status-amber-700); }
.dash-status-pill.bad     { background: var(--red-50);   color: var(--status-red-700); }
.dash-status-pill .dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    box-shadow: none;     /* override the global .dot pulse halo */
    margin: 0;
}

/* ============================================================
   Step 46 — Microinteractions (Dashboard-focused, applied where
   semantically appropriate elsewhere too).

   Five effects, each tied to a button's purpose:
     • press-scale (0.97)   feedback for the click action
     • CTA gap-widen        "lean forward" toward the destination
     • new-punch highlight  amber flash on freshly-arrived rows
     • receiving-pulse      live activity heartbeat for active devices
     • count-up             handled by JS (ZkPush.countUp), this
                            file just declares the @media reduce-motion
                            override that JS reads via matchMedia.
   ============================================================ */

/* (1) Press scale — tactile feedback on every clickable surface.
   Uses :active so it ONLY fires while the mouse button is down,
   not after; prevents the "stuck-shrunken" feel. */
.dash-stat,
.bydate-btn,
.settings-card,
.dash-panel-cta {
    transition: transform 0.06s ease-out;
}
.dash-stat:active,
.bydate-btn:not(:disabled):active,
.settings-card:active,
.dash-panel-cta:active {
    transform: scale(0.97);
}
/* The dash-stat already has translateY(-1px) on hover; compose. */
.dash-stat:hover:active { transform: translateY(0) scale(0.97); }

/* (2) Receiving-pulse — soft "ripple-out" glow on the green ok pill
   that means "data is being uploaded right now". Visually communicates
   live activity without being noisy (a single ripple every 1.6s). */
.dash-status-pill.ok {
    animation: receivingPulse 1.6s ease-in-out infinite;
}
@keyframes receivingPulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.30); }
    50%      { box-shadow: 0 0 0 6px rgba(16, 185, 129, 0); }
}

/* (3) New-punch highlight — when a fresh row appears in the Live
   Punches list, JS adds .is-new and we play a 1.5s amber-fade-in.
   The translate is small (-4px) so the row "drops into place" rather
   than scrolling around. */
.dash-list-row.is-new {
    animation: newPunchHighlight 1.5s ease-out;
}
@keyframes newPunchHighlight {
    0% {
        background: var(--amber-50);
        transform: translateY(-4px);
    }
    100% {
        background: transparent;
        transform: translateY(0);
    }
}

/* (4) Reduced-motion overrides — anyone who's set their OS preference
   to "reduce motion" gets static behavior. Pulses stop, transitions
   collapse to instant, animations are removed entirely. JS honors the
   same flag for countUp(). */
@media (prefers-reduced-motion: reduce) {
    .dash-status-pill.ok,
    .dash-list-row.is-new {
        animation: none !important;
    }
    .dash-stat,
    .bydate-btn,
    .settings-card,
    .dash-panel-cta {
        transition: none !important;
    }
    .dash-stat:active,
    .bydate-btn:not(:disabled):active,
    .settings-card:active,
    .dash-panel-cta:active {
        transform: none !important;
    }
}

/* ============================================================
   REPORT BY DATE — unified daily + range page
   Notion-styled cards (filter / toolbar / table) on a slate-50
   page background. RTL-primary; Inter for numerics, Cairo for
   Arabic glyphs. Reuses tokens from the design-system block at
   the top of the file. The few extra ramp colors needed by chips
   and badges (slate-100/600/700/900 and -100/-200 of each status
   hue) live as ad-hoc literals here rather than expanding the
   token block, since they are scoped to this page.
   ============================================================ */

/* ---- Page header (breadcrumb + title) ---- */
.bydate-header {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-bottom: 24px;
}
.bydate-crumb {
    font-size: var(--fs-secondary);
    color: var(--text-placeholder);
    line-height: 1;
}
.bydate-title {
    font-size: var(--fs-page-title);
    font-weight: var(--fw-semibold);
    color: var(--text-primary);
    line-height: 1.15;
    letter-spacing: -0.015em;
    margin: 0;
}

/* ---- Card shell shared by filter + toolbar + table cards ---- */
.bydate-card {
    background: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-radius: var(--r-card);
    padding: 18px 20px;
    margin-bottom: 16px;
}
.bydate-card.no-pad { padding: 0; }     /* table card uses 0 (its own header/cells handle padding) */

/* ============================================================
   DATE FILTER CARD
   Mode badge (left) + 3 inputs (preset, from, to) right-aligned.
   Each input has a tiny 11px slate-400 label above; inputs and
   badge bottom-edges are flush.
   ============================================================ */
.bydate-filter {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}
.bydate-filter-group {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}
.bydate-filter-label {
    font-size: var(--fs-meta);
    font-weight: var(--fw-regular);
    color: var(--text-placeholder);
    line-height: 1;
}
.bydate-input {
    width: 220px;
    height: 42px;
    background: var(--bg-card);
    border: 1px solid var(--border-default);
    border-radius: 10px;
    padding: 0 14px;
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
    font-size: var(--fs-body);
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    transition: border-color 0.15s, background 0.15s;
}
.bydate-input:hover { border-color: #CBD5E1; }
.bydate-input:focus {
    outline: none;
    border-color: var(--text-primary);
    background: var(--bg-card);
}
/* Native date pickers — make their text feel right inside our shell */
.bydate-input[type="date"] {
    font-family: 'Inter', system-ui, sans-serif;
    color: var(--text-primary);
}
.bydate-input[type="date"]::-webkit-calendar-picker-indicator { cursor: pointer; opacity: 0.55; }

/* Mode badge (blue50 + blue700) */
.bydate-mode-badge {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    background: var(--blue-50);
    color: var(--status-blue-700);
    padding: 8px 12px;
    border-radius: 8px;
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
    font-size: 12px;
    font-weight: var(--fw-medium);
    height: 42px;            /* match the input height for flush bottom */
    line-height: 1;
    flex-shrink: 0;
    white-space: nowrap;
}
.bydate-mode-badge i { font-size: 14px; }

/* ============================================================
   TOOLBAR (two stacked rows)
   Row 1: search (left in RTL = inline-end) + filter chips (right)
   Row 2: count text (left) + button group (right)
   ============================================================ */
.bydate-toolbar {
    display: flex;
    flex-direction: column;
    gap: 14px;
}
.bydate-toolbar-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}

/* Search input */
.bydate-search {
    position: relative;
    width: 280px;
    max-width: 100%;
}
.bydate-search input {
    width: 100%;
    height: 38px;
    background: var(--bg-hover);
    border: 1px solid transparent;
    border-radius: 10px;
    padding-inline-start: 14px;
    padding-inline-end: 38px;
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
    font-size: var(--fs-secondary);
    color: var(--text-primary);
    transition: background 0.15s, border-color 0.15s;
}
.bydate-search input::placeholder { color: var(--text-placeholder); }
.bydate-search input:focus {
    outline: none;
    background: var(--bg-card);
    border-color: var(--border-default);
}
.bydate-search-icon {
    position: absolute;
    inset-inline-end: 14px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-placeholder);
    font-size: 16px;
    pointer-events: none;
}

/* Filter chips — pill with inner count pill */
.bydate-chips {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}
.bydate-chip {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    /* DOM order is [label, count] — label appears at inline-start (right in
       RTL, left in LTR), count at inline-end. padding-inline-start gets the
       larger value so the label has air around it; the count pill is flush
       against the chip's inline-end edge with just 5px of inset. */
    padding-block: 5px;
    padding-inline-start: 12px;
    padding-inline-end: 5px;
    border-radius: var(--r-pill);
    background: var(--bg-card);
    border: none;
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
    font-size: var(--fs-secondary);
    font-weight: var(--fw-medium);
    color: var(--text-secondary);
    cursor: pointer;
    line-height: 1;
    white-space: nowrap;
    transition: filter 0.15s;
}
.bydate-chip:hover { filter: brightness(0.97); }
.bydate-chip-count {
    background: var(--bg-input);
    color: var(--text-secondary);
    font-family: 'Inter', system-ui, sans-serif;
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    padding: 3px 9px;
    border-radius: var(--r-pill);
    line-height: 1;
    font-variant-numeric: tabular-nums;
    min-width: 20px;
    text-align: center;
}

/* Per-status colors (chip bg / inner pill bg / label+count text). Slate-700 and
   slate-900 are inlined here since the global token block doesn't expose them. */
.bydate-chip[data-status="all"]      { background: #0F172A; color: #FFFFFF; }
.bydate-chip[data-status="all"] .bydate-chip-count {
    background: #334155;            /* slate-700 — dark active inner pill */
    color: #FFFFFF;
}
.bydate-chip[data-status="present"]  { background: var(--green-50); color: var(--status-green-700); }
.bydate-chip[data-status="present"] .bydate-chip-count {
    background: #D1FAE5;            /* green-100 */
    color: var(--status-green-700);
}
.bydate-chip[data-status="absent"]   { background: var(--red-50); color: var(--status-red-700); }
.bydate-chip[data-status="absent"] .bydate-chip-count {
    background: #FEE2E2;            /* red-100 */
    color: var(--status-red-700);
}
.bydate-chip[data-status="missing"]  { background: var(--amber-50); color: var(--status-amber-700); }
.bydate-chip[data-status="missing"] .bydate-chip-count {
    background: #FEF3C7;            /* amber-100 */
    color: var(--status-amber-700);
}
.bydate-chip[data-status="leave"]    { background: var(--blue-50); color: var(--status-blue-700); }
.bydate-chip[data-status="leave"] .bydate-chip-count {
    background: #DBEAFE;            /* blue-100 */
    color: var(--status-blue-700);
}
/* Inactive chip — softened so the active one pops. We keep colors but
   reduce saturation by subtle opacity; clicking restores. */
.bydate-chip:not(.is-active):not([data-status="all"]) { opacity: 0.85; }
.bydate-chip.is-active { box-shadow: inset 0 0 0 1px rgba(15, 23, 42, 0.08); }

/* --- Command-queue chip statuses (used by /Commands filter row) --- */
.bydate-chip[data-status="pending"] { background: var(--bg-input); color: var(--text-secondary); }
.bydate-chip[data-status="pending"] .bydate-chip-count {
    background: #E2E8F0;            /* slate-200 */
    color: var(--text-secondary);
}
.bydate-chip[data-status="sent"]    { background: #ECFEFF; color: #0E7490; }  /* cyan-50 / cyan-700 */
.bydate-chip[data-status="sent"] .bydate-chip-count {
    background: #CFFAFE;            /* cyan-100 */
    color: #0E7490;
}
.bydate-chip[data-status="acked"]   { background: var(--green-50); color: var(--status-green-700); }
.bydate-chip[data-status="acked"] .bydate-chip-count {
    background: #D1FAE5;            /* green-100 */
    color: var(--status-green-700);
}
.bydate-chip[data-status="failed"]  { background: var(--red-50); color: var(--status-red-700); }
.bydate-chip[data-status="failed"] .bydate-chip-count {
    background: #FEE2E2;            /* red-100 */
    color: var(--status-red-700);
}
.bydate-chip[data-status="stuck"]   { background: var(--amber-50); color: var(--status-amber-700); }
.bydate-chip[data-status="stuck"] .bydate-chip-count {
    background: #FEF3C7;            /* amber-100 */
    color: var(--status-amber-700);
}

/* Count text in row 2 */
.bydate-count-text {
    font-size: 12px;
    color: var(--text-placeholder);
    line-height: 1;
}

/* Toolbar button group — secondary buttons + columns/group dropdowns */
.bydate-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    position: relative;
}
.bydate-btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 36px;
    padding: 0 14px;
    background: var(--bg-card);
    border: 1px solid var(--border-default);
    border-radius: 10px;
    color: var(--text-primary);
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
    font-size: var(--fs-secondary);
    font-weight: var(--fw-medium);
    cursor: pointer;
    line-height: 1;
    text-decoration: none;
    white-space: nowrap;
    transition: background 0.15s, border-color 0.15s;
}
.bydate-btn:hover { background: var(--bg-hover); border-color: #CBD5E1; }
.bydate-btn:focus-visible { outline: 2px solid var(--status-blue-700); outline-offset: 2px; }

/* Tonal variants — replace the scattered inline `style="color:var(--status-X-700);
   border-color:#XXX;"` hints with named classes. Same neutral background as
   the default button so they don't dominate; only the text + border carry
   the tone. Hover deepens the border slightly. */
.bydate-btn.is-danger {
    color: var(--status-red-700);
    border-color: #FECACA;       /* red-200 */
}
.bydate-btn.is-danger:hover {
    background: var(--red-50);
    border-color: #FCA5A5;       /* red-300 */
}

/* Filled destructive variant — solid red background, white text. Used for
   the rare "this is a one-way action" CTA (Factory Reset, Permanent delete).
   The visual weight of a filled red button matches its irreversible impact;
   the outlined .is-danger is right for everyday "delete" buttons in tables. */
.bydate-btn.is-danger-fill {
    background: var(--status-red-700);
    color: #FFFFFF;
    border-color: var(--status-red-700);
}
.bydate-btn.is-danger-fill:hover {
    background: #B91C1C;         /* red-700 darker */
    border-color: #B91C1C;
    color: #FFFFFF;
}
.bydate-btn.is-danger-fill:disabled,
.bydate-btn.is-danger-fill[disabled] {
    background: #FCA5A5;         /* red-300 — washes out clearly */
    border-color: #FCA5A5;
    color: #FFFFFF;
    cursor: not-allowed;
}

.bydate-btn.is-warn {
    color: var(--status-amber-700);
    border-color: #FDE68A;       /* amber-200 */
}
.bydate-btn.is-warn:hover {
    background: var(--amber-50);
    border-color: #FCD34D;       /* amber-300 */
}

.bydate-btn.is-info {
    color: var(--status-blue-700);
    border-color: #BFDBFE;       /* blue-200 */
}
.bydate-btn.is-info:hover {
    background: var(--blue-50);
    border-color: #93C5FD;       /* blue-300 */
}
/* Disabled state — used by the always-visible /Devices control bar so the
   buttons stay laid out but read as "not actionable" until a device is
   selected. We override any inline color/border (e.g. amber on Reboot,
   red on Clear) with a uniform muted slate so the bar reads as "all off"
   at a glance. */
.bydate-btn:disabled,
.bydate-btn[disabled] {
    cursor: not-allowed;
    opacity: 0.55;
    filter: grayscale(0.4);
}
.bydate-btn:disabled:hover,
.bydate-btn[disabled]:hover {
    background: inherit;
    border-color: inherit;
}
.bydate-btn i { font-size: 14px; }
.bydate-btn .badge-soft-count {
    background: var(--bg-input);
    color: var(--text-secondary);
    font-family: 'Inter', system-ui, sans-serif;
    font-size: var(--fs-count);
    font-weight: var(--fw-medium);
    padding: 1px 6px;
    border-radius: var(--r-pill);
    line-height: 1.4;
    font-variant-numeric: tabular-nums;
}

/* Filter-dropdown trigger button — variant of .bydate-btn used inside the
   custom filter dropdowns (group / department / shift). 36-px tall to
   match the old <select> footprint; flex layout so the leading icon, the
   label, and the trailing chevron line up cleanly across all three. */
.bydate-filter-btn {
    height: 36px;
    padding: 0 12px;
    gap: 8px;
    min-width: 140px;
    justify-content: flex-start;
}
.bydate-filter-btn .bydate-btn-text {
    flex: 1;
    text-align: start;
}
.bydate-filter-btn .bi-chevron-down {
    opacity: 0.55;
    transition: transform 0.15s ease, opacity 0.15s ease;
}
.bydate-filter-dd.is-open .bydate-filter-btn .bi-chevron-down {
    transform: rotate(180deg);
    opacity: 1;
}
/* When a filter is active (hidden value non-empty), tint the trigger so
   the operator sees at a glance which filters are constraining the list.
   Implemented as a state class set by setupFilterDropdown(). */
.bydate-filter-dd.has-value .bydate-filter-btn {
    border-color: var(--status-blue-700);
    color: var(--status-blue-700);
}
.bydate-filter-dd.has-value .bydate-filter-btn > .bi:first-child {
    color: var(--status-blue-700);
}

/* Dropdown panel anchored under a toolbar button.
   Notion-style polish (Step 33-bulk redesign):
     • Custom 16-px checkboxes — square w/ rounded corners, blue accent
       on check (matches the bydate primary button)
     • Soft blue tint on checked rows so the picker reads as "this set
       is enabled" at a glance, not just "this box has a tick"
     • 0.14s open animation — feels deliberate, not jarring
     • Tighter padding + larger interactive target (the whole row, not
       just the box)                                                  */
.bydate-dropdown {
    position: relative;
}
.bydate-dropdown-panel {
    position: absolute;
    top: calc(100% + 6px);
    inset-inline-start: 0;
    background: var(--bg-card);
    border: 1px solid var(--border-default);
    border-radius: 12px;
    box-shadow:
        0 4px 12px -2px rgba(15, 23, 42, 0.08),
        0 12px 28px -8px rgba(15, 23, 42, 0.18);
    padding: 6px;
    min-width: 240px;
    z-index: 25;
    display: none;
    animation: zkDdPanelOpen 0.14s ease-out;
}
@keyframes zkDdPanelOpen {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: translateY(0); }
}
.bydate-dropdown.is-open .bydate-dropdown-panel { display: block; }

.bydate-dropdown-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: 8px;
    font-size: var(--fs-secondary);
    color: var(--text-primary);
    cursor: pointer;
    border: none;
    background: transparent;
    width: 100%;
    text-align: start;
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
    transition: background 0.12s ease, color 0.12s ease;
    position: relative;
}
.bydate-dropdown-item:hover { background: var(--bg-hover); }
.bydate-dropdown-item:focus-visible {
    outline: 2px solid var(--status-blue-700);
    outline-offset: -2px;
}
/* Single-select rows (Group-by, filter dropdowns) — soft blue tint +
   blue label, mirrors the :has(:checked) treatment used by the multi-
   select column picker so the two dropdown flavors read alike. */
.bydate-dropdown-item.is-selected {
    background: var(--blue-50);
    color: var(--status-blue-700);
    font-weight: var(--fw-medium);
}
.bydate-dropdown-item.is-selected:hover {
    background: var(--blue-50);
    filter: brightness(0.97);
}
.bydate-dropdown-item:disabled,
.bydate-dropdown-item[disabled] {
    opacity: 0.5;
    cursor: not-allowed;
    background: transparent !important;
}
/* Destructive variant — used for "Clear all", "Delete", "Factory reset"
   items inside dropdowns. Red text + subtle red tint on hover. */
.bydate-dropdown-item.is-danger { color: var(--status-red-700); }
.bydate-dropdown-item.is-danger:hover { background: var(--red-50); }

/* ---- Notion-style checkbox (replaces the OS-default) ----
   The native input is hidden visually but kept in the DOM so screen
   readers + form behaviour stay untouched. The styled box is rendered
   via appearance: none + ::after for the checkmark, matching the
   Fixed/Flexible toggle palette used elsewhere in the app.            */
.bydate-dropdown-item input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    width: 16px;
    height: 16px;
    border: 1.5px solid var(--border-default);
    border-radius: 4px;
    background: var(--bg-card);
    cursor: pointer;
    flex-shrink: 0;
    margin: 0;
    position: relative;
    transition: background 0.12s ease, border-color 0.12s ease;
}
.bydate-dropdown-item:hover input[type="checkbox"]:not(:disabled) {
    border-color: var(--status-blue-700);
}
.bydate-dropdown-item input[type="checkbox"]:checked {
    background: var(--status-blue-700);
    border-color: var(--status-blue-700);
}
/* Custom checkmark — a rotated border tick. Beats SVG/image: zero
   network cost, scales with currentColor, sits inside the box centred. */
.bydate-dropdown-item input[type="checkbox"]:checked::after {
    content: '';
    position: absolute;
    top: 1px;
    inset-inline-start: 4px;
    width: 5px;
    height: 9px;
    border-right: 2px solid #fff;
    border-bottom: 2px solid #fff;
    transform: rotate(45deg);
}
.bydate-dropdown-item input[type="checkbox"]:focus-visible {
    outline: 2px solid var(--status-blue-700);
    outline-offset: 2px;
}
.bydate-dropdown-item input[type="checkbox"]:disabled {
    background: var(--bg-input);
    border-color: var(--border-subtle);
    cursor: not-allowed;
}
.bydate-dropdown-item input[type="checkbox"]:disabled:checked {
    background: var(--text-tertiary);
    border-color: var(--text-tertiary);
}

/* Soft blue tint on the row when its checkbox is checked — uses :has()
   so the styling lives next to the rule it belongs to, no JS coupling.
   Browsers: supported since 2023 (Chrome 105+, Firefox 121+, Safari 15.4+). */
.bydate-dropdown-item:has(input[type="checkbox"]:checked) {
    background: var(--blue-50);
}
.bydate-dropdown-item:has(input[type="checkbox"]:checked):hover {
    background: var(--blue-50);
    filter: brightness(0.97);
}
.bydate-dropdown-item:has(input[type="checkbox"]:checked) > span {
    color: var(--status-blue-700);
    font-weight: var(--fw-medium);
}

/* Section header inside a dropdown panel — groups related items
   under a small uppercase label. Used in the Devices "Advanced"
   menu to separate Wipe / Access recovery / Lifecycle. */
.bydate-dropdown-section {
    padding: 8px 10px 4px 10px;
    font-size: 10px;
    font-weight: var(--fw-medium);
    color: var(--text-tertiary);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    line-height: 1.4;
    user-select: none;
}
.bydate-dropdown-section:first-child { padding-top: 4px; }

/* Hairline divider between sections in a dropdown panel. */
.bydate-dropdown-divider {
    height: 1px;
    background: var(--border-subtle);
    margin: 4px 0;
}

/* ============================================================
   TABLE
   Two layouts coexist on this set of classes:

   (A) Legacy two-region split (still in use by /Employees and
       /Devices) — .bydate-table-wrap is a flex row with a
       sticky inline-start employee column .bydate-employees-col
       (240px) and a horizontally-scrollable .bydate-scroll-col
       on the inline-end side. Header row is sticky (top:0)
       during vertical scroll.

   (B) Single-table layout (used by /Reports/ByDate after the
       column redesign) — uses .bydate-scroll-host instead of
       the wrap+split, with the Employee column rendered as
       an ordinary locked first column.

   Both layouts share .bydate-table-card and .bydate-table.
   ============================================================ */
.bydate-table-card {
    background: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-radius: var(--r-card);
    overflow: hidden;
}

/* ---- Layout (A) — legacy two-region split ---- */
.bydate-table-wrap {
    display: flex !important;        /* defensive — never collapse to block */
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: stretch;
    /* Vertical scroll lives at the wrap level so both columns scroll
       in lockstep; horizontal scroll lives inside the scroll-col only. */
    max-height: calc(100vh - 360px);
    min-height: 360px;
    overflow-y: auto;
    overflow-x: hidden;
}

/* Sticky inline-start column — anchors RIGHT in RTL, LEFT in LTR. */
.bydate-employees-col {
    flex: 0 0 240px;
    width: 240px;
    background: var(--bg-card);
    border-inline-end: 1px solid var(--border-subtle);
    position: sticky;
    inset-inline-start: 0;
    z-index: 2;
    align-self: flex-start;
}
/* Box-shadow uses physical x-axis, so we explicitly mirror per direction
   to keep the shadow on the side facing the scroll content (the inline-
   end side in both LTR and RTL). */
[dir="ltr"] .bydate-employees-col { box-shadow:  3px 0 6px rgba(0, 0, 0, 0.04); }
[dir="rtl"] .bydate-employees-col { box-shadow: -3px 0 6px rgba(0, 0, 0, 0.04); }

.bydate-scroll-col {
    flex: 1 1 auto;
    min-width: 0;
    overflow-x: auto;
    /* Subtle scrollbar */
    scrollbar-width: thin;
    scrollbar-color: var(--border-default) transparent;
}
.bydate-scroll-col::-webkit-scrollbar { height: 8px; }
.bydate-scroll-col::-webkit-scrollbar-thumb { background: var(--border-default); border-radius: 4px; }

/* ---- Row-height parity between the sticky and scroll tables ----
   The sticky column carries an avatar (32px) plus a name + #PIN stack,
   which natural-sizes its rows to ~56px. The scroll-column rows hold
   plain text and would otherwise size to the base 44px height. Force
   both tbody rows to a common 56-px height so each employee's name
   lines up with its own data row, even after dozens of rows. ---- */
.bydate-table-wrap .bydate-table tbody tr { height: 56px; }
.bydate-table-wrap .bydate-table tbody td { height: 56px; }

/* ---- Layout (B) — single-table scroll host (Reports/ByDate) ---- */
.bydate-scroll-host {
    max-height: calc(100vh - 360px);
    min-height: 360px;
    overflow-y: auto;
    overflow-x: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--border-default) transparent;
    -webkit-overflow-scrolling: touch;       /* iOS momentum scroll inside scroll host */
}
/* On phone-portrait screens the table can be the tallest thing on the
   page — relax the min-height so the scroll host doesn't push content
   below the fold, and shorten the max-height so the page chrome above
   it stays accessible without forcing a long scroll. */
@media (max-width: 767.98px) {
    .bydate-scroll-host {
        max-height: calc(100vh - 240px);
        min-height: 240px;
    }
}
.bydate-scroll-host::-webkit-scrollbar { height: 8px; width: 8px; }
.bydate-scroll-host::-webkit-scrollbar-thumb { background: var(--border-default); border-radius: 4px; }

.bydate-table {
    width: 100%;
    border-collapse: collapse;
    table-layout: fixed;
}
.bydate-table th, .bydate-table td {
    height: 44px;
    padding: 12px 16px;
    /* `start` aligns text to the inline-start edge of the cell:
       LTR → LEFT (English reading start)
       RTL → RIGHT (Arabic reading start)
       This pairs naturally with the flex-start anchors used by
       .bydate-emp etc., so headers sit directly above their data. */
    text-align: start;
    vertical-align: middle;
    border-bottom: 1px solid var(--border-subtle);
    background: var(--bg-card);
}
.bydate-table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--bg-hover);     /* slate-50 header */
    color: #64748B;                  /* slate-500/600 mid-tone */
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    line-height: 1;
}
.bydate-table tbody tr:last-child td { border-bottom: none; }
.bydate-table tbody tr { transition: background 0.1s; }
.bydate-table tbody tr:hover td { background: var(--bg-hover); cursor: pointer; }

/* Employee column — name (13px medium) + #PIN (Inter 11px placeholder color)
   DOM is [avatar] [name + #PIN]. Anchor the group to inline-START so
   the avatar lands on the reading-start edge: RIGHT in Arabic,
   LEFT in English. (Was flex-end with a misleading comment that
   confused inline-end with "right" — they only coincide in LTR.)    */
.bydate-emp {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: 8px;
}
.bydate-emp-name {
    font-size: var(--fs-secondary);
    font-weight: var(--fw-medium);
    color: var(--text-primary);
}
.bydate-emp-pin {
    font-family: 'Inter', system-ui, sans-serif;
    font-size: 11px;
    color: var(--text-placeholder);
    font-variant-numeric: tabular-nums;
}

/* Numeric cells (date / time) — Inter, tabular */
.bydate-num {
    font-family: 'Inter', system-ui, sans-serif;
    font-variant-numeric: tabular-nums;
    color: var(--text-primary);
    font-size: var(--fs-secondary);
}
.bydate-num-medium { font-weight: var(--fw-medium); }
/* Em dash placeholder for missing punches — gray, regular */
.bydate-empty {
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
    color: #CBD5E1;                  /* slate-300 */
    font-size: var(--fs-secondary);
}

/* Status pill in الحالة column */
.bydate-status {
    display: inline-flex;
    align-items: center;
    padding: 4px 10px;
    border-radius: 6px;
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    line-height: 1.2;
    white-space: nowrap;
}
.bydate-status[data-tone="present"]      { background: var(--green-50); color: var(--status-green-700); }
.bydate-status[data-tone="absent"]       { background: var(--red-50);   color: var(--status-red-700); }
.bydate-status[data-tone="leave"]        { background: var(--blue-50);  color: var(--status-blue-700); }
.bydate-status[data-tone="weekly-off"]   { background: #F1F5F9; color: #475569; }   /* slate-100 / slate-600 */
.bydate-status[data-tone="missing"]      { background: var(--amber-50); color: var(--status-amber-700); }

/* Day-of-week chip (slate, compact) — used by the optional Day column. */
.bydate-day {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 6px;
    background: #F1F5F9;
    color: #475569;
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
    font-size: 11px;
    font-weight: var(--fw-medium);
    line-height: 1.4;
}

/* Late / Early minute chips — amber for late-in, blue for early-out so
   the eye can scan them apart at a glance. Inter for tabular numerals. */
.bydate-chip-min {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 6px;
    font-family: 'Inter', system-ui, sans-serif;
    font-variant-numeric: tabular-nums;
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    line-height: 1.4;
    white-space: nowrap;
}
.bydate-chip-min.is-late  { background: var(--amber-50); color: var(--status-amber-700); }
.bydate-chip-min.is-early { background: var(--blue-50);  color: var(--status-blue-700); }

/* Locked checkbox row (Employee column) in the columns chooser dropdown.
   A locked row is always-on and uncheckable — the disabled checkbox plus
   a small inline lock icon next to the label tell that story. We make the
   row read as "permanently enabled" by tinting it like a checked row,
   then desaturate the checkbox itself so it doesn't read as interactive. */
.bydate-dropdown-item.is-locked {
    cursor: default;
}
.bydate-dropdown-item.is-locked:hover {
    background: var(--blue-50);
    filter: none;
}
.bydate-dropdown-item.is-locked input[type="checkbox"]:checked {
    background: var(--text-tertiary);
    border-color: var(--text-tertiary);
    opacity: 0.7;
}
.bydate-dropdown-item.is-locked > span {
    color: var(--text-secondary);
    font-weight: var(--fw-medium);
}
.bydate-dropdown-item.is-locked .bi-lock-fill {
    color: var(--text-tertiary);
    opacity: 0.55 !important;
    font-size: 11px !important;
}

/* Group subheader rows (when "Group by" is active) */
.bydate-group-row td {
    background: var(--bg-hover);
    color: var(--text-primary);
    font-size: var(--fs-secondary);
    font-weight: var(--fw-medium);
    padding-block: 10px;
    border-top: 1px solid var(--border-subtle);
}

/* ================================================================
   .hx-chip — Soft tag/chip primitive (originally defined inline on
   /HolidaysExceptions, promoted here as a global). Replaces the
   heavier Bootstrap `bg-*-subtle` badges across the app.

   Variants are tone-based, not semantic — pick the variant that
   matches the action's intent:

       paid    🟢  success / yes / active        green-50
       fail    🔴  failure / no / blocked        red-50
       unpaid  🟡  pending / no / warning        amber-50
       warn    🟡  alias for unpaid              amber-50
       info    🔵  neutral info / sent           cyan-50
       days    🔵  blue counter / numeric        blue-50
       kind1   🟡  alt amber tone                amber-50
       kind2   🔵  alt cyan tone                 cyan-50
       kind3   ⚪  inactive / disabled / neutral bg-input
       count   ⚪  tabular count badge           bg-input
   ================================================================ */
.hx-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 10px;
    font-size: 11px;
    font-weight: var(--fw-medium);
    border-radius: 999px;
    line-height: 1.6;
    white-space: nowrap;
}
.hx-chip-days   { background: var(--blue-50);   color: var(--status-blue-700);  }
.hx-chip-paid   { background: var(--green-50);  color: var(--status-green-700); }
.hx-chip-fail   { background: var(--red-50);    color: var(--status-red-700);   }
.hx-chip-unpaid { background: var(--amber-50);  color: var(--status-amber-700); }
.hx-chip-info   { background: #ECFEFF;          color: #0E7490; }   /* cyan-50 / cyan-700 */
.hx-chip-warn   { background: var(--amber-50);  color: var(--status-amber-700); }
.hx-chip-kind1  { background: var(--amber-50);  color: var(--status-amber-700); }
.hx-chip-kind2  { background: #ECFEFF;          color: #0E7490; }
.hx-chip-kind3  { background: var(--bg-input);  color: var(--text-secondary); }
.hx-chip-count  { background: var(--bg-input);  color: var(--text-secondary); font-variant-numeric: tabular-nums; }

.hx-chip-row { display: inline-flex; gap: 4px; flex-wrap: wrap; }


/* Empty state */
.bydate-empty-state {
    text-align: center;
    padding: 56px 24px;
    color: var(--text-placeholder);
    font-size: var(--fs-secondary);
}
.bydate-empty-state i { font-size: 28px; opacity: 0.5; display: block; margin-bottom: 12px; }


/* ================================================================
   .zk-empty — Unified empty-state primitive.

   Two visual variants, same DOM shape:

     • .zk-empty.is-hero    Onboarding / first-use. Tall padding, large
                            icon tile, bold title, multi-line description,
                            optional primary CTA button. Used when the
                            data set is *truly* empty (no rows ever).

     • .zk-empty.is-compact Filter / "no matches" feedback. Half the
                            padding, smaller icon tile, two lines max,
                            optional secondary CTA. Used when there IS
                            data but the current filter / search hides it.

   DOM (works inside a <td> with colspan OR a div):

     <div class="zk-empty is-hero">
         <div class="zk-empty-icon"><i class="bi bi-people"></i></div>
         <div class="zk-empty-title">No employees yet</div>
         <div class="zk-empty-desc">Add your first employee to start
              tracking attendance. They will appear here in the table.</div>
         <div class="zk-empty-actions">
             <button class="bydate-btn cr-btn-primary">…</button>
         </div>
     </div>

   When the host is a <td>, override .bydate-table's `text-align: end`
   inheritance with the explicit `td.zk-empty` selector. We mirror the
   same trick used by .cr-empty-state.

   Legacy aliases:
     .cr-empty-state, .bydate-empty-state — earlier primitives kept
     working for back-compat; new views should adopt .zk-empty.
   ================================================================ */
.zk-empty,
td.zk-empty {
    text-align: center;
    background: var(--bg-card);
}
.zk-empty.is-hero,
td.zk-empty.is-hero {
    padding: 64px 24px 68px;
}
.zk-empty.is-compact,
td.zk-empty.is-compact {
    padding: 36px 24px 40px;
}

/* Inner column so flex centering survives <td> defaults. */
.zk-empty-inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0;
    max-width: 440px;
    margin: 0 auto;
}

/* Icon tile — color tone communicates the kind of empty state:
   default = blue (neutral / informational), .is-warn = amber (filter),
   .is-success = green (all clear / nothing to act on). */
.zk-empty-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 16px;
    background: var(--blue-50);
    color: var(--status-blue-700);
    margin-bottom: 16px;
}
.zk-empty.is-hero    .zk-empty-icon { width: 64px; height: 64px; font-size: 28px; }
.zk-empty.is-compact .zk-empty-icon { width: 48px; height: 48px; font-size: 22px; margin-bottom: 12px; }

.zk-empty-icon.is-warn    { background: var(--amber-50); color: var(--status-amber-700); }
.zk-empty-icon.is-success { background: var(--green-50); color: var(--status-green-700); }
.zk-empty-icon.is-neutral { background: var(--bg-input); color: var(--text-secondary); }

/* Title — primary text tone, slightly larger in hero variant. */
.zk-empty-title {
    color: var(--text-primary);
    font-weight: var(--fw-medium);
    line-height: 1.4;
    margin: 0 0 6px 0;
}
.zk-empty.is-hero    .zk-empty-title { font-size: var(--fs-page-title, 18px); }
.zk-empty.is-compact .zk-empty-title { font-size: var(--fs-secondary); }

/* Description — wraps naturally up to the 440px container width. */
.zk-empty-desc {
    color: var(--text-secondary);
    font-size: var(--fs-meta);
    line-height: 1.6;
    margin: 0;
}

/* Action row — sits 18px below description in hero, 12px in compact. */
.zk-empty-actions {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    flex-wrap: wrap;
}
.zk-empty.is-hero    .zk-empty-actions { margin-top: 20px; }
.zk-empty.is-compact .zk-empty-actions { margin-top: 14px; }

/* Secondary "subtle" action — used for things like "Clear filters". */
.zk-empty-action-link {
    background: transparent;
    border: none;
    color: var(--status-blue-700);
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    cursor: pointer;
    padding: 4px 8px;
    border-radius: 6px;
}
.zk-empty-action-link:hover { background: var(--bg-hover); }

/* ============================================================
   RESPONSIVE
   The desktop spec is per the Figma. At narrower widths:
   • <1280: drop the 1200px max-width — handled by the global
     .main-container rule already in this file.
   • 1024–1279: same as ≥1280 (sidebar narrows to 64 at 1023).
   • 768–1023: stack filter card; chips wrap; action buttons keep
     full width but wrap.
   • <768: fully stacked, action buttons icon-only via tooltips.
   ============================================================ */
@media (max-width: 1023.98px) {
    .bydate-filter {
        flex-wrap: wrap;
    }
    .bydate-input { width: 100%; min-width: 200px; }
    .bydate-filter-group { flex: 1 1 240px; }
    .bydate-mode-badge { order: -1; align-self: flex-start; }
    .bydate-toolbar-row { flex-wrap: wrap; }
    .bydate-search { width: 100%; order: 2; }
    .bydate-chips  { order: 1; }
    .bydate-table-wrap { max-height: none; }
}
@media (max-width: 767.98px) {
    .bydate-card { padding: 14px 14px; margin-bottom: 12px; }
    .bydate-title { font-size: 22px; }
    .bydate-input { height: 40px; }
    .bydate-mode-badge { height: 36px; }
    .bydate-actions { width: 100%; justify-content: flex-end; }
    /* Compact buttons — keep icon, hide label below 480px */
    .bydate-btn { padding: 0 10px; }
}
@media (max-width: 479.98px) {
    .bydate-btn .bydate-btn-text { display: none; }
    .bydate-btn { padding: 0 10px; gap: 4px; }
}

/* ============================================================
   PRINT — clean, color-faithful, landscape
   ============================================================ */
@media print {
    .sidebar, .topbar, .bydate-filter-card, .bydate-toolbar, .toast-container,
    .no-print { display: none !important; }
    .main { margin: 0 !important; padding: 12px !important; }
    .bydate-table-wrap,
    .bydate-scroll-host {
        max-height: none !important;
        overflow: visible !important;
    }
    .bydate-employees-col, .bydate-scroll-col { overflow: visible !important; }
    .bydate-employees-col { box-shadow: none !important; position: static !important; }
    .bydate-table thead th { background: #F8FAFC !important; }
    @page { size: landscape; margin: 1cm; }
    * { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }
}


/* ---- KPI CARDS ---- */
.kpi {
    border: 1px solid var(--bs-border-color);
    border-radius: .75rem;
    padding: 1rem 1.1rem;
    background: var(--bs-body-bg);
}
.kpi .label {
    font-size: .78rem;
    color: var(--bs-secondary-color);
    text-transform: uppercase;
    letter-spacing: .05em;
}
html[lang="ar"] .kpi .label { letter-spacing: 0; text-transform: none; }
.kpi .value {
    font-size: 1.85rem;
    font-weight: 600;
    line-height: 1.1;
    margin-top: .2rem;
}
.kpi .delta { font-size: .78rem; }

/* ---- PANELS (used inside main pages) ---- */
.panel {
    background: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: .6rem;
    padding: 1rem 1.25rem;
    margin-bottom: 1rem;
}
.panel h5 {
    margin-bottom: .85rem;
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: .4rem;
}

/* ---- DOTS (status indicators) ---- */
.dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    display: inline-block;
    margin-right: 6px;
}
.dot.ok   { background: #10b981; box-shadow: 0 0 0 3px rgba(16,185,129,.18); }
.dot.bad  { background: #ef4444; box-shadow: 0 0 0 3px rgba(239,68,68,.18); }
.dot.warn { background: #f59e0b; box-shadow: 0 0 0 3px rgba(245,158,11,.18); }
.dot.offline { background: #94a3b8; }
.dot.pulse {
    animation: pulse 1.6s ease-out infinite;
}
@keyframes pulse {
    0%   { box-shadow: 0 0 0 0 rgba(16,185,129,.4); }
    70%  { box-shadow: 0 0 0 8px rgba(16,185,129,0); }
    100% { box-shadow: 0 0 0 0 rgba(16,185,129,0); }
}

/* ---- TYPOGRAPHY HELPERS ---- */
.mono {
    font-family: 'Cascadia Mono', Consolas, 'Courier New', monospace;
    font-size: .85rem;
}
/* Tech tokens (PIN/SN/codes) stay LTR even inside Arabic */
.ltr, code {
    direction: ltr !important;
    unicode-bidi: isolate;
}
code {
    font-family: 'Cascadia Mono', Consolas, monospace;
}

/* ---- INLINE EDIT (used in Devices + Employees tables) ---- */
.inline-edit {
    border: 1px solid transparent;
    background: transparent;
    padding: .15rem .35rem;
    border-radius: .25rem;
    min-width: 120px;
    font-size: .875rem;
}
.inline-edit:hover { border-color: #cbd5e1; background: #f8fafc; }
.inline-edit:focus {
    border-color: #3b82f6;
    background: #fff;
    outline: none;
    box-shadow: 0 0 0 2px rgba(59,130,246,.18);
}

/* ---- INLINE SELECTS ---- */
.group-select {
    border: 1px solid var(--bs-border-color);
    padding: .15rem .4rem;
    border-radius: .25rem;
    font-size: .825rem;
    background: var(--bs-body-bg);
}
.group-select:focus { border-color: #3b82f6; outline: none; }

/* ---- SPINNING ICON (used by syncing indicator) ---- */
.spin {
    display: inline-block;
    animation: spin 1.2s linear infinite;
}
@keyframes spin {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

/* ---- STATUS PILL (online / idle / offline + syncing) ---- */
.status-pill {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    font-size: .8rem;
    white-space: nowrap;
}
.status-pill .label { font-weight: 500; }
.status-pill.online  .label { color: #047857; }
.status-pill.warn    .label { color: #b45309; }
.status-pill.offline .label { color: #b91c1c; }

.sync-badge {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    font-size: .7rem;
    padding: .12rem .45rem;
    border-radius: 99px;
    background: rgba(59,130,246,.12);
    color: #1d4ed8;
    margin-inline-start: .35rem;
}
[data-bs-theme="dark"] .sync-badge { color: #93c5fd; }

/* Same shape as sync-badge but gray + clock icon — used when device is offline/idle and commands are queued. */
.wait-badge {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    font-size: .7rem;
    padding: .12rem .45rem;
    border-radius: 99px;
    background: rgba(148,163,184,.18);
    color: #475569;
    margin-inline-start: .35rem;
}
[data-bs-theme="dark"] .wait-badge { color: #cbd5e1; background: rgba(148,163,184,.22); }

/* Inbound — device is sending records to us (the mirror of sync-badge).
   .live = currently receiving (pulsing); .cool = just-finished cooldown. */
.recv-badge {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    font-size: .7rem;
    padding: .12rem .45rem;
    border-radius: 99px;
    margin-inline-start: .35rem;
}
.recv-badge.live {
    background: rgba(16,185,129,.15);
    color: #047857;
    animation: recv-pulse 1.6s ease-out infinite;
}
.recv-badge.cool {
    background: rgba(148,163,184,.18);
    color: #475569;
}
[data-bs-theme="dark"] .recv-badge.live { color: #6ee7b7; background: rgba(16,185,129,.22); }
[data-bs-theme="dark"] .recv-badge.cool { color: #cbd5e1; background: rgba(148,163,184,.22); }
@keyframes recv-pulse {
    0%   { box-shadow: 0 0 0 0 rgba(16,185,129,.35); }
    70%  { box-shadow: 0 0 0 6px rgba(16,185,129,0); }
    100% { box-shadow: 0 0 0 0 rgba(16,185,129,0); }
}

/* ---- ROW SAVE FLASH ---- */
/* Legacy green "save succeeded" flash — kept for back-compat with the
   existing flashRow() callers across views. New code should prefer
   .zk-row-flash below (amber, 1.5s — reads as "this row changed"). */
.saved-flash { animation: flash .9s ease-out; }
@keyframes flash {
    0%   { background: #dcfce7; }
    100% { background: transparent; }
}

/* ================================================================
   MICRO-INTERACTIONS — small celebratory animations that confirm
   user actions worked. All honor `prefers-reduced-motion: reduce`.

   Inventory:
     • .zk-pulse-success       button glows green after save
     • .zk-row-flash           table row pulses amber after update
     • .notion-tabs (slide)    underline glides between active tabs
     • .zk-copy-ok             clipboard button morphs to ✓
   ================================================================ */

/* ---- 1. Save-success pulse — 600ms green glow ---- */
.zk-pulse-success {
    animation: zkPulseOk 600ms ease-out;
}
@keyframes zkPulseOk {
    0%   { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.45); }
    50%  { box-shadow: 0 0 0 8px rgba(34, 197, 94, 0.00); background-color: var(--green-50); }
    100% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.00); }
}

/* ---- 2. Row-changed flash — 1.5s amber fade ----
   Used when a row in any table receives a server-side update (rename,
   status change, live-tail push). Amber says "look here, this is new"
   without screaming success — that's reserved for the explicit
   confirmation badge / toast. */
.zk-row-flash > td,
.zk-row-flash {
    animation: zkRowFlash 1.5s ease-out;
}
@keyframes zkRowFlash {
    0%   { background-color: var(--amber-50); }
    100% { background-color: transparent; }
}

/* ---- 3. Tab underline slide ----
   The .notion-tabs strip gets a single `::after` indicator whose
   --indicator-left + --indicator-width are set by JS to follow the
   currently-active tab. Browsers transition both values smoothly,
   so the strip looks like it "glides" between sections instead of
   the underline blinking in/out on each tab swap. */
.notion-tabs {
    position: relative;
}
.notion-tabs::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: var(--indicator-left, 0);
    width: var(--indicator-width, 0);
    height: 2px;
    background: var(--text-primary);
    transition: left 0.22s ease, width 0.22s ease;
    pointer-events: none;
}
/* When the indicator is active, the per-tab border-bottom becomes
   redundant — it'd double up with the sliding strip. Keep the per-tab
   border invisible so only the indicator shows. */
.notion-tabs.has-slide .notion-tab.active,
.notion-tabs.has-slide .notion-tab.is-active {
    border-bottom-color: transparent;
}

/* ---- 4. Copy-to-clipboard success swap ----
   Briefly replaces the button's content + tints it green after a
   successful clipboard.write. Pairs with the ZkPush.copyToClipboard()
   JS helper, which toggles the class for ~1.2s. */
.zk-copy-ok {
    color: var(--status-green-700) !important;
    border-color: #BBF7D0 !important;
    background: var(--green-50) !important;
}
.zk-copy-ok .zk-copy-icon-default { display: none; }
.zk-copy-ok .zk-copy-icon-ok      { display: inline; }
.zk-copy-icon-ok { display: none; }     /* hidden until .zk-copy-ok flips it on */

/* ---- Reduced-motion fallback ----
   Skip the celebratory animations for users who request less motion.
   They still see the color change (pulse-success leaves a green
   border briefly, row-flash leaves an amber tint flash), just not
   the easing curve. */
@media (prefers-reduced-motion: reduce) {
    .zk-pulse-success { animation: none; box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.4); }
    .zk-row-flash > td, .zk-row-flash { animation: none; background-color: var(--amber-50); }
    .notion-tabs::after { transition: none; }
}

/* ---- EMPLOYEE PHOTO ---- */
.emp-photo {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    object-fit: cover;
    border: 1px solid #e5e7eb;
    background: #f1f5f9;
}
.emp-photo-fallback {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: #e2e8f0;
    color: #94a3b8;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.2rem;
}
.badge-pri-admin { background: #fef3c7; color: #92400e; }
.badge-pri-other { background: #fee2e2; color: #991b1b; }

/* ---- (removed) duplicate .lang-pill rule that overrode the Notion-
   style language toggle with an old blue-accent pill. The topbar now
   uses .notion-seg / .notion-seg-btn (segmented control) — see the
   Notion segmented-control block above. ---- */

/* ---- BLACK CONSOLE LOG (used on diag) ---- */
#log {
    background: #0f172a;
    color: #cbd5e1;
    border-radius: .5rem;
    padding: .85rem 1rem;
    max-height: 340px;
    overflow: auto;
    font-family: 'Cascadia Mono', Consolas, monospace;
    font-size: .8rem;
}
#log .l-ok   { color: #86efac; }
#log .l-bad  { color: #fca5a5; }
#log .l-info { color: #93c5fd; }
#log .l-meta { color: #94a3b8; }

/* ---- TIGHT TABLES ---- */
.table-tight td, .table-tight th { padding: .55rem .75rem; vertical-align: middle; }
.badge-soft { background: rgba(59,130,246,.12); color: #1d4ed8; font-weight: 500; }
[data-bs-theme="dark"] .badge-soft { color: #93c5fd; }

/* ================================================================
   SETTINGS HUB CARDS
   Notion-style tile that links to a sub-page from the /Settings hub.
   Anchor element with three children:
       .settings-icon         tone-tinted square icon (44 × 44)
       .settings-card-body    title + short description
       .settings-card-arrow   chevron-right that reveals on hover

   Hover lifts the card 1px and tints the border with the brand lime
   (`--lime`) so every tile shares one common "I'm interactive" cue.
   The .is-danger variant tints with red instead — preserves the
   destructive signal on System Reset.
   ================================================================ */
.settings-card {
    display: flex;
    align-items: flex-start;
    gap: 14px;
    border: 1px solid var(--border-default);
    border-radius: var(--r-card, 10px);
    background: var(--bg-card);
    padding: 16px 18px;
    color: var(--text-primary);
    text-decoration: none;
    transition: border-color .15s ease, transform .15s ease, box-shadow .15s ease;
}
.settings-card:hover:not(.settings-disabled) {
    border-color: var(--lime);
    transform: translateY(-1px);
    box-shadow: 0 6px 20px -10px rgba(212, 255, 60, 0.35);
    color: var(--text-primary);
    text-decoration: none;
}
.settings-card.is-danger:hover:not(.settings-disabled) {
    border-color: #FECACA;
    box-shadow: 0 6px 20px -10px rgba(239, 68, 68, 0.25);
}
.settings-card.settings-disabled { opacity: .6; cursor: not-allowed; }

/* Tone-tinted icon square. The variants share the same shape but
   pick their background+foreground from the soft-color tokens so
   light + dark mode stay legible automatically. */
.settings-icon {
    width: 44px;
    height: 44px;
    border-radius: 10px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 20px;
    flex-shrink: 0;
}
.settings-icon.is-blue   { background: var(--blue-50);   color: var(--status-blue-700); }
.settings-icon.is-amber  { background: var(--amber-50);  color: var(--status-amber-700); }
.settings-icon.is-green  { background: var(--green-50);  color: var(--status-green-700); }
.settings-icon.is-red    { background: var(--red-50);    color: var(--status-red-700); }
.settings-icon.is-cyan   { background: #ECFEFF;          color: #0E7490; }
.settings-icon.is-purple { background: var(--purple-50); color: var(--status-purple-700); }
.settings-icon.is-neutral{ background: var(--bg-input);  color: var(--text-secondary); }

/* Body — title + description stack. */
.settings-card-body {
    flex: 1;
    min-width: 0;     /* allow text-overflow to work inside flex */
}
.settings-card-title {
    font-size: var(--fs-secondary);
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    margin: 0 0 4px 0;
    line-height: 1.4;
}
.settings-card.is-danger .settings-card-title { color: var(--status-red-700); }

.settings-card-desc {
    font-size: var(--fs-meta);
    color: var(--text-secondary);
    line-height: 1.5;
    margin: 0;
}

/* Chevron — hidden until hover so the row reads calm by default,
   then signals "this is clickable" without permanent visual noise.
   RTL flips it via the dir-aware mirror trick. */
.settings-card-arrow {
    color: var(--text-tertiary);
    font-size: 16px;
    align-self: center;
    opacity: 0;
    transition: opacity .15s ease, transform .15s ease;
}
.settings-card:hover:not(.settings-disabled) .settings-card-arrow {
    opacity: 1;
    color: var(--text-primary);
}
html[dir="rtl"] .settings-card-arrow { transform: scaleX(-1); }
html[dir="rtl"] .settings-card:hover:not(.settings-disabled) .settings-card-arrow {
    transform: scaleX(-1) translateX(-2px);   /* tiny slide on hover */
}
html[dir="ltr"] .settings-card:hover:not(.settings-disabled) .settings-card-arrow {
    transform: translateX(2px);
}

/* ---- SCHEDULE EDITOR (Step 33-sch — Notion-style polish) ---- */
.schedule-day-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 4px;
    border-bottom: 1px solid var(--border-subtle);
}
.schedule-day-row:last-child { border-bottom: 0; }
.schedule-day-label {
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    width: 80px;
    flex-shrink: 0;
    font-size: var(--fs-secondary);
}
html[lang="ar"] .schedule-day-label { width: 100px; }
.schedule-chips {
    flex-grow: 1;
    min-width: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}
.schedule-empty {
    font-size: var(--fs-meta);
    color: var(--text-tertiary);
    font-style: italic;
}
/* Time-range chip — extends .hx-chip-info with room for the inline
   close-X button + optional "+1d" wrap tag. */
.schedule-chip {
    gap: 6px;
    padding: 4px 8px;
}
.schedule-chip .mono { font-size: 12px; }
/* Inline close-X — small 16-px button that reads as a "remove" affordance.
   Replaces Bootstrap's btn-close pixel asset with a clean Bootstrap Icon. */
.schedule-chip-close {
    width: 16px;
    height: 16px;
    padding: 0;
    border: 0;
    background: transparent;
    color: currentColor;
    opacity: 0.6;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 4px;
    font-size: 13px;
    line-height: 1;
    transition: opacity 0.12s, background 0.12s;
}
.schedule-chip-close:hover {
    opacity: 1;
    background: rgba(0, 0, 0, 0.08);
}
/* Wrap-midnight tag inside a chip — small "+1d" pill, amber for warning. */
.schedule-wrap-tag {
    background: var(--amber-100, #FEF3C7);
    color: var(--status-amber-700);
    font-size: 10px;
    font-weight: var(--fw-medium);
    padding: 1px 5px;
    border-radius: 999px;
}

/* Add-range column — time-from → time-to + plus button, all on one row. */
.schedule-add {
    display: flex;
    align-items: center;
    flex-shrink: 0;
    gap: 6px;
}
.schedule-add .sched-start,
.schedule-add .sched-end {
    width: 7.5rem;
    height: 30px;
    padding: 0 8px;
    font-size: 12px;
}
.schedule-sep {
    color: var(--text-tertiary);
    font-size: 13px;
}
/* Small primary-style add button matching .cr-act-btn.is-primary. */
.schedule-add-btn {
    width: 30px;
    height: 30px;
    padding: 0;
    border: 1px solid #BFDBFE;          /* blue-200 */
    border-radius: 6px;
    background: var(--blue-50);
    color: var(--status-blue-700);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    transition: background 0.12s, border-color 0.12s;
}
.schedule-add-btn:hover {
    background: #BFDBFE;
    border-color: var(--status-blue-700);
}

/* Devices table — Schedules cell (Step 33-dev-sch).
   Renders 1-3 hx-chips with a small gap; "+N" trailing tail when more. */
.dv-sched-cell {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 4px;
    align-items: center;
}
.dv-sched-cell .hx-chip {
    font-size: 11px;
    padding: 2px 8px;
}

/* ---- COMING SOON PLACEHOLDER ---- */
.coming-soon {
    text-align: center;
    padding: 4rem 1rem;
    color: var(--bs-secondary-color);
}
.coming-soon i { font-size: 3rem; opacity: .35; }
.coming-soon h3 { margin-top: 1rem; font-weight: 500; }
.coming-soon p { max-width: 480px; margin: .75rem auto 0; }


/* ============================================================
   COMMAND PALETTE (Cmd/Ctrl+K)
   Floating modal overlay invoked from the topbar search or any
   keyboard shortcut. Dim backdrop + centered card with input,
   grouped results, keyboard hints in the footer.
   ============================================================ */

.cmd-palette {
    position: fixed;
    inset: 0;
    z-index: 1080;                       /* below toast (1090), above sidebar (30) */
    /* hidden attribute on the root toggles display via [hidden] global rule */
}

.cmd-palette-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(15, 23, 42, 0.4);   /* slate-900 @ 40% */
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    cursor: pointer;
}

.cmd-palette-modal {
    position: relative;
    max-width: 640px;
    width: calc(100% - 32px);
    margin: 10vh auto 0;
    background: var(--bg-card);
    border: 1px solid var(--border-default);
    border-radius: var(--r-card);
    box-shadow: 0 20px 60px -10px rgba(15, 23, 42, 0.35);
    overflow: hidden;
    /* gentle scale-in so the open feels intentional */
    animation: cmd-palette-pop 0.12s ease-out;
}
@keyframes cmd-palette-pop {
    from { opacity: 0; transform: translateY(-4px) scale(0.99); }
    to   { opacity: 1; transform: none; }
}

.cmd-palette-header {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 16px 20px;
    border-bottom: 1px solid var(--border-subtle);
}
.cmd-palette-search-icon {
    color: var(--text-tertiary);
    font-size: 16px;
    flex-shrink: 0;
}
.cmd-palette-header input {
    flex: 1;
    border: 0;
    outline: 0;
    background: transparent;
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
    font-size: 16px;
    font-weight: var(--fw-regular);
    color: var(--text-primary);
    min-width: 0;
}
.cmd-palette-header input::placeholder { color: var(--text-placeholder); }

.cmd-palette-results {
    max-height: 56vh;
    overflow-y: auto;
    padding: 6px 0;
    /* Subtle scrollbar to keep focus on the content */
    scrollbar-width: thin;
    scrollbar-color: var(--border-default) transparent;
}
.cmd-palette-results::-webkit-scrollbar { width: 6px; }
.cmd-palette-results::-webkit-scrollbar-thumb { background: var(--border-default); border-radius: 3px; }

.cmd-palette-group + .cmd-palette-group {
    border-top: 1px solid var(--border-subtle);
    margin-top: 4px;
    padding-top: 4px;
}
.cmd-palette-group-label {
    padding: 8px 20px 4px;
    font-size: 11px;
    font-weight: var(--fw-medium);
    color: var(--text-placeholder);
    letter-spacing: 0;
}

.cmd-palette-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 20px;
    cursor: pointer;
    border: none;
    background: transparent;
    width: 100%;
    text-align: start;
    color: var(--text-primary);
    transition: background 0.1s;
}
.cmd-palette-item.is-active,
.cmd-palette-item:hover {
    background: var(--bg-hover);
}

.cmd-palette-item-icon {
    width: 32px;
    height: 32px;
    border-radius: var(--r-tile);
    background: var(--bg-input);
    color: var(--text-secondary);
    display: grid;
    place-items: center;
    font-size: 14px;
    flex-shrink: 0;
}
/* Tone the icon chip by action kind so the row reads at a glance */
.cmd-palette-item-icon[data-tone="add"]      { background: var(--green-50); color: var(--status-green-700); }
.cmd-palette-item-icon[data-tone="page"]     { background: var(--blue-50);  color: var(--status-blue-700); }
.cmd-palette-item-icon[data-tone="system"]   { background: var(--purple-50); color: var(--status-purple-700); }
.cmd-palette-item-icon[data-tone="employee"] { background: var(--lime-soft); color: var(--text-primary); }
.cmd-palette-item-icon[data-tone="device"]   { background: var(--amber-50); color: var(--status-amber-700); }

.cmd-palette-item-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
    line-height: 1.3;
}
.cmd-palette-item-title {
    font-size: var(--fs-secondary);
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    /* clamp single line for visual rhythm */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.cmd-palette-item-subtitle {
    font-size: var(--fs-meta);
    color: var(--text-tertiary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.cmd-palette-item-arrow {
    color: var(--text-placeholder);
    font-size: 14px;
    opacity: 0;
    transition: opacity 0.1s;
    flex-shrink: 0;
}
.cmd-palette-item.is-active .cmd-palette-item-arrow { opacity: 1; }

/* Empty / no-results panel */
.cmd-palette-empty {
    text-align: center;
    padding: 40px 20px;
    color: var(--text-tertiary);
}
.cmd-palette-empty i {
    font-size: 28px;
    color: var(--text-placeholder);
    display: block;
    margin-bottom: 8px;
}
.cmd-palette-empty-hint {
    font-size: var(--fs-meta);
    color: var(--text-placeholder);
    margin-top: 4px;
}

.cmd-palette-footer {
    padding: 10px 20px;
    border-top: 1px solid var(--border-subtle);
    display: flex;
    align-items: center;
    gap: 16px;
    font-size: 11px;
    color: var(--text-placeholder);
    background: var(--bg-hover);
    flex-wrap: wrap;
}
.cmd-palette-footer .ms-auto { margin-inline-start: auto; }
.cmd-palette-shortcut-hint {
    font-family: 'Inter', system-ui, sans-serif;
    color: var(--text-tertiary);
    font-weight: var(--fw-medium);
}

.cmd-palette-kbd {
    display: inline-block;
    background: var(--bg-card);
    color: var(--text-secondary);
    font-family: 'Inter', system-ui, sans-serif;
    font-size: 10px;
    font-weight: var(--fw-medium);
    padding: 2px 6px;
    border-radius: 4px;
    border: 1px solid var(--border-default);
    margin: 0 2px;
    line-height: 1;
}

/* The topbar search input becomes a button-shaped affordance for the
   palette — give it a subtle "hint" decoration showing the shortcut. */
.topbar-search { position: relative; }
.topbar-search-shortcut {
    position: absolute;
    inset-inline-end: 40px;            /* sits just before the search icon */
    top: 50%;
    transform: translateY(-50%);
    pointer-events: none;
    font-family: 'Inter', system-ui, sans-serif;
    font-size: 11px;
    font-weight: var(--fw-medium);
    color: var(--text-placeholder);
    background: var(--bg-card);
    border: 1px solid var(--border-default);
    border-radius: 4px;
    padding: 1px 6px;
    line-height: 1.3;
    /* De-emphasised by default; pops on hover to confirm the shortcut.
       Hidden entirely while typing so it doesn't crowd the cursor. */
    opacity: 0.6;
    transition: opacity 0.15s;
}
.topbar-search:hover .topbar-search-shortcut { opacity: 1; }
.topbar-search input:focus ~ .topbar-search-shortcut { display: none; }

/* ============================================================
   LAST-LOGIN CHIP — step 27
   Tone-graded by recency so an admin can scan the Users list and
   spot abandoned accounts at a glance:
     fresh     (<24h)        green
     recent    (1–7d)        blue
     normal    (7–30d)       slate
     stale     (30–90d)      amber
     abandoned (>90d, never) red / amber
   ============================================================ */
.last-login-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 10px;
    border-radius: var(--r-pill);
    font-size: 11px;
    font-weight: var(--fw-medium);
    line-height: 1.3;
    white-space: nowrap;
    cursor: help;
}
.last-login-chip i {
    font-size: 11px;
    opacity: 0.7;
    flex-shrink: 0;
}
.last-login-chip.is-fresh     { background: var(--green-50);  color: var(--status-green-700);  }
.last-login-chip.is-recent    { background: var(--blue-50);   color: var(--status-blue-700);   }
.last-login-chip.is-normal    { background: #F1F5F9;          color: #475569;                  }
.last-login-chip.is-stale     { background: var(--amber-50);  color: var(--status-amber-700); }
.last-login-chip.is-abandoned { background: var(--red-50);    color: var(--status-red-700);   }
.last-login-chip.is-never     { background: var(--amber-50);  color: var(--status-amber-700); }


/* ============================================================
   AUDIT "CHANGED BY" CHIP — step 26
   Renders the most-recent actor (creator or last editor) plus an
   absolute timestamp. Hover shows the full breakdown via title=.
   Used in the WorkingShifts and Schedules tables.
   ============================================================ */
.changed-by-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 10px;
    border-radius: var(--r-pill);
    background: #F1F5F9;                /* slate-100 */
    color: #475569;                     /* slate-600 */
    font-size: 11px;
    font-weight: var(--fw-medium);
    line-height: 1.3;
    white-space: nowrap;
    /* The title= tooltip is the actionable thing; no pointer needed */
    cursor: help;
}
.changed-by-chip i {
    font-size: 11px;
    opacity: 0.7;
    flex-shrink: 0;
}
.changed-by-chip:hover {
    background: #E2E8F0;                /* slate-200 */
}


/* ============================================================
   DEVICE STATUS PILL — clickable when not online
   The pill is the SINGLE signal AND the SINGLE click target for
   "why is this device offline?". Hover shows a one-line hint
   (via title=); click opens the live diagnose modal. Two variants
   exist in the codebase — `.status-pill` (Devices list) and
   `.dash-status-pill` (Dashboard) — both get the same affordance.
   ============================================================ */
.status-pill.is-clickable,
.dash-status-pill.is-clickable {
    cursor: pointer;
    transition: filter 0.1s, box-shadow 0.1s, transform 0.1s;
}
.status-pill.is-clickable:hover,
.dash-status-pill.is-clickable:hover {
    filter: brightness(0.95);
    box-shadow: 0 0 0 2px rgba(15, 23, 42, 0.06);
}
.status-pill.is-clickable:active,
.dash-status-pill.is-clickable:active {
    transform: translateY(1px);
}
.status-pill.is-clickable:focus-visible,
.dash-status-pill.is-clickable:focus-visible {
    outline: 2px solid var(--status-blue-700);
    outline-offset: 2px;
}


/* Mobile: take over the full screen so the keyboard has room */
@media (max-width: 575.98px) {
    .cmd-palette-modal {
        margin: 0;
        width: 100%;
        max-width: none;
        height: 100vh;
        max-height: 100vh;
        border-radius: 0;
        border: 0;
    }
    .cmd-palette-results { max-height: calc(100vh - 130px); }
    .cmd-palette-footer  { display: none; }   /* keyboard hints irrelevant on touch */
    .topbar-search-shortcut { display: none; }
}


/* ============================================================
   STEP 28 — EMPLOYEES SCREEN
   New aesthetic shared with /Reports/ByDate + Dashboard.
   • Sticky-table layout reuses .bydate-* classes from Reports
   • Per-row gender-keyed avatar (falls back from photo)
   • Bulk-select bar appears when rows are checked
   • Per-row action menu (⋯) replaces inline button cluster
   (KPI tile row was removed — the chip badges already carry
    the same counts, no need to duplicate.)
   ============================================================ */

/* ---- Step 31b — flat device-name cell (NOT sticky).
   Stacks alias + #serial vertically inside a single normal-flow <td>. ---- */
.dv-name-cell {
    display: flex;
    flex-direction: column;
    gap: 1px;
    line-height: 1.25;
    /* Anchor both lines to the cell's start edge (right in RTL) so the
       alias and serial line up at the same edge regardless of length. */
    align-items: flex-end;
}
[dir="ltr"] .dv-name-cell { align-items: flex-start; }

/* ---- Step 31c — small info (i) icon next to the status pill.
   Replaces the per-row "⋯" menu — clicking opens the live diagnose
   verdict for that device. Always visible (offline OR online) so the
   action is discoverable; hover lifts to indicate it's clickable. ---- */
.dv-status-wrap {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.dv-status-info {
    width: 22px; height: 22px;
    display: inline-grid; place-items: center;
    border: 1px solid transparent;
    border-radius: 6px;
    background: transparent;
    color: var(--text-tertiary);
    cursor: pointer;
    transition: background 0.1s, color 0.1s, border-color 0.1s;
    flex-shrink: 0;
    padding: 0;
}
.dv-status-info:hover {
    background: var(--bg-hover);
    color: var(--status-blue-700);
    border-color: var(--border-subtle);
}
.dv-status-info:focus-visible {
    outline: 2px solid var(--status-blue-700);
    outline-offset: 1px;
}
.dv-status-info i { font-size: 13px; line-height: 1; }

/* ---- Step 31a — capacity meters on /Devices.
   Each meter is a single horizontal row: icon + fraction + tiny bar + %.
   Stacked variant (.dv-cap-stack) collapses 4 meters into one cell for
   the combined "Capacity" column. Tones (.ok / .warn / .danger) are
   driven by JS so a glance at the column surfaces saturated devices. ---- */
.dv-cap-meter {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: var(--fs-meta);
    color: var(--text-secondary);
    line-height: 1.3;
    white-space: nowrap;
}
/* Step 31h — combined "Capacity" cell lays its chips out as a 2×2 grid:
   users + fingerprints on top, faces + records below. CSS Grid with two
   auto columns keeps each chip at its natural width while still aligning
   the second column to a single visual edge. */
.dv-cap-stack {
    display: grid;
    grid-template-columns: repeat(2, auto);
    gap: 4px 6px;
    align-items: center;
    justify-content: start;
    /* `justify-content: start` (instead of the default `stretch`) prevents
       the columns from filling the full cell width — chips keep hugging
       their content even when the column is wide. */
}
.dv-cap-row {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: var(--fs-meta);
    color: var(--text-secondary);
    line-height: 1.2;
    white-space: nowrap;
}
/* Step 31i — capacity chip restyled to match the .bydate-chip pattern
   used for status filter chips elsewhere in the app:

      ┌────────────────────────────┐
      │  👥   [ 245/3000 ]         │
      └────────────────────────────┘
         ▲           ▲
         │           └── inner pill (tone-100, smaller, holds the number)
         └─ outer pill (tone-50, holds the icon + the inner pill)

   This is the same DOM shape as a Reports/ByDate filter chip — a
   coloured outer rounded pill with a darker inner count-pill — so the
   visual language is now consistent across the whole app. */
.dv-cap-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding-block: 3px;
    padding-inline-start: 9px;
    padding-inline-end: 3px;
    border-radius: var(--r-pill, 999px);
    line-height: 1;
    white-space: nowrap;
    flex-shrink: 0;
    /* Default (no data-cap): neutral slate. Overrides below per metric. */
    background: var(--bg-hover);
    color: var(--text-secondary);
}
.dv-cap-chip-icon {
    display: inline-flex;
    align-items: center;
    font-size: 12px;
    line-height: 1;
    /* Color inherits from the outer chip. */
}
.dv-cap-chip-num {
    display: inline-flex;
    align-items: center;
    padding: 3px 8px;
    border-radius: var(--r-pill, 999px);
    background: var(--bg-input);
    color: var(--text-primary);
    font-family: 'Inter', system-ui, sans-serif;
    font-variant-numeric: tabular-nums;
    font-size: 11px;
    font-weight: var(--fw-medium);
    line-height: 1.1;
}
.dv-cap-chip-num-sep { opacity: 0.55; font-weight: var(--fw-regular); margin: 0 2px; }
/* Step 31k — placeholder rendered when the device hasn't reported its max
   capacity yet. Italic + dimmed so the eye reads it as "TBD" rather than
   a literal value of "?". The tooltip on the chip explains the wait. */
.dv-cap-chip-num-unknown {
    font-style: italic;
    font-weight: var(--fw-regular);
    opacity: 0.55;
    cursor: help;
}

/* Per-metric pastel families. Outer chip uses the tone's -50 shade; the
   inner number pill uses the tone's -100 (one step darker), and both
   surface text in the tone's -700 for AA contrast. */
.dv-cap-chip[data-cap="users"]    { background: var(--blue-50);  color: var(--status-blue-700); }
.dv-cap-chip[data-cap="users"]    .dv-cap-chip-num  { background: #DBEAFE; color: var(--status-blue-700); }

.dv-cap-chip[data-cap="records"]  { background: var(--amber-50); color: var(--status-amber-700); }
.dv-cap-chip[data-cap="records"]  .dv-cap-chip-num  { background: #FEF3C7; color: var(--status-amber-700); }
/* Step 31j — "fingers" + "faces" tones removed alongside their chips.
   In a pure-attendance product they were diagnostic noise (counts roughly
   correlate to user count). The CSS rules and Domain entity columns
   `MaxFingerprintCount` / `MaxFaceCount` remain available so the API
   keeps returning them; the UI just doesn't surface them anymore. */
.dv-cap-bar {
    flex: 1 1 auto;
    height: 6px;
    min-width: 40px;
    max-width: 90px;
    background: var(--bg-input);
    border-radius: 999px;
    overflow: hidden;
    display: inline-block;
}
.dv-cap-bar-fill {
    display: block;
    height: 100%;
    border-radius: inherit;
    transition: width 0.25s ease;
}
.dv-cap-bar-fill[data-tone="ok"]      { background: #22C55E; }   /* green-500 */
.dv-cap-bar-fill[data-tone="warn"]    { background: #F59E0B; }   /* amber-500 */
.dv-cap-bar-fill[data-tone="danger"]  { background: #EF4444; }   /* red-500   */
.dv-cap-bar-fill[data-tone="unknown"] { background: var(--border-default); }
.dv-cap-pct {
    font-family: 'Inter', system-ui, sans-serif;
    font-variant-numeric: tabular-nums;
    color: var(--text-tertiary);
    font-size: 10px;
    flex-shrink: 0;
    min-width: 26px;
    text-align: end;
}

/* ---- Filter chips for /Devices (Step 30): online / idle / offline / has-pending.
   Reuses the same pastel + count-pill anatomy as the Reports/Employees chips
   so the visual language stays unified across screens. ---- */
.bydate-chip[data-status="online"]      { background: var(--green-50); color: var(--status-green-700); }
.bydate-chip[data-status="online"] .bydate-chip-count {
    background: #D1FAE5;            /* green-100 */
    color: var(--status-green-700);
}
.bydate-chip[data-status="idle"]        { background: var(--amber-50); color: var(--status-amber-700); }
.bydate-chip[data-status="idle"] .bydate-chip-count {
    background: #FEF3C7;            /* amber-100 */
    color: var(--status-amber-700);
}
.bydate-chip[data-status="offline"]     { background: var(--red-50);   color: var(--status-red-700); }
.bydate-chip[data-status="offline"] .bydate-chip-count {
    background: #FEE2E2;            /* red-100 */
    color: var(--status-red-700);
}
.bydate-chip[data-status="has-pending"] { background: var(--blue-50);  color: var(--status-blue-700); }
.bydate-chip[data-status="has-pending"] .bydate-chip-count {
    background: #DBEAFE;            /* blue-100 */
    color: var(--status-blue-700);
}

/* ---- Filter chips for Employees (extends .bydate-chip data-status set) ---- */
.bydate-chip[data-status="no-name"]  { background: var(--amber-50);  color: var(--status-amber-700); }
.bydate-chip[data-status="no-name"]  .bydate-chip-count {
    background: #FEF3C7;
    color: var(--status-amber-700);
}
.bydate-chip[data-status="no-bio"]   { background: var(--purple-50); color: var(--status-purple-700); }
.bydate-chip[data-status="no-bio"]   .bydate-chip-count {
    background: #EDE9FE;
    color: var(--status-purple-700);
}
.bydate-chip[data-status="no-group"] { background: var(--blue-50);   color: var(--status-blue-700); }
.bydate-chip[data-status="no-group"] .bydate-chip-count {
    background: #DBEAFE;
    color: var(--status-blue-700);
}

/* ---- Bulk-action bar (transient, slides in over the toolbar) ---- */
.emp-bulk-bar {
    background: var(--blue-50);
    border-top: 1px solid #BFDBFE;
    border-radius: 8px;
    padding: 8px 12px;
    margin-top: 8px;
    align-items: center;
    justify-content: space-between;
}
.emp-bulk-bar[hidden] { display: none !important; }

/* ---- Avatar (gender-keyed silhouette fallback) ---- */
.emp-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    overflow: hidden;
    flex-shrink: 0;
    background: var(--bg-input);
    color: var(--text-tertiary);
}
.emp-avatar svg { width: 65%; height: 65%; }
.emp-avatar--photo   { object-fit: cover; border: 1px solid var(--border-subtle); }
.emp-avatar--male    { background: #DBEAFE; color: #1D4ED8; }
.emp-avatar--female  { background: #FCE7F3; color: #BE185D; }
.emp-avatar--neutral { background: #E2E8F0; color: #64748B; }

/* ---- Sticky-col cell layout: avatar + (name on top / pin underneath) ----
   DOM order is [avatar] [name-stack]. We anchor the group to the
   inline-START so the avatar lands on the reading-start edge — the
   RIGHT side in Arabic, the LEFT side in English. The name sits to
   the avatar's inline-end side, which is what every Arabic app
   (Slack, Twitter, WhatsApp) does.                                       */
.emp-name-cell {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 10px;
    justify-content: flex-start;
}
.emp-name-stack {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    /* Name + #PIN inside the stack align with the writing start so
       multi-line names hug the avatar instead of drifting away. */
    text-align: start;
}
.emp-name-text {
    font-size: var(--fs-secondary);
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    line-height: 1.2;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 160px;
}
.emp-pin-text {
    font-family: 'Inter', system-ui, sans-serif;
    font-size: 11px;
    color: var(--text-placeholder);
    font-variant-numeric: tabular-nums;
    line-height: 1;
}

/* ---- Selected-row highlight (cb checked state) ---- */
.bydate-table tbody tr.is-selected td {
    background: var(--blue-50);
}

/* ---- Employees screen: thin leading bulk-select column + thin trailing
   actions column. Scoped to the unified single-table layout so they
   don't affect any other table that uses .bydate-table. ---- */
.bydate-table .emp-cb-col {
    width: 36px;
    min-width: 36px;
    padding-inline-start: 12px;
    padding-inline-end: 6px;
    text-align: start;
}
.bydate-table .emp-cb-col input[type="checkbox"] {
    margin: 0;
}
.bydate-table .emp-actions-col {
    /* Step 33-disc — Widened from 60→90px to fit the Schedule shortcut
       icon next to the kebab without crowding. The two buttons sit
       inline with a 2-px gap. */
    width: 90px;
    min-width: 90px;
    padding-inline-start: 6px;
    padding-inline-end: 8px;
    text-align: center;
    white-space: nowrap;
}
.bydate-table .emp-actions-col > .emp-row-actions-btn + .emp-row-actions-btn {
    margin-inline-start: 2px;
}
/* Step 33-disc — Schedule shortcut button is the same shape as the kebab
   button but with a blue-tinted icon so the operator can scan the column
   and spot the "Schedule" action at a glance. */
.emp-row-schedule-btn {
    color: var(--status-blue-700) !important;
}
.emp-row-schedule-btn:hover {
    background: var(--blue-50) !important;
    border-color: #BFDBFE !important;
}

/* ---- Per-row action button (⋯) ---- */
.emp-row-actions-btn {
    width: 28px; height: 28px;
    display: inline-grid; place-items: center;
    border: 1px solid transparent;
    border-radius: 6px;
    background: transparent;
    color: var(--text-secondary);
    cursor: pointer;
    transition: background 0.1s, border-color 0.1s;
}
.emp-row-actions-btn:hover {
    background: var(--bg-hover);
    border-color: var(--border-subtle);
    color: var(--text-primary);
}
.emp-row-actions-btn:focus-visible {
    outline: 2px solid var(--status-blue-700);
    outline-offset: 1px;
}

/* ---- Floating action menu (rendered into <body> by JS) ---- */
.emp-action-menu {
    background: var(--bg-card);
    border: 1px solid var(--border-default);
    border-radius: 10px;
    box-shadow: 0 12px 28px -10px rgba(15, 23, 42, 0.18);
    padding: 6px;
    z-index: 1080;
    min-width: 200px;
}
.emp-action-menu .bydate-dropdown-item:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}


/* ============================================================
   STEP 29 — REAL-TIME MONITORING SCREEN
   Reuses .bydate-* (page chrome / table) + .dash-stat (KPI tiles
   from the Dashboard, shared vocabulary). Adds only what's unique
   to this screen:
   • LIVE pulsing dot in the page header
   • Today's date line under the title
   • 4-column KPI grid override (Dashboard's default is 3)
   • Inline column-header filter row (sticky with the thead)
   • Compact per-cell variants for employee/device columns
   ============================================================ */

/* Step 29.2/29.3/29.4 — KPI tile grid was retired in favour of a row of
   .bydate-chip pills that now live INSIDE the toolbar card (above the
   action/filter row) — same structural pattern /Reports/ByDate uses. The
   chip styling itself is shared (.bydate-chip), with the .is-static
   modifier (defined below) suppressing the hover/cursor used by the
   filterable Reports chips. */

/* ---- LIVE indicator dot — pulses while connected, dims when SignalR drops ---- */
.rtm-live-dot {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: #16A34A;            /* emerald-600 — slightly darker than green-500 for AA on white */
    margin-inline-start: 10px;
    box-shadow: 0 0 0 0 rgba(22, 163, 74, 0.55);
    animation: rtm-pulse 1.6s ease-out infinite;
    vertical-align: middle;
    flex-shrink: 0;
}
.rtm-live-dot.is-stale {
    background: #94A3B8;            /* slate-400 — no pulse when offline */
    animation: none;
    box-shadow: none;
}
@keyframes rtm-pulse {
    0%   { box-shadow: 0 0 0 0   rgba(22, 163, 74, 0.55); }
    70%  { box-shadow: 0 0 0 10px rgba(22, 163, 74, 0);    }
    100% { box-shadow: 0 0 0 0   rgba(22, 163, 74, 0);    }
}

/* ---- Today's date line under the page title (sits inside .bydate-header
   alongside .bydate-crumb + .bydate-title). The date is informational
   only; the live status chips moved into the toolbar card alongside
   the filter buttons in Step 29.4. ---- */
.rtm-today {
    color: var(--text-secondary);
    font-size: var(--fs-secondary);
    font-weight: var(--fw-regular);
    margin-top: 4px;
    line-height: 1.3;
}

/* Static (non-interactive) variant of .bydate-chip — used by the four
   display-only stat chips on the Real-Time Monitoring screen. Removes
   the cursor:pointer + hover filter the filterable Reports chips have,
   while keeping the pastel tones so the visual language stays
   consistent across screens. */
.bydate-chip.is-static {
    cursor: default;
}
.bydate-chip.is-static:hover {
    filter: none;
    box-shadow: none;
}
.bydate-chip.is-static:focus-visible {
    outline: none;
}

/* ---- Scroll wrapper (no sticky-employee column on this screen) ---- */
.bydate-table-card .rtm-scroll {
    max-height: calc(100vh - 320px);
    min-height: 360px;
    overflow: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--border-default) transparent;
}
.bydate-table-card .rtm-scroll::-webkit-scrollbar { height: 8px; width: 8px; }
.bydate-table-card .rtm-scroll::-webkit-scrollbar-thumb { background: var(--border-default); border-radius: 4px; }

/* Filter inputs live in the toolbar card next to the action buttons —
   sized to match .bydate-btn (36px tall) so action buttons and filters
   read as a single, coherent command bar. Search input is wider than
   the dropdowns by default so the placeholder text fits. */
.rtm-filter-input {
    height: 36px;
    padding: 0 12px;
    border: 1px solid var(--border-default);
    border-radius: 10px;
    background: var(--bg-card);
    color: var(--text-primary);
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
    font-size: var(--fs-secondary);
    line-height: 1;
    transition: border-color 0.1s, box-shadow 0.1s;
    /* Keep selects narrow but not cramped; search gets its own override below. */
    min-width: 130px;
}
.rtm-filter-input:hover  { border-color: #CBD5E1; }
.rtm-filter-input:focus  { outline: none; border-color: var(--text-primary); box-shadow: 0 0 0 3px rgba(15, 23, 42, 0.06); }
.rtm-filter-input::placeholder { color: var(--text-placeholder); }
.rtm-filter-search {
    /* Wider so "Search by name or PIN" placeholder fits without truncation
       on either locale. Below the 1199px breakpoint the toolbar wraps anyway. */
    min-width: 220px;
    flex: 1 1 220px;
    max-width: 320px;
}

/* Vertical divider between Action buttons (Pull / Refresh) and Filters.
   Pure visual separator — keeps the bar readable when both groups co-exist. */
.rtm-toolbar-divider {
    display: inline-block;
    width: 1px;
    align-self: stretch;
    background: var(--border-subtle);
    margin: 0 4px;
}

/* ---- Employee + Device cell — name on top, secondary identifier under ---- */
.rtm-emp {
    display: flex;
    align-items: baseline;
    gap: 8px;
    flex-wrap: wrap;
    justify-content: flex-end;       /* RTL: anchor to inline-end */
}
.rtm-emp-name {
    font-size: var(--fs-secondary);
    font-weight: var(--fw-medium);
    color: var(--text-primary);
}
.rtm-emp-pin {
    font-family: 'Inter', system-ui, sans-serif;
    font-size: 11px;
    color: var(--text-placeholder);
    font-variant-numeric: tabular-nums;
}
.rtm-device-name {
    font-size: var(--fs-secondary);
    color: var(--text-primary);
    margin-inline-end: 6px;
}
.rtm-device-sn {
    font-family: 'Inter', system-ui, sans-serif;
    font-size: 11px;
    color: var(--text-placeholder);
}

/* ---- Verify-method pill (replaces the old gray text label) ---- */
.rtm-verify-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 3px 8px;
    background: var(--bg-input);
    color: var(--text-secondary);
    border-radius: 6px;
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    line-height: 1.2;
    white-space: nowrap;
}
.rtm-verify-pill i { font-size: 12px; }
/* "weekly-off" status tone (slate gray) is already defined further up in
   this stylesheet at the .bydate-status[data-tone="weekly-off"] rule. We
   reuse it here for AttendanceLog status code 255 (a punch with no IN/OUT
   direction) so the renderer doesn't need a fifth tone. */


/* ============================================================
   STEP 31e — NOTION-STYLE MODAL PRIMITIVES
   Used by the /Devices "Add device" modal; designed to be reusable
   for future modals (Edit device, etc.) so we have ONE consistent
   modal language across the app.

   Primitives:
     .notion-modal              shell — rounded, subtle border, soft shadow
     .notion-tabs / .notion-tab underline-style tab strip (replaces nav-tabs)
     .notion-field*             form-field block (label + input + hint)
     .notion-input              40px-tall input with focus ring
     .notion-divider            slate-100 horizontal rule
     .notion-note               soft info / neutral / warn panel
     .notion-error              red error caption under a field
     .notion-grid-2             2-up form grid (responsive collapse)
   And scan-specific:
     .notion-scan-controls      from / arrow / to / timeout row
     .notion-port-pills         port-toggle pill group
     .notion-scan-list          stack of result rows
     .notion-scan-row           one result row (dot · IP · port · tag · action)
   ============================================================ */

/* ---- Modal shell ---- */
.notion-modal .modal-content {
    border: 1px solid var(--border-default);
    border-radius: 16px;
    box-shadow: 0 14px 32px -10px rgba(15, 23, 42, 0.18);
}
.notion-modal .modal-header {
    border-bottom: 1px solid var(--border-subtle);
    padding: 20px 24px 16px;
    align-items: flex-start;
}
.notion-modal .modal-header .btn-close {
    margin-top: 4px;
    margin-bottom: 0;
}
.notion-modal .modal-title {
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
    font-size: 18px;
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    line-height: 1.2;
}
.notion-modal-subtitle {
    margin-top: 4px;
    font-size: var(--fs-meta);
    color: var(--text-tertiary);
    line-height: 1.4;
}

/* ============================================================
   .zk-confirm-* — UNIFIED CONFIRMATION DIALOG (primitive)
   Replaces 5 previous patterns:
     • bk-pwd-overlay     (Backup)
     • dvFactoryResetOverlay (Devices)
     • empDelConfirmOverlay (Employees bulk delete)
     • zkPwdModal         (ZkPush.promptPassword in _Layout)
     • the old ZkPush.confirm() Bootstrap-modal in admin.js

   Three modes (driven from JS, all share the same chrome):
     • simple      — yes / no                       → resolves bool
     • dangerous   — yes / no + password             → resolves { password } or null
     • destructive — yes / no + password + phrase    → resolves { password, phrase } or null

   Plus an optional `extras` block (checkboxes) for things like
   "Create snapshot first" on bulk-delete.

   Anatomy is self-contained (no Bootstrap.Modal dependency). The
   previous Bootstrap-based modal had reliability issues with stale
   instances + lingering backdrops; classlist toggling on a fixed
   overlay is bullet-proof.
   ============================================================ */
.zk-confirm-overlay {
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.45);
    z-index: 1090;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transition: opacity 0.12s;
}
.zk-confirm-overlay[hidden] { display: none; }
.zk-confirm-overlay.is-open { opacity: 1; }

.zk-confirm-card {
    width: min(440px, calc(100vw - 32px));
    background: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-radius: 16px;
    box-shadow:
        0 1px 2px rgba(15, 23, 42, 0.04),
        0 20px 50px -8px rgba(15, 23, 42, 0.18);
    transform: translateY(-4px);
    transition: transform 0.15s;
    overflow: hidden;
}
.zk-confirm-overlay.is-open .zk-confirm-card { transform: translateY(0); }
[data-bs-theme="dark"] .zk-confirm-card {
    box-shadow:
        0 1px 2px rgba(0, 0, 0, 0.40),
        0 20px 50px -8px rgba(0, 0, 0, 0.60);
}

.zk-confirm-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    padding: 18px 20px 12px;
    border-bottom: 1px solid var(--border-subtle);
}
.zk-confirm-header-text { flex: 1; min-width: 0; }
.zk-confirm-title {
    margin: 0;
    font-size: var(--fs-card-title);
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    line-height: 1.3;
    display: flex;
    align-items: center;
    gap: 8px;
}
.zk-confirm-subtitle {
    margin-top: 4px;
    font-size: var(--fs-meta);
    color: var(--text-secondary);
    line-height: 1.45;
}
.zk-confirm-close {
    flex-shrink: 0;
    background: transparent;
    border: 0;
    padding: 4px;
    color: var(--text-tertiary);
    cursor: pointer;
    font-size: 18px;
    line-height: 1;
}
.zk-confirm-close:hover { color: var(--text-primary); }

/* Kind-tinted title icon. Mirrors the kind ladder used by .notion-note
   so the dialog reads at a glance: info (blue) / warn (amber) /
   danger (red) / primary (slate). */
.zk-confirm-title i.zk-confirm-title-icon {
    font-size: 18px;
    flex-shrink: 0;
}
.zk-confirm-card[data-kind="primary"] .zk-confirm-title-icon { color: var(--text-secondary); }
.zk-confirm-card[data-kind="info"]    .zk-confirm-title-icon { color: var(--status-blue-700); }
.zk-confirm-card[data-kind="warn"]    .zk-confirm-title-icon { color: var(--status-amber-700); }
.zk-confirm-card[data-kind="danger"]  .zk-confirm-title-icon { color: var(--status-red-700); }

.zk-confirm-body {
    padding: 18px 20px;
}
.zk-confirm-message {
    font-size: var(--fs-secondary);
    color: var(--text-primary);
    line-height: 1.55;
    margin: 0 0 16px;
    white-space: pre-wrap;
}
.zk-confirm-message:last-child { margin-bottom: 0; }

/* Field stack inside the body — reuses .notion-field / .notion-input
   so the dialog ties into the rest of the design system. */
.zk-confirm-fields {
    display: flex;
    flex-direction: column;
    gap: 14px;
}
.zk-confirm-fields:empty { display: none; }

/* Extras row (checkbox-style toggles, e.g. "Create snapshot first").
   Slim hint text below for context. */
.zk-confirm-extra {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    font-size: var(--fs-meta);
    color: var(--text-secondary);
    cursor: pointer;
    user-select: none;
    line-height: 1.4;
}
.zk-confirm-extra input[type="checkbox"] {
    width: 16px; height: 16px;
    margin-top: 1px;
    accent-color: var(--text-primary);
    cursor: pointer;
    flex-shrink: 0;
}
.zk-confirm-extra-hint {
    margin-top: 2px;
    margin-inline-start: 24px;
    font-size: 11px;
    color: var(--text-tertiary);
    line-height: 1.4;
}
.zk-confirm-extra-hint:empty { display: none; }

/* Inline error caption — visible only when validation fails. */
.zk-confirm-error {
    margin-top: 12px;
    padding: 10px 12px;
    background: var(--red-50);
    border: 1px solid #FECACA;
    border-radius: 10px;
    color: var(--status-red-700);
    font-size: var(--fs-meta);
    line-height: 1.4;
}
.zk-confirm-error[hidden] { display: none; }
[data-bs-theme="dark"] .zk-confirm-error { border-color: rgba(248, 113, 113, 0.35); }

/* Footer — Cancel on the inline-start, primary (kind-tinted) on the end. */
.zk-confirm-footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    padding: 14px 20px;
    border-top: 1px solid var(--border-subtle);
    background: var(--bg-card);
}
.zk-confirm-footer .bydate-btn { min-height: 36px; padding: 6px 14px; }
.zk-confirm-footer .zk-confirm-ok[data-kind="primary"] {
    background: var(--text-primary); color: #FFF; border-color: var(--text-primary);
}
.zk-confirm-footer .zk-confirm-ok[data-kind="info"] {
    background: var(--status-blue-700); color: #FFF; border-color: var(--status-blue-700);
}
.zk-confirm-footer .zk-confirm-ok[data-kind="warn"] {
    background: var(--status-amber-700); color: #FFF; border-color: var(--status-amber-700);
}
.zk-confirm-footer .zk-confirm-ok[data-kind="danger"] {
    background: var(--status-red-700); color: #FFF; border-color: var(--status-red-700);
}
.zk-confirm-footer .zk-confirm-ok:hover { filter: brightness(0.92); }

/* Narrow viewport: shrink padding so the card breathes on phones. */
@media (max-width: 480px) {
    .zk-confirm-card  { border-radius: 14px; }
    .zk-confirm-header { padding: 14px 16px 10px; }
    .zk-confirm-body   { padding: 14px 16px; }
    .zk-confirm-footer { padding: 12px 16px; flex-wrap: wrap; }
}
.notion-modal .modal-body { padding: 22px 24px; }
.notion-modal .modal-footer {
    border-top: 1px solid var(--border-subtle);
    padding: 14px 24px;
    gap: 8px;
}

/* ---- Narrow-viewport compact mode for Notion modals ----
   Below 480px (mobile portrait): tighten paddings + collapse the
   modal-dialog margin so the modal fills most of the viewport with
   only 8px on each side. Footer also stacks the buttons so a long
   "Save" + "Cancel" pair doesn't clip the dialog edge. */
@media (max-width: 479.98px) {
    .notion-modal .modal-dialog {
        margin: 8px;
        max-width: calc(100vw - 16px);
    }
    .notion-modal .modal-content { border-radius: 12px; }
    .notion-modal .modal-header  { padding: 14px 16px 12px; }
    .notion-modal .modal-body    { padding: 16px; }
    .notion-modal .modal-footer  { padding: 12px 16px; flex-wrap: wrap; }
    /* Stack footer buttons to full width on very narrow screens — easier
       tap targets, no horizontal scrolling for long labels. */
    .notion-modal .modal-footer > .bydate-btn { flex: 1 1 100%; justify-content: center; }
    .notion-modal-subtitle { display: none; }     /* drops the smaller caption */
    /* 2-column grid collapses to 1 on phones so fields aren't half-width. */
    .notion-modal .notion-grid-2 { grid-template-columns: 1fr; }
}

/* ---- Tabs (underline style) ---- */
.notion-tabs {
    display: flex;
    gap: 4px;
    border-bottom: 1px solid var(--border-subtle);
    margin-bottom: 22px;
}

/* Hub sub-navigation tab strip — used at the top of /Devices, /Employees,
   /Reports and their sibling lookup pages. Sits between the page title
   and the first content card. Marker class so we can apply tweaks
   specific to this usage without touching the .notion-tabs primitive
   (which is also used in modals and the bell panel). */
.hub-subnav {
    margin-top: -8px;             /* tighten gap to the page title */
    margin-bottom: 18px;
    overflow-x: auto;             /* horizontal scroll on narrow screens */
    flex-wrap: nowrap;
    scrollbar-width: none;        /* hide scrollbar — tabs aren't a scroll surface visually */
}
.hub-subnav::-webkit-scrollbar { display: none; }
.hub-subnav .notion-tab { white-space: nowrap; flex-shrink: 0; }
/* Anchor tabs (vs. the original <button> tabs) need decoration reset. */
.hub-subnav a.notion-tab { text-decoration: none; }
.notion-tab {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 40px;
    padding: 0 14px;
    border: none;
    background: transparent;
    color: var(--text-secondary);
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
    font-size: var(--fs-secondary);
    font-weight: var(--fw-regular);
    cursor: pointer;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;     /* let the active underline cover the strip border */
    transition: color 0.1s, border-color 0.1s;
}
.notion-tab i { font-size: 13px; }
.notion-tab:hover { color: var(--text-primary); }
.notion-tab.is-active,
.notion-tab.active {
    color: var(--text-primary);
    font-weight: var(--fw-medium);
    border-bottom-color: var(--text-primary);
}
.notion-tab:focus-visible {
    outline: 2px solid var(--status-blue-700);
    outline-offset: 2px;
    border-radius: 4px;
}

/* Disabled tab — used when a feature is administratively turned off
   (e.g. Custom Reports while the flag is false). Rendered as a <span>
   so it's non-clickable; the cursor + faded color + "Soon" pill make
   the state obvious. The hover color is locked to placeholder so
   the underline cue from .notion-tab:hover doesn't fire. */
.notion-tab.is-disabled {
    color: var(--text-placeholder);
    cursor: not-allowed;
    opacity: 0.75;
}
.notion-tab.is-disabled:hover { color: var(--text-placeholder); }
.notion-tab.is-disabled i.bi-lock-fill {
    font-size: 11px;
    opacity: 0.7;
}

/* "Soon" pill that sits inside a disabled tab. Reusable for any future
   "feature exists but currently off" UX. Tiny so it nests cleanly
   inside the 40-px tab strip without visual jostling. */
.notion-tab-badge {
    display: inline-flex;
    align-items: center;
    height: 16px;
    padding: 0 6px;
    margin-inline-start: 2px;
    background: var(--bg-input);
    color: var(--text-tertiary);
    font-size: 9px;
    font-weight: var(--fw-medium);
    border-radius: 4px;
    line-height: 1;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    /* Tabular numerals + Inter for any digit-based variant text */
    font-family: 'Inter', system-ui, sans-serif;
}

/* ---- Form fields ---- */
.notion-field {
    margin-bottom: 18px;
    min-width: 0;
}
.notion-field-label {
    display: block;
    font-size: var(--fs-meta);
    color: var(--text-secondary);
    font-weight: var(--fw-medium);
    margin-bottom: 6px;
    line-height: 1.3;
}
.notion-field-required {
    color: var(--status-red-700);
    font-weight: var(--fw-medium);
    margin-inline-start: 2px;
}
.notion-input {
    width: 100%;
    height: 40px;
    border: 1px solid var(--border-default);
    border-radius: 10px;
    padding: 0 14px;
    background: var(--bg-card);
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
    font-size: var(--fs-secondary);
    color: var(--text-primary);
    transition: border-color 0.1s, box-shadow 0.1s;
    appearance: none;
    /* RTL-aware placeholder + caret position is handled by the container's dir */
}
.notion-input:hover  { border-color: #CBD5E1; }
.notion-input:focus  {
    outline: none;
    border-color: var(--text-primary);
    box-shadow: 0 0 0 3px rgba(15, 23, 42, 0.06);
}
.notion-input::placeholder { color: var(--text-placeholder); }
.notion-input.mono { font-family: 'Inter', 'Consolas', monospace; }
/* Step 31m — read-only state for fields that aren't editable in the
   current modal mode (e.g. SN in edit mode). Visually de-emphasises so
   the eye doesn't try to click it, while keeping the value readable. */
.notion-input.is-locked,
.notion-input[readonly] {
    background: var(--bg-input);
    color: var(--text-secondary);
    border-color: var(--border-subtle);
    cursor: default;
}
.notion-input.is-locked:hover,
.notion-input[readonly]:hover { border-color: var(--border-subtle); }
.notion-input.is-locked:focus,
.notion-input[readonly]:focus {
    outline: none;
    border-color: var(--border-default);
    box-shadow: none;
}
/* <select> chevron on the leading edge for our compact look */
select.notion-input {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 16 16' fill='none' stroke='%2364748B' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='4 6 8 10 12 6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: left 14px center;
    padding-inline-start: 14px;
    padding-inline-end: 36px;
}
[dir="ltr"] select.notion-input { background-position: right 14px center; }

/* Step 32 — multi-line variant for Address / Notes fields. Same focus
   ring + border tokens as .notion-input but allows content to grow with
   the row count instead of being clipped to 40px. */
textarea.notion-input.notion-textarea,
.notion-input.notion-textarea {
    height: auto;
    min-height: 80px;
    padding: 10px 14px;
    line-height: 1.5;
    resize: vertical;
}

.notion-field-hint {
    margin-top: 6px;
    font-size: var(--fs-meta);
    color: var(--text-tertiary);
    line-height: 1.35;
}

/* ---- Section divider ---- */
.notion-divider {
    border: 0;
    border-top: 1px solid var(--border-subtle);
    margin: 8px 0 18px;
    opacity: 1;
}

/* ---- Notes (info / neutral / warn) ---- */
.notion-note {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 14px;
    background: var(--blue-50);
    border: 1px solid #BFDBFE;
    border-radius: 12px;
    color: var(--status-blue-700);
    font-size: var(--fs-meta);
    line-height: 1.5;
    margin-top: 4px;
}
.notion-note i { font-size: 14px; flex-shrink: 0; margin-top: 2px; }
.notion-note-neutral {
    background: var(--bg-input);
    border-color: var(--border-subtle);
    color: var(--text-secondary);
    margin-bottom: 18px;
}
.notion-note-warn {
    background: var(--amber-50);
    border-color: #FDE68A;
    color: var(--status-amber-700);
}
.notion-note-success {
    background: var(--green-50);
    border-color: #BBF7D0;
    color: var(--status-green-700);
}

/* ============================================================
   Step 38 — Weekly backup reminder banner.
   Lives at the top of <main> on every page (rendered by _Layout
   when canManageEmployees). Notion-style: pastel blue, soft
   border, icon on the leading edge, message in the middle,
   action buttons + close on the trailing edge. The icon's
   inset-inline-start hugs the start side in both LTR and RTL.
   ============================================================ */
.bk-reminder-banner {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 12px 16px;
    margin-bottom: 16px;
    background: var(--blue-50);
    border: 1px solid #BFDBFE;
    border-radius: 12px;
    color: var(--text-primary);
}
.bk-reminder-banner .bk-r-icon {
    width: 36px; height: 36px;
    flex-shrink: 0;
    border-radius: 10px;
    display: grid; place-items: center;
    background: rgba(255, 255, 255, 0.7);
    color: var(--status-blue-700);
    font-size: 18px;
}
.bk-reminder-banner .bk-r-body { flex: 1; min-width: 0; }
.bk-reminder-banner .bk-r-title {
    font-weight: var(--fw-medium);
    font-size: var(--fs-secondary);
    color: var(--text-primary);
    line-height: 1.2;
}
.bk-reminder-banner .bk-r-text {
    font-size: var(--fs-meta);
    color: var(--text-secondary);
    margin-top: 2px;
    line-height: 1.4;
}
.bk-reminder-banner .bk-r-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
}
.bk-reminder-banner .bk-r-close {
    width: 32px; height: 32px;
    border-radius: 8px;
    border: 1px solid transparent;
    background: transparent;
    color: var(--text-tertiary);
    display: grid; place-items: center;
    cursor: pointer;
    transition: background 0.1s, color 0.1s;
}
.bk-reminder-banner .bk-r-close:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}
@media (max-width: 768px) {
    .bk-reminder-banner { flex-direction: column; align-items: stretch; }
    .bk-reminder-banner .bk-r-actions { justify-content: flex-end; }
}

/* ---- Inline error caption ---- */
.notion-error {
    margin-top: 10px;
    padding: 10px 12px;
    background: var(--red-50);
    border: 1px solid #FECACA;
    border-radius: 10px;
    color: var(--status-red-700);
    font-size: var(--fs-meta);
    line-height: 1.4;
}

/* ---- 2-column grid (collapses to 1 on narrow) ---- */
.notion-grid-2 {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 18px;
}
@media (max-width: 575.98px) {
    .notion-grid-2 { grid-template-columns: 1fr; gap: 0; }
}

/* 4-up form grid — collapses to 2 on tablet, 1 on phone. Same horizontal
   gap as notion-grid-2 so the rows line up visually across grids. Used
   by the Schedule modal's add-single + bulk-wizard sections. */
.notion-grid-4 {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 0 18px;
}
@media (max-width: 900px) {
    .notion-grid-4 { grid-template-columns: 1fr 1fr; gap: 0 18px; }
}
@media (max-width: 575.98px) {
    .notion-grid-4 { grid-template-columns: 1fr; gap: 0; }
}

/* Step 38 — 3-up form grid for tiered inputs (e.g. HourlyRate1/2/3 on the
   Employees modal). Same horizontal gap as the 2/4 grids; collapses to a
   single column on phone-portrait so the inputs don't squeeze too narrow
   to type into. */
.notion-grid-3 {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0 18px;
}
@media (max-width: 575.98px) {
    .notion-grid-3 { grid-template-columns: 1fr; gap: 0; }
}

/* Step 38 — Sub-label variant for inputs nested inside a labelled group
   (e.g. HR1/HR2/HR3 under "Tiered hourly rates"). Slightly smaller +
   placeholder color so it visually defers to the parent group label. */
.notion-field-sublabel {
    font-size: var(--fs-meta);
    color: var(--text-tertiary);
    font-weight: var(--fw-regular);
}

/* ============================================================
   Scan-tab specifics
   ============================================================ */

.notion-scan-controls {
    display: flex;
    align-items: flex-end;
    gap: 12px;
    margin-bottom: 4px;
}
.notion-scan-controls .notion-field { margin-bottom: 0; }
.notion-field-grow   { flex: 1 1 0; }
.notion-field-narrow { flex: 0 0 110px; }
.notion-scan-arrow {
    align-self: center;
    margin-top: 22px;       /* compensates the label height above the input */
    color: var(--text-tertiary);
    font-size: 18px;
    user-select: none;
    flex-shrink: 0;
}
[dir="rtl"] .notion-scan-arrow { transform: scaleX(-1); }

.notion-scan-actions {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin: 14px 0 18px;
}

/* ---- Port toggle pills ---- */
.notion-port-pills {
    display: inline-flex;
    gap: 6px;
}
.notion-port-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 36px;
    padding: 0 12px;
    border: 1px solid var(--border-default);
    border-radius: 999px;
    background: var(--bg-card);
    cursor: pointer;
    transition: background 0.1s, border-color 0.1s;
    user-select: none;
    margin: 0;
}
.notion-port-pill:hover { border-color: #CBD5E1; }
.notion-port-pill input[type="checkbox"] {
    accent-color: var(--text-primary);
    width: 14px;
    height: 14px;
    margin: 0;
    cursor: pointer;
}
.notion-port-pill-num {
    font-family: 'Inter', monospace;
    font-variant-numeric: tabular-nums;
    font-size: var(--fs-secondary);
    font-weight: var(--fw-medium);
    color: var(--text-primary);
}
.notion-port-pill-tag {
    font-size: var(--fs-meta);
    color: var(--text-tertiary);
}
.notion-port-pill:has(input:checked) {
    background: var(--lime-soft);
    border-color: #D9F99D;
}

/* ---- Scan progress / empty / list ---- */
.notion-scan-progress {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 16px;
    background: var(--bg-input);
    border: 1px solid var(--border-subtle);
    border-radius: 12px;
    color: var(--text-secondary);
    font-size: var(--fs-meta);
    margin-bottom: 14px;
}
.notion-scan-progress i { font-size: 14px; color: var(--text-tertiary); }

.notion-scan-empty {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 24px 16px;
    border: 1px dashed var(--border-default);
    border-radius: 12px;
    color: var(--text-secondary);
}
.notion-scan-empty i { font-size: 20px; color: var(--text-tertiary); flex-shrink: 0; }
.notion-scan-empty-hint { color: var(--text-tertiary); font-size: var(--fs-meta); }

.notion-scan-summary {
    font-size: var(--fs-meta);
    color: var(--text-secondary);
    margin-bottom: 8px;
    line-height: 1.4;
}

.notion-scan-results {
    margin-top: 10px;
    max-height: 320px;
    overflow-y: auto;
}

.notion-scan-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

/* ---- One scan-result row ---- */
.notion-scan-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    background: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-radius: 10px;
    transition: background 0.1s, border-color 0.1s;
}
.notion-scan-row:hover {
    background: var(--bg-hover);
    border-color: var(--border-default);
}
.notion-scan-dot {
    width: 8px; height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
    background: var(--text-placeholder);
}
.notion-scan-dot.is-zk     { background: var(--status-blue-700); }
.notion-scan-dot.is-known  { background: var(--status-green-700); }
.notion-scan-dot.is-other  { background: var(--text-placeholder); }

.notion-scan-ip {
    font-family: 'Inter', monospace;
    font-size: var(--fs-secondary);
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    min-width: 110px;
}
.notion-scan-port {
    font-family: 'Inter', monospace;
    font-size: var(--fs-meta);
    color: var(--text-placeholder);
}

.notion-scan-tag {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 9px;
    border-radius: 999px;
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    line-height: 1.3;
    white-space: nowrap;
    flex: 1 1 auto;
    min-width: 0;
}
.notion-scan-tag i { font-size: 11px; }
.notion-scan-tag.is-zk    { background: var(--blue-50);  color: var(--status-blue-700); }
.notion-scan-tag.is-known { background: var(--green-50); color: var(--status-green-700); }
.notion-scan-tag.is-other { background: var(--bg-input); color: var(--text-secondary); }

.notion-scan-row-action {
    margin-inline-start: auto;
    flex-shrink: 0;
}
.notion-scan-add { height: 32px; padding: 0 12px; }
.notion-scan-row-empty {
    color: var(--text-placeholder);
    font-size: var(--fs-meta);
    padding: 0 8px;
}


/* ================================================================
   .bk-page / .bk-section family
   ----------------------------------------------------------------
   Shared "settings sub-page" document shell. Used by /Backup,
   /License, /Settings/SystemReset, /HolidaysExceptions — any
   long-form settings page that reads top-to-bottom like a Notion
   doc (centered column, sections separated by .notion-divider).

   The .bk- prefix is historical (came from /Backup, the first page
   to use this layout); the rules apply to any view that mounts
   `<div class="bk-page">` at the root. Per-view CSS only needs
   to override these when the page has something unique (e.g. the
   stats grid on /Backup).
   ================================================================ */
.bk-page {
    max-width: 920px;
    margin: 0 auto;
    padding: 0 6px;
}

/* Header row — title + intro on inline-start, breadcrumb-back button
   on inline-end. Items align to the title baseline. */
.bk-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 8px;
}
.bk-title {
    font-size: var(--fs-page-title, 22px);
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    margin: 0 0 4px 0;
    display: flex;
    align-items: center;
    gap: 10px;
}
.bk-title .bi { color: var(--text-secondary); font-size: 18px; }
.bk-subtitle {
    color: var(--text-secondary);
    font-size: var(--fs-secondary);
    margin: 0;
    max-width: 640px;
}

/* Section — a labelled run of content separated from its siblings
   by .notion-divider. The head holds a small title + optional desc. */
.bk-section { margin: 24px 0 8px; }
.bk-section-head { margin-bottom: 12px; }
.bk-section-title {
    font-size: var(--fs-secondary);
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    margin: 0 0 4px 0;
    display: flex;
    align-items: center;
    gap: 8px;
}
.bk-section-title .bi { color: var(--text-tertiary); font-size: 14px; }
.bk-section-desc {
    font-size: var(--fs-meta);
    color: var(--text-secondary);
    margin: 0;
    max-width: 640px;
}

/* Action row — primary button(s) + side note (e.g. "Generating…"). */
.bk-action-row {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

/* Mono-block input variant — for code-shaped textareas (license tokens,
   long base64 payloads). Looks like a code block. */
.notion-input.is-mono-block {
    font-family: 'Inter', 'Consolas', monospace;
    font-size: 12px;
    line-height: 1.5;
    padding: 10px 14px;
    height: auto;
}

/* "DEV ONLY" badge — calls out development-environment-only sections
   so customers don't mistake them for production features. Pairs
   well next to a .bk-section-title text. */
.dev-only-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    background: var(--amber-50);
    color: var(--status-amber-700);
    border: 1px solid #FDE68A;
    border-radius: var(--r-pill);
    font-size: 10px;
    font-weight: var(--fw-medium);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    line-height: 1.4;
    margin-inline-start: 8px;
}


/* ================================================================
   .zk-tip — Notion-style hover tooltip primitive.

   Trigger any element by adding either or both attributes:
       data-zk-tip-title="Short heading"
       data-zk-tip-body="Longer description text (1–2 sentences)"

   The JS init in admin.js (window.ZkPush.tip) attaches a single
   floating .zk-tip element to <body> and re-uses it for every
   trigger. No per-trigger DOM created — keeps the tree clean.

   Shows after ~350ms hover (so brushing past doesn't fire), hides
   ~100ms after leave (so the cursor can travel onto the tip if
   we ever make it interactive). Pointer-events: none keeps it
   from blocking clicks underneath.
   ================================================================ */
.zk-tip {
    position: fixed;
    z-index: 9999;
    top: 0;                          /* set by JS */
    left: 0;
    min-width: 180px;
    max-width: 320px;
    padding: 10px 12px;
    background: var(--bg-card);
    color: var(--text-secondary);
    border: 1px solid var(--border-default);
    border-radius: var(--r-card, 8px);
    box-shadow:
        0 8px 24px rgba(15, 23, 42, 0.08),
        0 2px 6px  rgba(15, 23, 42, 0.04);
    font-size: var(--fs-meta);
    line-height: 1.5;
    pointer-events: none;
    opacity: 0;
    transform: translateY(2px);
    transition: opacity 0.12s ease, transform 0.12s ease;
}
.zk-tip.is-visible {
    opacity: 1;
    transform: translateY(0);
}
.zk-tip-title {
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    font-size: var(--fs-secondary);
    margin: 0 0 4px 0;
}
.zk-tip-title:empty { display: none; }
.zk-tip-body {
    color: var(--text-secondary);
    margin: 0;
}
.zk-tip-body:empty { display: none; }

/* Small chevron pointing to trigger. Direction set by JS via
   data-placement="top" | "bottom". */
.zk-tip::before {
    content: '';
    position: absolute;
    width: 8px;
    height: 8px;
    background: var(--bg-card);
    border: 1px solid var(--border-default);
    transform: rotate(45deg);
    left: 50%;
    margin-left: -4px;
}
.zk-tip[data-placement="top"]::before {
    bottom: -5px;
    border-top: none;
    border-left: none;
}
.zk-tip[data-placement="bottom"]::before {
    top: -5px;
    border-bottom: none;
    border-right: none;
}

/* Dark-mode tone — token system already inverts --bg-card etc., so
   the bubble inherits correctly. Just soften the shadow which is
   too heavy on dark surfaces. */
[data-bs-theme="dark"] .zk-tip {
    box-shadow:
        0 8px 24px rgba(0, 0, 0, 0.4),
        0 2px 6px  rgba(0, 0, 0, 0.25);
}


/* ================================================================
   RTL — directional icon auto-mirroring.

   Bootstrap Icons ship with fixed-direction glyphs: chevron-left always
   points left, arrow-right always right. In an RTL UI those arrows
   point the WRONG way (a "back" button should point inline-start, which
   is right in RTL — not left). This rule flips every directional icon
   horizontally when the page is in RTL mode.

   Opt-out: add `.no-rtl-flip` to icons whose meaning is independent of
   reading direction (e.g. play buttons that point right by convention,
   icons whose neighbours already handle their own direction logic).
   ================================================================ */
[dir="rtl"] .bi-arrow-left:not(.no-rtl-flip),
[dir="rtl"] .bi-arrow-right:not(.no-rtl-flip),
[dir="rtl"] .bi-arrow-left-short:not(.no-rtl-flip),
[dir="rtl"] .bi-arrow-right-short:not(.no-rtl-flip),
[dir="rtl"] .bi-arrow-bar-left:not(.no-rtl-flip),
[dir="rtl"] .bi-arrow-bar-right:not(.no-rtl-flip),
[dir="rtl"] .bi-chevron-left:not(.no-rtl-flip),
[dir="rtl"] .bi-chevron-right:not(.no-rtl-flip),
[dir="rtl"] .bi-chevron-double-left:not(.no-rtl-flip),
[dir="rtl"] .bi-chevron-double-right:not(.no-rtl-flip),
[dir="rtl"] .bi-caret-left:not(.no-rtl-flip),
[dir="rtl"] .bi-caret-right:not(.no-rtl-flip),
[dir="rtl"] .bi-caret-left-fill:not(.no-rtl-flip),
[dir="rtl"] .bi-caret-right-fill:not(.no-rtl-flip),
[dir="rtl"] .bi-box-arrow-left:not(.no-rtl-flip),
[dir="rtl"] .bi-box-arrow-right:not(.no-rtl-flip),
[dir="rtl"] .bi-box-arrow-in-left:not(.no-rtl-flip),
[dir="rtl"] .bi-box-arrow-in-right:not(.no-rtl-flip) {
    transform: scaleX(-1);
}


/* ================================================================
   .zk-skeleton — shimmer-loading placeholder primitive.

   Replaces the legacy "— Loading — " text rows with animated grey
   blocks that mimic the shape of incoming content. The eye reads
   a skeleton as "this card is going to fill in here" rather than
   "the app froze".

   Composition primitives — use the base class + a shape modifier:

       <span class="zk-skeleton zk-skeleton-line"></span>       <!-- one text row -->
       <span class="zk-skeleton zk-skeleton-line is-short"></span>
       <span class="zk-skeleton zk-skeleton-circle"></span>     <!-- 32×32 dot -->
       <div  class="zk-skeleton zk-skeleton-block"></div>       <!-- generic block -->

   Or build composite skeletons via the JS helper ZkPush.skeleton().

   Animation = subtle inline-gradient sweep (1.4s, infinite).
   Honors `prefers-reduced-motion` — those users see a solid mid-tone
   slate so we still telegraph "loading", just without movement.
   ================================================================ */

.zk-skeleton {
    display: inline-block;
    position: relative;
    background-color: var(--bg-input);
    background-image: linear-gradient(
        90deg,
        var(--bg-input)   0%,
        var(--bg-hover)  50%,
        var(--bg-input) 100%
    );
    background-size: 200% 100%;
    background-repeat: no-repeat;
    border-radius: 4px;
    animation: zkSkelShimmer 1.4s linear infinite;
    /* Block any text or icon children from being read by assistive tech
       — these are pure visual placeholders. */
    color: transparent !important;
    user-select: none;
    pointer-events: none;
}

/* The shimmer direction is logical-property aware: in RTL the gradient
   sweeps right→left rather than left→right, matching reading flow. */
@keyframes zkSkelShimmer {
    0%   { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}
[dir="rtl"] .zk-skeleton {
    animation-direction: reverse;
}

/* ---- Shape modifiers ---- */
.zk-skeleton-line {
    display: block;
    height: 12px;
    width: 100%;
    margin-block: 6px;
}
.zk-skeleton-line.is-short    { width: 40%; }
.zk-skeleton-line.is-mid      { width: 65%; }
.zk-skeleton-line.is-long     { width: 85%; }
.zk-skeleton-line.is-tall     { height: 18px; }   /* "title row" */

.zk-skeleton-block {
    display: block;
    width: 100%;
    height: 80px;
    border-radius: 8px;
}

.zk-skeleton-circle {
    display: inline-block;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    vertical-align: middle;
}
.zk-skeleton-circle.is-sm { width: 24px; height: 24px; }
.zk-skeleton-circle.is-lg { width: 44px; height: 44px; }

.zk-skeleton-chip {
    display: inline-block;
    height: 20px;
    width: 60px;
    border-radius: 999px;
}

/* ---- Composite: skeleton table row ----
   Drop into <tbody> directly: it renders a single <tr> with one
   left-aligned line per column. Used by ZkPush.skeleton.rows(). */
.zk-skeleton-row td {
    padding: 14px 12px !important;
}

/* ---- Composite: KPI tile skeleton (dashboard) ----
   Two-line block: title line on top, big value line below. */
.zk-skeleton-kpi {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 14px 16px;
}
.zk-skeleton-kpi .zk-skeleton-line:nth-child(1) { width: 60%; height: 11px; }
.zk-skeleton-kpi .zk-skeleton-line:nth-child(2) { width: 35%; height: 22px; }

/* ---- Composite: list row skeleton (Live Activity, feeds) ----
   Horizontal row: optional avatar circle + a stack of 2 lines. */
.zk-skeleton-list-row {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 10px 14px;
    border-bottom: 1px solid var(--border-subtle);
}
.zk-skeleton-list-row:last-child { border-bottom: none; }

/* Dashboard-specific list-row skeleton — matches the visual rhythm of
   .dash-list-item so the skeleton-to-real swap doesn't flicker. */
.dash-list-skeleton {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 10px 12px;
    list-style: none;
}
.dash-list-skeleton-body { flex: 1; min-width: 0; }

/* ---- Reduced-motion fallback ----
   Some users get vestibular discomfort from continuous animations.
   We respect their setting by freezing the gradient to a solid
   slate — still readable as "loading" thanks to the muted color. */
@media (prefers-reduced-motion: reduce) {
    .zk-skeleton {
        animation: none;
        background-image: none;
        background-color: var(--bg-input);
    }
}


/* ================================================================
   .zk-help-drawer — In-app help drawer (Step 33-help-redesign).
   Slides in from the inline-end edge when the "?" topbar button is
   clicked. Lists explanation articles + lets the user drill into one.
   Auto-suggests the article relevant to the current URL.

   Notion-style touches:
     • circular icon badge in the header
     • card-style list rows with hover lift + trailing chevron
     • pastel blue tint + side accent on suggested rows
     • soft layered shadow on the drawer itself
     • generous spacing + smooth transitions throughout
   ================================================================ */

/* Backdrop — semi-transparent overlay that catches outside clicks.
   Lower z than the drawer itself so the drawer sits on top. */
.zk-help-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.28);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
    z-index: 1390;
    opacity: 0;
    transition: opacity 0.2s ease;
    pointer-events: none;
}
.zk-help-backdrop.is-open {
    opacity: 1;
    pointer-events: auto;
}

/* The drawer itself — anchored to inline-end (right in LTR, left in RTL).
   Soft layered shadow (close + far) gives the depth Notion uses for its
   side panels — more elegant than a single hard shadow. */
.zk-help-drawer {
    position: fixed;
    top: 0;
    bottom: 0;
    inset-inline-end: 0;
    width: min(460px, 100vw);
    background: var(--bg-card);
    border-inline-start: 1px solid var(--border-default);
    box-shadow:
        -2px 0 8px -2px rgba(15, 23, 42, 0.06),
        -12px 0 32px -8px rgba(15, 23, 42, 0.18);
    z-index: 1400;
    display: flex;
    flex-direction: column;
    transform: translateX(100%);     /* off-screen on inline-end side */
    transition: transform 0.24s cubic-bezier(0.32, 0.72, 0, 1);
}
[dir="rtl"] .zk-help-drawer {
    transform: translateX(-100%);    /* RTL flips slide direction */
    box-shadow:
        2px 0 8px -2px rgba(15, 23, 42, 0.06),
        12px 0 32px -8px rgba(15, 23, 42, 0.18);
    border-inline-start: 0;
    border-inline-end: 1px solid var(--border-default);
}
.zk-help-drawer.is-open { transform: translateX(0); }
.zk-help-drawer[hidden] { display: none; }

/* ---------- HEADER ---------- */
/* Circular icon badge (blue tint) + title + tiny count line. The icon
   isn't part of the <h3> heading anymore — gives more room for visual
   weight and keeps the heading text clean for screen readers. */
.zk-help-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 18px 20px 16px;
    border-bottom: 1px solid var(--border-subtle);
    flex-shrink: 0;
}
.zk-help-header-main {
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
}
.zk-help-header-icon {
    flex-shrink: 0;
    width: 38px;
    height: 38px;
    border-radius: 10px;
    background: var(--blue-50);
    color: var(--status-blue-700);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 20px;
    box-shadow: inset 0 0 0 1px rgba(29, 78, 216, 0.12);
}
.zk-help-header-text {
    min-width: 0;
    line-height: 1.2;
}
.zk-help-title {
    font-size: 17px;
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    margin: 0;
    line-height: 1.2;
}
.zk-help-header-sub {
    font-size: var(--fs-meta);
    color: var(--text-tertiary);
    margin: 2px 0 0 0;
    font-variant-numeric: tabular-nums;
}
.zk-help-close {
    width: 32px;
    height: 32px;
    border-radius: 8px;
    border: 1px solid transparent;
    background: transparent;
    color: var(--text-secondary);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s, color 0.15s, transform 0.12s;
    flex-shrink: 0;
}
.zk-help-close:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}
.zk-help-close:active { transform: scale(0.94); }

/* ---------- SEARCH ---------- */
/* Pinned under the header. Soft inset background + tiny shadow on focus
   (Notion-y) instead of a plain border-color flip. Clear (×) button slides
   in when there's text in the input. */
.zk-help-search {
    position: relative;
    padding: 14px 20px;
    flex-shrink: 0;
    border-bottom: 1px solid var(--border-subtle);
}
.zk-help-search-icon {
    position: absolute;
    inset-inline-start: 32px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-tertiary);
    font-size: 14px;
    pointer-events: none;
    transition: color 0.15s;
}
.zk-help-search input {
    width: 100%;
    height: 40px;
    border: 1px solid var(--border-default);
    border-radius: 10px;
    background: var(--bg-input);
    padding: 0 38px;
    color: var(--text-primary);
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
    font-size: var(--fs-secondary);
    transition: background 0.15s, border-color 0.15s, box-shadow 0.15s;
}
.zk-help-search input::placeholder {
    color: var(--text-tertiary);
}
.zk-help-search input:focus {
    outline: none;
    background: var(--bg-card);
    border-color: var(--status-blue-700);
    box-shadow: 0 0 0 3px rgba(29, 78, 216, 0.10);
}
.zk-help-search input:focus + .zk-help-search-clear,
.zk-help-search:has(input:focus) .zk-help-search-icon {
    color: var(--status-blue-700);
}
/* Clear (×) button — sits at the inline-end edge of the input. */
.zk-help-search-clear {
    position: absolute;
    inset-inline-end: 28px;
    top: 50%;
    transform: translateY(-50%);
    width: 22px;
    height: 22px;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--text-tertiary);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 16px;
    line-height: 1;
    transition: color 0.12s, transform 0.12s;
}
.zk-help-search-clear:hover {
    color: var(--text-secondary);
    transform: translateY(-50%) scale(1.1);
}

/* ---------- CONTENT (scrollable) ---------- */
.zk-help-content {
    flex: 1;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    padding: 6px 0 24px;
}

/* ---------- LIST ---------- */
/* List rows now rendered as <button> for keyboard accessibility +
   semantic "tap to drill in". Each row is a card-like rectangle with
   icon (left) + content (middle) + chevron (right). */
.zk-help-list {
    display: flex;
    flex-direction: column;
    padding: 0 12px;
    gap: 2px;
}
.zk-help-list-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    border: 0;
    background: transparent;
    border-radius: 10px;
    cursor: pointer;
    text-align: start;
    width: 100%;
    font-family: inherit;
    transition: background 0.12s ease, transform 0.06s ease;
    position: relative;
}
.zk-help-list-item:hover {
    background: var(--bg-hover);
}
.zk-help-list-item:active {
    transform: scale(0.99);
}
.zk-help-list-item:focus-visible {
    outline: 2px solid var(--status-blue-700);
    outline-offset: -2px;
}
/* Suggested rows — soft blue card with a hairline blue ring instead of a
   loud side bar. The section header above ("Suggested for this page" + ⭐)
   carries the categorical signal; the per-card treatment stays subtle so
   the list reads as a calm hierarchy rather than a stack of alerts.
   The icon badge below gets a brighter blue, which leads the eye in
   without needing accents on the card edge. */
.zk-help-list-item.is-suggested {
    background: var(--blue-50);
    box-shadow: inset 0 0 0 1px rgba(29, 78, 216, 0.16);
}
.zk-help-list-item.is-suggested:hover {
    background: var(--blue-50);
    box-shadow: inset 0 0 0 1px rgba(29, 78, 216, 0.32);
}

/* Icon badge — 40-px circle with pastel blue background. */
.zk-help-list-icon {
    flex-shrink: 0;
    width: 40px;
    height: 40px;
    border-radius: 10px;
    background: var(--bg-input);
    color: var(--text-secondary);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 18px;
    transition: background 0.15s, color 0.15s;
}
.zk-help-list-item:hover .zk-help-list-icon {
    background: var(--bg-card);
    color: var(--text-primary);
}
.zk-help-list-item.is-suggested .zk-help-list-icon {
    background: var(--bg-card);
    color: var(--status-blue-700);
    box-shadow: inset 0 0 0 1px rgba(29, 78, 216, 0.18);
}

.zk-help-list-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.zk-help-list-title {
    font-size: var(--fs-secondary);
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    margin: 0;
    line-height: 1.35;
    display: block;
}
.zk-help-list-summary {
    font-size: var(--fs-meta);
    color: var(--text-secondary);
    margin: 0;
    line-height: 1.45;
    display: block;
    /* Clamp to 2 lines so cards stay roughly the same height. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Trailing chevron — hints at "this opens an article". Subtle by default,
   slides in slightly on hover (more prominent then). */
.zk-help-list-chevron {
    flex-shrink: 0;
    color: var(--text-tertiary);
    font-size: 14px;
    transition: color 0.12s ease, transform 0.15s ease;
}
.zk-help-list-item:hover .zk-help-list-chevron {
    color: var(--text-primary);
    transform: translateX(2px);
}
[dir="rtl"] .zk-help-list-item:hover .zk-help-list-chevron {
    transform: translateX(-2px);
}
.zk-help-list-item.is-suggested .zk-help-list-chevron {
    color: var(--status-blue-700);
}

/* Section header (e.g. "Suggested for this page", "All articles"). */
.zk-help-list-section {
    padding: 16px 18px 6px;
    font-size: 10px;
    color: var(--text-tertiary);
    font-weight: var(--fw-medium);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    display: flex;
    align-items: center;
    gap: 6px;
}
.zk-help-list-section .bi {
    font-size: 12px;
    color: var(--status-blue-700);
}
.zk-help-list-section-other {
    border-top: 1px solid var(--border-subtle);
    margin: 8px 18px 0;
    padding: 12px 0 6px;
}

/* ---------- ARTICLE VIEW ---------- */
/* Sticky article header keeps a Back button visible on tall articles. */
.zk-help-article {
    padding: 0 0 32px;
}
.zk-help-article-header {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--bg-card);
    padding: 8px 16px 12px;
    border-bottom: 1px solid var(--border-subtle);
    margin: 0 0 16px 0;
}
.zk-help-back {
    background: transparent;
    border: 1px solid transparent;
    color: var(--text-secondary);
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    cursor: pointer;
    padding: 6px 10px;
    border-radius: 8px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
    font-family: inherit;
}
.zk-help-back:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}
.zk-help-back:active { transform: scale(0.96); }
.zk-help-back .bi {
    font-size: 12px;
    transition: transform 0.15s ease;
}
.zk-help-back:hover .bi {
    transform: translateX(-2px);
}
[dir="rtl"] .zk-help-back:hover .bi {
    transform: translateX(2px);
}
.zk-help-article-title {
    font-size: 22px;
    font-weight: var(--fw-semibold, 600);
    color: var(--text-primary);
    margin: 0 20px 18px;
    line-height: 1.3;
    letter-spacing: -0.005em;
}

/* ---------- ARTICLE BODY ---------- */
.zk-help-body {
    color: var(--text-primary);
    font-size: var(--fs-secondary);
    line-height: 1.7;
    padding: 0 20px;
}
.zk-help-body h3 {
    font-size: 15px;
    font-weight: var(--fw-semibold, 600);
    margin: 22px 0 10px 0;
    color: var(--text-primary);
    letter-spacing: -0.003em;
    position: relative;
}
.zk-help-body h3:first-child { margin-top: 0; }
.zk-help-body p  {
    margin: 0 0 12px 0;
    color: var(--text-secondary);
}
.zk-help-body strong {
    color: var(--text-primary);
    font-weight: var(--fw-semibold, 600);
}
.zk-help-body em {
    color: var(--text-primary);
    font-style: italic;
}
.zk-help-body ul,
.zk-help-body ol {
    padding-inline-start: 22px;
    margin: 0 0 14px 0;
    color: var(--text-secondary);
}
.zk-help-body ul li,
.zk-help-body ol li {
    margin-bottom: 6px;
}
.zk-help-body ul li::marker {
    color: var(--text-tertiary);
}
.zk-help-body code {
    background: var(--bg-input);
    padding: 2px 7px;
    border-radius: 5px;
    font-family: 'JetBrains Mono', 'Inter', 'Consolas', monospace;
    font-size: 12.5px;
    color: var(--status-blue-700);
    font-weight: var(--fw-medium);
    border: 1px solid var(--border-subtle);
}
.zk-help-body pre {
    background: var(--bg-input);
    padding: 12px 14px;
    border-radius: 10px;
    border: 1px solid var(--border-subtle);
    overflow-x: auto;
    font-family: 'JetBrains Mono', 'Inter', 'Consolas', monospace;
    font-size: 12.5px;
    color: var(--text-primary);
    line-height: 1.55;
    margin: 0 0 14px 0;
}
.zk-help-body pre code {
    background: transparent;
    border: 0;
    padding: 0;
    color: inherit;
    font-weight: normal;
}
.zk-help-body a {
    color: var(--status-blue-700);
    text-decoration: none;
    border-bottom: 1px solid rgba(29, 78, 216, 0.30);
    transition: border-color 0.12s;
}
.zk-help-body a:hover { border-bottom-color: var(--status-blue-700); }
.zk-help-body table {
    width: 100%;
    border-collapse: collapse;
    margin: 0 0 14px 0;
    font-size: var(--fs-meta);
}
.zk-help-body table th,
.zk-help-body table td {
    text-align: start;
    padding: 8px 10px;
    border-bottom: 1px solid var(--border-subtle);
    color: var(--text-secondary);
}
.zk-help-body table th {
    color: var(--text-tertiary);
    font-weight: var(--fw-medium);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* Empty state — no search results */
.zk-help-empty {
    padding: 48px 18px;
    text-align: center;
    color: var(--text-tertiary);
    font-size: var(--fs-meta);
}
.zk-help-empty i {
    font-size: 36px;
    opacity: 0.4;
    display: block;
    margin-bottom: 12px;
}

/* Mobile — drawer becomes full-screen */
@media (max-width: 540px) {
    .zk-help-drawer {
        width: 100vw;
        border-inline-start: 0;
    }
    .zk-help-header,
    .zk-help-search { padding-inline: 16px; }
    .zk-help-list { padding-inline: 8px; }
    .zk-help-article-title { margin-inline: 16px; }
    .zk-help-body { padding-inline: 16px; }
}


/* ================================================================
   .zk-datepicker — Notion-themed custom date picker.

   Replaces the native <input type="date"> which renders differently
   on every browser (Chrome's flashy blue, Firefox's grey buttons,
   Safari's iOS sheet). The picker uses a hidden `<input type="text">`
   as the source of truth (so form submission Just Works) and renders
   a floating calendar popup on focus / click.

   Two modes:
     • Single   one input → one date          (data-zk-datepicker)
     • Range    two inputs → one calendar     (data-zk-datepicker-from + -to)

   The popup positions itself below the trigger input via JS, flips
   above when there's no room below, and clamps inside the viewport
   on narrow screens. RTL is automatic — month navigation arrows and
   day grid both follow the inline direction.
   ================================================================ */

/* Anchor wrapper around the input — gives JS a known parent to
   measure for popup positioning. */
.zk-datepicker-anchor {
    position: relative;
    display: inline-block;
    width: 100%;
}
.zk-datepicker-anchor > .notion-input,
.zk-datepicker-anchor > input[type="text"] {
    /* Show a small calendar icon at the trailing edge so users know
       the field is a date picker (since we hide native arrows). */
    padding-inline-end: 36px;
    cursor: pointer;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%2364748B' stroke-width='1.5'><rect x='1.75' y='3' width='12.5' height='10.5' rx='1.5'/><path d='M1.75 6.25h12.5M4.5 1.75v2.5M11.5 1.75v2.5'/></svg>");
    background-repeat: no-repeat;
    background-position: right 12px center;
    background-size: 16px;
}
[dir="rtl"] .zk-datepicker-anchor > .notion-input,
[dir="rtl"] .zk-datepicker-anchor > input[type="text"] {
    background-position: left 12px center;
    padding-inline-end: 12px;
    padding-inline-start: 36px;
}

/* The popup itself — fixed-positioned by JS so it floats over the
   page chrome. Width matches a typical month grid + breathing room. */
.zk-datepicker {
    position: fixed;
    z-index: 1300;
    width: 296px;
    background: var(--bg-card);
    border: 1px solid var(--border-default);
    border-radius: 10px;
    box-shadow:
        0 8px 28px rgba(15, 23, 42, 0.12),
        0 2px 6px  rgba(15, 23, 42, 0.06);
    padding: 12px;
    opacity: 0;
    transform: translateY(2px);
    transition: opacity 0.12s ease, transform 0.12s ease;
    pointer-events: none;
    user-select: none;
}
.zk-datepicker.is-open {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}

/* Range mode is slightly wider so the "from → to" highlight strip
   reads cleanly across two columns. */
.zk-datepicker.is-range { width: 320px; }

/* Header — month/year title + prev/next chevrons. */
.zk-dp-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 8px;
}
.zk-dp-title {
    font-size: var(--fs-secondary);
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    line-height: 1;
    background: transparent;
    border: none;
    padding: 6px 8px;
    border-radius: 6px;
    cursor: pointer;
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
}
.zk-dp-title:hover { background: var(--bg-hover); }
.zk-dp-nav {
    width: 28px;
    height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    background: transparent;
    border-radius: 6px;
    color: var(--text-secondary);
    cursor: pointer;
    font-size: 14px;
}
.zk-dp-nav:hover { background: var(--bg-hover); color: var(--text-primary); }

/* Day-of-week row + date grid. 7-column grid keeps weekday labels
   aligned with the dates below. */
.zk-dp-dow,
.zk-dp-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 2px;
}
.zk-dp-dow {
    margin-bottom: 4px;
}
.zk-dp-dow > span {
    text-align: center;
    font-size: 10px;
    font-weight: var(--fw-medium);
    color: var(--text-tertiary);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 4px 0;
}

/* Each day cell — keep them square for a clean grid. */
.zk-dp-day {
    aspect-ratio: 1 / 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    background: transparent;
    color: var(--text-primary);
    font-size: var(--fs-meta);
    font-variant-numeric: tabular-nums;
    border-radius: 6px;
    cursor: pointer;
    padding: 0;
    line-height: 1;
    font-family: 'Inter', system-ui, sans-serif;
}
.zk-dp-day:hover {
    background: var(--bg-hover);
}

/* "Other month" — days that overflow from prev/next months to fill
   the grid. Dimmed so the focus stays on the current month. */
.zk-dp-day.is-outside {
    color: var(--text-placeholder);
}

/* Today — lime ring (no fill so a selection still wins visually). */
.zk-dp-day.is-today {
    box-shadow: inset 0 0 0 1px var(--lime);
}

/* Selected — slate-900 fill, white text. Matches our primary CTAs. */
.zk-dp-day.is-selected,
.zk-dp-day.is-range-start,
.zk-dp-day.is-range-end {
    background: var(--text-primary);
    color: #fff;
}
.zk-dp-day.is-selected:hover,
.zk-dp-day.is-range-start:hover,
.zk-dp-day.is-range-end:hover {
    background: var(--text-primary);
}

/* In-range (between start and end) — soft slate, no full fill, so
   the two endpoints stand out. */
.zk-dp-day.is-in-range {
    background: var(--bg-hover);
    border-radius: 0;
}
.zk-dp-day.is-range-start { border-radius: 6px 0 0 6px; }
.zk-dp-day.is-range-end   { border-radius: 0 6px 6px 0; }
[dir="rtl"] .zk-dp-day.is-range-start { border-radius: 0 6px 6px 0; }
[dir="rtl"] .zk-dp-day.is-range-end   { border-radius: 6px 0 0 6px; }
.zk-dp-day.is-range-start.is-range-end { border-radius: 6px; }

/* Disabled days (e.g. future dates if min/max bounds set). */
.zk-dp-day:disabled,
.zk-dp-day.is-disabled {
    color: var(--text-placeholder);
    cursor: not-allowed;
    background: transparent;
}
.zk-dp-day:disabled:hover,
.zk-dp-day.is-disabled:hover {
    background: transparent;
}

/* Footer — small action row for "Today" / "Clear". */
.zk-dp-foot {
    display: flex;
    justify-content: space-between;
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid var(--border-subtle);
}
.zk-dp-foot button {
    background: transparent;
    border: none;
    color: var(--status-blue-700);
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    cursor: pointer;
    padding: 4px 8px;
    border-radius: 6px;
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
}
.zk-dp-foot button:hover { background: var(--bg-hover); }

/* "Year picker" sub-view — toggled when user clicks the title.
   Shows a 4×3 grid of the previous 12 years. */
.zk-dp-years {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 4px;
    padding: 6px 0;
}
.zk-dp-year {
    background: transparent;
    border: none;
    padding: 10px 0;
    color: var(--text-primary);
    font-size: var(--fs-secondary);
    font-variant-numeric: tabular-nums;
    border-radius: 6px;
    cursor: pointer;
}
.zk-dp-year:hover { background: var(--bg-hover); }
.zk-dp-year.is-selected {
    background: var(--text-primary);
    color: #fff;
}

/* Range-mode trigger group — two inputs + arrow, share visual frame.
   Used wherever a "from → to" range picker appears: /Notifications,
   /Reports/Range, /Devices (Pull modal), /HolidaysExceptions modals.
   Sets a consistent 36px height so the widget aligns with neighbour
   buttons + inputs across the app. */
.zk-daterange {
    display: inline-flex;
    align-items: stretch;
    gap: 0;
    height: 36px;
    border: 1px solid var(--border-default);
    border-radius: 8px;
    overflow: hidden;
    background: var(--bg-card);
}
/* Default inner-input sizing for the range widget — overridable by
   per-view modifier classes (e.g. .notif-daterange, .rpt-daterange). */
.zk-daterange input {
    width: 140px;
    height: 100%;
    padding: 0 10px;
    font-size: var(--fs-meta);
    font-family: 'Cairo', 'Inter', system-ui, sans-serif;
}

/* ByDate report — the surrounding .bydate-input controls are 42px tall.
   Stretch the range widget + its inputs to match so the filter row reads
   as a single horizontal line. The Inter font + 13px text matches what
   .bydate-input renders in. */
.bd-daterange {
    height: 42px;
    border-radius: 10px;       /* match .bydate-input radius */
}
.bd-daterange input {
    width: 160px;
    font-size: var(--fs-body);
    font-family: 'Inter', 'Cairo', system-ui, sans-serif;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}
.zk-daterange > .zk-datepicker-anchor > input {
    border: none;
    border-radius: 0;
    background-color: transparent;
}
.zk-daterange > .zk-datepicker-anchor:first-child > input {
    border-inline-end: 1px solid var(--border-subtle);
}
.zk-daterange-arrow {
    display: none;     /* the inline border between inputs is enough */
}

/* Narrow screens — popup fits inside viewport with margin. */
@media (max-width: 360px) {
    .zk-datepicker { width: calc(100vw - 24px); }
    .zk-datepicker.is-range { width: calc(100vw - 24px); }
}

/* ---- Dark-mode tones ----
   The animation gradient needs to remain visible on the darker card.
   We swap the two stops to slightly lighter slate-700 / slate-600
   variants so the shimmer reads without becoming too bright. */
[data-bs-theme="dark"] .zk-skeleton {
    background-color: #1F2937;
    background-image: linear-gradient(
        90deg,
        #1F2937 0%,
        #334155 50%,
        #1F2937 100%
    );
}
@media (prefers-reduced-motion: reduce) {
    [data-bs-theme="dark"] .zk-skeleton {
        background-image: none;
        background-color: #1F2937;
    }
}

/* ============================================================
   STEP 36 — LATENESS & SALARY DEDUCTIONS REPORT
   ============================================================
   Built on the existing .bydate-* design language so the page
   feels native next to /Reports/ByDate. Adds:
     • .lt-kpi-grid / .lt-kpi          → 4-up KPI row (Notion soft cards)
     • .lt-table extends .bydate-table → click-to-expand row pattern
     • .lt-detail-row                  → nested per-day breakdown row
     • .lt-warn-pill                   → red-tone "over threshold" chip
   Dark mode is inherited via the underlying tokens — no overrides
   needed beyond the cell tint and detail-row background.
   ============================================================ */

/* KPI strip uses the existing .dash-stats / .dash-stat grid from the
   Home dashboard so the cards are visually identical (pastel fills,
   icon chip, label + value + meta stack). The 4-up override below
   widens it from the dashboard's 3-up baseline since the lateness
   report exposes one extra KPI (over-threshold count). */
.lt-kpi-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--gap-cards);
    margin-bottom: 16px;
}
@media (max-width: 1199.98px) { .lt-kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 575.98px)  { .lt-kpi-grid { grid-template-columns: 1fr; } }

/* ---- Table card (reuses .bydate-table-card shell + .bydate-table) ---- */
.lt-table {
    width: 100%;
    border-collapse: collapse;
    table-layout: auto;            /* let the columns size to content */
}
.lt-table th, .lt-table td {
    padding: 12px 14px;
    text-align: start;
    vertical-align: middle;
    border-bottom: 1px solid var(--border-subtle);
    background: var(--bg-card);
    font-size: var(--fs-body);
    color: var(--text-primary);
}
.lt-table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--bg-hover);
    color: var(--text-tertiary);
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    line-height: 1;
    text-transform: none;
    white-space: nowrap;
}
.lt-table .lt-num { text-align: end; font-variant-numeric: tabular-nums; }
.lt-table .lt-flag-col { text-align: center; width: 1%; white-space: nowrap; }
.lt-table .lt-expand-col { width: 36px; text-align: center; padding-inline-end: 0; }

.lt-row { transition: background 0.12s; cursor: pointer; }
.lt-row:hover td { background: var(--bg-hover); }
.lt-row.is-expanded td { background: var(--bg-hover); }
.lt-row.is-expanded .lt-chevron { transform: rotate(90deg); }
[dir="rtl"] .lt-row.is-expanded .lt-chevron { transform: rotate(-90deg); }

/* Warned row — subtle red wash so it reads "needs attention" without
   shouting. The pill carries the strong color signal. */
.lt-row.is-warn td { background: rgba(239, 68, 68, 0.045); }
.lt-row.is-warn:hover td { background: rgba(239, 68, 68, 0.08); }
.lt-row.is-warn.is-expanded td { background: rgba(239, 68, 68, 0.08); }
[data-bs-theme="dark"] .lt-row.is-warn td { background: rgba(248, 113, 113, 0.10); }
[data-bs-theme="dark"] .lt-row.is-warn:hover td,
[data-bs-theme="dark"] .lt-row.is-warn.is-expanded td { background: rgba(248, 113, 113, 0.16); }

/* Employee column — name (medium) + #PIN (placeholder color). Mirrors
   the .bydate-emp pattern but flatter (no avatar; this report is
   data-dense and a 32px circle would push row height too tall). */
.lt-emp-name { font-weight: var(--fw-medium); color: var(--text-primary); }
.lt-emp-pin  { color: var(--text-placeholder); font-size: var(--fs-meta); margin-inline-start: 6px; direction: ltr; unicode-bidi: isolate; }

.lt-dim { color: var(--text-placeholder); }

.lt-chevron {
    display: inline-block;
    transition: transform 0.15s;
    color: var(--text-placeholder);
    font-size: 14px;
    line-height: 1;
}
[dir="rtl"] .lt-chevron { transform: rotate(180deg); }

/* Warning pill — red-50 background + red-700 text, same shape as
   .bydate-mode-badge (rounded rect, not a full pill, so it sits
   neatly in the column). */
.lt-warn-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    background: var(--red-50);
    color: var(--status-red-700);
    border-radius: var(--r-tile);
    font-size: var(--fs-meta);
    font-weight: var(--fw-semibold);
    white-space: nowrap;
    line-height: 1;
}
[data-bs-theme="dark"] .lt-warn-pill {
    background: rgba(248, 113, 113, 0.18);
    color: #FCA5A5;
}

/* ---- Expanded detail row ---- */
.lt-detail-row > td {
    background: var(--bg-page);
    padding: 0;
    border-bottom: 1px solid var(--border-default);
}
[data-bs-theme="dark"] .lt-detail-row > td { background: var(--bg-input); }

.lt-detail-wrap { padding: 16px 18px; }

.lt-detail-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 16px 24px;
    margin-bottom: 12px;
    font-size: var(--fs-secondary);
    color: var(--text-tertiary);
}
.lt-detail-meta b {
    color: var(--text-primary);
    font-weight: var(--fw-semibold);
    margin-inline-start: 4px;
}

.lt-detail-table {
    width: 100%;
    border-collapse: collapse;
    background: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-radius: var(--r-card-sm);
    overflow: hidden;
}
.lt-detail-table th,
.lt-detail-table td {
    padding: 8px 12px;
    text-align: start;
    border-bottom: 1px solid var(--border-subtle);
    font-size: var(--fs-secondary);
    color: var(--text-primary);
}
.lt-detail-table thead th {
    background: var(--bg-hover);
    color: var(--text-tertiary);
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    white-space: nowrap;
}
.lt-detail-table tbody tr:last-child td { border-bottom: none; }
.lt-detail-table .lt-num { text-align: end; font-variant-numeric: tabular-nums; }
.lt-detail-table .lt-late td,
.lt-detail-table .lt-late .lt-num {
    color: var(--status-red-700);
    font-weight: var(--fw-semibold);
}
[data-bs-theme="dark"] .lt-detail-table .lt-late td,
[data-bs-theme="dark"] .lt-detail-table .lt-late .lt-num { color: #FCA5A5; }
.lt-detail-table .lt-absent { color: var(--text-placeholder); font-style: italic; }

/* Step 38 — full-absence day row (>3h late). Stronger red wash than a
   plain tier row so the operator immediately sees days where the whole
   DailySalary was deducted instead of a partial tier sum. */
.lt-detail-table .lt-absence-row td,
.lt-detail-table .lt-absence-row .lt-num {
    color: var(--status-red-700);
    font-weight: var(--fw-semibold);
    background: rgba(239, 68, 68, 0.06);
}
[data-bs-theme="dark"] .lt-detail-table .lt-absence-row td,
[data-bs-theme="dark"] .lt-detail-table .lt-absence-row .lt-num {
    color: #FCA5A5;
    background: rgba(248, 113, 113, 0.10);
}

/* Step 38 — Calculation pill on each detail row. Two variants:
     .lt-calc-tier     → neutral slate pill showing "HR1 + HR2 + HR3"
     .lt-calc-absence  → red pill showing the full-absence label
   Compact (max-content) so they don't stretch the cell. */
.lt-calc-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: var(--r-tile);
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    line-height: 1.4;
    white-space: nowrap;
    font-family: 'Inter', system-ui, sans-serif;   /* HR1/HR2/HR3 stay LTR */
}
.lt-calc-tier {
    background: var(--bg-input);
    color: var(--text-secondary);
}
.lt-calc-absence {
    background: var(--red-50);
    color: var(--status-red-700);
}
[data-bs-theme="dark"] .lt-calc-absence {
    background: rgba(248, 113, 113, 0.18);
    color: #FCA5A5;
}
/* Step 51 — leave / exempt pills.
     .lt-calc-paid    → green   (paid leave — earns the daily wage)
     .lt-calc-unpaid  → neutral (unpaid leave — excluded from pay)
     .lt-calc-exempt  → blue    (late but excused by a partial exception) */
.lt-calc-paid {
    background: var(--green-50);
    color: var(--status-green-700);
}
.lt-calc-unpaid {
    background: var(--bg-input);
    color: var(--text-tertiary);
}
.lt-calc-exempt {
    background: var(--blue-50);
    color: var(--status-blue-700);
}
[data-bs-theme="dark"] .lt-calc-paid   { background: rgba(74, 222, 128, 0.16);  color: #86EFAC; }
[data-bs-theme="dark"] .lt-calc-exempt { background: rgba(96, 165, 250, 0.16);  color: #93C5FD; }

/* Step 51 — detail-row tints for leave / exempt days. Subtle so they
   read as "informational" next to the red absence rows. */
.lt-detail-table .lt-leave-row td  { color: var(--text-tertiary); }
.lt-detail-table .lt-exempt-row td { background: rgba(59, 130, 246, 0.04); }
[data-bs-theme="dark"] .lt-detail-table .lt-exempt-row td { background: rgba(96, 165, 250, 0.08); }

/* Step 59 — "not set" chip for employees with no daily salary. Makes the
   blank money columns read as "needs data entry" rather than a broken
   report. Amber/neutral so it informs without alarming. */
.lt-unset {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: var(--r-tile);
    background: var(--amber-50);
    color: var(--status-amber-700);
    font-size: var(--fs-meta);
    font-weight: var(--fw-medium);
    white-space: nowrap;
    cursor: help;
}
[data-bs-theme="dark"] .lt-unset {
    background: rgba(251, 191, 36, 0.16);
    color: #FCD34D;
}

/* Step 38 — main-row "X days" counter for full-absence days. Same red
   accent as the warning pill so the operator's eye groups them visually. */
.lt-absence-count {
    color: var(--status-red-700);
    font-weight: var(--fw-semibold);
}
[data-bs-theme="dark"] .lt-absence-count { color: #FCA5A5; }

.lt-detail-empty {
    padding: 14px;
    color: var(--text-placeholder);
    text-align: center;
    font-size: var(--fs-secondary);
}

.lt-loading {
    text-align: center;
    color: var(--text-placeholder);
    padding: 48px 16px;
    font-size: var(--fs-secondary);
}

/* Print: hide chrome, expand all detail rows, drop hover/pointer. */
@media print {
    .lt-row { cursor: default; }
    .lt-row:hover td { background: var(--bg-card); }
    .lt-detail-row { display: table-row !important; }
    .lt-table thead th { position: static; background: #fff; }
}
