/*
 * Modern theme for Django Admin — deep petrol brand, cool neutral surfaces,
 * dimensional controls.
 *
 * Overrides Django's CSS custom properties (see admin/css/base.css) rather than
 * fighting its layout — this keeps the whole admin (forms, tables, buttons,
 * messages, nav) consistent with one set of variables, in both light and dark mode.
 */

/*
 * !important is used deliberately on every custom-property redeclaration
 * below (but nowhere else in this file). These are the exact same property
 * names Django's own base.css/dark_mode.css declare on :root, and this
 * environment resolves same-specificity :root custom properties by
 * something other than plain source order (verified: a brand-new property
 * name added here cascades normally; every property name Django also
 * defines does not, regardless of link order, cache state, or reload).
 * !important sidesteps that ambiguity entirely per spec. New properties
 * this file introduces (e.g. --surface) don't need it.
 */

/*
 * Mirrors base.css's own "html[data-theme='light'], :root" pairing so an
 * explicit light choice still outranks the prefers-color-scheme block below
 * (which is why that block is guarded with :not([data-theme="light"])).
 */
html[data-theme="light"],
:root {
    --primary: #0f7a6d !important;
    --secondary: #0b5d53 !important;
    --accent: #16a394 !important;
    --primary-fg: #fff !important;

    /* How strongly the login photographs come through. Low: they are a
       watermark behind the card, not a picture the card sits on. See
       body.login::before. */
    --login-photo-opacity: 0.18;

    --body-fg: #14232b !important;
    --body-bg: #e3ebef !important;
    --body-loud-color: #0a1519 !important;
    --body-quiet-color: #566a74 !important;
    --body-medium-color: #33474f !important;

    --header-color: #cfe3e4 !important;
    --header-branding-color: #ffffff !important;
    --header-bg: #14303f !important;
    --header-link-color: #ffffff !important;

    --breadcrumbs-fg: #5c6f79 !important;
    --breadcrumbs-link-fg: #0f7a6d !important;
    --breadcrumbs-bg: #e8eef1 !important;

    --link-fg: #0d6b60 !important;
    --link-hover-color: #0a544b !important;
    --link-selected-fg: var(--secondary) !important;

    --hairline-color: #d2dde3 !important;
    --border-color: #cfdae1 !important;

    --darkened-bg: #eef2f5 !important;
    --selected-bg: #e2ecec !important;
    --selected-row: #e6f4f1 !important;

    --button-fg: #fff !important;
    --button-bg: var(--primary) !important;
    --button-hover-bg: var(--secondary) !important;
    --default-button-bg: var(--primary) !important;
    --default-button-hover-bg: var(--secondary) !important;
    --close-button-bg: #64757e !important;
    --close-button-hover-bg: #4b5b63 !important;

    --object-tools-bg: #23485a !important;
    --object-tools-hover-bg: #14303f !important;

    --font-family-primary: "IBM Plex Sans Arabic", "Readex Pro", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif !important;

    /* Local to this file. */
    --font-display: "Readex Pro", "IBM Plex Sans Arabic", "Segoe UI", system-ui, sans-serif;
    --surface: #ffffff;
    --surface-2: #eef2f5;
    --header-grad-a: #14303f;
    --header-grad-b: #0f5259;
    --btn-grad-a: #16a394;
    --btn-grad-b: #0d6f63;
    --btn-grad-a-hover: #18b3a2;
    --btn-grad-b-hover: #0a5d53;
    --notice-bg: #fdf6e6;
    --notice-border: #e8d19a;
    --notice-rail: #d9a441;
    /* The red counterpart of the amber notice trio above, for the one banner
       that is not a notice but a refusal: the seat limit, once it is reached.
       Defined in all three theme blocks like every other colour here, so it
       is never a literal that only works on one ground. */
    --danger-bg: #fdecea;
    --danger-border: #f0bdb6;
    --danger-rail: #c0392b;
    --danger-fg: #8c231a;

    /* Dashboard counter cards — one gradient pair per stat, distinct from
       both the brand teal and each other, so the row reads at a glance
       rather than by re-reading every label. */
    --card-good-a: #2fa876;
    --card-good-b: #0a6650;
    --card-amber-a: #eab54c;
    --card-amber-b: #c8922c;
    --card-red-a: #dd7060;
    --card-red-b: #9a3226;
    --card-blue-a: #4d8fe6;
    --card-blue-b: #2c62b8;
    --card-purple-a: #8b7ae0;
    --card-purple-b: #5b48b8;
    --card-orange-a: #e8823f;
    --card-orange-b: #c05a1e;
    /* For the overview cards (headcount, payroll, movement) — plain company
       data rather than something to act on, so it deliberately reuses the
       brand's own teal instead of joining the alert-coloured set above. */
    --card-neutral-a: #1c8f80;
    --card-neutral-b: #0d5c50;

    /* Status pills. Amber stays reserved for notices, so a decision that went
       the employee's way reads in the brand's own green and one that cost them
       money in a muted brick — neither competes with the teal controls. */
    --positive-bg: #e0f1ea;
    --positive-fg: #0a6650;
    --negative-bg: #fae9e6;
    --negative-fg: #9a3226;

    --shadow-sm: 0 1px 2px rgba(16, 38, 46, 0.06), 0 1px 3px rgba(16, 38, 46, 0.08);
    --shadow-md: 0 2px 6px rgba(16, 38, 46, 0.07), 0 8px 20px -8px rgba(16, 38, 46, 0.18);
    --shadow-lg: 0 10px 30px -10px rgba(16, 38, 46, 0.28), 0 2px 8px rgba(16, 38, 46, 0.08);
    /* Top highlight + bottom bevel — what gives the controls their raised edge. */
    --bevel: inset 0 1px 0 rgba(255, 255, 255, 0.28), inset 0 -2px 0 rgba(0, 0, 0, 0.16);

    --radius-card: 14px;
    --radius-control: 10px;
    --border-radius: var(--radius-card);
}

@media (prefers-color-scheme: dark) {
    html:not([data-theme="light"]) {
        --primary: #17a496 !important;
        --secondary: #12897d !important;
        --accent: #2ec9b8 !important;
        --primary-fg: #04201d !important;
        --login-photo-opacity: 0.12;

        --body-fg: #e7eef2 !important;
        --body-bg: #0d1418 !important;
        --body-loud-color: #f5fafc !important;
        /* The light-mode values above are tuned for contrast on a LIGHT
           background. Left unset here they would keep winning (they are
           !important and loaded after Django's own dark_mode.css) and go
           nearly invisible on a dark ground. */
        --body-quiet-color: #92a5af !important;
        --body-medium-color: #c3d1d8 !important;

        --header-color: #a9c6c9 !important;
        --header-branding-color: #f2fafb !important;
        --header-bg: #0b1b23 !important;
        --header-link-color: #eaf4f5 !important;

        --breadcrumbs-fg: #92a5af !important;
        --breadcrumbs-link-fg: #2ec9b8 !important;
        --breadcrumbs-bg: #111c22 !important;

        --link-fg: #2ec9b8 !important;
        --link-hover-color: #58e0d1 !important;

        --hairline-color: #243138 !important;
        --border-color: #33454e !important;

        --darkened-bg: #151f25 !important;
        --selected-bg: #1b2a30 !important;
        --selected-row: #14322f !important;

        --button-fg: #04201d !important;
        --close-button-bg: #3c4d56 !important;
        --close-button-hover-bg: #4f636d !important;
        --object-tools-bg: #24576a !important;
        --object-tools-hover-bg: #2e6b81 !important;
        /* Django derives this from --button-fg, which is near-black here so
           that text stays readable on the teal buttons — on the dark slate
           object-tools pill that would be invisible. */
        --object-tools-fg: #eaf4f5 !important;

        --surface: #151f25;
        --surface-2: #1b262d;
        --header-grad-a: #0b1b23;
        --header-grad-b: #103b40;
        --btn-grad-a: #1cb8a7;
        --btn-grad-b: #0f8578;
        --btn-grad-a-hover: #24cdba;
        --btn-grad-b-hover: #0c7268;
        --notice-bg: #2a2415;
        --notice-border: #5c4a22;
        --notice-rail: #d9a441;
        --danger-bg: #2d1613;
        --danger-border: #5e2b23;
        --danger-rail: #e07a6b;
        --danger-fg: #f0a79b;

        --card-good-a: #38c088;
        --card-good-b: #15806a;
        --card-amber-a: #f2c25f;
        --card-amber-b: #d6a13c;
        --card-red-a: #e88677;
        --card-red-b: #b0483a;
        --card-blue-a: #5aa3f5;
        --card-blue-b: #3a72cf;
        --card-purple-a: #a292ee;
        --card-purple-b: #6f5cd0;
        --card-orange-a: #f0954f;
        --card-orange-b: #d06f2c;
        --card-neutral-a: #22a996;
        --card-neutral-b: #147568;

        --positive-bg: #123a31;
        --positive-fg: #74dabf;
        --negative-bg: #3b201c;
        --negative-fg: #f0a89d;

        --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
        --shadow-md: 0 2px 6px rgba(0, 0, 0, 0.45), 0 8px 20px -8px rgba(0, 0, 0, 0.6);
        --shadow-lg: 0 10px 30px -10px rgba(0, 0, 0, 0.75), 0 2px 8px rgba(0, 0, 0, 0.4);
        --bevel: inset 0 1px 0 rgba(255, 255, 255, 0.18), inset 0 -2px 0 rgba(0, 0, 0, 0.3);
    }
}

html[data-theme="dark"] {
    --primary: #17a496 !important;
    --secondary: #12897d !important;
    --accent: #2ec9b8 !important;
    --primary-fg: #04201d !important;
    --login-photo-opacity: 0.12;

    --body-fg: #e7eef2 !important;
    --body-bg: #0d1418 !important;
    --body-loud-color: #f5fafc !important;
    --body-quiet-color: #92a5af !important;
    --body-medium-color: #c3d1d8 !important;

    --header-color: #a9c6c9 !important;
    --header-branding-color: #f2fafb !important;
    --header-bg: #0b1b23 !important;
    --header-link-color: #eaf4f5 !important;

    --breadcrumbs-fg: #92a5af !important;
    --breadcrumbs-link-fg: #2ec9b8 !important;
    --breadcrumbs-bg: #111c22 !important;

    --link-fg: #2ec9b8 !important;
    --link-hover-color: #58e0d1 !important;

    --hairline-color: #243138 !important;
    --border-color: #33454e !important;

    --darkened-bg: #151f25 !important;
    --selected-bg: #1b2a30 !important;
    --selected-row: #14322f !important;

    --button-fg: #04201d !important;
    --close-button-bg: #3c4d56 !important;
    --close-button-hover-bg: #4f636d !important;
    --object-tools-bg: #24576a !important;
    --object-tools-hover-bg: #2e6b81 !important;
    /* Django derives this from --button-fg, which is near-black here so that
       text stays readable on the teal buttons — on the dark slate
       object-tools pill that would be invisible. */
    --object-tools-fg: #eaf4f5 !important;

    --surface: #151f25;
    --surface-2: #1b262d;
    --header-grad-a: #0b1b23;
    --header-grad-b: #103b40;
    --btn-grad-a: #1cb8a7;
    --btn-grad-b: #0f8578;
    --btn-grad-a-hover: #24cdba;
    --btn-grad-b-hover: #0c7268;
    --notice-bg: #2a2415;
    --notice-border: #5c4a22;
    --notice-rail: #d9a441;
    --danger-bg: #2d1613;
    --danger-border: #5e2b23;
    --danger-rail: #e07a6b;
    --danger-fg: #f0a79b;

    --card-good-a: #38c088;
    --card-good-b: #15806a;
    --card-amber-a: #f2c25f;
    --card-amber-b: #d6a13c;
    --card-red-a: #e88677;
    --card-red-b: #b0483a;
    --card-blue-a: #5aa3f5;
    --card-blue-b: #3a72cf;
    --card-purple-a: #a292ee;
    --card-purple-b: #6f5cd0;
    --card-orange-a: #f0954f;
    --card-orange-b: #d06f2c;
    --card-neutral-a: #22a996;
    --card-neutral-b: #147568;

    --positive-bg: #123a31;
    --positive-fg: #74dabf;
    --negative-bg: #3b201c;
    --negative-fg: #f0a89d;

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-md: 0 2px 6px rgba(0, 0, 0, 0.45), 0 8px 20px -8px rgba(0, 0, 0, 0.6);
    --shadow-lg: 0 10px 30px -10px rgba(0, 0, 0, 0.75), 0 2px 8px rgba(0, 0, 0, 0.4);
    --bevel: inset 0 1px 0 rgba(255, 255, 255, 0.18), inset 0 -2px 0 rgba(0, 0, 0, 0.3);
}

@media (prefers-reduced-motion: reduce) {
    * {
        transition: none !important;
    }
}

body {
    background: var(--body-bg);
}

/* ---- Header / branding ----
 *
 * base.css lays #header out as a single-row flex container
 * (justify-content: space-between) sized for branding + usertools only. This
 * project's header holds four pieces — branding, a centred greeting, the
 * user menu, and (see .top-nav below) a full-width nav row underneath — so
 * it is replaced with an explicit grid: three columns on row one (the middle
 * one, 1fr, is what actually centres the greeting across the row rather than
 * just in whatever space is left after the other two), and the nav spanning
 * all three on row two.
 */
#header {
    display: grid;
    grid-template-columns: auto 1fr auto;
    grid-template-areas:
        "brand greet menu"
        "nav nav nav";
    align-items: center;
    column-gap: 20px;
    row-gap: 10px;
    padding: 14px 32px;
    background: linear-gradient(115deg, var(--header-grad-a) 0%, var(--header-grad-b) 100%);
    box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06) inset, 0 4px 18px -6px rgba(8, 24, 30, 0.55);
}

#branding {
    grid-area: brand;
}

.header-greeting {
    grid-area: greet;
    justify-self: center;
    text-align: center;
    color: var(--header-color);
    font-family: var(--font-display);
    font-size: 0.85rem;
    font-weight: 500;
    white-space: nowrap;
}

.header-greeting strong {
    color: #fff;
    font-weight: 600;
}

#user-tools {
    grid-area: menu;
    display: flex;
    align-items: center;
    gap: 10px;
    /* Django's base.css floats and right-margins this for its old single-row
       flex layout — neither means anything inside a grid cell. */
    float: none;
    margin: 0;
}

/* Django's own #user-tools is font-weight 300 at 11px, which reads as washed
   out against the brand gradient. Scoped away from #logout-form button now
   that it lives inside the user-menu dropdown (styled with the rest of that
   dropdown, below) rather than as plain text directly in #user-tools. */
#user-tools {
    font-weight: 500 !important;
    font-size: 0.75rem;
}

#branding h1,
#branding #site-name {
    font-family: var(--font-display);
    font-weight: 600;
    letter-spacing: -0.2px;
    font-size: 1.2rem;
    white-space: nowrap;
}

/* Django's own rule colors #site-name links with --accent, which is too
   low-contrast on the gradient — use the plain header link color. */
#site-name a:link,
#site-name a:visited {
    color: var(--header-link-color);
}

/* The mark carries its own rounded outline, so no ring here — a box-shadow
   would trace the square border-box and cut across the rounded corners. */
#site-name a img {
    display: block;
}

.brand-text {
    display: flex;
    flex-direction: column;
    line-height: 1.2;
}

.brand-subtitle {
    font-size: 0.54rem;
    font-weight: 500;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--header-color);
    opacity: 0.8;
    /* Plain English text — in an Arabic (RTL) session the page's own
       direction otherwise flips it, landing the "&" on the wrong side. */
    direction: ltr;
    unicode-bidi: isolate;
}

/* Language toggle: a compact EN/AR segmented control, both options always
   visible, the active one raised so it's clear which language you're on. */
.lang-toggle {
    display: inline-flex;
    margin: 0;
    padding: 3px;
    gap: 2px;
    background: rgba(0, 0, 0, 0.22);
    border: 1px solid rgba(255, 255, 255, 0.14);
    border-radius: 999px;
    box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.35);
}

.lang-toggle-btn {
    border: none;
    background: transparent;
    color: rgba(255, 255, 255, 0.72);
    font-family: var(--font-display);
    font-weight: 500;
    font-size: 0.72rem;
    letter-spacing: 0.5px;
    padding: 5px 14px;
    border-radius: 999px;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}

.lang-toggle-btn:hover {
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
}

.lang-toggle-active,
.lang-toggle-active:hover {
    background: linear-gradient(180deg, #ffffff 0%, #e4edef 100%) !important;
    color: var(--header-grad-a) !important;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.9);
}

/* Pairs the theme toggle and the language toggle together wherever they
   appear (the login page's branding row, and the logged-in user-tools row) —
   one small cluster of "how this page looks/reads" controls, kept apart from
   the user-menu icon beside it. */
.header-toolbar {
    display: flex;
    align-items: center;
    gap: 10px;
}

/* ---- Branch switcher ---- */
.branch-switcher {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    margin: 0;
    /* Extra breathing room from the brand name/logo beside it. */
    margin-inline-start: 22px;
}

.branch-switcher label {
    color: var(--header-color);
    font-size: 0.74rem;
    letter-spacing: 0.4px;
    margin: 0;
}

/* Sits on the dark header band, so it takes its own colours rather than the
   light form-control tokens the rest of the inputs use. */
.branch-switcher select {
    background: rgba(0, 0, 0, 0.24);
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.22);
    border-radius: 999px;
    padding: 8px 18px;
    min-width: 160px;
    font-size: 0.82rem;
    font-family: var(--font-display);
    box-shadow: none;
    max-width: 220px;
}

.branch-switcher select option {
    color: var(--body-fg);
    background: var(--surface);
}

.branch-label {
    color: var(--header-color);
    font-size: 0.76rem;
    border: 1px solid rgba(255, 255, 255, 0.22);
    border-radius: 999px;
    padding: 5px 12px;
}

/* ---- Top nav ----
 *
 * The horizontal menu that replaced the old per-page app-list cards (see
 * HRAdminSite._nav_sections) — one bar, fed by each_context, so it renders
 * identically on literally every admin page. Dropdowns open on hover/focus
 * for a mouse or keyboard with no JS at all; static/js/top_nav.js only adds a
 * click-toggle on top of that so a touch screen (no hover) can open one too.
 *
 * grid-area: nav (see #header above) is what spans this across the full
 * header width as its own row, under branding/greeting/user menu.
 */
.top-nav {
    grid-area: nav;
    padding-top: 10px;
    border-top: 1px solid rgba(255, 255, 255, 0.1);
    position: relative;
    z-index: 20;
}

.top-nav-list {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    margin: 0;
    padding: 0;
    list-style: none;
    max-width: 1640px;
    margin-inline: auto;
    /*
     * No overflow-x here on purpose: setting overflow-x alone (with
     * overflow-y left at its default) makes the browser silently bump
     * overflow-y up to "auto" too (per the CSS Overflow spec), turning this
     * row into a clipping/scrolling box on BOTH axes — which trapped every
     * dropdown (position:absolute, extending below this row) inside a
     * sliver of the header instead of letting it overlay the page like an
     * ordinary site menu. flex-wrap above already handles a narrow viewport
     * by wrapping onto more lines, so no horizontal scroll is needed anyway.
     */
}

.top-nav-item {
    position: relative;
    display: flex;
}

/*
 * Prefixed with #header throughout this block (rather than the bare class
 * alone): base.css's own "#header a:link, #header a:visited" and
 * "#header a:focus, #header a:hover" rules carry an ID in their selector, so
 * without matching that weight here, this nav's link colours and hover
 * underline would lose to Django's — invisibly, since it only breaks the
 * cascade rather than raising an error.
 */
#header .top-nav-link {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 12px 14px;
    border: none;
    background: none;
    color: var(--header-color);
    font-family: var(--font-display);
    font-weight: 500;
    font-size: 0.82rem;
    letter-spacing: 0.1px;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    box-shadow: none;
    border-radius: 0;
    transition: background 0.12s ease, color 0.12s ease;
}

#header .top-nav-link:hover,
#header .top-nav-item.top-nav-open > .top-nav-trigger,
#header .top-nav-has-dropdown:hover > .top-nav-trigger,
#header .top-nav-has-dropdown:focus-within > .top-nav-trigger {
    background: rgba(255, 255, 255, 0.08);
    color: #fff;
    text-decoration: none;
}

#header .top-nav-home {
    font-weight: 600;
    color: #fff;
}

#header .top-nav-home:hover {
    background: rgba(255, 255, 255, 0.12);
    text-decoration: none;
}

.top-nav-icon {
    width: 16px;
    height: 16px;
    flex: none;
}

.top-nav-chevron {
    width: 13px;
    height: 13px;
    flex: none;
    transition: transform 0.15s ease;
}

.top-nav-has-dropdown:hover > .top-nav-trigger .top-nav-chevron,
.top-nav-has-dropdown:focus-within > .top-nav-trigger .top-nav-chevron,
.top-nav-item.top-nav-open .top-nav-chevron {
    transform: rotate(180deg);
}

.top-nav-dropdown {
    position: absolute;
    inset-inline-start: 0;
    top: 100%;
    z-index: 30;
    min-width: 220px;
    margin: 0;
    padding: 6px;
    list-style: none;
    background: var(--surface);
    border: 1px solid var(--hairline-color);
    border-radius: var(--radius-control);
    box-shadow: var(--shadow-lg);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px);
    transition: opacity 0.12s ease, transform 0.12s ease, visibility 0.12s;
}

.top-nav-has-dropdown:hover > .top-nav-dropdown,
.top-nav-has-dropdown:focus-within > .top-nav-dropdown,
.top-nav-item.top-nav-open > .top-nav-dropdown {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

.top-nav-dropdown li {
    list-style: none;
}

/* #header-prefixed for the same reason as #header .top-nav-link above: this
   panel sits on a light surface, so losing this color to base.css's
   "#header a:link" rule would leave near-white text on it. */
#header .top-nav-dropdown a {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    border-radius: calc(var(--radius-control) - 4px);
    color: var(--body-fg);
    font-size: 0.82rem;
    font-weight: 500;
    text-decoration: none;
    white-space: nowrap;
}

#header .top-nav-dropdown a:hover,
#header .top-nav-dropdown a:focus-visible {
    background: var(--selected-row);
    color: var(--link-fg);
    text-decoration: none;
}

/* The logout entry is a <button> (its own <form>), not a link — styled to
   match the <a> rows exactly so the dropdown reads as one consistent list. */
#header .top-nav-dropdown button {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 10px 12px;
    border: none;
    border-radius: calc(var(--radius-control) - 4px);
    background: none;
    box-shadow: none;
    color: var(--body-fg);
    font-family: var(--font-display);
    font-size: 0.82rem;
    font-weight: 500;
    text-align: start;
    cursor: pointer;
}

#header .top-nav-dropdown button:hover,
#header .top-nav-dropdown button:focus-visible {
    background: var(--selected-row);
    color: var(--link-fg);
}

/* The icon that can precede a dropdown row's label (user-menu items only —
   plain nav dropdowns have no icon and this rule simply matches nothing
   there). */
#header .top-nav-dropdown a svg,
#header .top-nav-dropdown button svg {
    width: 16px;
    height: 16px;
    flex: none;
    color: var(--body-quiet-color);
}

#header .top-nav-dropdown a:hover svg,
#header .top-nav-dropdown button:hover svg {
    color: var(--link-fg);
}

.user-menu-divider {
    height: 1px;
    margin: 6px 4px;
    background: var(--hairline-color);
    list-style: none;
}

/* ---- User menu (header) ----
 *
 * Reuses the top nav's dropdown mechanics wholesale (same classes, same
 * hover/focus-within/click-toggle behaviour) — only the trigger's shape and
 * the dropdown's own alignment differ, since this sits at the header's far
 * end rather than in a left-to-right menu bar.
 */
.user-menu {
    position: relative;
}

.user-menu-trigger {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid rgba(255, 255, 255, 0.22);
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.16);
    color: var(--header-color);
    cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease;
}

.user-menu-trigger svg {
    width: 18px;
    height: 18px;
}

#header .user-menu-trigger:hover,
#header .user-menu.top-nav-open > .user-menu-trigger,
#header .user-menu:focus-within > .user-menu-trigger {
    background: rgba(255, 255, 255, 0.18);
    color: #fff;
}

/* Opens toward the start edge instead of the top nav's default (aligned to
   the trigger's start side) — anchored at the header's far end, an
   start-aligned panel would run off the edge of the page. */
.user-menu-dropdown {
    inset-inline-start: auto;
    inset-inline-end: 0;
    top: calc(100% + 8px);
    min-width: 210px;
    padding: 8px;
}

@media (max-width: 900px) {
    .top-nav-list {
        padding: 0 12px;
    }

    .header-greeting {
        display: none;
    }
}

/* ---- Breadcrumbs ---- */
div.breadcrumbs {
    padding: 11px 32px;
    font-size: 0.78rem;
    border-bottom: 1px solid var(--hairline-color);
    box-shadow: none;
}

/* ---- Typography ---- */
h1,
h2,
h3,
.module h2,
.module caption,
#changelist-filter h2 {
    font-family: var(--font-display);
    letter-spacing: -0.2px;
}

#content h1 {
    font-size: 1.5rem;
    font-weight: 600;
    color: var(--body-loud-color);
    margin-bottom: 18px;
}

/* ---- Page structure ----
 * Django hard-codes the dashboard to a 600px column (base.css
 * ".dashboard #content { width: 600px }") and positions the right-hand column
 * with a negative float margin. On anything wider than a laptop that leaves
 * most of the screen empty, so the dashboard becomes a real two-track flex
 * layout instead. Flex also drops the physical left/right margins, which is
 * what kept the float layout from mirroring cleanly in Arabic.
 */
.dashboard #content {
    width: auto;
    max-width: 1640px;
    margin-inline: auto;
    padding: 28px 32px 48px;
    display: grid;
    grid-template-columns: minmax(0, 1fr) 340px;
    align-items: start;
    gap: 24px;
}

/* base.html puts the page title (wrapped in .titles-and-tools) and a float
   clearing <br> inside #content, so both become grid items. The title has to
   span the full width, and the <br> would otherwise occupy a cell and push the
   two real columns out of alignment. Each dashboard-section (the counter
   rows) joins them here for the same reason: #content-main and
   #content-related below get no grid-column of their own — they land in the
   wide/narrow track purely by which one comes next in the source — so any
   other unpinned child ahead of them would take the wide track for itself
   and bump #content-main into the 340px sidebar column instead. */
.dashboard #content > .titles-and-tools,
.dashboard #content > h1,
.dashboard #content > .dashboard-section {
    grid-column: 1 / -1;
}

.dashboard #content > .titles-and-tools,
.dashboard #content > h1 {
    margin: 0;
}

.dashboard #content > br.clear {
    display: none;
}

body.dashboard.colMS {
    margin-right: 0;
}

.dashboard #content-main {
    float: none;
    flex: 1 1 auto;
    min-width: 0;
}

/*
 * Pinned explicitly rather than left to auto-placement: the dashboard no
 * longer has an #content-main sibling ahead of it (the old app-list card grid
 * moved into the top nav bar), so with nothing else competing for the wide
 * track, an unpinned #content-related would auto-place into it instead of the
 * narrow one.
 */
.dashboard #content-related {
    grid-column: 2;
    float: none;
    width: auto;
    margin-right: 0;
    margin-left: 0;
}

/*
 * The app cards flow into as many columns as the window allows instead of
 * stacking in one narrow strip. Multi-column rather than grid: the cards vary
 * from one row to five, and a grid row is as tall as its tallest card, which
 * left a large gap under every short one. Columns pack them end to end.
 */
.dashboard #content-main.app-list {
    column-width: 330px;
    column-gap: 22px;
}

.dashboard #content-main.app-list > .module {
    /* Keep a card from being split across two columns. */
    break-inside: avoid;
    -webkit-column-break-inside: avoid;
}

@media (max-width: 1100px) {
    .dashboard #content {
        grid-template-columns: minmax(0, 1fr);
    }
}

#content {
    padding: 24px 32px 48px;
}

/* ---- Cards / modules ---- */
.module,
.dashboard .module,
#content-related .module,
#changelist-filter,
.inline-group {
    background: var(--surface);
    border: 1px solid var(--hairline-color);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-md);
    overflow: hidden;
}

/*
 * Django paints module headings with --primary and white text — a solid bar of
 * brand color on every block flattens the hierarchy. They become quiet titles
 * on the card surface, with the rule doing the separating.
 */
.module h2,
.module caption,
.dashboard .module h2,
.dashboard .module caption,
#changelist-filter h2,
.inline-group h2 {
    background: transparent;
    color: var(--body-loud-color);
    font-weight: 600;
    font-size: 0.82rem;
    letter-spacing: 0.4px;
    text-transform: uppercase;
    /* Django hardcodes text-align: left, which strands these titles on the
       wrong edge in Arabic. */
    text-align: start;
    padding: 14px 18px 10px;
    border-bottom: 1px solid var(--hairline-color);
}

/* base.css colors a.section with --header-link-color (white), which was
   correct on Django's solid brand-colored heading bar but disappears now that
   the heading sits on the card surface. */
.module caption a.section:link,
.module caption a.section:visited,
.module h2 a.section:link,
.module h2 a.section:visited {
    color: inherit;
    text-decoration: none;
}

/* column-gap only separates the columns; the bottom margin is what spaces
   cards down a column, so it is set to match rather than zeroed. */
.dashboard #content-main.app-list > .module {
    margin-bottom: 22px;
}

#content-related .module {
    margin-bottom: 22px;
}

#content-related .module:last-child {
    margin-bottom: 0;
}

.dashboard .module table td,
.dashboard .module table th {
    padding: 11px 18px;
    border-bottom: 1px solid var(--hairline-color);
}

.dashboard .module table tr:last-child td,
.dashboard .module table tr:last-child th {
    border-bottom: none;
}

.dashboard .module table tr:hover td,
.dashboard .module table tr:hover th {
    background: var(--surface-2);
}

.dashboard .module table th a,
.dashboard .module table td a {
    font-weight: 500;
}

/*
 * The two dashboard notices ("you have N requests waiting" for HR, and
 * "updates on your requests" for employees) are the one place a warm accent is
 * spent, so they read as the thing to look at first. They must also stay
 * legible in dark mode, which a hardcoded pale yellow cannot.
 */
/* Selected through #content-related so this outranks the "#content-related
   .module" card rule above, which would otherwise repaint them as plain
   surfaces. */
#content-related #pending-review-module,
#content-related #request-updates-module {
    background: var(--notice-bg);
    border-color: var(--notice-border);
    border-inline-start: 4px solid var(--notice-rail);
}

#content-related #pending-review-module h2,
#content-related #request-updates-module h2 {
    background: transparent;
    color: var(--body-loud-color);
    border-bottom-color: var(--notice-border);
    text-transform: none;
    font-size: 0.95rem;
    letter-spacing: -0.1px;
}

#content-related .module h2 {
    background: transparent;
}

/* ---- Recent actions ----
 *
 * base.css hangs the add/change/delete icon off the <li> as a background image
 * with padding-left:16px. The icon then touches the first letter, a title long
 * enough to wrap runs its second line back underneath the icon, and the
 * physical padding puts the icon on the wrong side in Arabic. Giving the icon
 * a column of its own fixes all three at once.
 *
 * The inset is the other half of it. Everything else in these cards stands
 * 18px off the edge — the headings and the dashboard tables both do — and the
 * lists were the exception, so their text ran into the card's own border. The
 * padding goes on the items rather than the list so the separator rules still
 * reach the full width of the card.
 */
#content-related .actionlist {
    margin: 0;
    padding: 0;
}

#content-related .module h3,
/* The empty-state line a card shows instead of its list. */
#content-related .module > p {
    padding-inline: 18px;
}

/*
 * The icon is positioned into a gutter rather than made a flex/grid cell: the
 * title, a <br> and the model name are loose children of the <li>, so any
 * layout that turns them into separate boxes splits the entry across columns.
 * A padded block with an absolutely-placed icon keeps the text in normal flow,
 * which is also what indents a wrapped second line past the icon.
 */
#content-related .actionlist li {
    position: relative;
    padding: 11px 18px;
    background: none;
    border-bottom: 1px solid var(--hairline-color);
    line-height: 1.45;
}

/*
 * Only entries that actually carry an icon get the gutter. The pending-review
 * list uses the same .actionlist with no icon class, and indenting it for an
 * icon that never arrives just pushes it off the card's margin.
 */
#content-related .actionlist li.addlink,
#content-related .actionlist li.changelink,
#content-related .actionlist li.deletelink {
    /* The card's own 18px, plus the gutter the icon sits in. */
    padding-inline-start: 44px;
}

/* No rule to separate it from, but it still needs the card's own breathing
   room underneath — 2px left the last line sitting on the bottom border. */
#content-related .actionlist li:last-child {
    border-bottom: none;
    padding-bottom: 14px;
}

#content-related .actionlist li.addlink::before,
#content-related .actionlist li.changelink::before,
#content-related .actionlist li.deletelink::before {
    content: "";
    position: absolute;
    inset-inline-start: 18px;
    top: 13px;
    width: 16px;
    height: 16px;
    background-repeat: no-repeat;
    background-size: 16px 16px;
}

/* Relative to this file's own URL; collectstatic rewrites these to the hashed
   names along with every other url() here. */
#content-related .actionlist li.changelink::before {
    background-image: url("../admin/img/icon-changelink.b592a93b41be.svg");
}

#content-related .actionlist li.addlink::before {
    background-image: url("../admin/img/icon-addlink.cf2357b7d0e6.svg");
}

#content-related .actionlist li.deletelink::before {
    background-image: url("../admin/img/icon-deletelink.f736b5dbf79e.svg");
}

/* The model name is given its own line by the rule below, so the markup's
   <br> would only add an empty one. */
#content-related .actionlist li br {
    display: none;
}

/*
 * Recent actions only: the entry is an object name, not a call to action, so
 * it reads as text with the icon carrying the meaning. The pending-review list
 * next to it is the opposite — those lines exist to be clicked, so they keep
 * Django's link styling.
 *
 * A deleted object has no link at all, only text, so the weight and colour
 * have to sit on the item and the link only adds its hover.
 */
#recent-actions-module .actionlist li {
    color: var(--body-fg);
    font-weight: 500;
    /* An object name can mix Arabic and Latin — isolate it so the surrounding
       direction doesn't reorder the two halves. */
    unicode-bidi: isolate;
}

#recent-actions-module .actionlist li > a {
    color: inherit;
    text-decoration: none;
}

#recent-actions-module .actionlist li > a:hover {
    color: var(--link-fg);
    text-decoration: underline;
}

#content-related .actionlist li .mini.quiet {
    display: block;
    margin-top: 2px;
    font-size: 0.72rem;
    font-weight: 400;
    color: var(--body-quiet-color);
}

/* "My actions" — a caption over the list, not a third heading competing with
   the module title above it. */
#recent-actions-module h3 {
    margin: 0 0 2px;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--body-quiet-color);
}

/* ---- Buttons: raised, with a real press ---- */
.button,
input[type="submit"],
input[type="button"],
/*
 * The two arrows between the permission boxes are excluded because they have
 * no text: Django draws each as a slice of a sprite in background-image, and
 * the gradient below replaces that image, leaving blank squares with no way
 * to tell which direction each one moves. "Choose all" and "Remove all" beside
 * them carry words, so they keep the button treatment.
 *
 * Wrapped in :where() so the exclusion costs no specificity — this rule has to
 * stay at the weight it was, or it starts outranking the narrower button rules
 * further down this file.
 */
button:not(.lang-toggle-btn):not(.theme-toggle):not(.top-nav-trigger):not(.action-btn):not(:where(.selector-add, .selector-remove)),
.submit-row input,
.submit-row a.button,
a.button,
.object-tools a {
    font-family: var(--font-display);
    font-weight: 500;
    font-size: 0.82rem;
    letter-spacing: 0.1px;
    border: none;
    border-radius: var(--radius-control);
    padding: 9px 18px;
    background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.22) 0%, rgba(0, 0, 0, 0.1) 100%);
    box-shadow: var(--bevel), var(--shadow-sm);
    cursor: pointer;
    transition: transform 0.08s ease, box-shadow 0.15s ease, filter 0.15s ease;
}

.button:hover,
input[type="submit"]:hover,
input[type="button"]:hover,
.submit-row input:hover,
a.button:hover,
.object-tools a:hover {
    filter: brightness(1.06);
    box-shadow: var(--bevel), var(--shadow-md);
}

.button:active,
input[type="submit"]:active,
input[type="button"]:active,
.submit-row input:active,
a.button:active,
.object-tools a:active {
    transform: translateY(1px);
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.28);
}

.button:focus-visible,
input[type="submit"]:focus-visible,
input[type="button"]:focus-visible,
a.button:focus-visible,
.object-tools a:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* The primary action gets the brand gradient; the rest stay neutral so one
   button per row reads as the main one. */
.submit-row input.default,
.button.default,
input[type="submit"].default {
    background-image: linear-gradient(180deg, var(--btn-grad-a) 0%, var(--btn-grad-b) 100%);
    color: var(--primary-fg);
}

.submit-row input.default:hover,
.button.default:hover,
input[type="submit"].default:hover {
    background-image: linear-gradient(180deg, var(--btn-grad-a-hover) 0%, var(--btn-grad-b-hover) 100%);
}

.object-tools a {
    text-transform: none;
    letter-spacing: 0.1px;
}

.submit-row {
    background: var(--surface);
    border: 1px solid var(--hairline-color);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-sm);
    padding: 14px 18px;
    display: flex;
    gap: 10px;
    align-items: center;
    flex-wrap: wrap;
}

.submit-row a.deletelink {
    border-radius: var(--radius-control);
    padding: 9px 18px;
}

/* ---- Inputs ---- */
input[type="text"],
input[type="password"],
input[type="email"],
input[type="url"],
input[type="tel"],
input[type="number"],
input[type="date"],
input[type="time"],
textarea,
select {
    border-radius: var(--radius-control);
    border: 1px solid var(--border-color);
    background: var(--surface);
    color: var(--body-fg);
    padding: 8px 12px;
    box-shadow: inset 0 1px 2px rgba(16, 38, 46, 0.07);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

/*
 * base.css pins every select to a fixed height of 1.875rem (30px), measured
 * border-box. The 8px of vertical padding above leaves only ~12px of content
 * box inside that, which clips the selected option's text along the bottom on
 * every form in the admin. Selects need a height that accounts for the
 * padding, matching the text inputs they sit beside.
 *
 * Left as a bare element selector on purpose: the narrow-width rules in
 * responsive.css (.form-row select) and the changelist action dropdown
 * (.actions select) are more specific, so both keep their own sizing.
 */
select {
    height: 2.3125rem;
}

input[type="text"]:focus,
input[type="password"]:focus,
input[type="email"]:focus,
input[type="url"]:focus,
input[type="tel"]:focus,
input[type="number"]:focus,
input[type="date"]:focus,
input[type="time"]:focus,
textarea:focus,
select:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 22%, transparent);
}

/* ---- Tables ---- */
#changelist table {
    border-collapse: separate;
    border-spacing: 0;
}

#result_list thead th {
    background: var(--surface-2);
    border-bottom: 1px solid var(--border-color);
    font-weight: 600;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--body-quiet-color);
    padding: 12px 14px;
    text-align: center;
}

#result_list thead th a {
    /* Django wraps the header label in a sort link; center that too instead
       of leaving it flush to whichever side the sort-arrow float lands on. */
    display: block;
}

#result_list tbody td,
#result_list tbody th {
    padding: 12px 14px;
    border-bottom: 1px solid var(--hairline-color);
    font-variant-numeric: tabular-nums;
    text-align: center;
}

#result_list tbody tr:hover td,
#result_list tbody tr:hover th {
    background: var(--surface-2);
}

#changelist .results {
    background: var(--surface);
    border: 1px solid var(--hairline-color);
    border-top-left-radius: var(--radius-card);
    border-top-right-radius: var(--radius-card);
    box-shadow: var(--shadow-md);
    overflow-x: auto;
}

#changelist .paginator {
    background: var(--surface);
    border: 1px solid var(--hairline-color);
    border-top: none;
    border-bottom-left-radius: var(--radius-card);
    border-bottom-right-radius: var(--radius-card);
    box-shadow: var(--shadow-md);
}

#toolbar {
    background: var(--surface);
    border: 1px solid var(--hairline-color);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-sm);
    padding: 12px 16px;
}

#changelist-filter li.selected a {
    font-weight: 600;
}

.paginator {
    border-top: 1px solid var(--hairline-color);
    padding: 14px 16px;
}

/* ---- Changelist actions: buttons instead of a dropdown ----
 *
 * See templates/admin/actions.html and static/js/action_buttons.js — the
 * native <select>+"Run" stay in the DOM (hidden here) and still do the real
 * submit; .action-buttons is what the script fills in, one button per
 * action this page's ModelAdmin actually defines.
 */
.actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    padding: 10px 4px;
}

.native-action-controls {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
}

.action-buttons {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.action-btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    border: 1px solid transparent;
    border-radius: 999px;
    padding: 8px 16px;
    font-family: var(--font-display);
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
    transition: filter 0.12s ease, transform 0.08s ease, opacity 0.12s ease;
}

.action-btn svg {
    width: 16px;
    height: 16px;
    flex: none;
}

.action-btn:disabled {
    opacity: 0.42;
    cursor: not-allowed;
}

.action-btn:not(:disabled):hover {
    filter: brightness(1.06);
}

.action-btn:not(:disabled):active {
    transform: translateY(1px);
}

.action-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Delete: an icon-only circular button — the trash mark alone is the label. */
.action-btn-icon-only {
    padding: 8px;
    border-radius: 50%;
}

.action-btn-delete {
    background: var(--negative-bg);
    color: var(--negative-fg);
    border-color: color-mix(in srgb, var(--negative-fg) 25%, transparent);
}

.action-btn-approve {
    background: var(--positive-bg);
    color: var(--positive-fg);
    border-color: color-mix(in srgb, var(--positive-fg) 25%, transparent);
}

.action-btn-reject {
    background: var(--negative-bg);
    color: var(--negative-fg);
    border-color: color-mix(in srgb, var(--negative-fg) 25%, transparent);
}

.action-btn-generic {
    background: var(--surface-2);
    color: var(--body-medium-color);
    border-color: var(--border-color);
}

/* ---- "فتح PDF" + "تصدير Excel" buttons ----
 *
 * Two places use this exact same class: every changelist's object-tools
 * (see templates/admin/change_list.html) and PayrollRunAdmin.total_net_salary
 * (a shortcut straight into Payslip's own export, pre-filtered to that run).
 * Same shape/size/weight as Django's own "Add" button next to them (still
 * .object-tools a underneath), but in the brand teal rather than Django's
 * own muted --object-tools-bg navy, which read as too close to the page's
 * other dark chrome (the header) to stand out as its own clickable action.
 * The a:link/a:visited form (not a bare class) is deliberate: a plain
 * `.table-tool-btn` selector loses the specificity fight against base.css's
 * own `a:link` rule (an element+pseudo-class beats a lone class), so the
 * selector has to match that shape to reliably win wherever this class is
 * used, not just inside #content.
 */
a.table-tool-btn:link,
a.table-tool-btn:visited {
    display: inline-block;
    padding: 3px 12px;
    border-radius: 15px;
    background: var(--primary);
    color: var(--primary-fg, #fff);
    font-weight: 400;
    font-size: 0.6875rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    text-decoration: none;
}

a.table-tool-btn:hover,
a.table-tool-btn:focus {
    background-color: var(--secondary, var(--primary));
    text-decoration: none;
}

/* A manual Ctrl+P (or the browser's own print) on a changelist still gets a
   clean page out of it: chrome stripped down to the results table, centred
   like it already is on screen (see #result_list above). Nothing in the UI
   triggers this directly anymore — "فتح PDF" is the button for that — but
   there's no reason the browser's own print should look broken either. */
@media print {
    #header,
    div.breadcrumbs,
    #toolbar,
    #changelist-filter,
    #changelist-filter-header,
    .actions,
    .object-tools,
    .paginator,
    .changelist-footer,
    ul.messagelist,
    #content-related,
    .submit-row {
        display: none !important;
    }

    body {
        background: #fff;
    }

    #content,
    .dashboard #content {
        padding: 0;
        margin: 0;
        max-width: none;
        display: block;
    }

    #changelist .results {
        box-shadow: none;
        border: 1px solid #000;
        overflow: visible;
    }

    #result_list {
        width: 100%;
    }

    #result_list thead th,
    #result_list tbody td,
    #result_list tbody th {
        border: 1px solid #000;
        color: #000;
        background: #fff !important;
    }
}

/* ---- Messages ---- */
/*
  base.css pairs padding-left:65px with the icon at background-position 40px,
  and rtl.css mirrors both. Overriding only the padding left the icon sitting on
  top of the first word, so the icon moves with the padding here. This rule has
  the same specificity as rtl.css and loads after it, so it also wins in Arabic
  -- hence the explicit [dir="rtl"] line to put the icon back on the right.
*/
ul.messagelist li {
    border-radius: var(--radius-control);
    box-shadow: var(--shadow-sm);
    padding: 12px 16px;
    padding-inline-start: 46px;
    background-position: 16px 14px;
    font-weight: 500;
}

[dir="rtl"] ul.messagelist li {
    background-position-x: calc(100% - 16px);
}

/* ---- Login page ---- */
/*
 * Centred in the viewport, not parked under the top edge: margin-top:9vh put
 * the card in the upper third of a 1440x1000 screen with ~500px of dead space
 * beneath it, which read as an unfinished page rather than a focused one.
 */
body.login {
    min-height: 100vh;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    padding: 24px;
    background:
        radial-gradient(1100px 620px at 78% -12%, color-mix(in srgb, var(--primary) 26%, transparent) 0%, transparent 62%),
        var(--body-bg);
}

/*
 * The whole viewport behind the login card: four photographs of the work this
 * system is for — a team, the reader they clock in on, someone at their desk,
 * and pay being counted — composed into one 2x2 image so the page fetches a
 * single file rather than four.
 *
 * position:fixed rather than a background on body.login itself, for two
 * reasons: that body is the flex container centring the card, so anything left
 * in flow would become a flex item and shove the card off centre; and fixed
 * keeps the picture filling the viewport rather than only the card's own
 * height. See static/img/login/CREDITS.md for the sources and their licence.
 */
body.login::before {
    content: "";
    position: fixed;
    inset: 0;
    background: url("../img/login/wall_wide.4ac0fde91e0f.jpg") center center / cover no-repeat;
    /*
     * A watermark, not a photograph: faded almost into the page's own ground
     * and most of the colour taken out. Four busy photographs at full strength
     * fought the form in front of them — pale and near-grey they read as
     * texture, and the eye goes to the card.
     */
    opacity: var(--login-photo-opacity, 0.18);
    filter: grayscale(0.65) contrast(0.9);
    pointer-events: none;
    z-index: 0;
}

/*
 * A portrait cut of the same four panels. The landscape one cropped to a phone
 * shows the middle of the wall and little else — which is the seam between the
 * four photographs, the one part of it that is not a picture of anything.
 */
@media (max-aspect-ratio: 1 / 1) {
    body.login::before {
        background-image: url("../img/login/wall_tall.e5cccbbe90a4.jpg");
    }
}

/*
 * No scrim over the photographs any more. One was needed while they were at
 * full strength; faded to a watermark they need nothing laid on top, and the
 * page's own tinted ground underneath them does the unifying.
 */

body.login #container {
    /* Above the illustration, which is z-index 0 — without this the card's own
       stacking context is behind the fixed pseudo-element. */
    position: relative;
    z-index: 1;
    box-shadow: var(--shadow-lg);
    border-radius: 18px;
    border: 1px solid var(--hairline-color);
    overflow: hidden;
    margin: 0;
    background: var(--surface);
    min-width: 0;
    width: min(440px, 100%);
}

body.login #header {
    /*
     * The login page only ever renders #branding (the greeting/user-menu/nav
     * blocks are all gated on an authenticated user) — #header's sitewide
     * 3-column grid would otherwise leave it pinned to the start edge instead
     * of centred, since an empty 1fr track has no content to push against.
     * Back to a plain centred flex row, same as before the grid existed.
     */
    display: flex;
    justify-content: center;
    padding: 26px 24px;
}

body.login #content {
    padding: 30px 34px 4px;
}

/*
 * The login page renders .titles-and-tools with nothing in it, and base.css
 * gives it a bottom margin — a blank band between the brand bar and the first
 * field.
 */
body.login .titles-and-tools,
body.login #content > br.clear {
    display: none;
}

body.login .form-row {
    padding: 0 0 18px;
}

/* Django emits the label and input as siblings with a text node between them,
   so the label needs to be a block to sit on its own line predictably. */
body.login .form-row label {
    display: block;
    margin-bottom: 7px;
    font-weight: 600;
    font-size: 0.82rem;
    color: var(--body-medium-color);
}

body.login .form-row input {
    width: 100%;
    box-sizing: border-box;
    padding: 11px 13px;
    font-size: 0.95rem;
}

body.login .submit-row {
    padding: 6px 34px 32px;
    border: none;
    box-shadow: none;
    background: transparent;
}

body.login .submit-row input {
    width: 100%;
    padding: 12px;
    font-size: 0.95rem;
}

/* Wrong-password and "you don't have permission" notices, which land directly
   above the form. */
body.login .errornote,
body.login .messagelist {
    margin: 0 34px 18px;
}

/*
 * A link styled as a button should not also be underlined — the report's
 * "Export to Excel" and "Print to PDF" had a rule through the middle of them.
 * base.css underlines every link outside its own exclusion list, and that
 * rule's :not() takes the specificity of its heaviest argument
 * (#content-main.app-list a), which puts it at (1,1,2) — so outranking it
 * needs a second id, not just one.
 */
#container #content a.button,
#container #content .submit-row a.button {
    text-decoration: none;
}

/* ---- Status pills ----
 *
 * A decision like "Deducted from salary" is a state, not prose, and as plain
 * text in a narrow changelist column it wrapped onto two lines on every single
 * row. A pill keeps it on one line and says which way the decision went
 * without the reader parsing the sentence.
 */
.status-pill {
    display: inline-block;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 600;
    line-height: 1.5;
    white-space: nowrap;
}

.status-pill-positive {
    background: var(--positive-bg);
    color: var(--positive-fg);
}

.status-pill-negative {
    background: var(--negative-bg);
    color: var(--negative-fg);
}

/* ---- Checkbox and its label ----
 *
 * forms.css spaces the label off its box with `padding: 1px 0 0 5px`. That is
 * a physical left padding, so in Arabic the 5px lands on the far side and the
 * word ends up hard against the box, reading as though it sits on top of it.
 * A gap on the flex row is direction-agnostic.
 *
 * The `body` prefix is needed because forms.css is pulled in by the form's own
 * media, after this file.
 */
body .form-row .checkbox {
    display: flex;
    align-items: center;
    gap: 8px;
}

body .aligned .vCheckboxLabel {
    padding: 0;
    margin: 0;
}

/* ---- A table joined to the section above it ----
 *
 * Used where an inline belongs to the fieldset it follows — the allowance
 * lines and the total they add up to are one section, and rendering them as
 * two cards with two headings says they are separate things.
 */
.merged-with-previous {
    margin-top: -18px;
}

.merged-with-previous .inline-group {
    margin-top: 0;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
}

/* The section already has a heading; a second one directly under it is just a
   restatement. The heading sits inside the inline's own fieldset, not directly
   on the group. */
.merged-with-previous .inline-group h2 {
    display: none;
}

.merged-with-previous .inline-group fieldset.module {
    border-top-left-radius: 0;
    border-top-right-radius: 0;
    box-shadow: none;
}

/* ---- Change form: the add/change/delete/view icons beside a related field ----
 *
 * Django draws these at 24px, which next to a 37px-tall control is heavy
 * enough to read as the main thing in the row, and wide enough that the four
 * of them wrap onto a line of their own under the field.
 */
.related-widget-wrapper-link img,
.related-widget-wrapper-link svg {
    width: 16px;
    height: 16px;
    vertical-align: middle;
}

.related-widget-wrapper {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    flex-wrap: nowrap;
    max-width: 100%;
}

.related-widget-wrapper-link {
    opacity: 0.75;
    transition: opacity 0.15s ease;
}

.related-widget-wrapper-link:hover {
    opacity: 1;
}

/* The date field's "Today | [calendar]" cluster, same treatment: it sat a full
   line below its input at a size that competed with it. */
.datetimeshortcuts .date-icon,
.datetimeshortcuts .clock-icon {
    width: 16px;
    height: 16px;
    background-size: 16px 16px;
}

/* Specific enough to beat the raised-button rule above, which matches every
   bare <button> and would otherwise wrap this icon in a full control. */
.datetimeshortcuts button[type="button"] {
    padding: 4px;
    line-height: 0;
    background: none;
    border: none;
    box-shadow: none;
    vertical-align: middle;
}

.datetimeshortcuts {
    font-size: 0.8rem;
    white-space: nowrap;
}

/* ---- Change form: field labels ---- */
body.change-form fieldset.module.aligned label {
    font-weight: 700;
}

/* ---- Change form: two fields to a row ----
 *
 * Django stacks every field in one narrow column, so a change form on a wide
 * screen was a 1000px-wide card holding 280px-wide inputs and a column of
 * blank space beside them. Pairing the fields uses the width the card already
 * takes up and halves how far the eye travels down the page.
 *
 * .aligned is on top-level fieldsets; inline fieldsets carry it too, so those
 * are put back to a single column below — an inline row is already narrow and
 * a grid inside it just fights the table it sits in.
 */
/*
 * Every change form in the system lays its fields out in columns, and how
 * many is a question about the width available rather than about a
 * breakpoint being cleared. These rules used to sit inside
 * @media (min-width: 1100px), which meant a browser zoomed far enough in —
 * or a laptop at 1280 with the sidebar open — reported a CSS width below it
 * and the whole form came back as one long column on a screen that is
 * physically wide.
 *
 * auto-fit asks it the right way round: as many columns as will fit without
 * any of them going under 320px. Five across a wide desktop, four on a laptop,
 * two on a tablet, one on a phone — and the single column on a phone is now
 * the grid's own answer rather than the absence of one.
 *
 * The 320px is a plain length and has to stay one: a track size inside
 * repeat(auto-fit, ...) has to be resolvable before the browser knows how many
 * tracks there are, so a percentage of the width — which depends on the track
 * count — makes the whole declaration do nothing, and every field comes back
 * in one column.
 */
body.change-form fieldset.module.aligned {
    display: block;
}

/* The grid is on the wrapper, not on the fieldset.
 *
 * It was on the fieldset, and every form in the system came back as a single
 * column the width of the page. The wrapper exists because a <details> is not
 * laid out as a grid of the rows written inside it — see
 * templates/admin/includes/fieldset.html — and it wraps every fieldset, not
 * only the collapsible ones. So the fieldset's grid had exactly one item in
 * it, the wrapper: four columns asked for, one thing placed.
 */
body.change-form fieldset.module.aligned .section-body {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    column-gap: 28px;
    align-items: start;
}

/*
 * A section's rows live inside the .section-body wrapper inside <details>,
 * so every rule written for a direct child of the fieldset has to reach
 * there too.
 *
 * The columns are written out rather than inherited from the fieldset. An
 * explicit "grid-template-columns: inherit" is legal and reads well, and
 * it laid every field out in a single narrow strip anyway: a track list on
 * a parent that is not itself a grid is not something to take a used value
 * from. Said outright, it cannot fail quietly.
 *
 * Four columns across every section, so moving between tabs does not mean
 * re-learning the shape of the page each time. A section with fewer fields
 * than that simply fills fewer cells; the rules further down still give a
 * textarea or a multi-select the whole row.
 *
 * The grid is on details[open] and not on details: a closed one is a
 * single summary, and a grid container there would stretch it.
 */
body.change-form fieldset.module.aligned.form-section {
    display: block;
}


/* The heading is a row of its own, above the columns it introduces. */
body.change-form fieldset.module.aligned.form-section > details > summary {
    grid-column: 1 / -1;
}

/* Anything that is not a single field, plus the fields that genuinely need
   the full width, runs across both columns. */
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > h2,
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .description,
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row:has(textarea),
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row:has(.selector) {
    grid-column: 1 / -1;
}

body.change-form .inline-group fieldset.module.aligned .section-body {
    display: block;
}

/*
 * base.css pins these to fixed em widths (.vTextField is 20em), which is
 * what left the inputs stranded at a third of the card. Filling the column
 * is the point of the grid.
 */
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row input[type="text"],
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row input[type="email"],
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row input[type="url"],
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row input[type="password"],
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row input[type="number"],
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row input[type="date"],
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row .datetime input.vDateField,
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row textarea,
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row select:not(.admin-autocomplete) {
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
}

/*
 * input[type="file"] was missing from the list above, so a file field
 * (e.g. Employee.personnel_file) kept the browser's own narrow default
 * width instead of filling its column like every other field beside it.
 */
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row input[type="file"] {
    max-width: 100%;
    box-sizing: border-box;
}

/*
 * A row grouping two fields explicitly (e.g. the personnel_file upload
 * next to the link that opens it) is still one grid cell — without this,
 * its two field-boxes stack instead of sitting side by side, which reads
 * as unrelated to the field beside it rather than as one paired control.
 */
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row:has(> .field-box + .field-box) {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px 16px;
}

body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row:has(> .field-box + .field-box) > .field-box {
    flex: 1 1 auto;
    min-width: 0;
}

/*
 * A field is a field: leaving the dates narrow while the text beside them
 * fills the column makes the grid look ragged. Django wraps a date in a
 * <p class="date"> that is itself a flex item, and it shrinks to its
 * content — so the input's 100% above was 100% of a shrunken box. The
 * wrapper has to claim the row before the input can fill it.
 */
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row .flex-container {
    flex-wrap: wrap;
}

body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) .form-row p.date,
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) .form-row p.time,
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) .form-row p.datetime {
    /* flex:none so the flex algorithm stops sizing it from its content and
       the declared width is what it gets. */
    flex: none;
    width: 100%;
    min-width: 0;
    margin: 0;
    /* The "Today | [calendar]" shortcuts sit beside the input rather than
       under it: on their own line they make every date field a row taller
       than the text field paired with it, and the two columns stop lining
       up all the way down the form.
       p.time is in the list because a plain time field — the shift's own
       check-in and check-out — was left out of it, and its clock button was
       the one still sitting underneath. */
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 8px;
}

/* A datetime is written as "label input <br> label input" inside one <p>.
   The break is what put the time half on a line of its own. */
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) .form-row p.datetime br {
    display: none;
}

body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) .form-row p.datetime label {
    flex: 0 0 auto;
    margin: 0;
    font-size: 0.82rem;
    color: var(--body-quiet-color);
}

body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) .form-row p.date input,
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) .form-row p.time input,
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) .form-row p.datetime input {
    /* A basis rather than auto: two inputs sharing a cell with their labels
       and two buttons otherwise shrink until the date shows one digit. */
    flex: 1 1 6.5em;
    min-width: 0;
}

body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) .form-row .datetimeshortcuts {
    flex: none;
    white-space: nowrap;
    /* Matched to the height of the control it sits beside, so a date row
       is exactly as tall as the text row paired with it. */
    display: flex;
    align-items: center;
    height: 2.3125rem;
}

/*
 * A checkbox carries its label beside it rather than above, so left alone
 * it sits level with the *label* of the field paired with it instead of
 * with that field's control. The class is on the inner container, not on
 * the row, hence :has().
 */
/*
 * A checkbox takes a line of its own. It carries its label alongside
 * rather than above, so paired with a normal field it sits level with that
 * field's label instead of its control and reads as though it belongs to
 * it; on its own row the two fields after it pair up cleanly instead.
 */
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row:has(.checkbox-row) {
    grid-column: 1 / -1;
}

/* Nothing in this grid may outgrow its column — a select sized by its
   longest option otherwise pushes wider than the fields above and below. */
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row select,
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row input {
    min-width: 0;
}

/*
 * A related field is [control][add][change][delete][view] inside one
 * wrapper. The wrapper is content-sized, so a control set to 100% only
 * filled the shrunken wrapper — the wrapper has to claim the column first,
 * then the control takes what the four icons don't.
 */
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row .related-widget-wrapper {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
}

body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row .related-widget-wrapper > select,
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row .related-widget-wrapper > .select2-container {
    flex: 1 1 auto;
    min-width: 0;
}

/* select2 writes a pixel width onto its own container as an inline style. */
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row .select2-container {
    width: auto !important;
}

/*
 * One hairline per field reads as a broken line once there are two fields
 * to a row; spacing alone separates them more cleanly.
 */
body.change-form :is(fieldset.module.aligned, fieldset.module.aligned .section-body) > .form-row {
    border-bottom: none;
    padding-block: 8px;
}

/*
 * "wide" fieldsets (Django's own Add User page, and any add_fieldsets
 * declared the same way) hold long, sensitive fields — a username, two
 * password fields, the password-authentication toggle — that read as one
 * short list, not as pairs. Forcing them into the two-column grid above
 * scattered its four rows across two mismatched columns instead; a
 * single column keeps them in the order they're declared.
 */
body.change-form fieldset.module.aligned.wide .section-body {
    display: block;
}

body.change-form :is(fieldset.module.aligned.wide, fieldset.module.aligned.wide > details) > .form-row {
    padding-block: 14px;
}

/*
 * The Employee form's "الحساب والتسجيل" section (is_active, photo, user,
 * created_by) — four short fields that read better across one row than
 * stacked two-by-two. User Account gets a narrower share of the row since
 * its control is a compact dropdown, not free text.
 */
body.change-form fieldset.module.aligned.account-record-grid .section-body {
    /* A checkbox, a photo, a compact dropdown and a name — narrower
       minimums than the sitewide one, because none of the four needs a
       quarter of a wide page to itself. */
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    column-gap: 24px;
}

/* Overrides the sitewide rule that forces a checkbox field to span both
   columns — here it is meant to take just its own single column, first. */
body.change-form :is(fieldset.module.aligned.account-record-grid, fieldset.module.aligned.account-record-grid > details) > .form-row:has(.checkbox-row) {
    grid-column: auto;
}

/* cols-3 and cols-4 used to pin a section to that many columns. The rule
   at the top of this block counts them from the width now, so the classes
   stay on the markup as a description of intent and set nothing. */

/*
 * A payroll run's own summary: status and the total take the wide share
 * of the row since they carry a label plus a value; the PDF/Excel links
 * take only what their own button needs (auto), rather than an equal
 * fourth each like cols-4 above — a button column that wide would just
 * be empty space around a small pill.
 */
body.change-form fieldset.module.aligned.payroll-summary-grid .section-body {
    /* Status and the total carry a label and a value and take the wide share;
       the two export links take only what their own button needs. Wrapped in
       a minmax so the row stacks on a narrow screen instead of running off
       it — these are no longer behind a desktop breakpoint. */
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    align-items: end;
}

/* The link fields carry no label of their own — the button text already
   says what it does, so an auto-generated "Pdf export link:" above it
   would be pure noise. */
body.change-form fieldset.module.aligned.payroll-summary-grid .field-pdf_export_link label,
body.change-form fieldset.module.aligned.payroll-summary-grid .field-excel_export_link label {
    display: none;
}


/* ---- Dashboard: counter rows ----
 *
 * Three labelled rows (today's activity, pending employee requests, and a
 * company-wide overview), each of equal-size cards in its own colour so a
 * row reads at a glance — and each card links to the already-filtered admin
 * list behind its number, so clicking one is the drill-down.
 */
.dashboard-section {
    margin-bottom: 28px;
}

.dashboard-section-title {
    margin: 0 0 12px;
    font-size: 0.98rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--body-loud-color);
}

.dashboard-counters {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
    gap: 16px;
}

.dash-card {
    /* Icon beside the figure, not above it: the number is what the row is
       read for, and anything stacked on top of it is one line the eye has to
       pass before reaching it. Two columns — a fixed tile and whatever is
       left — with both halves centred against each other. */
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: 13px;
    text-align: start;
    padding: 14px 16px;
    border-radius: var(--radius-card);
    background: var(--surface);
    border: 1px solid var(--hairline-color);
    /* The colour, and almost the only place it appears. A card painted
       entirely in it spends the whole budget on category: there is then
       nothing left to say "this is the number with a problem", and seven
       cards in seven colours leaves the eye nowhere to settle. */
    border-inline-start: 4px solid var(--rail);
    /* The card is white on a ground only a little darker, so the shadow is
       what separates them. The small one left the two indistinguishable. */
    box-shadow: var(--shadow-md);
    transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}

/* The figure and its label, as one block beside the icon. */
.dash-card-body {
    display: grid;
    gap: 2px;
    min-width: 0;
}

/*
 * a:link/a:visited (base.css) carries an extra pseudo-class in its selector,
 * which outranks a bare ".dash-card" — without matching that weight here the
 * cards lose their own colours and render underlined.
 */
a.dash-card:link,
a.dash-card:visited,
a.dash-card:hover,
a.dash-card:focus-visible,
a.dash-card:active {
    color: var(--body-fg) !important;
    text-decoration: none !important;
}

a.dash-card:hover,
a.dash-card:focus-visible {
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
    border-color: color-mix(in srgb, var(--rail) 45%, var(--hairline-color));
}

.dash-card-icon {
    flex: none;
    width: 40px;
    height: 40px;
    display: grid;
    place-items: center;
    border-radius: 12px;
    /* A wash of the rail's own colour, so the tile names the category twice
       without either saying it loudly. */
    background: color-mix(in srgb, var(--rail) 13%, var(--surface));
    color: var(--rail);
}

.dash-card-icon svg {
    width: 20px;
    height: 20px;
}

.dash-card-count {
    font-size: 1.75rem;
    font-weight: 800;
    line-height: 1.1;
    color: var(--body-loud-color);
    font-variant-numeric: tabular-nums;
}

.dash-card-label {
    font-size: 0.85rem;
    font-weight: 500;
    line-height: 1.4;
    color: var(--body-quiet-color);
}

.dash-card-suffix {
    font-size: 0.74rem;
    font-weight: 500;
    color: var(--body-quiet-color);
    margin-top: -2px;
}

/* One variable per card, read by the rail, the icon and the hover border
   above. The pairs stay as they were so the colours are the same ones the
   cards have always had — only how much of each card they cover has
   changed. */
.dash-card-good    { --rail: var(--card-good-b); }
.dash-card-amber   { --rail: var(--card-amber-b); }
.dash-card-red     { --rail: var(--card-red-b); }
.dash-card-blue    { --rail: var(--card-blue-b); }
.dash-card-purple  { --rail: var(--card-purple-b); }
.dash-card-orange  { --rail: var(--card-orange-b); }
.dash-card-neutral { --rail: var(--card-neutral-b); }

/* ---- Reports: daily-detail highlighting ----
 *
 * One colour per attendance problem, on the offending cell only (a day can be
 * both late and an early leave, and painting the whole row one colour would
 * lose that distinction) — shared in spirit with the PDF export's own
 * td.cell-late/cell-early-leave/cell-absent rules in report_pdf.html, though
 * that one can't use color-mix() (xhtml2pdf's CSS support is far narrower),
 * so the two are tuned to look the same rather than sharing actual values.
 * Mixed with --surface rather than a flat hex so both light and dark mode
 * fall out of the same rule for free.
 */
.cell-late { background: color-mix(in srgb, var(--card-amber-a) 24%, var(--surface)); }
.cell-early-leave { background: color-mix(in srgb, var(--card-orange-a) 24%, var(--surface)); }
.cell-absent { background: color-mix(in srgb, var(--card-red-a) 22%, var(--surface)); }

/* ---- Reports: filters and column picker ---- */
.report-filter-row {
    display: flex;
    flex-wrap: wrap;
    gap: 16px 28px;
    margin-bottom: 16px;
}

.report-filter-row .form-row {
    border: none;
    padding: 0;
    margin: 0;
}

.report-filter-search .form-row {
    flex: 1 1 100%;
    max-width: 480px;
}

.report-filter-search input[type="text"] {
    width: 100%;
}

.report-filter-search .help {
    margin: 5px 0 0;
    font-size: 0.74rem;
}

.report-filter-dates .form-row {
    /* Keeps both date inputs the same width so the range reads as one control
       rather than two unrelated fields. */
    min-width: 190px;
}

/* ---- Analytics page: headline figures and bar charts ----
 *
 * The charts are drawn with CSS heights alone — each bar arrives with its
 * height already worked out as a percentage of the tallest in its series (see
 * reports/analytics.py::as_bars), so there is nothing to measure in the
 * browser and no charting library to load.
 */
.analytics-headline {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 16px;
    margin-bottom: 20px;
}

.analytics-headline-card {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 18px 20px;
    background: var(--surface);
    border: 1px solid var(--hairline-color);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-sm);
}

.analytics-headline-value {
    font-family: var(--font-display);
    font-size: 1.5rem;
    font-weight: 600;
    color: var(--body-loud-color);
    font-variant-numeric: tabular-nums;
}

.analytics-headline-label {
    font-size: 0.78rem;
    color: var(--body-quiet-color);
}

.bar-chart {
    margin: 0;
    padding: 18px 18px 8px;
}

.bar-chart-plot {
    display: flex;
    align-items: flex-end;
    gap: 10px;
    /* Room for the tallest bar plus the value above it and the label below. */
    min-height: 200px;
    overflow-x: auto;
}

.bar-chart-bar {
    display: flex;
    flex: 1 1 0;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    gap: 6px;
    min-width: 46px;
    height: 100%;
}

.bar-chart-value {
    font-size: 0.72rem;
    font-variant-numeric: tabular-nums;
    color: var(--body-medium-color);
}

.bar-chart-fill {
    width: 100%;
    /* A bar for a zero month would otherwise vanish, and a gap in the row
       reads as missing data rather than as a month nothing happened in. */
    min-height: 2px;
    border-radius: 6px 6px 0 0;
    background: linear-gradient(180deg, var(--accent), var(--primary));
}

.bar-chart-label {
    font-size: 0.68rem;
    color: var(--body-quiet-color);
    text-align: center;
    white-space: nowrap;
}

.bar-chart figcaption {
    margin-top: 10px;
    font-size: 0.76rem;
    color: var(--body-quiet-color);
    text-align: center;
}

/*
 * The column checkboxes used to wrap as a ragged flex row: labels vary from
 * two words to seven, so nothing lined up. A fixed-track grid gives every
 * option the same box, in aligned columns, however many there are.
 */
.column-picker {
    border: 1px solid var(--hairline-color);
    border-radius: var(--radius-card);
    background: var(--surface);
    padding: 12px 16px 16px;
    margin: 4px 0 16px;
}

.column-picker > legend {
    padding: 0 8px;
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 0.76rem;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    color: var(--body-quiet-color);
}

.column-picker-head {
    display: flex;
    justify-content: flex-end;
    margin-bottom: 6px;
}

/*
 * A small utility control, not a page action — selected through .column-picker
 * so it outranks the raised-button rule and stays flat.
 */
.column-picker button.column-toggle-all {
    background: var(--surface);
    background-image: none;
    color: var(--link-fg);
    border: 1px solid var(--border-color);
    border-radius: 999px;
    box-shadow: none;
    font-size: 0.74rem;
    font-weight: 500;
    letter-spacing: 0;
    padding: 5px 14px;
}

.column-picker button.column-toggle-all:hover {
    background: var(--surface-2);
    box-shadow: none;
    filter: none;
}

.column-picker button.column-toggle-all:active {
    transform: translateY(1px);
    box-shadow: none;
}

.column-picker-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(235px, 1fr));
    gap: 4px 16px;
}

.column-option {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 8px 10px;
    border: 1px solid transparent;
    border-radius: var(--radius-control);
    font-size: 0.82rem;
    font-weight: 400;
    line-height: 1.35;
    cursor: pointer;
    transition: background 0.12s ease, border-color 0.12s ease;
}

.column-option input {
    margin: 0;
    flex: none;
}

.column-option:hover {
    background: var(--surface-2);
}

.column-option:has(input:checked) {
    background: var(--selected-row);
    border-color: color-mix(in srgb, var(--primary) 38%, transparent);
    font-weight: 500;
}

.column-option:focus-within {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

.detail-toggle {
    display: inline-flex;
    margin-bottom: 4px;
}

/* ---- Date and time pickers ----
 *
 * These are <dialog> elements, so the page behind them was being dimmed by a
 * 75%-black ::backdrop — a modal treatment for what is really a popover
 * hanging off a field. The panel itself also hardcodes `background: white` on
 * the table and #333/#ccc on the header and nav, none of which survive dark
 * mode. Restyled here against the tokens so both pickers match every other
 * card in the product.
 */
body .calendarbox::backdrop,
body .clockbox::backdrop {
    background: transparent;
}

body .calendarbox,
body .clockbox {
    width: 20em;
    background: var(--surface);
    color: var(--body-fg);
    border: 1px solid var(--hairline-color);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-lg);
    /* Anchored to the button that opened it by static/js/date_picker.js, which
       also keeps it inside the window; these two only cap it so it can never
       be taller or wider than the space available. */
    max-width: calc(100vw - 32px);
    max-height: calc(100vh - 32px);
    overflow: auto;
}

body .calendar table {
    background: var(--surface);
}

/* The month bar. base.css puts near-black text on --accent, which is a mid
   teal — legible in light mode by luck and not at all in dark. */
body .calendar caption,
body .calendarbox h2,
body .clockbox h2 {
    padding: 10px 0;
    /* base.css centres the calendar's caption but not the clock's heading. */
    text-align: center;
    font-family: var(--font-display);
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--primary-fg);
    background: linear-gradient(180deg, var(--btn-grad-a) 0%, var(--btn-grad-b) 100%);
}

body .calendar th {
    padding: 7px 5px;
    background: var(--surface-2);
    border-bottom: 1px solid var(--hairline-color);
    font-weight: 600;
    color: var(--body-quiet-color);
}

body .calendar td {
    border-top: 1px solid var(--hairline-color);
}

body .calendar td a,
body .timelist a {
    padding: 7px 6px;
    color: var(--body-medium-color);
    border-radius: var(--radius-control);
}

body .calendar td a:focus,
body .timelist a:focus,
body .calendar td a:hover,
body .timelist a:hover {
    background: color-mix(in srgb, var(--primary) 16%, transparent);
    color: var(--body-loud-color);
}

body .calendar td a:active,
body .timelist a:active {
    background: var(--primary);
    color: var(--primary-fg);
}

body .calendar td.selected a {
    background: var(--primary);
    color: var(--primary-fg);
    font-weight: 600;
}

body .calendar td.today a {
    color: var(--link-fg);
    font-weight: 700;
}

body .calendar td.nonday {
    background: var(--surface-2);
}

/* "Yesterday | Today | Tomorrow", and the clock's equivalent. */
body .calendar-shortcuts,
body .calendarbox .calendar-shortcuts {
    background: var(--surface-2);
    border-top: 1px solid var(--hairline-color);
    padding: 9px 0;
    color: var(--body-quiet-color);
}

body .calendar-shortcuts a {
    color: var(--link-fg);
    font-weight: 500;
}

/* A full-width grey slab reading "Cancel" was the heaviest thing in the panel;
   it is a dismiss link, so it reads as one. */
body .calendar-cancel,
body .clockbox .calendar-cancel {
    padding: 8px 0;
    background: var(--surface);
    border-top: 1px solid var(--hairline-color);
    color: var(--body-quiet-color);
}

body .calendar-cancel:focus,
body .calendar-cancel:hover {
    background: var(--surface-2);
}

body .calendar-cancel a {
    color: var(--body-quiet-color);
    font-weight: 500;
}

body .calendarnav {
    color: var(--body-quiet-color);
}

/*
 * The arrows sit on the gradient bar. Only background-color is set here: the
 * sprite's position is what picks which arrow is drawn, so touching
 * background-position or the box size would swap or crop them.
 */
body .calendarbox .calendarnav-previous,
body .calendarbox .calendarnav-next {
    top: 11px;
    border-radius: 50%;
    background-color: rgba(255, 255, 255, 0.22);
}

body .calendarbox .calendarnav-previous:hover,
body .calendarbox .calendarnav-next:hover {
    background-color: rgba(255, 255, 255, 0.42);
}

body .timelist li {
    margin: 0;
}

/* ---- Nav sidebar ---- */
#nav-sidebar {
    background: var(--surface);
    border-inline-end: 1px solid var(--hairline-color);
}

#nav-sidebar .module th {
    font-weight: 500;
}

/* nav_sidebar.css colors the active app's heading with --header-link-color,
   which was legible on Django's brand-colored bar but not on the light
   sidebar surface. */
#nav-sidebar caption a.section:link,
#nav-sidebar caption a.section:visited,
#nav-sidebar .current-app caption a.section:link,
#nav-sidebar .current-app caption a.section:visited {
    color: var(--body-quiet-color);
}

#nav-sidebar .current-model {
    background: var(--selected-row);
    border-radius: 8px;
}

#nav-filter {
    border-radius: var(--radius-control);
    margin: 10px;
    width: calc(100% - 20px);
}


/* ---------------------------------------------------------------------------
   The public demo's banner (templates/admin/base_site.html, rendered only
   when settings.DEMO_MODE is on).

   Reuses the amber --notice-* tokens rather than introducing a colour: this
   is the same kind of statement as every other notice in the system — read
   this before you trust what follows — and the tokens already carry their
   own dark-theme values. It sits above #header, so it is the first thing on
   the page in both themes and on the login screen.
   --------------------------------------------------------------------------- */
.demo-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 6px 20px;
    padding: 10px 32px;
    background: var(--notice-bg);
    border-bottom: 1px solid var(--notice-border);
    box-shadow: inset 0 -3px 0 -1px var(--notice-rail);
    color: var(--body-medium-color);
    font-size: 0.82rem;
    line-height: 1.5;
}

.demo-banner p {
    margin: 0;
}

.demo-banner-headline {
    /* Takes the whole row on its own as soon as the two halves would start
       crowding each other, rather than letting the sentence break into a
       ragged two-line column beside the login. */
    flex: 1 1 24rem;
}

.demo-banner-tag {
    display: inline-block;
    margin-inline-end: 8px;
    padding: 2px 9px;
    border-radius: 999px;
    background: var(--notice-rail);
    color: #2a2008;
    font-family: var(--font-display);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.2px;
    white-space: nowrap;
}

.demo-banner-login {
    flex: 0 0 auto;
    font-weight: 500;
}

.demo-banner-login code {
    padding: 2px 7px;
    border: 1px solid var(--notice-border);
    border-radius: 6px;
    background: var(--surface);
    color: var(--body-loud-color);
    /* The visitor copies these by hand off the screen, so the two strings
       have to be unmistakable character by character. */
    font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: 0.8rem;
    user-select: all;
}

.demo-banner-sep {
    margin: 0 2px;
    color: var(--body-quiet-color);
}

@media (max-width: 767px) {
    .demo-banner {
        padding: 10px 16px;
    }
}


/* ---------------------------------------------------------------------------
   The employee-app setup sheet (templates/admin/mobile_setup.html).

   Printed and handed out, so it is laid out for paper first: the code and the
   typed address side by side, both large enough to read across a desk, and
   the admin's own furniture dropped when it goes to the printer.
   --------------------------------------------------------------------------- */
.setup-sheet { padding: 26px 30px 32px; }

.setup-lede {
    max-width: 68ch;
    color: var(--body-medium-color);
    margin: 0 0 26px;
}

.setup-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 32px;
    align-items: flex-start;
}

.setup-code {
    flex: 0 0 auto;
    text-align: center;
    padding: 18px;
    border: 1px solid var(--hairline-color);
    border-radius: var(--radius-card);
    background: var(--surface);
}

.setup-code img { display: block; }

.setup-manual { flex: 1 1 22rem; min-width: 0; }

.setup-manual h3 {
    font-family: var(--font-display);
    font-size: 0.82rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--body-quiet-color);
    margin: 0 0 10px;
}

.setup-manual h3 + * { margin-top: 0; }
.setup-manual > h3:not(:first-child) { margin-top: 26px; }

.setup-url code {
    display: inline-block;
    padding: 10px 16px;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-control);
    background: var(--darkened-bg);
    /* Copied off the screen or off paper by hand, so l/1 and O/0 have to be
       distinguishable. */
    font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: 1.05rem;
    font-weight: 600;
    user-select: all;
}

/* The code and its copy button sit on one line and wrap together on a narrow
   window. The code can be long, so it is allowed to break rather than push the
   button off the card. */
.setup-code-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
}

.setup-code-row code {
    min-width: 0;
    overflow-wrap: anywhere;
    /* Grouped in fours, so a little extra tracking makes the groups read as
       groups when somebody is typing it off a printed sheet. */
    letter-spacing: 0.04em;
}

.setup-caption { color: var(--body-quiet-color); font-size: 0.86rem; margin: 10px 0 0; }
.setup-steps { margin: 0; padding-inline-start: 1.2rem; line-height: 2; color: var(--body-medium-color); }
.setup-warning { margin-top: 26px; }

@media print {
    /* The sheet, and nothing else that happens to be on the page. */
    #header, .demo-banner, .breadcrumbs, #footer, .object-tools { display: none !important; }
    body, .setup-sheet { background: #fff !important; color: #000 !important; }
    .setup-sheet { border: 0 !important; box-shadow: none !important; padding: 0 !important; }
}


/* A field the country picker just filled in. Brief, and only a background:
   the three currency fields can sit below the fold on a narrow window, and a
   change nobody sees reads as nothing having happened. */
.field-just-filled {
    background: var(--positive-bg) !important;
    transition: background-color .9s ease;
}


/* ---- Waiting-requests bell ----------------------------------------------
   HR works in this browser rather than in the phone app, so this is what
   actually tells them a request has arrived — see static/js/pending_bell.js.
   Styled to sit beside the theme and language toggles as a third quiet icon:
   the count is the only thing that raises its voice, and only when there is
   one. The header is petrol in both themes, so the icon is white in both. */
.pending-bell {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    text-decoration: none;
    /* Logical, not left/right: the whole interface mirrors in Arabic. */
    margin-inline-start: 5px;
    border-radius: var(--radius-control);
    transition: opacity 0.15s ease, transform 0.08s ease;
}

.pending-bell:hover { opacity: 0.82; }
.pending-bell:active { transform: translateY(1px); }

.pending-bell svg {
    height: 1.5rem;
    width: 1.5rem;
    vertical-align: middle;
}

/* The count rides the bell's upper outer corner, which is the right in English
   and the left in Arabic — hence inset-inline-end rather than right. */
.pending-bell-count {
    position: absolute;
    top: -5px;
    inset-inline-end: -7px;
    min-width: 17px;
    height: 17px;
    padding: 0 4px;
    box-sizing: border-box;
    border-radius: 9px;
    background: var(--notice-rail);
    color: #23180a;
    font-family: var(--font-display);
    font-size: 0.66rem;
    font-weight: 700;
    line-height: 17px;
    text-align: center;
    font-variant-numeric: tabular-nums;
    /* Against the petrol header the amber needs an edge or it reads as a
       smudge at this size. */
    box-shadow: 0 0 0 1.5px var(--header-grad-a);
}

/* The "one just arrived" toast. Bottom-start, so it never covers the save row
   at the bottom-end of a form somebody is in the middle of. */
.pending-toast {
    position: fixed;
    bottom: 24px;
    inset-inline-start: 24px;
    z-index: 2000;
    max-width: min(32ch, calc(100vw - 48px));
    padding: 12px 18px;
    border-radius: var(--radius-control);
    border: 1px solid var(--notice-border);
    border-inline-start: 4px solid var(--notice-rail);
    background: var(--notice-bg);
    color: var(--body-fg);
    font-family: var(--font-display);
    font-size: 0.88rem;
    box-shadow: var(--shadow-md);
    animation: pending-toast-in 0.25s ease-out both;
}

.pending-toast.is-leaving {
    opacity: 0;
    transition: opacity 0.4s ease;
}

@keyframes pending-toast-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .pending-toast { animation: none; }
    .pending-bell { transition: none; }
}


/* ---- Collapsible form sections ------------------------------------------
   The employee form carries eighty-nine fields. Split into sections it is a
   page somebody works down; the heading has to carry enough for that to be
   true without opening each one. See static/js/form_sections.js and
   EmployeeAdmin._as_sections. */
fieldset.form-section > details > summary {
    cursor: pointer;
    list-style: none;
    /* The marker is drawn below, so the browser's own triangle would be a
       second one pointing the other way. */
}

fieldset.form-section > details > summary::-webkit-details-marker { display: none; }

fieldset.form-section > details > summary .fieldset-heading {
    display: flex;
    align-items: center;
    gap: 12px;
}

/* A chevron that turns when the section opens. inset-inline-end, not right:
   the whole interface mirrors in Arabic. */
fieldset.form-section > details > summary .fieldset-heading::after {
    content: "";
    margin-inline-start: auto;
    width: 9px;
    height: 9px;
    border-inline-end: 2px solid currentColor;
    border-block-end: 2px solid currentColor;
    transform: rotate(45deg);
    transition: transform 0.15s ease;
    opacity: 0.55;
}

fieldset.form-section > details[open] > summary .fieldset-heading::after {
    transform: rotate(-135deg);
}

/* How much of this section has an answer. Tabular figures so the number does
   not shuffle sideways while somebody types. */
.section-count {
    font-family: var(--font-display);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    font-variant-numeric: tabular-nums;
    padding: 2px 9px;
    border-radius: 999px;
    background: var(--darkened-bg);
    color: var(--body-quiet-color);
    border: 1px solid var(--hairline-color);
}

/* A required field left blank. Amber is this theme's notice colour, and a
   gap somebody chose to leave is not marked at all. */
fieldset.section-incomplete .section-count {
    background: var(--notice-bg);
    border-color: var(--notice-border);
    color: var(--notice-rail);
}

/* The server came back with errors in here. Django renders such a section
   already open, so this names it rather than reveals it. */
fieldset.section-has-errors {
    border-inline-start: 3px solid var(--error-fg);
}

fieldset.section-has-errors .section-count {
    background: transparent;
    border-color: var(--error-fg);
    color: var(--error-fg);
}

@media (prefers-reduced-motion: reduce) {
    fieldset.form-section > details > summary .fieldset-heading::after { transition: none; }
}


/* ---- Section panels ----------------------------------------------------
   A section's fields spread across the page, however wide the page is.

   Deliberately outside the min-width:1100px block that holds the rest of the
   change form's grid. The columns were in there, and a browser zoomed far
   enough in reports a CSS width below that breakpoint — so the panel quietly
   lost its columns and listed every field down one side, on a screen that is
   physically wide. auto-fit asks the question the right way round: as many
   columns as 250px each will fit, which is four on a desktop, two on a
   tablet, one on a phone, and never none.

   The rows live inside <details>, which is why the grid is there rather than
   on the fieldset around it. */
body.change-form fieldset.module.aligned.form-section .section-body {
    padding-block-end: 4px;
}

/* Nothing may outgrow its column: a select sized by its longest option, or
   one of Django's fixed-width text inputs, otherwise pushes the track wider
   than its neighbours and the row stops lining up. */
body.change-form fieldset.module.aligned.form-section .section-body > .form-row {
    min-width: 0;
}

body.change-form fieldset.module.aligned.form-section .section-body > .form-row :where(input, select, textarea) {
    max-width: 100%;
    min-width: 0;
}

/* The things that are not one field: a table belonging to the section, a long
   note, and a two-pane multi-select all take the whole row. */
body.change-form fieldset.module.aligned.form-section .section-body > .section-table,
body.change-form fieldset.module.aligned.form-section .section-body > .form-row:has(textarea),
body.change-form fieldset.module.aligned.form-section .section-body > .form-row:has(.selector) {
    grid-column: 1 / -1;
}

/* ---- Section tabs -------------------------------------------------------
   The sections become a row of tabs once the script has run — see
   static/js/form_sections.js. Until then, and if it never does, they are the
   <details> accordion the markup actually is, which is why everything here is
   written against .is-tabbed rather than replacing the accordion styling.

   A segmented control: one quiet track with the chosen section lifted out of
   it as a pale chip. Chosen over the dark bar it replaces for one reason —
   nine tabs do not fit a row, and this one scrolls sideways rather than
   wrapping to a second line or squeezing every name until it has to be cut.
   The row stays a row at any width, which is what a row of tabs is for.

   The track takes --body-bg rather than --darkened-bg: those two are the same
   colour in the dark theme, and a chip the colour of the track it sits in is
   not a chip. --body-bg is darker than --surface under both. */
.section-tabs {
    display: flex;
    flex-wrap: nowrap;
    gap: 3px;
    margin: 0 0 20px;
    padding: 4px;
    border-radius: 999px;
    background: var(--body-bg);
    border: 1px solid var(--hairline-color);
    box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.06);
    overflow-x: auto;
    /* Vertical hidden as well, or a browser reserves room for a scrollbar it
       will never draw and the track grows a few pixels taller than its own
       contents. */
    overflow-y: hidden;
    scrollbar-width: thin;
    scrollbar-color: var(--hairline-color) transparent;
    scroll-padding-inline: 12px;
}

.section-tabs::-webkit-scrollbar { height: 6px; }
.section-tabs::-webkit-scrollbar-track { background: transparent; }
.section-tabs::-webkit-scrollbar-thumb {
    background: var(--hairline-color);
    border-radius: 999px;
}

.section-tab {
    /* Grows into spare room, never shrinks. A tab is as wide as its own name,
       so nothing is ever truncated; when the nine of them outgrow the page the
       track scrolls instead. */
    flex: 1 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 9px 16px;
    border: 1px solid transparent;
    border-radius: 999px;
    background: transparent;
    color: var(--body-quiet-color);
    font-family: var(--font-display);
    font-size: 0.82rem;
    font-weight: 500;
    letter-spacing: 0.1px;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.14s ease, color 0.14s ease;
}

.section-tab:hover { color: var(--body-loud-color); }

.section-tab:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

.section-tab.is-active {
    background: var(--surface);
    border-color: var(--hairline-color);
    color: var(--body-loud-color);
    font-weight: 600;
    box-shadow: var(--shadow-sm);
}

/* The count: an outline at rest, so it reads as a figure rather than a badge,
   and filled on the chosen one. */
.section-tab .section-count {
    flex: 0 0 auto;
    background: transparent;
    border-color: var(--hairline-color);
    color: var(--body-quiet-color);
}

.section-tab.is-active .section-count {
    background: var(--primary);
    border-color: var(--primary);
    color: #fff;
}

/* A required field left blank somewhere in there; and a louder mark for a
   section the server came back with errors in, which is also the one the
   script opens on arrival. */
.section-tab.is-incomplete .section-count,
.section-tab.is-incomplete.is-active .section-count {
    background: var(--notice-rail);
    border-color: var(--notice-rail);
    color: #23180a;
}

.section-tab.has-errors { color: var(--error-fg); }

.section-tab.has-errors .section-count,
.section-tab.has-errors.is-active .section-count {
    background: var(--error-fg);
    border-color: var(--error-fg);
    color: #fff;
}

@media (prefers-reduced-motion: reduce) {
    .section-tab { transition: none; }
}

/* The panel the chosen tab opens. */
body.change-form fieldset.module.aligned.form-section.is-tabbed {
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-md);
}

/* The tab is the heading now, so the section's own one goes — and with it the
   chevron and the click target, which would be a second control for the same
   thing. */
fieldset.form-section.is-tabbed > details > summary {
    display: none;
}

/* The browser's own "[hidden] { display: none }" comes from its stylesheet,
   and the rule that makes a section a block comes from this one — so an author
   display wins and `hidden` did nothing at all. Every section stayed on the
   page as an empty strip and pushed the open one to the bottom, which is what
   "no tab opens" looked like. Spelled out, and !important because the display
   it has to beat is set with more specificity than this selector can
   otherwise reach. */
fieldset.form-section[hidden],
body.change-form fieldset.module.aligned.form-section[hidden] {
    display: none !important;
}

/* Nothing between the tabs and the panel they open — the panel is the tab's
   content, not the next thing down the page. */
fieldset.form-section.is-tabbed {
    margin-block-start: 0;
}

/* ---- Section headings ---------------------------------------------------
   The heading has to read as the same object as an inline table's heading
   beside it, so it takes its look from .module h2 like every other one and
   adds only what a summary needs. Spelled out because a <summary> is a
   list-item by default and a shrink-to-fit box in several layouts, and this
   one spans a full-width bar. */
fieldset.form-section > details > summary {
    display: block;
    width: 100%;
}

fieldset.form-section > details > summary .fieldset-heading {
    width: 100%;
    box-sizing: border-box;
    margin: 0;
}

fieldset.form-section > details > summary:hover .fieldset-heading {
    background: var(--darkened-bg);
}

/* A table that belongs to a section is inside it now, so it has to span the
   section's columns rather than sit in the first of them — and it carries no
   heading of its own, because the section it is in is its heading. */
fieldset.form-section .section-table {
    grid-column: 1 / -1;
}

fieldset.form-section .section-table .inline-group h2 {
    display: none;
}

fieldset.form-section .section-table .inline-group,
fieldset.form-section .section-table fieldset.module {
    margin: 0;
    box-shadow: none;
}


/* ---- Field help text ----------------------------------------------------
   Hidden until the field is in use, not deleted.

   A line of explanation under every input is what turns a tidy grid of cells
   into cells of six different heights, and most of it explains something
   somebody only needs the first time they fill that one field. So it waits
   there: the row it belongs to grows to show it while that field has focus,
   and the cells either side do not move, because each one is its own grid
   item sized from the top.

   What is NOT done here is stripping help_text off the models. Several of
   these sentences are the only place something is said at all — how many
   decimals a dinar has, that the sync token must not leave the company, what
   a shift crossing a day means — and a form that never shows them is a form
   that has to be explained over the phone instead. */
body.change-form .form-row div.help {
    display: none;
    /* Below the control rather than above it, so appearing pushes nothing but
       the bottom of its own cell down. */
    order: 9;
    flex-basis: 100%;
    margin-block-start: 6px;
}

body.change-form .form-row:focus-within div.help {
    display: block;
}

@media (prefers-reduced-motion: no-preference) {
    body.change-form .form-row:focus-within div.help {
        animation: field-help-in 0.15s ease-out both;
    }
}

@keyframes field-help-in {
    from { opacity: 0; }
    to { opacity: 1; }
}


/* ---- Cells that need more than one column -------------------------------
   The grid gives every field an equal share, which is right for a field and
   wrong for the handful of rows that are not one: a date beside a time, two
   fields written on one line, a long opaque value, a checkbox with its label
   beside it. Each of those was being squeezed into a share meant for a single
   input — the date box came out showing one digit — so each asks for the room
   it needs instead. */
body.change-form .form-row.form-multiline,
body.change-form .form-row:has(p.datetime),
body.change-form .form-row:has(.selector),
body.change-form .form-row:has(textarea) {
    grid-column: span 2;
}

/* A readonly value with nothing to break on — a sync token, an account number
   — cannot be wrapped into a quarter of the page, so it takes the row. */
body.change-form .form-row:has(> .flex-container > .readonly) {
    grid-column: 1 / -1;
}

body.change-form .form-row .readonly {
    overflow-wrap: anywhere;
}




/* ---- Lining a row up ----------------------------------------------------
   Three things were out of step once the cells had columns to share. */

/* A row holding two fields splits them evenly and keeps the grid's own gap
   between them. Without it the second field started wherever the first one
   happened to end, and the picker button of the first sat against the second
   as though it belonged to it. */
body.change-form .form-row.form-multiline {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
    gap: 0 28px;
    align-items: start;
}

body.change-form .form-row.form-multiline > .fieldBox {
    min-width: 0;
}

/* And room after the picker button itself, so it reads as the end of its own
   field rather than the start of the next. inline-end, so it falls on the
   left in Arabic and the right in English — the side the button is on in
   each. */
body.change-form .form-row p.date,
body.change-form .form-row p.time,
body.change-form .form-row p.datetime {
    padding-inline-end: 14px;
}

/* ---- One height, one baseline --------------------------------------------
   A row of five cells holding a text box, a number, a time with a button
   beside it and a dropdown was a row of controls at four different heights,
   each sitting a few pixels above or below its neighbour.

   A minimum was not enough. min-height lets a control that wants to be taller
   be taller, and several of them do — which is the stagger itself. This is an
   exact height, and the line-height with it, so the text inside sits on the
   same line across the row too. */
body.change-form fieldset.module.aligned .section-body .form-row
  :where(input[type="text"], input[type="number"], input[type="email"],
         input[type="url"], input[type="tel"], input[type="password"],
         input[type="date"], input[type="time"], input[type="datetime-local"],
         select:not([multiple]), .datetimeshortcuts button) {
    height: 2.375rem;
    line-height: 1.4;
    box-sizing: border-box;
    vertical-align: middle;
    margin: 0;
}

/* The paragraphs Django wraps a date or a time in contribute no vertical
   space of their own — left alone each one adds its own line box and the
   field beside it starts a few pixels higher. */
body.change-form fieldset.module.aligned .section-body .form-row :where(p.date, p.time, p.datetime) {
    margin: 0;
    /* Top, not centre. The picker button beside the box is a shade taller than
       it, and centring split the difference by pushing the box down a few
       pixels — which is why every field with a clock next to it sat lower than
       its neighbours. Aligned to the top, the box starts exactly where a plain
       input in the cell beside it starts, whichever of the two is taller. */
    align-items: flex-start;
}

/* ---- One line for the labels, one for the controls ----------------------
   Every cell is two rows: a label row of a fixed height, and the control
   under it. Fixed, and that is the whole point — a minimum lets a cell whose
   label wraps push its own control down while the cell beside it stays put,
   which is the staircase this kept coming back as. Two lines of room, so a
   long label has somewhere to go, and the control below starts at the same
   place in every cell whether the label used both lines or one.

   Subgrid would say this more directly and was tried: with display:contents
   on these wrappers the two halves of a paired row landed in the same cells
   and drew on top of each other. Two fixed rows need nothing taken out of the
   layout to work. */
body.change-form fieldset.module.aligned .section-body .form-row > .flex-container,
body.change-form fieldset.module.aligned .section-body .form-row > .fieldBox {
    display: grid;
    /* One line of label and no more. It was two, to give a long one somewhere
       to go, and the second line was empty in every cell on every form —
       which is where the band of white above each row came from. */
    grid-template-rows: 1.6em auto;
    align-content: start;
}

/* A checkbox carries its label beside it, not above, so it has nothing to put
   in the label row — and was being pushed down past an empty one. */
body.change-form fieldset.module.aligned .section-body .form-row:has(.checkbox-row) > .flex-container {
    grid-template-rows: auto;
}

body.change-form fieldset.module.aligned .section-body .form-row > .flex-container > label,
body.change-form fieldset.module.aligned .section-body .form-row > .fieldBox > label {
    grid-row: 1;
    display: flex;
    align-items: flex-end;
    margin: 0 0 4px;
    line-height: 1.25;
    /* Clipped rather than allowed to push its own control down: the label row
       is one line for every cell, and a label long enough to need two is a
       label to shorten. */
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* Everything that is not the label shares the second row, which is what lets
   a help line appear under a control without moving the cells beside it. */
body.change-form fieldset.module.aligned .section-body .form-row > .flex-container > :not(label),
body.change-form fieldset.module.aligned .section-body .form-row > .fieldBox > :not(label) {
    grid-row: 2;
    min-width: 0;
}

body.change-form fieldset.module.aligned .section-body .form-row div.help {
    grid-row: 3;
}


/* ===========================================================================
   Importing employees from a spreadsheet
   (employees/import_view.py, templates/admin/employee_import.html)

   Three numbered steps down the page, and the third only appears once a file
   has been read. The tally is the thing somebody actually looks at before
   pressing the button, so it is the one element here given any weight.
   =========================================================================== */

.import-step > h2 {
    display: flex;
    align-items: center;
    gap: 10px;
}

.import-body {
    padding: 20px 22px;
}

.import-body > p { max-width: 68ch; }

.import-required b,
.import-note b {
    font-weight: 600;
    color: var(--body-fg);
}

.import-note {
    background: var(--body-bg);
    border: 1px solid var(--hairline-color);
    border-radius: var(--radius-control);
    padding: 12px 14px;
    margin: 0 0 16px;
}

/* Amber is the notice colour sitewide and is not spent on anything else. */
.import-note.import-warn {
    border-inline-start: 3px solid var(--notice-rail);
    background: var(--notice-bg);
}

.import-dim, .import-note.import-dim { color: var(--body-quiet-color); }

.import-upload {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    margin-bottom: 6px;
}

/* The tally: three figures, read before the button is pressed. Sized so the
   number carries and the label explains it, rather than two equal lines. */
.import-tally {
    list-style: none;
    margin: 0 0 20px;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
    gap: 12px;
}

.import-tally li {
    background: var(--body-bg);
    border: 1px solid var(--hairline-color);
    border-radius: var(--radius-card);
    padding: 14px 16px;
}

.import-tally li b {
    display: block;
    font-family: var(--font-display);
    font-size: 1.7rem;
    line-height: 1.2;
    font-variant-numeric: tabular-nums;
}

.import-tally li span { color: var(--body-quiet-color); font-size: .9rem; }
.import-tally li.good b { color: var(--primary); }
.import-tally li.bad b { color: var(--error-fg); }

.import-subhead {
    margin: 26px 0 10px;
    font-size: 1rem;
}

/* A preview of three hundred rows is wide and long by nature: it scrolls in
   its own box so the page itself never does. */
.import-table-wrap {
    max-height: 26rem;
    overflow: auto;
    border: 1px solid var(--hairline-color);
    border-radius: var(--radius-control);
}

.import-table {
    width: 100%;
    border-collapse: collapse;
    font-size: .92rem;
}

.import-table th,
.import-table td {
    padding: 8px 12px;
    border-bottom: 1px solid var(--hairline-color);
    text-align: start;
    vertical-align: top;
}

.import-table thead th {
    position: sticky;
    top: 0;
    background: var(--surface);
    box-shadow: inset 0 -1px 0 var(--hairline-color);
    white-space: nowrap;
}

.import-table tbody tr:last-child td { border-bottom: none; }
.import-rownum { font-variant-numeric: tabular-nums; white-space: nowrap; }

.import-errors {
    margin: 0;
    padding-inline-start: 1.1em;
}

.import-confirm {
    margin-top: 22px;
    padding-top: 18px;
    border-top: 1px solid var(--hairline-color);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 16px;
}

.import-accept {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--body-fg);
}


/* ---------------------------------------------------------------------------
   The seat notice (templates/admin/base_site.html, config/licence.py)

   Same shape as the demo banner above it, because it is the same kind of
   thing: a fact about this installation rather than about the page. Amber
   while there are seats left, red once there are none — at that point it is
   not a notice, it is the reason the next save will be refused.
   --------------------------------------------------------------------------- */

.seat-banner {
    padding: 10px 32px;
    background: var(--notice-bg);
    border-bottom: 1px solid var(--notice-border);
    box-shadow: inset 0 -3px 0 -1px var(--notice-rail);
    color: var(--body-medium-color);
    font-size: 0.82rem;
    line-height: 1.5;
}

.seat-banner p { margin: 0; }

.seat-banner-full {
    background: var(--danger-bg);
    border-bottom-color: var(--danger-border);
    box-shadow: inset 0 -3px 0 -1px var(--danger-rail);
    color: var(--danger-fg);
    font-weight: 500;
}

@media (max-width: 767px) {
    .seat-banner { padding: 10px 16px; }
}


/* ---------------------------------------------------------------------------
   The free-trial countdown (templates/admin/base_site.html, config/licence.py)

   Same shape as the demo and seat banners above it — a fact about this
   installation rather than about the page. Teal while there is time, amber in
   the last two days, red once it has run out; and it keeps saying so after
   that, because nothing stops working and somebody has to know why.
   --------------------------------------------------------------------------- */

.trial-banner {
    padding: 10px 32px;
    background: color-mix(in srgb, var(--primary) 10%, var(--surface));
    border-bottom: 1px solid color-mix(in srgb, var(--primary) 30%, transparent);
    box-shadow: inset 0 -3px 0 -1px var(--primary);
    color: var(--body-medium-color);
    font-size: 0.82rem;
    line-height: 1.5;
}

.trial-banner p { margin: 0; }

.trial-banner-urgent {
    background: var(--notice-bg);
    border-bottom-color: var(--notice-border);
    box-shadow: inset 0 -3px 0 -1px var(--notice-rail);
    font-weight: 500;
}

.trial-banner-over {
    background: var(--danger-bg);
    border-bottom-color: var(--danger-border);
    box-shadow: inset 0 -3px 0 -1px var(--danger-rail);
    color: var(--danger-fg);
    font-weight: 500;
}

@media (max-width: 767px) {
    .trial-banner { padding: 10px 16px; }
}


/* ---------------------------------------------------------------------------
   Quick actions (templates/admin/index.html, HRAdminSite._quick_actions)

   Tasks to start, where the counter cards above are lists to review — so
   they read differently on purpose: no figure, a filled icon tile, and a
   row that wraps rather than a grid of equal boxes. Sized so five fit on a
   wide screen and two on a phone.
   --------------------------------------------------------------------------- */

.quick-actions {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 12px;
}

.quick-action {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 13px 15px;
    border-radius: var(--radius-card);
    background: var(--surface);
    border: 1px solid var(--hairline-color);
    box-shadow: var(--shadow-md);
    transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}

a.quick-action:link,
a.quick-action:visited,
a.quick-action:hover,
a.quick-action:focus-visible,
a.quick-action:active {
    color: var(--body-fg) !important;
    text-decoration: none !important;
}

a.quick-action:hover,
a.quick-action:focus-visible {
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
    border-color: color-mix(in srgb, var(--primary) 45%, var(--hairline-color));
}

.quick-action-icon {
    flex: none;
    width: 38px;
    height: 38px;
    display: grid;
    place-items: center;
    border-radius: 11px;
    background: color-mix(in srgb, var(--primary) 12%, var(--surface));
    color: var(--primary);
}

.quick-action-icon svg { width: 19px; height: 19px; }

.quick-action-label {
    font-size: 0.9rem;
    font-weight: 600;
    line-height: 1.35;
}
