@charset "UTF-8";

/* ==========================================================================
   HyperBuild design system
   RTL-first, token driven, light + dark. No external CSS dependencies so the
   PWA works fully offline.
   ========================================================================== */

/* --------------------------------------------------------------- 1. fonts */

@font-face {
    font-family: "Vazirmatn";
    src: url("../fonts/Vazirmatn-Regular.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Vazirmatn";
    src: url("../fonts/Vazirmatn-Medium.woff2") format("woff2");
    font-weight: 500;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Vazirmatn";
    src: url("../fonts/Vazirmatn-SemiBold.woff2") format("woff2");
    font-weight: 600;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Vazirmatn";
    src: url("../fonts/Vazirmatn-Bold.woff2") format("woff2");
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

/* --------------------------------------------------------------- 2. tokens */

:root {
    --brand: #3b6ff6;
    --brand-strong: #2b56d4;
    --brand-soft: rgba(59, 111, 246, 0.12);
    --brand-glow: rgba(59, 111, 246, 0.32);

    --success: #12b886;
    --success-soft: rgba(18, 184, 134, 0.14);
    --danger: #fa5252;
    --danger-soft: rgba(250, 82, 82, 0.14);
    --warning: #f59f00;
    --warning-soft: rgba(245, 159, 0, 0.16);
    --info: #22b8cf;
    --info-soft: rgba(34, 184, 207, 0.14);
    --violet: #7c5cff;

    --bg: #f4f6fb;
    --bg-elevated: #ffffff;
    --bg-sunken: #eef1f8;
    --surface: #ffffff;
    --surface-hover: #f7f9fd;
    --surface-alt: #fafbfe;

    --text: #16213e;
    --text-soft: #56628a;
    --text-muted: #8892b0;
    --text-inverse: #ffffff;

    --border: #e4e9f4;
    --border-strong: #ced6e8;

    --shadow-xs: 0 1px 2px rgba(22, 33, 62, 0.05);
    --shadow-sm: 0 2px 8px rgba(22, 33, 62, 0.06);
    --shadow-md: 0 8px 24px rgba(22, 33, 62, 0.08);
    --shadow-lg: 0 18px 48px rgba(22, 33, 62, 0.12);
    --shadow-brand: 0 10px 30px rgba(59, 111, 246, 0.28);

    --radius-xs: 8px;
    --radius-sm: 12px;
    --radius: 16px;
    --radius-lg: 22px;
    --radius-full: 999px;

    --sidebar-w: 264px;
    --sidebar-w-collapsed: 78px;
    --topbar-h: 68px;

    --ease: cubic-bezier(0.22, 1, 0.36, 1);
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
    --dur-fast: 0.16s;
    --dur: 0.28s;
    --dur-slow: 0.5s;

    --font: "Vazirmatn", "Segoe UI", Tahoma, "Noto Sans Arabic", sans-serif;

    color-scheme: light;
}

[data-theme="dark"] {
    --brand: #6d97ff;
    --brand-strong: #8fb0ff;
    --brand-soft: rgba(109, 151, 255, 0.16);
    --brand-glow: rgba(109, 151, 255, 0.32);

    --success: #2fd6a4;
    --success-soft: rgba(47, 214, 164, 0.16);
    --danger: #ff7a7a;
    --danger-soft: rgba(255, 122, 122, 0.16);
    --warning: #ffc861;
    --warning-soft: rgba(255, 200, 97, 0.16);
    --info: #4fd3e6;
    --info-soft: rgba(79, 211, 230, 0.16);
    --violet: #a48cff;

    --bg: #0c1020;
    --bg-elevated: #141a30;
    --bg-sunken: #090d1a;
    --surface: #141a30;
    --surface-hover: #1b2340;
    --surface-alt: #171e37;

    --text: #eef2ff;
    --text-soft: #a9b4d8;
    --text-muted: #7a86ab;
    --text-inverse: #0c1020;

    --border: #232c4c;
    --border-strong: #33406b;

    --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-sm: 0 2px 10px rgba(0, 0, 0, 0.35);
    --shadow-md: 0 10px 28px rgba(0, 0, 0, 0.45);
    --shadow-lg: 0 22px 56px rgba(0, 0, 0, 0.55);
    --shadow-brand: 0 10px 30px rgba(109, 151, 255, 0.24);

    color-scheme: dark;
}

/* --------------------------------------------------------------- 3. base */

*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
}

/* Every rem-based size in this file (headings, badges, table text, buttons, ...) is relative to
   this root value, so dropping it one notch on a phone is a single global change instead of
   hunting down every component's own font-size rule. */
@media (max-width: 720px) {
    html { font-size: 15px; }
}

body {
    margin: 0;
    font-family: var(--font);
    font-size: 14.5px;
    line-height: 1.85;
    color: var(--text);
    background: var(--bg);
    direction: rtl;
    text-align: right;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
    overflow-wrap: break-word;
}

h1, h2, h3, h4, h5, h6 {
    margin: 0 0 0.6em;
    font-weight: 700;
    line-height: 1.5;
    letter-spacing: -0.01em;
}

h1 { font-size: 1.6rem; }
h2 { font-size: 1.3rem; }
h3 { font-size: 1.1rem; }
h4 { font-size: 1rem; }

p { margin: 0 0 1em; }

a {
    color: var(--brand);
    text-decoration: none;
    transition: color var(--dur-fast) var(--ease);
}

a:hover { color: var(--brand-strong); }

hr {
    border: none;
    border-top: 1px solid var(--border);
    margin: 1.5rem 0;
}

::selection {
    background: var(--brand-soft);
    color: var(--text);
}

:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: 2px;
    border-radius: 6px;
}

/* Numbers stay readable inside RTL text.
   The paragraph direction stays RTL on purpose: these elements hold "<number> <unit>"
   ("۸ میلیارد تومان"), and forcing LTR here would flip the run so the digits landed to the
   *left* of the unit - i.e. read last instead of first. The digits themselves already come
   wrapped in an LRI/PDI isolate from MoneyFormatter, so signs and grouping stay left-to-right
   inside an otherwise right-to-left line. */
.num,
.amount,
td.num,
.table td.num {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum";
    direction: rtl;
    unicode-bidi: isolate;
}

.icon {
    width: 1.15em;
    height: 1.15em;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.7;
    stroke-linecap: round;
    stroke-linejoin: round;
    flex: none;
    vertical-align: -0.18em;
}

.icon--lg { width: 1.5em; height: 1.5em; }
.icon--sm { width: 0.95em; height: 0.95em; }

/* Scrollbars */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--border-strong) transparent;
}

*::-webkit-scrollbar { width: 9px; height: 9px; }
*::-webkit-scrollbar-track { background: transparent; }

*::-webkit-scrollbar-thumb {
    background: var(--border-strong);
    border-radius: var(--radius-full);
    border: 2px solid transparent;
    background-clip: content-box;
}

*::-webkit-scrollbar-thumb:hover { background: var(--text-muted); background-clip: content-box; }

/* --------------------------------------------------------------- 4. layout shell */

.shell {
    display: flex;
    min-height: 100vh;
    min-height: 100dvh;
}

.sidebar {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    width: var(--sidebar-w);
    background: var(--bg-elevated);
    border-inline-start: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    z-index: 60;
    transition: transform var(--dur) var(--ease), width var(--dur) var(--ease);
}

.sidebar__brand {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 1.15rem 1.25rem;
    min-height: var(--topbar-h);
    border-bottom: 1px solid var(--border);
}

.sidebar__logo {
    width: 38px;
    height: 38px;
    border-radius: 12px;
    background: linear-gradient(135deg, var(--brand), var(--violet));
    display: grid;
    place-items: center;
    color: #fff;
    box-shadow: var(--shadow-brand);
    flex: none;
}

.sidebar__logo svg { width: 21px; height: 21px; stroke-width: 2; }

.sidebar__title {
    display: flex;
    flex-direction: column;
    line-height: 1.35;
    overflow: hidden;
}

.sidebar__title strong { font-size: 0.98rem; font-weight: 700; }
.sidebar__title span {
    font-size: 0.72rem;
    color: var(--text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.sidebar__nav {
    flex: 1;
    overflow-y: auto;
    padding: 0.9rem 0.75rem 1.5rem;
}

.nav-group + .nav-group { margin-top: 1.15rem; }

.nav-group__label {
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    padding: 0 0.75rem 0.45rem;
    text-transform: uppercase;
}

.nav-link {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.62rem 0.8rem;
    border-radius: var(--radius-sm);
    color: var(--text-soft);
    font-size: 0.9rem;
    font-weight: 500;
    transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}

.nav-link + .nav-link { margin-top: 2px; }

.nav-link:hover {
    background: var(--surface-hover);
    color: var(--text);
    transform: translateX(-3px);
}

.nav-link.is-active {
    background: var(--brand-soft);
    color: var(--brand);
    font-weight: 600;
}

.nav-link.is-active::before {
    content: "";
    position: absolute;
    inset-inline-end: -0.75rem;
    inset-block: 22%;
    width: 3px;
    border-radius: var(--radius-full);
    background: var(--brand);
}

.nav-link .icon { opacity: 0.85; }

.nav-link__badge {
    margin-inline-start: auto;
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border-radius: var(--radius-full);
    background: var(--danger);
    color: #fff;
    font-size: 0.68rem;
    font-weight: 700;
    display: grid;
    place-items: center;
}

.sidebar__footer {
    padding: 0.85rem;
    border-top: 1px solid var(--border);
}

.main {
    flex: 1;
    min-width: 0;
    margin-inline-start: var(--sidebar-w);
    display: flex;
    flex-direction: column;
    transition: margin var(--dur) var(--ease);
}

.topbar {
    position: sticky;
    top: 0;
    z-index: 40;
    min-height: var(--topbar-h);
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.75rem 1.35rem;
    background: color-mix(in srgb, var(--bg) 82%, transparent);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border-bottom: 1px solid var(--border);
}

.topbar__title {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.topbar__title h1 {
    margin: 0;
    font-size: 1.12rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.topbar__title .breadcrumb { font-size: 0.76rem; color: var(--text-muted); }

.topbar__spacer { flex: 1; }

/* Shell-wide project filter. Given a hard cap so a long project name cannot squeeze the toolbar. */
.topbar__project { margin: 0; min-width: 0; }

.topbar__project .select {
    min-height: 36px;
    padding-block: 0.25rem;
    font-size: 0.82rem;
    max-width: 200px;
}

@media (max-width: 720px) {
    .topbar__project .select { max-width: 130px; font-size: 0.76rem; }
}

/* --------------------------------------------------------------- project cards (home) */

.project-card { position: relative; }

.project-card.is-selected {
    border-color: var(--brand);
    box-shadow: 0 0 0 2px var(--brand-soft), var(--shadow-sm);
}

/* Stretched link: the whole card opens the project's dashboard. It sits under the content so the
   buttons in the footer keep their own click targets. */
.project-card__hit {
    position: absolute;
    inset: 0;
    z-index: 1;
    border-radius: inherit;
}

.project-card__hit:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: -2px;
}

.project-card .card__head,
.project-card .card__body { position: relative; z-index: 0; pointer-events: none; }

.project-card__actions { position: relative; z-index: 2; }

.project-card__figures {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
    gap: 0.75rem;
}

.home-tabs { flex-wrap: wrap; }

/* Available to screen readers, invisible on screen - used for labels the layout cannot show. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* .topbar-more wraps the toggle button and .topbar__actions in one .dropdown, reusing the same
   is-open mechanics as every other dropdown in the app (see initDropdowns in app.js) - nothing
   page-specific needed to wire it up. .topbar__actions carries .dropdown__menu itself, so below
   the breakpoint it becomes a real dropdown panel for free; above it, the rules here cancel that
   panel styling back out to a plain inline row so desktop is unchanged.

   Both selectors below are qualified with an element type (button./div.) rather than left as a
   bare class. .topbar-more__toggle also carries .btn, and .topbar__actions also carries
   .dropdown__menu - a bare class here would tie in specificity with .btn's and .dropdown__menu's
   own base rules and then lose on source order, since both are declared further down the file.
   The element qualifier breaks that tie in our favour without outranking the three-class
   .dropdown.is-open .dropdown__menu rule that has to keep winning once the panel opens. */
button.topbar-more__toggle { display: none; }

div.topbar__actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    /* Cancel the .dropdown__menu defaults (absolute, hidden, boxed) - on a normal-width screen
       this is just the topbar's action row, not a collapsed panel. */
    position: static;
    opacity: 1;
    transform: none;
    pointer-events: auto;
    background: none;
    border: none;
    box-shadow: none;
    padding: 0;
    min-width: 0;
    max-height: none;
    overflow: visible;
}

@media (max-width: 860px) {
    /* The bell, theme switcher and each page's own topbar actions used to sit in one row with no
       room to wrap, so on a phone they either overlapped or forced the topbar to scroll
       sideways. Below this width the row becomes a single "more" button; the same content just
       moves into its dropdown panel instead of being laid out inline. */
    button.topbar-more__toggle { display: inline-flex; }

    div.topbar__actions {
        /* Restore real dropdown-menu behaviour - .dropdown.is-open .dropdown__menu (app.css,
           shared with every other dropdown) handles showing it again once opened. */
        position: absolute;
        opacity: 0;
        transform: translateY(-8px) scale(0.98);
        pointer-events: none;
        background: var(--surface);
        border: 1px solid var(--border);
        box-shadow: var(--shadow-lg);
        border-radius: var(--radius-sm);
        padding: 0.4rem;
        /* Wide enough for a notification to read as a sentence rather than one word per line -
           the panel now hosts the notification list inline instead of floating it alongside. */
        min-width: 240px;
        width: min(88vw, 340px);
        max-height: 70vh;
        overflow-y: auto;
        flex-direction: column;
        align-items: stretch;
        gap: 0.3rem;
    }

    div.topbar__actions > .btn,
    div.topbar__actions > a.btn,
    div.topbar__actions > .btn-group {
        width: 100%;
        justify-content: flex-start;
    }

    /* There is room to stack vertically here, so the labels this class hides in the cramped
       inline row can come back. */
    div.topbar__actions .hide-xs { display: inline-flex !important; }

    /* The project picker is the panel's first row and gets the full width, instead of the 200px
       cap (130px below 720px) that the inline topbar has to impose on it. */
    div.topbar__actions > .topbar__project { width: 100%; }

    div.topbar__actions > .topbar__project .select {
        max-width: none;
        width: 100%;
        min-height: 40px;
        font-size: 0.85rem;
    }

    div.topbar__actions > .topbar__project + * { margin-block-start: 0.15rem; }

    /* --- nested dropdowns (the bell, and any a page puts in its TopbarActions) ---

       The panel is itself a .dropdown__menu, so it is `overflow-y: auto` - which computes
       overflow-x to `auto` as well - and only 240px wide. A child menu positioned `absolute`
       inside it was therefore clipped on two axes at once: the notification panel asks for
       330px (.dropdown__menu--wide) and the reader got a sliver of it.

       Below the breakpoint the child stops being a floating panel and expands in place instead,
       as an accordion row of the parent panel. Nothing overflows, so nothing is cut off, and the
       parent's own scrolling handles a long list. `display` does the showing and hiding here
       because a `position: static` panel still takes up its space when merely transparent. */
    div.topbar__actions .dropdown { position: static; width: 100%; }

    div.topbar__actions .dropdown > .dropdown__menu {
        display: none;
        position: static;
        width: 100%;
        min-width: 0;
        max-height: none;
        overflow: visible;
        padding: 0.2rem 0;
        border: none;
        border-radius: 0;
        box-shadow: none;
        background: none;
        transform: none;
    }

    div.topbar__actions .dropdown.is-open > .dropdown__menu {
        display: block;
        opacity: 1;
        pointer-events: auto;
    }

    /* Set off from the rows around it, so an expanded list still reads as one group. */
    div.topbar__actions .dropdown.is-open {
        border-radius: var(--radius-xs);
        background: var(--surface-alt);
        padding: 0.15rem 0.3rem 0.3rem;
    }
}

.content {
    flex: 1;
    padding: 1.4rem 1.35rem 3rem;
    max-width: 1560px;
    width: 100%;
    margin-inline: auto;
}

.page-foot {
    padding: 1rem 1.35rem 1.5rem;
    color: var(--text-muted);
    font-size: 0.76rem;
    text-align: center;
}

/* Sidebar collapsed (desktop) */
body.sidebar-collapsed .sidebar { width: var(--sidebar-w-collapsed); }
body.sidebar-collapsed .main { margin-inline-start: var(--sidebar-w-collapsed); }
body.sidebar-collapsed .sidebar__title,
body.sidebar-collapsed .nav-group__label,
body.sidebar-collapsed .nav-link span:not(.nav-link__badge),
body.sidebar-collapsed .userbox__meta { display: none; }
body.sidebar-collapsed .nav-link { justify-content: center; padding-inline: 0.5rem; }
body.sidebar-collapsed .sidebar__brand { justify-content: center; padding-inline: 0.5rem; }

.sidebar-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(8, 12, 26, 0.55);
    backdrop-filter: blur(2px);
    z-index: 55;
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--dur) var(--ease);
}

body.sidebar-open .sidebar-backdrop { opacity: 1; pointer-events: auto; }

/* --------------------------------------------------------------- 5. buttons */

.btn {
    --btn-bg: var(--surface);
    --btn-fg: var(--text);
    --btn-border: var(--border-strong);
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.45rem;
    padding: 0.55rem 1.05rem;
    min-height: 40px;
    border: 1px solid var(--btn-border);
    border-radius: var(--radius-sm);
    background: var(--btn-bg);
    color: var(--btn-fg);
    font-family: inherit;
    font-size: 0.875rem;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    transition: transform var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease),
                background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease),
                opacity var(--dur-fast) var(--ease);
}

.btn:hover { transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.btn:active { transform: translateY(0) scale(0.985); }
.btn[disabled], .btn.is-disabled { opacity: 0.55; pointer-events: none; }

.btn--primary {
    --btn-bg: linear-gradient(135deg, var(--brand), var(--brand-strong));
    --btn-fg: #fff;
    --btn-border: transparent;
    box-shadow: var(--shadow-brand);
}

.btn--primary:hover { box-shadow: 0 14px 34px var(--brand-glow); }

.btn--success { --btn-bg: var(--success); --btn-fg: #fff; --btn-border: transparent; }
.btn--danger { --btn-bg: var(--danger); --btn-fg: #fff; --btn-border: transparent; }
.btn--warning { --btn-bg: var(--warning); --btn-fg: #241a00; --btn-border: transparent; }

.btn--soft { --btn-bg: var(--brand-soft); --btn-fg: var(--brand); --btn-border: transparent; }
.btn--soft-danger { --btn-bg: var(--danger-soft); --btn-fg: var(--danger); --btn-border: transparent; }
.btn--soft-success { --btn-bg: var(--success-soft); --btn-fg: var(--success); --btn-border: transparent; }

.btn--ghost { --btn-bg: transparent; --btn-border: transparent; --btn-fg: var(--text-soft); }
.btn--ghost:hover { --btn-bg: var(--surface-hover); --btn-fg: var(--text); }

.btn--sm { min-height: 32px; padding: 0.35rem 0.7rem; font-size: 0.79rem; border-radius: var(--radius-xs); }
.btn--lg { min-height: 48px; padding: 0.7rem 1.5rem; font-size: 0.95rem; }
.btn--block { width: 100%; }

.btn--icon {
    padding: 0;
    width: 40px;
    min-height: 40px;
    border-radius: var(--radius-sm);
}

.btn--icon.btn--sm { width: 32px; min-height: 32px; }

/* Ripple, injected by app.js */
.btn .ripple {
    position: absolute;
    border-radius: 50%;
    transform: scale(0);
    background: currentColor;
    opacity: 0.25;
    pointer-events: none;
    animation: ripple 0.55s var(--ease-out) forwards;
}

@keyframes ripple {
    to { transform: scale(2.6); opacity: 0; }
}

.btn-group {
    display: inline-flex;
    background: var(--bg-sunken);
    border-radius: var(--radius-sm);
    padding: 3px;
    gap: 2px;
}

.btn-group .btn {
    border: none;
    background: transparent;
    box-shadow: none;
    color: var(--text-soft);
    min-height: 34px;
}

.btn-group .btn:hover { transform: none; background: var(--surface); }
.btn-group .btn.is-active { background: var(--surface); color: var(--brand); box-shadow: var(--shadow-xs); }

/* A segmented control built from labels wrapping hidden radios: reflect the checked state right
   away instead of waiting for the round trip to re-render `is-active`. */
.btn-group .btn:has(input:checked) {
    background: var(--surface);
    color: var(--brand);
    box-shadow: var(--shadow-xs);
}

/* --------------------------------------------------------------- 6. cards */

.card {
    position: relative;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
    transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.card--hover:hover {
    transform: translateY(-4px);
    box-shadow: var(--shadow-md);
    border-color: var(--border-strong);
}

.card__head {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 1rem 1.15rem;
    border-bottom: 1px solid var(--border);
}

.card__head h2, .card__head h3 { margin: 0; font-size: 0.98rem; }
.card__head .spacer { flex: 1; }

.card__body { padding: 1.15rem; }
.card__body--flush { padding: 0; }
.card__foot {
    padding: 0.85rem 1.15rem;
    border-top: 1px solid var(--border);
    background: var(--surface-alt);
}

.card__accent {
    position: absolute;
    inset-block-start: 0;
    inset-inline: 0;
    height: 3px;
    background: var(--brand);
}

/* Stat tiles */
.stat {
    position: relative;
    padding: 1.15rem 1.2rem;
    border-radius: var(--radius);
    background: var(--surface);
    border: 1px solid var(--border);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
    transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

.stat:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }

.stat::after {
    content: "";
    position: absolute;
    inset-block-start: -40%;
    inset-inline-start: -20%;
    width: 160px;
    height: 160px;
    border-radius: 50%;
    background: var(--stat-glow, var(--brand-soft));
    filter: blur(12px);
    opacity: 0.7;
    pointer-events: none;
}

.stat__head {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 0.55rem;
    position: relative;
    z-index: 1;
}

.stat__icon {
    width: 36px;
    height: 36px;
    border-radius: 11px;
    display: grid;
    place-items: center;
    background: var(--stat-icon-bg, var(--brand-soft));
    color: var(--stat-icon-fg, var(--brand));
    flex: none;
}

.stat__label { font-size: 0.8rem; color: var(--text-muted); font-weight: 500; }

.stat__value {
    position: relative;
    z-index: 1;
    font-size: 1.42rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.4;
}

.stat__meta {
    position: relative;
    z-index: 1;
    font-size: 0.76rem;
    color: var(--text-muted);
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: 0.2rem;
}

.trend { display: inline-flex; align-items: center; gap: 0.2rem; font-weight: 600; }
.trend--up { color: var(--danger); }
.trend--down { color: var(--success); }

/* --------------------------------------------------------------- 7. grid helpers */

.grid { display: grid; gap: 1rem; }
.grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid--auto { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.grid--stats { grid-template-columns: repeat(auto-fit, minmax(212px, 1fr)); }
.grid--main { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); align-items: start; }
.grid--sidebar { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); align-items: start; }

.stack { display: flex; flex-direction: column; gap: 1rem; }
.stack--sm { gap: 0.5rem; }
.stack--lg { gap: 1.5rem; }

.row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.row--between { justify-content: space-between; }
.row--end { justify-content: flex-end; }
.row--tight { gap: 0.35rem; }
.row--nowrap { flex-wrap: nowrap; }

.spacer { flex: 1; }

.mt-0 { margin-top: 0 !important; }
.mb-0 { margin-bottom: 0 !important; }
.mt-1 { margin-top: 0.5rem; }
.mt-2 { margin-top: 1rem; }
.mt-3 { margin-top: 1.5rem; }
.mb-1 { margin-bottom: 0.5rem; }
.mb-2 { margin-bottom: 1rem; }
.mb-3 { margin-bottom: 1.5rem; }

.text-muted { color: var(--text-muted); }
.text-soft { color: var(--text-soft); }
.text-sm { font-size: 0.8rem; }
.text-xs { font-size: 0.72rem; }
.text-lg { font-size: 1.05rem; }
.text-center { text-align: center; }
.text-start { text-align: start; }
.text-end { text-align: end; }
.fw-600 { font-weight: 600; }
.fw-700 { font-weight: 700; }
.truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hidden { display: none !important; }

.amount--positive { color: var(--success); }
.amount--negative { color: var(--danger); }

/* --------------------------------------------------------------- 8. badges & chips */

.badge {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.2rem 0.6rem;
    border-radius: var(--radius-full);
    font-size: 0.73rem;
    font-weight: 600;
    line-height: 1.7;
    background: var(--bg-sunken);
    color: var(--text-soft);
    white-space: nowrap;
}

.badge--success { background: var(--success-soft); color: var(--success); }
.badge--danger { background: var(--danger-soft); color: var(--danger); }
.badge--warning { background: var(--warning-soft); color: var(--warning); }
.badge--info { background: var(--info-soft); color: var(--info); }
.badge--brand { background: var(--brand-soft); color: var(--brand); }
.badge--muted { background: var(--bg-sunken); color: var(--text-muted); }

.badge--dot::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
}

.chip {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.3rem 0.7rem;
    border-radius: var(--radius-full);
    background: var(--bg-sunken);
    border: 1px solid var(--border);
    font-size: 0.78rem;
    color: var(--text-soft);
}

.chip__dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }

.chip--removable button {
    border: none;
    background: none;
    color: inherit;
    cursor: pointer;
    padding: 0;
    display: grid;
    place-items: center;
    opacity: 0.6;
}

.chip--removable button:hover { opacity: 1; }

/* Avatars */
.avatar {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    font-size: 0.78rem;
    font-weight: 700;
    color: #fff;
    background: var(--brand);
    flex: none;
    letter-spacing: 0;
    box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.18);
}

.avatar--sm { width: 28px; height: 28px; font-size: 0.66rem; }
.avatar--lg { width: 48px; height: 48px; font-size: 1rem; }

.avatar-stack { display: flex; }
.avatar-stack .avatar + .avatar { margin-inline-start: -10px; border: 2px solid var(--surface); }

/* --------------------------------------------------------------- 9. forms */

.form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 1rem 1.1rem;
}

.field { display: flex; flex-direction: column; gap: 0.35rem; min-width: 0; }
.field--full { grid-column: 1 / -1; }

.field > label,
.label {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--text-soft);
    display: flex;
    align-items: center;
    gap: 0.35rem;
}

.label .req { color: var(--danger); }

.input,
.select,
.textarea {
    width: 100%;
    padding: 0.6rem 0.85rem;
    min-height: 42px;
    font-family: inherit;
    font-size: 0.88rem;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
    appearance: none;
}

.textarea { min-height: 96px; resize: vertical; line-height: 1.9; }

.input:focus,
.select:focus,
.textarea:focus {
    outline: none;
    border-color: var(--brand);
    box-shadow: 0 0 0 3px var(--brand-soft);
}

.input::placeholder, .textarea::placeholder { color: var(--text-muted); opacity: 0.75; }

.input[readonly], .input[disabled], .select[disabled] {
    background: var(--bg-sunken);
    color: var(--text-muted);
    cursor: not-allowed;
}

.select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238892b0' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: left 0.7rem center;
    background-size: 16px;
    padding-inline-start: 2.2rem;
}

.input-group { position: relative; display: flex; align-items: stretch; }
.input-group .input { border-start-start-radius: 0; border-end-start-radius: 0; }

.input-group__addon {
    display: grid;
    place-items: center;
    padding-inline: 0.8rem;
    background: var(--bg-sunken);
    border: 1px solid var(--border-strong);
    border-inline-end: none;
    border-start-start-radius: var(--radius-sm);
    border-end-start-radius: var(--radius-sm);
    color: var(--text-muted);
    font-size: 0.78rem;
    white-space: nowrap;
}

.field__hint { font-size: 0.72rem; color: var(--text-muted); }
.field__error, .field-validation-error, .text-danger {
    font-size: 0.74rem;
    color: var(--danger);
    font-weight: 500;
}

.input.input-validation-error,
.select.input-validation-error,
.textarea.input-validation-error {
    border-color: var(--danger);
    box-shadow: 0 0 0 3px var(--danger-soft);
}

.validation-summary-errors {
    background: var(--danger-soft);
    border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent);
    border-radius: var(--radius-sm);
    padding: 0.85rem 1rem;
    margin-bottom: 1rem;
    color: var(--danger);
    font-size: 0.83rem;
}

.validation-summary-errors ul { margin: 0; padding-inline-start: 1.1rem; }
.validation-summary-valid { display: none; }

/* Switch */
.switch {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    cursor: pointer;
    user-select: none;
    font-size: 0.85rem;
}

.switch input { position: absolute; opacity: 0; width: 0; height: 0; }

.switch__track {
    width: 42px;
    height: 24px;
    border-radius: var(--radius-full);
    background: var(--border-strong);
    position: relative;
    transition: background-color var(--dur) var(--ease);
    flex: none;
}

.switch__track::after {
    content: "";
    position: absolute;
    top: 3px;
    /* Rest on the *start* edge (the right one under rtl); the checked rule below slides the
       knob 18px towards the end edge. Anchoring on inset-inline-end instead parked the knob
       on the end edge already, so switching it on pushed it clean off the track. */
    inset-inline-start: 3px;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: #fff;
    box-shadow: var(--shadow-xs);
    transition: transform var(--dur) var(--ease);
}

.switch input:checked + .switch__track { background: var(--brand); }
.switch input:checked + .switch__track::after { transform: translateX(-18px); }
.switch input:focus-visible + .switch__track { box-shadow: 0 0 0 3px var(--brand-soft); }

/* Checkbox / radio */
.check {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
    font-size: 0.85rem;
    user-select: none;
}

.check input {
    appearance: none;
    width: 19px;
    height: 19px;
    border: 1.5px solid var(--border-strong);
    border-radius: 6px;
    background: var(--surface);
    display: grid;
    place-items: center;
    cursor: pointer;
    transition: all var(--dur-fast) var(--ease);
    flex: none;
}

.check input[type="radio"] { border-radius: 50%; }

.check input:checked {
    background: var(--brand);
    border-color: var(--brand);
}

/* The tick is an L of two borders rotated 45deg. Those borders must be *physical*:
   `border-inline-start` resolves to the right edge under `dir="rtl"`, which mirrors the
   L and turns the tick into a sideways chevron. A checkmark has no reading direction. */
.check input:checked::after {
    content: "";
    width: 10px;
    height: 6px;
    border-left: 2px solid #fff;
    border-bottom: 2px solid #fff;
    transform: rotate(-45deg) translate(1px, -1px);
}

.check input[type="radio"]:checked::after {
    width: 7px;
    height: 7px;
    border: none;
    border-radius: 50%;
    background: #fff;
    transform: none;
}

/* A locked permission is shown ticked but greyed, so it reads as "granted, not yours to change". */
.check.is-locked { opacity: 0.7; cursor: default; }
.check.is-locked input { cursor: default; }

/* Access matrix: tint the row by what the personal override does to it. */
.access-row.is-allow { background: color-mix(in srgb, var(--success) 8%, transparent); }
.access-row.is-deny { background: color-mix(in srgb, var(--danger) 8%, transparent); }

/* Segmented option cards */
.option-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 0.6rem; }

.option-card {
    position: relative;
    border: 1.5px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 0.7rem 0.85rem;
    cursor: pointer;
    background: var(--surface);
    transition: all var(--dur-fast) var(--ease);
    display: flex;
    align-items: center;
    gap: 0.55rem;
    font-size: 0.84rem;
}

.option-card input { position: absolute; opacity: 0; }
.option-card:hover { border-color: var(--border-strong); background: var(--surface-hover); }

.option-card:has(input:checked) {
    border-color: var(--brand);
    background: var(--brand-soft);
    color: var(--brand);
    font-weight: 600;
}

/* File dropzone */
.dropzone {
    /* The markup uses <label>, which is inline by default: the <div> hint lines would break
       out of the inline box and only a sliver of dashed border would wrap the icon. */
    display: block;
    width: 100%;
    border: 1.5px dashed var(--border-strong);
    border-radius: var(--radius-sm);
    padding: 1.3rem;
    text-align: center;
    background: var(--surface-alt);
    cursor: pointer;
    transition: all var(--dur-fast) var(--ease);
    color: var(--text-muted);
    font-size: 0.83rem;
}

.dropzone:hover, .dropzone.is-dragging {
    border-color: var(--brand);
    background: var(--brand-soft);
    color: var(--brand);
}

.dropzone input { display: none; }

.file-list { display: flex; flex-direction: column; gap: 0.4rem; margin-top: 0.6rem; }

.file-item {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.5rem 0.7rem;
    background: var(--surface-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-xs);
    font-size: 0.8rem;
}

.file-item__icon {
    width: 30px;
    height: 30px;
    border-radius: 8px;
    display: grid;
    place-items: center;
    background: var(--brand-soft);
    color: var(--brand);
    flex: none;
}

.file-item__name { flex: 1; min-width: 0; }

/* --------------------------------------------------------------- 10. tables */

.table-wrap {
    width: 100%;
    overflow-x: auto;
    border-radius: var(--radius);
    -webkit-overflow-scrolling: touch;
}

.table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: 0.85rem;
}

.table thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--surface-alt);
    padding: 0.75rem 0.9rem;
    text-align: start;
    font-size: 0.76rem;
    font-weight: 600;
    color: var(--text-muted);
    white-space: nowrap;
    border-bottom: 1px solid var(--border);
}

.table tbody td {
    padding: 0.72rem 0.9rem;
    border-bottom: 1px solid var(--border);
    vertical-align: middle;
}

.table tbody tr {
    transition: background-color var(--dur-fast) var(--ease);
}

.table tbody tr:hover { background: var(--surface-hover); }
.table tbody tr:last-child td { border-bottom: none; }

.table tfoot td {
    padding: 0.8rem 0.9rem;
    background: var(--surface-alt);
    font-weight: 700;
    border-top: 1px solid var(--border-strong);
}

.table--zebra tbody tr:nth-child(even) { background: var(--surface-alt); }
.table--zebra tbody tr:nth-child(even):hover { background: var(--surface-hover); }

.table .col-actions { width: 1%; white-space: nowrap; }
.table .col-num { width: 1%; white-space: nowrap; color: var(--text-muted); }

/* A value that carries more detail in its title attribute - hints at it rather than hiding it. */
.has-tip {
    cursor: help;
    text-decoration: underline dotted currentColor;
    text-underline-offset: 3px;
}

.table__title { display: flex; flex-direction: column; gap: 0.1rem; }
.table__title strong { font-weight: 600; }
.table__title span { font-size: 0.74rem; color: var(--text-muted); }

/* initTableSort() puts this class on the <th> of every sortable column. It used to also set
   `display: inline-flex`, which takes the cell out of the table's row/column model - the browser
   then wraps it in an anonymous cell and the header stops sharing the body's column widths. That
   is why the header columns did not line up with the data columns on every table carrying
   data-sortable. A header cell has to stay a table-cell; the arrow is a pseudo-element instead. */
.sortable {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}

.sortable:hover { color: var(--text); }

.sortable::after {
    content: "⇅";
    display: inline-block;
    margin-inline-start: 0.25rem;
    font-size: 0.8em;
    opacity: 0.35;
}

.sortable[data-sort-dir="asc"]::after { content: "↑"; opacity: 1; color: var(--brand); }
.sortable[data-sort-dir="desc"]::after { content: "↓"; opacity: 1; color: var(--brand); }

/* Mobile: turn each row into a stacked card */
@media (max-width: 860px) {
    .table--responsive thead { display: none; }
    .table--responsive tbody tr {
        display: block;
        margin-bottom: 0.7rem;
        border: 1px solid var(--border);
        border-radius: var(--radius-sm);
        background: var(--surface);
        padding: 0.35rem 0;
        box-shadow: var(--shadow-xs);
    }
    .table--responsive tbody td {
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 1rem;
        border-bottom: 1px dashed var(--border);
        padding: 0.5rem 0.85rem;
    }
    .table--responsive tbody td:last-child { border-bottom: none; }
    .table--responsive tbody td::before {
        content: attr(data-label);
        font-size: 0.74rem;
        font-weight: 600;
        color: var(--text-muted);
        flex: none;
    }
    .table--responsive tbody td:not([data-label])::before { content: none; }
    .table--responsive tfoot { display: none; }
}

/* Empty state */
.empty {
    padding: 3rem 1.5rem;
    text-align: center;
    color: var(--text-muted);
}

.empty__icon {
    width: 72px;
    height: 72px;
    border-radius: 22px;
    background: var(--bg-sunken);
    display: grid;
    place-items: center;
    margin: 0 auto 1rem;
    color: var(--text-muted);
}

.empty__icon .icon { width: 32px; height: 32px; }
.empty h3 { color: var(--text-soft); margin-bottom: 0.3rem; }

/* --------------------------------------------------------------- 11. progress */

.progress {
    height: 8px;
    border-radius: var(--radius-full);
    background: var(--bg-sunken);
    overflow: hidden;
}

.progress__bar {
    height: 100%;
    border-radius: var(--radius-full);
    background: linear-gradient(90deg, var(--brand), var(--violet));
    width: 0;
    transition: width var(--dur-slow) var(--ease-out);
}

.progress--success .progress__bar { background: linear-gradient(90deg, var(--success), #63e6be); }
.progress--danger .progress__bar { background: linear-gradient(90deg, var(--danger), #ff8787); }
.progress--warning .progress__bar { background: linear-gradient(90deg, var(--warning), #ffd43b); }

.meter { display: flex; flex-direction: column; gap: 0.35rem; }
.meter__head { display: flex; justify-content: space-between; font-size: 0.78rem; }

/* --------------------------------------------------------------- 12. toasts */

.toast-host {
    position: fixed;
    inset-block-start: 1rem;
    inset-inline-start: 1rem;
    z-index: 200;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    max-width: min(360px, calc(100vw - 2rem));
}

.toast {
    display: flex;
    align-items: flex-start;
    gap: 0.7rem;
    padding: 0.85rem 1rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-lg);
    animation: toast-in 0.42s var(--ease-out) both;
    font-size: 0.85rem;
    position: relative;
    overflow: hidden;
}

.toast::before {
    content: "";
    position: absolute;
    inset-block: 0;
    inset-inline-end: 0;
    width: 3px;
    background: var(--toast-color, var(--brand));
}

.toast--success { --toast-color: var(--success); }
.toast--error { --toast-color: var(--danger); }
.toast--info { --toast-color: var(--info); }
.toast--warning { --toast-color: var(--warning); }

.toast__icon { color: var(--toast-color, var(--brand)); flex: none; margin-top: 2px; }
.toast__body { flex: 1; min-width: 0; }
.toast__close { border: none; background: none; color: var(--text-muted); cursor: pointer; padding: 0; }

.toast.is-leaving { animation: toast-out 0.3s var(--ease) forwards; }

@keyframes toast-in {
    from { opacity: 0; transform: translateX(-24px) scale(0.96); }
    to { opacity: 1; transform: none; }
}

@keyframes toast-out {
    to { opacity: 0; transform: translateX(-24px) scale(0.96); }
}

/* --------------------------------------------------------------- 13. modal & drawer */

.modal {
    position: fixed;
    inset: 0;
    z-index: 150;
    display: grid;
    place-items: center;
    padding: 1rem;
    background: rgba(8, 12, 26, 0.55);
    backdrop-filter: blur(4px);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--dur) var(--ease);
}

.modal.is-open { opacity: 1; pointer-events: auto; }

.modal__panel {
    width: min(640px, 100%);
    max-height: min(86vh, 900px);
    display: flex;
    flex-direction: column;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    transform: translateY(18px) scale(0.97);
    transition: transform var(--dur) var(--ease-out);
    overflow: hidden;
}

.modal.is-open .modal__panel { transform: none; }
.modal--wide .modal__panel { width: min(900px, 100%); }

.modal__head {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 1rem 1.25rem;
    border-bottom: 1px solid var(--border);
}

.modal__head h3 { margin: 0; font-size: 1rem; }
.modal__body { padding: 1.25rem; overflow-y: auto; flex: 1; }
.modal__foot {
    padding: 0.9rem 1.25rem;
    border-top: 1px solid var(--border);
    background: var(--surface-alt);
    display: flex;
    gap: 0.6rem;
    justify-content: flex-start;
}

/* --------------------------------------------------------------- 14. dropdown */

.dropdown { position: relative; }

.dropdown__menu {
    position: absolute;
    inset-block-start: calc(100% + 8px);
    inset-inline-start: 0;
    min-width: 230px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-lg);
    padding: 0.4rem;
    z-index: 80;
    opacity: 0;
    transform: translateY(-8px) scale(0.98);
    pointer-events: none;
    transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
    max-height: 70vh;
    overflow-y: auto;
}

.dropdown.is-open .dropdown__menu { opacity: 1; transform: none; pointer-events: auto; }
.dropdown__menu--end { inset-inline-start: auto; inset-inline-end: 0; }
.dropdown__menu--wide { min-width: 330px; }

.dropdown__item {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    width: 100%;
    padding: 0.5rem 0.65rem;
    border-radius: var(--radius-xs);
    border: none;
    background: none;
    color: var(--text-soft);
    font-family: inherit;
    font-size: 0.85rem;
    text-align: start;
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.dropdown__item:hover { background: var(--surface-hover); color: var(--text); }
.dropdown__item--danger { color: var(--danger); }
.dropdown__item--danger:hover { background: var(--danger-soft); }
.dropdown__divider { height: 1px; background: var(--border); margin: 0.35rem 0.2rem; }

/* User box in sidebar footer */
.userbox {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    padding: 0.55rem;
    border-radius: var(--radius-sm);
    width: 100%;
    border: 1px solid var(--border);
    background: var(--surface-alt);
    cursor: pointer;
    font-family: inherit;
    color: inherit;
    text-align: start;
    transition: background-color var(--dur-fast) var(--ease);
}

.userbox:hover { background: var(--surface-hover); }
.userbox__meta { min-width: 0; flex: 1; line-height: 1.4; }
.userbox__meta strong { display: block; font-size: 0.84rem; }
.userbox__meta span { display: block; font-size: 0.7rem; color: var(--text-muted); }

/* Notification bell */
.bell { position: relative; }

.bell__count {
    position: absolute;
    top: 4px;
    inset-inline-end: 4px;
    min-width: 17px;
    height: 17px;
    padding: 0 4px;
    background: var(--danger);
    color: #fff;
    border-radius: var(--radius-full);
    font-size: 0.62rem;
    font-weight: 700;
    display: grid;
    place-items: center;
    border: 2px solid var(--bg);
    animation: pulse-badge 2.4s var(--ease) infinite;
}

@keyframes pulse-badge {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.12); }
}

.notif-item {
    display: flex;
    gap: 0.6rem;
    padding: 0.6rem 0.65rem;
    border-radius: var(--radius-xs);
    transition: background-color var(--dur-fast) var(--ease);
    color: var(--text-soft);
}

.notif-item:hover { background: var(--surface-hover); }
.notif-item.is-unread { background: var(--brand-soft); }
.notif-item__dot { width: 8px; height: 8px; border-radius: 50%; background: var(--brand); margin-top: 8px; flex: none; }
.notif-item__body { min-width: 0; flex: 1; }
.notif-item__body strong { display: block; font-size: 0.82rem; color: var(--text); }
.notif-item__body p { margin: 0; font-size: 0.76rem; }
.notif-item__time { font-size: 0.68rem; color: var(--text-muted); }

/* --------------------------------------------------------------- 15. tabs */

.tabs {
    display: flex;
    gap: 0.25rem;
    border-bottom: 1px solid var(--border);
    overflow-x: auto;
    scrollbar-width: none;
}

.tabs::-webkit-scrollbar { display: none; }

.tab {
    position: relative;
    padding: 0.65rem 1rem;
    font-size: 0.87rem;
    font-weight: 600;
    color: var(--text-muted);
    white-space: nowrap;
    border: none;
    background: none;
    cursor: pointer;
    font-family: inherit;
    transition: color var(--dur-fast) var(--ease);
}

.tab:hover { color: var(--text); }
.tab.is-active { color: var(--brand); }

.tab.is-active::after {
    content: "";
    position: absolute;
    inset-inline: 0.6rem;
    bottom: -1px;
    height: 2.5px;
    border-radius: var(--radius-full) var(--radius-full) 0 0;
    background: var(--brand);
    animation: tab-in var(--dur) var(--ease-out);
}

@keyframes tab-in {
    from { transform: scaleX(0.3); opacity: 0; }
    to { transform: none; opacity: 1; }
}

/* --------------------------------------------------------------- 16. filter bar */

.filters {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: 0.7rem;
    /* Top aligned, not bottom aligned: a field carrying a .field__hint is taller than its
       neighbours, and bottom alignment pushed its label and control *up* out of the row. */
    align-items: start;
}

.filters .field > label { font-size: 0.74rem; }
.filters .input, .filters .select { min-height: 38px; padding-block: 0.45rem; font-size: 0.83rem; }

/* Reserves the same space the sibling fields spend on their label, so the buttons line up with
   the inputs rather than with the labels. */
.filters .filter-actions { padding-block-start: calc(0.74rem * 1.85 + 0.35rem); }

.filter-actions { display: flex; gap: 0.5rem; align-items: end; }

/* --------------------------------------------------------------- 17. pagination */

.pager {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.3rem;
    flex-wrap: wrap;
    padding: 1rem 0 0;
}

.pager__link {
    min-width: 36px;
    height: 36px;
    padding: 0 0.6rem;
    border-radius: var(--radius-xs);
    display: grid;
    place-items: center;
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--text-soft);
    font-size: 0.83rem;
    font-weight: 600;
    transition: all var(--dur-fast) var(--ease);
}

.pager__link:hover { background: var(--surface-hover); color: var(--text); border-color: var(--border-strong); }
.pager__link.is-active { background: var(--brand); border-color: var(--brand); color: #fff; }
.pager__link.is-disabled { opacity: 0.4; pointer-events: none; }
.pager__info { font-size: 0.78rem; color: var(--text-muted); margin-inline-end: auto; }

/* --------------------------------------------------------------- 18. calendar */

.calendar {
    /* A whole month has to be readable in one look, with no scrolling. A fixed 96px row made six
       weeks ~640px tall, which never fit under the topbar and the filter card. Deriving the row
       height from what is actually left of the viewport keeps all six weeks on screen, and the
       clamp stops it collapsing on a short window or ballooning on a tall one. */
    --cal-chrome: 330px;
    --cell-min: 78px;
    --cell-min: clamp(46px, calc((100vh - var(--cal-chrome)) / 6), 96px);
    --cell-min: clamp(46px, calc((100dvh - var(--cal-chrome)) / 6), 96px);
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 6px;
}

.calendar__dow {
    text-align: center;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--text-muted);
    padding: 0.4rem 0;
}

.calendar__cell {
    position: relative;
    /* Fixed, not just a minimum: a busy day must not stretch its week row and push the rest of the
       month off screen. Overflow is hidden and the cell shows a "+N more" line instead. */
    min-height: var(--cell-min);
    height: var(--cell-min);
    padding: 0.3rem 0.35rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--surface);
    display: flex;
    flex-direction: column;
    gap: 3px;
    transition: all var(--dur-fast) var(--ease);
    overflow: hidden;
}

.calendar__cell:hover { border-color: var(--brand); box-shadow: var(--shadow-sm); }
.calendar__cell.is-outside { opacity: 0.42; background: var(--surface-alt); }
.calendar__cell.is-holiday .calendar__day { color: var(--danger); }

.calendar__cell.is-today {
    border-color: var(--brand);
    box-shadow: 0 0 0 2px var(--brand-soft);
}

.calendar__day {
    font-size: 0.82rem;
    font-weight: 700;
    color: var(--text-soft);
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.calendar__cell.is-today .calendar__day { color: var(--brand); }

.calendar__event {
    display: block;
    flex: none;
    font-size: 0.64rem;
    line-height: 1.35;
    padding: 1px 4px;
    border-radius: 6px;
    background: var(--ev-color, var(--brand-soft));
    color: var(--ev-fg, var(--brand));
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    border-inline-start: 2px solid currentColor;
    transition: transform var(--dur-fast) var(--ease);
}

.calendar__event:hover { transform: translateX(-2px); }
.calendar__event.is-done { text-decoration: line-through; opacity: 0.6; }
.calendar__more { font-size: 0.66rem; color: var(--text-muted); }

@media (max-width: 720px) {
    .calendar {
        --cal-chrome: 300px;
        --cell-min: clamp(40px, calc((100dvh - var(--cal-chrome)) / 6), 66px);
        gap: 3px;
    }
    .calendar__dow { font-size: 0.66rem; padding: 0.2rem 0; }
    .calendar__event { font-size: 0.56rem; }
    .calendar__day { font-size: 0.74rem; }
}

/* --------------------------------------------------------------- 19. persian datepicker */

.pdp {
    position: absolute;
    /* Above .modal (150) - the popup is appended to <body>, so a date field inside a
       modal would otherwise open behind the dimmed overlay. */
    z-index: 160;
    width: 290px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-lg);
    padding: 0.75rem;
    animation: pdp-in 0.2s var(--ease-out);
}

@keyframes pdp-in {
    from { opacity: 0; transform: translateY(-6px) scale(0.98); }
    to { opacity: 1; transform: none; }
}

.pdp__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.5rem;
    gap: 0.3rem;
}

.pdp__nav {
    width: 30px;
    height: 30px;
    border-radius: 8px;
    border: none;
    background: var(--bg-sunken);
    color: var(--text-soft);
    cursor: pointer;
    display: grid;
    place-items: center;
    transition: all var(--dur-fast) var(--ease);
}

.pdp__nav:hover { background: var(--brand-soft); color: var(--brand); }

.pdp__title {
    flex: 1;
    text-align: center;
    font-weight: 700;
    font-size: 0.87rem;
    cursor: pointer;
    padding: 0.25rem;
    border-radius: 8px;
}

.pdp__title:hover { background: var(--surface-hover); }

.pdp__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }

.pdp__dow {
    text-align: center;
    font-size: 0.68rem;
    color: var(--text-muted);
    font-weight: 600;
    padding: 3px 0;
}

.pdp__day {
    aspect-ratio: 1;
    border: none;
    background: none;
    border-radius: 9px;
    font-family: inherit;
    font-size: 0.8rem;
    color: var(--text-soft);
    cursor: pointer;
    transition: all var(--dur-fast) var(--ease);
    display: grid;
    place-items: center;
}

.pdp__day:hover { background: var(--brand-soft); color: var(--brand); }
.pdp__day.is-outside { opacity: 0.32; }
.pdp__day.is-today { box-shadow: inset 0 0 0 1.5px var(--brand); color: var(--brand); }
.pdp__day.is-selected { background: var(--brand); color: #fff; font-weight: 700; }
.pdp__day.is-friday { color: var(--danger); }

.pdp__months { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; }

.pdp__month {
    padding: 0.5rem 0.2rem;
    border: none;
    background: var(--bg-sunken);
    border-radius: 9px;
    font-family: inherit;
    font-size: 0.78rem;
    color: var(--text-soft);
    cursor: pointer;
    transition: all var(--dur-fast) var(--ease);
}

.pdp__month:hover, .pdp__month.is-selected { background: var(--brand); color: #fff; }

.pdp__foot {
    display: flex;
    gap: 0.4rem;
    margin-top: 0.5rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--border);
}

.pdp-input-wrap { position: relative; }

.pdp-input-wrap .pdp-toggle {
    position: absolute;
    inset-block: 0;
    inset-inline-start: 0;
    width: 40px;
    display: grid;
    place-items: center;
    border: none;
    background: none;
    color: var(--text-muted);
    cursor: pointer;
}

.pdp-input-wrap .pdp-toggle:hover { color: var(--brand); }
.pdp-input-wrap .input { padding-inline-start: 2.4rem; }

/* --------------------------------------------------------------- 20. charts */

.chart { width: 100%; display: block; overflow: visible; }
.chart text { font-family: var(--font); fill: var(--text-muted); font-size: 10px; }
.chart .grid-line { stroke: var(--border); stroke-width: 1; stroke-dasharray: 3 4; }
.chart .axis-line { stroke: var(--border-strong); stroke-width: 1; }

.chart .bar {
    transition: opacity var(--dur-fast) var(--ease);
    transform-box: fill-box;
    transform-origin: bottom;
    animation: bar-grow 0.7s var(--ease-out) both;
}

.chart .bar:hover { opacity: 0.82; }

@keyframes bar-grow {
    from { transform: scaleY(0); }
    to { transform: scaleY(1); }
}

.chart .line-path {
    fill: none;
    stroke-width: 2.5;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 1400;
    stroke-dashoffset: 1400;
    animation: line-draw 1.4s var(--ease-out) forwards;
}

@keyframes line-draw {
    to { stroke-dashoffset: 0; }
}

.donut__ring {
    transform: rotate(-90deg);
    /* Spin around the middle of the viewBox, which is where charts.js centres the ring. */
    transform-box: view-box;
    transform-origin: center;
}

.donut__seg {
    transition: stroke-width var(--dur-fast) var(--ease);
    animation: donut-in 0.9s var(--ease-out) both;
}

.donut__seg:hover { stroke-width: 22; }

@keyframes donut-in {
    from { stroke-dasharray: 0 1000; }
}

.legend { display: flex; flex-direction: column; gap: 0.5rem; }

.legend__item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.79rem;
    padding: 0.25rem 0;
}

.legend__dot { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.legend__label { flex: 1; min-width: 0; color: var(--text-soft); }
.legend__value { font-weight: 600; }

/* --------------------------------------------------------------- 21. auth pages */

.auth {
    min-height: 100vh;
    min-height: 100dvh;
    display: grid;
    grid-template-columns: 1fr 1fr;
    background: var(--bg);
}

.auth__aside {
    position: relative;
    background: linear-gradient(150deg, #16213e, #1f3a75 45%, #3b6ff6);
    color: #fff;
    padding: 3rem;
    display: flex;
    flex-direction: column;
    justify-content: center;
    overflow: hidden;
}

.auth__aside::before,
.auth__aside::after {
    content: "";
    position: absolute;
    border-radius: 50%;
    filter: blur(60px);
    opacity: 0.5;
}

.auth__aside::before {
    width: 340px; height: 340px;
    background: #7c5cff;
    top: -80px; inset-inline-start: -60px;
    animation: float-a 14s var(--ease) infinite alternate;
}

.auth__aside::after {
    width: 300px; height: 300px;
    background: #12b886;
    bottom: -70px; inset-inline-end: -50px;
    animation: float-b 17s var(--ease) infinite alternate;
}

@keyframes float-a {
    to { transform: translate(40px, 60px) scale(1.12); }
}

@keyframes float-b {
    to { transform: translate(-50px, -40px) scale(1.08); }
}

.auth__aside > * { position: relative; z-index: 1; }
.auth__aside h2 { font-size: 1.9rem; margin-bottom: 0.8rem; }
.auth__aside p { color: rgba(255, 255, 255, 0.82); max-width: 44ch; }

.auth__features { display: flex; flex-direction: column; gap: 0.9rem; margin-top: 2rem; }

.auth__feature {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    font-size: 0.9rem;
    color: rgba(255, 255, 255, 0.9);
}

.auth__feature .icon-wrap {
    width: 36px; height: 36px;
    border-radius: 11px;
    background: rgba(255, 255, 255, 0.16);
    display: grid;
    place-items: center;
    flex: none;
}

.auth__panel {
    display: grid;
    place-items: center;
    padding: 2rem 1.5rem;
}

.auth__card {
    width: min(420px, 100%);
    animation: fade-up 0.6s var(--ease-out) both;
}

.auth__logo {
    width: 56px; height: 56px;
    border-radius: 17px;
    background: linear-gradient(135deg, var(--brand), var(--violet));
    display: grid;
    place-items: center;
    color: #fff;
    box-shadow: var(--shadow-brand);
    margin-bottom: 1.2rem;
}

.auth__logo svg { width: 28px; height: 28px; stroke-width: 2; }

@media (max-width: 900px) {
    .auth { grid-template-columns: 1fr; }
    .auth__aside { display: none; }
}

/* --------------------------------------------------------------- 22. animations */

@keyframes fade-up {
    from { opacity: 0; transform: translateY(14px); }
    to { opacity: 1; transform: none; }
}

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

@keyframes scale-in {
    from { opacity: 0; transform: scale(0.96); }
    to { opacity: 1; transform: none; }
}

.animate-in { animation: fade-up 0.5s var(--ease-out) both; }
.animate-fade { animation: fade-in 0.5s var(--ease) both; }

/* Staggered reveal - the delay index is set inline as --i */
[data-reveal] {
    animation: fade-up 0.55s var(--ease-out) both;
    animation-delay: calc(var(--i, 0) * 55ms);
}

.skeleton {
    background: linear-gradient(90deg, var(--bg-sunken) 25%, var(--surface-hover) 50%, var(--bg-sunken) 75%);
    background-size: 200% 100%;
    animation: shimmer 1.4s infinite;
    border-radius: var(--radius-xs);
}

@keyframes shimmer {
    to { background-position: -200% 0; }
}

.spinner {
    width: 18px;
    height: 18px;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: spin 0.7s linear infinite;
    display: inline-block;
}

@keyframes spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* --------------------------------------------------------------- 23. misc blocks */

.alert {
    display: flex;
    align-items: flex-start;
    gap: 0.7rem;
    padding: 0.85rem 1rem;
    border-radius: var(--radius-sm);
    font-size: 0.84rem;
    border: 1px solid transparent;
}

.alert--info { background: var(--info-soft); color: var(--info); border-color: color-mix(in srgb, var(--info) 25%, transparent); }
.alert--warning { background: var(--warning-soft); color: var(--warning); border-color: color-mix(in srgb, var(--warning) 25%, transparent); }
.alert--danger { background: var(--danger-soft); color: var(--danger); border-color: color-mix(in srgb, var(--danger) 25%, transparent); }
.alert--success { background: var(--success-soft); color: var(--success); border-color: color-mix(in srgb, var(--success) 25%, transparent); }
.alert .icon { margin-top: 3px; flex: none; }
.alert a { color: inherit; text-decoration: underline; }

.timeline { position: relative; padding-inline-start: 1.4rem; }

.timeline::before {
    content: "";
    position: absolute;
    inset-block: 6px;
    inset-inline-start: 5px;
    width: 2px;
    background: var(--border);
    border-radius: var(--radius-full);
}

.timeline__item { position: relative; padding-bottom: 1.1rem; }

.timeline__item::before {
    content: "";
    position: absolute;
    inset-inline-start: -1.4rem;
    top: 8px;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: var(--surface);
    border: 2.5px solid var(--tl-color, var(--brand));
}

.timeline__item:last-child { padding-bottom: 0; }

.kv { display: grid; grid-template-columns: auto 1fr; gap: 0.5rem 1rem; font-size: 0.85rem; }
.kv dt { color: var(--text-muted); font-size: 0.8rem; }
.kv dd { margin: 0; font-weight: 500; }

.divider-text {
    display: flex;
    align-items: center;
    gap: 0.8rem;
    color: var(--text-muted);
    font-size: 0.78rem;
    margin: 1.2rem 0;
}

.divider-text::before, .divider-text::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--border);
}

.color-swatches { display: flex; gap: 0.4rem; flex-wrap: wrap; }

.color-swatch {
    width: 30px;
    height: 30px;
    border-radius: 9px;
    cursor: pointer;
    border: 2px solid transparent;
    transition: transform var(--dur-fast) var(--ease);
    position: relative;
}

.color-swatch:hover { transform: scale(1.1); }
.color-swatch.is-selected { border-color: var(--text); transform: scale(1.08); }
.color-swatch input { position: absolute; opacity: 0; }

.install-banner {
    display: flex;
    align-items: center;
    gap: 0.8rem;
    padding: 0.85rem 1rem;
    border-radius: var(--radius-sm);
    background: linear-gradient(120deg, var(--brand-soft), transparent);
    border: 1px solid var(--brand-soft);
    font-size: 0.85rem;
}

/* --------------------------------------------------------------- 24. responsive */

@media (max-width: 1200px) {
    .grid--main, .grid--sidebar { grid-template-columns: minmax(0, 1fr); }
    .grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 1024px) {
    :root { --sidebar-w: 258px; }

    .sidebar { transform: translateX(100%); box-shadow: var(--shadow-lg); }
    body.sidebar-open .sidebar { transform: none; }
    .main { margin-inline-start: 0 !important; }
    body.sidebar-collapsed .sidebar { width: var(--sidebar-w); }
    body.sidebar-collapsed .sidebar__title,
    body.sidebar-collapsed .nav-group__label,
    body.sidebar-collapsed .nav-link span:not(.nav-link__badge),
    body.sidebar-collapsed .userbox__meta { display: revert; }
    body.sidebar-collapsed .nav-link { justify-content: flex-start; padding-inline: 0.8rem; }
    body.sidebar-collapsed .sidebar__brand { justify-content: flex-start; padding-inline: 1.25rem; }
}

@media (max-width: 860px) {
    .grid--2, .grid--3, .grid--4 { grid-template-columns: minmax(0, 1fr); }
    .content { padding: 1rem 0.9rem 3rem; }
    .topbar { padding: 0.6rem 0.9rem; }
    .topbar__title h1 { font-size: 1rem; }
    body { font-size: 14px; }
}

@media (max-width: 560px) {
    .stat__value { font-size: 1.2rem; }
    .modal__panel { border-radius: var(--radius); }
    .toast-host { inset-inline: 0.75rem; max-width: none; }
    .btn { padding-inline: 0.8rem; }
    .hide-xs { display: none !important; }
}

/* --------------------------------------------------------------- 25. print */

/* The counterpart of .no-print: a heading that exists only on paper. The topbar and the filter
   card are both hidden when printing, so without one a printed list has nothing on it saying
   which project, status or date range produced it. */
.print-only { display: none; }

@media print {
    .sidebar, .topbar, .page-foot, .toast-host, .no-print, .btn, .pager { display: none !important; }
    .main { margin: 0 !important; }
    .content { padding: 0; max-width: none; }
    body { background: #fff; color: #000; font-size: 11pt; }
    .card { box-shadow: none; border-color: #ccc; break-inside: avoid; }
    .table thead th { background: #f2f2f2 !important; color: #000; }
    a { color: #000; }
    .print-only { display: block; }
    @page { size: A4; margin: 12mm; }
}
