html, body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

a, .btn-link {
    color: #006bb7;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
    box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* ===========================
   Student runner - cypher (secure) font
   ===========================
   CMR_Sans_serif is GLYPH-SHIFTED: each codepoint carries the outline of the
   letter one position forward (the glyph at 'h' is an 'i', at 'l' an 'm'), so
   the ROT(-1) text in Line.Cypher DISPLAYS as the original words while
   copy-paste yields the shifted characters.

   Enable chain: a "Secure"/"Cypher" token on any sequence's LogicValues ->
   EfAttemptSnapshotService freezes UseCypher into the attempt snapshot ->
   runner renderers apply .cypher-font PER LINE (only where Line.Cypher is
   populated). Per-student device-compat opt-out: AssessmentFlagKind.CypherOff.

   font-display: block — NEVER paint fallback glyphs. In a normal font the
   fallback flash is cosmetic; here it would flash the raw shifted text
   (gibberish) at the student. The font is also preloaded in App.razor's
   <head> so it is resident before the runner renders. */
@font-face {
    font-family: 'OmniaCypher';
    src: url('/fonts/CMR_Sans_serif.woff2') format('woff2'),
         url('/fonts/CMR_Sans_serif.woff') format('woff');
    font-weight: normal;
    font-style: normal;
    font-display: block;
}

.cypher-font {
    font-family: 'OmniaCypher', system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
}

/* ===========================
   Player option buttons (MCQ, navigation)
   Global so any renderer can use .mcq-option without component scoping.
   =========================== */

.mcq-option-group {
    margin: 2rem 0 1.2rem;
}

.mcq-horizontal {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.7rem;
}

.mcq-horizontal .mcq-option {
    flex: 0 0 calc((100% - 2.1rem) / 4);
    min-width: 0;
}

.mcq-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.7rem;
}

.mcq-vertical-half {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0.55rem;
    max-width: 50%;
    margin-left: auto;
    margin-right: auto;
}

.mcq-vertical-full {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0.55rem;
}

/* A lone navigation button (e.g. "Continue" on an end / informational screen)
   must NOT stretch full width — size it to its content (with a sensible minimum)
   and centre it. Global (not scoped) because nav buttons are rendered via
   RenderTreeBuilder, which scoped CSS does not reach. */
.nav-buttons-single {
    display: flex;
    justify-content: center;
}

.nav-buttons-single .mcq-option {
    width: auto;
    min-width: 16rem;
    max-width: 40rem;
    /* Nav buttons carry no .mcq-letter glyph, so drop the 3.6rem left indent and centre the label. */
    padding-left: 1rem;
    text-align: center;
}

.mcq-option {
    display: block;
    position: relative;
    min-height: 4.4rem;
    padding: 0.72rem 1rem 0.72rem 3.6rem;
    border: 3px solid var(--player-option-border, #111);
    border-radius: 0;
    background: var(--player-option-bg, #fff);
    color: var(--player-option-text, #111);
    font-size: calc(clamp(1.35rem, 1.55vw, 1.9rem) * var(--runner-font-scale, 1));
    font-weight: 400 !important;
    cursor: pointer;
    text-align: left;
    transition: border-color 0.12s, background 0.12s, color 0.12s;
    white-space: normal;
}

.mcq-option:hover:not(:disabled) {
    border-color: var(--player-accent);
}

.mcq-option:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.mcq-option:focus-visible {
    outline: 2px solid var(--player-accent);
    outline-offset: 2px;
}

.mcq-selected {
    background: var(--player-accent) !important;
    border-color: var(--player-accent) !important;
    color: #fff !important;
    font-weight: 600 !important;
}

.mcq-selected .mcq-letter {
    font-weight: 700;
}

.mcq-multi.mcq-selected::after {
    content: "\2713";
    position: absolute;
    right: 0.8rem;
    top: 50%;
    transform: translateY(-50%);
    font-size: 1.4em;
    font-weight: 700;
    color: #fff;
}

.mcq-audio-highlight {
    outline: 2px solid var(--player-audio-highlight, #f5a623);
    outline-offset: 4px;
}

.mcq-letter {
    position: absolute;
    left: 0.8rem;
    top: 50%;
    transform: translateY(-50%);
    font-weight: 400;
    min-width: 2.4rem;
    text-align: left;
}

.mcq-text {
    display: block;
    width: 100%;
    text-align: center;
    padding-right: 2rem;
}

@media (max-width: 1100px) {
    .mcq-horizontal .mcq-option {
        flex-basis: calc((100% - 0.7rem) / 2);
    }

    .mcq-vertical-half {
        max-width: 100%;
    }
}

@media (max-width: 768px) {
    .mcq-horizontal .mcq-option {
        flex-basis: 100%;
    }

    .mcq-grid {
        grid-template-columns: 1fr;
    }

    .mcq-option {
        min-height: 3rem;
        font-size: 1rem;
    }

    /* On phones a full-width button is conventional and avoids tight-fit overflow. */
    .nav-buttons-single .mcq-option {
        width: 100%;
        min-width: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .mcq-option {
        transition: none;
    }
}

/* ===========================
   Multi-row single-select pill buttons
   A Buttons line with .RadioSelect options + "Layout:Pills" in its Style renders
   as a responsive grid of pill buttons (one independent single-select row per
   Buttons line) instead of the Likert dot scale. Global because RenderButtons is
   RenderTreeBuilder output that Blazor CSS scoping does not reach.
   =========================== */

.radio-pill-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: 0.9rem;
    max-width: 1160px;
    margin: 1.1rem auto;
}

.radio-pill {
    min-height: 4.6rem;
    padding: 0.7rem 1.1rem;
    border: 2px solid var(--player-option-border, #444);
    border-radius: 0.9rem;
    background: var(--player-option-bg, transparent);
    color: var(--player-option-text, inherit);
    font-size: calc(clamp(1.05rem, 1.25vw, 1.4rem) * var(--runner-font-scale, 1));
    line-height: 1.3;
    text-align: center;
    cursor: pointer;
    transition: border-color 0.12s, background 0.12s, color 0.12s;
    white-space: normal;
}

.radio-pill:hover:not(:disabled):not(.radio-pill-selected) {
    border-color: var(--player-accent, #1cb6c9);
}

.radio-pill:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.radio-pill:focus-visible {
    outline: 2px solid var(--player-accent, #1cb6c9);
    outline-offset: 2px;
}

.radio-pill-selected {
    background: var(--player-accent, #1cb6c9) !important;
    border-color: var(--player-accent, #1cb6c9) !important;
    color: #fff !important;
    font-weight: 600;
}

@media (max-width: 768px) {
    .radio-pill-row {
        grid-template-columns: 1fr 1fr;
        gap: 0.5rem;
    }

    .radio-pill {
        min-height: 3.4rem;
        font-size: 1rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    .radio-pill {
        transition: none;
    }
}

/* ===========================
   Pinned-question mode (long multi-select MCQ)
   The stem + selected-answer chips stick to the top of .player-content-area
   (the runner scroll container) while the option list scrolls beneath.
   Global for the same reason as the other .mcq-* styles: parts of the header
   are RenderTreeBuilder output that Blazor CSS scoping does not reach.
   =========================== */

.mcq-pinned-header {
    position: sticky;
    top: 0;
    z-index: 5;
    background: var(--player-bg, #fff);
    padding-bottom: 0.35rem;
    /* Soft bottom fade so options sliding underneath read as "behind" the question. */
    box-shadow: 0 10px 10px -8px rgba(0, 0, 0, 0.3);
    margin-bottom: 0.8rem;
}

/* Inside the runner, .player-content-area (the scroll container) has a padding-top; a
   plain top:0 would stick the header below that padding, leaving a see-through strip
   where options show above the question. Pull the header up over the padding (negative
   top + margin) and give back the same amount as internal padding, so the resting
   layout is unchanged and the stuck header hugs the visible top with no gap. Scoped to
   the player because non-runner hosts (e.g. the /schema catalogue) have no such padding
   and the negative margin would overlap whatever precedes the question. Keep in sync
   with the .player-content-area padding-top clamp in PlayerShell.razor.css. */
.player-content-area .mcq-pinned-header {
    top: calc(-1 * clamp(1.2rem, 2.1vw, 2.4rem));
    margin-top: calc(-1 * clamp(1.2rem, 2.1vw, 2.4rem));
    padding-top: clamp(1.2rem, 2.1vw, 2.4rem);
}

.mcq-selected-strip {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.1rem 0 0.75rem;
}

.mcq-selected-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    max-width: min(28rem, 90vw);
    padding: 0.3rem 0.85rem;
    border: 2px solid var(--player-accent, #1cb6c9);
    border-radius: 1.4rem;
    background: var(--player-accent, #1cb6c9);
    color: #fff;
    font-size: calc(clamp(0.95rem, 1.1vw, 1.25rem) * var(--runner-font-scale, 1));
    line-height: 1.3;
    cursor: pointer;
    transition: filter 0.12s;
}

.mcq-selected-chip:hover:not(:disabled) {
    filter: brightness(0.9);
}

.mcq-selected-chip:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.mcq-selected-chip:focus-visible {
    outline: 2px solid var(--player-accent, #1cb6c9);
    outline-offset: 2px;
}

.mcq-chip-letter {
    font-weight: 700;
}

.mcq-chip-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.mcq-chip-x {
    font-weight: 700;
    font-size: 1.15em;
    line-height: 1;
    opacity: 0.85;
}

@media (prefers-reduced-motion: reduce) {
    .mcq-selected-chip {
        transition: none;
    }
}

/* ===========================
   True/False row layout
   Global so flex layout is not blocked by Blazor CSS scoping.
   =========================== */

.tf-list {
    margin-top: 0.5rem;
}

.tf-row {
    display: flex !important;
    align-items: center;
    gap: 1.5rem;
    padding: 0.85rem 0;
    border-bottom: 1px solid color-mix(in srgb, var(--player-text, #111) 10%, transparent);
}

.tf-row:last-child {
    border-bottom: none;
}

.tf-statement {
    flex: 1 1 0;
    min-width: 0;
    font-size: calc(clamp(1.35rem, 1.55vw, 1.9rem) * var(--runner-font-scale, 1));
    white-space: normal;
}

.tf-buttons {
    display: flex;
    gap: 0.5rem;
    flex-shrink: 0;
}

.tf-choice-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    min-height: 2.8rem;
    padding: 0.45rem 1.1rem 0.45rem 0.85rem;
    border: 2px solid var(--player-option-border, #444);
    border-radius: 0;
    background: transparent;
    color: var(--player-text, #111);
    font-size: calc(clamp(1rem, 1.15vw, 1.3rem) * var(--runner-font-scale, 1));
    font-weight: 400;
    cursor: pointer;
    transition: border-color 0.12s, color 0.12s, background 0.12s;
    white-space: nowrap;
}

.tf-choice-btn::before {
    content: '';
    flex-shrink: 0;
    width: 1rem;
    height: 1rem;
    border: 2px solid currentColor;
    border-radius: 50%;
    transition: background 0.12s, border-color 0.12s;
}

.tf-choice-btn:hover:not(:disabled) {
    border-color: var(--player-accent, #1cb6c9);
    color: var(--player-accent, #1cb6c9);
}

.tf-selected {
    border-color: var(--player-accent, #1cb6c9);
    color: var(--player-accent, #1cb6c9);
}

.tf-selected::before {
    background: var(--player-accent, #1cb6c9);
    border-color: var(--player-accent, #1cb6c9);
}

.tf-choice-btn:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.tf-choice-btn:focus-visible {
    outline: 2px solid var(--player-accent, #1cb6c9);
    outline-offset: 2px;
}

@media (max-width: 480px) {
    .tf-row {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.6rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    .tf-choice-btn {
        transition: none;
    }
}

/* ===========================
   ShortAnswer input font size
   !important ensures this wins over Bootstrap form-control (1rem)
   and any stale Blazor bundle values.
   Matches the stem clamp formula and respects --runner-font-scale.
   =========================== */

.shortanswer-textarea {
    font-size: calc(clamp(1.25rem, 1.55vw, 1.9rem) * var(--runner-font-scale, 1)) !important;
}

.shortanswer-line {
    font-size: calc(clamp(1.15rem, 1.35vw, 1.6rem) * var(--runner-font-scale, 1)) !important;
}

/* ===========================
   Runner integrity: question content is non-selectable.
   Selecting a word is the doorway to the OS dictionary/search popup
   (iPad long-press "Look Up", macOS right-click/force-click, Windows
   selection search). No selection → no popup. Answer inputs are
   re-enabled below — students must be able to select their own typing
   (Safari otherwise blocks caret selection under a non-select parent).
   Scoped to .player-content-area so admin preview surfaces (editor
   Inspect/Preview tabs, StudentView) keep normal text selection.
   =========================== */

.player-content-area .interaction-switch {
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
}

.player-content-area .interaction-switch input,
.player-content-area .interaction-switch textarea {
    -webkit-user-select: text;
    user-select: text;
}

/* ═══════════════════════════════════════════
   Likert / RadioSelect scale
   In app.css so changes take effect on hard-refresh without rebuild.
   Mirrors InteractionSwitchRenderer.razor.css — remove from here once
   the scoped bundle is rebuilt and stable.
   ═══════════════════════════════════════════ */

.likert-scale-row {
    position: relative;
    display: flex !important;
    justify-content: space-between;
    align-items: flex-start;
    width: 100%;
    max-width: 720px;
    margin: 1.75rem auto 2.5rem !important;
    padding: 0 0.5rem;
}

.likert-track {
    position: absolute;
    top: 0.7rem;
    left: 1.5rem;
    right: 1.5rem;
    height: 2px;
    background: var(--player-option-border, #444);
    pointer-events: none;
    z-index: 0;
}

.likert-option {
    position: relative;
    z-index: 1;
    display: flex !important;
    flex-direction: column;
    align-items: center;
    gap: 0.55rem;
    background: none !important;
    border: none !important;
    padding: 0 !important;
    cursor: pointer;
    flex: 0 0 auto;
    min-width: 3rem;
    transition: opacity 0.15s;
}

.likert-dot {
    display: block;
    width: 1.4rem;
    height: 1.4rem;
    border-radius: 50%;
    border: 2px solid var(--player-option-border, #444);
    background: var(--player-bg, #f0f0f0);
    transition: background 0.2s ease, border-color 0.2s ease, transform 0.15s ease;
}

.likert-label {
    font-size: calc(clamp(0.8rem, 0.95vw, 1.05rem) * var(--runner-font-scale, 1));
    color: var(--player-text, #111);
    text-align: center;
    white-space: normal;
    max-width: 5.5rem;
    line-height: 1.25;
    transition: color 0.2s ease;
}

.likert-option:hover:not(:disabled) .likert-dot {
    border-color: var(--player-accent, #1cb6c9);
    transform: scale(1.15);
}

.likert-option:hover:not(:disabled) .likert-label {
    color: var(--player-accent, #1cb6c9);
}

.likert-option.selected .likert-dot {
    background: var(--player-accent, #1cb6c9) !important;
    border-color: var(--player-accent, #1cb6c9);
    transform: scale(1.3);
}

.likert-option.selected .likert-label {
    color: var(--player-accent, #1cb6c9);
    font-weight: 600;
}

.likert-option:focus-visible .likert-dot {
    outline: 2px solid var(--player-accent, #1cb6c9);
    outline-offset: 3px;
}

.likert-option:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

/* ═══════════════════════════════════════════
   Assessment Completion Screen
   ═══════════════════════════════════════════ */

.assessment-complete-screen {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 80vh;
    padding: 2rem;
}

.assessment-complete-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    max-width: 520px;
    width: 100%;
    color: var(--player-text, #111);
    animation: complete-fade-in 0.5s ease both;
}

@keyframes complete-fade-in {
    from { opacity: 0; transform: translateY(1.5rem); }
    to   { opacity: 1; transform: translateY(0); }
}

/* Animated SVG check */
.complete-icon-wrap {
    margin-bottom: 2rem;
}

.complete-checkmark {
    width: 6rem;
    height: 6rem;
    stroke: var(--player-accent, #1cb6c9);
    stroke-width: 2.5;
}

.complete-checkmark-circle {
    stroke: var(--player-accent, #1cb6c9);
    stroke-dasharray: 166;
    stroke-dashoffset: 166;
    animation: complete-stroke 0.5s cubic-bezier(0.65, 0, 0.45, 1) 0.1s forwards;
}

.complete-checkmark-path {
    stroke: var(--player-accent, #1cb6c9);
    stroke-dasharray: 48;
    stroke-dashoffset: 48;
    animation: complete-stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.55s forwards;
}

@keyframes complete-stroke {
    to { stroke-dashoffset: 0; }
}

/* Typography */
.complete-heading {
    font-size: clamp(1.8rem, 3vw, 2.6rem);
    font-weight: 300;
    letter-spacing: 0.02em;
    margin: 0 0 0.5rem;
    color: var(--player-text, #111);
}

.complete-assessment-name {
    font-size: clamp(1rem, 1.4vw, 1.3rem);
    color: var(--player-accent, #1cb6c9);
    font-weight: 500;
    margin: 0 0 2rem;
}

/* Stats block */
.complete-stats {
    display: flex;
    gap: 3rem;
    justify-content: center;
    margin-bottom: 2rem;
}

.complete-stat-block {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.2rem;
}

.complete-stat-value {
    font-size: clamp(2rem, 3.5vw, 3rem);
    font-weight: 600;
    color: var(--player-accent, #1cb6c9);
    line-height: 1;
}

.complete-stat-label {
    font-size: 0.9rem;
    color: color-mix(in srgb, var(--player-text, #111) 65%, transparent);
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

.complete-message {
    color: color-mix(in srgb, var(--player-text, #111) 70%, transparent);
    font-size: 1rem;
    margin-bottom: 2.5rem;
}

/* Return home button */
.complete-home-btn {
    display: inline-block;
    padding: 0.85rem 2.5rem;
    border: 2px solid var(--player-accent, #1cb6c9);
    border-radius: 0.5rem;
    color: var(--player-accent, #1cb6c9);
    font-size: 1rem;
    font-weight: 500;
    text-decoration: none;
    letter-spacing: 0.04em;
    transition: background 0.2s ease, color 0.2s ease;
}

.complete-home-btn:hover {
    background: var(--player-accent, #1cb6c9);
    color: #051217;
    text-decoration: none;
}

/* ═══════════════════════════════════════════
   PullDown (dropdown) — themed select
   ═══════════════════════════════════════════ */

.pulldown-select {
    display: block;
    width: 100%;
    max-width: 680px;
    margin: 0.5rem auto 0;
    padding: 1rem 3rem 1rem 1.4rem;
    font-size: calc(clamp(1.15rem, 1.4vw, 1.6rem) * var(--runner-font-scale, 1));
    font-family: inherit;
    color: var(--player-text);
    background-color: var(--player-option-bg, transparent);
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%231cb6c9' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
    background-repeat: no-repeat;
    background-position: right 1rem center;
    background-size: 1.2rem;
    border: 2px solid var(--player-option-border);
    border-radius: 0.5rem;
    appearance: none;
    -webkit-appearance: none;
    cursor: pointer;
    transition: border-color 0.15s ease;
    line-height: 1.4;
}

.pulldown-select:focus {
    outline: none;
    border-color: var(--player-accent, #1cb6c9);
    box-shadow: 0 0 0 3px rgba(28, 182, 201, 0.25);
}

.pulldown-select:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.pulldown-select option {
    background-color: var(--player-option-bg, #fff);
    color: var(--player-option-text, #111);
}

/* ── AutoComplete (Text.AutoComplete PartCode) ──────────────────────────── */

.autocomplete-wrap {
    position: relative;
}

.autocomplete-input {
    font-size: calc(clamp(1.1rem, 1.3vw, 1.5rem) * var(--runner-font-scale, 1)) !important;
    background-color: var(--player-option-bg, #fff) !important;
    color: var(--player-text, #111) !important;
    border: 2px solid var(--player-option-border, #444) !important;
    border-radius: 8px;
    padding: 0.55rem 0.85rem;
    width: 100%;
    transition: border-color 0.15s ease;
}

.autocomplete-input:focus {
    outline: none !important;
    border-color: var(--player-accent, #1cb6c9) !important;
    box-shadow: 0 0 0 3px rgba(28, 182, 201, 0.2) !important;
}

/* A type-ahead blank woven into a sentence run (the Lord's Prayer cloze): sits in the
   text flow at blank width instead of a full-width block, dropdown wide enough to read. */
.autocomplete-wrap.autocomplete-inline {
    display: inline-block;
    vertical-align: baseline;
    /* Sized so the longest real answers fit without visually truncating as the student
       types — "Anointing of the Sick" (Y9 sacraments cloze) is the practical worst case
       and overflowed the previous 8/14vw/12rem ramp. Kept as a clamp so narrow tablets
       still get a usable blank and wide desktops don't run the box away from the text. */
    width: clamp(11rem, 19vw, 17rem);
    margin: 0 0.15rem;
}

.autocomplete-wrap.autocomplete-inline .autocomplete-input {
    padding: 0.35rem 0.55rem;
    border-radius: 6px;
}

/* AutoComplete in a Compound.GridN cell.
   The clamp above is sized for a blank sitting in running text. Inside a grid track that shape is
   wrong: it left-aligns a narrow box under a full-width heading, so the columns visibly stop
   lining up (Y7 Knowledge a10, the typed sacraments grid). Cells fill their track, mirroring the
   .compound-blank* override in InteractionSwitchRenderer.razor.css.

   This rule has to live in the GLOBAL sheet, not in that scoped one. RenderAutoComplete builds
   the wrapper with RenderTreeBuilder, and the Razor compiler only stamps its CSS-isolation scope
   attribute onto elements written as .razor markup — so the same rule in the scoped file compiles
   to `…[b-xxxxxxxx]` and matches nothing. (.shortanswer-line is the standing example: the scoped
   copy is dead and the live styling comes from this file.)

   The margin reset matters as much as the width — 0 0.15rem would otherwise shave each cell
   narrower than its track and reintroduce the misalignment. */
.interaction-switch .compound-grid .autocomplete-wrap.autocomplete-inline {
    width: 100%;
    max-width: 100%;
    margin: 0;
}

.autocomplete-wrap.autocomplete-inline .autocomplete-dropdown {
    /* Track the blank's max width — a suggestion list narrower than the box it hangs
       off reads as broken, and these option lists carry long multi-word entries. */
    min-width: 17rem;
    right: auto;
}

.autocomplete-dropdown {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 200;
    list-style: none;
    margin: 4px 0 0;
    padding: 4px 0;
    background-color: var(--player-option-bg, #fff);
    border: 1.5px solid var(--player-accent, #1cb6c9);
    border-radius: 8px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
    overflow: hidden;
}

.autocomplete-option {
    padding: 0.55rem 1rem;
    font-size: calc(clamp(1.05rem, 1.25vw, 1.4rem) * var(--runner-font-scale, 1));
    color: var(--player-text, #111);
    cursor: pointer;
    transition: background-color 0.1s ease;
}

.autocomplete-option:hover,
.autocomplete-option:focus {
    background-color: rgba(0, 188, 212, 0.12);
    color: var(--player-text, #111);
}

/* ── Schedule editor: Syncfusion date-picker popup styling ──
   The schedule dialog uses SfDatePicker (with CssClass="schedule-editor-datepicker")
   so we can highlight the in-range / selectable date cells. The popup
   renders outside the dialog's component scope (Syncfusion uses a body-
   level portal), so these rules live in global app.css rather than the
   ScheduleEditorDialog.razor.css.

   .e-cell is each day cell. .e-disabled marks days outside the Min/Max
   range. The affordance must follow the universal convention: grey =
   unavailable. An earlier version tinted the SELECTABLE cells grey to
   make them "pop", which read as the exact opposite — the whole valid
   window looked disabled while out-of-window days looked pickable. So:
   in-window days get a soft accent-blue wash (clearly "live"), and
   out-of-window days are explicitly muted. */
.e-datepicker.e-popup-open .e-calendar .e-cell:not(.e-disabled):not(.e-other-month):not(.e-selected):not(.e-today):not(.sched-today-local) {
    background: #e7f1ff;
}

.e-datepicker.e-popup-open .e-calendar .e-cell:not(.e-disabled):not(.e-other-month):hover {
    background: #cfe2ff;
}

/* Days outside the allocation window: unmistakably inert — pale grey
   text on a plain background, no hover response. (Syncfusion's default
   disabled dimming is too subtle next to the tinted in-range cells.) */
.e-datepicker.e-popup-open .e-calendar .e-cell.e-disabled span.e-day {
    color: #b8b8b8;
    background: transparent;
}

/* Re-point the "today" outline to the SCHOOL's local day. Syncfusion marks
   today from the Blazor Server clock (UTC on Azure) and its per-component
   ServerTimezoneOffset override is obsolete in our version — so the dialog's
   OnRenderDayCell tags the school-local cell with .sched-today-local and we
   move the outline here. (The only SfDatePicker in the app is this schedule
   picker, so .e-datepicker scopes effectively — matching the rules above.)

   Suppress Syncfusion's native today outline on any cell that ISN'T the
   school-local today (i.e. the stray UTC day in the early-WA-morning window). */
.e-datepicker.e-popup-open .e-calendar .e-cell.e-today:not(.sched-today-local) span.e-day {
    border-color: transparent;
    box-shadow: none;
}

/* Draw the today outline on the school-local cell (mirrors the theme's
   round today ring). */
.e-datepicker.e-popup-open .e-calendar .e-cell.sched-today-local span.e-day {
    border: 1px solid var(--admin-accent, #0d6efd);
    box-shadow: inset 0 0 0 1px var(--admin-accent, #0d6efd);
    border-radius: 50%;
}

/* ──────────────────────────────────────────────────────────────────────────
   Line style tokens (sequence.Line.Style → cmr-*)
   Curated, documented vocabulary the runner renderers append verbatim onto each
   line/stem element. Single source of the token list: Contracts/PlanFlow/
   LineStyleCatalog.cs; reference: docs/InteractionLineStyles.md. Global (not
   scoped) so they apply across every runner renderer.
   ────────────────────────────────────────────────────────────────────────── */

/* Alignment — !important beats the runner's non-!important text-align:center. */
.cmr-left   { text-align: left !important; }
.cmr-center { text-align: center !important; }
.cmr-right  { text-align: right !important; }

/* Emphasis — independent; combine freely. */
.cmr-bold      { font-weight: 700 !important; }
.cmr-italic    { font-style: italic !important; }
.cmr-underline { text-decoration: underline !important; }

/* Font size — rebinds the per-line --runner-font-scale that the renderers'
   font-size calc() already consumes. It COMPOUNDS with the student's A+/A- zoom
   by multiplying the untouched --user-font-scale mirror (emitted by PlayerShell)
   rather than replacing it — so a "larger" stem still grows when the student
   enlarges text. Outside the runner (e.g. editor preview) --user-font-scale is
   unset, so the fallback 1 yields the plain 1.3 / 0.8 factor. Reading
   --user-font-scale (not --runner-font-scale) avoids a self-referential cycle. */
.cmr-larger  { --runner-font-scale: calc(var(--user-font-scale, 1) * 1.3); }
.cmr-smaller { --runner-font-scale: calc(var(--user-font-scale, 1) * 0.8); }

/* Block treatments. */
.cmr-quote {
    font-style: italic;
    border-left: 3px solid var(--player-accent, #4ec3e0);
    padding-left: 1rem;
    margin-left: 0.5rem;
    text-align: left !important;
}

.cmr-callout {
    border: 1px solid var(--player-option-border, #888);
    border-radius: 0.5rem;
    padding: 0.75rem 1rem;
    background: rgba(255, 255, 255, 0.04);
}

/* Text colour — values are tuned to stay readable on the runner's dark theme.
   Keep in sync with the Swatch hexes in LineStyleCatalog.cs. */
.cmr-red    { color: #ff6b6b !important; }
.cmr-gold   { color: #ffce5a !important; }
.cmr-green  { color: #4fd18b !important; }
.cmr-blue   { color: #4ec3e0 !important; }
.cmr-purple { color: #b794f6 !important; }

