/*
  Desktop design tokens and shared components.

  One sheet, linked by every layout, for the pieces the design guide fixes: the tile (kpi-card),
  the notice, figures, selects, action menus, section spacing, row states and the How it works
  button. Overrides scoped to .desktop-ui (set on every desktop <body>) restyle legacy Metronic
  alert-* boxes, dashed borders and native selects so the old look cannot come back while the
  views are swept. Mobile CSS is untouched. See .claude/skills/desktop-component/SKILL.md before
  changing anything here.

  Dark mode: Metronic's bundle already carries a full [data-bs-theme=dark] palette (body, cards,
  sidebar, header, inputs, dropdowns). The block at the end of this sheet re-declares the --mt-*
  tokens for dark and restores the brand blue that Metronic's dark theme replaces, so every rule
  above stays theme-agnostic by reading tokens rather than literals. Light values never change.
*/

:root {
    --mt-blue: #1238FF;      /* Bold blue: interaction only (primary, links, active states) */
    --mt-midnight: #002953;  /* Midnight: text inside shared components, default figure */
    --mt-text2: #51708C;     /* secondary text, muted figures */
    --mt-border: #E8E9F2;    /* hairline */
    --mt-red: #F8285A;       /* problems, negatives and blocked states */
    --mt-row: #EEF1FF;       /* current row tint */
    --mt-green: #17c653;     /* money in your favour */
    --mt-amber: #f69600;     /* needs attention */
    --mt-font: "Manrope", sans-serif;
    --mt-figure: 28px;
    --mt-section: 32px;
    --mt-radius: 10px;
    /* surfaces and alpha bases: never write #ffffff or rgba(0,41,83,x) for a surface, border or
       caption; the dark block at the end of this sheet re-declares these */
    --mt-surface: #ffffff;                 /* card, notice, select, dropzone, menu */
    --mt-surface-2: #f9f9fc;               /* inset and disabled surfaces */
    --mt-ink-rgb: 0, 41, 83;               /* Midnight as rgb, for text and border alpha */
    --mt-shadow-rgb: 0, 41, 83;            /* shadow alpha base */
    --mt-tint-blue: #eaeeff;               /* .kpi-icon default */
    --mt-tint-teal: #e0f3f3;
    --mt-tint-icterine: #f4fad6;
    --mt-teal-ink: #2f8f8f;
    --mt-icterine-ink: #7a8a1f;
    --mt-select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%2351708C' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
}

/* ---- section spacing: 32px between major blocks (header, tiles, filters, table/form) ---- */
.page-section { margin-bottom: var(--mt-section); }

/* ---- figures and tables ---- */
.tabular { font-variant-numeric: tabular-nums; }
.money-zero { color: var(--mt-text2); }
.text-midnight { color: var(--mt-midnight) !important; }
.desktop-ui .table tbody tr.is-current > td { background: var(--mt-row); }
.desktop-ui .table tbody tr.is-future > td { color: var(--mt-text2); }
.desktop-ui .table tbody tr.is-future > td .badge { opacity: .7; }
/* A subtotal or total row inside a table body: one heavier rule above it. `border-top border-2`
   on the <tr> draws a box round every cell when the row is not the table's last. */
.desktop-ui .table tbody tr.table-total-row > * { border-top: 2px solid var(--mt-border); }

/* ---- KPI card container ---- */
.kpi-card {
    /* containing block for the footer link's stretched-link ::after, which makes the whole card
       the click target without wrapping it in an anchor */
    position: relative;
    height: 100%;
    background: var(--mt-surface);
    border: 1px solid rgba(var(--mt-ink-rgb), 0.08);
    border-radius: 22px;
    padding: 22px 22px 16px;
    transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease;
}
/* controls that must stay clickable above the stretched-link overlay (z-index 1): the refresh
   icon, the head-right slot (Dashboard profit toggle), tooltips and any link in the caption */
.kpi-card .kpi-refresh,
.kpi-card .kpi-head > :not(.kpi-title),
.kpi-card .kpi-title > *,
.kpi-card .kpi-caption a,
.kpi-card .kpi-caption button {
    position: relative;
    z-index: 2;
}
.kpi-card:hover {
    transform: translateY(-3px);
    box-shadow: 0 16px 30px -18px rgba(var(--mt-shadow-rgb), 0.35);
    border-color: rgba(18,56,255,0.25);
}
/* a figure-only tile (no footer link) must not invite a click */
.kpi-card.kpi-static:hover {
    transform: none;
    box-shadow: none;
    border-color: rgba(var(--mt-ink-rgb), 0.08);
}
/* a tile placed beside a filter row rather than in a grid column */
.kpi-inline { min-width: 220px; height: auto; }

/* ---- header: title + icon ---- */
.kpi-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 18px;
}
.kpi-title {
    font-family: var(--mt-font);
    font-size: 15px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--mt-midnight);
}

/* ---- coloured icon tile (top-right) ---- */
.kpi-icon {
    width: 36px;
    height: 36px;
    border-radius: 11px;
    display: grid;
    place-items: center;
    flex-shrink: 0;
    background: var(--mt-tint-blue);   /* default: Blue soft */
    color: var(--mt-blue);
}
.kpi-icon.teal     { background: var(--mt-tint-teal); color: var(--mt-teal-ink); }
.kpi-icon.icterine { background: var(--mt-tint-icterine); color: var(--mt-icterine-ink); }
.kpi-icon.mid      { background: rgba(var(--mt-ink-rgb), 0.07); color: var(--mt-midnight); }

/* ---- amount: 28px, Midnight by default; green = money in your favour, amber = owed to you or
        needs attention, red = problem or negative, muted = zero or empty. Display figures set
        proportional and tight; tabular numerals belong in table columns (.tabular), never at
        display size. No gap between the £ and the digits. ---- */
.kpi-amount {
    font-family: var(--mt-font);
    font-size: var(--mt-figure) !important;
    line-height: 1;
    font-weight: 700 !important;
    color: var(--mt-midnight);
    display: flex;
    align-items: baseline;
    letter-spacing: -0.01em;
    font-variant-numeric: proportional-nums;
}
.kpi-amount .cur {
    font-size: 19px;
    color: currentColor;
    opacity: .55;
}
.kpi-amount.green { color: var(--mt-green); }
.kpi-amount.amber { color: var(--mt-amber); }
.kpi-amount.red   { color: var(--mt-red); }
.kpi-amount.muted { color: var(--mt-text2); }
.kpi-refresh {
    margin-left: 6px;
    align-self: center;
    color: rgba(var(--mt-ink-rgb), 0.35);
    transition: color .15s;
}
.kpi-refresh:hover { color: var(--mt-blue); }

/* ---- caption ---- */
.kpi-caption {
    font-family: var(--mt-font);
    font-size: 13px;
    font-weight: 500;
    color: rgba(var(--mt-ink-rgb), 0.6);
    margin-top: 9px;
}
.kpi-caption b { color: var(--mt-midnight); font-weight: 700; }

/* ---- separator + footer ---- */
.kpi-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 16px;
    padding-top: 13px;
    border-top: 1px solid rgba(var(--mt-ink-rgb), 0.08);
}
.kpi-link {
    font-family: var(--mt-font);
    font-size: 13px;
    font-weight: 600;
    color: rgba(var(--mt-ink-rgb), 0.6);
    text-decoration: none;
    transition: color .15s;
}
.kpi-card:hover .kpi-link { color: var(--mt-blue); }

/* ---- arrow button (bottom-right corner) ---- */
.kpi-arrow {
    width: 28px;
    height: 28px;
    border-radius: 9px;
    border: 1px solid rgba(var(--mt-ink-rgb), 0.08);
    background: var(--mt-surface);
    display: grid;
    place-items: center;
    color: var(--mt-midnight);
    flex-shrink: 0;
    transition: background .15s, color .15s, border-color .15s;
}
.kpi-card:hover .kpi-arrow {
    background: var(--mt-blue);
    color: #ffffff;
    border-color: var(--mt-blue);
}

/* ---- inline stat: a lone figure in a header band instead of a stranded tile ---- */
.inline-stat { display: flex; flex-direction: column; gap: 4px; }
.inline-stat-label { font-family: var(--mt-font); font-size: 13px; font-weight: 600; color: var(--mt-text2); }
.inline-stat-row { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.inline-stat-caption { font-family: var(--mt-font); font-size: 13px; font-weight: 500; color: rgba(var(--mt-ink-rgb), 0.6); }
/* contained variant: where the inline stat would otherwise read as floating text, the same
   compact layout wrapped in a hairline border with light padding, no footer */
.inline-stat.inline-stat-contained {
    background: var(--mt-surface);
    border: 1px solid var(--mt-border);
    border-radius: var(--mt-radius);
    padding: 10px 16px;
}

/* ---- Profit tile before/after tax toggle (top-right of the header) ---- */
.kpi-toggle {
    display: inline-flex;
    padding: 2px;
    border-radius: 999px;
    background: rgba(var(--mt-ink-rgb), 0.06);
    flex-shrink: 0;
}
.kpi-toggle button {
    font-family: var(--mt-font);
    font-size: 11px;
    font-weight: 600;
    line-height: 1;
    white-space: nowrap;
    padding: 5px 9px;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: rgba(var(--mt-ink-rgb), 0.6);
    cursor: pointer;
    transition: background .15s, color .15s, box-shadow .15s;
}
.kpi-toggle button:hover { color: var(--mt-midnight); }
.kpi-toggle button.active {
    background: var(--mt-surface);
    color: var(--mt-midnight);
    box-shadow: 0 1px 3px rgba(var(--mt-shadow-rgb), 0.15);
}

/* ---- Notice (Components/Shared/Notice.razor, Views/Shared/Component/Notice.cshtml) ----
   White, hairline border, 10px radius, 4px left bar. Three tiers: blue for information tied to
   an action, amber for opportunities and cautions with a money implication, red only when the
   user is blocked or something is genuinely wrong AND unusual. No icon. Two sentences at most. */
.notice-box {
    background: var(--mt-surface);
    border: 1px solid var(--mt-border);
    border-left: 4px solid var(--mt-blue);
    border-radius: var(--mt-radius);
    padding: 14px 18px;
    font-family: var(--mt-font);
    font-size: 14px;
    font-weight: 500;
    line-height: 1.5;
    color: var(--mt-midnight);
}
.notice-box.notice-box-caution { border-left-color: var(--mt-amber); }
.notice-box.notice-box-blocked { border-left-color: var(--mt-red); }
.notice-box-title { font-weight: 700; margin-bottom: 2px; }
.notice-box a:not(.btn) { color: var(--mt-blue); font-weight: 600; }
.notice-box a:not(.btn):hover { color: var(--mt-blue); text-decoration: underline; }

/* legacy: alert-* boxes and the blue "notice" disclaimer panels render the notice look until
   each view is swept onto the component */
.desktop-ui .alert:is(.alert-primary, .alert-info, .alert-success, .alert-warning, .alert-danger, .alert-light-primary, .alert-light-warning, .alert-secondary),
.desktop-ui .notice:is(.bg-light-primary, .bg-light-warning, .bg-light-info, .bg-light-success, .bg-light-danger) {
    background: var(--mt-surface) !important;
    border: 1px solid var(--mt-border) !important;
    border-left: 4px solid var(--mt-blue) !important;
    border-radius: var(--mt-radius) !important;
    color: var(--mt-midnight) !important;
}
.desktop-ui .alert.alert-warning,
.desktop-ui .notice.bg-light-warning {
    border-left-color: var(--mt-amber) !important;
}
.desktop-ui .alert.alert-danger,
.desktop-ui .notice.bg-light-danger {
    border-left-color: var(--mt-red) !important;
}
.desktop-ui .alert > i.bi,
.desktop-ui .alert > i.ki-duotone,
.desktop-ui .alert > i.fas,
.desktop-ui .alert > svg,
.desktop-ui .alert > span.svg-icon,
.desktop-ui .notice > i,
.desktop-ui .notice > span.svg-icon { display: none; }
.desktop-ui .alert a:not(.btn), .desktop-ui .notice a:not(.btn) { color: var(--mt-blue); }
.desktop-ui .alert .text-warning, .desktop-ui .alert .text-danger,
.desktop-ui .alert .text-primary, .desktop-ui .alert .text-success { color: var(--mt-midnight) !important; }

/* ---- Footnote (Components/Shared/Footnote.razor) ----
   Legal disclaimers and liability footnotes are not notices: they apply every time. Muted, one
   size down from body, under a hairline rule at the foot of the content, width capped for
   readability. No coloured bar, no card, no box. */
.footnote {
    margin-top: var(--mt-section);
    padding-top: 16px;
    border-top: 1px solid var(--mt-border);
    color: var(--mt-text2);
    font-size: 12px;
    line-height: 1.55;
}
.footnote > * { max-width: 72ch; }
.footnote p { margin-bottom: 8px; }
.footnote > :last-child { margin-bottom: 0; }
.footnote a:not(.btn) { color: var(--mt-blue); }

/* ---- Row action icon (Components/Shared/RowActionIcon.razor) ----
   The pencil and bin at the end of a row. The colours come from text-gray-600 and
   text-hover-primary / text-hover-danger, the same utilities the Bookkeeping split rows use;
   this only strips the button chrome and gives keyboard focus a visible ring. */
.row-action {
    background: none;
    border: 0;
    padding: 0;
    line-height: 1;
    border-radius: 4px;
    transition: color .15s ease;
}
.row-action:focus-visible { outline: 2px solid var(--mt-blue); outline-offset: 2px; }
.row-action:disabled { opacity: .5; pointer-events: none; }

/* ---- row separators: solid hairlines only ---- */
.desktop-ui .border-dashed { border-style: solid !important; border-color: var(--mt-border) !important; }
.desktop-ui .border-bottom-dashed { border-bottom-style: solid !important; border-bottom-color: var(--mt-border) !important; }
.desktop-ui .border-top-dashed { border-top-style: solid !important; border-top-color: var(--mt-border) !important; }
.desktop-ui .table.table-row-dashed > :not(caption) > * > * {
    border-bottom-style: solid;
    border-bottom-color: var(--mt-border);
}
.desktop-ui .separator.separator-dashed { border-bottom-style: solid; border-bottom-color: var(--mt-border); }

/* ---- styled select: every form-select on desktop, including the small and solid variants ---- */
.desktop-ui .form-select {
    border: 1px solid var(--mt-border);
    border-radius: 8px;
    background-color: var(--mt-surface);
    color: var(--mt-midnight);
    box-shadow: none;
    padding-right: 36px;
    background-image: var(--mt-select-chevron);
    background-repeat: no-repeat;
    background-position: right 12px center;
    background-size: 14px 14px;
}
.desktop-ui .form-select.form-select-solid { background-color: var(--mt-surface); }
.desktop-ui .form-select:focus {
    border-color: var(--mt-blue);
    box-shadow: 0 0 0 3px rgba(18,56,255,0.15);
}
.desktop-ui .form-select:disabled { color: var(--mt-text2); background-color: var(--mt-surface-2); }

/* ---- action menus: anchored to their trigger, at most 260px wide ---- */
.desktop-ui .menu-sub-dropdown {
    max-width: 260px;
    border-radius: var(--mt-radius);
    box-shadow: 0 10px 30px -10px rgba(var(--mt-shadow-rgb), 0.25);
    border: 1px solid var(--mt-border);
}
.desktop-ui .menu-sub-dropdown .menu-link { white-space: normal; }
/* Multi-column mega-menus (BankAccounts/Edit, Admin Notifications) carry menu-mega and size
   themselves with w-300px w-md-700px; the 260px cap would squash their columns (T-553). */
.desktop-ui .menu-sub-dropdown.menu-mega { max-width: none; }
.action-menu .menu-item.action-menu-destructive {
    border-top: 1px solid var(--mt-border);
    margin-top: 4px;
    padding-top: 4px;
}

/* ---- Confirm button: row-level approve actions only, in tables where approving is the core
        repeated task (Bookkeeping). The page primary stays blue; never use green buttons
        anywhere else without it being specified. ---- */
.desktop-ui .btn.btn-confirm {
    background-color: var(--mt-green);
    border-color: var(--mt-green);
    color: #ffffff;
}
.desktop-ui .btn.btn-confirm:hover:not(:disabled),
.desktop-ui .btn.btn-confirm:focus:not(:disabled),
.desktop-ui .btn.btn-confirm:active:not(:disabled) {
    background-color: #12b048;
    border-color: #12b048;
    color: #ffffff;
}

/* ---- Destructive ghost: red text, no fill, for destructive actions that must not shout
        (footer "Set to previous period", bulk "Exclude selected") ---- */
.desktop-ui .btn.btn-ghost-danger {
    background-color: transparent;
    border-color: transparent;
    color: var(--mt-red);
}
.desktop-ui .btn.btn-ghost-danger:hover:not(:disabled),
.desktop-ui .btn.btn-ghost-danger:focus:not(:disabled),
.desktop-ui .btn.btn-ghost-danger:active:not(:disabled) {
    background-color: rgba(248,40,90,0.08);
    border-color: transparent;
    color: var(--mt-red);
}

/* ---- Bulk selection bar: soft blue, radius; count and "Clear selection" left, actions right ---- */
.bulk-select-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
    background: var(--mt-row);
    border-radius: var(--mt-radius);
    padding: 10px 16px;
    font-family: var(--mt-font);
}
.bulk-select-bar .bulk-select-count { font-weight: 700; color: var(--mt-midnight); }
.bulk-select-bar .bulk-select-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; flex-wrap: wrap; }

/* ---- How it works: one fixed size on every screen ---- */
.btn-how-it-works {
    height: 32px;
    padding: 0 12px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
    font-family: var(--mt-font);
    font-size: 13px;
    font-weight: 600;
    line-height: 1;
}
.btn-how-it-works .bi { font-size: 14px; }

/* ---- headers, pills: Manrope + Midnight inside the shared components only ---- */
.page-header-title, .page-banner-title { font-family: var(--mt-font); }
.page-header-title { color: var(--mt-midnight); }
.status-pill { font-family: var(--mt-font); }

/* ---- Wizard rail (Components/Shared/Wizard/WizardShell.razor) ----
   Metronic's stepper already styles current and completed; this adds the "pending" state a
   visited-but-incomplete step uses (amber), so no consumer needs inline hex. */
.stepper.stepper-pills .stepper-item .stepper-icon .stepper-pending {
    display: none;
}
.stepper.stepper-pills .stepper-item.pending .stepper-icon {
    background-color: var(--bs-warning-light, #fff8dd);
    border-color: var(--bs-warning-light, #fff8dd);
}
.stepper.stepper-pills .stepper-item.pending .stepper-icon .stepper-number,
.stepper.stepper-pills .stepper-item.pending .stepper-icon .stepper-check {
    display: none;
}
.stepper.stepper-pills .stepper-item.pending .stepper-icon .stepper-pending {
    display: inline;
    color: var(--bs-warning, #ffc700);
}

/* ---- File dropzone (Components/DevelopmentCase/DevelopmentCaseFilePicker.razor) ----
   The native file input is stretched invisibly over the panel, so a click anywhere opens the
   picker and a drop lands on the input itself and fires its change event: no JavaScript, and the
   same Blazor InputFile path as choosing files. Solid hairline, never dashed. */
.dc-dropzone {
    position: relative;
    border: 1px solid var(--mt-border);
    border-radius: 10px;
    padding: 16px;
    text-align: center;
    background: var(--mt-surface);
    transition: border-color .15s, background-color .15s;
}
.dc-dropzone:hover:not(.is-disabled),
.dc-dropzone.is-dragging {
    border-color: var(--mt-blue);
    background: var(--mt-row);
}
.dc-dropzone.is-disabled { opacity: .6; }
.dc-dropzone-input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
}
.dc-dropzone-input:disabled { cursor: default; }
.dc-dropzone-body { pointer-events: none; }

/* ---- Image lightbox (wwwroot/js/blazor-interop.js, mightyImageLightbox) ----
   Any <img> inside a [data-image-lightbox] container opens full screen. The overlay is appended
   to body once and toggled with the hidden attribute; it must sit above the Bootstrap modal
   (1055) that hosts the ticket detail. Same look in both themes: the backdrop is always dark. */
[data-image-lightbox] img { cursor: zoom-in; }
.mt-lightbox {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 56px 24px 24px;
    background: rgba(0, 0, 0, .88);
    cursor: zoom-out;
}
.mt-lightbox-image {
    max-width: 100%;
    max-height: calc(100vh - 96px);
    object-fit: contain;
    border-radius: 10px;
    background: var(--mt-surface);
    cursor: default;
}
.mt-lightbox-caption {
    margin-top: 12px;
    max-width: 100%;
    color: rgba(255, 255, 255, .8);
    font-size: 13px;
    text-align: center;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.mt-lightbox-close {
    position: absolute;
    top: 16px;
    right: 16px;
    width: 40px;
    height: 40px;
    border: 0;
    border-radius: 10px;
    background: rgba(255, 255, 255, .12);
    color: #fff;
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
}
.mt-lightbox-close:hover,
.mt-lightbox-close:focus-visible {
    background: rgba(255, 255, 255, .24);
    outline: none;
}
body.mt-lightbox-open { overflow: hidden; }

/* ---- Board card slots (Components/DevelopmentCase/DevelopmentCaseBoard.razor) ----
   Each card sits in a slot that owns the gap below it (padding, not the card's margin), so there
   is no un-owned space between cards for dragenter to miss. The insertion line is a pseudo-element
   drawn into the previous slot's gap, so toggling it never shifts layout (a border-top would move
   the cards under the pointer and make the hovered card flip). */
.dc-card-slot { position: relative; padding-bottom: .5rem; }
.dc-card-slot.dc-drop-before::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: -.35rem;
    height: 3px;
    border-radius: 2px;
    background: var(--mt-blue);
    pointer-events: none;
}
.dc-card-slot.dc-dragging > .card { opacity: .5; }

/* @-mention picker and pills (Tickets comment and description editors; ckeditor-blazor.js _attachMentions) */
.dc-mention-menu {
    position: fixed;
    z-index: 1100; /* above the Bootstrap modal (1055) that hosts the ticket detail */
    min-width: 220px;
    max-width: 320px;
    max-height: 260px;
    overflow-y: auto;
    margin: 0;
    padding: 6px 0;
    list-style: none;
    background: var(--mt-surface);
    border: 1px solid var(--mt-border);
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(var(--mt-shadow-rgb), 0.12);
    font-family: var(--mt-font);
    font-size: 13px;
    font-weight: 600;
    color: var(--mt-midnight);
}
.dc-mention-item {
    padding: 7px 14px;
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.dc-mention-item.is-active,
.dc-mention-item:hover { background: var(--mt-row); color: var(--mt-blue); }

.dc-comment-body a[href*="/Admin/DevelopmentCases?owner="],
.dc-description a[href*="/Admin/DevelopmentCases?owner="],
.dc-rte .ck-content a[href*="/Admin/DevelopmentCases?owner="] {
    display: inline-block;
    padding: 0 8px;
    border-radius: 999px;
    background: var(--mt-row);
    color: var(--mt-blue);
    font-weight: 600;
    text-decoration: none;
    line-height: 1.6;
}

/* ---- Sprint completion heatmap (Components/DevelopmentCase/SprintCalendar.razor) ----
   GitHub-contribution-style month grid: five shading buckets mixed from the blue token, a dot
   for sprint start / mid sprint review / end. Cells are hover targets for the day tooltip. */
.dc-heat-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(36px, 1fr));
    gap: 4px;
    max-width: 560px;
}
.dc-heat-dow {
    font-size: 11px;
    color: var(--mt-text2);
    text-align: center;
    padding-bottom: 2px;
}
.dc-heat {
    position: relative;
    border-radius: 6px;
    border: 1px solid var(--mt-border);
    aspect-ratio: 1;
    min-height: 36px;
}
.dc-heat-swatch { min-height: 14px; width: 14px; aspect-ratio: 1; display: inline-block; }
.dc-heat-0 { background: var(--mt-surface); }
.dc-heat-1 { background: color-mix(in srgb, var(--mt-blue) 15%, var(--mt-surface)); }
.dc-heat-2 { background: color-mix(in srgb, var(--mt-blue) 35%, var(--mt-surface)); }
.dc-heat-3 { background: color-mix(in srgb, var(--mt-blue) 60%, var(--mt-surface)); }
.dc-heat-4 { background: var(--mt-blue); }
.dc-heat-day {
    position: absolute;
    top: 2px;
    left: 5px;
    font-size: 10px;
    color: var(--mt-text2);
}
.dc-heat-3 .dc-heat-day, .dc-heat-4 .dc-heat-day { color: #fff; }
.dc-heat-flag {
    position: absolute;
    bottom: 3px;
    right: 3px;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--mt-midnight);
    border: 1px solid var(--mt-surface);
}
.dc-heat-swatch .dc-heat-flag { bottom: 1px; right: 1px; }

/* ---- Ticket history rail (Components/DevelopmentCase/DevelopmentCaseDetail.razor) ----
   Each entry is an icon column beside the text; a hairline runs between entries so the list
   reads as a timeline. Solid hairline, never dashed. */
.dc-timeline li {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    position: relative;
    padding: 6px 0 6px 2px;
}
.dc-timeline li + li { border-top: 1px solid var(--mt-border); }
.dc-timeline li > i { margin-top: 3px; }

/* ---- Ticket notifications (Components/DevelopmentCase/DevelopmentCaseNotifications.razor) ----
   One row per notification, hairline between rows. Unread rows carry the current-row tint, a blue
   dot and a bold headline; read rows stay in the list on the plain surface. */
.dc-notification-list {
    border: 1px solid var(--mt-border);
    border-radius: var(--mt-radius);
    overflow: hidden;
    background: var(--mt-surface);
}
.dc-notification {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    width: 100%;
    padding: 14px 18px;
    border: 0;
    background: transparent;
    text-align: left;
    color: var(--mt-midnight);
    text-decoration: none;
}
.dc-notification:focus-visible { outline: 2px solid var(--mt-blue); outline-offset: -2px; }
.dc-notification + .dc-notification { border-top: 1px solid var(--mt-border); }
.dc-notification:hover { background: var(--mt-surface-2); color: var(--mt-midnight); }
.dc-notification.is-unread { background: var(--mt-row); }
.dc-notification.is-unread:hover { background: color-mix(in srgb, var(--mt-blue) 12%, var(--mt-surface)); }
.dc-notification-dot {
    flex: 0 0 8px;
    width: 8px;
    height: 8px;
    margin-top: 7px;
    border-radius: 50%;
    background: transparent;
}
.dc-notification.is-unread .dc-notification-dot { background: var(--mt-blue); }
.dc-notification-headline { font-weight: 500; }
.dc-notification.is-unread .dc-notification-headline { font-weight: 700; }
.dc-notification-ticket { color: var(--mt-text2); }
.dc-notification-snippet {
    color: var(--mt-text2);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    white-space: pre-line;
    word-break: break-word;
}
.dc-notification-time { color: var(--mt-text2); white-space: nowrap; }

/* Unread count on the top-bar bell (TopBarTicketNotificationsButton.razor). */
.mt-nav-count {
    position: absolute;
    top: 0;
    right: -2px;
    min-width: 17px;
    height: 17px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--mt-red);
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    line-height: 17px;
    text-align: center;
    pointer-events: none;
}

/* ---- Import progress modal (Views/Shared/Component/ImportProgressModal.cshtml) ----
   The blocking modal every bulk import screen shares. Only the bar needs a height the Bootstrap
   default does not give it; everything else is utilities and tokens, so it follows the theme. */
.import-progress-bar-track { height: 22px; }

/* ---- Holiday calendar (Components/Holidays/HolidayCalendar.razor) ----
   Each person's events carry their own Accountant.Colour as FullCalendar data; these classes only
   say what KIND of time off a block is, so they must read on any colour. Kinds are told apart by
   stripe direction, inset bars and opacity, never a dashed border. The legend swatches reuse the
   same classes over the brand blue. */
.fc .holiday-pending {
    border-style: solid !important;
    border-width: 1px !important;
    opacity: .65;
}
.fc .holiday-unpaid,
.holiday-legend-swatch.holiday-unpaid {
    background-image: repeating-linear-gradient(45deg, rgba(255, 255, 255, .45) 0 3px, transparent 3px 7px);
}
.fc .holiday-pending.holiday-unpaid {
    background-image: repeating-linear-gradient(45deg, rgba(var(--mt-ink-rgb), .18) 0 3px, transparent 3px 7px);
}
.fc .holiday-sick,
.holiday-legend-swatch.holiday-sick {
    box-shadow: inset 4px 0 0 rgba(var(--mt-ink-rgb), .55);
}
.fc .holiday-sick-beyond,
.holiday-legend-swatch.holiday-sick-beyond {
    box-shadow: inset 4px 0 0 rgba(var(--mt-ink-rgb), .55);
    background-image: repeating-linear-gradient(135deg, rgba(255, 255, 255, .45) 0 3px, transparent 3px 7px);
}
.fc .holiday-study,
.holiday-legend-swatch.holiday-study {
    box-shadow: inset 0 -3px 0 rgba(var(--mt-ink-rgb), .4);
}
.fc .holiday-exam,
.holiday-legend-swatch.holiday-exam {
    box-shadow: inset 0 0 0 2px rgba(var(--mt-ink-rgb), .5);
}
.fc .holiday-absence,
.holiday-legend-swatch.holiday-absence {
    opacity: .45;
}
.holiday-legend-swatch {
    display: inline-block;
    width: 12px;
    height: 12px;
    border-radius: 3px;
    background-color: var(--mt-blue);
}
.holiday-legend-swatch.holiday-pending {
    background-color: transparent;
    border: 1px solid var(--mt-blue);
    opacity: .65;
}
.holiday-legend-swatch.holiday-nonworking {
    background-color: var(--mt-surface-2);
    border: 1px solid var(--mt-border);
}

/* ---- Holiday policy tab (MyHolidays.razor): the copy is CKEditor HTML, so headings need spacing ---- */
.holiday-policy h4 {
    margin-top: 20px;
    margin-bottom: 6px;
    font-size: 1rem;
    color: var(--mt-midnight);
}
.holiday-policy h4:first-child { margin-top: 0; }
.holiday-policy-section + .holiday-policy-section {
    margin-top: 32px;
    padding-top: 24px;
    border-top: 1px solid var(--mt-border);
}

/* ==== Dark theme ====================================================================
   Applies when <html data-bs-theme="dark"> (set by the boot script in _Layout.cshtml from the
   user's saved appearance, or by KTThemeMode on an Appearance click). Everything above reads
   tokens, so this block is the whole dark treatment for the shared components; the rest of
   the page (sidebar, header, cards, tables, inputs) comes from Metronic's own dark rules in
   style.bundle.css. Surfaces sit on Metronic's dark scale: app bg #0F1014, body and card
   #15171C, gray-100 #1B1C22, gray-200 #26272F, header and sidebar #0D0E12. */

/* ---- XML preview (admin View XML modal) --------------------------------------
   Pretty-printed GovTalk XML coloured by XmlSyntaxHighlighter. Reads tokens so it
   works in both themes; the dark block below only lifts the hue lightness. */
.xml-preview {
    background: var(--mt-surface-2);
    border: 1px solid var(--mt-border);
    border-radius: 10px;
    padding: 16px;
    max-height: 60vh;
    overflow: auto;
    white-space: pre;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.8rem;
    line-height: 1.45;
    color: var(--mt-midnight);
}
.xml-preview .xml-tag { color: var(--mt-xml-tag, #0550ae); }
.xml-preview .xml-attr { color: var(--mt-xml-attr, #116329); }
.xml-preview .xml-val { color: var(--mt-xml-val, #8250df); }
.xml-preview .xml-text { color: var(--mt-midnight); }
.xml-preview .xml-decl { color: var(--mt-text2); }
.xml-preview .xml-punc { color: var(--mt-text2); }

[data-bs-theme=dark] {
    --mt-xml-tag: #79C0FF;
    --mt-xml-attr: #7EE787;
    --mt-xml-val: #D2A8FF;
    --mt-midnight: #E6EDF5;                /* ink on dark; the token name is kept to avoid churn */
    --mt-text2: #8FA3B8;
    --mt-border: #26272F;
    --mt-row: #1B2238;
    --mt-surface: #15171C;
    --mt-surface-2: #1B1C22;
    --mt-ink-rgb: 230, 237, 245;
    --mt-shadow-rgb: 0, 0, 0;
    --mt-tint-blue: rgba(18, 56, 255, 0.18);
    --mt-tint-teal: rgba(47, 143, 143, 0.22);
    --mt-tint-icterine: rgba(122, 138, 31, 0.24);
    --mt-teal-ink: #8FD0D0;
    --mt-icterine-ink: #CBDA6E;
    --mt-select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%238FA3B8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");

    /* Brand restore. Metronic's dark theme swaps --bs-primary to #006AE6; Mighty's primary is
       #1238ff in both themes. These are the dark-block variables that reference the primary.
       Metronic's light block sets --bs-primary-rgb to 27,132,255 (see assets/css/custom.css);
       dark uses the true brand rgb deliberately, and --bs-primary-inverse stays white. */
    --bs-primary: #1238ff;
    --bs-primary-rgb: 18, 56, 255;
    --bs-primary-active: #0b2ed9;
    --bs-primary-light: #151C3A;
    --bs-primary-clarity: rgba(18, 56, 255, 0.2);
    --bs-text-primary: #1238ff;
    --bs-component-active-bg: #1238ff;
    --bs-component-hover-color: #1238ff;
    --bs-component-checked-bg: #1238ff;
    --bs-menu-link-color-hover: #1238ff;
    --bs-menu-link-color-show: #1238ff;
    --bs-menu-link-color-here: #1238ff;
    --bs-menu-link-color-active: #1238ff;
}

/* bg-white is used as "card surface" across the views; in dark it follows the body. Do not
   re-declare --bs-white-rgb: it also feeds .text-white and .border-white, which must stay white. */
[data-bs-theme=dark] .bg-white { background-color: var(--bs-body-bg) !important; }

/* assets/css/custom.css light-only rules, restated for dark here so all dark rules live in one
   sheet. Same selectors at higher specificity, and this sheet loads later. */
[data-bs-theme=dark] .menu-link.active { background-color: rgba(18, 56, 255, 0.18); }
[data-bs-theme=dark] .alert-warning { background-color: var(--bs-warning-light) !important; }
[data-bs-theme=dark] .job-task-panel {
    background: var(--bs-body-bg);
    box-shadow: -10px 0 30px rgba(0, 0, 0, 0.5);
}
[data-bs-theme=dark] .job-task-panel .modal-header,
[data-bs-theme=dark] .job-task-panel .modal-footer { border-color: var(--bs-border-color); }
[data-bs-theme=dark] .btn-close:hover { background-color: rgba(255, 255, 255, 0.12); }

/* the light hover border (blue at .25) is too faint against a dark card */
[data-bs-theme=dark] .kpi-card:hover { border-color: rgba(18, 56, 255, 0.45); }
[data-bs-theme=dark] .kpi-card.kpi-static:hover { border-color: rgba(var(--mt-ink-rgb), 0.08); }
