/* ------------------------------------------------------------------ base */
/* Every surface in this file comes from a token. Nothing below hardcodes a
   light-mode colour for a background, a border or a text colour, because the
   dark theme is a second set of values for these same names, not a second
   stylesheet. A hardcoded #fff here is what makes a dark theme impossible. */
:root {
  color-scheme: light;

  --brand: #1a56a8;
  --brand-dark: #123c76;
  --brand-light: #eaf1fb;
  --brand-on: #ffffff;

  --ink: #1f2933;
  --ink-soft: #52606d;
  --ink-faint: #7b8794;
  --ink-invert: #ffffff;

  --line: #dfe4ea;
  --line-soft: #edf0f4;

  --bg: #f4f6f9;
  --card: #ffffff;
  --card-2: #f8fafc;   /* table heads, toolbars, footers: a half-step down */
  --card-3: #f7fafd;   /* row hover */
  --well: #eef1f5;     /* recessed fill: meter tracks, ghost buttons */
  --field-bg: #ffffff;
  --field-border: #c8d0da;

  --ok: #137333;
  --ok-bg: #e6f4ea;
  /* Amber that stays amber and still clears 4.5:1 on its own tint. The
     previous #b06000 measured 4.23:1 against --warn-bg, which is below the
     threshold for body-size text; a warning a reader cannot read is not a
     warning. #b05200 holds the hue at 4.71:1. */
  --warn: #b05200;
  --warn-bg: #fdf3e3;
  --bad: #b3261e;
  --bad-bg: #fdecea;
  --info: #0b5c8a;
  --info-bg: #e7f3f9;

  /* Radius is a scale, not a single value, so a card, a tile and a pill can
     step apart instead of all reading as the same shape. */
  --radius-sm: 8px;
  --radius: 14px;
  --radius-lg: 20px;
  --radius-pill: 999px;

  /* Shadows are layered rather than a single blur: a hairline contact shadow
     that grounds the edge plus a wide, very soft one for lift. That reads as a
     card sitting on the page; one flat blur reads as a smudge. */
  --shadow: 0 1px 2px rgba(16, 24, 40, .05), 0 1px 3px rgba(16, 24, 40, .06);
  --shadow-lg: 0 10px 15px -3px rgba(16, 24, 40, .09), 0 24px 40px -12px rgba(16, 24, 40, .14);
  --shadow-pop: 0 12px 28px -6px rgba(16, 24, 40, .16), 0 4px 10px -4px rgba(16, 24, 40, .10);

  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* One stack for body copy, one for headings. No webfont is loaded: the panel
     has to work on a campus network with no outbound access, and a request to
     a font CDN that never arrives is a page that renders in Times. Segoe UI is
     first on Windows because that is where this is used, and it is a much
     better text face than Arial behind it. */
  --font-sans: "Segoe UI Variable Text", "Segoe UI", -apple-system, BlinkMacSystemFont, "Inter", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-display: "Segoe UI Variable Display", "Segoe UI", -apple-system, BlinkMacSystemFont, "Inter", Roboto, "Helvetica Neue", Arial, sans-serif;

  /* Chart ink. Kept as tokens so a dark theme moves the axes and the grid with
     the text, instead of leaving black lines on a dark card. */
  --chart-grid: #e6eaef;
  --chart-axis: #94a0ad;
  --chart-track: #e9edf2;
  --chart-tip-bg: #1f2933;
  --chart-tip-ink: #ffffff;
  --topbar-bg: #123c76;
  --topbar-ink: #ffffff;

  --chart-ok: #137333;
  --chart-warn: #b06000;
  --chart-bad: #b3261e;
  --chart-info: #0b5c8a;
  --chart-brand: #1a56a8;
  --chart-ink: #1f2933;

  /* The banner at the top of the dashboard. It is the one place in the panel
     that carries a gradient, because it is the one place that is meant to read
     as a masthead rather than as a box of data. Kept to the brand family so it
     still follows the theme. */
  --banner-a: #17457f;
  --banner-b: #123c76;
  --banner-c: #0c2a51;
  --banner-glow: rgba(140, 190, 250, .40);
  --banner-ink: #ffffff;
  --banner-ink-soft: rgba(255, 255, 255, .78);
  --banner-ink-faint: rgba(255, 255, 255, .62);
  --banner-glass: rgba(255, 255, 255, .12);
  --banner-line: rgba(255, 255, 255, .22);

  /* The sidebar rail: a navy ground with white labels, carrying the same #123c76
     the topbar uses so the two read as one frame around the page. Its own tokens
     because the rail has to stay navy in the light theme and step down to a
     near-black navy in the dark one, which is not something --card can express. */
  --nav-bg: #123c76;
  --nav-line: #0c2a51;
  --nav-ink: #ffffff;
  --nav-ink-hover: #ffffff;
  --nav-group-ink: rgba(255, 255, 255, .66);
  --nav-hover-bg: rgba(255, 255, 255, .12);
  --nav-active-bg: rgba(255, 255, 255, .18);
  --nav-active-ink: #ffffff;
  --nav-marker: #8fc0ff;
  --nav-focus: #8fc0ff;
}

/* The dark theme. Same names, different values, so no component rule has to
   know which theme is on. Applied via [data-theme] on <html>, set before first
   paint by the inline script in the header. */
[data-theme="dark"] {
  color-scheme: dark;

  --brand: #5ea1f0;
  --brand-dark: #8dbdf5;
  --brand-light: #16273f;
  --brand-on: #08111c;

  --ink: #e6ebf2;
  --ink-soft: #a9b6c6;
  --ink-faint: #7d8b9c;
  --ink-invert: #0b1119;

  --line: #2b3441;
  --line-soft: #212935;

  --bg: #0d1219;
  --card: #151b24;
  --card-2: #1a212b;
  --card-3: #1e2632;
  --well: #232c38;
  --field-bg: #11161e;
  --field-border: #313b49;

  --ok: #4ec97a;
  --ok-bg: #122a1c;
  --warn: #e2a13f;
  --warn-bg: #2c2110;
  --bad: #f4786d;
  --bad-bg: #2e1513;
  --info: #5cb4dd;
  --info-bg: #0f2531;

  --shadow: 0 1px 2px rgba(0, 0, 0, .40), 0 1px 3px rgba(0, 0, 0, .34);
  --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .50), 0 24px 40px -12px rgba(0, 0, 0, .62);
  --shadow-pop: 0 12px 28px -6px rgba(0, 0, 0, .58), 0 4px 10px -4px rgba(0, 0, 0, .44);

  --chart-grid: #262f3b;
  --chart-axis: #6b798a;
  --chart-track: #232c38;
  --chart-tip-bg: #e6ebf2;
  --chart-tip-ink: #0b1119;
  --topbar-bg: #0a0f16;
  --topbar-ink: #e6ebf2;

  --chart-ok: #4ec97a;
  --chart-warn: #e2a13f;
  --chart-bad: #f4786d;
  --chart-info: #5cb4dd;
  --chart-brand: #5ea1f0;
  --chart-ink: #e6ebf2;

  --banner-a: #16375e;
  --banner-b: #102a4a;
  --banner-c: #08152a;
  --banner-glow: rgba(94, 161, 240, .28);
  --banner-glass: rgba(255, 255, 255, .08);
  --banner-line: rgba(255, 255, 255, .16);

  --nav-bg: #0f1a2b;
  --nav-line: #1c2a3f;
  --nav-ink: #dce6f5;
  --nav-ink-hover: #ffffff;
  --nav-group-ink: #8fa2bb;
  --nav-hover-bg: rgba(255, 255, 255, .08);
  --nav-active-bg: rgba(94, 161, 240, .22);
  --nav-active-ink: #ffffff;
  --nav-marker: #5ea1f0;
  --nav-focus: #5ea1f0;
}

/* Windows and high-contrast modes set a background on their own; match it so
   the toggle and the theme agree. */
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) {
    color-scheme: dark;

    --brand: #5ea1f0;
    --brand-dark: #8dbdf5;
    --brand-light: #16273f;
    --brand-on: #08111c;

    --ink: #e6ebf2;
    --ink-soft: #a9b6c6;
    --ink-faint: #7d8b9c;
    --ink-invert: #0b1119;

    --line: #2b3441;
    --line-soft: #212935;

    --bg: #0d1219;
    --card: #151b24;
    --card-2: #1a212b;
    --card-3: #1e2632;
    --well: #232c38;
    --field-bg: #11161e;
    --field-border: #313b49;

    --ok: #4ec97a;
    --ok-bg: #122a1c;
    --warn: #e2a13f;
    --warn-bg: #2c2110;
    --bad: #f4786d;
    --bad-bg: #2e1513;
    --info: #5cb4dd;
    --info-bg: #0f2531;

    --shadow: 0 1px 2px rgba(0, 0, 0, .40), 0 1px 3px rgba(0, 0, 0, .34);
    --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .50), 0 24px 40px -12px rgba(0, 0, 0, .62);
    --shadow-pop: 0 12px 28px -6px rgba(0, 0, 0, .58), 0 4px 10px -4px rgba(0, 0, 0, .44);

    --chart-grid: #262f3b;
    --chart-axis: #6b798a;
    --chart-track: #232c38;
    --chart-tip-bg: #e6ebf2;
    --chart-tip-ink: #0b1119;
    --topbar-bg: #0a0f16;
    --topbar-ink: #e6ebf2;

    --chart-ok: #4ec97a;
    --chart-warn: #e2a13f;
    --chart-bad: #f4786d;
    --chart-info: #5cb4dd;
    --chart-brand: #5ea1f0;
    --chart-ink: #e6ebf2;

    --banner-a: #16375e;
    --banner-b: #102a4a;
    --banner-c: #08152a;
    --banner-glow: rgba(94, 161, 240, .28);
    --banner-glass: rgba(255, 255, 255, .08);
    --banner-line: rgba(255, 255, 255, .16);

    --nav-bg: #0f1a2b;
    --nav-line: #1c2a3f;
    --nav-ink: #dce6f5;
    --nav-ink-hover: #ffffff;
    --nav-group-ink: #8fa2bb;
    --nav-hover-bg: rgba(255, 255, 255, .08);
    --nav-active-bg: rgba(94, 161, 240, .22);
    --nav-active-ink: #ffffff;
    --nav-marker: #5ea1f0;
    --nav-focus: #5ea1f0;
  }
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body.admin {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Tabular figures everywhere by default. A dashboard is mostly numbers in
     columns; proportional digits make a column of counts look ragged because
     each digit is a different width. */
  font-variant-numeric: tabular-nums;
}

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

.skip-link {
  position: absolute; left: -999px; top: 0; background: var(--brand); color: #fff;
  padding: 10px 16px; z-index: 100;
}
.skip-link:focus { left: 0; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  line-height: 1.22; margin: 0 0 .5em;
  letter-spacing: -.015em; font-weight: 700;
}
h1 { font-size: 1.5rem; letter-spacing: -.022em; }
h2 { font-size: 1.16rem; }
h3 { font-size: 1rem; }

/* ---------------------------------------------------------------- topbar */
.topbar {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; gap: 14px;
  height: 60px; padding: 0 20px;
  background:
    radial-gradient(620px 200px at 12% -60%, rgba(140, 190, 250, .30) 0%, rgba(140, 190, 250, 0) 70%),
    var(--topbar-bg);
  color: var(--topbar-ink);
  box-shadow: var(--shadow);
}

.topbar a { color: var(--topbar-ink); text-decoration: none; }
.topbar-brand { display: flex; align-items: center; gap: 10px; font-weight: 650; font-size: .98rem; }
.topbar-brand a { display: flex; align-items: center; gap: 10px; }
.topbar-logo { height: 30px; width: auto; background: #fff; padding: 2px 5px; border-radius: 4px; }

/* Theme switch. Two icons in one button: the visible one is the theme you
   would switch TO, so the control labels itself without a tooltip. */
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0; border: 0; border-radius: 8px;
  background: rgba(255, 255, 255, .12); color: var(--topbar-ink);
  cursor: pointer; flex: 0 0 auto;
}
.theme-toggle:hover { background: rgba(255, 255, 255, .22); }
.theme-toggle:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.theme-toggle svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.theme-toggle .icon-moon { display: none; }
[data-theme="dark"] .theme-toggle .icon-moon { display: block; }
[data-theme="dark"] .theme-toggle .icon-sun { display: none; }
.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 14px; }
.topbar-link { font-size: .86rem; opacity: .92; }
.topbar-link:hover { opacity: 1; text-decoration: underline; }

.nav-toggle {
  display: none; background: transparent; border: 0; color: #fff; font-size: 22px;
  cursor: pointer; padding: 6px 8px;
}

.usermenu { position: relative; }
.usermenu-btn {
  display: flex; align-items: center; gap: 9px; background: rgba(255,255,255,.12);
  border: 0; color: #fff; padding: 5px 10px 5px 5px; border-radius: 999px; cursor: pointer;
  font: inherit; text-align: left;
}
.usermenu-btn:hover { background: rgba(255,255,255,.2); }
.avatar {
  width: 30px; height: 30px; border-radius: 50%; background: #fff; color: var(--brand-dark);
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: .78rem; flex: 0 0 auto;
}
.usermenu-text { display: flex; flex-direction: column; line-height: 1.2; }
.usermenu-text strong { font-size: .84rem; }
.usermenu-text small { font-size: .72rem; opacity: .8; }
.usermenu-panel {
  position: absolute; right: 0; top: calc(100% + 8px); min-width: 230px; background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-lg); padding: 6px; display: none; z-index: 50;
}
.usermenu.open .usermenu-panel { display: block; }
.usermenu-panel a { display: block; padding: 9px 12px; border-radius: 6px; color: var(--ink); font-size: .88rem; }
.usermenu-panel a:hover { background: var(--well); }
.usermenu-scope {
  padding: 8px 12px; border-bottom: 1px solid var(--line); margin-bottom: 4px;
  font-size: .76rem; color: var(--ink-faint); text-transform: uppercase; letter-spacing: .04em;
}

/* ----------------------------------------------------------------- shell */
.shell { display: flex; align-items: flex-start; min-height: calc(100vh - 60px); }

.sidenav {
  width: 242px; flex: 0 0 242px; background: var(--nav-bg);
  border-right: 1px solid var(--nav-line); padding: 18px 12px 40px;
  position: sticky; top: 60px; max-height: calc(100vh - 60px); overflow-y: auto;
}
.sidenav-group {
  margin: 20px 0 7px; padding: 0 12px; font-size: .67rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--nav-group-ink); font-weight: 700;
}
.sidenav-group:first-child { margin-top: 4px; }
.sidenav ul { list-style: none; margin: 0; padding: 0; }
.sidenav li { margin: 1px 0; }
.sidenav a {
  display: block; padding: 9px 13px; border-radius: var(--radius-sm);
  color: var(--nav-ink);
  text-decoration: none; font-size: .89rem; font-weight: 500;
  transition: background .14s ease, color .14s ease;
}
.sidenav a:hover { background: var(--nav-hover-bg); color: var(--nav-ink-hover); }
.sidenav a.active {
  background: var(--nav-active-bg); color: var(--nav-active-ink); font-weight: 650;
  box-shadow: inset 3px 0 0 var(--nav-marker);
}
.sidenav a:focus-visible { outline: 2px solid var(--nav-focus); outline-offset: -2px; }

.main { flex: 1 1 auto; min-width: 0; padding: 24px 26px 60px; }


.page-head {
  display: flex; align-items: flex-start; gap: 16px; flex-wrap: wrap;
  margin-bottom: 20px;
}
.page-head h1 { margin: 0; }
.page-sub { margin: 5px 0 0; color: var(--ink-soft); font-size: .89rem; }
.page-sub strong { color: var(--ink); font-weight: 650; }
.page-actions { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }

/* ------------------------------------------------------------------ card */
.card {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 20px 22px; margin-bottom: 18px;
}
.card > h2, .card > h3 { margin-top: 0; }
.card-head {
  display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap;
  padding-bottom: 13px; margin-bottom: 16px; border-bottom: 1px solid var(--line);
}
.card-head h2, .card-head h3 { margin: 0; }
/* The line under a card heading is a hairline in the light theme but needs a
   little more presence on a dark card, where a 1px #2b3441 rule all but
   disappears against #151b24. Same token, drawn once, both themes. */
.card-head { border-bottom-color: color-mix(in srgb, var(--line) 100%, transparent); }
.card-head .spacer { margin-left: auto; }
.card-flush { padding: 0; overflow: hidden; }
.card-flush > .card-head { padding: 17px 22px; margin: 0; }
.card-flush > .card-head { align-items: center; }

/* A card heading can carry a second line explaining what the card is for.
   Optional everywhere, so it is a modifier rather than a new heading level. */
.card-sub { margin: 3px 0 0; font-size: .82rem; font-weight: 400; color: var(--ink-faint); letter-spacing: 0; }
.card-head-txt { min-width: 0; }

/* The small uppercase label that sits above a block of figures. It gives a
   section a name without another heading level, which matters on a screen
   that already has an h1, several h2s and a sidenav of links. */
.eyebrow {
  margin: 0 0 10px; font-size: .7rem; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-faint);
  display: flex; align-items: center; gap: 8px;
}
.eyebrow::after { content: ''; flex: 1 1 auto; height: 1px; background: var(--line); }
.eyebrow .icon { width: 14px; height: 14px; }

/* A short pill used to carry a count or a state next to a label. */
.chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px; border-radius: var(--radius-pill);
  background: var(--well); color: var(--ink-soft);
  font-size: .72rem; font-weight: 700; letter-spacing: .02em; white-space: nowrap;
}
.chip .icon { width: 13px; height: 13px; }
.chip-ok   { background: var(--ok-bg);   color: var(--ok); }
.chip-warn { background: var(--warn-bg); color: var(--warn); }
.chip-bad  { background: var(--bad-bg);  color: var(--bad); }
.chip-info { background: var(--info-bg); color: var(--info); }

/* ----------------------------------------------------------------- stats */
.stat-grid {
  display: grid; gap: 16px; margin-bottom: 18px;
  grid-template-columns: repeat(auto-fill, minmax(196px, 1fr));
}
/* A flat tile. Used where a solid tone would be too loud, e.g. a row of small
   secondary figures under the hero. */
.stat {
  position: relative; background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 16px 18px; box-shadow: var(--shadow);
}
.stat-label { font-size: .73rem; text-transform: uppercase; letter-spacing: .07em; color: var(--ink-faint); font-weight: 700; }
.stat-value { font-size: 1.85rem; font-weight: 700; line-height: 1.12; margin-top: 6px; letter-spacing: -.02em; }
.stat-note { font-size: .81rem; color: var(--ink-soft); margin-top: 3px; }
.stat.is-ok   .stat-value { color: var(--ok); }
.stat.is-warn .stat-value { color: var(--warn); }
.stat.is-bad  .stat-value { color: var(--bad); }

/* --- solid tiles -------------------------------------------------------
   One flat colour per tile, never a gradient on the fill itself, because a
   flat block of colour is what makes the number legible. The depth comes from
   a sheen across the top 60% and a soft shadow underneath. The badge is a
   translucent white disc so the icon and the type both keep their contrast
   against the colour underneath. Every tone below clears 4.5:1 against
   white text. */
.stat-card {
  position: relative; display: block; overflow: hidden;
  padding: 17px 18px 18px; border: 0; border-radius: var(--radius);
  color: #fff; text-decoration: none;
  box-shadow: 0 1px 2px rgba(16, 24, 40, .12), 0 8px 20px -6px rgba(16, 24, 40, .22);
  transition: transform .18s cubic-bezier(.22, .8, .3, 1), box-shadow .18s ease;
}
/* The sheen. A single soft highlight so the tile reads as a lit surface. */
.stat-card::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(160deg, rgba(255, 255, 255, .22) 0%, rgba(255, 255, 255, .06) 42%, rgba(255, 255, 255, 0) 68%);
}
/* A hairline at the top edge, which is what stops a flat fill from looking
   like a pasted rectangle. */
.stat-card::after {
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 1px;
  background: rgba(255, 255, 255, .34); pointer-events: none;
}
.stat-card > * { position: relative; z-index: 1; }
a.stat-card:hover, a.stat-card:focus-visible {
  transform: translateY(-3px); color: #fff;
  box-shadow: 0 2px 4px rgba(16, 24, 40, .14), 0 18px 34px -10px rgba(16, 24, 40, .32);
}
a.stat-card:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

.stat-card .stat-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; margin-bottom: 12px; border-radius: 12px;
  background: rgba(255, 255, 255, .22);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .18);
}
.stat-card .stat-icon svg { width: 20px; height: 20px; }
.stat-card .stat-label { display: block; color: rgba(255, 255, 255, .88); }
.stat-card .stat-value { display: block; color: #fff; }
.stat-card .stat-note  { display: block; color: rgba(255, 255, 255, .82); }

.stat-card .stat-value.is-text { font-size: 1.15rem; line-height: 1.4; letter-spacing: -.01em; }

/* An optional foot line on a tile: a small tag on the left, a link on the
   right. Used for "jump to the filtered list this number describes". */
.stat-foot {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 12px; padding-top: 11px; font-size: .78rem;
  border-top: 1px solid rgba(255, 255, 255, .2); color: rgba(255, 255, 255, .88);
}
.stat-foot .spacer { margin-left: auto; }
.stat-go {
  display: inline-flex; align-items: center; gap: 4px; font-weight: 700;
  color: #fff; text-decoration: none; white-space: nowrap;
}
.stat-go .icon { width: 14px; height: 14px; transition: transform .18s ease; }
a.stat-card:hover .stat-go .icon { transform: translateX(3px); }

/* Progress read-out that also works on a coloured tile. */
.stat-meter {
  height: 6px; margin-top: 12px; border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, .28); overflow: hidden;
}
/* The dark theme keeps the same solid tiles, so the meter track stays white-
   translucent against them. The one thing that has to change is the value
   colour, which is white on a coloured tile in both themes. */
.stat-meter > span { display: block; height: 100%; background: #fff; border-radius: var(--radius-pill); }

/* Tile colours. Every one of these is a solid fill that clears 4.5:1 against
   white text, which is the constraint the whole set is built around. */
.tone-blue   { background: #1a56a8; }
.tone-indigo { background: #3730a3; }
.tone-violet { background: #6d28d9; }
.tone-teal   { background: #0f766e; }
.tone-green  { background: #137333; }
.tone-olive  { background: #4d7c0f; }
.tone-amber  { background: #b45309; }
.tone-orange { background: #c2410c; }
.tone-rose   { background: #be123c; }
.tone-cyan   { background: #0e7490; }
.tone-slate  { background: #475569; }
.tone-red    { background: #b3261e; }


/* ------------------------------------------------------------------ icons */
.icon { display: inline-block; vertical-align: middle; flex: 0 0 auto; }
.btn .icon, .badge .icon, a .icon { width: 16px; height: 16px; }

/* ------------------------------------------------------------------- hero
   The one gradient in the panel, because it is the one element that is a
   masthead rather than a box of data. Everything inside it uses the
   --banner-* tokens so it still follows the dark theme. */
.hero {
  position: relative; overflow: hidden;
  display: flex; align-items: center; gap: 24px; flex-wrap: wrap;
  padding: 24px 26px; margin-bottom: 20px;
  background:
    radial-gradient(760px 420px at 86% -22%, var(--banner-glow) 0%, rgba(0, 0, 0, 0) 62%),
    linear-gradient(152deg, var(--banner-a) 0%, var(--banner-b) 52%, var(--banner-c) 100%);
  color: var(--banner-ink);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}
/* A second bloom in the opposite corner, so the gradient has a light source
   and a falloff rather than reading as a flat ramp. */
.hero::after {
  content: ''; position: absolute; width: 420px; height: 420px;
  right: -160px; bottom: -230px; border-radius: 50%;
  background: rgba(255, 255, 255, .06); pointer-events: none;
}
.hero > * { position: relative; z-index: 1; }

.hero-eyebrow {
  display: inline-flex; align-items: center; gap: 7px;
  margin: 0 0 8px; padding: 3px 11px 3px 8px;
  font-size: .71rem; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--banner-ink-soft);
  background: var(--banner-glass); border: 1px solid var(--banner-line);
  border-radius: var(--radius-pill);
}
.hero-eyebrow .icon { width: 13px; height: 13px; }
.hero h2 { margin: 0 0 6px; font-size: 1.6rem; letter-spacing: -.028em; color: #fff; }
.hero-sub { margin: 0; color: var(--banner-ink-soft); font-size: .93rem; max-width: 66ch; line-height: 1.6; }
.hero-sub strong { color: #fff; font-weight: 650; }
.hero-actions { margin-left: auto; display: flex; gap: 9px; flex-wrap: wrap; }

/* The three figures inside the banner: how much is published, how much of the
   university is covered, and what is waiting. They sit on a glass strip so
   they read as part of the banner rather than as tiles dropped inside it. */
.hero-glance {
  display: flex; gap: 0; flex-wrap: wrap; width: 100%;
  margin-top: 20px; padding: 4px 0 0;
  border-top: 1px solid var(--banner-line);
}
.hero-glance-item {
  flex: 1 1 190px; min-width: 0;
  padding: 14px 20px 4px 0;
}
.hero-glance-item + .hero-glance-item { padding-left: 20px; border-left: 1px solid var(--banner-line); }
.hero-glance-label {
  display: block; font-size: .7rem; font-weight: 700; letter-spacing: .09em;
  text-transform: uppercase; color: var(--banner-ink-faint);
}
.hero-glance-value {
  display: block; margin-top: 3px; font-size: 1.32rem; font-weight: 700;
  line-height: 1.2; color: #fff; letter-spacing: -.02em;
}
.hero-glance-note { display: block; margin-top: 1px; font-size: .78rem; color: var(--banner-ink-soft); }
.hero-glance-value .hero-glance-unit { font-size: .78rem; font-weight: 600; color: var(--banner-ink-soft); letter-spacing: 0; }

/* Buttons sitting on the banner need their own colours. btn-primary is the
   brand blue, which on a blue banner is a shape rather than a button; here the
   primary action is solid white and the secondary is a glass pill. Both keep
   4.5:1 against the banner behind them. */
.hero .btn-primary { background: #fff; color: var(--banner-c); box-shadow: 0 4px 12px rgba(0, 0, 0, .22); }
.hero .btn-primary:hover { background: #fff; color: var(--banner-c); filter: brightness(.95); }
.hero .btn-default {
  background: var(--banner-glass); color: #fff; border-color: var(--banner-line);
}
.hero .btn-default:hover { background: rgba(255, 255, 255, .2); color: #fff; }
.hero .btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }


/* --------------------------------------------------------- status summary */
.statusbar {
  display: flex; height: 10px; border-radius: 999px; overflow: hidden;
  background: var(--chart-track); margin-bottom: 12px;
}
.statusbar > span { display: block; height: 100%; }
.statusbar .seg-published  { background: var(--ok); }
.statusbar .seg-pending    { background: var(--warn); }
.statusbar .seg-draft      { background: var(--ink-faint); }
.statusbar .seg-rejected   { background: var(--bad); }
.statusbar .seg-archived   { background: #64748b; }

.legend { list-style: none; display: flex; flex-wrap: wrap; gap: 6px 20px; margin: 0; font-size: .84rem; }
.legend li { display: flex; align-items: center; gap: 7px; color: var(--ink-soft); }
.legend .dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; }
.legend .legend-num { color: var(--ink); font-weight: 700; font-variant-numeric: tabular-nums; }

/* -------------------------------------------------------------- mini list */
.mini-list { list-style: none; margin: 0; padding: 0; }
.mini-list > li {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 0; border-bottom: 1px solid var(--line); font-size: .88rem;
}
.mini-list > li:last-child { border-bottom: 0; }
.mini-list .mini-main { min-width: 0; }
.mini-list .mini-title {
  display: block; color: var(--ink); font-weight: 600; text-decoration: none;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mini-list a.mini-title:hover { color: var(--brand); text-decoration: underline; }
.mini-list .mini-sub { display: block; color: var(--ink-faint); font-size: .79rem; }
.mini-list .mini-end {
  margin-left: auto; text-align: right; flex: 0 0 auto;
  display: flex; align-items: center; gap: 9px; color: var(--ink-faint);
}
.mini-list .mini-end .icon { width: 15px; height: 15px; transition: transform .16s ease; }
.mini-list li:hover .mini-end .icon { transform: translateX(3px); color: var(--brand); }
.mini-list .legend-num { font-size: 1.02rem; color: var(--ink); }

.feed { list-style: none; margin: 0; padding: 0; }
.feed > li {
  position: relative; padding: 0 0 14px 24px; font-size: .85rem; color: var(--ink-soft);
}
.feed > li::before {
  content: ''; position: absolute; left: 3px; top: 5px; width: 9px; height: 9px;
  border-radius: 50%; background: var(--brand); box-shadow: 0 0 0 4px var(--brand-light);
}
.feed > li::after {
  content: ''; position: absolute; left: 7px; top: 16px; bottom: 0; width: 1px;
  background: linear-gradient(var(--line), var(--line-soft));
}
.feed > li:last-child { padding-bottom: 0; }
.feed > li:last-child::after { display: none; }
.feed strong { color: var(--ink); font-weight: 650; }
.feed .feed-when { display: block; font-size: .75rem; color: var(--ink-faint); margin-top: 1px; }


/* A row the import preview will skip, e.g. a duplicate name. */
tr.row-skip td { background: var(--bad-bg); }
tr.row-skip td:first-child { box-shadow: inset 3px 0 0 var(--bad); }

/* ----------------------------------------------------------------- table */
.table-wrap { overflow-x: auto; }
table.data { width: 100%; border-collapse: collapse; font-size: .88rem; }
table.data th, table.data td {
  padding: 11px 13px; text-align: left; border-bottom: 1px solid var(--line); vertical-align: middle;
}
table.data thead th {
  background: var(--card-2); font-size: .7rem; text-transform: uppercase; letter-spacing: .07em;
  color: var(--ink-soft); font-weight: 700; white-space: nowrap;
  border-bottom-color: var(--line);
}
table.data tbody tr { transition: background .12s ease; }
table.data tbody tr:hover { background: var(--card-3); }
/* The last row has nothing under it to divide from. */
table.data tbody tr:last-child > td { border-bottom: 0; }
table.data td.num { text-align: right; font-variant-numeric: tabular-nums; }
table.data .tight { width: 1%; white-space: nowrap; }
table.data caption { caption-side: top; text-align: left; padding-bottom: 10px; color: var(--ink-soft); }
.table-empty { padding: 34px 24px; text-align: center; color: var(--ink-faint); }

/* An empty state that says what to do next, not just that there is nothing.
   The icon sits on a soft disc so the row is not a bare line of grey text. */
.table-empty-rich {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  padding: 40px 24px; text-align: center; color: var(--ink-soft);
}
.table-empty-rich .empty-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 52px; height: 52px; border-radius: 50%;
  background: var(--ok-bg); color: var(--ok);
}
.table-empty-rich .empty-icon.is-warn { background: var(--warn-bg); color: var(--warn); }
.table-empty-rich .empty-icon.is-brand { background: var(--brand-light); color: var(--brand); }
.table-empty-rich .empty-icon.is-bad { background: var(--bad-bg); color: var(--bad); }
.table-empty-rich .empty-icon svg { width: 25px; height: 25px; }
.table-empty-rich strong { display: block; color: var(--ink); font-size: .98rem; }
.table-empty-rich p { margin: 0; max-width: 46ch; font-size: .86rem; line-height: 1.6; }
/* The button that closes the gap the empty state has just named. An empty
   panel that only says "nothing here" makes the reader leave to find the work
   somewhere else; this is where the work is started. */
.table-empty-rich .empty-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }


th.sortable a { color: inherit; text-decoration: none; }
th.sortable a::after { content: " \2195"; opacity: .35; }
th.sortable a.sorted-asc::after  { content: " \2191"; opacity: 1; }
th.sortable a.sorted-desc::after { content: " \2193"; opacity: 1; }
tr.muted-row td { opacity: .55; }

/* -------------------------------------------------- Scan a page filter bits */
/* Belt and braces: the filter hides rows with [hidden]. No author rule sets
   display on tr/tbody today, but a stray one would otherwise beat the UA sheet
   and leave filtered rows on screen. */
[hidden] { display: none !important; }

/* Section band inside a table, e.g. all the Departments together. */
tr.group-row th {
  background: var(--card-2); color: var(--ink); font-size: .78rem; text-transform: uppercase;
  letter-spacing: .05em; padding: 9px 14px; border-bottom: 1px solid var(--line);
}
tr.group-row .legend-num { color: var(--ink-soft); margin-left: 4px; }
tr.group-row th:hover { background: var(--card-2); }

/* A link the user copies by hand, e.g. the one-off scanning URL. */
.code-snippet {
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
  padding: 11px 13px; background: var(--card-2); border: 1px solid var(--line);
  border-radius: var(--radius); margin: 0 0 12px;
}
.code-snippet code {
  flex: 1 1 260px; min-width: 0; font-family: var(--mono); font-size: .82rem;
  color: var(--ink); word-break: break-all;
}

/* A scan is starting in a new tab: dim the control so it is obvious it fired. */
a.is-busy, button.is-busy { opacity: .5; pointer-events: none; }

/* ------------------------------------------------------- Scan a page screen */
.scan-tiles { grid-template-columns: repeat(auto-fill, minmax(178px, 1fr)); }
.scan-tiles .stat-value { font-size: 1.9rem; }

/* The page list is the main object on this screen, so it gets the full table
   treatment: pinned headings, a calmer body, and a hover that reads as a row you
   can act on. */
.scan-table { border-collapse: separate; border-spacing: 0; }
.scan-table thead th {
  position: sticky; top: 0; z-index: 2;
  background: var(--card); color: var(--ink-soft);
  font-size: .72rem; text-transform: uppercase; letter-spacing: .06em;
  white-space: nowrap;
}
.scan-table tbody tr:not(.group-row):hover > td { background: var(--card-3); }
.scan-table tbody td { vertical-align: top; padding-top: 11px; padding-bottom: 11px; }

/* Page title, with the file name as a quiet chip underneath it. */
.scan-title { font-weight: 600; color: var(--ink); }
.scan-title:hover { color: var(--brand); }
.scan-file {
  display: inline-block; margin-top: 3px; padding: 1px 7px;
  background: var(--well); border-radius: 5px;
  font-family: var(--mono); font-size: .74rem; color: var(--ink-soft);
}

/* A row that is a byte-for-byte copy of another page file. */
.scan-row-twin > td { background: var(--warn-bg); }
.scan-row-twin > td:first-child { box-shadow: inset 3px 0 0 var(--warn); }
.scan-row-twin:hover > td { background: var(--warn-bg); }

/* A page the scanner can never read. Kept out of the list on purpose, so the
   list above only ever holds pages that will actually work. */
.scan-locked { color: var(--ink-faint); font-size: .8rem; }

/* ---------------------------------------------------------- Staff records */
.staff-tiles { grid-template-columns: repeat(auto-fill, minmax(178px, 1fr)); }
.staff-tiles .stat-value { font-size: 1.9rem; }

/* Same treatment as the scanner list: pinned headings, calm body, row hover. */
.staff-table { border-collapse: separate; border-spacing: 0; }
.staff-table thead th {
  position: sticky; top: 0; z-index: 2;
  background: var(--card); color: var(--ink-soft);
  font-size: .72rem; text-transform: uppercase; letter-spacing: .06em;
  white-space: nowrap;
}
.staff-table tbody tr:hover > td { background: var(--card-3); }
.staff-table tbody td { vertical-align: top; padding-top: 11px; padding-bottom: 11px; }

.staff-name { font-weight: 600; color: var(--ink); }
.staff-name:hover { color: var(--brand); }

/* ------------------------------------------- Export and reports */
.export-tiles { grid-template-columns: repeat(auto-fill, minmax(186px, 1fr)); }

/* The gap list: one block per faculty, each holding the departments that still
   need staff. The chips are the links, so they need to look pressable. */
.miss-group + .miss-group { border-top: 1px solid var(--line); padding-top: 4px; }
.miss-group h3 { font-size: .82rem; letter-spacing: .03em; color: var(--ink-soft); }
.miss-group a.badge { text-decoration: none; }
.miss-group a.badge:hover { background: var(--brand-light); color: var(--brand-dark); text-decoration: underline; }

/* ------------------------------------------------------------------ charts */
/* Server-rendered SVG, so a chart costs no script, no library and no network
   round trip, and it still prints. These rules only frame the markup the
   helpers emit: the geometry is inline attributes on each shape. */
.chart { display: block; width: 100%; height: auto; overflow: visible; }
.chart-legend {
  list-style: none; margin: 10px 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 6px 18px; font-size: .82rem; color: var(--ink-soft);
}
.chart-legend li { display: flex; align-items: center; gap: 7px; }
.chart-legend .swatch { width: 9px; height: 9px; border-radius: 3px; flex: 0 0 auto; }
.chart-legend .legend-num { color: var(--ink); font-weight: 700; font-variant-numeric: tabular-nums; }
.chart-legend .legend-name { min-width: 0; }
.chart-note { font-size: .79rem; color: var(--ink-faint); margin: 10px 0 0; line-height: 1.55; }

/* The three "how are we doing" cards. Equal columns so the donut, the bar
   chart and the sparkline line up as a row rather than one card dominating. */
.grid-charts {
  display: grid; gap: 18px; margin-bottom: 18px;
  grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
  align-items: stretch;
}
.grid-charts > .card { margin-bottom: 0; height: 100%; display: flex; flex-direction: column; }
.grid-charts > .card > .chart-note { margin-top: auto; padding-top: 12px; }

/* A quieter tier of tiles: the inputs to completeness rather than the picture
   of the whole, so they sit below the headline row and read as supporting. */
.stat-grid-slim { gap: 12px; margin-bottom: 20px; }
.stat-grid-slim .stat-card { box-shadow: none; padding: 14px 15px 15px; }
.stat-grid-slim .stat-card::before { opacity: .7; }
.stat-grid-slim .stat-card .stat-icon { width: 32px; height: 32px; border-radius: 10px; }
.stat-grid-slim .stat-card .stat-icon svg { width: 17px; height: 17px; }
.stat-grid-slim .stat-card .stat-value { font-size: 1.5rem; }
.stat-grid-slim .stat-card .stat-meter { margin-top: 10px; height: 5px; }

/* --------------------------------------------------------------- checklist
   A tick-or-cross list of the fields on one record. Used instead of a plain
   "these are missing" list because a person cannot tell from a warning how close
   they are, and "8 of 9 done" is a different message from "9 items outstanding"
   even when they describe the same page. */
.checklist { list-style: none; margin: 0; padding: 0; }
.checklist > li {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 0; border-bottom: 1px solid var(--line-soft);
}
.checklist > li:last-child { border-bottom: 0; }
.check-mark {
  width: 22px; height: 22px; border-radius: 50%; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
}
.check-mark .icon { width: 13px; height: 13px; }
.checklist .is-done .check-mark { background: var(--ok-bg); color: var(--ok); }
.checklist .is-todo .check-mark { background: var(--warn-bg); color: var(--warn); }
.check-label { min-width: 0; flex: 1 1 auto; font-size: .89rem; }
.checklist .is-done .check-label { color: var(--ink-soft); }
.checklist .is-todo .check-label { color: var(--ink); font-weight: 550; }
.check-state {
  flex: 0 0 auto; font-size: .72rem; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase;
}
.checklist .is-done .check-state { color: var(--ok); }
.checklist .is-todo .check-state { color: var(--warn); }

/* ----------------------------------------------------------- figure triplet
   Three plain figures in a row, for panels where a full stat tile would be
   louder than the sentence it sits under. */
.figure-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px;
}
.figure-box {
  background: var(--card-2); border: 1px solid var(--line);
  border-radius: var(--radius-sm); padding: 12px 14px 13px;
}
.figure-value {
  display: block; font-family: var(--font-display); font-size: 1.24rem;
  font-weight: 700; line-height: 1.2; letter-spacing: -.02em;
}
.figure-label {
  display: block; margin-top: 3px; font-size: .78rem; color: var(--ink-soft);
}
.figure-note {
  display: block; margin-top: 4px; font-size: .74rem; color: var(--ink-faint);
  line-height: 1.45;
}
.figure-box.tone-ok  { border-color: color-mix(in srgb, var(--ok) 34%, var(--line)); }
.figure-box.tone-ok  .figure-value { color: var(--ok); }
.figure-box.tone-bad { border-color: color-mix(in srgb, var(--bad) 34%, var(--line)); }
.figure-box.tone-bad .figure-value { color: var(--bad); }

/* ----------------------------------------------------------- column chart
   One column per period, for a series that runs over time. Not the ranked bar
   list: that one reorders by value and prints percentages, neither of which is
   right for "how many papers came out in 2019". Built from HTML rather than SVG
   so the period labels wrap normally and the numbers stay real page text. */
.columns {
  display: flex; align-items: flex-end; gap: 6px;
  height: 132px; padding-top: 4px;
}
.column {
  flex: 1 1 0; min-width: 0; height: 100%;
  display: flex; flex-direction: column; justify-content: flex-end; align-items: center;
}
.column-value {
  font-size: .74rem; font-weight: 650; color: var(--ink-soft);
  line-height: 1.2; margin-bottom: 3px;
}
.column.is-zero .column-value { color: var(--ink-faint); font-weight: 500; }
/* The track is the full height of the column and the fill grows up from the
   bottom of it, so a bigger number is a taller bar rather than a taller box. */
.column-track {
  width: 100%; max-width: 54px; flex: 1 1 auto;
  display: flex; align-items: flex-end;
  background: var(--chart-track); border-radius: 5px 5px 0 0;
}
.column-fill { display: block; width: 100%; border-radius: 5px 5px 0 0; min-height: 2px; }
.column-label {
  margin-top: 6px; font-size: .7rem; color: var(--ink-faint);
  text-align: center; line-height: 1.25; overflow-wrap: anywhere;
}
@media (max-width: 600px) {
  .columns { gap: 3px; height: 112px; }
  .column-value { font-size: .68rem; }
  .column-label { font-size: .64rem; }
}

/* Donut: ring, headline number in the hole, legend beside it. The hole is a
   positioned box so the number can be ordinary text over the SVG rather than
   an SVG <text> element, which keeps it selectable and translatable. */
.donut-wrap { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.donut-hole { position: relative; width: 138px; height: 138px; flex: 0 0 138px; }
.donut-hole .chart { width: 100%; height: 100%; }
.donut-centre-num {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  font-size: 1.6rem; font-weight: 700; color: var(--ink);
  font-variant-numeric: tabular-nums; line-height: 1; letter-spacing: -.03em;
}
.donut-hole:has(.donut-centre-label) .donut-centre-num { top: 41%; font-size: 1.38rem; }
.donut-centre-label {
  position: absolute; left: 50%; top: 62%; transform: translateX(-50%);
  font-size: .67rem; text-transform: uppercase; letter-spacing: .09em;
  color: var(--ink-faint); font-weight: 700; white-space: nowrap;
}
.donut-side { min-width: 0; flex: 1 1 150px; }

/* Horizontal ranked bars, used for "worst first" lists. */
.barlist { list-style: none; margin: 0; padding: 0; }
.barlist > li { padding: 7px 0; }
.barlist .bar-top {
  display: flex; align-items: baseline; gap: 8px; margin-bottom: 6px;
  font-size: .85rem; color: var(--ink);
}
.barlist .bar-top .spacer { margin-left: auto; }
.barlist .bar-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.barlist .bar-val { color: var(--ink-soft); font-variant-numeric: tabular-nums; font-size: .8rem; font-weight: 700; }
.barlist .bar-track {
  height: 9px; border-radius: var(--radius-pill); background: var(--chart-track);
  overflow: hidden; box-shadow: inset 0 1px 2px rgba(16, 24, 40, .05);
}
.barlist .bar-fill {
  display: block; height: 100%; min-width: 0; border-radius: var(--radius-pill);
  background: var(--brand);
  transition: width .5s cubic-bezier(.22, .8, .3, 1);
}
.barlist .bar-fill.is-ok   { background: var(--ok); }
.barlist .bar-fill.is-warn { background: var(--warn); }
.barlist .bar-fill.is-bad  { background: var(--bad); }

/* A row that counts something and also has a bar in the progress column. */
.prog-cell { display: flex; align-items: center; gap: 9px; min-width: 130px; }
.prog-cell .progress { flex: 1 1 auto; }
.prog-cell .prog-num { flex: 0 0 auto; font-size: .78rem; font-weight: 700; color: var(--ink-soft); min-width: 34px; text-align: right; }

/* Sparkline strip: a compact trend with a caption, no axis. */
.spark-wrap { display: flex; align-items: center; gap: 14px; }
.spark-wrap .chart { height: 38px; width: 100%; }
.spark-cap {
  font-size: .78rem; color: var(--ink-faint); flex: 0 0 auto;
  padding: 5px 10px; background: var(--well); border-radius: var(--radius-sm);
  white-space: nowrap;
}
.spark-cap strong { color: var(--ink); }

/* A single percentage, drawn as a ring. Used where the whole figure is one
   ratio, e.g. how much of the university's department list has anybody in it.
   A bar would need a fictitious axis to have a height; a ring is the same
   information with no scale on it at all. */
.ring { position: relative; display: inline-grid; place-items: center; }
.ring > svg { display: block; }
.ring .ring-track { fill: none; stroke: var(--chart-track); }
.ring .ring-fill {
  fill: none; stroke: var(--brand); stroke-linecap: round;
  transform: rotate(-90deg); transform-origin: 50% 50%;
  transition: stroke-dashoffset .6s cubic-bezier(.22, .8, .3, 1);
}
.ring.is-ok   .ring-fill { stroke: var(--ok); }
.ring.is-warn .ring-fill { stroke: var(--warn); }
.ring.is-bad  .ring-fill { stroke: var(--bad); }
.ring-body { position: absolute; inset: 0; display: grid; place-content: center; text-align: center; line-height: 1.15; }
.ring-num { font-size: 1.28rem; font-weight: 700; letter-spacing: -.03em; color: var(--ink); }
.ring-label { display: block; font-size: .66rem; text-transform: uppercase; letter-spacing: .07em; color: var(--ink-faint); font-weight: 700; }

/* Ring on the left, the breakdown it summarises on the right. Collapses to a
   single column on narrow screens so the bars keep their full width. */
.ring-panel {
  display: grid; gap: 22px; align-items: start;
  grid-template-columns: 190px minmax(0, 1fr);
}
.ring-panel-aside { text-align: center; }
.ring-panel-aside .chart-note { margin: 12px 0 0; }
.ring-panel-main { min-width: 0; }

/* The "what to do next" list. Each row is a sentence about the state of the
   data plus the one action that moves it, so a manager reading the dashboard
   top to bottom ends up with a list of jobs rather than a list of numbers. */
.actions { list-style: none; margin: 0; padding: 0; }
.actions > li {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 13px 0; border-bottom: 1px solid var(--line);
}
.actions > li:first-child { padding-top: 2px; }
.actions > li:last-child { border-bottom: 0; padding-bottom: 2px; }
.action-mark {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: 10px;
  background: var(--brand-light); color: var(--brand);
}
.action-mark .icon { width: 17px; height: 17px; }
.action-mark.is-bad  { background: var(--bad-bg);  color: var(--bad); }
.action-mark.is-warn { background: var(--warn-bg); color: var(--warn); }
.action-mark.is-ok   { background: var(--ok-bg);   color: var(--ok); }
.action-body { min-width: 0; flex: 1 1 auto; }
.action-body strong { display: block; font-weight: 650; font-size: .89rem; line-height: 1.45; }
.action-body span { display: block; margin-top: 2px; font-size: .81rem; color: var(--ink-soft); line-height: 1.5; }
.action-end { flex: 0 0 auto; align-self: center; }


/* ------------------------------------------------------ Import staff records */
.import-tiles { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }

/* A tile with no number, just a label and a line of explanation, so it sits
   the same height as the counted tiles above it. */
.import-tiles .stat-note { margin-top: 6px; line-height: 1.5; }
.import-tiles .stat-label { margin-top: 2px; }

/* The heading list is reference material, so it stays folded away until it is
   actually needed. */
.import-ref { padding: 0; }
.import-ref > summary {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: 15px 20px; cursor: pointer; list-style: none;
  font-weight: 700; font-size: .95rem; color: var(--ink);
  border-radius: var(--radius);
}
.import-ref > summary::-webkit-details-marker { display: none; }
.import-ref > summary::after {
  content: 'Show'; margin-left: auto; font-size: .8rem; font-weight: 600;
  color: var(--brand); white-space: nowrap;
}
.import-ref[open] > summary::after { content: 'Hide'; }
.import-ref > summary:hover { background: var(--card-2); }
.import-ref > summary:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.import-ref[open] > summary { border-bottom: 1px solid var(--line); border-radius: var(--radius) var(--radius) 0 0; }
.import-ref > .table-wrap, .import-ref > p { margin: 0 20px; }
.import-ref > p { margin-bottom: 16px; }

/* A form laid out inside a table cell, e.g. the per-row controls on User accounts. */
.inline-form { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.inline-form select { max-width: 190px; }

/* ----------------------------------------------------------------- badge */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 10px; border-radius: var(--radius-pill); font-size: .72rem;
  font-weight: 700; letter-spacing: .02em; white-space: nowrap;
}
.badge .icon { width: 12px; height: 12px; }
.badge-success { background: var(--ok-bg);   color: var(--ok); }
.badge-warning { background: var(--warn-bg); color: var(--warn); }
.badge-danger  { background: var(--bad-bg);  color: var(--bad); }
.badge-info    { background: var(--info-bg); color: var(--info); }
.badge-muted   { background: var(--well);    color: var(--ink-soft); }

/* ---------------------------------------------------------------- buttons */
.btn {
  display: inline-flex; align-items: center; gap: 7px; justify-content: center;
  padding: 8px 15px; border-radius: var(--radius-sm); border: 1px solid transparent;
  font: inherit; font-size: .88rem; font-weight: 600; cursor: pointer;
  text-decoration: none; white-space: nowrap;
  transition: background-color .15s ease, border-color .15s ease, transform .15s ease, box-shadow .15s ease;
}
.btn .icon { width: 15px; height: 15px; }
.btn:disabled, .btn.disabled { opacity: .55; cursor: not-allowed; transform: none; }
.btn-primary   { background: var(--brand); color: var(--brand-on); box-shadow: 0 1px 2px rgba(16, 24, 40, .10); }
.btn-primary:hover { background: var(--brand-dark); color: var(--brand-on); transform: translateY(-1px); box-shadow: 0 4px 10px -2px rgba(16, 24, 40, .22); }
.btn-default   { background: var(--field-bg); color: var(--ink); border-color: var(--field-border); box-shadow: var(--shadow); }
.btn-default:hover { background: var(--card-2); color: var(--ink); }
.btn-danger    { background: var(--bad); color: var(--ink-invert); }
.btn-danger:hover { background: var(--bad); color: var(--ink-invert); filter: brightness(1.1); }
.btn-ok        { background: var(--ok); color: var(--ink-invert); }
.btn-ok:hover { background: var(--ok); color: var(--ink-invert); filter: brightness(1.1); }
.btn-ghost     { background: transparent; color: var(--ink-soft); border-color: transparent; }
.btn-ghost:hover { background: var(--well); color: var(--ink); }
.btn-sm        { padding: 5px 11px; font-size: .8rem; border-radius: 7px; }
.btn-lg        { padding: 11px 20px; font-size: .92rem; }
.btn-block     { width: 100%; }
.btn-row       { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }


/* ------------------------------------------------------------------ forms */
.field { margin-bottom: 15px; }
.field > label, .label {
  display: block; margin-bottom: 5px; font-weight: 650; font-size: .84rem; color: var(--ink);
}
.hint { display: block; margin-top: 4px; font-size: .78rem; color: var(--ink-faint); }
.req::after { content: " *"; color: var(--bad); }

input[type=text], input[type=email], input[type=password], input[type=url], input[type=number],
input[type=date], input[type=tel], input[type=search], input[type=file], select, textarea {
  width: 100%; padding: 8px 10px; border: 1px solid var(--field-border); border-radius: 6px;
  font: inherit; font-size: .9rem; color: var(--ink); background: var(--field-bg);
}
textarea { min-height: 110px; resize: vertical; line-height: 1.55; }
textarea.mono { font-family: var(--mono); font-size: .84rem; }
select { appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--ink-faint) 50%), linear-gradient(135deg, var(--ink-faint) 50%, transparent 50%); background-position: calc(100% - 16px) 50%, calc(100% - 11px) 50%; background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; padding-right: 32px; }
input:focus, select:focus, textarea:focus { outline: 2px solid var(--brand); outline-offset: 0; border-color: var(--brand); opacity: 1; }
input[disabled], select[disabled], textarea[disabled] { background: var(--well); color: var(--ink-faint); }
input[type=checkbox], input[type=radio] { width: auto; margin-right: 7px; }
input[type=color] { width: 60px; height: 34px; padding: 2px; }

.check { display: flex; align-items: flex-start; gap: 6px; font-size: .88rem; }
.check label { font-weight: 400; margin: 0; }

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0 16px; }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0 16px; }
/* A field that needs the whole row of a grid - a long address, a note - rather
   than one narrow column of it. */
.grid-2 > .span-2, .grid-3 > .span-2, .grid-4 > .span-2 { grid-column: 1 / -1; }
/* Dashboard: a wide working column beside a narrow one. */
.grid-main { display: grid; grid-template-columns: minmax(0, 2.05fr) minmax(0, 1fr); gap: 0 18px; align-items: start; }
.grid-main > * { min-width: 0; }
@media (max-width: 900px) { .grid-3, .grid-4 { grid-template-columns: 1fr 1fr; } }
@media (max-width: 1100px) { .grid-main { grid-template-columns: 1fr; } }
@media (max-width: 640px) { .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } }
@media (max-width: 780px) { .ring-panel { grid-template-columns: 1fr; } .ring-panel-aside { max-width: 320px; margin: 0 auto; } }

/* Totals line under a data table. */
table.data tfoot td {
  background: var(--card-2); font-weight: 700; color: var(--ink-soft);
  border-bottom: 0; border-top: 1px solid var(--line);
}
table.data tfoot td.num { font-variant-numeric: tabular-nums; }

.fieldset {
  border: 1px solid var(--line); border-radius: var(--radius); padding: 16px 18px; margin: 0 0 18px;
}
.fieldset > legend {
  font-size: .78rem; text-transform: uppercase; letter-spacing: .06em; font-weight: 700;
  color: var(--ink-soft); padding: 0 6px;
}

.form-actions {
  position: sticky; bottom: 0; background: var(--card); border-top: 1px solid var(--line);
  padding: 12px 20px; margin: 20px -20px -18px; display: flex; gap: 10px; align-items: center;
  border-radius: 0 0 var(--radius) var(--radius);
}
.form-actions .spacer { margin-left: auto; }

/* ----------------------------------------------------------------- alerts */
.alert {
  padding: 13px 16px; border-radius: var(--radius-sm); margin-bottom: 16px; font-size: .89rem;
  border: 1px solid transparent;
}
/* Alert borders are drawn from the status colour at low alpha so they follow
   whichever theme and whichever status is showing. The plain line first is a
   fallback for browsers without color-mix(); the mix() line replaces it. */
.alert-success { background: var(--ok-bg);   color: var(--ok);   border-color: var(--ok); border-color: color-mix(in srgb, var(--ok) 34%, transparent); }
.alert-error   { background: var(--bad-bg);  color: var(--bad);  border-color: var(--bad); border-color: color-mix(in srgb, var(--bad) 34%, transparent); }
.alert-warning { background: var(--warn-bg); color: var(--warn); border-color: var(--warn); border-color: color-mix(in srgb, var(--warn) 34%, transparent); }
.alert-info    { background: var(--info-bg); color: var(--info); border-color: var(--info); border-color: color-mix(in srgb, var(--info) 34%, transparent); }
.alert ul { margin: 6px 0 0; padding-left: 20px; }

.callout {
  background: var(--brand-light);
  border: 1px solid color-mix(in srgb, var(--brand) 26%, transparent);
  border-left: 4px solid var(--brand);
  padding: 15px 18px; border-radius: var(--radius-sm); margin-bottom: 16px; font-size: .89rem;
}
.callout h3 { margin: 0 0 6px; font-size: .95rem; }
.callout p:last-child { margin-bottom: 0; }
.callout .icon { color: var(--brand); }


/* ------------------------------------------------------------------ misc */
.muted { color: var(--ink-faint); }
.small { font-size: .82rem; }
.mono  { font-family: var(--mono); font-size: .84rem; }
.nowrap { white-space: nowrap; }
.right  { text-align: right; }
.center { text-align: center; }
/* Lets a flex child shrink instead of forcing its parent wider. Needed wherever
   a long faculty or department name sits inside a flex row. */
.min-0   { min-width: 0; }
.mt-0 { margin-top: 0; }
.mt-1 { margin-top: 8px; }
.mt-2 { margin-top: 16px; }
.mt-3 { margin-top: 24px; }
.mb-0 { margin-bottom: 0; }
.mb-1 { margin-bottom: 8px; }
.mb-2 { margin-bottom: 16px; }
.flex { display: flex; gap: 12px; align-items: center; }
.flex-wrap { flex-wrap: wrap; }
.flex-between { justify-content: space-between; }
.spacer { margin-left: auto; }

/* An alert that carries an icon: the icon is a flex item so it aligns with the
   first line of text instead of sitting on the text baseline. */
.alert .icon { width: 17px; height: 17px; flex: 0 0 auto; }
.alert-icon { display: flex; gap: 10px; align-items: flex-start; }

.toolbar {
  display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end;
  padding: 14px 16px; background: var(--card-2); border: 1px solid var(--line);
  border-radius: var(--radius); margin-bottom: 16px;
}
.toolbar .field { margin: 0; min-width: 150px; }
.toolbar .field.grow { flex: 1 1 220px; }

/* The filters that are actually in force, each one a link that drops it.
   A row of selects on its first option looks the same whether nothing is
   filtered or everything is; these say it in words instead. */
.filter-chips {
  display: flex; gap: 7px; flex-wrap: wrap; align-items: center;
  margin: -4px 0 14px;
}
.filter-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px; border-radius: var(--radius-pill);
  background: var(--brand-light); color: var(--brand-dark);
  border: 1px solid transparent;
  font-size: .8rem; font-weight: 650; text-decoration: none;
  transition: background .14s ease, border-color .14s ease;
}
.filter-chip:hover { border-color: var(--brand); }
.filter-chip .icon { width: 12px; height: 12px; opacity: .75; }
.filter-chip-x { font-size: 1.05rem; line-height: 1; opacity: .6; }
.filter-chip:hover .filter-chip-x { opacity: 1; }
.filter-chip.is-clear { background: var(--well); color: var(--ink-soft); }
.filter-chip.is-clear:hover { color: var(--brand-dark); }

.batch-bar {
  display: flex; gap: 12px; flex-wrap: wrap; align-items: flex-end;
  padding: 12px 16px; margin: 0 0 14px;
  background: var(--card-2); border: 1px solid var(--line);
  border-left: 3px solid var(--brand); border-radius: var(--radius);
}

.pager { display: flex; gap: 6px; align-items: center; padding: 14px 2px 0; flex-wrap: wrap; }
.pager a, .pager span {
  padding: 5px 11px; border: 1px solid var(--line); border-radius: 5px;
  text-decoration: none; color: var(--ink-soft); font-size: .85rem; background: var(--card);
}
.pager .current { background: var(--brand); border-color: var(--brand); color: var(--brand-on); font-weight: 700; }
.pager .disabled { opacity: .4; }

.progress {
  height: 7px; background: var(--chart-track); border-radius: var(--radius-pill);
  overflow: hidden; box-shadow: inset 0 1px 2px rgba(16, 24, 40, .05);
}
.progress > span {
  display: block; height: 100%; background: var(--brand);
  border-radius: var(--radius-pill);
  transition: width .5s cubic-bezier(.22, .8, .3, 1);
}
.progress.thin { height: 5px; }
/* A bar at zero still shows its track, which is the point: "0%" has to look
   like an empty bar, not like a missing one. */
.progress > span.is-ok   { background: var(--ok); }
.progress > span.is-warn { background: var(--warn); }
.progress > span.is-bad  { background: var(--bad); }

.thumb {
  width: 44px; height: 44px; object-fit: cover; border-radius: 6px; background: var(--well);
  border: 1px solid var(--line); display: block;
}
.thumb-lg { width: 132px; height: 132px; }

/* Hero slide picture. The hero is a wide, short shape, so the thumbnail is
   cropped to that shape rather than squared: a 16:9 crop at this size is still
   enough to recognise which picture is which. */
.slide-thumb {
  width: 132px; height: 74px; object-fit: cover; border-radius: 6px;
  background: var(--well); border: 1px solid var(--line); display: block;
}
.slide-cell { display: flex; align-items: center; gap: 12px; min-width: 260px; }
.slide-cell strong { display: block; }

.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--line); margin-bottom: 18px; flex-wrap: wrap; }
.tabs a {
  padding: 9px 15px; text-decoration: none; color: var(--ink-soft); font-size: .89rem;
  font-weight: 600; border: 1px solid transparent; border-bottom: 0; border-radius: 6px 6px 0 0;
  margin-bottom: -1px;
}
.tabs a:hover { background: var(--well); color: var(--ink); }
.tabs a.active { background: var(--card); border-color: var(--line); color: var(--brand); }

.diffbox {
  font-family: var(--mono); font-size: .82rem; background: var(--card-2); border: 1px solid var(--line);
  border-radius: 6px; padding: 10px 12px; max-height: 190px; overflow: auto; white-space: pre-wrap;
  word-break: break-word;
}
.diff-old { background: var(--bad-bg); color: var(--bad); border-radius: 4px; padding: 2px 5px; }
.diff-new { background: var(--ok-bg); color: var(--ok); border-radius: 4px; padding: 2px 5px; }

.media-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(155px, 1fr)); gap: 14px; }
.media-item { border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; background: var(--card); }
.media-item img { width: 100%; height: 110px; object-fit: cover; display: block; background: var(--well); }
.media-item .media-meta { padding: 8px 10px; font-size: .78rem; }
.media-item .media-meta strong { display: block; word-break: break-word; font-weight: 600; }
.media-item.is-selected { outline: 3px solid var(--brand); }

/* A header row that only assistive technology sees. Used for label/value
   tables and for the column names of a card whose layout already makes the
   columns obvious to a sighted reader: the markup stays correct without
   printing a redundant header line on the page. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ------------------------------------------------------------------ login */
.login-page { background: var(--bg); }

/* Two panels: identity on the left, the form on the right. */
.login-shell {
  min-height: 100vh; display: grid; grid-template-columns: 1.05fr 1fr;
}

.login-aside {
  position: relative; overflow: hidden; color: #fff;
  padding: 48px 52px; display: flex; flex-direction: column; justify-content: space-between;
  background:
    radial-gradient(900px 520px at 88% -8%, rgba(120, 178, 240, .34) 0%, rgba(120, 178, 240, 0) 62%),
    linear-gradient(158deg, #17457f 0%, var(--brand-dark) 46%, #0c2a51 100%);
}
/* Soft light bloom for depth. */
.login-aside::after {
  content: ''; position: absolute; width: 460px; height: 460px; right: -170px; bottom: -190px;
  border-radius: 50%; background: rgba(255, 255, 255, .05); pointer-events: none;
}
.login-aside-inner { position: relative; z-index: 1; max-width: 30ch; }
.login-aside .login-logo {
  height: 60px; width: 60px; object-fit: contain; margin-bottom: 26px;
  filter: drop-shadow(0 2px 8px rgba(0, 0, 0, .3));
}
.login-aside .login-org {
  margin: 0 0 10px; font-size: .74rem; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: #a9cbf2;
}
.login-aside h1 { margin: 0 0 14px; font-size: 2.05rem; line-height: 1.18; letter-spacing: -.02em; color: #fff; }
.login-aside-sub { margin: 0 0 30px; font-size: .98rem; line-height: 1.6; color: rgba(255, 255, 255, .8); }
.login-points { list-style: none; margin: 0; padding: 0; }
.login-points li {
  position: relative; padding: 0 0 0 30px; margin-bottom: 13px;
  font-size: .9rem; line-height: 1.5; color: rgba(255, 255, 255, .9);
}
.login-points li::before {
  content: ''; position: absolute; left: 0; top: .42em; width: 15px; height: 15px;
  border-radius: 50%; background: rgba(150, 200, 255, .22);
  box-shadow: inset 0 0 0 1px rgba(170, 210, 255, .5);
}
.login-points li::after {
  content: ''; position: absolute; left: 4.5px; top: .78em; width: 6px; height: 3px;
  border-left: 1.6px solid #bcd9fb; border-bottom: 1.6px solid #bcd9fb;
  transform: rotate(-45deg);
}
.login-aside-foot {
  position: relative; z-index: 1; margin: 0; font-size: .76rem; line-height: 1.55;
  color: rgba(255, 255, 255, .6);
}

.login-main { display: flex; align-items: center; justify-content: center; padding: 44px 40px; }
.login-card { width: 100%; max-width: 396px; }
.login-head { margin-bottom: 24px; }
.login-org-sm { display: none; margin: 0 0 6px; font-size: .7rem; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--brand); }
.login-card h2 { margin: 0 0 6px; font-size: 1.6rem; letter-spacing: -.02em; }
.login-card .login-sub { margin: 0; color: var(--ink-faint); font-size: .89rem; line-height: 1.5; }

/* Messages ---------------------------------------------------------------- */
.login-alert { display: flex; gap: 11px; align-items: flex-start; }
.login-alert-icon {
  flex: 0 0 auto; width: 19px; height: 19px; margin-top: 1px;
  fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round;
}
.login-alert strong { display: block; margin-bottom: 2px; }
.login-alert span { font-weight: 400; }

/* Fields ------------------------------------------------------------------ */
.login-card .field { margin-bottom: 17px; }
.login-card label { display: block; margin-bottom: 6px; font-size: .84rem; font-weight: 600; color: var(--ink); }
.login-card .hint { display: block; margin-top: 6px; font-size: .77rem; color: var(--ink-faint); line-height: 1.45; }

.input-wrap { position: relative; display: flex; align-items: center; }
.input-icon {
  position: absolute; left: 13px; width: 18px; height: 18px; pointer-events: none;
  fill: none; stroke: var(--ink-faint); stroke-width: 1.6; stroke-linecap: round; transition: stroke .15s ease;
}
.input-wrap input {
  width: 100%; min-height: 46px; padding: 11px 14px 11px 40px;
  font-size: .95rem; color: var(--ink); background: #fff;
  border: 1px solid var(--line); border-radius: 10px;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.input-wrap input::placeholder { color: var(--ink-faint); }
.input-wrap input:focus {
  outline: none; border-color: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 22%, transparent);
}
.input-wrap input:focus + .pw-toggle { color: var(--brand); }
.input-wrap:has(input:focus) .input-icon { stroke: var(--brand); }
.input-wrap input[aria-invalid="true"] { border-color: var(--bad); }
.input-wrap input[aria-invalid="true"]:focus { box-shadow: 0 0 0 3px rgba(179, 38, 30, .13); }

/* Password visibility toggle ---------------------------------------------- */
.pw-toggle {
  position: absolute; right: 6px; display: flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; padding: 0; color: var(--ink-soft);
  background: none; border: 0; border-radius: 8px; cursor: pointer;
}
.pw-toggle:hover { background: var(--well); color: var(--brand); }
.pw-toggle:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.pw-toggle svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.pw-toggle .pw-eye-off { display: none; }
.pw-toggle[aria-pressed="true"] .pw-eye { display: none; }
.pw-toggle[aria-pressed="true"] .pw-eye-off { display: block; }
.pw-toggle[aria-pressed="true"] { color: var(--brand); }

/* Submit ------------------------------------------------------------------ */
.login-submit { position: relative; min-height: 46px; margin-top: 4px; font-size: .95rem; }
.login-spinner {
  position: absolute; inset: 0; margin: auto; width: 17px; height: 17px; border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, .4); border-top-color: #fff;
  display: none; animation: login-spin .6s linear infinite;
}
.login-submit.is-busy .login-spinner { display: block; }
.login-submit.is-busy .login-submit-text { visibility: hidden; }
.login-submit[disabled] { cursor: default; opacity: .82; }
@keyframes login-spin { to { transform: rotate(360deg); } }

.login-note {
  margin: 22px 0 0; padding-top: 18px; border-top: 1px solid var(--line);
  font-size: .78rem; line-height: 1.55; color: var(--ink-faint);
}
.login-foot { margin: 16px 0 0; text-align: center; font-size: .83rem; }

/* Motion ------------------------------------------------------------------ */
@media (prefers-reduced-motion: no-preference) {
  .login-aside-inner, .login-card { animation: login-rise .45s cubic-bezier(.22, .8, .3, 1) both; }
  .login-card { animation-delay: .06s; }
}
@keyframes login-rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

/* Responsive -------------------------------------------------------------- */
@media (max-width: 940px) {
  .login-shell { grid-template-columns: 1fr; }
  .login-aside { padding: 30px 26px; }
  .login-aside-inner { max-width: none; }
  .login-aside .login-logo { height: 44px; width: 44px; margin-bottom: 16px; }
  .login-aside h1 { font-size: 1.45rem; margin-bottom: 8px; }
  .login-aside-sub, .login-points, .login-aside-foot { display: none; }
  .login-main { padding: 32px 24px 44px; }
  .login-org-sm { display: block; }
}
@media (max-width: 480px) {
  .login-main { padding: 26px 18px 38px; }
  .login-card h2 { font-size: 1.4rem; }
}

/* ------------------------------------------------------------ responsive */
@media (max-width: 940px) {
  .nav-toggle { display: block; }
  .sidenav {
    position: fixed; left: 0; top: 60px; bottom: 0; z-index: 35; transform: translateX(-100%);
    transition: transform .18s ease; max-height: none;
  }
  .sidenav.open { transform: none; box-shadow: var(--shadow-lg); }
  .main { padding: 18px 16px 50px; }
  .usermenu-text { display: none; }
}
@media (max-width: 600px) {
  .topbar { padding: 0 12px; }
  .topbar-link { display: none; }
  .hero { padding: 18px 18px 20px; gap: 16px; border-radius: var(--radius); }
  .hero h2 { font-size: 1.32rem; }
  .hero-actions { margin-left: 0; width: 100%; }
  .hero-actions .btn { flex: 1 1 auto; }
  .hero-glance-item { flex-basis: 50%; padding-right: 14px; }
  .hero-glance-item + .hero-glance-item { padding-left: 14px; }
  .hero-glance-item { border-left: 0; }
  .hero-glance-item + .hero-glance-item:nth-child(2n) { border-left: 1px solid var(--banner-line); }
  .hero-glance-value { font-size: 1.16rem; }
  .stat-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 11px; }
  .stat-card { padding: 14px 15px 16px; }
  .stat-card .stat-icon { width: 36px; height: 36px; margin-bottom: 10px; }
  .stat-card .stat-value { font-size: 1.6rem; }
  .card { padding: 17px 16px; }
  .card-flush { padding: 0; }
  .donut-wrap { gap: 14px; }
}

/* ------------------------------------------------------------------ motion
   One short entrance for the blocks that make up a dashboard. Deliberately
   small: a long stagger on a screen with this many rows makes the page feel
   slow to use rather than considered. Opt-in only, and entirely skipped when
   the reader has asked for reduced motion at the OS level. */
@media (prefers-reduced-motion: no-preference) {
  .hero, .stat-grid, .grid-charts, .card, .eyebrow {
    animation: rise-in .34s cubic-bezier(.22, .8, .3, 1) both;
  }
  .stat-grid > *, .grid-charts > * { animation: rise-in .34s cubic-bezier(.22, .8, .3, 1) both; }
  .stat-grid > *:nth-child(2) { animation-delay: .04s; }
  .stat-grid > *:nth-child(3) { animation-delay: .08s; }
  .stat-grid > *:nth-child(4) { animation-delay: .12s; }
  .stat-grid > *:nth-child(5) { animation-delay: .16s; }
  .stat-grid > *:nth-child(6) { animation-delay: .2s; }
  .grid-charts > *:nth-child(2) { animation-delay: .06s; }
  .grid-charts > *:nth-child(3) { animation-delay: .12s; }
}
@keyframes rise-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

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

@media print {
  .sidenav, .topbar, .page-actions, .form-actions, .hero-actions { display: none !important; }
  .main { padding: 0; }
  .card, .stat-card { box-shadow: none; border-color: #ccc; }
  .stat-card { border: 1px solid #ccc; }
  a.stat-card::after { content: ' (' attr(href) ')'; font-size: .7rem; color: #555; }
  /* A printed banner is a lot of ink for a greeting. Keep the words, drop the
     gradient and the chrome around them. */
  .hero { background: none; color: #000; box-shadow: none; border: 1px solid #ccc; }
  .hero::after, .hero::before { display: none; }
  .hero-eyebrow { background: none; border-color: #ccc; color: #444; }
  .hero h2, .hero-sub strong, .hero-glance-value, .hero-glance-label,
  .hero-glance-note, .hero-eyebrow { color: #000; }
  .hero-glance { border-top-color: #ccc; }
  .hero-glance-item + .hero-glance-item { border-left-color: #ccc; }
  .hero-sub, .hero-glance-note { color: #333; }
}

