/* ---------- 2-column workspace: source/form settings | results to review. Used by both
   AiActivityGenerator and WebContentLab — lives here (global, unscoped) since Blazor CSS
   isolation can't share one scoped rule across two different components. ---------- */
.build-3col-grid {
    display: grid;
    grid-template-columns: 300px minmax(0, 1fr);
    gap: 1rem;
    align-items: start;
}

@media (max-width: 992px) {
    .build-3col-grid {
        grid-template-columns: 1fr;
    }
}

/* ---------- Compose page: the same grid grows a third column for the basket.
   A modifier rather than a change to the base rule, because the Build tab drawer
   and the Web Content lab both use the two-column form and have no basket. ---------- */
/* Widths are deliberately not Quizora's. The parameter form here has paired fields
   (difficulty next to thinking level, type next to option count) that go cramped
   under about 300px, so it keeps that much and no less. The basket holds one-line
   previews, which a few more pixels show more of, so it is the one that grows. The
   centre column keeps nearly all of the rest, because it has to hold code blocks and
   answer options. */
.build-3col-grid.build-3col-with-basket {
    grid-template-columns: 300px minmax(0, 1fr) 280px;
    gap: 1.25rem;
}

/* Both rails stay put while the suggestions scroll, so the teacher can add a
   question and see the count move without scrolling back up. 5rem clears the
   sticky 68px top bar with a little air. */
.build-3col-grid.build-3col-with-basket > .build-3col-col1,
.build-3col-grid.build-3col-with-basket > .build-3col-col3 {
    position: sticky;
    top: 5rem;
}

/* Writing a question by hand: one wide column and the basket. The generator's two columns
   are hidden rather than removed, so they leave the grid flow and this template lines the
   replacement up against the basket, which keeps the width and position it has everywhere
   else. */
.build-3col-grid.build-3col-with-basket.build-3col-replaced {
    grid-template-columns: minmax(0, 1fr) 250px;
}

/* The parameter rail is a stack of cards, one per section, rather than one tall
   panel. Each section is a thing you either need or do not, so giving each its own
   edge makes a closed one read as a shut drawer instead of missing content. The
   column itself is therefore just the frame: no border of its own.

   It has no height cap and no scrollbar. It does not need one, because only a
   single section is ever open, so the rail cannot grow past a viewport. */

.build-3col-grid.build-3col-with-basket .aigen-section {
    background: var(--surface-card);
    border: 1px solid var(--border-medium);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    padding: var(--space-3);
}


.build-3col-col3 {
    min-width: 0;
}

/* Each column reads as its own surface, so the three roles stay legible: what you
   are asking for, what came back, and what you have kept. Scoped to the basket
   variant, because the Build tab drawer already sits on a panel of its own and a
   second border inside it would just be a box in a box. The border is one step
   darker than a normal panel's, since these are the page's primary division. */
.build-3col-grid.build-3col-with-basket > .build-3col-col2,
.build-3col-grid.build-3col-with-basket .build-3col-col3 > .ex-panel,
.build-3col-grid.build-3col-with-basket .build-3col-col3 > .compose-basket-wrap > .ex-panel {
    background: var(--surface-card);
    border: 1px solid var(--border-medium);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
}

.build-3col-grid.build-3col-with-basket > .build-3col-col2 {
    padding: var(--space-4);
}

/* The centre column is now a bordered panel in its own right, so the empty state's
   dashed box would draw a second outline inside the first. Drop it and let the
   column's own border carry the shape. */
.build-3col-grid.build-3col-with-basket .build-results-empty {
    border: 0;
    min-height: 420px;
}

@media (max-width: 1400px) {
    .build-3col-grid.build-3col-with-basket {
        grid-template-columns: 275px minmax(0, 1fr) 240px;
        gap: 1rem;
    }
}

@media (max-width: 992px) {
    .build-3col-grid.build-3col-with-basket {
        grid-template-columns: 1fr;
    }

    /* Stacked, sticky rails would follow the reader down the whole suggestion list
       and cover it, and neither needs its own scroll once it has the full width. */
    .build-3col-grid.build-3col-with-basket > .build-3col-col1,
    .build-3col-grid.build-3col-with-basket > .build-3col-col3 {
        position: static;
        max-height: none;
        overflow-y: visible;
    }
}

/* ---------- Basket ---------- */
/* Two lines per row. The number and the start of the question share the first, so the text gets
   the whole width; what the question is worth and what can be done with it sit under the text. */
.compose-basket-item {
    display: grid;
    grid-template-columns: 1.5rem minmax(0, 1fr);
    column-gap: .5rem;
    row-gap: .25rem;
    align-items: start;
    padding: .5rem 0;
    border-bottom: 1px solid var(--bs-border-color);
}
/* The arrows that move a question appear with the pointer; a touch screen has none, so they are always there. */
.compose-basket-move { display: inline-flex; gap: .15rem; opacity: 0; transition: opacity .12s; }
.compose-basket-item:hover .compose-basket-move,
.compose-basket-item:focus-within .compose-basket-move { opacity: 1; }
@media (hover: none) {
    .compose-basket-move { opacity: 1; }
}
.compose-basket-meta {
    grid-column: 2;
    display: flex;
    flex-wrap: wrap;
    min-width: 0;
    align-items: center;
    gap: .2rem .35rem;
}

.compose-basket-item:last-child {
    border-bottom: 0;
}

.compose-basket-num {
    flex: 0 0 1.5rem;
    height: 1.5rem;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: var(--bs-tertiary-bg);
    font-size: var(--fs-2xs);
    font-weight: 600;
}

.compose-basket-text {
    flex: 1;
    min-width: 0;
    font-size: var(--fs-xs);
    line-height: 1.4;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* The whole question, beside the basket, while a row is hovered. inset-inline-end puts it on
   the side facing the rest of the page in both directions: to the right of a left-hand basket in
   Hebrew, to the left of a right-hand one in English. Long questions fade out at the bottom
   rather than scroll, since the card never takes the pointer. */
.compose-basket-wrap { position: relative; }
.compose-peek {
    position: absolute;
    top: 0;
    inset-inline-end: calc(100% + .75rem);
    z-index: 20;
    width: min(34rem, 55vw);
    max-height: min(70vh, 36rem);
    overflow: hidden;
    padding: var(--space-3);
    background: var(--surface-card);
    border: 1px solid var(--border-medium);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
    pointer-events: none;
}
.compose-peek::after {
    content: "";
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    height: 2.5rem;
    background: linear-gradient(transparent, var(--surface-card));
}
@media (max-width: 992px) {
    .compose-peek { display: none; }
}

/* A question's time, first on the row's second line. Never wraps or shrinks: a range cut in
   half reads as a different number. */
.compose-basket-time {
    flex: 0 0 auto;
    /* First on the second line; the actions are pushed to its far end. */
    margin-inline-end: auto;
    padding: .05rem .4rem;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--radius-pill, 999px);
    background: var(--bs-tertiary-bg);
    font-size: var(--fs-2xs);
    font-weight: 600;
    white-space: nowrap;
}

/* -- Quiz settings step --
   Settings on the start side in cards that fold, the quiz on the wide side. The settings
   column stays put while the quiz scrolls, so a points change can be checked against the
   total without scrolling back up. */
.details-grid {
    display: grid;
    /* The basket is as wide as the compose page's, so the list reads as one thing from step to step. */
    grid-template-columns: minmax(0, 1fr) 280px;
    gap: 1.25rem;
    align-items: start;
}
@media (max-width: 1400px) {
    .details-grid { grid-template-columns: minmax(0, 1fr) 240px; }
}
/* The cards as capsule tabs across the top: a step number, or a done or missing mark, and the name. */
.details-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    margin-bottom: 1.1rem;
    padding-inline-start: .5rem;
}
/* Room above the tabs, as on the compose page: they belong with the panel they open, not with the title. */
.details-tab {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    padding-block: .3rem;
    padding-inline: .35rem .85rem;
    border: 1px solid var(--border-light);
    border-radius: 999px;
    background: var(--surface-card, #fff);
    color: var(--ink-primary);
    font-size: var(--fs-sm);
    font-weight: 600;
    white-space: nowrap;
}
button.details-tab:hover { border-color: var(--primary-500, var(--bs-primary)); }
.details-tab.is-selected { background: var(--bs-primary); border-color: var(--bs-primary); color: #fff; }
.details-tab.is-soon {
    padding-inline: .75rem;
    border-style: dashed;
    background: transparent;
    color: var(--ink-secondary);
    cursor: default;
}
.details-tab-step {
    display: inline-grid;
    place-items: center;
    width: 1.45rem;
    height: 1.45rem;
    border-radius: 50%;
    background: var(--surface-subtle);
    color: var(--ink-secondary);
    font-size: var(--fs-2xs);
    font-weight: 700;
}
.details-tab-step.is-ok { background: var(--bs-success-bg-subtle); color: var(--bs-success-text-emphasis); }
.details-tab-step.is-missing { background: var(--bs-danger-bg-subtle); color: var(--bs-danger-text-emphasis); }
.details-tab.is-selected .details-tab-step { background: rgba(255, 255, 255, .22); color: #fff; }
.details-tab-step .bi { font-size: .75rem; line-height: 1; }
.details-tab-count {
    padding: 0 .4rem;
    border-radius: 999px;
    background: var(--surface-subtle);
    color: var(--ink-secondary);
    font-size: var(--fs-2xs);
    font-weight: 700;
    line-height: 1.5;
}
.details-tab.is-selected .details-tab-count { background: rgba(255, 255, 255, .22); color: #fff; }
@media (max-width: 768px) {
    /* One row that scrolls sideways on a phone, rather than tabs stacked three deep. */
    .details-tabs { flex-wrap: nowrap; overflow-x: auto; padding-bottom: .25rem; }
}

/* The end side: the basket. Sticky, so saving stays in reach beside a long card. */
.details-basket-col { position: sticky; top: 5rem; }
@media (max-width: 992px) {
    /* minmax(0, ...) and not 1fr: a plain 1fr grows to its longest unbroken line. */
    .details-grid { grid-template-columns: minmax(0, 1fr); }
    .details-basket-col { position: static; }
}
.details-pane,
.details-basket-col { min-width: 0; }

/* The lab basket mirrors the quiz basket's compact outline, but communicates the learner
   journey: units, their required work, and the final consolidation. */
.lab-basket-units li { align-items: flex-start; }
.lab-basket-evidence { margin-top: .2rem; font-size: var(--fs-2xs); line-height: 1.35; }
.lab-basket-final {
    display: flex;
    align-items: center;
    gap: .35rem;
    margin: var(--space-2) 0;
    padding-top: var(--space-2);
    border-top: 1px solid var(--border-light);
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
}

/* The wide side: the chosen card's work. A column whose body takes the free height, so the foot with
   the way on sits at the bottom of the panel however short the card is. */
.details-pane {
    display: flex;
    flex-direction: column;
    min-height: 24rem;
}
.details-pane-body { flex: 1 0 auto; min-width: 0; }
.details-pane-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
    margin-bottom: 1rem;
}
.details-pane-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem 1rem;
    margin-bottom: .75rem;
}
.details-pane-toolbar .details-action-row > .btn { flex: 0 0 auto; }
.details-pane-foot {
    display: flex;
    justify-content: flex-end;
    margin-top: 1.25rem;
    padding-top: .9rem;
    border-top: 1px solid var(--border-light);
}
/* Lists and pickers that read badly stretched across a wide screen. */
.details-pane-narrow { max-width: 44rem; }
.details-form-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .9rem 1.5rem;
    max-width: 52rem;
}
.details-form-wide { grid-column: 1 / -1; }
@media (max-width: 768px) {
    .details-form-grid { grid-template-columns: 1fr; }
}
.details-subhead {
    max-width: 52rem;
    margin: 1.25rem 0 .75rem;
    padding-top: 1rem;
    border-top: 1px solid var(--border-light);
    font-size: var(--fs-xs);
    font-weight: 700;
    color: var(--ink-secondary);
}

/* Scoring: a line per question - its number, the start of its text and time, its points. */
.details-score-list { list-style: none; margin: 0; padding: 0; }
.details-score-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: .4rem .75rem;
    padding: .55rem 0;
    border-bottom: 1px solid var(--border-light);
}
.details-score-row:last-child { border-bottom: 0; }
.details-score-text {
    display: flex;
    flex-direction: column;
    gap: .1rem;
    min-width: 0;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    text-align: start;
}
.details-score-text:hover .details-score-prompt { color: var(--bs-primary); }
/* A line with nothing to open - a PDF paper's question number - reads as plain text. */
.details-score-text:disabled { cursor: default; opacity: 1; color: inherit; }
.details-score-text:disabled:hover .details-score-prompt { color: inherit; }
.details-score-prompt {
    font-size: var(--fs-sm);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.details-score-meta { font-size: var(--fs-2xs); color: var(--ink-secondary); }
.details-score-points { display: flex; align-items: center; gap: .3rem; }
.details-score-full {
    grid-column: 1 / -1;
    padding: .6rem .8rem;
    border-radius: .4rem;
    background: var(--surface-subtle);
}
.details-points { width: 5rem; }
.details-total.is-off { color: var(--bs-danger-text-emphasis); }

/* Minutes are one or two digits. Full-width boxes made the range read as two free-text fields. */
.qfields-minutes { max-width: 6rem; }


/* -- Question sources --
   One row of entry points above the three panels. The disabled ones stay in the row rather
   than being hidden: a teacher hunting for "import my old exam" needs to see that it is
   planned, not conclude it does not exist. */
.compose-manual-preview {
    border-top: 1px solid var(--border-light);
    padding-top: .6rem;
}

.compose-sources {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-bottom: 1.75rem;
}
.compose-source {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .45rem .85rem;
    border: 1px solid var(--border-light);
    border-radius: var(--radius-pill, 999px);
    background: var(--surface-card, #fff);
    color: var(--ink-secondary);
    font-size: var(--fs-sm);
    font-weight: 600;
    line-height: 1.2;
    cursor: pointer;
}
.compose-source:hover:not(:disabled):not(.is-active) {
    border-color: var(--primary-500, var(--bs-primary));
    color: var(--bs-primary);
}
.compose-source.is-active {
    background: var(--bs-primary);
    border-color: var(--bs-primary);
    color: #fff;
    cursor: default;
}
.compose-source:disabled { opacity: .55; cursor: not-allowed; }
.compose-source-soon {
    padding: .05rem .4rem;
    border-radius: var(--radius-pill, 999px);
    background: var(--surface-subtle);
    color: var(--ink-secondary);
    font-size: var(--fs-2xs);
    font-weight: 600;
}

/* Only the list scrolls, so the count above it and the continue button below it
   stay visible no matter how many questions are in the basket. Sized against the
   viewport so a short screen does not push the button off the bottom. */
.compose-basket-list {
    max-height: clamp(12rem, calc(100vh - 15rem), 44rem);
    overflow-y: auto;
    overflow-x: hidden;
}

.build-3col-col2 {
    min-width: 0;
}

/* ---------- Results empty state — before generation runs, the center column would
   otherwise sit completely blank next to the form column, which reads as broken
   rather than "nothing here yet". Same icon+title+description convention used
   for empty states elsewhere in the app. ---------- */
.build-results-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: .5rem;
    min-height: 320px;
    padding: 2rem 1.5rem;
    border: 1px dashed var(--border-medium);
    border-radius: var(--radius-md);
    color: var(--ink-muted);
}

.build-results-empty i {
    font-size: var(--fs-xl);
    color: var(--primary-500);
}

.build-results-empty i.is-working { animation: build-sparkle 2.4s ease-in-out infinite; }
@keyframes build-sparkle {
    0%, 100% { transform: scale(1) rotate(0deg); opacity: .85; }
    50% { transform: scale(1.18) rotate(8deg); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .build-results-empty i.is-working { animation: none; } }

.build-results-empty strong {
    color: var(--ink-primary);
    font-size: var(--fs-md);
}

.build-results-empty span {
    font-size: var(--fs-sm);
    max-width: 22rem;
    line-height: 1.5;
}

/* ── Blazor validation (keep — Bootstrap doesn't cover these Blazor-specific selectors) ── */
h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid var(--primary-600);
}

.invalid {
    outline: 1px solid var(--bs-danger);
}

.validation-message {
    color: var(--bs-danger);
    font-size: var(--fs-xs);
}

.blazor-error-boundary {
    background: var(--bs-danger);
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

.blazor-error-boundary::after {
    content: "An error has occurred.";
}

#blazor-error-ui {
    background: var(--bs-danger);
    color: white;
    padding: .75rem 1.25rem;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 1080;
    display: none;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    float: inline-end;
}

.darker-border-checkbox.form-check-input {
    border-color: var(--border-strong);
}

/* ── Export-to-PDF (window.print): show only the element marked .print-area, hide the rest ── */
@media print {
    body * {
        visibility: hidden;
    }

    .print-area, .print-area * {
        visibility: visible;
    }

    .print-area {
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
    }

    /* The page's decorative background and card shadows are screen-only; on paper (and in the
       server PDF) the sheet is plain white, and a question card is never split across two pages. */
    html, body {
        background: #fff !important;
    }

    .print-area .card {
        box-shadow: none !important;
        border: 1px solid #ccc !important;
        break-inside: avoid;
    }
}

/* Float placeholder to end while unfocused (form-floating RTL/LTR) */
.form-floating > .form-control-plaintext::placeholder,
.form-floating > .form-control::placeholder {
    color: var(--ink-faint);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder,
.form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* ── App-wide utilities ── */

/* Cursor helpers */
.cursor-pointer { cursor: pointer; }

/* Prevent text selection on interactive UI elements */
.no-select { user-select: none; }

/* Full-page centered flex — used by auth pages */
.page-centered {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Card shadow variant slightly stronger than Bootstrap's shadow-sm */
.card-lifted {
    box-shadow: var(--shadow-md);
    border: 1px solid var(--border-light);
}

.border-dashed {
    border-style: dashed !important;
}

/* Status badge dots */
.status-dot {
    display: inline-block;
    width: .55rem;
    height: .55rem;
    border-radius: 50%;
    vertical-align: middle;
    margin-inline-end: .3rem;
}

.status-dot.active   { background: var(--primary-500); }
.status-dot.idle     { background: var(--accent-amber); }
.status-dot.offline  { background: var(--ink-faint); }
.status-dot.danger   { background: var(--accent-coral); }

/* Was a hardcoded violet with no relation to the palette; now the deep brand tone. */
.bg-purple { background-color: var(--primary-900) !important; }

/* ── Markdown-rendered question prompts (build-md) ── */
/* Keep prose tight in compact lists, and give embedded code blocks a dark, scrollable pane. */
.build-md p:last-child { margin-bottom: 0; }
.build-md p { margin-bottom: .5rem; }
/* Mixed-language prose: each block picks its own direction from its own content
   (English paragraph → LTR, Hebrew → RTL) even when the page is RTL. */
.build-md p,
.build-md li,
.build-md h1, .build-md h2, .build-md h3, .build-md h4,
.build-md blockquote,
.build-md td, .build-md th {
    unicode-bidi: plaintext;
}
/* -- Answer options --
   Rows, not list items. The key box holds the correct-answer mark, so nothing trails the
   text and nothing can wrap onto a line by itself. The row takes the exam's direction from
   its container; the text box isolates whatever runs are inside it, which is how a code
   token keeps its own left-to-right order inside a Hebrew sentence. */
.q-option {
    display: flex;
    align-items: flex-start;
    gap: .4rem;
    padding: .2rem .4rem;
    border-radius: var(--radius-xs);
}
.q-option + .q-option { margin-top: .15rem; }
.q-option-key {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.15rem;
    height: 1.15rem;
    margin-top: .1rem;
    border: 1px solid var(--border-light);
    border-radius: 50%;
    background: var(--surface-subtle);
    color: var(--ink-secondary);
    font-size: var(--fs-2xs);
    font-weight: 600;
    line-height: 1;
}
/* min-width:0 so a long unbroken code token wraps inside the row instead of widening it. */
.q-option-text { unicode-bidi: isolate; min-width: 0; }
/* With no check mark beside it, the tint and the filled key box are the only thing saying
   which answer is right, so the row carries a border as well — a flat tint alone reads as
   a hover state on a card that already has several. */
.q-option.is-correct {
    background: var(--bs-success-bg-subtle);
    border: 1px solid var(--bs-success-border-subtle);
}
.q-option.is-correct .q-option-text { color: var(--bs-success-text-emphasis); font-weight: 600; }
.q-option.is-correct .q-option-key {
    background: var(--bs-success);
    border-color: var(--bs-success);
    color: #fff;
}
.build-md pre {
    margin: .5rem 0 0;
    border-radius: var(--radius-xs);
    overflow: auto;
    max-height: 320px;
    max-width: 100%;
    width: 100%;
    background: var(--surface-subtle);
    border: 1px solid var(--border-light);
    padding: .75rem 1rem;
    direction: ltr;
    text-align: left;
}
/* highlight.js's own .hljs class carries the atom-one-dark theme's dark background straight
   onto the <code> element, which otherwise sits on top of (and hides) the lighter <pre>
   background above — the two were never actually contesting the same property before. */
.build-md pre code { font-size: var(--fs-sm); color: var(--ink-primary); background: transparent !important; }

/* A suggestion card exists to be judged, and the 320px cap cut even a 16-line snippet, so
   deciding whether to keep a code question meant scrolling inside the block first. Raised
   to fit a realistic exam snippet of roughly 26 lines whole. Still capped rather than
   removed: a 40-line block would push the other suggestions off the screen entirely, and
   at that length scrolling is the right answer. The saved-question list in the Build tab
   keeps the tighter cap, where compact rows matter more than reading the code in place. */
.build-3col-col2 .build-md pre {
    max-height: 560px;
}
/* Inline code keeps its own LTR run inside RTL prose. */
.build-md :not(pre) > code {
    font-size: .82em;
    direction: ltr;
    unicode-bidi: isolate;
    color: inherit;
    font-family: monospace;
}
/* Plain (uncolored) inline code still gets the tint — only text carrying its own <font color>
   (applied via the editor's color picker, or pasted in) should override it. Bootstrap's
   "subtle" triad (bg/border/text-emphasis) is already tuned per-theme for light AND dark mode,
   unlike a hardcoded rgba() tint which only ever looked right in one of the two. */
.build-md :not(pre) > code:not(:has(> font[color])) { color: var(--bs-danger-text-emphasis); }
/* MathLive's default virtual-keyboard layer (105) sits behind Bootstrap modals (1055).
   Formula entry is also available inside Student Preview, so keep the keyboard above it. */
body {
    --keyboard-zindex: 2000;
}

/* ── Teacher library ───────────────────────────────────────────────────────────────────────
   One row per item: what it is, the name and the student-facing description, then where it
   comes from and how many quizzes hold a copy. Actions sit at the row's end. */
.library-page { max-width: 64rem; }
.library-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .5rem;
}
.library-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: .75rem;
    padding: .6rem .9rem;
    border: 1px solid var(--border-light);
    border-radius: var(--radius-md, .5rem);
    background: var(--surface-card, #fff);
}
.library-row.no-icon { grid-template-columns: minmax(0, 1fr) auto; }
.library-row-icon {
    width: 2rem;
    height: 2rem;
    display: grid;
    place-items: center;
    border-radius: .5rem;
    background: var(--surface-subtle);
    color: var(--bs-primary);
    font-size: 1rem;
}
.library-row-body { min-width: 0; }
.library-row-title { font-size: var(--fs-sm); font-weight: 600; line-height: 1.35; }
.library-row-desc {
    margin-top: .15rem;
    font-size: var(--fs-xs);
    color: var(--ink-secondary);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
/* One line that never wraps, so no line can start with a separator dot; the course list in the
   middle is what gives way, cut with an ellipsis. */
.library-row-meta {
    margin-top: .2rem;
    min-width: 0;
    overflow: hidden;
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: .15rem .45rem;
    font-size: var(--fs-2xs);
    color: var(--ink-secondary);
}
.library-row-meta > span { flex-shrink: 0; white-space: nowrap; }
.library-row-source {
    margin-top: .1rem;
    font-size: var(--fs-2xs);
    color: var(--ink-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.library-row-used { color: var(--bs-success-text-emphasis); font-weight: 600; }
/* The facts on a row read as one line, each set apart by a dot rather than by wide gaps. */
.library-row-meta > span + span::before {
    content: "·";
    margin-inline-end: .45rem;
    color: var(--ink-secondary);
    opacity: .6;
    font-weight: 400;
}
.library-row-actions { display: flex; gap: .15rem; }
/* Row actions stay quiet until pointed at, so the item, not its buttons, is what the eye finds. */
.library-action {
    width: 2rem;
    height: 2rem;
    padding: 0;
    display: inline-grid;
    place-items: center;
    border: 0;
    border-radius: .4rem;
    background: transparent;
    color: var(--ink-secondary);
}
.library-action:hover,
.library-action:focus-visible { background: var(--surface-subtle); color: var(--ink-primary); }
.library-action.is-danger:hover,
.library-action.is-danger:focus-visible { background: var(--bs-danger-bg-subtle); color: var(--bs-danger-text-emphasis); }

/* The tools catalog is the main-menu entry meant to catch the eye: when it is not the current page
   its label reads darker than its neighbours and its icon carries the brand colour. */
.ex-tab.ex-tab-feature:not(.active) { color: var(--ink-secondary); font-weight: var(--fw-medium, 500); }
.ex-tab.ex-tab-feature:not(.active) i { color: var(--ink-muted); }

/* The course a quiz is filed under, one line under the page title on the question-assembly page. */
.compose-field-row { display: flex; align-items: center; gap: .35rem; }
.compose-course-select { flex: 1 1 auto; width: auto; min-width: 0; }

/* ── Create menu ──────────────────────────────────────────────────────────────────────────
   A main-menu entry that opens a panel of activity kinds: a card each with a CSS-drawn picture of
   what building it looks like, then plain links to the less common kinds. */
.ex-create-menu { display: flex; align-items: stretch; }
.ex-create-caret { font-size: .65em; opacity: .7; }
.ex-create-panel {
    width: min(58rem, calc(100vw - 2rem));
    padding: 1rem;
    border: 1px solid var(--border-light);
    border-radius: var(--radius-lg, .75rem);
    white-space: normal;
}
.ex-create-head { margin-bottom: .75rem; }
.ex-create-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: .75rem;
}
.ex-create-card {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    border: 1px solid var(--border-light);
    border-radius: var(--radius-md, .5rem);
    background: var(--surface-card, #fff);
    color: inherit;
    text-decoration: none;
    transition: var(--transition-fast);
}
.ex-create-card:hover,
.ex-create-card:focus-visible {
    color: inherit;
    border-color: var(--primary-500, var(--bs-primary));
    box-shadow: 0 4px 14px rgba(0, 0, 0, .08);
    transform: translateY(-1px);
}
.ex-create-preview {
    height: 6.5rem;
    padding: .75rem 1rem;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: .35rem;
    background: var(--surface-subtle);
    border-bottom: 1px solid var(--border-light);
}
.ex-create-card-body { padding: .65rem .75rem .8rem; }
.ex-create-card-title {
    display: flex;
    align-items: center;
    gap: .4rem;
    font-size: var(--fs-sm);
    font-weight: 700;
    color: var(--primary-900);
}
.ex-create-card-desc {
    margin-top: .25rem;
    font-size: var(--fs-2xs);
    line-height: 1.45;
    color: var(--ink-secondary);
}
.ex-create-more {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .35rem .9rem;
    margin-top: .85rem;
    padding-top: .75rem;
    border-top: 1px solid var(--border-light);
}
.ex-create-more-link {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    font-size: var(--fs-xs);
    font-weight: 600;
    color: var(--ink-primary);
    text-decoration: none;
}
.ex-create-more-link:hover { color: var(--bs-primary); }

/* The pictures: shapes only, no text, so they read the same in every language. */
.mock-w-90 { width: 90%; }
.mock-w-80 { width: 80%; }
.mock-w-70 { width: 70%; }
.mock-w-60 { width: 60%; }
.mock-w-55 { width: 55%; }
.mock-line { display: block; height: .45rem; border-radius: 999px; background: var(--border-light); }
.mock-opt {
    display: flex;
    align-items: center;
    gap: .35rem;
    height: .8rem;
    padding-inline: .3rem;
    border: 1px solid var(--border-light);
    border-radius: .25rem;
    background: var(--surface-card, #fff);
}
.mock-opt i { width: .45rem; height: .45rem; border-radius: 50%; background: var(--border-light); flex-shrink: 0; }
.mock-opt::after { content: ""; flex: 1; max-width: 60%; height: .3rem; border-radius: 999px; background: var(--border-light); }
.mock-opt.is-correct { border-color: var(--bs-success-border-subtle); background: var(--bs-success-bg-subtle); }
.mock-opt.is-correct i { background: var(--bs-success); }
.mock-bubble {
    display: block;
    align-self: flex-start;
    height: 1.1rem;
    border: 1px solid var(--border-light);
    border-radius: .6rem;
    background: var(--surface-card, #fff);
}
.mock-bubble.is-me { align-self: flex-end; border-color: transparent; background: var(--primary-100); }
.mock-step { display: flex; align-items: center; gap: .45rem; }
.mock-sheet { display: flex; gap: .5rem; height: 100%; }
.mock-page {
    flex: 1.2;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: .3rem;
    padding: .4rem .5rem;
    background: var(--surface-card);
    border: 1px solid var(--border-light);
    border-radius: var(--radius-xs);
}
.mock-answer {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: .35rem;
    padding: .4rem .5rem;
    border: 1px dashed var(--primary-200);
    border-radius: var(--radius-xs);
    background: var(--primary-50);
}
.mock-answer i { display: block; height: .3rem; border-radius: 999px; background: var(--primary-200); }
.mock-answer i:last-child { width: 60%; }
.mock-step i {
    width: .8rem;
    height: .8rem;
    flex-shrink: 0;
    border: 2px solid var(--border-light);
    border-radius: 50%;
    background: var(--surface-card, #fff);
}
.mock-step::after { content: ""; flex: 1; max-width: 75%; height: .4rem; border-radius: 999px; background: var(--border-light); }
.mock-step.is-done i { border-color: var(--primary-700); background: var(--primary-700); }
.mock-step.is-current i { border-color: var(--primary-700); }
.mock-step.is-current::after { background: var(--primary-100); }


/* Materials and tools on the quiz-settings cards: one compact row each, the name and the
   student-facing description on one line apiece, actions at the end. */
.details-attached {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .35rem;
}
.details-attached-item {
    display: flex;
    align-items: flex-start;
    gap: .5rem;
    padding: .4rem .55rem;
    border: 1px solid var(--border-light);
    border-radius: .4rem;
    background: var(--surface-card, #fff);
}
.details-attached-item.is-hidden { opacity: .6; }
.details-attached-icon { margin-top: .1rem; color: var(--bs-primary); }
.details-attached-body { flex: 1; min-width: 0; }
.details-attached-title,
.details-attached-desc {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.details-attached-title { font-size: var(--fs-xs); font-weight: 600; }
.details-attached-desc { font-size: var(--fs-2xs); color: var(--ink-secondary); }

/* Library toolbar and groups: search, a course filter and a sort on one line, then the items in
   collapsible groups with a count. */
.library-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
    margin-bottom: .75rem;
}
.library-search { position: relative; flex: 1 1 14rem; max-width: 22rem; }
.library-search > i {
    position: absolute;
    inset-inline-start: .65rem;
    top: 50%;
    transform: translateY(-50%);
    color: var(--ink-secondary);
    pointer-events: none;
}
.library-search > input { padding-inline-start: 2rem; }
.library-filter { width: auto; max-width: 15rem; }
.library-group { margin-bottom: 1rem; }
.library-group-head {
    display: flex;
    align-items: center;
    gap: .45rem;
    width: 100%;
    padding: .2rem 0;
    border: 0;
    background: transparent;
    color: var(--ink-secondary);
    font-size: var(--fs-xs);
    font-weight: 600;
    text-align: start;
}
.library-group-head:hover { color: var(--ink-primary); }
.library-group-icon { font-size: .95rem; line-height: 1; }
.library-group-title { white-space: nowrap; }
.library-group-count {
    padding: .05rem .45rem;
    border-radius: 999px;
    background: var(--surface-subtle);
    font-size: var(--fs-2xs);
    line-height: 1.4;
}
.library-group-rule { flex: 1; height: 1px; background: var(--border-light); }
.library-group-caret { font-size: .7rem; }
.library-group .library-list { margin-top: .4rem; }
.library-row-meta > .library-row-courses {
    flex-shrink: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Tool preview: a large window, the tool filling everything under its name. */
.tool-view { display: flex; flex-direction: column; height: 100%; min-height: 0; }
/* Quiz settings, empty tools card: common tools as one-click chips. */
.details-tool-suggestions { display: flex; flex-wrap: wrap; gap: .35rem; }
.details-tool-chip {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .25rem .6rem;
    border: 1px solid var(--border-light);
    border-radius: 999px;
    background: var(--surface-card, #fff);
    color: var(--ink-primary);
    font-size: var(--fs-xs);
}
.details-tool-chip:hover { border-color: var(--primary-500, var(--bs-primary)); }

/* Editing a tool: a narrow column of settings beside the tool, which fills the rest. The tool is
   what is being judged, so it gets the room. Nothing scrolls inside: the column is as long as its
   cards, and folding them keeps it short. */
.tool-edit-body {
    flex: 1 1 auto;
    min-height: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    overflow: hidden;
}
/* The settings are the cards themselves, outlined in the brand green, with no frame or title of their
   own around them, and the buttons under them. */
.tool-edit-fields {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    font-size: var(--fs-2xs);
}
.tool-edit-cards {
    display: flex;
    flex-direction: column;
    gap: .85rem;
}
/* One card per question the teacher answers about the tool: what students see, what this kind of
   tool needs set. */
.tool-card {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    padding: .6rem .7rem .7rem;
    border: 1px solid var(--primary-500, var(--bs-primary));
    border-radius: var(--radius-md, .5rem);
}
.tool-card-title {
    display: flex;
    align-items: center;
    gap: .45rem;
    margin: 0;
    padding-bottom: .45rem;
    border-bottom: 1px solid var(--border-light);
    font-size: var(--fs-2xs);
    font-weight: 700;
    color: var(--ink-primary);
}
/* Folding cards: the heading is a button, and a folded card is its heading alone. */
.tool-card-title.is-toggle {
    width: 100%;
    border-inline: 0;
    border-top: 0;
    background: none;
    text-align: start;
    cursor: pointer;
}
.tool-card-title.is-toggle:hover { color: var(--primary-900); }
.tool-card-title.is-toggle[aria-expanded="false"] { padding-bottom: 0; border-bottom: 0; }
/* The name keeps to one line; a folded card's summary gives way to it, cut short if it must. */
.tool-card-title > span:not(.tool-card-summary) { white-space: nowrap; }
.tool-card-summary {
    flex: 1 1 0;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 400;
    color: var(--ink-secondary);
}
.tool-card-summary.is-off { color: var(--bs-danger-text-emphasis); }
.tool-card-caret { margin-inline-start: auto; color: var(--ink-secondary); font-size: var(--fs-2xs); }
.tool-card-title > .bi:first-child {
    display: inline-grid;
    place-items: center;
    width: 1.3rem;
    height: 1.3rem;
    border-radius: 50%;
    background: var(--primary-100);
    color: var(--primary-700);
    font-size: .7rem;
}
/* The fields themselves: even columns on one baseline, so the strip reads as one row of answers
   rather than a pile of boxes of different heights. */
.tool-fields-grid {
    flex: 1 1 auto;
    min-width: 0;
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    align-content: start;
    gap: .4rem .6rem;
}
.tool-field { min-width: 0; display: flex; flex-direction: column; gap: .15rem; }
.tool-field.is-double { grid-column: span 2; }
.tool-field.is-triple { grid-column: span 3; }
.tool-field.is-wide { grid-column: 1 / -1; }
.tool-field-scroll { max-height: 8rem; overflow-y: auto; }
@media (max-width: 900px) {
    .tool-fields-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .tool-field.is-double,
    .tool-field.is-triple { grid-column: 1 / -1; }
}

/* A label is one line: the name of the field on the start side, a way out to the provider on the
   other. Anything longer lives in the field's own tooltip. */
.tool-edit-fields .form-label {
    display: flex;
    align-items: baseline;
    gap: .35rem;
    margin: 0;
    font-size: var(--fs-2xs);
    color: var(--ink-secondary);
    white-space: nowrap;
}
.tool-field-link { margin-inline-start: auto; font-size: var(--fs-2xs); text-decoration: none; }
.tool-field-link:hover { text-decoration: underline; }
.tool-edit-fields .form-text { font-size: var(--fs-2xs); line-height: 1.25; margin: 0; }
.tool-edit-fields .form-control,
.tool-edit-fields .form-select { font-size: var(--fs-2xs); padding: .25rem .45rem; }
.tool-edit-fields .btn { font-size: var(--fs-2xs); }

/* What a pasted address means, under the address: a word for it and a line saying what students get. */
.tool-mode { display: flex; align-items: center; gap: .35rem; min-width: 0; }
.tool-mode-chip {
    flex-shrink: 0;
    padding: .05rem .4rem;
    border-radius: 999px;
    font-weight: 600;
    background: var(--surface-subtle);
    color: var(--ink-secondary);
    border: 1px solid var(--border-light);
}
.tool-mode-chip.is-user { background: var(--primary-100); color: var(--primary-900); border-color: var(--primary-700, #257A59); }
.tool-mode-chip.is-starter { background: #fef3c7; color: #92400e; border-color: #fcd34d; }
.tool-mode-chip.is-simulation { background: #eff6ff; color: #1e40af; border-color: #93c5fd; }

/* The four edges of a provider's frame, trimmed in one short row. */
.tool-crop { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .35rem; }
.tool-crop-side { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.tool-crop-side > span { font-size: var(--fs-2xs); color: var(--ink-secondary); }
.tool-crop-side .form-control { width: 100%; padding-inline: .25rem; text-align: center; }
.tool-edit-fields .form-control-color { width: 2.2rem; padding: .15rem; }
.tool-edit-fields .form-check-label { font-size: var(--fs-2xs); }
.tool-edit-fields code { font-size: var(--fs-2xs); }

/* Cancel and confirm at the foot of the column, under the cards they answer, every time. */
.tool-settings-actions {
    flex: 0 0 auto;
    display: flex;
    gap: .4rem;
    padding-top: .6rem;
}
.tool-settings-actions .btn { flex: 1 1 auto; white-space: nowrap; }

/* The other shape, for the menu: the settings stand beside the tool instead of over it. Browsing the
   catalogue means looking at the tool while it is set up, so the two sit side by side; a quiz's
   settings step has already chosen, and gives the tool the width. */
.tool-edit-body.is-side { flex-direction: row; }
.is-side .tool-edit-fields { flex: 0 0 17rem; max-height: none; }
.is-side .tool-fields-grid { grid-template-columns: minmax(0, 1fr); }
.is-side .tool-field.is-double,
.is-side .tool-field.is-triple,
.is-side .tool-field.is-wide { grid-column: span 1; }
@media (max-width: 900px) {
    .tool-edit-body.is-side { flex-direction: column; }
    .is-side .tool-edit-fields { flex: 0 0 auto; }
    .is-side .tool-fields-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.tool-edit-preview {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    min-height: 0;
    border: 1px solid var(--border-light);
    border-radius: var(--radius-md, .5rem);
    background: var(--surface-subtle);
    overflow: hidden;
}
/* The tool's pane is titled by a strip saying it is the student's view. */
.tool-pane-label {
    padding: .4rem .75rem;
    border-bottom: 1px solid var(--border-light);
    font-size: var(--fs-2xs);
    color: var(--ink-secondary);
    background: var(--surface-subtle);
}
.tool-edit-preview .tool-view { flex: 1; min-height: 0; background: var(--surface-card, #fff); }
.tool-edit-preview .tool-pane-label { display: flex; align-items: center; }
.tool-pane-full { margin-inline-start: auto; line-height: 1; color: var(--ink-secondary); }
.tool-pane-full:hover { color: var(--primary-700); }
/* A tool's listed pages, as named links over its frame; the one open is marked. */
.tool-page-links { display: flex; flex-wrap: wrap; gap: .3rem; padding: .35rem .5rem; border-bottom: 1px solid var(--border-light); background: var(--surface-subtle); }
.tool-page-link { display: inline-flex; align-items: center; gap: .2rem; padding: .1rem .55rem; border: 1px solid var(--border-light); border-radius: 999px; background: var(--surface-card, #fff); color: var(--ink-primary); font-size: var(--fs-2xs); }
.tool-page-link:hover { border-color: var(--primary-500, var(--bs-primary)); }
/* The student's pane header, drawn in a preview the way the runner draws it. */
.tool-view-head { display: flex; align-items: center; gap: .5rem; padding: .45rem .6rem; border-bottom: 1px solid var(--border-light); background: var(--bs-light, #f8f9fa); color: var(--ink-primary); font-size: var(--fs-xs); }
.tool-view-head .btn-link { color: rgba(27,34,51,.68); }
.tool-page-link.is-open { background: var(--primary-100); border-color: var(--primary-700, #257A59); color: var(--primary-900); font-weight: 600; }
/* A page in the tool's settings: the words, the link, and a way to drop it. */
.tool-page-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: .25rem; padding-bottom: .4rem; border-bottom: 1px dashed var(--border-light); }
.tool-page-row > input:first-child { grid-column: 1; }
.tool-page-row > input:nth-child(2) { grid-column: 1; grid-row: 2; }
.tool-page-row > button { grid-column: 2; grid-row: 1 / span 2; align-self: center; }
/* Switching the middle between the document being written and the student's view of it. */
.tool-pane-switch { margin-inline-start: auto; padding: .05rem .5rem; font-size: var(--fs-2xs); }
.tool-pane-switch + .tool-pane-full { margin-inline-start: .6rem; }
/* The document written in the middle: it scrolls inside the pane, toolbar and all. */
.tool-pane-editor { flex: 1; min-height: 0; overflow: auto; padding: .5rem; background: var(--surface-card, #fff); }
.tool-pane-editor .ProseMirror { min-height: 17rem; }
/* The tool opened to the whole window, over the dimmed page, until it is put back. Specific enough
   to win over either shape's own height. */
.tool-edit-body.tool-settings-body .tool-edit-preview.is-full {
    position: fixed;
    inset: 1rem;
    z-index: 1080;
    height: auto;
    box-shadow: 0 0 0 100vmax rgba(0, 0, 0, .35), var(--shadow-lg);
}


/* A page's heading and the picker's tabs on one line: the title on the start side, the categories
   filling the space beside it and wrapping under it only when they must. */
.tool-picker-head {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    margin-bottom: .35rem;
}
.tool-picker-tabs { display: flex; flex-wrap: wrap; gap: .35rem; }
/* Under a heading the tabs run across the middle of the page; alone - a quiz's settings card -
   they start where everything else on the card starts. */
.tool-picker-head > .tool-picker-tabs:not(:first-child) { justify-content: center; }
/* Inside the head the row is one line of a heading, not a section of its own. */
.tool-picker-head .compose-sources { margin-bottom: 0; }
.tool-picker-head .ex-page-head { margin-bottom: 0; }
.tool-picker-head .ex-page-sub { max-width: 30rem; }

/* A page of handwriting, in a quiz and in the checking screen: as tall as a sheet of working, and
   the tool's own chrome above it. */
/* Typing or handwriting, for a quiz question that takes both: two tabs over the one answer shown
   (the question sheet's own are in PdfDocumentRunner.razor.css). */
.answer-tabs {
    display: flex;
    gap: .25rem;
    margin-bottom: .4rem;
    border-bottom: 1px solid var(--border-light);
}
.answer-tab {
    display: inline-flex;
    align-items: center;
    gap: .15rem;
    padding: .3rem .75rem;
    margin-bottom: -1px;
    border: 1px solid transparent;
    border-radius: var(--radius-sm, .35rem) var(--radius-sm, .35rem) 0 0;
    background: transparent;
    color: var(--ink-secondary);
    font-size: var(--fs-xs);
    font-weight: 600;
}
.answer-tab:hover { color: var(--ink-primary); }
.answer-tab.is-active {
    border-color: var(--border-light);
    border-bottom-color: var(--surface-card);
    background: var(--surface-card);
    color: var(--primary-700, var(--bs-primary));
}
.answer-tab-dot {
    width: .45rem;
    height: .45rem;
    margin-inline-start: .35rem;
    border-radius: 50%;
    background: var(--primary-500, var(--bs-primary));
}

.answer-pad, .check-ink { height: min(78vh, 46rem); display: flex; flex-direction: column; }
.answer-pad .handwriting-pad,
.check-ink .handwriting-pad {
    flex: 1;
    min-height: 0;
    border: 1px solid var(--border-light);
    border-radius: var(--radius-md, .5rem);
    overflow: hidden;
}
.check-ink { height: auto; max-height: 40rem; overflow-y: auto; }
/* The page a handwriting question hands over, shown at a glance while the quiz is being built. */
.build-preview-pad .handwriting-pad {
    height: 11rem;
    border: 1px solid var(--border-light);
    border-radius: var(--radius-md, .5rem);
    overflow: hidden;
}
.check-ink .handwriting-pad { min-height: 18rem; }

/* Writing at full size: the pad over everything else, with a little air around it. CSS rather than
   the Fullscreen API, which a locked exam is already holding and watching for exits. */
/* Above everything the exam itself puts on screen - its header, a floating tool, the teacher's
   preview banner - and below the lockdown warnings, which must never be covered. */
.handwriting-backdrop {
    position: fixed;
    inset: 0;
    z-index: 10000;
    background: rgba(15, 23, 42, .45);
}
.handwriting-pad.handwriting-expanded {
    position: fixed;
    top: 4.5rem;
    bottom: 3rem;
    left: 3rem;
    right: 3rem;
    z-index: 10001;
    height: auto;
    border: 1px solid var(--border-medium);
    border-radius: var(--radius-lg, .75rem);
    background: var(--surface-card, #fff);
    box-shadow: 0 1.5rem 3rem rgba(15, 23, 42, .25);
    overflow: hidden;
}
@media (max-width: 768px) {
    .handwriting-pad.handwriting-expanded { top: 3rem; bottom: 1rem; left: 1rem; right: 1rem; }
}

/* A checked paper shows every page at once, one under the other, each with its number. */
.handwriting-pages { display: flex; flex-direction: column; gap: .5rem; overflow-y: auto; }
.handwriting-page { display: flex; flex-direction: column; }
.handwriting-page .handwriting-surface { width: 100%; aspect-ratio: 210 / 297; }
.handwriting-page-label { padding: .15rem .4rem; color: var(--ink-secondary); }
.handwriting-page-count { min-width: 2.2rem; text-align: center; color: var(--ink-secondary); }

/* The tools of a category, as capsules: one press opens its settings, a second closes them. */
.tool-capsules { display: flex; flex-wrap: wrap; gap: .4rem; margin: .6rem 0; }
/* On a page of its own the row runs across the middle, under the categories it belongs to; inside a
   quiz's card it starts where the card starts. */
.tool-capsules.is-centred { justify-content: center; }
.tool-capsule {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .35rem .7rem;
    font-size: var(--fs-xs);
    font-weight: 600;
    color: var(--ink-primary);
    background: var(--surface-card, #fff);
    border: 1px solid var(--border-light);
    border-radius: 999px;
}
.tool-capsule:hover { border-color: var(--border-medium); }
.tool-capsule.is-open {
    color: var(--primary-900);
    background: var(--primary-100);
    border-color: var(--primary-700, #257A59);
}
.tool-capsule.is-added { opacity: .7; }
.tool-capsule:disabled { opacity: .55; cursor: not-allowed; }
.tool-capsule-note { display: inline-flex; align-items: center; gap: .15rem; color: var(--ink-secondary); font-weight: 500; }
/* A saved tool and the x that forgets it travel together, so the x cannot wrap onto its own line. */
.tool-capsule-pair { display: inline-flex; align-items: center; gap: .25rem; }
/* In a quiz the tools are cards: the name, and a line under it saying what the tool is. */
.tool-capsules.is-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
    gap: .5rem;
}
.is-cards .tool-capsule {
    flex-wrap: wrap;
    align-content: start;
    height: 100%;
    padding: .6rem .75rem;
    border-radius: var(--radius-md, .5rem);
    text-align: start;
}
.is-cards .tool-capsule-name { flex: 1 1 0; min-width: 0; }
.is-cards .tool-capsule-desc {
    flex: 1 0 100%;
    font-size: var(--fs-2xs);
    font-weight: 400;
    line-height: 1.45;
    color: var(--ink-secondary);
}
.is-cards .tool-capsule-pair { display: flex; align-items: stretch; }
.is-cards .tool-capsule-pair > .tool-capsule { flex: 1 1 auto; min-width: 0; }
/* What the page asks beside the tool's own fields - in the menu, which quiz it joins. */
.tool-settings-extra .form-text { margin-top: .15rem; }

.tool-settings { display: flex; flex-direction: column; gap: .5rem; }
.tool-settings-title { margin: 0; font-size: var(--fs-md, 1rem); font-weight: 700; }
.tool-settings-crumbs { display: flex; flex-wrap: wrap; align-items: baseline; gap: .4rem; }
.tool-settings-crumb { font-size: var(--fs-md, 1rem); font-weight: 600; text-decoration: none; }
.tool-settings-crumb:hover { text-decoration: underline; }
.tool-settings-crumb-sep { color: var(--ink-secondary); }
.tool-settings-back { padding: .1rem .5rem; font-size: var(--fs-2xs); }
.tool-settings-help { margin-top: .25rem; font-size: var(--fs-xs); line-height: 1.55; color: var(--ink-secondary); }
/* How a tool is worked, folded under what it does: there when wanted, not in the way. */
.tool-settings-guide { margin-top: .35rem; font-size: var(--fs-xs); line-height: 1.55; color: var(--ink-secondary); }
.tool-settings-guide > summary { width: fit-content; color: var(--primary-700); font-weight: 600; cursor: pointer; }
.tool-settings-guide > div { margin-top: .3rem; padding: .5rem .7rem; border-radius: var(--radius-md, .5rem); background: var(--surface-subtle); }
.tool-settings-head {
    display: flex;
    align-items: flex-start;
    gap: .5rem;
    padding: 0 .1rem;
}
/* In a quiz's settings the settings are a narrow column on the start side and the tool takes the
   rest - the tool is what is being judged. The settings never move above it. The tool has a height
   of its own; the settings are as long as they need. */
.tool-settings-body { overflow: visible; }
.tool-settings-body { align-items: flex-start; }
.tool-settings-body:not(.is-side) { flex-direction: row; }
.tool-settings-body:not(.is-side) .tool-edit-fields { flex: 0 0 20rem; }
.tool-settings-body:not(.is-side) .tool-edit-preview {
    flex: 1 1 auto;
    min-width: 0;
    height: min(72vh, 36rem);
}
/* The fields follow the width they are given, not the screen's: one column in the narrow strip. */
.tool-settings-body:not(.is-side) .tool-edit-fields { container-type: inline-size; }
@container (max-width: 30rem) {
    .tool-fields-grid { grid-template-columns: minmax(0, 1fr); }
    .tool-field.is-double,
    .tool-field.is-triple,
    .tool-field.is-wide { grid-column: span 1; }
}
.tool-settings-body.is-side .tool-edit-preview { height: min(72vh, 34rem); }

/* Quiz settings, tools card: two pickers side by side, the add button under them, above the list. */
.details-tool-add {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .35rem;
    margin-bottom: .75rem;
}
.details-tool-add .btn { grid-column: 1 / -1; }

/* Quiz settings panel: explanations behind an info icon, beside the label or button they explain. */
.details-field-head { display: flex; align-items: center; gap: .25rem; margin-bottom: .25rem; }
.details-action-row { display: flex; align-items: center; gap: .35rem; }
.details-action-row > .btn { flex: 1; }
.details-info {
    display: inline-grid;
    place-items: center;
    width: 1.4rem;
    height: 1.4rem;
    flex-shrink: 0;
    border-radius: 50%;
    color: var(--ink-secondary);
    font-size: .8rem;
    cursor: help;
}
.details-info:hover,
.details-info:focus-visible { color: var(--bs-primary); background: var(--surface-subtle); outline: none; }

/* Quiz settings: the basket from the assembly step, on the end side. */
.details-basket-head {
    display: flex;
    align-items: flex-start;
    gap: .45rem;
    font-size: var(--fs-sm);
    font-weight: 700;
}
.details-basket-head > .bi { color: var(--primary-700); }
.details-basket-facts {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .15rem .8rem;
    margin-top: .35rem;
    font-size: var(--fs-xs);
    color: var(--ink-secondary);
}
.details-basket-list {
    list-style: none;
    margin: .6rem 0 0;
    padding: 0;
    max-height: 18rem;
    overflow-y: auto;
}
.details-basket-list li {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: start;
    gap: .5rem;
    padding: .4rem 0;
    border-bottom: 1px solid var(--border-light);
}
.details-basket-list li:last-child { border-bottom: 0; }
.details-basket-text {
    font-size: var(--fs-xs);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.details-basket-pts { font-size: var(--fs-2xs); color: var(--ink-secondary); white-space: nowrap; }
.details-basket-pts.is-off { color: var(--bs-danger-text-emphasis); }
.details-basket-actions {
    display: grid;
    /* Side by side only when both keep their whole label; in the narrow column they stack. */
    grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
    gap: .35rem;
    margin-top: .65rem;
}
.details-basket-actions .btn { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.details-basket-save {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    margin-top: .75rem;
    padding-top: .75rem;
    border-top: 1px solid var(--border-light);
}

/* Making a quiz: the way back one step, where a page has one, and a small line of where the page sits.
   The line keeps one place on every step - at the end side, whether or not a back button is there - so
   it does not jump between pages. The row is as tall as a small button even where there is none, so the
   title below starts at the same height too. */
.details-flow {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    min-height: calc(1.5 * .875rem + .5rem + 2px);
    gap: .5rem 1rem;
    margin-bottom: 1rem;
}
.details-flow-steps {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .25rem;
    margin: 0;
    margin-inline-start: auto;
    padding: 0;
    list-style: none;
    font-size: var(--fs-xs);
    color: var(--ink-secondary);
}
.details-flow-steps li { display: inline-flex; align-items: center; gap: .25rem; }
/* The page's title shares the line: title at the start, the steps at the end, and a way back, where
   the page has one, beside the steps - so the steps sit where they always do. */
.details-flow > .ex-page-title { margin: 0; }
.details-flow > .btn { margin-inline-start: auto; }
.details-flow > .btn + .details-flow-steps { margin-inline-start: 0; }
/* The separator mirrors with the page direction, so it points the way the steps go. */
.details-flow-steps li + li::before { content: "\203A"; margin-inline-end: .25rem; opacity: .6; }
.details-flow-steps li.is-done { color: var(--bs-success-text-emphasis); }
/* A step that can be gone back to reads as plain text until pointed at. */
.details-flow-steps a.details-flow-link { color: inherit; text-decoration: none; }
.details-flow-steps a.details-flow-link:hover { text-decoration: underline; }
/* Grey says "not available": every step ahead that cannot be gone to - locked or only a place on the line -
   reads the same. One that can be gone to keeps the normal colour. */
.details-flow-steps li:not(.is-done):not(.is-current) { color: var(--ink-muted); }
.details-flow-steps li:not(.is-done):not(.is-current) > a.details-flow-link { color: var(--ink-secondary); }
.details-flow-steps .details-flow-link.is-disabled { cursor: not-allowed; }
.details-flow-steps li.is-current { color: var(--primary-900); font-weight: 700; }
/* The current step is underlined the way the main menu marks its active tab - a rounded primary bar -
   thinner here, to suit the smaller text. */
.details-flow-steps li.is-current > span { position: relative; }
.details-flow-steps li.is-current > span::after {
    content: "";
    position: absolute;
    inset-inline: 0;
    bottom: -.3rem;
    height: 2px;
    border-radius: var(--radius-pill);
    background: var(--primary-700);
}

/* Summary and publishing: sections down the wide pane, the published state's code and QR first. */
.publish-section + .publish-section {
    margin-top: 1.25rem;
    padding-top: 1.1rem;
    border-top: 1px solid var(--border-light);
}
.publish-section-title {
    display: flex;
    align-items: center;
    gap: .45rem;
    margin: 0 0 .65rem;
    font-size: var(--fs-sm);
    font-weight: 700;
}
.publish-section-title > .bi { color: var(--primary-700); }
.publish-ready {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .45rem;
    font-size: var(--fs-sm);
    font-weight: 600;
}
.publish-ready.is-ok { color: var(--bs-success-text-emphasis); }
.publish-ready.is-blocked { color: var(--bs-danger-text-emphasis); }
.publish-muted { color: var(--ink-secondary); font-weight: 400; }
.publish-issues { list-style: none; margin: .5rem 0 0; padding: 0; }
.publish-issues li {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: .55rem;
    padding: .45rem 0;
    border-bottom: 1px solid var(--border-light);
    font-size: var(--fs-xs);
}
.publish-issues li:last-child { border-bottom: 0; }
.publish-issues .is-block { color: var(--bs-danger-text-emphasis); }
.publish-issues .is-warn { color: var(--bs-warning-text-emphasis); }

.publish-summary {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 13rem), 1fr));
    gap: .65rem;
}
.publish-block {
    min-width: 0;
    padding: .65rem .75rem;
    background: var(--surface-subtle);
    border: 1px solid var(--border-light);
    border-radius: var(--radius-lg);
}
.publish-block-head {
    display: flex;
    align-items: center;
    gap: .4rem;
    margin-bottom: .3rem;
    font-size: var(--fs-xs);
    font-weight: 700;
}
.publish-block-head > .bi { color: var(--primary-700); }
.publish-block-edit { margin-inline-start: auto; font-size: var(--fs-2xs); font-weight: 600; }
.publish-block-edit.is-disabled { color: var(--ink-secondary); opacity: .6; cursor: not-allowed; }
.publish-block-line {
    font-size: var(--fs-xs);
    color: var(--ink-secondary);
    /* A setting reads "name: value" and the value can be a sentence: it wraps rather than being cut. */
    overflow-wrap: anywhere;
}
.live-draft-note {
    display: inline-flex;
    align-items: center;
    font-size: var(--fs-xs);
    color: var(--ink-secondary);
}
.live-draft-note.is-ready { color: var(--primary-700, #257A59); font-weight: 600; }

/* ── Grading ───────────────────────────────────────────────────────────────────────────
   The class or one paper, never both, in the pane the building steps use; the numbers keep
   the narrow column beside either. */
.grade-state { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .6rem; }
.grade-state-line { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.grade-state-actions { display: inline-flex; flex-wrap: wrap; gap: .4rem; }

.grade-filters { display: flex; gap: .4rem; margin-bottom: .6rem; }
.grade-filters input[type="search"] { flex: 1 1 auto; min-width: 0; }
.grade-filters select { flex: 0 0 auto; width: auto; font-size: var(--fs-xs); }

.grade-list { list-style: none; margin: 0; padding: 0; }
.grade-row { border-bottom: 1px solid var(--border-light); }
.grade-row:last-child { border-bottom: 0; }
.grade-row-open {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto 1rem;
    align-items: center;
    gap: .6rem;
    width: 100%;
    padding: .5rem .35rem;
    text-align: start;
    background: none;
    border: 0;
    border-inline-start: 3px solid transparent;
}
button.grade-row-open:hover { background: var(--surface-subtle); }
.grade-row[data-state="pass"] .grade-row-open      { border-inline-start-color: var(--bs-success); }
.grade-row[data-state="fail"] .grade-row-open      { border-inline-start-color: var(--bs-danger); }
.grade-row[data-state="ungraded"] .grade-row-open  { border-inline-start-color: var(--border-medium); }
.grade-row.is-absent .grade-row-open { opacity: .6; }
.grade-row-flags { display: inline-flex; gap: .3rem; }
.grade-row-score { display: inline-flex; align-items: baseline; gap: .4rem; }
.grade-percent { font-size: var(--fs-lg); font-weight: 800; color: var(--ink-primary); }
.grade-row[data-state="fail"] .grade-percent { color: var(--bs-danger-text-emphasis); }
.grade-row[data-state="pass"] .grade-percent { color: var(--primary-700, #257A59); }
.grade-row-chevron { color: var(--ink-faint); font-size: var(--fs-xs); }
.grade-back { background: none; border: 0; padding: 0; margin-bottom: .5rem; }

.grade-facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .5rem .8rem; margin-top: .5rem; }
.grade-facts > div { display: flex; flex-direction: column; }
.grade-fact-value { font-size: var(--fs-xl); font-weight: 800; line-height: 1.1; color: var(--ink-primary); }
.grade-fact-label { font-size: var(--fs-2xs); color: var(--ink-secondary); }
.grade-spread-title { margin-top: .9rem; font-size: var(--fs-2xs); color: var(--ink-secondary); }
.grade-spread { display: flex; gap: 1px; height: 1.4rem; margin-top: .3rem; border-radius: var(--radius-sm, 5px); overflow: hidden; }
.grade-spread-key { display: flex; flex-wrap: wrap; gap: .2rem .6rem; margin-top: .35rem; font-size: var(--fs-2xs); color: var(--ink-secondary); }
.grade-spread-key .bi { margin-inline-end: .2rem; }

.check-paper-head { padding: 0 0 .7rem; border-bottom: 1px solid var(--border-light); }
.check-paper-body { padding-top: .8rem; }

.publish-preview { display: flex; flex-wrap: wrap; gap: .5rem; }


.join-qr {
    display: block;
    padding: .35rem;
    background: #fff;
    border: 1px solid var(--border-medium);
    border-radius: var(--radius-lg);
}

/* Activities list, nothing yet: ghost rows faded toward the bottom, the first step centred over them. */
.acts-empty {
    position: relative;
    min-height: min(55vh, 420px);
    padding: 1.25rem;
}
.acts-empty.has-tiles { min-height: 460px; }
.acts-kind-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
    gap: .6rem;
    width: 100%;
    max-width: 680px;
}
.acts-kind-tiles.is-off { pointer-events: none; opacity: .6; }
.acts-kind-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .3rem;
    padding: .8rem .5rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 12px;
    background: var(--bs-body-bg);
    color: var(--bs-body-color);
    text-align: center;
    line-height: 1.35;
}
.acts-kind-tile:disabled { background: var(--bs-secondary-bg); color: var(--bs-secondary-color); cursor: not-allowed; opacity: .75; }
.acts-kind-tile:disabled .acts-kind-tile-icon { background: var(--bs-border-color); color: var(--bs-secondary-color); }
.acts-kind-tile:not(:disabled):hover, .acts-kind-tile:not(:disabled):focus-visible { background: var(--bs-success-bg-subtle); border-color: var(--bs-success-border-subtle); }
.acts-kind-tile-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 12px;
    font-size: 1.25rem;
    background: var(--bs-success-bg-subtle);
    color: var(--bs-success-text-emphasis);
}
@media (max-width: 575.98px) { .acts-empty.has-tiles { min-height: 640px; } }
.acts-empty-ghosts { display: flex; flex-direction: column; gap: .75rem; pointer-events: none; }
.acts-ghost {
    position: relative;
    display: flex;
    align-items: center;
    gap: .875rem;
    padding: 1rem 1.25rem;
    border: 1px dashed var(--bs-border-color);
    border-radius: 14px;
    overflow: hidden;
}
.acts-ghost:nth-child(2) { opacity: .7; }
.acts-ghost:nth-child(3) { opacity: .4; }
.acts-ghost-bar { display: block; border-radius: 6px; background: var(--bs-border-color); opacity: .6; }
.acts-ghost-dot { width: 40px; height: 40px; border-radius: 50%; flex: none; }
.acts-ghost-chip { width: 70px; height: 24px; border-radius: 12px; flex: none; }
.acts-ghost-lines { flex: 1; display: flex; flex-direction: column; gap: .5rem; }
.acts-ghost::after {
    content: "";
    position: absolute;
    inset: 0;
    transform: translateX(100%);
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .45), transparent);
    animation: acts-ghost-shimmer 2.2s infinite;
}
@keyframes acts-ghost-shimmer { to { transform: translateX(-100%); } }
@media (prefers-reduced-motion: reduce) { .acts-ghost::after { animation: none; } }
.acts-empty-cta {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 0 1rem;
    border-radius: inherit;
    background: linear-gradient(180deg, transparent 0%, var(--bs-body-bg) 70%);
}
.acts-empty-cta .dropdown-menu { z-index: 5; }
.acts-empty-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 52px;
    height: 52px;
    margin-bottom: .75rem;
    border-radius: 50%;
    font-size: 1.5rem;
    background: var(--bs-success-bg-subtle);
    color: var(--bs-success-text-emphasis);
}
.acts-empty-title { font-size: 1.125rem; font-weight: 600; margin: 0 0 .25rem; }

/* The study-period badge in the course bar: one amber, whichever period is chosen. */
.acts-period {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    height: 31px;
    padding: 0 .75rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 999px;
    background: var(--bs-warning-bg-subtle);
    color: var(--bs-warning-text-emphasis);
    border-color: var(--bs-warning-border-subtle);
    font-size: .875rem;
    white-space: nowrap;
}
.acts-period-text { max-width: 14rem; overflow: hidden; text-overflow: ellipsis; }
.acts-period-caret { font-size: .7rem; }
.acts-period-menu { min-width: 240px; }
.acts-period-meta { opacity: .75; }

/* Activities list: flat cards grouped by month or by course, with the one action each state calls for. */
.acts-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem 1rem;
    margin-bottom: 1rem;
    padding: .6rem 1rem;
    background: var(--bs-success-bg-subtle);
    border: 1px solid var(--bs-success-border-subtle);
    border-radius: var(--radius-lg);
    color: var(--bs-success-text-emphasis);
    font-size: var(--fs-sm);
    font-weight: 600;
}
.acts-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .6rem 1rem;
    margin-bottom: 1.1rem;
}
.acts-search { flex: 0 1 11rem; }
.acts-filters { display: flex; flex-wrap: wrap; gap: .25rem; }
.acts-view-fill { display: flex; padding: .15rem; gap: .1rem; }
.acts-view-fill button { flex: 1 1 0; padding: .15rem .4rem; font-size: var(--fs-2xs); font-weight: 600; white-space: nowrap; }
.acts-view-first { margin-inline-start: auto; }
.acts-filter {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .1rem .5rem;
    background: var(--surface-card);
    border: 1px solid var(--border-light);
    border-radius: var(--radius-pill);
    color: var(--ink-secondary);
    font-size: var(--fs-2xs);
    font-weight: 600;
}
.acts-filter.is-selected { border-color: var(--primary-700); color: var(--primary-900); }
.acts-filter-count { font-size: var(--fs-2xs); opacity: .7; }

.acts-group + .acts-group { margin-top: 1.25rem; }
.acts-group-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
    margin: 0 0 .5rem;
    color: var(--ink-secondary);
    font-size: var(--fs-sm);
    font-weight: 700;
}
.acts-group-count { font-size: var(--fs-2xs); font-weight: 600; opacity: .7; }
.acts-list { display: flex; flex-direction: column; gap: .5rem; margin: 0; padding: 0; list-style: none; }
.acts-card {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: .6rem 1.25rem;
    padding: .7rem 1rem;
    background: var(--surface-card);
    border: 1px solid var(--border-light);
    border-radius: var(--radius-lg);
}
.acts-card:hover { border-color: var(--border-medium); }
.acts-card.is-highlight { border-color: var(--primary-700); box-shadow: 0 0 0 3px var(--bs-success-bg-subtle); }
.acts-card-top { display: flex; align-items: center; gap: .5rem; min-width: 0; }
.acts-card-title {
    margin: 0;
    overflow: hidden;
    font-size: var(--fs-md, 1rem);
    font-weight: 700;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.acts-card-meta {
    display: flex;
    flex-wrap: wrap;
    gap: .15rem .9rem;
    margin-top: .25rem;
    color: var(--ink-secondary);
    font-size: var(--fs-2xs);
}
.acts-card-meta .bi { margin-inline-end: .25rem; opacity: .6; }
.acts-card-code {
    font-family: var(--bs-font-monospace);
    font-size: var(--fs-sm);
    font-weight: 700;
    letter-spacing: .08em;
    color: var(--primary-900);
}
.acts-card-actions { display: flex; align-items: center; gap: .35rem; }
@media (max-width: 768px) {
    .acts-card { grid-template-columns: minmax(0, 1fr); }
    .acts-card-actions { flex-wrap: wrap; }
}

.acts-group-title { min-width: 0; margin: 0; font-size: inherit; font-weight: inherit; color: inherit; overflow-wrap: anywhere; }


.acts-page-head { margin-bottom: 1.25rem; }

/* My activities: the course on its own line, then searching and reading the list. */
.acts-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
    margin-bottom: .75rem;
}
.acts-bar-course { display: flex; flex: 1 1 16rem; align-items: center; gap: .15rem; min-width: 0; }
.acts-bar-course > .form-select { min-width: 0; }
.acts-select { flex: 0 0 auto; width: auto; }
.acts-view {
    display: inline-flex;
    gap: .2rem;
    padding: .2rem;
    background: var(--surface-subtle);
    border: 1px solid var(--border-light);
    border-radius: var(--radius-pill);
}
.acts-view button {
    padding: .2rem .55rem;
    background: transparent;
    border: 0;
    border-radius: var(--radius-pill);
    color: var(--ink-secondary);
}
.acts-view button:disabled { opacity: .5; }
.acts-view button:not(:has(.bi)) { font-size: var(--fs-xs); }
.acts-view button.is-selected { background: var(--surface-card); color: var(--primary-900); box-shadow: var(--shadow-sm); }


/* Wide cards instead of rows: the same card, stacked, several to a row. */
.acts-list.is-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr));
    gap: .6rem;
}
.acts-list.is-cards .acts-card {
    grid-template-columns: minmax(0, 1fr);
    align-content: start;
    height: 100%;
}
.acts-list.is-cards .acts-card-actions { margin-top: .5rem; }

/* The search, the grouping and the view, on one line. */
.acts-tools { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin-bottom: 1rem; }

/* The first card is the activity not made yet: the same shape, drawn as an outline. */
.acts-chip-new { border-color: var(--primary-700); color: var(--primary-900); }

/* The name and the class, changed on the card itself. */
.acts-title-input { max-width: 20rem; }
.acts-title-edit {
    padding: 0 .2rem;
    background: transparent;
    border: 0;
    color: var(--ink-secondary);
    font-size: var(--fs-xs);
    opacity: 0;
}
.acts-card:hover .acts-title-edit,
.acts-title-edit:focus-visible { opacity: 1; }
.acts-card-title.is-untitled { color: var(--ink-secondary); font-weight: 400; }
.acts-meta-edit {
    padding: 0;
    background: transparent;
    border: 0;
    color: inherit;
    font: inherit;
    text-decoration: underline dotted;
    text-underline-offset: .2em;
}
.acts-meta-edit:hover { color: var(--primary-900); }
.acts-course-select { width: auto; max-width: 16rem; }

/* Starting an activity of a known kind: a name, then the kind, above the list. */
.acts-create-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem;
    margin-bottom: .75rem;
    padding: .55rem .7rem;
    background: var(--surface-subtle);
    border-radius: var(--radius-lg);
}
/* Two actions, one clearly first: the activity is the work, the course is the setup behind it. */
.acts-create-row:has(.acts-create-main) { background: transparent; padding: 0; gap: .6rem; align-items: center; margin-bottom: 1rem; }
/* The main menu's tab, as a button: a soft tint of the primary colour, bold, with room around it. */
/* One button; the list of kinds opens under it when it is pointed at, focused or pressed. Light at rest; dark
   green with white text while it is pressed and while the list is open. */
.acts-create-split { display: inline-flex; align-items: stretch; position: relative; }
.btn.acts-create-main {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .4rem;
    min-height: 2.25rem;
    padding: .3rem 1rem;
    background: rgba(var(--bs-primary-rgb, 13, 110, 253), 0.12);
    border: 1px solid var(--primary-700, var(--bs-primary));
    border-radius: 999px;
    color: var(--primary-900);
    font-size: var(--fs-xs);
    font-weight: 700;
    line-height: 1.2;
    box-shadow: none;
    transition: var(--transition-fast);
}
.btn.acts-create-main > .bi { margin: 0 !important; font-size: .95rem; line-height: 1; color: var(--primary-700); }
.btn.acts-create-main:hover:not(:disabled),
.btn.acts-create-main:active:not(:disabled),
.btn.acts-create-main.show,
.btn.acts-create-main:focus-visible,
.acts-create-split:hover .btn.acts-create-main:not(:disabled) { background: var(--primary-700, var(--bs-primary)); border-color: var(--primary-700, var(--bs-primary)); color: #fff; box-shadow: none; }
.btn.acts-create-main:hover:not(:disabled) > .bi,
.btn.acts-create-main:active:not(:disabled) > .bi,
.btn.acts-create-main.show > .bi,
.btn.acts-create-main:focus-visible > .bi,
.acts-create-split:hover .btn.acts-create-main:not(:disabled) > .bi { color: #fff; }
.btn.acts-create-main:disabled { opacity: .5; }
/* The list opens on pointing, with no gap between it and the button to lose the pointer in. */
.acts-create-split:not(.is-off):hover > .acts-create-menu,
.acts-create-split:not(.is-off):focus-within > .acts-create-menu { display: block; top: 100%; inset-inline-start: 0; }
.acts-create-menu { min-width: 15rem; padding: .35rem; margin-top: 0; }
.acts-create-menu .dropdown-item { border-radius: var(--radius-sm, 8px); white-space: normal; padding: .45rem .6rem; }
.acts-create-side { font-weight: 500; }
.acts-create-label { color: var(--ink-secondary); font-size: var(--fs-xs); font-weight: 600; }
.acts-create-row .acts-create-label { flex: 0 0 100%; }
.acts-create-grid {
    flex: 1 1 100%;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: .5rem;
}
/* One kind: its icon and name on a line, and what it is under them. */
.acts-create-card {
    display: grid;
    grid-template-columns: auto 1fr;
    column-gap: .5rem;
    row-gap: .15rem;
    align-items: center;
    padding: .6rem .75rem;
    text-align: start;
    background: var(--surface-card);
    border: 1px solid var(--border-light);
    border-radius: var(--radius-md);
    color: var(--ink-primary);
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.acts-create-card:hover:not(:disabled) { border-color: var(--primary-400); box-shadow: var(--shadow-sm); }
.acts-create-card:disabled { opacity: .6; }
.acts-create-card > .bi { color: var(--primary-700); font-size: var(--fs-lg); }
.acts-create-card-name { font-weight: 700; font-size: var(--fs-sm); }
.acts-create-card.is-wip { background: transparent; border-style: dashed; }
.acts-create-card.is-selected { border-color: var(--primary-600); box-shadow: 0 0 0 2px var(--primary-200); }
.acts-create-card-wip {
    margin-inline-start: .35rem;
    padding: 0 .4rem;
    border-radius: var(--radius-pill);
    background: var(--accent-amber-bg);
    color: var(--accent-amber);
    font-size: var(--fs-2xs);
    font-weight: 600;
}
.acts-create-card-desc { grid-column: 1 / -1; font-size: var(--fs-2xs); color: var(--ink-secondary); line-height: 1.4; }

/* The quiz's name, over the class it belongs to, at the head of the basket. */
.compose-title-input { width: 100%; }

/* The kind of each activity, first on its card in its own colour. A kind with no colour of its own
   still gets the tile, in grey, so a new kind reads as a kind before anyone picks its colour. */
.acts-kind {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .15rem;
    width: 5.5rem;
    min-height: 3.2rem;
    padding: .35rem .3rem;
    background: var(--kind-bg, var(--surface-subtle));
    border-radius: var(--radius-lg);
    color: var(--kind-fg, var(--ink-secondary));
    text-align: center;
}
.acts-kind > .bi { font-size: 1.15rem; line-height: 1; }
.acts-kind-label { font-size: var(--fs-2xs); font-weight: 700; line-height: 1.2; }
.acts-kind[data-kind="structured"]   { --kind-bg: #E6F1FB; --kind-fg: #185FA5; }
.acts-kind[data-kind="chat"]         { --kind-bg: #EEEDFE; --kind-fg: #534AB7; }
.acts-kind[data-kind="lab"]          { --kind-bg: #FAEEDA; --kind-fg: #854F0B; }
.acts-kind[data-kind="url"]          { --kind-bg: #E1F5EE; --kind-fg: #0F6E56; }
.acts-kind[data-kind="pdf-document"] { --kind-bg: #FAECE7; --kind-fg: #993C1D; }
.acts-kind[data-kind="creative"]     { --kind-bg: #FBEAF0; --kind-fg: #993556; }

/* On wide cards, and on a narrow screen, the kind sits as a small label over the name instead. */
.acts-list.is-cards .acts-kind {
    flex-direction: row;
    justify-self: start;
    width: auto;
    min-height: 0;
    padding: .2rem .55rem;
}
@media (max-width: 768px) {
    .acts-kind {
        flex-direction: row;
        justify-self: start;
        width: auto;
        min-height: 0;
        padding: .2rem .55rem;
    }
}

/* Low fields: small type and tight padding, so the questions below start higher - each with a small
   label tight over it. */
.compose-id-field { display: flex; flex-direction: column; gap: .1rem; }
.compose-id-label { margin: 0; color: var(--ink-secondary); font-size: var(--fs-2xs); font-weight: 600; line-height: 1.2; }
.compose-id-control { padding-block: .15rem; font-size: var(--fs-xs); line-height: 1.5; }
.compose-field-row .btn-sm { padding: .15rem .45rem; }

/* How many questions, and how long they take, on one line above them. */
.compose-basket-summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    margin-bottom: .6rem;
    font-size: var(--fs-xs);
    font-weight: 700;
}

/* The same name and class, to read, at the head of the basket on the later steps. */
/* The name has its own line and may take two, rather than being cut after a word or two. */
.details-basket-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    line-height: 1.35;
    font-weight: 700;
    font-size: var(--fs-sm);
}
.details-basket-course {
    margin-top: .15rem;
    overflow: hidden;
    color: var(--ink-secondary);
    font-size: var(--fs-xs);
    /* A class name that is long breaks onto a second line, as the name above does, rather than being cut
       mid-word; the whole of it is in the tooltip. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    line-height: 1.35;
}

/* A step line that carries the subtitle under the title: the line aligns to its top, and the way back
   and the steps sit level with the title rather than centred on both lines. */
.details-flow.has-sub { align-items: flex-start; }
.details-flow.has-sub > .details-flow-steps { margin-top: .9rem; }
.details-flow.has-sub > .btn { margin-top: .45rem; }
/* The title block takes the room that is left and wraps its subtitle inside it, so the way back and the
   steps are never pushed onto a line of their own. */
.details-flow-head { flex: 1 1 0; min-width: 0; }
.details-flow-head .ex-page-title { margin: 0; }
.details-flow-head .ex-page-sub { margin: .15rem 0 0; }
/* On a narrow screen the title takes the whole line, and the way back and the steps go under it. */
@media (max-width: 992px) {
    .details-flow.has-sub { flex-wrap: wrap; }
    .details-flow-head { flex-basis: 100%; }
    .details-flow.has-sub > .btn,
    .details-flow.has-sub > .details-flow-steps { margin-top: 0; }
}

/* The heading of a source's panel on the compose page, looking like the section headings of the generator's form,
   in the colour those headings have (the theme's secondary) so every source reads alike. The generator's own
   headings are styled for the generator alone, so its import heading takes the colour from here. */
/* An arrow drawn for right-to-left reading (back points right, forward points left) turns over in a
   left-to-right language, so it keeps meaning back or forward there too. */
html[dir="ltr"] .rtl-arrow { transform: scaleX(-1); display: inline-block; }

.compose-panel-head { display: flex; align-items: center; gap: 6px; margin-bottom: 10px; color: var(--bs-secondary); font-size: var(--fs-2xs); font-weight: 600; letter-spacing: .07em; text-transform: uppercase; }
.compose-panel-head::after { content: ''; flex: 1; height: 1px; background: #e9ecef; }
.aigen-sec-source { color: var(--bs-secondary) !important; }

/* Question assembly: the name and class in one low line under the title. */
/* Room after the name and class, little before the panels: the source tabs belong with the panel they open,
   not with the row above. */
.compose-identity-bar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: .5rem 1rem; margin-bottom: 1.6rem; }
.compose-identity-bar ~ .compose-sources { margin-bottom: 1.1rem; padding-inline-start: .5rem; }
.compose-id-name { width: 18rem; max-width: 100%; }
.compose-id-course { width: 26rem; max-width: 100%; }

/* An activity's own page: its kind, class and state under its name, the one thing to do now, how
   students come in, and how many have. */
/* The head of an activity's pages: a top row, then the name. The line of its life sits at the end of the top row. */
.life-head { margin-bottom: 1.25rem; }
.life-head-top { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .4rem; margin-bottom: .35rem; }
.life-head-top .hub-back { margin-bottom: 0; margin-inline-end: .5rem; }
.life-head-top .ex-chip, .life-head-title .ex-chip { padding-block: .1rem; }
/* A page that brings its own title block puts it on the row of the steps: the title at the start, the steps at the
   end, level with its first line. In a narrow window the steps go under it. */
.life-head-top { align-items: flex-start; }
.life-head-top > .life-head-page { flex: 1 1 22rem; min-width: 0; margin-bottom: 0; }
.life-head-top > .details-flow-steps { margin-top: .55rem; }
.life-head-title { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .5rem; }
.life-head-title .ex-page-title { margin: 0; }
.life-head .hub-meta { margin-top: .15rem; }
/* The page's own title under the head: a step in the activity, a little smaller than the activity's name. */
.life-head-identity { display: flex; flex-wrap: wrap; align-items: center; gap: .25rem .5rem; margin: 0; font-size: var(--fs-sm); color: var(--ink-secondary); }
/* Same weight as the rule that clears the back link in the top row, so its room is its own: 4px under the title, 8px before the chips. */
.life-head-top .life-head-back-below, .life-head-back-below { display: inline-flex; margin-top: .25rem; margin-bottom: .5rem; }
.life-head-identity-name { color: var(--ink-primary); font-weight: 700; }
/* The class above the name: the same size, regular weight and the quieter ink, so the name is what is read first. */
.life-head-identity-course { gap: .35rem; margin-bottom: .2rem; }
.life-head-identity-course > .bi, .life-head-identity-icon { color: var(--ink-muted); }
.life-head-identity-icon { margin-inline-end: -.15rem; }
.life-head-page { margin-bottom: .7rem; }
.life-head-page .ex-page-title { margin: 0; font-size: var(--fs-2xl); }
/* The line under the title runs the width of the page: the general limit on its length would wrap it. */
.life-head-page .ex-page-sub { margin-top: .1rem; max-width: none; font-size: var(--fs-sm); }
.hub-back { display: inline-flex; align-items: center; gap: .3rem; margin-bottom: .2rem; color: var(--ink-secondary); font-size: var(--fs-xs); font-weight: 600; text-decoration: none; }
.hub-back:hover { color: var(--primary-900); text-decoration: underline; }
.hub-meta { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem .75rem; }
.hub-now { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .5rem 1rem; }
.hub-now-text { font-size: var(--fs-sm); font-weight: 600; }
.hub-code { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1rem 1.5rem; }
.hub-label { color: var(--ink-secondary); font-size: var(--fs-xs); font-weight: 600; }
.hub-code-value { color: var(--primary-900); font-family: var(--bs-font-monospace); font-size: 1.6rem; font-weight: 800; letter-spacing: .16em; }
.hub-stats {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: .35rem;
    margin-top: .75rem;
    padding-top: .75rem;
    border-top: 1px solid var(--border-light);
    text-align: center;
}
.details-basket > .hub-stats:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
/* Running it: the step now, the clock while it runs, and the students as a list that colours itself. */
.live-control { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem 1.25rem; }
.live-control-text { flex: 1 1 14rem; min-width: 0; }
.live-control-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; margin-inline-start: auto; }
.live-control-separator { align-self: stretch; inline-size: 1px; margin-inline: .1rem; background: var(--border-light); }
.live-control-actions .live-bar-clock { flex: 0 0 auto; min-width: 7.75rem; margin-inline: .25rem; }
.live-extra-topbar { flex: 0 0 auto; }
@media (min-width: 768px) { .live-control-actions { flex-wrap: nowrap; } }
.live-confirm-text { font-size: var(--fs-sm); font-weight: 600; color: var(--ink-primary); }
.live-clock { display: grid; justify-items: center; gap: .1rem; min-width: 9rem; }
.live-clock-value { color: var(--primary-900); font-family: var(--bs-font-monospace); font-size: 1.9rem; font-weight: 800; line-height: 1; }
.live-clock-sub { color: var(--ink-secondary); font-size: var(--fs-2xs); }
.live-clock-bar { display: block; inline-size: 100%; block-size: 3px; border-radius: var(--radius-pill); background: var(--surface-subtle); overflow: hidden; }
.live-clock-bar > span { display: block; block-size: 100%; background: var(--primary-700); }
.live-clock.is-low .live-clock-value { color: var(--bs-danger-text-emphasis); }
.live-clock.is-low .live-clock-bar > span { background: var(--bs-danger); }
.live-pulse { display: inline-flex; align-items: center; gap: .3rem; margin-inline-start: .35rem; color: var(--bs-success-text-emphasis); font-size: var(--fs-2xs); font-weight: 600; }
.live-pulse > span { inline-size: .45rem; block-size: .45rem; border-radius: 50%; background: var(--bs-success); animation: live-pulse 1.6s ease-in-out infinite; }
@keyframes live-pulse { 50% { opacity: .35; } }
.live-filters { margin-bottom: .6rem; }
.live-list { list-style: none; margin: 0; padding: 0; max-height: 34rem; overflow-y: auto; }
.live-row {
    display: grid;
    /* The name a set width, so every state lines up in one column right beside it; breaches next,
       the actions at the far end. */
    grid-template-columns: auto minmax(12rem, 22rem) 7.5rem auto minmax(0, 1fr);
    align-items: center;
    gap: .35rem .75rem;
    padding: .5rem .25rem;
    border-bottom: 1px solid var(--border-light);
}
.live-row:last-child { border-bottom: 0; }
.live-dot { inline-size: .55rem; block-size: .55rem; border-radius: 50%; border: 1.5px solid var(--ink-faint); }
.live-who { display: flex; align-items: baseline; gap: .5rem; min-width: 0; }
.live-name { min-width: 0; font-size: var(--fs-sm); font-weight: 600; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.live-id { flex-shrink: 0; color: var(--ink-muted); font-size: var(--fs-2xs); white-space: nowrap; }
.live-status {
    justify-self: start;
    padding: .1rem .55rem;
    border: 1px solid var(--border-light);
    border-radius: var(--radius-pill);
    background: var(--surface-subtle);
    color: var(--ink-secondary);
    font-size: var(--fs-2xs);
    font-weight: 700;
    white-space: nowrap;
}
.live-row-actions { grid-column: 5; display: inline-flex; gap: .3rem; justify-self: end; min-inline-size: 2rem; }
.live-flags { grid-column: 4; display: inline-flex; flex-wrap: wrap; gap: .3rem; }
.live-extra-label { align-self: center; color: var(--ink-secondary); font-size: var(--fs-xs); font-weight: 600; }
/* A message to one student opens under their row. */
.live-message { grid-column: 1 / -1; display: flex; gap: .4rem; padding-inline-start: 1.3rem; }
.live-message .form-control { max-inline-size: 32rem; }
.live-message-sent { grid-column: 1 / -1; padding-inline-start: 1.3rem; color: var(--bs-success-text-emphasis); font-size: var(--fs-xs); font-weight: 600; }
/* A message to everyone, under the step. */
.live-announce { margin-top: .9rem; padding-top: .8rem; border-top: 1px dashed var(--border-light); }
.live-announce-label { display: block; margin-bottom: .35rem; color: var(--ink-secondary); font-size: var(--fs-xs); font-weight: 700; }
.live-announce-row { display: flex; gap: .4rem; }
.live-announce-row .form-control { max-inline-size: 36rem; }
.live-announce-current {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: .25rem .5rem;
    margin-top: .5rem; padding: .45rem .65rem; border-radius: var(--radius-md, .5rem);
    background: var(--bs-warning-bg-subtle); color: var(--bs-warning-text-emphasis); font-size: var(--fs-xs);
}
.live-announce-current-label { font-weight: 700; }
.live-announce-current-text { flex: 1 1 12rem; min-width: 0; }
.live-row[data-status="waiting"] .live-dot { border-color: var(--bs-info); background: var(--bs-info); }
.live-row[data-status="working"] .live-dot { border-color: var(--bs-success); background: var(--bs-success); }
.live-row[data-status="working"] .live-status { background: var(--bs-success-bg-subtle); border-color: var(--bs-success-border-subtle); color: var(--bs-success-text-emphasis); }
.live-row[data-status="waiting"] .live-status { background: var(--bs-info-bg-subtle); border-color: var(--bs-info-border-subtle); color: var(--bs-info-text-emphasis); }
.live-row[data-status="submitted"] .live-status { background: var(--primary-100); border-color: var(--primary-200, var(--border-light)); color: var(--primary-800, var(--primary-900)); }
.live-row[data-status="dropped"] .live-dot,
.live-row[data-status="locked"] .live-dot { border-color: var(--bs-danger); background: var(--bs-danger); }
.live-row[data-status="dropped"] .live-status,
.live-row[data-status="locked"] .live-status,
.live-row[data-status="unsubmitted"] .live-status { background: var(--bs-danger-bg-subtle); border-color: var(--bs-danger-border-subtle); color: var(--bs-danger-text-emphasis); }
.live-row[data-status="locked"] { background: var(--bs-danger-bg-subtle); }
.live-row[data-status="submitted"] .live-dot { border-color: var(--primary-700); background: var(--primary-700); }
.live-row[data-status="out"] .live-name { color: var(--ink-secondary); font-weight: 500; }
.live-empty { display: flex; align-items: center; gap: .5rem; padding: 1rem .25rem; color: var(--ink-secondary); font-size: var(--fs-sm); }
@media (max-width: 576px) {
    .live-row { grid-template-columns: auto minmax(0, 1fr) auto; }
    .live-status { justify-self: end; }
    .live-flags, .live-row-actions { grid-column: 2 / -1; justify-self: start; }
    .live-message, .live-message-sent { padding-inline-start: 0; }
}
/* A section looked at less, folded under its title. */
.hub-fold > summary { cursor: pointer; margin-bottom: 0; list-style: none; }
.hub-fold > summary::-webkit-details-marker { display: none; }
.hub-fold > summary::after { content: "\F282"; font-family: "bootstrap-icons"; margin-inline-start: auto; color: var(--ink-secondary); transition: transform .15s; }
.hub-fold[open] > summary { margin-bottom: .65rem; }
.hub-fold[open] > summary::after { transform: rotate(180deg); }
/* Under the step while students come in: the class screen, and the lock. */
.hub-entry-hint { display: flex; align-items: center; gap: .35rem; margin-top: .5rem; color: var(--ink-muted); font-size: var(--fs-xs); }
.hub-entry-hint strong { color: var(--ink-secondary); }
.hub-entry-facts { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1.25rem; margin-top: .75rem; color: var(--ink-secondary); font-size: var(--fs-xs); }
/* The class screen: the entry, large, over everything. */
.hub-project {
    position: fixed;
    inset: 0;
    z-index: 2000;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2rem;
    padding: 2rem 1rem;
    background: var(--surface-card, #fff);
    overflow: auto;
}
.hub-project-close { position: absolute; inset-block-start: 1rem; inset-inline-end: 1rem; }
.hub-project-title { font-size: clamp(1.5rem, 3vw, 2.4rem); font-weight: 800; color: var(--primary-900); text-align: center; }
.hub-project-body { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 2rem 4rem; }
.hub-project-entry { display: grid; justify-items: center; gap: .25rem; text-align: center; }
.hub-project-entry .hub-label { font-size: var(--fs-lg, 1.15rem); }
.hub-project-url { font-size: clamp(1.2rem, 2.4vw, 1.8rem); font-weight: 700; color: var(--ink-primary); }
.hub-project-code { font-family: var(--bs-font-monospace); font-size: clamp(3rem, 9vw, 6.5rem); font-weight: 800; letter-spacing: .16em; color: var(--primary-900); line-height: 1.1; }
/* ── The paper on the class screen, one question at a time ─────────────────────────── */
.review-screen {
    position: fixed;
    inset: 0;
    z-index: 2000;
    display: flex;
    flex-direction: column;
    background: var(--surface-page, #FAF8F3);
    outline: none;
}
.review-bar {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: .8rem 1.2rem;
    border-bottom: 1px solid var(--border-light);
}
.review-count { font-size: var(--fs-lg); font-weight: 800; color: var(--primary-700, #257A59); }
.review-title { flex: 1 1 auto; min-width: 0; font-weight: 700; color: var(--ink-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.review-body {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 1.4rem;
    padding: 1.5rem clamp(1rem, 6vw, 5rem);
    overflow-y: auto;
}
.review-prompt { font-size: clamp(1.3rem, 2.6vw, 2.1rem); font-weight: 700; line-height: 1.4; color: var(--ink-primary); }
.review-options { list-style: none; margin: 0; padding: 0; display: grid; gap: .6rem; }
.review-option {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: .7rem 1rem;
    font-size: clamp(1rem, 1.8vw, 1.4rem);
    background: var(--surface-card, #fff);
    border: 2px solid var(--border-light);
    border-radius: var(--radius-lg);
}
.review-option.is-right { border-color: var(--bs-success); background: var(--bs-success-bg-subtle); }
.review-option.is-taken { border-color: var(--bs-danger-border-subtle); }
.review-option-text { flex: 1 1 auto; }
.review-option-share { position: relative; flex: 0 0 8rem; height: 1.5rem; background: var(--surface-subtle); border-radius: 999px; overflow: hidden; }
.review-bar-fill { position: absolute; inset-block: 0; inset-inline-start: 0; background: var(--primary-200, #C7E5D5); }
.review-option-count { position: absolute; inset: 0; display: grid; place-items: center; font-size: var(--fs-xs); font-weight: 700; color: var(--ink-primary); }
.review-verdict { display: flex; align-items: baseline; flex-wrap: wrap; gap: .6rem; }
.review-score { font-size: clamp(2rem, 5vw, 3.5rem); font-weight: 800; color: var(--primary-700, #257A59); line-height: 1; }
.review-score-label { color: var(--ink-secondary); }
.review-answer {
    padding: .9rem 1.1rem;
    background: var(--surface-card, #fff);
    border: 2px solid var(--primary-200, #C7E5D5);
    border-radius: var(--radius-lg);
}
.review-answer.is-from-class { border-style: dashed; }
.review-answer-label { font-size: var(--fs-xs); font-weight: 700; color: var(--primary-700, #257A59); margin-bottom: .3rem; }
.review-answer-body { font-size: clamp(1rem, 1.6vw, 1.3rem); line-height: 1.5; color: var(--ink-primary); }
.review-answer-none { font-size: var(--fs-sm); color: var(--ink-secondary); }
.review-foot {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    padding: 1rem;
    border-top: 1px solid var(--border-light);
}

.hub-stat-num { display: block; font-size: var(--fs-lg, 1.15rem); font-weight: 800; }
.hub-stat-label { color: var(--ink-secondary); font-size: var(--fs-2xs); }

/* A card's name opens its activity. */
.acts-card-link { color: inherit; text-decoration: none; }
.acts-card-link:hover { color: var(--primary-900); text-decoration: underline; }

/* A link off Examin, not followed in a locked activity: a word about it, briefly, at the foot of the page. */
.link-guard-toast {
    position: fixed;
    inset-inline: 0;
    bottom: 1.25rem;
    width: fit-content;
    max-width: calc(100% - 2rem);
    margin-inline: auto;
    padding: .55rem 1rem;
    border-radius: 999px;
    background: #1f2937;
    color: #fff;
    font-size: var(--fs-sm);
    box-shadow: 0 6px 20px rgba(15, 23, 42, .25);
    z-index: 2000;
    opacity: 0;
    transform: translateY(.5rem);
    pointer-events: none;
    transition: opacity .2s, transform .2s;
}
.link-guard-toast.is-shown { opacity: 1; transform: none; }

/* PDF sheet: the window a paper is made in from text or a link (PdfSheetCompose). Global, since
   its content is drawn inside TopLayerDialog's <dialog>, out of the page component's scope. */
.sheet-source-dialog { width: min(960px, 94vw); }
.phet-picker-dialog {
    width: min(1100px, 96vw);
    max-height: 88vh;
    padding: 0;
    overflow: hidden;
    border-radius: var(--radius-lg, 12px);
}
.phet-sim-card,
.hover-lift {
    transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.phet-sim-card:hover,
.hover-lift:hover {
    transform: translateY(-3px);
    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.09) !important;
    border-color: var(--bs-primary, #0d6efd) !important;
}
.sheet-source-head,
.sheet-source-foot {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .7rem 1rem;
    border-bottom: 1px solid var(--border-light);
}
.sheet-source-head { justify-content: space-between; }
.sheet-source-foot {
    justify-content: flex-end;
    border-bottom: 0;
    border-top: 1px solid var(--border-light);
}
.sheet-source-body {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    padding: 1rem;
    overflow: auto;
}
.sheet-source-editor { min-height: 0; overflow: auto; }

/* The Python code tool: Run and Stop over the editor, the output under it. */
.py-runner { position: relative; display: flex; flex-direction: column; height: 100%; min-height: 0; background: var(--surface-card, #fff); }
.py-toolbar { display: flex; align-items: center; gap: .4rem; padding: .4rem .5rem; border-bottom: 1px solid var(--border-light); background: var(--surface-subtle); }
.py-toolbar .btn { font-size: var(--fs-2xs); }
.py-lang { margin-inline-start: auto; font-size: var(--fs-2xs); color: var(--ink-secondary); font-weight: 600; }
.py-editor { flex: 1 1 60%; min-height: 60px; }
.py-output { flex: 1 1 40%; min-height: 60px; overflow: auto; padding: .5rem .7rem; background: #1f2430; color: #e6e6e6; font-family: ui-monospace, Consolas, monospace; font-size: 13px; white-space: pre-wrap; }
.py-output .py-err { color: #ff8a80; }
/* The bar between the code and its output, dragged up or down. */
.py-splitter { flex: 0 0 7px; cursor: row-resize; background: var(--surface-subtle); border-top: 1px solid var(--border-light); position: relative; touch-action: none; }
.py-splitter::after { content: ""; position: absolute; inset-inline-start: 50%; top: 2px; width: 36px; height: 3px; margin-inline-start: -18px; border-radius: 2px; background: var(--border-medium, #c7d0ca); }
.py-splitter:hover::after, .py-splitter:focus-visible::after { background: var(--primary-500, #3fab80); }
.py-splitter:focus-visible { outline: none; }
/* The sandboxed frame JavaScript runs in: it shows nothing of its own, but is rendered, not
   display:none - a hidden frame gets no animation frames, and TensorFlow.js waits on them to train. */
.code-sandbox { position: absolute; width: 1px; height: 1px; border: 0; opacity: 0; pointer-events: none; }
/* A chart in the code output: a white card on the dark output, full width, drawn by code-chart.js. */
.py-chart { margin: .4rem 0; padding: .3rem; border-radius: 6px; background: #fff; }
.py-chart svg { display: block; width: 100%; height: auto; max-height: 260px; font-family: system-ui, sans-serif; }
.py-chart .chart-title { font-size: 12px; font-weight: 600; fill: #18342a; }
.py-chart .chart-tick { font-size: 9px; fill: #5b6b63; }
.py-chart .chart-legend { font-size: 10px; font-weight: 600; }
.py-chart .chart-grid { stroke: #e4e9e5; stroke-width: 1; }
.py-chart .chart-axis { stroke: #9aa7a0; stroke-width: 1; }
/* The data table tool: its buttons over a grid that fills the rest. */
.dt-tool { display: flex; flex-direction: column; height: 100%; min-height: 0; background: var(--surface-card, #fff); }
.dt-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; padding: .4rem .5rem; border-bottom: 1px solid var(--border-light); background: var(--surface-subtle); }
.dt-toolbar .btn { font-size: var(--fs-2xs); }
.dt-grid { flex: 1 1 auto; min-height: 0; font-size: 13px; }
/* The table's own chart, under it when opened: the grid keeps the larger share. */
.dt-chart { display: none; }
.dt-chart.is-open { display: block; flex: 0 0 45%; min-height: 160px; border-top: 1px solid var(--border-light); }
/* A compact grid: tighter cells than the Bootstrap theme, titles that read as titles though they can be
   renamed in place, and the summary as short lines under each column of numbers. */
/* Tabulator's theme loads after this sheet, so these reach past its own selectors. */
.dt-tool .dt-grid.tabulator .tabulator-row .tabulator-cell { padding: 3px 6px; }
.dt-tool .dt-grid.tabulator .tabulator-header .tabulator-col .tabulator-col-content { padding: 4px 6px; }
.dt-tool .dt-grid.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title .tabulator-title-editor { border: 0; background: transparent; padding: 0; font-weight: 600; color: var(--ink-primary); }
.dt-tool .dt-grid.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title .tabulator-title-editor:focus { background: #fff; outline: 1px solid var(--primary-500, #3fab80); }
.dt-tool .dt-grid.tabulator .tabulator-row.tabulator-calcs.tabulator-calcs-bottom .tabulator-cell { padding: 3px 6px; font-size: 10.5px; line-height: 1.35; color: var(--ink-secondary); white-space: pre-line; }
/* The chart tool: the kind and buttons over the chart, the data folded between them. */
.ch-tool { display: flex; flex-direction: column; height: 100%; min-height: 0; background: var(--surface-card, #fff); }
.ch-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; padding: .4rem .5rem; border-bottom: 1px solid var(--border-light); background: var(--surface-subtle); }
.ch-toolbar .btn { font-size: var(--fs-2xs); }
.ch-kind { width: auto; font-size: var(--fs-2xs); }
.ch-data { display: flex; flex-direction: column; gap: .3rem; padding: .4rem .5rem; border-bottom: 1px solid var(--border-light); }
.ch-plot { flex: 1 1 auto; min-height: 200px; }
/* A scripted table's frame fills what its bar leaves. */
.st-frame { flex: 1 1 auto; min-height: 0; width: 100%; border: 0; background: #fff; }
/* The chemical structure editor's frame fills what its bar leaves. */
.chem-frame { flex: 1 1 auto; min-height: 0; width: 100%; border: 0; background: #fff; }
.hwmath-frame { flex: 1 1 auto; min-height: 0; width: 100%; border: 0; background: #fff; }
.hwmath-result { flex: 0 0 auto; display: flex; flex-direction: column; gap: .25rem; padding: .5rem .75rem; border-bottom: 1px solid var(--border-light); background: var(--surface-card, #fff); }
.hwmath-formula { font-size: 2.2rem; min-height: 4.5rem; border: 0; background: transparent; --hover-menu-visibility: hidden; }
.hwmath-formula::part(virtual-keyboard-toggle), .hwmath-formula::part(menu-toggle) { display: none; }
.hwmath-latex-text { font-size: var(--fs-2xs); color: var(--ink-secondary); max-height: 3.5rem; overflow: auto; word-break: break-all; }

/* A third-party tool's required attribution, under its frame in the student's tool pane. */
.tool-credit {
    flex: 0 0 auto;
    padding: .2rem .6rem;
    border-top: 1px solid var(--border-light);
    background: var(--surface-subtle);
    color: var(--ink-muted);
    font-size: var(--fs-2xs);
    text-align: start;
}

/* ── Live dashboard: settings chips, and what needs the teacher ── */
.live-chips { display: flex; flex-wrap: wrap; gap: .4rem; margin: .6rem 0; }
.live-attn {
    margin-bottom: .75rem;
    padding: .75rem .9rem;
    border: 1px solid var(--accent-coral-border);
    border-radius: var(--radius-lg, 14px);
    background: var(--accent-coral-bg);
}
.live-attn-head { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin-bottom: .5rem; }
.live-attn-title { color: var(--accent-coral); font-weight: 700; font-size: var(--fs-sm); }
.live-attn-all { display: inline-flex; flex-wrap: wrap; gap: .4rem; margin-inline-start: auto; }
.live-attn-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem .75rem;
    padding: .55rem .7rem;
    border: 1px solid var(--accent-coral-border);
    border-radius: var(--radius-md, 10px);
    background: var(--surface-card);
}
.live-attn-row + .live-attn-row { margin-top: .45rem; }
.live-attn-row.is-awaiting { border-color: var(--accent-amber-border); }
.live-attn-row.is-dropped { border-color: var(--border-light); }
.live-attn-icon { font-size: 1.1rem; }
.live-attn-row.is-locked .live-attn-icon { color: var(--accent-coral); }
.live-attn-row.is-awaiting .live-attn-icon { color: var(--accent-amber); }
.live-attn-row.is-dropped .live-attn-icon { color: var(--ink-muted); }
.live-attn-who { flex: 1 1 14rem; min-width: 0; }
.live-attn-who .live-name { white-space: normal; }
.live-attn-who .live-id { margin-inline-start: .35rem; }
.live-attn-sub { color: var(--ink-muted); font-size: var(--fs-xs); }
.live-attn-timer { color: var(--accent-coral); font-weight: 800; font-variant-numeric: tabular-nums; }
.live-attn-actions { display: inline-flex; flex-wrap: wrap; gap: .35rem; margin-inline-start: auto; }

/* A row of the list is five columns on a wide screen; on a narrow one it stacks. */
@media (max-width: 767.98px) {
    .live-row { grid-template-columns: auto minmax(0, 1fr); }
    .live-status, .live-flags, .live-row-actions { grid-column: 1 / -1; justify-self: start; }
    .live-attn-actions { inline-size: 100%; margin-inline-start: 0; }
    .live-attn-actions .btn { flex: 1 1 auto; }
}

/* Rules while it runs: two fields side by side, the optional parts and the button in a row below. */
.live-rules { margin: .75rem 0; padding: .8rem .95rem; border: 1px solid var(--border-light); border-radius: var(--radius-lg, 14px); background: var(--surface-card); }
.live-rules-title { margin-bottom: .55rem; color: var(--ink-primary); font-size: var(--fs-sm); font-weight: 700; }
.live-rules-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: .75rem 1rem; }
.live-rules-field label { display: block; margin-bottom: .25rem; color: var(--ink-secondary); font-size: var(--fs-xs); font-weight: 600; }
.live-rules-field .form-text { margin-top: .25rem; }
.live-rules-foot { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .75rem; margin-top: .75rem; padding-top: .65rem; border-top: 1px solid var(--border-light); }
.live-rules-foot > .form-control { flex: 1 1 14rem; min-inline-size: 0; }

/* Time changes affect everyone currently working, so they live with the other live-side controls rather
   than in the central students header. */
.live-clock-controls { padding: .8rem .95rem; border: 1px solid var(--border-light); border-radius: var(--radius-lg, 14px); background: var(--surface-card); box-shadow: var(--shadow-sm); }
.live-clock-controls-title { margin-bottom: .55rem; color: var(--ink-primary); font-size: var(--fs-sm); font-weight: 700; }
.live-clock-controls-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; }
.live-clock-controls-expand { display: flex; flex-direction: column; gap: .5rem; margin-top: .75rem; padding-top: .65rem; border-top: 1px solid var(--border-light); }
.live-clock-controls .live-message-sent { padding: 0; }

/* ── Live dashboard: the bar that stays in view, and the students as cards ── */
.live-bar {
    position: sticky;
    top: calc(var(--examin-topbar-h, 68px) + .35rem);
    z-index: 20;
    margin-inline: -.35rem;
    padding: .35rem .35rem .1rem;
    border-radius: var(--radius-lg, 14px);
    background: var(--surface-glass, rgba(255, 254, 250, .94));
    backdrop-filter: blur(6px);
}
.live-clear {
    display: flex; align-items: center; gap: .4rem;
    margin-bottom: .6rem; padding: .55rem .8rem;
    border: 1px solid var(--accent-green-border); border-radius: var(--radius-md, 10px);
    background: var(--accent-green-bg); color: var(--accent-green); font-weight: 600; font-size: var(--fs-sm);
}
.live-search-row { margin-bottom: .5rem; }
.live-search { max-inline-size: 22rem; }

.live-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
    gap: .6rem;
    max-height: none;
    overflow: visible;
}
.live-row {
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: start;
    padding: .7rem .85rem;
    border: 1px solid var(--border-light);
    border-radius: var(--radius-lg, 12px);
    background: var(--surface-card);
}
.live-row:last-child { border-bottom: 1px solid var(--border-light); }
.live-row > .live-dot { margin-top: .45rem; }
.live-row > .live-who { grid-column: 2; }
.live-row > .live-status { grid-column: 3; justify-self: end; }
.live-row > .live-flags { grid-column: 1 / -1; }
.live-row > .live-row-actions { grid-column: 1 / -1; justify-self: start; flex-wrap: wrap; align-items: center; }
.live-row[data-status="locked"] { border-color: var(--accent-coral-border); box-shadow: inset 4px 0 0 var(--accent-coral); }
.live-row[data-status="dropped"] { opacity: .8; }
.live-exit-detail { grid-column: 1 / -1; padding: .45rem .6rem; border-radius: var(--radius-md, 8px); background: var(--surface-subtle); color: var(--ink-secondary); font-size: var(--fs-xs); }

.live-kebab { position: relative; display: inline-flex; }
.live-pop {
    position: absolute; inset-inline-start: 0; z-index: 30; margin-top: .3rem; min-inline-size: 13rem; padding: .35rem;
    inset-block-start: 100%;
    border: 1px solid var(--border-light); border-radius: var(--radius-md, 10px);
    background: var(--surface-card); box-shadow: 0 8px 22px rgba(24, 52, 42, .16);
}
.live-pop button, .live-pop a {
    display: flex; align-items: center; gap: .5rem; inline-size: 100%;
    padding: .4rem .5rem; border: 0; border-radius: 6px; background: none;
    color: var(--ink-primary); font-size: var(--fs-xs); text-align: start;
}
.live-pop button:hover, .live-pop a:hover { background: var(--surface-subtle); }
.live-pop button.is-danger { color: var(--accent-coral); }

/* On a phone the bar would take a third of the screen: it scrolls with the page there. */
@media (max-width: 767.98px) {
    .live-bar { position: static; margin-inline: 0; padding: 0; background: none; backdrop-filter: none; }
    .live-list { grid-template-columns: 1fr; }
}

/* ── The live screen: the bar on top, then the students beside what steers the activity ──
   Narrow, they stack with the students first; wide, they sit side by side. Before it starts, how
   students come in (the entry code) comes first. */
.live-page { display: flex; flex-direction: column; gap: .75rem; }
.live-cols { display: flex; flex-direction: column; gap: .75rem; }
.live-side { display: flex; flex-direction: column; gap: .75rem; min-width: 0; }
.live-side > * { margin: 0; }
.live-cols.is-prestart > .live-left { order: -1; }
.live-students { min-width: 0; }
@media (min-width: 1200px) {
    .live-page.is-running .live-cols { flex-direction: row; align-items: flex-start; }
    .live-page.is-running .live-cols > .live-main { flex: 1 1 0; }
    .live-page.is-running .live-cols > .live-left { flex: 0 0 25rem; max-inline-size: 38%; }
}

/* ── The live screen as separate panels ──
   Where the live screen sits, the one big card around the whole pane goes away and each part is a card of
   its own: the bar, the students, the rules, the message to everyone, how they come in, the shortcuts and
   the settings in force. */
.publish-pane:has(.live-page) {
    display: flex;
    flex-direction: column;
    gap: .9rem;
    padding: 0;
    border: 0;
    background: transparent;
    box-shadow: none;
}
.publish-pane:has(.live-page) > .publish-section,
.publish-pane:has(.live-page) > details.hub-fold,
.live-students,
.live-announce,
.live-childslot > .publish-section {
    margin: 0;
    padding: var(--space-4);
    border: 1px solid var(--border-light);
    border-radius: var(--radius-lg);
    background: var(--surface-card);
    box-shadow: var(--shadow-sm);
}
.publish-pane:has(.live-page) > .publish-section + .publish-section,
.live-childslot .publish-section + .publish-section { margin-top: 0; }
.live-bar {
    border: 1px solid var(--border-light);
    box-shadow: var(--shadow-sm);
    background: var(--surface-card);
    padding: .6rem .75rem;
}
.live-rules { box-shadow: var(--shadow-sm); }
.live-announce { margin: 0; }
.live-page { gap: .9rem; }
.live-cols, .live-side { gap: .9rem; }

/* ── Three columns: the students with their actions above (right), what is acted on (middle), what is known (left) ──
   The left one is the aside the hub already has; its panels are cards like the rest. */
.live-main { display: flex; flex-direction: column; gap: .9rem; min-width: 0; }
.details-basket-col > .publish-section,
.details-basket-col > details.hub-fold {
    margin: 0;
    padding: var(--space-4);
    border: 1px solid var(--border-light);
    border-radius: var(--radius-lg);
    background: var(--surface-card);
    box-shadow: var(--shadow-sm);
}
.details-basket-col { display: flex; flex-direction: column; gap: .9rem; }
.live-search-row { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.live-search-row .live-search { flex: 1 1 12rem; }

/* The same students as rows: one line each, like a table. */
.live-list.is-rows { grid-template-columns: 1fr; gap: 0; border: 1px solid var(--border-light); border-radius: var(--radius-md, 10px); overflow: hidden; }
.live-list.is-rows .live-row {
    grid-template-columns: auto minmax(10rem, 1fr) auto auto;
    align-items: center;
    padding: .45rem .7rem;
    border: 0;
    border-bottom: 1px solid var(--border-light);
    border-radius: 0;
    box-shadow: none;
}
.live-list.is-rows .live-row:last-child { border-bottom: 0; }
.live-list.is-rows .live-row > .live-who { grid-column: 2; }
.live-list.is-rows .live-row > .live-status { grid-column: 3; }
.live-list.is-rows .live-row > .live-flags { grid-column: 4; }
.live-list.is-rows .live-row > .live-row-actions { grid-column: 5; justify-self: end; }
.live-list.is-rows .live-row[data-status="locked"] { box-shadow: inset 4px 0 0 var(--accent-coral); }
.live-list.is-rows .live-exit-detail, .live-list.is-rows .live-message { grid-column: 1 / -1; }

/* The bar is the top of the students' panel: full width of the card, and it stays in view while the list
   scrolls under it. Alone (before the panel exists) it is a card of its own, as above. */
.live-students > .live-bar {
    margin: calc(-1 * var(--space-4)) calc(-1 * var(--space-4)) .85rem;
    padding: .75rem var(--space-4) .6rem;
    border: 0;
    border-bottom: 1px solid var(--border-light);
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    box-shadow: none;
}

/* The strip under the title: state, who is where, and what was set. What does not fit goes down a line. */
.live-strip {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem;
    margin-bottom: .9rem;
    padding: .55rem .8rem;
    border: 1px solid var(--border-light);
    border-radius: var(--radius-lg);
    background: var(--surface-card);
    box-shadow: var(--shadow-sm);
}
.live-strip > * { flex: none; white-space: nowrap; }
.live-strip-sep { inline-size: 1px; align-self: stretch; margin-inline: .35rem; background: var(--border-light); }
.live-count { font-weight: 600; }
.live-count b { font-variant-numeric: tabular-nums; margin-inline-end: .15rem; }

/* The student filters are the list's own and never scroll sideways: what does not fit goes down a line. */
.live-filters { flex-wrap: wrap; overflow-x: visible; }

/* ── Statuses over the whole width, on the background: no panel ── */
.live-strip {
    margin-bottom: .75rem;
    padding: 0 .1rem;
    border: 0;
    background: transparent;
    box-shadow: none;
}

/* The third column: what is known. Cards like the rest. */
.live-info { display: flex; flex-direction: column; gap: .9rem; min-width: 0; }
.live-info > .publish-section,
.live-info > details.hub-fold {
    margin: 0;
    padding: var(--space-4);
    border: 1px solid var(--border-light);
    border-radius: var(--radius-lg);
    background: var(--surface-card);
    box-shadow: var(--shadow-sm);
}

.live-cols.is-prestart > .live-info { order: 0; }

/* The statuses under the activity's name, in the head. What does not fit goes down a line. */
.life-head-status { margin-top: .5rem; }
.status-chips { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; }
.status-chips b { font-variant-numeric: tabular-nums; margin-inline-end: .15rem; }

/* Two columns: the students, and beside them everything else - what is acted on, then what is known. */
.live-left { display: flex; flex-direction: column; gap: .9rem; min-width: 0; }
.live-left > .live-side, .live-left > .live-info { flex: none; }

/* The left column in the order the teacher uses it: a message to everyone, the questions, then the rules;
   whatever needs the teacher stays on top while there is something. The rest follows. */
.live-page.is-running .live-left > .live-side,
.live-page.is-running .live-left > .live-info { display: contents; }
.live-page.is-running .live-left .live-attn { order: 5; }
.live-page.is-running .live-left .live-announce { order: 10; }
.live-page.is-running .live-left .live-info > :first-child { order: 20; }
.live-page.is-running .live-left .live-rules { order: 30; }
.live-page.is-running .live-left .live-clear { order: 9; }
.live-page.is-running .live-left .live-clock-controls { order: 35; }
.live-page.is-running .live-left .live-info > :not(:first-child) { order: 40; }

/* The running-activity sidebar is a single operational surface: every panel heading reads with the
   same quiet caption treatment as the announcement composer, instead of mixing panel-title styles. */
.live-page.is-running .live-left .compose-panel-head,
.live-page.is-running .live-left .live-rules-title,
.live-page.is-running .live-left .live-clock-controls-title,
.live-page.is-running .live-left .live-announce-label,
.live-page.is-running .live-left .live-info .publish-section-title {
    display: flex;
    align-items: center;
    gap: .35rem;
    margin: 0 0 .35rem;
    color: var(--ink-secondary);
    font-size: var(--fs-xs);
    font-weight: 700;
    line-height: 1.2;
    letter-spacing: normal;
    text-transform: none;
}
.live-page.is-running .live-left .compose-panel-head > .bi,
.live-page.is-running .live-left .live-rules-title > .bi,
.live-page.is-running .live-left .live-clock-controls-title > .bi,
.live-page.is-running .live-left .live-announce-label > .bi,
.live-page.is-running .live-left .live-info .publish-section-title > .bi {
    flex: 0 0 1em;
    inline-size: 1em;
    margin-inline-end: 0 !important;
    color: var(--primary-700);
    font-size: 1em;
    text-align: center;
}
.live-page.is-running .live-left .compose-panel-head::after,
.live-page.is-running .live-left .live-rules-title::after,
.live-page.is-running .live-left .live-clock-controls-title::after,
.live-page.is-running .live-left .live-announce-label::after,
.live-page.is-running .live-left .live-info .publish-section-title:not(summary)::after,
.live-page.is-running .live-left .live-info .hub-fold > .publish-section-title > span::after {
    content: '';
    flex: 1;
    block-size: 1px;
    background: var(--border-light);
}
.live-page.is-running .live-left .live-info .hub-fold > .publish-section-title > span {
    display: inline-flex;
    flex: 1;
    align-items: center;
    gap: .35rem;
}

/* The all-clear line now belongs to the students: inside their panel, above the search. */
.live-students .live-clear { margin: 0 0 .6rem; }

/* The statuses sit under the title row on the whole width, compact, and wrap only when they must. */
.life-head-status { margin-top: .6rem; }
.status-chips .ex-chip { font-size: var(--fs-2xs); padding: .15rem .55rem; }

/* A control of the top bar a few pixels past the edge must not give the whole page a sideways scroll
   (clip, not hidden: nothing becomes a scroll container, so sticky parts keep working). */
body { overflow-x: clip; }

/* In the monitor the questions are a reminder, not a reading: two lines each; the full text is on hover and in
   "view the questions". */
.live-page .details-basket-list .details-basket-text {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    white-space: normal;
}

.live-roster-chip { margin-inline-start: .5rem; font-weight: 600; font-size: var(--fs-2xs); }

/* The "live" mark of the students' panel sits at the far end of the title row, apart from the count. */
.live-students .publish-section-title .live-pulse { margin-inline-start: auto; }

/* The dot and the word of the "live" mark in the other order. */
.live-students .live-pulse { flex-direction: row-reverse; }


/* The dot of a student's row is their status, one colour each: answering green, handed in blue (grey once released),
   waiting at the door amber, stopped red, dropped a grey ring, not in yet a faint ring. */
.live-row[data-status="working"] .live-dot { border-color: var(--accent-green); background: var(--accent-green); }
.live-row[data-status="submitted"] .live-dot { border-color: var(--accent-blue); background: var(--accent-blue); }
.live-row[data-status="submitted"][data-released="true"] .live-dot { border-color: var(--ink-faint); background: var(--ink-faint); }
.live-row[data-status="waiting"] .live-dot { border-color: var(--accent-amber); background: var(--accent-amber); }
.live-row[data-status="locked"] .live-dot { border-color: var(--accent-coral); background: var(--accent-coral); }
.live-row[data-status="dropped"] .live-dot { border-color: var(--ink-muted); background: transparent; }
.live-row[data-status="unsubmitted"] .live-dot { border-color: var(--accent-amber); background: transparent; }
.live-row[data-status="out"] .live-dot { border-color: var(--ink-faint); background: transparent; }
.live-dot { inline-size: .7rem; block-size: .7rem; }


/* The entry code in the bar: monospace, big enough to read from a desk, one click to copy. */
.live-code {
    display: inline-flex; align-items: center; gap: .4rem;
    padding: .3rem .85rem;
    border: 1px solid var(--border-light); border-radius: var(--radius-pill);
    background: var(--surface-subtle); color: var(--primary-800);
    font-size: 1.05rem; line-height: 1.2;
}
.live-code:hover { border-color: var(--primary-700); }
.live-code-value { font-family: var(--bs-font-monospace); font-weight: 800; letter-spacing: .12em; }
.live-code-done { color: var(--accent-green); font-weight: 600; }


/* Handed in and waiting for the teacher's approval: an amber dot and an amber edge, like the red one of a stopped student. */
.live-row[data-awaiting="true"] { border-color: var(--accent-amber-border); box-shadow: inset 4px 0 0 var(--accent-amber); }
.live-row[data-awaiting="true"] .live-dot { border-color: var(--accent-amber); background: var(--accent-amber); }
.live-list.is-rows .live-row[data-awaiting="true"] { box-shadow: inset 4px 0 0 var(--accent-amber); }


/* Time has run out but the activity is still open: amber words instead of a red 00:00. */
.live-clock.is-over .live-clock-value { color: var(--accent-amber); font-family: inherit; font-size: 1.15rem; font-weight: 700; }
.live-clock.is-over .live-clock-bar > span { background: var(--accent-amber); }

/* A setting as "name | value": the name quiet, the value in bold, the values lined up. */
.publish-kv {
    display: grid;
    grid-template-columns: minmax(5.5rem, 40%) minmax(0, 1fr);
    gap: .5rem;
    align-items: baseline;
    padding: .18rem 0;
    font-size: var(--fs-xs);
}
.publish-kv-name { color: var(--ink-muted); }
.publish-kv-value { color: var(--ink-primary); font-weight: 600; overflow-wrap: anywhere; }

/* A form that stays one column: what a choice opens comes under it, not beside it. */
.details-form-grid.is-single { grid-template-columns: 1fr; }

/* The info icon shows its text at once, on pointing or focusing (a touch screen has no pointing). */
.details-info { position: relative; }
.details-info[data-tip]:hover::after,
.details-info[data-tip]:focus::after {
    content: attr(data-tip);
    position: absolute;
    z-index: 30;
    inset-block-start: calc(100% + .3rem);
    inset-inline-start: 0;
    inline-size: max-content;
    max-inline-size: min(20rem, 70vw);
    padding: .5rem .7rem;
    border-radius: var(--radius-md, 10px);
    background: var(--ink-primary);
    color: #fff;
    font-size: var(--fs-xs);
    font-weight: 400;
    line-height: 1.5;
    text-align: start;
    white-space: normal;
    box-shadow: var(--shadow-md, 0 4px 14px rgb(0 0 0 / .2));
    pointer-events: none;
}

/* A field's title with a line running from it to the far side of the panel. */
.details-field-head.is-rule { display: flex; align-items: center; gap: .6rem; }
.details-field-head.is-rule::after { content: ""; flex: 1 1 auto; border-block-start: 1px solid var(--border-light); }

/* A section title with a line from it to the far side of the panel, instead of a rule above it. */
.details-subhead.is-rule { display: flex; align-items: center; gap: .6rem; padding-block-start: 0; border-block-start: 0; }
.details-subhead.is-rule::after { content: ""; flex: 1 1 auto; border-block-start: 1px solid var(--border-light); }

/* A label that explains itself on pointing: the bubble shows at once, no icon and no text taking room. */
.tip-label { position: relative; cursor: help; }
.tip-label[data-tip]:hover::after,
.tip-label[data-tip]:focus-within::after {
    content: attr(data-tip);
    position: absolute;
    z-index: 30;
    inset-block-start: calc(100% + .3rem);
    inset-inline-start: 0;
    inline-size: max-content;
    max-inline-size: min(20rem, 70vw);
    padding: .5rem .7rem;
    border-radius: var(--radius-md, 10px);
    background: var(--ink-primary);
    color: #fff;
    font-size: var(--fs-xs);
    font-weight: 400;
    line-height: 1.5;
    text-align: start;
    white-space: normal;
    box-shadow: var(--shadow-md, 0 4px 14px rgb(0 0 0 / .2));
    pointer-events: none;
}

/* The lock's title keeps a little room from what it introduces. */
.details-field-head.is-rule { margin-block-end: .6rem; }

/* The closed list on the summary: names with their numbers, in as many columns as fit. */
.publish-roster {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
    gap: .2rem 1rem;
    margin: .6rem 0 0;
    padding: 0;
    list-style: none;
    font-size: var(--fs-xs);
}
.publish-roster li { display: flex; align-items: baseline; gap: .6rem; min-inline-size: 0; }
.publish-roster-name { flex: 1 1 auto; min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.publish-roster-id { flex: none; inline-size: 5.5rem; text-align: start; color: var(--ink-muted); opacity: .85; font-variant-numeric: tabular-nums; }

/* The status card on the summary: the conclusion and the action together. */
.st-card { background: var(--surface-card); border: 1px solid var(--border-light); border-radius: 16px; overflow: hidden; box-shadow: var(--shadow-sm); margin-block-end: 1rem; }
.st-head { display: flex; align-items: center; gap: .9rem; padding: 1.1rem 1.25rem; }
.st-mark { inline-size: 2.75rem; block-size: 2.75rem; border-radius: 50%; display: grid; place-items: center; font-size: 1.35rem; flex: none; }
.st-ok .st-mark { background: var(--primary-100); color: var(--primary-700); }
.st-notes .st-mark { background: var(--accent-amber-bg); color: var(--accent-amber); }
.st-block .st-mark { background: var(--accent-coral-bg); color: var(--accent-coral); }
.st-text { min-inline-size: 0; }
.st-title { font-size: 1.05rem; font-weight: 800; line-height: 1.25; }
.st-sub { color: var(--ink-muted); font-size: .85rem; margin-block-start: .1rem; }
.st-cta { margin-inline-start: auto; flex: none; }
.st-list { border-block-start: 1px solid var(--primary-100); padding: .4rem 1.25rem .9rem; }
.st-group { font-size: .72rem; font-weight: 700; color: var(--ink-muted); margin: .75rem 0 .4rem; display: flex; align-items: center; gap: .4rem; }
.st-group .n { background: var(--surface-subtle); border-radius: 999px; padding: 0 .45rem; font-variant-numeric: tabular-nums; }
.st-dot-block, .st-dot-note { font-size: .4rem; }
.st-dot-block { color: var(--accent-coral); }
.st-dot-note { color: var(--accent-amber); }
.st-row { display: flex; align-items: center; gap: .6rem; padding: .5rem .65rem; border-radius: 10px; font-size: .9rem; }
.st-row + .st-row { margin-block-start: .25rem; }
.st-row > i { flex: none; }
.st-row.block { background: var(--accent-coral-bg); }
.st-row.block > i { color: var(--accent-coral); }
.st-row.note { background: var(--accent-amber-bg); }
.st-row.note > i { color: var(--accent-amber); }
.st-fix { margin-inline-start: auto; flex: none; font-weight: 700; font-size: .82rem; text-decoration: none; white-space: nowrap; color: var(--primary-800); display: inline-flex; align-items: center; gap: .25rem; }
.st-fix:hover { text-decoration: underline; }
.st-tools { border-block-start: 1px solid var(--primary-100); background: var(--surface-subtle); padding: .45rem .85rem; display: flex; flex-wrap: wrap; align-items: center; gap: .25rem; }
.st-tools .lbl { color: var(--ink-muted); font-size: .75rem; font-weight: 600; margin-inline-end: .4rem; }
.st-tools .publish-preview { display: contents; }
.st-tools .btn.btn-outline-secondary { border: 0; background: transparent; color: var(--ink-secondary); }
.st-tools .btn.btn-outline-secondary:hover:not(:disabled) { background: var(--surface-card); color: var(--ink-primary); }

/* Nobody has come in yet: a place of some height that says who will appear in it. */
.live-empty.is-placeholder {
    flex-direction: column;
    justify-content: center;
    text-align: center;
    gap: .35rem;
    min-block-size: 11rem;
    padding: 1.5rem 1rem;
    border: 1.5px dashed var(--border-light);
    border-radius: 14px;
    background: var(--surface-subtle);
}
.live-empty.is-placeholder > .bi { font-size: 1.8rem; color: var(--ink-muted); }
.live-empty-title { font-weight: 600; color: var(--ink-primary); }
.live-empty-sub { color: var(--ink-muted); font-size: var(--fs-xs); }

/* The list on the settings: a box of a fixed height that scrolls, so a long list does not stretch the card. */
.roster-scroll {
    max-block-size: 11rem;
    overflow-y: auto;
    padding: .5rem .75rem;
    border: 1px solid var(--border-light);
    border-radius: 10px;
    background: var(--surface-subtle);
}

/* The student list window: the pane under the tabs holds one height, so switching tabs does not resize the window. */
.roster-modal-pane { min-block-size: 25rem; }

/* Tabs as a row of words with a line under the chosen one - a way to switch, not a set of buttons. */
.ex-tabs-line { display: flex; gap: .25rem; margin-block-end: 1rem; border-block-end: 1px solid var(--border-light); }
.ex-tab-line {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .5rem .9rem;
    margin-block-end: -1px;
    border: 0;
    border-block-end: 2px solid transparent;
    background: transparent;
    color: var(--ink-secondary);
    font-size: var(--fs-sm);
    font-weight: 600;
}
.ex-tab-line:hover { color: var(--ink-primary); }
.ex-tab-line.is-selected { color: var(--primary-700); border-block-end-color: var(--primary-700); font-weight: 700; }

/* The entry's code inside the status card, between what is left and the tools. */
.st-entry { border-block-start: 1px solid var(--primary-100); padding: .4rem 1.25rem 1rem; }
.st-entry { display: flex; flex-wrap: wrap; align-items: center; column-gap: 1rem; row-gap: .4rem; }
.st-entry .st-group { flex: 0 0 100%; margin-block-start: .6rem; margin-block-end: 0; }
/* The code, its copy button and the class-board button on one line; the hint of where to type it under them. */
.st-entry .hub-label { display: none; }
.st-entry .hub-code { order: 1; }
.st-entry .hub-entry-facts { order: 2; margin: 0; }
.st-entry .hub-entry-hint { order: 3; flex: 0 0 100%; margin: 0; }
.st-entry .hub-code-value { font-size: 1.6rem; }

.hub-project-note { color: var(--ink-muted); font-size: var(--fs-sm); text-align: center; }

/* The school's logo above the title on the class screen. */
.hub-project-logo { max-block-size: clamp(3.5rem, 12vh, 7rem); max-inline-size: min(22rem, 70vw); object-fit: contain; }

/* The entry in the side column once it is open: laid out as in the status card - the code, its copy button and the
   class-board button together, the hint of where to type it under them. */
.publish-section.is-entry { display: flex; flex-wrap: wrap; align-items: center; column-gap: 1rem; row-gap: .4rem; }
.publish-section.is-entry > .publish-section-title { flex: 0 0 100%; margin-block-end: 0; }
.publish-section.is-entry .hub-label { display: none; }
.publish-section.is-entry .hub-code { order: 1; }
.publish-section.is-entry .hub-entry-hint { order: 2; flex: 0 0 100%; margin: 0; }
.publish-section.is-entry .hub-entry-facts { order: 3; flex: 0 0 100%; margin: .15rem 0 0; }
.publish-section.is-entry .hub-entry-facts + .hub-entry-facts { order: 4; }
.publish-section.is-entry .hub-code-value { font-size: 1.6rem; }

/* Held at the door until the teacher approves. */
.live-row[data-status="held"] .live-dot { border-color: var(--accent-amber); background: var(--accent-amber); }
.live-row[data-status="held"] .live-status { background: var(--accent-amber-bg); border-color: var(--accent-amber-border); color: var(--ink-primary); }

/* How to get the list from Moodle, folded under its title in the import window. */
.roster-help { border: 1px solid var(--border-light); border-radius: 10px; background: var(--surface-subtle); padding: .5rem .85rem; font-size: var(--fs-sm); }
.roster-help > summary { cursor: pointer; font-weight: 600; color: var(--ink-secondary); list-style: none; }
.roster-help[open] > summary { margin-block-end: .4rem; }
.roster-help ol { margin: 0 0 .4rem; padding-inline-start: 1.25rem; }
.roster-help li { margin-block: .15rem; }
.roster-help-note { color: var(--ink-muted); font-size: var(--fs-xs); }

/* An option in a radio list: its name, and what it does on a line of its own under it. */
.form-check-label .opt-name { display: block; font-size: var(--fs-sm); font-weight: 600; }
.form-check-label .opt-hint { display: block; color: var(--ink-muted); font-size: var(--fs-xs); font-weight: 400; line-height: 1.4; }

/* The grades list wears the monitor's rows and cards; what is its own is the mark. */
.live-row.grade-card { cursor: pointer; }
.live-row.grade-card.is-absent { cursor: default; opacity: .7; }
.live-row.grade-card:hover:not(.is-absent) { border-color: var(--primary-500, var(--bs-primary)); }
.live-row[data-grade="pass"] .live-dot { border-color: var(--primary-700); background: var(--primary-700); }
.live-row[data-grade="fail"] .live-dot { border-color: var(--accent-coral); background: var(--accent-coral); }
.live-row[data-grade="wait"] .live-dot { border-color: var(--accent-amber); background: var(--accent-amber); }
.grade-score { display: grid; justify-items: end; line-height: 1.1; justify-self: end; }
.live-list > .live-row > .grade-score { grid-column: 3; }
.grade-score b { font-size: 1.3rem; font-variant-numeric: tabular-nums; }
.grade-score small { color: var(--ink-muted); font-size: .72rem; font-variant-numeric: tabular-nums; }
.grade-score.pass b { color: var(--primary-700); }
.grade-score.fail b { color: var(--accent-coral); }
.grade-score.none b { color: var(--ink-muted); }
.grade-sort { max-inline-size: 9.5rem; }

/* How the class did, question by question. */
.grade-q { display: grid; grid-template-columns: 1.4rem minmax(0, 1fr) 2.6rem; gap: .5rem; align-items: center; padding: .25rem 0; font-size: var(--fs-xs); }
.grade-q-bar { block-size: .45rem; border-radius: 99px; background: var(--surface-subtle); overflow: hidden; }
.grade-q-bar i { display: block; block-size: 100%; background: var(--primary-700); }
.grade-q.is-hard .grade-q-bar i { background: var(--accent-coral); }
.grade-q-pct { text-align: end; font-weight: 700; font-variant-numeric: tabular-nums; }

/* One student's paper: the name and the mark stay in view while the answers are read under them. */
@media (min-width: 768px) {
    .check-paper-head {
        position: sticky;
        top: calc(var(--examin-topbar-h, 68px) + .35rem);
        z-index: 15;
        margin-inline: -.5rem;
        padding: .6rem .5rem .7rem;
        border-radius: var(--radius-lg, 12px);
        background: var(--surface-glass, rgba(255, 254, 250, .95));
        backdrop-filter: blur(6px);
        box-shadow: 0 1px 0 var(--border-light);
    }
}

/* A narrow card: the title keeps a readable width, and the actions drop under it instead of squeezing it to a word a line. */
.st-head { flex-wrap: wrap; }
.st-text { flex: 1 1 14rem; }

.live-pop a { text-decoration: none; }

/* One student's answers, read-only: the question, and what they answered - nothing about points. */
.answers-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: .4rem 1.5rem; margin-block-end: 1rem; }
.answers-name { font-size: 1.4rem; font-weight: 800; }
.answers-title { color: var(--ink-muted); font-weight: 600; }
.answers-q { display: grid; grid-template-columns: 2rem minmax(0, 1fr); gap: .75rem; padding: 1rem 0; border-block-end: 1px solid var(--border-light); }
.answers-q:last-child { border-block-end: 0; }
.answers-num { inline-size: 1.7rem; block-size: 1.7rem; border-radius: 50%; display: grid; place-items: center; background: var(--surface-subtle); font-size: var(--fs-xs); font-weight: 700; }
.answers-option { display: flex; align-items: center; gap: .6rem; padding: .5rem .75rem; border: 1px solid var(--border-light); border-radius: 10px; font-size: var(--fs-sm); }
.answers-option.is-chosen { border-color: var(--primary-700); background: var(--primary-100); font-weight: 600; }
.answers-text { padding: .75rem .9rem; border: 1px solid var(--border-light); border-radius: 10px; background: var(--surface-subtle); min-block-size: 2.5rem; font-size: var(--fs-sm); }
.answers-text.is-empty { background: var(--accent-amber-bg); border-color: var(--accent-amber-border); }
.answers-code { margin: 0; white-space: pre-wrap; text-align: left; font-family: var(--bs-font-monospace); overflow-x: auto; }

/* The entry in the side column, as one tidy column: the code and its copy button, where to type it, and the class board
   under them - everything starting at the same edge. */
.publish-section.is-entry { flex-direction: column; flex-wrap: nowrap; align-items: stretch; gap: .65rem; }
.publish-section.is-entry > .publish-section-title { flex: 0 0 auto; }
.publish-section.is-entry .hub-code { justify-content: flex-start; }
.publish-section.is-entry .hub-code .d-flex { gap: .9rem !important; }
.publish-section.is-entry .hub-entry-hint { flex: 0 0 auto; align-items: flex-start; }
.publish-section.is-entry .hub-entry-hint > .bi { margin-block-start: .15rem; }
.publish-section.is-entry .hub-entry-facts { flex: 0 0 auto; }
.publish-section.is-entry .hub-entry-facts > .btn { inline-size: 100%; }

/* A row's menu: a layer under it takes the click that closes it, and a list of rows does not clip it. */
.live-pop-backdrop { position: fixed; inset: 0; z-index: 25; }
.live-kebab { z-index: 26; }
.live-list.is-rows { overflow: visible; }
.live-list.is-rows .live-row:first-child { border-start-start-radius: var(--radius-md, 10px); border-start-end-radius: var(--radius-md, 10px); }
.live-list.is-rows .live-row:last-child { border-end-start-radius: var(--radius-md, 10px); border-end-end-radius: var(--radius-md, 10px); }

/* The teacher's line about a student: a quiet chip in the list, a callout on the paper. */
.live-note-chip { max-inline-size: 18rem; border: 0; cursor: pointer; }
.live-note-chip > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.check-note { margin-block-start: .3rem; padding: .3rem .6rem; border-radius: 8px; background: var(--accent-amber-bg); border: 1px solid var(--accent-amber-border); font-size: var(--fs-sm); font-weight: 500; }

/* A student's answers on paper / as a PDF: each question stays whole on its page. */
@media print { .answers-q { break-inside: avoid; } .answers-text, .answers-option { border-color: #ccc; } }

/* The teacher's shelf of saved tools, inside a quiz: a plain list under the categories, one row per
   tool, the add button at the end. Different from the cards below, which are what the quiz has. */
.tool-shelf { margin: 0; }
.tool-shelf-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .25rem; max-height: 11rem; overflow-y: auto; border: 1px solid var(--border-light); border-radius: var(--radius-md, .5rem); }
.tool-shelf-row:last-child { border-bottom: 0; }
.tool-shelf-toggle { display: flex; align-items: center; gap: .4rem; width: 100%; background: none; border: 0; padding: 0 0 .35rem; text-align: start; cursor: pointer; }
.tool-shelf-row {
    display: flex;
    align-items: center;
    gap: .55rem;
    padding: .3rem .6rem;
    border-bottom: 1px solid var(--border-light);
    font-size: var(--fs-xs);
}
.tool-shelf-row.is-open { background: var(--primary-100); }
.tool-shelf-name { font-weight: 600; white-space: nowrap; }
.tool-shelf-desc { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink-secondary); font-size: var(--fs-2xs); }
.tool-shelf-name:last-of-type { flex: 1; }
.tool-shelf-actions { display: inline-flex; align-items: center; gap: .6rem; margin-inline-start: auto; }
.tool-shelf-add { padding: .1rem .6rem; font-size: var(--fs-2xs); white-space: nowrap; }

/* What the quiz has: small cards, in the order of the student's toolbar. */
.tool-added-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(12.5rem, 1fr)); gap: .5rem; margin: 0; padding: 0; list-style: none; }
.tool-added-card {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .45rem .6rem;
    border: 1px solid var(--border-light);
    border-radius: var(--radius-md, .5rem);
    background: var(--surface-card, #fff);
    font-size: var(--fs-xs);
}
.tool-added-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }

/* Section headings of the tools step sit closer to what they head. */
.details-subhead.is-tight { margin: 1.9rem 0 .75rem; padding-top: 0; border-top: 0; }
.tool-shelf-toggle.details-subhead.is-tight { padding: 0; }
/* The first heading of the tools step sits right under the step's own title. */
.details-pane-body > div > .details-subhead.is-tight:first-child { margin-top: 0; }
/* What the quiz already has stands out from what could be added: green border and a light green wash. */
.tool-added-card { border-color: var(--primary-700, #257A59); background: var(--primary-100); }


/* The class review of an open question with a key: what to show, the solution a step at a time, the rubric with the room's numbers. */
.review-key-toggles { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.review-key-pill {
    padding: .3rem .8rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 999px;
    background: var(--bs-body-bg);
    color: var(--bs-secondary-color);
    font-size: .85rem;
}
.review-key-pill.is-on { background: var(--bs-success-bg-subtle); color: var(--bs-success-text-emphasis); border-color: var(--bs-success-border-subtle); }
.review-key-step { display: flex; align-items: flex-start; gap: .6rem; padding: .25rem 0; }
.review-key-step-n {
    display: inline-flex; align-items: center; justify-content: center; flex: none;
    width: 1.5rem; height: 1.5rem; border-radius: 50%; font-size: .8rem; font-weight: 600;
    background: var(--bs-success-bg-subtle); color: var(--bs-success-text-emphasis);
}
.review-key-row {
    display: grid; grid-template-columns: minmax(0, 1fr) 9rem 3rem; align-items: center; gap: .75rem;
    padding: .4rem 0; border-top: 1px solid var(--bs-border-color-translucent);
}
.review-key-row:first-of-type { border-top: 0; }
.review-key-row.is-plain { grid-template-columns: minmax(0, 1fr) auto; }
.review-key-meter { height: .5rem; border-radius: .25rem; background: var(--bs-secondary-bg); overflow: hidden; }
.review-key-meter-fill { display: block; height: 100%; background: var(--bs-success); opacity: .75; }
.review-key-meter-fill.is-low { background: var(--bs-warning); }
.review-key-pct { text-align: end; color: var(--bs-secondary-color); font-size: .9rem; }


/* Answer keys: where a question's key stands. */
.key-dot {
    display: inline-flex; align-items: center; justify-content: center; flex: none;
    width: 28px; height: 28px; border-radius: 8px; font-size: .95rem;
    border: 1px solid var(--bs-border-color); background: var(--bs-secondary-bg); color: var(--bs-secondary-color);
}
.key-dot-draft { background: var(--bs-warning-bg-subtle); color: var(--bs-warning-text-emphasis); border-color: var(--bs-warning-border-subtle); }
.key-dot-disputed { background: var(--bs-danger-bg-subtle); color: var(--bs-danger-text-emphasis); border-color: var(--bs-danger-border-subtle); }
.key-dot-approved, .key-dot-manual { background: var(--bs-success-bg-subtle); color: var(--bs-success-text-emphasis); border-color: var(--bs-success-border-subtle); }
.key-status {
    display: inline-block; padding: .15rem .6rem; border-radius: 999px; font-size: .75rem;
    border: 1px solid var(--bs-border-color); background: var(--bs-secondary-bg); color: var(--bs-secondary-color);
}
.key-status-draft { background: var(--bs-warning-bg-subtle); color: var(--bs-warning-text-emphasis); border-color: var(--bs-warning-border-subtle); }
.key-status-disputed { background: var(--bs-danger-bg-subtle); color: var(--bs-danger-text-emphasis); border-color: var(--bs-danger-border-subtle); }
.key-status-approved, .key-status-manual { background: var(--bs-success-bg-subtle); color: var(--bs-success-text-emphasis); border-color: var(--bs-success-border-subtle); }
.keys-list-item { display: flex; align-items: center; gap: .6rem; }
.keys-list-text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.keys-list-prompt { font-size: .8rem; color: var(--bs-secondary-color); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.keys-list-item.active .keys-list-prompt { color: inherit; opacity: .85; }
.key-legend { display: flex; flex-wrap: wrap; gap: .75rem; font-size: .75rem; color: var(--bs-secondary-color); }
.key-legend > span { display: inline-flex; align-items: center; gap: .3rem; }
.key-legend .key-dot { width: 18px; height: 18px; font-size: .65rem; border-radius: 5px; }

/* The key on a basket row is a plain icon like its neighbours; only its colour says where the key stands. */
.key-ico-draft { color: var(--bs-warning-text-emphasis) !important; }
.key-ico-disputed { color: var(--bs-danger) !important; }
.key-ico-approved { color: var(--bs-success) !important; }
