/* ==========================================================================
   Digital Examination Paper Evaluation System — design system
   Light control-centre dashboard: indigo icon rail, blue-grey canvas,
   soft white cards, accent-bar KPI tiles, pastel status pills.
   Layered on top of Bootstrap 5.3.
   ========================================================================== */

:root {
    /* --- surface ------------------------------------------------------- */
    --canvas:        #eef1f7;
    --canvas-2:      #f6f8fc;
    --surface:       #ffffff;
    --surface-soft:  #f7f9fc;
    --surface-sunk:  #f2f5fa;

    /* --- ink ----------------------------------------------------------- */
    --ink:           #101828;
    --ink-2:         #344054;
    --muted:         #667085;
    --faint:         #98a2b3;

    /* --- lines --------------------------------------------------------- */
    --line:          #e9ecf3;
    --line-strong:   #dfe3ec;

    /* --- brand --------------------------------------------------------- */
    --rail:          #3b3a8f;
    --rail-2:        #4b48ad;
    --brand:         #4f46e5;
    --brand-soft:    #eef2ff;
    --brand-ink:     #3730a3;

    /* --- semantic ------------------------------------------------------ */
    --ok:            #12b76a;
    --ok-soft:       #ecfdf3;
    --ok-ink:        #027a48;

    --warn:          #f79009;
    --warn-soft:     #fffaeb;
    --warn-ink:      #b54708;

    --bad:           #f04438;
    --bad-soft:      #fef3f2;
    --bad-ink:       #b42318;

    --info:          #2e90fa;
    --info-soft:     #eff8ff;
    --info-ink:      #175cd3;

    --neutral:       #98a2b3;
    --neutral-soft:  #f2f4f7;
    --neutral-ink:   #475467;

    --violet:        #7a5af8;
    --violet-soft:   #f4f3ff;
    --violet-ink:    #5925dc;

    /* --- geometry ------------------------------------------------------ */
    --r-sm: 8px;
    --r:    12px;
    --r-lg: 16px;
    --rail-w: 84px;
    --top-h: 62px;
    --foot-h: 38px;

    --shadow-1: 0 1px 2px rgba(16,24,40,.05);
    --shadow-2: 0 1px 2px rgba(16,24,40,.04), 0 10px 26px rgba(16,24,40,.05);
    --shadow-3: 0 12px 40px rgba(16,24,40,.12);
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
    margin: 0;
    background: var(--canvas);
    background-image: linear-gradient(180deg, #eaeefa 0%, var(--canvas) 220px);
    color: var(--ink);
    font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
    font-size: 13.5px;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
}

a { color: var(--brand); text-decoration: none; }
a:hover { color: var(--brand-ink); text-decoration: none; }

/* ==========================================================================
   Layout shell
   ========================================================================== */

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

/* --- left icon rail ---------------------------------------------------- */
.rail {
    width: var(--rail-w);
    flex: 0 0 var(--rail-w);
    background: linear-gradient(180deg, var(--rail-2) 0%, var(--rail) 100%);
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 12px 0 14px;
    position: sticky;
    top: 0;
    height: 100vh;
    z-index: 40;
}

.rail-logo {
    width: 38px; height: 38px;
    border-radius: 11px;
    background: rgba(255,255,255,.16);
    color: #fff;
    display: grid; place-items: center;
    font-weight: 800; font-size: 13px; letter-spacing: .3px;
    margin-bottom: 18px;
    border: 1px solid rgba(255,255,255,.18);
    flex: 0 0 auto;
}

/* The institution mark sits on white, so a logo with its own white background
   reads as a proper badge against the dark rail rather than a pale smear. */
.rail-logo.has-img {
    background: #fff;
    padding: 3px;
    overflow: hidden;
    border-color: rgba(255,255,255,.35);
}
.rail-logo.has-img img {
    width: 100%; height: 100%;
    object-fit: contain;
    border-radius: 8px;
    display: block;
}

.rail-nav { display: flex; flex-direction: column; gap: 4px; flex: 1; width: 100%; align-items: center; }

/* Icon with its name underneath. An icon on its own is a guess for anyone who
   does not use the system every day, and the tooltip only helped people who
   already knew to hover. The rail is 84px rather than 64px to pay for it —
   twenty pixels, taken once, off every screen. */
.rail-item {
    width: 72px; min-height: 50px;
    padding: 6px 3px 5px;
    border-radius: 11px;
    display: flex; flex-direction: column;
    align-items: center; justify-content: center; gap: 3px;
    color: rgba(255,255,255,.62);
    transition: background .15s ease, color .15s ease;
    position: relative;
    text-align: center;
}
.rail-item:hover { background: rgba(255,255,255,.12); color: #fff; }
.rail-item.is-active { background: rgba(255,255,255,.94); color: var(--rail); }
.rail-item .material-symbols-outlined { font-size: 21px; line-height: 1; }

.rail-label {
    font-size: 9.5px; font-weight: 700; letter-spacing: .1px;
    line-height: 1.15;
    max-width: 100%;
    /* One line only. A name long enough to wrap would push its neighbours out
       of alignment, and the full name is on the tooltip anyway. */
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Tooltip on the rail — still there, still carrying the FULL name, which is
   what the shortened label under the icon cannot always show. */
.rail-item::after {
    content: attr(data-label);
    position: absolute; left: 76px; top: 50%;
    transform: translateY(-50%) translateX(-4px);
    background: var(--ink); color: #fff;
    font-size: 11.5px; font-weight: 600; white-space: nowrap;
    padding: 5px 9px; border-radius: 7px;
    opacity: 0; pointer-events: none;
    transition: opacity .14s ease, transform .14s ease;
    z-index: 60;
}
.rail-item:hover::after { opacity: 1; transform: translateY(-50%) translateX(0); }

.rail-foot { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.rail-avatar {
    width: 34px; height: 34px; border-radius: 50%;
    background: rgba(255,255,255,.2); color: #fff;
    display: grid; place-items: center;
    font-size: 11.5px; font-weight: 700;
    border: 1px solid rgba(255,255,255,.25);
}

/* --- main column ------------------------------------------------------- */
.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.topbar {
    height: var(--top-h);
    background: var(--surface);
    border-bottom: 1px solid var(--line);
    display: flex; align-items: center; gap: 16px;
    padding: 0 22px;
    position: sticky; top: 0; z-index: 30;
}

.topbar-eyebrow { font-size: 10.5px; color: var(--faint); font-weight: 600; letter-spacing: .2px; }
.topbar-title   { font-size: 16px; font-weight: 700; color: var(--ink); line-height: 1.2; }

.topbar-search {
    flex: 0 1 340px;
    margin-inline: auto;
    background: var(--surface-sunk);
    border: 1px solid transparent;
    border-radius: 999px;
    height: 34px;
    display: flex; align-items: center; gap: 8px;
    padding: 0 14px;
    color: var(--faint);
}
.topbar-search:focus-within { border-color: #c7d0e6; background: #fff; }
.topbar-search input {
    border: 0; background: transparent; outline: none;
    font-size: 12.5px; color: var(--ink); width: 100%;
}
.topbar-search .material-symbols-outlined { font-size: 17px; }

.topbar-right { display: flex; align-items: center; gap: 12px; margin-left: auto; }

.live-chip {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 11px; font-weight: 700; color: var(--ok-ink);
    background: var(--ok-soft); border-radius: 999px; padding: 4px 10px;
}
.live-dot {
    width: 6px; height: 6px; border-radius: 50%; background: var(--ok);
    box-shadow: 0 0 0 0 rgba(18,183,106,.5);
    animation: pulse 2.2s infinite;
}
@keyframes pulse {
    0%   { box-shadow: 0 0 0 0 rgba(18,183,106,.45); }
    70%  { box-shadow: 0 0 0 7px rgba(18,183,106,0); }
    100% { box-shadow: 0 0 0 0 rgba(18,183,106,0); }
}

.topbar-clock { font-size: 11.5px; color: var(--muted); font-weight: 600; }

.topbar-user {
    text-align: right;
    line-height: 1.25;
    max-width: 220px;
    min-width: 0;
}
.topbar-user .name {
    font-size: 12.5px; font-weight: 700; color: var(--ink);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.topbar-user .mail {
    font-size: 10.5px; color: var(--faint); font-weight: 500;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.avatar {
    width: 32px; height: 32px; border-radius: 50%;
    background: var(--brand-soft); color: var(--brand-ink);
    display: grid; place-items: center;
    font-size: 11px; font-weight: 800;
    border: 1px solid #dfe3f7;
}

.content { padding: 20px 22px 28px; flex: 1; }

/* ------------------------------------------------------------ site footer

   One line, on every screen including the login page. Quiet by design: it is
   an ownership notice, not navigation, and the evaluation screen in
   particular cannot afford to give it any real height.
   -------------------------------------------------------------------- */
.site-foot {
    flex: 0 0 auto;
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    padding: 10px 22px;
    border-top: 1px solid var(--line);
    background: var(--surface);
    font-size: 11.5px; font-weight: 600;
    color: var(--muted);
}
.site-foot .foot-main { flex: 1; min-width: 220px; }
.site-foot .foot-dot  { color: var(--faint); margin: 0 4px; }
.site-foot .foot-by   { color: var(--faint); }
.site-foot .foot-by strong { color: var(--muted); font-weight: 800; }

/* The login page has no app shell, so the footer is pinned there instead. */
body.auth-body .site-foot {
    position: fixed; left: 0; right: 0; bottom: 0;
    justify-content: center;
    background: transparent; border-top: 0;
}
body.auth-body .site-foot .foot-main { flex: 0 0 auto; min-width: 0; }
/* Room for it, so a short window cannot lay the notice over the card. */
body.auth-body .login-wrap { padding-bottom: 56px; }

@media (max-width: 720px) {
    .site-foot { flex-direction: column; align-items: flex-start; gap: 2px; }
}

/* Page heading inside the content area */
.page-head { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 16px; flex-wrap: wrap; }
.page-head h1 { font-size: 18px; font-weight: 700; margin: 0; color: var(--ink); }
.page-head .sub { font-size: 12px; color: var(--muted); margin-top: 2px; }
.page-head .spacer { flex: 1; }

/* ==========================================================================
   Cards
   ========================================================================== */

.card-x {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-2);
}

.card-x-head {
    padding: 14px 16px 10px;
    display: flex; align-items: flex-start; gap: 12px;
}
.card-x-head h2, .card-x-head h3 {
    font-size: 14px; font-weight: 700; margin: 0; color: var(--ink);
}
.card-x-head .sub { font-size: 11px; color: var(--faint); margin-top: 1px; font-weight: 500; }
.card-x-head .spacer { flex: 1; }

.card-x-body { padding: 0 16px 16px; }
.card-x-body.pad-top { padding-top: 4px; }
.card-x-foot {
    padding: 11px 16px;
    border-top: 1px solid var(--line);
    background: var(--surface-soft);
    border-radius: 0 0 var(--r-lg) var(--r-lg);
}

/* ==========================================================================
   KPI tiles — coloured left accent bar + pastel icon square
   ========================================================================== */

.tile-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 14px;
    margin-bottom: 16px;
}

/* ------------------------------------------------- master data shortcuts

   Deliberately dark and saturated, sitting above the pale status tiles.
   They are two different kinds of thing and must not look alike: these are
   places to GO and set something up, the tiles below are numbers to READ.
   Colour is what separates them at a glance; the count on each is secondary,
   which is why it is small and the label is not.
   -------------------------------------------------------------------- */
.mcard-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));
    gap: 14px;
    margin-bottom: 18px;
}

.mcard {
    position: relative;
    display: block;
    overflow: hidden;
    border-radius: var(--r-lg);
    padding: 16px 18px 15px;
    color: #fff;
    text-decoration: none;
    box-shadow: 0 10px 24px rgba(16, 24, 40, .18);
    transition: transform .16s ease, box-shadow .16s ease;
}
.mcard:hover,
.mcard:focus-visible {
    color: #fff;
    transform: translateY(-2px);
    box-shadow: 0 16px 34px rgba(16, 24, 40, .28);
}
.mcard:focus-visible { outline: 3px solid #fff; outline-offset: -3px; }

/* An oversized, very faint copy of the icon, bled off the right edge — it
   gives each card its own silhouette without competing with the words. */
.mcard .m-ghost {
    position: absolute; right: -14px; bottom: -20px;
    font-size: 96px; line-height: 1;
    color: rgba(255, 255, 255, .15);
    pointer-events: none;
}

.mcard .m-top { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.mcard .m-icon {
    width: 34px; height: 34px; flex: 0 0 34px;
    display: grid; place-items: center;
    border-radius: 10px;
    background: rgba(255, 255, 255, .2);
    backdrop-filter: blur(2px);
}
.mcard .m-icon .material-symbols-outlined { font-size: 19px; color: #fff; }

.mcard .m-count {
    margin-left: auto;
    font-size: 19px; font-weight: 800; letter-spacing: -.4px;
    font-variant-numeric: tabular-nums;
}
.mcard .m-label {
    position: relative;
    font-size: 14px; font-weight: 800; letter-spacing: -.2px; margin-bottom: 3px;
}
/* Not rgba white — a translucent white over a mid-tone gradient measures far
   below 4.5:1. Held at 88% opacity of pure white instead, which does not. */
.mcard .m-sub {
    position: relative;
    font-size: 11px; font-weight: 600; line-height: 1.45;
    color: rgba(255, 255, 255, .88);
}
.mcard .m-go {
    position: relative;
    display: inline-flex; align-items: center; gap: 4px;
    margin-top: 10px;
    font-size: 11px; font-weight: 700;
    color: rgba(255, 255, 255, .95);
}
.mcard .m-go .material-symbols-outlined { font-size: 15px; }

/* The light end of each gradient is the constraint, not the dark end: the
   11px sub-line has to clear 4.5:1 against it. The first attempt used #14b8a6
   and #a855f7, which measured 2.5:1 and 3.9:1 — pretty, and unreadable. These
   ends are held below the luminance where that fails. */
.mcard.m-indigo { background: linear-gradient(135deg, #3730a3 0%, #4f46e5 100%); }
.mcard.m-teal   { background: linear-gradient(135deg, #134e4a 0%, #0f766e 100%); }
.mcard.m-violet { background: linear-gradient(135deg, #4c1d95 0%, #7c3aed 100%); }
.mcard.m-slate  { background: linear-gradient(135deg, #1e293b 0%, #475569 100%); }

.tile {
    position: relative;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-2);
    padding: 14px 16px 13px 18px;
    overflow: hidden;
    display: block;
    color: inherit;
}
a.tile:hover { box-shadow: var(--shadow-3); transform: translateY(-1px); color: inherit; }
a.tile { transition: box-shadow .16s ease, transform .16s ease; }

.tile::before {
    content: ''; position: absolute; left: 0; top: 0; bottom: 0;
    width: 4px; background: var(--neutral);
    border-radius: var(--r-lg) 0 0 var(--r-lg);
}
.tile.t-ok::before      { background: var(--ok); }
.tile.t-warn::before    { background: var(--warn); }
.tile.t-bad::before     { background: var(--bad); }
.tile.t-info::before    { background: var(--info); }
.tile.t-brand::before   { background: var(--brand); }
.tile.t-violet::before  { background: var(--violet); }

.tile-top { display: flex; align-items: center; gap: 9px; margin-bottom: 9px; }

.tile-icon {
    width: 30px; height: 30px; border-radius: 9px;
    display: grid; place-items: center;
    background: var(--neutral-soft); color: var(--neutral-ink);
    flex: 0 0 30px;
}
.tile-icon .material-symbols-outlined { font-size: 17px; }
.t-ok     .tile-icon { background: var(--ok-soft);     color: var(--ok-ink); }
.t-warn   .tile-icon { background: var(--warn-soft);   color: var(--warn-ink); }
.t-bad    .tile-icon { background: var(--bad-soft);    color: var(--bad-ink); }
.t-info   .tile-icon { background: var(--info-soft);   color: var(--info-ink); }
.t-brand  .tile-icon { background: var(--brand-soft);  color: var(--brand-ink); }
.t-violet .tile-icon { background: var(--violet-soft); color: var(--violet-ink); }

.tile-label { font-size: 10.5px; font-weight: 700; color: var(--muted); letter-spacing: .1px; }
.tile-value { font-size: 25px; font-weight: 800; color: var(--ink); line-height: 1.05; letter-spacing: -.6px; }
.tile-value.sm { font-size: 19px; }
.tile-foot  { font-size: 10.5px; color: var(--faint); margin-top: 5px; font-weight: 600; }
.tile-foot .up   { color: var(--ok-ink); }
.tile-foot .down { color: var(--bad-ink); }

/* ==========================================================================
   Status pills
   ========================================================================== */

.pill {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 10.5px; font-weight: 700; line-height: 1;
    padding: 5px 9px; border-radius: 999px;
    background: var(--neutral-soft); color: var(--neutral-ink);
    white-space: nowrap;
}
.pill-dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; opacity: .85; }

.pill.ok      { background: var(--ok-soft);     color: var(--ok-ink); }
.pill.warn    { background: var(--warn-soft);   color: var(--warn-ink); }
.pill.bad     { background: var(--bad-soft);    color: var(--bad-ink); }
.pill.info    { background: var(--info-soft);   color: var(--info-ink); }
.pill.brand   { background: var(--brand-soft);  color: var(--brand-ink); }
.pill.violet  { background: var(--violet-soft); color: var(--violet-ink); }
.pill.neutral { background: var(--neutral-soft);color: var(--neutral-ink); }

/* ==========================================================================
   Insight / list rows — coloured left bar, like the reference "Live Insights"
   ========================================================================== */

.row-list { display: flex; flex-direction: column; gap: 8px; }

.row-item {
    display: flex; align-items: center; gap: 11px;
    background: var(--surface-soft);
    border: 1px solid var(--line);
    border-left: 3px solid var(--neutral);
    border-radius: 10px;
    padding: 10px 12px;
    color: inherit;
}
a.row-item:hover { background: #f2f5fb; color: inherit; }
.row-item.r-ok    { border-left-color: var(--ok); }
.row-item.r-warn  { border-left-color: var(--warn); }
.row-item.r-bad   { border-left-color: var(--bad); }
.row-item.r-info  { border-left-color: var(--info); }
.row-item.r-brand { border-left-color: var(--brand); }

.row-item .r-icon { color: var(--muted); display: grid; place-items: center; }
.row-item .r-icon .material-symbols-outlined { font-size: 18px; }
.row-item .r-body { flex: 1; min-width: 0; }
.row-item .r-title { font-size: 12.5px; font-weight: 700; color: var(--ink); }
.row-item .r-sub   { font-size: 11px; color: var(--muted); margin-top: 1px; }

/* ==========================================================================
   Tables
   ========================================================================== */

.table-x { width: 100%; border-collapse: separate; border-spacing: 0; }

/* Dark header on every table in the system.

   It was a pale grey band, which on a long list left the column names fading
   into the first rows of data. The indigo is the rail's colour, so a table
   heading and the navigation read as the same piece of furniture. */
.table-x thead th {
    font-size: 10.5px; font-weight: 700; color: rgba(255,255,255,.92);
    text-transform: uppercase; letter-spacing: .5px;
    padding: 9px 12px;
    background: var(--rail);
    border-top: 1px solid var(--rail);
    border-bottom: 1px solid var(--rail);
    text-align: left; white-space: nowrap;
}
.table-x thead th:first-child { border-left: 1px solid var(--rail); border-radius: 9px 0 0 0; }
.table-x thead th:last-child  { border-right: 1px solid var(--rail); border-radius: 0 9px 0 0; }

/* Anything the header row carries has to be legible on the dark band too —
   a sort link or a hint icon inherited the old grey and disappeared. */
.table-x thead th a { color: #fff; text-decoration: underline; }
.table-x thead th .material-symbols-outlined { color: rgba(255,255,255,.8); }

/* A totals row is part of the same frame, so it is banded to match rather than
   left looking like one more data row that happens to say TOTAL. */
.table-x tfoot th {
    background: var(--surface-sunk);
    border-top: 2px solid var(--rail);
    color: var(--ink);
    font-size: 12.5px; font-weight: 800;
    padding: 11px 12px;
    white-space: nowrap;
}

.table-x tbody td {
    padding: 10px 12px;
    border-bottom: 1px solid var(--line);
    font-size: 12.5px; color: var(--ink-2);
    vertical-align: middle;
}
.table-x tbody tr:hover td { background: #fafbfe; }
.table-x tbody tr.is-muted td { color: var(--faint); }

.table-x .num { text-align: right; font-variant-numeric: tabular-nums; }
.table-x .mono {
    font-family: ui-monospace, 'Cascadia Mono', Consolas, monospace;
    font-size: 12px; letter-spacing: -.1px;
}
.table-wrap { overflow-x: auto; }

/* Zero-state */
.empty {
    text-align: center; padding: 38px 20px; color: var(--muted);
}
.empty .material-symbols-outlined { font-size: 34px; color: var(--faint); }
.empty h4 { font-size: 14px; font-weight: 700; color: var(--ink-2); margin: 10px 0 4px; }
.empty p { font-size: 12px; margin: 0; }

/* ==========================================================================
   Progress
   ========================================================================== */

.bar { height: 6px; background: var(--surface-sunk); border-radius: 999px; overflow: hidden; }
.bar > span { display: block; height: 100%; border-radius: 999px; background: var(--brand); transition: width .3s ease; }
.bar.ok   > span { background: var(--ok); }
.bar.warn > span { background: var(--warn); }
.bar.bad  > span { background: var(--bad); }

.bar-label { display: flex; justify-content: space-between; font-size: 11px; color: var(--muted); margin-bottom: 5px; font-weight: 600; }

/* Horizontal category bars (reference "Energy usage") */
.hbar-row { display: grid; grid-template-columns: 90px 1fr 62px; align-items: center; gap: 10px; margin-bottom: 9px; }
.hbar-row .k { font-size: 11.5px; color: var(--muted); font-weight: 600; }
.hbar-row .v { font-size: 11.5px; color: var(--ink); font-weight: 700; text-align: right; font-variant-numeric: tabular-nums; }

/* ==========================================================================
   Forms & buttons
   ========================================================================== */

.form-label, label.fl {
    font-size: 11.5px; font-weight: 700; color: var(--ink-2);
    margin-bottom: 5px; display: block;
}
.hint { font-size: 11px; color: var(--faint); margin-top: 4px; }

.form-control, .form-select, .input-x {
    border: 1px solid var(--line-strong);
    border-radius: var(--r-sm);
    font-size: 13px;
    padding: 7px 11px;
    color: var(--ink);
    background: #fff;
    box-shadow: var(--shadow-1);
    width: 100%;
}
.form-control:focus, .form-select:focus, .input-x:focus {
    border-color: #a7b3e8;
    box-shadow: 0 0 0 3px rgba(79,70,229,.12);
    outline: none;
}
.form-control::placeholder { color: var(--faint); }
textarea.form-control { min-height: 80px; }

.btn-x {
    display: inline-flex; align-items: center; gap: 6px;
    border: 1px solid transparent; border-radius: var(--r-sm);
    font-size: 12.5px; font-weight: 700;
    padding: 7px 13px;
    cursor: pointer;
    transition: background .14s ease, border-color .14s ease, box-shadow .14s ease;
    white-space: nowrap;
    text-decoration: none;
}
.btn-x .material-symbols-outlined { font-size: 16px; }
.btn-x:disabled, .btn-x.disabled { opacity: .55; pointer-events: none; }

.btn-primary-x { background: var(--brand); color: #fff; box-shadow: var(--shadow-1); }
.btn-primary-x:hover { background: #4338ca; color: #fff; }

.btn-ghost-x { background: #fff; color: var(--ink-2); border-color: var(--line-strong); box-shadow: var(--shadow-1); }
.btn-ghost-x:hover { background: var(--surface-soft); color: var(--ink); }

.btn-danger-x { background: var(--bad); color: #fff; }
.btn-danger-x:hover { background: #d92d20; color: #fff; }

.btn-ok-x { background: var(--ok); color: #fff; }
.btn-ok-x:hover { background: #039855; color: #fff; }

.btn-sm-x { padding: 4px 9px; font-size: 11.5px; border-radius: 7px; }

/* Filter chip row */
.chips { display: flex; gap: 7px; flex-wrap: wrap; }
.chip {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 11.5px; font-weight: 700;
    padding: 5px 11px; border-radius: 999px;
    background: #fff; border: 1px solid var(--line-strong);
    color: var(--muted); cursor: pointer;
}
.chip:hover { border-color: #c7d0e6; color: var(--ink-2); }
.chip.is-on { background: var(--brand); border-color: var(--brand); color: #fff; }
.chip .n { font-variant-numeric: tabular-nums; opacity: .8; }

/* ==========================================================================
   Alerts
   ========================================================================== */

.alert-x {
    display: flex; gap: 10px; align-items: flex-start;
    border-radius: var(--r); padding: 11px 13px;
    font-size: 12.5px; border: 1px solid transparent;
    margin-bottom: 12px;
}
.alert-x .material-symbols-outlined { font-size: 18px; flex: 0 0 auto; }
.alert-x.ok   { background: var(--ok-soft);   color: var(--ok-ink);   border-color: #a6f4c5; }
.alert-x.warn { background: var(--warn-soft); color: var(--warn-ink); border-color: #fedf89; }
.alert-x.bad  { background: var(--bad-soft);  color: var(--bad-ink);  border-color: #fecdca; }
.alert-x.info { background: var(--info-soft); color: var(--info-ink); border-color: #b2ddff; }
.alert-x strong { font-weight: 800; }

/* ==========================================================================
   Grid helpers
   ========================================================================== */

.grid { display: grid; gap: 14px; }
.g-2 { grid-template-columns: repeat(2, minmax(0,1fr)); }
.g-3 { grid-template-columns: repeat(3, minmax(0,1fr)); }
.g-main-side { grid-template-columns: minmax(0,1fr) 340px; }
.g-side-main { grid-template-columns: 320px minmax(0,1fr); }

@media (max-width: 1100px) {
    .g-main-side, .g-side-main, .g-3 { grid-template-columns: minmax(0,1fr); }
}
/* Shed the least important things first as the bar narrows, so the name and
   the role chip survive longest. */
@media (max-width: 1000px) {
    .topbar-clock { display: none; }
}
@media (max-width: 760px) {
    .g-2 { grid-template-columns: minmax(0,1fr); }
    .topbar-search { display: none; }
    .topbar-user .mail { display: none; }
    .topbar-user { max-width: 140px; }
    .content { padding: 14px 12px 30px; }
    :root { --rail-w: 68px; }
}
@media (max-width: 520px) {
    .topbar-user { display: none; }
}

/* ==========================================================================
   Login
   ========================================================================== */

body.auth-body { color: #fff; background: #1b1b3a; overflow-x: hidden; }

/* The photograph, blurred behind everything.

   Scaled up 8% on purpose: a CSS blur samples past the edge of the element and
   leaves a pale feathered border, and the only cure is to push that border off
   the screen. Darkened as well, because white text over an unmodified daylight
   photograph is not readable and no amount of glass fixes that. */
body.auth-body::before {
    content: '';
    position: fixed; inset: -4%;
    background: url('../img/login-bg.webp') center / cover no-repeat;
    filter: blur(7px) brightness(.52) saturate(1.05);
    transform: scale(1.08);
    z-index: -2;
}

/* A colour wash over the photo, so the card sits on the institution's indigo
   rather than on whatever happens to be in that corner of the picture. */
body.auth-body::after {
    content: '';
    position: fixed; inset: 0;
    background: linear-gradient(125deg, rgba(59,58,143,.62) 0%, rgba(16,24,40,.72) 100%);
    z-index: -1;
}

.login-wrap {
    min-height: 100vh; display: grid; place-items: center; padding: 20px;
    position: relative;
}

/* Frosted glass. backdrop-filter is what does the work — it blurs whatever is
   behind the card specifically, which is what makes it read as glass rather
   than as a flat grey panel. */
.login-card {
    width: 100%; max-width: 410px;
    background: rgba(255,255,255,.13);
    backdrop-filter: blur(22px) saturate(165%);
    -webkit-backdrop-filter: blur(22px) saturate(165%);
    border: 1px solid rgba(255,255,255,.3);
    border-radius: 22px;
    box-shadow: 0 30px 80px rgba(0,0,0,.42), inset 0 1px 0 rgba(255,255,255,.28);
    padding: 32px 30px 28px;
    transition: opacity .35s ease, transform .35s ease, filter .35s ease;
}

/* Older Safari, and Firefox with backdrop-filter switched off, render the card
   as a barely-visible tint. A solid panel is better than an unreadable form. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .login-card { background: rgba(28,28,60,.86); }
}

.login-card h1  { text-align: center; }
.login-card .u-muted,
.login-card .u-faint { color: rgba(255,255,255,.72) !important; }

.login-brand {
    width: 48px; height: 48px; border-radius: 14px;
    background: rgba(255,255,255,.92); color: var(--rail);
    display: grid; place-items: center;
    font-weight: 800; font-size: 15px;
    margin: 0 auto 18px;
}

/* The institution mark is drawn for a white page, so it gets a white plate to
   stand on rather than being left to disappear into the glass. */
.login-logo-plate {
    background: rgba(255,255,255,.94);
    border-radius: 14px;
    padding: 10px 14px;
    margin: 0 auto 20px;
    width: fit-content; max-width: 100%;
    box-shadow: 0 6px 20px rgba(0,0,0,.18);
}

/* A wide institution logo. Height-capped rather than width-capped so a very
   wide or very tall file cannot push the sign-in form off the screen. */
.login-logo {
    display: block;
    max-width: 100%;
    max-height: 60px;
    width: auto; height: auto;
}

/* Form controls, scoped to the card so the rest of the system is untouched. */
.login-card .fl { color: rgba(255,255,255,.86); }
.login-card .form-control {
    background: rgba(255,255,255,.14);
    border: 1px solid rgba(255,255,255,.32);
    border-radius: 11px;
    color: #fff;
    padding: 11px 13px;
}
.login-card .form-control::placeholder { color: rgba(255,255,255,.5); }
.login-card .form-control:focus {
    background: rgba(255,255,255,.22);
    border-color: rgba(255,255,255,.85);
    box-shadow: 0 0 0 3px rgba(255,255,255,.16);
    color: #fff;
}
/* Chrome paints its own pale yellow over autofilled fields, which would put
   dark-on-dark text in the box. A long inset shadow is the only way to
   override it. */
.login-card .form-control:-webkit-autofill,
.login-card .form-control:-webkit-autofill:focus {
    -webkit-text-fill-color: #fff;
    -webkit-box-shadow: 0 0 0 1000px rgba(52,52,96,.96) inset;
    caret-color: #fff;
}

.login-card .btn-primary-x {
    background: rgba(255,255,255,.95);
    color: #35337f;
    border-radius: 11px;
    padding: 12px 16px;
    font-weight: 800;
    box-shadow: 0 8px 22px rgba(0,0,0,.2);
}
.login-card .btn-primary-x:hover { background: #fff; color: var(--rail); }

/* --- signing in ---------------------------------------------------------

   The form really is submitting while this plays: the button is swapped for a
   spinner and the card settles back a little, so the wait between pressing the
   key and the dashboard arriving is visibly the system working rather than the
   page having ignored the click. Nothing here delays the request. */
.login-card.is-signing {
    opacity: .82;
    transform: scale(.985);
    filter: saturate(.9);
    pointer-events: none;
}
.login-card.is-signing .form-control { opacity: .7; }

.login-spin {
    width: 16px; height: 16px;
    border: 2px solid rgba(53,51,127,.28);
    border-top-color: #35337f;
    border-radius: 50%;
    animation: login-spin .6s linear infinite;
    flex: 0 0 auto;
}
@keyframes login-spin { to { transform: rotate(360deg); } }

/* Respect a system setting that asks for less motion: the state still changes,
   it simply stops rotating. */
@media (prefers-reduced-motion: reduce) {
    .login-spin { animation: none; }
    .login-card { transition: none; }
}

/* The ownership notice sits on the dark photo now, not on a pale canvas. */
body.auth-body .site-foot,
body.auth-body .site-foot a { color: rgba(255,255,255,.62); }

/* ==========================================================================
   Utilities
   ========================================================================== */

.u-muted  { color: var(--muted) !important; }
.u-faint  { color: var(--faint) !important; }
.u-mono   { font-family: ui-monospace, 'Cascadia Mono', Consolas, monospace; }
.u-nums   { font-variant-numeric: tabular-nums; }
.u-b      { font-weight: 700; }
.u-xs     { font-size: 11px; }
.u-sm     { font-size: 12px; }
.u-right  { text-align: right; }
.u-nowrap { white-space: nowrap; }
.u-sep    { height: 1px; background: var(--line); margin: 12px 0; }

.material-symbols-outlined {
    font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
    vertical-align: middle;
    user-select: none;
}

/* ==========================================================================
   Allocation tree: examination -> paper -> evaluators
   Three states, three colours, used consistently everywhere on the screen:
     green  evaluated      amber  pending with an evaluator
     red    not allocated to anyone yet
   ========================================================================== */

.exam-group {
    margin-bottom: 14px;
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-2);
    overflow: hidden;
}

/* Native <details>/<summary>: collapsing works with the keyboard and without
   JavaScript, and every group starts closed on each load by construction. */
.exam-group > summary,
.paper-card > summary { cursor: pointer; list-style: none; }
.exam-group > summary::-webkit-details-marker,
.paper-card > summary::-webkit-details-marker { display: none; }

.twist {
    transition: transform .16s ease;
    flex: 0 0 auto;
    opacity: .8;
}
details[open] > summary .twist { transform: rotate(90deg); }

.exam-head {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 14px;
    background: linear-gradient(180deg, var(--rail-2), var(--rail));
    color: #fff;
}
.exam-group:not([open]) .exam-head { border-radius: var(--r-lg); }
.exam-head .code { font-size: 14px; font-weight: 800; letter-spacing: .2px; }
.exam-head .sub  { font-size: 11px; opacity: .8; }
.exam-head .spacer { flex: 1; }
.exam-head .chip-line { display: flex; gap: 6px; flex-wrap: wrap; }
.exam-head .pill { background: rgba(255,255,255,.16); color: #fff; }

/* Subjects run one after another, so the divider has to be dark enough to
   read as a boundary at a glance — a hairline in --line disappeared between
   two busy rows. */
.paper-card {
    background: var(--surface);
    border-top: 1px solid var(--faint);
}
.paper-card:first-of-type { border-top: 0; }
.paper-card > summary { padding: 13px 14px; }
.paper-card > summary:hover { background: var(--surface-soft); }
.paper-card[open] > summary { border-bottom: 1px solid var(--line); }
.paper-body { padding: 10px 14px 14px; background: var(--surface-soft); }

.paper-head { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap; }
.paper-head .title { font-size: 13.5px; font-weight: 800; color: var(--ink); }
.paper-head .sub   { font-size: 11px; color: var(--faint); }
.paper-head .spacer { flex: 1; }

/* --- the three stages a paper passes through --------------------------- */
.stages { display: flex; gap: 6px; flex-wrap: wrap; align-items: stretch; }

.stage {
    display: flex; align-items: center; gap: 8px;
    border: 1px solid var(--line-strong);
    background: #fff;
    border-radius: 10px;
    padding: 6px 11px 6px 8px;
    min-width: 132px;
    text-decoration: none; color: inherit;
}
.stage b {
    width: 20px; height: 20px; border-radius: 50%;
    display: grid; place-items: center;
    font-size: 10.5px; font-weight: 800;
    background: var(--neutral-soft); color: var(--neutral-ink);
    flex: 0 0 20px;
}
.stage .t { font-size: 11.5px; font-weight: 700; color: var(--ink-2); line-height: 1.2; }
.stage .v { font-size: 10.5px; color: var(--faint); font-weight: 600; }

.stage.is-done    { border-color: #a6f4c5; background: var(--ok-soft); }
.stage.is-done b  { background: var(--ok); color: #fff; }
.stage.is-done .t { color: var(--ok-ink); }
.stage.is-done .v { color: var(--ok-ink); opacity: .85; }

.stage.is-part    { border-color: #fedf89; background: var(--warn-soft); }
.stage.is-part b  { background: var(--warn); color: #fff; }
.stage.is-part .t { color: var(--warn-ink); }
.stage.is-part .v { color: var(--warn-ink); opacity: .85; }

.stage.is-todo    { border-style: dashed; background: var(--surface-soft); }

.stage.is-get     { border-color: var(--brand); background: var(--brand-soft); }
.stage.is-get b   { background: var(--brand); color: #fff; }
.stage.is-get .t  { color: var(--brand-ink); }
.stage.is-get:hover { box-shadow: var(--shadow-2); }
.stage.is-locked  { opacity: .5; pointer-events: none; border-style: dashed; }

/* --- the five figures, spelled out ------------------------------------ */
.figs {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
    gap: 8px; margin: 10px 0 4px;
}
.fig {
    background: var(--surface-soft); border: 1px solid var(--line);
    border-radius: 9px; padding: 7px 10px;
}
.fig .k { font-size: 10px; font-weight: 700; color: var(--faint); letter-spacing: .2px; }
.fig .v { font-size: 17px; font-weight: 800; line-height: 1.15; font-variant-numeric: tabular-nums; }
.fig.f-done    .v { color: var(--ok-ink); }
.fig.f-pending .v { color: var(--warn-ink); }
.fig.f-free    .v { color: var(--bad-ink); }

.legend { display: flex; gap: 14px; flex-wrap: wrap; font-size: 11px; font-weight: 600; }
.legend i {
    width: 9px; height: 9px; border-radius: 3px;
    display: inline-block; margin-right: 5px; vertical-align: -1px;
}
.legend .l-done    i { background: var(--ok); }
.legend .l-pending i { background: var(--warn); }
.legend .l-free    i { background: var(--bad); opacity: .55; }
.legend .l-done    { color: var(--ok-ink); }
.legend .l-pending { color: var(--warn-ink); }
.legend .l-free    { color: var(--bad-ink); }

/* Evaluator rows beneath a paper. */
.alloc-row {
    display: grid;
    grid-template-columns: minmax(0,1.6fr) 76px 76px 76px minmax(90px,1fr) 108px 118px;
    align-items: center; gap: 8px;
    padding: 8px 10px; margin-top: 6px;
    border: 1px solid var(--line);
    border-left: 3px solid var(--neutral);
    border-radius: 9px;
    background: var(--surface-soft);
    font-size: 12px;
}
.alloc-row.a-done    { border-left-color: var(--ok); }
.alloc-row.a-pending { border-left-color: var(--warn); }
.alloc-row.a-late    { border-left-color: var(--bad); background: var(--bad-soft); }
.alloc-row .who { min-width: 0; }
.alloc-row .who b { display: block; color: var(--ink); font-size: 12.5px; }
.alloc-row .who span { font-size: 10.5px; color: var(--faint); }
.alloc-row .n { text-align: right; font-variant-numeric: tabular-nums; font-weight: 700; }
.alloc-row .n.done    { color: var(--ok-ink); }
.alloc-row .n.pending { color: var(--warn-ink); }
.alloc-row .acts { text-align: right; white-space: nowrap; }

.alloc-head {
    display: grid;
    grid-template-columns: minmax(0,1.6fr) 76px 76px 76px minmax(90px,1fr) 108px 118px;
    gap: 8px; padding: 4px 10px 0;
    font-size: 10px; font-weight: 700; color: var(--faint);
    text-transform: uppercase; letter-spacing: .4px;
}
.alloc-head span:nth-child(n+2):nth-child(-n+4) { text-align: right; }

@media (max-width: 900px) {
    .alloc-row, .alloc-head { grid-template-columns: minmax(0,1fr) 60px 60px 100px; }
    .alloc-row > :nth-child(5), .alloc-head > :nth-child(5) { display: none; }
    .alloc-row > :nth-child(6), .alloc-head > :nth-child(6) { display: none; }
}

/* ==========================================================================
   Scan upload
   ========================================================================== */

.drop-zone {
    border: 2px dashed var(--line-strong);
    border-radius: var(--r-lg);
    background: var(--surface-soft);
    padding: 26px 20px;
    text-align: center;
    transition: border-color .15s ease, background .15s ease;
}
.drop-zone.is-over { border-color: var(--brand); background: var(--brand-soft); }
.drop-zone.is-off  { opacity: .5; pointer-events: none; }

.upload-list { max-height: 420px; overflow-y: auto; }

.upload-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 120px 92px;
    align-items: center;
    gap: 10px;
    padding: 7px 2px;
    border-bottom: 1px solid var(--line);
}
.upload-row .up-name { min-width: 0; font-size: 12.5px; overflow-wrap: anywhere; }
.upload-row .up-state { text-align: right; }

.up-bar {
    height: 6px; border-radius: 999px;
    background: var(--surface-sunk); overflow: hidden;
}
.up-bar span {
    display: block; height: 100%; width: 0;
    background: var(--brand); border-radius: 999px;
    transition: width .18s ease;
}
.up-bar span.is-bad  { background: var(--bad); }
.up-bar span.is-warn { background: var(--warn); }

@media (max-width: 700px) {
    .upload-row { grid-template-columns: minmax(0, 1fr) 84px; }
    .upload-row .up-bar { display: none; }
}

/* ==========================================================================
   Evaluation screen (Stages 6-10)
   Built for hours of continuous use: the script fills the screen, the marks
   panel never moves, and nothing reflows while typing.
   ========================================================================== */

.eval-shell {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 400px;
    gap: 12px;
    /* The top bar, this page's own padding, and the site footer. Guessed
       height here would either scroll the whole evaluation screen or clip the
       marks panel, so the footer's height is subtracted explicitly. */
    /* 48px is .content's own padding (20 top + 28 bottom), measured rather
       than estimated — the previous 26px left the footer 22px below the fold
       and put a scrollbar on a screen that must never scroll. */
    height: calc(100vh - var(--top-h) - var(--foot-h) - 48px);
    min-height: 520px;
}

/* --- left: the answer script ------------------------------------------- */
.eval-viewer {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-2);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    min-width: 0;
}

.eval-toolbar {
    display: flex; align-items: center; gap: 6px;
    padding: 8px 10px;
    border-bottom: 1px solid var(--line);
    background: var(--surface-soft);
    flex-wrap: wrap;
}
.eval-toolbar .sep { width: 1px; height: 20px; background: var(--line-strong); margin: 0 3px; }
.eval-toolbar .spacer { flex: 1; }

.tool-btn {
    width: 30px; height: 30px;
    border-radius: 7px; border: 1px solid transparent;
    background: transparent; color: var(--ink-2);
    display: grid; place-items: center; cursor: pointer;
    transition: background .12s ease;
}
.tool-btn:hover { background: #e8edf7; }
.tool-btn:disabled { opacity: .35; cursor: default; background: transparent; }
.tool-btn .material-symbols-outlined { font-size: 18px; }
.tool-btn.is-on { background: var(--brand); color: #fff; }

.page-jump {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 12px; color: var(--muted); font-weight: 600;
}
.page-jump input {
    width: 46px; padding: 3px 6px; text-align: center;
    border: 1px solid var(--line-strong); border-radius: 6px;
    font-size: 12px; font-variant-numeric: tabular-nums;
}

.eval-stage {
    flex: 1;
    overflow: auto;
    background: #5b6478;
    display: flex;
    justify-content: center;
    padding: 14px;
    position: relative;
}
.eval-canvas-wrap { position: relative; align-self: flex-start; }
#pdfCanvas {
    display: block;
    box-shadow: 0 6px 26px rgba(0,0,0,.35);
    background: #fff;
    border-radius: 2px;
}

/* The watermark is composited over the page. It is a deterrent and a way to
   trace a leaked screenshot — it is not, and is not claimed to be, copy
   protection. */
#pdfWatermark {
    position: absolute; inset: 0;
    pointer-events: none;
    display: grid; place-items: center;
    overflow: hidden;
}
#pdfWatermark span {
    font-size: 15px; font-weight: 800; letter-spacing: 1px;
    color: rgba(16, 24, 40, .13);
    transform: rotate(-32deg);
    white-space: nowrap;
    line-height: 3.6;
    text-align: center;
    user-select: none;
}

.eval-thumbs {
    width: 96px;
    border-right: 1px solid var(--line);
    background: var(--surface-soft);
    overflow-y: auto;
    padding: 8px 6px;
    flex: 0 0 96px;
}
.eval-thumb {
    border: 2px solid transparent; border-radius: 6px;
    margin-bottom: 7px; cursor: pointer; background: #fff;
    display: block; width: 100%; overflow: hidden;
    box-shadow: var(--shadow-1);
}
.eval-thumb canvas { display: block; width: 100%; }
.eval-thumb.is-on { border-color: var(--brand); }
.eval-thumb .n {
    font-size: 10px; font-weight: 700; color: var(--muted);
    text-align: center; padding: 2px 0; background: var(--surface-soft);
}
.eval-body { display: flex; flex: 1; min-height: 0; }

.eval-loading {
    position: absolute; inset: 0; display: grid; place-items: center;
    background: rgba(91, 100, 120, .85); color: #fff;
    font-size: 13px; font-weight: 600; z-index: 5;
}

/* --- right: the marks panel -------------------------------------------- */
.eval-panel {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-2);
    display: flex; flex-direction: column; overflow: hidden;
}
.eval-panel-head { padding: 12px 14px; border-bottom: 1px solid var(--line); }
.eval-panel-body { flex: 1; overflow-y: auto; padding: 10px 14px; }
.eval-panel-foot {
    border-top: 1px solid var(--line);
    background: var(--surface-soft);
    padding: 11px 14px;
}

.mark-section {
    font-size: 11px; font-weight: 800; color: var(--faint);
    text-transform: uppercase; letter-spacing: .5px;
    margin: 14px 0 6px; display: flex; justify-content: space-between;
}
.mark-section:first-child { margin-top: 2px; }

.mark-rule {
    font-size: 11px; font-weight: 700; color: var(--violet-ink);
    background: var(--violet-soft); border-radius: 6px;
    padding: 5px 8px; margin: 6px 0;
}

.mark-row {
    display: flex; align-items: center; gap: 8px;
    padding: 5px 0; border-bottom: 1px solid var(--line);
}
.mark-row.is-parent { border-bottom: 0; padding-bottom: 1px; }
.mark-row .qno {
    font-family: ui-monospace, Consolas, monospace;
    font-size: 12px; font-weight: 700; color: var(--ink);
    flex: 0 0 auto; min-width: 56px;
}
.mark-row .qlabel {
    font-size: 11px; color: var(--faint); flex: 1; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mark-row .qmax { font-size: 11px; color: var(--faint); font-weight: 600; white-space: nowrap; }

.mark-input {
    width: 58px; padding: 5px 7px;
    border: 1px solid var(--line-strong); border-radius: 7px;
    font-size: 13px; font-weight: 700; text-align: right;
    font-variant-numeric: tabular-nums;
    background: #fff; color: var(--ink);
}
.mark-input:focus {
    outline: none; border-color: #a7b3e8;
    box-shadow: 0 0 0 3px rgba(79,70,229,.14);
}
.mark-input.is-over { border-color: var(--bad); background: var(--bad-soft); }
.mark-input.is-saved { border-color: #a6f4c5; }

/* A question the choice rule is not counting. */
.mark-row.is-ignored { opacity: .5; }
.mark-row.is-ignored .qno::after {
    content: 'not counted'; font-family: inherit; font-size: 9px;
    font-weight: 700; color: var(--warn-ink); display: block;
}
.mark-row.is-missing .mark-input { border-color: var(--warn); background: var(--warn-soft); }

.save-state {
    font-size: 11px; font-weight: 700;
    display: inline-flex; align-items: center; gap: 4px;
}
.save-state.idle   { color: var(--faint); }
.save-state.saving { color: var(--info-ink); }
.save-state.saved  { color: var(--ok-ink); }
.save-state.error  { color: var(--bad-ink); }

.total-line {
    display: flex; justify-content: space-between; align-items: baseline;
    padding: 4px 0; font-size: 12px;
}
.total-line .v { font-weight: 700; font-variant-numeric: tabular-nums; }
.total-grand {
    display: flex; justify-content: space-between; align-items: baseline;
    padding: 8px 0 2px; border-top: 1px solid var(--line-strong); margin-top: 6px;
}
.total-grand .l { font-size: 12px; font-weight: 800; }
.total-grand .v { font-size: 24px; font-weight: 800; letter-spacing: -.5px; }

@media (max-width: 1180px) {
    .eval-shell { grid-template-columns: minmax(0, 1fr); height: auto; }
    .eval-viewer { height: 70vh; }
    .eval-thumbs { display: none; }
}

/* --- lockdown overlays -------------------------------------------------
   Opaque, not translucent: while either is up the answer script must not be
   visible at all, so that a screenshot taken from another window captures
   nothing. */
.eval-lock {
    position: fixed; inset: 0; z-index: 9800;
    background: #eef1f7;
    display: grid; place-items: center; padding: 24px;
}
.eval-lock[hidden] { display: none; }

.eval-lock-card {
    background: #fff; border: 1px solid var(--line); border-radius: 18px;
    box-shadow: 0 20px 60px rgba(16,24,40,.14);
    padding: 32px; max-width: 460px; text-align: center;
}
.eval-lock-card h2 { font-size: 17px; font-weight: 800; margin: 10px 0 8px; }
.eval-lock-card p  { font-size: 13px; color: var(--muted); margin: 0 0 18px; line-height: 1.55; }

.eval-lock-logo {
    display: block; max-width: 190px; max-height: 56px;
    width: auto; height: auto; margin: 0 auto 4px;
}

/* Bootstrap styles <kbd> as WHITE text on a dark background. Overriding only
   the background left white-on-near-white — an invisible key name. Every kbd
   rule here therefore states its colour explicitly. */
kbd, .eval-lock-card kbd {
    background: var(--surface-sunk);
    color: var(--ink);
    border: 1px solid var(--line-strong);
    border-bottom-width: 2px; border-radius: 5px;
    padding: 1px 6px; font-size: 11.5px; font-family: inherit; font-weight: 700;
}
.away-count {
    font-size: 46px; font-weight: 800; letter-spacing: -1.5px;
    color: var(--warn-ink); line-height: 1; margin-bottom: 2px;
    font-variant-numeric: tabular-nums;
}
.away-count.is-urgent { color: var(--bad-ink); }

/* Hide the script the instant focus is lost, before any overlay animates in.
   Belt and braces alongside the overlay above: blurred so nothing is legible
   even for the frame before `visibility` applies, and inert so a mark box
   underneath cannot be clicked or typed into through the overlay. */
body.is-hidden-script .eval-viewer,
body.is-hidden-script .eval-panel-body,
body.is-hidden-script .key-modal {
    visibility: hidden;
    filter: blur(22px);
    pointer-events: none;
    user-select: none;
}

/* ---------------------------------------------------------------- pager

   The same control under every list: what is being shown, how many to show at
   a time, and the steps. Identical everywhere on purpose — a user who has
   learned one list has learned all of them.
   ---------------------------------------------------------------------- */
.pager {
    display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
    margin-top: 12px; padding-top: 12px;
    border-top: 1px solid var(--line);
}
.pager-count { font-size: 12px; color: var(--muted); }
.pager-count strong { color: var(--ink); font-weight: 700; }

.pager-size { display: flex; align-items: center; gap: 4px; margin-left: auto; }
.pager-size-btn {
    min-width: 30px; padding: 3px 7px;
    border: 1px solid var(--line-strong);
    border-radius: 7px;
    background: var(--surface);
    color: var(--muted);
    font-size: 11.5px; font-weight: 700;
    text-align: center;
}
.pager-size-btn:hover { border-color: var(--brand); color: var(--brand); }
.pager-size-btn.is-on {
    background: var(--brand); border-color: var(--brand); color: #fff;
}
.pager-size-btn.is-on:hover { color: #fff; }

.pager-steps { display: flex; align-items: center; gap: 5px; }
.pager-steps .material-symbols-outlined { font-size: 16px; }

@media (max-width: 640px) {
    .pager-size { margin-left: 0; }
}

/* ------------------------------------- the evaluator's completed-work rows

   One card per allocation, and one line across it carrying the whole story of
   that allocation. Every field on that line was previously the same pale grey
   as its neighbours, which made it a wall rather than a row — so each field
   now carries its own tone, a defined border, and room to breathe inside it.
   The tones are the palette's existing semantic ones, used for their meaning:
   a deadline is warn, finished work is ok, an overdue deadline is bad.
   ------------------------------------------------------------------------ */
.hist-card { border-color: var(--line-strong); }

.hist-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
    gap: 10px;
}

.hist-cell {
    /* The complaint this answers: the text sat hard against the top edge. */
    padding: 12px 13px 13px;
    border: 1px solid var(--line-strong);
    border-left-width: 3px;
    border-radius: 10px;
    background: var(--surface-soft);
    border-left-color: var(--neutral);
    min-width: 0;
}

.hist-cell .k {
    font-size: 10px; font-weight: 800; letter-spacing: .4px;
    text-transform: uppercase;
    color: var(--muted);
    margin-bottom: 7px;
}
.hist-cell .v {
    font-size: 15px; font-weight: 800; color: var(--ink);
    line-height: 1.25; letter-spacing: -.2px;
    overflow-wrap: anywhere;
}
.hist-cell .s {
    font-size: 11px; color: var(--muted);
    margin-top: 5px; line-height: 1.45;
    overflow-wrap: anywhere;
}

.hist-cell.c-exam   { background: var(--brand-soft);   border-color: #dfe3ff; border-left-color: var(--brand); }
.hist-cell.c-paper  { background: var(--violet-soft);  border-color: #e4e0ff; border-left-color: var(--violet); }
.hist-cell.c-start  { background: var(--info-soft);    border-color: #d6e9ff; border-left-color: var(--info); }
.hist-cell.c-due    { background: var(--warn-soft);    border-color: #fceabb; border-left-color: var(--warn); }
.hist-cell.c-done   { background: var(--ok-soft);      border-color: #cdf3df; border-left-color: var(--ok); }
.hist-cell.c-count  { background: var(--neutral-soft); border-color: var(--line-strong); border-left-color: var(--neutral); }
.hist-cell.c-status { background: var(--surface);      border-color: var(--line-strong); border-left-color: var(--ink-2); }

.hist-cell.c-exam   .k { color: var(--brand-ink); }
.hist-cell.c-paper  .k { color: var(--violet-ink); }
.hist-cell.c-start  .k { color: var(--info-ink); }
.hist-cell.c-due    .k { color: var(--warn-ink); }
.hist-cell.c-done   .k { color: var(--ok-ink); }
.hist-cell.c-count  .k { color: var(--neutral-ink); }

/* A deadline that has passed with work still outstanding, and nothing else,
   is allowed to shout. */
.hist-cell.is-late {
    background: var(--bad-soft);
    border-color: #fbd3ce;
    border-left-color: var(--bad);
}
.hist-cell.is-late .k,
.hist-cell.is-late .v,
.hist-cell.is-late .s { color: var(--bad-ink); }
.hist-cell.is-late .s { font-weight: 700; }

/* All papers done: the count earns the strong green rather than the grey. */
.hist-cell.c-count.is-full {
    background: var(--ok-soft);
    border-color: #cdf3df;
    border-left-color: var(--ok);
}
.hist-cell.c-count.is-full .k,
.hist-cell.c-count.is-full .v { color: var(--ok-ink); }

/* ------------------------------------------------- answer key, over the page

   The scheme of valuation used to open in a new tab, which cost the evaluator
   full screen and started the away countdown every time they checked it. It
   now opens here instead.

   z-index sits BELOW .eval-lock (9800) deliberately: the key is confidential
   too, so the gate and the away screen must cover it, not the other way round.
   ------------------------------------------------------------------------ */
.key-modal {
    position: fixed; inset: 0; z-index: 9500;
    background: rgba(16, 24, 40, .55);
    display: grid; place-items: center; padding: 22px;
}
.key-modal[hidden] { display: none; }

.key-modal-card {
    background: var(--surface);
    border-radius: 14px;
    box-shadow: 0 24px 70px rgba(16, 24, 40, .3);
    width: min(1100px, 96vw);
    height: min(88vh, 100%);
    display: flex; flex-direction: column;
    overflow: hidden;
}
.key-modal-head {
    display: flex; align-items: center; gap: 8px;
    padding: 9px 10px 9px 13px;
    border-bottom: 1px solid var(--line);
    background: var(--surface);
}
.key-modal-title { flex: 1; font-size: 13px; font-weight: 800; }

.key-modal-body { flex: 1; background: var(--surface-sunk); overflow: auto; }
.key-modal-empty { padding: 34px 24px; text-align: center; }

/* The key is drawn page by page onto canvases rather than handed to the
   browser's PDF plug-in, which would come with its own download and print
   buttons. A canvas has neither, and there is no file for a right-click to
   save. */
.key-pages {
    display: flex; flex-direction: column; align-items: center;
    gap: 12px; padding: 14px;
}
.key-pages canvas, .key-pages img {
    display: block; max-width: 100%;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: 4px;
    box-shadow: 0 2px 10px rgba(16, 24, 40, .08);
    user-select: none; -webkit-user-drag: none;
}

/* Print: never make a scanned script convenient to print. */
@media print {
    .rail, .topbar, .no-print, .eval-shell { display: none !important; }
    body::after {
        content: 'Printing answer scripts is not permitted.';
        font: 700 16px system-ui; display: block; padding: 40px;
    }
}
