/* ─────────────────────────────────────────────
   StewardPortal — overrides on top of Metronic v9.4.13
   ───────────────────────────────────────────── */

/* ── Login page background — Union Blueprint ── */
.sp-login-bg {
    position: fixed;
    inset: 0;
    z-index: 0;
    overflow: hidden;
    background: #0d1f3c;
}

/* Blueprint grid */
.sp-login-bg::before {
    content: '';
    position: absolute;
    inset: 0;
    background-image:
            linear-gradient(rgba(27,132,255,.10) 1px, transparent 1px),
            linear-gradient(90deg, rgba(27,132,255,.10) 1px, transparent 1px);
    background-size: 80px 80px;
}

/* Radial glows — top-right accent + bottom-left warmth */
.sp-login-bg::after {
    content: '';
    position: absolute;
    inset: 0;
    background:
            radial-gradient(ellipse 500px 500px at 90% 10%,  rgba(27,132,255,.18) 0%, transparent 70%),
            radial-gradient(ellipse 400px 400px at 10% 90%,  rgba(5,110,233,.14)  0%, transparent 70%),
            linear-gradient(160deg, rgba(13,31,60,.0) 0%, rgba(6,16,32,.55) 100%);
}

/* Accent cross-hair lines — reference lines like a real blueprint */
.sp-login-panel {
    position: absolute;
    pointer-events: none;
}
.sp-login-panel-1 {
    top: 0; bottom: 0; left: 50%;
    width: 1px;
    background: linear-gradient(to bottom,
    transparent 0%,
    rgba(27,132,255,.18) 30%,
    rgba(27,132,255,.18) 70%,
    transparent 100%);
}
.sp-login-panel-2 {
    left: 0; right: 0; top: 50%;
    height: 1px;
    background: linear-gradient(to right,
    transparent 0%,
    rgba(27,132,255,.18) 30%,
    rgba(27,132,255,.18) 70%,
    transparent 100%);
}
.sp-login-panel-3 {
    top: 50%; left: 50%;
    width: 80px; height: 80px;
    margin-top: -40px; margin-left: -40px;
    border-radius: 50%;
    border: 1px solid rgba(27,132,255,.20);
    box-shadow: 0 0 0 40px rgba(27,132,255,.04),
    0 0 0 80px rgba(27,132,255,.02);
}

/* Corner tick marks */
.sp-login-glow {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background-image:
        /* top-left corner */
            linear-gradient(rgba(27,132,255,.25) 0%, rgba(27,132,255,.25) 100%),
            linear-gradient(rgba(27,132,255,.25) 0%, rgba(27,132,255,.25) 100%),
                /* top-right corner */
            linear-gradient(rgba(27,132,255,.25) 0%, rgba(27,132,255,.25) 100%),
            linear-gradient(rgba(27,132,255,.25) 0%, rgba(27,132,255,.25) 100%),
                /* bottom-left corner */
            linear-gradient(rgba(27,132,255,.25) 0%, rgba(27,132,255,.25) 100%),
            linear-gradient(rgba(27,132,255,.25) 0%, rgba(27,132,255,.25) 100%),
                /* bottom-right corner */
            linear-gradient(rgba(27,132,255,.25) 0%, rgba(27,132,255,.25) 100%),
            linear-gradient(rgba(27,132,255,.25) 0%, rgba(27,132,255,.25) 100%);
    background-size:   24px 1px, 1px 24px,  24px 1px, 1px 24px,  24px 1px, 1px 24px,  24px 1px, 1px 24px;
    background-position:
            24px 24px,  24px 24px,
            calc(100% - 24px) 24px,  calc(100% - 24px) 24px,
            24px calc(100% - 24px),  24px calc(100% - 24px),
            calc(100% - 24px) calc(100% - 24px),  calc(100% - 24px) calc(100% - 24px);
    background-repeat: no-repeat;
}

.sp-login-wrap {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    padding: 1.5rem;
}

/* ── Font: system UI — SF Pro on iOS/macOS, Segoe UI on Windows ── */
html, body, .kt-card, .kt-table, input, button, select, textarea {
    font-family: ui-sans-serif, system-ui, sans-serif,
    "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}

/* ── Page container ────────────────────────────
   Metronic caps .kt-container-fixed at --breakpoint-xl (1280px) with 30px of
   inline padding, but .sp-header-inner and .sp-footer-inner have always been
   1400px with 40px — so the brand mark sat ~50px left of every card edge.
   Matching all three lines the page up and, on the Group grid, gives a full
   week of columns the room to render without wrapping worker names. */
.kt-container-fixed {
    max-width: 1400px;
    padding-inline: 2.5rem;
}

/* ── Section cards ─────────────────────────────
   Opt-in, for a card that is one of SEVERAL stacked lists on a page — the four
   worker tables on a group or a request. The dark bar earns its place there by
   separating the lists; on a page with a single card it is decoration, so those
   keep the plain header.

   Named for the reason rather than the appearance, so the styling can change
   without the class name becoming a lie. Add it in the view: CSS cannot tell
   "this page stacks several tables", and a page should say what it is rather
   than have it inferred.

   A header holding a search box or buttons (.sp-toolbar) is handled: those
   controls get their own dark-ground treatment further down, since every one of
   their defaults is tuned for a white card. */
:root {
    --sp-section-head-bg: #3f4254;

    /* The portal's amber, and the only ink that is readable on it. Kept here
       rather than in a page stylesheet because the group grid's today marker
       uses the same pair — two copies would drift the first time one is tuned. */
    --sp-amber: #f6b100;
    --sp-amber-ink: #071437;
}

html.dark {
    /* Deeper than the light-mode grey: the card is already dark here, so the
       lighter value would glow off the page rather than sit on it. */
    --sp-section-head-bg: #2b303b;
}

/* Stacked sections need more air between them than the app-wide card gap gives,
   and that belongs to the same idea as the dark bar — a card that is one of
   several — so it rides on the same class rather than a second one. */
.sp-section-card {
    margin-bottom: 2rem;
}

.sp-section-card > .kt-card-header {
    background: var(--sp-section-head-bg);
    border-bottom-color: transparent;
    /* .kt-card is rounded but does not clip its children, so a filled header has
       to round its own top corners or it paints square over them. */
    border-start-start-radius: calc(var(--radius) + 4px);
    border-start-end-radius: calc(var(--radius) + 4px);
}

.sp-section-card > .kt-card-header .kt-card-title {
    color: #fff;
}

/* Whitening the title also swallowed the status icon inside it: this rule is
   unlayered, so it beats Metronic's layered .text-* utilities whatever their
   specificity. The semantic colours are restated, lightened for the dark ground —
   the light-mode danger red sits at roughly 2.3:1 on this grey, which is why the
   red sections looked flat even before this. */
.sp-section-card > .kt-card-header .kt-card-title .text-success { color: #3ddc84; }
.sp-section-card > .kt-card-header .kt-card-title .text-warning { color: var(--sp-amber); }
.sp-section-card > .kt-card-header .kt-card-title .text-danger  { color: #ff6b85; }
.sp-section-card > .kt-card-header .kt-card-title .text-primary { color: #6ab0ff; }

/* Cancelled sections. --muted-foreground is a grey for white cards and is nearly
   invisible on this bar, so it is restated light enough to read while staying
   clearly quieter than the three status colours above — which is the point of it. */
.sp-section-card > .kt-card-header .kt-card-title .text-muted-foreground { color: rgb(255 255 255 / .55); }

/* The count badge is a pale chip meant for a white card — near-invisible here. */
.sp-section-card > .kt-card-header .kt-badge {
    background: rgb(255 255 255 / .16);
    border-color: transparent;
    color: #fff;
}

/* ── Toolbar controls on a section header ──────
   Search box and buttons live inside these headers on Request Detail. Every one
   of their defaults assumes a white card — a light-grey field, near-black text,
   a muted-grey icon — so each is restated against the dark ground. Values are
   translucent white rather than fixed hexes so the one set works on both the
   light and dark theme values of --sp-section-head-bg. */
.sp-section-card > .kt-card-header .sp-search input {
    background: rgb(255 255 255 / .10);
    border-color: rgb(255 255 255 / .18);
    color: #fff;
}

.sp-section-card > .kt-card-header .sp-search input::placeholder {
    color: rgb(255 255 255 / .55);
}

.sp-section-card > .kt-card-header .sp-search input:focus {
    background: rgb(255 255 255 / .16);
    /* White rather than --primary: the brand blue sits too close to this grey in
       value, so a blue focus ring would all but vanish exactly when it matters.
       The field also has outline:none, making this border the only focus cue. */
    border-color: rgb(255 255 255 / .6);
}

.sp-section-card > .kt-card-header .sp-search i {
    color: rgb(255 255 255 / .55);
}

/* Amber outline that fills on hover. White-on-grey read as another piece of the
   bar; amber is the only thing in this header that is neither the surface nor the
   type, so the one control on it is unmistakably a control. */
.sp-section-card > .kt-card-header .kt-btn-outline {
    background: transparent;
    border-color: var(--sp-amber);
    color: var(--sp-amber);
    transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}

.sp-section-card > .kt-card-header .kt-btn-outline:hover,
.sp-section-card > .kt-card-header .kt-btn-outline:focus-visible {
    background: var(--sp-amber);
    border-color: var(--sp-amber);
    /* Dark ink on the fill, never white: white on this amber is about 1.9:1 and
       unreadable. Same reason the group grid's today disc uses it. */
    color: var(--sp-amber-ink);
}

/* Metronic tints button icons separately, which would leave this one a mid-grey
   against an amber label and then against dark ink on hover. */
.sp-section-card > .kt-card-header .kt-btn-outline i {
    color: inherit;
}

@media (prefers-reduced-motion: reduce) {
    .sp-section-card > .kt-card-header .kt-btn-outline { transition: none; }
}

/* ── Screen-reader-only text ───────────────────
   Removed from the visual layout but still read aloud. Use this instead of
   deleting a label that is obvious by sight — an empty <th> leaves a screen
   reader announcing an unnamed column. Neither Metronic nor Tailwind ships an
   .sr-only in this precompiled bundle, hence the local one. */
.sp-sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* ── Top-nav header ────────────────────────── */
.sp-header {
    position: sticky;
    top: 0;
    z-index: 100;
    display: flex;
    align-items: stretch;
    height: auto;
    background: var(--card);
    border-bottom: 1px solid rgba(27,132,255,.22);
    box-shadow: 0 1px 4px rgba(0,0,0,.06);
}

.sp-header-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    max-width: 1400px;
    margin-inline: auto;
    padding: 0.75rem 2.5rem;
    gap: 2rem;
}

/* Dark mode — amber glow on header bottom */
html.dark .sp-header {
    border-bottom: 1px solid rgba(246,177,0,.65);
    box-shadow: 0 0 14px rgba(246,177,0,.22), 0 0 40px rgba(246,177,0,.08);
}

/* Brand */
.sp-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    flex-shrink: 0;
}

.sp-brand img {
    border-radius: 50%;
    object-fit: cover;
}

.sp-brand-name {
    display: flex;
    align-items: center;
    gap: 6px;
    font-family: 'Oswald', ui-sans-serif, system-ui, sans-serif;
    font-size: 17px;
    font-weight: 500;
    letter-spacing: .06em;
    text-transform: uppercase;
    line-height: 1;
}

.sp-brand-word-plain {
    color: var(--foreground);
}

.sp-brand-word-box {
    color: #fff;
    background: var(--primary);
    padding: 3px 7px 4px;
    border-radius: 3px;
}

/* Nav links */
.sp-nav {
    display: flex;
    align-items: center;
    gap: 2px;
    margin-left: 1.5rem;
}

.sp-nav-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    font-size: 13.5px;
    font-weight: 500;
    color: var(--foreground);
    border-radius: 6px;
    text-decoration: none;
    transition: background .15s, color .15s;
    opacity: .65;
}

.sp-nav-link:hover {
    opacity: 1;
    color: var(--foreground);
    background: rgba(27,132,255,.08);
}

.sp-nav-link.active {
    opacity: 1;
    color: #1b84ff;
    background: rgba(27,132,255,.12);
    font-weight: 600;
}

html.dark .sp-nav-link.active {
    color: #7cb9ff;
    background: rgba(27,132,255,.20);
}

html.dark .sp-nav-link:hover {
    background: rgba(27,132,255,.15);
    color: #fff;
}

/* User area */
.sp-user-area {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-left: auto;
}

/* .sp-avatar is just a sizing host for the tooltip trigger — it must NOT clip
   (overflow:hidden), or the hover-preview tooltip (a descendant) would be clipped
   along with it. The circular photo crop lives on .sp-avatar-circle instead. */
.sp-avatar {
    position: relative;
    width: 36px;
    height: 36px;
    aspect-ratio: 1 / 1;
    font-weight: 600;
    font-size: 13px;
    flex-shrink: 0;
}

.sp-avatar-circle {
    position: absolute;
    inset: 0;
    overflow: hidden;
    border-radius: 50%;
    background: rgba(27,132,255,.15);
    color: var(--primary);
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid rgba(27,132,255,.30);
    /* content-box so the size above describes the PHOTO, not the photo minus the ring.
       Under the global border-box, a 1px border ate 2px out of every avatar. */
    box-sizing: content-box;
}

.sp-avatar-circle img {
    display: none;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

html.dark .sp-avatar-circle {
    background: rgba(27,132,255,.25);
    color: #7cb9ff;
    border-color: rgba(27,132,255,.45);
}

.sp-avatar-lg {
    width: 44px;
    height: 44px;
    font-size: 15px;
}

.sp-avatar-sm {
    width: 32px;
    height: 32px;
    font-size: 12px;
}

/* Hover-to-enlarge preview — overrides the default dark KTUI tooltip bubble,
   since the photo itself is the content here, not a padded text bubble.
   No border here on purpose: Metronic's own .kt-tooltip already gives dark
   mode a border via ":is(.dark *)" (shadows barely read on a dark page), and
   leaves light mode border-free, relying on shadow instead (which barely
   registers on white, so we strengthen it below). Same standard elevation
   pattern Material/Fluent use — shadow-led in light, border-led in dark. */
.sp-avatar-preview {
    /* Fixed width, !important: KTTooltip appears to set its own inline width on
       this element when positioning it, which otherwise silently wins over any
       plain CSS rule here and renders a much wider, mostly-empty card. */
    width: 168px !important;
    position: relative;
    padding: 4px;
    background: var(--card);
    box-shadow: 0 8px 24px rgba(0,0,0,.18), 0 2px 6px rgba(0,0,0,.10);
}

/* Same spinner as the Files preview modal, just centred over the reserved photo
   space. Hidden automatically once the photo finishes loading — no extra JS
   needed, :has() already used elsewhere in this file (.kt-btn:has(...)).
   Positioning (translate) lives on this wrapper, NOT on .sp-spinner itself —
   .sp-spinner's own animation rotates via `transform`, and a second, static
   transform on the same element fights the animated one instead of combining
   with it, which is what produced the diagonal-drift glitch. */
.sp-avatar-spinner {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

.sp-avatar-spinner .sp-spinner {
    /* Gray instead of the shared spinner's primary-blue accent — a photo
       preview reads better with a neutral loading indicator than a bright one. */
    border-top-color: var(--muted-foreground);
}

.sp-avatar-preview:has(img.is-loaded) .sp-avatar-spinner {
    display: none;
}

/* A failed fetch has to end the spinner too, not just a successful one. Without
   this the card spins forever on a network error, or on a photo that exists as a
   thumbnail but fails at full size — and a spinner with nothing coming reads as a
   hung page. The message takes the image's exact footprint so the card does not
   resize as it swaps. */
.sp-avatar-preview.is-failed .sp-avatar-spinner,
.sp-avatar-preview.is-failed img {
    display: none;
}

.sp-avatar-preview.is-failed::after {
    content: "Photo unavailable";
    display: flex;
    align-items: center;
    justify-content: center;
    width: 160px;
    aspect-ratio: 3 / 4;
    font-size: 12px;
    color: var(--muted-foreground);
}

.sp-avatar-preview img {
    /* aspect-ratio reserves the final size immediately, before the image has even
       started loading, so the card doesn't pop up as a tiny collapsed square and
       then jump to full size once it loads — it fades in within its final space
       instead. 3:4 is the standard portrait headshot ratio; object-fit: cover
       crops any photo that doesn't match it rather than distorting it. */
    display: block;
    width: 160px;
    aspect-ratio: 3 / 4;
    object-fit: cover;
    border-radius: calc(var(--radius) - 4px);
    opacity: 0;
    transition: opacity .15s ease;
}

.sp-avatar-preview img.is-loaded {
    opacity: 1;
}

/* Touch devices open the preview downward: near the top of a scrolling table
   there's no room above, and tap is the only way it opens there.
   This has to be a CSS custom property rather than data-kt-tooltip-placement:
   KTTooltip reads that attribute once, when it initialises (which happens in
   ktui.min.js, before our JS runs), but it re-reads --kt-tooltip-placement on
   every open — so this is the one hook that can vary placement per device. */
@media (hover: none) {
    .sp-avatar[data-kt-tooltip] { --kt-tooltip-placement: bottom; }
}

/* A worker with no photo shouldn't hover into an empty padded card — hide the
   preview entirely, overriding KTTooltip's own .show toggle. */
.sp-avatar-no-photo .sp-avatar-preview {
    display: none !important;
}

.sp-username {
    font-size: 13px;
    font-weight: 500;
    color: var(--foreground);
    display: none;
}

@media (min-width: 1024px) {
    .sp-username { display: inline; }
}

/* Mobile nav toggle */
.sp-nav-toggle {
    display: none;
    background: none;
    border: none;
    padding: 6px;
    cursor: pointer;
    color: var(--muted-foreground);
    font-size: 22px;
}

@media (max-width: 767px) {
    .sp-nav { display: none; }
    .sp-nav.open {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        position: absolute;
        top: 60px;
        left: 0;
        right: 0;
        background: var(--card);
        border-bottom: 1px solid var(--border);
        padding: 10px 1rem;
        z-index: 99;
        gap: 2px;
    }
    .sp-nav-toggle { display: flex; align-items: center; }
    .sp-header-inner { flex-wrap: wrap; height: auto; padding: 1rem; }
    .sp-page { padding: 1.25rem 0 1.5rem; }
    .sp-brand-name { font-size: 13px; }
    .sp-col-mobile-hide { display: none; }
}

/* ── User dropdown ──────────────────────────── */
.sp-user-menu { position: relative; }

.sp-user-btn {
    display: inline-flex;
    align-items: center;
    background: none;
    border: none;
    border-radius: 50%;
    padding: 2px;
    cursor: pointer;
    transition: opacity .15s;
}

.sp-user-btn:hover { opacity: .8; }


.sp-chevron {
    font-size: 11px;
    color: var(--muted-foreground);
    transition: transform .2s;
}

#sp-user-btn[aria-expanded="true"] .sp-chevron { transform: rotate(180deg); }

.sp-dropdown {
    display: none;
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    min-width: 220px;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: 0 8px 24px rgba(0,0,0,.10);
    z-index: 200;
    overflow: hidden;
    animation: sp-dd-in .15s ease;
}

.sp-dropdown.open { display: block; }

@keyframes sp-dd-in {
    from { opacity: 0; transform: translateY(-6px); }
    to   { opacity: 1; transform: translateY(0); }
}

.sp-dropdown-header {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 16px;
}

.sp-dropdown-name {
    font-size: 13.5px;
    font-weight: 600;
    color: var(--mono);
    line-height: 1.2;
}

.sp-dropdown-sub {
    font-size: 12px;
    color: var(--muted-foreground);
    margin-top: 2px;
}

.sp-dropdown-divider {
    height: 1px;
    background: var(--border);
}

.sp-dropdown-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 10px 16px;
    font-size: 13.5px;
    font-weight: 500;
    color: var(--foreground);
    background: none;
    border: none;
    cursor: pointer;
    text-align: left;
    text-decoration: none;
    transition: background .12s;
}

.sp-dropdown-item:hover { background: var(--muted); }

.sp-dropdown-signout { color: var(--destructive); }
.sp-dropdown-signout:hover { background: color-mix(in oklab, var(--destructive) 8%, transparent); }

/* Dark mode toggle pill */
.sp-toggle-pill {
    display: inline-block;
    width: 32px;
    height: 18px;
    border-radius: 9px;
    background: var(--input);
    position: relative;
    transition: background .2s;
    flex-shrink: 0;
}

.sp-toggle-pill::after {
    content: '';
    position: absolute;
    left: 3px;
    top: 3px;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: var(--card);
    transition: left .2s;
}

.sp-toggle-pill.on { background: var(--primary); }
.sp-toggle-pill.on::after { left: 17px; }

/* ── Page layout ───────────────────────────── */
.sp-page {
    padding: 1.75rem 0 2rem;
}

/* ── Page title ────────────────────────────── */
.sp-page-title {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 1.5rem;
}

.sp-page-title h1 {
    font-size: 20px;
    font-weight: 700;
    color: var(--mono);
    margin-bottom: 4px;
    line-height: 1;
}

.sp-breadcrumb {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12.5px;
    color: var(--muted-foreground);
}

.sp-breadcrumb a { color: var(--muted-foreground); text-decoration: none; }
.sp-breadcrumb a:hover { color: var(--primary); }
.sp-breadcrumb-sep { font-size: 10px; }


/* ── Search box ────────────────────────────── */
.sp-search {
    position: relative;
    display: inline-flex;
    align-items: center;
}

.sp-search i {
    position: absolute;
    left: 11px;
    color: var(--muted-foreground);
    font-size: 15px;
    pointer-events: none;
}

.sp-search input {
    padding: 7px 12px 7px 34px;
    border: 1px solid var(--border);
    border-radius: 8px;
    font-size: 13px;
    color: var(--foreground);
    background: var(--muted);
    min-width: 210px;
    outline: none;
    transition: border-color .15s, background .15s;
}

.sp-search input:focus {
    border-color: var(--primary);
    background: var(--card);
}

/* ── Card toolbar (search + export + add, etc.) ──────────────── */
.sp-toolbar {
    display: flex;
    align-items: center;
    gap: .6rem;
}

@media (max-width: 560px) {
    .sp-toolbar {
        flex-direction: column;
        align-items: stretch;
        gap: .65rem;
        width: 100%;
    }
}

/* ── Table loading overlay ──────────────────── */
.sp-table-loader {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: color-mix(in oklab, var(--card) 80%, transparent);
    z-index: 10;
    border-radius: 0 0 12px 12px;
    min-height: 120px;
}

.sp-table-loader-inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
}

.sp-spinner {
    /* Explicit display so the class works on a <span> too — width/height are
       ignored on an inline element, which collapses the ring to a bare sliver. */
    display: block;
    width: 28px;
    height: 28px;
    border: 3px solid var(--border);
    border-top-color: var(--primary);
    border-radius: 50%;
    animation: sp-spin .7s linear infinite;
}

@keyframes sp-spin {
    to { transform: rotate(360deg); }
}

/* THE in-button loading spinner — every button that swaps to a busy state uses
   exactly this markup, so the look changes here and nowhere else:
       $btn.prop('disabled', true)
           .html('<span class="sp-btn-spinner"></span> Saving…');
   Sized in em and coloured from currentColor, so it fits any .kt-btn size and
   variant without per-page overrides. */
.sp-btn-spinner {
    width: 1em;
    height: 1em;
    border: 2px solid color-mix(in srgb, currentColor 30%, transparent);
    border-top-color: currentColor;
    border-radius: 50%;
    animation: sp-spin .7s linear infinite;
}

/* Metronic fades a disabled .kt-btn to 50%, which washes the ring out — lift
   any button currently showing the spinner back to a readable opacity. */
.kt-btn:has(.sp-btn-spinner) {
    opacity: .75;
    cursor: not-allowed;
}

/* Metronic's .kt-btn-secondary:hover sets background to the same var(--secondary) as its
   resting state, so hover is invisible; its base transition also skips background-color,
   so even a fix would snap instead of fade. color-mix keeps this correct in both themes. */
.kt-btn-secondary {
    transition-property: color, box-shadow, background-color;
}

.kt-btn-secondary:hover {
    background-color: color-mix(in oklab, var(--secondary) 85%, var(--foreground)) !important;
}

/* Coloured outline button variants — Metronic only ships kt-btn-outline in primary/destructive,
   these mirror that same formula for the portal's action colours: slate = Steward Reports,
   violet = Files, green = Excel/file downloads, teal = zip export.

   Icons deliberately take `color: inherit` rather than their own colour. Metronic transitions
   the button's `color` over ~150ms but sets `.kt-btn-outline i` from a separate, untransitioned
   rule — so a hard-coded hover colour made the icon snap to white while the background was still
   fading, leaving an invisible icon for the length of the transition. Inheriting keeps the icon
   locked to the button's own animated colour in every state. */
.kt-btn-outline.kt-btn-reports,
.kt-btn-outline.kt-btn-files,
.kt-btn-outline.kt-btn-download,
.kt-btn-outline.kt-btn-zip {
    /* Outline only at rest — no tinted fill. A 10% tint read as a washed-out, half-filled
       button and muddied the icon; letting the surface show through keeps the border and
       label crisp, and makes the solid hover fill land as an actual state change.
       Overrides Metronic's .kt-btn-outline, which fills with var(--background). */
    background-color: transparent;
    transition-property: color, box-shadow, background-color, border-color;
}

.kt-btn-outline.kt-btn-reports i,
.kt-btn-outline.kt-btn-reports svg,
.kt-btn-outline.kt-btn-files i,
.kt-btn-outline.kt-btn-files svg,
.kt-btn-outline.kt-btn-download i,
.kt-btn-outline.kt-btn-download svg,
.kt-btn-outline.kt-btn-zip i,
.kt-btn-outline.kt-btn-zip svg {
    color: inherit;
}

/* Slate — Steward Reports. Deliberately NOT the #f6b100 amber this variant started as: at
   1.87:1 against white that amber sat far under the 4.5:1 WCAG AA floor, so the label was
   unreadable both at rest and (as white-on-amber) on hover. Amber is fine as a filled pill
   (.sp-action-btn-group), just not as a text/border colour. Slate is neutral on purpose — it
   pairs with the violet Files button beside it instead of competing for the same attention,
   and at 10.3:1 it is the most legible option in the set. */
.kt-btn-outline.kt-btn-reports {
    border-color: #334155;
    color: #334155;
}

/* Violet — matches .sp-action-btn-files so the Files button reads as the same "thing"
   whether it's a page-title button or a table action pill. */
.kt-btn-outline.kt-btn-files {
    border-color: #7239ea;
    color: #7239ea;
}

/* Green — #217346 is Microsoft Excel's brand green, the colour users already read as
   "this gives me a spreadsheet". Every Excel/file download in the site uses this. */
.kt-btn-outline.kt-btn-download {
    border-color: #217346;
    color: #217346;
}

/* Teal — the zip export only. Deliberately NOT the Excel green: it hands back an archive of
   attachments, not a spreadsheet, and promising .xlsx there would be a lie. */
.kt-btn-outline.kt-btn-zip {
    border-color: #0d9488;
    color: #0d9488;
}

@media (hover: hover) {
    .kt-btn-outline.kt-btn-reports:hover {
        background-color: #334155;
        color: #fff;
    }

    .kt-btn-outline.kt-btn-files:hover {
        background-color: #7239ea;
        color: #fff;
    }

    .kt-btn-outline.kt-btn-download:hover {
        background-color: #217346;
        color: #fff;
    }

    .kt-btn-outline.kt-btn-zip:hover {
        background-color: #0d9488;
        color: #fff;
    }
}

/* ── Table action buttons ────────────────────── */
.sp-action-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 4px 10px;
    font-size: 12px;
    font-weight: 500;
    border-radius: 6px;
    border: 1px solid;
    background: var(--card);
    text-decoration: none;
    transition: background .15s, color .15s;
    white-space: nowrap;
}
.sp-action-btn i { font-size: 14px; }

/* Detail — blue */
.sp-action-btn-detail {
    border-color: var(--primary);
    color: var(--primary);
}
.sp-action-btn-detail:hover {
    background: var(--primary);
    color: #fff;
}

/* Workers — green */
.sp-action-btn-workers {
    border-color: #17c653;
    color: #17c653;
}
.sp-action-btn-workers:hover {
    background: #17c653;
    color: #fff;
}

/* Group — amber */
.sp-action-btn-group {
    border-color: #f6b100;
    color: #f6b100;
}
.sp-action-btn-group:hover {
    background: #f6b100;
    color: #fff;
}

/* Files — violet */
.sp-action-btn-files {
    border-color: #7239ea;
    color: #7239ea;
}
.sp-action-btn-files:hover {
    background: #7239ea;
    color: #fff;
}

/* Reports — slate. Same #334155 as .kt-btn-reports on the Request Detail header, so Steward
   Reports reads as the same "thing" whether it's a page-title button or a table action pill. */
.sp-action-btn-reports {
    border-color: #334155;
    color: #334155;
}
.sp-action-btn-reports:hover {
    background: #334155;
    color: #fff;
}

/* ── Table density ───────────────────────────────
   Metronic ships padding-block: calc(var(--spacing) * 3) = 12px on body cells. With the avatar
   at a 34px footprint (32px photo + 1px ring each side) that puts rows at 58px. 10px holds the
   row at its original 54px while keeping the larger photo — mid-range for a data table
   (Bootstrap ~41, Material 52, Primer 40-48). Applied to every .kt-table so density is uniform. */
.kt-table tbody td {
    padding-block: 10px;
}

/* ── Striped table ───────────────────────────── */
.kt-table-striped tbody tr:nth-child(even) td {
    background-color: color-mix(in oklab, var(--muted) 60%, transparent);
}

/* ── Sortable column headers ─────────────────── */
th[data-col] { user-select: none; }

/* ── Error page ──────────────────────────────── */
.sp-error-icon-wrap {
    background: color-mix(in oklab, var(--primary) 10%, transparent);
}
.sp-error-icon-wrap-red {
    background: color-mix(in oklab, var(--destructive) 10%, transparent);
}

/* Theme-conditional illustration swap — Tailwind's dark:/light: variants aren't compiled
   into this static bundle (no live build step), so use the real html.dark toggle instead. */
.sp-illustration {
    max-height: 200px;
}
.sp-illustration-dark {
    display: none;
}
html.dark .sp-illustration-light {
    display: none;
}
html.dark .sp-illustration-dark {
    display: block;
}

.kt-card-table {
    position: relative;
    overflow: visible;
}

/* ── Workers page card header ────────────── */



/* ── Footer ─────────────────────────────────── */
.sp-footer {
    background: var(--card);
    padding: 0 1.5rem 20px;
    font-size: 12.5px;
    color: var(--muted-foreground);
}

.sp-footer-inner {
    max-width: 1400px;
    margin-inline: auto;
    text-align: center;
    padding-top: 16px;
}

.sp-footer-rule {
    display: block;
    width: 50%;
    margin: 0 auto 16px;
    overflow: visible;
}

.sp-footer-rule path {
    stroke: var(--foreground);
    stroke-width: 1.5;
    fill: none;
    stroke-linecap: round;
    stroke-linejoin: round;
    opacity: 0.35;
}

html.dark .sp-footer-rule path {
    stroke: #f6b100;
    opacity: 0.55;
    filter: drop-shadow(0 0 4px rgba(246,177,0,.5));
}

/* ── Detail label/value ─────────────────────── */
.sp-field-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--muted-foreground);
    margin-bottom: 3px;
}

.sp-field-value {
    font-size: 14px;
    font-weight: 500;
    color: var(--mono);
}

/* ── Info-card layout (RequestDetail, Group) ── */
.rd-group { padding: 1.25rem 0; border-bottom: 1px solid var(--border); }
.rd-group:last-child { border-bottom: none; padding-bottom: 0; }
.rd-group-title { font-size: .65rem; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--muted-foreground); margin-bottom: .75rem; }
.rd-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: .75rem 2rem; }
.rd-grid-3 { grid-template-columns: repeat(3, 1fr); }
.rd-grid-4 { grid-template-columns: repeat(4, 1fr); }
.rd-field-label { font-size: .7rem; font-weight: 500; letter-spacing: .04em; text-transform: uppercase; color: var(--muted-foreground); margin-bottom: .2rem; }
.rd-field-value { font-size: .875rem; font-weight: 400; color: var(--foreground); }
.rd-notes-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; }
.rd-note-box { background: var(--accent); border-radius: .5rem; padding: .75rem 1rem; font-size: .8rem; white-space: pre-wrap; color: var(--foreground); }

@media (max-width: 768px) {
    .rd-grid, .rd-grid-3, .rd-grid-4, .rd-notes-grid { grid-template-columns: 1fr; }
}

/* ── Missing Tailwind utilities not in compiled CSS ── */
.col-span-2    { grid-column: span 2 / span 2; }
.ms-2          { margin-inline-start: .5rem; }
.ms-auto       { margin-inline-start: auto; }
.shrink-0      { flex-shrink: 0; }
.grow          { flex-grow: 1; }
.gap-1\.5      { gap: .375rem; }
.gap-2\.5      { gap: .625rem; }
.mb-4          { margin-bottom: 1rem; }
.mb-7          { margin-bottom: 1.75rem; }
.mt-4          { margin-top: 1rem; }
.py-12         { padding-top: 3rem; padding-bottom: 3rem; }
.py-16         { padding-top: 4rem; padding-bottom: 4rem; }
.size-3\.5     { width: .875rem; height: .875rem; }
.text-sm       { font-size: .875rem; line-height: 1.25rem; }
.text-xs       { font-size: .75rem; line-height: 1rem; }
.text-base     { font-size: 1rem; line-height: 1.5rem; }

@media (min-width: 1024px) {
    .lg\:col-span-1 { grid-column: span 1 / span 1; }
    .lg\:col-span-2 { grid-column: span 2 / span 2; }
    .lg\:col-span-3 { grid-column: span 3 / span 3; }
    .lg\:gap-7      { gap: 1.75rem; }
    .lg\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0,1fr)); }
}


/* ── Tooltips (KTUI .kt-tooltip) ──────────────────
   Every tooltip in the app is KTTooltip: `data-kt-tooltip="true"` on the anchor
   plus a `<span data-kt-tooltip-content="true" class="kt-tooltip">` child. It
   positions with JS, so it flips at viewport edges and escapes overflow
   containers — the two things a CSS-only tooltip can't do, and what the old
   per-page .sp-tip patches were compensating for. */

/* Metronic's .kt-tooltip is sized to its content with no max-width, so a
   sentence-length tip renders as one very wide single line. Anything longer
   than a couple of words needs this. */
.sp-tooltip-text {
    max-width: 200px;
    text-align: center;
    line-height: 1.35;
}

/* KTUI ships no caret. This adds one, colour-matched to the bubble via the same
   --mono the .kt-tooltip background uses. It assumes the tooltip opened above
   its anchor (placement="top"), which is the only placement it's used with —
   the +1 next-day marker on the Group page, where a dense grid of shift chips
   makes it worth showing exactly which chip the tip belongs to. */
.sp-tooltip-arrow { position: relative; }

.sp-tooltip-arrow::after {
    content: '';
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    border: 5px solid transparent;
    border-top-color: var(--mono);
}

/* Keyboard-focusable anchors (Workers page) keep a visible focus ring — KTUI
   styles the bubble, never the trigger. */
[data-kt-tooltip][tabindex]:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
    border-radius: 50%;
}

/* ── Shared link resets ──────────────────────── */
.sp-tel-link { text-decoration: none; }
.sp-tel-link:hover { text-decoration: underline; }

/* ── Inline alert banner ─────────────────────── */
.sp-alert {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .75rem 1rem;
    border-radius: .5rem;
    font-size: .85rem;
    border: 1px solid;
}

.sp-alert i { flex-shrink: 0; font-size: 1rem; }

.sp-alert-success {
    color: #0d7a3c;
    background: rgba(23,198,83,.10);
    border-color: rgba(23,198,83,.25);
}

html.dark .sp-alert-success {
    color: #4ade80;
    background: rgba(23,198,83,.15);
    border-color: rgba(23,198,83,.30);
}

.sp-alert-error {
    color: var(--destructive);
    background: color-mix(in oklab, var(--destructive) 10%, transparent);
    border-color: color-mix(in oklab, var(--destructive) 25%, transparent);
}

/* ── NotForOpen warning icon ─────────────────── */
.sp-not-for-open {
    color: #f6b100;
    font-size: .85rem;
}

/* ── Workers page card header ────────────────── */
.sp-card-title-row {
    display: flex;
    align-items: center;
    gap: .5rem;
}

.sp-card-title-row .kt-card-title { margin: 0; }

.sp-card-top { margin-top: 1.5rem; }

/* ── Sort-arrow icons: support valid ARIA aria-sort tokens ──
   Metronic's kt-table-col-sort icon rules only key off the
   non-standard aria-sort="asc"/"desc" values. We write the
   correct ARIA tokens ("ascending"/"descending") to the DOM,
   so mirror the same icon rules for those values here. */
:is([aria-sort='ascending'] .kt-table-col-sort):before {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5' viewBox='0 0 8 5' fill='none'%3E%3Cpath d='M1.08333 4.83333C0.908333 4.83333 0.791667 4.775 0.675 4.65833C0.441667 4.425 0.441667 4.075 0.675 3.84167L3.59167 0.925C3.825 0.691667 4.175 0.691667 4.40833 0.925L7.325 3.84167C7.55833 4.075 7.55833 4.425 7.325 4.65833C7.09167 4.89167 6.74167 4.89167 6.50833 4.65833L4 2.15L1.49167 4.65833C1.375 4.775 1.25833 4.83333 1.08333 4.83333Z' fill='%234B5675'/%3E%3C/svg%3E");
}
:is([aria-sort='ascending'] .kt-table-col-sort):after {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5' viewBox='0 0 8 5' fill='none'%3E%3Cpath d='M4 4.24984C3.825 4.24984 3.70833 4.1915 3.59167 4.07484L0.675 1.15817C0.441667 0.924838 0.441667 0.574837 0.675 0.341504C0.908333 0.108171 1.25833 0.108171 1.49167 0.341504L4 2.84984L6.50833 0.341504C6.74167 0.108171 7.09167 0.108171 7.325 0.341504C7.55833 0.574837 7.55833 0.924838 7.325 1.15817L4.40833 4.07484C4.29167 4.1915 4.175 4.24984 4 4.24984Z' fill='%23C9CEDA'/%3E%3C/svg%3E");
}
:is([aria-sort='descending'] .kt-table-col-sort):before {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5' viewBox='0 0 8 5' fill='none'%3E%3Cpath d='M1.08333 4.83333C0.908333 4.83333 0.791667 4.775 0.675 4.65833C0.441667 4.425 0.441667 4.075 0.675 3.84167L3.59167 0.925C3.825 0.691667 4.175 0.691667 4.40833 0.925L7.325 3.84167C7.55833 4.075 7.55833 4.425 7.325 4.65833C7.09167 4.89167 6.74167 4.89167 6.50833 4.65833L4 2.15L1.49167 4.65833C1.375 4.775 1.25833 4.83333 1.08333 4.83333Z' fill='%23C9CEDA'/%3E%3C/svg%3E");
}
:is([aria-sort='descending'] .kt-table-col-sort):after {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5' viewBox='0 0 8 5' fill='none'%3E%3Cpath d='M4 4.24984C3.825 4.24984 3.70833 4.1915 3.59167 4.07484L0.675 1.15817C0.441667 0.924838 0.441667 0.574837 0.675 0.341504C0.908333 0.108171 1.25833 0.108171 1.49167 0.341504L4 2.84984L6.50833 0.341504C6.74167 0.108171 7.09167 0.108171 7.325 0.341504C7.55833 0.574837 7.55833 0.924838 7.325 1.15817L4.40833 4.07484C4.29167 4.1915 4.175 4.24984 4 4.24984Z' fill='%234B5675'/%3E%3C/svg%3E");
}
