﻿/* Admin Layout & Container */
:root {
    /* The two fixed bars of the shell. Both are locked so everything that has to fit
       between them — the sticky sidebar's `top` and height, and any page that sizes
       itself against the viewport — derives from the same numbers instead of guessing.
       A header mismatch makes the sidebar "scroll a bit then stick"; a footer mismatch
       leaves a dead band and a scrollbar. Change here, everything follows. */
    --admin-header-height: 56px;
    /* What the shell puts around a page: this much padding on every side, and the same
       again as a bottom margin. */
    --admin-content-inset: 1.5rem;
    --app-footer-height: 2.75rem;
}

.admin-wrapper {
    display: flex;
    flex-direction: column;
    width: 100%;
    min-height: 100vh;
    background-color: #f5f7fa;
}

/* Chrome, not content: a hairline instead of a drop shadow, so the bar sits behind the
   page rather than floating over it. */
.admin-header {
    background-color: #fff;
    color: #282828;
    height: var(--admin-header-height);
    padding: 0 1.25rem;
    box-sizing: border-box;
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-bottom: 1px solid var(--border-subtle);
    position: sticky;
    top: 0;
    z-index: 100;
}

/* Was --text-2xl — page-heading size on a bar that should never outweigh the case
   title beneath it. */
.admin-header-brand {
    margin: 0;
    font-size: var(--text-lg);
    display: flex;
    align-items: baseline;
    gap: 0.35rem;
    line-height: 1;
}

.admin-header-brand-liu {
    font-family: var(--font-display);
    font-style: italic;
    color: var(--liu-blue-dark);
    font-weight: var(--weight-regular);
    font-size: 1.05em;
    letter-spacing: 0.01em;
}

.admin-header-brand-rest {
    font-weight: var(--weight-semi);
    color: var(--text-primary);
    letter-spacing: var(--tracking-wide);
}

/* Stacked: name on top, role pill below right-aligned. Keeps the chrome
   tidy and lets the pill be a quiet visual subtitle rather than competing
   with the name on the same line. */
.admin-name {
    display: inline-flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.25rem;
    color: #444;
    font-size: var(--text-md);
    line-height: 1.1;
}

/* Role chip next to the user's name — color matches the role accents used
   elsewhere (purple for admin/manager, blue for staff). */
/* Wraps two role tags tight together for a hybrid manager+staff user so they read as
   a coordinated pair. The tags themselves are .role-tag now — the old .admin-role-pill
   was one of four ways this app drew a role, and they disagreed. */
.admin-role-pill-group {
    display: inline-flex;
    gap: 0.25rem;
}

/* ---- Superadmin role preview ----
   The pill itself becomes the control, so it keeps the pill's exact shape and tint
   and only gains a caret. Anything more would read as a second header action. */
.role-switch {
    position: relative;
    display: inline-flex;
}

.role-switch-trigger {
    border: 0;
    cursor: pointer;
    font-family: inherit;
}

.role-switch-trigger:hover {
    filter: brightness(1.12);
}

/* Catches the click that closes the menu without needing a JS document listener. */
.role-switch-backdrop {
    position: fixed;
    inset: 0;
    z-index: 40;
}

/* Chrome, and chrome should be small. It was sized like a page control — 12rem wide with
   0.42rem rows and a 0.85rem label — for a list of four short words that is opened, read
   and dismissed in a second. */
.role-switch-menu {
    position: absolute;
    top: calc(100% + 0.35rem);
    right: 0;
    z-index: 41;
    min-width: 10.5rem;
    padding: 0.25rem;
    background: #fff;
    border: 1px solid var(--border, #e2e6ea);
    border-radius: 8px;
    box-shadow: 0 8px 22px rgba(16, 24, 40, 0.13);
}

.role-switch-cap {
    padding: 0.25rem 0.45rem 0.3rem;
    font-size: 0.58rem;
    font-weight: var(--weight-bold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-uppercase);
    color: var(--text-subtle, #6b7280);
}

.role-switch-item {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    width: 100%;
    padding: 0.3rem 0.45rem;
    border: 0;
    border-radius: 6px;
    background: transparent;
    font: inherit;
    font-size: 0.78rem;
    color: var(--text, #1f2937);
    text-align: left;
    cursor: pointer;
}

.role-switch-item:hover {
    background: var(--surface-2, #f4f6f8);
}

.role-switch-item.is-active {
    font-weight: var(--weight-semi);
}

.role-switch-item-name {
    flex: 1;
}

.role-switch-note {
    padding: 0.3rem 0.45rem 0.1rem;
    font-size: 0.64rem;
    color: var(--text-subtle, #6b7280);
}



/* The glyphs were inheriting Radzen's icon size, which is set for buttons, not menus. */
.role-switch-item .rzi { font-size: 1rem; }

.role-switch-sep {
    height: 1px;
    margin: 0.25rem 0;
    background: var(--border-subtle);
}

/* Assigned column: an empty queue slot reads as absence, your own name as yours. */
/* Someone holds this case who can no longer review it. Amber rather than red: the case
   isn't broken, it just needs handing on — and red in this grid already means a denied or
   failed one. */
.assigned-flag {
    display: inline-flex;
    align-items: center;
    margin-left: 0.3rem;
    color: var(--status-warning-text);
    vertical-align: middle;
}

.assigned-flag .rzi { font-size: 0.9rem; }

.assigned-none {
    color: var(--text-subtle);
    font-style: italic;
}

.assigned-me {
    font-weight: var(--weight-semi);
    color: var(--role-doctor, #2f7d6e);
}

/* Toolbar toggle showing which slice of the queue you're looking at. A filter that is ON
   is a state, not an action — tinted in the doctor's own colour rather than filled solid,
   so it reads as "this is switched on" and not as the loudest button on the page. */
.admin-button.is-active {
    background-color: color-mix(in srgb, var(--role-doctor, #2f7d6e) 12%, #fff);
    border-color: color-mix(in srgb, var(--role-doctor, #2f7d6e) 45%, #fff);
    color: var(--role-doctor, #2f7d6e);
    font-weight: var(--weight-semi);
}

.admin-button.is-active:hover:not(:disabled) {
    background-color: color-mix(in srgb, var(--role-doctor, #2f7d6e) 18%, #fff);
    border-color: var(--role-doctor, #2f7d6e);
    color: var(--role-doctor, #2f7d6e);
}


/* Footer LIU brand — subtle Playfair italic on the institution name. Stays
   readable at small size because we're only italicizing two words, not the
   whole copyright line. */
.footer-liu {
    font-family: var(--font-display);
    font-style: italic;
    color: var(--liu-blue);
    font-weight: 500;
    margin-left: 0.2rem;
    letter-spacing: 0.01rem;
}

.admin-header-title {
    display: flex;
    align-items: center;
    gap: 1rem;
}

.admin-header-title h1 {
    margin: 0;
    font-size: var(--text-2xl);
}

.admin-sidebar-toggle {
    background: none;
    border: none;
    color: #282828;
    font-size: var(--text-2xl);
    cursor: pointer;
    padding: 0.25rem 0.5rem;
    display: flex;
    align-items: center;
    transition: opacity 0.2s;
}

.admin-sidebar-toggle:hover {
    opacity: 0.8;
}

.admin-header-actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

/* Was a solid grey fill — the only filled button in the shell, and it pulled the eye
   to the one control nobody visits the page to use. Same quiet outline as the page's
   secondary buttons, so the shell and the content speak one language. */
.admin-logout-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.35rem 0.7rem;
    background: transparent;
    color: var(--text-muted);
    border: 1px solid var(--border-default);
    border-radius: 8px;
    cursor: pointer;
    font-family: inherit;
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    transition: color 0.15s ease, border-color 0.15s ease;
}

.admin-logout-btn:hover {
    color: var(--text-primary);
    border-color: var(--border-strong);
}

.admin-logout-btn .rzi { font-size: 1rem; }

.admin-main {
    display: flex;
    /* grow 1, shrink 0, basis AUTO. Basis auto is the load-bearing part: the box is at
       least its own content, and only then grows into whatever space is left over.
       It was `flex: 1` + `min-height: 0`, which is basis 0% with the content floor
       removed — so the box was sized purely by available space and stayed pinned at
       one viewport however tall the page got. Content taller than that simply
       overflowed and painted over the footer, which is why the footer vanished on a
       short window and looked fine on a tall one. */
    flex: 1 0 auto;
}

/* Last child of the wrapper column, full width under both the rail and the content.
   The z-index outranks the sidebar's 99 so the footer stays legible even while the
   rail is mid-animation between 64px and 280px.

   Height is pinned to the same token .case-page subtracts from 100vh. Left to its
   own padding it renders a few px off that guess, which is the difference between
   a viewport-exact page and one with a stray scrollbar. */
/* Stuck to the bottom of the viewport rather than to the end of the document. The rail
   already runs full height and reserves this band at its foot, and .admin-content already
   subtracts the same token from 100vh — so the footer had a place kept for it and was the
   only part of the shell not sitting in it. `sticky` rather than `fixed` so it keeps its
   place in the flex column and nothing has to be padded out from underneath it. */
.admin-wrapper > .rz-footer {
    position: sticky;
    bottom: 0;
    z-index: 100;
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    height: var(--app-footer-height);
    padding-top: 0;
    padding-bottom: 0;
}

.admin-sidebar {
    /* Every colour in the rail comes from these six, so changing its treatment is six
       values rather than a hunt through nine rules.
       One ink now, white, top to bottom. It used to run white nav labels and near-black
       contact text on the same #69b3e7 — a mismatch, and near-black was the only half
       that passed contrast, which meant the readable half was the one that didn't look
       like chrome. The contact block earns white by getting a darker surface of its own
       rather than by sitting on the nav's. */
    --rail-ink: rgba(255, 255, 255, 0.92);
    --rail-ink-strong: #fff;
    --rail-hover: #4a93cf;
    --rail-info-ink: rgba(255, 255, 255, 0.86);
    --rail-info-strong: #fff;
    --rail-info-quiet: rgba(255, 255, 255, 0.6);
    --rail-rule: rgba(255, 255, 255, 0.2);
    /* The shelf the contact block sits on, and the band under it. One value, so the
       strip behind the footer can't drift away from the panel it continues. */
    --rail-shelf: color-mix(in srgb, #103a58 35%, var(--rail-bg));

    width: 280px;
    /* Two bands, not one: the rail proper, then the last footer-height in the shelf's
       darker blue. The contact block sits at the floor of the sticky column, which stops
       one footer-height short of the document — so without this the shelf ended in a
       strip of the lighter blue sitting behind the footer. */
    background: linear-gradient(
        to bottom,
        var(--rail-bg) calc(100% - var(--app-footer-height)),
        var(--rail-shelf) calc(100% - var(--app-footer-height))
    );
    box-shadow: 2px 0 4px rgba(0, 0, 0, 0.1);
    /* The rail is NOT the sticky thing — .admin-nav below is.
       Two earlier attempts got this wrong in opposite directions. Left to stretch with
       the flex row it filled the column correctly but the nav scrolled away with the
       page. Pinned instead, with a viewport-minus-chrome height, the nav stayed put but
       the rail stopped one footer-height short of the bottom and left a white gap
       mid-scroll — because the footer sits at the end of the DOCUMENT, not pinned to
       the viewport, so there was nothing down there to subtract for.
       Stretching the rail and sticking its contents gets both: the blue always reaches
       the footer, and the links stay under the header. */
    z-index: 99;
    display: flex;
    flex-direction: column;
    transition: width 0.25s ease;
}

/* Collapsed is an icon rail, not a disappearance: four destinations still need to
   be one click away, and the current section still has to be findable. Mobile
   overrides this back to 0 further down — there the rail overlays the content. */
.admin-sidebar.collapsed {
    width: 64px;
    padding: 0;
    /* No overflow here. An overflow value other than `visible` makes an element a scroll
       container, and a scroll container kills `position: sticky` on its descendants — so
       collapsing the rail silently switched the nav back to scrolling away with the page.
       The labels are already clipped where they live: .admin-nav-link > span goes to
       width:0 with overflow:hidden when collapsed, which is what the rail's overflow was
       duplicating. */
}

/* The icon centres in the rail: the label's box collapses with it, otherwise a
   zero-opacity span keeps its width and shoves the icon out of the 64px. */
.admin-sidebar.collapsed .admin-nav-link {
    gap: 0;
    justify-content: center;
    padding-left: 0;
    padding-right: 0;
}

/* Pins under the header while the page scrolls past it. Sticky works here because the
   rail is a plain flex column — giving the rail `overflow-y: auto` would silently turn
   it into a scroll container and kill this.
   It wraps the links AND the address block: sticking only the links left the contact
   details at the bottom of a rail as tall as the document, which on the submission form
   meant scrolling the whole page to read a phone number. */
/* One viewport tall and a flex column, which is what lets the contact block sit at its
   floor as a footer. `margin-top: auto` on the block alone isn't enough — it needs a
   parent with height to push against, and the rail itself is as tall as the document, so
   pushing against THAT put the phone number at the bottom of the page instead of the
   bottom of the screen. */
.admin-rail-stick {
    position: sticky;
    top: var(--admin-header-height);
    display: flex;
    flex-direction: column;
    /* Minus the footer as well as the header — the same two bands .case-page subtracts.
       At 100vh minus the header alone, the rail made .admin-main a footer-height taller
       than the viewport-fitting pages expect, which put a scrollbar and a grey strip on
       a case page that is built never to scroll. */
    min-height: calc(100vh - var(--admin-header-height) - var(--app-footer-height));
}

.admin-nav {
    list-style: none;
    padding-top: 1rem;
    padding-left: 0;
    margin: 0;
}

.admin-nav-link {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.7rem 1.25rem;
    color: var(--rail-ink);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    text-decoration: none;
    transition: background-color 0.2s;
    border-left: 3px solid transparent;
}

/* Where you are. The transparent left border above has been reserving this 3px since the rail
   was built and nothing ever filled it, so every page rendered an unlit rail — the one piece
   of orientation a sidebar exists to give. Border plus a wash plus full-strength ink, because
   any one of the three alone is easy to miss against a dark rail. */
.admin-nav-link.is-on {
    border-left-color: var(--rail-info-strong);
    background: rgba(255, 255, 255, 0.1);
    color: var(--rail-info-strong);
    font-weight: var(--weight-semi);
}

/* Collapsed to icons only, the label is gone and the border is off the visible edge, so the
   wash is all that is left to carry it. */
.admin-sidebar.collapsed .admin-nav-link.is-on {
    background: rgba(255, 255, 255, 0.16);
}

.admin-nav-link .rzi {
    font-size: 1.05em;
    flex-shrink: 0;
}

/* Stagger the text fade so it appears AFTER the sidebar finishes opening,
   and disappears immediately when it starts to collapse. CSS transitions read
   timing off the END state, so the base rule's delay applies on expand and
   the .collapsed override's no-delay applies on collapse. */
.admin-nav-link > span,
.admin-sidebar-info {
    opacity: 1;
    transition: opacity 0.15s ease;
    transition-delay: 0.18s;
}

/* Width goes with the opacity so the label gives its space back to the icon;
   without it the span stays 100px wide at zero opacity. */
.admin-nav-link > span {
    width: auto;
    overflow: hidden;
    white-space: nowrap;
    transition: opacity 0.15s ease, width 0.25s ease;
    transition-delay: 0.18s, 0s;
}

.admin-sidebar.collapsed .admin-nav-link > span {
    opacity: 0;
    width: 0;
    transition-delay: 0s;
}

/* The address block has no 64px form — it's prose, so it leaves rather than
   shrinks. display:none also keeps it out of the rail's scroll height. */
.admin-sidebar.collapsed .admin-sidebar-info {
    display: none;
}

/* ...and the shelf leaves with it. The darker band exists to be the ground the contact
   block stands on and to carry that ground down behind the footer; with the block gone
   it is a stray dark stripe at the foot of an otherwise flat rail, which is exactly what
   it looks like. Collapsed, the rail is one colour top to bottom. */
.admin-sidebar.collapsed {
    background: var(--rail-bg);
}

.admin-nav-link:hover {
    background-color: var(--rail-hover);
    border-left-color: color-mix(in srgb, var(--rail-hover) 80%, #000);
    color: var(--rail-ink-strong);
}

/* Sidebar is LIU Blue for every role now; the staff variant matches the rest
   so the chrome is consistent regardless of who's signed in. */
.admin-sidebar--staff {
    background-color: var(--liu-blue);
}

.admin-sidebar--staff .admin-nav-link:hover {
    background-color: var(--rail-hover);
    border-left-color: color-mix(in srgb, var(--rail-hover) 80%, #000);
}

.admin-sidebar--staff .admin-nav-link-submit:not(:first-child) {
    border-top-color: rgba(255, 255, 255, 0.3);
}

/* Only divide above Submit Request when there are admin/manager items above it
   — submitters see it as the first nav item and don't need a floating divider. */
.admin-nav-link-submit:not(:first-child) {
    margin-top: 1rem;
    border-top: 1px solid rgba(255, 255, 255, 0.15);
    padding-top: 0.75rem;
}

/* LIU + contact info pinned to the BOTTOM of the sidebar (margin-top:auto
   inside the flex column). Quiet two-section layout — uppercase tiny labels
   + body text. Links are underlined white (legible on the LIU-blue sidebar),
   no decorative icons or italic headers, so the block reads as a footer. */
/* The rail's footer: pushed to the floor of the sticky column, so it sits at the bottom
   of the screen rather than trailing the last nav link.
   It also gets a surface of its own — a darker shelf inside the blue. That is what
   licenses white text down here: the ink changes because the ground changed, which is a
   reason, where "white nav, navy contact, same blue" was just a disagreement. */
.admin-sidebar-info {
    margin-top: auto;
    padding: 1.4rem 1.5rem 1.6rem;
    background: var(--rail-shelf);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14);
    color: var(--rail-info-ink);
    font-size: var(--text-sm);
    line-height: 1.55;
    letter-spacing: 0.01em;
}

/* Recessive micro-label — the same job STATUS and NOTE do in the modals: name
   the group, then get out of the way. It was heavier than its own content. */
.liu-info-label {
    text-transform: uppercase;
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-uppercase);
    color: var(--rail-info-quiet);
    font-weight: var(--weight-semi);
    margin: 0 0 0.45rem;
}

/* Hairline between the groups, like the modal's control strip — the two blocks
   used to run together with nothing but a gap. */
.liu-info-label:not(:first-child) {
    margin-top: 1.35rem;
    padding-top: 1.35rem;
    border-top: 1px solid var(--rail-rule);
}

.liu-info-body {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    word-break: break-word;
}

/* The one strong line per group — the thing you came here to read. */
.liu-info-strong {
    font-weight: var(--weight-bold);
    color: var(--rail-info-strong);
    margin-bottom: 0.35rem;
}

/* Genuinely secondary, so it sits below body size — it was rendering *larger*
   than the content it qualifies. */
.liu-info-muted {
    margin-top: 0.5rem;
    color: color-mix(in srgb, var(--rail-info-ink) 85%, transparent);
    font-size: var(--text-xs);
}

/* a11y: give the sidebar contact links a >=24px tap target with spacing. */
.liu-info-body a {
    display: inline-block;
    min-height: 24px;
    padding: 0.2rem 0;
    line-height: 1.3;
}

/* Still obviously links — but the underline is a quiet affordance rather than a
   full-strength rule under bold, widely-tracked text, which made the phone and
   email the loudest things in the block. 0.07rem of tracking was ~1px a letter. */
.admin-sidebar-info a {
    color: var(--rail-info-strong);
    font-weight: var(--weight-semi);
    text-decoration: underline;
    text-decoration-color: color-mix(in srgb, var(--rail-info-strong) 35%, transparent);
    text-underline-offset: 3px;
}

.admin-sidebar-info a:hover {
    text-decoration-color: var(--rail-info-strong);
}

.content .admin-wrapper {
    width: 100%;
}

.admin-content {
    flex: 1;
    min-width: 0; /* allow flex child to shrink so wide content doesn't push horizontal scroll */
    /* Clip horizontal overshoot during the sidebar collapse/expand animation.
       `clip`, not `hidden`. Both hide the overshoot, but `hidden` makes this a scroll
       container — which silently breaks `position: sticky` for everything inside it, and
       everything the case page wants to keep on screen is inside it. `clip` does not
       establish one, so the sticky hero, lock bar and progress rail all work and the
       animation is still clipped. */
    overflow-x: clip;
    /* Tokenised because a page that sizes itself against the viewport has to subtract this
       exactly — .dash-page does, and hardcoding 1.5rem in two files is how it silently
       stops matching. */
    padding: var(--admin-content-inset);
    width: 100%;
    margin-bottom: var(--admin-content-inset);
}

/* Drop the admin chrome's 2rem padding when the autopsy submission/thankyou
   pages are inside — those pages paint their own vetmed-stain background and
   the padding would otherwise leak the admin-wrapper gray around them.
   Other admin pages (dashboard, sites, requests) keep the padding. */
/* Full-bleed pages size themselves against the viewport, so the content box must not
   add to them. .case-page computes its own height as 100vh minus header and footer —
   the shell's 1.5rem padding and margin pushed that 72px past the viewport and put a
   scrollbar on a page built not to scroll. */
.admin-content:has(> .autopsy-page),
.admin-content:has(> .thankyou-page),
.admin-content:has(> .case-page) {
    padding: 0;
    margin-bottom: 0;
}

/* Loading State */
.admin-loading {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
}

/* Login Page */
.admin-login-container {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    /*background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);*/
}

.admin-login-card {
    background: white;
    border-radius: 8px;
    box-shadow: var(--shadow-lg);
    width: 100%;
    max-width: 400px;
    padding: 1.75rem;
    /* Nudge the centered card upward by half this value so it sits a touch above the viewport midline. */
    margin-bottom: 4rem;
}

/* Admin login has no signup/forgot links so the card is visually shorter — give it extra bottom margin so it lifts to roughly the same optical position as the other login cards. */
.admin-login-card.admin-login-card--admin {
    margin-bottom: 8rem;
}

.admin-login-header {
    text-align: center;
    margin-bottom: 1rem;
}

.admin-login-title {
    margin: 0;
    font-family: var(--font-body);
    font-size: var(--text-2xl);
    font-weight: var(--weight-semi);
    color: var(--text-primary);
    line-height: var(--leading-tight);
    letter-spacing: var(--tracking-wide);
}

.admin-login-title em {
    font-family: var(--font-display);
    font-style: italic;
    font-weight: var(--weight-regular);
    color: var(--liu-blue-dark);
    font-size: 1.05em;
    margin-right: 0.12em;
}

.admin-login-title em:last-child {
    margin-right: 0;
    margin-left: 0.05em;
}

.admin-login-subtitle {
    margin: 0.4rem 0 0;
    color: var(--text-muted);
    font-size: var(--text-sm);
}

.admin-login-form {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}

.admin-form-group {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.admin-form-label {
    font-weight: var(--weight-semi);
    color: var(--text-primary);
    font-size: var(--text-sm);
    text-transform: uppercase;
    letter-spacing: var(--tracking-uppercase);
}

/* Actual clickable links in auth forms — distinct from the label styling above
   so they read as interactive (forgot-password, back-to-login). */
.admin-form-link {
    color: var(--liu-blue, #0a4d8c);
    font-size: var(--text-sm);
    text-decoration: underline;
    cursor: pointer;
}

.admin-form-link:hover {
    text-decoration: none;
}

/* Inline hint under a field (e.g. password requirements shown upfront). */
.admin-form-hint {
    font-size: var(--text-xs, 0.75rem);
    color: var(--text-muted, #6b7280);
    margin: 0.15rem 0 0.4rem 0;
}

.admin-form-input {
    padding: 0.55rem 0.75rem;
    border: 1px solid var(--border-strong);
    border-radius: 4px;
    font-size: var(--text-sm);
    transition: border-color 0.2s;
}

.admin-form-input:focus {
    outline: none;
    border-color: var(--liu-blue-dark);
    box-shadow: 0 0 0 3px rgba(53, 122, 189, 0.15);
}

.admin-error-alert {
    background-color: var(--status-danger-bg);
    color: var(--status-danger-text);
    padding: 0.6rem 0.75rem;
    border-radius: 4px;
    font-size: var(--text-sm);
    border-left: 3px solid var(--status-danger);
}

.admin-login-button {
    padding: 0.6rem;
    font-size: var(--text-sm);
    font-weight: var(--weight-semi);
    letter-spacing: var(--tracking-wide);
}

/* Login/auth pages keep LIU blue buttons (brand), overriding the app-wide
   neutral grey used for other action buttons. */
.admin-login-card .admin-login-button.rz-button.rz-primary {
    background-color: var(--liu-blue) !important;
    border-color: var(--liu-blue) !important;
}

.admin-login-card .admin-login-button.rz-button.rz-primary:hover:not([disabled]):not(.rz-state-disabled) {
    background-color: #4a93cf !important;
    border-color: #4a93cf !important;
}

/* Toolbar */
.admin-toolbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 0.75rem;
    padding-bottom: 0.75rem;
    border-bottom: 1px solid var(--border-subtle);
}

.admin-page-title {
    margin: 0;
    font-family: var(--font-body);
    font-size: var(--text-2xl);
    font-weight: var(--weight-semi);
    color: var(--text-primary);
    line-height: var(--leading-tight);
    letter-spacing: var(--tracking-wide);
}

.admin-page-title em {
    font-family: var(--font-display);
    font-style: italic;
    font-weight: var(--weight-regular);
    color: var(--text-primary);
    font-size: 1.05em;
    margin-right: 0.12em;
}

.admin-page-title em:last-child {
    margin-right: 0;
    margin-left: 0.05em;
}

.admin-toolbar-actions {
    display: flex;
    gap: 0.5rem;
}

/* ---- the two button tiers, admin side ----
   The same pair the case page uses and the submission form now follows: a TOOL is
   transparent with a quiet outline (.case-tool), an ACTION is a tinted fill with blue
   ink (.step-next). One 8px box on both, which is most of what makes them read as a set.

   .admin-button was a solid LIU-blue fill on everything — Refresh, Export, Only mine,
   Add Site — so four toolbars each had three or four equally loud blue buttons and no
   way to tell the one that creates something from the one that reloads the grid. It is
   the tool tier now; the action tier is opt-in below. */
.admin-button {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.85rem;
    background: transparent;
    border: 1px solid var(--border-default);
    border-radius: var(--r-button);
    cursor: pointer;
    font-family: inherit;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    color: var(--text-muted);
    line-height: var(--leading-snug);
    transition: color 0.15s ease, border-color 0.15s ease, background-color 0.15s ease;
}

.admin-button .rzi {
    font-size: 1.05em;
    /* Override any inline vertical-align/margin from the markup — flex handles
       both axes correctly, so the legacy spacing hacks become noise. */
    vertical-align: baseline !important;
    margin: 0 !important;
}

.admin-button:hover:not(:disabled) {
    background-color: var(--surface-raised);
    border-color: var(--border-strong);
    color: var(--text-primary);
}

.admin-button:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--liu-blue-dark) 20%, transparent);
}

.admin-button:disabled {
    opacity: 0.55;
    cursor: default;
}

/* The one thing a page exists to do — Add Site, Add user. Opt-in, and there should
   never be two on a screen. */
.admin-button--primary {
    background-color: var(--liu-blue-light);
    border-color: #bcdcf1;
    color: #1f5f8b;
    font-weight: var(--weight-semi);
}

.admin-button--primary:hover:not(:disabled) {
    background-color: var(--liu-blue-light);
    border-color: #bcdcf1;
    color: #1f5f8b;
    filter: brightness(0.97);
    transform: translateY(-1px);
}

.admin-button--primary:active:not(:disabled) { transform: none; }

/* KPI Cards */
.admin-kpi-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 1rem;
    margin-bottom: 1.5rem;
}

.admin-kpi-card {
    background: white;
    border-radius: 8px;
    padding: 1rem 1.1rem;
    box-shadow: var(--shadow-sm);
    border-left: 3px solid var(--liu-blue);
}

.left-border-gold {
    border-left: 4px solid var(--liu-gold);
}

.left-border-purple {
    border-left: 4px solid var(--accent-violet);
}

.left-border-success {
    border-left: 4px solid var(--status-success);
}

.admin-kpi-label {
    font-size: var(--text-xs);
    color: var(--text-muted);
    font-weight: var(--weight-bold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-uppercase);
    margin-bottom: 0.5rem;
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.admin-kpi-label .rzi {
    font-size: 1.2em;
    color: var(--liu-blue);  /* default = LIU blue, matches the default left border */
}

/* Per-card icon tint — matches the card's accent border exactly. */
.admin-kpi-card.left-border-purple  .admin-kpi-label .rzi { color: var(--accent-violet); }
.admin-kpi-card.left-border-success .admin-kpi-label .rzi { color: var(--status-success); }
.admin-kpi-card.left-border-gold    .admin-kpi-label .rzi { color: var(--liu-gold); }

.admin-kpi-value {
    font-size: var(--text-2xl);
    font-weight: var(--weight-bold);
    color: var(--text-primary);
    margin-bottom: 0.1rem;
    line-height: var(--leading-tight);
}

.admin-kpi-change {
    font-size: var(--text-sm);
    color: var(--ui-action-disabled);
}

.admin-kpi-change.positive {
    color: var(--status-success);
}

/* Charts */
.admin-chart-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
    gap: 1.5rem;
}

.admin-chart-card {
    background: white;
    border-radius: 8px;
    padding: 1rem 1.25rem;
    box-shadow: var(--shadow-sm);
}

.admin-chart-title {
    margin-top: 0;
    margin-bottom: 0.85rem;
    font-size: var(--text-md);
    font-weight: var(--weight-semi);
    color: var(--text-primary);
}

.admin-chart-empty {
    text-align: center;
    padding: 2rem;
    color: var(--ui-action-disabled);
}

/* DataGrid */
/* The list pages' card, same object the dashboard and the case page use: white, one
   hairline, the shared radius, no shadow. It was a shadowed panel with no border, so a
   grid on Requests floated while the identical grid of counts on the dashboard sat flat
   — two treatments for one idea. */
.admin-datagrid-container {
    background: #fff;
    border: 1px solid var(--border-default);
    border-radius: var(--r-card);
    padding: 1.1rem 1.2rem;
    box-shadow: none;
    overflow-x: auto;
    /* Query context so the expanded row panel can size to the visible width
       (cqw) instead of the full, horizontally-scrolled table width. */
    container-type: inline-size;
}


/* Grid edit-icon buttons stay LIU blue (brand action), overriding the
   app-wide neutral grey applied to other action buttons. */
/* Row actions are tools, not decisions: one quiet icon per row rather than a column of
   solid blue and a column of solid red running down the grid. Colour arrives on hover —
   blue for edit, red for delete — so the destructive one is still unmistakable at the
   moment you reach for it. Same treatment as the case page's .spec-edit pencils. */
.grid-btn-edit.rz-button,
.rz-data-grid .rz-button.rz-danger {
    background-color: transparent !important;
    border: 1px solid var(--border-default) !important;
    color: var(--text-muted) !important;
    box-shadow: none !important;
}

.grid-btn-edit.rz-button:hover:not(:disabled):not(.rz-state-disabled) {
    background-color: var(--liu-blue-light) !important;
    border-color: #bcdcf1 !important;
    color: var(--liu-blue-dark) !important;
}

.rz-data-grid .rz-button.rz-danger:hover:not(:disabled):not(.rz-state-disabled) {
    background-color: var(--status-danger-bg, #fdecea) !important;
    border-color: color-mix(in srgb, var(--status-danger) 45%, #fff) !important;
    color: var(--status-danger) !important;
}

/* The row expander. Radzen renders it as a primary RadzenButton, so the app's brand
   rule painted it a solid blue square with a minus in it — a filled action control on
   every row of the grid, for something that only opens a drawer. A bare chevron-weight
   icon instead, in the same muted ink as the row actions beside it.
   Unscoped: the expander appears on the Sites grid, the Users grid and the manager
   portal, and scoping it to one container meant fixing it in one place at a time. */
.rz-row-toggler,
.rz-button.rz-row-toggler {
    width: auto !important;
    height: auto !important;
    min-width: 0 !important;
    padding: 0.15rem !important;
    background: transparent !important;
    border: 0 !important;
    box-shadow: none !important;
    color: var(--text-subtle) !important;
}

.rz-row-toggler:hover {
    color: var(--liu-blue-dark) !important;
    background: transparent !important;
}

/* Sites grid: caret column is hidden and the whole row is click-to-expand,
   so signal it as interactive and keep frozen cells in step on hover. */
.clickable-rows .rz-data-row {
    cursor: pointer;
}

.clickable-rows .rz-data-row:hover td,
.clickable-rows .rz-data-row:hover .rz-frozen-cell {
    background-color: var(--liu-blue-light);
}

.admin-requests-grid {
    border: none !important;
}

.admin-requests-grid tbody tr {
    transition: background-color 0.2s, box-shadow 0.2s !important;
}

.admin-requests-grid tbody tr:hover {
    background-color: var(--status-info-bg) !important;
    cursor: pointer !important;
    box-shadow: inset 0 0 0 2px var(--ui-action) !important;
}

.admin-requests-grid tbody tr:hover td {
    background-color: var(--status-info-bg) !important;
    color: inherit !important;
}

.admin-requests-grid tbody td {
    transition: background-color 0.2s !important;
}

/* Column heads read like the case page's spec keys: small, uppercase, tracked and
   quiet. They were near-black bold at body size over a grey fill with a 2px rule under
   it — a header band that outweighed the rows it was labelling. Applied to every admin
   grid, not just Requests, so the three list pages stop each having their own table. */
.admin-datagrid-container thead,
.admin-datagrid-container thead th {
    background-color: transparent;
}

.admin-datagrid-container thead th {
    border-bottom: 1px solid var(--border-default);
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-uppercase);
    color: var(--text-subtle);
}

/* Radzen paints the sort/filter affordances separately from the cell. */
.admin-datagrid-container thead th .rz-column-title,
.admin-datagrid-container thead th .rz-sortable-column-title {
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    color: inherit;
}

/* Zebra, the same one the record sheet uses — it carries the row without a border
   under every line. */
.admin-datagrid-container tbody tr:nth-of-type(odd) td {
    background-color: var(--surface-raised);
}

.admin-datagrid-container tbody td {
    border-bottom: 0;
    font-size: var(--text-sm);
    color: var(--text-secondary);
}

.admin-requests-grid tbody td:nth-child(2),
.admin-requests-grid thead th:nth-child(2) {
    vertical-align: middle;
    text-align: center;
}

.admin-empty-state {
    text-align: center;
    padding: 1.75rem 1.5rem;
    color: var(--text-muted);
    font-size: var(--text-sm);
}

/* Animal Type Pills — quiet tinted chips, no border. Soft enough to read as
   metadata, distinct enough to scan across animal categories. */
.animal-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.2rem 0.65rem;
    border-radius: 999px;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    line-height: var(--leading-snug);
    white-space: nowrap;
    letter-spacing: 0.01em;
}

.animal-pill .rzi {
    font-size: var(--text-md);
    opacity: 0.8;
}

/* Font Awesome sets no width of its own, so three different glyphs put the word that
   follows them in three different places down a column. A fixed box lines them up. */
.animal-pill .fa-solid,
.animal-pill .fa-regular,
.animal-pill .mi-mark {
    font-size: 0.85rem;
    line-height: 1;
    width: 1.25em;
    text-align: center;
    opacity: 0.8;
}

.animal-pill-dog     { background: #EAF5FD; color: var(--status-info-text); }
.animal-pill-horse   { background: #FFF6D1; color: #8A6A00; }
.animal-pill-farm    { background: #F1E8F4; color: #7F5A8C; }
.animal-pill-default { background: var(--border-subtle); color: var(--text-muted); }

/* Material Icons */
.material-icons {
    font-family: 'Material Icons';
    font-weight: normal;
    font-style: normal;
    font-size: var(--text-xl);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    text-transform: none;
    letter-spacing: normal;
    word-wrap: normal;
    white-space: nowrap;
    direction: ltr;
}

.material-icons.icon-small {
    font-size: var(--text-md);
    margin-right: 0.3rem;
}

/* Type column filter — three little icon-only toggles, sized to match the
   pill's restraint. Active state uses the same soft tint as the pill. */
.animal-emoji-filter {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    gap: 0.25rem;
}

/* A fixed square, rounded to the same proportion as the case header's species tile, so
   the two places a type is drawn as a standalone mark agree on their shape. Round and
   padding-sized before, which spent the button on air and left the glyph too small to
   tell apart — the tile is the size it always was and the growth went into the mark. */
.emoji-btn {
    padding: 0;
    width: 2rem;
    height: 2rem;
    border: 1px solid transparent;
    background: transparent;
    cursor: pointer;
    opacity: 0.45;
    transition: opacity 0.15s, background 0.15s;
    border-radius: 0.45rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--text-secondary);
    line-height: 1;
}

/* As large as the tile will carry. A fixed 1em box so a wide paw, a tall knight and a
   wide tractor all centre identically instead of each sizing its own button. */
.emoji-btn .fa-solid,
.emoji-btn .fa-regular,
.emoji-btn .mi-mark {
    font-size: 1.25rem;
    line-height: 1;
    width: 1.25em;
    text-align: center;
}

.emoji-btn:hover {
    opacity: 0.85;
}

.emoji-btn.active {
    opacity: 1;
}

.animal-emoji-filter button:nth-child(1) { color: var(--status-info-text); }
.animal-emoji-filter button:nth-child(1).active { background: #EAF5FD; }

.animal-emoji-filter button:nth-child(2) { color: #8A6A00; }
.animal-emoji-filter button:nth-child(2).active { background: #FFF6D1; }

.animal-emoji-filter button:nth-child(3) { color: #7F5A8C; }
.animal-emoji-filter button:nth-child(3).active { background: #F1E8F4; }

/* Responsive */
@media (max-width: 768px) {
    .admin-header {
        padding: 0.75rem 1rem;
        flex-wrap: wrap;
        gap: 0.5rem;
    }

    .admin-header-title h1 {
        font-size: var(--text-xl);
    }

    .admin-header-actions {
        font-size: var(--text-sm);
        gap: 0.5rem !important;
    }

    .admin-content {
        padding: 1rem;
    }

    .admin-main {
        position: relative;
    }

    .admin-sidebar {
        position: fixed;
        left: 0;
        top: 57px;
        height: calc(100vh - 57px);
        overflow-y: auto;
        width: 220px;
        z-index: 98;
        box-shadow: 2px 0 12px rgba(0, 0, 0, 0.2);
    }

    /* Back to a full hide here: the rail is fixed and overlays the content at this
       width, so a 64px strip would sit on top of the page rather than beside it. */
    .admin-sidebar.collapsed {
        width: 0;
        padding: 0;
        overflow: hidden;
    }

    .admin-nav-link {
        padding: 0.75rem 1rem;
    }

    .admin-kpi-grid {
        grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    }

    .admin-chart-grid {
        grid-template-columns: 1fr;
    }

    .admin-header {
        flex-direction: column;
        gap: 1rem;
    }

    .admin-name {
        display: none;
    }
    .admin-header {
        flex-direction: unset;
    }
}


.type-column .rz-cell-data {
    text-align: center;
}


.admin-header-title h1 {
    color: #282828;
}

/* "You got here from somewhere" — the filter the dashboard applied on the way in.
   Loud enough that a short list never looks like an empty database, and carrying its
   own dismiss so the way back to everything is on the chip itself. */
.admin-filter-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.75rem;
    /* Filters stack up — a stage AND a reviewer AND the hold flag can all be on at
       once, so the chips need to sit apart rather than butt against each other. */
    margin-right: 0.5rem;
    padding: 0.4rem 0.5rem 0.4rem 0.75rem;
    background: var(--liu-blue-light);
    border: 1px solid #cfe4f2;
    border-radius: 999px;
    font-size: 0.78rem;
    color: #1f5f8b;
}

.admin-filter-chip .rzi { font-size: 1rem; }
.admin-filter-chip b { font-weight: 700; }

.admin-filter-chip button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.35rem;
    height: 1.35rem;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: inherit;
    cursor: pointer;
}

.admin-filter-chip button:hover { background: rgba(31, 95, 139, 0.12); }
.admin-filter-chip button .rzi { font-size: 0.9rem; }

/* The .admin-summary-* strip is gone. It was a bordered card holding two numbers,
   sitting directly above the bordered card holding the grid those numbers described —
   and the same two facts now live in that card's own caption (.admin-cap). */

.rz-scheme-pastel .rz-series-0 {
    fill: var(--accent-violet);
    stroke: var(--rz-series-1);
}

.rz-scheme-pastel .rz-series-item-0 {
    fill: var(--liu-blue);
    stroke: var(--rz-series-1);
}

i.rzi {
    vertical-align: text-bottom;
}

/* ===== Site status toggle =====
   A cell that changes its row rather than opening it, so it has to read as a control and
   not as a value. Quiet by default — a grid of these at full strength would be a column of
   buttons shouting over the names beside them — with the dot carrying the state and the
   border only appearing under the cursor. */
.site-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.15rem 0.5rem;
    border: 1px solid transparent;
    border-radius: 999px;
    background: transparent;
    font: inherit;
    font-size: 0.76rem;
    font-weight: var(--weight-semi);
    color: var(--text-muted);
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease;
}

.site-toggle:hover:not([disabled]) {
    border-color: var(--border-default);
    background: var(--surface-raised);
    color: var(--text-primary);
}

.site-toggle[disabled] { opacity: 0.55; cursor: default; }

.site-toggle-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--border-strong);
    flex: 0 0 auto;
}

.site-toggle.is-on { color: var(--text-secondary); }
.site-toggle.is-on .site-toggle-dot { background: var(--status-success); }

/* A row that is switched off should read that way before its Status cell is reached —
   otherwise a list of active and inactive sites looks like one list of sites. */
.site-row--off td { opacity: 0.55; }
.site-row--off:hover td { opacity: 0.8; }

/* ---- list-page caption ----
   The dashboard's .dash-cap, on the list pages: a small uppercase line above the grid
   carrying whatever the page was previously stating in a box of its own. Requests had a
   "Filtered Results" card holding two numbers — a panel above a panel for something a
   caption says in one line. */
.admin-cap {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin: 0 0 0.85rem;
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-uppercase);
    color: var(--text-subtle);
}

/* The right-hand half of the caption: facts, not a heading, so it stops shouting. */
/* A step up from the caption beside it: at --text-xs these were the smallest type on
   the page, and the numbers are the part anyone actually reads. */
.admin-cap-meta {
    display: inline-flex;
    align-items: baseline;
    gap: 1.1rem;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    text-transform: none;
    letter-spacing: 0;
    color: var(--text-muted);
}

.admin-cap-meta b {
    font-weight: var(--weight-bold);
    color: var(--text-primary);
}
