/* ───────────────────────────────────────────────────────────────────────────
   SmartSuli Admin — Design System
   Layered on top of Bootstrap 5. Extends Bootstrap — does not replace it.
   ─────────────────────────────────────────────────────────────────────────── */

/* ── Tokens ─────────────────────────────────────────────────────────────────
   A warm premium light theme. Deep ink text, bright premium-blue primary,
   mint/cyan positive accents, pink/lavender emotional accents, amber for warmth.
   Older token names are kept (mapped to the new system) so existing ss-* classes
   keep working without edits.
   ─────────────────────────────────────────────────────────────────────────── */
:root {
    /* Surfaces & ink */
    --ss-bg:               #F7F3EA;
    --ss-bg-alt:           #EEF7FF;
    --ss-surface:          #FFFFFF;
    --ss-surface-warm:     #FFF8ED;
    --ss-surface-mint:     #ECFBF7;
    --ss-surface-lavender: #F3F0FF;
    --ss-surface-soft:     #F4EFE4;
    --ss-ink:              #171A21;
    --ss-text:             #171A21;
    --ss-muted:            #667085;
    --ss-text-muted:       #667085;
    --ss-border:           rgba(23, 26, 33, 0.10);
    --ss-border-light:     rgba(23, 26, 33, 0.06);

    /* Brand + accents */
    --ss-primary:          #3B5BFF;
    --ss-primary-strong:   #2E48D8;
    --ss-primary-soft:     #E8ECFF;
    --ss-accent:           #3B5BFF;          /* legacy alias → primary */
    --ss-accent-soft:      #E8ECFF;
    --ss-accent-border:    #C2CCFF;
    --ss-accent-pink:      #FF4DA6;
    --ss-accent-cyan:      #18C7C2;
    --ss-accent-yellow:    #FFB020;
    --ss-accent-lavender:  #7C5CFF;

    /* Semantic */
    --ss-success:          #18A058;
    --ss-green:            #18A058;
    --ss-green-soft:       #E4F6EC;
    --ss-green-border:     #B4E3C8;
    --ss-warning:          #B7791F;
    --ss-amber:            #B7791F;
    --ss-amber-soft:       #FFF4DD;
    --ss-amber-border:     #F3D89A;
    --ss-danger:           #E5484D;
    --ss-red:              #D03439;
    --ss-red-soft:         #FDECEC;
    --ss-red-border:       #F6BFC1;

    /* Geometry */
    --ss-radius:           0.65rem;
    --ss-radius-sm:        0.45rem;
    --ss-radius-lg:        1rem;
    --ss-shadow-xs:        0 1px 2px rgba(23,26,33,0.05);
    --ss-shadow-sm:        0 1px 4px rgba(23,26,33,0.06), 0 1px 2px rgba(23,26,33,0.04);
    --ss-shadow-card:      0 8px 24px rgba(23,26,33,0.06);
    --ss-shadow-soft:      0 14px 40px rgba(23,26,33,0.08);

    /* Layering — keep page content low, lift the navbar + its dropdowns above it.
       Page heroes/cards never set z-index above 1, so the navbar (1030) and any
       dropdown it owns (2000) always paint on top. */
    --ss-z-sticky-action:  1020;
    --ss-z-navbar:         1030;
    --ss-z-dropdown:       2000;
}

/* ADR-0015 admin paper grading surface. The SVG layer contains no bitmap backing store; the
   retained normalized documents are the only per-page ink memory.
   Full-bleed: breaks out of _Layout.cshtml's Bootstrap .container (max-width + gutters) so the
   whiteboard canvas spans the entire viewport width, not just the admin content column. */
/* dev-planning/26: the teacher grading/companion paper view is now full-bleed like the student
   answer sheet — the paper fills the ENTIRE viewport and the floating toolbar sits on top, with no
   admin chrome and no reachable page background (the repeated dark-band bug came from body/navbar
   background showing wherever this shell didn't paint). Fixed + inset:0 over the sticky admin navbar
   (z 1030) makes the paper the whole page; navigation is via the toolbar's own back button. */
.ss-grading-paper-shell {
    position: fixed;
    inset: 0;
    z-index: 1035;
    overflow: hidden;
    background: #fff;
}

/* Premium whiteboard toolbar: a single floating glass pill, centered above the canvas, no
   header/title row above it — matches the student .ss-paper-toolbar's approved look (see
   below) rather than the previous sticky/left-aligned bar with an inline title block. */
.ss-grading-paper-toolbar {
    position: fixed;
    /* dev-planning/26: the full-bleed shell now covers the admin navbar, so the toolbar no longer
       needs to clear it — float near the top like the student .ss-paper-toolbar. */
    top: 1rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1050;
    display: flex;
    align-items: center;
    /* A wrapping flex container's shrink-to-fit width (used here since a fixed-position element
       has no explicit width) is computed from its widest *line*, not its full single-line
       content — that can make the browser wrap well before content actually needs to. This bar
       also carries more controls than .ss-paper-toolbar below (cadence select, peer-notes
       toggle, save status), so instead of chasing that: never wrap, and let genuinely too-narrow
       windows scroll the pill horizontally rather than break its shape. */
    flex-wrap: nowrap;
    overflow-x: auto;
    justify-content: center;
    gap: .3rem;
    max-width: min(96rem, calc(100vw - 1.5rem));
    padding: .35rem .5rem;
    border: 1px solid rgba(100, 116, 139, .18);
    border-radius: 999px;
    background: var(--ss-glass-bg-strong, rgba(255, 255, 255, .88));
    box-shadow: 0 10px 30px rgba(15, 23, 42, .12);
    -webkit-backdrop-filter: saturate(180%) blur(20px);
    backdrop-filter: saturate(180%) blur(20px);
}

.ss-grading-paper-tools, .ss-grading-paper-zoom { display: flex; align-items: center; gap: .3rem; }
.ss-paper-page-nav { display: inline-flex; align-items: center; gap: .25rem; white-space: nowrap; }
.ss-paper-page-input {
    width: 3.35rem;
    min-height: 1.9rem;
    padding: .15rem .25rem;
    border: 1px solid var(--ss-border, #d8dce6);
    border-radius: .55rem;
    color: var(--ss-ink, #20232a);
    background: var(--ss-surface, #fff);
    font-size: .78rem;
    font-weight: 700;
    line-height: 1;
    text-align: center;
}
.ss-paper-page-total { color: var(--ss-muted, #667085); font-size: .76rem; font-weight: 700; }
.ss-grading-paper-tools .is-active { color: #fff; background: #4f5fff; border-color: #4f5fff; }
.ss-ink-color { width: 1.65rem; height: 1.65rem; padding: 0; border: 2px solid #fff; box-shadow: 0 0 0 1px #aab2c3; border-radius: 50%; }
.ss-ink-color[data-color="teacher-red"] { background: #dc3545; }
.ss-ink-color[data-color="teacher-dark-red"] { background: #8b1e2d; }
.ss-ink-color[data-color="teacher-blue"] { background: #2457d6; }
.ss-ink-color[data-color="teacher-black"] { background: #171a21; }
.ss-ink-color.is-active { box-shadow: 0 0 0 3px #4f5fff; }
.ss-ink-size { width: auto; min-width: 6.5rem; }

/* Icon-only toolbar dropdown (line thickness, poll cadence): the real <select> stays fully
   functional (click, keyboard, screen reader) but invisible; the face shows only the icon
   matching its current value. Same technique as .ss-paper-choice-native/-display. */
.ss-toolbar-select { position: relative; width: 1.9rem; height: 1.9rem; flex: 0 0 auto; }
.ss-toolbar-select-native { position: absolute; inset: 0; opacity: 0; cursor: pointer; border: 0; }
.ss-toolbar-select-face {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    border: 1px solid var(--ss-border);
    background: var(--ss-surface);
    color: var(--ss-ink);
    pointer-events: none;
}
.ss-toolbar-select:hover .ss-toolbar-select-face { border-color: var(--ss-primary); color: var(--ss-primary); }
.ss-toolbar-select-icon { display: none; width: 1.05rem; height: 1.05rem; }
.ss-toolbar-select-icon.is-active { display: block; }

/* Full-bleed whiteboard: the toolbar above floats over this (fixed, out of flow), so this only
   needs to clear the sticky navbar, not reserve separate toolbar space. A faint grid on white
   reads as "whiteboard," not "grey card" — matches the student .ss-sheet-mid paper background
   below so every ink surface in the app looks like one system. */
/* ADR-0015 Phase 6: the LiveCompanion link-copy row is genuine normal-flow content (unlike the
   pages canvas below, which deliberately floats behind the fixed toolbar) — needs the same top
   clearance as .ss-paper-toast-stack's own admin-shell override so it isn't hidden under the
   floating pill. A one-time copy at lesson start is fine to leave at the top of the page; the
   per-exercise activate/reveal controls used to live here too, but that made them scroll out of
   view on any document past one screen — see .ss-companion-tasks-popover below for the fix. */
.ss-companion-panel {
    margin-top: 8rem;
    padding: 0 1rem;
}

/* Bug fix (live report): activate/reveal used to be normal-flow content directly below the fixed
   toolbar, so scrolling into a long PDF to review the relevant exercise scrolled the controls out
   of view entirely — the same "content invisible below N stacked pages" problem
   .ss-paper-toast-stack and .ss-paper-toolbar's own comments already document and fix the same way.
   Toggled from #companionTasksToggle in the toolbar; JS positions it against that toolbar's actual
   rendered rect (not a fixed offset) so it tracks correctly whether the toolbar sits at the top
   (desktop) or the bottom (mobile — see the @media override on .ss-grading-paper-toolbar). */
.ss-companion-tasks-popover {
    position: fixed;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1049;
    max-width: min(96rem, calc(100vw - 1.5rem));
    max-height: min(60vh, 30rem);
    overflow-y: auto;
    padding: .75rem 1rem;
    border: 1px solid rgba(100, 116, 139, .18);
    border-radius: 1.1rem;
    background: var(--ss-glass-bg-strong, rgba(255, 255, 255, .92));
    box-shadow: 0 16px 40px rgba(15, 23, 42, .18);
    -webkit-backdrop-filter: saturate(180%) blur(20px);
    backdrop-filter: saturate(180%) blur(20px);
}
.ss-companion-tasks-popover[hidden] { display: none; }
.ss-companion-tasks-popover-label {
    display: block;
    font-size: .72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--ss-muted, #667085);
    margin-bottom: .5rem;
}

/* The active exercise's stopwatch on the full-bleed teacher paper view. Bottom-left is the only
   corner nothing else claims (toolbar top, toast stack top-centre, page nav inside the toolbar),
   and it keeps the clock out of the way of the paper the teacher is writing on. Same glass
   vocabulary as the popover above; the dot pulses only while the timer is actually running, so a
   stopped clock reads as stopped at a glance. */
.ss-companion-timer {
    position: fixed;
    left: 1rem;
    bottom: 1rem;
    z-index: 1045;
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    padding: .35rem .8rem;
    border: 1px solid rgba(100, 116, 139, .18);
    border-radius: 999px;
    background: var(--ss-glass-bg-strong, rgba(255, 255, 255, .92));
    box-shadow: 0 10px 26px rgba(15, 23, 42, .16);
    -webkit-backdrop-filter: saturate(180%) blur(20px);
    backdrop-filter: saturate(180%) blur(20px);
    font-variant-numeric: tabular-nums;
    pointer-events: none;
}
.ss-companion-timer-value {
    font-size: .95rem;
    font-weight: 700;
    letter-spacing: .02em;
}
.ss-companion-timer-dot {
    width: .45rem;
    height: .45rem;
    border-radius: 50%;
    background: rgba(100, 116, 139, .45);
}
.ss-companion-timer.is-running .ss-companion-timer-dot {
    background: #2bb673;
    box-shadow: 0 0 6px rgba(43, 182, 115, .8);
    animation: ss-companion-timer-pulse 2s ease-in-out infinite;
}
@keyframes ss-companion-timer-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: .35; }
}
@media (prefers-reduced-motion: reduce) {
    .ss-companion-timer.is-running .ss-companion-timer-dot { animation: none; }
}
@media (max-width: 720px) {
    /* Same breakpoint at which .ss-grading-paper-toolbar moves to the bottom of the viewport —
       lift the clock above it instead of stacking underneath it. */
    .ss-companion-timer {
        left: .65rem;
        bottom: calc(4.75rem + env(safe-area-inset-bottom));
    }
}

/* Passive "something is live to the student" signal on the toolbar toggle itself, visible even
   while the popover is closed — the previous design had no equivalent once scrolled away. */
.ss-paper-toolbar-icon.has-revealed { position: relative; }
.ss-paper-toolbar-icon.has-revealed::after {
    content: '';
    position: absolute;
    top: .15rem;
    right: .15rem;
    width: .45rem;
    height: .45rem;
    border-radius: 50%;
    background: #e6a23c;
    box-shadow: 0 0 6px rgba(230, 162, 60, .8);
}

/* Lesson whiteboard — a blank ink canvas, deliberately not PDF-backed (see
   lesson-whiteboard-renderer.mjs). Opens in its own tab with no app chrome at all (no admin
   navbar, no logo, no back link — closing/switching the tab is "going back"), so unlike the
   grading-paper surface above it never needs to clear a navbar and can go truly edge-to-edge. */
.ss-whiteboard-shell {
    position: fixed;
    inset: 0;
    background: #fff;
    overflow: hidden;
}

.ss-whiteboard-canvas-area {
    position: absolute;
    inset: 0;
    overflow: auto;
    background:
        repeating-linear-gradient(0deg, transparent, transparent 39px, rgba(23, 26, 33, .035) 40px),
        repeating-linear-gradient(90deg, transparent, transparent 39px, rgba(23, 26, 33, .035) 40px),
        #fff;
    overscroll-behavior: contain;
}

.ss-whiteboard-page {
    box-shadow: 0 1px 4px rgba(23, 26, 33, .18);
    margin: 1rem auto;
}

/* Uploaded images (lesson-whiteboard-images.mjs) — a positioned layer below the ink SVG. Each image
   is a real <button> so exam-ink-engine.mjs's isInteractiveTarget check gives it free pass-through
   from ink drawing (see the comment in lesson-whiteboard-images.mjs); blank page area still draws
   ink normally. Small always-visible handles (not a separate "select" mode) keep this minimal and
   touch-friendly — no hover-only affordances that iPad users would never discover. */
.ss-whiteboard-image {
    position: absolute;
    touch-action: none;
}
.ss-whiteboard-image-drag {
    display: block;
    width: 100%;
    padding: 0;
    border: 2px solid rgba(79, 95, 255, .0);
    border-radius: .25rem;
    background: none;
    cursor: grab;
    touch-action: none;
    transition: border-color .12s ease;
}
.ss-whiteboard-image-drag:hover,
.ss-whiteboard-image-drag:focus-visible {
    border-color: rgba(79, 95, 255, .55);
}
.ss-whiteboard-image-drag img {
    display: block;
    width: 100%;
    height: auto;
    pointer-events: none;
    -webkit-user-drag: none;
}
.ss-whiteboard-image-delete {
    position: absolute;
    top: -.6rem;
    right: -.6rem;
    width: 1.5rem;
    height: 1.5rem;
    line-height: 1.5rem;
    padding: 0;
    border: 1px solid rgba(220, 53, 69, .5);
    border-radius: 999px;
    background: #fff;
    color: #dc3545;
    font-size: 1rem;
    box-shadow: 0 1px 4px rgba(23, 26, 33, .2);
    touch-action: none;
}
.ss-whiteboard-image-resize-handle {
    position: absolute;
    right: -.4rem;
    bottom: -.4rem;
    width: 1.1rem;
    height: 1.1rem;
    padding: 0;
    border: 1px solid rgba(79, 95, 255, .55);
    border-radius: .2rem;
    background: #fff;
    cursor: nwse-resize;
    touch-action: none;
    box-shadow: 0 1px 4px rgba(23, 26, 33, .2);
}
/* Stage 2 (dev-planning/21): the *other* participant's images, shown read-only. No handles, and
   pointer-events: none so they never intercept ink drawing or block the own-layer image controls. */
.ss-whiteboard-image--readonly {
    pointer-events: none;
}
.ss-whiteboard-image--readonly img {
    display: block;
    width: 100%;
    height: auto;
    -webkit-user-drag: none;
}

/* Stage 2 (dev-planning/21) exam paper image layer: exam-paper-renderer.mjs now sets the per-page
   overlay to pointer-events: none so blank overlay area passes clicks through to the image controls
   sitting below it (same strategy as the lesson-whiteboard renderer). The positioned answer-field
   wraps must therefore re-assert pointer-events: auto so their inputs/widgets keep receiving events.
   The read-only student-answer mirror on the live companion (.ss-paper-field-wrap--mirror) keeps its
   own later pointer-events: none, so it is unaffected. */
.ss-paper-field-wrap {
    pointer-events: auto;
}

/* A floating glass pill, same look as .ss-grading-paper-toolbar, but with no navbar to clear
   (top: .75rem, not 4.25rem) since this page has none. iPad/Safari long-press-to-select bug fix:
   without these, a touch that starts on/near the toolbar can trigger the native text-selection
   drag handles (visible as blue circles + a selection outline around the whole bar) instead of
   being treated as a button tap — the ink canvas already got this treatment
   (createCombinedInkInputController sets it on the drawing surface), the toolbar chrome needs it
   too since it's a separate, always-touchable element floating over that surface. */
.ss-whiteboard-toolbar {
    position: fixed;
    top: max(.75rem, env(safe-area-inset-top));
    left: 50%;
    transform: translateX(-50%);
    z-index: 1050;
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    overflow-x: auto;
    justify-content: center;
    gap: .3rem;
    max-width: min(96rem, calc(100vw - 1.5rem));
    padding: .35rem .5rem;
    border: 1px solid rgba(100, 116, 139, .18);
    border-radius: 999px;
    background: var(--ss-glass-bg-strong, rgba(255, 255, 255, .88));
    box-shadow: 0 10px 30px rgba(15, 23, 42, .12);
    -webkit-backdrop-filter: saturate(180%) blur(20px);
    backdrop-filter: saturate(180%) blur(20px);
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
    touch-action: manipulation;
}
.ss-whiteboard-toolbar * {
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
}

.ss-grading-paper-pages {
    position: relative;
    /* dev-planning/26: fills the whole fixed full-bleed shell (was `100dvh - 4.25rem` to leave room
       under the admin navbar, which the shell now covers). The toolbar floats on top. */
    height: 100dvh;
    min-height: 32rem;
    overflow: auto;
    padding: 1rem;
    background:
        repeating-linear-gradient(0deg, transparent, transparent 39px, rgba(23, 26, 33, .035) 40px),
        repeating-linear-gradient(90deg, transparent, transparent 39px, rgba(23, 26, 33, .035) 40px),
        #fff;
    overscroll-behavior: contain;
}

.ss-grading-ink-layer,
.ss-published-teacher-ink-layer {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: 4;
    overflow: visible;
    pointer-events: none;
}
.ss-student-solution-ink-layer {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: 3;
    overflow: visible;
    pointer-events: none;
}
.ss-student-solution-ink-layer[hidden],
.ss-published-teacher-ink-layer[hidden] { display: none; }

/* Live participant presence (exam-companion-presence.mjs) — the other person's pointer and the
   slice of the page they can actually see. Sits above every ink layer on purpose: the pointer is
   meant to show through the work, the way it does in a shared canvas. Everything is sized against
   --ss-paper-oversample because the whole page stack is rasterized oversampled and CSS-scaled back
   down, so a plain px size would render at roughly half of what it says. */
.ss-presence-layer {
    position: absolute;
    inset: 0;
    z-index: 6;
    pointer-events: none;
}
.ss-presence-view {
    position: absolute;
    border: calc(1.5px * var(--ss-paper-oversample, 1)) dashed var(--ss-presence-color, #dc3545);
    border-radius: calc(.4rem * var(--ss-paper-oversample, 1));
    /* Barely-there tint: this frame can cover most of a page, so anything stronger competes with
       the student's own work instead of quietly saying "this is what they can see". */
    background: color-mix(in srgb, var(--ss-presence-color, #dc3545) 3.5%, transparent);
    opacity: .6;
    transition: left .5s ease, top .5s ease, width .5s ease, height .5s ease, opacity .3s ease;
}
.ss-presence-view.is-away {
    border-style: dotted;
    opacity: .3;
    background: none;
}
.ss-presence-view[hidden],
.ss-presence-cursor[hidden] { display: none; }

.ss-presence-cursor {
    position: absolute;
    display: flex;
    align-items: flex-start;
    gap: calc(.15rem * var(--ss-paper-oversample, 1));
    /* left/top are the pointer tip; nudge so the arrow point, not its bounding box, lands there. */
    margin: calc(-2px * var(--ss-paper-oversample, 1)) 0 0 calc(-2px * var(--ss-paper-oversample, 1));
    /* Presence arrives on a timer, so glide between samples instead of teleporting. */
    transition: left .55s linear, top .55s linear, opacity .3s ease;
}
.ss-presence-cursor.is-away { opacity: .4; }
.ss-presence-arrow {
    width: calc(1.15rem * var(--ss-paper-oversample, 1));
    height: calc(1.15rem * var(--ss-paper-oversample, 1));
    flex: none;
    fill: var(--ss-presence-color, #dc3545);
    stroke: #fff;
    stroke-width: 1.4;
    filter: drop-shadow(0 calc(1px * var(--ss-paper-oversample, 1)) calc(2px * var(--ss-paper-oversample, 1)) rgba(23, 26, 33, .35));
}
.ss-presence-cursor.is-drawing .ss-presence-arrow { animation: ss-presence-pulse 1.1s ease-in-out infinite; }
.ss-presence-name {
    max-width: calc(7rem * var(--ss-paper-oversample, 1));
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding: calc(.05rem * var(--ss-paper-oversample, 1)) calc(.3rem * var(--ss-paper-oversample, 1));
    border-radius: calc(.6rem * var(--ss-paper-oversample, 1));
    background: var(--ss-presence-color, #dc3545);
    color: #fff;
    font-size: calc(.62rem * var(--ss-paper-oversample, 1));
    font-weight: 600;
    line-height: 1.5;
    box-shadow: 0 calc(1px * var(--ss-paper-oversample, 1)) calc(3px * var(--ss-paper-oversample, 1)) rgba(23, 26, 33, .3);
}
@keyframes ss-presence-pulse {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.18); }
}
@media (prefers-reduced-motion: reduce) {
    .ss-presence-cursor,
    .ss-presence-view { transition: none; }
    .ss-presence-cursor.is-drawing .ss-presence-arrow { animation: none; }
}

.ss-grading-answer-overlay {
    z-index: 3;
    display: flex;
    align-items: center;
    gap: .25rem;
    min-width: 0;
    padding: .08rem .25rem;
    border: 1px solid rgba(79, 95, 255, .36);
    border-radius: .35rem;
    color: #1f2937;
    background: rgba(255, 255, 255, .82);
    font-size: calc(.76rem * var(--ss-paper-oversample, 1));
    line-height: 1.1;
    pointer-events: none;
}

.ss-grading-answer-overlay > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ss-grading-answer-overlay .ss-answer-light { flex: 0 0 auto; }

/* A colored LED, not a text pill — the full status still reaches screen readers via aria-label
   (aria-live announces an aria-label change same as a textContent change) and sighted users via
   the title tooltip. Shared by the student, admin-grading, and teacher-companion toolbars. */
.ss-save-led {
    display: inline-block;
    width: .6rem;
    height: .6rem;
    flex: 0 0 auto;
    border-radius: 50%;
    background: var(--ss-muted);
}
.ss-save-led[data-state="saved"] { background: var(--ss-success); }
.ss-save-led[data-state="saving"] { background: var(--ss-warning); }
.ss-save-led[data-state="error"] { background: var(--ss-danger); }

@media (max-width: 720px) {
    .ss-grading-paper-toolbar {
        top: auto;
        bottom: calc(.65rem + env(safe-area-inset-bottom));
        left: .65rem;
        right: .65rem;
        transform: none;
        max-width: none;
    }
    .ss-grading-paper-pages { height: 100dvh; padding: .3rem; }
}

/* ── Base ───────────────────────────────────────────────────────────────── */
html {
    font-size: 14px;
    position: relative;
    min-height: 100%;
}

@media (min-width: 768px) { html { font-size: 15px; } }

body {
    background-color: var(--ss-bg);
    color: var(--ss-text);
    margin-bottom: 60px;
}

/* Improved focus ring */
.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 var(--ss-accent);
}

.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;
}

/* ── Warm text-muted override ───────────────────────────────────────────── */
.text-muted { color: var(--ss-text-muted) !important; }

/* ── Navbar ─────────────────────────────────────────────────────────────── */
.ss-navbar {
    position: sticky;
    top: 0;
    z-index: var(--ss-z-navbar);
    background: rgba(255, 255, 255, 0.85) !important;
    backdrop-filter: saturate(140%) blur(8px);
    -webkit-backdrop-filter: saturate(140%) blur(8px);
    border-bottom: 1px solid var(--ss-border) !important;
    box-shadow: 0 1px 0 rgba(23,26,33,0.02) !important;
    padding-top: 0.55rem;
    padding-bottom: 0.55rem;
}
.ss-navbar .navbar-brand {
    font-weight: 800;
    color: var(--ss-ink) !important;
    letter-spacing: -0.03em;
    font-size: 1rem;
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
}
.ss-navbar .navbar-brand::before {
    content: "";
    width: 0.95rem;
    height: 0.95rem;
    border-radius: 0.3rem;
    background: linear-gradient(135deg, var(--ss-primary) 0%, var(--ss-accent-lavender) 100%);
    box-shadow: 0 2px 6px rgba(59,91,255,0.35);
}
.ss-navbar .nav-link {
    font-size: 0.875rem;
    font-weight: 500;
    color: #3d4350 !important;
    padding: 0.4rem 0.7rem !important;
    border-radius: 0.5rem;
    transition: background 0.12s ease, color 0.12s ease;
}
.ss-navbar .nav-link:hover {
    color: var(--ss-primary) !important;
    background: var(--ss-primary-soft);
}
.ss-navbar .dropdown-menu,
.navbar .dropdown-menu {
    z-index: var(--ss-z-dropdown);
}
.ss-navbar .dropdown-menu {
    border: 1px solid var(--ss-border);
    border-radius: var(--ss-radius);
    box-shadow: 0 12px 32px rgba(23,26,33,0.16);
    padding: 0.375rem 0;
    font-size: 0.875rem;
    min-width: 220px;
}
.ss-navbar .dropdown-item {
    padding: 0.375rem 1rem;
    color: #3d342d;
}
.ss-navbar .dropdown-item:hover {
    background: var(--ss-surface-soft);
    color: var(--ss-accent);
}
.ss-navbar .dropdown-header {
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--ss-text-muted);
    padding: 0.625rem 1rem 0.25rem;
}

/* ── Bootstrap card upgrades (sitewide) ─────────────────────────────────── */
.card {
    border-color: var(--ss-border);
    border-radius: var(--ss-radius);
    box-shadow: var(--ss-shadow-sm);
}
.card-header {
    background: var(--ss-surface-soft);
    border-bottom-color: var(--ss-border);
    font-size: 0.875rem;
    padding: 0.75rem 1rem;
}

/* ── SmartSuli card ─────────────────────────────────────────────────────── */
.ss-card {
    background: var(--ss-surface);
    border: 1px solid var(--ss-border);
    border-radius: var(--ss-radius);
    box-shadow: var(--ss-shadow-sm);
    padding: 1.25rem 1.375rem;
    margin-bottom: 1.25rem;
}
.ss-card-soft {
    background: var(--ss-surface-soft);
    border: 1px solid var(--ss-border-light);
    border-radius: var(--ss-radius);
    padding: 1rem 1.25rem;
    margin-bottom: 1.25rem;
}
.ss-card-header {
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--ss-text-muted);
    padding-bottom: 0.75rem;
    margin-bottom: 1rem;
    border-bottom: 1px solid var(--ss-border-light);
}

/* ── Section title ──────────────────────────────────────────────────────── */
.ss-section-title {
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--ss-text-muted);
    margin-bottom: 0.75rem;
}

/* ── KPI / stat card ────────────────────────────────────────────────────── */
.ss-kpi-card {
    background: var(--ss-surface);
    border: 1px solid var(--ss-border);
    border-radius: var(--ss-radius);
    padding: 1rem 1.25rem;
    box-shadow: var(--ss-shadow-xs);
}
.ss-kpi-label {
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--ss-text-muted);
    margin-bottom: 0.25rem;
}
.ss-kpi-value {
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--ss-text);
    line-height: 1.2;
}

/* ── Empty state ────────────────────────────────────────────────────────── */
.ss-empty-state {
    text-align: center;
    padding: 2.5rem 1.5rem;
    color: var(--ss-text-muted);
}
.ss-empty-icon {
    font-size: 1.875rem;
    opacity: 0.28;
    display: block;
    margin-bottom: 0.625rem;
    line-height: 1;
}
.ss-empty-title {
    font-size: 0.9375rem;
    font-weight: 600;
    color: #5e5550;
    margin-bottom: 0.375rem;
}
.ss-empty-sub {
    font-size: 0.875rem;
    max-width: 36ch;
    margin: 0 auto 1rem;
}

/* ── Badges ─────────────────────────────────────────────────────────────── */
.ss-badge {
    display: inline-block;
    padding: 0.2em 0.6em;
    border-radius: 0.25rem;
    font-size: 0.725rem;
    font-weight: 600;
    line-height: 1.4;
    vertical-align: baseline;
    border: 1px solid transparent;
}
.ss-badge-success { background: var(--ss-green-soft);   color: var(--ss-green);  border-color: var(--ss-green-border);  }
.ss-badge-warning { background: var(--ss-amber-soft);   color: var(--ss-amber);  border-color: var(--ss-amber-border);  }
.ss-badge-danger  { background: var(--ss-red-soft);     color: var(--ss-red);    border-color: var(--ss-red-border);    }
.ss-badge-neutral { background: var(--ss-surface-soft); color: var(--ss-text-muted); border-color: var(--ss-border);    }
.ss-badge-info    { background: var(--ss-accent-soft);  color: var(--ss-accent); border-color: var(--ss-accent-border); }

/* ADR-0016 Phase 3: exam tier badges (Platinum/Gold/Silver/Bronze/Copper) — fixed palette,
   deliberately not theme variables, since these colors are the tier's identity everywhere. */
.ss-badge-tier-platinum { background: #eef1f5; color: #4a5568; border-color: #cbd5e0; }
.ss-badge-tier-gold     { background: #fdf3d7; color: #92660a; border-color: #f0d896; }
.ss-badge-tier-silver   { background: #eef0f2; color: #63727e; border-color: #d3d9de; }
.ss-badge-tier-bronze   { background: #f6e6d8; color: #92450f; border-color: #e6bd97; }
.ss-badge-tier-copper   { background: #f9e2d3; color: #a1470f; border-color: #edb98d; }

/* ── Next action hint ───────────────────────────────────────────────────── */
.ss-next-action {
    background: var(--ss-accent-soft);
    border: 1px solid var(--ss-accent-border);
    border-left: 3px solid var(--ss-accent);
    border-radius: 0 var(--ss-radius-sm) var(--ss-radius-sm) 0;
    padding: 0.75rem 1rem;
    font-size: 0.875rem;
    color: #1a3d6e;
    margin-bottom: 1rem;
}

/* ── Methodology hint box ───────────────────────────────────────────────── */
.ss-hint-box {
    background: #fffcee;
    border: 1px solid #ece8a0;
    border-radius: var(--ss-radius);
    padding: 0.875rem 1rem;
    font-size: 0.875rem;
    margin-bottom: 1rem;
}
.ss-hint-title {
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: #7a6010;
    margin-bottom: 0.5rem;
}
.ss-hint-box ul {
    margin: 0;
    padding-left: 1.25rem;
}
.ss-hint-box li { margin-bottom: 0.25rem; }

/* ── Action bar ─────────────────────────────────────────────────────────── */
.ss-action-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
    padding: 0.875rem 1.125rem;
    background: var(--ss-surface);
    border: 1px solid var(--ss-border);
    border-radius: var(--ss-radius);
    box-shadow: var(--ss-shadow-xs);
    margin-bottom: 1.25rem;
}

/* ── Step number marker ─────────────────────────────────────────────────── */
.ss-step-num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.375rem;
    height: 1.375rem;
    border-radius: 50%;
    background: var(--ss-accent);
    color: white;
    font-size: 0.6875rem;
    font-weight: 700;
    flex-shrink: 0;
    margin-right: 0.375rem;
    vertical-align: middle;
}

/* ── Table upgrades ─────────────────────────────────────────────────────── */
.ss-table thead th {
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--ss-text-muted);
    background: var(--ss-surface-soft);
    border-bottom: 1px solid var(--ss-border);
    padding: 0.6rem 0.75rem;
    white-space: nowrap;
}
.ss-table tbody td {
    padding: 0.6rem 0.75rem;
    vertical-align: middle;
    border-bottom: 1px solid var(--ss-border-light);
}
.ss-table tbody tr:last-child td { border-bottom: none; }
.ss-table tbody tr:hover { background: var(--ss-surface-soft); }

/* ── Connection point callout ───────────────────────────────────────────── */
.ss-connection-point {
    background: #f0faf4;
    border: 1px solid #b0d8b8;
    border-left: 3px solid #2a9a5a;
    border-radius: 0 var(--ss-radius-sm) var(--ss-radius-sm) 0;
    padding: 0.625rem 0.875rem;
    font-size: 0.9375rem;
    font-weight: 600;
    color: #1a5a32;
    margin-bottom: 0.875rem;
}

/* ── Back link ──────────────────────────────────────────────────────────── */
.ss-back-link {
    font-size: 0.875rem;
    color: var(--ss-text-muted);
    text-decoration: none;
    display: inline-block;
    margin-bottom: 0.75rem;
}
.ss-back-link:hover { color: var(--ss-accent); text-decoration: none; }

/* ── Inline note ────────────────────────────────────────────────────────── */
.ss-note {
    font-size: 0.8125rem;
    color: var(--ss-text-muted);
    font-style: italic;
    margin-bottom: 0.875rem;
}

/* ═══════════════════════════════════════════════════════════════════════════
   MODERN PRODUCT LAYER
   Page heroes, filter shells, metric strips, decision cards, modern tables.
   Used to move workflow pages away from a table-first admin feel toward a
   calm, premium workspace.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── App shell / page ───────────────────────────────────────────────────── */
.ss-app-shell { }
.ss-page { padding-bottom: 1.5rem; }

/* ── Page hero ──────────────────────────────────────────────────────────── */
.ss-page-hero {
    position: relative;
    overflow: hidden;
    border-radius: var(--ss-radius-lg);
    padding: 1.75rem 1.875rem;
    margin-bottom: 1.75rem;
    background:
        radial-gradient(120% 140% at 100% 0%, rgba(124,92,255,0.10) 0%, rgba(124,92,255,0) 45%),
        radial-gradient(120% 140% at 0% 100%, rgba(24,199,194,0.12) 0%, rgba(24,199,194,0) 50%),
        var(--ss-surface);
    border: 1px solid var(--ss-border);
    box-shadow: var(--ss-shadow-card);
}
.ss-page-hero > * { position: relative; z-index: 1; }
.ss-page-eyebrow {
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--ss-primary);
    margin-bottom: 0.5rem;
}
.ss-page-title {
    font-size: 1.625rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--ss-ink);
    margin-bottom: 0.5rem;
    line-height: 1.15;
}
.ss-page-subtitle {
    font-size: 0.95rem;
    color: var(--ss-text-muted);
    max-width: 64ch;
    margin-bottom: 0;
}
.ss-hero-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 1.125rem;
}

/* Highlighted current/next lesson block inside a hero */
.ss-hero-lesson {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1.25rem;
    padding: 1.125rem 1.25rem;
    background: var(--ss-surface);
    border: 1px solid var(--ss-accent-border);
    border-radius: var(--ss-radius);
    box-shadow: var(--ss-shadow-xs);
}
.ss-hero-lesson-meta { flex: 1 1 200px; min-width: 180px; }

/* ── Filter shell ───────────────────────────────────────────────────────── */
.ss-filter-shell {
    background: var(--ss-surface);
    border: 1px solid var(--ss-border);
    border-radius: var(--ss-radius);
    box-shadow: var(--ss-shadow-xs);
    padding: 1rem 1.125rem;
    margin-bottom: 1.5rem;
}
.ss-filter-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.875rem;
}
.ss-filter-label {
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--ss-text-muted);
    margin-bottom: 0.3rem;
    display: block;
}
.ss-filter-pill {
    background: var(--ss-surface-soft);
    border: 1px solid var(--ss-border);
    border-radius: 999px;
    padding: 0.25rem 0.75rem;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--ss-text-muted);
}

/* ── Exam review cockpit ────────────────────────────────────────────────── */
/* Filter pills as links with a clear active state and an optional count chip. */
a.ss-filter-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    text-decoration: none;
    transition: background-color 140ms ease, color 140ms ease, border-color 140ms ease, transform 140ms ease;
}
a.ss-filter-pill:hover {
    color: var(--ss-ink);
    border-color: var(--ss-accent-border);
    transform: translateY(-1px);
}
a.ss-filter-pill.is-active {
    background: linear-gradient(135deg, rgba(223, 247, 236, 0.95), rgba(228, 238, 255, 0.95));
    color: #1a2f72;
    border-color: rgba(180, 200, 255, 0.6);
    box-shadow: 0 4px 14px rgba(59, 91, 255, 0.14), inset 0 0 0 1px rgba(255, 255, 255, 0.6);
}
.ss-filter-pill .ss-pill-count {
    font-size: 0.6875rem;
    font-weight: 800;
    background: rgba(23, 26, 33, 0.08);
    border-radius: 999px;
    padding: 0.02rem 0.4rem;
    min-width: 1.2rem;
    text-align: center;
}

/* Liquid progress track for review completion. */
.ss-review-progress {
    height: 0.55rem;
    border-radius: 999px;
    background: var(--ss-surface-soft);
    overflow: hidden;
    box-shadow: inset 0 1px 2px rgba(15, 23, 42, 0.10);
}
.ss-review-progress > span {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: linear-gradient(90deg, var(--ss-accent-cyan), var(--ss-success));
    box-shadow: 0 0 10px rgba(24, 160, 88, 0.35);
    transition: width 500ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* Compact review row — a frosted glass card with a colour-coded status stripe. */
.ss-review-row {
    border: 1px solid var(--ss-glass-border);
    border-left: 4px solid var(--ss-border);
    border-radius: 14px;
    background: var(--ss-glass-bg-soft);
    -webkit-backdrop-filter: var(--ss-glass-blur);
    backdrop-filter: var(--ss-glass-blur);
    padding: 0.7rem 0.9rem;
    margin-bottom: 0.6rem;
}
.ss-review-row.is-correct,
.ss-review-row.is-reviewed-ok { border-left-color: var(--ss-success); }
.ss-review-row.is-incorrect   { border-left-color: var(--ss-danger); }
.ss-review-row.is-review      { border-left-color: var(--ss-accent-yellow); }
.ss-review-row.is-partial     { border-left-color: var(--ss-accent-cyan); }
.ss-review-row.is-blank       { border-left-color: var(--ss-text-muted); }
.ss-review-qno {
    font-weight: 800;
    font-size: 0.9rem;
    color: var(--ss-ink);
}
.ss-review-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr) auto;
    gap: 0.6rem 1rem;
    align-items: start;
}
@media (max-width: 767.98px) {
    .ss-review-grid { grid-template-columns: 1fr; }
}
.ss-review-celllabel {
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--ss-text-muted);
    margin-bottom: 0.1rem;
}
.ss-review-studentans { white-space: pre-wrap; word-break: break-word; }
.ss-answer-blank { font-style: italic; color: var(--ss-text-muted); }
.ss-guidance-block {
    margin-top: 0.5rem;
    background: var(--ss-glass-bg-soft);
    border: 1px solid var(--ss-glass-border);
    border-radius: 10px;
    padding: 0.55rem 0.7rem;
    font-size: 0.8125rem;
    color: var(--ss-text-muted);
}
.ss-review-actions { display: flex; flex-wrap: wrap; gap: 0.3rem; }

/* ── Official marking rules (megoldókulcs) ──────────────────────────────────
   A deliberately non-app look: warm paper, a heavy left rule, a serif face and a citation line.
   Everything else in this column is the app talking (grey guidance, violet AI proposals, coloured
   scoring pills); this is the printed answer key talking, quoted verbatim. The teacher is auditing
   an AI verdict AGAINST these sentences, so mistaking one for model output is the failure to
   design out. Never truncate the text or clamp its height — a half-shown rule is a wrong rule. */
.ss-marking-rules {
    background: rgba(180, 132, 46, 0.06);
    border: 1px solid rgba(180, 132, 46, 0.28);
    border-radius: 10px;
    padding: 0.55rem 0.7rem;
}
.ss-marking-rule {
    margin: 0.35rem 0 0;
    padding-left: 0.7rem;
    border-left: 3px solid rgba(180, 132, 46, 0.75);
}
.ss-marking-rule:first-of-type { margin-top: 0.2rem; }
.ss-marking-rule-text {
    display: block;
    font-family: Georgia, "Times New Roman", serif;
    font-size: 0.86rem;
    line-height: 1.45;
    color: var(--ss-text);
    white-space: pre-wrap;
    word-break: break-word;
}
.ss-marking-rule-cite {
    display: block;
    margin-top: 0.15rem;
    font-size: 0.7rem;
    font-style: normal;
    color: var(--ss-text-muted);
}
@media (prefers-color-scheme: dark) {
    .ss-marking-rules {
        background: rgba(214, 172, 92, 0.10);
        border-color: rgba(214, 172, 92, 0.30);
    }
    .ss-marking-rule { border-left-color: rgba(214, 172, 92, 0.75); }
}

/* ── AI review suggestions (dev-planning/35 B2b) ────────────────────────────
   A colour family used nowhere else on this page. Green/red/amber/cyan all mean "a score decided
   something"; violet means "a machine is proposing, a teacher has not decided". The two must never
   be mistakable for each other at a glance. */
.ss-ai-draft {
    border: 1px solid rgba(124, 92, 255, 0.32);
    border-left: 3px solid rgba(124, 92, 255, 0.85);
    border-radius: 12px;
    background: rgba(124, 92, 255, 0.07);
    padding: 0.55rem 0.7rem;
}
.ss-ai-panel { border-left: 3px solid rgba(124, 92, 255, 0.85); }
.ss-ai-chip {
    display: inline-flex;
    align-items: center;
    font-size: 0.66rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    padding: 0.08rem 0.45rem;
    border-radius: 999px;
    background: rgba(124, 92, 255, 0.16);
    border: 1px solid rgba(124, 92, 255, 0.35);
    color: #5B3FD1;
    white-space: nowrap;
}
.ss-ai-chip.is-redink {
    background: var(--ss-red-soft);
    border-color: var(--ss-red-border);
    color: var(--ss-danger);
}
@media (prefers-color-scheme: dark) {
    .ss-ai-draft {
        background: rgba(124, 92, 255, 0.15);
        border-color: rgba(124, 92, 255, 0.42);
    }
    .ss-ai-chip {
        background: rgba(124, 92, 255, 0.24);
        border-color: rgba(124, 92, 255, 0.45);
        color: #CBBEFF;
    }
}

/* ── Live lesson answer feedback (teacher-only) ─────────────────────────────
   Compact glass rows with a small glowing correctness light and an expandable
   solution. Wrong answers open by default; the red is soft coral, never neon. */
.ss-answer-row {
    display: flex;
    gap: 0.55rem;
    align-items: flex-start;
    padding: 0.4rem 0.55rem;
    border: 1px solid var(--ss-glass-border);
    border-radius: 11px;
    background: var(--ss-glass-bg-soft);
    -webkit-backdrop-filter: var(--ss-glass-blur);
    backdrop-filter: var(--ss-glass-blur);
    margin-bottom: 0.4rem;
}
.ss-answer-main { flex: 1 1 auto; min-width: 0; }
.ss-answer-value { white-space: pre-wrap; word-break: break-word; }
.ss-answer-blank { font-style: italic; color: var(--ss-text-muted); }

/* Top row: a short question marker kept visually separate from the answer text, which wraps
   cleanly on its own instead of being crammed into one run-on line with badges. */
.ss-answer-toprow {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    flex-wrap: wrap;
}
.ss-answer-qno {
    flex: 0 0 auto;
    font-weight: 800;
    font-size: 0.85rem;
    color: var(--ss-ink);
}
.ss-answer-valuewrap { min-width: 0; word-break: break-word; }

/* Second row: flag + marking controls, aligned in their own strip below the answer text. */
.ss-answer-metarow {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
    margin-top: 0.3rem;
}

/* Field-by-field teacher marking — a compact 3-way segmented control so the teacher can record
   "student achieved this" (or not, or needs another look) directly on the live answer row, without
   leaving the lesson. Quiet by default; the active decision gets a soft colour fill matching the
   correctness light so the row stays scannable at a glance. */
.ss-mark-controls {
    display: flex;
    gap: 0.35rem;
    flex-wrap: wrap;
    margin-top: 0.45rem;
}
/* Icon-only since the labels moved to title/aria-label: a tick, a cross, a clock. Circular, evenly
   sized, hairline border — three glyphs the eye can hit without reading, which is what a teacher
   marking live actually does. */
.ss-mark-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.7rem;
    height: 1.7rem;
    padding: 0;
    border: 1px solid var(--ss-glass-border);
    background: var(--ss-surface);
    border-radius: 999px;
    color: var(--ss-text-muted);
    cursor: pointer;
    line-height: 1;
    transition: color .15s ease, border-color .15s ease, background-color .15s ease;
}
.ss-mark-btn-icon {
    width: 0.95rem;
    height: 0.95rem;
    display: block;
}
.ss-mark-btn:hover { border-color: var(--ss-accent-cyan); color: var(--ss-ink); }
.ss-mark-btn:disabled { opacity: 0.6; cursor: wait; }
.ss-mark-btn-correct.is-active {
    background: rgba(43, 182, 115, 0.14);
    border-color: rgba(43, 182, 115, 0.55);
    color: #1c8a5a;
}
.ss-mark-btn-incorrect.is-active {
    background: rgba(232, 115, 107, 0.14);
    border-color: rgba(232, 115, 107, 0.55);
    color: #c14940;
}
.ss-mark-btn-needsreview.is-active {
    background: rgba(230, 162, 60, 0.16);
    border-color: rgba(230, 162, 60, 0.6);
    color: #92660f;
}
/* Part marks (doc 38). Sized to sit on the same line as the three glyphs without competing with
   them: the tick and cross stay the fast path, and this is the escape hatch for the exercise that
   is one field worth six. The "/ N" is static text, so the box never has to explain itself. */
.ss-mark-partial {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    margin-left: 0.15rem;
}
.ss-mark-partial-input {
    width: 3.1rem;
    height: 1.7rem;
    padding: 0 0.25rem;
    border: 1px solid var(--ss-glass-border);
    background: var(--ss-surface);
    border-radius: 0.4rem;
    color: var(--ss-ink);
    font-size: 0.8rem;
    text-align: center;
}
.ss-mark-partial-input:focus {
    outline: none;
    border-color: var(--ss-accent-cyan);
}
.ss-mark-partial-input:disabled { opacity: 0.6; cursor: wait; }
.ss-mark-partial-max {
    font-size: 0.75rem;
    color: var(--ss-text-muted);
    white-space: nowrap;
}

/* Inline "edit this value" affordance — replaced the ✏️ emoji, which rendered as an oversized
   colour glyph next to 0.8rem text. Sized to the text it sits beside and quiet until hovered. */
.ss-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--ss-text-muted);
    opacity: 0.55;
    cursor: pointer;
    vertical-align: baseline;
    transition: opacity .15s ease, color .15s ease;
}
.ss-icon-btn:hover { opacity: 1; color: var(--ss-accent-cyan); }
.ss-edit-icon {
    width: 0.8rem;
    height: 0.8rem;
    display: block;
}

/* Auto-preview hint — deliberately plain text, not a pill/badge, so it can never be mistaken for a
   selected mark button. Shown only until a teacher explicitly reviews the field. */
.ss-mark-auto-hint {
    font-size: 0.7rem;
    font-style: italic;
    color: var(--ss-text-muted);
    align-self: center;
}

/* Student-visibility toggle — doubles as the quiet "csak tanári" badge once a field is teacher-only.
   Visible-state styling stays understated (a soft outline chip) so it doesn't compete with the
   answer text; the teacher-only state gets a filled, slightly more prominent chip. */
.ss-visibility-toggle {
    border: 1px solid var(--ss-glass-border);
    background: transparent;
    border-radius: 999px;
    font-size: 0.68rem;
    font-weight: 600;
    padding: 0.08rem 0.55rem;
    color: var(--ss-text-muted);
    cursor: pointer;
    line-height: 1.5;
}
.ss-visibility-toggle:hover { border-color: var(--ss-accent-cyan); color: var(--ss-ink); }
.ss-visibility-toggle:disabled { opacity: 0.6; cursor: wait; }
.ss-visibility-toggle.is-teacher-only {
    background: var(--ss-surface-soft);
    border-color: var(--ss-border);
    color: var(--ss-text-muted);
}

/* The correctness light — a small soft-glowing dot. */
.ss-answer-light {
    flex: 0 0 auto;
    width: 0.7rem;
    height: 0.7rem;
    margin-top: 0.28rem;
    border-radius: 50%;
    background: var(--ss-text-muted);
}
.ss-answer-light-correct {
    background: #2bb673;
    box-shadow: 0 0 7px rgba(43, 182, 115, 0.6);
}
.ss-answer-light-incorrect {
    background: #e8736b;   /* soft coral, not harsh danger red */
    box-shadow: 0 0 7px rgba(232, 115, 107, 0.55);
}
.ss-answer-light-review {
    background: #e6a23c;   /* amber */
    box-shadow: 0 0 7px rgba(230, 162, 60, 0.5);
}
.ss-answer-light-partial {
    background: var(--ss-accent-cyan);
    box-shadow: 0 0 7px rgba(24, 199, 194, 0.5);
}
.ss-answer-light-blank {
    background: #b9b3a6;   /* grey, no glow */
    box-shadow: none;
}

/* ADR-0015 Phase 2 (Results Layer) — the same correctness light, as a small corner badge riding
   inside a positioned .ss-paper-field-wrap (which the pdf.js overlay script already gives
   position: absolute), so it sits on the field's rendered PDF rectangle with no script changes. */
.ss-paper-result-badge {
    position: absolute;
    top: calc(-0.35rem * var(--ss-paper-oversample, 1));
    right: calc(-0.35rem * var(--ss-paper-oversample, 1));
    width: calc(0.85rem * var(--ss-paper-oversample, 1));
    height: calc(0.85rem * var(--ss-paper-oversample, 1));
    border-radius: 50%;
    /* Fixed white, not the theme's --ss-surface (which flips dark) — this rides on the white PDF
       page regardless of the app's own light/dark mode. */
    border: calc(2px * var(--ss-paper-oversample, 1)) solid #fff;
    pointer-events: none;
    /* background/glow always come from the paired .ss-answer-light-* class (never used alone) —
       no background set here so cascade order can't override that color. */
}

/* Premium paper-view choice widget (ADR-0015) — the underlying <select> drives every real
   interaction (click opens the native picker, keyboard/typeahead, screen readers) but is made
   fully transparent once positioned on the PDF page; a custom display face — filling the entire
   widget, not a small badge riding on top of it — is what the student actually sees. Before the
   field is positioned (loading, or the PDF failed) the select renders as a plain, visible,
   in-flow control instead — see .ss-is-positioned below.

   Two looks, matching how the list view separates "still yours to change" from "final":
   while answering, this is the exact glass-pill + colored-icon language of the big True/False/
   Not Given control there (.ss-opt--tfng above) — a white/glass pill with only the icon or short
   key badge carrying color, so it never looks like a scored verdict. Once results are published
   (.is-graded), the active face switches to a solid color fill — the "locked in, done" look.

   These overlay widgets (and the text/textarea inputs further below) always sit on top of the
   rendered PDF page — a fixed white sheet of paper, regardless of the app's own light/dark theme
   — so unlike normal form controls they must NOT pick up the site's dark-mode tokens (--ss-muted,
   --ss-border, --ss-glass-bg-soft, --ss-text all flip to light-on-dark values there); on the
   white page that reads as low-contrast light-on-light. Pinned to their own non-flipping tokens
   instead, declared once here regardless of prefers-color-scheme. */
:root {
    --ss-paper-ink: #171A21;
    --ss-paper-muted: #667085;
    --ss-paper-border: rgba(23, 26, 33, 0.10);
    --ss-paper-glass: rgba(255, 255, 255, 0.78);
    /* The zoom stage (AttemptPaper.cshtml) rasterizes pages at this many times the fit-width
       scale, then CSS-transform: scale()s the whole thing back down for the default view — which
       shrinks every fixed-unit (px/rem) property in this subtree along with it, not just the
       canvas. Anything below sized in px/rem (not %) multiplies by this to compensate, so it reads
       at its intended size once the transform is applied. JS overwrites this before positioning
       any field; the default of 1 is a no-op for the pre-JS/no-JS fallback (nothing to compensate
       for there — no transform is applied until .ss-is-positioned exists). */
    --ss-paper-oversample: 1;
}
.ss-paper-choice-display { display: none; }
.ss-is-positioned .ss-paper-choice-native {
    position: absolute;
    inset: 0;
    z-index: 2;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    cursor: pointer;
    font-size: 16px; /* prevents iOS Safari zooming the page in on focus */
}
.ss-is-positioned .ss-paper-choice-native:disabled { cursor: default; }
/* Student paper view (AttemptPaper.cshtml): exam-paper-widgets.mjs marks the wrap .ss-px-enhanced
   once it has built the premium on-page control, at which point the <select> becomes a pure value
   carrier — invisible, not focusable, never intercepting the buttons' clicks. Until then the plain
   positioned select is the fallback (the teacher LiveCompanion mirror never sets this class: it has
   no <select> at all and keeps rendering its own display-only .ss-paper-choice-display faces). */
.ss-px-enhanced .ss-paper-choice-native {
    z-index: 0;
    opacity: 0;
    pointer-events: none;
}
.ss-is-positioned .ss-paper-choice-display {
    display: block;
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    border-radius: 999px;
    background: var(--ss-paper-glass);
    border: calc(1.5px * var(--ss-paper-oversample)) solid var(--ss-paper-border);
    box-shadow: var(--ss-shadow-xs);
    transition: border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
}
/* Keyboard focus ring on the (invisible) native select shows on the face beneath it instead. */
.ss-is-positioned .ss-paper-choice-native:focus-visible ~ .ss-paper-choice-display {
    box-shadow: 0 0 0 calc(0.15rem * var(--ss-paper-oversample)) #fff, 0 0 0 calc(0.3rem * var(--ss-paper-oversample)) var(--ss-accent);
}
.ss-paper-field-wrap.ss-is-positioned:hover .ss-paper-choice-display {
    border-color: var(--ss-primary);
}
.ss-is-positioned:has(.ss-paper-choice-native:disabled) .ss-paper-choice-display { opacity: 0.85; }

.ss-paper-choice-face {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 calc(0.2rem * var(--ss-paper-oversample));
    border-radius: 999px;
    opacity: 0;
    transform: scale(0.8);
    transition: opacity 0.14s ease, transform 0.14s ease, background-color 0.14s ease, color 0.14s ease;
    color: rgba(23, 26, 33, 0.3);
    font-size: calc(0.7rem * var(--ss-paper-oversample));
    font-weight: 700;
    line-height: 1.15;
    text-align: center;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ss-paper-choice-face.is-active { opacity: 1; transform: scale(1); }
.ss-paper-choice-face--empty { color: var(--ss-paper-muted); }
.ss-paper-choice-chevron { width: 45%; height: 45%; max-width: calc(1rem * var(--ss-paper-oversample)); max-height: calc(1rem * var(--ss-paper-oversample)); }
.ss-paper-choice-icon { width: 55%; height: 55%; max-width: calc(1.4rem * var(--ss-paper-oversample)); max-height: calc(1.4rem * var(--ss-paper-oversample)); }

/* Answering — glass pill stays neutral, only the active icon/badge carries color. Colors here
   are the theme's --ss-green/--ss-danger/--ss-primary-strong tokens, which (unlike --ss-muted)
   don't flip in dark mode, so they're already safe on the fixed-white page as-is. */
.ss-paper-choice-face--yes.is-active { color: var(--ss-green); filter: drop-shadow(0 0 calc(5px * var(--ss-paper-oversample)) rgba(24, 160, 88, 0.45)); }
.ss-paper-choice-face--no.is-active { color: var(--ss-danger); filter: drop-shadow(0 0 calc(5px * var(--ss-paper-oversample)) rgba(229, 72, 77, 0.45)); }
.ss-paper-choice-face--ng.is-active { color: var(--ss-paper-muted); }
.ss-paper-choice-face--choice.is-active { color: var(--ss-primary-strong); background: var(--ss-primary-soft); }

/* Graded (Model.IsResultsPublished) — the active face switches to a solid, "final" fill. */
.ss-paper-field-wrap.is-graded .ss-paper-choice-face--yes.is-active { color: #fff; background: var(--ss-green); filter: none; }
.ss-paper-field-wrap.is-graded .ss-paper-choice-face--no.is-active { color: #fff; background: var(--ss-danger); filter: none; }
.ss-paper-field-wrap.is-graded .ss-paper-choice-face--ng.is-active { color: #fff; background: var(--ss-paper-muted); }
.ss-paper-field-wrap.is-graded .ss-paper-choice-face--choice.is-active { color: #fff; background: var(--ss-primary); }

/* Pinned to the same fixed-light tokens as the choice widgets above — these inputs sit on the
   white PDF page too, so they must not pick up Bootstrap's dark-mode .form-control override
   (specificity here — two classes — beats that rule's `textarea.form-control` either way). */
.ss-paper-field-wrap .ss-paper-input {
    background-color: var(--ss-paper-glass);
    color: var(--ss-paper-ink);
    border-color: var(--ss-paper-border);
}

/* Premium word-fill blank (dev-planning/20). The old hairline slivers were near-invisible on the
   white page ("Windows XP"). Once positioned, a text/textarea blank gets a glass fill with a
   blue→cyan gradient border (padding-box/border-box double-background — border-color must be
   transparent for the border-box layer to show), a soft primary-tinted shadow and a hover lift.
   Everything is oversample-scaled so it reads at its intended size after the zoom stage transform.
   The select carrier is excluded (opacity 0 anyway); this is only the on-page fill blanks. */
.ss-paper-field-wrap.ss-is-positioned input.ss-paper-input[type="text"],
.ss-paper-field-wrap.ss-is-positioned textarea.ss-paper-input {
    border: calc(1.5px * var(--ss-paper-oversample)) solid transparent;
    border-radius: calc(0.7rem * var(--ss-paper-oversample));
    background:
        linear-gradient(rgba(255, 255, 255, 0.94), rgba(255, 255, 255, 0.94)) padding-box,
        linear-gradient(120deg, rgba(59, 91, 255, 0.55), rgba(24, 199, 194, 0.45)) border-box;
    color: var(--ss-paper-ink);
    box-shadow: 0 calc(1px * var(--ss-paper-oversample)) calc(2px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.06),
        0 calc(6px * var(--ss-paper-oversample)) calc(16px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.08);
    transition: box-shadow 0.15s ease, transform 0.15s ease, background 0.15s ease;
}
.ss-paper-field-wrap.ss-is-positioned input.ss-paper-input[type="text"]:hover,
.ss-paper-field-wrap.ss-is-positioned textarea.ss-paper-input:hover {
    box-shadow: 0 calc(2px * var(--ss-paper-oversample)) calc(4px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.08),
        0 calc(8px * var(--ss-paper-oversample)) calc(22px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.14);
}
/* Bold, primary-tinted "answered" fill — choice widgets get the fully-colored toggle/badge state
   instead. Higher specificity than the base, so it wins while not focused. */
.ss-paper-field-wrap.ss-is-positioned.is-answered input.ss-paper-input[type="text"]:not(:focus),
.ss-paper-field-wrap.ss-is-positioned.is-answered textarea.ss-paper-input:not(:focus) {
    background:
        linear-gradient(rgba(232, 236, 255, 0.92), rgba(232, 236, 255, 0.92)) padding-box,
        linear-gradient(120deg, var(--ss-primary), rgba(24, 199, 194, 0.8)) border-box;
    font-weight: 700;
}
/* Focus: strong ring + a slight grow. The text[type=text] width-pop rule below also fires on
   focus (position/width) — this only adds the ring/scale so both behaviors coexist. */
.ss-paper-field-wrap.ss-is-positioned textarea.ss-paper-input:focus {
    outline: none;
    box-shadow: 0 0 0 calc(0.16rem * var(--ss-paper-oversample)) #fff,
        0 0 0 calc(0.34rem * var(--ss-paper-oversample)) var(--ss-primary),
        0 calc(10px * var(--ss-paper-oversample)) calc(28px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.18);
}

/* A typed word-fill answer routinely outruns the narrow on-page blank (sized to match the
   printed space, not the answer). The script in AttemptPaper.cshtml shrinks the font to keep the
   full word visible; this ellipsis only kicks in as a last-resort fallback below its minimum
   font size, instead of showing whatever the input's own scroll position happens to land on. */
.ss-paper-field-wrap input.ss-paper-input[type="text"] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ss-is-positioned input.ss-paper-input[type="text"]:focus {
    position: absolute;
    inset: 0 auto auto 0;
    z-index: 5;
    width: max(100%, calc(10rem * var(--ss-paper-oversample)));
    outline: none;
    /* Focus ring + drop shadow together (dev-planning/20). The slight grow is a transform-origin
       left so the box still anchors to the blank's left edge as it pops wider. */
    box-shadow: 0 0 0 calc(0.16rem * var(--ss-paper-oversample)) #fff,
        0 0 0 calc(0.34rem * var(--ss-paper-oversample)) var(--ss-primary),
        0 calc(8px * var(--ss-paper-oversample)) calc(24px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.22);
    transform-origin: left center;
    transform: scale(1.02);
}

/* ADR-0015 Phase 6 — the teacher's LiveCompanion read-only mirror of the student's structured
   answers. Same base look as the student's own unpublished answer widgets, but nothing here is
   interactive (no name attributes, no submission), so hover/focus affordances are suppressed. */
.ss-paper-field-wrap--mirror {
    pointer-events: none;
}
.ss-paper-field-wrap--mirror .ss-paper-input {
    cursor: default;
}

/* ═══ Premium on-page answer widgets (dev-planning/20; built by exam-paper-widgets.mjs) ══════════
   Ported from the approved prototype (tools/paperview-widget-prototype.html, variant A + B). These
   ride the white PDF page inside the transformed .ss-paper-zoom-stage, so every fixed px/rem below
   multiplies by var(--ss-paper-oversample) and every color is a fixed-light --ss-paper-* / non-
   flipping token (never a dark-mode token). The floating remote further down is the sole exception:
   it is appended OUTSIDE the zoom stage and uses theme tokens.

   The inline control and the chip anchor to the spec box's LEFT edge and overflow to the right if
   wider than the printed blank (same approach as the text input focus width-pop above) — they are
   never squeezed to fit. */
.ss-paper-field-wrap.ss-is-positioned .ss-px-inline,
.ss-paper-field-wrap.ss-is-positioned .ss-px-chip {
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    z-index: 3;
    white-space: nowrap;
}

/* Which control is visible: default (on-paper) vs. remote mode (chip + floating remote for every
   field). A non-inline choice (>4 options / long labels) always uses the chip as its primary. */
.ss-px-chip { display: none; }
.ss-paper-field-wrap.ss-px-chip-primary .ss-px-chip { display: inline-flex; }
.ss-px-remote-mode .ss-px-chip { display: inline-flex; }
.ss-px-remote-mode .ss-px-inline { display: none; }
.ss-px-remote-mode input.ss-paper-input:not(.ss-paper-choice-native),
.ss-px-remote-mode textarea.ss-paper-input { display: none; }

/* ── TFNG: always-visible 3-way glass toggle ─────────────────────────────────────────────────── */
.ss-px-toggle {
    display: inline-flex;
    align-items: center;
    gap: calc(0.15rem * var(--ss-paper-oversample));
    padding: calc(0.22rem * var(--ss-paper-oversample));
    border-radius: 999px;
    background: var(--ss-paper-glass);
    border: calc(1.5px * var(--ss-paper-oversample)) solid var(--ss-paper-border);
    box-shadow: 0 calc(4px * var(--ss-paper-oversample)) calc(14px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.07);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.ss-px-toggle:hover { border-color: var(--ss-primary); box-shadow: 0 calc(6px * var(--ss-paper-oversample)) calc(18px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.12); }
.ss-px-seg {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: calc(2.35rem * var(--ss-paper-oversample));
    height: calc(2.35rem * var(--ss-paper-oversample));
    border-radius: 999px;
    border: none;
    background: transparent;
    cursor: pointer;
    color: rgba(23, 26, 33, 0.30);
    transition: transform 0.24s cubic-bezier(0.16, 1, 0.3, 1), color 0.24s cubic-bezier(0.16, 1, 0.3, 1), background 0.24s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.24s cubic-bezier(0.16, 1, 0.3, 1);
}
.ss-px-seg svg { width: calc(1.25rem * var(--ss-paper-oversample)); height: calc(1.25rem * var(--ss-paper-oversample)); }
.ss-px-seg:hover { color: var(--ss-paper-ink); transform: scale(1.1); }
.ss-px-seg:focus-visible { outline: none; box-shadow: 0 0 0 calc(0.12rem * var(--ss-paper-oversample)) #fff, 0 0 0 calc(0.26rem * var(--ss-paper-oversample)) var(--ss-primary); }
.ss-px-seg:disabled { cursor: default; }
.ss-px-toggle.has-answer .ss-px-seg:not(.is-active) { color: rgba(23, 26, 33, 0.14); transform: scale(0.88); }
.ss-px-seg.is-active { background: #fff; box-shadow: 0 calc(2px * var(--ss-paper-oversample)) calc(8px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.12); transform: scale(1.08); }
.ss-px-seg.is-active.ss-px-seg--yes { color: var(--ss-green); filter: drop-shadow(0 0 calc(8px * var(--ss-paper-oversample)) rgba(24, 160, 88, 0.55)); }
.ss-px-seg.is-active.ss-px-seg--no { color: var(--ss-danger); filter: drop-shadow(0 0 calc(8px * var(--ss-paper-oversample)) rgba(229, 72, 77, 0.55)); }
.ss-px-seg.is-active.ss-px-seg--ng { color: var(--ss-paper-muted); filter: drop-shadow(0 0 calc(8px * var(--ss-paper-oversample)) rgba(102, 112, 133, 0.5)); }
/* Graded (results published): active segment locks to a solid fill, white glyph. */
.ss-paper-field-wrap.is-graded .ss-px-seg.is-active { color: #fff; filter: none; box-shadow: none; }
.ss-paper-field-wrap.is-graded .ss-px-seg.is-active.ss-px-seg--yes { background: var(--ss-green); }
.ss-paper-field-wrap.is-graded .ss-px-seg.is-active.ss-px-seg--no { background: var(--ss-danger); }
.ss-paper-field-wrap.is-graded .ss-px-seg.is-active.ss-px-seg--ng { background: var(--ss-paper-muted); }

/* ── Short ≤4-option choice: inline circular badge row ────────────────────────────────────────── */
.ss-px-badges {
    display: inline-flex;
    gap: calc(0.3rem * var(--ss-paper-oversample));
    padding: calc(0.22rem * var(--ss-paper-oversample));
    border-radius: 999px;
    background: var(--ss-paper-glass);
    border: calc(1.5px * var(--ss-paper-oversample)) solid var(--ss-paper-border);
    box-shadow: 0 calc(4px * var(--ss-paper-oversample)) calc(14px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.07);
}
.ss-px-badge {
    position: relative;
    width: calc(2.2rem * var(--ss-paper-oversample));
    height: calc(2.2rem * var(--ss-paper-oversample));
    border-radius: 50%;
    border: none;
    cursor: pointer;
    background: transparent;
    color: rgba(23, 26, 33, 0.45);
    font-weight: 800;
    font-size: calc(0.95rem * var(--ss-paper-oversample));
    font-family: inherit;
    transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1), color 0.2s cubic-bezier(0.16, 1, 0.3, 1), background 0.2s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
.ss-px-badge:hover { background: var(--ss-primary-soft); color: var(--ss-primary-strong); transform: scale(1.08); }
.ss-px-badge:focus-visible { outline: none; box-shadow: 0 0 0 calc(0.12rem * var(--ss-paper-oversample)) #fff, 0 0 0 calc(0.26rem * var(--ss-paper-oversample)) var(--ss-primary); }
.ss-px-badge:disabled { cursor: default; }
.ss-px-badge.is-active {
    background: var(--ss-primary);
    color: #fff;
    transform: scale(1.1);
    box-shadow: 0 calc(4px * var(--ss-paper-oversample)) calc(12px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.4);
}
.ss-paper-field-wrap.is-graded .ss-px-badge.is-active { background: var(--ss-primary); }

/* Shared-matching-option single-use lock — parity with Attempt.cshtml's .ss-opt-owner/.is-used-elsewhere.
   Still clickable (picking it steals it, see stealSharedOption in exam-paper-widgets.mjs); grey + a
   small owner badge are the only visual difference from an unused option. */
.ss-px-badge.is-used-elsewhere { color: rgba(23, 26, 33, 0.18); }
.ss-px-badge .ss-px-opt-owner {
    top: calc(-0.3rem * var(--ss-paper-oversample));
    right: calc(-0.3rem * var(--ss-paper-oversample));
    min-width: calc(1.1rem * var(--ss-paper-oversample));
    height: calc(1.1rem * var(--ss-paper-oversample));
    padding-inline: calc(0.22rem * var(--ss-paper-oversample));
    font-size: calc(0.55rem * var(--ss-paper-oversample));
}

/* ── Compact chip (mirrors the answer; opens the floating remote) ─────────────────────────────── */
.ss-px-chip {
    align-items: center;
    justify-content: center;
    gap: calc(0.4rem * var(--ss-paper-oversample));
    min-width: calc(3.4rem * var(--ss-paper-oversample));
    height: calc(2.3rem * var(--ss-paper-oversample));
    padding: 0 calc(0.85rem * var(--ss-paper-oversample));
    border-radius: 999px;
    border: calc(1.5px * var(--ss-paper-oversample)) solid var(--ss-paper-border);
    background: var(--ss-paper-glass);
    box-shadow: 0 calc(4px * var(--ss-paper-oversample)) calc(14px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.07);
    cursor: pointer;
    font: inherit;
    font-size: calc(0.95rem * var(--ss-paper-oversample));
    font-weight: 700;
    color: var(--ss-paper-muted);
    transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.2s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
.ss-px-chip:hover { border-color: var(--ss-primary); transform: translateY(-50%) translateY(-1px); box-shadow: 0 calc(8px * var(--ss-paper-oversample)) calc(20px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.15); }
.ss-px-chip:focus-visible { outline: none; box-shadow: 0 0 0 calc(0.14rem * var(--ss-paper-oversample)) #fff, 0 0 0 calc(0.3rem * var(--ss-paper-oversample)) var(--ss-primary); }
.ss-px-chip.is-current { border-color: var(--ss-primary); box-shadow: 0 0 0 calc(0.14rem * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.25), 0 calc(8px * var(--ss-paper-oversample)) calc(20px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.18); }
.ss-px-chip svg { width: calc(1.15rem * var(--ss-paper-oversample)); height: calc(1.15rem * var(--ss-paper-oversample)); }
.ss-px-chip-value { display: inline-flex; align-items: center; max-width: calc(9rem * var(--ss-paper-oversample)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ss-px-chip-hint { font-weight: 600; font-size: calc(0.8rem * var(--ss-paper-oversample)); color: rgba(102, 112, 133, 0.75); }
.ss-px-chip--yes { color: var(--ss-green); }
.ss-px-chip--no { color: var(--ss-danger); }
.ss-px-chip--ng { color: var(--ss-paper-muted); }
.ss-px-chip--text,
.ss-px-chip--choice { color: var(--ss-primary-strong); background: var(--ss-primary-soft); }
/* A pulse when the mirrored value changes from an external sync (cross-tab autosave poll). */
.ss-px-chip.ss-px-pulse { animation: ss-px-chip-pulse 0.5s cubic-bezier(0.16, 1, 0.3, 1); }
@keyframes ss-px-chip-pulse {
    0% { box-shadow: 0 0 0 0 rgba(59, 91, 255, 0.45); }
    100% { box-shadow: 0 0 0 calc(0.7rem * var(--ss-paper-oversample)) rgba(59, 91, 255, 0); }
}

/* ═══ Shape-driven math inputs (dev-planning/35 §4 A3; built by exam-paper-widgets.mjs) ═══════════
   Same rules as everything else on this page: fixed-light --ss-paper-* tokens only (these ride the
   white PDF sheet, never the app's dark-mode palette), every px/rem multiplied by
   var(--ss-paper-oversample), and the premium language of the blanks above — glass fill, blue→cyan
   gradient border, the app's signature cubic-bezier(0.16, 1, 0.3, 1) motion, all animation repeated
   in the prefers-reduced-motion block at the end of this section.

   Nothing here exists for an unshaped field: exam-paper-widgets.mjs only adds .ss-px-shaped (and the
   rail/composite inside it) when the wrap carries a data-answer-shape this build knows, so a language
   exam's paper is byte-identical to what it renders today. */

/* The suffix rail rides just right of the printed blank, holding the fixed unit/degree chip and the
   keypad-summon button. z-index 6 keeps it above the focused word-fill input (z-index 5), which pops
   wider over its own box — the printed unit must stay readable while the student types the number. */
/* `display` lives on the single-class rule, never on the .ss-is-positioned one: the remote-mode and
   bypass rules below are two-class selectors and have to be able to win it back (the same reason
   .ss-px-toggle/.ss-px-badges keep their display off the positioning rule). */
.ss-px-suffix-rail {
    display: inline-flex;
    align-items: center;
    white-space: nowrap;
    pointer-events: none;
}
.ss-paper-field-wrap.ss-is-positioned .ss-px-suffix-rail {
    position: absolute;
    left: 100%;
    top: 50%;
    transform: translateY(-50%);
    z-index: 6;
    gap: calc(0.2rem * var(--ss-paper-oversample));
    margin-left: calc(0.25rem * var(--ss-paper-oversample));
}
.ss-px-unit-chip {
    display: inline-flex;
    align-items: center;
    height: calc(1.35rem * var(--ss-paper-oversample));
    padding: 0 calc(0.45rem * var(--ss-paper-oversample));
    border-radius: 999px;
    border: calc(1px * var(--ss-paper-oversample)) solid var(--ss-paper-border);
    background: var(--ss-paper-glass);
    box-shadow: 0 calc(2px * var(--ss-paper-oversample)) calc(8px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.06);
    color: var(--ss-paper-muted);
    font-size: calc(0.78rem * var(--ss-paper-oversample));
    font-weight: 700;
    line-height: 1;
}
/* Answered blanks tint their fill; the unit chip follows so the pair reads as one control. */
.ss-paper-field-wrap.is-answered .ss-px-unit-chip {
    border-color: rgba(59, 91, 255, 0.45);
    color: var(--ss-primary-strong);
    background: var(--ss-primary-soft);
}
/* Summon: only while the blank has focus (never auto-opening anything), and never in remote mode,
   where the chip already IS the way into the remote. */
.ss-px-keypad-summon {
    display: none;
    align-items: center;
    justify-content: center;
    width: calc(1.5rem * var(--ss-paper-oversample));
    height: calc(1.5rem * var(--ss-paper-oversample));
    padding: 0;
    border-radius: calc(0.5rem * var(--ss-paper-oversample));
    border: calc(1px * var(--ss-paper-oversample)) solid rgba(59, 91, 255, 0.35);
    background: #fff;
    color: var(--ss-primary-strong);
    cursor: pointer;
    pointer-events: auto;
    box-shadow: 0 calc(3px * var(--ss-paper-oversample)) calc(10px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.18);
    transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
.ss-paper-field-wrap.ss-px-summon-visible:not(.ss-px-revealed) .ss-px-keypad-summon { display: inline-flex; animation: ss-px-summon-in 0.24s cubic-bezier(0.16, 1, 0.3, 1); }
.ss-px-keypad-summon svg { width: calc(0.95rem * var(--ss-paper-oversample)); height: calc(0.95rem * var(--ss-paper-oversample)); }
.ss-px-keypad-summon:hover { transform: translateY(calc(-1px * var(--ss-paper-oversample))); box-shadow: 0 calc(6px * var(--ss-paper-oversample)) calc(16px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.28); }
.ss-px-keypad-summon:focus-visible { outline: none; box-shadow: 0 0 0 calc(0.12rem * var(--ss-paper-oversample)) #fff, 0 0 0 calc(0.26rem * var(--ss-paper-oversample)) var(--ss-primary); }
@keyframes ss-px-summon-in {
    from { opacity: 0; transform: translateX(calc(-4px * var(--ss-paper-oversample))) scale(0.85); }
    to { opacity: 1; transform: none; }
}
/* Remote mode hands every field to the chip: no rail, no composite on the page. */
.ss-px-remote-mode .ss-px-suffix-rail { display: none; }
.ss-px-remote-mode .ss-px-composite { display: none; }

/* ── Composite blanks: fraction (stacked boxes + bar) and coordinate pair ("( x ; y )") ──────────
   The composite REPLACES the plain input while it is showing; when the carrier holds a value its
   notation cannot represent (a decimal typed before this widget existed), exam-paper-widgets.mjs
   sets .ss-px-composite-bypass and the plain input comes back untouched. */
.ss-px-composite { display: inline-flex; align-items: center; }
.ss-paper-field-wrap.ss-px-has-composite:not(.ss-px-composite-bypass).ss-is-positioned input.ss-paper-input[type="text"] { display: none; }
.ss-paper-field-wrap.ss-px-composite-bypass .ss-px-composite { display: none; }
.ss-paper-field-wrap.ss-is-positioned .ss-px-composite {
    gap: calc(0.2rem * var(--ss-paper-oversample));
    padding: calc(0.18rem * var(--ss-paper-oversample)) calc(0.35rem * var(--ss-paper-oversample));
    border-radius: calc(0.7rem * var(--ss-paper-oversample));
    border: calc(1.5px * var(--ss-paper-oversample)) solid transparent;
    background:
        linear-gradient(rgba(255, 255, 255, 0.94), rgba(255, 255, 255, 0.94)) padding-box,
        linear-gradient(120deg, rgba(59, 91, 255, 0.55), rgba(24, 199, 194, 0.45)) border-box;
    box-shadow: 0 calc(1px * var(--ss-paper-oversample)) calc(2px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.06),
        0 calc(6px * var(--ss-paper-oversample)) calc(16px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.08);
    transition: box-shadow 0.15s ease, background 0.15s ease;
}
.ss-paper-field-wrap.ss-is-positioned .ss-px-composite:hover {
    box-shadow: 0 calc(2px * var(--ss-paper-oversample)) calc(4px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.08),
        0 calc(8px * var(--ss-paper-oversample)) calc(22px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.14);
}
.ss-paper-field-wrap.is-answered.ss-is-positioned .ss-px-composite {
    background:
        linear-gradient(rgba(232, 236, 255, 0.92), rgba(232, 236, 255, 0.92)) padding-box,
        linear-gradient(120deg, var(--ss-primary), rgba(24, 199, 194, 0.8)) border-box;
}
.ss-paper-field-wrap.ss-is-positioned .ss-px-frac { flex-direction: column; gap: calc(0.1rem * var(--ss-paper-oversample)); }
.ss-px-frac-bar {
    display: block;
    width: 100%;
    height: calc(1.5px * var(--ss-paper-oversample));
    border-radius: 999px;
    background: var(--ss-paper-ink);
}
.ss-px-coord-paren,
.ss-px-coord-sep,
.ss-px-duration-sep {
    color: var(--ss-paper-muted);
    font-size: calc(0.95rem * var(--ss-paper-oversample));
    font-weight: 700;
    line-height: 1;
}
.ss-px-cell {
    width: calc(2.4rem * var(--ss-paper-oversample));
    height: calc(1.35rem * var(--ss-paper-oversample));
    padding: 0 calc(0.2rem * var(--ss-paper-oversample));
    border: calc(1px * var(--ss-paper-oversample)) solid var(--ss-paper-border);
    border-radius: calc(0.4rem * var(--ss-paper-oversample));
    background: rgba(255, 255, 255, 0.92);
    color: var(--ss-paper-ink);
    font: inherit;
    font-size: calc(0.85rem * var(--ss-paper-oversample));
    font-weight: 600;
    text-align: center;
    transition: box-shadow 0.15s ease, border-color 0.15s ease;
}
.ss-px-cell:focus {
    outline: none;
    border-color: var(--ss-primary);
    box-shadow: 0 0 0 calc(0.1rem * var(--ss-paper-oversample)) #fff, 0 0 0 calc(0.22rem * var(--ss-paper-oversample)) var(--ss-primary);
}
.ss-px-cell:read-only { cursor: default; }

/* ── Sequence pad: the printed letter grid, made typeable (dev-planning/35 §4 A4) ────────────────
   Same construction as the composites above — it REPLACES the plain input while showing, and
   .ss-px-sequence-bypass hands the blank back untouched when the carrier holds a value the grid
   cannot represent.

   The grid is the ONE control that stays on the page in remote mode too (live feedback round 1,
   issue 2). Remote mode hands every other field to a chip because the on-page control is fiddly
   under a pencil; a row of letter cells is the opposite of fiddly, and the chip's alternative is a
   composed WORD ("LED") — which is not what the paper prints, not what the student is arranging,
   and unreadable as an answer at a glance. Individual tiles are the clean state here, so the chip
   stands down for as long as the grid is showing. On the bypass the grid is gone and the chip comes
   back, which is what keeps an unrepresentable value reachable in remote mode.

   Sized to the spec box rather than allowed to overflow: the wrap's width IS the printed grid's
   width (the renderer sets it from data-width), so the cells divide it equally and land on the
   printed squares instead of drifting right of them. */
.ss-px-seq { display: inline-flex; align-items: center; }
.ss-paper-field-wrap.ss-px-has-sequence:not(.ss-px-sequence-bypass).ss-is-positioned input.ss-paper-input[type="text"] { display: none; }
.ss-paper-field-wrap.ss-px-sequence-bypass .ss-px-seq { display: none; }
.ss-px-remote-mode .ss-paper-field-wrap.ss-px-has-sequence:not(.ss-px-sequence-bypass) .ss-px-seq { display: inline-flex; }
.ss-px-remote-mode .ss-paper-field-wrap.ss-px-has-sequence:not(.ss-px-sequence-bypass) .ss-px-chip { display: none; }
.ss-paper-field-wrap.ss-is-positioned .ss-px-seq {
    /* No position of its own: the grid is an .ss-px-inline, so it is already absolutely positioned at
       the wrap's left edge — which is both what makes width: 100% mean "the spec box" and what makes
       it the containing block the summon below anchors to. Declaring `position: relative` here would
       silently win over that rule and unpin the grid from the printed blank. */
    width: 100%;
    box-sizing: border-box;
    gap: calc(0.22rem * var(--ss-paper-oversample));
    padding: calc(0.18rem * var(--ss-paper-oversample)) calc(0.3rem * var(--ss-paper-oversample));
    border-radius: calc(0.7rem * var(--ss-paper-oversample));
    border: calc(1.5px * var(--ss-paper-oversample)) solid transparent;
    background:
        linear-gradient(rgba(255, 255, 255, 0.94), rgba(255, 255, 255, 0.94)) padding-box,
        linear-gradient(120deg, rgba(59, 91, 255, 0.55), rgba(24, 199, 194, 0.45)) border-box;
    box-shadow: 0 calc(1px * var(--ss-paper-oversample)) calc(2px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.06),
        0 calc(6px * var(--ss-paper-oversample)) calc(16px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.08);
    transition: box-shadow 0.15s ease, background 0.15s ease;
}
.ss-paper-field-wrap.is-answered.ss-is-positioned .ss-px-seq {
    background:
        linear-gradient(rgba(232, 236, 255, 0.92), rgba(232, 236, 255, 0.92)) padding-box,
        linear-gradient(120deg, var(--ss-primary), rgba(24, 199, 194, 0.8)) border-box;
}
/* A single letter, not a word — narrower and centred, unlike the composite's number cells. The
   width is the floor a lone cell falls back to (the remote's copy of the grid is not box-sized);
   on the page the cells share the spec box equally instead, which is what puts them on the printed
   squares. */
.ss-px-seq-cell {
    width: calc(1.3rem * var(--ss-paper-oversample));
    text-transform: uppercase;
    font-weight: 700;
}
.ss-paper-field-wrap.ss-is-positioned .ss-px-seq-cell {
    flex: 1 1 0;
    width: auto;
    min-width: 0;
}

/* ── Set: closed-alphabet pills (dev-planning/44 §2 Gap 1) ───────────────────────────────────────
   Same "replace the blank" contract as the sequence grid above (.ss-px-has-set-pills hides the
   plain input the identical way .ss-px-has-sequence does), but a variable-count row rather than a
   fixed cell count, so it wraps instead of sharing the spec box in equal fixed slots. */
.ss-paper-field-wrap.ss-px-has-set-pills.ss-is-positioned input.ss-paper-input[type="text"] { display: none; }
.ss-px-set-pills {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: calc(0.25rem * var(--ss-paper-oversample));
    width: 100%;
    box-sizing: border-box;
    padding: calc(0.15rem * var(--ss-paper-oversample));
}
.ss-px-set-pill {
    min-width: calc(1.6rem * var(--ss-paper-oversample));
    padding: calc(0.15rem * var(--ss-paper-oversample)) calc(0.4rem * var(--ss-paper-oversample));
    border: calc(1px * var(--ss-paper-oversample)) solid var(--ss-paper-border);
    border-radius: calc(999px * var(--ss-paper-oversample));
    background: rgba(255, 255, 255, 0.92);
    color: var(--ss-paper-ink);
    font: inherit;
    font-size: calc(0.8rem * var(--ss-paper-oversample));
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.ss-px-set-pill:hover { border-color: var(--ss-primary); }
.ss-px-set-pill:disabled { cursor: default; opacity: 0.85; }
.ss-px-set-pill.is-active {
    background: var(--ss-primary);
    border-color: var(--ss-primary);
    color: #fff;
}

/* ── Inline fractions, display only (live feedback round 2, issue 3) ─────────────────────────────
   nicefrac in three elements: a raised numerator, U+2044 leaning between them, a lowered
   denominator, all on one line. <sup>/<sub> already carry the raise and the drop, so the wrapper
   stays `inline` — an inline-flex here would flatten exactly the effect this exists for. Sized in em
   so it inherits whatever oversampled font the chip, face or note around it already resolved to;
   nothing here is a fixed px length, which is why it needs no --ss-paper-oversample of its own. */
.ss-frac {
    display: inline;
    white-space: nowrap;
    font-variant-numeric: lining-nums tabular-nums;
}
.ss-frac-num,
.ss-frac-den {
    font-size: 0.78em;
    line-height: 1;
}
.ss-frac-slash {
    /* Slightly tighter than the character's own advance: the numerator and denominator lean into it,
       which is what stops "8 ⁄ 5" reading as three separate glyphs. */
    margin: 0 -0.04em;
    font-size: 1.05em;
}
/* The mirror's face replaces the plain readout while it is showing, and hands it back on the bypass
   (a value that is not a bare fraction — a decimal, a mixed number, anything the student typed). */
.ss-paper-field-wrap.ss-px-has-frac-face:not(.ss-px-frac-plain).ss-is-positioned input.ss-paper-input[type="text"] { display: none; }
.ss-paper-field-wrap.ss-px-frac-plain .ss-px-frac-face { display: none; }
.ss-paper-field-wrap.ss-is-positioned .ss-px-frac-face {
    padding: calc(0.1rem * var(--ss-paper-oversample)) calc(0.3rem * var(--ss-paper-oversample));
    color: var(--ss-paper-ink);
    font-size: calc(0.9rem * var(--ss-paper-oversample));
    font-weight: 700;
    line-height: 1;
}

/* ── The read-only surfaces' letter tiles (live feedback round 2, issue 2) ───────────────────────
   The teacher's LiveCompanion mirror and the grading paper render the same grid server-side
   (SequenceAnswerFace) out of SPANS, because nothing on those pages is typeable. A span has none of
   an input's intrinsic box, so the letter has to be centred explicitly; everything else — the cell
   border, the fixed-light paper tokens, the equal share of the spec box — comes from the same rules
   the student's own grid uses, which is the whole point of reusing the classes. */
span.ss-px-seq-cell {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: calc(1.35rem * var(--ss-paper-oversample));
    overflow: hidden;
}
/* The grading paper's overlay is a flex row carrying the status LED as well, so its tiles stay in
   that flow instead of being pinned to the wrap's left edge at full box width. */
.ss-px-seq-face--flow {
    display: inline-flex;
    align-items: center;
    gap: calc(0.15rem * var(--ss-paper-oversample));
    min-width: 0;
}

/* The letter-pad summon rides inside the grid (see buildSequenceSummon), so it needs the rail's
   pointer-events grant without the rail. Slightly smaller than the A3 one: it sits between cells,
   not out in the margin. Taken OUT of the flow once positioned, into the margin just right of the
   grid: the cells now share the box width, so a summon appearing on focus would squeeze every one
   of them sideways mid-answer. Same left: 100% anchor A3's suffix rail uses — safe here now that
   the grid is box-width and no longer overflows past it. */
.ss-px-seq-summon {
    pointer-events: auto;
    width: calc(1.25rem * var(--ss-paper-oversample));
    height: calc(1.25rem * var(--ss-paper-oversample));
    margin-left: calc(0.15rem * var(--ss-paper-oversample));
}
.ss-paper-field-wrap.ss-is-positioned .ss-px-seq-summon {
    position: absolute;
    left: 100%;
    top: 50%;
    /* Half its own height as a negative margin, deliberately NOT translateY(-50%): the shared
       ss-px-summon-in keyframe ends on `transform: none`, so a baseline transform here would be
       blanked out for the length of the entrance and the button would jump. This way the one
       keyframe (and the one reduced-motion rule that switches it off) keeps working unchanged. */
    margin-top: calc(-0.625rem * var(--ss-paper-oversample));
    margin-left: calc(0.3rem * var(--ss-paper-oversample));
}

/* Decision 7's duplicate warning: quiet amber, fixed-light paper tokens only (this rides the white
   PDF sheet). Deliberately a tint and a shadow, never a red "wrong" treatment — the page does not
   know, and must never look like it knows, whether either arrangement is valid. The same class lands
   on the compact chip so the warning survives remote mode. */
.ss-paper-field-wrap.ss-px-seq-duplicate.ss-is-positioned .ss-px-seq {
    background:
        linear-gradient(rgba(255, 247, 230, 0.96), rgba(255, 247, 230, 0.96)) padding-box,
        linear-gradient(120deg, rgba(214, 148, 24, 0.85), rgba(214, 148, 24, 0.5)) border-box;
    box-shadow: 0 calc(1px * var(--ss-paper-oversample)) calc(2px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.06),
        0 calc(6px * var(--ss-paper-oversample)) calc(16px * var(--ss-paper-oversample)) rgba(214, 148, 24, 0.16);
}
.ss-paper-field-wrap.ss-px-seq-duplicate .ss-px-seq-cell { color: #7A5205; }
.ss-px-chip.ss-px-seq-duplicate {
    border-color: rgba(214, 148, 24, 0.7);
    background: rgba(255, 247, 230, 0.96);
    color: #7A5205;
}

/* ── Ink-first free-work zones (dev-planning/35 §2 decision 10; §4 A5) ───────────────────────────
   A worked solution is written in ink. The carrier (textarea, or a one-line input for the `drawing`
   fields) stays in the DOM and stays the value; what the student sees is a work AREA instead of an
   open box: a whisper-thin dashed outline, a tiny corner label, and a two-button rail in the opposite
   corner. Same house rules as every other on-page widget — fixed-light --ss-paper-* tokens only, every
   px/rem multiplied by var(--ss-paper-oversample), motion repeated in the reduced-motion block.

   POINTER-EVENTS, the load-bearing part: a free-work spec box is large, and the positioned field
   wraps re-assert pointer-events: auto (site.css ~392) so their inputs keep receiving events. On this
   shape that would make the wrap swallow every pencil stroke crossing the work area — the exact
   opposite of "ink-first". So the wrap drops to `none` here and only the two buttons take it back.
   Both are real <button>s, which exam-ink-engine.mjs's combined pointer controller already treats as
   interactive and skips ink handling for, so ink over the zone behaves exactly like ink over blank
   paper with no ink-engine change at all. */
.ss-paper-field-wrap.ss-px-freework { pointer-events: none; }
/* The carrier is hidden, never removed — it still posts as ans_{FieldKey} and still drives autosave.
   Same pair of selectors (input AND textarea) because decision 10 covers both controls. */
.ss-paper-field-wrap.ss-px-freework.ss-is-positioned input.ss-paper-input[type="text"],
.ss-paper-field-wrap.ss-px-freework.ss-is-positioned textarea.ss-paper-input { display: none; }

.ss-px-fw-zone { display: none; }
.ss-paper-field-wrap.ss-px-freework.ss-is-positioned .ss-px-fw-zone {
    display: block;
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    border: calc(1px * var(--ss-paper-oversample)) dashed rgba(23, 26, 33, 0.16);
    border-radius: calc(0.6rem * var(--ss-paper-oversample));
    /* Barely-there: this box can cover a third of a page, so anything more than a whisper competes
       with the student's own working. Same reasoning as the presence view frame above. */
    background: rgba(59, 91, 255, 0.014);
    transition: border-color 0.2s ease, background 0.2s ease;
}
.ss-px-fw-label {
    position: absolute;
    top: calc(0.15rem * var(--ss-paper-oversample));
    left: calc(0.35rem * var(--ss-paper-oversample));
    color: var(--ss-paper-muted);
    font-size: calc(0.55rem * var(--ss-paper-oversample));
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: lowercase;
    opacity: 0.55;
    transition: opacity 0.2s ease;
}
/* Once the zone carries something this module knows about — a typed value, or a photo it anchored —
   the hint steps further back. Ink is deliberately NOT a trigger: strokes live on the page-level ink
   layer and the zone never goes looking for them (see the widget script's section header). */
.ss-paper-field-wrap.ss-px-freework.ss-px-fw-quiet.ss-is-positioned .ss-px-fw-zone {
    border-color: rgba(23, 26, 33, 0.08);
    background: none;
}
.ss-paper-field-wrap.ss-px-freework.ss-px-fw-quiet .ss-px-fw-label { opacity: 0.25; }

/* The affordance rail: bottom-LEFT, anchored at the spec box's left edge and free to overflow to the
   right — dev-planning/20's hard constraint for every widget wider than its printed blank, which a
   short work-area box makes reachable here too. Vertically it sits at the far end from the corner
   label, and horizontally it never reaches the top-right result badge.
   One rail rather than two independently-anchored controls, so a narrow spec box cannot make the chip
   and the photo button overlap. The rail passes pointers through; its children take them. */
.ss-px-fw-tools { display: none; }
.ss-paper-field-wrap.ss-px-freework.ss-is-positioned .ss-px-fw-tools {
    display: inline-flex;
    position: absolute;
    left: calc(0.2rem * var(--ss-paper-oversample));
    bottom: calc(0.2rem * var(--ss-paper-oversample));
    z-index: 4;
    align-items: center;
    gap: calc(0.25rem * var(--ss-paper-oversample));
    white-space: nowrap;
    pointer-events: none;
}
.ss-paper-field-wrap.ss-px-freework .ss-px-fw-tools > * { pointer-events: auto; }
/* The chip lives in the rail, so it gives up the .ss-px-chip absolute positioning (and with it the
   translateY(-50%) baseline the pop keyframe below has to account for). Narrower too: on a free-work
   zone it is the typed FALLBACK, not the way to answer, and should not read as the main control. */
.ss-paper-field-wrap.ss-px-freework.ss-is-positioned .ss-px-chip {
    position: static;
    transform: none;
    min-width: 0;
    height: calc(1.6rem * var(--ss-paper-oversample));
    padding: 0 calc(0.55rem * var(--ss-paper-oversample));
    font-size: calc(0.72rem * var(--ss-paper-oversample));
}
.ss-paper-field-wrap.ss-px-freework.ss-is-positioned .ss-px-chip:hover {
    transform: translateY(calc(-1px * var(--ss-paper-oversample)));
}
.ss-paper-field-wrap.ss-px-freework .ss-px-chip-value { max-width: calc(7rem * var(--ss-paper-oversample)); }

/* Ádám's amendment to decision 10: the photo button. Same size and language as A3's keypad summon,
   but always visible rather than focus-gated — there is no blank to focus here, so a hidden-until-
   focus affordance would be unreachable. Gone entirely on a revealed field (.ss-px-revealed, set by
   setFieldRevealedReadOnly) and never built at all when the attempt is read-only or the page has no
   image-write grant. */
.ss-px-fw-photo {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: calc(1.6rem * var(--ss-paper-oversample));
    height: calc(1.6rem * var(--ss-paper-oversample));
    padding: 0;
    border-radius: calc(0.5rem * var(--ss-paper-oversample));
    border: calc(1px * var(--ss-paper-oversample)) solid rgba(59, 91, 255, 0.32);
    background: rgba(255, 255, 255, 0.92);
    color: var(--ss-primary-strong);
    cursor: pointer;
    box-shadow: 0 calc(2px * var(--ss-paper-oversample)) calc(8px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.16);
    transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
.ss-px-fw-photo svg { width: calc(1rem * var(--ss-paper-oversample)); height: calc(1rem * var(--ss-paper-oversample)); }
.ss-px-fw-photo:hover {
    transform: translateY(calc(-1px * var(--ss-paper-oversample)));
    box-shadow: 0 calc(6px * var(--ss-paper-oversample)) calc(16px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.26);
}
.ss-px-fw-photo:focus-visible { outline: none; box-shadow: 0 0 0 calc(0.12rem * var(--ss-paper-oversample)) #fff, 0 0 0 calc(0.26rem * var(--ss-paper-oversample)) var(--ss-primary); }
.ss-paper-field-wrap.ss-px-revealed .ss-px-fw-photo { display: none; }

/* Remote mode keeps the zone: it is already the clean-for-Pencil state that mode exists to produce,
   and hiding it would take the anchored photo upload away with it. Only the on-page controls of the
   OTHER shapes are swapped for the chip there, and this chip is on the page in both modes. */

/* The remote's own multi-line editor for a free-work field. Taller than the generic textarea variant
   (a worked solution is paragraphs, not a sentence) and monospace-ish line height so a typed
   calculation stays readable. Not oversample-scaled — the remote lives outside the zoom stage. */
.ss-px-remote-freework { height: 11rem; line-height: 1.5; }

/* ── The math keypad (inside the floating remote only) ───────────────────────────────────────────
   The remote is appended to <body>, OUTSIDE the transformed zoom stage, so — like every other rule
   in the remote block below — nothing here is oversample-scaled and theme tokens are allowed. */
.ss-px-keypad {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 0.4rem;
    margin-top: 0.7rem;
}
.ss-px-key {
    min-height: 2.9rem;
    border-radius: 0.9rem;
    border: 1px solid var(--ss-accent-border);
    background: var(--ss-surface, #fff);
    color: var(--ss-ink);
    font-family: inherit;
    font-size: 1.15rem;
    font-weight: 700;
    cursor: pointer;
    transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.2s cubic-bezier(0.16, 1, 0.3, 1), background 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
.ss-px-key:hover { border-color: var(--ss-primary); background: var(--ss-surface-lavender); }
.ss-px-key:active { transform: scale(0.94); }
.ss-px-key:focus-visible { outline: none; box-shadow: 0 0 0 0.12rem #fff, 0 0 0 0.26rem var(--ss-primary); }
.ss-px-key--backspace,
.ss-px-key--clear { background: var(--ss-primary-soft); color: var(--ss-primary-strong); border-color: transparent; }
.ss-px-key--advance { background: var(--ss-surface-lavender); color: var(--ss-primary-strong); }

/* A tall pad (formula has 28 keys as of doc 44 §5 D2's algebra layout) must never push the
   prev/next foot off the screen — that is now the card's own max-height plus the scrolling
   .ss-px-remote-body above, which does the same job for every body rather than only for the
   keypad one. */

/* The remote's own copy of the composite + unit chip: same control, app-shell sizing. */
.ss-px-remote .ss-px-frac,
.ss-px-remote .ss-px-coord,
.ss-px-remote .ss-px-duration {
    display: inline-flex;
    align-self: flex-start;
    align-items: center;
    gap: 0.35rem;
    padding: 0.5rem 0.7rem;
    border: 1.5px solid var(--ss-accent-border);
    border-radius: 1rem;
    background: var(--ss-surface, #fff);
}
.ss-px-remote .ss-px-frac { flex-direction: column; gap: 0.25rem; }
.ss-px-remote .ss-px-frac-bar { height: 2px; background: var(--ss-ink); }
.ss-px-remote .ss-px-coord-paren,
.ss-px-remote .ss-px-coord-sep,
.ss-px-remote .ss-px-duration-sep { color: var(--ss-muted); font-size: 1.2rem; }
/* Re-declares every oversampled length from the on-page .ss-px-cell above: --ss-paper-oversample is
   set on <html>, so it would otherwise leak into the remote, which is not zoom-staged. */
.ss-px-remote .ss-px-cell {
    width: 4.5rem;
    height: 2.8rem;
    padding: 0 0.5rem;
    border: 1.5px solid var(--ss-accent-border);
    border-radius: 0.8rem;
    background: var(--ss-surface, #fff);
    color: var(--ss-ink);
    font-size: 1.15rem;
    font-weight: 600;
}
.ss-px-remote .ss-px-cell:focus { border-color: var(--ss-primary); box-shadow: 0 0 0 0.2rem rgba(59, 91, 255, 0.18); }
/* The remote's copy of the sequence grid, and its letter pad. Same "not oversample-scaled, theme
   tokens allowed" rule as everything else inside the remote. The pad auto-fits its columns because an
   alphabet is 4-5 letters plus two action keys, not a fixed calculator block. */
.ss-px-remote .ss-px-seq {
    display: inline-flex;
    align-self: flex-start;
    align-items: center;
    gap: 0.35rem;
    padding: 0.5rem 0.7rem;
    border: 1.5px solid var(--ss-accent-border);
    border-radius: 1rem;
    background: var(--ss-surface, #fff);
}
.ss-px-remote .ss-px-seq-cell { width: 2.8rem; text-transform: uppercase; font-weight: 700; }
.ss-px-keypad--letters { grid-template-columns: repeat(auto-fit, minmax(3.2rem, 1fr)); }
.ss-px-remote-unit {
    align-self: flex-start;
    padding: 0.15rem 0.55rem;
    border-radius: 999px;
    background: var(--ss-primary-soft);
    color: var(--ss-primary-strong);
    font-size: 0.85rem;
    font-weight: 700;
}

/* ═══ Floating remote — appended to <body>, OUTSIDE the transformed zoom stage ══════════════════
   position: fixed needs a non-transformed ancestor, so this lives on document.body and is NOT
   oversample-scaled. It floats over the app shell, so (unlike the on-page widgets) it uses the
   app's own theme tokens, exactly like the list-view answer controls. */
.ss-px-remote {
    position: fixed;
    z-index: 1050;
    /* Same reason as .ss-paper-toolbar: fixed, so no scroll container's touch-action reaches it. */
    touch-action: pan-x pan-y;
    /* Width is picked per field by remoteSizeForField (data-size, just below) — see the three
       widths there. This base width is the "sm" case and stays the default for anything that
       renders before/without a size. Kept in px, like every width here: the root font-size is
       viewport-dependent (14px/15px), and the card's job is to be a fixed hand-sized panel. */
    width: 320px;
    max-width: calc(100vw - 1.5rem);
    /* Height follows the content up to the viewport, and only then does the BODY scroll (head and
       foot stay put). Before this the option list carried its own 45vh cap, so a long matching list
       was cropped mid-word inside a card that still had half the screen free above it.
       --ss-keyboard-inset (exam-keyboard-inset.mjs) is the strip the iOS software keyboard is
       covering, 0px whenever there is no keyboard: positionRemote lifts the card by that much, so
       the height it may grow to has to shrink by the same amount or a tall card would be pushed off
       the TOP of the screen instead of off the bottom. */
    max-height: min(720px, calc(100vh - 40px - var(--ss-keyboard-inset, 0px)));
    border-radius: 28px;
    background: var(--ss-glass-bg-strong, rgba(255, 255, 255, 0.92));
    -webkit-backdrop-filter: var(--ss-glass-blur, saturate(180%) blur(18px));
    backdrop-filter: var(--ss-glass-blur, saturate(180%) blur(18px));
    border: 1.5px solid var(--ss-glass-border, var(--ss-border));
    box-shadow: 0 24px 60px rgba(23, 26, 33, 0.24);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    opacity: 0;
    transform: scale(0.96);
    transition: transform 0.32s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.32s cubic-bezier(0.16, 1, 0.3, 1);
}
/* The three widths remoteSizeForField chooses between. "md" fits a short phrase per option on one
   line; "lg" fits a full sentence, which is what a matching list's options are. Both stay under the
   max-width above, so a phone still gets an edge-to-edge card instead of one that hangs off. */
.ss-px-remote[data-size="md"] { width: 420px; }
.ss-px-remote[data-size="lg"] { width: 540px; }
.ss-px-remote[hidden] { display: none; }
.ss-px-remote.is-open { opacity: 1; transform: none; }
.ss-px-remote.is-closing { transition-duration: 0.18s; opacity: 0; transform: scale(0.92); }
.ss-px-remote-head {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.85rem 1.1rem 0.7rem;
    background: linear-gradient(120deg, rgba(59, 91, 255, 0.10), rgba(255, 77, 166, 0.06) 55%, rgba(24, 199, 194, 0.10));
    cursor: grab;
    user-select: none;
    -webkit-user-select: none;
}
.ss-px-remote-head:active { cursor: grabbing; }
.ss-px-remote-no {
    width: 2rem;
    height: 2rem;
    border-radius: 0.65rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--ss-primary-soft);
    color: var(--ss-primary-strong);
    font-weight: 800;
    font-size: 0.95rem;
    flex: 0 0 auto;
}
.ss-px-remote-label {
    flex: 1 1 auto;
    font-size: 0.8rem;
    line-height: 1.3;
    color: var(--ss-muted);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.ss-px-remote-close {
    border: none;
    background: rgba(23, 26, 33, 0.06);
    color: var(--ss-muted);
    width: 1.7rem;
    height: 1.7rem;
    border-radius: 50%;
    cursor: pointer;
    font-size: 1.05rem;
    line-height: 1;
    flex: 0 0 auto;
}
.ss-px-remote-close:hover { background: rgba(23, 26, 33, 0.12); }
/* The one scroll container in the card: it takes whatever height is left under the card's own
   max-height, so head and foot (prev/next, progress) are never pushed off the screen by a tall
   keypad or a 14-option list. overflow-x stays hidden — the prev/next slide animation translates
   the controls sideways and depends on being clipped. */
.ss-px-remote-body {
    padding: 1rem 1.1rem 0.9rem;
    flex: 1 1 auto;
    min-height: 0;
    overflow-x: hidden;
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
}
.ss-px-remote-tfng {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.6rem;
    background: var(--ss-surface, #fff);
    border: 1.5px solid var(--ss-border);
    border-radius: 24px;
    padding: 0.7rem;
    box-shadow: 0 12px 40px rgba(23, 26, 33, 0.04);
}
.ss-px-remote-opt-tfng {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.35rem;
    padding: 0.7rem 0.2rem;
    border-radius: 18px;
    border: none;
    background: transparent;
    cursor: pointer;
    color: rgba(23, 26, 33, 0.32);
    font-family: inherit;
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    transition: transform 0.28s cubic-bezier(0.16, 1, 0.3, 1), color 0.28s cubic-bezier(0.16, 1, 0.3, 1);
}
.ss-px-remote-opt-tfng svg { width: 2.3rem; height: 2.3rem; transition: inherit; }
.ss-px-remote-opt-tfng:hover svg { transform: scale(1.14); color: var(--ss-ink); }
.ss-px-remote-opt-tfng:focus-visible { outline: none; box-shadow: 0 0 0 0.12rem #fff, 0 0 0 0.26rem var(--ss-primary); }
.ss-px-remote-tfng.has-answer .ss-px-remote-opt-tfng:not(.is-active) svg { color: rgba(23, 26, 33, 0.12); transform: scale(0.85); }
.ss-px-remote-opt-tfng.is-active.ss-px-seg--yes svg { color: var(--ss-green); filter: drop-shadow(0 0 14px rgba(24, 160, 88, 0.7)); transform: scale(1.16); }
.ss-px-remote-opt-tfng.is-active.ss-px-seg--no svg { color: var(--ss-danger); filter: drop-shadow(0 0 14px rgba(229, 72, 77, 0.7)); transform: scale(1.16); }
.ss-px-remote-opt-tfng.is-active.ss-px-seg--ng svg { color: var(--ss-muted); filter: drop-shadow(0 0 14px rgba(102, 112, 133, 0.7)); transform: scale(1.16); }
/* No max-height of its own: the list grows to its content and .ss-px-remote-body is the single
   scroll container (a nested one cropped the first option mid-line while the card had room left). */
.ss-px-remote-opts { display: flex; flex-direction: column; gap: 0.5rem; }
.ss-px-remote-opt {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.65rem;
    min-height: 2.9rem;
    padding: 0.6rem 0.8rem;
    border: 1px solid var(--ss-accent-border);
    border-radius: 1rem;
    background: var(--ss-surface-lavender);
    color: var(--ss-ink);
    font-family: inherit;
    font-size: 0.95rem;
    text-align: left;
    cursor: pointer;
    transition: border-color 0.12s ease, background 0.12s ease;
}
.ss-px-remote-opt:hover { border-color: var(--ss-primary); }
.ss-px-remote-opt:focus-visible { outline: none; box-shadow: 0 0 0 0.1rem #fff, 0 0 0 0.25rem var(--ss-primary); }
.ss-px-remote-opt-key {
    flex: 0 0 auto;
    width: 1.8rem;
    height: 1.8rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 0.6rem;
    background: var(--ss-primary-soft);
    color: var(--ss-primary-strong);
    font-size: 0.85rem;
    font-weight: 700;
}
/* An option label can be a whole sentence: give it a readable wrap rather than a tight one, and let
   an over-long unbroken token break instead of widening the card past its max-width. */
.ss-px-remote-opt-text { flex: 1 1 auto; min-width: 0; line-height: 1.4; overflow-wrap: anywhere; }
.ss-px-remote-opt.is-active { background: var(--ss-primary); border-color: var(--ss-primary); color: #fff; }
.ss-px-remote-opt.is-active .ss-px-remote-opt-key { background: rgba(255, 255, 255, 0.22); color: #fff; }

/* Shared-matching-option single-use lock — parity with Attempt.cshtml's .ss-opt-owner/.is-used-elsewhere.
   Still clickable (picking it steals it, see stealSharedOption in exam-paper-widgets.mjs); grey + a
   small owner badge naming the sibling question are the only visual difference from an unused option. */
.ss-px-remote-opt.is-used-elsewhere {
    background: #eef0f5;
    border-color: #d7dce8;
    color: #6b7280;
    opacity: 0.72;
}
.ss-px-remote-opt.is-used-elsewhere:hover { opacity: 0.9; border-color: var(--ss-primary); }
.ss-px-remote-opt.is-used-elsewhere .ss-px-remote-opt-key { background: rgba(23, 26, 33, 0.08); color: #6b7280; }
.ss-px-opt-owner {
    position: absolute;
    top: 0.4rem;
    right: 0.5rem;
    display: none;
    min-width: 1.6rem;
    height: 1.35rem;
    padding-inline: 0.32rem;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    background: #fff;
    color: var(--ss-primary-strong);
    border: 1px solid #d7dce8;
    font-size: 0.72rem;
    font-weight: 800;
    box-shadow: 0 4px 12px rgba(36, 47, 92, 0.10);
}
.is-used-elsewhere .ss-px-opt-owner { display: inline-flex; }
/* In the remote the badge is a real flex item, not an overlay: the on-paper option is a short chip
   with room in its corner, but a remote option is a wrapped sentence and the badge sat on top of
   its first line. Still hidden until .is-used-elsewhere, so it costs an unused option nothing. */
.ss-px-remote-opt .ss-px-opt-owner {
    position: static;
    flex: 0 0 auto;
    align-self: center;
    box-shadow: none;
}
.ss-px-remote-text-wrap { display: flex; flex-direction: column; gap: 0.5rem; }
.ss-px-remote-text {
    font: inherit;
    font-size: 1.15rem;
    font-weight: 600;
    color: var(--ss-ink);
    /* There is no global border-box reset here, so width:100% + this padding + border used to run
       ~33px past the card and lose its right border to the body's clip. */
    box-sizing: border-box;
    width: 100%;
    height: 3.2rem;
    padding: 0 1rem;
    border-radius: 1rem;
    border: 1.5px solid var(--ss-accent-border);
    background: var(--ss-surface, #fff);
}
.ss-px-remote-textarea { height: 6rem; padding: 0.6rem 1rem; resize: vertical; }
.ss-px-remote-text:focus { outline: none; border-color: var(--ss-primary); box-shadow: 0 0 0 0.2rem rgba(59, 91, 255, 0.18); }
.ss-px-remote-hint { font-size: 0.75rem; color: var(--ss-muted); }
.ss-px-remote-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.6rem 1.1rem 0.9rem;
    border-top: 1px solid transparent;
}
/* Only when the body really scrolls (class set by syncRemoteOverflow): the hairline is what tells a
   student that the half-row above the foot continues, instead of looking like a crop. */
.ss-px-remote.is-scrollable .ss-px-remote-foot { border-top-color: var(--ss-glass-border, var(--ss-border)); }
.ss-px-remote-nav {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.3rem;
    height: 2.3rem;
    border-radius: 50%;
    border: 1px solid var(--ss-border);
    background: var(--ss-surface, #fff);
    color: var(--ss-ink);
    font-size: 1.15rem;
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease;
}
.ss-px-remote-nav:hover { border-color: var(--ss-primary); color: var(--ss-primary-strong); }
.ss-px-remote-nav:focus-visible { outline: none; box-shadow: 0 0 0 0.12rem #fff, 0 0 0 0.26rem var(--ss-primary); }
.ss-px-remote-progress { font-size: 0.8rem; color: var(--ss-muted); font-variant-numeric: tabular-nums; }

/* Directional slide-fade when stepping prev/next, and a pop + glow on the freshly-picked control. */
.ss-px-slide-next { animation: ss-px-slide-next 0.24s cubic-bezier(0.16, 1, 0.3, 1); }
.ss-px-slide-prev { animation: ss-px-slide-prev 0.24s cubic-bezier(0.16, 1, 0.3, 1); }
@keyframes ss-px-slide-next { from { opacity: 0; transform: translateX(16px); } to { opacity: 1; transform: none; } }
@keyframes ss-px-slide-prev { from { opacity: 0; transform: translateX(-16px); } to { opacity: 1; transform: none; } }
.ss-px-picked { animation: ss-px-picked 0.4s cubic-bezier(0.16, 1, 0.3, 1); }
@keyframes ss-px-picked {
    0% { transform: scale(1); }
    45% { transform: scale(1.06); }
    100% { transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
    .ss-px-seg,
    .ss-px-badge,
    .ss-px-chip,
    .ss-px-remote,
    .ss-px-remote-opt-tfng,
    .ss-px-remote-opt,
    .ss-px-remote-nav,
    /* dev-planning/35 §4 A3 — every new control's motion is dropped here too. */
    .ss-px-keypad-summon,
    .ss-px-key,
    .ss-paper-field-wrap.ss-is-positioned .ss-px-composite,
    /* dev-planning/35 §4 A4 — the sequence grid's tint transition goes with them. */
    .ss-paper-field-wrap.ss-is-positioned .ss-px-seq,
    /* dev-planning/35 §4 A5 — the free-work zone's fade, its label's, and its photo affordance. */
    .ss-paper-field-wrap.ss-px-freework.ss-is-positioned .ss-px-fw-zone,
    .ss-px-fw-label,
    .ss-px-fw-photo,
    .ss-px-cell { transition: none; }
    .ss-px-fw-photo:hover,
    .ss-paper-field-wrap.ss-px-freework.ss-is-positioned .ss-px-chip:hover { transform: none; }
    .ss-px-chip.ss-px-pulse,
    .ss-px-slide-next,
    .ss-px-slide-prev,
    .ss-px-picked { animation: none; }
    .ss-paper-field-wrap.ss-px-summon-visible .ss-px-keypad-summon { animation: none; }
    .ss-px-key:active { transform: none; }
    .ss-px-remote { transition: none; }
}

/* ═══ Answer-correctness reveal — the verdict glow layer (dev-planning/22; geometry reworked in
   dev-planning/24) ══════════════════════════════════════════════════════════════════════════════
   When the teacher reveals an exercise (or homework/remote results are published), the affected
   field's CONTAINER (.ss-paper-field-wrap, .ss-qfield, or .ss-paper-field-wrap--mirror) gains one
   ss-verdict-<status> class carrying two custom properties: --ss-verdict-color (the accent) and
   --ss-verdict-glow (the soft outer bloom). This is unchanged from dev-planning/22 and stays the
   single source of truth every surface (including the floating remote, see below) reads from.

   dev-planning/24 finding: the container is sized to the PRINTED BLANK / the whole question CARD,
   not to the visible answer control living inside it (a compact chip, a TFNG toggle, a badge row,
   or a word-fill input can all be far smaller — or, focused, far larger — than their container).
   Painting the ring/glow on the container itself is what produced the "double width" / "sits behind
   the collapsed look" / "reads as absent on the card" reports. Fix: the custom properties stay on
   the container (CSS custom properties inherit to descendants for free), but every box-shadow that
   actually PAINTS a ring/glow now targets the live visible-control element via a descendant
   selector, chosen per surface:
     - .ss-paper-field-wrap (on-page paper widgets, AttemptPaper.cshtml) → .ss-px-chip / .ss-px-toggle
       / .ss-px-badges / the word-fill input or textarea — whichever CSS's own display:none rules
       currently show. All of them get the class (CSS decides which one paints); none of it needs
       exam-paper-widgets.mjs to know or care which mode (inline vs. remote-mode vs. chip-primary)
       is currently active.
     - .ss-qfield (card-carousel / list view, Attempt.cshtml) → .ss-qfield-input (the answer-widget
       area only — marker + prompt are excluded).
     - .ss-paper-field-wrap--mirror (teacher LiveCompanion) → .ss-paper-choice-display or the
       readonly input/textarea.
   None of this needs any exam-paper-widgets.mjs / Attempt.cshtml / LiveCompanion.cshtml JS change —
   every one of those scripts already stamps ss-verdict-<status> on exactly the container elements
   this section now reads from; only the paint target moved.

   Composing trap (do not reintroduce): every one of these controls already carries meaningful
   box-shadow of its own (glass fill, hover lift, focus ring, TFNG/badge base shadow) — box-shadow
   does not stack across separate CSS rules, so a verdict rule that only sets a bare ring/glow would
   silently replace (not add to) those layers, most importantly killing the focus ring on a revealed-
   but-still-focusable control (the text/textarea carriers become readonly, not disabled, so they
   stay focusable). Every rule below re-declares the full existing box-shadow stack for that state
   (base / hover / focus) plus the verdict ring/glow layers appended after it — never a bare
   replacement. ::after is not used anywhere here because it never renders on <input>/<textarea>,
   and the word-fill blanks are exactly the control that most needs this composed treatment.

   Same trap, transform edition (found while implementing, not spelled out in the plan): the pop
   animation itself sets `transform`. .ss-px-chip/.ss-px-toggle/.ss-px-badges already carry a
   permanent `translateY(-50%)` positioning transform (site.css ~1379) — naively keyframing a bare
   `scale()` onto them would blank out that baseline and make the control jump. ss-verdict-pop-inline
   below keeps translateY(-50%) at every keyframe step for exactly that reason; the plain
   ss-verdict-pop (scale only) is reserved for targets with no baseline transform of their own
   (.ss-qfield-input, the word-fill inputs in their default/unfocused state). */
.ss-paper-field-wrap.ss-verdict-correct   { --ss-verdict-color: #2bb673; --ss-verdict-glow: rgba(43, 182, 115, 0.50); }
.ss-paper-field-wrap.ss-verdict-incorrect { --ss-verdict-color: #e8736b; --ss-verdict-glow: rgba(232, 115, 107, 0.50); }
.ss-paper-field-wrap.ss-verdict-partial   { --ss-verdict-color: #18c7c2; --ss-verdict-glow: rgba(24, 199, 194, 0.45); }
.ss-paper-field-wrap.ss-verdict-review    { --ss-verdict-color: #e6a23c; --ss-verdict-glow: rgba(230, 162, 60, 0.45); }
/* Blank is a quiet grey with no bloom — an unanswered field is neutral, not alarming. Unchanged by
   dev-planning/24 (that reconsideration is scoped to the .ss-qfield surface below only, per the
   plan — this on-page-paper blank treatment was not reported as a problem). */
.ss-paper-field-wrap.ss-verdict-blank     { --ss-verdict-color: #b9b3a6; --ss-verdict-glow: rgba(185, 179, 166, 0); }

/* ── B: paper-widget controls (.ss-px-chip / .ss-px-toggle / .ss-px-badges / word-fill inputs) ──
   Each composed rule below = that control's OWN existing box-shadow for the same state (copied
   verbatim from its base rule earlier in this file) + the 3-layer verdict ring/glow, oversampled
   like every other on-page paper-widget length. */
.ss-paper-field-wrap.ss-verdict .ss-px-chip {
    border-color: var(--ss-verdict-color);
    box-shadow:
        0 calc(4px * var(--ss-paper-oversample)) calc(14px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.07),
        0 0 0 calc(2px * var(--ss-paper-oversample)) var(--ss-verdict-color),
        0 0 calc(9px * var(--ss-paper-oversample)) calc(1px * var(--ss-paper-oversample)) var(--ss-verdict-glow),
        0 0 calc(22px * var(--ss-paper-oversample)) calc(5px * var(--ss-paper-oversample)) var(--ss-verdict-glow);
}
.ss-paper-field-wrap.ss-verdict .ss-px-chip:hover {
    box-shadow:
        0 calc(8px * var(--ss-paper-oversample)) calc(20px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.15),
        0 0 0 calc(2px * var(--ss-paper-oversample)) var(--ss-verdict-color),
        0 0 calc(9px * var(--ss-paper-oversample)) calc(1px * var(--ss-paper-oversample)) var(--ss-verdict-glow),
        0 0 calc(22px * var(--ss-paper-oversample)) calc(5px * var(--ss-paper-oversample)) var(--ss-verdict-glow);
}
/* The chip is never disabled while revealed (only the inline toggle/badge buttons and the
   text/textarea carrier are) — a student can still open the remote to review, so its focus ring
   must survive the verdict just as much as the text blank's does. */
.ss-paper-field-wrap.ss-verdict .ss-px-chip:focus-visible {
    outline: none;
    box-shadow:
        0 0 0 calc(0.14rem * var(--ss-paper-oversample)) #fff,
        0 0 0 calc(0.3rem * var(--ss-paper-oversample)) var(--ss-verdict-color),
        0 0 calc(9px * var(--ss-paper-oversample)) calc(1px * var(--ss-paper-oversample)) var(--ss-verdict-glow),
        0 0 calc(22px * var(--ss-paper-oversample)) calc(5px * var(--ss-paper-oversample)) var(--ss-verdict-glow);
}
/* A revealed field's chip reopened in the remote (is-current): compose its own ring on top too. */
.ss-paper-field-wrap.ss-verdict .ss-px-chip.is-current {
    border-color: var(--ss-verdict-color);
    box-shadow:
        0 0 0 calc(0.14rem * var(--ss-paper-oversample)) var(--ss-verdict-color),
        0 calc(8px * var(--ss-paper-oversample)) calc(20px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.18),
        0 0 calc(9px * var(--ss-paper-oversample)) calc(1px * var(--ss-paper-oversample)) var(--ss-verdict-glow),
        0 0 calc(22px * var(--ss-paper-oversample)) calc(5px * var(--ss-paper-oversample)) var(--ss-verdict-glow);
}

.ss-paper-field-wrap.ss-verdict .ss-px-toggle {
    border-color: var(--ss-verdict-color);
    box-shadow:
        0 calc(4px * var(--ss-paper-oversample)) calc(14px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.07),
        0 0 0 calc(2px * var(--ss-paper-oversample)) var(--ss-verdict-color),
        0 0 calc(9px * var(--ss-paper-oversample)) calc(1px * var(--ss-paper-oversample)) var(--ss-verdict-glow),
        0 0 calc(22px * var(--ss-paper-oversample)) calc(5px * var(--ss-paper-oversample)) var(--ss-verdict-glow);
}
.ss-paper-field-wrap.ss-verdict .ss-px-toggle:hover {
    box-shadow:
        0 calc(6px * var(--ss-paper-oversample)) calc(18px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.12),
        0 0 0 calc(2px * var(--ss-paper-oversample)) var(--ss-verdict-color),
        0 0 calc(9px * var(--ss-paper-oversample)) calc(1px * var(--ss-paper-oversample)) var(--ss-verdict-glow),
        0 0 calc(22px * var(--ss-paper-oversample)) calc(5px * var(--ss-paper-oversample)) var(--ss-verdict-glow);
}

/* dev-planning/35 §4 A3: on a fraction/coordinate field the composite IS the visible control (the
   word-fill input beneath it is display:none), so the ring has to paint here or the verdict would
   land on an invisible element. Its own base/hover shadows are re-declared in full, same composing
   discipline as every rule in this section. In the bypass state the composite is hidden and the
   plain input's rules below take over unchanged — no extra case needed. */
/* dev-planning/35 §4 A4: the sequence grid is the same case exactly (it too hides the word-fill
   input beneath it), so it composes with the identical layers. Listed alongside rather than merged
   into one selector so each control's own base shadows stay readable next to their verdict copy. */
.ss-paper-field-wrap.ss-verdict .ss-px-seq {
    box-shadow:
        0 calc(1px * var(--ss-paper-oversample)) calc(2px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.06),
        0 calc(6px * var(--ss-paper-oversample)) calc(16px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.08),
        0 0 0 calc(2px * var(--ss-paper-oversample)) var(--ss-verdict-color),
        0 0 calc(9px * var(--ss-paper-oversample)) calc(1px * var(--ss-paper-oversample)) var(--ss-verdict-glow),
        0 0 calc(22px * var(--ss-paper-oversample)) calc(5px * var(--ss-paper-oversample)) var(--ss-verdict-glow);
}
.ss-paper-field-wrap.ss-verdict .ss-px-composite {
    box-shadow:
        0 calc(1px * var(--ss-paper-oversample)) calc(2px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.06),
        0 calc(6px * var(--ss-paper-oversample)) calc(16px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.08),
        0 0 0 calc(2px * var(--ss-paper-oversample)) var(--ss-verdict-color),
        0 0 calc(9px * var(--ss-paper-oversample)) calc(1px * var(--ss-paper-oversample)) var(--ss-verdict-glow),
        0 0 calc(22px * var(--ss-paper-oversample)) calc(5px * var(--ss-paper-oversample)) var(--ss-verdict-glow);
}
.ss-paper-field-wrap.ss-verdict .ss-px-composite:hover {
    box-shadow:
        0 calc(2px * var(--ss-paper-oversample)) calc(4px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.08),
        0 calc(8px * var(--ss-paper-oversample)) calc(22px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.14),
        0 0 0 calc(2px * var(--ss-paper-oversample)) var(--ss-verdict-color),
        0 0 calc(9px * var(--ss-paper-oversample)) calc(1px * var(--ss-paper-oversample)) var(--ss-verdict-glow),
        0 0 calc(22px * var(--ss-paper-oversample)) calc(5px * var(--ss-paper-oversample)) var(--ss-verdict-glow);
}
/* The boxes stay focusable while revealed (readonly, not disabled) — keep their own ring visible,
   recolored to the verdict, exactly as the word-fill blank does. */
.ss-paper-field-wrap.ss-verdict .ss-px-cell:focus {
    border-color: var(--ss-verdict-color);
    box-shadow: 0 0 0 calc(0.1rem * var(--ss-paper-oversample)) #fff, 0 0 0 calc(0.22rem * var(--ss-paper-oversample)) var(--ss-verdict-color);
}

/* dev-planning/35 §4 A5: on a free-work field the visible extent IS the zone outline — the carrier is
   display:none and the chip is a small corner affordance, so a ring on the chip alone would read as
   "that little button is wrong" rather than "this working is wrong". The ring paints on the zone and
   is deliberately SUPPRESSED on the chip (which keeps its own base shadow, re-declared verbatim from
   its base rule per this section's composing discipline) so one verdict paints once.
   The dashed hint border becomes a solid verdict border: at verdict time the box is no longer an
   invitation to write, it is a marked answer. */
.ss-paper-field-wrap.ss-verdict.ss-px-freework.ss-is-positioned .ss-px-fw-zone {
    border-style: solid;
    border-color: var(--ss-verdict-color);
    box-shadow:
        0 0 0 calc(1px * var(--ss-paper-oversample)) var(--ss-verdict-color),
        0 0 calc(9px * var(--ss-paper-oversample)) calc(1px * var(--ss-paper-oversample)) var(--ss-verdict-glow),
        0 0 calc(22px * var(--ss-paper-oversample)) calc(5px * var(--ss-paper-oversample)) var(--ss-verdict-glow);
    transition: box-shadow 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.ss-paper-field-wrap.ss-verdict.ss-px-freework .ss-px-chip {
    border-color: var(--ss-paper-border);
    box-shadow: 0 calc(4px * var(--ss-paper-oversample)) calc(14px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.07);
}
/* Its focus ring still has to survive (the chip is never disabled while revealed — a student can
   always reopen the remote to read the marked working), recoloured to the verdict like every other
   revealed-but-focusable control in this section. */
.ss-paper-field-wrap.ss-verdict.ss-px-freework .ss-px-chip:focus-visible {
    outline: none;
    box-shadow:
        0 0 0 calc(0.14rem * var(--ss-paper-oversample)) #fff,
        0 0 0 calc(0.3rem * var(--ss-paper-oversample)) var(--ss-verdict-color);
}

.ss-paper-field-wrap.ss-verdict .ss-px-badges {
    border-color: var(--ss-verdict-color);
    box-shadow:
        0 calc(4px * var(--ss-paper-oversample)) calc(14px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.07),
        0 0 0 calc(2px * var(--ss-paper-oversample)) var(--ss-verdict-color),
        0 0 calc(9px * var(--ss-paper-oversample)) calc(1px * var(--ss-paper-oversample)) var(--ss-verdict-glow),
        0 0 calc(22px * var(--ss-paper-oversample)) calc(5px * var(--ss-paper-oversample)) var(--ss-verdict-glow);
}

/* Word-fill blanks (input[type=text] / textarea): the border is transparent (the premium look is a
   border-box gradient background), so — unlike the pill controls above — there is no border-color
   to lean on; the ring has to live entirely in box-shadow. Base/hover/focus each re-declare that
   control's own existing shadow layers (copied from ~site.css:1300-1357) plus the verdict layers. */
.ss-paper-field-wrap.ss-verdict input.ss-paper-input[type="text"],
.ss-paper-field-wrap.ss-verdict textarea.ss-paper-input {
    box-shadow:
        0 calc(1px * var(--ss-paper-oversample)) calc(2px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.06),
        0 calc(6px * var(--ss-paper-oversample)) calc(16px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.08),
        0 0 0 calc(2px * var(--ss-paper-oversample)) var(--ss-verdict-color),
        0 0 calc(9px * var(--ss-paper-oversample)) calc(1px * var(--ss-paper-oversample)) var(--ss-verdict-glow),
        0 0 calc(22px * var(--ss-paper-oversample)) calc(5px * var(--ss-paper-oversample)) var(--ss-verdict-glow);
}
.ss-paper-field-wrap.ss-verdict input.ss-paper-input[type="text"]:hover,
.ss-paper-field-wrap.ss-verdict textarea.ss-paper-input:hover {
    box-shadow:
        0 calc(2px * var(--ss-paper-oversample)) calc(4px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.08),
        0 calc(8px * var(--ss-paper-oversample)) calc(22px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.14),
        0 0 0 calc(2px * var(--ss-paper-oversample)) var(--ss-verdict-color),
        0 0 calc(9px * var(--ss-paper-oversample)) calc(1px * var(--ss-paper-oversample)) var(--ss-verdict-glow),
        0 0 calc(22px * var(--ss-paper-oversample)) calc(5px * var(--ss-paper-oversample)) var(--ss-verdict-glow);
}
/* Revealed fields go readonly, not disabled (dev-planning/22) — a readonly control is still
   focusable, so this state is reachable and its own focus ring must not vanish under the verdict. */
.ss-paper-field-wrap.ss-verdict textarea.ss-paper-input:focus {
    box-shadow:
        0 0 0 calc(0.16rem * var(--ss-paper-oversample)) #fff,
        0 0 0 calc(0.34rem * var(--ss-paper-oversample)) var(--ss-verdict-color),
        0 calc(10px * var(--ss-paper-oversample)) calc(28px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.18),
        0 0 calc(9px * var(--ss-paper-oversample)) calc(1px * var(--ss-paper-oversample)) var(--ss-verdict-glow),
        0 0 calc(22px * var(--ss-paper-oversample)) calc(5px * var(--ss-paper-oversample)) var(--ss-verdict-glow);
}
.ss-paper-field-wrap.ss-verdict input.ss-paper-input[type="text"]:focus {
    box-shadow:
        0 0 0 calc(0.16rem * var(--ss-paper-oversample)) #fff,
        0 0 0 calc(0.34rem * var(--ss-paper-oversample)) var(--ss-verdict-color),
        0 calc(8px * var(--ss-paper-oversample)) calc(24px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.22),
        0 0 calc(9px * var(--ss-paper-oversample)) calc(1px * var(--ss-paper-oversample)) var(--ss-verdict-glow),
        0 0 calc(22px * var(--ss-paper-oversample)) calc(5px * var(--ss-paper-oversample)) var(--ss-verdict-glow);
    /* Only box-shadow is set here — position/inset/width/transform/outline stay whatever
       ~site.css:1344's focus width-pop rule already set; this rule is never allowed to touch them
       (per-property cascade), it only composes the extra ring/glow layers onto that same box. */
}

@keyframes ss-verdict-pop {
    0%   { transform: scale(1); }
    45%  { transform: scale(1.02); }
    100% { transform: scale(1); }
}
/* translateY(-50%)-preserving variant for the pill controls, which already carry that as their
   permanent baseline transform (site.css ~1379) — see the file-header comment on why the plain
   scale-only keyframe above would otherwise blank that baseline out and make the control jump. */
@keyframes ss-verdict-pop-inline {
    0%   { transform: translateY(-50%) scale(1); }
    45%  { transform: translateY(-50%) scale(1.06); }
    100% { transform: translateY(-50%) scale(1); }
}
.ss-paper-field-wrap.ss-verdict.ss-verdict-pop .ss-px-chip,
.ss-paper-field-wrap.ss-verdict.ss-verdict-pop .ss-px-toggle,
.ss-paper-field-wrap.ss-verdict.ss-verdict-pop .ss-px-badges,
/* The composite carries the same permanent translateY(-50%) baseline (it is an .ss-px-inline), so it
   takes the baseline-preserving keyframe, never the bare-scale one. */
.ss-paper-field-wrap.ss-verdict.ss-verdict-pop .ss-px-composite,
/* A4's sequence grid is an .ss-px-inline too — same baseline, same keyframe. */
.ss-paper-field-wrap.ss-verdict.ss-verdict-pop .ss-px-seq {
    animation: ss-verdict-pop-inline 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
.ss-paper-field-wrap.ss-verdict.ss-verdict-pop input.ss-paper-input[type="text"],
.ss-paper-field-wrap.ss-verdict.ss-verdict-pop textarea.ss-paper-input {
    animation: ss-verdict-pop 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
/* dev-planning/35 §4 A5: the free-work zone pops instead of the chip, and takes the BARE-scale
   keyframe — unlike every other .ss-px-* target in this section it is inset-positioned with no
   translateY(-50%) baseline, so the baseline-preserving variant would shove it half its height up
   for the duration of the animation. Four classes, so it also wins the pop back from the
   three-class ss-verdict-pop-inline rule above that would otherwise catch this field's chip. */
.ss-paper-field-wrap.ss-verdict.ss-px-freework.ss-verdict-pop .ss-px-fw-zone {
    animation: ss-verdict-pop 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
.ss-paper-field-wrap.ss-verdict.ss-px-freework.ss-verdict-pop .ss-px-chip { animation: none; }

/* ── A0: card-carousel / list view (Attempt.cshtml) — glow moves from the whole .ss-qfield card
   onto .ss-qfield-input (the answer-widget area only: marker + prompt stay unmarked). Not
   oversampled — this is the normal app shell, not the white PDF page. Server-rendered from
   InitialVerdictsJson for the published homework/remote case, live via the LessonCompanion poll
   (dev-planning/23 section A) for the in-lesson case; both paths call the same applyVerdicts. */
.ss-qfield.ss-verdict-correct   { --ss-verdict-color: #2bb673; --ss-verdict-glow: rgba(43, 182, 115, 0.28); }
.ss-qfield.ss-verdict-incorrect { --ss-verdict-color: #e8736b; --ss-verdict-glow: rgba(232, 115, 107, 0.28); }
.ss-qfield.ss-verdict-partial   { --ss-verdict-color: #18c7c2; --ss-verdict-glow: rgba(24, 199, 194, 0.24); }
.ss-qfield.ss-verdict-review    { --ss-verdict-color: #e6a23c; --ss-verdict-glow: rgba(230, 162, 60, 0.24); }
/* dev-planning/24 finding 1b: this used to be a zero-alpha glow — indistinguishable from the
   card's own resting border, i.e. invisible, which is exactly the false-positive the plan called
   out (QA "confirmed the classes were applied" while never actually seeing a rendered blank ring).
   Deliberate choice made here: a faint-but-present grey ring, not full invisibility — an unanswered
   field should still read as calm/neutral (no bloom, low alpha), not alarming, but "the reveal
   happened and this one has no answer" should be visible at a glance like every other status is.
   If the teacher would rather blank stay fully unmarked, flip this back to 0 alpha — a one-line
   change, isolated to this surface only (the paper-view blank above is untouched by this decision). */
.ss-qfield.ss-verdict-blank     { --ss-verdict-color: #b9b3a6; --ss-verdict-glow: rgba(185, 179, 166, 0.22); }
.ss-qfield.ss-verdict .ss-qfield-input {
    border-radius: var(--ss-radius);
    box-shadow:
        0 0 0 2px var(--ss-verdict-color),
        0 0 9px 1px var(--ss-verdict-glow),
        0 0 20px 5px var(--ss-verdict-glow);
    transition: box-shadow 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.ss-qfield.ss-verdict.ss-verdict-pop .ss-qfield-input {
    animation: ss-verdict-pop 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
/* A0 above rings the answer row, and on the plain list that is right: `.ss-qfield` is one row of
   many, and colouring whole rows would flood the page. In focus mode the same element is nearly
   the entire card, so the ring lands a couple of millimetres inside the white panel and pinches
   the options between two borders — obvious once the two-column card got wide. Here the panel
   itself takes the ring, which is also what makes it read as "this question was marked" rather
   than "these options are outlined". The row's own ring is cleared so there is only ever one.
   The pop pulse stays on the row: `.is-current` already owns the card's `animation` (the carousel
   slide, `!important`), and two animations on one element is a fight, not an effect. */
.ss-sheet--focus .ss-qfield.is-current.ss-verdict .ss-qfield-input { box-shadow: none; }
.ss-sheet--focus .ss-qfield.is-current.ss-verdict {
    box-shadow:
        /* the card's own lift, repeated so the ring adds to it instead of replacing it */
        0 18px 44px -12px rgba(64, 56, 168, 0.28),
        0 0 0 2px var(--ss-verdict-color),
        0 0 9px 1px var(--ss-verdict-glow),
        0 0 26px 6px var(--ss-verdict-glow);
    border-color: var(--ss-verdict-color);
    /* Same fade-in the row's ring had, so a live reveal still arrives rather than snaps. Listed in
       the prefers-reduced-motion block with the rest. */
    transition: box-shadow 0.4s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}

/* ── C: teacher LiveCompanion mirror (.ss-paper-field-wrap--mirror) — re-checked after B per
   dev-planning/24 scope C. Same wrap-vs-control geometry bug as B, just less obvious because the
   mirror renders display faces (large, roomy) rather than compact chips. Oversampled like the
   student on-page controls, since the mirror rides the same zoom-staged PDF page (positioned by the
   same initializeExamPaperRenderer). No focus/hover composition needed: the mirror wrap is
   pointer-events:none and every control in it is readonly/tabindex="-1", so those states are
   unreachable here. */
.ss-paper-field-wrap--mirror.ss-verdict .ss-paper-choice-display {
    box-shadow:
        var(--ss-shadow-xs),
        0 0 0 calc(2px * var(--ss-paper-oversample)) var(--ss-verdict-color),
        0 0 calc(9px * var(--ss-paper-oversample)) calc(1px * var(--ss-paper-oversample)) var(--ss-verdict-glow),
        0 0 calc(22px * var(--ss-paper-oversample)) calc(5px * var(--ss-paper-oversample)) var(--ss-verdict-glow);
    transition: box-shadow 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
/* The fraction face is a third display face on this surface (live feedback round 2, issue 3) and it
   REPLACES the readonly input while showing, so the glow has to follow it there or a revealed
   fraction blank would look unrevealed. The sequence face needs no rule of its own: it carries
   .ss-px-seq, which the student paper's own verdict rule above already paints, and that rule matches
   the mirror's wrap too. */
.ss-paper-field-wrap--mirror.ss-verdict .ss-px-frac-face,
.ss-paper-field-wrap--mirror.ss-verdict input.ss-paper-input[type="text"],
.ss-paper-field-wrap--mirror.ss-verdict textarea.ss-paper-input {
    box-shadow:
        0 calc(1px * var(--ss-paper-oversample)) calc(2px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.06),
        0 calc(6px * var(--ss-paper-oversample)) calc(16px * var(--ss-paper-oversample)) rgba(59, 91, 255, 0.08),
        0 0 0 calc(2px * var(--ss-paper-oversample)) var(--ss-verdict-color),
        0 0 calc(9px * var(--ss-paper-oversample)) calc(1px * var(--ss-paper-oversample)) var(--ss-verdict-glow),
        0 0 calc(22px * var(--ss-paper-oversample)) calc(5px * var(--ss-paper-oversample)) var(--ss-verdict-glow);
    transition: box-shadow 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}

/* ── A: the floating remote (.ss-px-remote) — dev-planning/24 section A. Applied directly by
   exam-paper-widgets.mjs's syncRemoteVerdict() (there is no DOM relationship to paint through via a
   descendant selector: the remote is one shared node appended to document.body, reused across every
   field). TRAP: the remote lives OUTSIDE the transformed .ss-paper-zoom-stage (position: fixed needs
   a non-transformed ancestor), so unlike literally everything else in this section it is NOT
   oversample-scaled — every length below is a plain px/rem, never `calc(... * var(--ss-paper-
   oversample))`. Copying the on-page rules verbatim here would render a ~2× oversized ring. */
.ss-px-remote.ss-verdict-correct   { --ss-verdict-color: #2bb673; --ss-verdict-glow: rgba(43, 182, 115, 0.45); }
.ss-px-remote.ss-verdict-incorrect { --ss-verdict-color: #e8736b; --ss-verdict-glow: rgba(232, 115, 107, 0.45); }
.ss-px-remote.ss-verdict-partial   { --ss-verdict-color: #18c7c2; --ss-verdict-glow: rgba(24, 199, 194, 0.4); }
.ss-px-remote.ss-verdict-review    { --ss-verdict-color: #e6a23c; --ss-verdict-glow: rgba(230, 162, 60, 0.4); }
.ss-px-remote.ss-verdict-blank     { --ss-verdict-color: #b9b3a6; --ss-verdict-glow: rgba(185, 179, 166, 0.18); }
.ss-px-remote.ss-verdict {
    /* Composes with (does not replace) .ss-px-remote's own base box-shadow and its transform/opacity
       transition pair — both are re-declared here in full, not just appended to, since `transition`
       is a shorthand and a partial re-declaration would silently drop the open/close animation. */
    box-shadow:
        0 24px 60px rgba(23, 26, 33, 0.24),
        0 0 0 2px var(--ss-verdict-color),
        0 0 20px 2px var(--ss-verdict-glow),
        0 0 44px 10px var(--ss-verdict-glow);
    transition: transform 0.32s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.32s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.ss-px-remote.ss-verdict.ss-verdict-pop { animation: ss-verdict-pop 0.5s cubic-bezier(0.16, 1, 0.3, 1); }

/* ── D: the official answer note (dev-planning/22 pinned decision 1a) ───────────────────────────
   Rendered by exam-paper-widgets.mjs's renderVerdictAnswerNote (shared by setFieldVerdict for the
   interactive paper widgets AND applyMirrorVerdicts for the teacher mirror — one wrap shape, one
   note). Colored via the same --ss-verdict-color custom property the ring/glow above already reads,
   so the note always agrees with the glow it sits under without a second color decision anywhere.
   Text color intentionally does NOT reuse --ss-verdict-glow (an alpha-heavy shadow color, unreadable
   as text) — a fixed high-contrast ink instead, since a Blank verdict's --ss-verdict-color (a pale
   grey chosen to keep the ring quiet) would be too low-contrast for actual reading text.
   position: absolute + `top` set by JS (positionVerdictAnswerNote) is deliberate, not a CSS length:
   the wrap is sized to the printed blank, not to the visible control inside it (same mismatch the
   ring/glow rules above already work around) — see that function's comment for why this cannot be
   expressed as a CSS rule alone. */
.ss-paper-field-wrap.ss-verdict .ss-verdict-answer {
    position: absolute;
    left: 0;
    top: 0; /* overwritten by positionVerdictAnswerNote the instant the note is created */
    z-index: 4;
    display: inline-flex;
    align-items: baseline;
    gap: calc(0.3rem * var(--ss-paper-oversample));
    padding: calc(0.18rem * var(--ss-paper-oversample)) calc(0.5rem * var(--ss-paper-oversample));
    border-radius: calc(0.5rem * var(--ss-paper-oversample));
    background: var(--ss-verdict-color);
    color: #fff;
    font-size: calc(0.72rem * var(--ss-paper-oversample));
    font-weight: 700;
    line-height: 1.3;
    white-space: nowrap;
    box-shadow: 0 calc(2px * var(--ss-paper-oversample)) calc(8px * var(--ss-paper-oversample)) rgba(23, 26, 33, 0.18);
    opacity: 0;
    animation: ss-verdict-answer-in 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.ss-verdict-answer-label { opacity: 0.85; font-weight: 600; }
.ss-verdict-answer-value { font-weight: 800; }
@keyframes ss-verdict-answer-in {
    from { opacity: 0; transform: translateY(-2px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* ── D0: list view (Attempt.cshtml) — .ss-qfield-answer, plain document flow underneath
   .ss-qfield-input (no absolute positioning needed: unlike the paper/mirror surfaces this is a
   normal responsive card, not a wrap sized to a fixed printed blank). App-shell theme tokens, same
   posture as the rest of this surface's verdict rules above. */
.ss-qfield-answer {
    display: flex;
    align-items: baseline;
    gap: 0.4rem;
    margin-top: 0.5rem;
    padding: 0.3rem 0.65rem;
    border-radius: var(--ss-radius);
    background: var(--ss-verdict-color);
    color: #fff;
    font-size: 0.85rem;
    line-height: 1.3;
}
.ss-qfield-answer-label { opacity: 0.85; font-weight: 600; }
.ss-qfield-answer-value { font-weight: 800; word-break: break-word; }

/* ── D1: the floating remote (.ss-px-remote) — .ss-px-remote-answer, appended by renderRemoteBody
   after the field's own controls. Not oversampled (same reason as the rest of the remote's own
   rules above), and uses the app's theme tokens since the remote floats over the app shell. */
.ss-px-remote-answer {
    display: flex;
    align-items: baseline;
    gap: 0.4rem;
    margin: 0.7rem 0 0;
    padding: 0.45rem 0.75rem;
    border-radius: 0.75rem;
    background: var(--ss-verdict-color, var(--ss-primary));
    color: #fff;
    font-size: 0.85rem;
    line-height: 1.3;
}
.ss-px-remote-answer-label { opacity: 0.85; font-weight: 600; }
.ss-px-remote-answer-value { font-weight: 800; word-break: break-word; }

/* prefers-reduced-motion: keep the color + glow (they carry the meaning), drop the pop and the
   glow's fade-in transition — decision 6. Every selector here mirrors the paint targets above (the
   controls), not the containers that merely carry the state class. */
@media (prefers-reduced-motion: reduce) {
    .ss-paper-field-wrap.ss-verdict .ss-px-chip,
    .ss-paper-field-wrap.ss-verdict .ss-px-toggle,
    .ss-paper-field-wrap.ss-verdict .ss-px-badges,
    .ss-paper-field-wrap.ss-verdict .ss-px-composite,
    .ss-paper-field-wrap.ss-verdict .ss-px-seq,
    /* dev-planning/35 §4 A5 — the free-work zone's verdict ring fades in like the rest. */
    .ss-paper-field-wrap.ss-verdict.ss-px-freework.ss-is-positioned .ss-px-fw-zone,
    .ss-paper-field-wrap.ss-verdict input.ss-paper-input[type="text"],
    .ss-paper-field-wrap.ss-verdict textarea.ss-paper-input,
    .ss-qfield.ss-verdict .ss-qfield-input,
    .ss-sheet--focus .ss-qfield.is-current.ss-verdict,
    .ss-paper-field-wrap--mirror.ss-verdict .ss-paper-choice-display,
    .ss-paper-field-wrap--mirror.ss-verdict input.ss-paper-input[type="text"],
    .ss-paper-field-wrap--mirror.ss-verdict textarea.ss-paper-input,
    .ss-px-remote.ss-verdict { transition: none; }
    .ss-paper-field-wrap.ss-verdict.ss-verdict-pop .ss-px-chip,
    .ss-paper-field-wrap.ss-verdict.ss-verdict-pop .ss-px-toggle,
    .ss-paper-field-wrap.ss-verdict.ss-verdict-pop .ss-px-badges,
    .ss-paper-field-wrap.ss-verdict.ss-verdict-pop .ss-px-composite,
    .ss-paper-field-wrap.ss-verdict.ss-verdict-pop .ss-px-seq,
    .ss-paper-field-wrap.ss-verdict.ss-px-freework.ss-verdict-pop .ss-px-fw-zone,
    .ss-paper-field-wrap.ss-verdict.ss-verdict-pop input.ss-paper-input[type="text"],
    .ss-paper-field-wrap.ss-verdict.ss-verdict-pop textarea.ss-paper-input,
    .ss-qfield.ss-verdict.ss-verdict-pop .ss-qfield-input,
    .ss-px-remote.ss-verdict.ss-verdict-pop { animation: none; }
    /* The answer note's own fade-in (decision 1a) — same treatment: keep it visible immediately
       (opacity 1, no transform offset), drop only the animated entrance. */
    .ss-verdict-answer { animation: none; opacity: 1; transform: none; }
}

/* Teacher LiveCompanion reveal toggle (dev-planning/22, decision 9) — the same btn-sm footprint as
   the activate toggle beside it, but warm amber, reading as "live to the student" when revealed with
   a soft glow that echoes the student's verdict layer. Plus a small revealed-state flag on the row. */
.ss-companion-task {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    margin: 0 0.4rem 0.4rem 0;
}
.ss-reveal-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    color: #b9791f;
    background: transparent;
    border-color: rgba(230, 162, 60, 0.55);
}
.ss-reveal-toggle:hover {
    color: #8a5a12;
    background: rgba(230, 162, 60, 0.10);
    border-color: #e6a23c;
}
.ss-reveal-toggle-icon { width: 0.95rem; height: 0.95rem; }
.ss-reveal-toggle.is-revealed {
    color: #fff;
    background: #e6a23c;
    border-color: #e6a23c;
    box-shadow: 0 0 0 1px rgba(230, 162, 60, 0.35), 0 2px 12px rgba(230, 162, 60, 0.45);
}
.ss-reveal-toggle.is-revealed:hover { background: #d9922f; border-color: #d9922f; color: #fff; }
.ss-reveal-flag {
    display: inline-flex;
    align-items: center;
    gap: 0.32rem;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: #b9791f;
}
.ss-reveal-flag-dot {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background: #e6a23c;
    box-shadow: 0 0 6px rgba(230, 162, 60, 0.7);
}

/* Page shells live in .ss-paper-zoom-stage while only near-viewport PDF canvases retain pixel
   backing stores. Zooming applies a CSS transform: scale() to the whole stage (no re-render), so
   .ss-paper-zoom-sizer's explicit width/height (kept in sync by the script) drives the scrollable
   area — a transformed element keeps its pre-transform layout box otherwise, which would either
   clip the zoomed-in page or leave dead scroll space when zoomed out. */
/* margin-inline: auto centers the sizer whenever it's narrower than its scroll container (zoomed
   out below fit-width) — without it, a shrunk sizer just sits flush at the scroll container's
   left edge (normal block-layout default), leaving dead space on the right instead of staying
   centered. This is the missing half of the zoom-centering fix: JS (setScale) re-anchors scroll
   position for the case where the sizer is wider than the container and scrolling matters; this
   covers the case where it's narrower and there's no scroll range to anchor within at all. */
.ss-paper-zoom-sizer { position: relative; margin-inline: auto; }
.ss-paper-zoom-stage { position: relative; transform-origin: top left; }

/* dev-planning/25 item 4 — invert the (black-and-white) exam PDF in dark mode. Covers every
   surface that renders through exam-paper-renderer.mjs's shared createAllPageShells: the student
   paper view (AttemptPaper.cshtml), the teacher grading view (Paper.cshtml), and the LiveCompanion
   mirror — all three build the same .ss-paper-zoom-stage > .ss-paper-page-wrap > canvas structure.
   html[data-bs-theme="dark"] (not the media query) matches the attribute the inline theme script
   in _Layout.cshtml / _StudentAnswerSheetLayout.cshtml actually stamps onto <html>.
   Scoped to the canvas ONLY — the ink SVG and answer-widget overlay are separate DOM siblings
   (appended after the canvas inside the same page wrap; see exam-paper-renderer.mjs) and must
   keep their real colors on the now-dark page, so they are deliberately left untouched here. The
   floating remote (.ss-px-remote) and toolbars live outside .ss-paper-zoom-stage entirely and
   already theme via tokens — this selector cannot reach them, so they are never double-inverted.
   Plain invert(1) (no hue-rotate) — the source scans are B&W, and hue-rotate risks muddying scans
   that do have some color; revisit with hue-rotate(180deg) only if a real dark-mode screenshot
   shows an inverted scan reading wrong.
   WebKit trap (see the "canvas hardware-composites" comment above ensurePageRendered in
   exam-paper-renderer.mjs, ~line 221): a CSS filter puts the canvas in its own compositing layer.
   The existing fix there (promoting the overlay to its own layer via translateZ(0) the moment the
   canvas gets a real pixel backing store) already runs independently of this filter and is not
   changed by it, so the overlay should still stack above the canvas — but this MUST be confirmed
   with a real screenshot on WebKit, in dark mode, with a revealed/ink-marked answer visible, since
   it could not be verified in a browser while writing this rule. */
html[data-bs-theme="dark"] .ss-paper-zoom-stage canvas {
    filter: invert(1);
}
/* The page-wrap's own background is set inline (#fff) by exam-paper-renderer.mjs — !important is
   required to win over that inline style. Without this, a 1px rounding gap at the canvas edge
   (canvas.width/height are Math.floor'd from a fractional CSS viewport size) could show a white
   sliver through the wrap at the page edge in dark mode. */
html[data-bs-theme="dark"] .ss-paper-page-wrap {
    background: #15161c !important;
}
/* dev-planning/25 item 2 (the real cause, found after the teacher clarified): in dark mode the
   teacher grading-paper surround (`.ss-grading-paper-shell` and the scrollable
   `.ss-grading-paper-pages` whiteboard, both hard-coded `#fff`) stayed white while the admin
   _Layout body/navbar behind them is dark navy — so the white paper sat on dark navy bands you
   could scroll into "outside the pdf area". With item 4 inverting the PDF pages to dark, white
   surround would look even more broken. Darken the surround to the same tone as the inverted
   page-wrap so the whole view reads as one coherent dark surface; drop the faint grid to a
   light-on-dark tint so the whiteboard texture survives without the white base showing through.
   The student paper view uses a different layout that already themes dark, so it is untouched. */
html[data-bs-theme="dark"] .ss-grading-paper-shell {
    background: #101223;
}
/* The remaining dark "bars" the teacher still saw top & bottom after darkening the shell: the
   admin _Layout body carries an aurora-gradient dark background, and it shows through wherever the
   shell doesn't paint — the `.ss-companion-panel`'s `margin-top: 8rem` collapses through the shell
   (leaving a body-gradient band above it) and the `.ss-page`/`main` bottom padding leaves one
   below. Flatten the body to the same flat dark as the paper for this view only, so every gap
   reads as one continuous surface instead of a differently-shaded band. Scoped by :has() to the
   grading-paper view; the sibling rule at ~line 91 already zeros this body's bottom margin. */
html[data-bs-theme="dark"] body:has(.ss-grading-paper-shell) {
    background: #101223;
}
html[data-bs-theme="dark"] .ss-grading-paper-pages {
    background:
        repeating-linear-gradient(0deg, transparent, transparent 39px, rgba(255, 255, 255, .05) 40px),
        repeating-linear-gradient(90deg, transparent, transparent 39px, rgba(255, 255, 255, .05) 40px),
        #101223;
}
/* The STUDENT paper's pages container (#ss-paper-pages) is the same hard-coded white/grid box —
   invisible today because the PDF over it is also white, but item 4 inverts that PDF to dark, so
   without this the dark pages would sit on a white surround (the exact bug being fixed on the
   teacher side). Darken it identically so both paper surfaces stay coherent under the invert. */
html[data-bs-theme="dark"] #ss-paper-pages {
    background:
        repeating-linear-gradient(0deg, transparent, transparent 39px, rgba(255, 255, 255, .05) 40px),
        repeating-linear-gradient(90deg, transparent, transparent 39px, rgba(255, 255, 255, .05) 40px),
        #101223;
}

/* Persistent paper toolbar: navigation, autosave state, zoom, and the irreversible submit action
   remain reachable without scrolling to the end of a long exam. */
.ss-paper-toolbar {
    position: fixed;
    top: 1rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: var(--ss-z-sticky-action, 1020);
    /* Zoom on this paper is the toolbar's own +/- and the two-finger pinch the viewer implements
       itself (dev-planning/43) — never the browser's, which is why .ss-sheet-mid below carries this
       same value for the page stack. It has to be stated again here because `position: fixed` lifts
       this element out of the scroll container whose touch-action would otherwise cover it, so it
       kept the default `auto` and with it browser DOUBLE-TAP zoom. A stray double-tap left the
       visual viewport zoomed with nothing able to undo it — every other surface refuses the pinch
       that would zoom back out, and script cannot reset the visual viewport — so the only way back
       was reloading the exam, losing the student's place in a 49-page paper. */
    touch-action: pan-x pan-y;
    display: flex;
    align-items: center;
    gap: 0.3rem;
    max-width: min(80rem, calc(100vw - 1.5rem));
    padding: 0.3rem 0.4rem;
    border-radius: 999px;
    background: var(--ss-glass-bg-strong, rgba(255, 255, 255, 0.88));
    -webkit-backdrop-filter: var(--ss-glass-blur, saturate(180%) blur(20px));
    backdrop-filter: var(--ss-glass-blur, saturate(180%) blur(20px));
    border: 1px solid var(--ss-glass-border, var(--ss-border));
    box-shadow: var(--ss-glass-shadow, var(--ss-shadow-sm));
}
.ss-toolbar-logo { width: 1.55rem; height: 1.55rem; flex: 0 0 auto; margin: 0 .1rem; display: block; }

/* Recovery/session-ended notices can appear at any point in a long (77-page) document, well
   after the user scrolled away from the top — normal document flow left them effectively
   invisible below the whole page stack. Fixed position keeps them reachable regardless of
   scroll; the flex column lets multiple simultaneously-visible notices (e.g. a save failing
   right as the session ends) stack without overlapping instead of needing separate positioning
   math per notice. */
.ss-paper-toast-stack {
    position: fixed;
    top: 4.5rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1040;
    width: min(92vw, 640px);
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.ss-paper-toast-stack > .alert { margin: 0 !important; }
@media (max-width: 575.98px) {
    /* .ss-paper-toolbar itself moves to the bottom of the screen at this width (see below) —
       keep the toast stack anchored near the top regardless, so the two never collide. */
    .ss-paper-toast-stack { top: 0.75rem; }
}
/* Admin/teacher toolbar clears the sticky navbar (top: 4.25rem, see .ss-grading-paper-toolbar) —
   the toast stack needs the matching extra clearance. */
.ss-grading-paper-shell .ss-paper-toast-stack {
    top: 8rem;
}
.ss-paper-toolbar-eraser-icon {
    width: 1.05rem;
    height: 1.05rem;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.ss-paper-toolbar-icon {
    width: 1.9rem;
    height: 1.9rem;
    border-radius: 50%;
    border: 1px solid var(--ss-border);
    background: var(--ss-surface);
    color: var(--ss-ink);
    font-size: 1.05rem;
    font-weight: 700;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 0.12s ease, border-color 0.12s ease, transform 0.1s ease;
}
.ss-paper-toolbar-icon:hover { border-color: var(--ss-primary); color: var(--ss-primary); }
.ss-paper-toolbar-icon.is-active { background: var(--ss-primary); border-color: var(--ss-primary); color: #fff; }
.ss-paper-toolbar-icon.disabled { pointer-events: none; opacity: 0.5; }
.ss-paper-toolbar-icon:active { transform: scale(0.94); }
.ss-paper-toolbar-icon:disabled { opacity: 0.4; cursor: default; }
.ss-paper-zoom-label {
    min-width: 2.6rem;
    text-align: center;
    font-size: 0.78rem;
    font-weight: 700;
    color: var(--ss-muted);
}
.ss-paper-toolbar-link {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    min-height: 1.9rem;
    padding: 0 0.45rem;
    border-radius: 999px;
    color: var(--ss-ink);
    font-size: 0.78rem;
    font-weight: 700;
    text-decoration: none;
}
.ss-paper-toolbar-link:hover { color: var(--ss-primary); background: var(--ss-primary-soft); }
.ss-paper-toolbar-divider { width: 1px; height: 1.35rem; background: var(--ss-border); }
.ss-paper-toolbar-save { min-width: 3.2rem; text-align: center; font-size: 0.72rem; }
.ss-paper-toolbar-submit {
    min-height: 2rem;
    padding: 0 0.8rem;
    border: 0;
    border-radius: 999px;
    background: var(--ss-primary);
    color: #fff;
    font-size: 0.76rem;
    font-weight: 800;
    cursor: pointer;
    box-shadow: 0 3px 10px rgba(73, 55, 166, 0.24);
}
.ss-paper-toolbar-submit:hover { filter: brightness(0.94); }
.ss-paper-toolbar-submit-short { display: none; }
/* Same self-referential wrap-sizing trap as .ss-grading-paper-toolbar above (see the comment
   there): a wrapping flex container's shrink-to-fit width can end up narrower than its actual
   single-line content, wrapping well before it needs to. Never wrap; scroll horizontally on a
   genuinely too-narrow window instead. */
.ss-paper-toolbar:has(.ss-student-ink-tools) {
    max-width: min(84rem, calc(100vw - 2.5rem));
    flex-wrap: nowrap;
    overflow-x: auto;
}
.ss-student-ink-tools, .ss-student-ink-colors {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: .3rem;
}
.ss-student-ink-tools .is-active { color: #fff; background: var(--ss-primary); border-color: var(--ss-primary); }
.ss-student-ink-tools .ss-ink-color { width: 1.45rem; height: 1.45rem; flex: 0 0 auto; }
.ss-student-ink-tools .ss-ink-size { min-width: 5.6rem; height: 1.9rem; border: 1px solid var(--ss-border); border-radius: .55rem; font-size: .72rem; }
.ss-student-ink-toggle {
    min-height: 1.9rem;
    padding: 0 .55rem;
    border: 1px solid var(--ss-border);
    border-radius: 999px;
    background: var(--ss-surface);
    color: var(--ss-muted);
    font-size: .7rem;
    font-weight: 700;
    white-space: nowrap;
    flex: 0 0 auto;
}
.ss-student-ink-toggle.is-active { color: #fff; background: var(--ss-primary); border-color: var(--ss-primary); }

/* ADR-0015 Phase 5: soft page-follow. A gentle pulse, never a demand — clicking is always the
   student's own choice, this only draws the eye to the fact that the teacher moved pages. */
.ss-follow-glow {
    color: #fff;
    background: var(--ss-accent-cyan, var(--ss-primary));
    border-color: transparent;
    animation: ss-follow-glow-pulse 1.8s ease-in-out infinite;
}
@keyframes ss-follow-glow-pulse {
    0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--ss-accent-cyan, var(--ss-primary)) 55%, transparent); }
    50% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--ss-accent-cyan, var(--ss-primary)) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
    .ss-follow-glow { animation: none; }
}
.ss-student-ink-tools :is(button, select):focus-visible,
.ss-student-ink-palette button:focus-visible,
.ss-paper-toolbar > .ss-student-ink-toggle:focus-visible {
    outline: 3px solid color-mix(in srgb, var(--ss-primary) 45%, transparent);
    outline-offset: 2px;
}
.ss-paper-toolbar-submit:disabled { opacity: .62; cursor: wait; }
.ss-student-ink-palette {
    position: absolute;
    top: calc(100% + .55rem);
    right: 0;
    z-index: 3;
    display: flex;
    gap: .55rem;
    padding: .65rem;
    border: 1px solid var(--ss-border);
    border-radius: 999px;
    background: var(--ss-surface);
    box-shadow: var(--ss-shadow-md);
}
.ss-student-ink-palette[hidden] { display: none; }
/* Quiet/extended toolbar (2026-08-10): students read the full strip as complicated — quiet keeps
   only the everyday tools (pen/eraser, thickness, undo, image, follow-the-teacher, submit) and
   the ⋯ toggle reveals everything tagged ss-tb-ext. display:none rather than visibility, so the
   quiet pill genuinely shortens; gap collapses with the hidden items for free. */
.ss-paper-toolbar .ss-tb-ext { display: none; }
.ss-paper-toolbar.is-extended .ss-tb-ext {
    display: inline-flex;
    align-items: center;
    animation: ss-tb-pop 0.22s cubic-bezier(0.3, 1.5, 0.5, 1);
}
.ss-paper-toolbar.is-extended #ssToolbarMoreToggle {
    background: var(--ss-primary-soft);
    border-color: var(--ss-accent-border);
    color: var(--ss-primary);
}
@keyframes ss-tb-pop {
    from { opacity: 0; transform: scale(0.7); }
    to { opacity: 1; transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
    .ss-paper-toolbar.is-extended .ss-tb-ext { animation: none; }
}
/* Pen ↔ eraser grow-swap (2026-08-10): exactly one of the pair is ever the selected tool
   (exam-grading-paper.mjs updateToolbar), so the INACTIVE one renders as a much larger target —
   switching tools is always the easy tap, on iPad especially. */
#studentInkPen, #studentInkEraser {
    transition: background 0.12s ease, border-color 0.12s ease, transform 0.1s ease,
        width 0.22s cubic-bezier(0.3, 1.5, 0.5, 1), height 0.22s cubic-bezier(0.3, 1.5, 0.5, 1);
}
#studentInkPen svg { width: 1.05rem; height: 1.05rem; }
#studentInkPen svg, #studentInkEraser .ss-paper-toolbar-eraser-icon { transition: width 0.22s ease, height 0.22s ease; }
#studentInkPen:not(.is-active), #studentInkEraser:not(.is-active) {
    width: 2.7rem;
    height: 2.7rem;
    box-shadow: var(--ss-shadow-sm);
}
#studentInkPen:not(.is-active) svg,
#studentInkEraser:not(.is-active) .ss-paper-toolbar-eraser-icon { width: 1.5rem; height: 1.5rem; }
/* `pan-x pan-y`, not `manipulation`: both remove the double-tap delay these controls want, but
   `manipulation` still PERMITS pinch-zoom — the BROWSER's, which is not the viewer's own pinch and
   lands in the same unrecoverable zoom a double-tap on the toolbar used to. That made the answer
   fields a hole in the workspace's otherwise deliberate no-browser-zoom policy (.ss-sheet-mid). */
.ss-paper-field-wrap :is(input, textarea, select, button, a, label) { touch-action: pan-x pan-y; }
@media (max-width: 575.98px) {
    .ss-paper-toolbar {
        top: auto;
        right: 0.65rem;
        bottom: calc(0.65rem + env(safe-area-inset-bottom));
        left: 0.65rem;
        transform: none;
        max-width: none;
        justify-content: center;
    }
    .ss-paper-toolbar-text, .ss-paper-toolbar-submit-full { display: none; }
    .ss-paper-toolbar-submit-short { display: inline; }
    .ss-paper-toolbar-save { min-width: 2.8rem; }
    .ss-paper-toolbar:has(.ss-student-ink-tools) { max-width: none; flex-wrap: wrap; }
    .ss-student-ink-tools { order: 2; width: 100%; justify-content: center; overflow-x: auto; padding: .2rem 0; }
    .ss-student-ink-toggle { white-space: nowrap; }
}

/* Expandable solution / details. */
.ss-answer-solution { margin-top: 0.35rem; }
.ss-answer-solution summary {
    cursor: pointer;
    font-size: 0.76rem;
    font-weight: 600;
    color: var(--ss-text-muted);
    list-style: none;
}
.ss-answer-solution summary::-webkit-details-marker { display: none; }
.ss-answer-solution summary::before {
    content: "▸ ";
    color: var(--ss-text-muted);
}
.ss-answer-solution[open] summary::before { content: "▾ "; }
.ss-answer-solution-body { margin-top: 0.4rem; }
.ss-answer-celllabel {
    font-size: 0.62rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--ss-text-muted);
    margin-right: 0.3rem;
}
.ss-official-answer {
    background: var(--ss-glass-bg-soft);
    border: 1px solid var(--ss-glass-border);
    border-radius: 9px;
    padding: 0.4rem 0.6rem;
    font-size: 0.8125rem;
}
/* A wrong answer opens this disclosure automatically, so the official answer is the one thing the
   teacher is looking for at that moment — a warm amber panel and a larger, heavier value pull the
   eye straight to it. Only when an answer actually exists: the "Nincs megadva" state keeps the
   plain treatment above, since highlighting an absence would be a lie. */
.ss-official-answer.is-highlight {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    flex-wrap: wrap;
    background: rgba(250, 204, 21, 0.16);
    border-color: rgba(202, 138, 4, 0.45);
    box-shadow: inset 0 0 0 1px rgba(250, 204, 21, 0.12);
    padding: 0.5rem 0.75rem;
}
.ss-official-answer.is-highlight .ss-answer-celllabel {
    margin-right: 0;
    color: #8a6100;
}
.ss-official-answer.is-highlight .ss-official-value {
    font-size: 1.02rem;
    font-weight: 700;
    line-height: 1.35;
    color: var(--ss-ink);
    word-break: break-word;
}
@media (prefers-color-scheme: dark) {
    .ss-official-answer.is-highlight {
        background: rgba(250, 204, 21, 0.14);
        border-color: rgba(250, 204, 21, 0.4);
    }
    .ss-official-answer.is-highlight .ss-answer-celllabel { color: #e8c66a; }
    .ss-official-answer.is-highlight .ss-official-value { color: #f6e7bd; }
}

/* Doc 37 — guidance, back under the official answer in the Lesson Workbench. Quiet on purpose: the
   amber panel above stays the thing the eye lands on, and this is what you read second. The quote
   is set apart because it is the part that makes the explanation checkable rather than trusted —
   the whole reason guidance was allowed back onto this surface. */
.ss-answer-guidance {
    margin-top: 0.4rem;
    padding: 0.4rem 0.6rem;
    border-left: 3px solid var(--ss-glass-border);
    font-size: 0.8125rem;
}
.ss-answer-guidance .ss-guidance-text { line-height: 1.4; }
.ss-answer-guidance .ss-guidance-quote {
    margin-top: 0.3rem;
    font-style: italic;
    color: var(--ss-text-muted);
    line-height: 1.35;
}
/* Kind B: unverifiable by construction, so it is visually separated from the anchored explanation
   and carries the one control that makes unverifiable content survivable. */
.ss-answer-teachingnote {
    margin-top: 0.4rem;
    padding: 0.4rem 0.6rem;
    border-left: 3px solid rgba(56, 189, 248, 0.55);
    background: rgba(56, 189, 248, 0.08);
    border-radius: 0 9px 9px 0;
    font-size: 0.8125rem;
}
.ss-answer-teachingnote .ss-teachingnote-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}
.ss-answer-teachingnote .ss-teachingnote-text { line-height: 1.4; margin-top: 0.15rem; }
.ss-dismiss-note-btn { white-space: nowrap; font-size: 0.75rem; }

/* Task-level "compared with the official answer sheet" confirmation. Outline chip while the check
   is still owed; the confirmed state is a quiet green badge (the control itself moves into the
   collapsed settings drawer once done — see .ss-exercise-settings). */
.ss-humancheck-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    border: 1px solid rgba(43, 182, 115, 0.5);
    background: transparent;
    color: #1c8a5a;
    border-radius: 999px;
    font-weight: 600;
}
.ss-humancheck-btn:hover {
    background: rgba(43, 182, 115, 0.12);
    border-color: rgba(43, 182, 115, 0.7);
    color: #1c8a5a;
}
.ss-humancheck-btn svg { width: 0.85rem; height: 0.85rem; }
.ss-humancheck-badge {
    display: inline-block;
    padding: 0.1rem 0.55rem;
    border-radius: 999px;
    font-size: 0.68rem;
    font-weight: 700;
    background: rgba(43, 182, 115, 0.15);
    border: 1px solid rgba(43, 182, 115, 0.45);
    color: #1c8a5a;
}

/* ── Lesson Workbench collapsibles — per-exercise body + the scoring/stat disclosure ────────
   Both use native <details> so a finished or skipped task block (and, live, the formal score
   summary) can be tucked away without extra JS. Only the marker/spacing differs from the answer
   solution disclosure above — same calm, no-marker-dot treatment. */
.ss-exercise-body summary,
.ss-exercise-settings summary,
.ss-exam-scoring-details summary {
    cursor: pointer;
    list-style: none;
}
.ss-exercise-body summary::-webkit-details-marker,
.ss-exercise-settings summary::-webkit-details-marker,
.ss-exam-scoring-details summary::-webkit-details-marker { display: none; }
.ss-exercise-body summary::before,
.ss-exercise-settings summary::before,
.ss-exam-scoring-details summary::before {
    content: "▸ ";
    color: var(--ss-text-muted);
}
.ss-exercise-body[open] summary::before,
.ss-exercise-settings[open] summary::before,
.ss-exam-scoring-details[open] summary::before { content: "▾ "; }
.ss-exam-scoring-details summary { font-weight: 600; font-size: 0.875rem; }

/* ── Student answer field (public answer sheet) ─────────────────────────────
   A calm two-column row: a small, fixed marker chip on the left ("a", "b", "14"…)
   that never carries the prompt text, and a body column on the right that owns the
   prompt, the flag switch and the input. Deliberately not a circle — a circle sized
   for one character turns into an ugly smear the moment real digitized data embeds a
   longer marker; a rounded chip with a min-width instead just grows a little. */
.ss-qfield {
    display: flex;
    align-items: flex-start;
    gap: 0.85rem;
    padding: 0.95rem 1rem;
    border: 1px solid var(--ss-border-light);
    border-radius: var(--ss-radius);
    background: var(--ss-surface);
    box-shadow: var(--ss-shadow-xs);
    margin-bottom: 0.75rem;
}
.ss-qfield-marker {
    flex: 0 0 auto;
    min-width: 2.1rem;
    max-width: 5rem;
    box-sizing: border-box;
    padding: 0.32rem 0.5rem;
    border-radius: 0.6rem;
    background: var(--ss-primary-soft);
    color: var(--ss-primary-strong);
    font-weight: 800;
    font-size: 0.95rem;
    line-height: 1.3;
    text-align: center;
    word-break: break-word;
}
.ss-qfield-body { flex: 1 1 auto; min-width: 0; }
.ss-qfield-prompt {
    font-weight: 600;
    font-size: 1.12rem;
    color: var(--ss-ink);
    line-height: 1.4;
    word-break: break-word;
    min-width: 0;
    margin-bottom: 0.8rem;
}
.ss-qfield-noprompt { font-weight: 400; color: var(--ss-text-muted); font-style: italic; }
.ss-qfield-input .form-control,
.ss-qfield-input .form-select { background: var(--ss-surface-soft); }
.ss-answer-lg { min-height: 6rem; font-size: 1.05rem; }

/* ── Big tap-target option buttons (Radio) ─────────────────────────────────
   The native radio is visually hidden; the whole <label> is the button. Sized for
   phone thumbs (≥52px), full width, with a filled selected state. Selected state is
   driven by `.is-selected` (kept in sync server-side + by JS) and, where supported,
   `:has()` — so it still works if the JS class ever lags. */
.ss-optlist { display: flex; flex-direction: column; gap: 0.6rem; }
.ss-optlist.ss-optlist--grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
}
.ss-sheet--focus .ss-optlist.ss-optlist--grid {
    max-width: 560px;
    width: 92%;
}
@media (max-width: 576px) {
    .ss-optlist.ss-optlist--grid {
        grid-template-columns: 1fr;
    }
    .ss-sheet--focus .ss-optlist.ss-optlist--grid {
        max-width: 420px;
    }
}
.ss-opt {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    min-height: 3.25rem;
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--ss-accent-border);
    border-radius: 1rem;
    background: var(--ss-surface-lavender);
    color: var(--ss-ink);
    font-size: 1.1rem;
    cursor: pointer;
    margin-bottom: 0;
    transition: border-color 0.12s ease, background 0.12s ease;
}
.ss-opt:hover { border-color: var(--ss-primary); }
.ss-opt.is-used-elsewhere {
    background: #eef0f5;
    border-color: #d7dce8;
    color: #6b7280;
    opacity: 0.72;
}
.ss-opt.is-used-elsewhere:hover {
    opacity: 0.9;
    border-color: var(--ss-primary);
}
.ss-opt-input {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.ss-opt-key {
    flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.95rem; height: 1.95rem;
    border-radius: 0.6rem;
    background: var(--ss-primary-soft);
    color: var(--ss-primary-strong);
    font-size: 0.9rem; font-weight: 700;
}
.ss-opt-text { flex: 1 1 auto; line-height: 1.3; }
.ss-opt-owner {
    position: absolute;
    top: 0.45rem;
    right: 0.5rem;
    display: none;
    min-width: 1.75rem;
    height: 1.45rem;
    padding-inline: 0.35rem;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    background: #fff;
    color: var(--ss-primary-strong);
    border: 1px solid #d7dce8;
    font-size: 0.75rem;
    font-weight: 800;
    box-shadow: 0 4px 12px rgba(36, 47, 92, 0.10);
}
.ss-opt.is-used-elsewhere .ss-opt-owner { display: inline-flex; }
.ss-opt-check {
    flex: 0 0 auto; width: 1.4rem; height: 1.4rem; opacity: 0;
    position: relative;
}
.ss-opt-check::after {
    content: ""; position: absolute; left: 0.45rem; top: 0.15rem;
    width: 0.4rem; height: 0.8rem;
    border: solid #fff; border-width: 0 0.18rem 0.18rem 0;
    transform: rotate(45deg);
}
/* Selected — filled primary. Both the JS-set class and native :checked cover it. */
.ss-opt.is-selected,
.ss-opt:has(> .ss-opt-input:checked) {
    background: var(--ss-primary);
    border-color: var(--ss-primary);
    color: #fff;
}
.ss-opt.is-selected .ss-opt-key,
.ss-opt:has(> .ss-opt-input:checked) .ss-opt-key {
    background: rgba(255, 255, 255, 0.22); color: #fff;
}
.ss-opt.is-selected .ss-opt-check,
.ss-opt:has(> .ss-opt-input:checked) .ss-opt-check { opacity: 1; }
/* Keyboard focus ring on the label when its hidden radio is focused. */
.ss-opt:has(> .ss-opt-input:focus-visible) {
    outline: 0; box-shadow: 0 0 0 0.1rem #fff, 0 0 0 0.25rem var(--ss-accent);
}
.ss-opt-input:disabled ~ .ss-opt-text,
.ss-opt-input:disabled ~ .ss-opt-key { opacity: 0.75; }
.ss-opt:has(> .ss-opt-input:disabled) { cursor: default; }

/* ── True/False/Not Given toggle — a horizontal button-group variant of .ss-optlist ────────
   Reuses the exact same markup/JS wiring as the vertical radio list (is-selected sync, digit-
   shortcut selection, autosave via .ss-answer) — only the layout and glassmorphic treatment
   differ, so no script changes are needed for this control. */
.ss-optlist.ss-optlist--tfng {
    display: grid !important;
    grid-template-columns: 1fr !important;
    grid-template-rows: repeat(3, 1fr) !important;
    width: 100% !important;
    max-width: 220px !important;
    height: 380px !important;
    margin-inline: auto;
    background: var(--ss-surface) !important;
    padding: 16px !important;
    border-radius: 40px !important;
    border: 1.5px solid var(--ss-border) !important;
    gap: 12px !important;
    position: relative !important;
    box-shadow: 0 12px 40px rgba(23, 26, 33, 0.04) !important;
}

.ss-opt.ss-opt--tfng {
    display: flex !important;
    flex-direction: column !important;
    justify-content: center !important;
    align-items: center !important;
    height: 100% !important;
    min-height: 0 !important;
    padding: 8px !important;
    border-radius: 28px !important;
    border: none !important;
    background: transparent !important;
    box-shadow: none !important;
    transition: all 0.28s cubic-bezier(0.16, 1, 0.3, 1) !important;
    cursor: pointer !important;
    position: relative !important;
}

/* Custom SVG Icons - 3x area scale */
.ss-tfng-svg {
    width: 58px;
    height: 58px;
    color: rgba(23, 26, 33, 0.32);
    transition: all 0.28s cubic-bezier(0.16, 1, 0.3, 1) !important;
    flex-shrink: 0;
}

/* Hover effect on unselected options */
.ss-opt.ss-opt--tfng:hover .ss-tfng-svg {
    transform: scale(1.16) !important;
    color: var(--ss-ink) !important;
}

/* Hide labels visually but keep them for screen-readers (a11y) */
.ss-opt.ss-opt--tfng .ss-opt-text {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    padding: 0 !important;
    margin: -1px !important;
    overflow: hidden !important;
    clip: rect(0, 0, 0, 0) !important;
    white-space: nowrap !important;
    border: 0 !important;
}

.ss-opt.ss-opt--tfng .ss-opt-key,
.ss-opt.ss-opt--tfng .ss-opt-check {
    display: none !important;
}

/* Active Glow States for Icons (adjusted for 3x size) */
.ss-optlist--tfng:has(.ss-opt--tfng-yes > input:checked) .ss-opt--tfng-yes .ss-tfng-svg,
.ss-optlist--tfng:has(.ss-opt--tfng-yes.is-selected) .ss-opt--tfng-yes .ss-tfng-svg {
    color: var(--ss-green) !important;
    filter: drop-shadow(0 0 16px rgba(24, 160, 88, 0.7)) drop-shadow(0 0 4px rgba(24, 160, 88, 0.4)) !important;
    transform: scale(1.18) !important;
}

.ss-optlist--tfng:has(.ss-opt--tfng-no > input:checked) .ss-opt--tfng-no .ss-tfng-svg,
.ss-optlist--tfng:has(.ss-opt--tfng-no.is-selected) .ss-opt--tfng-no .ss-tfng-svg {
    color: var(--ss-danger) !important;
    filter: drop-shadow(0 0 16px rgba(229, 72, 77, 0.7)) drop-shadow(0 0 4px rgba(229, 72, 77, 0.4)) !important;
    transform: scale(1.18) !important;
}

.ss-optlist--tfng:has(.ss-opt--tfng-ng > input:checked) .ss-opt--tfng-ng .ss-tfng-svg,
.ss-optlist--tfng:has(.ss-opt--tfng-ng.is-selected) .ss-opt--tfng-ng .ss-tfng-svg {
    color: var(--ss-muted) !important;
    filter: drop-shadow(0 0 16px rgba(102, 112, 133, 0.7)) drop-shadow(0 0 4px rgba(102, 112, 133, 0.4)) !important;
    transform: scale(1.18) !important;
}

/* Sibling dim & shrink when one is active */
.ss-optlist--tfng:has(.ss-opt-input:checked) .ss-opt.ss-opt--tfng:not(:has(> .ss-opt-input:checked)) .ss-tfng-svg,
.ss-optlist--tfng:has(.ss-opt.is-selected) .ss-opt.ss-opt--tfng:not(.is-selected) .ss-tfng-svg {
    color: rgba(23, 26, 33, 0.12) !important;
    opacity: 0.45 !important;
    transform: scale(0.85) !important;
}

/* ── Carousel Slide animation for changing pages ─────────────────────────── */
@keyframes ss-carousel-slide {
    from { opacity: 0; transform: scale(0.97) translateY(10px); }
    to { opacity: 1; transform: scale(1) translateY(0); }
}

.ss-sheet--focus .ss-qfield.is-current {
    display: grid !important;
    animation: ss-carousel-slide 0.28s cubic-bezier(0.16, 1, 0.3, 1) forwards !important;
}

@media (max-width: 420px) {
    .ss-qfield { padding: 0.8rem 0.75rem; gap: 0.6rem; }
    .ss-qfield-marker { min-width: 1.9rem; font-size: 0.875rem; }
}

/* Answer sheet lives in a calm centered column on every screen — no wide-desktop
   sprawl. Applied to the exam layout's <main> only, so admin pages are unaffected. */
.ss-exam-main { max-width: 760px; margin-inline: auto; }

/* ── Focus mode (one question per screen) ──────────────────────────────────
   Enabled only when the JS controller adds `.ss-sheet--focus`. Without it the shell
   is a plain scrollable list (accessible / no-JS fallback). */
.ss-focus-track { display: block; }
.ss-sheet--focus .ss-focus-grouphead { display: none; }
.ss-sheet--focus .ss-qfield { display: none; }
.ss-sheet--focus .ss-qfield.is-current { display: flex; }
.ss-sheet--focus .ss-qfield.is-current:focus { outline: none; }
.ss-sheet--focus .ss-focus-endnote { display: none; }

.ss-focus-head {
    font-size: 0.82rem; font-weight: 700; letter-spacing: 0.05em;
    text-transform: uppercase; color: var(--ss-text-muted);
    text-align: center;
    margin-bottom: 0.9rem;
}

.ss-focus-navigator {
    display: flex; flex-wrap: wrap; justify-content: center;
    gap: 0.5rem; margin-bottom: 1rem;
    max-height: 8rem; overflow-y: auto;
}
.ss-nav-chip {
    position: relative;
    min-width: 2.7rem; height: 2.7rem; padding: 0 0.6rem;
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid var(--ss-border-light);
    border-radius: 0.7rem;
    background: var(--ss-surface);
    color: var(--ss-text-muted);
    font-weight: 700; font-size: 0.98rem;
    cursor: pointer;
}
/* Semantics: unanswered = neutral, answered (not current) = soft purple, current = strong purple. */
.ss-nav-chip.is-answered { background: var(--ss-primary-soft); color: var(--ss-primary-strong); border-color: transparent; }
.ss-nav-chip.is-current { background: var(--ss-primary); color: #fff; border-color: var(--ss-primary); }
/* Student-facing "not sure, come back to this" mark — a small amber dot, independent of the
   answered/current fill above so it stays visible in every combination. */
.ss-nav-chip.is-flagged::after {
    content: "";
    position: absolute; top: -3px; right: -3px;
    width: 9px; height: 9px; border-radius: 50%;
    background: var(--ss-amber);
    box-shadow: 0 0 0 2px var(--ss-surface);
}

.ss-focus-footer {
    display: flex; align-items: center; gap: 0.6rem;
}
.ss-focus-prev, .ss-focus-next, .ss-focus-flag {
    height: 3.5rem;
    border-radius: 1rem; border: 1px solid var(--ss-border-light);
    background: var(--ss-surface); color: var(--ss-ink);
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem;
}
.ss-focus-prev, .ss-focus-flag { flex: 0 0 auto; width: 3.5rem; font-size: 1.3rem; }
.ss-focus-next {
    flex: 1 1 auto; padding: 0 1.2rem;
    font-size: 1.15rem; font-weight: 700;
    background: var(--ss-ink); color: #fff; border-color: var(--ss-ink);
}
.ss-focus-next.is-submit { background: var(--ss-primary); border-color: var(--ss-primary); }
.ss-focus-prev:disabled { opacity: 0.4; cursor: default; }
.ss-focus-next:disabled { opacity: 0.45; cursor: default; }
.ss-focus-flag svg { width: 18px; height: 18px; }
/* Amber at rest — the iPad showed the old white-on-white flag was effectively invisible. Resting
   state is the soft amber tint; flagged (is-active) fills solid so the toggle reads instantly. */
.ss-focus-flag {
    background: var(--ss-amber-soft); border-color: var(--ss-amber-border); color: var(--ss-amber);
}
.ss-focus-flag.is-active {
    background: var(--ss-amber); border-color: var(--ss-amber); color: #fff;
}

/* ── Full-screen answer-sheet app shell ─────────────────────────────────────
   The student answer sheet fills the whole viewport: fixed header (title + progress
   plates), a scrollable question area in the middle, and a fixed bottom dock with the
   question-number squares + prev/next. Used only by _StudentAnswerSheetLayout. */
.ss-sheet-page {
    margin: 0;
    min-height: 100vh;
    min-height: 100dvh;
    /* Indigo Pulse --paper / --paper-2 (site/assets/styles.css), replacing the old Friendly
       Luxury sage/ivory/peach wash. Everything else on this page (cards, options, buttons,
       the paper-overlay widgets) stays the shared Warm Frosted Glass --ss-* system used by the
       teacher/admin UI on purpose — student and teacher ink surfaces are meant to read as one
       system (see #ss-paper-pages above). Only the page's own background wash follows the
       current brand palette. */
    background: linear-gradient(160deg, #f7f8fc 0%, #edeffa 100%);
    background-attachment: fixed;
}
/* Aurora Tanterem depth layer (2026-07-23, approved from the three-direction exploration): the
   smartsuli.net hero's aurora blobs + masked grid-paper squares (site/assets/styles.css .aurora /
   .gridpaper), rendered by two fixed divs in _StudentAnswerSheetLayout. Colors are the Indigo
   Pulse site tokens (pine/gold/coral) verbatim, like the page wash above. Static on purpose —
   no drift animation on an exam surface. Negative z-index keeps them under every in-flow
   surface, and the glass card/dock backdrop-filters finally have something real to frost. */
.ss-sheet-aurora {
    position: fixed; inset: 0; z-index: -2;
    overflow: hidden; pointer-events: none;
}
.ss-sheet-aurora i {
    position: absolute; border-radius: 50%;
    filter: blur(90px); opacity: 0.5;
}
.ss-sheet-aurora i:nth-child(1) { width: 55vw; height: 55vw; background: rgba(64, 56, 168, 0.30); top: -20%; left: -12%; }
.ss-sheet-aurora i:nth-child(2) { width: 42vw; height: 42vw; background: rgba(20, 184, 166, 0.30); bottom: -18%; right: -8%; }
.ss-sheet-aurora i:nth-child(3) { width: 30vw; height: 30vw; background: rgba(229, 96, 79, 0.16); top: 34%; right: 26%; }
.ss-sheet-gridpaper {
    position: fixed; inset: 0; z-index: -1;
    opacity: 0.55; pointer-events: none;
    background-image:
        linear-gradient(rgba(25, 27, 46, 0.10) 1px, transparent 1px),
        linear-gradient(90deg, rgba(25, 27, 46, 0.10) 1px, transparent 1px);
    background-size: 44px 44px;
    mask-image: radial-gradient(ellipse 90% 70% at 50% 38%, black 30%, transparent 78%);
    -webkit-mask-image: radial-gradient(ellipse 90% 70% at 50% 38%, black 30%, transparent 78%);
}
.ss-sheet-app {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    min-height: 100dvh;
}
/* Same `min-height` trap the paper view hit below, reached from the other direction: with only a
   minimum, a question whose option stack is taller than the screen — eight long headings on a
   phone — grows the whole shell instead of clamping it, so `.ss-sheet-mid` never overflows, the
   document scrolls in its place, and both the prompt at the top and the dock at the bottom leave
   the screen. Bounded height puts the scroll back inside the card, where the prompt and the
   navigation stay put. Scoped to `--focus`, added by the controller alongside `.ss-sheet--focus`:
   the no-JS fallback really is a long plain list and really does want the document to scroll. */
.ss-sheet-app--focus {
    height: 100vh;
    height: 100dvh;
}
.ss-sheet-app--focus > .ss-sheet-mid { min-height: 0; }
/* The paper page's PDF stack can be far taller than the viewport (a 77-page paper), unlike the
   list view this shell is shared with. `min-height` alone lets a flex column grow past the
   viewport to fit its content instead of clamping it, so `.ss-sheet-mid` below never gets an
   actual overflow to scroll — every touch/pointer gesture routed at it becomes a no-op and the
   page falls back to native document scrolling, which page-wrap ink layers then block. Give
   only the paper instance of the shell a real, bounded height and let its flex child shrink
   below its content size so `.ss-sheet-mid`'s existing `overflow-y: auto` finally applies. */
.ss-sheet-app--paper {
    height: 100vh;
    height: 100dvh;
    /* #ss-paper-pages's own white/grid background only fills its own box, not the padding
       around it or the rest of .ss-sheet-mid — the list view's warm .ss-sheet-page body
       gradient was still showing through those gaps. Opaque white here, scoped to the paper
       view only, fully covers it without touching the list view at all. */
    background: #fff;
}
.ss-sheet-app--paper > .ss-sheet-mid { min-height: 0; }

/* The paper surface owns every touch on itself. `.ss-sheet-mid` below carries
   `touch-action: pan-x pan-y`, which refuses browser ZOOM but explicitly grants the browser
   permission to PAN — and that permission is what made a pinch move the paper.

   Measured on-device, 2026-08-06 23:16, with tools/ipad-zoom-anchor-trace-bookmarklet.txt: during
   a single pinch the content under the screen centre moved ~1000px further than ANY anchored zoom
   can produce (solving for the anchor that would explain it gives −1876px, i.e. above the top of
   the screen), while the tracer's noFinger counter stayed at 0.0 — so nothing moved the paper
   between gestures, and the extra travel arrived while two fingers were down. Safari was scrolling
   this container natively underneath the app's own zoom, and on iOS a native scroll that has begun
   ignores every later preventDefault, so the coordinator could not take it back.

   Declared on the SCROLLING element deliberately. A native pan is only permitted when the touched
   element AND the scrolling ancestor both allow it, so refusing here also covers the descendants
   that opt back into panning for their own reasons (the answer fields, .ss-px-remote) without
   having to find and change each one.

   Scoped to the paper app: every other .ss-sheet-mid page scrolls natively and has no JS scroller
   to take over. Here one-finger panning is implemented in exam-ink-engine.mjs — by the per-page
   controllers inside a page, and by the pinch coordinator's fallback drag everywhere else. */
.ss-sheet-app--paper > .ss-sheet-mid { touch-action: none; }

/* This scroller's position belongs to the app, full stop — the browser must not "help".

   Found from the 2026-08-07 08:40 device trace, after touch-action: none was already confirmed
   applied on-device: during a pinch, the anchor drifted by almost exactly what applyScale wrote
   (worst 4863.7px of drift against 4861px of writes; a second gesture 5749 against 5506), while
   NATIVE — scrollTop movement that never passed through the element's setter — ran in the
   opposite direction. That is compensation being UNDONE from outside: a correct write train would
   show large writes and zero drift, not drift equal to the writes.

   With touch scrolling already refused, the mechanism that rewrites a scroller's position in
   response to LAYOUT changes rather than touches is CSS scroll anchoring (in Safari since 18.4).
   Every pinch frame rescales .ss-paper-zoom-stage and resizes .ss-paper-zoom-sizer; anchoring
   reads that as content shifting and adjusts scrollTop to hold it still — then the app writes its
   own compensation, and the two fight. Headless tests never see this because the app's
   resize → forced reflow → write sequence resolves synchronously there, so the app's write lands
   last; on iOS scrolling is compositor-side and async, and the browser's correction lands after
   ours, between frames.

   overflow-anchor: none is the standard opt-out every JS-driven scroller ships, harmless where
   unsupported. overscroll-behavior: none additionally stops rubber-band bounce settling from
   showing up as no-finger movement after a hard fling into the document ends. Applied to every
   paper scroll owner: the student pair (.ss-sheet-mid vertical, #ss-paper-pages horizontal) and
   the teacher/companion container (.ss-grading-paper-pages). */
.ss-sheet-app--paper > .ss-sheet-mid,
.ss-sheet-app--paper #ss-paper-pages,
.ss-grading-paper-pages {
    overflow-anchor: none;
    overscroll-behavior: none;
}
/* #ss-paper-pages owns ONLY the horizontal axis — it never scrolls vertically. But
   `overscroll-behavior: none` above makes it a scroll-chain boundary on BOTH axes, and a wheel
   (Mac trackpad two-finger scroll) latches onto the nearest scroll container under the pointer:
   the vertical delta stops dead at this boundary instead of chaining up to `.ss-sheet-mid`, so
   desktop trackpad scrolling of the paper died entirely. Touch was never affected (refused wholly
   by touch-action: none and re-implemented in JS), which is why the iPad kept working and only the
   MacBook broke. Re-open the y axis: vertical wheel deltas chain up to `.ss-sheet-mid`, whose own
   `none` still refuses chaining onward to the document, and x stays contained here. */
.ss-sheet-app--paper #ss-paper-pages {
    overscroll-behavior-x: none;
    overscroll-behavior-y: auto;
}
.ss-sheet-wrap { width: 100%; max-width: 760px; margin-inline: auto; padding-inline: 16px; }
/* The paper view renders an actual PDF page image, not running text — the 760px reading-width
   cap above (right for the list view's prompt/option text) just wastes the desktop window. */
.ss-sheet-wrap--paper { max-width: none; }
/* Same faint-grid whiteboard background as the admin/teacher .ss-grading-paper-pages, so every
   ink surface in the app reads as one system instead of the list view's warm paper gradient. */
#ss-paper-pages {
    background:
        repeating-linear-gradient(0deg, transparent, transparent 39px, rgba(23, 26, 33, .035) 40px),
        repeating-linear-gradient(90deg, transparent, transparent 39px, rgba(23, 26, 33, .035) 40px),
        #fff;
}

.ss-sheet-top { flex: 0 0 auto; padding-top: max(env(safe-area-inset-top), 12px); padding-bottom: 8px; }
.ss-sheet-titlerow { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.ss-sheet-titleside { display: flex; align-items: center; gap: 14px; flex: none; }
/* Exit: deliberately quieter than .ss-paperview-btn — leaving is always available but is never the
   thing we want the student to reach for, so it reads as a back link, not a second action button. */
.ss-sheet-exit {
    display: inline-flex; align-items: center; gap: 4px; flex: none;
    color: #6b6f92; font-size: 0.85rem; font-weight: 600; white-space: nowrap;
    text-decoration: none;
    transition: color 0.15s ease;
}
.ss-sheet-exit svg { width: 1rem; height: 1rem; }
.ss-sheet-exit:hover, .ss-sheet-exit:focus-visible { color: #4038a8; }
/* Paper-view action: a ghost pill in the title row — secondary rank, but a real button, not a
   footnote link. Site pine indigo on purpose (it belongs to the aurora layer's world). */
.ss-paperview-btn {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 8px 16px; border-radius: 999px;
    border: 1.5px solid #4038a8;
    color: #4038a8; background: transparent;
    font-size: 0.85rem; font-weight: 700; white-space: nowrap;
    text-decoration: none;
    transition: background 0.15s ease, color 0.15s ease;
}
.ss-paperview-btn:hover { background: rgba(64, 56, 168, 0.08); color: #4038a8; }
.ss-paperview-btn:focus-visible { outline: 2px solid var(--ss-accent-cyan); outline-offset: 2px; }
.ss-paperview-btn svg { width: 15px; height: 15px; flex: none; }
.ss-sheet-title {
    font-family: var(--ss-font-display);
    font-size: 1.65rem; font-weight: 600; line-height: 1.05; margin: 0; color: var(--ss-ink);
}
.ss-sheet-save { font-size: 0.9rem; color: var(--ss-text-muted); white-space: nowrap; }
.ss-sheet-save::before {
    content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%;
    background: currentColor; margin-right: 5px; vertical-align: middle; opacity: 0.75;
}
.ss-sheet-save:empty::before { display: none; }
.ss-sheet-save.is-saved { color: var(--ss-success); }
.ss-sheet-save.is-error { color: var(--ss-danger); }

/* The plates sit in a white bordered rail: over the aurora/grid background the rail supplies the
   contrast, so both the empty indigo-tinted track and the filled state read from across a desk. */
.ss-sheet-plates {
    display: flex; gap: 6px; margin-top: 14px;
    padding: 5px; border-radius: 999px;
    background: rgba(255, 255, 255, 0.62);
    border: 1px solid rgba(64, 56, 168, 0.16);
}
/* Three distinct states, independent of each other so "current" always reads as "you are here"
   regardless of whether the question is answered yet:
   - default (unanswered) = neutral grey fill
   - answered = SmartSuli purple fill
   - current = a ring + slightly taller bar, layered on top of whichever fill applies above —
     this is the at-a-glance position indicator that replaces the "3 / 40" counter. */
.ss-plate {
    height: 6px; flex: 1 1 0; border-radius: 20px;
    background: rgba(64, 56, 168, 0.15);
    transition: background 0.15s ease, box-shadow 0.15s ease, height 0.15s ease;
}
.ss-plate.is-answered { background: var(--ss-primary); }
.ss-plate.is-current { height: 8px; box-shadow: 0 0 0 2px var(--ss-accent-cyan); }

.ss-sheet-mid {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    padding-block: 10px;
    touch-action: pan-x pan-y;
}
/* Message/waiting states sit centered; the answer sheet itself fills the space. */
.ss-sheet-mid > .ss-sheet-message { margin-block: auto; }

/* The answer card fills the available middle area — no empty bands above/below it — and its
   content (prompt + options) is vertically centered inside that grown card. Options themselves
   keep a comfortable, roughly-fixed tap height rather than stretching into huge slabs. */
.ss-sheet-wrap--fill { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
.ss-sheet-wrap--fill #answerForm { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
.ss-sheet-wrap--fill .ss-focus-shell {
    flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0;
    position: relative;
}
.ss-sheet--focus .ss-focus-track { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
/* Real 2018 iPad Pro trace (2026-07-15): giving .ss-sheet-mid a real bounded height (above) fixed
   the scroll owner on desktop/Chromium, but on-device the 77-page stack still never registered as
   overflow — .ss-sheet-mid.scrollHeight stayed equal to its clientHeight even though the page
   stack measured >130,000px. WebKit does not reliably propagate a deeply nested flex item's content
   size past several chained `min-height: 0`/flex-shrink layers into an ancestor's scrollable
   overflow region, even though the content renders and Chromium handles the same chain correctly.
   The paper's PDF stack is never short "centered" content the way an answer card is — it's always
   meant to overflow and scroll — so take .ss-sheet-wrap--paper and its #answerForm back out of
   that flex-centering chain entirely. Plain block content overflowing a bounded overflow-y: auto
   ancestor is unambiguous, ordinary CSS with no flexbox min-height edge case involved. */
.ss-sheet-wrap--paper,
.ss-sheet-wrap--paper #answerForm {
    display: block;
    min-height: 0;
}
/* Dissolve the group wrapper in focus mode so the single visible question becomes a direct flex
   child of the track and can grow — closest('.ss-focus-group') in JS still works with `contents`. */
.ss-sheet--focus .ss-focus-group { display: contents; }

/* Grid, not flex-row-with-a-leftover-column: a flex row would center the option stack only
   within the space left over after the marker column, shifting it visibly right of the card's
   true center. Grid gives the answer row its own full-width track spanning both columns, so
   `.ss-optlist`'s auto margins center it against the whole card — not against the prompt column. */
.ss-sheet--focus .ss-qfield.is-current {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-rows: auto 1fr;
    row-gap: 0.9rem;
    flex: 1 1 auto; min-height: 0;
    max-width: 560px; width: 100%; margin-inline: auto; margin-bottom: 0;
    border-radius: 26px; padding: 1.2rem 1.2rem;
    background: color-mix(in srgb, var(--ss-surface) 72%, transparent);
    border-color: rgba(64, 56, 168, 0.14);
    box-shadow: 0 18px 44px -12px rgba(64, 56, 168, 0.28);
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
    position: relative;
    isolation: isolate;
}
/* The current question number is a decorative orientation cue: oversized, quiet, and allowed to
   drift beyond the card edge so it reads as sitting partly behind the translucent white panel. */
.ss-sheet--focus .ss-qfield.is-current::before {
    content: attr(data-marker);
    position: absolute;
    z-index: -1;
    top: -0.18em;
    right: -0.12em;
    color: var(--ss-primary);
    font-family: var(--ss-font-display);
    font-size: clamp(9rem, 24vw, 16rem);
    font-weight: 800;
    line-height: 0.8;
    letter-spacing: -0.08em;
    opacity: 0.09;
    pointer-events: none;
    user-select: none;
}
/* Dissolve the body wrapper so the prompt and the input row become direct grid items of the
   card and can be placed independently (header row vs. full-width answer row). */
.ss-sheet--focus .ss-qfield.is-current .ss-qfield-body { display: contents; }
.ss-sheet--focus .ss-qfield.is-current .ss-qfield-marker { grid-column: 1; grid-row: 1; align-self: start; }
.ss-sheet--focus .ss-qfield.is-current .ss-qfield-prompt { grid-column: 2; grid-row: 1; align-self: start; margin-bottom: 0; }
.ss-sheet--focus .ss-qfield.is-current .ss-qfield-input {
    grid-column: 1 / -1; grid-row: 2; min-height: 0;
    align-self: stretch; height: 100%;
    display: flex; flex-direction: column;
    /* `safe center` (not plain `center`): once the option stack is taller than the answer row —
       eight long headings on a phone — plain centering pushes the first option past the top edge
       into unreachable overflow. `safe` falls back to start-alignment in exactly that case. */
    justify-content: safe center;
}
/* Option stack: an explicit 86% of the card's inner width (not "however much space is left"),
   capped so it doesn't sprawl on wider screens — this is what guarantees equal left/right
   margins against the card, and what stops the size from oscillating between passes. */
.ss-sheet--focus .ss-optlist { width: 86%; max-width: 420px; margin-inline: auto; gap: 16px; }
.ss-sheet--focus .ss-opt { min-height: 74px; flex: 0 0 auto; }

/* ── Long-option fields get a wider card ───────────────────────────────────
   The 560px cap above is a reading width for a prompt plus short options ("A 12", True/False).
   Heading-matching and the other shared-option-set exercises carry six-to-ten-word options, and
   two ~230px columns turn each of them into a five-line block: the card reads as a wall of text
   while most of a desktop window sits empty beside it. So the width follows the content — the
   focus controller measures the longest rendered option of each question and stamps
   `.has-longopts`, which is what keeps a True/False card from being blown up to fill a 1440px
   window it has no use for. Two columns stay two columns at every width: three would leave the
   usual eight-heading set with a ragged 3/3/2 last row, and the pairs stop scanning as a list. */
/* 701px, not a desktop-only 900px: an iPad in portrait is 834px wide, which is exactly the band
   where the grid has already split into two columns but the card is still pinned to 560px — the
   most crowded width there is. The cap is relative (94vw), so the same rule serves a tablet and a
   27" monitor. 700px is the boundary shared with the phone rules below, so no width is governed
   by both. */
@media (min-width: 701px) {
    /* The card can only be as wide as the column it sits in, and that column is the 760px
       reading measure set on .ss-sheet-wrap — so the column has to open up with it. Scoped to
       --fill (the answer area): the header and the dock keep the calm 760px column, which is
       what stops the plates and the number chips from stretching into a near-empty glass bar. */
    /* The column is deliberately wider than the card by --ss-nav-gutter on each side. The desktop
       prev/next chevrons are absolutely positioned against the shell, so that difference is the
       only thing keeping them off the white panel: at the old 1120/1080 pair the gap was about
       4px and they sat on the card's edge, overlapping the first and last option. 64px clears the
       3rem chevron with room either side. */
    .ss-sheet-wrap--fill.has-wide-question {
        --ss-nav-gutter: 64px;
        max-width: min(1240px, 96vw);
    }
    /* Prose options in a single column (three or fewer, so the server never built the grid): more
       room than the 560px reading card, but nowhere near the two-column width — the stack below is
       capped at 620px and the rest would just be glass. Same gutter arithmetic: 900 − 32 − 740 = 128,
       so 64px a side. */
    .ss-sheet-wrap--fill.has-wide-question--single { max-width: 900px; }
    .ss-sheet--focus .ss-qfield.is-current.has-longopts {
        /* The vw term subtracts both gutters so the chevrons stay outside the card at every width,
           not only where the 1080px cap is the binding constraint. */
        max-width: min(1080px, calc(96vw - 160px));
        padding: 1.4rem 1.6rem;
        /* Height stops following the viewport once the card is wide. The default `flex: 1 1 auto`
           exists so a narrow card fills the middle instead of floating in a band of empty page —
           but eight headings that now fit in four short rows leave that same full-height card
           two-thirds hollow. Shrink to the content and centre the whole card instead. */
        flex: 0 1 auto;
        margin-block: auto;
        row-gap: 1.4rem;
    }
    /* Same card, one column: keep it near the reading measure. Ordered after the block above but
       it does not depend on that — the :not() makes this the more specific selector either way. */
    .ss-sheet--focus .ss-qfield.is-current.has-longopts:not(.has-optgrid) { max-width: 740px; }
    .ss-sheet--focus .ss-qfield.is-current.has-longopts .ss-optlist--grid {
        width: 100%;
        max-width: none;
    }
    /* Non-grid stacks (≤3 long options) widen too, but only to a comfortable single-column
       measure — a 1000px-wide single option button would be absurd. */
    .ss-sheet--focus .ss-qfield.is-current.has-longopts .ss-optlist:not(.ss-optlist--grid) {
        max-width: 620px;
    }
    /* The decorative marker is sized off the viewport, so on the wide card it stopped being a
       corner cue and started sitting across the top of the second column. Cap it there. */
    .ss-sheet--focus .ss-qfield.is-current.has-longopts::before {
        font-size: clamp(7rem, 11vw, 11rem);
        opacity: 0.07;
    }
}

/* ── Phone: the option stack scrolls inside the card, the prompt stays put ──
   Eight long headings are ~830px of options against ~450px of usable height, so something has to
   scroll. Today it is the whole page, which carries the prompt ("27 Paragraph A") off the top of
   the screen — the one line the student needs while reading the headings. Making the answer row
   itself the scroller pins the marker + prompt and keeps the dock reachable. Clamping the text
   was the obvious alternative and is the wrong trade for a reading exam: the full wording of
   each heading IS the question. */
.ss-sheet--focus .ss-qfield.is-current { max-height: 100%; }
.ss-sheet--focus .ss-qfield.is-current .ss-qfield-input {
    overflow-y: auto;
    overscroll-behavior-y: contain;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}
.ss-sheet--focus .ss-qfield.is-current .ss-qfield-input::-webkit-scrollbar { display: none; }
/* Bottom fade = "there is more below". Removed at the end of the scroll (.is-at-end) so the last
   option never looks half-disabled. Applied as a mask, not an overlay, because the card is
   translucent glass — an opaque gradient strip would show as a smear over the aurora. */
.ss-sheet--focus .ss-qfield.is-current .ss-qfield-input.is-scrollable:not(.is-at-end) {
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 34px), transparent 100%);
    mask-image: linear-gradient(to bottom, #000 calc(100% - 34px), transparent 100%);
}
/* The reviewed state used to have to give this fade up: a mask fades everything the element
   paints, and back when the verdict ring was drawn on this same box the bottom of the glow washed
   out mid-scroll. The ring now sits on the card instead, outside the masked box entirely, so a
   marked question keeps its scroll cue like every other one. */

@media (max-width: 700px) {
    /* Denser rows for long options only: 74px of height per row is generous for "True", wasteful
       when the row is three lines of text tall anyway. Buys roughly one extra visible option. */
    .ss-sheet--focus .ss-qfield.is-current.has-longopts .ss-optlist { gap: 10px; width: 100%; }
    .ss-sheet--focus .ss-qfield.is-current.has-longopts .ss-opt {
        min-height: 58px;
        padding: 0.6rem 0.75rem;
        gap: 0.6rem;
        font-size: 1rem;
    }
    .ss-sheet--focus .ss-qfield.is-current.has-longopts .ss-opt-text { line-height: 1.25; }
    .ss-sheet--focus .ss-qfield.is-current.has-longopts .ss-opt-key { width: 1.7rem; height: 1.7rem; }
}

/* On a short phone (an SE at 667px, or anything in landscape) the fixed chrome — title, plates,
   group head, dock — takes 250px before the first option is drawn, and the card is left with
   four of the eight headings. Trim the chrome rather than the options: everything here is
   spacing and type scale, and the tap targets stay at or above 48px. */
@media (max-width: 700px) and (max-height: 740px) {
    .ss-sheet-top { padding-bottom: 4px; }
    .ss-sheet-title { font-size: 1.35rem; }
    .ss-sheet-plates { margin-top: 9px; }
    .ss-focus-head { margin-bottom: 0.5rem; }
    .ss-sheet--focus .ss-qfield.is-current { padding: 0.9rem; row-gap: 0.6rem; }
    .ss-sheet-dockpanel { padding: 10px 10px 9px; border-radius: 20px; }
    .ss-sheet-dockpanel .ss-focus-footer { margin-top: 9px; }
    .ss-focus-prev, .ss-focus-next, .ss-focus-flag { height: 3rem; }
    .ss-focus-prev, .ss-focus-flag { width: 3rem; }
}

.ss-sheet-message { padding-block: 2rem; }
.ss-sheet-message-title { font-size: 1.1rem; font-weight: 600; margin-bottom: 6px; color: var(--ss-ink); }
.ss-sheet-readonly-note {
    font-size: 0.85rem; color: var(--ss-amber); background: var(--ss-amber-soft);
    border: 1px solid var(--ss-amber-border); border-radius: 10px; padding: 8px 12px; margin-bottom: 12px;
}
.ss-sheet-instructions { font-size: 0.9rem; color: var(--ss-text-muted); margin-bottom: 12px; }
.ss-sheet-refresh-note {
    background: var(--ss-primary-soft); color: var(--ss-primary-strong);
    border-radius: 10px; padding: 8px 12px; margin-bottom: 12px; font-size: 0.85rem;
}

.ss-sheet-dock {
    flex: 0 0 auto;
    background: transparent;
    padding-top: 6px; padding-bottom: max(env(safe-area-inset-bottom), 14px);
}
/* One coherent control surface — number chips + prev/flag/next all live inside a single glass
   panel, instead of separate floating rows on transparent background. */
.ss-sheet-dockpanel {
    background: color-mix(in srgb, var(--ss-surface) 66%, transparent);
    border: 1px solid rgba(64, 56, 168, 0.14);
    border-radius: 24px;
    padding: 14px 14px 12px;
    box-shadow: 0 12px 30px -10px rgba(64, 56, 168, 0.22);
    -webkit-backdrop-filter: blur(16px);
    backdrop-filter: blur(16px);
}
.ss-sheet-numbers {
    display: flex; gap: 7px; justify-content: safe center;
    overflow-x: auto; scrollbar-width: none;
    /* dev-planning/25 item 3 (real fix — the flagged chips live HERE, in the dock's #ssNumbers,
       not the dead .ss-focus-navigator the first attempt padded): overflow-x: auto forces
       overflow-y to auto too, so the .ss-nav-chip.is-flagged::after amber dot (top:-3px, right:-3px)
       was sheared at the top. Inner padding gives it room inside the scroll box. */
    padding: 5px 5px 3px;
}
.ss-sheet-numbers::-webkit-scrollbar { display: none; }
.ss-sheet-dockpanel .ss-focus-footer { margin-top: 12px; }
.ss-desktop-nav {
    display: none;
}

@media (min-width: 800px) {
    .ss-desktop-nav {
        position: absolute;
        z-index: 3;
        top: 50%;
        display: inline-flex;
        width: 3rem;
        height: 4.5rem;
        padding: 0;
        align-items: center;
        justify-content: center;
        border: 0;
        border-radius: 999px;
        background: transparent;
        color: color-mix(in srgb, var(--ss-ink) 42%, transparent);
        transform: translateY(-50%);
        transition: color 0.15s ease, background-color 0.15s ease;
    }
    .ss-desktop-nav--prev { left: 0; }
    .ss-desktop-nav--next { right: 0; }
    .ss-desktop-nav svg {
        width: 1.75rem;
        height: 1.75rem;
        fill: none;
        stroke: currentColor;
        stroke-width: 1.5;
        stroke-linecap: round;
        stroke-linejoin: round;
    }
    .ss-desktop-nav:hover:not(:disabled) {
        color: var(--ss-primary);
        background: color-mix(in srgb, var(--ss-surface) 46%, transparent);
    }
    .ss-desktop-nav:focus-visible {
        outline: 2px solid var(--ss-primary);
        outline-offset: 2px;
    }
    .ss-desktop-nav:disabled {
        opacity: 0.2;
        cursor: default;
    }
    .ss-sheet-dockpanel .ss-focus-prev,
    .ss-sheet-dockpanel .ss-focus-next {
        display: none;
    }
    .ss-sheet-dockpanel .ss-focus-next.is-submit {
        display: inline-flex;
    }
    .ss-sheet-dockpanel .ss-focus-footer {
        justify-content: center;
    }
    .ss-sheet-dockpanel .ss-focus-footer:empty {
        display: none;
    }
}
.ss-sheet-source { margin-top: 10px; text-align: center; }
.ss-sheet-source summary {
    font-size: 0.72rem; color: var(--ss-text-muted); cursor: pointer;
    list-style: none; display: inline-block;
}
.ss-sheet-source summary::-webkit-details-marker { display: none; }
.ss-sheet-source p { font-size: 0.72rem; color: var(--ss-text-muted); margin: 4px 0 0; }

@media (prefers-color-scheme: dark) {
    .ss-sheet-page { background: linear-gradient(160deg, #101223 0%, #171a33 100%); }
    /* Same aurora, dimmed to glow rather than glare on the dark wash; grid flips to light lines. */
    .ss-sheet-aurora i { opacity: 0.35; }
    .ss-sheet-gridpaper {
        opacity: 0.4;
        background-image:
            linear-gradient(rgba(234, 235, 245, 0.10) 1px, transparent 1px),
            linear-gradient(90deg, rgba(234, 235, 245, 0.10) 1px, transparent 1px);
    }
    .ss-sheet-plates { background: rgba(23, 26, 51, 0.55); border-color: rgba(157, 151, 242, 0.18); }
    .ss-paperview-btn { border-color: #9d97f2; color: #9d97f2; }
    .ss-paperview-btn:hover { background: rgba(157, 151, 242, 0.12); color: #b6b1f6; }
    /* The plain "Következő" button pairs its light --ss-ink fill (E8EAF0 in dark) with a hardcoded
       white text — unreadable in dark mode. Flip the text dark so it stays a strong neutral CTA,
       distinct from the blue --ss-primary submit variant. */
    .ss-focus-next:not(.is-submit) { color: var(--ss-bg); }
}

/* ── Source / copyright attribution footer ──────────────────────────────── */
.ss-exam-attribution {
    font-size: 0.72rem;
    color: var(--ss-text-muted);
    margin-top: 1rem;
    margin-bottom: 0;
}

/* ── Profile evidence candidates (teacher-only working hypotheses) ──────────── */
.ss-profile-evidence-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 0.75rem;
}
.ss-profile-evidence-card {
    background: var(--ss-glass-bg-strong);
    -webkit-backdrop-filter: var(--ss-glass-blur);
    backdrop-filter: var(--ss-glass-blur);
    border: 1px solid var(--ss-glass-border);
    border-left: 4px solid var(--ss-border);
    border-radius: 14px;
    box-shadow: var(--ss-glass-shadow);
    padding: 0.8rem 0.9rem;
}
.ss-evidence-badge {
    display: inline-block;
    font-size: 0.68rem;
    font-weight: 700;
    border-radius: 999px;
    padding: 0.12rem 0.55rem;
    border: 1px solid transparent;
}
/* Strength → mint, Gap → soft coral, Focus → blue, Watch → amber. No harsh neon. */
.ss-evidence-badge-strength {
    background: var(--ss-green-soft);
    color: var(--ss-success);
    border-color: var(--ss-green-border);
}
.ss-evidence-badge-gap {
    background: #fdecea;
    color: #c0564c;
    border-color: #f3c6c0;
}
.ss-evidence-badge-focus {
    background: var(--ss-accent-soft);
    color: var(--ss-accent);
    border-color: var(--ss-accent-border);
}
.ss-evidence-badge-watch {
    background: var(--ss-amber-soft);
    color: var(--ss-amber);
    border-color: var(--ss-amber-border);
}
.ss-profile-evidence-card:has(.ss-evidence-badge-strength) { border-left-color: var(--ss-success); }
.ss-profile-evidence-card:has(.ss-evidence-badge-gap)      { border-left-color: #e0695e; }
.ss-profile-evidence-card:has(.ss-evidence-badge-focus)    { border-left-color: var(--ss-accent); }
.ss-profile-evidence-card:has(.ss-evidence-badge-watch)    { border-left-color: var(--ss-accent-yellow); }

/* ── Metric strip ───────────────────────────────────────────────────────── */
.ss-metric-strip {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 0.75rem;
    margin-bottom: 1.75rem;
}
.ss-metric-card {
    background: var(--ss-surface);
    border: 1px solid var(--ss-border);
    border-radius: var(--ss-radius);
    box-shadow: var(--ss-shadow-xs);
    padding: 0.875rem 1rem;
    position: relative;
}
.ss-metric-card::before {
    content: "";
    position: absolute;
    left: 0; top: 0.875rem; bottom: 0.875rem;
    width: 3px;
    border-radius: 0 3px 3px 0;
    background: var(--ss-border);
}
.ss-metric-value {
    font-size: 1.625rem;
    font-weight: 800;
    line-height: 1.1;
    color: var(--ss-ink);
    letter-spacing: -0.02em;
}
.ss-metric-label {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--ss-text-muted);
    margin-top: 0.125rem;
}
/* Accent variants for metric cards */
.ss-metric-action::before  { background: var(--ss-primary); }
.ss-metric-action  .ss-metric-value { color: var(--ss-primary); }
.ss-metric-safe::before    { background: var(--ss-success); }
.ss-metric-safe    .ss-metric-value { color: var(--ss-success); }
.ss-metric-review::before  { background: var(--ss-accent-yellow); }
.ss-metric-review  .ss-metric-value { color: var(--ss-warning); }
.ss-metric-danger::before  { background: var(--ss-danger); }
.ss-metric-danger  .ss-metric-value { color: var(--ss-danger); }
.ss-metric-muted   { opacity: 0.85; }
.ss-metric-cyan::before    { background: var(--ss-accent-cyan); }
.ss-metric-cyan    .ss-metric-value { color: #0F9B97; }

/* ── Workbench / decision cards ─────────────────────────────────────────── */
.ss-workbench {
    display: flex;
    flex-direction: column;
    gap: 0.875rem;
    margin-bottom: 1rem;
}
.ss-decision-card {
    background: var(--ss-surface);
    border: 1px solid var(--ss-border);
    border-radius: var(--ss-radius);
    box-shadow: var(--ss-shadow-xs);
    padding: 1rem 1.125rem;
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: flex-start;
    transition: box-shadow 0.15s ease, border-color 0.15s ease;
}
.ss-decision-card:hover {
    box-shadow: var(--ss-shadow-card);
    border-color: rgba(23,26,33,0.16);
}
.ss-decision-card-primary {
    border-color: var(--ss-accent-border);
    background:
        linear-gradient(0deg, rgba(59,91,255,0.015), rgba(59,91,255,0.015)),
        var(--ss-surface);
    border-left: 4px solid var(--ss-primary);
}
.ss-decision-card-review {
    border-left: 4px solid var(--ss-accent-yellow);
}
.ss-decision-card-danger {
    border-left: 4px solid var(--ss-danger);
}
.ss-decision-time {
    font-size: 1.0625rem;
    font-weight: 800;
    color: var(--ss-ink);
    letter-spacing: -0.01em;
    white-space: nowrap;
}
.ss-decision-meta {
    flex: 1 1 240px;
    min-width: 200px;
}
.ss-decision-title {
    font-size: 1rem;
    font-weight: 700;
    color: var(--ss-ink);
    margin-bottom: 0.25rem;
    line-height: 1.25;
}
.ss-decision-sub {
    font-size: 0.8125rem;
    color: var(--ss-text-muted);
}
.ss-decision-actions {
    flex: 0 1 auto;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    align-items: stretch;
    min-width: 220px;
}
.ss-decision-actions .ss-inline-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.375rem;
}

/* Key-value mini meta row inside a decision card */
.ss-meta-line {
    font-size: 0.8125rem;
    color: var(--ss-text-muted);
    margin-top: 0.125rem;
}
.ss-meta-line strong { color: var(--ss-ink); font-weight: 600; }

/* ── Muted / archive section ────────────────────────────────────────────── */
.ss-muted-section {
    background: var(--ss-surface-soft);
    border: 1px solid var(--ss-border-light);
    border-radius: var(--ss-radius);
    padding: 1.125rem 1.25rem;
    margin-top: 1.5rem;
}
.ss-archive-card {
    background: var(--ss-surface);
    border: 1px solid var(--ss-border-light);
    border-radius: var(--ss-radius-sm);
    padding: 0.75rem 0.875rem;
    opacity: 0.92;
}

/* ── Soft divider ───────────────────────────────────────────────────────── */
.ss-soft-divider {
    height: 1px;
    border: 0;
    background: var(--ss-border);
    margin: 1.75rem 0;
    opacity: 1;
}

/* ── Status dot + pill ──────────────────────────────────────────────────── */
.ss-status-dot {
    display: inline-block;
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    margin-right: 0.375rem;
    background: var(--ss-border);
    vertical-align: middle;
}
.ss-status-dot.is-safe   { background: var(--ss-success); }
.ss-status-dot.is-review { background: var(--ss-accent-yellow); }
.ss-status-dot.is-danger { background: var(--ss-danger); }
.ss-status-dot.is-action { background: var(--ss-primary); }
.ss-status-dot.is-muted  { background: var(--ss-muted); }

.ss-status-pill {
    display: inline-flex;
    align-items: center;
    padding: 0.2rem 0.6rem;
    border-radius: 999px;
    font-size: 0.725rem;
    font-weight: 700;
    line-height: 1.4;
    border: 1px solid transparent;
}
.ss-status-pill.is-safe   { background: var(--ss-green-soft); color: var(--ss-success); border-color: var(--ss-green-border); }
.ss-status-pill.is-review { background: var(--ss-amber-soft); color: var(--ss-warning); border-color: var(--ss-amber-border); }
.ss-status-pill.is-danger { background: var(--ss-red-soft);   color: var(--ss-danger);  border-color: var(--ss-red-border); }
.ss-status-pill.is-action { background: var(--ss-primary-soft); color: var(--ss-primary); border-color: var(--ss-accent-border); }
.ss-status-pill.is-muted  { background: var(--ss-surface-soft); color: var(--ss-muted); border-color: var(--ss-border); }

/* ── Doc 48 (S1) — matura choose-2-of-3 banner ──────────────────────────── */
.ss-choicegroup-banners { margin-bottom: 1.25rem; }
.ss-choicegroup-banner { margin-bottom: 0.75rem; }
.ss-choicegroup-banner:last-child { margin-bottom: 1.25rem; }
.ss-choicegroup-options {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}
/* A pill that is also a button: same visual language as .ss-status-pill, but interactive — bigger
   tap target and a pointer cursor, disabled state fades rather than hides (a read-only attempt still
   shows what was chosen). */
.ss-choicegroup-option {
    cursor: pointer;
    padding: 0.4rem 0.8rem;
    font-size: 0.8125rem;
    transition: filter 0.15s ease;
}
.ss-choicegroup-option:hover:not(:disabled) { filter: brightness(0.96); }
.ss-choicegroup-option:disabled { cursor: default; opacity: 0.85; }
.ss-choicegroup-option-tag {
    margin-left: 0.4rem;
    font-weight: 400;
    text-transform: none;
    opacity: 0.85;
}
.ss-choicegroup-status { min-height: 1.1em; }

/* ── Gradient accent helper ─────────────────────────────────────────────── */
.ss-gradient-accent {
    background: linear-gradient(135deg, var(--ss-primary) 0%, var(--ss-accent-lavender) 100%);
    color: #fff;
}

/* ── Table plate / shell ────────────────────────────────────────────────────
   Wrap a table in .ss-table-plate to lift it off the page as a warm, softly
   coloured surface with rounded corners — not a flat white SAP grid. Keep the
   .table-responsive wrapper inside so it can still scroll horizontally.
   ─────────────────────────────────────────────────────────────────────────── */
.ss-table-plate,
.ss-table-shell {
    background: linear-gradient(180deg, rgba(255,248,237,0.85), rgba(255,255,255,0.80));
    border: 1px solid var(--ss-border);
    border-radius: var(--ss-radius-lg);
    box-shadow: var(--ss-shadow-card);
    overflow: hidden;
    margin-bottom: 1.25rem;
}
/* The responsive wrapper handles horizontal scroll without breaking the rounded plate. */
.ss-table-plate > .table-responsive,
.ss-table-shell > .table-responsive {
    border-radius: inherit;
}
/* Tables sitting on a plate should be transparent so the plate shows through. */
.ss-table-plate .table,
.ss-table-shell .table {
    margin-bottom: 0;
    background: transparent;
    --bs-table-bg: transparent;
}
.ss-table-plate .table > :not(caption) > * > *,
.ss-table-shell .table > :not(caption) > * > * {
    background-color: transparent;
}

/* ── Modern / soft tables ───────────────────────────────────────────────── */
.ss-modern-table,
.ss-soft-table {
    border-collapse: separate;
    border-spacing: 0;
    width: 100%;
    margin-bottom: 0;
    background: transparent;
}
.ss-modern-table thead th,
.ss-soft-table thead th {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--ss-muted);
    background: rgba(59, 91, 255, 0.06);
    border: 0;
    border-bottom: 1px solid rgba(23, 26, 33, 0.08);
    padding: 0.6rem 0.85rem;
    white-space: nowrap;
}
.ss-modern-table tbody td,
.ss-soft-table tbody td {
    padding: 0.7rem 0.85rem;
    vertical-align: middle;
    border: 0;
    border-bottom: 1px solid rgba(23, 26, 33, 0.06);
}
.ss-modern-table tbody tr,
.ss-soft-table tbody tr {
    background: rgba(255, 255, 255, 0.55);
}
.ss-modern-table tbody tr:nth-child(even),
.ss-soft-table tbody tr:nth-child(even) {
    background: rgba(255, 255, 255, 0.30);
}
.ss-modern-table tbody tr:last-child td,
.ss-soft-table tbody tr:last-child td { border-bottom: 0; }
.ss-modern-table tbody tr:hover,
.ss-soft-table tbody tr:hover {
    background: rgba(232, 236, 255, 0.55);
}
.ss-soft-table.is-muted tbody td { color: var(--ss-text-muted); }

/* When a plain Bootstrap .table is placed on a plate, give it the same calm
   header/row treatment so it stops looking like a white grid. */
.ss-table-plate .table thead th,
.ss-table-shell .table thead th {
    background: rgba(59, 91, 255, 0.06);
    color: var(--ss-muted);
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    border-bottom: 1px solid rgba(23, 26, 33, 0.08);
    border-top: 0;
}
.ss-table-plate .table tbody td,
.ss-table-shell .table tbody td {
    border-color: rgba(23, 26, 33, 0.06);
}
.ss-table-plate .table.table-hover tbody tr:hover > *,
.ss-table-shell .table.table-hover tbody tr:hover > * {
    background-color: rgba(232, 236, 255, 0.55);
    --bs-table-accent-bg: transparent;
}

/* ═══════════════════════════════════════════════════════════════════════════
   BUTTONS & FORM CONTROLS
   Retint Bootstrap via its CSS variables so we don't fight specificity.
   Softer radii, calmer borders, premium primary, controlled danger/warning.
   ═══════════════════════════════════════════════════════════════════════════ */
.btn {
    --bs-btn-border-radius: 0.6rem;
    font-weight: 600;
    letter-spacing: -0.005em;
}
.btn-sm { --bs-btn-border-radius: 0.5rem; }
.btn-lg { --bs-btn-border-radius: 0.75rem; }

.btn-primary {
    --bs-btn-bg: var(--ss-primary);
    --bs-btn-border-color: var(--ss-primary);
    --bs-btn-hover-bg: var(--ss-primary-strong);
    --bs-btn-hover-border-color: var(--ss-primary-strong);
    --bs-btn-active-bg: var(--ss-primary-strong);
    --bs-btn-active-border-color: var(--ss-primary-strong);
    --bs-btn-disabled-bg: var(--ss-primary);
    --bs-btn-disabled-border-color: var(--ss-primary);
    box-shadow: 0 4px 12px rgba(59,91,255,0.22);
}
.btn-primary:hover { box-shadow: 0 6px 16px rgba(59,91,255,0.28); }

.btn-outline-primary {
    --bs-btn-color: var(--ss-primary);
    --bs-btn-border-color: var(--ss-accent-border);
    --bs-btn-hover-bg: var(--ss-primary);
    --bs-btn-hover-border-color: var(--ss-primary);
    --bs-btn-active-bg: var(--ss-primary);
    --bs-btn-active-border-color: var(--ss-primary);
}
.btn-outline-secondary {
    --bs-btn-color: #4a4f5a;
    --bs-btn-border-color: var(--ss-border);
    --bs-btn-hover-bg: var(--ss-surface-soft);
    --bs-btn-hover-color: var(--ss-ink);
    --bs-btn-hover-border-color: rgba(23,26,33,0.18);
    --bs-btn-active-bg: var(--ss-surface-soft);
    --bs-btn-active-color: var(--ss-ink);
}

/* SmartSuli semantic action helpers */
.ss-primary-action {
    --bs-btn-bg: var(--ss-primary);
    --bs-btn-border-color: var(--ss-primary);
    color: #fff;
}
.ss-secondary-action {
    background: var(--ss-surface);
    border: 1px solid var(--ss-border);
    color: #4a4f5a;
}
.ss-danger-action {
    --bs-btn-color: var(--ss-danger);
    --bs-btn-border-color: var(--ss-red-border);
}

/* Inputs / selects */
.form-control, .form-select {
    border-radius: 0.55rem;
    border-color: var(--ss-border);
    background-color: var(--ss-surface);
    color: var(--ss-ink);
}
.form-control:focus, .form-select:focus {
    border-color: var(--ss-primary);
    box-shadow: 0 0 0 0.18rem rgba(59,91,255,0.18);
}
.form-control::placeholder { color: #9aa1ad; }

/* Slightly larger radius on cards globally for the modern feel */
.card { border-radius: var(--ss-radius); }

/* ═══════════════════════════════════════════════════════════════════════════
   BRAND, PRODUCT SHELL, HOME, AVATAR, MOTION  (UI-Redesign C)
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Brand logo utilities ───────────────────────────────────────────────── */
.ss-brand-logo {
    height: 1.6rem;
    width: auto;
    display: block;
}
.ss-brand-mark {
    height: 1.4rem;
    width: auto;
    display: block;
}
.ss-navbar .navbar-brand {
    /* drop the gradient orb pseudo-mark — we now use the real logo image */
    gap: 0;
}
.ss-navbar .navbar-brand::before { content: none; }

/* Logo used as a large faded watermark inside a hero */
.ss-hero-logo-watermark {
    position: absolute;
    right: -2.5rem;
    top: 50%;
    transform: translateY(-50%);
    height: 150%;
    width: auto;
    max-width: 60%;
    object-fit: contain;
    opacity: 0.10;
    pointer-events: none;
    z-index: 0;
    filter: saturate(0);
}
@media (max-width: 768px) {
    .ss-hero-logo-watermark { opacity: 0.06; right: -4rem; height: 120%; }
}

/* Small circular logo orb (e.g. empty-state or accents) */
.ss-logo-orb {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3rem;
    height: 3rem;
    border-radius: 50%;
    background: var(--ss-surface);
    border: 1px solid var(--ss-border);
    box-shadow: var(--ss-shadow-xs);
    padding: 0.5rem;
}
.ss-logo-orb img { width: 100%; height: 100%; object-fit: contain; }

/* ── Navbar category reveal ─────────────────────────────────────────────── */
.ss-nav-categories {
    display: flex;
    align-items: center;
    gap: 0.15rem;
    opacity: 0.55;
    transform: translateY(-2px);
    transition: opacity 0.2s ease, transform 0.2s ease;
}
/* On hover/focus anywhere in the navbar, lift the categories to full presence. */
.ss-navbar:hover .ss-nav-categories,
.ss-navbar:focus-within .ss-nav-categories {
    opacity: 1;
    transform: translateY(0);
}
/* Category trigger pills */
.ss-nav-cat {
    font-size: 0.8125rem !important;
    font-weight: 600 !important;
    color: #57606f !important;
    padding: 0.35rem 0.6rem !important;
    border-radius: 0.55rem;
}
.ss-nav-cat::after { margin-left: 0.3rem; opacity: 0.5; }
.ss-nav-cat:hover { color: var(--ss-primary) !important; background: var(--ss-primary-soft); }

/* Below the expand breakpoint the menu is collapsed behind the toggler —
   show every category at full presence, no hover/focus dependency. */
@media (max-width: 991.98px) {
    .ss-nav-categories {
        opacity: 1;
        transform: none;
        flex-direction: column;
        align-items: stretch;
        gap: 0;
    }
    .ss-nav-cat { padding: 0.4rem 0 !important; }
}

/* ── Avatar + profile menu ──────────────────────────────────────────────── */
.ss-avatar-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 999px;
    padding: 0.2rem 0.5rem 0.2rem 0.25rem;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
}
.ss-avatar-btn:hover { background: var(--ss-surface-soft); border-color: var(--ss-border); }
.ss-avatar-btn:active { transform: scale(0.97); }
.ss-avatar-btn::after { display: none; } /* hide bootstrap caret */
.ss-avatar {
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    object-fit: cover;
    background: linear-gradient(135deg, var(--ss-primary) 0%, var(--ss-accent-lavender) 100%);
    color: #fff;
    font-size: 0.8125rem;
    font-weight: 700;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 2px 6px rgba(59,91,255,0.30);
    flex-shrink: 0;
    overflow: hidden;
}
.ss-avatar-name {
    font-size: 0.8125rem;
    font-weight: 600;
    color: #3d4350;
    max-width: 11rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ss-avatar-menu {
    min-width: 240px;
    z-index: var(--ss-z-dropdown);
}
.ss-avatar-menu .ss-avatar-menu-head {
    padding: 0.625rem 1rem 0.5rem;
    line-height: 1.3;
}
.ss-avatar-menu-head .nm { font-weight: 700; color: var(--ss-ink); font-size: 0.875rem; }
.ss-avatar-menu-head .em { font-size: 0.75rem; color: var(--ss-text-muted); }

/* ── Home dashboard ─────────────────────────────────────────────────────── */
.ss-home-hero {
    position: relative;
    overflow: hidden;
    border-radius: var(--ss-radius-lg);
    padding: 2.25rem 2.25rem;
    margin-bottom: 1.75rem;
    background:
        radial-gradient(130% 150% at 0% 0%, rgba(59,91,255,0.10) 0%, rgba(59,91,255,0) 45%),
        radial-gradient(120% 160% at 100% 100%, rgba(124,92,255,0.10) 0%, rgba(124,92,255,0) 50%),
        linear-gradient(180deg, var(--ss-surface) 0%, var(--ss-surface-warm) 100%);
    border: 1px solid var(--ss-border);
    box-shadow: var(--ss-shadow-card);
}
.ss-home-hero > * { position: relative; z-index: 1; }
.ss-home-greeting {
    font-size: 2rem;
    font-weight: 800;
    letter-spacing: -0.03em;
    color: var(--ss-ink);
    margin-bottom: 0.5rem;
    line-height: 1.1;
}
.ss-home-sub {
    font-size: 1rem;
    color: var(--ss-text-muted);
    max-width: 56ch;
    margin-bottom: 0;
}

/* ── Home dashboard: split hero + actionable glass cards ───────────────── */
.ss-home-dashboard {
    color: #171a21;
    padding-bottom: 1.5rem;
}
.ss-home-glass {
    position: relative;
    overflow: hidden;
    background: rgba(255, 255, 255, 0.7);
    -webkit-backdrop-filter: blur(16px);
    backdrop-filter: blur(16px);
    border: 1px solid rgba(255, 255, 255, 0.4);
    border-radius: 28px;
    box-shadow: 0 18px 45px rgba(15, 23, 42, 0.10);
}
.ss-home-welcome {
    min-height: 280px;
    display: flex;
    align-items: center;
    padding: clamp(2rem, 5vw, 3.75rem);
    isolation: isolate;
}
.ss-home-welcome::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background:
        radial-gradient(circle at 8% 14%, rgba(24, 199, 194, 0.18), transparent 34%),
        radial-gradient(circle at 92% 84%, rgba(124, 92, 255, 0.18), transparent 38%);
}
.ss-home-watermark {
    position: absolute;
    right: clamp(-4rem, -3vw, -1rem);
    bottom: clamp(-4rem, -5vw, -2rem);
    width: min(38%, 250px);
    opacity: 0.075;
    transform: rotate(-7deg);
    pointer-events: none;
    user-select: none;
}
.ss-home-welcome-content {
    position: relative;
    z-index: 1;
    max-width: 43rem;
}
.ss-home-kicker,
.ss-home-card-eyebrow {
    display: block;
    color: #3b5bff;
    font-size: 0.72rem;
    font-weight: 800;
    letter-spacing: 0.12em;
    line-height: 1.3;
    text-transform: uppercase;
}
.ss-home-welcome h1 {
    margin: 0.65rem 0 0.75rem;
    color: #171a21;
    font-size: clamp(2.15rem, 4.8vw, 4rem);
    font-weight: 600;
    letter-spacing: -0.04em;
    line-height: 0.98;
}
.ss-home-welcome p {
    max-width: 45ch;
    margin: 0;
    color: #667085;
    font-size: clamp(0.95rem, 1.8vw, 1.08rem);
}
.ss-home-brand {
    min-height: 280px;
    display: grid;
    place-items: center;
    padding: 1.5rem;
    background:
        linear-gradient(145deg, rgba(255, 255, 255, 0.84), rgba(255, 255, 255, 0.58)),
        rgba(255, 255, 255, 0.7);
}
.ss-home-brand-frame {
    width: min(72%, 230px);
    aspect-ratio: 1;
    display: grid;
    place-items: center;
    overflow: hidden;
    background: #fff;
    border: 1px solid rgba(23, 26, 33, 0.06);
    border-radius: 28px;
    box-shadow:
        0 22px 50px rgba(59, 91, 255, 0.12),
        inset 0 1px 0 rgba(255, 255, 255, 0.92);
}
.ss-home-brand-frame img {
    width: 62%;
    height: 82%;
    object-fit: contain;
}
.ss-home-card-grid {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: 1rem;
}
.ss-home-action-card {
    min-height: 260px;
    display: flex;
    flex-direction: column;
    padding: clamp(1.25rem, 2.6vw, 1.75rem);
    color: #171a21;
    text-decoration: none;
    transition:
        transform 0.2s cubic-bezier(0.16, 1, 0.3, 1),
        box-shadow 0.2s cubic-bezier(0.16, 1, 0.3, 1),
        border-color 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
.ss-home-action-card:hover {
    transform: translateY(-3px);
    border-color: rgba(255, 255, 255, 0.8);
    box-shadow: 0 28px 62px rgba(15, 23, 42, 0.17);
    color: #171a21;
}
.ss-home-action-card:focus-visible {
    outline: 3px solid rgba(59, 91, 255, 0.34);
    outline-offset: 3px;
}
.ss-home-action-card--lessons,
.ss-home-action-card--income { grid-column: span 6; }
.ss-home-action-card--next { grid-column: span 4; }
.ss-home-action-card--month-end { grid-column: span 5; }
.ss-home-action-card--evaluation { grid-column: span 3; }
.ss-home-action-card--lessons::before,
.ss-home-action-card--income::before,
.ss-home-action-card--next::before,
.ss-home-action-card--month-end::before,
.ss-home-action-card--evaluation::before {
    content: "";
    position: absolute;
    inset: 0 0 auto;
    height: 4px;
}
.ss-home-action-card--lessons::before { background: linear-gradient(90deg, #3b5bff, #7c5cff); }
.ss-home-action-card--income::before { background: linear-gradient(90deg, #18a058, #18c7c2); }
.ss-home-action-card--next::before { background: linear-gradient(90deg, #ffb020, #ff7b54); }
.ss-home-action-card--month-end::before { background: linear-gradient(90deg, #7c5cff, #ff4da6); }
.ss-home-action-card--evaluation::before { background: linear-gradient(90deg, #18c7c2, #3b5bff); }
.ss-home-card-heading {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
}
.ss-home-card-heading h2,
.ss-home-action-card > h2,
.ss-home-next-content h2,
.ss-home-next-empty h2,
.ss-home-nav-content h2 {
    margin: 0.3rem 0 0;
    color: #171a21;
    font-family: var(--ss-font-ui);
    font-size: 1.15rem;
    font-weight: 750;
    letter-spacing: -0.02em;
    line-height: 1.2;
}
.ss-home-card-arrow {
    width: 2.25rem;
    height: 2.25rem;
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    color: #3b5bff;
    background: rgba(59, 91, 255, 0.09);
    border-radius: 50%;
    font-size: 1.05rem;
    transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
.ss-home-action-card:hover .ss-home-card-arrow { transform: translate(2px, -2px); }
.ss-home-stat-row {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    margin: auto 0;
    padding: 1.25rem 0;
}
.ss-home-stat {
    position: relative;
    min-width: 0;
    padding: 0 1rem;
    text-align: center;
}
.ss-home-stat:first-child { padding-left: 0; }
.ss-home-stat:last-child { padding-right: 0; }
.ss-home-stat:not(:last-child)::after {
    content: "";
    position: absolute;
    top: 10%;
    right: 0;
    width: 1px;
    height: 80%;
    background: rgba(23, 26, 33, 0.12);
}
.ss-home-stat strong {
    display: block;
    overflow: hidden;
    color: #171a21;
    font-size: clamp(1.65rem, 3.3vw, 2.7rem);
    font-weight: 800;
    letter-spacing: -0.055em;
    line-height: 1;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ss-home-stat span {
    display: block;
    margin-top: 0.5rem;
    color: #667085;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.035em;
    text-transform: uppercase;
}
.ss-home-stat-row--money .ss-home-stat strong {
    font-size: clamp(1.25rem, 2.35vw, 2rem);
}
.ss-home-stat-row-label {
    margin-top: -0.4rem;
    color: #667085;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
}
.ss-home-stat-row--hours {
    padding-top: 0.4rem;
}
.ss-home-stat-row--hours .ss-home-stat strong {
    font-size: clamp(1.15rem, 2.1vw, 1.8rem);
}
.ss-home-card-foot {
    margin-top: auto;
    color: #3b5bff;
    font-size: 0.82rem;
    font-weight: 750;
}
.ss-home-next-content {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin: auto 0;
}
.ss-home-next-content time {
    color: #171a21;
    font-size: clamp(2.2rem, 5vw, 3.5rem);
    font-weight: 800;
    letter-spacing: -0.065em;
    line-height: 1;
}
.ss-home-next-content p,
.ss-home-next-empty p,
.ss-home-nav-content p,
.ss-home-evaluation-copy {
    margin: 0.35rem 0 0;
    color: #667085;
    font-size: 0.86rem;
    line-height: 1.45;
}
.ss-home-action-card--disabled {
    cursor: default;
    opacity: 0.72;
    background: rgba(255, 255, 255, 0.5);
}
.ss-home-action-card--disabled:hover {
    transform: none;
    border-color: rgba(255, 255, 255, 0.4);
    box-shadow: 0 18px 45px rgba(15, 23, 42, 0.10);
}
.ss-home-next-empty {
    margin: auto 0;
    text-align: center;
}
.ss-home-next-empty > span {
    display: block;
    margin-bottom: 0.65rem;
    font-size: 2.2rem;
}
.ss-home-nav-content {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin: auto 0;
}
.ss-home-nav-icon {
    width: 3.6rem;
    height: 3.6rem;
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    color: #7c5cff;
    background: rgba(124, 92, 255, 0.1);
    border-radius: 1.15rem;
    font-size: 2rem;
}
.ss-home-evaluation-count {
    margin: auto 0 0.25rem;
    color: #171a21;
    font-size: 3.5rem;
    font-weight: 800;
    letter-spacing: -0.06em;
    line-height: 1;
}
.ss-home-evaluation-copy { margin-bottom: auto; }

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .ss-home-glass { background: rgba(255, 255, 255, 0.94); }
}

@media (max-width: 1199.98px) {
    .ss-home-action-card--lessons,
    .ss-home-action-card--income,
    .ss-home-action-card--next,
    .ss-home-action-card--month-end { grid-column: span 6; }
    .ss-home-action-card--evaluation { grid-column: span 12; min-height: 220px; }
}

@media (max-width: 767.98px) {
    .ss-home-card-grid { grid-template-columns: 1fr; }
    .ss-home-action-card--lessons,
    .ss-home-action-card--income,
    .ss-home-action-card--next,
    .ss-home-action-card--month-end,
    .ss-home-action-card--evaluation { grid-column: 1; }
    .ss-home-welcome,
    .ss-home-brand { min-height: 230px; }
    .ss-home-action-card { min-height: 240px; }
    .ss-home-action-card--evaluation { min-height: 240px; }
}

@media (max-width: 419.98px) {
    .ss-home-stat { padding: 0 0.55rem; }
    .ss-home-stat strong { font-size: 1.7rem; }
    .ss-home-stat-row--money .ss-home-stat strong { font-size: 1.05rem; }
    .ss-home-stat span { font-size: 0.62rem; }
    .ss-home-next-content { align-items: flex-start; flex-direction: column; }
}

/* Action cards grid */
.ss-action-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 0.875rem;
    margin-bottom: 1.75rem;
}
.ss-action-card {
    display: flex;
    flex-direction: column;
    background: var(--ss-surface);
    border: 1px solid var(--ss-border);
    border-radius: var(--ss-radius);
    box-shadow: var(--ss-shadow-xs);
    padding: 1.125rem 1.25rem;
    text-decoration: none;
    color: inherit;
    transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease;
}
.ss-action-card:hover {
    transform: translateY(-3px);
    box-shadow: var(--ss-shadow-card);
    border-color: var(--ss-accent-border);
    color: inherit;
}
.ss-action-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.4rem;
}
.ss-action-card-title { font-weight: 700; color: var(--ss-ink); font-size: 0.975rem; }
.ss-action-card-count {
    font-size: 0.8rem;
    font-weight: 700;
    color: var(--ss-primary);
    background: var(--ss-primary-soft);
    border-radius: 999px;
    padding: 0.1rem 0.55rem;
    flex-shrink: 0;
}
.ss-action-card-desc { font-size: 0.85rem; color: var(--ss-text-muted); margin-bottom: 0.75rem; flex-grow: 1; }
.ss-action-card-cta { font-size: 0.85rem; font-weight: 600; color: var(--ss-primary); }
.ss-action-card:hover .ss-action-card-cta { text-decoration: underline; }

/* ── Motion system ──────────────────────────────────────────────────────── */
@keyframes ssFadeUp {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: translateY(0); }
}
.ss-page-hero, .ss-home-hero { animation: ssFadeUp 0.4s ease both; }
.ss-metric-card { transition: transform 0.15s ease, box-shadow 0.15s ease; }
.ss-metric-card:hover { transform: translateY(-2px); box-shadow: var(--ss-shadow-sm); }
.btn { transition: transform 0.08s ease, box-shadow 0.15s ease, background-color 0.15s ease, border-color 0.15s ease; }
.btn:active { transform: translateY(1px); }
.dropdown-menu.show { animation: ssFadeUp 0.16s ease both; }

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

/* ── Live Session Card (liquid progress bar) ────────────────────────────────
   A premium frosted-glass card with a liquid animated fill. The fill layer
   sits behind the content; the stripe pattern creates a soft flowing effect.
   ─────────────────────────────────────────────────────────────────────────── */
.ss-live-session-card {
    position: relative;
    overflow: hidden;
    border-radius: 26px;
    min-height: 88px;
    background: var(--ss-glass-bg-strong);
    -webkit-backdrop-filter: var(--ss-glass-blur);
    backdrop-filter: var(--ss-glass-blur);
    border: 1px solid var(--ss-glass-border);
    box-shadow:
        var(--ss-glass-shadow),
        inset 0 1px 0 rgba(255, 255, 255, 0.55);
    padding: 1.1rem 1.5rem;
}

.ss-live-session-fill {
    position: absolute;
    inset: 4px auto 4px 4px;   /* slight inset so the glass border shows all round */
    width: 0%;
    z-index: 0;
    border-radius: 22px;       /* pill inside the card */
    background:
        linear-gradient(120deg, rgba(24,199,194,.32), rgba(59,91,255,.26), rgba(255,77,166,.16)),
        repeating-linear-gradient(
            115deg,
            rgba(255,255,255,.18) 0px,
            rgba(255,255,255,.18) 10px,
            transparent 10px,
            transparent 24px
        );
    box-shadow: 0 0 18px rgba(59, 91, 255, 0.18);
    animation: ss-charge-flow 2.4s linear infinite;
    transition: width 0.9s cubic-bezier(0.4, 0, 0.2, 1);
}

/* Warning zone — covers only the final 5 minutes of the lesson timeline.
   Position is driven by --lesson-warning-start, set by JS from (D-5)/D.
   Sits above the fill (z-index: 1) so it stays amber even as the fill
   passes underneath. Hidden by default; JS shows it when durationMinutes > 5. */
.ss-live-session-warning-zone {
    position: absolute;
    top: 0;
    bottom: 0;
    left: var(--lesson-warning-start, 90%);
    right: 0;
    z-index: 1;
    pointer-events: none;
    display: none;
    background:
        linear-gradient(120deg, rgba(255,176,32,.26), rgba(255,77,166,.16)),
        repeating-linear-gradient(
            115deg,
            rgba(255,255,255,.18) 0,
            rgba(255,255,255,.18) 12px,
            transparent 12px,
            transparent 24px
        );
    animation: ss-charge-flow 2.4s linear infinite;
}

/* Thin amber line marking the boundary between normal and warning zones. */
.ss-live-session-warning-divider {
    position: absolute;
    top: 0;
    bottom: 0;
    left: var(--lesson-warning-start, 90%);
    width: 2px;
    z-index: 2;
    pointer-events: none;
    display: none;
    background: rgba(255,176,32,.75);
}

/* First-session phase dividers (Nyitás / Felmérés / Keretezés / Zárás) — three thin lines
   splitting the bar at the boundaries JS computes from Appendix B's phase lengths. Neutral
   tone (not amber) since these mark structure, not a warning. */
.ss-live-session-phase-divider {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 2px;
    z-index: 2;
    pointer-events: none;
    background: rgba(255,255,255,.55);
}

.ss-live-session-phase-labels {
    display: flex;
    justify-content: space-between;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--ss-muted);
    margin-bottom: 0.35rem;
}

.ss-live-session-phase-label.is-current {
    color: var(--ss-ink);
    font-weight: 700;
}

@media (prefers-reduced-motion: reduce) {
    .ss-live-session-fill,
    .ss-live-session-warning-zone {
        animation: none;
        transition: none;
    }
}

.ss-live-session-fill[data-state="warning"] {
    background:
        linear-gradient(120deg, rgba(255,176,32,.30), rgba(229,120,20,.22)),
        repeating-linear-gradient(
            115deg,
            rgba(255,255,255,.20) 0px,
            rgba(255,255,255,.20) 12px,
            transparent 12px,
            transparent 28px
        );
}

.ss-live-session-fill[data-state="overtime"] {
    background:
        linear-gradient(120deg, rgba(229,72,77,.30), rgba(255,77,166,.20)),
        repeating-linear-gradient(
            115deg,
            rgba(255,255,255,.20) 0px,
            rgba(255,255,255,.20) 12px,
            transparent 12px,
            transparent 28px
        );
    width: 100% !important;
}

.ss-live-session-fill[data-state="before"] {
    background: rgba(102,112,133,.10);
    animation: none;
}

@keyframes ss-charge-flow {
    from { background-position: 0 0, 0 0; }
    to   { background-position: 0 0, 56px 0; }
}

.ss-live-session-content {
    position: relative;
    z-index: 3;
}

.ss-live-session-title {
    font-size: 1rem;
    font-weight: 600;
    color: var(--ss-ink);
    letter-spacing: -0.01em;
}

.ss-live-session-meta {
    font-size: 0.85rem;
    color: var(--ss-muted);
}

.ss-live-session-autojump {
    border-top: 1px solid var(--ss-border-light);
    padding-top: 0.75rem;
}

/* Quick-complete pill — lives inside the timeline card itself so "Megtartva" stays in view for
   the whole lesson instead of waiting to be found at the bottom of the page. Right-aligned below
   the timer row (in-flow, not floated) so it never collides with the timer text or the
   first-session phase labels — the card just grows a little while it's showing. A slow pulse
   keeps it from blending into the animated fill without being distracting. Disappears entirely
   (not just disabled) once the status leaves the actionable set — see ShowQuickCompleteButton. */
.ss-live-session-complete-form {
    display: flex;
    justify-content: flex-end;
    margin: 0.5rem 0 0;
}

.ss-live-session-complete-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    border: 1px solid rgba(255, 255, 255, 0.4);
    border-radius: 999px;
    padding: 0.4rem 0.95rem;
    background: linear-gradient(135deg, #1FBF8F, #18C7C2);
    color: #fff;
    font-size: 0.82rem;
    font-weight: 700;
    letter-spacing: 0.01em;
    white-space: nowrap;
    box-shadow: 0 2px 10px rgba(31, 191, 143, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.4);
    cursor: pointer;
    animation: ss-complete-pulse 2.6s ease-in-out infinite;
    transition: transform 0.15s ease, box-shadow 0.15s ease, filter 0.15s ease;
}

.ss-live-session-complete-btn svg {
    width: 14px;
    height: 14px;
    flex: none;
}

.ss-live-session-complete-btn:hover {
    transform: translateY(-1px);
    filter: brightness(1.06);
    box-shadow: 0 4px 16px rgba(31, 191, 143, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.45);
    animation-play-state: paused;
}

.ss-live-session-complete-btn:active {
    transform: translateY(0);
    filter: brightness(0.98);
}

@keyframes ss-complete-pulse {
    0%, 100% { box-shadow: 0 2px 10px rgba(31, 191, 143, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.4); }
    50%      { box-shadow: 0 2px 20px rgba(31, 191, 143, 0.65), inset 0 1px 0 rgba(255, 255, 255, 0.45); }
}

@media (prefers-reduced-motion: reduce) {
    .ss-live-session-complete-btn { animation: none; }
}

@media (max-width: 575.98px) {
    .ss-live-session-complete-btn {
        padding: 0.32rem 0.7rem;
        font-size: 0.76rem;
    }
}

/* ── Dark mode ──────────────────────────────────────────────────────────────
   Automatic dark theme via prefers-color-scheme. Bootstrap 5.3 dark-mode
   components are activated by data-bs-theme="dark" (set by inline script in
   _Layout.cshtml). This block overrides SmartSuli's own design tokens and any
   component styles that hardcode light-theme colors.
   ─────────────────────────────────────────────────────────────────────────── */
@media (prefers-color-scheme: dark) {
    :root {
        /* Surfaces & ink */
        --ss-bg:               #0F1117;
        --ss-bg-alt:           #131924;
        --ss-surface:          #1A1F2E;
        --ss-surface-warm:     #1C1E2E;
        --ss-surface-mint:     #111E24;
        --ss-surface-lavender: #181630;
        --ss-surface-soft:     #212638;
        --ss-ink:              #E8EAF0;
        --ss-text:             #E8EAF0;
        --ss-muted:            #8B93A7;
        --ss-text-muted:       #8B93A7;
        --ss-border:           rgba(255, 255, 255, 0.10);
        --ss-border-light:     rgba(255, 255, 255, 0.06);

        /* Semantic soft backgrounds — dark tinted overlays instead of pale pastels */
        --ss-green-soft:       rgba(62, 204, 108, 0.15);
        --ss-green-border:     rgba(62, 204, 108, 0.30);
        --ss-amber-soft:       rgba(212, 152, 42, 0.15);
        --ss-amber-border:     rgba(212, 152, 42, 0.30);
        --ss-red-soft:         rgba(242, 101, 103, 0.15);
        --ss-red-border:       rgba(242, 101, 103, 0.30);
        --ss-primary-soft:     rgba(59, 91, 255, 0.18);
        --ss-accent-soft:      rgba(59, 91, 255, 0.18);
        --ss-accent-border:    rgba(59, 91, 255, 0.35);

        /* Semantic on-surface colors: slightly brighter for dark readability */
        --ss-success:          #3ECC6C;
        --ss-green:            #3ECC6C;
        --ss-warning:          #D4982A;
        --ss-amber:            #D4982A;
        --ss-danger:           #F26567;
        --ss-red:              #F26567;

        /* Shadows: stronger so surfaces still lift off the dark background */
        --ss-shadow-xs:        0 1px 2px rgba(0, 0, 0, 0.35);
        --ss-shadow-sm:        0 1px 4px rgba(0, 0, 0, 0.40), 0 1px 2px rgba(0, 0, 0, 0.30);
        --ss-shadow-card:      0 8px 24px rgba(0, 0, 0, 0.45);
        --ss-shadow-soft:      0 14px 40px rgba(0, 0, 0, 0.55);
    }

    /* Navbar: swap the hardcoded white-translucent background */
    .ss-navbar {
        background: rgba(15, 17, 23, 0.88) !important;
    }
    .ss-navbar .nav-link    { color: #B4BCC8 !important; }
    .ss-navbar .dropdown-menu { box-shadow: 0 12px 32px rgba(0, 0, 0, 0.50); }
    .ss-navbar .dropdown-item { color: #C0C8D8; }
    .ss-navbar .dropdown-item:hover { background: var(--ss-surface-soft); }

    /* Avatar / profile */
    .ss-avatar-name { color: #B4BCC8; }

    /* Category nav pills */
    .ss-nav-cat { color: #8B93A7 !important; }

    /* Table plate: swap the warm-white gradient */
    .ss-table-plate,
    .ss-table-shell {
        background: linear-gradient(180deg, rgba(28, 32, 50, 0.92), rgba(20, 23, 38, 0.88));
    }

    /* Modern / soft table rows */
    .ss-modern-table tbody tr,
    .ss-soft-table tbody tr                              { background: rgba(26, 31, 46, 0.70); }
    .ss-modern-table tbody tr:nth-child(even),
    .ss-soft-table tbody tr:nth-child(even)              { background: rgba(20, 24, 38, 0.50); }
    .ss-modern-table tbody tr:hover,
    .ss-soft-table tbody tr:hover                        { background: rgba(59, 91, 255, 0.12); }

    /* Modern / plate table headers */
    .ss-modern-table thead th,
    .ss-soft-table thead th,
    .ss-table-plate .table thead th,
    .ss-table-shell .table thead th {
        background: rgba(59, 91, 255, 0.12);
        border-bottom-color: rgba(255, 255, 255, 0.08);
    }
    .ss-table-plate .table.table-hover tbody tr:hover > *,
    .ss-table-shell .table.table-hover tbody tr:hover > * {
        background-color: rgba(59, 91, 255, 0.10);
        --bs-table-accent-bg: transparent;
    }

    /* ss-table (simpler variant) */
    .ss-table thead th  { background: var(--ss-surface-soft); }
    .ss-table tbody tr:hover { background: var(--ss-surface-soft); }

    /* Card header */
    .card-header { background: var(--ss-surface-soft); }

    /* Empty state title */
    .ss-empty-title { color: #9BA3B2; }

    /* Methodology hint box */
    .ss-hint-box  { background: #1E1C10; border-color: rgba(236, 232, 160, 0.25); }
    .ss-hint-title { color: #C8B44A; }

    /* Next action banner */
    .ss-next-action { color: #93B4D8; }

    /* Connection point callout */
    .ss-connection-point {
        background: #0D221A;
        border-color: rgba(176, 216, 184, 0.25);
        border-left-color: #2a9a5a;
        color: #6DC98A;
    }

    /* Outline secondary button: lighten text on dark bg */
    .btn-outline-secondary {
        --bs-btn-color: #B0B8C8;
        --bs-btn-border-color: var(--ss-border);
        --bs-btn-hover-color: var(--ss-ink);
        --bs-btn-hover-bg: var(--ss-surface-soft);
    }
    .ss-secondary-action { background: var(--ss-surface); border-color: var(--ss-border); color: #B0B8C8; }

    /* Decision card primary tint */
    .ss-decision-card-primary {
        background: linear-gradient(0deg, rgba(59, 91, 255, 0.06), rgba(59, 91, 255, 0.06)),
                    var(--ss-surface);
    }

    /* Home hero gradient */
    .ss-home-hero {
        background:
            radial-gradient(130% 150% at 0% 0%, rgba(59, 91, 255, 0.12) 0%, rgba(59, 91, 255, 0) 45%),
            radial-gradient(120% 160% at 100% 100%, rgba(124, 92, 255, 0.12) 0%, rgba(124, 92, 255, 0) 50%),
            linear-gradient(180deg, var(--ss-surface) 0%, var(--ss-surface-soft) 100%);
    }

    /* Page hero gradient */
    .ss-page-hero {
        background:
            radial-gradient(120% 140% at 100% 0%, rgba(124, 92, 255, 0.15) 0%, rgba(124, 92, 255, 0) 45%),
            radial-gradient(120% 140% at 0% 100%, rgba(24, 199, 194, 0.15) 0%, rgba(24, 199, 194, 0) 50%),
            var(--ss-surface);
    }

    .ss-muted-section { background: var(--ss-surface-soft); }

    /* Live session card */
    .ss-live-session-card {
        background: #1A1F2E;
    }
    .ss-live-session-fill {
        background:
            linear-gradient(120deg, rgba(24,199,194,.35), rgba(59,91,255,.28), rgba(255,77,166,.18)),
            repeating-linear-gradient(
                115deg,
                rgba(255,255,255,.10) 0px,
                rgba(255,255,255,.10) 12px,
                transparent 12px,
                transparent 28px
            );
    }
    .ss-live-session-fill[data-state="warning"] {
        background:
            linear-gradient(120deg, rgba(255,176,32,.38), rgba(229,120,20,.28)),
            repeating-linear-gradient(
                115deg,
                rgba(255,255,255,.10) 0px,
                rgba(255,255,255,.10) 12px,
                transparent 12px,
                transparent 28px
            );
    }
    .ss-live-session-fill[data-state="overtime"] {
        background:
            linear-gradient(120deg, rgba(229,72,77,.40), rgba(255,77,166,.26)),
            repeating-linear-gradient(
                115deg,
                rgba(255,255,255,.10) 0px,
                rgba(255,255,255,.10) 12px,
                transparent 12px,
                transparent 28px
            );
    }
    .ss-live-session-fill[data-state="before"] {
        background: rgba(255,255,255,.05);
    }
    .ss-live-session-title { color: #E8EAF0; }
    .ss-live-session-autojump { border-top-color: rgba(255,255,255,.08); }
    .ss-live-session-warning-zone {
        background:
            linear-gradient(120deg, rgba(255,176,32,.34), rgba(255,77,166,.20)),
            repeating-linear-gradient(
                115deg,
                rgba(255,255,255,.12) 0,
                rgba(255,255,255,.12) 12px,
                transparent 12px,
                transparent 24px
            );
    }
    .ss-live-session-warning-divider {
        background: rgba(255,176,32,.85);
    }
    .ss-live-session-phase-divider {
        background: rgba(255,255,255,.32);
    }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Warm Frosted Glass refresh (2026)
   A quiet-luxury layer on top of the existing design system. Appended last so it
   wins the cascade. Adds: warm mesh background, frosted-glass cards, serif
   display headings, softened buttons, glass inputs, and the lesson tab pills.
   Everything degrades gracefully where backdrop-filter is unsupported.
   ═══════════════════════════════════════════════════════════════════════════ */
:root {
    --ss-font-ui:      Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    --ss-font-display: ui-serif, "New York", Georgia, "Times New Roman", serif;

    /* Warm mesh palette */
    --ss-bg-warm:  #F7F0E4;
    --ss-bg-mint:  #DFF7EC;
    --ss-bg-peach: #FFE2CF;
    --ss-bg-blue:  #E4EEFF;

    /* Glass surfaces */
    --ss-glass-bg:        rgba(255, 255, 255, 0.62);
    --ss-glass-bg-strong: rgba(255, 255, 255, 0.74);
    --ss-glass-bg-soft:   rgba(255, 255, 255, 0.42);
    --ss-glass-border:    rgba(255, 255, 255, 0.65);
    --ss-glass-shadow:    0 18px 45px rgba(15, 23, 42, 0.10);
    --ss-glass-shadow-hi: 0 26px 60px rgba(15, 23, 42, 0.16);
    --ss-glass-blur:      saturate(180%) blur(20px);
    --ss-radius-glass:    22px;
    --ss-radius-xl:       28px;
}

/* ── Body: warm atmospheric mesh gradient — fixed so it fills the full page ── */
body {
    font-family: var(--ss-font-ui);
    background-color: var(--ss-bg-warm);
    background-image:
        radial-gradient(75% 65% at 10% 4%,   rgba(223, 247, 236, 0.92) 0%, rgba(223, 247, 236, 0) 70%),
        radial-gradient(68% 60% at 94% 8%,   rgba(255, 226, 207, 0.88) 0%, rgba(255, 226, 207, 0) 68%),
        radial-gradient(72% 65% at 80% 96%,  rgba(228, 238, 255, 0.90) 0%, rgba(228, 238, 255, 0) 70%),
        radial-gradient(65% 60% at 6% 92%,   rgba(247, 240, 228, 0.94) 0%, rgba(247, 240, 228, 0) 68%),
        radial-gradient(50% 45% at 50% 50%,  rgba(255, 248, 237, 0.50) 0%, rgba(255, 248, 237, 0) 100%);
    background-attachment: fixed;
    background-size: cover;
    background-repeat: no-repeat;
}

/* ── Display serif headings ─────────────────────────────────────────────── */
h1,
.ss-page-title,
.ss-lesson-title,
.ss-home-hero h1,
.ss-page-hero h1 {
    font-family: var(--ss-font-display);
    letter-spacing: -0.012em;
    font-weight: 600;
}

/* Lesson-specific title: lighter weight for editorial elegance */
.ss-lesson-title {
    font-size: clamp(1.55rem, 1.1rem + 1.6vw, 2.1rem);
    font-weight: 380;
    letter-spacing: -0.035em;
    line-height: 0.97;
}

/* ── Frosted glass cards ────────────────────────────────────────────────── */
.card,
.ss-card,
.ss-panel,
.ss-metric-card,
.ss-action-card,
.ss-decision-card,
.ss-kpi-card {
    background: var(--ss-glass-bg);
    -webkit-backdrop-filter: var(--ss-glass-blur);
    backdrop-filter: var(--ss-glass-blur);
    border: 1px solid var(--ss-glass-border);
    border-radius: var(--ss-radius-glass);
    box-shadow: var(--ss-glass-shadow);
}
.ss-card-soft {
    background: var(--ss-glass-bg-soft);
    -webkit-backdrop-filter: var(--ss-glass-blur);
    backdrop-filter: var(--ss-glass-blur);
    border: 1px solid var(--ss-glass-border);
    border-radius: var(--ss-radius-glass);
    box-shadow: 0 10px 30px rgba(15, 23, 42, 0.06);
}
/* nested glass-in-glass: keep inner panels lighter so they don't muddy */
.ss-card .ss-card-soft,
.ss-card .card,
.ss-card-soft .card { -webkit-backdrop-filter: none; backdrop-filter: none; }

/* Fallback when backdrop-filter is unsupported — stay readable with solid-ish bg */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .card, .ss-card, .ss-panel, .ss-metric-card, .ss-action-card,
    .ss-decision-card, .ss-kpi-card { background: rgba(255, 255, 255, 0.92); }
    .ss-card-soft { background: rgba(255, 255, 255, 0.82); }
}

/* ── Hover micro-animation ──────────────────────────────────────────────── */
.ss-metric-card,
.ss-action-card,
.ss-decision-card,
.ss-lesson-tabs .nav-link,
.btn {
    transition: transform 160ms ease, box-shadow 160ms ease, background-color 160ms ease, border-color 160ms ease;
}
.ss-metric-card:hover,
.ss-action-card:hover {
    transform: translateY(-2px) scale(1.008);
    box-shadow: var(--ss-glass-shadow-hi);
}
.btn:not(.btn-link):hover { transform: translateY(-1px); }
.btn:not(.btn-link):active { transform: translateY(0); }

/* ── Softer, quieter buttons ────────────────────────────────────────────── */
.btn { border-radius: 13px; }
.btn-sm { border-radius: 10px; }
/* Soften harsh danger toward a recognisable coral/rose (still clearly a warning) */
.btn-danger {
    --bs-btn-bg: #E86A72;
    --bs-btn-border-color: #E86A72;
    --bs-btn-hover-bg: #DB565F;
    --bs-btn-hover-border-color: #DB565F;
    --bs-btn-active-bg: #CE4A53;
}
.btn-outline-danger {
    --bs-btn-color: #C9505A;
    --bs-btn-border-color: rgba(201, 80, 90, 0.45);
    --bs-btn-hover-bg: #E86A72;
    --bs-btn-hover-border-color: #E86A72;
}
.btn-warning {
    --bs-btn-bg: #F0B45C;
    --bs-btn-border-color: #F0B45C;
    --bs-btn-color: #4a3410;
    --bs-btn-hover-bg: #E8A544;
    --bs-btn-hover-border-color: #E8A544;
    --bs-btn-hover-color: #4a3410;
}

/* ── Glass form controls ────────────────────────────────────────────────── */
.form-control,
.form-select,
textarea.form-control {
    background-color: var(--ss-glass-bg-soft);
    border: 1px solid rgba(15, 23, 42, 0.12);
    border-radius: 13px;
    box-shadow: inset 0 2px 8px rgba(15, 23, 42, 0.06);
    transition: border-color 140ms ease, box-shadow 140ms ease, background-color 140ms ease;
}
.form-control:focus,
.form-select:focus {
    background-color: rgba(255, 255, 255, 0.78);
    border-color: var(--ss-primary);
    box-shadow: inset 0 2px 6px rgba(15, 23, 42, 0.05), 0 0 0 0.2rem rgba(59, 91, 255, 0.18);
}

/* ── Navbar → floating glass ────────────────────────────────────────────── */
.ss-navbar {
    background: rgba(255, 255, 255, 0.55) !important;
    -webkit-backdrop-filter: saturate(180%) blur(18px);
    backdrop-filter: saturate(180%) blur(18px);
    border-bottom: 1px solid rgba(255, 255, 255, 0.5) !important;
}
.ss-avatar-btn { border-radius: 999px; }

/* ── Lesson tabs: separate floating glass pills ──────────────────────────── */
.ss-lesson-tabs-wrap {
    position: sticky;
    top: var(--ss-sticky-top, 3.75rem);
    z-index: var(--ss-z-sticky-action, 1020);
    margin: 0 -0.25rem 1.5rem;
    padding: 0.5rem 0.25rem;
    /* Transparent container — glass lives on each pill, not the bar */
    background: transparent;
    border: none;
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}
.ss-lesson-tabs.nav-tabs {
    border-bottom: 0;
    display: flex;
    justify-content: center;
    gap: 0.75rem;
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    -ms-overflow-style: none;
}
.ss-lesson-tabs.nav-tabs::-webkit-scrollbar { display: none; }

/* Each tab button is its own frosted glass pill */
.ss-lesson-tabs.nav-tabs .nav-link {
    border: 1px solid var(--ss-glass-border);
    border-radius: 999px;
    padding: 0.55rem 1.35rem;
    font-weight: 600;
    font-size: 0.875rem;
    color: var(--ss-text-muted);
    white-space: nowrap;
    background: var(--ss-glass-bg-strong);
    -webkit-backdrop-filter: var(--ss-glass-blur);
    backdrop-filter: var(--ss-glass-blur);
    box-shadow: 0 4px 14px rgba(15, 23, 42, 0.07), inset 0 1px 0 rgba(255, 255, 255, 0.55);
    transition: transform 160ms ease, box-shadow 160ms ease, background-color 160ms ease, border-color 160ms ease;
}
.ss-lesson-tabs.nav-tabs .nav-link:hover {
    color: var(--ss-ink);
    background: rgba(255, 255, 255, 0.80);
    transform: translateY(-2px);
    box-shadow: 0 8px 22px rgba(15, 23, 42, 0.10), inset 0 1px 0 rgba(255, 255, 255, 0.65);
}
.ss-lesson-tabs.nav-tabs .nav-link.active {
    color: #1a2f72;
    background: linear-gradient(145deg,
        rgba(223, 247, 236, 0.96) 0%,
        rgba(228, 238, 255, 0.96) 60%,
        rgba(240, 232, 255, 0.94) 100%);
    border-color: rgba(180, 200, 255, 0.55);
    box-shadow:
        0 6px 22px rgba(59, 91, 255, 0.14),
        0 2px 8px rgba(59, 91, 255, 0.10),
        inset 0 0 0 1px rgba(255, 255, 255, 0.70);
    transform: translateY(-1px);
}

/* ── Lesson tab pane glass panel ─────────────────────────────────────────── */
.ss-lesson-tab-pane-glass {
    background: var(--ss-glass-bg-strong);
    -webkit-backdrop-filter: var(--ss-glass-blur);
    backdrop-filter: var(--ss-glass-blur);
    border: 1px solid var(--ss-glass-border);
    border-radius: var(--ss-radius-xl, 28px);
    box-shadow: var(--ss-glass-shadow-hi);
    padding: 1.75rem 2rem;
}
@media (max-width: 575.98px) {
    .ss-lesson-tab-pane-glass { padding: 1.25rem 1rem; border-radius: var(--ss-radius-glass); }
}

.ss-lesson-tabcontent { padding-top: 0; }

/* ── Reduced motion ─────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    .ss-metric-card, .ss-action-card, .ss-decision-card, .btn, .ss-lesson-tabs .nav-link,
    .form-control, .form-select, .ss-review-progress > span, a.ss-filter-pill {
        transition: none !important;
    }
    .ss-metric-card:hover, .ss-action-card:hover, .btn:hover, .btn:active { transform: none !important; }
}

/* ═══ Dark mode — premium midnight glass ════════════════════════════════════ */
@media (prefers-color-scheme: dark) {
    :root {
        --ss-glass-bg:        rgba(28, 33, 50, 0.58);
        --ss-glass-bg-strong: rgba(28, 33, 50, 0.74);
        --ss-glass-bg-soft:   rgba(34, 40, 60, 0.45);
        --ss-glass-border:    rgba(255, 255, 255, 0.10);
        --ss-glass-shadow:    0 18px 45px rgba(0, 0, 0, 0.50);
        --ss-glass-shadow-hi: 0 26px 60px rgba(0, 0, 0, 0.60);
    }

    body {
        background-color: #0B1020;
        background-image:
            radial-gradient(60% 52% at 10% 4%,  rgba(24, 199, 194, 0.16) 0%, rgba(24, 199, 194, 0) 60%),
            radial-gradient(55% 50% at 95% 8%,  rgba(59, 91, 255, 0.18) 0%, rgba(59, 91, 255, 0) 58%),
            radial-gradient(65% 60% at 85% 98%, rgba(124, 92, 255, 0.16) 0%, rgba(124, 92, 255, 0) 60%),
            linear-gradient(180deg, #0C1224 0%, #0A0F1E 100%);
    }

    @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
        .card, .ss-card, .ss-panel, .ss-metric-card, .ss-action-card,
        .ss-decision-card, .ss-kpi-card { background: rgba(26, 31, 46, 0.96); }
        .ss-card-soft { background: rgba(33, 38, 56, 0.9); }
    }

    .form-control, .form-select, textarea.form-control {
        background-color: rgba(20, 24, 38, 0.55);
        border-color: rgba(255, 255, 255, 0.12);
        box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.35);
        color: var(--ss-text);
    }
    .form-control:focus, .form-select:focus {
        background-color: rgba(24, 29, 46, 0.85);
        box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.3), 0 0 0 0.2rem rgba(59, 91, 255, 0.30);
    }

    .ss-navbar {
        background: rgba(11, 16, 32, 0.55) !important;
        border-bottom-color: rgba(255, 255, 255, 0.08) !important;
    }

    .ss-lesson-tabs.nav-tabs .nav-link:hover { color: #E8EAF0; background: rgba(255, 255, 255, 0.06); }
    .ss-lesson-tabs.nav-tabs .nav-link.active {
        color: #DCE4FF;
        background: linear-gradient(135deg, rgba(24, 199, 194, 0.22), rgba(59, 91, 255, 0.28));
        box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45), inset 0 0 0 1px rgba(255, 255, 255, 0.10);
    }
}

/* ── Form-submit loading state ─────────────────────────────────────────────── */
/* Opt-in: add data-ss-loading to forms where a visible busy indicator is useful
   (slow server actions — calendar sync, exam import, billing generation). */
.ss-form-submitting button[type=submit],
.ss-form-submitting input[type=submit] {
    opacity: 0.6;
    pointer-events: none;
    cursor: not-allowed;
}
.ss-form-submitting button[type=submit]::after,
.ss-form-submitting input[type=submit]::after {
    content: " …";
}

@media (prefers-reduced-motion: reduce) {
    .ss-form-submitting button[type=submit]::after,
    .ss-form-submitting input[type=submit]::after { content: none; }
}

/* ── Exam paper alongside the answer list ─────────────────────────────────────
   The answer sheet is a one-question-at-a-time focus shell, and for a reading task the passage
   exists nowhere but the source PDF — the database stores the questions, never the text they are
   about. This panel puts that PDF on the same screen without needing an Overlay Spec, which only
   two packages in the whole vault have ever had.

   Above the questions rather than beside them: the sheet is used on a phone as often as a laptop,
   and a side-by-side split at 375px gives neither half enough room. On a wide screen the panel is
   capped so the questions below stay visible without scrolling past a full page of PDF. */
.ss-answer-paper-pane {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin: 0 auto 1rem;
    padding: 0.6rem;
    width: min(100%, 60rem);
    max-height: 60vh;
    overflow: hidden;
    border: 1px solid var(--ss-glass-border, var(--ss-border));
    border-radius: var(--ss-radius-lg, 1rem);
    background: var(--ss-glass-bg-strong, rgba(255, 255, 255, 0.88));
    box-shadow: var(--ss-shadow-sm);
}
.ss-answer-paper-pages {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
}
.ss-answer-paper-status {
    margin: 0;
    font-size: 0.85rem;
    color: var(--ss-muted, #6b7280);
}
/* The full paper view's toolbar is a fixed floating pill over a full-screen document; inside this
   panel it has to sit in normal flow, or it would hover over the page the student is reading. */
.ss-paper-toolbar--inline {
    position: static;
    transform: none;
    left: auto;
    top: auto;
    max-width: 100%;
    align-self: center;
    flex-wrap: wrap;
    justify-content: center;
}
@media (min-width: 992px) {
    /* Taller once there is room for it, since a Cambridge page is portrait and a laptop is not. */
    .ss-answer-paper-pane { max-height: 68vh; }
}

/* Doc 37 §3 — the rubric-review split: the printed paper on the left, the extracted field list on
   the right. Stacked below lg, because the decision needs both halves visible at once and a phone
   cannot give that; on a laptop each half scrolls independently so a long field list never pushes
   the page out of view. */
.ss-rubric-split {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
    align-items: start;
}
@media (min-width: 992px) {
    .ss-rubric-split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
    .ss-rubric-fields { max-height: 82vh; overflow-y: auto; }
}
.ss-rubric-pdf {
    max-height: 82vh;
    overflow-y: auto;
    position: relative;
    background: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: .375rem;
    padding: .5rem;
}
.ss-rubric-pdf-page { position: relative; margin-bottom: .75rem; }
.ss-rubric-pdf-page canvas { max-width: 100%; height: auto; display: block; }
