/* ============================================================
   Leaderboard-only additions on top of main.css (copied as-is from
   the course site). Colours, type and spacing come from main.css's
   tokens only — add a token there rather than a literal here.
   ============================================================ */

/* ============================================================
   Header, page shell and footer from the student blog
   (cs2680-blog/assets/css/blog.css, copied as-is), so the
   leaderboard wears the blog's bar: the MadSys mark and the site's
   own name instead of the SEAS lockup, which moves to the footer.
   ============================================================ */

/* ------------------------------------------------------------
   Page shell
   course.css lays the page out as ordinary blocks, which leaves
   the footer floating in the middle of the window on a short page
   -- the author index and the tag index are both one screen with
   a dozen pills on it. The body becomes a column tall enough to
   fill the viewport and the content between header and footer
   takes up the slack, so the footer is always on the bottom edge
   and never above it.
   ------------------------------------------------------------ */

body {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    /* dvh follows the mobile browser's collapsing address bar; vh above is
       the fallback for anything that does not know the unit. */
    min-height: 100dvh;
}

.container.page {
    flex: 1 0 auto;
}

.site-footer {
    flex-shrink: 0;
}

/* ------------------------------------------------------------
   Header brand
   The course site's header is the SEAS lockup and nothing else,
   which is exactly why this one cannot be. The blog leads with the
   MadSys mark -- the same drawing both sites use as their favicon --
   and its own name, with the course code and term as the small line
   underneath. Same white bar, same crimson rule, different thing
   standing on it.

   It is also the one element that has to hold together from a
   1200px desktop bar down to a 320px phone, beside a hamburger.
   Nothing here is hidden on the way down; the mark and the two
   lines of type step down together.
   ------------------------------------------------------------ */

.site-brand {
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
    margin-right: auto;
    padding: 11px 0;
    color: inherit;
    text-decoration: none;
}

.site-brand:hover,
.site-brand:focus {
    color: inherit;
    text-decoration: none;
}

/* The mark is a hexagon, not a tile, so the corner radius and the box-shadow
   that framed the old square are gone; a drop-shadow filter follows the real
   silhouette instead. */
/* The lab mark is an outline, not a solid shape, so it carries no shadow --
   a drop-shadow on a 19/585 stroke reads as a printing error. It lifts on
   hover and nothing more. */
.brand-mark {
    width: 44px;
    height: 44px;
    flex-shrink: 0;
    transition: transform 0.2s ease;
}

.site-brand:hover .brand-mark {
    transform: translateY(-1px);
}

.brand-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.brand-name {
    font-family: 'Open Sans', sans-serif;
    font-size: 1.3rem;
    font-weight: 700;
    line-height: 1.15;
    letter-spacing: -0.005em;
    color: var(--crimson);
    white-space: nowrap;
    transition: color 0.15s ease;
}

.site-brand:hover .brand-name {
    color: var(--crimson-bright);
}

.brand-sub {
    font-family: 'Open Sans', sans-serif;
    font-size: 0.7rem;
    font-weight: 600;
    line-height: 1.4;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ink-3);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.site-brand:focus-visible {
    outline: 2px solid var(--crimson);
    outline-offset: 3px;
    border-radius: var(--r-sm);
}

/* The brand and the toggler share one row on a phone; neither may push the
   other off it, so the brand shrinks and the toggler does not. */
.site-header .navbar-toggler {
    flex-shrink: 0;
}

@media (max-width: 575px) {
    .site-brand {
        gap: 9px;
        padding: 9px 0;
    }

    .brand-mark {
        width: 38px;
        height: 38px;
    }

    .brand-name {
        font-size: 1.12rem;
    }

    .brand-sub {
        font-size: 0.64rem;
        letter-spacing: 0.08em;
    }
}

@media (max-width: 380px) {
    .brand-mark {
        width: 34px;
        height: 34px;
    }

    .brand-name {
        font-size: 1.02rem;
    }

    .navbar-toggler {
        padding: 6px 8px;
    }

    .navbar-toggler-icon {
        width: 1.2em;
        height: 1.2em;
    }
}

.navbar-nav .nav-link {
    white-space: nowrap;
}

/* On the expanded bar the links and the user's controls (.nav-auth) share one row, each centred on it. main.css gives
   every <li> but the last a 5px bottom margin, which made the list of links 5px taller than the links themselves, so
   they sat 2.5px above the name and the buttons. The collapsed menu keeps the margin: it spaces the menu's rows. */
@media (min-width: 992px) {
    .site-header .navbar-nav .nav-item {
        margin-bottom: 0;
    }
}

/* ------------------------------------------------------------
   Assignment links
   Every assignment's leaderboard, on a row of its own under the brand
   and the links (header.html; the shared host's router fills it in).
   It sits outside the collapsed menu, so a phone shows it too, and the
   current assignment is the one pill filled in crimson. On the site's
   own host the list is empty and the row takes no room.
   ------------------------------------------------------------ */

.site-header .navbar > .container-fluid {
    flex-wrap: wrap;
}

.assignment-nav {
    order: 5;
    flex-basis: 100%;
    padding: var(--s2) 0 var(--s3);
    border-top: 1px solid var(--border-soft);
}

.assignment-nav:not(:has(a)) {
    display: none;
}

.assignment-nav .nav {
    --bs-nav-link-padding-x: 14px;
    --bs-nav-link-padding-y: 4px;
    --bs-nav-link-color: var(--ink-2);
    --bs-nav-link-hover-color: var(--crimson);
    --bs-nav-pills-border-radius: 999px;
    --bs-nav-pills-link-active-color: var(--on-crimson);
    --bs-nav-pills-link-active-bg: var(--crimson);
    gap: var(--s2);
}

.assignment-nav .nav-link {
    font-family: 'Open Sans', sans-serif;
    font-size: 0.95rem;
    font-weight: 600;
    white-space: nowrap;
    border: 1px solid var(--border-strong);
}

.assignment-nav .nav-link:hover {
    border-color: var(--crimson);
    background-color: var(--crimson-050);
}

.assignment-nav .nav-link.active,
.assignment-nav .nav-link.active:hover {
    color: var(--on-crimson);
    border-color: var(--crimson);
    background-color: var(--crimson);
}

.assignment-state {
    font-size: 0.8rem;
    font-weight: 400;
    color: var(--ink-3);
}

.assignment-nav .nav-link.active .assignment-state {
    color: inherit;
}

/* ------------------------------------------------------------
   Collapsed menu
   Below 992px the links become a drop-down panel. Bootstrap leaves
   them as bare inline text; these rules give the panel one row per
   link and the same crimson current-page fill the bar uses.
   ------------------------------------------------------------ */

@media (max-width: 991.98px) {
    /* the assignment links stay right under the brand; the menu opens below them */
    .site-header .navbar-collapse {
        order: 6;
    }

    .navbar-collapse {
        margin: 0 calc(-1 * var(--s3)) var(--s2);
        padding: var(--s2) var(--s3) var(--s3);
        border-top: 1px solid var(--border-soft);
    }

    .navbar-nav {
        margin-left: 0;
        gap: 2px;
    }

    .navbar-nav .nav-item {
        width: 100%;
    }

    .navbar-nav .nav-link {
        padding: 11px 12px !important;
        border-radius: var(--r-sm);
    }
}

/* ------------------------------------------------------------
   Footer additions
   The SEAS lockup moved here when the header took the blog's own
   brand, and the pages the four-item menu leaves out are listed
   beside the menu's own, so the navigation bar is a shortcut and
   never the only way in.
   ------------------------------------------------------------ */

.footer-seas {
    display: inline-block;
    border-bottom: none !important;
    margin-bottom: var(--s4);
}

.footer-seas img {
    height: 54px;
    width: auto;
    opacity: 0.85;
    transition: opacity 0.15s ease;
}

.footer-seas:hover img {
    opacity: 1;
}

.footer-nav {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--s2) var(--s4);
    margin-bottom: var(--s4);
    padding-bottom: var(--s4);
    border-bottom: 1px solid var(--border-soft);
}

.footer-nav a {
    font-family: 'Open Sans', sans-serif;
    font-size: 14px;
    font-weight: 600;
    color: var(--ink-2);
    border-bottom: none;
}

.footer-nav a:hover {
    color: var(--crimson-bright);
    border-bottom: none;
}

@media (max-width: 575px) {
    .footer-seas img {
        height: 44px;
    }
}

/* --- navbar: login / logout -------------------------------- */

.nav-auth {
    display: flex;
    align-items: center;
    margin-left: var(--s3);
}

/* the name in the type of the links beside it (main.css .navbar-nav .nav-link: Open Sans, 1.1rem), so the two share a
   baseline as well as a centre */
.nav-user {
    display: inline-flex;
    align-items: center;
    gap: var(--s3);
    font-family: 'Open Sans', sans-serif;
    font-size: 1.1rem;
    color: var(--ink-3);
}

/* the logged-in name: a menu (Change password) */
.nav-user-name {
    display: inline-flex;     /* the name and the caret, side by side, the name the one to shrink */
    align-items: center;
    max-width: 100%;
    font: inherit;
    color: var(--ink-3);
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
}

.nav-user-name:hover,
.nav-user-name[aria-expanded="true"] {
    color: var(--crimson);
}

/* Where the bar or a phone's menu is short of room, the name gives way, never the buttons: it ends in an ellipsis
   (header.js puts all of it in the title), every box around it may shrink below its content (min-width: 0; the
   menu's own box included, which a flex row would otherwise keep as wide as the name), and the buttons keep their size
   on one line. */
.site-header .navbar-collapse,
.nav-auth,
.nav-user,
.nav-user .dropdown {
    min-width: 0;
}

.nav-user-name [data-user-name] {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.nav-auth .btn-auth {
    flex-shrink: 0;
    white-space: nowrap;
}

/* On the expanded bar a long name is also capped, so that it does not push the links and the buttons onto a row of
   their own: inside, the bar is at most 1152px wide (main.css --page-wide), and the brand, the four links and Log out
   leave about 260px for the name, and about 165px beside Admin (staff only). */
@media (min-width: 992px) {
    .nav-user-name [data-user-name] {
        max-width: 15rem;
    }

    .nav-user:has(> [data-auth="staff"]:not([hidden])) .nav-user-name [data-user-name] {
        max-width: 10rem;
    }
}

.nav-user .dropdown-menu {
    font-family: 'Open Sans', sans-serif;
    font-size: 0.95rem;
}

.nav-user .dropdown-item:active {
    background: var(--crimson);
}

.nav-user[hidden],
.btn-auth[hidden],
.nav-item[hidden] {
    display: none !important;
}

.btn-auth {
    font-family: 'Open Sans', sans-serif;
    font-size: 1rem;
    color: var(--crimson);
    background: var(--bg);
    border: 1px solid var(--crimson);
    border-radius: var(--r-sm);
    padding: 6px 16px;
    text-decoration: none;
    cursor: pointer;
    transition: all 0.2s ease;
}

.btn-auth:hover {
    color: var(--on-crimson);
    background: var(--crimson);
    text-decoration: none;
}

@media (max-width: 991.98px) {
    .nav-auth {
        margin: var(--s2) 0 var(--s3);
        padding: 0 12px;          /* in line with the menu links above (11px 12px) */
    }

    /* Admin, a long name and Log out may not fit on one line of a phone's menu: they wrap rather than widen the page,
       and a name wider than the menu is cut short (above) */
    .nav-user {
        flex-wrap: wrap;
    }
}

/* --- buttons and forms ------------------------------------- */

.btn-crimson {
    font-size: 1rem;
    font-weight: 600;
    color: var(--on-crimson);
    background: var(--crimson);
    border: 1px solid var(--crimson);
    border-radius: var(--r-sm);
    padding: 8px 22px;
    cursor: pointer;
    transition: background 0.2s ease;
}

.btn-crimson:hover:not(:disabled) {
    background: var(--crimson-dark);
}

.btn-crimson:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

/* the same size as .btn-crimson, outlined: crimson border and text on white */
.btn-outline-crimson {
    display: inline-block;
    font-size: 1rem;
    font-weight: 600;
    color: var(--crimson);
    background: var(--bg);
    border: 1px solid var(--crimson);
    border-radius: var(--r-sm);
    padding: 8px 22px;
    text-decoration: none;
    cursor: pointer;
    transition: background 0.2s ease;
}

.btn-outline-crimson:hover {
    color: var(--crimson-dark);
    background: var(--crimson-050);
    text-decoration: none;
}

.form-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s3);
}

.form-alt-link {
    color: var(--crimson);
}

.form-card {
    max-width: 560px;
    margin: var(--s5) auto 0;
    padding: var(--s6);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-1);
}

.form-card.narrow {
    max-width: 400px;
}

.form-field {
    margin-bottom: var(--s5);
}

.form-field label {
    display: block;
    font-weight: 600;
    color: var(--ink-2);
    margin-bottom: var(--s2);
}

.form-field .hint {
    font-size: 0.9rem;
    color: var(--ink-3);
    margin-top: var(--s1);
}

.form-field input[type="text"],
.form-field input[type="email"],
.form-field input[type="password"] {
    width: 100%;
    padding: 8px var(--s3);
    font-size: 1rem;
    color: var(--ink);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-sm);
}

.form-field input:focus {
    outline: none;
    border-color: var(--crimson);
    box-shadow: 0 0 0 3px var(--crimson-100);
}

/* --- zip drop zone ----------------------------------------- */

.dropzone {
    display: block;
    padding: var(--s6) var(--s4);
    text-align: center;
    color: var(--ink-3);
    background: var(--surface);
    border: 2px dashed var(--border-strong);
    border-radius: var(--r);
    cursor: pointer;
    transition: all 0.15s ease;
}

.dropzone:hover,
.dropzone.dragover {
    color: var(--crimson);
    background: var(--crimson-050);
    border-color: var(--crimson);
}

.dropzone .dropzone-main {
    font-size: 1.05rem;
    font-weight: 600;
}

.dropzone .dropzone-file {
    margin-top: var(--s2);
    color: var(--ink);
    font-family: var(--font-mono);
    font-size: 0.95rem;
    word-break: break-all;
}

.dropzone input[type="file"] {
    display: none;
}

/* --- status messages --------------------------------------- */

.status {
    margin-top: var(--s4);
    padding: var(--s3) var(--s4);
    border-radius: var(--r-sm);
    border: 1px solid transparent;
}

.status:empty {
    display: none;
}

.status.error {
    color: var(--crimson-dark);
    background: var(--crimson-050);
    border-color: var(--crimson-200);
}

.status.ok {
    color: var(--green);
    background: var(--green-050);
    border-color: var(--green-200);
}

.status.info {
    color: var(--ink-2);
    background: var(--surface);
    border-color: var(--border);
}

/* submit page: how many submissions a new upload would wait behind */
.queue-banner {
    max-width: 560px;
    margin: var(--s5) auto 0;
    padding: var(--s3) var(--s4);
    color: var(--ink-2);
    background: var(--blue-050);
    border: 1px solid var(--blue-200);
    border-radius: var(--r-sm);
}

.queue-banner b {
    color: var(--blue);
}

.queue-banner + .form-card {
    margin-top: var(--s4);
}

/* when Assignment 4 closes, on that day until it does (the leaderboard's head and the Submit page: closingNotice in header.js) */
.board-closing,
.closing-banner {
    padding: var(--s3) var(--s4);
    color: var(--ink-2);
    background: var(--amber-050);
    border: 1px solid var(--amber-200);
    border-radius: var(--r-sm);
}

.closing-banner {
    max-width: 560px;
    margin: var(--s5) auto 0;
}

.closing-banner + .queue-banner {
    margin-top: var(--s4);
}

/* submit page: Assignment 4 is closed (instead of the form) */
.closed-card p {
    margin: 0;
    color: var(--ink-2);
}

/* submit page: what goes in the zip (src/ and engine/), folded under the field's label */
.format-help {
    margin-bottom: var(--s3);
    color: var(--ink-2);
    font-size: 0.92rem;
}

.format-help summary {
    cursor: pointer;
    color: var(--crimson);
    font-weight: 600;
    margin-bottom: var(--s2);
}

.format-help pre {
    font-size: 0.8rem;
    line-height: 1.45;
    margin-bottom: var(--s2);
    overflow-x: auto;
}

/* the catalog's model ids and the a4-models path are long unbroken words: they wrap rather than widen the page */
.format-help li code {
    overflow-wrap: anywhere;
}

.format-help ul {
    padding-left: 1.2em;
    margin-bottom: var(--s2);
}

.format-help li {
    margin-bottom: var(--s1);
}

/* engines page: long paths and flag names in its tables wrap rather than widen the page */
.engines-doc td code {
    overflow-wrap: anywhere;
}

/* submit page: every problem of a refused zip, under the error */
.status ul.violations {
    margin: var(--s2) 0 0;
    padding-left: 1.2em;
}

.status ul.violations li {
    margin-bottom: var(--s1);
    word-break: break-word;
}

/* submit page: today's gradings toward the daily cap, above the Submit button */
.gradings-line {
    margin-bottom: var(--s4);
    color: var(--ink-2);
}

.gradings-line.out b {
    color: var(--crimson-dark);
}

.gradings-line .of {
    font-size: 0.92rem;
}

/* --- tables ------------------------------------------------ */

.board-table td.rank,
.board-table th.rank {
    width: 4.5em;
    text-align: center;
}

.board-table td.num,
.board-table th.num {
    text-align: right;
}

.board-table td.empty {
    text-align: center;
    color: var(--ink-3);
    padding: var(--s6) var(--s3);
}

.badge-pending {
    display: inline-block;
    font-size: 0.85rem;
    color: var(--amber);
    background: var(--amber-050);
    border: 1px solid var(--amber-200);
    border-radius: 999px;
    padding: 1px 10px;
}

.board-note {
    text-align: center;
    color: var(--ink-3);
    margin-top: var(--s3);
}

/* --- leaderboard card (layout after cachecompetition.com/leaderboard) --- */

:root {
    /* medal colours for ranks 1-3 */
    --gold: #C7932E;
    --silver: #7C7C82;
    --bronze: #AD6B45;
}

.board-card {
    margin-top: var(--s6);
    padding: 28px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: 0 12px 38px rgba(20, 22, 26, 0.10);
}

.board-card .board-title {
    font-size: 32px;
    font-weight: 700;
    color: var(--ink);
    margin: 0 0 var(--s4);
}

.board-card .board-refreshed {
    color: var(--ink-3);
    margin: 0 0 var(--s5);
}

/* leaderboard head: when Assignment 4 closes, on that day until it does (closingNotice in header.js) */
.board-card .board-closing {
    margin: calc(-1 * var(--s3)) 0 var(--s5);
}

table.board {
    width: 100%;
    border-collapse: collapse;
    font-variant-numeric: tabular-nums;
}

table.board th,
table.board td {
    padding: var(--s4);
    text-align: left;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}

table.board td:nth-child(2) {
    white-space: normal;   /* long names wrap; everything else stays on one line */
}

table.board th {
    background: var(--crimson-050);
    color: var(--ink);
    font-size: 0.85rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

table.board tbody tr:hover td {
    background: var(--surface);
}

table.board td.empty {
    text-align: center;
    color: var(--ink-3);
    padding: var(--s6) var(--s4);
}

.board-rank {
    font-weight: 700;
    color: var(--crimson);
}

.board-rank.rank-1 { color: var(--gold); }
.board-rank.rank-2 { color: var(--silver); }
.board-rank.rank-3 { color: var(--bronze); }
.board-rank.pending { color: var(--ink-4); }

.board-score {
    font-weight: 700;
    font-size: 1.05rem;
}

.board-card .board-note {
    text-align: left;
    margin: var(--s4) 0 0;
}

@media (max-width: 575px) {
    .board-card {
        padding: var(--s4);
    }

    .board-card .board-title {
        font-size: 24px;
    }
}

/* main.css frames every .table-responsive; inside the card the card is the frame */
.board-card .table-responsive {
    border: 0;
    border-radius: 0;
    box-shadow: none;
}

/* the logged-in person's own best, below the top 10 */
table.board tr.board-me-label td {
    padding: var(--s5) var(--s4) var(--s2);
    font-size: 0.85rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--crimson);
    border-bottom: 2px solid var(--crimson-200);
    background: var(--bg) !important;
}

table.board tr.board-me td {
    background: var(--crimson-050);
}

/* "Your best submission", and on the right whether the baseline is reached (any one submission of all 26 tasks
   that solved at least BASELINE_SOLVED tasks in at most BASELINE_TIME_S end to end; kept for good) */
table.board tr.board-me-label .board-me-head {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--s2) var(--s3);
}

/* the skyline (index.html, public/skyline.js), under the leaderboard: an inline SVG at the box's width, its
   tooltip on top of it */
.skyline-plot {
    position: relative;
    width: 100%;
    min-height: 320px;
}

.skyline-plot svg {
    display: block;
    max-width: 100%;
}

.skyline-plot .skyline-hit {
    cursor: pointer;
}

.skyline-plot .skyline-hit:focus {
    outline: none;
    stroke: var(--ink);
    stroke-width: 2;
}

.skyline-tip {
    position: absolute;
    z-index: 2;
    max-width: min(340px, 100%);
    padding: var(--s2) var(--s3);
    font-size: 0.92rem;
    line-height: 1.4;
    color: var(--ink);
    background: var(--bg);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-sm);
    box-shadow: 0 6px 18px rgba(20, 22, 26, 0.12);
    pointer-events: none;
}

.skyline-tip[hidden] {
    display: none;
}

/* the legend above it: large swatches, labels in ink; a click hides or shows a group */
.skyline-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2) var(--s5);
    margin: 0 0 var(--s3);
}

.skyline-legend button,
.skyline-legend .legend-you {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    font: inherit;
    font-size: 1.05rem;
    color: var(--ink);
    background: none;
    border: 0;
    padding: 2px 0;
}

.skyline-legend button {
    cursor: pointer;
}

.skyline-legend button.off {
    opacity: 0.35;
}

.skyline-legend .swatch {
    width: 18px;
    height: 18px;
    border-radius: 50%;
    flex: none;
}

.skyline-legend .swatch.ring {
    border: 3px solid var(--crimson);
}

.badge-baseline {
    display: inline-block;
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    text-transform: none;
    border: 1px solid;
    border-radius: 999px;
    padding: 1px 10px;
}

.badge-baseline.ok { color: var(--green); background: var(--green-050); border-color: var(--green-200); }
.badge-baseline.no { color: var(--crimson-dark); background: var(--crimson-050); border-color: var(--crimson-200); }

table.board tr.board-me:hover td {
    background: var(--crimson-100);
}

/* --- grading results --------------------------------------- */

.of {
    color: var(--ink-3);
    font-weight: 400;
}

.muted {
    color: var(--ink-4);
}

.badge-status {
    display: inline-block;
    font-size: 0.85rem;
    border-radius: 999px;
    padding: 1px 10px;
    border: 1px solid;
}

.badge-queued,
.badge-unranked,
.badge-canceled { color: var(--ink-3); background: var(--surface); border-color: var(--border); }
/* a warning: a worker waiting or busy with something else, an audit's suspicious verdict */
.badge-warn { color: var(--amber); background: var(--amber-050); border-color: var(--amber-200); }
.badge-running { color: var(--blue); background: var(--blue-050); border-color: var(--blue-200); }
.badge-best,
.badge-done { color: var(--green); background: var(--green-050); border-color: var(--green-200); }
.badge-error,
.badge-failed,
.badge-timeout { color: var(--crimson-dark); background: var(--crimson-050); border-color: var(--crimson-200); }

.btn-tasks {
    font-size: 0.9rem;
    color: var(--crimson);
    background: var(--bg);
    border: 1px solid var(--crimson-200);
    border-radius: var(--r-sm);
    padding: 2px 10px;
    white-space: nowrap;
    cursor: pointer;
}

.btn-tasks:hover {
    background: var(--crimson-050);
}

.history-table tr.detail-row > td {
    background: var(--surface);
    padding: var(--s3) var(--s4);
}

/* under a submission being graded: how far it has got (updated every 2 minutes), one line on
   the right, a little smaller than the rows of finished ones */
.history-table tr.progress-row > td {
    font-size: 0.92rem;
    color: var(--ink-2);
    padding-top: 6px;
    padding-bottom: 6px;
}

.history-table .progress-line {
    text-align: right;
}

.history-table .copy-total {
    font-size: 0.85rem;
    color: var(--ink-3);
}

.grade-error {
    margin: 0;
    white-space: pre-wrap;
    font-family: var(--font-mono);
    font-size: 0.85rem;
}

table.task-table {
    width: 100%;
    border-collapse: collapse;
    background: var(--bg);
    font-variant-numeric: tabular-nums;
}

table.task-table th,
table.task-table td {
    padding: 6px var(--s3);
    border-bottom: 1px solid var(--border);
    text-align: left;
}

table.task-table th {
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--ink-3);
}

table.task-table .num {
    text-align: right;
}

.ok-mark { color: var(--green); font-weight: 700; }
.no-mark { color: var(--ink-4); }
.task-table .reason { color: var(--ink-3); font-size: 0.92em; }

/* --- Past submissions (history.html): a grading's phase, timeline and failure ------ */

/* how long it has been in its phase, after its badge */
.phase-since {
    color: var(--ink-3);
    font-size: 0.9rem;
    white-space: nowrap;
}

/* the timeline: queued, claimed, engine ready, agent over, finished; each with its time and how long after the one
   before */
ol.timeline {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2) var(--s5);
    list-style: none;
    margin: 0;
    padding: 0;
    font-size: 0.9rem;
    color: var(--ink-2);
}

ol.timeline li {
    margin: 0;
    padding-left: 16px;
    position: relative;
}

ol.timeline li::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0.45em;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--green);
}

ol.timeline li.todo {
    color: var(--ink-4);
}

ol.timeline li.todo::before {
    background: none;
    border: 2px solid var(--border-strong);
}

ol.timeline li.now::before {
    background: var(--blue);
}

ol.timeline .tl-label {
    font-weight: 700;
    color: var(--ink);
}

ol.timeline li.todo .tl-label {
    color: var(--ink-4);
    font-weight: 600;
}

ol.timeline .tl-gap {
    color: var(--ink-3);
}

.detail-block + .detail-block {
    margin-top: var(--s3);
}

.detail-block .engine-line {
    font-size: 0.9rem;
    color: var(--ink-2);
    overflow-wrap: anywhere;
}

.history-table tr.detail-row .progress-line {
    margin-top: var(--s2);
}

/* On a phone the table scrolls sideways; what a detail row says (the timeline, the failure, the tasks) stays in view
   at the width of the screen instead of scrolling away with it. */
@media (max-width: 767.98px) {
    .history-table tr.detail-row > td > * {
        position: sticky;
        left: var(--s4);
        max-width: calc(100vw - 64px);
    }

    .history-table .progress-line {
        text-align: left;
    }
}

/* On a phone, Past submissions leaves out the upload time (the timeline has it) and the file name, so the state of
   a grading and its Cancel button fit on the screen without scrolling the table sideways. */
@media (max-width: 575.98px) {
    .history-table .col-when,
    .history-table .col-file {
        display: none;
    }
}

/* a failed grading: its fixed failure text, and for a failure before the engine was ready the end of the startup
   log, folded */
.failure-box {
    margin: 0;
    white-space: normal;
}

.failure-box details {
    margin-top: var(--s2);
}

.failure-box summary {
    cursor: pointer;
    font-weight: 600;
}

.failure-box pre {
    margin: var(--s2) 0 0;
    max-height: 28rem;
    overflow: auto;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    font-family: var(--font-mono);
    font-size: 0.8rem;
    color: var(--ink);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    padding: var(--s2) var(--s3);
}

/* --- the development site (SITE_ENV=dev; src/worker.js page() puts it on every page) --- */

.dev-banner {
    padding: 6px var(--s4);
    text-align: center;
    font-size: 0.95rem;
    color: var(--ink);
    background: var(--amber-200);
    border-bottom: 2px solid var(--amber);
}

.dev-banner b {
    font-weight: 700;
}
